pax_global_header00006660000000000000000000000064141441521070014511gustar00rootroot0000000000000052 comment=31d5e30e9a9c2cd805acd23720812f061bcb2dc5 goodvibes-v0.7.2/000077500000000000000000000000001414415210700136665ustar00rootroot00000000000000goodvibes-v0.7.2/.clang-format000066400000000000000000000011331414415210700162370ustar00rootroot00000000000000# https://clang.llvm.org/docs/ClangFormat.html # https://clang.llvm.org/docs/ClangFormatStyleOptions.html AlignConsecutiveMacros: Consecutive AlwaysBreakAfterReturnType: AllDefinitions BreakBeforeBraces: Linux # Assume developer will line wrap appropriately ColumnLimit: 0 ContinuationIndentWidth: 8 Cpp11BracedListStyle: false IndentWidth: 8 PointerAlignment: Right SpaceAfterCStyleCast: true UseTab: Always # Keep formatting "as is" for these macros, otherwise # clang-format will mess it up WhitespaceSensitiveMacros: - G_DEFINE_TYPE_WITH_CODE - G_DEFINE_ABSTRACT_TYPE_WITH_CODE - MUTEST_MAIN goodvibes-v0.7.2/.editorconfig000066400000000000000000000004141414415210700163420ustar00rootroot00000000000000# https://editorconfig.org root = true [*] charset = utf-8 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true [*.{c,h}] indent_style = tab indent_size = 8 [*.{build,glade,ui,xml,xml.in}] indent_style = space indent_size = 2 tab_width = 8 goodvibes-v0.7.2/.gitignore000066400000000000000000000001221414415210700156510ustar00rootroot00000000000000*~ *.swp /build /docs/goodvibes.readthedocs.io/_build /subprojects/*/ /TAGS /tags goodvibes-v0.7.2/.gitlab-ci.yml000066400000000000000000000015531414415210700163260ustar00rootroot00000000000000variables: DEBIAN_IMAGE: "registry.gitlab.com/goodvibes/goodvibes/debian:sid" FEDORA_IMAGE: "registry.gitlab.com/goodvibes/goodvibes/fedora:34" debian-gcc: image: $DEBIAN_IMAGE stage: build script: - .gitlab-ci/build.sh debian-clang: image: $DEBIAN_IMAGE stage: build only: [ tags ] variables: CC: clang script: - .gitlab-ci/build.sh fedora-gcc: image: $FEDORA_IMAGE stage: build only: [ tags ] script: - .gitlab-ci/build.sh fedora-clang: image: $FEDORA_IMAGE stage: build only: [ tags ] variables: CC: clang script: - .gitlab-ci/build.sh debian-gcc-no-ui: image: $DEBIAN_IMAGE stage: build only: [ tags ] script: - .gitlab-ci/build.sh -Dui-enabled=false debian-gcc-no-feat-hotkeys: image: $DEBIAN_IMAGE stage: build only: [ tags ] script: - .gitlab-ci/build.sh -Dfeat-hotkeys=false goodvibes-v0.7.2/.gitlab-ci/000077500000000000000000000000001414415210700155775ustar00rootroot00000000000000goodvibes-v0.7.2/.gitlab-ci/Dockerfile.debian000066400000000000000000000011361414415210700210130ustar00rootroot00000000000000FROM debian:sid ENV LANG C.UTF-8 RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive \ apt-get install --yes --no-install-recommends \ appstream-util \ ca-certificates \ clang \ gcc \ gettext \ git \ libglib2.0-dev \ libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev \ libgtk-3-dev \ libkeybinder-3.0-dev \ libsoup2.4-dev \ libxml2-utils \ locales \ meson \ pkg-config \ && rm -rf /var/lib/apt/lists/* RUN useradd \ --create-home \ --shell /bin/bash \ --uid 4321 \ builder USER builder WORKDIR /home/builder goodvibes-v0.7.2/.gitlab-ci/Dockerfile.fedora000066400000000000000000000007311414415210700210310ustar00rootroot00000000000000FROM fedora:34 ENV LANG C.UTF-8 RUN dnf -y install \ clang \ findutils \ gcc \ gettext-devel \ git \ glib2-devel \ gstreamer1-devel \ gstreamer1-plugins-base-devel \ gtk3-devel \ keybinder3-devel \ libappstream-glib \ libsoup-devel \ meson \ pkgconfig \ tar \ xz \ && dnf clean all RUN useradd \ --create-home \ --shell /bin/bash \ --uid 4321 \ builder USER builder WORKDIR /home/builder goodvibes-v0.7.2/.gitlab-ci/build.sh000077500000000000000000000002551414415210700172370ustar00rootroot00000000000000#!/bin/bash set -e set -u set -x CPPFLAGS="\ -std=gnu99 \ -Wall \ -Wextra \ -Wshadow \ -Werror" export CPPFLAGS meson build "$@" ninja -C build ninja -C build test goodvibes-v0.7.2/COPYING000066400000000000000000001045151414415210700147270ustar00rootroot00000000000000 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 . goodvibes-v0.7.2/HACKING.md000066400000000000000000000327151414415210700152640ustar00rootroot00000000000000HACKING ======= Here's the place to get started when hacking Goodvibes. Table of Contents ----------------- 1. [Download](#download) 2. [Compilation](#compilation) 3. [Program Invocation](#program-invocation) 4. [Additional Tools](#additional-tools) 5. [Code Overview](#code-overview) 6. [How To Code That](#how-to-code-that) 7. [Coding Style](#coding-style) 8. [Contribution](#contribution) Download -------- To get the latest source, clone the repository from GitLab. git clone https://gitlab.com/goodvibes/goodvibes.git Additional repositories are available: # Unofficial Debian packaging git clone https://gitlab.com/goodvibes/goodvibes-debian.git # Flatpak packaging git clone https://github.com/flathub/io.gitlab.Goodvibes.git Compilation ----------- Goodvibes is built using [Meson][] and [Ninja][]. The build commands are: meson build cd build ninja (from now on, we assume that `PWD=/build`) In order to install in a staging directory: DESTDIR=../staging ninja install Goodvibes build is quite modular. Features that require an external library can be disabled at build time. To see all the options available, enter the build directory and run: meson configure For example, to disable the hotkeys feature: meson configure -Dfeat-hotkeys=false To disable compilation of the user interface (also disable every ui-related features): meson configure -Dui=false Additionally, it's possible to run a few tests: meson test # -v for details You might as well want to generate tag files for your favorite editor: ninja etags # for emacs ninja ctags # for vim [meson]: http://mesonbuild.com [ninja]: https://ninja-build.org/ Program Invocation ------------------ #### From the source tree (no installation) If you want to run Goodvibes from the source tree, without installing it, use the launcher script. ./goodvibes-launcher.sh The script assumes that the build directory is named `build`. Why do you need a script? Some libraries expect shared resources to be installed in some standard locations. To be more accurate: - GLib expects schemas to be installed at `/usr/share/glib-2.0/schemas/` or similar. - GTK expects icons to be installed at `/usr/share/icons` or similar. It's possible to customize this behavior with environment variables though, and that's what the launcher script does. Have a look at it for details. Also, don't expect any kind of desktop integration to work when you run from the source tree. Ie, the desktop won't find any desktop file installed in the standard location (`/usr/share/applications`), and various things might not work: notifications, icon display here or there, etc. #### From a non-standard directory If for some reason you install Goodvibes in a non-standard directory (`/opt` or `/home/user` or whatever), you will be hit by the same problems mentioned above: the shared resources won't be found at run-time. So please refer to the launcher script to see which environment variables you need to set before running Goodvibes. #### Command-line options To get a brief overview of the command-line options available, use `--help`. The option you will use the most is `-l` to change the log level. Here is a typical line: goodvibes -l dbg Colors are enabled by default, but you can disable it with `-c` if it hurts your eyes. Colors are automatically disabled when logs are redirected to a file. Logs are all sent to `stderr`, regardless of the log level. Internally, we use GLib to ouput log messages. For more details, refer to [GLib Message Output and Debugging Functions][]. Some of the libraries we use provide additional command-line options. To see them all, invoke with `--help-all`. For more details, refer to: - [Running GLib Applications][] - [Running GStreamer Applications][] - [Running GTK Applications][] Advanced GTK debugging can be done with the [GtkInspector][]: goodvibes --gtk-debug=interactive [glib message output and debugging functions]: https://developer.gnome.org/glib/stable/glib-Message-Logging.html [running glib applications]: https://developer.gnome.org/glib/stable/glib-running.html [running gstreamer applications]: https://gstreamer.freedesktop.org/documentation/gstreamer/running.html [running gtk applications]: https://developer.gnome.org/gtk3/stable/gtk-running.html [gtkinspector]: https://wiki.gnome.org/Projects/GTK/Inspector #### Change the language For testing, it can be convenient to change the language. For that, use the `LANGUAGE` environment variable. LANGUAGE=fr goodvibes Note that gettext will look for the message files in the path given to `bindtextdomain(3)`, so make sure that this path contains the message files. (hint: this path is logged when Goodvibes starts). Additional Tools ---------------- [GSettings][] is part of GLib, and is the component in charge of handling the application settings. It comes with a command-line tool named - as you can guess - `gsettings`. This is useful to witness Goodvibes reading and writing its configuration, and also to get or set some values. gsettings monitor io.gitlab.Goodvibes.Core gsettings list-recursively io.gitlab.Goodvibes.Core [DConf][] is the backend for GSettings. It's possible to play directly with the `dconf` command, therefore by-passing completely GSettings. dconf watch /io/gitlab/Goodvibes/ dconf reset -f /io/gitlab/Goodvibes/ [GStreamer tools][] come in handy as soon as a radio station doesn't work. It's often a good starting point to try to play a stream with `gst-launch-1.0`, and see what comes out. gst-launch-1.0 playbin uri=https://direct.fipradio.fr/live/fip-midfi.mp3 [Valgrind][] can be used to check for memory leaks, however using it is not exactly easy, so there's a wrapper script. It requires a number of so-called "suppression files" that are not necessarily distributed by libraries (eg. GStreamer), so you'll need to get them from source, and adjust the paths that are defined in the wrapper script... Anyway, here's how to invoke it: ./scripts/valgrind.sh [gsettings]: https://developer.gnome.org/gio/stable/GSettings.html [dconf]: https://wiki.gnome.org/Projects/dconf [gstreamer tools]: https://gstreamer.freedesktop.org/documentation/tutorials/basic/gstreamer-tools.html [valgrind]: https://valgrind.org Code Overview ------------- Goodvibes is written in C/GObject. The code is heavily object-oriented, and if you're not familiar with GObject, you will have to accept that some magic is going on, here and there. Good luck with that ;) The code is neatly split into different parts: - `base`: the basic stuff we need. - `core`: the core of Goodvibes, basically enough to have the software up and running, without the ui. - `ui`: the GTK user interface. - `feat`: features that can be enable/disabled at compile-time. I guess it's quite similar to the plugins you often find in music players on GNU/Linux. Except that I didn't dare to call it plugin, for plugins are usually something discovered and loaded at run-time, not at compile-time. How To Code That ---------------- #### Adding a new setting Settings are mapped directly to GObject properties. You must ensure that: - The object must be a global instance, part of the `core`, `ui` or `feat` system. - The object must implement the `GvConfigurable` interface. You must bind the property to a setting using `g_settings_bind()`. This **must NOT be done** at construct-time, but later on in the `configure()` function (implemented from the GvConfigurable interface). You must understand that the startup procedure is made in two steps: 1. Every objects are *created*. No errors are expected at this stage. 2. Every objects are *configured*. Serious things start to happen, there might be errors, and it's important to be ready to report it to the user. That's why the configure step is run *after* everything is created. #### Reporting an error to the user Here's how to report an error to the user from an object: - The object must have been registered using the function `gv_base_register_object()`. - The object must implement the `GvErrorable` interface. Then, it's just a matter of invoking the function `gv_errorable_emit_error()`. A few things to notice: - You shouldn't call this function too early (aka during construction), as the system is not ready yet. - The user message **must be** translatable as it's intended for *users*. - It's up to you to drop an additional log (`WARNING()` should be used). - The log message **must NOT be** translatable as it's intended for *developers*. Coding Style ------------ #### Code formatting In order to keep the codebase in good shape, we rely on [editorconfig][] and [clang-format][]. Editorconfig tells your editor how to behave, assuming that your editor has support for it. If it does not, you should install the corresponding plugin: # Using Emacs? apt install elpa-editorconfig # Using Vim? apt install vim-editorconfig Editorconfig only gives very rough guidelines though. In our case, it mostly means two things: 1) use an indent size of **8 columns** and 2) indent with **tabs**. For the rest, it's your job to keep the coding style consistent. It's not very difficult though, just use your common sense and try to match the surroundings. On top of that, we use `clang-format` (>= 12) to format the whole codebase. You can install it with: apt install clang-format clang-format can reformat a whole file, but what's more interesting is that it can also be used to reformat only certain git commits, using the command `git format-clang`. Here's a quick how to, taken as is from `systemd/.clang-format`: Option 1: Fix up formatting before committing 1. Edit and stage your files. 2. Run `git clang-format`. 3. Verify + correct + (un)stage changes. 4. Commit. Option 2: Fix up formatting after committing 1. Commit your changes. 2. Run `git clang-format HEAD~` - Refer the commit *before* your changes here. 3. Verify + correct changes, `git difftool -d` can help here. 4. Stage + commit, potentially with `--amend` (means to fixup the last commit). It can also be useful to format the whole codebase: git ls-files 'src/*.c' | xargs clang-format -i Just make sure to run this command from the root of the source tree. Also, note that we only format the code (`.c` files), not the headers (`.h` files). Header files are indented manually for better readability. [editorconfig]: https://editorconfig.org/ [clang-format]: https://clang.llvm.org/docs/ClangFormat.html #### Comments For comments, always use C style (aka `/* ... */`). C++ style comments (aka.`// ...`) are reserved for tool control (things like `// clang-format off` or `// fall through`) and codetags. #### Codetags For codetags, always use C++ style (aka `//`). Here are the codetags currently in use: - `WISHED` Things I wish, but will probably never do. - `TODO` Things that should be done pretty soon. - `FIXME` Things obviously broken. Always try to make it a one-liner if possible, or at least describe the problem in one line, then add more details on the following lines. Stick to these conventions, and then getting a list of things is easy: # TODO list ack 'TODO' # List everything ack '// ' Here are some links that discuss codetags: - - #### GObject C Files Layout If you find yourself writing a new file, therefore creating a new object, fear not! There's a script that generates all the boilerplate. ./scripts/code/gv-object-make.sh Have a look at the structure of the C file created, and please notice a few things. Functions are ordered from low-level to high-level, to avoid the need of declaring functions before defining it. So basically, you read the file from bottom to top. Functions are grouped in sections, titled by a comment such as: /* * Section name */ Most of the time, these sections are always the same, because implementing a GObject always boils down to the same thing, more or less: GObject inherited methods, property accessors, signal handlers, public methods, private methods, helpers. Sometimes we implement an interface, and that's all there is to it. So I try to show this consistent layout and this simplicity in the C file, and to have it always the same way, instead of throwing code in a complete mess. If you stick to these conventions, then it's easy to find your way in the code: whatever file you're looking at, they all look the same. Contribution ------------ Contributing is better done through GitLab, please follow the usual workflow: - Have your account setup - Fork the project - Create a branch for your work - Work - Create a merge request If you hack around for your own use-case, and think that other users could benefit from your hacks, feel free to share it and discuss it on GitLab. If we decide that your hacks should be integrated in Goodvibes proper, then you will have to make sure it looks good, with meaningful commits that fit into the existing design, proper commit messages, respect for the coding style, and so on. Please note that my first goal for Goodvibes is to ensure a long-term maintenance. There are more than enough software with a short lifetime around, and I don't want to add another to the list. It means I want the code to be clean, readable, maintainable. Clean design and zero hacks are my goals. Dirty hacks are OK to solve an outstanding problem quickly, or to demonstrate a possible solution. But they are not OK for long-term maintenance, and therefore won't make it upstream. goodvibes-v0.7.2/MAINTAINING.md000066400000000000000000000163661414415210700157620ustar00rootroot00000000000000MAINTAINING =========== Updating translations --------------------- Before every release, it's time to update everything regarding translations, so that translators have a chance to do their homework! First, ensure that the file `po/POTFILES` is still up to date. This is still a manual process, but it wouldn't be that hard to write a little script. Then, and before modifying the po files on our side, synchronize with Weblate and lock it. We can do it either via the client `wlc`, either via the web interface at , under *Manage -> Repository maintenance*. # Lock Weblate translation wlc lock # Push changes from Weblate to upstream repository wlc push # Pull changes from upstream repository to your local copy git pull Then, update the *translation template*, aka. `po/goodvibes.pot`, along with the *message catalogs*, aka. the po files. # Update translations files [ -d build ] || meson build ninja -C build goodvibes-pot ninja -C build goodvibes-update-po # Commit changes git add po/*.{po,pot} git commit -m"i18n: Update goodvibes.pot and po files" # Push changes to upstream repository git push At last, we can unlock Weblate and update it. # Tell Weblate to pull changes (not needed if Weblate # follows your repo automatically) wlc pull # Unlock translations wlc unlock For more details, refer to the weblate workflow as described at: . Releasing --------- #### Translation bits Be sure to update everything related to **translation** (see above). Only then you can go on with: - Update the translator list in the *About* dialog. - Update the translator list in the documentation. - Write down changes in the `NEWS` file. In bash, it translates to something like that: ./scripts/print-translators.sh code vi src/ui/gv-about-dialog.c git commit -am "ui: Update translation credits" ./scripts/print-translators.sh doc vi docs/goodvibes.readthedocs.io/credits.rst git commit -am "docs: Update translation credits" vi NEWS git commit -am "news: Update translations" #### Release - Ensure `NEWS` is up-to-date (check Git history and GitLab milestones). - Add a new release to the appdata file. - Bump the version in `meson.build`. - Git commit, git tag, git push. In bash, here you go: # Definitions VER=0.4.3 TAG=v${VER:?} DATE=$(date -u +%Y-%m-%d) # Edit the news vi NEWS # Add a release to appdata sed -i "/$/a\ \ \ \ " \ data/*.appdata.xml.in # Bump version in meson.build sed -i -E "s/ version: '(.*)'/ version: '${VER:?}'/" meson.build # Commit and push git add NEWS data meson.build git commit -m "Release version ${VER:?}" git tag "${TAG:?}" git push && git push --tags #### Move on Edit the file `meson.build` and bump the version to *something else* to avoid confusion. These days I just append `+dev`: sed -i -E "s/ version: '(.*)'/ version: '\1+dev'/" meson.build git commit -am "Bump version for dev" Head to the *Milestones* page on GitLab, available at , close the current milestone, and maybe create a new one? Packaging --------- #### Debian Let's move to the `goodvibes-debian` git repository from now on, available at . Make sure that the `DEB*` variables are all set: export DEBFULLNAME=$(git config user.name) export DEBEMAIL=$(git config user.email) For a new release, the only thing needed is just to bump the changelog: dch --newversion ${VER:?}-0goodvibes1 "New upstream release." dch --release Git commit, git push: git add debian/changelog git commit -m "Version ${VER:?}" git push Then, just fire the script `debian/build-all.sh`: ./debian/build-all.sh release The script uses `sbuild` to build the Debian binary packages. It should work for you if you have the sbuild chroots ready for the suites that the script targets. At last, a few `dput` commands will finish the damn job: dput <> ../goodvibes_${VER:?}-0goodvibes1_amd64.changes dput <> ../goodvibes_${VER:?}-0goodvibes1~deb11u1_amd64.changes dput <> ../goodvibes_${VER:?}-0goodvibes1~deb10u1_amd64.changes dput <> ../goodvibes_${VER:?}-0goodvibes1~ubuntu*_source.changes #### Flathub Let's move to the `io.gitlab.Goodvibes` git repository from now on, available at . The only thing to do is to checkout a test branch, bump the version, set the commit, and maybe update the runtime version. Then push the branch: git checkout -b test-${VER:?} vi io.gitlab.Goodvibes.yaml # set tag and commit git commit -am "New upstream release ${VER:?}" vi io.gitlab.Goodvibes.yaml # bump runtime version git commit -am "Bump runtime version to ..." git push -u origin test-${VER:?} Now, let's create a pull request (just open the URL ), with the words `bot, build`. This will trigger a test build on Flathub. Watch your mailbox for notifications, it's very fast. If the build is successful, the app will be available for 5 days in the test repo, which gives enough time to install it and test it. If it works, just merge the PR. Flathub will notice the activity on `master` and trigger a build. It can take a few hours, and there's no notification this time, so just refresh until it's done. Artwork ------- Both the SVG source files and the PNG files are versioned. To rebuild the PNG files, run: ./scripts/build-png-images.sh all Documentation ------------- We use the [font-logos](https://github.com/lukas-w/font-logos) to display logos of various Linux distros in the RTD documentation. This font is maintained and has new releases on a regular basis. So we probably want to update the copy of the font that we keep at `goodvibes.gitlab.io/fonts`. Integrations ------------ This is just for myself to remember what additional services are used for Goodvibes development, and how it integrates with GitLab. ### GitLab CI Up to date documentation should be available on the *Registry* page: The configuration is mostly in-tree: - `.gitlab-ci.yml` describes the different pipelines. - `.gitlab-ci/` contains the Dockerfiles. Images can be rebuilt using `scripts/build-ci-image.sh`, and run using `scripts/run-ci-image.sh`. See the usage message for details. ### Weblate Weblate needs to be notified of new commits, so there's a webhook. Additionally, Weblate needs write permissions on the repository. This is achieved by adding the [Weblate push user](https://gitlab.com/weblate) as a member of the project. I added it as a `Developer`, however developers can't write to protected branches by default, so there's a bit of additional configuration. The `master` branch needs to be configured so that the Weblate user is allowed to push. ### ReadTheDocs ReadTheDocs just needs to be notified of changes, so there's only a webhook. goodvibes-v0.7.2/NEWS000066400000000000000000000260641414415210700143750ustar00rootroot000000000000000.7.2 (2021-11-14) - Vanishing Point / Wheelfall ------------------------------------------------ Fixes * Crash when playing station from touchscreen devices. #138 Translations * New translations: Danish. 0.7.1 (2021-09-25) - Wisdom is More Erotic When Wasted / Wheelfall ------------------------------------------------------------------ Fixes * Add name of the station playing to MPRIS2 metadata, for real. #137 * Window decoration broken for themes. #140 0.7.0 (2021-09-24) - Le Visiteur / Lofofora ------------------------------------------- General * Relicense from "GPLv3 or later" to "GPLv3 only". * Add a second view for stations details, replaces the main tooltip. #133 * Show the stream type (HLS, DASH or HTTP(+Icy)) in the stations view. * Display name of the station playing in MPRIS2 "artist" field. #137 * Remove the "Autoset Window Height" setting from Preferences and the corresponding "window-autoset-height" from the Settings. Note that the height is still automatically set in status icon mode though. Fixes * Sample rate and channels mode are not always set. #132 Translations * Updated translations: Catalan, Croatian, Dutch, Indonesian, Portuguese, Portuguese (Brazil), Spanish, Telugu, Turkish. Under the hood * Code-refactoring to make room for a GtkStack in the main window. * Isolate most of the code related to status icon, to prepare for GTK4. * Re-format the codebase with clang-format. Dependencies * Drop dependency ``libamtk``. #135 * Bump requirements: ``gstreamer >= 1.14``. 0.6.3 (2021-02-25) - Far Eye Dub / Imhotep ------------------------------------------ General * Allow users to add security exception in case of invalid certificate. #128 * Add CSS class name ``goodvibes-main-window`` to the main window. #130 Translations * Updated translations: Croatian, Dutch, Norwegian, Turkish. Dependencies * Bump requirements: ``gtk >= 3.16``. 0.6.2 (2021-01-15) - Décollage / Thievery Corporation (feat. Lou Lou Ghelichkhani) ---------------------------------------------------------------------------------- Translations * Updated translations: Italian, Marathi, Russian. 0.6.1 (2020-12-04) - À Reculons / Flavien Berger (Rebeka Warrior Remix) ----------------------------------------------------------------------- Translations * New translations: Marathi, Portuguese, Turkish. * Updated translations: French, English (US), Italian, Norwegian, Portuguese (PT), Russian. 0.6 (2020-09-20) - Les Contemplations / Mansfield.TYA (Flavien Berger Remix) ---------------------------------------------------------------------------- General * Drop the migration code that was introduced in 0.4.1 to handle the new application id and the new path for the station list. #89 * Drop libcaphe, aka. Goodvibes internal implementation to inhibit the system from suspending/sleeping, and use ``gtk_application_{un,}inhibit()`` instead. #117 #126 * Use ``g_clear_handle_id()`` everywhere. #82 Dependencies * Bump requirements: ``glib >= 2.56``, ``meson >= 0.49``. Translations * New translations: Catalan, Croatian, Italian, Portuguese (Brazil), Telugu. * Updated translations: French, Portuguese (Portugal), Spanish. 0.5.3 (2020-07-09) - Bouteille sur Bouteille / Massilia Sound System -------------------------------------------------------------------- General * Add min/max bitrate to station information. #118 * Add channel mode (mono, stereo) to station information. #119 * Add sample rate (kHz) to station information. #124 * Check for memory leaks with Valgrind. #12 * Update default station list. Fixes * MPRIS status values 'CanGo{Next,Previous}' are never updated. #121 * Sample rate and channels mode are not always set. #125 Translations * New translations: Indonesian. * Updated translations: Dutch, French, German. 0.5.2 (2020-04-07) - Étranges étrangers / André Minvielle --------------------------------------------------------- General * Make the titlebar dynamic. #112 * Make the tooltip dynamic. #113 Fixes * HLS streams fail to play. #111 Documentation * Override the default CSS to create a yellow-ish "Goodvibes theme" (rather than just living with the blue-ish defaults from Read the Docs). Translations * New translations: Polish, Slovak. * Updated translations: Czech, Dutch, Norwegian. 0.5.1 (2020-02-06) - No Body / Goldenberg & Schmuyle ---------------------------------------------------- Fixes * Unable to play the AAC radio SomaFM (flatpak). #107 * Withdraw notifications when the application quits. #108 0.5 (2020-01-21) - La Ville / Mateo Langlois -------------------------------------------- General * Drop App Menu and Menu Bar, switch to a Primary Menu button. #104 * Add a window to document the Keyboard Shortcuts. * Allow to select the behavior of the Close button (ie. ``[x]``). #70 * Don't minimize the window on ```` (unless status icon mode). #102 Fixes * Allow to choose the theme variant in status icon mode. * Make saving the window size and position more robust. * Fix the position of the Preferences window (center on parent or mouse). * Fix the URL for the online help. Dependencies * Add dependency ``libamtk``. 0.4.3 (2019-09-26) - Uramado / Maki Asakawa ------------------------------------------- General * Add a minimal test suite for the station list, rework the code and fix some obscure bugs at the same time. #99 * CI images updated (debian: buster > sid, fedora: 28 > 30). Fixes * Right-click on an empty space of the station list doesn't popup the menu. #98 * No icon in status icon mode. #100 Translations * New translations: English (United States), Portuguese (Portugal). * Updated translations: French, German, Spanish. Dependencies * ``mutest`` is needed to run the test suite. 0.4.2 (2018-11-10) - Paris / L'Imperatrice ------------------------------------------ Fixes * Segfault on application shutdown. 0.4.1 (2018-11-10) - Parfum Theremine / L'Imperatrice ----------------------------------------------------- Outstanding! * Ready for Flatpak! General * Change application id to 'io.gitlab.Goodvibes'. * Rename icon files according to the app id (flatpak requirement). * Stations are now saved in $XDG_DATA_HOME (was $XDG_CONFIG_HOME). * Use GResource to handle glade files. #54 Fixes * Support lowercase keys in pls playlists. #88 * Add meson post-install script. Translations * New translations: Spanish. 0.4 (2018-08-18) - Sphynx / La Femme ------------------------------------ Outstanding! * Project moved to GitLab, good-bye GitHub. * CI is now handled by GitLab, good-bye Shippable. * Build system switched to Meson, good-bye Autotools. #53 General * Add an appstream file (ie. appdata). #47 Fixes * Desktop file: exec now accepts url (thx Elías Alejandro Año Mendoza). Translations * Updated translations: Dutch, German. 0.3.7 (2018-05-08) - Marilou Reggae (Gainsbourg Cover) / Alain Bashung ---------------------------------------------------------------------- General * Update default station list. Fixes * Fix notification crash on Cinnamon. #79 Translations * Updated translations: Czech, French, German, Norwegian. 0.3.6 (2018-03-31) - Birthmark / Deftones ----------------------------------------- General * Add support for dark theme. #59 Fixes * Fix ```` multimedia key. #83 Documentation * A new documentation is available at . * Remove the GitHub wiki. Translations * Updated translations: Dutch, French, Norwegian. 0.3.5 (2018-03-10) - No Ordinary Love (Sade Cover) / Deftones ------------------------------------------------------------- Fixes * Ensure ```` is a shortcut to toggle playback. #80 * Retry playback on error, hence resume playback after suspend. This is just a first attempt, error handling needs more care than that. #72 * Fixes here and there in translatable strings (thx Allan Nordhøy). Translations * Updated translations: French, German, Norwegian. 0.3.4 (2018-01-27) - God in Hell / Khynn ---------------------------------------- General * New icon set at last! Thanks to Hector Lahminewski and his endless patience - the icons have been waiting for almost a year. #51 * There's now a script to launch Goodvibes from the source tree - talking to developers here, users don't care. #55 Translations * Updated translations: Dutch, French. * Update pot file (I forgot that for a while). #49 * Document translation process. #50 0.3.3 (2018-01-07) - Psygnosis is Shit / Psygnosis -------------------------------------------------- Fixes * Fix XML escaping of station properties before saving. #69 * Add support for the ``NumberOfEvents`` key in pls playlists. #67 Translations * New translations: German, Norwegian Bokmal, Russian. * Add translators to the About dialog. #48 0.3.2 (2017-10-11) - Je m'en sortira / Babylon Pression ------------------------------------------------------- Translations * French translation updated. 0.3.1 (2017-02-22) - Where Dragons Dwell / Gojira ------------------------------------------------- Fixes * Fix missing status-icon glade file. #44 * Fix segfault when station list is empty. 0.3 (2017-02-20) - Pain Is A Master / Gojira -------------------------------------------- General * Make the main window resizable, and add an ``autoset-height`` option to the *Preferences* window. #27 * Add a giant tooltip on the station display area (this is used to display in-depth info about the current station). #24 * Add ``Add Station`` entry to the main menu. #31 * Add accelerators to the main window. #43 * Add GStreamer options to the preferences. #21 * Allow setting the user-agent per station (secret feature). #23 * Add a manual page. #10 * Add Freedesktop Portal support to ``libcaphe``. Fixes * Fix build on FreeBSD. #39 * Fix mute which was broken. #40 Gory Internal Details * Rework init procedure to report early errors. * Use ``g_signal_connect_object()`` pretty much everywhere. * ``GvStation`` is now initially unwowned! Translations * New translations: Czech, Dutch and French. 0.2.1 (2017-02-03) - Cradle Song / Tomahawk ------------------------------------------- General * Add ``play-stop`` toggle command to the client. #37 * Add ``is-running`` command to the client. * Add station name in mpris2 metadata. Fixes * Fix exec path in dbus service file. #36 * Some widgets were not initialized properly. 0.2 (2017-01-26) - Doom Town / Wipers ------------------------------------- This release will wipe out your conf! General * Handle configuration with ``GSettings``. #22 * Emit notifications with ``GNotification``. #22 * Load/store station list with ``GMarkup``. #22 Fixes * Fix desktop filename in mpris2 backend. #26 * Handle queries and fragments in station uris. #29 * Add a proper user-agent in HTTP requests. #33 Translations * Translations are now managed with Weblate. #14 Dependencies * Drop compile-time dependency ``libnotify`` and ``libxml2``. * Add run-time dependency ``dconf-gsettings-backend``. 0.1 (2017-01-13) - Positive Vibration / Bob Marley & The Wailers ---------------------------------------------------------------- * Initial release. goodvibes-v0.7.2/README.md000066400000000000000000000071141414415210700151500ustar00rootroot00000000000000Goodvibes ========= ![Goodvibes Logo](https://gitlab.com/goodvibes/goodvibes/raw/master/data/icons/hicolor/256x256/apps/io.gitlab.Goodvibes.png) **Goodvibes** is a lightweight internet radio player for GNU/Linux. Save your favorite stations, play it, that's it. There is no function to search radio stations, you'll have to enter the URL of the audio stream by yourself. Not very user-friendly, I know, but doing better than that is not simple. Places of interest: - **Development** is hosted here, on [GitLab](https://gitlab.com/goodvibes/goodvibes) - **Documentation** is available on [Read the Docs](https://goodvibes.readthedocs.io) - **Translation** takes place on [Weblate](https://hosted.weblate.org/projects/goodvibes) ## Installing You have a few options to install Goodvibes: 1. Install the package provided by your Linux distribution (if any). 2. Install the Flatpak app available from [Flathub][]. 3. Install a package from an unofficial repository. 4. Build from source (see below). For more details, please refer to the [Installation page in the documentation][installation]. [flathub]: https://flathub.org/apps/details/io.gitlab.Goodvibes [installation]: https://goodvibes.readthedocs.io/en/stable/installation.html ## Building from source *Note*: The following commands are for a Debian-like distribution. If you're using another distribution, please adjust the package names and replace `apt` by your package manager. *Note*: These instructions are for advanced users. Beginners should use it with caution. Goodvibes is written in C and builds with Meson. At first, make sure that you have all the dependencies required. ```console # ~ Build dependencies ~ # Build toolchain sudo apt install build-essential git meson # Core dependencies sudo apt install libglib2.0-dev libsoup2.4-dev \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev # GUI dependencies sudo apt install libgtk-3-dev libkeybinder-3.0-dev # Test utils (optional) sudo apt install appstream-util desktop-file-utils libgtk-3-bin libxml2-utils # ~ Runtime dependencies ~ # To load and store settings sudo apt install dconf-gsettings-backend # To play MPEG-DASH streams and AAC codec sudo apt install gstreamer1.0-plugins-bad # To play HLS streams sudo apt install gstreamer1.0-plugins-ugly # GStreamer audio backend dpkg -s pulseaudio >/dev/null 2>&1 && \ sudo apt install gstreamer1.0-pulseaudio || \ sudo apt install gstreamer1.0-alsa # ~ Additional tooling for developers (optional) ~ # To edit the UI files sudo apt install glade # To inspect radio streams sudo apt install gstreamer1.0-tools # To read the doc offline sudo apt install devhelp gstreamer1.0-doc \ libglib2.0-doc libgtk-3-doc libsoup2.4-doc ``` Now you're ready to get the source code and build it: ```console git clone https://gitlab.com/goodvibes/goodvibes.git cd goodvibes meson build ninja -C build ``` Then you can run the application in-tree, without installing anything: ```console ./goodvibes-launcher.sh ``` You can also install it: ```console sudo ninja -C build install ``` ## Credits The artwork was made by [Hector Lahminèwskï](https://lahminewski-lab.net/). Goodvibes wouldn't exist without free and open-source software such as [GLib][], [LibSoup][], [FFmpeg][], [GStreamer][], [GTK][] and many more. [glib]: https://wiki.gnome.org/Projects/GLib [libsoup]: https://wiki.gnome.org/Projects/libsoup [ffmpeg]: https://www.ffmpeg.org/ [gstreamer]: https://gstreamer.freedesktop.org/ [gtk]: https://www.gtk.org/ ## License Goodvibes is free software, released under the [GPLv3](https://www.gnu.org/licenses/gpl-3.0.html). goodvibes-v0.7.2/data/000077500000000000000000000000001414415210700145775ustar00rootroot00000000000000goodvibes-v0.7.2/data/goodvibes-client.1.in000066400000000000000000000007641414415210700205320ustar00rootroot00000000000000.TH "@PACKAGE@-CLIENT" "1" "July 2018" "@package@-client @version@" .SH NAME @package@-client \- command-line client for @package@ .SH SYNOPSIS .B @package@-client [OPTIONS...] .SH DESCRIPTION .B @package@-client is the command-line client for @package@. It communicates through the native D-Bus interface, which therefore must be enabled in @package@. For more details, type '@package@-client help'. .SH AUTHORS Written by @author@. .SH SEE ALSO .nf .BR @package@ (1) \fI\%@homepage@\fR\& .fi goodvibes-v0.7.2/data/goodvibes.1.in000066400000000000000000000025121414415210700172470ustar00rootroot00000000000000.TH "@PACKAGE@" "1" "July 2018" "@package@ @version@" .SH NAME @package@ \- a lightweight internet radio player for GNU/Linux .SH SYNOPSIS .B @package@ [OPTIONS...] [STATION] .SH DESCRIPTION .B @Package@ is a lightweight internet radio player for GNU/Linux. It offers a simple way to have your favorite radio stations at easy reach. It provides everything you can expect from a modern media player: multimedia keys binding, mpris2 support, notifications, and sleep inhibition. It can be launched with or without GUI, and comes with a command-line client. .PP This manual page is only a short reference for @Package@. Complete documentation is available at \fI\%@homepage@\fR\&. .SH OPTIONS .TP .BR \-b ", " \-\-background Run in the background. .TP .BR \-c ", " \-\-colorless Disable colors in log messages. .TP .BR \-l ", " \-\-log\-level =\fIlevel\fR Set the log level, amongst: trace, debug, info, warning, critical, error. The default is \fIwarning\fR. .TP .BR \-o ", " \-\-output\-file =\fIfile\fR Redirect log messages to a file. .TP .BR \-v ", " \-\-version Print the version and exit. .TP .BR \-\-without\-ui Disable the graphical user interface at startup. .TP .BR \-\-status\-icon Launch as a status icon (deprecated on modern desktops). .SH AUTHORS Written by @author@. .SH SEE ALSO .nf .BR @package@-client (1) \fI\%@homepage@\fR\& .fi goodvibes-v0.7.2/data/icons/000077500000000000000000000000001414415210700157125ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/000077500000000000000000000000001414415210700173515ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/16x16/000077500000000000000000000000001414415210700201365ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/16x16/apps/000077500000000000000000000000001414415210700211015ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/16x16/apps/io.gitlab.Goodvibes.png000066400000000000000000000015501414415210700254000ustar00rootroot00000000000000‰PNG  IHDRóÿasBIT|dˆ pHYsììy(q½tEXtSoftwarewww.inkscape.org›î<tEXtAuthorHector Lahminewskiì“l•ÀIDAT8•“½o[eÅï{}í8µã5iÒ–EU Båc HŒA(Q¥@§Î¬ü ^3D‰ä?€ÉC¨B…X¡RJqi‘¬´Eq*§¶sãä^ûýdp‘ZEœí‘žs¤ß‘Žà¨Z­Ê8ŽßµÖ®8çê›››÷R§™Ö××óZë1+ÖÚå òÖÚï1ÛâE¦Z­6'„X²Ö~¬µ^ÒZ?UJÝòÞ×étvjµÚ1àÿo€ØÚÚú “ɬ¦ÓéeçÜ‚RÊ+¥î‡Ã›Æ˜ß­µc¼R cLsccãWÀ‹jµš*'¾^˜ú{ùÜx!ÞYœ6X£±Zã´ÝZa•¢u\ä—góßÜýãáµpý­™?—/7FDÞá­ÅkÍáÔšäÎî^H*S=®½Öäjî“©ÖÊA÷íORZë+åR‹›½çxÁl0ëÓ|QŸ`‚ÂÔ†3|Ù¬ðÙå:•Â.Ƽùº<99 Ò’H†R@˜âÛûWÈŒOR(Ã0 Éd2(r«ûiidj0ˆœð¬ÍæAJ¼1x­ØÞ/06&Èçó”J%Â0¤ÝnÓï÷¹½+Y-Xkej0`†C\¢Apo ­ÖÓ3Èårôû}¤”d³Y”R $ÖKdÇØ$æ0:ÃÎÃ5šíK¸AÂ…RÄ`0 \.S(RâœãlºÇX GÖZo’„ýè,·ïw¸÷ä.9áú;Oè>.»±þ,FFF^Îd2G…Ç…o)¥J)%¥¼Çñ¥8Ž/œ>}zjã¾}.×¾Úè—“Ëå>J&“CÉdrã8XkÑZ£µFJJ)#­5Æ6ª~~·J©™(оŒJ¥†³ÉúÇý?ÒÕ²B&eÀZ0ŒÆ(Xm”Ä*E+£@ÆX% ëšGµü¡o:uêTßùóç?CCC=»z»fNìÿÆë*D `±Æ€1X¥°ZQ]ƒË“Û¹ñ Ïãð\ÁÞΈ®‡l{*¦Öð9;õ®¾3»Öë%‰ý…TÉKgcîVìßí2O:t¤”“œ½¸ƒ?W ¥¥…žÞV´ÖÌU«ÌÝí`¶ÞÍÉ—¾'›ˆÙ—q$v¾æI)ÓA"b"4ÜÐú%÷Ôï¥}¾¸ÚÇrMÓÓÓ¹yæy…Bc Sµmü²s$óùD cL΋¢È1F±·5E"4×Áêfo;}øu¾ƒR% ³3 €ïû8Ž€”­5×—p ùB¬µÂ«×뎒½¾¼ÖnôVI.Mï"²Ù,®ëR,Y__guu•(Šˆã˜Òr•{ÙvŒ1ŽÇ1Z¹X)A¬`-Öh¬V,.7S©AÇ1žçáû>®ëÁfåk*h*d­ð´Ö(i°Öqš?ÛÖ`µ&ª- uË °û÷ïcŒ¡­­0 q]·ù"7¤¡2M ¤”¨L±ª:¸¶ð>FKÚÇØ–XaOû*×þHm"—Ë‘Ïç±Ö†!ÏLãØˆÝ­K<]Øk­ÑªYñ£Zwfž ¥¤7ÙK¾å1‡»K\.XXXÀ÷ýM14 Â0¤Z­2Ðöi¯Ñ´àc¢êº‹‰" ò/iM[¶éIl#Â7™"E™ÙÙYVVV(—ËT*Êå2KKK¼Üáí“Ti¬µ¡W­VoO/õ˜'Ë8-s|°ës„GXL£iëAO~f|ºÛ‹u–TX:’Ë ïñzqGXÚcòi·)—ŸÜ€sìØ±±Wû²'OîûÝÛ+¤|ÝœÖb통õ³¹¡©„ |“ñb”qxT+pa®ŸëÓ\¸råÊ 8žçîïïÿ¬X,L&“íÖZ‡-„Â4ÅR©4qóæÍ¥ÔÕççqÈ)`K`ÀÐtQ¨n1wkñ,=•ÎJwUýIEND®B`‚goodvibes-v0.7.2/data/icons/hicolor/24x24/000077500000000000000000000000001414415210700201345ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/24x24/apps/000077500000000000000000000000001414415210700210775ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/24x24/apps/io.gitlab.Goodvibes.png000066400000000000000000000026331414415210700254010ustar00rootroot00000000000000‰PNG  IHDRàw=øsBIT|dˆ pHYsbb_'ÐStEXtSoftwarewww.inkscape.org›î<tEXtAuthorHector Lahminewskiì“l•óIDATH‰­•MlT×Ç÷}Í›±=þ†`„‘ƒC(¸…Òª•"’¦M”ª)¨J£H ûîʾ°bME@‹¼ê"K¢~)M»ÈTÑ4HAuLcbb06¶ÌóÌ{÷Þsº˜ñ4!ª”Òé-Þ{çþÿçãÎ5üíĉÕ$I^ôÞ_?{öì@ô¿‚žø4áGC Ð&Èók-êl«©A» ¢-Õ8áöUâ8&MS*• EQ¦)QaŒ¡R©lÍ ^ órgÀ"ï=®°hQ A€i«HÛ*Â{Ö×–ÓCEÐùùy‚¶ÿÖwUmF®ƒEuZä|R?ÄÂæSìîºÎÎò *Âøö&7>QUT•ÞÞ^Êå2"B’$ÔëuTçÃ¥ƒiÖAàœÃRä<\O¹<{˜ë3ïÞ:B½nÑ"çÈSK8çȲŒ<Ïɲ c aÒh4(Š‚¢(h4|{øãÏ©,p>@óò:kk«ÔjkÄA7¦h"â9ôDƒgÇ«üõ“¡¯¯(jíIk-yžS«ÕØUºÃ³#-§a§«‹ëe63KOòï¼Ã|ïnöôü“D›h. ÂñCӔ̮Ì666HÓ” :û«·xcÏû$0_ÄÚfÍ8p Ù¿ÿ‡/=½8þú×g>³`Ú5GZ$ê…;µn®ßßÁB½Š* •Ö88x—½½‹²¼·8Ư?üÆí›7o~ÅÁÁƒ²oß¾_LìX+~âýiãßEÔ6‘(¨´ß¼ðèÙ­põÞ®æôô͟ݸqã‚`tÛ¶m¯MLL¼R©TF€ð ;ã˙ϲìþÔÔÔï——— ÌùÉON¯Loc¥êP‡^®tÏ=÷l´,ë.;(¥¿ Bq1yÐ}Æ,Ëú€ß[n×ú±R u¨C/š˜˜0òùük)¥o°ƒr ”êÍÞçK¥Òðg?ûÙl›»-¥ŽС… |àý”ÒÛì, ; !=!¥º‹Bh]‘Hävßi÷dÔ€uHA÷ÜsÏzJéNBÈJé­L‘Á[Tõ}IÓ´»Ð€uheibb"V­VÕ²¬~ÀÚ0 Ý,„¨ó.ÔÌóæ£êø:ôЦ‰‰‰¡r¹|€îZ²Ý›ºÛ¶=é„vó§>õ©§Ã¤5êhzEÑÄÄ„fYÖõ–eÝ®iÚÎJ¥ò+„ÒÛ=ðž1<¥”R×=c|MÓ@!d€t¨CK¥‰‰‰tµZ}¥ô®jµúCš¦-Ùvo•éYêL˲ ë:!o0±Ä¡R:ô²¤}èC›!wØaYÖk !1Oluñé<Ë@ ÊÏÚ „\ÓÄp[¦tèeA¿÷{¿gŽŒŒü*j´®°,R=¬”gùx[_Õ'žùëéf3ão•:С˖>ö±õ‹ÅÛ4MÛI)Ý  g%–éxÔxƸüŎ8'ࡦ;Õu C—MLL¬'„줔î(—˯Ó4ÍX‰e:^Ê5©ÎÛóa¤¼_ù´:}[9 m¤tè%M÷Ýw_<“ÉÜF)ÝQ·é׈yVÚÇ3}«’žo‡µÍ­œ-—Ëÿ³éÊZ tè%GùÈGÖPJßB)Ý‘Ífo#„t•i—ª/SíˆZ€_}~ísRÞš¦ÍRJßö™Ï|f&°³m ÎF ]rš˜˜Ð Ãx¥tjN¼ë€Ö6×4[FÜŒcYVSR>Lg×»˜Ÿ¥Õ%ÿP©Tî¹ÿþûO{¶LÔÑ:tIèÞ{ïM”J¥ÛPÛ·À¨˜çR:ðÂHy¿þŠRž§æÀ¬mÛkš¶Û4Í?ñ‰OÌ5=˜%RGèЊÑÇ?þñõ¶mßNk/ؼ @d9¤ºìÞéƒTû°O25¿NÇ< à¡L&óä¾ð…Š´‚¢thÙè›ßü¦~ìØ±[,ËÚAÙ àêv0xPÙ2œŠé~Ø{Þ–gaMÓ à'#„|{×®]=ƒ¹„Ô€µ•>ö±õBn°³¾\çY›_)/[¦ ²å—bÛ R·å`šRúMÓ¢”þÓ®]»2ž¿D¨Z2}âŸØZ_¦»Àë(¥ßÒr:ðdL/ÚòbyUÝü•gp?>Às”ÒÇì¾÷Þ{Œx•·Ô€5M÷Ýw_ܲ¬×ض½ÀÛ¬YI)ÏÒT[nÃØòA÷~ _ËSJŸð¥ô¡]»võ ø2 t(}ô£6Móͨw§mÛI1ÏJ,Ó-…éÃÚò<ñª>!ä¸mÛiš¶;›Í~ÿþûï/yx™Q:$¥‰‰ -™L^Ok{ìw¸Rêy^VÚ¸íù¥Jy޹]¶}=Þð3JéC–e=¶k×®e?¥©rèÞ{ïM¸­~ÅNJ©gm~¥T{–ÙòÍzíei2I_¿ÎPJŸ „ì6Mó»óÊÊ^ÔÙô §O}êSë-˺½¾L÷&J©óÞ<¿ƒóc0Y¿}öŒD©.2=‹7,ùØò ”£”> à¡ .\òµù•¤Žð £o~ó›úäää-º®ï ”^-æYn)ÏîÛåÀóKãU|.®hÛöO4MÛ­ëú·&&&&ñ ¥¼蓟üä€a¯¯Ûóo¥”v‹yVÂ'Jv?¦opx[žÝókó*•J/éµù•¤¼Lé¾ûîÛªiÚBÈí¶m¿‚¹×—Åù1½ÌÁ–éýžÝ {›RºGÓ´Ç,ËzøÃþðe³6¿’Ô€— Ýwß}qÃ0^cÛöNMÓÞnÛ¶ç½ùå–òìÞÏ–—ÕVí—‘ Úç‰ÊoY–õSÛ¶Y\\üÞÜÜÜ™J¥"/Ó4¥‘ŸÍf•i— ñcX *‹ôk_ûÚRO&A@6÷Ëú÷XNh†ñŰ3艉‰ÑÞÞÞ;t]‹¦io@ýàKÃ6#íýL­ÃËÂü=¿ Ì›{²>,W¿Ãæ[κWº”Ò,!$kÛv–r‚Rzضíý¥RéH±X|úK_ú’ $TŒ®‚eå™ú£R÷]yo¹å}çÎ7%‰7ëº~€H•µhÛ«4Ù=OÍlm÷C&ÛÞ룴Ü÷ËÙVºî6ôÁ²mûyO–J¥íß¿ÿñ'Ÿ|Rvj1Â+íã‹ÞÓîÄÄÄ5ÝÝÝ¿mÆ¿0ÈKóV¹ 5»ô&-L>ƒ«öý·Ún»úºÜù.‡ºE€fdÛö¥ôËåòŸûÜçþ^F_q h'1¿‡ño¼ñFã7ó7ÿ­®ëïÖ4í?U^ÅÈ~ü0 2FY ðKkUÕ¿œ™(L>¿ø•êkËÀ G«Õê_>ÿüóû“Ÿü„× xÆçï—Ú2 ¯b|òº×½ÎܹsçÿeÆŸXϘv)v}X¦oVê3ºÔ@ zš­ÿrÊ·œÌߎ>øåS™n’úÎY–õ©'N|ñá‡ÎñIpkËK?©/SýññüÖX,vŸ¦i[ƒ7ã Sƒ,¯*.(-¬ŠÜìýr_ÚË%Ÿ*ÏK±¯<Óû])¥ç«Õê?÷¹Ï}E"ü`I ÐNP1?€÷¿ÿýÃÃÃéëúÛÖÕü0LßÌÖÞ0ÔÊÃt¿ ±’u¼ó­DdÚ€~¼X,þ¿ý×}^F|ZKÔÜkon™_®Nø#ùȽ½½êº~+ :òÂ8óšuèµÊð~$ƒ¥x¿ÜiÖ*–³â3ÌÇ×ïÇ Ãø7Þxcöé§Ÿþü÷Ë Dš/µZP´ï¥ÒÿöÛoÞqÇ÷éºþËœs~Þü°f@P\˜4…}@Tq/e ðKë€ÄÊi~mÛÿpìØ±?øÁ~ né¶¹ë’4V€/ÃK{óÿñÿñàèèè7 øEÅðA{ð[eú•’üªø— ø¥u@¢}ùÃËÀÀ¶í£sss¿ño|ã àRýÛÍrJ(æ¿çž{¶ ~Àìp ©¯Ú‡ßU©ëýaÒ/%4“÷¥Èàí¨ãåÏfr¹Ü¯ùË_~š©ÿ– ÍpFæ'ï}ï{·ŒŒŒ<`XÓ4Ϻ|³ª¾*ŽRŠ™™LOŸÇÜôIÌÏMÁª–¡ ØÐHØ£³‚ç*‚ëó-Oê÷¯ŠéTyãÎæI¢Ò é­>¼f„(ŠPvá=y›™Ûú S>“+FœEuPúUGW²Ñôº{N§Á^« ¡Û¶sårùß}éK_ú>×Ñ%ƒ@XP9üXÈ?øÁk{{{&„ô1ÆV9ðZQõ'''qòèó˜»p©h›×l»‚àªu&ÆG£ˆ˜Aà ’ì!Êø=Ô/^ŒÊÓ8õ¶%ÖÏÝSiš$,¶E©¼Ò0U·é Ëú!k‹++‰£ªúm8 %ë¿$ÎU¿l B;”‹egf)ŽLì;ÃÁ©(.» Ç‡Ð3²#££©ßÇü,¾\(~ãË_þòcŽº~Mƒ@³ õòÀþán¼âŠ+ž „ô‹’_”òâ= ™™ì}æIäæNàµÛ(î¼%‚«®ˆ…ì¶‚§ÅËÀ4 ]/g|Y9ж’)ÅþHÂÔ·m9ƒP®üŠ€@P“6†"Æqõ{âÔ`Ó÷˜x:;<~0‚G¦PÔ‡1ºáFôõõ7eðÌÏk²‹‹‹w~ík_{^æÜ Ðyü5p@ð[¿õ[½×\sÍã®dŒÏk~WWC\܉ãÇðüÏ¿‡-«óø;âØ´&¢«­’džõÔSÈA€w‚);´H¦ú»®ï~÷»ÿ\×õw ¦ÚÛïi„äóy<úÈW1¨=‹O¿'‰[¯#b*½jæ§*HÄ_F–Ï›Ö(ÎçgA1ŽpqêûF3,\?ÀååÆÅ…ň“,»Û®ý×&¼í+ÚóÔïÓf£(q_=íqÅ]qþm¹ò{êw·Cøžù<Ï’P·„4l³ð×ç07=‰?w ]=cˆDjš­ŸV  þþþþõ{öìùª O~½æG-ÚýrÏ=÷¼¶¯¯o7!D÷Sýƒœ|û_x“Ç®ÿ”Âú±ˆÐ3ÿ‰mQ×E™.Í#+Ë\U¾™{ ¤ÍÊaÇ×W§~íù:—KhB]ÇLKñ(ÆÓ¬ÀÓ\øï§qÑØ†ñÍ7p]“¯ ð&A±X|÷W¾ò•/Á-ùC›~ã|˜í;á‹J2›¯„=¢.ŽmÛ¶MBFdÒu˜O‘Ífñí¯ßÿòÖ‹øðïõCsZp3 cN ¡š*‚ùá›—ýcŠøsM…˜Î¥©€Â‰ãZtªæ_¼R³ÀÕ'o˜¨˜Óv39áÂ~qí×´KêõÄ §VÕwòñlE¾ð wn¯â/ßv“Ïþ#j§‡5³Œú”»cžÇ]Õ[y$/JyÏ{ÞsýÈÈÈô »_&ùçæfñÄ?ý-¾ô_ÓX5ñé]s“¹d’ -åCR,•Å‹q¢‰@…lêt©zÎ…ÿ}U|oXªF‹m©ê¾Ž3¾&Ͱêz5Ý[·;NY÷ ™ŒŠàÝ_OÃ\};úF„>4ú¤ú•J¥w>ðÀ»á6D‡ §c2€G MøÚÎ;¿BY#c~™Êχ§§Îãéý¾:чÁ^“‚~R˜uG’§ÕŸ r$ù8ù-h \¿ãø¾»ï‰#¡‡×ü=×W•6 Ó¤áFÿ¤ª®sñ‘ÒN?„:ChÅŠŽ¹¼‰©L³Qœ™b*ÅÔbÓÙ(re™’‹š ÝÙe"¯Ÿ{ .C3R›KôäókC¸iAp:ð–í%<ùìi,T{O¤]é23€×¶u]U±Xüë‹/Z>ÍÈF¥ÌÄK Çß»»»¿ÅÛýa077‡Ÿþðoñô#õ±>BÏ_3Ý*‹’ו& ²"^ªiRÀ_àÃé¨ P®x“RÚÕV£î‹“³1šê‰‹q\ÌEQªh¨Ú„h ZM–X´”jÐH]ºB'6(µ@íšð2uÉH#é6ö/`}kzòˆÏ÷ŸrÝ –þÞ¥Añêk‡Àн÷“È÷¿ ½ýáµ(•Jïyà¾X¸…Z€L̲«(ýÙ»ý†qà^ö :‹?—Ëᇻÿ˜èE_Úh4ãËÃ>¨ÝN¢ž€$O0 8lèÁ)emr÷¡×ïźƒÔtŸp+ `S‚ÃçcØ{*‰çN%1Ÿš*ÀBº®£Ù/-ˈR ˲`[UDH e褊ÕÝyÜ06‡m#sJ–Ô}–X(3#ÌŠƒ.²´&ɲÿòÍn`ìN¤Ò=®ypòX–Ç­œzì±Ç®;}út}2 PìT€Lúkò'òº¾¾¾ÃóžÕ¶_ ¶£i÷?~_ú@Fë6¿—B‚ªçaÉó7ñczEº2ž“Äܽ« UÝ a§¸ 0Ô Ðè¢,ÏÒ@ WÒðÓ£)üË¡nœ_ŒZe;ŽH$"Õú–“ªÕ*l«ŒòÐh›‡qëú)lZ¬½ ¦Ok„À%¨Þ&©X~÷+ýÞö6˜¦éJóó‹Åÿüõ¯ý+p€R }Œp?N§G숨îËV~ôÃïâ½ï2æW1|X©ßâÃFÄ? WÈœ¢ÍGUñõ‚ ¹{‡i½ QÝ a„P¤V=ë7%õ>®?Þ0­éؾÕ*ªµã„}!„‚:õ×Û"媎§'ñè¾>ÌäLX$͈C«›q˹IÛ Ã @l{g±ïÂ(`°i`w]uWd¸ñÀ=dðs)<Î4:c³‚¬"w¾Z2—WUoHŠ™À®·ÏàÝß~›®³ôQ$ùCp½c?®cîî‹aQý×hwß}÷ðu×]÷"“—þªFGìGwù üùìo’á‰;´\ÂÅ%”UÒ^’&Ó¤q~Z¿à•Î|> €…ݤÊ+×&g¢øî3}xñL,-¢wI5¼—"U*èV)âõëÏã›Î"aVÑЮ–¢ða÷ß÷Þè{û |õÐMX5~+^”üü&¡¹¹¹_}ðÁ÷¢&ñy-Àc„ÕȦM›ÞÀT1½Lò—Ëeþ壸î®a—g˜¿¸›U9P}¸? 0øý HMZº*ãÿÎěֈçãÍ@¡PB¸ü5Q:ת%¶•@£mB)(/õùö]ZA-ŽR‚ç&»ðõÀB!† IÁŒšK:<òRiš€ÙÀ÷÷à±£cØ28‡»·ÇP²ØìlšÒ ÍW ¶O €;¶Uñ£Ã/ —߀®®¹@qÞL&“ï°—ë/Ü]áÿ®<Ó{~·ß~ûG5M[æ(/F?~â!üù¿§uÕŸÀYs…Æ+ÄýãË8?Òøyuð?I}BþFvaû‘§ŒÐ''Îë¾>êF?XâÒ‘„Y³.•…Ýí.ÜhÃG)ÁO$qïëðÓc(¢Äèj‹ïR“¦› FçóÝøç£½Øw¾û3HE*îŒDœ?~ÅZÝ÷ßA–¿uºy]ßø— º‡Ö»âUæ!d|vvö¯yi/E!x€àíoûȪU«>Z«[­öóá ¦`ä†ßyk¯‚é% ïbNƒKX(£dt.Ÿ$$y@pƒÈøîC+ûsxÂlþ¸yóFØž›Là®ÂÓ'PÕz¡ñwè­išb&0[JãÇÇz±:Í ˆG$Kç¾–€§,¿˜pÿ[ ˜ ”K¼p±]Énpüo,̨Nôõõ=yðàÁSp3¿œÓ|$Wç·eË–7sy¥›X<ûíùÙ÷ðßþŸ>4ZÙVXù$-ÜL­æjI|òª€B6\¼ Ä1ÈîÕ@àNó 7úâšK%ða/œ›àÏþ~ Ÿ}l  ˜éËÆÆ_ †Ä‘ì8þô±›ñÀsP±ê²Ð#ý]œí%¢È«Êß"½óU%”§÷¸Öý¹é ±Xì× gWgƒ|Ã0^Ë#ŽèõŸ™™Áæ±<úR ®âºxnÄ|Šlí›UÞé#ëu§IÂÙã5s]´Ï!Ü×Ë vz­Z"ø8[Ýã¨ÅÕŠw»¼·šüÊ¡e‹àéÇS‡R°õÙc°4², Åbù|Þ¹–Ëeض][¾³íÚÚ¾mƒ-+†MÓ ë:LÓD4E<G<G,C4m«fb&` á'g“øùéAüö ‡pýèL#C}ÚàLeÀŠ€XŽ¿Y¢/@#À;¶Ïâ¡ó§10¼ZºA€ã 0 ãµþG½j@O‡ù¿¼ÈøÎ=ÿAÖ˜ø‡à;ôü/¾Ï¿·G"|î=·è°ù=t]ÔÈÎ9èœK0 ´M „Ö—îsN@€gx8ç¿7ˆ¢‚íR¾ Ö,•J%ÌÏÏcaa‹‹‹( `[Ueï‡6+lÛF¹\VÖÏ@¢«« ÝÝÝH§ÓH¥RmÑXŒHlÇŸ‰âŠîYüçW½X÷w°Ñ9xæ4È· IDAT¼ád”äo‘Þz]ßúÊ^С1W¼Âpóøøxìøñãy®—¢6 Õ\™ï¼óÎ~Jé:?†çÓŠÅ"z»0Ü'ñü;AÓ«$?qß¶L^ „ò!<€ š! (´z”€À´2ù>{ «6ð·Oöã§GÒ€ÑÝ\ãPJ±°°€‹/b~~…BAšO|?„=7âöq>?_N|¸s¹r¹Ξ= ]בL&ÑÝݾ¾>D£­ïH „@÷ãd>…>šÄ¸ñ®ñj­i¾‘¡I×€[Çgñó…ytw÷¸¤¾hˆnݺuÛñãÇù¯ y:b(¸Í›7o@$ 8ÄÇÜ¿¿}W—¿ôw‚j 2{›SΆŽé½ À‚ \yÜj¸?ø…i‡Ýन@àüœ‰ÿñí!d*)è‘®%ÍY>ŸÇôô4¦¦¦P*•” ¬i‰’É$Òé4‰‰t]G$®ëŽÊÔvõY–åü˜ÙËåÍfQ(P,6Øó˜Éd°¸¸ˆ3gÎ ™Lb``½½½-¯^èFTÁž1qÝðyüÎ aA{öãcNÛò. ú” I¿~}?¸éô«8•_À4ÍMž†Dò3R™ÎÏ0Œu@ã«’üŒ.ž}¯½¾¿QcH¦÷ÔŠÙEiíCÔùÏ· (+,2º‡ñ¯<Œé§ xL‚z{¼/@¡ˆ ðóÃ]øâ£}°ô^fë¶þôô4Μ9ƒL&SŸ:÷ aèëëÃàà ÐÝÝíÚ,æ'@XyÃ0œüétÚ“¿Z­bvvsss˜™™A>_ÓnYÝÙlÙl'OžD__FFFÇ]uNåÄ%‡ñülúÃ4Þ÷š=è—}ø¸m 5H=Ú”cZÉv2Òu}£Ð Â…)ôD]ׯ`ËÔ3¾ù|ëG-h:kƒkNrïšz8°„v~eýhHYÀ uIì^-¯er3>Ð`P.Í»­×+õö¥y$R_¡BaÛ_ùQ7~ôbZ¤F Ú“mÛ8þ<Μ9ãØôµnÕ®Ñhk×®Åðð0úúú¤‡¾2Û½¼D,?6#Ã0044„¡¡!5@˜™™ÁÔÔfff\»áPôôô`ttÉd2<ó×IïJ#_‰áÏŸŒànþ%6ÌÃíàç]6¸MË68àWfðÐô zûú8™S²rÁîd€ˆa]×׊Ëœ€pòÄü›×DÕ1¾”)ý¾M`¸5q,TÏÀ€øíïç5–Æk ‡Â `} u&§2©/ªEpïwúqìBz4ÕüÔPŠ3gÎàÌ™3(•J®4Ã0°jÕ*¬^½ÃÃÃuñ¯ŒË˜š'Ù¿ü¦ibtt«W¯F©T¹sçpöìYd2èºÛ¶±¸¸ˆÅÅE¤Ói¬^½©TÊÕž¬<éz–¹÷ÿÂÄÿyõ~¼víÙzF4g´Q xÍÆ2þñðI ²Ý€ iÚš ºD@ ½|™)Àè⹃ø•kSRÆwÉ{ÏÄË;ˆÙ‰ï­Cžnrv_H0p1¤. ANà}J@pkµd‰Ôw.‚\øo_ÄL> ÝlÞÞŸŸŸÇ‘#Gõº6Ñh›7oÆøø8"‘ÚK]~ ­RýULÍ›L’û=k|9Û¶aš&Ö¬YƒÕ«W#“É`rr.\p™ÀÀÀÖ®]«tÊ@A×u˜ýkñuLecø7WãøYÔNo#ã3Jë¢r)…)¥I4¤>ß#‡ I$¯*@Ó´.ÙᲉ2µ,’‰A¡)Ù.4Ž <Ýö#–‘ºo=é|6Î÷Ä«Á@ ¼Í.SÝ0ç\ /Xø‘"ƒËdóc|.^Ôü€@¦(µÑ$浸L€…œŽ=0€‚Õ Ýp¿?DgÏžÅñãÇ]vºišØ¶mÆÇÇëCV3' {3-H3àŸ)Æœ*bùy ÁÏ©Èò' lÞ¼k׮űcÇ\Á©S§0;;‹7"‘H¸ÊúQÏàìÓñùgtüî ¿ x ëcf´‘6•ðT>ïê;àáKÑþA€ú}v‡ „(ÿÊ¡ qÔ&sÆé½ ÄÍì®ûz˜çhâãOî%ŽnÂ+)MþøfX½¼–Äê'|œêž—3a˜üÂÄ)îª[HwÿÙøskQ™‚†}e¹j/4=<ó[–…ýû÷ãÈ‘#RJ122‚;î¸7ÖœÈ~Ì)j…~yùü2/vP9Þ¤Ó'ÆÌ‘H›7oÆöíÛ‘L&0Éçóxá…0==íœ^Äv"úýzÖàH~þ÷ž­õùgš¿(Ph…§-Ã%äs νbN˜ È=¼n ³.ä)(›ü¹Ù‹¸õFS.õ}%¾÷Þ ÂD ˆä{ìÍëŠâ¼ýކÀi¢V š¼Ô§uĵå¾LgÛˤcu _þô~äíÞÚÖÖ”Éd°ÿ~‹Eçï™J¥píµ×bxxØqÚ…%¶¥7ˆ9kÓÕx=È1ÈòóyÀ†LéîîÆu×]‡óçÏãĉNÚ‰'Éd°iÓ¦Ú!#É|=«qàÅ¿´ðï¶hüÍd$Mó+Lkû)HiÀ*¿ù„œÀe@þëG¥ÂÖ ™¡ß²)~¾7ƒ‹ó'M œ"XøÜú’´ûÔ?iUùÚMOÊÀÿqM ¦Î4f„¢ð 4²,‚‰¯ö!SnNí?sæ Ž;æb¦M›6aëÖ­ŽMÉ/©Ë–9Åraó6Hª1B0::ŠÞÞ^:tÙlš¦ann¿üå/±eËZ­¢‘uxþœžÃ%ܵé˜7ÇãíÞ4˜h9fyéïᘠ­ÀRý‘«•’ ®:Ø6ðG»Ž`ïT©‘h;µ¢e¥VþVÙéÖ?dà}h# ©öM€€ëaqƒÁ'¿ÕÙ| ºÞáwìØ1œ>}º6&Jaš&n¸áŒ”ô'ÕÒ°_~þÚl¹¥R,ömÛ099‰óçϨ½Ï°oß>lÙ²½½½Ê²|Ç6â‡'ËJäqóèyˆO§/‘ñ ÷ÞŠVæF¶ÀõWäí¿/¬j É„ä•JÁî=r*gOäqÇÝ͸\Ø¿5¢xt×QüòP×oIx>¿Pñ¤­$$ú"XÈTkFŽg>qpG7œµë³G#øÑ¾4#)´×ð°óN+BöíÛçbþ5kÖàÖ[o•2¿êÀÿ±6Ÿ_ö ù üa©™þôööbÛ¶mˆF£ÎkʇÆÔÔ”kÙY˜ü½Ó0ºn;>ýÌÍÈ”›[~] Zh“H©Ñ~w¦ø"/¥ÐuîAujh<Ø„h:Á½‰6u<óÇ È/Tqz¿=”-£˜©zâ­Jmb.œ*6Ußrnècj"r6” àbüF}ççt|fwÄìu˜Bå¹f­çž{33µ7Û!ذanºé&_†bõ†!xÂ0ßï°L*– CÍäÇãØ¶mâñ¸ó¼Ÿ8qgÏžõ̱ ¢Ñ(zF¯Æ_þüFPþæ ¦øµ&ÕŸ'À)èj´é·C”@À3<‹àl­‘CPÈÎU°á†Ú1Glgóù£yT«6¬ Å[“8¼€jÙ†U±±v[ Ó' ¨–)Š9 Ãëâ8¹/ƒ+¶¥­ R´]¨"ÕA¹`áÜáŠy k·¦°p¡ŒüBV•bÕ¦¦OäAÄ“úÇbÍNE(Òu]#Ží^[! ®5ü† àžP׋= ¨ZÿF7ˆÙïzq¦6íÄsÔ˜~~Þ©õÊ+¯ÄÖ­[¡"‘‘ƒvã‰ëúa¶ðòõ†yO@6Îfö „C,ÃöíÛqðàAd2BpêÔ)D£Q×¶gqÇ«?Ý݃‹ùõøö xÇU‡—ÝÐòƒxxU~ßÌæ'®e=M‡£Xf¤ž;[Ä‘§`U) Ù*VoN"žÒ‘[¨¢©bõUI$z"ÈÎVQÈXÛœ@º¿æùŽ%t«˜=WD÷pvµÖOÝÐ0º)¾Ñ(/”pq2ÃÔ@0?U@`F4t.ßÉöºI i"âË$„Ì,p'ÿÍ£IªiD£Q¤—ÝïÛ·.\pêºöÚk±}ûvŠ/‚¥Ôy-×iÞ'?'?¿'ÀïÇŸ”ŸïS³ù›P[I§Ó¨T*°mÇÇÜÜœTòóZ†¦i[ŸÛ„£sÝ!žŽå%_›2”…D“À‰½Oø‡ºÖˆ£® å¢ Û¢è]C$¦×VÊê·*šÑ@ûjņnr÷ÕÚÃ00Ã…Sµ)4ûƒVY9 ÍÐ`ÆtŒ¬cíÖ×Ä1veÝCQœØ»0 ­“®sp|.Ó”<™œëI?;˜D´«GÊôâïÀ8sæŒ3W[¶lÁúõë=ÛxeŒÀœaâÆ¯•ΩúëׯG2™D¹\v|ÙlÖcŠˆWBÖl¸Ÿßs=*¶À^KÓø=Ö}â—(Ã"³3Rj § k:X³%‰3‡r8s0‡HLƒf}£1œ9˜ƒ¦“šz¾*†3r ºÒúF£8s0 jF„@h0#úÇjï~wu×,›TŸ‰sGr(f«è01¼¾ §æpöP…\çŽå0{¦€Þ‘åÓ S«ùFéÏOÜÐ Ë&øŸßM!šR2<'NœpÖùÚÃ|ÕUW9õñRZü~ŸìïÊÇñŽE¿ü,žÙÎaòSJül?‚JóÇÀoE;¿1kš† 6 : pàÀ”ËeÏC¼F£Q$6áû¯òÔÛNjO”Y[ò¨›i<ÈÄ%µ‡›hšË MèX³ÅíÍg¸Á”ÝCQt©ï`tSÛ=¸¶#ÜKd])]W5ÚŠ'Û¦HºAjŸ¹à¼í¯|¡„ùþî±8`ö9ßßó³ýçççñ /8µŒáškÜ_•š³§Ãl¯;þÂîàkf“¿ÜfÓl AùÅ«_]×±qãF9rår†aàСCŽåGƒÃcxnÿ8^»0‰+º3’?³Â?Ð"µº0h¶m“0_”·ùE»V#€a¶Yz “npËW(zLWBÎÍêxj‰d_àuJ)~ñ‹_8ÌÙßß›o¾Ù÷aejoØÍìÈ ½‚$i£Ù2aÇÀcø‘iš¥¥R ¹\“““Js…¿®ߎ/>}}y[âÿ‘Ü6EáËzÄ1#M’àÉ,Crñǽ¢Ò(J¼Õizcà•@5¦Ä€+Êñéï&Ñ• d~]×ñì³Ï"—ËÒÚûû7ß|³ïR^3L&–Y.æl5Xj`b±ÆÆÆP­VQ­V1==ÙÙYðhb|\,íZƒ§N/m—¥ŠÈ’УFMm ž4¹'›Óa¼ü?>ÁH×IÝ It°@®ì9¢c®BWWW ó=z“““jöë 7Ü€®®¥z9Q»vª¨¿¿===Ž?àäÉ“¨TjÍü4¬á± øÖá«P±…CJÛ!›«Cš;ðXp¡š`'ž÷sñÕZ€¦‘Kj䫘?_Ī+“ÈÌ”QÈXˆ§tTÊvÅF¢ÇD<]›–™ÓEP›b`M 3§‹0£̘†¹óeÄ“:º‡"(d-hÈÎWa•m ^_c>ÃÔ ¹\á€; ¥Àßü …TϘã°R©œ…B{÷îuâׯ_ÑÑÑÀ¹»f¿—æ.uþVˆµ166†R©„b±Ã0099‰ 6ø–Õ4 éõxäè$Þ¶éвôOFÜ\ÈÔ'Nü4˜‡xÑOu"‚ô’dPS‡/¥Е6 Õ½rÙ¹ †ÖÅ‘[¨¢Z²Q­RDâ5fËÎV›¯B7ÈV-Û(-dfË0L‚Ü|³gŠ˜>‘G4¡£o4 ˦Z×¼ºA¸’ò>y,%?}1[ëA4•îôããöìÙãH£t:íÛ·‡žÙ’XPÞf·ð†Í˨Ù}4Ó†¸Œ×Lýº®cll ”R”Ëe,,,`aaÁÃâ}ïÀžœ\‡bÕhägý’Ä5 ‚‚¨ zAlX”þµ«¦‘S]"²ª´Ö>­Vn¡Š®”¡uqt¥ ê[‹‹Ù*†×ÇQÈ6ìK  ˜µ‰éˆ%t¦†µqĨ œ?žÇè†.nVk>Ÿñ1P;9ø«O$ÐÝ7Èü.\ÀÉ“'k% Áõ×_ïZvS6Ë1s3 × óðËŒâÖ_Ù_ä—öÂŽa9@L¬¿«« }}}(—˨V«˜œœ”::ť͞áux䘿¶Ð,©º/¬vx´yžWd?©g×o2¹$]#ÎF KAóçë§ÜÀˆj(åªHôš¸xª€JÑF²×ÄâÅ2ÒƒQ,L—‘ê«í6¤6è6°zK‘˜†D·x}¿A¼ÛÀâ…â  ÓegÀ4¾rg O{› Fó óÀÏ~ö³š–¬]»¡¥:¯þ1c ±ýUÌÏ·+î`QýÄ——øx8Tc³h.´’aJ¥*• ¦¦¦ä=}Ãøñéµ([/ÿZØ RØžv§ni“šviWúW7öü¬áÃH¤jot ­kÄ Hõ×⻺k Ð=q]e¤ëZ]Øûñ©_{² =ý«] $“˜pöù†«¯¾Ú³¯>h ÐÓ&óË>ñŇyÆJ²GUõ±«Ø¯vŽ™Rê™S]×144„óçÏ£R©àÂ… èëëƒaRæg”èÃO¯ÁëמP¶· ÄKÏ@@5 @[C¢þÉ.I¢¡Z¶aD^2h¸,dU(6r”5àäy Åj½±˜RUjò¾}ûœ¢[¶lA4õü]Du7ìš¼ä—åDPâUd¿e9QÝgõû=[²±³wÄvxM h ÍŽYÌßÓÓƒÅÅE‹ED"LOOcddD:&G Å÷mhh`*v[x$X»=«©„·]Ý‹GÌ#ëS·•NưD=WšòôVßi'vxãx½õU…0¾¾û“8Ò}ké¯Ò<ˆl¶öút<Ǻuë|ëeÌ´”ËÛ­€ûÌ>±?ü•1eYž£ªUKñc!*‰Ï3(»g}äïUéAû X6çAû X]8uê*• æææ¤Z?§@"=8<ÛƒM}óÊúW’ü€n4cÛ.Y¼;³Ÿ‹Û¼nS [®H P²)Ä}m˜Èâê‚ûê´«öˆý¥põ¯|>çÚH—æ½¢îX„ ÑB ]®¼x2Šñ+ÓR‡Ym˜µtïÞ½ÎýÆ•3þïÖì>V1>Ïü<ûùd$æÁƒ%Ö/ó‹ãhF€­t‰yãñ8âñ8*• LÓÄìì¬sŠJ èê]¿yq^3~qÔvÆBhK#ÝûŠôê®Æ¶´cEÁïÓ`®QsÚhˆF™wL¼´÷ÞÜ`™ú1~« æUÖŽžÚg"Ý;ê’þ²ßáÇtF"‘@é´v¢®LaaþÊHÅü*Æ%¿˜_Lë¸ó³ÃƒÆÌ·†úúúœ°oŠ/2ñ}ˆF£8XLâB¶îÈЭ†÷X‡Ž?YÄÚx²1V„z¼|9ÓïÓ`¼óÀ%Ed_#Yb›ešÈ¦ê×Jtw‰ôè3qôŽú.—À¾}ûfX¿~½ëÍ6…öÛÔ‰·ÑÙ=»ª@€W»ÅrbÝ~÷2ÓAìwê/ÖË3d;‰RŠD"H$‚jµ MÓ0??žž¥oƒRŠþT?R…Î œ Ý–në@®ùï<ÖIÉD~û4÷â„”¨4¨È$^} ¹mvJ…ÌÔ-ýCÖ§Œ\fÀË rQÄb1ßC'pñâE5oôøøxè6‚<ð,﨓…Å8Ù;ñbX¶@U§˜Wn¦mˆ¶“ó‘Šgð‡ŸØ¶Þd/ÖÏ®o{?xò<×4µ ¤V…ŸÛºÈ¦ÔÇv»Êâ(àúž@Iýe$^]wŠðj¸DUWµíÛwÙU^ÎÏçÈÓO_0êY%]óæÃpâFFFœïõù+ï'E›žõƒ7üüŒÙx?Ÿ—¿§”¢Z­‚ÒÚ«‡íóŠc`?Ù˜D3Dõ¬úyøýÒes–H$œÏ“³­Â¦i*Í]ב¨$ [:,‰­–!n‘§fx—6χC}@åÐ`cVÇRá®~𽓅½a(0<Ëäĵ/íª ’0Üö¿X‡¬Je3@ðË&п싣w`Ø×ö·m‡vʬ]»V^™„ü<â²¶ø÷dŽ@ x¿ËÏS¹\F>ŸwvЙ¦ébÂ$c¥ReYˆD"ˆD"H$.óF×u×±`|ÿe€Àêæ}|ºhnð+AÄkH$ÏçA)E&“‘šü¯'ÖƒÅLõN¶¥ìCŠ.ªäLÆ ùÔèh\&'È1¼äsÛ¨íëUÔ+Üx€€Ùg|~™Eï²§^Y›>š‚@6¦L\µ&îa2þwîÜ9çXªX,†‘‘‘À%.þÁV¥û©à~Ë€¼ÆÀvìñçRJ‘ÏçåÊîînô÷÷#‹y¶+³2•J…BÙl.\€aH§ÓèêêrÚdÚƒJ `}—e¦,?6ÆÌâò¤Šxm-™L¢P¨K—Íf‘J¥¤ö?û%£I¬™[³$²X“¦j胨œ¡ÈÅÓL]¯3±Ýh#D]œ*žü|U¦nƒOáÐ) ]‰%óײSGúSJ±jÕ*'/‹‰OãÓdq2æÛífð{˜¦Q(0??Ã0044äú(§ÊgNÄH$‚T*˲°°°€ÙÙYär9ôõõ9Œ-Jt>Ì÷‡Ýû4Äú!®¨ÀEfްW¶™&S(‰D”|cš&ºWîàP_3 ­1»Wˆª»ú/•þ> ÀõUÖžÌg Þ@@]@ 7ÓË¥?§MP!¯4?_”â¹£Qt¥jÇN˘ƒ…Ïž=ëÄŽŽJ»lTëVuEuWdxQ¥Oß嵈 ¶æççQ(088ˆT*åÑ*dÛ‡ù~òÀÓÛÛ‹T*…ÙÙYœ={}}}ˆÇã.&ËÊŽ7UøtžéUà «Ì·ÐÕÕ…\.Û¶Q(óEÕ·ˆA¬C1ÚÚ÷+Bš¡—¥Ô¼ÀßÍÀÀ£Ô*“h ¨ñ· pk~n›_4eÄ87‡‘þ!´ {EÑ¿ºÇÃü}>ŸÇÜÜ€ÚCÜÝÝ­´aÙÃì·zÿl#Se Àònà5ö©íÕ«W#‰¸ÌÑc/–•û  «« SSS¨T*ÎøÙÇO(¥ÿ€|},¿ÈÈ~@´—":'2•J%tuu)ù†RФžD_¶g£g¥õ‘ª“Ž…À™;›!o{‚À) ϽØ.,‚ îRÏ­wYÐOò7®BK¹V1ŸÕ1ñ¾PÄK.vmÛŽ*,ÛÎê÷ÊToYºJ3`a¾o¬_SSSˆF£ô¼Ö+[Ò%+/•eé]]]XµjÎ;J©ãh Ú{ ¶ág6‰ùÙ•WùUFAHÍZ,Q­V=õ¸4-‚‘…œío TϼÐÏ•Ò(&p–ê¸$‰)à N]\Xð‰âãð¶\Ü)ax>쿜(k3„ô§À…y‚H4^oÊýpòSÿ™4ôTUï§ß!Ÿ*û›—䪟W&½§§§‰D088¨|Ý×XßYý¢=ÎæÁ4MŒŽŽâÌ™3ÐuétÚÃØ¬o²9iª¹dá°;™ÿ¢T*9K‚~Ëš¦!•oysO[Èoõ$¿Ì¹èÇø*‡Ì4éïïwTæ0?Y"#ˤ§aƹsçFÇ=Àô¼ŠãšÇ0<ÀÙÅbѳ-Xl;bGx¦iŠ‚L>«*!È ¶m™3ƒÝ{‰‚RRïœÀðÄy@ Þ4«Û¥€µ(}CšNt0ã×GÓó»tçÓú IDATø[`zAa¢Žž5‰u{¿Ñ•Ú¦¶”Æìùä—0ŒÝ,°ûb±ˆL&ƒU«¼›˜xZ¦ È€HöН ¤"‘z{{qñâE¬^½ÚÕ§°ª½Ÿ¹à7Ÿ~Få@¶¯ÁtMG¼G!ZÝF;)ÔNÀp&\Oà–þàÂ< &õ c—ï}-’×U¡È#š1<¯JA!ˆùýT¯prÊD<—ڛ쟟wÒ‰„òÍ?‘¡ø¸vÐXûfõ_¼x===®•Ê_*M„ÝóÒSÜ‘HHmí=›ÍbqqÝÝÝž¥N±þ‡ýøˆl^UÄoˆbš€E´ºŠ]-@ íÿ4ŽË†¼ö2sn©_sð@#­1•ÐDݿʘ\–0¿KâËU–ta^Ǫ>÷A¢9077çHÇdÒý%‘TPÅÔ¢-ˆùym0›Í¶m$ iy1ÎÏ ǯ’übý===¸páÒé´ 0ü@€Í‘3óia5 Îü°#Â(Š(ÒÅ4’ ž4›Ø°µHÙÔ ü!ùßãùç©ugÂ` Ó:(q¿T X¹T}ÖG.F”Þ.†æJI]çTS‹+U 3(¥Î±_„åYÿ"C‹ëøb>–—gh1]ÆÈL±×_S©”oª:ù%HÖ_Ó«êdq¦i"‰ P( ™LzÎ1Í“¨Yð$š,Ð`ù Ãp¤¿eY¾okº_ɬÁ÷wµlÀæ6¶Ù–JÓ”f©½€¨&×5¹On À£  áL#¤î&Ÿ8ÿ…éœÜÆûH}çÀüÔ'ŽkТÞWUù—RŠ……'ÞOà7بÏ+îß—I\V^Ì_­VQ.—ÑßßïêƒÈt²{þÇ㫈âüü¼ãñQ£…™Ø¶¬¬0`&[šU‘®ëï/á¿¿ëe(Ut<ôO«ÅÆ|Ë„¡¶-rËo¾‰ê5SÎÛ_gzVU³6€WÒs)¢ÄÛ@*󨙟œ|Ð4Ý÷PKJ)r¹œsŸH$” Á;œdLd&QKúQÚx{/›Í"mÏW•›q²É(bnnÎYvóó¼ÞcÎxðáË©¾ŒíH@c“kË!XÌ«•õ-ÛR)7µY`×:c>Lkx îæSí쓾èÓ0 <Ö&\‰RU_v/“úª°?ó™a˜¾ûÓ)¥®OP‰¯Ìú= ¼ºÍ›¢½-æõ³Ïñ ³ßƒîy䟡—M¶<Ô>â™ÏçÑÝÝí+{ŸÏ/š\ªü²ve`Ê÷U4oüˆ‚B¹E6l´OH}ˆ{øy¿×~~×Î>A àÁ€%7Zv%ùŒDtEHU}÷½z¡LÕo„eˉÙ"@4Ý‘ªÒ^sR×¶m—·=Ì1_¢ª-ó¦ó€Á®üöW¾–§P(8Î?ÖO£Ë¼ø,ÑP=w²öt]G©Tò€ŸÊÛ¯ò°1‹ê>fWÕaÞÄ–ÝYÞ-û@î0¿G쾞^¸™Þõæ_+ê£ ü_L÷gx>¬ÚKP©êÜkß²¦\.;ÌÂÞ8ãÕ\‘D&Sý½d &{WæS=/"°¼2ªC¦!˜¦‰J¥âñgðãñY•fá7/ª¿or…2o”o²úÓKÐ`ƒ%œêw8ê÷õ§ÔË4nœ¦Âõ- Rdz¾«Í2¾ã#pUÂ…k×J•‚Âÿ´^JkØÃÄŽöÛîˇٽLò‹ùDf¥)/!E;»ÆgmÈÚdO0`å˜ÚÍ€ÿBoë³r²9“½6ÍçÁ4耕0d· +iða¹D¡iîù²Äó†ŸBð¤+GåÃôü˜”÷2©/ç¡j”©óŽÿ3Û0Șœ7ü„l½]7›Û -€õI<L2í‚ÿ;°ºüÀÚM­é~K-i2 6åþº6Pß! ^Îîxž‡ÚB2†WÅû1~H©Ï×mhÔ¶€—lìÐLÕ¹u~R[F*°à Õyæ‡ä¾Ñàž2·ú¯‘áE¿€ÕjÕy_œ7ÖÏ ‰Ïk52­A6ך¦)ÿ~aµ€vØò­R(»ªl7€×ׄ_-@ŒçÒƒ¢¤ ¡@%¨ã¼Œ/Þû‡c€Òªr;*#^²ñÇh‹6¹J'Ľ ȧ«òËöð`š&ªÕªË))“ü¬<ÿìÈL1Ïè*-€gêjµŠT*åê§l)Sœ_^Ró> Y~q®ùý2€aæNiª³,V€–çm@ɶ^)-@‡z]<(x:f4p3¢4Ýšf|çÂæ­NÅmØVU*Aø¹e*7/müÞ|UwþMÞ‰(+/æó³<©T ù|†a(AE¯ú7AÎ@ Œì¬@Ö–ÎÆÀ›âˆÒ_dvdùü|¼ßܪÈX·–Jm]d|+ßÖ+€3œÿ¼ ȫ˖*ÇŽO¥yUÌ.O£¤â–Âà$Þ(—ËÊ-¼TâH¦5ˆ[‚eïïê-Â|þt:ùùyçå$q™‡èø“€¨i†]àË$“I‡1ÅÕ6¬Ÿ²vø+?ÿ7àçg~ñï ó¨È¶m$»*¾yT`ü¨¥@|¼»~©w,ê·5­€+¸ #ƒº4‚ õ+óI˜ÞWÚsõ†d|Ç SN2y»RË6^fŸ‰©æü–VQÅóóÛ~E5]&ÉøwùÙ–[æ$ e±^•ô—•SÙüâáŸù|ýýý0 õåW¦­ò¾ Ù¼ËÆÎÆgY–뛲²Œü44q¼=ɲožå¤öo Üß/H| ÔŠH˜Õ3™KÑäe©oz3@@…,ò4] Öb±˜ ”#vµ¿¦!%™yÁKI>Ÿ(¥y0èïïÇüü¼ó†"_د0ÐŒÀS¥R‘¾•(22«Oö ´Œ¡UãðKçç!Œ`YNîÁÇ>ö+0 E(½þîŒ]çÝ`”c`—©}>ÃN- QÖׄF@ K fW¥‰Ò^Ì‚ñGÓHÆmÌ+g?·‘ú‰Á”Rç¸)ñê­Ê!§rΉ+™ÆÀÌ Æ@¸xñ¢ X™F!2¿ÊÀÆ¢Òxbï$Äãq×øù>‹cýb^>,>ïA`Á÷? PËÂM³%l/3! ßR!LJܚB;š6X­ßÔóÈö÷ÓFo}´‡\ñ4×´ålÒ 2¯Lò{ÊJÔ~Wqã»ÓF{-LŸª:çÛˈí–5XÚ{…Ô—ï!ÊÇ×ÏÊÄb1 ;'òðÄ3™ øºE†M™ô¥”¢X,âÊ+¯ ÅÌ|ýÍRØ2 HyÓÈŒJ½!þRZ) ‘ºTc›wûû=ŸóâÁ€ÕÀÂŒ›"S¥Ë€¤Æ `ÓX {NT`S¥Rq–ß<=æn•”••Q1e˜2CCCλøâùLÒJdc ‘2™ ‘H$¤{"d$zïÃPP?dmø½á骻ZÅ`ˆ‘.B”LúËÿ@õ¥ÎÃNQ³c¨óðsé|¸8§€,K_òõA쇢|׿ZvZ‡=*¤‹ùùñÕhÃHÝÿ¨'Bj§Þ0µ’æ—?l|+žd¾Œaw}C$̉ÆÂâOLócê\.‡h4ê|#q)cJofŽø•‰0åˆm#Ú¢iKÚ „{[!Qe瀠Æ(`–éô«‘eåª&ÍC=@—€™ß(êÇèª{6EµJÖY >=–}mÖxuX c§ò${Ç=‘H`ݺuÈf³¾»y•^âæ1?Èøø¸ò*’-•†¡°mPê>À%(oW«ê?ÚãlJðýy4@ÅDR0‚ŒáE†õüjyœÀ¾SÞtÛ¾øÄuŠz€DeLOáí§XV–^¯¯6ˆ›6t-Xd»ÜlÛvÎä‰_«W=¬"cå—µ¡:í¦¯¯ëÖ­C&“ uv«”ÏçaÛ66nÜèlýåà "•/B–hnŽXöúvPþJ¥‚+Ê—n hã»M=u»žr)žÓ~àÄQêªÔmóóeÜ‘€ù| ¦QE"ê~è¶®ž–e‡àw_÷¬+ŽÝX¶]³ÝÝwgtƒ›+(æ“ßSIúƒU¾àz,û¼6¥ù|•J¦iºòð†DϵŒx{\ôä™ ü¦F0MÇŽƒaˆÇãÊñ4K”Rd2ÄãqŒ;cçÇÌvJª4ˆ0Œ/+#–÷룦i(—Ëá¶‹Øx‰ l†qÈ4¼ü*-€rŒ"‘¸ŽLäÛbÂS-ò_èÆ\6ÎÕ'ù©Ô®Û¾ú¯ÛP®ê mÆeŠuùI‰ªß&Ÿ^ ߸¡jûïÓuÝñµ…ˆ4¯Z=¬üfñ<D§³ÕÅ~د··[·nE<ÇÂÂB[´B¡€ÅÅE 㪫®r$?ÿ^r‹ýcϳzŒÄgœÍ¿ª¡š§°ÎH • V-a~Úa´íH°£rkþ,™Hâø€¸KCChDÊ,ËwÝÚóöQcäbÙF©b#[J [J b°)…©•ÑefŒ‹jHÄt0 &øÍW?_{ÅRÞ O¼Zú;#5îþÚñþþ©<€˜·nŽº»»eÀ™™ ×›s¿ÀÂFÞÖUXÙzµlk0ÿ`óõ‹uòq]]]ؼy3.^¼ˆÓ§O#—Ë!‹5uŠ0ÓtªÕ*Òé46mÚ„D"áŒU\ç€Õ!ö_ÜO 25?/ ,dϾŒ,¾\.‡Ò2(¥ˆX"!Áb¹¨mË€ÄY½£îH1.Ä+¿à.®t×­;C¡dc6ÁäÂÌU®@k½ËõÚUÕ|´²£z)íÆ{'1Òg!Õ¥{߯n\ìïÃøì~¨Û‚Nªòôöö:ŸÆžŸŸGµZuvò‰œø°òoªT|¾Œø:0Kgõñ÷@c›0¿So``}}}XXXÀÔÔ2™Œ£&›¦é´ÁÚ­V«ÎV[¶Óp``‰DÂÙ‚,ÛŠ+»gcærrÁåƒ âüðW~ŽØ<•Cªô•rÛC|9ÈZYÁ©m‚0õ·¦HvîIµŸøZ¢s¡øþ¾ xÃÕ'`êU'o±lãôlG®GV»±x‘Dþ]î°Åâ«ð³ÙDÎ> =/âªÕe$ân«h6×…t¼ƒ(ØÓ#íÝqò±ñ÷µÀÕkÊxvÒk×óF‘L&‘Ïçaš&.\¸àhµf½§ìð('SY<¯ÆŠR̸O5FÓ4ôõõ¡¿¿årù|¹\ΑîŒaLÓD"‘@WW’É$b±˜ÃÀ*I¬öã_Ó b~Q£É/Žo3ìú¿V(`ËíÿÙ èGîIaV.q€r0àâ½ÌB¸‘5´Škל‡©U˜éŒéy`ïôMXÐoA²»é&—Àb±b#c¨V‡ñÂôõ8¼çGxÕ‡1>j‚KxèüÖÎb(•‘ö·)¦wp]^¿=‹½“ijjZ@.—ƒmÛ˜ššr@­ò£¶d/§¨€ëA£ð ª"Ó4ÑÓÓƒžždz.îí™Uü‰6¶ Ä>õO—µ!æeùø· …Bh©L+Œ^ @ŒBûxUM|™Ñ`RÿÏÎj@j5Mט°jQ>›À‹Ù;ë¾i©† ÃÀȪµÈåÞû9®žýg¼ê*À4^=~¢16ð]“õ3Lœ®^]¡Á*!3ªÕ*r¹r¹œc«¼øŒqDvؼ²2"©^£åëåãe}D)-2/Óf‚¤?_‡ 0d÷N¸§ûZQ BllY­ÞMÇH×u¤R)Çë}öìÙÆì<è<ù1ï§bû©ËªŸx˜‡(ÙU>U\P›oØ{‘ŠÅb¨÷*€šúƒÏK]aé’›<æ=g¼Ó;Â1\ZïL-/=(u ²jQ¼0™ÂÁ⯣wpM[œ "†ñ+oÄáö³ãM7ÙÐeÞA™¤ç.ò8&iäéo¹aûϦPóQ¨ixx‹‹‹°, ³³³(‹ˆÅbõºåj=ïá–­ˆÒ]Ô(d¤ò!ðmñu‰}ci*-@”æ|| ð«_Ö¾¸b *#Ž€ï[š"•«%\ì±0C½òW…!Q-PÄô%j jß—xõ€üs^8ñ\ ^‡MÇöoÆmW†¡ÙŽœÕðBn'F‚™¿\.£R©`zz†a ‰ ŸÏc``ñxÜ÷Ûmš¦aÕ×áàÞô¼ønÙqºœ+Eˆ2çMs@ b|¸r´ “”P–0 Oñxét™L†aàܹso´&atÓ«âùo"©ê¿;È—_2”ù¤ÏàqêùýøúY]A^\¾ «°´ÂP¥\Ä­ã'ñ¶+ùæ+Yºçƒ!Q½ý;,—¬¸ÿXAõd·îx.­N¶«1?€é™ ö\|#zW­W2¥¥R ÇG:F¡Pp½\.£Z­âĉÎ1V«V­B,“ÖgÖm~5~²çÆúbíHÍÔxüÐ&¼nãa¤b¼½×Ó;Aùý®É`÷ž4Œˆÿ.ºÁÁAd2X–…™™Œ9çˆö½èÁWIw0˜Í-Ö)c6ë”i<)… —Æ–1¿ !‚ŒŒ©ÙTŒØ/~NýiɨÎãÎõG3ü™9(õa©ÔÒN@ÙD’º¿Ï&™fàϧqÌ´*½ÐšÝÿÌä˜=7(ß›·íÿ¿½o’ã8ïûÍcg_·{·÷ćÇID‘ $Q$EIqѦ9–â$R–J‰l%Ž«Jª¤\ù#©T9å²Ëq^JÙ²ɶdK¶¥èU–dŠ E ïÃãp¸°{‡½»½Ç>gvfòÇ^ÏöôtÏÌÞ€#5¿«¹Ùééy÷ïû¾þúën sss¸|ù2r¹œBH´ÖÈd2NÁÇìì¬0z+N£{ÛÓøá›)4›­û~þÀ9dõÕç \ÏÐzG>ï‚õ‰PÛ|x Êß$N#“É8Qy7oÞô|/-ž*B±–_¹ªªrž»P(8}æƒÌ^¢yÄáå'Ú”§YyDe«AÎ;^ÕÃO°Ðß•î#à÷V‡¾?£\.{Þ¯–e!-¯àààíàÌ`#ZÁ7pD Z"ßæì[%G:VÅî¾"ˆµpm.‡®.wÝŸ|ˆB¡€X,Öj¶Û³’$a||F#´5@ÔF£ùùy—– èØŠ 7Î3¼4qf–{8ä&=ÒÃýȶáž&Æ+hÁ¾€\.‡D"áÔ‘'''g‘€G¶0ä KPºûpíO ?íÏúDBŒ-3"'¼ª“®ë¡;ýô>þàùðùCã.µ„‡Ü$+¨}«ûª¾Ô `µº……jétÚs%˲P«Õ išó1·nÝŠ\.‡K—.9ñò¢*¯0©ªŠR©ÄÕ ¹\Ós1˜fë>ß7vÛºçÛB>€ô¶+ûÞÚïåSO!‡°$IÂðð°^¯×‘Ïç…… † !@["-Î#›éDäÕûÜ›½o¶ìÝ ï¹É±ËËÁß„À4Môk%<Ø?ú˜»‰;Sh'„|ÁP©™ÐíŒ+Ü—gÆÑK.—ömÛpíÚ5‡Ì¢‚Äîà .ÁZ™LU#ŽÒr“zááN“ž~?¼÷ôvxï¾,#¸®™L&188ÛnuŸŸÇÂÂW«uºË#“Ÿæöó°Ç ^=>è~þ:!?H">ýÈ鎎 }îMUÀ/ì¾Ávñ† õ† Xض ]×±´´Ä5%S©vî܉ÙÙY'FÛÏä¤ U©Trê“4TU…e«¨Ö-‡&=øÞí.A±W¸Õ¹\ÙlÖ™?°P( Ñh„*àú˜ ÒÆøia œ£‘}†°ÏÂÂ//ñ?U*•ŽF8jê5<Ü?ƒá.ÿ¹î%6ÐÜD€€<Ò{ö·–fÓ†e{ÇɳmÛ CDîX,†]»v9³×ú Z°ƒN¸ ¼¤@7Z÷¶PMã\aÄMxöY¸Ï(J£…_{;¦˜øï+@jzåahhñxõz¦ibrrº® 8ÝO ÌB·…‡!?O“¡_5pÏH,Òþ´æç…ïú ^{>ïxF££x˲3îPÝ¿…Mg´ ýê‹cLd–(^¡Ð*øÅJ “ ø4ºs ö#Æb1_rw²ÂÇ]i4êµT¥õ š¢£'Q6‘À =•Ö–!6ÙQÅ®¾e4à2Y–±eËÈ²ì ºqýúu®Pãõ÷-¬Â#(ïÞà#"a@î…ä ?md1°ÏÌÃB’$†Ñ±éo7ðÉg‘Pï\ŸÿÍ× xˆ/2•½B¡%&æúQmÄÛ†¦º÷Å“U«ÕKh¿¦@Ñ@8ý¶®ëhêu¤-K'k`kw©ýÐ"ÂwDz7ñ[ùZ«_6ÅZäR‰D™0£Z­B×uLOO{Boi_ ý^ýÈÏ‹äYd˜/?_ ½°­¸B€À²,O4¡ß3ÐÏM“žÍ´Èoš&–––:Š»7:vgoox³Ÿ`ÜÙ!îf9†ÂáíWäl—³±èéîJ¤f³É³- ¼cÙmÓ4]qÒç(•JQCoV…CLï p­üÓÉ=øåkïÌ$l|æé<þÇObPâ½¼‹»L&±eËÇ"Inݺ…íÛ·{ˆÅ†ó@缃’4²&ÚŸÀïÜô÷ ×ä7ïXšÐdÍjy¿cxÕ^þ Y˜xǨÆ>sðÍŽŽ»WØÐ8·ùÆ ìãåYERzâ˘ŸŸç^sppÐÑØkÕþta3M¹\®ýLT¡ÈçóØÑg´^˜Ÿ†çî#é>šÞ|®wG+xÇöÌ­@+Tx``ÍfFÕjÓÓÓ0 #ð}ðÂwE¿éüDk²ß '!šŸmÙaG båÝ9}O@[óûùèg äcqѰësøì;N!¢7ßz±©ª =Syû~ÛÆž%ÌδG½¡û’“ ÑSAœ†mÛÐ4 ªªr-Š©Éq5¼¦¾‡oz÷Æ–IDATÄk$½Í¦±ï§uð§ŸÊ#§•B… @6›E__ Ã@¥RA­VÃ7`†ÐT¦Mí òÛ¶í2ñƒLkrl˜AV  !À’^Tý`Ëé\Å« úÁ¬/â£W1–+gÞl*' M“W° ¨ýœ|÷m•p{ê¸gò Y–¡ª*b±˜«úY~ÒÜ4MÄãq— B ^¯cúêI<¶/ÀFÕаÜHxÉî!üH櫓ï®E²ñÅ禠˜ ¾ÏC£§§°, •J¥õ<ÓÓ¾M„„ Aõv’Î á帵1¯K0O±ççÝû›õ=øÝI×u}Mšß4êMÏàCcW;:n=0-ñLRa±@…nXÉAzŽ5Ð4eÀöŒhÈ`SSS.í¯( TUEOOKâ‹´ˆˆ¡«« ªªºz£I’„“'Obkvc#1ÀnõW¸^tÝ·?áyç pòµÓl»å4íI7ñ¹÷OÃṅ~ÆL&ã ‘©ÅnܸávV{òˆÃˆµDš™·M;û‚HJ_‡÷ý„€ŸÃ‘.#’$¡Ñh¬‰üÍf)kÿê'::n½0ìõ»ð6L˜¶ Ãðš&W(pÃ|µ ?¸|°m(’§XÁÅÓ/¹>𦡝¯ÏÑ‚4uwwCÓ4ׄ@ËûÿÆ«ßÇGŸ„s“Û³E<4xÝõ\|ƒcÊC ÄÚ¿õGå±mìÛRÅÇŸ¸«±úYS©††† IÊå2t]ÇÜÜfgg}‰ågj‹46ÐAš˜' èßaµ?+Ìü„A¥RÁòòrÇ£ìX–UŸÅCü6ùñ`X›H4m u—»…eú­îïK.ã÷pò¾?Ž´~çÎбX š¦!£¯¯ÄT ­E:tH’„îînÄãqGjüäÅ¿Ã`rO>œâ“gÙˆÌý MvZûmÐùÜyÞ3¶„÷¥‡÷T“&ÂD"áô(—ËÈçóNÔ$­ƒ|ôºuk^ËÀä~{=ú~‚êô¢g`µ>qöu2¦û3ذk³øüãÇЛ¬°hZ€n­D쳤4fÀ'½Ãtˆ‰²šPBBS%üÊg}Ë5)f,C<wæëîîF"‘€,Ë(—ËŽ·›éÅ0 ”Ëe(Š‚d2‰L&Óš du!ŽEI’055…7~úmü뿯A‘ÏŠð>¤§5½Í!½C|þöGÅáÑYXz¸HA åMBww7t]G¹\F£Ñ@¡P@±Xt&!Dé„ü~¦8KL^’¨Ê@kë0–Šm»›öx×Öu¥RiM–£eY0«³øì¡Ø– ?*ÐF¡Ò*V÷ºÏ#²!¨rh{ÜÍìËZÓVMU£ ¶ƒÕ¼Ì|~Ü<ô*éá=žÞ{ ?üÞ×ñ¡ðÏœ‰&éހ砦iކ'uÚx<Y–‘L&V„x<ŽT*…d2‰x<¥%üåŸýo|ü)Žv¹5°çþ9ûœdÑq¶÷Hî5Ø4›=/¹…º.ãÍ‚Eó›&©ö”J%gêmB Ò¯€õ„ó¾=G@çg‰N§ÑÓ~‰¼í4éÙšÌôš½&ÝlH_.—øˆNaÛ6¬ZŸ9xûûÂWÁ6‹Uu) 2ݪÍ);w3³ í®üòêZ¡yÿþý¿,Ëò0ý‘y#²,#aæqx/‘¦’kŇÄý)Âþ .\¾…7..blïPUÕq ’j=l1åI5AQÇÌO$H&“H¥RŽö×4 Š¢ Z­âOþðppä>÷ËHækYL/ö£?% õ"Âk$½7í£K(,*È/ªÿY†iÄb1gÚñz½Ã0œþFëu[n-LÀ–šp¼mòxûY3ÀopRú~DÖA£ÑÀÒÒRè9üXX–»vŸ~ø$¸w]|ÏÝÎàbù~Ï0eÌ»Yºxñâ—XLj혦¬ ‹B~ïÛ·ïyEQFé¨/úƒ»^¢>gl„#}à»'¡ENÏîÁ@r1xô>'Ï^Çñ³ìܽßÖ›xîI%‹ªªNs¡¦iÐ4 ÉdÒYXòÏÍÍá¾ô{ØÛ3…ÿôBêjK‹iÉ€$!«‘!­ïìê€ð!ÎO#½µõèŽ%”*¦Š*dÕXqÄ*Ò4 †a8!Ö@+”¸^¯;“_,=1×iðHÆ«ð@§³1ô~Þš>?Ðòù¬¬¬¬Yë„ü³øGßÀý÷Fó¼<5„‚¹ÛóìŒ/¦8>>þe´ˆO G*[!w`Ûv…®SñL@‚b­ËÕ2)™á·DžWEXÍãú¹*ÉaC•t'-¥ÿþWãøý¿>?ýò,ž}îcllÌ@ 2ÝÌ´‡ÄH'OžÄ·¿ñ'xî5|ác¨Šä°+£U‘Ñ8xŒ™Žð«9Ã’Þsb›9m;Ï'¿…´Ž¿9kAŽ÷vT؉#”ÌÞ»¸¸ˆD"á´©W«U¤R)g^B4žPð Ïæž¶*YëW­ ¯Ãæeëù¤Ù3(Ü٦ل\ŸÅo>þ:F³u ÚhØ6p£ÒïQ¢+€í‡ìá9ÏpUöîÝûnUUâ™þì ­55ìî¾…ªÎÖ="…2ýÓK)¨2ð®4$¥üåw_Çt¾„Á¡tuu9B€›ÔóI5€,ÄP·nÝÂ×þôqúØwðÅ_±ñ©_HÃ3Ñ0§>žì«¹¹»; ½+ÍÓÎ<6PÁP¦‚“×5@IvTè%Ir¬$Y–¡ë:êõº#ø›Í&jµšIHêò€·Š`Û¶ïÂ"sžš˜ET¡C–É34 ,//£V«ù*­00:Rf¿õ®W±%ÝÁ@ wK5à;Óµ5R–Àü‡mÛ—ÆÇÇ¿‰¶ög-—Ðf~ÛlÃ0&‰Ã-‰D¯_Kà{W»­Ú¸>D»ð‹?_HH’ç$ñø~ü·?Á—ÿûkؾç:ø8ÆÆÆǹuI"´ªÕ*.\¸€“Ç_ÁÔµsøðaà×_H£7#¶¦%C•-±¡ïKv÷³­=%=µÁó9P™]Â`×%üî‹&VÕ;°ŠdYF:F2™tš u]‡¢(H$h6›¨×ëPʼn $T m +?Sß¹}Ž Ïî£ëƒ|grOdLÈõh|³¾ˆ©üÆ£'îz;¿ŠY4•\`O¾f³9 ¾eïüV™‰`Æà•.¼+I®ÎåÐÐóˆÇèº>•WTßk€¿‡sH©žAobC=2¾ø±þÉ3M|ïØ«øÁwŽá›Ë*·lGßÐvd»ûœƒZ­†Òü,nÏLa&[r6~é1à#ŸHaK¯âzuéIóÄq<¾×=N0¬p‰ªÇ+7àƒ»˜ÐKI‚m×g›8{MÇôm…’Œ¥ª Iºâ&†s6v©88¦aÇ X†š–Ôjûw ",BæÞù¿¤wŸz5®À¾uv?¸8 )Þëxõ× zf¢¥Ù*]M$~ÚûÏu"¯‚õ°M€$ˆ‹ÄyˆÎ³V4:Tc¿vè$öõÞ[g‹Åš„ÿxü`jÜ~Ö±º°°ðÉ_|ñÇ úêÚ@»Jà°ø´¬G$‰øsÏ=w^–å$¯¹ ð¾üÝñ×ñù_âGtJxvì€;ËdÉO‹ó”„wý\kšÍœºµu}>…ß{i ô@Õ¼#-¯t]¿'Õb ºŸàÖÖ¬8qÉšö l$á lÛ†ÝXÀ®Ì,~íà›¡&è¼ÛøÁå|¯ø÷\æ‘÷ΆJ_¹rå‰3gÎÜ€[4AùXè1êõzÓ²¬7$Iz’m U.—¶áæÜylë§=iœÁA¨]Ü ^÷ÁR#…©åa<<0*?ü,?r‰2öõÞ`öðˆÆCP¾N/Jg5;/Ozú ½ü·Îàk§¶ãµëƒ°µ¾u[tœàíºKz²9¬aËkÆûY C¯Am.à…gqhèä³FèMàøÜ^OË»^ÓgΜɣÍg—ó€|9ÖpÅìÞ½{$‹½p7×ð>Hjç‹xl þ,"|X±ìÖ‹ÉÆ½£°~{â=ØÝS€"™®W0Ò5‡d˜0Ú0®¯úïÚì$ŸæK|f[‘m<2¼„‡GJ8}CCà Ç7ŒXtOMR% =,é…®J²Á\túFÔçý`š&P/â`ïu|þ±×±-{÷ÃzÃâµéŽ->æ¿þoÆ÷/_¾ü·h¿ o~+‘d±WVV^M&½%"K@’$œ½½ 3¥q õH^G¾Í û¥!üÞ’ðH© ¤ºŠÜýÏl;M¶•ÙðdŸóó2ˆŒVˋ҃´='ÙÞÞSÅoÿâi¼2Ù‡¯¿¹†œ…KݲÝi¯–ez Z Ÿ>|zSÝ ´´ÿÑÂ}ŽóÏ/¦êõú«pk~šøÎA< Àcܾ}{~llìŸJ’” P3¸53'Æ«'n+߯–¸b@ ]É·½[!´>?IDlÑ>ÑùÖ@zŸÂ»½=Wų{ €i`²ØŠx”•Κ ßJ°, Vci{/ Ëþüü^äÍö Ù¢J²®T*¿;11qmâ€Kûn±ñts` €ú‘|ä'²,ïfcØB4LÓÄ#ÙãøÌ³ewœO(þÞmíB8„Ñú®d¿ý<Â Ž Ez^š¸ß@˜í+s]øG1±­¤!ÅÒ›^†ÕZlÕpdä>°s¹Dð¤*›¯\Ïá/n|²ÚŽÆe;_1¿«?ûÙÏÍçóKh·û³ÍÇü}òÎ;㚦½hk{¶0°B@–eäË9¤PÀîA¿QVÅ>‚»‚NêøÂÝþdæEÈ­‡ô¢{ Eüö}IGvÌâÙ=·Ð—\Áí%åêê|‹P6…0°m†¡C6½„¹iüÃûÎàÝæÜ$!¼âÆ¢†¯^}Êeú³ZŸÁ0Œ¯Ÿ8qâûh×ûÙú?@}`–^lý_]]4±‘‘‘ìO<ñSI’z;±@näñ/ß}ûFlêR‚»XD'éÀä¡§A Ù}α.A°ö~~Û6g£©àÜL7^½1Œ«óØR M)( Oˆê@‹ðT»Éj ©4php °«g¹gïæÅr]Æ=ùæ¤1WºŸö`^»ví}§Nš@+ðGGKó“è?O,ïkÙÔâjÌçóÕZ­öS©ÔçØº/t“†ÁÿzÕÀ¿yj;Èe¨+ &œ8ð$!ä@‡…†!LÇdÄ„iyÏéž—æO|Ûw¿¸ÒÄ£[çñèÖyÀ¶QªiŸËáT¡S‹Ôš* «°ƒd¹ Ö©HG·ÁjB“ë°M2šèÖtÜ?8‡f1–[‚¦¼55¼uøòÙƒ(b{œ,NÝŸæž®ëß:uêÔuxùKsÚÞ×àYY¶nÝÚwøðá—%IêõÄ[]Æeüæû¯a Ë»¶ Ö¶–A9x¤ÚÇNz‘„¾Û6szóÕ +IÌ–“¸±œE±šÄr#†zSu¶ S†ª´|–m#&·ŽU$ 阞DélË®`0UÃH×Ê]™qç^Bo_>së‡\bM~΀*æÕ«WŸ;}úô8Z¿vø¯§0HÞ¾€€Ø>ðžÍf¿@Gqñ:ñ„€mÛHøì“W1: ¸6Z&t¤óƒ´¹+ïZ…È­„ŽHïúÙ™¬›ø¾>î>JÈÒç¶}îûmŠºüŸóp¾zPØÞ/q¹^¯ÿùw¿ûÝÿ€ñÉBœ¤`^¤(Ü/48vóæÍñ±±±÷I’Ô€Û,(2÷$I‚¡ôâõk*vö, ?vú%É}gë Ÿ#̱šŸ{ <‹òwHzç–‚-ñv‡Äg¯áWíàT9~°X•ñ¥3ïÄUý!®c•6÷9C¨/½þúëÿ¶\.WÐ&¼«Óµ¸F! ®æWMÓT’Éät.—{€äGzQUÀR{ðÆTj³„]Mªãßf <ñ#îZò‹ÈÞJô%¼gßÚHß±¶÷<ÓFß½íD?Ä€ËÅþðâ»0kyø"ªóӬܾ}ûw.\¸ð&ÚM~¢àüº‚1D„&, 6333¿cÇŽ!MÓö“D¦¿HØJ·ps¶Œ‡+ˆ©›…ü<ø¸Ó¼AxQz‰Ï¹p‡‚ câ³Û¾ÄçÜßÛM øÑÄ|}êiTåAÏ~ééªR£Ñ8ñ£ýè¿¡íí§×žÎ?,üèŒÊ'033svttô]²,;Öw*d%†™ú¼>!#%/ckÎôZùÁ ‚§×qŽ_\¦CÛµÉ×ê¼´ŽIϳ`³ß}š >›÷çƒø0½ÇWÏ?‚Ÿ-=p†q‘Ÿ™4eñرcÿ®R©¬€ôC†%0¨38H‰ ˆ†¡Ôëõ [¶ly¿$I1ÀMôÐB@–Ñûpª0Œ«ù:¶w—‘MÜÙÃZ„Etá1~çí„ð¢ô5†w¤í©kwBüÕëØ>û~^PnHøö•]øÆÔ“˜··C’üëû¼:ÿêb_¹rå·'''¯¢ÝæO<ÿ´ãÏÆ:-™Z;Ž@¬---ÕÓéôBww÷1oð³šÃxùZ?®šHUKÙ÷¬E° Ûû+@ë‹6¹úTd>„ÕôÀ´½€øBA?K5?œÜޝMÁÕúý°eï@»¼Þ}¼)Ù`vvö/Nœ8ñc´‰OÈO€'ì—‡ ¸íP°P(f²Ùl=›ÍtXa„ƒ¤&±ÐÁ«×q)o£ÙÔÑŸÖ¡ù™­-0 ?C3ŒÖ÷$u „Z^tžµ‚NÌ|ŸíÕký¼ß0‰…4¾?¹ß¼v—kûaH]Âæq²"©TzñèÑ£†v{?-h' €Ÿrõt _]’Ò«Kfuzï{ßûñç¸bØß~ÕšÍ&Ò˜ÅXwû–ñàp}@ÙÐ0ô€Bé»;Œ@X‹æçR^L 5‘¾S3ßÛ¿ŽÏl¿Í`Û@M®-f1¾0€KËÛ1cŒ@QÄš‹vè…!ÿÊÊÊ›?þñÿÀ4Í € €òêB¶ëpÇpC€ :1´‚Rp €4€Ô3Ï<óÉÞÞÞ§ÉÏP$,LÓše &n£?YFª†-™:†³u¤4‰˜U†wbæ_Ù§À†Õú«É­Çïн3-ﺕÐ1éyi|³_bïüÿnVyÅÒ¶ÓtSB½C±šÄlµ ó,n׳¸UB™Pc-ŠôyýWVVοôÒKÿ³Ñh¬¨Â-ªj« =膗÷- ‰–èZ]Ò«Û‰#GŽ<722òáÖKtžg ¬wÀGzÞ8É6Sšî¨2îí³(ÝÍÆ ‹à‘œs¹°§Û€û ­u_*ôI|sI€a«°ìöÄ´öˆdûï9ü`qqñ£G~Å0Œ*ZZžh|" ˆ¨£= 0; к©ó+€€4Ú ½º/~èС÷ìÞ½ûcd‘ö÷³Þ.#ÑDˆxM}²i~:_±X|ùèÑ£…–i_‡[…h^¡À Ws õ[]Í£ÎÌÌäMÓ¼Ù××·_–e-H#».²ù0B„Í ¶ÌŸÓ×_Ÿ˜˜øÆñãÇ„™é¶~ZèöžÙ¿æVVÐÁ@ôB„€B­åùùùR¡P8944´UÓ´>úEÐÃù½¸ òGÂ!½Dbó3÷ÙN>t¾F£Q!9ÝÙ‡&¿Hëo˜àYD¸ÈOŽi4Æäääéîîn+NJ¼ÈKK×E¤° ÁóèÓé<â³i¬¹¹¹c/¿üòWJ¥Ò"Ú}„ä´Æ'–IgG úÙIs` m_q ’Vâ/ Ö<88ØðàÁg2™}ÎE|†ß('a„w~‚@$  ^¯çÏ;÷7SSS×Ñ"-­ù Ù‰€8kh;ÙnÀâ Óm ÐU"0ØsØ•J¥611qZ’¤™îîî-Š¢¤é&r”°ëNü "Ü 5ã‰êù`ÆÒÔÔÔ÷^yå•¿^\\\D{8o"ȳè&?ß9xcðb4¸ƒ‚RhkþÄj:±hkʱýû÷ß¿k×®gS©Ô6×…;°"« ½D‹W Ðu}!ŸÏ¿zöìÙ×t]§Çí#ÓyÑ€¶ˆPƒÛû϶V‚YÓ±t<@mÒó?N-¤ @kAH066¶oÇŽvwwe9æº Ÿˆü6x–«Ï¶¹¼¼|9ŸÏŸ?oY²K“–îÓÏzýh·ù“…&?¯5èPý"+€qæ·Óan‡!©6HÔZ '‰ä¾}û =œN§w˲ì;GU$"lUI-ËjÖjµ©b±xîÒ¥Ko–Ëå2ÜsöÑwÒħµ?YÙiK€&hí„dM ÆZDÐUšü¼€¶èsKdY–•ÑÑÑÑ¡¡¡=™Lf4‘H jš–CÛ¿ÀEPc„‚.O!”ŽmÆR£Ñ(®¬¬LÏÎÎ^½qãÆ”®ëÄKðIOÏâKݵ—è“]‹4¿¯öÂÅFÒŽmæ³ZŸ&-íÅçyþÙ€vðO“¹{o\tB¶I­ðšÙˆAùUÎ9èóÒ×ó›woë}Ö>yX“š^ÓunòÛ¤Ö4ñi³Ÿ´`…Éä§- ^}?°¼RˆâØ !šÐ¬µ@§‰ÈO¯YaÃZ$ì³DA„OãÓ¿YÂñÌ}?à'ØôKNÖ>kï¬ïÿ]þÿoüí…^˜šžž¶ÇŽku÷Â/ÒáîÝ»ãÍfó«"²CD¶;ç¦Â0œžùÂìÙ³gسxÒ9÷„R*ã½Ç9‡s­uvå÷ÿ{÷îÝä½ß ìpÎ=hçÖZD`^D^UJ½\,ÿô¹ìÛ·¯O)õ¨ˆì‘§s"‚sï=€UJSJj6›/ïß¿ÿcÀróYÿ1€¬ÕZxÜ{¿Í{ŸöÞc­@)UPJ½®”ú]£ÑøÃÞ½{+§Ÿj·°k×®`ëÖ­A°#‚íÀƒ"¢œsˆÞû€CÖÚWçææÞš½e”Ÿfêf§ÓÓÓÛúúúžÇãO„a8 µî:ë¥Ø³EÑaïýeéÕúvkt£Õj½µ¸¸øÒììlôovïÞ=™ÏçÇïÇ·ˆS$¦=("ˆx¼÷xß~vÖ]ßsî:Èξë8÷ÞOÝe°ªcÌÇKKKÏêõ: (¥0Æ044DE(¥P+"‚ˆ011AµZEDH¥R¸Î|èšsŽÁx‰áþ%ŠÄuô2`#ÄÄæ _çÝ ÷q|~„·¯|‹…rª—¡Í×H§Ó\¹r…î­Çƒ1†l6K6›EDˆ¢¨÷Þ9G³ÙäþóíÂßœ‘ݬADq¾2J¡PÀZKµZe2euü xÏÃk¯qdxˆÏÇ©V«h­{vëìœÃC«Õ"Š"jµ頯ׯNq³…t$ßYÓÖƒ ç©TÚ)\=8H¿ý¤ý®3¾³å4?­ßßËB>Ÿï5c7 (Šˆ¢ˆJ¥‚3Ë|ûîwÈÄÛ·qGÐë°hXk«‹‰o Åæ¾_áÖ=J¥ÙÇdú=&³#½Á4Üñý™9~y|K%jµ™L†D"Ök-­V‹F£AŸZâÙ»þ̦U—zQ_^Îc­­ðƘ¹ãFÿæ=óä’Ž|ñØÐk]ˆaÅXöˆw¦Küè‘·yóì:þrñNJ5¨V«½,ô‡ ¾ ÃL.—‘à–'~FSJ¹J¥²h­­e`hñ¯ðÿŸö/i‰WkàþüIEND®B`‚goodvibes-v0.7.2/data/icons/hicolor/48x48/000077500000000000000000000000001414415210700201505ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/48x48/apps/000077500000000000000000000000001414415210700211135ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/48x48/apps/io.gitlab.Goodvibes.png000066400000000000000000000066331414415210700254210ustar00rootroot00000000000000‰PNG  IHDR00Wù‡sBIT|dˆ pHYsÅʼnÖïtEXtSoftwarewww.inkscape.org›î<tEXtAuthorHector Lahminewskiì“l• óIDAThíš[Œ]ÕyÇkí½ÏeæÌœ™a®Æ{l0ƲEcHŠ ´n¨\Û)QCZ”·ÞQ‰'Þ"K¸–*ƒd©‚Š }*UTŠD1 Q°qJlÌu.žÃ\<3gæÜ÷Þk}}ØgïÙg<Ô¤¸•º¤­}ÎÚk¯õý¿ïÿ]Ö:þ7u½¸V;~ü¸®V«_Ž‰È½"òƒS§N}gã¸ÿUŽ?ÞÁ½"rÔsTD¶Xk¬µ†áøéÓ§/§ßq¯—°q{ôÑGo‚ਈ ÃðɈÖZ¬µ„a <Ï»¸¾žzê)oaaáà>9fŒÙ­”JŒ1±Ðˆˆh­ÏYkÿeffæµó}!:yòä ®ë~ÕsL)uÌÓצÖZŒ1c@ÝZû3cÌKõzý»O>ùä4`7›ûÌ?þøN9f­= ´ÖºJ)Òô0Æ$ßµÖSJ©AðÒÅ‹¿ÿâ‹/6ù¬u~cN:•w]÷n­õ1kíý"2n­%-tZëJ)£µ>/"/c^K¨ôô@\h3ôú¡‡*LLL|Ëó¼oxžwRÊÓZ£µ&yiM§’ ÖÚº1¦•vÐkŒKè+ ý\DÃpÍZûn†/}øá‡Ï¾üòËU6ø‰ÚðÙ9qâÄ× …ÂSžç)¥Ø(x¢Š6€ô‚ŸvÿuûŒ1¥z½þWO?ýô÷³÷ĉV(þÁq­µÆi~À¶žz³k üu &†è²V@,ˆ Ö"6²¾ˆ Æ‚´)fLô޵ˆm[)¿oB*M‡r«‹éú&Ã[<Œ1¦Ñhüù3Ï<ó\ " ~øá[·ný‰ã8YL•½}¯ó;»¯ ô0i ß¾‹$‚%BµïH[¨ö%úÖŸ™õ>cRÏÚý"L– ¼pùe‚0 ›+++¿÷üóÏÿ°nLÁÁÁ@ÖUŽÿ}Ûë 6ññ@t‰h”X°‚(…²”B¬B¡V4¿,õðî\/KÕ ÕV–À*z2-ŠÙÅEöÍÓ“i¥¥Q*²à޾*Ñý*O^ø*óìÌuuuý-p p|ðÁ›öïßÑqusá§>0q^µµ¯®¦M¢õOѼï[~ta3—†)7º„aˆˆàºnâ[ísÇ臶_b ×@¬ÑŒv0(Urœ¾ôÇø6#“““·ž9sæPÃÃÃw‹ˆ š+Üýåy”ã@”x`ƒóÆÂ«6}ÒšØïÇ‹Yžýñ8¥Õa²°PÂ÷}<ÏÃó<\×%Â0DkM?o”næüÂ6Žî<Ï][&£µ•Jléirk÷EέîW}}}¿ |èh­ÇŒ1 wÍÑÛÛÖŠÒí 6ú@,¼ ¢@ ʪ6ˆˆ2g?êæŸ~²jÓ£T*†!»w獵§' ­é†!³³³¬¬¬Ð½u+¯ÎßKÙ?Ë}ãg£R$â–b‰³å}[¡ÈÂ0Ì[k)äš(­yõýï5Íú )ùÓ–aÅ7÷v£lDÈÉ… Ͻ¾J]35õ u„Ý!Ùq¶oߎïûÌÎβsçNÞ•? ¸p÷àùޱÅL5¯ù€1Fcp‹ÒeåÁÏW&eË!Jk¨6à¹oG¹½ÌÌüŠ}ûöÑÝ݈ µî¸§[Ü—ÍfÙ±c³³³ìÙ³‡óá} ¯-qSa:±@V›Îwåû¾J2§Öd²Ÿ?Û{põÌ;£„î\¾|™]»v‘Ïç­WݯÕ722ÂÔÔŽ×ÍÏ*‘TºŠØ$ñB¹€Š-¸!ïÒW_ÿÞÑâ…D`09|¹æòÞÒ͸®ƒµ–þþþ«4G×u¡ãr"m!¥¹\Ž+W®ÐÓ3ÁÛË7ñ¥¾KbXk¬û@{s•·8¨{QÚ­QZG ¦Œ1ëßáÝÙ2ù!Þ=÷v]­u .ÇI66ÖZ‚ HÞËçó,--áº.ïµv$b}ŠHb¬µm Aœþ•"6rδÖm*î§è1[&“Éàû>Ùl6qÖl6‹ã8‰Ö•Rôõõá8«««4›M<Ï# CÇÁ÷ý¸çy¬¬¬PWƒø7ºdt˜V̺‚ P‰¬%©($I­Õ-6é[®X®,'¸®Ûqù¾Ïàà ÅbÇqð<¥¥%Â0Äó<”Rú\.Çââ"žç1½V䦾¥D” %ûÓv¢bm+Êv†Ñt“õº¦\…••r¹@"`|…aH&“Iú]×M>7 ºººzÅ~S ä:(776WÀ¬ÓCI;CÁf%D«Õ++e\×EDp')Å]×% C‚  ÙlR©TÐZÓl6ñ}?ñc Žã$ŸcE´L›‚l`Ý‘CF‚·‹²Í¢PR E–p”Å´– CÛ!|Ì}Çqèêêb~~ÇqÃ¥¾ïS¯×ÉçóÉØ„R*Þü#"t»-6kQÈZÄ„(¥Ûôi ß‘‰×Ki‰}ÀZrN‘ µZmÓ…òù<ÝÝÝT«U@²Ù/‹‰óÆmãiF1[o÷ kLÛÚG—[7p¥9Ž–&ù)dWYbÇ`™¥nZ­V"@¼x,ÌØØkkkT*¬µtww300БâwÚ[JD„Áì Û k×PȬS¥PÀ¥ò&ƒ¯Ñò£0÷~¥Âžü?3^œ[÷±ìYãõKø®K¥RÁó¼ŽS·¸êìííe`` ±@†Éçƒx;ç…[úæRdPWˆ'³ … Xlôó±ÿ‡Ì/,†!­V‹ÞÞ^‚æ!ÆrOã:$ ¾4¾Êho‘J¥Åb±C­uRNÇ?‹¤O묵4 BîüxSZBT ­ó‰(d bBæ«c´|ÃÚÚµZf³ÉÌÌ naÓóÙÔXƒ« ‡ö–PJ‘Éd˜M"Oø¾O«Õ¢ÕjÑh4h6›É=÷}ŸJ¥ÀoÝð;‹Ki!7·€˜ÈÑ.Laƒ*¡ ©T*I’‰´à©F46å wî¸ÂÛ“}ürn„J¥Âââ"CCCÉüéð˜¶|lDµZ%ú½2GÆÿ³cü¦aTÚÞÓ 4š$þe>àãÚr³E„ÁÁAšŸœalW !½?F„o}å=žx-ËeúX]]Å÷}FGG“l ;«µ–V«E¥R!rºÅŸîzƒ\gTk˜ÌÕ¬µks«y0aÿþœá¦Ê³TÑÐ[kè ů¡Û{Õt„Xº\á¯ï<Ï3¿ØËËC„aÈÔÔÅb‘žž2™Nbí×ëuF2Ý5þd×psÿ6¶ùz1~¯œh4“"ÂTy€O–F‹~¯v÷ϳ³ø¼?çÑ7ŒF«„©¯½;‹­Ð— ùöoŸå_ßÙɳã6K½^§V«%–ˆ3l¢¹,·§¹â,#]Õ«„8¿¼Á÷ýIˆÎ)Ek=?22ò—ŽëeWkpûÖ+ÅšF,„ó©".>ÈJÎwÖk#¬ÁÃÞ¡+ì,„–ZÅ—¨ž‰6Š©“UMv÷^æë;ÞâÈöw(xÏ£vqyˆWJ·Ójµê.\ø›r¹\‰}"wøðá¿ø¶ëºÚu?Ú;}uÖ€$KL›„>mgNû„]?ø².,ÞÀR#G¥•ÇXèv›½:{úçèö‚«×JµRµ—¿¿xˆr3ÃòòòS¯¼òÊÃ@#–0344´÷®»îz¡P(Œ;ŽÃ¾Áîßó£½ÍÍgìØ ¦Oê¢ïëçDq”Z/;’gŸ£VóÓOvñÊåÔ—jµZzóÍ7¿177whÅàÆ-[¶»ãŽ;¾S(†ÇA!lë]b´»FþŲa¿°É]% RÏÚmã źà«A×Fh˜ ƪÕêâ[o½õØììì À¤ ·õööÞzÛm·}sddä+Ùl¶ãw­/ºÅk·Z-³°°ðósçÎ}·R©¼¼ ”ÒÆ€íÀX__ßÈøøøí…BaÈuÝÞë ‚J­V[˜žž>[.—€`˜ê€Ýøû€&²†G¨dÚßãƒà/¢ Ññyø@­-°ßîO~¹ù4TêùõúK‚¤îׇÇÿß>£ýª”aVrp­IEND®B`‚goodvibes-v0.7.2/data/icons/hicolor/512x512/000077500000000000000000000000001414415210700203005ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/512x512/apps/000077500000000000000000000000001414415210700212435ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/512x512/apps/io.gitlab.Goodvibes.png000066400000000000000000002362411414415210700255510ustar00rootroot00000000000000‰PNG  IHDRôxÔúsBIT|dˆ pHYs‡‡åñetEXtSoftwarewww.inkscape.org›î<tEXtAuthorHector Lahminewskiì“l• IDATxœì½wœÅ™?ü­î™ÙÙœ´I +… Â`¶1"ie›»W矿gûlŸçóÙÇË%0ÜͲáXœ0lp"êŒÑŠ( ØD[&H r’V›wB×ûÇNÏötWUW÷ôÌάêûùìNwÕSO=ÕÓÓÏ·ž ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( 2Ö((((((ä .Ô§L™2Ç0Œ9„ÓÌ è#„l0 ãµ²²²Go¼ñÆÃcljÁ¡€‚‚‚‚¸ÂW\Ñ iÚE¸ˆRÚ ’§”xXÓ´k~üão+ˆ‘EEJW\qÅ,MÓæSJ;!çЭù”Raùtþ€ÅË–-»-o†PPPPP(9üë¿þkY$ù !¤À|Jét‘¼°Éü¢¶¶ö±XÌÈÝÒâEh¬ PPPPPPÁUW]Õ”L&/¡”v¸@™G:y·|›Ì׎92 àŸ1¼H¡" Å òýïÿ : Ø ¿%ê—ÍÿÒ²e˲´¡€‚‚‚‚BÑ ‹UõõõÍKçÏÐjÍÏÕÁ{Ô±'‰œxÓM7qUZ‚PC cŠÅ‹O3 ãB úûûçBÊxN:àP¿[~["‘ø:€N÷V”PPPPP((b±X¨¿¿ÿ\BÈ|Œ„öO4óvà¾aê ”~㔨!…¼ãŠ+®h(++› `aêEòõ ó !§uvvþյƒŠ(((((ä‹/n§”.ÀÈ$¾ó(¥a p½ü\uXò;(      ÀB,‹ }À”Ò/PJçÉ[¨ß%>€%9UT„PC ¾qÕUW5†qIzCž¬µù…Ó¡C"ß0 câm·Ý¶ÏÕ˜‚Š(((((x¢E‹f¥þJé9„fg²Cý¼|MÓ´‹Ü'4¤Ä €‚‚‚‚‚±X¬"‘HÌ¥”v¤Ãá“̼\³ŒL¡þ0㌨!®¹æšé”Òy™µ?@Ov…úEÙ=555M±X,.TRBP,\¸PŸ1cÆiš¦- „t†q†™W azø­C²Þš¾¾¾OxÖ·EEŽRÄb±†D"1#cù™µù,‡X $ ’`/Ï:7§4ا6†1Š(((((”"/^Ü …PJ;‰Äy¤×æ»a,{ñ¢|Ö¹™fÏ#„À0  ø<€+Ý[QPsÆ1b±X4™Lškó/pœH~ŒÕg9x«¼™nÏ7óì=~MÓ@±§ßÙÙ¹Qh@‰@EÆ~ðƒL2ߦ—L&/PŒ«eyÂ^¾Ýɳ 0 š¦eêLãRŠ(((((Œ=b±˜–J¥N§”^`€s(¥¾×满XBý†adÎYΞ—oý´æ»‹4 8[Ь’‚" %ˆX,VaÆ\JiG*•ê0Ñ쥖B/ß/D¡}Ñ1KÏÙ[¯£= ÀéÁ´dì¡€‚‚‚B‰ ‹ÍL;ûù©TêÓ",¹bqàùì囟f>‹Xá¥-Ö¡ÆÜ€iÒŠŠŠ(((().\¨Ïš5kNúõ¹†qFP½|s/Ÿ—fׄý¶H€á"^2P@AAA¡ˆ‹Å&˜/ס”^hFKn†P@AAAÁî¼óÎðþýû?E) ./×1Q,$ —eqöcQoŸçôóIb¼öòYiæ§õE@„ç{{{ÿÍ—QE õ6@üð‡?lJ&“™µùjy²nHÆAƒ¯kóYþòe›ݬ ”xý¬BÈšÁÁÁËV¬XÑ'4ª¡€‚‚‚×]wݬôXþ>ƒtÄ´T¸Ÿ|k+Œ/ÛËÏÕ¾ Ê²»5ÌÏú´B®Û¾}ûÒ‡~8%4¨D¡€‚‚‚FÖæ÷÷÷Ï%„ÌO‡÷áÉŽ'àeÖ¾—^¾[ý…îå›Ç”ÒLïÞšo‘ÛàgñxügË—/ß)4²Ä¡€‚‚ÂQ‹%K–´¸ˆRÚ‘5úÕ@é8x?:XN\&Äï¦+Ûü”õòÍsQ¨4MÛL)í¢”vUWW¿‹ÅÆÍv¿"(   pÔ ‹ieee§'“Éš¦uPJgƒó,Vî'Ÿ×ËFBý,`‚·9Lýù",'oϳ/ç³ÊQJ“„W)¥]©Tê÷·Þzë¡¡ãŠ(((Œk,]º´rxxø³éñüÚ€`œ³ŒÌX‘Þx¾¨·Ï*[Œ½|;XΞ‘w€Rú$€.]ןºé¦›Ž•P@AAaÜáG?úQ»aPJB˜kóÒvð¬|Þ§hBë#=üËÀY›_,>Wn3öy“øXŸnºýØæ·¬Ç.êå°ÀZÃ0~¿téÒ½Ò•)p¡€‚‚BQaÉ’%-º®_”Þv÷UGK/`åÛ?Ý"~í jÉŸhÖ¾¨—oKß  Ë0Œ®îîîçïºë®„Ð8ÏP@AAaLa®Í° ½T¹6¿X|®:D\4–o=ÎgOݯnQ˜_¦— ³6ŸRúû›o¾ù¨\›_H(   Pp,]º´2•Je­Í/¥^¾¼ w¢p>ëE<~êÎÅn¯ynûìsÆúxÒ0Œ®òòò'c±XÐ8…@¡€‚‚BAð£ý¨]×õ Ò³ö™kóK‰¸åÛÃénöxüüÔ_¨²>zù „¬'„¬J$jmþC…¼À\›¯ëz¥ôsN,%ïU‡hÖ¾Ÿž~±„ò­ðÙË ”þÀêT*õÈÍ7ß<®_°SJP@AA!0ÜrË- Ã8Ÿ² íôkí2¥DüöòÍc{¨ß.SŒûì[óy=|ë9§—¿ÀÓ”ÒÕ}}}O-_¾|XX¡Â˜@…œpã7ÎÒuÝËŸC)ÕÜÊ”* zÖþXõòYù¼µùÖ<^/–µùñx¼ëæ›o~CX¹BQ@Oèìì,O¥Rµù2ŽYF®Býnœê·»çU/Ÿ½üCY—¿–òèøÃ}ž+TS(   àŠ[n¹e*€‹0²ß%”Ò*™rÅàÀ½æ‹zùÖ|»ƒÍÖ/¦P¾5Ïc/6kšÖE)]µÿþÔÚüÒ†" Äb1­ªªêt:ò6=æÚü zù22ùÖÁrà^zúnø Ý“ç•eÞÆ<é²C„—(¥k !¿»á†Þ÷mŒBÑ!4Ö(((–.]Z©ëúgtPJ?G)m•ÙÑÍÍAYÃÆ~edtx…L7aåXezûcÞ¾ú¼^>!ä¥ôIJiW$QkóÇ1T@Aá(ƲeËÚS©Ô„Oö6<õòùYû¢ü êöRÖ ©éå›Ç–!‚õ„U©TªkÉ’%/êå:GPP8Š‹ÅBUUUg›kó)¥'Ê”+Æù22^ÇóE!Wûr™‡`¯çAfÙ^úx€Rú,Ùv·kÉ’%»\•+Œ;(  0ÎqË-·L „œOYÀ±6¿½|™ {ùÖ2¬q|»¾|õÔƒ.ë6cß<¶ôò·Bž¡”v>|øiµ6_A…qˆÎÎÎYš¦uPJPJç(šµùAëíå›Ç2þüÚ–¯²¼û¢cMÓRÖ†ÑE)]uýõ׿)4Nᨃ" ãå„sÓ³öÿÀdkþÑÔË·æ‹ÆóEõŒ•“·‚òç½F7vˆ²Ö0ŒÕñxü±o¼ñ°ÐX…£Š((”(n¿ýö©©Tê"Jé”ÒKTº•)”ƒ—‘ñšÏ:9uÞ¤=·É|n¶å³¬¢YûÖ|BÈfJi!dÕîÝ»ÕÚ|i(  P"x衇ô={öœfsm~©õòÝdDÜž/Ó»÷2¡¯½|Ö¾e¼ž)“Nð€µº®ÿîÚk¯Ukó|A…"ÆŠ+êãñø”Ò !ŸÐêVæhèåÛó¼†òóàå±öM`m¾Ãéå ”>IYE)}J­ÍWŠ((–-[Ö®iÚÎ6óŠ­—/#Ôx¾==ßN<¨ {VÈöòÓé묢”vý×ý×Ë„ùñ (  0ƈÅb¡úúú³)¥„Ï8Á­L)9xQßzÌëé‹vàs«¿Ð½|{>«7/èåx–RÚ¥ëz×5×\£Öæ+äŠ((Œ~ò“Ÿ4¸#½ü‹Ô˜yGK/ßZN6Ô´mùÐë±—¿À3”Ò®úúú§¿ûÝ蝹ù ƒz€‚Bpûí·Ï2 £#¯Ñå®Í—Ý÷>ˆ=ö½ÔçU‡lÏp(fx\ŸŸ°ÎœµÿŸÿùŸjm¾Â˜AEò„;ï¼³bxxøMÓPJ³ÖæÇ^¾—Yû,}¢ }ÅÔËw å3ä"ڇ뮺ê*µ6_¡( €‚B€X¶lÙ´p8|!¥t€yÊx²ã…ÈŽç»…òE[ðºÙ–/À‚  „l6 £‹²ª¹¹ù…o}ë[jm¾BÑA ((䀇zHÿè£NK¥RæÚü3dËòuº²22YZ'êé{Ñ™oد‰½ïâä­¢C„—!k!¿»úê«7æËf…  " ÑÙÙÙ‡çB.ðy-<Ù {ù2rcÕËg¥ÊéËE/À&EVÇîæßà)«Âáð“‹-ê¡ PdP@AA+V¬hO‡õ;œG) »•±¢áþ òyÎØm¬fìû"ÈŽå0!°†RÚõƒü@­ÍW(i(  À@, 555I­Í/t/_F&¨^>KVv2ŸÛ Ed{ù”ÒÏBV…B¡®E‹í¡ PBP@A!Ÿüä'Mº®_’Ë¿ˆRZãZ( /½ÏB8xY<û„<»ÜXLØËµ¬‚^>![(¥ÏPJ»jjjÔÚ|…q 5 P¡Ð0Ÿ¼4}Lá$¢ö´¼…Yo¿ýöYš¦™kóϱÖ+;IÏl®ör™Ðç6†oê/kóÝò)¥)BÈ+„UÖ\uÕUjm¾ÂQP<§rö>wÞyg¥tnzCžù&IW^¡~?=}·™ü^{ãn¶˜¤ÆÌg9q¿“ùò1Úïz,‹us…Æ)PÈ…¾‡<“€ŸýìgӉċ%]•((Œc( àÅr¿½‹¹6?íð=­Í?zù2edä¼/ù¬#ìcܺ^FFÔËgå³Â÷n:ÜòdgûË /¡|3Ý--ÝËßO)} À*MÓÔÚ|Šõ¯Px”Ú½@W¬XÑžÞg¿ƒr×µùAÏÖ—• rÙo|Ýϸ;+Ï­.QÙ ó$Ö皦ý…Rº†RÚµhÑ"µ6_AA¥öÐW%÷ÝwvvF+**>©iÚ\Jé(¥ÇûÕUèe{22nNQÖÁ{°WÈP¾[žhk^K^?!ä9BȪ´ÓWkó| äœ€‚o”äw}Çw4QJ/Ñu}>¥ô"5€·â"”Z/_¤G֑ˆòeòsÍ“™Ìš¦m°Æ0Œ®¡¡¡§b±X\ª ‚‚%é¤P²ßíwÜ1 @‡®ë†ad­ÍäœF>föU/ßZÎO¸>³òýæ±öK`åHišöJúzk-Zô&|¬QPPàCm4~P²éÒ¥µµµs)¥óaY›o]Væö%`2ònrùØÀǯsåû¹^ù¬qÒBž°Ú0ŒÇ¾ÿý﫵ù y„"¥‹’uø°bŊ麮Ï#„tPJçQJËx²¬‰`^œœ,‰(Ô+us*àÉ{!ù‚ŒÃ·õþ7Bº Ãèúð߿뮻xkó‹‡Õ((Œ”´9ÊPÒßU,Ó[[[O#„t¤¾ôÚ|+rû«P¿—нßa‚\õzÍóI* !/X=<<ü»k®¹Fvm¾" CEŠcáðs©Óñ€¾ýöÛ"‘È\ŒlÆ“µ6ß#wë]S/ßÍ¿“½F>dá=)#èåï'„ýwÞyg;€š¦Í§”J­Í*õ8[ÜZ&ß2^zùö´R˜°çVFƒòJi—a]ƒƒƒŽÅb¹2*PP*0v(”ÃÏk=±X,ÚÚÚzn(škÆeŽ‚›ˆFñ<î_¨ }&J}²ž—ëÊ+Gé'„o¾ùææÚÚÚ‹(¥š¦] :õˆzýA­ßÏÕy‹Ê±PyyãßL)]«iZ×Ö­[ŸZ¾|yùí©cRP'P ¿ÈçõõªÛ”§¶²¼óÌÃö3ŸùŒ¶páÂÓ5M›«ëz€sˆ—˜¼DëÂeÊä"'“ïűڼHÆ«M^¹ á ³˜K1Óù)¯¤R©®D"Ñõƒü`=K·¢üAPCÁ"_ß‹^7Y{>S>‹ULœ8ñ³†atèº>ÀD6x†5ÔÈOè“•õ ™1y»<ëØQï:õñlp˧”ð\"‘X=<<üX,+Ƶùª#3¾ ] ~8¹a,~àuwvvN¯¬¬œKY@¹wm~(¶PËçLâDO^ÖÉû%BŒèËúô¾µÏ=÷Ü O<ñ„um¾[%êá­P¨û,¨€wäÃé{íµ‚… êçwÞÙápx~ÚáÏöÙ÷3 àuV¿l/ßë|<»‚@.Ã^#,x]bI¤”þÑ0ŒÕ===Þpà ²kó™êÌ*sС àûM®î7PÀÅìð­cõöq|&–.]ÚXQQq¾®ëéMyêdœTPCþ~Býf¹ CýÅ O´–>(pÉ>Jé3‰DbõÞ½{Ÿ¼õÖ[û¬f€IÝgPD@¡°`ýpÔ½ç5ÀFÐ×E¤/_½ÿÌÍÛm·µ‡B¡ŽP(4Ày„ ñ9œ ‘›®B„úýNÊ Z§[/ßëÊ^Û57(¥ë ÃXmF×C=ô—7ß|Sö™°¿z8+ê~c@EF”·ËÅÙbÇ·¿ýíè¬Y³ÎI÷ò?` ÀÞSßQyÀ=РCý¹ Öݽâ€Ë5ïð|2™\}ðàÁÕ7ÝtÓø{ºõæe£¼2 G ýcôsŽ{í ˆ‡ÐXôîˆÅbÍ…B¡KíkóÝ¿W'íWVvf.cù,äÛñ{Ý,Èï$?‘n ÚbÆÚD"ñøsÏ=÷ôÓO?·‰çš Øõó ¾ €÷g_÷"i­ WÇË+Ÿkï_³fÍÒ¾õ­o …æB:4M›ÃªÃ²†ÛiPÆþe†‚ÚOcÕë—qê~—ûÙ®qÊ0ŒWS©ÔêÞÞÞÕ×_ý[QžÃ÷ûä}Áªç¯+dæ”x-熣š mÀïÃÇ«ÃÏËCnÑ¢E•“'O>_×õù„K5Mã®ÍÏÅñéôyò…rÌùZ™±ïg!û9c Þƒ”ÒçS©Ôã;vìXµ|ùò#æŠzþ2={YýAà¨{+¿‡Dd–'ã¥þ£æþ;€ŸSQ8|¸í¶Û¦‡Ãá¹:4M› ¸÷´ýŒñ{ Èuí~!I€YŸüìØç2cüvò–NßJ¥VÇãñg|ðÁ?¼õÖ[I¦wðœ}1=üT”@›s“ͥg_L¿…¼b<¯–|Áþ%—\¢Ï›7ïìh4:Ÿ2—2ÛQ9Ç™išÆÕ›‹ã—™<È’/†Iw2ðº7ì,~Ba!Û8¾¹6ÿOñxü‰}ûö=ºlÙ²¦ F•^{ðA,óSPÈ7d~¬eÐny~†«Æåoa</ÎYÆéç½7rõÕW7¶´´œ …æ§Çóëx²v'% õóÊxÍó»2 Ð½ü 7úᕱ9ji}†aߨG)ÝžJ¥ž‰ÇãϾóÎ;Oßÿý}v&\/«G­­V(vˆœ;Ïûéá˨Àx"¹8þ œ¾4c½õÖ[OJ;ü¹š¦‡ôw!ë¬e{å~óDѳìXôð‹qóžô”Òu©Têñ#GŽ<þ‹_übÝÎ;ý¬¹/ä¡HB1ƒw²ˆ‚ßå«ãæ~/@Öaα^e—_~yù™gž9G×õŽP(ô9¤×æg) ¸7îÕù»íó¢ãÌ)çÛ†~Jé ÉdrõáÇW-Y²dÄ÷% ~àø½øA/«7E…q–£·§y!ã&Pê“mdì9z·ò9]Ÿë¯¿~rMMÍ¥º®_ªiÚùÊýôÈýŒíû‰$øÕÄ oò¥/ŸeDëù­ç”Ò>Ã0žI¥RO ¿œJ¥Rv½Y8·/ßAyy¹ï‡N( ô¥ëº'}ÃÃÃAV_0”ªÝ"Œe›†††ú‰DrhhˆÞÿý~Þ.i%œ¹O{9·sY=%ƒR%^AœþÌ™3µú§:«¬¬ìÒP(t)€Sü†êsݵOTÆï.€¢ñk;ŠÁù­O4©—çwÓŸB/7§ï/e” žõ%¤”"„4 c?€”Ò­‰Db!dËÖ­[7­Zµ*ïÏg¿Nœ òr­«hPŠÀ‹—uü~fûS¸òÊ+k'Ož°Dy¼—ó˜eüD ¼Øà6³˜¯«²¡´m(»“†a¬ð§ááág÷ìÙó|À„`ÜR!~zý-ñûêW¿ZqòÉ'& ]¬iÚÅš¦MÍRâc)^¾Bü€ÿý¼È˜psún2…Ìˇ>™ºì2ãì!¬lÐëoo¼^J©ƒ¸d÷°a/†ñÔÀÀÀïî¹çžmÜ‚ué^ˆ@Ñ“€b'ùpüRm^¸pa䬳κ4‰|EÓ´ÏRJ£ö¾Ÿp}!~{ž2ùA!¿yùÔçÅÁ—òƒ[Ù¼¾ ëñkC1\¿6A¥”¾‘J¥xäç?ÿùŽt–ì*·4*sÓS4(f ëüe¿t;¯½öÚãþA×õ¯h²¦ŸÞu>vèËe2Ÿ¬Œ_äËñ‹òÇšL˜i2úd CPöE¯²œ‡²aüÙàåwfA)}.•Jýæ½÷Þûß5kÖ r ;ö¸&ÅJd&ìæø.\X>gΜËÓNÿ,^oÝ‹#Ï×F=²ûšÈôrŠ)"à7ÏOï^FŸˆTä{†~±ëS6(xé>ÈôþT*õ˾¾¾ÿ¹÷Þ{ws…ÓE8Çö´’%ÅFríõ{rü_ÿú׫N:餯†B¡Ð8{Ö^yÐcùnÎ>èP¿¬¼×f.Žß-¿HfžÒà·®£E_1Ø}cmC1\‡\#iö4‘„„a .»çž{^ç´±}ŠòD_|Q‘€b"¹ôú=9þï}ï{õS¦Lù—A­Ç IDATP(ô/2J,N\v½Ÿ €Aè”É—•ÉŽ–ˆ€=O¶GR à£M_1Ø B©¶©¢l~ƒé¿'úúú®»÷Þ{ÿÌ5ÖRÌöÉK/‰h@±¿ÎßÍñg¥ûÛß®Ÿ1cÆ•¡Pè[ª³ÓŽR6Ì_ˆÉ|neeò½ÊåÙ‡^)Eüäyíäš§ô^ŸrþùÑç±/,'ó;´äSJ骡¡¡ëî¾ûî·¹…,ÅmŸ¼ô¢Žuþ¬^¿ÌR?Lž›óŒU¨_„ —¬L©9÷bÈ+应¾Âë+‚ÒgžóÒEÇésÃ0Œ»vîÜyíªU«zíÕqÎýD NÆ‚øqþ<ÇŸ¥ëßøFí‰'ž¸LÓ´/ñÆëý:3_Ñ™ò…èåû-ë'¼éµ¬Šä'¯”ëRúJ__±ØÀËãí6h/C)Ý1<<üµ{î¹ç%–8ã¼èI€^ÈÊ vþÖ?{º½¼c(`É’%çOœ8±KÓ´socùnËÿX:ìeeÂü~ô%[Hx! ¥È—ÞbpâÅb‡ÒWúúŠÁ¬g4¥ÔKÇ«6 }yöìÙ Ñhôù;v¤¬ÙvqÉOG5RÆ„BVææüáá3ƒ .¸ lÞ¼y×E"‘5óY_4+<ïwãž\¢ùêå%ð(†ˆ€[¾Ê_u)}¥¯/hx¯ê¶—±—OG^ïííýû_ÿú×;à„L4 ("…òyqþÿþïÿÞÖÖÖö!d¶Ý©»ÇçkÙ^ù^å‚.+‚Wgïµ\1Gr)[ yª.¥ïhÑÇrô&<ƒƒñxüË÷ÜsÏóVQÁqÑ‘€B xuþötÖÌ\{íµ§755=I9ŽçôE‘/À®'¨ {¥2³ß/¼…±vünùùÈˇÞb!¢¼R®KéúL؟픲‡!}„ŠP(ô¥SO=uøÏþó+f2ä&­»Mh¥†|?Ο%“…%K–|¡ººz%!¤çÐYò¼8qMÓrZ¤L.ò…„×è@N_FFEJÛéŽ×º”¾âÑgwð²Ä€RªéºþÙ3Î8cj(zj×®]æ¼–_EµÇ„䓸uþÜ^?Üxãß)++»@Ätò2½|ÙeyANè“•ñ"T¹\áÕÙ{-7ÖŽß-ÿh& G£Jßø×Njۉ „SZZZæLš4©ëý÷ß6“ámh»à$ _ŠsuþŽr§œrŠöå/ùN]×/Ï$âè鳆¸FºDÆ¢—”CÏU_Ÿ‹Pyª.¥¯ô™iöOk>§Üú,X¹r¥ýUÃTâÓžÆ*( Iü8³fÍÒ¿ò•¯Ü¥ëú—2Œžº×É~22…ìåSJqäÈôõõeþ†††RuŒ5‚"~ôæZw.åÇ¢l©—+;ü¢ôñBÓnú m{8†¦i…B(//G4E4Eee%B¡´¾B’ ûd@«¬€À¶ÞÞÞKó›ßl¶gK|”äƒðœ¿ù)ãü3Çiç·®ë_Ìd2Öíûì7¡þ¡¡!ìÚµ »víÂÎ;qð£Ý8øÑA|t¨ÉäÈRueê"¨®¥©¯o2ÄC~Üq>!aqÁEó_'åž°Z¯õú´Ówó\ RÀü:$stx„¸WŸ%k5ŠqÈ–åóf»üCzr^dí6 ÄSÀPœ¢{82B*€ÔTFQSSƒÊšzÔ74¡±±¨®®ÎÖ9F‘‘ãçØ{øðáù>øàz–zdß vÇ_04ÔùŸqÆ‘/~ñ‹¿Ôuýs™L†Ó °Îíiù&‡ÂƱy󇨶e#vî:€Pˆàø©•øØŒN˜ÁÔÖ¦µ…1µ5‚ u!„ôíKû8÷SÔ«ƒwèËËQg’=E¨&—|Ê«4“è4‘²ÄØJxy®e¨3—û`䵕Îk¯ReÉo¿°}ü4÷vql—jg,›eÓ2fÈ´×o»ö{N“±=û¼{Øs˜bç!`Ç!`óàÝÝa¼·›âÈEueÍ­ÑÜ:mmmhnnæMÄs¤¹åy-c ð0°ïСCó~øáM¬jàtô'ù&¹:ÿ_iš¶ÈîÑ‹Æùý:û {ù”RlÚ´ ï¾û6¼ûWìÜ}Ó&VàÜSÊqÎÉ8ûc•8~JYáü¨…ÞK¸ñêØ%Ë6XäÌÝòÅŽ‘]µ„ƒÌ:;{^ºô5v8¼tE¤Ò2fH8|Ù´ÀH€\{¨#Mâ»°§øË6à-À+[Âxw§²H&3 ÇL™ŽiÓ¦!‰tuH@f8 ]fב#Gæ=øàƒ[ál¨ÝÑSF^ÉѸ¿çOššš´ÿøÿø¥¦i—e2]ÆüeÆòƒØ G4Þöá‡âÍ7ßÀ_×½‰ÁÁ!œF .S‰ ?QƒimR×jåØm!¯LL ¼8w¯e‚’Ë…¤Kˆˆ€[¥‰ÏÙ‹òüw‡ÉKw!7 ›¹dg¼€@ˆcÀ7 p¶‰ŠlÍ…À0É‹£"‰ßÐ7¼¸xnÁšõ!ì'8æ˜)˜Ö~ÚÛÛóò5‡@v8ÀVfóÁƒç­\¹r™e³œœåzrqþöOüð‡?\‰Dþ-K™mÆ¿ŸÝýò1¡¯§§¯¼ò ^ùÓpøð\ø‰Z,œ[KÏ©EU¹¿qû‚Â×mäî|?%jƒ$ÞdœÒÂF¹ä‚øsö¼t9"$h—ÀI?zI» N5þ>—äÉö H€ ƒonV­#Xµ.„ž!ÓgNœ…æææ¼O t`•1 cî]»>ûøã1E­Å,ç¢c‡i\£%ð:î/tþ×_ýW¢Ñè Ñ®~^'üåc¼ÇŽxîÙgðæ›Á±S+ðÍÏÕâK6 ¾ZG0—5ß¼w¤o1± ÿ÷!Š 0òg¾ˆë*Ÿø°€ ’Œ$p'ÏV/$`·)Wà¥ãÈ~'q—ö1ívGÊžßüæÏn hlœ€“>vÚÛÛ³:ŒAO  °Ê¥É k×®ýüæÍ›ãf²UÄr^0àÛù_sÍ5ÔÖÖ®$„dÅs¬ÎßË„¿\Æûyrï½÷žyêqlü`3>÷é\ñÅF|âäJ)}ž 21gÞ' /ÎÙ›ûwU@2àF‡‘rÑ"°>`ùröîéîN“—®H@–}²Ž4sê“á|€ £,èî{™àÞ—tP½'~ìã8餓„¯iÏe›ãgD`ƽ÷ÜsÏ·í*-Ÿ¢ã¢#^Cÿš%-ëóÊ+¯<©¥¥e-!¤Úm’ŸL?ˆñ~«Ü¦M›ðøêßcëÖíøÇøÞ'`úÄ2N!)ÕôqätÛø%Y2 r–ŽdQ›ü?Î^”gq`®Ž’¡À³Ã´Õ™IÏ•ðÒóL¼´'GbàîHý·+¸INÛƒ› c7K.7ƒqà‘×Û׆З(ÇI§ž…N8ºž½ñm­çn/2“Éäüâ¿XaŠ0> Fr!¹ŒûgË/¿¼þ”SNy™2ÅîàY=ú Æû­:yØ·oýÝÃxï½øjG#ÿC &5‡E¥ê-¸Ü7žn+Yg-WÆ™p#/y"\µbgïÌ= 4 º>9 ÚÈ#9ãäÚs•ú.r!B ›æl‹oòâÃIàáWÛÖ„1H«0ûÌsÑÞÞž®"wçoMç…þæpA¢¯¯oþý÷ßÿ²™Ìø4 FãKô‡ €›£ç®øÑ~ô[]×?gwö¼I"ïu¼Ÿ'788ˆÇ_ÿðt|ª7þK[z&)9ø\áÇ©»ú Âñonr®2òDÁA DÎÞsž;Að¼œ.sÊû~ÄeWÇ)j‹"|{s!ß›ÈN»2§:ßžV¨(€à àž?ËׄPÛØ†3Ïþ4˜²~'Ênl‘Û¿ÿþs{ì1sË` °;~ëyVU\ðëͼôþyi€ë®»î›ÑhtàtÞ^^öÃ:wKçÉ­[·+y“'·^1çžZ%UÞ©0gp¹ùmyuì~Ë93óCü‡ëÏ>˃³çµ+øh€˜NœJªƒ'NS‚$E0)0¯óüÙ=$ö÷K×ð¿á„Y§ãôÙgd ä²zÀË|3"@)}é÷¿ÿý¥}ô‘ùAž£Ï ŠørþW\qʼnmmmP8Cü,GïgË_™|èííÅÃÝ 6à?¿ÞŠï~±‰¿Y3¹£~¹„.W=^êöIÆšø(èƒ pØY‡Á‘©væ4LPä$€Õ»vT“ßIyŸàÙnVšóûñ»APxå`ñÃ!Š×`Χ梵µ5'çÏ:ç½GÀH&“ÿ}ï½÷.5Å,[‡²ªá˟׻…þe†pÖYgE?þñ?ªiÚ$ ÛA[Cÿ¢µÿÖô\—÷½ûî»øéË1©®«;Ûqé¹µÐ4âl!#Î ×ú‹Œvë<4Ï®.å\yg†ÕDná¬$ŽrG>«jÁEÈÜ6uŒ<‡bÞuá^–™$ÛL»|Ö!çú¯‰=}´¾ÑtÁµq&ŠÛfK“k“ -sÊNsšÂºdÒØmpVÍo+×~VaX/m;#Á¡Þž&c7«Ö÷c—c}7ùÁäàKgÂ#k7!O¡µ­Íá¼Î0ËóòÌt›¯ûd{{ûÚõë×›CV#¼^ÏW-(`}ªØŸȩ̂ýíN]×/àèí³^ô“¯1ÿd2‰•+Æc¿Wµ w,: µ!˃›Ûy8Ôüån¶ÃxÏÄÀf„«M¶L®¬SÎég³’DùÍïŽ×hàåe)Íf1ÙÆ8ª³]FºÐaFzæ”sM˜×‚0Là´±I€ Íb1v[U³ÚjW!Ñ&É&YaÙîzÉüÚm¯†U‘].§—!øäqÀœ)<úÇýØøÁv´´MFYÙÈÊ®\' ²ÈÇ?i‘H䓚¦Ý·gÏž$œWBx¥s…WÀ2ŽØŽímÍ^îꫯ>§¢¢âÇfº5ä/îçóÒxèîîÆ]wþºlÆêe3ð·Ÿ­O—÷p}3­%ü?ŽçcåñÇÔ.C<¥ÛMÝ&‡ÑYfmré3ëeçÌ:e(µ~§Ì‡© ÏjK;ÓÙ§%E×ÙnfvœBÄq ‘κ‚6rdŠEí²¥”pÛÌHLãþ4díç\BiÛùçAÆžæ”q^=ÁbR=°ðLwàé?nBMÝÔÔÔpå½8·ˆ€™GihnnÖ×­[÷¼™Í©Þß…À àUnýýk¶Ob9ÒÞÞš3g΃„3Ñ:ÙÏmÜ_¶ÇïF6mÚ„;V,Ç©í«omç¯é7[a>ô¹Î=ûbÈüå Þm0 ëÏ‹±v=ŽŠø– /Ž-ƒ)Çh•ù9dXE8†šß»×õúúú2y^VȬ^³•™0sæÌEvqÆq ÈÇÆd"Ö?{èŸ444+¯¼òMÓN³öôí;ý¹Möó³ÖŸRŠßüú>ìØºO/Ÿ‰ã§FY…\õºÔšõQRpÛÇ“.Áƒ•"U%oó/2¬bØ Âõ²’xy¼ qäʸo®Ã»n²›ëŒ&xÞBØË&5– üµI¶=ìvú‡uô…Ð;¤£wHGÏPÉÁ@\¥@Šƒ‰‘ÐTŠ@×FDB"úÈ>+ÑP a¢²,‰ê²ªÊ¨‰¦PIdä=Û/a{v]®?7-¨]íÈ×kƒÇj‡@âIàß~«ã¥­µøì¼TV²ßü*"–ÿe8Øø®]»f?õÔSÛ0z¨àÏžŸU¨}¹ÖŸ}ó,^¼øÒÚÚÚ€lgÎÛñçèE¯utJxèÁûñÁûëðüOÃŒÉef¦´›ÖÒtò~‘+9œäâèÝò=©<¯Á HKytø,»³M* Hìë‰`ozÃ8ÐÁ¾ž‘¿úÂèIû ÝòHI9_æÆEyØ@}E­5ÃhªFSÕÈ_suÍUè*K²©åÃ< †°¿×ëž·wø³y,wä!ißýµ†?moÀùó:P^^žm’‡­‚íÛóÞ!H$~õ«_ýÊ|m0ËÉ›w«ýeAÙÞ¿}â_V˜â†nx^×õÙ¬ÝþÌ4·ÿ¼Nü€ßýn%Þ^÷Ö®8'µ—»­-ë#w¸( ªYH=>tñfÒ²RdÚ"”‘… ‰ÏÙ‹òÄéÔžÁk‡t:Ÿ‚vÊ>Ð3§Á’€ý½!l?ÅöƒQl=ÅÖ¢Ø×SÃ"§i!]ˆ]×3ÑEMÓ2 óÜ„ìü"sþùé86RH)¤Rنו'1µ¡Së0¥nS1©v!-Åün˜×_ÖÁfNíi~·9fÜ?Ží®¤À“Íœ´bŒ#‘€¾OǺýpþó=oÄ#‚ÏÔ¾}ûÎ|â‰'6™*ÒŸ²=µåYeÁ9Ï  Õû¿úê«çWWWß8{ÿ¬4ÙÍ}Ü~ÀÏ>»Ï®ykï8§WáÞLßN˜5¾æWWáæ}éòP^– ¸þØEŽÞ-¿PDÀCº­2?N“Ÿ^Ü$`(¡áƒ}Q¼··ïï©Àûû*0Ow„B4= ]׳þ¼DóJ)R© Ã@2™9N%‘H&3ÍÔ5`jýŽoîÁñM½8¾¹  dÜ^ªÃ¥Ê:Ô@†äìsVãbŸè·“G %€¯ÝÂÖIøÌÜ‹…>ˆE Ì4Þ{ì¯ ¦”>pß}÷}ÓL²|š9G¼/½ äŠ+®x^×õÓÑ^¾ÛØ¿ì[þxyýë_ñ«_Þ‡‡nœùŸ¬c7˳“f͆Ç9<é²’å¤ÈÏÑÙ¤óI²Là‘Qž‚à…ðÒeIÀhB!IÀP‚àí•xgW%Öï®ÀöƒÑLÏ>Ö¡éa„B¡ÌŸ—è_1!•J!™L"™L"‘H •JÂH7tBe'¶ÁI­=8¥­qKÉ9TfçÞ±ÝLâ’#‘)Ö(tÿçŽ0†£3qö¹ŸfÞ“¼¨€—¹éãÔÎ;O_³fÍVd;v^ Pà·÷Ÿ¡ãW_}õüªªªû3 Ò¡8Þ˜?Ëñ{ùÑB°cÇܾü6t^1ßøBӨ鞞„yè>ÈñõÈá‡ã—xaìA‘<‚EléYG<û¥þ‘ ìë ã­x}K5ÞÚQ‰¤A i€®‡ü…B¡¢èÕç&H$0R $’#Ρ©j§´ÁìɇqJ[7ºáÉy²ÓŠ}B Òâå™0¶}|ñ§4Mû8>ö±SFMò9€E ÌãD"±â7¿ùÍbŒ6ÖîèsŠE½¸á†~¯iÚù€3ÔoØ­i²$ ¿¿·v.ÅßÍ-DzÿoŠß›‹Ã(Ö‹›óô¥KÖÁ{g…Ñìg2m9{ODÀÎÜùybÇÈ©X¢wO9éYH‘^z°$€ذ;Š?}PƒW?¬Â¡þ0€‘¾Š ¬¬¬¤{÷A!•J!##žˆƒR ,DqêÄÃ8{êG8cÒa”…lC¼9‘ß™–ã„À IKðúfà«?/ÃÇϹ“&M |.s¾Ißûï¿⫯¾zÙŽ=(ïWç6ùÏîð½ÿo|ãÓÛÛÛÿb¦Ù'ûñVdUêa(À0 Ü}ר-;€5+ŽC8$ÉmüÏes¨ÃÿÜ¢ty/„ 2 |äHxŽ3}ÈqÏ.ÎÞkzéJ{GœþK«Ñ=‚F€p¤ ‘H‘HdÜ÷ðs¥‰D"M†LQ„ugL>Œ9SâôI‡2KE4[©Ã¥Ê‘€¢ZÈú} ¸ï%à–gªñ™yŸCUUSFv.€•ØÉ@"‘¸ö·¿ýím¦¨åÏÞû÷`ïq(‹ØÓɤI“¾—†dwü“‘_³æ<°OþòDŽó÷2³?(gïaøÂzKIo?4Ó"v‚ÌòÄ&&’·ÊòäL™Ñ|³*jÍg:?ó»g=˜Üò¨­"[ÍéÑ¥³l‚™S‡‚t!b¯PN2×'+—ÆCšŒ´‡×N^ivZ¶j‹@æ0;mowkß­Å ïWãP_šFGPS3âøö^¾,!¢T!‘H`xxoìÔñʶF”…(Î<æ#|væ~œØt„³ªÙyɤ0Œ±Ý_Î{Ëy˲î){•îu»Ú+a~>ñçÝÑ—ÿø<>sÁ¥Rëåýæ²uV^(úG?ÁHk‰å“0Ò<]™€µ×oÿdõþÉ 'œ¾üòËßÑ4­ÕÞ˽ñÏKß*³cÇ,ÿÉ­xäæ™¸øsÒŸ—^>žz*ËJ-–瞨³k’Ô婜ÈÊv“a0kûW…K³zV…Q+¯^Ó³•qÓ³ŽXâ9EÌžC.}˜H¼¶¹kÞ©Å;;GVÛŒ8¯2”••)§0âñøÈßðREsÕ.˜¹çÍ8€Úèð¨ ë7$Ó«f¦1î—1[ÈúýŒ è¾°<ŒhËl|ì”ÓFLrà­ý·Ï°æ÷õõÍ_¹r勦ËŸµ÷Om@öÅqÆŠºÏŽsæçç?ÿùù„Vk#Dcùöý½Œû á7¿¾ß¼¬ŸSÏoÓ|ö©TkJÎϸ ’nl^P[æ·ÉÊe赋QG‚  aÿà³ä\¢Öûüo²4¦»æ¼ˆ€(`D²{ýÌtX;LŒh/°w¡FÛÈj¿¥W6ZŠÕëÏ–ÍjcŽ‘€½GÂxê­Z<·¾}CÂ! å(++ƒ®{y˸‚˜‘ZY‰ááat‡qÿº(|k*ΘtóŽÛ‹“[ºG~Œ{ÊW€%ãèŒЬ{êèˆT[I£ IDATE›&ðwÿ-­ÑÔÔÄ•µú7óm€<2`/W^^þ&àEÀ8‚õKe9{ûo€ /¼ðFMÓ¦óÖýÛ¼×e~V¬^½ ñþx覙…ˆÓzV3ì­ɦÿ0º¥}ú_ž­ƒÿr¨Ó¼4ö?dZ/U~4Û¥}™l9—ë”ùB²UgÎ̆°”‹ò,ÌtÂH‡]%É!ŒôÌ)çº2ÓY5d_ŽAYfeÌv™j7ïâW/MÀk[°qo9 •¡²²UUÕ‡Ãjl¿@ „  !¢,@°³;‚>lÂk;&@'“k³v:4ï%ÎmkUÎHc$x~t‰~à Ö½/ú­ŽÚê€d*…§ßøSÛcþü¼<È–7CÓ´ŸîÝ»7î’§@Ž Ü€ýélßèÇ1ûáÂ…mmmmKGl6ümíǬs^ÚöíÛ±ò‘GðÐM3Ñ>‰±Ç?Ïé‹ä¬îÎâe‰‚´ƒ,äÞì¶“Oí•–u“~žØî–fAú¨)6Äq`9e´0dG[YéYJXíc¶9[!…†W?¨ÄŠ5-xàO Øu8вhjjjP^^Î}]ªBa i"‘ÊË+ ë:º4¼¾£Ïmn ÇÔ Œ,'8÷÷!áIFîmvu2z]RdÌÏ3NŸ <¾.ý½:ZZZ³òDsDólÕÖÖ¾ÿî»ï¾cŠ10cUƒìh^~¹ÄåȱÇ{1Õûϲ4Ý@Ùƒýýʆaà‘‡ïÇ×/kƹ§V3Ì„KÓmO×›Hâ.ó|#æëÎe„ë¤ÅyÂÙa7bËrØÂtYY„-g•eMœË’!îâ¡æ’Àu2 /=«Ì¨ §‰tYÆ2Òó593¢À ë«ñð« ØßB8¤£ººBí1¢Ñ(¢Ñ(âñ8ðÛ¿LÅÊ·'ã¢c÷`ÁI»P]–`»ëX®„uÿ°îÖ°“¬ÊìYöß ë·àAo ±Ï%ðÕŸ¿…c¦LE]]°×o‚åìyåÂáðeÄhCíOÖ¹Ð{€E íšíÓú‡¹sç^M™á6ñ×ë—}°¼üòËØ²ém¬¼åX”EôQ²¬á7…{÷.WÅÁ|eþòuK™'./ŽpT9OÕ ®•k¾ù³ªÍÎsTÌk?÷»æ§gWE"œï„ù=q®•媦sÚÇj›%‚àÅ÷ªpKW+ž[_ƒ ²² UUÕjÍ~‰@×õ‘á²2$ ‚õ{+ðÔ¦6Ä“¦7ô#¢3ˆ¤+ÜeŽæ(ÀÄz`߯¾ß‡c¦¶såüB¦ÖÔÔülûöíÃŽLŸÃnÀúj_{øßñ7oÞ¼ÊéÓ§wÂY`™ÍDį~ùsÜô½ÉøÄÉÕ’NkôùÉö'tŒ>»ŒoêÏ·!âKâšI G²’†gŸL>«©æ—ž]íh³ Ý.é¬ `#„!Âz¨±Ú˹–9’ àÍÍøqW žz«ÃɪªªQYY¥Âü% MÓPVV†HY’ðîž*<¹± ƒÉf6ö pÞSœßš=ÁõY㪄‘eÿ}ÈTâÙÀqÊ1¿za¡ŠFÔÔÔ8òÝœ¿ËÖÂzyyù»ëׯ_Ÿ>g5˜5 À…ˆˆ<+ €Ë.»ì’h4º³‡ïà•³ã‰ÇW#J`ù¢i Ì„Ñ$߫ӓô°RNÙM(ˆ?Y[dᦃ£Ørý‰‹Üh2Ç@©|ÑwcÞ6uÈ2ÐY¡(y Xé£&°Ò™¾Ì)§­Ìk8z•9•Ž [’×ï,Ã͵`ÕŸë0qüf_¡ôa%‰$ņ½•XóÁDDÃ)L«ïƒæúL0á.”¯(€³ë·2¶¨ˆ!’ÂoÁ´öã2þ‹7ÖoBf¸4Mëûí·»Àn-åóõY푺ªªªKyY/<½\ww7þø§?âæïMFöôQ³Ìç ó¹Ç0]Ê¡ œSÌ·ç n6æq„¼\;¯d@:Ÿeª °‰gÈRÈ%¬jx$€dª'vÙ¬C %ò$à@o?îjµMÄŽCQTWW£®¾!óºS…ñ…P(„šÚZÔÕ×#(~ñÆ ,zb6ÞÞËØ?`ß7¬ûÝócMP€÷,/¿ŸÔ„Ž`Ë–-äBþ²y„yµµµÖŽ;ï¢H],‘" ‚4‰˜Ø'0Afc‡[ÖG—ýØž†K/½ô$ ¼qŒŒbNØCæÇwøða¼öúëxòNÈTÍ.Æqr‰ÞdyàŠàͪ‚s/oÖõ³`εe2’F­ßýheägëäóV˜ ÖŠ±b@¼Z€@~¥€ÅGº}•@–@úÔÖžŒO–µBÀ¹:à•åøÙÚFé×Q¢¦²²ä×ïSJÇ144„¡¡! #•JeÞ¸g¾Ž7•JÙu-•J9æ#QJ¡ë:Ì–™o(Ôu=s‡QV6²Óa4-ék‰D®oÀàà Öï?1Ÿ;i'¾pÒ¶Ñ÷ ä{EãÖe¦a„X8v,B|~6p÷‹}زe fΜ™•'ëøí+Ì㪪ªó¬s1Á¼‚œ+9™>–Çr¤544œk7ÔËZ2ðÒ‹À'N®Á§N«‘sÒÌÞ^dƒ(W0ˆHã¾àÞ*ök+"2àpŽÙ2¦Y”•Ÿ¥Ç ⥃¬eƒ„˜Y…Óìåṡ£o`KÏêÓq÷ÚF¼²©‘pµuU%×ã7 èïïG?088ˆd2™õr¬ç kG6S7€ I€ááá,ÏÒa‚ŠŠ ”——£¢¢%sm !¨¨¨@4E?}÷üi{¾uÖû8¡é¯ܼ½ó–8|±~•L]ž*È "!à›ŸŽã¦5ï`ÆŒܹÖ{uÿš°–Óuýl·ÁéäyNŸó@ô¾@V¯ßrL"‘ÈœLb:œæ¥‡ï›o¾†_ß0Ýæ‹dzû2=} Ç/g„_÷;ïÚ¹õöíe)§·/+çìéfR(#?+É- pê `õnXé^ÈXl €PJðÂúJÜó\† *++Q^^^ô!oJ)ÐÛÛ‹žžôööbhh(kûT»7Á{þØ;'¢çK¿9ÉznFúúú²ôD"TTT ººUUU¨ªª*êh¦i¨®®F4ÅÁ¾\÷ì©øäÔ}ø‡Ù¢*’”‹x›„çïÂÅá3“yÄyl±à4`ùÚ^ìܹÇsL`st]? îNœ´,„,‚^`'Ð4íLQ¯á~{¹W^y'„G÷û÷ÛÛ÷âô½WätÈH1I¯Œ´ÔƒumE:ìò‰--K.š@™(S!àdá2òo¼ö2~ðµfhšÈñçèôý:|—rùø‰{Ó™î­0 ²œ—K\o—•‰ ÈE„D ë”÷Pâü.xüh@ÎC–´,›GN‚$Ͼ]Ÿ­m@2¥¡ºº Ñ(k»ì±E2™Dww7:„îîn ;÷8õæíΛ—fž»-=6Ë‹¶,ç=·¬k)èëëC__öîÝ MÓPSSƒÚÚZÔÕÕÕÊ BF"EeeeèëíÁÒgaîŒ=øOûe¡”E.ÏŽ€°¿š#Ii›Ü{pç‹áðá误ÏÊË%"PSS3À&Œ6ÜëE#(ïm€"ÿâ Ç{쩼qÑ<Öx |ðz{{ñwë´Ây"—ïUÎ!BØÉ¹Â‹"÷7[5a7R@l‡~{üV–3w&ÈFEYyþ£™#OCü^?+=WÐ?Lp×Ó xqC"áêêk‹*ôœL&qðàA8pÝÝÝŽ¾n=‘“æÉYåÃápfì&eVRb¦™×Ø>qË”µê7 ‘#GÐÝÝmÛ¶¡¢¢¨¯¯/² …PW? xöÃ6¼µ·ß9{Ž›Ð“–°ÿžœ¿ ÿæF¾‹C-É€ª€óOàím[²@®Ã¡Pè$K²õ*Y ,ÇL¥nsˆíØþ‹ HEEÅÉ,ãy]Ä Y2~ãUüí¨¯ ãø½8ý >Éü žT²Ox!œ™ðÄrÔvÈNŽF£(//ÏÌq(++˼>7‰ ¬¬,31OÓ4.)J&“›‡‡‡Ç3« âñxf⡹â€7DaÿÔu=+:`hš†ÁÁAìܹ»víByy9ÐØØˆH$"y•ósRãáÞü÷³§ãofmÃe'mƒfŸ ÌÇÿü¾'’ÀÍv)S üÍ)leM3Áè,Ÿ o d>ôd' ‰@(:ŽUHvÜ_Äú‡††°~ÃÜôã:ãü8}’}*ƒ"Ïá4„M ìtGžˇˆ ÈšvÈvE,g™¥‚ñûqvW0ZœÑÞ@“³ÔˆI€àk±òOU…C¨««)Š]üÌp÷L&¹ÏÖ8¾5ÝšVYY‰êêê‘M‹êêP]]ÊÊÊŒƒÍæu …BÌÙÌCCCèëëCoooæs``€9Á%°³ÀîÝ»QSSƒ¦¦&ÔÖÖŽéœp8Œú†Fôôôà‘w¦býþZ|wÎzÔFã÷¿X¿ƒì,N ~JqøÌ™ L¨ÆîÝ»qÌ1Ç0e8cýÜÕš¦±"¬›CxyB„B¡™¢|¯+¬Ø°aêªuœ{Z rsü;ý¼þeuqw³ˆóÇË'¹\‰À¨C4«p?Ñó^ÈiH Æj“×¹œ6›_Bï Açc øëÖ‘7ÁUUU©£H¥RØ·oöîÝ›53Þ QÏßê C¡êêêÐØØˆ††444d;A`=Dó BÊËËQ^^ަ¦¦¬6ôôôd&49r$3¿ÁÚfkÂ0Œ,B`¶¥§§GŽA8Æ„ 0a„œç ÈÜ<™ºº: â½Àâ§?Ž+Ï}3xËÓº`çÕ‡/ÖÂSX´Ðpé)q<¶i;“øh;ùä“«ßyçFóiÊ‚×!ûñÈ£’!°CvìÖ¿óW|ñ†ìÉnŽ_¶·ïÅéçô@åõ¾s…¤Ršù'¯7Kµˆx%""à–o'NgO`úg^oÀK4€°¯`¶) š°u_?úßFìÑ3K¸Æ ñx»wïÆÞ½{ÇGLdôæY=c𦡡¡ÍÍÍhnnFMMëüÞ’¾ À›SàV!µµµ¨­­ÅÔ©SŒD3:”1‡LXW(XÉ€9w ‘H`ï޽طoêëëÑÒÒ’âñaõ#c•«¬¬D$Á‘îCøïgOÃå§~ˆ‹Ýi•Dö îÁYó~«Òå² fùcîI÷ýi‰DfÊÍñ‹V@[[Û”wÞyç]d÷þ=5V´ ÔñÙgŸ]K)mÈ$úp”[–-Ç$̪yDÀæìYÎÒvhOí”3»Ï^V2DìÍ™ì·äálÜAUUÊËËò‡x<Ž­[·bïÞ½\ûF:P]]©S§búôé‡ÃŽ™ï~áæœsqÔ,ÝÖtùhCuu5Ž;î8̘1ÀîÝ»qøðáL¾IœìD€RŠþþ~lܸUUU˜:u*ªªª\ë÷b«…ZñÑGûqÍšÙ¸âœwqjëÁlpÀ%(™=>> ÐŒA9r$kÈFQr!œ,Ñ•uýf*Ðu½Q´†W!èîîÆ¡Ã=øôìŒZ}:~©Þ¾§O„æxÒ<üX×R†züL9$†D“¥‡DÑ/C$À–ž; È®wÏáþûè ¡¦¶¦ kà ÃÀÎ;±}ûvî w¬á}ó¯­­ 3gÎDsss–¬ ¿š51•Ÿ‹ó·ƒ§+—6X?« º®£¥¥ÍÍÍ™=öìÙ“™hÖm+ñéïïÇúõëÑØØˆ)S¦ ‰äê—)FÑÚ:öïÅÍ/ž‚œý>æ¶ï¶JÁû0€=Ó…4ø eÀ‰m<ˆºººœ'jšÖ`˲¶œuì¸2~@Vd@×õZ«Q€ÿȪcÛ¶mhŤ–2Km~PNßÃÙ4XÎÜ ™Þ¾åÀÈŒL¶ÃcF¤œ½™gwö–tÛ¡#!`È{!Ãr$l»ß߯u6"žÔQ[[ØÉ~Ä–-[000àȳ¯‡'d亓'OÆqÇ— C‹ÞræöÞ·Ìlü\zéAD*ì°;}™6”——ãØcE{{;öìÙƒ;vd–Z‰„5 iZf‹es¾…u#¯ÏiYùp8ŒÖ¶IØ¿o/îùóñèK„ñùã·y© ìhØi"™Si/0ΜÇÊ@i»¯òÖ{†ÒçSG˜ð³5ƒ˜7³B°sÇœ{šÌä¿àÃDxZPðC Œ[‚ç¼-²d€åM GŸM8Ñ€Ýc6+t8M^º…À|ØHàtø\ÀK—%¬¶fÛýζ(nx¨tÔÖÕlKß|ðÁ™Ù0­µü~B¡f̘3fd–"Ê8Î\&/Ê:æ :$ùÐï§ŽP(„cŽ9“&MÂþýû±uëV eͰ륔bÏž=8xð ¦OŸî˜(Ȫ'W„B!´M±ñ·Ú1×ñ>¶y¤Ø69vÙ JfàcÇ¿|}¿¯²ö{…RÊÿ"%Ä€x üû÷mÇßœSi­ÊV»_Ç/ãÐ=F¸†x($~öj©#APÆ… X#5̇Ÿˆ' Ñ€¬ôl¡QŸ,áð3ip—ÍVÎ .°££vÿùƒ2ÜøH é¨®.Ì~þ¦³Ø¼y³°çn ?O:'žx¢'ÇoÕÃ:fÙÅ:·þBÔ‘«~]×ÑÖÖ†ÖÖVì߿۶mC¿c¢ µžx<Ž÷ßõõõ˜1c†¯a$¯sZ[[¡ë:~ÿ0ák³7¥…œg÷²Ø3]HCŽœ"_8q"Œbpp0kLÈßMÓ¬›~0NîÈy ažï"·(¥{÷ÀÉ3û ¥LðŽ_’0¥Ë°ŠÍlÎÜy¥EÎ¥#2àdG²ó²$ÜæxÏ `¥9”ñI€Írv”ƒöú¦n^YM ¡¦¶.°ž™}}}ظq#w÷¾Œués{{;N8áD£Qép¶›NóØ„}W<¯úíó“D“øòÝV2pkCkk+š››±k×.lß¾ñx†’!|ëÌ ïpFüÃþ;%åc… U@C¥ÞÞÞ YÀ‘ù^©+çe'@^~Ø<ñ;Ð,kâðáÈÇ8±Ýò"Q¯_TgÎãÿ^œ>qåÿ¹Í3ÁyjéÛú;‚B2=~«\ˆküœùp`¥Ë‘ƒÑy.½~/$ÀçÀ?¼ÅmÕ" lÿÎ;±eËfOØ^KK N;í4TWWg97?ŽóÿgïÍbìHÒs±/2ϾÕʪ"‹Í­¹5»Ù=3š™;ò×W#øÅ¶/ 0`ð‹mØ~4`À0È×°aÈ‹Ë×®`À¯,k¹–e]kÉÒHšî™îž™î&›dqk6·b-¬ª³Ÿ“™~Èy"#ÿˆŒ\N±ÈæTÌXþˆÈ%¾ïÿcIÕB9MÖq|•žœUú“,óJZ¥ŸÏ½8~ü8àx ÿÑ·?ÓLzÕN'n t4劃/"äKÃgžïš&™,Œ +ˆgw¿,OŸ>űÅ2Ž-…R «_ dU•Eçú!IéÈWÏÕ¬ ˜™2‘H«¼ ŸÆ`ì%HK¢iÕ$€h³< !w} þ­Öì?þÒï÷qãÆ ìí…÷r§ÞãF£«W¯âäÉ“Aš<–ôÉ$@U¼@T.'¯6p™uÄÙþr8c §NÂÚÚîÞ½ìÕ n3ÌÓ3ư¿¿Ÿýìg8þ|07`VÏßûáo¿ ÌÅ¿ÿí둞'”QïäÑ—Ëkcܸ£ž[“àQ±6ã«‘v`pnÅ L¦!»»»8·^IiõçüI@?W dJ¡@0>a´ÉB‚ d!Rœ‘7 nnE‚ôYIQ¹Ž* )øÉ2~õü777±±±Ù“^~gmÛÆ[o½…‹/F¬É¼fÈË^ÑåŸG9ò$eY^"ô¬Ú 9¥»T*áÒ¥KX]]ÅÆÆF0QPž¨ÍÃx<Æ7°¶¶†3gÎÌô¹[^^üÕ} \tðï~í†&µü'Dy2ù‹g '<8ê>fà/<_"ö¦jX–e€10Y%°··‡7O–@[ýýiIð£K£u8Ä)™m_¯?âD„¢Ye@¨ÉqA|"‹7@oõëIˆ´²z"”a×ñ_þÎ<˜eÏü]×Å­[·ðôéÓP8õn;v ßøÆ7‚)—Þ*ÏB7þy”#“ 6‹6ˆ¿º6ÌÏÏãßø¾øâ }Šƒƒ\¾|y¦›L­¬¬Àó<üé†ï ø·ß»5‰I€aФÑ`/Ý!ËÚ<0쇿ý`òœPÃs¢èœ}¡€E#çÌu]X–•ëR˜öþN¿S2ÿ™¿ è«ê£JŸ8QQYîr !0%y*Në Á~’"WUo¾DМÜü²€_þ­y6Z­ÙNø ¸víBáò;X,ján IDATqåÊœ;wNéÒÎØ(}³.'opuSeÍ¢“6X–LøÛØØ&yŠs 8yév»øéOŠ‹/biIµÑ\vY[[ƒçyøã[@£4Æ?|ëî42HËïò@øY†Ã>Ç ¾ñAä'º&¦8Icÿ\Úí=¬V8(Q8àɸxàgRR2Lad¶äÚÛ¬RDw(ŠiÓr2±Å¬iˆ@gê ˜Ü_Õ@®ž€ÀWv<ñÄàË-ü]07·0Sðþü9®_¿ŽÑh„©¬þo}ë[‘O Sió6Qç,uÇ…åUÎ,tSzMÊ©×ëx÷ÝwñðáCMÛ|âÄ ¸®ƒßù h•‡ø¥sÕ‰©wÔDŽ Xm6sÑï÷ƒÏ9‹’Ó3cÔ‘ÌlK1W¿JºÝ.–æ›<(±ÕÏÐî:ø½?ÛÄõ;]líÃy`xÅÂYL¸(¶Ï —4ƒQz}s —ÎÔð¯~oÇ QÀO1 0wc‘ìw,üòoÍù;üÍÏÖòðàîÝ»§fÛ6®\¹‚óçÏ'Z3Ÿ§È tXš·îY¶nSaŒáäÉ“hµZÁÜ`ºo€èxôèúý>.]ºÚAФ “6Àoœ‚ã8øÍ/a©ÚÇ׎oÉÚ ì Rƒû‹eµP²= ‡Ã€äðŒ¤ê<õâªFÊL§ÓÃÒ|1!øë½·¾èâ?ýooãþÖ=pfØÄËa…#F}sÀ_þìÿìûÛøÏþƒÓøöÕ†¹Õè$@ªIm4á“O>Á[o½ù–€ª¿OR¯R©„7Ï_ÄÍ›Ÿã¿þÁ×ñË¿ø#TìðòÔWi@µè¡—ãG²[Ûò™ø嵯IfÛòÝ«JE¡jIÁŸ!wë~CX;_{ þ¯ËN½ÓÄÀ­û½éLŽÈs&„±aT^ˆ!£¢¥0F†}ÿ§eüáûUÔjµÈ$»$"îšgYV°Dz, üä'?Ñ‚¹\Æw¿ûÝDàOÕ!o9 «yÖ彌m¨×ë¸zõ*ææ¦KP=σã8Á9÷(={ö,ôü©þøsɟ͸¿Z­†sçÎãáA¿þ£+‚ÓLÓ¾ÔM?Ü{$K©à}%sÖÞ¶´ ¸zI>õiB Æã1JÅ Šƒ?S†÷ú.˜ ”kæY^ËË!åš f½ÿæFÄ@õ8ª_”»Omüú5P*ƒuõ&¢{ù¸Óéàã?&wÑNd~~ßûÞ÷°²²b\^±>³òÖ-–1+õ¿,m( ¸|ù2VWWCå8ŽJ·±±ÍÍM#P§ž[ÝßÜÜNž|?zx xótê¶Dw¡ÞÏ'%;ßÏd§•¬ƒ¹\F±ƒò'P°-…u¥±ÒˆNŸ``[ú—e<òÐÞb<ôP¬X¨µ (–gûõµ']”6Öâ'×ôöÇxö ‡Soû«#žÞé¢X±±x"¿‰9/£0æß[qï÷ î!Ç(†ý&Ëþ&rÐcøGÿ´ϳÑj¶bÚ“ッ|üñÇÚe~+++øÎw¾“zÞ:yY)r»géž?¬6ä©[.gm`ŒáìÙ³¨V«xðàA ×q … ÌÛ·oÃu]¬¯¯'*×dÈbmm Ýn¿ý©‡3óm¼»ºm¢/Ó<€RÁƒ;HN׎·8qËU½€Ø#qò:Ò\„²àt."37Âl‹Á¶…çðx£ƒÇ·»(U-TêŒG.zûc,œ¨àÌÕf¶vh¤ý|EL¿é –ñØCw:&ÖÞ¡Útò¾ÊÂ,Ëf°-‹è4$DX, UPŽÆàyÀ¯þ^Û6Z­VhÆ×ÅïííEÀŸÇñ÷óܹsxï½÷R½£”ÕyXÀ–WY¯Û`.ÇG¥RÁÆÆFƇøß½{÷ ø6„i]âH€çy8{ö,ú½~탫ø/¾÷C¬Ô{“ÌH7àˆÉ!Ži/TÒ€bE^g,„ÂD¢I“¿ì}e °lú¥|z¯‹Ç·»¸ðíy4§óFϾè×yÖÒ\,âòÏ/Ä'ü Šx€¯%FÄ@޼ŒOì±<ð[߯áãšÍF0é/©u¯#ûûû$ø‹é¯\¹‚K—.Ñí3Ù-Ÿ§•«skgY÷N•3Ë6PCyôaµaii ¥R Ÿþy0 0CWïÝ»˲°¾¾äÏë>½yþ">ûôüw?|¿ü‹?BÑrâ3*‚x½_‰xÁ#È@U/퀊¼ä…?Iªª³ÓYø&àøãO¶vO*†Ç?_ ?àO0;qa:Në¹ÀÃmì>`4tQŸ+à+ ÔørEÏÃÛì<`4pQkpòr ®wâiؼ߃3öpìTp§½ýáZË%;=݃{ïÙn´ñÖwÐÞãáÍ.}g>ˆ<ÜþpÏŸPªÚX¿Ô ìoððf½ý1Š% +gªX=[àŒ<Üÿd{φpåšSo7Ñ:–߬ôÃf¶Í`[üZ†_êÄ$ ö¥òPY« æÓ{EüŸ]AµZ 6ýHjÝëŽù˜¿ü¯^½Š7ß|“Œ7°‰ïw?¥?oݳjƒî>åЇÝÆZ­®\¹‚Ï?ÿ<øhÔh4B±X ÆôïÝ»‡B¡€µµµ oÚ:ˆXR­Vñæù ¸yó~ûÓóø·ÞU|8Hñ"Fƒ¥—Û‰ KªÖ$d ˆ€ëº¡°´ë_é]³²ƒ?Ø`V8S¯í`<ôÐ\Œ½§wºØyÜÇùŸ›C¹VÀ£[ÜúÑ®þâ,›áÙ}<{ÐÇ…Ÿ›C¥YÀ“Û]lüxWqvÑÂÖ—=<½ßÃ…oΣҰñøv{ÛÔý[ÐX,bó~7D¶ôQ_ð7D;.ú0ÞzÐÇé«Mœûz »O‡¸ûñ*EÔZt÷ÇØøñsœ~§‰…ãeô;6>ÜC¡daédowàŒ=¼û½%X¶…~GZró’cþðŽmK./- €ô*MN´\€ötú~õwë(m4›ÍD®Ðëõðãÿ8´t+T=Æðµ¯} gÏž|TÖ¸œ&/`S••u÷;•^1MÞF,K4Š^¶6Ôëu\¹r7nÜÀ`0––J¥ ý;wÀ H€N’`Äââ"VW×ðÇÀ;+ÛøÚš¼S ¶58ªŸÇ€qA&—,³Ü‚&$]êg¤œ™‚¿ÏKX„žLÓ2‹Á²Xä¢G~}‹ÂžÝý1n´ü‡~šÍ/º8~Þ·øí"ÃÉË xž?ñ7sؼßÃÚ¹ê E؆õKuXE†íG~ç¼õEkgj¨Ïüø‹u„¥ŽËëô;N0Î?¹xþ´å“ê¥bÍÅ"–Ö+`Ãâñ2æ×*Á°Å³/zXX+céd–ÍPkpü\ ÛûAÛ­ h2 ¨6 ¨6_¾Í‘˜X6¢Þò S¼uŠçF&2¡dÂDÿ§ÿ»†çmÿ?|cL;‹_Œ×¥ øàƒB³ýE±, ßúÖ·BóICÌeñ<ù’뺹ìÿ![Dz~¹_™E²ê7iCÞrØm¨T*¸téRàþç$@,÷Î;ØÙÙ‰x*Lÿx=ä°Ó§O£^«â×ü6öº9Oò;x´%gYV™4.×iîªe~I^ÿÆ“1¡~Â@õõá´*ßøl,ÌÆ,-Ì-—Pm°ûx×qá¹À°ï¢Úš.#dPmÐïú€=è:¨IZk0˜Xíýî!ž1\»daîX[_úúΣ>*õÂtˆ°«Mƒ®_Þ íàùæŸüÅvð÷èVýIüÚ¹ú?ù³-Ü|›÷{3_s: a “áâ¡-çÈ2@¦NC¥þóKøëOKh¶Z¨T*J[ÖGÇqðᇢכÎQ‘à›ßü&Nžd ¶% Õ¦o­wöƾ«@©jaùT½önu&ù}®ä%w7p=Û× ·Ï{(y¼Ï /ûpFáóå7*¸÷³}¼q¹í},i¬p©¼‘»à—g–ÖËX¿(¯7÷ã+uoÿýEô;vŸôñx£‹a×ÁÉ·¢[}ia>ø[þM ¹þy<;¬¡€§»6þçÿ§†JÅßíϯ†™k?.Þu]üä'?Áþþ~'O¼ýÚ×¾,ÏÒY¼WS’Ôêuò´q3À)&ú©ó<ô‹ù¾êm(‹¸pánÞ¼€ÿp8 ¾ày®]»†wß}Õj5¢S®§\'UX­VéS§ñá=~w¿tFóå@•݈Èï´|?2k¡«`I/TxCÃI6ð|€ <ÌVN×ðx£XΤ0 ±PÄîã©»uÐqÐÝ“üêóEì<žŽÅû.:ÏG¨/ø.³ÆB;O„ø®ZÖs+þ˜ÚÓ;]t÷ÇXZ×€½Í!¼ ‡ð\`os€Æ„È4‹Ø{:ƒ]´‚?kBùøT©Û8þfÇNUÑ;xùæ°‰`º=å ©á%9+õÜÉé¢^Ïþ‡?¨aìZX\\Nlõs뇊gŒáÚµkxúôiP®<¶|õêÕ`ÌŸÇ«\é¢~¹qùMô«ÚD¿*¨+ILõ«Êøª·ðw‘¼páB°—„ëº ûÚ;Žƒ7n„– Êu1 ÛqâÄ ,ÌÏáûÙElw¥¡€Ôèõb`/†7'REü26,‹€áG¾i<>ÎJ +LÁÆ'‰Q\;q©ŽAŒOÿ¿Ì+¢\+Àu<ìm 'ãù~¦“—¸ñÃ]ŒG*uÛ_ö°x¢l-¼~©ŽÏÿn?ÞCµicûÑ­åZË~üñ u|þ·;¸óÑÊž?éGv&dŒaq½‚‡7Û˜[)ÅnDT(2Üø»]´Ž•°¿=³˜¿ºÀÊé*ö6‡¸þƒÌ/Ãbþü†bÅÆéwš¸óÑ>˜TExއÍ=œºò’YÿÀd€° €dùZ³=cšpÔŸ}\Ág÷ X\\@±èßû¬V??ÞØØÖ^Q+áí·ßÆ… èúQUV€¥ßTä÷],#ÉP i9/{D@ÃÅrf݆4ú+• Ο? 8Ž>ÜÕëõpãÆ ¼ýöÛAùºú©<r¾7Ï_ÀO>þ¿ññü'ßý8¶ž GûË€9IêV$%±H®{˜’D–Zè0Ƙ¤qÈ€s_›C÷Ü[CŒF.ªuKë­é>õùÞùû‹Ø{6ÄxèâôÕ&æV¦,´Ú,àí¿¿„½§þ2ÁSW˜_™®.¨Ôm\ù…%ì>îÃuó?7~׉€üꙪu;’Êoù˹;U…]´à¹öž °¼^Áâz%˜ØgÙ —þÞöž ÑÝÃsÅUÌM–ù­_nà`{ˆaÏ…]`¸ôí9í|ƒ£*¾eƒð£¡¯ Cò¡€iäó¶…ßüçT«´Z­ ~b]UaqñÏž=õkׂpù=;{ö¬Ñ:±SU¹dó9ªC—ãÒ–q˜mPõYyµAu²ê—ËQÅe‘Z­†3gÎ_”IÀÁÁîÝ»ñHz W U0ÆP.—qêôüôŽ‹¿ûr?ò‰T+áe=ÂàþÂÝí1ÝHt)¤®»Éçy“ΕÕA±VQBÀwô¨‰b©µ ¨Å|"¸T³CËô"ñK_µ°z®œ—ëÑoð9²Ë6OÓ‹ëõ‹ àf s+åQáR©Û¨ÔÕu}YÄßÀ‚M­ˆH}ƒðU™bäÿQ ýÉË—æ´ZÉ A¯×Ã~ô£(ˆà±²²‚÷Þ{ϨŽ<¿xÌuåeÝê:O`“Ë›ET€%×!mTy³nCVï‚(­V 'OžÄ—_~ Àwÿ‹sžqâ¶žmâ7òÞ>¶‹V™^ûZâe¶›Ýç*Œ`,ô£òøVbtÀkyù…M–š<Ê&÷?×IxÆ~t£ˆ¿»VÀüü"Êår¢%}T?v]ï¿ÿ~0Æ*wêsssøÎw¾cØþ°Èã¾y‚Jwžîs]Y<,o½³lÇa¶A ÏC–––°¼¼œ;ŽlĘ¿G@§Ó!ÇõãÎUqç/\DwTÀo}j>äõZ .dÖ¯²Y¼ŒÉ°fÜ_ÛYOÜá|àküå$Ø Ð"uÃÅN…ÿ}.ô úCàü¿üYÿ ©ŸÊÃÃOúSìîî[žŒ1T«U|÷»ßMõa^6p¦tV;f¥÷em¬¯¯cnnÀtR XÞ­[·B¤ -ðóãZ­†õ“oà¯îÇõ­[¤ HƒEš÷'u«r÷ıf“‡Igf௾¶Å`^{^EaŒÞãAH‘Fk"/Àïþu»m†ååålî¤ý0ÆððáÃ`ÒÕ)~ë[ßB¥¢_)'ò;™'8P$crº_µ6Ì¢¬S§N®×uÑï÷ƒçv0àîÝ»J —ÏuóÅxÜo¼j¥„ÿõ§oÁõLˆ¼RcÚŒ©…1}³¨N’8ôaJLbžvýGüsÀ/Ñ€Çk1“é×y {¨Lùy¶öþào*h6[¨ÕjZk> !è÷ûøè£&ÕeÁ·Ãy§qõêÕÛ5̬*'oë_Ô-—1 9Ì6¼Œ$lÛÆ™3g`Û6cp'Ø©’1†ÝÝ]lmm%²þ©c.–eáô™søb¯Ž¿¼BY/åÐñ«'â<=qÞžRt¨BÚ™_=&€¸²°˜q1,ð¼²÷ý«+Ü “÷xV^àŸüqc—áØ±c‰Ýû*B|ðAdÜŸÇŒ’vÒÏÃÃ7B›ýˆ_³ÙÄ7¾ñd}-¹Ék2/óóówÊóüÅíŠïܹ.¦î*ÍÙ³çÐð7Î 34d†’cµ˜ô›H²LTH+&’­sL‘†oôzàÕÆ&÷7vOË<¼ÓÏþñ–Q,°¼¼œØÚW‘„½½=|òÉ'¡öñ_þÛŽ.}-‡#3·=T9Œ6¬®®ûðù\Úí6?~œ[Yõz++«øãSxÒ®âÈ¢þ”™~ @fyºt¢ì"¡¥MçGXx^Þ4²ýeçc”ëVÏN÷!è·Çx´Ñ…;öpâb=ØááÍúmÆ®e3œ}¯Ñé¹ß;D¡háÌ»Í`".÷>9@½UöF \|ñÙ޽QEëX ou°´^Ai²#âÞ³!Ü‘‹…Üùx?ò\ð|¢ð¯ÚïCìŽ_B˜H%"„?¼VÄÇ6Nž\ Æ<ƒh´“„¹®‹?ü02ÞÏ—ý]¹r%˜i= áåä­ó0åUhÃ,ä°Û`YÞxã ܾ}€¿4p0¤àñãÇX\\ & &ùŸ>}Û[Ïð{7Îã?ü¹O¹ŽŽ•GJeWÏu]¦{áÒ¼ŒÑ‡5ëªÏ‰¯¢ ð¸0/ôñ¡ÑÀÅíö°öf ë—ê¸÷ÓéÇevõqòr'/7°~‘ØØnýx•º…7¿>‡ãoÖ"_ãsFöŸ CßGøòó6:ÏGÁPÌæý> ÂNˆ{Oü®_ªuh-•ÐoÉ ùðNäk€F^"‘Ád@Ïcøß¿_F¥\ÂÜÜ\bW¿jÏ÷7n`ss321Êó<,//'Úæ×DD‚1K€õç]Ϋ؆YµcÖºåßr¹ŒÕÕÕà|4Áqüo®xž‡{÷î%Â]Úb±ˆÕµãø›kxt íhJÖ{¡Xhâ*$ãjÈäïvç6@[²É…›¦™~ àˆÐ®CÆ–ߨÂuÐg÷{X:YE­Y@µYÀhòIäaÏÿÂá çÀ²ü eÙ{6œì$hcУÚ*DnIg„æbîØ×ÛÝ›NªÍ0ê;(YÈkÐÝ›‚|¹f£\³1è8Øü¢‡‹o…RôþM÷0xîSÝþèÜ€¿þ¤€ûO-¬¬N­ÿ¬îÿN§ƒO>ù„ìJ¥¾ùÍoΤóÖÍÂÎC·XÆË lòß,Ë™¥îY·ƒÒ½´´„FcjD Sƒ Óé`kkK«3GÄø'NÀ¶,üÎõ˜ ²Gf0 •d±‰c 6Ù€" ágSSŒápc€mYGÆíò"¤³7B­5%»OXZŸ|ÂÓ:¾‡…öŸ qýovq°=ŒèÚyÔG{wŒÝ§C<¸ÖÆãn$MwÏ'¥šO.Þè`íͺ؆ÎÁþƒk?Ø þza{äý­|ÞÆÅoÍ)?ŒÄàSøä.Å ¦&jÄõ€úý ª•rhÓŸ,àï¿ÿ~°QJ¸z W¯^E½^Îó œg y[Ð*`;,pË[ï«Ú†õõõà9çC\=z¬àB>/‡q/ÀVqïåû°YIÙù%#3‡O­ç€{¥„ m[1›Å¼Úâ¹ÀxàN­y\K>Øv÷Ǩ5üñÿÆ|§Þñ]ïkçëØ}%ýŽƒõKu¬_¬ãÔ;MìoF÷âîîQkùÞ…‡7:¨/á Ý`žA÷¹ƒãê¸ò ¸ò 8û^µÆüÛ;#|yýÀÿŠzÒ³ˆI€A$™C©Ë$É_|\ÀÃ-†ã'Ö•Ÿüïß¿'OžD:fÆüå…çÎ;´Ž›‡ÏB÷¬M·²"ÍŸ¸©“¼ÁSÞž’übxúãÊ) XYY ÎÇãq°*Àq<|ø0З„Pa'Nœ@Á¶ñ»×ÏgnÛWA^ÞQqCë¶~y[›Iºû7½ ›º¶7ïwqìTtG¹~ÇA­_ÏõPmúú]•ft.iwoŒú\µyíÝ!ÖÎÕ|ÿÄ ÑÝ¡>7Í×Ý£:9?Øá‹ÏpáÛzð&ÞíðN~ÞÇðüeõzsssJ€Oþ®ëâã?cážð4|É_ž ª„Y€‚*.‹~(çõ'Ï×EwÏ“¶C¾‡qfI2¨sX\\D­æO@7€´ÛíN‹Ÿ + X];ŽŸ¬à‹D^€C6 ®{ÎsëÈ3C¢èÂÏk>ÀôÁa¡ÃÜ‘sßMœÿä—AÞhãñFã‹/oL^24—‹øòó6¾¼Þ†;ö°p¼Œ~ÛÁƒkml=èãþ§v,ô‰ÁÍž£w໨OTðàZ›÷zxt³ƒk¡2±x종ùce¼ó/.Á²™ï˜Œñ÷&.Ýý1êsEôÛcÜzÿ9ªÍžÞíùuü¼Þ~Ô=øËòIžzéHŒD²Ð:~xÍÆ“†••5#€7!ׯ_G»Ý&ß‘·ß~Íf@tEMÚg9NGž$#.Τ @ó8Ýu¦È‡ÉÕ†¸{©Jgr T÷ÁôéĤYu«ôÈñÇžcþÁ þÜ?~ü8ÐëÂŽ?Ž‚má6Îdjß,%í•WLüK]Tše€:‡EµôÏ”øùãŠ&Ω<“°¯ì>ž‡æRÍ%ÿTlÿÙ÷ZØÛÀ*0´–ü¥u¥ª…¹c%Œ†.–×+¨/Lç œ8_G¥î?.koÖÐÞa4ppé; °‹rG œûztéàÊ™ª™¯µ`§Z:YA¹bÁuóßœä-“yY6ß ˜Lƒ•}ÓDºä¿ÿƒª•I  :DU˜çy øä“O¤ù/dokì IDAT> Ôëuœ?>X8 ‰#缞ID~÷“q`Âù9¥›ºÖI„[üªrta¢P}¡œGµ½0Ï+þ&‘$÷!‹þ$m(•JXZZÂÎÎ`8¢X,‚1†N§ƒÝÝ]ÌÏχòËzå0*β,,-¯ào¿tño¼µ¥jŸx¯:†—ER7ƔȠyÚM×üç"ÆÁ䯜€1´–£Kç&Q˜_ ¯ÁµlYgÏ¥±X$ÎiÝ–ÍÈ%{õùéãÖ”ôÕ&Ã6 œðG £v4È÷¾HIn}iãÆ§O¹„M@_÷ÑGa4EŠö<ï¼ó„€ÜQëÀE•FN«$Ó2tú)ð¢ÎEâ0UUžx,ËŠèHÛ]±¿“óËû8ðc!HrÒÜç¸2Ò>K¦úyØòò2ë8»h>zô­V‹lkR°¶¶†§OŸàOî¼óí[‘zG‡Wˆ˜ËLlâã!aÂÿÔ¾ÁlÅç¼ÊÂ5 Pã)¢¢ äwÿª„bÑÆÒÒ’Ò ¬ £\ÓØØØˆ”ãyŽ;†µµ5tt¢sgS’T?UFœ¨Êuˆ×ˆÚ/JKS䂺¦óT×Ew­)ýT½y˜ø«*Ï´lJ(ëÿE=K¶m;gzžZà8¶··#:Ä_øËq¥R ‹øÓ»§&ß8ZÆßŒmQcíYwd€ùlÍPƘ+פin³^À|ÙÆAÔÚz-/·ŒúæJZ“U ³`òOv,¼­€µã«(§ž SK_Ž€?þ8d)ŠîË«W¯ƒ±® @o-™ˆ ^”P³NO'ƒ8€wE.ƒ:Öõ#”'€j‡ "i q耪3?—‡uLÀ3î>äyŸÅ_¹Œ´úE ØÛÛ –ŽF#”Ëexž‡gÏža~~¶mk­~*LŽ[[[õüåýuüËçï'®ïQ‘¬µNL¾¨tûgñe“ÿÌèQ|žâØb k•¬¾‹ÝÇÑ%k¯å唃í!úÏÇx£ZÀ±EzèÂX4Ó?ÿ 0†ÕÕÕD–¾ÊºÜÝÝÅ;w"@ÁÃÙ³gƒ‰qBY»”U›¥Ó–E.CU9Oœ,^/Û¶aÛ6iAÇé0ýÓ-é3ɯj£hùSm êwnr’x(1y–ò¾,Æãq°Càx<Æîîn¬Õ/“5*®V«¡ÙlàOîžÆ 14•ð¡iYR¾£©1:í$À™:0´ÊŒÿsi5 ¸tº†¶ëáïô°ý°Ÿlœùµ9]Fþ…fo®”±¶¬! ½~rÿÿØaøóŠXXX@¥â¯ˆ0µôuÖ¿çù«fÄ4¶m'ÞîWe sàÏ þò¸µi§Eªê\5žnYVÄKbD:€æÀFµÁD75GA<§<ª6ÈeêÎÓÞ‡¤BµOlC–rÄ6Ôj5Ôj5ôz½`(€“­­-ÌÏÏ÷)í<Ïó°¼| wï¶q}{o-O‡^´dáSŠëÏ{¹Dd ŽÄ*¡:²£!áz¼s¡‚mañ¾½±ƒK\DÓº³ÈA4Òô20L** !„~=xæ@çE¢ç&º<“„^ŒZ/¶*rd©fcζpöxW.ä±ëÍÞ¿nc¿¼uj5Ø«:ëܽ{—½3gÎ$#©p°#‹;[Ö-Ëeˆit w.‡ó²(p6ígLÊàçT9òõSéS ið¥Ñb~ùZ™œËqq÷!¨eqg)ðÛ¶¼¼Œ˜^'Æü‚vvv°´´Ê#›’‚¹¹9 ¾oýHSÉÓ{GIR€üôGÞ†<*œ™Cù-Ë•ó œ?]Ãóý1º}'ZPLÁ,²/¼ ö,—&þLÑR(`•Iq.üD„Ï=9 Q…H|äÞËõ¤ˆJ²zWÊZ”KÖ¤¸Ù¼ ÿïJ¨”KÁ¶¿@2«_ŒO?ý”|7lÛÆÅ‹SÕQJë4«È ‚‚)ðǹ\W•‡C¦](‰Ó¯ò¨â’Xýò9ELÚDt€š§xž‡jµŠz½ŽN§xøŠ€Ð;—Ä ?3 ‹Ëøà‘ƒgø9š¥èΦ/«xžÇ½ñ©oÐL?œZfèE(-¬,•è2"aLÂkC`Bõrª^“_ê6˦)à+ @8M„˜þ!€ì€ÿÞ<ÝaøÙ†…7N­E&x‰Ç¦îÿN§ƒ[·n‘î¹sçP*e›Ç@€<Á_´^¹¨À= P¹»Uù’ÆËG.LòRm¡®{R2 ó¨Ú§ƳËËËèvýo‰È^€ýý}´Z­ ˜_÷+‡-..bss¿ü·?‡¥†¸ã`¸=¶åÁ¶\XŠ å‚?‡¡Àbþï?<¿„GòÌâü*EÉ›—t€¸°×¢ŠÄe‘òÈ/a&þ£ ³îeJùfò§ú›”¬­­¥vû‹aŸ}ö¹®ß²,œ?Ÿmïr@ç-qÀ”Åú—õH’6Ž0˜Ö9Ž ¤õä%ò³4K’Q.—Q­Vƒ¹ãñ8Øhgg'4!6)ð‹eÔj5Üë ðG+Ñ¥·:)8†),ì°]ßÞ9‰åü4˜po@"Iâ`Ô1ïÐ’ºî´½¦¯å‹çñq ‹ (—ˉ€œ†»7¯_¿.蟾§NÊlýSzóî¸u •  ®]Ü8~œèòëôë¼T:Ýp†Üãâã¼rþ¼ä0ž%Ïó0??^¯ ì h·ÛÁ—0“¿ø»¸¸ˆî—]ôÊ=ì×ösm‹,¥q Ø9©Msˆ§5£rÐ=˜<>‘®<*u$ÅCpë…C}O 3¿—33& ¿@ëÿÆ [{ —/¯€±èä.ñØÄ°±±úŠÿæ›1ß/7”¸÷0«È×A/Sà×¥KB,âÂubÒ'å1þ/Æ'ïWåU¥ËÓ 4ëgI”z½ŽR©l Ä—2æ/™åJCøq«Õp|ûøÌ ÀaˆfH&ÑM“×üºØ+Ë,]¥iÇ©©ñu­yüß(™I%f–ü舾âóI¶m»–%ùÂ` ׯ_'ouuF~ß.Ÿ…ÛVn ׯ#FÔuõ˜žS:äpUžYüéÊ¢âTaºg&.NuŸT÷+Æÿåß`Œ…VNt»] ‡ÃÐ<„4–e¡QoàÄö‰—¸Ï"%Ó6ñ¨ М‰Af}xIëÚÔäŽQ«U£‰¤¢<Àc^x"`|!“Œ&á žô|ØCJÉPÆ ªçy>X\\D¡à¿"i-ØÚÚÂææ&îܹ\ëž·ÕFµM\Ÿmb­›Zÿ&çqá¦ñIÅdüŸŠ3qùËçI½TYyxËÀC£ÑÀîî.Ç Y·žçáàà ™† Õj¡ý¨¹îöê{³o˜FËõúR>b#噇4®ˆD_¨“З qÂYä“óDEE{ H¿Ÿi µ*HEÒö *…Q¾$E¿Š>-óú} Ûû W®¬¤~1ìÚµk‘ÎØó<Ôëu;vlmA®¿ÎB 럖èÊÐ]Ÿ<ÆÿMÈ€ê\çæ¦=â×JÜ‹EY‡©È÷k^™T6 ìíí…ÚÃÃþþ>æçç@^×l6ƒa€MÒHÌ=HÅ&Òl‡7Z(ºkRjÈ­Ì”8Ÿ ²úIaÞÀUêÄ¿H´§^VgZWêܸé9u Z5ËH˜ý‚ûP»•y•†Ëh4 >ú#ç;sæL¶vI¢#&iõ¨<&îkݱ*N £¶¡U]gÕ=’Ó™^S}ª8y;h9­î<î˜{`øV½ª{¥º¦’5œn¹ ÕØ£Ñ(Ø+ Ëc zÇ·¿jò߬\>”·˜=k‚%­°À“ûâsD=ô/@FÕUÊ0àÂb롳æ©8Mú<Ýÿ—ã›F¨Üu?¼VÄââR"÷¿*ìÎ;ÁGOdËæÔ©S¹7CìPÓi]ûdàw]—üü®é1u.¦j1¸Uâº.F£Qð)Z×uƒ?^6/¿P( X,Ï“‰b¨O§ìGÝ ñyâǪ¡Ó為_³òðº@©TBµZE¿ß6ìïï£R©$šøGý6›M´·Ñê¶°_q“sæS©%óVÀ€þ¥H#¾ H²ê—A׋މÄæiÌH@pÈ4/œ `Ç‚¿ÞSA;<",©¤ñªhµAÝîäò`ÓÂÖpù²¿ iÀ§ž¾ñìÝ:~üxð´<„²B“ŒÇ•:‹!qŽ®îºþÆóü¥—ƒÁý~Ãá0 ,²¯Þ¶B¡€r¹ŒJ¥‚r¹ù–€ÜÿñkÄ'ŸÉÞM¨›€??–ï øSC ºk›öYŠÊúçe4 ôûýÐ;ÃC¯×S~ ¨V«€c{Ç^(ÈY(ÀäáÚ´С›¥‡#ÍÃÉ“÷£¦z’ŒásEp:Þ   ã‡àŠ­g¢K§@ ø›ê4MCÝïXlVx 2õ31™5ÑÞ´ 4ûŸKàyƒ>|êK¦"ëçõj4ØÙÙ‰”éyºÝnàIÇÿEïL©\±çÇpûÄíÜÚ“§è®sÞž˜\·Ö=Ô¹ŠÊ‚&Ø‚ …Jxå$!PÔ9B€h%)‰³Æc¼¡ÃYYÿ†«6YOH\BuÆoÐlÔ7ÿ¡Âøï;w0#ï@¡PÀêêj"‹L'rG-‡êŽÃÄÃ!Ž3ËÇâF`Y­‘dP„ŠÊøDäààNŽãÀ², ضJ¥‚Z­†r¹ŒR©Dné,‹ Ü‹ÐëõOÂh4 @ªV«¡Õj¡X,zÅëÄZ*É2@³¨[Lþ: ——ÇT¨çSþ¿ –Ûn·ƒe¥qÿóãz­Žùçó(:EŒìQê¶dý3¨ÍõO¬0ë瀙뺌Wj&`EO‚;•ÜÄ+@¤‰à¶4s_L¤³úYáTt¸ˆTàŸ¿õ/\¨ú&ÊŸ¨?>»kaýär" —Ãø º±±ê<¹œ8qœMDør]¨Ž[Õ 0Ÿ´$€ú¦V¯<Àƒã8888ÀÁÁAPÇr¹ŒF£F£X誶Qç2XrâÐl6BÐn·qpp€ápxªÕ*æççÉ!ñ:Q³õUeSà/^/1Ÿ¨_Ìþò3B=Gb\ {VÅøZ­†N§‰ïõz¤'…úÕÅU*0aio OŸ$ndž þÒÜv¤Ž©s žùL£)@­”X‚ê,^€ÈP€&…‘ðS…ÕŸêvê,tU\ !N À>¦>t™xºÄŠä6¤¸x“,?»mcì 4ûŸ‹)øsáîÿHQž‡õõuŒÓÙšËRÅÅ¿ÎR£€CU§$Ö?UWÄxüþþ~°d̶m”J%ÌÏÏ£Ñh(½*@ÕI¾vü¼Tò¿ 9??~¿½½=´Ûm ‡CËgµZ­ÃbƒAè¤%år•Ý *à ú¥è7:¾*’äsÀüoöbZJ"/€ øTºl$Þ„dP`¯µø 2Jâ,ñ à¯ÔO€rĹ °þuu‹±þ)Cª 8©Ü{báôéù *èU,Ïóðå—_F>ý +++±à×±ª:ÈõŽ#”·Žò¨ò‹úUîpUýäsÇq°½½Á`ÆZ­V°M3üqD€*O£€’òÖxÞtܽ\.ãøñã888ÀÎ΃žJ"JRKgkk+ä¾;e>¿€¬­°¦nÓ8¡,NÆ¢»ÔÉõ-rKš~1Œ"²ñ|8bssŽãÀ¶m,//£Ùl’@Oý'%2€ª<â}'àY–…V«…J¥‚§OŸb0àéÓ§X\\ vº“=&"àñªÉ‚q–»\_ñ>ˆí£Vo¨ôP÷¬ò4iÀŸë,•J‡AøRÍÁ`€b±˜É P(À,†…ƒü À´677áyþlü••”J%-ð›xt÷V†q×\\Y*•pâÄ looãàà»»»p]sss‘öÊ@-ÎtOþq÷KÞ4H¼IIÕ—–°r©ÕjFnq€"X¬G/ÀÂþBêú!Q™Ì±’ë>@>€´&„Í“-àËa¸ÏŒLjmLxÁB‹Ó¿' Ñ`øƒ ŒÞ)]PVlÒ„Ö¿ñu2 Bصû6šÍº@+Kœ\qö¿Øá¬¬¬é ¬8±CMãú×Yú<œÚØ' Ë ,i]¾n·‹­­-xž?Ùnuu5 iþx&$ ª®àúòò2lÛÆîî.öööà8–––" %oí+{H’‚¿xb›eÐOÓFêzÉ÷3‹õÏ¥R©`oo/ ’œ ƒÐu’Ë0%¥b ÍN§€±=NUÇ´">~Y®}VÉLÄo7ËL™Ú¡ÊDüÕ3pôǧˆ¤Ñcû‰’ˆ7ÀPUÖ=”+ÃPlÕƒ\À?‚ÑøËaøë A~Ö¿ã7ØX\^Â4«TÖà?ó›››¡ô@¼û_.ƒçÉÒ‰ê€ZnUŽ Ð›Ä©Ú¦JÓï÷ð¯ÕjXYYQZùqÖ¿ŠÈu¥®EZä?™4Qº`Û6¶¶¶pppÆCåÉ×>+øË÷TwUºL½r¿/–‘Ü … …Bà÷ƒFY+WEŠÅ"˜Ç0×™Ãvk;u=ÓJCYs]™ð+‡ò1 $A$­Ö @¥‹ñ LN=&-7LB8{ü¸)ÞJ µW19àOS? yUxZð×VLfr= Òmy¶ÇÐí§ ERµWKîxwwwƒqK.Œù[ÐÎÍÍÔ=¬[P.—ɯi‡CT«ÕD¿|lY˜ÅÐìÒŸ!þ*Hn@d«Y%¬BÞtWËfK¢•CœÕ?ýQ‘?öUÉòÅ~•Í þ^š°i”Ö£A•§8õÔéî Œù@îTG-Ÿ?yò$H'޳.,¤›l”ÔªJþ §!º°¸º‹×okk Žã X,beeE Ú¦d ) -nñ˜ÿéæIPzemµZÇØÛÛÃöö6J¥’ïŠ6u±>IÀŸú•'f%T]ó¾Üð‡ú}Íþh4 ½_b¨_•ìZÝVnõ}AÂ%è9h˜¢¡Ë‹NN¢ÎůmÅý‘b ªL* ô|ø¨2¾°" CJ§iò{G”EùuÕJCÅ«ÐxFàOåõG¢ ÀÞàBßj¡Z­cŠò9åêÁAÿçñŒ±T iGš¼uÖ¸i>@ëôSéö÷÷ƒïÀ›¸ýEº?þµ?Q&†ËÇrSb¡ŠŸŸŸG¥RÁx<ƳgÏ"}¥N_’û ÿRÞ…¼ž™¤Àk*Åb‘\Nê8ÆãqWø±'…BnÃßÈíEójª¨€( )Ʋc,Ñþ>¸hHi…«ÀÑ‹‚`H™–𽩧MÉ•'¨WŒ’P:]äu¯_¤ˆ_Zäß|®9)è‹R(`yjýZ|â º6MÂ3Ý+IÝ”)KyA$j°7"qd ($üçÅü%e !‚ J£‰'Úáu¥ “«”ü=e:E™Z‰¦Ç;,ÿOüüogg‡´€jµZ¦å…&BuÒ³„D÷0x¨Ž.k.¯wZ ª›Ø¾üŒ}ÇãЉi@4)ê@]^Õ!^“_®K&_#Ïó7 ÚßßÇ`0@¯×CµZÕnÕ+ÖÕ´^"èËÛèÊõѵJ+†QuË*üåõ¶m;ôÉŸ[N*¶e£Þ¯£2¬„Â]æÂµ¢Ë>ã¤ààz ®feA˜Å¼€Ü÷ Ž“<žçWgÌ @$9–&‡ðY….Åi‰@Í ˆ’•¤z8Òÿ4Ebo@è0gð7rý‡Ûôt×B¥RNþÑ"üÏ”öz½H^¡—˜ˆ °Äò¨´:=ª´¢~ym¸‰•…*ê•-NN–F£lÛF­VÓZ–TãÂT`/Ö‰ÿ¸ëg þ²]¼mÛh4èt:ØEMc IDATßß®GŽ‘„ÉÇi‰'ª<«P(`4×KÈLlÕnÿàÆwð.Cxß(/pŒ \}§€&†Ê4Ôs~Ør$=4ð¨€ð<–œ€ŸÞ Ò’P¾üÙ¢ ¨çü‘8³pcð§ 7ÿhÖ§»Êåéø¿(ªNL÷<NŽã„:XÀßÈ$M¥³Pu +þêâd·7¿ß‘üù±<™Žƒ‘ëºh·ÛDZË#ã:É<ˆ%I¬{±¬´D Õj^¾Ï½x?Äû.@SË_¼Ïâ½HC6ÄpÕ5¯eaŒ…>dÛv0Ú˜)©ð÷ó7þ½c±ÙW¦s]†þPý.;ž…ÑØB»WÀ¯ýÞ;¨åC Ú]/G’0Þ& ›i`÷ýÈW÷„´A8aõSá&q¼Ü 8† *“õ©ÀšJ—VŸ$ÿ ×†ú¶ö,T¥T–ÿ´(A>QŸ\h**`¢@@g)ëâTcÁT¾¤àOYÙržápXs¢w$ Ø›¤ÕµE¬ŸÎåÏÓ›Xür™&Ï‘eY¨V«Fh·ÛX\\„ã8¦têH€ØN1­eYÁGv’z¨ë+#ÉuÌâ à刟•怗—×}¿SÀú’´ë•Q¬¾ýn ËÓt¼/Þö÷åHNœ>[*OÅ C«Ô#Oôq‘àé„A-†z^øâ_Zá×DþS«õÄkÓ ×[u/ Ç×Q>ð—òeÈÅxz,ÿÏò×n·Cºy甄P %–!Jš‰\ª0± =&à/¦‘•»ÿ«Õj,›H/ލ$)#©×ë‡èv»ëÿšÜ1Œÿñû+ZÌr»)=qDJÌ/?«‘M"b9â Ñ£–+è–bR¾z’Ù vüfÈîѤú¤¨>³ 7˜GxââC¡4“k xˆÔD¾ÈI®âÉG±EXüd|«ŸP@B ½ üÝ>ƒãx¡1E‘ÒóÊårä9ÔîxQd&÷ÏT›ã,~yhAìû©}ù©:§ÏóBÑÛÇ×{˜àø¤‘É!€¨L:ðÈUS‘ª!é0…XYeñ ×u#L¥GΫç80—?µ«Óa"².¹î*=œq*N,ͺýÿWUŽ o>mxN]eõ«†€œ‰€Hb ! ’(%?†«Õ™ ôÕºôÀ¯‹Kï H þ0‡Á-Nt$Aœù/vj2ЉÇI_¦²ÊTú“’^IvÇÚÛt^Sð³þuå…ú ” Ÿ”Pa|×»ñxlÜ>ª¦¿ ÌÅ!JtzU¢JoB†e¯„Ч†ÑøðVÅ3¨‰üaJn˹P.5*\'>ŽRi'¼Ê@…AðãèùB>‘„Ò†„ RØŒDs­µ·Á„ ð3xÀØU»ä“ˆçyä—ɸ¥¢³ø’’hSfš7®,ü(p¡òŠ¿£Ñ(B’¼)Ø‹×U´šEýq“?ªL ˜“€B¡ìȯ]\»åë&ÞKÀǹþ¦zUñr»åë'³ÿÉðÔøµàHHpo=+Ñ…+½ âÈÒz&±d@R Äþô`“Xb‹R¬Aº¾&þ 8 )ƒ¼§'*G†„M¿¸;;MÇây^h9×£²0åcÄYÿT]túM_NÇÏe0¥ò«~ù5æ )ÀËž ùWgõ‹n™0©ŒS– ¨Âø¤TÕl~1¯øgbÕ'%ºë”$L%ÔõÒ õl™–§wäÉ}ñf*G’„Ä#Mô©¨òè† u‡"i¼p Â  %T4%¦”ê¹OâP·§K“ø„V¤2ú0©XÇñÀ˜90«€WeÉ`£S‰#ªÎ˜Jg¦+—"S`7û$$@&:*DåÉbñËéUGLôd!Ã2‘ËKuàÅ{ÿä¼ P–4ìÌó&Ö½x8`$ñÄXý±D vx€ #ƒÔéÉDrr%dhÏøuqøS ¯ §½cGý©]ºt:ñ»äb‡(®WÎ"&ÀÏ˦,ES=&a&úU¿Üª¿ïžÆÀ󊿺cQDbN: OŒKK¨0^7Ê ^Oêúë™J§ “ëh’7iŸOÝG•ˆ Ÿ7ø¶à•"³ˆÃXJ"²ÆàëQ. ~(2àMÿOÈU4é  žem¾‹¶(M¤ 臂S€;“Õ¯ §Áƃeew!Êk°¹È”ÊêT‰)ðÇåÏÓú÷ˆu—u™zâ<&é©öQψ °Y,þ¤aºzÈa&D@E$U"{İmXÖ8]Ó_žë5±úEQé¤tP`m²40©@þ•-ý$×ÂÔÏ Ôó"¦’„'ËÊþ]Qâš•wýÓÈ¡£‡_—ìÌÌÀãTs€X 7! ô¤d‰,}Ù“¡IšH(Jø¡è8bðO5%±ú…òà(ØúÉGY;&yeåO£7m¼.i:j ºÊê—Ë®ŠÈú:ð–…ë3J(çñ§Ò/ÿZ–"LÔ/Õ¾eÝ›9Oš¼³NÏóP*žåþâ¡ß—Ì@ÿ”… Ouó<Lç …N#9Ç=Y%‰,PED`“ d¾hþC:4¨ƒ èÑ)Ÿr½Õ¯ 7÷X–×5›}¬ÞaSK©Yó&’øeWwZ&aòÞñÔ¯(|ö?õaݯ ôåöQ×Z~ñ\®·¬3 H²aI:>|¡ûèLÄø4^ŠPû$!I€=) ë”U<ÏC±¯àe£;@¢ÁÔ0M'ÆQ‚ŽHñ!¥!å“C§õÄ„‚ŠH¶<© ñþxq bõäá˜ð“E& ÷P´ý€ñxœ ¤MDG,PÖ«I/§ÉËK@}kÀÄõ/æ·m;òá¤8«_úÔµ£ÆþUå¨tèÀ_>O2$`â Ú *fêYÊËâ˰,+2L–èUeÊ÷ÆTfå:?L0£&$–£Ik’r“O‰@dX P ‹&ªøà'Æ+ ¦ *M¥—òDÎÅ'3H kV˜±éãÒ%‹~Uœ|_Ì‹…išÅ wÊbçæ8ù=žO'I½rÙ:K7©~.¢kšùXü-‹°m;Øâˆ¡$V¿©wE¼/¦Ä+‰ /ËŸËx<†mÛ¡]$eâÂYô$ñ˜ê~šêLâ…Å$xÍò$¥BÔƒ÷ªKî;Ó›Ÿ¦S åñ€é¾ù †|%¼"ªBôñ!Q‘^ ñPJhôl¦žT’ì#ÉLÈA2‹š#øIÕ1ᑸéIÁšvžI:ù¹æ@ÆÇA©T ÎU©œO'qîÑu›'ðó_ñXµ¥¯T*E ôåk¥#"ð‹ç:ð ÊJ æIÈ€¬˜ñ¹‘ë,¶™"|bœ.Leý‹igeýË÷&NâöžÈ$ÞáÀõP»éQD@òH±æD@—F&‘B¤´ä)‘a–O@ˆ&Öc/öÖ¿:> ÀëÂ=¥¢Æ?*’V<Ï mo+Z›£ÑÕj•tI›XIqà*–'~²W®Ÿ©.]ù”~j«cªcfÌÿ0’mÛ°, £Ñ(°p“€>u½tKþx•;]%:€|.þ™ P:¹ŒÇc”Ëe‹ÅP=ùµ–÷—Ýô:R™„Èž$Q?%¦×VÔ)—!_{•Ȥ$àÁEéõ€#" P‚iô›¾ ˆ@$&šÆÄâ7&R>eP^l £7! à ¯N“øuqæàxhÕü#nq%±#çÖš7Hð’uz²P.vê—ÇYiº0ù3ÀTÝUm“ÃK¥Rð±ùCI*“Lª”_e)«®OÓ ‚bûF£QÈ _Oê^‹žyÇÅ$BÍóËJcý«tÉíS1ŽËmÏ$ž‹VmŸî“™€¤¡aò+Yýºa-tšZüÚgKAˆªé%ëuJ!IÁ>Hžžxò‘ÎS¡þH^ƒðP•üƒFÕï(ljµ2uâyþ\xÇ%<Þ­ ”¨cU^¹>:oN¯O\OyRšJ'c Õj¥R ƒÁµZ-RoñWçGÄë/žËà¢r?SÏB„ ‹E”J%‹ÅèÉ÷ABH&±þåv«ôRbbýS×Ù$¿œ&ËÄ]Y\‡Jòà,yÈL¶–_°¤Âü>:0ö%«?ä "h"ÓøÄ^ˆbÛ£¢4sr…œHG6@¼µ¯º®º8Êꇷj>F(—˺Š)…wÂ…BpþE7ª³O#²e.×E´þÒ¸üuiE³˜Ne…Qås°··IoJx]TnÊ;‘Èd†$µþ)¨V«‘8^oj¥ÊrNb±ËÃ9ò×òómª—J÷>¨¼@:OWZñ<Žë¡UeÖ•Drñ\d”#9µÚAƒ½˜y¡´òç!ˆT„< éK%9z’> õd~AKàU/zšU?Lö„óÇ_ ÏóBîl.Œ1 cà—-nÐR '‚¢l«È@\q®Yʽ.Ç‹R©TP*•ày^d€—cBâDl¿Ê ×OgË¿'~U›úý>FàQ¡Š”ÈmÒ]Y¿Žèð´¦cï:‚ä}óÈ(/÷?¯O³úzà‰4Ó MœW@PGx†'Èò@æäÆÅ¥ñ ‚¾V}Úx™Š·úÅÓf̓?@µpœðŽRžCÀ;½~¿ŸªÃ‹#bù&˜˜7ìÅ4:w.ED]*ýµZ år½^$N²Þ´ãþâ¹|tíS.ÿ5õÄyd‡p]•Jõz=¸(Sõ‘‡ D"¤»fºgH÷ˆÇ:ò–„ÈÉϹúãñ8W×ýz@ ‘H~玊Ó79ã9‰ MÜè#ñDOˆW’ ÒàaS¶ÏÔ[0I ô¢MvsÐ7I£‹Ïnõ‹jO;»Ù×íd—-úý~l^Yx‡­š%—!æ3qF¿ T}te¨ÂxäκÕjáùóçØÛÛC«ÕŠä¥êÀ½AN~ø›zvT¿I‰@ø@·ÛEµZE½^‡mÛd}ãê.Ç‹×Kå­0U( –#†™^w^?ù¹÷ŸòJà8Ä!€#àýpD=ŒMÌt¯Háþ7ñ L¢•d@N$Hrõ •ó r—øº&}“tÙßÄê—Ã.¶;Ù<‚I€®ëÂuÝÀ#„ÈÖ+ž•-–'ƒ¿,¦Ä€ƒªìb5!ª2ÄÎ_$êõ:Êå2ó‡H¨ù* Pý‹ùtC*ðR•›Ä@…™‚?à€¥¥%ÌÍÍE¬YâHØnù2­©ÁFÉ8Rm"bDßr;¯!®o¹5Ȭëe“#IBÂo" ˆ×y‚t"§‹%A¢¼¨Ý,^*Ç (:Ë ô•itñḤÀσÖ<Û'ôLE…k‹ic[7‰ ›*Kþ´ŸÄò²>ÈÒz(KPŽo6›èt:888PNÀL b)•F§K>6!q*ét:°m•J•J…´š“Xÿb)²(êIãá:äë+ÆÉ×-PePÞ,ÂÉůýþ»°˜ÛR×·`;¾õÎHé ¶Æ<Œ »Ý cõ"íUx¥<*ÖœV—Àçª1Pć‚dAN§ð(rѤ Hü"ï¸)`Gr¤Èo’6'ààÉ÷ŠLªŠó°2ïbìd÷0æouKís? ÈYÝqzåÎ;p–õ‹ú¨2)1)›Ñ’›ŸŸÇþþ>Úí6†ÃadQO\;Tî (’œ|L¿xœüààà­V óóó™­ªÎbÚ,ÀlRv’v›ˆ¨G¾6yy*ž‡õëô3¨ÀXªƒÀjXZ?"øŸÏ×g+˜§"Š4@<Ò{¡d²‡@“[M Tbòtäè ^á„eĺI•åN„û•dV¿¾:ïÂq¼X·· •J¥|ë΄P&ƒPà,ê£~³Xþâ1¯/÷€ˆm)•Jh4`iiI[OèãÜÿ2 ­i]Û(àuÀO…Å=;ÝnŒ1Ôj54›ÍP[ÒXæqíIªÇ´ì<,~n~¿øÞy ¸®‹5ÇÁ·RÌס¤kY¸7”_‚o Í!6éà€ â#iT“''ºa‚Hž©‡`zFLˆRkŠÑŸŸ¨U&÷˜¾iÚdÀŸÅÝOÅ­Ìû/išÝ厵Z­¢Ýn@h@§ÓÁòòr"Ýi'OéôÉÇT‡­#iˆŠÌÀÂÂÚí6677•sd]º2T Luúä8€šxâÄó<ìïï£ÕjannŽüòž‰Ä‘‘„©ê–e(@>—ò<Ãâõçä%îxŒyâSÞ_9 ÒùG±ö ëˆÌâ"mB"'‚· F!ªÔÉ^£8€…ŽÙx”À Š·úEY™ó_üÑh”yF±hå‹@ÚétëóËpž"vÌi;h1Ðé®T*˜››C¯×Ãóçϱºº[EVtãþY-i•—„~ê"Âä>ÕŠ½OIãêœ]Ás³[…B!4›]´ ùÜ€ÃYºg¡›7›Mììì$Ï ¯µçÇâ¯|,†‰ùä8ùX¥7iŸ?ÆZ­VVVå5•£àbÎC<Ï mÄ%/°üÚN¨‡? 3œæå@3¹É¤ÅÐÃbÕ¤@e`4 † ˆ<ºüTĬÞÏÌ<#gÀ4O4ÐÜͯ¨¤§‹ópfuŒng÷þ6·|%€ã8°mŒ±`¢×«$yÌêê*†Ã!†Ã!vww±¸¸˜X‡èÐýËC*]Ôy.îÞÿÏÞ›I’\gb_ä}VeÝU}TwõY=Ó3=ƒ908$âX IÛ%¹”¨¥D\ÙîÒd4ã®~Hfú#£vÍÖÖdZ­DrE‰FÒh W$à`Žžé陾ï«î#ïÈÈ8\?²<ÊÓÓÝÃ#ªœã™eF„ûóçþ=þÜÝ4Q¯×155…™™™Ž¤z!Yϼ_3 öšx+ ë«Ó/ À¸ç!þ©~ÓP´$èíÍ3[ômÃm<ïwi Ÿ Ç"{i4_Ì}çT€ÙEZ¥‡ "ˆ'Œi¼›2N4Åìì,lÛÆÆÆFO “ÊÑ­['@þ¼[¢ ÎØØÆÇÇ‘Íf…þ ƒ¢A¿hŠe¿ýú=þ´fÌ:ýQþÃаg†N0 CŸ¬©XÏ*ೄöà;2³ø¬:/þ ß2„ú>Ã( rÞ@Ðo î%^qs\Ãtd¦Õt3%Ã0üM\X˘aÂíoueò>ý¤AÌ,É×É#Ncvv°¾¾ŽH$¢åD2•iàK•›B¡€B¡Ð±öÁ {欓á ò„Ö·ÙlöU „Àv]Lï“ù†höEP½„Ðé ZHœƒ¬­3e€‰â~ˆ!€ w_hqÐ'³EÙLaf4À»œÉÂõ"ˆFt]º± ô ôuxúüôêÀ¸X´¥ÍE¢l6ëÏåfAÛ¶m4 ¤R)-9,pÊÁ—?%ƒÍCFFFüí™×ÖÖ|¥j´—fp þù|£££˜í3Ã0ÚæêwC2óÿ ­>މ‰ LOO£\.w5|2LdY–þccc8pà€_×l—Ý¡±Ä[;ú¥TêÈî×}ðJt?eS_ƒöÞí8l4tC€h|„0C¢>Q™U€áÚ,>«ÄŠÐ!LÖî)Ó&Ц“³Û¨Y1ØnÑ?~Õ™¯ãÈ&mÄ£ž¸\Z »ªg-ŠUóòõöý¨ð=~ÿŠ xì°[ËýièF7@ûŠ€[[[¾‰[E½6¤:=y™ü ta§&ŠòÑéLMMù€¸±±ÏóP(´ò&2MÛÛÛ( ÃÜÜ\G}ó[&÷SÉÉêg´ÇÏo]-rÄ Kìüÿ^×é‘ã8õ<ä? €À*í:ÙU˜ƒ0¡¦2к «½GeÐ¥Ï>~+,BðËŸzK3K2…üŽhå`o€ŸÒâa_µÕ(Äb݆ÑÚénuuµÃâU,Týf‘L¾Áfîne³àÆÊЄÉÉIÄb1D"lnnbmm ããã==Ÿ½¤R©„Z­æoñ;;;Û±Øè9óÊR·Ê¿K|ý÷Ë\/F 3ì£C–eùyôm@ÛÆ‘ýêýÉÀPîè‘S9ÂÆ‡³ 0‡ ]â…D!ÐH§È@ÈÚt¢8wIM IDATãÙNÏmJd ‚ÔÏÈlƈ¹ˆDÂÝ'Ào‹îô®€ŸÆ=v¨e´m»g€¡~t,€ß‹)•J=Yç4TÃ:*¯ù°Ä€H.{¥ãÏ …âñ8âñ8ŠÅ"Ö××Q(B﮸—ä8¶¶¶‰D033ã¯ôÇ“ª§,{®º³øž9ågŸŸg7Ï\õÉòÑ%Z/‘HÄ7×÷Ëráº.ÏÃá}˜|8 @IKvc”æ&uP+­üþvÀœ˜NK$? mÕ’øÎÕLåk=*=jÚ¤í€o]:†[£ø™ç/c"§±ˆMGö}}?zpÀOãF³¦ .J¦Ý3¸D"äóyßóŸ æ`‰S~Ý~;XÑ•ÕDà¬ÛØòŠ äªe€ƒŸËåJ¥H$J¥°¹¹‰jµŠ±±±¡²BP©TP­V‘ÏçQ(055å;1R)G2_ª0y³ç|o_$7È;lÞ½”_•¿ëºp§¯óÿížÿü €÷ÐÂð|=ZÄ4æÒ—@G`øx^Jš=}ÞR ¡x_gGkøü·pbzKšG߈y!€K"p=…L])K HÓÐz~Anâ{—ûóIŒŒŒø » ÐÖÖŽ;¦L+2½‹@:LC¨k‚—¹¬¢²°?»{›ªÜArãñ8>Œ­­-$ ”Ëe¬­­!—Ë!—ËõÍ,Ü-Õëu”ËeÄb1ÌÌÌ`ttSSSÊE~‚ÀYÔ; ~´þe=}VñËœ¼E @˜²ÓrG"4®Þy9Žƒ$!˜àÀªªÀà€°Z ÏOv†d<Ð*è+ ¯¤Hҵň­<==¿¤Å´nÛq l‡ i{p\À#žxm$¨s‹HÄ"HÄ Äcô&ÔðãO_AÓ‰îNìðÛØt„p½ýÎ៞=ÒÄ·ßI÷¼ a´–x¥==êõz[¯Ð/¾lÏ™—- “) A¦^6_:F-2Ç‹@Zf••_v/ª²ÓsB&&&Ïç±±±\.‡íím¬¬¬ “É ŸÏ÷¼SX¢À‰D066†|>‰‰ g?JlÏ_øeÃ>¢s?›·nÏ_`EéÃäÁ—Ydùbyèø??tÕ Ù–…ãŽã=Ô[õ¬ÐùΔtDz´‰ÊRõøýìt•^æ åo»0+o/¤ò#j5=4š ËC£éÁ󀆛CÝ- áfá\/Ç‹ÄCİ5DÐ@*RF:ZB̨Ã0€l*‚L:Šl:‚tRÐkbŠœˆºú½ñŽôa„ð½ýÎTÝ?=|ä˜Ãh58™L&¸L ¢æjÓ4ìîÇn†^~ 4Mž‡mDé4;®Ûó ±¬×FeòyèšñùsQ¼Ì ÀÞ7!ñxÀÈÈ2™ jµJ¥VVVJ¥ÉdJ¥ú<5›MÔëuÔëuÄãqŒ!›Íb||cccmJ=Ò}Dõ#:—ų÷FÛ,-,¯¨ç/#^©`ëž·æˆÊ+³VðñA–#¾ì4žNÿëÑñÿà锆rÀ0 ´šfÿ±³/rÏÊÀ¯òàÒ´)týÎ4m‚ªé¢nz°œJö,ŠÍC(ÚPµÇá‘("‘ˆoZöM üÆ†Ž¹®‹˜ÑD&ºŽ‘ø2F¢KÈÅÖ‘ŒŒåc!7Ä÷\TþD3Í AŸá~Jc9‡'¬”šzS€Ö|ö¥¥–u‡m×××±°°Ð•)–ïíñ cPÏ<°y^*“*¢Fœ™R £ðå§Ä ¬Ôü_¯×±µµåƒr¥RA±Xô•°d2Ù“¯€çy°, –e¡Ñh€‚l6‹ééid2 ŒŽŽúÖ^9êøyRÕ­(­î{&êȱ©nH–·HÁãjþï7Q‡Âãû¨P+í~ÓP(/ ýí¶ÏF{‚Ýs-e`G‚NoŸ·td®H+Œ ÷k¦‡rÍA£ íƒXmœÆ†u ÄH #™Nb|$îOÒ!Çqà8i¬›Çp¿f"‚Æâ÷1Y¹üÚFsQÌNÆ‘Ï(L¨Dz¡¦0V™“¿ þ.­ÏžhàÏ~ ”"2 …BËËË „´mcjYªÕª¿¼ŠtÊ¡2ó½¯ ’ñ²&^vjŸh¬W%Keu`åóùòüì1“É ›Í²,T*T*4 ˜¦‰F£R©Ã0‹Å·¾ª8óŠ Užé7cÛ6\×õ•‰ÉÉI$“Iär9äóù6K[.v¡*WÖSæëGÆ+JÀ®ÂOóQÉ ’Ëß 'zÞ:Š«L¦èÝà[vú_¿³ÙlbÂó0¶;î?ô·h ÓûB»```w<Ý(;µÍÿ´ü©®RlÕ2X-eqæÀºšq‡ª¦‹RÅE­™À’ù –̳p,R© ãé¶mfÃR,C,kÉ*@Ù»ÃåÊq$ŒfÍw±YºŽ‘¬ƒS ŒæXE äó = ÐÐâѰxzÁÂ×^É¡Ùlö¼,p"‘@&“A½Þr¤¤{ †ÕÕU,,,´rW4¾AÄ‚m 3¤&:|Á­ÓûW…Q¿×u}¥€FãdJ!ÉdÉd°, µZÍWlÛöØÙ­øŠM4E<G*•ò·xN$H§ÓÈd2Èd2Bp¤G:”öþEõ&zª° ø ºnÍ+ŒlÙ{Q(d<¢|X¢ëÿ÷ ü !pšMœÚ±|Ði(,eéB ôwBº6óÅw>ÏÅCM¤¤/ ŒŽŽ¢^¯·f‘¡µµ5ÌÏÏwÕ3W™ÐY@5æÝŽ×SCÔóçÁ8(L”'•%R6‚,¼ Ã0J¥ü霞ç¡Ùl²,¸® Û¶}¹´‡OÓÑaµx<ŽD"áYo~Ù‘W`ØÞ¿¨®ÙcPK´ŽØç&½¨|ÿœ{íù‹ÂT÷Îzÿ÷³ ´mö×ü?L4œ €,‚5%÷M`djHÒïPÔpñùÇob¥”Ãt®‘Ÿ!ÀVÙA©êbÅ<…;æÇèFÇòÚÆôƒ¨÷r¡P@©TÂÒZËÅÓ8Ò| ÅGX8Ä)ÆúÀõîߺ?‡·@­™ÂOä¢:³¾¾O8à§‹<}¬7nöîMnÆÆÆ°²²`×T‰DüÅbøáÚJÙ¥%€ö>U½°n”z­Ó3Õ5ýËÌÀlÏŸRøË†¨B@jÞ|ÎßO¢Þ»ª7OåòY>ÝXxeLUQú°òU²Ã*üóaÓ²;ÄT¯×ûÞj6›ˆ²+õU£*å´„Ñ3‘¸[eÐPvdwÜŽ®¥81½Óla}r\‚• 7ªŸEÕ;€|>ß—­O»%:f=::еµQ\[Ë¡½ºõ6¶k8³f¦îÒGæÂq#8wx©S¨ªÑ…耾8q§ÔÀÏ|bÑÄ+WÓ°mñx<¨PJŠÇãÈçóþ†6´wH‡x ×^ºÊÄÚ‹Û@³àtgúW6?…P§ç/*%Øð¦gQÈ®UJ[v‘C$[' ùMè”WFAåéGÏ_·Žé·A—û®X³í}ïùÀ°Ûõ\ì Ò¯‡µëùÏœ±L!f2Бª+…€ÉCø è)5ÓÃòFëc¸Qÿa$Rc˜)ö}!J†a`ff…B÷ï§ðny5ûe”k›8w*Óá$1€ŽÝo]ôð™”:½})Ÿð­[x清d¼å€Ô«`&&&P.—}ó05'—J%X–8Ô¼Yz,Ýž:¯#3L_âªx]ðçÇé鹨~Tõ¥:W·Š¯‹«(é–WDa¬AéÃvþdVj1Ms ½×ópÖ 1T9 üï};àAð¹Sƒ€o¼p”‡þ˜ÙÒ”,¿Ÿ.,íHÉÚù•*.®Ù¸^~—+ŸG6?ñññ¡–’É$Nœ8ñéc¸Zù,î–NàïT±QÜY:3 ÎÄõÍò«rgë^FºüÁîvÄs%cn!÷ðì v³?Sòù¼òt€6ntš`GÉC4Þ"0ë'È@JDu€NtTõœeü²s¾¬¬Y^4·]”¿ì\VnYÏ\uaêMVWAòÂÆ)8"Òå)fT)Äô?˲ Ç>4ÿû´çÈ#ûH:_ä G°2†/ŒB.M×JÁ.m–<\wq¹üY¬ÚO`ffù|¾'™ƒ&Ã0077‡ã'NáAãYÜ®<×/U±²ÑéA øý}–¯#3AIDy¦Ç%þœá^ˆúÐwÛÙYÜ0 ¬¯¯+·:íÈ»•¥JÂf·=YÔMYàç=ôe¼Aùˆî_‡'¨ºUD|2ÚwŠOϦ3 –eu%+({HÌÿ†f  o €°ë>T`Ú⣠€á P´• å`£èàцwK_BÑ=…™™™ž¦õí5ŽŽbqq[îܪ>‡W/×±´a÷øØM­ úPñ @¿ à§ôô‚…d¼¿ ȨçyX]]m¿“Ý~Ƌ¨™¶›ž«ŒG•^%GüUíªêF àËÝÍ=‡©W•3&’' ë&¾^Þ)sPζmÃ% ùŸÒ fx…¥á³=í$my7Ê@7 \)c”X1Ø.;XÚpq¹ü˜‘#˜››ªÌt)›ÍâñÇG§p¯ú4^¿\ÅVÑîð»ý^.B úúÀOƒ1/œª£iõÇ™H$0:: ¾“m–——û:ž+û+?ø|€[‚À{¸‹xƒÎÙk6œ‘¥LFйŒº­[î e,LX¢×«bÀ€´÷ï `‡¾F£!8ä8hFǯŸD¥…¨Š}£¡B¢ö6ÎÌÃa+Ôh»ã.ÚXœtœpI: e’Ë5V›¸VýêÆ<ÌÎåx¿.¥R)œ9sï¼ãàaÍÄË.ãÓÏæ1’•M™#ÂÓ@^%¿2Ú‚vã>w®Žï\Êj9ê‘a˜œœD±XК @}lÛÆÆÆ¦§§åðÀ+êEˆ€Y×ñOÆËgùDyÈŽ¢|Ø#»ßmø<ÙxÕ¹¨Ì|ÝÈ” Y˜ ªòuxÙ{éGï_—Z­dñAéE${·jµZß{Æžç¡iYÈG]üÉLÀk¢nßyJc±¦Žà"1õ*ƒCÐù0€Í€úFB ÖQ8>…@Ržû[cˆE]- •Û!¸·ÒÄ­ÚǰíÇ¡CýBˆ¿¢Y2™ÄÒÒòù<²Ù,*• r¹œovîe2<öØc¸xÑA”Tñ÷àGžA,j ­'®wÂS%Ÿ”WÔóH>¶p`ÜÁfÕìË¢@ét¹\Õjka(ÇqÇa–——155%ê €¦çìO7,žcó‚§þ…ý ðWjÐ@–—»Ž(^¦«(ð–ÙB=ÝZøç òÖïÕ"v&H˜x–ØgKWjì·`Y€ÿö…71›«é¥q£ ª}^$ÔôZÛ¨@*&·d þ—€’£¥!u`¿†2t§t°É‘ãEðŸß=ƒŠ•Â?ùÄ+˘mrî| /fúa=zôÍf‡Æ·¿ýmŒãäÉ“xã7pìØ1LMMá?øž|òILLLàÖ­[X\\ôWFzÊçó8yò$®\¶ÇÎ_®á£g³B^¸¾6…Å™5è÷ò‘!,Dß$¼ü¹'«øßŽõ¼E0Ъó©©)_p]ןfhYÖ×וV˜²€À6ÞºŠ@+@@×µ °¼¼Ã÷vuÙµ,½ ´t@ŸgZön”^6¯(±çÔ‘´—Þ¿ÈÒ S0d÷­Š—Íg½°uœ+âÌäfße¿×iHíÑBh îóa€:[‚Š>]+m,âá‡NÝÂsG0à¿KË6Ö+9ܪ¿ˆÉÉÉžþhãT,ñÖ[oÁ²,¬®®âþýûØÜÜÄüü<ÆÇÇQ«ÕpîÜ9 ضS§No˜F–o°e|az€4Ld]Ð sdeòyËʤ°<ªúYdŠ€(¶\¢úáÓtsdïAöØc·½•r¡3ôΊÊkªÕê@zÿF©˜‹¬ô]v/4€[íŠzVDÀΟw'—mØ-e@ª4¤#@žV)¸»daµq5SW‡H¦Þ<ÏÃ+¯¼‚wß}ñxçÎó­ :uÎ7`˜››ÃÒÒ®_¿Ž‡j5t2ŠD"8sæ 6í¬Wsxëj½£ú&6ñå'/b"[T˜8ˆ©Ž-Ð$yž àß'øÂS¸Õ§)D###Èf³~ƒ×l6}°[ZZRZøg$`ðuÓ3”õ’ù5úÙtÝùòÉu&tˆÊ–­$KÖ™aËGÍæ»šÞ•‘"$ª§nž±(ŒÊ¢òƒÀ9øY¼,˲``qž–ó_Ÿ:üɧ¼* å@ë¡/2 P„¼ºJ,PÞ8l–lVb¸g>‡ÉÉÉÐãþôé×ëxíµ×°¹¹‰S§NáðáÃÂ/Hù’)±X Çœ?¯¿þz׊ÛÈÈ8ˆeë,.Þl`»Â~h’zÓü@¿ãÙI² ~÷Ä| Ó6f½“· 2Œ–/ЪojZC:«² À6Ö"`¥ ű{΋ä÷bæfΠõ"zùsöšùï…ʦù„}Q9Øp¶üìóè¶^DõÄßLÙ`Ë%*+{dŸŸL9Ò‘†9öoš&¾pìnße÷JCbØ_@ô‚µ¿DíxHI°R)P&Cñ`µ‰GæcˆÄG166º(5 ‹E”Ëe¤Óiéød"À^óatßxBˆ¿;«…‡ù˜O:…¦1…¢=ƒ·®ÕÐV¯ú“XAèô!æiã R Äq?ñ\¶ãömœ2—Ë!›m9N†Ñæý¼¶¶æ/…Ê>™9–^h»EÁs5æ|•EÓu]8ŽãçCyeå Š?{T½ç*€¥è§J#’É—UŽXåB¥t[Olý³yÑrˆžŸÎsfãUÏ€–K5<ÀóÈ,T¦i¤÷OAÓ2ñÜÜ*f³ýQÜûI†ñ>ZHœÔ@Ï D Pжº$x>°ÑR¤W—ùm”j¬5Ïø½;bë¡T*á{ßûLÓÄÙ³g‘N§»®[UÆÒÈÈr¹Ο?ï|ç;m§«ÄãqÌÏÏcÍ:Žk÷,+®¢ ÅuÌ>K"ä•ˆÐæSLt|>²º’¿LYâIÔ`óJ€ªìA ™ªžtŽ¢0ÿ þ^‚,H¢²óruiPcÿ¦iÂq ¾tìNße¿ŸhH‡v^,)ˆR!P€DG´¾bpoµ‰¥Æ±,FFF´î“ÿø†¿V¯€/:—]»sÔö^„®°°°ÛC©9‰ ×Ùi‘€/UÄØÊâƒÏ£ôe<4s9ðÓÂÅ¢À>SF³i÷m¥²t:ݶ: Û*•J¨T*þt¯H$⟷ŠÖiÓpóÀ s’cåÈ=²òi¹ù#+[¥X¨ÎùòSù"% ¥™•Ëæ#Ê#è\ô|ˆÛ·o·Mƒê‡" ;篛Í&®^½ê¯F 8|ø0ŠÍiÜ~dÁv¾@v[t?• üíIwÚŸR&éáÇŸ-£ÙÇ]Ë¢Ñ(æææ¤wïÞí5€W5Þ*`èlø÷ˆw° r& ã¯y‡;]¯þ°ßŽê‚¾%û‘ùRè*až‡ªl<éı2u}P*•ÊÀ<àf ‡òUTHÛ»Ö²”Ð裀:Òåœ !þZ°ºº*7 ÛpSRyñËÒÈò¥õÐuˬ:g¯yð ;þ¯ÃÃZ‚¾µ ò«ÂxpÖYÊžG·ïK·qümÛ†išéùB`Ö«8:ZÆÓ3ÃßûJ`Ç?µà¥V!àÒ¨,;¿…‰ üЉkÈsëÙB°Y²Qv¦Ïç•÷Äü®ëâÖ­[(•JJ$Á±cÇ033Û¶qýúulnn†~þ:lëy"‘ˆp¬PEÓÓÓ¨¹£¸+Ø h4UÇ\¾‚lÂÒ|`—·GÐo áx„™Jƒ—ž."ŸòP¯UéÃQ,ó—&„Àqœ6ãþýûZ ­jÌuA! 8«\èŒýë‚Hóå ócÓ /¨®eõ°1Æ IDAT#z¼|º có•3(­î» ª?v¶A©Tœé¿Ñ€ã’Vïÿ-ëïJ XpïQ!­¨³á¡iÔÝQU·À»a>’ÇãããÒèÀ˜ŸŸ!÷îÝëÚ9Ï7¨‘¢×…B©TÊw j8(Ár3XÝr:êl<]Å/<ó}<{èŽúøiXÐWV¬è³|KyZñé8Á?úĬ¦Ý·=€–%ŸÏû %»O€mÛþÔÀ֭胴ŠG5ÌÓ é‚¢(L÷ÝT¼,¼WE@÷þÂÞ_ŸAJ@PËxÂøx„͛畀r¹<0Ó¿çy0ë5œÚ“ÓÁ †€†EIJ@­%ö „V é™_ÕtÑt’°ÝXÛ¯ ĵk×ðàÁƒŽ©E<ÀãøñãˆF£X]]ÅÝ»w»väË jœhY.\¸€„òåfPox¨š®^e ¿ß ¯’ô>Ñ÷g7þGž¨àÈ” ³^Óª2 sss¡ڲ;Ý5äÝ€J¯iƒÆþuÏUï²(\÷½ÕQdÓçTÊB%@·®ux{y¶:idü”¨éß4ÍyýØ™%ãáÏ^ˆüAЇ @ù` ÅB!`Ò¨”ö¼\sQ÷òˆD"mÓ¸¤¹2Àfµ¿l6‹“'O"•J¡X,âæÍ›BçÀ^aÃØØ˜¶‡/Кh{1Ø^ëEÉyW€Ïð³(µ€O(}oä…/bxøå^‡í¸0MS¾;ŠF£˜õ¯=Ïk[%pyyÕjkè¡—Æ> ¯ê('¿@¿ÀŸÍWú:ï|¿h%FÕu˜sU˜¬~Ã>/™œ 4:òX¢õäºîÀ–ûZ›gYVŸ_¸‡Cùþ Ç}PhxŸØFZ•»R˜tŠ\9¨7,7£Qáy>,5ÿ‹~ñx'Nœ@>Ÿ‡iš¸qãêõºvú E@vžL&Q¯×;ÆXU”ÏçAí&Û÷ ø`øI;O ¯4ñ³<²bîž=hâ…u4Ì:\·[Žf³YŒ·Í¦þ„´†„¨R ÓP«õ°iÃÈ’Éïöœ½}[:JA˜ïC&'¨ÝÞ§,LT‡²0Õs­ÿ¯›VU‹E-¾n©^«"sðåS7–Çû™†R0 !Þ¶ˆmäµ^,ÌU {€ N9hÚl/ê/åª%‰T*¼þúë¡Wý€ùùyŒÃqܺu+p†Í“Ã*µZ —.]R6Ž<Ñ%A]/«é!T…Kž¯Öû Š6в²Ï6ˆ§ýô¿¸ˆAú¶8ЪÃÉÉI¤R)¿¾iõB]׎{÷B5æmw"©¯n”X…QDéT+’'ê¡÷ú Ýó×)¿¨nز³Ã‚²û”ÕŸÎQgýÕ‘?òG׳Y–«iãg¯!ŸèÿŽ‚ƒ¤!NÀNƒ.€>-hK´ûkK/ËDLM›ÀqcZ«ÿ± E$Áøø¸tÍÿ yµsss˜››ÐúÂ6fA ŸN§qüøñP Ñ-vÛ‹À´*Sòü¤€¯mÒ}–O˜Y{)ÄfF›øÉg·Ðh4ú¶]0ÐZ¿ýÀˆÅb~#ÊN 4M³m}ѱ­¤Š8ÑrÀÝ€?/›5Ÿ e@+è ¥zuHõ½ðkè‹ÊøE † ,»y<èSù:À®£ÈÈ0 Ôëu4?!õZGF+øÌÑÉãƒ@ëÐóíl .C(F^Ó!p¼ÎÝÁ:¤ràžH$055jÓ^)Ãââ" …‚ÀuYÏk-{K}t‡b±\/ Ób<wžgÝŠ£XOÃv£íQU,~òšfø;…äc"”ŠAëÿ§žßÆÁ1µZ×8Çãþ*´çO— Z&ÖÕÕÕV9B4è”D›Ïèuâhì¹.0]óòe{ð2‚¾>è:è{SÝŸH®Œt^°Uõ¤º÷0J­R«ÕºÒ]µZ…çyø•§ÞAÔЫËa¢$­ÜÙ(î×Ó“R ¾#‡x„È÷—5B[[[x÷Ýw•Ž|AËRÙ½î¤ضGµ­}®CŽãÀóÜ–©Ë^­ôo.-àíÓ¸µ9¥aÕ Êi Ï)}¢xî¶b‚¯~~®ã¢V«:e2™iÃ0Ðl6ý] ií°½½Ýy7š  Û¶WÀ¡òØsBH[O]$/8e`-ºÿ^¿~C‘E£›òÓs¾îùºÒ­o¨Ü:J_—²8Vžçy÷·mV£/¿ƒ…ÑÒÀò4}¸°ŒˆÿÀÇ+̇&øµeС„W bQžÝæ¡-.f;°§R):å O„IÒ ¿ú™Ø  »ÒE‚ø3I€–ùõîÝ»¾ch«üjÀà‡™vï»{ðgA_FWܺà/Qv3 šG/–6-»Ùhá^ÊÏž³Õµî³a{æªMŸt‡}DD•¡r¹Üf‘Ù¶ ³^Ç~€³S›ËçƒD±ý.€œ¤zé8aX5^@Â¥õ“l³ù"^Z¼€ƒ£[m) ¹L1Ôü©x:¼ôüĉm½µn‰O¯jÄEaAz¡PèX¯@TöÞ[c€ barT½Cb kž*ù”¼|jÈêÚ½øÈÑ>¶ˆ¿~§eíÑY$J—b±æææ°´´ä/&E7”¢½í{÷îáèÑ£H$mfx¿¤;מç ÍÃ"pàÍÕ:qTaäǶeÄÊ]³y‰––åÓòçA$»ÿ 9ªüTߌhf {Þí÷,Ê—WàÙk¶,aÁ¿Z­t¥?ZÖjµŒÉŒ‰_<{u`ùì Áð?€!UÚ†Ú*JQk¼RF!ð“v¾èãé*Æ3t…)¦0š‹"JZs]ëõºö~N§±µµå7ÜÝ’ŽÀž‡Ñð=Ïó=ÎÃ4¨[[[ˆˆçazŒ}Åt›§0éöôå<¿ôÉu\~”Ár©ŒXa\kª¨.%“IÌÌÌ`eeÅïÑÑ÷θwïÚ¬@'ø‹€\ÄO‰ç‘3¯ˆÎY™"„ ½çl˜Œ/,é‚·ˆø{ uÂ:ȉʯûͲùˆî_õ-ë|ÿ:ï_ŽZ­†z½>p‡¶Z­ ÏuñOŸ¹€T¬?;pî' þç€ÁVa„ P›†þtÓêÈ"£YÄs%&*•pc¾¦ibkk«e*'áL”ô×OA9Žƒr¹ª€íímž‰|6‚t’­7hV?蕎cò·G°T*K$ÉÀ"HÄ<ü‹°Ä_¶·Ÿ”Éd0==íOô<¯m8Àu]ܽ{ןi ã½ :ò`Dyš§ ü»9g”wºcËÁ)ða¢Ýû¨ÜH$"üñJJŸ§$i^|X7u#"¶ŽdõæyóÏ8èYPðôt? µöE£aá+‹7p|¬8м>h4” €²µW*ºJú¢d’¤âd»cƒ– ÁÉ“'C­0ˆŸŠ‰N:%4a«>ú‡"†MÆÂ}W€O^è _ú¤SÆáq ÿð…uX–Õ¶€O¿(—Ëajjªmڣ׬c X‚Ž”xð&=Îóð ÌƋưƒÞk>Ljªt«©ÊO뉆‰Ê¦ûœy%€W–x+Í^¿ë¶jOoãÇNÜh^DR\ƒª« ¼RÐ!ƒ´ÿ䘌#…u¬¯¯ëÞ™ÿqB°¶¶Öõî~ƒ~ ÕûßÜÜô½zù€¿JžçáÁƒˆzeœžOrRÀ¾ï€O¹ Ð'Š IÁ:@_ÌócOoá‰C5Ôª-ÿ~R>Ÿ÷÷ Z &ë|è8t•!þ\ø2ÅL–6¬À—»Z­îø›¦ ÓlàgoàÜ´¾uõ½B†ñáJ€B2 0Oð £CºP)²FsQÌŒE‘1–pûvð ¤´±^__ÈšÝR­VC¹\î¤ågï…%BÞ}÷]$É6ž[L#Òñ~Kê³€ßOÐg£´Þ¿ ‚£&~ýÓ˰¬fß× 422‚ÉÉÉ6³-]3^onnbuuµ'ðPPZ•Ì Œïõ«ÒèıGYYËåòžxûØÙK Š§gÖð§ÞŸãþ®lMÝ ºi€­ñ,®K@íqÒXþãæW™ ”EÒÉâ#v’?v4;kðhy¦i"N·¢™i4|ï?úǹ¹9xžÇq´^Š^À=ˆ<ÏÃää¤_>Ö HäÄÒÝ»wQ)mc,R‹ON€¯Ä«ëѰ83ýɨjõDñýÉo[QQº¼A|Š%ÃC¼xº„ËK|ërÿ× ”Ëå|¥h½+µZ ™L±Xës/—Ëð<ÓÓÓˆÅbþ;Êñ|:}å£ùˆÒÓ´ôš•ÏÆQ ÅñÄ®VYR0è‘Êç‡/dçª8H³÷ Þ¥Uʼnî©X,|‘J®ë¢Z)a*]ǯ?sFH‹ì{…t6‘Û J AK Äöôá›Õ€î£ˆEØTÈÑûãG“HUDÝmܸqCyo"[º~ÀúúºÖö¾ƒ"Ó4±¹¹ Ã0L&¥S¥d ÀùóçG Ó£,NtTë«Oáon?Z“u×?íX³?yš (Í÷#To_@l¡A„Ò~ùÅeŸ1Q­”âÙlÖŸ"´ñz½ÞÖˆW«U<|ø°µd3ôz‹ü8½ˆ¿ŸVÞ¶,¼:rT||¸îؼn]±yÓç!Z¼GöüTq,±Šª¼`EAµRB~ó£o"ëß~ÃF¶»ÿà ¡ÐòŽ7àx `æ%8¨k»<²aÇàxÑ6QÙT§ç“Èã®\¹ÒѰˀŸý¥R)är9Äãñ°UÖò¼Ö&E###H$ˆF£þ/+o°±±+—/#ámàsÏf j/^:uŸ=v ãé øgÓñ<ûøAüa@?øY奃§ucñ(Áo¿ô“¹&Êå"\w0ë™g2ÌÎÎú Q%€¾a´¶m}øð!ÊåòNÃG¯^íª8!Q¸JQæÁYв4a” Uùùûí¶þغ zVAJ%ú=Û¶­­­¯íÏR¥\öþåû»—ư‘ãu¶¥ûAû_Ž"‘\CÃ"¢V”û11¡•Ic®`{eÿÛ_ĵَÈO=™FÚX…mnâÚµk"EJ@4E,óÔ‰kccCk~Q£ÑÀÖÖb± …Bø³J€l6ÀË/¿Œ)a$mã‹Ïg!ªÀÉL ™»ð¬ Hð\•ÊŒWôwø¤Ü‚̧ü÷?v©˜ƒJyp[§& ÌÌÌ ‘HøÏ¯Ñh ^¯Ø5ǯ¯·¦±ê€›~€?%•bÀ‡É€«”Ý_<Ý2†¹Ö9—•%¬Àý®†¿¥ï^µZ…Õlâ—Î^ÆGf×ö$Ïý¤j3þ¡ #‚ꡊöZ ê" ÑS Ä òdº„xĈÑÁ>]ˆâÔÁ8ò¸‡‹/ú.š3Ï*\é”Àd2‰t:x<>°Þ!%B\×E"‘ðLjYðç-2ð_[[ÃÅ‹‘ð6ð¥²H'éøjPýuß+àw€¾¨"è/èCÆ-¿Y_Ð̈…ßúâ}ÏEy@Ó–¯ÁÌÌŒï—BgœT*•¶÷«R©àÑ£Gþ{«$²xQx˜]­,®Ÿ?U¢8Q]èÖî¹ê9=;J¬OG¹\öW5Ý+ðoyü›øñ“·ñ¹…û{’ç~S¥Ùjk÷›†Sˆ$`Z.„ } Nƒ¹ ˜H€œƒ#›øožýŸ¿¤ŸùH¬À®-ã7Þh‹ãY€¥À‹ÅÇ199‰x<Ž­­­ÐK ‡¡r¹Œíím$ ŒIªˆ”B¾þõ¯#ên¡iâÇ>–U`Ÿ†¢Õ àó,¡øøÀóI$‚þNJæE;s Ž_ÿô#ضjupÏ9bzz£££vŸ[­Vk[ŠÚ²,¶„ýàxrHUS$CC!Ð1øH•ƒv;q¤2NFñÑ3Ip—Þ}kkí¦,Ù{d-Èår°m¥Rz‰ôCwù|mf~vH‚þdK‚À… pïî-$É:þñFKGBÕw_¿ôƒøC€þ¯4…6èav/ž*âç_XE£a TÙZkLLL ¶)t•@V,±²²â/lщhÏŸ‡‰‘.Ъ_6­®b"š•F)£°Ï#H `‰–Ѳ,lookÏBê5›MT*e<6¹_{ú¢ÐoèýH– Ôx( @—m`"™@¸ó@Aý'J²(V¼À6]Š šø#g”ƒˆRA ÀŸÏ¢®#M–ðío»í£9F£QÄãñÐF£Èd2H$ „øÎ8¬#W2Mõz‘HÄ¿M$H&“¾âÁ—ƒõ-T*•ð—ù—H‘uœ:ÁçžÍˆëNYÅúuߟ^~A_ ø•™ø‰sëøÊ3kh4Ù8ˆ%:C€®ð‰DÐl6Q­VÛœWMÓÄòò2J¥R«´ °É”`éÆQâ§ééö¾UñƒâÕ[W:ÊPXàçëˆ?Šê”MGÓ–Ëeï=ÿr £%üæóç¯Åð~¡º˜^&Ð ÃTM¬å™„‰ÂXºSAºP bñ–‹\O’6¦])a³:q*aàËŸÌ!;(mÜÃË/¿ÜÏ)? Çýk`§u5›Mœvkk –eÁó<Ôëu¸®ëoCiš¾9×¶m¸®‹h4Š©©)¤Óé6+Ÿ7¯ðS=ÏÃþá¢Y[C:ZÁ?ûJ¡õ2…zM°ïpt!¿¯ ü컼C?÷Ü~â© ˜¦9°…‚(Ña&º}0U «Õª¿i“_,±¼¼Üf à®0½UÞ°h)^†®R sÝ4a‘ Rtê&èœÊWÕO¬—ÿææfÛ¬½"Û¶Q­”p0_Åo½ðúûb{ß0To ¢¿u|¤ º½.Dô]uK$XÚ½Ü}ô=–Õ‰\]Êo£3ñS'øøc üÝ;×pùRsssX\\ô7¶ewô%27ýQ‡­‰‰ ŸŸšç=ÏC¥Rñu©T*þ<þX,Ã0P(„C‘H¤ôYDæü÷WõWxpïrXïýø(féôÅ Ÿ]S‘^¤!AB^™*°çrP*›ÀÏtu+‚¿¹Òzö™LF#ÿî(b||ÉdårÙ·0Y–ÇqÚj6›XYYA>ŸÇØØ˜/ƒ* þ]0ï­è\ǧ“ù™¨À,¨“!’É[´D=}õÊe ”‡ ¼ù:có=¾\”ªÕjÛ®‘{I¶m£R.b:SÃoüUäïß¹þ2ª7Fë]u”ëšz^ Ðó¼¾ÏWK¥RXڊ¶ â1#¨‹”  Å€I¤[ÅL?õ©n/o£±z ßüæ7‘N§1??ßbc€˜â+m¢¸œ~äìâccc~º¹¹9ŸŸz|Çãq¤Óié:äÙž?oû¿úê«xù»ßA+øÔ¹^zAbúg)ü´¨kÀïäï ôw$(Yƒ@¿“Çð+ŸZ‚åDðÝ­†œ.5(Êf³H&“(‹°,ËW6«Õ*’Éd›R©TÐh4022‚‘‘‘ö;éüÙ8•“)+Ÿ†óa|:YÙdøaâyf‡MT»öñ×2‹Ûàe«À¿5æ^éXmq¯ˆ‚ÿxÊÄ¿üøkIìÝTça¢µjn¤÷ïšÒsR/Cò‹0Göçó,..~>ž:=Ýe?J²0¢V\Æ'ÏØÈ¥YCxªGaƒÞdG#ÎÌ'páÚ6 —omàèÑ£Â]Ô`УªùÞ¹èÇû¨†è9™þßyçüÉŸü1Òdg»øyñ(sÿpH ÅFéû~öbè&­&à}}&'…GÀc€à¹£el×⸾ƒçyH&ùm•ûK‘H™L†aø«¾†Çq`ÛvÛ»@Hk¶€iš¾ò("ÕwÎ_³ûL¨zÐ*Ò3Ÿj¼\Æ+“-ºÑðF/½þ³àóuèZþ”gÀ¿„éLÿêã¯b<ÝØÓü‡‰¾{{œýXô­zåÊ•¿sN4~`xˆö:Âw(§Núl4}ÐS({äÉ0 Ô*Ûxf¡Š™1•§¤¡¼Ô#E¢¿‘LÊÀâ|o^ÙDÝòpéú>ì¯×îg£©8ñkòËÖê—­1Àšø‰D›Àÿór–ÇÿýÑ!á®âèdÿúצOón$sûiüݽs˜Ëoa4©1žÝѦöøJ)Ú€¿#¥gÐ×áixf¾Œ†ÅåG-'ÐAìÀS2™D*•‚ëº~/‘¾ëºþ{À÷5±,ËÇAˆ%Ù5ß[æASÒa€Ÿ_Õ¾.‰ÆúÃä¶÷Oó‘¥Ýk½^÷‡xDé÷‚¨Ãß¡|ÿꯢ ïÄü~!ǾqcÕØBÇ,€0ïÞéêÕ«ÿ½ä~ž Œ0¼:öœWX%À;yò䋱Xì)  ”*NO®ãÄÁn´¤^, Y B€ºB"ÚîÐ2šàØ\®®Á²,¼}ù!fggÛÆäÎqIè‹”Ã0Ú¼õE«ø‰Æ÷E½ÞéÿïÿûøÓ?ýS$Ü5ÌŽšø7_ÀTA¬œ•›Yl7òøØÁ+ˆE8‘^zömIB>®—(L—OÝÛg#xÆ0€s‡*°]à݇Ixž;pKöI4õ·¨6ŒÖ°@£Ñð}Oè»JÆÿ®íÞƒüù6€z‡¹°=~¾ç­¶]4Èmå »IìZd5Pm ´¦ö•J¥¶Éöü-ËB¥RÆñB ¿ý‰×‹ðÆüYÚ®ߺ{$s¸gYžçm\½zõ÷w.uÀŸ·ðÊÁ‰'žŠÇã/ýU𶋤·Š-²=L•?@õÃZ &Ç‹àë×>ŽW—Nbqü!Ñöü&G£8}(·¯oÀ²xûÊC¤ÓÌÍÍuԋȬÏ÷ìù£ìǃ>ü*š¯ã8ø‹¿ø üõ_ýH’UjâßþÆLÈÝGær[xrúⶺ{?YH°ÐËgríèûg È«HD Þº—†ë8H$“{ô¡ IDATÒSÿBˆ?=¾TX&UlÛöK)ñà#S x@cÃd²x’™áY’)*Y:¤R^d ‡Ž…DU<5›MßÜÏÞÃ~iš¨TÊXœØÆo½ðÒ0o-m»ôb©Bϲ!·¯^½úÇô-àçÁŸ¿n£ž]×-†(°ö˘Édpc%††MŒ;xÍ7ÔFÛ! wÅ¥@@ˆo&ñM4Pi¦Ùþ\:ÿüù1üϼ…¥’…oüµ…Û·o㥗^B*•òÇôØñ>ô©Y–ù=ÙýÛbêX6l ZçŸÎõgÓlooãþàpÿîM¤°3‡ÿåW§0–HÀm70jt±Œq·½ûևϬo ¯Ãú‚ÀËŸ8·†\ÒÅøÞ!”KÛÈåG÷dÑH$‚ÑÑQ¤ÓiÔj5ø=σiš°, ét©TªUÜá‚f³‰x<Žl6ë;¤RõfuüD톪#¡ÓבH.{¶χñVþžYK%Þy/“mÛþ3 «¸ ‚ªÕ*LÓÄ Wð«O]ü@ÍóWÑÃR –Q@Jrj»×òèXøñÿ6‹ÀÑ£G¦R©[Xz.£X,†òö:ž;ÖhŽ€ý€ †$Ä/VðØÔ¤ãrÇÌ‘lÏ/&qûQ•Ò&V6L\ºrï¨#Ùø¾¨ç/ZÁïý‹®e»ý½öÚkøÝßý]l¯ßG ëøâ³ üO¿<Ž‘L*UôòPžiP€Ï”@ô•¼,ȵ.ŽMÖqj¦ŠÜ…Ùh"OìÙR¢Ñhét±X¬cµ8Û¶}СCÀlÛöÏØáÑØ> yJ,¿Ì –åÕõP¹«¬ ²¹*Œwê“YHUò)mÛ¨T*¨ÕjmÞýûþ„T*e4¾xìþÉSï"éÒú÷>#BZãÿë8Þ6TÖ-¹®ûæµk×¾±s)2û‹ÂÚˆUè‘÷9ÿÑs:th&›Íþ ~¡U ›FDŪ“ãëX˜RæÀJQsô[1`%“^¯Zx&Á§žLÀ‹[÷VQ¯›xóâ ,/¯àðáÃÓöxe€lê”Å*²s6L¶Ë_$ÁÒÒ~ï÷~ß{ù;ˆØëÈÅÊø§_ÁW¿<Úš’†€^Ã2uÓ€è‚xXþ~˜øŠaJ3ù&ž=RÆkwFP®ÙˆpcX,æ/ Å[¨"´†X/u×uaš&lÛ†çymŠ%ð…Ë@Ÿ‚a·¤j§Â8ê(¬|ª‰¦ô±í5õ ð-+E¹T„íØø/ž¸ŒŸ<}sMí{–¶ëÀ_Ýœ‡—™—ZºxRY²lÛþÞ7¾‹vÀ— @pT*A–€199™û% S $ÓtÙ8Ù¬U|â1EOGó…×VöàÀãGX<Ç­E4ëÛXݨàÕ×ßF­VÃÌÌ R©”_7*ïÐE› ña"9ËËËø³?û3|ík_Cyë!’ØÄÓÇ€ßùÕ ¼øDZ~3 Ë2vÛ[àsLý}ašg4mãÇ·qñQkE0Œ~L'Ò&c'?ªðûÅ;ŽƒF£Ñ1kh)ÍfõzÝ_Y]fšÍC–7 îIwëÑÏ“Èï@åÜ'J&Lå—@ó÷<F¥R©mÿ†ý~ õÜË¥mD ÿì¹7ñ‰CËûZža¤›À·–ŸD<=ªäÓZj4_¿uëÖ…Kø=ˆ•_=‘)ì¹Ì `0lÛ6=ú4L;—™Ñ‚†¶6Wñâ©Dˆ<äÇZ…èÓ·63ÅçŸI#ñpçá<»Œû–ñÊ«o¡X,"“É´Í`Ççù¼j?Ñr¾´‡våÊ|ýë_ÇŸÿùŸcmédÓyÿÝÏŽâ_ütãyúŠ7¦¡›Ûèp&ý~Ái ¯’o@ŸOÇ=¼xb +ån¯Eẉ½q¤Ä*¬)ŸíõS§@> ®ë¶m4 PE‹ѼxbÁŸh8wCa€Z–F.’ÏËvµZ •J¥méÞa~h4(—K˜LÕñ/?þN÷<4ý¾¤ïßËãJí1­Ù<2ÇO6¼Z­þßwïÞ½ µÉ_©ðöC‚]xc Ç`cc£AÙ0 c*ð®BR<dž=Žà¹Ó h#¯ìã—ÿˆ3x.­¶Eϱ03ðsŸÎá³IãO¿[Ã_¿ñµæ&Þ~} ¯¿þÆÇ'pîÜ9œ>}èpô‹¤'dÏÇÁ;wðî»ïâÍ7ßDµRB&’¤ŠñÁ?üL?óCYäRt/†ÎÛ®Ù)dã!òè¹1Û»ç˜ûÖËo”ƒ¾ Bô;‚å<©˜‡þ™Ûxâê$þÏWæQ*n#?²7Î,FkÉâL&ƒF£á/%Ì*téÙT*åo>ì6l4a´¦´&“IÈŠò‰òvãØzи¹Šø†—ö¼Y냮•A×ÀÇQE¦ÑhÀ4Í6+˰€>Ð*g­V…i6ðÔÌ:¾úÌÛÈÄ>ØÓüdd6Kë“N±½P¥Ry ‰RawÛ hŽüX”9²?ß"ðå/ùkÑhô Øwt z™766ðÃóðë_ât£ã¤7êÓGÕ«”bÕÃ×¾WÃ7Þ0±YšÈ¡I²pH‰d 8t覦¦099‰‰‰ ¤R)aÃR­V±¹¹‰õõu¬®®âÎ;¸wﻉˆÒÀ‘™(~êÅ,~ò“Y¤–B€¿¾ýî–gñ‹Ù„ÕÙêì¹a?0M— / ×Ûï îŒw)ý·Ç`ÚQäò£{²hŠhÏÞ¶íÏv e H¥RH$¾2 ²Òé„ìbV¢áÞDf¶QQ¹²áJaV>K–eù ?, OÉu]T+%4m_Y¼/Ÿ¼Õ¯&ô}I·×óæ‹ˆŽ,´…‹f”ýhÒ¿ÿû¿|yy¹Š]s¿Ëý<æÈ[ €ˆ<ˆh¤Á]ËÌ pçU‚(ìÇ9::Š‹÷Ó(V-rŒ/€¬A4Ø¢‡ ÓéAÄ¿¶t§Æ—PH‰÷~ÍEð_~1_úBo\³ðÍ7M¼r¹‚ZƒÀi¤pëò2®_NÀ%1¸Ì㣠*í9X–µó²ÄQ؈ 1bc,ÁgŸNá¥&qöh`ÐF@&n!nذÜ$²¤×å<Úñ¹´‚Óþ¥0èw‡~Jgç*øŸ¼ŒßùÖ1Ü\o­è—Ïç÷ 4èô]¤Î”!¨×ë0M‘HÄwy‡FÏó:²á}X¨R Z·ÄÎVà} düAÄ>êA§OÒøa~ ¥¤Ôªe$£þù oáÉéý.ÒÐÓ¥Õ<êÆ$ò ž0ï¬çy–——ÙeVEøÌ÷ü;2èÅ…Øæ8Î5v\# ÈñÆãqlÚS¸rÿ6>ö˜lö$_*AEöÃbÐM£ÂÜÛõ­ƒxùÁ¸¹}ÿèñ¿U&‹Àó‹I<¿˜„ëWï7ñÖÍ&ÞºiâîJë%„.‰‚ R7Шí4T H‘¥xˆÀÜD'Æñ̉4ž;]À±¹ØnÑBÜ×'^Ä .#Õ5õuÛ£È-§;À÷¯öôuxZ'c™&þ‡—®áÏߞß¾5‹’ç —ÙÓY<Ñ=€Ýé‚ìê‚ü­¬éÞ±XÌ_¦Z´T*õ)`ó Š{MÏû  ¢ÞKª¼Ø8º¥w³ÙlÛ{a˜AhÝ/Ý2úÄx _ýÈLgêû]¬¡§j¸¸>Ó±»g/3<Ï»Ê²Š’ë”-¨uõø;4ŒZ­v•ßäFôqtkš‹¦§ñíKðÂ"iV›]¿€Žˆ|xL=œ(,á3G/`&³ t£ðø‘8?Ç/|¶UÏu‹àÁšƒ•mu‹À´ªf«—5’‰ •0I84Ãá©hø)|²²D¢àÁ¿ ÏÉ -/ŒI¿“)4à ƒ{ô¶¨ "äŠE¾òôg*øwß]@©è"Ét[a]¢VBˆ¿Éë+´”¶'ÌîT)RXÇ?Ö€ÚaH~v¬Ý’¥g¯©ÒBôÞ(»Ò0“mÛ¨VJð<?}ú¾|ê&"F¬,ïwº¿eàu©T°ŽlH€'Û¶¯¡‹yFa¯Ÿ%• Hh8Àxøðá•ééi¿À2¯]þÃÐ}éGFFp}mwW‹X˜‰ÔQ!>¨ †·…C@ÃÃÓÓ7ºNÏR&iàôá8Nô°n9„Ü®d÷øþÕž€¾_À (€Ÿ?;WÆï|ùþý÷àµ{€c7‘Íå÷ÜAPDtö« Ð%¶ñ³,˧¾üº¬löHÏEŽ}|™déx…A4û€’ëºþìö¾d|Ãú3\S¯c:[Ço—t𛟹…¿»1ßõŠEéôpX(±Ê°  Žãø=zvÑ!ÖzÀÊ +²ƒ¢Õ-)?_>\Ö³§;êÑ%¹é]¢›O¯Ê{ØÉ¶mT«e¸®‡ž€_8{©XK|€iµ\)F*«¾ë³²µµõ–H Ú1Z¥ï UÇy#‘Hh)B5‚ѱI¼|=‡yÊÄÄ·AR¸"`P@4dÚ 4ÜÆRåž‹$£íFÿéæ‹˜ÊñÒ±WºÐ-8÷[^'SW€/ ÞKÐß=‘(‚,­øOŸØÀSJøÝW[Ö€¦Õ@.¿¿¾2¢kWÐY TY…èN¤qÔŠÀ·%ìúª™ITëP([W€_†™{Q9Þ+D÷z¨×k˜Ì4ð+çÞÁÙ©Íý.Ö{’Þ|”Çæ‘“ 3uC„õ .°S Ø,  WhÑš¢LaYÖùD"ñÓL![8 K‰D«›ñÆõkøÂ3ì‡Fºò †^)bð÷â<þæîGð©Coã¹¹kÉc$YCãáÄA볨R½P¯Šƒ w(£€/Œêôýè ÐבÓ?–±ñ›Ÿ¾‰WîŽáwp¥â6RéÖÜýa)ªP ;ÞOñ{ä”x0) :ãö<_ƒÞ0×aX², õZžçáGOÜÁWor»z~Hº´Q^]™G&“ ft¦þlÛ~&á~àÂÁ…uPŒ‰”½Å²ñ…¶L677ÏçóyÿF‚h‚âD”Æ7/=À'k"—fÒÉp©è°Žƒš‘Ú·¢Ç˜Š5p(¿Žƒyýi6×O`µVÀ'^D:nòG ‚ÿê‰ÿŒ¸±cVí ¸S ¸áäè`aøü÷ôÕÅ ºQü®àcG¶pn®Œÿçüa|ëÆš–‰t&çïæ7ìd†¯°$j,©²À¦«®Ã®Yò^&×uQ«V`5mÌTñ+O]üp¬¿Gº´œÆ’{ 9Ŧ]º––¯Ùl¾vLs.3ÿwˆÔ>¬pð³qÆùóç¯9rdÍ0ŒiûéÊ)0“ÉàÞÊA¼zý:>{Žu~“¤“Ý~ ÷j—º6°é1ž*<À©ÂƒP²ïgp³xÏÎ\C:¬ØÿФp½Ëë ì•Q}ü¶èp|ýîíwáX2 ¿ò±;øô‰5üþëGqcÀj˜Èds{º§@?‰ïùËH·±}?=OÔÜoÖëHÇüâÙøÜÂ=D?ôðjÀ·E:3Òf)‹ÅïÓ¤ÌQõÇï«Jóo½Áýø}"Ü'Nœ8F:Mh¾P‰iS‘MááÒ^8i#¹³jböm ¼Þ’_C1•)cqü>ÆÓDŒn÷àÖx!{j@¯-»Ï€/ŒÖÍcA?°·ÄÓŠÏ4ñé“ÿ{gÉuç÷o}ò>çäšÑH#YÖa]öZv$c×±…ìb³’q²à¿b䟻dƒ66€aØì(öpd¯½^XP`y4ºV‰Òp.ÎÁ!g8<›W³›Ý]]Uùƒ¬æë×ïU½ª®&›œß(VÕ»ªºØ]ßßï÷^½ZÀ`GWæ²X]·àº ÃlèËÞ/øÝö;ÅbùµUض…ß9yÿîÙñÈàôûç´Œ÷&3x+÷ RiµA·²ÇþÆÂÌ믿þ_±-îä/ÿñ@nðÛ¼ðë‚<=z4Éd¾R«È‰{P¿šÊ/‘H`nÅÁ`j§InTZã‡j–Ø~,Í6´ùÿÍš%t' Ñź ½BÁü†ì˜DPðäUÊ(tp'¢8Ù_Ä—šƒ©»¸4“Aq£Çqk#ë‰ýG¹\Æz~¥råðÍg?Ä #ÓHQ‚eqøñ•‡QÍ÷ý ùEdT*7>>þ vß/ Pÿ`°Æ¬5ɾ," ˜?tèÐoå)YÚQ¢F"‹‰»9úëëëÔ‹·›ITñØÁüî™{èI[¸·šB.o£\*Âv܆y÷‰Ö²ííçQ,n ¡UðÒ‰;øÆg>Æ—NÜA¦´Û§x_P®¿¼:„OŠŸA*•–‰2øýÿê… ~‰Fáç#|ø?T Ö Àæ5Œ XZZš8yòä¿Ð4-{öÍD Ã@ÞêÄìì"ž:a#)ê hJ´cTüÝ2bíh¢1ÕPv¤ö¢FPí+—Ut•rñyûâ¤ú|ÓpñàÀ:¾|f\…íè˜ZJ P,¡Z­Øœ± ¶†r¹Œb±ˆõ|ËÂéÞeüþÃãøÓ'/⩃óèH´â¥^„Œî¤ðÓ©ç锿ñÏCæíËúý=r¹Üžššš<üÏ{þJ7+ÙûAù_.oèh4¼t”Ëe÷8`šæSµJ‚°\Q€t:Û L{Œh\¸«cv´Á«u .fMµÙ¼Ø+µÆËì«çŽÚ´èof*{ûÂ2Í.4 ê(ãÙ‘~÷Ì= w–1¿–Àüš‹bË‚ëºd Ä€eYÛƒúJe$õ2^™Æ?y_{ð&Ž÷äaè1ÿ–‰@î.¯\}Vú¸4úÆûŽã\ûÕ¯~õØþõÉ&ÿÒð? þ6@¯¢Æìó {' pggg_9qâÄ×!QDÙ ‚¾À£oð0~yyǦðüÃìÇ øQ¸qا?Båw„ ƒ¡•‚¿UvD¿ö7’è•‘ÜßÒ@6aãK§fðÒ©L­tàÝ©!¼59„ùu …Â:RÉ$É’É$u(R©T6—òlH›UüÖ‘95¸ˆF¦ñÔÁy$t𭝍T_\À¯æ¾€LG—´œßì~*oÿs]wõwÞùìÌÌÌ:¶=~›["Oì÷:`ÖÛ½>Ìà`ÛHp`yyùÛÃÃÃ5ÀûଷÏÒl Nc©ü0~ðÖ(¾ñÛEfP ¤­–{õZZxbª"¹Ýb T~¤:qFÜú"-ñö%•b~ïºí)âhÏ$~ïS“˜ZéÀÇ3ýéǵÅ‹€¡k0I˜¦‰d2y_uض ˲`YìjVuSÜvðäÉy<1¼ˆ‡–HôÛ ÇÞŸÊàõÙ§‘锋?KTï¿\.ÿ`ffÆ›¸ÁÓYY¨?(*Ѐß/M`_ÄGtn[{ùå—fÆãµ†#Q£°´8ϼˆó’…Ž´J]Ÿ2÷ǽHÑŠ÷QÅ>Téúб{ùªeUD_«¨«å P^©jàâl>žíÇ'³}XØŠè`š ˜‰‰Ì}2߀뺨V«¨V«5Á¯Ú›×#›¨âì@/àñ‹ÌlìòÙ~\ºgâ;—?‹jFÞïÈEŸß÷1*ccc/ŒÏBîý³¾‹ Ј2ÀåÒDÞ¿·hkkkßêëëûŸü…Ñ4Í7Àï‡1ú‡ñν3è{ï2þðó@"°{Æï¦U;;åãï)£!¬ØµìØ…¾V8âù…Ž ´«è«”iÞ0VMø=Ò¦gŽ.♣‹€ëby#‰ë¹\]ìÁøb/&–;P,nþLCƒn˜0 ¦¹¹´k¤Àu]8ŽS{Û¶áØVÍ»€¡ì^™þeœé_Æ‘Nš g¯0±¨ã•«OÂJƒRüùý ‘ÿårù'ãããsL’hÀ˜´@Áç úÚ©DXÏßDc@ùå—ÿÎ0Œ³µ†$Þ¿gM5óX ‡ã8X[œÀ?}t_{F‡é÷âã–Ó.¿îîP ÐgÔŒØ;#øå[(ú IA¢TÆ•—ˆ üjù.*¶›¹.L®tâÎZ&Wºpgµ{û¦kè€a˜Ðt£6E1¿Äi$x7lÇqj‹mÛ›Û¶ ǩ¶·$Msq cÇ»Wq¬;‘î†<ª4Õ/¿ö¶Ç©K“Lþc]¿~ýKŸ|òÉÈ=ÿ*šðþõ€×?.Àå/Pw«««Õßßÿ­ºÆÞ??>€ýñ†躎îÁ“xõ’ƒ„q_ùŒÓ»w4\ޏçàÙaáÝ TÅHšªÒ~³×ÍjaD‹Œ´èû 肼ýZ™VÿV».ÔmœZÁÙ¡•Zº sëiL­tbn=‹ùB …4æ‹Y,Ò¨:?ZMÛg ë:\hÐ4ÉÛv.D}±Žã@×¶ßuáºâ5¸èMW0Ü]Äp¶ˆáŽ e7p´{G;Ö Wìî fW€ŒÅ=÷$“jâ/Ú—uð”Ëå}òÉ'wÙf˜Åz>âš°ä²§„›ÖÒ™þT±ç*„üÚVÁ—–oÂÓoHnÂÛçʈóƒ.’ßÿÒÿ¤$ü ùQ„_ž^ø}òˆ=‰ë׿ |ÿò“˜ÓÎ*MV&ôÏ–ç#‹‹‹våʕ˨›Ûg·úˆ@(T À? À¦ñóÔ–\.WqJéU×~¡¿Gy,Ëúèµ×^ûKÔ ¼'ø~3þ_R¥/gT@ã ~0`m:`.ÝœššºxêÔ©/êº>Xķ @$øÍ™ln-wãæ5é)b KÛúí` ìEâðà%í2«Èõ%»Auâ|ŸŒŠ~ðé4)üAÝ>‡háâõ“ðïGŠeà7½øñ­ÏÁNa~o-zÀïÙ×u­+W®|3—Ë-a[ØE‚/›ê7Ò—2ŒÔ‡ÿÙ4~áÅ¿6/€ã8Èd2×ûûûÿ ˜èè >ÏÛf×ü¶*ét åAŒÞ²Ðe®ápŸ]µ­acŸ·Ç®p¬¦Eìë F|ß:MŠ~]Vooìž·Ï´/vÿEø -~Ÿ€ ÛSë•JEsglxxø+š¦ÕÎCuP _ù°hš†L¦¹Ê >¸ TËk8Ôã ͤMïk!¬øµÓñ¢ˆ}ÔsØa/¿.;Ñâñö}å'ü~âÍ%Ä"ü’«æ+ü>yÄžÆv€Û‹~|å Î->3;¨üÂ)Uñçóe£þggg¿5::úê…žÑì…?ücÊ‚Å`À?îÇwhhÿÚ˜€¥¥¥µÁÁA«££ãéºÆÞ·`ÓL btºãw7З*`°[Gl/#Û-;aWïcq¡Wn/Œ·µžª‡)»S¢¿ÕNÂ}`_Pþ ?@^ÿ>f¹œ›èÇo>‡yœA2•V®FüeYŠÅâû¿þõ¯¿ ±Ø{v¾‘÷_wå³E€÷óižø{íó‘öÅAæÔÔÔõ'NœH$Ç„ã„^õ‘À¨F€¦iHg:°d áÝ›)ÌåŠꬠ'«Q·@a„®ùöÃyõ…Ûk(´Â@ýZÙ6òö¥eÚIø¥'A¿)–OfÒøÉøY¼³ü ŒŒº×‹¿_žÈ°m{á­·Þú/¥Riõ¢ï ¾lÊ_OøeÞ(¢¢µp@øtl¥³³³£ÇÞ0ŒžÚAFùËflöéÓ4afúpsyÝJÅuôf«èÊøµ¹W-„ߣXï‘‘}œfŒ‘(ucôòëŠì¤è«”i¥ðû {P~Há¯å“ðßo”«À­ÿwü4þþÞó(˜Ç‘P˜Ø‡Åo´¿Jß¿ =kllìϦ§§ï¡qŽ~[4 ·_×l¨µE\€è@‘!à…ÿ=CÀÀæx§T*]>xðà‹š¦ÕzÞEÞ݉H ŒËÐ4 ©T vbÝć·€J©€¾ŒNÕ „ÚÑ&رûàËßôy„õÎCðºpõÂ;*úM{ûÌ«\¯¶~ŸD£ç/êûõÿ‹¼ÿ7¼m¶;@f °Âß07cuuµL&'~ Üüu`ˆöð9H0ƒª9„Ñ{½íbm}]©*ºîË®ñw-¬†m?\‘fÚˆYð늸Á5ÂS,¢Ï/o_X&š°å“ð,Å2p3—ÀϯŸÄÏ&ŸÁ´ýÌTW¨p?Ð(Ú2Ñ—mËÄ?—Ëýâí·ßþ[ˆûY\oðãj§êÃ1´" z @ôH ûÚ`€1777ßÝݽÖÝÝý™º*Šy+`ó}éLcçàÃÛî-–6ÊèL‰í74uœ#ø{U#ã8¶z±н°HØ:1zùÀŠ~³Þ¾ ‘„ŸˆÇ– ÀÅ™üìúƒøåô³˜wˆ$ü€ºø«¦{¬¯¯¿ÿÆo|Ïu]VØ=Ï_¶æŸ`ÅßE“_èV¢ð?ûD€h‚ €>===9<<œÍf³ÖTQ¼ÃÎ%]בN§á&‡psåÞèÆ¥)¥r ÃF&¥ÁPüÞÅm*„þ6´ì~¢aå¢Í Q뇭§àå× Ä)ú[í©8 Ñ:o_Òh+…qýˆ½€ëëeàÞ²×oÆÏ'ÃùÜSÈë#0“ÙX„ŸMSíï'þ7ßxãÿnYëå³a báw™E4ÿS_ì(ú#2øçÿ=A7¹% Á­S‚ôÄ‹/¾øõþþþTw`ÁL€~Û¢5ßV\X–…Âú:ºõ9Œtåð̱eœ>`c¸[C&µW"*Dø¾µÊ* Ž£Gl£ ˆ_ôßZo_RÉßÒ á'j8îæä=+E c [:Š©¸f‡ÒûüPñú½uXñ/—ËÓçÎû‹µµµUl ½·T ›^,ü ‚X‚µ/q¢p?oØx^ìS[K`†ùÒK/ýiww÷suû0F€,­YÊå2Jd‘ÃPz Ìã±Ãôwý]Ò‘_C1ß#7g„ ™¶¢ÔUüZ¡öýæ½}•2~ù$ü„ÇÝìÓ/”Ù| ——1‘?ˆéÒ!¸f'‰DÓ÷oU¯ß[û…þEíU*•…óçÏÿ·ååå6\d”ÑhXh¿7¶àõï'°m $ H$É_|ñ]]]×€‚Àoóe>T‹FòU«UlllÕú9ô§Öp¬¯€SƒêÚ|¬°+£!“Ô`F–).b»gFl¨¥BßLývü­vwDô¹ŒØ½}Ÿ“ðßw¸.P±J(U€õŠ;k˜\ëŽb?æÊƒ(¸½H$J¯çU?®¿×ïmû ½Ÿø[–•{çwþraa!‡mAg^°¸òlWAÛ|¿g°€êÑ–1·Œ€?êêêzªî$„ß/]‹Úl%Õj•J•J)Ði®¢Ã( +UF_¦‚áN =Ù*ºS²) cû-… ‰ÐªÓ®o7†«º‚n?Öv·^¯|p]Åÿ™Šào߿ɢ/-ç_FõÚŠóŲ¯i¾Î-þ–üÄÚ g°÷¾IÇÙþÏÙ`9ÖÊI¬W’X.¥‘+e±RNcÍêÄJµkÕ@77çb‰Qð=¢xý*i,–eåÞ}÷Ýÿ1???z!—u”¹tõQþñ@mhˆžûçÇðQ>À&€„®ëæ¿øÅÙßßÿ|ÃÉ(„ÿýæØ-C€Åu]T«Uضjµ Çq`Û6 ­Š¤V¦ÙÐ$t+öc‡ú¤!®ËnßÛ|ÿ‡¼ µÊ€jEÃ!š e¿Em7\ñwKrÎá¾³-)Ú’h!¿»¿Æ*L8Žè*v %' MÓ ëzm1 £åç©"ü|ZØ‘þP©Tfß~ûí¿ÎårËØpoïý[ØîòþY# m /ÀŽîgÅŸ5X€ÔÕÑuÝxá…þ`ppð 'C èÑB‚ bo!h>=Œ‡$þÅbqòüùóßÊçó먤ÏsVè=¯Ÿÿ³‘VøùA€M;õ (:À¿@”_[\×Õ&''¯vvv–º»»Ö8eVõäƒÒÈ ‚ØÛD~o;JÈVWW/¾ùæ›ß) ¿ÙsÙ¤?¼§$ö‘ÙéyDboH¶E‹vïÞ½)MÓf>¥1¯VÅûÇEüŸŒ‚ ˆöB&ú|^˜p?Ÿ';Λ¿ùÍo~dY–èí}ìd>2á÷›þwO2¯_$ú2#€íV¨- óÅbq|xxø!Ã02 '¡_?ê›É ‚ØTEŸßWy¯ßqœê­[·~òÞ{ïý?w³€'Öüâ'ü¢×û…ú÷Œ bð3Ê¢uÆÀêêj~zzúÃI&“ 'S(?ì‹…È ‚h ~‚/*f‚~äõ[–µ2::úÝk×®]F½8ÛÌšx/B "þ¢Gýö”à×ÿ/Šø‰~C$ R©X÷öö'øq@|‚ßìL‚dA¨¡"ô~åUBþaÖ<ù|þÚ¹sç¾³°°°ˆza…ýùÐ>oÈÄŸŸéoO*ƒýúÿùvê×uÝ;wîܬT*·O뺞n8áâVüIà ‚ v¿GúDûQ_ò¿{÷îkçÏŸÿi¹\® 1D$þ~á‘ñ°ç YWïõLyƒYëÌq¼ü†ã///¯LOO0444N§O|K¤ù€"d–_ñ'£€ ¢9TFô‹Ò‚žñgÓ‚„ÊåòÜ… ¾{õêÕ1wó~@}?½HÄÙÇ÷ø®‘ç/zÖߌ`EÝ}Yš´ÿŸ96{¨T*Õ[·n¹®{¯¿¿ÿ„(„3üˆCøÉH â~%L¨¿Y¯_´­"ü®ëÚ³³³o¾ùæ›?\YYYE½Ë /Íæòe]²'TžùLÜ¿/ë2ØhôüYÕÔ±}!4X\\\¼sçÎ…žžžd6›=*4¢¼°Ð@A‚ ˆð„à'J Û÷tÜb±8õÁü¯+W®ŒÚ¶ÍöɋĞ}KŸHüE"/¿€¬Ð»àç…þe^?»/;,˪NMMollÜèëë;œH$º¥(¢ ó_ê ‚ˆ‡0“ùå…þjµº~ëÖ­Ÿ¿ýöÛ»5«?ʨ7üÊD^6N@Eü÷Œ 3DÉ¢Þš=žh›?Gweeeíúõëïkš6ßÛÛ;b†°[h^¨ãŒ$AÜïø |˜}Uáw]·º°°ðî¹sç¾?===ån ²ß„?*>ßà·´àmËèСÏëºÞ0“`݇õyf $‚ˆŸfgøSig giiiôÒ¥K¯ÏÏÏ/yÕ¼<‡ùù~¼°³¯ó­rK…Û·¸²¢‰‚dÙóæ·¥DU¥ nÙd?üÛù%)H3 7d“±Ý ¼qR;ßt:zä‘Gž8räÈ Édr0ðCÓ‹„‚ v„(cüê±8ŽSY\\üàòåËon½¶ØöòEž?Û÷/ Ó³Þ»'êÞ6kð¯û-AóˆºB?ÐJ@4êßt?#@ 0¸º¼à7qp!æÜu]×µ³gÏ>622òÙŽŽŽª×¥™7Ar¢tQ©TrsssïýC±X,¡Q@E^µì±<ÙÈÙ›ÿ‚DŸˆæ uìºàmó‚ ˆ…ŸMkfAÑ„B:mxxxðÁ|zppðÓ4;B]”/"‚  ô°cªÇ©æóù+·oß~ïÆ7P/š"¯_ö— ¿—.{³_à‹„_æý‹Î­vi¸u ÍìvP@dˆ Y+ülšHüùî0k~nK×hº®kÇŽ;>22òxÿãa~‚ ˆp49xÚ)‹S ÇÇÇG×ÖÖØGù€z@&þl¾_øßË—MìÃ÷íËÆM¬âýóÛ¾4£L~Q¿ÇþXC@Ô% ((ëû÷{•°¬À3D  >:ºišÆ©S§NŸ¿±¸¸xõúõë—%!~Þó¶÷ÒùmÖÓç½~Ù@@™ðói¼÷¯ú8 GhïhÎ`ëóQ@ÞÀ µ¬K€7X#€Ÿ@H´fu ÈÆ°ŸÌ>†‡‡GFFìëë;ÙÑÑqÒ4Ížð—Ž ‚ˆŠã8¥B¡0™ÏçoÏÎÎ^Ÿœœ¼ë8Ž'’@£P²á~~[dˆÿ çþEa}‘ ê6P™ rß¿G\»-pç -üÀ?Qè_d°€Ì“'‹õF€p‚ÞC‡èéé9œÍff2™ƒ‰D¢Oé A¾Ø¶],•J³Åbq&ŸÏÏÌÏÏOÍÌÌÌ:Ž#ò„e") ï;‚t™ð‹Fü‹žð è·¨xýM‡þ=âèœVé PíP1 Ø1¢Ð¿WNcöEcøó`[´fv»î³g³ÙôÀÀÀ@www_6›íO§Óý©TªÓ0ŒÎD"‘5 £C×õ¤¦i¦¦iÒI‰‚ ö#®ëÚ®ëVǩڶ]´m»`YVѲ¬õr¹¼\(–×××—–——s+++y¯…N&ô@½¨{k/ßæÒüFû³^¿ì@Ù @?±ò%ôï§ Ú–~™AÀoóF€¬•î¾k@ÔMÀ~&ÒD†_Gtjd³ÙŒ,O¥>ADà+F¥R©´ªW©$x~ž>kÈBüªÂÆà…Ýo[&úaÄ_tÝ”ˆKH‚ŒvÛol€Ÿ‹Öž!àWWãÊóÇõ3D‘ÑXQùA×N% b¯"*Qº,¼Ïo;\¿¨ ìã#|¸ŸÝ—õùËÒ‚_äñ³Û²káw픈Sdª Ã@f°‚î7.€ï&(4¢qlÿ¹ý®›hŸ b¿!òôùm¿þ}>_æ1;\Ñ ¿ þ~‘±à7@UìÃxü±‹?б‰bˆ¢~ýõ*†AP]¿öEQv€ o°k6=(üÏ |~˜<‚ ˆvÂOœTŸÍw¸²|ß>ïé{å<1çEŸ÷öeÛ*â¯*𲈃HüE×#6á÷h• øü:(* ŠˆDÝÏH`Û`½}~[æùó Ð(ð|]Ñç㯇ߵòK#‚ØK¨„øYø°>Û†±AÀ 2ïZE˜Ek; ^˜¶T¼ý–Š?ÐZqQº C@!ˆb$ðõEªt°ù¢sçÓ=øè:ðP4€ ˆ½„ª×/Jc·y÷¶eÞ²Häý<ÿ q棢®Õú~‚FøEûM±"Å`·Ã~Æ‚¬LP¾, :OQº1ðóüƒö ‚ ö A"&~QœU>]&ð*F¨®¬¾ÊvØïšð{줰¨Š\c@ÅHðô CBäዺD/Š@ÇC¢OÄ~C$d¼·Ï{ö|ž·–…ûÙ6Š4?fÀ/ªfá?—HôUöce7DFEìx¡ì6›& Í«2o^¥_f°ýüºDÈòÈ8 ¢]‰VùEDÛ¢¢2¼€b#î¢sô;W‘gïçíËÒbg·…D%Ô-Š ðkUã@–& Ï‹ Ù¶è\eÇ}NY:Ÿ6 b'ð,Y^ì0i¼ÐókUï›q•raD>h-úœ²ý–ÓNâ¡àÓ›5ømY?¾(´/Š:ˆöeçî—Îç‡Í#‚ØIªû²È¿/hÒÂyTW ïËÒvŒvÕ>qUQ•Aåx!22D‚/šÈïœDe›¡]ÿÇAìâ*‘  ׯRÖo¬M•ò~ÛaÓv…½ a=ã°žv˜¨B˜ûGÔUÀ£jøAì%¡HÜù:a„ºo]5ÏWMßUöª¸D5 de‚ŒYïõ‡=^PšŒ½ú#bÿFì¢xÍ~‘¿°{1ò죤·ûEH¢ö“«†ÜƒOMñ¦~3ì—ÿ)AíK\"FDƒBð²úªÇ3†A5¯­Ùïbôù¢äGm“<|‚ ˆæ#*mDé{o6Ïq¿ Êç{mš56Âr¿þï‚h_âɸEY¥ü¾z$"bâÿ8‚ ˆv§âÚ #ྀD¦9ZuýèÿBÄ~¥ULš݇þAÜïxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAÄþçÿu<Êd'O-IEND®B`‚goodvibes-v0.7.2/data/icons/hicolor/symbolic/000077500000000000000000000000001414415210700211725ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/symbolic/apps/000077500000000000000000000000001414415210700221355ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/hicolor/symbolic/apps/io.gitlab.Goodvibes-symbolic.svg000066400000000000000000000127271414415210700302760ustar00rootroot00000000000000 image/svg+xml Hector Lahminewski goodvibes-v0.7.2/data/icons/meson.build000066400000000000000000000012361414415210700200560ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only themes = [ [ 'hicolor', [ [ '16x16', gv_icon_name + '.png' ], [ '22x22', gv_icon_name + '.png' ], [ '24x24', gv_icon_name + '.png' ], [ '32x32', gv_icon_name + '.png' ], [ '48x48', gv_icon_name + '.png' ], [ '256x256', gv_icon_name + '.png' ], [ '512x512', gv_icon_name + '.png' ], [ 'symbolic', gv_icon_name + '-symbolic.svg' ], ]] ] foreach i : themes theme = i[0] sizes = i[1] foreach i : sizes size = i[0] icon = i[1] install_data(join_paths(theme, size, 'apps', icon), install_dir : join_paths(datadir, 'icons', theme, size, 'apps') ) endforeach endforeach goodvibes-v0.7.2/data/icons/src/000077500000000000000000000000001414415210700165015ustar00rootroot00000000000000goodvibes-v0.7.2/data/icons/src/goodvibes-favicon.svg000066400000000000000000000135031414415210700226300ustar00rootroot00000000000000 image/svg+xml Hector Lahminewski goodvibes-v0.7.2/data/icons/src/goodvibes-large.svg000066400000000000000000001124751414415210700223050ustar00rootroot00000000000000 image/svg+xml Hector Lahminewski goodvibes-v0.7.2/data/icons/src/goodvibes-small.svg000066400000000000000000000467411414415210700223250ustar00rootroot00000000000000 image/svg+xml Hector Lahminewski goodvibes-v0.7.2/data/icons/src/goodvibes-symbolic.svg000066400000000000000000000127271414415210700230330ustar00rootroot00000000000000 image/svg+xml Hector Lahminewski goodvibes-v0.7.2/data/io.gitlab.Goodvibes.appdata.xml.in000066400000000000000000000047531414415210700231400ustar00rootroot00000000000000 io.gitlab.Goodvibes Goodvibes Play web radios CC0-1.0 GPL-3.0 Arnaud Rebillout elboulangero_AT_gmail.com

Goodvibes is a simple internet radio player for GNU/Linux.

It comes with every basic features you can expect from an audio player, such as multimedia keys, notifications, system sleep inhibition, and MPRIS2 support.

https://goodvibes.gitlab.io/appdata/screenshot-dark.png https://goodvibes.gitlab.io/appdata/screenshot-light.png https://goodvibes.gitlab.io/appdata/screenshot-preferences.png UserDocs https://gitlab.com/goodvibes/goodvibes https://gitlab.com/goodvibes/goodvibes/issues https://goodvibes.readthedocs.io https://hosted.weblate.org/projects/goodvibes io.gitlab.Goodvibes.desktop goodvibes goodvibes
goodvibes-v0.7.2/data/io.gitlab.Goodvibes.desktop.in000066400000000000000000000007341414415210700223730ustar00rootroot00000000000000[Desktop Entry] Name=Goodvibes GenericName=Radio Player Comment=Play web radios # TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! Keywords=Audio;Radio;Player; Categories=GTK;AudioVideo;Audio;Player; Type=Application # TRANSLATORS: Do NOT translate or transliterate this text (this is an icon file name)! Icon=io.gitlab.Goodvibes Exec=goodvibes %u DBusActivatable=true MimeType=audio/*; goodvibes-v0.7.2/data/io.gitlab.Goodvibes.gschema.xml.in000066400000000000000000000102671414415210700231320ustar00rootroot00000000000000 false Enable custom pipeline Whether the custom output pipeline is enabled '' Custom pipeline string Custom output pipeline description 100 Volume The playback volume (in percent) false Mute Whether playback is muted true Repeat Whether to repeat when next/prev reaches the end false Shuffle Whether to use shuffled playlist on next/prev false Autoplay Whether to start playing automatically on startup '' Current station uri The uri of the current station (-1, -1) Main window size The size of the main window (use -1 for no specific size) (-1, -1) Main window position The position of the main window (use -1 for no specific position) 'quit' Close action Action for close button 'default' Theme variant Prefer a different variant of the theme (if available) 'toggle' Middle click action Action for middle click on the status icon 'station' Scroll action Action for mouse scroll on the status icon false Enable the feature Whether to enable the feature false true true false false false goodvibes-v0.7.2/data/io.gitlab.Goodvibes.service.in000066400000000000000000000001051414415210700223520ustar00rootroot00000000000000[D-BUS Service] Name=@dbus_name@ Exec=@bindir@/@binary@ --background goodvibes-v0.7.2/data/meson.build000066400000000000000000000163561414415210700167540ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only # My understanding of meson (a beginner guide) # # 'configure step' and 'build step' # # *Configure files* are generated early when creating the build directory with # 'meson 'builddir', and afterward Meson automatically update these files if # the source changes. Therefore, my understanding is that there's no need to # declare a dependency to ensure configure files are up-to-date: they're always # up-to-date. # # However, custom_target() has a 'depend_files:' keyword for declaring # dependencies on files. It's unclear if we need to use it for configure files. # I'd say no need. # # *Build targets* are, on the other hand, part of a dependency chain, and the # 'depends:' keyword must be used to indicate a dependency. This is mandatory. # # 'source dir' and 'build dir' # # Meson has a clear concept of 'source dir' and 'build dir'. # # When you give a relative path to 'input:', meson assumes it's relative to # 'source dir'. Beware when you need to use configure files as inputs, as they # live in 'build dir'. # # References # # # # Credit for this file goes to . # ----------------------------------------------------- # # D-Bus Service File # # ----------------------------------------------------- # # services_dir = join_paths(datadir, 'dbus-1/services') service_conf = configuration_data() service_conf.set('bindir', bindir) service_conf.set('binary', gv_binary) service_conf.set('dbus_name', gv_application_id) configure_file( input: gv_application_id + '.service.in', output: gv_application_id + '.service', configuration: service_conf, install_dir: services_dir, ) # XXX It would be nice to have a tool to validate the service file. # ----------------------------------------------------- # # Desktop File # # ----------------------------------------------------- # # # XXX It would be nice to have desktop.in created with configure_file(), # so that we can do some variable substitution, like for the other data # files here. However, it means that the POTFILES should list the built # file, which lives in the build dir. Right now it doesn't work. # https://github.com/mesonbuild/meson/issues/1733 applications_dir = join_paths(datadir, 'applications') desktop_file = i18n.merge_file( input: gv_application_id + '.desktop.in', output: gv_application_id + '.desktop', po_dir: '../po', type: 'desktop', install: true, install_dir: applications_dir, ) desktop_utils = find_program('desktop-file-validate', required: false) if desktop_utils.found() test('Validate desktop file', desktop_utils, args: [ desktop_file ], ) endif # ----------------------------------------------------- # # Appstream File # # ----------------------------------------------------- # # appstream_dir = join_paths(datadir, 'metainfo') appstream_file = i18n.merge_file( input: gv_application_id + '.appdata.xml.in', output: gv_application_id + '.appdata.xml', po_dir: '../po', type: 'xml', install: true, install_dir: appstream_dir, ) appstream_util = find_program('appstream-util', required: false) if appstream_util.found() # Relaxed validation, our screenshots don't meet the expectations # (16:9 without padding is definitely not suitable). test('Validate appstream file', appstream_util, args: [ '--nonet', 'validate-relax', appstream_file ] ) endif # ----------------------------------------------------- # # GSettings Schemas # # ----------------------------------------------------- # # schemas_dir = join_paths(datadir, 'glib-2.0', 'schemas') schema_conf = configuration_data() schema_conf.set('id', gv_application_id) schema_conf.set('path', gv_application_path) schema_file = configure_file( input: gv_application_id + '.gschema.xml.in', output: gv_application_id + '.gschema.xml', configuration: schema_conf, install_dir: schemas_dir, ) #gv_settings = gnome.compile_schemas( # depend_files: schema_file #) # Generate enums.xml file. # XXX gnome.compile_schemas() can't generate the enum.xml file automatically, # like it was done with the autotools. We need to do it explicitly. # https://github.com/mesonbuild/meson/issues/1687 # XXX It's unfortunate that we have to list the headers here, # maybe that would be better done in src/ ? enum_headers = [ '../src/ui/gv-main-window.h', '../src/ui/gv-main-window-standalone.h', '../src/ui/gv-status-icon.h', ] generate_enums = gnome.mkenums(gv_application_id + '.enums.xml', sources: enum_headers, comments: '', fhead: '', vhead: ' <@type@ id="' + gv_application_id + '.@EnumName@">', vprod: ' ', vtail: ' ', ftail: '', install_header: true, install_dir: schemas_dir ) # Compile schemas for running the application in-tree # XXX We use a custom target, because gnome.compile_schemas() # would run in the source dir rather than the build dir, hence # missing the generated 'enums.xml' file. # https://github.com/mesonbuild/meson/issues/2219 compile_schemas = custom_target('glib-compile-schemas', output: 'gschemas.compiled', build_by_default: true, install: false, command: [ find_program('glib-compile-schemas'), meson.current_build_dir(), ], depends: [ generate_enums ], ) compile_schemas = find_program('glib-compile-schemas', required: false) if compile_schemas.found() test('Validate schema file', compile_schemas, args: [ '--strict', '--dry-run', meson.current_build_dir() ], # Requires meson >= 0.46, commenting for now # depends: [ generate_enums ], ) endif # ----------------------------------------------------- # # Manual Pages # # ----------------------------------------------------- # # XXX That would be nice to use install_man(), however right now # it's not possible to use it with generated files. So we have # to use a custom target and zip the files manually. # https://github.com/mesonbuild/meson/issues/1550 manuals_dir = join_paths(mandir, 'man1') manual_conf = configuration_data() manual_conf.set('package', gv_name_lowercase) manual_conf.set('Package', gv_name_camelcase) manual_conf.set('PACKAGE', gv_name_uppercase) manual_conf.set('version', gv_version) manual_conf.set('homepage', gv_homepage) manual_conf.set('author', gv_author_name) gzip = find_program('gzip', required: true) foreach exe: [ 'goodvibes', 'goodvibes-client' ] tmp_man_file = configure_file( input: exe + '.1.in', output: exe + '.1', configuration: manual_conf, ) custom_target('man-' + exe, command: [ gzip, '--keep', '--force', tmp_man_file ], output: exe + '.1.gz', install: true, install_dir: manuals_dir, ) endforeach # ------------------------------------------------------- # # Subdirectories # # ------------------------------------------------------- # subdir('icons') goodvibes-v0.7.2/docs/000077500000000000000000000000001414415210700146165ustar00rootroot00000000000000goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/000077500000000000000000000000001414415210700214715ustar00rootroot00000000000000goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/Makefile000066400000000000000000000011361414415210700231320ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = Goodvibes SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/_static/000077500000000000000000000000001414415210700231175ustar00rootroot00000000000000goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/_static/css/000077500000000000000000000000001414415210700237075ustar00rootroot00000000000000goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/_static/css/extra.css000066400000000000000000000016521414415210700255500ustar00rootroot00000000000000/* Navigation (colors derived from the Goodvibes logo) */ .wy-nav-top { background-color: #e08e00; } .wy-nav-top > a { font-size: 125%; } .wy-side-nav-search { background-color: #e08e00; } .wy-side-nav-search input[type="text"] { border-color: #e08e00; } .wy-side-nav-search > a { font-size: 125%; } .wy-menu-vertical a:active { background-color: #e08e00; } /* Links (colors derived from the Goodvibes logo) */ .rst-content a { box-shadow: inset 0 -3px 0 -1px #ffd204; color: unset; transition: box-shadow .15s ease-in-out; } .rst-content a:hover { box-shadow: inset 0 -30px 0 -1px #ffd204; } /* Code blocks (colors from the Solarized theme) */ .rst-content code.literal { background-color: #fdf6e3; border-color: transparent; color: #cb4b16; font-size: 90%; padding: 0px; } .rst-content div[class^='highlight'] { background-color: #fdf6e3; border: 1px solid #e08e00; } goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/changelog.rst000066400000000000000000000000551414415210700241520ustar00rootroot00000000000000Changelog ========= .. include:: ../../NEWS goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/conf.py000066400000000000000000000060251414415210700227730ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- Project information ----------------------------------------------------- project = 'Goodvibes' copyright = '2015-2020 Arnaud Rebillout' author = 'Arnaud Rebillout' # The short X.Y version version = '' # The full version, including alpha/beta/rc tags release = '' # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} html_theme_options = { 'display_version': False, 'prev_next_buttons_location': None } # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # These paths are either relative to html_static_path or fully qualified paths # (eg. https://...) html_css_files = [ 'https://goodvibes.gitlab.io/fonts/font-logos.css', 'css/extra.css', ] # Favicon html_favicon = 'images/favicon.ico' goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/credits.rst000066400000000000000000000067061414415210700236710ustar00rootroot00000000000000Credits ======= Goodvibes is a solo project: it's mostly me writing code and bugs. But there's more to it than just code. This page is an attempt to give due credits. Contributors ------------ Let me try to list here all the people who help with Goodvibes, who contribute a bit of their time and skills to the project. Artists ^^^^^^^ * `Hector Lahminèwskï `_ Translators ^^^^^^^^^^^ * Adolfo Jayme Barrientos - Catalan (ca) * Lukáš Linhart - Czech (cs) * Michal ÄŒihaÅ™ - Czech (cs) * Torben Grove - Danish (da) * Andreas Kleinert - German (de) * Vinz - German (de) * Daniel Bageac - English (United States) (en_US) * Adolfo Jayme Barrientos - Spanish (es) * Benages - Spanish (es) * Holman Calderón - Spanish (es) * Étienne Deparis - French (fr) * Jeannette L - French (fr) * Milo Ivir - Croatian (hr) * Notramo - (hu) * Reza Almanda - Indonesian (id) * Silvyaamalia - Indonesian (id) * Dallenog - Italian (it) * Luca De Filippo - Italian (it) * Prachi Joshi - Marathi (mr) * Allan Nordhøy - Norwegian BokmÃ¥l (nb_NO) * Heimen Stoffels - Dutch (nl) * Michal Biesiada - Polish (pl) * Fúlvio Alves - Portuguese (Brazil) (pt_BR) * Ssantos - Portuguese (pt) * Manuela Silva - Portuguese (Portugal) (pt_PT) * Ssantos - Portuguese (Portugal) (pt_PT) * ÐлекÑей Ð’Ñ‹Ñкубов - Russian (ru) * Иван - Russian (ru) * Andrej Shadura - Slovak (sk) * Akash Rao - Telugu (te) * OÄŸuz Ersen - Turkish (tr) Package maintainers ^^^^^^^^^^^^^^^^^^^ * Étienne Deparis (Archlinux) * Elías Alejandro Año Mendoza (Debian) * Robert-André Mauchin (Fedora) * Alexei Podvalsky (openSUSE) Technologies ------------ It's amazing the amount of technology involved in developing such a simple application as Goodvibes. Let's give some due credit here as well. First, there are a bunch of web services for everyday operation: * `GitLab `_ provides everything needed for development: code hosting, issue tracker and even continuous integration. * `Read the Docs `_ builds and hosts this documentation, and the service is free. * `Weblate `_ is a great platform for translation, and the service is free for FOSS projects. Many thanks to its author Michal ÄŒihaÅ™. Then, the code itself stands on top of a lot of free and open-source software, written by thousands of people. Goodvibes simply couldn't exist without such an ecosystem. It's impossible to credit everyone here, so let's just list the main libraries that Goodvibes depend on, and keep in mind that this is just the tip of the iceberg. * `FFmpeg `_ * `GLib `_ * `GStreamer `_ * `GTK `_ * `LibSoup `_ Finally, there's many other media players for Linux, and this is where all the inspiration comes from. And to this day I still rely on their code to write and improve Goodvibes codebase. * `Parole `_ * `Quod Libet `_ * `RadioTray `_ * `Rhythmbox `_ * `Totem `_ Talking about inspiration, the last word goes to *mgo* and *jcdr*, two fellow engineers who taught me a lot during my early years in the business. I hope you guys are doing great! goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/icons.rst000066400000000000000000000007401414415210700233370ustar00rootroot00000000000000.. |archlinux-logo| raw:: html .. |debian-logo| raw:: html .. |fedora-logo| raw:: html .. |flathub-logo| raw:: html .. |opensuse-logo| raw:: html .. |source-logo| raw:: html .. |ubuntu-logo| raw:: html .. |void-logo| raw:: html goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/images/000077500000000000000000000000001414415210700227365ustar00rootroot00000000000000goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/images/favicon.ico000066400000000000000000001032761414415210700250700ustar00rootroot00000000000000 hV ˆ ¾  ¨F00 ¨%î@@ (B–D(  ììHffffffffffHÃÿÿÿÿÿÿÿÿÿÿÿÿÄYÿÿõôÿÿÿÿðÌóÿÿÿZ}ÿëçÿÿ{‰ÿÿ~ÿÝØÿ¡²ÿ€~ÿÿËÉÿÿK[ÿ€~ÿÿÿÿÿÿFXÿ€~ÿψˆ®ÿ—¨ÿ€}ÿ“Mÿýkxÿÿ^ÿùîîõÿÿÿâ¼åÿÿÿ\ÌÿÿÿÿÿÿÿÿÿÿÿÿË\«ñÿÿЈwwwvR<ˆÕüÌ€3&r½úÝ’!U ;ÿÿ€ øøøø €ðÿÿá(0 bbh•™™™™™™™™™™™™™™•i ÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØ"¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ"ÿÿÿÿèæÿÿÿÿÿÿþͧ·òÿÿÿÿÿ$;ÿÿÿ —ÿÿÿÿÌ- ‰þÿÿÿ?<ÿÿÿ4)ÿÿÿÒ wÿÿÿ@<ÿÿÿe[ÿÿÿ@×ÿÿ@<ÿÿÿ÷“õÿÿèƒÿÿ@<ÿÿÿÿÿÿÿÿÿÍfÿÿ@<ÿÿÿÿÿÿÿÿÿä~ÿÿ@<ÿÿýÍÌÌÌèÿÿ5Îÿÿ@<ÿÿÜsÿÿÅdÿÿÿ@;ÿÿÝtÿÿÿ¹oúÿÿÿ?ÿÿÿÞÝÝÝòÿÿÿÿ÷²Žžáÿÿÿÿÿ'ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈ(ãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿá(rÇýÿÿÿÿ讪ªªªªªª¤xU¢ïÿÿô³f<ˆÕÿÿüÌ€3%r½úÿÿß”HU¡êÿÿ8ƒ8ÿÿÿÿÿÿÀ€€€ààðððàà€€€ÀøÿÿÿðÿþÿÿÃ( @ ØØ€½ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ̾CïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðE"òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó$›ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžáÿÿÿÿÿÖÓþÿÿÿÿÿÿÿÿó®¶÷ÿÿÿÿÿÿåøÿÿÿýUIúÿÿÿÿÿû —ÿÿÿÿÿýúÿÿÿ°¢ÿÿÿÿúIeÿÿÿÿÿúÿÿÿ˜ˆÿÿÿÿu–ÿÿÿÿúÿÿÿá Øÿÿÿå÷ÿÿÿúÿÿÿÿÐ_\Èÿÿÿÿ–·ÿÿÿúÿÿÿÿÿÿÿÿÿÿÿÿnÿÿÿúÿÿÿÿÿÿÿÿÿÿÿÿk‹ÿÿÿúÿÿÿÿÿÿÿÿÿÿÿÿ¯ÿÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÜ òÿÿÿúÿÿÿ:¤ÿÿÿ`ÿÿÿÿúÿÿÿ'˜ÿÿÿò1Iûÿÿÿÿùÿÿÿ(™ÿÿÿÿò`uúÿÿÿÿþæÿÿÿåÝÝÝÝÝõÿÿÿÿÿÿÜŒln•åÿÿÿÿÿÿèŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥ ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø,Fóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Sˆãÿÿÿÿÿÿÿ÷ÝÝÝÝÝÝÝÝÝÝÝÝГ#+w»ûÿÿÿÿÝ™MU¢ïÿÿÿÿô³f<ˆÕÿÿÿÿýÌ€3"f³ùÿÿÿÿè¢U U êÿÿÿÿõ†8„Ïÿÿ¾d-ÿÿÿÿÿÿÿÿÿÿÿÿàÀ€€€€ƒÀƒÀÿÁ€ÿÁ€ÿá€ÿá€ÿá€ÿá€ÿÁ€ÿÁƒàƒà€€€Ààü?ÿÿ€ÿÿðÿÿÿÿàÿÿüÿÿÿÇ(0` $ÅÅ$333333333333333333333333333333%3«ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ­4†þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþŠ‘ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–NÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/^ÿÿÿÿÿÿÿÿ먦æÿÿÿÿÿÿÿÿÿÿÿÿÿû¼vXF]€Ìþÿÿÿÿÿÿÿÿÿÿduÿÿÿÿÿÿÿ¨ ™ÿÿÿÿÿÿÿÿÿÿü›.¶ÿÿÿÿÿÿÿÿÿ{yÿÿÿÿÿÿÒÂÿÿÿÿÿÿÿÿïFkûÿÿÿÿÿÿÿ€yÿÿÿÿÿÿiUÿÿÿÿÿÿÿô0Vþÿÿÿÿÿÿ€yÿÿÿÿÿÿQ<ÿÿÿÿÿÿÿWˆÿÿÿÿÿÿ€yÿÿÿÿÿÿ~jÿÿÿÿÿÿ¾ æÿÿÿÿÿ€yÿÿÿÿÿÿïæÿÿÿÿÿÿDvÿÿÿÿÿ€yÿÿÿÿÿÿÿßG=Öÿÿÿÿÿÿí%ÿÿÿÿÿ€yÿÿÿÿÿÿÿÿÿôóÿÿÿÿÿÿÿÿ¸éÿÿÿÿ€yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÐÿÿÿÿ€yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÊÿÿÿÿ€yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²äÿÿÿÿ€yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâýÿÿÿÿ€yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3eÿÿÿÿÿ€yÿÿÿÿÿ틈ˆˆˆˆˆˆüÿÿÿÿ£Óÿÿÿÿÿ€yÿÿÿÿÿ¹åÿÿÿÿý6fÿÿÿÿÿÿ€yÿÿÿÿÿ¹åÿÿÿÿÿå2õÿÿÿÿÿÿ€yÿÿÿÿÿ¹åÿÿÿÿÿÿÖ$@ëÿÿÿÿÿÿÿ€vÿÿÿÿÿ»çÿÿÿÿÿÿÿíi…úÿÿÿÿÿÿÿÿ}dÿÿÿÿÿù½»»»»»»»ÃÿÿÿÿÿÿÿÿÿÿߊD(+Ošìÿÿÿÿÿÿÿÿÿÿg3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8Ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿbªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«¤ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤OÇþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÉOiµöÿÿÿÿÿÿÿÿÿò¬eUUUUUUUUUUUUUUUT> D‘âÿÿÿÿÿÿÿÿüÌ€3+w»ûÿÿÿÿÿÿÿÿÞ™MU¢ïÿÿÿÿÿÿÿÿõ³f<ˆÕÿÿÿÿÿÿÿÿÿÕˆ<"f³ùÿÿÿÿÿÿÿÿê¢UU¡êÿÿÿÿÿÿÿÿ÷¸k9…Ðÿÿÿÿÿÿÿÿù*h´öÿÿÿÿÿ3 L—âÿÔ( ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøààÀÀ€€€à€<?ø€~ü€~ÿþ€~ÿþ€<ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿþ€ÿ€ÿþ€ÿ€ü€ÿ€?ø€ÿ€à€€€ÀÀàøþÿÀ?ÿÿÿüÿÿÿÿ€ÿÿÿðÿÿÿÿÿÿÿÿàÿÿÿüÿÿÿÿÀÿÿÿÿøÿÿÿÿÿŸ(@€ @°°+m‰™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™Šn-Y×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚ]ÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâ ÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑ €ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöfÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿm±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹Öÿÿÿÿÿÿÿÿÿÿý¼ƒ³úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍ~?$ *K’àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞîÿÿÿÿÿÿÿÿÿÞ4'Ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·/PÖÿÿÿÿÿÿÿÿÿÿÿÿ÷õÿÿÿÿÿÿÿÿî!áÿÿÿÿÿÿÿÿÿÿÿÿðM|ýÿÿÿÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿ{_ÿÿÿÿÿÿÿÿÿÿÿÛ(Oôÿÿÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿ,ÿÿÿÿÿÿÿÿÿÿë!Oýÿÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿ÷ÿÿÿÿÿÿÿÿû?|ÿÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿ0ÿÿÿÿÿÿÿÿÿšÖÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿƒgÿÿÿÿÿÿÿÿõOÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿó)çÿÿÿÿÿÿÿÿ¡àÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿæD4ÚÿÿÿÿÿÿÿÿÿR’ÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿÿÿΖ“Åþÿÿÿÿÿÿÿÿÿý Jÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿé+ÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉ ÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿá$ÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú?ÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=~ÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÍÿÿÿÿÿÿþõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿå /þÿÿÿÿÿÿþõÿÿÿÿÿÿÿu[ÿÿÿÿÿÿÿw·ÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿN2ÿÿÿÿÿÿÿïMÿÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿN2ÿÿÿÿÿÿÿÿÏ 'ïÿÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿN2ÿÿÿÿÿÿÿÿÿ² !ÛÿÿÿÿÿÿÿÿÿþõÿÿÿÿÿÿÿN2ÿÿÿÿÿÿÿÿÿÿÐ;éÿÿÿÿÿÿÿÿÿÿþñÿÿÿÿÿÿÿQ5ÿÿÿÿÿÿÿÿÿÿÿïw šûÿÿÿÿÿÿÿÿÿÿÿùÞÿÿÿÿÿÿÿÚªªªªªªªªªªªÑÿÿÿÿÿÿÿÿÿÿÿÿÿå> R¡õÿÿÿÿÿÿÿÿÿÿÿÿÿâ¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúãÈÐèþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü$šÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›ãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâ.îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò2,ÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝ+ €òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó€ R¢ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞ»»»»»»»»»»»»»»»»»»»»»»»»­P 3€ÌþÿÿÿÿÿÿÿÿÿÿÿÿÝ‘D^ªóÿÿÿÿÿÿÿÿÿÿÿÿô³f D‘âÿÿÿÿÿÿÿÿÿÿÿÿýÌ€3+w»ûÿÿÿÿÿÿÿÿÿÿÿÿà™U U¢ïÿÿÿÿÿÿÿÿÿÿÿÿö»o<ˆÕÿÿÿÿÿÿÿÿÿÿÿÿÿÕˆD"f³ùÿÿÿÿÿÿÿÿÿÿÿÿëª^ M™æÿÿÿÿÿÿÿÿÿÿÿÿüÄw*8„Ïÿÿÿÿÿÿÿÿÿÿÿÿÿ؈g³õÿÿÿÿÿÿÿÿÿÿÿ  K—âÿÿÿÿÿÿÿÿ¥/zÆüÿÿÿÿ]^©é¾ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿü?øðààÀÀÀÀÀÀþÀàÿÀð?ÿÀÀðÿàÀøÿÿðÀðÿÿðÀðÿÿøÀàÿÿøÀÀÿÿüÀÿÿüÀÿÿüÀÿÿüÀÿÿüÀÿÿüÀÿÿüÀÿÿüÀÿÿüÀÿÿøÀÿÿøÀüÿÿðÀüÿàÀü?ÿÀÀüÿ€ÀüþÀðÀÀÀààðøü?ÿÿÿàÿÿÿÿÿþÿÿÿÿÿÀÿÿÿÿÿü?ÿÿÿÿÿ€ÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿàÿÿÿÿÿü?ÿÿÿÿÿÀ?ÿÿÿÿÿø?ÿÿÿÿÿÿ€?ÿÿÿÿÿÿð?ÿÿÿÿÿÿÿÿgoodvibes-v0.7.2/docs/goodvibes.readthedocs.io/images/goodvibes.png000066400000000000000000000533161414415210700254350ustar00rootroot00000000000000‰PNG  IHDRÀÀRÜlsBIT|dˆ pHYs  šœtEXtSoftwarewww.inkscape.org›î<tEXtAuthorHector Lahminewskiì“l• IDATxœì½y˜$Gy'ü‹<ê®îê»§»çÍŒ44è¶@Æ’ ÒHøÀò®í/Æûù°×þl°e{½î5kx9Œü­Ë2¬0£A ‘øH£[#iî«{¦é»»îÊÊŒý#+²"##²ªº{t@¿OWgfDddDäï}ã}ß8X£5Z£5Z£5Z£5Z£5Z£'"¯uÖhDúÝßýÝ>]×ßB)MPJG>õ©O}½ÏZc€5z=ùƒ?øƒ½ö¸À›(¥‹¤”²mû?}úÓŸ~yÕ¼Ú®Ñ5CøÀ’n† ú}€Òº Îg\þÉO~rl5Ëa¬ffk´Fat×]wm¢”ÞN)ýI1ÀtBˆw-œwSJïð;«Y¦µ`.ÝyçúÖ­[/p¥ôv{)¥æB¤½ê|1ŸÏ÷|îsŸ³V«Œk=À­*Ýu×]pU››¼@“¾™óöd2y=€ï­Vy×`VLwÝu×MÓ˜”¿€ @ b‘Ÿ]SJA¦iû°ÆkôZÒðð°aYÖµ„Û)¥?C)Ý ¸ äÀWˈßq_:MÓ×`þÃժ˚ °FMÑððp¿ã8ûjFì-R*]]¼næœR Û¶½xÇq<ÆÒ4 º®ƒR:´ZÞ µ`TDþìÏþl/!äv·Û¶ý&³©./áÅs^ía?BǦi ”n°Æk´ºôWõWÉ|> ¥t!dŸã8ëdé–kÈÊÀΫ9"øUù˜¦9µZu^S~ÌéCúÐfÛ¶ÙìOˆ¶ª¾¨âÀWÅ‘Iyß(ÀU4M{éSŸúÔe-WTAk=ÀÝÿýú‘#G®p;¥tŸã8»–c¼ªTž0ÕFvUž5UÇKO®ëEJéo®f{¬õ?4<<ÜiÆÛ(¥wx¥´“Å­†!+»mÛu†?—…‰ÌÀ޵ó§Çù¯ý×}`Y  5ø¥»ï¾{KµZ½£fÄÞÀ\ÆÈ«ôœ×ÛÐeú|3yò½ïí!„ؔ҄Çy䓟üä3Ëjˆ´Æ?"4<ò‘좔Þ^›zp=å&—ËWm€ÞH‡åÉéðì¼H)ý!€ý¶mõãÿøÈò[cå´Æ¯sN$“ÉëǹƒRúNBÈ °2 øÝ¤ø{›}®üó”Òïx°P(|ëž{îYj¡ .(­1Àëî¾ûîM¦iþ4€;ǹ@”Å­Ôxea2iϧi6Ož8Ð;Žã¼`Æ~˲üØÇ>öGzÓkLk ð: ûï¿_½œRʼ6oZMã•—æ*¿¼ ø* óÀÓçóÓ4íÁJ¥òàÇ>ö±ñæjÿÚÒ¼Fô‰O|¢“Rz!„M#îXÐÅk¨y)ßHÂó̯2}žRzÀ#„ý‹‹‹ßsÏ=å–â5¦5x铟üäêFIçÍ3Z®jÃÎeÆk3ÀÃpÀ· !j^›?ô¡½ÒZ ¼þh. ýÃ?üClaaáBÈÍ~–Rºƒ_‰š£RmØ9#¾h湌8I?ã8Îc„ý•JåëùÈG.¸oþÕ¤5Xeúô§?Ýã8έš¦y¾ù ­Ï³óVòáÃ$úü!Jé~BÈw<6<<\m¶þo4Zc€U ¿ù›¿Ùå8Îíîp=jíºÀÞªñÚHµ©])¥?$„ìwçáááѪÿ†¦5X}ö³ŸMT«Õ›¨;»À ‹[MCVÔåe†j«ª àIyÏ7ï8ÎCÃÃùÕþ‘¤5h’î¹çžÍº®ßw‹[(¥Q>~5Tþ|%Æ+;$½C)}^Ó´ýš¦=x×]w=GQsÉ ý¸1«oÃÿý÷ë333>ß<Ð<ÐÃâTÒœ_<ÒL²0^Ÿ§”>¦iÚƒ”Ò¯ÿéŸþéD£zÿ¸Ñ*´R/A÷Þ{og¥R¹‰Â\•@8°Åë傞O/S{Ty ~yBNišö¥t©Tzxxx¸ÒtKüÒ¬¨Ÿþô§·†q!d!ÄóÍ+¾`Q·KÛHÚ3ªÍ›’²ŸòïôGt¸™z¯‘KoÄ%‘+ü'>ñ‰X"‘¸AÓ´›ÇùYBˆÒ7¶TP¼–mä$æ× ØU$̨œð¨mÛ•J¥¯ /4Yý5èдRF‰~ÿ©O}ª7¿]Ó´}Žã¼@Ú»A9£åêób.ùÅûX™ø°ÚŽh‡(¥RJ¿ñôÓOÿðË_þ2Ó~ì Ù•Ðë±hðªtî¹çž]‘Hd_m@êzD4e 5®ùs^wg¤¸¯p¤qõjiŠ„8Žóã8_ùÃ?üó o\£–éõÂP!‹çÃèððpb``à§ǹ]×uo¿y :ž©9â}²0Ùtƒ0…[ ;â8η)¥äóùo g›ÈNl›µ¡z­T V¯Lû‰O|bs,»Ù0ŒÛkóm<ß¼ ôaáaça£­­zw†sÇy²Àþýû÷?ÿØcQAüãêW½ž¯V°\Àûî»õÖ[õ[n¹åÚd2y€› !{%kLƒ™sÓx ¡ÛŠÄ‹—å+I“§”>`.—Û?<<<ÉG³Û×Òb„òE¯zov! ì¥Å1@ò'ÒÕ××÷VÓ4÷éº~;€Lm—`÷ÆàD.åu#5G4>EjäÑ ¹÷tµZ}ȶíý?üðã=öX~p«€ÞÌÀÝÁ‘!#Yݦ.—K‚Âôõ†€€}ìc›“Éä횦í#„¼…bÞöØn à‹ÒhN¿çÓñyJFW¥0 «=£jÛöÓ¶mcnnnÿG?úÑ#ìÜ-üËmô¢ß¨ #Ÿ Ç…Q!L•vUh5@%½HHÐ÷¼ç=ñË.»ìzÓ4o×4í„ ^ e€çÃDæhÖ •Q#é¯`Žß+—Ë;vì÷Þ{ï"»U8†Ñr€þ£ ÉÔ^ÞR ŽU}ðjåÃ^%ñÉÝwß=‹Ån5Mó6MÓ~Š’ð"[½Êµ 47f9a‚Á[¢”q¸®Av/ÛÞ[åB {~Ø=ªg­ô«U&..wípÎqœ ÇqŽRJ-..¾ô…/|a–O*ÞÊýÄðÑJ@|)3lÚ´Iûßøë‰ÄÛMÓ|€=¢Ô^ޤ—juµ"lpk9à[ #^¨:^è0.Ρ”¾ì8Îcårù¡Gyä±'NðÈ÷«Ê+aèwîÅ ´µµýšaÿ…²QàF nô"µÂâIYÆa÷ªŒì×KØkõ|I;ŽQJ¿X.—?÷ÙÏ~v„E!œ–ÅËeQ¿HÿáááMíííÔuý]¢¢º"î0àe,‰W¥i†–ËÒÊT¡×À+ ãV’ß* ‘jµZý?årùc÷Þ{/ïHàAÖS4MËaüL4{Òÿ—ù—{÷îý}Ó4?@‰‰@oä½iÅ­)†Ë^¦¾š½;o$ùßaªº½Úa2ÕÒq‡Rúʼn‰‰>ðÀÌV ’¸cSÔ ÈTŸôÿË¿üˉÄßi𶍫.a*LX/ÐìµH;Xñzµz…•Þ³¦\:U.—ïÞ{ï}u ;Ü‘±M1‚ÞL"øÁ¯q?@Ûµk—ù|à/c±Ø§5MËA‰.²€/ùJT1LÅüùC¼va ;|Œ^LÐ(~I‡ÉvÄp繩©©Ÿà&áï øž a/Ð Àÿ|`ݺuë"„ì䥺̽ÉÎùpßCj׎ã`vvÅb¥bU«„juùÛÒHx|»ø\žŽRêÝâK¢¯¬Ý%Ïñ¥óÀ?B ”Æ;ÁgSB/N|á>Jž'IH%íRo«`î¦i€ê ˜f©T ñx¼e†T!~zaaáŽ/~ñ‹'k£¨3@Së%1€ ü¼ï}ïܾ}û75M»H%ùeÓD&˜ŸŸÇèÉçáÇ‘Žæ‘Œ”°}¢/c#×IéhØ P±–4/ ‡ ey„žS‚ ª#ß÷ê‹ãîñ•I¨$‹ï ˯vpçðùópBy) ”ËÏ4\—}Ŧ(”)æ :NÏF0¶G®šÀ‚•–ÞŒžu¡ëum¼Qo Ú333·|å+_9?4Õ„AKôöxzÿ{ÞóžŽK.¹ä?ìÔu]*ùU†/X–…Ã)Æž9ì»ÖDoç2fe4î$`üÀj暓ՀªÎ#ð @-‚V“id²£‚ ”——ÖþüùªóQ—'г00Sàð8Á7gp&Û£óRtõ­÷—;ç#ŒNMMýÔ×¾öµ1´È þ7½éM‘_üÅ_üw]×odjJê‹À/•Jxå¹ï 7~¿u»†u݆ÿiÍ’’Ÿ¹È@ÖxªtP½àå0‚?ŒR!Ž¿_k ¸«ÎáùÑð3æ ÔQ`šººÄå%ÐNøê˽($v£wý®|A†àíJ)lÛ~åèÑ£oýþ÷¿Ÿ…ŸBU!ìxÕÇ÷ûð‡?üÓ4›¿Jçg×”R¼òâ÷ÑN_Æïý‚‰ŽtÎ'¾d¡€ç)ü2°ÓñÀrç>€ñ —¨1>Æ eˆÀ•õ0œ Âòᙄ»O’§”‘$ôü¨Ž¿j‘Ît){‘ªÕê¿}þóŸÿ/ðƒŸ1רu c^úë´?þã?~[GGÇ¿BHí«Ý ¹––p䙯à¿þl»·E ?†ÙO,”rôŒI€öÛ¿ýÛ =IÑEé/S{^yî»Ø·çÞ~m´AEI°­R˜á•ý“þŠs©þîInœ¿\Íèñ€OwV¤)Z&M,u*òÙ’¥’‰TÄB"RE2RE[´‚¸i£/]B[´*—ò¡å‘õ&µø†yðñµôMô`;ÀÿüV'æ3oC2•ñåÃ÷¶m²Ùì­_þò—¿ºôg=A­0.É|¢þ¯õõõýIqò}ô•'ð¶Ý§ðöëbþ,•@'¾CÓĪ ºRãvá™+…äÜMCj甀p™xçµgÕŽ|1®^.–7—¯âH(%µ¼@0¾hâ©ÓŸJ P1Q¬(U Th–cЉ7£z)ÁÔ,$ ‰ˆ…t׬ŸÁÖî\MxËʃzj£ õÖ­×Ï=Hî­¥# ð¬'/¿pÒ5à¿ß:‡?èaä´}ˆÅ“^½ÜlܽšØ1‘Hü€ÇábX(hdý/ýõ÷¿ÿýÛŸ&„hº®C5â gÏÁ%™Çñ®·'%ÀT1 ½ôQ $ Gqb#3鈓õ²A”ê²°Õ0flIÃãÇ2xñ\ …Êvy»Í7x´RršS@T+ -Rƶ®%¼uë8Ö¥ {%IoÐ’AÌ·]ª:À¿¶‘M·ûð'ûÖñâââM_ýêW Þ l±àõ$ éééù5šv·ìuîËå²Àâãx×/%ëYÈ´­f˜@¥ÒdT ðKn>Nè økR;ö Üy Gp|‚PÙPP‰ä%¬ØXðäñ6|ïXfóqäívèzýu­"öÔ„™–B)LWÉqO¢ÍÌaïÀ4nÞ:ŽTÔª·ßø¤xMÊ3[‘@èAÜ{ý½šÅ? ø·Nàƒ}[oO+áñH)E*•z?€§k¹³§ûž&öžÞÀ¸âŠ+âï|ç;hšÖ)êþ"C<ûýÁßþ¾ŽhDóg-a¹IЊ$—üT'JŸ¤ã(Çõ0ˆa2ÉͧUytBuk÷8½dâ‹Oö`d&‰œ•1|ßáxM¨ZµÔÑ[ÂÏï:ƒíÝ‹Íõ¬^«Ü ÀÓ&>äÍèèßVËÂ/ýkç¥ãÇo{â‰'àJÞðõ¢…¬ 7ß|óOB:Ùx½’·Â~¿~[ÑH$x¿3 ‘º³L‹¸ÆÛâÝ”—Ì^:¾wàãj»LÅžDèxÉì ‚G_¾bÏA & |éÉ^œ˜J¢‚Œ«j¾N6®4 etc¬ÒÿýLÚÍyܶ}× Mû{7±`R~•{¸f³…oy ÖF†¿¡˜ 644ôsþ±–»?Ö)/Æ}à@b±Øm2C—›˲`–â'ö$ÀÐ^ï)¿Û°v-ºÿÂÜ’a®Kˆyøãüž*ɽ¢Û’wŠnHÑ­ê»_åŠäŽ,_–Wí8“5p÷ƒƒøŸnÃáé!TµÎ@ïúz"[OcÖ^û^¾Ãß½Gg2þ>·(üõ#ˆpO‹ô{oE~ü¨?ìhšæí܃EAï³øbšæOó™²sž|þË1ÀË+¬“É„úã$&€/)/Á%&€ß›ÃE°„œïÙžô’ëó¡Î> ”¢J þíÉ<~4ƒ2:\‰¯¨N3ä8Ž;…¼T‚ã8°mÛû†MÓÀœÑh±X, 9›%B¨‘Ƭ“ÆgžmÇPr ¿qåa´E­æ{/Uë!Fépy×—.G4õ fvÔ4íúÁÁÁÈØØX~Œp@dO ù•_ù• úYfÌûë>Žã cŽ`¨/UϦ)&yÕÊ(øÒtžÞâ“2¯¶„0¶\cÖ;2.¤/ŒÄñ¯?èÆB©DF#¥Ùl Èf³( (—Ëõ–b¾pdçìF"Äãq$“I´··#N·ìY²õvœ.¤ñµã­›FpÛE#îó<uðó/OP‡¸“–èW¯^ÂÿûÍºÊ jÓ—_~ùcccPg¯d²€ }}}oâ$y;uâ%üÖm&'^¹nMƾ(²<‰'ÜDù"Äð~~1 ìŒà {9ø1íPüýw{ðÂHl- ÒÖªÕ*fff0==¥¥%ßVŒüÑ0 D"˜¦ ]×aš&ªÕ*lÛ†ã8°, ¶m£6ƒ¹\ù|“““Ð4 Éd™L]]]ˆF›3À5MCEëÅÃ#m8x¾ ¿sÍA¤LËkH?Æù^€‹÷Ú¼µ^ ¶¥Çpž^é ç{‚x<¾ÀSp…;¿ŒRê%`šæÅ¢¾/fnçŽc×ÖxýNèe€_¦Þ'’+Üëh–3Ï Áó@ Q[ü®S™1ËŽr&˜Íiøøƒ½˜ÎwBÓÙ¤ÀÆ4??ÉÉIÌÎ΄P{{;º»»ÑÖÖ†d2‰t:HD7c˲ÏçQ(Ïç177‡l6 Û¶Q(P(0>>Žt:®®.tv6¶K!€Çxe3þâ±8Þ}ùa\Ò3+‘þÞFX.½c×<þ×ó£hïvwÔ”¨AÛPGC@ ¢iÚFѰàɲ,lî+¤­~»„ ê‚ð«ÁÒ_!ÉdÔU#J@¥½ÿy•ȧ6ù™àÀñîû~7ŠN4½9…gjj £££( n½a@?º»»}`×uÝsªˆÙo†a ½½ííí^œã8˜ŸŸ÷®\.#ŸÏ#ŸÏc||ýýýèííõT¤°å­وϿ”À5ý§pç%'j © àW‡Z“þŒvô;HUG@éú@\M€_T˜¨¨y纮q*:vöÞwSuµ‡?ôÀ *J«P{dŸËë=ì^Qgç6ê ša‚o>—Ä7žï…64ãÜ9þ="ÈØ%øòmxô•.T‘jXb±ˆãÇcaÁýà#!}}}¸ä’KÐÑÑQ«Ž¿kgé€ú$0&K#Ú ai˜êsÑEall ccc¨V«ÇÜÜ6mÚ„ÎÎNß½<¹=M'N—/Åÿz"‚ß½ú9˜S¿%Œà/Oºrh÷g‘L¦|u«a˜óÒø‰Wx¥4É*×+ƒ³†é:p‰äç &à;‘PâÕF!<8è…À¹Ô­âÑñ«DÍ0@‚Ï|«/ŽvÁ©oz-%ÇqpæÌœ;w®ÖT6lÀÅ_ŒD"Pk!`£òâÈg«iØ¢&ža,¾ëh4ŠmÛ¶aãÆÅØØ,ËÂñãÇÑÕÕ…­[·Â4MÈÈe‚6,Tv㯞0ðû×>…˜nK0Î,ÓØ3TÁŸM$‚‚Â$P¨ Ä—„(o‹3 ‘2@",1_ëz6’óÜ}– _p_V~°S"¤•øJA~ßlL°y-Í0pïwÚñÂh( ÷¢‹E>|¹\”R¤Ói\qÅèïï÷){𦅦q†aHÕ–FÌSF,Ü0 lܸ}}}8uêæçç±°°€—_~Û·oG&“‘Þz<Šy>yøàµ`°ž`Õ î`: ªúÄá~¨ „M-eÄgX­VÑÕæøÁ/~¾èx[Ö‘!ø§­„¨pâµ)»æ‚ŠanâhTƒ©3åÁ#øÉ]áLðå¦ðì™Î†àŸššÂñãÇ=Õe×®]¸è¢‹ ëº´õjP%ø[MÓ̳ø4±X —\r fffpêÔ)T«U9rëׯdž ¤êà2P…ìÁ=ÏØø½«Ÿñ&ea¹ÔfQ–×% û{åj”©L:”Ëe ¶“ økÇ…¬ßú«ã˜³lNê®Ú›%ê;4LmQüÒøµÛzw3«ëÖrNùÎó |÷¥N8W{NŸ>³gϺ=i"«®º ]]]nYšzͤaé¥]næ~=vìòù<Î;‡b±ˆ‹/¾Xé2'’È‘=ø»ç-¼ïòç„Xž–§E4 åÆÉ|@T.ˆáÕQ*—Ëhïဗî„à»OÏø4…7ßÐÑr%^ ¢øÒ'NáÝ·õ(Œ] P³ 5éà©x²U’äÏ·ÝÑ£G199 J)úûûqå•WúÜ™* z!Iææn&M$Á®]»pîÜ9LLL`aa‡Â¥—^ê›fÁß—Luàüâþ@ N'dë¥p<ˆééiÀŽ;°k×®`Ãs€lÆŸ&¹™‡‡Ù2by©Füù4€Z]b.Ûh4ŠãÇ£P(àСCسgLÓ”b§s`'¾tl ~鈵jv€ø¾˜ô*ÔT @æ ð†A iÔt“À*;}%‹ì¬…xÊ€Ñ0?YF4¥#×1?QF,¥#ÙnB3ò U,œ/#ž4j=A÷ú8 fÇJÐ4 ¿àÎ<´«M®†’Éò°ñŒPÉ_oÉû¾›Âä’;oÆ4MïÇ$¿a8rä&'ÝïaïÞ½—^z©|ð®;²Z­zj§lÄÕqœÐ4l°Ë²,o¬, ¥Ô›'Äç/>¯Z­¥d£š IDATz‡•;N㢋.òf¦9r”Roªøkz>óÂU~gè(¹À$ÛÅ÷d™TO?`:4@Ó àKÓ šÐµ_´Ê˜ÈÍYÈÏ[HfLdç,dç*#BàØ¤V:Ç¡˜+AÓ ’m&(R&4€h¹…åïÍ(RëX½êv»ÌàÖóÌyOè@$–öÞ0 Nž<‰ÑÑQPJ±}ûvlÛ¶ÍkC¦"±Ñ]Qè0€³<›IÃ÷â;k%ªLÊJ¥°yófT*,--áĉž:&2@4…ÙƒïŒl‘´ñê€Y¥H>±m›ˆ ÅgJàtÃ&ôoKÀ±)ºc¶'âÞ?xQª~¾#åá/‘®kf}›Ü…6ÔA!jÌA$3pm…dú–rR¯nrUˆ:÷~+=ÖëSIøßää$Ž= J)6lØàS{šñÕ³tì¸Ò4ì½…¥qº®¯hŒ¡½½›6mÂèè(æçç166†õë×û·eúðÄé‹qÝÀY¤ÍJ=“e境 ”M&ÉÁÊF 1 MCƒÆMYј.ˆ=æáÏUæîãó#Ä5ºWü@­¼Yþ#W?G¾ÚPwØÏ²,<ûì³ ”¢§§{÷î <“éçaØàšMæÍi& +S3iÂÊ”ÉdÐ×ׇR©„‰‰ d³Y© „ mà2Üwø ï^AÉR>C¤&R˜@µ3ùh! #`Ø2ahдWG‡[-2#ðƒo:#Êy±±d‡üº®ãÀ¨T*ˆÅb¸êª«”¾ñfýþÍøêW+ŸfójD}}}ˆÅb(•J8yò$,Ë Ø#€;ÝbÛqt®³Â{‚>¦Ù´b,ä=@ © A2rGÃX=É,£Â¢…üB¥¼ù‰2ì*ÅÜxVÙ•H¹y ¹9 Üt9³çJ(,UA©VX´0s¶«ä€f„uY|ã“@@ðϤ@¢CRð†ãÇcffš¦aïÞ½ˆÅøMÂüԌߵƚÍgµÊ¤i6nÜJ)J¥FFF”iÛ{7ã«'.kª|Ê2-#Y¨ˆï2E&pu|Á0¬M³n\"šëfÍ/X0"å|UËqÕ$J1?Y†c»e-l,ÍTPÊÛ(,ZÈÎZ(f-FD‡s‰2/{ˆ¤nÈ—€“mˆÇ>ß6¥RÁK/½ضmÖ­[§®G ~øF RšÉ‡wi®¤L­¤1MëÖ­C©TÂââ"¥é5MC5±‡fºCËר쭒lW¸sTF?œøï`è~`U‰3çJž}T): šÛYeŠJ™"Õ`Ûõ2&Ú ÄÓ4 ™1?ž´zÓ 8À¼:ñjпýÿ)DRë¥j®ëxúé§Q­V‹Åp饗†Ž0S½<4àø<Ø2H™ËQ”“©"¼·& L|¾ÍÔ­½½Éd¥R gÏžõÍ<åéÎA|ãLp|d•Hú‚•sdîÏC²sÌÚ8À!¬¿ØÝÊ{‹â¼çhK}.Nç@pRZ÷@Ýþ0× Ê=„Ÿ~Z[ÑT*‡Ïµ!Õ•ôÄ{€ÆÇÇ122MÓ°{÷n?©Ð³Ÿ¸k˲êUã<4|þ#làŠÅ3²çðƒV|¾,ŒÝË\˜¢!Ëv’ïã¯y£¼Ùºõ÷÷ãÌ™3(•J˜ššBoo¯W,¿‚±Çç;°­c«EaöŠŠa<ð/ –Ü× ôdLDF”r¡s3^7dWlK®Öã›ê§o<™€‘ØèùÒÅß‹/¾èììÄàà ¯á™4 ôê0¾˜¦î)ó÷Ç„Ž¤A ªCl` ð ¿•”›§H$‚®®.,..bffQhJ)’CØúü^Çãê¡ F $^ ÕtèPÉï¾põsÖuGñëÛ;qz¶ ÇñK§E{§Dax›Œ ÿg®(¥\:¡á(—–Òz^ ˆGt\±·áDpðLmÝíRðOOOcrr„ìܹ3p7?"ÛÈÏKyÔ¼ÐÑuÕj5†'Y¯j¤iš¯—}õbÙTånfŒ¡££ ¨T*˜››Cgg§góÕnÌãHØ»4u: ŸëÓ@çF7œ $VR WÑöõ lßP[T&cRcHøìÆ&€žòà€ì•‹r À}ñð3 åDBç¦5XD=ßçÙgŸttt »[nÈñ_2Q&üüµZF"heº¾øYo S{ùë0j¦n𦡽½¹\sssÞ¢|Æ\^Ý“CøÍƒÛ‘ÏŒ«H ˆ£ã'u¼èŒ<‰¼Ü¾ÆP.ˆi¤]bꇷ‚K˜ƒÔLw(M²òò>øDíF)!‹‹‹àNtS­…v“1Ôõðiø2ˆz;K'Jr™Ú&áya(¥Èd2Èf³¨T*XXX@:ØÑhÎ|O¶=ß°®-Ô¹åx‹•=@˜ìVV•¥‡âÚô"¥€lÙ"…ûA´Ô'±!ÄÓpŒSåES¼A)ÁØ\ ݃Qé´‡ãÇ{s[z{{•ùÈ€Çljº» ü¢T;Ÿ%öê¬7aת^€¥ S‹³’É$ŠÅ"½uÏâ¯KëB¢”@!Vh˜o Ô´ˆ¨**'Šàî |t| gR¾ÆM7ö$=ÏÞsdGŸ%àn0” ÛbâSãZ¤G*ý)¥8vì`ãÆ¡@à óăŸ¹!E/Žø3 Cê­)•J(•J°mÛÛ+ˆý˜«4{qlÞS©DÐ3CXøw.–Q5õš¥Ñ4 ©TÊÛÇ´\.ÊI)Ew¼æ6àÈÀe~-RÀ,‰T©@\êwÔA"ý=)ÏºËø…âJñ©bî~)Óòœ¢`j88š@ª½_*ý'&&°¸¸Bˆ·&V%áy—%K#~o™1K#?sp«Zßâв,¤R)on>¯þPêº%s¹²Ù¬»¯k&ãmœË\–âûfÏcLÉé욥ë&«?!ÉdsssÞ™¤ë:ºs­ Š©„O­<­°ŠŠÂe(˜{ ¸©žô÷Ý­ëD^©ÚûŃ—}P=B (ånSdª>»ÿÔd íëR0ž>}€;2w·‰T½ÞŸ/ö$¼!jÛ¶r†)ËÛ¶mB¼ÝÛºººÐÖÖæ P‰é™´F£Èd2(‹˜™™ÁÒÒ:::¼Åø2ýŸ¹Qŵx»@¬›ŒøÞ!•Jaii …BÁ§ñy'ìDíûhÍÙNËy’í çqŠÈå|Õš•»MPc¼­GäLÀ‚óÝ~ 'p@¦R›@Òmð ËçáKÔöò TM £x©ccc „ »»Û§:0o‘j&“ö|:¯ÖœÎ/KÃâgggaš&Ö¯_ï?bîV§>µ…‚D"ÁÁA,..bzzÝÝÝ}‚øô,¦~B¼Å42çßkÈÆ!b±r¹J¥’²÷iÓÚЖoÃbJ>}"@«`{Y°nHiõó'EâPJ~ßlyÝ*¿¿ ”¼Ü|×;øòö Á ÕéÔ¸=*ïŠËå2æææ\•s}²—®š3Ï/{äõkY¯ÀÒóê¥SSSH§Óhoo÷À/~@ż$nkkƒa˜žžö x¶çß+‰Z¶5"_O¦A¨T¢H$âÝW*•¼¥“ü3âZ M3Ê ‰PK6^W_Ôà¯ëúrðóÛ`PŸ à.EñžÇýç <§š¥ª‘Ø[ "QŸŽE‰eêuA½‘ÇÆÆÛ¶ÑÙÙ0œEFà{™:ÃzxB¢Ñ(’É$æççÑÝÝíë=TÌÀ—ŸïÅd*‘Œ"‘4MC¥RQnëuVý#¾ž éaî„PI\")­’úã)iGP?Ö×ׯàz¾˜a‚CLÏ«f2@7¿œ híX² DÓ–––¸ ‘DýYf'„õŒ=>{†Œx& QðukooÇÜÜz{{}Ꙩišæ›@'k¾þ2ݹ2†IÙ1¼uf 7u€¢ZûòuAð›{«@MÙì\š“LÕñÔîÈyxxÍÇŸ¡„!‚¥ò1*Wh!¿‡’ƒ_èQ¸c¹jHge:Žƒ¥¥%âúµ}m#HoðÆª˜†åÃŒ[Àe8Ù–ä|zž!Àdc |z>H$âÕ‹7by&` ey‹eàó[`ÏT Öž²o´E#øè-³Ø>(7„¿ý(7bn ÐõaFpíDðí×lÙàV­¨3‚øX¿á«,ûO…Pv¢®IÀÏÒU«”Ö="Œ!žªñx< ½x[dxl¶&{ù¢ŠÁÒ0…©â; ³ád÷02 ù|©TÊ'c–«;c q23¸ÅB$MÓ¼^PÆUÛ@¡Ô¤¢ÒH{PCÏÙµVŸq[ß7“pž #È ÝZÙ›gOê³ò†y¯P±B@¡<„T*Ÿi†OÕÓó™g(ž4Í]BÈ®,M±X ì·){?¢ÇGt-6jp©çr9¤R)@ÅM´Ø=¼ÊÆ>Â'–‹19c(–†µ‰ŠAª!Wjþ›j­Òêõ<Ð=MFÅâ ×2X7À5’kʺ &¨×ѽ·T(4éü–b±èó®A¾x•CWQŽŒ©Ø‘«R©xL&ð qVp¨|†#›ûÂO©>"PdF¨Ê€”õ 2Ð3`‹› È02&‚/_.3ˆ½ŸNÖ>,­8ûU¤R¥¹V½coØ™pnM¯$<ây´»jP ¹˜¬ïJ¤¼ä\.õýaQƒ‚:V€˜1pò/[æ¥á™EÅ*ߺiš°,K fBˆO÷n4¢QO ¤±Þ ¬¹}Ef‰o3¥âêq‚•Ò z€º äj65F¥{˜Ìbx“$»± &hv¼ £ ¶å›éȈ²mÛ¾¹,^í0éÒ|™­‰DP.—}€çU^Ò7š × ðŒ(®IwVfV6Qâë'›@§²ÇA:ÞÄw_€eu+ãS“øµªÔ=D>á/úøæXA1‚L ¿ö¿ &ˆ¨#gþ…ñn@ÑXdiEàÃÙ9?­A”¤‰DóóóIΡ؋°ç1p30óLÀ3‚eYH$øy&\œÜÇ$ËÒ‰R_Tµü¯Ž¢=Y‘ÆÞÉj°£(Ù¼‡‰Ÿg^5’©7 U¾2 ¸$„ ¼ÿ2ा{Nkañà8U8páÔ²¬À|x™:Ã@&n„ÅKkÞßÏ÷2ÉdÒd™]"J±àÕ'‘ ø<+•Šoi'/ýy¿?oÐòßDÕŽ¥¿e,;ФÉXs*"‹Pj¸ Fm0HâÕF|ƒ\Þ P@\ÑHOâËÇþû’+€ÏçMáói„B×T#™Lí) ¾é „âžþ¢úÀòЇ¥cß „ {Kev‰lš/Ú"0"ÄßÄâEuEóµ +ëQT0 ²A0Ðu …²‰±YùÇÆ-{e[®ÌðT @å¹7 X²ñÖ° ë’†¤Ê¸ÉïØËåÓ°xЏYE¡T•¾@¦— þ…Ûb»‰Ò]3‰ÉKNQuâóéëëÙ3gŽIfô<ðU«´ŠÅ":;;‘Édš{hÕ‡/¯l”§x °XÙ‚ ©’д’Ÿm81à ›ËÆpt¼K‘Ÿÿ9A¦åË¥:gu.(#ªË¿SÌ&‹QJ±°°à„H%©ØÆa#ÉŒ˜k”Q[[‘Ïç•÷„Ùp"±<ã\¦®ˆuk4…¡QÞWcdT­V1²X&ð̦SÖ©%HÚ<ð”Ì¢* ÒÿÄùN8N=L'6®ÙrΗ¦3YÀŽþß½ …‡•‚^|¾âœÖêJ1ÐYE2"÷CBJ¥@)õ}øwaòL ¶, ßöb>®§§[¶lA>Ÿ—ŽS4CŽã —Ë¡··›6mòÀÏž§òÜðã byùtaB€7Ž•.Ðb;ÊM‚…äFËs{ÔU |˜sýøÛ©~+zW2€ŸfXU¶Tm ¸_‡‰ÜöwïŸ^J -A÷ž+aTÕuíœ*Â3I K~ïQ[[òù<ƒc¢„§Øcq­KËò©T*>ï;Ïd2ؽ{7Ξ=‹¥¥%D£Q¥;Ñ_uŠb±Ó4±}ûvO•c.KÕ¬VÆš‰ñ|¹yG_'vnYVh­TÐÑŠ ´ Z–À"úõ[ø"õ¨L¢Çæ ãKƒ8_Ø„ é†EÓ°i yhÎôê$úb#ÊœGO†`[ï4W0™ pM}ÉeÀg½Ç›‹8ýLºl®ŽŽŒ£R©øv9ÇQ˜ääçÔ¸ò‚üTþ^ñš¥‰D"غu+ …ÆÇÇ‘Ïç½üøõü@T,Æ ÐÕÕåá kÀ¯ÊT*ßt >Üã÷øáçñiس*•J¨Ônk±g[ÕÉpa @)aa5ð{ r©¦<¸ñìèö¬‡¡9 ˜ËšÙƒY\‡H¬FÚ€ 8#|ÆËeœ˜œBjäq\1t ëº4Ïvzº»æ °%Ò>Ä]zõ¶z¾ mRD"¤R)X–…óçÏ{>zàìǤ!j‘Ä g2&`~x~ÊA"‘ð¾Ò^(P*•P©TP*•`¢Ñ(b±’ɤo‘?!Ä[Û+¾k–†g"Œø2˰ÂçÅO±Vy× Z©`kIn{)i5G‚1Xö“ÕK,¯X?±MÊ ÀÝV© & «tAT Ð‡±øÀôø$¿ÏmºæöU›âÉÓÛ‘Mý âQõÒ7¾,*½±³»‹æ/áÁgþ ?í,LÃbz¡°köò¸’Sàí—/áñ#Y”‘ <—í ·¸¸ˆ‰‰ ôööúôxÞ€”©52/‘ \²£ØF¼A*öì(fX/ ºªªº°z–J¥PõÇ*—П\‰6Æ$QanX•Ø 0cüG•Y*iù³A™ôW¼2†pÃ^8»—ƒF<{ºs‘}HIÀï8îæSlëoÖh†a Nv0€öö Î~ß~öŸ°ïj·›}ñÜ ö kôõkÞpn‹;èL•0¡ôíííÅüü<Êå2fffÐÛÛPqD•FdÀïQ‘ÍqoC)Þý)2?Mf´²s~ÿRðù9=âýªt|<_7JÝo„Q»1‹÷¾å šœ¹V“”ºF#‰@©ƒººA…஽°úïMëG Ss6ÎTnF*íÿ@¥Ùl§OŸF¡P@$A<G"‘@,!sss8zô(–––ÌÙ·n=N¯Ç™ñ @).ê9¯Twܺ0`HÔ5/-pó¥  v22M™LÕj“““RÀˆ$H5H&¡™á©RoØ;óáïgiD;E&ôdŒÖ+«âÙÇ;Tä86¥ç^ðËc`ñ]«¤˜îàØϜ݆TçÖÀ3ÇÇÇ1==x<îy;D]–¹Ïž=‹±±±Ð†¶^‡_ì„cS$ÌJýùJЃ+¯äœRüÄŽ<2±¬²½Ønj¥RÉûb$_/~ŽŒ1˜qË÷v²÷Q­VQ©TŸ6åÁmYÊå²//>-{V¹\F¥RQ±,¯°2±¼ÄùE☆mÛ¾Ý4d±gqçŽÃ¡iT´œ°Ö¬Mþa€8TV‰:½`aA¿¦izyRꎨf³Y˜¦X©$[¾Çzƒ\.ç{ÉdEsFÏ—¥ÌÖCù®…ó[.y‰DÐÛÛ‹J¥‚©©)ÏÏ­’â@Ý7.›¥É3;ﺔýD•Gfó+ÒT=ƒxÛ—ÛpçÏŸÇÂÂNŸ>íIÕÚUÞ—<22bí}—ààiÓ+Ÿãµª¸W—:SÿÔ¥Y´E–”ÍÒÝÝí-`mèá¡D ÍŽ¶m{N‰€ãÛ‡µ—êÇOŠ“1Ë£ÑþAü3ô–ŽõZtÿˆ3;wj‰uZíéÐáTŒüµk1¬vÞ™È!q?ž}j¦7°©ììì,(uç¹$“Iœ8qÂëžUŒÀ»T*yöR__^M”b®ÀÉ™ ãB¸®^¾[e nܹØêùAýýý(—ËX\\Äääd(Hø)¼ô.ë!øk^QýDpËÚ‘g¨Å2…¥cï"›U«Ž€+ý7$¦Ð› W‘ÂèUë!ð‡¨<5¦ˆe¤ŠrÅEÚ}Æ—mÛÞvã¶m#N£··'OžD±Xl¨ î~ý|kš&òå(lÛAg<‡‹º'¶H(èÅ:ÕÎo»|ñ9_oÃS*•Bgg§g 0õ¬‘´T1AX¦ÉÒ2`‹ÀókTÞf„Ô¿iF1{¿ºëàràèѪ/ˆ gürÒÿdLQ*ÙpˆGeÖEó/<‰`hhçÎó6£ c˜z$…lÎV2iCÐûêàžêà×Þ< áq$A©TÂèè¨tº¯ò„1„jZE«?ØÅ4ì}„•©ÑQ¹\n¨úP»ŒëÎ »ð#¿"­À ÁÔ G¨Xl¸Æ/ßõ2=—9!ëׯ÷¹üø^@ÜÎCÔ§ ‰¢TvJq`d³ðÍ^Þlë+b[÷¿˜€Tß—íc#S…xo £\vÉ„ûÒ¯:Òcqu’I~¯nµÞï½qImNÙB¦ib``•J…BgÏž Hv^Ý‘Iwq LÆboÒHúËòáóàÓÈÊ%–IVÛ¶±¸¸èå¥"ݚûv½äNc_!­êHpCòÀ/ f„²³«†N€‚ %(æýÀ¡Ô•dsßÅ8vTýÇA4 4x!·„dTÀÖ0ør^ûÒ°çÊÒS$£U¼ëº1|þ‡T‰0Q"‘@__¦¦¦ iÎ;‡3ÄIDATõë׃Rê[ úÌY}™AÊ/0ۆ߰JE>.ý`šŠ 1ÙÁââ"Úb…ZYk ”MÎT`~Z~ÅÆöŽN㩳q@“ïbœN§á8fgg¡ë:Ξ=‹o~¡ _ÏÀ/¢a’—}•WmXZFü×aÂæúËÒˆíËÆøA<±Gaegë"½”Rôêcø¥‹WàöèUëÜ?(¼Ž@Ö#ã.éŸÁ¹±sظi³÷òðU»žñG‘ØÇàx;z×l+4Ærñø‹à½SÅ5wþî&qú«qœ/©g²¶·»=û®°mÛ‚iš>µ‚×­ÅÞ€1ë9Ä4<±yB²å’¢ªÃÏõWõ®¬Ì*£˜=«µbÕI¼ÿê§ýë5VH”¾J#Áš¦¹_Oñ*/7vá‹@)lÛ=^2TÅø©ÞKe_5ìììô6•é©2ªT*hoo‡iš¾Êã/}×ìÐJñä趺­"¤ËY/»x-9§~÷æ3Hj3¡íÖÞÞŽžž är9ŒŽŽ¢P(ø@¥ÐâA§ïa^µ°ïóoÕkÄ¿˲š¿aÍàWv¾°ê^‹¶.Ï2€ tš¦!WÔ”`r`9cœÜŒªM°¾Ç@‡ó"ÆÆÆ<`“ÞÒé4Êår¨¾ ¸¬\.£­­ ñx‘HÄSz[2cêq§\3tLèÚ3kèõ8ÞÐ¥èLVñ7ŸDÔ g‚T*…þþ~T*är9Œcvv¶!˜Å_ØÈx³?^Ÿ>ÿ<±w Äý^ÚÂÂBsà¯.àöM/ã2¶„u©bû ~€eöº®c.[SgB¤¾Œ1.ï?Ø(~æª^:ð5ož ÏííížkTlX^º¥Ói$“IÄãqD£Qhš†r¹ŒüÇ—ðî[Dé®·,Œ¯8ЫÇ?2e¼ï†“0p£.{[är9ÌÎÎbrrÒûî˜L™ƒµCØÏ-Z835’ò*†`àÊf³Mo +‹ëúŽâ'7Œ6—¾E*6þ¦p€#4!’r‰”ìLÁ|!*•Œ*‰éX DC=:~bÃüౡiîâ‰R©R©ÚÛÛ‹Åà8îΖey ³£Ñ(Òé´—6‘HxŸÝ|àþÏ㮞Ç@§x®Zê‹að31¸z@R§ÚùÅëòøÏ{ÏÀ°çCß„®ëèëëC"‘@>ŸÇÒÒÆÇǽ-ÐÃ@ÉSøÝbÊÍÎy›C•8š-~g뚢j{»NàÛ6—¾E¢˜+Ë?X?–yŒ‡.ˆÉ‡u!³ù(@ç×·=‘z€Xøã)ÞqŽSÿþ(žüA;®¹þ'FÁ<#lgdƬR]ÂîÌ)üê®—š¾§U*T€%+˜þðZ}P c €:ŽSÛcfb)‰ju†NBÜŸjÆ(U#86;ˆËúNã·÷ŸùæWñͯá–Ûþ“frqÿ}f0»‹â£ˆD"°m_úç¿ÅŽöÃøà±qi÷É p½² €– z€úøêÊ HGá3?ØŠ2é õÑG£Qôôô —Ëyà*‹ˆÅbH¥RH&“‘W 8ßÞ-žÇŒiÙÂ{ÈEÝœŽã X,6œÏ/’^]ÀëãÛŽ·t_«tb.KOKuúHE³žZí§Ãe }çη麾IÜož5bÁŠà²¾1t¤Éß…x¥ ÍF_r®\}A~~÷ãiñ.¬[7Ó4= ÇãqÄb1$ $ ïÚ0 <óô“xà¾{🯞ÀûnBãž«ù{¼p°³´µkÊ1Aý@Ñªà’¾%<7……d(B¼z:Ž»µ c|~ЇjU?‡_fG°ÞƒI}Þ¥)ztø¥¶m£P( ›Í¶¼^™ÆÏmy·l:ÓÒ}Ë¡ÇÏâ\e“w-ØPçŽ9ò/lÕÚ Œx °}ûö½†a\.İ—¨é"ö.]¯hÉ»ï@pñ 7ì(à©§ŸÅ·þã–rE$’íhkkó$}$!ããã8ðÃïbÿWþƒÆ³¸û]À•™ÈUb!Ј0– Rç–ÉrЋ×íq Wo˜Ã+çtä­@Â]uš¦m¾ËÛ@¢DüÛ‘Ë iÞÕª2¶Ù±R© ŸÏ#—Ë5ÔÉqĪ“øËžÂ}S-Ý»¢xèÌNdiWí:0~ñÔÑ£GDü6\»Àç8õÁårù$˜Ré¯Lt€:ãþ¸ÚÎ2`ùB§QàåéMØÖ1ŽžvŠ?¾˜]šÃ}~çaŒL… ¦G`UJˆE(6÷7í¶ðg7GÑ‘ª—3[Ž"A+0ûyŠ  Aí^4 úú-îy&^ÁŸýô!üësY8;[î,!’¦iH&“H$¨T*Þ&W¼êÇF{Ù"yÙˆ2O ÈÌøåU)Û¶Q.—½å–ŒZ^fh19‡÷_ý ’æ…ûº#Oç#8WÞà“-¼jW­VOqÉ¥F0厧T*gÛüñÄKžó¥AÃöZd˜ .„AvvŽÀÐïþ®´†;®‰áŽkêi*Õ2"¯åé ÞC] ÷'çý5ò—^¸‡ xnôÁ8 øå½£¸¸7‹}n3 ¤„4·Å8¼ãÔ× —J%BFÕV¾÷fù°7v\ΚZ¯Ú”¬Îà†SøÙmÆÓ£¢ƒ FLŠU°,ëj¸† èypøD'Ož|¾»»»B)ˆŒôH¿Ü‰íëfçB°`ª¯ÀÄö/U’˜ÈubGgýó7ÑPµ ­Þ%?~;.ë9^ÛE@d‘Ti€—……˜ÛTwÅàvô,á Oo‘éØFsûñðÆJø)#L¥á§Zóçxà0¾\"Õ<:õ)üúÞ0˜ _ùµÚT¬<7·ÃsèËì£ÉÉɧàÇx€±gll¬@)}ÀÕ|ÆbC½2³çff0Ô (ç!܈ÚÌR™‚¯§˜Ìu"nZèŠÕ§Rïí="ä/.O´þ_Ôó§a —_×o ªB,a´ðÿ\'fÆñOÏlÅ¬Õ ¢Çe… ¥0// Wî0²mIçíLTpã¶óÐ"&€rÕÑVðéfŽT’~5È®ZH:çqE×I¼ÿò§±5£Ø;þÓRIÃO^‹¤|:?oÌ[–µÿرc nWÁõ<°#óéétzª££ãÝÄ%©> Sù ú¢ì°ý¹ù¨Á‹ ÒST8U“ ¼âð«7µk1Þ—•Ÿ³dÛº³xë– $ôÆç²mš0¢ó: »j!îLášžcøÍ=ÏboßùWm+ÝdF*Û½k™ÇkjjêCgÏžEÐÀß°£7.011‘ݾ}û[5M[ÇK~‘4ÝÀ©óÀµ›f‘¾7øWK8…¶ÊàÖ˜`y ¯OwNÅæŽÞºeÑ%L/:°ªUX4Ú”±|¡È¶m˜Î:´ó¸¡ï(Þ·çy\Ö3õšN¶á›7øÜÊûö·¿ý,îÇT!~#˜)ðê\â”J¥/†q…̸ð->ÁFüýã ø›¦|Q»€[í6 Su¤Á’xÖ•JÂ|(A.^Sávu¯êö×®ŸÂµë§-øÎÉA¼0Ùƒ¬•B‰¦`¾g°m&Í!©-asû,nÛrýÉÆk{_-š/øò™ëA5wò›j¼£T*ý¸Xf?Þ €޼ `ˆˆöôô¤ßüæ7_Ó´~æ^ã?ÚÆ3c[¸yýÓxç•Y![¬ž´o†šÑ—8õ&È7jÆðez_ÆÐ…ç‹çT>—àÙ‰¼x¾óÅ8ŠvEš |@¯Ub£Â&ŠHh9´™lËÌàÚ ¦²ËZeu!©\%øß/\k€º±/™&^xî¹ç~âÌ™3“p¿0\F]¢µŸÒ äqÌôôt¹P(Ü›L&ÿ›ÈibÃkº‰ÿ8»©ÈSxÛîÀ}cÕ¥}3$€N®)ÉBEp76n ¶AóÀgåëL”qË–³¸e‹ëm™ÌÆpj¾'æ3˜Ê%·L”l–c€Ö´XªT‡NlØÐàÔ†¡9ˆéâF‘¶dæ°5³€ mÙU]¡µÚTu€~e·~F2é_.—ÿåÌ™3sÜûÞ¢Èß|/ûe¢x___Çõ×_ÿ-]×Y/ ¶ø2±s¸}ëóxÛnu·.XT±ò—Ó𕩦A°»U6DC&XÆ`™ÿøÇ üááç2{ (YJU ¥ªŽ¢¥#ª;ˆêUD*†U÷®…¶Ëë‹*UàŸíÆK¹Ë“ú$Ó¸Ÿ{î¹·Ÿ9sæ\é_P7ˆå+:’ò´Ú¹@ÏçóZOOÏùd2y+ 7„}L Ept¦åÂvô—šÔ~šT•D5K«Â"๋0Ћ׼´˶,àóç´&ám¤"tDËHEʈ›UD4»Þ¼o ðç*:î}éJ-î€_¦ÿ/,,|üùçŸ?ðRã—‘l$Et‰"£££[·n½T×õ ¾ÄŠ-=ˆfàä⎹3"c¦¢Á‰÷o¤îrÒË%~Ó€÷NÕ ÷Á¾©1AÅj|ñ™Þ€D¥z£Ðéù>÷Ê «Ö·Ñ}þüBŸJ¥rø»ßýîGÇ©ÀÕû™ä÷éþŒT À»C™A[\\|yppð§5MKx‰Å1_¯ a¾Ú‹gÎÄ‘Ð1”±¼¯†…%®‰´ i/ï\d÷„I}±LêiÍŒPExð¥jÕ øå*Á·NmÆýgnDõEEâ"aÙfñÅ_ü“¹¹¹i¸à/Õþ¼¨i`kŒÚ/šÏç©®ë£ÝÝÝ7 *ð‹6AmxñüNNVѓȡ3é`uÝB¢4mœ6ì¾Óf‚×~~Q^·<àsO–Iÿ7Ùðüdþñèux9w)(ñ?ðƒŸWFGGÿ¿C‡=‹ºÞ_B]¢ÜÏ£fzÆ&€èôôôb[[[¥­­í ßM &WMǜՋ'Fpêúp%¾ÈlîOSnPÆëÿ&€€$€v™Ú1ý–·¼åW{zznâ'Ëñ‹2”^"lÛFóèO, +Y@g´€TÔ‚I,D¹ÏÿÛÞÕô4ná'Nb’솬²J@…"E¢—rZ~@O=ôÔCÿ]•zê}ÏE¨‡RZ!$°U{p>BBì¸=ÄOƯcg»´l˜G9Á‰±Ðó¼3ï|˜¤¢ û³™? ˆ¹'A¼ØÓã7QRO~%A±÷ÇD‰;ŸöúHFd<ÿ…$$ €‡BDz;°ñ$XÝn÷®Ùlþ¾¸¸ˆR©´ñØÅ8!¨ ž.⪹&âðÇùegg燋‹‹Bòó‚'? @ÁfÚSgè€"FáçâŒ6ÇEŒD‘¯×ëõÍÍͯ+•Êçê²bœT;PÌ7äÈHÙvßívÿ<<<|}~~þaµm»šÉdâo ˜gøƒÁà]¯×;ït:oÎÎÎÞ8Žã $,‘Ÿgz\„1>Åý=„¤§Ô§)ýÙpN[~¥È7àF!Õ È Œ3EB ³,+[«Õ^–Ëåù|¾ËålÛ¶ PÌ\×xžwïºnÿöööªÕjµ=Ï£>"©iÅ'âó/Åÿ÷ˆfRe€€…KyýÓçÙ 7¥Òy-<üJù}ìÂçËŸ‚÷ïŸcõ}Gù1!¶÷ í§½¯'ùéúÇ€¡ï+Jñ3k?¼a=Þøýç[ìîµqïyíNk#R-¼7õÂç7.BùŠFãÑù'¼îÏcòlq“1#›‹Çi¦L´oªÉ’¦›Îó¼Óf‰5ŸÜyÎyçò\¥ç™wñT¤›{™ÅBäg“5ʼäϵ¤gÞùÌ·Ó`fKÜ™ƒ%>ñË#üîâÿòøè^/Ý”<˜ã+Ö•½¦Y†gÎs I÷Syâû<ÂgZ¿~<±… ÊæÁ5®×KÒWm•'Ï…û$Ö_­‘º½ DÌ-,&2[*’ZŠ=çžqäGYy.5/2D²¥pÉM)äŒìs󙞞{³ä×e …DHi´Ê CJ²jê§×A )ÝSƒˆ4é2dжÒj“ÖZoŽQÚK¯]zë½>»Ž2êÑFcÌ¡3Ï„Él³‡9未Lª ­|Z¹CuåUV]²Úêk¬¹tS>»nÙm÷=öÜjÙŠÑþÖ¬6MO:”Ò©GN;ýŒ3^jí–[¯Üvûw^ýÌÚ;«?f-ý”¹?g-½³æ«Ï}ý+k\îýcˆäp"ž32–k"ãÝ3@AgÏY©Öì™óœÅ™i Éd-‰'Ç’gŒ Ö“²Üô™»¯Ìý1oAê?Ê[þ«ÌOÝÿ#sÁS÷ÎܯyûMÖLF)O‚¼ =¦±\€í:Ì'ѵjWÉŸÚÙì¦Ð¬Ge1©ÎÞµ/ïÛÛ67æE€ÆT¶°±ù¼»qfy'ÆùZ^Ò÷f‰éÛ›™OÊÐàk±÷UïÔ¯vnjÓÁ¥n‘ì–v«&­#ŸVO>ﺮS¹eöËÆô¬5öÖ-‰>/¯Ó‰ dÝÆÒs"Yýá0‚iÙ„1œ#©‹m²þž¡?ïoŸÀiV1ªõ س¹cÑß/µ›&nÏá5Ãæ+aÞ³ˆÎÛ†Üs’–ׇ¬¼£ÇX»¨i䜠ˇʈ¡ñ‘Y{Ú½î»OõØÊéÚÑÞmÞ]f¬0¶’ ۳Žaè¥@[.›–¹/a:4õÓ ]#mWÓ 9_h¤vh䛩Œq [wÒ¾-Ç`svˆÒ“¤„Ó+Þ¥uº»æÎãÍm_—œ1½Ö{ÍÁìT3|ï’iv+é"HaÓBûR6tÿÌdž6`SáCL|½D üNªóR?ý:9Ò#ÓàŽ ¼Fϵ°Qa4úexCy;B¡µ—ƒÄ+9 À(Ê•õ ޾·ÏÀ“Þ¨EÈ01Á vèÝˆæœ 1Ù<ƒ åqØÙM:€~Qð½7F ÎÁŠˆ9E‘­we*7ӜȆ7Ô‹L¾¸Y‡'é¹ `ã÷uH¥v(ÊÀejÑõÙ™b NG3䥉a`€ £Jk×k'ÐxÔˆDX7ÈEsW#=C(fÇÈn’nQ£¨µ K÷3]®)sÛØ,à²@£Ò”‹R5º =! ¨P½1c†”¼ÕSUTäÂÈA!´CÖÝ $Õí”V(¡–n§"•ÜF‡1´Œ€öÔcDk+mëÞ»n<s ¬gºÂfo.ðÑïäÖC4X/×H¼¨ÆF«)€ó˜è: ùiÉœèPÝÃÖÖ"Q»#¢P€44Çø²Ž÷û…YÕ`‡6˜( `¢µ[õ„ˆï°•X-é’€åÌâùŒ ~¶ãLºSrpMè¶½=÷À'R÷°yz宦1IWº«*+A:ºÒ虫Îó8 Êå! ·%|êæj8ÐÚfÑ¡,À{>4iFm~ gJOw\Ù±"{@UX°`oè\¤¥¡ÔjÆÔhY§€„rЭMn&B°+°MÀIê­=Ɇé´2IÙB“åîé·"é-5² Ò¡<ÙÐ:Õé´50'xLEЧdr7pÅJ©Šå¢\°c|lÕ½Ï 4Ñ*Ñ ]0‡|,ÇutƒÿCϸP%µžè z`zаŸ‹~„.!Ç«´@ÂÓë¶/» 4hš­HPv[Q%hsÈ@1ð•⇰;ÜÕˆÚ ‚XÃöê^g%³…µaó<\;®ÝaÎKï)SµrPsŽC<ï)(¼«¶'ÙtÇAž2‚5ÔjmTþ™ÊvšŸ¡=[‘EdÜ7Ì&`, –ˆ¶€_°ópî×+êñvohɹì 2á²I\€Ðµh®=‰pàÄJ9±õ‹"Ÿ0!™.B…åmÆÂ> .1|Ék{Xp˜”>7ý¼ ûŸÂMoòe‡WJ \¡ h=€þ6 ,AJ®¤Ø½*=#d²­E¡ qÏñDí¡÷½\»[Sƒ’}L{]AY°ì9B œ+gî¼A Ÿl·øm~Ê‚¢åoT$—Ò+Y]Õà `,f@ØìV>À)˜adÌwóò†QF¨™ K÷B,Á”æçŽU¨P„rxt­°¥Š¼ƒáÊ3½Ü2ÔŒ¦@ɹ {ÖG¥Ù…i‡ëøîÚ`ÎR½ªq$XÑé„|l~~L4k‚h#Ffº×A3á¿âÃ)o§â …:}ÁλãÑ¢Û… S?uëq1»mf¤;ä¸v]Ý[Çu4bÝEÕrDAòWo*²KÌ|™ˆxÒO39Òý ´ÑB‡q\RŸª§~î„Iª‹%¢æÉon¾>ª°Ê#R4ØÚ‚:ëv9´\:`€Q±Á;žÕDö•ÂA’3ò/R2¼^¸òüÃvã<<ƒP (XެH\=#ˆ_& ËÉí0œ&ñ 9ól蟷^„ ÛU–ˆßˆ*»Õ"ê)O¡ Ý·ñ°¾Q49Ýdë 1V<Aù‰`­¨œŠ=KšÂ±”Âþÿ”¨aš¿—x@‚!U"«FO¹°H|$rGéô¾Z¥§Ò?HvÀƒ-)ݦ!•zƒGpÍ%í\î#³ÑÆgÀŸÐ>dš› ¾Ã°#N€õܲð[@hƒ±x}k©YP¡Å@åHñŠÑë~JŽ-$àžŸC5@UîVúÑšsTiö~P—cWBað ß3YŸx´!J»¸â‰O³? XÞê ¥â.J¿J¯é¼èlÒ~$8õÓÆ%úø],àØ¸pÀl/®õÚW•TÜüꂎi%ȉŒ[d<”;€äÅð°?­qÄU3Î BÊQœšçÂûÈ1Œ)í¥Fe»¿l0¾˜?hÛñ=àÞ × M¡:çñYúXžlHV¶S­óZzÎŽ^"Žˆ‚[†ÜÊ踉ÈÂUbž@‰ë€HŸJ?:S>ÜôU jîiÙ½Û¿~ö^ó¶1×Ò°X¬ýØd åñe7,(rL»ù—]«æÔ,t“Û-? Õ6Ö‰ª½£w%ß$û†Av³eWÖ«,Çòç›/|„¸SóÙâ§H~xÑ]V'?Q4ô8ªÍfƒ g¸Po×v0ü±º\Rûù“ĦR`]Úæš0¶—*BgEEÿB…m0Ñ)yôi( sÏ~Þ22<™üÜ¡Úqáì§øZ?C¹‚8\¥?'³ß"¾Bjì‹¢vGU(‘{«CÊ£áß!…Ñ|¬{&Áß­³Þ·hjþºC7N¼¶ú†Cªö¯¬õ÷çð§p’$ñy控ÒH›OŒ“=ßÒÝ l‹lM(VÈb:ÅåhPÀº©q]ˆ0Y­øñ ^›²Š#yU’Å\4m'#Z´âÿHŠÃGþÜdú™3N‡í%ËOþ²Ð6 xì6~îG8âŒý<Ì“±Èˆ×xÓÈn#!úiùcãý 9²#:5ŽC†Üßéç¡´s1Ƭ@„´À,Ú×|/ÚU®¶Ñ•Õ•Êà·¼ƒ!kc;šüšÃD_€£õ¬Øp«ÃÓD²¸qÿ€¬ea{¨4(ëLmOØÀ1jÂz`NC¡ñé€A#ŒŒwYXΈ,ç®íß)Íê•zýdÜÏ@{¨~]?w!”¨³â'ûÁ]»°R—=}ùA•â{±:¨b]Ò}½M†Ùî†í†äÇ4› ¯e7­e ¥Íø4ôL¯ 2øÐÑmÍÙž@ÿƇ’Ù»€½ù_ i^rA«¡!.Åv_kóv@m€ÞÆ;(UóÓXBIÿ?ý€}J—;ì}Ý>~}Øí§ªÕÿÀ?y_¬³ÍDÝú!®ÁÚi~1ª[úÁÑÑ¥Éá–uŒŒ¦;Á4w£S#°L²ØÃ~pк~݆>`}ßçî%òPÛÇퟒ’NdÈLËQþÕÙëÎåCò¢¨Oî¢e‰6wRÓÀ†!7]"n5txV³Óéç­>=ª &dÓ$­”0yƒ&sÄq? ‰M--ñ9A¯~Â^q¾Ý_fÿNmùÉB{‚¶«þú#¡†¯ùþ  ìÁ>ÓubKGDùC» pHYs.#.#x¥?vtIMEä 7†$> IDATxÚìw`eúÇ¿ïlK%ô€ôžé„.Å(6À†Šž'–SO=Ëïlg¹³z ØQ°Š¢HïUzH€„P’H‡$»;óþþØÍf7Ù2»;»;»û|î"ÉÌ»o›™wßï<ïó¼¬c·~N`P¦p ¯³cÆLÁz§ÒÞÃÉP±+¤~‘SØŽW¦±,¤÷9G¦X–,ÔP‹`¡7˜*TUV'`㊚:š…ñeˆª{.‚;—Ó5ŒJx°n1ï ÒULùUHðÅ”où3U=ÀL©Ê°_R$¤d'Ž6œJ…•ˆ “‰PøÊ§(N4ѯNcÔ+‘¬}Bß¹Þ¤ š¨Šx SÕ¦¨…*Ö)U:Liݯ:QE‚*œ ]}‰©è’RQ*¤H@QGÑm¶}ËUÚf- t÷…‘…* ËýHL©z$cW)$¤T)¤T³7¸bn¨æ¡$ùD‰fÊQÜ!tù5Þ'r…œ6`Í# •ŠDU •$AE‚ÊcÂðTaa•bª{ Ãâ+<|&|Q$¤h6@—Šˆ†§L«xd¡RÉ”ÿT¸Z§í;Å‚ûä¼›IH&{üú¨Í*¥êÙ‹ñ1بœ‰3ª A„1Zŵh·PÑ’?ÈöŠ$ë *å³UŠÄTd‹©HRJºœ ¢KXE£…*Ã¥GIt?Õ/õ#!¡B*Ê-R$ HF TþiÒú$¨Ô.¦€·PÑR?ljSÀ:‘³Ì/6—‹æ§¦å}ª]Ú§ÚYœÚ­QæEa©ûÔÔJ¹EqQ±vízL| ì‡ù™¡Ñè Š&ŒLŠ&M1vô˜ÍFèôH’ˆÁûÖ¬]Q¡„z Š)'‰f˜Í&€Ùd„$ŠÐh-JÇl2BEÀºõQUUeic`L€ÑhçpiµâœC4’$Ál2B¯qÑ¢Ùƒ^X»v=Ê+*leåååK˜FpÕuà\˜QÃñ'AçN-m2› Šfh˜’("ãp&–.ûÅ&ž8çHNnéÑuä(ŒÆèô1 .‡(º¯›(šÑªUs<þèdddbËÖmpiŒ=ýÒzãÊkoFÕ…‹2¯‹¢ À}«8QæûÒ’¿ M¦HP‘ R™ Ruô>ÕZ£HL‘Poc"W@…÷ž‚ëÖo€$I½±s×nT]¸€˜Ø$%&àïÍÁÈôaÐëõØ´eþýÆ{(¯¨$%&àÙ<†éCa4‘ŸÒ¦¸¸Ó.Óé ˆK€F£c ‚ V«”ï´¼Ò²rÛl¿iRÌ{#Ò‡¡  ó|Š•¬„úù1爋‹˜Í’¥<­AFk%.EGyY9æ<üvîØÁƒ`Ĉt›â`4‚z} ´­MÈÅÅpëÌé8wî–.[­>ÇÄñ£ñâ?ŸBÑÙbŒ 8—ÜÖ Î— .¾ $I„(š¡Õh™YYŽ~_!|t´a/ªÈB奠ò¡qoR ‘$¨Tý­Z«‰©°”Sa`"쉨 Ô>ÂLnŒ‚ @«ÕbÍÚõ·X­[ÜmÝA°þh0ðÒ4Œ3cF§£{·®ø`þ‡(,,Bl\¢*nY­²ß ,xÏ%‹T1¥’ý§h©_À>®Úe~Q)¤Bå+E"JýŠyú•ÄS˜UœE[_°è¸½±S0f™Ü¯ß° O=ñ† Œù~Š¡Cc÷Ÿ{››ÜÜ<¤.š‘>l`ÆÍ`Œa䈡èÓ;Ÿ/üO<õ À?~_ŽqcGC+}Óú ¿4lÛ¶S§Ý.q<þØÃøÇSO¸­×„ñãþ>{¶K–š0vô—åé4ÌFKëËÆÕ×\&0Üs×xíÕ—1sú Øûô Ðju¶‘6+ë23³PUU…Þ½S1|ØP¼ùï—Áï¿­† h\.©Ðht`‹¸Ñh´®Õ«õ¢ŒaÖ·Âh4bÑâo¡Ó 8ç8v<é#'@Ðh 7Ä€q9u³|6}ø`Ü{Ï€êêjhµZ 1ª¹/µa'ª"ÚB¥‚%$¨ÖGág"A¥XHP…—˜ GKEÁã. on·ž ÑàpæQœ;wC‡ B«ÍеKgüøãÏÐ X·~î¾ëNôéÝ éÇ¢°°YG³¡×Ç k—΀YwÞŽYwÞîo\\ ºv¶uX»n= †80ÆP^^é±NC‡Dn^¾E´hµ0b¡ÑèÐ¥K'—åÅÇÇábõE@Mm- 1qÐh4øeÅJ¼öêËèÚµ³%°„Fg”üýÉç`2Õ‚K–¥z÷Ü}'^}å%Ü8m –ýò;A€«lŒ1h˜’¤qÞèâŠI—¡M›d|õõ·(¯¨Bl|HÐéô˜`ÉS«…ÈucL€ã¿ïÎÇ~Œúá™gžÂ½³ï´x뿨ãþ²=Ì×ÇÉñà Ú8­Oõóæ´’ƒŽëb¼¨„‹¤Î‡[¸t?Â"{ìB.´Y(w‹É *;p¨Ë„x(X~Ö«D£œÔí¿äLU1™™“ÿ©a"Ëê¯OÝ^( ¾gTÎ:ìbn&P8“ù£Þv»¨)óñ'ˆ5 ûpïVÿ% ÁÚu둘˜ˆY³nìØù'´:6nÚ ˜2ù¤¤ôÂÚuëm¾Oee¥€E‹¿Âe®Äø‰Wá² Wb¤kp¶øf™Vku:è ±Ðb!Xý€Ü}Çêõ±ˆ‹OB|Bââš@§—åM¼üZœ;_â0ÊêôbâmS{£Ñh aM¡Ñê—€øÄfˆOlŠØøDü²b% eËEÑê¦äí•g&’¢ÙŒ»ï²ôëÇŸ.„Vo€ hm߇ݻvźUËñÂ?ÿ@€V«óX7€A`*ªªñÇêõ¸ãÎÙ€Ë'·òà!¿Å%Ma·UÈßäÊBŽ‚Êo„@¿ dAû˜Ï9É.ÏeªžN[BU ·š4”–jf:jÜ?* ÷Š »l`*¬ÞnðQjQ¡DÇXÞIAÆh´:¬]»p÷¬;`4±ÿÀ!ètlß±&“ wÞq+`íº Ðhu`L@fÖQ@úðáÈÉ=ìã'“{9y'!Zääæ®¸|¢Õú$&$xlNo‰›CLœmÙœ»òêÄ$&&@§³,ù7v4àÈ‘c 4%C÷nÝ Õê¡ÓÇ@§Á„ñ—²²ŽxþP·9/çÖ=´êÛB%Ö½¿HCZß>ذa²çYq0fÍCBïÔhßþL?Ö5Ñ}Ý8—`0èÑ®Ý%4 ±ˆ‰M@Ë–­¥%¥Ö½ÇBÿêE«„L” Šj1áÁ(Ý/܃PP ÿ²eÁ)?ÔA'hYŸçÎ §å|Qê¸Å"¥Œn‘°’aÖ@ ›¶nƒ(ŠˆÇ¶í;`%è 1¨©©Æ®ÝbDúp˜ÍflÙºŒ Ø0k֬Ä —aÙ’ïðǪµèÖµ+**+ñêëocÛŽ]ÈÎ>ŽKû÷Ãß~Ž“'OãŠË'x¾Ì±PÐÔ-ÅãÛò^{ã]0Én½S§Žøù‡EÈÎÎÁ—O€$IøøÓÏ-âÎZfî]°ä‡/qôX6òòò‘”ÔC‡ ‚Éd»ïϳEt¡ªÀ¹„¿Ü{'’[·Ä%mÛF‰Ä&MPXTŒ>ùŒ $3îže¥~ützƒÅjÇ,íâàøeÅïÐé4ÈÈÈ„ÄE¤tï¥?.v]70 :sß{™™Gê’[·FZßÞ€>ùL5O•@¢*XƒŸ 9‚E•Ïï°ƒeŠQLË”*² ¬eÊù¼ó%‹”ûŽ kTؘ ”­¨úšíå[ì¿øŽ|Ë’znkA£Á… ÕØýçÀöí;-Qÿ4Zh´Zlظ°cç.T×!h44ètzÜ÷ׇ1wÞ$&&`Î_ïE÷î]P]]mYN,h0óö»±~ÃFtëÚ]:w½yUUUŽ~IÌq>Äkôe#‚ëòjjÀ%ÉvwÌ_ðŒF#&M¼ GfcúÌ;}<‚ÖbÕ’¸„òŠr,úêèt:Œ3:vÀ«VãêÉ×ãè± ˜]Q4câø1¸å¦i=j ¥WOL¿ùŒ7Ú²g—$¡}ûK0aü8ËÎÆ¦-Û¡ÕémÖ;˜³ÈñÕ7?â`FPQQá²nGŽå@ääæâ»ïBBb<.;íÛ]‚-[·aæm³°ì—ß¡ÑéÜû~ëöêÔ½?÷vGbÊ›"HLÁŸ¥~n\HÅ B7eʯòCt"܃ɪCÂÅ¥úq¤Z ÔñI‚(8øâUùcm5.^¨„h6A¯Al|"´Z=Ìf#ª/VÁX[ ­V‡Øø&–ÈuŒAÍ0ÖÔ ¶ö"D³`€F£…^½!€±¦µµ!‰¢eÃ_«ó¦Á Cl´Z8ç0kQ}± \aˆ‡Ágg±²´Ì]y:½¢hFÍÅ*˜Í¦úûN ×ÇØ6fŒKFc jk.Âd¬µmL,h,{ObâliÞ¹œÃdªEMõkk`6[ü·cÐètÖ¥yñÐh´–t/€ƒ#&&ú˜ØA1,›'K’dsL6›Œnë&˜¬iÌ&c}Acñ-3ÄZý¸„Ï$Ü+²PùWƒ@øO)“oXLÈÈ:UýHP©q6¦nA¥ZŸ(RQ'¢H@…Ÿ°8$Q„Ùd„Ä%h4Zh´:‚I’ šME³m£^K fûœ(š!Š–ý—A°|ÞjñE¢ÙI-Kü¬bEÐh Ñè  °äc6sVk‹ÞçXKÇòÀ9X]y‚œK0›M¸dó‘ªó!³lúk)Û–Ùd RÁ­ûzi¬ek]‹*k=¸(Ád6Z÷’êï&@£ÕB§Õƒ ÌÒ¯fÀ˜­ïì¿M¸“«fë3kÝаn`%3D³ÙšF²-Ÿ´õ½—¢*ˆÂŠ,TA™Gl¸t•Z§"m™_˜ÏB)¤T]%32õH¨0±HE‰Ã §¶`"|W¶À `Ö-˜¬~@¼~òßXpXC7Ø{`¶E} òu¼“êËhP>s·°ÚuyõÕl8•w–Ÿc»·[^9‹qÁX}?qK¼u}ç.oOusž¦nY¥/‚<ÀûX‘…Ê¿Dû’?²N\PyV$¨¼É1”(h‰Ÿ[AEÖ(5V*h‹¢KKv tê“ãIlXÇNƼÌ×t.ÊcŽÿ‘Qž?w¼¥¾ŒÉ¨­×¾NrêæoýƒóÜkƒ&¨ÈB%ópø£ 1°»&xÖ)R>)gêZ¶¥æ%}¬êX¸´Eð¥$"¢Ñe " •ÌÃáã?E‚Ê÷\TµÜ/ªUôùK©CN‘˜ŠN1|ë )‚ T#¬a¡ò>_²P©û›#PþSÁSªó s¿)R «T8ˆ¨UtL­õ&Ë„+[›þ8u%á†mo)‹£Ó™–,Tr‹ U`ÆÍp Hhë *Tá"¦ú$¦[!¦Öz“"è Q~¿³ïâŠ=0,p*Ŧ±^G›R¯…Šù3ˆÉ<Á°N1Eógþó~ZŽ‚½ÌÏûî` ²TN`†® ûK±P‹·ºRC¾ù¡Ú,Rò*¢‹Žªß¥G8©Öº¦}6~p¤ËˆhC¦ZÒ*u§3¦ð“ãõRB¥E•º—ü±å,QŘ·²‚JQù­˜Øñ¾;HP‘ RNP©]L©Ê¢£Z1‰B*|D”ê}ßH,„O÷°Ö¯œ¸ä/4*ÿ¾½Ü/ÜÅ”²‚Š)qý²Ó:l!%_F² Õ„…^¸¨b)y•7kT¤‰'¦¦¶„°‚a# IQ©¡íÒOT©}Ù-ù A¥ˆ„ ü’¿-÷  ÿF€ENt *b_-߯aSa'>ÂBH‘ˆ ZIQª H EÖ5P“HÓVLÉTd¡ ¦˜ N0 U‰©`,õ Æ2¿ùM‘R¶,ôKÔ$¢H@©F<©V81ÕÕH=¥DJ`T[e„†$áTþ!@ûŽ}ÁAÅýÈÃæJÈ©©6p¢Š,TêU*´P©Ø:¥*ß©,÷‹¬h~ÌNØ„äk8¤f)5,ñS£EŠù\ÕX.To…R×¶"ªko(Fµ ’hF~ÞhÎ9NäîEÇÎý!h´~wpUÔTë)©êƒR1Ê_ÔY¨Tfò_è(x‡Ãw*B¬S$¤BøER«”Ú,RjP³DE‹pbjjc„ÌíY—‰ˆf#òsöA§× ióæ€òÒ2äÿº\ ­ÎÁ­W‡ÝJëîOÑ÷íd¡R^ž„ã’¿E÷S—ï”:£ú‘ RªôЦB½Om)ß+  1EB*DíŒÅ"¨”(R N1kpâø^ÄÆÅ"©i’íxR³¦Ê+,âªëè ±¡¯,Ü‹­õ_P‘…Jmb*0ËýTÝ/$Ö)u/ó‹J!uþRê·J‘€ WzáÄÔЮ0Ñ , s&ͦ,µ5UÈÏÙ&MŸ˜Øè|bRyÇ÷ c—þˆ‰M ë §U¨" •2]þKþÔ.= —û8L:‹:Ab«-ñóKÍ…Ü¢CBŠD”*ª¢RUBbÉ+.^(é܃hÚ¼ââã]¦‹OH€ ÈÏÞƒvû">±9už7÷šì ‚£*(÷áû˜ÉëQŸö â ]t{9åƒüæž æÞÝ#‚ÜqÿÇQî*t“?÷ï²5ÊŠùöôz#¤¸eÈ×iÌ.kå_9R\î³äO¨4uX¥¢WDùVx8 (Uˆ'Õ §°Û¹8ÀÅGTc/¨,?‡‚“™hѲ ±ž—øÅÆÅAh-àô‰ChÓ>Mš¶¦NTø>׆NTùfQò¼pLžâ¼ñaîÏ—÷T+¿a=6‡y?…õaÉ÷»Î~ÉVõ–·ñä-»œY§¸Û,óõÍ ª@gÌûçÈ!5çÞ‹®hTj³H©"ØS¼à[lT-¤HD…´$šTKyi!ΞÉFËäÖÐëõ²?gˆ‰A«äd> I4£i‹K¨3Dë÷“¥ w:•u˜G9™ñúµÍ' •Ýøp鑌·¥~aè;~SJ-ñãÞŠ.ÆBÉ/”¾RžÛY¤B¾¤/ThÄSKNa­5X¸U8b`Pâùâ|œ?{­Ú$C«õ>ŒºN¯Gë6É(.ÌÙlD«ä.hwp#T¨%†V©"4¢ª¾+]¾ŒfÎÓ»ö¡òoyó%?¯DWøyfúÅ_Eá£y—¯ÿ‚Ê^¤RPùöèËF¡ÌÐâYPfkÍ+³ÓAØ^Lq»Á$°"K%‚JMKü"LL…ÔEBJ%mgáRQHaÒ‰EÙ¨(+Bë6ÉÐh}ß›J£Õ¢uÛ6(>[Q4#¹mw…¿óÕŸ\ÑÒ”žÍh}ê ¥—üù±i¥{A%gRÏäõ¬E,T2Öqoó•±öÍ+ï!·‰™oMô˜)Ýn|³j>÷Pa«Ë\»d±ÀŒ<.„”¿îYÌÝÐÉ )¯m¬Œÿ_6Ñ.¢Ôf‘R»€ WñÄB] ö;S_‡!œsœÊDõ…2´Nn A£ñ;OA£A«äÖ8w¶gÌF´ëÐ`,¢ï(ïró~2¦_Í}U¡³P¹TÜyÜ™Î`ò;0œÂ¦GÙ’¿HŒìì¨~¡ˆèü}¦˜M 1Ïw‹×õ•'¨.T•¢äÜ k.:XÌ‚ ¢Ž¸¸x4oÕRñÕœs”ŸÃÅ‹¨“ýD«A%Ï‡Ê AÅëÿ©T¼Aiܽ® ´…Š+(¦<æ«@¸ts=/—ʶ qßÞ%x4÷xÙÏ{ë”ï‘ýœWW¹¨~n­SŠèç¨×ì´*("œ=9¿|¸ãËû¡ƒqO çÜÚ÷A'.T• ¸ Í[4‡!¹}+AA…1†­[¡è;Ha¥æ ~ëE•GAÅíÿ±Äc&¾Ü.…oMhå+Ò-õSC®`„?Ç>ôÉÇK1îWBÏ9xé2æKíÜ—ã2,ž‚¾SÌ™` €ïk¨¬ÝNPÉîCI’ÉÕ Fn/À¸]óíEw8ù¸ò=Š_Iñ 4kÑ\V¨\‚ ‚ T-¬”TJ.ùküîžs÷‚ÊΞÕÀµŠ7@nC.»ž÷ù-¨dY¨¸÷aÇ=Z¨¸wÉË%y>ùOy©â¼öŸrzÐ;ÁæÞÅË˽ȼ­÷=åØ:åÚ2(Ae-NpÖ¯Ìö¼;ö(wX‚È8³)u©‡[ çü°Ûa¬­FL,í%BA‘'¬”ŸîÓæ¾®–þñúd.·›TÙ‰3wU— Ÿ£üÉJîc”?./O>T\Þ æw¾òá^õ‡wÊŠñÁ½ºÞ]:¦pg¹Ê)ÀQý_èìµ—œí£ÀïóÆêWCƒ«ÿÅ:tX?í °˜Ý^xܲ¶ŸkÝɧР‚ "MX…lÉŸ;ûdÜa¦Í] *Û’À{[9üÊ=ÎÞe¡jã/¤*YNDÜ7äQ¨ùtWÉðÍòn’ê±z<˜¾Sò›rÈ:Ö)ûç$Ж)æá b ß¿p¸ÝôºÞXåB` «™//‚ ‚ˆ"aÒÀΧºîE•3AÅído °Oøç³<à‚Ê’„y?åtk ñ¹¶üœ)¨| Fá1÷ïîuZ=ÿ¬SN³s˜Ñ+#B˜S}`¿)ÖÐ.X«K òhݱë[Ž:c’³À5ÌöLÜA`1ØçÂë–ÚÆ"æ˜ çaž– ‚ ˆÀ +Ê%n&Ǭñ1Î-Ò‰Ù ¬ºe=Ün}`C1Åë?ܨ(Îy@•T^…£óª¶²Œ#Ü»än*ç+䵸÷¶;¹u–ä0@¾S!ñ› PôÀz!Õ ?e-—sö44èsn7౺—*‚m…_Ýëf'YëµTcaæ88±€ÝÃAA„° þ¿26åtqÙ àâÊNP5²bÙ‰ûÉ`ƒì^†x”‚û2ñ”ï𣀅ÊÏä>Føc>ßvò"+½Ñý˜GÁ@ß)eëxD¿Æ ä²CÖph w˜ÞaŽOÙàHƒÈ€–²z‹•±¦ ZA`¢(Ò%Âk,c‡qÔVWÕ¿Ãav/yüq»˜€AA }¬” ŸÎäfv+gxA帿·TuKuLµU0èud©"Â/ŒF#ôzª/”—` Ñîbcñz_+xˆèç8JAD «€Eúó,¨\½íuج×~_RÞp#_³±1ºXzoLD&“e¹«(ŠAódŒA£Ñ@¯×C§ÓEvsÆÚêúe Ç,f¿Å0o°A±]6Öý¯ÜŒ¤«‚ "„U€Eó6»¹ g°Mp¸MYÕû>XœÐEÐr"šDUÓ¦M´m$IBMM ÊÊÊÀ9‡^¯\]Å9$ÑìèOe[ôg‘T7vg­"AEAQ%¬üJ!ë°7aØy£Uö×ÛºmþKD•¨JNNc ’$A’‚wïÇÄÄ 99EEE`ŒE´åJ’D»à9ö£Mýïõû[Án9`ÃqŽÉrA%‚ "2”UÌKQåà_Å,õ« ZaÓ[u–*8ìOE!Õ‰H§ÎRU*<øbòuARRRDû3:Œ)u?öcRëxƒÊÓ@(÷4AAaˆVIAåu>n’ÖOWl;ÉXJvgIP‘ç–ˆ—j°éõz›—£QÄu:8³Z¨êijcZçle•Hn” (‚ ‚ˆǯÿ‹ªú˜êõµáu›[=­l®Vœöá$¢J\©AÈ0Æ¢ÂBÌíUÝ ÎmcQ½Ø‚cx=¢A!h½ó}RHP1ûÉŠó¸mÛMûIݤd±"¢M\AQUV‹UÝKVÄ¢.¬ºõ}»¸oÕ#‚ ÂÏaÅX D•»S äNf=.NA(¢­˜-p…ÍÊ…uªnÄ"ÝKAÑÖ¤´•ãáz Uý2œ†¾TvÖ+ÀòFô&ŸˆðI¾ ïïÈ渣uÊ:î¬~ç*› r³Ÿ•×"AAa‹pQå§S·³œÕù@Ð$Ba¸QY–r›UŠ{±| ‚ ‚ˆaÅœç7>äE æîóœ—ê¿:Ï*{ß,RV„o´iÓ­Zµ¢Ž \Œ=u^ž.|9e[ìXãÁ•¹o ‚ ‚, ‰‰Ë[kû¢w2ðtÄí\Âí)ëŸu *µ3jÔH\sÍ5èÖ­Z¶lóçÏcÿþX¸p!Nž<2o¸a{ìQüç?o`éÒe.ÓÅÅÅáë¯C’$\}õµ0›Íظq=ŽËƬYwEÌ5ÈÈÈ•®OŸ>tújXƒuQ+œ„aw;’ˆ"‚ ˆˆFëÌBåÓl€É9åƒ8ãÎ';Œä•*ÿ÷ÏáòË'Áh4!77çÎCÝqÕUWâÓO? ykkk‘““ Q4Ãd2Elä¶Ó§OÊJwùåWªº999ˆ‰‰Arr24 @E¡¦¦]»v ¸¦â`Œ»ÃìÍO¬ñ!ï‡M‚ ‚ ÂNX©UTñ†Sœú“ É*U2mÚõ¸üòIØ¿ÿžþ[®chÓ¦ B^GQqÏ=³m×MÖ uRPp;wîÄý÷ÿz½PSSƒÅ‹aĈtŒ9:0Ût÷ ¦˜’C&AAá,¬T!¨ÜY¨l‚Š;&!m¥*4 î½w6ŒF#^xáE›¨,QäŠ*ƒÁ€{ïÑ£G£Y³¦8v,sçÎÃáÇeoÚ´)}ôoÓ¤I"þýï×1pàœ={Ÿ}ö9V¯^c+ëþŠ¡C‡@§ÓaçÎ]xï½÷PZZ¸òÊ+0cÆ ´oß•••ÈÍÍÅSOýF£)d×Cí–(¹äää`èСX¼xn½õ6p,Zô%úöíƒ'NHSñ#ç‚u“¹ÔV$¤‚ "J…•Ò¢J •sQÅIO©švíÚ!!!ÀÙ³g=¦õÕaøðá8xð Nœ8‘#G`îÜÿá¶ÛnÃéÓg<žóÍÿ 55(--Czúp[Þ«V­B÷îÝ0lØP›°>|Š‹‹±ÿ§õINNFqq1vïþ#GŽÀ‹/¾€³g‹‘‘‘wÞy;wƬDBB.¿|ðÄO"%¥ž{îYá‡~D\\ CHE9>V=öw¼ÿþ{HMMÅâÅ‹À9GïÞ½QPP€Ç" e[–Q}àÄ!‹AAD“° ¹ r+¦êe‰)õÓ©SGp°T=øà̘1ÝöwfffϾiii>|8vïÞ¿ýí1pÎqï½³1kÖ¸ùæ›±zõ·ç×­[‡ÔÔTìÛ·>ø8ç¸çž»q÷Ý–à«W¯Áý÷ÿC† ÁÂ…_ K—.HNNÆ×_ãr¦üü|ÜÿàœãÆoÀ£þ 7Þ8 Mš$¢gÏX²d)ÞxãMÀ'Ÿ|„ôôá0 èØ±#cÈÎ>ŽŸ~úEEE™ä{íE®Õ¤IW¨ú¾*..Æœ9â½÷ÞEjjªµm§ñÐC£¨èl€Juc™b>iAAD4‚RáÓ½Žì4©Œ‰cÃÉ%·L8é'ô?uËâZ·nm;vøp&~ýu~ÿ}¥ƒ@èÞ½`Ë–m$ œsÛ²»Î;{<ß¹sgÀöíÛmçKKKmeàСCHK님˜ > €Å’e/Pêê µµÆFeuìØ :u\wÝTlÙ² [¶lBJJ cHHHÀλPXXˆQ£Fâ§Ÿ~ÀgŸ}ŠQ£F*Ú·j#˜÷•ÙlÆ©S'Q]}ÑV~mm-Nž<³Ù¬xyÖå¾o⾈*Š·NA‘„Ö7䣅ÊcrfçjÅýÌ‹6¹¹¹€^½Rжm[`Íš5X³f ôz®¼²Þ2 Ðhê·R3™Œ€êêjçëN¸ <±jÕj¤¥¥aÀ€K‘žžŽ“'O!+눬¶˜Ífë¿&TVV~ùå|ÿýéJJJ In¾y:ÆŽ‹‘#G`üøËðúë¯aöìû™™²ë¡vK”\DQÄ7ß|ž={Ú|ªºv튯¿^ŒéÓg&øˆ Ëóz¢‹ ‚ "Á¿y€—*™¢ŠOªªªðË/¿@¯×á…þ‰–-[ÚÎétz‡´yyy€±cÇ@,·azz: ;;Ûãùü|Ë2·Q£FÚ&Öñññe¬]»¢(bܸqèß¿V­Zå¶þqqqÐj-ïê,\99¹ÈÎ>èß¿?òòNàØ±lÛ$IÐh4$ kÖ¬ÁK/½Œo¾ùŒ1tëÖ-¤×###CÖÚùì³Om¢jΜ‡0gÎC8qâºwïŽÏ?nø~ßlLÍýAADZo¾è•÷£jèIåE~ät¥JÞ.RSS‘––†ï¿ÿYYY$Ž>}z¨÷¿Ú¾}Ž?Ž´´4|øá|`ܸq(//Çwß}ªª*çóóó‘ššŠ?œ3gÎ`̘1u)))Åž={pÕUWB£ÑØ–÷¹¢]»KðÉ'#77cÇŽ$IøöÛïmÛ¶!==óç€ 6 cÇŽ¨¬¬Ä»ï¾‡.Å3Ï<Í›· ºº×^{ $I‘#Y!½gÎÈÛˆYí–­´´¾8zô{ìq”””æÌyï¼ó6úö là Ö@qÎeì{Æ\€$¤‚ "b¼×GJ§PN ê ²²wÝu>ø`>²²²Ð­[7tìØ«¯¾†gžy€ey×#<ŠU«V£mÛ¶8p¶lيٳïEyy¹¬ó=öwìÚµ :uBÇŽðüó/àâÅ‹õùãUÐh48v¬Þ æŠo¿ýf³ cÆŒF^Þ <úèãÈÎÎ<óÌsøê«¯‘€Y³îD—.]`4Z–&ZB½bâÄ ¸å–›Q^^Žýë;–M7„Œ1 ³fÝeUÑ\‚;#FLP1Ÿ>Á¼:EADäÀ†ŒšÂ²•9eÛÅ¡¨û[ÿ_çDÎÁ!Õ;–K8— q \²«8—‡„„8º’DÄÂ9GUUÚ´i£Šú"!!A†å&<¹p¡­Û§@0&€ BýïŒY×XÚϳ¾$b°³wúˆÈ:¸¬ÁX‚ ‚, ‰‰¨²úÏ+6xáÓ}ð¢r»¿•Çͯ‚ ƃ¿”2 ‚C×s/g ÌœòbÙŸÛe3õ'ie AÁRNF™{[y™” xdÎ_qç­3oÞ¬¾ýòs$&$ÈÊçµ—žÇø±c¢¦ßôz=t:ñ—Ÿ·ÜxƒÏŸ'‚ðJXn“_eòò¡vA~*ïNù‘” ¹ëö[1óæ›ŽíÙ»Çsr|þûâKŸ;ï¿:ï¼?€eÓöÞ};víÂ' ¿´L4´Z|:.žyþEœ:}Fv]µZ-fÞr ¸Í›5Ãù’ü´d6nÙêÐîßV®ÂØÑ£Ð­[W¼ýîûèß/ —Y—=^1i~\²?.Y†çþñöìÝ+ÿ\Ú/ ·Ü8 íÚµCYY¶íØ…¯¿û·ÏœîôóA…•òÑþdæÅáÆª^PDA¡U2N{Ö^Š™¸Â#ýûõÅÇŸ/DII)¦M‚Gžƒ9{f³Ù–F£ÑàÑçàD~>~øy©¬ÏN¿éôëÛo½û>ŒF#î˜9ÏýãI<öÔ3رk7þzï=’$¡[×.Ðê´p饀UX¥ôìÒ²2œ:}Æ«ºšÍf”––á£Ïâô™3蟖†ûï½ÇŽç  °Ð–nÖí·â‹Å_cáâ¯QTT„‡2‹ššZ,\ü•Ó¾JˆÇßÿö0¾Xü5¶lßæÍšA¯·øT}ùÕ7?Oá yûXm?*ü¨hbBDÃ<Ÿ1H’òzÈÛ 7²õS"%9_`Óæ­ÈÍ;òŠ |õÝ÷HjÒm’“í̺ýVÄÆÆâƒ>‘õY½^«¯¸ >ù'OBÑÙ³øßü‘˜€ýûcÿÁCÐjµèÚ¥ ­Ooü²â74IL@Û6ÉV•†»÷xWW+ËWü†C‡QZZ†õ7áä©ÓèÑ­«Cšï~üë7mFþÉ“¨µî/è‰fÍšA«ÕbϾý¸páNž:…ã9¹táZ_E‹òKþ¯¬T4)!¢é ˆ Àd2Á`0„´F£‚ DM¿ËæçÁ†Eãáïè%µµµ0ˆ‰‰±»lÌhtíÒ>öw˜L&YŸm“ÜLp"?ßv¾º¦Ù99hß®-vïÙƒ½ûàÒ~}‘}ü8ÒúôÁÇŸ®]º ­o¡_ß¾ødá^ÕµŽÄÄDŒJŽ^={ 11m’[Cß`4ÊSöœ>s‡3ñÖë¯`ϾýX³~e¦{ ÿæk¾ äJºp;¡ AÐét¨¬¬„(Š!«ƒ(Ѝ¬¬ŒŠÄÞùL‘eŠ.^tR=66¢(âbuµËÏ6 2sèðaT×ÔàªË'y,·î³:‚u#l{ÌfF«8Û¹ûOôë۽͛7éӧ±gß~ôïÛIIMдiŽe—Už=111xå…¢E‹æXµvæÎÿù'O)Ò¯’$áå×ÿƒ¿ý_˜L&üý‘‡ðÄß¦Ž …•‡W³^íH¥ô~T4A!¢Æ4 A@NN***‚*°DQDEErrr 4M/”aâ «ÉÏ?‰Þ))^h¤öê…ü“§¼ Š‹Ïáýy 0ùê«0dÐ@YŸ),,‚ èÕ³‡í˜F£A—Îl"gÏÞ}èÜ©# €ƒV«ÏÞýûÑ;5ú÷ÇŸ{öùI´k—ÎHLHÀ¢¯¿EÆáL””–Bâò—Dk´igfaÞ‡ãŸ/¿‚Áƒ¢y³f^}ž ­ÃdÀít"D~TÁ‚ X6¬lÚ´)Š‹‹qúôé ù\ ‚`+[¯×G³Äõ¬·”ÉI›víBRnáéÓA+kæ͘ríÕxìáñýOKP^^ŽÔ”ž˜~óxÞ¯óÛð ¾ÿi ¸ï^<ýÏPXTä6ý…‹±aÓfÜ{×,üoþœ/)Á×_‡’’ÛÒ¹êšddfáæ®Çâo¾”––¡èìY\7ùZ,\´Ø§¶—••!..—Oí;w¡_ZtêØ›¶lóøÙó%%5"M“’P]SƒÚÚZ‡ó ññÞy.2gú”çÏË–£{·®xü‘‡ðÜ /yLÿÉÂ/qëô›ñ䣃 °ÿà!¼úÆÛivìÚûîžåà§ôçÞ}¸öª+|ö]:SPˆÅß|‡›n¸Óoº3ãÀÁC²>»rÕ¤¦¤àÝ7ÿm;vaþÇŽÁ:âãã0lÈ`̼å&ÄÅÆâLA!Þ~ï6ß3OŸ'‚pú]?tÌT®€¤’œÂ! ·ßð—Ã2OäÖÿ[þÎÁ¹Î98ç$ëï’d=.¡ü\âbcèJQC¨6çAU]]‹ä©`‚Æâ[b÷/Ac`õ;a̺@šYGÍ"ÔSЋÌëСsgº±} ,VA„²$$&¢ª²2 yký”SÞ *9yqÉfED-Ñî<ø ¿,SLþ€IAD˜£õùK?DûQ‘ËAAT>j0Å2$‚ "ü„UÈS8UJŽŠ<«‚P›örmÌbîGBÒYAqN!+GA¡b ‚ B ¨|9KaØ ‚ "rÑ*)¨üË«ñ"?¹ËþNœ8AW’ EhݺӑɵeÊÞVAA°’'„üUÌ·ùˆÝºÀ~ýúÑ•$B ÏÊטO§‚ ‚ˆ6a¥” bòOÈÚ㊜­‚:ÂÚ&Qzq¬P8ôP…s'‚ "EX)íÏe>\~±$¨‚™ ’1.&w"ʹå†)è×7Õåùï~ZŽý«¦¾ãF§cÒø1¶¿Ë+*Ptö¶nßcÇsƒZ—‡ÿz7Öm܆ƒ™NÿDÛ»vîˆO¿üÖ«ÏL‚±#‡ã­÷ Öh´‰Áß¹?-[ŒÌ£A»~/ƒŸ~‰“§Î8œKíÕ“ÆÆ{|J&AÈVŠöçá„G?*—{[‘8AAT>ìkå:W¿÷ü´ì7,ýe% K玸ù†Éø÷[smçM&“êêœ{â$}ý#˜ÀмYSôêÑ ·Ï¸6oÇšõ›é¢6`ûŽ?1tÐ¥=rV¯Ûd;>vôpž-š¨ªÃl6aêµW`Þ‚Ï!qz“MÞ"ÈU²ç>.ý“ŸMF‚PÖ’ûQæâ/‚p…ÉdBMm-jjka4Îm×ÔÖB”$ÕÕYEÔÔÖ¢ºº§Ïbí†-øúû%7:­[µ¤‹ÚQ’°båZŒJ‚&‰‰€¤&‰>d V¬\ôú9–I’>l0]‚ð­'!¤\´¿Æùf¥¢7(AIHùhb¾¢á5WL‹”žÝѤI"ÊË+°~ãVì=aKóÀ½w"#óz§ôDëÖ-qþ|)–ÿ¶ 'òOáæi“!I~Xò«-ý¨ô¡èÚ¥#û³Ç¼åy$§Ïbà¥}ñûªõ–§‚1ŒŽýû">.yù'±|Å*”•Wx¬3h5\>a,úöé­F‹ŒÌ#øíu0ʰâ¹kó_ý ÈuñTG1syù§0ñ²QøiÙo˜0neÅé3…õcЇþzö‰‡ñÕw?#÷ÄI@‡v—àî;nÁ‹¯½c«Ï¶»1àÒ4´o×_}û3²sò cIÂÒå+q÷ÓqèpÊ+*}ºç"Z\Ï&”ˆöç÷ZÐÛ]‚ ‚.¨üÔb4vÁBETVVaé/+ñö{ °qóv\?õj´lÑÜ!]î]±ôוxóÝùÈË?‰7]FƒƒYèÕ³V¯¦öêŽCY²ó–é3Ÿ›4~ R{õÀâoÆÜ?çwß1‚ x¬3L¾ær4i’ˆ¹ bî‡ Ñ²e \>a¬¬º¸k³’¸«C~ý} ú÷냴>©èÛ'«Ölp8/§¿ütNœtàåtA!öì;ˆk®œè×=GQ.¬•ì`2„¬—¾~¼&‚•«ÿ‘ÐO?-‚hȦ­;q<÷**«ð羃8{ö:´këfßþC8SP„ .âÕÍ›áXv4‚€,ãbcÑ®][>rLvÞr¨¬¬B\\,@§ÕbÄðÁX²üw-FIi¾ÿéÄÅÅ¢WnëƒA—¦á—ßVãÂÅ‹(¯¨Àšõ›‘šÒCV]<µY)\ÕßÅçÎc×î}¸å†Éزm—ƒ¥HnybõºMØ³ï ‹Š=úç­^» Ú·u™¿R÷ADÚ†“ÿ£ýy)~\.ûãò“AYP5VJ^¡qq±èŸÖ:´G|\,Z4o Nç2½Ñd‚Él‚A¯‡Y‘y$)=»ãDþ)ôêÙ 9¹'PSSëSÞ®HLLÀ¹ó¥€æÍ›1†‚Â"ÛùZ£§N u«Èt"pìëlhÑÃý³o·×hèuzYuñÔæ@`_WlÛù'Ò‡ ÂÎÝûŽûÒ_NÛm2Ë®o­Ñˆå+VcòÕ“3/ßï{Ž ¢HXy±ìOÉÀN¹—MäaE¨‘äädˆ¢ˆsçÎQgD°jlÓ÷ ¨È"E½^¿Î¾™G±s÷^œ;_Š7MõüA»ˆo3²på¤qX¹z=R{õÀAë’8ŸóvBûKÚâàaK¾:­c`ŒÛÕCE˜Ìfu®©©cÀ¼ââÅjŸêãªÍ ¹öíh×¶íïUk7bã–¾],QöDQtø·ÙýÅ”pgÅ i¸l̇ðëJÞ⦩—¾_ºÎáxë–ÍpiZw$5IÀËÖ;ô§ý¹†Ÿ 4¯<ÿ”í÷ç^ú8çN³>EgÏaÅÊ5|ñ<€g_üwô +¥ƒSÈUNɦ`¢Óé0eÊdLš4mÛ¶Emm-Nž<…Ÿþ›7o Y½Þzë ôíÛ×鹩S¯GMM-Ík¯½Žõë74J7lØP¼ôÒ‹ÈÊÊÂ#<게{î¹§NòºŽS¦LÆœ9à½÷ÞÇÚµëðÉ'AEÜ|ótÔÖÖúÜ^Î9Š‹‹‘‘qŸ}ö9ŠŠŠèFU‰ òꕉ)"ˆ´»¤ ââbñûªuvówï¾KÏÅMÓ®ErëVèÚ¹#~Z¶B±¼ WnhÞ¬)þÜsp®¤Œ1tìØyÖ` ‚ à’¶ÉØº}·ÇüJKË`4™Ú³;þÜwЧ~sÕæ†ÌÿøË_c9ýe4™«xÙËW¬Âƒ÷ße‰J©à=´1Ü*Fcb è×»+:¶Ok @Ý‹f’[·Ä]·ß‚¬£Ç±|Ŷ@)®ÎQßYðìõèò­«s7mïý¨ÜŠy]/Bÿûã¸ï¾{ѪU+dff¢¬¬ ½{§">>^õÛ³ØR:ö IDATg6mÚäðÓð-ÞŒÓ>ÈÓ§ß´zÖÖÖ"/ïòòòüÚSf÷îÝØ¶m;4 .»lÞ|ó?ˆ‹‹£5¤‚ÊKËID¨¬¬BŒÁ€aƒ >.ýÓz£M›Ö^å!Š"2³Žáš+ÆãÄÉÓ¶%q¾ä-ô: z=’[·Â˜‘ÃpÓõ×bÉ/¿£Æú⩦¦{öÂu×^vmÛ !>×^5å•8ž{Âc}Í¢ˆÍ[w⊉ãЭK'hí/i‹í.±¥¹x±Í›%ÙɆ»j³‘Ó_'òOaôˆ¡hšÔM“š`ð þŠ”]V^õ·aìèt¯î¹)×\Žk®˜À…q¸Q€Û¦Oèô¡ï³:¡ÇCJޏjÂ0têÐÆaÎàîQOJÏnxä{0fÔðFÁWìÏÕRáQ¾ÿ™ÖãìB¾Ôwy1ɹ»I ·|¦U«V7n,rssñàƒÃl]J ÓéTó@ÌûGkRçΑž>[·n³KKë‹Þ½{­ž’$áÁR ½ópæLbbbðÖ[o¢{÷n¸ì²qøõ×tÆLZ)—ÌõÇ鋜ðsçK°rõzL¸l&Mƒã9yÈ>žëu>3²pÇÌBû’w—Îñü3AâçÏ—àLA>øø œ/)uH·ü×?på¤ËpÛŒ =ž‹…‹¾—]ßµ¶Àd2aòÕ“Ð4© *«.`Ãæí8yÚ²dmÛÎ?1mêÕHnÝ ßý´¼Ñ߮ڬV<õ×o¬Ã´©Wá‘9³QQQ‰=û*¶ÏÙÖ»qi¿>&û¾hšÔf³åeh³¦I0›ÍC³¦MQRZ´~;òR´lžäõ9½^+&XBì{sŽ„•Û‰Bpü¨<Š*ÞPaÞÒ²eKëÃ`€N§³ «†½^Y³îÄðáÃѬYS?žƒ?þYYY¿ýö+~ýuôz=† ‚ššj,]º ‡ÂìٳѣGwaîÜy8xðZµj…Ûo¿ýû÷G‹-pîÜy¬Y³‹ɇ/“É„™3g:«éÓoAYY9š6•7XÖµcéÒe0ô><¢hÆÏ?/Á÷ß[ö3IJJÂü\ ³ÙÜh‰Þ’%?¡¦¦Ó§Ï´¥Ÿ=û 4Z­þ¹ ,@YY¹ÇúÔÔÔ`ÕªUèÞ½Ú·o/û:,Z´:´Ç AƒPZZН¾úk×®£›=zËë )/¾œt:ÄÄÆÂ`0@£Ñ@Ðh Š"$³µµµ¨©®VuþJs<÷„mO¢†lܲíßϼ6:Ö0¯âsç!JR£@žò¶gý¦mX¿i›¼ñÛlÆòßVaùo«|ª3çÜmÝ22"#ó¨Ë¿ÝµÙ¶]NŸ7¤¬¼Â¥oЧþª¬ªÂÂÅŽÂtÃæínë㬠Î$ ÿ[ð™W÷…ý~` >]dûýýùŸÒàFD427@ðÑÊ +•wóšœøC^^*++Ñ®Ý%øì³OðÀEjjj£tÿüçs¸á†i(//ÃÁƒ‡–Öo¼ñ\rI}HÕk®¹))½päÈ´lÙùË}x÷ÝÿòóóÑ¥K<÷ÜsÐëõEC‡E~þIlÞ¼±±1¸í¶[qíµ×4*û/¹O?ýÛÏĉ¥Y¹òôèу[vˆïÖ­+Œ¥K—zÝ'S§NAZZ233ѤIÌž}Í÷éå—_ĸqcQPP€£G!%%Å­P{õÕW0nÜ8ìÞ½Äøñ—áñÇ—ÿP Sûùóçe_‡Ûn»íÛwÀÞ½{Ѷm[<õÔ“è×/nv¥•Ïg$ªd¢ÑjÑ´ys´lÝ ‰‰Ðéõ¬ËO4 tš4AËädUæ®ôNéðÈxÔf"Ølز·YÏäž#1X¹f>YøµËsŸ}ù uì-V2—ýÉŸî… ÜX¨NR°u%¨®®ÆsÏý÷ßRSS1uêL:YYYxå•×pöìYôîÝC† ÁÞ½{ñôÓÏ‚sŽ;ï¼3gÎÀu×]‡ùóØ&ÿ÷Ýw?$I¬YwbÆŒéøå—_1wî<À‚ sçÎèÔ©#Ž˶Yu ]»Kðé§Ÿ`È!X¶l¹C‡âðwII V¯^ãplÉ’¥˜4i"fΜŽÝ»wcúô[PYY‰•+ÿÀwÞáUŸœ?³gßI’0{ö=¸é¦ѯ_zõê…ƒá‰'žç·ß~n»íV§ù :Ý»wï¿®À{ï½xÿýw1dÈ` ·Á-cèÔ©&O¾’$aß¾}²¯ÃéÓ§ñàƒA’$\}õUx䑇1eÊd8pnx„”Ï+Ÿ™Ã¿„< š6oæÅ¦§jÊ?œé×7»÷ 6çYÇò‘w²¨Q€ wçˆzœ¯pvŽú®NX)íÏQåò¤Ì½­”¬,üío¡ÿ~˜0a<ÆŒƒ””<úè#xúégÑ­[WÀλm~W6lÄÌ™3бcG[>f³Ù¶ŒïèÑ£¶cöåtîÜ €bòäkÑ­[W4kfÙ(±I“ÄFõ“±¯¼¼+Vü†ë¯¿W]u%F…¯¾ú555^÷‡};²²Žmmݽ»¾ÊË]/é«KÍ5Wãšk®v8—àRX}öYýòI’ðÅ_";û8&O¾VÖu¨©©µÕëÖ­x䑇mK ‰à*2æ]rŠ>&ÍZ´ÛüÙ͛ᒶmùõÔf""©©©ÅÎ=™ÈË/Dÿ¾ÝÑ4)AÖ¹h¦°¨¿ý±¶QHuWçHXÕ « *.ëc$¨Éþý°ÿ,Y²|0iii`Œ!&&lΪ`2­ƒPKqÒQ¬÷JIIÁ+¯¼ŒììlÌ;……EøðÃù~ÕÿÇÂäÉ×âᇂ(ŠX¾|¹ß}"IõË´Z­µä½á®ªª¬\¹K–8.I,--uù¹;w¡¢¢ضmŽÏ¯ƒ¥þÞ†~'|RŽ1IHyF«µ½h ÇüÃó%¥xî¥S›‰ˆá‡eë?{®«Öïòú\ qæ[ʽ Ü•íêœ$IQ½U‚ò¢J©ðéœ"¤¦¦:¼]¨®®ç¥¥¥àœ#?ß²ÓúÈ‘#l¢bÈËò¼œœŸÊì×/ ‚ `Ë–­Ø±c§"êcÍšµ+Wþ!+@„7œûÛ·oÇáÙŠ÷Õ¾}ûpêÔ)ôìÙï¾û ‘žžî2}VVvíÚ…!C†àí·ß–-[о}{TVVbÁ‚½._îuhÙ²%æÍûŽËƈéE?üðÝì K)ÙA( T²¿„t:¬–ÙpÌŸ ‚ êœÍ|Ûä׋ä>Š3BJJJðí·ßáܹsèܹ:vìˆÜÜ<¼ýöñÖõæ’$á駟źu둜œŒþý/ÅŽ;ñðࢢ§r7oÞŒeË–C«Õ`âÄ ÈÊ:‚½{÷ùÝžÒÒRlݺ eeÊï!Iž}öÿ°gÏtèÐíÛwÀë¯ÿÕnB1¿üò+øá‡3¦£S§N>o,÷:œ?çÏ— =}8òóOâ™gžµY»e•ÌÄ4ŒyILllXçOA¶iÀðqÓ¸,I£x` î`“²øås‹ƒ>çàv¿K’d9Æ%pëßœKà’„Òâ´m“LW’ˆÎ7#Ö}¬ŽÏÁÌ¡Q€Â¢³hÛ±/AÌú¯ €YgŒY~¬¿ ŒY²úг3/CÆÞÕèйsÔöw‹V­ Ó땽†§O-‚ "¼HHLDUee`æe!UòS»OAo† ‚~[¦È´U‡Æº‡T¸æOAuh½AÁTòö·"‚‚bþdàÍèE]«pP‰ºm ‚•?AAÔ!\TùíO^ šžQå·"£QË¥Pq-ÓFßT:¥ó#‚ "­çI„ŸÑþ¼œ85FÙ”^„Ã$òŠ+®¢Ž„˜b¾~°þ_Ú×Ê3¢(BPp¹ž(ŠAÍŸ ‚ ê|ý’D´?ÏF2š…¡~%æ[tÕèDé¬ 6ÎV:Úx› ‚p/¬\†PgÞ */—ý1YÅÓ,„ 5 )kD@xpÃò˜ z©q³uù:‚ ‚¨V2•¬y†û>æE³‚ Ô&¨¼-is‹ÙdRL¬ÔTWÃl6,ÿê‹aöqO:‚ "„•Ì)‚Ç7±^L5˜»E •êé§ÿ•+sù3~üeªªïŒÓê·hÑxå•—1pàÀ ×eÁ‚0vìX—¢í¯¿þª×Ÿ›6m¾ýöÄ6ØŒ4!!?þø=FÔë÷ûï+’’Òè܈éX°àù|SLžá‰¬S^SYQáwPI’PY^Ðüµï AQÂÊAåôcíÏo¿ý¦M»Ӧ݈_| 555¶¿§M»7nR]8ˆiÓnÄ7ÞŒ—^zYYGðÒK/à¶Ûn¥§Æ Ë–-Ã… U¸ù曎OŸ~ òòN`óæ-A­ÑhÄ#< AáPÓÑ)¨¼°L‘ò ÑlFYI‰Ï¡Ì9ç(+)qXB±üX ¢°b¾ *­TN2ïg%á4‡©­­Å… páÂTWWƒsnûûÂ… –®¨b¢#šqáÂTVVâèÑcøòËExå•W1sæ têÔ‰žœ˜ÍfÌŸ¿7Ü0 -Z´´lÙS¦LÆ‚ ‚^Ÿ;wBE\wÝutq‘VnNz=‘IËÕ8Yrþ|£péž$ eçÏÃè!¨D ó'‚ ­«¯}·sù©]§ðÓ+Ò¢®ët:Ü}÷, 6­ZµDqq1¾úêk¬^½Æ–æý÷ßÖ-[0bD::uê„Ó§Ï`Þ¼þŸ½;ˆªZüþ=wFYAAÜpÅ43åõr·LŸO[LËg™©e¹Õ¯ÒÌ×K{™¦¥fVn/Ë´Å\p_°RL wEÅ AQ@ö¹¿?€‘f»ÃðýÁÜåÜ;3—Ëùιç\œ8qÓ¦MEQQæÍ›¯]þ™gžFÇŽ1kÖGFË6Etô!œ?ýúõÅŠ+‹“¹$aøðaèÛ·/ÜÝëãĉ“øê«¯‘’’btŸÀÉ©^~y{ì1Ô©ã„`Ù²åÈ-7²—>•=çÿûEÞcû¯fâpâÄ ¼ôÒH|þùB¼øâ¿õÎ;ÿ“ #¯×/¿ü„ÿþ÷cÄÇ´nÝsæÌÆ!Ok÷'<<}ûöEëÖ­0kÖqäÈ=ÁXƒE‹aîÜ9ˆŒŒÄ­[·,>梣£†ÀÀƸ|ù2¾ür BCCñä“ýáããƒcÇþÂÂ…‹p§ä2,kÞS{ TÖ- ðI¯–(ÈËCÚÍ›pswGÝr—Õê“›“ƒÌ»wMnI²¨ü;w8Ä:™D*ÿg¿ÊZ©t˜>»£ŽkQPP€´´t,^¼£FÆÏ?ÿ‚7ßœ‚Fé,×¹sg,^ü%^ziNœ83¦£N:ˆŒŒÄ#tѹô+,, ‘‘‘&—mгgÏé¬÷ÒK/",, ³f}„‰'A£Ñà“OfCUæ¾1†ö&Lx>>>˜0a"&L˜ˆÆãå—G™´/•=g%U¶ÿå}ýõRôêÕ=zô@÷îÝñí·«tæ›òz3~ükøí·ßðæ›ÿ‡“'O\îüùìÞ½ãÇ¿fÕ1×±cG,\¸£FÆÕ«×°xñhÐÀÿþ÷0aÂ$øúúbôèûï™5ï©]'-«[§ŒM¥âŠ‘žŽ[7o"+3ùùÐA–ehŠŠŸŸ¬»wqëæM‹.Ï3V~Aiù))•^^HDDd0X)¨ –gE*ª _]£G!-- »víÆåËWЦî{qþ|22îàÿûÀáÇ¡V«Ñ¶m[@ýúõѪU¢£™\¶)ÒÒÒàî^àää„§Ÿ~ ‹-Bbb"’’nàÓOçÁݽ>ºv}Äè>»¹¹¡_¿~X²äkddÜAjj*V¯^°°n&틱ç¬Cû¯ÏÕ«W±}ûvL›ö.֯ߠÓRdêëeÌ÷߯ÆîÝ{péÒ%£÷Ôùî»ïѦMk<òH‹¹½{Ã… ‘‘‘ï¾ûBüúë$''ãúõëØ¸q#ÚµkV¿§v¨Z‘aÊt…Ⱥ{i©©¸™œŒ”¤$ÜLNFzj*²23­ÏPùi¥å³?™I]}7ù•Í*O¶lõÅÝݽzõD»vmQ¿¾;üáììlpùÜÜ\äååÁÅÅùùˆŽ>„nݺâĉxä‘.8vì/deeYT¶!ÞÞÞ¸ví:”´l\¸pQ;?''gÏžE`` €èJ÷¹Q£FBà‹/Þ? Õj“÷ËØs¶…²ûoÈæÍ›1dÈ`lÛ¶]gº%¯—þ}0½¯GNN–,ù &¼±c_µú˜ËÈÈ8;;•™vGûzXûžÖì@¥¿eªVZDDDT›‚•é —°°•Êp *­¼8V²rqqÁ¢E qàÀlݺ ×®]njӮWv”«ÈÈHŒó V¬X‰°°0ì߿ߪ²õiÕ*û÷bX§N¨T„:ûQPPˆ‚J>M.]6;;BLœ8YÛ?Ç\†žsy‹-@Ë–-µ¿ývÖ­ûÕ¢mY¬°°¨ä»î'Þ¦¾^B([ã>pà üqŒñΜ9cÕ1§o×Ê>%ÞSGI` UDDDµ‹Ty=ÁÂ~T”cpI G ¬iBBZ¢~}7,_¾ý4 F¯úóÏ#ðòòBpp0|°ŠQ¬lxä‘.hØÐ»ví$%%AvíÚj—Q©ThÙ².]ºd´¼””däææ¢[·®¿n†žsy“'¿‰þ¡ý²4TYÔ×+77nnõßö’%_aàÀ:#:*u\(ýžÖŒ Uy¿)aújDDDäpÁJ©‘!¬(Ç«BZZ:xàüãááQ|yVóæÍÍ*£  øÒ¸×^‡S§Nk/‰³¤lIR¡nݺpqqAÓ¦M1tè³x÷Ýw°hÑ"dgg²²²°gO&Ož„–-[ÀÃï¿>©©©8zô˜ÑýÍÏ/Àºu¿bôèÑèÔ©#Ôj5BBBtnl{çÎ]øû7Ô¶â”lè9Û#S^¯S§NcèÐgàëë ___ Ð_‘mß¼yk×þˆáÇ)zÌYòžÖì@eÎ)""¢ÚDm»áÓ­ TrÙ9Ž8Àº®k×®aÅŠ•9r$^~yŽ=Š?ÿüÓìröïß>š¥3¹%e?ø`„‡o„F£Áõëבp“'OÁõëI:Ë}ùåŒó fÎü*•‡ÆûïÿÛäý]»öGäååá7^‡¯¯/ÒÒÒñóÏ¿h/YÛ¼y3ÞzëMcΜ¹zÎöÊØëµlÙrüßÿ½‰åË—j”PjT²M›ÂѧO¨T’¢Çœ¹ïiÍ Uúó’°:H1u9 ñ·ÞÏÈV‡*&Vd²viÅÝ4dÈ‹çÉ%d²FSü]Ö@Öh )ù.ˤ§\€C?¾“%üýbÅŠå6ìy»n½ás&{•œrÁ¡’IH$UñÏ%… $!DñÏB¢¸oœ€(sé²±þrÅóŽÿ¹ÁÁ|ቈˆª€«›²23mR¶Úª@¥äðé:ƒS‡,2ßßþö7ÄÇÇת€QŸ3U²mæÜu”³(9I¹Kÿ,¬^T䂬ѳgOüñÇ>g"+•0k†E¥‘c+3«zGû3-œ(ÐüŠ«)5j€–-[(~ƒ\>gªõ)Ëâh&·ˆˆˆj µYu %F Ô[(/ûSÂõëIèߟ3Qµ…*a›b‰ˆˆ¨æ+›ö¥*û@6¿jQÁDéê2«'Dd/éK˜¤xê"""rì`¥d¨2\‘°lpŠŠíZlé"¢ê SôOe """rì`UµÊüê PÚB@ˆ2ˆˆª2H óÎ SDDDµ†T5¡Êòª k(DTý¡Ê´Øe|"9*µm••­TÚʉ(yÀV*"ªª0eÚ¨~NwÂÔò‰ˆˆÈ‘Höªî×XX!"û‹\–å$6c9*µ]ö¥ÒýïU{˜2xÞ¦®MDDDŽLªîPUñR~¢KDöªLR-LDDD5œº:**€-TDd7JÀÔËÙx£`""¢Z¬ª>T±fAD5#Y™³°°MÑDDDäÁʦƒSXYÅ`儈ª'T™Ý”eiwV"""r„`eÓKÿdëj ADd‹ÔÅ@EDDÄ`UÊʾT¥EÈLVDd—JÛÓŠ§(""¢ZCªžPeEµFp¤-"ªª %,[ÃìÁ.ˆˆˆ¨¦Sפ@% È5ˆª"T™¤„I2H9*©f„*¾QD¶°k×|ùåbxx¸×˜ýýì³yP©TÕ»Ì;M²¥ˆˆÈვþ›_Zq Œöu m¬Ù̹sçвe Ì;ÇnÂU£FX»ö½óÒÓÓѾ}{ úlµ„)”=µ=5™7j Õlê µ†jn¥Òm!xÃ`²uêÔÁK/½ˆ>}zÃËË«ÒeÓÒÒ±«Wÿ€‚‚‚jÙß]»vèÝŸ°°0¼ÿþ{–Æüùóðî»Óžž^m¯³ŸŸ>ùd6¼½½+<Ÿo¿]…Ï>[€?þC‡>‹-[¶";;»J•°ñDDDäÁÊ^•õ¥*[IÕ§ÿ«qK÷ï‰'žÔ™Þ©SG¼úê84mÚ´Â>–W~½ªØßÄÄD|óÍ2=zÔ¬yúžgUxñÅ‘&·’x{{cذç Ë2¾ývUµ¿å÷gøðahÞ¼™Þe«ó8öóóüysáçç§wþK/½ˆ‰'᯿âñàƒгgl۶ݦaŠ­SDDDdz°²6T)p£_`ÆÁ), …€,ËðööÆèÑ/£OŸÞåÆ{®l^U Æœ9³‹%K¾BJJJ¥óD5[ݧOoÀ”)oáôéÓ•.Û¶m[,Xðúõë[mÁªlø,¿?¯¿þ†vÞâÅ_ $¤%1uê4ddܱ«.víÚÙ³çàÈ‘#x衇0`ÀìÝ»>Ø;w¶Y°2oT?+>Pb#""r¤`eáÍ. T¥ý¨ŠóC•%dY†jµC† ƈ/ÀÅÅE÷®d^uêÚõtèеkÄÆ› ÎÛ´i“ö¹V‡ÒKÓŒ…*8uê”Î:Õ­²ý i‰óç0cÆŒj U¦¾€5­SŒ‡ˆˆÈ¡ƒ•¡ Ö‡*¡ó•æÎýíÚµ3{^usqqÁ+¯ŒF·n]ÍšGÖ«ŽË*Í„sçÎkÃá­[i//ÏjTVœï¨ˆˆˆ¬lÑ—Š×À(§´Åª¶Våýüó/xþùá€#GŽ"44páÂû S¢L •¨ìŒÊ@EDDä¨Ô†ªº”êK¥@ì‘e¸r'´•èyóæ#""B;¤zÙ ¶¡yUéÒ¥KXºty…!Õ Í«î0±Æ=‡ >PÜ7ÉÃÃsæ|‚àà`@«V­tZ­vïÞSmû[º—.]Âk¯½^aÿË«ÎûX•öóòóóÃܹsàï߰¼ٳÿ Fƒ­[·aòä‰€ØØ8» T¦ž‰ˆˆ¨+¡t R¤B, „ ¹šR•½wðòÉz§=z ¯½öºÙóªóõ44¯¨¨¨Z߇իôíÛÇèhiiiؽ{Ö¬Y[íÇFÓ¦Mѽ{w“÷¿:[2SRRðî»S1wîøW˜¿råÿàöíÛ##ãþøã@Õ)”k2¼CQ-#þÞ÷9¹ÂŸúR ceÈr™Œ$k‡[—e¹xž,C† È€,k Ëh4šâé 4²²Fƒ[7ÎÁ¿¡ßIª¥—ãܹs˜8qrÚ{¸Uy 4Àܹs0z`È“9 IDATô+æýôÓZxzzâ›o–Vž_)É)7Ѹك$„$A’$HB‚¤RAI’ „QrY²Pr©³€¤sþ4¥¥õXì–´z‘m¹º¹!+3Ó&eKöÚ—ª¸’ÂO{ɾedÜÁÔ©Ópþ|BBBjÜþ—ÞÇÊ^B¤¦¦ê Uàé鉸øãßlóýÐé%LYŠç+""¢ÚL­S?°—¾TBV›jP¸š0abÜ÷šp«êØ_㟠)¤˜Åˆˆˆ(XÙ]_*”»LˆÈ(Ð2%˜§ˆˆˆ7X)q_*ª ¢¤•ŠŠˆì3PY{žd """rð`eiR0PWþ‘=†)ëZ§*9ˆˆˆju°R¼/‘ƒe2a”ˆˆˆji°R´/UIVŽÈ: ÙO˜2x¹OTDDD V‘u™ªäÒ?åê3DDÕ¨ôŸx†"""b°*\¹a°2£BÈÚoDDU¤JÔ•µN1PÕ6’ñP¥Ä§®J…*¾aDT}¡ÊðÙ­SV½´B °q#ôëýúùò!"¢I]y ²¾&b}1¥×ÚÈÚV*6VQ•…)½­S ŒXËÕ­ëŒÍЪes´lÑ j• /ñ ODDެE]ÙKÿНþãŸ["ªê\eòT… whžžhÓªZµlަMq÷n&.&bCøvœ¿p EEE<興¨æ+¥úRA%înU²l¥""{T õµ+W Í›£Më–iÑ ^že×®'á·ýp+ívñ"µmZµÄÝÌL\½–™76$"¢š¬ UJ•ÀV*"²ã˜¥\ rÜx¥R©ðò¿žCÓà Ýg\ÒŸ*°q#½ë¿p ߯ýÆ¢í>=x†=û @–eÜLMÅ¡Ø8¬ß´yyy€ùŸü·àà¡«ŸçGÿ™'Oãç_×óW…ˆ¨¶+¡T  pQÒJÅPED5:L•kàRôÆê5Dû¶­*„ªR·3î â÷(œ¿Y£Ap“@ôëý|ø eó¦hß¶âOœ¶xۧΜÁ¼‹ R©Ñ¢YSŒyù%4ðñÁ¢%_+þ<ý W¯]㯠AmW—þ1PQMÏcµ°uJo/OdffbïþƒÚi-›# ¡¾^¹ÙÙ÷Ê¡sH¸˜ˆ±£^@€¿|¼½¬ÚvQQîÝËý+KWüïO}«~Xƒ;wî*ú<7nÞÂ㞈ˆJƒ•å5eFüØ—Šˆj|˜Ò;{í5PH*¸¸ÔÕ>®S§vFì× U¥òóó±m×^Œõ‚âûqæÜy@Ÿ‚•Z­Æ Æ¢s§ŽðòôDZz:6lÚŒÈÅpâø× ÑaÉÒåÚuþ1 ?Úµiƒ¹Ÿ-ÀŒiïàÈÑ¿°}×nÀ'³þƒ˜¸?Ñ¥óClÜ7’Sðí÷?à̹sŠ/‘|î™§ñhX7Ô­ëŒØ¸?ѶMk|2ï3¤Ü¼iÑó hèowÄŸ¼ÀßG"¢š¬” UÖ´RU¾III|‡‰He4 @™P¥÷lÄaØÀõzx¢Oi?ÿºÙàò—¯\CAAâûá^¿> 99¹Â¼ÂÂBܾåß~‡ëIIx04¯}ç/\ÄädDÇÄâµ±£!I’¶ß×Ã:á÷È(ƒÛ{°C{¬XõÒÓoãéÁÿÄ›“ÞÀSþ………xṡhß® æ-Xˆ{÷r0dÐ@ø7ô«žà+ùâʵ¬DDÕ¬”ìK%¬)£ôºA#¼¼¼ø.‘ŽôôtËO=z[¨x_+Si*ñO–eEGBÀ·A¼2j$öEý¬ìl½ËmÙ¾Cûó¾È(<ùx?´lÞ 7’“ñW|<Ô*BZ´À™sçàæêŠæÍšbÞ‚E·õÇA\J¼ XûË:<ѯúùáfj*žè×|4—¯\¬úa-úöîUmÁ*¤y ƒQu+EîMUR„kZ©Ø‹ˆª²Š ­SBß ªàÚõ$¬Û¸ ÝíŠÎ: Yp ΜÓÉZ£899Y½ÍÐvíðóêU€ÛØu6…\ÞÍÍ ë†V!-áææ††~¾prvâð‘£èüPGœ9w:>ˆ§N#ûÞ=“ö%//ùùù¨[·.|4€$I¸”˜X6M*úz7ðñ@ã€8_|ùãã½ÁîßcQ·®l×jµ ²,#:î$Ú„4ë.ø{×8ü×äææ¿®õÐ¥Ski4p©ëŒ‹‰I8w¡8¶ FÔ©£Â…‹×‘x5{¤=Î&\EZú´oÓ ®¸àÐá“prªƒn·CäÁcüE "+e•(›«¬¨Ù0TQ†)Q¾¡JÁÖ)Îd¥7n¤Ü­´t\¾z;uÀã}zâbâUäççë,¯R©0ð‰^ŠüÍ)°¨°yå¶S^ݺuññ‡ öðaìùíwܸ‘Œ7'¾¡³LtL,F †5?ý‚.BtL¬YûSÚ §V« I$Iªò›!7òo€{9¹8~ê¢vÚés—áïç?bâ+,_PX„?ÅCýºáüÅkðñv‡‡»+"ƒJ%¡÷cqãf:._MA#¤¥ß›k=¸¸8C%Ih胫×ÙFDµ‡dûP¥ûÙ®UµZvsM"ªÆP¥÷\#”+ßÁObé·3w ŸÑÿ 'ûü|}ðêèhÑ,¢dTÙà&x}ì‹hHK¿mÕ¶KG4ª YÓ`¸¹ºâ‡ÆÉS§‘~û64²î=´þ:~žlÜm[·ÆŸGŽZ´_))7!Ë2Ú´ ±Iˆ­ÌÕk)PIzýý!´kÝÔè:……Ee÷rò ’$xº»!õVFÉk¬ÁíŒL¸Ö«‹)ihàã×\p73É)iðmà ?o\OJåù„ˆj µmÊŽQa}%GÖi·""²i¶å;U)ÖîOrÜtuüätëÒ  I îÍ€úùâå‘ô­6*•ê~¸~ÇOž±é¾effÁ·A!‘‘zõ\ðxŸÞ8‡¡íÐ$(Q¢Ë„ŒB>r/ýëyœ;ÞäËËËÉÍÅþ¨óò(|ùÍR¤¦ÞBÿÇûYý|<Ü]qï^ñÍ Šàæú@’$í@Sù…8v"Ö¥|¼Ý‘–~’$™þºeÝC‹¦pé„ðòtCüÉ{Ðh4ȸ“…Ö!MpñŠŠ4hÛ*²,#¿ '"ªåÁJÁ¾TÖ·RÉÚÅ@EDU¨¬={ U¢´¹bÙ·kÒ¢)\]]MZ'++ç.Úü2¹»÷`üØ1lÜ‹¿þk~úCŸy Ç>ƒã'O!þø‰ ëDÇÄbêÿ½©3ìº%¾ý~5^ü× ˜úo¢¨¨‹íhØMƒü!„À’ËùnßÉD^^>úöx¹¹ùÈÏ/6þ ½Ñ¬I4EEE¸‘ FÆ”4üí‘ö8y&wîfUº½)iðkà‰^ï„"Œ“gµÁéÊÕty¨ -Åîõ]qú\"O(DT»êÝ.ëü¹·º¡ÊÄËþd¹LH’‹ûï–L+þ#—Œ¥)ž.k Ñh k4ÐÈ÷¿ßº~ U›””ÄQ‰¨‚ôôtƒÃ­§¤ÜD`‹‡´}a$IQò³÷¿ !´_(ý^z-= a4Lý½ÁÁ|S윟o,øtƾ>Ñâ+}<=<ðõ 0ò•q6jžˆˆt¹º¹!+3Ó&e«Ë"ë?Cµ¶/U™blØJ5vìXLœ8±ÂôK—.aðàÁÚeºté‚qãÆUXG£ÑàúõëØ³g–.]ŠœœœJ·aÊòæX½z5bcc±xñbþvÙW ö ­ñº<Ü'Í ÐTÁM‚ró&C‘P+¨éKU£dnU6qøðaLš4Igš±K1J×Q«Õ ÅŒ3€wß}W‘åMµÿ~$$$ðè%²§0%*)*Çñh·®øm_¤Õå4 ð‡Cœ:sîîxþ¹¡Ø²m_`"" U¨*3žq™)¶VXXˆ¬¬,¯l7,¿NFF¢¢¢ðᇢÿþðööVdyS­X±ûöíãÑKd¡ ºŠaÊñ4ôóCÓà`¶p4À²êÕ«‡§Â7‹áÝ·¦`_T"~çùœˆÈ¨-_U‰@… #þ•üX#9r€´´4³–¿{÷.¦L™‚=zÀÏÏÉÉÉX¶l¶lÙ¢]þÇÄÚµkñÏþíÛ·Ç[o½…èèh,[¶ û÷ïÇš5k>ú(&L˜€æÍ›#55»wïÆ¢E‹xtÙ"H•k¡Úñ¨UrJ †¿ø²"eO¸€éÿ™É•ˆˆÁJ7T)]_±¬†c•Ji(,4}hØÒ–§+W®˜½|AARSSñÑGáâÅ‹xôÑG1sæLÄÇÇãòåËÚu¦NŠyóæaÞ¼yz·ãî bÞ¼yعs'|}}Q·n]ÙD¶T&3L‘EÁJ(;Œ:,i¥RæNV]ºtALLŒÎ´Ù³gã§Ÿ~2¾B qãÆxÿý÷Ž;wîX´üªU«´ËlÚ´ /¼ð:tè ¬–,Y‚ððpƒe7hÐuêÔATTîÞ½‹»wïò¨&R2L•ë;UöŸ"Û ""¢Ú¬ Uz.ý³¸BbE¶:tèvÄ?SuëÖ ññÅ÷ IMMÅæÍ›±téR‹—÷ððÀÀѱcGxzz"((¨BkS^^^¥ûtñâEÄÅÅaãÆˆŒŒÄúõë+F"².ð(ÞwŠŠˆˆ¨6+Q!Y¨¬ª‰WµÒQþ ‘››kÕòõêÕÃÚµkuëÖ!11óçÏ7{Ÿ4vhøÁƒcÁ‚ˆ‹‹ÃäÉ“ytYyÎV^r\y˜bºRÚ°gþ‰íÛœÿˆ-øëø)»Ùßž…¡_ïîÚÇwîÞEÊÍ[8xè0Î_¸T¥û2iühüã'Oë}l‹çÞ,8ÿ[ý³Yë=Ö=í†Ï¾Xмü|ít—ºuñöä×°aóvœ<}®ÊÞ¿¾½ºcéÿVãêµ$ymZµD¿ÞዯÿÇ_L"+½¹J‘Ú„e—þ•¬Q®•ªª«%¥£ü)±|»víàááÏ?ÿ\;ÍØp‹‹C\\Z¶l‰õë×Ã××7oÞäNdáYK(Y˜ ŒVÊÚ°y·î4 ÂsÏ ÂÜÏ–hçÛã½¢.]¾Š~\! xyz UËæùü3ØÿÇ!ìÝ÷ßÔrÅü‰G:wÄcvEÄïQÚé=ë†ä›©Uªîÿ/Àà<¯–®‚F–ùÕrR¥µaÍçµe†Q–VTô§:E+=Õ$55®®®xî¹çàéé‰ $$ÄìrÜÝÝ1dÈí ;wFNNŽÑ~_Ddr²<™‰Š„ƒœÃìQAAróò›—‡üü|@–µsóòP¤ÑØÝ>kŠŠ›—‡œœ\\OJÆoûàÇu›Ðó±0ø6ðá›ZN‘Fƒí»~Ãßú ¾›[ñßÁúnèÖå!lßµ·Ê÷çìù‹Ðh4ëú0ß"2Ôb¥ðPê—¡¿•Ê>JLLÄ‚ ðÆo`Ò¤I8t袣£Í.ÇÍÍ }ûöÅ”)SðÀ 11o½õ–ѾYDd£L&ô‡4)û R©ðDßhÒõë»áλØyGãOj—y}ìK8yú,Ú¶¯¯ÒÒncËŽ=¸|åž{z4 ~Ý´M»üßÃA³¦AXóóF£e›âôÙ\OJÆCÛcçž}ÅÇèùX:=ØÔ«‡Ä+W±eûdܹktŸ@­Ráñ>=о]+¨Ujœ<};vÿŽ|Zñ*{Î߯ýU‘÷ÅØþ놙 H¼r }{ý6ï@ŸžljÓçp=)ùþ×ëýw&aí/qéòU@`£Œ~qf~²@»?Ñ1‡Ñ©c(7òÇÚŸ7"ábbÅ`¬Ñ |Ë.Œ~i8Nœ:ƒ;w3->æNŸ=6­Z¢RRoaó¶Ýnˆ.=÷ún¸˜x6ïÀ½{9V¿§DT%ÁJá*`M+UI|²A¨Z¾|9–/_nÖ2¦¬cî6¾ýö[|ûí·ç?ÿüóz§—tãÚµk˜0ad¢êS¢bºb˜²?EEEÈÌÌBøÖ]H½•†–Í›â©Ápõú ÜJK×.ײE3„oÛ…;w3Ñ«ûßðüÐ!˜·ðk?yO~’ÚK¿Ú´j?Æ›\¶)®%Ý€·—öq¿ÞÝÑ¢Y0Öü¼ðDoŒ~q8.YMIKœ¡}.** ÃÙÉ K–~µZgŸˆÇûôÀÖF÷¥²ç¬¤Êö¿¼m;÷bÂk/ãü…D´o׋–¬Ð™oÊëeÌÀ'ûbû®ß°}×^¤¥g\îúd9vû÷ÅÚ_6Z|Ì5oÚ·ì@ff6žèׯ…ƒ1qø~í¯P©Töì?ñDŸظe'Xõž‘mHå‘"—þYªøÆQ UШx³_QcqáÒeÜÍÌŸǎãæÍ[l䯳̱¿N éF ²³ïawÄ~¸>ð¼½á"T’„ÀÀF€z..hÔȧΞ7¹lSdff¡^½â{-ÖQ«ñ·ncÓ–H¹™ŠôÛX·a+êÕsA«–ÍK]t;"}ïîܽ‹½ûþ@›Ö-MÚcÏY)†ö_ŸÔ[iˆ;| Þ„Ñq:-E¦¾^ÆDü…#ÇŽ#9%Õhÿ¼ˆß¢ØØß`ù&sñ'q#ù&²²³ñ[„þ8‹Ûwp+-£ãÐ$¨1Xýž‘m¨­o¥ª¸²ÅªôG¼ôˆ$Hé´Péö²ü¦3Ý’×Kïó.(4yóòó±e{ 臋_]±ú˜Ë¾w¯¸’¦V—™–§’×ÃÇÊ÷”ˆl¬”êeU¨’õP†*"²ë„Å UÓ899aü˜qòô9Ä>Š[i·ñüÐÁÆW,3âÛñ“gп_OìŠØ‡6­ZâøÉ3Ö•­Gã?U\nµ’BèŒ[TT„‚ÂB£ûœ››!€¯–§íŸc.CϹ¼×ÆŒD#ÿ†ÚÇ{~‹Dä ï­hd”½ÒKË_*hòë%”ýí=uæ:w E¯îÓ~Ý¢ãBÏs/û\”xO‰ÈÁJ©*¬¾ìÏ¢aÔyÁ UI~‚MFöãé«Ê5 hˆzõ\°sÏïz+¬¦8á†>ýøù6@³à lؼ]±² UËæðòôÀŸGŠû0ÝJO‡AA‘X2Ø‚$Ið÷ÃÁC‡–wûvò Ð&¤þõlÚº¹%£»æææáÈ_'0äO ‘C¸>ðþñd_ܹ›‰ —.Ýߢ"üq0Oôí‰æM›@%IhàÀFÚeîÝË—§»ö°,ÿØÐs¶G¦¼^—¯\Ãc{îõáá^w~P‘mgܹ‹}‘ÑèñX˜¢Çœ%ï)U=µy‹+ØŸÊâKÿJk$2+&Ddãde«Ö)ž¼ªË­´tìŠØ‡>½þŽ~}ºãÂÅD$\¸dv9ÇOžÁ‹/<«3¹%e7 ¦¿,#--I7RðõŠï‘–~[g¹-Ûv£¿^ø×óÏ@%I8wá¾ûaÉûûÛþ(((À ýàá^™YÙØÿÇ!\½^|ÉZtìŸxzðøù6À/¶Txlè9Û+c¯×ŽÝ¿ãéÁObòcp÷n&Ž;®Ø}ÎÆFÇí IBÑcÎÜ÷”ˆª¡öгÿ &¶G+té_Ée›ÁäâÇ2 k.þ.Ëhd²FSüX£F£Œâï©×Îüä')) ^^^|—‰HGzz:ô²›’’Š ‡ I*H’Tü%$H’ B’ J‹’>B”ÜL]”1qÿ|W<¯ò@uøÀfóM©a¼<=0eÂXÌž÷…]·Þð9érusCVf¦MÊ6¡ÅÊö­T¦—!ë4XÙ‚Õ-TÚ!E¥³©æjÛ:Äæ#ãñ9Õ,¦õ±ªÖPeÕÆ‰ˆª)‘1T9²íÛàÔ™ó|ÎDDdJ°Ò½á¯ùŸà–Y_¨w˜^C±mGr""kÔ¨ð@ÿ"Tãy{y"À¿!N×¢QŸ3‘¹Ôk JÝðW‰J‹lòd"¢ª U:ÑÉfgB²#ié·1cÖ\>g""ª,XYÛŸÊðŠf_ú'JbCÙSÒ~n"""¢Z¬”õÏüІñרˆ¨ZC•峉ˆˆÈñƒ•½´RÉó•é÷¸2ÍØ±c1qâÄ Ó/]º„Áƒk—éÒ¥ ÆWaFƒëׯcÏž=Xºt)rrrLÞFFFºwïذa–-[†;wZýœœyy­‰HùP¥ÓT¥snRdô@"""r„`eÞS2T韫¨Ã‡cÒ¤I:ÓÊÞ[«²uÔj5BCC1cÆ àÝwß5y¶òÞ{ïáÞ½{øôÓOyT)¦ ŸÍ” TLVDDDŽB²‹Qÿ„þ`&L]ß………ÈÊÊÒùÊÎÎ6iŒŒ DEEáÃ?Dÿþýáíímò6²²²xÔÕ˜TU±uʪŸJn&Ì1‰ˆˆ1X÷¦²Ô‘#GŠ”çää„·ß~»wïÆ¾}û0cÆ ¸¸¸Ü©„À¨Q£ŽÈÈH,^¼xûí·ñÔSOaĈ8zô(^}õUÀ?þˆAƒaùò刎ŽFXXXñ›.I7n¶nÝŠhË!"£g, U ?Ù‘QêªíOe

<ð† gggü÷¿ÿŤI“0wnñ·cÆŒA¿~ý0mÚ4¤§§£sçÎHOOÇüùóáêêª÷RÀ©S§bÞ¼y˜7ožv?'L˜€°°0L™2yyyxûí·±lÙ2 <EEEü­ ‚•-TÚ/aÒbDDDäÁÊìš‚­T•,!›¨¬¼’¦K—.ˆ‰‰Ñ™6{ölüôÓOÆ÷S4nÜï¿ÿ>ÂÃÃqçΓ·±lÙ2|ùå—–uwwÇ!CЧOܾ}ðõ×_ãã?ÆÜ¹sáää„Ñ£Gã•W^ÁéÓ§Û·o7º¯K–,Axx¸ö±³³3Fމ‘#G"!!AèvíÚ…îÝ»ã÷ßço1TY›ÆÀ@EDDÄ`UYÀÚVª²e+"—#Z:tH;⟩ºuë†øøx@jj*6oÞŒ¥K—*²&Mš@5kÖÜsÔjm‹WãÆQ·n]œ9sƬ}.?J`PP„8{ö¬vZvv6Nœ8fÍš1XY¦¨ˆˆˆ¬,ÌGfW@Ì®h(ª,U:Ê_aa!rss-;++ B >滹¹)²ggg¨T*!tFA,((àPíDÓ¨ˆˆˆ¨dð ÃU;õÏj&¥£ü)ªàÚµkÈÉÉAÏž= ÎW©ThÛ¶­á”¬6ž“/_¾ !:uꤦR©Ð¦Mœ?ž¿D¦$$Ñý Ÿ½8öQ­VÂŒ©F–T¢i«†ÖLJ¯(ÿUêöíÛhܸ1„ÈÏÏǪU«0eÊtíÚjµíÛ·Ghh( -- û÷ïÇŒ3WWWôìÙS[^rr2ÂÂÂàããSaÀŒ²233±yóf|ðÁhÛ¶-¼½½ñÞ{ï!%%¥B0"ÒwbëU¤65)=ê_åƒTÈ&-lï}ƒWddd {÷CŸ5kZ´hiÓ¦aéÒ¥ÈÍÍÅôéÓáïïÔÔT¬\¹Ç̘1Ó¦MÃêÕ«¡V«qæÌœ={999øé§ŸððÃcë֭صkþóŸÿܯ?þo½õ/^ •J…`üøñüm Òw’)½ÿT%gËGd #""r˜jC¯'GÈŠ‡*Ê(Û¿!ËÅJ†\2¯ø»,k ÑhŠÖh ‘5@ÉwY£AÊÕÓhØÐWï%%%ÁËË‹ï2éHOO7xï¶””T·ê I’ $$II’ I*I@$I@ Bˆû_(¾TDDDµˆduU£úGD UVæÝ”‚ˆˆˆƒÚü?ð¢ÒÌj©Æ—dKÙ3ý®U<ƒ9p°Rê³U³îî"T¬¡Qu*aÑÔDDDähÁJ‰ûSYÔJ%^J6µ8’U[˜2=P1HÕ&ô±Rx ³—b,"²—PÅþSDDD¤ŸºÊ•‘>Y²±9¼‰UyÂýÖ)[¨Ìº0鋈ˆÈao±ª–QÿXÛ ";Uf´N™uÖ UDDDŽF]i¨Qª?•YÑIO*¶T‘†*³. d"""rh’r¡JXªôTQX!"GHi<—9<µõ 0·?•þ9rÅe˜º‘­ã‘é-T¥ý±Qm!™y÷)…*Ó7¨D5eìØ±ˆ¯ð®³Ì²eËô®sìØ1lÛ¶ S¦L‹‹‹Ámüõ×_èСC…y½zõ† ârvv†³³³Áù£F˜1cø›F5>T™¼$ÃQ­¤®¬æ Ä MTF6hòMƒMtøðaLš4Igš,Ë&­£V«Š3f ï¾û®ÞåóòòðïÿÆ ƒF£qÈè½÷Þý{÷ðé§ŸV˜×±cGÔ¯__|ñÓ¨Æ)ó[¨Ì»äñ‹ˆˆÈ±H–…*kkU¾Á ‘••¥ó•mÒ:ˆŠŠÂ‡~ˆþýûÃÛÛ[ïòQQQ(**ˆ#jåÁuìØ1†*rðfY C‘#+= OJ Ra¸ó/ý³×JÈ‘#GzçaÖ¬Y?~<üüü ¿ ’„qãÆaëÖ­8pà/^¬Sfdd$~øaíãÐÐP:tðé§ŸbÖ¬Y:å½òÊ+X°`ÀÉÉ o¿ý6vïÞ}ûöaÆŒÚËëÔ©ƒwÞy[·nE\\¶lÙ‚Aƒé”UÙ¶ß~ûm<õÔS1bŽ=ŠW_}Õè6àÑGÅ?þˆØØXlÛ¶ “'Oæo"9| v~>#"""kƒ•ÙUã¡È6ý© TI,¬¥¨T*¸¸¸è|©Õj³Ê(m©ºråŠÁeN:…ððpL:Õà2&L@¯^½0eÊ >Ë–-ƒJ¥2º;vì@= I÷ßÊ^½zaç΀éÓ§ÃÏÏÆ Ãðáì½²  ©©©øè£0`À¬\¹3gÎD“&MLzþóçÏÇÆ±fÍtêÔ K—.5ºMwww,\¸7nDß¾}1yòdìÝ»—¿‰TS•Ùç†)""¢Z¬Lk©2¾°C© ½¡J™@UªK—.ˆ‰‰ÑùzöÙgM«( ÀÀ@|ðÁÇ;w*]þË/¿D‡н{÷ óœ1räHÌœ9 ¸zõ*¦OŸ½Ë—÷Ç@­VkÉðññAóæÍ±ÿ~¸»»cÈ!˜3gnß¾ääd|ýõ×èÕ«—výU«V!&&©©©Ø´iô¸a*cÛlРêÔ©ƒ¨¨(ܽ{ 8qâÉŽƒ”0»…Êx딺 ""r,jãÅFƒTȆ—–-¬ÀX뇸qãÌZ§[·nˆ¤¦¦bóæÍÚVšÊdggã“O>ÁôéÓ«3/((Bœ={Vgù'N Y³føý÷ß+-»  èÙ³'Ž;†=z 22¹¹¹ kÖ¬¹ÿ†«Õ:—åyxx`àÀèØ±#<==„ºuëZüº6iÒ¤Òm^¼xqqqظq#"##±~ýzÄÄÄð7‘ì4TY´–MÊ%""";VfµR™U‘0Ô?èä%é©o`U:*`aa!rssÍZwïÞ½2d^}õUm8Š[¬T*„:£ //ïþÓ®ä“í;v`Ú´iX¸p!z÷îuëÖ²²² „ÀðáÑ‘‘Qa½zõêaíÚµˆˆˆÀºu똘ˆùóçW|ÉÍøTÝØ65 ÆŽ‹.]º`ðàÁX°`âââØÏŠì*O·P™¹Š)çR*"""‡%™—,[J …tT(ÐÜPUjöìÙ:t(š7o®vùòe!ЩS'í4•J…6mÚàüùó€{÷îÁÝÝÝ`¹±±±¨_¿>Z·n¶mÛâàÁƒ€k×®!''={öÔ»^»víàááÏ?ÿ±±±¸yóf…!çm€Nß4cÛ,‡3fॗ^B¯^½àëëËßFª™Ì”%ØÑŠˆˆ¨6+=£þ s*¦­ox¶yý±j’7n`ùòå;v¬vZff&6oÞŒ>ømÛ¶…··7Þ{ï=¤¤¤h/‘;vìFøûûã™gžÑ)W£Ñ`÷îݘ>}:¢¢¢ŸŸÈÏÏǪU«0eÊtíÚjµíÛ·Ghh(€âË]]]ñÜsÏÁÓÓ @HHˆNÙÆ¶œœŒ°°0øøøÀÅÅÅè6Kû`ùúú¢nݺèܹ3rrrŒöS#²·0e4P *""¢Z¬¬ 4ÂÊ¥•¤ÂÞüðø|ù²Î´?þ‡ÂâÅ‹±~ýz¸¸¸`üøñÚùŸ}ö233±iÓ&,[¶ W¯^Eaa¡NÛ·oGÇŽµ£–Zºt)¾ûî;LŸ>ÄܹsѲeK@bb",X€7Þx[¶lAïÞ½­³¾±mÿôÓOHIIÁÖ­[1mÚ4£ÛtssCß¾}ñË/¿ ** Ï<ó Þzë-ˉj|ìb˜"""ª}µ€>GÊzŒ‡RGÙKÎd@.þdÈe¹ä±²,C£)ý^YÖ?.ùJ¾z ýô_F–””///¾ËD¤#==ÝàýçRn¦¢ië.$$I‚JRAH$©¸/¤$I’!$!Š¿K¢¤•ª¸Kbñ Дþ‰‡öo@`p0ß""¢*àêæ†¬ÌL›”-Yªl0”º)‰ÇÙ˜­GøãyŒˆˆÈq¨«cä?“Ò›\y0“Ùyˆl¨*m·¢ÿÏZDDD¬LË9Fç*¬q‘½G.¶P‘Á`eö¥¦× P!W:IßZº-U2ß="²}²¢…ÊèSLZDDDC2¿¥ÊÒÍea-„ˆª0TYqűÑ>©<9µé9ÇÚKÿ¬JÝ@ +&Dd“L%,î?UiK?ÏYDDDK2½º`^åÂÚPu¿‚"”Ø%""Ûå0i¡âX;DDDOm<§XÓRUI 2ÒŸJ.÷“ˆˆì,Pñr?""¢ÚHR*T)*ÖHˆ¨&*è*žÂˆˆˆju•ߟÊH²¡&-HDv¨¬9ˆˆˆƒ¤l¨²f!ËCQµ*ž£ˆˆˆF4ïÒ?kYùEäÊB[©ˆ¨”áˆ%t~T®|"""ª©$KB•Rµ£ý©Xó "» UÊœ›Ø—”ˆˆÈñ¨­¿ôÏš–*#÷§’•aY¥dìØ±˜8qb…é—.]ÂàÁƒµËtéÒãÆ«°ŽF£Áõë×±gÏ,]º999F·‘žžŽcÇŽaáÂ…HLLä‘GT£"–Ò-T TDDD¬ª6T)u韀€lÅ5‚‡ƤI“t¦É²lÒ:jµ¡¡¡˜1cðî»ïVº¼$Ið÷÷ǤI“ðÍ7ß`ðàÁÈËËãÑGT“²•"«+|eÙ Iç¼Y#ÿé© ˜|+̤–.aqKUY………ÈÊÊÒùÊÎÎ6iŒŒ DEEáÃ?Dÿþýáíí]éòwïÞÅÙ³gñþûï# ­Zµâ‘GdïiJXµv¹›œëé—ÅPEDDäPÔ¦VPY(2« Ç©M9r€´´4£ËçææB£Ñ ??àää„I“&áñLJ““"""ðÙgŸi/-T©T˜0až|òIÔ«W{÷îE—.]0~üx\½zµÒùÉÉɘ2e zôè???$''cÙ²eزe‹vŒmŸˆ¬9ו{Ä EDDäÐ$ó[ªô,`҃졥ª”J¥‚‹‹‹Î—Z­6«ŒÒ–ª+W®]ÖËË S§Nʼn'pæÌÀôéÓáçç‡aÆaøðáÖ¹AAA&Í/((@jj*>úè# 0+W®ÄÌ™3ѤIíúƶOD¦‡)£-TDDDäàÁÊh 2ª¬­˜´˜òµ’.]º &&FçëÙgŸ5m„@`` >øà„‡‡ãÎ;z—ëÖ­âãã}ûöÁÇÇ&L¸»»cÈ!˜3gnß¾ääd|ýõ×èÕ«ÀÙÙÇÇÌ™3qöìY$%%áÓO?Õ–ml>¬Zµ 111HMMŦM›€:˜´}"²ä4Æ*""¢ÚJmVEÁŒP¤ÌPê¢d, Ù”äg–C‡iGü3UiP€ÔÔTlÞ¼K—.5¸|ll,^ýuÀüùó‘ŸŸŒŒ @“&M „Àš5kî¿j5\\\7†J¥ÂéÓ§µóË®al>xxx`àÀèØ±#<==„ºuëš´}"ªüü&´g(=-TDDDÄ`ebÞ1£úa`r ¼éoé(………ÈÍÍ5º|ÙþT+V¬À÷ßæÍ›ãÂ… ÈÊÊ‚Ç׆­²êÔ©I’ I4ÙóëÕ«‡µk×"""ëÖ­Cbb"æÏŸ¯olûDd8PU8¿1LÕz’þ ƒþ)¦Žü' Õ4ÌêOem¨S^é(¦„ªòâããñçŸâÕW_\»v 999èÙ³§Þå¯^½ x衇,šß®];xxxàóÏ?Gll,nÞ¼©Ó¢elûDdè¼VæDÆ*"""*¬LªY˜¼ˆåý±De­\5¼³råJ<þøãhÚ´)òóó±jÕ*L™2]»v…Z­Fûöí ÈÎÎFxx8>øà´oß^^^ÚPfÊüÔÔT¸ººâ¹çžƒ§§' €í|cÛ'¢JZ¨ ÌùH0•9N°2¹¥Ê¤P%,U¢²ää uƒâÌ™3Ú¾]K—.Åwß}‡éÓ§ãàÁƒ˜;w.Z¶l©]þ“O>All,–,Y‚uëÖÁÉÉ ´—þU6?11 ,Ào¼-[¶ wïÞˆŽŽÖÙcÛ'¢r' >àeþ‘Õú zI®4ÁØhŠû—¥ÉÚþVÅ“dȲ YÖ”|/ýYFYSò½äñË'ÐÐÏWï6’’’àååå0oVƒ GyyyyfÏ'¢béééÐ;/åf*š·é I¥‚$¤âïRñw!Šû5 !AH¢ø»Å_€âþ‰OQ!T•??ømƒƒù¦UW77defÚ¤lµÁTÅ—þUùOÜÏ]T¬uëÖ¸zõªÁÐdl>™É—û±¡ŠˆˆÈ!©-ýKo´¥ÊäRd+ÖulM›6Epp0âââàããƒI“&aÕªU&Ï'¢ê=½0TÕª`eM*Ë+$ÂØú²ñ@æèu777Œ;sçÎEJJ ~þùgüúë¯&Ï'¢ªÏcú‹2LÕ–`e~ÅÁúʇ0|Ó_“ö v\^xÁâùDTµ¡ l¡"""b°²Õ #•¥yI lK»^Qõ‡©²#ü±•Šˆˆ¨6“Ì«Xª”ºé/ë+Dd/¡Š'("""º¬Lh©²¶O•u7ýÕÛkˆ¨ZÂT…{P €'("""’ŒEØ2TYWÃaE†ˆª4T)~ãIŒˆˆÈa¨- U6ïOu#¢dAÙìõ‰ˆ¬SÊ·P)s³ """²/’mB¬ìOUÉҬ‰Q…*åÏ?B'›‘ãP›iªèÒ?#ÿ݈¨êB•Õ)HÏGULUDDDGR2TY[•1²DDU°)@0T9<µîßyk.ý³îþV¥­T€ž–ª ‰ˆl¨„u+3LÕ.’2ݨ­_·*ëcÇŽÅ_ý…:T˜×«W/lذGY>cg*""¢Ú¬ #+oükò=ª`õú–ÈËËÿÿýoH’Ä£€ˆ¬ S÷Ç TDDDµ5XYþ×ßúËÿªo俨¨(aĈ< ˆÈŠPÅ*"""Ò7ܺ-U–…*=ýÌ^ßzEEE˜5kÆ???Ã/$aܸqغu+8€Å‹# ðé§ŸbÖ¬Y:Ë¿òÊ+X°`êÔ©ƒwÞy[·nE\\¶lÙ‚Aƒñˆ#rˆ0Å*"""2¬LŠT69ÐÌPeíæN:…ððpL:Õà2&L@¯^½0eÊ >Ë–-ƒJ¥ÂŽ;УGË {õê…;w¢  ©©©øè£0`À¬\¹3gÎD“&MxÔÕèP¥d Ó‘ã+*•D*ëZªŒn[w%?þòË/Ñ¡CtïÞ½ÂçÎãGTc•R}¨*¶P B‘£+kB•I e©°uýcöìÙ:t(š7o®vùòe!ЩS'í4•J…6mÚàüù󀨨XÔ¯_­[·FÛ¶mqðàA@»víàááÏ?ÿ±±±¸yó&d™w9&ª9±J‰“Ð_.Qí V6 UÖö§²Qׄ7n`ùòå;v¬vZff&6oÞŒ>ømÛ¶…··7Þ{ï=¤¤¤ && Ñh°{÷nLŸ>QQQÈÏϤ¦¦ÂÕÕÏ=÷<==1`À„„„ðˆ#ªIÉJ(³ri »VÕÂ`e³–*;öÃ?àòåË:Ó>þøc:t‹/Æúõëáââ‚ñãÇë,³}ûvtìØ;wîÔNKLLÄ‚ ðÆo`Ë–-èÝ»7¢££yÄ9|"«±¦ˆˆˆjA-àñÁ/Ë&F*ë[ªÊ¸Yœ\úŸvš,Ë€¬,Ë52dYYÖ@£Ñ@#k kŠ Ñh k4¸žx ý|õîIRR¼¼¼ø.‘ŽôôtíýèÊK¹™Š–íà I*H’¤ó]H$I‚„eFq§©âÖ)â‡ÆOšQ?#08˜o QpusCVf¦MÊV›œLìe“fm;0 ‘''ÝGl¡"""ªÔ÷+Õ=H…¬ç‘±PÅÚ UG˜ª¤ÕJ’õEXÓRedä?aúºDDUªôLÊ•GDDD5“Úž†S—+`òL""*÷¡–”EDDDŽÆŠ«êºG+%DT=J{b  V&}k£PeÆý­ˆˆª2PÖô£2°"OiDDD¬Lª Tu¨""ª¾ˆ¥UÉ,žöˆˆˆ‡ÚÜPdÙpìÖ RQº€ld ‘raʺ‘þ¦ˆˆˆjI‘Rlªˆˆª)]Yɪˆˆˆjuµõ§29h©b—+"²«&¦ˆˆˆj1Ë[¬lª,Ü$Qµ„*0TÕrjc•› Raé=ªØÁŠˆì LUÒBÅDEDDTë˜ßbU ÷¨b…ˆì+TÁp¨Rö„JDDD57X«÷óX3BU…õö‰öÄÔõM7vìXÄÇÇk¿öíÛ‡… "88Øâ2œœÀÀ@ÀêÕ«1qâD«öó¡‡ÂÂ… ±mÛ6ÄÅÅaÛ¶møðÃѰaC»?°Ê?ÿ 6 ÿþÙ˜2ðÁ°äôÄN¢DDDŽFmV=ÀºŒ¬[Épê6¸ôïðáØ4i$I‚¿¿?&Mš„o¾ùƒF^^žÕåïß¿ ¯?qâD <_}õ¾úê+dffÂßß]»vEnn®ÝXÖ>¢·Ì>í1LÕ‚`eiŸ*%îQ B•uŸø"++ p÷î]¼ÿþûˆŒŒD«V­oõ »bÅ ‹×}ä‘G0tèP <·oßÖN¿qãŽ9R#,kž?‘D&Ã'0QÒJ%,)ˆˆˆ•dôO¿ÍêÂ`¨ª¹¹¹Ðh4ÈÏÏG:uðÎ;ï`ëÖ­ˆ‹‹Ã–-[0hÐ åU*&Mš„;w"** ~ø¡ÎüeË–aĈ÷_hI¸qã°uëV8p‹/F@@€Þ}3f V­Z¥ª ùñÇ1hÐ ,_¾ÑÑÑ P|iâÛo¿Ý»wcß¾}˜1c\\\tÖ3f Ö®]‹˜˜üòË/èÔ©“Éû‰‡~Xû844‡2øü1¶¿Dv·„—ü1oÑÿ·wîARTçÿþ¼½‹+Fâ²’ Dq¹„.+ ^  ).e¿ìj¤H ˆA´°"J*1H %š›A¨Pá%1°‚ù•B$¢ e!Ê= Hén\.‹,Lÿþ˜™žî[Ï9§gfg?µÅ\úôôt÷ô¼Ï¼ç¼‡RHbS•Ù—¾7`ÈlL•n‘ ³ÑHYY¦M›†Ý»wcÏž=hjjB}}=fΜ‰‘#GbáÂ…˜1c®¿þz§ÍÃ?Œâá‡FUUS¾ÆäÉ“1dÈ<òÈ#¨®®F(‚ PTT·lÏž=ñá‡úÞþiÓ¦aýúõ7nÞ{ï=ÀôéÓѱcGTUU¡ººååå˜2eЧÝí·ßŽ™3gbĈø÷¿ÿ¹sç¢M›6o¯ ül/!¹±)Õ •¤}š^E!„”Xå"S•æÑ,D·Þz«§xE‡0yòdçùE‹aÛ¶m¨¯¯ÇÚµkqàÀTVVJJJpï½÷bƌػw/Ž?Ž9sæ$}­’’Œ;3fÌÀpôèQLŸ>¥¥¥øö·¿íY¶M›6(++ùsçœÇFå)¶±páBO›çŸ555Ø·oΟ?«®º ?üáñôÓOãĉøä“O0oÞ< 2ÄÓnýúõøðÃÑÐЀßÿþ÷¸úê«Ñ¥K—Œ¶×~·—cb>„ŠRE!„ÅÙS•\ªl÷Óvð¦÷Î;ïàÁ<óÌ3¸páNž<é<_ZZŠQ£F¡_¿~hß¾=ºté‚Ë/¿píµ×¢¨¨È“U²íäÕ5ºtéÁÞ½{ÇΞ=‹Ý»w£[·nØ´i“óxSSêêêÐ¥Kìܹ°aüñÆ€±cÇzºàˆ+¶qýõ×CD°|ùòØ..NÙµ®±±çÏŸÇW\‘Ñöš@e{ ÚÔºüIÊKeŠB)`±2ê/ŠR嵫ìO„ -,Y²Ý»wÇÁƒqÅW`ÅŠxíµ×°zõj>|Ï<óŒÓ¶M›6°, –e! ¥}­’’AD<ÖÔÔ”°ápçwbݺu¼…6üT,ÿÅ™K•n¦ éÛsL!¤¥ •﫯i„BH!ae&U:á3U„–.Uþžö_ŽB!…Bq¾dª©âäU„\ËTf]þ|äå !„R€X:ßûÁfª„Br-Uþžf†ŠB!VúïýÄ!ƒøŠ(Rÿv+R%„±JÎéܹ3:vìÈA L¨üe©ü /T„BHë«–˜©ÒˆUJJJ0nÜ8üíoömÛ°yóf,Z´ßýîwsz0/^ŒÚÚÚ„mÛ¶õ,3|øð„ë¸óÎ;Q[[‹eË–¥|òòr¥m¬®®Fmm-F/}éK¨©©ÁË/¿ŒË/¿\ëýîܹ6lÀÓO?Î;ó“IZ„~i ‹B)Šs:¦J!Ê01êjæÌ™>|8NŸ>;wâ+_ù úõ뇵k׿ÅAyë­·pöìYÏc¡PÈs„ ذalÛ»'ÆŸµílllÄÁƒqñâE\¸pAy=[¶lÁ… PQQ‘#G⦛nÂ=÷܃3gÎðJòΤ´ÇQQ¦!„«Ìã ý¥’ª¾HÅ5×\ƒáÇcÿþý¨ªªÂÅ‹„³X—.]Ê‹ƒòôÓOãðáÃ)—éÑ£† ‚7:õïßýúõËÚv†B!TWWk¯gÖ¬Y8zô(Ú¶m‹Å‹£W¯^1bV¯^ÍO(É ™òwu£TB!+ßüFç©ògZÞ%$YåÀ̈Ž*))AII‰#V_|ñ…g¹’’Lž<C† AYYöîÝ‹gŸ}»ví‚eYxÿý÷±zõj”””`РA8wî–/_Ž÷Þ{S§NEïÞ½qüøqÌž=;vìÀ5×\ƒI“&á[ßú:tè€úúz¬[·óçÏËFùá‹/¾À„ V®\‰’’ <—.]ÂÒ¥K±hÑ"@ii)¦OŸŽ[o½MMMøøã=ëØºu+Î;‡¡C‡:Ë?ú裸í¶ÛpÙe—aË–-˜3gNœ8‘v{QSSƒ^½z¡k×®¾Ã /¼€òòrÜ~ûíøì³Ï°`Á¼òÊ+üt=¡j–¡’” ú/tA¿"„B ËoŒad)ãÕÿ2o{àÀœ:u ]ºtÁºuëðøããÆoŒ[î¹çžÃ¸qãÐÐЀ;v ÿþøë_ÿŠë®»ÎYfôèÑèÛ·/jkkѱcG<öØcX¾|9àСCèÙ³'æÎëdà „ƒbãÆhÛ¶-&Mš„1cÆÄ½öÏþsÌ™3Çù»ë®»â–©©©AŸ>}pÇwzõê…;î¸ÃyýL¸÷Þ{Ñ¿ÔÖÖ¢´´S§NÅM7ÝøóŸÿŒáÇã£>ÂþóTVV¦µ `ĈxóÍ7±}ûvŒ5 3gÎô½-EEE€ºº:ßÇá@yy9¶mۆ뮻³gÏÆÍ7ßÌO71âWÚ×,]ºË–-C§NýúõÃÀ±uëVŒ7=ô,X€Ë.» ?úÑœuÕÕÕáî»ïÆC=ädx^|ñEŒ?÷ß?8€²²2tëÖ õõõ:t(&OžŒiÓ¦aܸq€Æmã Aƒ0|øpç¯wïÞqË,_¾çÏŸÇ„ „ÇV:u kÖ¬ÉxŸÔ××ã?ø¦L™â½¸å–[пTTT`ÇŽ;v,¦L™‚ùóç']Ï AƒÐ«W/¬]»¿þõ¯1uêTìÞ½ƒ J[ÜBDУGTUU! aëÖ­¾Ã‘#GP]]G}³fÍ#]Ië*íqTâgÔ,B!¤ÅJU]ZF¦ÊÍ®]»0vìXŒ?kÖ¬Á¹sçPYY‰'Ÿ|ðo|ð¯ýË)ñꫯºwïî¬çâÅ‹N7¾]»v9E©­­|ùË_ÜvÛmøÝï~‡W_}/½ô€p×¹æ|ÿûßGee¥ó7gΜ¸eNœ8—^zÉ)ô0lØ0¬\¹簾¦¸÷QZZê¼×-[¶8û!U—¾èòcÆŒqªýUTT@DЮ]»¤í^yåìܹÿûßñµ¯} úÓŸ°gÏßÇ¡±±ÑÙþ×_”«ÄõN2T7B!„´<ŠóyLعx÷Ýwñî»ïbÅŠX½z5ú÷ïqJ›G»¥pªÞ%—D…/Ü’UYY‰yóæáƒ>ÀìÙ³qìØ1¥ì’›Å‹£ªª ¿üå/ÑÔÔ„•+Wjï÷ûˆ¾ËòÕc§OŸ¬Y³+V¬ð<÷ÙgŸ%m·yófœ:u GÅ?ÿùOìÙ³´ŽCó1s„è •¹­ÒNL™"„B K½iËËTE¹ñÆ!®þ…gÏž…mÛhhh€mÛøïÿ :t¨#ƒ ìÝ»Wé5o¾ùfX–…7â7ÞÀ§Ÿ~ªý>>ùä¬_¿–e¡¦¦ÆWˆL8rä`ðàÁÎ~¸òÊ+“.¿oß>À7¿ùM:t{÷îuþRèøío‹_üâxá…©àû8´oßÞéjs¶ÿ~~ºIðB•ä²$^¿!„’Ÿ'ýê,S•&FI‘©H8W¥‘´êÛ·/–.]І†>|ÅÅÅèÓ§D«V­îz¶ÿ~ôë×Ë–-ñcÇ0lØ0œ°;c:ü‡Ž;âÅ_Ä|€¡C‡:• Ñ*ñ^˜Ò8WüC”)B!¤µ ±j¹™*øßÿþ‡… ¢®®_ÿú×Ñ­[7ìÛ·O>ù¤S€" aâĉøÇ?þN:aÀ€ؼy3î»ï>œÿ<˜Ø4S\DáW['I7x¿B&ÉÀâ¹VexM⥋B)8ŠýyJÆT%z¶y„!9ó©”ã¨$á£*B!¤•béH4¥*¾…øj+ê/A!¾ÍJ$ÝU-"T’~]þ«B!¤%R¬*U¦2UÞ66<%ÿ€è~¸½0¡Õª …B¨¬¬äŽ æJ\㤠°ûá'ü°wK8œJÒ]Y©V„BH¡`åC¦ÊÓÖOsa0B Ò¬àH•rµ?ÉèaB!„´x±Êð«?ñøÿ!ƒH2/_.e9î%©+nBˆ²WIÜ|Tâw²àã²$ÕB„B)4±Êyª$ö“±K®Ä13Ý×$„TW¤ÈuÆÓ-0•|©]ë!„RûýÒ7?¦Êívv’ר‘çDÄ™û*<êÊp)íë644ð(B2ôŸhFÜrýÈ#‰/yÍPâÍKQ¨!„V'V¹(ìÊJÙ)–°vÈÇ+¶À†@ÄNÛ0Ù „’Ú«¢Ùñˆd¹å*™`¹ÝŠBE!„´f±JãRŠE.$“v K¢óY‰À¶cN¸: ƒBH jÉ8‰3¦T\¿‰ˆ§À…{ô…ŠBiX¾‚Ê*2}Þò.ÍPEƒ¡[B‚ªhW@që•@¬f•Ku»ýñúE!„’XIÒï{Ñ*AfÕÿš?*Vì—b÷ qÍ-ꀄ`Ü*R$‘r,q]ÏbݽBe!£Rì¼|B!…&VJQ‡â"®ßt“̦)O¦*úÏmiÞ_“ !Äð…12®ÊÝåÏI‘KøäN¨'ºž%¼ÆQ¨!„‚¥8©þhdªtʱGcÑ!W®± ‘Àƶ÷Åf„B ‰tû³bÝ%2̓å)³îÓ”x©"„BZŸXùÐ&…"ƒî1«l¡Øóvx!‘ˆvYá®7ŸÖÕ7[&ád…IVã@F˜Ü ¹Úå’â4S%ËòdÆ¥ù:ÄrU åc-<!„«ÜeªbÙ^ó‰VtBÛÕõ/šÖ²Ñ½Ï„ì`Û°C6lÛýlÀ¶Cáù°l8ÿÙ!ËDœ+FšHFmEqK49åçEɱ=ä MŒû'bh÷›:`"š\²t¾ùÝÊf%Ð=ü:ã1›gŸ¬ðXNç¶ËŠÜŽ>æní’,Vò3ÅS:BE!„´J±Ê‹L•r¸&F¤²ºçåhw@ ‚£B‘¹†ÃB&v¶m…+µÛ‘õÙ‘µF&v«ULßì ·YÃG1*Uºq²©†)§˜WÒÝnQm­›•*ÉxºÁ{J3ÐWñyÞd¸cÄeN©tXΤ¿QAòH–[ª"Ùªhw@Ë-bÍsT C=)U„BH‹•N¦*¿¨â’(b³#Ýîh{×fCÂ?Ûl B°QѱlH´× $’½ØaÉŠf®Äv橉Šࣻ ˜‘Ldª’ÏmÞNúº)škRJ(Šö›“{Á„PŒçµ2UF… …*¶ß¬ØñŒ™ˆ+w–ÊŠe«b,¬X gâà$Ý™¥"„BZ¯Xiÿ¦­#U–[jÂc§¢wÃc¬Î>!šµJ–²\JdEÄÌ KÂÁ”mÛ‘m<æt ŒJƒíoËó,Se" Ö•ªÔ^®šé2„f]ªt³T¢¹ùJ•èç-ýïZQ{מ®®€Ñíwu „,+:?•åé"èÎRÁ5U°$y-i6¿DóŠ€”*B!¤ˆUò&ØLU4H³¶Kžµ²Ý½þ­ÉHÁŽ´‹Ü;&WÑn€ˆeªl;ƒrº™*1U:‘±N¦Jtò9îú§ÜR?K¦&UºY*ݽ!V’¡ò• ’f§¯{bñˆYIÆ\%¼íšÞ¡Y¶J¬ $B!.VÁÄ뾤*a’ k%v¸pE´{tHUÜ6DL-Ü.,XNw@±cE0À'ƒ•YÌ(êoÚ`¬*¾Û*v¶Ü„dUŒLˆYî‹Tˆæç+ ÞD‘Š »þ%|¡æåXVÉ™dÜ%LN·?KÄ –»ë ³bO6Œ*B!„b¥à¨Ö£Ks‹XI2Gᬕخ.V$se¶ÓÐF,ÃåÎTEå*2ž*²Ož¬ÙËÚJÁ\@R%¾ÂGe1 ,S%ÁWL¼¸N¦*ϺþXs"Ss7Uõ/+Y*Iu\]båˆVø±Ø˜+W‰ux3Uâ.¹n¹»Æ¿¦dò B!…,V>â`ÀWñugèUL‚ÄŠŽ“Šd®ìð¸+iÖ¡Ði)±cU£]x¢U¤I4wVöÆT—© ¦û_dªrÜýÏT{íîz…3?g´¤J’ž»Ò|§H¬ˆ…#N@ÂRìîqXŽTy²]qgOfY* !„RÈbå3 ÅAü*F¼(…±aI¸Ìz8¸‰Ž˜Š9‹Œ»’XÙv ž:®ÿl_¯–ˆ0›©ò÷ºª!¬n‘ M¡RÊT©K‘J ™ÊT™È2I>¨ðý¶ U;Œ«(±ãïtõ\2åÊr®ã*î1YÞn±1Y‰K«G¯]*B!„b•iH§ÈûY*Zý¯yX±¤¸ìUT°`G˜;O!òT³+Áv¸d.7cª²]¨-7SÕ¢ÆS%Øz䉡Ï\¾I•òÏ nKò3(áðÌOÕ¬ÛŸçÒ’*B!„´J±ZÖ¢¨ÑBÎx+‰p –k{]c²lØ>^ÙŽñž©ÊUõ¿,Mü›wE*rÝõÏÀ„¿FÆSI¶ºý]õ/˜‰?ÉnþƒJ³ûV ¡Š2ŸrN!„Ö%V¹’ªXõ®Ãv✘I\ü#±’I”*GR•¤­) .SØUºÿ¶ªÊ:³|åºê_îºþÅ7>û±J‚ƒlyvZ©Êä<"„BH뫜dª’ÓÑÀ%^°\KZ®çšÏø«`‘&0ʲTéÏÒ'þe×?u)2X BK¨|Éô­#T~ö’ϯsôS}ÎÄÏùE¡"„BZ½XåDªÒÔ¯Ž/Åž(€¸ªUxžKšøÊ…T1S•ÙI¡;ñoŽ&îµCŇ}Gþ©0P B¹¨Ž{g§/°ããW J!„ÒšÅJk¢RR•.qÿJl{ŠM¸ƒi?ÁiŽ UXý/˜LU /R¡(rÙïúG¡ÊŠPY¢|îøËPex=¡uB!…+V0ZVݬT% t\Å*2{ÍVÔýÏÇæ3UzÅ"ÔO¥ÂO…S¤"7ReÒP’]ŽŸb–ŠB!éÄJg\•ò0{E©ŠËT¥h×}™*í[V¦Jü[@ RU0]ÿòenª ÇR©¬G\s[©»›˜8ì„B)±Ò-V¡/UªøváQ•*¥°®©2 UJb•'™ª|(§.ªþ‰óCKªÌ¨PÛL1p™1Ñõ¦E!„ŒX©~Ñ‹1©R ptz[eEª˜©ÊLªrP¤"Ÿºþi978ž*˨›ÊRQ¨!„’e±R ôʪk„Än«d/S• ©2/ª½£,ªÐ5eÉÊõ±÷ò¬HEpä¾ë(U„BÉŽXew`­qUù,Uœ©Ê‡‰ ¦H…¡ù©Z|ŠV,TÔ+B!¤`ÅJEªTɾT©m3UšïȨTé½¼¹L•‘rê¤ÊÔ ‘ÓñTæ­ÞÚÄ·ÎSª!„’L¬ 0S•* RªrQ¨‚™ª€¥J§HEvýó=4Í×ASUÿT&ûÕV! !„B̉•ø#t¤J§`K’*ý¸QMªt÷¦h¼‡\wÿCî?1蹞ð7‹*L?J!„B|‰•h„’ƒL•ªT1SåOª²<Ï”n¦J4Ú¦9¼™íõ¬dªOUHY*2ıT„B1/VªH–ÛéH•΋3S•·RebïIàßTå¼H…î›;©–Q— OSB!„´ ±22®* %Õu¤J¢\IU ÈTåvâ_Ý"ù—©Êïù©r]õBE!„|+cÅ*”bZ­vYI0hIUpñV®3U¹•*s{ÎD¦Jò(SÌçJ·êŸäô³B©"„BHðb%¦†ígžæhÉ™*Ýh›™ª\eªt¤J ¾ÛZ×üTùUõ/_„Š–E!„®Xå¸XE. <ÛÄLU@R¥³éù'U¦N„ü,R‘þRª!„’±RþªW­‰­=°ÂH.QŒÒDÝé+fª|IUŽÊ©gÞ>˜9ªrš©  ”:³T*B!¤uŠ•(¹(VaD3•*1Òm«¾ê–™©ÒÙ:“÷æÉÄ¿ÁIUnŠT´H©ò¹rJ!„BŠ•‘*µE}þÉN¦J=\Í$âŽ_³hl3Uþ·<+R%šŸññ9Ò7û|›ð7ëBåC†L 5‹B)4±R•*Õ J]ÔC’Æìä2ÛÔº3Uæ Ud/S¥s¸t–8tÍW” ΓRE!„¿be¬¬º‰ùdü¶Õ¯è¿m0™ª”03UHU~©P=KòµëŸ¹RêYÊTQ¨!„bJ¬LΓy«,«md¦*X©RÜsbâ@èNüø§F·HE€R¥¯!Oø›¯c©(U„BH‹•ÒW¾²ê¢QŽ]'ìË,žV9Ó¤Bt2UÐl›ëL•G‹Zh¦*_*ÿåçüT-½ê…ŠB!êXŽ¢Ü.—Å*m§ò*ǤÒ"2UbàäÑÍTiwÿÓ= –S7±YY—*Pªh\„BHáRœÑ·»jYuU©Ò‰@ ÏU\¦ zsU1Såë]K”So±•ÿ ¥ê…ŠB!be³¬ººKé—L–À78?¥J÷ýL¦ Ù8yZ‡T™:¦ÜB‚üXSª!„â“bí Ûw[ÕòèYª¨š©J#G¹–*Ñ1fª²‘©Ò-R‘Ç™ª‚®úgB¨hX„BHë+Q  %²R¬B ®k£q©Ò݃ÌTeÜV§ ¥J{ úׇÀ²T”*B!¤õ‰U¶ÇUå$S¥zjÔИ«JGªtúq2S•‘˜*R!š'^Öç§*”ª’Á¹B¡"„BHZ±ÒýÕú¿-+‹€f;ÝbH•šb艑®©J•dë,PÍT;ə߷&ö§È—"&>•”*B!„b•8ÀË<0ÊåÀ¹ª¨“åJ¹Ö,eªt¥JLdª4²Mºc¢DSªZmå?ÑÔ¡¼­úG¡"„Bˆ?|Îc•…bÉeYõÔm%¨w“_R¥ø³›©2/U&¶PŒŸ¹“*SRaªêŸ})H•‹B)Š“}Ó+uÿÑ’­Êb¦J'ôÅK§•Ρc¦*#© .S•™åO¦ªUý3Q ‚BE!„P¬Œ­)g]Õ·3ó¶ªù„`7ÖøÀ¹ÈT™9õLÖŒÄHg MLI–_Ýÿ² UþÏè`¥*KWB!„ä7ÿ®vfÉÍëIEND®B`‚goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/images/screenshot-kde-5.16-01.png000066400000000000000000001762761414415210700272120ustar00rootroot00000000000000‰PNG  IHDRçˆzi©zTXtRaw profile type exifxÚ­šgrì8–…ÿc³x³؈ÞA/¾RR>SÑSª§L%™$pÍ1Íþ÷¿ŽùþËÉfS©¹ålù/¶Ø|çMµÏÏ«³ñþþú,¼Ÿþò¹Ùå}ëyÕ)Ïi6ïçÕu>O?_(ñý|üú¹)óyãë{¡¯;¿ º³çÍ{^}/üó¹{ÿ6íý^Óyÿõ~–ž—ßÿŽ…`¬Äõ‚7~,¿«îžý¾êwâ$ï=¯úþ;óýö·àõ÷æ¿ÇÎö÷Œðk(ŒÍï ù·½Ÿ»ô÷ØÝ}ŽÈÙï¬ýr E÷UÄîœUÏÙÏìzÌD*›wRö½Ä}ljƒP†ûµÌOá_â}¹?ŸÊ'[dsð3kÎíã¢[®»ãö}n2Äè·/¼z?‰µ>«¡øægP ¢~Üñ…d,*™˜d-ð±ÿ‹»÷m÷~“I.»gzÇÅßøãÇüíÃÿËÏ÷…ÎQé:§`ö'VŒË«¦†2§ßœEBÜycšn|ïùNëÏJl ƒé†¹2ÁnÇs‰‘ÜOm…›çÀyÉFcŸÖpe½ DÜ;1È€Í.$—-Þçˆc%?‘ûý .%¿œ9ä&„Lrª×½ùNq÷\Ÿüó1ÐB"RȡڅdبŸ+5ÔSHѤ”r*©¦–z9æ”s.YÕK(±¤’K)µ´Òk¨±¦šk©µ¶Ú›oK-·bZm­õÎM;—î|»sFïÃ0âH#2êh£OÊgÆ™fžeÖÙf_~…Eû¯¼ŠYuµÕ·Û”ÒŽ;í¼Ë®»í~¨µN<éäSN=íô﬽Yý5kî·Ìý笹7kÊX¼ç•Ÿ¬ñq)_—p‚“¤œ‘1/Êí•3[]Œ^™SÎló4EòdÍ%%g9eŒ Æí|:î;w?™ûy3)þWyóÿ”9£ÔýdÎ(uoæþÌÛ_²¶úe”p¤.TLm8ÛÎgV·[,͆åûγ§|Î(¢Î\ìm(5íÜÑ…5ë‰}s4EqÜo›֘Š´U‘m8u¸SâÉ!¶“Çðg3¬CÇ3ТW—basœ±ƒ<¢93¥xHBáÞ#·åÛ O©º2r'”Ü(¥óïtÛ×´g¤U71Y³ WÇš)J¡·E@ÇÞLî,ð°Ì÷ΛXŽrœ_d4r¦£¥ÚƦ<@§÷3¹P0;×Úçœ+×ÖAº{JµKª÷•{­x¶çŠ~§¤Ô9ÏÞ)•pÚν¦Om˜Úv­³˜Ä9™y¶î]΀L·'÷•F8vÇS<õ‰K‹¾Œ“f! Ç–ÚNÌÃÀƒ1^ä~ì™|9aRóÔÙ‘käÝÁ1BÌ}–c¹QÕWšb·¶7äEpÿ||üú8ÛV†)âçR‘Áqz#ÏçSwå[¹E$Š´\"íƒ!“ ¨«$W`ãk÷ì\¯Žó½«µPòv2Ó˜=)2¨atçxGÞqŒÕ)¿žFÚ£Ç8íÌ¥©qIÚŠæi–Ú9‹f;[˜íJ55/牦ßü|±3¸5i ¾Sl¦º? Q:T7£äm ¤¨ ÐCq®8Í!nn%rQPÊË5nYG®°3˜¼n>AQ‹!‡1ˆîéÖçvU²w¶3´Cp OÛ¶Óh¤>&+HBcÙ%Ìe†%¬R§SåfЋö ¡»|ÜfÍD“ª¯gd{Â:a¯´×3žáŒÈô+CƒKò8ÈäÁÊÞçv\lò²Ž¥³r˜ÕÚ{sŒ¸'b*MG£¸m$ KE2w·ËÚÃ18  Íò²ñxjdô~ˆ#å`w]¹øQA1*#µ¾‘…zŽRßél‚x[ÑmcÄ8¸[nÖ3§(d>Ú–Ðó€¦jk3}ŸD·3ò»¡!…¯Tð+ôÕa“‘Cß ÙæêàÆ¡ Ê¡ž9…WÉÌÍ¥÷ؔߦ†ÀÇám4“¨}o> zÍ5¢äÀ¿©pO’»ø·ÌO%0H[›¹Ï&ÚY¦ê8ªXÝ7ìá»’Ÿ'.cÁ[`¢É3ì €ç´Ñºmf!q…4[®îáò¢†7zBð²1* Mº#úª Fô–‡>| xÀÉGÚSXC\«¬ÀQ¦+'iD—7¼‡Usœ°6²Ìž‚ñÖ #üÙø ƒ¶Ã!jh= РCKχ«Ïp@Öêï³é‡e|ŸÝµÜ{ ½XÚÑÒœAŠ,ÔÞrM½ÆÝœƒŒ¥Ê‰Ï=¸ÞB ÀkuÓRª` ø†ØøJ1(Lt'ÿýs ‹‰v"oòµ".h[Hë²¥2Ò¶YÀˆS…°™P9¨ ¤q2üaŽ'k !Íé*2RȽ=½} ›üûÐ>q¡Å Cm´ Êà6[3µH΂¶C=¬Ýñ+º2Íeé{ îÿO•_sb5Ò¶Ü÷ðvùobfnÐf蓼Tô`ËLz¤b‘…" öìÎÜ=ŠÃ{TPï<±‹Ö÷è§\ ¢0Z’æ:é*·Ó hd‚°… HQ.Tu]*ç¿}n@ùåè%Ay…Ø¢p9”´R¾@ô%î’ë?|>P“3ß!©?»ïþÈ ,W OÊ_Çæ{ì$ƒ˜ý˜’ ¹Ö<šîïÀÕŸK¾ßÕ™ÐTÙœçºBz.`¸Bâ åéä LèÝ×ôïUlÁÇö „ôr)ÈmÊÀîä›fÓ:èhÌ$)|=¥q–Ö4V@.¡³¤áC­Ã¦µ[¸ë]¢žNÓÐÌr- öºNé»ÐÛ]$ µæÐ/IÈ>1- EŸ1ãʼnNB²Àa&¡ÿô¾w'2I¿ôÍÌÈÚoZTEDã(&À‰*O¨OjŽL"D$—d [éÁ.âsˆKÆ%Ò•2qΰ:4„ø†Î*#iŽÐ|_û}Ì왉€RÌ ô¦êSÞßhV@ÿ ¿4/ILa ›°n` ÜÔÁ,´l¤ëŽF}wè+`™§¬‘r„I“I'O)u(´š^k8>ÄíÞ“.ÊÆ#7"gÔµÖÎPã.‘Ë k eæd"ªB+ãpž$¸PåÛAºQG¤;£#pÈÜ,wK8ºì˜Ÿ#Î)H£h µ–‹´6‚ ÝÁ[Mñ¶n´Ž×J‚ Fµ‰™:ˆ‡Ö=ô5ð2!5¨§.ˆz{y7Ðs®1ml­ßÓxô®Oˆ¹Ò‰ †kÍ “ßÄÂðíDSt;9²¥Eœ”ëî´ÈéW»u }Û;B¶öÓSÁ –æwÜHf ãã49ÈÍÕ ,ªH]3hXilú‰lU@Hφ}ƒ¯"ÌÃÔØìÇ­ƒô90^¤Ãhüz1:˜ÑÕ)ƒj¡²`@ÌáÄóÁO9*K|#ä‰XÉ󯇻žqµÇ»HyKàTÄ—ÔËB Ô<‹HÆ{Ù—Žc€²ÇÂ…Ó+ÌI¸¡-VÆ+¼C@ðlKx“DÆ•,•ñаcèg¦X!tŒ'ö‰n#ýx|Lärwif¡û#Ú½ i Ùd£„’üY§^Q°®.H¢‡\V5îq .›Bs ´b'P`dÀt“ÝBTou¡Žb Í€€kúäÛÖh6‰NAd"UµvIŒrùß~¼">dPþòFsÇÚ„ßÿ!3‚çö«¼^°›+óÚI[5im{–6Eؘ"?º“¢rªT„„øÓQ«4 .Ú>KŸ;ös!’ÈUÕ]A³r³˜Q±…Ó'ØðM'ÒðTnIˤhÈi=ÐW¨(] UrÑÔî–gDE+Diz³NàŒZ'‹HÕ[i`ÿ ³`¸6€W°Æ]=¢ñPy ´F‰ 5ðêàffŠ Ô¨ÂKW€69Õ -C(î1; ŸâÜBÄA67fÕ:’R®]9mhÉÚ„ ô, ð|¿ŠøªÚ¶õiÕÕ‡2©Ó‡¿5~Ôžój‡{ö÷©¢éï“›xØüœëCÄÃÔ%³]%õ11[ÊJ©G¹ÎØ¡TJ5©°G€Cu+,;9 ß<®WU#Í•pa7N ^r`¢:· ¤}Ç„G°;(Ês”+Þ Ìg~|Çk?Uª¥[2àW‰t£ÊøÍ‚ý!­Ç€%X»Ó5]\ðÃŒ§Ñ‰-1çiW-¸ =Á j‹{  ņ¬‡Ší }bÍ%i5°€ðÇF£RÐd g’s-ÒÕžíZŠü·^ŽFÅ̧s/`[êUkˆ÷áZšÛãÉ+|ì$Uð´÷™8^fàG]ñöêæ=Ò;'à1Jâ¬C.Sé§øèš Zgä•wG5zÓØˆïìϽ?u}» ø£Ük‰›k-Cai¸øÑlŒ¸ /kÑšSÂM‚¼¡åJ…Ó×°>ŽKâƒl"=²) *RØÐ)& ·æîÞ¡¥Òu²Ü/üí•æ­}xÍÞÅ5§FL`A+‘|¸0‚²¹Ëª7èDkT__úøŠ±éYÍŠúÖó¯ o…ÞjL>8ª3Ý%$K›ùgÝÈf‡ÞèO»º¨2;c@É«Qœ³]<qË3Ìt¹×iW±Þæ„ÃÆÒúEÖŒ“•|º«AŒ*F´ 0G§!žIW[¢”ºVA‘-ÂB¢xƒf—fì+1Q³6¸_IJ”:JK2Ýhµt‚E­ ý×*éh™Z9nP-+Ãv ãG÷®îg‡÷ê?)P$6“Öΰ¯%®Ïdh5 NöÝ⤡•ÆHo± üÆŽb)lÚÑÙ­á2çé)Z›úAòíu ¡¬8²7Á.jÈË,¶¬…‚Ràk+³Áb4<èx†ÈÀ©2Œ)ךr§NGiBCb—Ïþ£o–¤ ¯Öo¦KË¢ê@†Û*ͺúV6#7Q)Ã!­ÑÓëKôƒ®Ugq 4ŠWGI úÛ1õÑÖ±]tÿÊZÎì±-G£z-5EìhÒò&Zt£1#tïï*”¥Ö:RO€€x´o¾:©€Êæc?<]±dëÁà˜ž^cv|œ± ³T‹ì"â[,+Za ?wÑ1¶­ˆT!ÛZÍ.0ŸÊK;—²ÊÚÌBÏŒœ¼,r ^–š%%-Á È“Q3î¡\׈«£8 Lm"^øÿËëDã£ðˆ:vÉc#Y.Ukce<0¿ˆ©òòx¿!µùõ•˜`8Ëùc$>ûM¦ä"À¹‘âäþÂÿÓáˆi¤¢UÞêj{+EåàßcèºfdÞØ:ç(Z5Gêpü kvRÓZ—OÜ ­ÙŠ#ÔrÖæ<{y¡ûEñSêÈ—ÆÜ†*ù*oä:OM^µŒ[Žö4O”H"Ýòˆp»V—-œÏ7 îAïôDD5Nî+ÒI_ŸY‹ïì¦?J6ÁǾ—=µ6Ǧí¥4IÿÉXõ ;‹Ñ£±¬vs´z ó‰ÜuÆÏ"Ïì& °Â¾Â[F×i‘Ä-åŒ!‹Zl€ø÷Ol© °óaþ¯v{šímµDØàÃWÎÀˆOn¥ö6X]èqåÈè‚}È´Î R„R:rÓÃ{h Ý´wŽAŽ JÓZH¤q±à *s´/J‘ú÷HÔ’ñGYªöPµ­Î[|о_ÑŽ} éH)8²Yš†â§.iš›ûépã³kMfÊÜÇ@WZ‰Ø «Îh8 ÷‡ô .ˆˆ¥Úzªöœ¤:SC>àÝ•¡š·i˜eÔ|‰PÉ@0UJÏ®Ýõ!T"’Ivjõ¾Ó?k7sÅ›¶àÐô›v;Ûfi·ø¥Ýúv£ÒŸ/•«ÝÌûàö³É[QW»õíy]×ß{Û.p±TgƒðôepãÚŒ‡Œ †¦š +üAH¶öû´Ö‹–K9ÀÉÙv­}È¿¢Ü; 5šv£éºÖ½öGñÙ¿‡ïÀ{jõ“¾A¥Ôæð21R‹ ¥oñç¼6GmQÛ@‹L—gw ±8mVi“(bšï“-tèÝßÜ1* o£õ)2Ìüƒ–Ï«ö— Xk†6GëfÄÀ¾‹±¨ýRRœÐAH¦A»«Mešƒv‚Dò¢µ\3Bð ¶¼(á*“,°Ü)ä`„ÓÂmr[ãÑ cÚ9P°_à\Þáçü@sQcï^•ÎqZU‡±3\8JMH›cqØPoQT¤Ï˜Mž #ª@‘ F&¦†±[a:&ŸD#– ÌqeŠÞZ¸c(pëör›ŒžGØœfÎÖžz“NÉŒæ½SU¸¨ç xêõíÚ~ŽÔ9è“ýFH,­ø`FÓ‚__Ô@ÕF]†„m^z-Ó{9À±4Qþeâ¾,öpØe0#{ˆ™vÀ³ˆOë0ëhY­` 5݃‹_à²u,Hàot90[ç*œ¡,`ƒãµ" mX!s—0Œ2ÏÍ4”ž­À°Rèñªeê!¡b¤€¤5 å„ ÑZ]zܧIZ/Ê8ÃX«°c‚33 ˆrI§uîà­öü¨†}×5û…Ð] ¥[µp3Ôn\‡ZBTêuœBƒh=)‡©Òú0v;zrRì)t"ÖP  ^ËE(qÚô]6Ðüè<-¡"òr36ú½mv ˆ÷µ›|p©(ºJ4 Lh¦šøŸ¥k~—,±(ã|ŠPçžGT´eDDó%\Ëý†Ä®ó¿|§Lx¿ñ©uµÒæÀkÝný³šb• 9ÇEzGMZÂ¥t°yé®mp…£uNucysksÐ˜È ¼qŠ.E8гð€½NßÈZxÒ1A“¢¾&g{™&hÎ Sj±$A«¤Ä­*pC)°z—k1ÅH镵o`骬֓,™èT6vCõ ¾©ªS³“þ,'4!u­×Ví"¤¬ç=üäN |P= /À5PGs ¾3tä¾6—õ|I A»ÚðC‡p²š²»kÆÅwâIOi]ú.lüÓ@¬!—I0Öv;ò±’?QKƒPÀ9÷T¯ÚHð¥m¨pâÕÅgÓ3P0(§Zú+_H¶žÀšÚæA|Ï“›D -üž}=ª5VG±b³€fȪ‘B1dZ ÖºÎä‚XJôKÖŒ§ì«ÐQÏ5:·¡ƒp°¨\ªŽÊF@$=—ÔpÕ|Õ JDG`ÂùjgšPÏç&“9mâ·ûðÌ–h„û)Ù­õ^ä`Ño½ ÷±èrqzVBN o>‚DŽ¿€#â/&P®µ~ ®†àD-ž¦Çµ~´2E¾’fhšÕ¶Î=RžÆ2Ÿ}ø†×ï~¬¢ÎrÏñužÇ`fJz°d5ð>{¦Žôø¼ÚC…Ðz]‰â/èA0tÊí!нԭV±Î:ð²6âÜ=`üíéÏc÷ޝåâ$ ’cÜ(·ùìžFûìÏŽg?Öêé+ó}ü9úãVle±vþÐ_nË Ì߇{äæz6DÆ’N¾Kï2ѲrG…~ÒÃ!xý<&ç,iV5/j£5ŒÖÉ•aÞ/ÊgªS~èéÁ÷9ïüÓ‚M®Q¹ZÐQ€¢×3’à}£(ÓÙÐ#ˆ2¯'%®Pìf-î‚\zÿ|pÁ¼ÙXè Ä·…©ˆFË¡gÜíÝOÌT¼žXRÁ”·ž3Š÷ÃËì÷È5Å\Eªß¤ÌÄÈLÚ¤42=%†Q<ÚË×.®•"ñpêÊåpècRÀ¸v¦žÞ±4ko“8v=@¹lnp…¬©u’PŒvcª[A»’ È“Ÿ”/9Su0#ͧ§·õLÚÖfm¨P:^sœhȶ´Û¦šû< ¢k{nkø’Uv~ÑÍ-¯çAÑ ;[ªmXh¾šB7ÚÍ´y„d}c>hâ¨Gà`'Ü×®8@'ÔÊ|EÈžR ½=˜ Eô ŠÁŽÌæ©m‹ØDÅS¥KšëÑ[E GÄ;CÕl]¬·Tm“–GºÝË®&Y…‰CP;nÌ$K¤Ç_ч¥éqTâ¨51<Ê Ì1¬GpÑÌx·¬­ÔŠh~zÔqrg·`Ç]Šè E–»/¦ªet횛഼n¥uZMG«ÑÃM;Q³éÙ$yn):‚dêz¶A»_¼Nmú´Ð æÆ©ý¥!fq]°GfÉ4w— »£ÿ(m‚£Û‚B%¾Ä@‘…ŠhI~MÏs{Ú|Ja¢4êdãОˆ\$z][Êqßu^´‚ù_HÆÊÁïñábKGDùC» pHYsÄÄ•+tIMEä ¸Õ†ú IDATxÚìw|TUú‡Ÿ{ï´”IïB ´Ð«(EŠXÖÞ ØuUPqWQ*kïµ€uíXv-€]é‚Ò BMIHHO¦üþ˜ÉIOHH{½&·ž9ç̹ï÷¼çœW™qþuv„£ 4zÜ?(o?R÷í¥áSˆé‡ÑÃDÊ®$·ãf_ú „‚êºDQÜoèø[9¶_9–FEQ@QÈÎLçо½ÇÒ­(µî£àøóØ=jžë:RëºcÏvÿNµÓˆR}7Å-?çß Jí,q¿_û4»ŒZpnCØívl6;6› ›ÝŽÝÞõ.&“#ÇÇìãÓnϨ,/'==Ý;¶Kc!‚ÐÐëuhª*ÑÍÑ©ª&¹Ðx›} '7'‹òÒ²&ÏÏÌHcð°èôF¬–*—1\\\DNf&áQÑ5 ùºBÀÍ(®6ÆkÈá‘ÑT–W™ÑzaදÚÐ?~qP^êâ ú>š¦ iŽÆÐfwЧhèj˜Í>Œ?“É£]î_ZRÂöÍ›H8H^ ‚ Ý EQ¥mï)¡­ )¡¶@ðAÓ4bbãØSíh$¯­V+Yi“›éf g¤¥âëç·oýF°¢Ô1¢7CÞaÄ÷ŠïGeeùùÇ' Žl\Ô¼O#â ŽP¨OtTEAÕ4ÐÀØl67/Cg& (ˆcÆ¢ÓéÛåþ¹9ÙlþóOBÂÃñõ÷'#=UA„n„A'Ê=g³l-Û”F6UUñ2›Q¿À ̾~(ŠêòSû¿×åfgãåíå8WQ]ûö&ïÂRY‰¢ª¨ÎMQç¿8?«ŠcSç9ªÓˆw¦«ß€Áxy{»†UYªÕ‚ fºêx \ÇÚHP¿8¨¥†:_O  ©*:Mà×a20ôèu÷kgßá‘QŒwR»‰ƒÌô4þúcK}úö /ç°´°‚ ÝMsØBÏ@§*â*js…m0 ×\ÇÄõaç–ÍͰƒ òŽ`Ш²TÕ[CU•…äÝI 2jŸuzý•ÚºâæiÐT•C†“´e3ee¥u¼õ ç©9œÈýPSCŠš)”† ðíl”×’Tíe°ÙìNOÉ÷2ôŽïK¿Û-oR`Ç–-€ƒÁˆ—·7…GŽP\\$ B7@QôšN2¢' QƒmO•¥Š‚ü<‚‚AQðñõ#($”¼ÜÃMÀÅE…è «R§×¼¬¤”ý{’‰0Èåp¿ŸRÿ°#7¡`4y8r;6o¢´¤¸qaàJh]AûˆƒÖÏ;è”*/ƒVC‡WÏc°Ûìí<ñYa`b"1½ãÚíûíß›â6¹ªª’Ò’ö¥$KC ‚ÐMÐ4µóùÚY H‰· ÷¥`öóÇh0GA~¶&ŒAEQ°X,( LÊÏË%ýТ{ŹèJ=cökˆ‚Úž½ÁÀÀáÃIÚ¼™²Ò’:B£¦÷ÂÝFoÀkà&êÿ­­èáè ÔœÇΕ’ìv×\{ˆM§cÈð„†G´Û÷سk'{“w»í³ÛíÜ¿ìÌLiAºŠ¢ ÓdîAÏ2½]°ZmHÙCÿÁ‰ (xxzCVú¡Z?<__ ŽÔ1”kÙù®ci©xy› …f ‚š§T3L$ÉŽ­)).n–0¨×¯=ßà8ÄA#_¼­X×m|µZ‰·W ‡Vx<=½>zl»-cj·ÛÙ•´ƒûöÕÉsʤ®ô;‚ tô:MÚôž(¤Ð[mÖ5yFaÁr²2  &¶Gr³©¬¬t3Í>~‡r`o2n6`ƒcóö&ïB¯×ãTËð®_\4ÿ@o48l$Û·l¢¤¸¸^AÒz´Õ 5â@BóÐTUTÔÙ¯iý‡Ö®"dÛæd¦IlA„ž€¦©èu²rQϳç‰@hÏ vš¾Q½zë˜X\VZʶ¿Ö×Ò+>°ÈHge²w÷ÎfÔ:½žÁÃGâéå]ߩЂÚ¼Ýf'%y'‡kO2­/nAû¸ïoîp¡ÍK¨«WšU ]º)EîáªÜ†§‡—'ƒ‡ŽÄÓÛ»]ÅÁÖ‘•‘.?hA„b½˜ z™{ГâÀa'-PÿZüŸÒ¼ g<²¢£øa4ÑôØlVŠŽu ¾VTT@HX8¾þ(ªÂÑ‚‚ŸYó:»ÍF^îaƒ1 ®IʪR7˜š[|Ž¥ÑPÍL-(8ÞHA~ž[ 5·aKŠ»Xi8p¿gëŒúî. &# ƒÉLK¥²–÷ 8˜!#Æ`4™Úíù6›m›6’™!­¥ BA§ÓPe›.†OXÐ\cW¶[ ÿ…£G  DU5|}ýÉÍÎÂjµºîi·Û)/-%(4_*++‡kF8®¹Õ 6›¼¼Ãøb0šuDA(ÏÕV·Ù׿€@Žäåb³Z´ÈÝ„@46G4l¿·¥8èºÁÇÏACG’²3Ém)ZEQèÕ§/} vdkq`µ‘´eGò£Ó4t: MSÑ4ÍÍ[qEüAº ½ -0|ÂjõHËÖ>›ÅbÁj±Œ¢©˜<<ÉËÉv3úËËÊ@Uñó÷Ç?0ˆ’¢"ÊËËêx Ü–4un6«•Üœü1M z ê5|çf2yÁÑÂ#Ž•—êó¸‰÷ût¼8Pº¬8 cÀaìܺ™â£…®ýÞf3ƒ‡"$<¼]cÈUUV²mã ŽäÕÊ÷êúãð6iªŠ¦©Ç„SDTSUÅ-€Ÿˆ A„Î^§C•‰É"/¼ön™ƒp‚I<” à`vmÛR7² ƒ‡ŒÕjeǦ?)v­.Ô´Q¬ét : _?ÿúòz¬ìú ·ê}6›½É»ÈJOk âró­vñ4MTL/úôïÏž¤$2Ò¹Ê"ºwzõ‰ow#»¬´”m7PVZÚnϰØíŽ¥[mvìØKüVïs®÷Û~Q¦A„úÞûFƒ^2B@'=z'ž½»’ðö‹É˃¸¾ 9‚Ífu;gÏÎíxzŽÁÓË›CG°õÏõT”—¹ŽL&*Ë+ê5šmV I[63 q(þ­5Ñ4~aöñeÿžÝXCŽZ³ -ÄAw§wß~Äôî8âh¨šJpX8±½ãñðôl÷ç-,`û¦¿êL†nó—³Þ(šÒ|1a·ƒQ¯¨a!‚Ð: ²j‘Pýž¾èº{äMÚxz™2r4š¦‘~è öî©çO†Ž‹ªÓ(/-eë_°Xª\†|`p(¹9Ù >CUUúB@pHƒÆó÷;>—W”³wwGn¶8h\4_´LËv-Á¡(ß Ñ±îÆ±Ý~†åÎÎb×ö-ج¶nñ«#,\;9æ±p© ×!‚ ôHTMÁ ïàóå xŒ¿ú #ap"v»Íë×QZR\ÇÀ ¦â0 رé/×ò¨††‘v`ƒ±âúõ'<*¦Õ¢ ÎwPr³²IIÞIUeEó„A½6»Ò&ùØÕ‚¢(ô<„ðˆKCÚ¡ìݵ«†™,Ôõˆ êÕ¤Ö%uþ¨/§["HŽW¼t´÷XitžRËÆ¾Š‚Òªf¢UékF‚”ÖÖ½VÙí>¿Þz×TÝoìwÓ†uZ8>LFƒd‚p¬­ºøú{åÙôî—@dT GòrÙ±eS½çDDÇ×·ŸÃ0ÏÎfwÒWÉÑd"uÿ¾FŸÕ«7½úô£þØgJƒ¯¯Ú &Kû÷$“žVç%Òqâ kyTUeÐÐ8礜x[ÀNÊîd:(?FA: wóì5àïßñ£Óiè4M2Bp¡ qÊɆö–a o…Gò1ûøâÌÑ£”——¹ŽU¯S|´E¿€@¼¼½Ñë ÉËCA¡¤¸ˆÈ˜XL&G \Aéhaå¥%£:ã"¸–Ç©÷“ºoŸcóqÌ5hA2º¬8‹ˆ$~À@´ê±ÉËÉf×öí®9-‚ B]ïEµçÂf·»íëN ñõ¡ 9qdCÇ©v;w8ÿÀ TUq ŽVs+8’^¯w1󠪪’â£G±Ùlæç3 q(U•”¹ûjÜ£¢¼œüÇ  FoÐ×õ46vÖÍëPm’[çÞ×ÏŸð¨h°CqñÑ hËãtWq`öõeÐÐaDÆÄvH”J»ÝξݻHÙ½Ë5—EAp[Õö\ÔôVÔç¡PðLtvTôz™˜,Ô'FM’@iµÕ2Ürs²  °àHÞ‰êF« ?ƒÑ„ÙÇ—À  J‹K)+)¦ÊREEy9 ƒcµÚ(:ZØ`fKU9™Y˜ÍfL^^­õYÓ4 ÿ  £°Ù¬”—´¸§¥;Š/ooâû¤OÂL’†ò²2¶É"A„¶ ¬OHT¯>¿3 ×ë$€¥P¿@H9qA#Cäe;@A~>F“'Öª*·žŒc¢B!?7OO¼¼} ¡¤¨ˆ²²RJ‹‹ÑÄöŽÃ`4r$/·ntcçf³ÛÈÉÊBUÔ:cà›- jÞ·:½ŽÀàÂ#£PU•Ò’âfõXw'q j*!aáô4ˆÞ}ûáåíMGµÃ9YYlß´‘ò²RiõA:@HÔöFÔ¾vôùv}G) “¸BCõöÒ›9] q©oo\B‘Ñ1ØlvGDæÃ9(ŠÊÐQ£1ûùQ—Ïέ›±X-Þ?(8”„ÄÄ:ãá\°K Ú¬V2Ó3ÈH=HiIIƒÎ)EÁ/  àBÂÃÑu°ë¶¢¢‚=;w—“#?$A„.ˆ½FÔy›sþC[̃0êu(0ÔUè"¶çe"º@)5lGÆÄÒ»o`g×¶­äæd;&-‹‡—'eÅ%l߼ѱ:R#xy{3pèp<=½Zœ†–÷G ÉLKãpv&V‹µKгѱ(ªÂž; &($”€à t$ÐLVz{wïÂb±ÈoH¡» ‡z8&jˆ¦PUƒÌ=7?(¡‹JÂÀDUa×ö­äfgc2™2z,&“‰ÊªJ’6or,ƒêfb»Ù:ž‰‰ø·¹(¨«ÕFîálò²³ÉÏÏÅfµvjq jÁ!¡DDÇàãëçj¢mv;ªÒyzaÊËÊØ´‚ü<ùq‚ ôDñЄpï ¡[—Þ±¾~þ 6¦gOÒv²2Óñôòfب1èôzìV;»wnãpVV÷Sèݧ/ѽãÚE4dàÛl6òssÉ;œÍ‘ü\*+*:…8Ð4€ `‚Cà FíÔKÁÙI?tˆý)ÉŽ¥fA¡–pPTЩ:§ˆ°!1åÀoO<½¼I6“‡é©Ù—¼o³/CFŽBÓ4ìvH;´Ÿƒ))Øh|ÂpPp( ƒ‡ Ói’e¥%9r„£ùy-ÜKàNIq1û’w‘Ÿ—+?A¡QªWNj  ‚rùß’"ïºJ¢Á½âú×‡ŠŠ ’¶nÂfµ‘8l“ÑñãÇÆ½{IÝ¿Ÿ†"7ƒc^ÂÀÄ!øPaPS4ÞˆAUe•Ø,¬+««Å‚¢8Ö›VTÇ’q:ƒÑˆÞ`D¯7Е—~®¬¨à@Ê23Ò-?AA¨Æd4´úZ "„67Ø;¿€ $A§×qhß~r²2õC`ñ«Ï3~Ì(É@¡^"#›-ªñööâ{æ&(‚@uO·˜s'EQÐiFƒƒA¦©(Jû?³z«ž+¡ÕÜ4 MÓÐéŽmzîØ¦?¶ôz z=FC#›Ñ€Ñh9] o//.<÷oœ4n4ÁA”—•“šžÁ‡Ÿ~ΖíIž¾y÷>„­›¸à¾ûüÃæ…WHm·Ýx­›8ÈÎ9LhHp½çÖ"™(BGëaQ“;•0sÎû¨=±Y©¡Eqøvœûª)(.§ó DZêó:øûé"cbI?tPJº àëãÃsOü‹ÌÌ,^\ø&™™ø˜Íôï×—Ãyù"µÅ]Ö ëV$À¶;ë>Ç fˆÓÓT-îœÿ ÷Þ5×õŒj¶íØÉÏ½Ì O>ì ƒöghâ ¶lÛ!…"BE‘¨Éíž¿µ ~Eq7ðõ(.ÿ»LlÖõŽO ´¤˜#yyR:97\}ÅÅ%<øØÓ.û ð(‡ÒÒÝÎó0™¸éÚYœ4n4 ë6üÅëo½GIii³ŽøsÇ-7’8hÙ9‡yçƒO\÷~í…§øfÙ,ýá'×¾»ï¸•’ÒR¾ù‹_}ž·Þÿˆµëÿtˆ_žâazÅD±ïÀ!^~}‡Ré5™ŒÜtí,N7–ʪ*¾ûþG>^ò•ë»÷·Ó9ûŒ™øùù’“s˜ÿ|²„Ukן°üîÌÐ`²s÷9-÷Þ5€'ž{ù¸DÂÄ ãë¤õÿǞy‘»ïœã¦¶cçnž|þeî™7§ŽwᔓƉ@¡G£ïÁñýµ ~UQûZyM§¡¡aµZ»ô\UQ ÿà!˜<<¤Ötbôz=Æá“Ï¿j²²Ýtí,"#ÂøçýÿbÞ½âçëÃÝwÜÚìã·Ýtƒž[ﺗ§_\ÈEçýÍulÕÚõL7úXã¤Ó1zÄpV¬^WoZ.:÷o¼ûá'Ü|ÇݤìÛÏ¿î»ÛÕ ÝvÓuøš}˜ûÏûxøÉg9mꦟ: €¸^±\zÁy<ñÜË\Û<ÞþàcÒ3²¤"8éž|¸NO{mc¾fûñ2h@óçÍå©ç_áѧ_dþ¼¹.#¾5 ì߯ž} ÜÿÏ;xú…Wز=‰;wóø³î‚¡&õ­z$‚Г ]ñ¸çGõ}UÓÐétèô:ôzÇ|ƒsl½Á9À56_§¡Ó44Í1¦_9qà&4Íõœ®ŸBç0òôô”È–¿ÖK¹NJP`ƒƒ‡R]û®½ê2fN?€Í[·óø³/áíåÅÔɧ0÷Ÿ÷s05 €WÞx‹w^{‰ðЊŠK=^ZVÎèØ{÷ý¤gdðÙ—_óàü»X±z-—\pÞÞ^—0lÈ`**+Øž´«Þtÿºb5[s#Þzÿ#¦LœÀÐÄAìNÙˤ ã¹úï·“——OfvË~ü…É'ŸÄ?ÿ†ÉhŽ‘——O^ ¡ê¬s²sóèÓ/ºÓ ómáA4 ûþáþ¬ÇžiøùÍ!00 áàHûϽ P¿ A„žJOòTOÒPT¥ÍzûOÕ¢Ån³cµ9¼ ]—ô4ûú%¿¸ÎZPÎFeU•kß’¯¾aÎ?îãûŸ~ÅÃd ,4ÀeüäÎåhQÑQ‘M àÀÁcB$5=ÃísZzãF`ÂØÑ¬\óGƒ^4§È¨ªª"3+‡°Ð"ÃÃQU•×^xŠÏÞ_Ägï/â’óÏÆß߀É{øþ§ßxõ¹'˜sóu²jM-’ví®3§!c¾­ÅAcÏ?‘ØmÒ›!BϤ;yÜVçÑëù¯Ýë_Ýãß½ý'ä{ª :£ANÓÚ}õ£ã 5ÿèß¼Ã9TUUʯ¯“‘›‹ÕJtT$yùG(*.¦¨¸˜’Ò2×yš¦¹O¦q ‹ÅÒäqEQ°Ûín¿Åbq;wÅêuœ4v4¿¬XÅØÑ#xô©ùá×VÒ v{õ¿vî¸ç¬Vë±g9?ÛívÞþÏG,ûñg.8ç,>ÿ$?ó"6n>ayÞÙW%ª6Ò«{ò-Vk›ŠƒÐ`îÿç Î7HÚå˜pï]s¹sþƒ-òVäååQgÍ9zÖ¨ÉÍoÚ«$«‚ ‚ÐÑ«Z—jß\EqöúwÞÿvÊ Çr¤|ž‚›@Ðéuôî—@rÒ6ùõu2***ؼegŸq›·noð¼ôÌLl6q½bIÙ·ßeèyyy’–‘IiYY£Ç+++QU•è¨H9½ µ{ïW¬YÇ%žË°!ƒ©ª²°3yOƒé‰Œw}6èõ„‡†’“CFf6fo/vïÙÛàõ™YÙ¼úÆ[äæåsæÌé'T tjépü“‡kR½²Pc†ÿ¶;[,ª…@mPßœƒûÿyGƒ"aGÃÚD!‚ЭíKT}çjܪ¥z¯â¤ôDЪcØìîˤv:NFêAŠ‹ŽJÉu2¿÷!Ï>¾€{ﺯ¾YJVv6&“‰ˆ°P×9ÅÅ%üºb5·Üp5/½¶»ÍÆ×ÎbÝú¿È9œ Ðäñ?7n憫¯äõÅ®Îù¦ IDATï¢7è¹ìÂóÜÒ‘‘™EZZ³.»ˆ•kÖ5ª|gœ:™;w“‘™ÅçœEqI [¶íÀbµ²rÍ:æÞ|/¿¾˜Ü¼<"#ÂÑT•M[·×+–è¨HvïIÁn·Ó§w,yù'vBW‰ƒP½ hõ綤9†kæ9¬X³ŽÓ¦Mq»ÇcϼXgÎAÍ9 µ']¯\³NAz:M£#ÌîúD€êÂñ¡**ªNÅn·c±Z;E<©z¥ÅÄõ!i‹ôÔv6RÓ3˜7ÿA.¿øþïî;ð1›)+/'''—Ÿ~[á:oá›osýì+xúáÐtþÚÄ¿½Ûìã/,|ƒ¹7ßÀKO?J~A!/¿¶ˆ÷ý³–·–«¯¸”׿Û`zív;O=ÿ 7_?›èÈöî?ÈC?íFôâ¿qÝU—ñнwáéáᶤªÝnçÂsÎ"2"Œ*‹…-ÛvðÞ‡ŸI%hD$t%6oÝΖíI.¯@õÊLõ­ºTߊLÛ“v6s•¼¸Aè>((¨:µ]Û6Wä^µ†@<'L„é]BÁÒ¡åÍÿþQïÓ7ÿ¹žâ£…’¨—žÃ¥Wß(5Eº1a¡!¼ðÄÃøø˜[t]Qq1óî}ˆŒÌÆ—½ýäÝ7¹ýÁW$£Aè6è5 Mwü«Õô¨5½":9ôH×ИÞq$mÙ$¥#B»•ãϼÀ÷ÌÃìíÝlqðÈSÏ7)Ú«Õ‚Ýéæ•ˆà‚ tT£[¥å¸„Šv;8þÇ*ÙÚùi¡àƒŽÕ£tÍA˜}|)ê /‚ ÝŸ;ws×½1çæë ü°u{¯¼ñV»‹«ÕBUe–ªÊã@E ‚бhŠ†Í¢6bî9€ªÈü€nŠÍnÃfµbk“N«j ¡Óë1LnBÁõÉn¯»$eL¯8vl/‚ íGzfóz”¡‰ƒ8å¤q @pP ‡sóرs7+W¯m“¥[›#*ÊJ©¬(§ªª›ÕâjA:ƒ^GEÅ1! 8£óŠè™BÁjµa?ŽÎ«ê:£i:ô#6›“ÉË%\a_ò.ú$ôw»Ø/0“§'奥R‚ ´+[¶í`˶š†ªÊ *+Ê©(/¡²¢«Å˜Pô C 8–ÃĪõ̸BƒØí6¬6[«:±ªšNÑÙ¦ª:owp$/—ôC‰Œ‰u»8"*š}É»¥AèÖX­,U•TU9D‚·—7\s=ýúôÆÃd ¡Ýå@ÍêQD *Ç?-¸¤¼¼’”}Yü”•¡i:tzV‹M§Ã5˜ÍìçÇþ”=äv_Ó<4<Ò¡\Aºsûj³a³Ù°Y-X-UÜpÍ ” â@„vÎM©¹‰8Z[¥ŽE¥nnM1™ ؗ믾«¥ «Õ‚Ív,ƒK vv'm%/7ÇuMÓ‰ˆ”Ì¡GѯOoÉAÚNÔbó í(šKŸ¸˜z÷»B`pz½»ÕÆ®­[9´¯ki¿ÈÈhÔZuº=7A„6|-7¯Ý±ÛQª×ýñ‚pÜbàXÔáã°‰Ä{ ´B(TO]ojó4ï=Ç;б՘ƒ ª*¡Q¤<@êþýäæäÛ;Ž€àPü‚8’—'™.B{YœAh^3£´çÍ¡UUGqÿ-}ºÅAŠ&ãÐA—ç ¬¤„]Û·a0îÆdò”ÌA¡Ç‹fº‹>ÚL(´l©#·ˆF£‘ àÐ:'UVTr´°@2YA„že\Éô¡[Ôe¥EÃÕê„ä ‰–LAAAGŠ™{ ´[½jºnÕf_Ì>¾’‚ ‚ ô1 ˆw@èIu¿ñÚ®«ogL\;6oêдÿsîÍR€‚ ‚ ´QÔU€x„ö›PPUµyÁ/ €  òss;,Í#GŒ‚A¡û Aè@4UE§iÔŒ  6tr¯¾ ¨Š*¹&‚ B×Ýa¸x„©v z½MS„GGIŽ ‚ ‚ ‚@zš¦¡×ëhÔEÝ+½A¢‰ ‚ ‚ ‚ ý¿£H¡ãQ¥q étÄö‰¯5Åÿl‚ mõ²mQÛ£H¤ ˆ „.CJr2ï½ûNüÖ”ÿ6!BÃÃñ6›¥AAhwA@OÒA*´//Þ}ïÝã µP›ó‹ï?UUÝ"¶ç&‚ÐÆö†´A‚Љ¤¥XZNJJ z ÍEB³–)òòö&¶w) A„.HVv§NÌôSÝöÛl6æÞ~_rÙ9Ù2íúOOOÉá„2`ÀvîÜÉ?þÀ¥—\Öè¹K—. BvíÞÕ)Ò¢=÷ο—aC‡¹þ.,,以ßòöÛo±fí>úèCfϺZ*–ˆ‚”w’kBóÉÊÊâÉ'gþüûøàÃÚå9-•FP;º¡…ŽáªY³5f “¦L¡  Àµï¾½Œ3†|ð„§é‹/fÔ˜1ìÞ½Ûmÿ« 2jÌ-^Œªªœ4n<'Ñhlö½‹‹‹ysÑ"²²²¥ð…ãâ”S&ðýòež——ŸÇú ë‰‰Ž¦w\œdœ___.¿ì ®¼ò*‡ˆZ¶L2¥«™K 'œï—/cþüû #,,ŒÜõvyŽ®5Å%$p$/«Õ*%ÕÍ())aÑ[‹ùg;U¸–0eÊdÞyç]V¬\IBB‚kÿŠ•+˜zê©Ì»óŽßûç_~æÍE‹3f4aa"x…Öß'žØØX<ÈÎI 0°þFýûåX­Vfž~)))Þ³°°/¿ú‚+~'++qq}8ûì³™xʤz¯©²Tñ߯¾ä‡Ÿ~"-5½^Gï>}¸ø‚ éß@šÎ>ç,Š‹‹yëíwéÛ˵ßjµ²zÍ*V¬XARRyyy¨ªJXX(§œ2‘Ë/¿“ÑÔfù8vì8Þÿ=çdSYU‰Aßtά¬L¾ÿá{þüs¢´¤³ÙÌ€¹ô’K2d¨ëÜÛïœË¶­[¹öÚë¸òŠ«ê½ß®Ý»¸å–›Ñ4K>û???×±õÖóÕW_²'y7Å%¥qÒI¸øâKê -Û›²‡nºsÏ=¹sngçÎ$Þzk1;wíÂn·óܳÏ5XO:½(!ÐÆy*¹)´ŒÙW_sBž£¶æ¢¬ôtÝ///L&_|ñ%‡RS5^\°€S§Nå䉙sûíù”™gœÁ¤)SxàÁ)//à÷¿óêÂpý 72jÌÖ®]Û¬´%''sùWpÒÉ'sÛÜ9|ôÉÇŒ3†ÿþï¿®¼yàÁ™2u*ÓO;|ÐÍ##t?¬V §6€eË–6xÞòeKQU•3fRUYÙàySqóÍ7òŸÿ¼Ï‘ü|âãûàc6³yó&,xˆ_z¡Î5•U•Ì¿çn^{ý5ö¦ì!,,”è˜8À>À—_|Þâïµ~ý:,xˆ_~ùEQ6t(±11:tˆ>øwßýìv{›å£Åb@¯74{ÂC â½÷ÞeÏžzÅÆ2|øEaݺµÌ»ëN6ü¹Áuî™§ŸÀ?|ßàý~ùåg‡X3ÆM,Zôóçß͆ ë1›ÍôíÏÑÂB–,ùŒo¼ž½ûöÖ{¿ü¼<Ö®[ÃíwÜÎÆMÑ4‹ÅB@``×â%„G‹=‡³²8´oŸ4ݽ^Ïå—]ƢŋY¸ðUžzò©zÏû¿`íºu„……föaíÚµÜtóÍ|öé§œ6}¯½ñ:¿ÿ¾‚!C†`±XX³v-š¦1mª£ÇÿÁlݶø¾ñÎ9̪U«(**â­E‹ÜžÓ¿ááìܵ‹ÜÜ\‚‚‚øÝ¹jÌ”S§4ú]V®ZÍÊU«IèŸÀžäd–-_N¯Ø^\wݵ$'ïÁê4FŒÙÛ?ÿ&Óf·Û¹ïÿþANÎa^zée·çþãî{Ø´i#±11M&–-_N~~> _}U*X‡Y7-´„ZhYmv¦OŸÁ[o-æ—_å–[o«Ó³¾uëÒÒÓ9iüIøû»Œá:F²Õ£ÿ‹ìœl.ºèb®»îzWOúÎIÜ{ß|¾þú 2”)SNu]÷þ{ï²ió&ÌÞÞ<ñÄS 8pÌÑùú›ÿñÊ+/·8çÆŽÏ™gžÅYgý„~Çî"ÊÌÌàÍ7_ç‹/>GQ.«=Ñ»‘²˜zêTÎ?ï7Af2š¸jÖlGúv'»í?uê4¾¯Ç‹°eëòòrñ1›7nœkÿ;ï¼åÿwýÃ% z·Í½8ÀæÍ›êýnñ}âyìÑÇ‰ŽŒèœâ@¼¢¡–Fh‘@(/+mS—²Ð¹¨¨(ÇÃÃo¸€^~ Mçîd:pàC1:_ÊcÇ8zèRöî%**ŠpààA<èZ'~æŒnÆÒ¬«g3ᔉŒŸ0òò ¬V+•õ »˜êô¬\¹š’’6nÚDDx8ýû'4ù}Æ;_ò½bcÇüЦh,miΡFCc4šÃ.†[eïÞ½.5fÜ8FÃêÕ«HMK“ ÖÍ™yš£×ùûZÃŒJJKøý÷ßñ÷÷gÜØqÞcõ*ÇïeÒ¤Éõö¢u®þ³k×1Ã<%%…ââb4MÇôé3ê½ïäÉSÚô»F9ÝÒfü¦jóÄ“OpõÕWqÕ¬+8ûœ³¸âÊËùäÓOÐ4sæÌelyÔ¬ôE9ÒWUUIeÕ±våogœéb¿RQYávÍÏ?ÿäÈ«S§º†8í?x€´ôtÌÞÞŒ1²ÎsLF ÎÎ…†V¥ºöºëëxD‚ÐÙiѣЈHrçH°´nιçœÍÇŸ~ÂÖ­[Y³vÛ1K•#FM¡XýQ§wT§ÓN›AÒά\¹’Õ«×`0™ ýÙ²a=6™¤ÜmÑ4Ûo»;ﺋwÞy×íXß~Ž¡9Û¶o§¢¢£ÑÄë×׫73¦OçÅ—^fíÚµ¤¥¥1õÔ)®øÉ{«· 8˜èhöíÛ× 8¨6Æ'OšÄç_|Á’%K€c^…ãªøN7Iñ±ЦÒî J²uÛ6×wß¶cû±ßF‡(ðöòäÙgžvyX ]넱‡š}îñŒtÔëôL:¯¾ú’ï¿_îZË™3öÁLgïuc= @bb"þÎy1ýNÊÊJ hð\/VòŸ.ùŒ?þXGff&6›­MÊäùç_t‹ƒp<üüËÏ,_¾Œ¤¤$W>4UÎ8óL^z1™~üÞ%Ö¯_OQq1±11 è?ÀuîÑBÇ>f3Æo4-qõ,]ëííÝñÞ‚ ´à}Yýl±@0™<ˆéÇ”=’›Ý˜SN9…#F°qãF·ýñ}â?nk×­ã‹.Æl6“¼gœu–à aذáüå¼vF¹˜˜h~úégÒÓ+ù˜ÍŠ„)S¦ðù_°qÓ&‚‚‚HLL<îï 6ðÈc×»7_|Q“iKLL$""‚ŒŒ .ºäR¼<=ÙïrÀØ1cøcýzÎ>÷öH½×MŸ:×_{?7làHÁüýü]«M¯ÑVxy›ð`ÁCw9U,¢ ³–‘”ŒÐùiU×FxTþ’{Ýœ;n¿½ÞqÐO=ù$çŸw.¥ee8tˆÑ£Fñê+¯ºE1ž9cV«Nžp’kÿ¨‘#¹ü²Ë1™LdddpÃõ×såW6šŽQ#GâëãƒÝngòäÉÍ^á¤1®¹æ‡$r´¨ˆýâímn2mªªòø#Ò«worss `úôéÎcŽÞܧŸzŠ /¸pxÊKK9ãŒÓ¥2õâûö#¾OfŸ&¯é×·/F£‰ŠŠrV¯YÃDgd皬^Ó²ÞþTg\“)S¦Ö;D&eoJ‡æñAçÜ€ˆˆˆ:Ë•ìm"ÝgžÅ÷?|ÏŠß'<,œÒÒFŽEppˆÛy}ûö#<<‚ÌÌ ¾üêKf]5»ó ] ñ]„VŽTU•C†âÙÀ¤8AèŽÌ<ãt.ºôRnºùf.ºøb¶nÝJTT‰ƒIæL›: MÓñûï¿QXXÈ©“'c25oÀäÉSèݫ̿çn<àv¼¢¢œ_ý…µëÖºö&Î:Óá¡xå•—ëíúõ×_ø¦…CÜü¼ö×#¾ùök¶nÙÒ¡yèL_nn…îóòóXøï…^Ÿ88‘ØØXv$%ñÛoŽØ3j /ªæjgÔÒ÷ß%K>Åb©r;~ààÞy÷í+ dõ!AÚÝñ\¬i:ÆîíÛÄ“ ô&OšÄë7–šŠ§N™ÌœÛæº&$ ‚¯/'4Þ|f3‡ FþõÈcüß}óÙµ{×^{5aaáøúøPZZBzF6›Ù³¯fü¸ñ®ë®»îzvìÚÉ®¤$n¼ñzúÄõÁÃÛ‹Ìô rssíµ×ñá‡Ö1nâÂó/dÛÖ­|¶ä3223èÓÇ9xóæÍdfeqιçñ¿ÿ~Õayœ88‘~}û‘¼'™¿ß|S¦LÁh2‘ššÊªU«?~<åe¥õFh¯æôÓÏàõ×_ãÇŸ~Ädò`â)§Ô{ÞôiÓÉÈHç?ÿyŸ×^·ßy‡¨hÇ|’ÜÃ9:ß}×\}mûŠ‚ t ×8l8û““ÉÉ”`PB÷fÁCI&M2sæ¬\¹’˜èhÜ¢k£""yãE|óÍ×ü¶âwÒÓÒ8|8ÇDø±ãêôv›L¼ðìó|ñåüôÓÜyç<Æs–}µ¼ qòɧðÌ3ÏñÉ';–;^³FŽÁ#<Цª*4MãÙgŸã³%Ÿ±â÷ßøò«¯0ôDÇÄ0wÎN?ýLž{öéFÂŒ§±xñb,–*¦LžÜ¨ÈŸ=ëjÆŒÃ7ß|Íæ-[H=t›ÍF`@#OÅÉ'Ÿ"¢@hF™Ji ]¨º¾ùß?Ž;òYeE-à྽ÇÆo%Ï?t‹”Œ Çͼý»ÙçVUVPVRDiÉQJKŠxÿÍ$»16›K.¾ˆ¼ü<^|á%† Úy^ÌR<"árãœ{ðòöÁËì‹—ÙƒÁxü€ä];(*(@§×c6ûP\Tˆ\A:+6l /?°Ð0‡ Q ˆ8„´I£Þ8¢ìq ‚ tn¾üâsÎ9÷Ü6Y:ùx„˜Ž‚ tK)9)‚ t ¾øò 6ü¹ÎþÛÙ& DôÄ{ tAÚdˆ‘¯>¾~®°ô‚ ‚Йxéå—H=xìœlÒÓÓQU•{ï½Û[‚ ô(KQw̛ǎ;øü³Ïðõõuí_»v-Ï=ÿ<Ÿ/Y¹çŸOZZZ½×/|õU>þäV­ZÅ¢7ß`ø°ánÇ:Ä^È€þýë3kê¾cÇŒ!99™7Þ\ÄÖm[)++%&:†‹.ºˆóÎ=×uîU³fqî9çÖ lvßý÷Îm·ÝÖ¬gµ$Orss™yÆüôÃøùùqǼy¬ZµÊí¾çœ}üßýìHJâ…^$i×N<ŒFz÷ŽãŽ;nçÿx Eiª¾¿¿¿? ý¸åï·0pÀ×9ï¾÷ï¼û.?,_VgE“†Ò0xРf·wß^žzú*+*ˆ‰ŽæË/¾¨·.5”ÍI#@VV6‹-bͺuàïïÏĉéß?¡Þçß1o àFgÀ·–¦§³£´ò\1æº%%%lݶ MÓø«Õ ÀE—\Êísçpò„ €#*ñÇŸ|Bhh(Ë—/¯#–.[JHHH«î»{÷n®»á.¾è"æÎƒ·—7Ûwlãégž%=#Ûn¹µÙß±©gµ4OüýýÑëõnâêY³˜=ûX¤RƒAOyyso¿óÎ;—'”’âÖoø“ÿ§éêY³˜5{6ùyy|øÑGÌ™3‡/>ÿ???¾]º///~ûm§6Ãu]ci¨Icç þÛÙxMüûõ×ëåAMJ#@FF³¯¹†Ñ£GóÒ Ï@ÊÞ}dffpN3ŸßÒôBW¡©èï" „ö©R#„®‰®-oÖ«o?vnÙØã&)_~É%üçÃ9cæLÆ_çxMcUQ<vksn¿ýx’Üéòä‚óÎç£?tm“&M`â)ùæë¯™6u¯½þ:—]y%¹¹¹­2vO™4‰)S§òç_ñÊË/ãííÍŠ•+Ñt:F ÀŒéÓùn×67 Ç›ÖÚypÊÉ'4™F;ŽU۸Ǫ¡2éÖú@š¨J‚м#]÷]©¶Çs‚‚ îqù;}Ú4ÆŽÃÃ?Öª^ÿˆˆúñ󯿰rÕ*úÄ÷%"<Ü휳Ïú›ËX[ðàƒ Þ+$8MÓHMK¯÷xZz:‘ÎùF£‰ªªª:çTVV¡ÓëOXž˜}ÌD„‡»6×1³™«gÏæËÏ¿À ×óþûÿi•±»äÓÏXµbï¾óŽk£o¿û–üü|ÆŽϨ1cxúÙgØ·o;wír»¾¹i8ž´ÖΓÉÔ¬4V—÷ÁƒÛ´N7V&‚Гޛ‚ ú@èIèÚëÆ½âûq´°ªªÊ•¡÷Ü}7_v}òq«®?ãôÓY¾l9žœyÆÌ:ǽ½½ñöönò>&“‰Q#Gòõ×ÿcÜØ1nÇþÚ¸‘¬¬,&8{§ccbHÞ“ìvŽÕj%yO2SOÒáyâþ½Œ :„£EG[e솅…ºíËÏÏgÍšµ<óôSÄÄĸö?ùäÓ|ûÝ· èß¿Õi8ž´¶4ÕåýÙçŸ3Á¹’“ bÛ ‚ ´µ½n¬7ˆKèßã24**Šk¯¾šuëþhÕõÓ§McËÖ­løsÓ¦N;®´Ì3‡+WòêÂ…¤¦¦‘ŸŸÏŠ•+xhÁCÌž=Ëå¸äÒKXþÃ|öÙ2³²8pàO>õV«•É“'Ÿ°<©¬¨ ¨¨ÈµUT”³'%…eË–qàÀòòòY½z5?üð#';ÅÍñ²üûåô‰‹cÊä)ô‰ëãÚ.ºèB¾ÿþ,K³ÓÐ^imNæÜ6‡¿þú“Çœ½ûö’ŸŸOòž=lÞ²¹Ñû)(àÀ®­æäçúÊDº³0q E’Z$t}tíysÿ€@Â"£ÈJOëQ™:{Ö,–/_Þªk}}};v,Fƒ¡Yž‚ÆHHHàíÅ‹yãÍE\sݵ”••ŵ×\Çùç[ݨ_ß¾¼üâ‹üûµ×XøÚ¿ÑT•#Fòæë¯·Ù’æäÉ»ï¿Ï»5‚Ásö9\rñE|ôñÇJM¥Êb!:2ŠÛn½…iS§¶Iº¾ýö;Î?ï¼:û§LžÌ³Ï=ǪիˆhVì6[ƒç-]º”§žy†’’.¿â >úðÃ6MãäI“éß?Åo.æEopãM7SRR‚¯¯/—_~é >É’%,Y²Äõ÷ŒéÓyü±Ç,“®(Mš# A„Zmã›ÿý£]§«Úm6¶mú«EÔž{ðïR2‚ 7w=üZ³Ï­ª¬ ´¤ˆÒ⣔•ñÞ›ÏKŠ0„–×,©\B㦹óñòöÁËì‹—ÙƒÁØ~CŒ\?U¥ï€A¨š&% ‚ t¨(aD‚(OAh݉xˆ‡§'½ââÙ¿g·ä¸ ò]-Æ£ÔAú&ôÔ:[{SOÔÃCÂ#èKŸ ‚ û„Sé©oB·Aw"ׯ¿#X¹¬„"‰2Ťìy%.e(‚ÐòÖSq}VOä£5Žøþd0A¡^m"ðAÚõD?ÐìãKdl/TMAÕTt: ^Ã`Ða0è¥TAhK«±u›ÐeD¡3TF©B·x_:?ë:" ѽ(-.¢¤¸H DAhñ{LÌ1A„öC­è€ùб}ú¡i:)A¡ÙÂ@¼Bg¬˜R#…n'Rv%a·ÛOøƒ F#1qñR‚ B“¢@„  %ÅGI=°·Cîë@Pp¨”‚ ‚P¯0Q ‚ÐÁ`08‡#ù¹’€ÈØÞ˜<<þŸ½7³£®òþßµÜ}é½;},„`ÁQYœ1€À8ê %茢Œ:óÌv|ôyüã2‚ŒŽ3:2¼†E@wQ@–Ù—ÎÒIïwߪ¾¿?î­êº·ow§!$½œw^•{«ê[KWÕ­:Ÿ:ßsŽœ AA„0å.X¹R…i)œ/{vl#›Nýß–®³ð¸eèº.gCA„ A„É"”m³së«”JÅ£¾ÁP˜ÙóÈÙAa Sæâ•«V˜Æa8ñw¡P`ç–ÍÇ$h¹µ½“†¦fyH‚pÄÍω?ûÐf$ðXa²>+GôëI§’ìݽó˜ìÞ¼ÅÇáóûå< ‚ LÛG# A„ÉJÝŽÿ}‡ÒwèàQßÓ0Y°äx9+‚ ÓPˆ·@˜Nµ\ÍÂtÆí ß»g'Pˆh,~TwèhoO„in™NÌŠEìØ#|ø59˜‚ “þf]3Œš:H)Å®m[( ràA„ ô¡ñÜŸž›Ð<¡š+®¼’¯ýë‹EÙ¯qÄÁ“O=ÉÙgŸÃ}7Ò××'A˜×½ LÌÃm8Ð׋ß sî|9jG˲ø·ï}»îº‹R©tØóÞtwsÝõãŠ}ˆë®»Ó4kÞL¡P(p×Ýwóð#ÐÛÛ;j»û6n¤·¯›oºé¨ì—ãÁiˆÇùâ¿ÈI«VÕÝÏ£½_c‰€?ø }½ý\|ÑEüçÀÇ®»^nSDˆ•$‚ €ƒöá imk—#w òõëoªûf~¬yo´`¹oãFþøÌ3ÜvëmÌ›7÷°æÍîºûnvìÜÉ¿Ým£üÞzæ™|äÃæ#þðQÛ¯?>ý4½½½D"‘ºûy,öëͧÆŸ~º®8øÈ•æÿŸ/qÓúõÜrë­"D‚ SM ìÛ½¿ßG¼¡IŽÞëä‘Gå‹_ú©TjBóŽ/mÚÄW®åsŸýì„æMg~ä6Þ7º8Øxß},]ºô˜ì_kkë¨ûy,÷k4–-[ÊŽ;˜7w.]]]rS˜¬Â@² BE$ ÂÌ@ŸèJÁ®m[ȦÓrô^'>Ÿ}”ïXóŽ&J)”mOxÞtåч¦½½}̀ߥK—rðàAÖœÁ1ÙǃííSÇÓ§Þ°'í5‚V"Af @x-NeÛìØò ù\VŽàëà]çžË÷¾÷=N^}ò„æ-N<ñD¾»ñß8ÿüó'4o:S*•øÖ† ¬½êª1 ô;¾ðÞsÁÑ?6Þmg³Yz{{Üyk¯ºŠomØpTbY—Í›·°hÑ"ººº˜7oÞ¤‘3Ù$v„Ô0A˜÷ÿÊðš£LK¥";6¿Â’'â÷û娾F:gÍâ®oþkÝ@䱿½‘†1j òXóf·ßqÿóÐC£¦vmmmã=çŸÇµýèQÛ/g_¼Û~þ…øÜßÿ=CCCn»omØÀþýûZ²ñÑk®©‡ðûïå¯Þû^~öäÏyÛÛΔÂ1~0ˆ(„q~#‚0Cx]^¡gû«›8nÅJ|> ¯Ç ¿jíZÞ|ê©|~ýçéêÚ{XóÞ(Árë­·Ôõ\Œ5o¦ðùú'.ZÄÝwßÍoýëI³_Ž!^%N=•Ÿ>þ¸;~úgpŇ>ĵ×^{Ô÷ï£×\ã /]x!«N\ÅW^Åw7ÞlŸè3Ô} Â@A¨E½+(äsìØü VIª-¿^N\¹’»#ç­Y3¡yGŠóÖ¬áûÿþïuÀXóf¢˜»ÿÞ{§Ü¾ÿÛý÷ó7ó7ø|¾c²ý^s+Î>ël~ö³Ÿó‘«®¤¥¥EnGYHW"A¡î3âãÿø•#ŠD9nÙ è†ñº×õ³–Ë™„iÊ}7òõ¯½®×ãHó¹/lÀ¶ïW,äɤdRåá¾»¾LØör¹ò±Ax½"[D CìÜ&"A„ɉaèe¡`NO¡ Q)p&– ‚ðÚÂë)%4údçÖ͇ (‚ðF˜Êc †nà÷ù1M³R1xjç:a Gþ"3çJ~¾¡‘{ÉÄ;%pY„IŽ¡ëÃ]D‚ 3œ7<µG*1ÄŽ­¯b[–mA&÷ Q×0MCŸYD‚ SR ”EB‚[7c‹'A„)"4M+ùŠ0„‰ü¼„ý|c"F™d‚[^O‚ Gõ¸Ô‰>ИTBa8e©œ[Aá{VU?z:™dû«/S*ål‚0¥îže¡p¬…(Aáç¨w´ÍfÒl{e…|N޾ SL(”½ GËL/¿Éa ÇB” ‚„£L!ŸcÛ+›ÈfÒrA˜z–ÃQèvä ±TA„™ JÅ"Û_}™T"!gA„))Þˆø @Af¬@°-‹[_e¨¿O΄ 3Z(ˆ0„Ió“„y¬ ÊVìÞ¾9¥-írFA8FúÚ|F¯A(¨òr 5ÅD‚ “åyY~š“c§ûvï¢P(0kμ1[>óì³rAp9õ”S&ÝýUSÚa‰‚ ÂdÄœL;Ós`?…\Î^>f»³Þñv9s‚ ðä/~99wL«ÿª¾7A„ ‚ a ôËYazPÓíH„ LúŸ¬ €)¿AäiOýÒÊGp4¹Ù ‚ “ùYéyšrTAŽžFQrHA„IŽ)ï´A˜)Æùxm¤AgºA˜º÷ A˜n×~í ËaA8ú††#‚ ÂdEº ‚ EaP¯½xA„É„xŽïÿëKøÝï/B¦‰0Ð^çò‚ L.qŸL&Ùµs¿ûÝoxþù?‘Íåä@ 3†iãA¸öúñäSOUáï{'Ÿ¼š[o»|>Ï øÉ#hßÜÜÌŠËùô 7pâÊ•c®{¼¶‡Ë~ÿß1 C®@A˜fFÅë]—xáØ144Äþý{è`` þAÒé$¦ÏÇààgœùgø}>9P‚„©ÄG×­ãšuW»ã~¿Ÿ`0H(ä+_ýš+¼í×­»šÞÞ^î»#W¯»†G~ˆ¦¦¦ºë>ܶ‡ƒˆAq0ÚzE$ÂÑ!—Í288HÏ¡ƒ Ðß×K"™$—ÍR,)‹(UþE¾¼é%/^¼yóåÀ ÓžiÕÅ(ÇÝ! ŽÛ¾!gÉâÅÜrÓzJ–ųÏ=7á¶øÃùàåWpêi§ógïx'ßøæ7Ýåþ|Íy¼ºy3×\{«VŸÌóÏ¿àNâ§?à¾ûïçìw½‹“N>…ó.x?yì1¹2a mlCf*ù\–Û·ñûßþ–_ÿê—üúWOñܳäÕW^fßÞ½$Ée³X¥’»ŒRŠl&ÃÖ­[ä 3 Rv”’^ÖJ–eO¸m(âʵk9iÕ‰tíÝË5×^Ç9gÅŠ+øÌg?Ǻ«?Â††xÕº^}õUþõ›wñí ߢ£½M/¿ÌìÙ³å„Â$Çb›âM„×G©Xdp`€ÞÞ’‰=½½¤R RÉ–eQ*±mÛŽÇ@)…mÛî iöï“*ˆ@˜jܽa÷mÜèŽßvËÍœ·f͸Ë% 6|ûLÓ䌷ž>á¶'®\éÆ#tvv²lÙR¶nÛæ „3Ï8ƒ‹/º¨îú2Ù, hnj¢££ƒŽŽ¹*AÄÁˆm‹P„‰‰‚îƒÝ:ØM*‘ ‘HL&) ärylÛªJ),˪^Ñ000 W˜!aù±/»äÖ®½ÂoinWPÜsï½d2NZµŠ{6|‹X,6á¶¿úiîÞ°;w’J¥I§ÓäóywÙÕ«ß4ê>œ¼z5xÿû¸ð/ßË{.¸€uù HÿFA8ê–½6rm’ý)"¡>¶m“L&9°?étšÄ`?C‰!Ò©4ÅR9–Àë%Ð4­ÊCP+¼ÂÀißÞ&/ñ„iþ¼¬ôqV„x<ÎÜ9s&$(®ºòJšš …B¯©íÞJ—¢«?òa>{ãDc1>ñɪ–õ‘ñ@Ó4>ûwÇ¥ý×|û;ßá¿üK¾ú•æ¬w¾S.VA8F†>)߈7Aª9tð ý}½ôõõ‘Τ¤X*RÈçQªÚà×4Íý^+j=Þg´®ëDb1Z[ZyËio•ƒ.Ìft B<göìÎ×Õöù^ ¥¥™Oþíߺo&úúû'¼/óçÏçÖ›ofVÇ,¾ÿÿ)AŽ…®ë˜†®Mnתx„™J6“¡·§‡¾þ^2©4ƒCƒdÒiòù–ecY%÷Yñù¯MºgË-7®uÞ3Ï>ËYïx»œ9Axò¿äÔSNuþM_Þ8úÍO×ðZÅcPÈçH'‡HU†o~åŽ)u,D$S™l&Cw÷†‡(ò ô“ÍfÊÅÊJ¥º]†êeªdì>ŸŸH$BCC#í³:˜5«“@ H8&KSaFrý ÿH4Ö@4Ö@$Ö€?”:‚ Ì040²×`šýY"„)C©XdhhˆÞžCäs9R©$C‰!rÙJ)J¥bÝ®@Àa;˜¦Ó4ˆFã4651oþâñÁPˆP(Lxœ%‚0S ÂŒA7tL]w½ÓPû€a’R,èîîfhhÛ¶éïí#“IS*•(•Jnm‚Zc¿^—¡Ñ¦i†I8& Ó1k³: ‡Ã‚A¢‘¦Ï'•ÊA‚ 3MÃgè3&ÈP¼ ÂdÀ¶mÒé´KN§H&d3”R‹£{ Fë.TO躎a„#éè줩±°Üu( I€± ˆ@AÆ0uL}æõ+‘ +Qpð`9ãU*188@2‘IJJn€±Ó®ž(+–À‹®ëD£1"Ñ‘p˜ÎÙ³‰Æâü~Bá0þ@`Úz A‚ ÂëÀï3g´‘ "Ax£QJ‘Ídèëí%—Ï’JN§Éç³Ø¶M±8ÜuÈ»ŒSŸ^@±7Ù+4M' ‰Æhkï µµ…H4ŠiúˆD"‡`,²AG È/E„éj ë¸ÁÕ&žF" ÂĶmúû$›ÉËeËËrk8íÜë°RŸÀ1üÇÞe‚Á ¦é£¡±‰öŽY477aúü„C!ü€d„#õ ¨ă ‚0Cîÿ"„‰âíù\Ž¡D‚Áþ~2Ù4…|¡`\,V<v]¿^ZRïx-ºn`±XŒÆ¦fÚÚÛ…Ân*R¿Ô&„£‚A ‚P%”R$ èGÓ J’Ïå*u p¯&gGx…ÁXÛp¾›¦‰®DcqZZ[ikk' ƒ¦é®_‚ ‚ˆá(ˆ(×&H&“ôö"—Í’ËåÉeÓärt]s»•uçjª^ßXâ`Øk ¡ë¡P„H4J[{;Ñh¹6A0 W‰z1 oÔõïÝ®A‚ ‚ "A˜¢Àkpg³Yúz{@SŠd*I6“ƶm4MwERš¦¹F³wãy ¼D""‘~¿Ã4ñùý†A©T$,‘J$õqDÿÖÃKJa‘H„h¼œÉù›E,3P ÈE/Ât6…'ÒvšF*zd”…& ”Rd2öïÛG6“&_©OÏçÑu½*°Ø)0š‘\+6ê!{Ç5M£P(`Yƒ†Y%Së­g¬mNÛ‘û¢Õ?Ü®ü·B¡áh„¶¶6Ì_H8± Ló'Bõ A„üPÐD$L{œt¢‰D‚®=»H§R a•JÃ}¸0™Sx̽BjºúŒm`×Þå‹Å"…Ba\ƒ~,¡q8ã¯e×¢• $ÃÐ †Blݲ™YsYºt)M#Ž‘ L»'ƒ&AAD‚ˆ„i‰côÛ–ÍÎ;8x`CCƒØ¶B)ÛóF\ÓKàýmúDŒpgZí÷#cà5ÞàéÃwº)¥(mrÙ,‰¡!ì?@W×.–/?å+NÀPF¥6ƒx„é‡ièˆ>†üùšóøù“OÉaˆa:Šƒ\6ÇK/¾Àö­›éííò¬q Õ]v´Á¶m×#Qû9ZäÃéŠ4^m„±Ö32‘qG} ¥]{ýÇxò©jcûýï{'Ÿ¼š[o»|>Ï øÉ#hßÜÜÌŠËùô 7pâÊ•c®»±¡¥K—òéOÝÀÉ«WË•$“ÛîXÛ™‚P_$(ñ$L7q°éÅ8p`–Uªˆê·Þ¶m“ϥɥz‰úD})t]¡¡ªÞ´¦5ãî÷òËwœØ×xVjx>Î[úJ»ªïÃÁÂΊkÛV¯÷mÿðö*í•*ïEe›xÖëõ8ë°”lÁbC¥f¨Ãð¹ÓJ)”m3ØßÏŸþô,éTŠ?{ç;‰D¢èšx„ið¬Ô]Ãð™Ó¯PÚG×­ãšuW»ã~¿Ÿ`%òW¾ú5WxÛ¯[w5½½½ÜwÿF®^w >üMMM£®{ppï}ÿ?øèu×óèCÿCKK‹\X‚ L“‡DÙ“ oE§6e£?ϦM/°o_¶mð”J%ò©ý´øw³´±ŸŽùfŸ^DÓT0¨QõÄAÑN•Á^ߨ¯š®jÚW¾5ÕÝ‚œu ïƒ=b™Z! e—JÎò“µüìKDÙžl§»´ÍU1éTŠ—_Þ„fèüÙÛßI8–ŠÎ´À4†¯ói%ñx|Bíâqâqn¹i=?òÏ>÷çžsΨëŽÇãüýg?Ãx€?=ÿ<çžs™L†Û3Ç¥çžsŸÿÇÿE,+ßLÒi¾ðÅ/ñøãS,•xç;ÞÁ?ùÿV­ÿСC¼ÿ’K¹áŸà¯Þû—r• ‚p u‚&"aŠbYVÉbóæWèÚ³U#”R$}´™›8}amÑ á"Ñ>ŽÓLy4òUåAð ¯qO PßkÔW½í¯£úõ‚]™5Š@p–µ‡By ¶‚‘NIŽ5 IDAThîfojÏôÌg¿ZéW–ÎçØôÒ‹„BÎ8óL ”F¯y‰ AÊΩ¼°,û°žº¦Q,¸ýÎ;Ùµk7ßÿÞwÑu;¿ð¿¹ñ3Ÿå[w}€üüzº»»Ùxÿ}º»»«Ö—Íæ¸îcçÂ÷\ â@ Âk H>x°›Ý;w`•ŠU´¶m‘ÜÁI³^aEg‚Ö†"~SCÓ FïSçùZ¥‰BSÃß•Ò@S0â“J7¥ò›ùJª$W$8£¨JUg5ÒiáÔdS£ìž{Í:«C9c•üškãÃÙ §8ˆ®A<¤ˆ3´G2Ìò§žƒünà´`»+~²™ Ï=ó4sçÌaþÂ…´,LÝû= ÕaÉÓJ ܽa÷mÜèŽßvËÍœ·f͸Ë% 6|ûLÓ䌷ž>fÛb±ÈwßÏ[ßúV‰ÿïÇòÀýã;®²Ý[8û]«‹h4Êc?Î?üË–.`႞‡0|îþöŽvnüÔ§ä*AD‚ðšpÒˆn~õer¹¬ûâ«Üwß&3°•w,ÞIJ9)âa 4£Ž™Pk}kÓ•Çb¯ÔÐÜ6eqP/ŠEiËOhºª˜ú–½)“´IÁÒÈ ,»;ã7Šøt‹ˆ¿D[$GÈ,¢iÃ]«GnC¡<ÁFUm<"&èƒùbþÝDÍ€Æïx´ûÏЂ-®Hعs;;¶ocùФF‚0Eoô:ÓºPZ<gîœ9W]y%MM„B¡Ã?ûÙϹ÷þûÜ픊¥ºoØ”mã÷ù°+éäFcO×^N9å¾òÕ¯rçí·ËE*ÂdT ’áh’SÎ\d±{Ï.,«ä¾ÉVJ‘NrRÇ‹¼iQ– ßî³£Õ£¾Ö÷vÒj²9Ý„´á·ïc˜Ûõßèo6SÔy©+Âæƒ ìŠÈI|äKв!3ZzÖŠ\ÂÐl¶ˆvgi ¦XÜØÇ‰m‡èŒfFÙU½'5ÏuMƒH@qÚìCJ?Ë3oG7å®FÙ,›6½ÄñK—‰A˜z·÷Q¦ëõ2ûëáh ŠÙ³;Ç^ñqÅ墥¹…¯}ã,\´MÓxuóf·íÞ}ûH$“,\´ysçaÛ6Û¶o¯»Þ>ñ ¾xç<òèO¤&‚ ¼7¾×rï?Â("AŽÂä–M.Ÿ§¿·§jz©T¤A{‘Ó—eòS_×ËŸš^tmxº;Mnç´u¦izÕ4MÓÑt½lE»m´ò5S™ï®·R¥X«Ìw4Ry“_omâÞ_-â^ZÄ÷Ìbg3}™y+šéu«‰:µ l|$Šö¥[x¥.Ov-åß7­æÇ›co2Šíý¡»‚I«›Ñ­‘°_q΂.ZÔæª—ƒ;wì ›Mc[–t˦œ8¨÷<?Ø„Ÿ‘ë?ÿOlü·ï²yËâq.¾èBn¹í6¶lÝÊöíÛYÓM¼ëÜs™3{6­­-œuÖ;¹õ¶ÛÙ¶mûöï!æÎËß}úS|~ýz‡†ä ‚ "A˜˜@°möïÛG>_¨š—K÷rú’ý4Æ£}ØX×ôa^󎻃æ™W1Ì«ÄAµ‘ïfKª3hcLSšÆËûÃ|ÿ÷sxdÓ<6u·r(%où]A༩ïïï§»»›®®.vïÞÍÎ;Ùµk]]]ìß¿Ÿ¾¾>R©–e¹Ïl“D!Ê®d¿Ù?|å$žÜ5ŸtÑWZ}K©þO€Öh‰·w¼J!Ÿv:bÇöíRÏïm¥7Aa– {Û&•J±oß>6oÞÌŽ;8xð ™L¹›Ïç# 0M“R©ÄÐÐû÷ïgÇŽìÝ»—¡¡!7ã ¦éd¬0;“ü|ï ~ðò‰ìŒátŠr½ãôgÐ48iV‚fµkøXÛ6{÷îQáY&í­|¼ùŸ¼é“îJ¾ùS—:ï™gŸå¬w¼]ά <ù‹_rê)§Œ:ÿ ßøÏÃ^W!Ÿ#•"•$â_ÿù9Àc[¥“0I°,‹|.Ï?J_¯›M§X,²ºåþâ-•`sÍk3üYû½ê<»ÿÕ-x6\A¹¶È5µ FÖ/84hðÐsͼ´·™d>€Óõȶm‰½½½d2B¡±XŒh4ê '¥¨®ë(UŽ.•J”J% …Ùl–L&C:FÓ4illÄ4M·‹’O+2'ÒË;:_æMÑ<5ªö»þåÏ6ÍáWÉs ˲X²äx._{%þ@@b„)%þöïn"o o$oÄbÊ« Afì+’Ú¶r?œÀñ’ÀåÉ¡Ó*o°Q¤Ó÷íµmÛ¤=œtš¦›Ã" bkõD‚ó¨JöSÉôSÉ\¤”'y¸˜'ÛÕpv¢ÚUyÙÓãã§ÛÙ~0NÞò¹ÝˆÒé4ÝÝÝd2¢Ñ(ÇwÑh”`0ˆÏçÃ0 WT_ŽZ•çÄ ™L†D"A"‘ •JÑÖÖFcc#†a iAÃüì`„téEN›µŸfת£Ñ.–6÷ñ«,Ê¡”b(1èî—#ZaÊ<+k)”&‚ ˆH˜"¡XÌ—‹¥Ù6º®cª!šŒ*AUŸ{ÍÓñ¾6£‘æx<ã ´JÊÒÑ’“Ž—¡`oŸÉ×ÎÖƒ ØÊp½½½½tww Y²d D"÷m¼WÔθ¦iøý~ü~?¡Pˆx—w»!©N…I¬‚ ‚ˆ„)ŽmÛXvuüAC(…ÏŽ èî/ò‹­)”®<‡£˜óµÅè÷œûŽ€ÆÛWÆ0´jAàåРÁ~ÓV%ŠÅ"ûöíchhˆÎÎNfÍšE<ÑM§nJñqúû;bÁçó I$ôõõ±wï^æÍ›G4EÓ4òj6Ï&Ï `9µmÏah6†’hsÏÄ ÓAˆ@AD$Lq¼ÝZ¼FjÈW@7†ƒ_Ü•eSJ–Âôi˜~½ÊÞ·J6¦OÇ*ÙØ¶Âç7lСh) ËÂ** ŸF dx›Þ›Å²ŽspvMKÁŠñÒ¾VtÓ¡éX–ÅîÝ»Éf³,_¾œ¶¶6LÓt úÑâ Æšæ‹zoñ5MÃ0 b±~¿ß @€X,†R&9k!¿<ƒ–àã´3•m5Ú%®.Øa*#äA„#'ä(}UÝ‹ÊA²T¥5ý:†§ÿmU2Ù ¥ÀôéØ%…nGì;‹[¥ñߌûü:zhh°e¯ŸßoŸ…Ò£A]]]$“I–-[F{{;>Ÿo„‘íõ$δÚx„z"AÓ4‚Á mmmèºNWW¥R Ó4ñù$|+ømωe±£gx¢Î[YÂd¼5¿†eă ‚ Y‘ ž„c†·‹‘òXøš¦áóë&„b&h+4}X0h:(4]ì è‡g`˜¾r}„aƒºÜ·(3øùKíäU@9 Ѿ}û8tèË–-£µµÃ0;ÆàHM ´¶¶ÒÓÓþ}ûX¼xq%»Q˜ÝÙ7³ep7Ë›ºÛòRrÝ ÓDˆ@AD$Lqj=Õ²Ã1~Ÿ¦Ùþñ;³"Ÿ)•k mL_y½ºQ·Jåd=†YÞFȬd£:Éés;ÂìOv ÑuT*ŶmÛ˜;w.Ǭf€ãIhmm¥··—þþ~ÚÚÚÊ]®müqèÅ~BÀ(k|I·"aº¡×I}zÌA„#õæDîAÇP$ÈQ8*âÀ¹ÖmO ‚mÛ•L¦ÃÝ}|~‰ÆþóŠbÞÆ¶¥¢]ù¥‚Â.ÙXEE!k£é`ú‡=Îv3§·wbúcü~?Û¶m#°páªÂe£u !^*µ4Ouæò›mÜeëµ ‡ÃÄb18@6›­S>}›‡f{×k‚0Yžmë<Ń ‚ ¼q"A< GM(Œð T)^†°Cy¬‚;ãl(WÎiIáw…@¹Û½^TÄ m8Æ¡ÂK»Â¤­N·úñ¡C‡8pà«V­" zÖ=z}¯pæyÛ¶ÝïÞBqµëªT5ÁͱXŒžžzzz˜5kVYlaž\ÉÊÆ}š:ìóà|J¡4aªˆƒzˆ@AD$LqàxÊ33¡(¼¨=ÈÅAÛ Ö†™Ñ*iK´á ÉÎPÞHÙø­G9Ò4ê`zB±¨x±«@0êÆlݺ•ÆÆFš››ëos<º®»”k…Bmee§s<êe?ªÅ0 âñ8Äãq|>¶mÓm-â;ûÛ ˜(\šÂPºÆš¦Ì#L<ÂtÁ° Ó×8`[ég$"aŠ‹„ºÅºÊ–-Ñ É*¿‰òt;Ò4½òr”²šÇø”í|·«CeÐ5Uî·\ÙvÏA®Ð®ë °ÿ~V¯^í¦3uö½ž0¨µÂÀ+j½θ÷-¾×«P/]j("™L288Hcc#¶mSRAìdss_å8*tà +Ìš¦J”…a ?úê>ÿ4ñ ‚ "¦…@ðÆ Tuo)'ó/g22jÒºñ ÷7Ò(/p–uº3^yc¯lw¾ÛÆãeغ?‚îoÂ4Mt]gÏž=„B!«öO×õ*ãÝ0 LÓ,D×ñ X–…eY˜¦éz&J¥¶mã÷ûÑ4 ˲ÜåO§#j {Ã0ˆD"$ B¡+*ü…NöÛ/`›Ãûxœª¶ÄF‡ £ AA8j"AòÄ¿!òÀýæ(kʱګ´BMð¸²+Ó4ÀéšTÉCT1øU'ax fPû¢;{šñùü®‘¿k×.:::Fd-rŒjG8ƒ· ‘cЗJ%@ŸÏW5½X,’ËåÊÆME`8ÇC)å7[–5ê‘t¼™LÓ4±m›¦RÁRŒ™W¤‰@¦ ætñ¥_{ýÇxò©§ª¦½ÿ}ïãä“Wsëm·“ÏçY¸`?yäáí›››Y±b9Ÿ¾áN\¹rĺ?pée\|á…\þ¡Ê#Óö]Šô1::A#êHËƒŠ‡ ~šSEµÛFUªÔ‘î[q5<Áýôt5ª TD„»&EÑ‚žT3¡xÙ¸O¥R$ –,Y2j— ÚìDº®WõÅb‘P(D8v‚ã}pæ†A6›¥X, ±,Ë döñõ‚˜aš&étšP(„mÛ˜%“ÆL#™`¦®æK° Âd}â÷ œV„®[Ç5ë®vÇý~?Á`P0ÈW¾ú5WxÛ¯[w5½½½ÜwÿF®^w >üMMMr• ‚ ¼11¡Ž<Þ·åN £´ãÕퟯiÃ*CÓªí[¯8¨´Q5ÂÀ&µž…žA [¹ÆxOOº®FÝí{©^q iù|¾ü<…Üt©~¿ßíBT*•\! ”"™LbY–ËàÝf=â% 244TU¼-–¶A&úW˜L+ˆÇãjßÓsËMëyø‘Gxö¹ç8÷œsÆ\îø#ÿü/ÿÂæ-[ƒ\vÙ¥|üúëyúàŠ+¯ªj{þyçqÙ¥—ÔþÏ_þ¿r ‚0‚Ä#i†mxO ‚'`T¯ 9â`˶6[Q•ç N×#¥ûM sø ~?¡PŸÏ7býŽpw0M“|>¦iøý~LÓÄ4M|>Ÿ;îü½¶mcš&~¿Ÿ@ @>Ÿ'Žjö1{¿;âÁÉ`äå˜]hÁ¯¢(lMÑa€äˆ£*â@8–/^D ¼8o,Ë·m(âʵk9iÕ‰tíÝË5×^Ç9gÅ©§œÂ~÷[²Ù,ï¿äRÎ9ûìQ§ ‚ ˆHŽ˜H°k‚e•×bUžXO7:¥ÊYÜX­Žµ[ÛÕˆºYŽÜx霎m—ßì ‰DFEó9«'t]'ŸÏ»uj½ >Ÿ˲\‚38Ý„j‹¨y»_†ázª £Jð³mÛn¼Ä3ÂíoÉ»ÂjhŸ¯²!½…i(¦@¸{Ãî۸ѿ햛9oÍšq—K$løö=˜¦Éo=}Üö'®\éÆ*tvv²lÙR¶nÛÆŠ+\Æ7¾y+–/çÂ÷\0êtA ÂÞ*ÊÎ[ñã×±ó5My„‚ã~¨×ù«:Ay= õb*Órƒb±„¦•‚³Ù,@`Ä›{Ç{P[Ù1Ì5Msã jÿFÛ¶ÝÌDÞ®UÞ ggYo‚³ ïúœéÞx/A|œ9«èŽoIY0 ú@˜¾L+pÙ%—°víîxË8…XîÞ°{î½—L&ÃI«Vqφo‹ÅÆÝÎïŸ~š»7l`çΤRiÒé4ù|ÞÿÒ¦Müèxð¿ÿ»j¹Ñ¦ ‚ ˆHŽ„@¨6’+©Hk<hJin^ÍõTg<>)Ê „´ŒÇ£àù’F¡PpßÜ ÂápÝúŽPpƽÓ£ÝImê ¥RÉ P¶m›b±8¢¦iUíœ`çZ/†÷øÕ [é£\¿rÝ Çø*a|âñ8sçÌ™ ¸êÊ+ijjtsÇÞJ—¢«?òa>{ãDc1>ñÉÜù–eñŸ_Ï Ÿø³gwŽ;]Ažp’þôH‹Û¶=]êÃQ ªÊ‹Po¥žcTR®ö*Ø®@-ý§WxÓ™Ö~wž¡^qଷ6‹Q©TrÛ9¢ÀYŸ3îï>T§:ÓŽ¶q&ÇR˜3ùâŽÇã6ÖŸáZZšùäßþ­{3îëïwçç¾û|ð²K«–mº “ãæ)INõ¹’ÜF¯÷¯íb4"‘SÍ”\%μ…ÒªÔAÕzœm Ww;RJáÓKnp±ƒóöÞÛ½¨¶æAmºSg¾#¼±N–"ç{©T¢P(¸Þ„R©D0t½N7$g½^1Và2€®Ù"„iñ>%Hy‚Ìž=›ÞÞ>þøÌ3tvvrÿýI&“tuuñÍ»îæž ß"•J•˜iÒ××Wwz8–*‚0Â(^ ‡ÛÅHÕ® €‘uTÕFF œ„áï³D¡w·ëÄÔE« Vvº9Æ»ó¬Ìd2nÀ³#„¼Õ•/‚#/ƒ»à݆7;‘Ð\(êvr¿[Š­›‡S ì ÐHŠ<&ÅË7‚i/~üàƒÜzû¤R).~ï_ñãÿ~àu­ïäÕ«Y{Åå\÷±ck¯¸œK/ùkþ硇I§Ó\úÁ¹íßò–7ó¶3ά;ý»÷ß/W¸ °E&]^§@pÞ†»A»v9®@ÓT9¹VTâÆíNS7£žJËÃ⥈-òùšV®%àóùÈf³ÃõFœúѧ566’N§±,Ë?Ž÷Àiã'hÙ²,b±†aTu'ªÝ¾wÚX!ÞkÓÿÀ\w<+ÂéÝâ@¦¥8˜VáîoþkÝé_t_tÑa·¯‡U*UÅ(|æÆùÌ7Öm{ýu×Nhº ‚ "áHŠUUЬìAP®IQNvêŠ528Y)§?X6LFÔ¯¸¬”¢³©v[ ÃÅÇŠÅ"~¿Là=€rÍ¢ÆÆF\#j='N=„ÆÆÆºÂ£v›^aàW^oFcº)׿3{Ô 5A8˜ryŽmÛìܵ‹;w°|ù29 ‚0õ¬Í ¶•( S_8i;Q l:¯å.F ‹§wQ®hâ7-l¥Q²t‚>‹LÞG4P@ÙP´ üFilqPÙns¤DØ_ ], „p8L©T"›Íâ÷ûGˆ€zÂÀ¬ÜØØ”ë)8ÓÏ@­qïóùhmm%¸Ý’ÆÂ[m¹^Ë¢½Pxý·A8ÊOµ×þüÓ$a4²Ù,§q&–eñá«®då 'ÈAA‘0éBõ`ƒ­ WVv«ZùøÚª"®lòEÈ6’*6‚f`[yš‡h ¥±•†ªëjžÌFõ–M]±¨-Ëû¢ndŸÏÇÐÐ n€× à›ÚiNA´ÖÖV‚Á ƒƒƒär9·­×kÐÔÔD$Áï÷WU•övEª'*¼8ñ F©Dë(BCô0õÅA}D ŒB(⩟ý”p8\ä$‚ ÍÇ¡ˆ„‰'‹‘k°{³UÄÁp†£r‡£TÞ$`ZìOΦ§¸’¢ÖFI+‘JL¥ðífNðE:“äŠ>"þ‚GP7›Ñªy)^ÜÛ šázúúú˜3gÎˆÊÆõ'–À†a‹ÅƒnÆ"g¾ßïÇ4Mü~?†a¸éNk×7Úv¼q^¯ƒRŠ–l–˜mË&Ì(D ŒAó8…ÖAÑ“M$TÅ Ø6è•nn‘´ÊA­]•xµïDúx3ÕzuŒ€Â&M™tÉìS,iB){Ø‹àÆ/W,ß™%äË“-…Ð4x<Ξ={H$477WèÞXGä8)H5M« 4ƒA×°w¦;¿SÁ™ïÔEP±42ã®8ðSC)Z i²>Èš*Gsäõêí ÂѸŠ@ALj•®F‡-¼„a‘P1äk2e ¦n³cpÕÛ°Íl¥H'“ ×H…B„BQòú v¦M½?¦£‚fCsjxÅBù³=V丶/('ùD"º»»ihh¨ªX\k¬U¡¶³¬#ê-[åMeZírN×%?ÞyÊ&Úôa‰R:v $œ0mÅAADÂ4µu”ëA”Vq”=~½D_ÚϾÂlÁ¶lÜBcŽáL&‰Çã477c‡ç•žh‹>‹fVÞà×VZ®ŒûtÅÇðÒÀ‡®ë455ÑÝÝÍÐÐ---U1µoB©TrÅ„7½©cø{?ë¥{­µBÊÙ‘ŠÅbU»Å±}7ç¦îÍt$±ÂôG—C ‚ L• ‡`⊡œæÔ¶ËÙŒ”²Ë¢¡PÒ°,ØÕ?å/ç÷ïïï§»»›\.çvµ)‹d2:D:Æ4}dü«9ЯQ,jØVeNw¦J`´cì/Ÿf~Ó û–> ¹^ÇKáíäŽ@qƽӜšÎ4oGàxÛ9‚ÇÙ–w\Ó4lÛvë48˜äxûìíø …®á†Nâ#éa$L×;ŸAAž’ÓZ$8BÁ1æm4e¡ì‡róÑ ¥R‰¡¡!”Rô÷÷“ËåÜz–e‘Ëåèëëöm‚ÑöôE±íjqP.Ìæ 6aŸÅÙË{0´ò›yÇ‹Ïç9xð`]£Ý;xE€W´8Ó½Cm{ïPOx‡|>_ã=°YÛÃ’†¹,…‰t1A¦ˆH®FÕTʶ‡_sWê hhèX‹6y»P(Èår†aär9LÓ¬2¬‰–eá÷èOù±m°M÷dMªùÔP¬š“då¬^8Љ¦éøý~šššèëë# ÒÚÚê¦<õvûqÞì;ß½éLëU=öz¼Y‰ÆŠGÀó·{ ë Ξ³… i‰rfä{‚ ‚ˆ„iÉp%e<Õ“¹¢T¹ëŽ“½§P(Tþ‹ÅªL@†a –J˜ZÍÙFM²7h9ì/ñî•Ýìì‹‘*ÆÑ4p8L±X¤»»ŸÏçV=6 cDl€SëÀ1ú½ñ Þ6ŽgÀ;½6£‘W@nªª«Ä™³7³°apü.D¢„i(*A®nAä+U”§Úy‘0îQR”»ic½âA(ˆ|nðf×ðvºÙÔKê÷—ÓŸ¦Ó)âþ:ºrê)¨Q+,k 4gY³b/ÿýÂJÊ®ØüœÚ IDATëÄb1º»»QJÑÔÔ„R Ã0\ƒ_)…®ëîà `vÇÃ1ž0pÄÓ¶T*‘N§«*'+e±<¾“3;wá3Ô½ÃÂä— RIYA˜Òú@DÂx­r_z×Èp²†RdJ~Zô­d2iLÓ$ 1000"·¿cxG"t]§çÀVÞÙš"›÷ñç=IŒFv1r> Þ² ŸCɿر0ÝÂgétÚG¨­?ä¤Zu÷¨tIrÚx÷ÓÛ­¨6ðÙ/ d³Ùq hõõðîù¯ÐÌÖ1¶•„r o”,8¶òS‚ ‚0EE‚†ªCRc«¦ >lKa8Ó5§ë†BaRbEÇ!žØ÷4­ ÞFCCÙlvD6çm,#—Íb>żã,L²‡Â•ÿBHˆú-Î]ÚM2kòÜ9hš‰išD"r¹ýýý”J%ššš…Bî¶ß›ÞÔŸP['¨ŠGpD„7 9—ËH}ÚdöqñÂ?1?>tXÙ‰,r–„„#%D ‚ ‚¨„×v4g¨Vé –¥*ÅÌœC¥UúO/Ð68.øK^íj µs9–e100@>ŸÇ¶m à ÓÔÔ„¦Á¾WåÝ‹÷¢4ŸV¨©¢ÌQàýÔ”¢5’ãüº°mxþà\Ð w…Bt:M¡P ©©‰p8ìJ{ ªyEB­Ppâ¼ÁiS,ÝìLÎtg¹F³ ü‰Í=U5Æ"SÔÉØawÜÐM÷\ÂÔ•ÓL \{ýÇxò©§ª¦½ÿ}ïãä“Wsëm·“ÏçY¸`?yäáí›››Y±b9Ÿ¾áN\¹²îú‰¾}=þ8º» ‡Ã,Y¼˜¿ý›óÖÓO—+Zá˜èƒ™-ÊFnyƒUoÑûsih‰ÙÃJ¢ò†_C«Ä-k¬šÕ:ð(›¶÷i;‘ŽŽ …–eáóù0 ƒT²ŸôžßðŽ¹›YØZB¡†+5× ƒš8„Úø„ÎX– NØCÐ,ñ‡ýó°*EÔ†aP*•èïï'•JF ƒø|>׃0Z ³7Áñ88iMóù¼û7 7P¶E{ ‡ æ¿ÀŠæC‡wàЛ ‘±Âî1†‚î¹ÐD%¯YLŽkgZy>ºn׬»Ú÷ûýƒABÁ _ùê×\qàm¿nÝÕôöörßý¹zÝ5<úðC455Uµëïïç¯/û æÏçÎ;ngá‚ òÜóÏÓ9«S®hA„c*fö!púçÇâñ*P"ÈæÎŒ¦q³U„‚c´ûõ"FÀ渖¿`çàfº³dí,eb¨ ­—ù±·ðÍqS·Ë± ®®<ç 6“‘#ªƒ–gÇ3¼{Y Á<¿Ø9Ÿ¬AÓ4LÓtE‚S›!•J¹Ïs¿ßiš5i¸Ë‘ó÷; P(P*•ª29ËØV‰ñ]œ5g Ç5öMH(ÛÛ(*¿[®¡¡aD¬„ LU¦•@Äãñ µoˆÇiˆÇ¹å¦õ<üÈ#<ûÜsœ{Î9Uí¾ðÅ/ѳáî»Ü~KK K–,©j÷çkÎãkÿò¾üÿý3¿ûýïùîý÷ó¦7D&“áö;ïä±ÇŸ@)ŹçœÃçÿñ¹ÆšÁEsù?Èe—^ânçÆÏ|†X,ÆÍëדÍf¹ýŽ;yô±Ç|ðÒKùøÇ®—·‚ Ì ‘0sSŸ:Áº†aÐÒÒR1^” ìjã- SeãW£Z(†fƒÒh•håh¦ÌÀÆ h™ø<>­H,X¤#^¢h`«Jb¤áõ¨QDB½O§m[$Ë;í£#’æÉséJ¶VÎ`äóùªº   …‚û·š¦éþíÞ®FµujÓ£B¹6DÄHrZç6NmÛCg4Yާ˜yKã…þ(4Wx´´´ºAžÁÂky×1™òb‰Ìõ¼°¬j·e¡Pà±Ççc×_wXo>óÙÏñž ÎççO<Á '¬àö;ïd×®Ý|ÿ{ßåÿñ}úúú¸ñ3Ÿu—kþšwÿ=þ¸Û¶X,òäS¿àüóÎ`ýM7Ó?0ÀÿûÑq×7¾Îô#~ôÀrBA˜‘×ùüÒ4tÃ`áÂÅeï€Ç‹°k°mýecÙ.W:V•jÊåŠÇåªÇ~­€©Ù´Å,–´&YÞÚÏ’–A–¶ ±¸5KKÔBS6>½X^‡k|;Þ„á*Íîwo¥e5ÜÞ×”MC ÀêÎ>°r3ç,ÜLƒoe[®ñošå@fÇèöÖlp‚Àê|>ïVDv¼Žˆr„“ngYßÎûý³ælcnlââàÕ¾öçfU¥]¸hQy?Ń Lƒ{˜9î©woØÀ}7ºã·Ýr3ç­Y3îrN|išœñÖêx‚îînr¹K?Þö¾üeþó?àÌ3ÞÊW¿òwÞ™gœÁÅ]Tµîÿ÷ãyà¿~ÈñÇWÙ¯[8û]«‹†††1çŸÞy|ó®»J$hˆÇùÍoK(äͧžÊP"ÁC<ÂÏŸxœŽŽæÍ›Ç%øÿóÐüÿ}ï“ßš ¼Ö2òòojžëàôËïèè ¹¹™þ¾>W d­Ol™Ãü¦í„NÅãr±4÷XiîdtlôrxA£ˆA¹šŽ×a ª>Fd1rÛªqÓŸ‚¯Ã¦ÁÇ7÷óò¡V^êé` Í@ÓªßÈ{³–9È+ P6A=ËÂX7«[÷² ÚO[8=¡.EU6CÎäVPÂRåz Ì™3_ų!SRxžÓª‹Ñe—\ÂÚµW¸ã-5ù”ë Š{î½—L&ÃI«Vqφo¹Ý~ìÊÍ'¸Ó®Y·ŽË.½”ïýû¿³eËÖªö«W¿©j¼kï^×ø˜=»“ÆÆF¶mßA{{Û˜óÏ>ë,Y¼˜Ÿýìç¼÷//æ±ÇŸ`Íšw£ë:»vî²,.¸èbwÙb±Èœ9sä×&  3ðÏ®„p8Ì’%ÇÓ×ÛëféÑu­übÛ¾¬§’òt8“Qµ¡ïíæ 0ñÆPÓ^5‹QQ êSk æˆûsÌŽ$9©½›]ƒ lhf_ª‘D!‚ªt?ÏWJ¡l ¿V`V¸ŸÑ^–Ä{éˆ$i ¦ñökÎ4T²àÉ}DZ;3»ªÓÂE‹‰D£n%jA˜˜8˜|׌©M£Weñxœ¹0Ž/»ä®ºòJššݜ˵t´w`š&Û¶o§½½€Æ†ˆEc#Úû|¾ê›I±T÷ ‡²mü>߸óÎ?ï<{üq.ºð=üìç?ç_ûjE¼”³4üè?À0á}0%{­ ”o¹Ú„Ú¿<‘üTÜÎ4`&†irÂÊyé¥Èf2ÃÕ‘í¿Ü½˜†@Ó ¢ëªFHy®xÍcüW}¨z–øèã5Ô8q åO…4³4²Ì‰±²õ ƒ¹ ¹û“Qù ýù0ÉBKé(UÙwÍ&¤i ¦ˆú²t„´Ó424úsÄüù×ì1ðŠƒßvÏçéÞå•Û.B‡ÂaN\õ&LÃÀ‚0ïYZ3Ú’ŒÇãÌž=v¢P(ÈÛÎ<“ßý.gžqÆ„·±pÑB4MãÕÍ›Yy ìÝ·D2ÉÂE ‰F£cÎwÂ7ï¾›ßþîwøý~N^½€óç044ÈI'$¿1A„êE(÷×7hkogé²å<ÿܳX–åzóq~²íx,{§ÍïÃgàIدFô•/´jņòô(ª“öÔ»O3wÜS3!곈˜f…“”,t“¼¥“/™,ÃÓ¼üÅÔ,‚fŸ^^.`Zš:"u %ßXÈ/®$iE±mÛ  ^¼x ³fÍÂ4Å{ LŸ×òªù0øÜg?Ã¥üŸ¸áS|䪫˜7o.Ùl–Ý{öŒ»lC<ÎÅ]È-·ÝÆí·ÞŠ¡ëÜñ…/ð®sÏeÎìÙãÎ_°`>‹/æ+ÿòUÎ[³Æ½577sÞš5üÓú›¸í–[èìœÅÎ]»(•,ÞværâA‘0SþdMÃ0L~?+WžÈÎ;H Vu5êÉ6ñØö¥ô¤»xûÂý4‡-´ª˜›zæŠC8¨}0J&#÷£6jµ÷`¤h(Ï7P†",ºmʱ5˾Ø ú2A~uàxž\Ä@!Žm+·kQ$eÅ 'â÷ûGÄJÂT–Ó>ÔþÇ>Èú›oaÏž=\üÞ¿zMëX²x1?üÏÿÀï÷ñ7Ÿøo?ëlÞûþ°yËÞñŽ·»üÍë׳ò„•\~ÅZÞÉ¥466qûm·ö|€óÏ[Ë/½ÄùkÞ]5ýŽÛnãÔSO府sÿâÝÜ|Ë­ärYùÍ ‚ Ì4S£â-hllâ„VâóûÝz¶m£ÐèÍ5ðÛ}Çñ/žÀ¯v¶Ò—Ò±-ÛÍf4rPåìGNÆ"Û;¨áŒH¶7{QM&#»ÜÆÉ ¤lÏ2î§r?‡ç×fFRî¶LHo”8°ôfüzÿB~°ãt~ß·ÌÎ15 ƒeËWÐÔÔ„.±´‘•ýûô­wOº÷,ŸÿÄ¥£Î{æÙg9ë0ŒrA¦?Oþâ—œzÊ)£ÎÿÒ]?:ìuò9RÉAR‰ARÉ!¾þå[åOuf˜Á²,2Ù ýýýtíÙÍ‹/¾À¶-›QJaFUŽ~Á á$³£Ì'i gˆŠøên9 ÐP£×Q2Á°7 Ö›àñ4hÀðìÏÀˆú jdô‘¸T,D1H_6ƾLûÒ-ôä(D±•î%[–𯂋8qՉ̟¿Ö¶6¢‘¦Ï'BA8Lq0y®“O~öf¢ñFb MÄâøAéb$‚ Lã§ð  N,B8fΜ9d³YöïírûË+¥Ðu¥ôçâ æ"ìN4í-4J˜z C³Ñ¼YŽjÃö«f€ÕhÂc"©š†Zý…ßP-iÙe’·ü$‹r–[é•âk–+4M£}V'³çÌ!ŠH`²ðš%ÂdF‚ ‚ L+‘``úü„ÃfÍ*'âèÞ¿b±8¢²°­i¤‹AÒÅ`•¹<sAUÉáhVUufŸÏO[{;mííD£|~¦t/¦•4 ‚ Ì„§ñ ò"”Sž˜¦‰?à' ÓÚÒŠ¡ H •»È€ÛÝÈkÜÎ\CW(ÀæÇëoh ©©™†ÆFbÑ~¿Ó4ÝÚ"„é$D ‚ "¦‰8(w12 ƒD ùJ)LÓG("‹“N¥H¥Š%P–kØŠ[S¥ Ÿé# ‹—kE¢‚ ‘h„p8J(Âç«xtÉb$Li๖A»qU‰yA˜ŠèºŽÏôaƒÄbåBž†é# ‘ gøÿÙ»ïøÏÿã¯sr²HN†#$¥*j´Vk%f)¥Ã¨M«”RŠˆQÕêPv©ŠmµöVZ~VÌèWU‹ìuÎùý™‚Œ÷ÓãÆ¹ïë纯û¾¯Ï¹¯ë¾ÝÝ݈OH >>žø¸8“‰‹%.>«ÅŠõÊSl6[=_p01špuv¥¸[qMޏ+†k±b¸¸¸àâ쌋k1Š»Çl6ãîæŽ‹kj`4U%Ë2–o¯•7 &] EDÂÍÎRèŠB¨÷:88àìäœ,8¦Vnâ=HLL$..ޤÄD’HNJ&))‰„„±Y­¤XR®<<èZ`Ëg™–׿Ð_í~m4±aÃä`Âèà€££ ggWœprrÆÉÉ g'\œ\pvu¥˜‹ .ÅŠáâì‚££#zIšd³¬”AMŒDDDAB! ®ÞMpqu%ÅÝKJ ÉÉÉ$_ù7%9™””’SR°Z,X¬V¬Kê{ l6òíM„ÛPÇ2RŒFŒ FLNŽ˜LŽ8šL˜L&¯ô9p49âp¥Ÿ‡ÑÁ¨;’½b[ÀâG“â])Ô'å¤ÕùP MÙ¿òÒ4œ°ÙlX¬–ë:ÞZ¯½ØbÁjjÏõo2Îw_ê¶åÕõÿ^í¼í`t¸öÞˆ«ï0ì’uÇ@îjd{¶ÐpÝÿuADDŠÖuºˆ5±¿Z™½þ—î›ù¾ïáÎå×õÿÞø‘ÛX| ˆˆˆµJKA|r‘êèRƒƒZpÕpNDDŠâU[DDç""" Däb pÓ4"""¢€ND…VBArîÜ9ƒˆŠŠRfˆˆèú-"ùýÔRÀÏ/…&@4d(Ai†ñÃøjùrjßW—€À Ú„¶½iúF›ÐwÀ~ûý÷L—ýó¾}é¦8y’€À ºtí¦#BDDDD!Bÿ¦Âô ÊÀþýпŸý³““...¸º¸0kö;|¿nmºôýû÷ãÂ… ,ùp)ýú໵kðòòJ·ìråÊñíêÕÔ N3~Õ·ßR¶lY "ým(TçvÉÉ~·)D$/;†¸Ñ7 …ª‰‘³³3f³Ù>¸¸¸d™ÞÃl¦Š¿?“'…‘b±°ÿ—_nš¶e‹‡ùî»ïINN¶³Ùl¬Zõ-­[µL“6..Ž—&L ^ƒ†×oÀ˜±ãˆŽŽ¶OoÊ–­[y²WojßW—v;±s×.ûôóçÏÓoÀ@jÕ¹Ðvíùý?Ò,?>>žñ&\¿4iʻ⦅ÿŸ_-"RI‘LÏ'…ã„¢>W3âÊ d,ëM§W¯V%J°eëVû¸}û÷ãääD@@@š´Ó¦OçäÉS,ûäc¾ølÿþû/£FI“fÒäÉ 8€õß­£YÓ&Œó¢½’NbR"«V|Ã3g2áÂ4ó†M ç¿‹YñõWÌ}ï]¾üúk¾^¾\;QDDÀ‰Üµ¢Zx k¡ æ-X@½ íúï¾ËÖ|QQQ¼ñæ[˜L&5lpÓ4‹•öíÚ±rÕ·öq+W}KǰY­i–µbå*Â&N ZÕªTñ÷gêäÉlÞ²…3gÎØÓuîôM7¦L™2 4ˆ.\àüùóü÷ßlÚ¼…ñcÇRÙÏÀÀš 0Ð>ßå¨(Ö¬[GxØD*T¨@íÚµéÞ­«×¬Õ‘)"¢J¨ˆÜ­SH!:ª7)?Þ½;½zõ´.áíe@±hñbâââ¨]«‹ÌÇÝÝ=Ãô;´gÞ‚DGGãììÌ÷ßÏò¯¿bçÎö4g"#¨Vµª}œO9<==9qœ *àïïoŸ~u1±±ÄÅÅa0¨^½º}úõiOž8‰Åb¡]ÇNöqÉÉÉøúúêèѯ · ¦¯c6›)ŸƒŠòãݻӧwo¼¼½RÅŠ\¾|‰Úµkë€Qt§ÔI9‡Ú†„°ÿ—_ص{7!mÚ¤›îa6Ó©c&OÊ‘£G‰ˆˆ lÒ$Z¶h¯O–Ë/Q¢Íš6eú«¯ròÔ)9Â{sæÚ§{{{„°I8póçϳ{Ï¶ïØ©#"r«—xýT-"Š €°rÕ*ÂÂ'súôi:uîrËËóôôäÁ i“&öW #(0ˆ§zö¢k÷xzz1mê”l¯ã•—§áìäL—®ÝxnøžnÅŠ³OyêT‚ƒƒüÌ3´h݆ðÉSHHˆ×*""ª{‰ÜÑZ8K¨á…ióó]‹ËñÏvÏpÚ¾ýûiÞ´‰ ¤ˆ°iËV‚ëÖÍpúëó³ÿøß¤Äb¢.u‰˜è˼ûúdepe³ÿ%ù!:P€ n¯/NÆÝ쉻‡nfOœ]0ÖÈGD$Gç7ƒáÚ ¢b "E 8Ȉú ˆˆˆ(6) ˆˆˆˆˆ(>P€ ""RÔ¯ý"¢3„Q5LDS‚ˆˆˆê"¢3C™tQ|p•î ˆˆˆíº€ˆèŒ ADDDDDñÕ D$'‚¢u&Л”ED'ô«iõ6eI(:×Ë+×@ÝAQõ@y.¢Âh§A²ôÑÇŸ°aãFe„ˆˆˆ(:P€ EÝþ_~aÖìÙøùù)3DDÕѯ¡à4d(Ai†ñÃøjùrjßW—€À Ú„¶½iúF›ÐwÀ~ûý÷ —ݨq.]º”füÖmÛm×¾ÀçÝG²Ï>¿é´×f¾ÎÛo½‰åÊ:Iˆˆˆˆ¢ƒ"ÀT˜¾ÌÀþýпŸý³““...¸º¸0kö;|¿nmºôýû÷ãÂ… ,ùp)ýú໵kðòòJ·ìØØX^~e3_Qè ÁÎ]»hÒ¸ñM§}öé':?ˆˆ\W]°)Dr…ª‰‘³³3f³Ù>¸¸¸d™ÞÃl¦Š¿?“'…‘b±°ÿ—_nš¶wÏž¬Y»–­Û¶e¸¼¸¸8^š0z \¿cÆŽ#::Ú>½Np=öíßoÿE@`çΣ]ÇNé~Å5z4áS¦Ïø  ®ß€š4åÝ÷ÞÇf»v™Ú»÷gžxª'Áõиi3Þ›3'[ë7~àíåE™2exø¡‡¨ “‰ˆ¨Þ ÊcQ , Õ„Ç»w§W¯žöÏ%¼½³ (-^L\\µkÕbÑ‚ù¸»»ß4­ÍfÃÍ͗ƾÈÈFÓ¾m; ×½LéLd$ÕªVµóñ)‡§§'Ç"ŽS¡B…L·¥Š¿?UüýÙ°a#éÄë$$¤ F£‘“'Nb±Xh×±“=}rr2¾¾¾öÏ»÷ìaÞ‚œ8q‚˜˜XbccILLÌU>fµ¾ûêÔ¡[×GéðHgÚ·kGÿ¾}©T©¢Î'"""¢ø P6C¡É³ÙLùë*ÍÙ (úôî——'®®®Ùš'4$„¯¾^Îø°0^ý‚}|JrJš6úöÀÂjÅÉÑñ¦ËJHHHó¹mh(?¬_OÇíÙ°q#ï½3«ÍŠÑhäë/¾ÀÁä`OïhJï"¯4)ê×÷iÆŒ…›»;Ï ‘áw¸q½7Êj}ƒ1/¼@ÇcáÐá‘G˜=ë-š7k¦#J ðYÞpCz]!$³ÒbSgeÕÐD Ùñf°¦Ä”dŒFŒF Æ¢u@šÍf||Êåx¾I'оÓ#,YºÔ>ί²ƒC‡˜Zq?{–¨èhü*ûP¼x1bbbìó=‘.@˜3o;wíÂÉɉûêÔ RÅÔ_ç/_¾DíÚµÓmσ)Q›áÆ¥%6ÿþ÷Ÿ}zVë5 X­ûç¬ÖwUÅŠ™NÙ2eYöÙç ¤pÄÕM$»F!‚dÅ–1’ûSºáN¬¤(Æ©},V+I))$'[°YUt³R±bE Àöí;ìã<Ìf:uìÀä©S9rô(„MšDË-ð½Ò·fš|øÑGDž=ËŸþÉìwßI³ÜJ•*âïïϬ·gboÂäííMhHÂ&qàÀAΟ?Ïî={ؾc'>>>\¸ð/?ïÛÇÙ¿þ╯¦yzRVëõ)çæÍ[8ÿÏ?\¼x1Ëõ:tˆÕkÖ™ºÌCRºti )ÀùÔ!oÿn¼ØKy··Ò,Q?Ù¥é¤lµ] R°Z G1^¹jaá“9}ú4:wɳåìß?ÝËÃÂà⩞½èÚ½žž^L›:Å>ý¥qc‰‹§]‡Ž 9’Aàa6§YFÛÐþï·ßhÒ&Íø—§N%88˜ÁÏ }§Nt~´+ƒQÏÐÑ$"EŠAµ‰|UmÌ«j¢Ý › EuWÎoÏMžkËøäFŒw4ªziH× §íÛ¿ŸæM›¨D‹›¶l%¸nÝ §¿¾he¶—•”˜@LÔ%¢£.u‰w_›¬ –lT.T¥QПA%º€1v îfOÜ=¼póðÂÙÙ%ó§Ùþ¾?¡ IDAT€«…«£ÁˆÉ¡èõSɬB¡ ![‘Ôõÿ¤Ÿpc%MU ¹ãÇòí)ó5˜ÈöcNS›Y1 80:èèQE?$U/¤p„yLÜÁMÌñ{¬6VK ËÝi~$"""¬Ò/¢ÐàöyøEL·²×7?r0uWADDŠ`å"Ÿ53R…_¤°†w,€È“7)[mV¬+ ‹è;DDDî\¥_~…y@X,ÖÔÁjÃfµb³Z1åeÖØ‹Õ†ÅjÁh0؃5A‘üê×›ÒäVJZ.ï"Ør[ÕWQˆwõw+`µÚH±Ø0Zl˜nW¶Øl6, ‹ž€$" ¤ˆ]m3þ(ªªI> ì%ýõÏ@51ÊÊÕ'  †Ô¾ Fƒý-Á"""ù¿ÎŸ½j¾MÑ€ˆ¦;¹2›ÍFŠÅìÁ‚ƒƒQ{ADDòyÕ_îýv(w¬¨©°å›áfÁBŠÅ‚Ñx]‘Zù7 &E"¢!OX­6¬V )¤ """ù­ò¯A¤pÐ݃ Ü,ˆˆˆ*þ""¢ADDTù/ðtA$?Ÿº{ ADDTùW„ "R4„AC†²ióæ4ãº>ú(÷ÝW‡)S§‘˜˜ˆ_¥J|¿nmºôÞÞÞÔ¬Yƒ‘#FpOPP¦Ëöôð zõêŒ|~÷Õ©£$"ªú‹ˆˆØ]wŠ\€0°ôïgÿìää„‹‹ ®..ÌšýŽ=8¸>}ÿþý¸páK>\J¿þøní¼¼¼2\ö¥K—ødÙg <„ïÖ¬¦D‰*E"¢ª¿ÜPÑM)¸Œ7ÚÝrÇÙÙ³Ùl\\\²Lïa6SÅߟɓÂH±XØÿË/™.»bÅŠŒ3›ÍƯÏø  ®ß€š4åÝ÷ÞÇvÝÛrþý÷_ L­:÷Ѷ}víÞM½ ‰ŠŠÊÖô½{执z\¿›6ã½9sìËÎjÝ"ª¦ÝÙóP~«þgþGnkÑe™ä£ò¥–“k¥ú \”®¼ƒÁb±fFƒäädÂ&…ËŠ¯¿ââÅ‹ ñîîî™±±±Tñ÷gñ‹ðôðàÀƒX,Úuì”&ˆðõõàLd$U«T±O¯rÝÿ³š°{Ïæ-XÀ‰'ˆ‰‰%66–ÄÄDNž8™éºE$¿QÕ_‚ˆHþW¨³ÙLùTŽïÞ>½{ãå剫«k¶‚ 6²øÃ%öõXmVŒF#_ñ&{zGÓµ¬5 ®û)ÓÉÑ1í5$“é‘Wšõëû4cFÂÍÝç†ÈöºED•QŒ RtAÝ=È cQþòf³ŸrY×=Ÿz’Þ%xç½÷¨T±"—/_¢¼¯¯}(S¦ ¾>>X­VŽEDØ—uêÔiûÿ³š~ààAJ”ðfø°aÔ¨Q_þýï¿l­[DrSÍç†*-›DDD ÍHÔ` lâ–~ô1‡ÁÛÛ›Ð&„MâÀƒœ?žÝ{ö°}ÇN õ Í›7cú+38qò$‡aÖ;ïØ——Õt.\ø—Ÿ÷íãì_ñÊŒW혳Z·ˆ¤¯üÛl6¬Vkê`±’b!%%…ääRR’III!ÅbUÅ_ò袡,¹{‡Ÿ@X¹jaá“9}ú4:wÉ“eÖ®U‹N;ŽÍfãå©S fð3ÏТuÂ'O!!!Þž~ʤI:tz„gŸÎc]Óv Îlú}uêЫçS ú ]ºvÃÃӃݳOÏjÝ"EÙµÊ2IIÉ$'%_ ®–+‚Õ†ÍfCOÈ)óóÝ%qÒsÝ3œ¶oÿ~š7mR 3ýì_ñpËVüºo®®.9ž."©6mÙJpݺNŸ1wy¶—•”˜@Lô%b¢.}™÷ߘ¬ –¼aS?„lUHôc¯äeyºîoÉܰ1“q7{âæá…»Ù'g51ºŽ>‚§‡G†•ÿ¬¦‹ˆH«©ˆˆ¼C»¹–|ø!UªT¡Š.^ºÈÌ7ß$$¤M¶§‹ˆˆ¨*'’Ýò¤¥¡°ZmLž:óçÏcvw§YÓ¦¼0rd¶§‹ˆHÁ¯«™‘ˆœAAÖm×÷é>ô}ºO®§‹È-ÔÊr’Ö€~ÊEùýXQyÊûüºaP)$u]EyÁ¤l‘Â{¡ÈYZÝDüRE$—ǘ´\Ÿ›®¿ê‚ˆˆˆˆ( ;"""ª»ˆè""""RXbE DDDDD®E¢ADDDDDwn“B.)Ì—œ¥Õ3ŒäI½Aäîê%[tADDDD hl è@B>Ô*$”úI!"""rç#¹ L…%c ʦ͛ӌëúè£Üw_¦LFbb"~•*ñýºµéÒ{{{S³f FŽÁ=AAÙ^öËS§äɶôñ'˜L&ïÑ]%Rän]8 ºàÈ,—jf$’‡’NÖyvNºa0¦ï7°ôïgÿìää„‹‹ ®..ÌšýŽ=8¸>}ÿþý¸páK>\J¿þøní¼¼¼²µì¼²s×.š4n¬*"" äETvîºBÕÄÈÙÙ³Ùl\\\²Lïa6SÅߟɓÂH±XØÿË/¹^v||<ã'L$¸~hÒ”wß{›íÚÏD±±±L›DƒFP÷þúe AÌ[°Hmºtèða L­:÷qàÀAâââxiÂê5hHpýŒ;Žèèh•d)B±¢ƒÛɤ,¸)Sc%‹Åšëe„M '&6–_ÅÅ‹6âyÊ–-C×G`üÄ0Î;ÇÒ—àìì̹sç¨]«GŽ¥CûvtéÜ9Mà1zÌ‹ôï×—W^~3Ó¦OçäÉS,ûäcŒF#Ó_™Á¨Ñc˜?wŽv¢ˆˆˆ•Ang€P˜òwÞ‚,YºÔþyêäpBCB²œ/**Š a2™hÔ°A¶–=iâD:´ogÿ|9*Š5ëÖ±ñÇõ”)S† *н[7V¯YK×GåâÅ‹ü°~=Ë¿ü‚€êÕð«T)58qpÀÙÙ³Ùœf4jD§ŽÓl犕«XþÕ—T«ZõÊwœÌC-[ræÌ*T¨ -’ËëÇM0En{áT?‘\>:KçõéèÆ¡PÝAx¼{wzõêiÿ\ÂÛ;Ë€bÑâÅÄÅÅQ»V--˜»»{¶–í}C?…“'Nb±Xh×±“}\rr2¾¾¾œ>}›ÍFµjÕ²ý}êÔ¹7Íç3‘‘öàÀǧžžž‹8®ADDDŠFVn«B ˜ÍfÊ_©g7 èÓ»7^^ž¸ººÞÒ²­6+F£‘¯¿ø“ƒ}¼£ÉdŸn0ä¬D;::¦ùœ’œ’¦OÃU6«§ÒŠˆˆˆ(6\E7«Íf3>>åòdY•*VàòåKÔ®];Ýô å+`µZ9aobt•ÑhÀjµd¹¿Ê~ >LP` ‘gÏ_e?•f‘[º”¨‘‘ˆˆB„¢ž¯©×@½(-›‰ŠŠ² i¦{{{„°I8póçϳ{Ï¶ïØ @É’%hÞ¼S¦NãØ±cœýë/6nJ}·‚O96mÞÂùþáâÅ‹nƒ‡ÙL§Ž˜>ÚI"""R¸+±ŠîˆB ¬\µŠ°ðÉœ>}šN»äz9ó.äþ†ìÃÔ—§§KóòÔ©3ø™ghѺ á“§oŸþê+¯P®\Yº?ñ$í:t`ÍÚ5 <ˆsçÎñpËV|³be¦ÛFP`OõìE×î=ðôôbZ½°MDDîl]GDtÈä˼9ma¾{ŽÂÄg»e8mßþý4oÚD{NDØ´e+Áuëf8ýµù+²½¬¤Äb¢.¥Ñ—x÷õpe°Ü~z’‘j}’ý¢bPa¹†¿87³îž¸›½prvÁ¤SDTñ@Ý ¡¤¤$Z·n‰Ÿ_e^y”õõìO‹-éÞ½}úôæäÉ.kÑ‚Eü{ñ"cƼ@‰%øò˯íïÏ¹Þ³Ï å·ßcøðt¾É]ë¬Ö³pÁ"ªV«F\\Ë–}Ê–-›9÷÷ß8::R©b%Ú·ï@hhhšyºv{”ÎtáÉ'ŸL·¼V­[2fôê×o@§N2\¯››«W¯M7~Þ¼¹,[ö)ã'L¤UËVi¦íرƒÉSÂùþ»xá…‘”(Q’qã^`ÈÐ!üùÇïé–9wÞjpþï¿™7?ÿ¼—¸øxJx{S³f Ï>;Œ>þˆß,Ïp{Ÿ}v]»v»éþ¾ÊÙÅ…ò*Сm;¹a_ìß¿Ÿ‘#G0vì8BBBÓ-ÝÚ5|ùÕ—œ‰ŒÄÕÙ__:´ï@»ö7ÏÃÌÒ/Z´Ï¿øœ¤ÄDJ”,ÉÌ×^Çßß?Ý22ʯyó`ÿüÜsÃ8ÿÏy>ýdÙMË`DDŸ}þ¿ø•‹ÿý‡»›þUªÐ¯ß._üW^™NTt4ÅŠgسà mËüùóرcK–|xÓï7þ<>ýô“ôÛ½\¹rÄÆÆ2tè`’’“éùT/ªW«FRr2¿þú+³ßy›ÿûíÿ3zLŽòÀÍÍ?üÈþùµ×^¥dÉ’ôíÛ‡ çuquå·gQ/¸^7<âûz^ž^xzz¦ PZ¶â©ž½ÒŒ+[¶,/¾8““'„áåéÉßÿœç÷ßÇÓÓ“Þ½zÛ¬£GŽ0mÚÞ~û<¯¬ß;“ǘx~$ 4äòåËìØ±·g¿££cšÊýŠo–S¶lY¾ùfyºa˖ͼ6ó5 Hýz÷—ÀñÇíë¾QVéûõ믯/o¼ù_µ<ÓýÔªe+žêÕ;͸rWò àøñã>rWöìÞEÃF¤I»}û6Â'‡sp=^x~$%K•æÒÅ‹8x€Ò¥JX³&ó~@îÝx}æ^yàIv”÷-Ï˯ÌH»Ï3)wõQ€ ""’™ýðþœ÷iÚ¤)ÕnxŠÜ•V³‡Ž&GûÓé®gµÙhݪ5+W­L ¬Zµ’–=Ìž}?g¸-é×c¢Ò•—f^µpáÎýý7Ÿ|ü)%¯ 6ñ«T‰—Æã¡æqÿý÷g;Ò¬ÇÙÅ™bÅ‹§[÷Í4jôÿüý7o½ùS¦NË0‡§'žži+ŒÅÝÝoºŽ„„Nœ<ɤ°IÔ»ò=ªT«Æúhé--ËjµRÉÏ{‚îaõêoíã¿ÿá{Ê—/O5°Z­¹^~JJ ΟÏô—ýJ+q62òæ_¾åË3tÈPÞ~{–ýýµmÛ–|0͸oW­¤Uë–ö¡sçNöi“çð×¹ÿѽ{WæÏŸÇ©Ó§ót»£cbX·v [·n!¤M{þ~ûí*Úµkwe›Û³aÓF¢¢£íó5k֌޽û0rÔó¼0z?ýôÉÉ19MŸ™•+¾¡E‹‡ìÃõùõÝ÷ßáìäD“¦M ¤BÅJ¬ZuíɇgΜÆÍݲeÊÜ–rpêÔÉ4û²Uë–DDDܨG›Þ &幈V¹é£,…“³³3/½4žçGŽ iÓf7í$š:vâý÷ߥoß~888ðíª•<ÚåÑté^ye:?þ¸Þþù§Ÿ6f¯j©S§Gرc;³ÞžÅ¤°IÙš÷á‡NÓáú¾•üüX0!à»ï¾Kí4þÐü0zLšÎ·9ý¦S¦NÆ6ÙŠÕj¥T™2 <„ж©Á¦M›ˆŽ‰¡uëÔ€¡I“&¼õ–3ëÖ®IÓÙ¶wï>tîò(?®ÿO?ý˜9sßgÒÄIÔÊ éPNÓg˜_· çuùu5l6«V¬ uë6öÎöíÚ¶åã?âé§ûâää„ÍfÃ`³Ý–ó#€¯/ÓoèƒP®\¹Û~ìê¼|算׿*M}DD¤H ¢[×n̘1¹sçcrÈý%°Y³f¼3{[·n¥dÉ’üïÜ9ZÜ𔀃óä“Oe»úc2™(]º4'OÊpŽS§Otm’“Ó7k²Z­X-–tOoº£ÇŒ¥ïÓ½Ù¶u+ŽNNY¦Ï¨Âõj×¾—Úµï¥GÇyºojÕªe¯ÐçÆÐ!Ciذnnnxxx¤™¶â›å¤$'óè£íã, +V®à±Çº§ ÌÌîîtéò(;wáÕ¯0eÚ¾üâ« ×›Óô7ãn6ãçç—nü¾}û8}æ4‘g#ùæJ“(›Í†ÕjeãÆ ´i‚Ñ11üóÏ?Ùê«‘S&GÇl7!ËÓà@Â]aR΋HáþM$7¿ŸèªTXËÂÓO÷e÷î]|öÙ2Lö ®!‡åÇ€““3mBÛ²êÛ•”ðò¦uë68;»¤+O%¼KP»Dƒæ¬úv}z÷I÷”˜];wrêÔ)ž6Ü>®B… ü¿ƒé–sðàA, ~~þy–‹%K”`äÈQ¼ùæë 12O÷OÅŠ•(Uªÿ;w.ûäºm,Y _ßòéÆG=Âo¿ÿÆÄ‰“¨R¥Š}üÿþ:˸—ƱgÏ4h˜~uëÖåû¾ÇjµÝôÑ¢ÙOoÈÕykÅ7Ë©]«6#G½füûï¿ËÊ+hÓ&”FÀÉٙŋ?`̘±¹æ£¥rîüyû‹Ò†}–¶mÛ¦I_§NÞ}o‹?XHøäIDÇÆR¦T)BCB³hÞ”{#†àé~Og™nýëY] H}Qš·§'üñ;+V|CTt4înnT«V7Þx‹×5ŸÊ+1ÑQüøÓŒýâM§÷èÞƒq/ãܹÿaµÙøøã8ÿÏy¬V+¥K–âá‡NóÓëe•þƒR_”–œ”D·ÇåÕ¯áï_%ÛÛ¾rå |}|iØèÁtÓjÝ[‡Àš¬\ñ c^G‡(W·/¿üœi/O%** ·âÅ©Rµ*Íš5gçŽmÌx5µÁèGóÌÐg MmÎuöl$}ú¤}gEŸ>OÓ§O_"ÏFÒ«WÚò”ú¢´ÇB†QÓ廽OÚ5Ãiûöï§yÓ&Ús"¦-[ ®[7Ãé3®Ìö²’ˆ‰ºDtÔEb¢.ñîÌÉÊ`¹ClÊU寡"qÇ q ›½póðÂÙÙE9¢!×(@E¢ADDDµ! Rˆ„AC†Ò¨q.]º”füÖmÛm×^{ZDDDD¤(±±±¼|Ã[þDDDD¤€Ð-„ü Èr«wÏž¬Y»–­Û¶e˜&..Ž—&L ^ƒ†×oÀ˜±ãˆŽŽ ]ÇN,ûìó4éGMø”)ìÝû3O<Õ“àú hÜ´ïÍ™£$’¯¯3·v&Ò áN E½>¨AƒŽ…ür­Lý\¨î T®\™'ïAXx8qqq7M3mútNž<ŲO>æ‹Ï–ñï¿ÿ2jôBÚ´æ‡õמלœœÌ¦Í[h €k1Wz÷êÅê•+xó×™¿`!þù§J–ˆˆˆH^D‹’/ª!))‰Ï=GJŠ…7gÍJ7=**Š+W6qÕªV¥Š¿?S'Ofó–-œ9s†¶¡¡ìÞ³‡ËQQìØ¹WêpOPmZ·¢\¹rÔ¿ÿ~ªsôØ1•"Q€Ùl6ÜÜÜxiì‹|òé2~ýõ†ëÞ´q&2€jU«ÚÇùø”ÃÓÓ“cÇ©âïO6lØÀë$$¤ýUé»÷ì¡ï€<Ô²%÷7lÄo¿ýNbb¢J‘ˆˆÜ"ýt*:$?…°ZhH4jÄø°0¬V«}|Jr 6[ú7VÚ¬VœhÊë×c±Xذq£½yQdd$ æÞÚµ™óî»|óõWÔP Éï5„ÕDD²>]w ,´ïA˜4qgΜaÉÒ¥öq~•ý0 :|Ø>.òìY¢¢£ñ«ìg¶ïØÁÎ]»prrâ¾:u8pð %Jx3|Ø0jÔ¨¯ÿþ÷Ÿ •ˆˆˆˆ"äB¥Ð+VdÐÀlß¾Ã>ÎÃl¦SÇLž:•#GAؤI´lÑ_*Uªˆ¿¿?³ÞžMhHˆ½‰’.üËÏûöqö¯¿xeÆ«ö§‰ˆˆˆˆ(@(öïåÊiÆ…‡…ÄS={ѵ{<=½˜6uJš4mCCø¿ß~£mHû¸ûêÔ¡Wϧ<ôºt톇§=º?¦$""""…Š©°|‘ysÞO7ÎÑÑ‘ukV§çìì̤‰˜4qB¦ÅÀþýÓ=j£GR©É j^”/•"""""r•I¡›ˆ^†¦ÕcŒän—W›²AtŠ–»´3® ºƒ """""v DDDDDÄΤ»;"""wŸI‘+ð’o邈ˆˆˆˆ(@‘»@w ˆˆˆˆˆˆ‘´t÷@‚ˆˆˆˆˆ(@"""rkÔþBT¼E‚ˆˆˆˆˆ(@«¬V+m;t¤Eë6ÄÆÆ*CDDD¤ðÑ݃ÅTX¾H—®Ýøý?pssãÇï¿ÃËË €£ÇŽÑ¾c':¶oÏÌ×^½£ÛÔ¶}"ŽgÅ×_Q³fMûø7ߚż öì3<;t(M7&)1—l/;::š—~D—Îññ)§’,""wMDD‡þü3Ûé›4mŠÙlÎVÚ¨¨Ëlݺ%Û˨HUÿ*Ú)" ®‰‰‰á½9s˜ðÒKw}[ZµjIļùü´qcšá§iÝ€q/ŽÉñ²¿ÿáÞyï=5j¨AD¤0¶¸Ý{÷îeîÜ÷³¾F@@¶„óçÏóÚÌײ½ì!C†*@¹E…ª‰‘››®®®,ûìsNž:•aºK—.1fì8ê7lDàzô8ˆ'OЮcGƒøå×_íéŸõA|öùlݶ>}ûÑðÁÆÜß°ƒ† åÌ™3éÖÓ¦Uk6nÜdɱcøW®LÕª©'°:Áõ "11Ñþù¹#XúÑÇ4iÖœàú =æEâããSŒ xã­Y<ñTOƒØºm[¶¶íСCtêÜ…Zuî£ï€,Yº”€À ¾üê+{ÞŒó"÷7lD£ÆM=æE.^¼¨#EDDDrùJ  Wv\~rÉÑÑ‘~O?MJJ o¾õV†éä,=D IDATFÃÊU«(îVœJ+²uÛ6zõîÃå¨(Ú…¶M­„ÿ”ú+JJ [·mÃÁÁ6©þˆãÇùõÀ*V¬@ñâÅÙ´y3cÆŽK·žÀÀš”÷õå÷?þàü?ÿ¤.wãFZ·n•éwÙ¸i33ßx_,–V­^Íâ%ðç¡CXRR¨]»6|ooï,·Íf³1ò…Ñ:|˜Ò¥Kó÷ßóÚÌ×Ó¬÷™ç†³jõjJx{SºtiV­^ÍÈFëH‘‚{QºCç‘Âr­,Twâããé×÷iJ•,É÷?¬ç×_`¸áŠøÈ¶mßNÙ²eY·z5+¿YNË-8ÿÏ?¬\¹Šöí®R„={÷̓<€§§'OôèÁÎmÛøbÙ2Ö·WW<ˆÅbI·M-[¶Äf³±yóæÔŠÿ•» Wï.d$))‰)áá|þé§¼4v,û?ž:”êÕ;f4‹Ì'(00Ëm;xðÿˆ8~œ2eʰfÕJV¯\Iýûï·¯óСCüüóÏT¯ÎÚÕß²rù×4kÚ”;w©HDDDr^ù”§P +VŒgŸy€¯½†ƒÉ!MšãÇPçÞ{í‚hÔ€#GR±bEî âø‰?q‚ W~ñ¿8¬\µŠ®ÝãÞºÁÜsoâã°X,ö&B×ksåNÁÆM›‰‰‰aïÏ?SÞ×—ÀÀšY~ŸÆÀ¿re µEV2Û¶ÓWš]ýîƒààºöy=j¢jÞS‹€À 6oIívú&M¨DDDDDBЭë£T©R…_~ý•-[·¦™–œœ ¤6·¹êêÓ7nbó–­8;;Ó²E <È„°I$''³ò›åúý7<2éhu_:”.UŠ]»w³mûvRRRhÕªU¶¾‡ÙÝ=5ø6fo7ewÛ †ká¼ÉÁt]>¤fDéÒ¥iüàƒi777-"""’}º{P`™ ã—rpp`ôÈ‘ ~ææÍ_fZ58p𠸸¸°cçNªøûÐ64”Wg¾ÎÖmÛ8}ú4mZ·¢xñâüyè0µî¹¿J•8v,‚ËQQ-[´àÓÏ>ã“O—×î*ÜÒŽ»R±¿þ®BVÛæëëÀ¯Ø¿ûÿ;hŸ^½Z5ÜŠç½wfÛï°\ºtÉÞ¼JD¤(8v,õŽªƒÉDe¿Ê¹ZFRrNŽN9ž/9)G'Gí„"$9%G“ã›àäɤ\éÏXµj5í¹1@(œáÝC5çþûë±wïÏiÆW¯VÆ>ȶíÛiÛ¾îîî:|˜’%KÐù‘N”)S†àà`öìÝ @»¶×šùùU`ÝwßsúL$x˜Í™ ­ZµäÓÏ>cïÏ?SºT)êÜ{ï-¿ *°s×.&L £J•*<õäYn[{ï¥|ùòDFFÒ®c'Š+FÄ•&W5kÖäFرs'-[·¡råÊüwñ"±±±lúéG-"Rdôëß///V|³*Ç•¶Å‹?ÀÓÃÇë‘£y#NçåiSX´h±vB2hÈ ÂÆOÄ/‡ÁèŠ+¸té"O?Ý7M‹€ìñüpûS 7oÚª iê7)3&Msš«fÏz‹îu#6.Ž'OÒ°A>X¸Ð~— }Û¶X,Š+F³¦MíãÔ¯OŸÞ½quuåìÙ³<3dO÷é“év4¨_Ol6-[¶¼é6åød2puêÜËå¨(ŽŸ8»»{–Ûf4ysæLªT©Âùóçñõõ¥mhè•i©}5Þy{OôH½ ýòë¯ÄÇÅÑ©c)""ÙpêÔIÌ'Ÿ|ŒÕ–ý7X­V¾üòs @DD„2²ˆ‰8z”ƒðåW_¦i‹%…?þˆ!Cqêô©üõ¥Ô¼¨@+4MŒ–õeºq÷qè÷ßÒ/^¼8SÂÙžáòïÑÇ{t¿é´q/ŽI÷r³!ƒe¸,vïÜ‘áô_÷ýœéçàºu9üÇïiÆ•÷õåóO?Íñ¶U¯^µß¦þf³ÙxüɧR—WÞH}—Ĥ°‰L ›¨£CD$›l6Ë¿YÎÜysHºÉ+2sþŸó¼2ãößpî—¢%)1‘wßÍÎ];7v¥J–Êö¼GŽaÀÀþ 4„λ(3E‚äL“æÍ)S¦ Þ^^\¸pã'NP±bEî­][™#"’ þ½ÀŒ¯°wïžÏ»aÃO¼ñÖÄDG+#€}?ïåé>½õÂjþP¶çKLH`ÖÛo±s×NÆŽ‡·—·2Srͨ,(ZZ¶x˜èèh~ùõW¢££iݪ% çͳwH‘ìÛ¼e3O÷é•ãà &:ŠiÓ¦0yJ¸‚I':&†ðð0¦MŸFl\lŽæÝ½{}úôbëÖ-wï ¨yQ§;EÌŒéÓ• ""·(6.–wÞy›uëÖåxÞ_üÂËÓ_柿ÿVFJ¦Öÿð=üÊ„—&PûÞ:ÙžïòåËL˜8ž¶mÛ2lØpйSfJ΃¢<)¤rr~3ÒúL®Ž´<ÈË/Oåܹs9š7))‰>XÄgŸ-ËQGÔ‚˜Gw|þÛ½¼»èï¿ÿføóÃéÑãqúöíggSv¬]»–_~ý• ã'rÏ=÷‰ü’\.W¯W®‡jb$""’ ÉÉÉ,X0ŸáÇe/8¸Î‰'2dË–}Zø‚¹í¬V+Ÿ~ú ƒ† âÔ‰9š÷ýÅsÏ=ËÂ… ìï=ÉŠ‘,œ:y’!CóñÇaµZsT±ûòË/0°?ÇŽSFÊ-‰8z”þƒðåW_åðq¨>úh)C‡æôéÓÊHÉ’ú ˆˆˆdâ›åËyîû9~|ilL,£^¥Ç—JžJJLäÝwÞf×ÎæhÞÇÓ@?†ªŒ”¬5<‘ÂÊ‹´:'Ê %ã:ëÔÑOn_ÁÌõ¬6•KIwýK;¨‰‘ˆˆH&éÜ™… Q½zõÍWÜ­8¯Ï|g‡ ÇÑÉI)yÂÉÙ™aÆóúÌ×)^¼xŽæ `á‚Et~äe¤dJ‚ˆˆH*UªÄûïÏ¥gÏ^Ù¿tFºuíÊÂù ©Zµª2RnIÕêÕY8o]»vÍÑÝ)zõêÍܹó©X±¢2R ˆˆˆäGGGú÷ÀìÙïR¶\¹ÍëW¹2sæÌãñÇŸP³#ÉyeÍhäÉ'Ÿbîûs©T¹rŽæ-çãÃìÙïÒ¯_ÿ·¢ADDD²©V­Z|°h1mÛ¶ÍÑ|NNN <„·gͦt™2ÊHÉ–2eÊðö¬Ù 8(Gï@h×¶-‹?X’ù;D ˆˆˆÜºâÅ‹óâ‹ã˜:õeÌfsŽæ½·N/ZB«V­•‘’©6!!,^ü!µï½7Góyxx0mêËŒyq®®®ÊHQ€´ åÇŸ~º+ëîúXwvíÞ­ "r4mÚ”ÅK–rÿýõs4Ÿ›»&LdÒ¤pÜÜÝ•‘’†ÙÝðÉSxiÜøwDnР!K–|D“¦M•‘r Bú'Ýý! J£ÆM¸téRšñ[·m#´]û|¿#òjû?_ö) 4à£?aÙgŸ«”KÑu‡ÏCR4•,Q‚™3_gøðçqrvÎѼ?Ü‚Å,¡np=e¤P¯Þý,^²”‡š?”£ùœ]\x~ÄH^{m&ÞÞ^ÊH¹¥ke¡ºƒË˯Ì(ÒÛïàà`ÿÿÎ]»TèEDîÄõÕ` K—.,œ¿j9|jéÒ¥yãõ7xöÙçŠäãPm*>@êãKŸ6œ×_ƒ’%KæhÞêÕ«³pÁ"éÜY)yÂXHn лgOÖ¬]ËÖmÛ2LÇK&P¯AC‚ë7`ÌØqDGGЮc§t¿¸=šð)SØ»÷gžxª'Áõиi3Þ›3'ËmÊÉ<ÙÙþ:ÁõØ·¿ýsTTAœ;w¸Ö¼iÜøñü´aáS¦ļ TÚE?ŠdãÜ£›r+*ùù1çý¹<ùäS8äôq¨Ýº±`þBªT©R 5ɽ*Õª±`Þºåøñ¥&zöìÅœ9óôøRÉ“cøêõ°P½I¹råÊ<ñxÂÂÃY³jÅŠK—fÚôéœ>üûß™nCnæÉlûŠ/FLLŒýóÑcï\£«Õ¢R."""" *V¬È ؾ}‡}œ‡ÙL§Ž˜>TªTf½=›Ð{.\ø—Ÿ÷íãì_ñÊŒWíO?ÊHnæÉlûj֨ɇ}DäÙ³üùçŸÌ~÷Œ×_·M›·pþŸ¸xñ¢J»ˆˆˆˆÝ``ÿþøßÐÁ,<,Œ À žêÙ‹®Ý{àééÅ´©SÒ¤iÂÿýömCÚØÇÝW§½z>Åà¡ÏÐ¥k7<<=èÑý±Lן›y²Úþ—Æ%>.žv:2|äH €‡Ù|Óù‡ Ĺsçx¸e+¾Y±R¥]DDDD²dxqæÒ|÷Ž’±:e8mßþý4oÚD{NDØ´e+Áuëf8ýÍ%k²½¬¤Ä¢£.sù"ÑQ—xçÕ0e°Ü¶øæ°ˆˆc:ôg¶Ó7iÒ³ÙãZe$“çqDEE±uËæl/; f U à»$Dî–ç_zwOÜ=¼q7{âìâZ¸žb$"""w^•*U©R¥êmY¶Ùl¦]ûÊd‘;Ȩ,‘«Lz“¡ˆ^†\¤7 ó¢ˆˆ½ëåµÁ¤Ë ˆ(ÒPDDDDDnš‰ˆˆˆˆˆ‘üFMñED‚ˆˆˆˆˆä+&=ÏOD -C.Òêœ(rÇÙtè‰ÜýkåuÏ9Õ±Ó›”E¤ËÍ«ÒtNÉý¹T æþM{!_>æôÕ…ßæ(½Ñ&“IûWDDDDäø&FÀÁÁA{RDDDä¶Ô´¤¨1ôÝîà`ÄhPá‘[¿ª‘‘ˆˆÂƒ¢~ý3PÀï  à`T?k)ôÉ/u삼ñjZ$"""r{tÿ@BÛp5-‘BB7DD­D´0u÷@DDD’ˆä5SRRBÁÛh#6k²öžˆä™¤¤’“HIN$%%›Mµ‘;è°Ë7 6í"!99‰¤äD’’IJJ˜b¢.¨/a4‚ƒîˆHž‰ÄÅDC||¬¹ 5d•9ÔtXåQ¸X0`'ggL1Q Ô—pppP—ÉsÉIIÄÇEMB\,6«U™"w¶>¦,À Ji>Ù*EI||,£0`³Zqtr.XwŒF#FE"r¤$'‘G|\, ñ±Xm DÂÏ]ãóCl ²XÄ$ÄÅ‚ lV+VK &G' gþ:o+0V')¬•cUÊÔÂ(ßÔ¹´#Š:“FQp öAY ""rwåå݃'våí­ˆOªÂ(RÔ•"""""¢ADDDDìÔ¼Hìe!òçÕ-JDDä.Rå+•ÕO H¾ ;"""""b§NÊ"""wQ³fM2¾iÓÖ>Ô ®GÇŽˆ8vŒÆMšÞö|Û¼ykžï«%‹³xÉ·eÙw›îä¦ÍßÒü[6oS€ """·WJJ2³g¿Í†x¬Ûc„…OÆ×ǀȳgÙ±}Ï=÷,-nÁ°aÏa29æz=sç¼ÏO~⡇fÌ‹ã¨ìçÀñˆ~XÿC† ".>ž/>ÿ OOÏ•Wƒ‘ÛUÉ¿ÕàB‚ˆˆH–Õ¯ß×kþÿíÝyTg¾Æño/,Q–¨H+Š¢€Š¢9G£ PÍh„¸D¢¸œ$*¸;1*h’ÉÅ,GÁ3Žˆ¹qAÆeDÄÜp½æ£‰JâAÜ¢†E°ï,‚,v³¨˜ß眜ÓtU½oUWuû>©÷}kè£uõ½›PRò€ æ£T©Øº%KKMµåv¶vØÙÚ1r¤‘Q‘,\¸€uëÖ늋‹Xºt …lÛ¶33³jËz÷Á¡wT*÷ïßãÊåK˜˜´®Y½Šššz¬ÆgYÛ{ª»yëßKæ‰Lòóó151ÁÖÎÿº Ô«¬céi¬\‹¹™9q;vê´Ío¿Ý!(x:yyylür½{÷©wýŒÌ E„Wþ­T*iÕªVVV ìŠÿLLL+—0bÄpzöèÁæÍõŸ»µ‘kHNÞ Óþ´JJȾp[;{ Ôú5ùe ‚Bñ èÓM¥¡ÝŒ6lˆF¡TòÑúk„ƒª,-5|üÑ' P°aC´ÞõlßþwÔj5ŸüõÓjá 6v+±UÖï¼Fxøb¾ýŸo‰¯Ù˜|Çj¿háàî½{Ì™Áî=»qpèMpðÛŒôÅÅ‹¿°`Áûü3á¿u*çáÇ|þÅg,Y²˜Â‚½öaÍÚÕܾ}[ï}ïãèH@À›Œ?žÁƒsûömbcc˜1swîÔ,ÏØØ˜ŸÎã|yC¹6¿üNJÊ _¬ß—´£džÈ$óD&©iGõÞ^î !„/ ìŸ³ù÷‘³uK ¦R(„‡G<±¯ÿIç1 7nä³gÏnþö·­Õî”…ƒ˜Ê¿ƒ*_ûû¿ÉÌ™Á à¢oØzêwnܤ8>‘‡gË J{ Æ¡h×F§ò‹‹Š¹ž]¯}joÑC£†7f:ȵÜ\æÌ™K@À›eçþLž2‰¸qŒ÷F½eäßÈgÅŠeœ9s†ÐÐ0ö'í£°¨X§úããã8qâcÆŽå›ÄD½öÝÅÙ…éU®£’ÒV}ðRަ°#n³gÍ©¶þ W‘r4…ää$ìCÿ\k™‡¢°°wwwÒÒÒZÜ÷\—ðzåòåÊõª~~„BˆçPjê±f›â4iß^ü'ø×¸s°xQ8¡aóèØÑªÆ6K þüIÚ·—ÐÐy:Õ·ãŒ퇅Eû:ÃAÅëŠ`ffưaÃÙ·ƒyóÞ}®ÎIEÈHM=VoãK{ã…KV£zÅ£5Ë ´„Òôo)Z…ÑÊpB‚J­"66†ƒè´oÞÞ#ˆˆXÔ¨ã»sû}ûö«ö~›6m±²²âòåË•ïݼu“˜š˜ðÅ—›062®\öÛÝ{¬‹Z³³ ‰ :ÕýãO?²é«ML›ÈK/½Ôès¥V©y{ÆLRަðý÷§j,·°hG¿W^áàÌš5CÃZ¾'{°³µÅƦ[‹ u5ø9Ì•òsiÝ¥ Ã<‡éU®t1B!žmÙƒúþk¨ÿÍÊÂÕ­æöÇ33-p*±±[)..ª±|°«YYY:×ó]VNÎÎu†ƒª!¡jw#¡œ¾>u¨TO\ÿñåKJ162jÐ1ér—ÀÃcHó?¯ ´e/{ŒBgT†E'+½‹©/$4e8¨0þÚ[´gkÌ222puueÕªÕ¨Uš†F†F„‡/jt}ßìN$åh ­ÿ‹¶í} —”–pýúuNdgã¦ãÿÆ„ÛiµZŒŒñ|Í“ä%“——[Ù宨¸ˆCâ>Ä3S3yœ¹!„âùáQG7¢ÔFÞYè`©ájNvÝmk,342bÒÄILš4#C£jË®æäè5p¸ƒ¦ç/œc@'½öO«PЀSƒïIÿæîj¤t°§4ý[Ô£½«…•£Jû& ͈ߵÛîÝñôÆÑ”#ddd°lÙÂFðòËmš¬®ìŸ³ùüóϘĢ5‹—.o’c)=}úS;oã(ZUÖ¸wuAÑÉ •£¥ßŸÆxõÒ•Y5$Ív“ȧŸþ•7Æ¿Áܹ¡¨T*Þš2•ƒ°n}ï¾;¯6oÁ Ì«ª°¨•+–Ó³gO‚ƒÞnty}úàèà€¡æææØÛÙÓ¿ÿ€'vwêëØëÎÖìߟÄÔ·¦¡P(HÚ»+++ú÷ÐòCÒŽ¢Ð–Ýgù5çªÞ’% !„£ýÆö#GúT»#°vmTÛ\ËËe××»ˆŽþLçzޜȬøùÁ²½e­ëN äë+°7çÑ{e¯LêÒøcmÊ`PõÎÄôÀ :g2R´kƒÑÊpÄ'TŽ9P:Øc¼z©ÎÓœ>)$4u8ˆ‹Û¥FÜòpPÁÛ{¹yylÞ¼‰ÌÌã¸ñht]YYßqéò%¸ ¯yÖ^Þ¬Ù!¬Y½77÷zË{Õe ÎÓt>Îg”/_}µ‘“ÿw’ §¾?EpðÛU¦V´ÈïyÅõ9eÊ[@Ù3Iê›}K¦9B!žgå]ž«v1jÊ» ¶ÝºãåéEdT$Ÿ|ôÉŸ… Õj‰ŠŠÄËÓ [ŸÐQÓqãÆ“ðOf…Ì®óXfŸ¬ÙÇûX>5‚žOSnNÁ£¾`8'¸ÉëmŽ`P!ÿÆ zô艺–:¬­­¸QeÆ¢ÆÐtèÀر¯×ºìÜ…óœýᆸ»Ó¶M[4;6ë¹éãÖ-›INÚG{¥R‰Ï¨ÿ32=0ˆÃG“–Z6EkC¦4•€ „Bz¨ xïÝ÷ûúŸHÚ·—Uü¥ò9K NNNDG¦W·¢Ú´iÓ†åËVÐÕÚšÄÝßðᇫøOî5´Z-ÎNάøàC@Qe2L°nÄç÷”ˆö¢…„òÅç_²sgéi©Ü¼u sss¬»taùò•Õžн[74 ¦&&XXX´øïœ««æææ(•J\]›ïúÑ÷îÁóHñëòdâW!„âi“}ëiÈGм¯|À¢~Jù„B BÒ—„B!$!A!„B!A!„âNîÝÈ,FB!„´ÓÄ岓kOè@î !„BH0¢ÒÿµV]€+ó‰IEND®B`‚goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/images/screenshot-ubuntu-19.10-01.png000066400000000000000000003000741414415210700300310ustar00rootroot00000000000000‰PNG  IHDRrïIÜT”%µzTXtRaw profile type exifxÚ­œg–¹Ž…ÿs³Ð-‡öœÙÁ,¾Ë,ÙV÷{c¤–ª”•AÀ5 £Ýù¯ÿ¼î?øÕZmÎrm¥—âùeÝz|Óüç×çkðöþ~¿jüú.üúº;÷ë[½!ñ5}~PÎçk¼žº}½>}ÝÕõù&¶¯ }ý€ ¿_IwÖ÷_ïk_Jñózøú·ë_ŸöÓt¾þÄõ}Ÿkÿöo«,ÆÎ\/EO ÉówÓ]ÒçÏx_õwæM!¾O|ü‰^;7ï×H~[¼ïßý¶v~|½ž~] çË×ÊokôõzÈ¿½ž¾ß&þ2¢ðãοü ×øcl¿­Ý½»Ý{>³VX©â¾&õm*ï;Þ8YÊô>Vø]ù“ù¾¾ßß)."¶‰æä÷r¡‡Èjß`a‡n8ïë ‹!Z<‘ Œ1®˜Þk-ÕØãJ éw¸±¦ž¶Kø,¢–x9~Kx÷íï~+4î¼¹¿þvzñóûû…îUê†àÛgH Æ•5 C‘Óß¼‹€„ûµ¦ù­ïûíü¯±ùØDó[æÆ×}—˜9üÈ­ôâœx_öæü§4BÝ_`‰¸wf0!_BÊ¡Ê>ÖXÇF|#Éâ$!縃»Ä&¥BpZÔ½ùL ï½1ÇÏË@ ÈJ%4= ‚e–ÉŸj9es9ç’kn¹çQR±’K)µ£FMÕj®¥ÖÚj¯£¥f-·޵ÖÛè±' ,÷Ò«ë­÷>7\zðéÁ;Ƙq¦i3Ï2ël³Ï±HŸe+¯²êj«¯±ãN›òßeW·Ûî{œpH¥c'Ÿrêi§Ÿqɵ›®Ý|Ë­·Ý~Ç÷¨…¯²ý%já·ÈýsÔÂWÔ1{ï«?¢ÆËµ~»DœdÅŒˆE D¼*$tTÌ| fQ‘SÌ|EŽD-dgEŒÚ 1ßð=v?"÷qsÙþGq‹9§ÐýDÎ)t_‘ûkÜþµ=£¤ U¡ÖÔ§ °Ý°4˜ ´ßsëéVû¦<á°ymÔÂÄãš3læWÌ1N«õŽS˜hÔ·ciÂûäå7ë?ˆ¥Žxl pÏ©ƒ5Üv-ÌÊ×d­]ne«—;ÀÙV 8É Ö™`þ*¨nÒ"1êâø¸IËy'`f·ù|  /wâ ʲÆ$k#™â¨-Ý|\_ÄÐ:DȨÕ¹KçÆ9ýV…Æ»ïÙû²®Ä7Er°ÏÅPúfqâ´]aK×:59WÙ…ôR;Äɺ±3ÙC¶é+‹dÛ(·ÅµóžE·]¥Íš UiɚׄZ×gô€¤õ9d|S•Ô¥“|™ ±4á»[I]óe™ËAþÅ3½[ð|,u„Uà-¬ÝÓ:‹³4•xÏA`AO«’¼cøž2A¦ño£â†£¦)Ðxr'ïìuüÞ2?ˆÆ¢–• ¸»cûéLèSê1“Â÷ÇÇ)°¦RDƒÌ(á·kq¡óo\ëkÄŸ«ýñZî3°ÿÛ¸²éÆÅÀ/`ȉãj¢ó¤€Z\ÛƒWkgRÙʆÈwY¥ÜKÚÁR† A’ü½Þ‡éƒýpó3u©Ý0b1ƒD&ü‰L°ÏI‰Å0SÞ§äØÎ!·î6°@CâШiÒš„TZ¯Œ¡ˆŠ?s¦zò«ã»æM’¬è®N°vupö…±56¬À= €¹Ôœeá~Ën¢òj†e¨Û0¸æ:Û‘ÿ-”ÌRû¾àG™ ²K,p€V¬×[Q«™_³¼QB;²x”񣀾Š|ïfL «Õe }nç¤[Xœ–ßàêÁ¡›‰æî9êûuKsŸ™Œ3ÏM >á“G°Ç¸à‘±YÚ!ˆVïVn¨Ü½S‚o}±b°Àní°Ö"†ÖÙßnü㶺é÷Õ#—ìHJ7·Ã‚¬s`p=ר3m…ŸQ±àƒ}6-òQh8{Û,deò¾û>„S’S q#%P} D¦2 ªùä©F&%+…O”Ç™òßóÓûÜçí¯“’ÑmÿþÄÝÏ3?FÉ­Ìä2xF —_`?3eéö–W…S>°ÝësAKM¹¼cüL`M*5›{e[FÞíU/žOã׿±ê°ß† î Õƒ@ºø…R/Œ¤ñЦ6T‚%–ýÖqÐ'³ÑÏr½!êŸù+m•ç 3’£Ëgæ‰êØ-®œ»]Õ Ï-©œ¦ñ]5ß?Ä:m•Ý ÷µ¢ÃÈb_^'X„{{ `±_àeÁfx‹;Ü~?¨¤’ä@—í¤/”Ñž”J¡Þˆ­ÒûÆ«kRçˆB+uõ¶`ÑŒ`?cÎ5à z*‰bãŠõŸÎšæ/ñ€‡—Á”(dTB~G°‰¸ÜlH¶¡^ÀôÛw þ/Œö¬1x2 îýAZΧ0~¼î©sdX¢i•[Ä©gݰoYP%l6ˆƒÈZB`d¯ÔÚH2Ìàâ¢i¢(»KûñOˆå`ôœlÖŠ¨Y Ñ›7” z!ˆÑ8qó-†¹Ð〤ÄÚEÎPM…ÑtÄÔ Qóè¿ ©nÄ&âˆù€`áI²!½KGlê 5ˆ<Bò9d|B0/xºõ’rýðÚ’¦Kb’u ¨Ž·ûÐ"0] Ô… Ni±Æ06ªã¨ßC*âpUâšÕJ YyäÍYÄ–¡Rv‚ø«9H¦Ã™š ä‹yÀœS·fM‘:¬—Bñ’Ô†-Fú5Ðî}  öñÁÐ7¢ rç:Ðh#E;ù—•³ßaêS!³Q€Œ&kZ[JµPñáå"F—è€ÅÀ]¹”cÂAü!ëð"3© B­QN Žî ¨ˆU©b6?›ôË×0W–°nÃ(å-?2IsCÌNÂõ»µ—º]ͨRÁr„Àðøú;O6`‹#Dð àw®³u"âIÅ}öla>PdL¤ü4Ê&†ˆƒMу— 3xí@¡O/AŸ‰f zÚÈjÆB".+‹Oþ¢-–å`/"g*‡² ¾9Ȇ0ÆEJ` œ/T‡)§í‡ i.‹B¥«þ 7oqwnC[+C‰­ÒŸÃc…Ð zyNÅH‚ûä’ƒÛe9Yý«†ß3vì°%Yx¨4–é"`\,¬¾È.êPÁÀ•ÂNv%Ÿz'¼ÊA’º‰Ý¸£ñ–ËKKlI”Akª9Ve£d¨ˆ‘ Ö€S¼A l}uß¾AÀ1 4m°‰¿"Q]ƒœ}@ǘ¡ÙHFÖ·™³•#ìUõdÈ,]øez>@Ð{Fï£ó©YÿÞÍüEéIü¹ÀC‡lZs¡/Xfô«JÑ`ÕDéå ÆEr’µã˲ N.1ùÛX‘G™sNv ‹#~$Ct•€»‰!¢ öF P²H¤ž±øÛOX¹µY\çÚ+-  OÉ$l‚>kÀVG†éOHÙyÜè"ô¡¯Œ«&¤¥cx ´€] +dŠÛ]|¹@p´ Ù¡¿Ò½´ªî…ùîŠrº¾&´j &GÆÔX ?ŠñqIäA%ã$gC3!2 ÎÜ*؈€BhŒMM͆\ñµ¯–3ELtÈGdyBÊ1íHÑ¡Žg¤&8€ Rn(쪾„è¾e5…u¥Ñ¼m…n¢gÎÈŸ#¿ ^¼n÷QRÏË'SJ,´£¾ в‰ ߯”¢* Å ÄÁþ¨$&8"˜¾Åb˜$p@]¡Ò‚ØG(+¢> ifA©¥… ývCb”×»ÀwvŸ:5›ûGP–U;¢ABäÇUBï$é’$ ¬–4ø¶µJõr{Џ$ɼ­~+*…â¡Ø›ó‚+2é"’ÈgrézŠ‚Œ4h[ ·w 5%“S°–{A¨„TIѩˀ턡Î7Oõ û¡Œ[µYv6‹\Eõ{î¹T‘¤”¾èÐÙ¹ñ‹Ð2óq$ŠR9Jûà[¨bpƒ@£ 'µ ’°„Þ_~ ¶Ì…Ì~Š=.ŸÙ:ÀtZ]X]t‘ý6ŸQûŽ‚_ (¹®ECSɺɞ90hõÈ*>Sq Wš¼.À:G¾T°Hk†[&åÕ*U´2Rm#âÈZ1핈À·™]k¼M¹ ­ ·@K2ª'ààÖ­»„L‚ pªZ­¹Õ¶([eîQ ™L%Û-úü‹ h(ÐHm¡öç'¯Ý ù«ç*l8»Ÿ²%!«Ð Jç¦~!Ò-J+Ö@UGiŠX€Hx6^4ïv¤<ÌÖM&¥ž¢¢¡ÿàä"±aW%Ñ÷k±8°¶ô&{ï;~Ã)ÉÚ@ÎzX¹«‹ˆ ㌲Uw›:WwvuÂe‚\Ï­[ pE „ÁgÖt¨öû5ºïƒc;É>}Þ¯gSäø(:P‘j££`˜ûðA’"AaÁý}ƒ)2|„SÎ0d¤ôE™XƒÜÈ@žÚé(ÏE`rL.1£‚ºš Á1„ö5¢Æo_°m¼b¼„WÛ\øyÌyð™  ™Ö³éÔ÷G¶M”¿<¡°rEZvhò3Õ_îó7¯Žëp¶(­Ö + ŽвüëÂÁ؃óI:ÅÈÒ$–¿«žÍÚ‚Z¤zÎãÍr†;€ &&ŵM…ÈÊÈðÁA^4! B™½~¥¦ûä¦'ÎÐ5¤@–`¯¢Ý Ÿ(aœJ0Ø&Ï…hBÂ>ms8)C¼°ao®,'xg º’šÅ&A0aÃKô£Œ‹–Y¡òÞ@ë;rmî BŠÐ1ê /Ä3c˜ ‰ „ëƒ9— ‘‚‡ñHB«rjxÀ)ã _áj†,=T™B1Oév±–áj¥¢†¡îsË·•ƒ”aAÉ'tGGv|KxsˆVAo,šrGéW»ŠÀNƒ„gbâ´õ¤F0NÓWÇ#AËpP ³Ï„¯„*Ѧç$5òeµŸiËðãˆÁ=`µ:ï†FMªðAg ¡pG”(ßÉ*º+‰¢èJëàüðæô@CWí´æz2K‹‚ÏÔ™tità|:îkçâ&ìÒ/0{‘aEõ c>¸Pd?tÁ‰…EÁ™ö­ù\@d¹}å®&Þ÷_~GöéD|zKóÊ÷«v›4vŸî- q@Y bº`ú¯…œ´OÁÌ‘”2%±I¶éL[°«öŒ$¹”éSüc6°x"5f/Ÿí*Ž;æ`ÉÇE¦n>€Ì"œ Ì‹Êø¾q‹­ÑŒ³T#”ù$N¸+H8rnØŠí"ÕÐ`š:SÈc ™Â ãÉÍ‘}ƒÚD½«Ç‚×V-Þ©&/?™±GhÈ¡n׬9‚%8ò-µ«"D¬®¬ ÅGMú¡—¼n¢š B5ÈgTu¥]o@m^í ºkzçBòJ)bþA¬žrÓn%D%vR>½Æ¹8À… Zné ¯Á1O6Mpµ&4O¼1#n'ãºeS‘j5YjEMH5À`¼ìaE"Ê´Sº@Ê3]äTsˆà|†/à¹÷^CÇb1cL½g ÞÞ†ªJ®oã­@1cÖ›õcÑþÖô˜=Ô÷ò8×yà ` CåÈcÔ¸‚âD‰.µ`Z`wŽÊ>2èUõ€'—·E4‹4˰Èî:Z03Úo"‹¼¡#*w ·‘ƒÃëgEÞc¤#òòÐ5oV p#;ç xøŽÝÜ™žÉÔÝÜ q(¾ÁãA<²‘_Ç/ZŒ¡ƒúÚ] ØŠµ’{í¶Óeü I‚¨í$9ê)´ÈzOgÔ;³Æ¬'¢=Âøä²0ߟCý8|^&áÃ䈎&èìEPËÐÔȃ8\˜ÅA0擒Λ¡¢…ŠaÈUŸ¾–Wö ™ŽBlÔ9l 6“lU¯H»QÊ$:á VQO1¨7Jt ãDÂÍðW”vë^Œ…k' hÔF‡þ…—š+:ÕÆOz@T 5ÇkÅ9Ð"ü$%[ðß4f ï×ÈPG—`Po$a£¼[ÇB¿~ .µé´²3òv ¬\ø95I…+†â¦8ÕEÕi×S0‚ú[”-ƒEA`6,„\ÆUïC~`‚tñ¾-‚DäXÓ´ $1¥®ÝwÖüí5 .÷p9DêB¢mN0W[*ýQèüŸ’:4Ld˜ÛHÕy–ª 6â;ƒä“Ãí_ЦchåAp§‘ êùµ ÍDÉîwÞ¤a‡ÙÃl DŒ88æHò».ÿ$53ÃÑ.E(3SûS]n%€1 Vp ¢$ãªlCx\>Ë1Œu|Ý÷6âo¶ãoÁÌ]=ŒŠ|?€)Œ:A{QEx®ãtBè5VYÿ†¢¡(R2jc¦$©êú…ŒZ1íï  +>h€U’fÕ¯]¥êæö½R"ÕY)jZt´kŽØ©54¬CÞ:ë5R²¿k+>F2œ¹!| vbÆ·zkè<ÏÉ-1aÑÀ£®q ô, @Ï›4ZE¬+©\1â¤L!ÁyÛÅÁƒÖÔ~†¸±Y“h÷QùŠ8 s—\êm!Zl*oDÓÃZA=bX,ÌJLÆeîÅm¯F&ë(𻥷_̕޾˜Im²q¡”{‹òZ§—R (d¼Cá"fTK‚ ÁGå!š6¡£+)ƒŸH¼¤Ü@ÔÐê ^¢)¡³ tþ©ƒÔËÛܨ¬Š{‡$Áª^çñNðŸÑ ®J]&5ÐÆ7%Ý/kDrv™À®:ñ!è¨.Š7"bÜ«*jÚ÷µ÷²p*K£M'r¯¾í+ìÙª«7Ë ¨x×Ó;àBÊCïM&~¤·¿2½"›SÑ9AÛ¸—¤n þ ªp‹’ÏNÔ•úÚkÆäåˆÌ4'&I·þPAž ^@g²tàP§)`«Ÿª ¸uj/Cç||;²é²„ºÝs÷@¹oý”Iªó›µ9RW›_Jª;bì cCÃQ Ú‹@Koßþ2Ä>î€õe…‹R_Іw ŠÒAî_õ! Rõ¼¶ÊFO³QP¶ÜV]`0ñ\ØsødëÔ}Ðv—©äð<]‡õ@.¤Û;ÍNk$ä•àE‚˜(GÀ=U«)Çš^WÓ(„´W©HPvóÔnG ’%µ•¼ôaE ³ëpÖÈŽ¥”8Bx É;oFã^“s*=Ÿ)ëÁTæŽrˆ”ØH” ö¨žâÀ¯_÷T;^tÐò—½Ê3.qS$oôE¼ô`Hè…—ŠÎT3.¢DÑ^Ó‰ZpFí²Ï0î4u™t~Çaâ:=P1‰ŠkÔ£˜üŽÀ÷¥9" Øf®IÇßPEò;‘¬³šŠ¸¶H/E£ µp!éD Y%’o¦è |¸eéäLX˜Z¤ÁæµÞØ H¹‰²¼pj¡’3ô² Ï#|[hbŒè­NF°":‡j˜1â…Üж3Ì—tNž(z†ÌÚʦˆaòÑ_žÝ›PlÚžWW4òu}гMxðÎSìÌÕ_¾:=:tÔ?BGe„Æ”¾™  <º þÁ¢V$ý;bá•ø:Æ$ƒe—Óï#<ÔòúÔÄ;34ò†<¯Ú<k¤Vxç´ƒ…ÚÆD¨Á.< ôçÐ;1²¥†Rt¦9'í’²@@:t«­©öŸ!(j*ëÖÅŠfÅžÏm`ꎴ v¾ó Ž­Ì@’…CQ߬œKæ›e€åÁr° -§Ö¬šÕùµ¯Þ§¤)yµÊÐ6€|hŸ S€©6&Sz raZ!yQµ¢‡$½œ‡ÎpoÌe«è}”^Ò¡¬°£à ‘C(U<;W“ÕÖçjR#09õ´÷Õ²Á€ÁäA‡†¯šDHâø ×¿R»*Å>É8}@ç·²‚º¦ ÚPµz)Î#÷ö’’]åÖÕVLJj'Æe‹Žg °W–tSñx§îtƒÏ¥å«¨”EõbŒÑ®p6¢Žžù8ïáO(Ž\eøHVa&p'Äà :óEžÃkfT¶ªž*Ʊ “u®fXÕ…\˜:ÂùÆÒº¹ztVNeÄcJ@Uhü‹¯ßš9ò¦Œ:&eåu,hL’™A:V08jq1*V=EÕ«®üÆ´¼ç³¾Õ‹`°³«÷Œò \ÈB²Ì±. a4Ô§¯øSü$C3Àšš8AÞ uÂg§¶|Ë$ØÄ íMØÎŽ$*AGÍ%B€Ó‘·ß³«yk^\²ÍÅô€,uëVRt KÂK;ØÁð9rc™ý[˜ñá»úi!.í,¾Ç<À ›ìÚBZ:èf4Uf%Å3JŽ–¢ÌÖS …ÛoÁ«§­g¥¨;  Ž/I篗àÈ«ÿÇ¢'=ìNí8dUÀTTE¤è=ƒ¡|\‚0·x¤ãíwA¾OŒ±ÔBуBv˜‘:þ%¢Ë¥êœ8à/PœeïÈðáx“¸´¹¶!¢¹ÊIé$ùèÜ3Lle,<Æ%…ŘA=°/Æ Õµ†Á£¼±ÂA‡^tàqb†È™Y‚]q"?ô5‹[€¿ªÖ'“Ú[§çP“ˆÑ€_e%¾¨nܶDã·^¡k‰‘ñ)Ô)¦ƒê8­ëXÇä{8ÚŠÓ ífaÁ‘qÙƒyÒÛÒÔÙgŠˆQ³SÏ­ Y+h|ˆ4Ч=aDZ¥ú×Yöyz’@D˜D¤DûPOúî¥À~j+VCðG»Ý(ü·Á6äÁ­xÂÔ‘<¤ U& VÀ÷:‚H„°Rسªy’3ÖSVfˆˆ#}Bu<£âVÿ ]ðø'½v¤ö¡»v³gÑcMŸ#º‹ƒP'“Y²6L‰›ë°·:qãw©E™80êI$94K$‘T¦ôYá©ý2l>bA¶êñóÁ Àk‹ô„ib÷†}(Oé}ÒMg1Y~é€sSGX'r›£è²zŠG‡ÿ[ßpõ‰UÒ‘dkA“lÄ„U„u˜50@7™!lô&÷J³êÛjÂ'ã µ!³¥¹ƒ¶ù‘œhy MP³Dzb"u)ªtv@ÏXiqtÞªE2`I òîT‹­©<À]%®+Ÿ­mn+X(è8ïÖc4L$‰_º Ùã ðŽ­Ád†ç]‰ÑY¾9¼Î‚‰¶0Ø&=Íûm£ƒ×ÊÛÊ¥ð;´«cÔ~¿¹C²ÜÙZß±gDŠS4°(V*žüC·#ÉsÀdu?ïâÚ±`CÑ‘d˜*CŒh¤*× ÍÇ\øîZKÐŽ0 óÖ)â­gTÞ¦ÀXÕ¥Õ1:#¾ôô¥š¹]nW Ðg}ªùr= Ø7¤üÐ#LÀÃ@¨z`×#ÐJ5 Pfu¿MÂà_â~Ò‘â¡FrL;Þw ž^ zÞämŒ –´ U’gx¡Å²A3$.km¢\즖¬tD\ô*¦'ÉTÎ?n=æNÌuÂO+Ͱ!•ôDÕщq#?µ5©Gp‡ï`RÁ¬ôªNèIZž£­²˜2Òß‘T¼äÖ^¯Míßi“¯µ†#É{æÒtu¡ƒ“¹{®¶´c,S: u=r~Èŵ)ª] }’YsŠpŒÆº"Wò·I~ä¨!>uT@¶ð’Ñé+ù‰\"gȇ®çÐ*šc!~iuÞ:ˆ;6…]ß—Ü÷{—Ó۾߶ηˆoüV?£J܉k¡ëgpî§Ñ½Á©oÍUÚœL˜·Ï£s]'ïƱjS 9{Ó<ˆW*1±®Ó¦‹MmIkw”â@³É«¨Ÿ®þgìs½[=lF¡ãxÚÓB•`UÚª&À¥Uבi{«s5tè8¯zÀs­õ‚¼©›T½6ôxeFMBK¯F˜;lú>°¤éáüP”—Ñi6‰%øV;âˆù®3„ްò)i?ˆÌĤ€»rÈ>©ë¡£AOál!•v»²zÙgj Þ3eA¥ï SE²Í3R\‡¶øËógêÚ‚bº;¼sÕ€¦þ×8Ü"µ&Á·^={Ã[ ƒëÊmvà¢h3U-V² ÷6˜u†Ú:±«“¨HpÊzj^Î"q6tĵêÑ6ìÑÒîI~;=¼ ´Hy/= ­ég\ìëßpë0'jõ¬gGuÆŽ7HoéÁM=ÔÜ‘3”ÆÔ¡1­KÖƒ¹“$Ä!-[}Ïõf$·éÎz¦¸5Õšã€ÚÔåÈ »ã½‹œÕÇ…%äb`+ ´À "ÊÞ ½ÞÓÕƒ¬äFèu*¡š‹Lw:vw›ñGVGP Y—·]€œ.¶Í ëÍl£tìlØÜ»šõ=Ò·ô †CD•¢¢âˆ Ž©®Ayî&m’ ;LÙ3ñÊLùŽÔ¹èu’Ñ/+®Q:‘KúéЇîÄ»“§Ì¢N笢áhcHcbîØ>^=É=u"4i_ÍU ˆà†€i=S©Ý+³UYD®!?©™bv˜ËõH¡žz¨Ø Z©Ž\öV`UXÇtj*Fjíí¥·)?CaXm½DYu5ðÚGÄD ©?r€ Ç ?{,:O­­ãwÆÙ¬ÃrMÏjKN"ÐôäpðM¡]ô>“Ó[èD›C§&Úü¸á¬êÚ+Ðó àŠã¿r¼Æ¹× |ûbKGDùC» pHYs.#.#x¥?vtIMEä  °%Ê IDATxÚìw¼ÕÝÆ¿gfvïÞ.½( °‹Š¢Ø1Æߨ¢yŠ]c,ñÕÓÞÄÄäÕD£A%ö˜ÄXcWbÆì¤—Ë…[¶ÎyÿØ6sæœÙEž'"ìÎΜiÏùç×Ä&›l"±°°°°Ø`áØK`aaaa‰Ü¹……………%r Kä&\×eܸqœsÎ9Œ;ÖÞQ ‹Ï¼ qнzõbÏ=÷dŸ}öa¯½ö"‘H0}út|ß·wÔÂÂâs±¡„n²É&ì»ï¾ì»ï¾Œ?ž… òì³Ï2mÚ4¦OŸN6›Ý@Ö@CØñDzËÇäÚ';VûçÛ}û®¸…ß?¼ˆ‚}~-,,Ög"w‡ &0iÒ$öÙg†N¡P`æÌ™<þøã|ôÑG¡í-ZÄ«¯¾Š”ë÷¼$½ œûПØÿ/ûqèu kl» gÜ=…c¦OfßKgpâ=oqÂS‡2éò÷ÉÛçׂõTZñ<©S§2a„Ðçe=|ܸqÚß=ýôÓœvÚiäó5(®y ûžx:DzÛo%™kgÁ{/ñìWðã¿°þ̲²“ÅÎæƒ…«ì“jaa±aùA!ñ2æÍ›ÇW\ÁÓO?ïûŒ?žsÏ=—Ñ£G³Ç{pÀpÿý÷›­Ü^9ëÆk8{äGüûößñÓWÓÓ<á_Ø™íš×3k¾ð&w}ûhî²Ï©……ņFä#FŒ`ñâÅ\uÕU•ÏöÜsO¶ÜrKŽ>úh–/_^ùü‘Gá™gžá¶ÛncË-·d³Í6‹ÙsÛœ})gm6‹ë;‘Ë_êêoêa§S~ÀÇLdëþ–¿=;.ÿ×>·¢ªM×±lÍþç^̹‡íÄȦ.¼ö6«Z«Æ.¿x•›¶úGù'Þ(z}í¯L?ÿ=¾ÿÅ?3িsÂS‡²÷åï—~áдç÷™zÀöì0Ì¥ë£éüëÊŸsù# ÈT$™MØýôïqögì èxãAnüÅ/¸ùõ¤èÅæ_ý?>uÛIâ·ÈôËOä[w/Õ^±“O9… .øŸØûuõÕWquà^YXX¬[¬·á‡®ëÒÚÚZù“J¥¸ì²ËB$^FWW—\rIÍ}ÊÆ½9ê¿ÒóÀïøÃëqÎÑF¾ð훘zÊ>™z§œò}®y;&_{ßÙÖ©ÑÊNß»‘+náÍ)çpêéß窲4$ªGzù©tn6‘Ý–ɽ7;ï¾5îËOòL—Ð .¤’Ëyéú‹8ëôïqõë›qäo¯çâ]Ë;mfëóoâگ —ŸÂqÇÿ€WîÏ…¿ÿ“Z|ü-NçÒN pÇ·9þ¨£9ñ{×qç¬åÆ+qýu×qÙeÿgIÜÂÂZä«~ýúqÁ„>;ï¼óŒÛ¿øâ‹ôôôÄïtÐhF÷ÊñèŠ#üÞqÒ7†1çÚýùî­ (Ï¿8—†/ÜÓ÷à×ç=Yß6}æ¸ÿjåÝ߯÷nYŠÈÝlvøì_:Vîùi<»ˆ½'63õïÝøMÙ{|ž™W<Ë ¹©Noaùc×2厒³óÙÙ8›ßËw¿>‘>ÿ8²õ&ÓÊKÿû®|¨ ¼þÓQìùð‰|y§Ÿñp¦ýD;³^xŽWßÊoÖ¼×_w@Ä2·$naa-òÕF\DŠ”²v¹ˆâÆ‘ùlä÷ðä§ÿ¹Ùvl“šËË3æWe”üûÌxqÞØmëßfä–ŒI~Âk¯,Æ42Ñþ÷¿`Ü~{Ð&ÀÝiöm˜ÎÓVPWT|a6¯¾ÖŽ;zËâ¿GmÍ–ÍìzÉ ^›õ:3g½Î¬‡Ïa·D36!^¾…)Ïâ«z€Û~>™/oÙ»®Ù\µÌ-‰[XX‹¼.Ìœ9“‹.ºˆ“N:‰£Ž:ŠñãÇóøãk·ÝvÛminnŽßáâø8í±ß–[ÐÀLÒÁ ÑÔFÿ¶æŠ|!jŽ®Žm¤,nçÄl)—ñÔ}ÏÑýÓCØ¿ïS¼ÿ¥½i›q1.uêŸf…€2í ‹xôû'pÅëÁãú¤§Ù7øû©“xv÷#ùïOà'wžÊ‰WË‘x¿.2O5¤*Dnaaa-ò^*Ð;ï¼ÃìÙ³yå•WJKû ´dH$øîw¿ú­v¿ÝOð¯G»hýò™|c³˜SŸ=“×3›°ãø!¸¦ÍÎ;õ£ðÎ뫹ͦì:q$I3“ÓóøÜŸÞ›¯~8‡ï—ç©»eI42¹ wéCæÍYÅ>z‹·3»©Ï¼Ù³ù°òç#t–v*;™7ý&.;ùPŽ»©‡±ÇU÷½¹úê«,‰[XX‹¼6æÎ ÀQGÅæ›oΖ[eƒ-¶Ø‚Ûo¿_ýêW<÷ÜsH)Ùi§øáȘ1c˜3gN ëuðÌoÆ];_Ê·ÞÊèÿÂco.¦Sôe«1M [¬|€©·žÆßNŸÂ/»¯äïï'õ•ïpîèYÜú¿O¯Æ6÷sý-§ñ×SþÀU\Åm3ÓÓ¸›7…YZtNãï÷wó—³/bëöÛ9÷É,Òhï R›O`Ï }élÁÎÿ}§{‰/z¶$ÕÜÇwžÆ­'Oá ÿþöòbzRCÛç}îúÇë¬ÜäŽÝ5Ç{o/¤31œ]G·àt´Û7ÁÂbÆz™ÙÙÐÐÀ­·ÞÊvÛmgÜ&ŸÏ#¥$‘¨†€¼öÚk{ì±µÓõûíÂWÏ8•£÷ÇèAMxÙ•,_ð?ø3ßÞo FêE2ßÎü×â/W\ƯtV4ué c©ßåœ#vgË!8ó™ýÐ/øÖO¦1ûs¸îß`ÇM{“Êw0ÿG¸í——pìû6XXX"_ÃKÏcï½÷¦_¿~um¿téRž|òÉÚY–È-,,,,Ö'ØÆ–È-,,,,,‘[XXXXX"·°°°°Dnaaaaa‰ÜÂÂÂÂbÝÃkø$}XÌ&”¥¼ÂR¥ÒßÃQŠåï¨ü"¸"ÛÆÁG†*—¨UL‚G1Whê#qÚñ•÷•Â¥‘|ºÈQÀÙ üª$.]äÈR(]+Ù·¨³<æêgåQ‹Àõ®^÷zª¼D¯iܯdä:VÇŒüÎ4Y9—òXœÒß|åwõŒUj¬ Y¹?åý)"&BgTü—_ɉÊ™Ä_§ò}qJwªzˆyÆâžôòõëŒöNȘû¬{ßÂ÷EFÆ|öt÷¿žwQÿÖº¶"ð'ðIo@üÀó/ ×3Ì7q×CDFT‰z¿„áÙ2ßtŽ(ïpð¿åó¾ QެŸeèÚ‡ß3ݶÁñzºVÜàIT#•›ª^,”‡ªø²åâOSj~´T#j¼TBó;õ§É‘§@# ú’¢‹,iò:/oïâÐL’•¤É—:fŠÀ8L/ŒˆŒÓ,QTñšøFšû û\G)ºýˆÈ«¾2ê¹ ÃƒTëš— 68)š&@Ýý±/Aõj)M7VÝÄTdÃãm£FŒy‚ [–&%yÂÕû`zу㠿2òü¨o‰¤vŠˆ —¬LcA‚¥Æø‚gØHt“£xB«do".iøÎLæÂ0á©ç懸*È=q¦jü½Rå Ý»YÏäg¼©wܶžTfp¡¼"@Î"t`šIƒÄ®#4¡Xª¢†µ}%|%#Cv¥iR’F"«þ­€OwÉÆn$‡Sù7€‹ ™$òÊ#}°tÓ† …ƒzµu“hœ¥]e¥{øýˆ¥,Vˆ¬ÃÞÔu­1Ȉը;¶ KôÙ ßëê³!*{Ö;Þ¢ʳ§>³RyÕõO±0Ø‹:‰~4zëpuVõX·24ªô*”•±(‘sp.$J$^½§"`vWpá•„ŽÄ¨ÃÀ”þПµŒ€úI>ún Œ]gVFŸ QsµV"¦ƒ0?‘ÉÏÆGÒô™½”åÿZoÂ8ãGÉUj¬ëð,-+/´éf‡­?K<2ƶ/ I“§›A $qqx¸8zÈVHKDlZ¡L,RY¸«ö‹n’q*ûv*WBÔ$SÕ~Êñ¤öºéHG„^,¡±ðDèÛèöÁç L"æù’gID^¼8Éȳ!5¿Ñ}+´ÇÊ Ryþ¢"•k†®Ão4>Ç(/©Mªl 5÷Xo‹ …à¤Ñþ5¿Ïabw¤HÃ'XͪÆYY|ó[Dß{óä$b¥9½ áÎHÿfV7 èW±*3 eŠBaa´Ð…vi~_Bùêè­R3#Hƒ¦]fÈX½6n™!53TNó­’· Ü4z…ÊŸõ”¬î&´Ð@†< á’•yr¡EYX@’š•Ê [Ê E¦¢ G¯›¯Y¶EW"¤}—'†ð¤£^ýµSï´ ý.*f…ØDDgT¶òD ՈȕŽJ(êk-´þXö‹Õ°kUGFtf"*µ¬ÂçNHN“Z«P}΄2éTéÍ7ŒYIHÃsZ¾6êÛíWV–¢"1$ñhÀeéÈ;©Ne¾"Ëú¡U“™Øô÷ØìmˆN–¢ÎÕŠÊGº©+(yè¥#bYs=¨®<¤b‰ëøXàù± ÅàòvvüK c_èO=1³Ü1-?ÌPj’ðy|VQ …G‹h¤€O†¼ÆåçP3/â’µôhZ¨FuNõzš¦Fstzzœ­"•éCh®­0LÎRYÑ©S¯ÐØvÂðL ídïVʽ3¿ 2¢¥Ë+QÄ*©•¥Ö +ˆø( ‘ÔøCDÌX„v•, úuóx2ä¢cœæ¯[™•'(´ZzxÇXñ-ëŸTùOĬˆ‘¹¢Æ§ú¦ÖsB+ÿee¼ffK8hJå1 ß~?`[©ŠŒ,\Ê/¤Œ,ÿâ"_¤æaÛžµæf=qê.<¥K”“y Ò/iç®ÑA§_˜ÜŸqÎÇøI =†r¥…â(kä~HÆ0ˇ„Ò(MèIK,µèŠ'hÙù!ëX}ÂLò‰¬!ÙèUÔÚ“–î>J£U‡ñ•š¤æþiÊ;aZ×Iíu÷1ºW½Ý* ç¦úQ¢2Aù9ôI‘@ è!§Y†=:ÉŽP”FXR ·è¡æûéøIhÞ;¡±Ù¥Æ!þ1é êU— (z¤V„5ÉÆªGÈÓY ©YIͺ"îZï¼oXY³Úˆ—GDe¿zç°>eúWƒ|Å" ^Ÿ¢É!SzG‚RRÕ ­—ìœô>?Dü°º ˜å7U†u´Î^Œ“¨Tî™IúQWº"ä÷ŠŸxL:‡ˆìSøO–5r¡è­BK¨ªãÄWXZŽHÃŒ¾º©¡¾%nÜÒX7ÈÐì%5še[.È”œ79 4’¬8uzÈ¢ ›T# ¤Á9$ ³}Tb0­¢¡Î}'"N,}|pù¥uBj<•±†¯ríÖÐê]uЙ™Âÿ0È1:2Šê‰zÉ©Jh «DUµêä©• túª0LÀB;YÊšþB.Q_K{Ni$BŽu™êtrŒŒÈIÅÿ5’$Gž yÅé(ú¿£ÈQaƒDDž-ýÔ¥„Fj4Ý+a\ Iƒ,¢7Vu¢)ÎDÎhu1DH^Ü (jXKD‚ðEèõJ€ÎÒ‘5^ßðâšœR»oû蜧I $è$²¼{È‘+yè[h ‹LÀQÀ 9wÌ‹ä°D$ T¥$¡¹'Uš²ð¥AËN(2bÉF-§Zúb¼3§¾}˜,b¦(5/K}ò‰0<¥f —1«žðjJ†$Ia¸>æ\€ 30zÍÆŽ9üNDœËU]5.ÖYe&H–žž …6®:šÃÖ>ùE¤’âUr@“0$ «='¢)ÄEå -Lj:žk?àì­G‹ÔÎйT%áGÓ=©í P ¾àáÈh‰>0©œ:û[f3©hôBCî&Ý\—å%%k$¸ò#qÙåÀ%G¾I^‡¤€¤pi&Iš¹’Õ.£TQsÖ9”-ºÜ­-pÅ{¤æW^0„’é_ ©<ª^,#KEõõ ÏC­Õ[-ßB­É'ºEhdkxíã”tYã¿zІ—SÄ(æ§XbÀê9©Xˆfšš÷*n~q¤†hˆ8êuö‘9LU*¡™µÃk¯dl¬VܪÁ”–ç‘}Ä%½HƒÆDŠHààã“.ä £ç!vY–ít¦ •ÔCÑXiЫ£äh’Edä9šgɤ« v¦è'î,4‘NBㆷ_ÏD)Bqžˆˆ"®¾,Ñ4Ÿ¨å •%ƒ ÕJp*·Y—Õ&jÚE¥)ÈGÕúÂÎ\rÈPéËÁ -J DdHâ‘ÂÃ!AwI7÷ aÙâ4éäzÌìöš c]½5«{ðÃa„zë¢>²4­™tM³DÝ·NY•1ñA*1øFc$ÎF7'ÀHc’‘Ô8èEÍýÖZCãŠ-:U[T£‚Ðú LÉ~Mù,‹@&O¡¤¹Kcvrõ]PíTaŒôšPcqÿ›ÖûŽ&.&(‡HEVtŒjƒN/®D¬B">ĸª-ú‰N·N¬nãD|Vj!­Røaµ’—~‘\¤EmÂ"yŠ>‰Öš[šI57âz.޳aTÈímßJ ‹µ ß÷)ä ¤»zèêì"בÁ_‘Y.¦¥)Ñ+„ذÈ_Í3J¸²•1%ˆYÍFWïæ”yµ*b„Eñ½•,rÝÂ]D–jA÷Må„8’4¶6Ñw`Ùl–L6ƒÌÈ•kaañ9¶ß…@A¢1É€ÞÍ´{ËéItã/É‚ EV‹òš-oY#PTÃ;M—Ðp¡ˆ)ô ëMÂEÈD…A‰Hºµ ¡''ñÛ³“öD¤‚Q|Bbðtewõug¤¶xU´–¹ÐVëT« WU S$K˜U…1™KWöAYSMÍÅŒs±ÃE n8êLªÌ› ÓâÑ«_ozzzìÓjaaQ©÷ôôл+ùBžLgrz§d\K }‰ls>5´´V™Ys~K´Ú‹¢+Ÿb&µ6h'…bT]$.‚,…¢FN@>1ª‡-v§%ASs===Ö߈^²5µ”¶°ˆC:&Õ˜"×’A¶ç”5”}Ô`<]4•ºµ0¦cé"‹ÌYÔºðKuÑåÓ+Žš’M%¾ÕŒlpqI• jûÈJé<>žÔÖ¬jLaÿr Ù£ÑÅI¸–Ä7Pø¾O6›¥P(¬Õ㸮K2™Ü`œßëÎhp."åÀªYœUÂ.*Ááìh¿,ŒötT’¨Ýኘ…µ¶Ö×ü×5àˆJ,R»çr9‡D)IQY •œ•2#{a•F ŠIqA8é["ßI<“ÉÐÜÜLSS®ë®•ã º»»éêꢡ¡Á’¹E˜¬žPêÚ‡,ÂÚ·4–!3§wéóÌá‡Ñ„zÓ0Tc”†qÄÕ`)[à <ñðpÈ•’¯ÊAT¬§›ÃdÄWÓÂe‘Ä­5¾AZB¹\Žææfš››‘R’Ïç×΋*ÍÍÍtwwÓÐÐ`e‹Ð³(БýJ¥@ÕŽ®ÝPYjKRë ­ÕêÐ#b+¦¢L3µ‹û‰˜òêoÝR§.N©òêJ2¢êZ ¯\0ÒÔ .)Ûù†‰|>Occ#¾ï¯õUJIcc#öÂ[ù#ÚL-øíì§›š’èÚõ9š®ªá¸”ÕI ’†VÒĸo‹9R¥^ÁÅŠ«r¥¦ð2rEÔBÝ‹\·ŒfxJ¬Ue±z–¹ô-bžÅd¬SR&Y(\«‚Ôô=%RÚB's¨{Ôkí²®~ºÁ}˜N% ¥Üç S*}àÇDí¨ð„¦®®q¹š¡…Eý–—Åú OÀ®©&¤Òl–ÌÓÏ-:×\>Èz<ŸNñ|º‘ü½¥eóÐAgX–-f?TIÆvªª·¯‘†4:†‹¦è Më5 +„¨‰\”P<ðJukr¥d¦&†Ñ„¥àxCE³|ÔŽÛºs#voLs\ï• ñ¢‘Lü<ü<{5¥YXèäæŽ^<Ó“ZcD®cÙæ6u7EKuÑ–è¦ÿê¬rSÜ9D[K‚._’}ª¿@Š©w‘%G!Tß<¾;”~RòtÈD¤T~°»´–ÖF`ÿ'î]Y3·Ïõ ’ã[;9¬WW]Ûvó\ØÖÎ=«š¹©£%Ðsè³P¹žl«DOÄjŽþ=\„V}º†8µõø¨N-27D”ë·ãÀ¡¨çñK"JqK_±¾MS“Îéë…»™‡g©,}Â͘7²‡¹u;í¸ £Zæñè}/³|uOR´°é¸Øvó6–>õž_è[†°Ø p|ŸNkéZíßÖ« ÜØÑë3Ž š„¯æ‘¶pj´¸ ØH¦jé; ©«ñp “Š.Y2Z QßM´\ö7‰K—<>=äðE†uEruMNtzµJ"á¸Êè\dî¼½1!9þ4.¿ôbÎ9fmõü yGÎøÓCLûç½¹‰qLþ¿_ñ£óe·6^ÚbÑS> ‰—qx¯.&4fÖܪ-T°Vj‹WÙÈ-þÌÑèéúªˆú¾Äõ¬"tš¿‘$½hÀÅ¡‹,¥À…R@¡ÉEªëyìDºŸ,òà€D“[Û‹qÑ2šý¿qGì»3_Ú—FºYöÉ{¼6ýÜ|ãC¼ÛµþH9ÎÀmÙuLšÝ™0¶™;æn|/ù´iÓêÚn¿ýö³Œ¸Âp|ë*Í‹è€4¬(5ßк’=ý)|JÏÔ‘Eò†DSR Ö³4ÚÁq+4–²n©U >+ÉT¢Pâ´na\Ô:¿‘ëBqLýåZ²È~{qáïÆa£Rý÷bàæ;2©×ÇÜ;õÁõê%ð?º‡Ë/kaR¿wùç´ŽÒ l zíàùçŸý~×]w]'ã˜J3Xul ‡Á?ÿOÜEçÃ}ÕrÀ×iÙû^|tˆÌ‡x&4e˜Þý霟"æ35’ĉ‘1L=Lkmï(D/ê,Ò¥6&w€[1(ÙÉÜE‚%ý_ôáËîæ‡{¦Xv÷9~é dquüŸ¸õ̱ø¯ü–£ÏÏ‘ß:ŽƒšæÁ ÿ抷ËçÒ}¿;Lcf>¯=8•ßLy˜Ê«O·?ã¿q6§¾cú;tΛÅc·^͵÷½KqÁá2xâÉœú¡ì<ª/^ºƒE>Âïþç žZaË Ld]‹ä×èÓóاó‰»èöeˆD’U÷ÝX!ñþg_Ʋk¿¯µÖwI¥?5‘«Ê³)dPj±ÉTÔA¼f+Üü›p÷¢¨¬â”zx4à’§@š<9 5Î4LéÒÀ·êïuÛxB™‰‚*”ˆ”x7÷BüL<Þ¶/_Ù«/ŽìaÆ~È•.,#ͼ7Ÿ`^eÃfvüöï¹òk£H’§»+C¯»pÄ…Û0¦õ4N¿á]²õlã äà]É{·"ünV,÷ºIïj»;ÙÁsOÏ$³Çúî8žÑî ¼)û±ãøÍp)ðÎÓϰÈi8™FmÒöiD¿ìzÌO¸Ümç›—Í ‹fvüÖUüæ¿7#‘í`Áâ }FìÂÑ?¸‚~™ãøÁ#Ë ßÁ\ø³™Ø”§ã“™›ifÈÀ$™.Kâk£“úÒ eK¼ÿÙ—É#—­x™ØU|!ùYÊ<èCû‘e¸9»Î¶Uû[‚Ù·§‹7*êÁ˜¡ØÅjöhc©g¯ô#[)ge:S}0·ÚõGgyë$&¿\4KÔÔ®Ö.ÜMG1"! 0‡—^^\(D ›l¹m èøèuæ¤bòæÍL€ IDAT#IÊE<òãSùÉÃí ýꯘzþnlùõo2ñŽ‹y¢¹ŽmÚàÈÝ{ãæò÷ó&sÙó+i>ø2îÿñž¸¥3oŸþ3³»²Ë°ÝØmÔ5¼µtvÛ:‰È¿Ã“Óçác r!wœ}4—¿ì3ôð_qó…vÐáL¸fÿnþ'1ŠDvSN8‹›gçtØåÜþÝÝØóðýðØ,¸ CS™~‰kÏ:Ÿ»p<òëþE·ùÆ¾Ž¹ò¥JæK¯º "µÔ»¯Õ¡r£«ß›ê‹Õç¿9jWþçowqà´ò—[þ´rŸYÐZÝûµ¦ ÚæÔw-6ÔëôéÇ-#íÞu¼Ǩ.í\Q'y« @R‰×ÖKÈå½{84à‘À%KŽt ¡>.…Ríq¤«xPˆ‹N:j¿PO)µ4+µ%µÖüys™—— NlÊÎ;ä¦Ù ×r¬zy¢H0.G–ñäC38gžl±ïÁ¹d<ÍdyõáiÌ«sp"‘,î_Jü‚Qê{’y—Gÿ2OûñW¼Ê"H¿ÊէƇÇÏ1‡Nd»ƒOe»}÷âúÓO溷²–-ÖÚ}—FG¿Ô jâei%¨™«XõmŒÈ¦ÎòÁ_ Mž&Š4¢1c‰_M»/ÿÒ<\R$H«À7²¤¾m›Ê®Â`¹ë$£òÞ¼ðŒ&#;%º:¿kÖbð—<΃3Nglj-ìtúÏøVû¯¹ù©÷Y“óïÎâ­ìAŒï³‡î;˜^ÎЃ¾Ä¸„@v½Ã›È/­c›ïðAî ¶ï½‡8œÿù‰öñjò^ž\±mõuNÌ' ëiþùð‚ú&ÑÂÖGÂ6øíoóÖ<Ÿ|ö]fçd»DÉ…pËÝïÓéC²ÏZs Yâ^oúÊw¹÷ª ¹÷ú-8iÊMœ¶õhöÞm8SßšM3ª^x!öû]vÙÅ^$ཬÇP/KâAâ.Ë,:2/ë}&aE%l}:PÐÔ”Z›ƒ«ïÌc2M¥R0«¸:(¦Ö'ÈR M®ÔŸ‡˜©"œªšÊBÉ×ñ5+ZRTEæ WÊ Ÿ†NY+‹>1÷ýæ &nñ]ö¸=Çþò6¾!}¤Åâó•ÍâÆ»¿ÆÇŒà‹?»‹Ý/Êli&Ašwþz+Ou‚ßUÏ6÷sãýGó_ÆÞ?ø+ž±œtCIû™;ŸãîçqàoB2á³ôÁ»yby+ †rÄåÿ`zÑ¿w ‡N^½åϼÿ“û¸é¯ò«C‡²÷…·òàYtÉFz7g¸ÿüCøÙ3Y¼±'ðÇ?Ió¢y,îJ2d3dóæ-[ç!ŽªF¾!u­Éf]àùžönR"W„CóÞ‡G4ñòß[ö>‚U÷߉\y®»á3EÔõ>jÅÔÐØºêF-Ý0¦J «È]M‡U=’À¬ý™trO`?ÒC:îd×,òsÿÉ÷N˜Ãaßü‡î±=#õ¦ÑÉÒµt1sÞ{•g?΃L3ãʳøÎòosêá»ó… º?y…'ï˜ÂÕ{“ €ì¬c›¦ÿú ¾³àL&y7Æ @Ÿ|K?~÷g¼ÇŠÊ¨2̺ãfqÛ»ïs÷_ŸÅXU»°ˆYÓ_fô6›1t@ýéaé{O3íoà÷Î.Þz¹‚§.=óçžÎ‰_Þ±ÃZé]èdñûo²0›DÅuW2÷Õl7b3¾0$Ǫ…¯óð?þÀ¬ûXuëÄܸ'šçzR,Èw†‹dIŸE£ 1ì|øÏt>ò—Èw ò.Ï÷4|F6G WN19UÒöcêÿí¤“°Õ_Ô¬›h(uèÉÓ]ª"#–¶nºˆO¨ÔËHÕß;˜*0²ì+G-†Ë`©Å"¡û!±E†cŠÿŸÑLS¯fûVn@(;£Òé4ýû÷_§Ç^ºt)©T1ÎØÍZ°{S†ïö[ñ™öñó¥}xáSy÷ª.²wUÒ鋤ì(Vv°)„PŠ_éÒÖeˆdM >~hëêñÊZ¸‡‡[ )ÌW êšDȺ»n ²ÚöfùHŽ£Ä$ŠÒ…Ô÷ê¯C`aa±þã™îî^Ùô©תæOMâ&ø¯œ¾K™PÜ™Á~œ¢F$‹šÂ_žH ‰K‹Hââ°Š4ir¡ª¯B#‡˜ö/kÈH"°B_gGk`•ÿåUË8FÝ£Å$-,,66ÜÔÑBpx¯Õ+žuתfn^ñÙWåBcG«áuA©W¬Æ>ëC5C³…\á–¹¯Þ¾Tg­ vÐZY¦2TÅ1جºJüž)À\j*sU[½…—ê–´òy=¾…àOíͼ™ö˜Ü·SÛX"ˆy—ëÛ{ñBOr˜xR±°ƒ‹N@Q}vdíG!ZA‡&’H]2¨®w¨ªk]K8sȈyÒÑYòN`´ú2/~V §½®½Hr ‹ÿ4žëiàÅt»6¦™Ð”et"Oÿ©/Í»¼Ÿóx®;Éó=©5ÚêÍ$Qèä‘z²ÐeŒ.^%SY‘T’¸4àáãÓ#ó%7O ¾¦l‰JÔfÙ%>—TuÚšÎ=xU¼`-ri¨®¥zH}+²lXš [Gçú¼„éÝ©ÏPëÓ‹+Ž"D-±‹H£äp{ °ë¥&¶»ú}16Ü£Y4Ð#st“UHZ/.ËP"R¼¬W"W2JNe´N¦rp_†æ¡ºnud±~Ë,ëŠT­¤bQ[\uØ«ñª@±¥šT ïÏ È \šIàà°R¦+}3¥!„­ÔQïúnH²N¾4¦r@¡Pª¸¥ŽW6l8ŽC:^gÇK§Ó8ŽíšdOw¦œqA´â_!yîk"Á Øõjw $Í$‘:ÉTHV§^K8ÊDåIêt¤·¤ÃÛH¥ y”iELI0/|à ¤±ˆŒµ°6dxžGGG]]]ôéÓÏóÖÊqr¹är9š›mÎE}RATÖ×ÇSëk†—ÿåàÄ£‘y ô’ìãz.¨õ\t‘(AÅB]5¥ø‰ˆÄéD‚¢çdB)>2f- +릣iR J-"FZÑgxŠØÉ®\F@uŒ D8³SºÔÎW–Ì-,,Ö¼E.kÊ & ÓD‚”ðÈ‘§‹l%ÕÞ\ˆJÔuµ†iüúWQ±&ZAqõ¤–àq=Œn£³¦Z“¥»»Û>…ŸŽâ—Ó£ZßPìp§Dâ "AVæé)µa3¥Ûˆˆ²n.SmJÑ_­¬^ÞV—|dJÏ—J«®Ø–öKe!?G 2Ä>‰Ÿ‹æ/T(¶¶u¬ƒ‹ ‰GJ$É#M¾T¹PD2ÓÃ$í£) †l¼É[N¶—«\'®—¤¦©FtÒ*×R—bbžnG¨<ü‘•V,,,Ö¼´"”Ü}-“ í\¥E§#.ôÊϪrJ%E•bE$ŠEÆL(Ñ`G*efê~°æxø÷úvÌ"²jqBetkÇ´®Î96¦ÜÂÂbí`õlªÿm&A£H°J¦é"G>”%à¨eþé«»ê36«–½™P¢k¡ü1­D̾<4³ºq4ßÚäkÖ*#u¡•SŠ=5Â-…úF=['`¨î qÑ(TÖ¦´z5¾,âø„[Ì™œ¯fçi0‹58IÈjÑ,ã'–5k‡ÌÃîÇpå§pA#I’%Ï’xøˆØÇá NÕ:VKy‡'µa³¬y.UK?ºŠ¨¯(a¹ a™¡¥q‚ñtI=PÁ&ë[XX¬Ek\MšÑU æD¦D‚F¥´~ˆø%úä"SÓÊh¼Š:±T§W‘f0Øøj–©®®Šˆ‘fêé|üÌqˆjãꎜP´¥´”naa±Ö]ÔØ¦…$ ܉×#¦©7(M„­nýïuÍwÊ:¸£•g¢²02¡±H툣 DÕÕ‡LÍ7ÏbåH^Y ‹Ï“cRå$A³HâáÒ#sd)„¬[´dÇi*M†›Ð;¨1á¦b´f™$®JŒZÛÊ"XFôqꎌ™ uÚŽ………ÅÚ rS¾rø^ <ÊšxÁhNš33õV±®q…C|I’¸•ƒ‰KMÑ)Žp©ç[¡MéN:u/{ÀÖ[±°°Xó’J­ïSx4ŠD€Äe]˜«ÿÖË&>ú éWcJ¿n,~ÌjC%w4$®þÖ1¬0‚LVÆÌ ÿa8›qܵ×pÂæÑ?²g?9òvF]u 'lo_w2gÿe.¾3ŒCú+&ï8ˆ^),äèY¹„¹ï¼Ìÿü+ÿx~™Èqó¥‹~ʱ;¡­w#I‘§gå>y÷Užú×߸û™OHÛ»añ,ò8}¼±TK¼Gf*rJ­}ÉKÙ×È%½ì,ê8§¸ŽÁo|̺y¸‚y¸XX^ÐcìÇΉÒJ,ëñ«PÈôГ«ÞA‘Îùú @ï”ÀÏtÑ•sIµeì„aŒÙe¶ù¿³øÉË”g¡‰£F2´ÍCú>ߣ©Ï0Æì:Œ1ã÷b‡ßÅ÷î‡oo„Å´Ì E‚„ðHË|)ÄPh MsŠ}\•A“%-"¿>:=®ð•ÐÔ‘ ÿNF¢WÔ"¶B;æhFOGÔ /Õ(ëœ,Ö5 ¼wó™E‹[±ØGÇüæÝ›¿ÅÙ™‹ÓºÇüèRNØ¡/»¾›üÝå‡ÊÀÎGMæíÊ5AvËÌ὇ÿÈ¥w¾C®®mÙqò%œuÀ(õiÂËv0÷'øÛ§òȇ¥õA=c±øÏ/, 'É—¥.©R¬l©vJŸˆí?/jȵ,ò01‡c¸%z§£Rh6‹UǦÐd–†{ ź²ŠÔ‚‘œÁ9ÁÒøÆˆ|ÇÛL{öC €Ó·?}kzN|2‹Y¼*_"L·d¹oʯùÅÉû³U[åKzhÜd'»àR.ا­øäˆÞìröo¸ääØv°`ùœXœmaèèØ}ûáEË¢žmÈQhDÿD†Ž%Kérû2j—ùàâ¯ó¯Î±X¬7¶¸´ ‰C³H’§@†\¥¯O0ž[(A¸ïpPÇ©K13Tv6·QÛΙV„ÒT »:Î0Ç–Ïà ýqÇ‘xt'ÄÆÕ1zWæc ‹õ cO¹‘GN©ZÎïýéTμ½ŽWÈi Ï¦;sØ~£ñ€ÂÒE,5j$.#ü9× hî7~- „¿ŠWx‚9>8&ñCGàåÞdêßáöò 8䦞7žÝ¿¼ýŸ¸‡e¿Èׂ'ñÐOÏàÿžéd³o^Ã5Ço^}1ÕÞòÌúýq>%IKŸÞ4õÝ“ó¯<ƒ†mǶýÞ/ÔË«­'Â`¸ k‡”(vºï’¹Ø:Üú§4œU©³`ãÔõ®ZßµjÚš-«)úÒ ŸDWáŽ@êÄå…7”ÊÅ•šU‹õñUkä>ÝY¿dsÄ“ÿçöRXÂSŒù¾ùñLµ gD[ù0 xìWßå7~‚$FoŘ[qòÔ89hÃÂ@Vl¶£Å‹L›ÑԌѭcœìrÒÿpöaã” Ô–Ë7Ò˜wHí±X"_ìñ ÕšI HÒ2IÖÑ‘¸_rš"NtÎJ©T¨ÔJ1Á *ú¦jIp ’Kµº¬šH$5Ù§9Ò|ÍL£žl\Au‹õf<ŽüýBž\.Cºc1¿ó OÜs÷½¶,Æi™çíëNæÜ'wàgS¾Íø–Al½ÝP¼Ç>)=@EB•™xâ®ç˜Ø‘ßñ:‹ýê6È%±Žmšw?…‹ŽÞ‰–®7ùÇõÿàîÑü×·ŽbÛdùA®c,ë!Rnß–"D’‘9òøq7œ@SL½í€ -5Í×Âýªß¨“€OØ©ª§ì þ­û"5ã‹6Ò(Ÿg0ÊFFLíàˆCÒJ°æ€©Ú¡UÉ7ò÷O§DÉ¿ž_ο«oÙ—kÎØžA_:ƒcžÉµ¯§)|ü>å'±u¢‰Åó×ͦˇDë`ZsEÉÆùøææ÷clŸ]øÒ^Cyóé4Úõ}jnãÐwèPš(¼û0·Ýó(˽ìpÊ‘E"‡ºÆb±þÀðH—YŽO‰@J4gˆŠˆõ zçd0FEgä ÙàqÜHMó`‡![Qb’µÃ…áÊí¥7",­G—;Á:q:’Åç >ŸÜûGþñq¼M8ô¤Cæ€?ÿantyщßþwÞ}'wþã>îûû5œ¸MqñçÏ”¿?³_ dÒ÷oá_÷ßÁ%‡ =cµ·ñYòþ{´ûà;•«~¿ºädvm}Ô‹Åú#­$HݲŸ" Ü£ûÜ ðU½*‚.ÓR—ø§‡ mâZÃW*ûVÇk≫W ¿Œ Æ×ÿØRùçÙw¸ã–é¬ô©mŽä«ãAvðìçñƒ©3ó“•R½é•ȱô÷Yœk(>5þ2þý«óøÑÔñôK3ymÆ<õf;>?_*TÇ6™W¦ò“)ñÆ‚<}FïÀŽ;Œ µjï¾ú­¢¾±X¬hÀÃ=2G>”ý *v¹ˆT=ŠÆVí()–·s”xb ^B¤[O8²$l‰ëº…£bœÊ„öØÁó©nˆbÙFŒ”eÓ½ªÃÈÒâÛ¯, ü€èâ#qF42hè`ûZ¬¶ýÕ«Á (ºÝaËX„¢HÀ¬kë+ªèÅd5ÊD*–~¼ -´«'òßð™„ÏC®A‰Èƒâ¼c8¬Z„Ýâsч±»MdÛÁŸ}úö¶â¤ëîæ†3¶§Á^Y‹ÚT‰ÑLÿµ(×\ïÜÔGÓÑ|_kr4“o˜ ÔÑë-pQÇYëÒô½Â)úÑl'©ýq­‚1ʛӛ½¿w=îÓFƒ~¾‡•‹çðöŒG¹ãÏÿäµekرèw³tÞæ.î´P–1¤.´ß†#´}T‡¢TˆN/z;-4Š&R"‰#ò~®G. „Ã-}o¤/QÇ\ÞxãMz^›Áó³\å—9p§?0cººµÃðCÌÏŽß‘!½“ä;æðêý×qÅM/°ÔO1áÂ?ó“ÑwsÆé·ð¡_Ü~Ôq×rÍÁ3¹èøûÙîêß3é…s9éúw(8ØóŒÿáØ]G1x@+¤YúÁ³Üùûßq×[]ÅçÍéËvGœÊI_Ù/ lÄïYÎüÞâß_Â]ïÙPÄ…ÄõÍ0’¯®Ÿe•tÃÅ´Êñ/¾+â¢Oå×5©G8ÜPwOÕÖŽVy”J³Š¢,ã–$—^¢‘nmN+½œRN !R× ¿@F¦i÷W£V\­¼MAõíÇbãD!ÓMFz$“žöµ[ñÆ¿¸þ—cÉ*Á€qGsæ‰ßã´÷åOu3ëÅ™d÷ÝíúÝÆ‡K|­l¹õp²¯ßÌ»yØ.´¯^ŒÜ~;ú~ô'.½â]²©aìzÌÉœöãÓ™;ùr^èn`Ì7É/¿šâù[¯äGo¶#íÃ)ߙĨX"ß(à(RŠ‹YŸv‰¯ ®Ö.QÃã¬d•d³`wz>Ö\€Ô}ÖrªŠÐ$àÐ(R´‰Vº}hu{“¸ÂÅ-¹U%’nÙÍÊB'+ü–ù+Xé÷PÀÇ#„¯Òxµh¤ª koT"¥K"™D&›é7|;¾tòW•™Å½¯wk‰¼sö ʆú»ï¯bä¾×ò¥­6Å}ê-º_~–™ùo3açVþù@;²a,ÛŽ‘¼qÝ›¤é£Ý_÷œ×xáåw(ð*¯.ÌÎWíÏ.[x¼ðÁ®}øHæýí,~yûäÑg(GÈIöžm\`¨úH8ê[Dª¥Dk ¥5E”¦U[,ZóRK²÷,c)Z¦_©æè"HR–KZhuzÑâ´ $E‰$í§é–i:ü•tø]tɲ~ŽâÿòäKë Oj•¤àÐD%(xA-‘oLç¨íتá=þùò ¤–ÈÃðÍc¡ìMk/;|,£–òÜKûxZl¤4N¨îITèP ݦº€"BȾҎ-8Ç ýÇ,†¥³öƒÑæÁVm.I¤D’f‘¢ŸÛJoÑ‹§™„ðB•9zdš™¡Ëï¤Ý_ÅJ¿“n™!K.ä;Nƒž0žzu®qK·`¶’ÅÆƒÜk7òÝ?Π;×êe‹X´"m¼ÇΨ¯ò£†ÿ~}õ»´Ëa|ù¢‹™XyˆVòÜc/ræÿìËn}§ñÖŽãðñ¼Xg'éç) p×Å¡@!o͆ÈE¤wp™XÃÖ¬®Ò ŽtÉF§1Z£D,Cz}TŸ—¡1ª B9£ôuz3ÀéK›Û›f§™„H–²æ}r~Žn¿‹E…å,÷W²Ê龜l$„Ñ1ðµ¤ì°¦#c [l<óyçí·‹ÎNÓËVz‚’£Æ0Òy+nz˜—WIpº˜·*œ¼êùûx¢ó9pÿq´NØ„9O=Ã\)Q…ùó‰ÿ_lµõ@œ·Xb#„ƒ ·Ûˆ(zðŠ´'DQH!Š¡w’E€/«OFAˉHY¤UŸ`ÛJÐÐED& z8[Fú•ÜèDQþ¼­No9môwÛhršp„S\yHIZ¦éð;YZXÎ’B;«dwiÌEr—DÓùÃ]ƒª#ò‚Ë¿´¼ öò#K ‹Ï˯¤} ÚaÆŸÏ‹s>äçã¾ÈÒi²¢ÐŸÁÍʲ6ýÿzp>Wõ; ižÃÝ—ÍýT$,W<ͽO|“Ÿ÷}Îè¹…'çÀ û°¹ ¯Ø;³q<^@Ÿ«X±Ž¬Z¹Ž,å6Ê"'¹%ªçBVUƒJ'z!pô«Üæp@Jª‚!é—œœ’‚,º:¥ô• @ T‰€T•6ÊÒPŠ$½fú»}é#zÑ(ipðD=©gl IDAT‘ò{üVø´û¬ô»ÉÊ,=2C±»Œ ¡TWA‚ЄÇ-wKèŸø‹xìÖ¿³û¹_æÔC¦óâþÊ¥Wõãì¯ÉÏoÆÉu³ª}oÏï <yfÿë.^>â|ƽý7þ=÷SÚÒr%Ï^õ~›>#¿˜C[²|òÎbòÒÇ·àFB䒜̇²Ü<¡@09?G¡dh¥‰£&³K"µ[ŽläŽ(ü Q$BßwB"eÑÊ/Ûô•ŒvYµðƒ~Ã4ˆM"Å·•6§/"EBxä(•:ü4«d'Ë tø«HËl©‰FØpÖÊ ‹MDêBJ$b{1Jw³“¡&ÕÅE¨¹Äˆ”­~haFrθî|óIüä±ö56ñ;#¾Î”kä•ó'ó‡·òö:oàX4!âãt U›¨T~’¨½t@W§Dê©`°bME¶b¥ëŽ(ü¹¢xÔrøŸç¸Eî“Uc¶A¤è´ÑWô¡·ÛŒ‡W”F¤OF¦Yæ·³ °Œå…zÈâÚÑ’¸:öZe1ÊÁ(žzQÔ !¨¯w…¤¼ùšE#c;‹ºïæÂ'? ‰»ŒÜó0¶ræ2oé*ü–‘L<æHF,xˆß}`I|ãƒ>¹½Üpר¸6) Õ<uù’¤tzJ¿ø÷>¢™‰¾ vÑ×éCB$p‡‚_À>« +Y\XÂâü2:ý y ¥ŠëÑF÷BaÕ`ü £õZ¢ÑNÒJÐT7ÕX ZëQÞÎþß¹Œã6+°pæ\ö“?óÞg‰Í ÞjŽÚwƒú4"ÒKùèµû¹äò[x3k/÷ÆDß„¬æpŠM­:áúúãæˆ|íþªÖrŠ$­N3Ý~ôvzÓâ4’IéÐåw±2ßÉ2¿¿‹ŒŸÁ'O^pAJxE­L–& _Ý™…’t£#hÕ2w"d‡ÜAŒ’¾vnªÖ`‘OÊi®ÒJ+k@Zq>΄ …Òµ3n(4Í”u¤$E?Ƃ׼*® <EÍ"E·/œ6šf èñÓtÊn–ùí,÷;èò{ÈmúŠj_vÀz¥h'0R¿Ä¦¾ôñ‹á8ø¯àSŒQ°TÕÄC9p¤J°xk•[XX¬)¨‰?:+\„ÈW…È6Šv”‘¦ÉáUA±ÚI#Iz}ì¤Íi%ABÒ÷ºYä/eaa9+ü¥–Ña­Â/™Áù’,”“B±®‹Ú»‹KBxxÂQÌ×)Èy ä¥_²Øe¤„Zá1¤‘ ÐÄõ²³˜-,,ÖÂZ²®Vx=’ŒJòŽ"^D‹^¹ôM qû3ØH³ÛŒSЉÉÈ +ò+Yè/fa~9¹¢@B?4‰èÎEÇ£$ò Åøy)@-ö'AJ&pDñ3_Êbƒjé—ŽYˆD´xÕ…‰T8?X·ºH±O ‹µ…pçK¡d\†c¹Ãq-ºqÕøoS»8‰¤‘mn/¸ýh½hvñ(êÚ«üU,£¥ù¬ò»IË ù’ .,¨ãPsžA{ºÄ´|| …@Œz)rÆÃ!åé—%¿äF•xlàB• ¯Tà(Ò¼………Åg§oÐwÅÑÕöD5r´9/Ñ~še¸¸4‘¤Ùid ÛŸþN_šÜF\\22K‡¿’v¿ƒ%…v:üN²dKvp˜ªEõðiET‰¢GVûmVû‰VÏ¡¬cÚ9?ƒS’dIÇEún5üP µë´,R™,,,,Ö,ÌãE„UËXµ¶ƒ6½•N;ýÜV†;ƒèöÅ !È‘£Kv3?·ˆÅ…¥¬]!Ý=5rJB‘,§›Lc}a”qƒâPp%áWNüJ»(-õ¤“×Z‰^óüiå‹u…B¡@gg'Ùl¶TCã?`3 A2™¤¥¥×uíMY+Ô­Òµ>2\u^F!!!RÜ"‰Ç·/›xƒiuú9™eyaó ‹YXXF^Ç‘üIØjšx„f5 5M&tgŒ´1iþPÔÚÓ~®LäRéi§&I倖Æ-Ö |ßgÙ²eÿyª‘’L&C&“¡ÿþ8ŽcoÎUPÈQ_š¶Ú5A¬ô°©YÜ[³HÑßéË ·-¢…F'Ò~–eþr–Òáw‘–™Jø`0’y;Êj@Û8YúM¸é„Ðö'•ùHez5´²\ Fqv†K7†IHÍ’ÆÂbíbÕªUëå˜Z[[íÍYCT®ÖeáY#¬è?/­‡GJ$iuZì  ÍíK£Ó€/%=2ÍJ¿“%…å,),§G¦Cú· .d1’7–Ð9ED”pö¦Ê»aí_„J뢴¬+:CCf}Ø"wJblhñŸB6›µcúXå¡4 %F(â4%…f2ðphsû0ÌÌ ·_1EH²2Ï %srói—+è‘Ù€•,BîÉpÄKÄ닜HD{U ž]YSw´)?"2)T¹¹º_O5ïƒ3›­±b±>HvLõŽM¿×ä6YæÅ­<Úœ>lšL›ÓVj™æ’‘iç—óIa!Ë øøøB®Ñ31nÙ¸óÓ‘´n/&í=Bäj?NRyj¬m,,,,>#–ëŠë¨L'ï–—¢>N C½ô­49!*øÂÂRVú¤e†…â/¥:•háÞ@F´ÞMJ¾n¿úÏEd:¢Æ ‘^‡H)U‹<ê –Á@kˆXXX¬ mE M'«-d¸¦JfÑÌ@¯/ƒ´8ÍQÌÂì(¬bq©óN† yY¨”ª•‘fmÁd#©µ²e$µ?,yøÁ€¡[µ…µíPš½T½ªÏR†fµò¾Ë„Ž(ù*3DiBˆÒßËìoMq‹ 7ß|3»îº«½ë5‹“|¤¢ÑÍ]šH1ÜÄvÉ1l‘Io·>’~ïæ?äõì»|œŸO·ìÁÇ/5‘Àhí;•Â"Á§[HÅb/·¨«t ¢ò'¸¨î;Z}ÜQ2Y«‰E—QyE pª'¡×§Â§mIÝbøqã¸þúë™2e #GŽ\ïÇ{å•W2nܸÿø8ÆŽ»Ž©ÜDïaÒLâ1ÌÀö [269š^n±áŠÂ ÞÍÍæåÌÌË/&-sFâ  Ït¢‰Ð8M²H=g­›0Ô‰C(M¿;:›K©œj°|m¹#„›˜F¤8te ‹µŠÅ‹×½í˜1c¸øâ‹3f ‘ï{zz8á„xóÍ7™5kVå³·ß~›K.¹„wÞy§îc 8p­œïc=À /¼À”)S˜;wî:¿æ›m¶¿ùÍo8ì°ÃBŸ§R)Òéôš½¿óáÎI—úoŠpG QýwJ6ÐßíË`·?}œ^¤DÁJ¿›þ–V’–éJxPÚ0:REY)ì+ žu‹ öWú0º¡(¶ Ë9BT?3Åœ—­~Y .¯¹yñO±o¬š÷¢ú¯ü¦kžÈËq-Lš4i­?̃fáÂ…Úï† Æ‚ ð}_ûÝüùó×Zdƒ‚¡C‡2oÞ¼ÈwŽã0dÈíwµÎic òÛn»|ýë_tttàû~…°Ÿ}öY.½ôRfÏžº^­­­|ùË_æK_úÇ{ìZ!òÇ{Œ®®.fÍšÅõ×_χ~X9þAÄÁÌÈ‘#I¥R¡ß¥ÓiÎ=÷\Þ{ï½uv½‡Îoû[ÚÚÚB)S¦pã7òüóϯY"ÿ8¢TgPTºó8’£ÙibÛŸÁ¢?ÍN3>>i™f…¿ŠùŬ«(P0'*Ê(WHÐ`qGÞß’ŽÐ6ªÛË’,‚œ-+Qä8å @6ê8¢iûÁí¥ÙÉQcǃ–¸~ û|È+·Ýv[ä³!C†pÞyçqà 7T % :ÔøÝš&òo¼‘ýèGŒ1¢îïLç´1aìØ±ÜsÏ=´··W&Ù3fpâ‰'rê©§†HŠY£íííÜsÏ=kUJ˜4i_ûÚ×xòÉ'ùõ¯ͦ›n À…^È¿øE®¹æŽ<òH&MšT!—^z‰3χ’ vú³ebsF¸Cirñ…Ï29oç>àì{,ñ—““ùH1¿PkŠŠÏ/à ”ÍYFµý;šŠ÷Jc° Q­Z¨“¢Ñ)ÑÌy¿ÒÊ'ºM™Ÿ…,ΉWOúýç=X¥­­SN9…ý÷ß?’šÝ·o_&OžÌ¸ÎÒ¶Çaï½÷fÏ=÷äà†n ½½=òÝ£>Êu×]ÇòåË?÷)™LF2A'Ož\ów«V­¢¡¡a­Ž­»»›‡zˆ^½z1qâD6ÝtSFÍgœA.Wí‡7sæLnºé&^{íµurÍ?üp¾õ­o¿ohhàÊ+¯ä§?ý)o¿ý6?þ8Gy$7ÜpÃZ“‡G§•M½¡´:­$„‹OU~'Ÿ°´ÐQêƒYˆ$í|#Vâö#"-ê¥6s3ÖÓð©Vó68[u†· d‘kmŸÏëç”À=ÏãÈ#äÆoä€uð»ƒ>ø?R{Ãq9än½õVŽ?þx’Édè»8€[n¹…ã?žD"Å÷Þ{/÷ß?'Ÿ|2¿ÿýïC$pþùç¯38ðÀc¿Ïd2LŸ>ÓO?€|=÷Üsͯ4K =}éÅhw6ó6¡·Ó WÀ*¿‹ósy3÷‹ ËÉ’«XàA"RTþèLÑ`;¹8¢V ÚJΩ(—PâÀտБ‚¿1–häx£ô£k(ªµ" K€ò2ãóV,kêÔ© >\ûÝŸþô'^?z–¦R)¾ùÍorÀ¿Ûo¿ý,‹®Èd2l¾ùæ¼òÊ+ÿññ 6¬æ6wß}7·Þz+BfÏžÍСCqGë#úT2Špi$E_§…ANZÞxÂ%#3,+t3¯°ˆ.ÙM–|¸§Ð×T+ FÚ=ˆð÷GKêÕíôrH0¤QhJ_éêÉJ(vþÑL*áMè²q-ƒ«*¥s£4¯Y€V²°°¨“&Mâ‘GÙ`Æ›Ïçq]7M±ý@-¢‰aÎ@ú¹m$ñB°Jv± ¿ˆ¥…ä)²ƒ¥jG ÐŧÄ1§ÏK¥‹Þ°šIóE¤Û‘N¶‰–Å ònéŒW¥£›ÉdÌIžpÒI'1eʺºº"ßMž<™)S¦ÐÙÙùg:ææ›oæä“O^­ï,Ö=ØgŸ}˜6mšqµ·.aŠp b=öà½÷ÞÃ÷}† ÂòåË) käø[x#éçµá H“annoeßg‘¿œ¼(  Ø Ü±2ljê,[#éÔ’4ê…¬Q\·–d¢Lu3X^W³c}Á–Ï™çóyî¼óNN8ázè¡ÐR2øÝý÷߿Ɩ™«ß÷¹ï¾ÿgïÌãì¨Ê¼ÿ=§êÖ½½wºÓ ! I@Tf@ôÅQÞA}ÅQP‘EQ$‚™ÑÁ *(`&DPFœ‰#I „„B²@¶N'éõ®UuÞ?îVË©ÛÒ çן@wߺU§ª»ç9¿ó<¿ç^Î8ã ~øÃ†\ù|ßçþûïçÌ3Ïä‡?üaL‹5øëàä“OfÕªUH)¹üòËc{×^{-GyäK6žûî»oÔ‰ç¼óÎã?øÇ<<òȸ]ßG¤(ù%öøûØPÚÂ67yŠÚ¶’"@D¾òcN‰É:xT³ÚZíwÿ³znI°S%¦t4–C¢ù):7İ#µ¯¦æ´+Bé/â•›ïÝ»—o~ó›œuÖYüæ7¿ E#ûöíãÚk¯Õ¾öbøƒ>ÈÇ>ö1®»îºZÆJðµóÎ;o}ë[¯˜Œ(Û˶µµí÷ûÚÚÚ^tkÚ––Þõ®wqÚi§qË-·°}ûv6nÜÈ5×\ÑGYË!?ꨣX¸p!ßøÆ78äC^ôgö«_ýŠO<‘O<‘ÓO?]»v…^/ |ò“ŸäÉ'Ÿ¤§§‡SO=•_üâã:†?˯—­îöªJL?Æ{"öÿºCTTÎP jÜÈJÅ4ô°i€N“Йaé°ô±öزl‚âJÕ,«ºá3{½œªì ^©ì YÉ”¿v§§ÿW5ÂßjAÐß"ö§ èöÛoçw¿û÷Þ{/£Nª–e… ‚>ò‘ŒùZû[4<<ÌêÕ«ùÞ÷¾Ç–-[j?¯w¾ó¼ûÝïþ›(Z´hüã¹óÎ;Çíz{vôѹE2B.Òh9äP¡W1y$.¸D©»‘Òm.¡Fc\ª-úÉ'©*§Áéí>$Ž‘sTðæ‚DîèÜ ˆÜÀà@ˆü°Ã«•è777é=Ùl¶V¢ÿÌ3ϼ(D¾?X²d BV¬XÁ 7ÜP#ý—I%ú3gÎdóæÍãûóݾ gk1§ÊX³5B¤Îቱs”L“2ô¢s”N}­äR™z*•šRDÏCÃI`t"W¡ñ±¬t«¹Š5~ԉݹÁË‘È_J¼XD~Ýu×½¤AIxÕ«^ÅÓO?ýâÿ|·ïÂÞZ¨”ç‹ÂE¤Pp‹³žá6šÎ:á0U×R­QaPB©|•3U}Õ’DݺÍ }Š¡ 5”‹ïV*M¯";i  K¦Ñ™®Œ>ûÙϾ,ÆñRx<6ZÂKö'Œ§è…‰™X6G˜ð…–…6–nP)TÂØ’I\4x5ÚC™hºªç‹¨¯/dtI!"7«ö…±²5007Ôwç®àÄTኄ>*TÎ íQ´îãGxŒ½ë”Hgx¼:Í=ikVŽÂ¹õû’úvBÆyÜÀÀà¥Dœ…D$¢-€T±ø^ T“ȰÑùЪñ4Ãè¤>#áÚ˜ÑJæElB¨¯o\VÓ%Ã6:GE¡%æÑäQ‹ˆœGÒà„†¸ þ:Ä."9äU’Œ:‘è"ñd‚,Ÿ§\ )Ñ—¿e’ÐÉ@<ÏÃUµø))2ibS± §‘ë, u%¦I³ˆÁø ,BK”*¡3Z¼¨gƒˆÒˆ>QÄtï¤Vå-ý&d¸:µ>=È“†ÆõòJðžHXYÈèv¦.S\%Ì@ãGâñV c•vUĬV· o.ê#k©=7ÚöIB¼Ï½îÚ½=bü•´š$ ÙÁ)ÍÌVE|L‹Áø#y•/´Ä­#9Ñ è=h_[}Ŧ•¸ZáF´‡ÐRvB„Zaè'.¥)ªyRäí¿@9ååj=j``ð×A#Û_¡‰Œ“6GÓ­ƒÍ‹EÄMh˜s.&ŠF–²IÂj8©Ô'…¤éJT+ôM4 ¢‘Çg¥±³Ý?2ooo7¿½ ŽAXQ±¯¤†$õY-ñúȸF®õô(qˆ£ Ÿ?<=èUùú±aË‘0‘%=!™(yǼV‚uT£'ÉŒ‡´"5$%F°"Ç5j/Ðïï©„Âøpä­bº; -å”&c=ìu õÏ >F§º¾’ÔVÿ«Èþ°$è€` ³ ^,252èRÑv2 …ž: Z1 Iê,LˆÉqå^O–Ôå§‹Èçc œ£%ûÒ‹<ebpƒ—‘®ƒQVtr "£ˆQVÉ¥ýIWVMƒ1©Ñ2™0ŽFù:ÕcíðžoØ1Y°7än``0¾ÒJÐÖDæDâ^@#{èÚUƳσ´+’M]}ˆŸ_%¨ÉÎ'ñà8j]\gˆX„®´RNTÓ®¤h ÍìhÚ¼ŒoÜÔì,˜ÙÞ¬¬§èUUjŠÜu—Á¢œøæføº¢1G§iU§lÂ:ºuE Ë‡ÿGùZÊ?Üꃉ®\¯ ØÇ…wüž»æCÚüÝŒ;‘'¥ Z!¡p2Þ_8L„²òž`9¾$î'X“Ï_þ°*ç jøÑ,˜h¶®Ô^.Û×ñmxG@×­HUÆS~§Måa©†3ä8ltŠ.NY|/—½1jÊ_dÙ‚“¸èÁûßË¥ù+8ñ’ûÉŠÞ¾à.ÇDÒüRŽ]›xrÙo¹ýÖ;Y±Û‹œ?ßø#ônÙÂæÞᕦÁø’yrJžõýå•x¤ÓøÆÞÉ8ZR•fF+ý'"Ib%w[÷°â´Ìi90”»™‹ox˜B`HC[QDmJ-Ú&t`­¾…y‹"g·Ð=ýhÞóÑ‹¸áM³˜æU,RpüþùFîü—s¹Óü½¼($æ}šDu~¬-ZTúˆw≺ M+4ÇB½Ê]h^SZâ‰ùµZ ñªú¾ÝX³ «1ãAäjp «W®$;ÆiQõobÕ«ÊǯXÎÒ )î¸å¼÷Ø…,_’ÛÏã Ì8õj®ûä™ÚáàöoæÑ_.â7=D¯ÈnÞü‰ÏsÞÛþŽ9S;qJ}<ðͳù·ßvpÞí?â]Ë?Ư_ƒ'š9ü}—ð/çü#¯>¨ wßVþxýgøÒo¶›ˆÝÀà€%–pÑ}tOoJŒ&jê "ac2‰Hë›°úDE]ïN¡½v|¢ÐOá3Žf]üÌŽÎaÉb¼rÈ%–eÕ´/¥¾?vúórY Ê&íX/àxÅž'îæú/ÞNïLzÃY\rÁW¹ø™÷ñÅß¡˜À«ßò¦=w_¾j%ƒV;ÞÖ=ø‘Õ‚œý¾ò/DZûæ/sþ²ÝÈî™´îê3$n`ðB‚»‘pRßÎhÏ¡9žò’eŠp4¥•1tµ›}¢H0'é¾’ ¸‚ãP‰×®¿ÏVÚy-ü`‚¦Y ç„<ðÈ‚:Ñnº•³O[ÌÚ$6Žãà;­ôÌ<†“ç}ˆ9…ǹkeö¯Þ°œ6”]ûô Ì~û9ùÈ™X¿_[yÃaé_Ö੪_vvÑ)yø±GY½.i䀨¨X')Êf”]EêL£î‡>q«]Ôn‹³TôM}õÑþ ´â;\°pyM#W…>6yÉ'uŽ¿‚ß/¿¢rp‰K¹éÒ«¹{»ÿŽw8ø„ó¸è¬·qä̉4(¶Xx«ö¯+º»ê.n{äÿò™îàÕ¿»›;~X·Ïü-¼ F¡º¶h2’Ÿ-j$GDKݓҬñül"×͉1©5J dQk9—¤_ˆQžWMZi<ƒèê•„ÈÕÐ6Ö­]KvÌÄ#ó-#[Ê1°{;öæJŽ—³Ïયÿ—×pù5kÙ£fpê_ç„ý½‰â~vñ)<ôÞÍ‡Îø_¹ý,NÿÎ<>uëÚÀ&®ÁþºÞ‰0_ÇI»û ¶úYÐtD4Œ¾ãÌ裯À„m¿õÍx„ÖâV$0°Hv´ÒJ˜ýËßôk~awƒàͼdúÙÐs¬}r͘‰¿Ññ鹯fŽXÉ×oú *#lx‚‘ÊòÜÃwríÃ÷²dþmÜtÚ?ñ÷·¯å‘’ùƒ40xá¹> º.’Z½9N‚J»yª´*DRþ^0üˆ<,«¨„ì™úh qF Áˆ<.§(M× ñÈYù뢸i[Åi¼ÿcï¦÷¿6ÐïMbjëþŸGN{ §¾^²qýNröA¼~V; ö3`v; ö~i¡a,5J›äY""×M’B ±V¯B½7UÃ÷1ÉH„žAÝïPŒAƉޛMl.$­‹îû¾‡ÞºÛøòÕ=\zö%,<½YÊ2Ø·…'ŸÚ¯»³»çògý3Ÿ™Öã±í©?rÝ?â#’Ø |?H]$D½²BÏá¼oÓ›%ñ>IIQ{"Q~ðÈF­ß¢ÒLkuöXÑnFIeIâò0<­ˆÇ«Ÿûzÿê÷òÓSLš:Yû€{{{Mc  ÜX"©CPïö]¤·–j¥ôªV /kdT˜õ À°¡m¼ü&©¢2:Ô sTb$LB$\åÌีpnŽ IDATTd»T¤iŸð¦ªnc×DÞ2"…+GË_ÙQ?®¨ý¹¨î]n```0ÐXé"hQõ:¥‚Œ¥ï2|_Ôâ*8= ÜGŸã"¨YXéìêC8ÍQTêgê#švt20Τõ(KÁh®‡¿k```p D7šÒË ºH›!뤡õè†gh…¦/fãn¡z¹%*‰˜ —^Çß› =#ýšB¡áT›è‚#>L¦‚ÑÀÀàE¡tQëÀ©4ÍÊÒnõ‘@þ*Dž¢&W‰ÄEBoŸøÙÑL*¢¯‹H²ˆŠ¸,–%êÚ}‰úõ½š|ˆ¬„Æ¥"K©e‹ÄOÕÄ›ŒTBtœ¤(Í»…æ!RÔi0‰¦ƒG%‘`"£Ð4šˆA$ê¸%@òÔ—.cP„öʰ±p„…­ë]N¬W¡Ë‰DGƒ'óè`Ô;<Tꥉ Î]×Ñ£ iÊ ¨ðv©ŠP­¬B€R‰ÅD"ÖßX¡õ.TAÙHïy–€DHkl!i¶°ê%úJ[ÃÍ“”¡qƒqUШñ¢Ÿ ñ)m÷¨Ä¢b%öÉÆ€‚`ΉHì‡&"®ˆñ[ƉÈg~芢a~ô G¤È‰$O‰¿ˆ-øø‰ ¨oRFV100x‘H\¿3WW̓4+B•hµ6¨˜xQ'}éPL‚»K8ªT£v("J’O„F‚‰ªÝ‘÷WVѵƒH(ac!ñ”‹‹_ùP刼ü˜üZã øFƒuF300080"OÚ~Tˆ щÀÆ¢u ¥,WÔÙKÖ’ÃÑp™3e™ïTEBQ*æ .Ö É«ÕBhŠè¥fZˆ­!DTo©Oi[”up¸øT JÁ´µ-U/Óqhúf```Šd%h²2â’‰B_HôF‘ˆ2ñ‡ô:w©`€÷?²n^&Êpæx’ßIˆì+üÝQŒn·ÖƨT`tªFàia“‘B²~‚_ªì]ÆW¶þ‘‘ƒ 700oø-GSíÂ-(¥&ÿC†ây¥y¿J`· Þ>6á8êÆ(LPai'9…Pd¢fá–6Ò²vó”|?FöaßuIàÖ‰):úNq¯X2—“ùÇù ùÚ‡f¿ “ƒFòJ´\¦*Y„ÉQT£Y¥*²H$¡¯ÂÛ¾R¬ÓoRÀ*#rНü€#Šqôw¡”žü«E=Ñœw‰Ä’fáÐ)›±„$ï—(ŽPôK•±Ä'Ÿ`&¡„p­“„*?(‘ð^¹žmÌ~ñ19c6} Æ‘Äcåˆ"Lx¨äÎA1ÞT"°¹T<“„ˆ|HÂåôáSªxÓ Q•GÂ…?"@’!=½r|ù_¥³‚´°i‘iš¬>9¿Hòf¦ È0Á÷Vˆº|_¢âµ¢› AoÝŠÜS{8ÁEÏþÿ´º8eñ½\öÆh7ž"ËœÄE÷Þ¿ø^.Í_Á‰—ÜOVtðöwpù;&’–à—r ìÚÄ“Ë~Ëí·ÞÉŠÝ«ÁØñYv>˪?ýšÞúKVí5õ¨/oÄ{ÆÇóÄë¤T/ö±0X`•ýG‚Þ*¢±Œ"Bì«B:»h`*+Úㄈ$Gªð¤"&‘&mÙ”|¯@A¹õ‰FˆQbÿº‹]½¸ ÔsªH²½7ŽŽä¥ÇnæâtÒQ mDEƒEÛ„¬Õ·0oÑCä캧Í{>z7¼ióϼŠåCªññ3^ÏûϽ”ß0… Î^Ìyó§b`ðrŒÉE¨(]OŒI–®Ôä™Hy±)C©D¢ Óo<Ã]Á(åý¯n«òFfZ¤(â1àæ*I„Dª7GcÛú;ìøUã†2Y3°=PqE naõÊ•ñ> §Tý›XõĪòñ+–³tCŠ;nùï=v!Ë—äF=~ùŽvîþ÷“y÷ßßÄ—@öpìÙó¹ðÔã˜Û¥Ø³þOüâúosÛ_úê~ ²›×Ÿ9Ÿy8ŽÃ'Úd{×òÓ/Ïãû«#Cn=Š o¼ž÷îYÈÇ?w[Lw ƒX“¶ˆR«H~G8é.¶ëWÍ÷ð4“F¤½ˆÖgVŽªG§™PŽ{&U-«°š÷ž6)a£PŒ¨".^ÝgEÕ7i}Uw?,?Õ,íà0D|Óy’¾3vH,˪íP+¥ðý±Ë^.KAÙ¤kLÇÉ‘!“@š×^°ˆoXqÿâ/±h£`Î{>É… ÿÌùçpãš"æ5ç/báGlþç¦+ùîS¤&v2ô¼þ%rfrÊ•WóAÿ'\tÙÝ†Ä ÆM`QåoQç!%M&7!â-Ò¢ç„h–Jd&P>Ñ$ê|3ÃUáö—ÑîAµŒ!°±HËWy•‚xV Šuö T€Ì«yïA%ÈÆÝJꦄÜÆøµ¾qNXÀ,¨ó¦[9û´Å¬Mâraã8¾ÓJÏÌc8yÞ‡˜Sxœ»VfÎîÒi¥gÖëø§ ßË´}KYüD ÑþvÎüàLÖßr:WÞ±xôñ­4Ïþ1gžñVnÿ†ÛÞÊ™§ÊÆ[NçŠÛ7‡ï¼:[ñ¶Ë>Íg¦ý‘Ë>qO ›¤Lƒ¥o"¦Pq{×0õVÛ£E7@CQ1åÍA¿áW#ó`<ªD}ƒtÉŠ•t¿ /„¶PR Põ AF¤ÈØi|å3âå))/>qU´ YϤQzk]SŠXóerV!`+9(­ø,\^ÓÈU¡M^²´â¿_~Eåà—rÓ¥Ws÷v?a¢øKW|­r¼ÇžU¿äk-âý ëÈ#8,³‹W<_—Q¼­¬x|7çw3¬%¬›ùjo*ã%L“ß÷e¾’ÙÉmç|“?î1›¨ã%­èͳÃrB½²@ÅDT‰¯sNTâÈôº¼Œ4_eþQŽÂ;d>Šœ[  J‘föB{þàÓ`Lc؈è㕜zSÆ8ïmcÝÚµdÇLü72oÑ2²¥»w°co®¡zñÑÅ|òº‡Èg^ÃG¿þyŽYÏãF4‹£¤'4Zƨ¢åž>ô$>ü…óç ¿ËãC&"70xá^åY vcwbïL­´€#å×óÌ… OŒF°JŸPð4N IZ¦p¤MÞsë%õ‰BˆË?¡è?ò47]í*Thµ®TÄMg¢ZÓK¶àz޵O®aíºgÙ6 ‰0¼ƒõëžaÝ¿âk_ü)}¯¿˜ËϘC ð¶<Å3…É}ÌÁõ±5×ÕCþ™§Øâ·õ6–&sô1ÓHRá ïæß.ø*˺Îâ›W¾Ÿé¶ùc40xá¢JP“Vµ(Wg9•Ô´MÄ: M~º¨m:–s±E8w0ö>{o²TŽÐ3"E³L“9¿HN Óx|]Ðh…2ÖµŒÔ9Œëü ¢^d ¿#—¯Þ¶•W}ìóœ6ËB >È~¾™¹ç~ƒË>ôVŽ9æx>üůsîìMÜñ£¥ ªÿüä—Û8ìcßâ«g¿“7s ÇžðN8,:»»í>¾ú¯·²ãuóYpö«H›¿GƒˆÈã}6£dõç–È:CÂzE¸¼}tÒTÄ Et,Ó*¥($’Ë¡ÃnF Å—#ïñð#Ö„¢m™Ìj©à"\¥hÜ Bêõ¨$Ýæo­?P5ÿñ-îì}-ç]t“džÕß½˜ù?ÜÂܳ¯dñâœ9ûY~0ÿ"nZSUí³¬Xt!Ÿ½u=ÓNý<×|÷®ùœphG,K5·æ¾rÓ3zîeœyxÊüE¡ë¾#FòªJ 4õ/Ý¿Ñ4˜Ó¹¿Ö¿ï£H)›6™!-Rô»# {…HíMx›RwK‹ã¬#Tp°UÛ`‘BUòU-q}dºÅ¤©“µ'ííí¥½½Ýüf088ȤI“ô\±}-[½šé• Þž V¨r•Úe€€Umã“Äâ¡xýK]CW™G×>¨£—^¡ÍÒ!…E ‚òp+yêI²t2aë²ß+߯ÚÕŠ°)n}’嬕è’FJ«Ù‡0ƒýÉU(W.Þˆ8ÌIÉQ»ˆùŸÍ{cƲ¡×UBþ¸@–=RdŠ”°((—¼*·yˆGØ*¶}Ü1&šë^o&V‚RPn,ì:l€®hT"d```p`’JYk®©ˆë Ð¥$ܰREìeEH-wõTë)­:¯bÚ}uL6-Ò!ör¸šb#"S‡@$w¸§¨åŸÄ³xôòQv´†¾‹sT2Dn``0>ˆFÀõ8;^t“ÔYxÍC'.&5V†V» [YT‰¼W¬hẳÕ[]ˆQ®¯bí'„¶ÙÄ·a«€¼‹«B2¡I‘ÁFäB9‡e“h~J’mXŽP ®…õJöz´+ÎN% —ån=>dý%¼ªR‘]‚©*– R>F…ÚÃ銙’¾Úi¤ˆ„öI3ÖxVw4Š‘ƒá£hË';AE9+Ú9¨nk›——Ïá›f‘FVrÃK§pU«âTšë mð+jRŽŠ]Kh^in3)è.;¸Ü2Bi6 ^|ZGÉ*ÐYGG¿Á R#I¥ÑÍÑ: ZQq+l|Ã^ŽR-g%ì}¨4BOxÃSé!ñŽDlw•*Ò6™‰ØØjˆ[›c"sƒ—0VëãÉ ‚ÔÍ¥Q‹}£t[}ÝF‘Í2Ű—§ ¼šc¸ªy†µêè¶fòjCâï¸c°ÊUDZЯk I›Õ„‡_ÏZ‰>ÝæƒnSÂÀÀÀàÀ£o´B„%† æ—èÞwM 汈ØiµÌÞ’ŒHa ‹ÁŠ[aÍ3~Þä6*äwm§–A¥ UÉco–i„€a·*Dª”èëg?•ë1ö©·‹SßËeot"/Y¶à$.ºGðþÅ÷riþ N¼ä~²¢ƒ·/¸ƒËß1‘´¿”c`×&ž\ö[n¿õNVìnÜ~.õ¦/sÿusøÑ‡Ïæ?6›.>/o:1OB¡£ªÈñ L]ŧŽXEˆãÊgËX)Ò2EÎ+Q¬µ` 7›S «îºô)"BT,oë]„Tƒè½ÚÊí!22…mYŒ¸êù3•k ®¾ _h涆Òc7sñ ×Z½bhë ŠŽÈ‘m:°Vß¼E‘³[èž~4ïùèEÜð¦YÌ?ó*–›Î<óÒŠÐv9[eÉXŽH\Î{"&© uÿð&é±R –r¸ÊPsrn¸ˆ±bt\J{\°šS‘”LO&i±2¤„$ë))7ÎÌJéóÈEÌåDß~EW„_n ­h¶Ò¤„$ ñ }mõ*vüAUvA…ÀWá9i|J$–eÕÚ¨)¥ðý±K^.KAÙ¤k¿–n{ž¸›ë¿x;½C0é gqÉ_åâgÞÇ?„»êf>þ៕g5{&§,¸’÷¹¿ã¿×y¸~òkãKà ¦‚*rÄTÈ“ ”ŠXBFãó— ^{ÞNwæVíÂëy:÷hÜÕ×ñ—~EqÓ¶ŠÓxÿÇÞMïm ß›ÄÔÖÀþ°æ³œÄSÿqÛ»evwùnGvmfÏÔä×v›ØÜÀ`\6Œ`+4-*1gU“®6V>ÑŒK”Û4K!h’ìf5Íd’=™¼W`_iÃî0¿„K‘‚WÂS~ =D9¯Ü‘iéiR2…#Ò–ƒ#ÚdÄJv‰Éª‡!o˜ÝÅ>ú½AFü²ãâE<ÉUÌ.7¸íYM!±„EFØ)ȺEüš”]ÏC¯J,B£×Ü%ñòV¥õè}é ›Òž}´Ÿþq¾rV©Bù>Ÿ¿öNž÷u·ñå«{¸ôìKXxz+²”e°o O>?„Brð›ÞÊ\§ûüïpÇùÕ“yà‹ogÑä¤×Nä’û²æ/ÐÀ`¼ÈœHú¡ ZN‰@Ä.‘ªjW«°…EF–ÓôŠi¦ØÓ%'âú»ò½l,ld7@¹E²Jh§S'`‰ %,Ò´È V+]©.&Xp¬4íV;vÓœƒÙçîcgqÛŠ;ôG*“CÝþªÊ­~Bo€¦ÒvÚeSÓ“é´;iµ[˜eÍ ÛêbW©—mÅ úCõóªv^•K2Ø8"EÞ«›`U³WªRJˆÀŸûÁ¬•š@ è ŒÞe×ÀÀÀ`eQ-ïor"‚_Ka‘¶,!)ùnù>h“-ÌqfqHf¾òØãîã™ÜFöùƒ5M]†ÊîU¨J2”w¢TÜ\Ô(âQR.Y/O¿7À^o“S=Lv&Ó™ê Ûé¢Íne‚ÝÁ¦Âöº)U&*rŠÀÂ"# ªDIy± P!¢i˜DD @· ЗÄêÒÏË›¦?Áø"œ‘RHËß”ù!c9xÊg¸”ÃEÕÞ“&ÍŒôT¦gÁ–{ ûXŸÛÀo%• þ(õBúçb¼7hhå¢ð‡( ñ|q3ÒÓ8$=6»©™©tZ¬ÏoâùÒ6òª€_·…EK*ƒëy¼Jó !B¦[Jw¨7˜"¤ŠÈª×J]ˆ×G߆º ^Ôˆ<‰¢f ‹6™A .å()/ÔæM ˜•žÎLg&Žt(ðdv}^D6‰_O\[ªä­3ÞŠ’¸"¾1 ‚C¥\™ U0Ù$>N4« Y‘€B%³M 1FÕç¬r¹© „ü†Î ÆGV ºÀ‘6™T×u)ú%|&|›‰V7‡5Í¢ÉjfØbcîY¶»½µìFQµõtFŸzƒcˆÚÎ6&U]N·‡Ïˆ?B©PÀõJÌb&Ý©.zœ‰¤„ÅNvò|v®ïjuújÒ‰B}ë˜× LÄÔ£7d"rƒñD´rÆN‘–…b‘M8oEo“½b``ðâGæ-© Ë¡è•(PŠ7:d;³2‡0!ÕIIÙ”–-Åçqqµ§LBõrËèï^]û¸úWÕ4B[”Yµä—èJM`nz6“ìIÚsGy7øyíš•ÙÂB!]®ì¬¾ÙèAå£Emt"`¦¥L¹Á8ÂB’)R–M¶˜Çõ}*>V!bm¢‰¹™CéIMB¡Ø”+GâEUŒEͪA43 dˆ ô&XAiÇ×MŠ7ºDàà0»i6© ¸Êe_ivN'¯s)äé÷kVÕH»>¹ le‘©Ê?›fÙLÆÊ±2¤EG¤°eƒ({,ÄÀÀÀà@aK›´åw‹”ürÛµpn78Âazz“ÉH$»‹»Ù˜ßDÖËâ‹d ‘ ±Äke‰‡J³b+¬ŠÛØ"ÀÁaVf“œIX¢¯°‡u¹g˜Ñ<Éö$:SíÌÍÌæéür~¾â#I‰¶°ËÄ-mZd+Í"M³lÁ–ŽpÊ“ž´‘Ø(ác—7.U8/±ªU_3 ˆ/Z4.IIW¹äÜb¥Õ¡^ö)l&§&2·y6)‘¢¯ØÇ3¹ åÊÉŠ[btÓ4ŒIÜÈQ'E¥ù¿.2^åÐrF¥,&ØÞ4[Úì-îe]v»¼=FJÈ&É´ôÁÒt%¿Ä^¯ŸŒpȈ&ÚR­4Ér´mI»f»«*®rA•«L‹^žœ‰öì¬ß¤Šü3000x1ÐìdPž_îÚ£éÒ 0ÉîfnÓa82ÍPi€­…çè÷t·¯“¬‡^7ORtECD$•˜ÁW$–ïNuòêæÃHÉYo„mÅíäÉÑcO U¶ ”«Üò*£y:‡pH9]Ȳ˜(O.®ï1âórä¼Y/O–,#^–¬—¥@¹ƒMƒ¥F’Fô‚ˆ]tqÊâ{¹ìNä…"ËœÄE÷Þ¿ø^.Í_Á‰—ÜOVtðöwpù;&’–à—r ìÚÄ“Ë~Ëí·ÞÉŠÝúž™¢yÿx湜öŽc9bj')w€W±ì—7sÃ=ë0~…/ˆJoàb%OF¤ tÉfd¦Ób5Qðò<›ßLo©·’+Ͱ“£vôxï%¥ ¥Bv¸m[9ÿ½ÍjefzívGÅàËâ`g*SÓSI [Ø8ÒÁBÒ2ç{¼ýÞ9•-·Ê‘õ UÏw)QÂU>žò(𪚇Œ]5h Î8µ› lvŽW\üÒ‰Ç IDAT^zìf.¾ááZ«7P mDÑ[pµMèÀZ} ó=DÎn¡{úѼç£qÛf1ÿÌ«X>é?Ýv ~çÛ|túN¸ë|}ÕNŠ-™6çï™ÝBàš/Ü"¾òC=.eErɈ ³23™àtâáñ|aÛK»È‡¤q0š ŠšË¡*W½#´)€ñ”¿òF¦……-,lY&æ´phMt9]ô8“HË4>>;C† …«øæÙüÛovãËŽ={>žzs»{Öÿ‰_\ÿmnûK_=“§á1Žûʯ¹æðŸsÆßcCÙ’ž|‡ÿ¼p/ÿöîËøCq Ç÷>ö®×3g’dïSpÛµ×r×Úa#a¼ò ÀS^ˆDe-”³9Ä™J3‰ÅÞžÉo¤¨ 1 Dh%êx¦"Ä.+YçUOovT@B©[zI!i!M‡ÝN›ÕN›ÝJ«ÕJ³ÕLZ¦kÅF>>EUÄ÷E¿Èˆ7Ä ;Ì?LÖË‘ós¸ªÈëZfræ Ë!-rnŽ"nÚñQ“Võºßf˲ê¹êJáûcï´ãå²”MÚ±Â/dÞÀûNšFÿ_Ë×6P&ðê·¼…iÏÝÄ—¯ZÉ ÕŽ·u>i^{Á"¾ýaÅý‹¿Ä¢‚9ïù$.üw2çŸÃkŠ0ê1yV.{ŒÂ;ßÀëznaÃ.háè7¼wå·x<×ÄQ/âêwìà‡×]ÂÕ»:8öÜås×ÌgÇðЈù»6x¥ñ¸ éÍõÖeV³šf‘ý¥žÉo$¯òŽ•Éyáõöj¢aÄž”’ô#ÏÐD»ÕB‡ÕN‡ÕA{ª•&ÙŒc9•B¥2ÉûªÞmh¨4Ä®b/}þ^FÜa²~Ž’ªÕV-rËÖ6–iºœ.m:”¾Ò>Jj04)ôEMÕÛI±¯y8ã•;',àGÔ‰yÓ­œ}ÚbÖ&q¹°qßi¥gæ1œ<ïCÌ)<Î]+Ãd-'ÍbF«ËúÕkÉá×gxã#,ýËšš#ƒh?ž3?8“õ·œÎ•wlÆ}|+ͳÌ™g¼•Û¿°„‘±óèRs?ÏqoìäÎ{ö¢š^Ç›±XýÝGl;3N™ÄŸ¯ú8ß[ÒÖ]5…7ýêã¼í¨«yè¡¢ùË6x¥æ!Ži·:84s(ié0ì³­°(rl8ÄÔµ'Všh=v\ÕæVHR¤ÈÈ4m²µiËr¤¶RÂF +›•% ^¬W@(EWª‹´•&ëfy®ð϶PT¥Š’í%Ë¥vØ;i±Z*]‡¦P,É‘UÙƒ>§FäáMÌz'9?ò@ª¯HyiÅw¸`áòšÂ¥ }lò’¥çø+øýò+*—ظ”›.½š»·GKÕøËN3‡Ÿ÷=¾}ü#\zάI˜…¬GpXf®x¾þÀ½­¬x|7çw3¬%¬Ã1kúbÉ_|þõ­ÿ@ÇÞKþèãycz%7>¸9ãpæ453õË÷±ìKõŠò)ô´ (yÅà•Kæ•Ä&ÑÄTg2v®rÙUÜE¯Û‹[‘¢w¨Š2Dð*q°XصF™&c5ÓnµÑ!Ûi±›q¤CŠB–Sý ^‘,9Š~‘¬;Â;DVå(*iÎ&ЉxÊcga[‹ÛöGÊãÁ©%¢¹EJì*îfRjÙ“™’žBŸ»¬[@ˆhêwØJ¼z>»º±ŒÀeˆÄ£a|t±²Ÿ?¬¡m¬[»vÌÙ#¥72oÑ2²¥»w°coN;³ù»Ÿc[ÞâïŸMŠG ƶvs']Íõ¥ ÒèÔŽQýüé¿þÌçþím7áì=ñ82+óàSø}ÜwÅÔEHh\Vªi*ÒêXPïkš Ç™D©Ÿ5#kØZÚh&i u¥U=êG×v,“IäTžåý0àÒã •ªZd```ð×B‹larjM2CÁ˳)¿‰~o0"x—i1Cšv«•.»‹‰én:d;)é`K [Ø(9/Ç;Ä@i€=nýÞUÂCUtr0Œ&W«…V\iMLOLgjE¿È†ÜFv–úÂ+†™Ëˆ ”$NW76wvÑn·c ›‰N#ù C:ITu·ƒK€è°U$GS•Œ¦¤&Ñiwâã³³°“o‰E›ŒÌÐ)Ûèrºhm´ZMd¬&ÒV¡ ¨Š ”†* 2à 3ä‘óòT±¦s«H:¢¨$R'ÙÞ†µè:? $ÓÓÓ™”šŒ§<¶ç·³³Ø‹K)±‡¶Tˆ?e€ÌUE* º7îs÷‘õrt;]tÛ]l;(ªRhì"$܈zF^ÜŸ@„fèW )$]N7RHF¼v{±…E³ì E6Óiµ3!ÕA»ÝN“•Á&…RŠ¢*±¯ØÏˆ7Ì7D¿×ψ›-“7nL§Ö%ÇI\Í÷–×,,º¬ LËL#%†½a6å·Wù+Æ[I‹Z³‰0w–£vº2@ÎÏ1ì Ó#&2ÁîÄ6B‰ÚZA„4÷JDVˆTlÔ Æ•È‘4YMxžËžRiËaª5…®T'V;Mv3– >Ã¥!úJ{ÙçícÐbÄ©d–èüÈÛŒõèºît^Έ©Ç¸*Ö½Z‚´H3§éPÚìVò^ž-Ù-ôy{bñ{TÑ‘sEuBS @‘aoß÷i±[ɇa~¥èÑW^dšð#Í—có”Ò\›ÝŒ="/w³—Lu¦‘±Ò¤dŠ´L/È»9F¼aúÝöºý yC¸ªh¬ì‡¶Ã}…D¨§‚Ô¸‹DOÄ 4¦§§2%=!`gq'[‹Û"-BAp”;ã¯;BáY?GÞÏÓj·ÒnuÒï Q¬¥5Æ|;ªç?6gÄoÔÀÀÀà@` Ë’È å¼,{ åL“ao„¢_ ¤Š!Ó©ºæœÌJÁ*Ï0}†©6\`'Y)$ÝvÓ33°¤Åîân¶¶Q¤¨¼µÏuˆGì"ÿÇž‚Ÿ§¨ ÑF›Õ‚-, Üíi–Ïi‹š…cðt2 uq?òh  ®ï’W%Š~‘¼—c¸"™T·’]oÜ 3¬ÕIu-YÄÈRh¶9äÔ×[e+‡d¦Ín#ïåÙTØÌ>o°–ßtÃc!”hTN& ªëU¢àQJÑd5‘)„ÊGVõwj;Eƒ‘]L™DI"D¾–ÈXÙç{äÝ"¤!cgHYvÍP'Ù$¸¨„k9ã3šéäi``pÀѸrÙZÜF¼Ç}˜ì¢y Iût"òZP7Wò«¿.£¯FØI·œÀ!™ƒi²3 Ù[OAk]‚¼ÚYe` Ž›Ñ®⹂AåÛãH!DÙ¥Q9‰Ñ¿@7;£Ù“õ$¥]Œ"7008Àˆ…‡K5ëD>‚‘®µ£p\[PbVvu>ók¬"î®M2Üæ94[Í —FØ”Ý̰Ÿ •IŠ¡¾ LC"—¿ïG&›rfŠOÖÍ£T9Ï\ÖžN¸¡D@5Q±®ÓQY>¬“ ÷2@öð†Ïã¬7w‡¿Äᇈ–Y«Øv ª Šˆ+q’‹Rw<¿\Å¢õ²ñÖäÔd¦f¦ ôûØ^ÚYIuT•qû„3üâåT¬—OxdA#°`÷ Ÿ’ò,!±¥¥ÉNPŽJý£ÕI"0X‹FäSyË?„·Ím5ÏÂÀ`ÔøY…Tùº^¿¨g\‹˜äŽ€uÔ®bUž*Ä{åN»“™™8–È;ÌóÅídU6tóM‰Ë@á‰"˜¡®/ÿ×Mº‹ºn­‹(2êñ¿¹èS/,ýPtqÊ ËY±bEäßr®ßDåõå×¾“fÑÁÛ¯¼Ÿ‡y”G}”G–/å¿ußþ—sL¥½Dú¿ÎÒG~ÍŽmѼjqèÙÿÁ²?ŸÓ§š8ÙÀàåDåáXSÄâé ÆYJ/Ù(mÛx‘×Ï›!ÍÔôAt¦:p}—¹gÙSÚ»f<·•¡H½>?Vx©"Û·áaWõx•0áÕôC]cÏ`2L°Ëõu*=v3ßð0…ÀÕ†¶¢èˆoÛ„¬Õ·0oÑCä캧Í{>z7¼ióϼŠåC¡Å&M$eMὟ>;W|Ÿ nà‡5ñÿñ©³^CZneâÛÍŸÁˑУq­ ª2‹˜jÔÇ“Xt N©E¶AûàjálËn£·ÔWóGCÞAb¯nÙJM€,"ŠG°|IF¢uKTT0wNXÀ,¨ó¦[9û´Å¬MJ„6Žãà;­ôÌ<†“ç}ˆ9…ǹke4¦wèìh¦4¸›?ÿøÇ¬ü§óøç7þ”˜sÊ?ó»ÉÇ–láø³àU]HQíÇsæg²þ–Ó¹òŽÍøÀ£o¥yö9óŒ·rû–ÔÚ·e7?ƲGÖàñ(öNåØ[OâÍGØ,ßx§4‰57~Нýl[9ŠlÃN>Ž£ª·Ð~gœ2‰?_õq¾·¤¬»j oúÕÇyÛQWóÐCEó×l` Ö«d.öËáI¨R„ f£’Kù¿6’6Ù¬æ™dì ;r;ØXx·R—qt¡­BïpNŒüUÄ¡%ZŒ)Q¤+Í3||<åŲz‚\m«PЭ•Šš˜'-VÆŽÒŠïpÁÂå5\úØä%ÿ„œã¯à÷˯¨\b`ãRnºôjîÞa~ÙF{; ÷ãíz˜Ûî?‡o}ä$¾¿j'§Ÿ2•¿Üøsž.f8fÚ:ÚÊË—GpXf®x¾žPémeÅã»9ÿ¸#˜a-aæ6ýíϱCuÐÙ.±¦ÏeVj'xbgbR¦5ãpæ453õË÷±ìKõ¹ÚNùzZ¼bð Ê£wxƒ³ºo§³}­ É}‡!h9; ÅjæÐÌ,ZR­ •†ØYÚɈ— É?!™DÖ*8ŠØUô¾Ûê!!Ý^Ø8²!ÊESe¿sQ÷7Wá§êÙ‰¨ ªúÿš£BÚùͰCÛX·vm\#O$þ™·hÙRŽÝ;ر7—@˜­´µ@.›C‘ã/?ý›n?yó÷ò–Ò}\ò»Ýøª›lNÐÚÖZ¹³fÿ¥<‰%©=0!œIðû¸ïŠyÜúT¸™k®oÀ¸Á+žÄÃQx ´ ñ ŒIAZÖ¾_ÆeÌÿ[Ô£üŠô‘¢'5‘žÌD”òÙ]ìeWqw­ñr¸ì¿BÆ*JÓñõêΣHÌn¯ŒT £S¤ÈXMå¦Ì^×wCr .–Á-¡*C…uYïœÇK]Ñ©†žcí“kX»îY¶%’8 [hk…|6WvOØr?]ÞÅ;ÞûZ6Ýõ3Vä•#›ƒæ¶–r.Ζ§x¦0™£9¸þ+bMçuGõæ)¶x£¯|Žƒxñ³H%³u=Ï»8lºÅ¶Í›Ù\û·…]æ¸ÊÀ °Þ Q»TB«LÃrU«¬ëÎÁÌ‘*q3D‚\Ö"›˜âL¦ÅnaØáÙÜf†ý‘÷Y!«.›y*™Ü1­âÙ1úúNY/(á‘iò~Oyµûš ÐŽo1Ô ±YRïÊòr™Ö›ii‚\®â¦öñ?·,æïÛyàW[+€K.ïbµµÑ, ðA~ôóÍÜtî7¸,{#¿Ý(˜ûž 8wö&~rõÒš>Þp¢|€Ûîx–›Îùßàfî~l'Åæ¿cFsà˜þ?ð“{Îáú³®âJïþsåNJM“9´ãy~}ï*FLHn`¢5BK°w|8BŽFå"ñŒJ3”IvFzR]ä½[r[ñs±÷ˆPB¶&S%"æÄï!|w*Á|°æn¥i²2 x#ôÇp‹èªfbûš[K2ˆŒÞÚËëçßDSä³ùÚ‹OßÅÕ Âw’ËæaJ-úUžÕß½˜ùùù\xö•,ž{×ÿ‰Ì¿Ž®)Œñž¼qŸø4ŸüÀ%\óñvda€Þç盪©…#<ºðS\Òç¿÷s|ëüÞÉú%×ó‡ßbˆÜÀ Ô¬a¬E×"H¤ñÖgQ‹¨ÒeòC0Áîä ÌA¤dŠ}Åž/ƒî žï†º°ÎUµ bü£!e§ÌïšÁ¸CØ2Þ!¼Kta‰kÑhé¸úY›låЦÙ8VšáÒ0; »(¨"T¤ 1jÕL0![%oÈ6P‰›Ÿut84Ù-¸xì-îÅS.:¥]"r;ø`d “t½ót8íÐ’”eó[g``pàñ·ÚÎmBԣР©TÔDT:ìÄrÆEå*\ia3É™H·ÓvvÒçîÁ È0*6!„ dƒeF²¶%\OD·J£{""ùÔWS¬)4[ͽ"{Ý}¸xí?ØÊN!…fþBÔbu¥PõÅM§Ró( 4  ±³"ÔØ&HáJˆ@©z˜nUÅo…H“e´Y­”>ˆ¦T†Ryƒ³àµÅDq#èð ¡ÊBÓ­š\O‹””S[E3NIÖÏ2àÿöÞ;^’ªÌÿŸS©»o¾“30¬` .ô§ë×Àº+"à Š¤1AHXÄ]DÝW%¸É i$Â003L¼nîîJçüþ¨UÕÕwî2ÃÊB}æÕ3sûV8U·ïç<õ9Ïóy†›‹šµÅêG’:q’æÿ³zL#À¶l\ßË]Ésäȱ] uRÄÈ2ŸM7pµªÅz2dèÙB$(¬N¶óyôÛý¸¡ËK•UŒ©2a¬_6zÅ—YÛ‰@ºe¢Iœ_¤'J_wMa2ÕžJÑ(àkõÕõx:h$›h]{%˜9:zÃ4Kdú×N-¢‹³ ‹ P9‡çÈ‘c;Fä bOEå­Õ-Y™ÛMj$œ6ß3ôÊn¦9Ó±„Ř?ƪêj|´Ij^ ¾+3³YTjêigñ•îó Ñ˜Â`gÃa<gÀÛ˜:¾&½ˆšºg2±:¬j3]´uÍôÜ0°m ×óQ*¯FÌ‘#Çö@–™k¬Öý‚ÓBA$#jÿÖx+mve ›ÅùtZTÂ*/UWSÁÍ0ÁŠGþ±äAQÓók9ìªÅÇ'ÓÌë!´­’M2 la1ÕœBŸÓ‡¯6z›Uc© ®Ý&u3ƒ6bJã–a ”ŽÕüç)ˆ9räØvÏZ§KÑtS)©¯‹ê( :ÒГÞ'ñȹ®I Ëèdva¦0Ùâof­¿U“Ù…H·…K.'F&u$Þ(:.ŽÔ¾JM2h‘šTtK³û’,1¯0Çt ÆØlªe5Ç×Ú\#¦‘£›Î½ñÑLÐXÒÄÏskß˵•9rl"M= –d!2#ôÈU°áÕˆxcYÓù :Šè%P;8óè´;)e6x DzL"/EÔS“éÙ s.‘WDCÈQ±ï·.5ÖB_H¦ZýôÛýh­Xïm`(iØ]aêk»©: QÏNÑ’n#æT…‚V? &¡$åÈ‘#Çä …Ä”VÁ%é•¤Å´Ë )Y$Iœ}vÓéH6º›ØèmI,‚¦Ï-Û(±ö-nTÉç‡x<´šd5÷î5»™SœCÁ,0ê²®ºO»‰ÅLE»åàú}Œ 5‘‹)¢[–‰R/ðÛ &r¼ò0f½ƒ÷ïÓOÞj:Ç«™È{,a¶­§ÿiúª$9Štüž¤»’(0ÚNÉ,2î—Yí®¦ªªµ?ŠÚ›ùƒ"fžŒÔã†êžãšd ¤ A’‰É&á·^{’°0Ù±0Ÿ>«7¨òbùEʪ=M¤{5.L´t3•Í9#zœ@Ôþ­Í-Že>¡ sú~Õ<–˜ó–ýÙoaGû•ŠÒ›8îì~'¨ IDAT¼ïðéÜ‹)9“çxÕJ+0ÓžI‡,a Ópҭ©Ìp]“’4ÞÔ¶§ÛS™fOE ÍZwÃá(>A¢:RÔ”ÑHñKçœèÌf‘£òJz„"Ñ´ù¾)LfY³˜áÌÂCþ0«Ý5øÚoyâhd¬ë¤JÃE‹˜s˜Õ^ XÒÀJ©DÊa{‚‰~bý|Éb–,Y’z-æ¢êCÔ¾¿ø‡ï£ zøû³ÿÈ}=Â#<ÂC‹ïáÖß]Éù_:œ½§m÷/Nä-©±Ùðí»sÙ¡3^Ñ©¹;Ÿ8ïûœð¶¾6?›7û ޿tÖmlÌ“Œr¼J¡«›éö ¬˜£¢R´ú®ˆšQT²ÜÆhx‚Gz»…Å\gF• Ê*w%®r®å:¡Í7ãy•:gܶKЬ¢ûd '-þáÍëH:d‘]:v¦ÓîdÜ/óbyãºBØ,kM?™šŸ´Ì¦§¯Žu¸ÓHaP,p}—@©Æ…hˆNô2áÿù§œrɃ±Íèª4=ia€®¾Œ'/ç¤ ï£bv0e‡=9蓟咷ïÈ©GžËâÑÖqtì~4ç}k5Ç|ù¬ _¯¿Oýì_øDuˆá '‹¯b"×!Óœ©ÊgµûR¦+`²F³L“Šz-,f93éµ{ tÀÆê6{[k„¯3âûx+‘¡½‹Ö*›Ô´Ô*SÇ·¯¿ßit±sqgú }¸Ëêê6´f´$Kù›ÿ&íl‚dì†a"…ÀóýÚ¬Ouyù©‡zd%O>ö5^³|K{ÆÕC/òÄãOðø’Åüé·ÿÊ—¿y kg€íWȦ°gfÕ_â»Ç¾‘â„?õiìwô9\yãÝ,^|7\ùŽÚwjsƳöå«\ÌGÌm¼'çü3—/¾™¯ìÓÅþgÜÊ¿:ŽE²ù‘›vÈ¥,¾ã;¼«˜³Øÿ_Îãç¿»{ᅢß_~‡ìÖÙ°Üœÿ‘ïó›[ïañCrÏ-×pþ‰ï`ºœÌ¹ëQ‹É›O½ž‡kO5ß{oí~ÔÎ{ÅÕ¿áÆ»Sç%výð7¹ü÷w°ø¡¸ç¿¯å;ÍÎ5ô5„„”ƒq,i1»0‹Ùö,,a5 Ù%ÍL!DãUÏ&‘‘¬€Ôõ4½fôl “ù…y ‡QŒUîšFÿ¡fZ Ñú)D¬®³–MÓ’Í^LšDj4™(u§,± 0—…(­XWYÇj¯.©4û~&R3urš‰¿~ìæ‰Z§؆Äó}T}! ¥¸¿|*—†ÑxIù?£‘°RÆÕ&Žmdåu|óŒ?ÒõÉsøÒýmÆéðÆ/äüO-àù+¿ÉÉ'ŸÁÕ/,ä˜ ~Ìñ»Û“E•Çîÿ3îŽû²×´úø;ØsßÝ[Ì£•"{œt!çýc‘û/>ãþå[\7²_øÁ©¼½#º‡›¿Ž‹¾qÇu<ßüåj}òÛœò®®ÿÁ} xöªÏrø¡‡rè¡ç‡÷¹ÀÄç• ?Î_ÚŸ±ßžÎñGÅIgþŒ›žÙD®¼äøkFäkÝuTÃ*E³È¼Â<úÌ,ÌV¹^£žaž%ÀÄ`ŠÑÇTg*Zk6û›†›š:qk[‘Ò¡›DÚà:‘zBб>ž¢&µÄ'™“k4B ¢À|gsŠs±M›Õ<_YÎx8ÞÜ/¥ÌëTìßLHi$ÁcÖW_U-KEŽi!¤Äs«©ŽÐɇˆ—ûgqçCͶ=á‹WpÔa³´›Û¶Qv'ÓìÍ?œt(‹ÜGùÍcíÚ7+ïû!_¿â§\öõ¯³ä¹/rÃæÔ!»äÈ.`ÙåãìkV €G]Eiá/9òˆ¸ú«·mµÍÛø#÷ðçà+ìÿÖ^~}ýtq/Þ±·Á“—>ÄH×;9âàéké_.aáßÿ’xӌ۟d²jˆ»i%Ë—¯nº®uO|ÞÝ~zÅþùž|¶ü%g’u|ÀÛˆƒS\@·ÝÃ|=Ÿ—XÍæ`3 DM¬™rVk‘ ºN£ÄÜRT`³¹²‰o¿ š‹”’di}ƒ®5„õ¨7¥Ç Ý”;$­ä+Ò‹›B` ‹yö”v¤hñFY:ö £j”P‡©ÚRPÜã’K½½[}üBˆÈƶ²G7Ê6-¼ÐGi•!Šm2Ìò—ü+'^°¸¡‘kw/†íC|ûÀ3¹}ñ™µ}†—ßÃe_<ëÖª #æ¥WœÎ%{]Ái§”'O[—Tßç¿] ¸kI“ W±äÑ¿ÿ˜oÜÆÓ[ûÝÇm+¾|ÀÛèùýT÷<·:ñ“»6"çïÊ¢b‰Ù§ßÌýßl>‰˜–ÂešÌ~ç1|öïáM ¦Rô†ñ: Â'ìmú¥0¶rÞð¦ßpåCïâs—\Ãnÿ}¿¾özîxv0ç“E*µb½?€% v2Òçôa ³*ÙèolÑœ“a[Rê­ë}V?S¬)*d­»Ž‘`¸%<ÉjÔÚ»%Ý åêƒh¡]Íçâ\¢ÀöuîJÉ.°©º™çÆžcK¸%åÏ¢O -£©±ÇÏiÆSXlÓB)…ïûE<”gÛRõèž]º”ò¤‰ÿ'œtáý”ý Ã×±nKerR@°‚kϺ€·^ùy¾qØllû#h7PE¨À0ÍvLν·<À¾ööﻃ-ïÞŸÂ’‹¹k³†ÙÔ&n>ó$®x&L||*›† âÜsGýö|ëKÙ¬çó‘3Ïá“=w»‚˜ø¼Úä?O9˜ûþöƒzÄÇ9ãêOð±=‰O_±4w—ÏñW%sO¬õ6`Jƒ9Î<ºì.v’;Q¬:¬÷6âã5‚H¡› v²¦&Ä#ã’Qbš3‚U`Äa½;€GrwM{Œ§Y¡¹œ¨kšµª» &*>iЩֱ¾E1ë“.£‹J ˜[˜CÑ,²ÉÛÌòêrüM-M˜E̾·^uŸ8MC’J–ü›:¦ý!°m‹JÕCéhY@ˆúcD\åþ_ü¾ÄÒ§žž4ñ'fëµà»çÿWåhþÆ¥ ýžscÏ½ç Ÿ\M ÆìµÇ4ªÏ=ÃÊClÌÚq+¨d|ø†ï¾»?ÿ>ðÁ²åƒûλ“A ¬ZÆ Þá첃Áš›—×èš(î¿‹ÄcœsÙ <4¢A޳j8v‡ÕVέ]ª.tvw&>áVÎí[æ¥Í¼‘ÛN½’ËûGÞrõRòs:Éñ×@Šžòx©º“ÙÎlº­.±†°Øäo¢¢*5^ÊÊÓnrÓ sýV?¡Yï®§¬#›Úz *b%ÿq2–éqÅk’´Ž5¶ 3§<îX%5HaÒct±ci'f8Ó± ›Íîf–U–1àFROÝäK7¬|kSÖ©ly‘hpW?_ýêM©£Fki˜!Q:ŒSf4í,_½Pl¸ùû\øî_qúMÙBÜÅU׮ಣ¿Ë×Ë?á¦å‚:‘£¾È¯Î»'ÒÇÕ î¹óŽ=êd¾ö¬ÍÏ ÃœEôÆ/¼ü×ß2È¥'|Ž`è>ïht†îàW׊‹>q.g‡—óûÇÖãg°SÏjþpãT_|žUâ0>|ì¸åy†ÂéÌîŒ{+çWòÌ2Ãß÷){ê×}N%cgzª]¬¨® ¬*(TBk‘:¢f)¢î?s sé0KŒã¬õÖà+¿Ù @‹º÷Uæn½»Ž“©-2ŠÐÍTÌyQj0…Á³Ÿ‹»0¥8 Vyfl)›ƒALÀ¢É4Æú Ɔ"ÞËTDÒ €iœ®ë6f½t'¼ÿ“Uj7þðRÞ·×iͪñÏ|nnv0ÊšgîæGg^Ås¹Hžã¯*¬$)ÍÃg»OUØYïBŸÓÇÜŽyôZ}¬¨¬`À[OТ#˜Ì´¦Ñiwê¨ßåfjY|Œs›˜x~iH¹ÍR£ìm9âf'óœyÌ+Ì£ÓêÀÖWײ¬ò<#áJ¨ Î×Ϊ69‘z4÷ÔB€eÙ 6Ãcc„Z¡´Š:SèHƒªW:Õ_ç†L==s@twwçŸÐ9r022ÂôéÙ\±qíÓW5"nvæ1XXôš½,(Ígª5Çpð‚*›½Í¬óÖ1äàá"´F!)Éoêx#³K³ Æxzì)Ö¹MÍ9&0'=Zê•íIÕ[%²OD¬£qT ªcÛK$E£À s:3‹³è³û°…MÙg­»–UîjÆÂ1TmiSÐ~·yÜXš¤h%ùø%˜õ kî‰Cþ0#ÁHË1ÒäG[‰#ËS%úŸM—ÙÍT«ŸÖtz }ØFÔ¯aÌg£7À w%#þHMÇ„ÈÌôÓólU³4ýø^¦mšHiàºR²Qã %'ùg/GŽÛÀ…HÚ7c`C4)Ui…f­»–Aˆ)^/3œôYýͦ4zíveWF‚AF‚1ʪŒ¯||DY/q6ÓÉHØÔQVº…%M ¦Óè Ëè¢Óê¦dvP4 ˜":ßPu˜-þf6x #TUU³ÌZ¬l«EÃó¥žêØ•“áyN¬²³`ÛA@†™7Y¥CräÈ‘cûJ*âQ«lCú«=<å3Åd§Âæ–æaI‹jX¥Ã,Q4 L §à*WU¨„Êa•ª*ãê¨j]‹¨UŽÖõúPY+¥7±0)ÈF‘³GÚØÒÆ2œÚ¤à1ì0Œ²Á`X SUÕ(_=A¶ÍÁ'*2ÅDרÜ.*x"óé Ó²lªã£„JmõFçÈ‘#Ç+#«$%ÔmñêùÜZˆ„Um@À°Á×M5tY7¾G:t˜%lÓ¡CvÐ-»‘B …Œ2ò”"TAM4#íYKÐY«§i6v@ CüЧâ0èoa“·™A £j4ÊC"Ó ÙÖ¾½šÑÈ]íŸÖðãºzü|ZkL? &$qAûJ¦9räØyÜ+NâqŽ@Ð!ŠLu¦aI‹Aoˆ§Çž&!ŽaÓkôÐ);é4:(E f‚(b&–a“ìþÙôøUˆ¸TU•ªr©ª £þÃÁ0£j„P‡µZ›z£eÑ6²ŽÛζ [Ú_·Ne®Ô;¼™¾ë¡k#T›¬R'È#ô9r¼ÒúÖèßÄdš5•’YÂSÝ T©‚Ö„¡O5¬` [˜Hab32ã’&¦Ò@*»v4…Â'ЊP‡ø*ÒÓè@„„-sº‘v¢SÁoÜ3Z zÒ×Ún6Iî"c"˜ž´%f1Á£OŽ9r¼ÄÝR=)D¦Á•--f9³pL›ÁÊ ëÜ ±=MHH¨C\íÆSÕAD$Ô \]£s]+„Ô±ÅÄ–VÔ¥‘r>LJ! ÖL4æiÀE2Oð¯hfÓ$Ö.…ÀÔ1SÚfvŠH¸rIhÌCòŽ29räxEPÓA¤‰AÙ͌ PQ㈱p´±˜àÜ C5þVÙ²F¬’²m +ÚOH:6áè6ÒIj+’ PkO×Z8T»gzRQ¶ }räÈ‘cûFåºM´šn²V3­™X¦M9¬0l! l³à˜ü;þš —ÅåeÚüû?©z×L.;Ë×%«Ïh£ (]é¤St.¯äÈ‘c{Ag||ò)IBÓevÓoO‰Ú¸¹[ ÆÚÊMúoQFZ&Õ†@“íÞZ‘EÞqwE=‰ëΊÚu"€Nڥĕ3Ëc ý‚íPÕ)qÜÕ¿ä„][›%èòùâß_Á¢Ÿý’v…§/:”£~¾%çqÈ.áÄýfÑSèЧ2¼KâÖkÁµ÷­imˆ çðg~Ÿc÷›ÍÔžlDû<óúÍU\s×Ê oñ9rüµ£ñ¬à±n^%cDlbÒcöÐa”ðB—õîzªªÚTZE’½D;)DÓT¼3dÕ^Ui!ù}‘ ä­I)z‚¯ã¦^I.Ý4b"ßñmÏXÑ„n™±±±Ø«Ú¦O¥CßÌôÚgl<ÀîËîû„S.ø9ç4+C¯/2sáBæNé‘ ´I©o»¿ãÜüƒŸóƒCæåޝJ"¬Á5+?K¢D¿5˰v‡‡jåð"Öm¢Ì¤h#Ñ¿h)ØŸXþ€‰Òãõªÿ3‰Ig<]¤-l6äYÃ0hærꌙhÛÈ<ä/?="ЏSûßL°Ï3?ýGý|%²ïÍ|òÜ 9aŸ~ö?ì}Ì¿é ^̺–^r(Gýü%¬©»qè·.ä³oëaŸ½›y×ýœ• 0¦ñ·G~–þ;Þ0Í`tõÜvå¹ø†ç(7îÐ Þzħ9æCï`·ÙPfãÚ<ó‡ 9ã?žÆ›Ô6%þöÓð…ƒ1«¿ÓdÕã·såsãòÚóÁdÆ’#ÇkTZÉZàŒ'[ij±§ÙSè4;qCõÞ´—CÚlÚ «¥ø†É'u´f˜Dï¤[Q蔇ËÖ¢p™ºÙæ_2ír 2™Æç¹ÉÏ-¯ ‚Á§øã=/rÊt¦lõn+ÜÁõl®õ23ÍZƒ‰{ö~|Òxó´eŠ öã°Ó/æô÷N©=’õðŽ/ÿ”Ÿt{ΑlzáyÖûÝÌÝu޹ϼÚ,8‰mð;g2ê2¸a€19……o?ŒÓ¿û)v3'9–9^£htµOÈIΩ3’…É4gæVðÖàgÊ¢å(iK&zȺá¡H6RN‰®¬“vës€ÈxJˆï-Ï"ÙÒOs S´<¿ÔÿH¸}íg2¸ðm¡“ÝO¾Ž‡—,aÉ’%,Yò ¿ÀïD' ‡k#›Ä6ȼý3gðåÃöeV1öñ J”Š`ÎÞúX6ä9Ÿ9^³$®c½D ¡7~ׄÅtk:ËÁõ]Ü Þb‡NL ­4(jœ–ì¾£c\'Ú„´MNL_A,×±Ìе[S E Í7ǯÑ|ÓV¼y3yÓtcÓè@z›TòöùD?júøžGyh=/>ý ·^ûK~»dãy O_t(Çܶç_õÞÖ5‹·ì5ûæ•T5 e¤9¹ÏqëÜÃK±Ê[=üë dÃVR·LØê6žÌ™ŸØî±'øõÅ¿æññ]9äKgÏz?èÉŒ%GŽ×,Ú¹‚ë]I"W™řXÂd£?ÀP8”’<%2& ÑÂ_Y ƒ ]/žt¡OSìP±‰A¤¶Þšß¸Îœ0²£ïø¹Cšº¹Ù.R48¿i,£Ýëþ7òÌ¿ÞJþ“ÙsõoùÞOÿ¿:aê•”tŒÚ‘MÃDêLº~e Xõ›sí ˜óùÈIfžµúF~~ÓZ1w~å?¸íŽ[¹õŽû¸÷–«8qO+Úó¥›øÕ›Pb&ï;ûzî¹ÿ~|ÈÜ„´õmþò,›XûžÂ¿ø —üødÞÑÕ¼»“KޝÍX\`b¶´xK•Y`š3[ÚŒù£ ùC1ïïf.µÈx‘Šfë_É”.ž\ï¯cÑwrÙ3MÞÉÚ›äñdcœM<.'‰Tf‹HŒ9y^)$–abÛ§Pë”q±2#™çÿ,µ{K¹úßï`H Š{|œ#ö-äîsŽç”‹oàÏ«† ½ô8Ë–²Þ³£+Uùï3Žã´‹¯ãŽÿÌ’Å·qû[¢¶x~ùÏLb÷áKøÒÿÀãk|úÿfþvŸ(¼ÄÒG–ðÂ(“KޝAH$†0ÛF¨urì”t›]! £ENSÈEjŸxD,S1qk®v6óIé²~ j‘xþ®O(ͤF‘’~âl*iÍÖi>¤“LšÜìX6ÅR‰B¡@øˆ²öÖÍÛ‘\SU:Z®¿tìÿëçL==ó000@ww÷k"^èš9¹y Ã>`Ïçð ®â ûXqÅQvñRÂIm“#Çë###LŸžÍÃëÙyM£áh£‚SÆ£U!°±XX܉»vEhxdða¶›k¹®"¥U'Û•äC;ÛØú"¨Î”oÒ¯NI5qY:>ɈÄer,É÷ø‘šûZ†E±TŒ:%U=üÀGk…™”曄޺àù:¬Jý¼÷ôÿâ+{TÙ´y Ñ=©”çú›žz2ÛäÈ‘£­´ÒmöPQ|í·$hŠ¢@Õ‹’ÍÕTT¹PmºïˆŒzÈxY{;Mïhyœ^”Ô±mÉ8N–¦¡["oÚ$A&÷4 ÇqBâº.A†M–6ÓÑxaGá¼lhR¯ŸOYxqþŽÌš:ÓäŇîå·ÿv ÿùB8ùmräÈÑ=VÃþžö[2>4ÐcöÒitª€ÍÞ&<íg*+{º%ýP0Q/útL¢µœ˜À†+;ŠÎŽøEËñuæêÇ4M“‚ã€Ïõð}?‘95Ù@d<>4WO›9æ­¦3¯y¨UÜpÖqܰ­ÛäÈ‘#MÑ(Òit2¦ÆZrO ¦:Ó(˜\UeÐßB ƒTä­3è³—¹hdŸèŒâ¡ˆûdŠêUŠþÓJ6±…Óø÷’Ëž­ë‘í¥ŸH?—¦I±£HàûTËU”N–!%¼VÈT~tæ#KŽ9rlO˜Ò¢ÃìÀòM‚Æ" Ä KvÒiu¢1oœÁp¸Þ)25çVÉB¦å$阄Œ"'°ÆJ{¥¤ æ[UŒì#‰¶rPl_)±mÛq¨Vª¾‡ÖÙÁt”ÅÂdÕoñ*pZÉ‘#Çk*"× !¡Óì¢ ‹ m[#00è5û(å³ÉÝ#ñxæGv¹=c%KßEŒ¼³ ¯ê߯ëði·A*ÏÑ$ߎÀ›ÇÈbÖÚ)°Ó2©”+øž‡Ò:å}.bÇ«Mí¼U²=uóè¹Æ \LiÑoöÕbˈˆÃfJ¡‰`Ô¥¬Æ„Ùœ ÒÞÝñ¡¤ëIwÉŒÈxcüû²E0Ñm¹3Yä”ü:ëœRJÇÁ´ \×Å‚6¯ÒŠn3k¥õ§väž#GŽ/›Èєà EÑ*Ñmtaa`aÒ#{±¥E5p Gñ ÚðOk¾vé‰ ½³׉ZìqTDŒØÅ{€Q#qÃ2)• ü 1™šdT"¢HÙ¦AQVªn$ßçÈ‘#ÇöÁ·_y؆E·ÙƒŽtè³ú1 ‹qŒo¤…PÓ¥õ2UDª³R¬ÅV(Y’UeÓ:UÖ IDAT!$ ~’ÇL`ÄÆoÙ–cS-WT˜ÐÜujñ6®ï×Ç(“öªª¤Õ<&GŽ9¶FÃ1*aS˜ôÛýX¤dtÒiv¡µf(¢ª+¶o‰ô¿dþJøÒr†ž€d³ÌkEFg‘1ˆÔ’¤¬ FYþ)°, Ë2q+.A¤&)Rn,i>®¥‹'sÓ»4WEIù üŸƒœÉ»Nþ._ûÇùyÏÎ9^EpµËX0 @·ÕM‡,ÒevÒauà‡›ƒ-xÚk!ñÖõºV›Ú‰ä‘víá²%˜Ö3µóÏ2×dËÒRJ EÂ0$ð¼Ø“€hÑHH­gêX¬.Û_”ÎÔ›þÏBô³ûïdÙÅm¿QbÎ[ög¿…¹Ð”#Ç6" `,§T° ›©ö úí~¤Œù#Tü ªát81“ªøÌо·¦(ˆ ߊž0Ë/my"j%÷–eÑÑÙј‘,Ǧà8ضƒmÙX†…i˜Âh\E˜ðÃjª(f“çuÊk ›ÌõË¥tÑÏÁßÈ×ßj§¾áqÿYà³× >|ñ|±z&ï>í”EÖ5|ë½Sq$(¿Âð†yêþ›¸úŠ_³dc8ÉãG &Žºj;~òÌÝùÄyßg—«?ÊÃËÇs©)GŽmD5¨PËtÛÝôYý­J‡ z[ð3e=a„tÞu²%DœŠEª˜>¾_k±QV[:ݦ['H)ÒÀ0 „!µF4ÂHÅÑa …@`4¢kûFýCkçUÑtø!J…èP5&3ÛîE':`ÄÉ\°m™+þŸÊ)—<ˆ»á£«FÐô¤¶4èêëÁxòrNºð>*fSvØ“ƒ>ùY.yûŽœzä¹,Õ“:~yíz óß–9^Ë5yeÔefa&³€-m¼Ðe0$ÔaKù:´3Ön§‹„§xÚ:V'3iÙJ“ýI!±ÖËR"„)0„À0M¤ŒÜ•V(¥ÐJ#•Æ(˜¸å AÔ ~bSmf0)‘†D"‘¦Ä6 t¨Ã¥¢ý̸ö"Õp S±›¢&õH2è‘•<ùØc”'÷<ƒz‘'"Ú~ÉbîyÞâšËáCû]ÀâÛ*“?¾‘YÏbÿc>DZï߇EÓ%[ž¹“+øC~³t,ºV9…}Ž<•“ÙŸ]§š”–ò§ŸÄ¿/0yó©×óð©ÑSÅí_}_º­ƒwüËW8æ=ofÑì^lw~ï(¾vÃF”œÆ~GÊg>²?;÷k6/»—ÿºè|®|xy ¯ghÀ'`ÄÆW>%«ˆDàzCþŠ0+‹„6 fXq-ZfhæÍ@•ÌÀ4ÄŠŒÀ5ÛÁ5{° †e ‘¨Pá>çG¤«ë…=’b©@àû¸®Û8’jh#ÍŒÁ°Å+ŠÔMÃÄ0Lì¢ ²!­´Fåñ÷XÓeͶjåÃ0¼ªµF©ÉSYX)ãjÇ6¶ñãSd“.ä¼÷®ã?:ó6ô°ßÑ_æ ?8•u=‹ûÆv?þB.ø¸ÉŸ.;›KŸÆšÚËèê !Ù<{Õ8ãëQhÆÖ»À\vû»¿cîK—qú¹1bt®ÚŒÂá'^Èù‡kþxñ7¹p¹`ÑA'ð™ ~LáøOñ“§½ü·9Çë:"×(ʺL9(S²‹:`ÌÁÃodih¹Öêë éRúøâe+§Û¶53[D†¬’bDƒ-ÇÂt )ñ=·\% C´Ò!)–m!„Ä­VǯO<ªEçO9)*¯üÈHË&3¹¬có—¹í=;íwžÅÕ$æ¯à¨Ã.fi;.&¶m£ìN¦-Ø›8éP¹ò›ÇÊmŽÿîYòÆ×jËo9ùgó@êø¢ûqðt8÷8þí¶!4ðì¹³xûïŽã={œÇ}OÀ‘‡íÄòË?Æ™W¯HúŠ×º¯¹›V²|ùêfD-£û5¶ü!îyøéÆ>¢û@Žüè–]þ1ξf xäÑU”þ’#8€«¿zcùïsŽ×-‘7#ç±`”~Ý‹ CFÕIóY‘ <1Á½hs–´bVíLú:ÃWH‰ešX¶éÖž‡*‚0¬iÙHµÅ>×.8¾O„™>-Y©é'†ˆðAÔ$³Ý#G}^ÛÞýþ’åÄ 74línâŰ}˜ox&·/>³¶±Ïðò{¸ì‹çqÝÚì‘y\Ì ?ºŸjýøÁ«Zr&ù»²¨Xböé7sÿ7›LlZ wZÖ‚ÝØµ¸»–¬ÞææÆü7°K!:VcÔá*–<º‘ã÷óÛx:·.Ï‘K,ØÂF’¢,dô±lmÛ6‘ä’ŽÂ㦷É8›²ÎŠø¥H3Ê8‘†D)纨0½O[¥ˆliAúAËÙuÛr2ì¥Iau"× ´ž$5"‹—ì¿|µ\®áÙ¥K)OšøÂIÞOÙ¯0¼që¶T&ž\ÆÖ±ìÙg·~|!@mâæ3OâŠgÂÄm«lFï´•ÖÿÑ-È=#sä˜èWI&Ýv7BG zýN?fÅh´tKïu5í3KâÄ.h5én×ï3½¿Ó2±‹ À­ºø~0Aõ&™íØø®G†çϦ˜¬EB¤XŽíêD.&¡aMüàòŠýG_béSOOšø·J©µZÖpÕ2^ðg— Öܼ<•àbÕs,÷eϽçb<™’V´KÕ…ÎîÎI݉på3<çÆž{ÏA>¹2šˆŒØkiTŸ{†•y4žãu-­€A·ÙƒcC…¦´é1z ‡Q„]vD‹ÜŒÎÓl¥Éj¦'xEV“eÛ8¥n9òBÑmdd»·ØÄb,Û¢R©Ô>“ãô.Š_•e[X¶iøž®‘·æ’gÇ”*á3ö*ýPôîÈž{ïÝVte-Ïüe˜ÁÁ¬}ÞÃÛvXÅ}«îàW׊‹>q.g‡—óûÇÖãg°SÏjþpãŒÝÁ¯~û).>öû|›Ë¹éÉ]³(®½;—­ä™e‡¿ïSöÔ¯y^Ì¢oð.nyªÍd4rW]»‚ËŽþ._/ÿ„›– v>èDŽ^ø"¿:ïž\Ïñº‡%lºÌn¤øÊCË¡×îe¬:†§ÃLù#mPÅ„žà"E®ºÅTK6˜Fäë ¦çÕڬŢéú$¢b™2éÖpË´|´n+¿ˆ”¿y};)Ò0° …(›ÅÖU×^[í:«Mmå1 ]¡–‘"%VÁÆ4M|×Ãó=´JGÒíÚ%'aH‰i[xUT½Õ\úz’+”¢6‘¦a˜HË¥ ½[ÛL±¦0¿cfâ#Ö¾ºQÓ)¢KUÔÒu­’¨–,_¯#RhÖÌõ™:{zæm »»;ÿtæÈ‘ƒ‘‘¦OÏæŠMk˜³Úf¶=‹E‹(™%VŒ¼@‡ÙÁ¼Ž@)z„á`¨–-æ}Yæ°i‚K­uëÙ²ˆa‹ Ó¤R—RtVÁdöb)‰ÊS° NÁa|x´Á¯QŸÕ3Ò¿…ˆ$«`£üßóü’Ybaqs:æR4 ”ýJ2k¥]Ä;æÈ‘ã•‚¤`±M?ôÙlÆ×>3‚”¬½fãA¿±’ÕTŒ³3ěĪ –‡ÞŠ,26MÛq@ Ê££„J§úeNÎE11) •²£ÍžLŒú"f¡JáŽGÕŸ†6è7ûÙµë ô;ýÒ`Ky +ª+Ré‡Z'f›¬¤ø9r䨮²Š°)E0âà*)ÊŒ‡ãt»éµúØämÂW~&Qf-Ùç"»ëV9£¾ŸešXÅ: qÇ=T˜ÝWhkä/¬”R"øž7!Ÿ $¦)±maH¼JTXŽá0ÍšÊN;Óíô¨€ò^‘p8¾Ø™œ¥ µŠ3Y÷™#GŽÛŽ¢Q¤h–PJ3꠵ «Œ£Ìb&VäM>îµDºqnš(Oh[ûq^¢m!0¤Ä.ÐZ×RUâø¶®Õ-K« Í»viDU躖kž•T"¥aXŽ JáU›ÞäŽp˜aÏ`açBúœ©Œc¬_ËÊò †ÃµýÄUÅ®¾–©[M»…åÈ‘#Ƕ¡Ëè‘6 W£(hŰ¿™PíH§ÝA§ÙÉ€'†RB7ã鈾š}:U,ëIu²i9E7 WJш„ݱrKžw”›Ç‹·¨üˆ_Ö¤#+>”Ò‰‚¤:í !°‹NÔ\¢ê¸n”ž(’ölvê܉^§/ôX1ú«+kWcš=&Ôó*'ó8‘#GŽ/V¦´pC—a%BЂQŒ²7N—ÕM—ÕEQ¨ªöY^Y6²J§[ã$³ÈÛq0-w4Iâéã¶+Š¥Ÿ Ã@a¦—¢iZ8%*Ê£ã„J5f SÌ(ÌdçîEtÙ]Œûã‡+o¼›Å‹ïâ†+¿ÃQûN­ ¸ÀþgÜÊ¿:ŽE²9Êi‡\Êâ;¾Ã»J’ùù>¿¹õ?ô ÷Ür çŸø¦çá~ޝ:Œ…ã:€šfmHË2Å?¨ÊA ÛìÅ‘N[… ®ƒ×|%–ã!*["ïýl%ÝZ´‰¬`Ú–c¡ü°ÖH¢õ©A#t´ªÛïLaŠ34l(¯g‹¿™PûºÎ¦«XŠ”H)S*cGɶkåáÊëøæ¤ë“çð¥ûÛÈ4o<ñBÎÿÔž¿ò›œ|ò\ýÂB޹àÇ¿» Tyìþ?ãî¸/{M«¦ƒ=÷Ýà±ÅnÕE«¨Z1’lÙS “>«Ÿ.» {Ãl 6Ôú“¦åMR‰Œº$VG1Zìl^„Ntµ âqû4™¨²ôŠÓ¹dù^œvúG™o&¿kÌ»6ðè’ÕÍ'ƒpKÝHq×70ß=t·=¬Øã€·Ñ# °ç¼ÕyŒ[ïÚˆÂfÎ;O伟ý†›þtwÜt‡Î7°m;ÿÍÉ‘ãÕDä5'‹0à>¾ëb,Óˆª<ƒQÆÜq$’¢ìÀ¢jK™ 5ë]¢Æi›žßHÿí¤Š´W ­-æêüX'~Ã0šxèz Â¨—§®ÙÈÆJþëDZ„ÉìÂ\ŠVr8ÎÚÊ*ÂÔ§È$óè8Rš8¥h²ÊêÍYO½m¢ñí"O+¸ö¬ x|çOóÃvJv¸˜ js@CÜ{Ë„û¾‡ýûJìõîý),ùoîÚ¬‘ àÜsgæ3?ç['ͱ§^ÀíëTþ[“#Ç«0&kh‚T•&ð}B?ˆÒ I@À°;ˆÒгG&Ô¹ã0MižßXÀl§}Çù'ËÛœDƒä(Åа-„x•jm¢¨¬^~/ê5Ÿº!ÙÒaZaɰ;Ä ?˜­÷§HXÂ4±‹hW©ÖÜ3 [´”æoo}Y­ýß=ÿa}4[ù W>Ãsî öÜ{NSÃ2v`¯=¦Q}îV†ÑH‡ï¾»«ûð~`pß w2¨ÁÙy7‰Çø¯Ënà¡¥ËYþìRV çeL9r¼¥M½weŠq”Æ+WPa--QÀ yÑ,Ði”R¼%j¯&cÕ×¥”è0ê;œ&H’T’Z{2¨”IÕ=ZÜ4M¤aàWª„AÈaסBH0’ûÚ ×ê¥ÓéÂ|†¼!ÜšEÁÖxVš&NÑ¢6p¨°éµ’ýpÒÛ‹Ònþ>>ìS0bH#wqÕµ+ØùèïòõC`ï½äðoœÃÑ _äš«î¡Ñ'¤ü×ß2ÈÞ'|Žwy·sý½£hÀ{ñyV‰7óác?È~oüvÝufwæ¿49r¼ú¤P)9! ¿RE‡ ³T`LãÕM´ Ó3'‘€ë•™u›Øxä­3DädáORP‰ÿ lËÆ.P~@„©DZ0¬BÈÇŒ3 ³°L“QŒ‘`$óÉB¤¾0- §£„ CÜq·f[ï»ðhðª U‹íßHmàÆ^Êûö:-&¯TyòÒS8µz*Ÿ9êl.îƒ-Ëîåg§þˆ_<7–÷xì·×³ìã±þpTký³WrúyÓøâQ§qÁÇ:‘~™‘M+yjõhn/#Ç«,"O ¹­ºpÔzMJIP4 uˆI—Õ‰OРoÑ ñ¦¢ ¤ ðüD|]³)]‹Ipck“f!fÑ!p}ß'é}½ ‘"™³R2Št˜„*d<¡Ž· —ë•£Ò4pJE×Å÷|Íë¶öѪqŠº?¹Q»-aãÝæwC4«æ¸L==ó‡300@wwwþ)Í‘####LŸžÍ›Ö°ÃD6¥d#ÛDÔHÐ’R©ƒ7ßÂT{U¯Ê£ƒ3 £b¢JSÀˆþ6- òñ«Õ¦fR!ZÛ17 wt#å"¶\)%¦ma8&îh­šÊ{ü©@ °K‰W®6&—ù…Ùµ{7ËæÑÍKXW]ƒOÐæÉ@`ØfÔO4Px• Z%µ©Ss#µÛ¨šh®Ì‘#GŽmEVÓ†Ö6ïJAe¼Ì–`3¡ð±-‹³C1ßðV¦2L3ªÀT*&˜è”ál’ۚϪ‘¥.bß±, ˱ñk$ž Õ˜!®F£¼°±(J-w¼ÃìÀ±‚0d4!H‘xSö‘˜¦‰mÛ‘éÖxÄ“š2>û¤‹}D[Å&(räȱý–V^©wjó6Ž PöÊXҤ߮9-áfØ¥4Z´î$ŸµŠ,"A¨É±J#2Ýò«. ·Ê‡AÍ@˜Á;²@Ñê@0ââ+/#Ñ».ßÈ(£…MËpOX Äk:“•Ь~´Ì“9räȱ=¨\L±7Sþ*a…áêBH¦tLÇ2XÚ h9¢4d-¥Z'´‘ð/Ìž:t#§¥yD³`E…L~PS/’£Ô‰†uD%úJaYp„CÑ,¢´b8ŽåŽ'Ç/¤Àé, ƒ0j~‘XÀ‰û"“ÊO¼ H¤z>g÷ Î‘#GŽmVZ‹×ÓïÇJ_y”Ãq´ÒtXô”z°L'q´Äþ‚Dæx¼§l{î:EÆþ'¢®BBJϥNJg³ˆ´ª¡5Ò4 ht`K›P‡ŒcµsÍ1F“S(€x®‹Rªƒë*o()Ñaâž j¾¼"eœ®sa%Gޝ ¥ëD²_}ÑS6–#7ÄjPE I¯ÝGG±„!eâH)bצ¼§=dR¯«èz:T‰p7Iå­®…* ÑZaÙ]v–´€JXɰ·ضišx£•Fãæú=‘i²ý%éÌG›9räxehœ”¬Ae0ÏXPfØ à—0µ‰ÓYDš2)Ž¢ÅÈ0l¨´Ñtæ8ê½@%–e!DÍ F7 nE{[­F¬MPõ¶I—Ý%M@ã†ÕZºwó–c#M·ZEÕ̽’ãjd|#Ý¢U5Wx“3FŽ9rlO´r@%剘Æíê ãþhTåi±|‹Ð (‹˜¦ÕhÕ#êé¢Y©IJOÇ·­òHÓÀ°m¼Š×ˆãŽNéè:ö|!j“‘ ,ËÄ1KHa„!®rW-ˆZÐ5 \å ÁF·˜‰ëÚZf£CH§°'þ¯SÊL™çÈ‘c{RxR‘Ž1¡¥©GÊg4F)EÑ(P2J„^€R «è`ZV”ÊQ“V´ÖF­BN’Ût*õCÔ{|j…r½˜gŠHäŽ$y2>9Õ˜T+D¨± !$¡ ñµ×´«Ö½(ðš=~’U¥:CŒJ¨JYù)ªöErá3§ò9r¼²Ò 1\$ª?5Š€Qo?ôq¬%³„Ђêx…Ð0 Ë4¥ñd )Éÿ‰T?4Aäl(Mßó2÷k5Щ ¸N¦‚°êaf´•®†E“‚a[˜†Ä¯z1IE´„×¢e2Šù‘g¬€¶ 8yy9räȱ½¼µ((Û¶J!®öuG@jŠf GFiˆ~Õ#t¬R«è káªÌ̽ˮ–ѱÕ*:Qå¦$Ü “ñ¸hI\lÚÄM´¡ŠOùk”¶‰å8¸ed/Ķ:aÕÏ$“—º[ËDNà9räxÅ‘Pd‹ðÒ”*¢n÷!Cþ é°:è¥FLèûxåJÔ½GÊš×I\¨iÆÍ"U\Oz4ÌȧEaMêHæöÅÉ_B*ñP´ä†a@D ¯Ó¡Kvb&¦i¢ýºg i‡Ô;: t³²SL§×s_—tn¾™Ï\s;¿9uoœI…%æ¼eö[Ø‘O9rl…³”Öð1I¸¡V û#-ºÌÞ¦?ŠÖø^€_©"…À,8‘yV†Œ"R4ïñi:6ÚPAH\«N3arȢߚw•ö¶ꀢUdNi0L“ø IDAT%§)A­èGgæê–eØø¤Î¼aq/òF|™ú¢Ÿƒ/YÌÝ¿8‘·t¦¨Í>€oßµ˜ËÁÿjdÑÍû¾w'ýþóìaµ~ÛzË©\ÿà­œy@ Ô8+W²b`ŒIµ¨0wçç}ŸÞÖ—yŽ[‘VDl±0i—.’^­!š˜P),âÛêKP4@èú(¥0m §£ˆ!e"rn-=Šåk ‰i™®ß0Å)O—,á#]à/jÒÀjw5#Á«À‚îé+öƒ¯ C“ŽâÏ YjˆNTÞo•7e¢šHosDÞ±ûÑœ÷­ƒ˜ójhž©G¸÷÷1:óÝÿ?{çoYYÞûïû®¶ÛÙçœf`˜Â HrCŒW®)ıÊ%{‰Šb‰Ø¢Æ«D,7$ŠrÅX. e”¢t„™i ÓgÎ9»¬ö¾ïýcí²ÖÚû”‘!‰Üõð9Ì>ûìµö»ÚïyÞßû<Ï?yN^Îåèþ7Øu7Üѽ–yçù\öGˆŠg¯°ÂžÆØÜä‰|-c銘–ž"ˆ},lF½Q<ÜL‘Nc?@GåúÒufE"Ïfyq£'k_2½šC:ãd2ž`[´0 ©zU˃)›ò´Ø*¦ ŸÓ½±dnø™é¡k±O­eVøÈl8ö|üÂgSžÑƒ,à¹ç_ÎÕ?þ«VÝÌõWŒóNÚ¯3à§|ð§üò»¯ge*~Á˿̪?ÆÉÒ—}ŠïÿôVÝñ+nùÉ5|öM'³pˆÛjüò~±g§ÿÉñYÊÄ}/Ïg_·Œ5W¿ŸK.ù ß~l\ù9.:Ê|î¹ý×ËOâøÝÓ[帓Ž$¾g¿ivÞ{-_xßÅ\pÞE¼ÿ;›XùÚ¿åmgŒ ~_ë®ÿéVæöBNLyïørú¼üûï#:Æ9î8æ­ýï{Û›¹ä]Ÿã&}2—^ñvžßS Šyä[oåœ³Ïæì³ÿŠÏܳÍ㨋¾ÀÞt4Ûð þç›ßÂ;>sý‡}€”9öâÏsÅŸ•¹ý‹—ñú7|ˆk'ŸË_úRþ°Z€BaÏ’eú¸4 ¢Éÿ'‚ÝÄ&ĶlêÎ8–°34ŒV Ìjmˆüȱì$KDZV*¼–e!…H€¼7C˜n°¿¨)si"ùæ¸Àíó¤~’‰p¶íp@e1û—aaOsD®pI’-ÑïŒÞ•]!ré÷¢WÕY*F<¥4rÍîÛ>Ã{¯ÚÙï}/g- “Eý4^ýŠe¬þú{øè57s×]7ñOyW=v¯<÷Tj@ó®[øu|$§Áâþ[ï`ÒkVqÓ¯î塇îå¦o}‰®©pÔÑËdtBîùÑ ¬=•ÿþ¼®\…?xÑiŒ®þ1?þm<ãÑ´ÖýšÛ︋_Ýü>û©ï³~Þó8ùˆ¾ÖQ°c=k×®eíÚÇØÚLn—ÇV{ç¾rë¯~ïÿÇgÕÝw±êö‡ØÞÕp­ŸÎ¹/]È/¿ø¾ö³{xøþ›¹úWsßøéœy¬[`Aa¿wÖ§²$y¾8O5ts˧Ô~ìãØ5§Ž%¬ßÞ$0–|K†ÄaˆíÚ”Ê.–´@Ó’&R:9]¨Ôm™ðA‡”^¼íÊ@,bÚV›G÷£ÿçÇøÇÆó¼{øÊÍÛѸ,>ýÞúš39zÙ~”à ª…ºo8ЩG¯çG½š Ïzc7ÝÀÄÈÉœuZ…{¿üo¬×{á¢6oäI3ÊX}¦¥‡™Çf-;’ÃËÛ¸å— åâ­¥‡³²\áÀÜÀíïï? ¶£ T„E¹Va¿·ñx—BìòtQ0´u‹VÜ`¾™Ï¼Ò<솥$btmc’-ã0ÑØ,•Kx#eB?ì´Š%I9ìt4ãž2ìXÒ #™,ßg²±›ªá Ú*^•#ÆŽâþÝ÷1íÔÐo1 ŸŽÿîu?èìG÷bê}`ñ:þù#Wò¼«ÿ'ï{åUl¸H³k{¸õ'¿ä¯ßs&§ŒßÈ®?:…ÒÝ_äæ¹â\>qù9è|š}ú!vš¥¼ì×sú´¼‘½öW\ø7/áÌ…?á¦ç½„?4·ñÑß²WÇjTLŒÄ’Ó/&Ì66aYXÄ(5  z7|øb®zXe®V{ÇDâ…ýÞw—K–Ci“Éú9@BÑŒ›hm(9%\ádJï•V,„eat¿ØÆhCа\;‘m“‰X„]r“¶±JçJzDFQÈ0Ø*+Ý7E „Àɧ-ÇÁvm¢P¡Ðlh>ŽDrPm ãåqž¥Žà±ÆvÛzÂÔdæÙ°;ÕCFd<È óÓßÎìCµ7½ùG|ü³w²ìüóyA9Í£?Ì£ÁþwÂâþŵ–pü± ð}˜õ*9¬‰_\Ï/üyñYgñâS-n»þ&vð=’•â¾÷Õë¹ã¡µ¬}ä!6LÌq†ÿ÷ûÜØ<Ž—þùüÙËNdê§×ò‹‰§‹&À VÏ.BÎ66µñq6èEsÌCSŠÔ†Õ<Îã°%O¬[ǺÞÏz¶6t …ýÞA¹˜¦G丂þrcè5жjë×rqF±DŸ.1:é1â8ö@ˆ¨µ&"T! §ìa96h“êu’ k‡¾×Õ4†tÏ–tr`_Â)»Ò– mÝfSkÛZ[1F³ßÈ~,YÁVGÍtбeèÊaÞѤ‚¿nSvcÌÓ@±h¶Þð)>gDÉJyÚÉ›ùÖ?¯ãÐó?Î{Ï>•N8sÞw9ç¯xœk¾u AýK®ûÉnNxãÛ9#ü9×Ý:…ÂÇ×°AÃ_\xÏ}ö³8üðÃ8°6ËP𫏿ºrî9ïÐuüà_îÂ*‡¦Öóðê%/z¯<õDþà´—ð'ÇÔ‰f›Ù}#ßûénŽyýG¹ìÏOæ¸cÿ³þê…jw'"7òÝë6±ì5Ÿà£žÅÉ'žÀ¼àÅœó?Ž¡Z¤­öûÆ 3­¨dšXVmÞý×Ûø*À¶mƼq\áöcdc@i¤e äæ%=O4Qµ|,ÏFÑûá“fèk‘s=yBH(cPQŒÑa%Y3 5ÁêÆoÙÚØŠÑ†…µ…ùHâ…=ólÄ«³ ^H¤”‰(–|þ¶ì Qº¹U¤c&‚=(¥ñª=’UƒÖ†8Œ’jÍ Â˜Dª-½€šôõ–¨8ék®µÂ.{ØD¨H¡R²kéèØ gz¼ˆ¤M­Ñ Õc´ÆÄ YöRn ÙOCM²®±mb˃©•j,µ¡lUØØ\ÏT4Id¢ nk’¾¸¦úç§Ãz”ö4š¨±èèÿƹ/\Éó*Èö6ÖÜõCÞó‘¯q_XœžÂžYfŒÁ¶læ•çÄ-¶Û2j÷fHvˆòoLL#nF!%×£d—éä%ÛŽcœŠ‡Ãû¤m%Úš]&"މ§b¼R »äaYŠÐ÷ÑÑe“*ÄI÷‰É7Ë5€e%RqQA'ú6±FФU§"lƒ¡©¦X3õSчŽFÍ«³t|cî8M­e›¿…@‡€Bv¶1Ëà‚gz‘ðC ?½wö·^ùzn½²8…=óM…¶¨¹u×–â«€fö‘,ö#lÛ²­NvLB­“T’éø’FÆNô8&l·‰ãˆR¥Œ[-%eý¹ñtûÇØŽ…¢Ç¿g댒¨^ÚVÒ¦;ž®H5LÈ–ö“<°û~Ýõ0»[»0ê•Ñ$kÅ -%-˜ð +ì?ÆqƒÍuÔœ#^•E•E„±ÏÖ`Kª¡V¾,§ß ¡‰V4IÅÔ‹º7Š%Úd»ÄqRÅi—<¨•@ohÓÄF¾çTº«¸Ð·#tã”Kxµ2*RÄ-mLÏÀòˆ5Z›ÁÌ@ ö#ÏÅvT Ÿ³¯cžô7³3ØÁ~îö//ê;ª¬èé`Ú½™f÷…VXaOÕt‡>Ø8µŽvPö*\_Ƹ;Ù«lLGÆ]¾\¦r¹5Ͱ‰RIT^–#ÈLéL²È©£)Âs–Dvu93‰€iÌVVj@£QÚ¶}ÂV€·^Å.»™bÌ…èðñy,M^éXÑØe7ã8²òÐÙâÊ(í³Õ’G&êyvèÙdö¼ +¬°§Ó´QF±=ØÊN;J)FËu–Ö–Q±*½*L“ÿ>6¥‹§Â bc[6#v=•ñÒMu¬ˆ£Çs¶…Aw"òôçÄŠ%+„aPZ£Â˜¨ ”Jšb•lÇN(C‡CÔ#í"o"Èœ,ŠdR€™K´ìVòô±9q\Sj*­Ù™Õ}Ô`^Xa…í{ë/+ÆF±©¹ží$‹j‹YT;Ox¹ÞÜixNÇ®’©x’ öBRwê©~ã}b­5‘"…À)¹It®LJãÇ Ñ*ê¶Àê3×éü­Q£MÜ °¤Ä«•q+%¤èÖêˆL_ñ´Ë0aLì‡8ž‹ƒ]òiâ}G–ZØn¿·&àŒK>Î{þlé¬ðsa…–ÊB}Õfss;;‘6\;˜Eµƒ°pR‘i69# ŒÍ¨IhBÇ¢æÔ:ÔL6{x§ ~³,zJ™¤Z§àÓô ²芜vqVô¢§ÇÍvҧű°¤•D碅›\ðl€8R ÁvíDºmˆü¦’“ ÂójÃHÿgf00£N;c,ÿçÎ8ìcxË5?çû—žh‡Š ‹Ÿs Ï]Q-fB…ýA®ôáÓЈ'ÙÔ|œÉfÇu9°v”öÇJi|‰T„›v‘ hG-¤´ñư…$²ØÏE7ë¤HÈ’ØÙ·.h‹Tsª.ÊŒòO·ßË ’Tƒ1D~€e[”jeœrý§Õ…z0­5Q+Äñ¬\ËÝì,$;. qéF4&#«”ž8 2<{ ˜/ýÒ*~ñÍ7ñœZnî©üíÍ«øêÙûÿ~EÅûò˜t“më׳n[#‰ì£xÍŸâÏ/€¼°ÿÀøS~ùÝ׳2E-xù—YuãÇ8£"Yú²OñýŸÞª;~Å-?¹†Ï¾édÎ)4ÞûmçtLÎI¼ûßVqÕ¹õ"t¹ø/ùúªx׉È•¼þ»«¸ö’£R“G›c.½Ž;ï¾›»ï^Å'_X*žöž‘±ø°€2ÑÜÑlh®cËÔfŒ†õ…¬¬NÕ®çJk²1k Úøq )#î¶trÒký¬ QLÔð“Ì‘²—ÈÂåD#¬Ü, +Qg† ¤„%0JgfBAÐlÓšj¡òh w¤Œåö¿ÏÄ:éùbI,×èzn2Eœ}š…éz‹ ¼xjô  Ö_Ëû?øoŒ¼örÞyÚ¼iöäñì7}žÏ¾nk®~?—\òA¾ýØ .¸òs\t” øÜsû¯ –ŸÄñ º°X帓Ž$¾g¿ivÞ{-_xßÅ\pÞE¼ÿ;›XùÚ¿åmgŒÌaä{¿íÜŽéw±˜G¾õVÎ9ûlÎ>û¯øÌmAñÌöŒ39@|ˆLÁÐlnod[s د¶?Õ¢$+=ÄJÇƤZ´û `Äë(e{ tie«£G¬µ&êäƒ{•„fa2%ÍŸç¡R8v"JarÇeÄŠ¨à7Ú˜Xã”=Ê#7éQnbE†8eáX=A ÑëÓÞÅhÝm­"r¬ËpÕ rlÍSàØ}ÛgxïU{8ó½ïå¬Eƒ¡®¨ŸÆ«_±ŒÕ_½æfîºë&þé#ïáªÇá•çžJ hÞu ¿Žä”ç%#*ÏÉ'XÜëLCcÍ*núÕ½<ôнÜô­/ñÃ5Ž:z³³¿Ë¶³ÓïjÁŽõ¬]»–µkck!ÿSØ3–RɾβٚFÔàñ‰µìnîµmUdqå ád »åñ‘ŠhƉ(dÙ.S²JýºLÑÿœY¥t+âv0y•RŽ3¼û^Z£ç „À’£u§K¢@ŠÎOgK­*ŠÑ~ˆj'„ªë9I:¤ma”FÇ*ѵm¬¡atª@–å1™^^©°=ו婘ÏCW}€/­=žË>ð –ÚÙ¿ZKà°ÒV~s÷¦¾Š½ÚÀÝ¿ÙNùð#XjÙs?»Ssì©ÏgT@é¸Óxžw?½y;—ŧ¿‰+¾ñ}þõÿÞÌÿz%g/µp]wcû]·ù˜ +¬°éB§AÊ|÷@ebñ$í~”†ß¤RªqÈèJ–a §ûÝTDebšÑ$ƒg—ð¬rooÒ¤K$ÔŠJˆƒAÇŠ¨ ¯špæ}dLi}fR ³¡°%,!ѱîa¨1Q‰Îg»92J+TNµ‰ÚB ¼²‡]rÑ‘Bjp«¶mu~iŒîÄ•3{Í>+´/XƒuüóG®äÞCßÌû^yHVªh.ýÍnýÉ/Q'É)ãŽÿ£S(ÝýïÜ¼Ó WœË'.?‡þG>tñù\xé•üüɹû©l;ã1Ò`ÙöÞÝá…ö 7=À/ËÔû}’%21Ûãm<¾{-`ŠZ­ÊaãG0Ïï¥%öKØc¦Â)´2xn‰²[é¥otyíîSh¡rL'2÷>&ÖIÚ çöz¾˜Ìbº÷x*뻳†iTºñI:û=ßÃ<ùDÇ6þT ÜŠ‡´%–íàU°si‰=ÜJf…ŠÌ´ÓŸ}v7ÿˆöN–>/H­ªõóh°?ǰ¸ïi¬%ìüGf½JÆ7ñ‹ëù…"/>ë,^|ªÅm×ßÄnÞ¡G²RÜÃ÷¾z=w<´–µ<Ɖ™Q±[Mõ»l;—cBïaçnÁ¢å3§Ní&À V¯釅=é•|Šs?ÂÍòßm4[ƒ-lܳžF³ÁHy„cG0Ï[@6£\ã›6A ¥¤d•±„ÓÉ7éCqRµÑ´…ц¨µlׯ­zH+›/cFßÁRÙ©5f Ø?½E:­»Ûj áë QÐn¶ý ²@P©`•„”½‚éèšfv$†€øSO:œÞo½áS|þΈ’•âæ'oæ[ÿ¼ŽCÏÿ8ï=ûTN8á4Îyß圿âq®ùÖ-4ºlý’ë~²›ÞøvÎÎu·&JôáãkØ Žá/.<‹ç>ûY~øaX›0'Ø=)Xtâ™<I•ho¶Ý‹cB¯ã–›£væ%¼ç/ÿˆçx"pâJƦ_AåáÕ!K^ô:^yê‰üÁi/áOŽ© ^Ø3ÈɉD˜ŒtšÉµ™Š´Ï–ölžÚLŒUÆXZ?„Qg,UE3h`ДíRgÁSät@“îˆbHœ«é¤F1a",‰[ñ°«Ç­ òÙì‘ &WZŸUõìVí˜9“&µÖ¨Hù!Q³MØ Ð±Â.¹8%ǵ“†_B$³ÿþnÍ€Gœ>FßWX¾•æË¼èøËRT„Ïý_~—ú—ò–ó>ÊÇa×ê[ùÆ¥Ç7Lgn„ÜóƒëXýò‹p~t-wù |äj>pÅÞqÞe\ùª2j1¹c=lš<½™¾öN}÷Kyû_ÜÌ9ŸÛ‹m÷ê˜~óÝ|xìo¸è‚ð'uI0¹ƒ'¸“ßîRCÌÏ¿ð ŽÿÐżñ“g`77rû—ä§÷MŒKaÏ(KsÌ’t…¤ÌuþëƒmKµX7µWº,;µýæpÜu/ö“ÄEc˜ 'cŒ’S¢$=ZºÙˉÑ]pÙ<™®,soIÓ@ƨXQª–°+*RàI1¤¶“™Ù„1AWÑû}˜œ}’"rÜ·@)nûDm[ríQéEƯrÿÀô£nÓK:Ozê&¹–ª³  {ÿÖ/ÙïÀ…C/̶mÛ¨×ëÅZXa…199É…ñbçæm,¢ŒÝm+•b×}-„È•Æ÷#xWx=ÿŽ, [&¶°zâ!Zª-*/å°Ž Ž#ÞöOø›:ð™ìWØ·ì·T¬3Žeõ#H‘Dæ¶e†Qõºv·ëfšøSÍLeh¼³úžéø>:ÎÃ-ÇÆ*»”<eRé‡ùÍòÑx~:RXa…¶/L¦3„àŽPÉoñylâ¶OmEJÉõX^?Œš=‚6Цš"Ž#éPwFS™xf®1à(²3“hwCØi§Ò½j ˶1¢ßGÅ"ÛÍ\égE¦çJžÿüþ^¥i¬ˆ[A«LòÈM®R(êÄоä”VXaûÌó- £‰\¤œ@ÝD´›õS±{j7¶csÀÈT[BÉ*ÓTMb#…ňWZÖ„Æâ IDATŸïŠHf1Ò Â¬IZÖÆh\EqÒ×spJ–ët@=Åw 1 Æ[ä쳋³"ÑíôAWaÜÍ#ôùö0&sÊ +¬°ÂöõàI€ÙåÀ<¨ç{s'T°bw°“5{bª1IÉ-±hd ‹+KQ:&Žc¤”í*ް3i"ƒÍDœ¾Ÿ|Ý¥ž»¦µ& ‚¦Š"KâTKØžƒ°í¤Ä^ŠÌ±I!³, IŸ™ úÄK—Z2$û0¤ma{^¥Œ´ílªó@³óÔAI’Qèù +¬°§åÙx4@rH¬:(À¦PìŽvóð®û9BÃhe”¥ãËQF£´Â q\O–‰•B£è/qN×xDdF"¦á#  F‡I†‹¯â%ßWö0¢„4‰’ˆ ±Š“Ïë¤@“›kt‰½ÌiI„k#e‘G±"BTc »™¿$¦U›+¬°Â {jfR/Ò€™‡ì¼*O¯i tØÝáNÖì~”æPF«c,[†Š4mYÔì|ݘN\­M7à¢ÎÙ‰ºÎ°Õ]ª¤÷¦ïbL‚êQ´,!:u* ÛŽ$ÂÐt3^²u£Ý9@܉ȕ2ÄZC;LªA•J¾§#mw=ŒTÆ¥Bû”,h• +lßCy:·Z àY˜™¹Ï:+³£½W:Ør%µÒÊV =!,jN]ÑNDíŒì!%ºÍ­rE‘¢Ÿžˆ\,Mç›t< F%±òyÑɯ ˆ R&ÅC"u<: ÑR‰Þ' ŒA(qxé•K{NZEôþ¯‹û­°Â {ˆ1ñö»ß<]åiL 4Eˆ#°¥õÂ$åR©GWœ*–„Úô€OÙ 5-fÜs!f “yÎ D'2OrÇ{9ñÆdhšn­óAq È»Û÷¦¹ß{ÅRÝõ„$ýp®4Ivµ·ˆÌ +¬°}M¯ #r óæÌ ºeöyÒ‚Õ»¢Ñn"„À’«2´ŠÓVVªmõ3ð¾É€y:r7Ú ÄÜ°Õ AZ“9~3D›¨O3i2šdꨆ•éçûVXa…í3(¢\‹¡aäpÕ„î‚á dÆ:dskš1ÕžB ©WFsÆq¤ÛW£×&¥4(è–U½Ï HD›MR0)0“ ¥%²iŒ="óÓ öMj†!I"|mú Ýíž(Hxýž@ tKôó3Í€§ì·ˆÔÅRga…¶Oa¼Ûâµ¢“+ÝÁua²pš´LÃm*Q±óRMlž˜ÜHÙªPq*”+e–Šž0ì ¶é0É(‘ƒTG†­7ýïI*<ÅP½Ž•…H+ƒ ‰£¼Óè.{šikx„}*hXsšédGÿK™\ÀIç\ÌkNžÿû%ÜüzŽàŒK>Î{þlirŽò¿VØ1›Ž¾ÍP,ˆV ™D¶ÝȼÃ)‹ŽC;n²§¹›(Ž‘–`Áè=”qg>I¬TÒ %O˜=†¾ãDDçGúÛvfJ²WA:ã± 2㎴èŠVS‡¢œl2â˃CÎóâÃ8«ÿ|:üå_qæ¡…ö1¼åšŸóýKO˜[»Øg¢åϘÇQ§Î±–;@î÷ ûÏ~Œ‡èÉ­‘Æ%ÓhÛ›2xÊÇÛè8á0Ækó9|þ³™ç.Ä2V'€³­Óéçß±8ㆱd¸ù|޼8bàœÐ§V²œS¶\5݆q3š»‹å?r zá~xtÔbbËcÜwëøæU?à¾]û(+F7Ù¶~=ë¶5žRž¨,翽ú|^ùÂçrÄc8ñO®½Ûð|éºGhýW~*öÑ9(¬°ÿ¨è{¨Ä¤è«ò‘ÃTîvšR2ÎÉ6 u?BZ’zµÎáæHÖ7Ö0e& EÐÏ<6ÈÝ ×ë$©øŒEJpBˆiOv÷ÝÔÆîŒC“d¥˜†d2_l ×WLS4x<{g#ã£X÷‹?m»Êü¥'ò翃¯œ´ˆ7÷Eîõ÷ˆ­å_Þy>ÿòTn¬‘xËß–×.ÙÂMßÿ—ß·…°º­|+ªð_^yœƒÂ ûϰöt^äå&ÓŸÌ¢ÙERc2)Õ±Žðã6Aø>ÕJ•yµùK°É_OøÊïÉEõüËsèyŒ4ITNŽª1ôS×›Ôž{ÉÂgÏ™eÒ/{@žÏ#7™¨SÞî©è›=sß½÷%íÝ«Xõdk?÷ÎzÎW¹÷WŠ¥/»‚¿{ãó8pÔ%Þ³Ž»~ðy>þÕÛØÖ„»”?~Ëe\ôâ8¸ê³ùG êõæ+yý·¿Å_u!¯øÂƒI2¾\ÀsÏ»”·¼ìgعúV¾÷…Ïrõ;†D¬%޽è}¼fùcüÃEoæk¥cïï¥æ9ó9ù ïâ‚3aåc¸Ñnúäy¼çúíh{§\ðv.üï'²r¡d×Ã7qõg>Ã÷j`äþüÑeä‚“W²xÿ1Ê´ØöÈ-|ç3ŸäŸîïô<Ÿm¼ÎI¼ûGŸç°o¿‚ ¾èšÊÅÉ×¾÷jV_ò§|â×KÏÁ¬tÌ c.ð¥°ÿ Ø\’nö*h‰li°ìÜ Rd#ñôçcãëf™5Ûš[°}‡eó—3^‡íHTðD¸¹“åMOl½÷bæ`6S“£M²½Iõè@@?˜ÿg28;„‚Yä—©c•ÃØž¬"COæ¾²pr’6%J¥äPwÞ{-_xßÅ\pÞE¼ÿ;›XùÚ¿åmgŒtòçó¹ïëïãmoÿ߸³çÎDôx<ûMŸç³¯[Æš«ßÏ%—|o?¶‚ ®ü5DP¹tþâƒØóÓÿÅwš‰@çȼ€ƒ6þxëyó;?ÉÿþõN4e޽øó\ñgenÿâe¼þ âÚÉçòן¾”?¬ŒpÈqÇ1oí·xßÛÞÌ%ïú7铹ôŠ·óüÊï0Þ}b³¹°Âþ#è•áùäšá©yÖ'±û£LÌTÐÄhpl%bÖ5×°nç:Ú~›w”•óŸÍ•±¥=”öHï;/‚Á4 †%eo¡ROƒ¯{»æ8l­ÒÎwZÉWz>5:eÏëÖX°üxþì-ÊA»oá‹÷F€¡±f7­I>õÐo¿ÄŠ?þ/9zÖÏïGžÎ+_¼¿òf>öOO$'æ×“ö’S8vºoªŸÆ«_±ŒÕ_½f¸ë7¨¬ø¯>÷T¾ýîŸõ¥ã¹p9Kk1«ïˆöNicíÜrg?êõÓ9÷¥ ùå'^Ï×~¶<ò‰Eüá_Ï™Ç^Ám«’ϵÖýšÛïxÅ]ܵí@ž{Õ‹9ù›U«÷n¼ûäjÌ6æÛÂi {ú¡\ ®½u£Í|ä™.Ùï÷hÉr̽jQ M;nG Çrp¤C¬C66CX†Åò`j¥+ç= kÂf{kKféælKÉ&G¿èN$o´&Ž–%Q±Ê ek{ÅV»¦ï¬¤°°„-llé$íúDt$†=&U›S§û]Ì=ýcÜr÷Ç:ƒSì¼ï|ì­ŸçÆ=pY|ú¼õ5grô²ý(‡„U u_‰ZKe¹³…ïÝ2çEô釨i–ò²_Îé© =Åû¹Þ&{ói½}#OøÏ9|w‘ŽEíÊóÆ*ýiÌЯ wpÇ/檇Uæ²µwL`˜?׫˜‰%ç8^£Q,ÛÞGOÒlc.¬°§Èóí?DN¥gú,¹¡´‡C“5´Öøq‹rÅó<,a™ˆVÜ`ÓÔãǰ¤º‚±ÚR¬Ä6ë¡M<!ÏÀX˜VÅJá•JH!:Ò™ùm»»ùéI¯r›’U¡æV)‰*nO–ðìäÇqÑedÒ™Ñh“mš•ÿÉ–ëï£Ç¹ñ$«y”«ùØû–ò¿:÷ÞðÍ5؇ÉJq—õzî˜4 ›l˜è¯Zÿ0çpÒs—ãÜ·º¿À9ƒ%Û¼’ãNXŒ¼}åZK8þØø>ÌúüJ`ûNþý¦Ýœù¢×ñçÿt/ÿ¼>Ú«ÃSVóXx‡-±x↵ƒc”û`¼r;w -?us €:7OÎvŸuÌ…ö´šÊx§còt?1tk²b ©Â™tõd#l0ÆžS•¾JžžVÜbóÔFB¥XR^F­4Âòy‡"±ØÔz<‰Ìsã00T=­;~¥tgZU’€…+KIt-ËTì5§FÕ¥ìzX–…”!“b')e§ØI†aØ"ˆ}üØ'TA7¸ÌÒú35­Ù—¯ù›/ó·WŸÄ×.|¯üŸߝaƒx%qáYlûÉö¨…XKm1yW_ó_}ݧø8ÿÀµ¿ÞBX9†¥•¾eòf¾õÏëøêùç½­¯ð¯k‡þ7qþŠÇùî· òÍfŠ[þþÓüûqá¯ÿ×?pøw¾Ï-¿ÝJKŽrôÊڬѲÙs#ß½îu|á5Ÿà£êëüŸ{¶•÷çÑMüèÇ÷Ñœmû¹ŒW¯ã–›ãÂó.á=¸üøÑ X¼’±ég&Ø=)Xtâ™<ÉnÛ”û}Ã,c.BòžFëòÊ"ÝžU [­ËQ':ºgç{Œ¢%Òo¶´ñ¤‡P# J+Zq›ÝjjOÌâÚRêÕ–Œ-ò$›šëiÅM$¦G©ˆ98 Ϫ`I‰g—©Z5*v•’SJ(ËÁ¶\»CŸ 1&O…Ä:&ŒåÓRMBÐŽÚÄ:$61JÇ(£òé‡f€«ÉrXùÓôT-àÁüÿræW¸à­/æ'—]Í®XÀ;λŒ+_UCF-&w¬çM´<øÊÅ\2q o|ùe|úõud0Á¶¿áÆÇ§K“ó¹ÿËoãRÿRÞrÞGùâ8ìZ}+߸ôïøæƒÃ3ÂõÖŸòÁó¶qïçñç/»Œ-¬bE vmÙÀ¯ny€3ôMîºòÍ\¶ç­\ô§ͧ.ªBc «önüWfò¹Wñè7ß͇Çþ†‹.øR—“;xò;ùí.5Œ„熯}‡SßýRÞþ7óËÏÝŸûý¾™Ç\yaOs<>ìß4(J†'] RÃÙâ™4Ð*£i«&J+l˦dU1©égEÄDlk=ŠKXÎhe”Åb ‹Mu´ÔT?ôMµ¨µ„ÄêpÙ¶LS=»Ä˜;wÜÅ5noàÚ.RZ JÅÄ&&ˆB”iÅADmZq+‰¸u›ÈDD&BkEŒê uÏÁ¹îóŒÎˆ+%ÿ)Ó}­Q$ŸP€îü÷øâ€ù.za¶mÛF½^/îР+ŒÉÉI.Ž;ŸÜÎa›*Žn ºÄê°ã]Eú®Îež;7¹÷Ì4ýYJC2êÌã9‹NÀ–6ëw<ÎêF?u@J‹j½J{ªÚb¾·€gíw$Õr Ù>±G'$Ò~OÑ!p…CÙª1âŽPsê”íe»LÉ-ãyn¯‚Gé„ÏÖZÅ!­À§NÐT“´£¾ö ã€Ðø}£´CÊõ#=ÚÉ$Rož1¤¯Þ1|2QXa…¶÷6˜•"†ü-+ñ–'[»ö#óüþÃ8@)…g•©º£™¨ÞMFàZ¨ b§¿•¶ú6ÿhÆjã0¾KJ67ž@¨ÙuF½1j¥®íaÙ!d²iÀ(C…„"¤Ñš¢ÑnÐŒ&iÆ ÚªEd¢N‡ÇþÏð‚Ëa5¤ýß%ô;Óé…ýÊ*3ädïû¬ò +ìÿoZ…TÕätåøºG±tÑI s¹#y€OW*i¡´BJIÉõza,$}ÅØe„–DF±«½“ŠS¥T.±ÿø"ê•1„Çr°¤Ä"V1A¨€0nÓŠš4¢)|ÝFÖlüV‹v»…2 cTÂu=pì©ð»wv„é·²%ãÜú®ËN‡êiQ"CVüÔº…VØÓ È ,‰JóMþ²’ÅÙˆy§Æ! qmG¸h%tŽt°…Ǩ;ŠeÙ¸ÚeÄcÄÁ’hk;x®›¨Ø‡1vƒVÔ¤6hF ÚQ“@µ uHl"ŠŠW%21¡ö³Ùñ‚¡ÀL~v"ÓMtê `›\™OZ¼¨SGÔë=ÐïÅR@ya…¶o¨•,hg%z*<ô[ÝÒy­éÖº˜i1©ß¨°ï&ÝBƒeÛÔœ:JGTíêÞ(U§ÎxmÏ+u8ðDðB)ED~LÉó°¤ElžØÄ–ÖF|DÛ"Wž*j$*ÉyÉÐ9Ã\Ùð³4¬óLÿ(m‘a¾MFP"ù›NMOúSšÂ +¬°}æiàF«ôÊäŒTJ^g§¯hÖ݃…†h£¨¸eŽœw,žë`;¶ÓYb5IO;l3Õœ 5hÄ4‚)BÝfÉÈa0ºˆJµÌ2k± ÙÒzeÚCA:Œ"Ü’‹´-â8ž¾ÍïpÞ$@Þõi™"“i_“¬øvO‰,€¼°Â {š]ºù|/¨é ÎÁű<ªvwœšWe´<Žë¸ØŽÍh­Ž1š0ŽhmZa“f8IhªÍÔÔ¡QģѬŸz”д9hd)µêËÆWâÙ%žhl O,Ú¥%!×FÄñÓu[35ÇlQÐuuv–2I{A ·ØÙUì,`¼°Â ÛwÞÿOfbn1¤|Xðä‹N.·tp-²U¢êŒQ÷êT¼%»„c» Rʤd?ðÙíïd*˜`*˜¢©&‰Tˆ,I¬’EC50FgFÒR 676`”a1K¨–«d-ŵ<ÖM­¡52LZc”ÆsB|H)yæ[уÓÎ`RyvR’ ì»Í´âÞïÉôZVMNNwha…6G |§ÔBdÊíûŸ²°éêvZT­FÜyŒz xW½¶c#,ÐÆ "E;hã‡-*^¯ä±§¹‡Gö<@[52bVdc¹%\Ï!ò#ò]|Õbcc-~ì³|Þ¡ŒTF8hÞÁx¶Ëš]¿¥O¡;=Q  c…t,ÛBÇ*cçˇõa\EèþÍÎ3Lét eÓeFvb¸M—ü_Xa…6#be–.Ó"“'n!)YUêÎ8cÞ8£åñ¸m+éS",ŒÒ´[m¦Â ¦Â)¦ÂÝì vƒ6ïYX^BÙ+õ%wREÅŠÈð<—8Œ1Ú nlb¶´7âïh±|ì0ŽîÏóãÊ2ëö¬f‡¿MB¥(¥“&Z®‹¯üNþøtÔP•³äËЈ\ä %}%ºtcH=@´VXa…í;4Ï'Zt±F"°°pe‰ª]aÄ¥îŽRõF({J¶‡%mŒ2øa€OÒŠ¦˜ ÷Ð Ê'Ò!ÊÄD&ÂÆÆ„I;[aõ"Ý.q#Lj²Û› mAf*ÜÃã{!Ò‹ê‹­Õ9DFy²Â­õI_­ ©T üm†/ÔΆ°‰†g•…éÐ.»»Ô™Ÿ¼ˆµbz§{¥©E ba…¶/©•,°Iléà ²]¦j×+SsG©8e<ËCZÊD* ÝÚM;ô™hï¦OÐVm"wZЦUÒŠPù(¥qì¤q±ìñÚ]ØÖ:iEk{‘o0ZgÃÞN¦aHÈD¸›pOG0¶˜z¥Žg¯@Z’­ÍÍ´ã&JiŒ1HËBk=­s&\1›lÖÊp©!C7›EÑoX£R_Xa…¶/LÒï±bw sʲ˜7qgcå1ª¥Žãtå qãû vµ¶³;ØI3JRu¯æ¥CɈ¾³èG¾†0ŽPqŒëº”d …!¯Æú!åJ+T¨Ú˜6*šqƒG÷<„<¾ŒZµÆJëÊV•“kiê&by6J)Ð:µ¯A¾#½r™y_¤Ý_2 ;}Б¢>[.Ò­%‡e·VXa…=õˆÜ.U«Æ¨7Î|o>£å1Jn ×uZ¢cM«Ùd¢½›Éx{üøª1I'@3mŸ’ᦈˆMŒ‹KÅ©  ¥ãtÓ‘BƒãØè0f¶Lï˜ Í5´u“¥Ñ æîÇAó—Pv*<¾g-»‚-ÔGªà(‚ ̇ü+3C‡RtPo±3¿¨9(ìfüPa…VØSÆ%KGVPsêTœJ’M"=$’8ŽhI^wߤµ•OdB´V(ToMOæ¢Ø,Z™ÇÅAP­Tñ¬2«G›˜¡l€°íãx.¶mÇÑÐTiäŒMÌ+‘ âC˜_[ÀxmŽåðÄd•)³ ÇÑDQœ’”LÕäȦÁ†)éoìt?ÌöXÉ4)©\•~ ¿kóöÏÀ83Ô+Ïæ¶)$cLáGþË…T"«&e~·}$­L“GRÕo*TØïá-‘ÈIs?ÓkÁÚ[Æ)¶´Y<¶W:H$J+íIü¨M+jÐŒ¦ð£±Nú–¤×îÄ ‘l¸óXéˆHEžUÂv&|MVÅ Ç6¸%‡¸g@^O LÞ‹uÈî`'‘ ‰uÄ~# ­âXÛÛ›Ù¥·‡?lçråûÉ`ú½YD7éRla#…•hyæ}X7ѧ›f¨…Atµ2SÚGlééÐuû–wÝ€6éw:SúÓ3à“ï°:Úui'`I dzÑÚõ¦#3wa4C.«`Ÿ†;›<'5Ójò0'•W0™nlfÚéÒÜrƒÌ€ÏfNÇ”ÿlZ`{pt3o×,ËÂvä! "”ÖC'ù®mý+bQµj,= €mí-lio:ÎaÎ?{.gÿ°O‰9œW³7ÁÇ4ßû»l;S@$fù{yÍ>Îa­f³€f[¥²GèG Žp©»uÆíùIO“RÏéäUG†Ài‡ ö´vÓŒ÷àÇ-ŒÐ=àÎ/ˆŠyf3$vehwò£b"•µ8–ÛË\I'rô–CŔܶ㢢(CädEëÓ}“ñ«÷FSMqH|8õ‘: k ©—GxT;lkl&Òa§°ÇÂé-—e‰’U£ì”©:Iª¥mYXV"¾,%)Áˆþbg/²êuçMÉ, A—Ñ0Ýèz[ÞìúW Ý‹ä“æí¤n“a…Hu9‰b´´ˆÃ(“-#2…IÓé‚ìmü#f™nã™âC3ëc=Ý+2lØtßtm†òŽGýŽé\Èìç)?§:THE҈ȶˆÃ­õ@Ì’v/=×, V%Ë¥l•ñ„×Q0tfèã;ÛT[ÌÑùÊÜvràX‡ëLî~æ{ff²qæ{›ÔlÚdšÀÌ^\U‘Û×0§8ýñ‰NÖ‰k•([êå1ƪ£8ÂÅ‘.FZ­&í¨E3œ"ŒÛD:Ä);(+Tï{…9E é©’ôUvtñGczWRE¨Úh­qÏv!Ϋw‡ cŒçb»&Œ:÷5×Id^w¶Õ1»ÛÛùm²L­`~m%¯Ìʇ3bÕ ãÏ.ãÚ.®åbÛŽívZÈ„B­4JÇøQ’Z©_µ“ˆ\Üž¢Ó'¬ŸGnD¿"&ií(sq^¾‘K¿[Y ÚÅÌ7g·A—¤x®›DvÊ`ub²Ánafà¡&åpL.÷Ý |~¶(Í }ÜòÑ™%§Ç¤3$òf˜2CHŠFÓ¹¢Ýfüù*¹é¡[Ìyw{CÈ!Ùï+Zk Çs°…EEh¥;ô¹É»Ô}c ŒÛHw„’åQ¶+„a;ÒÓ_ÃôlRLKˆ9#ä®C§â&ÕZnÐuˆ9­ûÌ<ÙÉ1åï·A0¤IN‰È-‰1#e&ÒôçÐÈ×L{WZXXÒ¥d•(¹eFq*n•’[ƵbEp?ô™v¨6JG=ôP¾ëy8Žƒ é¢ CëV†w=œÙ1¦Cm~䣵Á¶lËËTŠ!”ÖšÐñÊFi"­C‘r‚Ý5ŸDËÓÁ–6¶´ñU‹8֏ޠ^śAʤ*Uk…2eb”ŠñàHŠ IDAT£XEÄ:"Œ"|åãGM|í©HGØÉ"Sz̦wËL {Oþ¦R±Š…DŠ~\cLªí-CØp3m,;b¦¶ccK‹(‘z¦‡AN;ÕÓFÍÓǃ s°)†DhÓ2‹0sŒ‘†µ·4©ñ¤N¦nºþœFÎõ›9E°éíä–{ÐéH£t„]v)Ùq‡qîûeæ\K¡n#x²Dͪ3Å.fïD‘>/{KMˆfY&·z0[ýÝ\è)3@²‰Y“̦›mÍ%ÚŸm6•¥CÒ«<Ó}—Äê,¶YTì:cî'¹»z¸C6C*§'¶ ûm°d‚3¦ÃHô³W4Ù¤kC„‰ …ç+º–쎶(Þ/ž,QsG([5jN²]¥âUq\)e"¾¬^¹â@£"Åd{’F8I#šìvЩN P&+E·ûá°éIMÖzQ€=°Ïr«ÙÚP3dR˜WÐ3½ø®ÿøu§ÙRlÇÆó\‚fš^6Ã\)3ô› Ì…6™ío3}~ØëìÓäé‰1ã÷ÏtÜ3=ïrf×\zÒmˆš¶gãzŽeøÁ´c€·1ÆP²]FKu¶ú{K‹íÍõøÓÏUÌŒ÷Î\£oñ;|nvrnï÷'fuÃf˜‹§NÝÇhi¯ŒíX‰0šF³ÅT0A#ž ’mÇÚh¬^ …È( *Œq-Yñý°“©a†.TÊ«&ö²w²ïÈøÄ±Âul\ÛÃÆîÍÌ´3ƒïû”Ê%×!ôCáâÙªV•Š“TTJU<ÛKú¾X)­.§CkÚ~“vØÆW-F료”êXŽ…R°ÇßÅ“Í 4âÉÞ¦“GÖî~0냳S‹n ¦I)9›¼Ì© ô4‹/¦“И¤­î¿BâÙ(ZÏ Lb€S¶&¨ ¦™R§æ¹i]žÊ d¿ÏäV²ë ù‰±˜a\ƒÐžm&”_)˜-—¥»ª®‡ÌZº¸=lf ùq>¦“¸j´êP-Õ ¡&TK긻ߢtÆààPµj”e™H‡ý} QX™^&O'™! ‚b  ]þóÝï”ÕsÏ2ÉR€ÃÞ˲ât‹½fÆüéüz‰™† ï>™&—Çl —’,%BÃÞ(e¯‚g—“(Vüv›fÔ`"ÜM;l)Âv-¤ wîÈxæÜ+CäG”kethP*†´¼lE‹8ºìñ—Àéßs1š0p=;áñ…ÝY\Ï^ëþ¼Ì¢,\Y¦.G©ÏÊ\áàØ®íâØ–°*Î"&Zž±µø•½g“ÜH–µù¸CB¤ ªfu÷Ìì»¶fkûÿÿÊ®­°wlz¦KL ÊÝï~"ÂÉaYu³ÈÌŒÀqÅ9çž{6'ªöDc×âœ!?¯øzÿ7>¬¾P$+þòø•,Éøçþ<×ßûiÕñçÏøô:rM³ƒV“oêÈA‰i\ÔÄKwÔ´4…ã¯ÌpxDH’®õhË›Je^Hg–Ñ6;å=øj‚5«ÉéfÜýeûY2Í_¹IÙ&6™il”§¦˜Vàj‚¥ªI’‘Ѿ}å/–5)>‘Y¨r“Q€è5H"ˆqgHW «<Ç´k,ÎÙÑõ±Ôm ÉŽL'¬ã-‡æ&IË×#Ìè ’Àuô©^dr݇òL;M¦Ê›ìnå Þgl¼÷r—Ül¼ÖU\/©<ì¡oO{AJ…B«„T¥äIN‘ÜqŸ=P¤Y²B°´¦åTí95'ÞêWNfOkë«u«RšX"¢H{$Ï@O¥ç°![¥Ô¥ÃZ7^j¡:Ÿ>GÃg_<÷ü€- b©ˆV:ŽN_¼ÍUL%dQFžl“-«xC‘­Y­ ãzgCãZÓдi(ÛŠÊœ87gJ{¢u-Vš+n?³J þkŸóT~ëˆY¯u¡ôî‡=d‰ßtus“^®Õ @Šê°0 ’~Xç ³wø’žµëˆ¦Ã£Dgÿ3t‘$ Î8p2ÒTþL•–ŠME“xÑna¾HƒY¥óþûXRd‡ähj†ý¹ÀCïûtâ©^ühøOT÷ï žSrP4$ž÷æ„æÔ¦ «,Ã%Žê\÷ó#"¡lOˆ8’(á>¹§1'' ï#T§þœ«œxíûoÝMlhÇ)~‰yŸ¤ ;¬Ñ‚¼tzŽºgN{T8jv..äÜ.}àCþ‰»ÕY–¡#…u޶i9”o<Ÿ¿³o_9Ûó(õ]¶êa¥Ô¨@œUÒpê²b½Ù¦)uÙÀNPž®hyÏ¥ÿ”÷ß#["Š®¢Ö9VZb•°ŽwlÓ{ŠdÍ&ÝQäiš¢t§œHGxª†ªªy;½ðÖ¼qnTöLeÊnµç/оgÅá8Ö{$6Ûg~‰ÿÎÃú#÷Û6醾ý'ÿ<þ;§öÐóyÔšaäÎR-A×|ã&Ê ñ,Óá™!–¯rÄu|½€ãù3í2nÚç«7oŠÆoP¨‡+«ÆÁÂ!üñÃÈuPß#³D{ˆÕžV” :ˆç«Ù‘¹éaÆš6/TeNÈò”ínCUV˜Æ^¯BãÎXgXE+vé=ÏÕ7 mp^O‚x¶ üðZ- ¬Áw?Ëe¨t3¼>!®D.5ë®|{×çSþój"ä|šdáÝ©sj÷´®µôjr=|`¥R Déè:b®F¥…ò2;—b°mZt¤ÉW¥-·Dp‹§+îwxꄸßá™EëôŽmºc•¤QNžf½v[°ÎQ6'ê¦âlNœê§æHéNÛbigÚ¶EiÈVι΢v‡ˆ‡S“Þ\¤³*Ðq„‹„×êåKÉWów>®?QäkŠì_Y%kþyüoÕ ;wEðϺþÌλöÌGGB þ[ÔLo>Ò.«±F{ š›tÒ$!ÑšòX]£ý¸¢š?ßVií­ §ínHª'Ü· ¨8dVYßz™é+*ïÑõ#Õá×84>Ó Á_¿ð°T¨E&>bjPe –Œ’ò6ÂÃ÷{]«ÕZœu$YÌzSP•gZ©1Ò’Ç+vÑ=GýF#Í5h–f9Õ,•ë ÎØ/Aß=á…”æL 7KÞ2QøÎˆ?êIöÍ´†-5ºò¤à1û̇Õgvëqa­£m[ö§7¾ÿà¥ùAeË+qy©,™MVßN±ö¨]4 é‘n|.P³A5A¡\¯bÙ¸4¡®ÛëV="—ÇЬB£”&Ò«hÍ}úmvÇ6¿£H‹ëOA°­¡5 JwC‡ß_ÿàÇÿIÕœhi +Ì $çhë–HkòuNy,qn,àf¦u›®† DUÖäyF¼J8ßøŸ¯ÿûú•¿ßýî¶;>ßÿB‘mø~øÿØÿ¿g†#ú2i“%èfàêòa¨ÐÞ‡×!† Ž5QQ×Í•½Èö8ˆ½·]z(#úõî{c!ãÇX5èÔKX&NfÁpÞk(/M¬¼j‚?'Dû¿^~¿,¯Sžô ‚ƒW #XNhËk,ù:#Š4Fµh îòžªï´¶ ŽÃ,çø'ný¢ÄÒ[Þ.ô]=£oÏy+IÿOïÌ©ï:äzÅ.¹ãcñ ›$iB¤#œއ¯å~”r0¯g¯ï²*]M`<æê©K1¦”—%›_§I‚²ŽÓ±dµY!JÑ”Í èð„„<)ØFwÜåÜ·K_ÓˆXG]Õì«WŽÍ³9ÑÚ†ÿíóÿNgˆ²œš·ëg—ÿ†FÍ9¡*kÖÛ‚,KiêçdA ¬f»Ë/c,UÝ9IžÒÖ-žÿÉ¡yãïå¿ñùþ+Ûõ–<˹Ï?òï/ÿ7ñeyÄœåUÆ]®AV+5®”ò r—ŸÛE@Í «4ïX`ãF­ì¸6U3m¯šUzó vš |H®‡÷í†E"kªæ˜¾3ÿm›Ò„: 5œ>ØÊ íèa[6Ò¼K@<¨&°’òª4ðXíÁÅS_X8ïL”G!-­£:ÖÄé™f{&É?²aÃ:[s:¿Îî’ò`òó>dÜkH`œIÍæeö½Óº74­0†ÝÆýÏ¥ŸÐÁÉST¥f#äjí ÏQ¬VÑŠ‡ô÷ëló Y𣉩›’·ú•çóûº[Ðp±‰½œÞ™1¬BÉT.0‡šfå-%ƧÆK]Õ$IŒ¤)mÓv…žJH¢Œ"Ú²Ëïº ÊtEž$*AG½Æ½ª©š’csàP¿P›’Êv£íGL‚1–,Ö$juÝ04wè§­¬ånòR'”§Š|•‚ºjÀ-«§ÔDnzùSgº¤“¯sœëø£ÒùÃÿÇkóÂ×Ý߸+x¼ Iþ#ú|+QjH”´( a*u ñÿ!QÄIL)LeË5è‡IHíéTàL}Hð èzò¾ôLo!ŒÓ§“d„—ë~è“æ)O@½ýL;¸ z"#“™ÛÛ|ÌBõù¬bBäª è3&]ñ¦xŸ#‹œœÍ*Géμò̯›a•ì²;žª?qÎ2Jðë€æª‘P3ÄÖ§Ý ’›Ñzô»òªUT`BQ \‹ÔHY6'щŽYÅkvÙ=ù'v«;Ò$É£jKNÕ¯§g^ê'Js¢¥}Î)§&÷†@UJ¨cE¬ÆêÌ &éâRŒDžÎÛ6–,ÊØ­7åHtÊ:ݲËîY§òdE'Ý׊¡jJªó™s]²¯_95*[öX¿½_«:ß{­5iœ QØëîN|Ê@×£¢ÒKS5¤EÚ UmoÛ­‚]ã¼ìb±k¦6äEF}ÓXÎöDs®©Ì™¿´ãÃîë|s“N À¸äŠû^@Éó8gÙW/œOgŠbÍÝúžM³át>Œ6‡¡ÿ׌ƒˆO¯å–ö¼k™¨cüdø´è/̤<;aBÂV=Á…5 ¥4i´âKþ>®a»ÙÇ ÖêºæåôÄ·Óï¼ÕO´Ò^OR´ ª”Yå,#©©,R¾ý³è§Õ¥Zù˜¯c¼B‘Šˆˆ(¸çSþ…ínK¦ Ò$%Š:_ÛʺäTîy.Ÿ9¶oœÍ¡ÇùÝBåÛù’Û‘˜ÝTf‚Hã½,ħáI7­AÕŠ´È@¦Ç÷ÆF`TÀ:G}ªP›œ,OScœÁbxi~p|ÞóV~å¯÷ÿÚ-_ÿàÊÅÝPfØ–OVþuyèõ$¨%iJ¬#ªÖyU>€¯åñÜÃ=µðkbÔŒˆ’Ió'3ÊRý„{Å´í&¨Å™ 3û”j¦´À+\j}‡ŸrŒSª †ÞUNÚ .¸‰üî=öAÍæMoîp›©NIHÙd[¢ Êc…įÁ c+o²Ržn1û„HPõ¯¼´»ÏO  ]€êÃø}öȧâ+Ÿ6ŸÈ³ ­c¬1ì÷o<¾ñ½ü“=ãœÁu 鄜ô“¨óÀ®‚U¤öÚàu¸t¬4í@û¬x¯‹Úd¯Y'w<¤ŸxÜ<çIÚYº‚¢©Þö¯¼Õ/Ì+‡ê…Ò•]’ð0Çq9¯­5ž­±J1bðoIбuE¬cµ-(Åuû>"’ûf‡ëSCV¤ä»œòP^í-ZiøýüŸ¼6/Äjˆ‘«…ì0y¹‹Gùµ¹Žá«Á½H‡ ÁI§1§²ºŽâ¢Æ¯;›Æ‡Üýä^²GzÏPýDVöVËO÷Y5láч¥0òwj’`§IÑ-$@€–ÌÜ3üÝg¾ÎSªž@²ê'v鎘”Ôä¼4?HV qÓV ÆØ‰ï}RO…éƒÜäSɤ£º pÕOèa|ŒZ\1ì#‡Ã@q5¤|(>³-îX¥‘Žhš†SýÊóìëWªöL}•úÑh7ƒQƽøŒ)ø3«x¥›€Œ•ƪ˄8^3‡T§¬’‚M|Ï]þÀ:_“'?w¤bŒ5OGJ{¦¥âõôÌ¡ÜÓ´F V,2ð3÷qJÎkŸ­pb;x%J¨/Œ‡N”éLÓ:ÎÇ’l•¡tD[·8g'Ð¥&`$ ‘Î>7•„ÕfE}ª°ý¹wX*sì  ´“þÊbðP^¯ø¨Î œqµÀU !Ž#´Ó¯nÓž`M.Žö\>¹©„ Š1μôPá‘©[ãA ô(–Ô½ÊóЈG?> x·žÈM*XµÀHøªÄÛBå\êA ï‚ôÖ%&£û£fâ̹NŠÁ2Ánty_¿ðËú¯½ ñ‘ßêÿè–¤I·J¬m»©PçfÅGøáyŒ¦žõL$³Sù븂]®ãÅË_(¯;»_¥ÔyDäq·-æqõ™ûÕ#ëlZ¨MÍëñ™—ó/å·NûìÚɰÍrA‚5SýzèZNú +ޏ— Á̈„D'$QÎ*)ØeÝPÒ*)ÈÓ ”º–ª)9×%§æÀ¾zådÞ ´£´åˆ3ñCŽ‚ÏlxVê~R5Š4‰ÎˆÐ“Ä7N´²XÞŒ“]Û´ i–vÖßµLÔ,ã©í.t“ŸuÕ°ÒŠ¬ÈhÊÛZ,‡¹¸^Æì•Èà=vò! VuÃb~ä§uÖ%@D±&NLÕ^M‰æÀßz+ )7–+¿²`: 9ÜS’ÒKÇ”ç\Êáê]œmÜVkÏãçÃ'ÕH½‚›'Âð4£šÜ\ ß(‘ŸÀƒ›·¼ï=ˆGä<þ‰G³§µ ëdÃ6Ý‘ªŒª9S¶†4M(Šœ&i;é¢uƒªOf¯,ǘznû ¹üV/EzÕ(IÊìÚ Ïa¤4±ÊØå÷ü²þ•ÇÍG²´[„Ð6†Ãù•ß¿ñ|þNíN,m€òiáíÌdô¿>æÃo 6ÁÔCçq7ätçk¢¶é÷ÙGîò¶ë-I’ 4Xkiù>ñ|úÁ[ýÄ¡ÝÓºêv}M7|³Þ¬8íKĉ‡ëÀ“†Ç×ør6[ÛâÄ©˜Tç(­úýË»¾dT ùÁYE‡‘+¬H1ŠÎýÓ9ñŒðM;å„êT³Úäëõ±Fõ~<~“2cêÕ;NÕšåÅe¾ ’å+°îJpê…à2}u=¬b… 0D^ð«ŸKº…(?…ƒ¸šx4.!×"ùF&_rú¼³°½HéB¹ŽÒ¨ÅºÓGÁŽ0_˜6½Ã2¹¿—ã|h÷lÒ-‘޹Ë>b«ß1b0µáØIV«í c,õ©ç†jV{úçXÐE]ÔSnôØÎýTÔŒýwm¾wœéùg¾l¿r¿~ I:]tU•¼œŸùcÿöí+N,FœgÆ@¼]ưŠÖYËP77ç B¾¡ ,h‘Ç yTð¸þÌcñ,í4ÞZiÄBY–ê¯ç¼VOTöˆ“NQ"“ë&ÒC…b½]q>t82O#ÞNlšlkJœr¤±&‹STRóûÇ‹oÁÞ6†ÒXŠÝŠH+êªÅÇtˆpîõtÛš©„Lg¤mÎ.½£ø¼%rŠ„¢›ìóH`Ô<„ЪÉ'™›ô +tˆÓ´»xMãÍp¾‡G–,O›A<¨žß­YÕ8£€R^°ÄPEæs#<â ˜þuÂj08¢½dt(„.{v÷eŠG×1vØ›Oä…€A?sð(³Ãì½°ožø$ vÙ#ûæÆöV mÕ`%‰cÖ›c,mmžç—{ëfpÈñÁ+¡nÐwêú}ŠgªQÍΉ»BŠ1yÔðÇõ'îŠ-iÒ) ާÏç¼”ß9¶{jS""Qf}Ù¼cõÉ!§'ÙMÚÂ𿈈TLålÒ;>o¾’msÒ(!ë½\Œ1œŽGN퉷ò™s³§´ÍÕÏÅ-‚¥Ö9êºAÇÙ*¥ªĺ9¾<²Hð„­i:«ˆ>X^lýÝKÐwvº§; pÂùX‘æ)é*EU-¦5#ï›HÇÄ*&R]%«xMå$:&‰»}žiš’$q÷ÙD_†yF7F–¡=©^§{QºóS1õe±ïfÏiÛºÔ(ºY°›Z±Ð û|Ÿ—Zmÿâ«ðT ¾‡eŸ”)¼£fUÆ”¤Ò=óyK©l~ÕBÝ¿ÕEÙLfÝ÷͉ҔɆ»ôŽ?t‚²Õ5Ø8'HcqFHó˜(ŽQZw‹Ÿí1IY˜UfÂŽø¯x(QÎ×5HÐä-„·vÂ.€¯Ón ó¾¸§È ¬tPþ<ðtü“·æ‰ÆÕ8±£“2)V B€_ [`Ì—XȤ3TÄd:#KVÙ–‡ü›lËf½C£hMç×½¯Þ8T{^ËgΦKF—a$ð¨o–Y¬P• «UNš%ÔUƒ¸)ä©©Yèìy5Ä:ñ¥Ä ÄãæJöT`¶r¬V9«Ý©b»§T¯ÈÓœÞ'UyÿÊÃæ4‰§ó‰§ã7~?ý'çöˆíý¿ ‚y·ßGƒä6}®ÞóåaV“kêŒmzÏ}ö©wç+H²% ÓZŽûÏÕuräåüÄá¸ïÒŒ0q}¯È;*º[(n ÒU(š¦¹9²>É— Öu»dó$›Î`ÂW¯“Sf¤QN¦sŠdË&Û²N·¬Òœ(ЉbŽ¢n ³tvÓXÊòÜM¦š’Ú–”í‰s{¦2§ë¢ %ä¹vï/·Þý«Éðµøè©y³2¬–Õ¤BMâÎÎÒ˜›$JÞ‰ó-}c{RdÞýVÿ¡ªX_a™ñÌ£žÕ¿2è<Ôb÷¡&êÆü3˜ÛÒ¯ù:žÊÃ{œ\IDATsNÌ„˜xЇ쀧ªé«…†¢XïMÙ‘Òœ8™#iœ²Íî(ª5mS“"BçWqjˆbM”F›ÖØÎÐÈÚ…ëæ¡û-y¥šAt·§&Ry´â>ÿć⠻bG–t›Ô§#ÏåwžËo›=­q#–H-rS˜‡…IÚŸYk­G½‘"ÑYç*˜ஸgmÈÓUgÂe„ÓéÈ¡~ãùüƒSs ¶É:ÂHÛCA·bC¼ºÇìÏ#Pf°EÛcÍÙ*Cè¼In ؘ͙WéVµ©þó]¶¦©ài/ÅÄqJ¦ºÀ½J ŠxÓA#IF'$QJ¤#tg»ˆ±-UUÑÚ–ÆÖTæD£\d: Ýó™Ö6XkºÏâ,`ûQ®ñóv¹±Ï eêã0&1eöÀΧeTCGQ„NâNvã¯ö×D ¼þ.2[k<¿è zϘÎïÁ'¸R‚Ô̪ì¥ÚE½³ûÒÿè?ÁÐ.@<2YôUW‹ø{È[ Ù«ßéD|÷Ûgiàfà{cùþ.áòç­«x«^¸Ï»©¿tÇkóÄÈ”MæIÄ:‹m‘µ$iJ”FäÚFcÃ97£*o„¿ƒPÁ™‰ÐÄâÍb5r6ÙŽÏë¯<XeÖÙ?¿ñãüG7FïšQ®ðhúÕXp°´¡È7J7!óóBZõû;uF‘¬ÙåÜe¬³5Y–ãÄv¸÷ù•C¹çµzáÔ¾ÒÚº·PˆÉÐ:"Ò ëB]óðYš{9êÌ3öÿp—­bIDºJ»J½6`Dq3 ìy/ãKâtàç4ì¼¢N9¢ûEÍQB¦Sò¤`ï(¢‚,ʈ“”$JÑZáÄbÅ8Û-¥6 MÛP¶gÊöDe䮦u-N9tqã´¥µ¦ ÝNcÌåïâPKõž¢dyíøWš§¸~ßß½R¾,‡wF¼ž’€ã¥ÃƒÞíÙ›7V>=¶Âßú‰'ȇªÛ^ qçËÆß"€ ® ÆÓÒ‡ÐÁ%¾ï«Åud*ÀkðîÀ¹©rÆ×ÇGÂ×òRçoòo$*¡ˆ¶D*ÆJ;æqF8uÿ'Æ:LYUŠl’¯3lk©Ëkå%\¹êw°ÒP"×hb³Iv|Yÿ•ÛÏEÒPkžß; ¼yÂÐ\?‹V,ÜÅá>uª?ÓÙI0ê×/˜î‡â òOìŠGVyZ0ÆPUoçW¾Ÿ~cß¾Pšvd|Õý<ÛZtªûJ×u>J Up¾ž;¼-WÑ=àõ±Bo²Î›ÄJ¿äáv>†?ðr¬8RÄ ±îd‘—;®U—€×ñŽu²c›nY¥VéŠ$MPºË ‡kÖÎUÅ©>±¯^)M7‘Ú´%µtS¶Š±Å¶ P¶ƒ‰VëŒUšÓÔ-mÕŒ‘‹I|ºÄÀ˜Å€óÞA•…:¤×['‘Ö˜²ö<úxÖ7…ÞO•99ß$¯£Ï´Ü²p`¦dª›$µ¸iÈ?Æî£:ýž’Ë®êòS˜ºÏå/”Ô¢zcìÅ3îpð þ9 ”>ªÊb)í‰}ýÂ.{`¬YÇí+¡r`¬¹¿¿n¨Bë–,K(vëniú•baY‰Î±äÁ¢¸Kù²ù+Ÿ·_È×9ÊjÎå‰çÃþ<ý“CûŠí«2µå¨…îŧ9Ònæ61+#ï£éN£œM|ÏÇâ 7_Èò”8Ž×ÁÏ/O¼”?x*¿Q¹âÜuGAä9“Îu‹Ôå­X¾¤ãòÆÐS©¸x“d仜z_vƒb]‹î¹f‚ÂaL§ëÖZ‘'É-›ü<ÉI⸋gQN!®›Ü<×'JÓaÙG³çXh¥ÂŠéƒpÿj"L÷AMÏ›sÝX’Æ$yJ’%´eCÛš™új¨!‹C¹áÃ;Ä®üÆJ×7¦ Ë2šÖÐZë…IBDÇ\½1½ 22u^lw,Œ&UoÌ7 0Hö/šsr6|݇Æa›ç!dÆÊšp¢ó¹!Ê„4^ÂèeA“î{l$ Å¿~zùŠâ±NeT¹ˆXÞê'ÖÉ–,Z±Iî9´¯AâùFÀ»D!ÎQU º5ÄiL¶ÊÈ2GS¶ë:*-M1þÛÔë­KŒuÊ*ÞòËæWî׬³5ZETç²w"üƒSó6RoÜΤŒvÛ€¶æ+ Ç…Âû™úwŒÖßAª³~{Ð'v«Öù†,Y«„ÆTÏ^Ë'^ë'Î͉ÚV]ò™Lªú{ïâÚÙUÛ9dyñ{ÁºúX™t$“`ˆôÞ$1ù&§<׸Ö^ã¸ë¿1êÀ.Ò8%óîšEš}ø_»Ø•¤DQ<À¶ §ó‰ÆÖœšÇêHëÎ4¦¡u-Fº=žÖYæsÖsîGð[‹@ÛX¬­‰cMœ%DiLÛ´˜Úxø*éW½ñs¿Ô»Uóø Eq„Ž5¦¬zOÞåƒ M=Ê f´€û\ï 9$ÂT ÒY’Œ)/wÆÀÁïÑ|ÅÂE1ã”Á+6œ%M5o§N ¡L2|š[µ@€ŠG%£ú,Bi|ýÇ_/¢'Âh.ïSD*éͪÖÜ­x\}bmIÓ ç,µ©y)Ÿ8”oÝþHsìögNø£ë™’@—.‚(ˆt˜y±#¡Žp~îÔà@É,aß¾×öÞ$Jënc´(#hG1i”‘F›dÇ&Û²Íï‰"M”hîÖw]¬ë½ÍëŠÆÖœÍ‰cõÖ‘“¶Á8ƒ`fh‚ò:ùJõØ»ø¶´Nö=ˆàŒ¥5ÝùŒ³˜4O‰´¦5±~~écY ·¦í>“êÎg›zm»uDžç´µAœóCγ “@þõ4_ª­¹%¬ò™ÙÜöÃPWG5Áxež0´gžD &ɼšÀ'2H8ƒD£&­å{V½ÎSá{ÏÓ~ù>ñgjøsP ¸™À#>BTO¸‘ËõÖýÁÙ. ’ïåþˆXŽõ+µ«X©5ÛôŽ"ÙrnWbÐÍ—K ƒ›s‚Ô-¦nIò„4ëþmë¶¿øí{$mOo¥4Z%|È¿ðõîïÜ­ïI’ã ûÞßÿÉÓùO*{º*ÃFÎàý¹A› Sçèõ3·pÉ`rW]ö«÷E¢S¶Ù=ó_xX¢(Vh­°ÖR×ÏÇ'~œ~çµy¢±õõõG•ÿ8Vß®ì´0‘nª1J"t­®_+u½¿·îK&ËNðýô?ª?©m‰\ ‚kµ¿l²½v·ÊÉk07ªšF>•[hâ Ö)y´f—>p—Þó°ý@¾ÊÓ!Ê)œš¦åT'«5E±âÛé7þëíß;{[„}ˆ†À"³Kïx\}ä¹úÖ6A5‰ò(RdÚé × :°b©O5*Žˆ“ˆ¬È‰Å4ŒE9E¬vÙ#_Ö_¹[?²Ê ”ÀþðÊ·ã<—ß©ÌãÚÎUX̃Œ±RýšÞ2X S¹&ÓçɇÕ¶«-E¾!‰RÄ §ó‰×ò™—ò‰ss 4çûv“¢Ã3«*2XÃ>&”ÕT(Ž8JÆçßÓiÏe–á¥××¥:ýwªsòhÃ.ïôí«,'Mr"•DYë´áx>°/_Ù—oœÛ#ÆÖäц¿éÿA±^¡\ÜÂOÖ ÑËò£¡ KwHŒÝEa>G35SƒÄw}¦¤Ûc*V0QKG$Iðnò+Wrl^Ù¦wlÓ{òxMe«ÙVñ@]âæg—ä¬sÐ8Œ±ÝÃ’ÆÄy."u9Ù>®aoP ªöÌëñ™ïÇߨ×/Ô®^P [ëqæôÁ[‚Œ#Ǭ ë³b‘F9Eºåaõ‘‡ÕŠlCÇ4¦æpÞs¬ö<¿qh^¨mëñë[P‘ŽÇ1Ou;W½¼ðo^¦G2àº)=²J`ö|¨Rš±Ü¢e·É‡˜H§¬â¬×æß³ËïÈâ‚,É;• ŽÖªæÌk[R›ŠZ•ÔRr8½RÖgZÛ"À.¡óoW]‚Žš7LHýs¡A…Ö\‰7w/ì(³û¯&­É•¯r®Óä‹‹#b”qÃÁe‘üòMî)¥Hò„¶½°·þiB7’áøö’ÌI¹Žã6Mª¤ÛZt'%‘ÒTv%£Àq»ã)%WÌvš=¦­Ô4OÉŒ—è£þ(©Á8½ Hž¡cP¡OÉÏ•Yå0¹OÒ¼%²þ«{Ù”SjØø¸É0V‘zåe“X6r—ñûöÆ5ÚW>¹¯¤qÆ6}äØîiúÀ)ª³fÆÉ«NY©ë‚ÛÑõñX,ÎÑÖ-¶6dyÎ×»¿óiý E¼%ŠMÝð²æ÷ý?øQ}ChƒçO qc5M2·Ñáðœbb*=î<µºL#k"pŸ}àSñ>l?“f)JuÚïÃyÏ·×ßyª¿qjßúµfƒ{}MäýhšºTØsþòŸZô¸S·gðšœ]QÄIDÛÈܾÍC⌻UÝqZª¿Ë>pŸ}`›ï(òuïgÞ­tÆRVgåçò;çö@iŽ4®#jWëF`í.S¹é'OÝÎ+;Wu õzNnÏõåÌ‹Í[âÅ›‹"JM6ÙŠßxË[& ü­¬±7¯•0” ÞjÜWõ å"‚iÚwÞŒZ`Õ™Irü€ô|øÖU€Œ\4°’iÉî—Ú w‘.áÌËJ›0f=4±ZÚà4$›Tà¦K Ý UÆ*`¤Äh‹žêÞ¥ÊA².ì­¡=ï¯2gJs$3óì›ï4u½ˆYþ \'„WJ_0DÄh"ÀqnK~Ûÿ'?Žÿ¤ªÏý°ÇØ{…%â¿ukÂÓ¯ YEù>o¾²]o»îuÓðrzâéôßË?pbpâ<{-/P Î`^IÐÎaÞ0r Â~Óî,U+ŠdÃ.à>ÿÈ&ßôRÒ¹©;¡ªKö§=çæÈkýƒCûÖMÇŠëSÒ­ ¨Ï5é*%/rjjÚ¦ÅJW½  u‚"öÀ~ÊÛuÃÜëÝ·O»s•§Äô]Ãá{Ñ“Iô˜@ »õwJŽZ;­HWÕ©+ž*ZIR5UÍH“©‰–á=ËÞ†OùJÁɹèXõ„ŽŸvà Mµ€ø÷¯Ÿgª ÞÒx}ª…^ aÔ³¦üS¸—NCØt™)dܤS›c…óå \+¹qœ}X*h–ÓÉÕÚ–<—Oì²Gò¤`“>ðÚ¼x‰7Í|’qæ`8U© Y«5I£SÍŸçðtøƒ8Jil‰Ñ ’öb/]dà…î“ØÍQ’¹—½R7Ù“Èä¿|†HŬ“ÅgŠÎu0‰Rœ8ާϧnõÛ¹=R› #͘€VãsêõW3jW£JSj a·ãL·sXÅŽ“mïç¢R’8§ˆ7ÜåÝ:»"/HãŒ$NA:9çiÿÊ©9q¬_95{*Óy™×ô;ˆsÄ¿(ÐP©Ù>Öѧ¸Täë¼ïÐ4JEÄ*bo¹_=r—=p·~$‰¢HV4uKYžy+_x­~p¬÷”Í#fLÖOQ¥&«eBÖvt[µˆƒt“åý9Qª[Açá e*ޝŸÍ£¨Eˆq¸ìFÌöõÌ+µXæNcq¼ŒÊ½×J ÜÉRSÒyš*T3iÞÏClènåK°_³ WÈ„|œâ• ÁyÔ3!}iˆøÞø$?£,R?ùZÿ½ûü37µ2ÿÒ¢@…¸¯ÀëŠXžªßY§[²¸àCö…×ê»W <„ï¬iÝUiÉ*Á¶–ó[‰ë‹“ äx!Ÿ¬¥)ªî¤añ*!ÉSœq˜º[í¥–ñfaRADÂ6½ç—õß:MtžÅ¶^Ïü8þÎòwÓm’Ñ&ÿ û¥ÂÍ_yŠgU x—«Xéà×D¯È£‚»ì‘›Ï¬‹u¼µ§háí¸ï‚wùƒ£}Ãôxw·\ÛÍÀZß,‰fiÇðç©›u(±IƒŠ4Q”ÅÉÂyWïš’©@1E ¾\¾ÎöéÏGdn~äòD†! DqLÇÔeã?Ôl禚݄±uëÔ«ZFß3vwöa{s¢VüfPC5…ò+P|Ã#—,zí ¸ÁH¥ij»ÖÒyîº/àúT0ÓÚçâ!=£DFv¾ÓöY&dÍ´â¿5GhyGhkXõÊD®å&aÁ)å5 – †¨&ò7‹åµzâëº&ÓwÙ#«¸ 4%L,`gÒkæ¶½ZAœ%Äi„ˆ¢>ÕLhí@ö2u‚ÆkwÛ­Ÿ“.Dè%!ÎbœLÛbó–C*Q9»ìÅîÖ÷½ÔQQ· û·WžO²oÞ¨ÚƵݻQ·»¦ûJQ{à©k@êøæ,Tð OÊ3‘75 ïQ”²J·üuõ7’<ï–.$Y§gG]לë3ûê™CýÒoÀ©imƒÃ"jÂ)YVu Ní´óϦ-k çS‰m» Ê$JfZ&“¡.¿«ÌåÊq¸¿ÜÁ;J”ZüžX¦óÂ{S†´DwØÒ$ADhÚvr±ô¬¾ð›6ù ÿ0‰ßÃpH[‚šCã=ñ¨¼ÁÑG†©w52r ðÓ¬Þé,œ‡ðuï¨]nÐÁX¯ÂÓæNÚã„è`aeÄRÒ8…Ö¨ùØ}Ÿoîj9•§]RãÎêWVIA¥ÜçiÏ¿Ñ:w½Ç"2Id/­Q‘&Ž#t¢qÖajƒ5S Ô階qw1¯¨{UkA+"Ý é8ê<«/žVÀ¹é›Å)«dËãª[ÀÜ-oÐ4¶æpî7<Ÿ¿_WÞÉ€üšŸÎY&U{¸c½iÄ—q‡g\“D)Y¼¢ˆ;ó]ñÀn½Ck±†Ö´¼^8VåKÀ]ÙUßýEw%•;`ãjÂÏÄ8X ³ÅQ5 ΑŽÈ²¬Kº½Tÿ²È[RJ§Ïø˜çc–:[kñZp„Öƒ “åÿÙ·<‡OÖIEND®B`‚goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/images/screenshot-ubuntu-19.10-02.png000066400000000000000000005646651414415210700300540ustar00rootroot00000000000000‰PNG  IHDR¤CÝ“' zTXtRaw profile type exifxÚ­œi’\·Î¦ÿs½‚3—Ã1¢wÐËïçefI–¯}#¾Ž–,U©2ó ð ŽÝù?ÿûºÿůîsr)×Vz)ž_©§ß4ÿùõùj>½¿ß¯‘|øþôŸ»¿/~ù?/”óùjƒŸçߨéûóùçÏ]]ŸoBûèûÂ÷ð>êÌúþû¾ö=P ŸŸÛ÷ß®û_—üûv¾Âúö{ð¿ÿ;UcgŽƒ 'wwbÓYâçÏx_õwæM ßÇ÷‹ÿ²vî×·[¼=ÝâkçÇ÷ñÏ¥p¾|ßPþ¶Fߟ[þÛϨúëÙÏ·áÏR ßåþϵ»w·{ÏçîF*¬Tqß›úYÂ÷oäÆR|+ü®üÉ|_ßïÎïÆ-."¶‰æä÷rÖ-°‚×’mví¼¯Ë—˜Â •¯!¬ßÏZ¬¡‡‚¤ßvC=nÑXD-òãðëZì·¿ó-kœyï ÆÁŒOüÇo÷O?üùýë@÷*uÍ´˜{¾µâºÞ‚sŠœþæ]ÄîwMó[ß÷Ûý ëï_ l$‚ù-s㇟ŸCÌl¿s+¾8GÞ—}rþSV÷÷,çÎ\ŒE"à‹ÅlÅ| ¡š±Žø ®<Ä&°œÃ6w‰MŒ…à´ só™jï½!‡Ï‘)”Jhz+¥LþÔÔÈ¡‘#x”s.¹æ–{%–Tr)¥aÔ¨±¦šk©µ¶Úëh±¥–[iµµÖÛè¡G ,÷Ò«ë­÷>'zðéÁ;ƘaÆ™fžeÖÙfŸc‘>+­¼Êª«­¾Æ;nÊ—]Ýn»ïqìJ'|Ê©§~Æ%×n¼éæ[n½íö;~EíÕ?£f‹Üš}£¦ˆ¥÷¾ú;jü¸ÖŸC˜à$+fD,$#âU ¡ƒb曥9ÅÌ÷@Qä@Ô,+8Û1"˜Ž…|íWì~Gî¿ÆÍåô?Š[ø·È9…îÿGäœB÷ÜÆí¢¶Çc”ø¤*Ôšúx¶k7]Jç¨crÍen(Üß>¥Û;5B°,®Rg_%^Ë.·v³»†iÏZ/õÌ{’¾Îãw®ÜUº¥¬›¼V¾Õ˜7Ùg¡”ZI;žxgáß1ðáV+%q©§ÜéïŽ\ÿ&×W.Îü¾åÜÉ-_ósŽ8Yl.wTש€^=1¯|–›KWщ¹¾îë F õ¯C©w¯˜È€:cÝëî.Ø(·gk•òðå+>ÌIäS[£ª?7=\Òé+§Õ­, Äw^ÙdA;,Xì@Ï%‰B*JuFͲ·®`]ëñžÛb*{RýÄÀ%é2OöZ²Ó: ±CÙD2ãìÄ»{×XN‰œëÜQMÇ…:jæ’v±âd¡8Ñ©¤ïX6â;÷_ÎìÏ;U'[ýûËŸ ÓþíÒîìÜmöççÊÝÏëÿ”$˜µo …´Ÿ·|Þ —£m]ãå~×týôÙ«¥Ÿ+›¦â#4›‹·e;üð4… öLðBÉ*§qû w(ÛÅ团B¹ÆŠ›—6œç"6å1"ßÅíïÉßS¥EIÁRÔac™C»ÙSíÁM§)ÕÈšZP@°Èêyç’&±´€ÆªŠ)“ ‡€\Њ«î½­â)ÔäЊ©ŸG2ðujú¹“u¸®ïÕ(!Yã½3‹Q}d¶å3¹(]å!*ëëêZb»«Æ âÍNCPÌT'§ÿ¸z´ZÍs]J>â[ýLGEµÌb¥ïZ½¥¥àò'd½o+“l97°êÞB~Q^©q–Å=+/Àÿ)>Ûµs‚¸þ2mü=(ÿ“úbòîÝ\£Ê-R­(m@Ø:RÛ—oâºÕ6E®…ŠvgPñýå ¬û¾›Ìþ¼ƒÀfäTéhÍ,ÆCñ¦Nvùœ“CkP߬j@D,Ç}&wksú”‚ÔsMˆ†’çÆ¢n\ÇEή½§4+7sÐ}wGsld´ÐªÂYîfâÔéÔ·?Qç|¼-“##ZPW‹»ŒnóÎÁåáo´Y=r* ¿95òÐKà|È{R‚¬qëÑHkqŽÒf]]ôØ88B,ÅBFû;jß±š[°Jôk ‡¹aï€ys¯ ПܢÁ¤\³±—ᯠU ý"!Gô7 Š|x7®f²`›Úº°=Z¸ß±#lÁÏÍ1à(Ù*ß˼y Dtö èT蛟Sé}”Û´¬¼Õ`42 m3í¥Ê­ÌI)ÆŠð¡ªÍ ¨yQÐü¤¼8+Ô hv pì¬G™#a„ü!H „Ì;t:ŽJQHa–û²(÷´Dxa)£ÒÁMª¹ßÃ$ZˆôZ·Ç.é «Àz4Öˆ…g¢»fEe4ࣘ‘v3KFA€NàÃ¥»V›K“”˜-]V1ºûó^›í…¼³†\ T‰xiªø¾0‚+€JX±Ì Wš@—Û)~dé,l¯¡ ꢼÛ—Nï°\í@¦PµÍv€~;Šê k-à/n%a*8¦oá"R'Kx ¬gNrÛ…0¤÷:QZP6Œޱò¨ß¯3¼JcÅææ_°ê¥ÍzœwµÁSãP-ïˆ tÁwHƒœ&ú%%jªÖEúÂ"µóY²0£*Bƒ.sC&zäLH¦¦€(ÂûŽ„ ­T;)!ú€û&"•Ã;0a^Ä@„•Úr©A`6%;ˆ2) Û&,cÑ„+‚[!Qb‚®ûê|9RÁ xøÅ l6Y™]Å®ZG D{n˜R|ÀL €ò^³¤iŽA\§Qz?‰€r;ªwv”•¡1Þ „*=±{ ¬™‹Œµ0×Ý"C_n¡ç¨“Ž‚MÔøU{‚{¡’CžyÌK¶r ½ŸKPå[©Š®R¢d!š n<訜%bY  ºmÖ† ™GÏ›7Œ@ÿЬín‰«(zPÁIçHÊÌE?±\7áÔx‚”õÀì bqSTøI@ Ô÷$¬™ø1:+å_~r1ÌØ—.þ™+Z aÁ©wÏõgx‡ Ý#p$WG¥\(ûÓÌ¡œûX¼¨U!~3ud° †Ç+’›Z?ñÃSxD#4@0+² .¤àh‰ ¨Ð‡B¤IEeº$dL”þàù,®¿Ö´îH ¸,Ü+EÑŒ„yfÁ<Åð"9ÒDÈaui…¡fàqÛ—kˆZ`K¶Y…ƒ²óŸUQ…0æË+]Vžµãé³¢Ÿ=@ÃhêSµ¦3C%gUu=*rÚDpU¨ê³GåüêD¤!sÆšÔÃå2Õ”°ôô† PÝ70✈„ª už  QSúqƒ·E…ÏÀ •~Öu½ßEÂôô²íÂw±oIf“hMM.Ä_ÀVO ÓÞmR"ˆµàÁ²ºËA%áYÏ! 8“£¹ÔGw */˰)ŽÌwxHçF –ÝGs ,(%6õÒàd+&êžy’rɨ,O“¾SS‰u‰0ìp•JCH •"6 >9 <22ÆxI¢ïFÒ£ î­èRn VJ)[SÆî‡‡›ˆy²UH ¸ªxÐ[1¡dÓŒÛMmÀz¥y` 4ž.~&4 4 6‘z|”¿ *×ñÆ[ö&€Kªž«Kû"UˆE"YÑ ý}ÄÇÕÀò®ªñV<^åˆ/J‚ËìÐ0dÊØ`â¥E ºÇÉ¿V` ¤†LJSî;€‘€ ý}h´ôØÜnÆÚJ^÷Hyr!ž,ʧ§L§f)d5ôñäp¯’TkãVXt J8+$`ûòÄ Á1=ZÐR }‘Z›—|…ø¹dŽÝþQ™¼†ü¯|öÊpE8BÞ‹Sï ôC÷Ó]_@£:êÀÇ;­9à!InhW‹ˆ¹……øç(²WbU•Fbø,„‹{ÙØŸLPà‰Ñ\0ˆT8¢ µ ~‚…h»µ%^ J6‡Œ»¡Wd€[Æþó6„b-¨üÔB Ïðù¥¾Â §8 ÙpcÜw,h–A–Ô,ð1&U ˆ6\‹ÚÿÁäö ýAת#·Êz$Ä8n8ëŽà ¸{&[ŽF€oŒÝ‡ º–‹ï—k^Ú/Üsð/|1•É]cÈut«ö§!½JšåFTÌ,R™@²Lb~h» /å•à!X•Ë|MžlR¤ÜÞÖ«ÁŠ>–*x·s`-µž€²jy€]Ž¥|é+rúó¶kt8¦ái{ý¶´o Z㿬qoÓ$¤Q‹õGQþÛלɣw ‘Ä8âþËë<^u~´&€enêºa×À2¤ñBGp¼E=Ëìp‡x }qˆK) l`”«[<)(Ê×c1º¡Ó3`{µÿ“ÔÒ>œƒdP;°8ÊБ(G,u+ï‡ „ÈÌ2nÃÀ3²3ø¢V‰*Þ !u×a.%w›¯ ê[X&pàÇ´i8¶¤½Ji/ó†Ð$yÏšT?—;ùaI¤7uÕ%lùJ`*GZ#SX¿5¬ªoz$00ŸV0,0³{ç€old‰‹´KY€XÎû§©ˆ~T¥9(vÒR8AŠB,ËŸ½`ãuŽ†Æ»ß;Éà>¹M4 U€Ï%+‰f‘Ù…‚I> @áU‡U aR±føH²)ií‰gâÃ`9„ƒü«&éøz߯D(C˜¨)Âs:À•â(ä6¾M½ Jn•Ži`à"ý¨1¿qM¤—ƒwnÇ̵È!Q‡>50UúZm9‚‚Í,Æ3 ”1Ÿýõö)kŠ ÈÚá®6yàD°¥a­$ŸÑÚŸ7Ü¡êd§I+5äÞG·FP†00ïÍXÇ_GM‘1&†×…ÂéËïó·TM_ÚIYKÒRØ€³“RG~ Í‹ývH¦6BdwÔzýÀÙÀ1Ãyµ¨2‰NZõ»÷uŽê´*/ÎDCtŽ7ܶä†ãÀÁÛD'í©UK úI¶4)sÞw¸`H³Š– iññXgyd=åÆ2ªsKµÅ¥ —‰ÈmÌŽÜWÖF,- µ ‚Ešõ«j±̀õzýÓ="k]ø¤ª. ®%‰;”5<)h¥¡¿à£¨¶ïôxÇ8I Ü%²a½LQ}…mL@^D퟾XßvCúøåDmLýSœ5ÒC|4*8Bqó-|Öá ñh³Ü²bjYO<81UH]iHŒ;ÞO É–¤Ü ›e‘7¢[¤]3iÅÝìg¿Àrã¸øTåPÍ ,^PâÊ92‚ôF¬m­žŒ®P–S\Ôoêf× p j(å‹mïɃGq=P픞mÇFŸÆE'Wºb‹,Ù¸öR½¤‡Bâ5¡ûshaVÜ<ÍÐ÷ ²ãܶªÇ——ÓzKöFo”[CU&¤££°ö0±ZÉ‘ì‚F»ÉpXë®ÝCl›ÚM$ÈXè?œð"©ÑÅañ"Å-Û/™e ÉóÙyÛPHN7¦Ž R–[ÿEü°'hõPY¦ž¾ºº^8ŽÛ ›„‚ko ‰=ÕGʉÕuHmR'‚z2{æ€Ç‚ºrD ²QÚúÜVÙF,mJvzµŒ‚49*± A"ŠÓõ-A @såwa&ù¦‘^© Üû âªJä¹E°«SuœúF 1 d¥¡žÓï4&-㽆K„ųò¾X‘ g9ê~¶¤šãfµs.œ#|“d¨‰q–Ý5º(½ìª¡ 6.’s°‚Gí㪶֜±Ö|kDJp~Ð §|G\T‚¦H ðVb cô ­GíüV9j¡7·PužÏVÍ,†È#µÐæÔgå£Q+­È›¢cÇÛÑ>¸õy´gA ºA ‰9,œ Ê@…D¥­Õ±Ö„žµà”jØB¡b2á"WdXÒPPéÇ0[ùÂñµ¹‚vKÜ †J2Ñ.Œá¤ýSÜèsõ«bäbzÊB,Éã1××mWÀ.ü!P7vCX¦Ü¹„R¥ƒa´åÅ}ÕۓĶêˆ1ÌM@çm¾ÄB“» 䊿kŸ|ÆŸh--·ŠMØÛ#n¶: øg¨ÑW›Ã@‚ÀðÁ5Èø½™M¶ õe’2EÕ«V¬ÀUý0Ýøï®Å\ÅøðžÀyã))*pGÄ}Ê/Ž€fM¤™“•ê à‰Ã¬†aoZΤ}"ì+~å.‚¼¾If3v*ªk+ë´¹š6xßž8Ìàë!j°4ÈÂ!!­ oÑ‘¨-8e¿=d˜m>nÕyG1k“»H,>i-ù""IdŽ'F?¨Ûà)ü«z{æÃüx€d»qT:™¹mÂ4†·¢&;˜e”•žííJ³Újÿ} ß+ pD­%° W‹ÈY0Y<ƒ>Eöá4(‰p) ã3Ôk²t©zD5¼¼jEº }»¤&ÑIoCU]¥UYMsXh£®C"M×–žQRߪäj4ÔÇ ª¨÷é5[‚O•BF &u ³x~‡pÐÆ¹£­†‰ÄAª¿ Ã¥«}7w<•ޠܧv*Ë3Ÿ¥7¥¯É\ å]w"Ht™Å©T¢˜2S“š.j°`!zm‰ðM$¤o ÊYÇð\âTÛ«%áv€o°_ýUµA-{ Ã)«(MV˜Š÷Hn¨UÕ¸­àåD‚éJð5-kO VåQ›š«,Öò„iArH{-í"±§¢¤1™‡lO3糄(Å}ÑHϪ]?,¾°XBÌiêÚ¨ £èÃBÞ_:Ò6·_¤a¦8x°â, J‡—'É…¤®ˆôˆB„Ú€F Ê «vÕn ¨/€›²9»U1'À¯ñƒÆÏA`ˆ hÝhr•꯿7Ú¶úyuÔÅP3£úM@pˆGKÈ_°GgQªJä.® Šã•‰?ºhªR<êÚH(ˆjÊ×’ç‰tK¿=v¯ÔÀ¦H EŠN¬åø—Ó0ÒŠ ¼b\¢^™HKÉœD!OJ. 7àÓ«[U«{&uQsÜ/v¥A±@D¬ ¥N"“²d¸×.u|Mi°$ÌhÐׯ[Æ2B<”Þ/ u‚©×¾GÓŽæÆÎëŒjŸè@*9£ZŠ}ø‘ þc1r[.w hüDe¯©ÜâöÑ4ÃеÚ`)[ÎýNÜø¥8(žruToÚ¬–{=Å-¼®é¸1C,<²ðh|¾$ˆ?Ä…’»š–;Úþ!Q’ö뤖ˆ«c¶—|û|EÔ×Jÿ函©›ÃZ±ä=e ƒ¦ ”ª&IÒî°Ujˆ þ]j#¨«^mGÀ¯>—§},BG^(ðxpô7DP0\QRa¨‹˜58ÝÕ* „«¦çú_6ö™ÔH¥2…·/FÍÊRbI (È*£P[æF׊ºOp§â8Öó†n9fíÁªfg¶&åŠ:=,E™rE6?å†.ûVñg³¡ [X«ª‰I ™[CÉy$2cý«Š¨˜vå@—pÏÛl¨C–R[ S&8Äè‘à-(ŒV½¨‘7ÿ$œÀÙ¨¡—í©ôi@PÆTKç;DŒkK™A‘½„¨uÇÙìxqy ¸®Ç¬CQ°Ú‘Cåm5ÂY"m“*Wö¸Ô,ËëÃrœTŠã¨'€kñ[î 9ü OG¤ ¾rŽ4$,ÿxýÔh{0ª7§½³£gÔK%4P²Ô¨Rº‚hqTKÓh7{‡d' KðfnÂ~;ªäÊy“šAÃ÷c\mùä(ëS?\õXQS ‘½‘W4›”IÔq)oŒ1iŸ¹hb ?Òµï*n ¤¶ÇÁ¯ì¡¶Ï±^,0ÅKêŠ-4ftŸîvñÊ\³@‹«.MÜV4…¶çg-¾ÉV¡E¥kbåÄ"ÌUÁwQŸ:ôÛ¥…aŽ\ûZ‰ŠâÕäm§v=üdÚ™b¬ªQÑžž°ÜsÕH¡Ý#‰Å‘¹\ÙdgÉj„ˆ‚‰+­ÏSòÛÄBõ`Ðçð!.LCtK“5ϧ"Ý•²ˆB-kjÈkײrˆÔO zÊ WÍg…U\á»ÑžäÄ=葵{ÞŒ^Ъ\KJÑ4u†’/þEC£•Dšhys,c'Àwa˜Ä/QTˆÕ^obKÍ2Ð$˜1þ@äuô³·JÞÐÝ(Óòl&ñНÆk¸I‚ÝÜ/‰Eùl·ÀH0”æÙ¼&Æ£ø{8+ Àr¤p‡‡õã ª6cp±;3B‰QغhU£þï€òbq-54Q,B_/åæIZ®…[&Àk;ÿЏÚs‰à,bï”·Âéù«OU{!0IÜÄk‘;„Œ½pµÆF!øÊk\›+Ò”`ØšP'ÀYÑfP}¡M¼!ÔköÁUà‰žÇZÛBeœõ{<‚Êq}i²UM„K‰R•`4HaêÄ ùÒÑ¥kâ­à¤£šzÛË&à–‰ù)k8V°hWn[nÆãÇ¢â%a<ÕäQ¬]&êxYú/ýIGé G³ ÂÙ…Ky¶È¹hÜKªLs àæ§ÃƒÚÑfKÏS ê&" “¦4ª ’›UGbz4^²« ‹ºêPH~7 8, :‹kË?ÞdJp;ìÅ /í%=aŠܦƵÊkë,wñœ¥Ë1¡6B=Wµ@bôÈ)DȆ@ýF+-=ÏcUÛÛ]cëmhÆÐµ²ààœ®Ùk*m÷Àß×%d OG¢JRǽõ \=?ê:EÓ²o¾GQ?ßg â$^?œ$p×Btá6Nõ¯7!hçǘF$:>Š\PÇöj\ Eë8ƒÑ÷³3í¦¶{‹0.iúúÀœê&þÇu¡Ô4Àƒ¶~’¢†»Àß«=péjÚö7˜¿ƒgX®hþ¼¿̯ôTR¨!€Pïq;ßÐdWD×%õCs¸ê##†ÑèitK¢ç ¯BíeäÎ^jóHyê)ÍÕÊ—ÂÓàš¶˜ñóà¹;¥d`rÀ˜Ú¯ª=¨EÛfI/øü F%º4d¼”ÙQƒ–ŽòÖ#NS½Çg+Ío<ãÎ;ä?YÞù#Ø7Š^mhÌJcMAu-QÔ£p~Þ†i =bïá°¨öÄno Ðó!);u—5ª];ò%­G)¤ñYmXFðD¤ºôi)ü×¼N•öz= +e`­Y‰\Ò¤Ÿ»]ƒÿ|"<}ŠhX$~>¬Ø<_ƒÛ¿ÔÖŒ(êr¹E‚c•Õõko¨ X˜÷96Ѓck&¿£™•#MO÷å›örSÉ€³i(¤TGA&"}·º"JêDQu¥,.ƒ4°dæe¢†»ÅÆšsPK‡ÂÀÁ^9pý|n«7x6Îsù’V™¤ToWZ45<§†Mï„®3zñ`I¼TĜ΃æGÏQ|z8)YP?– …vª8*~&ÏûùÁñÙÒjÊDO .ÍËø>ŽÓä]¼rë§T!»ƒ†°A~^·›TßM#¢7=HúN (íên¬¹ °yMªËºO¨{g²OÚT‚‡Ú4RÆG]Q¼P¨ÏµMÍ}CdIÍ€×´…ûA ’äÊ“qEèNÎUÞ ä‡ÁËœÎBIB}òÇ(“ÕpÁ/nÝ“G_¬ vÒ§8ùI–4 ‹N*VW¬D© nzÜ¦Ýæñ¶ÊaMÙ9=ˆ b…k3mNv©Æ•í §$<%% |ûA?{[Ÿ=B“]Í=T{?VЭt¯±ª f<áV•í-m¤ˆ È Ï7ï€ †B‡q+”Kl8!júÌAl톴7 ]4”t¸(S½óú’êµ¢cµg¥V<‡V FÓÐ\v™š^ nÔÂɽåéõäÊÔs8ÚŸÑËçm\çìM'¬2›¥!é£.°h€ i0hC£S"%¾G~Õs¬âa5‹tïF•Ãë`)n¦§mœí¥±*‹ÖõØ*TRahrïç¤í×u? ‚%Ýä fÃ9M³‘Úê–ºßT\ÓsÚúF"ƒk| Ð狜CXiÖ„Z„XSØuzóï7¬>¾×Ä*|}ËÄžàýSµ•iʚãÞq*AÏË j²h}Ð÷àÅy–‚‘¼4\«ˆ2±j:ñ÷yäµí{“ȱ \“§½†ë+›ÕAßñ¹á©VlC WO ¨SŽÒXP]ÖÀ£Æ6.y¸…¡ù”»2˜jSÏ=éQW æè§¶ r†ÞÉh M¯Á¥~.Ú~\b­[¢º%TútAó˜N4»‰l%Í*ì ª…Fà‚¦‡¶”îbæƒ×­§eˆ5ñ‰¦©B.ÞÛJMà¾ÄpZaÄmÒÓ[à$˜"d:kj‘—ro0öà$µIã¶„¨jÐD®±h‚–)[&óNPÍ1îÈ &k¨G4{®vœÆÊÚNIj‚Ä(¢<‘ëGúcÞñ–zôÝ’žå bˆOGnãÛàL€v«>]Ò£¶¼4{Ùê¤Â!šaÕÀ/ZZ/k Xæ-Š/}Þ×¥¾ýÛª­øI÷ ƼTü²óõzËëVëñ3bËkzŒs.ÍáêiÀ ‘ø|ÖS[p µ»ï‚r|'ÓÿÆ`’]Ž‘ºrŸ¦+ú?&hŽ~¨ÃNzh”A¾((uU»¼QÁÍZ}m¡ê€š·eM¥n`?ØÉ‘ªªÿ)©CÞ“/5¢§;öêP̲êEêTµ ªïiª÷ð«žH¼ˆ =LÈ¿A!kJC›§ös°¥AÀ‰Õe•ÏÃ:Ï7qg:9aA¾ û«f}µw0VÛ-²½kb%ï7wH¹¡ÙCR^SùKÝŸíÕk£$±íW»©ËC¢jpâ~qÛÈNG¡¨Ñßg¢v>ÓÔŒ˜iÆ)ktÀ]ªÅ!gÑßêá YŽ©zÌ£¿ÂNKFE!a®šÀ0¢¶é W3bGŽ‹VPm˜#sN7»ø6Ð…ˆ•ò£æ”zàX£y¥ï—¥háµUýȻխÒ\Ôä\N;¯$že“óð7Y¼L"acô€dGôgiëJù×Ô§¨Û4Ô€gÑŽ³×ãBHp Ì5¸øü«ª·(ä^ _°JêÅãLÔ} ÔS-û¿HR~ÆE¾Å÷sAÛP˜{òBg¥5¨š£š‹´érjcƒÍV#Š›óŸgFÞÞƒˆÿÕŸþó ÔðûwÖÿ. ¾öðs®¶·=ݲTŠJ‹•¢¬kÓ ‡Ï,DS ²¦oÙˆ;Ø »>Åž{}ÏYÔ0èßk¨z’GêæšÚÔ(Š C}IolCÚDÖó—0‡Ç¯!h†æ‚ÀŒ‡Á!roa–·Àœ´»ÿ ÇØ-$|¤`bKGDùC» pHYs.#.#x¥?vtIMEä ˜–à IDATxÚìw¼UÝÿßgvvïÝÛ“›Þ …B¡…Šˆ€t°Ài*‚‚úXàÑGPšŠ  (à#"Az „PÒHrIr{Ù2s~l›rÎì&¤âùð ÉÝ;;sæÌ™ïù|»9r¤ÄÀÀÀÀÀÀÀÀÀ` Á2S``````````©!¤†Bº-"‹±ûî»sá…2qâDóT ¶!ØÛêÀëëë9à€8øàƒ9ðÀ‰ÇãÌœ9×uÍS50000øÔØù”K˜Þv¿}|5Ž™ƒM ±-•}9r$Ó§OgúôéL:•U«VñòË/óôÓO3sæLÒéô¶q#ÖPöøêiìµä¹åùv#$ ¶Bœù…|í…c8ôºÈšé00Ø´Ôh«œe±ß~ûqÕUWñÔSOñä“Orùå—“L&¹ù曹öÚk™9s&UUUrÈ!ì¾ûî!¶úI—Övðµ39jB²ü±ö^œ÷ðkÌØ ØegF¶½Æ>Êsý»s½÷KMýéïHƒ†Ð”i¡ÍË ëh´·;"÷2ï.à}qFK0„Ô` `«Ž!Åb466ÿTWWsíµ×úÈhÝÝÝ\}õÕeÏ)“qÒÑûèüa~”3%ÉøïÜÉígeùíßåì³Èï?Ø…¯ßr+ïlU~ŒhdÊîà†SêxûwrÎy?ä¦WÓTÅKWšóÂlºÆLcßA’ÚÀžûM&6çy^êV †’üÖy?àæùc8ñ7·qåÞ…“æíÉðêugsúWpGÇa|ÿ·sh‹»Ãyüâª}pîÿgœt gþàVœ·N;·Ýz+×^û+CF ¶9ÈCˬo3?ÿçí‡ÏcdãQ|ýÔF^ÿõÅÜðØ›,xóqîøÉ¼ÜÿhŽžR»랺…ßÝÿ$/¾ø(¹â"~³pŽÿÊ4Å•âŒ8ã¾Ãüð‹I­o”¿ë¢õ”;ð÷ŸüŠçÆþœûú=?ûùmÜ÷ÉÿŠÇºÔÛx¢Ú¡­s¾óxeÎ,ýíù:47&ñák¼Ö5–ÃO;‚qub5 9˜Z'‘0dÔ`Ë`«.ûÔÜÜÌ%—\âûì»ßý®öø×^{ÞÞÞè“Ǹú ï½9î(!Öp$g}u8Ko9ŒËï^‰ÌzmUãÿÁ%_ߟÿùîó•ÓôNÿb#ïÝx?¸k . g÷0æøý9,­Ì¬§y1sM«åözpk¦qÐÔ,o]ÿ2mr”J:å…d>óóåEXcæò¯LãªYÏ–íO/æ†Çr®§ù?ÍŸÉÑS~Æã©fšE+ó^}…7f·Ë>‹Ûn½ d)5dÔÀÀ`k†˜õßœö?³)î }«Y”FOfR²–í¯žÍÜŸ—l4±¸Ã›ƒjÅ^â,â͹­Äö<ëß3ŸÌ¥gY<|þ½,̈¼»Z$‡Œa¤µ€?ðüÁŒW¯ä7‡ìOÿ§uÊaL¯šÉíO·á2ªü¤…dA+º‹ß½üG~üÇG™ðȽÜ{Ïü{aGÙ²&ARjȨÁVÎ¥,œ??C*B®æÉ~ëç{ ˜K_K_ä‚–Y{à1Lï7뮹œœ;ËŽ»øÞœx.»|æ†ï{õ_àÒkޤç×såý¸ÜË]]Å_KŸ:€o?•¥ª®žx¶‡®¾@1Àl;Ë_¹ƒ]·'‡ÞøÛîO,øZŸû)gt-MCš©Mµ"»—Ǿù.o¼kȨ!¤J¼õÖ[\vÙeœuÖYœtÒIL:•gŸ}VyìÎ;ïLmmmô [>bIŸÍ!“v Š·ðŠ»¦?ú¾/*Ð+8Fæ´MaE)×òÂ#¯Ðó“£8¬ß |pÄAôŸ}%ÿ^cUTá’e­H/àsååýNäËg~ÿzðμá4NüÃe/sÛ­·R]U]$¤Û$>ZÈ;©“˜<ÊeÅ?—T” /;1m¯&RoÏ ý®÷Ñ‹9öuO&kG¾üûÿaÿÇÎà‚?¿ý©†*‡LdBÝ:.j+6R _en×—Ømx°Ž }Sžþ{þø õ¡ÚƒÜÚ>î¡cä—øåׯÑþÏñX‡!¤†øUî¥x÷ÝwY´ho¼ñ't—\r ³gϦ»ÛïpÇã\~ùå¾ï*ÏÛóÿz²›ãþ&_½û›Ü¾HcQ]ôóS'±ÇÔ¡ÄÞ\•KP²Ç±ç”fœwç¯ç1'³÷´íI¼¡z’ÞgdFß5{üñ,:$Ë ?y’O*Lø ÉJ­ìbÅÌ;¹væƒ2gt¨»ìÓ®ªíØóËrÞð׹㲗"õc>òšaí&Z3Y·”Å-½Ÿn¬Ë^fæÊopúÅ3ç73˜ßÝ̤“/à‹µïð·Ùk‹eo¼ÊëåËÆÆpÈ7cÀ¢|ÔµÛÀÉçBÝ‚«y|I.Y«fØ$ÆÁØ=?Ï)_=’ñ‹¯ã›×͡˸ë !õcÙ²eœtÒIŒ;–I“&°Ã;pï½÷òË_þ’W^y)%S¦L᪫®b„ ,]º4‚½µóÒo~Æß÷ü—Ü}7ãÞn¡KôcÇ 5ÅWQt<ÊíwŸËßÎûÿÝs|`ô±sѸyÜýÓ×ã˜Üv×¹üõì?p7qÏìz“S[ãg›¢ëi˜ÑÃ}\ÆäÖ{¹èù4R+¢…d9AÛ1òsœ¶w†÷ßYEW|{«Ãjo5oƒÁ:˜û˯rvëå\xÂOøÍ·’X]³è±kxòŸÐ)añì9¬8äB®»­žD¶•ç>Æ g]ËooÞò€¢o&¿=ÿrb—Ëwoù½¬ûðeüÞÕܰ0g1½÷;§soa›µ4;”3Îø£cdÛ>bÁ³Wsîoîfa Á®ý[¦¯eÅ;¯3óƯrñ¯óqÆQƒ-¨$n­­C«ªª¸ûî»Ùe—]´Çd³Y¤”Ä㥔õ¹sçrÚi§•o#Ú¼'Ÿ§²;ã×`§;X·ò#>|éV¾þó'óZæ0ö<ç .9u?vlvi}çiîÿõÕüþeoÙ§JŽÄ®§Ÿ‹¾|»¨ÃNµÑ²ômž»ñ»üôéŽâÜqßãoÿøÍ8’#oZ’‹éŒíÄ™Þï+ûtðá;‡ìÀöƒKBò¾ë¯åŽ7ºŠ‘MÒÎ>ç\Î…'ìǤ¡%Aûíÿzšw½[¯ù*{Œj :ÛÎÇ žàžÿ¾š?Íë5o„!¤^ضÍADsssEǯY³†çŸ¾|—&CH `+/Œo`````````©!¤†Bj`````````©ÁF…íteˆÕÅ}V-™ï”ïÄžÿ·¿BTáw…Ÿ ¿UõzÖ– ã"}áƒ]â½WÑ_W(¯ê"±ÊñÎUM’8.ݤqp}c¶<ßJ'AŒn2¤ÉæçJ„Î-Š×,Œ¹ôYaÔ…ïIÏ‘¢‚Öm¢‚9UÍ¿ýì }O7Y¼—ÂX¬ü¿ÜÀ÷*«ThJ²ø| ç7tZn±Ç•ÜIô<ž‹•R¥gDÄ‹Zé…ùžyFù$dÄsV½oþç"Cãõ®=ÕóBi¼ª·ßÜ ßwJŸ4R…ºHázÖ¿Ô̧_ÞD͇¨pÝàóŠ'u}Ý=x‡½çd ¾wÅõtëÍûæ}¶Â#»P®hõ½†ßý*%ô¤gnJ׳Bw蟿àóD3žð["}ï`I6…åcáýDùäGxžžP>á[:Y+ÏÓ.¡gÝwÃß ÏŸ¬P¶ëßù\Uß©DFG~6ºÑÉ➌vNKëÞ{œ¤ò*•º¹Ž’…" GT÷"•r9¼¦t²½0ÿUĨ¥ŠÒ¤É‡ŒØ3uë6z=¨÷¡|uòO%#Õ²X–áº÷G °cV,DF½_¡A•.Zº˜ E± z…nP…Ù…§{ ú¢ Y’ÄéG’nÒô)I ‹ZtÐG6×KII¼„âÑ ¡S Ð(RZnŽu±ÐœG-BÊúe¨|Î DŸ€QyÕó‘›¹ôrµð—/Hé…  é KÔ3 ^Û« ÉÀˆDB_˜°„7ÿý–GpCÚ[?>ïOIâ=¤‹Ï\h…§ŸüV"›†Œ®âÞ\\Ï5¼²GF<åèg•{¶2òݬ„d‡• =‘ Ù µç³3Z¸_¯ìš¹V͇óðUýæ‹ ±Hby§œ—$ÊJ #û÷¹-K­’,”«ØOܼó|_¤b UJ uß¾=¡2"êýÜR*,Òw¯B)geh†ýï‘^f‹Ð<ûUb…ÄõPV©P¶¥ïþÃc”E3¼Ÿê )þ/=_+d˜$ÜRx¿Ú½k¥Éb!¨'Á:²Ú=JŒ?á=¡òË<¡ ¥Q{qPxŸoy%.(‡¤‚y,¤‰åÒ3° •ù…ÞÑ5EF,‡¢ŒõO†–¢«°,IŸOõ@£,tÞÅãàÒC'¿©ÚXÅŸbjI"K¦ø©zƒT‘<ï+`·²0!îõí8\£ÊRk’ Ó0á,7²â©®-}Iú„Iðå(­ ¢•TDô½«A*6ƒÒØeõT+#ÞÍU„,²aA¯ÚZ·>ÖÚJ¬ÒCdH˜íR᱌’G,’Äé!ƒã¡†*2*|:!ÛŠÞ’¾w7´1èÈ¥-e‰JIT=7KIcPn梬"íWƒ6G5EIâEˆ~H…åFoéJa¡´œ«Æ U—Ð&'BÏÅÒ\/¸9 bžw(÷Ÿ°¢Z@¡´˜ @JÕósó–o|ï»ô¨/2àÏòÏ´š»yWÞN®¶³åÞ9½².4†#ïÚr•D¯<F¹îKRD(Þ[¡Ya*¥J¯dÊRKl­ÌχjÕ«xŠßÖ+׊÷?Êpà*”{¯b™#¥\’$è-ÊL²:K¥Å=¬°ϯS/…v‡ò^C†öw0XJªš×àu„ÏÊ+J„T”1«¾İ‘. ¡´âxXxÁ ÍB”¡«%eБnïÕ$}y7|"oRïËÓÏ81b:ónÈà„KÅïRºZ»°´‹MwO*}¨ð ÅF "œ*k–ÔºÚ«J9Ñ ¦°HEAj,G2`¿JG‰P0 ’J÷u@©±Î­¢v ¸¡ï©ˆ‚E…-RaS³¼ã6xõ(wšÊ²›³~U'‹ësA©ž³÷~Ô$Toÿñ[®Â*Ÿê»A«¼TÊQ+RfzMvá½T'gTVY•÷а’zÏcEŒÏ;v»œQ÷õqRëáe.ÔBµÑ©ã”Â&}KAgfÖ»cdÈ í"éÍ[AkHPO5)²ÄEŒ´tŠÆué³|‰À¦¥"tRcµ jpêØ]¬œKïüYJ§M˜.ëÄ™PŒ,¬#—Ö†ŠB Ís »%eXm[kyBiÑ‘w²(KKdH» »+‚.+Ðö½£rŒ€Íï_>ë¢Ð(åÃôy©uË9´€*lª±i£7d× nZ®Fºšç Ç‘K¥ Ojå˜Øxˇ0x³Š‚«¼þkɲŠJO‹÷õ’¡UsÄÛë¼ÕuY‘¼$ (–6c•Ï lå>µ*F ‹XÞî »HMsçppó„ÏÅAæ‚£dÖGÝ")ÌQ–B‚Šê€·¤Ê)ž§ô«VÞâ_Rj,el1bX"÷™+K–¤Ü'/œ¢O­àºÊ©XÁ:•#ønIíÊXà°!IH¡qÝK垦’$®VÞGÅ2J­<ZÞò„ˆå$亾`ïã bð[ù\eØ¡0ì“¥Z’&Kš l2¸¡ü• å•€—B*C>D€Šê W(¬ç"ôüÕ† uÌ«Pì7QÞU¯Ü²À¥_E9%õ®wë^§ ê6MQæ¦t¤3<æædY\:é£ ›z‘ÄÁ%E¶ìfVÎí{¨¶² e@1ÊÀw¡Õ°Ð„Tf3‹v _,˜Úš§'£A[ ˆ ±Ñ¡R±VÊÅðEi z·ŒÐè`m”ê’:‰O(Ð*-”¹\¬´Ð¸›ÔϰE2*>ÎÆÂÆ¢¬gã"|"Â2¢XþÍXRIô”Î^.ÆV†”–¨3a‹ÑùP:Ô˭Π2­Ž|&äðÖ¿/þd—J“ß„’PÈ¢sÝÊ“L…EÌ‘'qò„­@Ô ä­ dBš¤&‘DD¤rèC`ôJR‰D•ž¹ yZ\ø®£X2oš•¾0‚ÙÎÝwœ85ù¸j‰ƒC—lþ NDÂL9eÔþ'#V¥Þ‡£þÛUz7U¦ ]tØ{S^ùÔ͇PÐ’¢ðDy©¢×¾_ÖÍ¥Ðì*…£‡4õTQK‚vú"÷Ù(~½BtÁ;¥'b)÷qQ»ŒzÏNŒ,íIv9Ë“ŒØø ¹cü‘ª*ö )tQç¨ÉÀVmbÝA [?õ..×§¹Ãr³yí<‰M/’4ŽbqIÝ!—‰j<§M©­Z:a#4óã×ð„Ç6Wr«,!R£ X-žõF*’&ü÷\ÊRv‹®1Wá<•#<•PܧTˆ)©RcQ—e ©°D„s£%®  ³áG«^[2 eD´YÔY…&ST(׫ŒKò¿gÕT!€^2ʨdW@ΣŠ +7Ñ9ËRé°$ðV¨Ç 5Â>hÉ•!¢ÊÉ«vß :g˜R„•;© „„ì—ÁO5ùÚ5®ŽÜ§Hºbka1y¥ W]2¸¤pnl+ßñÐ=½N N^!J㟊©U/¹è×MFd‘Vâ´ŒrŸ¥2#BÛgTòzãV+C2"´DjʘÃ*ÙÉ¢J]é’¨‚ó *X½:XÎz«~*º¬HØxô.@ú†**0N"¬"†MŒ>ÒÅË\/"\Kz‚ CU7Â?#}*–ÚUç§ŽVÈE•c/#]PÑÅOrê³NPË Ôº˜…F… ºÓd¨Ô\y'½Ú2S°ü•â:½?S´*:ùX8×çV—­o/©T)`QÏ%jCÌuúE'Å©I²÷Ú®"ê³SçC«hU¶$ˆÇ*Γ§ç®'rô’Z ©7/—€*µ2LW M•$T2eèí”B©‚«’PuV_©ÜëEÈ!5<Æ X{ƒäÚÒzmýRMú‚ÀÂkÖ ¢25¤qˆåIi–¾ÀúÖWˆ2tçRxž½¥H2Uh‹î —\”Ú}Ž€VÍ m ”9)•–0©tŸc¹(býfY<ÖL]lJ%®S”âJ(]ñþÌT! “ß\}ÒHå]‘\jH$A‡^OiP'¨µ;oöœÐÄçEouBS'1ø©Ê.5ä®°©[«Y¡MM¬Ç&#|¦y©qƪêšoªy –›Ñ‘ç°F/Cõ=EÙ-³¼.üŒD™g¥Z÷¢¬à‘!a¡/câ4½>„«è½k U¤É­`„ž…ëÃR¹.­-vè("´ ¸Úg¥Jˆ“ʘw)øUá ::*4±p:w¥N¾é݃êz®úÄI© !…§ë™ÀÆÎ;ß]!qdÁåîæÓ@eÈ8!4NG©ŒF¤ŒÚ­·’ù×xÎ-nyJÝÈ@°DØcçÂ! ‚`é¨rµƒ£¬š®gOÊ­a…8ºœÍ0ž+ôZ)3yW¿ã &@!ÓE€ð í˜ÔuhÃîÔpýìpÍΰÂnùÞ¡\·RKWý?YÅV_—ÂOb…²þ©ëáBi'Ug¯«“Â,mx¢.”¡I*_z2AŒ”§”÷xK¡`FÕUY|U;„>Ö3sìF“¨ÂæDÈ4båþm—³Æ¨Jè„°÷’IU\O%¿;]ŸØ£ ò–¶4]üaáûñ|Öpg~iô 2ÄqHbSO­X*_ˆ°³N•åþçGªÊr‰€ãPäI€ˆ°ú-m2bózËHzV®¨yeçšu¢Š’J§EôùÃö8Q6Ó:zÜÁÄé ¿Ð9–]ôÁàÒç!ǰNisÑU°ð-YahD%á@"ï¦My’6T[‰*¨!L)…R€«jz7“à†dA¤C]å=p–ïr ܈¸]*²‚Vò{x¯Õ.u©õV•·Fú-ä Q.ñ(!bù¥œ¹¯ šK]è Ä éh».LoíS½éªØi×ó\¥ÆØQ®!…Ê¥²:†e›ÔPiµj£òÎdp°y·m¡ä`νoçë`HA‡4YA-ç:Uͳ?|K*]΢ŒO4lQ[µËF÷ ¼¡ Ró~ªb;eHN{9K°iŽî­*·V‚þ8¡©h@+¢ƒKij©Ê»ñâT5`+7D5Ç)çiUÕ Wµ›ÇžŠ dw³ƒ– ·L'‡àFÈXŠ÷ IDATtGK_¤¢>"NçxDwAPMž1•µÂ«eú­p®²|C.=Ë‘Îk*%·”C:o¨Î“Ò>²ÅR7j #(Àd(ÎЊtD„u1YÆiµ,U.M•[Â5FEJ*ZÔ(6ºŒM±DEºtØê«³ ƒ.AÃ?öl~½[ »¥@Š&èR›¤'Áâߺª Qš©,# Ô6^µ•Î?v‹*lzòÁ+AåF*ÝFªÒK2tÇ%«…ŒWW·p¦jCpAaÑY3Ô•õT„ÂR¤`–óô„›0ˆ2®R™HåàzòÖQZ4 ÖÄódÇÊ'!9¸88tÉBÊÙ.äO($ôľ|¢a)Îeö´ZZ/‚?$J÷<„–ŒúíÜQ2£œ·(øø½V²Xk”@¼m.[ߢL>|Â*Z®½–àtžžºÅªªÑ‰7h­å(ýa"dyUW°,—¨«Ýª¶’{ã¶U 7:%@DT*¾¼ ´‰}:?UØúì*kÅ–³ê{Éš,IlâÄÈ(úº¡$u}àèŠÙªZÝRcRíxÑÆŸ¨Ép’l‰äÚQö#©Ý¸¥²=™«ມëo+\´]W¯/Ê)ÕžJÕj1hÅMät7)åF˜ûãЃÌ"ç„B*¯­ŠÍ„ÐËMšmÐy$ ê—ND†ëɪX Õ×Õ—…ÑÅ ŠëÚBêJªbÿáô犨ò`% ±TZ´uäš«š©Ð¬è …ÐRnÆ2dSSpxK5©êÍŠ!y­VÑk%üº¡ _mÓ’E«u.õ)•?*EŒX>ÙÐ&›ÏàÎí¬h›½'*òT—ôÝžÄzíAúP¢èxEÊÔ7«TÒç±”žßà>lÇ+=ùªp@·¸7”+AèwoçþŸ"K<ßõ.•·ˆKÙED¨ª–&ôM­l«üMêzÛRYô*˜¢’%~W¨ZùÈæá2±®´…¹ÊLi©(£¤ÈûŠƒ¨³!i!l«Ø9cS² ·°c‚˜åu…×v\IGƒ+U–ŽÊ²ŸÕîáõûîúÖTÜxØR×-7–r•@·Ö{JUùìÀªxçï]YÙëBW™QUÙ&\"^Qê•WªBÍÞv¹h¬ºKŸ%«?*d"¨”‹OÓå­ª,ªÀ`mÅ‚´*Ÿ”+I”%ƒ[¤ ª&¬‡ü²PÇJË2fUå•Âí…R=3=!¥Œâ¬“÷T ä«$¬`= ôasº³y³Â k$N¬Øe°wšÍ—ã’Z?œÚ,K;çB›ñM™ûxÎ(k£U›ÙDD¨˜ªÂ‹…ºæhIIÚ06©QJ |kœµÄÉàæËéI¥Qª–ãj‹à댌års¢çWhC.¢S3Õ‰ÄèÔ¤{ð®G[RÚñ„€& »±Š~õM$ëkˆ'X1k›Ø\```°)á:.™Þ4½=´u¶“iïC¶9ÅVzë! ŽW…#²ÂÁ©‚XmœXU ,á#Ïå,š›ãºQR5¦°U¸@¯Çzœ;C!}iãŒ-f–øV‰\9¡ÜŸÒ[dûž™\OÓI¥kq[€¶ ¼+ɦ²8Ýb)ALZžØð°j£¯(vÿ‡ë„Je_x'_¿·Žx±ÊºçšZy‹j®ï€¨RZª¯ ‹.&40m”·¢ŠÌ ^6?±BÀ@›úÆzLÆÉ’ÍfÉôö[¹ügC‚dÿZ6²zÙjºâÈO2 Qºh:YûíÁ˜$Iª:K²¹–~ýê©­­Å¶m,Ë2o``°þ ´ë’Íféîµ“ÞµÝTõÙ>…@5UKO]]1À ­5ƒƒKœjlzò%&Ed‡ ‹‹êâè?™T夶úDàà1AÓ‚*ÃörWuý.µS®È¦ûÅ©k¬§yÄ@zúz 500 G)‘R’N§Éˆ ÍÀ”te:‘­™@}:Ò*2ZÊúÎ4Hêš2r(étšt:M*•2²ÈÀÀàÓ)ÐÉ$ ´ÄVÑÝÖ þ^÷Áô%uv½º¤°\Aëµ÷¡–D¾ìZÖ÷ý  \£,®¯k뫵ª§Àßn¨5h)cF¼à/Ù§H@ß`†yþöâ‚X}œæaèëë3€AEä4•JÑ».iaWÇÍF°Âu]zzzÈd2›ì !ˆÇãÔÔÔ˜˜>ƒÐú‹'Èêpp “ÄdI2U.Íýê 500Øäèëë#ÙPCïÚnb}^Vä-ïäj³î…¯a@ÖPÈý±œlª±I‘-¶óQgɇ;*©šÎøì–§¯3¤J\ò~CªkÓ]:Fv¸³ŒŸëš® €˜ÀŠ[˜­`Û$íííTWWoR²Xp{´··ÓØØhH©_KŽçKÄÄf8—Õ_f[˦·mÛQƒM)%‰D‚Xè#ÐLVøø“®©ŽÞÉP[P¿õÐÉwp²óMx ÍÌÕM&JEù„§‰º³“TÖÅ q=µÁÃ÷)M¸ƒDM‡­Rc Å •6uplñSKK«l[èéé!™L’L&7)I´,‹êêj’É$===fâ  D€šãz¥ØEKÄŒ‚c``°Ù „ Ve+]Ѻj´åZmëò#ƒCŠ,5$¨¬X—º"kù,}I¤õÍtMQ¤²È“ªL•å=‘?©\jžkëÄ6‰L&C"‘Øl×K$d23ñ!«ƒ¿M¨¿,y)¶I†Ê=Y1ËȃÍ*¯¬˜å+ŸïúR¿ý=ÇD€Ê@g©›—>‰Ü˸Ò8¸¸ù®^’©¦£®†ŽF5c(õÒÓwª ÓWugM7Ô|¸ð“_ª~k/¤f¿ª°]cÝæ_¬Íh]²,C ‚D”Öì/þT^Θ5e`` Ýw ²²ô‹åˆQ›cU6¶Áa†yãm3ë%§¥Âõ꼺öÞú’ôá>ò}dI'‹K'ÀÝdÈìB¯î&C¤Yß- å}yÃHooÑp«ÙÒ'védážØ„Š!¥Ÿ5!бʼnD‚X,WÚqœb”]æM£Hýú~°_³»5ò þ31 æpB}7û&ûèg9¾ß­sb¼Ü[ÍÿuÕ±ÆÙp£Œ*u;\o’R®»½«ÈN×£Wõ½ï#C5qØùô&oW$¡í¸©gxÂ'cÃþq2¨ZëæZ9û‰jeª °Uœ×ßË]*R T500ØÖQSSCcc#¶ ضmÛÔÔÔÍfikk£··wãa±¤Q%E ‚øb]7§5t’jÑ?æpT]7‡×öpWG=wÕn  ­*õî·B ÅË Üßë¡ëI:ÂCÚ,ÏwúHS…Mœ}d|¦` y½¬ ·ò w‘*•­ç¼ßŸ¥®=*ËÎf>Ë>ÈÌE€ _á€Â—]ˆ¨ºo``°í¡±±‘†††ŠŽµm›ÐÑÑA{{û§¦¢øtü’(t}B^†„¦qÙpnS_¨«,y6!$g5v0$æð¿mõë­<û3È-¥­ÔEê7øé`E”Ô/–¼Ã!NŒ$vÞmïgpa>>B•¨$4ÕP½¥š¬üÈõð“ åÏ"˜e…Ãpýü_ø¢$dÄ ·}ˆªF†n7މ; !¹!·(4 ÅØ‰ZkȺÁ¶ƒ¦¦¦ŠÉ¨ 455}úwOagp•bM5ØÀÀ „cëº+&£^U×Í1ëù=¯•P„Š=y“2KìÊ{´7¡)HØÂ5AñPÈ/sÈeÝ[X$ý›dàXèîäÿÎM,ó¤ÎŒ/h(òltâ·%òJ³iE½Tˆ}UoSuµ­m±¡»sèÁÓ˜:q0Õ•|!>„=Ž:•¯œ|›,ƒØåƒÙ¯ÉŒ0„Ô`AMM õõõüýúúz’Éd弬UÓŸ{*•ï$&Xè?Õ£9â»×sïŒg˜ùÒó<~ÿÿpÒhSúë?b.§5vÄHĺüîôÆNšcΑR š–Ç~zéåOÁšž2`q%DDñP½ÜÑ28Ô¹ÓEˆ£QïÕ®u•” :Œ'U]O8fYz­¯¶w êt‚ÒeEd‘èÇö;îÄ„í‡Ð¯.Iœ =­´,yo-¦5³õlE¢v ÃúWCÞçÝŽÏÞ‹~ÆgTtÜwÞi¤â6!ÊÏuÄQõ»¦¦¦O•èä×¢Uî'©[¬‘œðË8o¯¡4T ¤“¦·½…% _ãÉïâ™+H}:6ÊîçÿŠ:’˜ì£c]ÔeiûÄ5s¿aÖ¬Y‘¿ß{ï½7ú5O¨ï"!ü„sÈÏÿF×s§ëñ¿øŽ­ûÜW¨;èV]y ÈÜÚ©’ê{¸µB×½ô±¢`«NuE"Uö9è‹é« ]‰nº@Š,µÔ`#ÈcP ´(õ‡bzIgdêƒü˜„æ÷AI•ФªÁj‡o:\€@DLÒFÝ“#Ùûˆßh{Ο ¶i0ÛÇÛù`î"Z·¢O¶¿Ç¬Wãl_ÝÊûKRŸÉmÒÍ-Cô7×¼'“ÉP“‚+®¸‚™3gòÌ3Ïø~7}út¦M›ÆÕW_í#Ÿ¶mªæª¸*ü“d}ã– 67ªè7t0Õ7ÕMWÚ"Ù4‚§dÒ¾±ëÏÏäòGV±ÁôÑžÈÁ Ç–íÆEдóQ³GX=›ÿ{¢“ {ìÄØx¬\Âì5Åmžíö;–1µuØÙNZÍåÕ9‹i/xD C'Oa· #h®±Hw¶°dþkÌù°•œXP;b7öÚ}C«±œÝm‹™ýÌk,ï3"ø³Nö+µJo,B6R2sæLÎ>ûllÛæ‰'ž(’ѳÏ>›?ýéOJKè§íÆå¯…§?&ì©1غàðÎm_ç¬?/ÁjÚ™Ó¯¹žs¦ôgÚÉG°ÝãO3æüó9i¯ÉŒÑL],Eë‡pÅÙ¿çMwS¿zç¿?Xt­˜ÇSwßÌ-¼G·¬zëX~͸«þÆ·N¼Ž9²ÜwGrè·7ðº±8᪠9vò(÷o¤®JÒõñžýËÜôл¹cD“Ž=›sO9”]G5buÌü¿þœïýq.©X™kcÈ´oð½óŽaÏÑý°ûÚY½ø n¼ôz^h3«= ƒcýîö‚etÀ׿dG&]$£‚êEÿ˜Ã Ûau6¶j4¡¢²È™D¤ŒS' %éSµ+*\³— ùRùޝT_ó£Š"‰åe¶PÞgø÷²ÌùCYö^3¯—ãËP5Àï4Õ£?ª!³¬|óy^[Ô¿F–®5Ëx¿x`œÁ{Áç&5bá’Í8$†1~ŸAô¯~”GßZ‡[Ñ1µŒÙÿ0öY2Kª×¢®>QZ2ÅÇËVã NrèPú‹YC’ÁC›HÖ.[A·Ô$›šú$}}D²a“àP«‡g­$Cœ!Sç°›nŠîî Õ C™¸ßáT;óüâ^HŽcŸwf„í’êl£ÃIPWcãfŒ`2ظÐuë*XFÏ>ûìÜ[˜ÍÉh Vz®ÊD»ZhÉ€|²|£ ¶vdÛðø ‹8kÊ.ÄšÒ?¾={{ÔB¦§•¶T5uNëœZö¸à&~ýå1ÄÓí¬lIÑ´Ý^œrÅõ4§NçŠ'Ö–ö™¡cõÇ´¦À]³Ž^YËß.ó]ûS\76„]Ø“‰µ’Tw+=uôµ'Ç_ö?È•_æ—³z€$“Ϲ‘ßž9‰$.©®vúªS/;ÉP~|4ïÿìL¦Õdi_¾˜e©Z†Jê62¿š,}ìg”®¹é’ ßGJ-‡ÕÄÖ[z·0}°Û»PØýeèu¤SG ½~ì,.›Ç×»ÞûשêFXQ½×·Ðuf¢XŒJjšæ~ ×èY´Ã'OÆæpÙ‹†&ê-²U«zJ·(447QmAªý:bcÙuB=,~a/.î£nât¾°×pšw܉‘ï<ÏR»‚cª·gâ°BvðÎ3xueŠøØC9eÿÅ­±oùRZÜa «Æð¦9¬íʈÂ]ÇÒH4„Tvñîãÿ`Öj¨?cöNݘñ Ÿ³’%‰Ñì<¡á~ÂÿzŒym.5ãå¸}†1jüvÔ|ô=5õÔÙé¬bÎcOñ^DXlP)CúÙF¡è}%¤ôÖ[o ¹ð+=WeÒr"_¡›²O[¤ô> š¶ß‡“>·6à´¬dµ“ß¾ä î¿ð®Ë!á4Ë¥'Œ&žžÇïÎø&w.Ê0ø¸_sïöå€ã¦3àÉûi‘…ï®æ_W~™ëßʹ̬A'rcÅßý4×ý˜/:…ë ääïã’©ƒ8àÀI\÷Êk8>Ç9_šHR¶òµçs僋èµj©‹÷ žÀ™e®±và†U dßëÜòÍïòàj˶!»mVÜz/7ö;»QtÈÊÚ¡ëzÉEÕëù¹Zi—¾@/)’$¨Â&E¶ìutнU(݈¯‚cì É8 ”¼v£[(„çQzc `ò!‡1>é²ä¹ûx>;€fK û–óÁÒn\ cÑbZöÆÈD3˪+8&ÙŸFK {W±´%ÿé:~MKö.ã£US:¼‰Ãx·{ƒbà¬YÌ’v1 WæÊâv/[Êš½‡3,Þ@S`iÝ ÄB dcOcï·jë©Ðݾ‚åí“ilÆ>ÇŸÈØ%ï³pþB–´¥7»`1DÓ`3maŠŸJ’Ç5VÑm6;~û~^þ¶çy:-xýY>˜ÛŸ]8ŠÝôcÔðÞZÛf¶ã °µd±oíH¥RJBê%£^Zpß«Hi:ýi,øÂ×`OÕ2TD¨Ì[•šÃ;¿ÿ*_ÿó’Š£Œ²‹ßcQöóìïObÕÜõÐåB¢i(™UDUvÚRßõŸç}g?ÏÎMÓøâáÃycÆ RÄ©«‹ÑSÉ5ìúÉ÷xø¦ïóðm;pÖïîäÜÉã8hßܾpQEó¸5Îräxãª?ðROG×÷ø”€ÚƒŽÅŒþ]wÐ tÎø³/Óþ¥žªõ*BI&…¦O¼®_½êŒ*ë¨ðÑZ 9dpIçë’vâ’-ZTƒ½ç+-4UJÐÒS”±ÀúÉy8‘+HÀm¿õA„L±êS›bUu³èÕ×ñ¹}U;ˆ:†ÉUÓÃHeïbæ½·#ƒ&60ú€±ƒ#ËÚ… X–™©ä˜yëƒI²C£ö=–SwOáĪÂA¼™yoQ'cvlÀŠIz¿ËÒÞrsPÇøCNd;Ô$rÂV/x›•Y2oÑD¦«cäÞÇð¥=R¤±©Š;|øÔߘ¹Bb5ïÄç˜H¼§“žtŒº&2CWWïf,Ûj é¶@87g6½½½½ÔÖú{9 !Øo¿ýB1£…O›6'Ÿ|2kµ1ûÚë´x¹ÉTbƒ- wù#ÜùèÉüò˜aôý»ù÷·Úé–IjSÌøÞQüì¥ôV÷]ÿyfp÷“§rÍçsØdÿïµÑkÕRýÆ/8æÒò×°'~ÿýÉԮ^AKw‚¡cb »Y±bí6·Ö·1~°³†ÏÕõ–ú×K—ÕWž*íT ¥]OÜçû]J ì¬Yo",”3,«¢k}F“:©ÌíÑ}ÇÁ%E–j€4QµJtWõó@YöÞ¥¯«ºr@P† m(ER7зT(±‰„RÇû<÷HãwšÌ¸‘iª©"f¹dz»éX×ÂÇí.È,+g?ÎÓ½SØmüpškbd:W³øÝ9¼¾pMÎP-Ó“bŬót÷ì2v8ýë’Øn†ÞŽVZW¶z 8;|òλ´LØ“A¢÷ß^A&‚T²¼…þƒ©«IRC–ž¶å,Yø&s?hËi¹²å¯ü›§:wg—qÃi®«¢J¦én]C·k#H#DšÎŽ4šè_ç’î^Ãâ÷ÞäµÅ›¿Ö©±$~¶çµ¯¯l6ë³’J)¹æšk”ÁýÏ<ó Ï>ûlèwÙlöSR‘i¯­Î;5ñ¤Ÿ1È6^øÅy|oÙyœyô¾LÞHƒÓEËo³*@Ö˾-õ]ßyÖñÌ5çsÙòó9ãȽØaHµ}Ÿ°¸Í¡–ò׈Å:X¶¸ƒ]¶Ãø¡:WÍçñ‡þÀõO´å«¬ubü¹½Žo4y<2®øÝíu¬s6,)S¢¯ª.’/}UCY Õ2/ܼHUJ’I§›t±l§ªÈ“ªÔ>r­OvûV•÷'{É@Á+¶¸Žj.«ÖY!Îêà‚r]_O{±]5ƒ‡ 6oÃ6ˆuëÖÑ¿ÿÏü5 Ê#™L2`À€OuŽ5kÖTLH½dVJIËÊäÒ>Ÿ¸J·‘ô÷¨¦¾©Á<@Îí×ÉQëÙ—þáΚŠ;4t¶uà.ï u£É/Y8ƒÎüÜobÚúÑUDƒXv„5Õ IDAT¿ª±IŠÝ2•Op Ä`¹z×cÉU»èý [„¬¢*;©:‹_ * eŸD¾€ Ôû³“!#Ël{èíí¥££ƒ†† #wÝ]ïæåŽå *aiÊ>p˺:Vfcü¿Æ®’û^ƒ´ü©­–uÖ°!~_oÆ||ùcàE(®DÞ¢:!EC45NK?¥p¨–’81Ò*yè«:ÎÓïŸR;ïýŽùpmÕpaü`¨ª×šûŽ A“sðö 50ø¬£½½!õõõëõ½ÎÎNÚÚÚ>õõK˜R«Y£!¥<Ô‘äÅî'6ô²_MŠæ@öýZ'ÆÌžvԲα>Õµ¢Š‰õ<:‡G}&©qÐG†1l,²ž”8Uÿy Š´UwM]wShkk#•JÑÔÔ¤-U@6›¥µµ•¾¾¾ríBœ“åkÁçòk߆–¨±Ö‰ñ¿­uÜÚZÇ`Û¡,GÊÖ9«³±$=Ô1™Öz3#©¨kªOüqóÒÒkÕôwÚ”¤É'F5qº<1*Âæ~aÙìè4Dõ©WůêfÈ.o¹ÖV²l (ƒm½½½ôõõ‘L&I&“Äãñ"9Íf³d2z{{éííÝèîr¡‘?ª*CG *QtWec¬ÊÆ6ã5eQ±ö¹/7V)ñµq×ÉÎBL¨‹$«“«¹–÷ßQ]9¥–¬ú]ò¥Ï‚cÊk†©ð0kÿ ‡™°×hë"±Ìz70øì q)ééé¡§§g³]³@<-D^xJ%ù‘úµÁæ“Y*·³u‘“AKA6½r0XrÓûouù0[Kã`£Š8}Ū¤^k§?Ê t™*…O©z1…¯ï/ʹ!4ÿg–÷P©é’â$Ál£/“¸îæëúäº.B˜5c ·*TJ7¥²\³Á–!¦–"I'Ë„R¦éã-ѯ&£¥ð'— YjIøŠê‰£Žç©H¨Jîå±€ 2VJÈuÍ”ž˜oæ“ÐjÂdÛoãˆÇ㟲³Îú!•JÇÍÄh…ºJ@ú…¸±lòJhäRíÏpkvÝù¼Eí¤2AHEFƒçð~ÇE’ÅÅ‚XÞ„(+Pôe ÷”Ú"uO•ý8ÈUŸvø&ƒéøª©ÆX(¶q$“I:::RRUU…emš ×uI¥RôõõÑØØh&Þ Œ–,}‚Ð[pN%MÙ'ƒ-!©¼ÎsI¸äQ,çÒöJº°T*J@ù(TEu‘¤¥C'ßÒÇëŠÇ{Š@,¨PÈfïè¤2.T*ÇîOÚR‘u[]õ_úhù˜¾”$0Ø‹Åhll¤§§‡öööM¶± !ˆÇã466n2ÒkðÙ²=¨^“Hi``°5ÑQ q^ î8祀aXæÉ€a0—¨ä%›^«*Šï¹ã\$½d¨¥Š N¾.©¬nàhwêµ uh KnéóeÉP‡&´`[ϲÃÖƒÏ,Ë¢®®ÎL„ÁV#䃩¬ôgòì ¶õYhɪ·v2D•MBIW£võwÂg•@‡ZÔ…òÃgôw¤—Êö2@ˆõEþ¥"AK(¼ôÒp_So–Œ¼ƒ+àýBü."CG ¶"ªò4‹ÀOÒמ3œÅô@{¿­ëq¦¯¥‘¹žóç åDZ±Èàk>ûí¬aR*4õEUòYÅ …²7•÷*þÏí eBFX"¼“#ͦ```°Q„{IÖ¨›t”zPmÎÒT*âægK%£ž²€ú]ùÁ3…»ØKŸã^e”’êE—*!¨’6E(Að΢ÚÄ‹ÐØdÈ.»÷.eˆøæ>µK§WU·òߪqÃ.Úe^œ©]V^ë÷÷±XÌLžÁfC6ë†hœªC¼PTÛ´|ôMUXIš郕‚”@m)-”€ÊJ‡jaÓ#Ó¡s¨B \Mw%º‚.ñJ(hrجà‹­æ¾þK6û 6¡ÏKкP[[k&ÑÀÀ`“£»»;Ä“‚Žo}Ö¹ˆ,–/48¥VëQR ÒdIH‹1RùXR]»Ðy–ãPåÏûí¤ªßëG,J„TXëÿ]ÐÐ*uëÖ™Uj``°~$TÊHb´í¦²Á–„ëqÉëhVÉó£ký)C yXÚ…e¢ˆè¯¯K—jâdq‹1¦:¢YŽ@Êrê¿\ÊxÕܽZÁ¤&¡8LF2ïþýû›•i``°AdTJÉ'+[ˆê¦l’˜ ¶6D•zªìû~Õ:X*JM4eà›Âw}©-üä²Ô‹j„Œ*x_"’VÞ@)Q—² vŒ &g雜¨»SÙjF_¨=*‹f[‰©h``°©„»_޲(’j``°¥åUPr å§ÁøÑ0©U¥$ í•Ðt˜Û$-Eû .®”ıpò6Ò`쨮ˆ”PŒO(ê ¨fÄ;¶p¦Ré¾,Ð÷E ºï-ã(300ØD0ÓÀÀ`[€«$§a)&"dœêßR1ªú®Ó“ˆ¨ "K’qbë!…£ë£[¥FøàxñpLß—¤rêÂÔ†šl «ƒ©l``°­Èªp”{!þ²”¬$<îýÄÑUXQýöE¡•‘Áë»! +¾Q¹yBZèo¯&–}íuéXRq½Â8¼ã’šñOm•bÙ§Rüp!S 6>üERS%Oø„¦Á– ¤ºÂõ~z¦‹)uÁcŸ> hî†Tø«Èîy u>YšûÌAâàb ‹˜!"Œ…•šZôAÙ”Ѻºª(Huá\¶÷"êo¢¼1ØÀÀÀ`ÃE¼Dݤ$uÂý›_bé¼ÇÁHx?y Çm}áXÎR &(¯ä¿V0›]Üý…¼ ´Ìqb2j¹ë…I¤TpEo!P™ê7p¢©ådþg)Šœ'Äk/5Ѥ›BÈ—£š¨!n&ËÀÀ`‹Ë++ª¾'JJæ?^–!Áþó¨È£ô•œ²Þ¦LÞ©^…yNiô2EUÝQU§=¡¬åD€Dç ©·¥•~s(u° 500ØÈ|CP#Ä„e&ÌÀÀ`‹Ê+/G²´}݃õ>¥–Ä©ù—P<ݘ¼l­PÝ3|¾,IBؾs[ž?A›mŽ€ŠÀÙÑÑrYÞ3 O¿P߉TYa²BÆo```ðiÈhPtµì66}2câH ¶¼òJ*?; ÷¨»Ó+!ºh8—PÊÆÒ¿uÖNˆ1ÍJ A<àW1AÕ§BKDÕÝš‚á –¢”½>Áç````°± "~*|’À¦JÄè–i²¾Â+ZXýÙqúÁL×ÈòGîä™åÿáó&j4v4Û«éxg¶›ÝÌ<› x­4YçÁº›BKQÐVu2PTŸzíJ½uGýÉ¡ø²þ\2Ò!Iœ.R c@¸5’÷Ü…ûsÑ·: &eY„ü-JíJY¹Šùl /q?v=úhvë¯(á]Ìs÷Í£é G³[XóúC̘ß ŒŸ~8{ ­%aÒ%Û×MûÚ•,yg> Wtå;Ýz¯SǸý§³ËÐ:’Uqb"ÿu«Xúî.ë kž†Á[¢]QÕıEŒ>™&CÖÈŸJaOæä\Ê‘5+¸÷¥»¶=BšÆÞÇÁ.µŸðÊ¿f0oݧ¿5ˆ> ñæ.yÃÒ ÝvbIšjqÚ×е±ÿ6òl¢,œÂ&P÷´"x .«$Ä”ÄOݨÈïâRõ%YjE5Ý2®Q¨ª³’„2&`ÃÐè# ÿ¶ l.QÙcRË„ ¶ü–.,ÇóT²ŽfÑÄHÖÖPeƒt2¤ »ºž#h>ŠA3gð̇]ç§¶©‰ú¤…”)-ìê o`À°íòÊ ž|¯Ã¬ ƒOiyð :‹œe4.bôÉ,é|½Í²ñÔã°¯œÎñÓ§0~X?’ô°vùûÌ}éŸüùŽÇx¿Û¬ö ›ØFv:ò‹ì5¢Žª€C¦¯‡Žµ«YöÁ|æ¾³’žü"µC50I\ gÔÀ8ó×¥ŒŒÑMk¢?cwÝÇg`c 2t·~Âòßdöœ¥tn¤‰Uøü‡0Úú„Wþú sZÿ³žH!^´PkÓ $÷è¨,¨¢ ¥E^VúÜÕÖ|Þ^“{Ëc3t÷ÃØk\uUq,·ö–%,xm¶åÕöJÆb°Uù‚F_ö¹ø¦Õ"AŸÌÁÙlŽz«ù@.½ù§7ºÚ# ë4v­_Ê¿nÿ·yh ›d]-Õy…8•…x^!0|;ŽŸÍ#¼ÎªŒD¶-àù⌫YËÂE†ŒjߟšÑxìáìØÏö¬×*ê gwsæ,݈ÄÄv6IRÄé“ÙzÝϨĴ¼ÕФ‡:ê Ð{?óó¯Ü\%Ñ“ŠÄ#?•u‘dpH`ãÁ }Gjå5¨âDUÙRui×chP}ËÆ3=V ÛÞo) –z2¢á³·o ‹–µ±ËAXÕ5T—Õ:$N_7]éü³ Ù|q†L9œÃvlB¸)º»3T7 eâ~‡Sí<Ìó‹{‘¢Šá{žCv¨GÈ]mm¸É&ú¡¶«ëí5¸•ƒ‹›¨¥ÆrèëÉ`'ki>‘ýj3´=ükÝ ÆbýVCI…'ŠÉâX$E‚ i2—•ØtÏN4qðE—sìèjdÇ<þöëøëK°6Sŀѻ²Ç€e¼Þ—?66€©_ù6ç¿?ãXt­˜ÇÓwßÌ-3Þ§[±8áÊïðÅɣܿ‘º*I×Ç xî/7qÓ?ߥhd-wk$‡~ëu{i5Ž5„öÝ ñXö!3WIê¶Û‹öžÈˆ~I¬l­ïóÒ£/ñQ_3“Ù÷£¾¶š„ÈÒÓº’ÞšÅì÷ÖF( qFì}ûOh¦>Çr{i[ù!o¾4‹w[m&~á4¦ogÓ³àaî~n9‚æ©'sÊÔfÜ¥Ïq×#oÓ³%‰H2fÚAìØÏ†ÌÞžù"s—¬¥ÇMP×<Œ¬ èý5 Ûuöš<šÁuéŽÕ,zk³Þ^MŸÄ&²“7QŸ¬&aKÒ«YüæËÌ\ø‰­AìóåóÙp>~‘{z‹.·ÏÞì4r0Í5$,‡žuóxâï³øØ-sm\Ð>ïÞ-3é¹¢öÕ"NJfq=†<+Ò`'4M];4üpw§ ¡‹y¤gøú"p ¿%4%³Ôˆiéàâ„úÓ8 —Lc]Õœ‘Pj,tBg!­„›í{ë…Å€)Çóÿ¦x,™oü“GæU"ÐbT7cÒèþ¹ÅÖÓ!`M;ʱc!^SK2CÈ«Þÿˆv ¢v4;OhD¸ŸðÆ¿c^›KÍøC9nŸaŒ¿5½COíXv[‡E<û/-KÑo×c8z·~¥«Tp ¸´¼úwî{5F"YE¼z;ö=rO†Õfp`,?ãuÝzȉWʼnQ%l²¸ùø¦Í¸¿÷?˜ãì‡%{™ý‡+¹áß«ó¸o?ϊ⑵ìþ­ÿÏÞyÇÇQÞùÿýÌÌ®´Z57Ù–»16¶1;„bz9 ´”Ë%ËïrÉ%ù].—Ü¥!íRÈ¥€ Íü T ÆÆ¹[²­ÞµÚÝ™y~l›¶²l\„ý|xI3³3Ï”ýÎçù–Ï÷§ü𣵄ìkŠS>þ4þîë?fxâV¾ñ\+R¯æäsOezTïm§+V̰šS¹ò+?ÀÞ÷Q~ðfßàöcLàœ+>̼($ûÚéˆRlöÐf%­¦2££±—Ȉ*&y_¯Š±ãÖ_±éƒr÷“Ýì}÷^,«âŠÙ¥”OžJÕÊFöú<ÓYtá<Ƈlâ]m´›aJ£fB‚VLõÄ1T„ÀŠÇˆ‰0Ñ‘8åã¨,|”¿®kÏãa·°ÃÅëIb= BEÅ ¯9™óŠ“´ýùMöìjĪKÑè1Œõ4eô˜a mwï!vŒmˆˆLä¤ÚBZì}c/¿—‰’ʼn×o¦5»aˆÑg]Áe³‡¡c“L˜”夅UŒ,|Œ¥«[°Ò×°2f"FÜ, R6–™‹.Fv=ÈËõIÇM³ˆuw·ÁîI{¯µaŒ?i£C`™1b‰V‚>{Çôüp¿¡­ê•IŠD˜!úH ²à[z|ÒACsEH¹hµði›J‘Ô\Ë%AUþ? A2v"IÕ•¾6ó"ÓÀß„ ×ynª)^øê´$JþiH¾Ð]9¤ÒO:( Rö²ë½ä ŒH)e‘Ìzؾü9^ßž2‚ÚðJFê!*˜wÅÍÌsŽ0ZBT@lØp†iÙ¿—{ûS³Å ¶AD3ï,NŸ>Ѝá˜ÕÙ! DÉÇ¢éÐñfÌ—† P„±°ˆÉ$A]=ŽämÓk&R`ífõÛÍ9#Š;£–!IçÎ ì.XÌ'¯žH(±ž_~âsÜ·=IÕwñÀWÏäÜ+Ï£âoД}àöòè?ÝÀOÞ«àÚ?À—TpüèÍ·°*f? <ònà'ïZ„B:IÛ¤þG×rÑÃ+#:ü<¾vÏX0îæThljý ={Iž\J8RJYX°×û†Œ–RH³•?ņ‰Ð4O/Æ.Ö?ý +CTŸ~—Ï­dÔÜS·áEvåICÚ·üüv¹N¸¨pdç]ucËF1* ïîÚIÓÙc]6ŽqåoÒÜ7š±ØMìÜÝsÌÝ4¢|8ešÙÆžúîü¢êÑiœ:«êž”¿ÕõS:k WŸ[C圹Lx÷9¶Y¹k¸á©YÑeÖe7pîØ(j«X^_Ÿû>ÈVÖ=•/‡´‹õO<ÈŠý]ØÑ™,ô±y¿é“j—I !L¿ƒÊy‹˜r•èÂnÏù4ÝDM ÈØ”>R*³ÞJ§wTä™ì“g•ÛëéöGî¶¥ÁíDó¹(D4¨Ï½ÿ¹K¼Œ Df<Ð@Ž=!Í—C: ‰•Û2I¦«ìwn|ºÆ¾î¯MËêÇY¶kç]r:cÂQ*«JÐvt¥Šô—Îjg×{{R9¥™£Å›è‘î‚¢ ¼M¨fçœ4šp¢™Í«7ÑdgÚi3©Ò=ó·Ç¢0TžßL}!I¿4}òNÞ–xGŽ<|ÆIÜr׸bx’¿±˜oöÏbz@ˆYÜþÀËÜîü–T¡J#G$³o²&ֿ׈½`¥#ÊÑ1õöƒ$™4A«äÌÛþ/w^;ŸêˆCOÐ,¢(òœ˜ˆ\ ¸Å²};;Ná”aãXxã-LÛ¶‘uo¯c{[€]Æi\¿™¦9•Œ.¬ ¢T°«3è ÅÔœñaÎCqÈ9±  lßÎÖýg2jôÆO(åŽqŒ2²y;;‡D%øàÆ ¬¢BÈØn6mïÁ:¶Ô±ï¬qL,¨¤j˜`[‹w×=46öÀØa‚#JbYýPŽ}°÷û(O¡“X€ D`K™ÎÀ t¯/É÷uG…‹ÐB1ÈgC iBGG¤ÏÂ툔'Iå9·5ñ'zмÖ[²wfry®äQòP(E»æq?‰ìî-¼ùÎD.=µŠèäÌÞÖÈ[M&²³v{"•ZzïNÖoIi…ÅZ½ôIítÚ’‘…c˜<¾„æ]&EEnI\yÀm‘âBdëVÞÙ¼˜ÖOõ¼!ÌX†4RòN!¡Ó+ãÙ{ÿ<úÈÂÞ»‡SRªaþ¼ îÛÑìÑ´”‡"^ÇßZSQÉîXG“3®æä¤É¤ëóö!î zîçø×›PÒ³ž¿üòÞéÊÕ_ü(§ð c…]S!@ö¶Ó‘”þs0÷²réRÚO™Ëœé㩚¶€‹jÇóÖÒÇXÕt3­T(X@¾:œÐÄ38ÿ”±ÄÙ°ò]ö%GrÒ9s17²—u œ9zµ“™Ö:–,š·í`(ðQÙÕE·-)ц1ft1kÚºëwDÚɃ4¹rÅ;‡ Ýïfë˜O,lbÒ$"B % ¬¼RúÈä"Îîv›Þ²%‘ö’:×K‚»Å‹07‹°Ð)}$ik…ƒfú“;…c¤^ϰ–Çokdú‡J&àÄI»¥ã塤©Ol2Ûµù-6M¹˜Yå¥L›7•-ϾGW÷6ÞÝ>ó&3îô˸a^œ!‹mû3+$²{;ïí™Á¹ã£Ôž{5µçìý€ÛHzÛÚè—DFÊG>2®d˜ò°s‹ÂñŒ¥»0…EˆO{Fƒ½äö®¿OBÚü2ËÞú sÏ*fþg¾ÍçÚÿ‹û—o¥ázs˜Û·°Ý\ÌÉ¡a„ŸçþǶÒkC¸|eÉý4Ò@ú~4†CTsóÓüþáe´íÌÿÜ )B -,[eTU!ÞŠÒl#2‚q³Îdáô(BZ´lÞœ"âº÷” ‰Ø-lz}›ÞÉ©—_ËiU#™XSÆ[m¹ë¢i©7[ñ„‰Th “´w§ÔSž×BŠ‹Ãâ•–`·lâ­õ[èÓbŒ9ÃAH‘ômÛÈÎ3Æ1¹rg 3ÀÚGݶÎ!q-eï.¶7žÉ¨ÑcN_̹æ Öín£ÏÒ)*Iuq/[w´b·4ÒlOel¤†éµÅ4ÔÅ(:…Ѻ@&šiêÜÙH+A–`”0|xÑMÛÎ{=wlï½éGt¿›ÛŽùõ—HúIfÓŒ&¦C‘;ŸÐ½ðLÄÝ<+W ï'¢2›ýž!¨zÀdÞ+õi¦+õÃB÷9jÒ%uŽ%¨£§íó‘æÏ#ä)jÊW9åÜÖVY¤ V+ï­ÛÍ”…(¬œÁI£¶ðúÞ~êW.ãoÝs™=y #Š ( zÛ[èµ  ¤ìcÇògI¶Ääê2 dŒx¸ŠšŠB¤ÒÄ6Öþ5¼ø¦Î‚ã>¢š(6ÉþnZÛ÷Ó‘ä Æ¢îâ@!†Ðé“qL——ÁY=ä8Œ´‰§ïþ gOþ +gsÓÞÏÒFJÐrǵžæ¾e×òÝKG³ðÎûxæ¶Nze„Òhœgï¼6¸°â¡ïǦiËfZíYTÎÿ¿ù݇ÙÝ]Bm‰ãÚX{رÛDÎ*á¼ý_¾Ú=ß]>Tªï§_Ï?,躖ò`J‹ž]¯ñš–@‡‡V9—«®˜E¨§‹ž„Nép2AWWPšÞm1³.û8µ1Ia´›ÎÍëÙD'mí6²º€Úó¯c‘ù ¯¶´Ð'«‰Ž9“k®™DG¢€áâßɆ-ÝLž]J8,IîÚÂÖî!b=d^}ƒ WœÅ˜ÂJf}ø*f9W÷m¢¿þvõnaٌ͆:¹œ)ÜÌø…&z8ŒŽIó;kÙ˜Úý&ê÷šLaò73úl #¹…§zýyIó Žpo^éè~ë§7g…ÒÄE OJ@[«Ôɳ4÷Ð/Ó4ªiþöœV:¿T¸÷Òs áóö†1ÐÑ\rVbÀß¼­IüQäýwªÚ;—^Fíeñ¶ç2ªpýPsT¶³î‰ûYw0ëô™AGÒ¿óeÚù²ç=ÙËÞw—³÷Ýü_åp¡Mó{¯Sÿ. •2ãüK©zº{Ò_åAl#´lZÁ3•p, CB'!­t^–×ÖÈÀÕ‘„Yÿ_ûÄ®¼åF.9g6ªJ‰h z[›Ø³e+w™ ;xõû·ñ¥=Ÿáã—œÉô1e”Z=4o{ý‰ðà'<ïc?ñ·~Å×~\È?^w.ӦΧŠ}õlÚú6;º{}ÿ‡Ô|éΛ–¤£51î¶I_w7±²R Cº°IÆ:èhÙËÎÍëY¿µ%¯ AGGœê²áT”Ú$º›¨Ûø¯míGf\MÒ¤·«½¤âÔo^Å+o4¤‘²“ /¿Jù¹s©­°‰õ™XÍoðìrƒ³fO¤¢b ÅX$c]4·î¥=žeaì[¿¦“ΠJëgÇÆmC*åÇj]ÇÓ´3kÞl¦Ž«¢<F“ ú:[Ù¿{/1‘"rõ+þÊÓ±3Y0³†Š¨Ùµ—ºwßàõw›t\ö²ù•ç(_˜zV‹"ÐÓc¢ ôµ̱îиßÇ”ŒºË–l$q™Da 1èwX21Ç‚àt¤üФ‹Ì9 ƒúSy/9Îl™”a! ¡c‘ôøb½žaÿØE/i¾õDuˆq²¸Í$¢n„t¸“SÊ¢rt•z›*ä·9´ ¯åôÊ$±XÂ"aÌ&Ö<¹ŒõrpÛ(|0çPŽj)%-ûš‰î¶Iu¨÷ÊDK‡Î_¦›HêóæØFAˆh4ª.ªè8ÿ§ZO>ö +öPø IDATž¤2!RV‘ çqíÅÓ(î\Ë£½N£¥.ù‰†ÞÞ^Ìx­>™íØ” ¯ 4JE!I,úeŠ”êÚåîýîùäëgoCžîôù‰ž—àÊ<ÞMgÊ@!a ¡Ñ-û]>^áøgô»'Ïy¸Éµ$(›5ÐCÔKÚ{’¶gp ‡†fo3ýeD‹¢èV?]ûزn ³Ds0Û(/È‘Ñ [ä˨'@áè Lí7rî(0¢ÂÄØ¶jMŠŒžØ>‚=6½2N‰Vˆ-ÉÊAEŸ3?5œ­:sý뽟>Bëß_fIN¾ÉßìÈ ÍC&“X LÓtrÀ ÂKBìÌ|Íà|EªysHôp¨çQáý@v±mÅ2¶½ßmŽÇÁÑR.ŸA 6¼ Gšy Ù‘"´XÛÖ¾ÄK[ûÔ3¨šÊMbÓk')¶´I`¹l˜W0ÃöíÓ-Øäœ ‹t…º ˜ï'Áù¥ùx\º¬,-¿¤#I+T8d ¼ME3Õò"·V @bÛgÏqˆq²¤Íð] á“®ÄX0UÈ^AAá` ¹'dß¼¯ }w2 #JxìNÎ K…ìŽ"z{{±!{‘ýÏ/sT$„„AŸÇt”'ykåªnù¥5%ZžóÞð·gâîÍ•…K‚¨(@"铉´&©“ zE«æpžqÙx¥¨D^’¬ev¬!²_šc™››çgÛ Ç³— Êøù‹8cJ©ºï GÌÛÔ˜CsØ#÷ @%)(( [åÌ ¶B6V*÷X„³=܃ÂüÂgã2ä0GTµ›'pçf ósxö­9\Œ^›šÙwBZhhY*š9¾æKÜpõ½ŸczÏÙ9VÓõž lW…+N42bl UÅ¡÷¿+­‚y—ßÀ• ª¥0¢pÍ{ŒëÀý¡•%RPP8v¶j õ)Ñüat 1Ð<œAÞQß+Ó±ÞK ý½åýDÔk1\.Åç¤kyè¥_Õí] ª?Ê#Ÿµ¶H«áO.®†÷Alw07DáƒòÍ*`Â9—söÄHŠ J“þžNZ÷ncû›Ù¸µMd’¾®Nºz“êÚ+øŒšÓ˜çµùRŽ­òò!çz ›§H`IIÓEþ4‚CëÁ-Ûƒ­`~º,Ž#}6ÀÏ„ÖmÀ MŠl‰•3Üž"Ò¹â)ÛCž½a{™Þ^æ¹N™ŸŽ*{éШ’i0öìkøÐðM<ùä:RÝèås.å²)M<÷ت>r µ ËXº¦)¢Œ_p³Ç £8ZHˆ$½mõl\õ›Ó‚à"BÕŒùÌ›>–Ñ2£»£™ºU¯°±U=‘LJ‘w¦ ‚z”x ¡‚‚‚±²WùrG5WÝyŠKÅd’¨ÑÂôÚýi?©W£Tô£'ë…ô·Íu±ó èçDðÝš„‹ãeè©— &±0Ð1ÐHºúNy»=áú;S¬*\£º¦FfGz Ãõ_§[Wáø…m&±¤†®kÁüµi «—o /E£Nâ´SÎåÔ¶GyuW’¦½Ø«©.z‡Ž^ ¢ŠŠ2̦u´ØàÖeS^UMaÇÛ,_Ù‚e”2fÖ!-Š„A!!úIú8TP@Ð$]LÓõìVÂUïo¾ð¿8+-Ii (¾ùªúsc¬½6ppVá9ÀÜ$„ªðAý– t]=LQi“æM£ÜjbSS2p.“ho`O{ê¯Ö¶8Ã&\Ƥ‘eˆ]Í$÷Õ³ß>±£ Ù\Cê#©)i^ÝŒEaàþÌÎýÔïkA²Ÿ}}ÅŒ¹¸–±#4ÚÆrÒŒrº6<ÅòwÛ±ÑY jÕ=;î ½péðw\†£ÓiêoËÓå)¨JßÊC¦M$iBê`ÉÀ«ã^T/óeO/{á¼ <¨"¥'"âô'$Ñê Œ)íbog]rSf×Ò»£ƒ¸ŒPö0Ms?›ëº¹dæY‡;ÙôZ×!=7²7›wÎá¼Ù Y\Ç®N(;áT öø€DÒg'²O¦­”ÎnÉ2+"m;|¥¥¥îÿšý¥ê"¿”ªË§ @WWê=#R€Y²§!2å;R ©yô6sÙ¢èÄ¥L“UM¤HmÊòihZÚ!(¥§S“D‘kÜá¼é¿M,l)Óâó9J+|!t7tz\mlLia„Ìy½ù¨Â7²|ôÔé„póÎÌ' |¬Õí` Ê'U”ôDc ½lç=Æ1•ùSw³÷­õ,³ˆÓf-àüa„$ë µ;áx2lÚ·¼Ç¾™gQݼž‡øÌÈ8õoü×ÍS™yÊ"¦…MºZ{S3Eõ'„ÒtÙ;k€­¬ásÞnÛ#¥   p´”v® ÇÎ…±5GÈ™ÊÍPA¯’ˆ%%E"eÅb2-˜Ÿã—)ª'Ý1jÍq²í<ºHMä…–²‰ºÔ@€%m„ØÒvQ[ºý¬d½¯Ò•B`#Ñ„†.E:hï&š§%ª“ˆJ‡ô®soŸ¦¦GŒ“em!°jæ:Û;Éì‹ S€¢rt•z:‚¡W²àòóˆ®û+/o¶iŒ(?™K.̾ggu³ ûÀPÇLBJIó¾&B»“žü¤ÜŒÝIH3FRæØzq@©‚‚‚Âá@WWVÜ$TŸtõrÏ‘ÄsÒ³K…'ŸÔÝ4ŒAT+¤ÛîKÅi’2@“Ô­ÙœO›9KTÑÐ4 M¦R t‘ÒMeÈlª€%-,l,™J‡Ê(è”h…ôÉ8q™Ì+÷éwZ樫í#©ÎŸÎXOÈ>Ÿu>u}? Ї—Æ`ÄôÓ˜”ÜÄs»Þ”ŸA…褫/¡2ÆÍšIy÷VÞhSdôøC°jž­l‚‚ÂPšX§ýž2O7¢\×Kw`ÚIP“˜$HR$ÂôÉ„£†CÊK d5ÝÇOWÊc¦«CÓ1}GÑhfk®£IA:ZÚ˪¥‹Ft¡Q`‡‘R¤R²¾Ô|¤4WOt¯ÕðŸ˜;@æ¹ ÁߌRjϼˆ9ÃmzëXñÒ;´¾½&¦xd 3'–S\B˜}´7ÖñêëëhV:PÇ uw7ñ*ß©M CƒŒæ²ÛE~$9›A‚všÚo')Ö IÓçuG­ý¢›AB~‰¨k³³ãJå‰ÚØX–åÈM‘WM¤¼¬†Ð cP¨‡°ì\o)™úlÚ£*É^á ýKϵ g–Šƒ¸ A¿+(äŒ6ÞyêÞ9lû‹S¿zõ«Õ¥=þ)iþ©®PvGAAaÈÐQïd:XZ)ØÊ¹É«¤ßNR BXÒ&å¿y*ê½üÍÛI䱫Þä;-ø”R:I î¥Û~Ò&¤$lKZéœU=­ª¥ÄóÓû´H‡û¥tdÿÛ®Pîz ×È\!{ïŲqëM9=)eHõ\*((V#/4îNµ<…c3y®Ÿn{¥y$ó…£,)iÍ ækavÌG8…ÇÏ)8BòXOÿx½ÙK\“ÙìVI3õ9™ÌîUG` £ Ð)$DFéÊ’Iicb¥óRÝvÞKT=9¤9w°0pçàU}«‚‚‘‚àpÚå)UPP:ÕŸÇéåOÂÕÌÓÀOH‹ˆÐ)"L?æ€VN rY(ø  pç  ¿íê÷¶ü´‘$0}í© t ¡Öt 1RÇéŽUvSZXi¯ª+dŸï9½¹Ki4ˆRPPPx=ce„«'´7Ô­¥§   p,mVÖR È–­{›îáLÒÓÀÂ")M 5ƒ¸mf ¥“\÷§“ø«2ÓK3Ý-àågäd‚ ib¤”Q³Ñqé ª"àüL¬´‡4¼B IR•þB'¬H)‘Rb ‰)m ¯gTæQ#Í1}ÍÁgÞ?d¶Sœȳ šÕ+((( Rš¶PRxRŽ‚žÓéï*ŸÄJaÐOÒU€„¢Š¼Ö0GDƒ¶d oǧ,iÒtô¬²jj·Ü•tYóÜØSuùB 4IÛNÛy!4ttBšŽaK éaì¼܃“yx´‚‚‚Âá"¨ùL§jÌ¡ ¦i’L&±mÛ¥÷{ÂE!Ð4 Ã0…BGÜNA®î&Õ¾SX,¿»Ï [Jb$‰h!LÛÆÆG»ÐŒgÓ_ø?£sêÏÁw†ØýÙ¥~ €Œë1çÍd‘èŽ&&Âá{¾kâttf=²ié)3Mk2½­)ÒRø¼ú£ƒi ¥ p$`Û6}}}$“Écfh…„B!ŠŠŠ²Zm G´»©ÈbW”TAÁx<ŽmÛæz¨Ÿ¨Ö$ýŽˆÅbضMAAÁ‘?>·_R È›26-¥õizZ°žtþ¦æ)0w;‘Í 1´=ôÁ9¤Î±H—÷3³ÌQý v¨KóyÇ$=MHM윇4w¹„Ký?K|'¬^ GŒvvv¢i¡Pè˜Z)ev,eeeŠ”Vcî6¬NcçÍsR¶GAÁd2‰eYÐDÔé<ˆD"YGÆáö”JÑô , ý’ÅENB–Ùƒ%%1™$"B„…A¿£­²—Þ O‚€³ˆÉÝE*ˆÄ–T‹ì@u¡ùR¤'–îÏXNÒ«ùY² ÌLÊX G}}}躎aÇÔØ !Ðu]×éëëS7æzòyx”ÕQPðO”“É$‘HD‘Ñ›‰DŽHd-Cú4Tõîñ¦?˜ºW"Û‘Þå„Iëyˆ'ü/²Õî9,Ü4X€ë1!rGrŠV ßR²ŽI/,llIº´)Xq5w6šÇ®Uû ñÕüÚTn·¯æpܪG]áXÍþ‡’7RÓ4’ɤº1‡Õ¸‹l¢{¦SHÆÖ…†Ô„XAÁA`l[El°×¶}xuÓóyÿ„>Bç³UiÛ–+n’®Vœ™»)-!ÈÃÀrÚ§:(Sÿ|—l.*¾")éð¸JGy{æŸÄJ—'éh¾œSáò»ÏEóQÞ̘½ÉXÒM˽§àÕTP8V€¡4óBœÐGäÜ‘Û(k¤ à¶‘ G÷ú 6…Qr_^›fbÓO’°¡Ä~œÒ$=AûÁl/²­Bõ-R÷õ®ö©~ÊœZÐ ærIø»ÈKÖÞ)µóÔÕ«I±‚‚ÂбUNbçhÞ!(»&ñ7á¢X™.I&aaøš‰€P¾s¹3÷37g |€Ûî:sûeºg}¡J§)dºvJG‡Oéº.ùÇ*‹¿<¤AòªBê÷ù*(((¼_wƒÇHº»-gº|¨p½‚‚³]Âï§s¾¥£h)GÜü47ˆ¬Å¥EX„\Uö2Ý >׉^{F…—D§òK…^ÞRé"¬–´Ñ…;kV ²¥s&A^?O&mVNÀ‘p›ú]¢‰Ô¦Šƒ*|qõÕW3vìXu!>vÝ•ëäêOâ0@š«6SAáÄ… ×ýëh}Ò¼ÉYÎã ëK¸ÜOKsDOJÒºŸ’a8Ž›&v"À#Š?ª-²ÂöÙ÷Rd%ždº=§g éñ  ßìOøt/’î1ÛHôtW{K:Ò²© bó>p{#ÿ¸„ëe/½à€l÷ýàcûØ ¶»÷Þ{øCW\\LOOOàºÒÒRº»»Ã­;,ïm!()) $*­;Ð9X¸p!Û¶mã™gž¡¿¿)e–xÖ××óꫯÒÞÞÀ/~ñ „2uêT-ZÄ_þò—#ö\'“IY½z5Ùû5eʦL™Byy¹‹D3†Ë.»ŒeË–ÑÚÚzÔ®aii)^x¡¯»‰‚%K–ðöÛoÓÐÐp¸-|.UÈ‘Œ/$„„A¡(d„QÎpQFX`b¬q×~åK,)ÚËŸVþoŠÍå3?ù׌ÜÄÿ|é+<¼ýý«VPΨš‘˜{¶ÒØïXq˜£ p"@:zîý.œ&ÌA·„G¶^ŠTÕ“ðçQze—œáõL_ø¤´)©NI¬4ù”îz%œ¤©cze˜œãÎÉ4É@­#Þ2ÕÚÔÆ&$tÒ"×0ÕM mìÔþ„_XÔ{ÎN’®ùcþ8ÒpƒêÅNœ°ý5×\HèÎ<óL®¼òJߺ’’’¼ë7®¼òJ-Z‚h]Ð9O¨¨¨`Ó¦MÄb±ìoïÞ½,]º”¿þõ¯Y2꜑Æb16mÚĈ#ŽØ¸î½÷^~øavíÚÅE]DYYgŸ}6µµµ¬ZµŠ‡zˆ{ï½7;î}ûöñÔSOU2F¹ð ‰D">2*¥dåÊ•œsÎ9G¥ŸŽ €0•bSÃã£WÑ ‘BÒiw¥túŽÔKgø¾»ìUV¬x•ÇþùtŠŽâ3¬UÎâ´©åD+ærÚ´¢÷eiEÉþóé'ùóo¾Ê…UÚ;Ž‚Â‰]è”kÅÉz‰tS#I  fg÷#¤g}î?SÚ$¤Eé\ÒLn¦—Ü:ŠÎ¥KU4@jI’Õ-õj‚f=®žý%Ód3«/ê+’¾‚T.©{;g]@f\yô¡„‡‚ÚìÄz|#‰0þ|jkk}š˜‘H„¹sç2yò䣦—)„`„ Œ?žºº:Ö®]K,ó­Û¾};«W¯Î®;î…®Ç]Ë–.]zÀÏÅãq_ˆÿp#™L²uëVÂá0555tvv2bÄžxâ —psSSk×®eÿþýGåšMŸ>ÓO?}ÀkzñÅóÒK/ÑÒÒÂÎ;™9s&o¿ýöSƒáZ)cjŠ´(01é¶{Øk5ÑfwÁ¢:“¯¼³ŠS®â¸è«XÚttÂéö®¿ò£óááu<ùR×ûSN9Å× WÓ4¦M›Æœ9s‡ÃÇd|B¦NJmm-6l`ýúõ®u“&MbüøñlذwÞyGY“!€Í›7 …¸øâ‹Y¹r¥¯‹È²eËŽêx¦L™2àz˲سg ,àÅ_dëÖ­œ{ !tJD”*}e¢„­! Ûî¥Éj¦Åê"F?&p„ŠšŠNãš+&¡'·ðÆ»,˜?Ÿk®˜Ì“ÿ³%—( OæÊ¯ÝÁe3'0¦¢œˆ§uç^|èwüîÙ:zå · dã'sÕm7±¤h/rÕKüì]D 3.ûŸú»ó˜=® ­w/þ._þÃVNþôøÂÅ“¨V„‘è`Ï»/òÀÏïaÙvÇ$Ô˜Ám¼Êm@rí¹þóÓhGÁÜë>˧®:‡•!úö­ç¥‡Í=Kߣ;}NW}íó\6³†Êá¥Hzö½Ç+þ7?bKþsR8¾`ÌâÓ¿ý>‹ÞúŸüÙÛÄE£N:…1½ëX½£÷¸žÜH$!¢RAY¨˜«‹f»8Ilé÷ZŠ\LÚGöüi’ÎÉàö&6…„è#á¦{"ñÓ|ôÒqxÜ‘3»½tcá!¬ÎȼÈ(xZ¢Hy€"*á"áÎq~FìÙ·ƒÜäcÌÇ3®¸â JKK×]yå• û`Ì™3‡I“&å]7qâDeH‡,Ëbøðá@*,¬‘ïùvbãÆ\}õÕ!hkk£¸¸ø°v¬Ò…F„BÊõªµ‹„ôË~º­¬FºeŒÉ#lƒÃ]Áy#±7å®ÿ­å's¯cÂG®dþÿþ€72O¯æäsNezTïi§#¥bÊBnøú<¦•}–ÿóÐÌÁl3¨1E˜ù©óÓM'‚M¼§“xA5%²‡$ ¬h•¡~:šz‰ ¯¢öŒkù—Ê>v~â×lξuâtìk¤Ë³±›Àdeî?áîë&Ƥ¯7AIÍ®úÒIL+½ÏÝ[GB¯fÖ9ó™•$z;èŠE6n>W|ù{Øûnæ®U}ê >Ä!ŠÆ³ðú[¹öüS™6º-ÖÄöµ¯ðøý÷ó̦.°{i©ßÍîÆîÔöÆ nüÎw˜ò§[XsœRKÚtYÝkE”hÅ‹(Q»ˆf«nzHbú<ŒAòI"€x _E‘›YJ .M""DHj$ǵҜ'*!zŸ½ˆj&¬ pȨ­­eÛ¶m˜ñz{eÎÔ”hQ&c˜¬O L”!„ [ö°Ó¬gsr'ív7É4};¢I1Ú_q:Qzxã™—Ù÷Î2žÛe¡UœÏ•ç–û-÷ñØ—¯æŠ\ÉíÜLB3ç–XPxÛ 4¤‘ò©ë¦‘í¬¸ûV.¹øR.^| w<´“u?¹ž‹¯ø(ûçø‡Ûþ›·b`Œ;…ÙŽœQk;ÞyýèG¹åß—ÑÀ:´ŠÅ|üÊ „eÏë:.^| ·üh%ݲˆé½™³ŠÝçôèWqù¥7ñãÕ1¤VÁ9 gR_í¡MFKæñÙŸýš¿®––Ï÷¿ùùÎ/Ÿbûˆ%|ù÷ð•…#ó@z ÂýúmüËŸ·’<Á®¡Ísv I™D:Õz%SB5ŒÓFQH(+£™ÊÁƒãTÀùñ„4‘Ht¡û:?!ÝN ЄH‰Ù{ò<3Ë\{)„ÙœP)Óœg AFØßr Ù{hµ—„»$¡„@ËÃC\5ÿ‰çm|ʼnÈCüqV­ZE2éÿÚ-]º”U«V‘H$Žù8MÓdݺu<þøãµNáèC×u&L˜ÀöíÛå<ÒŒ¬TMM ­­­H))))¡¯¯ïðyGÑ™aL¦B†F?qö˜ lLl£ÉnÇVÞ®"‡ýÞLZÂGf†‘¯ñìë]H«ŽçŸßŽ)¢œv釩Ì÷¶–ÝløËÓl0A+™Æ´ÑÚ¡mcÚlfFvçr}r1 X}ôö³VE§ IDATKÐ*9ã3wóçgŸãéÇå‘?ü# "DE‘ƒ;wcêL¦‡vçJžz©“»Ÿ}޵¦D‹N㤚€ «‰ ›°Ñ(QŽR†ʈpÊ'¿Â cë¸çóŸå[¿]Êß^]ÎKOÞÇ÷>?z«”%w~ŽsK„æó¥¥/ð«ÆdI‚6ê:~ñÂc|q^´I|â/ðÀg§;î¹Á¬;â•åËY¾üþãüÂãöJögÙÈ{Ém´X­X$‰ˆ"Æ…F3'rv)¯<HO[üßy¦ IOcü8¾˜“ÙÇs/v£õJJµJ´&è´;Ùk5Óa÷äùr‰Á}…ÇîI™Ò%"DŸL ©ïì^ï…[#Õ½,Ÿ]ÈrŸtŽ©&SÕÿ©ÍDÚ뙓°Bâîår‰æ$®2òS†Sr “õ*ó ÞÅÀO°*§X,ÆŠ+X·n'Ÿ|²«$‹ñÚk¯ñÎ;ïøÖI"ºk×.Ö­[—Õ´ô®[»v-'Ô}²,‹‚‚_¥ýPPP€eYGtl¡Pˆ &0kÖ,–-[Fww7mmm,^¼˜5kÖÐÚÚŠišTWW³dÉ’£&Œ¿iÓ&6mÚ”"8Ñ(K–,qåF[–Å“O>I?Ñh”™3gòÌ3ÏÆzì>ZìvZ¬6zéWûУAF¹,ùp5š4énÚK{öÒ(®ÃðÈ_4…~½Ù‘ƒ©£`P}î¹Ì0@öíaÇ>ç³4À6Ò²%PJUeA·ïüÌuì0/bVÙ™\vÁhÖ>³—!Š‹C ;†¨æ¦eÜûȳ´íÌ»ýº,!•ñz“"UÔNˆ"vv# LÓ—+hÖm`Sb §–Á%ªbÍsíŒ^|!§Ù[ÇÆÝ \h㩉Zl{o3A1=sÛ¶$®gÊ„Q‡<±èoÝÃöí ÿíR,ɦ~LÛ$.ŒÒG2\F‘ˆP …)Qê­ý´ÛÝ©¢§t¨Û9]¨“Q¦Bß»Îli©žIvqö¦}™Öi‰'§CË™>ê-R²Ò¼ÑYi/œ™ÒS¤åì#åÖ>M­7ê¿$Øt.›àø ßïééáõ×_gÆ µîpcéÒ¥y‰Ê@ëŽFCc‰––¦OŸÎ–-[²ÂøR!‡0~ssó×Ç>ö1‰MMM<ûì³Ùû³|ùr&OžÌ©§ž:$„ñ{{{yî¹çX²d‰K‹´¿¿!‹-býúõ‡•$Kl¶š;é–½|‘ 6Ät(ˆ.XÌÂá2¶‚Ÿ|ò_x¦#s$ƒ·ßÏ=7Õ0þü‹˜ùûͼ›}ËWqíÝOp^‡EÉÈR „ÉžgeeÄ6Z=;÷˜È“JøÐ7îãŸûoäû«üõ‡8ë ÄD”Âu?à†‡·ÐfŸDÅüÛ¹çÎcOO ‹–ÙÜÄêwú¸àÌr.ø·G˜Û– °o_üøÏñZ)»ù9îÿëu̹¶† ¾ùgÎúR‚Pq!úÙòȬpž“‚zºgÒ U³°A]Igîg‹V»“nÙKµÖE>–b­˜rQN©VÂ>k?õæ~zˆ»{BŠ‹„g•“Ä%¤$Lˆþ0åˆoÎ_:PçM·¾ªpPfBì:qa¡á.ˆwÃ;„÷û "Ú†Ó¥¤_•ÏÝ{4IàPÄ@/ä#íÑ’Ræ=Æ@ëN¼ôÒK,Z´ˆÓN;Í'Ó•Éd’––^~ùå£þ\gR.êêê²Ën½õV„ìÛ·7ß|Óç?ÏösÏ=ÇâÅ‹}c}íµ×ûxLiÑ%{](8Js-(áÌÅgQ¦Ibo½ÈkÎc™lùÛKì¾áV&Ž>‹fÿšw3ŒTöÓ´·‚êjèÞêgËÝ¿\ƒ«Ö| mì=,ý¯»©ù§[øÐÔm¾+ÙÎËß»ƒ¯7|†›ŸÊäª2¢ý-ìì°0ÖÜÃ×ZÀç¯=‡©SçQE’¾Îz6m}›Ý€ÝÌ3w}‡qw~šÌǰaÐÔ” ¬tÙÃ[?ûÿÜv;ŸºüL¦T„ˆ5¬åÿýå~ùÈFâ(|a·ÔÓÓ9yÚdBO¿íó’ê§Q[`ÒP¿[ÖbÛœ6ó V`âãK,,öÙmtؽÔ£¨Ö*(ÒŠK¹VÆns/v+fÚǘwy¦œ¾ŸÂ&‰E‘“&Öéó@"ýd}˜î¼ÐÜñ¼2UX¥9Âò"à˜2ÝËIøødORøL ðédùe¡†Z[[yôÑG?Ðçp´…ñƒÐÞÞÎóÏûÉŽ9–ÈtãœÉûã ó™·K7Ïc ùRç¬Í÷póÂ{r 2žBÙÆ‹ß»9¥á™wßoÓW÷ß»ý ¾—]²œo/>‡o;7J4ðêoÿ•Wëÿüþ‡ÿƒÛ€ðï…_Üù ¿ð­ 8ŽÕÄ›÷ýoÞ—gg ÿg¶þîã,ü²=C±·XöB ¾è.ûËzþ²ÛQ¨«UqÁ­—RÓ·’û_ëBÚ´vªÆ¥€]°AƉÇ!Z=a¸3Dí´S661g—¹N­‡±F5ôrJ´b&‡Æ3Â.g§Ù@ŸìÇ´{N«çv W7' ‰]êØ©¿ât"ùŽÝ De±|éD"½é&±Â3f1ÈùK`/{é9°[°U°_AAáÐq´…ñó¡¥¥å(ö N' ‡}¼ù›»yrî·ùüÿ”Ú?=ÆŠÍ-Øå“8í²¸rNœ—¾ûß<ß.ݼþêN>~Óg¹³ÎàÙ­Ý0jeùئµ›ÍÛ\{Á-\óÞcl§š²ŽWya}÷qû=–ÄRRI6’>ÙOÒN3û§b„6Œ"QÈh=B( !¹Ÿº‰g`©<iò›&aR„Ô Ðü8G•,ÍÑRgØ_’ÊaÍä¸:kîÝAú k/ó¾ 7‹öfˆ´çB¼2„ò’*((ÃÔ®8·îÀ&TAAaßµöüðödÛ'?Á•×ßÉ%Ã"èfÛÞzÿþÂXúN{š\XÔýñ›ügéùûý+”hÄ»[ÙÿÞêÚ‚ò‹—~u7§|ý3üý·bôÕóÆo6òâqLHs”Ï|wU ›N»—^{£´ &„ÆR¢•P©¤DDÙg5±Ã¬'‰<Øø³L…Ã$)at©ay…ò]ãôÊ.IŸK 1MµÇ÷w&4§†"\G’®º#é’›òÒd ˆÚ‘5rx[‹};_¶§¾ÞùWo bt•ú–+Q´µµQPP0¤ÆdzݖÒ„;Š*¤”4ïk"²ÇFsØopÈvØžÌòþ1:z1$´\ŽlÛ¦··÷¾:“?ñ[~}M ÷|é[<¼¹û¸­Žïêê"ºš{ê~¬¸Iaƒ•åKšCø=#u”¡xšÈ¹ùtt"RkŒ¥ZIH„±0é´»Ù–ÜM‡ìÆ› šë^äg欅 QQˆ%múÓíDóOýý šÞ‚uÿñ3ROîíBèDD˜¸L·ð÷pÂ[ï>¬¨ìߊb!O͘¿TŒ@²L-)VAáˆ"‹\rýQ€eY®*t…÷q{z uI!ÍÍÊîøMj ³T Ú›¬((í ^2™<„ï¤mËzºj/ãÓŸÿWœ3‡é%-¬Y¿Äqvâñ8ápø}ë„Çãq¤ectgô3µ¬eÊþ-r݈D–Î¥¬–‰E·ÝKŸì£Z!QH™V‚ .˜ŽŽt™½º ž‡§¥»†1°ÓùøN«éer¯Ãµ_‰[?@xÛš‚«ï’!RtÜòä® ¼-GóWö;·0‚*žl‚õÊ€*p¦p´é™~´› ô*c9nî±g†îÖ½óZ$¡ì‚Âá@ßVûæ-<7n6§Î›ÉèŽfzÕ—ë ì–—¶¹ëÈýB™6}ô‘°ãÄe‚ñŒ¡L+¡D/&,ÂDôB’MôÈ^’.¯£w8„BSÕýƒ°ÔIfýÜÒ%tÔ7)ȇé\¯9¬¯à{µ¤L‘R™/á@xä ¤‡´ºÇ”u;ÙyD‚ªµ¤z(E„B!lÛFׇFcBÛ¶-)¥p°>› L§`{¤  pX,={ÖòÒžµêRÄ:kO…@þîGOi³ì /c¬QÅ81†­€±Œ¦ŒRêÌ´Ù$ÓeJÞŒO¯ü’MªÀ)$t„Lº¨±³‚ìäs"`²ïLáÐÂö™u¶'o–*ìm{*ËæSK4ȧ?*‰§ øMAáH"‰`š&–e¶þé‡D–¤Ä²,LÓ¤¨¨Hݘ#èsð7é‘QPPPKæåIÁÛgþň³ÕÜÃúÄf:¬eZ)'‡§2ÁEQZ_NLÙ…çøIi#hyŸ#£¸HkþÚ÷ ¨•Û_«Úc/åt/ø("å!ÕÒC Êð6ð;Zù èºNyy9}}}$“ÉcFJ…„B!ÊËËßwr¼ÂÀ†JøŒ Hkü)((( 2êÅÔ\ä9ë–óNæ,Z®ö\Òjw’Lî`´]É(½’°VÀx£†QÂN«N»ÛWA/=vÒÂ&)M 00Ix¤šrã>zêwx+âƒè·L7BÕ„R(¥ˆ<îá¹¹cA'æ\ïBù(Ž4MsõWW8þ {~¢êí¢&Å ÇvÚC8°ß/_æfŽ"š˜tÈ.bÉ~ⲟQz5EZ„J½C3Ø•ÜK§ÝEo8>—>`II“¨V€&½¡Á“}‘‡âŠ@(ïÿ½ú¨ÁöÜ:\ IÍÒ¼ÖÒÿp -H¡TAAAáðR‘ǶˆAX…£a¯œY’Z€ Š&瞦¶¶‘ÄDœ­Ö¶˜Û鴻Є J«`fhcô Òe?AöR"³ Iuô*Å‹,,ŠDµÆD¦(¢=/¹$°¡gõQ °¡‚üQpá97‘çó¸kê?'ñØ)¥D ò²ù|2,Ê;¡  päh¨põ÷pÊÑåz,+Jª àƸqãÔEȃ 6ÖýI) îŸSìt<À²¼œÊƦGö±=±‡^½—ñF a¢Z¯¢@DØiî¦KöÄÌR@!D:Ô¢P„0¥†LKG¹Sžü­D…JNªè&ÝÂGD3º§ÎžöΜP‘‡SzÛ™º©ô°á B~¿´ª‚‚‚Âá†ô¨ŽâP"=èæz Ghêì'¨"À?”óDB½û3±è#Ænk?&’±F5Å¢˜J}aa°Ël Õn'NÒUvnb£ ŽFRZŽ}û{$ÕGú¤«2õþ66zV³TúÎWº”PƒŽ“…ÄRƒ’^uÈÊ´ª¢…ÃíqÈŸa„OýXÙ…¡BN½í޽ÊÉÞ³ _H;§`*i­O‹]Ö>údŒZ£†az9Ãôa„Dˆ"³Vf¶?SjBoJ;›Ej»Ž-<âôN­ÔiôÛfh&‡ÔÂÆH‹?I… ß5ñ^³Ü¾ ‰;8/<nïé`ÐÑÑ¡žT…,ÊËË4êAÞT1¥‚‚ÂB† &jœÚP ÃE9!´t;QCèy¨sБ‚e©Ü™„/ýÀNË>iéƒìÌ4UR/èâ¹|æ'ß禱¼ú×û¸ëÝýô gâIó)+4ñ=3(¯ª¦xó½|á—«H†‹>v6ßtß¹{_ºõ.Võ©{˜º‘#YpÃÌÞ÷0¿iŸJ‘Ôt™,gr†êƒó0e Ö… ë›HzdŒ-ÉŒ‘ýTë•DD!µÆxŠDÍV3 ‘@HÓ1BÿQl×1¼þ\éú™û”ûŒ|1H‹ÔÆñ ªæ7 •½Ÿ ×ΗÇ!Ÿ6Ÿí3€*p?´PÈì¿ÿ 7Ölç·w|‘?lα»WŸ{<õ‹^åÿ˜6’7žÏ\y&“†IÚ¶½Îc¿ü¬nMÝsQÄ”Ëþ‘/Þò!¦WbvìaÅ/ÿ™&M”ŒjÎúØ|ü¢yÔVh´o~…~òS–nê |>dÇn6lØ@àí7x³gý·YÌ¥±j÷\î|ä.¦—-Mûó?âO›ëj,`æ?ÜÅ÷¯…çîù6¿Ø.˜tñßóéÜEáŸá7hµ×óõ:‹æß}›;V6£OqSšÌaögïâÛçïç?ý*?l*cÁ­_ä ß½ƒý7×uÐÂWÍà#Œ±ÿeÖí¬{ŤîÁ¯ñ§±‘ô4ÆX~àóƒ&ž2‡a;~Í¿ÿ×f‘±œuÓmÜñ;Ø}Ó÷Xy„¼¶¢p .½Š%gÎbBe1F¢“ý;6ðúbézõ8v!›©®îÞnG¹Zz‘ÇÖ¹Cå"WU¿Ë<£é£ŸÝæ~Ò¤64BQÀ(£šaÆ0º­=v_ºúÞÝs ‘~§©SÅ;~‰ŽÀ@s(¢IÉ€3ÉìÏÀaô&œ¹Pþ¡ßÅÎþì·¸~†áû¢o|ð›üj%œõÙoqMòþå7oQæÞúUnž[Š!@Z zÛ÷³kã*^xn9[;=†ä`·?¨)Q?íMM4uö©W¡‚Â2òAùU¹ß•otHN&*ÆSµØ¾q ñA¿ ÎáÆ«Ç³íÞóýGvakÖí!Rû;nüèÙüé__¤¿le¢‹Uk×°¡.u[Ÿ?—^^Áª~Žß¿Ø‰ê~XÍ}‚ófßÍë¯'|Ç }è[<ÿê·@4!±íüåÿÞËÚܸã-»Ù¾½!! /Ìùõ¤·íÛµ–•omÄb kšF±àžÅœ1Í`åÛæáÿŽÍàú¯ßÉeÕ­¬~a)¿ÛÒB¨Œ±SN¢¸ÀTóAÛ,¥½—@Ù¸B×9ª+|„ÍKTÝ}$ ’Ô[Äe’ÚÐ8†éå gs#'³¡ÍV[^²›û 6·t ØÕ@ûÈ,£cøWˆ<;pö‘~/sÛ3üú‰Í.u¬¾æ^$Qß\¢¨(ŠØù,¿|ü=F„’ŠIœ~þÜ>£šÿ¹ëa6ÆäûØþ `ïgùï~ÌrõTP8"dT’¿ œRøÊ7/íA‘ƒ¿;zÍt&4±üm¹³öðöº>yætÆé/²qýã<ðÖ">÷£û˜þüã<ú—'y¥® Ðk¦R)bÔ¿,å…¯æì¿²‰,Bð=+ÉU?ç³?{“¤Ð),ËìÅãÿñ øÊ?ð㵇÷’ êü.—½¯žý²”ò’#!ŽUÀ´k?Í¥£êyäÛßå±¹b®·^û[zà#fÃ8ùò[¸þü9Ô”Açîµ<÷àý<¹¡#Zaüy7ó‰+N§vDf÷>Ö<ø_üü•æÔ=ÐG2÷ª›¹ú왌®ÑµcOÞw?Ïoÿà:wr^MáòúižN‘Çÿé-f ûÿìy|åýÇßÏ3³› B¸!È âˆZ‹w‹xUëU/Jm½µÚzö²õ¨µV­µ?«UëõªŠ"*—Š¢à­ B# ! ¹wwfžß{ÍÌÎ&•ú||!agvžgžÝ|çó|Ï7ÝK{fã“6;õ81‡áÊ¡Ø@o³ˆÝÃèqê3¼¤™›¼iTŽG% µ³Ržžz鴄ލkZÖ*,|„4˜%‹®ÿÒ4o¤¬´4s7B7ÕPVV?e K×ÜtÍaLó %‹¢[y¾E¿CÂE'ìAﻩš•Lçù™%lqÙ=;‹ãöÎÀÞ˜VK^¼—'?-à˜ë¯ã€e÷ñ§8"‡AŸÆ™GgH¯0vÓ&–ÌxˆT«ók54¶‘”¶' ¢4)ýFÂÙ¸–ª6ƒq£Gâs¢[å[jÑÕ¼pý9|ðã8ýGgóëÇÎ㬇Á5O-ÇœÍ̹ã—<õµíù–´nnþŽ4×Pžzî¬dÙ¢õíýON;~ø¢ÛÃ4Ûg!~]lSZ›r,,$Æá£{3ñ°¾4|ðo”µuòMaFy#× Ÿg+aðágpÖõ7’sëͼPC>žË¦ìOíKÿàÖŵÈÅä×Ö'–%—1?º‘«ÙÄŒ©÷ðØæîìsê&_{¯„E»p1™Ÿ¼‰Àï@Gyš¢C¤?Õÿo^jüçuÎ옅‚ÁyƒèiôdtX ¢k¨uê‰aŽœ§/ȤË$BöíQ¹L’š&óaL$2IH3#û•ëoï“GJ陜ãtžÆ9Ñ–2›Æ6œ£±l!¯N}›-­‚»Éé'\À©U·1uQ+Šî ÝgozoœÉS/”Ò"óP58>ï­8‘óÏKýÌsÉD·~äÖ7h2ª¡±ÆÝß5$È3ª‹-¾ahûœ7ç×qä1ðƒ¿âÅŠì”TÈø§gW,gUätöݯ¹´"n7ÁŒׇȪå¬MrLÕBå'ÓøÛ'³xûªGøû'±ÏsËùlí*Ê¢g0j°Áº9¥Û–Ó*  W`G£(g µõ‚Ãv#‡5®&ŒÉyDˆD  Ð§7šåõNÝßNî {Sœï°vuy'7 à¤cŠ©xå&™½Xº|¹ƒïओöçõû?"Ò½ÝEKJ–±ª¼ (÷¼ÿÄ#{±äñ[™öQ# (¼ãÿúC&Œy‚E_ìšÕùèDÞÁlí:ƒÈ¨ÊB “ònb,‰‚ư©S|ýš‚P>ÝDÝD7ö âëØ*6ÚuXØ’žþyú‰d6Réé×äUj’dF¿’™¶"D‹²0ÝMBÛcËÉIw15÷™ÌŸï›œ&ŒÕs¹÷O3¨Èvqi2MœPEýF1áäÃ[Å{¥‘m8_Ѻ®„%És+›8þF& ë\Tž*žh]ÿ5KWT¤?(ß7PtëN7ZY¾jå•Q`­~0ihlÿ^5Ðëêúoòn¢‰…ßÇ›ãÏÏÿñŒ~áU>XQC«QHñ¨}(^÷<¾Ù@]£`ÀþG2ap%¬}Ÿg¦­áï“oå†ÖG™]*uüϘ<| ÿ¹g!M€t(§î/)]µVsû-„†-lq@m™Ï^»€{ν•ßÛOòú—ÕÄòú1¼°Š7f}EsÀE cܸqÄBytï7’ 'Íɽ“6©â IDAT*™6ûKbN„…óË™rþ¥\¿"ĬU P<’"Å0Y¾:ÊGOæôeÓ((ª{·–fyý«Žïoçÿr%cw›È#Þ̧˪]©ëY¶¼ŽÓÇ`€ñ¥+ÞbÆWrÞ¯þÌð…o1wî<>-oÄäÀa ÎÍ£ï%ðäÅ釩a:D‹r }Ì]q-Û±a¢‹ì^{^I?gËÜÈÇÃè­N‹[–2*g=E/òÉcÏðhBÑrªì기}×ëÐéçRpû?Ýd4ó^’n"LŒ¸W×ôW©XpqÝZX+_ãéËÓ»ÚèÖ;Ù/jŽ=;î=/1¸Eóú¥Ì|ì>¨u¶á|“>ûËÉGgD¿BBV V®Ä)mÕ=Ø¥ ˜»r§^ñ+ºïÈâÊFí!ÕÐØ"êW³s·äÓZ¤ß\8çqûÅ›Xòãó9ùä+9ªo>2ÚȦ5KYøbÒ)gÎãÏsØu§pÅÞãã¾bé#×scÛU\rþÍ[u«?`ê÷ótIÜÑ`öÉ÷Î=›+¶šX÷õûüýŽgYe4óù×ð«-—sá‰×ðÇ ó¡¹šÕo?È»³ñR‹úê 4MšÌ½ÿ¸ì(-õÕ”/›ÇÿýòßLû"7^ùôÍÜÑã—\8åfŽê.‰4nfòÏXQgƒjàïaüo.åg·ÙRÉG”ðöWeY_ïèþvúgT·šˆd÷¡C0YÚuJ1± fÝõK=‚cN<žKÿx2kþó'î˜^O­Pu,xèN¦—yS+ÚêwÝF" 7j˜úÃûíU¸;tœƒ):ôĦµå[œ*¢U´È6É9  !$BTXUXØ:¡™¤33j•úY”áû@e;$["c … ÉiKt•ꀙû].k­¥²¢¢Ó™Öª7xhz mV- uÔ6FÛ%~í/NdÊ”#p>˜ÆS/UÐH?=ïÇŒÛjV½ŽùÝNɘ9|âDνîH¾÷úƒ<0·BKBihl§÷!s“¬Ëš¾ñ¤´n /ßw#/ß|¼î£¹êÌÝ,–Ož¼™Ož >?ºd*WŸ3µ\ÇOüŸèè ÕÈ»wžÃ»wvp^´‚¹÷]ÃÜ,ó­Ã_.Ã_ü÷åõŽîg5O™ÈãîךfsÓÄÙ;æŠ,eá' L8ô9{³×eR‰„7ÕY_JEôXöØ«?rÕºDêÁ@öÜ£'ÑŠR6¤R+ÚØ°dS—ÌçÃóoçwÇNd÷×KYº¡‚ÊØq (©Y°ëÐN‰<–î#íõ·w{ÛË+uhÏcª\2Mé1bÊ¢Õn¥Ò©ÆŠ~È|›ƒPÊa½‘(QO´<ˆDg'ΉB&!HÖ6úçè_IŽѦb :LfQ“[¼Ÿž”»óéNõ¶lbÍš5&°ío¥X”òü³¢Elcã¶&V«—¿Ï´åŸ°è”ë¸òˆƒ>¯‚Z=CCc«=íɨíŽËhh|Ûwz-|þŸ'ù`Ï+¹à÷73læ[|±¦–ˆ, ïÐ1ô¯~ƒg6ÑÐ,è³÷AŒPÍ¢ Ÿ3cÎ:~wÊÏùYÛ ¼_)røœ2¨Š™}A ûïÏQ{IÖ®ÙD›Ù‡½uƒ¦Fš¨ÆO™5ïn<éç\i¿Â»+6ËéÍnݪ™ÿÞJZ¶r©”B)ŸqãX¼x1BˆÔŸÿ&Í&Ÿ¶aÙɨŸ{µ—š$È®UJåM³QX8ce´ ;e°¹Ýe£sF""ëì D‰á¸Ø]fç¥Ì|W‡xe¿P ”h—¤'a È!ZUzcd’õæTBðÔ_¢¿k‡Ë¬êuÔp‡{ [>_O£êA¯¼­¿Žì3–CFÃúªzbf£Šó ¥™³×ÐØzïZ2šía ¡¡± Dªö#¼¹Ž§þ€‰GžÏÁ½r‘±êÖ­ä‹Ya¤ªâýio°ÿ…“8oÒ§|ùôJV½pwGÎçG?¸œÃ aKÅb¦ß=•«ã¥QF!xÒ œÛ¿!»™š²Ïxê¡×Xc´°ôé;¸§ñ\Îøþ¹æô\hÝLŇÏòñ{+Ù_RŠßýîwÜzë­|ùå—i2&vþ†µ3úš™ )²lµƒ$—üãH²‡ûƒf¨<1eQ­$æØ  !_02<i ÖÆÖ%ê)tr“ï Ç;µ@Štؾ½î{ù"Œ´¨¨g “€…R®ep/ó?ð(p*ßâ© 9ã¨rñ÷rv„–†jÖlÚº|Ù} ãü>§õ-À°[Ù¼ö+^~úmª4!ÕÐØaFtq“†Æv=·¬`î“w37K*AÃâg¹ýêg]o¨eÉ+÷³ä•àóc+^á×¾’}@{3‹_þ;‹_îÛ ¶m‡¹öÚk¹ë®»øê«¯RîTRÚQþf¶cÞîL*k¡’ôY9°ïŒ”®YØ8„00İYoÕ`)›¡¡t“ 5abRn­%ª¢uRòâä1Ç3o÷{„§uªBÂ$$LZœHF*¨ÕgˆêW›çyÑqñc®P½÷ï-C oqÿÀ…¨¯¯§  @ÿökhhÐÜÜLQQQê¡â~ÀlZ_C÷µÊ%c¢"é¤!'±ÓW.ùüÆA#Ǥ°°P/°Æ·—`:ÍÍÍì½÷Þߊ{F£Ìœ9“O<‘©S§r÷Ýw§Hi¶ðýÒ¥K)((H×mECCvÄ¢{•C2/©Þô2UYž–Íw÷Ž÷Vçw¬U›Mßïa "ÁÂw<ŒA¾Ì¡Å‰Mdó† Qlôcxx¹"¨ŠQaUQ[GOõ¼ð‘N岯P sQJѬ"©ñÝiž0t—¹XÊ¢EY‰ ÿôõdzQ3ɯmån’%u>—††F{¼Þî c/õ¢ih| ‘ÜÐ~öÙgL™2…ë®»Ž}öÙÇqR9¦;|í¼.]DU´Ó…)óXf¦i6I§´ö§HýIÞ`…¤øFßV¦‰wXØTØë)UТZ Ë£ÂÃØ-4\®NJîÙ¹=›ÞdNpNrŽaab"iUé\Uåã™®_‘屡i¨††ÆŽ ¤áëõ,|ÇÉ0ˆßN†ÁçŸÎ…^ȵ×^ËØ±cw")í|ËöXS¶Kî¨ôÙ@™…»ù7øN¢°)‡Ë~Æ)l•]ÚX%-v &#ÃCD3á|L“h7ñT΂`û #Lr1iTmY¼º 7ƒò1÷ìˆô2:ú ¡¡± ½HíöUÀn_i­_ LÓÄ4M-ZÄE]Äõ×_¿S=¥2-ö2¥læ`išg¹ƒÛéT/ÿRžãÙtNÝ×óŽh÷&#ÜI[j«¬VG×Ðh7a “Áf1#ÃÃJyX“²Riò+<ÿBúæÿÏD&QlbÊNE¿ü¤U¦Ež„‡Í«À^Ò*ð&5444¶ÊgsT'< ß^†‘ú³hÑ".¾øâ¾÷¤5 ‘ü!ÀNyE4U@ENògw¾)[Á»¼þKòeæ–>c«RÛ~ B”geV-N+¹2‡æ†„ŠÉ!„LPï´Ö©ðØk!~ï˜BrD¼ñP›Š¥òRUÀjIï mê¥ë²ÊÅË¥H$ëß BNU€WAd=GCCãÛ‡¤‡4ùçË/¿äòË/ßi¤4U^©JeÚ$¿lS& ˬÜ‚ši…‡æú¯éïx¯®›(Â#ÈŸD”k­u”EËiršÉ!F…‡Sl œeò;-Ý#;)¹Ð441È5BDx?¨TÞk”þÉ3o÷QJÓÐÐÐè ¿C¦YÇGPI%Ûkhh|;áö&ÿ,^¼˜+¯¼2ƒ”îŒí³H…ÁU†×R"0vK$HfZBw ¹7zí·…"‹ýô{0“¯[Žƒ™J4âx°ÖÚ@E¬’6§“‘áaì*&„˜Ä™œ§á[ è.´ÙbXž’yEf”L¦¢W´zóÚk#¥¡¡¡±}T4mo2­ðdRé-±†Fâ7D|;ŸÆ~iòÏ’%K¸ú꫹îºë;vlÊKÚUëä­®Y:ËŤå’œÞ+øµ‹ŒOï–ÝŸ`©‚,¨ˆ‡Û-lB"„‘Q;/\¹¥ÕöFV[´ªVLa04TÌÐðn¤3sÖéÿr0QJQ–'- By9¦DbâJ.“RàuýKiaj ®¥£i#.²ÎøE)•UÞDCãÛLJ+**þçÉ©RŠX,ÞjÒ0Œ¬ç-Y²„_üâÜ{ï½üú׿fùòå]JJqõŒo¿)¨ðyü‰®FI¢&%D‚c ÷MUÞZy÷n1úÔ±DZ-rŒR âç3J ¡Tüý­*Bµ‰bˆ9ˆ\™Ë`s QÕÆ†Ø&,lDòˆç:®u ƒ°Ñ¢¢Ø‰Bøt†©ëž”HÝ¿‰¯ºË½¦B%ø•‹°Š€r' í1ïA2ÏÁÞ‰x*¾ƒŽÓhhı½‚ï»"LÓôÜ¿RŠ_|‘¡C‡zλÿþû¹ä’KX¼xq—Ù*ñ‘?Ó=Ý—^!„‹€©¤|úgýT™Ü*1J¼,ÿ´]yši,PÂKZãÅFéKX8HG¤´H½„?=@QQÊ£k10ÙÍHžÈeLhQ'Æ&»;µñaUªÆ(G„°•C4á•A•ÿ‰…”òõ² tDSQ ®'¥Ê—äàoÒ—Ü KCª¡á!g­­­äççkî9é!=ñÄ …B¼òÊ+L›6#FðË_þ2uŽã8äää““Óå›èDw¡Œpz²Ûœ×¦¥É«Ê(OoµÝ«='³Võ{G4|£;žJÿ Ïjº±i\<_Q[‹ÁàÐ rE.c£0¢eT;›\÷*‘ÝD|­›‰zl»è@¿Uzo%³ÌÃû…·é·s+Úƒ}O½˜É‡нb44ºÌ´§-NªŸ¿}ž¦£^B ÐÔÔ„ã8ßš{Þo¿ý¸öÚk9óÌ31M“Y³f1qâD ”"çáp˜P(ÔeãwWJ¶,U™…H*îyTÊMd]VO@*¡’ö8VüÊéª~?õYÎÀsŽrRÕî+¬ü×H{^c*FE¬’ŠØZll d>Ãr‡ÐÇìåY AIDÅ<Ä;ûJ&VC¸<¤Âcü(‘J@p?:ÃÜÿ·QÀÀ1{0xÙ,m54ºÒÈ»’ÓSú~ŠÔn¥ *542~wy‘999Äb1ZZZvZ ;uU Û¶(..æöÛoç’K.á–[naß}÷嫯¾bþüùüà?à‘GI‘Ñl=î·Ç^e´ R^"*D’z«n”+ÏR¥X£ˆ›;Wª¨PÊkSùó"c“žÐWB–?ùS œÄµdÂcêù.ù¾7ñ~ó’fÕÊšX!b€ÑB eŲ£lQÍqN-y"LDY$G…Ë‘‰òWõ 7“‹§’‚©*(K¬“«ªHü[¹¤^·æSìΡ—ÞÂÙ{š¾%Ïþž‡>„C.½…ÓcÏð«G?%" ØoòMœ¿_!¦eGi®ÛÀš’Ox{îû¬ÚâÛ fœ¡¹neK?â͹PÞ¨{¼hh|Ã9àÊ3òVf¦7ϺÊ^C#“”†B¡”·ô•Z–ÀwÜÁƒ>ÈG}ijÏ>Ëi§ÆòåË™1c7ß|3O>ù$†at9M[¡4©tksJ!]¤Ò•Wš˜Cªl\¥CäÒï$ÉćT: ÑÀm¹òÔ(%rW…¯=>?KÙñR!2"½BÈtH_)O„¼•6VFËÈËÉ¡—ìE¡,d÷ðH¾Š~ *îu5” ‚•^åº/§)¹ñ3Lí—»‡€»BËqe'·í<¬Õ3yxÆ×‰ì‚øx-›QøÎ”äç ÊgóàôeDÍ<º÷É„I§pùžxäî(iUíŸßo‡}:WîÅÿÝ;ƒ²˜6^ß°Ç©Ëë M¿Ò¥”"¥~òõ¿DL•R©®E‹ñôÓO“››ËŒ3¸úê«éÝ»7¥¥¥tëÖ^½zÑÐаÕ¤'Á(S재v —Ç33Iú:Öoϧ˜ftñëÛ8˜HbØg»m®¿ÙiDE)‰¬fϰ¤ŸÙ‡žF/ÆäŒf]¬Š˜j£™h`ŽªûZ"KÅ’©Ÿ ÞÔÓøE%('í™Û!þÔ¼‘²ÒR"ÁÏ¥L4ÕPVV?e K×ÜtÍaLó %‹¢ž_R›Ïï.™ÀwG̤ìk dÆL:“Û‹AÝ UËX0ãeÞZÑî“-»³û‘?ä¤Ãöb·Bƒ¶ú æ?õf­ñM9o']q_áþ- ÆÒ†QCcë¦d(!+o|'áqФTC£#RÚÑk»2!M¶ ½ãŽ;ÈÍÍEAkk+Ï=÷·ß~; |ùå—Ô××·+ µý[h<^?«No0:­"’"š"3ô¿G·Â“ò_7™SYÆ :Gxݦñªw!\߆_ÅÃô2™빉ø™-ª²Ø†I‘,¢¯ìEÈ„u‘õÔ9­¾Q]^_¥RžâÌ{i)/N¶.NÛõXq™·«xk±hK„ÍÎ}Ù¬æÚ„zâe\|„â³S™±A0àÀ8ù’Kýí¯¼Qa!†w)O4X<ëY^_Ó‚YX@ë&ï…ÙŸƒ/¸ÃÔ;üsêBMF54ºâ¡“ü¿J¤_yŒ¬Ökh|› ·a\ýõq’–ð–†Áý÷ßÏ'Ÿ|‚a,X°)å'ãn‚©bïÊÅs„ðDl¦ò· ©É´VgšÌ&…íU@Ç{…+DOº[€™2û6ˆx稨£<Ž?áö)›-N#+#¥ì™»;=ŒÅØBQÛB”X‚Ü*Ÿ¤Ÿ[Ó6yŽé»¥x2t²RL¸ûÙ;]VM`î3™?ß79M0«çrïŸfP‘íI# B¦‰Ê£¨ß(&œ|8b«x¯4Ò.ë•f.…ýGrðIÑ·i¯•Ûˆüý˜tX?ªfÿ™çæWã+VÕ3ðF&9–yO,¢5o,“ŽȺÙæé·ªñÐÐ$‹6z1þG'sjß%YÅï£ëòäOSÒýH.œ|} m 44¶Ç´ _8ÊñY­Aª¡¡ñͳ\xÂõ…JnÖ’PµOª(—ÍS>2*:™9¯2èj{\M‘N-HЦÄö3Ó¤WÇô,.ºâr~vÖ÷WœŸá1®™Ã¿gU1𘳙4ÈÔ¿§ÛMKý¯h¿¨††Æ7Ñ^ù5’½ú›~å$4?…ÏÕ§:5V6"l=Ûó”¦bPNœKÅ5G÷QR~Þ0’IçÍ AQ–µ¬¤Eµb éi²{ÎHB‰ìÐtK%•AÚÄè>CÕÀ:¯ ½“X:‰tyJÓ!´¤ ·v°Cßâ~‹W__OAAþÆjhhÐÜÜLQQQb§í2¦J±iýFzVºÜãæÜIÔÙËT?(夌fà …Ì1),,Ô ¬¡¡±ÃÑÐЀ±(¬J“J)ÒžQéŠ" ?%M˜=)Ò]褋:ªD«¥TN¨È¢%J:zgbÊg9Ó²OéNI¤»q&~Î&Ýd.Mv„(VqÍÐH8Y =d>eѦÒ)”ù"Lžb‹ÕF£'#CÃèaô FŒÕ‘2ÖY5´ù*‡âRYi±ÌdÙ¤X1>/|É´]ísð†ºðØœ3¯¡¡¡±ÓíTš´eÅÌú{—ñœRdïÒjQ>>–YD‚Ðz‹›2ÃáÉq“‚ôI‡€£J’huš½ƒ’»¼‘èU/„ ¦lWviÂãéÄ–›ìzªbëiS­äÊ\‡ÑÛ(Âô ;ùü¥{òŠì­û¼Í­ôÃ@CC£«Í{ÜÆÈÄÁäS‡ð544þÛöJd%qÞPy°Íê¢Iú·}25†ð(q·+õ¾žlDêµ >íòÐZ–¸ŽÊ À"õSš°J$ù2‡f;‚•ð¦î@0âW²±©±7Se­ÃV6…²¡áÁÊ‚@ão#Nš àù"`Q3“^5444ºÊåaðRÏtõê7¢›½ìÉžGžÆùMáûƒºHx[tcÈ~‡süé§r`ÿ æ-û±ß)çsÙ/®à˜!RÙ44ºÈ^¹ tü2LAYžA +(‡Te4eX&£ *5²Æœ”«õ'˜äÉ1쥙µÒFe´šÊè:bÄèaô`tÎpòDN†î*nipíª_¼4{ò¬†††Æöy’ËÃ)9ò_«·7÷振å²É'°O¯."w¡ñüøÎ?ñÛkÎå ~;€0š»sÒå—rþ¿Ç˜šjht•½rS7©àºwó,:¼šð…䳋Õ{“+ýÛtH9¯ì”‹ÏÙ(Laxæ!\>ÓäEIƒ;ê)|ò\×N¿K­D(UPo5€Rô=BÓÓÍ*yŸfò¢Ê£˜ÌŸâêh暴†Æ1ò" HÚöìpË#wã´;þÊE 0W ì­ 5T”|Ê[ÓžfÚë:ÝÌCCCã^ßeú_ÁòÊÛÇ>ƒ„»û²éˆŠ,ÝŽü~Ð`é(á©r—R‚“‚ïT(L$!iQQ¬àFI ”ô×)šT ¥ÑrÆäŽ¢ÈèÁnf1MNÕÖf¢©~ÊUäÕ#WýQ´wTCCcgy!²Ë‘t=r(йi¦¹Å"Üc{r*WÜõ08~Ú¿¨¡¡Ï*y‹süz ÊçQíJlYAow&éIqRë¨\¯zÿóž“~wº•©û¨r9$¹" "*–Å‘lÛªHg¤¦Û›nrꨌ­#J” 1"<ŒžF‘O1UÙ×`êma¥I©††ÆÎ¡¤^ã¾3lÍ×]Ä ÇÍQ'_Êß7¡dO9ãh‚S0óùÎÅÿÇS/Ïdî;ï2oÎt¦Þs ÇÈQÄ y“÷¼Çôë¾CƒÑ?}’ù Þçí{O¦WÊ&÷bâOñÚ›óxëõgøëÕG34§c¤©;{þàÜóÔ«Ì}ç]ÞzýYîŸ2ŽÿtCÃ8ã¯oðÞ‚÷˜óÐöÌ×ß4 ­·NÙT“e»…NÙÙVºjÛrÓØxÑ’¿ *Ø¡(|axṚ¥H5çô%E%ßo"È&VJðÓ››êždVû'i醨F*"•BRh284Bá-r’Aõ_AKæfûšŽjhhìhãî÷ ü7`Õ/cî‚2l@öêKpÚh» ?ýBmÔ×l¤YöbÄAgð«[.`£O.%¦$=ÇÀp½Ùw¿!H£äƒO¨Oíõóè7¸74G=†ðÝ3Ç_®øùaä±×Ïîãï7œÁAÃ{b´5Í@wՄǧ! 9ðŠÛ¸âÀB쪙ÜþÛ©”´èƶBeü¬*FY6Únj§Rï3%ïü×Í ®§ÿö»Ý>Ñ´G4&7 0àÇs IDATðŒ‘† @æSQeá-tJçŠ&Y¢“Ñó3M~£D©Œ­§*¶[Ùô6z2"<”0&Fb|3-: 2ÞcÉ… ²ÛNFÝûêÎU‘î z*d=‡Màô£FaöÆ l 4}‹ÿv6Çߦ[Ïôü>7>xϸ¾ðâ‡ï³,vãO`ÂàGX±y<ß«„Õà0*~g/]sýBQ|ÊŸx캃)>ö&üóSæ5·7†dEìh~vÖòT þz¿¹ŒV™OA¨‡âÄ<ÃŒ8ë6NøÞpÌÚ…Ü{ÃÝÌߤm¹†Æ¶Qo¯ùdÈ9[v)¯¹ªÔu2Cèî÷©ËÌ7M—$ ˜TðÆß;º›BÆÿ˜Â $ ê­¶/Ç4=¦?—58íS­*BY´‚îF7òE½^ìHUlQbñ¢¦Ž*V…+@÷“ÖÐÐèjÃ,{â6ÛNV¯D×ÃdÏËžeþe®ñìÞy~UΙ§Ë~tɯùåé0 ϼ²òÉϧ|>ouûî7‚ÃÄK•—+°¿~—÷+ÒZÑ6–åëçϧ䚃ùnÞFÌ[Ý»Ý1ÌãØ+OàÔ¿Ï´×ÊhU€ÝB³ ‰<}ùîľà40ïþ;x¥<ª¿|Ûµ‘¯6HRÔËŸüîàšÕ}ü™‚RIb,;ÁÔäQe¾'£’\"âİhµÂÛ:5@ÓwV¼ªß¢ÞÙBY¤œ‘9#È7óe §Án¢ÎiÎÑ®tu/‚¦¤]mØý¦78̵ãm±£DZ›¨ß°šEóžç¯W_Âíom Œ v¿=÷@ú[˘vß­üáϱ¸Å5?g#ïÎù‚VLvÿþ$Ž9ôº‹+ß™Oe¥… %úõÙ¶êx )SÞ'ÛÒ8ÍÔT7âÈîqÙ5ÙO—hihlŸí Šè¨v¬œ Øt{+ì•Ï»é½‚Ê „[ÓŒ¯ °‰Ö¢¦”´8ÑÎ9"•ÂQޝ€*Óÿª¬³j¨¶6Š09ŒÎN¡ì–½hÔ]¦|~ è˜ÕÐÐÐØú‡ËÂxÃVÙ6Á;6§Ôæë‡ÌQGˉ§OæŠßÞÏK_ÔÄs1•í( þýòHzí6ˆ ÖòY<ùâlæÎýˆ5÷|6¿ó æ^çré¤ÞˆØRæ¼]œü$º±×©Ç³— Nã×|½ŽǰÊWRf)dƒ9ù¨â„S4D·n¹®•ªgÞ]¿âéä€IÜpóYŒéƶn¤ƒêj2«ëÓžM?—ò’6áÊÜ|û‰ª ¨éQ¾êù`®–8®J9熅!$-v4UÈ$}÷ì¿_!‚FJÒS¯kÁ¡2¶žõ±j@QdQêŸÖ! ZlÇÅØÓ‹ºµ®¢;‡^z gïiúX”<û{ú¹ôN=ïý”ˆ(`¿É7qþ~…˜”¥¹nkJ>áí¹ï³jKûùOæçrëÅÅÌûó½Ì­Ö¹Rßt¯<ùNÞ±@Tw:ìJÊ×Z¨½»óýßN冶s¹oÅ j½é{Àåü󑉬mêÎðn>nãB^™³#Ï,¦[¾¢åƒY¼½Þg“Ä@N½ëEŽTÝéS˜‹¤™Åÿ~žOÚŒÆp*gñÌÛgqÛ1ý8ò7ÏsÈÕ[hä.¾‹Ó~טžGóþuë#Œ{øJöÿSn8ûc®úw)1ýõÓÐØj[åí]OJþ(Üp>5w7¤‹„ú[ˆºég°DTfÃ÷àŽOþâ'•¢£î@»D`»¸]ž‘ƒ£ ªì[œ.»rÛj)J)¤‡XKܹ±Éõ@‹ÓJY¤‚nF÷4!u³råIÒõ²w÷òm¬Õ3yxÆ×D]ËÖ²±EAÆG–Ÿ_€(ŸÍƒÓ—5óèÞw$&Âå{à‘»_ ¤U§hhìúž¿‘u›På2mÿå”!g-¯üå^†ü⾿{”-µQ"ËÿÉoîÏáª3c÷Ý÷§?1Z¶T²|Õ”¥¸`„ÅÓ^aù©—±—QÏû¯Îcsò6ìj¾ú`#÷FqŸ^ô¡M«ò΋ð¯×K±ë³ÆPu¼{ç•ü¦êÎ?ö;Œê߃‚¶M”×Ûøžh±²¹ûɉ<|éXÆ^ðsNzë—¼¼^oØ54¶Æf‘òºå ¼÷ç‚z ­;¸­(pì>ø‹’„KTØTRÖT"î}Tñ×BÂÄÄ ÙnKUy}£*8V)”JâN?ðk4 Àơى‹æï‘;†‘ƒ™¹Ôi'sp^DôLiÞHYiifדlnצÊÊÊãç¯,aé:ƒ›®9Œ c^¡dQgó%ýý °½ Lì¦jV~0çg–°Ås³ùÃ¥‡RèNbpjy÷ï·1=tFÖc/­¶õo§†F—{¯ÕñoŽw)uVóø”ïñx§µ¬œÁ—ÏàÎÔ+,{á\öBË#%Ž…C†ÒÓ»r/}Ø”¾{%/ÿáJ^noPÕþD«xï_7óÞ¿2ÝvìaÜæúwé¿/á¨ëÆöAAÖLw2Z‚fRH‘…Zfz OCåK ðVû§™š×šw¬O{/M$"L«Ý†…•·3DMÛ±ÁöRo·oÔ­‚(¬MôŒQˆ)—$¬Oº`ÓÌVùr!¶ý‰#SIø‰ÎïÐhK„Mc«¾8e yuêÛliôØýHN?áN­º©‹Z±Kgqÿó1`ôçÐ &s°ý_T9ØNöc]cØÝ¡/`ØU@Üæ›|¾÷ûç¸vœ$§wùÔ3ï±gY¦ Ü54þ6Î^çK~+%3gpCP·OÓ_Nž šB Tú“ùó}“Ó³z.÷þiÙ.) B¦‰Ê£¨ß(&œ|8b«x¯tk:K+Zו°$ùÏÊfŽ¿‘ Ãú#•cG·P³a bÈ çqPá*¦ß÷¥m h†ÆöúHÖ÷ÿ`¡“o8dŠò,BE½‘ «˜÷ﻹóÍÍZÉYCc†“ T2£¦Æ('лèWüL“Æà"%ýU^+(|¹¤ÙÆ ò‰òëœ&H.SHrDˆ6Kôd"5¦›ýù«ÜsNrF÷][uáYËfÕBelC:dŸöºÙw"Q5yëÊ ˜ÖÖÃZùL_žN¤na½“ÝíaŽ=;î=/qgÍë—2ó±ø vk̺IŸ}Žå䣯3¢_!!«+W┇<^¸`R–>ýïWÛ<¦¡¡±ÝÄT¸ ¥òïó½åM»Î“«‚—oøaûáx ] A=˜²K0ùÓ};B¦€^ÍR¯/2X4³/”¨ÿI{+½ÒRP‰ètX†Zí(v’l+å9ßm…Ó1+‰L]¥œÔ8iâî­p<³K—¨n´6'«ìÝÙA­«ÚËëÚ´ÖRYQAgý›Öª7xhz mV- uÔ6F;÷H’".«@œÈ”)Gà|0§^ª ‘~zÞçþúäŽæ¤Nø‹'xñ‹-ž1Ú;¦¡¡±ýÆ=¸ÇGÚØ«®Ìc×ÐÐÐØ.›LAe@Ú‘L’?×vÚðE¤ÓÜÊ[2dœÂ„‹nfæzEó„Çk›KBÆ{Õ !hu¢ Š)²ÜqrDåÉ«' Èì厭KG8~NLYî¢&¿+×çtö…ïwêã ekÖ¬!²Uo’ôêI˜ZZæîC)¥<ÿÆÇ¬hQ ÛØèéáœÃˆãÏäÐg<öÊUgihht•‘WЄɖ¸kïÔoumŠ5444º`#í¡”B¤r)Ý´,IþAp”òÔÓxȬðRºdŽªÈ'õúV³×¤'ÓM8…  ÑfLj);5¦Rî~yÊ'ùéu\ !ê&•Ê+//Ò;sWSQåŠÅI›T®"¢”G6Sx>%%Ü9¤ñÙyš‹(E›ÅNHã+yB&ˆ«W‹É/ã/8ŽBy3¨§?=Aø¤­Äî}†©Ý꺥NL»”Óa2• ¤Ž§½ßÆÁ6}Šû~`õõõèo®††ÍÍÍ%ìŸkÿ­›×o¤W¥ôHC« ¡'°}Ú% % ¶ØFæ˜êÖÐÐØáhhhÀ‰Xô¬’ ¹¥8á3Üš|–,)p)!áñª OèÛ*O¾Ï#Á䎹¤9³mÑ•Ë#꾎T#LŠÂÔG›iÃJŸãº¾?”ª's@‹Š¦;|ª´ÇT¤œ ™ønr/jry‚\ÄÞ~ÞÅÑÐÐÐØnƒåÏAЇµ—õ‘€!$†4¢M/œ††ÆÏn'ÿW?÷xª‘WvI¨´ŸÐßûRø¨r“I\Õé"SzÉW'•*°Í»›˜šÂ ÀÌ!.!ª|EEŠãùç—N7H{m“¼5¥Îä&ФõISõHîŸÓÞ WQ“òÆÿƒ,èb ƒ¤ñ‰ìø´Û!%¦4½fÿ" ñwá"qÂON]áî´c/ñJ’¼*ŸÆ»È »K³5už!¾ð~<@ AØ0‘R`9621?Ç5?¿:½7vå%œ&–pÀQ^gÀ¬ÒvI'Ñ)*ƒýzVx«ìãÅTßÈ,R ]ÒCN½ÖWa#DÈ0±›+("­¡¡¡±ã©WÓ_ž:’¨½I”߸tÝ3ù˜é>.Uæ "ËÆ^P”ߦ0i±c„¤áá{~$ý¯|dÙ@`È…ÝcmIy+‘&¤ÙZO¹Ý´; *€˜‚Â@’o„QZb¢ÊÚ)öH)ÅæÍ›illIJ¬N½Ç4ã9­½zõòx 444þW¸h-°0É çs,"N4aƒÃH; ôëׯÓdâÒ~ýúÑÐа]4ùïsŽ(Ø1+ z²×a‡3n`î®íxèêuÚÑë®±klž9í¹F˜0|– W»ÍLÙ#ÿÏéB¦ÌÚu7õ5OH‘AÁ‹ÿ-2 ¡R˜òÍ\"V”¨K‰í;I%‘ÐB~ ›©UO‚JÀ2ƒ ßyþj}¥TJú*¹nÒ“Q®WƒÕÐÐÐØ~Bê¶+¹F˜°&ÒlG±6HФ–Ÿˆÿ¼aYÖV‘Ñ$B¡P‡!~ÑóXîœõsÿväV`îɹ·ÿ‘Ÿ}·è[n‡Ýö8…ëî{ŠWç¾ÃüwæòÚssÇO'ÐKî€uÒ뮑`D-V¥yF8!ë”–{JÉ* ÑnÚNŠx á9W´Ã¹Ü¤Ð¯ëéæpBO5~ò˜D"‰X1’âR)"é“eJ:%¯'³+Éx™~ª‘I6~¨þv\÷帋š2X-Þ<pA ®„A<fÊmV”6¬ Ù9€î²€-4ï¢ádÉð“Ïâ»V-ûžÅé{½Î_Eõ‡¿µt´h"7ÜsßÝð:OÜýk hÈÞìá4Ñä$¾L;ˆ”¶ÙQ„™CQN‘Vì,ÖÈ¢—Çýù¢î÷Ùà!jAy¥íñ²´"€A®‘CÄŠ`);a‰Dºc”×]ae«„ÔUûó\3nûE‚»<ÊEFI²}Ûx5¦4444¶ß¼ÇcH†0-V+'–±QòÈapxRÈ]ófóà‡§îÆçüšÇõáÄ3¥»Ûœ†àºéóøç¥^²ø,œ÷ ×îJ¼b2öªÿðÞ‚,X0?NÊMœØ‡'ßÂ£Óæ0oÞl^zôœ@ïôCPö别îå_ÏÍ`μù¼ûÖkÿ }a)iz´NíÝ {qðEwðÐ3¯2gÞ»¼3g·ß'1Ç€ëuôymÓšk|Ó핃"bE‰XQ ¹„13"ÈAÌ (sP3v÷yvÀ¹™¹¥™stÏ5B˜†¤Í‰&œ‰TLG†øƒ<°îk‹”çTbEbÙÓ88d*Ô fVá{›Uí°~ íòv!a’#ôY1,ÇÜñ‡0) W¨7°j—¼Ó¢ÃNcoñû¹KYVû.Sn>£û¿Ë´ [ãïµXù쯹ýjMÕ ‡½.º›?Ÿsÿyÿ(Œ<þ§\|×Ýä^y –DBF°}Ëå¶{¾&’7Nù WÜ;šn—_Î+£ŸÚ%Óyè–g©i„~œÇÏ/úW¬<‹[ÞiæË=êÆ÷™Ji¢{BôËÇùÚ“¹Ð™ùugøø}éYö0·þåk¢y»qÈy—qåíWRqÞ|Ø’¾š½a-ëì“8ð˜CP2Ÿ ÑήíÜOŠžìqè! ªü·ßó% F!öÚZ†f½^ûØ–5ר`£hµ£B’cš`+,W¸ßè&yޝ6ÇObƒŠ ’mBE§`6ÙNÀQÇÂRN*ßI ¬²è–e÷ëñ4G9©ûñóÈlÕöø<¨ 0“I­¸nТwõ’NV¶*MH544ºIÈ0±±iµ#aƒ>fOç ŽWµîŠ…•r Çœv µs~Îç- ë£éÌÝ|??8q$ÓÿµÒã é‘M”–V¥;¸t?Œs8”ÕONáÏ/®Á>_¼–¼qî9‡òÜÍóhJœÛ\ñ9 ?)Á>þpê_qÖ9ñŸ[çÓð˜kZýóWÇÿµ|Å?>é1N;ã¥4º€EÖ/9ä»=xùµ:TÎ^컇ÃW.¡ÕýÛŠùµ¬YćŸ–`ó9Ÿ× äÀËAcL>ü"Ípª—¹ó/ÃùÃÕ·óìá+X0ûu¦¿2›O«Z<ßÿ:íÞ•¼ç²OYðYIú3 e¹^¨sŸ×Ö­¹Æ7}íþ»ÍŠ’Ê%?œKS´5Α\D)IÈD Ë&iï—Ásï;îk(åØJáAá8­NáêdŸ§·J¿½9ezbÒ8NÜçšlB¦$VÏ1pHç*tÞ¨††ÆŽC·œ|¥h¶Ú²æ/õ6{1,wa¢ÅnÁÞ*úöÍ€1â8N³†™3¿ŽŸèR^ŸYÆÐcgïðv^{Ȍʩañ.²d¯å‹Å›È½ƒ³åTFWòÑçµäÃnç„)>â"nè^ž9›Y/ßÅéƒ Â¡ø„UýBÞþÔaßâH€9r„C.½…ÓcÏð«G?%" ØoòMœ¿_!¦eGi®ÛÀš’Ox{îû¬Ú’%‰8§?ûy,Gì¿;ƒ{wð›©[_NÉÂ7˜ñaý{¤¡ñÍñ6ã8Dh`ΕzÊîì.&ÏÈ#âDYYç• Ù%fïŽcdî F?ó—yXWN<àQ¾ü ”ƒí€Ñ^…¿ ~|›Fg µõ‚Ãv#‡5ž°w|q"D"PPèÕô+–³*r:ûîWŒ\š´73~\"«–³Ö&8&&1vlo"å«Xg“ªN2~õœ‘{2B|É_þ5“OÈf*¼U _å­¦;9áØèqÈÊß}Ÿ Ýæùu­”~¼ˆê‹Ïd·”¯SÇ÷“A_Ž>/:±æ» !1‰§ùCÔ1eS6yá\Z£b ä1Tí½x“¢¸ü½"{a‘Ÿø‰d’JÉäå†Ã4´5»R3Ý)">Žê\¦?¿U 0¥­2«Œ*•¦@U÷žÑË>©–áw«Ž6ç[ƒæ”•–fz*³­HS eeåñóW–°tÁMׯ„1¯P²ÈbÄñç0iÀfÝÿ³*Ü#,pƒîìyÜY·ßpö.À´Xòâ½<ùñÙƒ1“Nã¤ÃöbPwECÕ2Ìx™·V4¤ÃLížbïóþÀOw{»ÿ2‹u‰¬á‡^ÁïOnà‰ßOåK«{s Ç~g4ÅE’ƵKxkÚ4T´êÔo`ÇûÎe)ƒAáô÷E(¨ÖQ© ªv½BnËÝ—ñøŒÏYé)`’4¿õ5çüôxŽ(šÇëukX8¿œ)ç_Êõ+BÌZÕÅ#é‘Ò©`ùê(g=™Ó—M£T  ¨î=Þúê}ž™¶†¿O¾•Zev©`Ôñ?còð5üçž…©üLô=ô\&¯ËÒjzô…üxÔz^{àÃø9ªºFÁ€ýdÂàJ>._ÍZq?øÉql|s5õv?ú}m_0ýõ*>ç&Š»•óâŸÖdˆà¨ÆÎίsï}7œ˜ÇÒÏWPU׆,ÆÁgÂж¥¼´ÒʺNïtæ~‚uÝ;ø¼:³æ»!Å @Ðè4CE›¡(œCØ0±]zÄA|*¨¦ðy ð”zó=ý2ü*õzX˜(¶ã¤BêcdDš„ëÁŠ^2-REQi¢«RóRYîÍ_èå!¤@»š~¢+I©)¥çƒˆ'ÅvN4‚¥ ¦/ù&4†ƒ¾Ó›¦/¦1¯¢½À|w†î³7½7Îä©Ji‘y¨šFB =ñ2.>BñÙŒ©ÌØ pà œ|É%„þöWÞ¨° Ãsb”.[Eì€ÑŒ,œÃºzÈeä˜ÁØ¥/Q 3â”˸ð€ZÞ|éQ^¬ÏgÌ1gñËNa󟣤MÿÂk|Ë©Š·“>ƒe(I³a¹CBR«§<²†¨Šî”^ö]ëîÁ!ÇBî²Gy¯Æoë6Ì›å—\ÊqGöcæKÕ¬|úfîèñK.œr3Gu—D7³aÙg¬¨³A5ð΃÷0þ7—ò³ÛÀl©ä£GJxû«2–>r=7¶]Å%çß̱EP·ú¦Þx?O—D<Ö;frà¿fò€ÍŸóòïïãÑ/¥5Îzæ<þ<‡]w Wüà=>~àn»·¿8ÿjî:³k¡qsK«š\Ÿ‚Åêé/ðÙY7pÀ²gx³"Èž·ur~{ˆ˜Ñ{MäìkÎa`\œ–ͬ-™Ï߯{˜éë Ë:½Ü™û ú¢f_÷v?¯Î¬¹Æ.ô» Ãs‡PYC“Ýä#YEK¤•#DH†ˆ9VV›lÇ'':Áµ²†ÉöSP IDATö#D^N-M©ÜQár8¦Þ!E*ßÝ*^´SyŸ.¤í䱪výA:¥fÐÜÞRA׋ã›ûLæÏ÷MN›ãê¹Üû§Td»¨4™&N(¢~£˜pòá ˆ­â½R¯“EýéŸçPU^Agü'­ë¿féŠt¯f‘?žI‡õ£jöŸyn~5°bU 9odÒ‘c™÷Ä"Úò÷éøœUKYmŸÅÞ{ðþ‡¨ðHöiPöÆ×´äíчRòŸ¿1kQÜVþ§{Þ|ûx‘’e–þ…×øÖzJݶ¨‡Ñƒa9ÃË0[¬ª£ëitZ¶CÜY;ešÛÔ­)‹e¾GmaίŽeN¶MöºÿpÙÄÿ¤_ˆV0÷¾k˜{_–1ÖÎá/—Îá/ÚÈ'OÞÌ'O¶¿Òõ=ÄU–d) SÔ}ô Wù`ê••¯ÞÉå¯ÞÙ¾£`K9k67{õM²*Xu4?g5O™ÈãîךfsÓÄÙslYù:»éuþÖÞg‘eÚ½Ÿ 9t´îí}^²3k®± ñQ ÍB8}©tbžhM²Ÿ}Ô±I“<"æØ)ï©è€Pzl +•2›Ås狺=A؈ۯhâ—äsI!üxÑ“ÄÁA °.ª{nîÌS/I•ÄÛ˜…ßýžQw(àànBª|ì8™ŸåuÓŠÔÑíIõ±V¾ÆÓ—§ó9£[Xïd¥Ù˜cÏãŽ{ÏKL¢yýRf>öÔú¬žY>;É c~Î%û¬àÑ¿¾AE6ÞÛo0ƒBu,Yµ)í-v6±zu=Çï5˜¾rU8§¢©„/V:œ¹Ï|ô ‘cÙ#¼š7–4 ú f`8‡ÞçÞÊ=禿VÒPÄzä!hÔa{o±·4þkœ'òîOQ¨–²ØÛÈ&k3Ù½ ]ÂÂBª««éׯ¡Pç´}b±555~K>±\ŽNyìyÆ5œÐòWÏ«Ó6LãŽRÒ O¨;ÂúX5¸òßãäOs,Âf!a`{ ŠüÅã"Efݯ)ë.Næw U®‡¤‰@±¢¾óÝõB“è3/Hdšd'òŽ-‘´CÝ2æ&3Ò¼>Yku'£*2;8.¦¼Íh­¥²¢¢ÓÕîÖª7xhz mV- uÔ6FÓ œ-©J†ˆA#n_£‘S@·n9ñ*.ÕÞÞ§3û£ŽžªM,ûl9ÎÙû±W·/iØo,¡•3XÒ¨ —ÕȧOÿƒ¹•Žç£kkhÖ†\ã[ëuHX ƒÌô ÷AÿÏÞ{ÇÛUÕùßïµv9åÖÜ›ÜäÞôÒ" ˆ ‚"Ã`¤9#XŽSœ>:–ñ±Í(¶qGT@PPŠ€´Hôrors{;e·µž?ö){Ÿsn  ûã+rï¹ë¬½v9ßóYßòùŽãìtwSˆy">:::¡··wŸ½é+›gÓ¤µµ•ŽŽŽ¿ŒgÌçÌË–ìúÓ¯øÂ'~Ƥr4ÁŸ1œÀ%+³ÌKÍe*È‘S9TŒ”j|¥p•O“•bÊ *ÞÓ8É‹ÿ^¥'Õ±"bñâô%Ê+5Yi<”ºÝ‰Ç ™\<Ü.T›#©ªçzQ/¬%TLò©šK*J<²>Ù³±Î©Ž{Hk6®Š‡ž4凨¾}û¾ ¬»G×Nqì §óêßoá¾çþV;èóNcɲNä¶ôÊ™,]ÚŽ·«—Aµc@“{ê¬=ïN\µŠÜJÁúëŸdJC»Ùã73¯K0üØ’}‚~ƒ¤Ó˜AOº‡Œ‘eÊ›dcn#Ž*îWåJ’… ³³“ÎÎÎ?#÷Îôáèç÷í¼‘üÍùAòèºkžàŽZÓïìa^¦‡&+ËÒÌ"ž)„6*NÜžrɚ͈RW$QC ‰ÑÔzÛ±¯ à‹ˆÇ4êõ´…‰! òN±â‹œNªSå#b?·ù¦X†çù{U ¨­¨Þ•ó1ë/À4•X‘‹ð’õâéOßòs_úÎÿ?aîï`ÝÎQ²,êÉìóKLç×r÷ý|xõe¼Ó¹•Gû¡û•ofuw?÷\ÿT(ç±?cœgxè‰I>ôæóPSóÝu¥ ú©'ùÝCoäÊ3.ãbumÅ·ÛéÎñð#Û(&.Ò¡H22ÍÂôšÌ,… À§—I•‹$å×$Hà vûýdÝ4Ý©9tÚ3™ãÓïöãhD¤G—C÷6Ž_-Æu”3Úm©LSÃü¥úGqZK`3v ÇwñUq êzÂ[’lò?Ö:4N(u].iùøRH¤0p§â!m¤9º7]ÕÚ¼Z³–õFéh­öè*j:¨»—ñ'øß/³eõ¼ê”óXÕ–F¦Fyö©íì]nÎcÛ­Wó]ç<ÞòÆ‹YÕ “}OsÇ·oä®íÞsà³õ‡è;å,̇ˆ„±Šløå7øÎÔ¹œ}Òù¼÷¬4Fé[s3kMi‚¿\ØÂ¦Çêf¦5¥†½!öxƒ¨Xˆ®1ã $HphQTEö¸{h5šéHuÒcwãh§¤¬*–JkxdÌžòcáñ¨{O/jÔʳ–$Ò`Œ)%–a‘s'J$QÇ¢ÚQ‚(… žòcŒ/Êïâ^͸›R"¦&¥T…ÚPmêA´R©¶þ¾œ!+Ÿ¹H/k­LW¾”2váÿTiDù@»çùÌìéjxÃÆÆÆhjjJžÜ Ëåhoo-GDDYkÍðîAf÷™Ì±fsxz9­f CÞ0Ïæ70¬Æ*ý™5­ˆð÷Á™2þ‚ ˆ$Hðbbbbåøtì2Ha2ÛžÅQÙ#IË{œ=l*l&§ó1I$ƒöT3ÅÀ¡P*2ªU•Ô§HR· ÓºªúhÈÍL$M†a[L¨’®Û¼W=™)Ã&›Ê0–ŸDEd¡Ê<°ìU Š©41R¥÷OTÞuÄùc\³´–¶F¯ÃsBâÀK ÁF«h¡ÛœMÖÈPÔE¶¶2L4´>63d+†É…K Á!‡£Þýn?>V S êÈd€ÂSAè‘ÜKdGLÓ¾qe~”†ëɘ6éL†©B¾.5 QÎiØ>TTDìÃ%õ©µéåŸe©ås”ŒFI( vT@¿ì䌾;’CWû‘7E'-'ç&Ô4A‚ sín:ìÐ[ìcLM „B"11°¥M‹‘¡Él%%ÒØÒf›x•\¼ 2W{ì(öÒn´Ñfµ1ÓžÉloÃÁh¥µ¨@âëÛ01…$ЪA7úxH¶a!¤ à:®Ö˜îV_7¤Vª†ÑÕ×û‹:±©ðwCJ”®}µ¶ƒ“ŽÑZ_k´åh8‘9ùÔ±Kþžø"$HpÀ <Y#‹8 :¤¤E·1‡³…³SÚh|®öÈù9F¼Q‚fHÂö $8´„tYfžrÉy&üq ªÈ¸7Fsº‰®t£jœ^§W;€®3Ù¦E1ðô> ÅòE ý¬—ˆR 2–x¸!ƒ¢â_Õ•°¼Žå•ªŽKKÕ×ç‹Hé•D …,µEWßצˆHÏOEÕÛZ%¢UŸ©Ù(Yuz5Röãõ $ØjáI-™—Zˆe˜R"„D '(0L0æ3ì2Lák@넎&HàBé0o2cfi¶š™£»ÂBM~à“6SÌ·ç‘÷sìñ*ÄO)…)eD—³‘T’®”Éת]‡$Sb[Wò¼²WNW­t—2´¯U2?N}RAùØ)Brë+Yy#–ؘ¨ê)­zOf<¢ Ê×Ë Ý'!û BZò:HÓ@hE€&ï˜ô'Æ&)ŽvpµOP©M6Å $8´XŸßÀ £•f;Ífi‘Æ´L¤id‹ÙLwª›)•#§ò!)U Ã4kˆfµ«Q•´5îc$$QJ!i²Ò=/ðë˜Z|†*¥”пâá¤n¬®tuõë•f¨—ªU‰JÆÛ›êɩڊýzÿo,‡TÄò(1ï(Ë­žàÞ:—&H Ásƒ<íQ Š8ºÈ¤7Å„?΄šÂÑET<Ѷw $Hp¨¡´bÈdÂgФÝjg† ‰i“Ì¢EŠŒL3ËšÉT0Éo'A– !+ýÜËÿdµÅghãtÌ{o9ZÞÈBâø~Eä)jW£áòÐëÎ.d8.ЪÆÇ“d]ùU8Â’ávÑ"³´›ítY3é¶ç13,nZH±X`ÂD£Ð,i()ìuKqBXëSÔ€!$)Ó <´V5äUÔäqÆg6DØNJUimÄIT*úõÁuSšZE:èÅ×LŒ`—ÉuÔnë–]” i­Nõ4t¬êÞ¤[$Hà¹#Ð[œm蘢]ÜUM–Ø«Q; DYk†‡‡™œœÄ÷÷¯ªß4MZ[[éèèeQ$HðgºŽ¶õTäT‚WdÀÂWó3óHÉ ]tQE0Cû±Ó8®SÓ!‰}p«ú§”a‘²mƦ&bóˆihitSš(M%T×Ðh£xQ§‚ª1 ß j¬pm“Ó¸g5úJ#_nÉCZåßÚñé?u~Š $x!PZããÇŒYT4„˜ÚÇ¡³<###¸®Ëüùó1Ms¿Þãû>{öìadd„ÎÎÎäæ&ØÈÙœvÅ9iç÷¸êW;U›—8…¸Æ‡F—„å=v¸½´˜ÍÌIÏf¶=‡~§ŸIo !4–a`Øép|Šæ‡ýäƒØ\£báo¡RLÓÄñ¼’s#n¦´ í©)Œš`~™.êXØ^ÄHwä5­ J^Y]Iñ¬×G­¯Ðj7Qw g¯Ä$K5õ ûñæR ¦7.möÚs8ýˆ–¤8A‚}‘Ò:›SÛ­¤–ŠüýÄÄ]]]ûMF!ô€tuu111qè/¢ÈÒsôɬZÒôòŽ_Ìó8Ps›+¹âšÛ¸öÃÇ‘:`k›Á‘§¾†cºÓáÚÆ1ˆUc·DŒìL©{Ü=‚Y3Ã\{.)RøA€ëû}_H!ÈØ6Y;Eư±¤nÅ1ÂVýoØ;Þ”p<"½¦sÖÚOÓ4Ѻºz³»"ÖQ©v6S)+áþzr©k¼¦Õÿ6~]”Rtm•}ýE¨5ý:ñîç3ÛÉQ¯{=Kx˜{ÖO&×#A‚½šøx ¦º1Ó8}(êÀÃ÷ýçDF˰,k¯!~‘]Äkß} o?ýDëiÃtGè}æ1îºö[\óÐàóßê›GpÁg?ÇòŸ\Äc[r/Š¥Ùżî]qþé«8¼§Y`Ëš{¹éG?â¶õãûwnó<ögnÑÊ>ù#>qz') Ê+01°uý†ŸþèFž @åܹƒíSÏ5s(Ž‘ày¢L¤Ê¿Åi™F3ì2âŽÐl4Ó•êb\3¤ñU@ÑwÑh$KZ¤L›¬AÅR8Q²z:j#ß²V _±*ùÆ%Cõ¢öeO¤Ö¡Vî—äSÕGjï—©hÀ5µBü:Ö“©ž”–kïÍèDºÁð(sV/´ @´pÊ•ŸæGÔyŸõ?ùßzN¾òÓœï]Ë¿|ïQÑÄqÿ3׊)@.¹Ñ~¶¯„»ï¼ŸMãõSë—òùËñÈ·þëž)ÖüU2ûŒòOgÜüÙÿä÷#ÉÇ;Ñz"úúxÇœ>îºáøå†!‚¶Åœôæwðß| ¯øÌ‡øÂ=ƒ/reÐÒÞŠ±î‡|ì›R0›è˜ÿ κðý|åU ùø{¯âá©-Üø‰+¹ñ`.C‚c$xöІt´l©r*Ï6g;3­™dÌ,³Í.¦Ü)\¦*¶N®òð\‰À’é”M“ÌàAèI üH5¼% LÛ¢PpVõÄõª&w3 ¯+(µ0nðÛ_bA{‰) |h‘Ü»ªõF:¨ñ桲r #„TLCFËBAL—T¼Óéo¾ïÜò,nähùÁš¦:™Í6!¶ý†«oz×ÌÐ2k)'~.8bßýÒõ¬/èØøæ¶V 9ƒW½åµÜ¿ñ7ì ¢óxÎ9}–¤µI>V ¼Ô }}¶¨ˆµÏ{ù’Ò4Ǽ÷ã\°` ÿý¡ñƒgó•¿ÜwçM¥o›ø‡¾ÄòŸ\ÄûÚŠh÷¼ƒoüø6ýýÛ¹ê ‹åç|„½çu¬˜ÆÛÉWÿŸ¹}w‰è™¬üðuÜ÷a—ß}ò,>qWäLV]ôa®ø«W³t†fdóƒüâêÿäÚdžÃ÷É.^÷‘åÒ“—Ñ3«•4yúžø×ßërìY§sâ².¬ÜvþÙ—ùâµO2Yw 2÷Þóöîg¹úƒϵ›œò™qϯïfýç¿Íßÿýyà±Oó»Âñ{?ǵӜÇ=-œöÁá’S–3v)5Aߺû¸ñ»ßâÆµ%ïë¾®ßÚ½\£ZØø6žZûy€'æ›-®¹ú¯xóªoððï{¸ìûßcõÃࢫ×ÈYû^€9‡“/ù—®>ž%³$£ÏÞ˵_û:¿|fªþ©–KkŽQºG¯^JwW n|€Ÿ}íË\¿®ôþç2‚BG«¤TVìS(¯¤É©"}Å>7-a†ÕÁ\ Lå7FlYµ*]žöñ…!†4°L‹´B+ã»J‘¶lÏ%‚/dÜS©#³WùœÄ6La¨¨ ÖëU5Á|!1 ƒ¢ë†Í" µÎJAmj£ÜTc”f-VDËöUì!>`]ìsƒlݲgßw9ÄÔ[·n Ço\Ϻ]ÿüÑS9éð_²~ÚÒÖヌÎ>ÕGßÏ×ä*]R¼öLŽ( 2˜j¢¥IRɃ•m~úy¼åÔ#™Û¢™è{šnùwm˜(ë6Ž=ï"VÙMçŒfR8Œõ®ãž¯çÞm…’!èâ¸7ŸÏÙ¯\FgÚaxÛ.‚,x±wG­>—3O\NO»drçZîºñFØQHŒE‚¿h躠WÜÔw\~~bÒ'pΙ=Œþö+ü4BFŸ ä’wò‰¿;™ÁïÿzhÙ¹æáˆ×ÑgãOþ•ÏÞ¶…fj¤8òo¿Äßw~ûßøæÁÒ³ÞËûþ¿/‘þÐ|o½ ´°øØW0cÓÕ|ò ðZŽàü¼Ÿ¿{ÿnþîÕü¿ß)ÐqòeüÝÿ—?u_û“Wwng¯žÍð_äç›j,»×Ë­ßÿïúöùœ}J+¿ÿí~¹-œÇ\–žp³¶~»êYœL7'œ{üòrš?ð~°ÑÝ_—Hƒ¹÷ ˜ÇÑ&¶m4økë~¬-Ã1W~‰;½ŸýŸ¹j UŒÿó…ÑÑ¿ó`q_+(Ý£­ßá3ÿñ,nf'_ø~>ôÙ±ãÂç¡ü>æÏ%væàm¡Eì§Ê£¯\zÝ]ÌNuÑf·Ó%æÐçìÆU>¾j„ãAkP: „ 0‰i˜Ø–MÊL!ÐX†Å”S ãk]•¯×%Vo?%„`&_/ØmeZÛ}¾Ba„À2LòºH£Ä)‰fÅ ¥â!üF}› ¦CZ›n= EmoSýÂî RÊX±RûÈQ®ƒ¯ lÓ¨÷¨¶4Á®ÛøEÿë¸ìŒS˜óäìV ZŽgõ)YžºñçäιöVð‹…o~?ï;MóØ-×pK¿`Ϊ³9çŠ+°¾önÛáMÌY¶”–Ý¿âG?ëųg²òŒs8ï½ç²ç³?å7Ê¿¾’÷œPà·þúíKNâ‹¢„ÔfÉ9ïçòFøíÏ¿Ç cY_ýþúoÏeøs?e}1ù`'øK6겆 Æ•ë$’ dÖ^®R9k! š¶¬ß€ó|çh›A›˜à‘5³nc6n¨ã í`Ë–¾ I-§rÁ_/dó/å‹7lGÿi'™%ßç‚wŸÂO?yS¥±ùkyä‰õ<ÉÀ¼×óê‹¶rßM¿ã°Ž=óß9æ˜䟶ÇBïrÖd6¯ß@#ZèoYÏFÇfù¢žý.ò¬=òs;ç¬'þøÐ&ô‹w¼ûU\÷™{É?ß¹ÂIJmÒv3ÇÙWžÏRçOÜôäôGÙÛÚ -¯áÝoÅ#W}ÿ¹g l¼j¯úÙe¼þ˜/óà÷oíùíkxèÑõ<Îãݬúö™¼êp“‡7ícþÝÄÐ4©hèP Uì*î¦Él"-S̶f3îOáë bËBk5_>œ,Ì È{læÉ Þ²©4Ž'q_Ð%–¦£ùš1ª[Yªix~€®ã\:°7ºLÓ@ ]òÖàKƒã¤TÆe½üS¤—}­kRÄeÕSRuURÏæÑóů^\%˜{îäË_¸…ÓM. ,ÓDYÚ»–qÒ9¯aŽ·‰û¶8u¤¹)ŸçÙ{îaÓ«ÞÄkÿ=?]ïÑsÊëX1ñ_{b£Ï€yÍMHòèìÑœ~j}¿ù"?½w ذi€T÷Ç9ý +¹çk(”f/îÙÄÓv ØÀƉNV|ôDVÎ7x¶ÿhN[ÕÎŽÛ¾Éu÷…Fnsžž“ŽbIùZfæ '·²þº¯ñë5aø¤÷ºŽøä›8~É ¬ÚO>× 4ôž–ÛË\ÛC”̼~éNOÝĵ¾–~åVüö&nüù¯¸wãÁ^Þc,XÁ²Ô÷?!`ÁNžøÓ—¿zó{X_·$Åðà0dÚiO‡á-MÓ–Or¸yøñÞuüáÌ3îeÜÚ~Íÿå¶{þoé!ô˜ØòþûWqÓnÅ~±êšµmYpK2Yºÿå—ÜýÏU–mZ gföy]?µ»—~ÝJ{‹ÄØçün‰;T´Â’t•—Šš1}þn:ÝNºS³™kwÓëôâ)·ºi¬{G½VM¦ÐR¶M[ª­y§ˆøø‘¼P]ç‰ ;5HŠ!”Q:*""ýñô(£ô7ßкì¨l\`ZÀ×5+ª½z}ÔðÇhÇÒøéˆ¾Ý8éõ9Õ¿â7=SõºãìÞKGRså…|þË–ÎÐ'·{·ÿz¬-J’š²àŒ9¨±G¸ç±7rùNäŽmc¼ö”N6Üz/½Í²"d²Ùð-]ó™k²vÓPÕX«!6oã¬#ç3K®aG£/Éá†i¢)+3»™mŒòä–‘i¿0e×|ºí|†«.ˆø… ×–A0™‹áÆ=ªc§jöù"¶9zIÕàNúŠÇ,_‚Åã =‰hE À˜®ºßÝÌõÿøn<ñMœÿ®wò¯ß¿w|çïøèž ½®zïWö9ÙiÏG“AвÛÃà ÂèVí|j¨—ƒãV†}ë£u`sÉ –¦çOp@Q–y—{¥§ÕâC˜H!Òb¶=›!w„¢vPZ#ttl½…3 Ó0p\·­”*¥1¡lÃÄ”+…Ò×÷ t€RñZ !D5ÏTDÂñBÄlÒ)0 ÇsãT%ÞüijGë‰|2¹.õfÒq%Rk*uˆó¸òClß¾}ßV¤É¤Áu‘jàaî]ÔÉŽ[~Â&À¥èA:“.ÙAŸwK–u"· ”Š˜f²ti;Þ®^o³8x IDATÕ¾uyŽå+æ`në£Qð] ífß̼.Áðc{Hô 4òá麾pRW ›ž–¾¾Á¦øÃw¾Êoýùæ±üú›yp㕞eGÓ³ëg\}Çvþpï6.½èJþqƒÅ¯7M@ÏRÚD™ãœÂ_/Ù²©Ÿ‚9‡ã¶ÂÄ8ã P;xf³ËÛÞx1ç?}#[ÄÚGïã®§îçÚ·óŸ†*|ßl,;ëo¸xñv®»ê•üÑh¤yô»ÿÁÏþü¯«Y~Ý/¹ÿÙ!‚ö%¼êœwð–# ÜõÙÿâž1 ìý ¦9§$³N¹€‹wÞɺ=°ð—sɲÝüê…硞çÜOˆÕ>Ö6~/×ýê=\uÁgøTðCn}r^¦‹Å­}Üöë§Èé F'sŽ'ÍïåÁ¾çøxísþÄÎÈ t£R&A¼ú¦,úž–6i¶7ðŸ6³•.«“o„"^d]rúEÉ› m§QJã^ÃüN?ðñ!–4I[6Íé,J)Š^Ç*]¢¥ªÖVOG‚k‹Ž†0JöñFJR7~Wc§ƒˆdÉŠ—QÙ§ètL›-ªkšY½ôž’)\·´7Ô“¬¹ã粬}°¬à9²)CZÀT~-wß?À‡W_Æ;[y´º_ùfVw÷sÏõO5öPÔ9,Ör×}{øÈ/çRnçþÍ£¨ÔºìèwÑ“üî¡7rå—q±ºƒ‡¶ŽâÛítg‡xø‘mc‘à/ÜÛ ìÌë³Ø£’&/?¨Á{øìû†X{ÉEœs·8cVéN2´}¸!…$`ã?ÉçÛ>Æå—~’3Z$Îä0ýO?Ɔѳc)¯½à|pn+¶?Å®gïç??ÿ6üîê«8öWò7Ÿ= 3ßËÃß]ÏÝOmeÝwÿ‘?Ì}’3Ûatóƒ\óñ¯óãõλ‡ãóõ\Áº /æü3¯à—4c8Cl]s7_ùð5ÜüÔhÅïíÑÓœÇÓá÷üVV½ç_¹xŽEnÇãüâS_å{O”ý˜ÏsîBH÷µ¶ã£üËø¸üÍås—g!·‡Íw_Íï9µ›;þçgœúçòÁ·Þǯ.<Çãïcþä;æ Y­¨•*G^e„|eE[˜xÊÇ |2Vš™v3Ü!úýØV<ÜÚˆj£M–iQpŠ‘@{•ŽV;8iÐ/ðð• ±g3M4)ð””7ðjJ§1ºxŠ€”¥5žòKÞT±Åµ4<Ê£t´1·¬ì3ëEcm5ZRª¦mTõËAE8í®ùÝ] oÕØØMM5º¢%aüŠð}¡¬ùû¾Æ×Â|—þû%dnüßúÃtÆdÅ;?Íûºïâó_¿›!È6VœÊ>õ4ÃdßÓÜËÙ§ÞôÿÀ O•/ÜR yeNä}Ÿ{7Þ5çÖø [XúÚ·òæSbagáåìãO·ýˆ›ËÆÎhgÅçröIG0·# …QúÖÜÌ_ÿã‰F‚?cär9ÚÛÛK»òˆ¢‡Ö 蠟/3òÑ {tè7PùÐ>íéñ)ƒÖÖÖƒ²æ­[·>§>öexžGoo/‹/NnüD­.g²¶‡('`ö.«RhTö‚JBT½¦Ñ¨òéÃXš]‚@²«ØÇ¼Ì<‚Ý…]<ž{²R T~C9=R)Ó&e¥˜rrøAP p«꡼’Ã00‘¤,Ë4PZ“+ñ”*å©ÊJ²©¦)Á”&ù)ˆyrËô;Êš@×zbk5JÅŠ™‹õâ±vêkYË_ ªÂÔË}TË‹î›ïÒÙ=kÿ i‚ BÚÚ5A¯H_8]ÝikªúÈý™S,éê견ý&£¤Ói:;;“ŸÒ–„Ô¬!¤!!“"ômÊJMzH.Oj^EWº‹¼ŸgÍäÕt$©Nò~žÇ&g<˜(q+ëc/… 5ÝDÑsC1|Y"¤¢±‹¿j% ¤M›¦t?ðQ*´¢Nà¡´B+…ÒÑNœ’h¬Ü@МɢEÎ-ÖE¨D2´®éÏ ñ׊REét$d/ˆJáO'<-bõR $Hp0PÛð.R‡@ª££ƒ‘‘z{{÷Ú›> Ó4imm¥££#¹… ü™¢\è£cù£ñž—‚,išÌ&„äƒ<£Á8;^š¬fš¬&zRÝä í+×Cš†WôbG•Õc”Ç—”õ‰+iBÉ(¥5ã…¦4I[ÍV-¡è¸xW’sŠwÊ%Ï/Zã© ’õIE;U7hb*bÔ¸zÕd ~”Išñ ,"ÂÓbšNb[‚DÍ=A‚Š€Æ šBo ª±ƒ¶"!èììLôT¢ò†™ð&0¥AWj6ÍFs…`J¦4 |_‘×£$TLC «ŽÅ2 Ã@~ ÂÈ“¨Ùaå|Ñw)¸E<ßÇ’Œ&]*„²­P”_éÚÜU]#u]CÔÕ #Ö=*üDͺiTM_+~η-›¢ë¼ x $H@ÅË@l“M¯ŸîÕ„’&HàÅØ<ë˜öPégµMKtí´¤ZñUÀˆ?F^*4m*˜¢·Ð‡¯|f˜ít˜í˜¥î–a \§ŽŸÕ•2•œ†:Vø·§–a¡•B« 2@o@¢5øJQô¦œ®ïc-é& )+DX 0xj««ŒÒE]ÕMÓ·5)¯]F_u¾‰êtIŠ eØøG 2š A‚gäë jt3\KK:š A‚"RY¯©×EÖ@“ÑÄœt†LxãLú“¥:úÐzùÂg$ ½¤¦I‡ÝAFfH¥Ò(­K2OĉoMU4{³QqQ™à*]-Z—5Aõòø²†’ÖÇs™tòäK†šÒYZ³ÍX†Yi7Z«UZë)­•xRq*»ž‚˜È¾ŒI›*­Ch¸:4CJRi›‚ãF$$Hà…÷Æ;æ²4´Ð:’®Ÿ A‚/¶½ RŒª01è0;i1[ð•Ï€»‡©`2,Ì,¥Bj4Eí²Ûé'Ð3¬f[]˜B`~àG<žºa°[—ÄðËë5ô5ôfJ¤! ´Š‘CËÍŸÞK)ð<Ÿ©B@+²™4-é&l˲&VzlËmÔ«ÚMºæØqZZ]¥étAúð­FIõ_ _%/$HpàI©¨3ûq_€(Y®„˜&HàÅ@Y|©ÎRErH³2ÃL»“Œ‘bÊ›b»O{áßu©Ë‘øÊc—»›œŸ'm¤èNwÓœj&AÔUÐ˘_4’±©©Ë¿/ÿÅ;ëi¯ëÊHÃ2(YyU Ó4q<Çw)8E×EAÆJ‘¶S¤-)%RÈÊZâþÊúò¥¨u—D2MËzW¢¤(g«¡[ZöG5¥ã8¥¿%9\ $8dTÈRNRyw]ÝéÇsÚ)U &û ¼˜›g ŸKÂp³,íšg[]´Y­øìvv“Wn¥»RÔÙ§PŒŒ{£ 5ÕÊìLžã¨ B@Eƒ»Ñ ê¾\d”ÛŠ A*eã{ êBìÑ,Ørîk™lK-0„À2L| µF)ëºLås¸ž‡mÙ4g›iJg0¤¬tªjØ€^¯ýñþö•¢¦¸šUuPTz:“N£” ¼ú%H Áó„Db »Æä뺧h%~‚ ¼xÐ1Ö¤#T¯E6Ñ“žKZf˜ò¦ØéîB•½£5´¶Ì¤ú‹{È22E§œQ*†ß£GÖ5G¬]Y„tŠP3Þõ}´7úÕ™ãô´öLâyû:ÞmIße27ÉøÔDH¦³M´¤3XÒØK2CdŽs×ujе *IXfXòïún,)5!¥/ƒ/ûŽ#9næNžØ0yzÛ$Hð<žQ!é°Úò†qñ*tTÄùi¤¡¨€Jß’ ¥Žãàû~¢"’àEE9\šN§+9y{{n‹ÅbæMžÛP¢Ò0H§ÓH)Æbt°LÀRÂfaj-V… Àîân¦üÉHÏhkÐj úD0E!ÈÓf·Ò&:°°(â ¨•ÃÓu¢*VPˆR§¥Ð: ÀD©¡ˆ]ÝÚ—ªŽä Ædý…Ä4 <ß 3 *î‚h?§@äœ"†0°Íðš§Ð8Ž‹øÑ&ЕõJˆt…*]fm['-tDô5\\Ú²q<_©ÞœàÅûÄ¥è\´Œ™…Ílè/6¾'©E¼éòKxåÀOØ´i ã #Mðýò˜kÏÅ ÆÔx¥`²bmtÕSÇ9 >­Éçód2š››ÒY‚Ïms”Ëåhjjš–”FŸ[Û¶“ç6ríòùü^¯Ý °ÄÛ laÒiÎ`NzRHF½Qv»»K¤²¶}&ò`"1……! 2F†f;¯ˆ‹W²ñÒ£Ú¢ Élj“BV6)]* jãk{;Ó0q=·²É‰±RjA€Bá>–e‘²l2v éK”òaãÝœªÙ’‡VkÌ --RèÐQÎ0¥! ” b•õâùäŠN¹òÓ¼ó³æ>ëò)¾õœ|å§9ß»–ùÞ£8¢‰ã.þg.:®S€\r£ýl_ÿwßy?›jViþs3¿åê«ogk!BÓÌ•¼çs—Óö«OóÍûÆ_þÞBc!§_þ^æýîóllHHMù.N¾‘o^›Ñ/ahM»ÕÎì ›\±€ƒ)¨îîѪÆGq`Q,I§Ó¤Óéäž$xÑ!¥$“ e€Ç™ö¹, És»—kW,+?RR»6³y™ù¤ SÞ$»œÝL©|µ•ŠŸ¨PS—5‹f³ ­–´è±»õÇð´_‰éýéËTR"*¤±¼")%¦iâº^IОˆV*¾'jHnù³ôþ|±#¥øK­q\×uI™iËFZŠÅŽç•¯c-P£bfœ'ëˆÒ ›ÎPô|¥*¢zRÏþæÛùÎ-ÏâFnl~0‡¦©ö±"›mBlû Wßô4®™¡eÖRN:ý\>pľû¥ëY_¨§bé…«¹üÂa¾öý‡ú‹%b>Ûïü/®r§È‰Jðöf ‘R0'= /(²ÍÝ^ ÈS1}ñŒ©ƒ£ñ}Ÿ–––ä†$xI!•JUHçtÏmkkkr¡öríÊÜ)+3ÌKÏ¥Óî PÛŠ;ñG¨Wå¬z4Ë 5³"Åœt7)iãëK˜´Ùm4»-Œ{“Ó ÞI!"í•u]~½iضÅÄÔTM•P‰°ÖK|}–a¢UþבóU1•zQJ¥Š¾?ü¯ë{¸‡é›dí )+Vë»ni<5ç&Â}­~™¿†‰!®ç…¯ J9Ī»ž3rƒlݲ§þî6ÆÔ[·n Ço\Ϻ]ÿüÑS9éð_²~[ÿíÝÈàâ·qé™ý|ýöí¸Ón£Ú8üôóxË©G2·E3Ñ÷4Üò îÚ0nó0Þñ©+™÷»ÏóÕ»†P€ì|ùÄéìúÖçY·êÿòÞy÷ñ¥ÿø5»Txm§|O3Á>u Oúµú\Îÿk÷Bè¨ -lgÒ™êÀ'` ØÏw/ÌÛ,ÿO×Ó¿”°˜eu’µ²¸ÊcÒ¢Ín!kfi7[ð „_%›:RéSRGŠæ™F%ñLi •®D¶E)ÚÔG­©¨ü"B~e^*Qô¸\Tåg!:Ôø9•Ç”&–ib7Ù¸¾‡çûJ†€Ð¾›Dõý¨+I)° Ç Ý½Z×ç+¼€ûêVE¿”ÔþÓ!å:ø:L mø÷Á¸æÖ!>üÞKxûίpíS“ ö ßü~Þwšæ±[®á–~ÁœUgsÎW`}í+ܶc_Z«[žÞ„wÂr–¶ÞÁ®1¤Yzø|‚-?g‹k³äÜ÷sù #üöçßㆱ,‡¯~ý·ç2ü¹Ÿ²¾¨™Üún¾ænÆ ‚¶ÃÞÀùg¿‡¿êû7®YSØO¢ïîoó¿¡PF}ÀfÉ9Ó÷ÙŽ×sÑÛV2vûÿòõõãˆæ.Òc Mð"RÅÎâNfÑb51?;ŸB®È¨?†7ºÜ÷.ùN Á‹@ AJ¤˜—ê¡+Õ…! Æœ1vwâh7âC,‰—I¥¨”3ÙÒbNj)ÃfÔggq;Šº²sè°fÐTL3¡§ª’w1_f”FÖêÈ LÃÀ‚JŠ€ «U'DÄþj–Š¡|ß#ÂG#¤:Þ‹¾Ú4$ÞºÆf{A˜Gê+ƒ”e“²lLÃÀu=­Ð%þ†ìˬ"<) ¤M!%ŽS(ý]°@™yôÅ|ñ«W¿öÜÉ—¿p ;¦;€4BQ~+C{×2N:ç5Ìñ6qßg:_SOßÈïü~×;ÙôåïóðDÍ•=šÓOí¢ï7_ä§÷îA6 êþ8§¿a%÷ü` ûrô7­csðŽZÑÄýM¢í¥¹Ô`ëmÏ’ÏÍNneýu_ã×k¦Ð@ïuñÉ7qü’Xÿ´Oa×zÖ–'ëÍÑ}ìÇ9iÑläšmìo”Ý›d÷î¡ ¡Ù½wƒßB3žÙ´m½.°3±. ^tô;ýØX¤²‹h³ÚY”Y… ùƒ¥èŒ¨tŒ‹+”&H Á!&¤",bšmw± »´‘fÔe{~;cÁD¥5gU0?BcúiºÓÝ #î0Ãþ¶›bFªƒ³…fS^®ä~Ò1Q&!DŒ¤–A˜n à—ò6Ôxh«sÔ6#-‹éx*ˆYÛFy£UŠLƒÔ€r«Òð'OùøN€å™¤íM™,¾ïQt<­0kY¹fÌ6-¼ÀG+ÕÀúÂÈ©¿ñW|ã¦gðÊ/¸ãìVLët5W^Èç¿|aéü|r»×qû÷¯çÁ‘½ùõíNÆë.Ͼ§FiÒ˜Ž‘òôcÏ ÞyG6?ÉÄq+±6ÞÂÚI ô$þø›ÜÙ«b·³8‘CtŸÎ¥—ž†zðF~ôóLÒÅ)^Â1û{ìé½×ãj’{¿õYÖ¾Š×¼þõ\ðo൷^Í7îÜQÝ $Hpˆ¡Ñ8Úg§Û’îÔ<šíÉ…˜Â¤ßÄ¡HY4q&Hð2†hfщ«èœ7Œ¿¬>ÎÁ¢ôf¥»° ›1œùíŒùc‘êñ*i,“@I;j1›™™š‰’1œ‰`Ÿ€±`‚IŠ”‘¦Éj¡E6“Ó9TI ©Ñ )CUOÔ2-\Ï1à(!®%©ñdL) žW©ˆ/s¿P´J5D¥4«”#+âä–ºõ–Õ¡×¶à|Ó´J:¤¢Ê^…ضE¡è† §•¼Ó®:¤Jgù!¶oß¾ß6 5òG~öË•üÓ;V3Ï‚íå×vÐçÆ’eÈm!Á•3Yº´oW/ƒ “LLÂŒ®Y˜ìiP¥É=õGÖžw '®ZEn¥`ýõO2¥¡Ýìñ›™×%~lµ©ö‘ é[øÙmdC^ƒ,2˜N½ckÇ¥NÎBíã¸á{Ÿ¹ŸŸy„5çþ:íU,¾güÄN&x1I©ÂUÛ‹;ZÒ•šC«ÙÊ´‰’Aoˆ¼*è¤-G‚/o¤éœ¿ÙêÉ—ÝÊ¥ÌJÏÆãÞ»Š} {#:ˆ—U‹Œj^)µý”̰gÐl6ã)ŸA·Ÿ‚ å•<|ÝAÚì6ÒFš«¼[¨ÈÞE ؤˆê™† —åž•‚$jôC…œÆ-^Ó0ÐBãA:il|¥ð¨š'oë,êŽÕåz žïák ¢Ôƒ5¬²¤’@‡º£Z룹6íSŒ?v7oô±",Zç×r÷ýÌ]}ï|ÍJ–/_Éiï¾”ÕÝýüþî§ÂüQ5ÀSkûÉÿVÞõºcYqØa¶¼'.Nå<ÃCOL²üÍçqŒÿ'Z$é©'ùÝCCtqŸ¹Š#[Æa+O䵯\DZ€¿g,æä3W±bá|æÎ›KG”[îëØj¾]3?ƒÓVÆa+OâøEYØÇqåÌ•œúê•,]0K–³¬'ùù$fŸà%ñiU8Êc[a;ó;)ú­©V–f—°0µ€¬Lc™T'Hp°<€™¥œ~Ñe\ü¦4'³:ÂÀ&cî8[sÛØã àk¯ÚsHt¤J<Ú§]Ê5eŒ v'3Ô;É€7Xš#dYCþù GÚH1ÓîÂa¤TBëÊ¿èÜ¢”GšJ¥|¥JY§5ñ|1í¿Ð®Z¦‰ïh­¨¶p.·Äi+^×p~-Ê¿©Ø1êŠOu©‹VɆ—ÿi!PJWCöêV¥ÓiÇ©T½×VÔ¿,ýjŒG~~+Ç/=ŸjÜcÛ­Wó]ç<ÞòÆ‹YÕ “}OsÇ·oä®í^åëq×oÀ›ÞÎY«ßÃñY—Ÿ`dûz+Jó>[xˆ¾SÎÂ|ø6TâÞE6üò|gê\Î>é|Þ{V £ô­¹™5n£Ø{?º¡•·ñ×¼ïµiDàŸØÃö¡zŽ­s¬¹ù:–^ôVξüÅAÖß¶5Ûöìõ¸~K7Ǿáuœ7« #(0¼ó)~ñã»éKi‚µº.}nEU`±^D“ÕLw¶›&³‰Å yƒÉEKàÀÓQÚ;ŠyAoöQ1k $_µ(²ËÙENåJD1^ù.¡±W±díf3h³Zñ´G¿³Où±\МŸc›¤Ãî`fª+¶­H)Enô8†¤l›©\O5j$Ҙˉ ٖضM¡P@i]WÌ.Xk›OÔÙôFšLµÓŠùˆ™Kô²±v¤Ø–M&“b|r’ ì-‹åG*ÅtÉͺ}^žÎž®†7lllŒ¦¦¦äÉM ¹\ŽöööІDе֌ìd^_&”ƒ«˜§pg.µA‹ÙJOº‡6«CHò~o„ßÏx mË& >11Agggr³¼ä0<<<ís~@Ÿ[«‡“Ï{=ͺ“‹Ïä¸â}üü÷Ûp*Ì!Ë¢WžÊ1ó;ikNc(‡Éí¬ü1Ö”:îט™œpÞ[X°ý~ùØ8ó^óÞÐñ47ß¼†Ñp3Ž}+ç6À¯ox~up®Ýó±Ò…¥»[)h­2ô-RîS/…¬äŠ–Ó £uì’U-'ЙCÞϳfü Füq‚Ššˆ@°<½”ÚÃ6l}”Ý^?~Åë*¡2¾l6Mâµ¹™±É‰J»ÎjoúÆdPGtEM३…±©ItIîSGˆo˜ýY*`¢¾”®æ˜j!"EXÕÔ‚©ynËbýB€X†‰çù!¥* jr$HàÀ¢”TÙ‡ ÷>>#Þ[ó[ÙUì#ççH6³R³0„‘\¶ éG°˜m¬Û<Èæ§·¡¬`ISÔ¿•bÆœ²cëøý¿áÎß=Êv=—W¾i5ÇtÈç0&Š€¾~T{7s²eqö4³ºÚñv½äº-: §ó¥²œF¤“¥I›Ž¤>HZfÚ¬6´ÖLySŒù!-±ÃðýЉ`’B ÊÏ´:±„UqJÎ_-HI¢m˜¸®/À—S IDATF"î%ÅÚÁ ! ´RHÝ༱÷Wg‹n!*AþJjA… êH¿q.Håé0 !EÇTcéXÏú„Œ&HààÒRM­ÉÒÀd0ÅöÂ6ç63쌔ÂI2¹` 0>ÚÌÒÃçRؼݾÆí}–-ùÙ¾|F]ñ‹7¶›»vÑ·cþöÖNvpÔÑó°žÓ˜êXw×Nv«™ÌŸ›eÌböLÅЮ^ª¯u­gÌJC²j I—=›´™ÂQa¸ÞÅ'Zî#Jä2§rLúh ÝžA“ÑTo#uµ·¼! LÓÄqݽt«!e®H)±,«Ôs^U»/ #£•wëéóQkíx¹_ë©XôtÊFKTZ¥£j¯¼6A‚ ^ˆofzS^~5Àg$cCþ¶å·¡t’Û– Áû ÎXÆò™cl,ks«A6n¥mÙ2ºö¶÷ †éÛ]Àê˜E«x~c´³“m»aöÂy¤ÈŽÙ̶†éÝU|É:¦sÔ‰+ǘma3Çž-SLz“ {£•‚$Y"m屎ròFPJÑl5Ól4WÆ4º&†e! IøuÚ¤{«ªHEa"ßs뼫óDuÝ:jÝ ºî:ÄÿÉš¹Mئ…”Åb!þ• ˆç”~N¾$Hp`»¨ØQÚ «’Í‘•ÐTúòP z:€ÄKš Á€¤kÙ2f˜-¬:ÿ2VÅXF–å=³§woº€z?¼U{£vnîå¤×,fAz3ƒÝ=4n£/÷Ò¤£ 0½¢¢f;-jÉœsL ZVÚ­v|å1ê STÅŠ´fí¥ ð™ &(úEÒvš#‹Uò¨kb‰%%žçÅœ‡B4"¯Õðz4ü.¥RŠ ²ÑyŸˆ´?k¡–_`-ªï‹β›U‹úkT~A4\zTÕ_Á ídŸ A‚u;ëZ}cÙ`óÚ#§ýHÑ ¼ XÝ,_’epͯùÃv'BR,:e5+–Ï瑾­µÀE ]³2£#LNG¦­Nqv>ËVç –/ë!5¿ÑíÛ‰ºGEƒß…®W9*q8Ò2Ål» Û´™pÇ÷Çp…?­-Ô€£\&ü1ÒölZ­6R"…§ýʸpr‰i˜ žëN¯]wôVÉRÈ¿Lhk=¼qO¯®u%4\{í9•©­ˆvUŠf˜B„m¢ŠS“øJíõ #A‚ ¦oÔEDCUñCˆý’$HðB`Ï]Æ‚Ô OlØÍÈTœp¸[†8úøe,Hocc1|-»`%ÇNle` Ú–¾‚WtL±á‘‘.ûãบ¦îÅÌk§wÂCûý<³a’·u -©1Öß?þ’×7²]µœ<¬J‡£™« 9CLøS1¢¨ð-Où »#ÌLÏ"k4Ób´2éOUH$²Ãîš~$\Oƒ¹§[·,õ®÷U°_Îõ® ‘êz]ZŒn@zC…!ñ|?ìÊ´—‹=)Iâ!M Áƒš†ŒNç‘H ÁbWiæ/™5¸•¹zßÖÔŽ­ =,[ÔTÉ TAŠîcNá«Oã3ó¬ÿÝ<ÒïÇÞ·×1zŠMkÖ1”9ŒU‡Ï¨TªŒ>»Ž]V–Ôà&6¿¼6<ƒå× $íÖ ²V®çÒï RÔÅ:âWÛƒÎÇgÜÇQ.i™¢Ãn¯#œÓ2ñƒ ÎnêiHbù_ÙîÚ–…ÖßöJ4£uoÅîuú¢5ç&¦9†)¥Äu\´Ruáy,\4¸h $Hp <IJP‚‡ºÈæ»ÿ—ÍÓýyr·þ`]éƒ:€bßcÜþèà^yÀÞÇhнrûõÆ_uÆÏ;øÏn%÷&ºÎféJNf]x\2"K»ÕŽ) Fœ&ƒqT©ÕèÞ ÆãþžrI›-´[3°0P}PÛ¶ üI¹lä,ó6Ù`Œi˜8ž‹ªa€µó”óR÷e«Å^~oœ×)jr=oÚ«QîHi‚ ¢w¡n÷1ö aMàÏ &Í3°µÉÌ#_Å2ïi~½­ðÒæºb–j_jÙ™aÍ ÕjÅW>Î žöÊ4vZ›W¶uE¦ü)ZÌÒFš&™eRåPh )±S6ÅB@•âó²'[4ì­?ž)M„ Ãýrd|º5Ç~"¶®½ÙnSéR¿ÓºTÕ¤Êù*rð¤¶5A‚FÅ;P¿ÃO ÁŸÛn´•¥'¿‰ã:“ý¸ïî?1¼Œ–Oµ´§vìÁ{)™"äpª]™jxW#/¦B3êŒ035“´™¦Ýh'§Šh<¤Râ~i” ÈnÙ®ÊÊ>_JÙ¾Öº¡V¨ØÇïÓ‘ðFQ/9ßè¹VÚ×+¥)K¼êý¸ð‰ôS‚ žao&¢ÎúÎ tèQž¸é‡<ñBÇÄÆð§[~ôÿ³wÞqv”õþ?SNÙ¾Ùͦ' t0R½ˆREPA.   Q/b¹¶+×~-4ý!¨ÈõZ° H“N@ „P# $Ò{²É¶Ógæy~œ63gÎîv Êóáµd÷œ9Ï<3çœï¼çû| ÿø'³Uõì—_–2h1›iµ”:3õâ/~Qó· Ïë'ïåi´i±ZØêlC‹Ç) ¨Aà­lCË‹ñ~o¥!±XŒt éË# ÇóWç5¼õqÅàaXå‚}5Ç^nÓ'j(]Õx& JÕÒÒÒÚéë\¿ÃóýÑKÑH´´´´FŸH…<¡À6öXVœ‚W`[akd7ºz°[^§Ny)Rn Ó0hµƒ!°L Ë´(ä ¥ˆÒðë‚9@Q2J>Q%eÝÜ (Ю·íPIKƒ-í JuH‡Ú™¢6Ó~§dLä„/|“&Õö VùçøåWdâg¿ÀI“`Íß㚇¶"±ññK9yÏ14Ú%= 鶬}•˜Ç_îÆ©ÙOs?r!ÇÏê µ1Ž)$…t[×-çOÌãïK¶úÊShii½U<õìKØàZ˜\½Œ¯¥¥µkn¢ËNN„,ÿ’µ%L:cX†EÛÃgGeÉ`Ý”J; ƒ™Wy2^)ÍV q#Žg{xž‡#Ýú%—" µü»À²,Ç©éY`E¥GT8¬ \U­*Š .¨3ðbƒè†¸h¼ˆtòd³ÙÊO.W¨Cñ6Ííí4ÙåäÈå<¬ÆN¦ís§^ôY>:wLD?þ^õᕊ€Ûú¶¼Ü 5x^@qL­zÐiF¾COf˜ÖÝûâ4ð.Ndò ¯¹ŠkÚŠhÞcÏ¿”“f5³÷Q3ö™ÙI³kïþ×<ÔÙ2…£Î¹„÷ïÕȬàsÁƒl•€ÑʬwŸÊÉGìÔVƒÌö•,zèîzz#ùòAšmÌ>ædŽçÞL“'Cïöͬ[x'¿ýûÚb#¯!·‰3ë}qú;&ÒÞ”ÀtSl]µˆ‡o¿‹g6•üµÃ™‹–Ö¿¢·!t3¬" ¼ÿ0Æje•`$S,Ë"—Ë‘L&õ¢õ–Q>ŸÇ²¬ºÏ›¦©?·ƒœ;Ó4GåÚˆ¸%ö_ÌH0>ÑĚџë§ÏíÅÅ‹Èx/>b„:Ň·Êy9ò2OC¬qÉñ¤³i ŽØ2˜U¯|ôì!¯Û¶+ QQ^ZáËÒâ¾Á<ªÆë8þùå ±ê¿Ádå;Í"°å®“7°–ç_ÞT<ˆÖ6Z‡œŒÄIõЛñÊ·¥gS.ã’Sa·V¾Þ<ñ±³9ú¬OröÍ%w|#{þ.>e.Ó; ú¶ld‡Û@çäYì7³«îÃÙ•C«éêí#k43a¯£9ûcÇ1Õæ\´´þEU5”þ;uáË^­~,,Úâ#n…âñ8¹\Žl6ð×ÒÚUÞ½âêaŽD"Qw»D"¡?·;yîvÞ^וˆx¼ÄMM¢‘6«ƒo€”—®YÒ¯Ú8UÃ_B›¾g¼ 9/!-VB dËÞ‹Àx„¶•V¡^©~y¹Üô‡QBîzÿÁ\ ¸:‹ê¾ A©Vk-EÛî» Uk´ì¦~ãR“iïÿ×¾¿‚˜¬»û¸ê¡a¼Ô°iîšÍ‘MÆdÏzêNÆ`â¿]Ì—çB¢¥–¤…^}ê¶J0Záø£Çaz«¹û?æÁÍ’¶Ã/æ+gÎfßàeÑã ´Âqs;0Õþò*nY’fâ‰_ä 'N¬îeÌÐۀNJ۾ŗo3I67o>€³.?=;g0½Õ`½7ô\ú´­Ñú– ÝéWíMõ^ßÄ Él"i6Œøþ à©©©rq×Ë Z»ô&Mcü]v6 ƒÆÆFý¹ ;Ó4ihhÀ0F/@OÔÀhÙÛiÐb·Ðh5ãâ²Ãé&/ó«Ú=òkm^Y.y/‡@`caJ£²m=;Z»&ûß´BP(äGàO ÿÜDe…Ê´nŽ.w埿 ÷êëEH ŸñW¡!Ã.ZQr:¿±Kt ä]Yµ¼7Ô˜aˆ%ûøÇ£Ï²]Öÿ¸Ø-c×R¾mêæ¹ßÞÈ-ÏC¬)»1ű§|åjNñßaé`Œ€ô„©L42µŒç– ”ºÒ† Â0¶Áhe¯“?ÂïšI{ÌWíË‹Ñ9ô\ú´Öú×¾Œ„Ì[íZŒ),:ìâVlÔúÙ'‰Qñªhi&€é%û]eµÂËì‚8-v q3FÆÉÐíìÀÁñ¡—¨±_*„oa«hbb™6 ð\…PÁžKåíŒ:iRÕ„$…mÛH)#[…ª _òRÐ.ûçïwiÖfÚ—·>nÿcVÔCmVkð2ñFU?†tPˆ•žëKõ°uí+<ÿø<,ï¤&ªÇš;¿Çu‹fò‰ÏŸÁ^É1ì¾Gös[qTÙßÊÙÈ .¡Ûwp2ýZÑó*Joï`wÃØ&1ç4Î9vOr«yâÎÇX™ŸÄ‘§¿›VùÆ\´´þeMz­-O*K?K˜tÄ;±0µ'HKKk—JEX-h4›h¶šx¥ÎL…HΪgk…ÎXcì1=²‡”LÕlÕÒ4ì¿4X¶M¾GV0SÕ,¼GÕƒ6|žOòÃú÷ôܪAŽY Ô DmЪœ‘%û„Ø»ÿ§b‡óÊîüùÞƒøâgÒ>÷ƒ¼÷épÇJ¹e›¼ƒÙÝlÄîyž‡žÜHN‚Õ<†ÆB}Œ-ØìÄnM{óŽý;Yõbž¶æXpü!·1hÛAÒ¹n!÷?þ,ýfŠ™ï?¦¤¥1†š‹–Ö¿¢ !01pKßìðÒOY&&mf3 3NÁsFÍCª¥¥¥5Ü[é¨ÐÆ®X'I#IÎͳ=¿ WÉ;‰A+ «Uod“ÑLg¼“¸eÓ—`Ûpq}Kߪ÷YüK¢*±* `[6 …뺑v¶êq­®š«AÛŠPw&Uµ© 1P!`µT„ª¹þ+˜}ɶùwòØ—óžñ]uÊá<ù“¿³­ûi|öH.œÛÁ¾g|‘ïž’&«4&ò<ó³¯ó»¥.²û]ü.Î; CÎÿ:‡F-ïPÛHz×­g@îFëÌÓ¸âs²%“dbÒç‘Æ\´´þ5º,U®-7⌉Å6,ú ý¨@<---­7FÃõÙ'F›]¬=ÚWè£×íAáE]9&3* Ô‘:Öî¢ÝnÇU³È[y”u@–šìøÊ¨¥ÎLN¡€’²&öÓÿ ?@‹è¤Æ#Õ4´63?ª5iuFă®Y#¢¨Á?m»>w-Þ÷"))ˆí~ ÇÌŒƒ`ÉÌÏî~š[ÓÈX#¶Cï¦uìðìâ‘Ê>žÿí¹éîù¼øÊ ^[ú‹V Q(¯äÓÆ6Îò»ùåm YµÝ¥yòLöœ5X¦›µ¯¾Ææ Û‹–Ö¿¢a†aÕéüQ}´Ñh Él!ýè{--­· ”X´X-$­$®ôèsûÉ’«„@F•ß)ÎÚ11ˆa399‰„™ í¤XŸ[ƒ†aÖMZªõÜ=¦¦0±lÇqkBž‚óˆ†Ð0à† ÷÷ôÏOÔåÀsûtNW{ö©lP­Æ_¦d*[tK¥X59CÇÄ®È7ª··—ÆÆÆ‰\²½c ›´ X]¼ëâÏóÁ=cl}ðj¾w×Zä°¶ÑÒzû*NÓÖÖV´ >C¨”"µ¥ý6¥ÇëCV*Ò•ÂàKÛš˜LINaJã4\Ïeqÿ^ìÚˆˆ›´´´è¬¥¥5êêïïGå=&oJ`P-yT„GAB$˜žœÎôæÝÉ2¼2ð [½-!÷žð­ëˆºÝša4>™½ZöB X—YË’ôšš›É äò¹:^Î*³ùW½±ñDœt*…'½ˆ›9^Ôßµ€¬P¯]hØ£\äLåoZ«"s¾Þ†. ÑÄAg}…3fÈA²…–¤ù•,xz}4‡³––VÍj'å¥)Pˆ !J š­‚>§œÌê󦥥µ‹¤u6Ë€i ›ŽX¦0I¹¤d%’“šïÊ—sO Ì%ØMI&%'3ãlËne]n=…ã8•Ulé«TÇÀ Ë4±ãÅæázµá,ùz¥§ÂmäƒÛ…=Ÿ"P·É¿…¨¯Cê'ã°Ã6xHa—ìÛåb™ÀéYÃÖTí-mÅK¯¾Äü{ïæ±r{¨ál£¥¥UWm±1t;ÛÉËB™T@‹ÙLƒÙˆT}N/.:¦ZKKk Ai)=\‰ÈÄ¢Ùj¦Én¢à9ô¹}T!"u‰Ð#Á¬²llºb]tÄ;)È[ [ðžÄ´Ìbcx¥êtÑ 6<²m!ŽSðdt5¥`¾*3狨Å6©‹Z/!¾ü¸EˆŒ!œŠOÀcZÝiôIþ—“ÜÆÓ¿¿Ž§ßè6ZZZu½ f-f3ý²?tÇ.11ë$aÆÉxYúœ><åéÓ¦¥¥õæ©(¾—¡ŠìqæÃêÀ6lúò}¤œ^i}4:S€. j­V“ˆY1¶§¶Ó]ØŽƒ[ôŠ*‰‰…!R…ÇŒŽî´ ézHíQJÐR¶Zî ôˆB8ɉJÖ¿¿«¬äðW«ÑÄ>xsP`£¥¥5b8ªÀ6c4YÍØØSlegÒd6ÒkDY'À(eØkiii½¹2aVlT¨ZìfRÞi•®dŽW·õ£]y“&«…»Oztç·ã”ÂüÝå‰è-¯¾T΢íë°:èŠE‚­Ù-lu¶âá–ÖŒJ*„S€ ¯f—ík,Ç“žçÕÆ«í”'Bh*Bð°çø>/¨^µA †ðàä‚e«ÆQ--­R%)H—¤• ÍnóÙ…i˜Œ‰u`›&Y7CÛZ2ÒÒÒÒzA³Ý„‰I9—Ý6ÍV3–i“rSô¹½8•8÷àâ·ª3¦‚J™§© Si²›É8Y6ä6P_é¨r_y¦‘xn·lbX&®'Qª~”òý'|Dmãÿ;ÌŠ¾)Çþ]Æ4üD+*Å DÍêêžZ---­áK¢È»9b"Æ»£%ÄÍí±v Rnº’]¯»4iiií*µ[cˆ± 5šIšì&¤”ìÈoÇQN¨´Q4”ët ,,ºbãèˆwb è+ô²¥°¯†¸`˜f$ìùeÇbx®‹tÝH6*ÞÛÚñbô GµŠ–a@~mõ7£RY`P¸¬—ú¯/ZZZ##¥Y'ƒBÒh7Ðb´`aacÑb´a69·@F¦pñ´ýÑÒÒÚ¥j4IÉ PµZcˆ ²@Óƒ7D£¨Å8S“ÓHZ ú ý¬É¬ÁÅ Ä{ú 'BÔïÐ Ã0J™d©3“ è …ÚÖ ”†ýšµ—^ÏŠU=X­$ËuJžFG‹ªˆNZZZZ;¯N7™'fÄh³Ú0„A\Äi·Ú±L›Œ—&å qÏ­¥¥¥5úJXIÌL,bX´ÅÚ±„EÎÍÓçõ¢èžªþçg©¸ˆ31>‘ÎD'Žç²%¿™^·7ÄcþÌu‰0üKäUF+îÇ(«(&Aù=—"2®SÔügø° °F„†“œ‚Ç´Šp}'UMj Wè¯Q³Ò--­Q¯ÛOÆËbñNbX4˜M4YÍ(¥èwûÈÉœŽ_×ÒÒÚ¥R(,âÉj&a&h¶šh°x ¸äT>v)êüëG1 ÝjgBb"1+FO¡‡m…­(ÔŸˆ§P aÀ¯R†ßÄã1\ÇA©¡AëÙV?d–« >Q‘߯‰# ·50ªKöµ)ÿaŠø§ô’íìwÊG9ó]ÁzWZZZ»Ty™§¿Pô€¶Ú-4Iš­Fìé°ÃíÅQN¤Q×ÒÒÒzÓ€T+h6Y ´XM´ÚmÄ­Y`Ga[M¢OTÛ¡pÊOLÄŸ˜@[©ÌÓÚÌjúÝü‹æáåu%(…i™Of9ß²-LÛ*.×+È z?Ű–Ü«ÝàìëÙäzõUUͨUO©18!û‹œiöŸSÍLÛo?ftÄßøP"NÇîû°çø„¾8ji½A98¤¼~rnÛŒ166Ž1±1˜† ] çæJ}î5Œjiií:IŽW fÄcuÒnw“.ýnjô r–ߎMŒM`l¢ e(6e7ÑínÇÃE•öÕúŸe”J¢ÜbÁ¨âvUøµX¦…ô$ŽçDbp•{ð*aŽª¾fŨãŠUõiUó3¸¢ÚA ¥¢tà;uYÍqÉ78s/+ô„ËÒß_ÉOÁá—|ƒ9·ð回%/9ð¼/ñ‘[°(¯@ºg3k–>üŸàµ>9Ìñ=ÖÜù=®™7‚ŸJsÇ^p!“ý.Ë7çtš…–ÖTÆÍ’v34ÚÍ´Ç:°LOIzÝ^\å r—­¥¥¥õæH)EÁ+Ðl7Ñf·3l¤” ¸8ª€*ä÷+YЗXŒÇ4h2˜Ü8µ˜È”`Mf5Yð•_>* ¢Ã2Àñ?.Jó…|Á·|^Íê¯Î©6¥½¶[“–Uº.ÕÂjí~ª‘©å¿¬àŠ€–ë]ù¦ ¥;IpWÜËÏïzÅ¡ÈlK£h miÐÐЈX}?7Üñ2+IóØÌ=öT>¹×x~ñ£[YšUÃ?·½ÉýmÒÒz Jy•£ßégl¢‹„•Ä4Y7KÛƒT^]#§¥¥¥õ¦)Š”›¢)ÖHŠ—’™²d¼4..Á˜NU“¾£*]Š"ΔäTÚ­ä‡m¹ÍìpËÍ?üÝ⫘W ¨T¥Ä¦RâªêJŒÅãxJâ¹^]{†Ä2óÕB© º)i/ƒmCEzFÏ¥"jhU ZU‰Kåî(#’Ê”ÞÆª•+Év–üJmeÕªÕÅí—/å¥&_ºüHæîy;K†?~Tà¨9†}Ž;•ã™ÉÄ6ƒu‹yø¶Û˜¿6[<}F3³ÞýAÞwäÞLn1Éõ®å±ßü”ûÖÜíÔ¯sí©.‹~õŸÜ¼(Á^'|˜Ü Xn?‹ÿ|5ÿ÷tÒheÏc?ÀûŽÜ›IÍŠþ /3ÿ®¿òð«ýº÷ŒÖÛÜȃƒKŸÓ‹#V EÞ+Ðïô#ñª]JôéÒÒÒÚ…êsûé”4Æ@6Ÿ!ã¥+ãOØ1ê‰É»i »a ‹M…ͬϭ+B&Ôôy¯±™J¡¤Ä4cÊ·ì¦iaÅ,²™,2”Ìö¸û%‰!Ð5è² ?+Jô¨*Yÿõ"7°d_»Kÿ ÌJEý€R†aøPÊá#™,äq•IÌ2ßàÇ*ÆôS.傃wðÐ_nâϽ ìy܇ùà'NeûwþÀÒœÍÔ.á¢cLþqßïùÛš VK#Ùîò\%æýŒß.ìE"Éö¸@3Óö݇Žm÷ò›[W’1’¨­Hl¦|)­xî®_s×fÁøCO┋/ƾîîYëêo¹ÖÛVEØ”¤eš´›&aÇð”Kª¢PZ®÷›õÖ8#ÞÆ„©¸ë^cKN¿ïZZÿŒJ¹d½-ª€§œ,@¼+3“á{®Íje·†4Ø ôæzÙ”Û@Z¦ æøÀ6¼Ü®ŠR\²//ŠuG=×C–¼£åQ ê/ÍCmÖ;u=šÕW׃Îh˜¡Fµ²jOœª¸ˆ!\4_ø¨zç‘ÔÚ÷<¾íyUÀÜò Wï.ÖÖs}&¶e!í$m]{0÷”£ï¼Æã+óÑãïw?ü±oü'¹ñÊ?°,rÚ°/ï>¼…¥ºŽû¥PÀú?a¯¯ŸÀAÓÿÌÒUs8öè l¼ÿûüîá-Á‹`)LÕØÆ¦MÝÕçJŸ¸ì¦WxéÕµ•ÇEÃ{dîÿ>Ÿüq IDATx¬8Ö«¯m%>á?9öÝsxäW‹Èêï¹ÖÛH«}˜Sîmª×sI©jË(«QK®´ºærÖEçrâÜ=™Øjãôoaåâùüõ¦Ÿqߊü;Ææøîí_ã(k7œw¿]£×E´´þåá‘—yT‰ Ón?Ry””¾zíÁº›ÅÇF‚qɉ´%Úq¥ËæüFv8;"ÈýY=µËé²Tü^PÃ0°b6Ùt¦Ô&TÔ…Ê¢'SÔ}.èO­ª|lµ}ïÛ‚R>(Å€ j±* ¤ÁË„$˜!öF—ÌÜåwsý˪q¸…>6Iêº]­9çðÝ«Ï)MÐ%½é%î½ùVìˆ6èî«wñ“¿¾\‰!U^ší^íøF×&ÄâtœýM®:»z̆©pZ“Xã¦29ÖÃâ׺߰GÆèšÂ$;4–ìfÅŠ^NÜ{ cE¬Õ×'­·9–*Š¢… )D(Ã~$¬P„ûséµßçÌi6B)¤”˜m“™sÄ!»ž‚,ø¶j㤯cfúT`{!–eRHÏ+•zªç½ ƒ"‚Ï­^¶}-ÄúǯEÐ WZþ—㌚݈JéPîÜa)»ƒõk×2\ƒûÚ=ÜxÇRrnŽL; ƒb®‡6 #FUàÙßý”×ËÀã¹þ4jÜÈ^lµ´´¢U6Z¶°hµb(mÚ´ÇÇ`eÍRó¼Ñ½c³fÃ1Sl„·’?^~×?ßM“Ø{ß66­ð@´qÒþÌW³ý¯ŸåŒ=K“™ÞÌ//˜»ðûœþ¹{°»€Ï^t2íÖŽ•ëcËꇹþK?f~9¿ÀÚ‹KoyœKgѵœùé[Ù"³“CÎú$?õöì4HmX¼[~ÊÏî]NFæœúåOñþ}vgÒØVÌ<[^yœ¿ý};3=ŽCg´Aïž¹ý'üè7ÏÑ£opµ´FE f f²rãÜjµÑëôàÊ\*k[¯7˜IvoœN³ÕHÚɰ2µ‚¬—­Üt‡óÞý0ê/¯$¥ö¡…) ,ۢˣ”м}Wuƹˆ:–;œUïf1̵¬2Üb˜À$v%`eºY³f k7l¡{(–ŽZvob‹ÛÄä.Áö-[ØRùÙJ_V"»7²ÙkgúµùPª@¾ÉdrXû”[ײÁigúÕ±ŒNfÌhÃÙ¸žmúâ¡õ6V±ä‡I›ÙF,Ç• <° ›«¥ÜÃcTšTj€ˆNfzSš ¼Ô/x‰n ¨~žyò%eÐ~ÀÁìnFû8C9,]ð }cNäóß8Ÿ£öhÃÛ²ŠÕ[󴎵ɧ}3Wyz7®eíÚµ¬Û2@‘S8ð“×ðÃKŽgN§G÷¶,‰i‡rú—¯æËÇŽ)Z[s<ûu³§Œ!îÐï$˜°ï‰|âSá]³špsɱ3ù·“ÿ8ºYßki’ÆÄÆ3âÅXMOÑk"a%jêq†¿ƒ ‘`·ÄtZb­¤Ã¦ì¶8[pq+¯ó|¹:áê µ%¤J|¡À´m0ùB!¢æht Ñz%™Â\úÂaá&Ka¦Îþ*HÜ‘Ôè‹Jb“â-] °iÓgÎd¦ïgÆÔ1ÄH“JCûÌýÙklR/òèSÝt½çcœwü¡ì=kfÍ9„w½c7TêEyr;“Oø8ç¾÷`öš¹{î7—}'Ú ·±a£CçAïáè9³˜5g.íÖP÷" 2‹™÷ÄV&÷1ÎÊq6ó÷yKtü¨ÖÛ^1£ÅnÅŽ,à!‰ÛqZíVLa âhXoÅ_¸á/ËɈ<ï{üö®¿rÓ·.᤽Û)î]²ý©'xÙQ˜Sæ2wŠh<€Cö´Á}ù ·ÂØÉLŒ Tîy~þé ¹à¼3xß™×òœ¿Œª·’ßþÎ:ë,Îýæ}tK0ºŽçüÓvÇv–pã…§qƇOãœ>EZ´sä©Ç0ÖG¬6qÛ>À)ú/ì‘  ,üáœ|òÙ\õLe´pÈܽjã±´´´FàæYÐ`5a&®òð¤GƒÕ@ƒÑêxI ô“MŒ±±.&$'b`°-·uÙµÏhutQÓ•)ܪ3ª Ë41-'ìfg„^!"l¨ò•kRuÀ3 ªQ6¸~ëQQfk{ÜWm–(@U-~/j¨Yêa¤dM?žK?}|ðb³á~øÃûyöþGØçÃGpÚá‹YvÇj^½ýz~ž:•“æ~ˆ OL@¶‡ ‹îdѳ«É©<¯Ýy=¿HŸÆ‰G|˜Oœl㥶°èöe¼´±Ewþ‰y?']°?fnKïYÍ¢µõfå°úo7ð‹üxß{ÏãÐ&Øð2üì6^ãèo¹ÖÛÜÈCÜHÒd7ƒ¤4¶eÓd7Òf·±1¿T~t'¡zy꺋8gÞ±¼ï”xï»bö»Ïå+ï:‘£~ðI¾z÷ä–ǘ·äRö?p:G6‰¿¬ŸË~ ÷Êßyb½Ä‹-`Áº3™¶Û;¸â÷·rü£wñÇßÝÊ£+SƒÛ¬™û0;.b—þæ.õ{?ÆMdœ[ÃÓM/ã¥ÕÇ(hH&r+Ë^Ù‚|Çn4¶¶`¨—­¥¥5¶JfWºœ «&»‘« ·´æá‡;“V»•ÉÉ©4Æ›èÍî(eÕgêtÈ,ñ˜(.ˇã<Ãñ˜v,VÌ÷qÝA¡0z­[D‚cœK>‰ºNƒpqýúÕ™ü¼iU(Y© ˜–7VåÀÚú½I‡oð˜ÃÌîóCmÿzÇXzÿïwùHµ—e÷ÿËî¯ç6éeé¿béµOÉmÏñÇkžã¡ÇïûþÜ5–ìcÙ¿bÙúK­¥6K f‚+‰”ÛóÝ$Ü V-±6âfŒœLãÕØ‘V­‹ïåæÅ÷ò¿×NçÔÿº†ÏÝÉ缟=ï½¥Þ6þþÀ \rà\fýÛ±·ê`š…Ã+>Æz äþÁO/»ŒUgŸË'Æœ>ΜwÅÍŸº„ÿÝ *Mkr›JeðT~9ón}’õ¾zÖªïÅbŒi8nH98NÑÌFñI·PBPCD–]ÖÒÒzc2X¦MÞͱ-¿•ÆÒk¤Éj"nÆqe&p ˆ1&''Ñ‘l§ ÖçÖ³½°ÃWs4\¾ÊeQ5;ÃIæv"F>Ez^Ú^¿~'%`røÑ Rª®G´Ì—¥Ò¡D¯ŠÙª¥ÕzŒz×ÒÒÒz#21IÄÌ8¯À·›^·—‚SÀ2lZÍLìѵ@Æ~ÏaÌìjÀ”ÓGwO)I!Ù@ƒQ¼5ßþèÝ<Ù¯°ö>›KŽí@8/ñÀ¼õÅ…7«™6¹œ¿ýôË|ôôsÓË."9ƒ£ß9‘O‘v㘾[qyϰ, À]õ*+]…°Û±7?Ìïnú9?ÿùÏùÕŸîâö»²Uǘki½5n…!/KŸ;@ŸSì$—0“4˜ 5Üd`°[|ã’ãAÀ–ô&6ä6PPùaW/Ru€±\ßu\ÇÁcðeõ Ìî|fP}Nƒ„|m½kË>©"Ó‹P!üáœ4---­Q܈Ñd7€€~g€‚tP"CÊK161–»îB79z‹Ðæ¬S¹âëç²›©PÒÃÃÄ4BI¶<õ$¯”zW¨'¹ýͼûŒ‰45(2 îcÞ¦"1Z{žÏ ןFãÖlMÛŒßÝ•fã†í(w€ç^ÌðžÃÚxÏÿ™wHdîãŠ^ÏKîã·÷ˆïœ<‘£?÷+´JÒÒ˜ç¾/žÆwô‡DKë­¤”„´›"'³ 8&Žë7â´Ùlsº+ÛÚXŒµÇ1¾a"–aÑ›íeUfŽÌ^ă¢ô…HFÕô,f¥ ÓÄÉe‘RºÜ^¢éЇ²º¯úIGÑËj3QÜi=ol÷¶õ²Á*rRÕ@]饥52j0h°J2PèG")x9Òn P4ZM4XM£:3³”Gz–×¶¤((ÃËÒ³v1ÿßqù5 HUližÜv;˲—'î|„í¥çL£u«°ÇîÎÌãÛ^âÁŸ«îCÉmÜû£osË‚Uôz ÚÛc ¤ ÄL@õòÄ?Ån¼—Eëúð­´Ä¶­XÊ–BL[[-­·/OÖÍà*‡´—!ïå0 “f« ³7*LšÌfvkܦxéBšõÙµô¸½¥D&‘jTýÓ(?*Š5F…R—!¦ea˜Fååõ†QÙõ©¾áÿ7bÌòóJDÃkxÁ^Dh=Ôß§sºÚ§¯3´sèP~¤LòäE}eRŠŽ‰c#‡îíí¥±±Qrµ´´H§Ó´µµm‰ï])ÅŽMÝ¿}_&7M!x¥o)ÛnÐkgNûþxÒcÅÀJVeW+’ ÅêñDܤ¥¥åM;Ã0ÚŽü7}ï$ºÖÿOžÿÿX¢˜ZZÿòêïï'æš×0kÓ«Hy) û6îË„æ ¼Kv,"#34ìÞ0ÝšwÇÃcÕÀJV¥Wà(§3)¢¢FëÐ$µõI-Û&٘Ĵ,2© N¡€’ŬtA0/(€}!24&TU÷Y,¾Lx/Ǹ–_¯„@¨úÞ]ÿ¾¤¬Ëó²¢—IË:€«ïÚµ´´Þ¨ÐkÅ2m²n†~·)$J)œ™Bš„Ý@£ÝH<#'ó»h¦ ¼ëÊ?ð¹ý âm4ÐË#7ÿž—5Œji½­”qÓT¡Â@ý^ce'1æ%ÖŠÌ+&6LbbÃ$„!ØÜ¿‰Ùx¸Ñ­5#¬v©;èu´l „ /T¡†Y”3º¢iØÓY¯VhåÕ5!þQU`Œ²Ë³^Ò“*]œ„ªz,¤R¥‚U*èÙПI--­! f…"ëdÈ«\ÅÈy¸ìÈ÷¡$Íd¥­è®™h'mI»­£ÿ5¹þKüÏCÛÑùFZZo)}N®rŠÞHn?Y7),Úì1Lh˜ÀøäLÓdGvk²«ÈÊtqÅÙ·^f)QÝ]ÔÐV5JÓ²,LÓÄÉç+¶ÒßN´D‹•eþèbO¢äå¬r èûS†zå‰ÐOí"Ј¾¦>«(ýUÚY¥©D•â¢ý—–ë5‘jii š$ìžtésúK¥PжÈUŠ>§‡NÙÏÓl5Ó›ïß5•kùë?È_õ[¦¥õöR/ƒ‡Ä(Ù©¬Ì“ó²¢öØla‘°d,«S+ðp• äæˆ`@f°Þgí²·‹!=‰ëxØq#˜x®ªKìJÔ®QÞO1hæ¼ÿßá _eü7k­·ê55Ê'¸\g´<«„A©Ô¬ÒËõZZZ#!!q+NÞËÓïôùBžÉ€LáJ—¸£ÝîÐ •ZZZ»H•ÂS^Ñ«Wr1:ª@NæJ]œhŒ7RpòlL¯cca#®ò±œ~Ï¢A½:¡Yä±R"‘e[˜¶…ëºHéU*#E³(N¸X\_©èÌyᛚ ίü‹*ç®ôïП ¯„¯Ô²"ºÖU¸½”†Q--­ÒÒ¿9/GVf–F)¬›&å¤P b $Œx)UKKKëMR$N©÷|™¤òÈÈ»9b±Žë°9»™Õ¹µÒHa®!x‹²‹þß CKÄð Òu‹¬9¤e­ÿHm½RU7u°ØTQçøTã­ÝÒ×êT•€S©Ú'Ð.UiÝ_èë––Öˆøâÿ (PLX’¢º`åáÑ_è£àˆ1Z­V ah$ÕÒÒÚöJ•£/€–Wy\á‚Rôç{ÙšÛŒ« Ú ¬0«›¯äÉD;¾UéŠ7U˜–…ã8(¥0D¸g}Õ#Y~.»eOiÙk"Mÿþ¢ ÓˆiQ“¨HŒªx’Cþ%ü­”UDw—‹jz/B*ЮR--­‘§<\á ‚¡A¢XdÀíÑLˤÉjF`ê0v--­7HC Þˆbë^×ôÈS@–ž—Êà •E ›/žTՉŗìd&±¸t\¤'Zå´¯hä2y‹aÕÒžÂzJ@2z޵ÇÞ‡ÿ W ßFõåMUJUÔÆ:èþÈZZZ#)ä¼,9•ÇN&0„Qòø–óUž¬—Å&­±6,aé§¥¥õæ©ÁÊYì¦e¡,èsúPR·’ÄÍdå5 Jëëþ%qT?¤–ˆ­l­X1v´ÉAD@ÕžŠšewÞGùGE½2¼Ä^ƒ œƒàØj0_ H*Î_Ž(fÙGÅŽâÛ‰Œx~§œ£¢™#.ù§&â†îeUÖ7¢5‡s¿s­wƒŸ>Þ§K©hi½”*¤éËõ!mI<™ —ÍÊ™äeŽ´“¢ÓK£ÝHÂŒë-­]¬|>O6›¥P(ày^5ûä54M˲0Œ‘ue†Q˶í¸7Ò †A,‘ ïæÙátãÆv'a%IšÉR¬{°œRÙ¹§"`N)ULþ!èa4 Ó4‹±£¡òLÃ+a8í¨m£¶ójÔØåãÖ!­n­ê¼ÞòO@¢@I_êå àúz•˜vœ³ën^H·&O-­·½ÒN1!Àõ\â‰8±¸M!ïàI¯dt$Y/CÎÉ%h±Z€úÄiií R©ŽãÐÒÒBWW±XŒX,Vy¾P(Ð××Wìn‹ÑØØˆiš#²¥RJ<Ï#Nc‰DbÄÆ îD©’aâ œBYpÉ&7EK¬•F«Kظ*Eß… ÊYëø/F+f¡„À)*‘k‡«&…¡·PGRkù<‹`âS¥x}TÆ G`ªZ3Õb ñujªGÁþ“U~Ñ…Rwýr¶í~:=~3?¾w u­ìyìxß‘{3©YÑ¿áeæßõW~µ‰Í>çü7N~œýð>6ÊâL[¸Œ+OéçWWþ–͇|ŒOœ4›ŽF /µ…å îà÷.¥OC°–Ö[F Hy)\é „Â-°â1,[" ²R®$çåÈz9"I£Ù\»â¤¥¥5ª’R’J¥Èårtuu1f̘ –Ÿ°mÛ¶ijjbÒ¤IìØ±ƒ­[·ÒÐÐ@<ÃM!¦YôÆb1Ç!“É`Û6‰DbôO„˜†‰ˆ£8C‚'$)7Es¬…£¸Ç•…jù#îÀ„ -Ûy‚‹;©æJùñ3\•ßýìô‡zÛ|~ý»gI¾ç|ΘÓ\géÍfÚÉ—rÑqãØôð¯¹ñÆß2oóxŽ»øbN˜j+_~ gÜLf´”g“`ÆžSðV.ceA1°êIîüõO¹öêŸðëG»™x친¶_R/õii½Å4+Ó(<$ ×uqó–e³­JU¼Ê“ñR(¥HX tV¥–Ö›'Ïóèíí`Ö¬Y´··#¥DJYÛɱœØSz¾½½Y³fU€¶ ®#%Û¶I&“i éh4x°b6‚B&WÜŸOz 8)0 a'i oD9ɨv‰Ü¢ÂÞ±DOJ<ÇÇYŽ»¬…A¨ ‰P?[Þ(ý&˜V›'‰ÈÇÛÂ5V«¡¤¢R»TD0f]’6ˆ®/õÆßjEêåÛø¿ÓðïgòŽ1µx+öåØ#»Øpÿ¯øÃcKxõÕÅ<öû_ñÀ¦ ýî9$Ük/±Â›Æ>³‹óŒÍ`ï&«^z…ŒRd7.eñ+«X»v%‹çÝÅ“LÝmœNÌÒÒz+á¨G:Á‹Ÿëà9Vø[îÚã³|àì#Yõ‹g®)L²{XüZwu_²›+z9qï)Œ5±6µ”–KÎØw6 Ÿ!?}³c+¸gq?‹Î}ç”÷Àô®l7ƒ›0«mm]´´ÞJ»RåâQÑÞx€rÝbg’„§<<×#ãfȲÄl;dÒµ´´Fç†Q‘N§±,‹ &¼!ÐSJ1qâDÖ¬YC>Ÿ'Q»‘„RÛ¶Éçóär9’ÉäÈŸ!¦ÀË;(©UÒ4ŽôˆY š­æl¾ ùèRI¥Pà ³qò¤”^õªBgå…zé{.¼¿Ú}Õ.Ñ‹RÇNò!sð7"ö!}vÛ?~ø÷òoFM&õŒQì®JÍT£2¨êWÜß y[xâ–ÛY9ádÎ:z} ñ7ãØFrüðÜßìïfÉ;ÚÕÕUù{$~ÆŽ;jñžBb±¹\n4 žë”›*Õ<é!ɸH$ 3N“Ñð úyÂIâ•$'ÃÀ´-<×Å A{ °<a“¥Œê­|¥P½¨ÁFjP1ÚÑ("çUöábö•0ê^UÉÓïèjMÅ-½ì£Y˜:¬;R_eIßsæÎå.¶vUf1óžØÊ¤ã>Æ™GÍaæÌ9}ÖG9nÂfþ>o Ùò†ùe<õÂ3Oþû¹ÿà©—Š½°Ý-ÙÊî~ü¡Ìž6…I“'1&‰––Ö[HÝAïèl%v"NVeq”Ë›’Ô$Zx÷•wððcOðÄOðØ#r÷oâûŸ=ƒ;Í·îIµöâìo‡¿£Mƒ»Ì°mÝZÖlMýóÕ·ÞÅswÇqhmmñ±ÛÚÚp×uGç+`Y•¤ª¦ôÁX ×éÃU.–aÑh6ᘠ—M Ó”âÉb¥'_¨ØÈ(êë W´«ùöA²‹öʆý³þq£òîÃGJ]*T¡š£~6¾||Vp:þÉËJª`<€Ä@ŒÌ—®—gþò7šñ!ß²½Ãê¿ÝÀ/òà}ï=C›``ÃË<ð³Ûxx?ÂeÕü§ØpĉX çójé)¹þa~óçNϹè] „—'Ó¿…5Ýiu¦¥õCÒÚEQ©\6XnÁAÅÁHxêͺ<›4·µ`¾ô\ñÓd­FÆLÙŸϹ”kÞ9ÿ¼ð*¦´EykCÝJnûê%ܦçþ:¹K‘Édhnnµ¡ææfòù<–eJŽiš …/UE-#p]®-šqS¼ñX3M±&,ÇBªBÈj=…Eº², Ó´J7á%™D¹ùBˆR±|9ˆ} c§ßæF!ª!vÔ¨¥eÛ´ç*b·ï«Cª*‡¨„+|îW ßø€ªæßpó£¾wÛçsÆž‘},{àW,{`ˆïìæøÑáòlxò\÷ä´AÖÒú'±'Ê´*)qòy !p-çM­ú¤úV³dñ2/,äÉ6¿¾á4N>ôzþ=Éa~žó™Ãô ­Äœí<~ÍE\yo7ÒÏáçŠwÓÇô¼ò·\÷cn_ærØWþÌwgý… /ø_V”ê(w~àZn½¸‡+?øßó>zØ &tµ’$˶åóùãuWsëK©Š¿aΧÿÄãŸ(ðè×Oä«û–0E3Où WœûoÌ—Àí]Çü¾È7ïÝ}i³™ÏPÏc9ú²/sþ3™2®•¸ìgÃKsÛ/nä¶ÅáÎ|1ÞñÅ?óƒ9wñ‰ý‚å›Ë—nû&Ýüaþã¶mH­'ÿ€Ûþ#Ïw?ô¿€`ìq_ç÷§tÑ•tÙî?Æ >vóM·ð“|䆥x;sl÷Îë9ŽªOhêißæ>~(Zb¸}kxþÎøÑ/°M&8ü«á»3oå‚ ~ÅJYÜ~úGoæ—ï_Äÿ~üÜ7÷á¼ïF;|ø2.þà‘Ì—Df·³aåËÜuí•Üúêë‹©ÌçótttŒÚ÷ª©©‰-[¶ŒÚø–eá8ΛhÊF)¯òä½<­F+Ífsåq£ùD€¶ ÃÀNÄñ×sK¡L¢Æë(Â0)•/]EïÿXpdÿ¶*ÝZDE„ÒIâ# IDAT‡TÔø]E(÷^øÈ“ÒqaB¯zCRp« U)ZPÓÒÒÒŸƒ Uæ ,íHÈe³ô{½»t¶^.C^YÄb&ÐÎì#gÒú[ùöç?Ãå_»†?-Ú$É~—üˆo½/ÁS7~‰Ë>ýîè?”ÿøÞ§xgcŽŸZD~ÚÁÐY^`kdÿƒgãþc!ÿÈöZ€fv?`ÚWÝÂ7¿p9Ÿûúõ<&ãSßþs+¥]–ÿþóœ{î\÷T>ˆIÓÏ䫟=œÔßâS^Äßý5÷¿²½T 5Ÿ¡žoaÆÁ2vÕøÖ¯à ßø;s¹ìê«8of,´¯/?¿˜Â佨«¥øî›Óöeï‹™û̦¸µÍì}÷B,}ŽKñ[ù5pÓ·ÿ“+¾òcuÞ:ƒi¨¹ûõzŽ£z]ݱønüÆ\zѧøÖ62ãœÿâ²£›äyñéEüŸÑÊ>ûM¥ðâ ¼R³š=Ôûg¯ ®æªOìC÷?âK—–¯^·ö™Ë>ã^ˆ‰R ˲F,v4ücÛö¨-ÙC1s4§Â™çÒe)8Ê!/s4XM˜Â 0V½¤Ë41-“B>ªdÖScýÄf UDn[®`"–"0sU‰?Uköâç­|ñ«akìâTÝ߯\<†Š‡Ô¿±r#‹šjQ5)]ZZZZoÔº_ö‡Æ·AAo¦ÕôfÚ ;#k¤sêœtɇ˜‘ÿw¼˜©Ì-µêYæ?WõÀ‰æ£8ëýcyæªËøßGúPÀò«ÆóÎ?~Œcö»š§žŸÏ"÷söŽVn»»•<€Ã4yéÏ2Ð4øk,,î#³fO=»çy~ëýÙñ¼sO‹§–” ­{-+Wnˆ„L£µVÑÏ3‹žç¥åYXþjýËïPDzdxóM¯}ž'Ÿ)ž£§Ÿz õËùðYïäOß| Îizѳ¼ÌÅ8'ÉçèØ&dÒ¨}`¦õ8‹Õ Ú¯‘U÷>ÏviŠþWçá'‹cÿcû$ÞñóŠçâC ÅǶ`Am/ÁáGå³±b!­(þµìÕŸ±û±7sÒœi˜¾DúÙù,r¯àðw´ò×»{Pñ½Ù¶dÉ ‹ÉÒ9çºïûòÃ9ëôÝYwË'øæo—ã¢}–ÇíÔ§^J‰eY£êÁ`,½·BŒZ¸ßãèç&‰ÂÁ!ëfñ\IÒN7âäd–p¶Ž¿b¨iYXÉ8…lJç¤~*RìŒJL¥ò9ý%˜D]˜ 㵈𬊈cU‘–;èÃU…øð¶õúG‰2Fs¸ E0$¯kiii½_/È¿lUl#ªÞÄ›âØQ_ãžG¾Vš¨CÿÊ'ùåW¯âŽM²nésê,¦'˜ðåÛ™÷¥ bÙ’|gô-à‘ç$W1—–¿ÝG~ÿ#yGìEnz¼cˆ×FY_¹i=›U mÍë…â.¹ƒ[ž}—]ókf?t·ýån[Þ¹¤=Ô±X;1_ ËYøüþý =™l>†5YíXÈ‚W?ÿ:›ØË9ààé¼üûß=ë=<Õà%ç@¿ž§žZd·Ès±e˜çÂrî…Á?iáã¨ýô0ñèóùäÙǰϴ’…~ &Þâ¢GUõ>ɼg/çsG½“¶¿ÝËÀŒ˜“x•ÛžíEÕÒzï»9u/f&¶ñäÓ«©…êp¦‘ÔhÂèèKø /ØÃH™Ç•v’&³™”›Âà Ýp7…QìW/‹±òQ–PEd²‹5®ÄðÍ}Ð?IÈsFD¿WT| *0'Uã=öWjªéu_Çãl‰ÀIÅçz®žXÿ Tu+Liiiií¬i«@p½@]å ší:Òr^¸‰+~ú'G÷f6÷d‡Îzävøîüæ/`ü³ÛûQJñäÃÏpùŽáð¶Çèù·Ã‰¿p#ïP0aˆ×R×§¤‹‹iÔqeÔ€Ô nýÂY,8ä>ôïgò•›ÏáÃ?ÿ,—ÿfù×{,»½þùaGÇ®!•Í,œ¿’KN>œYMM¼sÎzÞð ÙÏ罇Là~ï0¦oYÀ5«¼:ãzÁsñFÞ§aA[ãŒéÿηþûtä×ñk—±CMáÔ¯}ƒ£+/îcþƒ ù/Ë‘íòÒ!‡ÐµjOo¬ùßwaš˜¸xÞÈ|7 ÃÀu]b±Ø¨|¯<Ï{Ã}퇂éÑH– æGEˆBÖËQP F#ÍV+ÝNw©¯½Q)>_æ,˲1 “B®Pü,ÕäÀ‡=£D‚ŸÙÖp{Q×=­þÎöa/ª¨æ`Ü+6¦Át'áCçRBWØ™j„Vÿ•*•*°C¥‘TKKkÔÐTùîÄ‹1JÒgˆ«õJß;¤RëYöòR^O)coÝk¬*œÎSL6>°2ÒcÕ?ÿ^žøÌ• ôiòÔUÑ«€¡^;Ô5\åÉ硱eˆ:¤*ÃúgnãºgîcÞ§ÁONûþaÏ–XHbXÇ"vf¾Æ$æÌé ¿ú56Öp¥díc²ò‚“xï™c8hdzü~C7é§Vréѧð~9›­ý”å.;]{¸Ç64µ…ŽÃ ŽŸ±ÓÅ‹üð—÷òì€#Íúþ ïkàÉ;y8õ?œtüÁ´>•Õ‚µòõ›·~5ëä™3gÆËßp©(!…BÛ‡ù|Ó½òiRÊQ^Q6$dÐ9nŽœ—Å0ÆÐh7`ç-òRÔàŸÄ’1\Ç_È?jýGÔø@"Ðå.Ú©jðQJCU‘ÂçŒô{BƒÖZP®B¨ð%\1¨›AÕDš‚MýDÜb‹ÐiÒ1¤ZZZ#m裭OTç7Hw bûãOwŸËUg“+½ÿão/nÁIv±{Ëî¹o idžæî‡z¹îã—áöÝË—”2¥‡zíd²–e+ œþÞóøÐË·±RŒ§­çq^2P­‡8éN;È`åk›ÉZã9hZ ô÷Ñ'ÕOÏ€`üAïfî”õ,X7Ä|†5_ƒ±GœÍyëä¥-0í½pþ›¸ûú§HEÁĺGyèÕ ¸øœ.ÖÝr)+=‰zâQV^t g‰uüî'¯²Sé0¯÷ØT-]z¡ñŸ^½‚uâtÞÿ±ØöÐ z½.&„“¥r/pÇß6ðó³¾ÄĦÕüù{kv &Uïß¹}Þ|ç‚+ùLöW<²F1ñˆcÙÂçwb¼xþí£±2ëYø‹¥Ìó©5fï:ûL.›ÔBÌM±ñ•'øÉwÏkÀ&øß?räçOå²÷?ÎÓ×/âX†3_…ã¶pè¹_á¼ñ6éµÏó×+¯å¦êøåºgÎnç‘G^+ƶn|ŒyË.fOóî[±“m!åë=¶ÚÏé ÇQ3þ-|ëêN>û‘ÿàg4b8¶¯å¥ þ’R.+ç>üE~ùZ»“¾MÕÏW‘ä.ãÌ ¿Á©ÍÖ/ÝŒ«$;s¶‰}}}tvvŽøÒ·RŠT*5*E÷+÷ež7â5H£p4ìy4Oy¤J*bFŒ¸™ |%% ÃDÄ,œtåÉ@jQ8Ö4¸^ E0 ¡êõ÷÷¼'Ð9)¸/Cp…±Õ™yÈŠ‡VD–’5È©"è²ì!®<>§s†šÝ×bÕˆl)á»$(ð,›<À˜ c#ß°ÞÞÞQ»«ÒÒÒúçR:¦­­­r!ò_”z6w3kCsѰ‰jäºR¢R‘Ôóa¨*•YY9>ñÿÏÞyÈUÕíÿsÎÜ™í%Ùd“M!eIitb(ÐDUPXQ^D^ *`x‘*=¡·ÐB/BBÚ&Û7Ù:³;åÞóûcÚm³»IfÀóè’;·œ{væ{Ÿó-ÏWR^^®'øÓŽÁhþ·ßGhß»ýwŒù×Yüì™ö¼-¸äij¹å_ÇóÞwÏäoËïSN3µ··S^^žwâØÙÙIww7eeeI~‘g›H$H$yå!]]]5™ÜT’ÒÍ)9%ûïI¾d`0ÂÁ¾#ÄÂäãöålìÛ"d 00 ‹D8j‹=+—&¨rPË4S“R …Rë¦Ò+Á¬÷ðýëíõCÞ¤)oÈ_¤ÜöüOÀ– ê­5ÊŠIIO¹TöüFš¡Z™zËû-@Ú·Úõõ5444òã%gGlî¨}mƒ4>ë(¤fÊ$J(b÷SÈq‘øÁ¢í!£&v 3ÄF6µua•NfΙ§3©á ®]½u i‚XZZÊ–-[(--Í[>¦eYlÙ²…ŠŠŠ!!£J)b±Ø9ŤËvIÏQ`bµ¢$Ì¡`…F!& „Ä(aHIoOŸÃ¨\ÅœVFço¡¹LžÏÍÙüzÇgÇ.<ÄÔžƒš‰gó÷•í¾²çP™± ð­,RžŠ~ÿøº§¨É]åçtuž@í544òwý§s5o7wʳ¿†Ægñ}éõœ[kÒðÁc\}ù™ö×ÛöE*aôÌCùÊÜZF +Böµ²ö½…üòwÿâ£Ø¶2 RXXHss3£GÞnò¨”¢©©‰¢¢¢!ÉïL“QÃ0†¬‚ßÙ¯R’+ã2IÄ Sd& ›D¦êK¶J H¢Ñ((Ë0w÷p‚€K|)}u)$2 ±ú¾Š#Þjx5¨îôÂs„JÝ¡S%Kì”æWŽ~Lî^MÞú‘ ÙOO…ìý:6Ê„%)Ñ+K),²×ÐÐ$ ÙïV_šòÊÈŒYSÁçtöhR9²CA@‡ì54†J)âñ8ÝÝÝlW>©Rж¶6b±ÅÅÅyïaŸ&£J)Š‹‹óîyM‡ìk›J2aú2ã!•)r%E’|Ë"v+ݱeãØÜ»™•Ëè±z(,+Š›$¢1”¥|Ȭ? ¶34Ã0ï’H$lžÕ´<ƒý¼öíQ6 18î'=ŽÝíËá*œ0óÎÜŸòSðmS–¿»ŒÈð=ÙÇÃH &ìµ'Ãz>ä£`5ñê­×qó¢úmÓ¿ÓÐФïA9ÖÓªŸÊ;–”–••ÑÝÝM]]mmmD"âñ8–eaYñxœH$B[[uuuôôôPVVFAAA^ȨeY˜¦I4%‰`š&ÅÅÅ} רˆ¤I8у© V\I"ÃJ8õ,p•5 léü—T?Õ;FÕOi¨êÇ#jåt ²µÞˆz6Àÿ/oaäDú„–'M5×bðHlZMë¤S9çè&®r9s­e»ÍÏ—™ÎØ2EWýr/üϯêÂ"ÈŒ3Å7ǽŸþø Vr„_Ä/Oèâ¶_ÞEÓ¾çrþqÓ¨*10{šYýú#Ü÷䊤𳠽+ßeYÏÞÌÞkW^µ2۩ØÌ>{Tйä>‰r Ç\òöY~W/¬Ã’ì9ÿ,æM¯¡jX)D騴ŒE àåõ½™„ç‰'þ‚ëNH°ä¶K¹õ=“ꃛ,£ö°/sÂ!³ØeX*ÚEkãz^_p;/m´ 0œóNäè}§0¦RÒ½q)Ï/XÀâº^ý¨Öø ÒP•Y1{Wû–O§ö‘jhìhRª”"PXXˆaÄãqb±­­­X–•ñÖ¥+祔™Â¥X,–·Šz)%BˆÌX†²ýh.Ò™%jØd„«_Q²§½¥aP( ‰Çâ6¯£$ë3t÷I®Þó®1H´TŽ‚¡\#ÎŽU‚CcÞi…ÍévŸÆ£Ù*}|uI³Nå"Åöw Dj¥¢”“| P Ëv‚|yH­ÖÅÜñxßûæ78mãŸù·M¬9‹ ŽÿÌQ¼»ð6 Fïw'|ûÛÿògž¨‹³vù'Ä÷™Bmù34tX@!µ»Ç\ûkc sÝká;œ·Ïž{èì(f·y§sòù'²ùÊ{Yѧ§Ægîqç¡¡^Ó&ОQ KJ3Oè`Ã0(((À²’ž;!MÇ4ytÿY·VÂñ_{Qá }%Á2¤ €e*°”O&gÙOºs<bš$¯g¨¥¬œ‹sáãŸÄvµ´7Ó²5û.)|®—¹åêþ$|•¢_Bï¥i¥{ d²:LØSPSÜ8µT嫨®ç‚¢gùn6Ìž_9ƒý‡{W7¢xs©¦þéÛ¸÷åXµj)/ßsÏ4Ö0爙}Ÿ,c9ÓRýšCµL¯ °nÙÇD”¢·aK?^G]ÝZ–¾°× Ùeâ(Ÿ<…kÞ|‡Ö’ì7-íò/`·}fQÒðomê_þ¸¯ù–¯ZÅǽƼFkÉîÌŸ•³ˆw·ÒØØHcc3}ÉÇl¿c+šÁsjhyî_Üùì{¬X½Š—o¤SeçæˆƒÊYñÈ<µd-uë?âùûŸc}Élöžlh«©ñ4ðv­=åR­KšÎÿú¢J O1Mÿ‚Á Á`P(D(ʼŽ}?_ O¡Èht E²]J'Pd’ ΖØf‡·P:r-³ç’6ïa¶šßFäRù£‰x–+ê>3¿Ó>n\4˜ •o§¥Ú›«RÜ==-¤'ó_øÐã¬B‘+×9üþ\ÁÛŠ8uÏÞÅÂ]Äü¯º›·8Þ•Õãlgé'mY=T«5k:8vúxFÊ%Ôõ¬àýÕ§ÍšFÉ›o<“i¡5<±´ ƒ³Žæ„#÷dru9ÁD„D¡ÄZï_oÕ¿Éàèý§Sºô]ÂEÓÙoV!k—Ö­èâ¦6·°™JŠûûòõ?6Y½ ãB|ôq³¯gVV§&T@Õ×~Í5_ËþQe@¯(BЭýHŸAŸ®œQgÇeíÕÐøôÓÿNxŽÜ(…Œ-OUñHPŠÖh+-V³« “]{TÙì^ÿÖ0¯¶”¯ ½w”"çûî½U¿dTø/°7>Aý\î§<¿C_ùš©ÈJgá/°M0›yõß3íÇóùêœgéôy@õÿ™èaù»+±ÎØ‹é¥Òµ×L‚«²´[!kçœsæ`½¾€;ª£›j>óÌÎu.«·¯äèÓ¾Àï³tÚì®–qï{ílMWae%»¼ö—Ò2ÐØ„”H,,Såþ˜©nÞ¹ûžÝäl_Ð×ÖmÏ$, àùþ§É¨Ò½™444>5 h‘C]S FŒbLñ2@[¸•¦D# ‘Håg*]tÖé8K;³L,³_@¢©v¡ÂÁÖ’gW6u¤¤—ÔJåâ _W@š§ž½1(Ž1ÒYµK<ÙÛ…ºçËÞâ$úÏæ­ŠlHÞÁk…=lŸÝ¦ÉõÚò÷=¼šêyó˜Q`ÛÞRG}|“w­Ê†Øåjk+‰7lJy-áÞbil ûî·Ì¬xëCzc&0F¬eño±ª®‘ÆMu´Fú_ñtðöÕrð{ð…ƒ§Ðûþk, oÇ#PňÆðTþ 46«­…V5œI“†ûÊ Xm4'JW-ØÜÜLsæ§…Î^KÛ Ïœi÷Ë!M›+†L ~Áª¡¡¡1dv+›çévÐU+S:–¢Âbz£a6t¯£+Þ BúPAá f»e˜ìt3å-2é—R¸œ„£ÊÇ›úä.1ò>Â{öÂ-»u¶Úzoò>Ö]¹ôG³ïJ¿Iv<’\5å%а™Eç»òèêAÛhTd)/¼ÚÂØyçrÆg2eÊLæ|õæÕ4ñÒ Ñ›Þ1º’7ÞïfÊñó™ø€7–% –Í ´0‰ƒŽÞiÆ3vÜX†¤Ñ·‚WÞhcô_㨱ͼþêjâÛuk­Ô7ı÷‘Ì™9•©3`ï‰Å˜ŒMõ|À+ï…™tÌÙÌ?hwj'OcÿÃöb¬L¿ÿ!/¾ÑFõ‘çröÑû1}ê®L¹/‡î?‘BýœÖøÌvIP}’鯨¾n×ÐÐÐø4.«C0±t2Ë+‰«»7±%¾™¸CI'-³Û·,yKçÓ —$}ÖîŒ@²8ʲ\>T¿+Ç+Ñ/¹¶SR•Shϧrº”Ï~Ê#ÆOÎó‚!ékP®C~9io?ô8{מb ÙÅYÿøÜÏ—Ž:›ýJ¡»~9ÏüsÏo°ÓÄë¿AýÁÇb¼¹8ÓØÚôXΩGžÌ‡"Ì(‘®f6´õÒ6ÙôêK¬>ô4&®|‰×·ÓÛ¨Â,yô~jÏú2Ç·¾VV<±ž%¯06fÙƒ7ro|>‡{.Çi]ߎ‰…²úXõðß¹©çDŽ;à¾yl!ô¶S¿äQ–¼³ž>×Ôø ! %åÁrºbí©æ v›“6’–ËØê¹††ÆÎ‚ò¥Š!QÀ¸â]V< eA{¤ƒ½ëI¨¡ä¾B‚Ê&? [h\¸ßÙ«)‡ÏS Ìh,Lj„P§? MVó Ç5Ó|ÞüPrPh/)·\yÿ¼xÇRÿ5²VÍêá0ö–mr,[ ,…B¥ä Š•ãzt/û9ê(.¾ô |rýU<¼ÞÔ¢ñ™B½ìÃ-]Ìï9ˆ Ýké2;lïe©hú¦­«ý'º—½††ÆDº—ý®M%™Zò@ª–<$BŒ(Á´a3(-(¡=ÜΚÎÕ´Ç7 #Ä(( ‰‚™°©âjì ´Û;Ø E•¥$zcÄ£1ŸÅy–èúÑ+Ý©ÇôO:½Ä\¹È¯°é§Úéq¶*¶ßÜ:¤¾¼ßµ!½MèPÙ¢ŸŒÞó‹L-´vôbÕ0óÈóùMܤɨÆç•…ÃèKŒ&î%¦b®#°+üy»5khhhìx(›=*5ÊWº ¥E¥ôöFh 7ЕÈ.°IÕ½)°Lw¦¥¿Ç1û[¶r@’NAËKíÿ•Zšv.zÃ÷Îb${–6[•Ëë)2Ù¢ÊeÉqä¡Ú1²ó”´U®ÝaÕÈ?DU÷äð}jVR€ˆuÒ¸ú n»çIÖ랥Ÿ3X(Œ€Áˆâ‘„ã=4E2‘¥«ë544>mh²Š› ÊjŠÇ2¬¨ Ó2ièn ­¯™„Jd=€ÊJîXq;§Rž$'õsêƒJ#€iZ(ÓrùB½ÞP÷Â^9H òQWrŠ~ÚÏ%S¶Zøö¦r¦0àr(×µrå’î °Çú•‡«T4ò»ì ³ìá¿°ìa=ÿ„Ô2éMôPl”²KÙ$"‰Ýf' LüèµõÑÐÐøT<ªQŒ/GUq5hélaSd}ªÏAè,eaÆãÙ¿~©›T:Dê…@eZ(—RÙøÂÕöSõÃí` Yšê§wêôtÚ[ :U…ŵ{o“ÛdúT<,[’­²—ÂÕ·5©7 ¡¡¡±}„‹†îz¢‰>Š ‹˜T^K±,Îäf G{¡Ãõ;™ˆ¦<£"Huá(F—ŒÁèìíd}Ï¢*ê]L+‘‘lr6då”Ü›„³b^ †eY$»µúuhòoÍéì½é-g·_Ç-⤶Wdˆ¯rd·z3D…k«ó\x‚ù’—"ù“KQÊí—Ð |¡5ÚB[o+VB1¢t$cK&P(rD?Ó«¡¡¡±c‘äIŠ2Æ–ìBQa1}ñ>›舷cbzˆ¡LºI“’M¶ÔHåZh —0½=@.È€D%”#\ï–mrZI·ô’›” ÏUíòOvÂhWdš‡úË:93`ñ¹¦ól¶öÑÊG¿ÊÎ~ñ^T?444ò„ˆÙKS¤®h'B¨’QŒ,íè:¢r.5444v$!öÛ« IDATÉ"Æ£²¤Ó4i 7Òi©ęeQÉB$)Ž^Fr¥#9ëÝŠÌ‘)MxéÚ3ÛÞM#Ýû)‡Ä½Äéÿ´2Ûs_e5F“¡vi£–ÎãÀ-Þï/º/Ya|•³¡”æCžDº×Ð 2‚Œ.ÍÈ‚‘«×ÀŸ„]<†‘©>õÍ=M4GšHxZéoƒ‘%ŠÎNOÙð·WA4µUŠLº—ˆú)£ÚcÞÊCíy¥YšéqrŽß]ò”U‰)ÙPljÒY‚š~O9:Ý+ÏH}/f÷ŽúUp郆†F>abÑèfc÷:"±(Å¡bÆ•M 2X‰Ì´ÌÐ%•;‚êÂÑŒ(4[Â[¨o¤×굉3e©ÃZ)e#”"ãÍ”®àµ·/’@J HĨd©ÃªpæpúE”Ü ŽÒÇøIé+á”®îóÊ—šârŸNN–«©“îÊ\¶+RYš„jhh )’–'A‚¦hm½-˜V‚ò¢ Æ—L È(¶%økBª¡¡±s … ¦´†Q@Oo„M=uô&ÂX©¶YŠèlý™!›Je4ÝÒ÷þ˜û(’ù£‰X‡lz}œÂEí7·“=à¯|e÷²g¾é…Kx ‡T6=é¬@–Æ{dŸ¤kJ”ÿÔhhhhäÑ祛¦J°)¼C]^èÒ"*B¼;†iõâî­¡¡¡±£РЉÅb4‡h‹µ¦ò@…ƒŠ «²«© !>ÕçÎJ÷¤ögr¿@ €eš6Ê—l¶,m%HéзòÑUž°}.ýPoËÏ,YuöaR>nLgI”W†ßNÚ³„UØsHÿ‹É¦ÆìÎáŒ/T;s444v )͵>«—†p=›»· Á˜â±T× SÍå´Ê‡††ÆN±SBb™&m}-4G›H÷@²< Êk£ÉvÉ2 3ÄÌ^ÇžÞ"q‡½“ÞÑ${s†ËÝ…GÒ‘IêdtöÔ™*#rçzÓ2ÝÁuáÉ/uû{û·ñÞV¦ö¿ËKïÏç;s«Œ ³gS[U°“—^9á§WqùISˆª&Í`·Ñ…úñ«ñ¹‡¶ŠzE8ÑɦÈ:ºÃ=ƒAF•Œ¦º`„-ŸTCCCcÇ£-ÒJk¤‰„Ç. o÷“:+Ëmβ’ºîÂÝ¥Þêw’`”2ÓIøÈ&9õξL2‡þhÚk*\òNiÂ*\’Pä «^éª,Y–¶sùIR)\:¤öê&‘ÀçPçë­_Z”qðw®åǤ"×9Œ™|ý÷×òÝ/V|¶¼”ù¼'ÕG{K -‘ä\&0÷¼orüŒRMH5>×ðFh1eÒëLæhE#‹Sº Á š”jhh쌅³²h×1#.iLáÈØ¶ôi›f©äO@Ȥz“­¼½ÌÇ)8¯2”ËJX®ðºr•¥¯i9œÊ\I9dôÜëÎkûÐWÙ»›‚ûµ#õvmÂEÂ%Ùj|¼I¨oÒjZ5¥ 'Ìã¼3÷gÄç(>ž—{²šxõÖë¸yQ=ºe½Æœ!›ì¢7¡4õn¤%ÜLÂ4)+*c\ÉD …è2K OHMÂff¦u§Ýv9‹…ÒžÁtW#¥¦e!Œ@FþIºªuü$î …™Hzd-¼ÒQNK*ÅBØxœpIæÛ;á¥sLíÅLÎsd}¦²z£Ò—H{i­ÊQ•”.t2È Ëë¡°×õÇš·‰M«it*çÝÄõOn –kGYÁnsçó¥C¦3¶LÑU¿œÅ ÿÃ󫺰2ãÌ_ñÍq¯ð§?>ECªVÅÁñ˺¸í—wѴ﹜Ü4ªJ ÌžfV¿þ÷=¹‚Nk JªÞºcuOÆTNÿå…Œ{ñ*®{¾-™Œ\uß¿|. ÿ¸‚û?©æ˜K~Â>˯ãê…u©5Žd≿àº,¹íRn}/®­‚Æç Ê–îîl.g¡ØÙ"@My ÃJ‡1NM`‰hÉo‘ÏÉWÿ™oí[MyDYqúºÛ¨ÿdo>·€ûŸüÍý­åŽÿÅUœ³×h*Ë‹)4,¢=›©_ý//¸¿¸^ý‡ÖÐøŒR…Ê”9ÃßÒFúÜ÷Y7'6r§¿y{¿§¨CaeYÉO¼½È)Æä.*rë–f·)uú=-,”òÍõ6x>>³þXw1•ôžÊ>lgÛ*|Z?mÓ´u1wÜýEG~ƒÓf–å8S LJ æ¢ñù;øÇ?îâ…¦ÑÌûö·9fˆ³vù'ÄGM¡¶<}…Ôî6síJÖÆÝë^ãÑ;nàºkÿÊ/¶1fî×9ivÑ F¾õÇîž¶i¶¨áF~õÕ\}õïY°\ûN5>©.n]½ô–¸£%ÚÀæÞV0¬h8Rä;Ä¢bÔH* %*&I`”×0eߣ8ëÒ¿s×õç0«¤¿ovÕ'1fD)Áx[6w“¤vŸ£9ï7㲹ôOWCãs°x¶<,ËœüúÙ38-¥PB€È•}jž+”àL…P“þ'4O9ȯ»M¨WùSá Ø;%ö-²^TgRáaxZ‘ ‡´l© é½½÷b;­ò­¯r3à|ôKQô,_ÀíφÙó+g°ÿpïƒEÏbî!ÕÔ?}÷¾ü«V-åå{nã™Ææ1“" ï“e¬1'0cZIrD¡Z¦×X·ìc"JÑÛ°‚¥¯£®n-K_XÈk …ì2qÔ ò9·åØïi[ïn¥±±‘ÆÆf:ú´ÜÆçÂw•í4œ‘x˜ÝutFº0! ¤i˜||ëùwÌQÌ=ú4¾ûççØ”Ï>—ËΙAhào,Kn8›“N:cŽ9•+_ìÀ’Ã8ðÐ=¢ì8þ´h1‹_º…³&HÀ`¯ý‡W¿Âã?= YÐH€QÏïn{˜g^|‰žz”{þñ©Ô”VCãÓd¹¤OI‘›p »Ì¼Jj™Já,ëQ.–ɰ ’ZúV¶Ï‘Mt)›ŸVØ$øýî@8è«7H‡B Wù”“þÚ‰¦´ÉË$Ýóexi)|'¯•€IîÜ€m YÔ={ wýó¿vënÞâdÊÕãlgé'mY·ÕÆš5;}<#åêzVðþj‹ÓfM£äÍ·‰NžÉ´ÐžXÚ……ÁˆYGs‘{2¹ºœ`"B¢Pb­blÛzlÿ÷¤¡¡‘k9ç6«Ê&_’4’¦JIôPß½ñIˆÐГ3+ÒÈû^ÅïÇMçúÓÆ0~î‘ÌøçG¼?¨@…¤°¬Šá% homÃìc®êX~rÅ78¨(AWý:6EK=2H4¬¤;W5}¶(ÉÝ£È)g'¡)0¥°yZݹ—IZ'Aâ–é.Tr21áÃÉ”+ôî·øw‹óKWkOe+вN ìm@•' À_*Ê놰qÃc€¡&`~ͼúöãù|uγtæô™äzŠõ°üÝ•XgìÅôÒéÚk&ÁÕ YÚ­5‡sÎ9s°^_ÀÕÑM5Ÿù fæ1²Çö{OJa)RWkh8íŽr­¶¥­4koÊdKb3²+ˆ±£ÈY”åï/§ïÔ1ÇØÁûý];Ä~—,äÕK²…ø¦GøóíËH β!GŽcL@õ½ÇMß»„ÿ´˜HÃ@W;jh|šÑÙßí¢óʇGe|¦"¡”­Þî!uz<…”„BAâ}1,Ëò\ßîU®òtû>–ËCé$¨"'QÅCºÝ÷¤|›=…O6©pQfû»ƒV¹†¦r˜·Ñ–·¸ïáÕTÏ›Ç › ¨ÕRG}|“w­Êþaåjk+‰7l¢ÕJŽ)üÑ[,Maßýö〙‚o}HcÌƈµ,~â-VÕ5Ò¸©ŽÖÈ^ ™¼Ãm9v0÷„ꦫ†Uô®|?ñ1¢1(**Òß~Ï5„Ø _Û^[J±%Ö†e™;n|RÚÖ•!¾ø‹gxuñb/^ÌâůòÔåÙBù=›V°téRV|ÒDO‚cOà²ËŽcÌ ³ Ìõ¯óúÆíÏÅ÷<À?~~.‡îR¨ûSih|*ȨÊ,¤ÉA¹,²9¥V&‡R©´‡Õ®Ö)<^U!R¡ö@+a‚§Ǒ“ä)G>ª²Uì;õJí5ôvO¦åªr抶Ûî®´Rg±Ï‰rÄÖ•m›S¸JÚ'Ó}j\tthŒ¡Eç»òèêA›±V‘¥¼ðj cçË_œÉ”)3™óÕs˜WÓÄK/|”­V®ä÷»™rü|f'>àe½( ÑÜ@ “8èèý˜6aö W…¼Ý’¹EâÅEÉB%™t PÂ/«R!¥Ä²,,å,”r—K[!Rú7§õ/´’.¿¦ôVÚª–O”_Æ©W“ÉY_/]³•þÝpNðèDÙO2dÝš¬Þ~èqö®=Å&wgýã7rst>_:êlö+…îúå<óÏ<¿!î0þë¿AýÁÇb¼¹˜U©·¬MÏsçƒåœzäÉ\ph!ÂŒéjfC[Ø{ÖÞyz3N?˜“ZÊïÙŠc·êž,ž»»KNãØy_gïbA<ÒÅ– «Øä§'¥Â,yô~jÏú2Ç·¾VV<±ž%ë#ÚS¢ñ9ó8Øó„-ä•üÝ´Õ{fÌã ¡aµpòEü丑HcõSO²"#ñëyòk×î²Öç 9ŒÂÔ¨- ˆ¶Ó¶ r 3fT!WûäšeTZ«yü†Ëxü_»rÎõÿÇùÓk™ó…qܶrí£âþËgO%ºˆa+1ÊØ5içVv²™òI 4HDãX– *Ýf<Ôm]¥bºûÕ»=­öâ&鸢÷Üʇ|ÛïÆ>+ÒE¥qHd)Ä쑵jvçHó¶\¼Ú²¹W“:\Šãz^3Òw˜”””èÏ®††áp˜ÊÊʤE±1I¥íMmL¯¯H.ÜD¶¿IÚÙíO¶½¨ÅÊÑ=P )//ÏÏ e-çÞz+ßšÀJĈYBA)§åµ¿qÉÏä“èÀÇÇºÛØV„J+©, !…E÷›×ðÍŸQm'os*˜ðôLRýœ3KJ“NƒÌu”r¤([Æ©»Ð+]X%×Î’ue÷zëYÓCLö$P™¤Üd/W,×­=¤b éÌúʤõÙ•¶RÊÕ%N¥¤ç’ögåèîüzH5444úÝCšn ÈxFe&—S ·½JµþH¾.,+ÆŠ'HôÅËëŒCP@aqŠÞp¯ƒðZR™"sŽtå¯+ðÈ>ÙBØ"QÊ#åìRoãO\„7KF“4สò„ôít]*vz'œÕVöÎöù£/“ð Lg ¦º\v aÏæ\Uæ^¾dëq)ˆdo{»r6[§P SB4÷œ;)ª/³dQ¥~ÀVÊä%ŸÞÆ¡"u\¶—¼pDÀ½´Ñ¦5’JœR¶ûS™Ó‹TÑ–Ä]{$\ú©ÎRV)|&$W¿&é˜xMI544òGI&_å|m{444v:-Uל³-¦ÈAcA¤½’Bø†ÊA„¥ nºš(ç‹ß6?qútKP縔GQTäµWÔ\6Üþ®òŒS8®«Ò9«ÿެn+tÞ¨††ÆÐwO2“³r4m…džškhhhl¤£ÝF8EÊû(„Ë~¹êsdÒ“˜öšÚs4eÐHvfRNÐdˆ]e%F„p‘_•’ÃËvU²l9=‰[+@z¼¸iX€e³¸ÒæÕ”_j6'…Ããêæ˜ÂEàÓ£0ì¼\"0ýT…Cqß™ ƒfÛ¯î±r­ÎSVHG˜JCCCcçX¬i³å_¦¥šÜÄ3cËDÖª,…‚8TÖ_)…@ KeHh®†íÂglŽD!*].ªÂ)¸”<©3]Je#ïÞ }šcïR‡lzÅC³â±f8Ž•ÈÓÓ¤?å·€ „BõQ¸9°sب™útaêÊ‘#I%‹šÒ|SF € °úbY¾¥ì?³Åïáë©´´6©À_ ßÓÑ)M$m9¨Bxï:ÍíÅZÙ”¯HŸ²‰Wù'$ɵnQ|÷júµõi|(ˆ*fv8“¿É¢Ý˜È —\Àì•ÿâ¯&þßh)x-·¿Õ‰ÎŒy'rô¾SS)éÞ¸”ç,`q]/JV°çü³˜7½†ªa¥¥cÓ2-x€—×÷&çw ñS9ý—2îÅ«¸îù¶äß©ê0¾ù\þq÷RíƒÐߘµ=ÖbƒÝ˶> ÿ{ HF‚ö±a Ç•P}øXò-¾fÅ-šîZCa´€šÀH”¹u./¥˜0AŽ¥Áj¡3ÔEE¤„ù÷”*‘v¸ }l”ßLY€¥²!íPQX_˲r.Öíâ#G¡èÿ¯a'²df$©‰ªØâÝŽª~×9ÅH®ý2DW‡ ·“ò¬ð¾--Ô5}†ßdÛsp1i†à¡Gè#H(˜àïÙžYÌΡn9'çÒÅ‚óGGÒ(¤|T-~é ŒìYÎcëM@ÑÛ°‚¥éÝ6…©ÙóR˜8 ¹d=VÉ,æìWIÝ7pÿËIÏ k"Œ9`“s]©xs©¦þéßsïËÍXÀªOZ(¨¹”¹GÌdÑmKèµßså(FYÔ¯¯#6ˆ;émü˜e«²^HQ<‹#*gÅýá©%=(`ÓýÃÙýǰ÷äY±2¹__ó',_U‡Å*VwU1í‡û2s|€¶n¼yyø4æå m•5†œ‚ž úl!@6t¯U!44v ÌÑ‚Þî0Nžž_r—r÷ÅVôÐÝÚÅ”À¤í"£vR:^Žau|…# m 2Ú?)s¶Mý›úÅJoU …$ í‹¡RÅL¹Xž|&m`Òý(û›^[á”R*º·/øÁ¯Û<ž”•ƒ»÷‘äö'³’<ÓRÞ¹2²ÄSd¨}ˆÎ†¡ÙŽöÛó±4fŸÍ¯?;ýé¡kýkÜ{ã£|Ø“Ì"1ëhN8rO&W—LDHJ¬õÉê:9¢†Qv>\»eÐ%Y=ž±Áv–~Ò–=ÆjcÍšŽ>ž‘r uö“ ‘ƒwKÆÎû>ßžµŠ[þüuý\¯&T@Õ×~Í5_³ð€"^Qä¿ÚÜÂfJ()ƒož¿`¹[{¥4†˜ŠºUô²õôƒ‘-ÑØ¨bÆÌÜ“±áxgm8;ÿÆL¾}ë8ôíË9÷¯ïº >5¶ ]¥aªö¬!P0áo Z_ª§ZT©ò÷}6a¤¨¢Ýìd˜*Í»—4ž¶{=ý{ ä,ÙW)‘R$sG•“ñ³KE Ô½ÎYq/|IãÀá~!²EA6<¯؇.°KXeÏlø³ýlÑ’•9@â­£Ú6$V-ä¯ÿYF¬`"G}ã4¦öÕ³¦±/9Q5‡sÎ9s°^_ÀÕÑM5Ÿù f»§h«>`[7Z«³•-1ɤ±5èÆî ”PZZ€ÑïÓPêæ»oàÙMÎ`c_WE™ÏÓLönƒ¯RX ¤Ì—‘hÌCKH³Õ é”!•‰ÚXú¸“;Œ¯]ÀE¥ÿæÇ?º…¥=¶¹ ÂÏþ†ê›Îà BØØ¯ýöJ¦ÜsïÚ ©¡ucZzòë½åñ‹;¹|nRaÆ"t4­cÅ;/²ðÁGX\—#V$Ê8ôÒ›¸dîh*Š‚è#¼¥Õ¼Äÿº›—ëúòúé-öe.¼ðTæÌOe0NWÓ:>|öÿøÓ¿ÞdËgØo‡X_œòUùw1¢?ÇqŸ IDATˆ·÷‹Æ©ðy&n/*)£Um&:̤°ÃÈÿð}¶I7ér½ï𜠉aÈdW&eùVÎç¢~aû,=VþÌGäð„ºH¤·–^ùÚêþRüæÈ=~i †ûd¹* Üïn×㡯†ú¢4rïÕüð¢“8óˆuüí¹ä˜ Œk¹ï‰·XQ ûhµUËY-uÔÇç0eÚhŒõõ &œ>fò®UÈõ)£+GP[[I¼a­nƒ[Å;K{ØsŸ¹øÒZ^iÙºpµÕÖHs¢”qÕ‚Íï6{Ç(ó0^ÙMW7 «‰Aó R DGúõ€cÖÐØAÄ4kwœkx ÝÏ~{P²ûÙüöòz¾ó¿OÐo¹k- .¿yu€²ÊrÝÆÅÿx‹X¨”ªñÓ9ðø³¸êÖãy슋¹æ•VlP9j4¥ßÎn|›x¨”áãfsì™gòÛk‡ñã³ÿÄÛyªÂ•‡ó?×ü„ý›ç¶?Ý̆.¨ÜeÓ¬z>ã¹%±‘&¥£+ò#íäõ©Ð³¶“2Uâ(tÉ'k,%„Kû(h/É«—t°½ë!‡—T€0’ÞÑHG/(áKüÜDV xݬմðŠâ‹Ž‘ƒT+üI¬€|n­Î(©dé­³ÇEPÕ|búÖ<οŸkaüѧqè(I¢¹&qÐÑû1mÂxÆŽËð"Û‘¥<ÿJ3£:sŽÙ—iSj™º{-Õ¡~®YÊ ¯¶0vÞ¹œñÅ™L™2“9_=‡y5M¼ôÂGÞ|LÕËò…ñ^ïNùÁ÷ùÊQ0kÚT¦N›Í´1EN´êùßh£úÈs9ûèý˜>uW¦ÎÜ—C÷ŸHá`œŸƒ¯ÕÂGK›(ÚûË|å°=™6u*S§Œ¡$çYÃô„aØ”=Ø}d!Âõší³†Ævñûï Ÿžq^#¯}¦[I.V¿ËÆÙ?â×çL§(ç^’]NºŠ?ö,‹^~‰gÞÁïÏ?‘ÒIöf~ï~^Y¼˜Å‹qåÜBµœ{Û"îùÎî(ä Ÿ=Æ‹·K­Ì>rFÌÿ ‹žúsŠc4}ó·Ütß<÷ÂSz'¿:¶ÆãO0j÷fÒfž¼þOÜûô˼þúˈ±›þß^ó!]rÌ ¬‹nÇúžsËVwÌh‚`eÁÐ|Ó•"ÞÇPÆÐxH L3ÿçÎUÀ”«ÐÇ’$e2ØžˆÅSÄP9Ú×[n2J¶RîMåCžW•Ãóêk‡UÖ›«rxWû#ãîcTι¶¥n¿ÜÞ9Äí®nUÝ,¾ñb»’gjé¾–‡¯¼˜‡S/;_»—¿¼vo?î”nÖ,º›ëåz¿§~1O9&+Ÿ¹•Ïl…צ{-¯>t¯>”kŸëd¾É¬|úvV>=ÈãzßᦋßÙºñ&Zx÷¡¿óîCƒ¹†¢{ÅBþvÅÂÌ÷ëþǬ¡1tp$ÏÛ•Æ<û›D5m‹ºãõëùÕã¯ÿs)ï¯þOnñÚùž5oòòšä«•«þɤ¹·rÜÌ ^\–I剶ձvm}öyàrÙ„ß[Ì’Ä9pÿ <ÖŽ*Ú“÷ °ìæwè.ý"_ýòHÞ¾æ"þµ¨¬¾f4_¸ï\Ÿ}-¯¿>8–µi«z°Ë8‚…a‚Fúù• /Ejƒ‡]Ás¯\B …@õ®å¡ŸßÎ’^ 8!­F…èâí%ï±lu/¬^å?Žúÿð»?NâW?ø-÷|q‹Ÿ~œG~šwê“]òDÙ÷ûfjÞ×½ÃâwWd5¿{·csœsk‘èK`”‡ì™Ç1†°«’aë044„ÔNëÒRP~¼©IÝÑ””IU~¼Hµü´ŸAdŠ€¤‹È)”\úåÚ…<•O.©]øÞ{ŠSìå§ÊA¦ctω»ÀÊHXØ})]j0˜* ­7ò¹rœÜ¿i/é¶¢•wþ†›öü'?¸ìd–]Öäz?Ę9ßàÿ}ípfL¨¢(ÖE¬8€¹4´u¾‡Î×Yô®ÅÅ@ùãOÝãö}È-¯´!w™Êä¢bj.{˜~š}¾A‹èˆb±Á/4DrÌûÿø!®>®,ù2¶ˆÿ=úâoÿ ÿúq °r³þç^y\z>×-€,}ôÿ~çP.úóL{î<ô/¯îð!w16<ù;Î[ô/f~ñhŽÿò™\}÷¹,¿í¹üöéð~‡b?Åpýq•R¨!ZZZÊÚÆ>çÛv[%”6š&ì„Q ŒPK™ë&Ac7ý¼ÊAT³äQôã¿ôórÊnKáó¾Qö“ÄJ“VùQ¸Zˆ*(¾Òž !&¦^ƒ§|û¾÷D*à§:X—€CÂ-¬_»6%CµšåK©œñOæ»7¿Ó‚€‘£;¶†.ù*¯ï{ §|å ~v뙜~Óøá+}e­T_3KŸ½ƒ¥Ï>ÀCg]à çÿ„3ŸËmb û­‚9ÌOU¼QhÇ …†Æ‹i$úLBjhX£)Lo‹Ï¼Ú*‘ñÀŠT»P‰p´Ø¶.÷B ‚E$â ,›„¦Û[‰‹¨Ù¿{ÙMéh‘ðýjæ^Ü{wK{ ²2NÂ#Êg?‡rXgéñÚº¡:͈Ó++Ü­Cí®_åbÒÂÕûTCCCcèè¨ð¡£z1œ'ÏQãüé¯sëÏfj¤zuPP»;“Ňüñÿžän2Ì&{¼ŠBIyÉÏE×â'yõû¿äècŽ¡ýo\ó2 Øø ëb§²ëø Ϭí§Ã^?ŽåØ Narø ®\ÔJçæfÞß`ÿ Ëq\ %…3CYl錞8Ž6ø7Q6½½€¿¼ý/|ïfþzê—˜uïJÞéwн¬}k ͜Ƹšæܯ‚9Ì¿ Ä;¢„†æ÷+žúHå!b›c•1$Ÿó(q¡g+é>ïÞþ–Øè(„$2ˆ÷Æ1R$_¸Ô>3äÖÇÒ%É£r]Éi9ýH­3h¯lT9—v÷ëÌP{;gáà…v_ëÀ–ÚNFÓógX)~ì7@eó•zVy¦£ÊJòš]1d¾ÿ*JJË3æ†Ãnã²/fÃñ±õkØ(NåËçCëskè0«©±Kw˜u¬\ãÔ£Îæ”å X+FSÙþ Ï/÷¹Dä-{®ƒ¿|ë"OòÓ×SEQ/sÿc_皯ýš_š·óø‡ÍÄ‹ª™T^ÏO}DØç+*'±Ç^{ –0|üt:îDæNØÂÂ+®åÙ͹8DåDfÏžMé‚1µT¤sõÆÌI{KÖ~ÒD¯1š½'”CW'îšÑÐŒ¯ò?DZì½UÔ·÷!Ë'ràé'2¡o­N ºßþþLÛ:‡yú” èY×EɤŠ!ù+%¼!L‰Y<$ç‹Á`hH씽£‘ðð'o \H™ Õ›X¤rgžG…=Àí¡ªÂÛî3×÷ŒBåànÊEŠÝîÎñ Ývúý„øUÎñ ›76 #û†ß€D¦W“=@邆†Fž |nïÀP¨{h`µðÔõ7sÔ^ßÏ„ÊÌÕÿæ7׎àGgý€?œV‚ŒGèÞ\DzúR]¼xã5ìyù…|ë·s0"›xóæ¼àGH‰ñáÂÇX3ÿ<Œ'Ὄ¼H˜÷þþC.ëüœwü¹ò¼b7³æ…yéi\„Ô¤§³ kß³ùã_ÏÆŠ‡éhZϲ·ïä²+úÆ'AGs=sÏæÚÎ3F¤£™õËñ·‹ïbÁûIÒÕwÿ‚«*.æ¼s~Á‘e’h÷fš–¿Ëªvcx-‡~í .[N(ÑCÃǯò׫îáÓùÉ5b]t?œ3~øUj* ±"›Ù¸âeþú“›x¤Ñø~ûý#mãæé#j Ðî¤ú°qyÖ"Mƃ‹Ç—Ó Z¡*óîäR(zd„ŠžÒº˜bv,Á`PAžî0ÈtfÊe …Of©}Ážklü-m1Õ gLôk›qpCÿsà*’R>êÊAZm„x‘»ª½:GÙÖÍ8V&ÒŸü¯…ÂRÉ |0®á5#}‡ÕÑÑAII‰6ú„Ãa*++“ÆÈVõª”¢£©=’!Ö@*+­;’µ;ö-”R,Ý…*”——ë ÖÐ"lÙ5BùžUTΑO¶ˆR +fÑð«(Qaå·[Sg ‡nÙCe{²uh>„ñ»ººQ‹éMåH‘,—ÈÔ¿IšHý+…È„µ¥”‚ô†{1‚¡‚±¾fÂ쇊ç&sîPynBš;º­\4Rà/•%¶Ê‘WêÞËr…ùýÒÜûØ—Î ~®çm¨´§BCC#Þé°AþTç¯khìXTt³ùµFÌh~Û{ ÒTTÃÙ‰™ZhæãÇÄb‹ì ˜Â¼‘Q|išŸÿÐË Ã@ ˆGc)âšÜAª,YË•ÑéìÒ$|÷ðÚH{!’³Àʦ‹ï+ˆ/lÐy—~‰Âg$Â5ú\öÑ6œ›½S™–ƒ²ðº ãÑÐÐМ‰wÊ’8sIõ2XCcg Ð %ÓJh|r=cOœœrg+ã.˜VJÉ¥4onct|Äv‡îЦ@2Dh³1$+X/ñ"'É’ CÄ£fÊš.oG!A%ÃOGóÝki³:an{>©BÑhÇ’&e­Eˆ@þ½£n›%\~H7­3BA°f"áÈ1õ# î{ÉEs‘BÿíöB)•s}ÐßkwW''á¶ËFùoùVì{ 2MC…‹ÁúiG)Û­éÇ‚††F¾ýNµQgø(€¤@RnT2ªhÐS¦¡±#¾™J0¬¾„h}„úGÖ`Ŭ||Ý“ÿ‘‚`Y‘'N ^œ Ñhݦð½‰E£ÑFTÆ(  ]‡)e+ìñ—§ì–‹BôFc˜f"˳„ÀL¥<­GíìK‘»(Éݦ3{uwm½³ÊXw«ØÙŸrî\ãÈÕ.U¹è°ðyïLR™¹}åšÄ[÷e@ÕÐÐÐÈÛƒO8­!ƒJ¨ gdÑÆ—Ob|E-c+Æ#…Ô¦¡±£¾›½Šak‹0z%ëo[FLJmÉÖ—ÛIJ…H’Ò‚Ñ…TŸ6`UƒtƒJ T(º=l 6" Aé–BBqÃcOòi£ dLù’;¯2I¹¤ ‚X–…ešÎbf§ˆý ܃ۻ˜UMó3oð¹ø©†ö'¨/qõ'Ëb€{ð;€\Á0oK«LË>‘i ¡¡¡‘gI¦"‰”AYHY¨œ2£‚’‚RBC·$¬„±£¿§– teÂêJº?ØÌ–7)­­¤dRÁŠFY¹ ]„„†RuòxŠWöÐùv3íÑNJ¬"Š­"‚Ê êèd ‹¸H‘½„e/†0(³J µJÀ‘Q€€0¨)ž@O¼“Þx&RyT À(DmÄÝæ?UÞ8´»˜Ü$º½Â'š”þ7MVýB깫ô½Yû¹Ú…º5LÓÙNÅÄÚÞ^Ô§=‚›~”“” W¿& üÒ ( (PByA%•Ã(),Å‘†Ä2±DŒH$BG_‰å m‡44vŒÖRŒ9¼˜X³É–†Fâ±f8Ž•ØŽ•bZîÍRX‰$c‹ât=6f#’¹§R`ƒ”vìÉJö€R2š>wÍð1(³3‘ ÜÛMw¬ƒH¢›˜,„„Œ *a¡â ‡“LŤIÇž8¾ÿöÎüR {;Ù”8墄#´ï¤½N-UwÏ{êª|rD½ÄÕ?Tïø\¹™p.Ø,›• ,ý(ÐÐÐȃ0±| %ÁR ƒÅ† H¥ÑD”h_]ñºúºÇÃDÍ>¢*†*Ó]í54v&[ hK€"òÛi)­U¬”J†¼-˱=M8¥”H)Á¡'¢iXÊ¢·¯—Â`!E¡bJ KaŽ$õÑíÆ4úHˆ8}‘ˆ£>3:™Ü’Öt÷aåÙë¡øt~²ÚI_öu²”(ëÝtWå;Å™”ÇÓꦵ¸QÖÝ2ë_óŸm\oàâȸê[³§•x»¡B{ckn¿µa}Ñ?»Nww@)Ïz@¹ˆsyƒí‡Ý\îÕÂ@×h¿\=jšÖþö¾ó9ð¾¹þ¬Þñ ÛF•óúý{ ¤„(LËÄÄtì³5¹5í7ØÏˆ`üÛòQhžÕVüÝs­‘»ÿ`>ƒù,ø*ä »¾ÍL©äÃ# "À¸Ê Ò@q3NO¤‹p¼‡žD7ݱNz½ÄÌ(1’•ªvq} ÏÛ"5kM@& ?˜ý‡žšlì\Oy¨œÒPÅ¡ŠƒEÿŸ½÷Ž·-©«}¿5óÊk§;#AQ’é‚—§<( É„‚Š`B} ô) ˆ"\EE‹Š6—Ø`C“´›Î}ÂÎ{Å™«Þ+ÍPsíÓpxŸ»úÓçì³ö\3Ô¬9kÔøý~cP«u1kƤšÞJéè%ûñ˜H$2!%žÀDÓ€Tjl{>*·¡“¤°,Ûs0„ADšIäÖ}¯¸ƒ–Õå+ÖˆÎïäŽÑçr×$ æfU®‡0ý‚Ane0ª´Ç¿ÅîXÜp{O-âÜ¿å ”å0U׿¶eR«{øã)Á6lÚN‡Ukƒv­CÍ­ã8“0[§ø~È(ì±;Þe˜ì3NÆ$S:{ŸÌ$FŽàèQ;jÿ¹€é”¦”b/Üâ ÚÁ.M«EÇ^a½¾N»Ö¦Þ¨ag…¤v9àO?8`/Üa”ôJb ‹”Xó®…÷·Îo© Ä Í̯{O—Ñš^•tf*/J 8—ø./€uØ,T¶@±tVi$©³’¨b1Z0P,¼þÄ”YU¹‹Aë Ÿ$Í‚à,ÓIÈ®¹(‰–ˆfÁBç*ë›jÜFIån𝄆¤.Oæe?ÅáhÅT`½#M5Lɯ\È)¨Bß‹ $¥ ;1 \ß7ë[U€ui’Â8Âñ,jÍñ8"ŒcÐ8•©œ}.I‰¤O’JÓ¦fÖð„G¤-H‡ÃSLô½AEŸ/Ó|ˇ#î £Ì!ëÊ*€XÎRkÒüx °0Ò¯~a¥JéòTô\y5M)$0p©ÓòÖh9]:N‡šWÃ4 &2I}†é€‘°;Øf@ɹŒKþZDÉ6卑£vÔ¾$`ˆUÈ NÇ}v‚³´Ü';§ðdƒ–Ó¢áÕY«o°š¬qYr%¾?f×ß&°|É‚ðÐù"O:©{D‚è®ZBTPz“—¡n^ Ì%øêBÎy¶%4“±‘ÁÅrβ˜¶éÏbZÉ¥˜{:‰éI.Nhâ÷:«ÎÏòe@ r_¶ccqçÎÅÈ(¨Î𹑙’U©û.ä&ŠÒúB”n¡¨¤¬uŒ¬ÎKVä"Êð'¿F)^‹Ž ΃ÙÅ]ÈÂmC{N*#ãuák¢°Ÿé=“Š$H ǵ1m“8ˆ‘RjC Ͱñ “1®Õ¡fÖq 4K‰×yöW”Öô ßYm·ÃÓF!¯Æ(œƒ¨\$P±(RÚµjõhUÚÆ2à- ^ÃY ‡}ŸÒ¾ ëžwÉŽ]×ô¨[MºõUº­lá`™J†Áq4¤íÄ>‘ 0k‰ âT©È?í‡-lÚQ;jGíRƒÑÙ»p’*‘FBlúÜ}p"5ñL–Óa¥¾NËjá9ÝV—V³IJÊ(²oíÑ v¥bÏãÊEÍNQ(DBËDêá¨.Š˜×“_]C WqÝ2b˜T‰@‰¥Ü¨ÊÌØ3\g© Ô[d\欟˜‚%¦.„˜Na*Ë]ª OÔj¨˜•w5 ×uˆ£K´AžÝ,º HòòE\Tä5”Ù2¥½õÅd^µtàd—QÎÁÔÂý Q™4‰Yb@öØÆüʲÄÞ–™AQ9„T©²Vhª’D“ hÛ³1<ƒ8Ž' ªÊŸçl_ó±§`h»-\Ó¥f5ðÓ¡6½Aiä.T ¦V=ªBËí© ÙbG„.”F-û§¸ Ç³ø ÷^¾Æì1̽)E#·vÕ×: Š©$3hRWŠÏÚ¤™XX†KÍò¨¹ Vì5n Ï­aÛQEã`È(±ìà'#“’`ù&¶ë`[6Iœ”Vòª9jGí¨µ/5$5¦yò˜¶…B2 |@1Nú ¢vƒ-ºö*]o•¦Ó¤îÕixMÖD_yžà Ú£çïãÇ#¢4 –Ñ”LPˆ§*=•HŒ 5¢ ³L•ÅjŽ DI–Jh µ”ì«ffÁš*Ó.NW©%Ö  ¥f“ Ê€,’!²lZBçýžtY–0±Ó0 ã!õÜȶ|‘F‰I-çh”Ù']^¢‘Y½äÙA£2-AåX«ÅñÄ¡ ]œ@^¶ÁÈ1v‹©ÊYcz~eß›*†tyÖmÞEWiëê[§Q©Än¸x¶K$$a\x|Œ<ÈV’ ×tiYÂ-zƒ.ó¶h/3ßäzZzEî÷ù­TËÒœå|^µ*D!tã4Ï~/¬,ô{•²Åª‚áó»a<9Êu“j"„% šö kîíÚ*ÍfÓ0HÒ4%vümö[ô£>¡ æ£/kº‘& ¦ea×]d/™¾Š…Æ!D݃xÇQ;jGíR¶t]­¥ipqdŸª¹|3¦ià:µ †·ûÅum“…Ã21 ƒ0Œ2b‘Æ £¸Ïæø u³NÇYåŠõ«h:MLËdÅ[aU®†!CÀž¿ÍN°Å("eŠ1ñ­Z‚£ô)Lꀠ8tg+àåèêì}Ê´Z‘L£âs«ŒšutnÞ 5 ”Œc$Q•Ûù¹•÷\f!0×sRª¥ Î:`™ áëòd…DeET‰)3´Œ—žk\ÜP#“qYfK«Y<….çRh!‚Ò°y:¶S·ÂʦG,ÏÌ“‹¹µúëR|lÏÆñl,Ë$‡ç1i£xHª$žéÑõºÜ=^žw¬çËõÜtõ³ªª Õ¥–¯â‹KUZ< í"«z\dýƒ—³À*ÇG•¨Ð._åsº-:N—®»ÎJmƒ¦×À´ a¢¤d0pàïÓOöˆÌ1AG!‰Jç½ih"i”`&NÍ!ð£yác9#ø¨µ£ö%¢'½æˆ8ˆižè°rõIœŽ;Æ·¿µ)f?;dûŸÏmštÌ6Ž1±dL”FôFCzªÏxÅ¢i4qv¿8KU‘y¯ 5Ï!‰Râ8ÉI4e#‰’”Q:ÄA²*¨ IDAT÷ÇDÃFbSS-ÖÜ :\ÇcÕ[cµ³ÂåáUô‚vF›ìù;r4eL«)¥‰|]˜êLuõ{ñÓ"#*tOõoêå$œ1)jR…Š÷<ôœu¼D_d³Èq”óPt¾Cª˜GY‚Š9¦Æ˜ËÊD"ezh¸³Ì&ê²ß²o"㓯Lׂ¨½½LšV”úE×3ŠrRD¹0mÞTW×å÷%+R*ÔRk°lVßrÉ hJ¢i^©çÐlÕ Ç!išj˜;E,¤L01©› <Ã#’aI™@Uö.špF>Œ^ý mPÖQÂÈÀ©ªB(4ý¨;ß²†Üá°´Š_×1™Åý™W]†¨ÔŒ$[8ÔŒ:+Þ*mo…º×À³'Õ¤J*üñ˜aÔg?ÜeˆSŸ”Ë1†šä™«ò²!Ë(+)‰Â¯Q#‰R’$!ÍDÔÒ×ìQ;jGíR7e(†÷I÷G¬?è$íû¯!Œ‹§´Œ%;oº“áNŸcbU«;…(“ fì«-,l,šFƒÓâ8{²ÇV²ƒ·æÑØs0Õ¥gLg)ŒžçÄ)q碆º™aR•£ˆEBéE=öƒmj£:-w…ÕÚ: §‰k»l¸Çè4ºœ.cÙó·éG„éhž:§~ªË¿ÙÅ!Ò˜*Geè®@G3橾j5¢eÄ‹BÍŠšªP]OáÚ”†ñ+Âæ*¾wòïÙj9Ñü§æP«yÄQB’$ÓÇâÈR%øÑˆŽÓÁ1-šV›^´$W·,óË¢ÂÍBTÀÇòý‘Y¿0Ó÷ÊÝ¡9gT[æ“!*DÅ®†ÅÁ¨Ü6…k¸Ô¬ g•ÕÚM§ëÔQ¤DqHßßgNÑAr@”ó¼P! Le`£äÓ,2eWÙÞRR‘Ä ®ç ýIØ¿`«FÀQ;jGí°š`ÿô³fqõwÞÃ1.ÒŽ'$ƒ„óz F ¸u B‚Lõáÿ¬p>ÀŠhÓµÚÜ™ž¡×Ó [˜ãKÍ’ LËÀ4­©=èÌúxYýÂT‰H)”’Ä2&–!Ãd@/<`gt޶;}×z“<üÕÆÝÚ «õuúA½ñ&£d0Ñf–Šlν‘‹iW‘ƒeXZ©Ã\åô5ÞÒQoúhä «XÊy:)•Ï.€* &µõB,a*#:EfúîÀ¬¸4¬*ᓨdê²|•"át>W5ûyY9À(MêU篴@§FPd„•n ‘JÂa€ã:xž‹L-‚q€T™ž0Œû$òŽiÓuV'²Ðgù<žrŠE1ý¤:Ä­4¢RjîK6ӌРg‰ÌÈRZseåa+îù9he浟dÇ¢DbbäôZ…VÙ(iíΞؙ9A×Yc½vœÕÆžç!L”Š(ŒèöÙç Þgœs¯§yôaR‚:±úËœ½¡yÆæ¼¬RD~€×¨OÞþDþI”tøŽ éQ;j_40j öOð.kpì§¹ØYÜ2–œÿÓ[ðB—“æ*½gÂnJ)HáJã4gå=§OgÜÀäÒ0¥Ë4±]—8IæŽKbÉR?ûÖ6ûI‹UHœô“¶FwÓtÚtk« ^kÐl4h5[œˆOÑï³çï²3ÞdLUld&×ÓÈ¡œâœ¯«þPȬ2ÄD)– yŠ% êLXÊ^öJs˜ìD[íX#rÜ‘^Õ²6UNÈ­{¦$еZ¢:¾´Š‹<,¤Z+‹Ó´8„£DË›’ƒj m%aæeùËœk‹”Õ°¨Ür>øô\q±g¥68\–9*K-¶ŽÃ•JÜšC«ÓÂû$Q2gA:$N#<³FËYÁòÏ‘”4ÛÄR8Ÿe÷©ä©LN04\¨¨„ßTrìú{¡çæõ)úP·ŽÛ6ç…lú1¥퓟llN‡5oƒõÆ ê^Ó²0…I…ôû=ö‚]¶ƒ³‰O*S Õ>ób%®D¹âÇòb%û™Bà‡!žëà¸~jÞPGí¨µ/VÞ;¬[l|ÓE£Ópüî_Ü…ˆ •Ÿÿó­¤â”qŒ;äFë­mï’ˆë 1©ª'•ÄA”«b_Ϊ‚0ŒÒk¬øîLˆ9ˆöèÇ=Î n§ë¬²æmЭ¯Ñ®·Yé¬Ònv8]Nßï±=<Ç^¸Í8Q,/ª •뵯¿»âP4UÄCU>÷ª„ ,ÝIÏÿVyD«&ôh.orŽ¢E´ä‹Ÿ¦_]L¨J –eaZÁÈ'•2u…¶$C7éç'ïbEt5PÈŽ¹ìêBáÙЪȭ:ªŽJ‰ë+ƒLUbÁÔ+±,Kª£ÉËõå:‘"µtp*¼ƒ¾žA~Ïi’âC[âx¦eGy¨0 ñ“!]w…–ÕÂ1,´˜3«J@Wi†º*éà mÍ|ùž¨h×”!¦ÔÊi¨ œeÂõ¯Ýb)?^« Cõ`7¾10ñÌ]woV½EÍ­a ‡8‰Žúô‚öƒ-Æñ˜p*I’Õ£0†³}`ÊIÄØ²¶Å:Y5ÎEb¦Ø®Mœ&ÄIzDÚQû´ô„ÀŒ¸ò|ÅÅwÓP}ôÙ!ƒí>÷6¯þ‚Àh”^nœâæø6¼ug÷â÷‰åZÂ0^j-­Jò`˜YCOUI«(©JHUÂ^°Å :ÀóÏÐu麴km\ÛcÃ9F»ÑæDpš¾À¾¿M?îÉIº„µÕ¡$ •õ‡%7fßþzec‰n{qö²jc™Ãë£*Ø)ŠŒœ’’󕀑¯y/ábzÌ0MdzQq‚š¼u*Ë% 囃sìE[øÉIºäÅ$r/NQ*ó›þdä-lËú¹ ÷·¬'2R’„1µ†‡c[‘D)©]ð5p¿ñE¼ãWïÏ»Ÿÿ4^rý¨ð[“«Ÿò*^û ¯þÞçð—ç$ˆ6ÿ÷/ý ¿ðÈ5\dìÓߺƒOèݼéOÞÊ ;©f›1ýÍÛùÔßÉŸ¾áoøÔ¾±Âã_úVž¾˜ÇþÜ{#hÞïÛxÖ³¾‹ÿëþ—ÓµcúçoãÆ÷¼–—üñ‡Ù“§ö=üÀSÇCï½lòÙ÷ý%¯ü½·ñÙaáyÈ_‘FcÎ߯g?úÏüí_½÷ßéW Îßø³ÀO?òš IÀhï,7â_xó¿‘ëî .bÏv½ÿ³£«1íû¯]d$:ù#ވ˜­‹~î]Zl«]•ïÀºxw[LTcÒ$­¤DFØRd”—…˜Ô줩švÙ9Cåâveòk¢û‰R ’ƒ¤ÇùÑÝ´œ6ëµSl47¨¹ Úíæ 'ÃÓŒöÙòϳïo«©ä”55JÞóy!rºA |Uœ×túÞz°Êk1‹q,4ŒÝ…¼ìg BRö¯Ê›Ós›“ŸMÛÄ´,Â0BJ9©ä˜)±°Q9QKóô´³Ž>/Ú«êŒOˆ£¢xU!—®Ê©Jd×_‹¢*_¶H‹/Ó?/ë1U`~«îƒ(­ÂJÇK!Ç$IŠ×p1LƒHDxÔèx«ì›Ä2ª”}¯¾z–HQ(MŸŠŠœPµ4'¹,á$rÏ„(‹kOrÜ´. »lÓZìU -î²gÔi;]6ê'Ykmà86¦i"E¯ßg¼ÃŽž~¼O"Óéžä’µ?%¹°|D^Î"Ç2KÍ2OW÷)S…?pê.) ÎR÷Òâ[®{l Û<Ácä ¼í†×qK’éÛµÿÆ=ùËq»Xë 87yöºmÌO¿žçýþñ­«—?oyòð²ÿr%?ûý×òá‘f›+·=õ¹¼â!'ø‰~5Ÿ,¤v‹î#ø™kН;ÿw¼î%È}è^qî'‡ %POzö£i¾ÿMüÖÎ#®üï<ó~’§wñ¤ß¾¿¤[Ý6æ§^Çó^ý"§ÉÚå_ÁCû^üGå/z×¾o[S,hÑ=~‚æ¿¿žŸxÕõÄN“ÕËÀ·<ùÉüÚKWxþS_Âõ© ûÐëý?¸E)Í‹#íT|÷*ÞÚ£¥ójù‹ z[¢Á¨àî7.*KšÄiå|p˜w»}n©M,S,Ó-¾+bzÑ>ƒ¸ÇÙÁmtÜ56êÇYi¬ázǽ¬%Œý1ÛÃóìç9ˆö+yPQÂpÉ}ƯÞÿ«8m\Ïç4 NõïàS7Þ89þG?À?þí;ùØ‹^Å ^ð÷ü'pí 6÷þÖçyß÷MÜï¸GrpïÕÏð+ï:—À‡^/€u‚‡=í9<ý›Â5ûÿ~ö;¯à¯ÿmˆ2Vyè÷ÿO{ÄWrÍÉN¼Ëû^öã¼ûÁ¯æ×îó¾ÿ™Ì-rr׿ãå¼ù‡÷ùåÿñB®‹îé>ˆ_~×÷#ûiÈÚÕ'.ÍU0¾sÀÍKöT4iÐ †@ã"i5U¡´èf.ûTœu€É42¡PÔ$–`™ò"?ûÖ”H”RøjLD â}ãÚ«µ‰s«Ù¢VóXŽÑ÷{ìŒ6ç2}²@<”#~y2I±ÌG¨E)UPe¾©*f³ü¬%ç€tj®¤f¾ s·¥LÕ¼€\å\@öÚ²ðfæYµ- Sû1*Uóp¿Êª¤aXÅO)­üSX”s!uéS:ƒtºÔ-¯á÷î|/ü¾ïàêë^Ï­)ˆÕGò}ïòßy?üóëz@\¦ Æ„ÊÂqªs£þÏ+êôü]œMÇ×~óÃ8ñÙë8é¦ }k´X_µÎÞÅÖ…ÞÎä,ï~Í[xÂëžÁ·>bƒ÷üÕÖr e849ùæ¯Â:ÿ/|âìá°Ì¸æ‰üÂO>Œí?úUžó¡mŒµ+iní–ŽsøõÖxÀ³^¯>ò”رËÐDœq«é¤2åÀßc8Ѩ7h7:4Â&£ñ`ªûF‰e¹À‡ÒžÑâzËù–ªbñ ¨¶(ƒS½Ni7ÔŒ5=ˆÏŽQ¥ÑD˜‹1 Ǭq¢vŠõÆIÚÍ–e“Ê„0 Øí°5<ËA¸K¬bÊ9<:F–7œ÷Yå ‘Ë9Zäù"Ò˜(q,t‡U…2C6J3Õ;ƒmÚÎ ^½†‰Í‡Ä]sþ£æÐi׈;|äMÁ}ßýuoæ7?r¯Ç?‘¯ßþ~ô½wòð'Á}Wº 2àÄÂv<§Áúæ1ÏúNî~‚·ß8:¹»c8 Ö¯|0ûáÇrúà¼úÆ2°•gÞÆoþöÕ¼ð'~?øM¼ÿÝÇÛÿúÝ|ôÌXóDÚ\ñ¸çóôûþ;¯ûѦ°ƒ¼û&nš|ý—a{#lk6J‚i ­ýM/âÞ÷¢‰éŠ(ÿVÞòÿ¼žû€} í¬Ð}®ÿø¿òé›}¸ù&ýyr½¢õp¾÷Û6¸þÚgóÇïí¡€›¯=Áù‹gðˆ¼”~xò\ oû(ïÿØgçܵðßÏÇ“çóЯëðÖwì£jâ¡6ùô~”AóóÛç=M Vþd£6ÅX—ÐUÉÂ*Kú|áÁ aÎg•±÷ÑÏùº! D*K ~UQ# ³Æ²ô³Ùw$)}ÂÐg?ܦ9l±âmp¬u’v­K«Ù¢Ýìp<<Íî`‡ÿ{Á6‰ŠH•Ì™éÃõË(61Åj)ÕíÃKÀP™Ño»@¸K^ÛXŽ) dœVÝ‹ Aà`5ߥ*VÙüW@+“$4ÔµN7QÇ¡²„ Ά´¥žCª"«žEl«Å¢Z/— ªŒ:^<†Ò@·ªÞ®ßR h 0è¸k«Ÿb£u Ïu1 ‹4I8è°;ÜdË?Ë(!e2 ñdî_µÉs„ ô¹Ê2I1… -3T/LŽUóš´ì.­zËqÀ%©’±£ ôԤ݆Ñötëzþì=ßǯ?ñQ¼î“›<áñ'ùØkÞÂM‘ǃÇÐì4swÑyø/òÎ÷þâôÄôoý¯ý…kyû99ÎÃ_È{ÞÿÂé6){Ÿú~ëù¯âºžŽæ‰¸ã]¿É3ßûÇ|åÃÅc¿íÉüÆŸÁg^÷‹üÂëod0¿m.WÛ/ó’çœâº_y.qKüypøºç¿…ßxLkòÏè½ü⣮ ¾þ•<ëw?B,L¼îe<àQOã¿þûð³?ÈË?~XúÔÛù³~#Ï~Ù¸ß?¼·¾å\wóÜ-¿^ÿŠûpM­ÎÉü5ÿôs ªÁ²%áz½úÜû ïý˜äyßðõ´ÿîï ø_ù:çF^ó¾ŒÏsŸÿ!š*‡À/UN¸TŒ‹»OKد_FM˜ÅX…‡¼9óèÂ0fSêòKi pÔR¨r„t8%Æ£çÆw³â®r¬~š•Æ*µZÞ Ö£u~Ÿ­áYvÂMÆÑ¨Ÿ–ÔYv»J3Õɹ©È䢖L¾†Sé‚Y¥¾šÛƒŠÂ—Œ‚©lÓÄr,F~@:³ù”Ãöyñr:‘Ëdåu¥úw•ハÎ,‘9'µd_y#ÒÅZË(¸,‰CÎPB©æ¹Ì–lYZ‰y¿ë¼þ!P‰-]ÿ”ÅàU)Û&ûH™Òwi»m,lìØ#ˆ¶±k6–cI’–ßà%ÆO·…«Rä%ÂTÆRT-=‚>£´ª :Ç®<+ibáš.mg•µúqÚ5§Ži˜DQÄ0Øgg|ž~¸O e8- *.P×Y;å™m½²m‘Ù,HA)H•“ S”*íÙ‘mÃÆ³jÔ­M·ƒëzØ–‹i˜(!ˆ’ß3JG„¤VÊQ›µ&Íø¾Âçco~w¼ö <ëÇöyXün^ð;HµŠï šÍ&Ù,Òø†×ð¼ßÿã8 ¿sžóû~)´ýë«ùñW|ˆÀûržò¢çóµ£[øÄ­£¥0B›|ò=oà“ïy3oyʵüþþO|ÿ3xÍÍé„ýÖò²çœâºý$/ÿî='—ßû5RÎÜy7^ÿÓüØ;§?uÀm |#Àh‹Ûo½uj©q3Ÿùø9º÷ÿŸ|Ç·<„WÞ0 •`ZØÑ-¼ù§¿—~Í£ùÎïy"?ÿGOæ»ÿà'yîŸü[É¢cÙõ¾N»ü¯??ù÷4÷ù»}kUˆ”üãõ<÷§ÁúױÿMýáÕ¼oOÁÉÏsŸ÷|yÉ(^š¾ñí¿n‘)Ž2.ÉþS‘æ-Ë/A*'WN!“„8Ž ã€QÔ'LÆ$iŒšjçB5×Á\n}\ÔÃ*£8ïèf]u¾P ©$»Á£xÀæ¸Åjm•Ú:u·A§Ù¥îÕ9d¸Çîx‹Q: –a©†¢8¿«J ª#üòób–±t@JÏ0êv” mp2Çe œ„¦e"äĵGÇЪcW•Û Ëí«¢²Å!î¢@T±J¾È"Šp¼00\¥Ù•·ºZ •»‡gBê`_/t•}ô¡ùrE¹ÒˆþÌœY:Êç) m±›R$½péexV¶¹Â™ðvR)q·æ"â˜$JQR–ÆguQ²L(f(—/‹ó/¯«<¡ß:(,JyÁù300…‰gÕhÚ-VkÇèÖÖhxM0a²?Øe¼Ëž¿…/G$2Öæ;Wv}êŠÁò‚+Uè…ù·¤Jb™F¡ÜO`bO˜oÓŵ®}Ò¯ðËéëù»7‰kǸº}†wþý§-ëîñGxÇ?ð;?ðl’Þ»ø¹'Oâ²Ï{Ðj¦Ëð¶>«;—dØÖ/k2ºcD#­_’ýÄÛv..ëªRîÞ»nm¯KͩӨ7èmHO’Æ £`À0ìÑö ä©&E\Æ÷¶,¿5ÿ$UaàYu<³Îjã+µ5ÇÃrL‚4UŒý1ƒ°Ï¾¿Ã~°‹ŸITBªfbTùhËQiö¦xÔ<ü`Þ/ÑMÍK37ƒq'š4ô>ï ù쟾Œ·=â<ýGÅ?¾àƒ¹ÑaE}«à‰Ïý^Nv<äx—»>{¿ûS0ÉKuîÅWÜ×£Õú1^õÐìLoøÁ§ñ?s!è”a¯üš§òÛ¿ûTd<âàüí|úú?á/Z"ŒOÂÁæy†|*/ýýgB1>Øäöϼ—ß{ÞŸòÖ&|ðÍoü%^ÜyÏ|ú/ñßZá`—óŸù7í§X«÷âŸôDž}º“ 9ûïÿ›ß}ñŸó¹”{v½Œø×W>—ô~”g>ö¹üú3ë0Úä–zÿòn7þí;¸å;ž‰õηó¯s=ÿ/dŸÞœm“QcßtÙEÖ",Lë—·9+¶YWÝ‹nv¡P 1áÅ••RJqnÓö8×·i;]VÜ5:n—–צæÖ¨×k“ÇH¢„q0bןXÆ"˜€SÐÆ?ËóbQòOÿ^¯&ÊÑz-mÅ(>Àï 8?¸“úel4ŽÓ©wé´Û4ê Ž‡'Ùos~p7ýè9-(½ÐÚ2k[NQÞ¸·úšÞ‰ …BƒÂ,´Læ)ëô$*X¿ÙÏ–ã`»6¡&é!Ã6Û D&üKeWÍ%SSfG³À -”Ȇʫ2Yâ£$*Ø3¨Î]P¥˜óY‚‚åþ—ƒ^.ÓE8º,ä«JûÕÙ†) ß\ÑÉöÑšwœkÚ÷ųœŸå®ÁMéb22 a™Ø–…i$I:± M% y´œy6_¹è ¥´Ë„ü˜H)J‚©ŠÅEÑAI”$ïÕü¾ZxfUï« :õ6Ží‚Œƒ1{ãmöýmqŸ0ñQÓ³PZž¼¸ÐZöbZœ×DŸnùkEUŒ…Â6®éÑt:[9…+\ÓÁ±]La'1A4f'j Q?õ Ó€XFS:—¨9(•HR¥¸ñÄ>Ê5h·ÛG€ô¨µKÔö¾lLûAkt¿jýb¢E”RÈHröµ7Ñ×èÈ‹ëÖÔ3‡ Œ!ÝýæÔ!é ¼ý~Jx~!&ŽéP3\<«AÛíÐu×h¸u<«†a¤2&L$c¶{›Œ£>‘ŒJ½jÉ»U7¯•˜ãBÈ–ljÞBzÐ6\<«FÃn³^?A§¾‚c;(!#z£vüó ¢üÔ¯4?*.$ #WXž%­™'} X¨jÆR€QY%±:ì. ÇsHÂh ªá\U¥û²¤DŸCQ ‰÷Y–]Ðs¤úJ6´£å¼HQ!Å¡à@ßO"W8U¬ˆË®°Œ Ô¢˜ŽÕõ€^íµš{,[¯ê€¡Ò†‚ÆÉ?ñ©Û-:N—s†HƒùC$¥BE)2Q8ž…iYà ‰d’"¥ªÈÊ){i騨\ –9aQÊ Îs‰zöq"Ý4¢M§ÃzýÝú u¯NªÆáˆ¾ßgw¸ÉA´K$C¤Js#EB:bÉ'úgN”ò£u×!4 ºÉÿ®áâÚ5n‹®·FÓmÑl´1ÄIL˜†ôýúAý`QÒ'H|’‰²0ZTå’£v©[gPgççhÝgå¢úÙ †%X{ØIÎýÓ4eC]–T Åžq@‡‹F«¡$!J%q2Œ Â}v-:Þ o…–Ûœh€zm,Ã&MZæ =Ÿ~°‹ŸDS°*‘\¨Ñµ¨à‚\Ÿ(ÍÁEçúl9´D‘Ê€0 FúÁ«þ«Í Úµ6­Z‡ºÝ¤Sï²ëo±3ÚÁ†D2˜Ú’–çŒlôp™Ù¹y¢±öÂÓa³âEÃ&Í*ògyYw%¦âúÉì?˱±m‹À@©Ò~DÉkHh:ÒÈU6šúm²“|izÍK}ç·È› [Yù*8{¶0f±¿Ã˜F=-ŸPpJÒ]Ñ A*é“w™Xˆ×‹œ®A99@ç UõŸ‰%¦µ9· ©ð¬:]wÇt9¶ R.Ÿ5‡´ Ò8E¦)¦iLŠžL™ÊÒÙ*ªSDÊ<(èœ7Ô!!—ª©¢›»‰mx¬xǸ²}/._¹š•vÛ¶ØíÁyî8¸•3ýÛÄ$*žVZÚjKYRDÕÕ*YçÅ'F)»º¼h0…!L,Ã¥ë®q²q—u¯âT÷ ZŽã`‚‘?fkÿjGí¨]Ê&”`åLƒ}Fœyû-œzÌ5Ψ-5³#7ÀnÙl<þJ¶ÿæÎùÛOÖïqø^ Ŧµ‹)ÍQÛ´.]” ^yΟ/«•"%%!!bȹa-yžšåÑv»¬Ô×h8mlÛfÅY¥ÙhâGc†þ€^°O?Ü%LR•VZ„ëc˜eõP¡Í­’a*ƒ}IŠJSö‚óŒâÍQ‡ÆIºµ.®ãq²{šŽ×¥7Ügk|ŽAÔ#‘aiΤ@tÎæódcí…§ÃV‰Õ,†YY}YUÁì•™ÒY³LËu&UÑiªçßD¹SÄ[C¡Í,-ð†BÌå§tðU”@5Z²¼ˆïUÅЯ`¨ô7(þ¨ YdH•ö¬À!ì¢D"Xy e »º~©bÔ^òBË3jGØÒÚìláÑqW1„‰ŸŽèG ¶2SPgd®_)E**•1¦–9=%¦r™…Õmë© EOzÖZçýÅÔSÉÀ3ët¼UN·¯ædç2ºÍ aàGcv;œéßÁæènFñ€djñ©¿S©61楾.꼯1o¥ Ï›6…À2jfƒŽ»Ê±æiNµ®`£uœNcË´ˆÓ˜ßcg°ÉÙþÝlïfî3N†-Tk’?.“$W!_-2ËfÒm;¹ªÍfpĵ£öÅ¥ xiG±ý‘3ÛÄݨ}a äLR€Õ°ð®iÝí³ía`à(ûP¶T¡˜#6­],Ë¢±ëà({ÊR^ÜÜÑCêaˆ™:ôbî0³s›¹”'Ç61-ì¤!~2b øøñ˜$IA(Û¦n7iy-Zn—†ÓÂÓÔŒ)9!+íÓ V³µªà,‹?U!¥Rb2NF Ã>a‚Û´©»MN“–ÓÁ2¬ 14ÏUÚäÂÙOs@zÏø8[vùàÕ=¤RDA”ž¾PˆÏ |•é|Á’<ż¾È„Ê>ªå\W* RqÂZ†¯ê¦g¯oqnJ0§þ³À²ÌÓå·ôR=EÛ±e ñ°ÕT‡èE ‚é³I©ÖY>Oh¤éu¢ôjê4q¬~ KX„IH/Ú͸V”™Ù,_)•"IRÒ(ÁtLÏÁ4 dšN˜ú°£bA!X¦©¦_iXb"rYëj._¹†µÎ:Žë;½-îÜ¿•³ÃÛ$¤2™l‰üx­îBãs\4(‡IÐòÒÙ´S˜8FÆ).k]Åéî5뜠^¯#„ #ö{ܽgw°5>à é“NCòóý aÈDÎeO”.j!ôÏÏBömqG€ô¨µ/>Sê옸5ÁAŸÝœ#>ˆ&O±TK Ì{ÎœŠ©ƒ‘éYx_Ö©×l°GD$­I‘=sÀ޵Ob¦4TÚ®m^0š¤Ç3!{#—V˜Á"Ÿ2gÙ–mÑB2P¥„Òg°?Þ¡E!†´° ×qi7º¬Ô×iÛ«xf)©Š¡Tm£¯lŸäÐÂáv>åzެaI«]L¬Yc9Í1õ{¡…ƒã¸mÖúM³‹PQ‘ª4åÎLJ‚Œ©nÕq!yª§/.Ñ´MLà ñÃÒ¶º\‡j‡¨¢ï¶ÒZ/êŠyD…%#Z  U:?Øœ…ú³®Db‰¤¨°×¤ÀÂQáßÀ!@yY®f‘e+²O KiÐ×d/ª÷ÉÕŠ‚©d9l]>^öo£à«žò)é„ ÷i»+4ì «É >ÐŽÞØ•…‡I)E0 1Œ×µ©·Ó0~D§•ÎNzš?†t :Î*Ç›—q¬u¯á!Rƒ±?bo°Ãæè ƒø`â„\*h&*ÎE¡×H0 ¹Ë³íä\Kµ¸Œ™üË1=šV—õúqÖ›Çq=˲P’4fo—}‡]‹@ŽPRN÷©05cRJ R• •–õ­¨¼¶£vÔŽÚ—²Y[°²U']­m¦ì=G%¤£™ÈÏÇS7G%2™¤ÆfLÏ’e–„1‘ú³l›f¯Ž}``Y— Œ^Û«S'šG> 1ï¥yÇ¥Ä ¢}†q»·Ò¶WY¯`¥¾F«Ñ¢ÓjÓj69Ñ=EÔgw¼Å^°Ã(îMíBtb¹zQwFWWSÄ U‘ßHÙòïf7<ÏÆàÇš§é6WévWh6šŸdsp–Ýñ9Æé¤¥ë4, |ŒÐbì̉ p]—(NˆÓ´4±TÉJ‰¿¥²*ãijå°æœa+¬ÈT~Â+f‹.ƒwÅ*6]½º2ÛOBQZ·¨‚x‚¨)'à ô¦Æ!Ö®Åã惣 ¢´°PZ•ª¬F¥YíÍr  ¥Rzá. »…kÖhÚ]ñVF*[EQëS ””A„'XŽ…[sq]IäÇ$©œª@”M:†qábµé[†CÍjq¢yšnc…†ÛÀ&ÁØg´ËÖè<£¨Gú¤³â,µ¸¢4ˆ”–å$sùÏ"7ºhõ:ù)ëaï.ž]gÕÙÈ5qí–°‰’€áxÀ¿ËA¸Ë8MòšdœÛ‡¬ì3Ú4IªJf³ñ¿xFõ FQ²¹8jGí¨}©š¹µ=“×iI©Eº•”r² Í|>œ†a`ÂþbѼ¶BÌço±/T©åh65‰Ø„»Œâ>›~Æ ÅZ}ƒ¦Ó¦î5Yi¯Ðj69ždLÌDzáþ\“:—~¥Èt‹cIªÌ[Êä•ÌCëÜ~çX@ɉFtš°éŸ¥÷èŒW9Ö:I«Ö¡Õhãy5Öý ¶[ì[„éˆTÉ< ½PǨ¶»¬š0MËİ ?˜¦el‘Ë}*–3Weÿyòáz•/xÌ“¨MUž5bŸ©WP%ò·v:Ö¯vf ZU¬XŠ’D9°<;¬*öá2½M°ÌT›Owœû,Sq8éÿüöJ•s-UÄÂ$/F©É‘{ñ>Çäi\«ÆŠ·ÎæøNÒé#SåÀ¤Ë V R¥2E¦Ó¶p§î`ÄéD>jÊìU- TiÑd` k¢—ê­q¬uж×F˜‚(éO}æwÇ[Di@Jºä9SKt]‹ç!BåÆƒÐ,Šj¦°q ‡šÝ S[aµ¶AÃmá8.R¦„IȾ¿ËÀïqìà'CüÔ/x.gÆ”ªˆš(…`&L>r‹¶Ì8”KØô9Û-Ž,CÚQûÿkË>ߦibšæoÿÅizWô“ŸG§ÒSR-T–RÜ6¿¿”„T&¡Ï(êqàï⻬66hz-jnƒV½C§Öa¥¾ÊÁø€}›q2 Lü‰¹ˆ’%Ì T–›• ¯š¥¯©…mø…öL"#†qŒŸL \g…OZ“µÖ1jv‹¶¿Âöè ƒhr¾’4Ï.c.‹öƒFE„‘½ ÆDÖ ””Ú‰Òݲ¬wVÿ3< ÞîyþJh¶Ïìs*c¥ P•gª|¿ŒJ£EäU†!*³[™°¼Êç `.j‰)Ý5d@¨Ô0®º…ƒµÌ¾—íE©‰Y®Ç¢ÏDî{T0±Y–6Û/³þ6æ«_‘ß§š­¾fÇL†„2 &´œu»Å8̵,õI¢¤DiR*T“„1¶g㸓ÿã0& ¢ {—³$˳½&! a³æçTç :.¶m“È„þ Ï¹Á]ìŽ7 ÒÑ\ÉB¨²rE¶òÜùÕx•$Ùü3ë¥2N\Óñ ¦I;&Ûph¹]Ö½¬46¨×k† MSÂ0`o¸ËÎèÜD„? çÇÏ1±yÌY*Pœ3¥@*LÛÄÅôÜ$†˜\«TÙg\MA©Òê,SrÔŽÚQûÏLÿƒY…L#¹O ±¸ ‰ãJò‹Ö™¼M’” $#¶ý³4œ6]wæ Úõ.õzƒF­Éñä$CÀîh“`? ¥œ»Üó׳@àTf~/þNdÀ‘ÒòÌ)±øW*czÑýh—֨˱úiŽwOQ«{Ôë—±Ú\ŸÂÞÉ(î/RabHÚQ;jÿ ©~®ÌpÆÂ¸¥Œ{TƳˆJmñ~UŒ£>ãxÈæønšf›æ)Vjk4ëMV:+tÚN¯à`´ÇÖð,½xâì—e„¥J¡[üC…¤TùZD…³$H†ÑãdÈ–†«XomP¯Õ9í\Îzsƒ­Áyê2X._)£j,Ó$ö#ä4OVLĪÀÀJÁcõ@ÈœÝ|¶RePSEW©•­òÌ&Z2}Á@.ËÍj‚šÅՄб:+Ò ·'DnϵôNUäè*´L²P <Ãä.Üt2ƒ¹€p•~,úy6. òÛ‹\þ'9“ô¢Ö’c´«µuö‚sÄi¤]@d™<¡Ê¬­È¬çÇ‘ª”p",Ë6që–LI¢’!–aÓvW9Þ8E§±JÍ­#ôl ϳçoL¥$2—ϳ<$–~:¿×$-Õän˜9†rò²s‰§üZí8­Z‹º×Ä6”TŒÆ#ü=öý]ÆÑ?gªäóÙR¥˜…Rs½ÐÜXœº°e7—Jb™v>”éÿeÏRQÙàŽµ£vÔ¾” ThÈ£‚É2ÅDSª³(Jêæ¢ˆ¹ÄB‹¹Œ&õRJ†r°?bw|–Æ`…µÆ ¯ëxl8'è´Vú=öý]ümüdL"ÒR>®Êà–’rùZ! Ø ó‘#”&ꃨGœÞÄ~°ÉñæI:õ5<×ã¤uÙÞ³"½/z6åÕ¶„0§ÂåúI¤<õêŠ6T%³(–XqŠrÞœÈD\U>Ü8ûE)OR,a³¿Sºpƒ¾h‰Ì/Mr0{r¥žÍEsöÈ!k¥T¦KeôTU™ÚͰ‘zø ˆ_@¡t\W¦?U™¦â¾ë€»,\c }†Ñ-§CËéâY ‚4È(ŠÈ?xe“ÛŠÿ½7Õ­I«ÃÖSµ‡w8ó9÷Üoꆦ &bÂЄÁÈ’,ÚÆ€@&!±d'2¶üÊdGXIˆŒ-¬$$q,b D¦‰“F`ÀŽcº 4éîï»Ó™Ï;졪žü¨=ԴϽÓýaôúè{ïÞ½kµj=ëY Ú 1PJ#Ï3dE†l&#Q˜ŽËSœ-_Ãr¶" j7¸Y]áÅêÜÕרÝ])®EòŸ^N\>À8Mm‘Âû—‘D!gXû8žŸáx~ŠE¹‡<ËШ÷›;¬*Û­yß\£Ö5L§ïÁ9[/û:îñ¾¢NïÔÉfàŸh ìêò’ô`ôoƒ×ÿEP_Æçàâñ¸!¢]ÇýnìÆn¼;h”»Q܇ῗ !­m!N~ÀA¸˜ ¦Bp¤ICë *½Åmsƒ›ê9ç§8šY`:/g˜—¯ã`~Œ³ú1.×/pW_a«6ht"^oø8š¶lŽ%1 Á·Mdê Ào7Øê5šm…us‡“êg‹sì/Žú’} GÃŒøéÔž8‰‡ˆÏr´­‚6:‚)ýOÙò—ÒÇ!™zî¦C@kJf–J w!# G˜Ìž©w ÿqÀÒ Žhínuõ€€pÐçQÐ9În£‡k¬f#f8ÌÐâøoÊî»Ù~Õ¹Dô„ö·ËsÊœ¶õ 0à<Œì‡Ë÷YÁÌ(²ûÅ VíÝP–`"ˆ ¼ÍîCÝ™G»:ŽJ Æ ­[èZ¡œÍðÆá{ñhùÙ>dFhê×wWxr÷1\TÏÁh'ï¿aŽFÑ¥–É¿ÌnèMë0òÔ§‹ H‘ã¨<Å£Åë8Ý?GQ b(¥p¿¹Ãó›'¸¬ŸcÝÞB±>À—Gt.´Ô3žþsê¾ÄúüåQk<>ƒÃ&Ã0˜ ²\Â4‰ &ïf íÃ:Ͽ"›,síÆnìÆn|*ÇhŸ7êD]|#‚uQäL«ì¦œ¨{OÇtd¿ÖrŠ[’ŸàáãÌ-îÛ[Ü··x±z{ù1Ηoâxïå¬Äb>ÇÙÁ9nî¯qµ¾ÄóÍS¬Õ ¼pS¶sV IDATfg§ ÑÉ]×û7~OÄÂNyÃõÂö~…«õs-¨@ZJÑ¿"ær“Ùcœï½ýå¾¢†P7 ®×—¸\?Å‹íSV0MGê³$Ò>µñ‹™'cf“¬op$ zï™bØ„®"›c^ìaQ ,JüCñ|Ò¥`7vc7vãSI’ øU¬©w86¤®†¸%j&~`mœÂÙ⺮q]?Çün‰G‹7p¶ŽƒùŽq°ˆ×ë7qy÷›'¸mnPóvR ›òŒâÈ”©ÀÃ^Ó•ÙàíGã’}8<EM:&NŠy‰j½4'XMžfŒ@8¼t®§â(—5]hWt3P&E¢4ø ‡‰L1´îdKçç§rÛÝàGò«%?{6ôÒ|È(Hk8# NéØÔžùx.꯳ÿ™&`Σ‡%`2]úžÀ£F£ÕÝòˆ‰‡ ~èµÞâj{‰ƒò³|½â7Íu²ÁFÀ—yPÀ¸÷²æÊ=7!ò2ƒ(žm>†Ëû§ÈdFo¡D.:©JgñÄ $Y†çÁb`n]íƒs’2,ó/Îq¼8ÁÞ|¹,`Ø`µ¾ÇÕú×ÛçØ´+Ôª‚âÆo4#ÿ>M&Œu×UL¼RFéÊã3ŒÒÖ70±Ydç¼sÊ!²¥\`YZ=TQ !AB@3÷îQ»±»±Ÿ¶!)Ãk‹·°Uk´º†1­çúáTd}R³ïíVÂõùeäIŠñ¸n÷1£•ºÇ;«âº~ŽýâÇ LËr†×NßÀñá)î7·¸Z_া@­6ÐÐq>•LI“k.¦bxP™ de!EHyÒŸ¢EŠ“8¸ÿ¿<Ïmj‹20Qqœ“]û©TŸCE0–)àuRSL'§ì¤üIs>3ÐeÂW1-Çð˱¾n*ê2Ír m¶üZ3q¢‰½Že ÝþéxN€x÷A°Oå+E2@úTRròMIPÆÐÜàº~Ž·ÌûÉ{Å Q¢1uë š°Í ýXÉWH3)„}XŠ $ڦƺ^;þ¤ì}OVä6’Thm¬lÅôe¬8¢oùì­~í÷d"C! Ìó=”'8Yœc9ÛG‘åP¬p¿½ÅÝö×Õ Ün¯Ñ˜j°ÅÂÄŽ—ÜçŠGùM ‚š04x¸À4¹ígÃÐÆ /s ´RB #‰L(ò³l{³C”Å ™,ÀP¦E­lê6õzfv«ãnìÆn|Z‡ ׎ÞB«[4õÛfVWPº…6­5¶w¬èõ£~ѰQZSÊÞ›9x×bÚˆë¶5¬¯ LU}‹›í%Žçp¼<ÅÞü³|†ò`†ƒÙîª3\¬ŸaÕÜ¢Ö[hÓt¸a C ÛoÒî\4àšB€d·fæ™!mÒÁ¨}ÒpÆɃ”˜—%¶›¬Md×ãBP3$°L'…­S,Ïnw~e1ÅÞúkW"/·5®è½(£À­éÁŒ) i*Ôÿ¤T4ggEÓ¬ýÇçf&¥™Öhv>Tvˆ<Í¢?¹àšÑ˜ wÍ ŽÊ,²=,‹´Õ‹ñŽ6 Âs;àÄÆÃÝ…XŠe‰,—PµBµ®`´~¨àÆ0´VP­‚y‘!›çÈ@Pu]+hÃ鹤Ô|ÄT÷˜,°Wà|ù&N±¿8 ´Æf»Æ³Û§x¾}U{m³ã / ‘?M -ñôÉ”æÚ©à,z†t9ës"‰Œfrócì‡Ø[BJ  CUÝ ®¶¸©npS_bÕÜcÛ®`ë¶’»±»±ŸŠÁȲ ór¹8‚`ºª°­×XÕ7¸onÁ¦F„´Q¨Ì¾ö? X’2'·É™F×·Ê7´ Ñ•á¿ä 4Vêëû;<_‡Å)ΖoâxïóÅËå§{ç¸]_ábó —ÛwÐè†5RF©…%UÅÈÌ6‹"ƒ1 UY›EfmŸ^ dM):E!`ˆÑ4MBݨø$Œ[Ò>X!l4ää±Nê0õoœ¸à¢5&ÑíŸ:æÔç„Þœn>ÇüòkB¯øYŸÜu~•¯‘×dö*%‡Ií2áaͰ;Ó¸¬ž`Yì£Ì8-ã¦zí§6S÷š„¬Ì‘ÏsèVcs»…é6Y“R–þ×ÍÖ€ê2“Èæ9òY£ TÝBµ*²ÕJ3–~DŽýâ¯-߃ãýSÌf3ÈLB·Œ›û+\¬žàbûªaX;/*Š¥¦ÊBô’ hÚXŒÁ‰-\èUgh¶²ž\ÌQÊ9Šcœìa1_@fˆc€¦mp»¾ÅÍö7Õ%VÚ6¬iÖÐŽöu7vc7vãÓ9”QøøÕGqá`v€e¹‡½å>–û8×Áš±Þژ庲v2ȃ^a½M© ~ †λºÿùÖÔ¸¬žâª~޽•5®?Û;Ç|>ÇÙÉ9ŽNðfõ™xrûq\mža¥î;2‘“½*SºS!y‘C9(#´B{_ÁhÝUNíoÉ’4¯ÇP…q‘”Þœ¿yY"“%м@&rhÖ¨ê ›zƒÛêwÍ5¶j‹JUhtM¦{Žù“Ù?ïÆnìÆnü6ò£ŒëÍ3Ü‹\­KÌó=ÎqPbQ,0/ç˜%Ž÷Ñêšîëk\·—¨x‹ÖTÝ»ÙO{µÖ¿NS9P11@^>½H  ÐÃȵ>dhkÜ×רõÛ·q4{„Óå9æåûË”åûqº9ÇÍʲ¦•º‡B›¬£’ãE/¤´-a%[mÝÂl Œ60Az'€4¶“&]_×`Í"ÏÁÌhÚ6´"â{(ÉȤJ÷iSušÌá_ÄT:Â="J‚¼TÓKÊT><‚±›Ì—ÐK˜Þ0õÇËì~bÛ’´ïwšb°Èƒü±‹,%ÕÙZÓóÁ Gÿo*¦40¼0û´ßÂ4fƒûúó|\8š¡Ý¼ƒÖ˜Q³3€söì®Ü†%’Y&!r£ T­ •†ƒ‡‘Òô¤äã†Ik h ‚Öd_d¹, ”kÖ ã4wå(³ó|'ósœ,aVÌ!…@£k+@ß\àjó[µQô¦FP—‚mÑÃ)Y£Ç(?°Ç÷›Ù2ä²@™Í1Ï–8˜ã`qŒýžÕâêjp»¹ÆªºÇÝö÷õ5¶f‹ÖÔÐ`6Ѓvé¡íónìÆnìÆ§%ÕF£Åëö«ê×ù‡ÅÇXä ,ŠÅažr„ÃìVÕ5î›´ªF­*´¦³ñ©Ã5_ṖR ø#¤õÆ5oº©Ü@¡V-jµÁº¹Çmu‰“Ù9÷N°œ/q¼w‚ýÙ!Ž¶Ç¸X=ï¼L×hM=âAv-¶YIJi×ÀVA5z¨2¦Y^5¤ ëDKQ¼¨BHÈ\ ©ZÏ`>f3Óó¸!?bðü 2&]¾¡ÅB9ÜÆž±YÇ•ìúÜœHîJ‚ GÇ÷2ê ‰`Lòo"Ðx¾2¸ îÜ÷ßoà7]|{¦‘¦OY8¹z2y…Cp™-œHŒxxôÀ±s ¦l&B0{Y?ÅÁüól‰Óùk¸Ù^@¡MJäpt 5d3[N0lPmj ÕA¨‚_ø~ þ9¤ª}âÚ(h¥ ÈŠ¹óY€µ® ´Ò öòc<Þ{§{çX,`0š¦ÆÍêOî߯õæ9³öÓC×:ù7á4ã…ÕŽÐRÄ$¶ œ(1ˆÆ î {n”aYœ †üËŲL0F£i+\Þ_ârõ÷Í-Ví}×Õi<¯_"{½4|Mõ’îÆnìÆ»9ìº`EjÙ@Õ[Ü×Wx±*±Wâ¨<ÁéòG‡GÈd޳ƒ3<:|ÝjÜonq½¹ÀõöÖê‚Ù¯þ /â¸ÝiÚ¶’’Œh_¸J)JÁâ e*\Wn«KÌVû8?x ö_Dz\âÑñ9Ž–§¸[ßâbý Ï7oC™$¢Có¯nZ4UÝê¨óej>³˜h× Aj>ÏуV©W¸ˆ)%(ɇNÑæ©,ìt–¶ÏèÊ€ã4ˆpÿL¿û¡³IYga‚uò¿Ÿœ‰”¶ðe@ת_:£ésðöY‹D}™oåˬê{\¬Ÿã¾ºÂ¦¹ƒ~ÀÏŸñòþˆ©ß3åRd °Q·øøõ Ïï߯éâ5OytÅþ©iö´÷t5HU_Ö\46U»Ýöc?^ªŒÎÑùpÐzÇJ1¸£ øK¹áeøcÏç5Î9dO%mÖê­iS“ùkبj³ à®ý³ÌdžCdºëò3Ú kvü_ÝÎÈÔÆ U„`;@C9€j0t­‘g3œÎ^ÃÁì™È¡¸ÅÍöÏo߯ýæ•ÚB™ÖÓ MoóRñS™þ“Ç“÷ÿ¬MÓ\îáxq†ýù!³=Y‰\äÐl°Þ¬±ÚÞá¶¾Áº¾ÁVm`HA.2n¡‹êÜ2x¿lC³»±»ñn á„ÅfhÒ¸«®`6¸Ú<Áün‰ýüGË3,g ”å å¬ÄþòÛjƒm³ÆÍê÷í5*½µ6RÑû‚õ<\ÍC`ÚmÆVîJâöxôÞäŠL»ÂÓÕÇp×^ã¤9ãƒ×±(ö0/àì z£±ZÝC7*ÂH.ÑÇI¬gGˆCÆ48tê´£ÚÚ_Jâ%IÆ•\  m< q’7ûà—GÍ›o7ž Ç; ƒðàæÁœqð¦–ôrî!Ë›j.sAðð[)ð’œcø'1Ì)`UG]+ElîøHð$ˆDYýãæ6M?2”Âã-¿iî°nïpXœâ`vŒEu€ºÞúw® dyYäÕ´Puë{¤:b —¡MglÄOÆôv)½}vµž­ÞÁ¶Y#£ëö÷ú•Ù@g C 6ÆzÚ%6;@+*¯'ý!;n` ÏD 9JYb–/qXžâpq‚E¹@ž0`Ôm…Õö›ú—Ë4ª‚Æør]`’P.\N ä]íïË7É»±»±ŸúÁÞêË¥ ÈŒ T‹Ö4Øê ëæ7t‰«íSÌOqXc9ßÃ,Ÿãpq„ƒÅŽæ§Ø4+\m_`UÝ¢n7¨mŽšj4ç 1ænÓø…è(D+I ƒšV¸l[l®î±_£ ¶¼Â½º†‘²0ÊØ¨è`=qˆO²|¥òw¦JgY&QÌ l6”1ÑÂó]1÷Ê {˜Ø6†&PŽ>ÆN3fOS7Õ¦Å&À…ëyš!ä“ÿ LI Òbi¦Ö¿Äô@‰!e‰ôPô§Aʃƒ]™ F.5"`tMbVCMª°Uk\®Ÿá ?Æ\.pXžà®¾:“ ’E™AÎr¨ªE[·¶™h‚yög’‚ÝðÔ|…W„¦«‚gd™Cdu³ÁýêÏŒã¡J„,—E†|Y‚˜ šºUCç¿À˜´–adbó* !1Ï—8*ãx~‚ýÅ!fÅ ”JiTÕwÛ;<_=Áª¹B­70l†]¶wŸ†"_ï±çqu"ÖÞÝÝíVÇÝØÝø´‚Ñ)9Þ°N ‚ÈTÕA# Å îš÷Í ž"Ç¢ØÇñü§ËGXÎö¬èb§ûçØl6¸Þ\â²zŠ»ê Š{ )ã½'G=h˜?]_¥‰Jt?$YwYd…„Ì$ Õ¶X­ïq§nñoÉ"—³ÅÞ f«ÐvUGŠ&ûT€Ì×ÿ½jâ·]3i[&L«_á¢å8ñ€F”@CÓIÒœ;É ø6PÛHãW§²µCj›Ì`*—ž'Îvd£Ò=mS?ŸfŽiˆj|™õe9âèzh¦æÌNñ€S]nÒ•Ip€SqbSàN:¿—+2e¿åþ¯â×Í ¼¡Þ‡Y>Ã^yˆy¾Ä¦½G1+ÍmŠX}»…ÖæÁùO©&é%×ìe×Åý», ä‹RJèmƒjSÃhA£‰MŽMiOݵTJa .ÎÓhƒ¶nѬká„2Óþƒê"³)(%f³9T« ¶m´ÞNa‚ p4ñ£Ô±£"“h¶í’D“6Q=ð KI^‰1ÍCž‰˜¡@ˆ;6.ç&àIà©‚;O0d ÈâfÍ{Ä Vn¼ÑÉc(§èuDˆ‰ãDž›è˜Aì w3©ßÞî"¸>)F—n©†0Ž‹Hï£[\nŸá±|‹l'{¯[I k¨­íø3p›z@¦!¦ºý§îOÿEYYJ€L­ ”M53} )Çå”Þ'”5ƒM A× BDžA²¢@ÎÆ&^h3¦I%uÚS€zÜ]b޽ü‡³3,°\ì[›*H4M=t‰Þm¯P« ]C›Ö3¬§ˆ™u\Z QH[¶×±]ÿ éè^§ žw7vc7R`ôææËåGGGRî&å“Zkl6ÜÜÜàèè(ñn‘@o"h3fØûï·±¾g˜ÁZc¥¯P©.·ŸÀ¼8Äáü‡³#”¹õÏ8oϱ©V¸Ý^áf{M»CÁO€Š#KBY\¿.A !¬ažuÈÐM‹¶³$ÔF%x„S䜣4šU!%d.1[Πµ†ª{ßmö°Ÿû¾Ï(ÁÆeé˜%’]uÛ¶Uv!ÀH¿”Y_ÂçÄió€×Ÿì$Gùð °›†{±Ojü¹c_¶‘Æ&·,™î‚#¸2O§ ¥õ! æIÐN/až09¯”*sÿÔî ˆ£´SäËŠÄå—Æ+®Yá¦~†“Å#,ç{x­x·7/°ÙÞC·*+¬¯©±fþl ¬yÒÁ$QÊ9fŇ³3/O­·k–CC¡j6XoW¸ßÞâ¦ya3œMý’RV:f×F KHI3º5~¨9r7vc7^e03ÖëµeÙ–K03Ô+8áìFÌ.—KÀz½N’ )l@RD’0šX¯û7%C£64õ÷Í=n¶/°—áp~‚½ùåÒ&C•8\žàtû—«X77¨Õ­©¡Xy묻¾qg‰)„‘í_R@dÂ’dÚÀ´ƨ¡Wˆ¥{SÕÊlÓFÛ¨h”9d.!¥5Æ×ÃK?X9²6rÄnÄ6ïD–!Õ­êTòxÍT,d¿,‰Ø¬&¡itA)à­(ñ[ûNõѪǀ!;Á öS<Àd™IÀ2Íèù |ÇtQ|Ñú *œÏJ1C.S2±:Ñ7¶pf•²!±O•’MPdÐ3¾]pbË•9Œ1PUÓ( ñP(E0MÎGw§m­Ìø‚ɤM›9˜aËùÝ‹€Ø–\Æk"@B  9Næg8ž?ÆñÞ)ò"I@µ «í×›¸Ú<Ã}}ƒÆTÉ{)õw3q>ÊhK)AÐC⻡›z>w t7vãÕFÓ48<<„1»¨Ý`Ï̘ÏçX­VŽp»7\¹,éà 'áeüÅøÀ]m˜*¥°Uk\lßÁ"ßÃáìg‹×±??BQ˜žãtÿ«Í·›K\nŸà®¾² Pl,c«£V&%r‰¬Ì:¶œ […¶RPžó™Ü893%룈XëP£a´Uƒ¶î¢¸%ˆa ™VÙPAÙe/cC“s€Q”`MÝ>˜îþNAŸñÔÞINõ)ÇzS÷†˜ZÌðàä…LÛtâG@’=ld¨C¡ŸẆ”·x *ž‹ØEl¤¿)º —§_ýLô¿s²`‘ÎOÝ$SZ\™d Ó`Ò¼B ˜å³ÕSä\ 3œí½Ž•ºÆV©¤7ìÔœ„À7Å´Oú’JBQÚc1­F³®¡”›Q';%#Abƒ#&ïwÄ€RÐÊ@Õ¤gÖ¨¸X–ÃFG+ÝjH“cOâdÿ1Nç0+gÈò `ºªp»¹Æå殫çhtmŸb6 É ¬$7€Æ–„# âà‰)pº¥»±¯¦vã·)u›ŸäWžD&-¦u"häáLÄÔŠbÀX·+Ôjƒëw°‡8^œãdïGØß_b¹7Ç£ö16Ûž¯Ÿàfûµ^™„(2Œ169iÓBwYò‚0)7‹ß½ññ»UgÿÝÏC¬´ªZèFAäż„™P•‚ªšá÷dîòaSâRº IÊ2ÇvÛt:µ‘çq­[hB ÉQ0ç¢x"Ç'îºOO–Ó`\ü§ÁRl’äe6ÑÑÙß„¸óšœ¯L™ÌÆÔ7{j ? )öí¤äuAâ蜟vù¤ Ѐ!°0P÷oÈȸ˱'_ćG6lH²\B–HjSãF?Ç`¾|‡å1J±D &¹!y(Þ5ŸÖó•“úc!²2u¥ùzµµÚOm¡y_úg~¸á(Üœ„@Ðÿø5˜ `ºñ‰¬žIf9–厖§8˜a&(ó2‘¡U-nïop½½Âíæ[µBk*hÝxLgjWïL7‚¹/f£ÙL;òé¸áÝØÝØwÜ?¬M ,xÕÚ$«Àr‚„ ˆi’„Ò¬ÖX›kT÷k\UO±Èpºw†ƒÙ1ÊbŽ£Ã,KTê lÛ5®·W¸k.ÑVuWŠghÃC#‹ÛE*˜bF `AÀX  - 6ŠmRa™AæbˆéNdÙs`Åäd–e Ör&±0É`™fpÄ ¼NS†ë®ùNÊ`>‚’I¾L7#ù-UãéSÄb¥žh’žbÝ(?ÂyJ$ЏÖ°Sð‡¾ÁÞ?ßwôá¢éT,7_ LÃ)fÒ¥½‡,3Lpk÷… mckF³im ¡q×^ã˜ÏQÈÇ³ÇØª5j³üKëÞ‘"Q'ö®ïp˜V#*3Qä‚ [Ý5Yp†„¿Ãß³ÜÌñË‹¼Ý:%X[7S~”» †À¬\ ÖÀ^£jÖ¸«nq¹~†u}VUÐFyבÁL²Ý&Š¡ÅXôœvß#¥€ÖÚw¸ FHîìLñwc7vãw[ᘘ!e‹Â¼µï¥)‰¡Ôc2Ä‡Ú¶ÆÆ¬°á[4'8Ý;ǬX §³|!$Z­±ÚÞA·kÛW¬'n¶%MÜ—lŒÉ^%Ò]/˜ýw·é*yÊ% g9òY©õX²gÏæUþÝ ²(ÐTíÐ,g·+œ#˜3Zñ²ÃÞpá'G;ÎÒ¶Eýß+Nm IDATE·€¦a0èè\ŸR|2ù]:Îî%ÕÆ]Pq(àÜx0Ÿíù÷ Çp*&tLü!ÄùQV|˜}ë gr,vÙèÊT”椅Q÷ÃýuÖŽd*ªRô]üœ×€u¶Ü¨¿I=6õ~H 3B6/!2vSC×jØÅ2VÍ Öí-2q†“Åî›+4ÕfèZïõÐä|–w<½vs€I ä‹"³†Àõª3>Ä4>ca7~ª@].|è)GNY…-­~‰û“:ð. {q…ún”(Ä­n±1·hEʱ 䦀TF) q^bl?{©pÃ8vÏèA;1ÅÎÆJd.¡Œu²äÐþßÂûÇ”îÆn¼›·+ÛjiâzfSö€´Õþˆá]ý…Ó­–†ý‚,"2 ‘[¾&…+õWÏPÐÙ +Ô¼…*袂„j«‡Ûw³a‡å4œÀ˜­yUQï{ÆÅœ&Á»8ÃZ1Ô½F^æÈfY*:tJ¡HÈ3™KlÖ€cðèê8i¢»Öô=¿ü›¶ar‡H²uqo¥v¯À¬N=ôª¯˜Ï(«•Þ¸7CÃ8sJøðJ…‡Âô÷Œ B>^§3ª¦æÑßE:%î ûÛóYY0Jcs»q¬ƒÆ¹­Û-žß=ÁáÙfÙG³Ü6Ï¡Mß…8¶¯ÅGÑx12—Ö«­È …ên c (Ø.¦Ë üưˆÐËçîA÷… æy†|V€2‚i5êû5¶z=lǽַþ¥—[#ä¼,@ °µ’R­†Qö?rBüéyïט® S„“ìøÐú÷—ëw»[fwc7þâøîïÁïÿøàÿ×ã_ôöª”öß#©ˆ@Â’>a×ÌË$q&X¥ €È„m@*²<ƒ a–´·Ͷ…Öª#Ë6¸ÇÕ×líg¹m®ÕmÕÀ(=¬AqlúHø²L‘ ”Òr½sL…墨…j[ HS¥åÚÔ‰¦qc}‰kjÁ5Õ¼..û¦NËׯ™)8ª¹ÿL¿Û\P¼¨Qb—90tÁ9˜Ä»;Ѱ„*Øe0-"GÑZ°§öU.£ÈÁß{Í=8Ó“AwÈœh:ëJ”Ú–s&X䄇7Þ0‡jñ|6™¼-ƒ[ªÊŽ)‘M¾Èæ…5Œß6ÐJf•=û;(ܵW¸«o±—ïc¯8²8Âmuái€ÇùKù¨úšWê^6²Ì ³ l ÚugádÍ Û¢´uØoêãž!%ç8vS˜J½™Mw™Ý:¨VÁl­‡õò°[ÛáEÔ3Ù×7¬4´õÈέrV 'ÀÆtÀRY‹“.Š–2uµ'w‹*ئ5i­íõ¬ÚÎÎʺýÉä—lQwc7vãw‚;Âç~éðùêü]SÕðtûì“(2ËúÚôð6ó*lÁËzXa I@ Ñ1Ÿ$úþËlªÊ¦ ö€Ý{Ô®áœlµé~V£)¥åÒ61Ã4¶»†»Ÿï ýžÑñî¢ÕëN‡J.‡>ÚÁŸ;ÌÓWAÉÁ' € z iZC8/²L‚2fÛDÜ—›1Ÿnxñ ­q™âY9ø½Â›"8“Ož ¥ãÉßu)E<ðÑŒr`K;lg§q£(AÜMÈðš‰úYO†„Ž¢âˆe߬=ÕÈ%<:ž+%RßÝRÃÐ<ľ€bxLصVwdåÓ§º”èÑXâeN9TöׄÿÖîÏRØŽñY£Ù4`­IÎq“25.7O0ÛŸa–Íñhñî«kÚz{Ã$މ *2 Yfv6”¶)JÑ>Ø9¿a‚ú·Îè!\†»/Î0 5‘£äv`û@‡¡£è¨·}"a=OIX³UÚ©y;LhÒ°£cÞu¿‘³ì³&€„„™KV;+…Õð*ÓR¶^¢¦¿ÐÆa ÆÁÐÊ ‚‰ZQp—sçB;<º»ñ; Í?ø–oÇýê Ÿýú!òæŸøÈ/ãg~ì‡ñ£¿ô»ñ„yÐmEµL)ml KØB=AÓòAa‰!:¬‘†¡»Š¶RIÌŽ¤¶°a›²¤ „Òa!‡Øj¥4Xs×4²u>ñç·4÷Qì ú5ãŽPèþdR²©P9`Óê€EJïyx‚¤ªÅ™æ>¨£IÁ¬OŸ“×«îƒ ’â] {¦çb¢)+¾:j¾þV°Ë€±Íƒa¼(‘¡Ð¯#hfDZTéN!o)08"Ÿyæ‘©cò·‚Ä ïe‡ÍÁ¾ñ>PïGHçI¹\aJØàöeÎÝõ dó2³¥ñz[€XP¿ƒ£A[én^ +\nžàÑòuìG8Y>“û=Tí½—vb°3Û¹’™@VæeÓh4Û ¬Æm«¥„h%Ý3zƒQ×%5Yš0@£nÙqÝ%ûËÊY™ 躵‘mÊ8±Ñ5Î=ïT¯uƸÁ÷÷bÆ@†Vjdg…åB’}Á ‰œ¸3V6еcœ vê%̶ÌtÍPsÛ3®K21yÀ7vc7Þel¶üü©ø+ø¶7^àçþçÅ_û'ϰ-Žñ™¿÷ qPªß•µ 7ðÆe (0F{oïAKO„¬ÈòÜšÓw)IÄVNe”†2¬ºÊS±<ü.F„Ji§xf“ý½²L]űœÙR~oÍDÎÇ ×uœÒëXßÃB{Æl±àËúʪ™,f $óÛÝny™ @Úm›,ib"³Ü-u¦Ê‹Ñáý½gv¦µžð± _Ó€„y•Ä¢PSv}³£¯4ƒfÕŸW1œbyÒfY##,á{šF̤³öuA¢gxO©òÄX:t$§3ÐÝY!ø„XãË/ ˆB \¢XÎÀÆ ÙZSyÂÜ®CLt24jÜÕW˜å Kœ/ßÄ;÷¿9¤ ìcâAË2‰l^€riõ—·[h­!8avE/¿§#›)¢ ©ã»Ðe`É~¯È È2G–[ù€ªZèº5¹DH¥÷Œ<™â3 À0%“ר(pmÎ5)¬* ‚(2‘b|ŸªÆ¦HÆzõI¡ºÈ$ÛÆ9Û±oÆîOÄ¿»±»ñn>ïOþ‡øÖ·~ ?ò|þöG¶ÃW~ç‰ü_ôÇÿ4þÔ7~1Þ̸ú_ÀOþðßÄýò•]ßh÷Ý¿‡ïý_‰Ï=/ÑÞ¾ŸÿῈïÿߟگgñ%ßþïâ;¾ö÷á3Ï$n>ò÷ñw~èoâ'?²ú´àp½d-õVCdÖ|>Ë2ˆ<³ïY€¬tª¯®™Vx‹zâhÄaqܶ‹6Ò½ìÊ÷&ް¡)Íjk׺"G1+€å zÛ@5- Ã–òñw‡t"Gà×r×5·gF8ÿžù ŠRx‚ˆPtæÞJ«—µ}DVJòd,Î4é×™b]û#¥ OŠ#æ¦7±çpëE»æbÄÅö¨qJå6,¦pvËšÂ+ÎÎǸ ·‘ ÀÉ,} sF‡S†³[›tó ,³×ïM—}´‡'âÝû§G˜Ëü ¢Q*àWî>o`Ûò ºn¡ côÐ(†î{ö»½)ÕƸÚ<Á²ØÇay†“Åc\nŸ@5Çn÷ÚGloasâÙ0Ô¦¶l£¶MKÚsÃCÊxL°ëä»ðR´­c§^À`u^EdyYZÀg”A³®l™È˜Dä¬ÿ¤¥³¶xP Q°ô@kóJt^~Ü1·}5pÛ½vD§ñ6 UHI ŸÍ ò FkXm)´±XB^m´kÞÝø‚G¿_÷µ¯ãægþ~Ü£ÿò¿õWñW>üôóWñ_}ø¬?ø'ñ]ÿñ÷£ü³?òáâ}ߌ¿ð½_ŠÿÝ÷ãßÿ… ˆ“÷`ïùeGÆÌñùßýýøK_ý ?öC ?øâ_ôíú/ž~×àCëOÂê=I@LJd²Kž“„¬(ìËк5ÐMe×¶lî# Ù±bì_ ðŒÃ;•Èm‡ånÎ?c}$æ ðÆ÷7ÃJ»ŒÖVŽ%%ŠÅ F[}Ó*¸BœÈr¤ ’C¸ôõSöj 8!Ne7‹%ùñ–DÈó›z34<„eêÁ7Q H’!{ßï”"ŸQž`¾BV—£D)Ñd'Ç5n!+äëXÉ3îO§êpt‹”ÀœGÛ+ ZT–‘ ¿E Œ¥ò@Û1hð§Ør]xQªŒ”‹© kC¡‡>]{ŽZkbtœ[ BzðÓÿMdÙ, aKÏUë”'b¾oœk°n¼V[µÆmu‰ýò3¹ÀqyŽFmј:Ò6ŠL"Ÿå !lcÛ@kãñˆî=ë-÷…0<ìá<9,6ó;ú€Ç¶©‹¤„Ì3k›Q¢txÚR8¼ô´QZh¦6rä(Ë‘-– 18Â-±3ÇxÙ €Ÿaü½Y?µ„l1ƒ`‚1BØy¹€á ¤  hcÀFÔ;ïÆnìÆ»:ÄÙ{ñÞ¥ÁGí×Q¿*˜Ûû2|ë7½¿ù·ÿmüÀßû €_ùÇocþYÿ%¾õ[¾?þ—ÕÁp‡_üÕ_Á?ýõ-€6þüþ—ã}ý~éÿ,þÖÏÞ‚üú_Œ/þï¿_ùyúPû)çE³¼°ØHÈŽ éÖÍPµõÃî–ø²1;R:åj&ñæáÓ´]è34ÚŸðÄš0Ã(£ „4…´ÖR²€–FY£÷V&ð »ë€ŸÈ鮂(E²-Ù‡kô½Èri›6 G h¼p¹<ŒI~w:˜2Šâˆ N"¤S„óóVÓ0²€VîPžIL£›Ìäwû3'ðpZÑxl>|2ÝÏåÎ73U~ù×e­âRìp¬Þ×| æ]PÌN–<ñä¬÷;"éÌ#ØÕèöÌóŠ…Ë’m–É¥5ÎÝ6Э¶1"b™EÐÒæà¿áüÕ¸o¯Qé5ò'‹s¬Û\UÏö›È¦,åó.îsS[–qVÏ#Þ(¸ãÓclÂýçÝŽì4¬]óP[56 ˜}¹EÀÇzÛ¶t釢͙ûì<$ˆq(B ƒ±ñh”Œo ÖÖ¬€lV@5 í¶‹.1Nr—xPžACR-Jk`§#ÝÝxwÇ uu3'ùžÏÁgÏñ~õñ§ô'ð«ÿèßùo|.Þ”?‹ÿ“ŸÂýïÀ÷ü'ÿ5~ÏOÿ~â'þüý_¿… ßúl¼o¾Àkîïàûsãê*3F}ºáöS[¶ïí[mkÊFÁ@‘@Q`ÁÐMÛw€xö…ì1˜!™¯ž3üææpvI)‡&LמúµÑf« „€,r+ ›tÓBW-˜ÆPPºç€–bø«$< å®@–=ElûäN‡”ŬD³ml—VôÑ9öSPNéHSIFÜåšÇLaèõI å`ìïÅ3ƒØ‹}h˜PK”øó\œ-xðhrŽ9ÁêzL$€Þ¹QûÒòÔ‘‡ÚN"J²lœV¯DàÇÀoóսݔ„lV"+¤5¹olŒo½5uÏL·Ö XÛvg÷ïà}G˜e{8šã®¾†â"ÏÍm÷|s¿µ]¡Æ8œÜÄñ¤Ú¶Üs6ÎJ‰M† K@–ç³$l hu»évÝn¸„5ÜSþÓç‹R Ó—k¨_vî)—ŠñEžÛº¨Aò`:Á˜ 43tÃÃõ40Ð`±cIwc7ÞÍa.>'µÀç½ÿ}Èñ+h^ѽt+Ùþ&þî_üN|è‹þ ¾é›¿ß÷_|+þÈû}ø¾ýˆµ¢3Wø?þÓ?ýˆöÞ0Û«ûO½†”ªi¬¾ÓiR@¦j‘rõ©¸´:MÅ¥xî(þ×…Ó„Úqˆ4=ùY=#KN}ºÿi‰ÞP×-TÝ Pò0ïdtÊ:« î5)4ŸZKôôô䀦gfBJ€Õê„Ñú˜%.ávÅ'À[ȼ°&LaÃÿÒàa9•7.M(¶ƒ¯&¹ÿæÅ9ÿùÒ"™æê«(È[ä)¹8 ¸ÑbÑuÍÛ寴¿C €‚™òÛáøœ9çÀ–N—;w~Ú='€¯Ÿá%4Â#«ç<¨Þñ“g­d»s‹„Úu ÓèŽYe/©*žð“)Jžãå…6-nëçX·w ög'8\œav°@6Ë¡f]Y½Œ7Wì?¶¼‡æ‚RwÇÀ‰ÂxÇ»VQ$äeŽb¯„œ¶«]CuC¬­Ìð*!gD²šàï` a[¯×á…à:s°œÄM‚ñýáß_TU†ë‚®•m€’Âcl‰ƒ£ïèz2f‡vc7ÞíQý ~úÿ¼Áé×þ |ý{òWbSõÇ?Œß¬Ïñù_ðú”ä[øW?ÿ Ío|o÷“·xû?ôþüùÿéŸûÁ¯Ã¿’úíßÀoµGxÿ›O>ö1|løïãx±úô½z}';€”`KÛî{Ó—:ÿ7:+:LÀèçýê¿Qý–ñ±Êë¾kGÀJŒ %<5¤öÎ'­F»®Ñ®+ óŲD–gƒV5<×…Uçñë€|cyú½§>ð€‚ÙÎ2Å ¥ØÈ¤9´4»âaÄȼ~¼d"XjA~´"M€ÃþCÒ€Ùµ¿âPØWÉÞmjQãKkûReNvÁÏ„òoø<—ñLî<9ºyá”Xûò{X¦êÊ£Db/'ºâá\7 ž¯xÜ•¡e‘!+s»«Ú¡DŽDôl*˜lã°ièÀyBp†öçG˜•s”Å ·íÚMݶƒ§hø“³¤@'MI÷ØTYœƒ„sWP×È•Kde‘g`zÛØ¨­9€Qóå Á1¦®‘¢®GÚ!l¤ç¿©s?vvÝëNkX.-à6fdÈj¡ |ãí½-{;P°»116› –Ëå§ð¼ýáçxëkþ¾å¿¯å 9?ÂãÏø=ø×¿êñUçÇ/´Áû¾æ›ð7¶øõôkxûâcxºør|Û·|%^Û^b3{/¾ì;¾ßùûnðwÿóÆ/\hˆ7¾ø+>¥(qøú¿„/úНÄdÿ7þÞOþ2®¶Oq}üoâ›?øUx­±ûxü¾ÏÇ—þÞ>ö›/Ð~Šç“4ã=ëÅø.$ åd™ÛXîFdÅ´Àü8©R+‰ßü-hlÖŽ»l8ª´ùíä¡´ÑÇ n÷GO0x VfkÌo,H™Õ™Žá7H4êý9Œ ÷büêHÃ1€¬È ó MÝ‚Øxà0d+C”;‚¿XÙú„¹Õ½ãð}Æ)÷ rõþ´£Q-'ÁèHwK,`çF ’pHz¦ñ®*y^ž!‚U„€-à‘e¬,ð(çå[Èp¹yŽÝüS4z}:<ßø¿;˜a8ØO×Bp\vƒ%H@ær^XGVÁT5ŒS`žˆÆ Ã9: °þòä3oÜ{È„÷A¦Sêù ÿäž½ 6D®÷EÿYb–¹@u·é,ØL÷uÀ°±vP]„žãC/pöÖk;Ô±»>›Ý»àÅ‹8;;ÛMÈoÓ¸¸¸€h ¾äù©•ßõ¹2CQ–@£À­i :ØxÐa%åqà6€;¤Y[:cù°&LR‚cɩЫ³˜X÷÷Œ«ÖÌãzŽ1džYÏn)Ý)+¡‘™í긒dÃïsNEfH ´Uã÷Ê€°è—ؘèÛ˜M ÙKr ˆ>À£hÁô»àû?$ä©ýÄÞcS ùáâ¯äx+<†.b¿l΃¥‡Ùqušfî'ö[•ÂÅ_$Û¡œýQGj Dqíp®Cªƒ @«ˆxÈ` §kŽ iƒI>ûÄÿ$%A–d‘Ùn½ÖÆ•¹O.ÁêÎvöXÂ9.îÀhïÑÆ<ζqwžDEYäív‹‹ÍÛ˜,p2{ Çó3\n «í8»ìhhÉ7v¢îõwªÏܰuaÏtó6ÚS–Yg-¥ÁU—ªdúûˆ¼h6À ±¨>(uü ˆ(qôóþÇŽ×. þ5pmªüڇ߸sµý&•,ëø ³íê”´n»ýAÀt±¢älÙv M»±»ñn!~?ØXkÃhæûê©ô×7¸Ñ£¤Žñä`'Àă©6n߇Ò.³9Ø‹ ¼æ_1Üf^×Â’j#Hn5”i@5a±(ÁZ[`ªMç*0’~.íq[²/}´•ÑÆæ_GìLlÿÄ 6”›*»°O%ïM 8™¨4ìì0úÙ£´r}° ¸ëR¦@§ê»¥FEJÇdÞµ& »æÀ6¶+=‚)¹›u“ðÌÉyðõ½{ÚöÆžÖc´kêsf}¬bÙâP7H€în 6˜Š ô}кù’°LB× ºi:ð%ºÈ±`»Ÿ×›èŽ×ËíÔæ±2ÏãÆº2°,rÈ\Â(]·0Ê 7Û8(OPfs<Ú{ U»ÁVÝF¦þ3¬v§ãª9%|¡ÙhƒÕ¿\ºã•b0Ýg†íVl•5’>ƒJ—]à¼â0rÙÙâcÂmùÒÏ V¬º¸Û€8Þ¾=€%WßÜ߻ާš­ÆA£ 3DN`CÐ<²àî‰ÝÓnìÆn¼‹ÃSå3IiKÕÆmÌæ¤ØÉecÒÎ×ö¸Á«r‘K¹„XCèÌ”Ô.Fbɽó>礒·¢ødߨDÍÚÀh’(rˆ\"#·ÚöghãY†ëOæêÎ,ÏQmjËX?e˸QQŽEÛpaŒá2¾K£t¬}´Ã„ÅñÀ‚BöÊ»"hsòöä3­S Óé8ü#p· D±³#;7¢öfAÂQë;/ÍÎóÆî¦ÈOyb ³C#Ç3Ôå!Ý ’à> ô]”ˆaí™<$ãµ£"“È‚ªj›m>÷(¸mɃiDi‡—ê ŽcÖ¹3ÀŒ£‚Üv[=;!VÎgftiaŸp›Âý*3‘#'¤X°hÃL× de‰l^À°µÒµrz†ü(áÀF&2Q—3N]òaÇø=>sÁAËDèÉÏHÙݧRNö©#˽VÍ_„)2ª™*ù¥ü›ǥìý6¡^°Ë®×‘ÎíL½¯ eçßÓ–ä¡ü Žïô°¸Ãx€Ö_ó | ÿ¨«å}‰h*¿õÔ8 šðÖ"§G‚œ~“NO:x9:X »©ÆöwkpÕB( )%ä,Âà´ Ü-‰™»dHiÍ]ëMe“™(Eûè|w÷ä»°8Á-ù]·án! ÓÒØŸc Ç³B<æŸånrêt²¬«ÛàÈRÁçŠÓc{-¨ï¿8æÃIQrì´Uo9•6üá¡”îÖü9UqÏ’7½nI„È ]ç8e¢+•¦n +…Pú@ƒ0Ãi7ã‘:΀UÏD‘ƒ„+ ]+°Öþï¥8—H›wŸÀ";À"ÛÃÙþë¸Ü>A­îaX/_—ã{³y÷ž P.í±JA7-ŒbøJe°QØÌFbÛÓ’†ÙWýID,v ÇP_ö_SD×î¿~}Ÿ_ Vn-Çxafˆ vÅ }vŸû®ë^ÈŒlRSp7Úk1æCïÆnìÆË)ít×ÿÜ×$Ž6Y&¡×MG|øž,½lŽƒ89ïø12Ü8éqú|/¤(£û]™áÃ+2 ¹˜!_Î!˜¡·5ÚM£tÄm»×UDÑ}!˜ÁÉΚabÁÂr¢æ\ï:r6~^8@‘‡gß-ùýò£þÓÞW6nñ3>1ÖéB8º'ÛQÏJAäy·àΙot¦]Á~7vã•Fžç¨ªj7¿ £ª*E“>²Ó¹wºÒÁgÔ•õ؂ǰtï[Œi~¤‡w~ª<ÌjX~OçhJé°J ¿¦ èÖ˜níÝqz?Ó­An/‘Ûש!êQPëªj óÙr†lVt8€Ì$d&¡ª¾»ß*”RN†ŠËÎQiYab“ mÖªvÚ4“ËË“šë‡Bt%ÿ±Hn´n_†þqØ*øH~*‚]@h > Ég<œrŽI8Ë“Ko¨êŒu~xŸ}íÓÁGê~:#4J7ðÐ|dY"Û››zÓ€•&s§ÂãÁ<’ {0&ÁóøÕ,ƒ\–È–%¸m¡7[˜¶ æ˜"å~N?/¶ú½AQäx|ð,Š=‡÷Oßýy‹ (–%òåÌFíޭЬk˜¶“î˜ ° |6æ7Ãõ#+3PQÌPë H›®âÁA€C\úˆ¯ ¡n׸Ú>ÅÞìE^âñág¢Ñ5¶Í­3ÃÆÏ[Ê2È2·–Z[fV€õà‚ Âr €M€êõì{§J ž1`﹌":kJ&xù/!ϲ¸Ó÷¥¦á…8GÇ_­—GM¯¯Duþ"aÇÃ=ÅÚºö‘ƒîÔ Û€%vEûÝØ‡G–e899Á³gÏðâÅ ³s§ød‡{{{xüø1²,èAˆ PÚª—˜ âÄ`&ˆÈvÈ%ºfß¡RÜÉŸ8l{6zÒx4¯mõ• ¾Ç¹[§å°ëf¨£ÿlžnkâá,ýgEæ³@°š¬ÎgGµàAÖk!d!miÑ1v9`g¸ö€‰ÆÈ¿þÂ9 o4áfµ¡TO÷?MŽSÆsUƒE•àm2þLç=ÊiÀèqÀLsc?Ù-ûtÜSî2£ÔÓÇìÚxоÁ°¸Óh7$(0âñvê m…ÃXzÀÌÃ/qÏ™;°äþ#õ•& ) J›4dS·Ã½ÚƒÙCsÁ¥pÆHXiÁ‰3ë¾[XO‘K°¶¾¢ÜE•ù&ù)ݯ¿³OŠa¸Áýæw³G8Ë^Ãáâ«í#4íšU7]ùAJP& ŠÌ–!”=oÙ3¹Û‘>`¡Œû›ŠX/nò.™L,£qÒˆùæÀ#˜¸û7‘ @-Ùëïjc)HÛr®ßªÆ£ŒÂÝý'á4»jÛÿ½7m–ë:®Wæ9Uu18ˆÔl·m…û}xáýÿo=Ew[–mY²DŠ„H€î­ªsv¾{Z™{$wGttt4_È|$/nUÚCæÊ5ä×OÖû@5j¯;T$UHÚr3c½yª{áÕÑjÜþë¯ù¹E×°¥ÿ/"KöŸ*èˈýÿ’²ÿÇÿòÇÕúù?^Nxüüôÿšõô»HJnJ“íH–þÓó?óŽÀ»o¿Ç»þò=°,ÅîI%nÈo©“ùŒTÂx:Z§™i‹¡¶¦M®Jh^ص1q&ö½ÀÅ@3+–}-%|È#6E½f§¼3j2¼fŠ„jöL7ج`9¬€ûekbŸY/®0ð4Ú:N F®(·÷9ü\GMYhÔ{„þku]+¬Ú˜ÏíRïZ¿,ít8{|‰‰!l,"Ýóð9PÞ-þ©+€º/¯šó±¨#ÈnÔqùR â¾a¯p–¦6ê’{¿ëW2ß ôî°„íýlÛ)ѨCãÍÓªY| O@†èI)¾jìµ°Vèé]×ûØe£‚¬+ý¥­µ4)?4tyý¿?\ßá¯ÿ ÏŠÓz‡O?Ç뇯ñþòC{˺`9¡Çv¾b¸"QÖgã_››æŠ&Š¡•á}ÍGÓêã™[C‘,o^¿Cdqë7Õ”¤rÆ%ë¢0~=EÄÚ,%"Þ\4·T²b–O‰mé ²Ú^NkÎ N—^$µcù¿}óyãŠo ÉÍÿu7v€0$çO€ž8%pDŠú®ÿö³ÿŠGwÏp~‡ß¼ú_€´ ©tnÈÁ[„þ}Gû'×ß ø4çMí~Ô<ªÒ_ËB>D¬gè÷ø“§E‘i-ôEÐ,~Å’ ‚l_–€=eóë”<ºn2œOÞéclÛó“7±°p’H׬uÚ2~‡ù)ìðd‘J(~bhô…ÏÞò`Ft-ç!48²h~Î9³–®H{BÚ±½ï‘°†œ•ö¬Ú²‹WÄ9ãt}Œ8€g´e!ÈOzz];“,v3öëW™Ž¦Åßí|ôès|ùâgPQüé»ßáõý·Øì:¾?ZÐ×îç@qcãÀö­WƒÆjiXƒK]ù–Ùv‚’hJUtn"*ÞAG$d ¥aøy°LžV¬w0±RTHÎì~}Çìð=Ö âîwZ×uþ¹ÔÒÛmd –×D*õÍà­{·®p8®°ë†´m}„+¡¸n€>YB‰ì‘I¬â¯ÙÊTlTI*ÐM£His«ð!Lhi9YU³ý‚í5J=hÊÃò ¥On·ÉåP?þó~æFî8FÖ8Ê7†È õö塌“Ê©å—ôó%ž>½*îfûÆœO÷] ÖåÑ#ÈaÉ*òû—ÌÕMÒAãÛ~·ðÕd°@WXÝA׬ ßÞ¿sž0mB¬¡ˆ,Ö鱞2¥Tä×Üðpý¯ÞýŸ<þOÏñùÓ_â·ßý¯À,wGèá»nظϩ¦½ãsc÷Fç+¿£¾9¹H“7L&Š ¥8EZ=»½YåI·P8tû¥D‡4r‰Ð½çßµ.bvä?‡2‡Ê?b™ž_sw $Í1ÆÛ[‰`­‡g-˜3蛲d,[ì’JˆBÍ|ø®øöq¡@Ã"ŠE¬º`qÏÕ_™1Czhk)lÍ×µ¡ ±‰”y1ƵrË.Ô€ M úž,¹"™BøUsž»iRž X¥àhþž°°ºƒ¥¶íù N©ªX'šQ`á/W-‡•4æ–‹ØDÚÔDä<Æ…}µ ”y©$æ´±ÀôIx_‚Rpfb¨æâÈ…}J@ÚóÔeO9ùGò¿˜84MÝ×Ó¬úÕÝïssSǹuÑÛqÉ!&žJUרè¬Ñ¦mB âú¸äÄ3W WpB«(T,E¼³˜¶î*¦yŠ”!EI„‚\,…*ˆÃÀª¹W–}«7/©ĉI{s³Lv5——T%4ŠÛX/øýÃS©Öõ¯¼\§dнÉd=”í-¹P­8{û,-Iªßäß Ç«»´Ã*äêŸj€õ°âry—Sµ@Cq×edðïTáF“ZϹï#êúލZáwöÍ˽>˜EÒpPW¥ed‡"9Åñ!;ÇB>¢>$|ú»î·¹¹”ª¿VŨ»Ô–#oÆ>ŽpH+w!Y÷"©\¸œÕ;FI›¾_k‡Ù9j-na—e-ܽä‹_tÁr:ë »œ‘.›;T¤…À–-'¼ÌÀ2¯r) ]Wè©t¢—3R‰ÙŒmŒÐ8š \0gVøÏ™Ãk!²¥+¾zóoxvzÇǧøôù—x›¾ÃÛô[ºbÿ;â•æåí n%líd–Üe&Ò­|WM4xn¨Á'8i”BÅ5ª³z8è„w+”n&ÎÖƒyÎæÖSÿ¼ZÆ=¡9k<(ÉILõO›ARvm°3œÐ‹TV߯u´+A­ÁJÒSb˜e¾H’a\YŸ ]°É’§;3t¨þY‘IÛVAÌ×çÝwáèÀ§O‹`Xg’ˆu†kÞü÷ܢĂn@ÇÚ䀬wK;DvÔ„â¼V`Y["[¥ÁöܘZö2­²B©jaGK‘1™DfÃ×ÀC¡†¯;”ÕÿæÏaL[Õ8åbljæá,"0ÑöûT‘U:ÊüÌ Ø°oÜ·æ,fy*¦}ÄéÖ¬ÌÑG~>uÄQ×€ŸÐ¦æbPäŒ9«ìÛeÂÂ%|OFŠ\aÛäN½Š6î* Vͩ؂Ŗ6÷ô¼44Œ³ßhÅžó3–vo«ÀYÝ ²nBTa—+Jç¸o}­ô,Óžà$}œÞ¸¥<½h¨h[¢ÜZ¤£ÐU•ß‹`_÷8°ãftt‚Ï“Š¦†2 Í Cr>Jî›ïSïiOÇëášù• ûž7ˆ*¢ïÒÙ£@ÏnÀK'ÄVêõëÓ‘„º†zø!¥\ìVNÚn>úª?.ã©¿)ÑÏvãO¡d¼Çb¢a¤BéáTæ‘ió+¯ÒÄõ?‚ŒJY(BÛXÊ:ª‹5ó7±*öÞçhARM‹󇿔;æ¯÷êO ÖÇwÀ² ]®Àùùp:õxk+uXä%fœHGkXAöï/_ãÙå9>=}Ÿân‚{{ «¤nBeˆ«ÃÛû±ˆJ–BÙl°¥ÏÅ`E¤$ƒLÖ9¹íì’ÎçiG9˜B:Na/˜%¬ù†Á¦þ¼Ü ¥þ^©€í".Ï`ív¢ÆÔ°@‘Ò³º°ìWìf>üB€…ŠIkü>GR¨+´]Μ,’¬Í1²…™(VUT‹k*:ƒ%ŒëºMíR²ÚÌI$’g4EÚd%ÕI{Æ´<‚¨SÉ4x›­¶&¹€ô‰_½Ü&ãiNhŒˆÿ|Ûzô:ÅpZD`—*Â(”ä3ªÙ/q`=ä` Ewý-¨©%þ©ü‹î΋B °)›42õtRÌ×÷…\0Ó~l‘2 «ë+•÷˜.Û»¯ö¾#íå¬ß÷v7 Üaíü5RaHü. JfKHT-ì•€ Î)hkLÀÒššzs‘ÔÏ_hâC2:º+Ω£‹'}CjÃ=dܰ»ß' òälÍS ›5[Òî¦^” m±À­âc>BÀ¦ŠBW.9a(û}–}/H#nÆI<ùòý·4-šØ™ùW$Ó|®x:çg{ï­#½EÃÍkcOBgŠ/ýLlALÂRZ •–äª8Ǩ€ZkÖ;uäzÆ¡Œ„+¿).®Za¯žR š®¢bÄRè|Z¢'•÷æÁ*³œküƒ‘÷Âcç:¢ïwð"pÌ-vÀ vÓ©äf"”Ðf|n»°PwoR†È,ú±"¶‚Eoi­+÷,ØhMD.ý¢Q™§6²cî‹Ù´óu,NÀ¾cûá]C+X Á ÓuW´ÔfŽ–}ì.ë‚åñ£<Ö»¿®{®Ìy4ÓaiHÑdѼÀ‡–²HNXº»Ãy»Ç·oÿÏqZáˇszÀ÷ß4òvkŠüsÃò4µ ·FÁp®ÖbÅ·|êM+¥‚•Œ6õÓµŒ:WFʦP6•ÖF’,D¸q½…0¥‚ø¨äç¿‚Y¡ÙÒQ×\ p¹BŽ+ìz&i™Ž[fØÁ$9<‰G…Öø¥ùÏ$ótF¢†UV(V,’¯>‘.„Y{#«0ÇÄ4ºøË…L. "24ZæüòŒÖ‹7 Ϙô®õ½í m­ÏAJÊVkïR`¥häø%âáÂHOiAÃæS½xJÕÈXeLd)Îp^KI*"%-cºM5J’‚¬ùû†Hù~çZ"¯‘±Ö©Vî'ÌèüO}܆”ßb)ÿóVÎ#Û2ÒY>[ª­Q(Œ–Vj‹Vöê#þ§ÐsŒÜΰXìÊ5sâð¢bmº iãš›zq¡[V8­k+N4¹‘é@Bà…ò$O›|<¦¸©M8$££‹*T‹”¿“Ò¾=7ë(¢ŠF¹Ï;åÌ{P V&« H‡’…f•…^CnÌ–^HÖ§ÓÀ¸ŽØ 5 ©­–^çð¾3ÌîÔŠ,žç3][ìÄ£E—@¥ãg’†s}ô„‘pŒLÜKÕ•M‚¥¬Ã–«òdâšä6ò©¤ ³HÀ|Uÿ"F>ó£MñYrhf‡–òhNÈŸ„umyÔ–5‡ÞºóŠÇiA]fêÔ fBàW¢Ë[¡æä´ÍÖÆª$äðT³Qœ ÁPwqEç(“ ju¡¬wéyÍÛíh-q_ë«,9] ìr…]·ÒÕ‡mA#Ý~Ñ3ϰǦvª‡øñ°*ôx„¤Ë¶]a{rE\T'c5:uëÆ«Äb$!M5©Iq8d'ÒýÒuÃëåOøJŸà/…'wÏðéÓãáü×ti G¿Š ;È‘ÒT¨ ÷#õ‚†ãü¤Yƒø«Ä¡Œ‘Î!énî nÍÍÌB‚AϦªb¿!¬Ö ld+Õ UÄE˜°`A*è§|ç'ž¡z†¥®Ò*¸¨<åDûÊÚEj-öÔÜ.m2¡®ÀcþÓªŠ=å± QAW))ísš8ž/-0KˆºÙ~~ ¹{$vG°|Žy‰¥¹‰X˜'KŽ8oc«œCÍM)̪Ö]8*PTÖyjî5pYö2¡:°y?u½Ïw£¸˜A·là†Tñ›ö÷Íhuý»¤òÍ5%Éxì¯X-RŠ„ï®ª÷ª_¿k1³HçûTÔÈF·×¶®‡GñCŽ€·yªÝN~íÔødΫ´(훢U*âoÔŒúOâ–HÔmh ªKú¢ÛU­õ`.'‹®$.ë|SBÍ:À£]B‘“ÌAõV%6áò:9D;j+*Ó =dt*]®YýI${ߨ[øŽ{Dšó;mk˺ˆgY ‡c®„®W¤óÙ•BìÉÊa‹ŒÖîô¹õþR¢KbÉ#aYWXJØÏg ÄŽ^í¯ÞþŸ<þONáãÇŸãÍûoñêÝzWÙ9ö˵P0DŒš³6Æ#^š™7«Z_Ïš„=ÝH«cc$Ç]ê4î/ýQµXP'¸bŽ[™ÏÌ6iÖ^Ì7g›%1Âͬ[)1«*¹ßöÕ»äQ­6=iâÁœs°Æë@ cZ3_zÖP`AÙﲔöŠ@f$xŸ^fšamÜïÅ_Ѧà"´dñŸG ZéGVaÌ;C…Æíq0*€¥"¶hèéDÇGNE*vjž,4]KäjÆç(ÅÀÑJ3©Q¾©¢Ï|håâyµëÖ̵ÙÎÎ:í¨þ¬j¥VÉ8yž±„Âg2nnþ‘"´BÿUü22†û:ë.gíU÷ŸÈ@„u2GrÒi*h1B×B“cD )ßoj¥\D¹°ŽÜuiM^¥Ä9'ç>®ËE–ÌGÎRuç¾–,´Š2Ô6|Ç÷ ïÒ…Î4eQèáœÏ1¯´©9ôJãú>ºóZqb!¾ÅR†i„ð4ÔhÞ—&ÍD8Ž]Ö`:"ÜPpI¥ï€U›è‰W#À¡°žjÑ׿÷ ªoŒ¨„ùµ×Näí¾”»y^_2ŸåÓFRGôwì05¯¼gäÕªÒú„^T¡Tä1a™Me͈÷ÃŇˆ{hæÌek§]ëþBË IDATl|°T¤Í÷†x'm YdBÖKÜ‚ýD/¦4ÜcLH·ë´­~YhVJ}«|>MH¸4`ýá£Áˆ_ÖµT¿wUè£dØùŒt¹¸2HrH’´,ãÙ²Qµ"†JøTµÓPäbôñãÌ»z¸`¿^ËH]YCŒaŒ:Ë^ÔùŽ,t†âõôz]±OÀ¢°‡sެ4.j¶ë{|ýæ_ð³—ÿˆÓá ¾|ñxóðgìéÉ’uï[³0(狾ºZŽ$Œñ¥·%¢œ¦!}­YF¬›"·Æðª¹Áj¡‘îQö2—«4R.’ENi"Ä£O$ôFc¥ï2£d v½@ÖºmmdWíML ;󤩘n€ñ™šÐJúêUoØU8¤ÅkP‹îHŠi´ªz¾”¬Júj±Þq£Lhæüi½ HX4«ŽÙ8ûÒfÄ¥†ü¸œ‰ÕÚȳWK!’HüÀEª(èÌ‘~Ç-–/ih}=o¬¼Ÿv†¸è sBR9¦61+´»U„‘cUÖ7+[jªØgÚXŒ.ÖÅmFŽ mõ£ÉŽüv£r²Õb+¹ï-ê:1Á¨D3±A¼nA€ÔwµQ±µ`A"ÿÈTM#µ~mØ4z+ÂÏT*f ƒ±n¢dEÕYŠxè‚åÉ£Ì-}¸ÏyìG÷y‚C@;œ° 2'Nät9€ó=ös‚`oÏÏ[ŽHèné} “ÈkÁcˆ¦#ÝÖb=BïNù¤¹œ‘Þ_Û%׿ßTØwW|ûöwx~÷9^>ûOîžâÇ/ÿøîŸ‘¶‡`/1…yúŠða¾;îÙ›:{>yŠIZ„S±XŠã‘C@yØ©Í'Aƒ€©«B©vv[5WÜÒÖ² F-¦p½B?†VÈew2•ŠâÖ"fq˜*°ÒÄX°úáÃC:©+‹šÖÛ¾@mñJæFC …C)Úø ™Fö)‚clÚ-Q”Ó‘ˆ=•B‡&(¾1A›¬­&ŸÀzv3Z¬´—:ᒹǤ™_ Ðq•öRˆÛÖŸKŒ}®ûÅkpS! Ȥô︬q¡*‰ZD-2D[\Hß±ÚУêÔ€°WDFg’D´´~¿¨s}òá¶O,ôHCÓaí]j›Ìµ±µTð'û /‘ß- ß[}e¡µÝB¨”ìz‰Ïžý o޽›ý$Û<;ɸã+y!>—Ë‹g´¦úaºCŽÎaƒgÒÀ8q—”$ÌÄv³À‹®élJárˆ‹úª ÎhS}s{AÇ€q¢i›d¤=Y. ã.½òÀ2 d ð\W#S£Æ ÅêÇèA×,œ(Ÿ9ñ•ÇIuùÂ{¬Í%~ÉdHXáH?kvT•Inä×VAC7Õ Y ®Ì¯)!ªMB5ÎïY‰NÇ®ÂS“ÈÂ*™ALK£`=ãMÌåá4tµž-ÔŽsEÈ¡¥øž.ç.°”9¦©‰¾Ùƒ@û]D„÷XŸ­J0I¢f&§ ö š8 FÜ)ßm—…uzƒ„Cˆ±#7]k° Òµ‰‘QnÒÿ3…»–]T¼tP[ô¹·ÄsËÕ3…htî&I”ì8ôXØ Lò+/R&;EK´\2 Q¡>É4“6Ñ*Ú*ªžÙ|, 캕ôIiN7`ŠÅr´ЗÄú4¬=wm- ÖZbÎN¬MƒEÚ3eSŠªí©S‘6A£Ø–*IÛ«ˆÔPçDS¥æübÞÍ)ð½€Û7¢ :Q°Z¯[,èˆÀnFÎ4K!xÖ= È|‘Ñ.¦ ̾|=8Ô¼T¿;Þ)uÖhØBK }+.Ýø£†cö9 ª€zA˜æ4µÌc %*êèyhHïá4 ´…Áê(ü-, ‘uÙóó|†íÉqWTýC—j6nÝhºîùв^4 ŸŽÙsð|AÚ¶N§Ñ£8¯Í®[v†æ/7sP°‡RåP¹¢9{8]òÈ…ÂD‹A;£^eÝ¿?¿Æ«·ÿ»ãSÜžâˉ-=àýù»6–²ILf·õ2çu âk³EW7¦o´ll#9j€G"zšÆ²ûg`…jÿüÆÍ'é$^«¡o–¾®µü®Á~§°5Ú‡ù}5™Év…ê ¨"¥"–ªéÅ„sGVFêÚê´’^D Vdï*†ÝQ0Ô„$hêçs8q{ØY—‘„à „Æà´?ZJ\ 21«î'ŠdÄ­&{¡–˜¹žk«ÉWŽ©(Ã~brýž’-ÆA(¢1É@·Zܳ¢q.¡ª"^µL ¦ýíGµïGaï„þrJüb%{¶T( ÍX_@Ï Áˆ^Úûe„O«R/1朵î Aw%qž—"Št¾ ÷5çÔUº¿’kŒCcf} *Ñð~æ¬ =Ñ©û¿"4sc- Wb¿çR·1 Ñ„‰sÛ„‰¼f|j˜ˆÇ_ÐÚ … z%`Õð³ñÖ¬nb±8Dhä[ ?o8Úm* ¯9ù’,¦=uÄ/ˆ„ƒi‚ï°ÝŸsËPÅZD67u…“E>ÇHÖ/RN¹ÎP\.x÷íãÎ…k‡â¼DŽJKº¡Olâx«CÈÁ’£9e]ûwد;¬Xêh_£(ütÀ›«=J騩ã"f´Waë’•ìûéºe^ªuT2• àšæà\`¬#Á>ÀÐZ(‰™ fø¢ = Ë‚t½æ¬è}÷µ3Møbj(¬ÀË6‹õõy{‹?~ÿküäå¯Êèþïðþá5®û{$ÛÈeÛ ÄYšÃáS×[æ]%âgþÚÒ2ËÍ5þ°°†®}™u]0¤b ÒŽ–ÅŒÎ3:v¤£$‰³<C‡Ü8‘ÊscÆÔºèÀmQYbRbúÖ¥ø{’ÒÔ©…Ű2áêû¤# #.@Lóÿªø@„ÈùŖɉ%ଥԉDûè*A½ÀÇ<—ÔjŽ9s¤eD*_gIjJt.9‚ï«PaÓI…Äâyà^ú)Nk!±—ã%Šä‚ý\ÿžîNÏaén9oÄ[Ë·ý³ÔUk]‘Å1Kǃˆ,ô¬IT H& y{’ÌÖM¨ÄM‚K¨C<–ÌVn¨d«©6U±Àó•^B)‰w3÷ú‘¿}¥¦4‘†!püÁÆÌ ²XÂ$º{ç Ô±‚x-…7'2ÊK¼Š[åqj'gu‘KuÊþ[h IGÇ›y}+Æäf0iã,³k‚›þÑ[‹)öv´)à«ÝdŸºZk8ÖÝj¬F[MÕ‹ÙèléÅÓNgµzZy\DÑo{«Y”âj6`ÁδW- ©Íÿã©FIkÄù;yZˆÂÅ÷MuiY#*G"Ív89ƒIøB“½LãËaÂÓïQÌD“D›œ“×tGŸDé‹ÿ,†ÙIþ`Èo‘K$ÝRšýà¼á¿z;øsšÁŠ| øoµÝ’@McÞŽ…œ™`ÑbûR: 7|ZÈa…, py@º^›Å¢§B”Í-æx‡†ˆîúQf2+ïš Þ´#]®°ÝœÍI5œî„ø°Q$’bÜ…-¢¶¬Õ»^B@GsËC÷¨¤j´£ök¢¾=]ñç~‡î>Ãó'ŸãéÝÇøìã_âOßÿ3®…œ; '¸ÚæäuàâˆCDœO$¼ŠX›º\{s¨Ä;šö}ÛíϤÇK·Ø‘–¦|‰okœ9~Sûö}<Ò¡1ל^@0@J)j‡ËåÒ ÷ÍoŸ‘cr› Œ„’~¦jÃ+²P¢MÖcõ Ö8uÑ”7â©GkRÈxb´Õn!W˜\®RÞ{„³çù–‚:E1Û7|ìÜÃ,‘Íý„Ä,ìZ¼XðDì95^« ã^ï§ã°Í ëû—ŠgSâ¶8‘$ `-‰þRÚVƒ3ž—Ž~%Vð2}IxäÕÁ^¦$.ä€m¯™¶ã­»ê¡!>q‰ù~:µçë礊·_«ˆ½Šwuµ‚æ$ž`I/L0(Aßi%û‹*‘rYˆÓÚ#`1$…›–2‚Žá¤‹ÞE·ZÕo„ä?P`Iy=…ä`XY7Y™ìI˜HÕ=ÛCK¬ Ž­0®ï“øÍ«Ôùùì¢eW/~û™)d1úïB ‹y×)ªMP¹§«h§vÊÔ8é-2Š7ÙóÁ÷< Û¢k¡´åO´—O«Î‹G¸|jÏÏ]‰§AFâ½ 5¶æuµ‡ô`²ä/ŒÁ4¥I…àQŵI¡ å1¿M žÎÍ Ô# ám²¾'“c&÷O²Fœ€³èÙ³Óø …/&ù ãrñ\L*gÏiBWŠþ©Œ\È‘£:á#ÙH¿³ Ž×a¼ÄfüQÆ Cw"2ŽÌ⦜Œ&…àÈÕbtÍwàžëä–¼uScVõê²`¹»ËàᡱcÚ¹2DbA£Y‰gžRM{EWÈ£'Yxòp†í;‘2R¢>Ägã9f:e†ÊCèér<@Žwyw=¼Ç~Ý;·O<ß²~®¥GäCûXÙÜæ“ã·ÒÛûWøæõ¿ãîð§ã‚/>ù{\¿¹âíÃ7~ŒÎk*t‘W—šœ&9<ŒÑÆ×xŽœA™ˆ:,´N25Œb‹jÂßr™ð>Ífp࣑ ˉOwhænWÈé*ý"„ÈSž0H‘þ.Ëš4ð›DÙeÊZ­J°ònzÅ™•+F$Bä8K¢“ÍùK³Ç5* za¸p@¤ X>!LCQÄPÃF(ÜÈl—žç$1ÎöKä¦þ€¯$cDR:õ©}&Z+-7žÆËý¥„eÆ ß©•Ô}z"édôL¢£ˆ³&|ÙÀÏ„÷Îu{*Ó ö ÷³ÊÊô-^cn’Pw•Ùz‘Þ(ÏѨi‰|bI¥ðq#qûÒ GãcÛ«H¹X%F­Ä¢+å§Ûœ'êú,†šŒ"\ƒR_<'˜ÑHÔâDªR±9ÁáÌ3'° ²âЙ™Ýe/4½y#à±Æáé™?bÅ{ËÒTÏø¿‹MŠN)–rvûgø|b3„‰´9?3om5K Ö\Î CˆYÄŸÁ„œÚ¨alÛ$@b‚>FS} …p'¤û}1Á`Þ®~ŠäQQøÂºùtYŒƒKí3#ÛÂÜI’–˜ 6Uõ8CwÿS÷wŒ×N+<,ŽÙ*¢d¢"Ò­ÐbëdfÀCI^2oCÄ> bc¥Ÿ=9}šÛ¸èá9°oWì× $¥îã'2,ØŠŠ¤¿™‚Mf™$Œìivu®`Û`iw¡~pG\ƒÚHø!ë5­¬~É6üpÿ5þüö#|ñâoðìî>}þ\¶pÙÎ`U¯V‹,H@9Åù…*˜ÎíªÉüœÌÙ…ÆÁmœ&2Lfi(T¥Ïn5óc[gŸ¢(£ÚÒ?Is莿Œ³…4øë)º7^Ûófдë ³kƒV4T¹*#‡Ž]Y„%ð‹ÚsŽŠeEÛÿÝ7Ö㉠M':Œ1æ)6)”μØD´Ó}bL§0ƒ9·\·ÆOt`Ô;.£›JyO’ŽžrÃ(L…̤­•î葆NBÄ;×°V„IúmÿGž¬s0ï®!hçëÓÇ/±bجcwx"·{v3ÖyÊ—¶°–pÁšÜ-ö=Kép‹`Ò¬MRo|1kÎÏÌ11¯öl}൲7^5ˆf^’ç÷KP…Ú0Ωœ,ÎkÃPóî2ÄÜ• iY §4íØÏؾ·¦C]MÔúþ9é` ˜(ô°@–,Òùì\½j\QÚ…ZÄYÚ:W›ª1[ä¨(L3V°ëvéE6B!;ú²švD {™òŸ…Ô‚ù¿lïðõëßàééž=ù/žþï/ßáÛï_†:Ì•öb4ëé²ír† è›±(¯r9ƒÑu+¸Åó£ >7Š÷¢Mè7·ºÝJ“h6(4jß™ÓXÜù1‰óª/Tp¯I‚3?þ·Ìy>!û¤Ö"wûa”jβÑ÷(â‹i—àTh³‹{B+wOØG1®RÓ›_ËÃDeˆc4ïój1ñÎÙ§QL÷‰^XØšHO`~šïðÆäþÞñ¡À3…9€ƒ2× —‘›Á脹SeàDþavTÞkÓÈ[¸KV8Òêî!ºNµý…of)ª–gµ rô"ó(¿4!OçÞ›É4ÙJ Ϲ eHPËM¶¨g°š7ÍЃE<ó€áΔp$ï™kÑÞÑ;7ø¹‹8ÑŸ‘-¢—™GÝ(ÿ9uA.y&Pƒ—NiGB­›g¨ñ÷^ù×ÑÖ28fhaž§Î#B)'ÓW¸éõ,¤š!èN("iÍ/—)¥–$‘ó#7ëYÎâ0ÖkhdÁaãÕd3Ažã+úHËvñQ³X )ÕŽ(v+()ääFìÑ5c,žz!œ.pL#p¯Æ›Á‚.•÷Xo8å…%ÖG†ÄøŠ¼TEÏ£®#c/úþÅe”'ƒK/zsŠø _Êå­"¾ø6À•QbÆÂ£½Ùß©‘h,Ÿ|‹ô¦Dš‘ €B“§fÛfá",ô³Þ ·FÛ=Cs—&'-”HÁ'ýB]€ÅÏÜr"×/þ§Ã£^OÙÄ¿ÓýOÆÿÙkŒÖJ‘ÔÜX£“íUlÈÀf%<\nê š¼‹ç:’XJü?ÏRª¼9AÏFÝÄ2O’VH›4ê¼µ,f×Ç#Ñj¤â}3ëŽíNZÊ‘äx̪óí\.d®MB«ÖCž*Žb¥£§2D9œ²¸d߇Ø>J“89bÜþ6àr6‘{ÔÃYOwÐÓ)¿ß‡s)F-àÙ ì)P¨Êõâ‰Y±žîaji?ÒÆQ¬Ð;Í+¾ùþ7øþí·XøøÉgøäéÏqZ{‹Î½lk.ì/¥1¶€Æ©6&Öóx”§2÷4*á1jìŒËH2-"Iðg#UGD]ß|eöm–1³$ÿúä‡*Õ·•u«Ø:ù\b»f¥ñ’×gEô…03Ÿf&‘‘e¾°–0µ”“v8t<’œ×i½àë{™p+¢á<\YºÁã³Á¯ÃŸ ÎAAº½LŒŽuEXák„VÅWàçç)Ÿ‹ÉRGTƒøµØºw º­[^gpÎB±¡•'nŸw:Û ×Fþ” Ž]îÿ=þlâÄó%G¼zÒŽ3Å%óMTí“yWE¶Â,î¾’‰Šï/CöÓ4övåïÛúSgÚʘvhþ½„gê©5Õf/5¿‹dêVWöÍö…VŠéZü½×óyOeaŽ7ßÏæ¾×Š[Rb×c¡ûÝ¥ÒÈ–5¿Çd½)2ƒO—¤€úL%úC"ß Ög t·*ý{œŽ'lé]5llä™ñ G[ž±Wò®+*% W7S³1±À£ æD 6=¼%ÔXÉÊ´¤ú2»ÎÔ-öE®l–žƒ­dOáxrd>í£ó¢R5‘• +2eÄgCkR{=rgw½æ‹ÛB^Š˜ãZ ÝPÙòE–Gæàx€lW¤ë)UË!q¤å~xzD²od›Ú…¹‘œ*tY ‹Â¶k.DÓîzogÑL‹%|·2¦N€£lcGYG¥à28¦\dŸ¯?àÕ›ããS<{üŸ}òS\Ò[|÷ð§Ý?¿P‚)ÁBè»(Èà\ 8ͦšqû.š;sÐeo¥k”õ *³PP:¥±8´»q—ù{'/Âü`ÓˆG ‹ ¬Ð „šqvûså°–ÃFêWVO4å»s#HÎá"úÏ."[;×{(Rl 1 ´£2Êí%‰ñ~qðʽk7Žî€Ë+“µóf\ã´GA*å ÔÈ”à_<`µýÊ[ê{°€%’õT}h#¸`NôcN‚A<@ÃH'0!ÒîÒl¥‰¶ïÒ|ˆi_ÁõN±J®€˜ˆ5½A@Ôçƒ=t˜e˜O2öªà¸Ž^9Üå^‚:ÈðÍ©W]qVô½9cå6Ò2I/¡)©§‹i4Ц=ÞÅbæ',“|ùj=Ò^ŒôM'Zpá[¸ÛáEWøøç¤ ¹y^ÛvÐ0t†h€Çcmf8ÞSÄ#Ùu¢ÄìAÝi&2Aø#ûWSj‡¹Pï£Wˆ^^A&ûn’sëqÚãúŸ<ÿž?ùX6¼zóUÙOÕ}Äp8`TÌ{$s–0b®"š[¶7K€€¤nì™pPƒkC±áø0mà¯ù«™Æ&c…]h+9cÞ2jTiBƒVˆ>©ùµÒÀ‚#u×1„4¾˜‡ä¸"=œ2Ò6ænF4P7´h›ué_ôp„žŽÀù麑J»'^4Z€°@rÊTöK n–F#úõQ ].À¶׸·âÇ<âöW8ä¢sDq?hií~2þ֙刷çWøú»ÆãÇÿáó—?Çýå5Þ?¼™-crQŒ”@ºæ¸¾®ZŒC~ dvßüt¯yÒ€ôkJZ#OJsëE™/â.[ŸöÖP¶ì ùÈ©5b±DóÆøŒh'˳ÿt:AôRÜ=l F`2WL=€hÜ«9…%u!Š0—Æè"2:;š™  ¥0Jâçz /#ËqQ|æN…`L £öXß(k«ßït´ŠN󨄸»w梗ý©6‰ZŽiLn=džYœboÆûDBj xR¢yÞ­BS˜Ãb:AYʾ$þFÔÖ‘u¯RÕfÖ‹¸g_÷éøŒ:ÆmîYUw¥‚µ›ÁcPDs² ¼¯"„š&¡ß‘ý°ÌÊwÿ»[V‰äÌÖ½TEh²B™gâlšÇb¢EïzcJY"ªFÝ~Š8úöáp¢<ƒ,Åh­X#öý©\ck 5W^ã”ëî¼?id_2ûœ¡=˜zPVÔ"‰2­ŒÄݹ…ÐtU$&Þ~¬‘+X0‰¹kQ¯µ׃˜§/|œu^ï ^<ûŸ>ÿtQ¼{x‹Wo~çgB2åxÚMôpàYþÅ‚Ï>PôýÈ¥}è7Üz…[Ê®¿þ¯·NËrsÝÐŒ¸j¡Ëê*7®Ž† ½l|&ùŽÇ–ã’GèÛeÚ;™ë1ÑÊß«‹`9 Ç¸›‘JJI7ÂáDþ~uê¯ê»½† Ýݧ;˜%ìï`×Hñ,2UÉñZ4°ðNäCÃ.sý>Ô8Oo­„Ò~Áë÷_áßþ.xöøüôÓÿÕÃdpøÏ´¸uЇ[à’J°·ñ÷:•ò81Ù×%h÷ûã÷”lóàølãÈ2|^X³Åjö¦dåS˜i °mí¬‡ûs묱Û'ýãZÌGd2ïEc࢚Ü>Ѭü0Œ-oŸu2!³ÌùÀáj…ÖBžF7ž‰ù¦Œ‹Ãä-?gêøË €1ÇÎÏÍÒÿ áãÀb3ý ·L´wIÝÕA0¢aÎÆKn<ìPMכܸ=m2&—A<8´WæñÄŽÊI@ï8ÂËS5ŸIÁd}Å”/ kˆ¾kóß, DÓUm·V®ÿ3Öc‚ Š´¶+ ëÒ: OàÏ¤å ²34Øñ.;xÔéKhÈü©ŒI#l“˜uEh2nk÷f› Ù¨ÜïÈ´QbbŸêúºUÇïL ôE™Ì.mŠÎZàþÞ>O†—OŽÓñˆËeÃï¿þ'¼»ÿŽ2'{‰yƒeË0üH#FË[¹ñ3„'ðFýú°³ ü½(­XòÊUD›sü½ÈŸeˆ¾› ÷¸³áx—>vôŸ%‘Á< 7NÇß/2ø74/µ÷Ÿ5Òð{ñ\ÖAìÅ``Á‚Õ}‡bÝô¿Úf-â#1ÐIÑÞr9@‘`ûÖר0 žšÁå ÞCÍnNno*>7Úù"„Ћpú‘t¯YßæãyS¤ÚFøYQo͞ѽ®ÁOÕ™¦"POðã—ÿ€§ŸaÛÏøúÏÿ‚wß"ÙŽU>Ø)ÙŠsüPññØã‡ðo”‘¡ò—1QŸõnÓ×Y&öðSJëTp …uj–OÐ&×#i”9ñÇi-¾T¤‰#ok=KæŒ. p½ ]÷£7‹[¥>Í](‡–HænŽ´gÿÇ=µÃWLBW4ªª*{HóJ}£/ dÉ…/°Ã²+@ÎßóDÞyyÖDÝzÌ\ôçS#3ž±PñbŽãø7$OÈÿwÇùüúî×X×Ä“»ñù‹_àáá{|ÿþlû5(4{s2+pQy‘ /Œìµ%¶Z‹öì,mÎÓ<ʘŽ'2ɯœé7§ŠlDZÌ㞎×ÖÔýÑ›¢óšD éö<#îÄ »gÆ …٨¶„Ý.ˆþ±“¶Ú—CÓžúweuÏENb3Š8þ ÅU6-¨\ñ#æ8®ÞIa|â%Ìaeµðˆœ±Ê æ±ò¨Úç› Bæýœ«'üÝR“Üé:=„‹9o\ÞG¶*6 @#en;‰Á‘Þ|IÕ‡nðšhÜ]*ìªgk£L´‘>º]‘0ÒLEƒÅUv[Ä(Xv®8®H™ú‘«°°Qº%›°û=8âÙÚØWªG=¹ÉŠ0/|‚p-ÇKqÝ^Ϩ4Ds”»üA}.R?K¡YT1pÚ[àøöd‡mãÌ|d-‡,x5;Å13V’`Ú,º9D‹Mµâs«‹ÍR È·ÕdRî±›h¡d1!^ôäh{UsÓòpx„Ïžÿ >zòЄÞü¯~ø-¶t°Šò¤V´Ý~ÍÌ:ìˆå8®´1hòzÑ¡pàUǕłé{xÃi^ÈóŽÃœ¿¡4D$È\ŠH2sñ¢£ã¢¸1Þü)$8PRÏaÍèrŸ Ø¡ûfÒtëâKnw>L’{‰¼›¥íºÁ®—Œ6aó ¢ß_7ƒæ•¥q¯Ü– w<Ëš…Kç‡vÑTcCˆ¤›ÃÛÍx3›ÂÌcmÁhÕø3©í‹ÕªÈúÚj?#n?t_ß…Û'xtzŠÓá ~ôÉ?à²_ñöþ›~Qò…"Æ.{¹œ–)ñ<" ÃrÇÁÖù.Å ,[Ð0WÒ\Ã0rm[0œ0i"ÇÓ"ò—+ê”éQ ˆ"}EoÌÒnX+’¥²FÑ8ÝgRµF›M[å® ñ57ŽËèyr2Z†õ2|)ŠæžXg-Ñ7]dÜ_à 1ú…úhâD!<¦±„ঠ΋EWÅÔ v%¤½rN­¡v)ð¸é uhºMîx¯Åúµ êw# ßȤ۸„1+™ñSqc§’÷|nüÝÔ£D£Ð¶¨°P™OgeªÒ£ ÊaÛ1+Ez ss¶m3T³;©ÿÞP´å(ds)pàFÁ2^¦äG)3û³¶G”¥æ úo½¦¼'ö×ÅMÉQ÷¯ÍŸ55o³i’âªêo`I!i!¿doL'šS³ÈI­¢—ÊÛ—vVȪ°÷{1ÃM­Ôu¤CCêV#5$CpFÈ,¾qÃk1Ü&[åù¦MX{¦æ“!9¸ î²d9v¸vÍÛ·îmã†C¦žÐáïS»g«ÿSÑKµFõ€}/_þ-Ö#ðæí·øê»_ã|}Ûž¯Bò!nÕÖDn°Aþò<šü5,S™² o³Y1\Ï3bwËö>EwަI¹n”H"z•äÌUzêw( ›íG&£/§#–£os!gL¢û˜±ž˜_"À‚Ìá<븜*J ùÂÍÚÞiý4qìkÚ àÑãâeú;_ÀZâaÔèP·þÌF?9™¬-…Of·ˆƒwŽ ­#öÛA"Ç„d&|ÿÃà«W¿…,;>zú_|úKÏ\€¥/FëZ¨MÂâía§l­úe´³0 ·_;^nŠÑwcÎ7³¿<Á˜?bG±g|ÔÙ?“£Žf¯g¤åèêBg׃9Øš@+•Bİ®K¾PeÆJ™© .™$Þ³žYí2F§G—{FÿŽRk"좕)é̈êÑf环.·X–î™)S‘¡eîûzЍS T'.™Þ;f¢5b©SNÐŒÏÆß¤wÑd¤TIêb°ÁÜ= #Â2µY’˸٧F1™²¯o+½Û…yοˆo¼9¹‘s ™ŠÒnKØ’îÙB‚iýìR—¡1­}͕ƘsÚC4ø}ss‘pÁ.—\üR‰5ws: ÅFoí÷«ÐÔG‹…,Zš7ëxŒ\Z”JÛ”çÛèxSn¸´‚³fLkt©‘CcÔy÷)nÅ´šká|òš¯ü×OéH)Œg%Ÿ'=þ?ùôïrYq}‡o¾ÿ¼½åþ|¶}ZÖÌ—˜Ø£[¿Ø$Q×ܲÕpE%*·0õ›õMɬʹÆÂV’0+ e°ßÞsæŒ/\ao·ò ¹(·Î$®Zí\èÏjU*NKð±›ì¾rÙ#q9òŸ>_€kÖŸC¢¸Ú ÕO¬}„8§râÅñ./äëi»ºŒ%ŸSÛùWQ_óOž­¢¬+tQ¤ý \¯°”ÜØšuO‚·Z.—šiù·{ù»Â[–tÞOÕApâø2æ¸lOx@æäÐÕº|*ž·ýŒ?ÿð¯xúýc|üìS<ö~¼ñû¯ÿw\÷=XÀL,ª&íW‰8ÔQ%GKbQ_ᲆ„Š:¸¦œ¨»°ªkððÁ ð~›Fá¬,E±ØbNRå¸UäÖ!åè>¼¶oÐý ¨ m·xp#–n‘Ÿ)äY»dFd Ô §“ùügX÷Tn²„†º¨îtjœ.`ƒí]p*Ñ3¨à+ä.!¥‘¨_w©—\ÎwŸGp¬ô5 ÑKKÿJ*Qt×'dŽ_94úµUÇ•‚DYô-Euh° . Éí6uòýÜïÞÛ¬pØ‘ÁöÌÙ'–KmÝÍèq&ƒ½8zB¨ hf<ŽO®ÙLõû+–vÖ¾×ßjážwïÏÛëq´%(€%¥‰«*0®s ¿@hš9~WRrË2¥N°7›RZÓ€3P¤ÅÿzRÿï‹d«t¢ä-Ø&.LÏèÿZ”‰¨=55ÑhÞ+F…½÷²æ<#ªËŒuçȈOÌ3ÄO·ó$ÒhĆߗ1ø¥5?Ž/ñ£¿À£Ç'$løÃ«Áëw¯`¶»»x z<"=ìÙˆU†¨ðQ˜ñ5Zþ޹àc fš Rï[*Cqìôîê›w÷s¿Ôñ FFÆU ¼šyjÛ4ÕjEdBa÷¿½Û"u[ /*—áሠ]®9Ïl­#è0ÈÀø…Kªã©£Ø–¸:lĪ£€ÌcI˜Ob&QÖ²HNwºnŽ5^Þl ˆ…7pîêÃK½Pe÷Ìf»Åi0æGa!‘EH¡¡hÍ€âæüô„Ëö_½ú އ?ú/Ÿÿ÷ç7øöõ±íߨ¹1h}™sâX>/‚ „Œ ƒÇ—„+&Yh—Õûb“"¯ó—„˜† \—Š"£ç0{àv’?[çbº_õ˜Ž”H6ßÝýñ|M+Öl²DÒÞ ùU•–ÜéÖŠ#›Ôɾž­™MÞs˜$L\ÄÕ#“s¸Ü¢TPz¯NL†¨.OP*ŸØÓ+f]¸$ÂA;•ZÑôÂf¶]2DyºFHºlã¼±£jˆIŽá%ÙêLB˜„R‰×àøÄ!6À7±9ãI[0Ç×uE¡°s€¸ìñÖŒYø®œÀxivóz.m˜`XEÜ…ìæè>’^DϘi bÁãæ£sú£ý^Ÿxz‡šåѹ˪eo*­A8:ÇÌGÄW²,€f—oØ_êt@È/ØxÚã÷&óOljn"$5Ìn‹>ƒ)<‚Ù ¬·<)ìíá"C44ý®E I‹Þ2Œ%ž·ê46Ü(ë#|ñòðìÉ $ÛñÝë¯ðêõ`Û\ü¯ –t]a² ]7å ÷Ñ"­ te¤Òé72ðÿŒàõ}c+˜YׇhœÜÁÅkKÇë^JurVæUqo3k#f–$Œ ¶ €6gvs£¬R;¤¢‰?!Ë‚t9ûvÃX$’¸)-[¬ÎäŽÇü¹Î¹-Æ f¾pÆÚ•7ªÁæÅoúò™u…žÀz@ºîÅžÊÚHÓ,ŽÓSáäÐÅæžQMìðã{ Ã{¾]úêm6¼©¶'P~y3§OðÆF©§ý4φ7ïÿˆ¯^ýç‡3V½ÃO>ûŸ^@eOqWÊúÕ‰P;ï>úJpÝ&mæÒVTê1£"!+ÄÿUBŽ:Â÷04õƒ:ÁGjè0Z’Føà˜Z¡låyBœ!§°˜!;BˆÑ>üõP¤ßGY¡‡<Û.‰¢DëE’ÚhœDkß!¼BÂZ÷ 4jmÒNWN¢BÑÿ®î‘ Ì„V¯«gâM8þ.¬%AI Œð T¤¨„Ýb-ó Šù4oÖ«µZ€~bÊ›Qñ+´öº ¬ó‡9±,>Ã` _0‚ïq•¦áüm&þ2 ¡(?omÄßï»:^†s*èm²Æý½RýD… j‘PÀ÷©ˆ–Ÿ5 óŸS¥§ všÉ-[Èè ! ÕŒ*Ó­ßzŸŸrŒ"³ž×Aa¶‹Bö -iNlJW’ÁH1ŸJS9ãiŽ¡ü÷ì×QçÖ0ëIÉ]†§U$ç4ÀˆLÈPö k4Þ»Lë࿈ö,\ßFíF£áÌùzSÄüåÖf ÕÜ:íQ]Äݶ)H(— … ²eŸÃmãˤ£fT©)F/ §!Uô¢P&$"›?×Bä—1Iñ Ð(OÄs/{¤ŸÐ'ÓsÚ†©‰·š÷Ëâ¹î?êù»Å¢ËOâ–8ÎoiRR8Õ¼Ï Í”]ÛE™÷&þ3oÐyBŸÐ:«C)ÌoäÛ =“‰î`D|=ê,4åJð¢Åqrä£àm­!ë)ñ^ Q˜ã©H€ìáL¤X×3}ûYš|αyªë#aæÁŽ0 ëÞTöæ²X§PŦ:üœ’m†Aƒ+”nÛ5y@ Fãîã¬ÂdhüÞK=òWfχƒcÂú“‰KG¥f9$6µó÷¶êÆë]ø^£÷22n¿ßZ#—°,+ž>û_¼ü{ïïÞ~?|õâ²½q£zŽÌ^Ã~¹@hRì[ê\5“I÷®…c"¤ê5ÿñgÔ]„,dæ’T.„ ¤ßÀQr~s~0ÈÊT›ÀÜ‘*ûŒÝ²˜0¿¹eèœÍ-ŽÑ >o õ³¦2¬S·ñ„<õtÊŸîü@ FpîiEÆïh^^空(d= mWØV•…}D^“Wà"ëX8·pç1¸ˆa9œÈö€´% ñ¸´>x9t½!RJ>v™z¶õ IDAT‘û›&¼ œ¯Øâpd«H @è²!t'ø9ö¡kÝœyü—ì‚ïÞþÇÃ#üè³_âÙ“ð£ô \·3ί‰B’‚0"uõ7 ÐâgB£éªM‹X”[êO"®ßUÕ¢Æû·òXËgŠ›³Áb˰úzÉ!.ÎCÕ™fG«-ÿ\=™'\nB­ýSÍÖaÛµÙ°¸ºãØ1§¸'B­vÊú¨=Ál”Ñô~ï6Xá¿ ó2P"¥f¼ðšÍÍæŸ. ¹õ&Ã^'ѳ6Õ0—T‡ÐŽzk¨òy%gÛ¹€e„.<¦ôñ§mI<Ì‹2͈S©daÔ©EbFTà÷óŒ‹R#d39N¤™AI;‰ú-ÖFôŠbˆöLÝ#•×ãf‘hÔgåÔžG¢SLêçGÀÑhP†0w_Œ3ü·k„ÜÆxï.ÒPöM[_â½½}—NhkJÒ¡žvز “¾íÕóJ²°;€´=FøÕ~Ì­@Õ,QÁš³…QúÖL¶uergÕ¡9ÖØ`¿9ñ«pa°Ö&ÚΙ’,Ù»O^ÚŸ' KY·a P¢„eÁG¿ÀO?ù{Ž+îÀ7¯ÿ ï^¥‚Á¦|çe­Îž…+²_­ ]Ï,]L|äFÏelj©8xÜu”Ò…âPWωª“ CZª¡ƒè­#!iP'ÇL\˜U-BòX™ö¯Î˜^G@4‹Œö½qëdBvŸ™à·W“\ÀYåq.èz€í[Ÿ'ai׋O'êYIR‘’Ír8`ÑÙ.°ëV")]-Åå‰n¡yDÚù>"6ªÌãЧ X¤ý³ž»b}‚¨HöXç"ª¤°ÐS;€Î—÷øÓw¿Æ÷o¾….ÀógŸã‹OŽƒ>‚Tssw›óÖsIˆÂ)T2t¹<ÌA \x4Õ£\ÂÂÇÈ)ë?k&CãØ< ¿ÒÌ&‡*ºíº¶§4²MÉØó“ÃêüßýEeî æ ¢ ‹šdë€õÄïGåS6Aܨ„46ÚK$ ddÊ7 ñßY·Í™¢:Ds#.q-.)Ëà¾+‹äÐèR0 Ò±Ðu”0±à × ‰x±0I¼€…N•¸Eæò^œ~ò#~æcÕ™!±ÝJÚêæ¾ƒ Æ;Ei'É!fÔU(¢³"JO¥^ÈMJ‰µ)’Ùø>˜;*7¦!í¼qq…ª ~¿…§oÁÁñ#?ì›ÃÈrùk)ÙõûÖ aöU5›H§o9È”ïJ7ÏÔñ}¥°~Â?UêŒD€0}å·Àq·¾ÅŸ%Üè[ ´öýìâ$Ô:L“àÉÝK|þâ—xtzŠ=]ðêûßâõÛ?b·KØã~Ý´Çfû,+ Úr»ÝˆF8¨wýã.9m–Ê/à…1ÄAÑÎIjfÌdï2뙇ÅE§L…= y'R#¿ZñÅœ?4wÊ”ƒ%£Y»ÀeR3ÒZ»3—¸äŠdGȺÀ.Ũ—8Zî`µ›Q77®Y;œ ‡iÛ.WB«´ýùzì«ÚP„Æ‘é ¾D Ë= ªH× °o®Ø˜ e9»\&q˜ÃÈѨ ßh »¢D—®…Íp¸v˜=ã7æÔXåÒä".ð!Î×{üñëÿoxÀa=áGŸÿ/Ÿÿ‡µðw)^®ÔY›8Þ›´xÏñB—ÐÙ7$Ú$’)ˆ\¼Å’w ½Ï^Œ3w·þNLÅ{|ñò^’À§Œîœ1öÅœwÄÀ¾Cu¾²Ì…4Ó^œÌt- ØV¼ qq,L.(ÇÓ…=D_a­ÔF m«~–Œ¬i£b ŒÜ{1 ŠÜ7háu¯º‘©GYÔGê/ ëšnD£¹àŽžF"®á÷ôÔã¼Þ;:9=‹2£èxÿÏ(¸jë -@ß•ï)x¿9š".è¤ßAÉ;eÔ`*½€ äÑiªUÿ»’ùÏêEo2Yd2õÿ­[¥Y(®{‘ßGþ)’‡ÚYÙ)&&œî¶74ö°=·QâÙ‰¡øU.t…lç2'´ÉQ_Má+÷yXÛÿ}j{Ì„‰î|>S¢¶N8§7âŠM¦‘Īòb\xÞ û1ˆ£bƒºƒuÝ­ú?þäñü£€îxýök|õçÃe{9«‘–Ø>Æ~…Ú†Ãqu +±™ÔÚy™PÌfá ‘#«¹éP‰ÍͤÀ/Ú” Î<ÙXpyÐhä,ŠLØ÷&±—é¾Êá×;téù‰*ªâªe,9¬°mJu”ˆ Ê›„²0wˆ2+>VHv³}™J|/«Ფ ýÐ×~ZÅ(°Üh¤Fk±–Ãñð?ûÑÁóADðöí÷ø÷?ýÏØ¶ËpÖš*„X5¤eƒ®GÀ.7ªkk– Ì÷Gu·ªç‡–1ò4Q†™1¡t0PbŽ;F„Rè=©Œ‡Qâ}S}¥wì*\2v‘ðYSxd þ|AQœLòex8åûôüPÒlи)}ˆÎˆRV45 F¯³,yó‰Â®÷ÿ½°+í‘ã8²ïEVÍðJ4ÅS&Ö2¼6lìÿÿ^,°€m¬l¬%íR¤DRâ1œîª ¨Ê̈È,[€ Qšéé©Î#âÅ;6{CÜmâ? á`Sf=%çË-Kø¼ìâ«fi¤Ç*迈Zð(ÝèÍ6ÈÓ$lþï‰;ª€µZ•‘œ¿4´©­åJDåXŠ?’3ÞxW¯/1_ü—Óüòé+^¿}Å:( F^\cÿO¶?[#‡ë€ÚN5Üë¶ÆèqìMÃGå@ÊEZ”*J¯4q„ê&꬟™~D$ÎÀrFº5aMÍžM{nØ«ZÏ•yÞ̶±( kM"ªª.YÉpî0zÎÑ2Šá.w#UoûÓ[»qàqšüáÚlû*Q„Ÿúô)>г 爋ÈIe°sã8ˆ†ßì v ܾࣽ-â÷øLø¬M7Ð.â¸þs[ãhd^ˆ·m.5‘¥¦l×:D|#eøêjŠ g:ÆŸ{¾¬Ý…Ù¸%°:Wô ÿÈ¡ÁŽÀƒšÃz ;°CÔ®ƒoÔ$ZÜ¥ý[•X‘±™H‰¦p´ f0¶Gpörum¦yÕëÈ<­5Ï£ËÍÒ*D@y¶µ}ޱø·I\Rýº´=–ÑR±9kÞ®:Ú®‰4Ö~ æþ»\LWøâê×xø‹'Lx÷á5¾{ù'Ü|úèrïcqn“ÎÄ]“kF†€Óä©j¸ø.¨­Õ¬=î¤ñ{Ѭ!6†W–æîÀq²ê¶ø¬Ÿ4ã0$*|ǃ‡òåMÝ 8¹x5_BE‹"ÎE×ûY ˜x¹ÛÑ·ìxç3˜ÏÞŸ h LÉh ¥- ÏŸ +*lï‹oN³6›¯§ÎF/¸*T_é²ÍWw4‹ *Ð!Âï)ö3qè­x²Œh‰ ÝŠè-™¤oéÔÔòåBÊÚÄŠ"êâ4ãšò¶Eûw™¬ùšþ³C .e¬žßZÝ2œ2yí¬èA{öÒ QŠn‚Ñ—%­àÍ:>[Ú™àl#ÌôÏ 7à¶=qb5a.¶qòB¡ftb•î ²Üquìk›|¦ÌŽÃ-u°?/©b˜r(µÑm©c`cÃØp± £U­Ç¼¶¹Ó³Û?Ü÷\ûý¶»7»B]lrQ@=Üðu®+¼|P›»s ªñÇ0 *[A«m'éšò/£›·{vÕ¸oË´QÇ.˜Ã}Èî>óšA¿s[9v= îÝz„ÇŸãÖÅÖå/^þ?}|…%/U7¤†ª!áDRË!;~XÏ{¡#HŒ]>°Z£Ví{*1…* ÏÓïùcç²ÂeÊcVÕ!;3ò«×ÇÝà¿%"W°sJ<ì*¥imó„tà´qFqOËÙ‰#uzázëÞVŒžn Á?Ý!·c¬­å)åwíj Àå-`žÀå„|>#f¥á¸Ç£t40¨»«(…Gþvöçn=GÓ6)nÄ3¸«F¡<äúèñ ~]Ʋ^ã›þ ïß¿ ¸º÷¿|ú\^Üvo6‘•ù”Dâ3º› R†vªý¡¸Ì¤ïµN9ð†õŸíOvþ½Ëàñ_Gì±Ñ"¢;¤çmÍOÃ=‡ÁûýÒ™E¡«@³âtöË&k£ê‹„Ñ3 ODÇ_Ó#úÌG£hm†@VxIu§Ðˆ¨QM4&† ¢™8:Ú%=ÚÑßu»)ò?ù”ŽXY­Çpî³Õ»SH‡®8ôÔ{:s„œwÜJ“OOÛt óUwò©§k0a½Gî~¯è¡Ü…Ü:L¸et7sªÆsD{ž( œypoÒ¼·ÈWõæ&NòÈgB¦ ãôüà>Û 1IÇ|õÊcOêpÕ”Ýú ŸêÐ4S ÀM ýïUµ;+›µ¡ú3¹ž~¹34 ñü£þw=º©»©°L¸sû>žùܽw…UÞãÿ_ý¯ß‡¼~ð¹uHœ#‚ç¬gèr‚ÌS…/eð謗¨µuM…²ùHqÁèÌh´Ðón, ¾«ÔÙ®#kØ_(ŠÑHÒ¼`¨Î¬¤{ªCÓÖ\º¸ çKi²†+òp0–vá4mjôÓÍfø«8èpÛ”b͈Sïœfàò6Òz½£®ÌÏU¦ÐÓp0þƒW©Š@/ocœOXWuh#»æ )Ó¿Çç÷!ç~~÷¯Þ|ÓùÚóé9¨ÖÌzhÅ)ËÈÞçÂo£û¼)¦ÉŽëT ¤lí:¼¿â‹¼v|¾Èk„8VØ Ñv|»-ww Òr?y`²^U†½WiEr´Ù·“ÁŸE4C—ýòÔà%Y¾V}ñ¡í¬M -i+‘èÍ'P°wL¥¯µï ë†FÙ¬£lˆî>ÇÔÈ™¡vè /"* 1À@hùZÍ÷Sè³²ì¸XÆ[Ú•,ntÜøñžŸeíŠhã÷UðAªsÕ5GU@Du}ÀçwqÁϾǷ/þK¾ÆÅ|¿xŽû÷Ÿ Q<ïq_È^p”~Å ªC‚kÐ…Öœð­'¦/‡3ã)¿‹–Hùï0\SƒX —–A¯nba“Äm Û •ãz5fÿUEÌÆo«ˆÆzÓ䢅ÆçÔL"ÒþùÍ·ér…L7Ð=MG"ý'‹áöPAn…¶èÝ"t1:µ"+zQŽrÎì¿gÚ_#™óÂaæÑÑ1¹…Á;YÌÆkºì í> â[7o¼ÅË¿ÆÍé§!-SB#–¨®Ò8²ïÆÕËÍös1Ôœ¯˘CæÀM¶b\ðÏ- Ò‹}ÃÎ7u¼ÓeëÈéè öã7ä‡m¬è-!œZßë)—àr]s°XjO¼Sºiϱ‹òš7gÃóâ«:ò¢/ÔÐ?±¦6^\Ë zºqvdÏEiÂ/Á;èZÁnDoßïhÔ£ŽÈÏn¬XÑçÎiÁúðÁ)Á±­ *ÏaH˜p2?ª<&@È1#K>ã݇·øûÿþçåwïÞÆ¯žÿ_ÜŠ$³»ø—±ÓS»õæb éùÇYCgIñ`orÌ´±¹Pú *‡´Ù'/‰þ€Œ¨^œ²Ö›:¼X‰m*Ç|Bš6oQaxMÚ3`£¤‹ dB3P"—í~¥­t:\záaŠ]÷· ¾e(}<âQ s4ž¯ôœHmIažÒý$·Nì¹VDES€cˆj1œÑj¯Owb  Œü¯Õüþ•k©Uþµ5Žë߈Rë[è?Ĭa"ÖëÈRtÈ-õnËÙD"—¢%Ñ ¤0à^6 ÜØÅ¡†3‡{×"âÑ+;ì -íîj¸Å=ÒЈèE9Óý,áXàwÝ‘HŠéR@1î–²ç*ÙM«!ëµËSwŸBð0ÇÑïm¦ÔF¾_÷~O”õÿª–~Šñ„cÄ¥ï ÉØ^ÑÜÿ‚¬GMH%[žË$3ž=û¾xøÅ)ÿˆo_þ¯B´Ä;º?Ô*õÛèzSÙ3tˆ›Â2ƒyÓ„eY\–´»Ü£•™áF•M1Î`Îî&›·—%[;!ðМO"½;¶ØàrºC.úÿù” Š´ìhñ$mãIÅ÷È¥ Áâã“ÒL y9!¯«Û€bV_S;Ë𽺂`J@èr†®--§t¯ ‹—Ž‹ªî¡çý‘`šIÀùÈy¿ÜÕ¶\L+G†¹"&Qg°%C5å,ºË®o%€–ð1XydPð³uíѵŒDÌ¥±*Gl4‚î³Ù Õ‚½%1µÈ¸®yÁOï¿Ãüj³'ÿ†I®ðåÓßc9?üȧý¢Ë¾åžbÞ'¸gÞ[Q…á6;\L-j¥®`ƒe!44ö(ÅÛOÍøvÿw â­`ñ áØÒUëø¸)aéTÆžZ°Ø—iž!"Èëj,ªš×*ÜÎÈVŒ6ÙSeFåXŸX« ƒl{ms@t²/>áÈ»þNû:ÊÆ–®¢ Ò¸C]²ÿ9¹HMbœ™»í ƒÒv}ÓFXjà„헜ť‡YÞª+´EöR wµ)é”êL©*ïÖ {8†F¾¥(Má¢Ó—8oìrÏ(¼×¦²%±©‘Ö}e¨f×7®êi^{C–sû¬ÛúíE9>EN*ò©CеÙO+T“§^÷föÚ6:ƒ§fÀ¸^ø¢Bvn2è©ØÏƒæ‹lÀ$¶@ÚÐÍxÖ›ª¼rK1]dÃs5¨,½‡­$©¾£•çŠy±ÑÓꢀÐ?ÅÀ^²Eg÷U̹5Ñê]¥õJl–WÖ÷\»&ª4¡2|åÏínË@h}K·õb=¯yº‹gO¾ÂŸ=Câã‡wøîû?ãúú«®æ1*›½¦MHµžÚmŸÊ‡'ˆPä¼nñÓ ]ζji…RØì±„Ö²p­—å'ÂÀ£Çflky9 c ßµ4ÏA+ŠQÇz8WvÓ•åkÕÛ@F‚HÆs#Õóõv‘ÓÍ+tY‹Þ™B×ƒÉØ²ÄüWgŸ&ˆym1¶yT…p¥ÎüÜĘ’jJOœJõÑú,nÛ0Å=]Žàö¥ÒCЬ+¶#£Ø“… »½£jøÕK—២d,™º¶Ü§&mka &¢6æaÖ>áªðêc§Žâß «øl ç×®ÑL¯ ›0¸Z¯5.°ep=o çWEÎ2¯PLÛºª‰¨ £4%p¹1÷Iƒ¿ Ul ÃÉUj-ÒZeÓ™bZkáZÎHaO rõÜ. HÌ£a8óm!šÕï¡èñʦҦ„å§gø„´V/JÚ<]áþ½çxôàW4ããÇŸðýë?áíϯªok[¹9ªTШ6Ä0ÿpâ|+©½IEND®B`‚goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/images/screenshot-xfce-4.14-01.png000066400000000000000000000763521414415210700273630ustar00rootroot00000000000000‰PNG  IHDR’²ALÕ›%zTXtRaw profile type exifxÚÕœYv$7s…ß± /ó°Œçx^¾¿‹,Žbëo©ýbµÔ,’U™"â@¤ÌþŸÿ>æ¿ø§ÅjML¥æ–³åŸØbóÕ>ÿ<_÷ïûOó¯WîëÏÍxû…¾¾†çy?_]ççéã%¾~>¾þÜ”ù¼ðõu¡×/¸ðý'èÎzýz_}](øççîõ½i¯Ïõøi:¯ÿü|ŸÆsíoßÇÂb¬Äõ‚7~,WÝ%<ÿõûU'ÞäBæu‘¿}È?¯Ùë5’o‹÷þêÛÚÙþúyøºÆæ×ò·5zýÜ¥o?ï·ñ_Fä>îüåÇù`?ÿóiíÎYõœýÌ®ÇÌJeóšÔÛTî+Þ8XÊp?–ùSø/ñºÜ??•)N"¶ˆæàÏ4®9ÏjÝrÝ·ï×é&CŒ~ûÂWï§÷g5ßü AÔw| -,*‘˜DMQñïcq÷¾íÞoºÊ—ãÞq1Ç'þòÇüôÃóçýBç(u³õY'Ò‚qyå4ÃPäô7ï" î¼Ö4Ýõ½̧¼±Ÿˆ`ºË\™`·ã¹ÄHî#·Âsà}ÉFcŸÒpe½.ÀqïÄ`\ 6»\v¶x_œc+ñéŒÜ‡èp)ùåÌ!6!d‚S½îÍgŠ»ïõÉ??ZD¢P ¡i¡¬ùSb%‡z )š”RN%ÕÔRÏ!ÇœrÎ% £z %–Tr)¥–Vz 5ÖTs-µÖV{ó-a©åVL«­µÞ¹içÒOwÞÑûð#Œ8ÒÈ£Œ:Úè“ô™q¦™g™u¶Ù—_aQþ+¯bV]mõí6©´ãN;ï²ën»rí„O:ù”SO;ý=jîU¶_¢æ¾Eîï£æ^QSÄâ}_ùˆ?.åíNp’3"æ£#âE ¡½bf«‹Ñ+rŠ NQ$OÔ\Rp–SĈ`ÜΧãÞc÷¹¿›IñÅÍÿ*rF¡û¿ˆœQè^‘ûkÜ~ˆÚê—Q ªPkjÃØNI Æž‘oä]gÎpø(uròép‰VËÙ€"«ã˧·¿Þ,^ûô~xôã|³+ɼWËC¿ üãë:@cYÃ/ÞYvÆ©bür‹ÿ4¢o׮εÍ÷‹“t§~¿úû~=~c ÑȽ—¸Yæ=_bwµÎ­¹t.:j߀ߘ¹…:v©‚l„tV9šj‰8ó>³®Óö.¶¾{â½D{gësHd“Û™àRh®ípjq‡8‚µ]5ã!äQ iØÚsñ{ów$ŸK?at“|_¬I/e“S-'§Wg:iž8G4ƒŒÞäU/«OÀ¬®TíNL­N˜æt¾§ð*|‰<(Ž“ù«Ífcêq1·Ø(TÓ#Sì°Vl.nñ¨›97²³úIxüñëÞ~ŽêZ³$G¶¶°Ça<‡’:³fF ž";ݲ$yPvÝ­Î€Óæ£ˆ8È1¹^wcÀž±œ¶WJ{ ¢¯µ‡¦ ÕKœA ¤Ê€yeÒ•TH1¬ÊÚy–>8žûïaªA•±². p˜¡Ãk,QY»ézÙmð‘6“Y+zЉç¸l]s?¡gÒh‡•Z=¡®: ¡èfÙ]©5†f §fÉËq—>VË–]R[›twI¹q ;¢ók.eo5™‘Ͷ0»-5¬™ ÿ]ÉßXm»|O‹ï÷^‘Bv5øp-V̪H¦;Ów )ƒ2ñ|Wz™- /† cÎf±ÕÕÙ{<Á̽IIÛPMôŒ¥.Hš²V*¬—=ŒÁMR.µ ðûš° 6îA«~ëG£nˆÆœ ×b<,Ág›Ÿ@‚âX=ç7H93׬`¾w£†Q@44¥h¹D%=šîÏó rmo™öY; ßJiÓÎA½Ìcs´nno?ó:›ÚfÙk3D¤‰š`…Ùv&¼›ŸÔE¨ ŽcÉÁ™ÜH¢ .û¸›ç½1”Iü³GMè_ ?ÉTSÖÛl^;Séb…µÁW’hHkÁgó*¶}si1¦¾À#çóX0]Ñäæn,¸M#øî€™Z ôg#³ML€:©ÀFgm ßSî;Æ\)Älf¯‘2"–PÙ|Z“{AHšÄò;ÅÖˆ@}÷ý]¬Õ'„›T ®ÌHE Âò€0…$ÏŠ~*Åæ•2mdÕ-Qa B‘|Jc¬ç±6ÔE¼ÐÜ”y†«­>.1IYMê ÎpÞ„¯;…‡Z¯Á’’d¤I›HH{`ZÖ}–“Ü%‡ }Üð*Ž ÙƒZ…×±‰œ˜±–(yôF¢–ð÷BJl;w0‚šÌÙ±»´>ú½vñTØ>#'g,{æ0 ~Bµxb3rªE!T’G3É!xøÃnMT àm‘V9³‘«Œ2DqD‹Fð›…ki´|%j ŒÏ’e„R²(„qµPFå¨õ{õÎ@¹°[ú`1a\[Y¾žáüé ¢:vrq”`<¹A-D…¡Á~˜ &žLI)ò&ê$Us¹µ{@ªÏ*}7/펃ÙdV„ÛšÉ3Ò½¬xì‰hò≭“ýDD¬åž×÷%h:XyºÉR‚hÖ\’ït<ÍD»‚¦‰$ˆ§8 \Ü_@83N´P™ü—Œ ¨W¸(Ú¼W&wa7”pà6í؉¹) ¸IìÆ\údok–<´¡·€NF{@îŽ?∰Å/ŸµßBy˜œtƒRй¬ìÀW•Ò¾ø÷­r?vLÆ¿È+ŸÝȺ.´œST|vDvh\Xìˆ6“â]_Âw"ª›‘C4IÌÅâ ._ïg€­2ç†2_Pði•v ªï›OQ{Þ‘B¨s ±(#{ÐÚ¼›z–ç"…7ÔËüÐB² iCBhV\JÍ ñá÷ÌÛØ»ÔÏBQ]ØVU?!ê% Xuü vÿÊ##EÑIIì+9‰Åg>ñgP::¨Ð>~-£]™€í ¸‘D÷§OÜOd9̇bt‘UŒvaþ–„Vê$c6¦]I£¼#yT" ‡Ñ€,vgú‰Q³0DŸ#‰VaÛ$Ë„DàÂ3¾¬C‚­t×–f#%Œ/£Bª/!-%¯M¤–ЂÄdw(eÁ”û$8Ú{Áoj'¸¨ð‚Nû5à¿_±ë7ëP…Z'¸ „ Oýâ«ÌµA ™ÛtÀI§8õ„ò…L$Ž…Ñp?pBB7Ô!i¢Í:4.£ cÑ~7^Ø ª"Î7 W/ÔÄ|§)l.Œ×Ð!§²)Ô;S†_(Áªö¸–$ª Ͻ6·*™®¤N• öÙ}»é2ñ" #U0ŒêåÖ;ƒö·úUc4ŠÁ:íXòOðùêæ|çH “4ÌŒí&wOS“œÊŽ©ð>CE(±„Aòwj›/°`2Vèi™A<¯"I·§ŽOú ‰®¢ µù tam `ßÉ q\tšóqŠÑÞÚa‡¾©á"ýÌ bsˆÕ8ß,h^[P:Ü ¹MÅàß2°Ñˆ,HG¢™†ä±N8Îm1hQ*”¥ ‚Ä£](ô© ]ÞŒIèÚ“Þ'dmx>©c=á4pN¤7•J!£&|f_NBÆâõ±Ñ7nœR—ÉpŽK Z×q“ÁÅG'“Zµ|øÚ¨ý ´ 7¼˜vÛˆ!õ†å &B·+(P¬­¥Úß8ããRåËûY˜ìîz°j[áï¹¢ÚÁMPñ„«?¾;ç)«b%úÎ̪§è&”ͤ²Žzä`A{Ý eÕ5ÆkÏV•48FÇ;À‚ûþó;oúq!¨›B@%ïŽnZ³#“õäƒ è>µe/TZ¼…þT*kH§RoÅ Y€â¾Òú¼0²,±%Q9Ú.N0%uˆ®€õƒÜßPdΨ¢†ü™äJ·âÚFBWÃoùŸH?`Á>°€{Lв\ˆQPuJ;{©¿žÀŒõlï‡}eÍ©Ïå¤Ðµ{s„Þ’;Aâ:$"?2;#û©—ä22p‹K/ˆ^Û]lDHcg®G`!ŸÃ]ô5¸£eÆ1ê9˧‘q5lŽª_¸¨¡¹ÂÍÜiz HmÂïTò]âKàQºvÕïb7jûä¤úAžgÅcªÍqÚ‘ø‚Õ" DÝ:RÝ\H]_2uªÍû ];90KUÐaÏR árX¾Ýó£%ó)ÿ2g’ÐD¡´ÉñTn¥Ù\éÖi/1\û2í9¯l ³Hmд¥ÓF¼èÝqbÌÚñíM€š”ti:Íó›ÍX‡!s‘}˜ŸTÉ)µˆÂ £é,žkH‰ Zyr¯$µ9¥ëPþÁWs_$¢&˜½yоz2Á‚³ºâ—µÕIíAס€å’åM> ädó[²îžœNtH[ú¦2`€÷o™ÝîrÂT÷¼?’-úQ×Û†Å#&Â_%(Öbº)ðÒ赚È8J‡+¸¼ëmóÉD’¤U–žâI ïi@h]K; ]?:߯éW”\k&9WBB8¤²];&L…êૃZ/CŠš[ãySÍPBdB#NüÐ ™¨iDo…h«’bgíµ¡óH7øŒ—¶ùRÛ.¿×6J¤©AHÝ"0*X…lU_=JìÕþÔöxËæKyà 罸#~Ždà+b£Ïœ1ÈÌ¢ZcÒa¹a¯ìƒžÍ:rü3‹µÒÕ'e¢3Cµ?Ô„sè·¨°Y—pWÅ;n]›·ÂOaG”AGv¨-2¸j5vÉæV7 v½ê`·•VÌ»ýDòÃB‰)Æ0"ú&øêEÄÒ3NÅÊŠ#¨Ò©ùIú ×!ejŽäiƼÎïnfMŒëô~ä©#ˆÏI¶bæÉ®ôH8|Ärð8c[P¥TívW1WH¨mNëouOtÇ’WM·ZÖ XAƒyækcgèm¨dµÑ”©þ‹à#ëÈ¢èàb3a¸¸%sÕõц:Ū7"‰0Ò¸xœ@jÈlüšÎÓåèq_‰ï@VgͤՅiz”ŸÝèh4Áö¬“ T“6„!HJ ´V?:Þ€¤- Ãó©ÚãR»DlÇv~.’À«-Ç#+,+€„=‚=Ym` C,³Ôt·#|€&’+ÎK¬ÓÀ4Â(ßÝ›½mY'Pÿ4kÀ] ü¬uÒá\0ßcjnÇ2 u…£:E@ÀòÚ}Gà` ÃU$ ¢;{{ûx´…ùõQSjª ý'ƒY:R$Ï"òÝ·¥VA§£Ñ‹ƒn醒àÞ[eD$ñ:“©IÊâ(fŒw{¿©óšþhÌï#ÎWÀyßPAŽù sÊ/0çï ǼaŽ·[ý‚륟÷ë%¦ èÁê+ Û»€þ‚C=Z5«i,1‹ac(T³,ÆO¡Ù·=|ÑÍÁr7L™™Eá´s8õÀ ™ÇL`‰‹¦=jà:wKØE-¸ïøÌä½ò#?·"¸³72‚h¹µ(G@µ”#«†ì+¢6ÿÕÑ2ï9z…­©ÛÛ–Æ¿PÍ{ ½E¿`›aE”·3AÕãÆ‘…d°äXÓÛþ= I • ‘Õ@·óÁÚÔ€‚XVñy«SjÇï@/ÛÕö*{꺻æ´=êdijÿÁ›€™—ÿÚ¦së½MP,`Rîž÷8€W׉vDï8X¢‘»Ç‘û)2ÆÍ’9« ô·,1… ~ˆÞYJ€|&-e†öÁ–Ú”†;á›v{ øÐ*ÅØ ɘç…Î?ÔÒ0ñTÌè’!ÝÜ5Å)¯ª†pÝâî £Z“€ÚÕCJ” ¶Î9m[ï 1HY1† ÕÒ89_ØZ¥#n_/IhZþ÷>ö³5öT±,ÙöÝ”žö­žm+rÎ=;ÝÀû˘׹Â2¸ÿõü~”#¬¿žr¼v‚®±~)N”Tk*NmyÔ8¬{ÎD>oü •÷$œ¶š½ú„‰Hƃ°À0,Ñ‹a=oktä£sµ—¸oKmYEM-»ã,'<ÁèhÔ·ªC("=íKÕ›—¬çCé¨KŒ¡oT ·8É'–ÒªèB]@@& ŒÇ?Ê‘XYŸ.!hs*Qý;n².v´Çç± ·Â’¼:e^Ä-È= ûX”ÎTË^­†»'µ¿¨sàtãÇuå!Ý¡N‹…Ç2Ö[ §;w.¢æÇ)ðm°áß ÊYQ'¢ñ¤ YjU‚ /¬Ïü‹œ‘#×}° Ç\àë8Eða@GãÜ2m5ä–¢ö‘Žj>V Fêi–ØìdLX±$žk%F1ï’©pY8ÅR6÷í38¤þ5Úª©|èü/ªÑr2œ„¨ª:ý‹ÛÉÉ/m·«¨Œè:Êe{µ†,ª czŠÀ+r}8D`t‰ÊÄ;Víç:¸›{Èf"Âgî:b^‹AŸ¨~OÄÓ?ÜR2ŸjQ•H,žJ|Îüa±ª#•˜£Jñâs¾üígWwèi•h®ýþ¨D¼ÿbË¿á›Á–\ ÒP¼|®B‰æ· p=[Ííe±Á2jðµíŠÔîTê‚—¨åú]çÓR…MÒ³Z°U`«¡þ'f­MgJx`ÇÝzøÌ`¥»ÒŸQɲ{˜öB>ê6 Aà8°®uÉvˆùÀòù 4«ú³ZþŒÅ?gÙÊ€,Ài÷àÑ©ŒÔáš‘¤‹j› ƒPÓ:‘î¼ëµß¢6Y#ó.î¿l“¹ =1<äÖ28jgˆ3RÈEí˜ðЃHL°]Q“+œmªóÌâg§}/]ÂC;©UúÂâƒ÷· cä§SÕè`5€c0cD¢áaÚìZYµj,ܶlFF–“äõêÈݧݸ/âíWii~›:~HWqø ˜ü{kŸ°¿b«Û>'úgÎ1;¡þžžB¬ñgÿ瀮¦÷Ý ÷ì~ÞœÕQgóªÐÐQç;C€ŸÕÂíu:­{JfÊ¿7-`".O¾°ä(™ÑDë8£®ÓC-H)ÈæŸnHz¤?ºt2ó¬-ßrÏOqî~ÖÄïÔ@’ÈI5[]™QæKO»ŒòF+ù¡Œf‘ªHG ¶ø¦ãt’^Ý‹äÏ3V£‡vêvéª}µ÷æÚ6*¨MLÔV;ï@í«÷-XGvìÕªHÖÁe"x§5·Óai¿QSRO5AŸeôW×é9¹q‡Hlà—Uƒ:r¢b{%«ÍÛÔ3P h uì‘{‚a‚p7E!8m¢¸…¸ ÀœûÞëXJ[ÞTŸÕ™Õràh¶¡˜6¶>Ì9‘.èÏu{MI¾š¶š˜ê–S_‰'³¶Rðýzp?Ô¹ÂÏ&²æˆ÷AÎWÀÀø)ûÔÝ€ “I ¨R%¶Sí'qÔ.¼¯n§'LŽm€¢ë”)»bèÉ…qÓaHC3ÄšÜ [ÈÔŠÖ03ªƒ;^ìÕð“՝޷UcU?ö—:E Òîº yõ©¨Ï*S0,ÀÖËäSËZ*äPkçAÏ @¾A}Ç:ŽT?¼úiT'—ÕÞ>ɽ-6 µ¥ÞTû£^ü¶qüìcüî&Ñc×n[ÕìÜuxíË–’6 ÿz2‚í™:1÷ÇT £ì¦ ‡—/™ÝÍç™u–P.`lGÙõ¤§T „žóЪZ÷h÷gÛgRI[F $þ\ IgOWFÈ¿}‰Å ´oX§À‰^ûôÁ òÆ #)üvÝ"gÒ ¬ú„/£¿uûaqú»rähÛ‡J¾ß&n Dº†;[SdIýcá§ë %í|í÷v¯´Ð)ºît ¢«\”g†Åæ`µ?±îænÐÊ–Œ°`ÁwUÓÛ«7,ZS´âònj‹ˆK-ÖA[ïx¨Á8j¥9<Û Å§¢#ȰZï¬ÞIRjçž½üHV:©ý2ÛRJGSÔòS[§3ˆÆÔNÖV-u‘Fu±•uPϳQ±šfwKòýo°*èöœÖ ®`Kj©AO9õd þ™¡ryeíÝêr8*4Ω÷IOÏ#L6Ž uØcªoQ›“2¢œ(&žðzv]Í‹z¬›UB/œ0ïÝXŒX›™Ü¯?í½u`  ¥ †Wê¹r¥äïCO‘ hß°hË‚€q… a8úº„ÕÊõ̢é öÉø^«G¤À}Ó¤D\¸aÕ‚Œ³TOWêÓšˆ;¹¢Í}FC9d“®€”Bàb’™`‹‘ó¤Y|eÜà×jÑqÔ ˜…ùV_;&‹Áœ7O“§Wß;Wïh²b|ú•;Õ_õŸº“! 1jÉC’ð!ò6u¯3S FQ' QIÝ ûðî½!:QWPéFTrSµù@Ñ…z¹Û!Â)54á=O„7, ç$˜±›˜…‡Ù'CVÝᧆ6¡ê’€¤^éž6e?b Ú zà¦î1ƒ6ó•Kr%bT«BÄèØ_Dõ5aà>øçhs’)\™u#0eÜ E'%<î?t1z,K‡ ŠåÜ1oRðêý¦-ªIi"Ûõd¼ Ø«€ì@Cd=ä¢gmÿŠ3ï2ί»§MØ&×מÜÄ›IåÎr”‡ÚgŸæÙGû­v¥«Ê¡ýQ³ò¶©¡ÍÿŠºšØô|GlÌÛ+¨Ÿ?Sˆ°§oäIœCy³>bÜû/FW{ý¡HÐß¼åz%C֌딑¸{uä7øÓ0?jO€ÂÝ}¿é©2=Î`©²5$H§”ÿÕ¯U- Ðé´ pd|[†¾H;MB¬'rv Ý§1Fz.ÎcÜñ)zB£˜¶§h=yF‚£Y ê8ÖÎ/½úV“þ§Ùæ¢f’£gBu…Ž‚ÈÉPNÇËGÿ/=~Èåöõt¦LŠ''PKh7mÓ½¦Á¢g¾ÍÈèÁ‘K’^R‘¼èö:EûiÊ:$sÅõÑk‹¨ÀÈ:,[ÓÏV‡YеڋìêûÈlÔzÖ“.ŽL;ñdÙ •dõ\X8pÕnCÉh%ÁH‰ ¢ÕÚYàægßÀ¾§’²²´‡x= U¿u>—ÊÓ„ùyæN#¿OCp!È,¤M|€´ñitª¨WHør.kétžJ×Nòœ"^õ ÍÈÀèÐÏÉ-³v›¨õ×€W k 9ÔR¿uÈSL%qIèq=E™Õûhk”{ÛÈ1ðIëæ§vO¬Ú„†PÍ”Ù=ž~ægž-ï×¾Ú3ÇŸføšïù>Ã×üÌà3?–Y΢Á#ÁÖAHìupS‡èvjžWÏcSŸÑÒºžž’Â24÷g’'¬"T[-X;=ò½µy=§ú-(¸„`Ôÿ*àÎ˽ϋ¸™WúýqÜÌÛ¼ TÁh5<ö3çQ‘kü«žÑíRÛêdç;=©gºþÒî11ÔókA×s×òìõ¥hSeµžšGAV+I)ú<±Ÿrè þ­ªÜ_jŠôþá¶a›?Ë3óçcy~fþ|,xô‡cy~fþ|,ÏPÌŸåù™ùÏcéÉ¡ktHÀí`×÷,ïYn¾ÂÓ“åò]ÇI1jgÔ¸ÀSïî5Ÿ=¤÷zk[7oûŠR9ºqçÆýýÆ62"¿Ÿôì~ûJ¹Î* ¦ÄÌí@‡Vd:›´oIiØC…w×\pêÿÄÓjÎÜPß(ºåž µðS‡Ï3³%=ìñNûÖå·ß]#A ‡_ïsUüäo^˜ßyÓ¿»¡ZLý_L”#­¨.Õ pHYs.#.#x¥?vtIMEä "°}q† IDATxÚìw\ÇûÇŸ½Âq4©ÒD,`EŠEE£†(¨ÄбEÄØˆ%–“¨‰ñk v£ñg,¨ˆŠXÐÈ¡ €Ò{/w·ûûcõD¸;ïà8Ê=ï×½’Û¹Ù™ÙeæãÌìì|ˆò²R@i90ð Ò²`á-@d$//?11111ñõ›7ÕÕÕ}û:Ž7o ‚²…4/H’|—––œœœ˜˜˜““+ ©©©­mÇž=zàýAP¶æBUUUZZzJJJÒëו••€ kkkk+[[[--- o‚²…41ÅÅ%iiiiii™™ÕÕÕB¡PSSÓÖ¶cûöíÛ™›3™LyÕŠÅb€@ÀoÔbóù|@Àårñ/¨"ø$QÅ¡(êí»wïÞ¥eeeñù|á F;ssmm¡PH)Š¢ÿ †vvv †´G:L&‹Éb€P  7ŒýnÅJ>ŸÿëO[ê­\³ü¾eŒý{þPÙj——§®¡¡ÙB¤eK¥)++»~ãFQ~†žZ¦«(’")’ I¡"…¤ ¤@H ¤PH …¤@P.༫06¶è:uªW›6mĦÌ`0LæGq IRØð—””jkk‰‹Š‹u´µÏ_ºüÓ/¿M2yñ‚ùò&Ïãíøãϸçÿ@wûï}ÛÅή~eþ«ÖˆýiÅwKÌÍÌê—ì›·Nœþ·¸¸¨Wž ýæéhk×üõÂ¥Ëaáw¼§MíݳöTãC‡éë¢Ñk£»~í*I¹øÌ?vôè¯'M±ToRRïEF&¼|Y\RºqíjQ5GNœ¼s÷~yYÙÀýçúÎûo‰¤‹ÊËÏßÿ÷¡§Oc ÂζÓkVŠý§‰*Í­ÛáUÏÆÛÞ3Ô©|ßù"I …”¡€ I¾ )K=½P$(CÍâŽm²™d5%à—V2þMq:uš=gö,1ÿ ’$kƒ¢ÈŠìâå+þþkÏûÃŠŠ™sýÆ¸Žš=Óû^äƒÏž›â1ɸm[Ù|“œ¼p©YE}÷ìùÂ¥þA{ÿ´jß¾Åc0#†¡¿‡^¿QPPà5ùkúPûS­‘û¢ÖmÚ²lñB«öîú~Màÿ~ß)úõí»´¿ËÎÉq=ºî¹ /_U”—»»½ÿI]]‘=©ðˆˆ‡èG=|TU]- ßñÇŸ‘Q—/]ÌårwïýkÖŸ~úqƒŒU\R2gÁ"K ‹ófs8œ˜Øg8·…ˆáuR¢gï‡F†êôˆ‘ IJ(†Äi:§š¿L# ‚`0$Išë“îÖ/úµMÖTãÝ>bûsC€º²EP´ü‰ëÝPõ+jiié¢eñ ôáËW¯:ÚØ8öé}èØq7ב¾3¦GÜ»ûÎÝ)“dOsoÐß"Íz/…ååûþ½eÃúúÉÖx÷±ô÷ /™ &}+|#?|ò¤«ýX£¨¸ø^äƒòòŠ.vºuíR7ÍÃÇOïþÕ„/àÇu&{=ûï?‡nÝèÑýæŸþvÞœ[·I*’E»v¢"Õì¢JÊ41éõ“§1úzÆ f±¤‰ƒÏôi>Ó§Å'$\»% äóùç.^Ú´î‡_ô€5+–Oõ™õ&%¥Ö?’.êï#G554vü¼•ž?ý¢_?‰·›®*£ÎÌç¶áGd #2™‚;™Â;Yœj‚`³ï'êm¿dž”IXZZvèÐÁÒÒ²}ûö¥B½soGÜÎî¡ÁXª¿«›æˆ;›7mÚ¼iY:äæ­› ÎÕ}äÐ,ÿ"ÍÊÎÉ™óíâ«7Âfy# /‡\ílgghhõP®Ë‰‹«øäi¬boòÆ­?Õì8|¿æ‡¤×ÉôøÔcêôk7n&&%}øÃŸ{÷Õ:Q(>ÿ/¾O¯Þô¡qÛ¶–íÚÅ>{Nþ{ö¼††¦ËpgyÅ’2½}çÎúÍ[RÞ¦þoÿo¿[F’rwó „Ba;sSúÇÄÄÀ…ËW¼fÌ”~Q·ÂïŒuÏã]¼÷\JFØÛRmÙbU¥WC8ó“Y' >CX¥þïC+®©¯¯_sœ§§§ÇçóŸ”9[r³­Ô“´ØUuÓ2xðÁƒ@ |_ï)ŠZ³fõÇy.9ÛÃC‡× 124456>sî¼Û¨‘¶m=‰ž;k¦m§Ô·)rMEÕ /** …ÌsEQ›þeÆ´©ßxy@Nn®çtoב.m¬Eq ¡áÇ?~NNdff=vpïŸÒs‰Œz¸`Éwô÷/ǹ1\J¦Ù99Gq8œâ’¯é×ÂÂF)×Er8œø„—¶;Ò£TÈÉÍM Mccc)Åçó33³nܺuúÿÎv²µÙ³¯»}·m›6âÜR>Ÿo¡«>‚É"˜LŠ …”PhÅ%<³®¬¦hÍb³Ùt …|>ŸÍfE2­L“@êZúä˜õ–-qsgDg;»ˆ»w ¹Ù³ç/èÚ#{"……’~*,,400hÔ;Ÿ™••˜ôzº—Áƒ‡)Š¢(hÛ¶íó/jÊ–$€¢>¹púîmÝþ‹Ï´©ŸÈ³´l7Þ} ý½SGé™:”Ãဎ¶öAƒžh¿­­¾ž^Í8:ÚÚA”ל–j£ÓæÁÇÏãyÎÆ^¸t™þçáÉÓ§ZZú÷¯•‹¹©YÍfOU%eÚ¦ÎǬÛ褾}WëšíãmÕ¾}䃇BR¸zEÀêu e¹(uuuŽšÚàAié411¶ïÚå¿ñ([ˆ˜Þ%àIR €¤(¡¯S Ùl¶ººº††Fuuµºº:‹Å"BCCC(VVV I"¿ŠK4‘lQÏãY[[@ZzF[##ÈÍÏ×7З=‘ØçgOâž=;A^?¸êÜòÊrÑ$]EE´mkÞÓ§Z[YI««[´3ÁK4p”•—§¤¾µíh£ÎáöèîpçÞ}ÑÝxÁKÐÓÕ­+[µžiZzFÍï&ÆFõ»ÞÎÃF8€ø„„ÂÂBÇ>½d¹(‚ :ÙÚVVUŠbVVU©sÔÅwë°éª2‚j>U]MVWQUUTUY]Eñ«AP]\”ÅçóY,‹Åâp8oß¾MII!‚¡;8,ôó5§äe —ăð›7._èÒ¹³($'773;{ŒëȬììW‰I}ûô¦(êå«Wæfæ²_{Ľ{º_7¹SG›°›áô"Þ?÷Ñ7ÍÌÔ´_Ÿ>;~ÿ³èC¿ãÙÿåÔ:w¬Ûès—.gff‘$ù÷¡#:ÚÚ_ôëÛ³‡Ão?m}X,Ö7^žófû~¶$Ò3½–œš ÿ½ˆ¿9ÊÅ"£¢6oÛ^7©ªªªìœœ‚‚BÈËÏÏÎÉ©®®€7)©%%¥ô—ÍÛ¶» wî`iY+±ãƺ] »õ.-¢?yÏs8Pì…`oKµ{[‚jªº Œ7¥=ÓËmÛkF›s_Q$ÙѸ2.•MQEQmÚ´ár¹$Iª©©•––R%Œ8êeõ$Öœób°dšùÖÒÒúãןE ÚíÛý{GëŸ~ùÉdºÍKHÈÍÍ›áå%ã…’²,(:&öÀ¡Ã³¼g(ä&Ïòñ^ðý¨q‚øfª§ºúûÄëÖnüéçq_M624,,,40ÐßñóOðé8qڔɯ_¿ùjêtuuu.W}ˆõôªÞHÉÔeøpÿïW@Vvö7^žÝí»ÀËĤè1Vïܽ¸ñGúû̹~°g׎Þ={ܽÏ_û9Nuuõ¨#V.ÿ@ %õÝÍð;k¾rQãÆ¸%½~íå=S[K«¼¢â»E {t·?¹‰«äU™mk§®üòMaµÞ…7SKKË´µÔ¿²Ú§ÁD'ký~µƒ¹¹¹žž—ËUSS€êêꊊŠÒÒÒ¼¼¼Úp$e* ߦ¥ééê¶Ñy?ϵlåêÁ¾˜øåxy720 ×¦É{Q%%¥……f¦&RŽaoKµ‰BUUU¥EE…El†Q]I’ž&ƒ;êÜÃ$IRWW—®@|>¿ªªª  À‚“2Øì%(i­¨æ¢ˆzUVVæ4xp­h‘÷ï³Ø¬!uÂ뢩©ùû/?<ärÿÞ÷?mís.FÜ»?ÝsŠŒšÇþ> ÌûÌ`0,Ú‰¹GG[[çskèõôt¥ˆZ=”)“ɤÇt"ºÛw7vŒB—ñ¢´µµêŠ;Êò‘Ìbny_[-ßÕìÊÛ6ím´yjT%UEENíù‚CØ„'%%%êêê ƒ–­®:‰^6Qj Þ–Êñ¸màÀ÷ïGFܹ3ÈÉéã¼Ud$[-‹f‰êtÍÃ6::$I޺ѻgÙ3½ëwÖ¬ßv+F8Û¼þ¬5ñ™>­– ‰*ÍŠï¿ï¡çÙë¼"N½ž‡$")!™R aš^ªCQ`È)êað®S›Ì÷½¤L«Ë9n¿lÿY®ïß,))8hD=x ÆQ“]³$ÎÐ5l㚪êêka7 FÎQSÃZÑüAÙRiÎ;zµ“ANo“4=õoçQPER@‘@Q@‘ $k¾KXÆç$›=̶î<|Ê”Éòfzÿ~dYyp8œ†k‚²…¨)))¡W¯¾z•XQQ!ûr*ƒ¡®®nccíââÒÉÖ¶ùÞ‰ˆÔ,e ©ÿ «²ªªºªJÆøl55®º:›ÍÆ[‡ l!‚ÈÐßÇ[€ HË‚u#äÞAZ’l¢¯Ô ?ìX2”» õÀí6ˆ^U=ç7°•éÕA½ÿ/EqÕ±ú!H½`XfG¢oª&[ååF}”-k‚Ô¯Õ’ÄGÙ"P¶[¶( (Š£ŽžÆR¿Þ–€¨¡\*([,ev{(º«ÅQCÙBúµZáGÍzßáR9ÙRâKXô›"@Pjj¸öAêSð‰` ìm)K¶¨]- H55&V?©—lÁµb|Ô/UR.ÁTÖ+e‹Šd³qÏ©CøA³>(—ªÉ0•%ŸÊ‹…o´ÉJNnI’Ú::ê\ÈÉÍ%I¡¶¶¶†Ìö¨-çJ?¹4釪 C •–-‚8P¨<Ùm„B‘ù“[¿lýñ¿½É©oéïlÛ¸­¡óС=»;È›Žû¤¯‹KJ–.\0éËñðõôYYÙ æÍñôøJÞ¤ª««C®^‹¸ÿ #3ƒÂÀ@ß¡[·‘.ÃÛ[X4‡;VëÒ¤ª,ú'*€`ÔP.•›ÞbC‰½-ŠŠŠ’²Qt«á/á¿ñ5CÎ]¼¼mÓF§äü·…ƒIß´Î:´52’÷-ö_ñ*)‰>äp8©ïÞ=+.)Y±lis¸cµ.íÃ…3Īð(‘Ö)f>— É–²¦Æ) ((H4¶Yyó¨]A={8l^¿.--ý»ïW•••…\»>t°¤¥§'¼LÌËÏðùýû9Š|àå«WÑ1qÆmÛ4€N‡Á`Ð7Íókêêªö––¢{XRRúàá£ÜÜ\í6Úýúô‘´¥úÖ_~K|ýšÁ`Ìñõ™òÕ$MMÍ‚‚»‘‘••tRÑ1±tÊB!õÐζcw{(++‹Œz˜““««§;ð‹þ¢rŠâ@Ü| ¥­9d “††ºôR=|ò„ ­­¬èúÐÆÚÚÈа֥պðZ‡R ÖÚ+z[ ¦JÊ–’­8UØL¶¾¾¾¾e»vñ ¢ÝøÖ¬ßÈKx)ЦÉåîÞõ[;;8wáâ¶ßvR=ºÛÓ>ã"6ný)33kÑ‚ùÓ¦L€{÷#ظ©¬âýÞ¤,kéÂo=&~Y«Ù99÷î€Û¨‘¾3¾¡õôtÇq«•ò ?~RU]=cªWwû¨G׬ßXZú~ƒ#M -×÷ïë(Šßß±O̳çUUU`աÑûX,–”R}¿æ‡ŠŠŠÕ+–;Ft¸vÕ ÷Ñ£k]št¤¬•C}x"OÀûÿªÚä^Óh–*ÉVNnîÙóþس7áÕ+&“ùÕ„÷‚2ÖÕuÏ®çOŸ} ¯iòm›7†ß¹{æÜùZ1»uíR7ý=í¯'¯†í{»€êõ’’R¾P K©>t¨ZC`‘¥`­X·TmÈÒ´²¥Š·[_Woäç!N§|ã“™•µc÷ž}»ô$:¿ @MMíÔÑCçû‚Þ¤¤ÐñMŒé/I¯ßØwëšž‘!I³ÀÔÄäCä×]:w.**ÊÍËSc“Z1;Z[[µ·|“’zòô¿Ã¶±¶ê×§OJÊ[1“q56‰§Ó'â¿ÿÒeåå\õ1ê ÷¤—Š«®^QQQŸñ<ŸÏ¯Ç]•¥`­’Fù‰J0ªgÆT¯ŸwìŒ{öüQttm¨®®þëÀß::Ú§þï¬(f{‹vfoߥ¯Ýð£ó°!‘‘QR’uìÝ«ƒ¥erjêªuF:;?xô˜Ïçkkkr^+&A«V,Z¶¼¤¤Ôgî|‡n] _ðâ¥Û±w/Zì–­Xõå8w@˜˜tóÎíKgþOôÄPÞRÙÚXG=~røøÉä”Ôȇëw?ëW°VÕÛÕím5ÅûÌôÈ\õ7Ö~ä0øpÿ¾}ǸŽ€ãÿœºú¥ûG¿r&“¹nõjCCƒÌ¬¬NŸq:DÊs}&“¹sû¶¾½{gee9qòUb¢­Íï¿n×ÕcSÞݾ[Ðÿv÷ïë( £cb¯Þ{—–ÑÅÎnÁœÙRÒÿý·_\†;'½yóËÎ];wÿyýæM{6[Úúé¥ò›;Ç@_¿´´ôÆ­ÛÓ<§ÔÏ–µ~kEs[P ²ãD‚VÖ“ŠRB Ë|´Aå)((,-+373­;Î …oÓÒôõôt´eºQÅ%%yyy:ÚÚŸ7¸//¯ÌÎÍæ¨©ÈèÆçó3³²Ùl–¡ì«=%•J(¦gdêëëij4ô5ú¬E£¹?˜l`°€É ˜¬÷«·P¶E¶„ …([Ò ÙÚ—L60Ùïe‹Á&K¥d«‰‰€þÒÀ¤ºʽ×ð^°(”-©[ªñD^n1Š -±·¥Ò4‰lQxßD½-$¢`µjòòó¥¼ÛØxç"ÓÛ¢Ä|Wšâ18%ßx8t8îù ÎáX¶k÷Õ„/MLŒ pÃeg…º4ä\¤1;8HDZ /_UUUºrÐ¿ß ošïì̬¬¦9ÝsÊ}ûâ½mF½-Õµ E+ÄÜÔlôÈ‘àæ:jüW“oܺ=ÝsJÍUUUOccß¾MÓÒÖrìÝ‹~§/åíÛòòrzÃ/šØ¸çÆÆF&ÆÆººm44Þ¯eößF††•UU=Ñ××:xh‘gbÒë'Oc ôõ†8 Šyö¬s§N2®•E9{[4Á‡ÄA¢2਩éèh—•–Õ ß´íçÿœNNM½~ÇËÛ76î9¤¥¥/ünyeåû×¶srsý–,¥w¶Ù½wßÍð;tø¯»þøé—k×oä½|ùÇÞ½þ«ÞoqsíÆÍ™óü^ÄÇß½ÿ`iÀ÷«ÖŠvøB¥·ERMÓx›ÁGù½-œ•W|>?äÚõ·ïÒêšn®ü^Mí½?æáã'öÿý÷î¿~ѯoë·ns ç/]v°ïjÕ¡CÝ”‹ŠŠ‚÷íe±XYÙÙ§L}“’ÒÎÌlמÿ-[ôíÄ/ÇÀ…K—£cbñO  ýRÉù-$¶B®\½rí:EQ:::‹Ìï߯ö´ƒÁ¸vëURRIiiNNî›ä:pÂx÷ó/sM’äÅË!~sg‰Mè'z`hܶ­žž^FF&äåårAG9bø–í¿âAÙBdeø°¡Ë/Tçp455ÅFø.`eeUÕÈáÎ6ÖV)©oŸ<}J‡ãô÷¡¤×o233+**œ‡{º¦ÆÇdÙlŸÏ/--e0¢½ÔÕÕUÁâAÙB†:‡Co+*–ŒÌÌGÑÑW/ž£÷ùpùŠè']]ÝáC†œ»x1=3k¬›+çÃ@ò³˜™š’$™’šJ*Sß½ …ø‡@ \¡rp8‚ h7ŠÜܼc'þ©ùë¤ ã¯„\|õå8wÙÓ4Ð×4pÀo»vgçäääæîúó V-e Qúzz¾3¾Y²|Å×Ó¾™9Ïoä§û vw°715éÙÝ¡ƒ¥¥\É®ðg«±=¦~3gÁ¢Q#œ FÃ÷ÒB±ÄßÊÚoëýN[|@È/›­w¿ ).)É/(075­µM P(œ0Ùkñ¿‘#œëxÂËW3çù]»xNKK oµÂÑü3XjÀT&X¬÷[2Th2‘¥Ä¨µV_ M‰Ž¶vݵ qÏž_½Æb±†"o‚QŸ¤¦¤Z[[åæå>ä>Æ 5KYMIåÚNÉ#9|HËýyóÆz<42лyëÎÝ{ZÚš_OšôÕ„ñx?‘F$TÖ›ýµ‰s ðî#H}‰»ST|ˆSò‚ l!‚ l!‚ l!‚²… ‚²… ‚²… Ê‚ Ê‚ ˆXMðJ"à+‰¢¸¦¤z¯ùbo A”-A”-A”-AP¶AP¶¤ÕÀçói‹oe ivlؼÛg-H’\¾jÿÊÕxgP¶æHȵë3çúцfª©¤%%¥5‹Š‹ ‚p:$:&vÿ߇°†¨ºl}1t¸”2KRV^¾aóVYΚõjµIGxÄÝK¾s=–¾íÉ©©³,¼ ΣÇ.Xò]Ľû*¢¤eee‹—¯øxXQ1s®ßàÃƹ4ð߳粲³Qw°·Õôü÷"ÞgÎÜk×?Ø„Ý ÿföœ˜ØgŸ l$vÿï¯ï×þ+¶GSQQ°zíž}Aýؘ _í;,èÀªªªýƒ¿üÚ³–’6¥¥¥ ¿[Ïãч/_½âr8Ž}z:v<-=ÝwÆôêêêÛwîb“i°lQПV4gqøø‰y‹–¼}—.=°QÉÌÌZ°ô»=íµXIÑÏ:zò55µ…~ó.ÿßéá7Àª}ûƒíq=.ÿßéoçÏe³Ù‡¿{¿qû\ù^²|EIIiAA¡ß’eVZ領´t‘ÿŠø„ú0;'gη‹¯Þ›åýP(¼rµ³¡¡AdÔÆæ$öÍUú`o«EŠ…$»PŒ€Ö T,ÿü{æÎš9ÝsЏ}ð¯=VíÛÓ ¾ñòœãë'NýÛHÅx~óAøÍ“G‚í:Ù²˜Ìªªªo¿óïЭۉCk)i#u÷\ÆŽõ³ÀÈÐÐÔØøÌ¹óÆmÛÚv´yô$š ;ÛNiéï°íà ±i½qcšïìZ£0±ÊäE|¼ÏÜy¡7n|6PQð^&€Û¨‘¢µ«Vp¹ÜZÑÆŽv/¡Q/¿ƒ¥åÿvíúaõÊ€Õk_¿yÓ»gÝ;~±êС–’*‚ :ÛÙ½yýÚ™›Ñ>ýܼl>([M›Éf0Y• EõOÅÍ IDATQ$%S B3ýlgnÊ^¡°zý†ø„„®]ºüøÃZŠjSEôµS69”­&c„óÐ#Aû{öpøl 2éÖµKðþ}5û>’EçNvp¥Î“‡ŠŠŠM[†\½]:Û5êå¿~ófÁRÿظçm¬·oÞ¸aóO –ú§¼}{)4ÔwÞ‚7))J–ªxÏÚÚÒÒ3Ú@n~¾¾>6”­&ÃÄÄxÏÎ æÍa±XÒ•ñ‡d0fLõúë]í̤*¯É_ÀþƒÁGNœÌÍÍ£ߤ¤øÎ[@?¶ËÍÍ;|ü½^iê䯩ô“Ä©>³^ÄÇÛwëúçŽ_ÕÔ8BRø">~ÊtïZEj „ß¼qùB—ÎE!9¹¹™ÙÙc\Gfeg¿JLêÛ§7EQ/_½273Ƕƒ²Õ¤·Á˜1Õkßn1bQ+°µ èàAgL›Z]]ýçÞ}î_} 5»6_ îþÕ×{þÚÏçóg~3}à€/õ>·m;ÛÇûÏ¿µiÓF[[k×/?ûzÏ0ÐWRïFKKë_îb÷¾GÙÖÈhßîßÇ» úû“Ét3š—››7°?l8 „…· átíÒ%xß_ÛÛõÙÀÆ`´‹KÀ²%šššŸ l$ÌÝݾë‰SÿÆ'ðêþª¡¡Þµs—©“¿nTÍ¢^Ô®Ü,Ö\_Ÿ¹¾>_ ÞÁÒró†u6ÖV­\»wü²pY}h×ɶ¬¢âÉÓŸo¦™™šîøãO55µaCc“i T®¤¬H!òA(!¿l®ÞýÖJrjêšu’^¿«&ÊgÓÖŸý—.ªû|³‘())ÕÖÖëhkŸ¿xé§_wL÷œ²Ðo^Ó×ü=XjÀR&˜,`²ÁS•z[ÊßKžÂ½ä[9,-ƒöìþuçͤý oB]p°† HË•-IaõXÅFIÁE¦¢@¨7Õ†7mª„Ìñk¼ÜCHBÎ (ÉI.’GiG­%cSm ¾º õÈú³§HŠOá AÜÜÜœŽinn^ZZ6`À###mmmú'>Ÿùòåääd~ýúÙÛÛcB”­¡®®®¯¯_TT$K__ß¼¼<[[Û´´´Å‹_¼xÑÉÉ -Z¤¯¯Ÿ’’Ò§O}}ýýû÷ÓƒD¡PèääD’¤““S^^ÞîÝ»££qY?‚ lÕ!44tذaô÷¹sçN:URÌêêê#G޼zõjðàÁ²Ä äp8tø¶mÛÖ­[F¦§§ÇÄÄоçû÷ï§èÁ&í-üÙ>QQÙêÔ©“hØ£G±q>|ôèQ’$ ~ùåWWWYâðùü“'OÆÆÆ¦¥¥½xñBÙÃÃƒÖ¬š˜ššr¹Ü9sæxyy 2¤nA”.[”KJ«JþS¤"š“’Â×_½k×..—Û¦MÙ㸹¹•——{zz:88ðx¼›7oŠ"ÖMAOO/""b×®]sæÌÉËË[ºtéæÍ›±!M@=Ö—õ=…jXRõΚ’–K¦¨ Y%ßhp¹\¹â$''‡……åææÀdɨG€›7oº¸¸Lš4©OŸ>؈¦Q.éo›ˆ]Ú.»Ö<¥!I5$kÉERÑå¦\.— ˆgÏž@zzúöíÛ?{JJJJLL ýÝÊÊŠÁ`¨©©aóA壢²ellèêêÚ©S§~ýúyyy}ö”ÌÌÌ#FuéÒ¥gÏžXDù#.VŠFvrìnú¡Û&é1»›’BŠ€€O ù”@pi‚^­ñqÑJÞo+???;;ÛÚÚZÆ~I’ïÞ½«¨¨°°°ÐÐÐÀÚƒ46Ñ"ên8çâ;[ÅRc°X ›Áb7î$Õ»›J.K•넾¾¾¾¾¾]SWÌ#AP¶AÙBAÙBi¸lá;v‚Ôåªë“×q¨Æ,%õéîׂ(D/äògP”‡EÃ÷’o@Ö8HD¤åAP¶AP¶AÙBd###£¤¤ï‚49*g8fmm=wî\;;;y 3nÜ8Ÿ… b¥A”-‰(Üp¬²²òìÙ³½{÷ŽŽŽ®‡r!‚²õyn8æããÓ®]»3gά^½RRRBCC«««kˆåçç_¹r%77×ÊÊjĈZZZ5S+--½{÷n=LMM±!ˆ’‘anK!KC•²¾TÃ1‹ÅårËÊÊàÊ•+vvv!!!QQQŽŽŽ»wï¦ãDFFÚØØüõ×_/_¾üóÏ?wìØQ3…ÌÌÌ¡C‡ž>}ÚÈÈ+Ò ZEÖTÍÞõ¹¨’ö˜¯G®”ÜKäe8–ŸŸSYYyúô錌 @ðí·ß®^½ú‡~€±cÇΙ3gòäɆ††³fÍš1cÆ®]»ès…B¡(øøø1cÆx{{¯_¿›ÒdZPï½ä©Æ—y÷’—1ßI©ŠáXXXXtttqqqIIÉáÇ{õêõòåËäääY³fÑ<==çÎÕ­[·øøøѹL&“þrÿþýüqëÖ­¾¾¾Øv¤©hî²¥@ñàà`’$·lÙ²dÉ’áÃ‡çææíÜAääädee@Û¶më&CÄ Aƒ°Þ HÂø´Ç©œ‚GÛ´™˜Íúxµ Æš5klmméuIIIôOeeeíÛ··¶¶W÷ô ,^¼xèСϟ?Ǫƒ Ê ×þ¨ÜrS‚ Ö¯_äÈ‘ÊÊÊaÆòù| 433srr266?~¼¿¿aa!”——'&&ŠN_½zõÊ•+Ÿ>}Š5 Ašª·¥r¸¹¹9::nÚ´)((())ÉÐÐÐÈÈèÌ™3ÇŽãp8°oß>‚ ŒmllŒÃÂÂjž¾téÒM›6¹¸¸DEEaB&è|Œ8_ñá«<†c”äS$„SB!E @È'JÈ¿4±¶eŽò Çh’““ù|¾ ƒñ‰ˆgff˜››ëèè`EAšñ†c`8&ÎÅK’4¥á á˜4:tè 6ÜÄÄÄÄÄï‚à ADQ²¥¨-SiVPÍ8‹z-e¯±J^Òjxy×¹¢Ì!ˆ2%IìŠyy—ª7Ÿ½ä?›î% HK$"‚ l!‚ l!‚²… ‚²… ‚²… ‚²… HˤÆ;‰Hx1ZºÖT†c¢ø‚(…¨ŠDÙ’w5<%ÿO”ÜÛÉ+ÐpLAZƒHÚ³1:ŠÚKžRX¾ªb8&¢¢¢"<<üÕ«WºººÃ‡777€¼¼¼GՌֵkWMMͺµA&$¶|d1óõõÍË˳µµMKK[¼xñÅ‹œœÒÓÓƒ‚‚èÕÕÕ/^Ü»wïÀëΛ7+ ‚ lICQ†c"‚ƒƒé-L`Û¶mëÖ­ sppø÷ßé@ooïþýûϘ1ƒËåÖ Äƒ ([ŸAQ†c" ÆÉ“'ccc ÓÒÒ^¼xQó× 6ܹs'**ŠËåJDeK<Š2áææV^^îéééààÀãñnÞ¼)ú騱c;wî¼wï^M·1±‚ l5ÚpL–˜ÉÉÉaaa¹¹¹´=âD?EDDÌ›7ïܹs]»v•ˆ HÓ D¨ñ½‘>PãK“AÏž=€ôôôíÛ·Ó቉‰'Nܹs§‹‹‹(²Ø@irÞZ›gRR²`ÔºJ¸ãʇÏçÓÓðÆÆÆ®®®:uêׯŸ——!444//oéÒ¥ZØ·oŸØ@l3Hó¯Ö”4Õv9Wð~eAHŒ(‡á%ÁpŒüÄpìâ$åŽuéÒeêÔ©ôa~~~vv¶µµµššÖ~¤9#Öplîù†cL †câ´D†c”ä¤$ˆÉŸõÑÜ^Ò*y¢žÛÔ‹9EÞ½ æÂ… 'OžLLLôôôêëëëëëc“@Z0Ô皡▤Ë¿ñ·±oå¯R'$$=xðÀÖÖ«:‚´Z¹½«”·|i¡àÆ5‚ l!‚ l!‚ l!‚²… ‚²… ‚²… ˆ*ÈVý¶Bá GFCiûÄSM—5||¹GJT)/÷Ôã} å¾Üƒ ­_ªDŸz¼I#ï)„åjŒ¬%ÇoÖ«äe8»víºzõjvv¶‰‰I=üüü°Ú#H‹£YË–¢ ÇÒÓÓœœÔÕÕ—/_nggWVV:xðàÂÂB¬‚²¥`b8¶páB55µèèhuuu:dÔ¨Q .¤¿GFFš››—––†…… 0ÀÑÑ1%%%44´ººÚÙÙÙÞÞ^”NÝpWZZêèè(Šs÷î]KKKZ7óòò._¾\RRâèèØ¿¬m¢’½"û£xÄŽ_¸paùòå"͉ýeÑ¢E³gÏž8qbdddBB•+WìììBBB¢¢¢wïÞMG?jÔ¨ªª*:NVV–³³s~~>Ÿ®¼)xYÙ±cGhhèâÅ‹³³³é˜˜±1ÍÍ͇ H-ÍÌÌœœœ$…Ógy{{c‹-š7oÞ”)SžcHs¤ ›p㬞g}Ô,I‹V¥,y¯Giä\%ßpñ Í`̘1cóæÍ<8sæLuuµè'I>cô¡ŽŽŽ(¦ššZuuunn.ƒÁY–µiÓ†¶‰EŸ1DI2$‹¯„Ø&Ü@‘w5|c¬žWÃ±šøøøtêÔiÚ´i:::´TÑHñ‹¥¥%I’oÞ¼±¶¶€ÔÔT@è3† Ê$ª ¶¶¶©©©ÿûßÿj…K÷«‹………““Ó?ü@’$I’ôŒ Ï‚ l5ÚÚÚuÃ¥ø‰‰å¯¿þºÿ¾±±±™™™™™™hMúŒ!HcC¸ü[þ~xIHtƒºæ?ÔûŸd7"£„ŸŽMnñÏ"—Ÿ˜P(|õê•X7 ôC‚xñ³ 0£dhÝ€†c- ¹üĘLfçÎÅþ„>c‚ƒDA”-AP¶Aš‡l)d+Uߤ‘·ÝQЄ• 2Çg}~;gBÎ p/yQ¦T‰ÝK¾±—ªã^ò‚ $"‚ l!‚ l!‚²… ‚²…4„ÜÜ\ooïwïÞá­@TV½#Ê)œb ǶnÝÚ«W/:dçΕ••+W®l†’k×®YZZÖzÛ‘¢¨3fŒ=šÞÒ Qmj.#¢äYUD´ŽëWñ«W¯RuõêU:äùó祥¥Íóª7nÜèááQK¶._¾ìèè¸xñbl²ÂúDÁ Ê.»F+z©¢ ÇÆwíÚµ[·n9;;×MA¬ÕXxxxçÎE[*߸qÃÑÑ133³´´ÔÑÑQtîÝ»w---i¡k5VQQþêÕ+]]ÝáÇӎg’ÒOMM-,,äñx¡¡¡‡.m^^^~~¾±±qTT”(Y>Ÿùòåääd~ýúá~ª*Úëjþ%$tJpÆ'×O‰ûH —7rã#ÅpÌÒÒrþüù«V­ªû“$«1ooQ´ &<þ<>>~Ô¨QUUUt`VV–³³s~~>H¶óõõýí·ßx<Þ™3gìííïÞ½+%ý{÷îegg?~üøèÑ£gΜ‘”¬P(trrÚ¼ysJJÊíÛ·g̘MLj²î1/oólH|¿HŸ"•2[³fÍÙ³g'Nœ( ”n5Vwww&“yñâE8zôh·nÝzöì)Åj,88X´Qê¶mÛÖ­[&é*üüüŽ;æáá±téRì`Æb±èÑ1m)DQø¶¢B4÷'‰ 4322Z¶lÙÚµk…B¡(PºÕX]ØlöÔ©S:D:tˆv“b5Æ`0Nž<¹jÕ*??¿{÷î½xñBöË—”¬©©)—Ë3gÎÕ«W+**DFA¢bs[ÍŽ€¿¿ÿž={èÇ‘4Ò­ÆÄâããÓ¯_¿¬¬¬´´47mÚ4j5æææV^^îéééààÀãñnÞ¼)ûåKJVOO/""b×®]sæÌÉËË[ºtéæÍ›±6#([-Ù Ç´µµW¯^½nݺ!C†Ð!R¬Æ´´´DO EN½zõêÖ­ÛñãÇ“““ÇŒcdd’­Æ’““ÃÂÂrssie}°B#8Hl…øùùqöìYúPŠÕX=N:%ÁªU«jļ½½I±>.?•kÏøzïx^Òp äÙˆ½½Ë« ÉBþ=æqãAZî Ae Ae A”-A”-A”-A”-AZ­láÒPAêGã¨ëóËÖ‰z.ÀsŠœ[Ë+ÖpŒŽcmm=wî\;;;¬QH+ѹìêý~KýÒ©©”"ò}/[ÍŽÑq*++Ïž=Û»wïèèhT.i¡4÷ e8&ŠãããÓ®]»3gά^½$X„@\\Ü­[·LLL&L˜Ñ»woz±á’,Ť¤ H½iUSòR Ç>ê4‹Åårém%Y„8q¢oß¾>¼téÒèÑ£Ç'%\’¥˜¤ôiͽ-EŽåççÇÄÄTVVž>}:##ÃÃÃC’—WçÎýýýwíÚ5þ| º}û6¾Øpk)&Å‚ «‚´fÙ¢ ÇèïR ÇŽ=J’¤$ñ°°°èèèâââ’’’Ç÷êÕ+999... 44”¢(Š¢h//‹•‘‘!Ú=ÙËËkΜ9˜˜(6>XŠÅÆÆ¦¥¥Ñ–b´WXÝôQ¶¤•Ë–¢ Ǿþúëàà`’$·lÙ²dÉ’áÇKòò***b0ÚÚÚt ††‹ÅIá ÁRLŠ‚ ­Y¶dAvÃ1ƒ±fÍš‹/‚8/¯ÌÌL’$ãããi ²—/_ °²².ÉRLŠW‚ AåVɱ~ýú#GŽ$)ÖËËÄÄÄÝÝ}É’%ïÞ½KKKó÷÷§M1$…K²“ä†uA_¶ZÝ*y777GGÇM›6‰õò€ýû÷s8œŽ;8ÐÓÓ“ÉdêèèH —b)&)}i1PMzº¤ÎÇÈ“’Ò Ç„JÀ¿àÕ\ ÇÄZ„‰ˆŽŽîÛ·o^^žh«ØpI–bŸMA¤ ÞpìßT[ÅR£­ÆÄŽQ²µWB\|h†c’ã£á˜/¯ëׯóx<{{ûŒŒŒ 6øúúÒÚ$)$[ŠIÿ Az€²%SSÓþùçüù󺺺‹-òóó“Ž ÊVcoo${8‚ Ê÷ÛBe Ae Ae A”-A”-AºÔXAÕY—JI-ƒ•ã €’wãkAdF|»×rkBÕ#©†ÅoX,™J/Idä:E¤h¨Z¢@©}Z%¡ S24mYÒQà)’ã«ô 133³¸¸k>‚´ØAbóCá†c"æÌ™óÕW_yzzº»»/_¾\Þ‚]»vÍÒÒ²sçÎXeëh8V3èÔ©SõÔÃÃe AP¶Ä (Ã1YÒ‘d–ŸŸåÊ•ÜÜ\++«#F¼~ýº°°Çã…††r8ggçÈÈHssóÒÒÒ°°°8::¦¤¤„††VWW;;;ãZ²%²ŽIâÑ£G®®®ýû÷·´´Ü²eËôéÓ·mÛ‘‘‘cÆŒ±··wpp¸råJ\\œ¡¡avvöãÇKKKuuu-Z¤¯¯Ÿ’’Ò§O}}ýìììI“&=ZKK+ à—_~Y¸p!V5QÙR”áØµk×\\\D‡¿þúkM Iæ`ööö³fÍš1cÆ®]»è˜B¡Éd;vÌÃÃcéÒ¥¢ÒÓÓcbb¸\®@ °µµ]½zõ?ücÇŽé{fUIDAT3gÎäÉ“q@QÙR”áX- Z:’ÌÁ´´´âããCBBD1™L¦Ø2xxxp¹\xýúurrò¬Y³èpOOϹsçFEE7k‚¨„l)Êp¬cÇŽ>>>’~•dF;VÈÒQ244¤¿äææíâAäää`UCÅÉ–ØÅfŸ¥ëE©O?ŠCvÃ1IH2£e‹ÇãõêÕ«f8“ɤ(ñ×@/¿HJJ¢}ÉÊÊÊ222ê®É@úCIø4^ËmfñUe¹iyyù»Ôš¶—dfll<~üxÿÂÂB:‘ÄÄD077åóùu32776lX`` ýk`` ™™™““¶5Qª"[§OŸ¶¨ÁºuëjEd¶oß>‚ ŒmllŒÃ àéÓ§ZZZb{yAAAIII†††FFFgΜ9vì‡ÃÁª† Š‚y¢ì}?ðb£ñý:ɧˆ1~j86ͰV„¦2«‰$s°ÌÌÌ‚‚sssÚ0Q’““ù|¾ í‹ õ@¼áØé†c †cuš0H6#$´z@ñ$s0y§Ï:tè€÷Apˆ ‚²… Ê‚ Ê‚ Ê‚ -–|û¢~6\zR¸#3‚4YšRs[Ú®Ð,°·… AP¶AP¶AÙBAÙÚ¸qãèÑ£Ÿ>}* Ù¹sçO?ý„6Qeš»áØÕ«W)Šºzõ*òüùóÒÒRü³!ÊVóeܸq×®]»uë–³³sÝ_ëúzñx¼ÒÒRGGGQœ»wïZZZZZZVTT„‡‡¿zõJWWwøðáæææøçG$*KKËùóç¯ZµªîOW®\±³³ ‰ŠŠrttܽ{7ÄÇÇ5ªªªŠŽ“••åì윟Ÿ¾¾¾¿ýöÇ;s挽½ýÝ»wñÏ -¼·EøMÿ$…K¡§HfÍš5666gÏž8q¢(P |ûí·u}½ÜÝÝ™LæÅ‹=<<àèѣݺuëÙ³'‹vݶmÛºuëè­J¤• Ðv§˜,G=Ÿ¼% i_}¹6Û¯Ç)R122Z¶lÙÚµk…B¡(°®¯AQQQl6{êÔ©‡¢Ã:$2ìa0'Ož\µj•ŸŸß½{÷^¼xõi R%¶©Ê_ö]7‹Æ+|>~ËXáï“säÈQˆ_/ŸÐÐЬ¬¬èèh7mÚ4:Ž››Ûï¿ÿnjj:xðàž={–””`G>HlÆhkk¯^½zݺuC† ¡C¤øzõêÕ«[·nÇONN3fŒ‘‘$''‡……åææÒJwàÀüÛ#H ¥Å,7õóó#âìÙ³ô¡t_/ooïƒ?~\4Bär¹A<{ö ÒÓÓ·oߎ{AÙj\8Îúõëi0)¾^Ó§OOHH bìØ±tˆ±±q`` ««k§Núõëçåå…{i¡£Ž}±îa”dK"©§ˆ1#…) „|J( „üó 2“Ë×+???;;ÛÚÚZMM ÿöHóG¬áؼS© ¶“¥ÆTIÃ1h†crùzIòC–×ãPá ÇW©ia l!ÒreK![¼SR“Â]äDù4é¾ï‘ëc!OBÒ×¼ŠMJÆ%³‚ÈÒe·À¥i×§_YPÀR¢„Èþv‚ ²ˆÕDŽXMü<ç¶ia l!‚²… ‚²… ‚²… ÊVk'33³¸¸ï‚ lICɆc+V¬Ø±cG­ÀcÇŽùúú€§§ç¾}û¤§píÚ5‡µ A”%[Íoi;m8¶råJQÈóçÏcbb); ‹-[¶Ðx‰øùçŸÛ¶m ®®®ŸÕÙÐÐP¬U¢xõød•¼h¨¤E«R–¼Ë›«ü+ã”i86mÚ´€€€K—.‰¼6¢££ãââN:FFFÚÚÚtxdd¤¹¹yyyùõë×MLL&L˜Àf³ãââ y<^hh(‡Ãqvv–’c\\Ü­[·ès#""z÷îMoM‘——wùòå’’GGÇþýûcµGähV¤# yѧ«ç%…×H Ç>¢¯¯?~üøàà`QHppðÀíììè!ä¿ÿþK‡/Z´hÞ¼yS¦LyòäI@@À¸qãàÞ½{ÙÙÙ?>zôè™3g¤äxâĉ¾}û>|øðÒ¥K£G?~|\\¿3!ç2YÜoùs˜ššþóÏ?çÏŸ×ÕÕ]´h‘ŸŸÞD¾&ÖØ{ÉËÛ¢kfÑøÛØ³°({{û   ¼R?pãAP¶AP¶AP¶AÙBAÙBAÙB¤5É–Bö’§ð+‚ ÕÚ²¨±Jž¼Ð^!Ê%Ú´žj¾ú•™™©¡¡Qs/­–› ÒšeHìGR3là*yYâ+7 4ûY\Å^š¹ G‚ÈK³~¹‡6£(êêÕ«tÈóçÏKKK[z ,--EÂçááѹsg¬‹"«lÝJ()+R¤H@Êš©2 Çj!c|)% ïܹ³hcå7n8::êêꊼËêz”aD>ˉç|`€A“ LÕ¹h8&ãK)¡··wDD„èp„ ÏŸ?‡Þeu=ÊiÙƒDeŽÕDöøbK( u=Êiñ½-P®áØ'·FæøbKˆ ˆêÊøûûçääÈn8š••]Ëpì÷ß755ü¢u¦µ~o††c 7( …oÞ¼100ÐÓÓÃæ†È‹XÃ15‰ÀT#˜l`°É ,•Z·Õâ-0Õp¬áÞ_L&³cÇŽØüDç¶AD½-¥=ÆjØä‚ ⛒ʵ)ìm!‚²… ‚²… ‚²… Ê‚ ˆR`áƒDia¨úƒDìm!‚²… ÒȃDàíÛ·B¡ÐÀÀ€ÞÁýÝ»w@__¿õY~Õº4釂½-!@Üþ-bùî»ïF`üøñ . ¯G–½zõ²²²myêäädee%—±˜ˆªªª½{÷º¹¹ÙÙÙuîÜyøðák×®m>Žaµ.Mú!Òâ!B¯»©z[2ÝëÈÈȨ¨¨š!{öì9wîÜøñã’w¯^½LLLÌÌÌä=177×ÅÅ%66–>är¹ ·nÝÊÍÍÝ»wosøsÖûÒ–%W>8Hl® <øÔ©SIIIcÇŽ-**:tè-[¯_¿ŽŽŽÎÌ̬®®6555j”hfxúôixx¸……E][ºtiEEEMk‚‚‚«W¯¦§§ëë뻸¸´k×NlIfÍšKÄÆ-ZÔ¦M›ìììK—.‰öØ §S …!!!½zõ4h…††¦¥¥3FTNQ|‚ .]º¤««ûå—_jiiI/Õ7½½=B:88˜››×½4éH*‚ l5555[[ÛÇ‹l&&OžüäÉQ4mmí›7oÒ‰ýõ—ŸŸ½Ù±““S­½C½½½SRR¶oß¾|ùr¸téÒÔ©SE[ųÙì;v|ûí·u§.\¸ß|óÍÚµkéÀ¶mÛúúúÖJÙÝÝýÆ•••ßÿý Aƒ®]»6eÊ”ÂÂ÷ûšéèèœ>}zÔ¨Q¢ø#G޼{÷nEEtëÖíéÓ§l6[J©&L˜PVVD;€Ð‡ÿý·O­K“Ž”‚!Í}lHª|šäI¢|7=--mïÞ½ÑÑÑ,kÁ‚"¸}ûvjjj||¼««kIIÉêÕ«éNÊŠ+(Šrss»r劅…Eqq±¤Ä hupww ›?>ŸÏ_´h½sMâââè/nnnô—C‡‰fÍh.]º4iÒ¤íÛ·÷ëׯ°°pòäÉ………Ë–-KNNþñÇ‹‹‹½¼¼jjDDDlÚ´iÆ ðßÿ…„„ÈR*Y¶E”‚,Cpb {[õüW‚ÇãùùùÀÿ·w6¡MeQ?ç5R5"c‘hŠøfÑê¢EhpS‹.D]h‰u!ݨ +.Šà ¸˜¢ X ®\(¶R¡XÝHl-ˆƒtP„*TªbI‘¦lLÛœYܼ—÷‘¤ùp’Ô÷ÿÂK¼}\ßÍý¿ÿ¹ïÞ{˜¹§§Ç°ííí}}}ýýýÑhtvv–ˆTòÔ±±1¥S½½½;vìƒÙÒLŒŽŽªŽÚÛÛ»mÛ¶ýû÷÷õõÅãñ§OŸ644˜K)c½^¯:8þ¼ ½^ï™3gŒ’GíïïWǃƒƒ±XLù²¡¡!µKêÜÜÜ«W¯Œ £O:uéÒ%"ºuëV4ýòåK"‘X¶V\ÚýöÅ‹ËV T³Ýr³óòTæšr—hhh¸råÊýû÷ÃáðÅ‹››››››#‘ÈÞ½{#‘È–-[€Ò¥V*©ÝÙׯ_ïõz :6TÂj"ª¯¯W±ØÆãñ¸ñ½ÁöíÛQ³#GŽÑ“'O†††îܹc+¹oß>çù»ººÌe"‘ˆqllºvíÚh4šH$ò©•b).}F>Õ$º7TÔÊšó¢¨U>ŸïäÉ“?ÞºuëÂÂBgg§rI‘H¤¶¶öÓ§OÏŸ??vì˜Q^¥#"RMNNfÓ,2íé¬bÀïß¿ûö2íõÜØØ”'RgnkkSß8ãlçgæééiщÅbFJ¡Œá^îZ©1{¥/oß¾M$E4|>U«[:”›R`xÊz­SV‹ŠˆÉ׬YÓÕÕuîܹׯ_ŽŽª-ÞüøqõêÕºººÛ·o%ƒÁàÎ;'&&B¡Ð‰'ž={–ã´­­­»wïþøñc{{{(N$6lp¦êaæ»wï¶µµE£Ñ¦¦¦––¿ßÿæÍ›ÜÕnmm ããã‡îèèXXXxÿþýàààÌÌŒñİÐZ566ŽŒŒ\¿~}||<×ÅU T‹lq©j…»­ŠYÜ‚9{ö¬zäíÚµC‡>}šˆnÞ¼yïÞ½ŽŽŽtÜëñ|øðóçÏMMMÝÝÝ9Î?22 …>|øpáÂ…ÎÎÎ`0˜;PîZuwwoÞ¼y~~þÑ£G—/_6ÆÚ (ªb Z‚D!º5HäUDŽ(Ͳñ\å%I²ÄzÚ±ùßíºPD±ÙÙÙX,–1ÛØâââÄÄ„ÏçË3õÎÜÜÜÌÌL]]NÊM<Ÿžž^½zµßïϳŸ'‰¯_¿ÖÖÖÖ×ׯZµ*ÿ¨ŒµZ\\œœœÜ´iSéËtŠ«(™Ž]ý—kJ°[®i)ã^òjŽœ0“@¶(M¶„‰õ®Ä쪽ä˾&Q(Ç⟿ÿÂO€åú‘¦w"vgìRö5‰Ì$’1wÎOdv[.Ö,ªÐRj¨<%ÝûSs \zÊ/[ Í Ô ‘ÙÍ[3—}à'ßû1¶U‹´Ô”Ò‰RË{\: ¢B›2ÃvPj¦Ìå¹ÖL¤6GfþíÏ8‰ÚÂüZIR2ý‘$IÉ%IÛ_B"$I"!"X9P]Ö(½)§†¥XØ^5ÄLZ~¬k¤ilþ˜~™ž!ºoSùJ ɧċ‰ÕLÍöÏ¢1‰F²DÂ$Ib$I¢é:•L¤ŽuÙbH¨BoD¦m”S³®4“xeU1Îð½m@L€(·xª¥ïÂ,L’!‡T:•$!§fÁmj­ôúA›rQJ‰ˆ­ª¤Ù‹LGl™·åÒ QÊÞŠÆÖ×d>ÐL˜ˆ™5¢¤¤Ú&I¬‹;Ô*õNÖóPEvËî¹,úe¶`ĆT‘ã]ô¿5Vö¸lYOeÝ–¾Ð‡­MKzð(L"ħJI’Ô8¤Êx'“Û ªTË´Ý»M¶ÒïÄv cË"5žEnÏÙSÙb"Ñï’V.!b!ÒLÙ9”x ±¦Z‰}ÜݰW-°²dK7_œUÅ2èšC³Øù/*ç¶œÊeëÒ5ËüNéqw"ë`V:6Ȩ:Õbk´èËC«ZY¢ËŒp[e3\dÚjCµŠ1[Ó(šõ‹ì#îbÓ?²I¨ÕÊl»2ê—ÃŽY¿a‡öž$VB¹Èâ±LžK¬ó$ÌÅtUû¨¾ã{ª$Bt¨˜Sw8“#s–dW/ë1d«‚\,­%†–Iº…Œ]nØäÂD¬í+Ð|eÓ e¾4ßç]úË÷TSfôÕ™ÜC­À¯¥_YÝ™ÛUÊV‰ p#.`¥¹-DZ¸-€ld ÙÈ@¶-€ld ð«€Yò¸-€ld ÙÈ@¶-€ld ðë€Yò¸-€ld °Rùt óHU·€IEND®B`‚goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/index.rst000066400000000000000000000057301414415210700233370ustar00rootroot00000000000000.. title:: Goodvibes - A Lightweight Radio Player for GNU/Linux .. toctree:: :maxdepth: 1 :hidden: :titlesonly: screenshots installation user-guide changelog credits .. image:: images/goodvibes.png :align: center **Goodvibes** is a lightweight internet radio player for GNU/Linux. Save your favorite stations, play it, that's it. There is no function to search radio stations, you'll have to enter the URL of the audio stream by yourself. Not very user-friendly, I know, but doing better than that is not simple. Features -------- The main interface is quite stripped-down, but there's a couple settings and features waiting for those who dare to open the *Preferences* window! * **multimedia keys** support - ie. the ````, ```` and ```` keys that are present on most keyboards. * **notifications** - ie. information that pops up when the song change. * **do not suspend** - ie. prevent the system from suspending while a radio is playing. * **autoplay** - ie. start playing the last radio when you start the application. * **MPRIS2** support - ie. integration with modern desktops. And for those of you who live mainly in a terminal and avoid graphical user interfaces, you should know that you can build Goodvibes without GUI, and control it via the command-line client provided. Under the hood -------------- Goodvibes is free software, released under the `GPLv3 `_. It relies on various free and open-source libraries. The core building blocks are provided by `GLib `_, the HTTP bits are handled by `LibSoup `_, the audio part is left to `GStreamer `_ and `FFmpeg `_, and the graphical user interface is written with `GTK `_. **Development** takes place on `GitLab `_, and this is where you should head if you want to get in touch. Arnaud R is the main author, and is the one writing these lines right now. **Translation** happens on `Weblate `_. If you want to contribute to the translation effort, don't be shy, `get involved `_! **Documentation** is hosted by `Read the Docs `_ on this very page (don't click, you're already there). **Artwork** is made by Hector Lahminèwskï. Feel free to visit his homepage `Lahminewski Lab `_ and get in touch. Similar Projects ---------------- * `GRadio `_ is a GTK3 app for finding and listening to internet radio stations. * `Pithos `_ is a native Pandora Radio client for Linux. * `RadioTray `_ (unmaintained) was an online radio streaming player that runs on a Linux system tray. goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/installation.rst000066400000000000000000000070621414415210700247310ustar00rootroot00000000000000Installation ============ .. include:: icons.rst .. highlight:: none The easiest way to install Goodvibes is to use the package provided by your Linux distribution, if any. It's 100% hassle free, however you might not get the latest version. To get latest version, the easiest way is to install the Flatpak app. Another option is to use an unofficial repository for your Linux distro, if any. At last, it's always possible to grab the source code and build it. |flathub-logo| Flathub ---------------------- * `Flatpak app on Flathub `_ :: flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo flatpak install flathub io.gitlab.Goodvibes |debian-logo| Debian -------------------- * `Debian repository `_ (since Debian 10 "Buster") :: sudo apt install goodvibes * `Unofficial Debian repository `_ (Debian 10 "Buster" and later) :: wget https://pkg.arnaudr.io/debian/arnaudr-archive-key.asc sudo mv arnaudr-archive-key.asc /usr/share/keyrings/ codename=$(lsb_release -sc) sudo tee << EOF /etc/apt/sources.list.d/goodvibes.list deb [signed-by=/usr/share/keyrings/arnaudr-archive-key.asc] https://pkg.arnaudr.io/debian ${codename:?} goodvibes EOF sudo apt update sudo apt install goodvibes |ubuntu-logo| Ubuntu -------------------- * `Ubuntu repository `_ (since Ubuntu 19.04 "Disco Dingo") :: sudo apt install goodvibes * `Unofficial PPA `_ (Ubuntu 19.04 "Disco Dingo" and later) :: sudo add-apt-repository ppa:elboulangero/goodvibes sudo apt update sudo apt install goodvibes |fedora-logo| Fedora -------------------- * `Fedora repository `_ (since Fedora 30) :: sudo dnf install goodvibes |opensuse-logo| openSUSE ------------------------ * `openSUSE multimedia:apps repository `_ (rolling release "Tumbleweed", stable release 15.x "Leap") :: # For Tumbleweed (for other releases, adjust the url) sudo zypper addrepo https://download.opensuse.org/repositories/multimedia:apps/openSUSE_Tumbleweed/multimedia:apps.repo sudo zypper refresh sudo zypper install goodvibes You can find more install instructions on the `Expert Download page `_. |archlinux-logo| Arch Linux --------------------------- * `Archlinux User Repository `_ :: git clone https://aur.archlinux.org/goodvibes.git cd goodvibes makepkg -si * `Étienne Deparis' Unofficial Archlinux User Repository `_ (for a prebuilt binary package) :: # Setup the repository first, then: pacman -S goodvibes |void-logo| Void Linux ---------------------- * `Void Linux package `_ :: xbps-install -S goodvibes |source-logo| Release Tarballs ------------------------------- If there is no package for your distribution, and if you have some packaging skills, please get in touch, your contribution is welcome! For build instructions, refer to https://gitlab.com/goodvibes/goodvibes. goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/screenshots.rst000066400000000000000000000034211414415210700245630ustar00rootroot00000000000000Screenshots =========== Some basic screenshots to start with: .. figure:: images/screenshot-ubuntu-19.10-01.png :alt: Main window, dark theme (Ubuntu 19.10) :align: center Dark theme (Ubuntu 19.10) .. figure:: images/screenshot-ubuntu-19.10-02.png :alt: Main and preferences windows, dark theme (Ubuntu 19.10) :align: center Dark theme, with the Preferences window opened (Ubuntu 19.10) These days, "modern" desktop environments (say GNOME, KDE, Ubuntu at least) are able to communicate with `MPRIS2 `_ capable media players. It means that they can display information about what's playing, and also allow some basic control over the player. Here are some screenshots off such desktop integration: .. figure:: images/screenshot-gnome-3.30-01.png :alt: Main window, dark theme, notification area (GNOME 3.30) :align: center Dark theme, with the notification area opened (GNOME 3.30) .. figure:: images/screenshot-kde-5.16-01.png :alt: Main window, light theme, media player area (KDE 5.16) :align: center Light theme, with the media player area opened (KDE 5.16) It's also possible to launch Goodvibes with the ``--status-icon`` option. In this mode, Goodvibes will not display a main window, but will instead add an icon to your notification area (also called system tray). This is a legacy feature. It doesn't work on some desktops. It's deprecated in GTK3, and will be removed in GTK4. And at this point, it will also be removed from Goodvibes. This being said, here's how Goodvibes looks like in *Status Icon* mode. .. figure:: images/screenshot-xfce-4.14-01.png :alt: Main window, light theme, status icon mode (XFCE 4.14) :align: center Light theme, in status icon mode (XFCE 4.14) goodvibes-v0.7.2/docs/goodvibes.readthedocs.io/user-guide.rst000066400000000000000000000227561414415210700243100ustar00rootroot00000000000000User Guide ========== .. highlight:: none This user guide is still a work in progress, and for now it's just about documenting advanced tips and tricks. If you're interested in helping with the documentation, please get in touch. Adding a radio station ---------------------- Out of the box, Goodvibes is pre-configured with the favorite radio stations of the author (that's me). The first thing you'll want to do is to listen to it of course, and possibly remove it (which is easy). Then you'll want to add your own favorite stations. And here comes the first (and hopefully the last) painful moment with Goodvibes. So it's 2020, and there is no way to search for a radio station in Goodvibes. You might think that you would just do a research by entering the name of you favorite station (say "Radio Grenouille"), get a list of results, and select the right one. Well, no, it doesn't work like that. Instead, you will have to enter manually the name of the radio station, and the URL of the audio stream. Your fingers on the keyboard. Plain old fashioned. So, how to do that? You know the name of your favorite radio station, but what about the URL of the stream? The easiest solution is probably to open your web browser, visit a "radio directory" website (that's a site that list plenty of radios, along with some details), and you do your search there. If you're lucky, you'll find it, then you'll look around hard for the URL of the stream, and finally copy it into Goodvibes. A list of websites that come to mind: * `Community Radio Browser `_ * `Icecast directory `_ * `Shoutcast directory `_ The second solution, a bit more involved, technically speaking, is to open the website of your favorite radio station with your web browser, then open the Developers Tools, and more specifically the Network tab. Now, play the radio, and look at the HTTP requests passing by. There should be the URL of the audio stream somewhere. If you ask yourself "why so complicated", well, it's because there is simply no radio directory that would be exhaustive, maintained, and open (meaning free to access), and that Goodvibes could query in order to find your favorite stations easily. Keyboard Shortcuts ------------------ Assuming your keyboard comes with multimedia keys, then it's all you need to control Goodvibes. On modern desktops, multimedia key events are caught by the desktop environment, which then forwards it to the multimedia player. It should work out of the box, assuming that you didn't disabled the *MPRIS2 D-Bus Server* setting. On older desktops, or bare-bones environment, it could be that there's nothing in place to receive and forward the multimedia key events. In this case, you can enable the *Multimedia Hotkeys* setting in *Preferences -> Controls*. Additionally, when the main window has the focus, hitting ```` will start or stop the playback. GStreamer Output Pipeline ------------------------- This setting can be configured under *Preferences -> General -> Playback*. It's quite a powerful thing, but not for the faint of heart. Here are some examples: * ``alsasink``: Use ALSA for the audio output. * ``alsasink device=hw:2``: Use ALSA, and select the second soundcard. * ``autoaudiosink ts-offset=5000000000``: Delay the audio output by 5 seconds (ie. 5000000000 nanoseconds). How does it work? You must specify the name of the GStreamer element to use for the audio sink, followed by optional arguments. You can experiment with that on the command-line. Make sure to install the `GStreamer Tools `_, run a quick ``man gst-launch-1.0`` to warm you up, then try to do something with it. For example, to use the ALSA audio sink, you'd run this kind of command:: STREAM=http://direct.fipradio.fr/live/fip-midfi.mp3 gst-launch-1.0 souphttpsrc location="$STREAM" ! decodebin ! alsasink Once you work out the details of what you want to achieve on the command-line, you just have to copy-paste the last part in the Goodvibes configuration. Command-Line And Scripting -------------------------- Goodvibes can be used and abused with the command-line. The command ``goodvibes-client`` gets you started, and provides basic control over Goodvibes. The main purpose of this client is actually to ease development and debug, and that's why it's a bit rough on the edge, and doesn't offer everything that is provided by the user interface. But it's still good enough for people who prefer the command-line over a graphical ui. However, if you want to write some scripts that interact with Goodvibes, I recommend **NOT TO** use the client, which might be modified anytime, and doesn't promise any backward compatibility. Instead, you should make good use of the D-Bus interfaces implemented by Goodvibes. Goodvibes implements and exposes two interfaces on D-Bus (assuming you didn't disable it in the preferences): * the native D-Bus interface is actually the one used by ``goodvibes-client`` to communicate with Goodvibes. This interface might be modified anytime a new version is released, there's no plan to maintain any compatibility over different versions. * the MPRIS2 D-Bus interface is an interface implemented by many media players on GNU/Linux. To get you started with D-Bus, you should know that there are different dbus command-line tools around: * ``dbus-send`` is the most basic tool, provided with the dbus daemon. * ``gdbus`` is provided by GLib. * ``qdbus`` is provided by Qt. * ``busctl`` is provided by Systemd. If you plan to write scripts that interact with Goodvibes, I recommend to use the MPRIS2 interface as much as possible, mainly for two reasons: * It's well defined, stable over time, therefore your script will not break when a new version of Goodvibes is released. * You will be able to re-use your work with any other media player that support the MPRIS2 interface. And there's a lot of them. You should refer to the `MPRIS2 Specification `_ for more details. A quick example with ``qdbus``:: qdbus org.mpris.MediaPlayer2.Goodvibes \ /org/mpris/MediaPlayer2 \ org.mpris.MediaPlayer2.Player.Metadata Conky Example ------------- Here's a snippet to integrate Goodvibes in `Conky `_:: ${if_match "${execi 10 qdbus org.mpris.MediaPlayer2.Goodvibes /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.PlaybackStatus}" == "Playing"}\ Goodvibes Info Station:${execpi 10 qdbus org.mpris.MediaPlayer2.Goodvibes /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Metadata | grep "^goodvibes:station:" | cut -d':' -f3-} Artist - Track:${execpi 10 qdbus org.mpris.MediaPlayer2.Goodvibes /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Metadata | grep "^xesam:title:" | cut -d':' -f3-}\ ${else}\ Goodvibes is not playing\ ${endif} There are a few things to notice here: * **Don't use the native interface** ``io.gitlab.Goodvibes``, use the MPRIS2 interface instead, ie. ``org.mpris.MediaPlayer2.Goodvibes``. The reason being that when you query Goodvibes on its native interface, it will automatically be launched by D-Bus in case it's not running. * If for some reason you really need to use the native interface, then use a D-Bus command-line tool that allows you to disable auto-starting the service. As far as I know, you can only do that with ``busctl --user --auto-start=no``. One last word: I'm not a Conky guru, don't quote me on that snippet above ;) Custom User-Agent ----------------- If for some reasons you need to use a different user-agent for a specific radio station, it's possible. You just need to edit manually the file where the stations are saved. Where is this file? * ``~/.local/share/goodvibes/stations.xml`` if Goodvibes was installed with the package manager. * ``~/.var/app/io.gitlab.Goodvibes/data/goodvibes/stations.xml`` if installed with Flatpak. In this file, add the field ```` to the station that you want to customize. For example:: http://example.com/radio Example Radio Custom/1.0 GTK CSS Theming --------------- Since version `0.7.1`, it's possible to add your own custom CSS to make Goodvibes look different. The location for the CSS file depends on your installation: * ``~/.local/share/goodvibes/style.css`` if Goodvibes was installed with the package manager. * ``~/.var/app/io.gitlab.Goodvibes/data/goodvibes/style.css`` if installed with Flatpak. For example:: /* Draw a border around the window */ window { border: 5px solid; } /* Fancy buttons */ @define-color green rgba(0,133,66,1); @define-color yellow rgba(255,224,0,1); @define-color red rgba(231,0,1,1); button { border-style: none; border-radius: 50%; } button:hover:nth-child(3n+1) { background-color: @green; } button:hover:nth-child(3n+2) { background-color: @yellow; } button:hover:nth-child(3n+3) { background-color: @red; } For more details, refer to the documentation: * `GTK CSS Overview `_ * `GTK CSS Properties `_ goodvibes-v0.7.2/docs/notes/000077500000000000000000000000001414415210700157465ustar00rootroot00000000000000goodvibes-v0.7.2/docs/notes/bookmarks.md000066400000000000000000000015561414415210700202670ustar00rootroot00000000000000Bookmarks ========= ## GLib - [First steps with GSettings](https://blog.gtk.org/2017/05/01/first-steps-with-gsettings/) ## GTK - [GTK Development Blog](https://blog.gtk.org/) - [Those “realize†& “map†widget signals](https://blogs.gnome.org/jnelson/2010/10/13/those-realize-map-widget-signals/) - [Another Container for Gtk+ 3.12 | GtkActionBar](http://worldofgnome.org/another-container-for-gtk-3-12-gtkactionbar/) - Composite Widgets - [Announcing Composite Widget Templates](https://blogs.gnome.org/tvb/2013/04/09/announcing-composite-widget-templates/) - [Embeding GtkBuilder UI definitions into GObject classes!](https://blogs.gnome.org/xjuan/2012/07/13/embeding-gtkbuilder-ui-definitions-into-gobject-classes/) - [GTK+: Aligning / Justification in text widgets](https://www.murrayc.com/permalink/2015/03/02/gtk-aligning-justification-in-text-widgets/) goodvibes-v0.7.2/docs/notes/gobject-lessons-learned.md000066400000000000000000000261121414415210700230030ustar00rootroot00000000000000GObject lessons learnt ====================== Just sharing the long journey of learning GObject. How Do I Learn GObject? ----------------------- First, you need to bookmark the official documentation. It's the best place to start, but also a good place to go back afterward. Things that don't make sense at the first reading will become clear as you go further. - [GObject Reference Manual](https://developer.gnome.org/gobject/stable/) Second, there's one essential article that nicely sums up the GObject construction process, and raise some important points around it. This is the most important blog post you'll come accross, read it again and again. - [A gentle introduction to gobject construction](https://blogs.gnome.org/desrt/2012/02/26/a-gentle-introduction-to-gobject-construction/) At last, you need some good examples. You will find it first within the GObject library itself, but you will need more. I recommend the main GObject based libraries, and applications where development is very active. You will find sometimes that the code is very up-to-date with the latest best practices, so it's good to look in different codes. Warm up your git, sharpen your grep, and there you go! - [GLib](https://gitlab.gnome.org/GNOME/glib) - [GTK](https://gitlab.gnome.org/GNOME/gtk) - [GStreamer](https://gitlab.freedesktop.org/gstreamer) - [Rhythmbox](https://gitlab.gnome.org/GNOME/rhythmbox) - [Totem](https://gitlab.gnome.org/GNOME/totem) Good Practices -------------- #### Follow The Conventions The first place to learn about good practices is the GObject conventions page. It's more than good practice actually. GObject expects you to follow a few conventions, so that code generation tools (`glib-mkenum`) and other stuff can actually work. Don't try to avoid that. - [GObject Conventions](https://developer.gnome.org/gobject/stable/gtype-conventions.html) #### Use `G_DECLARE_*` And `G_DEFINE_*` Do not clutter the header file with too much boilerplate, as it's often seen in some old GObject-based code. - [G_DECLARE_FINAL_DERIVABLE_TYPE](https://blogs.gnome.org/desrt/2015/01/27/g_declare_finalderivable_type/) #### Do The Minimum In `_init()` There should be almost nothing in the `init()` function. Most of the time, it just boils down to setting up the private pointer, if any. For all the rest, it's better to implement it in the `constructed()` method. - [A gentle introduction to gobject construction](https://blogs.gnome.org/desrt/2012/02/26/a-gentle-introduction-to-gobject-construction/) #### Always Use A Private Pointer You will notice that in here, I never put anything in the object structure. I ALWAYS use a private structure. Using a private structure is particularly useful for derivable objects, since for these objects, the object structure *must be* defined in the header (public) to allow inheritance. And of course, you want to keep your private data private, so it makes sense to define it within the implementation, therefore using a private structure. But when the object is not derivable, and therefore the object structure *should be* defined in the implementation, what's the point of having a private struct? 1. Consistency - you want all your objects to look the same. 2. Code Refactoring - when your objects change from final to derivable, or the other way around, you'll be happy that you don't have to constantly move your data from the object struct to the priv struct. - [Changing quickly between a final and derivable GObject class](https://blogs.gnome.org/swilmet/2015/10/10/changing-quickly-between-a-final-and-derivable-gobject-class/) Also, I *always* use a pointer named `priv` to access it. It's just a convention, but a very common one, and doing it everywhere makes the whole code consistent. HOWEVER, I've read somewhere that I shouldn't! But I didn't have time to dig into that yet. #### Best Way To Set Properties The correct way to set an object pointer. It's not as trivial as it seems. - [Use g_set_object() to simplify (and safetyify) GObject property setters](https://tecnocode.co.uk/2014/12/19/use-g_set_object-to-simplify-and-safetyify-gobject-property-setters/) Quick FAQ --------- #### How To Change A Construct-Only Property Via Inheritance This questions is asked on StackOverflow: - Unfortunately, the answer given by Emmanuele Bassi is wrong: setting the property in the `constructed()` virtual method won't work. Now that I've been dealing with GObject for a while, I see 3 ways to do that: - hack around with the `constructor()` method, as suggested in . - don't use construct-only properties, use construct properties. Then you can modify it in `constructed()`. - set the desired values within a wrapper, ie. `my_inherated_object_new()`. If you only create your objects with these wrappers, it's fine. However that is not binding friendly. I personally settled for the third solution within Goodvibes. #### How To Implement A Singleton This is discussed here and there on the Net. - [GObject API Reference](https://developer.gnome.org/gobject/unstable/gobject-The-Base-Object-Type.html#GObjectClass) - [How to make a GObject singleton](https://blogs.gnome.org/xclaesse/2010/02/11/how-to-make-a-gobject-singleton/) #### How To Have Protected Fields GObject doesn't provide any solution to have protected fields. I managed to do without up to now, but just in case, here's a workaround. Basically, it's just about defining the private structure in the header (therefore making it public), and naming it 'protected' instead of 'private'. - [GObject and protected fields – simple hack](http://codica.pl/2008/12/21/gobject-and-protected-fields-simple-hack/) #### Any Design Patterns Out There? For design pattern examples, the best is to hunt for it directly in the source code of some big projects. Additionally, here are some discussions about it. - - [GObject design pattern: attached class extension](https://blogs.gnome.org/swilmet/2017/08/09/gobject-design-pattern-attached-class-extension/) Been there, done that --------------------- #### Should I Set A Property Asynchronously? Sometimes, setting a property is just about setting a variable. But at other times, it might trigger quite a bit of code execution. For example, at some point I had an `enable` property, and when I set it to true, it really enables the whole object, and possibly a lot of things happens. For some technical reasons, I had to actually delay the execution of the enable/disable code, with `g_idle_add()`. And since I wanted the value of the `enable` property to really represent the state (enabled or disabled), I decided to change the value of this property only *after* this asynchronous code was executed. Ie, only when the `g_idle_add` callback runs. Only then the `notify` signal was sent. This seems OK at a first glance. But in practice, there's a problem. The code that sets the property may not want to receive the notify signal, and therefore use `g_signal_handler_block()` before setting the property, and `g_signal_handler_unblock()` after setting it. If I set the property asynchronously, and therefore send the notify signal afterwards in a callback, the code that sets the property will receive the notify signal, even if it tried to block it. So it creates a bit of an unexpected behavior for the code setting the property. So... Should I set a property asynchronously? The answer is NO. For this particular problem of the `enable` property, I solved it by having two properties: - `enabled`, readwrite, which reflects how the object is *configured*. Therefore this property is set synchronously. - `state`, readable only, which gives the current state of the object, and go from *disabled* to *enabling* to *enabled*. This property is tied to `enabled`, but its value changes aynchronously. With two properties, one can connect to the `enabled` property to know how the object is configured, and one can connect to the `state` property to know the real object state. #### Should I Define A Base Class For My Object Hierarchy? For example, GStreamer has its own `GstObject`. And at some point, I thought it was cool, and furthermore it seemed that it would solve some of the problems I faced. So I defined my own `GvObject` for a while. I then removed it quickly enough. Here's my feedback from that experience. First questions that arises: should all my objects inherit from GvObject? Or only those who have a solid reason for that? At first, I went for the latter, because I thought that if an object has no reason to inherit GvObject, then it should remain as lightweight and simple as possible, and inherit GObject. So I ended up with a mix of objects that were either GObject derived, either GvObject derived. It's a bit of a mess suddenly, because the separation between both, and the reasons for choosing the right parent, was all very unclear. Then, serialization kicked in, I wanted to be able to serialize objects to save it as configuration (I didn't use GSettings by the time). And suddenly I had another dilemna. If the serialization API targets GvObject, then GObject derived objects were left out, and couldn't be serialized. On the other hand, if the API targeted GObject, I couldn't take advantage of some of the specific features GvObject, that were actually very helpful for serializing. Which made me change my mind: if I define a GvObject base-class, then I should embrace it completely, and have every object inherit it. It looked nice and consistent now, but I just added an additional layer, I just made things a bit more complicated, and I was wondering if it was worth it. But still, I was happy because now the serialization was easier to implement, since I embedded some convenient stuff for it within my base class GvObject. But then, another problem kicked in. The UI part is implemented by inheriting GTK widgets. How will I serialize this GObject-derived objects, now that the serialization API targets the GvObject type? My only solution here was to rewrite the UI, use composition instead of inheritance, so that my UI objects could be derived from GvObject and be serializable. Which promised to be quite some work of moving code around... I didn't do that though. Instead, I came back to GObject for the base class. For all the problems that I solved with GvObject, I thought again and solved them differently, and in a better way. After all that, I draw some conclusions for myself: - Think twice before defining a base class for your object hierarchy. It has its good points, for sure, but also its drawbacks. - If you work with GObject-based libraries, be aware that the base class you define can't help for objects from those libraries, or that you inherit from those libraries. Your lower denominator for the whole set of classes you deal with will always be GObject. So, does you base type really helps, or does it get in the way? - Ask yourself if you can't solve your problem with an interface instead. Interface are more flexible. Even if you use objects from other libraries, you can still inherit them, and implement an interface in the child. goodvibes-v0.7.2/docs/notes/gst-tools.md000066400000000000000000000035211414415210700202240ustar00rootroot00000000000000GST Tools ========= This is a getting started to `gst-launch` and friends. ## Get started In order to print a list of all plug-ins and elements available: gst-inspect-1.0 Then you might want details about a particular element: gst-inspect-1.0 autoaudiosink ## Play a stream Let's get started with a curated selection of audio streams: AAC_STREAM=http://ice2.somafm.com/indiepop-128-aac MP3_STREAM=http://direct.fipradio.fr/live/fip-midfi.mp3 OGG_VORBIS_STREAM=TODO OGG_OPUS_STREAM=TODO HLS_AAC_STREAM=https://stream.radiofrance.fr/fip/fip_hifi.m3u8 DASH_STREAM=https://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/dash/nonuk/dash_low/aks/bbc_radio_one.mpd STREAM=$AAC_STREAM The most simple way to play a stream: gst-launch-1.0 playbin uri="${STREAM:?}" A bit more complicated, for the same result: gst-launch-1.0 souphttpsrc location="${STREAM:?}" ! decodebin ! autoaudiosink Let's use ALSA for the audio output, and select the second sound card: gst-launch-1.0 souphttpsrc location="${STREAM:?}" ! decodebin ! alsasink device=hw:2 Delay the audio output (nanoseconds): gst-launch-1.0 souphttpsrc location="${STREAM:?}" ! decodebin ! autoaudiosink ts-offset=5000000000 Ignore invalid SSL certificates (for HTTPS streams): gst-launch-1.0 souphttpsrc ssl-strict=false location="${STREAM:?}" ! fakesink ## Debug a stream `gst-launch-1.0` comes with a bunch of handy options, like: - `-t` to display metadata - `-m` to display messages - `-v` to display a lot of details, and `-X` to exclude some properties For full details about a stream, without continuous flooding due to tags: gst-launch-1.0 playbin uri="${STREAM:?}" -v -X tags To display only the caps for this stream: stdbuf -oL -eL gst-launch-1.0 playbin uri="${STREAM:?}" -v | sed -n 's/.*caps = //p' | uniq goodvibes-v0.7.2/docs/notes/implementation-details-core.md000066400000000000000000000251651414415210700236770ustar00rootroot00000000000000Implementation Decisions - Core =============================== Some decisions I made early on regarding the implementation. I write it down here just for my own remembering. Why Implementing The MPRIS2 D-Bus API First? -------------------------------------------- Implementing the MPRIS2 D-Bus API was one of the first thing I did. It was not the easiest thing to start with though, but it forced me to implement all the common features expected from a music player. Once MPRIS2 was implemented, I knew I had the right design for the core, and indeed, I never had to rework it afterward. So I really recommend that to anyone writing a music player: implement the MPRIS2 D-Bus API early, it will help you to get the design right, you won't regret the pain. Why Not Choosing Python? ------------------------ I definitely wanted to implement the MPRIS2 D-Bus API. Which means implementing a D-Bus server. Implementing a fully featured D-Bus server in Python is a bit of a frustrating experience. Properties are not fully handled. See: - The quick answer, as I understood it, is that the D-Bus python binding is broken by design, and there will be no attempt to fix it. This bug will stay opened forever. If you have a look at QuodLibet code, you will see that these guys had to write quite a bit of code to add support for properties. But they did it well, and the code is easy to take out of QuodLibet. So my guess is that the best way to fully implement the MPRIS2 D-Bus API is to copy/paste some code from QL. It's suggested here and there to use another Python D-Bus implementation, like GLib's implementation GDBus, through PyGObject. This is perfect if you're a D-Bus client, however if you want to implement a D-Bus daemon? Apparently, at the time of this writing, this is yet to be implemented. See progress here: - So, with this situation, I decided to write in C instead, as I was sure that I would be able to implement the MPRIS2 D-Bus API properly, without having to hack my way around broken things. Note that it's probably not an exhaustive view of the situation, and I might have overlooked other solutions. Another reason is that Goodvibes is a minimalistic app doing very little, and it's pleasant to see it start in a fingersnap. With Python you don't quite get that, especially if you're the first Python app being launched and there's nothing cached yet. On the other hand, there was a BIG reason to choose Python rather than C: development time. Writing in Python is so much faster, you have no idea until you have the chance to maintain both a C and a Python desktop app. Why Choosing GObject? --------------------- GObject has its cons, that I was very aware of when I started to learn it. As often when I face a new technology, I was a bit reluctant and only saw the downside: - Boilerplate: there so much copy/paste to do that I ended up writing a script to automate C/H file creation. - Magic: some of the boilerplate is hidden by macros. It's great since it saves code typing, but it's bad since it makes some definitions implicit. So you end up calling functions or macros that are defined nowhere, and it's a bit unsettling at first. GObject code looks like dark magic for the newcomer. - Steep learning curve: yep, learning GObject by yourself can be a bit tedious. On the other hand, GObject brings in everything I needed to implement cleanly this MPRIS2 D-Bus API: - signals: from the moment I have signals internally, there's no need to hack around to send signals on D-Bus. It's trivial. - properties: same thing. From the moment the code is GObject-oriented, getting and setting properties through D-Bus is trivial. Plus, GObject allows you to write quite clean object-oriented code in C. The memory management through reference counting is nice and clean. And the more you go on working with GObject libraries (GIO, GTK, GStreamer), the more you realise that your code benefits from being GObject oriented. You can use high-level functions, bind your objects here and there, and a lot of problems are solved by the libraries you use. But these libraries expect GObjects, so if your code is not written as such, you might be left out and bitter, rewriting stuff that is already implemented in libraries. So that's maybe the most important: if your application lives in a GObject ecosystem, make it GObject, and it will fit in nicely, good surprises will abound all along. But be aware that GObject is much more than a library. It really changes the architecture of your application, which basically becomes event-driven. Which is exactly what you want when you're writting a GUI application. Why Not Using Gdbus-Codegen? ---------------------------- First, I'm not a big fan of code generation. Second, as far as I remember, the generated code produces some warnings at compile time, and I usually compile with the flag `-WError`. It's annoying. Third, there's a bug about `Property.EmitsChangedSignal`, that has been pending forever, and in all likelihood will never be fixed. - This bug affects the MPRIS2 D-Bus API, so I prefer to stay away from that `gdbus-codegen` and remain in control of the code. Why Using Explicit Notify Only? ------------------------------- This is just an convention in use in the code, I just find it more logical when I started, and I never had to come back on this choice. For this reason, every property is created with the `G_PARAM_EXPLICIT_NOTIFY` flag, and emitted only if the value of the property changed. It's a bit more job on the object where the properties are changed, but it's less job on the object listening for signals, since they can be sure that they have to do something when the signal handler is invoked. Why Having This Feature Thing? ------------------------------ You will see in the code that some features are quite isolated from the rest of the code. First, there's a `GvFeature` object, that is the parent of all features. Then, features just need to inherit this object, connect to some signals from the core, and that's it. Isolating features is simply a way to keeps things cleanly separated, and to allow to disable some code at compile time, without having to cripple the code with `#ifdef`. It's possible thanks to the fact that features just react to signals. The core of the code itself is blissfully ignorant of the existence of these features, and never explictly invokes any function from there. Features are created once at init, destroyed once at cleanup, and that's the only place where you will see `#ifdef`. They live in a world of their own. Such a strong isolation has some benefits: easier debug and maintenance, mainly. And it's nice to be able to disable it at compile-time, if ever people want to integrate the application in an environment where some of these features don't make sense (like on embedded devices). It also forces a better overall design. However, it can be a pain in the ass when a new feature shows up, and doesn't quite fit in, and the whole thing needs to be reworked, and all the existing features need to be updated to match the changes... Ultimately, with some improvements, these features could be loaded dynamically, and from this point they could be called plugins. And then, it's very common to have plugins in GNU/Linux media player, right? So, these features can be seen as a poor's man plugin system. Station List: Shuffle Implementation ------------------------------------ Things I've tried and didn't work. One idea was that the player would have two StationList objects. One would be ordered, the other would be shuffled. With that, the 'shuffle problem' is taken out of the StationList object (which is therefore esier to implement) and is then solved outside of it. Mainly in the player, I thought. In practice, it adds quite a lot of complexity, because these two lists must be kept in sync (from a more genereal point of view, from the moment you duplicate data, you need to keep it in sync, and that's why duplicating data is always something to avoid). Furthermore, the station list is a global variable, that can be accessed by anyone. How to handle duplication then? We could have both list (ordered and shuffled) global. But it would be a mess for the rest of the code. Always wondering which one you must deal with, and what if you want to add a new station? So the code is responsible for adding it to both list? And therefore, the code is responsible for keeping lists in sync? Of course not, the sync between both lists must be automatic, but if both are global, it means that they both must watch each other's signals to keep in sync. Or another object must watch both of them and keep them in sync. This doesn't sound sweet to my ears. Another possibility is to have only the ordered station list public, and the shuffled one private to the player, and then the player would be left with the responsability to keep the shuffled list in sync, somehow. Hmmm, I don't like that too much either. OK, so let's forget about that. If the shuffle problem is not to be solved outside of the list, then let's solve it within the list. We could have a 'shuffle' property added to the station list, and that's all. The code set it to true or false, and doesn't bother anymore. It kind of works, but the StationList API becomes confusing. Because if you look at it as it is, you will notice that some functions are expected to support the shuffle property (obviously, `next` and `prev` will return a different value depending on the shuffle property), while other actually don't care about the shuffle property (`append`, `prepend` only deal with the ordered list). So, completely hiding the 'shuffle' from the outside world is also not a very good solutions. At some point, when you use the API, you start to find it confusing. Even if you set the shuffle property, some functions keep on dealing with an ordered list internally. It's magic, and we don't like magic. Actually, from the user point of view, if you enable shuffle, the list still appears ordered in the ui. It just affects the previous and next actions. And the best is to see this behavior appearing in the API. So in the end, the best implementation of the shuffle I came with is a 50/50: both the Player object and the StationList object know about it, and both do their job handling it. It appears explicitely in the StationList API, and only makes sense for `next` and `prev`. The Player is the 'owner' of the shuffle setting, and feeds it to StationList when calling methods that require it. In the end, this implementation is the most simple, and leaves little place for magic. I've been happy with it so far. goodvibes-v0.7.2/docs/notes/implementation-details-ui.md000066400000000000000000000102331414415210700233520ustar00rootroot00000000000000Implementation Decisions - UI ============================= Some decisions I made early on regarding the implementation. I write it down here just for my own remembering. How much margin and spacing for the UI? --------------------------------------- When designing the UI in Glade, there's no margins by default, so the whole thing looks ugly at this point. Margins, spacings, paddings and such must be set, and it's up to me to decide how much. So I wonder: how much? I have no idea, and don't especially care, so I let someone else decide for me. I just followed the guidelines provided by GNOME. - [GNOME HUman Interface Guidelines](https://developer.gnome.org/hig/stable/) Why not using GtkDialog to handle preferences? ---------------------------------------------- Mainly, it's because a dialog requires a transient parent, aka another window that must be the parent. Ever had this warning in your console? Gtk-Message: GtkDialog mapped without a transient parent. This is discouraged. But Goodvibes might be running in `--status-icon` mode, and in such case it doesn't really have a main window to speak of. Loosely speaking, we could use the popup window as the transient parent, since it always exists. It's hidden most of the time, but it exists. But it looks a bit hacky to me. I don't really know why GTK needs a transient parent, and what it expects from this parent, but I'm almost sure the popup window is not up to the expectation. So in the end, I find it cleaner to use a window rather than a dialog. Should I use the GObject property details in the UI? ---------------------------------------------------- Short answer: No! It might be tempting though, simply to avoid duplicating strings. Indeed, in the preferences dialog, often a setting is mapped to an object's property. Given that a property already has a nick (that could be mapped to the setting label) and a blurb (ie. a description that could be mapped to the setting tooltip), why should we define these strings again in the glade file? Why don't we just set labels and tooltips from the property nick and blurb in the C code? It's tempting. Even more after reading this piece of the doc: - However, I have good reasons not to do so, both for the nicks and blurbs (although in the following, I'll only talk about nicks, for clarity). First, there's the context. Let's suppose you have a property that decides what action should be associated with a middle-click. In your code, an ideal nick for the property would be "Mouse Middle Click". However, assuming that in the UI you have a "Mouse" section for the settings related to the mouse, then you don't want to repeat the word "Mouse". The ideal name for the setting would then be "Middle Click". So, because of a different context, the best name for a property in the code, and the best name for the corresponding setting in the UI, might be different. Second, there's more problems when translation kicks in. Because then, the property nicks must be translatable, and if I want to be consistent, then **ALL** the properties should have a translatable nick. It means that those properties which don't appear in the UI will be translated for nothing. Useless work for translators, isnt't it? Or should I only translates the nicks that appear in the UI? But then how do I maintain this mess? What will happen, in the end, is that I'll have nicks marked as translatable, but don't appear in the UI, while others appear in the UI but are not marked as translatable... The REAL problem, here, is that there will be no warnings for these missing translations. So there will always be missing translations in each new release. So, here are my two main issues: context, and translations. In the end, my choice is to use nicks and blurbs only as hints for developpers passing by. For the UI, I'll define everything in Glade, or in the related C file. It solves the context issue, and also the translation issue, since Glade automatically mark strings as translatable, so I don't have to bother. More interesting reading: - goodvibes-v0.7.2/docs/notes/metadata.md000066400000000000000000000176161414415210700200630ustar00rootroot00000000000000Metadata ======== Metadata, or tags, everything I learnt so far. ## Plain HTTP streams, and the ICY protocol ### A bit of history "Plain HTTP" streaming means that the client downloads an infinite file, be it MP3, AAC or anything else. This is the "traditional" way of streaming radio stations, as it appeared in the late 1990s. The tricky thing is that the MP3 format only defines how to store audio data, and it doesn't have support for metadata (or tags, ie. title, artist, album and so on). It's not much of a problem for a MP3 file, where metadata are either prepended at the beggining of the file, either appended at the end. However, in an infinite MP3 stream, how to tell the client what is the title of the track that is being played? The folks from Nullsoft, who were developing SHOUTcast at the time, came up with a fairly simple solution: to embed the metadata in the audio stream at regular interval. On the receiving end, the client just needs to make sure to remove those chunks from the audio stream. This is what we'll call the **ICY protocol**, as the communication between the client and the server is based on HTTP headers starting with the prefix `icy-` (which stands for "I Can Yell", if ever you wondered). SHOUTcast is a proprietary solution. Icecast was released quickly afterward, as an open-source alternative. And there are surely many more streaming solutions around. However they all stick to the ICY protocol to send the metadata. References: - - - ### How it works Here's how it works, in a nutshell. In the original request to the server, the client sends `icy-metadata=1`, to say that it supports reading metadata from the stream. The server responds with `icy-metaint=xxxx` (metadata interval), to tell to the client where to find the metadata in the stream (it's a number of bytes). Then goes the MP3 stream, with the metadata embedded within. Regarding the support in GStreamer, there are two files of interest: - `gst-plugins-good/ext/soup/gstsouphttpsrc.c` is where the HTTP headers are handled. - `gst-plugins-good/gst/icydemux/gsticydemux.c` is where the metadata is extracted from the audio stream. Upon reception of `icy-metaint=xxxx`, GStreamer creates the capability `application/x-icy` with the property `metadata-interval` set accordingly. What we can expect to see in the HTTP dialog, according to `gstsouphttpsrc.c`: - `icy-name` → `GST_TAG_ORGANIZATION` - `icy-url` → `GST_TAG_LOCATION` - `icy-genre` → `GST_TAG_GENRE` Then, embedded in the stream, according to `gsticydemux.c`: - `StreamTitle` → `GST_TAG_TITLE` - `StreamUrl` → `GST_TAG_HOMEPAGE` The first set of tags can be seen among the GstTagList at the beginning of the stream, and then they quickly disappear. If an application wants to make use of it, it needs to cache it, and it should not overwrite it with `null` later on, when they don't appear in the GstTagList. For more details: gst-inspect-1.0 icydemux gst-inspect-1.0 souphttpsrc References: - - - - - ### Case studies Radio Paradise (legacy stream): - Stream: - 3 ICY HTTP headers: - `icy-name` : "Radio Paradise (128k mp3)" - `icy-url` : "https://radioparadise.com" - `icy-genre` : "Eclectic" - 2 ICY embedded metadata: - `StreamTitle`: "Ben Howard - In Dreams" - `StreamUrl` : picture url, current song SomaFM, Indie Pop Rocks: - Stream: - 3 ICY HTTP headers: - `icy-name` : "Indie Pop Rocks! [SomaFM]" - `icy-url` : "http://somafm.com" - `icy-genre` : "College Indie" - 2 ICY embedded metadata: - `StreamTitle`: "Starflyer 59 - Wicked Trick" - `StreamUrl` : picture url, logo of the radio station Pedro's Broadcasting Basement: - Stream: - 3 ICY HTTP headers: - `icy-name` : "PBB: Pedro's Broadcasting Basement" - `icy-url` : "http://www.pedrobroadcast.com" - `icy-genre` : "Various, Eclectic" - 1 ICY embedded metadata: - `StreamTitle`: "(R) THE ABYSSINIANS -SATTA MASSA GANA -MABRAK - blood and fire" FIP (legacy stream): - Stream: - 1 ICY HTTP header: `icy-name`: fip-midfi.mp3 - No ICY embedded metadata. Here comes some GStreamer logs, for the sake of it: # SomaFM - AAC stream, with ICY STREAM=http://ice2.somafm.com/indiepop-128-aac /GstPlayBin/GstURIDecodeBin: caps = application/x-icy /GstPlayBin/GstURIDecodeBin: caps = audio/mpeg /GstPlayBin/GstURIDecodeBin: caps = audio/x-raw # PBB - MP3 stream, with ICY STREAM=https://pbbradio.com:8443/pbb128 /GstPlayBin/GstURIDecodeBin: caps = application/x-icy /GstPlayBin/GstURIDecodeBin: caps = audio/mpeg /GstPlayBin/GstURIDecodeBin: caps = audio/x-raw # FIP (legacy) - MP3 stream, no ICY STREAM=https://direct.fipradio.fr/live/fip-midfi.mp3 /GstPlayBin/GstURIDecodeBin: caps = audio/mpeg /GstPlayBin/GstURIDecodeBin: caps = audio/x-raw And now a few remarks to draw from that. We could use `icy-name` and `icy-url`, and display the name of the station as a clickable link. However, for Goodvibes user, it's of little value, because users already know what station they're listening to, and where to find the homepage on the web. We can also see that the field `icy-name` can be junk, in such case displaying it will just be annoying. When it comes to the embedded tags, not every station sets it, far from that. When they do, we don't know if `StreamTitle` is of the form "artist - title", or "title - artist", or anything else really. All we can do is display it "as is". Interestingly, `StreamUrl` is used for the URL of a picture in some cases. SomaFM uses it for the logo of the radio station, while Radio Paradise goes as fas as using it for the picture of the current song. It's kind of nice, but it's of little interest for Goodvibes, for various reasons. We could try very hard, read this field, and if it's an URL, download it, and if it turns out to be a picture, display it (except that right now, in the UI, pictures have nowhere to be displayed). But heck, that's a lot of effort, and I don't even know how widely this field is used for this purpose... At the end of the day, the only thing supported by Goodvibes at the moment is the embedded metadata `StreamTitle`, and I don't think that will change. ### What about OGG streams? The reality is that OGG streams are not common. Seems like MP3 and AAC rule the game, and ICY is the most common way to stream the metadata. This beind said, from , there are two ways to have metadata in an OGG stream. 1) is the traditional icy protocol, and 2) is apparently about having tags straight in the stream. Let's check this stream: STREAM=http://play.global.audio/nrj_low.ogg Indeed, we see no caps `application/icy`, and still we get tags that are updated on a regular basis. Even though there's only a `title` tag, nothing more. ### What about StreamTitle when there's no music? It's up to the radio broadcaster. Things I've seen so far: - PBB interludes: "PBB - Luckily We Still Have Music" ## MPEG-DASH streams So far I've never seen any metadata with a DASH stream. It seems that DASH has support for metadata, and it seems that there's some work in progress to add support in GStreamer: - #762: add support for DASH events - ## HLS streams So far I've never seen any metadata with HLS streams. goodvibes-v0.7.2/docs/notes/under-the-hood.md000066400000000000000000000027011414415210700211120ustar00rootroot00000000000000Under the hood ============== Features -------- #### Notifications Notification support is provided by GApplication. Under the hood, two backends exist out there: `org.freedesktop.Notifications` and `org.gtk.Notifications`. Glib gives preference to the Gtk backend in case both are available. 2018-11, Flatpak, Freedesktop runtime `18.08`, notifications don't seem to work. 2020-01, Flatpak, Freedesktop runtime `19.08`, notifications work. #### Multimedia Keybinding Currently implemented with libkeybinder, which works at the X11 level to take global keybindings. Obviously this won't do with Wayland. Another possibility is to use D-Bus: `org.gnome.SettingsDaemon.MediaKeys`, but this is GNOME-specific as you can see (I think there's also a Mate-specific `org.mate.SettingsDaemon` D-Bus service). There's a discussion at But the truth is: keybinding is actually not needed if the desktop already has some kind of MPRIS2 support, and also grabs the multimedia keys by default. Which is the case for GNOME and most modern desktops I suspect. In this case, when a multimedia key is pressed, it's intercepted by GNOME, which forwards that to the current MPRIS2 player active. So the multimedia keybinding feature in Goodvibes is mostly useful for stripped down desktop environments with no MPRIS2 support. And in this case it's useless to try the GNOME daemon, so we're just fine implementing X11 keybinding only. goodvibes-v0.7.2/goodvibes-launcher.sh000077500000000000000000000017511414415210700200110ustar00rootroot00000000000000#!/bin/bash # The launcher is for people who want to run Goodvibes from the source tree, # without installing it. GLib and GTK need to access some shared resources, # so we need to tell them to look for it in-tree. fail() { echo >&2 "$@"; exit 1; } BUILDDIR=${BUILDDIR:-build} GOODVIBES=$BUILDDIR/src/goodvibes [ -d $BUILDDIR ] || fail "Build directory '$BUILDDIR' does not exit" [ -x $GOODVIBES ] || fail "Executable '$GOODVIBES' not found" # XDG_DATA_DIRS is needed for: # - GTK to load local icons from '$BUILDDIR/data/icons' [ "$XDG_DATA_DIRS" ] \ && XDG_DATA_DIRS="$BUILDDIR/data:$XDG_DATA_DIRS" \ || XDG_DATA_DIRS="$BUILDDIR/data:/usr/local/share:/usr/share" export XDG_DATA_DIRS # GSETTINGS_SCHEMA_DIR is needed for: # - GLib to load local settings schemas from '$BUILDDIR/data' # If you don't define it, you'll be hit by something like that: # [GLib-GIO] Settings schema '...Goodvibes.Core' is not installed export GSETTINGS_SCHEMA_DIR="$BUILDDIR/data/" exec $GOODVIBES "$@" goodvibes-v0.7.2/meson.build000066400000000000000000000104261414415210700160330ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only project('goodvibes', 'c', version: '0.7.2', license: 'GPLv3', meson_version: '>= 0.49.0', default_options: [ 'buildtype=debugoptimized', 'c_args=-Wshadow', 'c_std=gnu99', 'warning_level=2', # -Wextra ] ) # Paths prefix = get_option('prefix') assert(prefix.startswith('/'), 'Prefix is not absolute: "@0@"'.format(prefix)) bindir = join_paths(prefix, get_option('bindir')) datadir = join_paths(prefix, get_option('datadir')) localedir = join_paths(prefix, get_option('localedir')) mandir = join_paths(prefix, get_option('mandir')) # Modules gnome = import('gnome') i18n = import('i18n') # Project settings gv_binary = meson.project_name() gv_version = meson.project_version() gv_name_lowercase = 'goodvibes' gv_name_camelcase = 'Goodvibes' gv_name_uppercase = 'GOODVIBES' gv_application_id = 'io.gitlab.Goodvibes' gv_application_path = '/io/gitlab/Goodvibes' gv_icon_name = gv_application_id gv_homepage = 'https://gitlab.com/goodvibes/goodvibes' gv_online_help = 'https://goodvibes.readthedocs.io' gv_copyright = 'Copyright (C) 2015-2021' gv_author_name = 'Arnaud Rebillout' gv_author_email = 'elboulangero@gmail.com' # Goodvibes Core cc = meson.get_compiler('c') math_dep = cc.find_library('m') glib_req = '>= 2.56' glib_dep = dependency('glib-2.0', version: glib_req) gobject_dep = dependency('gobject-2.0', version: glib_req) gio_dep = dependency('gio-2.0', version: glib_req) gio_unix_dep = dependency('gio-unix-2.0', version: glib_req) gst_req = '>= 1.14' gst_dep = dependency('gstreamer-1.0', version: gst_req) gst_base_dep = dependency('gstreamer-base-1.0', version: gst_req) gst_audio_dep = dependency('gstreamer-audio-1.0', version: gst_req) libsoup_req = '>= 2.42' libsoup_dep = dependency('libsoup-2.4', version: libsoup_req) gv_feat_console_output = get_option('feat-console-output') gv_feat_dbus_server = get_option('feat-dbus-server') # Goodvibes UI gv_ui_enabled = get_option('ui-enabled') if gv_ui_enabled gv_feat_hotkeys = get_option('feat-hotkeys') gv_feat_inhibitor = get_option('feat-inhibitor') gv_feat_notifications = get_option('feat-notifications') else gv_feat_hotkeys = false gv_feat_inhibitor = false gv_feat_notifications = false endif if gv_ui_enabled gtk_req = '>= 3.16.0' gtk_dep = dependency('gtk+-3.0', version: gtk_req) endif if gv_feat_hotkeys keybinder_req = '>= 0.3' keybinder_dep = dependency('keybinder-3.0', version: keybinder_req) endif # Test suite # Tests are enabled either when mutest-1 is found in the system, # either when mutest is fetched as a subproject. Disabled otherwise. mutest_dep = dependency('mutest-1', fallback: [ 'mutest', 'mutest_dep' ], default_options: ['static=true'], required: false, disabler: true, ) # Subdirectories subdir('data') subdir('src') subdir('po') # Post-install meson.add_install_script('scripts/meson/post-install.sh', datadir) # Summary summary = [ '', '--------------------------------', '', ' @0@ - @1@'.format(meson.project_name(), meson.project_version()), '', ' Core', ' Console output: @0@'.format(gv_feat_console_output), ' D-Bus server : @0@'.format(gv_feat_dbus_server), '', ' UI', ' Enabled : @0@'.format(gv_ui_enabled), ' Hotkeys : @0@'.format(gv_feat_hotkeys), ' Inhibitor : @0@'.format(gv_feat_inhibitor), ' Notifications : @0@'.format(gv_feat_notifications), '', '--------------------------------', '', ] message('\n'.join(summary)) # Install the git pre-commit hook git_hook_installer = join_paths(meson.source_root(), 'scripts/meson/install-git-hook.sh') git_hook = run_command(git_hook_installer) if git_hook.returncode() == 0 message(git_hook.stdout().strip()) endif # Targets to generate tag files git = find_program('git', required: false) if git.found() git_dir = join_paths(meson.source_root(), '.git') all_files = run_command(git, ['--git-dir', git_dir, 'ls-files', ':/*.[ch]']) all_files = files(all_files.stdout().split()) etags_output = join_paths(meson.source_root(), 'TAGS') run_target('etags', command: ['env', 'etags', '-o', etags_output] + all_files) ctags_output = join_paths(meson.source_root(), 'tags') run_target('ctags', command: ['env', 'ctags', '-o', ctags_output] + all_files) endif goodvibes-v0.7.2/meson_options.txt000066400000000000000000000016031414415210700173230ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only option('tests', type: 'boolean', value: true, description: 'Build the test suite (requires mutest)') option('ui-enabled', type: 'boolean', value: true, description: 'Enable the graphical user interface (depends on GTK)') option('feat-console-output', type: 'boolean', value: true, description: 'Enable the console-output feature') option('feat-dbus-server', type: 'boolean', value: true, description: 'Enable the dbus-server feature') option('feat-hotkeys', type: 'boolean', value: true, description: 'Enable the hotkeys feature (requires ui, depends on keybinder)') option('feat-inhibitor', type: 'boolean', value: true, description: 'Enable the inhibitor feature (requires ui)') option('feat-notifications', type: 'boolean', value: true, description: 'Enable the notifications feature (requires ui)') goodvibes-v0.7.2/po/000077500000000000000000000000001414415210700143045ustar00rootroot00000000000000goodvibes-v0.7.2/po/LINGUAS000066400000000000000000000001161414415210700153270ustar00rootroot00000000000000cs nl fr nb_NO de ru hu es en_US pt_PT sk pl id pt_BR it te hr ca pt mr tr da goodvibes-v0.7.2/po/POTFILES000066400000000000000000000013161414415210700154550ustar00rootroot00000000000000# Integration files data/io.gitlab.Goodvibes.appdata.xml.in data/io.gitlab.Goodvibes.desktop.in # Glade ui files src/ui/resources/app-menu.ui src/ui/resources/playlist-view.glade src/ui/resources/prefs-window.glade src/ui/resources/shortcuts-window.ui src/ui/resources/station-dialog.glade src/ui/resources/station-view.glade src/ui/resources/status-icon-menu.ui # Source files src/main.c src/core/gv-engine.c src/core/gv-player.c src/core/gv-station-list.c src/ui/gv-main-window.c src/ui/gv-playlist-view.c src/ui/gv-prefs-window.c src/ui/gv-station-context-menu.c src/ui/gv-station-dialog.c src/ui/gv-station-view.c src/ui/gv-status-icon.c src/feat/gv-hotkeys.c src/feat/gv-inhibitor.c src/feat/gv-notifications.c goodvibes-v0.7.2/po/ca.po000066400000000000000000000353111414415210700152320ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-06-10 10:33+0000\n" "Last-Translator: Adolfo Jayme Barrientos \n" "Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.7-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Reproduïu ràdios web" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" "El Goodvibes és un reproductor de ràdio per internet simple per al GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Inclou totes les funcionalitats esperables d’un reproductor d’àudio, com ara " "compatibilitat amb les tecles multimèdia i el MPRIS2, notificacions i " "supressió de l’estat de repòs del sistema." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Reproductor de ràdio" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Àudio;Ràdio;Reproductor;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Reprodueix/atura" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Afegeix una emissora" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Preferències" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Teclat" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Tanca" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Surt" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "No s’ha seleccionat cap emissora" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Aturat" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Botó per a tancar" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Aplicació" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Reprodueix automàticament en iniciar" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Canonada de sortida personalitzada" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Aplica" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Reproducció" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Impideix el repòs mentre es reprodueixi la ràdio" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Sistema" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Servidor natiu del D-Bus" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "Servidor MPRIS2 del D-Bus" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "General" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Variant del tema" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Per defecte del sistema" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Prefereix la clara" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Prefereix la fosca" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Finestra" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Envia notificacions" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Notificacions" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Sortida de la consola" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Consola" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Visualització" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Tecles multimèdia" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Teclat" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Clic mig" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Silencia" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Desplaçament" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Canvia d’emissora" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Canvia el volum" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Ratolí (mode d’icona d’estat)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Controls" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Nom" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Excepció de seguretat" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "Suprimeix" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadades" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Títol" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artista" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Àlbum" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Gènere" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Any" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Comentari" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Emissora" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Fluxos" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Agent d’usuari" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Taxa de bits" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Freqüència de mostratge" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Canals" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Còdec" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Fluxos" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "No s’ha pogut analitzar la descripció de la canonada" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "S’està reproduint" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "S’està connectant…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "S’està omplint la memòria intermèdia…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "«%s» no és una emissora coneguda ni un URI vàlid" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "No s’ha pogut desar la llista d’emissores" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "Voleu afegir una excepció de seguretat?" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "S’ha produït un error en intentar reproduir %s." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Cancel·la" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Afegeix" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "URL" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Error" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "S’ha inhabilitat la funció durant la compilació." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Acció a fer quan es fa clic al botó Tanca." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "El paràmetre no està disponible si utilitzeu el mode d’icona d’estat." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Si s’ha d’iniciar la reproducció automàticament en engegar." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Si s’ha d’utilitzar una canonada de sortida personalitzada." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "La canonada de sortida del GStreamer que s’utilitza per a la reproducció. " "Llegiu la documentació en línia per a obtenir exemples." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Evita que el sistema entri en repòs durant la reproducció." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Habilita el servidor natiu del D-Bus (necessari per a la interfície de línia " "d’ordres)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Habilita el servidor MPRIS2 del D-Bus." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Prefereix una variant diferent del tema (si n’hi ha)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Mostra una notificació quan canviï l’estat." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Mostra informació a la sortida estàndard." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" "Vincula les tecles multimèdia (reprodueix/posa en pausa/atura/anterior/" "següent)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Acció activada en fer un clic mig a la icona d’estat." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Acció activada en desplaçar-se amb el ratolí sobre la icona d’estat." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "" "El paràmetre només està disponible si utilitzeu el mode d’icona d’estat." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Suprimeix l’emissora" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Edita l’emissora" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "S’ha suprimit l’excepció de seguretat" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Desa" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kb/s" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "desconeguda" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominal" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "mínima" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "màxima" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Monofònic" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Estereofònic" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "silenciat" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "vol." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Cap emissora" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Cap metadada" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "No s’han pogut vincular les tecles següents" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "No s’ha pogut impedir el repòs del sistema" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "S’està reproduint %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "S’està reproduint <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Títol desconegut)" #~ msgid "Autoset Window Height" #~ msgstr "Defineix automàticament la mida de la finestra" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Ajusta automàticament l’alçada de la finestra en afegir o suprimir una " #~ "emissora." #~ msgid "stopped" #~ msgstr "aturat" #~ msgid "connecting" #~ msgstr "s’està connectant" #~ msgid "buffering" #~ msgstr "s’està omplint la memòria intermèdia" #~ msgid "playing" #~ msgstr "s’està reproduint" #~ msgid "unknown state" #~ msgstr "estat desconegut" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/po/cs.po000066400000000000000000000310561414415210700152560ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-02-08 14:50+0000\n" "Last-Translator: Andrej Shadura \n" "Language-Team: Czech \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Weblate 3.11-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "PÅ™ehrát webové rádio" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes je jednoduchý pÅ™ehrávaÄ internetových rádií pro GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 #, fuzzy msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Poskytuje vÅ¡echny základní funkce, které můžete od audio pÅ™ehrávaÄe " "oÄekávat, jako jsou multimediální klávesy, oznámení, inhibice spánku systému " "a podpora MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "PÅ™ehrávaÄ rádia" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;PÅ™ehrávaÄ;Zvuk;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "PÅ™ehrát/zastavit" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "PÅ™idat stanici" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Nastavení" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Klávesnice" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Online nápovÄ›da" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "O programu" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Zavřít" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "UkonÄit" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Nebyla zvolena žádná stanice" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Zastaveno" #: src/ui/resources/prefs-window.glade:55 #, fuzzy msgid "Close Button" msgstr "Zavřít rozhraní" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Automaticky pÅ™ehrávat pÅ™i spuÅ¡tÄ›ní" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Potvrdit" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "PÅ™ehrávání" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Zabránit uspání pÅ™i pÅ™ehrávání" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Nastavení systému" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Nativní D-Bus Server" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "D-Bus Server MPRIS2" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "VÅ¡eobecné" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "varianta motivu" #: src/ui/resources/prefs-window.glade:337 #, fuzzy msgid "System Default" msgstr "Nastavení systému" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "UpÅ™ednostnit tmavý motiv" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "UpÅ™ednostnit svÄ›tlý motiv" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Okno" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Poslat oznámení" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "oznámení" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Konzolový výstup" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "konzole" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Displej" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Multimediální klávesy" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Klávesnice" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "StÅ™ední klik" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "ZmÄ›nit stanici" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "ZmÄ›nit hlasitost" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "" #: src/ui/resources/station-view.glade:229 #, fuzzy msgid "Station" msgstr "PÅ™idat stanici" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "" #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "" #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "" #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "" #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "" #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "" #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "" #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "" #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "" #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "" #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "" #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "" #~ msgid "Autoset Window Height" #~ msgstr "Automatická výška okna" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" #~ msgid "Misc" #~ msgstr "Různé" #~ msgid "Menu" #~ msgstr "Menu" #~ msgid "Help" #~ msgstr "NápovÄ›da" #~ msgid "goodvibes" #~ msgstr "goodvibes" #~ msgid "Goodvibes Radio Player" #~ msgstr "PÅ™ehrávaÄ rádia Goodvibes" #~ msgid "File" #~ msgstr "Soubor" #~ msgid "Player" #~ msgstr "PÅ™ehrávaÄ" goodvibes-v0.7.2/po/da.po000066400000000000000000000326271414415210700152420ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-11-07 16:51+0000\n" "Last-Translator: Torben Grove \n" "Language-Team: Danish \n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.9-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Afspil internet radio" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes er en simpel internet radioafspiller til GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Den har basale funktioner som forventes af en medieafspiller, bl.a. " "multimedietaster, notifikationer, afbrydelse af dvaletilstand og MPRIS2 " "support." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Radio Afspiller" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Lyd;Radio;Afspiller;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Afspil/Stop" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Tilføj Station" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Præferencer" #: src/ui/resources/app-menu.ui:22 msgid "Keyboard Shortcuts" msgstr "Genvejstaster" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Online hjælp" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "Om" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Luk" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Afslut" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Ingen station valgt" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Stoppet" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Luk Knap" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Program" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Autospil ved start" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Custom Output Pipeline" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Anvend" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Afspilning" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "UndgÃ¥ hviletilstand nÃ¥r der afspilles" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "System" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Native D-Bus Server" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "MPRIS2 D-Bus Server" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Generelt" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Tema" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Systemstandard" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Foretræk Mørk" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Foretræk Lys" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Vindue" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Send notifikationer" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Notifikationer" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Konsol output" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Konsol" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Udseende" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Multimedietaster" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Tastatur" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Mellemklik" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "SlÃ¥ lyden fra" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Rulning" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Skift station" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Skift lydstyrke" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Mus (statusikontilstand)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Styring" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Navn" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Sikkerhedsundtagelse" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "Fjern" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadata" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Titel" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Kunstner" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Album" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Genre" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Ã…r" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Kommentar" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Station" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Streams" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "User-Agent" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Bitrate" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Sample rate" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Kanaler" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Codec" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "Stream type" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Kunne ikke parse pipelinebeskrivelsen" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Afspiller" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Forbinder…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Buffer…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' er ikke en kendt station eller en gyldig URI" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Det lykkedes ikke at gemme stationslisten" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "Vil du tilføje en sikkerhedsundtagelse?" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "Der skete en fejl under afspilningen af %s." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Annuller" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Tilføj" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "URL" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Fejl" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "Detaljer" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Funktion deaktiveret ved kompileringstidspunktet." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Handling nÃ¥r der trykkes pÃ¥ Lukknappen." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Indstillingen er ikke tilgængelig i statusikontilstand." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Om afspilning skal startes automatisk ved opstart." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Om der skal bruges en custom output pipeline." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "GStreamer output pipeline, der bruges til afspilning. Se " "onlinedokumentationen for eksempler." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "UndgÃ¥, at systemet gÃ¥r i dvale, mens du spiller." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "Aktiver native D-Bus-server (nødvendig til kommandolinjegrænsefladen)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Aktiver MPRIS2 D-Bus-serveren." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Foretræk en anden variant af temaet (hvis tilgængeligt)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Vis notifikation nÃ¥r statussen ændres." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Vis information om standard output." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "Bind multimedietaster (afspil/pause/stop/forrige/næste)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Handling udløst af et midterklik pÃ¥ statusikonet." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Handling udløst af muserulning pÃ¥ statusikonet." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Indstillingen er kun tilgængelig i statusikontilstand." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Fjern station" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Rediger station" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "Sikkerhedsundtagelsen er fjernet" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Gem" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbps" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "ukendt" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominel" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "min" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "maks" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Steroe" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "dæmpet" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "vol." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Ingen station" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Ingen metadata" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Kunne ikke binde følgende taster" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Det lykkedes ikke at stoppe systemdvale" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "Afspiller %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "Afspiller <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Ukendt titel)" goodvibes-v0.7.2/po/de.po000066400000000000000000000404471414415210700152450ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-06-25 15:41+0000\n" "Last-Translator: Milo Ivir \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Webradios abspielen" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" "Goodvibes ist ein einfaches Internetradio-Abspielprogramm für GNU-Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Es enthält alle Funktionen, die man von einem Internetradio-Player erwartet, " "wie: Multimediatastenunterstützung, Benachrichtigungen, " "Systemruhestandsunterdrückung und MPRI2-Unterstützung." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Radio-Abspielprogramm" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Player;Webradio;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Abspielen/Stoppen" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Radiosender hinzufügen" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Einstellungen" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Tastatur" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Online-Hilfe" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "Über" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Schließen" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Beenden" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Keinen Radiosender ausgewählt" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Angehalten" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Schließen-Schaltfläche" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Anwendung" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Automatisch beim Start abspielen" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Benutzerdefinierte Ausgangspipeline" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Übernehmen" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Wiedergabe" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Ruhemodus währen der Wiedergabe verhindern" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "System" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Nativer D-Bus-Server" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "MPRIS2 D-Bus-Server" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Allgemein" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Thema Variante" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Systemstandard" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Lieber Dunkel" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Lieber Licht" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Fenster" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Benachrichtigungen senden" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Benachrichtigungen" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Konsolenausgabe" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Konsole" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Anzeige" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Multimedia-Tasten" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Tastatur" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Mittelklick" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Stummschaltung" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Blättern" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Radiosender wechseln" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Lautstärke ändern" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Maus (Statussymbol-Modus)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Bedienelemente" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Name" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "Adresse" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 #, fuzzy msgid "Remove" msgstr "Radiosender entfernen" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadaten" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Titel" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Künstler" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Album" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Genre" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Jahr" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Kommentar" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Radiosender" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Streams" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Benutzer-Agent" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Bitrate" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Abtastrate" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Kanäle" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Codec" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Streams" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s:%s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Pipeline-Beschreibung konnte nicht analysiert werden" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Wiedergabe" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Verbindung wird hergestellt…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Wird zwischengespeichert…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "„%s“ ist weder eine bekannte Station noch eine gültige Internetadresse" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Senderliste konnte nicht gespeichert werden" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Abbrechen" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 #, fuzzy msgid "URL" msgstr "Adresse" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Fehler" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Funktion zur Kompilierungszeit deaktiviert." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Aktion, wenn die Schließen-Schaltfläche geklickt wird." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Einstellung im Statussymbolmodus nicht verfügbar." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "" "Gibt an, ob die Wiedergabe beim Start automatisch gestartet werden soll." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "" "Gibt an, ob eine benutzerdefinierte Ausgabepipeline verwendet werden soll." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "Die für die Wiedergabe verwendete GStreamer-Ausgabepipeline. Beispiele " "finden Sie in der Online-Dokumentation." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Verhindern, dass das System bei der Wiedergabe in den Ruhemodus geht." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Aktivieren Sie den nativen D-Bus-Server (für die Befehlszeilenschnittstelle " "erforderlich)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Den MPRIS2 D-Bus Server aktivieren" #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Bevorzugen Sie eine andere Variante des Themas (falls verfügbar)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Benachrichtigung anzeigen, wenn sich der Status ändert." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Informationen zur Standardausgabe anzeigen." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" "Multimedia-Tasten verbinden (Wiedergabe/Pause/Stopp/Vorherige/Nächste)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" "Aktion, die durch einen mittleren Klick auf das Statussymbol ausgelöst wird." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" "Aktion, die durch Blättern mit der Maus auf dem Statussymbol ausgelöst wird." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Einstellung nur im Statussymbolmodus verfügbar." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Radiosender entfernen" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Radiosender bearbeiten" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Speichern" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbit/s" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "unbekannt" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominell" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "min" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "max" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Stereo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "Stummgeschaltet" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "Laut." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Kein Radiosender" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Keine Metadaten" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Folgende Tasten konnten nicht gebunden werden" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Fehler beim Verhindern des Systemschlafs" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "Spiele %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "Spiele <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Unbekannter Titel)" #~ msgid "Autoset Window Height" #~ msgstr "Fensterhöhe automatisch festlegen" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Fensterhöhe automatisch anpassen, wenn eine Station hinzugefügt oder " #~ "entfernt wird." #~ msgid "stopped" #~ msgstr "gestoppt" #~ msgid "connecting" #~ msgstr "Verbindung wird hergestellt" #~ msgid "buffering" #~ msgstr "Wird zwischengespeichert" #~ msgid "playing" #~ msgstr "Wird wiedergegeben" #~ msgid "unknown state" #~ msgstr "Unbekannter Status" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" #~ msgid "Station Information" #~ msgstr "Senderinformationen" #~ msgid "Playing Station" #~ msgstr "Radiosender wiedergeben" #~ msgid "New Track" #~ msgstr "Neuer Titel" #~ msgid "Misc" #~ msgstr "Sonstiges" #~ msgid "Menu" #~ msgstr "Menü" #~ msgid "Help" #~ msgstr "Hilfe" #~ msgid "Toggle Play/Pause" #~ msgstr "Wiedergabe/Pause umschalten" #~ msgid "goodvibes" #~ msgstr "goodvibes" #~ msgid "Goodvibes Radio Player" #~ msgstr "Goodvibes Radio-Player" #~ msgid "Window Behavior" #~ msgstr "Fensterverhalten" #~ msgid "" #~ "The GStreamer output pipeline used for playback. For example, use " #~ "'alsasink device=hw:2' to force audio output to ALSA, 2nd soundcard.\n" #~ "For more details, please refer to the gst-launch manual page." #~ msgstr "" #~ "Die GStreamer-Ausgabepipeline, die für die Wiedergabe verwendet wird. " #~ "Verwenden Sie z.B. „alsasink device=hw:2â€, um die Audioausgabe auf ALSA, " #~ "2. Soundkarte, zu erzwingen.\n" #~ "Weitere Einzelheiten entnehmen Sie bitte der gst-launch-Handbuchseite." #~ msgid "Emit Notifications" #~ msgstr "Benachrichtigungen ausgeben" #~ msgid "End of stream" #~ msgstr "Ende des Streams" #~ msgid "Connecting..." #~ msgstr "Verbinden …" #~ msgid "Buffering..." #~ msgstr "Zwischenspeicherung …" #~ msgid "Seeting available only in status icon mode." #~ msgstr "Einstellung nur im Statussymbolmodus verfügbar." #~ msgid "Add new station" #~ msgstr "Neuen Radiosender hinzufügen" #~ msgid "Edit station" #~ msgstr "Radiosender bearbeiten" #~ msgid "Add New Station" #~ msgstr "Neuen Radiosender hinzufügen" #~ msgid "File" #~ msgstr "Datei" #~ msgid "Player" #~ msgstr "Player" #~ msgid "Set Timeout" #~ msgstr "Zeitüberschreitung einstellen" #~ msgid "How long the notifications should be displayed (seconds)." #~ msgstr "" #~ "Wie lange die Benachrichtigungen angezeigt werden sollen (Sekunden)." goodvibes-v0.7.2/po/en_US.po000066400000000000000000000271351414415210700156650ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-09-29 19:10+0000\n" "Last-Translator: Robert Cook \n" "Language-Team: English (United States) \n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.3-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Play Internet radio" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes is a simple internet radio player for GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Radio Player" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Player;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Add Station" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Preferences" #: src/ui/resources/app-menu.ui:22 msgid "Keyboard Shortcuts" msgstr "" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Online Help" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "About" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 #, fuzzy msgid "Close" msgstr "Close" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Quit" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "No station selected" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 #, fuzzy msgid "Stopped" msgstr "Stopped" #: src/ui/resources/prefs-window.glade:55 #, fuzzy msgid "Close Button" msgstr "Close user interface" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "" #: src/ui/resources/station-view.glade:229 #, fuzzy msgid "Station" msgstr "Add Station" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "" #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "" #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "" #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "" #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "" #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "" #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "" #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "" #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "" #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "" #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "" #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "" goodvibes-v0.7.2/po/es.po000066400000000000000000000357331414415210700152660ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-06-10 10:33+0000\n" "Last-Translator: Adolfo Jayme Barrientos \n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.7-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Escuchar radios online" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" "Goodvibes es un simple reproductor de radios de internet para GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Viene con todas las funciones básicas que puede esperar de un reproductor de " "audio, tales como teclas multimedia, notificaciones, supresión de la " "suspensión del sistema y compatibilidad con MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Reproductor de radio" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Reproductor;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Reproducir/detener" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Añadir emisora" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Preferencias" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Teclado" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Ayuda Online" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "Acerca de" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Cerrar" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Salir" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "No se seleccionó ninguna emisora" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Detenido" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Botón Cerrar" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Aplicación" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Reproducir automáticamente al iniciar" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Canalización de salida personalizada" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Aplicar" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Reproducción" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Evitar suspensión mientras se reproduce" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Sistema" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Servidor D-Bus nativo" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "Servidor MPRIS2 D-Bus" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Generales" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Variante de tema" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Predeterminada del sistema" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Preferir oscura" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Preferir clara" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Ventana" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Enviar notificaciones" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Notificaciones" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Salida de consola" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Consola" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Interfaz" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Teclas multimedia" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Teclado" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Pulsación central" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Silenciar" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Desplazamiento" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Cambiar de emisora" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Cambiar volumen" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Ratón (modo icono de estado)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Controles" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Nombre" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Excepción de seguridad" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "Quitar" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadatos" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Título" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artista" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Ãlbum" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Género" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Año" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Comentario" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Emisora" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Transmisiones" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Agente de usuario" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Tasa de bits" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Tasa de muestreo" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Canales" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Códec" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Transmisiones" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "No se pudo analizar la descripción de la canalización de salida" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Reproduciendo" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Conectando…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Almacenando en búfer…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "«%s» no es ni una emisora conocida ni un URI válido" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "No se pudo guardar la lista de emisoras" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "¿Quiere añadir una excepción de seguridad?" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "Se produjo un error al intentar reproducir %s." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Cancelar" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Añadir" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "URL" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Error" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Función desactivada durante la compilación." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Acción al pulsar en el botón Cerrar." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Configuración no disponible en el modo de icono de estado." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Indica si se iniciará la reproducción automáticamente al iniciar." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Indica si se utilizará una canalización de salida personalizada." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "La canalización de salida de GStreamer utilizada para la reproducción. " "Consulte la documentación en línea para obtener ejemplos." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Evitar que el sistema se suspenda durante la reproducción." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Activar el servidor D-Bus nativo (necesario para la interfaz de línea de " "órdenes)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Activar el servidor MPRIS2 de D-Bus." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Preferir una variante diferente del tema (si se encuentra disponible)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Mostrar una notificación cuando cambie el estado." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Mostrar información en la salida estándar." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" "Asignar teclas multimedia (reproducir/pausa/detener/anterior/siguiente)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" "Acción provocada por una pulsación con el botón central del ratón en el " "icono de estado." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" "Acción provocada por el movimiento de la rueda del ratón en el icono de " "estado." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Configuración solo disponible en el modo de icono de estado." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Quitar emisora" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Editar emisora" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "Se quitó la excepción de seguridad" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Guardar" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kb/s" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "desconocido" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominal" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "mín." #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "máx." #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Monoaural" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Estéreo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "silenciado" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "vol." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "No hay emisora" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "No hay metadatos" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "No se pudieron asignar las teclas siguientes" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "No se pudo desactivar la suspensión del sistema" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "Reproduciendo %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "Reproduciendo <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Título desconocido)" #~ msgid "Autoset Window Height" #~ msgstr "Establecer automáticamente altura de ventana" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Ajustar automáticamente el tamaño de la ventana cuando se añade o quita " #~ "una emisora." #~ msgid "stopped" #~ msgstr "detenido" #~ msgid "connecting" #~ msgstr "conectando" #~ msgid "buffering" #~ msgstr "almacenando en búfer" #~ msgid "playing" #~ msgstr "reproduciendo" #~ msgid "unknown state" #~ msgstr "estado desconocido" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" #~ msgid "Station Information" #~ msgstr "Información de la Estación" #~ msgid "Playing Station" #~ msgstr "Reproduciendo Estación" #~ msgid "New Track" #~ msgstr "Nueva Pista" #~ msgid "Misc" #~ msgstr "Varios" #~ msgid "Menu" #~ msgstr "Menú" #~ msgid "Help" #~ msgstr "Ayuda" #~ msgid "Toggle Play/Pause" #~ msgstr "Alternar Reproducir/Pausar" goodvibes-v0.7.2/po/fr.po000066400000000000000000000403641414415210700152620ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-10-03 10:41+0000\n" "Last-Translator: Christophe Nemo \n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.3-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Écouter des radios web" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes est un simple lecteur radio Internet pour GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Il est livré avec toutes les fonctions de base que vous pouvez attendre d'un " "lecteur audio, telles que les touches multimédia, les notifications, le " "blocage de la mise en veille du système et le support MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Lecteur radio" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Player;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Lire/Arrêter" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Ajouter une station" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Préférences" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Clavier" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Aide en ligne" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "À propos" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Fermer" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Quitter" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Aucune station sélectionnée" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Arrêté" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Bouton de fermeture" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Application" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Lancer l'écoute au démarrage" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Chaîne de traitement de sortie personnalisée" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Appliquer" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Lecture" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Empêcher la mise en veille lors de l'écoute" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Système" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Serveur D-Bus natif" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "Serveur D-Bus MPRIS2" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Général" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Variante de thème" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Valeur par défaut du système" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Plutôt sombre" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Plutôt clair" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Fenêtre" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Afficher des notifications" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Notifications" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Sortie de la console" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Console" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Affichage" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Touches multimédia" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Clavier" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Clic milieu" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Sourdine" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Défilement" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Changer de station" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Changer le volume" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Souris (en mode icône de statut)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Contrôles" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Nom" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "Adresse (URI)" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 #, fuzzy msgid "Remove" msgstr "Supprimer la station" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Métadonnée" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Titre" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artiste" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Album" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Genre" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Année" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Commentaire" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Station" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Flux" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Agent utilisateur" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Débit" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Taux d'échantillonnage" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Chaînes" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Codec" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Flux" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s : %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Impossible d'obtenir la description de la chaîne de traitements" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Lecture" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Connexion…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Mise en mémoire tampon…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "« %s » n'est ni une station connue, ni une URI valide" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Échec de l'enregistrement de la liste des stations" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Annuler" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 #, fuzzy msgid "URL" msgstr "Adresse (URI)" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Erreur" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Fonctionnalité désactivée lors de la compilation." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Action quand on clique sur le bouton de fermeture." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Paramètre non disponible en mode icône de statut." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Automatiquement reprendre la lecture au démarrage." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "" "Nécessité d'utiliser une chaîne de traitements de sortie personnalisée." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "La chaîne de traitement GStreamer utilisée pour la lecture. Vous pouvez vous " "référer à la documentation en ligne pour avoir des exemples." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Empêcher le système de se mettre en veille lors de la lecture." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Activer le serveur natif D-Bus (requis par l'interface en ligne de commande)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Activer le serveur D-BUS MPRIS2." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Choisir une variante du thème actuel (si disponible)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Afficher une notification lorsque le statut change." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Afficher les informations sur la sortie standard." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "Associer les touches multimédia (play/pause/stop/previous/next)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Action effectuée lors d'un clic milieu sur l'icône de statut." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" "Action effectuée après utilisation de la molette sur l'icône de statut." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Paramètre disponible seulement en mode icône de statut." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Supprimer la station" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Éditer la station" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Enregistrer" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbit/s" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "inconnu" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominal" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "minimal" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "maximal" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Stéréo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "coupé" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "vol." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Aucune station" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Aucune métadonnée" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s :\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Échec lors de l'association des touches suivantes" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Impossible d'empêcher la mise en veille du système" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "Lecture de %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "Lecture de <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Titre inconnu)" #~ msgid "Autoset Window Height" #~ msgstr "Fixer automatiquement la hauteur de la fenêtre" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Ajuster automatiquement la hauteur de la fenêtre lorsqu'une station est " #~ "ajoutée ou supprimée." #~ msgid "stopped" #~ msgstr "arrêté" #~ msgid "connecting" #~ msgstr "connexion" #~ msgid "buffering" #~ msgstr "mise en mémoire tampon" #~ msgid "playing" #~ msgstr "lecture" #~ msgid "unknown state" #~ msgstr "état inconnu" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" #~ msgid "Station Information" #~ msgstr "Information sur la station" #~ msgid "Playing Station" #~ msgstr "Lecture de la station" #~ msgid "New Track" #~ msgstr "Nouvelle piste" #~ msgid "Misc" #~ msgstr "Divers" #~ msgid "Menu" #~ msgstr "Menu" #~ msgid "Help" #~ msgstr "Aide" #~ msgid "Toggle Play/Pause" #~ msgstr "Lecture/Pause" #~ msgid "goodvibes" #~ msgstr "goodvibes" #~ msgid "Goodvibes Radio Player" #~ msgstr "Lecteur de radio Goodvibes" #~ msgid "Window Behavior" #~ msgstr "Comportement de la fenêtre" #~ msgid "" #~ "The GStreamer output pipeline used for playback. For example, use " #~ "'alsasink device=hw:2' to force audio output to ALSA, 2nd soundcard.\n" #~ "For more details, please refer to the gst-launch manual page." #~ msgstr "" #~ "Chaîne de traitements de sortie utilisée par GStreamer. Par exemple, " #~ "entrer 'alsasink device=hw:2' pour forcer l'utilisation de la 2nd carte " #~ "son détectée par ALSA.\n" #~ "Pour plus de détails, se référer à la page de manuel de gst-launch." #~ msgid "Emit Notifications" #~ msgstr "Émettre des notifications" #~ msgid "End of stream" #~ msgstr "Flux terminé" #~ msgid "Connecting..." #~ msgstr "Connexion…" #~ msgid "Buffering..." #~ msgstr "Mise en mémoire tampon…" #~ msgid "Seeting available only in status icon mode." #~ msgstr "Paramètre disponible uniquement en mode icône de statut." #~ msgid "Add new station" #~ msgstr "Ajouter une nouvelle station" #~ msgid "Edit station" #~ msgstr "Éditer la station" #~ msgid "Add New Station" #~ msgstr "Ajouter une nouvelle station" #~ msgid "File" #~ msgstr "Fichier" #~ msgid "Player" #~ msgstr "Lecteur" #~ msgid "Set Timeout" #~ msgstr "Durée d'affichage" #~ msgid "How long the notifications should be displayed (seconds)." #~ msgstr "Durée d'affichage des notifications (en secondes)." goodvibes-v0.7.2/po/goodvibes.pot000066400000000000000000000260131414415210700170130ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "" #: src/ui/resources/app-menu.ui:22 msgid "Keyboard Shortcuts" msgstr "" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "" #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "" #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "" #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "" #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "" #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "" #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "" #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "" #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "" #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "" #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "" #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "" goodvibes-v0.7.2/po/hr.po000066400000000000000000000344171414415210700152660ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-09-22 21:38+0000\n" "Last-Translator: Milo Ivir \n" "Language-Team: Croatian \n" "Language: hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.9-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Dobre vibre" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Sviraj internetske radio stanice" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" "Dobre vibre (Goodvibes) je jednostavan program za sviranje internetskih " "radio stanica za GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Sadrži sve osnovne funkcije audio ureÄ‘aja, kao Å¡to su multimedijske tipke, " "obavijesti, spreÄavanje sustavskog stanja mirovanja i podrÅ¡ka za MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Radio-player" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Player;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Sviraj/Prekini" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Dodaj stanicu" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Postavke" #: src/ui/resources/app-menu.ui:22 msgid "Keyboard Shortcuts" msgstr "Tipkovni preÄaci" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Pomoć na internetu" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "Informacije" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Zatvori" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "IzaÄ‘i" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Nijedna stanica nije odabrana" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Prekinuto" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Gumb zatvaranja" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Program" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Automatski sviraj nakon pokretanja sustava" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "PrilagoÄ‘eni proces rezultata" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Primijeni" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Sviranje" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "SprijeÄi mirovanje tijekom sviranja" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Sustav" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Izvorni D-Bus poslužitelj" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "MPRIS2 D-Bus poslužitelj" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Opće" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Varijanta teme" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Standard sustava" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Preferiraj tamnu" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Preferiraj svijetlu" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Prozor" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Å alji obavijesti" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Obavijesti" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Rezultat konzole" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Konzola" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Prikaz" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Multimedijske tipke" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Tipkovnica" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Srednji gumb miÅ¡a" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "IskljuÄi zvuk" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Pomicanje" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Promijeni stanicu" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Promijeni glasnoću" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "MiÅ¡ (modus ikona stanja)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Upravljanje" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Ime" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Sigurnosna iznimka" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "Ukloni" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metapodaci" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Naslov" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "IzvoÄ‘aÄ" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Album" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Žanr" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Godina" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Komentar" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Stanica" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Emitiranja" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "KorisniÄki agent" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Brzina prijenosa" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Frekvencija" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Kanali" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Kodek" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "Vrsta emitiranja" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Neuspjela obrada opisa procesa" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Svira" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Povezivanje …" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "MeÄ‘uspremanje …" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "„%s†nije poznata stanica niti israpvni URI" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Neuspjelo spremanje popisa stanica" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "Dodati sigurnosnu iznimku?" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "Dogodila se greÅ¡ka prilikom sviranja %s." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Odustani" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Dodaj" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "URL" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "GreÅ¡ka" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "Detalji" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Funkcija deaktivirana tijekom vremena kompiliranja." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Radnja kad se pritisne gumb zatvaranja." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Postavka nije dostupna u modusu ikona stanja." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Da li automatski svirati nakon pokretanja sustava." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Da li koristiti prilagoÄ‘eni proces rezultata." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "GStreamer proces rezultata koji se koristi za sviranje. Primjere potraži u " "internetskoj dokumentaciji." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "SprijeÄi sustavsko stanje mirovanja tijekom sviranja." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Aktiviraj izvornog D-Bus poslužitelja (potrebno za suÄelje naredbenog retka)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Aktiviraj MPRIS2 D-Bus poslužitelja." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Preferiraj drugu varijantu teme (ako je dostupna)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Prikaži obavijest kad se stanje promijeni." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Prikaži podatke standardnog rezultata." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" "Poveži multimedijske tipke (pokreni/zaustavi/prekini/prethodna/sljedeća)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Pokrenuta radnja pri pritiskanju ikone stanja srednjom tipkom miÅ¡a." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Pokrenuta radnja pri okretanju kotaÄa miÅ¡a na ikoni stanja." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Postavka je dostupna samo u modusu ikona stanja." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Ukloni stanicu" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Uredi stanicu" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "Sigurnosna iznimka je uklonjena" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Spremi" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbps" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "nepoznato" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominalo" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "min" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "maks" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Stereo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "iskljuÄen zvuk" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "glasnoća" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Nema stanice" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Nema metapodataka" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Neuspjelo povezivanje sljedećih tipki" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Neuspjelo spreÄavanje sustavskg stanja mirovanja" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "Svira %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "Svira <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(nepoznat naslov)" #~ msgid "Autoset Window Height" #~ msgstr "Automatski postavi visinu prozora" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "Automatski podesi visinu prozora kad se stanica doda ili ukloni." #~ msgid "stopped" #~ msgstr "prekinuto" #~ msgid "connecting" #~ msgstr "povezivanje" #~ msgid "buffering" #~ msgstr "meÄ‘uspremanje" #~ msgid "playing" #~ msgstr "svira" #~ msgid "unknown state" #~ msgstr "nepoznato stanje" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/po/hu.po000066400000000000000000000260621414415210700152660ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "" #: src/ui/resources/app-menu.ui:22 msgid "Keyboard Shortcuts" msgstr "" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "" #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "" #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "" #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "" #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "" #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "" #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "" #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "" #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "" #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "" #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "" #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/po/id.po000066400000000000000000000327071414415210700152510ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-03-20 11:18+0000\n" "Last-Translator: Reza Almanda \n" "Language-Team: Indonesian \n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.5.2-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Memainkan radio web" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaut Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes merupakan pemutar radio sederhana untuk GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Goodvibes hadir dengan beragam fitur dasar pemutar audio, seperti tombol " "multimedia, notifikasi, sistem penghambat tidur, dan dukungan MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Pemutar Radio" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio:Pemutar;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Mainkan/Hentikan" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Tambah Stasiun" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Preferensi" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Papan tik" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Tutup" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Keluar" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Tidak ada stasiun yang dipilih" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Berhenti" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Tombol Tutup" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Aplikasi" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Putar otomatis pada startup" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Pipeline Keluaran Kustom" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Terapkan" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Pemutaran" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Cegah tidur saat memutar" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Sistem" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Server D-Bus Asli" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "Peladen MPRIS2 D-Bus" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Umum" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Varian Tema" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Bawaan Sistem" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Gelap" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Terang" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Jendela" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Kirim Notifikasi" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Notifikasi" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Keluaran Konsol" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Konsol" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Tampilan" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Tombol Pintas Multimedia" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Papan tik" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Klik Tengah" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Senyap" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Menggulir" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Ubah Stasiun" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Ubah Volume" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Mouse (Mode Ikon Status)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Kontrol" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Nama" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Pengecualian Keamanan" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "Hapus" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadata" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Judul" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artis" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Album" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Genre" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Tahun" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Komentar" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Stasiun" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Stream" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Agen-Pengguna" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Bitrate" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Laju sampel" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Saluran" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Kodek" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Stream" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Gagal mengurai deskripsi pipeline" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Sedang memainkan" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Menghubungkan…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Buffering…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' bukan stasiun yang dikenal atau URI yang valid" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Gagal menyimpan daftar stasiun" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "Tambahkan pengecualian keamanan?" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "Timbul galat saat mencoba memainkan %s." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Batalkan" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Tambah" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "URL" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Galat" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Fitur dinonaktifkan pada compile-time." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Tindakan saat tombol tutup diklik." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Setelan tak tersedia dalam mode ikon status." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Memulai pemutaran secara otomatis pada saat mulai." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Apakah akan menggunakan pipeline keluaran khusus." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "" #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Aktifkan server MPRIS2 D-Bus." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Pilih varian tema yang berbeda (jika tersedia)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Tampilkan notifikasi ketika status berubah." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Tampilkan informasi pada keluaran standar." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "Ikat tombol mutimedia (putar/jeda/hentikan/sebelumnya/berikutnya)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Tindakan yang dipicu ketika mengklik tengah pada ikon status." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Tindakan yang dipicu oleh mouse-scrolling pada ikon status." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Pengaturan hanya tersedia dalam mode ikon status." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Hapus Staisun" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Sunting Stasiun" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Simpan" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbps" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "tidak diketahui" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominal" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "min" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "maks" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Stereo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Judul tidak diketahui)" #~ msgid "Autoset Window Height" #~ msgstr "Setel Otomatis Tinggi Jendela" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Sesuaikan tinggi jendela secara otomatis saat stasiun ditambahkan atau " #~ "dihapus." #~ msgid "stopped" #~ msgstr "berhenti" #~ msgid "connecting" #~ msgstr "Menghubungkan" #~ msgid "buffering" #~ msgstr "buffering" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/po/it.po000066400000000000000000000341721414415210700152670ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-12-18 02:29+0000\n" "Last-Translator: Luca De Filippo \n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.4-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Ascolta web radio" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes è un semplice lettore radio internet per GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Viene fornito con tutte le caratteristiche di base che ci si può aspettare " "da un lettore audio, come le chiavi multimediali, le notifiche, l'inibizione " "dello standby di sistema e il supporto MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Lettore radio" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Player;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Play/Stop" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Aggiungi Stazione" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Preferenze" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Tastiera" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Chiudi" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Esci" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Nessuna stazione selezionata" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Fermato" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Chiudi tasti" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Applicazione" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Avvia allo Startup" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Combinazione personalizzata di uscite" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Applica" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Suona" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Previeni lo standby quando in riproduzione" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Sistema" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "D-Bus Server Nativo" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "D-Bus Server MPRIS2" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Generale" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Variante Tema" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Sistema di base" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Modalità Nero" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Modalità Chiaro" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Finestra" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Invia Notifiche" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Notifiche" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Uscita Console" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Console" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Visualizza" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Scorciatoie tastiera" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Tastiera" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Click centrale" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Muto" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Scorri" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Cambia stazione" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Volume" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Mouse (Modalità Icone di Stato)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Controlli" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Nome" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 #, fuzzy msgid "Remove" msgstr "Rimuovi Stazione" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadati" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Titolo" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artista" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Album" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Genere" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Anno" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Commento" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Stazioni" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Streaming" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "User-Agent" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Velocità Bit" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Velocità di campionatura" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Canali" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Codec" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Streaming" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Descrizione del tracciato impossibile da analizzare" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "In riproduzione" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Connessione…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Ricarico…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' non è né una stazione conosciuta né un URI valido" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Impossibile salvare l'elenco delle stazioni" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Annulla" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 #, fuzzy msgid "URL" msgstr "URI" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Errore" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Funzione disabilitata al momento della compilazione." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Esegui quando clicchi il tasto \"Chiudi\"." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Impostazione non valida nella modalità Icone di Stato." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Riproduci automaticamente all'avvio." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Continua ad usare le impostazioni di output personalizzate." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "Il tracciato GStreamer output è utilizzato per la riproduzione. Per esempi, " "consulta la documentazione online." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Impedisci al sistema di andare in Standby durante la riproduzione." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "Abilita il D-Bus Driver Nativo (Necessita del prompt di comando)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Abilità il server MPRIS2 D-Bus." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Scegli una variante tema differente (se disponibile)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Mostra notifiche durante il cambio di stato." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Mostra informazioni sugli standard output." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "Blocca tasti multimediali (Avvia/Pausa/Ferma/Precedente/Seguente)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Azione richiamata da un click al centro delle icone di stato." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Azione richiamata con lo scroll del mouse sulle icone di stato." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Impostazioni disponibili sono in modalità Icone di Stato." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Rimuovi Stazione" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Modifica Stazione" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Salva" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "KBPS" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "sconosciuto" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominale" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "min" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "max" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Stereo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "muto" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "vol." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Nessuna stazione" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Nessun metadata" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Impossibile bloccare i seguenti tasti" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Impossibile fermare lo standby di sistema" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "Riproduco %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "In riproduzione <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Titolo sconosciuto)" #~ msgid "Autoset Window Height" #~ msgstr "Imposta automaticamente l'altezza della finestra" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Regola automaticamente la finestra quando una stazione viene aggiunta o " #~ "rimossa." #~ msgid "stopped" #~ msgstr "fermato" #~ msgid "connecting" #~ msgstr "in connessione" #~ msgid "buffering" #~ msgstr "ricaricando" #~ msgid "playing" #~ msgstr "riproduzione" #~ msgid "unknown state" #~ msgstr "stato sconosciuto" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/po/meson.build000066400000000000000000000001341414415210700164440ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only i18n.gettext(meson.project_name(), preset: 'glib') goodvibes-v0.7.2/po/mr.po000066400000000000000000000340221414415210700152630ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-12-23 22:43+0000\n" "Last-Translator: Prachi Joshi \n" "Language-Team: Marathi \n" "Language: mr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.4.1-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "वेब रेडिओ वाजवा" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "अरनॉड रिबिलआऊट" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes जीà¤à¤¨à¤¯à¥‚/लिनकà¥à¤¸à¤¸à¤¾à¤ à¥€ à¤à¤• साधा इंटरनेट रेडिओ पà¥à¤²à¥‡à¤¯à¤° आहे." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "रेडिओ पà¥à¤²à¥‡à¤¯à¤°" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "ऑडिओ; रेडिओ; पà¥à¤²à¥‡à¤…र;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "पà¥à¤²à¥‡ / थांबा" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "कीबोरà¥à¤¡" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "बंद करा" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "बाहेर पडा" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "कोणतेही सà¥à¤Ÿà¥‡à¤¶à¤¨ निवडलेले नाही" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "थांबवले" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "बंद करा बटण" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "अनà¥à¤ªà¥à¤°à¤¯à¥‹à¤—" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "सà¥à¤Ÿà¤¾à¤°à¥à¤Ÿà¤…पवर ऑटोपà¥à¤²à¥‡" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "सानà¥à¤•ूल आउटपà¥à¤Ÿ पाइपलाइन" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "लागू करा" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "पà¥à¤²à¥‡à¤¬à¥…क" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "पà¥à¤°à¤£à¤¾à¤²à¥€" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "नेटिवà¥à¤¹ डी-बस सरà¥à¤µà¥à¤¹à¤°" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "MPRIS2 D-Bus सरà¥à¤µà¥à¤¹à¤°" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "सामानà¥à¤¯" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "थीम वà¥à¤¹à¥‡à¤°à¤¿à¤à¤‚ट" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "सिसà¥à¤Ÿà¤® डीफॉलà¥à¤Ÿ" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "गडद पà¥à¤°à¤¾à¤§à¤¾à¤¨à¥à¤¯" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "लाईटला पà¥à¤°à¤¾à¤§à¤¾à¤¨à¥à¤¯ दà¥à¤¯à¤¾" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "विंडो" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "सूचना पाठवा" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "सूचना" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "कनà¥à¤¸à¥‹à¤² आउटपà¥à¤Ÿ" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "कनà¥à¤¸à¥‹à¤²" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "पà¥à¤°à¤¦à¤°à¥à¤¶à¤¨" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "मलà¥à¤Ÿà¥€à¤®à¥€à¤¡à¤¿à¤¯à¤¾ हॉटकीज" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "कीबोरà¥à¤¡" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "मधà¥à¤¯à¤® कà¥à¤²à¤¿à¤•" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "मूक" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "सà¥à¤•à¥à¤°à¥‹à¤²à¤¿à¤‚ग" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "सà¥à¤Ÿà¥‡à¤¶à¤¨ बदला" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "वà¥à¤¹à¥‰à¤²à¥à¤¯à¥‚म बदला" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "माउस (सà¥à¤¥à¤¿à¤¤à¥€ चिनà¥à¤¹ मोड)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "नियंतà¥à¤°à¤£à¥‡" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "नाव" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "मेटाडेटा" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "शीरà¥à¤·à¤•" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "कलाकार" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "अलà¥à¤¬à¤®" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "शैली" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "वरà¥à¤·" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "टिपà¥à¤ªà¤£à¥€" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "सà¥à¤Ÿà¥‡à¤¶à¤¨" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "पà¥à¤°à¤µà¤¾à¤¹" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "वापरकरà¥à¤¤à¤¾ à¤à¤œà¤‚ट" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "बिटरेट" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "नमà¥à¤¨à¤¾ दर" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "चॅनलà¥à¤¸" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "कोडेक" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "पà¥à¤°à¤µà¤¾à¤¹" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "पाइपलाइन वरà¥à¤£à¤¨à¤¾à¤šà¥‡ विशà¥à¤²à¥‡à¤·à¤£ करणà¥à¤¯à¤¾à¤¤ अयशसà¥à¤µà¥€" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "कनेकà¥à¤Ÿ करीत आहे…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "बफरिंग…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' हे जà¥à¤žà¤¾à¤¤ सà¥à¤Ÿà¥‡à¤¶à¤¨ किंवा वैध URI नाही" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "सà¥à¤Ÿà¥‡à¤¶à¤¨ सूची जतन करणà¥à¤¯à¤¾à¤¤ अयशसà¥à¤µà¥€" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 #, fuzzy msgid "URL" msgstr "URI" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "संकलित वेळी वैशिषà¥à¤Ÿà¥à¤¯ अकà¥à¤·à¤® केले." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "कà¥à¤²à¥‹à¤œ बटणावर कà¥à¤²à¤¿à¤• केलà¥à¤¯à¤¾à¤µà¤° कृती करा." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "" #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "सà¥à¤Ÿà¤¾à¤°à¥à¤Ÿà¤…पवर आपोआप पà¥à¤²à¥‡à¤¬à¥…क सà¥à¤°à¥‚ करायचा की नाही." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "सानà¥à¤•ूल आउटपà¥à¤Ÿ पाइपलाइन वापरायची की नाही." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "" #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "नेटिवà¥à¤¹ डी-बस सरà¥à¤µà¥à¤¹à¤° सकà¥à¤·à¤® करा (कमांड-लाइन इंटरफेससाठी आवशà¥à¤¯à¤•)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "" #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "" #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "" #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "" #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "" #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "" #~ msgid "Autoset Window Height" #~ msgstr "विंडोची उंची ऑटोसेट करा" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/po/nb_NO.po000066400000000000000000000373421414415210700156500ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-02-23 02:50+0000\n" "Last-Translator: Allan Nordhøy \n" "Language-Team: Norwegian BokmÃ¥l \n" "Language: nb_NO\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Spill av nettradio" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 #, fuzzy msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes er en enkel internettradiospiller for GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Den kommer med grunnleggende enkle funksjoner du kan forvente av en " "lydspiller, som multimediataster, merknader, systemsøvnunngÃ¥else, og MPRIS2-" "støtte." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Radioavspiller" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Lyd;Radio;Avspiller;Nettradio;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Spill av/stopp" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Legg til stasjon" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Innstillinger" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Tastatur" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Nettbasert hjelp" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "Om" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Lukk" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Avslutt" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Ingen stasjon valgt" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Stoppet" #: src/ui/resources/prefs-window.glade:55 #, fuzzy msgid "Close Button" msgstr "Lukk grensesnitt" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Program" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Spill av automatisk ved oppstart" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Egendefinert utdatakanal" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Bruk" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Avspilling" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Forhindre dvalgang under avspilling" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "System" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Lokal D-Bus-tjener" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "MPRIS2 D-Bus-tjener" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Generelt" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Tema Variant" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Systemforvalg" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Foretrukket Mørk" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Foretrekker Lys" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Vindu" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Send merknader" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Merknader" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Konsollutdata" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Konsoll" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Vis" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Multimediataster" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Tastatur" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Midtklikk" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Demp" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Rulling" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Endre stasjon" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Endre lydstyrke" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Mus (statusikonmodus)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Kontroller" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Navn" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Sikkerhetsunntak" #: src/ui/resources/station-dialog.glade:74 #, fuzzy msgid "Remove" msgstr "Fjern stasjon" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadata" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Tittel" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artist" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Album" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Sjanger" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Ã…r" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Kommentar" #: src/ui/resources/station-view.glade:229 #, fuzzy msgid "Station" msgstr "Legg til stasjon" #: src/ui/resources/station-view.glade:255 #, fuzzy msgid "Streams" msgstr "Strømmer" #: src/ui/resources/station-view.glade:266 #, fuzzy msgid "User-Agent" msgstr "Brukeragent" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Bitrate" #: src/ui/resources/station-view.glade:321 #, fuzzy msgid "Sample Rate" msgstr "Samlingsrate" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Kanaler" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Kodek" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Strømmer" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Klarte ikke Ã¥ tolke kanalbeskrivelse" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Spiller" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Kobler til…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Mellomlagrer…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' er hverken en kjent stasjon eller en gyldig URI" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Klarte ikke Ã¥ lasgre stasjonsliste" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "Legg til sikkerhetsunntak?" #: src/ui/gv-main-window.c:99 #, fuzzy, c-format msgid "An error happened while trying to play %s." msgstr "Klarte ikke Ã¥ spille %s." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Avbryt" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Legg til" #: src/ui/gv-main-window.c:116 #, fuzzy msgid "URL" msgstr "URI" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Feil" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Funksjon avskrudd ved kompilering." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Handling nÃ¥r lukkingsknappen klikkes." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Innstillingen er ikke tilgjengelig i statusikonsmodus." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Hvorvidt avspilling skal startes automatisk ved oppstart." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Hvorvidt en egendefinert utdatakanal skal brukes." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "GStreamer utgangsrørledningen brukes til avspilling. Se referanse " "dokumentasjon for eksempler." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Forhindre systemet fra dvalgang under avspilling." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Skru pÃ¥ den lokale D-Bus-tjeneren (trengs for kommandolinjegresesnitt)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Skru pÃ¥ MPRIS2 D-Bus-tjeneren." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Foretrekker en annen variant av temaet (hvis tilgjengelig)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Vis merknad nÃ¥r statusen endres." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Vis informasjon i standardutdata." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "Tilknytt multimediataster (spill/pause/stopp/forrige/neste)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Handling utløst av midtklikk pÃ¥ statusikonet." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Handling utløst av muserulling pÃ¥ statusikonet." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Innstillingen er kun tilgjengelig i statusikonsmodus." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Fjern stasjon" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Rediger stasjon" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "Sikkerhetsunntak fjernet" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Lagre" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbps" #: src/ui/gv-station-view.c:182 #, fuzzy msgid "unknown" msgstr "ukjent tilstand" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 #, fuzzy msgid "nominal" msgstr "nominelt" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 #, fuzzy msgid "min" msgstr "nominelt" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "maks." #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Stereo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "dempet" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "lydst." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Ingen stasjon" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Ingen metadata" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Klarte ikke Ã¥ tilknytte følgende taster" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Klarte ikke Ã¥ forhindre systemdvalgang" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "Spiller %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "Spiller <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Ukjent tittel)" #~ msgid "Autoset Window Height" #~ msgstr "Sett vindushøyde automatisk" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Juster vindushøyden automatisk nÃ¥r en stasjon legges til eller fjernes." #~ msgid "stopped" #~ msgstr "stoppet" #~ msgid "connecting" #~ msgstr "kobler til" #~ msgid "buffering" #~ msgstr "mellomlagrer" #~ msgid "playing" #~ msgstr "spiller" #~ msgid "unknown state" #~ msgstr "ukjent tilstand" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" #~ msgid "Station Information" #~ msgstr "Stasjonsinformasjon" #~ msgid "Playing Station" #~ msgstr "Spiller stasjon" #~ msgid "New Track" #~ msgstr "Nytt spor" #~ msgid "Misc" #~ msgstr "Ymse" #~ msgid "Menu" #~ msgstr "Meny" #~ msgid "Help" #~ msgstr "Hjelp" #~ msgid "Toggle Play/Pause" #~ msgstr "Veksle Spill/Pause" #~ msgid "goodvibes" #~ msgstr "goodvibes" #~ msgid "Goodvibes Radio Player" #~ msgstr "Goodvibes radioavspiller" #~ msgid "Window Behavior" #~ msgstr "Vindusoppførsel" #~ msgid "" #~ "The GStreamer output pipeline used for playback. For example, use " #~ "'alsasink device=hw:2' to force audio output to ALSA, 2nd soundcard.\n" #~ "For more details, please refer to the gst-launch manual page." #~ msgstr "" #~ "GStreamer-utdatakanal brukt for avspilling. Bruk for eksempel \"alsasink " #~ "device=hw:2\" for Ã¥ tvinge lydutdata til ALSA, lydkort nummer to.\n" #~ "For flere detaljer, sjekk gst-launch -manualsiden." #~ msgid "Emit Notifications" #~ msgstr "Vis merknader" #~ msgid "End of stream" #~ msgstr "Dataflytens slutt" #~ msgid "Connecting..." #~ msgstr "Kobler til…" #~ msgid "Buffering..." #~ msgstr "Mellomlagrer…" #~ msgid "Seeting available only in status icon mode." #~ msgstr "Innstillingen er ikke tilgjengelig i statusikonmodus." #~ msgid "Add new station" #~ msgstr "Legg til ny stasjon" #~ msgid "Edit station" #~ msgstr "Rediger stasjon" #~ msgid "Add New Station" #~ msgstr "Legg til ny stasjon" #~ msgid "File" #~ msgstr "Fil" #~ msgid "Player" #~ msgstr "Avspiller" #~ msgid "Set Timeout" #~ msgstr "Sett tidsavbrudd" #~ msgid "How long the notifications should be displayed (seconds)." #~ msgstr "Hvor lenge merknader skal viser (sekunder)." goodvibes-v0.7.2/po/nl.po000066400000000000000000000403241414415210700152600ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-09-21 17:38+0000\n" "Last-Translator: Heimen Stoffels \n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.9-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Luister naar online-radiostations" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" "Goodvibes is een eenvoudige speler voor GNU/Linux waarmee u kunt luisteren " "naar online-radiostations." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Het bevat elke basisfunctie die u mag verwachten van een audiospeler, zoals " "multimediatoetsen, meldingen, slaapstandonderdrukking en MPRIS2-" "ondersteuning." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Radiospeler" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Speler;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Luisteren/Onderbreken" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Station toevoegen" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Voorkeuren" #: src/ui/resources/app-menu.ui:22 msgid "Keyboard Shortcuts" msgstr "Sneltoetsen" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Online hulp" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "Over" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Sluiten" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Afsluiten" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Geen station geselecteerd" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Onderbroken" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Sluitknop" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Toepassing" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Automatisch afspelen na opstarten" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Aangepaste uitvoer-pijpleiding" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Toepassen" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Afspelen" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Slaapstand onderdrukken tijdens afspelen" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Systeem" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Systeem-D-Bus-server" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "MPRIS-D-Bus-server" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Algemeen" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Themavariant" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Systeemstandaard" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Voorkeur voor donker thema" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Voorkeur voor licht thema" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Venster" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Meldingen tonen" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Meldingen" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Terminaluitvoer" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Terminal" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Weergave" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Multimedia-sneltoetsen" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Toetsenbord" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Middelklik" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Dempen" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Scrollen" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Station wijzigen" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Volume wijzigen" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Muis (systeemvakpictogrammodus)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Bediening" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Naam" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Beveiligingsuitzondering" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "Verwijderen" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metagegevens" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Titel" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artiest" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Album" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Genre" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Jaar" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Opmerking" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Station" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Streams" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Gebruikersagent" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Bitsnelheid" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Samplesnelheid" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Kanalen" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Codec" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "Soort stream" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s:%s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Het verwerken van de pijpleidingsconstructie is mislukt" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Bezig met afspelen" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Bezig met verbinden…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Bezig met bufferen…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' is een onbekend station of ongeldige uri" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Het opslaan van de stationslijst is mislukt" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "Beveiligingsuitzondering toevoegen?" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "Er is een fout opgetreden tijdens het afspelen van %s." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Annuleren" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Toevoegen" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "URL" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Fout" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "Details" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Deze mogelijkheid is uitgeschakeld tijdens het compileren." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Actie bij klikken op de sluitknop." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Deze instelling is niet beschikbaar in systeemvakpictogrammodus." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Of de toepassing automatisch moet beginnen afspelen na het opstarten." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Of een aangepaste uitvoer-pijpleiding moet worden gebruikt." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "De voor afspelen gebruikte GStreamer-uitvoerpijplijn. Bekijk de online-" "documentatie voor voorbeelden." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Voorkom dat het systeem in de slaapstand gaat tijdens het luisteren." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Schakel de system-D-Bus-server in (benodigd voor de command-line interface)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Schakel de MPRIS2-D-Bus-server in." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Voorkeur voor andere themavariant (indien beschikbaar)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Meldingen tonen bij statuswijzigingen." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Informatie tonen over de standaarduitvoer." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" "Multimediatoetsen gebruiken (afspelen/pauzeren/stoppen/vorige/volgende)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" "Actie die wordt uitgevoerd na een middelklik op het systeemvakpictogram." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" "Actie die wordt uitgevoerd na scrollen met de muis over het " "systeemvakpictogram." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Deze instelling is alleen beschikbaar in systeemvakpictogrammodus." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Station verwijderen" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Station bewerken" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "Beveiligingsuitzondering verwijderd" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Opslaan" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbps" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "onbekend" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominaal" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "min" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "max" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Stereo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "gedempt" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "vol." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Geen station" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Geen metagegevens" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Het toewijzen van de volgende toetsen is mislukt" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Het voorkomen van de slaapstand is mislukt" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "Bezig met afspelen van %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "Bezig met afspelen van <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(onbekende titel)" #~ msgid "Autoset Window Height" #~ msgstr "Vensterhoogte automatisch instellen" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Vensterhoogte automatisch aanpassen als een station wordt toegevoegd of " #~ "verwijderd." #~ msgid "stopped" #~ msgstr "onderbroken" #~ msgid "connecting" #~ msgstr "bezig met verbinden" #~ msgid "buffering" #~ msgstr "bezig met bufferen" #~ msgid "playing" #~ msgstr "bezig met afspelen" #~ msgid "unknown state" #~ msgstr "onbekende status" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" #~ msgid "Station Information" #~ msgstr "Stationsinformatie" #~ msgid "Playing Station" #~ msgstr "Afspelend station" #~ msgid "New Track" #~ msgstr "Nieuw nummer" #~ msgid "Misc" #~ msgstr "Diversen" #~ msgid "Menu" #~ msgstr "Menu" #~ msgid "Help" #~ msgstr "Hulp" #~ msgid "Toggle Play/Pause" #~ msgstr "Schakelen tussen afspelen/pauzeren" #~ msgid "goodvibes" #~ msgstr "goodvibes" #~ msgid "Goodvibes Radio Player" #~ msgstr "Goodvibes Radiospeler" #~ msgid "Window Behavior" #~ msgstr "Venstergedrag" #~ msgid "" #~ "The GStreamer output pipeline used for playback. For example, use " #~ "'alsasink device=hw:2' to force audio output to ALSA, 2nd soundcard.\n" #~ "For more details, please refer to the gst-launch manual page." #~ msgstr "" #~ "De voor het afspelen gebruikte GStreamer-uitvoerpijpleiding. Gebruik " #~ "bijv. 'alsasink device=hw:2' om audio-uitvoer te forceren naar ALSA op de " #~ "2e geluidskaart.\n" #~ "Voor meer informatie, lees de gst-launch man-pagina." #~ msgid "Emit Notifications" #~ msgstr "Meldingen weergeven" #~ msgid "End of stream" #~ msgstr "Einde van stream" #~ msgid "Connecting..." #~ msgstr "Bezig met verbinden..." #~ msgid "Buffering..." #~ msgstr "Bezig met bufferen..." #~ msgid "Seeting available only in status icon mode." #~ msgstr "De instelling is alleen beschikbaar in systeemvakpictogram-modus." #~ msgid "Add new station" #~ msgstr "Nieuw station toevoegen" #~ msgid "Edit station" #~ msgstr "Station bewerken" #~ msgid "Add New Station" #~ msgstr "Nieuw station toevoegen" #~ msgid "File" #~ msgstr "Bestand" #~ msgid "Player" #~ msgstr "Speler" #~ msgid "Set Timeout" #~ msgstr "Timer instellen" #~ msgid "How long the notifications should be displayed (seconds)." #~ msgstr "Hoe lang de meldingen moet worden weergegeven (seconden)." goodvibes-v0.7.2/po/pl.po000066400000000000000000000265561414415210700152750ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-02-23 07:32+0000\n" "Last-Translator: Michal Biesiada \n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.0-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 #, fuzzy msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" "Goodvibes to prosty odtwarzacz radia internetowego dla systemu GNU / Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Odtwarzacz radia" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "" #: src/ui/resources/app-menu.ui:22 msgid "Keyboard Shortcuts" msgstr "" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Zamknij" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Wyjdź" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Nie wybrano stacji" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Zatrzymano" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Aplikacja" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "" #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "" #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "" #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "" #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "" #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "" #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "" #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "" #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "" #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "" #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "" #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "" goodvibes-v0.7.2/po/pt.po000066400000000000000000000346111414415210700152740ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-09-11 22:17+0000\n" "Last-Translator: ssantos \n" "Language-Team: Portuguese \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.9-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Ouça rádios na web" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" "Goodvibes é um reprodutor simples de rádios da Internet para GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Vem com todos os recursos básicos que pode esperar de um reprodutor de " "áudio, como teclas de multimídia, notificações, inibição da hibernação do " "sistema e suporte para MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Reprodutor de Rádio" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Ãudio;Radio;Reprodutor;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Reproduzir/Parar" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Adicionar Estação" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Preferências" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Teclado" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Fechar" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Sair" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Nenhuma estação selecionada" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Parada" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Botão para Fechar" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Aplicação" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Reprodução automática ao iniciar" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Pipeline de saída personalizada" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Aplicar" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Reproduzir" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Evitar hibernação durante a reprodução" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Sistema" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Servidor de D-Bus nativo" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "Servidor de D-Bus MPRIS2" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Geral" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Variante de Tema" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Predefinição do Sistema" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Preferir Escuro" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Preferir Claro" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Janela" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Enviar Notificações" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Notificações" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Saída do Console" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Consola" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Vista" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Teclas de Atalho de Multimídia" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Teclado" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Clique do Meio" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Silenciar" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Roda" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Mudar Estação" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Mudar Volume" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Rato (Modo de ícone de estado)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Controlos" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Nome" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URl" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Exceção de Segurança" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "Remover" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadados" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Título" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artista" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Ãlbum" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Género" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Ano" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Comentário" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Estação" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Fluxos" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Agente de Utilizador" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Nr. de bits" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Taxa de Símbolos" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Canais" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Codec" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "Tipo de fluxo" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Falha ao analisar a descrição do pipeline" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Reproduzir" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "A ligar…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "A pôr no buffer…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' não é uma estação conhecida um URI válido" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Falha ao gravar a lista de estações" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "Adicionar uma exceção de segurança?" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "Ocorreu um erro ao tentar reproduzir %s." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Cancelar" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Adicionar" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "URL" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Erro" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "Detalhes" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Recurso desativado no momento da compilação." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Ação a ser realizada ao clicar no botão de fechar." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Configuração não disponível no modo de ícone de estado." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Opção para iniciar a reprodução automaticamente ao abrir." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Opção para usar um pipeline de saída personalizado." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "O pipeline de saída do GStreamer usado para a reprodução. Consulte a " "documentação online para ver exemplos." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Evitar que o sistema entre no modo de hibernação durante a reprodução." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Ativa o servidor D-Bus nativo (necessário para a interface de linha de " "comando)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Ativar o servidor D-Bus MPRIS2." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Caso prefira uma variante diferente do tema (se disponível)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Mostrar notificações quando o estado for alterado." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Mostrar informações na saída predefinida." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" "Vincular teclas de multimídia (reproduzir/pausar/parar/anterior/próximo)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Ação desencadeada por um clique do meio no ícone de estado." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Ação desencadeada pela rolagem do rato no ícone de estado." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Configuração disponível apenas no modo de ícone da barra de estado." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Remover Estação" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Editar Estação" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "Exceção de Segurança removida" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Guardar" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbps" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "desconhecido" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominal" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "mín" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "máx" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Estéreo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "silenciada" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "vol." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Nenhuma estação" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Sem metadados" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Falha ao vincular às teclas seguintes" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Falha ao inibir a hibernação do sistema" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "A reproduzir %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "A reproduzir <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Título desconhecido)" #~ msgid "Autoset Window Height" #~ msgstr "Autodefinir o tamanho da janela" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Automaticamente ajustar a altura da janela quando uma estação for " #~ "adicionada ou removida." #~ msgid "stopped" #~ msgstr "parada" #~ msgid "connecting" #~ msgstr "a ligar" #~ msgid "buffering" #~ msgstr "a pôr no buffer" #~ msgid "playing" #~ msgstr "a reproduzir" #~ msgid "unknown state" #~ msgstr "estado desconhecido" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/po/pt_BR.po000066400000000000000000000346341414415210700156640ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-06-27 19:32+0000\n" "Last-Translator: Fúlvio Alves \n" "Language-Team: Portuguese (Brazil) \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.7.1-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Ouça rádios na web" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" "Goodvibes é um reprodutor simples de rádios da Internet para GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Ele vem com todos os recursos básicos que você pode esperar de um reprodutor " "de áudio, como teclas de multimídia, notificações, inibição da hibernação do " "sistema e suporte para MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Reprodutor de Rádio" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Player;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Reproduzir/Parar" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Adicionar Estação" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Preferências" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Teclado" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Fechar" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Sair" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Nenhuma estação selecionada" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Parado" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Botão Fechar" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Aplicação" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Reprodução automática ao iniciar" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Pipeline de saída personalizado" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Aplicar" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Reprodução" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Evitar hibernação durante a reprodução" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Sistema" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Servidor D-Bus nativo" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "Servidor D-Bus MPRIS2" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Geral" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Variante de Tema" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Padrão do Sistema" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Preferir Escuro" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Preferir Claro" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Janela" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Enviar Notificações" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Notificações" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Saída do Console" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Console" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Exibição" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Teclas de Atalho de Multimídia" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Teclado" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Clique do Meio" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Mudo" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Roda" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Alterar Estação" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Alterar Volume" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Mouse (Modo de Ãcone de Status)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Controles" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Nome" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Exceção de Segurança" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "Remover" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadados" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Título" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artista" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Ãlbum" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Gênero" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Ano" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Comentário" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Estação" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Fluxos" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Agente de Usuário" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Taxa de Bits" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Taxa de Símbolos" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Canais" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Codec" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Fluxos" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Falha ao analisar a descrição do pipeline" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Reproduzindo" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Conectando…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Armazenando em buffer…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' não é uma estação conhecida um URI válido" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Falha ao salvar a lista de estações" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "Adicionar uma exceção de segurança?" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "Ocorreu um erro ao tentar reproduzir %s." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Cancelar" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Adicionar" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "URL" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Erro" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Recurso desabilitado no momento da compilação." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Ação a ser realizada ao clicar no botão de fechar." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Configuração não disponível no modo de ícone de status." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Opção para iniciar automaticamente a reprodução ao abrir." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Opção para usar um pipeline de saída personalizado." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "O pipeline de saída do GStreamer usado para reprodução. Consulte a " "documentação online para ver exemplos." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Evitar que o sistema entre no modo de hibernação durante a reprodução." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Ativa o servidor D-Bus nativo (necessário para a interface de linha de " "comando)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Ativar o servidor D-Bus MPRIS2." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Caso prefira uma variante diferente do tema (se disponível)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Mostrar notificações quando o status for alterado." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Mostrar informações na saída padrão." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" "Vincular teclas de multimídia (reproduzir/pausar/parar/anterior/próxima)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Ação desencadeada por um clique do meio no ícone de status." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Ação desencadeada pela rolagem do mouse no ícone de status." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Configuração disponível apenas no modo de ícone da barra de status." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Remover Estação" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Editar Estação" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "Exceção de Segurança removida" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Salvar" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbps" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "desconhecido" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominal" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "mín" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "máx" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Estéreo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "mudo" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "vol." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Sem estação" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Sem metadados" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Falha ao vincular as seguintes teclas" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Falha ao inibir a hibernação do sistema" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "Reproduzindo %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "Reproduzindo <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Título desconhecido)" #~ msgid "Autoset Window Height" #~ msgstr "Autodefinir o tamanho da janela" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Ajustar automaticamente a altura da janela quando uma estação for " #~ "adicionada ou removida." #~ msgid "stopped" #~ msgstr "parado" #~ msgid "connecting" #~ msgstr "conectando" #~ msgid "buffering" #~ msgstr "armazenando em buffer" #~ msgid "playing" #~ msgstr "reproduzindo" #~ msgid "unknown state" #~ msgstr "estado desconhecido" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/po/pt_PT.po000066400000000000000000000346321414415210700157020ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-09-23 14:40+0000\n" "Last-Translator: ssantos \n" "Language-Team: Portuguese (Portugal) \n" "Language: pt_PT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.3-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "Ouça rádios na web" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "" "Goodvibes é um reprodutor simples de rádios da Internet para GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Vem com todos os recursos básicos que pode esperar de um reprodutor de " "áudio, como teclas de multimídia, notificações, inibição da hibernação do " "sistema e suporte para MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Reprodutor de Rádio" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Player;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Reproduzir/Parar" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Adicionar Estação" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Preferências" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Teclado" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Ajuda On-line" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "Sobre" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Fechar" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Sair" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Nenhuma estação selecionada" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Parada" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Botão para Fechar" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Aplicação" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Reprodução automática ao iniciar" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Pipeline de saída personalizada" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Aplicar" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Reproduzir" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Evitar hibernação durante a reprodução" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Sistema" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Servidor de D-Bus nativo" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "Servidor de D-Bus MPRIS2" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Geral" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Variante de Tema" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Predefinição do Sistema" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Preferir Escuro" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Preferir Claro" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Janela" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Enviar Notificações" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Notificações" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Saída do Console" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Consola" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Vista" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Teclas de Atalho de Multimídia" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Teclado" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Clique do Meio" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Silenciar" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Roda" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Mudar Estação" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Mudar Volume" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Rato (Modo de Ãcone de Status)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Controlos" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Nome" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URl" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 #, fuzzy msgid "Remove" msgstr "Remover Estação" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Metadados" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "Título" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Artista" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Ãlbum" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Género" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Ano" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Comentário" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "Estação" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Fluxos" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Agente de Utilizador" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Nr. de bits" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Taxa de Símbolos" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Canais" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Codec" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Fluxos" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Falha ao analisar a descrição do pipeline" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Reproduzir" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "A ligar…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "A pôr no buffer…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' não é uma estação conhecida um URI válido" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "Falha ao gravar a lista de estações" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Cancelar" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 #, fuzzy msgid "URL" msgstr "URl" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Erro" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Recurso desativado no momento da compilação." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Ação a ser realizada ao clicar no botão de fechar." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Configuração não disponível no modo de ícone de status." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Opção para iniciar a reprodução automaticamente ao abrir." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Opção para usar um pipeline de saída personalizado." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "O pipeline de saída do GStreamer usado para a reprodução. Consulte a " "documentação online para ver exemplos." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Evitar que o sistema entre no modo de hibernação durante a reprodução." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Ativa o servidor D-Bus nativo (necessário para a interface de linha de " "comando)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Ativar o servidor D-Bus MPRIS2." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Caso prefira uma variante diferente do tema (se disponível)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Mostrar notificações quando o status for alterado." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Mostrar informações na saída predefinida." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" "Vincular teclas de multimídia (reproduzir/pausar/parar/anterior/próximo)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Ação desencadeada por um clique do meio no ícone de estado." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Ação desencadeada pela rolagem do rato no ícone de estado." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Configuração disponível apenas no modo de ícone da barra de estado." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Remover Estação" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Editar Estação" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Guardar" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbps" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "desconhecido" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominal" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "mín" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "máx" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Mono" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Estéreo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "silenciada" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "vol." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Nenhuma estação" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Sem metadados" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Falha ao vincular às teclas seguintes" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Falha ao inibir a hibernação do sistema" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "A reproduzir %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "A reproduzir <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Título desconhecido)" #~ msgid "Autoset Window Height" #~ msgstr "Autodefinir o tamanho da janela" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Automaticamente ajustar a altura da janela quando uma estação for " #~ "adicionada ou removida." #~ msgid "stopped" #~ msgstr "parada" #~ msgid "connecting" #~ msgstr "a ligar" #~ msgid "buffering" #~ msgstr "a pôr no buffer" #~ msgid "playing" #~ msgstr "a reproduzir" #~ msgid "unknown state" #~ msgstr "estado desconhecido" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" #~ msgid "Misc" #~ msgstr "Diversos" #~ msgid "Menu" #~ msgstr "Menu" #~ msgid "Help" #~ msgstr "Ajuda" goodvibes-v0.7.2/po/ru.po000066400000000000000000000434631414415210700153040ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-12-18 02:29+0000\n" "Last-Translator: Иван \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.4-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "ВоÑпроизведение веб-радиоÑтанций" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 #, fuzzy msgid "Arnaud Rebillout" msgstr "Ðрно Ребилло" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes - Ñто проÑтой плеер интернет-радио Ð´Ð»Ñ GNU/Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Оно поÑтавлÑетÑÑ Ñо вÑеми оÑновными функциÑми, которые можно ожидать от " "аудиоплеера, такими как мультимедийные клавиши, уведомлениÑ, блокировка " "режима Ñна и поддержка MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Радио-проигрыватель" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Ðудио;Радио;Проигрыватель;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 #, fuzzy msgid "Play/Stop" msgstr "ЗапуÑк / Стоп" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "Добавить Ñтанцию" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 #, fuzzy msgid "Preferences" msgstr "ПредпочтениÑ" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "Клавиатура" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Онлайн-Ñправка" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "О программе" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Закрыть" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Выход" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Ðе выбрана радиоÑтанциÑ" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "ОÑтановлено" #: src/ui/resources/prefs-window.glade:55 #, fuzzy msgid "Close Button" msgstr "Закрыть окно" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Приложение" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "ВоÑпроизведение при ЗапуÑке" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "ПользовательÑкий конвейер вывода" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Применить" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "ВоÑпроизведение" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Отключить ÑпÑщий режим" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "СиÑтема" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Родной Ñервер D-Bus" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "MPRIS2 Ñервер D-Bus" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "Шина D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "ОÑновные" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Вариант темы" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "СиÑтема по умолчанию" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Предпочтительно тёмный" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Предпочтительно Ñветлый" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Окно" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "ОтправлÑть уведомлениÑ" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "ОповещениÑ" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Вывод конÑоли" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "КонÑоль" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "ИнтерфейÑ" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Мультимедиа-клавиши" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Клавиатура" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "СреднÑÑ ÐºÐ½Ð¾Ð¿ÐºÐ°" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Приглушить" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Скроллинг" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "Сменить Ñтанцию" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Регулировка громкоÑти" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Мышь (режим Status Icon)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Управление" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Ðазвание" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "URI" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 #, fuzzy msgid "Remove" msgstr "Удалить Ñтанцию" #: src/ui/resources/station-view.glade:83 #, fuzzy msgid "Metadata" msgstr "Метаданные" #: src/ui/resources/station-view.glade:97 #, fuzzy msgid "Title" msgstr "Заглавный" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "ÐртиÑÑ‚" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Ðльбом" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Жанр" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Год" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Комментарий" #: src/ui/resources/station-view.glade:229 #, fuzzy msgid "Station" msgstr "СтанциÑ" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Потоки" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "" #: src/ui/resources/station-view.glade:310 #, fuzzy msgid "Bitrate" msgstr "Битрейт" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "ЧаÑтота диÑкретизации" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Каналы" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Кодек" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "Потоки" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "" #: src/core/gv-engine.c:214 #, fuzzy msgid "Failed to parse pipeline description" msgstr "Ðевозможно Ñохранить наÑтройки радиоÑтанции" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "ВоÑпроизведение" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "Подключение…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "БуферизациÑ…" #: src/core/gv-player.c:870 #, fuzzy, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' не ÑвлÑетÑÑ Ð½Ð¸ извеÑтной Ñтанцией, ни допуÑтимым URI" #: src/core/gv-station-list.c:1282 #, fuzzy msgid "Failed to save station list" msgstr "Ðе удалоÑÑŒ Ñохранить ÑпиÑок Ñтанций" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "Отмена" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 #, fuzzy msgid "URL" msgstr "URI" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "ошибка" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 #, fuzzy msgid "Feature disabled at compile-time." msgstr "Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° во Ð²Ñ€ÐµÐ¼Ñ ÐºÐ¾Ð¼Ð¿Ð¸Ð»Ñции." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "ДейÑтвие при нажатии кнопки закрытиÑ." #: src/ui/gv-prefs-window.c:407 #, fuzzy msgid "Setting not available in status icon mode." msgstr "ÐаÑтройка недоÑтупна в режиме значка ÑоÑтоÑниÑ." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "Ðачинать ли воÑпроизведение автоматичеÑки при запуÑке." #: src/ui/gv-prefs-window.c:417 #, fuzzy msgid "Whether to use a custom output pipeline." msgstr "ИÑпользовать ли наÑтраиваемый таймаут Ð´Ð»Ñ Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ð¹." #: src/ui/gv-prefs-window.c:423 #, fuzzy msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "Конвейер вывода GStreamer, иÑпользуемый Ð´Ð»Ñ Ð²Ð¾ÑпроизведениÑ. Примеры Ñм. в " "интерактивной документации." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Предотвратить переход ÑиÑтемы в ÑпÑщий режим во Ð²Ñ€ÐµÐ¼Ñ Ð²Ð¾ÑпроизведениÑ." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "Включить родной Ñервер D-Bus (необходимо Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ в конÑоли)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "Включить MPRIS2 Ñервер D-Bus." #. Display #: src/ui/gv-prefs-window.c:456 #, fuzzy msgid "Prefer a different variant of the theme (if available)." msgstr "ПредпочеÑть другой вариант темы (еÑли еÑть)." #: src/ui/gv-prefs-window.c:462 #, fuzzy msgid "Show notification when the status changes." msgstr "Выводить Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ изменении ÑтатуÑа." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Выводить информацию в конÑоль." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" "Ðазначить мультимедиа-клавиши (воÑпроизведение/пауза/Ñтоп/назад/вперёд)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "ДейÑтвие, выполнÑемое Ñредней кнопкой мыши по значку ÑтатуÑа." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "ДейÑтвие, выполнÑемое при проматывании мышью по значку ÑтатуÑа." #: src/ui/gv-prefs-window.c:491 #, fuzzy msgid "Setting only available in status icon mode." msgstr "Приложение не было запущено в режиме значка ÑтатуÑа." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "Удалить Ñтанцию" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "Изменить Ñтанцию" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Сохранить" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "кбит/Ñ" #: src/ui/gv-station-view.c:182 #, fuzzy msgid "unknown" msgstr "неизвеÑтное ÑоÑтоÑние" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 #, fuzzy msgid "nominal" msgstr "номинальный" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 #, fuzzy msgid "min" msgstr "минимум" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "макÑимум" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Моно" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Стерео" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "кГц" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "приглушено" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "гром." #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "Ðет Ñтанции" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Ðет метаданных" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "Ðе удалоÑÑŒ назначить Ñледующие клавиши" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Ðе удалоÑÑŒ заблокировать ÑпÑщий режим ÑиÑтемы" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "ВоÑпроизведение %s" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "ВоÑпроизведение <%s>" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(ÐеизвеÑтное название)" #~ msgid "Autoset Window Height" #~ msgstr "ÐвтоматичеÑÐºÐ°Ñ ÑƒÑтановка выÑоты окна" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "ÐвтоматичеÑки регулировать выÑоту окна при добавлении или удалении " #~ "Ñтанции." #~ msgid "stopped" #~ msgstr "оÑтановлено" #~ msgid "connecting" #~ msgstr "Ñоединение" #~ msgid "buffering" #~ msgstr "буферизациÑ" #~ msgid "playing" #~ msgstr "воÑпроизведение" #~ msgid "unknown state" #~ msgstr "неизвеÑтное ÑоÑтоÑние" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" #, fuzzy #~ msgid "Playing Station" #~ msgstr "Сменить Ñтанцию" #~ msgid "Misc" #~ msgstr "Разное" #~ msgid "Help" #~ msgstr "Справка" #~ msgid "Toggle Play/Pause" #~ msgstr "ВоÑпр./Пауза" #~ msgid "goodvibes" #~ msgstr "goodvibes" #~ msgid "Goodvibes Radio Player" #~ msgstr "Радио-проигрыватель Goodvibes" #~ msgid "Emit Notifications" #~ msgstr "Показывать оповещениÑ" #~ msgid "Connecting..." #~ msgstr "Соединение..." #~ msgid "Buffering..." #~ msgstr "БуферизациÑ..." #, fuzzy #~ msgid "Seeting available only in status icon mode." #~ msgstr "Приложение не было запущено в режиме значка ÑтатуÑа." #~ msgid "Add new station" #~ msgstr "Добавить новую Ñтанцию" #~ msgid "Edit station" #~ msgstr "Изменить Ñтанцию" #, fuzzy #~ msgid "Add New Station" #~ msgstr "Добавить новую Ñтанцию" #~ msgid "File" #~ msgstr "Файл" #~ msgid "Player" #~ msgstr "Проигрыватель" #~ msgid "Set Timeout" #~ msgstr "УÑтановить таймаут" #~ msgid "How long the notifications should be displayed (seconds)." #~ msgstr "ÐаÑколько долго должны отображатьÑÑ Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ (Ñекунды)." goodvibes-v0.7.2/po/sk.po000066400000000000000000000272301414415210700152650ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2020-02-08 14:50+0000\n" "Last-Translator: Andrej Shadura \n" "Language-Team: Slovak \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Weblate 3.11-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes je jednoduchý prehrávaÄ internetových rádií pre GNU / Linux." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Poskytuje vÅ¡etky základné funkcie, ktoré môžete od audio prehrávaÄa " "oÄakávaÅ¥, ako multimediálne klávesy, oznámenie, inhibícia spánku systému a " "podpora MPRIS2." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "PrehrávaÄ rádia" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;PrehrávaÄ;Zvuk;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "" #: src/ui/resources/app-menu.ui:22 msgid "Keyboard Shortcuts" msgstr "" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "ZavrieÅ¥" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "UkonÄiÅ¥" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "Nie je vybratá žiadna stanica" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "Automatické prehrávanie po Å¡tarte systému" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "" #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "" #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "" #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "" #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "" #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "" #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "" #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "" #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "" #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "" #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "" #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "" #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "" #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/po/te.po000066400000000000000000000436631414415210700152700ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-06-15 11:33+0000\n" "Last-Translator: Deleted User \n" "Language-Team: Telugu \n" "Language: te\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.7-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "à°—à±à°¡à±à°µà±ˆà°¬à±à°¸à±" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "వెబౠరేడియో లౠపà±à°²à±‡ చేయండి" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "à°…à°°à±à°¨à±Œà°¦à± రీబిలà±à°²à±Œà°Ÿà±" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "à°—à±à°¡à± వైబà±à°¸à± à°…à°¨à±à°¨à°¦à°¿ GNU/Linux లో నడిచే à°ªà±à°°à°¾à°¥à°®à°¿à°• ఇంటరà±à°¨à±†à°Ÿà± రేడియో à°ªà±à°²à±‡à°¯à°°à±." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "ఆడియో à°ªà±à°²à±‡à°¯à°°à± లో మీరౠకోరే మలà±à°Ÿà°¿à°®à±€à°¡à°¿à°¯à°¾ కీసà±, నోటిఫికేషనà±à°¸à±, నిదà±à°°à°ªà±‹à°¨à°¿à°µà±à°µà°¨à°¿ సూచికలà±, MPRIS2 సపోరà±à°Ÿà± వంటి à°…à°¨à±à°¨à°¿ " "మౌలికమైన ఫీచరౠలని ఇది కలిగి ఉంది." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "రేడియో à°ªà±à°²à±‡à°¯à°°à±" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "ఆడియో;రేడియో;à°ªà±à°²à±‡à°¯à°°à±;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "à°ªà±à°²à±‡ చేయà±à°®à±/ఆపà±à°®à±" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "à°¸à±à°Ÿà±‡à°·à°¨à± ని చేరà±à°šà°‚à°¡à°¿" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "ఇషà±à°Ÿà°¾à°²à±" #: src/ui/resources/app-menu.ui:22 #, fuzzy msgid "Keyboard Shortcuts" msgstr "కీబోరà±à°¡à±" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "à°®à±à°—à°¿à°‚à°šà±à°®à±" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "బయటకà±" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "à°¸à±à°Ÿà±‡à°·à°¨à± à°Žà°‚à°šà±à°•ొనలేదà±" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "ఆపివేయబడినది" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "à°®à±à°—ించే బటనà±" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "à°…à°ªà±à°²à°¿à°•ేషనà±" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "మొదలైన పిమà±à°®à°Ÿ ఆటోపà±à°²à±‡" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "నచà±à°šà°¿à°¨ ఔటà±à°ªà±à°Ÿà± పైపà±à°²à±ˆà°¨à±" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "à°…à°ªà±à°²à±ˆ" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "à°ªà±à°²à±‡à°¬à°¾à°•à±" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "à°ªà±à°²à±‡ చేసే సమయంలో నిదà±à°°à°¨à°¿ ఆపండి" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "సిసà±à°Ÿà°‚" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "à°ªà±à°°à°¾à°‚తీయ à°¡à±€-బసౠసరà±à°µà°°à±" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "MPRIS2 à°¡à±€-బసౠసరà±à°µà°°à±" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "à°¡à±€-బసà±" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "సాధారణం" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "థీం à°°à°•à°®à±" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "సిసà±à°Ÿà°‚ డిఫాలà±à°Ÿà±" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "à°®à±à°¦à±à°°à± రంగౠఇషà±à°Ÿà°‚" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "తేలిక రంగౠఇషà±à°Ÿà°‚" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "à°•à°¿à°Ÿà°¿à°•à±€" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "నోటిఫికేషనà±à°¸à± పంపà±à°®à±" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "నోటిఫికేషనà±à°¸à±" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "à°¸à±à°•à±à°°à±€à°¨à± ఔటà±à°ªà±à°Ÿà±" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "à°¸à±à°•à±à°°à±€à°¨à±" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "à°¡à°¿à°¸à±à°ªà±à°²à±‡" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "మలà±à°Ÿà±€à°®à±€à°¡à°¿à°¯à°¾ హాటౠకీసà±" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "కీబోరà±à°¡à±" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "మధà±à°¯à°²à±‹à°¨à°¿ à°•à±à°²à°¿à°•à±" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "à°®à±à°¯à±‚టౠచేయà±à°®à±" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "à°¸à±à°•à±à°°à±‹à°²à°¿à°‚à°—à±" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "à°¸à±à°Ÿà±‡à°·à°¨à± ని మారà±à°šà±à°®à±" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "వోలà±à°¯à±‚à°‚ ని మారà±à°šà±à°®à±" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "మౌసౠ(à°¸à±à°Ÿà°¾à°Ÿà°¸à± à°à°•నౠమోడà±)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "à°•à°‚à°Ÿà±à°°à±‹à°²à±à°¸à±" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "పేరà±" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "యూ ఆరౠà°" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "" #: src/ui/resources/station-dialog.glade:74 #, fuzzy msgid "Remove" msgstr "à°¸à±à°Ÿà±‡à°·à°¨à± ని తొలగించండి" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "మెటా డేటా" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "నామధేయం" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "కళాకారà±à°¡à±" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "ఆలà±à°¬à°‚" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "à°°à°•à°®à±" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "సంవతà±à°¸à°°à°‚" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "కామెంటà±" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "à°¸à±à°Ÿà±‡à°·à°¨à±" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "à°¸à±à°Ÿà±à°°à±€à°‚ à°²à±" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "యూజరౠ- à°à°œà±†à°‚à°Ÿà±" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "బిటౠరేటà±" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "నమూనా రేటà±" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "చానెలౠలà±" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "కోడెకà±" #: src/ui/resources/station-view.glade:354 #, fuzzy msgid "Stream Type" msgstr "à°¸à±à°Ÿà±à°°à±€à°‚ à°²à±" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "పైపౠలైనౠవరà±à°£à°¨à°¨à°¿ పారà±à°¸à± చేయలేకà±à°¨à±à°¨à°¾à°®à±" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "నడà±à°¸à±à°¤à±‹à°‚ది" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "సంధానమవà±à°¤à±‹à°‚ది…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "బఫరౠఅవà±à°¤à±‹à°‚ది…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' తెలిసిన à°¸à±à°Ÿà±‡à°·à°¨à± కానీ తగిన URI కానీ కాదà±" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "à°¸à±à°Ÿà±‡à°·à°¨à± లిసà±à°Ÿà± సేవౠచేయలేకà±à°¨à±à°¨à°¾à°®à±" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "" #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "à°°à°¦à±à°¦à± చేయà±à°®à±" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "" #: src/ui/gv-main-window.c:116 #, fuzzy msgid "URL" msgstr "యూ ఆరౠà°" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "తపà±à°ªà±" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "కంపైలౠసమయంలో ఫీచరౠనిరà±à°ªà°¯à±‹à°—à°‚ చేయబడినది." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "ఆపివేయౠబటనౠనొకà±à°•ినపà±à°¡à± చేసే పని." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "à°¸à±à°Ÿà°¾à°Ÿà°¸à± à°à°•నౠపదà±à°§à°¤à°¿à°²à±‹ సెటà±à°Ÿà°¿à°‚గౠలభించలేదà±." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "మొదలవగానే à°ªà±à°²à±‡à°¬à°¾à°•ౠని మొదలెటà±à°Ÿà°¾à°²à°¾." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "నచà±à°šà°¿à°¨ ఔటà±à°ªà±à°Ÿà± పైపౠలైనౠని వాడాలా." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "à°ªà±à°²à±‡à°¬à°¾à°•ౠకోసం GStreamer ఔటà±à°ªà±à°Ÿà± పైపౠలైనà±. ఉదాహరణల కొరకౠఆనà±à°²à±ˆà°¨à± సమాచారానà±à°¨à°¿ సంపà±à°°à°¦à°¿à°‚à°šà°‚à°¡à°¿." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "నడà±à°šà± సమయమà±à°¨ సిసà±à°Ÿà°‚ ని నిదà±à°°à°ªà±‹ నివà±à°µà°•à°‚à°¡à°¿." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "à°ªà±à°°à°¾à°‚తీయ à°¡à±€-బసౠసరà±à°µà°°à± ని మేలà±à°•ొలిపి ఉంచండి (కమాండà±-లైనౠఇంటరà±à°«à±‡à°šà± కోసం అవసరం)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "MPRIS2 à°¡à±€-బసౠసరà±à°µà°°à± ని మేలà±à°•ొలపండి." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "థీం వేరే à°°à°•à°‚ నచà±à°šà°¿à°‚ది (లభà±à°¯à°®à±ˆà°¤à±‡)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "à°¸à±à°Ÿà°¾à°Ÿà°¸à± మారినపà±à°¡à± నోటిఫికేషనౠని చూపించండి." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "à°ªà±à°°à°¾à°®à°¾à°£à°¿à°•మైన ఔటà±à°ªà±à°Ÿà± పై సమాచారానà±à°¨à°¿ చూపించà±à°®à±." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "మలà±à°Ÿà±€à°®à±€à°¡à°¿à°¯à°¾ కీసౠని బైండౠచేయండి (à°ªà±à°²à±‡/ఆగà±à°®à±/ఆపà±à°®à±/à°®à±à°‚దరి/తరà±à°µà°¾à°¤à°¿)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "à°¸à±à°Ÿà°¾à°Ÿà°¸à± à°à°•నౠపై మధà±à°¯ à°•à±à°²à°¿à°•ౠవలన à°šà°°à±à°¯ మొదలైంది." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "à°¸à±à°Ÿà°¾à°Ÿà°¸à± à°à°•నౠపై మౌసౠనడపడం వలన à°šà°°à±à°¯ మొదలైంది." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "à°¸à±à°Ÿà°¾à°Ÿà°¸à± à°à°•నౠపదà±à°§à°¤à°¿à°²à±‹à°¨à±‡ సెటà±à°Ÿà°¿à°‚గౠలభà±à°¯à°‚." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "à°¸à±à°Ÿà±‡à°·à°¨à± ని తొలగించండి" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "à°¸à±à°Ÿà±‡à°·à°¨à± ని ఎడిటౠచేయండి" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "సేవౠచేయండి" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "కేపీబీఎసà±" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "తెలియనిది" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "నామమాతà±à°°à°ªà±" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "అతి తకà±à°•à±à°µ" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "అతి à°Žà°•à±à°•à±à°µ" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "ఒకేరకం" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "à°¸à±à°Ÿà±€à°°à°¿à°¯à±‹" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "కిలోహరà±à°œà±" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "à°®à±à°¯à±‚టౠచేయబడినది" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "వోలà±à°¯à±‚à°‚" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "à°¸à±à°Ÿà±‡à°·à°¨à±à°²à± లేవà±" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "మెటాడేటా లేదà±" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "కింది à°•à±€ లని బైండౠచేయలేమà±" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "సిసà±à°Ÿà°‚ నిదà±à°°à°¨à°¿ ఆపలేమà±" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "%s నడà±à°¸à±à°¤à±‹à°‚ది" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "<%s> నడà±à°¸à±à°¤à±‹à°‚ది" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(తెలియని నామమà±)" #~ msgid "Autoset Window Height" #~ msgstr "à°•à°¿à°Ÿà°¿à°•à±€ à°Žà°¤à±à°¤à±à°¨à°¿ ఆటోసెటౠచేయండి" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "à°’à°• à°¸à±à°Ÿà±‡à°·à°¨à± ని చేరà±à°šà°¿à°¨à°ªà±à°¡à± లేక తొలగించినపà±à°¡à± à°•à°¿à°Ÿà°¿à°•à±€ à°Žà°¤à±à°¤à±à°¨à± ఆటోమేటిగà±à°—à°¾ సరిచేయండి." #~ msgid "stopped" #~ msgstr "ఆపివేయబడినది" #~ msgid "connecting" #~ msgstr "సంధానమవà±à°¤à±‹à°‚ది" #~ msgid "buffering" #~ msgstr "బఫరౠఅవà±à°¤à±‹à°‚ది" #~ msgid "playing" #~ msgstr "à°ªà±à°²à±‡ à°…à°µà±à°¤à±‹à°‚ది" #~ msgid "unknown state" #~ msgstr "తెలియని à°¸à±à°¥à°¿à°¤à°¿" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.à°—à±à°¡à±à°µà±ˆà°¬à±à°¸à±" goodvibes-v0.7.2/po/tr.po000066400000000000000000000346641414415210700153060ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the goodvibes package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: goodvibes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-20 23:36+0700\n" "PO-Revision-Date: 2021-09-21 17:38+0000\n" "Last-Translator: OÄŸuz Ersen \n" "Language-Team: Turkish \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.9-dev\n" #: data/io.gitlab.Goodvibes.appdata.xml.in:4 #: data/io.gitlab.Goodvibes.desktop.in:3 src/main.c:148 msgid "Goodvibes" msgstr "Goodvibes" #: data/io.gitlab.Goodvibes.appdata.xml.in:5 #: data/io.gitlab.Goodvibes.desktop.in:5 msgid "Play web radios" msgstr "İnternet radyolarını dinleyin" #: data/io.gitlab.Goodvibes.appdata.xml.in:8 msgid "Arnaud Rebillout" msgstr "Arnaud Rebillout" #: data/io.gitlab.Goodvibes.appdata.xml.in:12 msgid "Goodvibes is a simple internet radio player for GNU/Linux." msgstr "Goodvibes, GNU/Linux için basit bir internet radyo oynatıcısıdır." #: data/io.gitlab.Goodvibes.appdata.xml.in:15 msgid "" "It comes with every basic features you can expect from an audio player, such " "as multimedia keys, notifications, system sleep inhibition, and MPRIS2 " "support." msgstr "" "Çoklu ortam tuÅŸları, bildirimler, sistem uyku engellemesi ve MPRIS2 desteÄŸi " "gibi bir ses oynatıcıdan bekleyebileceÄŸiniz tüm temel özelliklerle birlikte " "gelmektedir." #: data/io.gitlab.Goodvibes.desktop.in:4 msgid "Radio Player" msgstr "Radyo Oynatıcı" #. TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/io.gitlab.Goodvibes.desktop.in:7 msgid "Audio;Radio;Player;" msgstr "Audio;Radio;Player;Ses;Radyo;Oynatıcı;" #: src/ui/resources/app-menu.ui:6 src/ui/resources/prefs-window.glade:563 #: src/ui/resources/shortcuts-window.ui:16 #: src/ui/resources/status-icon-menu.ui:6 msgid "Play/Stop" msgstr "Oynat/Durdur" #: src/ui/resources/app-menu.ui:10 src/ui/resources/shortcuts-window.ui:23 #: src/ui/resources/status-icon-menu.ui:10 src/ui/gv-station-context-menu.c:34 #: src/ui/gv-station-dialog.c:455 msgid "Add Station" msgstr "İstasyon Ekle" #: src/ui/resources/app-menu.ui:16 src/ui/resources/status-icon-menu.ui:16 #: src/ui/gv-prefs-window.c:599 msgid "Preferences" msgstr "Tercihler" #: src/ui/resources/app-menu.ui:22 msgid "Keyboard Shortcuts" msgstr "Klavye Kısayolları" #: src/ui/resources/app-menu.ui:26 src/ui/resources/shortcuts-window.ui:30 #: src/ui/resources/status-icon-menu.ui:22 msgid "Online Help" msgstr "Çevrim İçi Yardım" #: src/ui/resources/app-menu.ui:30 src/ui/resources/status-icon-menu.ui:26 msgid "About" msgstr "Hakkında" #: src/ui/resources/app-menu.ui:34 src/ui/resources/prefs-window.glade:42 #: src/ui/resources/shortcuts-window.ui:37 msgid "Close" msgstr "Kapat" #: src/ui/resources/app-menu.ui:38 src/ui/resources/prefs-window.glade:41 #: src/ui/resources/shortcuts-window.ui:44 #: src/ui/resources/status-icon-menu.ui:30 msgid "Quit" msgstr "Çıkış" #: src/ui/resources/playlist-view.glade:49 #: src/ui/resources/station-view.glade:25 src/ui/gv-playlist-view.c:108 #: src/ui/gv-station-view.c:331 msgid "No station selected" msgstr "İstasyon seçilmedi" #: src/ui/resources/playlist-view.glade:65 #: src/ui/resources/station-view.glade:41 src/core/gv-player.c:150 msgid "Stopped" msgstr "Durduruldu" #: src/ui/resources/prefs-window.glade:55 msgid "Close Button" msgstr "Kapatma Düğmesi" #: src/ui/resources/prefs-window.glade:68 msgid "Application" msgstr "Uygulama" #: src/ui/resources/prefs-window.glade:91 msgid "Autoplay on Startup" msgstr "BaÅŸlangıçta Otomatik Oynat" #: src/ui/resources/prefs-window.glade:104 msgid "Custom Output Pipeline" msgstr "Özel Ses Çıkış Kanalı" #: src/ui/resources/prefs-window.glade:135 msgid "Apply" msgstr "Uygula" #: src/ui/resources/prefs-window.glade:158 msgid "Playback" msgstr "Oynatma" #: src/ui/resources/prefs-window.glade:195 msgid "Prevent sleep while playing" msgstr "Oynatırken uykuya geçmeyi engelle" #: src/ui/resources/prefs-window.glade:208 msgid "System" msgstr "Sistem" #: src/ui/resources/prefs-window.glade:255 msgid "Native D-Bus Server" msgstr "Yerel D-Bus Sunucusu" #: src/ui/resources/prefs-window.glade:267 msgid "MPRIS2 D-Bus Server" msgstr "MPRIS2 D-Bus Sunucusu" #: src/ui/resources/prefs-window.glade:280 msgid "D-Bus" msgstr "D-Bus" #: src/ui/resources/prefs-window.glade:296 #: src/ui/resources/shortcuts-window.ui:11 msgid "General" msgstr "Genel" #: src/ui/resources/prefs-window.glade:324 msgid "Theme Variant" msgstr "Tema ÇeÅŸidi" #: src/ui/resources/prefs-window.glade:337 msgid "System Default" msgstr "Sistem Öntanımlı DeÄŸeri" #: src/ui/resources/prefs-window.glade:338 msgid "Prefer Dark" msgstr "Koyu Tema Tercih Et" #: src/ui/resources/prefs-window.glade:339 msgid "Prefer Light" msgstr "Açık Tema Tercih Et" #: src/ui/resources/prefs-window.glade:353 msgid "Window" msgstr "Pencere" #: src/ui/resources/prefs-window.glade:380 msgid "Send Notifications" msgstr "Bildirim Gönder" #: src/ui/resources/prefs-window.glade:403 msgid "Notifications" msgstr "Bildirimler" #: src/ui/resources/prefs-window.glade:439 msgid "Console Output" msgstr "Konsol Çıkışı" #: src/ui/resources/prefs-window.glade:452 msgid "Console" msgstr "Konsol" #: src/ui/resources/prefs-window.glade:471 msgid "Display" msgstr "Görünüm" #: src/ui/resources/prefs-window.glade:510 msgid "Multimedia Hotkeys" msgstr "Çoklu Ortam Kısayol TuÅŸları" #: src/ui/resources/prefs-window.glade:523 msgid "Keyboard" msgstr "Klavye" #: src/ui/resources/prefs-window.glade:550 msgid "Middle Click" msgstr "Orta Tıklama" #: src/ui/resources/prefs-window.glade:564 msgid "Mute" msgstr "Sesi Kapat" #: src/ui/resources/prefs-window.glade:577 msgid "Scrolling" msgstr "Kaydırma" #: src/ui/resources/prefs-window.glade:590 msgid "Change Station" msgstr "İstasyon DeÄŸiÅŸtir" #: src/ui/resources/prefs-window.glade:591 msgid "Change Volume" msgstr "Ses Seviyesini DeÄŸiÅŸtir" #: src/ui/resources/prefs-window.glade:605 msgid "Mouse (Status Icon Mode)" msgstr "Fare (Durum Simgesi Modu)" #: src/ui/resources/prefs-window.glade:624 msgid "Controls" msgstr "Denetimler" #: src/ui/resources/station-dialog.glade:13 msgid "Name" msgstr "Ad" #: src/ui/resources/station-dialog.glade:36 #: src/ui/resources/station-view.glade:243 msgid "URI" msgstr "Adres (URI)" #: src/ui/resources/station-dialog.glade:63 msgid "Security Exception" msgstr "Güvenlik İstisnası" #: src/ui/resources/station-dialog.glade:74 msgid "Remove" msgstr "Kaldır" #: src/ui/resources/station-view.glade:83 msgid "Metadata" msgstr "Üst Veri" #: src/ui/resources/station-view.glade:97 msgid "Title" msgstr "BaÅŸlık" #: src/ui/resources/station-view.glade:108 msgid "Artist" msgstr "Sanatçı" #: src/ui/resources/station-view.glade:119 msgid "Album" msgstr "Albüm" #: src/ui/resources/station-view.glade:130 msgid "Genre" msgstr "Tür" #: src/ui/resources/station-view.glade:141 msgid "Year" msgstr "Yıl" #: src/ui/resources/station-view.glade:152 msgid "Comment" msgstr "Yorum" #: src/ui/resources/station-view.glade:229 msgid "Station" msgstr "İstasyon" #: src/ui/resources/station-view.glade:255 msgid "Streams" msgstr "Yayınlar" #: src/ui/resources/station-view.glade:266 msgid "User-Agent" msgstr "Kullanıcı Aracısı" #: src/ui/resources/station-view.glade:310 msgid "Bitrate" msgstr "Bit Hızı" #: src/ui/resources/station-view.glade:321 msgid "Sample Rate" msgstr "Örnekleme Hızı" #: src/ui/resources/station-view.glade:332 msgid "Channels" msgstr "Kanallar" #: src/ui/resources/station-view.glade:343 msgid "Codec" msgstr "Kodlayıcı" #: src/ui/resources/station-view.glade:354 msgid "Stream Type" msgstr "Yayın Türü" #: src/core/gv-engine.c:213 src/core/gv-station-list.c:1281 #, c-format msgid "%s: %s" msgstr "%s: %s" #: src/core/gv-engine.c:214 msgid "Failed to parse pipeline description" msgstr "Ses kanalı açıklaması ayrıştırılamadı" #: src/core/gv-player.c:140 src/feat/gv-inhibitor.c:142 #: src/feat/gv-notifications.c:67 src/feat/gv-notifications.c:112 msgid "Playing" msgstr "Oynatılıyor" #: src/core/gv-player.c:143 msgid "Connecting…" msgstr "BaÄŸlanıyor…" #: src/core/gv-player.c:146 msgid "Buffering…" msgstr "Ara belleÄŸe alınıyor…" #: src/core/gv-player.c:870 #, c-format msgid "'%s' is neither a known station or a valid URI" msgstr "'%s' bilinen bir istasyon veya geçerli bir URI deÄŸil" #: src/core/gv-station-list.c:1282 msgid "Failed to save station list" msgstr "İstasyon listesi kaydedilemedi" #: src/ui/gv-main-window.c:95 msgid "Add a security exception?" msgstr "Bir güvenlik istisnası eklensin mi?" #: src/ui/gv-main-window.c:99 #, c-format msgid "An error happened while trying to play %s." msgstr "%s oynatmaya çalışırken bir hata oldu." #: src/ui/gv-main-window.c:102 src/ui/gv-station-dialog.c:299 msgid "Cancel" msgstr "İptal" #: src/ui/gv-main-window.c:103 msgid "Add" msgstr "Ekle" #: src/ui/gv-main-window.c:116 msgid "URL" msgstr "URL" #: src/ui/gv-main-window.c:125 src/feat/gv-notifications.c:124 msgid "Error" msgstr "Hata" #: src/ui/gv-main-window.c:145 msgid "Details" msgstr "Ayrıntılar" #: src/ui/gv-prefs-window.c:259 msgid "Feature disabled at compile-time." msgstr "Özellik derleme sırasında devre dışı bırakıldı." #: src/ui/gv-prefs-window.c:401 msgid "Action when the close button is clicked." msgstr "Kapatma düğmesine tıklandığında gerçekleÅŸtirilecek eylem." #: src/ui/gv-prefs-window.c:407 msgid "Setting not available in status icon mode." msgstr "Durum simgesi modunda ayar kullanılabilir deÄŸil." #: src/ui/gv-prefs-window.c:411 msgid "Whether to start playback automatically on startup." msgstr "BaÅŸlangıçta oynatmanın otomatik olarak baÅŸlatılıp baÅŸlatılmayacağı." #: src/ui/gv-prefs-window.c:417 msgid "Whether to use a custom output pipeline." msgstr "Özel bir ses çıkış kanalı kullanılıp kullanılmayacağı." #: src/ui/gv-prefs-window.c:423 msgid "" "The GStreamer output pipeline used for playback. Refer to the online " "documentation for examples." msgstr "" "Oynatma için kullanılan GStreamer ses çıkış kanalı. Örnekler için çevrim içi " "belgelendirmeye bakın." #: src/ui/gv-prefs-window.c:439 msgid "Prevent the system from going to sleep while playing." msgstr "Oynatma sırasında sistemin uykuya geçmesini engelle." #: src/ui/gv-prefs-window.c:444 msgid "Enable the native D-Bus server (needed for the command-line interface)." msgstr "" "Yerel D-Bus sunucusunu etkinleÅŸtir (komut satırı arayüzü için gereklidir)." #: src/ui/gv-prefs-window.c:450 msgid "Enable the MPRIS2 D-Bus server." msgstr "MPRIS2 D-Bus sunucusunu etkinleÅŸtir." #. Display #: src/ui/gv-prefs-window.c:456 msgid "Prefer a different variant of the theme (if available)." msgstr "Farklı bir tema çeÅŸidini tercih et (varsa)." #: src/ui/gv-prefs-window.c:462 msgid "Show notification when the status changes." msgstr "Durum deÄŸiÅŸtiÄŸinde bildirim göster." #: src/ui/gv-prefs-window.c:467 msgid "Display information on the standard output." msgstr "Bilgileri standart çıkışta göster." #. Controls #: src/ui/gv-prefs-window.c:473 msgid "Bind mutimedia keys (play/pause/stop/previous/next)." msgstr "Çoklu ortam tuÅŸlarını ata (oynat/duraklat/durdur/önceki/sonraki)." #: src/ui/gv-prefs-window.c:479 msgid "Action triggered by a middle click on the status icon." msgstr "Durum simgesine orta tıklama ile tetiklenen eylem." #: src/ui/gv-prefs-window.c:485 msgid "Action triggered by mouse-scrolling on the status icon." msgstr "Durum simgesi üzerinde fare kaydırma ile tetiklenen eylem." #: src/ui/gv-prefs-window.c:491 msgid "Setting only available in status icon mode." msgstr "Ayar yalnızca durum simgesi modunda kullanılabilir." #: src/ui/gv-station-context-menu.c:35 msgid "Remove Station" msgstr "İstasyonu Kaldır" #: src/ui/gv-station-context-menu.c:36 src/ui/gv-station-dialog.c:455 msgid "Edit Station" msgstr "İstasyonu Düzenle" #. We don't do it yet #: src/ui/gv-station-dialog.c:174 msgid "Security Exception removed" msgstr "Güvenlik istisnası kaldırıldı" #: src/ui/gv-station-dialog.c:300 msgid "Save" msgstr "Kaydet" #: src/ui/gv-station-view.c:180 msgid "kbps" msgstr "kbps" #: src/ui/gv-station-view.c:182 msgid "unknown" msgstr "bilinmiyor" #. TRANSLATORS: we talk about nominal bitrate here. #: src/ui/gv-station-view.c:186 src/ui/gv-station-view.c:196 msgid "nominal" msgstr "nominal" #: src/ui/gv-station-view.c:191 src/ui/gv-station-view.c:197 msgid "min" msgstr "en düşük" #: src/ui/gv-station-view.c:192 src/ui/gv-station-view.c:198 msgid "max" msgstr "en yüksek" #: src/ui/gv-station-view.c:213 msgid "Mono" msgstr "Tek kanal" #: src/ui/gv-station-view.c:216 msgid "Stereo" msgstr "Stereo" #: src/ui/gv-station-view.c:262 msgid "kHz" msgstr "kHz" #: src/ui/gv-status-icon.c:138 msgid "muted" msgstr "ses kapatıldı" #: src/ui/gv-status-icon.c:142 msgid "vol." msgstr "ses" #: src/ui/gv-status-icon.c:149 msgid "No station" msgstr "İstasyon yok" #: src/ui/gv-status-icon.c:156 msgid "No metadata" msgstr "Üst veri yok" #: src/feat/gv-hotkeys.c:144 #, c-format msgid "" "%s:\n" "%s" msgstr "" "%s:\n" "%s" #: src/feat/gv-hotkeys.c:145 msgid "Failed to bind the following keys" msgstr "AÅŸağıdaki tuÅŸlar atanamadı" #: src/feat/gv-inhibitor.c:120 msgid "Failed to inhibit system sleep" msgstr "Sistemin uykuya geçmesi engellenemedi" #: src/feat/gv-notifications.c:61 #, c-format msgid "Playing %s" msgstr "%s oynatılıyor" #: src/feat/gv-notifications.c:64 #, c-format msgid "Playing <%s>" msgstr "<%s> oynatılıyor" #: src/feat/gv-notifications.c:105 msgid "(Unknown title)" msgstr "(Bilinmeyen baÅŸlık)" #~ msgid "Autoset Window Height" #~ msgstr "Pencere YüksekliÄŸini Otomatik Ayarla" #~ msgid "" #~ "Automatically adjust the window height when a station is added or removed." #~ msgstr "" #~ "Bir istasyon eklendiÄŸinde veya kaldırıldığında pencere yüksekliÄŸini " #~ "otomatik olarak ayarla." #~ msgid "stopped" #~ msgstr "durduruldu" #~ msgid "connecting" #~ msgstr "baÄŸlanıyor" #~ msgid "buffering" #~ msgstr "ara belleÄŸe alınıyor" #~ msgid "playing" #~ msgstr "oynatılıyor" #~ msgid "unknown state" #~ msgstr "bilinmeyen durum" #~ msgid "io.gitlab.Goodvibes" #~ msgstr "io.gitlab.Goodvibes" goodvibes-v0.7.2/scripts/000077500000000000000000000000001414415210700153555ustar00rootroot00000000000000goodvibes-v0.7.2/scripts/build-ci-image.sh000077500000000000000000000030631414415210700204660ustar00rootroot00000000000000#!/bin/bash # vim: et sts=4 sw=4 set -e set -u DOCKERFILE= # $1 GITLAB_REGISTRY=registry.gitlab.com GITLAB_NAMESPACE=goodvibes GITLAB_PROJECT=goodvibes ## utils usage() { local status=$1 (( $status != 0 )) && exec >&2 echo "Usage: $(basename $0) DOCKERFILE" echo echo "Build a container image. For example:" echo echo " export http_proxy=http://localhost:3142" echo " $(basename $0) .gitlab-ci/Dockerfile.debian" echo exit $status } fail() { echo >&2 "$@" exit 1 } make_image_tag() { local dockerfile=$1 local from= from=$(sed -n 's/^FROM *//p' "$dockerfile") [ "$from" ] || return # https://docs.gitlab.com/ee/user/packages/container_registry/#image-naming-convention echo "$GITLAB_REGISTRY/$GITLAB_NAMESPACE/$GITLAB_PROJECT/$from" } ## main [ $# -eq 1 ] || usage 1 DOCKERFILE=$1 [ -e "$DOCKERFILE" ] || fail "Dockerfile '$DOCKERFILE' does not exist" IMAGE_TAG=$(make_image_tag "$DOCKERFILE") [ "$IMAGE_TAG" ] || fail "Failed to make image tag from dockerfile '$DOCKERFILE'" WORKDIR=$(dirname "$DOCKERFILE") DOCKERFILE=$(basename "$DOCKERFILE") pushd "$WORKDIR" >/dev/null podman build \ --tag "$IMAGE_TAG" \ --file "$DOCKERFILE" \ . popd >/dev/null cat << EOF ---------------- Now you might want to push this image to the registry: podman login $GITLAB_REGISTRY podman push $IMAGE_TAG podman logout $GITLAB_REGISTRY For an overview of the images present in the registry: https://gitlab.com/goodvibes/goodvibes/container_registry EOF goodvibes-v0.7.2/scripts/build-png-images.sh000077500000000000000000000046761414415210700210550ustar00rootroot00000000000000#!/bin/bash # vim: et sts=4 sw=4 # Rebuild png images set -e set -u APPID=io.gitlab.Goodvibes SVGDIR=data/icons/src ICONDIR=data/icons/hicolor SITEDIR=docs/goodvibes.readthedocs.io usage() { local status=$1 (( $status != 0 )) && exec >&2 echo "Usage: $(basename $0) TARGET..." echo echo "Build PNG images from their SVG sources." echo "Targets can be: all, icons, site" echo exit $status } fail() { echo >&2 "$@" exit 1 } step() { echo "$(tput bold)âµ $@$(tput sgr0)" } checkcmds() { local cmd= while (( $# )); do cmd=$1; shift command -v $cmd >/dev/null 2>&1 && continue fail "Command '$cmd' is not installed, aborting" done } checkdirs() { local dir= while (( $# )); do dir=$1; shift [ -d "$dir" ] && continue fail "Directory '$dir' does not exist, aborting" done } do_icons() { step 'Building small icons' for size in 16 22 24 32 48; do inkscape \ --export-area-page \ --export-width $size \ --export-png $ICONDIR/${size}x${size}/apps/$APPID.png \ $SVGDIR/goodvibes-small.svg done step 'Building large icons' for size in 256 512; do inkscape \ --export-area-page \ --export-width $size \ --export-png $ICONDIR/${size}x${size}/apps/$APPID.png \ $SVGDIR/goodvibes-large.svg done step 'Copying symbolic icons' cp -v $SVGDIR/goodvibes-symbolic.svg $ICONDIR/symbolic/apps/$APPID-symbolic.svg } do_site() { local tmpdir= tmpdir=$(mktemp --directory --tmpdir=$(pwd) favicon.XXXXXX) trap "rm -fr $tmpdir" EXIT step 'Building favicon' for size in 16 24 32 48 64; do inkscape \ --export-area-page \ --export-width $size \ --export-png $tmpdir/$size.png \ $SVGDIR/goodvibes-favicon.svg done convert $tmpdir/*.png $SITEDIR/images/favicon.ico identify $SITEDIR/images/favicon.ico step 'Building logo' inkscape \ --export-area-page \ --export-width 192 \ --export-png $SITEDIR/images/goodvibes.png \ $SVGDIR/goodvibes-large.svg } checkcmds convert identify inkscape checkdirs "$SVGDIR" "$ICONDIR" "$SITEDIR" (( $# )) || usage 0 while (( $# )); do case "$1" in (all) do_icons; do_site; ;; (icons) do_icons ;; (site) do_site ;; (--help) usage 0 ;; (*) usage 1 ;; esac shift done goodvibes-v0.7.2/scripts/code/000077500000000000000000000000001414415210700162675ustar00rootroot00000000000000goodvibes-v0.7.2/scripts/code/gv-object-make.sh000077500000000000000000000112651414415210700214260ustar00rootroot00000000000000#!/bin/bash -e type="$1" objname="$2" parentname="$3" print_usage() { echo "Usage: $(basename $0) []" echo echo "Create a new goodvibes object in './src'," echo "that's to say a .c and a .h skeleton file." echo echo "Parameters:" echo " might be 'core', 'base', 'ui', 'feat'." echo " should contain only lowercase, digits and dashes, and start with 'gv-'." echo " should contain only lowercase, digits and dashes." echo echo "Example:" echo " $0 ui gv-about-dialog gtk-about-dialog" } name_get_invalid_chars() { # Keep only invalid chars tr -d 'a-z0-9-' <<< "$1" } name_to_lower() { # To lowercase, '-' to '_' sed -e 's/-/_/g' <<< "$1" } name_to_upper() { # To uppercase, '-' to '_' sed -e 's/-/_/g' -e 's/\([a-z]\)/\U\1/g' <<< "$1" } name_to_camel() { # To camelcase, '-' removed sed -e 's/-\([a-z]\)/\U\1/g' -e 's/^\([a-z]\)/\U\1/' <<< "$1" } # -------------------------------------------------------- # # Check and make everything ready # # -------------------------------------------------------- # # Check for help arguments [ "$1" == "-h" -o "$1" == "--help" ] && \ { print_usage; exit 0; } # Ensure we're in the right directory [ -d "src" ] || \ { echo >&2 "Please run from project root directory"; exit 1; } # Sanity check on [ -z "$objname" ] && \ { print_usage; exit 1; } grep -q '^gv-' <<< "$objname" || \ { echo >&2 "'$objname' should start with 'gv-' prefix"; exit 1; } [ -z "$(name_get_invalid_chars "$objname")" ] || \ { echo >&2 "'$objname' contains invalid characters"; exit 1; } # Notice that has no 'gv-' prefix from now on. # We explicitely add it when needed. objname="$(sed 's/^gv-//' <<< $objname)" # -------------------------------------------------------- # # Copy skeleton files to source directory # # -------------------------------------------------------- # # Select source depending on the type srcdir="scripts/code/gv-object-templates" srcfile="" case "$type" in core|base|ui) srcfile=gv-dummy;; feat) srcfile=gv-feature-dummy;; esac [ -z "$srcfile" ] && \ { print_usage; exit 1; } # Ensure that destination files don't exist dstdir="" case "$type" in core) dstdir="src/core";; base) dstdir="src/base";; ui) dstdir="src/ui";; feat) dstdir="src/feat";; esac [ -z "$dstdir" ] && \ { print_usage; exit 1; } dstfile=gv-$objname [ -e $dstdir/$dstfile.c ] && { echo >&2 "$dstdir/$dstfile.c already exists"; exit 1; } [ -e $dstdir/$dstfile.h ] && { echo >&2 "$dstdir/$dstfile.h already exists"; exit 1; } # Copy files cp "$srcdir/$srcfile.c" "$dstdir/$dstfile.c" cp "$srcdir/$srcfile.h" "$dstdir/$dstfile.h" # -------------------------------------------------------- # # String substitutions # # -------------------------------------------------------- # # Customization for ui files if [ "$type" == "ui" ]; then # Replace 'core' by 'ui' in the include path sed -i \ -e "s|core/gv-dummy|ui/gv-dummy|" \ $dstdir/$dstfile.c fi # Customization for base files if [ "$type" == "base" ]; then # Replace 'core' by 'base' in the include path sed -i \ -e "s|core/gv-dummy|base/gv-dummy|" \ $dstdir/$dstfile.c fi # Special customization for ui files if [ "$type" == "ui" ]; then # Add gtk include sed -i \ -e "//a #include " \ $dstdir/$dstfile.c $dstdir/$dstfile.h # Return a GtkWidget sed -i \ -e "s/^GvDummy \*/GtkWidget */"\ $dstdir/$dstfile.c $dstdir/$dstfile.h fi # Replace 'gobject' and variants by another parent if [ -n "$parentname" ]; then upper="$(name_to_upper $parentname)" upper_pfx="$(cut -d_ -f1 <<< $upper)" upper_end="$(cut -d_ -f2- <<< $upper)" camel="$(name_to_camel $parentname)" sed -i \ -e "s/GObject parent_instance/$camel parent_instance/" \ -e "/^G_DEFINE/s/G_TYPE_OBJECT/${upper_pfx}_TYPE_${upper_end}/" \ $dstdir/$dstfile.c sed -i \ -e "/^G_DECLARE/s/GObject/$camel/" \ $dstdir/$dstfile.h fi # Replace 'dummy' and variants by lower="$(name_to_lower $objname)" upper="$(name_to_upper $objname)" camel="$(name_to_camel $objname)" sed -i \ -e "s/gv-dummy/$dstfile/g" \ -e "s/gv_dummy/gv_$lower/g" \ -e "s/DUMMY/$upper/g" \ -e "s/Dummy/$camel/g" \ -e "s/dummy/$lower/g" \ $dstdir/$dstfile.c $dstdir/$dstfile.h # Fix things ./scripts/code/standard-header.sh add $dstdir/$dstfile.c $dstdir/$dstfile.h # Done! echo "Skeleton files '$dstdir/$dstfile.[ch]' created." echo "Don't forget to add these new files to the meson build file'." goodvibes-v0.7.2/scripts/code/gv-object-templates/000077500000000000000000000000001414415210700221435ustar00rootroot00000000000000goodvibes-v0.7.2/scripts/code/gv-object-templates/gv-dummy.c000066400000000000000000000054321414415210700240600ustar00rootroot00000000000000#include #include #include "base/glib-object-additions.h" #include "base/log.h" #include "core/gv-dummy.h" /* * Properties */ enum { /* Reserved */ PROP_0, /* Properties */ // TODO fill with your properties /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * Signals */ /* * GObject definitions */ struct _GvDummyPrivate { // TODO fill with your data }; typedef struct _GvDummyPrivate GvDummyPrivate; struct _GvDummy { /* Parent instance structure */ GObject parent_instance; /* Private data */ GvDummyPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvDummy, gv_dummy, G_TYPE_OBJECT) /* * Helpers */ /* * Signal handlers & callbacks */ /* * Property accessors */ static void gv_dummy_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvDummy *self = GV_DUMMY(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); // TODO handle properties (void) self; (void) value; switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_dummy_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvDummy *self = GV_DUMMY(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); // TODO handle properties (void) self; (void) value; switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public methods */ GvDummy * gv_dummy_new(void) { return g_object_new(GV_TYPE_DUMMY, NULL); } /* * GObject methods */ static void gv_dummy_finalize(GObject *object) { GvDummy *self = GV_DUMMY(object); GvDummyPrivate *priv = self->priv; TRACE("%p", object); // TODO job to be done (void) priv; /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_dummy, object); } static void gv_dummy_constructed(GObject *object) { GvDummy *self = GV_DUMMY(object); GvDummyPrivate *priv = self->priv; TRACE("%p", object); /* Initialize properties */ // TODO (void) priv; /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_dummy, object); } static void gv_dummy_init(GvDummy *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_dummy_get_instance_private(self); } static void gv_dummy_class_init(GvDummyClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_dummy_finalize; object_class->constructed = gv_dummy_constructed; /* Properties */ object_class->get_property = gv_dummy_get_property; object_class->set_property = gv_dummy_set_property; // TODO define your properties here // use GV_PARAM_{READABLE,WRITABLE,READWRITE} g_object_class_install_properties(object_class, PROP_N, properties); } goodvibes-v0.7.2/scripts/code/gv-object-templates/gv-dummy.h000066400000000000000000000004041414415210700240570ustar00rootroot00000000000000#pragma once #include /* GObject declarations */ #define GV_TYPE_DUMMY gv_dummy_get_type() G_DECLARE_FINAL_TYPE(GvDummy, gv_dummy, GV, DUMMY, GObject) /* Data types */ /* Methods */ GvDummy *gv_dummy_new(void); /* Property accessors */ goodvibes-v0.7.2/scripts/code/gv-object-templates/gv-feature-dummy.c000066400000000000000000000021711414415210700255060ustar00rootroot00000000000000#include #include #include "base/gv-feature.h" #include "base/log.h" #include "core/gv-core.h" #include "feat/gv-dummy.h" /* * GObject definitions */ struct _GvDummy { /* Parent instance structure */ GvFeature parent_instance; }; G_DEFINE_TYPE(GvDummy, gv_dummy, GV_TYPE_FEATURE) /* * Helpers */ /* * Feature methods */ static void gv_dummy_disable(GvFeature *feature) { GvPlayer *player = gv_core_player; // FILL THAT /* Chain up */ GV_FEATURE_CHAINUP_DISABLE(gv_dummy, feature); } static void gv_dummy_enable(GvFeature *feature) { GvPlayer *player = gv_core_player; /* Chain up */ GV_FEATURE_CHAINUP_ENABLE(gv_dummy, feature); // FILL THAT /* Signal handlers */ // g_signal_connect_object(player, "blabla", on_blabla, feature, 0); } /* * GObject methods */ static void gv_dummy_init(GvDummy *self) { TRACE("%p", self); } static void gv_dummy_class_init(GvDummyClass *class) { GvFeatureClass *feature_class = GV_FEATURE_CLASS(class); TRACE("%p", class); /* Override GvFeature methods */ feature_class->enable = gv_dummy_enable; feature_class->disable = gv_dummy_disable; } goodvibes-v0.7.2/scripts/code/gv-object-templates/gv-feature-dummy.h000066400000000000000000000003131414415210700255070ustar00rootroot00000000000000#pragma once #include #include "base/gv-feature.h" /* GObject declarations */ #define GV_TYPE_DUMMY gv_dummy_get_type() G_DECLARE_FINAL_TYPE(GvDummy, gv_dummy, GV, DUMMY, GvFeature) goodvibes-v0.7.2/scripts/code/standard-header.sh000077500000000000000000000050461414415210700216610ustar00rootroot00000000000000#!/bin/bash -e SPDX_LICENSE_ID=GPL-3.0-only print_usage() { cat << EOF Usage: $(basename $0) [...] Tool to check/add standard headers in C files. Commands: check Check if source files have a standard header. add If standard header is missing, add it. EOF } git_is_tracked() { local file="$1" # Fill list of tracked files only once if [ $GIT_TRACKED_FILLED -eq 0 ]; then GIT_TRACKED="$(git ls-files)" GIT_TRACKED_FILLED=1 #echo "--------" #echo "$GIT_TRACKED" #echo "--------" fi # Remove leading './' from filename if any file="$(echo $file | sed 's:^\.\/::')" echo "$GIT_TRACKED" | grep -q "$file" } unit_name() { echo "Goodvibes Radio Player" } do_check() { local file="$1" local unit="$(unit_name "$file")" head -n 4 "$file" | tr -d '\n' | \ grep -q -F "/* * $unit * * Copyright (C)" } do_add() { local file="$1" local unit="$(unit_name "$file")" cat << EOF > "$file.tmp" /* * $unit * * Copyright (C) $(date +%Y) $(git config user.name) * * SPDX-License-Identifier: ${SPDX_LICENSE_ID:?} * * 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, version 3. * * 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 . */ $(cat $file) EOF mv "$file.tmp" "$file" } # Check for help arguments [ "$1" == "-h" -o "$1" == "--help" ] && \ { print_usage; exit 0; } # Check for proper usage [ $# -lt 1 ] && \ { print_usage; exit 1; } # Ensure we're in the right directory [ -d "src" ] || \ { echo >&2 "Please run from root directory"; exit 1; } # File list if [ $# -eq 1 ]; then filestmp=$(find src -name \*.[ch]) files="" for file in $filestmp; do if ! git_is_tracked $file; then continue fi files+="$file\n" done else files="${@:2}" fi # Do the job case $1 in check) for f in $files; do if ! do_check $f; then echo "'$f': no standard header found." fi done ;; add) for f in $files; do if do_check $f; then continue; fi echo "'$f': adding standard header." do_add $f done ;; *) print_usage exit 1 ;; esac goodvibes-v0.7.2/scripts/meson/000077500000000000000000000000001414415210700164765ustar00rootroot00000000000000goodvibes-v0.7.2/scripts/meson/install-git-hook.sh000077500000000000000000000003641414415210700222250ustar00rootroot00000000000000#!/bin/bash set -e set -u cd "$MESON_SOURCE_ROOT" [ -f .git/hooks/pre-commit.sample ] || exit 0 [ ! -f .git/hooks/pre-commit ] || exit 0 install -m0755 .git/hooks/pre-commit.sample .git/hooks/pre-commit echo 'Git pre-commit hook installed' goodvibes-v0.7.2/scripts/meson/post-install.sh000077500000000000000000000010701414415210700214640ustar00rootroot00000000000000#!/bin/sh set -e DATADIR="$1" # Package managers set this so we don't need to run if [ "$DESTDIR" ]; then exit 0; fi cmdcheck() { command -v "$1" >/dev/null 2>&1; } if cmdcheck glib-compile-schemas; then echo "Compiling GSettings schemas..." glib-compile-schemas "$DATADIR/glib-2.0/schemas" fi if cmdcheck gtk-update-icon-cache; then echo "Updating icon cache..." gtk-update-icon-cache -t -f "$DATADIR/icons/hicolor" fi if cmdcheck update-desktop-database; then echo "Updating desktop database..." update-desktop-database "$DATADIR/applications" fi goodvibes-v0.7.2/scripts/meson/validate-ui-file.sh000077500000000000000000000005221414415210700221550ustar00rootroot00000000000000#!/bin/sh set -e UIFILE="$1" SKIP=77 # the Meson test harness recognises exit code 77 as skip cmdcheck() { command -v "$1" >/dev/null 2>&1; } # Skip test if there's no graphics [ "$DISPLAY" ] || exit $SKIP # Skip test if gtk-builder-tool is not installed cmdcheck gtk-builder-tool || exit $SKIP gtk-builder-tool validate "$UIFILE" goodvibes-v0.7.2/scripts/print-maintainers.sh000077500000000000000000000103171414415210700213620ustar00rootroot00000000000000#!/bin/bash # vim: et sts=4 sw=4 # Print the list of maintainers for distro packages set -e set -u ## repology backend - only gives email addresses though REPOLOGY_API_URL=https://repology.org/api/v1 REPOLOGY_QUERY_URL=$REPOLOGY_API_URL/project/goodvibes REPOLOGY_JSON= # set below repology_get_maintainers() { local target=$1 local repo= local maint= repo=$(echo "$REPOLOGY_JSON" | jq ".[] | select(.repo == \"$target\")") if [ -z "$repo" ]; then echo "repo not found" return fi maint=$(echo "$repo" | jq '.maintainers') if [ "$maint" == "null" ]; then echo "maintainers not found" return fi echo "$maint" | jq -r '.[]' # echo "$JSON" | jq -r ".[] | select(.repo == \"$target\") | .maintainers | .[]" } repology_query() { REPOLOGY_JSON=$(wget -q -O- $REPOLOGY_QUERY_URL) echo " * $(repology_get_maintainers aur) - Archlinux" echo " * $(repology_get_maintainers debian_unstable) - Debian" echo " * $(repology_get_maintainers fedora_rawhide) - Fedora" echo " * $(repology_get_maintainers opensuse_multimedia_apps_tumbleweed) - openSUSE" } ## http backend AUR_URL=https://aur.archlinux.org http_get_aur_maintainer() { local pkgbuild= local maint= pkgbuild=$(wget -q -O- "$AUR_URL/cgit/aur.git/plain/PKGBUILD?h=goodvibes") maint=$(echo "$pkgbuild" | sed -n 's/^# Maintainer: *//p') echo "$maint" } DEBIAN_URL=https://salsa.debian.org http_get_debian_maintainer() { local control= local maint= control=$(wget -q -O- "$DEBIAN_URL/debian/goodvibes/raw/master/debian/control") maint=$(echo "$control" | sed -n 's/Maintainer: *//p') echo "$maint" } FEDORA_URL=https://src.fedoraproject.org http_get_fedora_maintainer() { # Changelog headline, as found in the .spec file: # * Thu Feb 06 22:29:07 CET 2020 Robert-André Mauchin - 0.5.1-1 local spec= local changelog= local latest_entry= local maint= spec=$(wget -q -O- "$FEDORA_URL/rpms/goodvibes/raw/master/f/goodvibes.spec") changelog=$(echo "$spec" | sed -n '/%changelog/,$ p') latest_entry=$(echo "$changelog" | grep '^*' | head -1) maint=$(echo "$latest_entry" | cut -d' ' -f 8- | rev | sed 's/^.* - //' | rev) echo "$maint" } OPENSUSE_URL=http://download.opensuse.org http_get_opensuse_maintainer() { # Changelog headline, as displayed per the rpm command: # * Thu Sep 26 2019 Alexei Podvalsky local tmpdir= local rpmpkg= local changelog= local latest_entry= local maint tmpdir=$(mktemp -d) pushd "$tmpdir" >/dev/null trap "popd >/dev/null && rm -fr $tmpdir" EXIT wget \ --quiet \ --recursive \ --level=1 \ --no-parent \ --no-directories \ --accept-regex 'goodvibes-.*\.src\.rpm$' \ "$OPENSUSE_URL/repositories/multimedia:/apps/openSUSE_Tumbleweed/src/" rpmpkg=$(ls -1 *.rpm) changelog=$(rpm -q --changelog $rpmpkg 2>/dev/null) latest_entry=$(echo "$changelog" | grep '^*' | head -1) maint=$(echo "$latest_entry" | cut -d' ' -f 6-) trap - EXIT popd > /dev/null rm -fr "$tmpdir" echo "$maint" } VOID_URL=https://raw.githubusercontent.com/void-linux/void-packages http_get_void_maintainer() { local template= local maint= template=$(wget -q -O- "$VOID_URL/master/srcpkgs/goodvibes/template") maint=$(echo "$template" | sed -n 's/^maintainer=//p') maint=$(echo "$maint" | sed -e 's/^"*//' -e 's/"*$//') if echo "$maint" | grep -q 'orphan@voidlinux.org'; then # package doesn't have a maintainer return fi echo "$maint" } http_query() { local maint= maint=$(http_get_aur_maintainer) [ -n "$maint" ] && echo " * $maint - Arch Linux" maint=$(http_get_debian_maintainer) [ -n "$maint" ] && echo " * $maint - Debian" maint=$(http_get_fedora_maintainer) [ -n "$maint" ] && echo " * $maint - Fedora" maint=$(http_get_opensuse_maintainer) [ -n "$maint" ] && echo " * $maint - openSUSE" maint=$(http_get_void_maintainer) [ -n "$maint" ] && echo " * $maint - Void Linux" } ## helpers strip_email() { sed 's/<.*@.*> *//' } ## main #repology_query http_query | strip_email goodvibes-v0.7.2/scripts/print-translators.sh000077500000000000000000000112321414415210700214210ustar00rootroot00000000000000#!/bin/bash # vim: et sts=4 sw=4 # Print the list of translators set -e set -u # translators that are discarded, either by names or email addresses BLACKLIST=( "anonymous" "Arnaud Rebillout" "vistausss@outlook.com" # dup email address ) # this is the minimum number of additions that a translator # must have made to be part of the credits. MIN_LINES_ADDED=20 LCS=() LANGS=() NAMES=() EMAILS=() fail() { echo >&2 "$@" exit 1 } usage() { echo "$0 " exit 1 } git_get_lines_added() { # get the number of additions by an author on a file # https://stackoverflow.com/a/7010890/776208 local file=$1 local name=$2 git log --author="$name" --pretty=tformat: --numstat "$file" \ | awk '{ add += $1 } END { printf "%s\n", add }' } git_get_authors() { # get the list of authors for a given file # output: name${sep}email local file=$1 local sep=$2 git log --pretty=format:"%an${sep}%ae" "$file" | sort -u } git_get_names() { # get the list of authors for a given file # output: names local file=$1 git log --pretty=format:"%an" "$file" | sort -u } get_translators_git() { # get the list of translators from git logs local file=$1 local lc= local lang= local delete_names= local names= local name= local authors= local emails= local i= # get lc and language from po file lc=$(basename "$file" | cut -d'.' -f1) lang=$(sed -En 's/"Language-Team: (.*) <.*/\1/p' "$file") # create the blacklist, aka names of authors we don't keep delete_names=("${BLACKLIST[@]}") mapfile -t names < <(git_get_names "$file") for name in "${names[@]}"; do local lines_added= lines_added=$(git_get_lines_added "$file" "$name") if [[ $lines_added -lt $MIN_LINES_ADDED ]]; then delete_names+=("$name") fi done # debugging? uncomment below #printf "[%-8s]: " $(basename $file) #declare -p delete_names # get the list of authors, and filter it out authors=$(git_get_authors "$file" "|") for name in "${delete_names[@]}"; do authors=$(echo "$authors" | grep -v "$name") done # move the results to arrays mapfile -t names < <(echo "$authors" | cut -d'|' -f1) mapfile -t emails < <(echo "$authors" | cut -d'|' -f2) #declare -p names #declare -p emails # add results to global arrays for ((i=0; i<${#names[@]}; i++)); do # capitalize 1st letter of each word # https://stackoverflow.com/a/1538818/776208 name=$(echo ${names[$i]} | sed 's/\b\(.\)/\u\1/g') LCS+=("$lc") LANGS+=("$lang") NAMES+=("$name") EMAILS+=("${emails[$i]}") done } get_translators_po_file() { # get the list of translators from po files -- DEPRECATED # This method can only output the last person who authored, which # is not representative of the work that was done on the po file. local file=$1 lc=$(basename $file | cut -d'.' -f1) line=$(grep '^"Language-Team' $file) value=$(echo $line | cut -d':' -f2- | sed 's/^ *//') lang=$(echo $value | cut -d'<' -f1 | sed 's/ *$//') line=$(grep '^"Last-Translator' $file) value=$(echo $line | cut -d':' -f2- | sed 's/^ *//') name=$(echo $value | cut -d'<' -f1 | sed 's/ *$//') email=$(echo $value | cut -d'<' -f2- | cut -d'>' -f1) LCS+=("${lc:?}") LANGS+=("${lang:?}") NAMES+=("${name:?}") EMAILS+=("${email:?}") #echo "$name <$email> ($lc) ($lang)" } get_translators() { # get the list of translators (populate global variables) local file= for file in po/*.po; do get_translators_git "$file" #get_translators_po_file "$file" done } output_code() { # C-style output, to be pasted directly in the code local len=${#NAMES[@]} for ((i=0; i<$len; ++i)); do local lc=${LCS[$i]} local lang=${LANGS[$i]} local name=${NAMES[$i]} local email=${EMAILS[$i]} [ $i -lt $(expr $len - 1) ] \ && echo -e "\t\"$name <$email> - $lang ($lc)\\\n\"" \ || echo -e "\t\"$name <$email> - $lang ($lc)\";" done } output_doc() { # RST-style output, to be pasted directly in the doc local len=${#NAMES[@]} for ((i=0; i<$len; ++i)); do local lc=${LCS[$i]} local lang=${LANGS[$i]} local name=${NAMES[$i]} local email=${EMAILS[$i]} echo " * $name - $lang ($lc)" done } [ $# -eq 1 ] || usage [ -d "po" ] || fail "'po' directory not found." get_translators case "$1" in code) echo "-------- 8< --------" output_code echo "-------- >8 --------" ;; doc) echo "-------- 8< --------" output_doc echo "-------- >8 --------" ;; *) usage ;; esac goodvibes-v0.7.2/scripts/run-ci-image.sh000077500000000000000000000005601414415210700201720ustar00rootroot00000000000000#!/bin/bash # vim: et sts=4 sw=4 set -e set -u fail() { echo >&2 "$@"; exit 1; } [ $# -eq 1 ] || fail "Usage: $(basename $0) IMAGE" IMAGE=$1 podman run \ --rm \ --tty \ --interactive \ --hostname goodvibes-builder \ --user root:root \ --volume $(pwd):/home/builder/goodvibes \ --workdir /home/builder/goodvibes \ $IMAGE \ bash goodvibes-v0.7.2/scripts/test/000077500000000000000000000000001414415210700163345ustar00rootroot00000000000000goodvibes-v0.7.2/scripts/test/hotkey.sh000077500000000000000000000005701414415210700202000ustar00rootroot00000000000000#!/bin/sh command -v xdotool >/dev/null 2>&1 || \ { echo >&2 "Please install xdotool."; exit 1; } case $1 in play) key=XF86AudioPlay ;; pause) key=XF86AudioPause ;; stop) key=XF86AudioStop ;; prev|previous) key=XF86AudioPrev ;; next) key=XF86AudioNext ;; *) echo >&2 "Usage: $0 " exit 1 ;; esac xdotool key $key goodvibes-v0.7.2/scripts/test/inhibited.sh000077500000000000000000000015741414415210700206410ustar00rootroot00000000000000#!/bin/bash # qdbus syntax : qdbus org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.IsInhibited 4 # dbusctl syntax: busctl --user call org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager IsInhibited u 4 set -eu fail() { echo >&2 "$@"; exit 1; } checkcmd() { command -v "$1" >/dev/null 2>&1; } assertcmd() { checkcmd "$1" || fail "'$1' is not installed"; } checkdbusname() { qdbus "$1" >/dev/null 2>&1; } assertcmd qdbus if checkdbusname org.gnome.SessionManager; then qdbus \ org.gnome.SessionManager \ /org/gnome/SessionManager \ org.gnome.SessionManager.IsInhibited 4 elif checkdbusname org.freedesktop.PowerManagement; then qdbus \ org.freedesktop.PowerManagement \ /org/freedesktop/PowerManagement/Inhibit \ org.freedesktop.PowerManagement.Inhibit.HasInhibit else fail "No D-Bus service found" fi goodvibes-v0.7.2/scripts/test/mpris2.sh000077500000000000000000000042501414415210700201100ustar00rootroot00000000000000#!/bin/bash MPRIS_OBJ='org.mpris.MediaPlayer2.Goodvibes' MPRIS_PATH='/org/mpris/MediaPlayer2' MPRIS_IFACE='org.mpris.MediaPlayer2' print_usage() { echo "Usage: $0 [options]" echo "" echo "Commands:" echo " introspect" echo " call [type:arg] [type:arg] ..." echo " get " echo " set " echo " watch-signals" echo "" echo "Examples:" echo " $0 call Player Play" echo " $0 call TrackList GoTo objpath:\"/io/gitlab/Goodvibes/TrackList/0x55be33e73a60\"" echo " $0 call TrackList GetTracksMetadata array:objpath:\"/io/gitlab/Goodvibes/TrackList/0x56502c272620\",\"/io/gitlab/Goodvibes/TrackList/0x56502c272660\"" } iface_real() { SFX="" case $1 in Root) SFX="";; Player|TrackList) SFX=".$1";; *) echo >&2 "Wrong mpris iface" exit 1 esac echo $SFX } case $1 in introspect) dbus-send --print-reply=literal \ --dest=$MPRIS_OBJ \ $MPRIS_PATH \ org.freedesktop.DBus.Introspectable.Introspect | tr \" \' ;; call) IFACE=$2 METHOD=$3 dbus-send --print-reply=literal \ --type=method_call \ --dest=$MPRIS_OBJ \ $MPRIS_PATH \ $MPRIS_IFACE$(iface_real $IFACE).$METHOD \ ${@:4} ;; get) IFACE=$2 PROP=$3 dbus-send --print-reply=literal \ --dest=$MPRIS_OBJ \ $MPRIS_PATH \ org.freedesktop.DBus.Properties.Get \ string:"$MPRIS_IFACE$(iface_real $IFACE)" \ string:"$PROP" ;; set) IFACE=$2 PROP=$3 TYPEVALUE=$4 dbus-send --print-reply=literal \ --dest=$MPRIS_OBJ \ $MPRIS_PATH \ org.freedesktop.DBus.Properties.Set \ string:"$MPRIS_IFACE$(iface_real $IFACE)" \ string:"$PROP" variant:$TYPEVALUE ;; watch-signals) dbus-monitor "type='signal',sender='$MPRIS_OBJ'" ;; *) print_usage exit 1 ;; esac goodvibes-v0.7.2/scripts/update-copyright-years.sh000077500000000000000000000016641414415210700223340ustar00rootroot00000000000000#!/bin/bash # vim: et sts=4 sw=4 # Update the years for the copyright statements fail() { echo >&2 "$@" exit 1 } git diff-files --quiet || \ fail "Working tree has unstaged changes" git diff-index --quiet --cached HEAD -- || \ fail "Working tree has staged changes" TRACKED_FILES=$(git ls-tree -r HEAD --name-only) INTERESTING_FILES=() # AFAIK, all of this will fail miserably # if ever a filename contains a space... for f in $TRACKED_FILES; do case "$f" in (COPYING) continue ;; (*.po) continue ;; (*.ico) continue ;; (*.png) continue ;; (*.svg) continue ;; (*/font-mfizz/*) continue ;; esac INTERESTING_FILES+=("$f") done #echo "${INTERESTING_FILES[@]}" | tr ' ' '\n' YEAR=$(date +%Y) sed -i -E \ -e "s/(Copyright \(C\) 20[0-9][0-9])-(20[0-9][0-9])/\1-$YEAR/g" \ -e "s/(Copyright \(C\) 20[0-9][0-9]) /\1-$YEAR /g" \ ${INTERESTING_FILES[@]} goodvibes-v0.7.2/scripts/valgrind.sh000077500000000000000000000016661414415210700175330ustar00rootroot00000000000000#!/bin/bash # https://stackoverflow.com/a/4256967 set -e set -u LOGFILE=valgrind-$(date +%s) echo "Valgrind log file: $LOGFILE" echo "Starting up, please be patient..." export G_DEBUG=gc-friendly export G_SLICE=always-malloc valgrind \ --leak-check=yes \ --log-file=$LOGFILE \ --trace-children=yes \ --suppressions=/usr/share/gtk-3.0/valgrind/gtk.supp \ --suppressions=/usr/share/glib-2.0/valgrind/glib.supp \ --suppressions=$HOME/src/gnome/libsoup/tests/libsoup.supp \ --suppressions=$HOME/src/fdo/gstreamer/tests/check/gstreamer.supp \ --suppressions=$HOME/src/fdo/gst-plugins-bad/tests/check/gst-plugins-bad.supp \ --suppressions=$HOME/src/fdo/gst-plugins-base/tests/check/gst-plugins-base.supp \ --suppressions=$HOME/src/fdo/gst-plugins-good/tests/check/gst-plugins-good.supp \ ./goodvibes-launcher.sh "$@" # Remove pid, so that it's easy to compare two log files cut -d' ' -f2- $LOGFILE > $LOGFILE.tmp mv $LOGFILE.tmp $LOGFILE goodvibes-v0.7.2/src/000077500000000000000000000000001414415210700144555ustar00rootroot00000000000000goodvibes-v0.7.2/src/base/000077500000000000000000000000001414415210700153675ustar00rootroot00000000000000goodvibes-v0.7.2/src/base/glib-additions.c000066400000000000000000000050551414415210700204310ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include /* * Version Information */ const gchar * glib_get_runtime_version_string(void) { static gchar *version_string; if (version_string == NULL) { version_string = g_strdup_printf("GLib %u.%u.%u", glib_major_version, glib_minor_version, glib_micro_version); } return version_string; } const gchar * glib_get_compile_version_string(void) { static gchar *version_string; if (version_string == NULL) { version_string = g_strdup_printf("GLib %u.%u.%u", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION); } return version_string; } /* * String Utilities Functions */ gchar * g_strjoin_null(const gchar *separator, unsigned int n_strings, ...) { gchar *string, *s; va_list args; gsize len; gsize separator_len; gchar *ptr; guint i; if (separator == NULL) separator = ""; separator_len = strlen(separator); /* First part, getting length */ len = 1; va_start(args, n_strings); for (i = 0; i < n_strings; i++) { s = va_arg(args, gchar *); if (s) { if (len != 1) len += separator_len; len += strlen(s); } } va_end(args); /* Second part, building string */ string = g_new(gchar, len); ptr = string; va_start(args, n_strings); for (i = 0; i < n_strings; i++) { s = va_arg(args, gchar *); if (s) { if (ptr != string) ptr = g_stpcpy(ptr, separator); ptr = g_stpcpy(ptr, s); } } va_end(args); if (ptr == string) string[0] = '\0'; return string; } /* * GVariant */ void g_variant_builder_add_dictentry_array_string(GVariantBuilder *b, const gchar *key, ...) { GVariantBuilder ab; va_list ap; gchar *s; g_variant_builder_init(&ab, G_VARIANT_TYPE_ARRAY); va_start(ap, key); while ((s = va_arg(ap, gchar *)) != NULL) g_variant_builder_add(&ab, "s", s); g_variant_builder_add(b, "{sv}", key, g_variant_builder_end(&ab)); } goodvibes-v0.7.2/src/base/glib-additions.h000066400000000000000000000030001414415210700204220ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include /* * Version Information */ const gchar *glib_get_runtime_version_string(void); const gchar *glib_get_compile_version_string(void); /* * String Utilities Functions */ gchar *g_strjoin_null(const gchar *separator, unsigned int n_strings, ...); /* * GVariant */ #define g_variant_builder_add_dictentry_string(b, key, val) \ g_variant_builder_add(b, "{sv}", key, g_variant_new_string(val)) #define g_variant_builder_add_dictentry_object_path(b, key, val) \ g_variant_builder_add(b, "{sv}", key, g_variant_new_object_path(val)) void g_variant_builder_add_dictentry_array_string(GVariantBuilder *b, const gchar *key, ...) G_GNUC_NULL_TERMINATED; goodvibes-v0.7.2/src/base/glib-object-additions.c000066400000000000000000000056611414415210700217000ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "glib-object-additions.h" /* * GType */ gchar * g_type_dup_name(GType type) { return g_strdup(g_type_name(type)); } gchar * g_type_dup_name_no_prefix(GType type) { const gchar *type_name; const gchar *ptr; type_name = g_type_name(type); ptr = type_name; if (*ptr != '\0') ptr++; while (*ptr != '\0') { if (g_ascii_isupper(*ptr)) break; ptr++; } if (*ptr == '\0') return g_strdup(type_name); else return g_strdup(ptr); } /* * GObject */ const gchar * g_object_get_property_desc(GObject *object, const gchar *property_name) { GParamSpec *pspec; const gchar *desc; pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), property_name); g_assert_nonnull(pspec); desc = g_param_spec_get_blurb(pspec); g_assert_nonnull(desc); return desc; } void g_object_get_property_uint_bounds(GObject *object, const gchar *property_name, guint *minimum, guint *maximum) { GParamSpec *pspec; GParamSpecUInt *pspec_uint; if (minimum) *minimum = 0; if (maximum) *maximum = 0; pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), property_name); g_assert_nonnull(pspec); g_assert_true(G_IS_PARAM_SPEC_UINT(pspec)); pspec_uint = G_PARAM_SPEC_UINT(pspec); if (minimum) *minimum = pspec_uint->minimum; if (maximum) *maximum = pspec_uint->maximum; } /* * Signals */ void g_signal_handlers_connect_object(gpointer instance, GSignalHandler *handlers, gpointer gobject, GConnectFlags connect_flags) { GSignalHandler *handler; if (handlers == NULL) return; for (handler = handlers; handler->name; handler++) g_signal_connect_object(instance, handler->name, handler->callback, gobject, connect_flags); } void g_signal_handlers_block(gpointer instance, GSignalHandler *handlers, gpointer data) { GSignalHandler *handler; if (handlers == NULL) return; for (handler = handlers; handler->name; handler++) g_signal_handlers_block_by_func(instance, handler->callback, data); } void g_signal_handlers_unblock(gpointer instance, GSignalHandler *handlers, gpointer data) { GSignalHandler *handler; if (handlers == NULL) return; for (handler = handlers; handler->name; handler++) g_signal_handlers_unblock_by_func(instance, handler->callback, data); } goodvibes-v0.7.2/src/base/glib-object-additions.h000066400000000000000000000046501414415210700217020ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include /* * GType */ /* Duplicate type name, eventually without the prefix */ gchar *g_type_dup_name(GType type); gchar *g_type_dup_name_no_prefix(GType type); /* * GObject */ /* Duplicate type name, eventually without the prefix */ #define g_object_dup_type_name(obj) \ g_type_dup_name(G_OBJECT_TYPE(obj)) #define g_object_dup_type_name_no_prefix(obj) \ g_type_dup_name_no_prefix(G_OBJECT_TYPE(obj)) /* Chain up for finalize() */ #define G_OBJECT_CHAINUP_FINALIZE(module_obj_name, obj) \ G_OBJECT_CLASS(module_obj_name##_parent_class)->finalize(obj) /* Chain up for constructed() - beware, constructed() is not guaranteed to exist */ #define G_OBJECT_CHAINUP_CONSTRUCTED(module_obj_name, obj) \ do { \ if (G_OBJECT_CLASS(module_obj_name##_parent_class)->constructed) \ G_OBJECT_CLASS(module_obj_name##_parent_class)->constructed(obj); \ } while (0) const gchar *g_object_get_property_desc (GObject *object, const gchar *property_name); void g_object_get_property_uint_bounds(GObject *object, const gchar *property_name, guint *minimum, guint *maximum); /* * Signals */ struct _GSignalHandler { const gchar *name; GCallback callback; }; typedef struct _GSignalHandler GSignalHandler; void g_signal_handlers_connect_object(gpointer instance, GSignalHandler *handlers, gpointer gobject, GConnectFlags connect_flags); void g_signal_handlers_block (gpointer instance, GSignalHandler *handlers, gpointer data); void g_signal_handlers_unblock (gpointer instance, GSignalHandler *handlers, gpointer data); goodvibes-v0.7.2/src/base/gv-base.c000066400000000000000000000045251414415210700170650ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "log.h" static gboolean initialized = FALSE; /* * Global object list * * This list contains all the global objects, ie. objects which lifetime * last for the whole program duration. It must be populated during * initialization, and accessed only after. */ static GList *object_list; /* Return the list of global objects. This list must be treated as read-only by * the caller, it's not supposed to be modified. */ GList * gv_base_get_objects(void) { /* This should happen only after initialization is complete */ g_assert(initialized == TRUE); return object_list; } /* Register a global object. */ void gv_base_register_object(gpointer data) { GObject *object = G_OBJECT(data); /* This should happen only during initialization, never after */ g_assert(initialized == FALSE); /* Add to the object list (we don't take ownership) */ object_list = g_list_prepend(object_list, object); /* Add a weak pointer, for cleanup checks */ g_object_add_weak_pointer(object, (gpointer *) &(object_list->data)); } void gv_base_cleanup(void) { GList *item; g_assert(initialized == TRUE); /* Objects in list should be empty, thanks to the magic of weak pointers */ for (item = object_list; item; item = item->next) { GObject *object = G_OBJECT(item->data); if (object == NULL) continue; WARNING("Object of type '%s' has not been finalized!", G_OBJECT_TYPE_NAME(object)); } /* Free list */ g_list_free(object_list); } void gv_base_init_completed(void) { g_assert(initialized == FALSE); initialized = TRUE; } void gv_base_init(void) { /* Dummy */ } goodvibes-v0.7.2/src/base/gv-base.h000066400000000000000000000024441414415210700170700ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* _() is defined here */ #include "base/config.h" /* generated by the build system */ #include "base/gv-configurable.h" #include "base/gv-errorable.h" #include "base/gv-feature.h" #include "base/gv-base-enum-types.h" #include "base/gv-param-specs.h" #include "base/log.h" #include "base/uri-schemes.h" #include "base/utils.h" #include "base/vt-codes.h" void gv_base_init (void); void gv_base_init_completed(void); void gv_base_cleanup (void); void gv_base_register_object(gpointer object); GList *gv_base_get_objects (void); goodvibes-v0.7.2/src/base/gv-configurable.c000066400000000000000000000022521414415210700206060ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "gv-configurable.h" #include "log.h" /* * GObject definitions */ G_DEFINE_INTERFACE(GvConfigurable, gv_configurable, G_TYPE_OBJECT) /* * Public methods */ void gv_configurable_configure(GvConfigurable *self) { g_return_if_fail(GV_IS_CONFIGURABLE(self)); return GV_CONFIGURABLE_GET_IFACE(self)->configure(self); } /* * GObject methods */ static void gv_configurable_default_init(GvConfigurableInterface *iface) { TRACE("%p", iface); } goodvibes-v0.7.2/src/base/gv-configurable.h000066400000000000000000000021761414415210700206200ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include /* GObject declarations */ #define GV_TYPE_CONFIGURABLE gv_configurable_get_type() G_DECLARE_INTERFACE(GvConfigurable, gv_configurable, GV, CONFIGURABLE, GObject) struct _GvConfigurableInterface { /* Parent interface */ GTypeInterface parent_iface; /* Virtual methods */ void (*configure) (GvConfigurable *self); }; /* Public methods */ void gv_configurable_configure(GvConfigurable *self); goodvibes-v0.7.2/src/base/gv-errorable.c000066400000000000000000000030751414415210700201270ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "gv-errorable.h" #include "log.h" /* * GObject definitions */ enum { SIGNAL_ERROR, /* Number of signals */ SIGNAL_N }; static guint signals[SIGNAL_N]; G_DEFINE_INTERFACE(GvErrorable, gv_errorable, G_TYPE_OBJECT) /* * Public methods */ void gv_errorable_emit_error(GvErrorable *self, const gchar *fmt, ...) { va_list args; gchar *string; va_start(args, fmt); string = g_strdup_vprintf(fmt, args); va_end(args); g_signal_emit(self, signals[SIGNAL_ERROR], 0, string); g_free(string); } /* * GObject methods */ static void gv_errorable_default_init(GvErrorableInterface *iface) { TRACE("%p", iface); signals[SIGNAL_ERROR] = g_signal_new("error", G_TYPE_FROM_INTERFACE(iface), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GvErrorableInterface, error), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); } goodvibes-v0.7.2/src/base/gv-errorable.h000066400000000000000000000021501414415210700201250ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include /* GObject declarations */ #define GV_TYPE_ERRORABLE gv_errorable_get_type() G_DECLARE_INTERFACE(GvErrorable, gv_errorable, GV, ERRORABLE, GObject) struct _GvErrorableInterface { /* Parent interface */ GTypeInterface parent_iface; /* Signals */ void (*error) (GvErrorable *self); }; /* Methods */ void gv_errorable_emit_error(GvErrorable *self, const gchar *fmt, ...); goodvibes-v0.7.2/src/base/gv-feature.c000066400000000000000000000167361414415210700176150ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "config.h" #include "glib-additions.h" #include "glib-object-additions.h" #include "gv-base-enum-types.h" #include "gv-configurable.h" #include "gv-feature.h" #include "gv-param-specs.h" #include "log.h" #include "utils.h" /* * Properties */ enum { /* Reserved */ PROP_0, /* Properties */ PROP_NAME, PROP_FLAGS, PROP_SETTINGS, PROP_ENABLED, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * GObject definitions */ struct _GvFeaturePrivate { /* Properties */ gchar *name; GvFeatureFlags flags; GSettings *settings; gboolean enabled; }; typedef struct _GvFeaturePrivate GvFeaturePrivate; static void gv_feature_configurable_interface_init(GvConfigurableInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE(GvFeature, gv_feature, G_TYPE_OBJECT, G_ADD_PRIVATE(GvFeature) G_IMPLEMENT_INTERFACE(GV_TYPE_CONFIGURABLE, gv_feature_configurable_interface_init)) /* * Property accessors */ const gchar * gv_feature_get_name(GvFeature *self) { GvFeaturePrivate *priv = gv_feature_get_instance_private(self); return priv->name; } static void gv_feature_set_name(GvFeature *self, const gchar *name) { GvFeaturePrivate *priv = gv_feature_get_instance_private(self); /* Construct-only property */ g_assert_null(priv->name); g_assert_nonnull(name); priv->name = g_strdup(name); } GvFeatureFlags gv_feature_get_flags(GvFeature *self) { GvFeaturePrivate *priv = gv_feature_get_instance_private(self); return priv->flags; } static void gv_feature_set_flags(GvFeature *self, GvFeatureFlags flags) { GvFeaturePrivate *priv = gv_feature_get_instance_private(self); /* Construct-only property */ g_assert(priv->flags == GV_FEATURE_DEFAULT); priv->flags = flags; } GSettings * gv_feature_get_settings(GvFeature *self) { GvFeaturePrivate *priv = gv_feature_get_instance_private(self); return priv->settings; } gboolean gv_feature_get_enabled(GvFeature *self) { GvFeaturePrivate *priv = gv_feature_get_instance_private(self); return priv->enabled; } void gv_feature_set_enabled(GvFeature *self, gboolean enabled) { GvFeaturePrivate *priv = gv_feature_get_instance_private(self); GvFeatureClass *feature_class = GV_FEATURE_GET_CLASS(self); /* Bail out if needed */ if (priv->enabled == enabled) return; priv->enabled = enabled; /* Invoke virtual function */ if (enabled == TRUE) { INFO("Enabling feature '%s'...", priv->name); feature_class->enable(self); } else { INFO("Disabling feature '%s'...", priv->name); feature_class->disable(self); } /* Don't forget to notify */ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_ENABLED]); } static void gv_feature_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvFeature *self = GV_FEATURE(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_NAME: g_value_set_string(value, gv_feature_get_name(self)); break; case PROP_FLAGS: g_value_set_flags(value, gv_feature_get_flags(self)); break; case PROP_SETTINGS: g_value_set_object(value, gv_feature_get_settings(self)); break; case PROP_ENABLED: g_value_set_boolean(value, gv_feature_get_enabled(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_feature_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvFeature *self = GV_FEATURE(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_NAME: gv_feature_set_name(self, g_value_get_string(value)); break; case PROP_FLAGS: gv_feature_set_flags(self, g_value_get_flags(value)); break; case PROP_ENABLED: gv_feature_set_enabled(self, g_value_get_boolean(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public functions */ GvFeature * gv_feature_new(GType object_type, const gchar *name, GvFeatureFlags flags) { return g_object_new(object_type, "name", name, "flags", flags, NULL); } /* * GvConfigurable interface */ static void gv_feature_configure(GvConfigurable *configurable) { GvFeature *self = GV_FEATURE(configurable); GvFeaturePrivate *priv = gv_feature_get_instance_private(self); GSettings *settings = priv->settings; TRACE("%p", self); g_assert(settings); g_settings_bind(settings, "enabled", self, "enabled", G_SETTINGS_BIND_DEFAULT); } static void gv_feature_configurable_interface_init(GvConfigurableInterface *iface) { iface->configure = gv_feature_configure; } /* * GObject methods */ static void gv_feature_finalize(GObject *object) { GvFeature *self = GV_FEATURE(object); GvFeaturePrivate *priv = gv_feature_get_instance_private(self); TRACE("%p", object); /* Unload */ if (priv->enabled) gv_feature_set_enabled(self, FALSE); /* Free resources */ g_object_unref(priv->settings); g_free(priv->name); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_feature, object); } static void gv_feature_constructed(GObject *object) { GvFeature *self = GV_FEATURE(object); GvFeatureClass *class = GV_FEATURE_GET_CLASS(self); GvFeaturePrivate *priv = gv_feature_get_instance_private(self); gchar *schema_id_suffix; TRACE("%p", object); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_feature, object); /* Ensure virtual methods are implemented */ g_assert_nonnull(class->enable); g_assert_nonnull(class->disable); /* Ensure construct-only properties have been set */ g_assert_nonnull(priv->name); /* Create settings */ schema_id_suffix = g_strjoin(".", "Feat", priv->name, NULL); priv->settings = gv_get_settings(schema_id_suffix); g_free(schema_id_suffix); } static void gv_feature_init(GvFeature *self) { TRACE("%p", self); } static void gv_feature_class_init(GvFeatureClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_feature_finalize; object_class->constructed = gv_feature_constructed; /* Properties */ object_class->get_property = gv_feature_get_property; object_class->set_property = gv_feature_set_property; properties[PROP_NAME] = g_param_spec_string("name", "Name", NULL, NULL, GV_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_FLAGS] = g_param_spec_flags("flags", "Feature flags", NULL, GV_TYPE_FEATURE_FLAGS, GV_FEATURE_DEFAULT, GV_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_SETTINGS] = g_param_spec_object("settings", "Settings", NULL, G_TYPE_SETTINGS, GV_PARAM_READABLE); properties[PROP_ENABLED] = g_param_spec_boolean("enabled", "Enabled", NULL, FALSE, GV_PARAM_READWRITE); g_object_class_install_properties(object_class, PROP_N, properties); } goodvibes-v0.7.2/src/base/gv-feature.h000066400000000000000000000041511414415210700176060ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_FEATURE gv_feature_get_type() G_DECLARE_DERIVABLE_TYPE(GvFeature, gv_feature, GV, FEATURE, GObject) /* Chain up macros */ #define GV_FEATURE_CHAINUP_ENABLE(type_name, obj) \ do { \ if (GV_FEATURE_CLASS(type_name##_parent_class)->enable) \ GV_FEATURE_CLASS(type_name##_parent_class)->enable(obj); \ } while (0) #define GV_FEATURE_CHAINUP_DISABLE(type_name, obj) \ do { \ if (GV_FEATURE_CLASS(type_name##_parent_class)->disable) \ GV_FEATURE_CLASS(type_name##_parent_class)->disable(obj); \ } while (0) /* Data types */ typedef enum { /*< flags >*/ GV_FEATURE_DEFAULT, GV_FEATURE_EARLY, } GvFeatureFlags; struct _GvFeatureClass { /* Parent class */ GObjectClass parent_class; /* Virtual methods */ void (*enable) (GvFeature *); void (*disable)(GvFeature *); }; /* Public methods */ GvFeature *gv_feature_new(GType object_type, const gchar *name, GvFeatureFlags flags); /* Property accessors */ const gchar *gv_feature_get_name (GvFeature *self); GvFeatureFlags gv_feature_get_flags (GvFeature *self); GSettings *gv_feature_get_settings(GvFeature *self); gboolean gv_feature_get_enabled (GvFeature *self); void gv_feature_set_enabled (GvFeature *self, gboolean enabled); goodvibes-v0.7.2/src/base/gv-param-specs.h000066400000000000000000000024261414415210700203710ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include /* Default flags for objects: * - STATIC_STRINGS because we only use static strings. * - EXPLICIT_NOTIFY because we want to notify only when a property * is changed. It's a design choice, every object should stick to it, * otherwise expect surprises. */ #define GV_PARAM_READABLE G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY #define GV_PARAM_WRITABLE G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY #define GV_PARAM_READWRITE G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY goodvibes-v0.7.2/src/base/log.c000066400000000000000000000215711414415210700163220ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "vt-codes.h" /* Additional log level for traces */ #define LOG_LEVEL_TRACE G_LOG_LEVEL_DEBUG << 1 /* Error printing */ #define perrorf(fmt, ...) fprintf(stderr, fmt ": %s\n", ##__VA_ARGS__, strerror(errno)) #define print_err(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) /* Predefined log strings */ struct _log_strings { /* Log level prefixes */ const gchar *error; const gchar *critical; const gchar *warning; const gchar *message; const gchar *info; const gchar *debug; const gchar *trace; const gchar *dfl; /* Colors codes */ const gchar *reset; const gchar *dim; }; typedef struct _log_strings LogStrings; static LogStrings log_strings_colorless = { // clang-format off /* Log level prefixes */ .error = "ERR ", .critical = "CRIT", .warning = "WARN", .message = "MSG ", .info = "INFO", .debug = "DBG ", .trace = " -> ", .dfl = "LOG ", /* Colors codes */ .reset = "", .dim = "" // clang-format on }; static LogStrings log_strings_colorful = { // clang-format off /* Log level prefixes */ .error = VT_RED ("ERR "), .critical = VT_RED ("CRIT"), .warning = VT_YELLOW("WARN"), .message = VT_GREEN ("MSG "), .info = VT_GREEN ("INFO"), .debug = VT_DIM ("DBG "), .trace = VT_DIM (" -> "), .dfl = "LOG ", /* Color codes */ .reset = VT_CODE_ESC VT_CODE_RESET, .dim = VT_CODE_ESC VT_CODE_DIM // clang-format on }; /* Global variables that control the behavior of logs */ static FILE *log_stream; static gint log_level; static LogStrings *log_strings = &log_strings_colorless; /* Copies of std{out/err}, in case we redirect it */ static int stdout_copy = -1; static int stderr_copy = -1; /* Convert from string to log level */ static gint string_to_log_level(const gchar *str) { GLogLevelFlags level = G_LOG_LEVEL_MESSAGE; if (str == NULL) return level; if (!strcasecmp(str, "error") || !strcasecmp(str, "err")) { level = G_LOG_LEVEL_ERROR; } else if (!strcasecmp(str, "critical") || !strcasecmp(str, "crit")) { level = G_LOG_LEVEL_CRITICAL; } else if (!strcasecmp(str, "warning") || !strcasecmp(str, "warn")) { level = G_LOG_LEVEL_WARNING; } else if (!strcasecmp(str, "message") || !strcasecmp(str, "msg")) { level = G_LOG_LEVEL_MESSAGE; } else if (!strcasecmp(str, "info")) { level = G_LOG_LEVEL_INFO; } else if (!strcasecmp(str, "debug") || !strcasecmp(str, "dbg")) { level = G_LOG_LEVEL_DEBUG; } else if (!strcasecmp(str, "trace")) { level = LOG_LEVEL_TRACE; } else { print_err("Invalid log level '%s'", str); } return level; } /* Default log handler. * We DON'T honor any environment variables, such as * G_MESSAGES_PREFIXED, G_MESSAGES_DEBUG, ... */ static void log_default_handler(const gchar *domain, GLogLevelFlags level, const gchar *msg, gpointer unused_data G_GNUC_UNUSED) { GDateTime *now; gchar *now_str; const gchar *prefix; level &= G_LOG_LEVEL_MASK; /* Last chance to discard the log */ if (level > log_level) return; /* Discard debug messages that don't belong to us */ if (domain && level > G_LOG_LEVEL_INFO) return; /* Start by getting the current time. Note that it would be more * accurate to get the time earlier, but we don't need such accuracy * I believe, plus it's more convenient to do it here. */ now = g_date_time_new_now_local(); now_str = g_date_time_format(now, "%T"); /* Prefix depends on log level. * Cast is needed at the moment to avoid a gcc warning. * This is because GLogLevelFlags *may be* 8-bits long * due to the way it's defined. * Check the net for more info: * https://mail.gnome.org/archives/gtk-devel-list/2014-May/msg00029.html * https://bugzilla.gnome.org/show_bug.cgi?id=730932 */ switch ((gint) level) { case G_LOG_LEVEL_ERROR: prefix = log_strings->error; break; case G_LOG_LEVEL_CRITICAL: prefix = log_strings->critical; break; case G_LOG_LEVEL_WARNING: prefix = log_strings->warning; break; case G_LOG_LEVEL_MESSAGE: prefix = log_strings->message; break; case G_LOG_LEVEL_INFO: prefix = log_strings->info; break; case G_LOG_LEVEL_DEBUG: prefix = log_strings->debug; break; case LOG_LEVEL_TRACE: prefix = log_strings->trace; break; default: prefix = log_strings->dfl; break; } /* Send everything to the log stream */ fputs(prefix, log_stream); fputs(" ", log_stream); fputs(log_strings->dim, log_stream); fputs(now_str, log_stream); fputs(log_strings->reset, log_stream); fputs(" ", log_stream); if (domain) fprintf(log_stream, "[%s] ", domain); fputs(msg, log_stream); fputs("\n", log_stream); /* Cleanup */ g_date_time_unref(now); g_free(now_str); } void log_trace_property_access(const gchar *file, const gchar *func, GObject *object, guint property_id, const GValue *value, GParamSpec *pspec, gboolean print_value) { gchar *value_string; guint max_len = 128; if (LOG_LEVEL_TRACE > log_level) return; if (print_value) { value_string = g_strdup_value_contents(value); if (value_string && strlen(value_string) > max_len) { guint idx; if (value_string[0] == '"') idx = max_len - 4; else idx = max_len - 3; value_string[idx++] = '.'; value_string[idx++] = '.'; value_string[idx++] = '.'; if (value_string[0] == '"') value_string[idx++] = '"'; value_string[idx] = '\0'; } } else { value_string = g_strdup_printf("(%s)", G_VALUE_TYPE_NAME(value)); } g_log(G_LOG_DOMAIN, LOG_LEVEL_TRACE, "%s%s: %s%s(%p, %d, %s, '%s')", log_strings->dim, file, func, log_strings->reset, object, property_id, value_string, pspec->name); g_free(value_string); } void log_trace(const gchar *file, const gchar *func, const gchar *fmt, ...) { va_list ap; gchar fmt2[512]; if (LOG_LEVEL_TRACE > log_level) return; snprintf(fmt2, sizeof fmt2, "%s%s: %s()%s: (%s)", log_strings->dim, file, func, log_strings->reset, fmt); va_start(ap, fmt); g_logv(G_LOG_DOMAIN, LOG_LEVEL_TRACE, fmt2, ap); va_end(ap); } void log_msg(GLogLevelFlags level, const gchar *file, const gchar *func, const gchar *fmt, ...) { va_list ap; gchar fmt2[512]; if (level > log_level) return; if (!file && !func) snprintf(fmt2, sizeof fmt2, "%s", fmt); else snprintf(fmt2, sizeof fmt2, "%s%s: %s()%s: %s", log_strings->dim, file, func, log_strings->reset, fmt); va_start(ap, fmt); g_logv(G_LOG_DOMAIN, level, fmt2, ap); va_end(ap); } void log_cleanup(void) { /* Restore standard output */ if (stdout_copy > 0) { if (dup2(stdout_copy, STDOUT_FILENO) == -1) perror("Failed to restore stdout"); if (close(stdout_copy) == -1) perror("Failed to close stdout copy"); } /* Restore error output */ if (stderr_copy > 0) { if (dup2(stderr_copy, STDERR_FILENO) == -1) perror("Failed to restore stderr"); if (close(stderr_copy) == -1) perror("Failed to close stderr copy"); } } void log_init(const gchar *log_level_str, gboolean colorless, const gchar *output_file) { /* We send every log message to stderr, so that it's easy * to separate logs (intended for developpers) and messages * (intended for users). */ log_stream = stderr; /* We have our own log handler */ g_log_set_default_handler(log_default_handler, NULL); /* Set log level */ log_level = string_to_log_level(log_level_str); /* Redirect output to a log file */ if (output_file) { FILE *fp; stdout_copy = dup(STDOUT_FILENO); if (stdout_copy == -1) perror("Failed to duplicate stdout"); stderr_copy = dup(STDERR_FILENO); if (stderr_copy == -1) perror("Failed to duplicate stderr"); fp = fopen(output_file, "w"); if (fp) { if (dup2(fileno(fp), STDOUT_FILENO) == -1) perror("Failed to redirect stdout"); if (dup2(fileno(fp), STDERR_FILENO) == -1) perror("Failed to redirect stderr"); if (fclose(fp) == -1) perrorf("Failed to close log file '%s'", output_file); } else perrorf("Failed to open log file '%s'", output_file); } /* Set colorful log prefixes. * Colors only make sense if logs are sent to a terminal, * since they're implemented with VT commands. */ if (isatty(fileno(log_stream)) && !colorless) log_strings = &log_strings_colorful; } goodvibes-v0.7.2/src/base/log.h000066400000000000000000000045651414415210700163330ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include void log_init(const gchar *log_level, gboolean colorless, const gchar *output_file); void log_cleanup(void); void log_msg(GLogLevelFlags level, const gchar *file, const gchar *func, const gchar *fmt, ...); void log_trace(const gchar *file, const gchar *func, const gchar *fmt, ...); void log_trace_property_access(const gchar *file, const gchar *func, GObject *object, guint property_id, const GValue *value, GParamSpec *pspec, gboolean print_value); /* * Wrappers to GLib message logging functions. * Use that for logs intended for developers. */ #define ERROR(fmt, ...) do { \ log_msg(G_LOG_LEVEL_ERROR, __FILE__, __func__, fmt, ##__VA_ARGS__); \ __builtin_unreachable(); \ } while (0) #define CRITICAL(fmt, ...) log_msg(G_LOG_LEVEL_CRITICAL, __FILE__, __func__, fmt, ##__VA_ARGS__) #define WARNING(fmt, ...) log_msg(G_LOG_LEVEL_WARNING, __FILE__, __func__, fmt, ##__VA_ARGS__) #define INFO(fmt, ...) log_msg(G_LOG_LEVEL_INFO, __FILE__, __func__, fmt, ##__VA_ARGS__) #define DEBUG(fmt, ...) log_msg(G_LOG_LEVEL_DEBUG, __FILE__, __func__, fmt, ##__VA_ARGS__) #define DEBUG_NO_CONTEXT(fmt, ...) log_msg(G_LOG_LEVEL_DEBUG, NULL, NULL, fmt, ##__VA_ARGS__) #define TRACE(fmt, ...) log_trace(__FILE__, __func__, fmt, ##__VA_ARGS__) #define TRACE_GET_PROPERTY(obj, prop_id, value, pspec) \ log_trace_property_access(__FILE__, __func__, obj, prop_id, value, pspec, FALSE) #define TRACE_SET_PROPERTY(obj, prop_id, value, pspec) \ log_trace_property_access(__FILE__, __func__, obj, prop_id, value, pspec, TRUE) goodvibes-v0.7.2/src/base/meson.build000066400000000000000000000035271414415210700175400ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only # Configuration file config = configuration_data() config.set_quoted('GETTEXT_PACKAGE', gv_name_lowercase) config.set_quoted('GV_LOCALEDIR', localedir) config.set_quoted('PACKAGE_NAME', gv_name_lowercase) config.set_quoted('PACKAGE_VERSION', gv_version) config.set_quoted('GV_NAME_CAPITAL', gv_name_camelcase) config.set_quoted('GV_APPLICATION_ID', gv_application_id) config.set_quoted('GV_APPLICATION_PATH', gv_application_path) config.set_quoted('GV_ICON_NAME', gv_icon_name) config.set_quoted('GV_HOMEPAGE', gv_homepage) config.set_quoted('GV_ONLINE_HELP', gv_online_help) config.set_quoted('GV_COPYRIGHT', gv_copyright) config.set_quoted('GV_AUTHOR_NAME', gv_author_name) config.set_quoted('GV_AUTHOR_EMAIL', gv_author_email) config.set('GV_FEAT_CONSOLE_OUTPUT', gv_feat_console_output) config.set('GV_FEAT_DBUS_SERVER', gv_feat_dbus_server) config.set('GV_UI_ENABLED', gv_ui_enabled) config.set('GV_FEAT_HOTKEYS', gv_feat_hotkeys) config.set('GV_FEAT_INHIBITOR', gv_feat_inhibitor) config.set('GV_FEAT_NOTIFICATIONS', gv_feat_notifications) configure_file( output: 'config.h', configuration: config, ) # Sources and dependencies base_sources = [ 'glib-additions.c', 'glib-object-additions.c', 'gv-configurable.c', 'gv-errorable.c', 'gv-feature.c', 'gv-base.c', 'log.c', 'uri-schemes.c', 'utils.c', ] base_dependencies = [ glib_dep, gobject_dep, gio_dep, ] base_enum_headers = [ 'gv-feature.h' ] base_enums = gnome.mkenums_simple('gv-base-enum-types', sources: base_enum_headers ) base_enum_h = base_enums[1] # Library definition gvbase = static_library('gvbase', sources: [ base_sources, base_enums ], dependencies: base_dependencies, include_directories: root_inc, ) gvbase_dep = declare_dependency( dependencies: base_dependencies, sources: [ base_enum_h ], link_with: gvbase, ) goodvibes-v0.7.2/src/base/uri-schemes.c000066400000000000000000000023311414415210700177560ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include const gchar *SUPPORTED_URI_SCHEMES[] = { "http", "https", NULL }; const gchar *SUPPORTED_MIME_TYPES[] = { "audio/*", NULL }; gboolean is_uri_scheme_supported(const gchar *uri) { gchar *uri_scheme; const gchar **schemes = SUPPORTED_URI_SCHEMES; const gchar *scheme; gboolean uri_ok; uri_scheme = g_uri_parse_scheme(uri); uri_ok = FALSE; while (schemes && (scheme = *schemes++)) { if (g_strcmp0(scheme, uri_scheme) == 0) { uri_ok = TRUE; break; } } g_free(uri_scheme); return uri_ok; } goodvibes-v0.7.2/src/base/uri-schemes.h000066400000000000000000000015421414415210700177660ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once extern const gchar *SUPPORTED_URI_SCHEMES[]; extern const gchar *SUPPORTED_MIME_TYPES[]; gboolean is_uri_scheme_supported(const gchar *uri); goodvibes-v0.7.2/src/base/utils.c000066400000000000000000000051311414415210700166730ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2018-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "config.h" #include "log.h" gboolean gv_in_test_suite(void) { gchar **env, **ptr; gboolean ret = FALSE; env = g_listenv(); for (ptr = env; ptr && *ptr; ptr++) { if (!g_strcmp0(*ptr, "GOODVIBES_IN_TEST_SUITE")) { ret = TRUE; break; } } g_strfreev(env); return ret; } /* * Settings */ GSettings * gv_get_settings(const gchar *component) { gchar *schema_id; GSettings *settings; schema_id = g_strdup_printf("%s.%s", GV_APPLICATION_ID, component); settings = g_settings_new(schema_id); g_free(schema_id); return settings; } /* * XDG */ const gchar * gv_get_app_user_config_dir(void) { static gchar *dir; if (dir == NULL) { const gchar *user_dir; user_dir = g_get_user_config_dir(); dir = g_build_filename(user_dir, PACKAGE_NAME, NULL); } return dir; } const gchar * gv_get_app_user_data_dir(void) { static gchar *dir; if (dir == NULL) { const gchar *user_dir; user_dir = g_get_user_data_dir(); dir = g_build_filename(user_dir, PACKAGE_NAME, NULL); } return dir; } const gchar *const * gv_get_app_system_config_dirs(void) { static gchar **dirs; if (dirs == NULL) { const gchar *const *system_dirs; guint i, n_dirs; system_dirs = g_get_system_config_dirs(); n_dirs = g_strv_length((gchar **) system_dirs); dirs = g_malloc0_n(n_dirs + 1, sizeof(gchar *)); for (i = 0; i < n_dirs; i++) dirs[i] = g_build_filename(system_dirs[i], PACKAGE_NAME, NULL); } return (const gchar *const *) dirs; } const gchar *const * gv_get_app_system_data_dirs(void) { static gchar **dirs; if (dirs == NULL) { const gchar *const *system_dirs; guint i, n_dirs; system_dirs = g_get_system_data_dirs(); n_dirs = g_strv_length((gchar **) system_dirs); dirs = g_malloc0_n(n_dirs + 1, sizeof(gchar *)); for (i = 0; i < n_dirs; i++) dirs[i] = g_build_filename(system_dirs[i], PACKAGE_NAME, NULL); } return (const gchar *const *) dirs; } goodvibes-v0.7.2/src/base/utils.h000066400000000000000000000020151414415210700166760ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2018-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include gboolean gv_in_test_suite(void); GSettings *gv_get_settings(const gchar *component); const gchar *gv_get_app_user_config_dir(void); const gchar *gv_get_app_user_data_dir(void); const gchar *const *gv_get_app_system_config_dirs(void); const gchar *const *gv_get_app_system_data_dirs(void); goodvibes-v0.7.2/src/base/vt-codes.h000066400000000000000000000057071414415210700172750ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once /* Terminal codes (ANSI/VT100) */ #define VT_CODE_ESC "\033" #define VT_CODE_RESET "[0m" #define VT_CODE_BOLD "[1m" #define VT_CODE_DIM "[2m" #define VT_CODE_BLACK "[0;30m" #define VT_CODE_RED "[0;31m" #define VT_CODE_GREEN "[0;32m" #define VT_CODE_BROWN "[0;33m" #define VT_CODE_BLUE "[0;34m" #define VT_CODE_PURPLE "[0;35m" #define VT_CODE_CYAN "[0;36m" #define VT_CODE_GREY "[0;37m" #define VT_CODE_DARK_GREY "[1;30m" #define VT_CODE_LIGHT_RED "[1;31m" #define VT_CODE_LIGHT_GREEN "[1;32m" #define VT_CODE_YELLOW "[1;33m" #define VT_CODE_LIGHT_BLUE "[1;34m" #define VT_CODE_LIGHT_PURPLE "[1;35m" #define VT_CODE_LIGHT_CYAN "[1;36m" #define VT_CODE_WHITE "[1;37m" /* Convenient macros to wrap static strings */ #define VT_BOLD(str) VT_CODE_ESC VT_CODE_BOLD str VT_CODE_ESC VT_CODE_RESET #define VT_DIM(str) VT_CODE_ESC VT_CODE_DIM str VT_CODE_ESC VT_CODE_RESET #define VT_BLACK(str) VT_CODE_ESC VT_CODE_BLACK str VT_CODE_ESC VT_CODE_RESET #define VT_RED(str) VT_CODE_ESC VT_CODE_RED str VT_CODE_ESC VT_CODE_RESET #define VT_GREEN(str) VT_CODE_ESC VT_CODE_GREEN str VT_CODE_ESC VT_CODE_RESET #define VT_BROWN(str) VT_CODE_ESC VT_CODE_BROWN str VT_CODE_ESC VT_CODE_RESET #define VT_BLUE(str) VT_CODE_ESC VT_CODE_BLUE str VT_CODE_ESC VT_CODE_RESET #define VT_PURPLE(str) VT_CODE_ESC VT_CODE_PURPLE str VT_CODE_ESC VT_CODE_RESET #define VT_CYAN(str) VT_CODE_ESC VT_CODE_CYAN str VT_CODE_ESC VT_CODE_RESET #define VT_GREY(str) VT_CODE_ESC VT_CODE_GREY str VT_CODE_ESC VT_CODE_RESET #define VT_DARK_GREY(str) VT_CODE_ESC VT_CODE_DARK_GREY str VT_CODE_ESC VT_CODE_RESET #define VT_LIGHT_RED(str) VT_CODE_ESC VT_CODE_LIGHT_RED str VT_CODE_ESC VT_CODE_RESET #define VT_LIGHT_GREEN(str) VT_CODE_ESC VT_CODE_LIGHT_GREEN str VT_CODE_ESC VT_CODE_RESET #define VT_YELLOW(str) VT_CODE_ESC VT_CODE_YELLOW str VT_CODE_ESC VT_CODE_RESET #define VT_LIGHT_BLUE(str) VT_CODE_ESC VT_CODE_LIGHT_BLUE str VT_CODE_ESC VT_CODE_RESET #define VT_LIGHT_PURPLE(str) VT_CODE_ESC VT_CODE_LIGHT_PURPLE str VT_CODE_ESC VT_CODE_RESET #define VT_LIGHT_CYAN(str) VT_CODE_ESC VT_CODE_LIGHT_CYAN str VT_CODE_ESC VT_CODE_RESET #define VT_WHITE(str) VT_CODE_ESC VT_CODE_WHITE str VT_CODE_ESC VT_CODE_RESET goodvibes-v0.7.2/src/client.c000066400000000000000000000450751414415210700161120ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "base/config.h" /* http://misc.flogisoft.com/bash/tip_colors_and_formatting */ #define ESC "\033" #define BOLD_CODE "[1m" #define RESET_CODE "[0m" #define BOLD(str) ESC BOLD_CODE str ESC RESET_CODE #define print(fmt, ...) fprintf(stdout, fmt "\n", ##__VA_ARGS__) #define print_err(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) /* * Help */ const char *app_name; void help_init(const char *str) { app_name = str; } void help_and_exit(int exit_code) { #define REVISION(name) print("%s (version " PACKAGE_VERSION ")", name); #define USAGE(name) print("Usage: %s []", name); #define HEADING(str) print(BOLD(str ":")) #define COMMAND(cmd, desc) print(BOLD(" %-32s") "%s", cmd, desc) #define DETAILS(desc) print(" %-32s%s", "", desc) #define NL() print(""); REVISION(app_name); USAGE(app_name); NL(); HEADING("Base commands"); COMMAND("launch", "Launch " GV_NAME_CAPITAL); COMMAND("quit", "Quit " GV_NAME_CAPITAL); COMMAND("is-running", "Check whether " GV_NAME_CAPITAL " is running"); COMMAND("help", "Print this help message"); NL(); HEADING("Control"); print(". can be the station name or uri"); COMMAND("play []", "Without argument, play the current station"); DETAILS("Otherwise, play the station given in argument"); COMMAND("stop", "Stop playback"); COMMAND("play-stop", "Toggle play/stop mode"); COMMAND("next", "Play next station"); COMMAND("prev(ious)", "Play previous station"); COMMAND("volume []", "Get/set volume (in %)"); COMMAND("mute [true/false]", "Get/set mute state"); COMMAND("repeat [true/false]", "Get/set repeat"); COMMAND("shuffle [true/false]", "Get/set shuffle"); COMMAND("current", "Get info on current station"); COMMAND("playing", "Get playback status"); NL(); HEADING("Station list"); print(". can be the station name or uri"); COMMAND("list", "Display the list of stations"); COMMAND("add [] [[first/last] [before/after ]]", ""); DETAILS("Add a station to the list"); COMMAND("remove ", "Remove a station from the list"); COMMAND("rename ", "Rename a station"); COMMAND("move [[first/last] [before/after ]]", ""); DETAILS("Move a station in the list"); NL(); HEADING("Configuration"); print(". sections: core, ui, feat."); COMMAND("conf get

", "Get a config value"); COMMAND("conf set
", "Set a config value"); COMMAND("conf list-keys
", "List config keys"); COMMAND("conf describe
", "Describe a config key"); exit(exit_code); } /* * DBus */ #define DBUS_NAME GV_APPLICATION_ID #define DBUS_PATH GV_APPLICATION_PATH #define DBUS_ROOT_IFACE GV_APPLICATION_ID #define DBUS_PLAYER_IFACE DBUS_ROOT_IFACE ".Player" #define DBUS_STATIONS_IFACE DBUS_ROOT_IFACE ".Stations" int dbus_call(const char *bus_name, const char *object_path, const char *iface_name, const char *method_name, GVariant *args, GVariant **output) { GDBusConnection *c; GVariant *result; GError *err = NULL; if (output) *output = NULL; c = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &err); if (c == NULL) { print_err("DBus connection error: %s", err->message); g_error_free(err); return -1; } result = g_dbus_connection_call_sync( c, bus_name, object_path, iface_name, method_name, args, NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, &err); if (err) { if (err->domain == G_DBUS_ERROR && err->code == G_DBUS_ERROR_NAME_HAS_NO_OWNER) { /* Goodvibes is not running */ print_err(GV_NAME_CAPITAL " is not running!"); } else { /* Other error, just dump the GError */ print_err("DBus call error: %s", err->message); } g_error_free(err); return -1; } g_dbus_connection_close(c, NULL, NULL, NULL); if (output) *output = result; else if (result) g_variant_unref(result); return 0; } /* * Supported DBus commands */ enum cmd_type { METHOD, PROPERTY }; struct cmd { enum cmd_type type; const char *cmdline_name; const char *dbus_name; int (*parse_args)(int, char *[], GVariantBuilder *); void (*print_result)(GVariant *); }; struct interface { const char *name; const struct cmd *cmds; }; int parse_play_args(int argc, char *argv[], GVariantBuilder *b) { const char *station; if (argc == 0) station = ""; else if (argc == 1) station = argv[0]; else return -1; g_variant_builder_add(b, "s", station); return 0; } int parse_add_args(int argc, char *argv[], GVariantBuilder *b) { const char *station_uri; const char *station_name; const char *around_keyword; const char *around_station; if (argc == 0) return -1; station_uri = argv[0]; argc--; argv++; station_name = ""; if (argc > 0) { if (strcmp(argv[0], "first") && strcmp(argv[0], "last") && strcmp(argv[0], "before") && strcmp(argv[0], "after")) { station_name = argv[0]; argc--; argv++; } } around_keyword = ""; around_station = ""; if (argc == 0) { /* Nothing to do */ } else if (argc == 1) { if (!strcmp(argv[0], "first") || !strcmp(argv[0], "last")) around_keyword = argv[0]; else return -1; } else if (argc == 2) { if (!strcmp(argv[0], "before") || !strcmp(argv[0], "after")) { around_keyword = argv[0]; around_station = argv[1]; } else { return -1; } } else { return -1; } g_variant_builder_add(b, "s", station_uri); g_variant_builder_add(b, "s", station_name); g_variant_builder_add(b, "s", around_keyword); g_variant_builder_add(b, "s", around_station); return 0; } int parse_remove_args(int argc, char *argv[], GVariantBuilder *b) { const char *station; if (argc != 1) return -1; station = argv[0]; g_variant_builder_add(b, "s", station); return 0; } int parse_rename_args(int argc, char *argv[], GVariantBuilder *b) { const char *station; const char *new_name; if (argc != 2) return -1; station = argv[0]; new_name = argv[1]; g_variant_builder_add(b, "s", station); g_variant_builder_add(b, "s", new_name); return 0; } int parse_move_args(int argc, char *argv[], GVariantBuilder *b) { const char *station; const char *where; const char *around_station; if (argc < 2) return -1; station = argv[0]; where = argv[1]; argc -= 2; argv += 2; around_station = ""; if (argc == 0) { if (strcmp(where, "first") && strcmp(where, "last")) return -1; } else if (argc == 1) { if (!strcmp(where, "before") || !strcmp(where, "after")) around_station = argv[0]; else return -1; } else { return -1; } g_variant_builder_add(b, "s", station); g_variant_builder_add(b, "s", where); g_variant_builder_add(b, "s", around_station); return 0; } int parse_boolean(int argc, char *argv[], GVariantBuilder *b) { gboolean value; if (argc != 1) return -1; if (!g_strcmp0(argv[0], "true")) value = TRUE; else if (!g_strcmp0(argv[0], "false")) value = FALSE; else return -1; g_variant_builder_add(b, "v", g_variant_new_boolean(value)); return 0; } int parse_volume(int argc, char *argv[], GVariantBuilder *b) { long int value; char *endptr; if (argc != 1) return -1; value = strtol(argv[0], &endptr, 10); if (*endptr != '\0') return -1; g_variant_builder_add(b, "v", g_variant_new_uint32(value)); return 0; } void print_boolean(GVariant *result) { gboolean bool; bool = g_variant_get_boolean(result); if (bool) print("true"); else print("false"); } void print_volume(GVariant *result) { guint volume; volume = g_variant_get_uint32(result); print("%u%%", volume); } void print_current(GVariant *result) { GVariantIter *iter; GVariant *value; gchar *key; gchar *uri = NULL; gchar *name = NULL; gchar *artist = NULL; gchar *title = NULL; gchar *album = NULL; gchar *genre = NULL; gchar *year = NULL; gchar *comment = NULL; g_variant_get(result, "a{sv}", &iter); while (g_variant_iter_loop(iter, "{sv}", &key, &value)) { if (!g_strcmp0(key, "uri")) g_variant_get(value, "s", &uri); else if (!g_strcmp0(key, "name")) g_variant_get(value, "s", &name); else if (!g_strcmp0(key, "artist")) g_variant_get(value, "s", &artist); else if (!g_strcmp0(key, "title")) g_variant_get(value, "s", &title); else if (!g_strcmp0(key, "album")) g_variant_get(value, "s", &album); else if (!g_strcmp0(key, "genre")) g_variant_get(value, "s", &genre); else if (!g_strcmp0(key, "year")) g_variant_get(value, "s", &year); else if (!g_strcmp0(key, "comment")) g_variant_get(value, "s", &comment); } g_variant_iter_free(iter); /* Radio name and URI */ print(BOLD("%s") "%s(%s)", name, name ? "\t" : "", uri); /* Artist and title */ if (artist || title) print(BOLD("> %s%s%s"), title ? title : "", title && artist ? " - " : "", artist ? artist : ""); /* Album, year and genre */ if (genre) { gchar *tmp = genre; genre = g_strdup_printf("(%s)", tmp); g_free(tmp); } if (album || year || genre) print("%s%s%s%s%s", album ? album : "", album && year ? " " : "", year ? year : "", (album && genre) || (year && genre) ? " - " : "", genre ? genre : ""); /* Freedom for the braves */ g_free(uri); g_free(name); g_free(artist); g_free(title); g_free(album); g_free(genre); g_free(year); g_free(comment); } void print_list_result(GVariant *result) { GVariantIter *iter1; GVariantIter *iter2; GVariant *value; gchar *key; g_variant_get(result, "(aa{sv})", &iter1); while (g_variant_iter_loop(iter1, "a{sv}", &iter2)) { gchar *uri = NULL; gchar *name = NULL; while (g_variant_iter_loop(iter2, "{sv}", &key, &value)) { if (!g_strcmp0(key, "uri")) g_variant_get(value, "s", &uri); else if (!g_strcmp0(key, "name")) g_variant_get(value, "s", &name); } print(BOLD("%-20s") "%s", name ? name : "", uri); g_free(uri); g_free(name); } g_variant_iter_free(iter1); } struct cmd root_cmds[] = { // clang-format off { METHOD, "quit", "Quit", NULL, NULL }, { METHOD, NULL, NULL, NULL, NULL } // clang-format on }; struct cmd player_cmds[] = { // clang-format off { METHOD, "play", "Play", parse_play_args, NULL }, { METHOD, "stop", "Stop", NULL, NULL }, { METHOD, "play-stop", "PlayStop", NULL, NULL }, { METHOD, "next", "Next", NULL, NULL }, { METHOD, "prev", "Previous", NULL, NULL }, { METHOD, "previous", "Previous", NULL, NULL }, { PROPERTY, "current", "Current", NULL, print_current }, { PROPERTY, "playing", "Playing", NULL, print_boolean }, { PROPERTY, "repeat", "Repeat", parse_boolean, print_boolean }, { PROPERTY, "shuffle", "Shuffle", parse_boolean, print_boolean }, { PROPERTY, "volume", "Volume", parse_volume, print_volume }, { PROPERTY, "mute", "Mute", parse_boolean, print_boolean }, { PROPERTY, NULL, NULL, NULL, NULL } // clang-format on }; struct cmd stations_cmds[] = { // clang-format off { METHOD, "list", "List", NULL, print_list_result }, { METHOD, "add", "Add", parse_add_args, NULL }, { METHOD, "remove", "Remove", parse_remove_args, NULL }, { METHOD, "rename", "Rename", parse_rename_args, NULL }, { METHOD, "move", "Move", parse_move_args, NULL }, { METHOD, NULL, NULL, NULL, NULL } // clang-format on }; struct interface interfaces[] = { // clang-format off { DBUS_ROOT_IFACE, root_cmds }, { DBUS_PLAYER_IFACE, player_cmds }, { DBUS_STATIONS_IFACE, stations_cmds }, { NULL, NULL } // clang-format on }; /* * DBus related commands */ static int handle_launch(int argc, char *argv[] G_GNUC_UNUSED) { GVariantBuilder b; GVariant *args; int err; if (argc != 0) help_and_exit(EXIT_FAILURE); g_variant_builder_init(&b, G_VARIANT_TYPE_TUPLE); g_variant_builder_add(&b, "s", DBUS_NAME); g_variant_builder_add(&b, "u", 0); args = g_variant_builder_end(&b); err = dbus_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "StartServiceByName", args, NULL); return err; } static int handle_is_running(int argc, char *argv[] G_GNUC_UNUSED) { GVariantBuilder b; GVariant *args, *result; int err; if (argc != 0) help_and_exit(EXIT_FAILURE); g_variant_builder_init(&b, G_VARIANT_TYPE_TUPLE); g_variant_builder_add(&b, "s", DBUS_NAME); args = g_variant_builder_end(&b); result = NULL; err = dbus_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "NameHasOwner", args, &result); if (result) { gboolean is_running; g_variant_get(result, "(b)", &is_running); g_variant_unref(result); print("%s", is_running ? "true" : "false"); } return err; } static int handle_dbus_command(int argc, char *argv[]) { struct interface *iface; const struct cmd *cmd; GVariant *args, *result; int err = 0; /* Find command in lists */ for (iface = interfaces; iface->name; iface++) { for (cmd = iface->cmds; cmd->cmdline_name; cmd++) { if (!strcmp(argv[0], cmd->cmdline_name)) break; } if (cmd->cmdline_name) break; } if (iface->name == NULL) help_and_exit(EXIT_FAILURE); /* Discard arguments that has been processed */ argc -= 1; argv += 1; /* Process arguments left */ args = NULL; switch (cmd->type) { case METHOD: /* For methods, if there's a parse function provided, we run it, * no matter the number of arguments left. */ if (cmd->parse_args) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE_TUPLE); err = cmd->parse_args(argc, argv, &b); args = g_variant_builder_end(&b); } else if (argc > 0) { help_and_exit(EXIT_FAILURE); } break; case PROPERTY: { /* For properties, it's the number of remaining argument which * determines if it's a get or a set. Zero argument means get. */ GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE_TUPLE); g_variant_builder_add(&b, "s", iface->name); g_variant_builder_add(&b, "s", cmd->dbus_name); if (argc > 0) { if (cmd->parse_args) err = cmd->parse_args(argc, argv, &b); else help_and_exit(EXIT_FAILURE); } args = g_variant_builder_end(&b); break; } } if (err) help_and_exit(EXIT_FAILURE); /* DBus action (method call, property get/set) */ result = NULL; switch (cmd->type) { case METHOD: err = dbus_call(DBUS_NAME, DBUS_PATH, iface->name, cmd->dbus_name, args, &result); break; case PROPERTY: if (argc == 0) /* Get command */ err = dbus_call(DBUS_NAME, DBUS_PATH, "org.freedesktop.DBus.Properties", "Get", args, &result); else /* Set command */ err = dbus_call(DBUS_NAME, DBUS_PATH, "org.freedesktop.DBus.Properties", "Set", args, NULL); break; } if (err) exit(EXIT_FAILURE); /* Print result */ if (result && cmd->print_result) { // print("%s", g_variant_print(result, FALSE)); if (cmd->type == METHOD) { cmd->print_result(result); } else { /* cmd->type == PROPERTY */ /* Result is always a GVariant, encapsulated in a tuple */ GVariant *tmp; g_variant_get(result, "(v)", &tmp); cmd->print_result(tmp); g_variant_unref(tmp); } } if (result) g_variant_unref(result); return 0; } /* * Configuration related commands * * Here I sse GSettings through a subshell for convenience. * It's a bit ugly, but maybe it's a strong hint that I should rewrite * this client in a script language... */ static void capitalize_first_letters(gchar *str) { char *ptr; if (!str) return; *str = toupper(*str); str++; for (ptr = str; *ptr != '\0'; ptr++) { if (*ptr == '.') { ptr++; if (*ptr != '\0') *ptr = toupper(*ptr); } } } static int handle_conf_command(int argc, char *argv[]) { const gchar *cmd; const gchar *section; const gchar *key; const gchar *value_str; gchar *schema_id; gchar *gsettings_cmd; int success; if (argc < 1) help_and_exit(EXIT_FAILURE); /* Command */ cmd = argv[0]; /* Section */ capitalize_first_letters(argv[1]); section = argv[1]; schema_id = g_strjoin(".", GV_APPLICATION_ID, section, NULL); argc -= 2; argv += 2; /* Process the other arguments */ gsettings_cmd = NULL; if (!strcmp(cmd, "get")) { if (argc != 1) help_and_exit(EXIT_FAILURE); key = argv[0]; gsettings_cmd = g_strdup_printf("gsettings get %s %s", schema_id, key); } else if (!strcmp(cmd, "set")) { if (argc != 2) help_and_exit(EXIT_FAILURE); key = argv[0]; value_str = argv[1]; gsettings_cmd = g_strdup_printf("gsettings set %s %s %s", schema_id, key, value_str); } else if (!strcmp(cmd, "list-keys")) { if (argc != 0) help_and_exit(EXIT_FAILURE); gsettings_cmd = g_strdup_printf("gsettings list-keys %s", schema_id); } else if (!strcmp(cmd, "describe")) { if (argc != 1) help_and_exit(EXIT_FAILURE); key = argv[0]; gsettings_cmd = g_strdup_printf("gsettings describe %s %s", schema_id, key); } else { help_and_exit(EXIT_FAILURE); } success = system(gsettings_cmd); g_free(gsettings_cmd); g_free(schema_id); return success; } int main(int argc, char *argv[]) { int err; err = 0; help_init(argv[0]); if (argc < 2) help_and_exit(EXIT_FAILURE); if (!strcmp(argv[1], "help")) { /* Help command */ help_and_exit(EXIT_SUCCESS); } else if (!strcmp(argv[1], "launch")) { /* Launch commmand */ argc -= 2; argv += 2; err = handle_launch(argc, argv); } else if (!strcmp(argv[1], "is-running")) { /* Launch commmand */ argc -= 2; argv += 2; err = handle_is_running(argc, argv); } else if (!strcmp(argv[1], "conf")) { /* Configuration related commands */ argc -= 2; argv += 2; err = handle_conf_command(argc, argv); } else { /* DBus related command */ argc -= 1; argv += 1; err = handle_dbus_command(argc, argv); } return err ? EXIT_FAILURE : EXIT_SUCCESS; } goodvibes-v0.7.2/src/core/000077500000000000000000000000001414415210700154055ustar00rootroot00000000000000goodvibes-v0.7.2/src/core/gst-additions.c000066400000000000000000000026161414415210700203270ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include /* * Version Information */ const gchar * gst_get_runtime_version_string(void) { static gchar *version_string; if (version_string == NULL) { guint major; guint minor; guint micro; guint nano; gst_version(&major, &minor, µ, &nano); version_string = g_strdup_printf("GStreamer %u.%u.%u.%u", major, minor, micro, nano); } return version_string; } const gchar * gst_get_compile_version_string(void) { static gchar *version_string; if (version_string == NULL) { version_string = g_strdup_printf("GStreamer %u.%u.%u.%u", GST_VERSION_MAJOR, GST_VERSION_MINOR, GST_VERSION_MICRO, GST_VERSION_NANO); } return version_string; } goodvibes-v0.7.2/src/core/gst-additions.h000066400000000000000000000015111414415210700203250ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once /* * Gst */ const gchar *gst_get_runtime_version_string(void); const gchar *gst_get_compile_version_string(void); goodvibes-v0.7.2/src/core/gv-core-internal.h000066400000000000000000000016411414415210700207340ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ /* * This header contains definitions to be used internally by core files */ #pragma once #include /* Global variables */ extern GSettings *gv_core_settings; extern const gchar *gv_core_user_agent; goodvibes-v0.7.2/src/core/gv-core.c000066400000000000000000000075711414415210700171250ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "base/gv-base.h" #include "core/gv-engine.h" #include "core/gv-player.h" #include "core/gv-station-list.h" #define CORE_SCHEMA_ID_SUFFIX "Core" /* * Public variables */ GApplication *gv_core_application; GSettings *gv_core_settings; GvStationList *gv_core_station_list; GvPlayer *gv_core_player; gchar *gv_core_user_agent; /* * Private variables */ static GvEngine *gv_core_engine; static GList *core_objects; /* * Underlying audio backend */ #include #include "core/gst-additions.h" void gv_core_audio_backend_cleanup(void) { if (gst_is_initialized()) gst_deinit(); } GOptionGroup * gv_core_audio_backend_init_get_option_group(void) { return gst_init_get_option_group(); } const gchar * gv_core_audio_backend_runtime_version_string(void) { return gst_get_runtime_version_string(); } const gchar * gv_core_audio_backend_compile_version_string(void) { return gst_get_compile_version_string(); } /* * Helpers */ static gchar * make_user_agent(void) { const gchar *os = NULL; gchar *agent; /* https://sourceforge.net/p/predef/wiki/OperatingSystems */ #ifdef __gnu_linux__ os = "GNU/Linux"; #elif __linux__ os = "Linux"; #endif if (os) agent = g_strdup_printf("%s/%s (%s)", GV_NAME_CAPITAL, PACKAGE_VERSION, os); else agent = g_strdup_printf("%s/%s", GV_NAME_CAPITAL, PACKAGE_VERSION); return agent; } /* * Core public functions */ void gv_core_quit(void) { g_application_quit(gv_core_application); } void gv_core_configure(void) { GList *item; /* The station list must be loaded before any configuration is done. * Otherwise, configure the player current station will fail. */ gv_station_list_load(gv_core_station_list); /* Configure each object that is configurable */ for (item = core_objects; item; item = item->next) { GObject *object = item->data; if (!GV_IS_CONFIGURABLE(object)) continue; gv_configurable_configure(GV_CONFIGURABLE(object)); } } void gv_core_cleanup(void) { /* Destroy core objects */ core_objects = g_list_reverse(core_objects); g_list_free_full(core_objects, (GDestroyNotify) g_object_unref); /* Clear application pointer */ gv_core_application = NULL; /* Free strings */ g_free(gv_core_user_agent); } void gv_core_init(GApplication *application, const gchar *default_stations) { GList *item; /* Create strings */ gv_core_user_agent = make_user_agent(); DEBUG("User agent: %s", gv_core_user_agent); /* Keep a pointer toward application */ gv_core_application = application; /* Create core objects */ gv_core_settings = gv_get_settings(CORE_SCHEMA_ID_SUFFIX); core_objects = g_list_append(core_objects, gv_core_settings); gv_core_station_list = gv_station_list_new_from_xdg_dirs(default_stations); core_objects = g_list_append(core_objects, gv_core_station_list); gv_core_engine = gv_engine_new(); core_objects = g_list_append(core_objects, gv_core_engine); gv_core_player = gv_player_new(gv_core_engine, gv_core_station_list); core_objects = g_list_append(core_objects, gv_core_player); /* Register objects in the base */ for (item = core_objects; item; item = item->next) { GObject *object = G_OBJECT(item->data); gv_base_register_object(object); } } goodvibes-v0.7.2/src/core/gv-core.h000066400000000000000000000031251414415210700171210ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ /* * This header contains definitions to be used by core users */ #pragma once #include #include #include "core/gv-metadata.h" #include "core/gv-player.h" #include "core/gv-station.h" #include "core/gv-station-list.h" #include "core/gv-streaminfo.h" /* Global variables */ extern GApplication *gv_core_application; extern GvPlayer *gv_core_player; extern GvStationList *gv_core_station_list; /* Functions */ void gv_core_init (GApplication *app, const gchar *default_stations); void gv_core_cleanup (void); void gv_core_configure(void); void gv_core_quit (void); /* * Underlying audio backend */ GOptionGroup *gv_core_audio_backend_init_get_option_group (void); void gv_core_audio_backend_cleanup (void); const gchar *gv_core_audio_backend_runtime_version_string(void); const gchar *gv_core_audio_backend_compile_version_string(void); goodvibes-v0.7.2/src/core/gv-engine.c000066400000000000000000001012211414415210700174250ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gst-additions.h" #include "core/gv-core-enum-types.h" #include "core/gv-core-internal.h" #include "core/gv-metadata.h" #include "core/gv-station.h" #include "core/gv-streaminfo.h" #include "core/gv-engine.h" /* Uncomment to dump more stuff from GStreamer */ //#define DEBUG_GST_TAGS //#define DEBUG_GST_STATE_CHANGES //#define DEBUG_GST_ELEMENT_SETUP /* * Properties */ #define DEFAULT_VOLUME 100 #define DEFAULT_MUTE FALSE enum { /* Reserved */ PROP_0, /* Properties - refer to class_init() for more details */ PROP_PLAYBACK_STATE, PROP_STATION, PROP_STREAMINFO, PROP_METADATA, PROP_VOLUME, PROP_MUTE, PROP_PIPELINE_ENABLED, PROP_PIPELINE_STRING, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * Signals */ enum { SIGNAL_SSL_FAILURE, /* Number of signals */ SIGNAL_N }; static guint signals[SIGNAL_N]; /* * GObject definitions */ struct _GvEnginePrivate { /* GStreamer stuff */ GstElement *playbin; GstBus *bus; /* Properties */ GvEngineState state; GvStation *station; GvStreaminfo *streaminfo; GvMetadata *metadata; guint volume; gboolean mute; gboolean pipeline_enabled; gchar *pipeline_string; /* Retry on error with a delay */ guint error_count; guint start_playback_timeout_id; }; typedef struct _GvEnginePrivate GvEnginePrivate; struct _GvEngine { /* Parent instance structure */ GObject parent_instance; /* Private data */ GvEnginePrivate *priv; }; G_DEFINE_TYPE_WITH_CODE(GvEngine, gv_engine, G_TYPE_OBJECT, G_ADD_PRIVATE(GvEngine) G_IMPLEMENT_INTERFACE(GV_TYPE_ERRORABLE, NULL)) /* * GStreamer helpers */ static void set_gst_state(GstElement *playbin, GstState state) { const gchar *state_name = gst_element_state_get_name(state); GstStateChangeReturn change_return; change_return = gst_element_set_state(playbin, state); switch (change_return) { case GST_STATE_CHANGE_SUCCESS: DEBUG("Setting gst state '%s'... success", state_name); break; case GST_STATE_CHANGE_ASYNC: DEBUG("Setting gst state '%s'... will change async", state_name); break; case GST_STATE_CHANGE_FAILURE: /* This might happen if the uri is invalid */ DEBUG("Setting gst state '%s'... failed!", state_name); break; case GST_STATE_CHANGE_NO_PREROLL: DEBUG("Setting gst state '%s'... no preroll", state_name); break; default: WARNING("Unhandled state change: %d", change_return); break; } } #if 0 static GstState get_gst_state(GstElement *playbin) { GstStateChangeReturn change_return; GstState state, pending; change_return = gst_element_get_state(playbin, &state, &pending, 100 * GST_MSECOND); switch (change_return) { case GST_STATE_CHANGE_SUCCESS: DEBUG("Getting gst state... success"); break; case GST_STATE_CHANGE_ASYNC: DEBUG("Getting gst state... will change async"); break; case GST_STATE_CHANGE_FAILURE: DEBUG("Getting gst state... failed!"); break; case GST_STATE_CHANGE_NO_PREROLL: DEBUG("Getting gst state... no preroll"); break; default: WARNING("Unhandled state change: %d", change_return); break; } DEBUG("Gst state '%s', pending '%s'", gst_element_state_get_name(state), gst_element_state_get_name(pending)); return state; } #endif /* * Private methods */ static void gv_engine_reload_pipeline(GvEngine *self) { GvEnginePrivate *priv = self->priv; GstElement *playbin = priv->playbin; gboolean pipeline_enabled = priv->pipeline_enabled; const gchar *pipeline_string = priv->pipeline_string; GstElement *cur_audio_sink = NULL; GstElement *new_audio_sink = NULL; g_return_if_fail(playbin != NULL); /* Get current audio sink */ g_object_get(playbin, "audio-sink", &cur_audio_sink, NULL); DEBUG("Current audio sink: %s", cur_audio_sink ? GST_ELEMENT_NAME(cur_audio_sink) : "null (default)"); /* Create a new audio sink */ if (pipeline_enabled == FALSE || pipeline_string == NULL) { new_audio_sink = NULL; } else { GError *err = NULL; new_audio_sink = gst_parse_launch(pipeline_string, &err); if (err) { WARNING("Failed to parse pipeline description: %s", err->message); gv_errorable_emit_error(GV_ERRORABLE(self), _("%s: %s"), _("Failed to parse pipeline description"), err->message); g_error_free(err); } } DEBUG("New audio sink: %s", new_audio_sink ? GST_ELEMENT_NAME(new_audio_sink) : "null (default)"); /* True when one of them is NULL */ if (cur_audio_sink != new_audio_sink) { gv_engine_stop(self); if (new_audio_sink == NULL) INFO("Setting gst audio sink to default"); else INFO("Setting gst audio sink from pipeline '%s'", pipeline_string); g_object_set(playbin, "audio-sink", new_audio_sink, NULL); } if (cur_audio_sink) gst_object_unref(cur_audio_sink); } /* * Property accessors */ GvEngineState gv_engine_get_state(GvEngine *self) { return self->priv->state; } static void gv_engine_set_state(GvEngine *self, GvEngineState state) { GvEnginePrivate *priv = self->priv; if (priv->state == state) return; priv->state = state; g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PLAYBACK_STATE]); } GvStation * gv_engine_get_station(GvEngine *self) { return self->priv->station; } static void gv_engine_set_station(GvEngine *self, GvStation *station) { GvEnginePrivate *priv = self->priv; if (g_set_object(&priv->station, station)) g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_STATION]); } GvStreaminfo * gv_engine_get_streaminfo(GvEngine *self) { return self->priv->streaminfo; } static void gv_engine_update_streaminfo_from_element_setup(GvEngine *self, GstElement *element) { GvEnginePrivate *priv = self->priv; gboolean notify = FALSE; if (priv->streaminfo == NULL) { priv->streaminfo = gv_streaminfo_new(); notify = TRUE; } if (gv_streaminfo_update_from_element_setup(priv->streaminfo, element) == TRUE) notify = TRUE; if (notify) g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_STREAMINFO]); } static void gv_engine_update_streaminfo_from_tags(GvEngine *self, GstTagList *taglist) { GvEnginePrivate *priv = self->priv; gboolean notify = FALSE; if (priv->streaminfo == NULL) { priv->streaminfo = gv_streaminfo_new(); notify = TRUE; } if (gv_streaminfo_update_from_gst_taglist(priv->streaminfo, taglist) == TRUE) notify = TRUE; if (notify) g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_STREAMINFO]); } static void gv_engine_update_streaminfo_from_audio_pad(GvEngine *self, GstPad *pad) { GvEnginePrivate *priv = self->priv; gboolean notify = FALSE; if (priv->streaminfo == NULL) { priv->streaminfo = gv_streaminfo_new(); notify = TRUE; } if (gv_streaminfo_update_from_gst_audio_pad(priv->streaminfo, pad) == TRUE) notify = TRUE; if (notify) g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_STREAMINFO]); } static void gv_engine_unset_streaminfo(GvEngine *self) { GvEnginePrivate *priv = self->priv; if (priv->streaminfo == NULL) return; gv_clear_streaminfo(&priv->streaminfo); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_STREAMINFO]); } GvMetadata * gv_engine_get_metadata(GvEngine *self) { return self->priv->metadata; } static void gv_engine_update_metadata_from_tags(GvEngine *self, GstTagList *taglist) { GvEnginePrivate *priv = self->priv; gboolean notify = FALSE; if (priv->metadata == NULL) priv->metadata = gv_metadata_new(); notify = gv_metadata_update_from_gst_taglist(priv->metadata, taglist); /* We don't want empty metadata objects, it makes life complicated * for consumers who will forever need to write this kind of code: * * if (ptr && !gv_metadata_is_empty(ptr)) * ... */ if (gv_metadata_is_empty(priv->metadata)) gv_clear_metadata(&priv->metadata); if (notify) g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_METADATA]); } static void gv_engine_unset_metadata(GvEngine *self) { GvEnginePrivate *priv = self->priv; if (priv->metadata == NULL) return; gv_clear_metadata(&priv->metadata); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_METADATA]); } guint gv_engine_get_volume(GvEngine *self) { return self->priv->volume; } void gv_engine_set_volume(GvEngine *self, guint volume) { GvEnginePrivate *priv = self->priv; gdouble gst_volume; if (volume > 100) volume = 100; if (priv->volume == volume) return; priv->volume = volume; gst_volume = (gdouble) volume / 100.0; gst_stream_volume_set_volume(GST_STREAM_VOLUME(priv->playbin), GST_STREAM_VOLUME_FORMAT_CUBIC, gst_volume); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_VOLUME]); } gboolean gv_engine_get_mute(GvEngine *self) { return self->priv->mute; } void gv_engine_set_mute(GvEngine *self, gboolean mute) { GvEnginePrivate *priv = self->priv; if (priv->mute == mute) return; priv->mute = mute; gst_stream_volume_set_mute(GST_STREAM_VOLUME(priv->playbin), mute); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_MUTE]); } gboolean gv_engine_get_pipeline_enabled(GvEngine *self) { return self->priv->pipeline_enabled; } void gv_engine_set_pipeline_enabled(GvEngine *self, gboolean enabled) { GvEnginePrivate *priv = self->priv; if (priv->pipeline_enabled == enabled) return; priv->pipeline_enabled = enabled; gv_engine_reload_pipeline(self); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PIPELINE_ENABLED]); } const gchar * gv_engine_get_pipeline_string(GvEngine *self) { return self->priv->pipeline_string; } void gv_engine_set_pipeline_string(GvEngine *self, const gchar *pipeline_string) { GvEnginePrivate *priv = self->priv; if (!g_strcmp0(pipeline_string, "")) pipeline_string = NULL; if (!g_strcmp0(priv->pipeline_string, pipeline_string)) return; g_free(priv->pipeline_string); priv->pipeline_string = g_strdup(pipeline_string); gv_engine_reload_pipeline(self); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PIPELINE_STRING]); } static void gv_engine_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvEngine *self = GV_ENGINE(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_PLAYBACK_STATE: g_value_set_enum(value, gv_engine_get_state(self)); break; case PROP_STATION: g_value_set_object(value, gv_engine_get_station(self)); break; case PROP_STREAMINFO: g_value_set_boxed(value, gv_engine_get_streaminfo(self)); break; case PROP_METADATA: g_value_set_boxed(value, gv_engine_get_metadata(self)); break; case PROP_VOLUME: g_value_set_uint(value, gv_engine_get_volume(self)); break; case PROP_MUTE: g_value_set_boolean(value, gv_engine_get_mute(self)); break; case PROP_PIPELINE_ENABLED: g_value_set_boolean(value, gv_engine_get_pipeline_enabled(self)); break; case PROP_PIPELINE_STRING: g_value_set_string(value, gv_engine_get_pipeline_string(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_engine_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvEngine *self = GV_ENGINE(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_VOLUME: gv_engine_set_volume(self, g_value_get_uint(value)); break; case PROP_MUTE: gv_engine_set_mute(self, g_value_get_boolean(value)); break; case PROP_PIPELINE_ENABLED: gv_engine_set_pipeline_enabled(self, g_value_get_boolean(value)); break; case PROP_PIPELINE_STRING: gv_engine_set_pipeline_string(self, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public methods */ void gv_engine_play(GvEngine *self, GvStation *station) { GvEnginePrivate *priv = self->priv; const gchar *station_stream_uri; g_return_if_fail(station != NULL); /* Station must have a stream uri */ station_stream_uri = gv_station_get_first_stream_uri(station); if (station_stream_uri == NULL) { WARNING("Station '%s' has no stream uri", gv_station_get_name_or_uri(station)); return; } /* Cleanup error handling */ priv->error_count = 0; g_clear_handle_id(&priv->start_playback_timeout_id, g_source_remove); /* Set station */ gv_engine_set_station(self, station); /* According to the doc: * * > State changes to GST_STATE_READY or GST_STATE_NULL never return * > GST_STATE_CHANGE_ASYNC. * * https://gstreamer.freedesktop.org/documentation/gstreamer/gstelement.html#gst_element_set_state */ /* Ensure playback is stopped */ set_gst_state(priv->playbin, GST_STATE_NULL); /* Set the stream uri */ g_object_set(priv->playbin, "uri", station_stream_uri, NULL); /* Go to the ready stop (not sure it's needed) */ set_gst_state(priv->playbin, GST_STATE_READY); /* Set gst state to PAUSE, so that the playbin starts buffering data. * Playback will start as soon as buffering is finished. */ set_gst_state(priv->playbin, GST_STATE_PAUSED); gv_engine_set_state(self, GV_ENGINE_STATE_CONNECTING); } void gv_engine_stop(GvEngine *self) { GvEnginePrivate *priv = self->priv; /* Cleanup error handling */ priv->error_count = 0; g_clear_handle_id(&priv->start_playback_timeout_id, g_source_remove); /* Radical way to stop: set state to NULL */ set_gst_state(priv->playbin, GST_STATE_NULL); gv_engine_set_state(self, GV_ENGINE_STATE_STOPPED); gv_engine_unset_streaminfo(self); gv_engine_unset_metadata(self); } GvEngine * gv_engine_new(void) { return g_object_new(GV_TYPE_ENGINE, NULL); } /* * GStreamer playbin signal handlers */ static void on_playbin_audio_pad_notify_caps(GstPad *pad, GParamSpec *pspec, GvEngine *self) { /* WARNING! We're likely in the GStreamer streaming thread! */ const gchar *property_name = g_param_spec_get_name(pspec); GstElement *playbin = self->priv->playbin; GstMessage *msg; TRACE("%p, %s, %p", pad, property_name, self); msg = gst_message_new_application(GST_OBJECT(playbin), gst_structure_new_empty("audio-caps-changed")); gst_element_post_message(playbin, msg); } static void on_playbin_element_setup(GstElement *playbin G_GNUC_UNUSED, GstElement *element, GvEngine *self) { /* WARNING! We're likely in the GStreamer streaming thread! */ #ifdef DEBUG_GST_ELEMENT_SETUP gchar *element_name; element_name = gst_element_get_name(element); DEBUG("Element setup: %s", element_name); g_free(element_name); #endif gv_engine_update_streaminfo_from_element_setup(self, element); } static void on_playbin_source_setup(GstElement *playbin G_GNUC_UNUSED, GstElement *source, GvEngine *self) { /* WARNING! We're likely in the GStreamer streaming thread! */ GvEnginePrivate *priv = self->priv; GvStation *station = priv->station; static gchar *default_user_agent; const gchar *user_agent; gboolean ssl_strict; if (station == NULL) return; if (default_user_agent == NULL) { gchar *gst_version; gst_version = gst_version_string(); default_user_agent = g_strdup_printf("%s %s", gv_core_user_agent, gst_version); g_free(gst_version); } user_agent = gv_station_get_user_agent(station); if (user_agent == NULL) user_agent = default_user_agent; ssl_strict = gv_station_get_insecure(station) ? FALSE : TRUE; g_object_set(source, "user-agent", user_agent, "ssl-strict", ssl_strict, NULL); DEBUG("Source setup: ssl-strict=%s, user-agent='%s'", ssl_strict ? "true" : "false", user_agent); } /* * GStreamer bus signal handlers */ static gboolean when_timeout_start_playback(gpointer data) { GvEngine *self = GV_ENGINE(data); GvEnginePrivate *priv = self->priv; if (self->priv->state != GV_ENGINE_STATE_STOPPED) { set_gst_state(priv->playbin, GST_STATE_NULL); set_gst_state(priv->playbin, GST_STATE_READY); set_gst_state(priv->playbin, GST_STATE_PAUSED); gv_engine_set_state(self, GV_ENGINE_STATE_CONNECTING); } priv->start_playback_timeout_id = 0; return G_SOURCE_REMOVE; } static void retry_playback(GvEngine *self) { GvEnginePrivate *priv = self->priv; guint delay; /* We retry playback after there's been a failure of some sort. * We don't know what kind of failure, and maybe the network is down, * in such case we don't want to keep retrying in a wild loop. So the * strategy here is to start retrying with a minimal delay, and increment * the delay with the failure count. */ if (priv->start_playback_timeout_id != 0) return; g_assert(priv->error_count > 0); delay = priv->error_count - 1; if (delay > 10) delay = 10; INFO("Restarting playback in %u seconds", delay); priv->start_playback_timeout_id = g_timeout_add_seconds(delay, when_timeout_start_playback, self); } static void on_bus_message_eos(GstBus *bus G_GNUC_UNUSED, GstMessage *msg G_GNUC_UNUSED, GvEngine *self) { GvEnginePrivate *priv = self->priv; INFO("Gst bus EOS message"); priv->error_count++; /* Stop immediately otherwise gst keeps on spitting errors */ set_gst_state(priv->playbin, GST_STATE_NULL); /* Restart playback if needed */ if (self->priv->state != GV_ENGINE_STATE_STOPPED) retry_playback(self); /* Emit an error */ //gv_errorable_emit_error(GV_ERRORABLE(self), "%s", _("End of stream")); } static void on_bus_message_error(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, GvEngine *self) { GvEnginePrivate *priv = self->priv; GError *err; gchar *debug; priv->error_count++; /* Parse message */ gst_message_parse_error(msg, &err, &debug); /* Display error */ WARNING("Gst bus error msg: %s:%d: %s", g_quark_to_string(err->domain), err->code, err->message); WARNING("Gst bus error debug: %s", debug); /* Stop playback otherwise gst keeps on spitting errors */ set_gst_state(priv->playbin, GST_STATE_NULL); /* Here comes the actual effort to handle errors. At the moment there's * not much to it, we only handle SSL failures. */ if (g_error_matches(err, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_READ)) { const GstStructure *details = NULL; gst_message_parse_error_details(msg, &details); if (details && gst_structure_has_field_typed(details, "http-status-code", G_TYPE_UINT)) { guint code = 0; gst_structure_get_uint(details, "http-status-code", &code); if (code == SOUP_STATUS_SSL_FAILED) g_signal_emit(self, signals[SIGNAL_SSL_FAILURE], 0, err->message, debug); } } else { /* When in doubt, retry! */ if (self->priv->state != GV_ENGINE_STATE_STOPPED) retry_playback(self); } /* Cleanup */ g_error_free(err); g_free(debug); /* Emit an error signal */ //gv_errorable_emit_error(GV_ERRORABLE(self), "GStreamer error: %s", err->message); /* Here are some gst error messages that I've dealt with so far. * * GST_RESOURCE_ERROR: GST_RESOURCE_ERROR_OPEN_READ * * Secure connection setup failed. * * The protocol is https, however the server failed to return a valid * certificate. Note that souphttpsrc has a 'ssl-strict' property that * default to true. Setting it to false might be enough to play the stream. * * See #128. * * GST_RESOURCE_ERROR: GST_RESOURCE_ERROR_NOT_FOUND * * Could not resolve server name. * * This might happen for different reasons: * - wrong url, the protocol (http, likely) is correct, but the name of * the server is wrong. * - name resolution is down. * - network is down. * * To reproduce: * - in the url, set a wrong server name. * - just disconnect from the network. * * File Not Found * * It means that the protocol (http, likely) is correct, the server * name as well, but the file is wrong or couldn't be found. I guess * the server could be reached and replied something like "file not found". * * To reproduce: at the end of the url, set a wrong filename * * Not Available * * Don't remember about this one... * * GST_CORE_ERROR: GST_CORE_ERROR_MISSING_PLUGIN * * No URI handler implemented for ... * * It means that the protocol is wrong or unsupported. * * To reproduce: in the url, replace 'http' by some random stuff * * Your GStreamer installation is missing a plug-in. * * It means that gstreamer doesn't know what to do with what it received * from the server. So it's likely that the server didn't send the stream * expected. And it's even more likely that the server returned an html * page, because that's what servers usually do. * Some cases where it can happen: * - wrong url, that exists but is not a stream. * - outgoing requests are blocked, and return an html page. For example, * you're connected to a public Wi-Fi that expects you to authenticate * or agree the terms of use, before allowing outgoing traffic. * * To reproduce: in the url, remove anything after the server name */ } static void on_bus_message_warning(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, GvEngine *self G_GNUC_UNUSED) { GError *warning; gchar *debug; /* Parse message */ gst_message_parse_warning(msg, &warning, &debug); /* Display warning */ DEBUG("Gst bus warning msg: %s:%d: %s", g_quark_to_string(warning->domain), warning->code, warning->message); DEBUG("Gst bus warning debug : %s", debug); /* Cleanup */ g_error_free(warning); g_free(debug); } static void on_bus_message_info(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, GvEngine *self G_GNUC_UNUSED) { GError *info; gchar *debug; /* Parse message */ gst_message_parse_info(msg, &info, &debug); /* Display info */ DEBUG("Gst bus info msg: %s:%d: %s", g_quark_to_string(info->domain), info->code, info->message); DEBUG("Gst bus info debug : %s", debug); /* Cleanup */ g_error_free(info); g_free(debug); } #ifdef DEBUG_GST_TAGS static void tag_list_foreach_dump(const GstTagList *list, const gchar *tag, gpointer data G_GNUC_UNUSED) { gchar *str = NULL; switch (gst_tag_get_type(tag)) { case G_TYPE_STRING: gst_tag_list_get_string_index(list, tag, 0, &str); break; case G_TYPE_INT: { gint val; gst_tag_list_get_int_index(list, tag, 0, &val); str = g_strdup_printf("%d", val); break; } case G_TYPE_UINT: { guint val; gst_tag_list_get_uint_index(list, tag, 0, &val); str = g_strdup_printf("%u", val); break; } case G_TYPE_DOUBLE: { gdouble val; gst_tag_list_get_double_index(list, tag, 0, &val); str = g_strdup_printf("%lg", val); break; } case G_TYPE_BOOLEAN: { gboolean val; gst_tag_list_get_boolean_index(list, tag, 0, &val); str = g_strdup_printf("%s", val ? "true" : "false"); break; } } DEBUG("%10s '%20s' (%d elem): %s", g_type_name(gst_tag_get_type(tag)), tag, gst_tag_list_get_tag_size(list, tag), str); g_free(str); } #endif /* DEBUG_GST_TAGS */ static void on_bus_message_tag(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, GvEngine *self) { GstTagList *taglist = NULL; TRACE("... %s, %p", GST_MESSAGE_SRC_NAME(msg), self); gst_message_parse_tag(msg, &taglist); #ifdef DEBUG_GST_TAGS DEBUG("->>- GST TAGLIST DUMP ->>-->>-->>-->>-->>-->>-->>-->>----"); gst_tag_list_foreach(taglist, (GstTagForeachFunc) tag_list_foreach_dump, NULL); DEBUG("-<<--<<--<<--<<--<<--<<--<<--<<--<<--<<--<<--<<--<<--<<--"); #endif /* DEBUG_GST_TAGS */ gv_engine_update_streaminfo_from_tags(self, taglist); gv_engine_update_metadata_from_tags(self, taglist); gst_tag_list_unref(taglist); } static void on_bus_message_buffering(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, GvEngine *self) { GvEnginePrivate *priv = self->priv; static gint prev_percent = 0; gint percent = 0; /* Handle the buffering message. Some documentation: * https://gstreamer.freedesktop.org/documentation/gstreamer/gstmessage.html#gst_message_new_buffering */ /* Parse message */ gst_message_parse_buffering(msg, &percent); /* Display buffering steps 20 by 20 */ if (ABS(percent - prev_percent) > 20) { prev_percent = percent; DEBUG("Buffering (%3u %%)", percent); } /* Now, let's react according to our current state */ switch (priv->state) { case GV_ENGINE_STATE_STOPPED: /* This shouldn't happen */ WARNING("Received 'bus buffering' while stopped"); break; case GV_ENGINE_STATE_CONNECTING: /* We successfully connected! */ gv_engine_set_state(self, GV_ENGINE_STATE_BUFFERING); /* NO BREAK HERE! * This is to handle the (very special) case where the first * buffering message received is already 100%. I'm not sure this * can happen, but it doesn't hurt to be ready. * * To avoid warnings, the next line could be: * __attribute__((fallthrough)); * * However this is gcc-7 only, and I don't know about clang. * So we use a more portable (and more magic) marker comment. */ // fall through case GV_ENGINE_STATE_BUFFERING: /* When buffering complete, start playing */ if (percent >= 100) { DEBUG("Buffering complete, starting playback"); set_gst_state(priv->playbin, GST_STATE_PLAYING); gv_engine_set_state(self, GV_ENGINE_STATE_PLAYING); priv->error_count = 0; } break; case GV_ENGINE_STATE_PLAYING: /* In case buffering is < 100%, according to the documentation, * we should pause. However, more than often, I constantly * receive 'buffering < 100%' messages. In such cases, following * the doc and pausing/playing makes constantly cuts the sound. * While ignoring the messages works just fine, do let's ignore * for now. */ if (percent < 100) { DEBUG("Buffering < 100%%, ignoring instead of setting to pause"); //set_gst_state(priv->playbin, GST_STATE_PAUSED); //gv_engine_set_state(self, GV_ENGINE_STATE_BUFFERING); } break; default: WARNING("Unhandled engine state %d", priv->state); } } static void on_bus_message_state_changed(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, GvEngine *self G_GNUC_UNUSED) { #ifdef DEBUG_GST_STATE_CHANGES GstState old, new, pending; /* Parse message */ gst_message_parse_state_changed(msg, &old, &new, &pending); /* Just used for debug */ DEBUG("Gst state changed: old: %s, new: %s, pending: %s", gst_element_state_get_name(old), gst_element_state_get_name(new), gst_element_state_get_name(pending)); #else (void) msg; #endif } static void on_bus_message_stream_start(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, GvEngine *self) { GvEnginePrivate *priv = self->priv; GstElement *playbin = priv->playbin; GstPad *pad = NULL; TRACE("... %s, %p", GST_MESSAGE_SRC_NAME(msg), self); DEBUG("Stream started"); g_signal_emit_by_name(playbin, "get-audio-pad", 0, &pad); if (pad == NULL) { DEBUG("No audio pad after stream started"); return; } g_signal_connect_object(pad, "notify::caps", G_CALLBACK(on_playbin_audio_pad_notify_caps), self, 0); gv_engine_update_streaminfo_from_audio_pad(self, pad); } static void on_bus_message_application(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, GvEngine *self) { GvEnginePrivate *priv = self->priv; const GstStructure *s; const gchar *msg_name; TRACE("... %s, %p", GST_MESSAGE_SRC_NAME(msg), self); s = gst_message_get_structure(msg); msg_name = gst_structure_get_name(s); g_return_if_fail(msg_name != NULL); if (!g_strcmp0(msg_name, "audio-caps-changed")) { GstElement *playbin = priv->playbin; GstPad *pad = NULL; g_signal_emit_by_name(playbin, "get-audio-pad", 0, &pad); gv_engine_update_streaminfo_from_audio_pad(self, pad); } else { WARNING("Unhandled application message %s", msg_name); } } /* * GObject methods */ static void gv_engine_finalize(GObject *object) { GvEngine *self = GV_ENGINE(object); GvEnginePrivate *priv = self->priv; TRACE("%p", object); /* Remove pending operations */ g_clear_handle_id(&priv->start_playback_timeout_id, g_source_remove); /* Stop playback */ set_gst_state(priv->playbin, GST_STATE_NULL); /* Unref the bus */ gst_bus_remove_signal_watch(priv->bus); gst_object_unref(priv->bus); /* Unref the playbin */ gst_object_unref(priv->playbin); /* Unref metadata */ g_clear_object(&priv->station); gv_clear_streaminfo(&priv->streaminfo); gv_clear_metadata(&priv->metadata); /* Free resources */ g_free(priv->pipeline_string); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_engine, object); } static void gv_engine_constructed(GObject *object) { GvEngine *self = GV_ENGINE(object); GvEnginePrivate *priv = self->priv; GstElement *playbin; GstElement *fakesink; GstBus *bus; /* Initialize properties */ priv->volume = DEFAULT_VOLUME; priv->mute = DEFAULT_MUTE; priv->pipeline_enabled = FALSE; priv->pipeline_string = NULL; /* GStreamer must be initialized, let's check that */ g_assert(gst_is_initialized()); /* Make the playbin - returns floating ref */ playbin = gst_element_factory_make("playbin", "playbin"); g_assert_nonnull(playbin); priv->playbin = g_object_ref_sink(playbin); /* Disable video - returns floating ref */ fakesink = gst_element_factory_make("fakesink", "fakesink"); g_assert_nonnull(fakesink); g_object_set(playbin, "video-sink", fakesink, NULL); /* Connect playbin signal handlers */ g_signal_connect_object(playbin, "element-setup", G_CALLBACK(on_playbin_element_setup), self, 0); g_signal_connect_object(playbin, "source-setup", G_CALLBACK(on_playbin_source_setup), self, 0); /* Get a reference to the message bus - returns full ref */ bus = gst_element_get_bus(playbin); g_assert_nonnull(bus); priv->bus = bus; /* Add a bus signal watch (so that 'message' signals are emitted) */ gst_bus_add_signal_watch(bus); /* Connect bus signal handlers */ g_signal_connect_object(bus, "message::eos", G_CALLBACK(on_bus_message_eos), self, 0); g_signal_connect_object(bus, "message::error", G_CALLBACK(on_bus_message_error), self, 0); g_signal_connect_object(bus, "message::warning", G_CALLBACK(on_bus_message_warning), self, 0); g_signal_connect_object(bus, "message::info", G_CALLBACK(on_bus_message_info), self, 0); g_signal_connect_object(bus, "message::tag", G_CALLBACK(on_bus_message_tag), self, 0); g_signal_connect_object(bus, "message::buffering", G_CALLBACK(on_bus_message_buffering), self, 0); g_signal_connect_object(bus, "message::state-changed", G_CALLBACK(on_bus_message_state_changed), self, 0); g_signal_connect_object(bus, "message::stream-start", G_CALLBACK(on_bus_message_stream_start), self, 0); g_signal_connect_object(bus, "message::application", G_CALLBACK(on_bus_message_application), self, 0); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_engine, object); } static void gv_engine_init(GvEngine *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_engine_get_instance_private(self); } static void gv_engine_class_init(GvEngineClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_engine_finalize; object_class->constructed = gv_engine_constructed; /* Properties */ object_class->get_property = gv_engine_get_property; object_class->set_property = gv_engine_set_property; properties[PROP_PLAYBACK_STATE] = g_param_spec_enum("playback-state", "Playback state", NULL, GV_TYPE_ENGINE_STATE, GV_ENGINE_STATE_STOPPED, GV_PARAM_READABLE); properties[PROP_STATION] = g_param_spec_object("station", "Current station", NULL, GV_TYPE_STATION, GV_PARAM_READABLE); properties[PROP_STREAMINFO] = g_param_spec_boxed("streaminfo", "Stream information", NULL, GV_TYPE_STREAMINFO, GV_PARAM_READABLE); properties[PROP_METADATA] = g_param_spec_boxed("metadata", "Stream metadata", NULL, GV_TYPE_METADATA, GV_PARAM_READABLE); properties[PROP_VOLUME] = g_param_spec_uint("volume", "Volume in percent", NULL, 0, 100, DEFAULT_VOLUME, GV_PARAM_READWRITE); properties[PROP_MUTE] = g_param_spec_boolean("mute", "Mute", NULL, FALSE, GV_PARAM_READWRITE); properties[PROP_PIPELINE_ENABLED] = g_param_spec_boolean("pipeline-enabled", "Enable custom pipeline", NULL, FALSE, GV_PARAM_READWRITE); properties[PROP_PIPELINE_STRING] = g_param_spec_string("pipeline-string", "Custom pipeline string", NULL, NULL, GV_PARAM_READWRITE); g_object_class_install_properties(object_class, PROP_N, properties); /* Signals */ signals[SIGNAL_SSL_FAILURE] = g_signal_new("ssl-failure", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); } goodvibes-v0.7.2/src/core/gv-engine.h000066400000000000000000000040651414415210700174420ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include #include "core/gv-station.h" #include "core/gv-metadata.h" #include "core/gv-streaminfo.h" /* GObject declarations */ #define GV_TYPE_ENGINE gv_engine_get_type() G_DECLARE_FINAL_TYPE(GvEngine, gv_engine, GV, ENGINE, GObject) /* Data types */ typedef enum { GV_ENGINE_STATE_STOPPED = 0, GV_ENGINE_STATE_CONNECTING, GV_ENGINE_STATE_BUFFERING, GV_ENGINE_STATE_PLAYING } GvEngineState; /* Methods */ GvEngine *gv_engine_new (void); void gv_engine_play(GvEngine *self, GvStation *station); void gv_engine_stop(GvEngine *self); /* Property accessors */ GvEngineState gv_engine_get_state (GvEngine *self); GvStreaminfo *gv_engine_get_streaminfo (GvEngine *self); GvMetadata *gv_engine_get_metadata (GvEngine *self); guint gv_engine_get_volume (GvEngine *self); void gv_engine_set_volume (GvEngine *self, guint volume); gboolean gv_engine_get_mute (GvEngine *self); void gv_engine_set_mute (GvEngine *self, gboolean mute); gboolean gv_engine_get_pipeline_enabled(GvEngine *self); void gv_engine_set_pipeline_enabled(GvEngine *self, gboolean enabled); const gchar *gv_engine_get_pipeline_string (GvEngine *self); void gv_engine_set_pipeline_string (GvEngine *self, const gchar *pipeline); goodvibes-v0.7.2/src/core/gv-metadata.c000066400000000000000000000131341414415210700177450ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ /** * SECTION:gv-metadata * @title: GvMetadata * @short_description: Non-technical information about a stream * * Metadata is a term that appears in the GStreamer documentation, * so you might want to read the GStreamer documentation first: * * * Metadata are tags that describe the non-technical parts of stream content. * * A GvMetadata object is created and updated by GvEngine, according to GStreamer * tags. That's all. */ #include #include #include #include "base/gv-base.h" #include "core/gv-metadata.h" /* * GObject definitions */ G_DEFINE_BOXED_TYPE(GvMetadata, gv_metadata, gv_metadata_ref, gv_metadata_unref); struct _GvMetadata { gchar *album; gchar *artist; gchar *comment; gchar *genre; gchar *title; gchar *year; // WISHED Label // WISHED Cover // WISHED Organization /*< private >*/ volatile guint ref_count; }; /* * Public methods */ const gchar * gv_metadata_get_album(GvMetadata *self) { return self->album; } const gchar * gv_metadata_get_artist(GvMetadata *self) { return self->artist; } const gchar * gv_metadata_get_comment(GvMetadata *self) { return self->comment; } const gchar * gv_metadata_get_genre(GvMetadata *self) { return self->genre; } const gchar * gv_metadata_get_title(GvMetadata *self) { return self->title; } const gchar * gv_metadata_get_year(GvMetadata *self) { return self->year; } gchar * gv_metadata_make_title_artist(GvMetadata *self, gboolean escape) { gchar *str; g_return_val_if_fail(self != NULL, NULL); if (self->artist && self->title) str = g_strdup_printf("%s - %s", self->title, self->artist); else if (self->title) str = g_strdup_printf("%s", self->title); else if (self->artist) str = g_strdup_printf("%s", self->artist); else str = NULL; if (str && escape == TRUE) { gchar *str2 = g_markup_escape_text(str, -1); g_free(str); str = str2; } return str; } gchar * gv_metadata_make_album_year(GvMetadata *self, gboolean escape) { gchar *str; g_return_val_if_fail(self != NULL, NULL); if (self->album && self->year) str = g_strdup_printf("%s (%s)", self->album, self->year); else if (self->album) str = g_strdup_printf("%s", self->album); else if (self->year) str = g_strdup_printf("(%s)", self->year); else str = NULL; if (str && escape == TRUE) { gchar *str2 = g_markup_escape_text(str, -1); g_free(str); str = str2; } return str; } static gboolean update_str(GstTagList *taglist, const gchar *tag, gchar **out) { const gchar *str = NULL; gboolean changed = FALSE; g_return_val_if_fail(out != NULL, FALSE); gst_tag_list_peek_string_index(taglist, tag, 0, &str); if (g_strcmp0(str, *out)) { g_free(*out); *out = g_strdup(str); changed = TRUE; } return changed; } static gboolean update_date(GstTagList *taglist, const gchar *tag, gchar **out) { GDate *date = NULL; gboolean changed = FALSE; g_return_val_if_fail(out != NULL, FALSE); gst_tag_list_get_date_index(taglist, tag, 0, &date); if (date) { gchar *year; year = g_date_valid(date) ? g_strdup_printf("%d", g_date_get_year(date)) : NULL; if (g_strcmp0(year, *out)) { g_free(*out); *out = year; changed = TRUE; } g_date_free(date); } else { if (*out != NULL) { g_free(*out); *out = NULL; changed = TRUE; } } return changed; } gboolean gv_metadata_update_from_gst_taglist(GvMetadata *self, GstTagList *taglist) { gboolean changed = FALSE; g_return_val_if_fail(self != NULL, FALSE); g_return_val_if_fail(taglist != NULL, FALSE); changed |= update_str(taglist, GST_TAG_ALBUM, &self->album); changed |= update_str(taglist, GST_TAG_ARTIST, &self->artist); changed |= update_str(taglist, GST_TAG_COMMENT, &self->comment); changed |= update_str(taglist, GST_TAG_GENRE, &self->genre); changed |= update_str(taglist, GST_TAG_TITLE, &self->title); changed |= update_date(taglist, GST_TAG_DATE, &self->year); return changed; } gboolean gv_metadata_is_empty(GvMetadata *self) { g_return_val_if_fail(self != NULL, TRUE); return self->album == NULL && self->artist == NULL && self->comment == NULL && self->genre == NULL && self->title == NULL && self->year == NULL; } void gv_metadata_unref(GvMetadata *self) { g_return_if_fail(self != NULL); g_return_if_fail(g_atomic_int_get(&self->ref_count) > 0); if (!g_atomic_int_dec_and_test(&self->ref_count)) return; g_free(self->album); g_free(self->artist); g_free(self->comment); g_free(self->genre); g_free(self->title); g_free(self->year); g_free(self); } GvMetadata * gv_metadata_ref(GvMetadata *self) { g_return_val_if_fail(self != NULL, NULL); g_return_val_if_fail(g_atomic_int_get(&self->ref_count) > 0, NULL); g_atomic_int_inc(&self->ref_count); return self; } GvMetadata * gv_metadata_new(void) { GvMetadata *self; self = g_new0(GvMetadata, 1); self->ref_count = 1; return self; } goodvibes-v0.7.2/src/core/gv-metadata.h000066400000000000000000000034341414415210700177540ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_METADATA gv_metadata_get_type() GType gv_metadata_get_type(void) G_GNUC_CONST; typedef struct _GvMetadata GvMetadata; /* Methods */ GvMetadata *gv_metadata_new (void); GvMetadata *gv_metadata_ref (GvMetadata *self); void gv_metadata_unref(GvMetadata *self); #define gv_clear_metadata(object_ptr) \ g_clear_pointer((object_ptr), gv_metadata_unref) gboolean gv_metadata_is_empty (GvMetadata *self); gboolean gv_metadata_update_from_gst_taglist(GvMetadata *self, GstTagList *taglist); gchar *gv_metadata_make_title_artist(GvMetadata *self, gboolean escape); gchar *gv_metadata_make_album_year (GvMetadata *self, gboolean escape); const gchar *gv_metadata_get_album (GvMetadata *self); const gchar *gv_metadata_get_artist (GvMetadata *self); const gchar *gv_metadata_get_comment(GvMetadata *self); const gchar *gv_metadata_get_genre (GvMetadata *self); const gchar *gv_metadata_get_title (GvMetadata *self); const gchar *gv_metadata_get_year (GvMetadata *self); goodvibes-v0.7.2/src/core/gv-player.c000066400000000000000000000612141414415210700174630ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core-enum-types.h" #include "core/gv-core-internal.h" #include "core/gv-engine.h" #include "core/gv-metadata.h" #include "core/gv-station-list.h" #include "core/gv-station.h" #include "core/gv-streaminfo.h" #include "core/gv-player.h" /* * Properties */ #define DEFAULT_VOLUME 100 #define DEFAULT_MUTE FALSE #define DEFAULT_REPEAT FALSE #define DEFAULT_SHUFFLE FALSE #define DEFAULT_AUTOPLAY FALSE enum { /* Reserved */ PROP_0, /* Construct-only properties */ PROP_ENGINE, PROP_STATION_LIST, /* Engine mirrored properties */ PROP_STREAMINFO, PROP_METADATA, PROP_VOLUME, PROP_MUTE, PROP_PIPELINE_ENABLED, PROP_PIPELINE_STRING, /* Properties */ PROP_PLAYBACK_STATE, PROP_REPEAT, PROP_SHUFFLE, PROP_AUTOPLAY, PROP_STATION, PROP_STATION_URI, PROP_PREV_STATION, PROP_NEXT_STATION, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * Signals */ enum { SIGNAL_SSL_FAILURE, /* Number of signals */ SIGNAL_N }; static guint signals[SIGNAL_N]; /* * GObject definitions */ typedef enum { GV_PLAYER_WISH_TO_STOP, GV_PLAYER_WISH_TO_PLAY, } GvPlayerWish; struct _GvPlayerPrivate { /* Construct-only properties */ GvEngine *engine; GvStationList *station_list; /* Properties */ GvPlaybackState state; gboolean repeat; gboolean shuffle; gboolean autoplay; /* Current station */ GvStation *station; /* Wished state */ GvPlayerWish wish; }; typedef struct _GvPlayerPrivate GvPlayerPrivate; struct _GvPlayer { /* Parent instance structure */ GObject parent_instance; /* Private data */ GvPlayerPrivate *priv; }; static void gv_player_configurable_interface_init(GvConfigurableInterface *iface); G_DEFINE_TYPE_WITH_CODE(GvPlayer, gv_player, G_TYPE_OBJECT, G_ADD_PRIVATE(GvPlayer) G_IMPLEMENT_INTERFACE(GV_TYPE_CONFIGURABLE, gv_player_configurable_interface_init) G_IMPLEMENT_INTERFACE(GV_TYPE_ERRORABLE, NULL)) /* * Playback */ const gchar * gv_playback_state_to_string(GvPlaybackState state) { const gchar *str; switch (state) { case GV_PLAYBACK_STATE_PLAYING: str = _("Playing"); break; case GV_PLAYBACK_STATE_CONNECTING: str = _("Connecting…"); break; case GV_PLAYBACK_STATE_BUFFERING: str = _("Buffering…"); break; case GV_PLAYBACK_STATE_STOPPED: default: str = _("Stopped"); break; } return str; } /* * Signal handlers */ static void gv_player_set_playback_state(GvPlayer *self, GvPlaybackState value); static void on_station_notify(GvStation *station, GParamSpec *pspec, GvPlayer *self) { GvPlayerPrivate *priv = self->priv; const gchar *property_name = g_param_spec_get_name(pspec); TRACE("%p, %s, %p", station, property_name, self); g_assert(station == priv->station); if (!g_strcmp0(property_name, "stream-uris")) { DEBUG("Station %p: stream URIs have changed", station); /* Check if there are some streams, and start playing if needed */ if (gv_station_get_stream_uris(station)) if (priv->wish == GV_PLAYER_WISH_TO_PLAY) gv_player_play(self); } /* In any case, we notify if something was changed in the station */ g_object_notify(G_OBJECT(self), "station"); } static void on_engine_notify(GvEngine *engine, GParamSpec *pspec, GvPlayer *self) { GvPlayerPrivate *priv = self->priv; const gchar *property_name = g_param_spec_get_name(pspec); TRACE("%p, %s, %p", engine, property_name, self); if (!g_strcmp0(property_name, "streaminfo")) { g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_STREAMINFO]); } else if (!g_strcmp0(property_name, "metadata")) { g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_METADATA]); } else if (!g_strcmp0(property_name, "volume")) { g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_VOLUME]); } else if (!g_strcmp0(property_name, "mute")) { g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_MUTE]); } else if (!g_strcmp0(property_name, "pipeline-enabled")) { g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PIPELINE_ENABLED]); } else if (!g_strcmp0(property_name, "pipeline-string")) { g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PIPELINE_STRING]); } else if (!g_strcmp0(property_name, "playback-state")) { GvEngineState engine_state; GvPlaybackState playback_state; engine_state = gv_engine_get_state(priv->engine); /* Map engine state to player state - trivial */ switch (engine_state) { case GV_ENGINE_STATE_STOPPED: playback_state = GV_PLAYBACK_STATE_STOPPED; break; case GV_ENGINE_STATE_CONNECTING: playback_state = GV_PLAYBACK_STATE_CONNECTING; break; case GV_ENGINE_STATE_BUFFERING: playback_state = GV_PLAYBACK_STATE_BUFFERING; break; case GV_ENGINE_STATE_PLAYING: playback_state = GV_PLAYBACK_STATE_PLAYING; break; default: ERROR("Unhandled engine state: %d", engine_state); /* Program execution stops here */ break; } /* Set state */ gv_player_set_playback_state(self, playback_state); } } static void on_engine_error(GvEngine *engine G_GNUC_UNUSED, const gchar *error_string G_GNUC_UNUSED, GvPlayer *self) { /* Whatever the error, just stop */ gv_player_stop(self); } static void on_engine_ssl_failure(GvEngine *engine G_GNUC_UNUSED, const gchar *error, const gchar *debug, GvPlayer *self) { /* Just forward the signal ... */ g_signal_emit(self, signals[SIGNAL_SSL_FAILURE], 0, error, debug); } /* * Property accessors - construct-only properties */ static void gv_player_set_engine(GvPlayer *self, GvEngine *engine) { GvPlayerPrivate *priv = self->priv; /* This is a construct-only property */ g_assert_null(priv->engine); g_assert_nonnull(engine); priv->engine = g_object_ref(engine); /* Some signal handlers */ g_signal_connect_object(engine, "notify", G_CALLBACK(on_engine_notify), self, 0); g_signal_connect_object(engine, "error", G_CALLBACK(on_engine_error), self, 0); g_signal_connect_object(engine, "ssl-failure", G_CALLBACK(on_engine_ssl_failure), self, 0); } static void gv_player_set_station_list(GvPlayer *self, GvStationList *station_list) { GvPlayerPrivate *priv = self->priv; /* This is a construct-only property */ g_assert_null(priv->station_list); g_assert_nonnull(station_list); priv->station_list = g_object_ref(station_list); } /* * Property accessors - engine mirrored properties * We don't notify here. It's done in the engine notify handler instead. */ GvStreaminfo * gv_player_get_streaminfo(GvPlayer *self) { GvEngine *engine = self->priv->engine; return gv_engine_get_streaminfo(engine); } GvMetadata * gv_player_get_metadata(GvPlayer *self) { GvEngine *engine = self->priv->engine; return gv_engine_get_metadata(engine); } guint gv_player_get_volume(GvPlayer *self) { GvEngine *engine = self->priv->engine; return gv_engine_get_volume(engine); } void gv_player_set_volume(GvPlayer *self, guint volume) { GvEngine *engine = self->priv->engine; gv_engine_set_volume(engine, volume); } void gv_player_lower_volume(GvPlayer *self) { guint volume; guint step = 5; volume = gv_player_get_volume(self); volume = volume > step ? volume - step : 0; gv_player_set_volume(self, volume); } void gv_player_raise_volume(GvPlayer *self) { guint volume; guint step = 5; volume = gv_player_get_volume(self); volume = volume < 100 - step ? volume + step : 100; gv_player_set_volume(self, volume); } gboolean gv_player_get_mute(GvPlayer *self) { GvEngine *engine = self->priv->engine; return gv_engine_get_mute(engine); } void gv_player_set_mute(GvPlayer *self, gboolean mute) { GvEngine *engine = self->priv->engine; gv_engine_set_mute(engine, mute); } void gv_player_toggle_mute(GvPlayer *self) { gboolean mute; mute = gv_player_get_mute(self); gv_player_set_mute(self, !mute); } gboolean gv_player_get_pipeline_enabled(GvPlayer *self) { GvEngine *engine = self->priv->engine; return gv_engine_get_pipeline_enabled(engine); } void gv_player_set_pipeline_enabled(GvPlayer *self, gboolean enabled) { GvEngine *engine = self->priv->engine; gv_engine_set_pipeline_enabled(engine, enabled); } const gchar * gv_player_get_pipeline_string(GvPlayer *self) { GvEngine *engine = self->priv->engine; return gv_engine_get_pipeline_string(engine); } void gv_player_set_pipeline_string(GvPlayer *self, const gchar *pipeline_string) { GvEngine *engine = self->priv->engine; gv_engine_set_pipeline_string(engine, pipeline_string); } /* * Property accessors - player properties */ GvPlaybackState gv_player_get_playback_state(GvPlayer *self) { return self->priv->state; } static void gv_player_set_playback_state(GvPlayer *self, GvPlaybackState state) { GvPlayerPrivate *priv = self->priv; if (priv->state == state) return; priv->state = state; g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PLAYBACK_STATE]); } gboolean gv_player_get_repeat(GvPlayer *self) { return self->priv->repeat; } void gv_player_set_repeat(GvPlayer *self, gboolean repeat) { GvPlayerPrivate *priv = self->priv; if (priv->repeat == repeat) return; priv->repeat = repeat; g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_REPEAT]); } gboolean gv_player_get_shuffle(GvPlayer *self) { return self->priv->shuffle; } void gv_player_set_shuffle(GvPlayer *self, gboolean shuffle) { GvPlayerPrivate *priv = self->priv; if (priv->shuffle == shuffle) return; priv->shuffle = shuffle; g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_SHUFFLE]); } gboolean gv_player_get_autoplay(GvPlayer *self) { return self->priv->autoplay; } void gv_player_set_autoplay(GvPlayer *self, gboolean autoplay) { GvPlayerPrivate *priv = self->priv; if (priv->autoplay == autoplay) return; priv->autoplay = autoplay; g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_AUTOPLAY]); } GvStation * gv_player_get_station(GvPlayer *self) { return self->priv->station; } GvStation * gv_player_get_prev_station(GvPlayer *self) { GvPlayerPrivate *priv = self->priv; return gv_station_list_prev(priv->station_list, priv->station, priv->repeat, priv->shuffle); } GvStation * gv_player_get_next_station(GvPlayer *self) { GvPlayerPrivate *priv = self->priv; return gv_station_list_next(priv->station_list, priv->station, priv->repeat, priv->shuffle); } static const gchar * gv_player_get_station_uri(GvPlayer *self) { GvStation *station; const gchar *station_uri; station = gv_player_get_station(self); if (station == NULL) return NULL; station_uri = gv_station_get_uri(station); return station_uri; } void gv_player_set_station(GvPlayer *self, GvStation *station) { GvPlayerPrivate *priv = self->priv; if (station == priv->station) return; if (priv->station) { g_signal_handlers_disconnect_by_data(priv->station, self); g_object_unref(priv->station); priv->station = NULL; } if (station) { priv->station = g_object_ref_sink(station); g_signal_connect_object(priv->station, "notify", G_CALLBACK(on_station_notify), self, 0); } g_object_notify(G_OBJECT(self), "station"); g_object_notify(G_OBJECT(self), "station-uri"); INFO("Station set to '%s'", station ? gv_station_get_name_or_uri(station) : NULL); } gboolean gv_player_set_station_by_name(GvPlayer *self, const gchar *name) { GvPlayerPrivate *priv = self->priv; GvStation *station = NULL; station = gv_station_list_find_by_name(priv->station_list, name); if (station == NULL) { DEBUG("Station name '%s' not found in station list", name); return FALSE; } gv_player_set_station(self, station); return TRUE; } gboolean gv_player_set_station_by_uri(GvPlayer *self, const gchar *uri) { GvPlayerPrivate *priv = self->priv; GvStation *station = NULL; /* Look for the station in the station list */ station = gv_station_list_find_by_uri(priv->station_list, uri); if (station == NULL) { DEBUG("Station URI '%s' not found in station list", uri); return FALSE; } gv_player_set_station(self, station); return TRUE; } gboolean gv_player_set_station_by_guessing(GvPlayer *self, const gchar *string) { GvPlayerPrivate *priv = self->priv; GvStation *station = NULL; station = gv_station_list_find_by_guessing(priv->station_list, string); if (station == NULL) { DEBUG("'%s' not found in station list", string); return FALSE; } gv_player_set_station(self, station); return TRUE; } static void gv_player_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvPlayer *self = GV_PLAYER(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_STREAMINFO: g_value_set_boxed(value, gv_player_get_streaminfo(self)); break; case PROP_METADATA: g_value_set_boxed(value, gv_player_get_metadata(self)); break; case PROP_VOLUME: g_value_set_uint(value, gv_player_get_volume(self)); break; case PROP_MUTE: g_value_set_boolean(value, gv_player_get_mute(self)); break; case PROP_PIPELINE_ENABLED: g_value_set_boolean(value, gv_player_get_pipeline_enabled(self)); break; case PROP_PIPELINE_STRING: g_value_set_string(value, gv_player_get_pipeline_string(self)); break; case PROP_PLAYBACK_STATE: g_value_set_enum(value, gv_player_get_playback_state(self)); break; case PROP_REPEAT: g_value_set_boolean(value, gv_player_get_repeat(self)); break; case PROP_SHUFFLE: g_value_set_boolean(value, gv_player_get_shuffle(self)); break; case PROP_AUTOPLAY: g_value_set_boolean(value, gv_player_get_autoplay(self)); break; case PROP_STATION: g_value_set_object(value, gv_player_get_station(self)); break; case PROP_STATION_URI: g_value_set_string(value, gv_player_get_station_uri(self)); break; case PROP_PREV_STATION: g_value_set_object(value, gv_player_get_prev_station(self)); break; case PROP_NEXT_STATION: g_value_set_object(value, gv_player_get_next_station(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_player_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvPlayer *self = GV_PLAYER(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_ENGINE: gv_player_set_engine(self, g_value_get_object(value)); break; case PROP_STATION_LIST: gv_player_set_station_list(self, g_value_get_object(value)); break; case PROP_VOLUME: gv_player_set_volume(self, g_value_get_uint(value)); break; case PROP_MUTE: gv_player_set_mute(self, g_value_get_boolean(value)); break; case PROP_PIPELINE_ENABLED: gv_player_set_pipeline_enabled(self, g_value_get_boolean(value)); break; case PROP_PIPELINE_STRING: gv_player_set_pipeline_string(self, g_value_get_string(value)); break; case PROP_REPEAT: gv_player_set_repeat(self, g_value_get_boolean(value)); break; case PROP_SHUFFLE: gv_player_set_shuffle(self, g_value_get_boolean(value)); break; case PROP_AUTOPLAY: gv_player_set_autoplay(self, g_value_get_boolean(value)); break; case PROP_STATION: gv_player_set_station(self, g_value_get_object(value)); break; case PROP_STATION_URI: gv_player_set_station_by_uri(self, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public methods */ void gv_player_stop(GvPlayer *self) { GvPlayerPrivate *priv = self->priv; /* To remember what we're doing */ priv->wish = GV_PLAYER_WISH_TO_STOP; /* Stop playing */ gv_engine_stop(priv->engine); } void gv_player_play(GvPlayer *self) { GvPlayerPrivate *priv = self->priv; GvStation *station; GSList *uris; /* If no station is set yet, take the first from the station list */ if (priv->station == NULL) { GvStation *first_station; first_station = gv_station_list_first(priv->station_list); gv_player_set_station(self, first_station); } /* If there's still no station, return */ station = priv->station; if (station == NULL) return; /* To remember what we're doing */ priv->wish = GV_PLAYER_WISH_TO_PLAY; /* Stop playing */ gv_engine_stop(priv->engine); /* Get station data */ uris = gv_station_get_stream_uris(station); /* If there's no URIs, that probably means that the station URI * points to a playlist, and we need to download it. */ if (uris == NULL) { /* Download the playlist that contains the stream URIs */ if (!gv_station_download_playlist(station)) WARNING("Can't download playlist"); /* Downloading a playlist is an asynchronous operation. * We have nothing left to do here. */ return; } else { /* Play the station */ gv_engine_play(priv->engine, station); } } gboolean gv_player_next(GvPlayer *self) { GvPlayerPrivate *priv = self->priv; GvStation *station; station = gv_player_get_next_station(self); if (station == NULL) return FALSE; gv_player_set_station(self, station); if (priv->wish == GV_PLAYER_WISH_TO_PLAY) gv_player_play(self); return TRUE; } gboolean gv_player_prev(GvPlayer *self) { GvPlayerPrivate *priv = self->priv; GvStation *station; station = gv_player_get_prev_station(self); if (station == NULL) return FALSE; gv_player_set_station(self, station); if (priv->wish == GV_PLAYER_WISH_TO_PLAY) gv_player_play(self); return TRUE; } void gv_player_toggle(GvPlayer *self) { GvPlayerPrivate *priv = self->priv; switch (priv->wish) { case GV_PLAYER_WISH_TO_STOP: gv_player_play(self); break; case GV_PLAYER_WISH_TO_PLAY: gv_player_stop(self); break; default: ERROR("Invalid wish: %d", priv->wish); /* Program execution stops here */ break; } } void gv_player_go(GvPlayer *self, const gchar *string_to_play) { GvPlayerPrivate *priv = self->priv; /* If we have no argument, we rely on 'autoplay' */ if (string_to_play == NULL) { if (priv->autoplay) { INFO("Autoplay is enabled, let's play"); gv_player_play(self); } return; } /* If we have an argument, it might be anything. * At first, check if we find it in the station list. */ if (gv_player_set_station_by_guessing(self, string_to_play)) { INFO("'%s' found in station list, let's play", string_to_play); gv_player_play(self); return; } /* Otherwise, if it's a valid URI, try to play it */ if (is_uri_scheme_supported(string_to_play)) { GvStation *station; station = gv_station_new(NULL, string_to_play); gv_player_set_station(self, station); INFO("'%s' is a valid URI, let's play", string_to_play); gv_player_play(self); return; } /* That looks like an invalid string then */ WARNING("'%s' is neither a known station or a valid URI", string_to_play); gv_errorable_emit_error(GV_ERRORABLE(self), _("'%s' is neither a known station or a valid URI"), string_to_play); } GvPlayer * gv_player_new(GvEngine *engine, GvStationList *station_list) { return g_object_new(GV_TYPE_PLAYER, "engine", engine, "station-list", station_list, NULL); } /* * GvConfigurable interface */ static void gv_player_configure(GvConfigurable *configurable) { GvPlayer *self = GV_PLAYER(configurable); TRACE("%p", self); g_assert(gv_core_settings); g_settings_bind(gv_core_settings, "pipeline-enabled", self, "pipeline-enabled", G_SETTINGS_BIND_DEFAULT); g_settings_bind(gv_core_settings, "pipeline-string", self, "pipeline-string", G_SETTINGS_BIND_DEFAULT); g_settings_bind(gv_core_settings, "volume", self, "volume", G_SETTINGS_BIND_DEFAULT); g_settings_bind(gv_core_settings, "mute", self, "mute", G_SETTINGS_BIND_DEFAULT); g_settings_bind(gv_core_settings, "repeat", self, "repeat", G_SETTINGS_BIND_DEFAULT); g_settings_bind(gv_core_settings, "shuffle", self, "shuffle", G_SETTINGS_BIND_DEFAULT); g_settings_bind(gv_core_settings, "autoplay", self, "autoplay", G_SETTINGS_BIND_DEFAULT); g_settings_bind(gv_core_settings, "station-uri", self, "station-uri", G_SETTINGS_BIND_DEFAULT); } static void gv_player_configurable_interface_init(GvConfigurableInterface *iface) { iface->configure = gv_player_configure; } /* * GObject methods */ static void gv_player_finalize(GObject *object) { GvPlayer *self = GV_PLAYER(object); GvPlayerPrivate *priv = self->priv; TRACE("%p", object); /* Unref the current station */ if (priv->station) g_object_unref(priv->station); /* Unref the station list */ g_object_unref(priv->station_list); /* Unref the engine */ g_object_unref(priv->engine); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_player, object); } static void gv_player_constructed(GObject *object) { GvPlayer *self = GV_PLAYER(object); GvPlayerPrivate *priv = self->priv; TRACE("%p", object); /* Ensure construct-only properties have been set */ g_assert_nonnull(priv->engine); g_assert_nonnull(priv->station_list); /* Initialize properties */ priv->repeat = DEFAULT_REPEAT; priv->shuffle = DEFAULT_SHUFFLE; priv->autoplay = DEFAULT_AUTOPLAY; priv->station = NULL; /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_player, object); } static void gv_player_init(GvPlayer *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_player_get_instance_private(self); } static void gv_player_class_init(GvPlayerClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_player_finalize; object_class->constructed = gv_player_constructed; /* Properties */ object_class->get_property = gv_player_get_property; object_class->set_property = gv_player_set_property; /* Construct-only properties */ properties[PROP_ENGINE] = g_param_spec_object("engine", "Engine", NULL, GV_TYPE_ENGINE, GV_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_STATION_LIST] = g_param_spec_object("station-list", "Station list", NULL, GV_TYPE_STATION_LIST, GV_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); /* Engine mirrored properties */ properties[PROP_STREAMINFO] = g_param_spec_boxed("streaminfo", "Stream information", NULL, GV_TYPE_STREAMINFO, GV_PARAM_READABLE); properties[PROP_METADATA] = g_param_spec_boxed("metadata", "Current metadata", NULL, GV_TYPE_METADATA, GV_PARAM_READABLE); properties[PROP_VOLUME] = g_param_spec_uint("volume", "Volume in percent", NULL, 0, 100, DEFAULT_VOLUME, GV_PARAM_READWRITE); properties[PROP_MUTE] = g_param_spec_boolean("mute", "Mute", NULL, DEFAULT_MUTE, GV_PARAM_READWRITE); properties[PROP_PIPELINE_ENABLED] = g_param_spec_boolean("pipeline-enabled", "Enable custom pipeline", NULL, FALSE, GV_PARAM_READWRITE); properties[PROP_PIPELINE_STRING] = g_param_spec_string("pipeline-string", "Custom pipeline string", NULL, NULL, GV_PARAM_READWRITE); /* Player properties */ properties[PROP_PLAYBACK_STATE] = g_param_spec_enum("playback-state", "Playback state", NULL, GV_TYPE_PLAYBACK_STATE, GV_PLAYBACK_STATE_STOPPED, GV_PARAM_READABLE); properties[PROP_REPEAT] = g_param_spec_boolean("repeat", "Repeat", NULL, DEFAULT_REPEAT, GV_PARAM_READWRITE); properties[PROP_SHUFFLE] = g_param_spec_boolean("shuffle", "Shuffle", NULL, DEFAULT_SHUFFLE, GV_PARAM_READWRITE); properties[PROP_AUTOPLAY] = g_param_spec_boolean("autoplay", "Autoplay on startup", NULL, DEFAULT_AUTOPLAY, GV_PARAM_READWRITE); properties[PROP_STATION] = g_param_spec_object("station", "Current station", NULL, GV_TYPE_STATION, GV_PARAM_READWRITE); properties[PROP_STATION_URI] = g_param_spec_string("station-uri", "Current station URI", "This is used only to save the current station in conf", NULL, GV_PARAM_READWRITE); properties[PROP_PREV_STATION] = g_param_spec_object("prev", "Previous station", NULL, GV_TYPE_STATION, GV_PARAM_READABLE); properties[PROP_NEXT_STATION] = g_param_spec_object("next", "Next station", NULL, GV_TYPE_STATION, GV_PARAM_READABLE); g_object_class_install_properties(object_class, PROP_N, properties); /* Signals */ signals[SIGNAL_SSL_FAILURE] = g_signal_new("ssl-failure", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); } goodvibes-v0.7.2/src/core/gv-player.h000066400000000000000000000067561414415210700175020ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "core/gv-engine.h" #include "core/gv-metadata.h" #include "core/gv-station.h" #include "core/gv-station-list.h" #include "core/gv-streaminfo.h" /* GObject declarations */ #define GV_TYPE_PLAYER gv_player_get_type() G_DECLARE_FINAL_TYPE(GvPlayer, gv_player, GV, PLAYER, GObject) /* Data types */ typedef enum { GV_PLAYBACK_STATE_STOPPED, GV_PLAYBACK_STATE_CONNECTING, GV_PLAYBACK_STATE_BUFFERING, GV_PLAYBACK_STATE_PLAYING } GvPlaybackState; const gchar *gv_playback_state_to_string(GvPlaybackState); /* Methods */ GvPlayer *gv_player_new (GvEngine *engine, GvStationList *station_list); void gv_player_go (GvPlayer *self, const gchar *string_to_play); void gv_player_play (GvPlayer *self); void gv_player_stop (GvPlayer *self); void gv_player_toggle(GvPlayer *self); gboolean gv_player_prev (GvPlayer *self); gboolean gv_player_next (GvPlayer *self); /* Property accessors */ GvPlaybackState gv_player_get_playback_state (GvPlayer *self); guint gv_player_get_bitrate (GvPlayer *self); GvStreaminfo *gv_player_get_streaminfo (GvPlayer *self); GvMetadata *gv_player_get_metadata (GvPlayer *self); GvStation *gv_player_get_station (GvPlayer *self); GvStation *gv_player_get_prev_station (GvPlayer *self); GvStation *gv_player_get_next_station (GvPlayer *self); void gv_player_set_station (GvPlayer *self, GvStation *station); gboolean gv_player_set_station_by_name (GvPlayer *self, const gchar *name); gboolean gv_player_set_station_by_uri (GvPlayer *self, const gchar *uri); gboolean gv_player_set_station_by_guessing(GvPlayer *self, const gchar *string); gboolean gv_player_get_repeat (GvPlayer *self); void gv_player_set_repeat (GvPlayer *self, gboolean repeat); gboolean gv_player_get_shuffle (GvPlayer *self); void gv_player_set_shuffle (GvPlayer *self, gboolean shuffle); gboolean gv_player_get_autoplay (GvPlayer *self); void gv_player_set_autoplay (GvPlayer *self, gboolean autoplay); guint gv_player_get_volume (GvPlayer *self); void gv_player_set_volume (GvPlayer *self, guint volume); void gv_player_lower_volume (GvPlayer *self); void gv_player_raise_volume (GvPlayer *self); gboolean gv_player_get_mute (GvPlayer *self); void gv_player_set_mute (GvPlayer *self, gboolean mute); void gv_player_toggle_mute (GvPlayer *self); gboolean gv_player_get_pipeline_enabled(GvPlayer *self); void gv_player_set_pipeline_enabled(GvPlayer *self, gboolean enabled); const gchar *gv_player_get_pipeline_string (GvPlayer *self); void gv_player_set_pipeline_string (GvPlayer *self, const gchar *pipeline); goodvibes-v0.7.2/src/core/gv-playlist.c000066400000000000000000000363321414415210700200330ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ /* * This code is based on / inspired by the following: * - Parole * - Totem (Playlist Parser) * Big thanks to them. * * A definitive guide to playlist formats (a must-read, though I didn't): * http://gonze.com/playlists/playlist-format-survey.html */ #include #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-playlist.h" // WISHED Test with a lot, really a lot of different stations. // I'm pretty sure this playlist implementation is not robust enough. // TODO Validate URI, send an error message if it's invalid ? // But then, shouldn't that be done in GvStation instead ? Or not ? /* * Properties */ enum { /* Reserved */ PROP_0, /* Properties */ PROP_URI, PROP_STREAM_LIST, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * Signals */ enum { SIGNAL_DOWNLOADED, /* Number of signals */ SIGNAL_N }; static guint signals[SIGNAL_N]; /* * GObject definitions */ struct _GvPlaylistPrivate { gchar *uri; GvPlaylistFormat format; GSList *streams; }; typedef struct _GvPlaylistPrivate GvPlaylistPrivate; struct _GvPlaylist { /* Parent instance structure */ GObject parent_instance; /* Private data */ GvPlaylistPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvPlaylist, gv_playlist, G_TYPE_OBJECT) /* * Helpers */ typedef GSList *(*PlaylistParser)(const gchar *, gsize); /* Parse a M3U playlist, which is a simple text file, * each line being an uri. * https://en.wikipedia.org/wiki/M3U */ static GSList * parse_playlist_m3u(const gchar *text, gsize text_size G_GNUC_UNUSED) { GSList *list = NULL; const gchar *eol; gchar **lines; guint i; /* Guess which delimiter is used to separate each lines. * Traditionnaly, UNIX would only use `\n`, Windows would use `\r\n`. */ if (strstr(text, "\r\n")) eol = "\r\n"; else eol = "\n"; /* Split into different lines */ lines = g_strsplit(text, eol, -1); if (lines == NULL) { WARNING("Empty m3u playlist"); return NULL; } /* Iterate on lines */ for (i = 0; lines[i] != NULL; i++) { gchar *line = lines[i]; /* Remove leading & trailing whitespaces */ line = g_strstrip(line); /* Ignore emtpy lines and comments */ if (line[0] == '\0' || line[0] == '#') continue; /* If it's not an URI, we discard it */ if (!strstr(line, "://")) continue; /* Add to stream list */ list = g_slist_append(list, g_strdup(line)); } g_strfreev(lines); return list; } /* Parse a PLS playlist, which is a "Desktop Entry File" in the Unix world, * or an "INI File" in the windows realm. * https://en.wikipedia.org/wiki/PLS_(file_format) */ static guint pls_get_n_items(GKeyFile *keyfile) { const gchar *keys[] = { "NumberOfEntries", "numberofentries", "NumberOfEvents", "numberofevents", NULL }; const gchar **ptr; const gchar *key = NULL; GError *err = NULL; gint n_items; for (ptr = keys; *ptr; ptr++) { if (g_key_file_has_key(keyfile, "playlist", *ptr, NULL)) { key = *ptr; break; } } if (key == NULL) { WARNING("Failed to get the number of items in pls playlist"); return 0; } n_items = g_key_file_get_integer(keyfile, "playlist", key, &err); if (err) { WARNING("Failed to get key '%s': %s", key, err->message); g_error_free(err); return 0; } return n_items; } static GSList * parse_playlist_pls(const gchar *text, gsize text_size) { GSList *list = NULL; GKeyFile *keyfile; GError *err = NULL; guint i, n_items; keyfile = g_key_file_new(); /* Parse the file */ g_key_file_load_from_data(keyfile, text, text_size, 0, &err); if (err) { WARNING("Failed to parse pls playlist: %s", err->message); g_error_free(err); goto end; } /* Get the number of items */ n_items = pls_get_n_items(keyfile); if (n_items == 0) goto end; /* Get all stream uris */ for (i = 0; i < n_items; i++) { gchar key[8]; gchar *str; g_snprintf(key, sizeof key, "File%u", i + 1); str = g_key_file_get_string(keyfile, "playlist", key, &err); if (err) { WARNING("Failed to get '%s': %s", key, err->message); g_error_free(err); err = NULL; continue; } /* Add to stream list. * No need to duplicate str, it's already an allocated string. */ list = g_slist_append(list, str); } end: g_key_file_free(keyfile); return list; } /* Parse an ASX (Advanced Stream Redirector) playlist. * https://en.wikipedia.org/wiki/Advanced_Stream_Redirector */ static void asx_parse_element_cb(GMarkupParseContext *context G_GNUC_UNUSED, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **err G_GNUC_UNUSED) { GSList **llink = (GSList **) user_data; const gchar *href; guint i; /* We're only interested in the 'ref' element */ if (g_ascii_strcasecmp(element_name, "ref")) return; /* Get 'href' attribute */ href = NULL; for (i = 0; attribute_names[i]; i++) { if (!g_ascii_strcasecmp(attribute_names[i], "href")) { href = attribute_values[i]; break; } } /* Add to stream list */ if (href) *llink = g_slist_append(*llink, g_strdup(href)); } static void asx_error_cb(GMarkupParseContext *context G_GNUC_UNUSED, GError *err G_GNUC_UNUSED, gpointer user_data) { GSList **llink = (GSList **) user_data; g_slist_free_full(*llink, g_free); *llink = NULL; } static GSList * parse_playlist_asx(const gchar *text, gsize text_size) { GMarkupParseContext *context; GMarkupParser parser = { asx_parse_element_cb, NULL, NULL, NULL, asx_error_cb, }; GError *err = NULL; GSList *list = NULL; context = g_markup_parse_context_new(&parser, 0, &list, NULL); if (!g_markup_parse_context_parse(context, text, text_size, &err)) { WARNING("Failed to parse context: %s", err->message); g_error_free(err); } g_markup_parse_context_free(context); return list; } /* Parse an XSPF (XML Shareable Playlist Format) playlist. * https://en.wikipedia.org/wiki/XML_Shareable_Playlist_Format */ static void xspf_text_cb(GMarkupParseContext *context, const gchar *text, gsize text_len G_GNUC_UNUSED, gpointer user_data, GError **err G_GNUC_UNUSED) { GSList **llink = (GSList **) user_data; const gchar *element_name; element_name = g_markup_parse_context_get_element(context); /* We're only interested in the 'location' element */ if (g_ascii_strcasecmp(element_name, "location")) return; /* Add to stream list */ *llink = g_slist_append(*llink, g_strdup(text)); } static void xspf_error_cb(GMarkupParseContext *context G_GNUC_UNUSED, GError *err G_GNUC_UNUSED, gpointer user_data) { GSList **llink = (GSList **) user_data; g_slist_free_full(*llink, g_free); *llink = NULL; } static GSList * parse_playlist_xspf(const gchar *text, gsize text_size) { GMarkupParseContext *context; GMarkupParser parser = { NULL, NULL, xspf_text_cb, NULL, xspf_error_cb, }; GError *err = NULL; GSList *list = NULL; context = g_markup_parse_context_new(&parser, 0, &list, NULL); if (!g_markup_parse_context_parse(context, text, text_size, &err)) { WARNING("Failed to parse context: %s", err->message); g_error_free(err); } g_markup_parse_context_free(context); return list; } /* * Signal handlers & callbacks */ static void on_message_completed(SoupSession *session, SoupMessage *msg, GvPlaylist *self) { GvPlaylistPrivate *priv = self->priv; PlaylistParser parser; GSList *item; TRACE("%p, %p, %p", session, msg, self); /* Check the response */ if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code) == FALSE) { WARNING("Failed to download playlist (%u): %s", msg->status_code, msg->reason_phrase); if (!g_strcmp0(soup_status_get_phrase(msg->status_code), "SSL handshake failed")) { /* XXX This is an error we can handle, we should ask user if they * want to trust the server anyway, ie. add a "security exception" * for this particular server. * * However I won't do it now, because: * - libsoup 3.x will break whatever solution I can come up with * - * - * - more code refactoring is needed on Goodvibes side (the interaction * between GvPlayer, GvStation and GvPlaylist is awkward at best) * - nobody complained on the bug tracker anyway * * So I'll try to get back at it when libsoup 3.x is out. */ INFO("XXX Handler not implemented yet"); } goto end; } else { SoupMessageHeaders *headers = msg->response_headers; const gchar *content_type = NULL; if (headers) content_type = soup_message_headers_get_content_type(headers, NULL); DEBUG("Playlist downloaded (Content-Type: %s)", content_type); } if (msg->response_body->length == 0) { WARNING("Empty playlist"); goto end; } //PRINT("%s", msg->response_body->data); /* Get the right parser */ switch (priv->format) { case GV_PLAYLIST_FORMAT_M3U: parser = parse_playlist_m3u; break; case GV_PLAYLIST_FORMAT_PLS: parser = parse_playlist_pls; break; case GV_PLAYLIST_FORMAT_ASX: parser = parse_playlist_asx; break; case GV_PLAYLIST_FORMAT_XSPF: parser = parse_playlist_xspf; break; default: WARNING("No parser for playlist format: %d", priv->format); goto end; } /* Parse */ if (priv->streams) g_slist_free_full(priv->streams, g_free); priv->streams = parser(msg->response_body->data, msg->response_body->length); /* Was it parsed successfully ? */ if (priv->streams == NULL) { WARNING("Failed to parse playlist"); goto end; } DEBUG("%d streams found:", g_slist_length(priv->streams)); for (item = priv->streams; item; item = item->next) { DEBUG(". %s", item->data); } end: // TODO Is it ok to unref that here ? g_object_unref(session); /* msg needs not to be unreferenced. According to the doc, * it's consumed when using the queue() API. */ /* Emit completion signal */ g_signal_emit(self, signals[SIGNAL_DOWNLOADED], 0); } /* * Property accessors */ const gchar * gv_playlist_get_uri(GvPlaylist *self) { return self->priv->uri; } static void gv_playlist_set_uri(GvPlaylist *self, const gchar *uri) { GvPlaylistPrivate *priv = self->priv; /* This is a construct-only property */ g_assert_null(priv->uri); g_assert_nonnull(uri); priv->uri = g_strdup(uri); /* Set format */ priv->format = gv_playlist_get_format(uri); } GSList * gv_playlist_get_stream_list(GvPlaylist *self) { return self->priv->streams; } static void gv_playlist_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvPlaylist *self = GV_PLAYLIST(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_URI: g_value_set_string(value, gv_playlist_get_uri(self)); break; case PROP_STREAM_LIST: g_value_set_pointer(value, gv_playlist_get_stream_list(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_playlist_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvPlaylist *self = GV_PLAYLIST(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_URI: gv_playlist_set_uri(self, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public methods */ void gv_playlist_download(GvPlaylist *self, gboolean insecure, const gchar *user_agent) { GvPlaylistPrivate *priv = self->priv; SoupSession *session; SoupMessage *msg; DEBUG("Downloading playlist '%s' (user-agent: '%s')", priv->uri, user_agent); session = soup_session_new_with_options(SOUP_SESSION_SSL_STRICT, !insecure, SOUP_SESSION_USER_AGENT, user_agent, NULL); msg = soup_message_new("GET", priv->uri); soup_session_queue_message(session, msg, (SoupSessionCallback) on_message_completed, self); } GvPlaylist * gv_playlist_new(const gchar *uri) { return g_object_new(GV_TYPE_PLAYLIST, "uri", uri, NULL); } /* * GObject methods */ static void gv_playlist_finalize(GObject *object) { GvPlaylistPrivate *priv = GV_PLAYLIST(object)->priv; TRACE("%p", object); /* Free any allocated resources */ g_free(priv->uri); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_playlist, object); } static void gv_playlist_constructed(GObject *object) { GvPlaylist *self = GV_PLAYLIST(object); GvPlaylistPrivate *priv = self->priv; TRACE("%p", object); /* Ensure construct-only properties have been set */ g_assert_nonnull(priv->uri); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_playlist, object); } static void gv_playlist_init(GvPlaylist *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_playlist_get_instance_private(self); } static void gv_playlist_class_init(GvPlaylistClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_playlist_finalize; object_class->constructed = gv_playlist_constructed; /* Properties */ object_class->get_property = gv_playlist_get_property; object_class->set_property = gv_playlist_set_property; properties[PROP_URI] = g_param_spec_string("uri", "Uri", NULL, NULL, GV_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_STREAM_LIST] = g_param_spec_pointer("stream-list", "Stream list", NULL, GV_PARAM_READABLE); g_object_class_install_properties(object_class, PROP_N, properties); /* Signals */ signals[SIGNAL_DOWNLOADED] = g_signal_new("downloaded", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } /* * Class methods */ GvPlaylistFormat gv_playlist_get_format(const gchar *uri_string) { GvPlaylistFormat fmt = GV_PLAYLIST_FORMAT_UNKNOWN; SoupURI *uri; const gchar *path; const gchar *ext; /* Parse the uri */ uri = soup_uri_new(uri_string); if (uri == NULL) { INFO("Invalid uri '%s'", uri_string); return GV_PLAYLIST_FORMAT_UNKNOWN; } /* Get the path */ path = soup_uri_get_path(uri); /* Get the extension of the path */ ext = strrchr(path, '.'); if (ext) ext++; else ext = "\0"; /* Match with supported extensions */ if (!g_ascii_strcasecmp(ext, "m3u")) fmt = GV_PLAYLIST_FORMAT_M3U; else if (!g_ascii_strcasecmp(ext, "ram")) fmt = GV_PLAYLIST_FORMAT_M3U; else if (!g_ascii_strcasecmp(ext, "pls")) fmt = GV_PLAYLIST_FORMAT_PLS; else if (!g_ascii_strcasecmp(ext, "asx")) fmt = GV_PLAYLIST_FORMAT_ASX; else if (!g_ascii_strcasecmp(ext, "xspf")) fmt = GV_PLAYLIST_FORMAT_XSPF; /* Cleanup */ soup_uri_free(uri); return fmt; } goodvibes-v0.7.2/src/core/gv-playlist.h000066400000000000000000000030161414415210700200310ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include /* GObject declarations */ #define GV_TYPE_PLAYLIST gv_playlist_get_type() G_DECLARE_FINAL_TYPE(GvPlaylist, gv_playlist, GV, PLAYLIST, GObject) /* Data types */ typedef enum { GV_PLAYLIST_FORMAT_UNKNOWN, GV_PLAYLIST_FORMAT_M3U, GV_PLAYLIST_FORMAT_PLS, GV_PLAYLIST_FORMAT_ASX, GV_PLAYLIST_FORMAT_XSPF } GvPlaylistFormat; /* Class methods */ GvPlaylistFormat gv_playlist_get_format(const gchar *uri); /* Methods */ GvPlaylist *gv_playlist_new (const gchar *uri); void gv_playlist_download(GvPlaylist *playlist, gboolean insecure, const gchar *user_agent); /* Property accessors */ const gchar *gv_playlist_get_uri (GvPlaylist *self); GSList *gv_playlist_get_stream_list(GvPlaylist *playlist); goodvibes-v0.7.2/src/core/gv-station-list.c000066400000000000000000001076431414415210700206300ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-station-list.h" // WISHED Try with a huge number of stations to see how it behaves. // It might be slow. The implementation never tried to be fast. /* * More defines... */ #define SAVE_DELAY 1 // how long to wait before writing changes to disk #define STATION_LIST_FILE "stations.xml" // where to write the stations /* * Properties */ enum { /* Reserved */ PROP_0, /* Properties */ PROP_DEFAULT_STATIONS, PROP_LOAD_PATHS, PROP_LOAD_PATH, PROP_SAVE_PATH, /* Number of properties */ PROP_N, }; static GParamSpec *properties[PROP_N]; /* * Signals */ enum { SIGNAL_LOADED, SIGNAL_STATION_ADDED, SIGNAL_STATION_REMOVED, SIGNAL_STATION_MODIFIED, SIGNAL_STATION_MOVED, /* Number of signals */ SIGNAL_N }; static guint signals[SIGNAL_N]; /* * GObject definitions */ struct _GvStationListPrivate { gchar *default_stations; /* Paths */ gchar **load_paths; gchar *load_path; gchar *save_path; /* Timeout id, > 0 if a save operation is scheduled */ guint save_timeout_id; /* Set to true during object finalization */ gboolean finalization; /* Ordered list of stations */ GList *stations; /* Shuffled list of stations, automatically created * and destroyed when needed. */ GList *shuffled; }; typedef struct _GvStationListPrivate GvStationListPrivate; struct _GvStationList { /* Parent instance structure */ GObject parent_instance; /* Private data */ GvStationListPrivate *priv; }; G_DEFINE_TYPE_WITH_CODE(GvStationList, gv_station_list, G_TYPE_OBJECT, G_ADD_PRIVATE(GvStationList) G_IMPLEMENT_INTERFACE(GV_TYPE_ERRORABLE, NULL)) /* * Helpers */ static gpointer copy_func_object_ref(gconstpointer src, gpointer data G_GNUC_UNUSED) { return g_object_ref((gpointer) src); } /* * Paths helpers */ static gchar ** make_station_list_load_paths(const gchar *filename) { const gchar *const *system_dirs; const gchar *user_dir; guint i, n_dirs; gchar **paths; user_dir = gv_get_app_user_data_dir(); system_dirs = gv_get_app_system_data_dirs(); n_dirs = g_strv_length((gchar **) system_dirs) + 1; paths = g_malloc0_n(n_dirs + 1, sizeof(gchar *)); paths[0] = g_build_filename(user_dir, filename, NULL); for (i = 1; i < n_dirs; i++) { const gchar *dir; dir = system_dirs[i - 1]; paths[i] = g_build_filename(dir, filename, NULL); } return paths; } static gchar * make_station_list_save_path(const gchar *filename) { const gchar *user_dir; gchar *path; user_dir = gv_get_app_user_data_dir(); path = g_build_filename(user_dir, filename, NULL); return path; } /* * Markup handling */ struct _GvMarkupParsing { /* Persistent during the whole parsing process */ GList *list; /* Current iteration */ gchar **cur; gchar *name; gchar *uri; gchar *insecure; gchar *user_agent; }; typedef struct _GvMarkupParsing GvMarkupParsing; static void markup_on_text(GMarkupParseContext *context G_GNUC_UNUSED, const gchar *text, gsize text_len G_GNUC_UNUSED, gpointer user_data, GError **err G_GNUC_UNUSED) { GvMarkupParsing *parsing = user_data; /* Happens all the time */ if (parsing->cur == NULL) return; /* Should never happen */ g_assert_null(*parsing->cur); /* Save text */ *parsing->cur = g_strdup(text); /* Cleanup */ parsing->cur = NULL; } static void markup_on_end_element(GMarkupParseContext *context G_GNUC_UNUSED, const gchar *element_name G_GNUC_UNUSED, gpointer user_data, GError **err G_GNUC_UNUSED) { GvMarkupParsing *parsing = user_data; GvStation *station; /* We only care when we leave a station node */ if (g_strcmp0(element_name, "Station")) return; /* Discard stations with no uri */ if (parsing->uri == NULL || parsing->uri[0] == '\0') { DEBUG("Encountered station without uri (named '%s')", parsing->name); goto cleanup; } /* Create a new station */ station = gv_station_new(parsing->name, parsing->uri); if (!g_strcmp0(parsing->insecure, "true")) gv_station_set_insecure(station, TRUE); if (parsing->user_agent) gv_station_set_user_agent(station, parsing->user_agent); /* We must take ownership right now */ g_object_ref_sink(station); /* Add to list, use prepend for efficiency */ parsing->list = g_list_prepend(parsing->list, station); cleanup: /* Cleanup */ g_clear_pointer(&parsing->name, g_free); g_clear_pointer(&parsing->uri, g_free); g_clear_pointer(&parsing->insecure, g_free); g_clear_pointer(&parsing->user_agent, g_free); } static void markup_on_start_element(GMarkupParseContext *context G_GNUC_UNUSED, const gchar *element_name, const gchar **attribute_names G_GNUC_UNUSED, const gchar **attribute_values G_GNUC_UNUSED, gpointer user_data, GError **err G_GNUC_UNUSED) { GvMarkupParsing *parsing = user_data; /* Expected top node */ if (!g_strcmp0(element_name, "Stations")) return; /* Entering a station node */ if (!g_strcmp0(element_name, "Station")) { g_assert_null(parsing->name); g_assert_null(parsing->uri); g_assert_null(parsing->insecure); g_assert_null(parsing->user_agent); return; } /* Name property */ if (!g_strcmp0(element_name, "name")) { g_assert_null(parsing->cur); parsing->cur = &parsing->name; return; } /* Uri property */ if (!g_strcmp0(element_name, "uri")) { g_assert_null(parsing->cur); parsing->cur = &parsing->uri; return; } /* Insecure property */ if (!g_strcmp0(element_name, "insecure")) { g_assert_null(parsing->insecure); parsing->cur = &parsing->insecure; return; } /* User-agent property */ if (!g_strcmp0(element_name, "user-agent")) { g_assert_null(parsing->user_agent); parsing->cur = &parsing->user_agent; return; } WARNING("Unexpected element: '%s'", element_name); } static void markup_on_error(GMarkupParseContext *context G_GNUC_UNUSED, GError *err G_GNUC_UNUSED, gpointer user_data) { GvMarkupParsing *parsing = user_data; parsing->cur = NULL; g_clear_pointer(&parsing->name, g_free); g_clear_pointer(&parsing->uri, g_free); g_clear_pointer(&parsing->insecure, g_free); g_clear_pointer(&parsing->user_agent, g_free); } static gboolean parse_markup(const gchar *text, GList **list, GError **err) { GMarkupParseContext *context; GMarkupParser parser = { markup_on_start_element, markup_on_end_element, markup_on_text, NULL, markup_on_error, }; GvMarkupParsing parsing = { NULL, NULL, NULL, NULL, NULL, NULL }; gboolean ret; g_return_val_if_fail(list != NULL, FALSE); g_return_val_if_fail(err == NULL || *err == NULL, FALSE); context = g_markup_parse_context_new(&parser, 0, &parsing, NULL); ret = g_markup_parse_context_parse(context, text, -1, err); g_markup_parse_context_free(context); if (ret == FALSE) { g_assert(err == NULL || *err != NULL); return FALSE; } *list = g_list_reverse(parsing.list); return TRUE; } static GString * g_string_append_markup_tag_escaped(GString *string, const gchar *tag, const gchar *value) { gchar *escaped; escaped = g_markup_printf_escaped(" <%s>%s\n", tag, value, tag); g_string_append(string, escaped); g_free(escaped); return string; } static gchar * print_markup_station(GvStation *station) { const gchar *name = gv_station_get_name(station); const gchar *uri = gv_station_get_uri(station); const gchar *insecure = gv_station_get_insecure(station) ? "true" : NULL; const gchar *user_agent = gv_station_get_user_agent(station); GString *string; /* A station is supposed to have an uri */ if (uri == NULL) { WARNING("Station (%s) has no uri!", name); return NULL; } /* Write station in markup fashion */ string = g_string_new(" \n"); if (uri) g_string_append_markup_tag_escaped(string, "uri", uri); if (name) g_string_append_markup_tag_escaped(string, "name", name); if (insecure) g_string_append_markup_tag_escaped(string, "insecure", insecure); if (user_agent) g_string_append_markup_tag_escaped(string, "user-agent", user_agent); g_string_append(string, " \n"); /* Return */ return g_string_free(string, FALSE); } static gboolean print_markup(GList *list, gchar **markup, GError **err) { GList *item; GString *string; g_return_val_if_fail(markup != NULL, FALSE); g_return_val_if_fail(err == NULL || *err == NULL, FALSE); string = g_string_new(NULL); g_string_append(string, "\n"); for (item = list; item; item = item->next) { GvStation *station = GV_STATION(item->data); gchar *text; text = print_markup_station(station); if (text == NULL) continue; g_string_append(string, text); g_free(text); } g_string_append(string, ""); *markup = g_string_free(string, FALSE); return TRUE; } /* * File I/O */ static gboolean load_station_list_from_string(const gchar *text, GList **list, GError **err) { return parse_markup(text, list, err); } static gboolean load_station_list_from_file(const gchar *path, GList **list, GError **err) { gchar *text = NULL; gboolean ret; g_return_val_if_fail(err == NULL || *err == NULL, FALSE); ret = g_file_get_contents(path, &text, NULL, err); if (ret == FALSE) { g_assert(err == NULL || *err != NULL); goto end; } ret = load_station_list_from_string(text, list, err); if (ret == FALSE) { g_assert(err == NULL || *err != NULL); goto end; } end: g_free(text); return ret; } static gboolean save_station_list_to_string(GList *list, gchar **text, GError **err) { return print_markup(list, text, err); } static gboolean save_station_list_to_file(GList *list, const gchar *path, GError **err) { gboolean ret; gchar *text = NULL; gchar *dirname = NULL; g_return_val_if_fail(err == NULL || *err == NULL, FALSE); ret = save_station_list_to_string(list, &text, err); if (ret == FALSE) { g_assert(err == NULL || *err != NULL); goto end; } /* g_file_set_contents() doesn't work with /dev/null, due to * the way it's implemented (see documentation for details). * However it's convenient to set the save path to /dev/null * when we actually don't want to save (ie. test suite). * So we handle this special case explicitly here. */ if (!g_strcmp0(path, "/dev/null")) { ret = TRUE; goto end; } dirname = g_path_get_dirname(path); if (g_mkdir_with_parents(dirname, S_IRWXU) != 0) { g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), "Failed to make directory: %s", g_strerror(errno)); ret = FALSE; goto end; } ret = g_file_set_contents(path, text, -1, err); if (ret == FALSE) { g_assert(err == NULL || *err != NULL); goto end; } end: g_free(dirname); g_free(text); return ret; } /* * Iterator implementation */ struct _GvStationListIter { GList *head, *item; }; GvStationListIter * gv_station_list_iter_new(GvStationList *self) { GList *list = self->priv->stations; GvStationListIter *iter; iter = g_new0(GvStationListIter, 1); iter->head = g_list_copy_deep(list, copy_func_object_ref, NULL); iter->item = iter->head; return iter; } void gv_station_list_iter_free(GvStationListIter *iter) { g_return_if_fail(iter != NULL); g_list_free_full(iter->head, g_object_unref); g_free(iter); } gboolean gv_station_list_iter_loop(GvStationListIter *iter, GvStation **station) { g_return_val_if_fail(iter != NULL, FALSE); g_return_val_if_fail(station != NULL, FALSE); *station = NULL; if (iter->item == NULL) return FALSE; *station = iter->item->data; iter->item = iter->item->next; return TRUE; } /* * GList additions */ static gint glist_sortfunc_random(gconstpointer a G_GNUC_UNUSED, gconstpointer b G_GNUC_UNUSED) { return g_random_boolean() ? 1 : -1; } static GList * g_list_shuffle(GList *list) { /* Shuffle twice. Credits goes to: * http://www.linuxforums.org/forum/programming-scripting/ * 202125-fastest-way-shuffle-linked-list.html */ list = g_list_sort(list, glist_sortfunc_random); list = g_list_sort(list, glist_sortfunc_random); return list; } static GList * g_list_copy_deep_shuffle(GList *list, GCopyFunc func, gpointer user_data) { list = g_list_copy_deep(list, func, user_data); list = g_list_shuffle(list); return list; } /* * Helpers */ static gint are_stations_similar(GvStation *s1, GvStation *s2) { if (s1 == s2) { WARNING("Stations %p and %p are the same", s1, s2); return 0; } if (s1 == NULL || s2 == NULL) return -1; /* Compare uids */ const gchar *s1_uid, *s2_uid; s1_uid = gv_station_get_uid(s1); s2_uid = gv_station_get_uid(s2); if (!g_strcmp0(s1_uid, s2_uid)) { WARNING("Stations %p and %p have the same uid '%s'", s1, s2, s1_uid); return 0; } /* Compare names. * Two stations who don't have name are different. */ const gchar *s1_name, *s2_name; s1_name = gv_station_get_name(s1); s2_name = gv_station_get_name(s2); if (s1_name == NULL && s2_name == NULL) return -1; if (!g_strcmp0(s1_name, s2_name)) { DEBUG("Stations %p and %p have the same name '%s'", s1, s2, s1_name); return 0; } /* Compare uris */ const gchar *s1_uri, *s2_uri; s1_uri = gv_station_get_uri(s1); s2_uri = gv_station_get_uri(s2); if (!g_strcmp0(s1_uri, s2_uri)) { DEBUG("Stations %p and %p have the same uri '%s'", s1, s2, s1_uri); return 0; } return -1; } /* * Signal handlers */ static gboolean when_timeout_save_station_list(gpointer data) { GvStationList *self = GV_STATION_LIST(data); GvStationListPrivate *priv = self->priv; gv_station_list_save(self); priv->save_timeout_id = 0; return G_SOURCE_REMOVE; } static void gv_station_list_save_delayed(GvStationList *self) { GvStationListPrivate *priv = self->priv; g_clear_handle_id(&priv->save_timeout_id, g_source_remove); priv->save_timeout_id = g_timeout_add_seconds(SAVE_DELAY, when_timeout_save_station_list, self); } static void on_station_notify(GvStation *station, GParamSpec *pspec, GvStationList *self) { const gchar *property_name = g_param_spec_get_name(pspec); TRACE("%s, %s, %p", gv_station_get_uid(station), property_name, self); /* We might want to save changes */ if (!g_strcmp0(property_name, "uri") || !g_strcmp0(property_name, "name") || !g_strcmp0(property_name, "insecure") || !g_strcmp0(property_name, "user-agent")) { gv_station_list_save_delayed(self); } /* Emit signal */ g_signal_emit(self, signals[SIGNAL_STATION_MODIFIED], 0, station); } /* * Property accessors */ static void gv_station_list_set_default_stations(GvStationList *self, const gchar *stations) { GvStationListPrivate *priv = self->priv; /* This is a construct-only property */ g_assert_null(priv->default_stations); priv->default_stations = g_strdup(stations); } static void gv_station_list_set_load_paths(GvStationList *self, const gchar *const *paths) { GvStationListPrivate *priv = self->priv; /* This is a construct-only property */ g_assert_null(priv->load_paths); priv->load_paths = g_strdupv((gchar **) paths); } const gchar * gv_station_list_get_load_path(GvStationList *self) { return self->priv->load_path; } static void gv_station_list_set_load_path(GvStationList *self, const gchar *path) { GvStationListPrivate *priv = self->priv; /* This is a construct-only property, however it might also * be set internally by the gv_station_list_load() method. */ if (!g_strcmp0(priv->load_path, path)) return; g_free(priv->load_path); priv->load_path = g_strdup(path); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_LOAD_PATH]); } const gchar * gv_station_list_get_save_path(GvStationList *self) { return self->priv->save_path; } static void gv_station_list_set_save_path(GvStationList *self, const gchar *path) { GvStationListPrivate *priv = self->priv; /* This is a construct-only property */ g_assert_null(priv->save_path); g_assert_nonnull(path); priv->save_path = g_strdup(path); } static void gv_station_list_get_property(GObject *object, guint property_id, GValue *value G_GNUC_UNUSED, GParamSpec *pspec) { GvStationList *self = GV_STATION_LIST(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_LOAD_PATH: g_value_set_string(value, gv_station_list_get_load_path(self)); break; case PROP_SAVE_PATH: g_value_set_string(value, gv_station_list_get_save_path(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_station_list_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvStationList *self = GV_STATION_LIST(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_DEFAULT_STATIONS: gv_station_list_set_default_stations(self, g_value_get_string(value)); break; case PROP_LOAD_PATHS: gv_station_list_set_load_paths(self, g_value_get_pointer(value)); break; case PROP_LOAD_PATH: gv_station_list_set_load_path(self, g_value_get_string(value)); break; case PROP_SAVE_PATH: gv_station_list_set_save_path(self, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public functions */ void gv_station_list_remove(GvStationList *self, GvStation *station) { GvStationListPrivate *priv = self->priv; GList *item; /* Ensure a valid station was given */ if (station == NULL) { WARNING("Attempting to remove NULL station"); return; } /* Give info */ INFO("Removing station '%s'", gv_station_get_name_or_uri(station)); /* Check that we own this station at first. If we don't find it * in our internal list, it's probably a programming error. */ item = g_list_find(priv->stations, station); if (item == NULL) { WARNING("GvStation %p (%s) not found in list", station, gv_station_get_uid(station)); return; } /* Disconnect signal handlers */ g_signal_handlers_disconnect_by_data(station, self); /* Remove from list */ priv->stations = g_list_remove_link(priv->stations, item); g_list_free(item); /* Unown the station */ g_object_unref(station); /* Rebuild the shuffled station list */ if (priv->shuffled) { g_list_free_full(priv->shuffled, g_object_unref); priv->shuffled = g_list_copy_deep_shuffle(priv->stations, copy_func_object_ref, NULL); } /* Emit a signal */ g_signal_emit(self, signals[SIGNAL_STATION_REMOVED], 0, station); /* Save */ gv_station_list_save_delayed(self); } void gv_station_list_insert(GvStationList *self, GvStation *station, gint pos) { GvStationListPrivate *priv = self->priv; GList *similar_item; g_return_if_fail(station != NULL); /* Give info */ INFO("Inserting station '%s'", gv_station_get_name_or_uri(station)); /* Check that the station is not already part of the list. * Duplicates are a programming error, we must warn about that. * Identical fields are an user error. * Warnings and such are encapsulated in the GCompareFunc used * here, this is messy but temporary (hopefully). */ similar_item = g_list_find_custom(priv->stations, station, (GCompareFunc) are_stations_similar); if (similar_item) return; /* Take ownership of the station */ g_object_ref_sink(station); /* Add to the list at the right position */ priv->stations = g_list_insert(priv->stations, station, pos); /* Connect to notify signal */ g_signal_connect_object(station, "notify", G_CALLBACK(on_station_notify), self, 0); /* Rebuild the shuffled station list */ if (priv->shuffled) { g_list_free_full(priv->shuffled, g_object_unref); priv->shuffled = g_list_copy_deep_shuffle(priv->stations, copy_func_object_ref, NULL); } /* Emit a signal */ g_signal_emit(self, signals[SIGNAL_STATION_ADDED], 0, station); /* Save */ gv_station_list_save_delayed(self); } void gv_station_list_insert_before(GvStationList *self, GvStation *station, GvStation *before) { GvStationListPrivate *priv = self->priv; gint pos; g_return_if_fail(before != NULL); pos = g_list_index(priv->stations, before); g_return_if_fail(pos != -1); gv_station_list_insert(self, station, pos); } void gv_station_list_insert_after(GvStationList *self, GvStation *station, GvStation *after) { GvStationListPrivate *priv = self->priv; gint pos; g_return_if_fail(after != NULL); pos = g_list_index(priv->stations, after); g_return_if_fail(pos != -1); pos += 1; gv_station_list_insert(self, station, pos); } void gv_station_list_prepend(GvStationList *self, GvStation *station) { gv_station_list_insert(self, station, 0); } void gv_station_list_append(GvStationList *self, GvStation *station) { gv_station_list_insert(self, station, -1); } void gv_station_list_move(GvStationList *self, GvStation *station, gint pos) { GvStationListPrivate *priv = self->priv; GList *item; g_return_if_fail(station != NULL); /* Find the station */ item = g_list_find(priv->stations, station); g_return_if_fail(item != NULL); /* Move it */ priv->stations = g_list_insert(priv->stations, station, pos); priv->stations = g_list_remove_link(priv->stations, item); g_list_free(item); /* Emit a signal */ g_signal_emit(self, signals[SIGNAL_STATION_MOVED], 0, station); /* Save */ gv_station_list_save_delayed(self); } void gv_station_list_move_before(GvStationList *self, GvStation *station, GvStation *before) { GvStationListPrivate *priv = self->priv; gint pos; g_return_if_fail(before != NULL); pos = g_list_index(priv->stations, before); g_return_if_fail(pos != -1); gv_station_list_move(self, station, pos); } void gv_station_list_move_after(GvStationList *self, GvStation *station, GvStation *after) { GvStationListPrivate *priv = self->priv; gint pos; g_return_if_fail(after != NULL); pos = g_list_index(priv->stations, after); g_return_if_fail(pos != -1); pos += 1; gv_station_list_move(self, station, pos); } void gv_station_list_move_first(GvStationList *self, GvStation *station) { gv_station_list_move(self, station, 0); } void gv_station_list_move_last(GvStationList *self, GvStation *station) { gv_station_list_move(self, station, -1); } GvStation * gv_station_list_prev(GvStationList *self, GvStation *station, gboolean repeat, gboolean shuffle) { GvStationListPrivate *priv = self->priv; GList *stations, *item; /* Pickup the right station list, create shuffle list if needed */ if (shuffle) { if (priv->shuffled == NULL) { priv->shuffled = g_list_copy_deep_shuffle(priv->stations, copy_func_object_ref, NULL); } stations = priv->shuffled; } else { if (priv->shuffled) { g_list_free_full(priv->shuffled, g_object_unref); priv->shuffled = NULL; } stations = priv->stations; } /* If the station list is empty, bail out */ if (stations == NULL) return NULL; /* Return last station for NULL argument */ if (station == NULL) return g_list_last(stations)->data; /* Try to find station in station list */ item = g_list_find(stations, station); if (item == NULL) return NULL; /* Return previous station if any */ item = item->prev; if (item) return item->data; /* Without repeat, there's no more station */ if (!repeat) return NULL; /* With repeat, we may re-shuffle, then return the last station */ if (shuffle) { GList *last_item; stations = g_list_shuffle(priv->shuffled); /* In case the last station (that we're about to return) happens to be * the same as the current station, we do a little a magic trick. */ last_item = g_list_last(stations); if (last_item->data == station) { stations = g_list_remove_link(stations, last_item); stations = g_list_prepend(stations, last_item->data); g_list_free(last_item); } priv->shuffled = stations; } return g_list_last(stations)->data; } GvStation * gv_station_list_next(GvStationList *self, GvStation *station, gboolean repeat, gboolean shuffle) { GvStationListPrivate *priv = self->priv; GList *stations, *item; /* Pickup the right station list, create shuffle list if needed */ if (shuffle) { if (priv->shuffled == NULL) { priv->shuffled = g_list_copy_deep_shuffle(priv->stations, copy_func_object_ref, NULL); } stations = priv->shuffled; } else { if (priv->shuffled) { g_list_free_full(priv->shuffled, g_object_unref); priv->shuffled = NULL; } stations = priv->stations; } /* If the station list is empty, bail out */ if (stations == NULL) return NULL; /* Return first station for NULL argument */ if (station == NULL) return stations->data; /* Try to find station in station list */ item = g_list_find(stations, station); if (item == NULL) return NULL; /* Return next station if any */ item = item->next; if (item) return item->data; /* Without repeat, there's no more station */ if (!repeat) return NULL; /* With repeat, we may re-shuffle, then return the first station */ if (shuffle) { GList *first_item; stations = g_list_shuffle(priv->shuffled); /* In case the first station (that we're about to return) happens to be * the same as the current station, we do a little a magic trick. */ first_item = g_list_first(stations); if (first_item->data == station) { stations = g_list_remove_link(stations, first_item); stations = g_list_append(stations, first_item->data); g_list_free(first_item); } priv->shuffled = stations; } return stations->data; } GvStation * gv_station_list_first(GvStationList *self) { GList *stations = self->priv->stations; if (stations == NULL) return NULL; return g_list_first(stations)->data; } GvStation * gv_station_list_last(GvStationList *self) { GList *stations = self->priv->stations; if (stations == NULL) return NULL; return g_list_last(stations)->data; } GvStation * gv_station_list_at(GvStationList *self, guint n) { GList *stations = self->priv->stations; GList *item; if (stations == NULL) return NULL; item = g_list_nth(stations, n); if (item == NULL) return NULL; return item->data; } GvStation * gv_station_list_find(GvStationList *self, GvStation *station) { GList *stations = self->priv->stations; GList *item; item = g_list_find(stations, station); return item ? item->data : NULL; } GvStation * gv_station_list_find_by_name(GvStationList *self, const gchar *name) { GList *item; /* Ensure station name is valid */ if (name == NULL) { WARNING("Attempting to find a station with NULL name"); return NULL; } /* Forbid empty names */ if (!g_strcmp0(name, "")) return NULL; /* Iterate on station list */ for (item = self->priv->stations; item; item = item->next) { GvStation *station = item->data; if (!g_strcmp0(name, gv_station_get_name(station))) return station; } return NULL; } GvStation * gv_station_list_find_by_uri(GvStationList *self, const gchar *uri) { GList *item; /* Ensure station name is valid */ if (uri == NULL) { WARNING("Attempting to find a station with NULL uri"); return NULL; } /* Iterate on station list */ for (item = self->priv->stations; item; item = item->next) { GvStation *station = item->data; if (!g_strcmp0(uri, gv_station_get_uri(station))) return station; } return NULL; } GvStation * gv_station_list_find_by_uid(GvStationList *self, const gchar *uid) { GList *item; /* Ensure station name is valid */ if (uid == NULL) { WARNING("Attempting to find a station with NULL uid"); return NULL; } /* Iterate on station list */ for (item = self->priv->stations; item; item = item->next) { GvStation *station = item->data; if (!g_strcmp0(uid, gv_station_get_uid(station))) return station; } return NULL; } GvStation * gv_station_list_find_by_guessing(GvStationList *self, const gchar *string) { if (is_uri_scheme_supported(string)) return gv_station_list_find_by_uri(self, string); else /* Assume it's the station name */ return gv_station_list_find_by_name(self, string); } void gv_station_list_save(GvStationList *self) { GvStationListPrivate *priv = self->priv; const gchar *path = priv->save_path; GError *err = NULL; gboolean ret; /* Save the station list */ ret = save_station_list_to_file(priv->stations, path, &err); if (ret == TRUE) { INFO("Station list saved to '%s'", path); } else { WARNING("Failed to save station list: %s", err->message); if (priv->finalization == FALSE) gv_errorable_emit_error(GV_ERRORABLE(self), _("%s: %s"), _("Failed to save station list"), err->message); g_error_free(err); } } void gv_station_list_load(GvStationList *self) { GvStationListPrivate *priv = self->priv; GList *item; TRACE("%p", self); /* This should be called only once at startup */ g_assert_null(priv->stations); /* If a single load path is defined, try to load the station list * from there. It must work. Failing to load from this path is a * fatal error. * Use-case: test suite */ if (priv->load_path) { const gchar *path = priv->load_path; GError *err = NULL; gboolean ret; ret = load_station_list_from_file(path, &priv->stations, &err); if (ret == FALSE) { ERROR("Failed to load station list from '%s': %s", path, err->message); /* Program execution stops here */ } INFO("Station list loaded from file '%s'", path); goto finish; } /* If a list of load paths is defined, it's a best effort. We try * them all in order, the first that succeeds wins, and is assigned * to the property 'load-path'. If none succeeds, it's not a fatal * error, we simply keep going. * Use-case: real-life, xdg locations */ if (priv->load_paths) { gchar **paths = priv->load_paths; guint n_paths = g_strv_length(paths); guint i; for (i = 0; i < n_paths; i++) { const gchar *path = paths[i]; GError *err = NULL; gboolean ret; ret = load_station_list_from_file(path, &priv->stations, &err); if (ret == FALSE) { if (err->code != G_FILE_ERROR_NOENT) WARNING("Failed to load station list from '%s': %s", path, err->message); g_clear_error(&err); continue; } INFO("Station list loaded from file '%s'", path); gv_station_list_set_load_path(self, path); goto finish; } INFO("No valid station list file found"); } /* If some hard-coded defaults are defined, try it. It is a fatal * error if we can't parse these defaults. */ if (priv->default_stations) { gboolean ret; ret = load_station_list_from_string(priv->default_stations, &priv->stations, NULL); if (ret == FALSE) { ERROR("Failed to load station list from hard-coded default"); /* Program execution stops here */ } INFO("Station list loaded from hard-coded defaults"); goto finish; } finish: /* Dump the number of stations */ DEBUG("Station list has %u stations", gv_station_list_length(self)); /* Register a notify handler for each station */ for (item = priv->stations; item; item = item->next) { GvStation *station = item->data; g_signal_connect_object(station, "notify", G_CALLBACK(on_station_notify), self, 0); } /* Emit a signal to indicate that the list has been loaded */ g_signal_emit(self, signals[SIGNAL_LOADED], 0); } guint gv_station_list_length(GvStationList *self) { GvStationListPrivate *priv = self->priv; return g_list_length(priv->stations); } /* Create a new station list with load paths and save path derived * from XDG paths. In case none of the tentatice load paths work, * ultimately the default_stations string will be used. */ GvStationList * gv_station_list_new_from_xdg_dirs(const gchar *default_stations) { GvStationList *station_list; gchar **load_paths; gchar *save_path; load_paths = make_station_list_load_paths(STATION_LIST_FILE); save_path = make_station_list_save_path(STATION_LIST_FILE); station_list = g_object_new(GV_TYPE_STATION_LIST, "default-stations", default_stations, "load-paths", load_paths, "save-path", save_path, NULL); g_free(save_path); g_strfreev(load_paths); return station_list; } /* Create a new station list with explicit load and save paths. There * is no fallback in case we fail to parse the stations from the load * path: this is considered a fatal error. */ GvStationList * gv_station_list_new_from_paths(const gchar *load_path, const gchar *save_path) { return g_object_new(GV_TYPE_STATION_LIST, "load-path", load_path, "save-path", save_path, NULL); } /* * GObject methods */ static void gv_station_list_finalize(GObject *object) { GvStationList *self = GV_STATION_LIST(object); GvStationListPrivate *priv = self->priv; GList *item; TRACE("%p", object); /* Indicate that the object is being finalized */ priv->finalization = TRUE; /* Run any pending save operation */ if (priv->save_timeout_id > 0) when_timeout_save_station_list(self); /* Free shuffled station list */ g_list_free_full(priv->shuffled, g_object_unref); /* Free station list and ensure no memory is leaked. This works only if the * station list is the last object to hold references to stations. In other * words, the station list must be the last object finalized. */ for (item = priv->stations; item; item = item->next) { g_object_add_weak_pointer(G_OBJECT(item->data), &(item->data)); g_object_unref(item->data); if (item->data != NULL) WARNING("Station '%s' has not been finalized!", gv_station_get_name_or_uri(GV_STATION(item->data))); } g_list_free(priv->stations); /* Free resources */ g_free(priv->default_stations); g_strfreev(priv->load_paths); g_free(priv->save_path); g_free(priv->load_path); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_station_list, object); } static void gv_station_list_constructed(GObject *object) { GvStationList *self = GV_STATION_LIST(object); TRACE("%p", self); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_station_list, object); } static void gv_station_list_init(GvStationList *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_station_list_get_instance_private(self); } static void gv_station_list_class_init(GvStationListClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_station_list_finalize; object_class->constructed = gv_station_list_constructed; /* Properties */ object_class->get_property = gv_station_list_get_property; object_class->set_property = gv_station_list_set_property; properties[PROP_DEFAULT_STATIONS] = g_param_spec_string("default-stations", "Default stations", NULL, NULL, GV_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_LOAD_PATHS] = g_param_spec_pointer("load-paths", "Load Paths", NULL, GV_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_LOAD_PATH] = g_param_spec_string("load-path", "Load Path", NULL, NULL, GV_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_SAVE_PATH] = g_param_spec_string("save-path", "Save Path", NULL, NULL, GV_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties(object_class, PROP_N, properties); /* Signals */ signals[SIGNAL_LOADED] = g_signal_new("loaded", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[SIGNAL_STATION_ADDED] = g_signal_new("station-added", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[SIGNAL_STATION_REMOVED] = g_signal_new("station-removed", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[SIGNAL_STATION_MODIFIED] = g_signal_new("station-modified", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[SIGNAL_STATION_MOVED] = g_signal_new("station-moved", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); } goodvibes-v0.7.2/src/core/gv-station-list.h000066400000000000000000000071121414415210700206230ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "core/gv-station.h" /* GObject declarations */ #define GV_TYPE_STATION_LIST gv_station_list_get_type() G_DECLARE_FINAL_TYPE(GvStationList, gv_station_list, GV, STATION_LIST, GObject) /* Data types */ typedef struct _GvStationListIter GvStationListIter; /* Methods */ GvStationList *gv_station_list_new_from_xdg_dirs(const gchar *default_stations); GvStationList *gv_station_list_new_from_paths (const gchar *load_path, const gchar *save_path); void gv_station_list_load (GvStationList *self); void gv_station_list_save (GvStationList *self); guint gv_station_list_length(GvStationList *self); void gv_station_list_prepend (GvStationList *self, GvStation *station); void gv_station_list_append (GvStationList *self, GvStation *station); void gv_station_list_insert (GvStationList *self, GvStation *station, gint position); void gv_station_list_insert_before(GvStationList *self, GvStation *station, GvStation *before); void gv_station_list_insert_after (GvStationList *self, GvStation *station, GvStation *after); void gv_station_list_remove (GvStationList *self, GvStation *station); void gv_station_list_move (GvStationList *self, GvStation *station, gint position); void gv_station_list_move_before(GvStationList *self, GvStation *station, GvStation *before); void gv_station_list_move_after (GvStationList *self, GvStation *station, GvStation *after); void gv_station_list_move_first (GvStationList *self, GvStation *station); void gv_station_list_move_last (GvStationList *self, GvStation *station); GvStation *gv_station_list_first(GvStationList *self); GvStation *gv_station_list_last (GvStationList *self); GvStation *gv_station_list_at (GvStationList *self, guint n); GvStation *gv_station_list_prev (GvStationList *self, GvStation *station, gboolean repeat, gboolean shuffle); GvStation *gv_station_list_next (GvStationList *self, GvStation *station, gboolean repeat, gboolean shuffle); GvStation *gv_station_list_find (GvStationList *self, GvStation *station); GvStation *gv_station_list_find_by_name (GvStationList *self, const gchar *name); GvStation *gv_station_list_find_by_uri (GvStationList *self, const gchar *uri); GvStation *gv_station_list_find_by_uid (GvStationList *self, const gchar *uid); GvStation *gv_station_list_find_by_guessing(GvStationList *self, const gchar *string); /* Iterator methods */ GvStationListIter *gv_station_list_iter_new (GvStationList *self); void gv_station_list_iter_free(GvStationListIter *iter); gboolean gv_station_list_iter_loop(GvStationListIter *iter, GvStation **station); /* Property accessors */ const gchar *gv_station_list_get_load_path(GvStationList *self); const gchar *gv_station_list_get_save_path(GvStationList *self); goodvibes-v0.7.2/src/core/gv-station.c000066400000000000000000000255041414415210700176520ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core-internal.h" #include "core/gv-playlist.h" #include "core/gv-station.h" /* * Properties */ #define DEFAULT_INSECURE FALSE enum { /* Reserved */ PROP_0, /* Set at construct-time */ PROP_UID, /* Set by user - station definition */ PROP_NAME, PROP_URI, /* Set by user - customization */ PROP_INSECURE, PROP_USER_AGENT, /* Learnt along the way */ PROP_STREAM_URIS, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * Signals */ enum { SIGNAL_PLAYLIST_DOWNLOADED, /* Number of signals */ SIGNAL_N }; static guint signals[SIGNAL_N]; /* * GObject definitions */ struct _GvStationPrivate { /* * Properties */ /* Set at construct-time */ gchar *uid; /* Set by user - station definition */ gchar *name; gchar *uri; /* Set by user - customization */ gboolean insecure; gchar *user_agent; /* Learnt along the way */ GSList *stream_uris; }; typedef struct _GvStationPrivate GvStationPrivate; struct _GvStation { /* Parent instance structure */ GInitiallyUnowned parent_instance; /* Private data */ GvStationPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvStation, gv_station, G_TYPE_INITIALLY_UNOWNED) /* * Helpers */ static gpointer copy_func_strdup(gconstpointer src, gpointer data G_GNUC_UNUSED) { return g_strdup(src); } static void gv_station_set_stream_uris(GvStation *self, GSList *uris) { GvStationPrivate *priv = self->priv; if (uris == priv->stream_uris) return; if (priv->stream_uris) { g_slist_free_full(priv->stream_uris, g_free); priv->stream_uris = NULL; } if (uris) priv->stream_uris = g_slist_copy_deep(uris, copy_func_strdup, NULL); g_object_notify(G_OBJECT(self), "stream-uris"); } static void gv_station_set_stream_uri(GvStation *self, const gchar *uri) { GSList *list; if (uri == NULL) list = NULL; else list = g_slist_append(NULL, (gpointer) uri); gv_station_set_stream_uris(self, list); g_slist_free(list); } /* * Signal handlers */ static void on_playlist_downloaded(GvPlaylist *playlist, GvStation *self) { GSList *streams; streams = gv_playlist_get_stream_list(playlist); gv_station_set_stream_uris(self, streams); g_object_unref(playlist); } /* * Property accessors */ const gchar * gv_station_get_uid(GvStation *self) { return self->priv->uid; } const gchar * gv_station_get_name(GvStation *self) { return self->priv->name; } void gv_station_set_name(GvStation *self, const gchar *name) { GvStationPrivate *priv = self->priv; /* What should be used when there's no name for the station ? * NULL or empty string ? Let's settle the question here. */ if (name && *name == '\0') name = NULL; if (!g_strcmp0(priv->name, name)) return; g_free(priv->name); priv->name = g_strdup(name); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_NAME]); } const gchar * gv_station_get_uri(GvStation *self) { return self->priv->uri; } void gv_station_set_uri(GvStation *self, const gchar *uri) { GvStationPrivate *priv = self->priv; /* Setting the uri to NULL is forbidden */ if (uri == NULL) { if (priv->uri == NULL) { /* Construct-time set. Setting the uri to NULL * at this moment is an error in the code. */ ERROR("Creating station with an empty uri"); /* Program execution stops here */ } else { /* User is trying to set the uri to null, we just * silently discard the request. */ DEBUG("Trying to set station uri to null. Ignoring."); return; } } /* Set uri */ if (!g_strcmp0(priv->uri, uri)) return; g_free(priv->uri); priv->uri = g_strdup(uri); /* The uri either refers to a playlist, either to an audio stream. * We "guess" it right now: if it does not seem to be a playlist, * then it's probably an audio stream, and so we save it as such. */ if (gv_playlist_get_format(uri) == GV_PLAYLIST_FORMAT_UNKNOWN) gv_station_set_stream_uri(self, uri); else gv_station_set_stream_uri(self, NULL); /* Notify */ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_URI]); } const gchar * gv_station_get_name_or_uri(GvStation *self) { GvStationPrivate *priv = self->priv; return priv->name ? priv->name : priv->uri; } gboolean gv_station_get_insecure(GvStation *self) { return self->priv->insecure; } void gv_station_set_insecure(GvStation *self, gboolean insecure) { GvStationPrivate *priv = self->priv; if (insecure == priv->insecure) return; priv->insecure = insecure; g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_INSECURE]); } const gchar * gv_station_get_user_agent(GvStation *self) { return self->priv->user_agent; } void gv_station_set_user_agent(GvStation *self, const gchar *user_agent) { GvStationPrivate *priv = self->priv; if (user_agent && user_agent[0] == '\0') user_agent = NULL; if (!g_strcmp0(priv->user_agent, user_agent)) return; g_free(priv->user_agent); priv->user_agent = g_strdup(user_agent); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_USER_AGENT]); } GSList * gv_station_get_stream_uris(GvStation *self) { return self->priv->stream_uris; } const gchar * gv_station_get_first_stream_uri(GvStation *self) { GSList *uris; uris = self->priv->stream_uris; if (uris == NULL) return NULL; return (const gchar *) uris->data; } static void gv_station_get_property(GObject *object, guint property_id, GValue *value G_GNUC_UNUSED, GParamSpec *pspec) { GvStation *self = GV_STATION(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_UID: g_value_set_string(value, gv_station_get_uid(self)); break; case PROP_NAME: g_value_set_string(value, gv_station_get_name(self)); break; case PROP_URI: g_value_set_string(value, gv_station_get_uri(self)); break; case PROP_INSECURE: g_value_set_boolean(value, gv_station_get_insecure(self)); break; case PROP_USER_AGENT: g_value_set_string(value, gv_station_get_user_agent(self)); break; case PROP_STREAM_URIS: g_value_set_pointer(value, gv_station_get_stream_uris(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_station_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvStation *self = GV_STATION(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_NAME: gv_station_set_name(self, g_value_get_string(value)); break; case PROP_URI: gv_station_set_uri(self, g_value_get_string(value)); break; case PROP_INSECURE: gv_station_set_insecure(self, g_value_get_boolean(value)); break; case PROP_USER_AGENT: gv_station_set_user_agent(self, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public methods */ gboolean gv_station_download_playlist(GvStation *self) { GvStationPrivate *priv = self->priv; GvPlaylist *playlist; if (priv->uri == NULL) { WARNING("No uri to download"); return FALSE; } if (gv_playlist_get_format(priv->uri) == GV_PLAYLIST_FORMAT_UNKNOWN) { WARNING("Uri doesn't seem to be a playlist"); return FALSE; } /* No need to keep track of that, it's unreferenced in the callback */ playlist = gv_playlist_new(priv->uri); g_signal_connect_object(playlist, "downloaded", G_CALLBACK(on_playlist_downloaded), self, 0); gv_playlist_download(playlist, priv->insecure, priv->user_agent ? priv->user_agent : gv_core_user_agent); return TRUE; } gchar * gv_station_make_name(GvStation *self, gboolean escape) { GvStationPrivate *priv = self->priv; gchar *str; str = priv->name ? priv->name : priv->uri; if (escape) str = g_markup_escape_text(str, -1); else str = g_strdup(str); return str; } GvStation * gv_station_new(const gchar *name, const gchar *uri) { return g_object_new(GV_TYPE_STATION, "name", name, "uri", uri, NULL); } /* * GObject methods */ static void gv_station_finalize(GObject *object) { GvStationPrivate *priv = GV_STATION(object)->priv; TRACE("%p", object); /* Free any allocated resources */ if (priv->stream_uris) g_slist_free_full(priv->stream_uris, g_free); g_free(priv->uid); g_free(priv->name); g_free(priv->uri); g_free(priv->user_agent); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_station, object); } static void gv_station_constructed(GObject *object) { GvStation *self = GV_STATION(object); GvStationPrivate *priv = self->priv; TRACE("%p", object); /* Initialize properties */ priv->uid = g_strdup_printf("%p", self); priv->insecure = DEFAULT_INSECURE; /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_station, object); } static void gv_station_init(GvStation *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_station_get_instance_private(self); } static void gv_station_class_init(GvStationClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_station_finalize; object_class->constructed = gv_station_constructed; /* Properties */ object_class->get_property = gv_station_get_property; object_class->set_property = gv_station_set_property; properties[PROP_UID] = g_param_spec_string("uid", "UID", NULL, NULL, GV_PARAM_READABLE); properties[PROP_NAME] = g_param_spec_string("name", "Name", NULL, NULL, GV_PARAM_READWRITE | G_PARAM_CONSTRUCT); properties[PROP_URI] = g_param_spec_string("uri", "Uri", NULL, NULL, GV_PARAM_READWRITE | G_PARAM_CONSTRUCT); properties[PROP_INSECURE] = g_param_spec_boolean("insecure", "Insecure", NULL, DEFAULT_INSECURE, GV_PARAM_READWRITE); properties[PROP_USER_AGENT] = g_param_spec_string("user-agent", "User agent", NULL, NULL, GV_PARAM_READWRITE); properties[PROP_STREAM_URIS] = g_param_spec_pointer("stream-uris", "Stream uris", NULL, GV_PARAM_READABLE); g_object_class_install_properties(object_class, PROP_N, properties); /* Signals */ signals[SIGNAL_PLAYLIST_DOWNLOADED] = g_signal_new("playlist-downloaded", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } goodvibes-v0.7.2/src/core/gv-station.h000066400000000000000000000037101414415210700176520ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include /* GObject declarations */ #define GV_TYPE_STATION gv_station_get_type() G_DECLARE_FINAL_TYPE(GvStation, gv_station, GV, STATION, GInitiallyUnowned) /* Methods */ GvStation *gv_station_new (const gchar *name, const gchar *uri); gchar *gv_station_make_name (GvStation *self, gboolean escape); gboolean gv_station_download_playlist(GvStation *self); /* Property accessors */ const gchar *gv_station_get_uid (GvStation *self); const gchar *gv_station_get_name (GvStation *self); void gv_station_set_name (GvStation *self, const gchar *name); const gchar *gv_station_get_uri (GvStation *self); void gv_station_set_uri (GvStation *self, const gchar *uri); const gchar *gv_station_get_name_or_uri (GvStation *self); GSList *gv_station_get_stream_uris (GvStation *self); const gchar *gv_station_get_first_stream_uri(GvStation *self); gboolean gv_station_get_insecure (GvStation *self); void gv_station_set_insecure (GvStation *self, gboolean insecure); const gchar *gv_station_get_user_agent (GvStation *self); void gv_station_set_user_agent (GvStation *self, const gchar *user_agent); goodvibes-v0.7.2/src/core/gv-streaminfo.c000066400000000000000000000170031414415210700203330ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ /** * SECTION:gv-streaminfo * @title: GvStreaminfo * @short_description: Technical information about a stream * * Streaminfo is a term that appears in the GStreamer documentation, * and that was reused here in Goodvibes. So you might want to read * the GStreamer documentation first: * * * Streaminfo are tags that describe the technical parts of stream content. * * A GvStreaminfo object is created and updated by GvEngine, according to the * data that is provided by GStreamer during the playback. More specifically, * some fields are updated from a list of Gstreamer tags, while others are * updated from the caps of the GStreamer audio pad. * * When a new stream is being played, a new GvStreaminfo is created. Then we * expect most of the fields of the GvStreaminfo to be settled after a few * seconds of playback, except for the bitrate fields. In particular, for a * variable bitrate stream, the bitrate will change constantly, every second or * so. The minimum and maximum bitrate are also updated when a new minimum or a * new maximum is reached. * * There's no data aggregation done in the various update() functions. It means * that zero (or NULL) is considered a valid value, rather than "unset". For * examples, if we update from GstTags, and the nominal bitrate value is zero * in those tags, then we update this value to zero in GvStreaminfo, rather * than considering than zero is unset, and not updating the value in * GvStreaminfo. */ #include #include #include #include "base/gv-base.h" #include "core/gv-streaminfo.h" /* * GObject definitions */ G_DEFINE_BOXED_TYPE(GvStreaminfo, gv_streaminfo, gv_streaminfo_ref, gv_streaminfo_unref); struct _GvStreaminfo { GvStreamBitrate bitrate; guint channels; gchar *codec; guint sample_rate; GvStreamType stream_type; /*< private >*/ volatile guint ref_count; }; /* * Public methods */ void gv_streaminfo_get_bitrate(GvStreaminfo *self, GvStreamBitrate *bitrate) { g_return_if_fail(bitrate != NULL); *bitrate = self->bitrate; } guint gv_streaminfo_get_channels(GvStreaminfo *self) { return self->channels; } const gchar * gv_streaminfo_get_codec(GvStreaminfo *self) { return self->codec; } guint gv_streaminfo_get_sample_rate(GvStreaminfo *self) { return self->sample_rate; } GvStreamType gv_streaminfo_get_stream_type(GvStreaminfo *self) { return self->stream_type; } gboolean gv_streaminfo_update_from_element_setup(GvStreaminfo *self, GstElement *element) { GList *pads; GvStreamType type = GV_STREAM_TYPE_UNKNOWN; gboolean changed = FALSE; pads = gst_element_get_pad_template_list(element); for (; pads != NULL; pads = g_list_next(pads)) { GstPadTemplate *pad_template; GstCaps *caps; guint i; pad_template = (GstPadTemplate *) (pads->data); if (pad_template->direction != GST_PAD_SINK) continue; caps = gst_pad_template_get_caps(pad_template); // DEBUG("Caps: %s", gst_caps_to_string(caps)); // should be freed for (i = 0; i < gst_caps_get_size(caps); i++) { GstStructure *structure; const gchar *struct_name; structure = gst_caps_get_structure(caps, i); struct_name = gst_structure_get_name(structure); if (!g_strcmp0(struct_name, "application/x-icy")) { //DEBUG("HTTP+Icy stream"); type = GV_STREAM_TYPE_HTTP_ICY; break; } else if (!g_strcmp0(struct_name, "application/x-hls")) { //DEBUG("HLS stream"); type = GV_STREAM_TYPE_HLS; break; } else if (!g_strcmp0(struct_name, "application/dash+xml")) { //DEBUG("DASH stream"); type = GV_STREAM_TYPE_DASH; break; } } gst_caps_unref(caps); } if (type != GV_STREAM_TYPE_UNKNOWN && type != self->stream_type) { self->stream_type = type; changed = TRUE; } return changed; } gboolean gv_streaminfo_update_from_gst_audio_pad(GvStreaminfo *self, GstPad *audio_pad) { GstCaps *caps; gint channels; gint sample_rate; gboolean changed = FALSE; g_return_val_if_fail(self != NULL, FALSE); caps = audio_pad ? gst_pad_get_current_caps(audio_pad) : NULL; if (caps) { GstStructure *s; // DEBUG("Caps: %s", gst_caps_to_string(caps)); // should be freed s = gst_caps_get_structure(caps, 0); gst_structure_get_int(s, "channels", &channels); gst_structure_get_int(s, "rate", &sample_rate); gst_caps_unref(caps); } else { channels = 0; sample_rate = 0; } if ((guint) channels != self->channels) { self->channels = channels; changed = TRUE; } if ((guint) sample_rate != self->sample_rate) { self->sample_rate = sample_rate; changed = TRUE; } /* We don't have a good way to detect if the stream type is HTTP, * so what we do instead is to assume that if the stream type is * still unknown at this point, then let it be HTTP. It might be * updated later if we know better. */ if (self->stream_type == GV_STREAM_TYPE_UNKNOWN) { self->stream_type = GV_STREAM_TYPE_HTTP; changed = TRUE; } return changed; } gboolean gv_streaminfo_update_from_gst_taglist(GvStreaminfo *self, GstTagList *taglist) { const gchar *codec = NULL; guint bitrate = 0; guint maximum_bitrate = 0; guint minimum_bitrate = 0; guint nominal_bitrate = 0; gboolean changed = FALSE; g_return_val_if_fail(self != NULL, FALSE); g_return_val_if_fail(taglist != NULL, FALSE); gst_tag_list_peek_string_index(taglist, GST_TAG_AUDIO_CODEC, 0, &codec); gst_tag_list_get_uint_index(taglist, GST_TAG_BITRATE, 0, &bitrate); gst_tag_list_get_uint_index(taglist, GST_TAG_MAXIMUM_BITRATE, 0, &maximum_bitrate); gst_tag_list_get_uint_index(taglist, GST_TAG_MINIMUM_BITRATE, 0, &minimum_bitrate); gst_tag_list_get_uint_index(taglist, GST_TAG_NOMINAL_BITRATE, 0, &nominal_bitrate); if (g_strcmp0(codec, self->codec)) { g_free(self->codec); self->codec = g_strdup(codec); changed = TRUE; } if (bitrate != self->bitrate.current) { self->bitrate.current = bitrate; changed = TRUE; } if (maximum_bitrate != self->bitrate.maximum) { self->bitrate.maximum = maximum_bitrate; changed = TRUE; } if (minimum_bitrate != self->bitrate.minimum) { self->bitrate.minimum = minimum_bitrate; changed = TRUE; } if (nominal_bitrate != self->bitrate.nominal) { self->bitrate.nominal = nominal_bitrate; changed = TRUE; } return changed; } void gv_streaminfo_unref(GvStreaminfo *self) { g_return_if_fail(self != NULL); g_return_if_fail(g_atomic_int_get(&self->ref_count) > 0); if (!g_atomic_int_dec_and_test(&self->ref_count)) return; g_free(self->codec); g_free(self); } GvStreaminfo * gv_streaminfo_ref(GvStreaminfo *self) { g_return_val_if_fail(self != NULL, NULL); g_return_val_if_fail(g_atomic_int_get(&self->ref_count) > 0, NULL); g_atomic_int_inc(&self->ref_count); return self; } GvStreaminfo * gv_streaminfo_new(void) { GvStreaminfo *self; self = g_new0(GvStreaminfo, 1); self->ref_count = 1; return self; } goodvibes-v0.7.2/src/core/gv-streaminfo.h000066400000000000000000000043661414415210700203500ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_STREAMINFO gv_streaminfo_get_type() GType gv_streaminfo_get_type(void) G_GNUC_CONST; typedef struct _GvStreaminfo GvStreaminfo; typedef struct _GvStreamBitrate GvStreamBitrate; struct _GvStreamBitrate { guint current; guint maximum; guint minimum; guint nominal; }; typedef enum { GV_STREAM_TYPE_UNKNOWN = 0, GV_STREAM_TYPE_HTTP, GV_STREAM_TYPE_HTTP_ICY, GV_STREAM_TYPE_HLS, GV_STREAM_TYPE_DASH, } GvStreamType; /* Methods */ GvStreaminfo *gv_streaminfo_new (void); GvStreaminfo *gv_streaminfo_ref (GvStreaminfo *self); void gv_streaminfo_unref(GvStreaminfo *self); #define gv_clear_streaminfo(object_ptr) \ g_clear_pointer((object_ptr), gv_streaminfo_unref) gboolean gv_streaminfo_update_from_element_setup(GvStreaminfo *self, GstElement *element); gboolean gv_streaminfo_update_from_gst_audio_pad(GvStreaminfo *self, GstPad *audio_pad); gboolean gv_streaminfo_update_from_gst_taglist (GvStreaminfo *self, GstTagList *taglist); void gv_streaminfo_get_bitrate (GvStreaminfo *self, GvStreamBitrate *bitrate); guint gv_streaminfo_get_channels (GvStreaminfo *self); const gchar *gv_streaminfo_get_codec (GvStreaminfo *self); guint gv_streaminfo_get_sample_rate (GvStreaminfo *self); GvStreamType gv_streaminfo_get_stream_type (GvStreaminfo *self); goodvibes-v0.7.2/src/core/meson.build000066400000000000000000000016141414415210700175510ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only # Sources and dependencies core_sources = [ 'gst-additions.c', 'gv-core.c', 'gv-engine.c', 'gv-metadata.c', 'gv-player.c', 'gv-playlist.c', 'gv-station.c', 'gv-station-list.c', 'gv-streaminfo.c', ] core_dependencies = [ glib_dep, gobject_dep, gio_dep, gst_dep, gst_audio_dep, gst_base_dep, libsoup_dep, gvbase_dep, ] core_enum_headers = [ 'gv-engine.h', 'gv-player.h' ] core_enums = gnome.mkenums_simple('gv-core-enum-types', sources: core_enum_headers ) core_enum_h = core_enums[1] # Library definition gvcore = static_library('gvcore', sources: [ core_enums, core_sources ], dependencies: core_dependencies, include_directories: root_inc, ) gvcore_dep = declare_dependency( dependencies: core_dependencies, sources: [ core_enum_h ], link_with: gvcore, ) # Tests if get_option('tests') subdir('tests') endif goodvibes-v0.7.2/src/core/tests/000077500000000000000000000000001414415210700165475ustar00rootroot00000000000000goodvibes-v0.7.2/src/core/tests/meson.build000066400000000000000000000004211414415210700207060ustar00rootroot00000000000000unit_tests = [ 'metadata', 'station-list', ] if mutest_dep.found() foreach unit: unit_tests test(unit, executable(unit, unit + '.c', dependencies: [ gvcore_dep, mutest_dep ], include_directories: root_inc, ), ) endforeach endif goodvibes-v0.7.2/src/core/tests/metadata.c000066400000000000000000000035731414415210700205030ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "base/log.h" #include "core/gv-metadata.h" static void metadata_empty(mutest_spec_t *spec G_GNUC_UNUSED) { GvMetadata *m; GstTagList *l; gboolean changed; m = gv_metadata_new(); mutest_expect("new() does not return null", mutest_pointer(m), mutest_not, mutest_to_be_null, NULL); mutest_expect("new metadata is empty", mutest_bool_value(gv_metadata_is_empty(m)), mutest_to_be_true, NULL); l = gst_tag_list_new_empty(); changed = gv_metadata_update_from_gst_taglist(m, l); mutest_expect("update from empty gst taglist returns false", mutest_bool_value(changed), mutest_to_be_false, NULL); mutest_expect("metadata is still empty", mutest_bool_value(gv_metadata_is_empty(m)), mutest_to_be_true, NULL); gst_tag_list_unref(l); gv_metadata_unref(m); } static void metadata_suite(mutest_suite_t *suite G_GNUC_UNUSED) { mutest_it("update from empty gst taglist", metadata_empty); } MUTEST_MAIN( gst_init(NULL, NULL); log_init(NULL, TRUE, NULL); g_setenv("GOODVIBES_IN_TEST_SUITE", "1", TRUE); mutest_describe("gv-metadata", metadata_suite); ) goodvibes-v0.7.2/src/core/tests/station-list.c000066400000000000000000000304671414415210700213570ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2019-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "base/log.h" #include "core/gv-station-list.h" #include "default-stations.h" #define TOUCHTMP(tmpl) g_close(g_mkstemp(tmpl), NULL) static void station_list_load_default(mutest_spec_t *spec G_GNUC_UNUSED) { GvStationList *s; s = gv_station_list_new_from_xdg_dirs(DEFAULT_STATIONS); g_object_add_weak_pointer(G_OBJECT(s), (gpointer *) &s); mutest_expect("new() does not return null", mutest_pointer(s), mutest_not, mutest_to_be_null, NULL); gv_station_list_load(s); mutest_expect("length() is not zero", mutest_int_value(gv_station_list_length(s)), mutest_not, mutest_to_be, 0, NULL); g_object_unref(s); mutest_expect("finalize() was called", mutest_pointer(s), mutest_to_be_null, NULL); } static gsize get_file_length(const gchar *path) { GError *err = NULL; gchar *contents = NULL; gsize length; g_file_get_contents(path, &contents, &length, &err); g_assert_no_error(err); g_free(contents); return length; } static void station_list_load_save_empty(mutest_spec_t *spec G_GNUC_UNUSED) { GvStationList *s; gchar *input, *output; gchar template[] = "/tmp/gv-station-list-XXXXXX.xml"; TOUCHTMP(template); /* First, test with empty file */ input = "/dev/null"; output = template; mutest_expect("input file is empty", mutest_int_value(get_file_length(input)), mutest_to_be, 0, NULL); s = gv_station_list_new_from_paths(input, output); g_object_add_weak_pointer(G_OBJECT(s), (gpointer *) &s); mutest_expect("new_with_paths() does not return null", mutest_pointer(s), mutest_not, mutest_to_be_null, NULL); gv_station_list_load(s); mutest_expect("length() is zero", mutest_int_value(gv_station_list_length(s)), mutest_to_be, 0, NULL); mutest_expect("first() is null", mutest_pointer(gv_station_list_first(s)), mutest_to_be_null, NULL); mutest_expect("last() is null", mutest_pointer(gv_station_list_last(s)), mutest_to_be_null, NULL); mutest_expect("prev() is null", mutest_pointer(gv_station_list_prev(s, NULL, FALSE, FALSE)), mutest_to_be_null, NULL); mutest_expect("next() is null", mutest_pointer(gv_station_list_next(s, NULL, FALSE, FALSE)), mutest_to_be_null, NULL); gv_station_list_save(s); g_object_unref(s); mutest_expect("finalize() was called", mutest_pointer(s), mutest_to_be_null, NULL); mutest_expect("station list was saved to file", mutest_bool_value(g_file_test(output, G_FILE_TEST_EXISTS)), mutest_to_be_true, NULL); /* Second, test with file that contains an empty station list */ input = output; mutest_expect("input file is NOT empty", mutest_int_value(get_file_length(input)), mutest_not, mutest_to_be, 0, NULL); s = gv_station_list_new_from_paths(input, output); g_object_add_weak_pointer(G_OBJECT(s), (gpointer *) &s); mutest_expect("new_with_paths() does not return null", mutest_pointer(s), mutest_not, mutest_to_be_null, NULL); gv_station_list_load(s); mutest_expect("length() is zero", mutest_int_value(gv_station_list_length(s)), mutest_to_be, 0, NULL); g_object_unref(s); mutest_expect("finalize() was called", mutest_pointer(s), mutest_to_be_null, NULL); g_unlink(output); } /* Match a GvStationList against an array. Consume the array */ static bool match_station_list_against_array(mutest_expect_t *e, mutest_expect_res_t *check) { mutest_expect_res_t *value = mutest_expect_value(e); GvStationList *s = (GvStationList *) mutest_get_pointer(value); GPtrArray *array = (GPtrArray *) mutest_get_pointer(check); gboolean ret = TRUE; guint i; for (i = 0; i < array->len; i++) { GvStation *a = (GvStation *) g_ptr_array_index(array, i); GvStation *b = gv_station_list_at(s, i); if (a != b) { ret = FALSE; break; } } if (i != gv_station_list_length(s)) ret = FALSE; g_ptr_array_free(array, FALSE); return ret; } /* Create a GPtrArray of GvStations */ static GPtrArray * make_station_array(GvStation *stations[], ...) { GPtrArray *array; va_list args; gint idx; array = g_ptr_array_new(); va_start(args, stations); while ((idx = va_arg(args, gint)) != -1) { g_ptr_array_add(array, stations[idx]); } va_end(args); return array; } static void station_list_add_move_remove(mutest_spec_t *spec G_GNUC_UNUSED) { GvStationList *s; GvStation *ss[7]; gchar *input, *output; guint i; input = "/dev/null"; output = "/dev/null"; s = gv_station_list_new_from_paths(input, output); g_object_add_weak_pointer(G_OBJECT(s), (gpointer *) &s); /* Create stations. Due to floating reference, stations are finalized * when removed from the station list. And due to the weak pointers that * we add here, when it happens, the pointer value is set to NULL in the * array, so that at the end of the test, we can check that the array * is completely NULL. */ for (i = 0; i < 7; i++) { gchar *name = g_strdup_printf("s%u", i); gchar *url = g_strdup_printf("http://sta%u.com", i); ss[i] = gv_station_new(name, url); g_object_add_weak_pointer(G_OBJECT(ss[i]), (gpointer *) &ss[i]); g_free(name); g_free(url); } /* Station list is empty to start with */ mutest_expect("list is []", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, -1)), NULL); /* Populate with 3 stations, using append() and prepend() */ gv_station_list_append(s, ss[1]); mutest_expect("list is [1]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 1, -1)), NULL); gv_station_list_append(s, ss[2]); mutest_expect("list is [1, 2]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 1, 2, -1)), NULL); gv_station_list_prepend(s, ss[0]); mutest_expect("list is [0, 1, 2]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 0, 1, 2, -1)), NULL); /* Insert ss[3] after first, then remove first */ gv_station_list_insert_after(s, ss[3], gv_station_list_first(s)); mutest_expect("list is [0, 3, 1, 2]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 0, 3, 1, 2, -1)), NULL); gv_station_list_remove(s, gv_station_list_first(s)); mutest_expect("list is [3, 1, 2]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 3, 1, 2, -1)), NULL); /* Insert ss[4] after last */ gv_station_list_insert_after(s, ss[4], gv_station_list_last(s)); mutest_expect("list is [3, 1, 2, 4]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 3, 1, 2, 4, -1)), NULL); /* Insert ss[5] before first */ gv_station_list_insert_before(s, ss[5], gv_station_list_first(s)); mutest_expect("list is [5, 3, 1, 2, 4]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 5, 3, 1, 2, 4, -1)), NULL); /* Insert ss[6] before last, then remove last */ gv_station_list_insert_before(s, ss[6], gv_station_list_last(s)); mutest_expect("list is [5, 3, 1, 2, 6, 4]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 5, 3, 1, 2, 6, 4, -1)), NULL); gv_station_list_remove(s, gv_station_list_last(s)); mutest_expect("list is [5, 3, 1, 2, 6]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 5, 3, 1, 2, 6, -1)), NULL); /* Move ss[5] before last */ gv_station_list_move_before(s, ss[5], gv_station_list_last(s)); mutest_expect("list is [3, 1, 2, 5, 6]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 3, 1, 2, 5, 6, -1)), NULL); /* Move ss[3] (ie. first) before first */ gv_station_list_move_after(s, ss[3], gv_station_list_first(s)); mutest_expect("list is [3, 1, 2, 5, 6]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 3, 1, 2, 5, 6, -1)), NULL); /* Move ss[3] (ie. first) after first */ gv_station_list_move_before(s, ss[3], gv_station_list_first(s)); mutest_expect("list is [3, 1, 2, 5, 6]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 3, 1, 2, 5, 6, -1)), NULL); /* Move ss[2] at last */ gv_station_list_move_last(s, ss[2]); mutest_expect("list is [3, 1, 5, 6, 2]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 3, 1, 5, 6, 2, -1)), NULL); /* Move ss[2] at first */ gv_station_list_move_first(s, ss[2]); mutest_expect("list is [2, 3, 1, 5, 6]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 2, 3, 1, 5, 6, -1)), NULL); /* Move ss[1] at position 0 */ gv_station_list_move(s, ss[1], 0); mutest_expect("list is [1, 2, 3, 5, 6]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 1, 2, 3, 5, 6, -1)), NULL); /* Time to remove stations one by one */ gv_station_list_remove(s, ss[1]); mutest_expect("list is [2, 3, 5, 6]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 2, 3, 5, 6, -1)), NULL); gv_station_list_remove(s, ss[6]); mutest_expect("list is [2, 3, 5]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 2, 3, 5, -1)), NULL); gv_station_list_remove(s, ss[3]); mutest_expect("list is [2, 5]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 2, 5, -1)), NULL); gv_station_list_remove(s, ss[2]); mutest_expect("list is [5]", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, 5, -1)), NULL); gv_station_list_remove(s, ss[5]); mutest_expect("list is []", mutest_pointer(s), match_station_list_against_array, mutest_pointer(make_station_array(ss, -1)), NULL); for (i = 0; i < 7; i++) g_assert_null(ss[i]); g_object_unref(s); g_assert_null(s); } static void station_list_suite(mutest_suite_t *suite G_GNUC_UNUSED) { gchar *tmpdir; /* A station list can be loaded/saved from/to the XDG directories, * so for unit tests, we must make sure to set the various XDG env * variables, so that we don't mess up with the test environment. */ tmpdir = g_dir_make_tmp("gv-station-list-XXXXXX", NULL); g_assert_nonnull(tmpdir); g_setenv("XDG_DATA_HOME", tmpdir, TRUE); g_setenv("XDG_DATA_DIRS", tmpdir, TRUE); g_setenv("XDG_CONFIG_HOME", tmpdir, TRUE); g_setenv("XDG_CONFIG_DIRS", tmpdir, TRUE); mutest_it("load the default station list", station_list_load_default); mutest_it("load and save an empty station list", station_list_load_save_empty); mutest_it("add, move and remove stations", station_list_add_move_remove); g_assert_true(g_rmdir(tmpdir) == 0); g_free(tmpdir); } MUTEST_MAIN( log_init(NULL, TRUE, NULL); g_setenv("GOODVIBES_IN_TEST_SUITE", "1", TRUE); mutest_describe("gv-station-list", station_list_suite); ) goodvibes-v0.7.2/src/default-stations.h000066400000000000000000000073121414415210700201170ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once /* * FIP * Just the best radios you'll ever listen to. */ #define DEFAULT_STATIONS_FIP \ "" \ " FIP" \ " https://stream.radiofrance.fr/fip/fip_hifi.m3u8" \ "" \ "" \ " FIP Electro" \ " https://stream.radiofrance.fr/fipelectro/fipelectro_hifi.m3u8" \ "" \ "" \ " FIP Groove" \ " https://stream.radiofrance.fr/fipgroove/fipgroove_hifi.m3u8" \ "" \ "" \ " FIP Jazz" \ " https://stream.radiofrance.fr/fipjazz/fipjazz_hifi.m3u8" \ "" \ "" \ " FIP Monde" \ " https://stream.radiofrance.fr/fipworld/fipworld_hifi.m3u8" \ "" \ "" \ " FIP Nouveautés" \ " https://stream.radiofrance.fr/fipnouveautes/fipnouveautes_hifi.m3u8" \ "" \ "" \ " FIP Pop" \ " https://stream.radiofrance.fr/fippop/fippop_hifi.m3u8" \ "" \ "" \ " FIP Reggae" \ " https://stream.radiofrance.fr/fipreggae/fipreggae_hifi.m3u8" \ "" \ "" \ " FIP Rock" \ " https://stream.radiofrance.fr/fiprock/fiprock_hifi.m3u8" \ "" /* * Nova * Another killer radio from France. */ #define DEFAULT_STATIONS_NOVA \ "" \ " Nova" \ " http://novazz.ice.infomaniak.ch/novazz-128.mp3" \ "" \ "" \ " Nova Classics" \ " http://nova-vnt.ice.infomaniak.ch/nova-vnt-128.mp3" \ "" \ "" \ " Nova Danse" \ " http://nova-dance.ice.infomaniak.ch/nova-dance-128.mp3" \ "" \ "" \ " Nova la Nuit" \ " http://nova-ln.ice.infomaniak.ch/nova-ln-128.mp3" \ "" \ "" \ " Nova Nouvo" \ " http://nova-nouvo.ice.infomaniak.ch/nova-nouvo-128.mp3" \ "" /* * More of my favorite radios. * - PBB * - Radio Grenouille */ #define DEFAULT_STATIONS_MISC \ "" \ " Pedro Basement Broadcast" \ " https://pbbradio.com:8443/pbb128 " \ "" \ "" \ " Radio Grenouille" \ " http://live.radiogrenouille.com/live" \ "" /* * Default stations */ #define DEFAULT_STATIONS \ "" \ DEFAULT_STATIONS_FIP \ DEFAULT_STATIONS_NOVA \ DEFAULT_STATIONS_MISC \ "" goodvibes-v0.7.2/src/feat/000077500000000000000000000000001414415210700153745ustar00rootroot00000000000000goodvibes-v0.7.2/src/feat/gv-console-output.c000066400000000000000000000124731414415210700211610ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "base/glib-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "feat/gv-console-output.h" /* * GObject definitions */ struct _GvConsoleOutput { /* Parent instance structure */ GvFeature parent_instance; }; G_DEFINE_TYPE(GvConsoleOutput, gv_console_output, GV_TYPE_FEATURE) /* * Helpers */ #define PRINT(fmt, ...) g_print(fmt "\n", ##__VA_ARGS__) static const gchar * time_now(void) { static gchar *now_str; GDateTime *now; g_free(now_str); now = g_date_time_new_now_local(); now_str = g_date_time_format(now, "%T"); g_date_time_unref(now); return now_str; } static void print_hello_line(void) { PRINT("---- " GV_NAME_CAPITAL " " PACKAGE_VERSION " ----"); PRINT("Hit Ctrl+C to quit..."); } static void print_goodbye_line(void) { PRINT("---- Bye ----"); } static void print_error(const gchar *error_string) { PRINT(VT_BOLD("Error!") " %s", error_string); } static void print_station(GvStation *station) { const gchar *str; if (station == NULL) return; str = gv_station_get_name(station); if (str) { PRINT(VT_BOLD("> %s Playing %s"), time_now(), str); } else { str = gv_station_get_uri(station); PRINT(VT_BOLD("> %s Playing <%s>"), time_now(), str); } } static void print_metadata(GvMetadata *metadata) { const gchar *artist; const gchar *title; const gchar *album; const gchar *year; const gchar *genre; if (metadata == NULL) return; artist = gv_metadata_get_artist(metadata); title = gv_metadata_get_title(metadata); album = gv_metadata_get_album(metadata); year = gv_metadata_get_year(metadata); genre = gv_metadata_get_genre(metadata); /* Ensure this first line is printed, with a timestamp */ if (title == NULL) title = "(Unknown title)"; PRINT(". %s %s", time_now(), title); /* Other fields are optional */ if (artist) PRINT(" %s", artist); if (album && year) PRINT(" %s (%s)", album, year); else if (album) PRINT(" %s", album); else if (year) PRINT(" (%s)", year); if (genre) PRINT(" %s", genre); } /* * Signal handlers */ static void on_player_notify(GvPlayer *player, GParamSpec *pspec, GvConsoleOutput *self G_GNUC_UNUSED) { const gchar *property_name = g_param_spec_get_name(pspec); if (!g_strcmp0(property_name, "playback-state")) { GvPlaybackState state; state = gv_player_get_playback_state(player); if (state == GV_PLAYBACK_STATE_PLAYING) { GvStation *station; station = gv_player_get_station(player); print_station(station); } } else if (!g_strcmp0(property_name, "metadata")) { GvMetadata *metadata; metadata = gv_player_get_metadata(player); print_metadata(metadata); } } static void on_errorable_error(GvErrorable *errorable G_GNUC_UNUSED, const gchar *error_string, GvConsoleOutput *self G_GNUC_UNUSED) { print_error(error_string); } /* * GvFeature methods */ static void gv_console_output_disable(GvFeature *feature) { GvPlayer *player = gv_core_player; GList *item; /* Disconnect signal handlers */ for (item = gv_base_get_objects(); item; item = item->next) { GObject *object = G_OBJECT(item->data); if (GV_IS_ERRORABLE(object) == FALSE) continue; g_signal_handlers_disconnect_by_data(object, feature); } g_signal_handlers_disconnect_by_data(player, feature); /* Say good-bye */ print_goodbye_line(); /* Chain up */ GV_FEATURE_CHAINUP_DISABLE(gv_console_output, feature); } static void gv_console_output_enable(GvFeature *feature) { GvPlayer *player = gv_core_player; GList *item; /* Chain up */ GV_FEATURE_CHAINUP_ENABLE(gv_console_output, feature); /* Say hello */ print_hello_line(); /* Connect to player 'notify' */ g_signal_connect_object(player, "notify", G_CALLBACK(on_player_notify), feature, 0); /* Connect to objects that emit 'error' */ for (item = gv_base_get_objects(); item; item = item->next) { GObject *object = G_OBJECT(item->data); if (GV_IS_ERRORABLE(object) == FALSE) continue; g_signal_connect_object(object, "error", G_CALLBACK(on_errorable_error), feature, 0); } } /* * Public methods */ GvFeature * gv_console_output_new(void) { return gv_feature_new(GV_TYPE_CONSOLE_OUTPUT, "ConsoleOutput", GV_FEATURE_EARLY); } /* * GObject methods */ static void gv_console_output_init(GvConsoleOutput *self) { TRACE("%p", self); } static void gv_console_output_class_init(GvConsoleOutputClass *class) { GvFeatureClass *feature_class = GV_FEATURE_CLASS(class); TRACE("%p", class); /* Override GvFeature methods */ feature_class->enable = gv_console_output_enable; feature_class->disable = gv_console_output_disable; } goodvibes-v0.7.2/src/feat/gv-console-output.h000066400000000000000000000017741414415210700211700ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "base/gv-feature.h" /* GObject declarations */ #define GV_TYPE_CONSOLE_OUTPUT gv_console_output_get_type() G_DECLARE_FINAL_TYPE(GvConsoleOutput, gv_console_output, GV, CONSOLE_OUTPUT, GvFeature) /* Public methods */ GvFeature *gv_console_output_new(void); goodvibes-v0.7.2/src/feat/gv-dbus-server-mpris2.c000066400000000000000000001114511414415210700216300ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "base/glib-additions.h" #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #ifdef GV_UI_ENABLED #include "ui/gv-ui.h" #endif #include "feat/gv-dbus-server-mpris2.h" #include "feat/gv-dbus-server.h" #define TRACKID_PATH GV_APPLICATION_PATH "/TrackList" #define PLAYLISTID_PATH GV_APPLICATION_PATH "/Playlist" #define DBUS_NAME "org.mpris.MediaPlayer2." GV_NAME_CAPITAL #define DBUS_PATH "/org/mpris/MediaPlayer2" #define DBUS_IFACE_ROOT "org.mpris.MediaPlayer2" #define DBUS_IFACE_PLAYER DBUS_IFACE_ROOT ".Player" #define DBUS_IFACE_TRACKLIST DBUS_IFACE_ROOT ".TrackList" #define DBUS_IFACE_PLAYLISTS DBUS_IFACE_ROOT ".Playlists" static const gchar *DBUS_INTROSPECTION = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* * GObject definitions */ struct _GvDbusServerMpris2 { /* Parent instance structure */ GvDbusServer parent_instance; }; G_DEFINE_TYPE(GvDbusServerMpris2, gv_dbus_server_mpris2, GV_TYPE_DBUS_SERVER) /* * Helpers */ static gchar * make_playlist_id(GvStation *station) { /* As suggested in the MPRIS2 specifications, "/" should be used if NULL. * https://specifications.freedesktop.org/mpris-spec/latest/ * Playlists_Interface.html#Struct:Maybe_Playlist */ if (station == NULL) return g_strdup("/"); return g_strdup_printf(PLAYLISTID_PATH "/%s", gv_station_get_uid(station)); } static gboolean parse_playlist_id(const gchar *playlist_id, GvStationList *station_list, GvStation **station) { const gchar *station_uid; g_assert_nonnull(station); *station = NULL; if (!g_strcmp0(playlist_id, "/")) return TRUE; if (!g_str_has_prefix(playlist_id, PLAYLISTID_PATH "/")) return FALSE; station_uid = playlist_id + strlen(PLAYLISTID_PATH "/"); *station = gv_station_list_find_by_uid(station_list, station_uid); if (*station == NULL) return FALSE; return TRUE; } static gchar * make_track_id(GvStation *station) { if (station == NULL) return g_strdup(DBUS_PATH "/TrackList/NoTrack"); return g_strdup_printf(TRACKID_PATH "/%s", gv_station_get_uid(station)); } static gboolean parse_track_id(const gchar *track_id, GvStationList *station_list, GvStation **station) { const gchar *station_uid; g_assert_nonnull(station); *station = NULL; if (!g_strcmp0(track_id, DBUS_PATH "/TrackList/NoTrack")) return TRUE; if (!g_str_has_prefix(track_id, TRACKID_PATH "/")) return FALSE; station_uid = track_id + strlen(TRACKID_PATH "/"); *station = gv_station_list_find_by_uid(station_list, station_uid); if (*station == NULL) return FALSE; return TRUE; } static gint compare_alphabetically(GvStation *a, GvStation *b) { const gchar *str1 = gv_station_get_name_or_uri(a); const gchar *str2 = gv_station_get_name_or_uri(b); return g_strcmp0(str1, str2); } static GList * build_station_list(GvStationList *station_list, gboolean alphabetical, gboolean reverse, guint start_index, guint max_count) { GvStationListIter *iter; GvStation *station; GList *list = NULL; /* Build a GList */ iter = gv_station_list_iter_new(station_list); while (gv_station_list_iter_loop(iter, &station)) list = g_list_append(list, station); gv_station_list_iter_free(iter); /* Order alphabetically if needed */ if (alphabetical) list = g_list_sort(list, (GCompareFunc) compare_alphabetically); /* Reverse order if needed */ if (reverse) list = g_list_reverse(list); /* Handle start index */ while (start_index > 0) { if (list == NULL) break; list = g_list_delete_link(list, list); start_index--; } /* Handle max count */ GList *tmp = list; while (max_count > 0) { if (tmp == NULL) break; tmp = tmp->next; max_count--; } if (tmp != NULL) { if (tmp == list) { g_list_free(list); list = NULL; } else { tmp->prev->next = NULL; tmp->prev = NULL; g_list_free(tmp); } } return list; } /* * GVariant helpers for MPRIS2 types */ static GVariant * g_variant_new_playlist(GvStation *station) { gchar *playlist_id; playlist_id = make_playlist_id(station); GVariant *tuples[] = { g_variant_new_object_path(playlist_id), g_variant_new_string(station ? gv_station_get_name_or_uri(station) : ""), g_variant_new_string("") }; g_free(playlist_id); return g_variant_new_tuple(tuples, 3); } static GVariant * g_variant_new_maybe_playlist(GvStation *station) { GVariant *tuples[] = { g_variant_new_boolean(station ? TRUE : FALSE), g_variant_new_playlist(station) }; return g_variant_new_tuple(tuples, 2); } static GVariant * g_variant_new_metadata_map(GvStation *station, GvMetadata *metadata) { GVariantBuilder b; gchar *track_id; const gchar *station_uri; const gchar *station_name; const gchar *artist; const gchar *title; const gchar *album; const gchar *genre; const gchar *year; const gchar *comment; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); /* Station properties */ if (station == NULL) goto end; track_id = make_track_id(station); g_variant_builder_add_dictentry_object_path(&b, "mpris:trackid", track_id); g_free(track_id); station_uri = gv_station_get_uri(station); if (station_uri) g_variant_builder_add_dictentry_string(&b, "xesam:url", station_uri); /* In the MPRIS2 spec, there's no room for a radio station name. So we set it * in the non-standard field 'goodvibes:station', in case someone cares. */ station_name = gv_station_get_name(station); if (station_name) g_variant_builder_add_dictentry_string(&b, "goodvibes:station", station_name); /* If no metadata, set the station name as the artist. */ if (metadata == NULL) { if (station_name) { g_variant_builder_add_dictentry_array_string(&b, "xesam:artist", station_name, NULL); g_variant_builder_add_dictentry_array_string(&b, "xesam:albumArtist", station_name, NULL); } goto end; } /* In practice, very often for radios, only the 'title' metadata is set. * So let's recycle the unused 'artist' field for the stations name. */ artist = gv_metadata_get_artist(metadata); if (artist) { g_variant_builder_add_dictentry_array_string(&b, "xesam:artist", artist, NULL); g_variant_builder_add_dictentry_array_string(&b, "xesam:albumArtist", artist, NULL); } else if (station_name) { g_variant_builder_add_dictentry_array_string(&b, "xesam:artist", station_name, NULL); g_variant_builder_add_dictentry_array_string(&b, "xesam:albumArtist", station_name, NULL); } title = gv_metadata_get_title(metadata); if (title) g_variant_builder_add_dictentry_string(&b, "xesam:title", title); album = gv_metadata_get_album(metadata); if (album) g_variant_builder_add_dictentry_string(&b, "xesam:album", album); genre = gv_metadata_get_genre(metadata); if (genre) g_variant_builder_add_dictentry_array_string(&b, "xesam:genre", genre, NULL); year = gv_metadata_get_year(metadata); if (year) g_variant_builder_add_dictentry_string(&b, "xesam:contentCreated", year); comment = gv_metadata_get_comment(metadata); if (comment) g_variant_builder_add_dictentry_array_string(&b, "xesam:comment", comment, NULL); end: return g_variant_builder_end(&b); } static GVariant * g_variant_new_playback_status(GvPlayer *player) { GvPlaybackState state; gchar *state_str; state = gv_player_get_playback_state(player); switch (state) { case GV_PLAYBACK_STATE_STOPPED: state_str = "Stopped"; break; default: state_str = "Playing"; break; } return g_variant_new_string(state_str); } static GVariant * g_variant_new_loop_status(GvPlayer *player) { gboolean repeat; repeat = gv_player_get_repeat(player); return g_variant_new_string(repeat ? "Playlist" : "None"); } static GVariant * g_variant_new_shuffle(GvPlayer *player) { gboolean shuffle; shuffle = gv_player_get_shuffle(player); return g_variant_new_boolean(shuffle); } static GVariant * g_variant_new_volume(GvPlayer *player) { guint volume; volume = gv_player_get_volume(player); return g_variant_new_double((gdouble) volume / 100.0); } static GVariant * g_variant_new_can_play(GvStationList *station_list) { guint n_stations; n_stations = gv_station_list_length(station_list); return g_variant_new_boolean(n_stations > 0 ? TRUE : FALSE); } static GVariant * g_variant_new_can_go_prev(GvPlayer *player) { gboolean has_prev; has_prev = gv_player_get_prev_station(player) ? TRUE : FALSE; return g_variant_new_boolean(has_prev); } static GVariant * g_variant_new_can_go_next(GvPlayer *player) { gboolean has_next; has_next = gv_player_get_next_station(player) ? TRUE : FALSE; return g_variant_new_boolean(has_next); } /* * Dbus method handlers */ static GVariant * method_raise(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { #ifdef GV_UI_ENABLED gv_ui_present_main(); #endif return NULL; } static GVariant * method_quit(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { gv_core_quit(); return NULL; } static GvDbusMethod root_methods[] = { // clang-format off { "Raise", method_raise }, { "Quit", method_quit }, { NULL, NULL } // clang-format on }; static GVariant * method_play(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gv_player_play(player); return NULL; } static GVariant * method_stop(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gv_player_stop(player); return NULL; } static GVariant * method_toggle(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gv_player_toggle(player); return NULL; } static GVariant * method_next(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; if (!gv_player_next(player)) gv_player_stop(player); return NULL; } static GVariant * method_prev(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; if (!gv_player_prev(player)) gv_player_stop(player); return NULL; } static GVariant * method_open_uri(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err) { GvPlayer *player = gv_core_player; GvStationList *station_list = gv_core_station_list; GvStation *station; const gchar *uri; g_variant_get(params, "(&s)", &uri); /* Ensure URI is valid */ if (!is_uri_scheme_supported(uri)) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "URI scheme not supported."); return NULL; } /* Check if the station is part of the station list */ station = gv_station_list_find_by_uri(station_list, uri); /* Create a new station if needed */ if (station == NULL) { station = gv_station_new(NULL, uri); gv_station_list_append(station_list, station); } /* Play station */ gv_player_set_station(player, station); gv_player_play(player); return NULL; } static GvDbusMethod player_methods[] = { // clang-format off { "Play", method_play }, { "Pause", method_stop }, { "PlayPause", method_toggle }, { "Stop", method_stop }, { "Next", method_next }, { "Previous", method_prev }, { "Seek", NULL }, { "SetPosition", NULL }, { "OpenUri", method_open_uri }, { NULL, NULL } // clang-format on }; static GVariant * method_get_tracks_metadata(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err G_GNUC_UNUSED) { GvStationList *station_list = gv_core_station_list; GVariantBuilder b; GVariantIter *iter; const gchar *track_id; g_variant_get(params, "(ao)", &iter); g_variant_builder_init(&b, G_VARIANT_TYPE("aa{sv}")); while (g_variant_iter_loop(iter, "&o", &track_id)) { GvStation *station; if (!parse_track_id(track_id, station_list, &station)) /* Ignore silently */ continue; g_variant_builder_add_value(&b, g_variant_new_metadata_map(station, NULL)); } return g_variant_builder_end(&b); } static GVariant * method_add_track(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err) { GvPlayer *player = gv_core_player; GvStationList *station_list = gv_core_station_list; const gchar *uri; const gchar *after_track_id; gboolean set_as_current; GvStation *station, *after_station; g_variant_get(params, "(&s&ob)", &uri, &after_track_id, &set_as_current); /* Ensure URI is valid */ if (!is_uri_scheme_supported(uri)) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid URI scheme for param 'Uri'."); return NULL; } /* Handle 'after_track' */ if (!parse_track_id(after_track_id, station_list, &after_station)) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid param 'AfterTrack'."); return NULL; } /* Add a new station to station list. If 'after_station' is NULL, the MPRIS2 * specification says that the track should be placed at the beginning of the * track list. */ station = gv_station_new(NULL, uri); if (after_station) gv_station_list_insert_after(station_list, station, after_station); else gv_station_list_prepend(station_list, station); /* Play new station if needed */ if (set_as_current) { gv_player_set_station(player, station); if (gv_player_get_playback_state(player) != GV_PLAYBACK_STATE_STOPPED) gv_player_play(player); } return NULL; } static GVariant * method_remove_track(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err) { GvStationList *station_list = gv_core_station_list; const gchar *track_id; GvStation *station; g_variant_get(params, "(&o)", &track_id); if (!parse_track_id(track_id, station_list, &station)) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid param 'TrackId'."); return NULL; } gv_station_list_remove(station_list, station); return NULL; } static GVariant * method_go_to(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err) { GvPlayer *player = gv_core_player; GvStationList *station_list = gv_core_station_list; const gchar *track_id; GvStation *station; // WISHED What about the last line in MPRIS2 specs ? What does that mean ? g_variant_get(params, "(&o)", &track_id); if (!parse_track_id(track_id, station_list, &station)) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid param 'TrackId'."); return NULL; } gv_player_set_station(player, station); if (gv_player_get_playback_state(player) != GV_PLAYBACK_STATE_STOPPED) gv_player_play(player); return NULL; } static GvDbusMethod tracklist_methods[] = { // clang-format off { "GetTracksMetadata", method_get_tracks_metadata }, { "AddTrack", method_add_track }, { "RemoveTrack", method_remove_track }, { "GoTo", method_go_to }, { NULL, NULL } // clang-format on }; static GVariant * method_activate_playlist(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err) { GvPlayer *player = gv_core_player; GvStationList *station_list = gv_core_station_list; const gchar *playlist_id; GvStation *station; g_variant_get(params, "(&o)", &playlist_id); if (!parse_playlist_id(playlist_id, station_list, &station)) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid param 'PlaylistId'."); return NULL; } if (station == NULL) return NULL; gv_player_set_station(player, station); gv_player_play(player); return NULL; } static GVariant * method_get_playlists(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err G_GNUC_UNUSED) { GvStationList *station_list = gv_core_station_list; GVariantBuilder b; GList *list, *item; guint32 start_index, max_count; const gchar *order; gboolean reverse_order; gboolean alphabetical; g_variant_get(params, "(uu&sb)", &start_index, &max_count, &order, &reverse_order); /* We only support 'Alphabetical' */ if (!g_strcmp0(order, "Alphabetical")) alphabetical = TRUE; else alphabetical = FALSE; /* Make a list */ list = build_station_list(station_list, alphabetical, reverse_order, start_index, max_count); /* Make a GVariant */ g_variant_builder_init(&b, G_VARIANT_TYPE("a(oss)")); for (item = list; item; item = item->next) { GvStation *station = item->data; g_variant_builder_add_value(&b, g_variant_new_playlist(station)); } /* Cleanup */ g_list_free(list); /* Return */ return g_variant_builder_end(&b); } static GvDbusMethod playlists_methods[] = { // clang-format off { "ActivatePlaylist", method_activate_playlist }, { "GetPlaylists", method_get_playlists }, { NULL, NULL } // clang-format on }; /* * Dbus property handlers */ static GVariant * prop_get_true(GvDbusServer *dbus_server G_GNUC_UNUSED) { /* Dummy accessor to return TRUE all the time */ return g_variant_new_boolean(TRUE); } static GVariant * prop_get_false(GvDbusServer *dbus_server G_GNUC_UNUSED) { /* Dummy accessor to return FALSE all the time */ return g_variant_new_boolean(FALSE); } static gboolean prop_set_error(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *value G_GNUC_UNUSED, GError **err) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Setting this property is not supported."); return FALSE; } static GVariant * prop_get_can_raise(GvDbusServer *dbus_server G_GNUC_UNUSED) { #ifdef GV_UI_ENABLED return g_variant_new_boolean(TRUE); #else return g_variant_new_boolean(FALSE); #endif } static GVariant * prop_get_identity(GvDbusServer *dbus_server G_GNUC_UNUSED) { return g_variant_new_string(g_get_application_name()); } static GVariant * prop_get_desktop_entry(GvDbusServer *dbus_server G_GNUC_UNUSED) { return g_variant_new_string(GV_APPLICATION_ID); } static GVariant * prop_get_supported_uri_schemes(GvDbusServer *dbus_server G_GNUC_UNUSED) { return g_variant_new_strv(SUPPORTED_URI_SCHEMES, -1); } static GVariant * prop_get_supported_mime_types(GvDbusServer *dbus_server G_GNUC_UNUSED) { return g_variant_new_strv(SUPPORTED_MIME_TYPES, -1); } static GvDbusProperty root_properties[] = { // clang-format off { "CanRaise", prop_get_can_raise, NULL }, { "CanQuit", prop_get_true, NULL }, { "Fullscreen", prop_get_false, prop_set_error }, { "CanSetFullscreen", prop_get_false, NULL }, { "HasTrackList", prop_get_true, NULL }, { "Identity", prop_get_identity, NULL }, { "DesktopEntry", prop_get_desktop_entry, NULL }, { "SupportedUriSchemes", prop_get_supported_uri_schemes, NULL }, { "SupportedMimeTypes", prop_get_supported_mime_types, NULL }, { NULL, NULL, NULL } // clang-format on }; static GVariant * prop_get_playback_status(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; return g_variant_new_playback_status(player); } static GVariant * prop_get_loop_status(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; return g_variant_new_loop_status(player); } static gboolean prop_set_loop_status(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *value, GError **err) { GvPlayer *player = gv_core_player; const gchar *loop_status; gboolean repeat; repeat = FALSE; loop_status = g_variant_get_string(value, NULL); if (!g_strcmp0(loop_status, "Playlist")) { repeat = TRUE; } else if (!g_strcmp0(loop_status, "Track")) { /* 'Track' makes no sense here */ repeat = FALSE; } else if (!g_strcmp0(loop_status, "None")) { repeat = FALSE; } else { /* Any other value should raise an error */ g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid value."); return FALSE; } gv_player_set_repeat(player, repeat); return TRUE; } static GVariant * prop_get_shuffle(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; return g_variant_new_shuffle(player); } static gboolean prop_set_shuffle(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *value, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gboolean shuffle; shuffle = g_variant_get_boolean(value); gv_player_set_shuffle(player, shuffle); return TRUE; } static GVariant * prop_get_volume(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; return g_variant_new_volume(player); } static gboolean prop_set_volume(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *value, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gdouble volume; volume = g_variant_get_double(value); volume = round(volume * 100); gv_player_set_volume(player, (guint) volume); return TRUE; } static GVariant * prop_get_rate(GvDbusServer *dbus_server G_GNUC_UNUSED) { return g_variant_new_double(1.0); } static GVariant * prop_get_metadata(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; GvStation *station; GvMetadata *metadata; station = gv_player_get_station(player); metadata = gv_player_get_metadata(player); return g_variant_new_metadata_map(station, metadata); } static GVariant * prop_get_can_play(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvStationList *station_list = gv_core_station_list; return g_variant_new_can_play(station_list); } static GVariant * prop_get_can_go_prev(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; return g_variant_new_can_go_prev(player); } static GVariant * prop_get_can_go_next(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; return g_variant_new_can_go_next(player); } static GvDbusProperty player_properties[] = { // clang-format off { "PlaybackStatus", prop_get_playback_status, NULL }, { "LoopStatus", prop_get_loop_status, prop_set_loop_status }, { "Shuffle", prop_get_shuffle, prop_set_shuffle }, { "Volume", prop_get_volume, prop_set_volume }, { "Rate", prop_get_rate, prop_set_error }, { "MinimumRate", prop_get_rate, NULL }, { "MaximumRate", prop_get_rate, NULL }, { "Metadata", prop_get_metadata, NULL }, { "CanPlay", prop_get_can_play, NULL }, { "CanPause", prop_get_can_play, NULL }, { "CanGoNext", prop_get_can_go_next, NULL }, { "CanGoPrevious", prop_get_can_go_prev, NULL }, { "CanSeek", prop_get_false, NULL }, { "CanControl", prop_get_true, NULL }, { NULL, NULL, NULL } // clang-format on }; static GVariant * prop_get_tracks(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvStationList *station_list = gv_core_station_list; GvStationListIter *iter; GvStation *station; GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("ao")); iter = gv_station_list_iter_new(station_list); while (gv_station_list_iter_loop(iter, &station)) { gchar *track_id; track_id = make_track_id(station); g_variant_builder_add(&b, "o", track_id); g_free(track_id); } gv_station_list_iter_free(iter); return g_variant_builder_end(&b); } static GvDbusProperty tracklist_properties[] = { // clang-format off { "Tracks", prop_get_tracks, NULL }, { "CanEditTracks", prop_get_true, NULL }, { NULL, NULL, NULL } // clang-format on }; static GVariant * prop_get_playlist_count(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvStationList *station_list = gv_core_station_list; guint n_stations = gv_station_list_length(station_list); return g_variant_new_uint32(n_stations); } static GVariant * prop_get_orderings(GvDbusServer *dbus_server G_GNUC_UNUSED) { static const gchar *supported_orderings[] = { "Alphabetical", "UserDefined", NULL }; return g_variant_new_strv(supported_orderings, -1); } static GVariant * prop_get_active_playlist(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; GvStation *station = gv_player_get_station(player); return g_variant_new_maybe_playlist(station); } static GvDbusProperty playlists_properties[] = { // clang-format off { "PlaylistCount", prop_get_playlist_count, NULL }, { "Orderings", prop_get_orderings, NULL }, { "ActivePlaylist", prop_get_active_playlist, NULL }, { NULL, NULL, NULL } // clang-format on }; /* * Dbus interfaces */ static GvDbusInterface dbus_interfaces[] = { // clang-format off { DBUS_IFACE_ROOT, root_methods, root_properties }, { DBUS_IFACE_PLAYER, player_methods, player_properties }, { DBUS_IFACE_TRACKLIST, tracklist_methods, tracklist_properties }, { DBUS_IFACE_PLAYLISTS, playlists_methods, playlists_properties }, { NULL, NULL, NULL } // clang-format on }; /* * Signal handlers & callbacks */ static void on_player_notify(GvPlayer *player, GParamSpec *pspec, GvDbusServerMpris2 *self) { GvDbusServer *dbus_server = GV_DBUS_SERVER(self); const gchar *property_name = g_param_spec_get_name(pspec); if (!g_strcmp0(property_name, "playback-state")) { gv_dbus_server_emit_signal_property_changed( dbus_server, DBUS_IFACE_PLAYER, "PlaybackStatus", g_variant_new_playback_status(player)); } else if (!g_strcmp0(property_name, "repeat")) { gv_dbus_server_emit_signal_property_changed( dbus_server, DBUS_IFACE_PLAYER, "LoopStatus", g_variant_new_loop_status(player)); } else if (!g_strcmp0(property_name, "shuffle")) { gv_dbus_server_emit_signal_property_changed( dbus_server, DBUS_IFACE_PLAYER, "Shuffle", g_variant_new_shuffle(player)); } else if (!g_strcmp0(property_name, "volume")) { gv_dbus_server_emit_signal_property_changed( dbus_server, DBUS_IFACE_PLAYER, "Volume", g_variant_new_volume(player)); } else if (!g_strcmp0(property_name, "station")) { GvStation *station = gv_player_get_station(player); GvMetadata *metadata = gv_player_get_metadata(player); gv_dbus_server_emit_signal_property_changed( dbus_server, DBUS_IFACE_PLAYER, "Metadata", g_variant_new_metadata_map(station, metadata)); /* This signal should be sent only if there was a change */ gv_dbus_server_emit_signal_property_changed( dbus_server, DBUS_IFACE_PLAYER, "CanGoPrevious", g_variant_new_can_go_prev(player)); /* This signal should be sent only if there was a change */ gv_dbus_server_emit_signal_property_changed( dbus_server, DBUS_IFACE_PLAYER, "CanGoNext", g_variant_new_can_go_next(player)); /* This signal should be send only if the station's name * or the station's icon was changed. */ gv_dbus_server_emit_signal_property_changed( dbus_server, DBUS_IFACE_PLAYLISTS, "PlaylistChanged", g_variant_new_playlist(station)); } else if (!g_strcmp0(property_name, "metadata")) { GvStation *station = gv_player_get_station(player); GvMetadata *metadata = gv_player_get_metadata(player); gv_dbus_server_emit_signal_property_changed( dbus_server, DBUS_IFACE_PLAYER, "Metadata", g_variant_new_metadata_map(station, metadata)); } } static void on_station_list_station_added(GvStationList *station_list, GvStation *station, GvDbusServerMpris2 *self) { GvDbusServer *dbus_server = GV_DBUS_SERVER(self); GVariantBuilder b; GvStation *after_station; gchar *after_track_id; after_station = gv_station_list_prev(station_list, station, FALSE, FALSE); after_track_id = make_track_id(after_station); g_variant_builder_init(&b, G_VARIANT_TYPE("(a{sv}o)")); g_variant_builder_add_value(&b, g_variant_new_metadata_map(station, NULL)); g_variant_builder_add(&b, "o", after_track_id); gv_dbus_server_emit_signal(dbus_server, DBUS_IFACE_TRACKLIST, "TrackAdded", g_variant_builder_end(&b)); g_free(after_track_id); } static void on_station_list_station_removed(GvStationList *station_list G_GNUC_UNUSED, GvStation *station, GvDbusServerMpris2 *self) { GvDbusServer *dbus_server = GV_DBUS_SERVER(self); GVariantBuilder b; gchar *track_id; track_id = make_track_id(station); g_variant_builder_init(&b, G_VARIANT_TYPE("(o)")); g_variant_builder_add(&b, "o", track_id); gv_dbus_server_emit_signal(dbus_server, DBUS_IFACE_TRACKLIST, "TrackRemoved", g_variant_builder_end(&b)); g_free(track_id); } static void on_station_list_station_modified(GvStationList *station_list G_GNUC_UNUSED, GvStation *station, GvDbusServerMpris2 *self) { GvDbusServer *dbus_server = GV_DBUS_SERVER(self); GVariantBuilder b; gchar *track_id; track_id = make_track_id(station); g_variant_builder_init(&b, G_VARIANT_TYPE("(oa{sv})")); g_variant_builder_add(&b, "o", track_id); g_variant_builder_add_value(&b, g_variant_new_metadata_map(station, NULL)); gv_dbus_server_emit_signal(dbus_server, DBUS_IFACE_TRACKLIST, "TrackMetadataChanged", g_variant_builder_end(&b)); g_free(track_id); } /* * GvFeature methods */ static void gv_dbus_server_mpris2_disable(GvFeature *feature) { GvPlayer *player = gv_core_player; GvStationList *station_list = gv_core_station_list; /* Signal handlers */ g_signal_handlers_disconnect_by_data(station_list, feature); g_signal_handlers_disconnect_by_data(player, feature); /* Chain up */ GV_FEATURE_CHAINUP_DISABLE(gv_dbus_server_mpris2, feature); } static void gv_dbus_server_mpris2_enable(GvFeature *feature) { GvPlayer *player = gv_core_player; GvStationList *station_list = gv_core_station_list; /* Chain up */ GV_FEATURE_CHAINUP_ENABLE(gv_dbus_server_mpris2, feature); /* Signal handlers */ g_signal_connect_object(player, "notify", G_CALLBACK(on_player_notify), feature, 0); g_signal_connect_object(station_list, "station-added", G_CALLBACK(on_station_list_station_added), feature, 0); g_signal_connect_object(station_list, "station-removed", G_CALLBACK(on_station_list_station_removed), feature, 0); g_signal_connect_object(station_list, "station-modified", G_CALLBACK(on_station_list_station_modified), feature, 0); } /* * Public methods */ GvFeature * gv_dbus_server_mpris2_new(void) { return gv_feature_new(GV_TYPE_DBUS_SERVER_MPRIS2, "DBusServerMpris2", GV_FEATURE_DEFAULT); } /* * GObject methods */ static void gv_dbus_server_mpris2_constructed(GObject *object) { GvDbusServer *dbus_server = GV_DBUS_SERVER(object); TRACE("%p", object); /* Set dbus server properties */ gv_dbus_server_set_dbus_name(dbus_server, DBUS_NAME); gv_dbus_server_set_dbus_path(dbus_server, DBUS_PATH); gv_dbus_server_set_dbus_introspection(dbus_server, DBUS_INTROSPECTION); gv_dbus_server_set_dbus_interface_table(dbus_server, dbus_interfaces); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_dbus_server_mpris2, object); } static void gv_dbus_server_mpris2_init(GvDbusServerMpris2 *self) { TRACE("%p", self); } static void gv_dbus_server_mpris2_class_init(GvDbusServerMpris2Class *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); GvFeatureClass *feature_class = GV_FEATURE_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->constructed = gv_dbus_server_mpris2_constructed; /* Override GvFeature methods */ feature_class->enable = gv_dbus_server_mpris2_enable; feature_class->disable = gv_dbus_server_mpris2_disable; } goodvibes-v0.7.2/src/feat/gv-dbus-server-mpris2.h000066400000000000000000000021251414415210700216320ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "base/gv-feature.h" #include "feat/gv-dbus-server.h" /* GObject declarations */ #define GV_TYPE_DBUS_SERVER_MPRIS2 gv_dbus_server_mpris2_get_type() G_DECLARE_FINAL_TYPE(GvDbusServerMpris2, gv_dbus_server_mpris2, \ GV, DBUS_SERVER_MPRIS2, GvDbusServer) /* Public methods */ GvFeature *gv_dbus_server_mpris2_new(void); goodvibes-v0.7.2/src/feat/gv-dbus-server-native.c000066400000000000000000000400001414415210700216710ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-additions.h" #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "feat/gv-dbus-server-native.h" #include "feat/gv-dbus-server.h" #define DBUS_PATH GV_APPLICATION_PATH #define DBUS_IFACE_ROOT GV_APPLICATION_ID #define DBUS_IFACE_PLAYER DBUS_IFACE_ROOT ".Player" #define DBUS_IFACE_STATIONS DBUS_IFACE_ROOT ".Stations" static const gchar *DBUS_INTROSPECTION = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* * GObject definitions */ struct _GvDbusServerNative { /* Parent instance structure */ GvDbusServer parent_instance; }; G_DEFINE_TYPE(GvDbusServerNative, gv_dbus_server_native, GV_TYPE_DBUS_SERVER) /* * Helpers */ static GVariant * g_variant_new_station(GvStation *station, GvMetadata *metadata) { GVariantBuilder b; const gchar *uri; const gchar *name; const gchar *artist; const gchar *title; const gchar *album; const gchar *genre; const gchar *year; const gchar *comment; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); /* Station properties */ if (station == NULL) goto end; uri = gv_station_get_uri(station); if (uri) g_variant_builder_add_dictentry_string(&b, "uri", uri); name = gv_station_get_name(station); if (name) g_variant_builder_add_dictentry_string(&b, "name", name); /* Metadata if any */ if (metadata == NULL) goto end; artist = gv_metadata_get_artist(metadata); if (artist) g_variant_builder_add_dictentry_string(&b, "artist", artist); title = gv_metadata_get_title(metadata); if (title) g_variant_builder_add_dictentry_string(&b, "title", title); album = gv_metadata_get_album(metadata); if (album) g_variant_builder_add_dictentry_string(&b, "album", album); genre = gv_metadata_get_genre(metadata); if (genre) g_variant_builder_add_dictentry_string(&b, "genre", genre); year = gv_metadata_get_year(metadata); if (year) g_variant_builder_add_dictentry_string(&b, "year", year); comment = gv_metadata_get_comment(metadata); if (comment) g_variant_builder_add_dictentry_string(&b, "comment", comment); end: return g_variant_builder_end(&b); } /* * Dbus method handlers */ static GVariant * method_quit(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { gv_core_quit(); return NULL; } static GvDbusMethod root_methods[] = { // clang-format off { "Quit", method_quit }, { NULL, NULL } // clang-format on }; static GVariant * method_play(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err) { GvPlayer *player = gv_core_player; gchar *string; g_variant_get(params, "(&s)", &string); /* Empty string: play current station */ if (!g_strcmp0(string, "")) { gv_player_play(player); return NULL; } /* Otherwise, string may be a station URI, or a station name. * It may be part of station list, or may be a new URI. */ if (gv_player_set_station_by_guessing(player, string)) { gv_player_play(player); return NULL; } if (is_uri_scheme_supported(string)) { GvStation *station; station = gv_station_new(NULL, string); gv_player_set_station(player, station); gv_player_play(player); return NULL; } g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "'%s' is neither a known station or a valid uri", string); return NULL; } static GVariant * method_stop(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gv_player_stop(player); return NULL; } static GVariant * method_play_stop(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gv_player_toggle(player); return NULL; } static GVariant * method_next(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gv_player_next(player); return NULL; } static GVariant * method_prev(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gv_player_prev(player); return NULL; } static GvDbusMethod player_methods[] = { // clang-format off { "Play", method_play }, { "Stop", method_stop }, { "PlayStop", method_play_stop }, { "Next", method_next }, { "Previous", method_prev }, { NULL, NULL } // clang-format on }; static GVariant * method_list(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GvStationList *station_list = gv_core_station_list; GvStationListIter *iter; GvStation *station; GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("aa{sv}")); iter = gv_station_list_iter_new(station_list); while (gv_station_list_iter_loop(iter, &station)) g_variant_builder_add_value(&b, g_variant_new_station(station, NULL)); gv_station_list_iter_free(iter); return g_variant_builder_end(&b); } static GVariant * method_add(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err) { GvStationList *station_list = gv_core_station_list; GvStation *new_station; GvStation *around_station; gchar *uri; gchar *name; gchar *where; gchar *around; g_variant_get(params, "(&s&s&s&s)", &uri, &name, &where, &around); /* Handle new station */ if (!is_uri_scheme_supported(uri)) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "URI scheme not supported"); return NULL; } new_station = gv_station_new(name, uri); /* Handle where to add */ around_station = gv_station_list_find_by_guessing(station_list, around); if (!g_strcmp0(where, "first")) gv_station_list_prepend(station_list, new_station); else if (!g_strcmp0(where, "last") || !g_strcmp0(where, "")) gv_station_list_append(station_list, new_station); else if (!g_strcmp0(where, "before")) gv_station_list_insert_before(station_list, new_station, around_station); else if (!g_strcmp0(where, "after")) gv_station_list_insert_after(station_list, new_station, around_station); else { g_object_unref(new_station); g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid keyword '%s'", where); } return NULL; } static GVariant * method_remove(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err) { GvStationList *station_list = gv_core_station_list; GvStation *match; gchar *station; g_variant_get(params, "(&s)", &station); match = gv_station_list_find_by_guessing(station_list, station); if (match) gv_station_list_remove(station_list, match); else g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Station '%s' not found", station); return NULL; } static GVariant * method_rename(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err) { GvStationList *station_list = gv_core_station_list; GvStation *match; gchar *station; gchar *name; g_variant_get(params, "(&s&s)", &station, &name); match = gv_station_list_find_by_guessing(station_list, station); if (match) gv_station_set_name(match, name); else g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Station '%s' not found", station); return NULL; } static GVariant * method_move(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *params, GError **err) { GvStationList *station_list = gv_core_station_list; GvStation *moving_station; GvStation *around_station; gchar *moving; gchar *around; gchar *where; g_variant_get(params, "(&s&s&s)", &moving, &where, &around); /* Handle station to move */ moving_station = gv_station_list_find_by_guessing(station_list, moving); if (moving_station == NULL) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Station '%s' not found", moving); return NULL; } /* Move the station */ around_station = gv_station_list_find_by_guessing(station_list, around); if (!g_strcmp0(where, "first")) gv_station_list_move_first(station_list, moving_station); else if (!g_strcmp0(where, "last")) gv_station_list_move_last(station_list, moving_station); else if (!g_strcmp0(where, "before")) gv_station_list_move_before(station_list, moving_station, around_station); else if (!g_strcmp0(where, "after")) gv_station_list_move_after(station_list, moving_station, around_station); else g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid keyword '%s'", where); return NULL; } static GvDbusMethod stations_methods[] = { // clang-format off { "List", method_list }, { "Add", method_add }, { "Remove", method_remove }, { "Rename", method_rename }, { "Move", method_move }, { NULL, NULL } // clang-format on }; /* * Dbus property handlers */ static GVariant * prop_get_version(GvDbusServer *dbus_server G_GNUC_UNUSED) { return g_variant_new_string(PACKAGE_VERSION); } static GvDbusProperty root_properties[] = { // clang-format off { "Version", prop_get_version, NULL }, { NULL, NULL, NULL } // clang-format on }; static GVariant * prop_get_current(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; GvStation *station; GvMetadata *metadata; station = gv_player_get_station(player); metadata = gv_player_get_metadata(player); return g_variant_new_station(station, metadata); } static GVariant * prop_get_playing(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; GvPlaybackState playback_state; gboolean is_playing; playback_state = gv_player_get_playback_state(player); is_playing = playback_state == GV_PLAYBACK_STATE_PLAYING ? TRUE : FALSE; return g_variant_new_boolean(is_playing); } static GVariant * prop_get_repeat(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gboolean repeat; repeat = gv_player_get_repeat(player); return g_variant_new_boolean(repeat); } static gboolean prop_set_repeat(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *value, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gboolean repeat; repeat = g_variant_get_boolean(value); gv_player_set_repeat(player, repeat); return TRUE; } static GVariant * prop_get_shuffle(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gboolean shuffle; shuffle = gv_player_get_shuffle(player); return g_variant_new_boolean(shuffle); } static gboolean prop_set_shuffle(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *value, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gboolean shuffle; shuffle = g_variant_get_boolean(value); gv_player_set_shuffle(player, shuffle); return TRUE; } static GVariant * prop_get_volume(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; guint volume; volume = gv_player_get_volume(player); return g_variant_new_uint32(volume); } static gboolean prop_set_volume(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *value, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; guint volume; volume = g_variant_get_uint32(value); gv_player_set_volume(player, volume); return TRUE; } static GVariant * prop_get_mute(GvDbusServer *dbus_server G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gboolean mute; mute = gv_player_get_mute(player); return g_variant_new_boolean(mute); } static gboolean prop_set_mute(GvDbusServer *dbus_server G_GNUC_UNUSED, GVariant *value, GError **err G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; gboolean mute; mute = g_variant_get_boolean(value); gv_player_set_mute(player, mute); return TRUE; } static GvDbusProperty player_properties[] = { // clang-format off { "Current", prop_get_current, NULL }, { "Playing", prop_get_playing, NULL }, { "Repeat", prop_get_repeat, prop_set_repeat }, { "Shuffle", prop_get_shuffle, prop_set_shuffle }, { "Volume", prop_get_volume, prop_set_volume }, { "Mute", prop_get_mute, prop_set_mute }, { NULL, NULL, NULL } // clang-format on }; /* * Dbus interfaces */ static GvDbusInterface dbus_interfaces[] = { // clang-format off { DBUS_IFACE_ROOT, root_methods, root_properties }, { DBUS_IFACE_PLAYER, player_methods, player_properties }, { DBUS_IFACE_STATIONS, stations_methods, NULL }, { NULL, NULL, NULL } // clang-format on }; /* * Public methods */ GvFeature * gv_dbus_server_native_new(void) { return gv_feature_new(GV_TYPE_DBUS_SERVER_NATIVE, "DBusServerNative", GV_FEATURE_DEFAULT); } /* * GObject methods */ static void gv_dbus_server_native_constructed(GObject *object) { GvDbusServer *dbus_server = GV_DBUS_SERVER(object); /* Set dbus server properties - we don't set a name as we don't want to acquire any */ gv_dbus_server_set_dbus_path(dbus_server, DBUS_PATH); gv_dbus_server_set_dbus_introspection(dbus_server, DBUS_INTROSPECTION); gv_dbus_server_set_dbus_interface_table(dbus_server, dbus_interfaces); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_dbus_server_native, object); } static void gv_dbus_server_native_init(GvDbusServerNative *self) { TRACE("%p", self); } static void gv_dbus_server_native_class_init(GvDbusServerNativeClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->constructed = gv_dbus_server_native_constructed; } goodvibes-v0.7.2/src/feat/gv-dbus-server-native.h000066400000000000000000000021171414415210700217050ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "base/gv-feature.h" #include "feat/gv-dbus-server.h" /* GObject declarations */ #define GV_TYPE_DBUS_SERVER_NATIVE gv_dbus_server_native_get_type() G_DECLARE_FINAL_TYPE(GvDbusServerNative, gv_dbus_server_native, \ GV, DBUS_SERVER_NATIVE, GvDbusServer) /* Public methods */ GvFeature *gv_dbus_server_native_new(void); goodvibes-v0.7.2/src/feat/gv-dbus-server.c000066400000000000000000000476331414415210700204300ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "feat/gv-dbus-server.h" #define MAX_INTERFACES 4 #undef DEBUG_INTERFACES /* * Properties */ enum { /* Reserved */ PROP_0, /* Properties - refer to class_init() for more details */ PROP_DBUS_NAME, PROP_DBUS_PATH, PROP_DBUS_INTROSPECTION, PROP_DBUS_INTERFACE_TABLE, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * GObject definitions */ struct _GvDbusServerPrivate { /* Properties */ const gchar *name; const gchar *path; const gchar *introspection; GvDbusInterface *interface_table; /* Dbus stuff */ GDBusNodeInfo *introspection_data; guint bus_owner_id; GDBusConnection *bus_connection; guint registration_ids[MAX_INTERFACES + 1]; }; typedef struct _GvDbusServerPrivate GvDbusServerPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(GvDbusServer, gv_dbus_server, GV_TYPE_FEATURE) /* * Debug helpers */ #ifdef DEBUG_INTERFACES static void debug_interfaces(GvDbusServer *self) { GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); GDBusNodeInfo *info = priv->introspection_data; const GvDbusInterface *iface; const GvDbusMethod *method; const GvDbusProperty *prop; GDBusInterfaceInfo **g_ifaces; GDBusInterfaceInfo *g_iface; GDBusMethodInfo **g_methods; GDBusMethodInfo *g_method; GDBusPropertyInfo **g_props; GDBusPropertyInfo *g_prop; /* Find missing things in introspection. * We iterate on methods and properties provided by the implementation, * and ensure they exist in introspection. */ DEBUG("Debugging introspection data..."); for (iface = priv->interface_table; iface->name; iface++) { g_iface = NULL; /* Look for matching interface in introspection data */ g_ifaces = info->interfaces; while (g_ifaces && (g_iface = *g_ifaces++)) { if (!g_strcmp0(iface->name, g_iface->name)) break; } if (g_iface == NULL) ERROR("Implemented interface '%s': no match", iface->name); /* Look for matching methods in interface */ for (method = iface->methods; method && method->name; method++) { g_methods = g_iface->methods; while (g_methods && (g_method = *g_methods++)) { if (!g_strcmp0(method->name, g_method->name)) break; } if (g_method == NULL) ERROR("Implemented interface '%s': method '%s': no match", iface->name, method->name); } /* Look for matching properties in interface */ for (prop = iface->properties; prop && prop->name; prop++) { g_prop = NULL; g_props = g_iface->properties; while (g_props && (g_prop = *g_props++)) { if (!g_strcmp0(prop->name, g_prop->name)) break; } if (g_prop == NULL) ERROR("Implemented interface '%s': property '%s': no match", iface->name, prop->name); /* Check that 'get' and 'set' match in the introspection */ if (prop->get && !(g_prop->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)) ERROR("Implemented interface '%s': property '%s': get: no match", iface->name, prop->name); if (prop->set && !(g_prop->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)) ERROR("Implemented interface '%s': property '%s': set: no match", iface->name, prop->name); } } /* Find missing things in implementation. * We iterate on methods and properties provided by the introspection, * and ensure they are implemented. */ DEBUG("Debugging implementation..."); g_ifaces = info->interfaces; while (g_ifaces && (g_iface = *g_ifaces++)) { /* Look for matching interface in implementation */ for (iface = priv->interface_table; iface->name; iface++) { if (!g_strcmp0(g_iface->name, iface->name)) break; } if (iface->name == NULL) ERROR("Instrospection interface '%s': no match", g_iface->name); /* Look for matching methods in implementation */ g_methods = g_iface->methods; while (g_methods && (g_method = *g_methods++)) { for (method = iface->methods; method && method->name; method++) { if (!g_strcmp0(g_method->name, method->name)) break; } if (method == NULL || method->name == NULL) ERROR("Introspection interface '%s': method '%s': no match", g_iface->name, g_method->name); } /* Look for matching properties in implementation */ g_props = g_iface->properties; while (g_props && (g_prop = *g_props++)) { for (prop = iface->properties; prop && prop->name; prop++) { if (!g_strcmp0(g_prop->name, prop->name)) break; } if (prop == NULL || prop->name == NULL) ERROR("Introspection interface '%s': property '%s': no match", g_iface->name, g_prop->name); /* Check that 'get' and 'set' match in the implementation */ if ((g_prop->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) && !prop->get) ERROR("Introspection interface '%s': property '%s': " "get is not implemented", g_iface->name, g_prop->name); if ((g_prop->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE) && !prop->set) ERROR("Introspection interface '%s': property '%s': " "set is not implemented", g_iface->name, g_prop->name); } } } #endif /* * GDBus helpers */ static void handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GvDbusServer *self = GV_DBUS_SERVER(user_data); GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); const GvDbusInterface *iface; const GvDbusMethod *method; GVariant *ret = NULL; GError *err = NULL; const gchar *bus_name = connection ? g_dbus_connection_get_unique_name(connection) : "(null)"; TRACE("%s, %s, %s, %s, %s, ...", bus_name, sender, object_path, interface_name, method_name); /* Iterate over interfaces */ for (iface = priv->interface_table; iface && iface->name; iface++) { if (g_strcmp0(iface->name, interface_name)) continue; /* Iterate over methods */ for (method = iface->methods; method && method->name; method++) { if (g_strcmp0(method->name, method_name)) continue; if (method->call) ret = method->call(self, parameters, &err); else g_set_error(&err, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Method is not implemented."); break; } /* Check if method was found */ if (method == NULL || method->name == NULL) g_set_error(&err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method not found."); break; } /* Check if interface was found */ if (iface == NULL || iface->name == NULL) g_set_error(&err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_INTERFACE, "Interface not found."); /* Return with error if any */ if (err) { g_dbus_method_invocation_return_gerror(invocation, err); g_error_free(err); return; } /* Return value if any */ if (ret == NULL) g_dbus_method_invocation_return_value(invocation, NULL); else g_dbus_method_invocation_return_value(invocation, g_variant_new_tuple(&ret, 1)); } static GVariant * handle_get_property(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **err, gpointer user_data) { GvDbusServer *self = GV_DBUS_SERVER(user_data); GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); const GvDbusInterface *iface; const GvDbusProperty *prop; const gchar *bus_name = connection ? g_dbus_connection_get_unique_name(connection) : "(null)"; TRACE("%s, %s, %s, %s, %s, ...", bus_name, sender, object_path, interface_name, property_name); /* Iterate over interfaces */ for (iface = priv->interface_table; iface && iface->name; iface++) { if (g_strcmp0(iface->name, interface_name)) continue; /* Iterate over properties */ for (prop = iface->properties; prop && prop->name; prop++) { if (g_strcmp0(prop->name, property_name)) continue; if (prop->get == NULL) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Property reader is not implemented."); return NULL; } return prop->get(self); } /* Property not found */ g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Property not found."); return NULL; } /* Interface not found */ g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_INTERFACE, "Interface not found."); return NULL; } static gboolean handle_set_property(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **err, gpointer user_data) { GvDbusServer *self = GV_DBUS_SERVER(user_data); GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); const GvDbusInterface *iface = NULL; const GvDbusProperty *prop = NULL; const gchar *bus_name = connection ? g_dbus_connection_get_unique_name(connection) : "(null)"; TRACE("%s, %s, %s, %s, %s, ...", bus_name, sender, object_path, interface_name, property_name); /* Iterate over interfaces */ for (iface = priv->interface_table; iface && iface->name; iface++) { if (g_strcmp0(iface->name, interface_name)) continue; /* Iterate over properties */ for (prop = iface->properties; prop && prop->name; prop++) { if (g_strcmp0(prop->name, property_name)) continue; if (prop->set == NULL) { g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Property writer is not implemented."); return FALSE; } return prop->set(self, value, err); } /* Property not found */ g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Property not found."); return FALSE; } /* Interface not found */ g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_INTERFACE, "Interface not found."); return FALSE; } static const GDBusInterfaceVTable interface_vtable = { .method_call = handle_method_call, .get_property = handle_get_property, .set_property = handle_set_property, }; /* * Private methods */ static void gv_dbus_server_register_objects(GvDbusServer *self) { GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); GDBusInterfaceInfo **interfaces = priv->introspection_data->interfaces; GDBusInterfaceInfo *interface; guint i; // TODO: checks i = 0; while (interfaces && (interface = *interfaces++)) { guint id; g_assert(i < MAX_INTERFACES); id = g_dbus_connection_register_object(priv->bus_connection, priv->path, interface, &interface_vtable, self, NULL, NULL); g_assert(id > 0); priv->registration_ids[i++] = id; INFO("Interface '%s' registered", interface->name); } } static void gv_dbus_server_unregister_objects(GvDbusServer *self) { GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); guint i; for (i = 0; priv->registration_ids[i] > 0; i++) { g_dbus_connection_unregister_object(priv->bus_connection, priv->registration_ids[i]); priv->registration_ids[i] = 0; } } /* * GDBus signal handlers */ static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data G_GNUC_UNUSED) { const gchar *bus_name = connection ? g_dbus_connection_get_unique_name(connection) : "(null)"; TRACE("%s, %s, ...", bus_name, name); } static void on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data G_GNUC_UNUSED) { const gchar *bus_name = connection ? g_dbus_connection_get_unique_name(connection) : "(null)"; TRACE("%s, %s, ...", bus_name, name); if (connection == NULL) WARNING("Name '%s' can't connect to the bus", name); else WARNING("Name '%s' lost on the bus", name); } /* * Property accessors */ void gv_dbus_server_set_dbus_name(GvDbusServer *self, const gchar *value) { GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); g_assert_null(priv->name); priv->name = value; } void gv_dbus_server_set_dbus_path(GvDbusServer *self, const gchar *value) { GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); g_assert_null(priv->path); priv->path = value; } void gv_dbus_server_set_dbus_introspection(GvDbusServer *self, const gchar *value) { GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); g_assert_null(priv->introspection); priv->introspection = value; } void gv_dbus_server_set_dbus_interface_table(GvDbusServer *self, GvDbusInterface *value) { GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); g_assert_null(priv->interface_table); priv->interface_table = value; } static void gv_dbus_server_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TRACE_GET_PROPERTY(object, property_id, value, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } static void gv_dbus_server_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvDbusServer *self = GV_DBUS_SERVER(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_DBUS_NAME: gv_dbus_server_set_dbus_name(self, g_value_get_string(value)); break; case PROP_DBUS_PATH: gv_dbus_server_set_dbus_path(self, g_value_get_string(value)); break; case PROP_DBUS_INTROSPECTION: gv_dbus_server_set_dbus_introspection(self, g_value_get_string(value)); break; case PROP_DBUS_INTERFACE_TABLE: gv_dbus_server_set_dbus_interface_table(self, g_value_get_pointer(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public methods */ void gv_dbus_server_emit_signal(GvDbusServer *self, const gchar *interface_name, const gchar *signal_name, GVariant *parameters) { GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); GError *err = NULL; /* We're not sure to have a connection to dbus. Connection might fail * (for example, if the name is already owned). Or, early at startup, * we might still be waiting for the connection to finish when we're * asked to send the first signals. In any case, we must check that the * connection exists before using it. */ if (priv->bus_connection == NULL) return; g_dbus_connection_emit_signal(priv->bus_connection, NULL, priv->path, interface_name, signal_name, parameters, &err); if (err) { WARNING("Failed to emit dbus signal: %s", err->message); g_error_free(err); } } void gv_dbus_server_emit_signal_property_changed(GvDbusServer *self, const gchar *interface_name, const gchar *property_name, GVariant *value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); g_variant_builder_add(&b, "{sv}", property_name, value); GVariant *tuples[] = { g_variant_new_string(interface_name), g_variant_builder_end(&b), g_variant_new_strv(NULL, 0) }; gv_dbus_server_emit_signal(self, "org.freedesktop.DBus.Properties", "PropertiesChanged", g_variant_new_tuple(tuples, 3)); } GvDbusServer * gv_dbus_server_new(void) { return g_object_new(GV_TYPE_DBUS_SERVER, NULL); } /* * Feature methods */ static void gv_dbus_server_disable(GvFeature *feature) { GvDbusServer *self = GV_DBUS_SERVER(feature); GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); /* Unref DBus connection & objects registered */ if (priv->bus_connection != NULL) { gv_dbus_server_unregister_objects(self); g_object_unref(priv->bus_connection); priv->bus_connection = NULL; } /* Unown name */ if (priv->bus_owner_id != 0) { g_bus_unown_name(priv->bus_owner_id); priv->bus_owner_id = 0; } /* Chain up */ GV_FEATURE_CHAINUP_DISABLE(gv_dbus_server, feature); } static void gv_dbus_server_enable(GvFeature *feature) { GvDbusServer *self = GV_DBUS_SERVER(feature); GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); GDBusConnection *connection; /* Chain up */ GV_FEATURE_CHAINUP_ENABLE(gv_dbus_server, feature); /* Get dbus connection */ connection = g_application_get_dbus_connection(gv_core_application); g_assert_nonnull(connection); /* Add a reference */ priv->bus_connection = g_object_ref(connection); /* Register objects */ gv_dbus_server_register_objects(self); /* We might want to acquire a name or not */ if (priv->name) { /* Acquire a name on the bus (objects will be registered in the callback) */ priv->bus_owner_id = g_bus_own_name_on_connection( connection, priv->name, G_BUS_NAME_OWNER_FLAGS_NONE, on_name_acquired, on_name_lost, self, NULL); g_assert(priv->bus_owner_id > 0); } } /* * GObject methods */ static void gv_dbus_server_finalize(GObject *object) { GvDbusServer *self = GV_DBUS_SERVER(object); GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); TRACE("%p", object); /* Unref introspection data */ if (priv->introspection_data != NULL) g_dbus_node_info_unref(priv->introspection_data); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_dbus_server, object); } static void gv_dbus_server_constructed(GObject *object) { GvDbusServer *self = GV_DBUS_SERVER(object); GvDbusServerPrivate *priv = gv_dbus_server_get_instance_private(self); TRACE("%p", object); /* The 'name' property might be left to NULL if we don't want to * own a bus name. Other properties, though, must have been set. */ g_assert_nonnull(priv->path); g_assert_nonnull(priv->introspection); g_assert_nonnull(priv->interface_table); /* Parse XML introspection data */ priv->introspection_data = g_dbus_node_info_new_for_xml(priv->introspection, NULL); g_assert_nonnull(priv->introspection_data); #ifdef DEBUG_INTERFACES /* Ensure that the interface table matches the introspection data. * Be sure to enable this test if you work on this part. */ debug_interfaces(self); #endif /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_dbus_server, object); } static void gv_dbus_server_init(GvDbusServer *self) { TRACE("%p", self); } static void gv_dbus_server_class_init(GvDbusServerClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); GvFeatureClass *feature_class = GV_FEATURE_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_dbus_server_finalize; object_class->constructed = gv_dbus_server_constructed; /* Override GvFeature methods */ feature_class->enable = gv_dbus_server_enable; feature_class->disable = gv_dbus_server_disable; /* Properties * All the strings are supposed to be static, we won't duplicate them. * This makes a little optimization in memory usage. * See the set() accessors if don't get it. */ object_class->get_property = gv_dbus_server_get_property; object_class->set_property = gv_dbus_server_set_property; properties[PROP_DBUS_NAME] = g_param_spec_string("dbus-name", "Dbus well-known name", NULL, NULL, GV_PARAM_WRITABLE); properties[PROP_DBUS_PATH] = g_param_spec_string("dbus-path", "Dbus path", NULL, NULL, GV_PARAM_WRITABLE); properties[PROP_DBUS_INTROSPECTION] = g_param_spec_string("dbus-introspection", "Dbus instrospection", NULL, NULL, GV_PARAM_WRITABLE); properties[PROP_DBUS_INTERFACE_TABLE] = g_param_spec_pointer("dbus-interface-table", "Dbus interface table", NULL, GV_PARAM_WRITABLE); g_object_class_install_properties(object_class, PROP_N, properties); } goodvibes-v0.7.2/src/feat/gv-dbus-server.h000066400000000000000000000053061414415210700204240ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "base/gv-feature.h" /* GObject declarations */ #define GV_TYPE_DBUS_SERVER gv_dbus_server_get_type() G_DECLARE_DERIVABLE_TYPE(GvDbusServer, gv_dbus_server, GV, DBUS_SERVER, GvFeature) /* Data types */ struct _GvDbusServerClass { /* Parent class */ GvFeatureClass parent_class; }; typedef GVariant *(*GvDbusMethodCall) (GvDbusServer *, GVariant *, GError **); typedef GVariant *(*GvDbusPropertyGet) (GvDbusServer *); typedef gboolean (*GvDbusPropertySet) (GvDbusServer *, GVariant *, GError **); struct _GvDbusMethod { const gchar *name; const GvDbusMethodCall call; }; typedef struct _GvDbusMethod GvDbusMethod; struct _GvDbusProperty { const gchar *name; const GvDbusPropertyGet get; const GvDbusPropertySet set; }; typedef struct _GvDbusProperty GvDbusProperty; struct _GvDbusInterface { const gchar *name; const GvDbusMethod *methods; const GvDbusProperty *properties; }; typedef struct _GvDbusInterface GvDbusInterface; /* Methods */ GvDbusServer *gv_dbus_server_new(void); void gv_dbus_server_emit_signal(GvDbusServer *self, const gchar *interface_name, const gchar *signal_name, GVariant *parameters); void gv_dbus_server_emit_signal_property_changed(GvDbusServer *self, const gchar *interface_name, const gchar *property_name, GVariant *value); /* Property accessors */ void gv_dbus_server_set_dbus_name (GvDbusServer *self, const gchar *name); void gv_dbus_server_set_dbus_path (GvDbusServer *self, const gchar *path); void gv_dbus_server_set_dbus_introspection (GvDbusServer *self, const gchar *introspection); void gv_dbus_server_set_dbus_interface_table(GvDbusServer *self, GvDbusInterface *interface_table); goodvibes-v0.7.2/src/feat/gv-feat.c000066400000000000000000000071271414415210700171000ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "base/gv-base.h" #ifdef GV_FEAT_CONSOLE_OUTPUT #include "feat/gv-console-output.h" #endif #ifdef GV_FEAT_DBUS_SERVER #include "feat/gv-dbus-server-mpris2.h" #include "feat/gv-dbus-server-native.h" #endif #ifdef GV_FEAT_HOTKEYS #include "feat/gv-hotkeys.h" #endif #ifdef GV_FEAT_INHIBITOR #include "feat/gv-inhibitor.h" #endif #ifdef GV_FEAT_NOTIFICATIONS #include "feat/gv-notifications.h" #endif static GList *feat_objects; /* * Public functions */ GvFeature * gv_feat_find(const gchar *name_to_find) { GList *item; for (item = feat_objects; item; item = item->next) { GvFeature *feature = GV_FEATURE(item->data); const gchar *feature_name = gv_feature_get_name(feature); if (!g_strcmp0(feature_name, name_to_find)) return feature; } return NULL; } void gv_feat_configure_early(void) { GList *item; for (item = feat_objects; item; item = item->next) { GvFeature *feature = GV_FEATURE(item->data); GvFeatureFlags flags = gv_feature_get_flags(feature); if (flags & GV_FEATURE_EARLY) gv_configurable_configure(GV_CONFIGURABLE(feature)); } } void gv_feat_configure_late(void) { GList *item; for (item = feat_objects; item; item = item->next) { GvFeature *feature = GV_FEATURE(item->data); GvFeatureFlags flags = gv_feature_get_flags(feature); if (flags & GV_FEATURE_EARLY) continue; gv_configurable_configure(GV_CONFIGURABLE(feature)); } } void gv_feat_cleanup(void) { feat_objects = g_list_reverse(feat_objects); g_list_free_full(feat_objects, (GDestroyNotify) g_object_unref); } void gv_feat_init(void) { GvFeature *feature; GList *item; /* * Create every feature enabled at compile-time. * * Notice that some features don't really make sense if the program * has been compiled without ui. However we don't bother about that * here: all features are equals! * The distinction between 'core' features and 'ui' features is done * in the build system, see the `configure.ac` for more details. */ #ifdef GV_FEAT_CONSOLE_OUTPUT feature = gv_console_output_new(); feat_objects = g_list_append(feat_objects, feature); #endif #ifdef GV_FEAT_DBUS_SERVER feature = gv_dbus_server_native_new(); feat_objects = g_list_append(feat_objects, feature); feature = gv_dbus_server_mpris2_new(); feat_objects = g_list_append(feat_objects, feature); #endif #ifdef GV_FEAT_INHIBITOR feature = gv_inhibitor_new(); feat_objects = g_list_append(feat_objects, feature); #endif #ifdef GV_FEAT_HOTKEYS feature = gv_hotkeys_new(); feat_objects = g_list_append(feat_objects, feature); #endif #ifdef GV_FEAT_NOTIFICATIONS feature = gv_notifications_new(); feat_objects = g_list_append(feat_objects, feature); #endif /* Register objects in the base */ for (item = feat_objects; item; item = item->next) { feature = GV_FEATURE(item->data); gv_base_register_object(feature); /* Drop a line */ INFO("Feature compiled in: '%s'", gv_feature_get_name(feature)); } } goodvibes-v0.7.2/src/feat/gv-feat.h000066400000000000000000000020221414415210700170720ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ /* * This header contains definitions to be used by feat users */ #pragma once #include "base/gv-feature.h" /* Functions */ void gv_feat_init (void); void gv_feat_cleanup (void); void gv_feat_configure_early(void); void gv_feat_configure_late (void); GvFeature *gv_feat_find (const gchar *name_to_find); goodvibes-v0.7.2/src/feat/gv-hotkeys.c000066400000000000000000000115571414415210700176510ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "feat/gv-hotkeys.h" /* * GObject definitions */ struct _GvHotkeys { /* Parent instance structure */ GvFeature parent_instance; }; G_DEFINE_TYPE_WITH_CODE(GvHotkeys, gv_hotkeys, GV_TYPE_FEATURE, G_IMPLEMENT_INTERFACE(GV_TYPE_ERRORABLE, NULL)) /* * Signal handlers & callbacks */ static void on_key_pressed(const gchar *keystring, void *data G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; DEBUG("Key '%s' pressed", keystring); /* * Here are the key definitions, from libinput (input.h), and how * they show up from keybinder as xorg keystrings (XF86keysym.h). * * KEY_NEXTSONG 163 XF86AudioNext * KEY_PLAYPAUSE 164 XF86AudioPlay * KEY_PREVIOUSSONG 165 XF86AudioPrev * ? ? XF86AudioStop * ? ? XF86AudioPause */ if (!g_strcmp0(keystring, "XF86AudioPlay")) gv_player_toggle(player); else if (!g_strcmp0(keystring, "XF86AudioStop")) gv_player_toggle(player); else if (!g_strcmp0(keystring, "XF86AudioPause")) gv_player_toggle(player); else if (!g_strcmp0(keystring, "XF86AudioPrev")) gv_player_prev(player); else if (!g_strcmp0(keystring, "XF86AudioNext")) gv_player_next(player); else WARNING("Unhandled key '%s'", keystring); } /* * Private methods */ struct _Binding { const gchar *key; gboolean bound; }; typedef struct _Binding Binding; static Binding bindings[] = { { "XF86AudioPlay", FALSE }, { "XF86AudioPause", FALSE }, { "XF86AudioStop", FALSE }, { "XF86AudioPrev", FALSE }, { "XF86AudioNext", FALSE }, { NULL, FALSE } }; static void gv_hotkeys_unbind(GvHotkeys *self G_GNUC_UNUSED) { Binding *binding; for (binding = bindings; binding->key != NULL; binding++) { if (binding->bound == TRUE) { keybinder_unbind(binding->key, on_key_pressed); binding->bound = FALSE; } } INFO("Multimedia keys undound"); } static void gv_hotkeys_bind(GvHotkeys *self) { Binding *binding; GString *unbound_keys; unbound_keys = NULL; for (binding = bindings; binding->key != NULL; binding++) { const gchar *key; gboolean bound; key = binding->key; bound = keybinder_bind(key, on_key_pressed, self); if (bound == FALSE) { if (unbound_keys == NULL) unbound_keys = g_string_new(key); else g_string_append_printf(unbound_keys, ", %s", key); } binding->bound = bound; } if (unbound_keys) { gchar *text; text = g_string_free(unbound_keys, FALSE); WARNING("Failed to bind the following keys: %s", text); gv_errorable_emit_error(GV_ERRORABLE(self), _("%s:\n%s"), _("Failed to bind the following keys"), text); g_free(text); } else { INFO("Multimedia keys successfully bound"); } } /* * Feature methods */ static void gv_hotkeys_disable(GvFeature *feature) { /* Unbind keys */ gv_hotkeys_unbind(GV_HOTKEYS(feature)); /* Chain up */ GV_FEATURE_CHAINUP_DISABLE(gv_hotkeys, feature); } static void gv_hotkeys_enable(GvFeature *feature) { /* Chain up */ GV_FEATURE_CHAINUP_ENABLE(gv_hotkeys, feature); /* Unbind keys */ gv_hotkeys_bind(GV_HOTKEYS(feature)); } /* * Public methods */ GvFeature * gv_hotkeys_new(void) { return gv_feature_new(GV_TYPE_HOTKEYS, "Hotkeys", GV_FEATURE_DEFAULT); } /* * GObject methods */ static void gv_hotkeys_finalize(GObject *object) { TRACE("%p", object); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_hotkeys, object); } static void gv_hotkeys_constructed(GObject *object) { TRACE("%p", object); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_hotkeys, object); } static void gv_hotkeys_init(GvHotkeys *self) { TRACE("%p", self); } static void gv_hotkeys_class_init(GvHotkeysClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); GvFeatureClass *feature_class = GV_FEATURE_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_hotkeys_finalize; object_class->constructed = gv_hotkeys_constructed; /* Override GvFeature methods */ feature_class->enable = gv_hotkeys_enable; feature_class->disable = gv_hotkeys_disable; /* Init keybinder */ keybinder_init(); } goodvibes-v0.7.2/src/feat/gv-hotkeys.h000066400000000000000000000017231414415210700176500ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "base/gv-feature.h" /* GObject declarations */ #define GV_TYPE_HOTKEYS gv_hotkeys_get_type() G_DECLARE_FINAL_TYPE(GvHotkeys, gv_hotkeys, GV, HOTKEYS, GvFeature) /* Public methods */ GvFeature *gv_hotkeys_new(void); goodvibes-v0.7.2/src/feat/gv-inhibitor-impl.c000066400000000000000000000245341414415210700211100ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gv-ui.h" #include "feat/gv-inhibitor-impl.h" /* * Inhibitor implementation via GtkApplication. * */ #define GV_TYPE_INHIBITOR_GTK gv_inhibitor_gtk_get_type() G_DECLARE_FINAL_TYPE(GvInhibitorGtk, gv_inhibitor_gtk, GV, INHIBITOR_GTK, GvInhibitorImpl) struct _GvInhibitorGtk { GvInhibitorImpl parent_instance; guint cookie; }; typedef struct _GvInhibitorGtk GvInhibitorGtk; G_DEFINE_TYPE(GvInhibitorGtk, gv_inhibitor_gtk, GV_TYPE_INHIBITOR_IMPL) static gboolean gv_inhibitor_gtk_inhibit(GvInhibitorImpl *impl, const gchar *reason, GError **err G_GNUC_UNUSED) { GvInhibitorGtk *self = GV_INHIBITOR_GTK(impl); GtkApplication *app = GTK_APPLICATION(gv_core_application); GtkWindow *win = GTK_WINDOW(gv_ui_main_window); if (self->cookie != 0) return TRUE; self->cookie = gtk_application_inhibit(app, win, GTK_APPLICATION_INHIBIT_SUSPEND, reason); return self->cookie != 0; } static void gv_inhibitor_gtk_uninhibit(GvInhibitorImpl *impl) { GvInhibitorGtk *self = GV_INHIBITOR_GTK(impl); GtkApplication *app = GTK_APPLICATION(gv_core_application); if (self->cookie == 0) return; gtk_application_uninhibit(app, self->cookie); self->cookie = 0; } static gboolean gv_inhibitor_gtk_is_inhibited(GvInhibitorImpl *impl) { GvInhibitorGtk *self = GV_INHIBITOR_GTK(impl); return self->cookie != 0; } static void gv_inhibitor_gtk_finalize(GObject *object) { GvInhibitorImpl *impl = GV_INHIBITOR_IMPL(object); TRACE("%p", object); gv_inhibitor_gtk_uninhibit(impl); G_OBJECT_CHAINUP_FINALIZE(gv_inhibitor_gtk, object); } static void gv_inhibitor_gtk_init(GvInhibitorGtk *self) { TRACE("%p", self); } static void gv_inhibitor_gtk_class_init(GvInhibitorGtkClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); GvInhibitorImplClass *impl_class = GV_INHIBITOR_IMPL_CLASS(class); TRACE("%p", class); object_class->finalize = gv_inhibitor_gtk_finalize; impl_class->inhibit = gv_inhibitor_gtk_inhibit; impl_class->uninhibit = gv_inhibitor_gtk_uninhibit; impl_class->is_inhibited = gv_inhibitor_gtk_is_inhibited; } /* * Inhibitor via the D-Bus service org.freedesktop.PowerManagement */ #define FDO_PM_BUS_NAME "org.freedesktop.PowerManagement" #define FDO_PM_INHIBIT_OBJECT_PATH "/org/freedesktop/PowerManagement/Inhibit" #define FDO_PM_INHIBIT_INTERFACE "org.freedesktop.PowerManagement.Inhibit" #define GV_TYPE_INHIBITOR_PM gv_inhibitor_pm_get_type() G_DECLARE_FINAL_TYPE(GvInhibitorPm, gv_inhibitor_pm, GV, INHIBITOR_PM, GvInhibitorImpl) struct _GvInhibitorPm { GvInhibitorImpl parent_instance; GDBusProxy *proxy; guint32 cookie; }; typedef struct _GvInhibitorPm GvInhibitorPm; G_DEFINE_TYPE(GvInhibitorPm, gv_inhibitor_pm, GV_TYPE_INHIBITOR_IMPL) static GDBusProxy * get_proxy_if_service_present(GDBusConnection *connection, GDBusProxyFlags flags, const gchar *bus_name, const gchar *object_path, const gchar *interface, GError **err) { GDBusProxy *proxy; gchar *owner; proxy = g_dbus_proxy_new_sync( connection, flags, NULL, /* interface info */ bus_name, object_path, interface, NULL, /* cancellable */ err); if (proxy == NULL) return NULL; /* Is there anyone actually providing the service? */ owner = g_dbus_proxy_get_name_owner(proxy); if (owner) { g_free(owner); } else { g_clear_object(&proxy); g_set_error(err, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER, "The name %s has no owner", bus_name); } return proxy; } static gboolean gv_inhibitor_pm_inhibit(GvInhibitorImpl *impl, const gchar *reason, GError **err) { GvInhibitorPm *self = GV_INHIBITOR_PM(impl); const gchar *app_name = g_get_application_name(); GVariant *res; if (self->cookie != 0) return TRUE; if (self->proxy == NULL) { GApplication *app = G_APPLICATION(gv_core_application); g_assert_true(g_application_get_is_registered(app)); self->proxy = get_proxy_if_service_present( g_application_get_dbus_connection(app), G_DBUS_PROXY_FLAGS_NONE, FDO_PM_BUS_NAME, FDO_PM_INHIBIT_OBJECT_PATH, FDO_PM_INHIBIT_INTERFACE, err); if (self->proxy == NULL) return FALSE; } res = g_dbus_proxy_call_sync(self->proxy, "Inhibit", g_variant_new("(ss)", app_name, reason), G_DBUS_CALL_FLAGS_NONE, -1, NULL, err); if (res) { g_variant_get(res, "(u)", &self->cookie); g_variant_unref(res); } return self->cookie != 0; } static void gv_inhibitor_pm_uninhibit(GvInhibitorImpl *impl) { GvInhibitorPm *self = GV_INHIBITOR_PM(impl); if (self->proxy == NULL) return; if (self->cookie == 0) return; g_dbus_proxy_call(self->proxy, "UnInhibit", g_variant_new("(u)", self->cookie), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); self->cookie = 0; } static gboolean gv_inhibitor_pm_is_inhibited(GvInhibitorImpl *impl) { GvInhibitorPm *self = GV_INHIBITOR_PM(impl); return self->cookie != 0; } static void gv_inhibitor_pm_finalize(GObject *object) { GvInhibitorImpl *impl = GV_INHIBITOR_IMPL(object); GvInhibitorPm *self = GV_INHIBITOR_PM(object); TRACE("%p", object); gv_inhibitor_pm_uninhibit(impl); g_clear_object(&self->proxy); G_OBJECT_CHAINUP_FINALIZE(gv_inhibitor_pm, object); } static void gv_inhibitor_pm_init(GvInhibitorPm *self) { TRACE("%p", self); } static void gv_inhibitor_pm_class_init(GvInhibitorPmClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); GvInhibitorImplClass *impl_class = GV_INHIBITOR_IMPL_CLASS(class); TRACE("%p", class); object_class->finalize = gv_inhibitor_pm_finalize; impl_class->inhibit = gv_inhibitor_pm_inhibit; impl_class->uninhibit = gv_inhibitor_pm_uninhibit; impl_class->is_inhibited = gv_inhibitor_pm_is_inhibited; } /* * Base abstract class for the various inhibitors. */ enum { /* Reserved */ PROP_0, /* Properties - refer to class_init() for more details */ PROP_NAME, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; struct _GvInhibitorImplPrivate { gchar *name; }; typedef struct _GvInhibitorImplPrivate GvInhibitorImplPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(GvInhibitorImpl, gv_inhibitor_impl, G_TYPE_OBJECT) gboolean gv_inhibitor_impl_inhibit(GvInhibitorImpl *impl, const gchar *reason, GError **err) { GvInhibitorImplClass *impl_class = GV_INHIBITOR_IMPL_GET_CLASS(impl); g_return_val_if_fail(reason != NULL, FALSE); g_return_val_if_fail(err == NULL || *err == NULL, FALSE); g_return_val_if_fail(impl_class->inhibit != NULL, FALSE); return impl_class->inhibit(impl, reason, err); } void gv_inhibitor_impl_uninhibit(GvInhibitorImpl *impl) { GvInhibitorImplClass *impl_class = GV_INHIBITOR_IMPL_GET_CLASS(impl); g_return_if_fail(impl_class->uninhibit != NULL); impl_class->uninhibit(impl); } gboolean gv_inhibitor_impl_is_inhibited(GvInhibitorImpl *impl) { GvInhibitorImplClass *impl_class = GV_INHIBITOR_IMPL_GET_CLASS(impl); g_return_val_if_fail(impl_class->is_inhibited != NULL, FALSE); return impl_class->is_inhibited(impl); } const gchar * gv_inhibitor_impl_get_name(GvInhibitorImpl *impl) { GvInhibitorImplPrivate *priv = gv_inhibitor_impl_get_instance_private(impl); return priv->name; } static void gv_inhibitor_impl_set_name(GvInhibitorImpl *impl, const gchar *name) { GvInhibitorImplPrivate *priv = gv_inhibitor_impl_get_instance_private(impl); /* This is a construct-only property */ g_assert_null(priv->name); g_assert_nonnull(name); priv->name = g_strdup(name); } GvInhibitorImpl * gv_inhibitor_impl_make(const gchar *name) { if (!g_strcmp0(name, "gtk")) return g_object_new(GV_TYPE_INHIBITOR_GTK, "name", name, NULL); else if (!g_strcmp0(name, "pm")) return g_object_new(GV_TYPE_INHIBITOR_PM, "name", name, NULL); ERROR("Unsupported implementation name: %s", name); /* Program execution stops here */ } static void gv_inhibitor_impl_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvInhibitorImpl *impl = GV_INHIBITOR_IMPL(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_NAME: g_value_set_string(value, gv_inhibitor_impl_get_name(impl)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_inhibitor_impl_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvInhibitorImpl *impl = GV_INHIBITOR_IMPL(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_NAME: gv_inhibitor_impl_set_name(impl, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_inhibitor_impl_finalize(GObject *object) { GvInhibitorImpl *impl = GV_INHIBITOR_IMPL(object); GvInhibitorImplPrivate *priv = gv_inhibitor_impl_get_instance_private(impl); TRACE("%p", object); g_free(priv->name); G_OBJECT_CHAINUP_FINALIZE(gv_inhibitor_impl, object); } static void gv_inhibitor_impl_init(GvInhibitorImpl *impl) { TRACE("%p", impl); } static void gv_inhibitor_impl_class_init(GvInhibitorImplClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_inhibitor_impl_finalize; /* Properties */ object_class->get_property = gv_inhibitor_impl_get_property; object_class->set_property = gv_inhibitor_impl_set_property; properties[PROP_NAME] = g_param_spec_string("name", "Name", NULL, NULL, GV_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties(object_class, PROP_N, properties); } goodvibes-v0.7.2/src/feat/gv-inhibitor-impl.h000066400000000000000000000034231414415210700211070ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include /* GObject declarations */ #define GV_TYPE_INHIBITOR_IMPL gv_inhibitor_impl_get_type() G_DECLARE_DERIVABLE_TYPE(GvInhibitorImpl, gv_inhibitor_impl, GV, INHIBITOR_IMPL, GObject) /* Data types */ struct _GvInhibitorImplClass { /* Parent class */ GObjectClass parent_class; /* Virtual methods */ gboolean (* inhibit) (GvInhibitorImpl *impl, const gchar *reason, GError **err); void (* uninhibit) (GvInhibitorImpl *impl); gboolean (* is_inhibited)(GvInhibitorImpl *impl); }; typedef struct _GvInhibitorImplClass GvInhibitorImplClass; /* Public methods */ GvInhibitorImpl * gv_inhibitor_impl_make (const gchar *name); gboolean gv_inhibitor_impl_inhibit (GvInhibitorImpl *impl, const gchar *reason, GError **err); void gv_inhibitor_impl_uninhibit (GvInhibitorImpl *impl); gboolean gv_inhibitor_impl_is_inhibited(GvInhibitorImpl *impl); const gchar * gv_inhibitor_impl_get_name (GvInhibitorImpl *impl); goodvibes-v0.7.2/src/feat/gv-inhibitor.c000066400000000000000000000151461414415210700201500ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/gv-base.h" #include "core/gv-core.h" #include "feat/gv-inhibitor-impl.h" #include "feat/gv-inhibitor.h" /* * GObject definitions */ const gchar *gv_inhibitor_implementations[] = { "gtk", "pm", NULL }; struct _GvInhibitorPrivate { GvInhibitorImpl *impl; gboolean no_impl_available; guint check_playback_state_timeout_id; }; typedef struct _GvInhibitorPrivate GvInhibitorPrivate; struct _GvInhibitor { /* Parent instance structure */ GvFeature parent_instance; /* Private data */ GvInhibitorPrivate *priv; }; G_DEFINE_TYPE_WITH_CODE(GvInhibitor, gv_inhibitor, GV_TYPE_FEATURE, G_ADD_PRIVATE(GvInhibitor) G_IMPLEMENT_INTERFACE(GV_TYPE_ERRORABLE, NULL)) static void gv_inhibitor_inhibit(GvInhibitor *self, const gchar *reason) { GvInhibitorPrivate *priv = self->priv; TRACE("%p, %s", self, reason); if (priv->no_impl_available) { /* We tried everything already */ DEBUG("No implementation available"); return; } if (priv->impl) { /* We have a known-working implementation */ GError *err = NULL; gboolean ret; ret = gv_inhibitor_impl_inhibit(priv->impl, reason, &err); if (ret == TRUE) { DEBUG("Inhibited system sleep (%s)", gv_inhibitor_impl_get_name(priv->impl)); } else { DEBUG("Failed to inhibit system sleep (%s): %s", gv_inhibitor_impl_get_name(priv->impl), err ? err->message : "unknown error"); g_clear_error(&err); } return; } /* This is the init case, let's iterate over the known implementation * until we find one that works. The only way to know is to try. */ const gchar **impls = gv_inhibitor_implementations; const gchar *impl; while ((impl = *impls++) != NULL) { GError *err = NULL; gboolean ret; DEBUG("Trying to inhibit with the '%s' implementation", impl); priv->impl = gv_inhibitor_impl_make(impl); ret = gv_inhibitor_impl_inhibit(priv->impl, reason, &err); if (ret == TRUE) { DEBUG("Inhibited system sleep (%s)", gv_inhibitor_impl_get_name(priv->impl)); break; } else { DEBUG("Failed to inhibit system sleep (%s): %s", gv_inhibitor_impl_get_name(priv->impl), err ? err->message : "unknown error"); g_clear_object(&priv->impl); g_clear_error(&err); } } if (impl == NULL) { priv->no_impl_available = TRUE; gv_errorable_emit_error(GV_ERRORABLE(self), _("Failed to inhibit system sleep")); } } static void gv_inhibitor_uninhibit(GvInhibitor *self) { GvInhibitorPrivate *priv = self->priv; TRACE("%p", self); if (priv->impl) gv_inhibitor_impl_uninhibit(priv->impl); } static void gv_inhibitor_check_playback_state_now(GvInhibitor *self) { GvPlaybackState playback_state; playback_state = gv_player_get_playback_state(gv_core_player); if (playback_state == GV_PLAYBACK_STATE_PLAYING) gv_inhibitor_inhibit(self, _("Playing")); else gv_inhibitor_uninhibit(self); } static gboolean when_timeout_check_playback_state(GvInhibitor *self) { GvInhibitorPrivate *priv = self->priv; gv_inhibitor_check_playback_state_now(self); priv->check_playback_state_timeout_id = 0; return G_SOURCE_REMOVE; } static void gv_inhibitor_check_playback_state_delayed(GvInhibitor *self, guint delay) { GvInhibitorPrivate *priv = self->priv; g_clear_handle_id(&priv->check_playback_state_timeout_id, g_source_remove); priv->check_playback_state_timeout_id = g_timeout_add_seconds(delay, (GSourceFunc) when_timeout_check_playback_state, self); } /* * Signal handlers & callbacks */ static void on_player_notify_state(GvPlayer *player, GParamSpec *pspec G_GNUC_UNUSED, GvInhibitor *self) { GvPlaybackState playback_state; playback_state = gv_player_get_playback_state(player); /* 'connecting' and 'buffering' are intermediary states, so let's wait * a bit handling it, but let's schedule a callback all the same, just * in case the player gets stuck in one of those states for some reason. */ if (playback_state == GV_PLAYBACK_STATE_CONNECTING || playback_state == GV_PLAYBACK_STATE_BUFFERING) gv_inhibitor_check_playback_state_delayed(self, 10); else gv_inhibitor_check_playback_state_delayed(self, 1); } /* * Feature methods */ static void gv_inhibitor_disable(GvFeature *feature) { GvInhibitor *self = GV_INHIBITOR(feature); GvInhibitorPrivate *priv = self->priv; GvPlayer *player = gv_core_player; TRACE("%p", feature); /* Remove pending operation */ g_clear_handle_id(&priv->check_playback_state_timeout_id, g_source_remove); /* Cleanup */ g_clear_object(&priv->impl); priv->no_impl_available = FALSE; /* Signal handlers */ g_signal_handlers_disconnect_by_data(player, feature); /* Chain up */ GV_FEATURE_CHAINUP_DISABLE(gv_inhibitor, feature); } static void gv_inhibitor_enable(GvFeature *feature) { GvInhibitor *self = GV_INHIBITOR(feature); GvInhibitorPrivate *priv = self->priv; GvPlayer *player = gv_core_player; TRACE("%p", feature); /* Chain up */ GV_FEATURE_CHAINUP_ENABLE(gv_inhibitor, feature); /* Connect to signal handlers */ g_signal_connect_object(player, "notify::playback-state", G_CALLBACK(on_player_notify_state), self, 0); /* Schedule a check for the current playback status */ g_assert(priv->check_playback_state_timeout_id == 0); priv->check_playback_state_timeout_id = g_timeout_add_seconds(1, (GSourceFunc) when_timeout_check_playback_state, self); } /* * Public methods */ GvFeature * gv_inhibitor_new(void) { return gv_feature_new(GV_TYPE_INHIBITOR, "Inhibitor", GV_FEATURE_DEFAULT); } /* * GObject methods */ static void gv_inhibitor_init(GvInhibitor *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_inhibitor_get_instance_private(self); } static void gv_inhibitor_class_init(GvInhibitorClass *class) { GvFeatureClass *feature_class = GV_FEATURE_CLASS(class); TRACE("%p", class); /* Override GvFeature methods */ feature_class->enable = gv_inhibitor_enable; feature_class->disable = gv_inhibitor_disable; } goodvibes-v0.7.2/src/feat/gv-inhibitor.h000066400000000000000000000017371414415210700201560ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "base/gv-feature.h" /* GObject declarations */ #define GV_TYPE_INHIBITOR gv_inhibitor_get_type() G_DECLARE_FINAL_TYPE(GvInhibitor, gv_inhibitor, GV, INHIBITOR, GvFeature) /* Public methods */ GvFeature *gv_inhibitor_new(void); goodvibes-v0.7.2/src/feat/gv-notifications.c000066400000000000000000000144231414415210700210270ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "feat/gv-notifications.h" #define NOTIF_ID_ERROR "error" #define NOTIF_ID_PLAYING "playing" /* * GObject definitions */ struct _GvNotifications { /* Parent instance structure */ GvFeature parent_instance; }; G_DEFINE_TYPE(GvNotifications, gv_notifications, GV_TYPE_FEATURE) /* * Helpers */ static GNotification * make_station_notification(GvStation *station) { GNotification *notif; const gchar *str; gchar *body; if (station == NULL) return NULL; str = gv_station_get_name(station); if (str) { body = g_strdup_printf(_("Playing %s"), str); } else { str = gv_station_get_uri(station); body = g_strdup_printf(_("Playing <%s>"), str); } notif = g_notification_new(_("Playing")); g_notification_set_body(notif, body); g_free(body); return notif; } static GNotification * make_metadata_notification(GvMetadata *metadata) { GNotification *notif; const gchar *artist; const gchar *title; const gchar *album; const gchar *year; const gchar *genre; gchar *body; if (metadata == NULL) return NULL; artist = gv_metadata_get_artist(metadata); title = gv_metadata_get_title(metadata); album = gv_metadata_get_album(metadata); year = gv_metadata_get_year(metadata); genre = gv_metadata_get_genre(metadata); if (title && !artist && !album && !year && !genre) { /* If there's only the 'title' field, don't bother being smart, * just display it as it. Actually, most radios fill only this field, * and put everything in it (title + artist + some more stuff). */ body = g_strdup(title); } else { /* Otherwise, each existing field is displayed on a line */ gchar *album_year; if (title == NULL) title = _("(Unknown title)"); album_year = gv_metadata_make_album_year(metadata, FALSE); body = g_strjoin_null("\n", 4, title, artist, album_year, genre); g_free(album_year); } notif = g_notification_new(_("Playing")); g_notification_set_body(notif, body); g_free(body); return notif; } static GNotification * make_error_notification(const gchar *error_string) { GNotification *notif; notif = g_notification_new(_("Error")); g_notification_set_body(notif, error_string); return notif; } /* * Signal handlers & callbacks */ static void on_player_notify(GvPlayer *player, GParamSpec *pspec, GvNotifications *self G_GNUC_UNUSED) { const gchar *property_name = g_param_spec_get_name(pspec); GApplication *app = gv_core_application; if (!g_strcmp0(property_name, "playback-state")) { GNotification *notif; GvPlaybackState state; GvStation *station; state = gv_player_get_playback_state(player); if (state == GV_PLAYBACK_STATE_STOPPED) { g_application_withdraw_notification(app, NOTIF_ID_PLAYING); return; } if (state != GV_PLAYBACK_STATE_PLAYING) return; station = gv_player_get_station(player); notif = make_station_notification(station); if (notif == NULL) return; g_application_send_notification(app, NOTIF_ID_PLAYING, notif); g_object_unref(notif); } else if (!g_strcmp0(property_name, "metadata")) { GNotification *notif; GvMetadata *metadata; metadata = gv_player_get_metadata(player); notif = make_metadata_notification(metadata); if (notif == NULL) return; g_application_send_notification(app, NOTIF_ID_PLAYING, notif); g_object_unref(notif); } } static void on_errorable_error(GvErrorable *errorable, const gchar *error_string, GvNotifications *self) { GApplication *app = gv_core_application; GNotification *notif; TRACE("%p, %s, %p", errorable, error_string, self); notif = make_error_notification(error_string); g_application_send_notification(app, NOTIF_ID_ERROR, notif); g_object_unref(notif); } /* * Feature methods */ static void gv_notifications_disable(GvFeature *feature) { GvPlayer *player = gv_core_player; GApplication *app = gv_core_application; GList *item; /* Withdraw notifications */ g_application_withdraw_notification(app, NOTIF_ID_ERROR); g_application_withdraw_notification(app, NOTIF_ID_PLAYING); /* Disconnect signal handlers */ for (item = gv_base_get_objects(); item; item = item->next) { GObject *object = G_OBJECT(item->data); if (GV_IS_ERRORABLE(object) == FALSE) continue; g_signal_handlers_disconnect_by_data(object, feature); } g_signal_handlers_disconnect_by_data(player, feature); /* Chain up */ GV_FEATURE_CHAINUP_DISABLE(gv_notifications, feature); } static void gv_notifications_enable(GvFeature *feature) { GvPlayer *player = gv_core_player; GList *item; /* Chain up */ GV_FEATURE_CHAINUP_ENABLE(gv_notifications, feature); /* Connect to player 'notify' */ g_signal_connect_object(player, "notify", G_CALLBACK(on_player_notify), feature, 0); /* Connect to objects that emit 'error' */ for (item = gv_base_get_objects(); item; item = item->next) { GObject *object = G_OBJECT(item->data); if (GV_IS_ERRORABLE(object) == FALSE) continue; g_signal_connect_object(object, "error", G_CALLBACK(on_errorable_error), feature, 0); } } /* * Public methods */ GvFeature * gv_notifications_new(void) { return gv_feature_new(GV_TYPE_NOTIFICATIONS, "Notifications", GV_FEATURE_EARLY); } /* * GObject methods */ static void gv_notifications_init(GvNotifications *self) { TRACE("%p", self); } static void gv_notifications_class_init(GvNotificationsClass *class) { GvFeatureClass *feature_class = GV_FEATURE_CLASS(class); TRACE("%p", class); /* Override GvFeature methods */ feature_class->enable = gv_notifications_enable; feature_class->disable = gv_notifications_disable; } goodvibes-v0.7.2/src/feat/gv-notifications.h000066400000000000000000000017671414415210700210430ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "base/gv-feature.h" /* GObject declarations */ #define GV_TYPE_NOTIFICATIONS gv_notifications_get_type() G_DECLARE_FINAL_TYPE(GvNotifications, gv_notifications, GV, NOTIFICATIONS, GvFeature) /* Public methods */ GvFeature *gv_notifications_new(void); goodvibes-v0.7.2/src/feat/meson.build000066400000000000000000000017531414415210700175440ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only # Sources and dependencies feat_sources = [ 'gv-feat.c' ] feat_dependencies = [ glib_dep, gobject_dep, gvbase_dep, gvcore_dep, ] if gv_feat_console_output feat_sources += 'gv-console-output.c' endif if gv_feat_dbus_server feat_sources += [ 'gv-dbus-server.c', 'gv-dbus-server-native.c', 'gv-dbus-server-mpris2.c' ] if gv_ui_enabled feat_dependencies += gvui_dep endif endif if gv_feat_hotkeys feat_sources += 'gv-hotkeys.c' feat_dependencies += keybinder_dep endif if gv_feat_inhibitor feat_sources += [ 'gv-inhibitor.c', 'gv-inhibitor-impl.c', ] feat_dependencies += gvui_dep endif if gv_feat_notifications feat_sources += 'gv-notifications.c' endif # Library definition gvfeat = static_library('gvfeat', sources: [ feat_sources ], dependencies: feat_dependencies, include_directories: root_inc, ) gvfeat_dep = declare_dependency( dependencies: feat_dependencies, link_with: gvfeat, ) goodvibes-v0.7.2/src/gv-console-application.c000066400000000000000000000073601414415210700212040ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2017-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/gv-base.h" #include "core/gv-core.h" #include "feat/gv-feat.h" #include "default-stations.h" #include "gv-console-application.h" #include "options.h" /* * GObject definitions */ struct _GvConsoleApplication { /* Parent instance structure */ GApplication parent_instance; }; G_DEFINE_TYPE(GvConsoleApplication, gv_console_application, G_TYPE_APPLICATION) /* * Public methods */ GApplication * gv_console_application_new(const gchar *application_id) { GvConsoleApplication *self; self = g_object_new(GV_TYPE_CONSOLE_APPLICATION, "application-id", application_id, "flags", G_APPLICATION_FLAGS_NONE, NULL); return G_APPLICATION(self); } /* * GApplication methods */ static void gv_console_application_shutdown(GApplication *app) { DEBUG_NO_CONTEXT(">>>> Main loop terminated <<<<"); /* Cleanup */ DEBUG_NO_CONTEXT("---- Cleaning up ----"); gv_feat_cleanup(); gv_core_cleanup(); gv_base_cleanup(); /* Mandatory chain-up */ G_APPLICATION_CLASS(gv_console_application_parent_class)->shutdown(app); } static void gv_console_application_startup(GApplication *app) { DEBUG_NO_CONTEXT("---- Starting application ----"); /* Mandatory chain-up, see: * https://developer.gnome.org/gtk3/stable/GtkApplication.html#gtk-application-new */ G_APPLICATION_CLASS(gv_console_application_parent_class)->startup(app); /* Initialization */ DEBUG_NO_CONTEXT("---- Initializing ----"); gv_base_init(); gv_core_init(app, DEFAULT_STATIONS); gv_feat_init(); gv_base_init_completed(); /* Configuration */ DEBUG_NO_CONTEXT("---- Configuring ----"); gv_feat_configure_early(); gv_core_configure(); gv_feat_configure_late(); /* Hold application */ g_application_hold(app); } static gboolean when_idle_go_player(gpointer user_data) { const gchar *uri_to_play = user_data; gv_player_go(gv_core_player, uri_to_play); return G_SOURCE_REMOVE; } static void gv_console_application_activate(GApplication *app G_GNUC_UNUSED) { static gboolean first_invocation = TRUE; if (first_invocation) { first_invocation = FALSE; DEBUG_NO_CONTEXT(">>>> Main loop started <<<<"); /* Schedule a callback to play music. * DO NOT start playing now ! It's too early ! * There's still some init code pending, and we want to ensure * (as much as possible) that this init code is run before we * start the playback. Therefore we schedule with a low priority. */ g_idle_add_full(G_PRIORITY_LOW, when_idle_go_player, (void *) options.uri_to_play, NULL); } } /* * GObject methods */ static void gv_console_application_init(GvConsoleApplication *self) { TRACE("%p", self); } static void gv_console_application_class_init(GvConsoleApplicationClass *class) { GApplicationClass *application_class = G_APPLICATION_CLASS(class); TRACE("%p", class); /* Override GApplication methods */ application_class->startup = gv_console_application_startup; application_class->shutdown = gv_console_application_shutdown; application_class->activate = gv_console_application_activate; } goodvibes-v0.7.2/src/gv-console-application.h000066400000000000000000000020741414415210700212060ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2017-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_CONSOLE_APPLICATION gv_console_application_get_type() G_DECLARE_FINAL_TYPE(GvConsoleApplication, gv_console_application, GV, CONSOLE_APPLICATION, GApplication) /* Methods */ GApplication *gv_console_application_new(const gchar *application_id); goodvibes-v0.7.2/src/gv-graphical-application.c000066400000000000000000000165621414415210700215000ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2017-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/gv-base.h" #include "core/gv-core.h" #include "feat/gv-feat.h" #include "ui/gv-ui.h" #include "default-stations.h" #include "gv-graphical-application.h" #include "options.h" /* * GObject definitions */ struct _GvGraphicalApplication { /* Parent instance structure */ GtkApplication parent_instance; }; G_DEFINE_TYPE(GvGraphicalApplication, gv_graphical_application, GTK_TYPE_APPLICATION) /* * Public methods */ GApplication * gv_graphical_application_new(const gchar *application_id) { GvGraphicalApplication *self; self = g_object_new(GV_TYPE_GRAPHICAL_APPLICATION, "application-id", application_id, "flags", G_APPLICATION_FLAGS_NONE, NULL); return G_APPLICATION(self); } /* * GActions */ static void play_stop_action_cb(GSimpleAction *action G_GNUC_UNUSED, GVariant *parameters G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { gv_ui_play_stop(); } static void add_station_action_cb(GSimpleAction *action G_GNUC_UNUSED, GVariant *parameters G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { gv_ui_present_add_station(); } static void preferences_action_cb(GSimpleAction *action G_GNUC_UNUSED, GVariant *parameters G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { gv_ui_present_preferences(); } static void keyboard_shortcuts_action_cb(GSimpleAction *action G_GNUC_UNUSED, GVariant *parameters G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { gv_ui_present_keyboard_shortcuts(); } static void help_action_cb(GSimpleAction *action G_GNUC_UNUSED, GVariant *parameters G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { g_app_info_launch_default_for_uri(GV_ONLINE_HELP, NULL, NULL); } static void about_action_cb(GSimpleAction *action G_GNUC_UNUSED, GVariant *parameters G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { gv_ui_present_about(); } static void close_ui_action_cb(GSimpleAction *action G_GNUC_UNUSED, GVariant *parameters G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { gv_ui_hide(); } static void quit_action_cb(GSimpleAction *action G_GNUC_UNUSED, GVariant *parameters G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { gv_core_quit(); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" static const GActionEntry action_entries[] = { { "play-stop", play_stop_action_cb }, { "add-station", add_station_action_cb }, { "preferences", preferences_action_cb }, { "help", help_action_cb }, { "about", about_action_cb }, { "quit", quit_action_cb }, { NULL } }; static const GActionEntry standalone_action_entries[] = { { "keyboard-shortcuts", keyboard_shortcuts_action_cb }, { "close-ui", close_ui_action_cb }, { NULL } }; #pragma GCC diagnostic pop static void add_g_action_entries(GApplication *app, gboolean status_icon_mode) { g_action_map_add_action_entries(G_ACTION_MAP(app), action_entries, -1, NULL); if (status_icon_mode == FALSE) g_action_map_add_action_entries(G_ACTION_MAP(app), standalone_action_entries, -1, NULL); } /* * Accelerators */ struct _GvActionAccel { const gchar *action; const gchar *accel; }; typedef struct _GvActionAccel GvActionAccel; static const GvActionAccel action_accels[] = { { "app.play-stop", "space" }, { "app.add-station", "a" }, { "app.help", "F1" }, { "app.close-ui", "c" }, { "app.quit", "q" }, { NULL, NULL } }; static void setup_accels(GApplication *gapp, gboolean status_icon_mode) { GtkApplication *app = GTK_APPLICATION(gapp); const GvActionAccel *a; /* No keyboard shortcuts for status icon mode */ if (status_icon_mode == TRUE) return; for (a = action_accels; a->action != NULL; a++) { const gchar *accels[] = { a->accel, NULL }; gtk_application_set_accels_for_action(app, a->action, accels); } } /* * GApplication methods */ static void gv_graphical_application_shutdown(GApplication *app) { DEBUG_NO_CONTEXT(">>>> Main loop terminated <<<<"); /* Cleanup */ DEBUG_NO_CONTEXT("---- Cleaning up ----"); gv_feat_cleanup(); gv_ui_cleanup(); gv_core_cleanup(); gv_base_cleanup(); /* Mandatory chain-up */ G_APPLICATION_CLASS(gv_graphical_application_parent_class)->shutdown(app); } static void gv_graphical_application_startup(GApplication *app) { DEBUG_NO_CONTEXT("---- Starting application ----"); /* Mandatory chain-up, see: * https://developer.gnome.org/gtk3/stable/GtkApplication.html#gtk-application-new */ G_APPLICATION_CLASS(gv_graphical_application_parent_class)->startup(app); /* Setup actions */ add_g_action_entries(app, options.status_icon); setup_accels(app, options.status_icon); /* Initialization */ DEBUG_NO_CONTEXT("---- Initializing ----"); gv_base_init(); gv_core_init(app, DEFAULT_STATIONS); gv_ui_init(app, options.status_icon); gv_feat_init(); gv_base_init_completed(); /* Configuration */ DEBUG_NO_CONTEXT("---- Configuring ----"); gv_feat_configure_early(); gv_core_configure(); gv_ui_configure(); gv_feat_configure_late(); /* Hold application */ g_application_hold(app); } static gboolean when_idle_go_player(gpointer user_data) { const gchar *uri_to_play = user_data; gv_player_go(gv_core_player, uri_to_play); return G_SOURCE_REMOVE; } static void gv_graphical_application_activate(GApplication *app G_GNUC_UNUSED) { static gboolean first_invocation = TRUE; if (first_invocation) { first_invocation = FALSE; DEBUG_NO_CONTEXT(">>>> Main loop started <<<<"); /* Schedule a callback to play music. * DO NOT start playing now! It's too early! * There's still some init code pending, and we want to ensure * (as much as possible) that this init code is run before we * start the playback. Therefore we schedule with a low priority. */ g_idle_add_full(G_PRIORITY_LOW, when_idle_go_player, (void *) options.uri_to_play, NULL); /* Present the main window, depending on options */ if (!options.without_ui) { DEBUG("Presenting main window"); gv_ui_present_main(); } else { DEBUG("NOT presenting main window (--without-ui)"); } } else { /* Present the main window, unconditionally */ DEBUG("Presenting main window"); gv_ui_present_main(); } } /* * GObject methods */ static void gv_graphical_application_init(GvGraphicalApplication *self) { TRACE("%p", self); } static void gv_graphical_application_class_init(GvGraphicalApplicationClass *class) { GApplicationClass *application_class = G_APPLICATION_CLASS(class); TRACE("%p", class); /* Override GApplication methods */ application_class->startup = gv_graphical_application_startup; application_class->shutdown = gv_graphical_application_shutdown; application_class->activate = gv_graphical_application_activate; } goodvibes-v0.7.2/src/gv-graphical-application.h000066400000000000000000000021371414415210700214760ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2017-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include #include /* GObject declarations */ #define GV_TYPE_GRAPHICAL_APPLICATION gv_graphical_application_get_type() G_DECLARE_FINAL_TYPE(GvGraphicalApplication, gv_graphical_application, GV, GRAPHICAL_APPLICATION, GtkApplication) /* Methods */ GApplication *gv_graphical_application_new(const gchar *application_id); goodvibes-v0.7.2/src/main.c000066400000000000000000000115301414415210700155450ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "base/glib-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #ifdef GV_UI_ENABLED #include "ui/gv-ui.h" #endif #ifdef GV_UI_ENABLED #include "gv-graphical-application.h" #else #include "gv-console-application.h" #endif #include "options.h" /* * Print informations about program. */ static const gchar * string_package_info(void) { return GV_NAME_CAPITAL " " PACKAGE_VERSION; } static const gchar * string_copyright(void) { return GV_COPYRIGHT " " GV_AUTHOR_NAME " <" GV_AUTHOR_EMAIL ">"; } static const gchar * string_date_now(void) { GDateTime *now; static gchar *text; g_free(text); now = g_date_time_new_now_local(); text = g_date_time_format(now, "%F, %T"); g_date_time_unref(now); return text; } static const gchar * string_compile_libraries(void) { static gchar *text; if (text == NULL) text = g_strjoin(", ", glib_get_compile_version_string(), gv_core_audio_backend_compile_version_string(), #ifdef GV_UI_ENABLED gv_ui_toolkit_compile_version_string(), #endif NULL); return text; } static const gchar * string_runtime_libraries(void) { static gchar *text; if (text == NULL) text = g_strjoin(", ", glib_get_runtime_version_string(), gv_core_audio_backend_runtime_version_string(), #ifdef GV_UI_ENABLED gv_ui_toolkit_runtime_version_string(), #endif NULL); return text; } /* * Main - this is where everything starts... */ static gboolean sigint_handler(gpointer user_data) { GApplication *application = G_APPLICATION(user_data); /* There's probably a '^C' written on the console line by now. * Let's start a new line to keep logs clean. */ putchar('\n'); /* Stop application */ g_application_quit(application); return FALSE; } int main(int argc, char *argv[]) { GApplication *app; int ret; /* Initialize i18n */ setlocale(LC_ALL, NULL); bindtextdomain(GETTEXT_PACKAGE, GV_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); /* Set application name */ g_set_prgname(PACKAGE_NAME); g_set_application_name(_("Goodvibes")); #ifdef GV_UI_ENABLED /* Set application icon */ gtk_window_set_default_icon_name(GV_ICON_NAME); #endif /* Parse command-line, and run some init code at the same time */ options_parse(&argc, &argv); /* We might just want to print the version and exit */ if (options.print_version) { g_print("%s\n", string_package_info()); g_print("%s\n", string_copyright()); g_print("Compiled against: %s\n", string_compile_libraries()); g_print("Running along : %s\n", string_runtime_libraries()); return EXIT_SUCCESS; } /* Run in background. * This option may be used along with the 'logfile' option, therefore the * arguments for daemon() must be as follow: * - nochdir = 1: we MUST NOT change the working directory * (otherwise a relative path for logfile won't work) * - noclose = 0: we MUST close std{in/out/err} BEFORE initializing the logs * (and let the log system re-open std{out/err} afterward if needed). */ if (options.background) { if (daemon(1, 0) == -1) { g_printerr("Failed to daemonize: %s\n", strerror(errno)); return EXIT_FAILURE; } } /* Initialize log system, warm it up with a few logs */ log_init(options.log_level, options.colorless, options.output_file); INFO("%s", string_package_info()); INFO("%s", string_copyright()); INFO("Started on %s, with pid %ld", string_date_now(), (long) getpid()); INFO("Compiled against : %s", string_compile_libraries()); INFO("Running along : %s", string_runtime_libraries()); INFO("Gettext locale dir: %s", GV_LOCALEDIR); /* Create the application */ #ifdef GV_UI_ENABLED app = gv_graphical_application_new(GV_APPLICATION_ID); #else app = gv_console_application_new(GV_APPLICATION_ID); #endif /* Quit on SIGINT */ g_unix_signal_add(SIGINT, sigint_handler, app); /* Run the application */ ret = g_application_run(app, 0, NULL); /* Cleanup */ log_cleanup(); options_cleanup(); return ret; } goodvibes-v0.7.2/src/meson.build000066400000000000000000000014071414415210700166210ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only # Subdirectories root_inc = include_directories('.') subdir('base') subdir('core') if gv_ui_enabled subdir('ui') endif subdir('feat') # Source and dependencies goodvibes_sources = [ 'main.c', 'options.c', ] goodvibes_dependencies = [ gvbase_dep, gvcore_dep, gvfeat_dep, math_dep, ] if gv_ui_enabled goodvibes_sources += 'gv-graphical-application.c' goodvibes_dependencies += gvui_dep else goodvibes_sources += 'gv-console-application.c' endif # Executable definition executable('goodvibes', goodvibes_sources, dependencies: goodvibes_dependencies, include_directories: root_inc, install: true ) executable('goodvibes-client', [ 'client.c' ], dependencies: [ glib_dep, gio_dep ], install: true ) goodvibes-v0.7.2/src/options.c000066400000000000000000000071071414415210700163210ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/gv-base.h" #include "core/gv-core.h" #ifdef GV_UI_ENABLED #include "ui/gv-ui.h" #endif #include "options.h" struct options options; static GOptionEntry entries[] = { { "background", 'b', 0, G_OPTION_ARG_NONE, &options.background, "Run in the background", NULL }, { "colorless", 'c', 0, G_OPTION_ARG_NONE, &options.colorless, "Disable colors in log messages", NULL }, { "log-level", 'l', 0, G_OPTION_ARG_STRING, &options.log_level, "Set the log level, amongst: trace, debug, info, warning, critical, error.", "warning" }, { "output-file", 'o', 0, G_OPTION_ARG_STRING, &options.output_file, "Redirect log messages to a file", "file" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &options.print_version, "Print the version and exit", NULL }, #ifdef GV_UI_ENABLED { "without-ui", 0, 0, G_OPTION_ARG_NONE, &options.without_ui, "Disable the graphical user interface at startup", NULL }, { "status-icon", 0, 0, G_OPTION_ARG_NONE, &options.status_icon, "Launch as a status icon (deprecated on modern desktops)", NULL }, #endif { .long_name = NULL } }; static void print_help(GOptionContext *context) { gchar *help; help = g_option_context_get_help(context, TRUE, NULL); g_print("%s", help); g_free(help); } void options_parse(int *argc, char **argv[]) { GError *err = NULL; GOptionContext *context; const gchar *context_summary; /* Init options */ memset(&options, 0, sizeof(struct options)); /* Create context & entries */ context = g_option_context_new("[STATION]"); context_summary = GV_NAME_CAPITAL " is a lightweight internet radio player for GNU/Linux.\n" "It offers a simple way to have your favorite radio stations at easy reach.\n" "\n" "To control it via the command line, use '" PACKAGE_NAME "-client'."; g_option_context_set_summary(context, context_summary); g_option_context_add_main_entries(context, entries, NULL); /* Add option groups and perform some init code at the same time */ g_option_context_add_group(context, gv_core_audio_backend_init_get_option_group()); #ifdef GV_UI_ENABLED g_option_context_add_group(context, gv_ui_toolkit_init_get_option_group()); #endif /* Parse the command-line arguments */ if (!g_option_context_parse(context, argc, argv, &err)) { g_print("Failed to parse options: %s\n", err->message); g_error_free(err); g_option_context_free(context); exit(EXIT_FAILURE); } /* There should be at most one argument left: the URI to play */ switch (*argc) { case 1: options.uri_to_play = NULL; break; case 2: options.uri_to_play = (*argv)[1]; break; default: print_help(context); g_option_context_free(context); exit(EXIT_FAILURE); break; } /* Leave no trace behind */ g_option_context_free(context); } void options_cleanup(void) { /* Run some cleanup code that matches the init code done in parse() */ gv_core_audio_backend_cleanup(); } goodvibes-v0.7.2/src/options.h000066400000000000000000000021431414415210700163210ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once struct options { /* Options */ gboolean background; gboolean colorless; const gchar *log_level; const gchar *output_file; gboolean print_version; #ifdef GV_UI_ENABLED gboolean without_ui; gboolean status_icon; #endif /* Arguments */ const gchar *uri_to_play; }; extern struct options options; void options_parse(int *argc, char **argv[]); void options_cleanup(void); goodvibes-v0.7.2/src/ui/000077500000000000000000000000001414415210700150725ustar00rootroot00000000000000goodvibes-v0.7.2/src/ui/gtk-additions.c000066400000000000000000000025101414415210700177750ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include /* * Version Information */ const gchar * gtk_get_runtime_version_string(void) { static gchar *version_string; if (version_string == NULL) { version_string = g_strdup_printf("GTK %u.%u.%u", gtk_get_major_version(), gtk_get_minor_version(), gtk_get_micro_version()); } return version_string; } const gchar * gtk_get_compile_version_string(void) { static gchar *version_string; if (version_string == NULL) { version_string = g_strdup_printf("GTK %u.%u.%u", GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION); } return version_string; } goodvibes-v0.7.2/src/ui/gtk-additions.h000066400000000000000000000043471414415210700200140ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include /* * Version Information */ const gchar *gtk_get_runtime_version_string(void); const gchar *gtk_get_compile_version_string(void); /* * GtkBuilder */ #define gtk_builder_get_widget(builder, name) \ GTK_WIDGET(gtk_builder_get_object(builder, name)) #define gtk_builder_get_object(builder, name) \ G_OBJECT(gtk_builder_get_object(builder, name)) /* * Convenient macros to save widgets from a GtkBuilder instance. * If the widget is not found, it is considered as a programming error, * therefore ending the execution of the program. * This macro also ensures that the widget name in the ui file is the same * as the one used in the C file, therefore enforcing a consistent naming * across the code. */ #define GTK_BUILDER_SAVE_WIDGET(builder, str, name) \ do { \ str->name = gtk_builder_get_widget(builder, #name); \ g_assert(GTK_IS_WIDGET(str->name)); \ } while (0) #define GTK_BUILDER_SAVE_OBJECT(builder, str, name) \ do { \ str->name = gtk_builder_get_object(builder, #name); \ g_assert(G_IS_OBJECT(str->name)); \ } while (0) /* * GtkWidget */ #define gtk_widget_set_margins(widget, margin) \ do { \ gtk_widget_set_margin_start(widget, margin); \ gtk_widget_set_margin_end(widget, margin); \ gtk_widget_set_margin_top(widget, margin); \ gtk_widget_set_margin_bottom(widget, margin); \ } while (0) goodvibes-v0.7.2/src/ui/gv-about-dialog.c000066400000000000000000000070141414415210700202210ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "base/gv-base.h" static const gchar *authors[] = { GV_AUTHOR_NAME " <" GV_AUTHOR_EMAIL ">", NULL }; static const gchar *artists[] = { "Lahminèwski Lab https://lahminewski-lab.net", NULL }; static const gchar *translators = "Weblate https://hosted.weblate.org/projects/goodvibes/translations\n" "Adolfo Jayme Barrientos - Catalan (ca)\n" "Lukáš Linhart - Czech (cs)\n" "Michal ÄŒihaÅ™ - Czech (cs)\n" "Torben Grove - Danish (da)\n" "Andreas Kleinert - German (de)\n" "Vinz - German (de)\n" "Daniel Bageac - English (United States) (en_US)\n" "Adolfo Jayme Barrientos - Spanish (es)\n" "Benages - Spanish (es)\n" "Holman Calderón - Spanish (es)\n" "Étienne Deparis - French (fr)\n" "Jeannette L - French (fr)\n" "Milo Ivir - Croatian (hr)\n" "Notramo - (hu)\n" "Reza Almanda - Indonesian (id)\n" "Silvyaamalia - Indonesian (id)\n" "Dallenog - Italian (it)\n" "Luca De Filippo - Italian (it)\n" "Prachi Joshi - Marathi (mr)\n" "Allan Nordhøy - Norwegian BokmÃ¥l (nb_NO)\n" "Heimen Stoffels - Dutch (nl)\n" "Michal Biesiada - Polish (pl)\n" "Fúlvio Alves - Portuguese (Brazil) (pt_BR)\n" "Ssantos - Portuguese (pt)\n" "Manuela Silva - Portuguese (Portugal) (pt_PT)\n" "Ssantos - Portuguese (Portugal) (pt_PT)\n" "ÐлекÑей Ð’Ñ‹Ñкубов - Russian (ru)\n" "Иван - Russian (ru)\n" "Andrej Shadura - Slovak (sk)\n" "Akash Rao - Telugu (te)\n" "OÄŸuz Ersen - Turkish (tr)"; void gv_show_about_dialog(GtkWindow *parent, const gchar *audio_backend_string, const gchar *ui_toolkit_string) { // WISHED "license-type" shouldn't be hardcoded gchar *comments; comments = g_strdup_printf("Audio Backend: %s\n" "GUI Toolkit: %s", audio_backend_string, ui_toolkit_string); gtk_show_about_dialog(parent, "artists", artists, "authors", authors, "comments", comments, "copyright", GV_COPYRIGHT " " GV_AUTHOR_NAME, "license-type", GTK_LICENSE_GPL_3_0, "logo-icon-name", GV_ICON_NAME, "translator-credits", translators, "version", PACKAGE_VERSION, "website", GV_HOMEPAGE, NULL); g_free(comments); } goodvibes-v0.7.2/src/ui/gv-about-dialog.h000066400000000000000000000015651414415210700202330ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include void gv_show_about_dialog(GtkWindow *parent, const gchar *audio_backend_string, const gchar *ui_toolkit_string); goodvibes-v0.7.2/src/ui/gv-keyboard-shortcuts-window.c000066400000000000000000000027471414415210700230230ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "base/gv-base.h" #define UI_RESOURCE_PATH GV_APPLICATION_PATH "/Ui/shortcuts-window.ui" static GtkWindow * make_shortcuts_window(GtkWindow *parent) { GtkBuilder *builder; GtkWindow *window; builder = gtk_builder_new_from_resource(UI_RESOURCE_PATH); window = GTK_WINDOW(gtk_builder_get_object(builder, "shortcuts-window")); gtk_window_set_transient_for(window, parent); gtk_window_set_destroy_with_parent(window, TRUE); g_object_ref_sink(G_OBJECT(window)); g_object_unref(builder); return window; } void gv_show_keyboard_shortcuts_window(GtkWindow *parent) { static GtkWindow *window; if (window == NULL) { window = make_shortcuts_window(parent); g_object_add_weak_pointer(G_OBJECT(window), (gpointer *) &window); } gtk_window_present(window); } goodvibes-v0.7.2/src/ui/gv-keyboard-shortcuts-window.h000066400000000000000000000014451414415210700230220ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include void gv_show_keyboard_shortcuts_window(GtkWindow *parent); goodvibes-v0.7.2/src/ui/gv-main-window-manager.c000066400000000000000000000300031414415210700215050ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "ui/gv-main-window.h" #include "ui/gv-ui-internal.h" #include "ui/gv-main-window-manager.h" /* * Reasons to have a separate object to deal with the main window configuration, * aka size, position, maximized. * * Delayed method calls * -------------------- * * We invoke methods on the main window in two contexts: * - at init time (to load configuration) * - in signal handlers (to save configuration) * * Both of these contexts are not suitable to query the window size, and because * of that we need to delay all the method calls. This logic is better taken out * of the main window, and handled here in the window manager. * * Window finalization * ------------------- * * Saving the window configuration is an operation that is delayed by a timeout, * and therefore there's a possibility that the main window is finalized before * the timeout is reached. If this 'save timeout' is handled by the main window, * then we have to try to save the window configuration from within the finalize * method of the window itself, and it doesn't work. It's too late to query the * window at this moment. * * The window manager solves this problem elegantly, since the 'save timeout' is * taken out of the main window, and handled by the manager. The manager is sure * to be finalized before the main window, and therefore can safely query it at * this moment. */ #define SAVE_DELAY 1 // how long to wait before writing changes to disk /* * Properties */ enum { /* Reserved */ PROP_0, /* Properties */ PROP_MAIN_WINDOW, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * GObject definitions */ struct _GvMainWindowManagerPrivate { /* Properties */ GvMainWindow *main_window; /* New values, waiting to be saved */ gint new_x; gint new_y; gint new_width; gint new_height; /* Window configuration */ guint save_configuration_timeout_id; }; typedef struct _GvMainWindowManagerPrivate GvMainWindowManagerPrivate; struct _GvMainWindowManager { /* Parent instance structure */ GObject parent_instance; /* Private data */ GvMainWindowManagerPrivate *priv; }; static void gv_main_window_manager_configurable_interface_init(GvConfigurableInterface *iface); G_DEFINE_TYPE_WITH_CODE(GvMainWindowManager, gv_main_window_manager, G_TYPE_OBJECT, G_ADD_PRIVATE(GvMainWindowManager) G_IMPLEMENT_INTERFACE(GV_TYPE_CONFIGURABLE, gv_main_window_manager_configurable_interface_init)) /* * Private methods */ static void gv_main_window_manager_save_configuration_now(GvMainWindowManager *self) { GvMainWindowManagerPrivate *priv = self->priv; GSettings *settings = gv_ui_settings; gint width, height; gint x, y; TRACE("%p", self); /* Save size if changed */ g_settings_get(settings, "window-size", "(ii)", &width, &height); if ((width != priv->new_width) || (height != priv->new_height)) g_settings_set(settings, "window-size", "(ii)", priv->new_width, priv->new_height); priv->new_width = priv->new_height = 0; /* Save position if changed */ g_settings_get(settings, "window-position", "(ii)", &x, &y); if ((x != priv->new_x) || (y != priv->new_y)) g_settings_set(settings, "window-position", "(ii)", priv->new_x, priv->new_y); priv->new_x = priv->new_y = 0; } static gboolean when_timeout_save_configuration(GvMainWindowManager *self) { GvMainWindowManagerPrivate *priv = self->priv; gv_main_window_manager_save_configuration_now(self); priv->save_configuration_timeout_id = 0; return G_SOURCE_REMOVE; } static void gv_main_window_manager_save_configuration_delayed(GvMainWindowManager *self) { GvMainWindowManagerPrivate *priv = self->priv; g_clear_handle_id(&priv->save_configuration_timeout_id, g_source_remove); priv->save_configuration_timeout_id = g_timeout_add_seconds(SAVE_DELAY, (GSourceFunc) when_timeout_save_configuration, self); } /* * Window signal handlers */ static gboolean on_main_window_configure_event(GtkWindow *window, GdkEventConfigure *event, GvMainWindowManager *self) { GvMainWindowManagerPrivate *priv = self->priv; TRACE("%p, %d, %p", window, event->type, self); g_assert(event->type == GDK_CONFIGURE); /* Don't save anything when window is maximized */ if (gtk_window_is_maximized(window)) return GDK_EVENT_PROPAGATE; /* We can either use the values available in the GdkEventConfigure struct, * or use GtkWindow getters. After a few tests (KDE/X11 and GNOME/Wayland), * it turns out that the GdkEventConfigure values can't be used. For * KDE/X11, the y value has an offset. For GNOME/Wayland, both x and y are * zero, and both width and height are too big. So let's use GtkWindow * getters. */ gtk_window_get_position(window, &priv->new_x, &priv->new_y); gtk_window_get_size(window, &priv->new_width, &priv->new_height); //DEBUG("GdkEventConfigure: x=%d, y=%d, widht=%d, height=%d", // event->x, event->y, event->width, event->height); //DEBUG("GtkWindow getters: x=%d, y=%d, widht=%d, height=%d", // priv->new_x, priv->new_y, priv->new_width, priv->new_height); /* Since this signal handler is invoked multiple times during resizing, * we prefer to delay the actual save operation. */ gv_main_window_manager_save_configuration_delayed(self); return GDK_EVENT_PROPAGATE; } /* * Public functions */ GvMainWindowManager * gv_main_window_manager_new(GvMainWindow *main_window) { return g_object_new(GV_TYPE_MAIN_WINDOW_MANAGER, "main-window", main_window, NULL); } /* * Property accessors */ static void gv_main_window_manager_set_main_window(GvMainWindowManager *self, GvMainWindow *main_window) { GvMainWindowManagerPrivate *priv = self->priv; /* This is a construct-only property */ g_assert_null(priv->main_window); g_assert_nonnull(main_window); g_set_object(&priv->main_window, main_window); } static void gv_main_window_manager_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_main_window_manager_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvMainWindowManager *self = GV_MAIN_WINDOW_MANAGER(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_MAIN_WINDOW: gv_main_window_manager_set_main_window(self, g_value_get_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * GvConfigurable interface */ static void gv_main_window_manager_load_configuration(GvMainWindowManager *self) { GvMainWindowManagerPrivate *priv = self->priv; GSettings *settings = gv_ui_settings; gint width, height; gint x, y; TRACE("%p", self); /* Get settings */ g_settings_get(settings, "window-size", "(ii)", &width, &height); g_settings_get(settings, "window-position", "(ii)", &x, &y); /* Set initial window size */ if (width != -1 && height != -1) { GtkWindow *window = GTK_WINDOW(priv->main_window); DEBUG("Restoring window size (%d, %d)", width, height); gtk_window_resize(window, width, height); } else { GtkWindow *window = GTK_WINDOW(priv->main_window); gint win_height; /* * Now is the tricky part. I wish to resize the main window to its * 'natural size', so that it can accomodate the station tree view * (whatever the number of stations), without showing the scroll bar. * I don't think I'm wishing anything fancy here. * * However, it seems impossible to find a reliable way to do that. * * Querying the station tree view for its natural size RIGHT NOW is * too early. Ok, that's fine to me GTK, so when is the right moment ? * * Well, the problem is that there doesn't seem to be a right moment to * speak of. I tried delaying to the latest, that's to say connecting to * the 'map-event' signal of the station tree view, then delaying with a * 'g_idle_add()', then at last querying the tree view for its natural size. * Even at this moment, it might fail and report a size that is too small. * After that I have no other signal to connect to, I'm left with delaying * with a timeout to query AGAIN the tree view for its natural size, and * discover, o surprise, that it reports a different natural size. * * And as far as I know, there was no signal that I could have used to * get notified of this change. * * So, screw it, no more autosize, it's way too much hassle for such a * trivial result. */ gtk_window_get_size(window, NULL, &win_height); win_height += 240; DEBUG("Setting default window size (%d, %d)", 1, win_height); gtk_window_resize(window, 1, win_height); } /* Set initial window position */ if (x != -1 || y != -1) { GtkWindow *window = GTK_WINDOW(priv->main_window); DEBUG("Restoring window position (%d, %d)", x, y); gtk_window_move(window, x, y); } /* Connect to the 'configure-event' signal to save changes when the * window size or position is modified. */ g_signal_connect_object(priv->main_window, "configure-event", G_CALLBACK(on_main_window_configure_event), self, 0); } static void gv_main_window_manager_configure(GvConfigurable *configurable) { GvMainWindowManager *self = GV_MAIN_WINDOW_MANAGER(configurable); gv_main_window_manager_load_configuration(self); } static void gv_main_window_manager_configurable_interface_init(GvConfigurableInterface *iface) { iface->configure = gv_main_window_manager_configure; } /* * GObject methods */ static void gv_main_window_manager_finalize(GObject *object) { GvMainWindowManager *self = GV_MAIN_WINDOW_MANAGER(object); GvMainWindowManagerPrivate *priv = self->priv; TRACE("%p", object); /* Run any pending save operation */ if (priv->save_configuration_timeout_id > 0) when_timeout_save_configuration(self); /* Release resources */ g_object_unref(priv->main_window); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_main_window_manager, object); } static void gv_main_window_manager_constructed(GObject *object) { GvMainWindowManager *self = GV_MAIN_WINDOW_MANAGER(object); GvMainWindowManagerPrivate *priv = self->priv; TRACE("%p", self); /* Ensure construct-only properties have been set */ g_assert_nonnull(priv->main_window); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_main_window_manager, object); } static void gv_main_window_manager_init(GvMainWindowManager *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_main_window_manager_get_instance_private(self); } static void gv_main_window_manager_class_init(GvMainWindowManagerClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_main_window_manager_finalize; object_class->constructed = gv_main_window_manager_constructed; /* Properties */ object_class->get_property = gv_main_window_manager_get_property; object_class->set_property = gv_main_window_manager_set_property; properties[PROP_MAIN_WINDOW] = g_param_spec_object("main-window", "Main window", NULL, GV_TYPE_MAIN_WINDOW, GV_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties(object_class, PROP_N, properties); } goodvibes-v0.7.2/src/ui/gv-main-window-manager.h000066400000000000000000000021331414415210700215150ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include #include "ui/gv-main-window.h" /* GObject declarations */ #define GV_TYPE_MAIN_WINDOW_MANAGER gv_main_window_manager_get_type() G_DECLARE_FINAL_TYPE(GvMainWindowManager, gv_main_window_manager, GV, MAIN_WINDOW_MANAGER, GObject) /* Methods */ GvMainWindowManager *gv_main_window_manager_new(GvMainWindow *main_window); goodvibes-v0.7.2/src/ui/gv-main-window-standalone.c000066400000000000000000000250221414415210700222300ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gv-ui-enum-types.h" #include "ui/gv-ui-helpers.h" #include "ui/gv-ui-internal.h" #include "ui/gv-main-window-standalone.h" #include "ui/gv-main-window.h" #define APP_MENU_RESOURCE_PATH GV_APPLICATION_PATH "/Ui/app-menu.ui" /* * Properties */ #define DEFAULT_CLOSE_ACTION GV_MAIN_WINDOW_CLOSE_QUIT enum { /* Reserved */ PROP_0, /* Properties */ PROP_CLOSE_ACTION, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * GObject definitions */ struct _GvMainWindowStandalonePrivate { /* Properties */ GvMainWindowCloseAction close_action; /* Widgets */ GtkHeaderBar *header_bar; }; typedef struct _GvMainWindowStandalonePrivate GvMainWindowStandalonePrivate; struct _GvMainWindowStandalone { /* Parent instance structure */ GvMainWindow parent_instance; /* Private data */ GvMainWindowStandalonePrivate *priv; }; static void gv_main_window_standalone_configurable_interface_init(GvConfigurableInterface *iface); G_DEFINE_TYPE_WITH_CODE(GvMainWindowStandalone, gv_main_window_standalone, GV_TYPE_MAIN_WINDOW, G_ADD_PRIVATE(GvMainWindowStandalone) G_IMPLEMENT_INTERFACE(GV_TYPE_CONFIGURABLE, gv_main_window_standalone_configurable_interface_init)) /* * Private methods */ static void gv_main_window_standalone_update_header_bar(GvMainWindowStandalone *self, GvPlayer *player) { GvMainWindowStandalonePrivate *priv = self->priv; GtkHeaderBar *header_bar = GTK_HEADER_BAR(priv->header_bar); const gchar *default_title = g_get_application_name(); GvPlaybackState state; GvMetadata *metadata; GvStation *station; /* Here's how we set the title bar: * - not playing? -> application name * - playing? -> track name (if any) * - else fall back to station name (if any) * - else fall back to application name */ state = gv_player_get_playback_state(player); if (state == GV_PLAYBACK_STATE_STOPPED) { gtk_header_bar_set_title(header_bar, default_title); return; } metadata = gv_player_get_metadata(player); if (metadata != NULL) { gchar *title; title = gv_metadata_make_title_artist(metadata, FALSE); if (title != NULL) { gtk_header_bar_set_title(header_bar, title); g_free(title); return; } } station = gv_player_get_station(player); if (station != NULL) { const gchar *title; title = gv_station_get_name_or_uri(station); if (title) { gtk_header_bar_set_title(header_bar, title); return; } } gtk_header_bar_set_title(header_bar, default_title); } /* * Public methods */ GvMainWindow * gv_main_window_standalone_new(GApplication *application) { GvMainWindowStandalone *self; self = g_object_new(GV_TYPE_MAIN_WINDOW_STANDALONE, "application", application, NULL); return GV_MAIN_WINDOW(self); } /* * Signal handlers */ static void on_player_notify(GvPlayer *player, GParamSpec *pspec, GvMainWindowStandalone *self) { const gchar *property_name = g_param_spec_get_name(pspec); TRACE("%p, %s, %p", player, property_name, self); if (g_strcmp0(property_name, "playback-state") && g_strcmp0(property_name, "station") && g_strcmp0(property_name, "metadata")) return; gv_main_window_standalone_update_header_bar(self, player); } static gboolean on_window_delete_event(GvMainWindowStandalone *self, GdkEvent *event G_GNUC_UNUSED, gpointer data G_GNUC_UNUSED) { GvMainWindowStandalonePrivate *priv = self->priv; GvMainWindowCloseAction action = priv->close_action; switch (action) { case GV_MAIN_WINDOW_CLOSE_CLOSE: /* We just close the window (ie. hide) */ gtk_widget_hide(GTK_WIDGET(self)); return GDK_EVENT_STOP; case GV_MAIN_WINDOW_CLOSE_QUIT: default: /* We're quitting, and we do it our own way, meaning that we don't want * GTK to destroy the window, so let's stop the event propagation now. * * We don't even want to hide the window, as the window manager might * need to query the window height and position in order to save it. * Such queries would fail if the window was hidden. */ gv_core_quit(); return GDK_EVENT_STOP; } } /* * Property accessors */ GvMainWindowCloseAction gv_main_window_standalone_get_close_action(GvMainWindowStandalone *self) { GvMainWindowStandalonePrivate *priv = self->priv; return priv->close_action; } void gv_main_window_standalone_set_close_action(GvMainWindowStandalone *self, GvMainWindowCloseAction action) { GvMainWindowStandalonePrivate *priv = self->priv; if (priv->close_action == action) return; priv->close_action = action; g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_CLOSE_ACTION]); } static void gv_main_window_standalone_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvMainWindowStandalone *self = GV_MAIN_WINDOW_STANDALONE(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_CLOSE_ACTION: g_value_set_enum(value, gv_main_window_standalone_get_close_action(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_main_window_standalone_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvMainWindowStandalone *self = GV_MAIN_WINDOW_STANDALONE(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_CLOSE_ACTION: gv_main_window_standalone_set_close_action(self, g_value_get_enum(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * GvConfigurable interface */ static void gv_main_window_standalone_configure(GvConfigurable *configurable) { GvMainWindowStandalone *self = GV_MAIN_WINDOW_STANDALONE(configurable); TRACE("%p", self); /* Configure parent. Kind of hacky to do that explicitly here, * but there's nothing in place for that to happen automatically, * and this is the only place where we need it, so live with that. * Eventually this will go away, when gv-main-window-status-icon * is retired, and then gv-main-window-standalone can be merged * into gv-main-window. * Note that calling gv_configurable_configure() on the parent * instance doesn't help, as it just calls this very method, * and we're caught in an endless loop. */ GvMainWindow *parent = &(self->parent_instance); gv_main_window_configure(parent); g_assert(gv_ui_settings); g_settings_bind(gv_ui_settings, "close-action", self, "close-action", G_SETTINGS_BIND_DEFAULT); } static void gv_main_window_standalone_configurable_interface_init(GvConfigurableInterface *iface) { iface->configure = gv_main_window_standalone_configure; } /* * GObject methods */ static GtkWidget * make_menu_button(void) { GtkBuilder *builder; GMenuModel *menu_model; GtkMenuButton *menu_button; builder = gtk_builder_new_from_resource(APP_MENU_RESOURCE_PATH); menu_model = G_MENU_MODEL(gtk_builder_get_object(builder, "app-menu")); menu_button = GTK_MENU_BUTTON(gtk_menu_button_new()); gtk_menu_button_set_direction(menu_button, GTK_ARROW_NONE); gtk_menu_button_set_menu_model(menu_button, menu_model); g_object_unref(builder); return GTK_WIDGET(menu_button); } static void gv_main_window_standalone_setup_header_bar(GvMainWindowStandalone *self) { GvMainWindowStandalonePrivate *priv = self->priv; GtkHeaderBar *header_bar; GtkWidget *menu_button; g_assert_null(priv->header_bar); header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_header_bar_set_title(header_bar, g_get_application_name()); menu_button = make_menu_button(); gtk_header_bar_pack_end(header_bar, menu_button); gtk_window_set_titlebar(GTK_WINDOW(self), GTK_WIDGET(header_bar)); gtk_widget_show_all(GTK_WIDGET(header_bar)); priv->header_bar = header_bar; } static void gv_main_window_standalone_finalize(GObject *object) { TRACE("%p", object); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_main_window_standalone, object); } static void gv_main_window_standalone_constructed(GObject *object) { GvMainWindowStandalone *self = GV_MAIN_WINDOW_STANDALONE(object); GvPlayer *player = gv_core_player; TRACE("%p", self); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_main_window_standalone, object); /* Create the header bar */ gv_main_window_standalone_setup_header_bar(self); /* Connect main window signal handlers */ g_signal_connect_object(self, "delete-event", G_CALLBACK(on_window_delete_event), NULL, 0); /* Connect core signal handlers */ g_signal_connect_object(player, "notify", G_CALLBACK(on_player_notify), self, 0); } static void gv_main_window_standalone_init(GvMainWindowStandalone *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_main_window_standalone_get_instance_private(self); } static void gv_main_window_standalone_class_init(GvMainWindowStandaloneClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_main_window_standalone_finalize; object_class->constructed = gv_main_window_standalone_constructed; /* Properties */ object_class->get_property = gv_main_window_standalone_get_property; object_class->set_property = gv_main_window_standalone_set_property; properties[PROP_CLOSE_ACTION] = g_param_spec_enum("close-action", "Close Action", NULL, GV_TYPE_MAIN_WINDOW_CLOSE_ACTION, DEFAULT_CLOSE_ACTION, GV_PARAM_READWRITE); g_object_class_install_properties(object_class, PROP_N, properties); /* Register transform function */ g_value_register_transform_func(GV_TYPE_MAIN_WINDOW_CLOSE_ACTION, G_TYPE_STRING, gv_value_transform_enum_string); g_value_register_transform_func(G_TYPE_STRING, GV_TYPE_MAIN_WINDOW_CLOSE_ACTION, gv_value_transform_string_enum); } goodvibes-v0.7.2/src/ui/gv-main-window-standalone.h000066400000000000000000000027641414415210700222450ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "ui/gv-main-window.h" /* GObject declarations */ #define GV_TYPE_MAIN_WINDOW_STANDALONE gv_main_window_standalone_get_type() G_DECLARE_FINAL_TYPE(GvMainWindowStandalone, gv_main_window_standalone, GV, MAIN_WINDOW_STANDALONE, GvMainWindow) /* Data types */ typedef enum { GV_MAIN_WINDOW_CLOSE_QUIT, GV_MAIN_WINDOW_CLOSE_CLOSE, } GvMainWindowCloseAction; /* Property accessors */ GvMainWindowCloseAction gv_main_window_standalone_get_close_action(GvMainWindowStandalone *self); void gv_main_window_standalone_set_close_action(GvMainWindowStandalone *self, GvMainWindowCloseAction action); /* Methods */ GvMainWindow *gv_main_window_standalone_new(GApplication *application); goodvibes-v0.7.2/src/ui/gv-main-window-status-icon.c000066400000000000000000000311171414415210700223530ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gv-ui-helpers.h" #if 0 #include "ui/gv-stations-tree-view.h" #endif #include "ui/gv-main-window-status-icon.h" #include "ui/gv-main-window.h" /* * GObject definitions */ struct _GvMainWindowStatusIconPrivate { GtkWidget *stations_tree_view; }; typedef struct _GvMainWindowStatusIconPrivate GvMainWindowStatusIconPrivate; struct _GvMainWindowStatusIcon { /* Parent instance structure */ GvMainWindow parent_instance; /* Private data */ GvMainWindowStatusIconPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvMainWindowStatusIcon, gv_main_window_status_icon, GV_TYPE_MAIN_WINDOW) /* * Helpers */ /* https://stackoverflow.com/a/23497087 */ static GtkWidget * find_child(GtkWidget *parent, const gchar *name) { const gchar *widget_name; widget_name = gtk_widget_get_name(parent); if (g_strcmp0(widget_name, name) == 0) { return parent; } if (GTK_IS_BIN(parent)) { GtkWidget *child; child = gtk_bin_get_child(GTK_BIN(parent)); if (child) return find_child(child, name); else return NULL; } if (GTK_IS_CONTAINER(parent)) { GList *children, *child; GtkWidget *found = NULL; children = gtk_container_get_children(GTK_CONTAINER(parent)); for (child = children; child != NULL; child = g_list_next(child)) { GtkWidget *w; w = find_child(child->data, name); if (w != NULL) { found = w; break; } } g_list_free(children); if (found != NULL) return found; } return NULL; } /* * All the mess to automatically resize the window */ static gint get_screen_max_height(GtkWindow *self_window) { gint max_height; #if GTK_CHECK_VERSION(3, 22, 0) GdkDisplay *display; GdkWindow *gdk_window; GdkMonitor *monitor; GdkRectangle geometry; display = gdk_display_get_default(); gdk_window = gtk_widget_get_window(GTK_WIDGET(self_window)); if (gdk_window) { monitor = gdk_display_get_monitor_at_window(display, gdk_window); } else { /* In status icon mode, the window might not be realized, * and in any case the window is tied to the panel which * lives on the primary monitor. */ monitor = gdk_display_get_primary_monitor(display); } gdk_monitor_get_workarea(monitor, &geometry); max_height = geometry.height; #else GdkScreen *screen; screen = gdk_screen_get_default(); max_height = gdk_screen_get_height(screen); #endif return max_height; } static gint compute_natural_height(GtkWindow *window, GtkWidget *tree_view) { GtkAllocation allocated; GtkRequisition natural; gint width, height, diff, natural_height; gint min_height; gint max_height; min_height = 1; max_height = get_screen_max_height(window); /* * Here comes a hacky piece of code! * * Problem: from the moment the station tree view is within a scrolled * window, the height is not handled smartly by GTK anymore. By default, * it's ridiculously small. Then, when the number of rows in the tree view * is changed, the tree view is not resized. So if we want a smart height, * we have to do it manually. * * The success (or failure) of this function highly depends on the moment * it's called. * - too early, get_preferred_size() calls return junk. * - in some signal handlers, get_preferred_size() calls return junk. * * Plus, we resize the window here, is the context safe to do that? * * For these reasons, it's safer to never call this function directly, * but instead always delay the call for an idle moment. */ /* Determine if the tree view is at its natural height */ gtk_widget_get_allocation(tree_view, &allocated); gtk_widget_get_preferred_size(tree_view, NULL, &natural); //DEBUG("allocated height: %d", allocated.height); //DEBUG("natural height: %d", natural.height); diff = natural.height - allocated.height; /* Determine what should be the new height */ gtk_window_get_size(window, &width, &height); natural_height = height + diff; if (natural_height < min_height) { DEBUG("Clamping natural height %d to minimum height %d", natural_height, min_height); natural_height = min_height; } if (natural_height > max_height) { DEBUG("Clamping natural height %d to maximum height %d", natural_height, max_height); natural_height = max_height; } return natural_height; } static void gv_main_window_status_icon_resize(GvMainWindowStatusIcon *self) { GvMainWindowStatusIconPrivate *priv = self->priv; GtkWindow *window = GTK_WINDOW(self); gint new_height; gint height; gint width; new_height = compute_natural_height(window, priv->stations_tree_view); gtk_window_get_size(window, &width, &height); DEBUG("Resizing window height: %d -> %d", height, new_height); gtk_window_resize(window, width, new_height); } static gboolean when_idle_resize_window(GvMainWindowStatusIcon *self) { gv_main_window_status_icon_resize(self); return G_SOURCE_REMOVE; } static void on_stations_tree_view_populated(GtkWidget *stations_tree_view G_GNUC_UNUSED, GvMainWindowStatusIcon *self) { /* If the content of the stations tree view was modified, the natural size * changed also. However it's too early to compute the new size now. */ g_idle_add((GSourceFunc) when_idle_resize_window, self); } static void on_stations_tree_view_realize(GtkWidget *stations_tree_view G_GNUC_UNUSED, GvMainWindowStatusIcon *self) { /* When the treeview is realized, we need to check AGAIN if the natural * height we have is correct. */ g_idle_add((GSourceFunc) when_idle_resize_window, self); } static gboolean on_stations_tree_view_map_event(GtkWidget *stations_tree_view G_GNUC_UNUSED, GdkEvent *event G_GNUC_UNUSED, GvMainWindowStatusIcon *self) { /* When the treeview is mapped, we need to check AGAIN if the natural * height we have is correct. */ g_idle_add((GSourceFunc) when_idle_resize_window, self); return GDK_EVENT_PROPAGATE; } /* * Public methods */ GvMainWindow * gv_main_window_status_icon_new(GApplication *application) { GvMainWindowStatusIcon *self; self = g_object_new(GV_TYPE_MAIN_WINDOW_STATUS_ICON, "application", application, NULL); return GV_MAIN_WINDOW(self); } /* * Signal handlers */ #define CLOSE_WINDOW_ON_FOCUS_OUT TRUE static gboolean on_focus_change(GtkWindow *window, GdkEventFocus *focus_event, gpointer data G_GNUC_UNUSED) { g_assert(focus_event->type == GDK_FOCUS_CHANGE); DEBUG("Main window %s focus", focus_event->in ? "gained" : "lost"); if (focus_event->in) return GDK_EVENT_PROPAGATE; if (CLOSE_WINDOW_ON_FOCUS_OUT == FALSE) return GDK_EVENT_PROPAGATE; #if 0 /* This code does not seem to be needed, as the call to * gtk_window_close() below has no effect anyway when the * context menu is on. */ GvMainWindowStatusIcon *self = GV_MAIN_WINDOW_STATUS_ICON(window); GvMainWindowStatusIconPrivate *priv = self->priv; GvStationsTreeView *stations_tree_view = GV_STATIONS_TREE_VIEW(priv->stations_tree_view); if (gv_stations_tree_view_has_context_menu(stations_tree_view)) return GDK_EVENT_PROPAGATE; #endif DEBUG("Closing window"); gtk_window_close(window); return GDK_EVENT_PROPAGATE; } static gboolean on_key_press_event(GtkWindow *window, GdkEventKey *event, gpointer data G_GNUC_UNUSED) { g_assert(event->type == GDK_KEY_PRESS); /* When is pressed, close the window */ if (event->keyval == GDK_KEY_Escape) gtk_window_close(window); return GDK_EVENT_PROPAGATE; } static gboolean on_window_delete_event(GtkWindow *self, GdkEvent *event G_GNUC_UNUSED, gpointer data G_GNUC_UNUSED) { /* On delete event, we just close the window (ie. hide) */ gtk_widget_hide(GTK_WIDGET(self)); return GDK_EVENT_STOP; } /* * GObject methods */ static void gv_main_window_status_icon_setup(GvMainWindowStatusIcon *self) { GtkApplicationWindow *application_window = GTK_APPLICATION_WINDOW(self); GtkWindow *window = GTK_WINDOW(self); /* Basically, we want the window to appear and behave as a popup window */ /* Hide the menu bar in the main window */ gtk_application_window_set_show_menubar(application_window, FALSE); /* Window appearance */ gtk_window_set_decorated(window, FALSE); gtk_window_set_position(window, GTK_WIN_POS_MOUSE); /* We don't want the window to appear in pager or taskbar. * This has an undesired effect though: the window may not * have the focus when it's shown by the window manager. * But read on... */ gtk_window_set_skip_pager_hint(window, TRUE); gtk_window_set_skip_taskbar_hint(window, TRUE); /* Setting the window modal seems to ensure that the window * receives focus when shown by the window manager. */ gtk_window_set_modal(window, TRUE); /* Handle keyboard focus changes, so that we can hide the * window on 'focus-out-event'. */ g_signal_connect_object(window, "focus-in-event", G_CALLBACK(on_focus_change), NULL, 0); g_signal_connect_object(window, "focus-out-event", G_CALLBACK(on_focus_change), NULL, 0); /* Catch the keystroke to close the window */ g_signal_connect_object(window, "key-press-event", G_CALLBACK(on_key_press_event), NULL, 0); /* Don't quit when the window is closed, just hide */ g_signal_connect_object(window, "delete-event", G_CALLBACK(on_window_delete_event), NULL, 0); } static void gv_main_window_status_icon_setup_autosize(GvMainWindowStatusIcon *self) { GvMainWindowStatusIconPrivate *priv = self->priv; GtkWidget *stations_tree_view; GtkWidget *go_next_button; GtkWidget *station_name_label; /* Get the stations tree view, and connect all the signal handlers so * that we can resize the window vertically whenever the number of * stations change. This is also what sets the initial vertical size * of the window. */ stations_tree_view = find_child(GTK_WIDGET(self), "stations_tree_view"); g_assert_nonnull(stations_tree_view); g_signal_connect_object(stations_tree_view, "populated", G_CALLBACK(on_stations_tree_view_populated), self, 0); g_signal_connect_object(stations_tree_view, "realize", G_CALLBACK(on_stations_tree_view_realize), self, 0); g_signal_connect_object(stations_tree_view, "map-event", G_CALLBACK(on_stations_tree_view_map_event), self, 0); priv->stations_tree_view = stations_tree_view; /* Get the next button and hide it, no station view in status icon mode, * as it's too difficult too get a useful width, without impacting * the width of the playlist view (tried to set width-request on map * and unmap of the station-view, didn't work) */ go_next_button = find_child(GTK_WIDGET(self), "go_next_button"); g_assert_nonnull(go_next_button); gtk_widget_set_visible(go_next_button, FALSE); /* Get the station label, make sure it does not wrap. Let it define the * minimun width for the window, if ever it's longer than the control bar. */ station_name_label = find_child(GTK_WIDGET(self), "station_name_label"); g_assert_nonnull(station_name_label); gtk_label_set_line_wrap(GTK_LABEL(station_name_label), FALSE); gtk_label_set_ellipsize(GTK_LABEL(station_name_label), PANGO_ELLIPSIZE_NONE); } static void gv_main_window_status_icon_finalize(GObject *object) { TRACE("%p", object); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_main_window_status_icon, object); } static void gv_main_window_status_icon_constructed(GObject *object) { GvMainWindowStatusIcon *self = GV_MAIN_WINDOW_STATUS_ICON(object); TRACE("%p", self); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_main_window_status_icon, object); gv_main_window_status_icon_setup(self); gv_main_window_status_icon_setup_autosize(self); } static void gv_main_window_status_icon_init(GvMainWindowStatusIcon *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_main_window_status_icon_get_instance_private(self); } static void gv_main_window_status_icon_class_init(GvMainWindowStatusIconClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_main_window_status_icon_finalize; object_class->constructed = gv_main_window_status_icon_constructed; } goodvibes-v0.7.2/src/ui/gv-main-window-status-icon.h000066400000000000000000000022351414415210700223570ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include "ui/gv-main-window.h" /* GObject declarations */ #define GV_TYPE_MAIN_WINDOW_STATUS_ICON gv_main_window_status_icon_get_type() G_DECLARE_FINAL_TYPE(GvMainWindowStatusIcon, gv_main_window_status_icon, GV, MAIN_WINDOW_STATUS_ICON, GvMainWindow) /* Property accessors */ // gint gv_main_window_get_natural_height (GvMainWindow *self); /* Methods */ GvMainWindow *gv_main_window_status_icon_new(GApplication *application); goodvibes-v0.7.2/src/ui/gv-main-window.c000066400000000000000000000277161414415210700201160ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gv-playlist-view.h" #include "ui/gv-station-view.h" #include "ui/gv-ui-enum-types.h" #include "ui/gv-ui-helpers.h" #include "ui/gv-ui-internal.h" #include "ui/gv-main-window.h" #define USER_CSS_FILENAME "style.css" /* * Properties */ #define DEFAULT_THEME_VARIANT GV_MAIN_WINDOW_THEME_DEFAULT enum { /* Reserved */ PROP_0, /* Properties */ PROP_THEME_VARIANT, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * GObject definitions */ struct _GvMainWindowPrivate { /* Properties */ GvMainWindowThemeVariant theme_variant; /* Widgets */ GtkWidget *stack; GtkWidget *playlist_view; GtkWidget *station_view; /* Internal */ gboolean system_prefer_dark_theme; }; typedef struct _GvMainWindowPrivate GvMainWindowPrivate; static void gv_main_window_configurable_interface_init(GvConfigurableInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE(GvMainWindow, gv_main_window, GTK_TYPE_APPLICATION_WINDOW, G_ADD_PRIVATE(GvMainWindow) G_IMPLEMENT_INTERFACE(GV_TYPE_CONFIGURABLE, gv_main_window_configurable_interface_init)) /* * Core Player signal handlers */ static GtkWidget * make_security_exception_dialog(GtkWindow *parent, GvStation *station, const gchar *error, const gchar *debug) { GtkWidget *dialog, *message_area, *grid, *label; const gchar *ptr; /* Create the dialog */ dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("Add a security exception?")); gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG(dialog), _("An error happened while trying to play %s."), gv_station_get_name_or_uri(station)); gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), GTK_RESPONSE_CANCEL); gtk_dialog_add_button(GTK_DIALOG(dialog), _("Add"), GTK_RESPONSE_ACCEPT); /* Append a separator */ message_area = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog)); gtk_container_add(GTK_CONTAINER(message_area), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)); /* Then comes the error details */ grid = gtk_grid_new(); g_object_set(grid, "row-spacing", GV_UI_ELEM_SPACING, "column-spacing", GV_UI_COLUMN_SPACING, NULL); label = gtk_label_new(_("URL")); gtk_label_set_xalign(GTK_LABEL(label), 1); gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1); label = gtk_label_new(gv_station_get_uri(station)); gtk_label_set_selectable(GTK_LABEL(label), TRUE); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_grid_attach(GTK_GRID(grid), label, 1, 0, 1, 1); label = gtk_label_new(_("Error")); gtk_label_set_xalign(GTK_LABEL(label), 1); gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1); label = gtk_label_new(error); gtk_label_set_selectable(GTK_LABEL(label), TRUE); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_grid_attach(GTK_GRID(grid), label, 1, 1, 1, 1); /* For the debug details, we drop the first line, which is about * where the error happened (function, line number), and we don't * want to show it. However we want to show the rest. */ ptr = strchr(debug, '\n'); if (ptr) while (*ptr == '\n') ptr++; if (ptr == NULL || *ptr == '\0') ptr = debug; label = gtk_label_new(_("Details")); gtk_label_set_xalign(GTK_LABEL(label), 1); gtk_label_set_yalign(GTK_LABEL(label), 0); gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1); label = gtk_label_new(ptr); gtk_label_set_selectable(GTK_LABEL(label), TRUE); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); gtk_label_set_max_width_chars(GTK_LABEL(label), 60); gtk_grid_attach(GTK_GRID(grid), label, 1, 2, 1, 1); /* Pack it */ gtk_container_add(GTK_CONTAINER(message_area), grid); gtk_widget_show_all(message_area); return dialog; } static void on_player_ssl_failure(GvPlayer *player, const gchar *error, const gchar *debug, GvMainWindow *self) { GvStation *station; GtkWidget *dialog; int response; /* Get the station */ station = gv_player_get_station(player); if (station == NULL) return; /* Create and run the dialog */ dialog = make_security_exception_dialog(GTK_WINDOW(self), station, error, debug); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); /* Handle response */ if (response == GTK_RESPONSE_ACCEPT) { gv_station_set_insecure(station, TRUE); gv_player_play(player); } else { gv_player_stop(player); } } /* * Widget signal handlers */ static void on_go_next_button_clicked(GvPlaylistView *playlist_view G_GNUC_UNUSED, GvMainWindow *self) { GvMainWindowPrivate *priv = gv_main_window_get_instance_private(self); GtkStack *stack = GTK_STACK(priv->stack); gtk_stack_set_visible_child(stack, priv->station_view); } static void on_go_back_button_clicked(GvStationView *station_view G_GNUC_UNUSED, GvMainWindow *self) { GvMainWindowPrivate *priv = gv_main_window_get_instance_private(self); GtkStack *stack = GTK_STACK(priv->stack); gtk_stack_set_visible_child(stack, priv->playlist_view); } /* * Property accessors */ GvMainWindowThemeVariant gv_main_window_get_theme_variant(GvMainWindow *self) { GvMainWindowPrivate *priv = gv_main_window_get_instance_private(self); return priv->theme_variant; } void gv_main_window_set_theme_variant(GvMainWindow *self, GvMainWindowThemeVariant variant) { GvMainWindowPrivate *priv = gv_main_window_get_instance_private(self); gboolean prefer_dark_theme; if (priv->theme_variant == variant) return; priv->theme_variant = variant; switch (variant) { case GV_MAIN_WINDOW_THEME_DARK: prefer_dark_theme = TRUE; break; case GV_MAIN_WINDOW_THEME_LIGHT: prefer_dark_theme = FALSE; break; case GV_MAIN_WINDOW_THEME_DEFAULT: default: prefer_dark_theme = priv->system_prefer_dark_theme; break; } g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", prefer_dark_theme, NULL); g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_THEME_VARIANT]); } static void gv_main_window_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvMainWindow *self = GV_MAIN_WINDOW(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_THEME_VARIANT: g_value_set_enum(value, gv_main_window_get_theme_variant(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_main_window_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvMainWindow *self = GV_MAIN_WINDOW(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_THEME_VARIANT: gv_main_window_set_theme_variant(self, g_value_get_enum(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * GvConfigurable interface */ static void _gv_main_window_configure(GvConfigurable *configurable) { GvMainWindow *self = GV_MAIN_WINDOW(configurable); GvMainWindowPrivate *priv = gv_main_window_get_instance_private(self); TRACE("%p", self); /* We want to save the value of 'prefer-dark-theme' from GtkSettings * right now, as we might modify it later, and need a way to go back * to its default value. */ g_object_get(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", &priv->system_prefer_dark_theme, NULL); g_assert(gv_ui_settings); g_settings_bind(gv_ui_settings, "theme-variant", self, "theme-variant", G_SETTINGS_BIND_DEFAULT); } void gv_main_window_configure(GvMainWindow *self) { _gv_main_window_configure(GV_CONFIGURABLE(self)); } static void gv_main_window_configurable_interface_init(GvConfigurableInterface *iface) { iface->configure = _gv_main_window_configure; } /* * GObject methods */ static void gv_main_window_populate_widgets(GvMainWindow *self) { GvMainWindowPrivate *priv = gv_main_window_get_instance_private(self); /* Create the views */ priv->playlist_view = gv_playlist_view_new(); priv->station_view = gv_station_view_new(); /* Create a stack and populate it */ priv->stack = gtk_stack_new(); gtk_stack_set_transition_type(GTK_STACK(priv->stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT); gtk_stack_add_named(GTK_STACK(priv->stack), priv->playlist_view, "playlist-view"); gtk_stack_add_named(GTK_STACK(priv->stack), priv->station_view, "station-view"); /* Add stack to the window */ gtk_container_add(GTK_CONTAINER(self), priv->stack); gtk_widget_show_all(priv->stack); } static void gv_main_window_setup_widgets(GvMainWindow *self) { GvMainWindowPrivate *priv = gv_main_window_get_instance_private(self); /* Connect buttons to switch between stack children */ g_signal_connect_object(priv->playlist_view, "go-next-clicked", G_CALLBACK(on_go_next_button_clicked), self, 0); g_signal_connect_object(priv->station_view, "go-back-clicked", G_CALLBACK(on_go_back_button_clicked), self, 0); } static void gv_main_window_setup_css(GvMainWindow *self G_GNUC_UNUSED) { GtkCssProvider *provider; const gchar *user_dir; gchar *css_path; user_dir = gv_get_app_user_data_dir(); css_path = g_build_filename(user_dir, USER_CSS_FILENAME, NULL); if (g_file_test(css_path, G_FILE_TEST_EXISTS) == FALSE) { g_free(css_path); return; } INFO("Loading css from file '%s'", css_path); provider = gtk_css_provider_new(); gtk_css_provider_load_from_path(provider, css_path, NULL); gtk_style_context_add_provider_for_screen( gdk_screen_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER); g_object_unref(provider); g_free(css_path); } static void gv_main_window_finalize(GObject *object) { TRACE("%p", object); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_main_window, object); } static void gv_main_window_constructed(GObject *object) { GvMainWindow *self = GV_MAIN_WINDOW(object); GvPlayer *player = gv_core_player; TRACE("%p", self); /* Build window */ gv_main_window_populate_widgets(self); gv_main_window_setup_widgets(self); gv_main_window_setup_css(self); g_signal_connect_object(player, "ssl-failure", G_CALLBACK(on_player_ssl_failure), self, 0); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_main_window, object); } static void gv_main_window_init(GvMainWindow *self) { TRACE("%p", self); } static void gv_main_window_class_init(GvMainWindowClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_main_window_finalize; object_class->constructed = gv_main_window_constructed; /* Properties */ object_class->get_property = gv_main_window_get_property; object_class->set_property = gv_main_window_set_property; properties[PROP_THEME_VARIANT] = g_param_spec_enum("theme-variant", "Theme variant", NULL, GV_TYPE_MAIN_WINDOW_THEME_VARIANT, DEFAULT_THEME_VARIANT, GV_PARAM_READWRITE); g_object_class_install_properties(object_class, PROP_N, properties); /* Register transform function */ g_value_register_transform_func(GV_TYPE_MAIN_WINDOW_THEME_VARIANT, G_TYPE_STRING, gv_value_transform_enum_string); g_value_register_transform_func(G_TYPE_STRING, GV_TYPE_MAIN_WINDOW_THEME_VARIANT, gv_value_transform_string_enum); } goodvibes-v0.7.2/src/ui/gv-main-window.h000066400000000000000000000030111414415210700201010ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_MAIN_WINDOW gv_main_window_get_type() G_DECLARE_DERIVABLE_TYPE(GvMainWindow, gv_main_window, GV, MAIN_WINDOW, GtkApplicationWindow) struct _GvMainWindowClass { /* Parent class */ GtkApplicationWindowClass parent_class; }; /* Data types */ typedef enum { GV_MAIN_WINDOW_THEME_DEFAULT, GV_MAIN_WINDOW_THEME_DARK, GV_MAIN_WINDOW_THEME_LIGHT, } GvMainWindowThemeVariant; /* Methods */ /* Exposed so that gv-main-widow-standalone can call it */ void gv_main_window_configure(GvMainWindow *self); /* Property accessors */ GvMainWindowThemeVariant gv_main_window_get_theme_variant(GvMainWindow *self); void gv_main_window_set_theme_variant(GvMainWindow *self, GvMainWindowThemeVariant variant); goodvibes-v0.7.2/src/ui/gv-playlist-view.c000066400000000000000000000320101414415210700204550ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gtk-additions.h" #include "ui/gv-stations-tree-view.h" #include "ui/gv-ui-internal.h" #include "ui/gv-playlist-view.h" #define UI_RESOURCE_PATH GV_APPLICATION_PATH "/Ui/playlist-view.glade" /* * Signal */ enum { SIGNAL_GO_NEXT_CLICKED, /* Number of signals */ SIGNAL_N }; static guint signals[SIGNAL_N]; /* * GObject definitions */ struct _GvPlaylistViewPrivate { /* * Widgets */ /* Top-level */ GtkWidget *playlist_view_box; /* Current station */ GtkWidget *station_grid; GtkWidget *station_name_label; GtkWidget *playback_status_label; GtkWidget *go_next_button; /* Controls */ GtkWidget *play_button; GtkWidget *prev_button; GtkWidget *next_button; GtkWidget *repeat_toggle_button; GtkWidget *shuffle_toggle_button; GtkWidget *volume_button; /* Station list */ GtkWidget *scrolled_window; GtkWidget *stations_tree_view; /* * Internal */ GBinding *repeat_binding; GBinding *shuffle_binding; GBinding *volume_binding; }; typedef struct _GvPlaylistViewPrivate GvPlaylistViewPrivate; struct _GvPlaylistView { /* Parent instance structure */ GtkApplicationWindow parent_instance; /* Private data */ GvPlaylistViewPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvPlaylistView, gv_playlist_view, GTK_TYPE_BOX) /* * Core Player signal handlers */ static void set_station_name_label(GtkLabel *label, GvStation *station) { const gchar *station_title; if (station) station_title = gv_station_get_name_or_uri(station); else station_title = _("No station selected"); gtk_label_set_text(label, station_title); } static void set_playback_status_label(GtkLabel *label, GvPlaybackState state, GvMetadata *metadata) { const gchar *playback_state_str; playback_state_str = gv_playback_state_to_string(state); if (state != GV_PLAYBACK_STATE_PLAYING) { gtk_label_set_text(label, playback_state_str); return; } if (metadata == NULL) { gtk_label_set_text(label, playback_state_str); } else { gchar *artist_title; gchar *album_year; gchar *str; artist_title = gv_metadata_make_title_artist(metadata, FALSE); album_year = gv_metadata_make_album_year(metadata, FALSE); if (artist_title && album_year) str = g_strdup_printf("%s/n%s", artist_title, album_year); else if (artist_title) str = g_strdup(artist_title); else str = g_strdup(playback_state_str); gtk_label_set_text(label, str); g_free(str); g_free(album_year); g_free(artist_title); } } static void set_play_button(GtkButton *button, GvPlaybackState state) { GtkWidget *image; const gchar *icon_name; if (state == GV_PLAYBACK_STATE_STOPPED) icon_name = "media-playback-start-symbolic"; else icon_name = "media-playback-stop-symbolic"; image = gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_BUTTON); gtk_button_set_image(button, image); } static void set_volume_button(GtkVolumeButton *volume_button, guint volume, gboolean mute) { GtkScaleButton *scale_button = GTK_SCALE_BUTTON(volume_button); if (mute) gtk_scale_button_set_value(scale_button, 0); else gtk_scale_button_set_value(scale_button, volume); } /* * Private methods */ static void gv_playlist_view_update_station_name_label(GvPlaylistView *self, GvPlayer *player) { GvPlaylistViewPrivate *priv = self->priv; GtkLabel *label = GTK_LABEL(priv->station_name_label); GvStation *station = gv_player_get_station(player); set_station_name_label(label, station); } static void gv_playlist_view_update_playback_status_label(GvPlaylistView *self, GvPlayer *player) { GvPlaylistViewPrivate *priv = self->priv; GtkLabel *label = GTK_LABEL(priv->playback_status_label); GvPlaybackState state = gv_player_get_playback_state(player); GvMetadata *metadata = gv_player_get_metadata(player); set_playback_status_label(label, state, metadata); } static void gv_playlist_view_update_play_button(GvPlaylistView *self, GvPlayer *player) { GvPlaylistViewPrivate *priv = self->priv; GtkButton *button = GTK_BUTTON(priv->play_button); GvPlaybackState state = gv_player_get_playback_state(player); set_play_button(button, state); } static void gv_playlist_view_update_volume_button(GvPlaylistView *self, GvPlayer *player) { GvPlaylistViewPrivate *priv = self->priv; GtkVolumeButton *volume_button = GTK_VOLUME_BUTTON(priv->volume_button); guint volume = gv_player_get_volume(player); gboolean mute = gv_player_get_mute(player); g_binding_unbind(priv->volume_binding); set_volume_button(volume_button, volume, mute); priv->volume_binding = g_object_bind_property( player, "volume", volume_button, "value", G_BINDING_BIDIRECTIONAL); } /* * Signal handlers */ static void on_player_notify(GvPlayer *player, GParamSpec *pspec, GvPlaylistView *self) { const gchar *property_name = g_param_spec_get_name(pspec); TRACE("%p, %s, %p", player, property_name, self); if (!g_strcmp0(property_name, "station")) { gv_playlist_view_update_station_name_label(self, player); } else if (!g_strcmp0(property_name, "playback-state")) { gv_playlist_view_update_playback_status_label(self, player); gv_playlist_view_update_play_button(self, player); } else if (!g_strcmp0(property_name, "metadata")) { gv_playlist_view_update_playback_status_label(self, player); } else if (!g_strcmp0(property_name, "mute")) { gv_playlist_view_update_volume_button(self, player); } } static void on_control_button_clicked(GtkButton *button, GvPlaylistView *self) { GvPlaylistViewPrivate *priv = self->priv; GvPlayer *player = gv_core_player; GtkWidget *widget = GTK_WIDGET(button); if (widget == priv->play_button) { gv_player_toggle(player); } else if (widget == priv->prev_button) { gv_player_prev(player); } else if (widget == priv->next_button) { gv_player_next(player); } else if (widget == priv->go_next_button) { CRITICAL("Unhandled button %p", button); } } static void on_go_next_button_clicked(GtkButton *button G_GNUC_UNUSED, GvPlaylistView *self) { g_signal_emit(self, signals[SIGNAL_GO_NEXT_CLICKED], 0); } static void on_map(GvPlaylistView *self, gpointer user_data) { GvPlaylistViewPrivate *priv = self->priv; GvPlayer *player = gv_core_player; TRACE("%p, %p", self, user_data); g_signal_connect_object(player, "notify", G_CALLBACK(on_player_notify), self, 0); /* Order matters, don't mix up source and target here */ priv->repeat_binding = g_object_bind_property( player, "repeat", priv->repeat_toggle_button, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); priv->shuffle_binding = g_object_bind_property( player, "shuffle", priv->shuffle_toggle_button, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); priv->volume_binding = g_object_bind_property( player, "volume", priv->volume_button, "value", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); gv_playlist_view_update_station_name_label(self, player); gv_playlist_view_update_playback_status_label(self, player); gv_playlist_view_update_play_button(self, player); gv_playlist_view_update_volume_button(self, player); } static void on_unmap(GvPlaylistView *self, gpointer user_data G_GNUC_UNUSED) { GvPlaylistViewPrivate *priv = self->priv; GvPlayer *player = gv_core_player; TRACE("%p, %p", self, user_data); g_signal_handlers_disconnect_by_data(player, self); g_binding_unbind(priv->repeat_binding); priv->repeat_binding = NULL; g_binding_unbind(priv->shuffle_binding); priv->shuffle_binding = NULL; g_binding_unbind(priv->volume_binding); priv->volume_binding = NULL; } /* * Public methods */ GtkWidget * gv_playlist_view_new(void) { return g_object_new(GV_TYPE_PLAYLIST_VIEW, NULL); } /* * Construct helpers */ static void setup_adjustment(GtkScaleButton *scale_button, GObject *obj, const gchar *obj_prop) { GtkAdjustment *adjustment; guint minimum, maximum; guint range; g_object_get_property_uint_bounds(obj, obj_prop, &minimum, &maximum); range = maximum - minimum; adjustment = gtk_scale_button_get_adjustment(scale_button); gtk_adjustment_set_lower(adjustment, minimum); gtk_adjustment_set_upper(adjustment, maximum); gtk_adjustment_set_step_increment(adjustment, range / 100); gtk_adjustment_set_page_increment(adjustment, range / 10); } static void gv_playlist_view_populate_widgets(GvPlaylistView *self) { GvPlaylistViewPrivate *priv = self->priv; GtkBuilder *builder; /* Build the ui */ builder = gtk_builder_new_from_resource(UI_RESOURCE_PATH); /* Save widget pointers */ /* Top-level */ GTK_BUILDER_SAVE_WIDGET(builder, priv, playlist_view_box); /* Current status */ GTK_BUILDER_SAVE_WIDGET(builder, priv, station_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, station_name_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, playback_status_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, go_next_button); /* Button box */ GTK_BUILDER_SAVE_WIDGET(builder, priv, play_button); GTK_BUILDER_SAVE_WIDGET(builder, priv, prev_button); GTK_BUILDER_SAVE_WIDGET(builder, priv, next_button); GTK_BUILDER_SAVE_WIDGET(builder, priv, repeat_toggle_button); GTK_BUILDER_SAVE_WIDGET(builder, priv, shuffle_toggle_button); GTK_BUILDER_SAVE_WIDGET(builder, priv, volume_button); /* Stations tree view */ GTK_BUILDER_SAVE_WIDGET(builder, priv, scrolled_window); /* Create the stations tree view, add it to the scrolled window */ priv->stations_tree_view = gv_stations_tree_view_new(); //gtk_widget_show_all(priv->stations_tree_view); gtk_container_add(GTK_CONTAINER(priv->scrolled_window), priv->stations_tree_view); /* Pack that within the box */ gtk_container_add(GTK_CONTAINER(self), priv->playlist_view_box); /* Cleanup */ g_object_unref(builder); } static void gv_playlist_view_setup_appearance(GvPlaylistView *self) { GvPlaylistViewPrivate *priv = self->priv; g_object_set(priv->playlist_view_box, "margin", GV_UI_MAIN_WINDOW_MARGIN, "spacing", GV_UI_ELEM_SPACING, NULL); g_object_set(priv->station_grid, "column-spacing", GV_UI_ELEM_SPACING, NULL); } static void gv_playlist_view_setup_widgets(GvPlaylistView *self) { GvPlaylistViewPrivate *priv = self->priv; GObject *player_obj = G_OBJECT(gv_core_player); /* Give a name to some widgets, for the status icon window */ gtk_widget_set_name(priv->go_next_button, "go_next_button"); gtk_widget_set_name(priv->station_name_label, "station_name_label"); gtk_widget_set_name(priv->stations_tree_view, "stations_tree_view"); /* Setup adjustment for the volume button */ setup_adjustment(GTK_SCALE_BUTTON(priv->volume_button), player_obj, "volume"); /* Connect next button */ g_signal_connect_object(priv->go_next_button, "clicked", G_CALLBACK(on_go_next_button_clicked), self, 0); /* Connect controls */ g_signal_connect_object(priv->play_button, "clicked", G_CALLBACK(on_control_button_clicked), self, 0); g_signal_connect_object(priv->prev_button, "clicked", G_CALLBACK(on_control_button_clicked), self, 0); g_signal_connect_object(priv->next_button, "clicked", G_CALLBACK(on_control_button_clicked), self, 0); /* Connect map and unmap */ g_signal_connect_object(self, "map", G_CALLBACK(on_map), NULL, 0); g_signal_connect_object(self, "unmap", G_CALLBACK(on_unmap), NULL, 0); /* Make sure the play/pause button has focus */ gtk_widget_grab_focus(priv->play_button); } /* * GObject methods */ static void gv_playlist_view_finalize(GObject *object) { TRACE("%p", object); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_playlist_view, object); } static void gv_playlist_view_constructed(GObject *object) { GvPlaylistView *self = GV_PLAYLIST_VIEW(object); TRACE("%p", self); /* Build widget */ gv_playlist_view_populate_widgets(self); gv_playlist_view_setup_appearance(self); gv_playlist_view_setup_widgets(self); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_playlist_view, object); } static void gv_playlist_view_init(GvPlaylistView *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_playlist_view_get_instance_private(self); } static void gv_playlist_view_class_init(GvPlaylistViewClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_playlist_view_finalize; object_class->constructed = gv_playlist_view_constructed; /* Signals */ signals[SIGNAL_GO_NEXT_CLICKED] = g_signal_new("go-next-clicked", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } goodvibes-v0.7.2/src/ui/gv-playlist-view.h000066400000000000000000000017361414415210700204750ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_PLAYLIST_VIEW gv_playlist_view_get_type() G_DECLARE_FINAL_TYPE(GvPlaylistView, gv_playlist_view, GV, PLAYLIST_VIEW, GtkBox) /* Methods */ GtkWidget *gv_playlist_view_new(void); goodvibes-v0.7.2/src/ui/gv-prefs-window.c000066400000000000000000000420161414415210700202770ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "feat/gv-feat.h" #include "ui/gtk-additions.h" #include "ui/gv-ui-enum-types.h" #include "ui/gv-ui-internal.h" #include "ui/gv-prefs-window.h" #define UI_RESOURCE_PATH GV_APPLICATION_PATH "/Ui/prefs-window.glade" /* * GObject definitions */ struct _GvPrefsWindowPrivate { /* * Features */ /* Controls */ GvFeature *hotkeys_feat; GvFeature *dbus_native_feat; GvFeature *dbus_mpris2_feat; /* Display */ GvFeature *notifications_feat; GvFeature *console_output_feat; /* Player */ GvFeature *inhibitor_feat; /* * Widgets */ /* Top-level */ GtkWidget *window_vbox; /* General */ GtkWidget *general_vbox; GtkWidget *application_frame; GtkWidget *application_grid; GtkWidget *close_action_combo; GtkWidget *playback_frame; GtkWidget *playback_grid; GtkWidget *autoplay_check; GtkWidget *pipeline_check; GtkWidget *pipeline_entry; GtkWidget *pipeline_apply_button; GtkWidget *system_frame; GtkWidget *system_grid; GtkWidget *inhibitor_label; GtkWidget *inhibitor_switch; GtkWidget *dbus_frame; GtkWidget *dbus_grid; GtkWidget *dbus_native_label; GtkWidget *dbus_native_switch; GtkWidget *dbus_mpris2_label; GtkWidget *dbus_mpris2_switch; /* Display */ GtkWidget *display_vbox; GtkWidget *window_frame; GtkWidget *window_grid; GtkWidget *window_theme_combo; GtkWidget *notif_frame; GtkWidget *notif_grid; GtkWidget *notif_enable_label; GtkWidget *notif_enable_switch; GtkWidget *console_frame; GtkWidget *console_grid; GtkWidget *console_output_label; GtkWidget *console_output_switch; /* Controls */ GtkWidget *controls_vbox; GtkWidget *keyboard_frame; GtkWidget *keyboard_grid; GtkWidget *hotkeys_label; GtkWidget *hotkeys_switch; GtkWidget *mouse_frame; GtkWidget *mouse_grid; GtkWidget *middle_click_action_label; GtkWidget *middle_click_action_combo; GtkWidget *scroll_action_label; GtkWidget *scroll_action_combo; }; typedef struct _GvPrefsWindowPrivate GvPrefsWindowPrivate; struct _GvPrefsWindow { /* Parent instance structure */ GtkWindow parent_instance; /* Private data */ GvPrefsWindowPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvPrefsWindow, gv_prefs_window, GTK_TYPE_WINDOW) /* * Gtk signal handlers */ static void on_pipeline_apply_button_clicked(GtkButton *button G_GNUC_UNUSED, GvPrefsWindow *self) { GvPrefsWindowPrivate *priv = self->priv; GtkEntry *pipeline_entry = GTK_ENTRY(priv->pipeline_entry); const gchar *text; text = gtk_entry_get_text(pipeline_entry); if (text) { /* Strip leading and trailing whitespaces */ gchar *stripped; stripped = g_strstrip(g_strdup(text)); gtk_entry_set_text(pipeline_entry, stripped); g_free(stripped); text = gtk_entry_get_text(pipeline_entry); } if (text && strlen(text) > 0) { gv_player_set_pipeline_string(gv_core_player, text); } else { gv_player_set_pipeline_string(gv_core_player, NULL); } } /* * GBinding transform functions */ static gboolean transform_pipeline_string_to(GBinding *binding G_GNUC_UNUSED, const GValue *from_value, GValue *to_value, gpointer user_data G_GNUC_UNUSED) { const gchar *str; /* GtkEntry complains if we set the 'text' property to NULL... * [Gtk] gtk_entry_set_text: assertion 'text != NULL' failed */ str = g_value_get_string(from_value); if (str == NULL) g_value_set_static_string(to_value, ""); else g_value_set_string(to_value, str); return TRUE; } /* * Construct helpers */ static void setup_notebook_page_appearance(GtkWidget *vbox) { g_return_if_fail(GTK_IS_BOX(vbox)); g_object_set(vbox, "margin", GV_UI_WINDOW_MARGIN, "spacing", GV_UI_GROUP_SPACING, NULL); } static void setup_section_appearance(GtkWidget *frame, GtkWidget *grid) { static PangoAttrList *bold_attr_list; GtkWidget *label; g_return_if_fail(GTK_IS_FRAME(frame)); g_return_if_fail(GTK_IS_GRID(grid)); if (bold_attr_list == NULL) { bold_attr_list = pango_attr_list_new(); pango_attr_list_insert(bold_attr_list, pango_attr_weight_new(PANGO_WEIGHT_BOLD)); } label = gtk_frame_get_label_widget(GTK_FRAME(frame)); if (label) { gtk_label_set_attributes(GTK_LABEL(label), bold_attr_list); gtk_widget_set_margin_bottom(label, GV_UI_ELEM_SPACING); } g_object_set(grid, "row-spacing", GV_UI_ELEM_SPACING, "column-spacing", GV_UI_COLUMN_SPACING, NULL); } static void setdown_widget(const gchar *tooltip_text, GtkWidget *widget) { if (tooltip_text) gtk_widget_set_tooltip_text(widget, tooltip_text); gtk_widget_set_sensitive(widget, FALSE); } static void setup_setting(const gchar *tooltip_text, GtkWidget *label, GtkWidget *widget, const gchar *widget_prop, GObject *obj, const gchar *obj_prop, GBindingTransformFunc transform_to, GBindingTransformFunc transform_from) { /* Tooltip */ if (tooltip_text) { if (widget) gtk_widget_set_tooltip_text(widget, tooltip_text); if (label) gtk_widget_set_tooltip_text(label, tooltip_text); } /* Binding: obj 'prop' <-> widget 'prop' * Order matters, don't mix up source and target here... */ if (!widget || !obj) return; g_object_bind_property_full(obj, obj_prop, widget, widget_prop, G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, transform_to, transform_from, NULL, NULL); } static void setup_feature(const gchar *tooltip_text, GtkWidget *label, GtkWidget *sw, GvFeature *feat) { /* If feat is NULL, it's because it's been disabled at compile time */ if (feat == NULL) { tooltip_text = _("Feature disabled at compile-time."); gtk_widget_set_tooltip_text(sw, tooltip_text); gtk_widget_set_tooltip_text(label, tooltip_text); gtk_widget_set_sensitive(label, FALSE); gtk_widget_set_sensitive(sw, FALSE); return; } /* Tooltip */ if (tooltip_text) { gtk_widget_set_tooltip_text(sw, tooltip_text); gtk_widget_set_tooltip_text(label, tooltip_text); } /* Binding: feature 'enabled' <-> switch 'active' * Order matters, don't mix up source and target here... */ g_object_bind_property(feat, "enabled", sw, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); } /* * Public methods */ GtkWidget * gv_prefs_window_new(void) { return g_object_new(GV_TYPE_PREFS_WINDOW, NULL); } /* * Construct helpers */ static void gv_prefs_window_populate_features(GvPrefsWindow *self) { GvPrefsWindowPrivate *priv = self->priv; /* Controls */ priv->hotkeys_feat = gv_feat_find("Hotkeys"); priv->dbus_native_feat = gv_feat_find("DBusServerNative"); priv->dbus_mpris2_feat = gv_feat_find("DBusServerMpris2"); /* Display */ priv->notifications_feat = gv_feat_find("Notifications"); priv->console_output_feat = gv_feat_find("ConsoleOutput"); /* Player */ priv->inhibitor_feat = gv_feat_find("Inhibitor"); } static void gv_prefs_window_populate_widgets(GvPrefsWindow *self) { GvPrefsWindowPrivate *priv = self->priv; GtkBuilder *builder; /* Build the ui */ builder = gtk_builder_new_from_resource(UI_RESOURCE_PATH); /* Save widget pointers */ /* Top-level */ GTK_BUILDER_SAVE_WIDGET(builder, priv, window_vbox); /* General */ GTK_BUILDER_SAVE_WIDGET(builder, priv, general_vbox); GTK_BUILDER_SAVE_WIDGET(builder, priv, application_frame); GTK_BUILDER_SAVE_WIDGET(builder, priv, application_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, close_action_combo); GTK_BUILDER_SAVE_WIDGET(builder, priv, playback_frame); GTK_BUILDER_SAVE_WIDGET(builder, priv, playback_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, autoplay_check); GTK_BUILDER_SAVE_WIDGET(builder, priv, pipeline_check); GTK_BUILDER_SAVE_WIDGET(builder, priv, pipeline_entry); GTK_BUILDER_SAVE_WIDGET(builder, priv, pipeline_apply_button); GTK_BUILDER_SAVE_WIDGET(builder, priv, system_frame); GTK_BUILDER_SAVE_WIDGET(builder, priv, system_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, inhibitor_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, inhibitor_switch); GTK_BUILDER_SAVE_WIDGET(builder, priv, dbus_frame); GTK_BUILDER_SAVE_WIDGET(builder, priv, dbus_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, dbus_native_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, dbus_native_switch); GTK_BUILDER_SAVE_WIDGET(builder, priv, dbus_mpris2_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, dbus_mpris2_switch); /* Display */ GTK_BUILDER_SAVE_WIDGET(builder, priv, display_vbox); GTK_BUILDER_SAVE_WIDGET(builder, priv, window_frame); GTK_BUILDER_SAVE_WIDGET(builder, priv, window_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, window_theme_combo); GTK_BUILDER_SAVE_WIDGET(builder, priv, notif_frame); GTK_BUILDER_SAVE_WIDGET(builder, priv, notif_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, notif_enable_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, notif_enable_switch); GTK_BUILDER_SAVE_WIDGET(builder, priv, console_frame); GTK_BUILDER_SAVE_WIDGET(builder, priv, console_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, console_output_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, console_output_switch); /* Controls */ GTK_BUILDER_SAVE_WIDGET(builder, priv, controls_vbox); GTK_BUILDER_SAVE_WIDGET(builder, priv, keyboard_frame); GTK_BUILDER_SAVE_WIDGET(builder, priv, keyboard_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, hotkeys_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, hotkeys_switch); GTK_BUILDER_SAVE_WIDGET(builder, priv, mouse_frame); GTK_BUILDER_SAVE_WIDGET(builder, priv, mouse_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, middle_click_action_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, middle_click_action_combo); GTK_BUILDER_SAVE_WIDGET(builder, priv, scroll_action_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, scroll_action_combo); /* Pack that within the window */ gtk_container_add(GTK_CONTAINER(self), priv->window_vbox); /* Cleanup */ g_object_unref(builder); } static void gv_prefs_window_setup_widgets(GvPrefsWindow *self) { GvPrefsWindowPrivate *priv = self->priv; GObject *main_window_obj = G_OBJECT(gv_ui_main_window); GObject *status_icon_obj = G_OBJECT(gv_ui_status_icon); GObject *player_obj = G_OBJECT(gv_core_player); /* * Setup settings and features. * These function calls create a binding between a gtk widget and * an internal object, initializes the widget value, and set the * widgets tooltips (label + setting). */ /* General */ if (status_icon_obj == NULL) { setup_setting(_("Action when the close button is clicked."), NULL, priv->close_action_combo, "active-id", main_window_obj, "close-action", NULL, NULL); } else { setdown_widget(_("Setting not available in status icon mode."), priv->application_frame); } setup_setting(_("Whether to start playback automatically on startup."), NULL, priv->autoplay_check, "active", player_obj, "autoplay", NULL, NULL); setup_setting(_("Whether to use a custom output pipeline."), NULL, priv->pipeline_check, "active", player_obj, "pipeline-enabled", NULL, NULL); setup_setting(_("The GStreamer output pipeline used for playback. Refer to the" " online documentation for examples."), NULL, priv->pipeline_entry, NULL, NULL, NULL, NULL, NULL); g_object_bind_property_full(player_obj, "pipeline-string", priv->pipeline_entry, "text", G_BINDING_SYNC_CREATE, transform_pipeline_string_to, NULL, NULL, NULL); g_signal_connect_object(priv->pipeline_apply_button, "clicked", G_CALLBACK(on_pipeline_apply_button_clicked), self, 0); setup_feature(_("Prevent the system from going to sleep while playing."), priv->inhibitor_label, priv->inhibitor_switch, priv->inhibitor_feat); setup_feature(_("Enable the native D-Bus server " "(needed for the command-line interface)."), priv->dbus_native_label, priv->dbus_native_switch, priv->dbus_native_feat); setup_feature(_("Enable the MPRIS2 D-Bus server."), priv->dbus_mpris2_label, priv->dbus_mpris2_switch, priv->dbus_mpris2_feat); /* Display */ setup_setting(_("Prefer a different variant of the theme (if available)."), NULL, priv->window_theme_combo, "active-id", main_window_obj, "theme-variant", NULL, NULL); setup_feature(_("Show notification when the status changes."), priv->notif_enable_label, priv->notif_enable_switch, priv->notifications_feat); setup_feature(_("Display information on the standard output."), priv->console_output_label, priv->console_output_switch, priv->console_output_feat); /* Controls */ setup_feature(_("Bind mutimedia keys (play/pause/stop/previous/next)."), priv->hotkeys_label, priv->hotkeys_switch, priv->hotkeys_feat); if (status_icon_obj != NULL) { setup_setting(_("Action triggered by a middle click on the status icon."), priv->middle_click_action_label, priv->middle_click_action_combo, "active-id", status_icon_obj, "middle-click-action", NULL, NULL); setup_setting(_("Action triggered by mouse-scrolling on the status icon."), priv->scroll_action_label, priv->scroll_action_combo, "active-id", status_icon_obj, "scroll-action", NULL, NULL); } else { setdown_widget(_("Setting only available in status icon mode."), priv->mouse_frame); } /* * Some widgets have conditional sensitivity. */ g_object_bind_property(priv->pipeline_check, "active", priv->pipeline_entry, "sensitive", G_BINDING_SYNC_CREATE); g_object_bind_property(priv->pipeline_check, "active", priv->pipeline_apply_button, "sensitive", G_BINDING_SYNC_CREATE); } static void gv_prefs_window_setup_appearance(GvPrefsWindow *self) { GvPrefsWindowPrivate *priv = self->priv; /* Window */ g_object_set(priv->window_vbox, "margin", 0, "spacing", 0, NULL); /* General */ setup_notebook_page_appearance(priv->general_vbox); setup_section_appearance(priv->application_frame, priv->application_grid); setup_section_appearance(priv->playback_frame, priv->playback_grid); setup_section_appearance(priv->system_frame, priv->system_grid); setup_section_appearance(priv->dbus_frame, priv->dbus_grid); /* Display */ setup_notebook_page_appearance(priv->display_vbox); setup_section_appearance(priv->window_frame, priv->window_grid); setup_section_appearance(priv->notif_frame, priv->notif_grid); setup_section_appearance(priv->console_frame, priv->console_grid); /* Controls */ setup_notebook_page_appearance(priv->controls_vbox); setup_section_appearance(priv->keyboard_frame, priv->keyboard_grid); setup_section_appearance(priv->mouse_frame, priv->mouse_grid); } /* * GObject methods */ static void gv_prefs_window_finalize(GObject *object) { TRACE("%p", object); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_prefs_window, object); } static void gv_prefs_window_constructed(GObject *object) { GvPrefsWindow *self = GV_PREFS_WINDOW(object); /* Build the window */ gv_prefs_window_populate_features(self); gv_prefs_window_populate_widgets(self); gv_prefs_window_setup_widgets(self); gv_prefs_window_setup_appearance(self); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_prefs_window, object); } static void gv_prefs_window_init(GvPrefsWindow *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_prefs_window_get_instance_private(self); } static void gv_prefs_window_class_init(GvPrefsWindowClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_prefs_window_finalize; object_class->constructed = gv_prefs_window_constructed; } /* * Convenience functions */ static GtkWidget * make_prefs_window(GtkWindow *parent) { GtkWidget *widget; GtkWindow *window; widget = gv_prefs_window_new(); window = GTK_WINDOW(widget); gtk_window_set_title(window, _("Preferences")); if (parent) { gtk_window_set_transient_for(window, parent); gtk_window_set_destroy_with_parent(window, TRUE); gtk_window_set_position(window, GTK_WIN_POS_CENTER_ON_PARENT); } else { gtk_window_set_position(window, GTK_WIN_POS_MOUSE); } return widget; } void gv_show_prefs_window(GtkWindow *parent) { static GtkWidget *prefs; /* Create if needed */ if (prefs == NULL) { prefs = make_prefs_window(parent); g_object_add_weak_pointer(G_OBJECT(prefs), (gpointer *) &prefs); } /* Present */ gtk_window_present(GTK_WINDOW(prefs)); } goodvibes-v0.7.2/src/ui/gv-prefs-window.h000066400000000000000000000020531414415210700203010ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_PREFS_WINDOW gv_prefs_window_get_type() G_DECLARE_FINAL_TYPE(GvPrefsWindow, gv_prefs_window, GV, PREFS_WINDOW, GtkWindow) /* Convenience functions */ void gv_show_prefs_window(GtkWindow *parent); /* Methods */ GtkWidget *gv_prefs_window_new(void); goodvibes-v0.7.2/src/ui/gv-station-context-menu.c000066400000000000000000000156461414415210700217710ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gtk-additions.h" #include "ui/gv-station-dialog.h" #include "ui/gv-ui-internal.h" #include "ui/gv-station-context-menu.h" #define ADD_STATION_LABEL _("Add Station") #define REMOVE_STATION_LABEL _("Remove Station") #define EDIT_STATION_LABEL _("Edit Station") /* * Properties */ enum { /* Reserved */ PROP_0, /* Properties */ PROP_STATION, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * GObject definitions */ struct _GvStationContextMenuPrivate { /* Widgets */ GtkWidget *add_station_menu_item; GtkWidget *remove_station_menu_item; GtkWidget *edit_station_menu_item; /* Selected station if any */ GvStation *station; }; typedef struct _GvStationContextMenuPrivate GvStationContextMenuPrivate; struct _GvStationContextMenu { /* Parent instance structure */ GtkMenu parent_instance; /* Private data */ GvStationContextMenuPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvStationContextMenu, gv_station_context_menu, GTK_TYPE_MENU) /* * Signal handlers & callbacks */ static void on_menu_item_activate(GtkMenuItem *item, GvStationContextMenu *self) { GvStationList *station_list = gv_core_station_list; GvStationContextMenuPrivate *priv = self->priv; GvStation *selected_station = priv->station; GtkWidget *widget = GTK_WIDGET(item); if (widget == priv->add_station_menu_item) { GvStation *station; station = gv_show_add_station_dialog(GTK_WINDOW(gv_ui_main_window)); if (station) { if (selected_station) gv_station_list_insert_after(station_list, station, selected_station); else gv_station_list_append(station_list, station); } } else if (widget == priv->edit_station_menu_item && selected_station) { gv_show_edit_station_dialog(GTK_WINDOW(gv_ui_main_window), selected_station); } else if (widget == priv->remove_station_menu_item && selected_station) { gv_station_list_remove(station_list, selected_station); } else { CRITICAL("Unhandled menu item %p", item); } } /* * Private methods */ static void gv_station_context_menu_populate(GvStationContextMenu *self) { GvStationContextMenuPrivate *priv = self->priv; GtkWidget *widget; /* This is supposed to be called once only */ g_assert_null(gtk_container_get_children(GTK_CONTAINER(self))); /* Add station */ widget = gtk_menu_item_new_with_label(ADD_STATION_LABEL); gtk_menu_shell_append(GTK_MENU_SHELL(self), widget); g_signal_connect_object(widget, "activate", G_CALLBACK(on_menu_item_activate), self, 0); priv->add_station_menu_item = widget; /* In case the station list is empty, we have no station here. * We must handle this case. */ if (priv->station) { /* Remove station */ widget = gtk_menu_item_new_with_label(REMOVE_STATION_LABEL); gtk_menu_shell_append(GTK_MENU_SHELL(self), widget); g_signal_connect_object(widget, "activate", G_CALLBACK(on_menu_item_activate), self, 0); priv->remove_station_menu_item = widget; /* Edit station */ widget = gtk_menu_item_new_with_label(EDIT_STATION_LABEL); gtk_menu_shell_append(GTK_MENU_SHELL(self), widget); g_signal_connect_object(widget, "activate", G_CALLBACK(on_menu_item_activate), self, 0); priv->edit_station_menu_item = widget; } /* Showtime */ gtk_widget_show_all(GTK_WIDGET(self)); } /* * Property accessors */ static void gv_station_context_menu_set_station(GvStationContextMenu *self, GvStation *station) { GvStationContextMenuPrivate *priv = self->priv; /* This is a construct-only property - NULL is allowed */ g_assert_null(priv->station); g_set_object(&priv->station, station); } static void gv_station_context_menu_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TRACE_GET_PROPERTY(object, property_id, value, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } static void gv_station_context_menu_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvStationContextMenu *self = GV_STATION_CONTEXT_MENU(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_STATION: gv_station_context_menu_set_station(self, g_value_get_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public methods */ GtkWidget * gv_station_context_menu_new(void) { return g_object_new(GV_TYPE_STATION_CONTEXT_MENU, NULL); } GtkWidget * gv_station_context_menu_new_with_station(GvStation *station) { return g_object_new(GV_TYPE_STATION_CONTEXT_MENU, "station", station, NULL); } /* * GObject methods */ static void gv_station_context_menu_finalize(GObject *object) { GvStationContextMenu *self = GV_STATION_CONTEXT_MENU(object); GvStationContextMenuPrivate *priv = self->priv; TRACE("%p", object); /* Unref station */ if (priv->station) g_object_unref(priv->station); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_station_context_menu, object); } static void gv_station_context_menu_constructed(GObject *object) { GvStationContextMenu *self = GV_STATION_CONTEXT_MENU(object); TRACE("%p", object); /* Populate */ gv_station_context_menu_populate(self); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_station_context_menu, object); } static void gv_station_context_menu_init(GvStationContextMenu *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_station_context_menu_get_instance_private(self); } static void gv_station_context_menu_class_init(GvStationContextMenuClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_station_context_menu_finalize; object_class->constructed = gv_station_context_menu_constructed; /* Properties */ object_class->get_property = gv_station_context_menu_get_property; object_class->set_property = gv_station_context_menu_set_property; properties[PROP_STATION] = g_param_spec_object("station", "Station", NULL, GV_TYPE_STATION, GV_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties(object_class, PROP_N, properties); } goodvibes-v0.7.2/src/ui/gv-station-context-menu.h000066400000000000000000000022301414415210700217570ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include #include "core/gv-station.h" /* GObject declarations */ #define GV_TYPE_STATION_CONTEXT_MENU gv_station_context_menu_get_type() G_DECLARE_FINAL_TYPE(GvStationContextMenu, gv_station_context_menu, \ GV, STATION_CONTEXT_MENU, GtkMenu) /* Methods */ GtkWidget *gv_station_context_menu_new (void); GtkWidget *gv_station_context_menu_new_with_station(GvStation *station); goodvibes-v0.7.2/src/ui/gv-station-dialog.c000066400000000000000000000316641414415210700206000ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gtk-additions.h" #include "ui/gv-ui-internal.h" #include "ui/gv-station-dialog.h" #define UI_RESOURCE_PATH GV_APPLICATION_PATH "/Ui/station-dialog.glade" #define DEFAULT_WINDOW_WIDTH 480 /* * Properties */ enum { /* Reserved */ PROP_0, /* Properties */ PROP_STATION, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * GObject definitions */ struct _GvStationDialogPrivate { /* Widgets */ GtkWidget *main_grid; GtkWidget *name_entry; GtkWidget *uri_entry; GtkWidget *sec_hbox; GtkWidget *sec_label; GtkWidget *sec_button; /* Buttons */ GtkWidget *save_button; /* Existing station if any */ GvStation *station; }; typedef struct _GvStationDialogPrivate GvStationDialogPrivate; struct _GvStationDialog { /* Parent instance structure */ GtkDialog parent_instance; /* Private data */ GvStationDialogPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvStationDialog, gv_station_dialog, GTK_TYPE_DIALOG) /* * Helpers */ static void remove_weird_chars(const gchar *text, gchar **out, guint *out_len) { gchar *start, *ptr; guint length, i; length = strlen(text); start = g_malloc(length + 1); for (i = 0, ptr = start; i < length; i++) { /* Discard every character below space */ if (text[i] > ' ') *ptr++ = text[i]; } *ptr = '\0'; if (length != ptr - start) { length = ptr - start; start = g_realloc(start, length); } *out = start; *out_len = length; } /* * Signal handlers */ static void on_uri_entry_insert_text(GtkEditable *editable, gchar *text, gint length G_GNUC_UNUSED, gpointer position, GvStationDialog *self) { gchar *new_text; guint new_len; /* Remove weird characters */ remove_weird_chars(text, &new_text, &new_len); /* Replace text in entry */ g_signal_handlers_block_by_func(editable, on_uri_entry_insert_text, self); gtk_editable_insert_text(editable, new_text, new_len, position); g_signal_handlers_unblock_by_func(editable, on_uri_entry_insert_text, self); /* We inserted the text in the entry, so now we just need to * prevent the default handler to run. */ g_signal_stop_emission_by_name(editable, "insert-text"); /* Free */ g_free(new_text); } static void on_uri_entry_changed(GtkEditable *editable, GvStationDialog *self) { GvStationDialogPrivate *priv = self->priv; guint text_len; text_len = gtk_entry_get_text_length(GTK_ENTRY(editable)); /* If the entry is empty, the save button is not clickable */ if (text_len > 0) gtk_widget_set_sensitive(priv->save_button, TRUE); else gtk_widget_set_sensitive(priv->save_button, FALSE); } static void on_sec_button_clicked(GtkButton *button G_GNUC_UNUSED, GvStationDialog *self) { GvStationDialogPrivate *priv = self->priv; GtkWidget *sec_hbox = priv->sec_hbox; GtkWidget *sec_label = priv->sec_label; /* We don't do it yet */ gtk_label_set_text(GTK_LABEL(sec_label), _("Security Exception removed")); gtk_widget_set_sensitive(sec_hbox, FALSE); } /* * Property accessors */ static void gv_station_dialog_set_station(GvStationDialog *self, GvStation *station) { GvStationDialogPrivate *priv = self->priv; /* This is a construct-only property - NULL is allowed */ g_assert_null(priv->station); g_set_object(&priv->station, station); } static void gv_station_dialog_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TRACE_GET_PROPERTY(object, property_id, value, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } static void gv_station_dialog_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvStationDialog *self = GV_STATION_DIALOG(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_STATION: gv_station_dialog_set_station(self, g_value_get_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public methods */ void gv_station_dialog_retrieve(GvStationDialog *self, GvStation *station) { GvStationDialogPrivate *priv = self->priv; GtkEntry *name_entry = GTK_ENTRY(priv->name_entry); GtkEntry *uri_entry = GTK_ENTRY(priv->uri_entry); GtkWidget *sec_hbox = priv->sec_hbox; gv_station_set_name(station, gtk_entry_get_text(name_entry)); gv_station_set_uri(station, gtk_entry_get_text(uri_entry)); if (gtk_widget_get_visible(sec_hbox) == TRUE && gtk_widget_get_sensitive(sec_hbox) == FALSE) gv_station_set_insecure(station, FALSE); } GvStation * gv_station_dialog_create(GvStationDialog *self) { GvStationDialogPrivate *priv = self->priv; GtkEntry *name_entry = GTK_ENTRY(priv->name_entry); GtkEntry *uri_entry = GTK_ENTRY(priv->uri_entry); const gchar *name, *uri; name = gtk_entry_get_text(name_entry); uri = gtk_entry_get_text(uri_entry); return gv_station_new(name, uri); } void gv_station_dialog_fill_uri(GvStationDialog *self, const gchar *uri) { GvStationDialogPrivate *priv = self->priv; GtkEntry *uri_entry = GTK_ENTRY(priv->uri_entry); gtk_entry_set_text(uri_entry, uri); } GtkWidget * gv_station_dialog_new(GvStation *station) { return g_object_new(GV_TYPE_STATION_DIALOG, "station", station, NULL); } /* * Construct helpers */ static void gv_station_dialog_populate_widgets(GvStationDialog *self) { GvStationDialogPrivate *priv = self->priv; GtkWidget *content_area; GtkBuilder *builder; /* Build the ui */ builder = gtk_builder_new_from_resource(UI_RESOURCE_PATH); /* Save widget pointers */ GTK_BUILDER_SAVE_WIDGET(builder, priv, main_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, name_entry); GTK_BUILDER_SAVE_WIDGET(builder, priv, uri_entry); GTK_BUILDER_SAVE_WIDGET(builder, priv, sec_hbox); GTK_BUILDER_SAVE_WIDGET(builder, priv, sec_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, sec_button); /* Add to content area */ content_area = gtk_dialog_get_content_area(GTK_DIALOG(self)); gtk_container_add(GTK_CONTAINER(content_area), priv->main_grid); /* Populate the action area */ gtk_dialog_add_buttons(GTK_DIALOG(self), _("Cancel"), GTK_RESPONSE_CANCEL, _("Save"), GTK_RESPONSE_OK, NULL); priv->save_button = gtk_dialog_get_widget_for_response( GTK_DIALOG(self), GTK_RESPONSE_OK); /* Cleanup */ g_object_unref(builder); } static void gv_station_dialog_setup_widgets(GvStationDialog *self) { GvStationDialogPrivate *priv = self->priv; GtkEntry *name_entry = GTK_ENTRY(priv->name_entry); GtkEntry *uri_entry = GTK_ENTRY(priv->uri_entry); GtkWidget *sec_hbox = priv->sec_hbox; GtkButton *sec_button = GTK_BUTTON(priv->sec_button); GvStation *station = priv->station; /* We don't allow creating a station with an empty uri, therefore * the save button is insensitive when the uri is empty. We can set it * insensitive now because of the following code... * Watch out if you change something here! */ gtk_widget_set_sensitive(priv->save_button, FALSE); /* Configure uri widget */ gtk_entry_set_input_purpose(uri_entry, GTK_INPUT_PURPOSE_URL); g_signal_connect_object(uri_entry, "insert-text", G_CALLBACK(on_uri_entry_insert_text), self, 0); g_signal_connect_object(uri_entry, "changed", G_CALLBACK(on_uri_entry_changed), self, 0); /* Fill widgets with station info */ if (station) { const gchar *name, *uri; name = gv_station_get_name(station); if (name) gtk_entry_set_text(name_entry, name); uri = gv_station_get_uri(station); if (uri) gtk_entry_set_text(uri_entry, uri); } /* Security exception is shown only if insecure is true */ if (station && gv_station_get_insecure(station) == TRUE) { gtk_widget_set_visible(sec_hbox, TRUE); g_signal_connect_object(sec_button, "clicked", G_CALLBACK(on_sec_button_clicked), self, 0); } else { gtk_widget_set_visible(sec_hbox, FALSE); } } static void gv_station_dialog_setup_appearance(GvStationDialog *self) { GvStationDialogPrivate *priv = self->priv; GtkWidget *content_area; gtk_window_set_default_size(GTK_WINDOW(self), DEFAULT_WINDOW_WIDTH, -1); content_area = gtk_dialog_get_content_area(GTK_DIALOG(self)); g_object_set(content_area, "margin", GV_UI_WINDOW_MARGIN, "spacing", GV_UI_WINDOW_MARGIN, NULL); g_object_set(priv->main_grid, "row-spacing", GV_UI_ELEM_SPACING, "column-spacing", GV_UI_COLUMN_SPACING, NULL); } /* * GObject methods */ static void gv_station_dialog_finalize(GObject *object) { GvStationDialog *self = GV_STATION_DIALOG(object); GvStationDialogPrivate *priv = self->priv; TRACE("%p", object); /* Unref station */ if (priv->station) g_object_unref(priv->station); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_station_dialog, object); } static void gv_station_dialog_constructed(GObject *object) { GvStationDialog *self = GV_STATION_DIALOG(object); /* Build window */ gv_station_dialog_populate_widgets(self); gv_station_dialog_setup_widgets(self); gv_station_dialog_setup_appearance(self); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_station_dialog, object); } static void gv_station_dialog_init(GvStationDialog *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_station_dialog_get_instance_private(self); } static void gv_station_dialog_class_init(GvStationDialogClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_station_dialog_finalize; object_class->constructed = gv_station_dialog_constructed; /* Properties */ object_class->get_property = gv_station_dialog_get_property; object_class->set_property = gv_station_dialog_set_property; properties[PROP_STATION] = g_param_spec_object("station", "Station", NULL, GV_TYPE_STATION, GV_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties(object_class, PROP_N, properties); } /* * Convenience functions */ static GtkWidget * make_station_dialog(GtkWindow *parent, GvStation *station) { GtkWidget *dialog; dialog = gv_station_dialog_new(station); gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), TRUE); gtk_window_set_transient_for(GTK_WINDOW(dialog), parent); gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); gtk_window_set_title(GTK_WINDOW(dialog), station ? _("Edit Station") : _("Add Station")); /* When 'Add Station' is invoked from the popup menu, in status icon mode, * we want a better position than centering on screen. */ if (!gtk_widget_is_visible(GTK_WIDGET(parent))) gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); return dialog; } void gv_show_edit_station_dialog(GtkWindow *parent, GvStation *station) { GtkWidget *dialog; gint response; /* Create and configure the dialog */ dialog = make_station_dialog(parent, station); /* Run */ response = gtk_dialog_run(GTK_DIALOG(dialog)); if (response == GTK_RESPONSE_OK) gv_station_dialog_retrieve(GV_STATION_DIALOG(dialog), station); /* Cleanup */ gtk_widget_destroy(dialog); } GvStation * gv_show_add_station_dialog(GtkWindow *parent) { GvStation *station; GtkWidget *dialog; gint response; /* Create and configure the dialog */ dialog = make_station_dialog(parent, NULL); /* When we're asked to display an empty station dialog, we play a little trick. * If the application was started with an uri in argument, it's possible that * the current station is not part of the station list. In such case, we assume * that the user intends to add this station to the list, so we save him a bit * of time and populate the dialog with this uri. */ GvPlayer *player = gv_core_player; GvStationList *station_list = gv_core_station_list; GvStation *current_station; current_station = gv_player_get_station(player); if (current_station && gv_station_list_find(station_list, current_station) == NULL) { gv_station_dialog_fill_uri(GV_STATION_DIALOG(dialog), gv_station_get_uri(current_station)); } /* Run */ response = gtk_dialog_run(GTK_DIALOG(dialog)); if (response == GTK_RESPONSE_OK) station = gv_station_dialog_create(GV_STATION_DIALOG(dialog)); else station = NULL; /* Cleanup */ gtk_widget_destroy(dialog); return station; } goodvibes-v0.7.2/src/ui/gv-station-dialog.h000066400000000000000000000026471414415210700206040ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include #include "core/gv-station.h" /* GObject declarations */ #define GV_TYPE_STATION_DIALOG gv_station_dialog_get_type() G_DECLARE_FINAL_TYPE(GvStationDialog, gv_station_dialog, GV, STATION_DIALOG, GtkDialog) /* Convenience functions */ GvStation *gv_show_add_station_dialog (GtkWindow *parent); void gv_show_edit_station_dialog(GtkWindow *parent, GvStation *station); /* Methods */ GtkWidget *gv_station_dialog_new (GvStation *station); void gv_station_dialog_fill_uri(GvStationDialog *dialog, const gchar *uri); void gv_station_dialog_retrieve(GvStationDialog *self, GvStation *station); GvStation *gv_station_dialog_create (GvStationDialog *self); goodvibes-v0.7.2/src/ui/gv-station-view.c000066400000000000000000000411361414415210700203060ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gtk-additions.h" #include "ui/gv-station-view.h" #include "ui/gv-ui-internal.h" #define UI_RESOURCE_PATH GV_APPLICATION_PATH "/Ui/station-view.glade" /* * Signal */ enum { SIGNAL_GO_BACK_CLICKED, /* Number of signals */ SIGNAL_N }; static guint signals[SIGNAL_N]; /* * GObject definitions */ struct _GvProp { GtkWidget *title_label; GtkWidget *value_label; gboolean show_when_empty; }; typedef struct _GvProp GvProp; struct _GvStationViewPrivate { /* * Widgets */ /* Top-level */ GtkWidget *station_view_box; /* Current station */ GtkWidget *station_grid; GtkWidget *station_name_label; GtkWidget *playback_status_label; GtkWidget *go_back_button; /* Properties */ GtkWidget *properties_grid; /* Station properties */ GtkWidget *stainfo_label; GvProp uri_prop; GvProp streams_prop; GvProp user_agent_prop; GvProp stream_type_prop; GvProp codec_prop; GvProp channels_prop; GvProp sample_rate_prop; GvProp bitrate_prop; /* Metadata */ GtkWidget *metadata_label; GvProp title_prop; GvProp artist_prop; GvProp album_prop; GvProp genre_prop; GvProp year_prop; GvProp comment_prop; }; typedef struct _GvStationViewPrivate GvStationViewPrivate; struct _GvStationView { /* Parent instance structure */ GtkBox parent_instance; /* Private data */ GvStationViewPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvStationView, gv_station_view, GTK_TYPE_BOX) /* * GvProp * * A simple abstraction to represent a property. */ static void gv_prop_init(GvProp *prop, GtkBuilder *builder, const gchar *propname, gboolean show_when_empty) { gchar *widget_name; GObject *object; GtkWidget *widget; GtkLabel *label; GtkStyleContext *style_context; widget_name = g_strdup_printf("%s_%s", propname, "title"); object = gtk_builder_get_object(builder, widget_name); prop->title_label = GTK_WIDGET(object); g_free(widget_name); label = GTK_LABEL(object); gtk_label_set_xalign(label, 1); widget = GTK_WIDGET(object); gtk_widget_set_valign(widget, GTK_ALIGN_START); style_context = gtk_widget_get_style_context(widget); gtk_style_context_add_class(style_context, "dim-label"); widget_name = g_strdup_printf("%s_%s", propname, "value"); object = gtk_builder_get_object(builder, widget_name); prop->value_label = GTK_WIDGET(object); g_free(widget_name); label = GTK_LABEL(object); gtk_label_set_ellipsize(label, PANGO_ELLIPSIZE_END); gtk_label_set_xalign(label, 0); gtk_label_set_justify(label, GTK_JUSTIFY_LEFT); prop->show_when_empty = show_when_empty; } static void gv_prop_set(GvProp *prop, const gchar *text) { gboolean visible = (text != NULL) || prop->show_when_empty; gtk_label_set_text(GTK_LABEL(prop->value_label), text); gtk_widget_set_visible(prop->title_label, visible); gtk_widget_set_visible(prop->value_label, visible); } /* * Helpers */ static gchar * make_bitrate_string(guint bitrate, guint maximum_bitrate, guint minimum_bitrate, guint nominal_bitrate) { GString *str; if (bitrate == 0 && maximum_bitrate == 0 && minimum_bitrate == 0 && nominal_bitrate == 0) return NULL; /* We want to display kilobits per second */ bitrate /= 1000; maximum_bitrate /= 1000; minimum_bitrate /= 1000; nominal_bitrate /= 1000; str = g_string_new(NULL); if (bitrate > 0) g_string_printf(str, "%u %s", bitrate, _("kbps")); else g_string_printf(str, "%s", _("unknown")); if (nominal_bitrate > 0 && maximum_bitrate == 0 && minimum_bitrate == 0) /* TRANSLATORS: we talk about nominal bitrate here. */ g_string_append_printf(str, " (%s: %u)", _("nominal"), nominal_bitrate); else if (nominal_bitrate == 0 && (minimum_bitrate > 0 || maximum_bitrate > 0)) /* TRANSLATORS: we talk about minimum and maximum bitrate here. */ g_string_append_printf(str, " (%s: %u, %s: %u)", _("min"), minimum_bitrate, _("max"), maximum_bitrate); else if (nominal_bitrate > 0 && (minimum_bitrate > 0 || maximum_bitrate > 0)) /* TRANSLATORS: we talk about nominal, minimum and maximum bitrate here. */ g_string_append_printf(str, " (%s: %u, %s: %u, %s: %u)", _("nominal"), nominal_bitrate, _("min"), minimum_bitrate, _("max"), maximum_bitrate); return g_string_free(str, FALSE); } static gchar * make_channels_string(guint channels) { gchar *str; switch (channels) { case 0: str = NULL; break; case 1: str = g_strdup(_("Mono")); break; case 2: str = g_strdup(_("Stereo")); break; default: str = g_strdup_printf("%u", channels); break; } return str; } static gchar * make_stream_type_string(GvStreamType stream_type) { const gchar *str; switch (stream_type) { case GV_STREAM_TYPE_HTTP: str = "HTTP"; break; case GV_STREAM_TYPE_HTTP_ICY: str = "HTTP+ICY"; break; case GV_STREAM_TYPE_HLS: str = "HLS"; break; case GV_STREAM_TYPE_DASH: str = "MPEG-DASH"; break; default: str = NULL; break; } return g_strdup(str); } static gchar * make_sample_rate_string(guint sample_rate) { gdouble rate; if (sample_rate == 0) return NULL; rate = sample_rate / 1000.0; return g_strdup_printf("%g %s", rate, _("kHz")); } static gchar * make_stream_uris_string(GSList *stream_uris) { GString *string; GSList *item; string = g_string_new(NULL); for (item = stream_uris; item; item = item->next) { const gchar *stream_uri = item->data; const gchar *prefix; /* We assume that the first stream is being played */ if (item == stream_uris) prefix = "‣"; else prefix = "\n"; g_string_append_printf(string, "%s %s", prefix, stream_uri); } return g_string_free(string, FALSE); } static void set_station(GvStationViewPrivate *priv, GvStation *station) { const gchar *text; g_return_if_fail(station != NULL); text = gv_station_get_name(station); if (text == NULL) text = "???"; gtk_label_set_text(GTK_LABEL(priv->station_name_label), text); text = gv_station_get_uri(station); gv_prop_set(&priv->uri_prop, text); text = gv_station_get_user_agent(station); gv_prop_set(&priv->user_agent_prop, text); text = gv_station_get_first_stream_uri(station); if (text == NULL) { /* There's no stream uris to display */ gv_prop_set(&priv->streams_prop, NULL); } else if (!g_strcmp0(text, gv_station_get_uri(station))) { /* If station uri and stream uri are the same, there's * no need to display it. */ gv_prop_set(&priv->streams_prop, NULL); } else { /* Let's display the list of stream uris then */ GSList *stream_uris; gchar *str; stream_uris = gv_station_get_stream_uris(station); str = make_stream_uris_string(stream_uris); gv_prop_set(&priv->streams_prop, str); g_free(str); } } static void unset_station(GvStationViewPrivate *priv) { gtk_label_set_text(GTK_LABEL(priv->station_name_label), _("No station selected")); gv_prop_set(&priv->uri_prop, NULL); gv_prop_set(&priv->user_agent_prop, NULL); gv_prop_set(&priv->streams_prop, NULL); } static void set_playback_status(GvStationViewPrivate *priv, GvPlaybackState state) { GtkLabel *label = GTK_LABEL(priv->playback_status_label); const gchar *playback_state_str; playback_state_str = gv_playback_state_to_string(state); gtk_label_set_text(label, playback_state_str); } static void set_streaminfo(GvStationViewPrivate *priv, GvStreaminfo *streaminfo) { GvStreamBitrate bitrate = { 0 }; gchar *str; g_return_if_fail(streaminfo != NULL); gv_streaminfo_get_bitrate(streaminfo, &bitrate); str = make_bitrate_string(bitrate.current, bitrate.maximum, bitrate.minimum, bitrate.nominal); gv_prop_set(&priv->bitrate_prop, str); g_free(str); str = make_channels_string(gv_streaminfo_get_channels(streaminfo)); gv_prop_set(&priv->channels_prop, str); g_free(str); gv_prop_set(&priv->codec_prop, gv_streaminfo_get_codec(streaminfo)); str = make_sample_rate_string(gv_streaminfo_get_sample_rate(streaminfo)); gv_prop_set(&priv->sample_rate_prop, str); g_free(str); str = make_stream_type_string(gv_streaminfo_get_stream_type(streaminfo)); gv_prop_set(&priv->stream_type_prop, str); g_free(str); } static void unset_streaminfo(GvStationViewPrivate *priv) { gv_prop_set(&priv->bitrate_prop, NULL); gv_prop_set(&priv->codec_prop, NULL); gv_prop_set(&priv->channels_prop, NULL); gv_prop_set(&priv->sample_rate_prop, NULL); gv_prop_set(&priv->stream_type_prop, NULL); } static void set_metadata(GvStationViewPrivate *priv, GvMetadata *metadata) { const gchar *text; g_return_if_fail(metadata != NULL); text = gv_metadata_get_title(metadata); gv_prop_set(&priv->title_prop, text); text = gv_metadata_get_artist(metadata); gv_prop_set(&priv->artist_prop, text); text = gv_metadata_get_album(metadata); gv_prop_set(&priv->album_prop, text); text = gv_metadata_get_genre(metadata); gv_prop_set(&priv->genre_prop, text); text = gv_metadata_get_year(metadata); gv_prop_set(&priv->year_prop, text); text = gv_metadata_get_comment(metadata); gv_prop_set(&priv->comment_prop, text); } static void unset_metadata(GvStationViewPrivate *priv) { gv_prop_set(&priv->title_prop, NULL); gv_prop_set(&priv->artist_prop, NULL); gv_prop_set(&priv->album_prop, NULL); gv_prop_set(&priv->genre_prop, NULL); gv_prop_set(&priv->year_prop, NULL); gv_prop_set(&priv->comment_prop, NULL); } /* * Private methods */ static void gv_station_view_update_station(GvStationView *self, GvPlayer *player) { GvStationViewPrivate *priv = self->priv; GvStation *station = gv_player_get_station(player); if (station) set_station(priv, station); else unset_station(priv); } static void gv_station_view_update_playback_status(GvStationView *self, GvPlayer *player) { GvStationViewPrivate *priv = self->priv; GvPlaybackState state = gv_player_get_playback_state(player); set_playback_status(priv, state); } static void gv_station_view_update_streaminfo(GvStationView *self, GvPlayer *player) { GvStationViewPrivate *priv = self->priv; GvStreaminfo *streaminfo = gv_player_get_streaminfo(player); if (streaminfo) set_streaminfo(priv, streaminfo); else unset_streaminfo(priv); } static void gv_station_view_update_metadata(GvStationView *self, GvPlayer *player) { GvStationViewPrivate *priv = self->priv; GvMetadata *metadata = gv_player_get_metadata(player); if (metadata) { set_metadata(priv, metadata); gtk_widget_set_visible(priv->metadata_label, TRUE); } else { unset_metadata(priv); gtk_widget_set_visible(priv->metadata_label, FALSE); } } /* * Signal handlers & callbacks */ static void on_player_notify(GvPlayer *player, GParamSpec *pspec, GvStationView *self) { const gchar *property_name = g_param_spec_get_name(pspec); TRACE("%p, %s, %p", player, property_name, self); if (!g_strcmp0(property_name, "station")) gv_station_view_update_station(self, player); else if (!g_strcmp0(property_name, "playback-state")) gv_station_view_update_playback_status(self, player); else if (!g_strcmp0(property_name, "streaminfo")) gv_station_view_update_streaminfo(self, player); else if (!g_strcmp0(property_name, "metadata")) gv_station_view_update_metadata(self, player); } static void on_go_back_button_clicked(GtkButton *button G_GNUC_UNUSED, GvStationView *self) { g_signal_emit(self, signals[SIGNAL_GO_BACK_CLICKED], 0); } static void on_map(GvStationView *self, gpointer user_data) { GvPlayer *player = gv_core_player; TRACE("%p, %p", self, user_data); g_signal_connect_object(player, "notify", G_CALLBACK(on_player_notify), self, 0); gv_station_view_update_station(self, player); gv_station_view_update_playback_status(self, player); gv_station_view_update_streaminfo(self, player); gv_station_view_update_metadata(self, player); } static void on_unmap(GvStationView *self, gpointer user_data G_GNUC_UNUSED) { GvPlayer *player = gv_core_player; TRACE("%p, %p", self, user_data); g_signal_handlers_disconnect_by_data(player, self); } /* * Public methods */ GtkWidget * gv_station_view_new(void) { return g_object_new(GV_TYPE_STATION_VIEW, NULL); } /* * Construct helpers */ static void gv_station_view_populate_widgets(GvStationView *self) { GvStationViewPrivate *priv = self->priv; GtkBuilder *builder; /* Build the ui */ builder = gtk_builder_new_from_resource(UI_RESOURCE_PATH); /* Save widget pointers */ /* Top-level */ GTK_BUILDER_SAVE_WIDGET(builder, priv, station_view_box); /* Station */ GTK_BUILDER_SAVE_WIDGET(builder, priv, station_grid); GTK_BUILDER_SAVE_WIDGET(builder, priv, station_name_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, playback_status_label); GTK_BUILDER_SAVE_WIDGET(builder, priv, go_back_button); /* Properties */ GTK_BUILDER_SAVE_WIDGET(builder, priv, properties_grid); /* Station Properties */ GTK_BUILDER_SAVE_WIDGET(builder, priv, stainfo_label); gv_prop_init(&priv->uri_prop, builder, "uri", TRUE); gv_prop_init(&priv->streams_prop, builder, "streams", FALSE); gv_prop_init(&priv->user_agent_prop, builder, "user_agent", FALSE); gv_prop_init(&priv->stream_type_prop, builder, "stream_type", FALSE); gv_prop_init(&priv->codec_prop, builder, "codec", FALSE); gv_prop_init(&priv->channels_prop, builder, "channels", FALSE); gv_prop_init(&priv->sample_rate_prop, builder, "sample_rate", FALSE); gv_prop_init(&priv->bitrate_prop, builder, "bitrate", FALSE); /* Metadata */ GTK_BUILDER_SAVE_WIDGET(builder, priv, metadata_label); gv_prop_init(&priv->title_prop, builder, "title", FALSE); gv_prop_init(&priv->artist_prop, builder, "artist", FALSE); gv_prop_init(&priv->album_prop, builder, "album", FALSE); gv_prop_init(&priv->genre_prop, builder, "genre", FALSE); gv_prop_init(&priv->year_prop, builder, "year", FALSE); gv_prop_init(&priv->comment_prop, builder, "comment", FALSE); /* Pack that within the box */ gtk_container_add(GTK_CONTAINER(self), priv->station_view_box); /* Cleanup */ g_object_unref(builder); } static void gv_station_view_setup_appearance(GvStationView *self) { GvStationViewPrivate *priv = self->priv; g_object_set(priv->station_view_box, "margin", GV_UI_MAIN_WINDOW_MARGIN, "spacing", GV_UI_GROUP_SPACING, NULL); g_object_set(priv->station_grid, "column-spacing", GV_UI_ELEM_SPACING, NULL); g_object_set(priv->properties_grid, "column-spacing", GV_UI_COLUMN_SPACING, "row-spacing", GV_UI_ELEM_SPACING, "margin-start", GV_UI_WINDOW_MARGIN, "margin-end", GV_UI_WINDOW_MARGIN, "margin-bottom", GV_UI_WINDOW_MARGIN, NULL); gtk_label_set_xalign(GTK_LABEL(priv->stainfo_label), 1.0); gtk_label_set_xalign(GTK_LABEL(priv->metadata_label), 1.0); } static void gv_station_view_setup_widgets(GvStationView *self) { GvStationViewPrivate *priv = self->priv; g_signal_connect_object(self, "map", G_CALLBACK(on_map), NULL, 0); g_signal_connect_object(self, "unmap", G_CALLBACK(on_unmap), NULL, 0); g_signal_connect_object(priv->go_back_button, "clicked", G_CALLBACK(on_go_back_button_clicked), self, 0); } /* * GObject methods */ static void gv_station_view_finalize(GObject *object) { TRACE("%p", object); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_station_view, object); } static void gv_station_view_constructed(GObject *object) { GvStationView *self = GV_STATION_VIEW(object); TRACE("%p", object); /* Build widget */ gv_station_view_populate_widgets(self); gv_station_view_setup_appearance(self); gv_station_view_setup_widgets(self); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_station_view, object); } static void gv_station_view_init(GvStationView *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_station_view_get_instance_private(self); } static void gv_station_view_class_init(GvStationViewClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_station_view_finalize; object_class->constructed = gv_station_view_constructed; /* Signals */ signals[SIGNAL_GO_BACK_CLICKED] = g_signal_new("go-back-clicked", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } goodvibes-v0.7.2/src/ui/gv-station-view.h000066400000000000000000000017351414415210700203140ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2020-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_STATION_VIEW gv_station_view_get_type() G_DECLARE_FINAL_TYPE(GvStationView, gv_station_view, GV, STATION_VIEW, GtkBox) /* Methods */ GtkWidget *gv_station_view_new(void); goodvibes-v0.7.2/src/ui/gv-stations-tree-view.c000066400000000000000000000471461414415210700214350ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gv-station-context-menu.h" #include "ui/gv-stations-tree-view.h" /* * Signals */ enum { SIGNAL_POPULATED, /* Number of signals */ SIGNAL_N }; static guint signals[SIGNAL_N]; /* * GObject definitions */ struct _GvStationsTreeViewPrivate { /* Current context menu */ GtkWidget *context_menu; /* Dragging operation in progress */ gboolean is_dragging; GvStation *station_dragged; gint station_new_pos; }; typedef struct _GvStationsTreeViewPrivate GvStationsTreeViewPrivate; struct _GvStationsTreeView { /* Parent instance structure */ GtkTreeView parent_instance; /* Private data */ GvStationsTreeViewPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE(GvStationsTreeView, gv_stations_tree_view, GTK_TYPE_TREE_VIEW) /* * Columns */ enum { STATION_COLUMN, STATION_NAME_COLUMN, STATION_WEIGHT_COLUMN, STATION_STYLE_COLUMN, N_COLUMNS }; /* * Player signal handlers */ static void on_player_notify_station(GvPlayer *player, GParamSpec *pspec G_GNUC_UNUSED, GvStationsTreeView *self) { GtkTreeView *tree_view = GTK_TREE_VIEW(self); GtkTreeModel *tree_model = gtk_tree_view_get_model(tree_view); GvStation *station = gv_player_get_station(player); GtkTreeIter iter; gboolean can_iter; can_iter = gtk_tree_model_get_iter_first(tree_model, &iter); while (can_iter) { GvStation *iter_station; /* Get station from model */ gtk_tree_model_get(tree_model, &iter, STATION_COLUMN, &iter_station, -1); /* Make the current station bold */ if (station == iter_station) gtk_list_store_set(GTK_LIST_STORE(tree_model), &iter, STATION_WEIGHT_COLUMN, PANGO_WEIGHT_BOLD, -1); else gtk_list_store_set(GTK_LIST_STORE(tree_model), &iter, STATION_WEIGHT_COLUMN, PANGO_WEIGHT_NORMAL, -1); /* Unref the station */ if (iter_station) g_object_unref(iter_station); /* Next! */ can_iter = gtk_tree_model_iter_next(tree_model, &iter); } } /* * Station List signal handlers * Needed to update the internal list store when the station list is modified. * (remember the station list might be updated through the D-Bus API). */ static void on_station_list_loaded(GvStationList *station_list, GvStationsTreeView *self) { TRACE("%p, %p", station_list, self); gv_stations_tree_view_populate(self); } static void on_station_list_station_event(GvStationList *station_list, GvStation *station, GvStationsTreeView *self) { TRACE("%p, %p, %p", station_list, station, self); gv_stations_tree_view_populate(self); } static GSignalHandler station_list_handlers[] = { // clang-format off { "loaded", G_CALLBACK(on_station_list_loaded) }, { "station-added", G_CALLBACK(on_station_list_station_event) }, { "station-removed", G_CALLBACK(on_station_list_station_event) }, { "station-modified", G_CALLBACK(on_station_list_station_event) }, { "station-moved", G_CALLBACK(on_station_list_station_event) }, { NULL, NULL } // clang-format on }; /* * Stations tree view row activated * Might be caused by mouse action (single click on the row), * or a gesture (touch on a touchscreen), or a keyboard action * (Enter or similar key pressed). */ static void on_tree_view_row_activated(GvStationsTreeView *self, GtkTreePath *path, GtkTreeViewColumn *column G_GNUC_UNUSED, gpointer data G_GNUC_UNUSED) { GtkTreeView *tree_view = GTK_TREE_VIEW(self); GtkTreeModel *tree_model = gtk_tree_view_get_model(tree_view); GvPlayer *player = gv_core_player; GvStation *station = NULL; GtkTreeIter iter; gboolean ret; DEBUG("Row activated"); /* Get iterator */ ret = gtk_tree_model_get_iter(tree_model, &iter, path); if (ret == FALSE) return; /* Get station */ gtk_tree_model_get(tree_model, &iter, STATION_COLUMN, &station, -1); if (station == NULL) return; /* Play station */ gv_player_set_station(player, station); gv_player_play(player); /* Cleanup */ g_object_unref(station); } /* * Stations Tree View button-press-event, for context menu on right-click */ static void on_context_menu_hide(GtkWidget *widget, GvStationsTreeView *self) { GvStationsTreeViewPrivate *priv = self->priv; /* Sanity check */ if (widget != priv->context_menu) CRITICAL("'hide' signal from unknown context menu"); /* Context menu can be destroyed */ g_object_unref(widget); priv->context_menu = NULL; } static gboolean on_tree_view_button_press_event(GvStationsTreeView *self, GdkEventButton *event, gpointer data G_GNUC_UNUSED) { GvStationsTreeViewPrivate *priv = self->priv; GtkTreeView *tree_view = GTK_TREE_VIEW(self); GtkTreeModel *tree_model = gtk_tree_view_get_model(tree_view); GtkTreePath *path; GvStation *station; GtkWidget *context_menu; DEBUG("Button pressed: %d", event->button); /* Ensure the event happened on the expected window. * According from the doc, we MUST check that. */ if (event->window != gtk_tree_view_get_bin_window(tree_view)) return GDK_EVENT_PROPAGATE; /* Handle only single-click */ if (event->type != GDK_BUTTON_PRESS) return GDK_EVENT_PROPAGATE; /* Handle only right-click */ if (event->button != 3) return GDK_EVENT_PROPAGATE; /* Do nothing if there's already a context menu (this case shouldn't happen) */ if (priv->context_menu) { WARNING("Context menu already exists!"); return GDK_EVENT_PROPAGATE; } /* Get row at this position */ path = NULL; gtk_tree_view_get_path_at_pos(tree_view, event->x, event->y, &path, NULL, NULL, NULL); /* Get corresponding station */ station = NULL; if (path != NULL) { GtkTreeIter iter; gtk_tree_model_get_iter(tree_model, &iter, path); gtk_tree_model_get(tree_model, &iter, STATION_COLUMN, &station, -1); } /* Create the context menu */ if (station) { context_menu = gv_station_context_menu_new_with_station(station); g_object_unref(station); } else { context_menu = gv_station_context_menu_new(); } /* Pop it up */ #if GTK_CHECK_VERSION(3, 22, 0) gtk_menu_popup_at_pointer(GTK_MENU(context_menu), NULL); #else gtk_menu_popup(GTK_MENU(context_menu), NULL, NULL, NULL, NULL, event->button, event->time); #endif /* Save it for later use, handle destruction in callback */ priv->context_menu = g_object_ref_sink(context_menu); g_signal_connect_object(context_menu, "hide", G_CALLBACK(on_context_menu_hide), self, 0); /* Free at last */ gtk_tree_path_free(path); return GDK_EVENT_PROPAGATE; } /* * Stations Tree View drag-and-drop source signal handlers. * We watch these signals to know when a dragging operation is in progress. */ static void on_tree_view_drag_begin(GvStationsTreeView *self, GdkDragContext *context G_GNUC_UNUSED, gpointer data G_GNUC_UNUSED) { GvStationsTreeViewPrivate *priv = self->priv; priv->is_dragging = TRUE; } static void on_tree_view_drag_end(GvStationsTreeView *self, GdkDragContext *context G_GNUC_UNUSED, gpointer data G_GNUC_UNUSED) { GvStationsTreeViewPrivate *priv = self->priv; priv->is_dragging = FALSE; } static gboolean on_tree_view_drag_failed(GvStationsTreeView *self, GdkDragContext *context G_GNUC_UNUSED, GtkDragResult result, gpointer data G_GNUC_UNUSED) { GvStationsTreeViewPrivate *priv = self->priv; DEBUG("Drag failed with result: %d", result); priv->is_dragging = FALSE; return TRUE; } static GSignalHandler tree_view_drag_handlers[] = { // clang-format off { "drag-begin", G_CALLBACK(on_tree_view_drag_begin) }, { "drag-end", G_CALLBACK(on_tree_view_drag_end) }, { "drag-failed", G_CALLBACK(on_tree_view_drag_failed) }, { NULL, NULL } // clang-format on }; /* * Stations List Store signal handlers. * * We watch these signals to be notified when a station is moved * in the list (this is done when user drag'n'drop). * After a station has been moved, we need to forward this change * to the core station list. * * The signal sequence when a station is moved is as follow: * - row-inserted: a new empty row is created * - row-changed: the new row has been populated * - row-deleted: the old row has been deleted */ static void on_list_store_row_inserted(GtkTreeModel *tree_model G_GNUC_UNUSED, GtkTreePath *path, GtkTreeIter *iter G_GNUC_UNUSED, GvStationsTreeView *self) { GvStationsTreeViewPrivate *priv = self->priv; gint *indices; guint position; /* This should only happen when there's a drag-n-drop */ if (priv->is_dragging == FALSE) { WARNING("Not dragging at the moment, ignoring"); return; } /* We expect a clean status */ if (priv->station_dragged != NULL || priv->station_new_pos != -1) { WARNING("Current state is not clean, ignoring"); return; } /* Get position of the new row */ indices = gtk_tree_path_get_indices(path); position = indices[0]; priv->station_new_pos = position; DEBUG("Row inserted at %d", position); } static void on_list_store_row_changed(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, GvStationsTreeView *self) { GvStationsTreeViewPrivate *priv = self->priv; GvStation *station; gint *indices; gint position; /* We only care if it's caused by a drag'n'drop */ if (priv->is_dragging == FALSE) return; /* We expect the indice to be the one registered previously * in 'row-inserted' signal. */ indices = gtk_tree_path_get_indices(path); position = indices[0]; if (position != priv->station_new_pos) { WARNING("Unexpected position %d, doesn't match %d", position, priv->station_new_pos); return; } /* Get station */ gtk_tree_model_get(tree_model, iter, STATION_COLUMN, &station, -1); /* Save it */ priv->station_dragged = station; /* Freedom for the braves */ g_object_unref(station); DEBUG("Row changed at %d", position); } static void on_list_store_row_deleted(GtkTreeModel *tree_model G_GNUC_UNUSED, GtkTreePath *path G_GNUC_UNUSED, GvStationsTreeView *self) { GvStationsTreeViewPrivate *priv = self->priv; GtkTreeView *tree_view = GTK_TREE_VIEW(self); GvStationList *station_list = gv_core_station_list; GtkTreeSelection *select; GvStation *station; guint indice_inserted; /* End of drag operation, let's commit that to station list */ station = priv->station_dragged; if (station == NULL) { WARNING("Station dragged is null, wtf?"); return; } /* Move station in the station list */ indice_inserted = priv->station_new_pos; g_signal_handlers_block(station_list, station_list_handlers, self); gv_station_list_move(station_list, station, indice_inserted); g_signal_handlers_unblock(station_list, station_list_handlers, self); DEBUG("Row deleted, station moved at %d", indice_inserted); /* Clean status */ priv->station_dragged = NULL; priv->station_new_pos = -1; /* Reset selection, as GTK doesn't do it itself, hence the column that * was selected (ie. highlighted as we're in hover selection mode) * before the drag'n'drop is highligted again after the drag'n'drop. */ select = gtk_tree_view_get_selection(tree_view); gtk_tree_selection_unselect_all(select); } static GSignalHandler list_store_handlers[] = { // clang-format off { "row-inserted", G_CALLBACK(on_list_store_row_inserted) }, { "row-changed", G_CALLBACK(on_list_store_row_changed) }, { "row-deleted", G_CALLBACK(on_list_store_row_deleted) }, { NULL, NULL } // clang-format on }; /* * Helpers */ static void station_cell_data_func(GtkTreeViewColumn *tree_column G_GNUC_UNUSED, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data G_GNUC_UNUSED) { gchar *station_name; PangoWeight station_weight; PangoStyle station_style; /* According to the doc, there should be nothing heavy in this function, * since it's called intensively. No UTF-8 conversion, for example. */ gtk_tree_model_get(tree_model, iter, STATION_NAME_COLUMN, &station_name, STATION_WEIGHT_COLUMN, &station_weight, STATION_STYLE_COLUMN, &station_style, -1); g_object_set(cell, "text", station_name, "weight", station_weight, "style", station_style, NULL); g_free(station_name); } /* * Public methods */ void gv_stations_tree_view_populate(GvStationsTreeView *self) { GtkTreeView *tree_view = GTK_TREE_VIEW(self); GtkTreeModel *tree_model = gtk_tree_view_get_model(tree_view); GtkListStore *list_store = GTK_LIST_STORE(tree_model); GvStationList *station_list = gv_core_station_list; GvPlayer *player = gv_core_player; TRACE("%p", self); /* Block list store handlers */ g_signal_handlers_block(list_store, list_store_handlers, self); /* Make station list empty */ gtk_list_store_clear(list_store); /* Handle the special-case: empty station list */ if (gv_station_list_length(station_list) == 0) { GtkTreeIter tree_iter; /* Populate */ gtk_list_store_append(list_store, &tree_iter); gtk_list_store_set(list_store, &tree_iter, STATION_COLUMN, NULL, STATION_NAME_COLUMN, "Right click to add station", STATION_WEIGHT_COLUMN, PANGO_WEIGHT_NORMAL, STATION_STYLE_COLUMN, PANGO_STYLE_ITALIC, -1); /* Configure behavior */ gtk_tree_view_set_hover_selection(tree_view, FALSE); gtk_tree_view_set_activate_on_single_click(tree_view, FALSE); } else { GvStation *current_station = gv_player_get_station(player); GvStation *station; GvStationListIter *iter; /* Populate menu with every station */ iter = gv_station_list_iter_new(station_list); while (gv_station_list_iter_loop(iter, &station)) { GtkTreeIter tree_iter; const gchar *station_name; PangoWeight weight; station_name = gv_station_get_name_or_uri(station); if (station == current_station) weight = PANGO_WEIGHT_BOLD; else weight = PANGO_WEIGHT_NORMAL; gtk_list_store_append(list_store, &tree_iter); gtk_list_store_set(list_store, &tree_iter, STATION_COLUMN, station, STATION_NAME_COLUMN, station_name, STATION_WEIGHT_COLUMN, weight, STATION_STYLE_COLUMN, PANGO_STYLE_NORMAL, -1); } gv_station_list_iter_free(iter); /* Configure behavior */ gtk_tree_view_set_hover_selection(tree_view, TRUE); gtk_tree_view_set_activate_on_single_click(tree_view, TRUE); } /* Unblock list store handlers */ g_signal_handlers_unblock(list_store, list_store_handlers, self); /* Emit a signal */ g_signal_emit(self, signals[SIGNAL_POPULATED], 0); } gboolean gv_stations_tree_view_has_context_menu(GvStationsTreeView *self) { GvStationsTreeViewPrivate *priv = self->priv; return priv->context_menu ? TRUE : FALSE; } GtkWidget * gv_stations_tree_view_new(void) { return g_object_new(GV_TYPE_STATIONS_TREE_VIEW, NULL); } /* * GObject methods */ static void gv_stations_tree_view_constructed(GObject *object) { GtkTreeView *tree_view = GTK_TREE_VIEW(object); GvStationsTreeView *self = GV_STATIONS_TREE_VIEW(object); /* Hide headers */ gtk_tree_view_set_headers_visible(tree_view, FALSE); /* Enable hover selection mode, and single click activation */ gtk_tree_view_set_hover_selection(tree_view, TRUE); gtk_tree_view_set_activate_on_single_click(tree_view, TRUE); /* Allow re-ordering. The tree view then becomes a drag source and * a drag destination, and all the drag-and-drop mess is handled * by the tree view. We will just have to watch the drag-* signals. */ gtk_tree_view_set_reorderable(tree_view, TRUE); /* Horizontally, the tree view grows as wide as the longer station name. * I'm OK with this behavior, let it be. */ /* Vertically, the tree view grows forever. If someone has too many stations, * it might cause a problem. * I tried to put the tree view inside a scrolled window, but it creates more * problems than it solves, mainly because then we have to assign a fixed size * to the tree view. Finding the appropriate size (that would be the natural, * expanded size if NOT within a scrolled window, OR the screen height if too * many stations) seems VERY VERY tricky and slippery... */ /* * Create the stations list store. It has 4 columns: * - the station object * - the station represented by a string (for displaying) * - the station's font weight (bold characters for current station) * - the station's font style (italic characters if no station) */ /* Create a new list store */ GtkListStore *list_store; list_store = gtk_list_store_new(4, G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT); /* Associate it with the tree view */ gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(list_store)); g_object_unref(list_store); /* * Create the column that will be displayed */ /* Create a renderer */ GtkCellRenderer *renderer; renderer = gtk_cell_renderer_text_new(); g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); /* Create a column that uses this renderer */ GtkTreeViewColumn *column; column = gtk_tree_view_column_new_with_attributes("Station", renderer, NULL); /* Set the function that will render this column */ gtk_tree_view_column_set_cell_data_func(column, renderer, station_cell_data_func, NULL, NULL); /* Append the column */ gtk_tree_view_append_column(tree_view, column); /* * Tree View signal handlers */ /* Left click or keyboard */ g_signal_connect_object(tree_view, "row-activated", G_CALLBACK(on_tree_view_row_activated), NULL, 0); /* We handle the right-click here */ g_signal_connect_object(tree_view, "button-press-event", G_CALLBACK(on_tree_view_button_press_event), NULL, 0); /* Drag-n-drop signal handlers. * We need to watch it just to know when a drag-n-drop is in progress. */ g_signal_handlers_connect_object(tree_view, tree_view_drag_handlers, NULL, 0); /* * List Store signal handlers */ /* If stations are re-ordered, we have to propagate to the core */ g_signal_handlers_connect_object(list_store, list_store_handlers, self, 0); /* * Core signal handlers */ GvPlayer *player = gv_core_player; GvStationList *station_list = gv_core_station_list; g_signal_connect_object(player, "notify::station", G_CALLBACK(on_player_notify_station), self, 0); g_signal_handlers_connect_object(station_list, station_list_handlers, self, 0); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_stations_tree_view, object); } static void gv_stations_tree_view_init(GvStationsTreeView *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_stations_tree_view_get_instance_private(self); /* Initialize internal state */ self->priv->station_new_pos = -1; } static void gv_stations_tree_view_class_init(GvStationsTreeViewClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->constructed = gv_stations_tree_view_constructed; /* Signals */ signals[SIGNAL_POPULATED] = g_signal_new("populated", G_TYPE_FROM_CLASS(class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } goodvibes-v0.7.2/src/ui/gv-stations-tree-view.h000066400000000000000000000022671414415210700214350ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_STATIONS_TREE_VIEW gv_stations_tree_view_get_type() G_DECLARE_FINAL_TYPE(GvStationsTreeView, gv_stations_tree_view, \ GV, STATIONS_TREE_VIEW, GtkTreeView) /* Methods */ GtkWidget *gv_stations_tree_view_new(void); gboolean gv_stations_tree_view_has_context_menu(GvStationsTreeView *self); void gv_stations_tree_view_populate (GvStationsTreeView *self); goodvibes-v0.7.2/src/ui/gv-status-icon.c000066400000000000000000000354651414415210700201360ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "base/glib-object-additions.h" #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gtk-additions.h" #include "ui/gv-main-window.h" #include "ui/gv-ui-enum-types.h" #include "ui/gv-ui-helpers.h" #include "ui/gv-ui-internal.h" #include "ui/gv-status-icon.h" /* This file uses the GtkStatusIcon object, which is deprecated in GTK 3.x. * Let's silence the warning to keep the compiler output readable. */ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #define UI_RESOURCE_PATH GV_APPLICATION_PATH "/Ui/status-icon-menu.ui" #define ICON_MIN_SIZE 16 /* * Properties */ #define DEFAULT_MIDDLE_CLICK_ACTION GV_STATUS_ICON_MIDDLE_CLICK_TOGGLE #define DEFAULT_SCROLL_ACTION GV_STATUS_ICON_SCROLL_STATION enum { /* Reserved */ PROP_0, /* Properties */ PROP_MAIN_WINDOW, PROP_MIDDLE_CLICK_ACTION, PROP_SCROLL_ACTION, /* Number of properties */ PROP_N }; static GParamSpec *properties[PROP_N]; /* * GObject definitions */ struct _GvStatusIconPrivate { /* Properties */ GtkWindow *main_window; GvStatusIconMiddleClick middle_click_action; GvStatusIconScroll scroll_action; /* Right-click menu */ GtkWidget *popup_menu; /* Status icon */ GtkStatusIcon *status_icon; guint status_icon_size; }; typedef struct _GvStatusIconPrivate GvStatusIconPrivate; struct _GvStatusIcon { /* Parent instance structure */ GObject parent_instance; /* Private data */ GvStatusIconPrivate *priv; }; static void gv_status_icon_configurable_interface_init(GvConfigurableInterface *iface); G_DEFINE_TYPE_WITH_CODE(GvStatusIcon, gv_status_icon, G_TYPE_OBJECT, G_ADD_PRIVATE(GvStatusIcon) G_IMPLEMENT_INTERFACE(GV_TYPE_CONFIGURABLE, gv_status_icon_configurable_interface_init)) /* * Helpers */ static void gv_status_icon_update_icon_pixbuf(GvStatusIcon *self) { GvStatusIconPrivate *priv = self->priv; GtkStatusIcon *status_icon = priv->status_icon; gtk_status_icon_set_from_icon_name(status_icon, GV_ICON_NAME); } static void gv_status_icon_update_icon_tooltip(GvStatusIcon *self) { GvStatusIconPrivate *priv = self->priv; GtkStatusIcon *status_icon = priv->status_icon; GvPlayer *player = gv_core_player; GvPlaybackState playback_state; const gchar *playback_state_str; guint player_volume; gboolean player_muted; gchar *player_str; GvStation *station; gchar *station_str; GvMetadata *metadata; gchar *metadata_str; gchar *tooltip; /* Player */ playback_state = gv_player_get_playback_state(player); playback_state_str = gv_playback_state_to_string(playback_state); player_volume = gv_player_get_volume(player); player_muted = gv_player_get_mute(player); if (player_muted) player_str = g_strdup_printf("%s (%s, %s)", g_get_application_name(), playback_state_str, _("muted")); else player_str = g_strdup_printf("%s (%s, %s %u%%)", g_get_application_name(), playback_state_str, _("vol."), player_volume); /* Current station */ station = gv_player_get_station(player); if (station) station_str = gv_station_make_name(station, TRUE); else station_str = g_strdup_printf("%s", _("No station")); /* Metadata */ metadata = gv_player_get_metadata(player); if (metadata) metadata_str = gv_metadata_make_title_artist(metadata, TRUE); else metadata_str = g_strdup_printf("%s", _("No metadata")); /* Set the tooltip */ tooltip = g_strdup_printf("%s\n%s\n%s", player_str, station_str, metadata_str); gtk_status_icon_set_tooltip_markup(status_icon, tooltip); /* Free */ g_free(player_str); g_free(station_str); g_free(metadata_str); g_free(tooltip); } static void gv_status_icon_update_icon(GvStatusIcon *self) { /* Update icon */ gv_status_icon_update_icon_pixbuf(self); /* Update tooltip */ gv_status_icon_update_icon_tooltip(self); /* Set visible */ gtk_status_icon_set_visible(self->priv->status_icon, TRUE); } /* * GtkStatusIcon signal handlers */ static void on_activate(GtkStatusIcon *status_icon G_GNUC_UNUSED, GvStatusIcon *self G_GNUC_UNUSED) { GvStatusIconPrivate *priv = self->priv; GtkWindow *window = priv->main_window; if (gtk_window_is_active(window)) gtk_window_close(window); else gtk_window_present(window); } static void on_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, GvStatusIcon *self) { GvStatusIconPrivate *priv = self->priv; GtkMenu *menu = GTK_MENU(priv->popup_menu); #if GTK_CHECK_VERSION(3, 22, 0) (void) status_icon; (void) button; (void) activate_time; gtk_menu_popup_at_pointer(menu, NULL); #else gtk_menu_popup(menu, NULL, NULL, gtk_status_icon_position_menu, status_icon, button, activate_time); #endif } static gboolean on_button_release_event(GtkStatusIcon *status_icon G_GNUC_UNUSED, GdkEventButton *event, GvStatusIcon *self G_GNUC_UNUSED) { GvStatusIconPrivate *priv = self->priv; GvPlayer *player = gv_core_player; /* Here we only handle the middle-click */ if (event->button != 2) return GDK_EVENT_PROPAGATE; switch (priv->middle_click_action) { case GV_STATUS_ICON_MIDDLE_CLICK_TOGGLE: gv_player_toggle(player); break; case GV_STATUS_ICON_MIDDLE_CLICK_MUTE: gv_player_toggle_mute(player); break; default: CRITICAL("Unhandled middle-click action: %d", priv->middle_click_action); break; } return GDK_EVENT_PROPAGATE; } static gboolean on_scroll_event(GtkStatusIcon *status_icon G_GNUC_UNUSED, GdkEventScroll *event, GvStatusIcon *self G_GNUC_UNUSED) { GvStatusIconPrivate *priv = self->priv; GvPlayer *player = gv_core_player; switch (priv->scroll_action) { case GV_STATUS_ICON_SCROLL_STATION: switch (event->direction) { case GDK_SCROLL_DOWN: gv_player_next(player); break; case GDK_SCROLL_UP: gv_player_prev(player); break; default: break; } break; case GV_STATUS_ICON_SCROLL_VOLUME: switch (event->direction) { case GDK_SCROLL_DOWN: gv_player_lower_volume(player); break; case GDK_SCROLL_UP: gv_player_raise_volume(player); break; default: break; } break; default: CRITICAL("Unhandled scroll action: %d", priv->scroll_action); break; } return GDK_EVENT_PROPAGATE; } static gboolean on_size_changed(GtkStatusIcon *status_icon G_GNUC_UNUSED, gint size, GvStatusIcon *self G_GNUC_UNUSED) { DEBUG("Status icon size is now %d", size); gv_status_icon_update_icon(self); return FALSE; } /* * Goodvibes signal handlers */ static void on_player_notify(GvPlayer *player, GParamSpec *pspec, GvStatusIcon *self) { const gchar *property_name = g_param_spec_get_name(pspec); TRACE("%p, %s, %p", player, property_name, self); if (!g_strcmp0(property_name, "playback-state") || !g_strcmp0(property_name, "volume") || !g_strcmp0(property_name, "mute") || !g_strcmp0(property_name, "station") || !g_strcmp0(property_name, "metadata")) { gv_status_icon_update_icon(self); return; } } /* * Property accessors */ static void gv_status_icon_set_main_window(GvStatusIcon *self, GtkWindow *main_window) { GvStatusIconPrivate *priv = self->priv; /* Construct-only property */ g_assert_null(priv->main_window); priv->main_window = g_object_ref(main_window); } GvStatusIconMiddleClick gv_status_icon_get_middle_click_action(GvStatusIcon *self) { return self->priv->middle_click_action; } void gv_status_icon_set_middle_click_action(GvStatusIcon *self, GvStatusIconMiddleClick action) { GvStatusIconPrivate *priv = self->priv; if (priv->middle_click_action == action) return; priv->middle_click_action = action; g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_MIDDLE_CLICK_ACTION]); } GvStatusIconScroll gv_status_icon_get_scroll_action(GvStatusIcon *self) { return self->priv->scroll_action; } void gv_status_icon_set_scroll_action(GvStatusIcon *self, GvStatusIconScroll action) { GvStatusIconPrivate *priv = self->priv; if (priv->scroll_action == action) return; priv->scroll_action = action; g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_SCROLL_ACTION]); } static void gv_status_icon_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvStatusIcon *self = GV_STATUS_ICON(object); TRACE_GET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_MIDDLE_CLICK_ACTION: g_value_set_enum(value, gv_status_icon_get_middle_click_action(self)); break; case PROP_SCROLL_ACTION: g_value_set_enum(value, gv_status_icon_get_scroll_action(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void gv_status_icon_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvStatusIcon *self = GV_STATUS_ICON(object); TRACE_SET_PROPERTY(object, property_id, value, pspec); switch (property_id) { case PROP_MAIN_WINDOW: gv_status_icon_set_main_window(self, g_value_get_object(value)); break; case PROP_MIDDLE_CLICK_ACTION: gv_status_icon_set_middle_click_action(self, g_value_get_enum(value)); break; case PROP_SCROLL_ACTION: gv_status_icon_set_scroll_action(self, g_value_get_enum(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* * Public methods */ GvStatusIcon * gv_status_icon_new(GtkWindow *main_window) { return g_object_new(GV_TYPE_STATUS_ICON, "main-window", main_window, NULL); } /* * GvConfigurable interface */ static void gv_status_icon_configure(GvConfigurable *configurable) { GvStatusIcon *self = GV_STATUS_ICON(configurable); TRACE("%p", self); g_assert(gv_ui_settings); g_settings_bind(gv_ui_settings, "middle-click-action", self, "middle-click-action", G_SETTINGS_BIND_DEFAULT); g_settings_bind(gv_ui_settings, "scroll-action", self, "scroll-action", G_SETTINGS_BIND_DEFAULT); } static void gv_status_icon_configurable_interface_init(GvConfigurableInterface *iface) { iface->configure = gv_status_icon_configure; } /* * GObject methods */ static void gv_status_icon_finalize(GObject *object) { GvStatusIcon *self = GV_STATUS_ICON(object); GvStatusIconPrivate *priv = self->priv; TRACE("%p", object); /* Release main window */ g_object_unref(priv->main_window); /* Destroy popup menu */ g_object_unref(priv->popup_menu); /* Unref the status icon */ g_object_unref(priv->status_icon); /* Chain up */ G_OBJECT_CHAINUP_FINALIZE(gv_status_icon, object); } static void gv_status_icon_setup_menu(GvStatusIcon *self) { GvStatusIconPrivate *priv = self->priv; GtkWindow *main_window = priv->main_window; GtkBuilder *builder; GMenuModel *menu_model; GtkWidget *menu; /* Load the menu model */ builder = gtk_builder_new_from_resource(UI_RESOURCE_PATH); /* Build the menu */ menu_model = G_MENU_MODEL(gtk_builder_get_object(builder, "status-icon-menu")); menu = gtk_menu_new_from_model(menu_model); /* Attach to main window */ gtk_menu_attach_to_widget(GTK_MENU(menu), GTK_WIDGET(main_window), NULL); /* Save a pointer */ priv->popup_menu = g_object_ref_sink(menu); /* Cleanup */ g_object_unref(builder); } static void gv_status_icon_constructed(GObject *object) { GvStatusIcon *self = GV_STATUS_ICON(object); GvStatusIconPrivate *priv = self->priv; GvPlayer *player = gv_core_player; GtkStatusIcon *status_icon; /* Ensure construct-only properties have been set */ g_assert_nonnull(priv->main_window); /* Create the popup menu */ gv_status_icon_setup_menu(self); /* Create the status icon */ status_icon = gtk_status_icon_new(); /* Connect status icon signal handlers */ g_signal_connect_object(status_icon, "activate", /* Left click */ G_CALLBACK(on_activate), self, 0); g_signal_connect_object(status_icon, "popup-menu", /* Right click */ G_CALLBACK(on_popup_menu), self, 0); g_signal_connect_object(status_icon, "button-release-event", /* Middle click */ G_CALLBACK(on_button_release_event), self, 0); g_signal_connect_object(status_icon, "scroll_event", /* Mouse scroll */ G_CALLBACK(on_scroll_event), self, 0); g_signal_connect_object(status_icon, "size-changed", /* Change of size */ G_CALLBACK(on_size_changed), self, 0); /* Save to private data */ priv->status_icon = status_icon; priv->status_icon_size = ICON_MIN_SIZE; /* Connect core signal handlers */ g_signal_connect_object(player, "notify", G_CALLBACK(on_player_notify), self, 0); /* Chain up */ G_OBJECT_CHAINUP_CONSTRUCTED(gv_status_icon, object); } static void gv_status_icon_init(GvStatusIcon *self) { TRACE("%p", self); /* Initialize private pointer */ self->priv = gv_status_icon_get_instance_private(self); } static void gv_status_icon_class_init(GvStatusIconClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); TRACE("%p", class); /* Override GObject methods */ object_class->finalize = gv_status_icon_finalize; object_class->constructed = gv_status_icon_constructed; /* Properties */ object_class->get_property = gv_status_icon_get_property; object_class->set_property = gv_status_icon_set_property; properties[PROP_MAIN_WINDOW] = g_param_spec_object("main-window", "Main window", NULL, GTK_TYPE_WINDOW, GV_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_MIDDLE_CLICK_ACTION] = g_param_spec_enum("middle-click-action", "Middle click action", NULL, GV_TYPE_STATUS_ICON_MIDDLE_CLICK, DEFAULT_MIDDLE_CLICK_ACTION, GV_PARAM_READWRITE); properties[PROP_SCROLL_ACTION] = g_param_spec_enum("scroll-action", "Scroll action", NULL, GV_TYPE_STATUS_ICON_SCROLL, DEFAULT_SCROLL_ACTION, GV_PARAM_READWRITE); g_object_class_install_properties(object_class, PROP_N, properties); /* Register transform function */ g_value_register_transform_func(GV_TYPE_STATUS_ICON_MIDDLE_CLICK, G_TYPE_STRING, gv_value_transform_enum_string); g_value_register_transform_func(G_TYPE_STRING, GV_TYPE_STATUS_ICON_MIDDLE_CLICK, gv_value_transform_string_enum); g_value_register_transform_func(GV_TYPE_STATUS_ICON_SCROLL, G_TYPE_STRING, gv_value_transform_enum_string); g_value_register_transform_func(G_TYPE_STRING, GV_TYPE_STATUS_ICON_SCROLL, gv_value_transform_string_enum); } goodvibes-v0.7.2/src/ui/gv-status-icon.h000066400000000000000000000035051414415210700201310ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include #include /* GObject declarations */ #define GV_TYPE_STATUS_ICON gv_status_icon_get_type() G_DECLARE_FINAL_TYPE(GvStatusIcon, gv_status_icon, GV, STATUS_ICON, GObject) /* Data types */ typedef enum { GV_STATUS_ICON_MIDDLE_CLICK_UNDEFINED, GV_STATUS_ICON_MIDDLE_CLICK_TOGGLE, GV_STATUS_ICON_MIDDLE_CLICK_MUTE, } GvStatusIconMiddleClick; typedef enum { GV_STATUS_ICON_SCROLL_UNDEFINED, GV_STATUS_ICON_SCROLL_STATION, GV_STATUS_ICON_SCROLL_VOLUME, } GvStatusIconScroll; /* Methods */ GvStatusIcon *gv_status_icon_new(GtkWindow *main_window); /* Property accessors */ GvStatusIconMiddleClick gv_status_icon_get_middle_click_action(GvStatusIcon *self); void gv_status_icon_set_middle_click_action(GvStatusIcon *self, GvStatusIconMiddleClick action); GvStatusIconScroll gv_status_icon_get_scroll_action (GvStatusIcon *self); void gv_status_icon_set_scroll_action (GvStatusIcon *self, GvStatusIconScroll action); goodvibes-v0.7.2/src/ui/gv-ui-helpers.c000066400000000000000000000033311414415210700177250ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include void gv_value_transform_enum_string(const GValue *src_value, GValue *dest_value) { GEnumClass *enum_class; GEnumValue *enum_value; enum_class = g_type_class_ref(G_VALUE_TYPE(src_value)); enum_value = g_enum_get_value(enum_class, g_value_get_enum(src_value)); if (enum_value) g_value_set_static_string(dest_value, enum_value->value_nick); else { /* Assume zero holds the invalid value */ enum_value = g_enum_get_value(enum_class, 0); g_value_set_static_string(dest_value, enum_value->value_nick); } g_type_class_unref(enum_class); } void gv_value_transform_string_enum(const GValue *src_value, GValue *dest_value) { GEnumClass *enum_class; GEnumValue *enum_value; enum_class = g_type_class_ref(G_VALUE_TYPE(dest_value)); enum_value = g_enum_get_value_by_nick(enum_class, g_value_get_string(src_value)); if (enum_value) g_value_set_enum(dest_value, enum_value->value); else /* Assume zero holds the invalid value */ g_value_set_enum(dest_value, 0); g_type_class_unref(enum_class); } goodvibes-v0.7.2/src/ui/gv-ui-helpers.h000066400000000000000000000016221414415210700177330ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ #pragma once #include void gv_value_transform_enum_string(const GValue *src_value, GValue *dest_value); void gv_value_transform_string_enum(const GValue *src_value, GValue *dest_value); goodvibes-v0.7.2/src/ui/gv-ui-internal.h000066400000000000000000000024231414415210700201050ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ /* * This header contains definitions to be used internally by ui files */ #pragma once #include #include "ui/gv-status-icon.h" #include "ui/gv-main-window-manager.h" /* Global variables */ extern GSettings *gv_ui_settings; extern GvStatusIcon *gv_ui_status_icon; extern GvMainWindow *gv_ui_main_window; /* * Visual layout, according to: * https://developer.gnome.org/hig/stable/visual-layout.html */ #define GV_UI_WINDOW_MARGIN 18 #define GV_UI_GROUP_SPACING 18 #define GV_UI_ELEM_SPACING 6 #define GV_UI_COLUMN_SPACING 12 #define GV_UI_MAIN_WINDOW_MARGIN 6 goodvibes-v0.7.2/src/ui/gv-ui.c000066400000000000000000000125251414415210700162720ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "base/gv-base.h" #include "core/gv-core.h" #include "ui/gtk-additions.h" #include "ui/gv-about-dialog.h" #include "ui/gv-keyboard-shortcuts-window.h" #include "ui/gv-main-window-manager.h" #include "ui/gv-main-window-standalone.h" #include "ui/gv-main-window-status-icon.h" #include "ui/gv-prefs-window.h" #include "ui/gv-station-dialog.h" #include "ui/gv-status-icon.h" #include "ui/resources/gv-ui-resources.h" #define UI_SCHEMA_ID_SUFFIX "Ui" /* * Public variables */ GSettings *gv_ui_settings; GvStatusIcon *gv_ui_status_icon; GvMainWindow *gv_ui_main_window; GvMainWindowManager *gv_ui_main_window_manager; /* * Private variables */ static GList *ui_objects; /* * Underlying graphical toolkit */ GOptionGroup * gv_ui_toolkit_init_get_option_group(void) { /* Very not sure about the argument to pass here. From my experience: * - if we don't use gtk_init(), we should pass TRUE. * - if we use gtk_init(), passing FALSE is ok. * Since we use GtkApplication which calls gtk_init() internally, * let's pass FALSE and pray that it works. */ return gtk_get_option_group(FALSE); } const gchar * gv_ui_toolkit_runtime_version_string(void) { return gtk_get_runtime_version_string(); } const gchar * gv_ui_toolkit_compile_version_string(void) { return gtk_get_compile_version_string(); } /* * Ui public functions */ void gv_ui_hide(void) { /* In status icon mode, do nothing */ if (gv_ui_status_icon) return; gtk_widget_hide(GTK_WIDGET(gv_ui_main_window)); } void gv_ui_present_about(void) { gv_show_about_dialog(GTK_WINDOW(gv_ui_main_window), gv_core_audio_backend_runtime_version_string(), gv_ui_toolkit_runtime_version_string()); } void gv_ui_present_keyboard_shortcuts(void) { gv_show_keyboard_shortcuts_window(GTK_WINDOW(gv_ui_main_window)); } void gv_ui_present_preferences(void) { GtkWindow *parent; parent = gv_ui_status_icon ? NULL : GTK_WINDOW(gv_ui_main_window); gv_show_prefs_window(parent); } void gv_ui_present_main(void) { /* In status icon mode, do nothing */ if (gv_ui_status_icon) return; gtk_window_present(GTK_WINDOW(gv_ui_main_window)); } void gv_ui_present_add_station(void) { GvStationList *station_list = gv_core_station_list; GvStation *station; station = gv_show_add_station_dialog(GTK_WINDOW(gv_ui_main_window)); if (station) gv_station_list_append(station_list, station); } void gv_ui_play_stop(void) { GvPlayer *player = gv_core_player; gv_player_toggle(player); } void gv_ui_configure(void) { GList *item; /* Configure each object that is configurable */ for (item = ui_objects; item; item = item->next) { GObject *object = item->data; if (!GV_IS_CONFIGURABLE(object)) continue; gv_configurable_configure(GV_CONFIGURABLE(object)); } } void gv_ui_cleanup(void) { GList *item; /* Windows must be destroyed with gtk_widget_destroy(). * Forget about gtk_window_close() here, which seems to be asynchronous. * Read the doc: * https://developer.gnome.org/gtk3/stable/GtkWindow.html#gtk-window-new */ /* Destroy public ui objects */ ui_objects = g_list_reverse(ui_objects); for (item = ui_objects; item; item = item->next) { GObject *object = item->data; if (GTK_IS_WINDOW(object)) gtk_widget_destroy(GTK_WIDGET(object)); else g_object_unref(object); } g_list_free(ui_objects); } void gv_ui_init(GApplication *app, gboolean status_icon_mode) { GList *item; /* Create resource * * The resource object is a bit special, it's a static resource, * it lives in the data segment, which is read-only. So calling * g_object_ref() on it causes a segfault, for example. * * Actually, we don't even need to keep a reference to this object. * All we want is to initialize the resource, which is triggered * by the first access (lazy init). */ gv_ui_get_resource(); /* Create ui objects */ gv_ui_settings = gv_get_settings(UI_SCHEMA_ID_SUFFIX); ui_objects = g_list_append(ui_objects, gv_ui_settings); if (status_icon_mode == TRUE) { gv_ui_main_window = gv_main_window_status_icon_new(app); ui_objects = g_list_append(ui_objects, gv_ui_main_window); gv_ui_status_icon = gv_status_icon_new(GTK_WINDOW(gv_ui_main_window)); ui_objects = g_list_append(ui_objects, gv_ui_status_icon); } else { GvMainWindowManager *mgr; gv_ui_main_window = gv_main_window_standalone_new(app); ui_objects = g_list_append(ui_objects, gv_ui_main_window); mgr = gv_main_window_manager_new(gv_ui_main_window); ui_objects = g_list_append(ui_objects, mgr); gv_ui_status_icon = NULL; } /* Register objects in the base */ for (item = ui_objects; item; item = item->next) { GObject *object = G_OBJECT(item->data); gv_base_register_object(object); } } goodvibes-v0.7.2/src/ui/gv-ui.h000066400000000000000000000027111414415210700162730ustar00rootroot00000000000000/* * Goodvibes Radio Player * * Copyright (C) 2015-2021 Arnaud Rebillout * * SPDX-License-Identifier: GPL-3.0-only * * 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, version 3. * * 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 . */ /* * This header contains definitions to be used by ui users */ #pragma once #include /* Global variables */ extern GtkWindow *gv_ui_main_window; /* Functions */ void gv_ui_init (GApplication *app, gboolean status_icon_mode); void gv_ui_cleanup (void); void gv_ui_configure(void); void gv_ui_play_stop(void); void gv_ui_present_add_station(void); void gv_ui_present_main (void); void gv_ui_present_preferences(void); void gv_ui_present_keyboard_shortcuts(void); void gv_ui_present_about (void); void gv_ui_hide (void); /* Underlying toolkit */ GOptionGroup *gv_ui_toolkit_init_get_option_group (void); const gchar *gv_ui_toolkit_runtime_version_string(void); const gchar *gv_ui_toolkit_compile_version_string(void); goodvibes-v0.7.2/src/ui/meson.build000066400000000000000000000021441414415210700172350ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only # Subdirectories subdir('resources') # Sources and dependencies ui_sources = [ 'gtk-additions.c', 'gv-about-dialog.c', 'gv-keyboard-shortcuts-window.c', 'gv-main-window.c', 'gv-main-window-manager.c', 'gv-main-window-standalone.c', 'gv-main-window-status-icon.c', 'gv-playlist-view.c', 'gv-prefs-window.c', 'gv-station-context-menu.c', 'gv-station-dialog.c', 'gv-station-view.c', 'gv-stations-tree-view.c', 'gv-status-icon.c', 'gv-ui.c', 'gv-ui-helpers.c', ] ui_dependencies = [ glib_dep, gobject_dep, gio_dep, gtk_dep, gvbase_dep, gvcore_dep, ] ui_enum_headers = [ 'gv-main-window.h', 'gv-main-window-standalone.h', 'gv-status-icon.h', ] ui_enums = gnome.mkenums_simple('gv-ui-enum-types', sources: ui_enum_headers ) ui_enum_h = ui_enums[1] # Library definition gvui = static_library('gvui', sources: [ ui_enums, ui_resources, ui_sources ], dependencies: ui_dependencies, include_directories: root_inc, ) gvui_dep = declare_dependency( dependencies: ui_dependencies, sources: [ ui_enum_h ], link_with: gvui, ) goodvibes-v0.7.2/src/ui/resources/000077500000000000000000000000001414415210700171045ustar00rootroot00000000000000goodvibes-v0.7.2/src/ui/resources/app-menu.ui000066400000000000000000000024661414415210700211750ustar00rootroot00000000000000
Play/Stop app.play-stop Add Station app.add-station
Preferences app.preferences
Keyboard Shortcuts app.keyboard-shortcuts Online Help app.help About app.about Close app.close-ui Quit app.quit
goodvibes-v0.7.2/src/ui/resources/io.gitlab.Goodvibes.Ui.gresource.xml.in000066400000000000000000000010671414415210700264000ustar00rootroot00000000000000 app-menu.ui playlist-view.glade prefs-window.glade shortcuts-window.ui station-dialog.glade station-view.glade status-icon-menu.ui goodvibes-v0.7.2/src/ui/resources/meson.build000066400000000000000000000015221414415210700212460ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-3.0-only # Resources ui_resources_conf = configuration_data() ui_resources_conf.set('path', gv_application_path) ui_resources_file = configure_file( input: gv_application_id + '.Ui.gresource.xml.in', output: gv_application_id + '.Ui.gresource.xml', configuration: ui_resources_conf, ) ui_resources = gnome.compile_resources('gv-ui-resources', ui_resources_file, c_name: 'gv_ui', export: true, ) # Validation ui_glade_files = [ 'app-menu.ui', 'playlist-view.glade', 'prefs-window.glade', 'shortcuts-window.ui', 'station-dialog.glade', 'station-view.glade', 'status-icon-menu.ui', ] validator = find_program(join_paths(meson.source_root(), 'scripts/meson/validate-ui-file.sh')) foreach file : ui_glade_files test('Validate ' + file, validator, args: [ files(file) ], ) endforeach goodvibes-v0.7.2/src/ui/resources/playlist-view.glade000066400000000000000000000226451414415210700227240ustar00rootroot00000000000000 True False go-next-symbolic True False media-skip-forward-symbolic True False media-playback-start-symbolic True False media-skip-backward-symbolic True False media-playlist-repeat-symbolic True False media-playlist-shuffle-symbolic True False vertical True False True False True No station selected end 0 0 True False True Stopped center True end 2 0 1 True True True go_next_image none True 1 0 2 False True 0 True False True True True play_image none True False True 0 True True True prev_image none True False True 1 True True True next_image none True False True 2 True True True none vertical audio-volume-muted-symbolic audio-volume-high-symbolic audio-volume-low-symbolic audio-volume-medium-symbolic False False False False False True end 3 True True True shuffle_image none True False True end 4 True True True repeat_image none True False True end 5 False True 1 True True in True True 2 goodvibes-v0.7.2/src/ui/resources/prefs-window.glade000066400000000000000000000744161414415210700225420ustar00rootroot00000000000000 100 1 10 True False vertical True True True False vertical True False 0 none True False end True False 0 Quit Close 1 0 True False end Close Button 0 0 True False Application False True 0 True False 0 none True False Autoplay on Startup True True False True 0 0 Custom Output Pipeline True True False True True 0 1 True False 6 True True True False True 0 Apply True True True False True 1 0 2 True False Playback False True 1 True False 0 none True False end True True 1 0 True False end Prevent sleep while playing 0 0 True False System False True 2 True False 0 none True False end True True 1 0 True True 1 1 True False end Native D-Bus Server 0 0 True False end MPRIS2 D-Bus Server 0 1 True False D-Bus False True 3 True False General False True False vertical True False 0 none True False end True False end Theme Variant 0 0 True False 0 System Default Prefer Dark Prefer Light 1 0 True False Window False True 0 True False 0 none True False end True False start Send Notifications 0 0 True True 1 0 True False Notifications False True 1 True False 0 none True False end True True 1 0 True False Console Output 0 0 True False Console False True 2 1 True False Display 1 False True False vertical True False 0 none True False end True True 1 0 True False end Multimedia Hotkeys 0 0 True False Keyboard False True 0 True False 0 none True False end True False end Middle Click 0 0 True False 0 Play/Stop Mute 1 0 True False end Scrolling 0 1 True False 0 Change Station Change Volume 1 1 True False Mouse (Status Icon Mode) False True 1 2 True False Controls 2 False True True 0 goodvibes-v0.7.2/src/ui/resources/shortcuts-window.ui000066400000000000000000000042241414415210700230100ustar00rootroot00000000000000 1 1 General 1 space Play/Stop 1 <Primary>a Add Station 1 F1 Online Help 1 <Primary>c Close 1 <Primary>q Quit goodvibes-v0.7.2/src/ui/resources/station-dialog.glade000066400000000000000000000065371414415210700230330ustar00rootroot00000000000000 True False True False Name 1 0 0 True True True 1 0 True False URI 1 0 1 True True True 1 1 True False True False Security Exception 1 False True 0 Remove True True True False True end 1 0 2 2 goodvibes-v0.7.2/src/ui/resources/station-view.glade000066400000000000000000000500351414415210700225360ustar00rootroot00000000000000 True False go-previous-symbolic True False vertical True False True False True No station selected end 1 0 True False True Stopped center end 1 1 True True True go_previous_image none True 0 0 2 False True 0 True True True False True False True False Metadata 0 0 True False Title 0 1 True False Artist 0 2 True False Album 0 3 True False Genre 0 4 True False Year 0 5 True False Comment 0 6 True False True True end 2 1 1 True False True True end 2 1 2 True False True True end 2 1 3 True False True 1 4 True False True 1 5 True False True True start 2 1 6 True False Station 0 7 True False URI 0 8 True False start Streams 0 9 True False User-Agent 0 10 True False True 1 8 True False True 1 9 True False True 1 10 True False Bitrate 0 15 True False Sample Rate 0 14 True False Channels 0 13 True False Codec 0 12 True False Stream Type 0 11 True False True 1 15 True False True 1 14 True False True 1 13 True False True 1 12 True False True 1 11 True True 1 goodvibes-v0.7.2/src/ui/resources/status-icon-menu.ui000066400000000000000000000020171414415210700226560ustar00rootroot00000000000000
Play/Stop app.play-stop Add Station app.add-station
Preferences app.preferences
Online Help app.help About app.about Quit app.quit
goodvibes-v0.7.2/subprojects/000077500000000000000000000000001414415210700162315ustar00rootroot00000000000000goodvibes-v0.7.2/subprojects/mutest.wrap000066400000000000000000000001251414415210700204430ustar00rootroot00000000000000[wrap-git] directory=mutest url=https://github.com/ebassi/mutest.git revision=master