pax_global_header00006660000000000000000000000064132363610600014512gustar00rootroot0000000000000052 comment=3e833ec5f199753702db3f715ce12e74493670a1 linux-show-player-0.5.1/000077500000000000000000000000001323636106000151245ustar00rootroot00000000000000linux-show-player-0.5.1/.github/000077500000000000000000000000001323636106000164645ustar00rootroot00000000000000linux-show-player-0.5.1/.github/ISSUE_TEMPLATE.md000066400000000000000000000014401323636106000211700ustar00rootroot00000000000000Before posting an issue: - avoid opening a new issue if you need a simple or a yes/no answer, use `gitter` instead - check if the same issue is already been reported - check if the enhancement is already mentioned in the `TODO project` When writing an issue: - use the markdown formatting to make the issue easier to read - if the issue is spefic to some git version start the title with `[git]` or `[git:]` - don't worry about being sarcastic ;-) - for bugs/unexpected behavior, use the following "template" - remove the template text you don't need, like these first 11 lines **Open diffrent issues for different and unrelated bugs/requests** --- Short description of the issue, if the title isn't enough. #### Expected Behavior #### Actual Behavior #### Step to reproduce linux-show-player-0.5.1/.gitignore000066400000000000000000000004241323636106000171140ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging build/ eggs/ *.egg-info/ *.egg # Eclipse project /.project /.pydevproject # Pycharm IDE .idea/* # QT project files, generated only for pylupdate (qt i18n) *.pro linux-show-player-0.5.1/.landscape.yml000066400000000000000000000000301323636106000176500ustar00rootroot00000000000000python-targets: - 3 linux-show-player-0.5.1/.tx/000077500000000000000000000000001323636106000156355ustar00rootroot00000000000000linux-show-player-0.5.1/.tx/config000066400000000000000000000031161323636106000170260ustar00rootroot00000000000000[main] host = https://www.transifex.com [linux-show-player.lisp-application] file_filter = lisp/i18n/lisp_.ts source_lang = en type = QT [linux-show-player.lisp-module-action-cues] file_filter = lisp/modules/action_cues/i18n/action_cues_.ts source_lang = en type = QT [linux-show-player.lisp-module-controller] file_filter = lisp/plugins/controller/i18n/controller_.ts source_lang = en type = QT [linux-show-player.lisp-module-gstreamer-backend] file_filter = lisp/modules/gst_backend/i18n/gst_backend_.ts source_lang = en type = QT [linux-show-player.lisp-module-media-info] file_filter = lisp/modules/media_info/i18n/media_info_.ts source_lang = en type = QT [linux-show-player.lisp-module-midi] file_filter = lisp/modules/midi/i18n/midi_.ts source_lang = en type = QT [linux-show-player.lisp-module-presets] file_filter = lisp/modules/presets/i18n/presets_.ts source_lang = en type = QT [linux-show-player.lisp-module-replaygain] file_filter = lisp/modules/replay_gain/i18n/replay_gain_.ts source_lang = en type = QT [linux-show-player.lisp-module-synchronizer] file_filter = lisp/plugins/synchronizer/i18n/synchronizer_.ts source_lang = en type = QT [linux-show-player.lisp-module-triggers] file_filter = lisp/plugins/triggers/i18n/triggers_.ts source_lang = en type = QT [linux-show-player.lisp-module-timecode] file_filter = lisp/plugins/timecode/i18n/timecode_.ts source_lang = en type = QT [linux-show-player.lisp-module-uri-changer] file_filter = lisp/modules/uri_changer/i18n/uri_changer_.ts source_lang = en type = QT linux-show-player-0.5.1/LICENSE000066400000000000000000001044621323636106000161400ustar00rootroot00000000000000GNU 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. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the 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: {project} Copyright (C) {year} {fullname} 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 . linux-show-player-0.5.1/README.md000066400000000000000000000043301323636106000164030ustar00rootroot00000000000000# Linux Show Player [![GitHub release](https://img.shields.io/github/release/FrancescoCeruti/linux-show-player.svg?maxAge=2592000)](https://github.com/FrancescoCeruti/linux-show-player/releases) [![Github All Releases](https://img.shields.io/github/downloads/FrancescoCeruti/linux-show-player/total.svg?maxAge=2592000)](https://github.com/FrancescoCeruti/linux-show-player/releases/) [![Code Health](https://landscape.io/github/FrancescoCeruti/linux-show-player/master/landscape.svg?style=flat)](https://landscape.io/github/FrancescoCeruti/linux-show-player/master) [![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg?maxAge=2592000)](https://gitter.im/linux-show-player/linux-show-player) Linux Show Player (LiSP) - Sound player designed for stage productions --- Every component on which LiSP relies (python3, GStreamer and Qt5) is multi-platform, but the program is currently tested and developed only for **GNU/Linux**. No special hardware is required to run LiSP, but some processing operations would surely take benefit from a recent CPU. For bugs/requests an issue can be open on the GitHub issues-tracker, for everything else [gitter](https://gitter.im/linux-show-player/linux-show-player) can be used. --- ### Installation You can download an [AppImage](http://appimage.org/) bundle of LiSP from the release page, once downloaded make the file executable. Now you should be able to run LiSP by double-clicking the file. Tested on the following systems *(it should work on newer versions)*: * Debian 8 * Ubuntu 16.04 * Fedora 24
Otherwise follow the instructions on the [wiki](https://github.com/FrancescoCeruti/linux-show-player/wiki/Install) --- ### Usage Use the installed launcher from the menu (for the package installation), or $ linux-show-player # Launch the program $ linux-show-player -l [debug/warning/info] # Launch with different log options $ linux-show-player -f # Open a saved session $ linux-show-player --locale # Launch using the given locale *User documentation downloaded under the GitHub release page or [viewed online](http://linux-show-player-users.readthedocs.io/en/latest/index.html)* linux-show-player-0.5.1/dist/000077500000000000000000000000001323636106000160675ustar00rootroot00000000000000linux-show-player-0.5.1/dist/AppImage/000077500000000000000000000000001323636106000175525ustar00rootroot00000000000000linux-show-player-0.5.1/dist/AppImage/Recipe000077500000000000000000000240251323636106000207120ustar00rootroot00000000000000#!/usr/bin/env bash # TODO: # - # FIXME: # - PyQt5 from pypi is available only for x86_64 # Cleanup the console echo -en "\ec" # Exit on error set -e # Enable verbose shell output set -x # Allow to use ldconfig under Debian Jessie export PATH="/sbin:$PATH" ######################################################################## # Create the AppDir ######################################################################## APP="LinuxShowPlayer" LOWERAPP=${APP,,} if [ -d $APP/$APP.AppDir/ ]; then rm -rf $APP/$APP.AppDir/ fi mkdir -p $APP/$APP.AppDir/ cd $APP/ ######################################################################## # Get Build dependencies (and virtualenv) ######################################################################## sudo apt-get update -qq || true sudo apt-get -y install build-essential python3-dev libffi-dev libportmidi-dev libasound2-dev \ libjack-jackd2-dev bison flex libcppunit-dev bison flex uuid-dev libprotobuf-dev protobuf-compiler \ libprotoc-dev libncurses5-dev autoconf libtool rsync wget python3-pip virtualenv # If the python3 version is < 3.5 if [ `python3 -c 'import sys; print(sys.version_info.major >= 3 and sys.version_info.minor >= 5)'` = "False" ]; then # Install and initialize pyenv sudo apt-get -y install libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev curl git llvm libncurses5-dev libncursesw5-dev xz-utils curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" #eval "$(pyenv virtualenv-init -)" #pyenv install --list pyenv install -s 3.5.3 pyenv global 3.5.3 fi # Install some via pip pip3 install --user -U protobuf pip setuptools virtualenv ######################################################################## # Source some helper functions ######################################################################## wget "https://github.com/probonopd/AppImages/raw/master/functions.sh" . ./functions.sh ######################################################################## # Get the last version of the app from GitHub ######################################################################## REPO="FrancescoCeruti/linux-show-player" if [ -z "$1" ]; then VERSION="master" elif [ "$1" = "latest" ]; then VERSION=$(wget -q "https://api.github.com/repos/$REPO/releases/latest" -O - | grep -E "tag_name" | cut -d'"' -f4) else VERSION="$1" fi DLD="https://github.com/$REPO/archive/"$VERSION".tar.gz" ARCHIVE="linux-show-player-"$VERSION".tar.gz" # Download in the "$APP" directory wget $DLD -O $ARCHIVE ######################################################################## # Get the dependencies ######################################################################## # Get OLA OLA_VERSION="0.10.3" OLA_FILE="ola-"$OLA_VERSION wget -nc -c "https://github.com/OpenLightingProject/ola/releases/download/"$OLA_VERSION"/"$OLA_FILE".tar.gz" # We need to re-compile PyGObject aganist our python version (may become unecessary in future) # Get PyGObject apt-get source python3-gi # To be able to compile it we need to install some build-dependecies sudo apt-get -y build-dep python3-gi # Get .deb(s) mkdir -p debs cd debs apt-get download libglib2.0-0 libgstreamer1.0-0 gstreamer1.0-plugins-base libgirepository-1.0-1 \ gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly gstreamer1.0-plugins-bad gstreamer1.0-alsa \ gstreamer1.0-pulseaudio gstreamer1.0-libav gstreamer1.0-fluendo-mp3 gir1.2-gstreamer-1.0 \ gir1.2-gst-plugins-base-1.0 gir1.2-glib-2.0 libportmidi0 alsa-utils libpulse0 libffi6 cd .. ######################################################################## # Create a virtualenv inside the AppDir ######################################################################## PYVERSION=`python3 -V | cut -d' ' -f2 | cut -d'.' -f1,2` SYS_PYLIB=`python3 -c 'import os; print(os.path.dirname(os.__file__))'` APP_PYLIB='usr/lib/python'$PYVERSION cd $APP.AppDir/ # Create the virtualenv in "usr" with python3, coping the files virtualenv -p python3 --always-copy usr # Copy all the python stdlib files skipped by virtualenv (why??) rsync -tr $SYS_PYLIB/* $APP_PYLIB/ --exclude="*-packages/" # For some reason using "--always-copy" even "python"/"python3" are not linked but copied # fix that and save ~10MB of space cd usr/bin rm python3 python ln -s python$PYVERSION python ln -s python$PYVERSION python3 cd ../.. # Create "sitecustomize.py" to load "site-packages" cat > $APP_PYLIB"/sitecustomize.py" < usr/bin/linux-show-player <" -f 2 | xargs) ./usr/lib/ # move libpulsecommon-.so to the parent directory mv usr/lib/x86_64-linux-gnu/pulseaudio/* usr/lib/x86_64-linux-gnu/ rm -rf usr/lib/x86_64-linux-gnu/pulseaudio/ # make sure that GI_TYPELIB_PATH point to the right directory (some distro put gir files in different locations) mv usr/lib/x86_64-linux-gnu/girepository-1.0/* usr/lib/girepository-1.0/ || true rm -rf usr/lib/x86_64-linux-gnu/girepository-1.0 || true # Remove some "garbage" rm -rf ./home || true rm -rf ./var || true rm -rf ./etc || true rm -rf usr/lib/udev/ || true rm -rf usr/lib/systemd/ || true rm -rf usr/lib/x86_64-linux-gnu/mesa/ || true rm -rf usr/lib/x86_64-linux-gnu/mesa-egl/ || true rm -rf usr/lib/x86_64-linux-gnu/gio/ || true rm -rf usr/share/alsa || true rm -rf usr/share/doc || true rm -rf usr/share/lintian || true rm -rf usr/share/man || true rm -rf usr/share/python-wheels || true rm -rf usr/lib/python-wheels || true rm -rf usr/share/sounds || true ######################################################################## # Finalize the AppDir ######################################################################## cd .. # Extract the archive ARCHIVE_CONT="linux-show-player-content" mkdir -p $ARCHIVE_CONT tar zxf $ARCHIVE -C $ARCHIVE_CONT --strip-components 1 # Copy icons/mime MIME_DIR="$APP.AppDir/usr/share/mime" DESKTOP_DIR="$APP.AppDir/usr/share/applications" ICONS_DIR="$APP.AppDir/usr/share/icons/default/512x512/apps" MIME_ICONS_DIR="$APP.AppDir/usr/share/icons/default/512x512/mimetypes" mkdir -p $MIME_DIR mkdir -p $ICONS_DIR mkdir -p $DESKTOP_DIR mkdir -p $MIME_ICONS_DIR cp $ARCHIVE_CONT/dist/linuxshowplayer.xml $MIME_DIR cp $ARCHIVE_CONT/dist/linuxshowplayer.png $ICONS_DIR cp $ARCHIVE_CONT/dist/linuxshowplayer.desktop $DESKTOP_DIR cp $ARCHIVE_CONT/dist/application-x-linuxshowplayer.png $MIME_ICONS_DIR cd $APP.AppDir/ # Apprun & desktop-integration get_apprun get_icon get_desktop get_desktopintegration $LOWERAPP ######################################################################## # Package the AppDir as an AppImage ######################################################################## # At this point, you should be able to run the application by executing # ./AppRun cd .. generate_type2_appimage # To check libraries opened from the base system #strace -e open -f ./AppRun 2>&1 | grep '("/usr/lib/' | grep so linux-show-player-0.5.1/dist/application-x-linuxshowplayer.png000066400000000000000000003154241323636106000246310ustar00rootroot00000000000000PNG  IHDRxbKGD pHYs  tIME &,iTXtCommentCreated with GIMPd.e IDATxkmYVιjniav"! -@Vpz@daaE,H6K#Gy 9 N=ozP][U}֭ǽ9ccZk}=u9g?^{9č7ρ~g_C|5^"&w9894 ipApG<^xù?9 ?}߁Oso @Vft {g -|Kh[mKh=m]usw~}fߗSwNIׇWܘyV7u4{ǣqޏgaP#nρ{_kԞw}{[߯{}KhIKhOvo%wg:n3ӛn2W 3^$,+Wu~ٟWLu~ ~w=s >roz',3tk#7s~jA|0 N76b^;- MS"~_iX>9n9muNQPoo{3pC8?" - @:֯{78g{$&ru?"/zϿsV}3[<@M&F5 `X@?9cT22@L'邁d'@D DH@K?d;qABvx$O@@}0`x}ܫ<xN_* |<873bڬ|<pQpNVN;fbƂMۥl~=w@ԧHoѯ` }=،C@6uv;F_G\%!3j "PCJz.;\}q[e9Pv@̀@:|AL$F^1odV=b3vBۡub:tyۺyZ_<(u~@FIFdf"BX)lE!=f}~UD*: ut` ʼxW߬ti~T/֢^HN+z 9w`.{|jko+<Hhv?"t vn,mMz%"ABT?'4]pcӐuNߑ(l1" M= _T?B:TxZrwk8 7xY/jU|tt;V1? } bozv8*o9'w^?cvv&GL!C71 7IH2`e>4 7P ضC쌟E=8]2ٽܭ8tD>h(" 0"o^&MĆo\ /cףqm$AJ8a ؐd:4Va Vi&aQz}\jw1'p^[3E {֡?)*2- 4>@CCd ie)c>Z>:::?O~d& MP|clSFIu6`2[L-u^*g%8 jso#8-X|@D#c.\sG+ _V9n ?ߞY)ocGh3),#w"nlDXm7"=xOj珎)*2! q柆hA,o8v/>`@#~`ɩ>^V pttw m"=V|ILޓU \vA0Y' ~| w@,hx|^ * wv<Ϲ2¤BAMɷ3ߧ ?I9zDQ|@!{z֝5Ԝ1?/9#Bߝ#&!l7X˖vYH# ,e ;:I$zwEH`H)$Q3Hszy!Cpl/~ߜBu^So/q7 :m it3:68m% [M~^Nϟ=RYkn/1?Եkמ9G?g/^AA").`MB]Q! , =|`t8~ePzns8?! TE! ˞Gqojwճ0΀ ?9ECX.>N`,= ihA=Jv(cN@u[b1HMb! '#[v1O )T4@n6W8;%<ňL5<6><^oBBc  _ꛎY;``WcMSS8%. [ H;%Afq4N"mLt9)]=jn$-| /xw!_JMg947Sh4{\h:#;8+zX'kum[.Bqj쎉緥p؈Ia0fЦ_W#h ̉l&N&]l]*;A1 PaH2y~x/%F',7^߈%t-XEk z66u+yz.8d}B(8y`[{)ry$Ek_[dJD:$XnmpưW mK=]K^:)io'KQ +qA~)ӓ3 SŲ d q6.J:;Gh:is25ҿ΅ 7 AtZoDZ*8E4ԥeIfC m?ZC>~,9+ޔ4 ߌ6-rܼtEgk lnPu$M&l1Ip,K& +P~#!p-j$c=+=?Pd9!,Q#po۝/;*_˸ zRJrSn`3wjfldhe\vl'h5@jࠟ @ ; ge Tc7?GwP>؋M[[7|OC NG.=xh/^._tz.AFį/n pZ}]u Wnop[8kP?Oֿ7$Bt]OrAbBrff863l6}-w߁OF.}BQ;JP=&~.F FIj}0%C3z@p)û} ~}4?|#G{ЉGsmMNO,k=W{ w}sWꇎVڳ^}%\}xr"wHcb^z*0>E1c*@&c)Pn)x 6I: HsS8ҒcD@ 7L?8{ ƤjPq4=r8" *P<#3Tω?GcB0r%"I^~ A={{A/ޭkjoj'۾9se\~9\}e\.dU)[0rfnXGjP,0p.aʱO) `"VC*FK/*Q޸ϢKJ%NtJ wGk) DCZg!z;r 2|CL=¤7zvdk%.h8H . tEr՚:<&{'=v`=ԟ.\\ɃjsGGG<hNSWl_ƅkpϣҋ/›o!B5M/ۆdGƂu{G$t$$j*e{ gb,`$$TҠXy`)=.YB&%Qi0 kX@#'Ra lC"߉V2_+H\"A2%o/;]zPyknߠY(5ɯi}GMt/PG'T6W!Q.R2揂 W$gAHblGYﱁH+{1NXdk{@so zEt?ڵkO->g>!6N,ށw? s,(>˥05S (@ Nw^OerT뷈Rdg,,.pYtݘ^.|q"2H&-L 'ff;Gd@ܨBS&p. @pK ɣ tH4(~r^8> ^8!D %P@_Gs8x)+WT>ÿuڵgl9yEEhNN$<) X7AA5@X};VL QΔLIJƬCòu[g/.zKFe@:F7J-)#x>0$90qt>W^27aυA{'l.!'Ꝿ 䨝Y8fRY=WbCTU0 =*@*oƦV]ZpH3?VnA"+)1^ eQ4J8* |1c7?+,|S8xAʳt"?=nx[> $UnQZ'=m4#2-&j);%j)ioGƄMAQC>380ϫ-mK L٨9ql9vZ$} }˞TG} P.h6J[%`9f$$Z#"uGԔ?MJmIx[JmnGŕO~>Agn.=9yeqrzaE7\i\xq} >[%vҾ]'qw~'l~꾀GY5% 5?gmlيlD;2d(mΪ`t !Q4l'"=kVV"IL^e&'N)q( emwm"3 r9k<sLq"qN$pVC@z"Te | V…?h[ /J`>t$yA39z`+^C}cC_?;C5RbX5m2ݸç¿] Rz IDAT~_v_u@7y[8y<p|\ pT ՛/yZLxUFw-W[VZ=sRNuH1m8;(HuK0mZӜȿ[CмO]9bo3f6(K-]?oDມ0`p!0@uR$P-} U ߊ ,3IaI(UTF;N9S}m 7 #5;\X۵aN$cȂ=ǜ3.l^p}ӺJK.Qd?7)i jb;p!70HJ "J$am2;G^2jX .gۑ{kUw˗FpO-{Ok \=eK2̥JBרXڴkw2Qk -as"}HV G$8 g:`l&rJ% W10>&)2?,6%ԍK':("[da?n!OV`ukCIp\||x?)4O=5԰L-msy8on>)){nZ ٽxQZk5P/hh A'x_.Ǒ1Uoĸ%"S fk bhOrnc0,?*ٌ At rM1fģNؖZ?~k}SO^{m_wuj|bo&n(Z$ۭMcg2TB_r O U<6|f:&AX}oIςԥ˚1 MQ`F9k aNKUn95g+):%D&I^+j񿰐!=c #Vy!x,iTP;aZj08) 7p7|Vp.]˟<ͨѡLzᆴ"0؈ArNAsH t dTRKD@HMo p/܂Mq I8}[\c$@ɮ}"HM K'Q=G2$CfbRq.]jMXμfMAFf"iq[.#qDžk%۝;oaW}#_V)$+/V~){Ky-.0҇w\2zaN>xZ UzVJoK)W72HzRXt]sEk 2(Q #߽EeH5Ayn?p^B[e3ɇ'nikpGT.wvj38e:>ƻ>\znYpsڥ6-=>(= #@K5kCx(!GRpۘ(KyI`_'qS=Y*aJ 1އ(Ӑ^oJ@ߌA1B)9ҿ 5"I_h/l 3#;}t pW-9B.ɈN 67c+8M?*Krdp oUta.],q-6蝨iYږE)Vlܑ\0%]5SRX1"65/$@v(-g-*g&3 ۔tuGBvaWAFr|g5@8| )҆Ag[x.[o#9 ࣝ}Z7NܺFHrDf5a졮Yo[QStn`Ysf߆!ƢY)ժ#(/ 4.$ 90#$c Bi4pMv6 !&,.=@%C~1i]/-Sr0AhfIw̎1.v9otx_i|bw||s82MM縬q+;8D @kPQas>qOL]Jkӻ};Ci}w=.  [-xjjki d-i8x W2V$莤UMƌx:bf@?Nvhݗm9oCj'$]9=l%k}wK m:g%ḻxKr2!9skTu{t:m'MWE#esc$BbpQĥ ZEAqVKHCGB'6PsSΠcBs,= `N4x ]#ZSB^u6 2ۗ@OBd`~0C.洽 lr_Fʆ9mS8D^spI'3ϜbG t}W]+a^2q}*a^Z=W8f"mRJMJXY0z)B2h02rMb+b MBaW%(Xe [J~l&V `'BzUq9_"VڙfKQ6qޮP `YSRj%j;gj]-}kNsv@o+>Oռ2~!r֚[{0v^5g* :(`P, 3ΚyCX $ Ҷwlk9C;2Xi~8Pr08VGf⹀vi= tA@d^YK"~ByGOk:5;ӧ}1; gix˨a#yO4*gwF(9C;F =v1rzZ )gkޔIs%1kCYCPl5 (^G,0(UZ|Q9#ʀ `Y3`IהwXc)$ 8L `)Z,eH^h3`d(.pP-c^A0퀖&{1'w xbm+MSPwg"XT!Ixo7YK'Ht8$wR5Gڱz['} o ȄNe(.Gss!@N! } ̄PFj5g4aq~詣A nqg'gٹy#'k_,^)$h؏?(M(Zof:q_G#?A p ǽj@"Ђ\gg&@5^=*|F_!8, 3zLʔ@6B~>kOVN/|$&d8pГdETt;TJ]P(U+f@=O^bb h(*\Æ-2mÜ$Om=ѩ8ÉNM;3?5ثt,0D_&M;Ț"S\c/|a׮cl~lhzjoE\pNȧ1G2VcI~m&<AFrd(hĻ UIϑ+'#eJm^bh;Qg \`f> k.p4d΅-ThR+!~R؋l" [aǑc0.`E ;g{S/m؜Gwz_;}?㵱#py(ž7RqΥ;%O Tyɂ"JnC\F]V 0=r0s _ 0u$@ 23gl0j灞`Lsyw Hd dĄ]6g>T"ή%Zu`"C-ap@v~t5[V]%?,HVK,AE(3N5;G``mp@xraa^[Hk9g8 "YB Zk~MMH[zLjNtY;Κ`LllwFJGbZG:%j%;*e3y.8Rq0]oE*ׯv;+}/~d`]*:̽+<_nP>z6ci^d15Kk,fH.;b{ D@ |_|"8?6lr ~al$>Nn[}w|K֐v0FL?oFkGndy.1|XA,2;!XC:~ 7{KaE&@֕@DhJXI6أbC lg4^b5z}V0:?K5zo) &R oXa̗.EtǁO x5{">o?n؏i|0\%w %Z͖<ђvqZ&gPq%J֐'(h_Fik`'p8lh44vMBO(Xe[NaIg dNZt' 'ƚ kdQ *,*D8,GVJ;mJO>Mѱ7n^|qj|E_n6]RfWsL_1"d.rj,2Y4Vsz-i`yWRVƯY ¡Sd4`Sr ajg"J+ȹ px g-\4a]:@z1Gk#C۱sSvږAZz-|{zβ[)RUi\]3&y_ ؈ _2"KZI (Þ8I]OCȅ2A UeZ>cp(ڹ{R`tFEo1(_c5(RЭz=B69xmM$bRN{>tS skzy`djO}҈#&hT C *= d5-x )*Ue~4XL3r%0Pdv=BV-Bh(M)7wZN@X_yfU`npUJ /ߕXDu|sk)o眃(f`@l ʒ y%sAH,]*ݎ =; Of9K )Uj0D Igk$fΚ@}gRbExQcs k&B2pjPFI Ir Y2#Zjf vlrcHTett1ħ{):%zz*t @g}h_+|N|vFsv]􁕷IjL/x!S` z}>SyĆ6A`)~΅=vt!f ee) `guPa j =ÜNnDoj4T hh HuXARD-F4j( ׏m0B1c`Do]=ڟ^0#A b9YF 92M4N@oyjsE/3¬O+ͱ fA/1p2 #H4hTap'eLX?T9FiE԰sͩ\MXC_ YVtyBo.-=#?w 瞛E\Ev#FPjLK rD~k hXepI6ß חN1p8Xesop$¶@爗rxi#6GPhnk//Na\o29K/FDMk`JT/IsAjE dhw"(ς;vs|6{9Ν]]2j2[0.Srd)S'udkR(5}М9%Bj9TkKAUo1K^:b5g{MMeZe9򪥼{`cש^ , )E| 2Z׬ZrS!U ץT IDAT-uiEmm&{|[G;vusEuHWSdžjV4cND@mXp$d/A{eUSE*봜eƨ'j$>uV<Pj ieB.(˒nia?P )&ߙKC1@YEΡhsWiMLD,!dPD0][-߾:o;?kҶ40/2U\dI_ix 1~@aڗ58VF0Fxa+`%xkp0X՜'=[hs2J 'W5|Nq#Ȑgn q&=اQC1Sk(\K!Tp$-O  w]ܾv}N^HB5KrjSFT̬Zx<.XccڝכӆdIvsn{_X "1fsj $ @%gΧ49d2Bc01LN#ҿքƚ`>V@<#~VT)cw ];}u}e;cWeνpAmws=0F*K J1԰yظ[$@=N N&:|>3DFwMQ 6QsM„=dKcЀ0F"?^: \rn6dZ};Yi ,!h}%  @CoA!!A\7W Y/*[fkeuN Iu@2|A3j2&҉c\FApd~##`LV=4jܯR!ɳD.hײ&Fv9ʃAw(%ܹK:Z?˽}`^ڶ:ʲtKE|gb dXaF NC%㶕DC܃+81䞘МlhJ``IFRK#ReZNtxu bvLtqزıEXd 20t r ʌFJ5cL?J̝SM8[C}TTM_")rFN$dt 2Mi">2wXs`|XrV|){*DpAx#ݫI,m.YSѹչh”@E|n(v,g\ϝ^ 1qH1t=6Aq:JF?u>V`4F9 ! pzUT;@%naC^N*׽Bw^XQ'Q$_`Q, "R[SAYbA֕8yZ qsǮrx*77nn*R;tn7'@2S}* Bѽj(S&KE}U@qh5CDuc1B8Pt0Y]*8ؤ<}Nk0g2455DPFXZbn_9$ "KUr@4B3-Aؖ9#xz֝[qE,y 6 pv+4$WtןU9D 68C S= g,/=_=SP-W,s-/KB)e3}&ՒV`se.2 A_[MQnZFnC qeP-t5N\j#M).)e-}kJSjKt| ukZ^&[ [5pdK 40"r>,iY$A #G6QhDVw>?'gRXidR5uLi}劽CXd 'Wl-q9Ԝ_ r`R)w\ܾ n~V2"wuҗ] 0&ԇ;8Kʵh#kdF@cBcT1fx,74*9 gv@%XhNR\Sd.= ?ϡ6Zب;w&DQ6pA%&5[:BruY)rO F""b4 J} @#gzтds5ȧ5(qt4\taz9`>, HC",rj%|F?e1X֌dVޝAZ*vKWVKէ/x\xL34]\Naݕ;SS2ZEr c) CCO}Tީ,i-KdhQSH1mfJƖah"K\ԠGZzrFz2wˈMwLt,ZP?k"' HP jU_c-'^+w XwjJ0W|a {Ϋ"1>!GvZGDŮ, 0H9*5@\xgrL!; h^SK\t@d`9|gmc;tYgנUef%Ǧ#2<2oʭZ7j):SK5cZ "cC˚Gz"! eDGP289D!;^:dg1nԫּWwN8DR@(fgj%:g3tD @F?Ry.a קΦrnϑ!Z <_rK$o2fﻔP|~4ÁA%/ ?y[')mKwHFg9jTCS (sm %a 4Z?WGѿ\q tt$9S~x/s'M- sAqoK˄.dc.o!!v+],u1k"m@Vp Xm5xrhLcZsvxөv%Ĥ$)d`4ml gK?E yD i Z2T890Mhe?`LSPD?=໯][ey+|ʠ" 1B8c˖KtimKMpA]~ie{9$j/75| NoI`^E"eDld qV[&jߠH\~Ö,6@θκu~PghcZ[z*7Kh >O,I C!41ĹE'vlGo:56P*% UӆAjCmCv~OMm$.8.8I NlHb zm9ˌVKV ,b-|XtD\(\JgyD?[mg Ēs^0"VOXP,Η>AHP o,}.` CY%Fim 7i2 # 'Q2Xyr}Mي~@1$Z%J8ST!ŦB(A\J2wHT({9gb8BØtÖG_lTq#7SPb -MwܴM?d=Y7t@! ""]/&sd@g+R "FGҿss<L=]>; 9Kc/uZpLvFp5Y{XgrViz_Bv+OY?%:F(V]踫2ܹܒ (LdRiu6 7n@и?"-jFHMJ$n zm ʄPPNsb,q\_0Uϧ{6HҴuZ}Kk|.S M2[;U^^lD-u<ٗ9S}l Nf_B *k kA Nիc6k}(?Ƃ@G(?"B:ǐ\Q: C̷+Lp xi! <]"fMiłwv؛$bQ`RTk/32KB$'FʚhH1QCk])@daΖ TV j5 @Xյ-"X)o7A8s8?C\qQ4CbT@ B t#EVbc)R;&Vҳ5ۮ+yC]&i=@^̉J&2$8pv\H-}T=%êZ S;%&C&Dz ~'1:`L{ZiX ]`?"[GK#D,,hݸn25B$I!=YD+%R#%ж?Y⤕}EAst(MAlrsډ N,gN&6M#o:^GڶE6ldkID' x-q} 1/#DoI#pٹ }zՋdYecal 9o,WBMW-J'0q1KFrfRFjլE9T.2aTQ/aLKx V hKc-?Ga` 3Ƙ*4/oDT;_g?pBZ'Z (`MdzxC^[,û-{-b.eOH+?w!V^?Y IDATeC.Kfӳ@if[lPCx.k@B\K]'0ohgp}Gf$ĺ%*`,ԔC.hX$,@}_89QH PҜê9%3]rP (7`{#pa\ #x bos Třs* Dbp, 5 ΊP!""C cm`}cЏg`)斕X.%vťx?R4Bɀ Is E'&oSDBkғ2R$"2f)}2;94"veh8乫2mgǔv#vH\!# 08Ů_IS\1;$lwo|{y Hx3gS6fxVBǶaE>$)PHBpsڴ北M5sTK\=hSٜN ml'oi^)"59)Hc%SDb7[nS&8M'tu0gE+?he!щFg` K)ĮArXZ$ms]Jk @  ? }V]_ri8mutn-TPgy0d @ʊOOs kdfiF'%ЩwM;V9DO7&M4hв -ZZZ33OZ\89E ''hčnITeFI-q+"L + בHbdԺ,YSENZxz'w2\N5Pl*M٧&K%u0o=|iC?2@AڜshhG`X ,oݒ+hGI1^y^y"^}7qr7Np^tOM x!f\89% '-.\>9;Ԟ 1TCuɎgvgЀ@duR*B );[;DJ 6-1 Rb!,ԛ*)s|p!~!.-\R0cF,Β6 qn\hȵT9D @'\yo^K_o\ko.b8K& ñ?z|wp'#q`i$T]t"1N]1 t Ւ[; cV~VoZ9G|( f`8'}{\BŵtoI wΤ]@Ӆ9'W}Wc3iȟ9aucGXHB}KѝhSa,uI=~?D[w~ϼ|/v_yc?u_[t /:こx۸7#م̉rn:WZ:8ᰤDh <ˌԿ(:`8Z޴a+8n\?~nƃm8^# "X(h6(NCͭq˙:!%AA#5'tzO+ǵ|RN9WM0Kjt`{zn2pq ;H@ Tg l Ed}/yߛ.K/^[w3wd:VΖ3Fu\8h;xmA) V,أ%Si<5Jd d94+Й*.7D&J9GYKj%r|י vkWC&`֬HGHn ]2 )"F!KЊaCi!2?!1!Z{]O]xtf״4 ?rݚq9{p--|۸DmtrPJtkx"5P!7\9<DTP2t̬?b82J2%uw2K SpsfN BIJ&hGRw0ŠK*ⷜ] ͙B776eWc _9O݋{31ϡ 2tq<'/݃W+'oyRQ<@w_ Mjmv22n ++g $"c< xN>S:QKW) !$Hk8sՋ6lNn8Iǜp%~ yZإ4y%Ԟ֭5 _w?5kaY8H"H:D ^yQ\xr?{>IVlfi9^po6p[maDsk'ح8PƗ Ƈ^:ןaD .(Rȸa/ˋK }//--!‚3V `iKj~^sWp7Ug97s}]09s}C+&.qGWx7.]}wn=Ȅc㧇cjM5%d [75l$(#j{)T`!R }9/BJuYz8iMaCUQM+ÍjL )bx8M! ?9e .t}QmP?)忯|GqD3?{o/YQ?U}wafaaf( bDĸdш+11hM DFMj eaan}oݧNU:[w;lU|O; 7mr }E̫MZ$u"Q>kjK 6б'?]Oax# \&i/Lݴ&ߩj'6u 4hA 5 9QB[u >w2.H?x+[USn8I~㏗≝G? 7難n_~!+%}ű.GpZp> Rկy (́}[kjw"sÜA=P<"G:Scs4L/]C `'5d9jFcD \hu2͵?&",3vq]~uSǠ|Q| zCCCp: .?p)'y04~q' Z:PQe \淮y#B;UԔ-@ɉ@=)mLa1wZUYn][VAQ1">Zxb^@'̄(6_PYJ@ !],-"𓥸wkй_w?N n>Ycq,P pGF|9f @YPȄPry_igN)JK%c3N$ @AH(bȒ@@zd7M9P Y'L"u4ټZ;G1ç„ܷ[k ZtM;Q{tQ_8pC#`Jk~W?.+0da{ zP'qh";~C0`J}71'~ 8 y4l|/ı˗G7#}}121cκI2$s -`V~Z5QCQ`v_7ֲ( tGEz9@c@(:osߍ+mSSќ7S&Nr(kfL6b4 .M 85k?Q*J^O|έZdЅ7tv~+ w04RX"P ix\}088尻ZD6ot>xHC/ 85iB"4`Nc!*zKwWu}>-fZ,ÎQy$`^;M:K ,lކ/~Q(SBU77 #=rE߂)$)_Orj\kQpNXx#' (Km$2@k)poNZB܏#}S~䎨_H /~qiQrW]~9ps r)_OiazoN<}"j?cZu}9X`vK%PTkHr` yxN1`&`. ] Jf~3i!k3-9t*` %@+JZ&" \EΟẁ5r75!Q% - Ng+VG7$UOАy5 tS7;~3ry ھE(0q[0H\oI'o=Y,b_FbR'/N᳁`اH4 ZpĶ&E_w$n 3?Es&`!Y8pcЁ5%QhuЛq.LJ7 MpuC%ko{|߾kL~2yo|T?s:|¢ŋ{>ewp?6(mB-Npuf@R\70+p籷2'D*Fkq N֦ǡe : yhNǗ1m)8>:W p}I: Yq5Yw >@, p}C khb5=C׽׽J>5㕫V[,YmoK"@q? kBsf@ Ҩ 4rl<.f,BOi4rnuu5ɳ7 Hu_ZAQ%wtJ@iFʼnD-*5:`cF$9b#\uթR1вe/~U;el`soqݿ5iJ?cפKI$(?|r\%W`& 1.8{$QQ. M~ٸqpe_z<5xoqf h%&%|1hMDXe IDATd`ҥ앯h_;!8_YZ!>7ڏ)\3pЃ\vo`]g?"VnC!l;ʒsϳ.H$89-i=0bk{:eOg۶<8|r0c S+~_\]7\s +g+P (`|~69gI᷺Vki4BJIzV-Z,F W*R I2])u$Z/ Ǣ]gU{Cl`K6i',hJAS:;A2*77 :կ̧e<8M(m\ ͏MC˿%^XL>I|rw{~"Ȱ/x{މJX1ւۿo +Py ,_qf@BoxMʡ]b\LPok% 0i[%Pب64.cfQD\醮aW!`<1״|'fj ѵ^xNs4ן[ 2CRɩw_ m_oj(.rz??CSY@PZ+b:n/ @!%PKi5 0Zdw" k5B;ЊCl5ؼ$raZ-Q=Q |0x>HVq4ʍ[3ۤI-e,+?8q!N?]pqk?~p$>9s\.>7LZe&O\/܏Fov0 yCP7ʵg˖ѷJWGx쁰\C糎٣EG&!>"f[b$[I4\l?qna}z% 9Do,F" jN۶o ЇgԝE o;z*OzpU8c?jo#("s}l/be!a/9ߴ aw4*: o% 7=,vFc sq u>&D¬]` /(h,Z`N?ۈ!r<o6@%ŷ~"W\_PRɌw:Ll?d?]z2Dx֔p07B ,ϤQJ+M , %-x hT_Pƍ86.?  i4P:(5T.= ?J%ێIC?@Lb?'0ǃ̀1xy\>]WRĔ??GO :B>aQ,_(ګ^ j)[Ѓ-!S9r9 G9 v Vr6`!(e];wbǎC)ch|<m]5s t>I~1aN9Fj|ž |CklxAnVEmŦ В V /tZ\oG>L?9?paʕ=bRԱqbJ\qeAsir/"|Co~شq0}v䦛} ;Iymw4 _vw=x}R@ hΎx'fuDZ {W?뼪s #Ňt)0k4w7 5#D7!$t 7#3ӱ߽gS3r~ɥׯ"VV}ffr+|l߾m xbp≸eIA{:|tײSp7>Q=l|O#F ;nMK^%K1@'tF755ٽ* {]}S>gfPcl筁lB*\pjDR. ¯ u U ]H˶|ΦGѐ1o&A8iz>Ti``|.&{G7U_-{IfiXH:wc )$%Mg4.e?Cx\#a) a`@Cv57ۿLs(~~ͯC5֭[.<;qч C[l#r_=S$}-`@H1чn [+߻@C`I LSC/ZcSl0 kA$2TJ`\Asz?4Y(¬M JK*M @t`r磫PĬ㦇1^>+WtU*Ɲ."|c;? >yC3_~?J_f fLv ބ-Olp$ `Xdon:e;uB-G3qy2鋶5)E.~ɼ{A`)W5 ićFETz@`<^ KK4? w+F Ӷ}E2k|^R{s~vO;oy+vޝ9ﻩ^Wpi˩tA= 7~VTO}Y-_ c%q{K\Dc+(ŁAqQKİ II.M}vG I$I?zQw5X:uiHzJQ,'8Gw14U?x=ݻvu# ~O)` rbAE :}LEЖj~X܊}}XaoG-_=ҥ#(]WgjU"PBtRs0- l.ΗZĴ2t[5bH7\ Khāka=&3M.I+Zp8 `AY^R \8$#x]H_اkfџ/ٽ($PDR\a_s#]DX&0~ʥEpQń"GpLR YCd M hUaFϛ$x]JG;Ad]6k„u>d@1'ZCD"X+?˶k!$Rwcffa3'R_/h~>ZA S9Ѳlxo$&HV1vdb2Eˇm]u0^Ь6E\j Eu&U7j %T,BV&wtkKi${,/ɹ>@huRځ`.|CTe1Og*38g[+ˑ>O`֭ #?޷/q)TtBSD@`˓[  žX(.Qliq9@o0HCvR ? 3'l(GپuRuQ];NiE\M`k mzGdt t|JHnd\˙CԧUh"0DysNjY/5k&8Q#f 4t>`@,nńkQ\:[E%x֡FgI|},ǡ25߰] F\@:S TohPDд!&gy2C m߾w̤VH#p EW4r'}u3۶9 ~vT04$z.ZbDNL+uDqؗ?7QƾhV\sv=Q`*S,"i{a( Q6uf2AXlӇ;J'>)ӱU("9|տ8EKi"Lm7j!?X'q!/+9u4gy&3z{ы_7甕G{JX|>/~]e0/;݀ѡGuGpڲg)|ddqλiY)?"\D4@XuJ͚[o&OU?4@K@-`~ ؿ_4Bi-/Y@fy3 =jWT@Us}wQJj. #5whJZw`PH:\_\$,1*gS,eSO ]z饙\.>KVnoYGr>Խu}qI@ mжMNNB~ ֞dL]r*7Ӂ9WGxi9^\FWNV"7 xa`;6*'oRC&!oM\˦/F1sD_ou;s=sORoM#)zJ0gYi!+5P0Ne;9Y%(N@šfe9"Lyj%D(Ot|`6cۂv;̔T*Eζg\L:BsSW@N(fDf K#4ou^7Kj_ *S ;4dIJg9ׇ,)GHo=3S s,]:pH-e_]wU7nLL.d 02:WQS>xJ_u>j4f@u4sqA@\ᠨߎ@w\+YH"FP,凣dD ½W nu-x}1 {޹bSm`өÑO !YV~e]P,Hd ,(z+"5`\^6feqe6JFD+0Ǝu1>GCޣom2eF`'- ?nf 3?33qs֓TP 5R_j pu2pC x Wr) ]aҘI|Y.7_ 8tėَJcs uS(<[Z ٽoaDZ "'xB`ߌ1]`~Y!6w`'tEvXx;M*ؖ{ż&Ȟ:~ϖS 6d@u9a! g=w<,ZvmOfdY2|95ZGP(`d%:Q\LsL_ܠi]!yu\.՛u"8* 4߉i`v >,Ttr6=v ,/|aG*;Ⱥ2ofbSN9YZ&6;$s=t(w쨁2lQ|gI\d=#'YN@} PEw^Q $j$ߤp1. =%x0*mN{ox{PY/xA$VEs3s%b`'~nTEۀtZ < $ȀD NWjk't]~?LN>i"g>*fyI?O4"G$'Cx-ОA!P40>T_(yձ'rVS, *Ue\|EZmq|9.ZPq@JpVG"(i$Oqu%(ߵhFN/ ̹%$0u#"d5Rρ@K$z4"UŚC}u0g_,&`yի_ݑQ tҘYnx""ᷪ?i*Mpyb`(tKe8묳ҳN@ ^[E'Vأ(Β8*p68"*<m9wF` k(+X62hϠ`MU,=RpʒX]b.䒮ukÁ/eD 1y\xх*`sH54=3,EJ˔F"&@!ھ,X1Wh`88$Tu W(/Xn!$Ǹea$$ektt:#35U^}ɥ3@><R;>9s9qZC:!.1GFFqKM׸eW'=£<,|I]j V6)B,}<8P}O2" jfEfEZnjy pKVea}%S343m2i5ke_ܱO{p_cqb|:G_U'lp;b.}^ M qxR|uDz7؝lm 3߈SqO nCg] @ e:}3&z5iɸc IDAT4$:r`!E:rW\y%Vp`x?d*[ry~=Y~LK?VIܙ_qV_? +TrX޴ JJok*3=dLJ奎NSD,=_ č [S a{OKLD'ϕCeSy2is}3_>I4&'&":  fA?m?&CX\%ҹN"DZL|-i Z@p\aPD ݋EϮM⬷4fb [F2('gZ&|PXXS$r@^"}4q5/=

%hpΚ*( q;o<ŋŋ,Lg9&wDDp괵]0Nqˠ=.DrrYI\ #N*n:Uȟz͙(\T 7?ar4FS6y=ԕ.K,vfX`{*>`||sHis"ۊKfh"hVºߦ[0:Ji=p|Ǎ?a81^~eK? * }> (b @#hjp"p  a@sɁ1C^AZEKKnِMFmCtр$UYZkQ~mݳVh]3Mw.!D.H"8aiEզz_yQJj. s9ʥ̞c.Z?[o׾Uz- wMoyU`%?__>`V}{0:2ܔn,fyoB .oYz$$`  vbZ(e?kjhk |ה- -z`NS=[6NdFل1nNl*| qD,L[aRGáCp~|۷mǎ8V\V'իꤓKQ>v [?S]pv@FDD(j?W6@D ]mkjWQ$kع{.QZi*8_S's2$:(OY]0h ;q@@̂xMtnLoCCC㪫_m߶)×/RTSAُmk?i&%Qk$ vB oØ<:m-]9`>X8Tġ33dx4q\/r@rl{-p,] oSd&AvVE6?15A}`+87LJ@қLTc8(ú5LL] u /Ygx`}x~3oGǕ)b/{Dwn3 J?̝wذe Bp/: @G<%7^֎'yn/j᣶Kѳ.gIzUG0A4RFFS.CT\\c8Kwov\@۸11q2}Zm62Dͥvܱ36 M~:TUO]$l⬳3mֻY$Q sl&$i|D nݝLcE>M=%5"S v=Dw>IV>ii oR±}۶Vt[Ήҿ.rl߾-rz6\g7LWqTS.${ 2 !lZSD!A7m3Bu\3Bky1"(يaHE^ݽ{7l/G }۶g[K*-t7B̖U9tp3PF!sR!򐀄BN襞/:=U)4IX=J @#>Lϱ4 4H,(\Dd-ĩ=ϖ A6CE&$g1Rcx[n֧2 <ϛ{H2*V߮ k AQ!|P0(FFG088Q&`ayK T &-19~kIBXŶhd:&6[4[欑N=HcDwe566m1x/rsE¹옹aϟ^KT07(AP.}pM zwyO6{8v܉];wb]صk'{s.LK_kdE#fUBja}<捌`td#(l jFGq BnZX*H~ "8 IS De3:@[G*m 9IfI%l8DOoqB%΅Q nw/;90ưwl:];w w5ީ&/o#r[4 g2_  * b?e س{w .i~`0:rs<:tH38!Gik$Eb@4D#5+J3b\z@xwK Clxјj,SXT:4Ѓn\&(1|h`Crqԯlg&1inm󐱱1Bt g/+@KHJT叠f8йgU3[hUO",$?k}]'? N~E0}r-ԟ\ာ 5)Kǥ[f&k791}nw}||rQqByXcqbֺDptNo"#6p"s"GNˮd~6&&A#082IpdbltP8x{ц#.cҥ8l2\.cզMriGuY3'ꗐe3 9pMpRI N$aQ[w*H(mֵm H 1#8MMK_ϕu )2MW<1` Oi͛65k2 }s=_޻-[R'xXrPEu,_ᘅ,>Q:l/%Y}Fu@y7MC^)v} r{`>|w~{8SbJ \s}ˆm\syAo'T[`D B6,t]CG(Q덦1K myå}%gQ:uD$%Aa  - p )WOc8ǶgswwJu8X1v \5ѡWT5G(Qz:_jq) Z OLZ) U[was}bρ"z?~F`zک vA`޽{N1KhlE pqn v{sbX:-_8,kE4L;@?0( ~tS Y -f9Ao\s^>q-`zzb1ֳ=ַq-uXVo9f2l6_uH)`ƒ5¯o wU%hI3 zF`Â!Nh:ls0'!ӫ,ؾ}[_Rk`ǢR>{zR? Y`QG?"nY0J1 ѩN{-t{@_q{Fl%0I1.9*1jq6 p-e_|݋;n_WgSSXOcbYyM4sW'' "ys(A~+$kic9`h)scfh#0YK~qu睸;A)IWcժU72<\ P2$ rm t>(fj'M8(6RbժxIOu(h#^Wz9`/n+K4'ׇVgq%5lق}wCn58Y93W5 ^6'r6gL (⼉)\nedR?:/8/|aYMܗ3;xfW1{ ?֠ yϳ6 Io9f":ULtky!Cpvnq4.g] t.D4AWWȧd$'6 Շ Z4Ob4i{;pѢg&&&pƍ7{;X;~IlSXj}Q(IX\N[`(6ǙK_jc:V.cgHJ1dZ-ZhnY|jcS@koww AAHXe؅^vV ]7)̱hl|D! ;*O:lB 3Gy zpo׾ػgo{qYS`} ',cmj񟟡;/S u]]/,hn6 t:f'Y-{8xPtdEK<C8P]i8nL޹9'J q8U!1 !%w9f"(D':Zu28a @%Rup"-:'#iiiD!Nj@i~8~<h wNU)s Ϛg0<۝P׏f| E@/V۸!..T-8 9e -@pp,uG!kh) GԸ`2d@D0U& BCyޓ0MlΦe3&Z6ҬȤ` t#`T8I@*8 9gt?43Y:p^x4 wܬ٪EJ_ַ:K@ ^/nu8h p WXwRWOaj⹽y<<c2_q:DI 6 {lut0,!i]QM>^NX?W5ω~;99({Zn+55%s!:i;em=iWK}DT?&k-v1צB|WD#5pӔ`6|̀J57aE"y-gqK3 e&v] ؾq?4m+KtˏtPŅo*(2:8Tp*R(3Q3'W s3GpSU8'aI%W%Q0 b_'0g rln)u*?t!P0B)G광J 1j:a6vYߡgܜŋ5>iK WE8YI&VWRE2H ~ŽF0Uu YHo :V9q9*FZ ^}>W?>Io|b/9MȪ@E?[m}jm"Ht)+_-Ya7)dij?8C-o8nSpnO mu:EN],v'!!<9z6&ž8ek Ui@Ҽ(tE,Nk5FjtH@c03J"BxX~l15qS۬ lQS? өNziO2 1Wh.v&1;H{_*|oN?QIᙔq٩+o,>w9+t)X|gՇgvΞ2(B).\SN;'|v.c 2".*3ݲnb3YH00^9K$i#E$ 2DPWܙCV'o۔v_ C([EԒ̫qo_('۟ ُ\ 7?vYGm-9h7U TCI_=Wģ[36Je̟?y#FRA܏BJs&?!41Q#>ftyKv逢 Qt8<fT%O-(:sv)v<*{4UisF3;GaRJAӎT~Ty_ \SYW+r,6d/= nV=t|0RQH^~p9 ^gFY ?5Lo۞B) cdtPVߏ>JY;iyr3&'&~!`/xL&B("C K6-iiqQV?^0$G#lL}_We@I*o@j_ XK$ǹykDžEdSa/?~/FqC<;p[j@ 7<'a@)L9I`H)*y#008jR b9\m#B066@q)&N[A۫KFn%dqll/8ްe96SͤrCԌِC*2H5ע{J=.,(1]X ݁3}[e*O>%Z" TϸI|m"8xMp'rNn^caҿڨRup:Qrrneb|0a<ÇN]4 e Q& M8cT]Ԁ!+geڬ 5.ikc1&#tW\KQS!7ÌG3Uyw|z_=9PEGJM5~B/"*J>i1Т"X bJάA(1YAX1 ՝& ?ۯsyx=X@7}IK דbjƝѹYwDžޢSrѣ+'̹oZ7`f̃hXis \aW`/N^68]񮿝=JpV@%u9~PCB"t'sx<2?ӱHo"5v3>"?AR?f/{ىr.g#D j(mK ֙Ck LLk[*$#}UaC]Yma褢?}X<=7Sӽp? 4Dw#䑡`"~rw<U83/}X\38Xj . D6`w)9n=A4"J&gv Pq& ꄂ1F,<.S%8 ch3zhvxG&saÑ\krxL7s?Г̓tı&n_P&KnvsS(YBNlǟ.:c6d8&IpQL[@D} /,./gk W|/>,|l>䁺EQ ۲0k;j+}82~RUg*"9}ǧr82o9Z=Lk S=9tIz..p.djf ˥h~".oG=);*cx-Z}vvkgOs=?w 5y΃5;lu^vg>EtEqxG>f2e/cD'x SLw8:Lqdߡ<O1ufho>H9<ђEMQ0?Qݤp04=2oݣ2Ƨ⍿yFpt-ػwQu} rIHNXm|{^)# 9ιǸ>۹%8LLY VE0Uˣ6GD=<Ƨ1]~_*|}A`=s^ ނ܉,€^/13\#"z#ie'jtrl>z:/k`>"- TO?k89|qC7OLIOYr@$ЄpmaG-godϣ1cQcyԼYmC}C+f:~|'5.Q!}rDxDe Cϯ랻! ]$Ҿ}.{eݹN)xE;%b1Z;k?/@ϕMN9PnXCrx+wE F/`|l(m}J%(RMªfxt ?-c+Kp%+WA;OiWv_Xnn%;.;vqBik5QOSKEi&vliΕ[{۸~h>>! N8ڎCSc&7`2׵U@+ T*TVPmn:j@culDZ=,{q>Cc~i?>8t߲$!Y?d]8~` $@j&]z 84`ym{QI\wsg:zL_\hΕA @"E@41q;39PE/~=rbuc@1}xN#L8]uc8O\v6Hn- :^ $b5F3$Q3Kv,ޜ't) #5@q&! &yq쨘N_eP&,^g9[h@'O#13AqD: g8EȎuՄQ4#M->f_xꩧ02:c.lk֮?9oDFo.f&s&Q,0e-B7-عl}IH@s=K-Y_rJUSZ"8q@uDU',L7gK23dɮĺ+NV̕%XH#M#9ɃwTh3Zr:*ܹd3t353$Y")Q%KVǖx]aS ;0Ge @OjɈ Eqtg1ϕZl_?3WP777rd;crr6mV"@ytrT?ݏ]B(ԏo4`qv,4xU׫~OC"wb&k_m4  4~m_m3_iKxLUh>O_鍂&>&\pž.9E{:h v{oQ/Nk۝EI_her[j am8yɳ'gysnۊn=Xd4YJ_XTt` s |e,0)3%`$u1 "sHMfX hVvOUqKc,pםw+@N333~OO?<g) gjxo;vnqѹ{J IDATs3޲5_ٯJ.7qjYYsUsn@Lk7qsZ-ZJT.iw]Q `0Ub 6T7vԤ̂<޻dfhXu   @*\̀q,&c`dFW-KSއ/u7>Vv@8~y;5`vcaIh5ZMBAh4Us_`XvCaXZa8y_*{ǵ]ZrrcVOf>u@OAAv+R3 9)}vM4?-iH5B//z8!ղQ4֝)lִcpI`mطcxxzAG wu'2=`PPIF!q<(c!cg8\7` \8wn8:5ЬZMBIh< / DBc)4tUZ{\V z/osJAdi0,_\MY [Il+ы3SXui k}M,JoOٛΟr o1|Pk{`qqO}s/tF lù4 1q7P a~Yaxry_X 9\xhZCpffU!RPh ~*Q)U<.}JH{eUHv_ >E b p5 UM2;&Í;-x:{/~'rfP-cA;]wǏ?~3̧?ǾūA`s3!hsN#U eڸۍМBWߏK.=u4SR)xA izQw?u1̀r/ٖi&M%W.pe3PeO`!Rh.$RPPJZ͢@7M9`iIO} >O/|fOxT|Rmy</FVOW9_q333CPs] 4E)JRĀv;/u?\G A_ 522{2qNM_^a/cY5?u'h{sDʳLyǢjʧ:<}\}5޿6@_>􋿀^z~}_g`z;7x< I0ky0oZطovލi%lAh1ЯnSG(4c` ʁѸ|`ޓ~@cX^p0@]#*e$֯iVB'EA*}޻ah~75{ދw6#1pQ^ЛO Yyk %*4$.ڿvdi/_sh4h4m4׋S*@S@@Epx>Gc130Y)d`3Hf9y+1 ք)0~D.M`çsB@3g=$PNf u+q?XX8o?>${ })8~$,>3] Կ2dd;yӦMq}عc;A40zZz>!-jo*tTw<0tj ⡹As\b:0Y>-[.WU.rUK dŀfV" c5 g8՜ŠS޷6x_~k BHY0Any-W1$:XXs =_x/zGȋ7Կy-9o߆m۷cӦMD Z jkdA.!c?4EX~uԪßZKm-`jn)w(gDx+RmKzբIcz4eEMŪhaAT0f'&q5yAxqM7miC.AK sN`~e]K/Du˘=ӧٓ;`~ fOc~~^g߆rTVMBƬll9Ė͛i&LIՀ [s]>[m^|r"m~VU!tjR3o4pm l_ֳspH]i/(J"[la!0K}q=<6L \rk8:1Sq+{ @[CA3@h L ^Q @$B-%30331A D2:%tetV!0{~ pp9t6ÄP/˭pZZ_ a@In}tcA`d&>) 8ښ3rvLj:0Y/%J8@hȶ^HIL؂>/y mC",82>7z^~pzi"̈́BEtV ~ 0 dUw0O5{Q^`"$QG? صk.87-fΪ!d8 lߥ iU A\!3`c E<54@0S20D5w̧?v俤I^}0Hx"xD1 0KFBl:mL ;w֓5 uZ&fcxfv,!EI\}n/@8d @/d@/O(biZ8޾K,v_???Z޿%dy @Lu ݸ ?q1֍+˵+{cxxvCso5?fK`Ba)׮%~abC!&0 r?X<" _2^zہFސ@^>;ٺ2 RFW #W`4 *:'/&6I1777%d\/u _L@(l$:@e(TlߠW0ȑ#xGt27DʷP 22K:LMȼM퉇! \h[^0kk"Ut¡ɡ;O'.DumV/OPr'"DOqֿjPWFB ^ NRKjR@kBb>[A+īlOտ&p5Xr{KHNӄI#KEApJ5V.txKHm#<>1c Ͽn"h)CVPd D$! qe;`9ri utCtA 3OL4s+:ֹ,%. HV&/m"_BkYxnrbD^>th j¸Z߯z H$ SoF`a / O,/jaQat#DS2O9mߓqbqmCEAl -~NCWS(`(?UXXEhxph?~ϽtX{Y'(}e4j@%/B?u s!B=db!E"@)||⟿C^g$ݯMS#[ r $Yr6~d`¦h+Y榲4 _o 6kns#$KB(y9j266>r_ w%UԼ}|qpɑ"ٯw9@*}/R\a(ek"+؂4̦M8pŁ5Gx>)f?i޿HY(#"b>qjѾ)Joܢ3.:,/={C v4' lcPN|Id(<~7uV`eqh_!fZƒ_sA#4jz|-J*j|zžU owRā@"/C Jwcد:Gsn ngLٟ=oOuTCd ݯ._PfrE(YākfO),|'3y0ۆcV_`M&\Pp(ULxv֑&ƛXBŚcc( jZʧZO#k$HjՕ.1=GwN^vyFX&!d|6gJׂ~Y²(z=FrZiby?C)n333#߃{x ϾR'#ppujtkc!.S!j쟷{?w'rh6L.\V4eScbWg_uҌh!憛ba> `e9>Moy3.?0Oÿq<p a=Jg?#owxKp&~y\qFu\+.CI- o˦)xe)xuzw:nR}Av]]m[GTof,f^u xoceS-"*z5 Eˠy ^ {ݫE Z'֕ehrU=[8 eVRTekV >0mjmiiu]ܿwk,ߕpAx>߹L*:pOk~+*lJbː)>]lƛoI,GD@e(TdEV#۰)^(U<d( ^wV0?HF**.>{2}{'daOHqV @1"Pa+Lˬ_ǀ yc KGEDck@u_u=&&'?i)^~/9 eQ4EW8N^bk=,bNe;\87;4n^5]/"y:2" q[_SG7ea?SY~=bJ,u]ƛnJTU+$l-clZXE+/Q\hp,G0]"4`!;=-C x v0}O@j5@c=]n+db#AeT?I'um`Rjݽ gyQ!< 7z1 g⯹1{f e¡ pS'S8^Xi3 _5 IDATN8ԟ؀{/|lh#Q * R$I4V{zwPx:cEKQ "r `2<2!sLBT$K@_$ECiTph?z[qǘFa6b"I.v?qG'|"=D7qAdlOHeEƦj2[ˀL+pZ"C R28p0-Ddžͣ3_4Q9_&{t"\kBSd%84JP= J,(\i+")]΀aOQ@Im ;XS #lv=Nnτ5wTLd 2Nv W6aHS{U@HRm#_LS<^",ߛf?le(OrEÚ:^X,ll:QC!`Tb<"ي&0Fxoæq<{PM@e|8|9✝[!D?Co<K+` \n/ved0 D;VWJMCl[9÷ne^{ر}sa%z-/Rsx;n3.xEVr߼S/ݱY̢AY6qY-$h4^zR*o6djqWLD´~kN $hHi|8rÀ>(jbQIA{ܼ8S310//PwΎp]Bpp}|@|<y!hǛXZW zH!0&0=9>^9z*y 6#1`o'I]۰dg003kONaϮmzH`sШע _Om:ᾏyv8FtPfjz}:evM7 RB4-fqOfzJp찁j'PLUV7g@vXAK+󘌬c۲G`eS9L.^{(~ |qqt8pL5YL7ҽpP XJ3?s\w'~n9yy>t=y6Sg 0 <TNQbj3\g|nqP3yjP}"7m^(ht>.,{< > [7zB6S>AOxr?ŵ2qi =#|ۏ@H8Ԓ?5/F{O3u&秞y¡Å:O?2p;E䩳h;>Ba'~1</X\?8R~ ݸt: eې!,_5օx;!S^{fgg1,/u]{~Ў7Rc"^&1$|}߇Dh $k^5) C` QrUV^[}/~׷J JcXT VPΐ>kЄ`&ϺJ& qm@WԱ]@Z`p( (VdZN1ԗm9QMit?v.8\NRN]Ϻu! 0b|~RcL ϒ޶XY:CULj!;_1x ( 8^ bU}Ι}8RZ⧗I7wA _)|2LQDPJl ʍ0[B,Bo} ԖTH ss3MQp, txe],чԳ?t!TEAQןEemr67<[T~-h:d=Wt^ϺkU?0Y'QUF Ҭ{, XJ~!l!|YpA=ʏ^iR+/D_s  ?뉋U؆ٳZ1S2O̊dGK+ e[_?)!Oߠ6d w_jnCΑ޼KH?~TNe@ƶM|+~jO`-Qe(j0M>.WgN`zan&?Ott]—> ?~IqRH Ć,c(+rE3{ KàY鿄1V5>lz-="a`[gObj! Ԥa?%`Dر}Fa2zzDcA`Gw_ zZx .T2419SDuLPNv|b1.(4>w^DyL{i"n8wf}&CB(sjU& J=deq>%N[%E*FX|%mCEZ!x7C8Mt@w>eΞNM`*_8p~GqhvH$HC/|- ߎO_gP w\%{pɅowXYY w 0Np!!s8 ^qLݷހ'~>g uk7\p.\{>9~ʺk\r>ncX\3\/&B}7<VutШ{n>\%n2W]څ2BW_o}It:vJn5Uj(hMT`ߺg§!R{o[A^!K/|>s/<{b|;5.|S ixZqC={ߪSiҿ~C>ֿ>(8Bx`QЁ{{D1$: xv<0AIUF 탣 $=JgFX'2ig.c q88s?z>^a|g1G I c9~3;p^E+ 5<0(N 8e")ǣ/Ԉ*;Ux) }oMo٣\+&VjM,-x,:?~Zn|߼!oK= Lӑw59[_}{+Gp@uD$/h>co?w݃Ng8N?2@_;}=_HvԤ>_sفe]IJ[f;nX/:/59ǥÏ> ϖڛĥfD_#xg{ 4 vո8~4>obvn=;7 yYбl.n\wnP̼P̈%0v? L)#ҚHGb[yP4}x600J`e򂔋@vB_>2hD'²aG0%zX͜@_aq>0;FM&KޓR?>;wE<~@?87|wܸ|gp/ߏٹ L(J 'x^pͥXXXqwtpO`xCxBԳppObɡ>4bo=VHQo7|ۋ|' 뭴;wǞJ\z%\{`?vڊx o>jGeΚ c F+Z' r%gNdb %kP(:d7gB@yʸlۅ؜& wjC?JwifH) eZ2UqfBVP崰\nSgsKKKKa_$"k@+:v_&ҿ]8Aa I{nƯI'StZiw")S;N++r.8Ф y盜L8A0$3dV'J{X7 (w ۋn3E.y'гnRu?A7ASk5!DUTRA%VO`\OUyP/fMF4'J [Ԕ9&9 D(2 Q1JLF'o/VxԶ!r2L/yNjC6=ѧ}%ol|pKkT@^;D8x_p(;ei&_ PG?RX*qe(!2  _S(oL$df Id;܉ LfB /v_͢ D4[Tůױ;똇xGNn\  fln9IR>& !ejyQHWHϵol\ `M d*g~zz:Uhsl~4 zͩ*3>iGcD1s?~5q֪OSQPשK o@c I LpՇ^es'TPe yw}&;@9Adwd\k2@`}]6KX6I}M8hU^{"F:4BF&U-3n"7qZFMŁ(@].[)ҩ _ 1#S8J?)PuU@iY Kl>kyfTz+8A_F6e"yy[ lG$"֠ *)d QplB/?oj6בczpKQ@h5kI~3j% 4uW^4bp\vW+!'Sx )hhSh߃}nOX-yMOSqBuUqn[i߼Ae` EI۪Yv( mPeъ79#Ywd$I~>fz>@@ U̵l7l(a]P@055eADґy,d9{V_ ;11d[U;uYZlDXk+`K^ȼ` T?ÜҧzC@$>#_HBZd6%1mݿ1xq5MMM'U 'JUPㆅN6m^uiOW+=,S,Fϴ 6yI&@ms~'%? _zB'F`/"Jc$r `m ` ̎c Ӊn<T=`z/= 9$PPBc+pHJx&`A%q Ż# Rh5{|'&BZ_nK =~Wk?ߘ֊)W%bɋNaJ#`jj:tXY _m(F[Q$s,zP0 KVh{P#g]ϟᤚe΁J ih?E3$9#e\$c?RWW_ /)~$IR'?H[w`0lC]{Ȏz# y!﩮`)26cY/kUV0ubrUn]gpQЫ"fs,, "9irÚW{U@jy%O52d<>\ /%VA?ƶBD@q1>> #)_:@П4all" ԢcyԒѶr٤<"lk!rf-b9.[!@? C?.(}RE_FT=R`^+I<6rXֹ9~@X& xy D^e *.+m[L @?kq}~AHDJFTWW‡sNHm( 0cAUt*hûB|dϕe ʌqÚ Xy9 _c$e{I6i^P,Gi4-Lv?,Id#$?%@ T =YedH,B' SS( PY چ#zE[s %So@)j"mVV@/!D?c?YT%`z5@k R@(ZEmEDŽ*du*_-jUIuEa=&/ }!v9f=B6x_RZDi0VF*,bߘ2x֪33 }Z Y],d!abN>HUd\ȉ9tW/a!*=s&VPc?k~W}i@R$/ 6,[+"r9+[l ڱ*>d~Q>fρթe ,f\OSՖ(?3EGGnd$A .Pj2 yʶm؊ AgB\\^ɩ"@ YJ/yC& x @^"D!0@&;je_&j d,|߇}BDwIkDԾ)ޣ); vq0 rpaE1PQ&;њ_AE@&z_XSv7JmĂw%ƬY޲MU=򔲔D 6\ ٵ*/kѕse2@ LOj|! E9x u/E`n+NABpuaaaa~4]} ']{ nBCp. 3$ʥ93 u"ՋZ*a[ZT_YDٮ&)kYB@14[y>s̒ GkjR@$EϟY/aa~ s;Y,.aqaKXZV汲'  `@'|WCHlt9/ܐtWә4O.KDyZgisly7f"ĐCYACs8GOq@\|= 4cEKc)ǐ6oc4Qp^Jc3b0j'd 5n4@Zh?xgC`(/9Q``є3mLI"(3 чI3/p1@ WSz2{^y:ld+{2' r9tW PE[bn (urZI05A'8T0R.hQ ͚@!/|=&yH,,stOT0F_ pw3  5:G?eCHMe݄8~~}5>g?68!P3eeA֦41-O0D@[9RV%n. h?^r<~eeV/g Թ@hUhzޭ@h5cusCq^4:Z6>nx]T}Pz<#'=6i{3= y`%(2؍ki(=Q``<{4ÏX7FU4][ELTυ))޿j4cNNOfD %o ;ږڙEPR[E;*#S:}2AQ+/n$,,nݢUTCw\p@ ԙ:h8MG fM`&Ш\ыb;pFrj}iPػxye7NZStp~J/j5B MP_mϟb9YKN=|Ya'Vb膓Q O_!l&"NG xQ+T-2yl)[`lcl,S:I»R%iZcv췋7rR)c p} q9P @ ˙K3ʣLF4Q(Ϻ,)YRѨ S=3lI*qMzHSB'Qe O^BIbVw:IZ<}]Fc~`q ,K)fџ@[L%5P X捗(9.{-L|5XX4EVXnFѶ'[c!)-E<Uւj` ~\v؇6lME.&5&7:JmB`(a$+sQ`F,Td '~v .2 prkt }߼O=߄k]~?Ls?7 3Zq2b  7ڃ^@iROTd!DFC+r\9YU0 Л*9truM8!RڲGi"TWq.zԑh QLDbT%6$g~?&x~;;b;,Q\{˟(Q%w1(nSB`( Ͻ'`'9 ]r=s;`B v妨[Iy4*'bw'/|&-SPeQ "%υ3K0*&ls1_ ?uh(ffAdUO{eӻPJ!ƫ7,%J?tЂd׮  pFS.4Ҙ~,%1n$:ތceU&T*qmr[@3()W."6eISbP,Sezǟ?^+Xh)\?(я,ŵ~-ʧw7x}wn$zW/F#/ h)O!^<T:EY,rw<*WVqTdFGRE,;9D^s]2LEd–%Gj 4 jꕎG@h~姮 cwn{I! IDATQbVX8Vjz}g2؎}N ?U.o]']OAAYI2"w%c9C;T5tJ:dJ]eR6QnkQɧr@݅YrN];4!jN1Ul}LeV'\!] k3~Ͼ/<Ȯ6DO 4?jdJ֘eC#(͎C$u~;Ktm;Clu29R0KgdHk? 1(Uii.j&5%oek Lc"p"E-3&(iVulG/rH^V9빺H}}l=an)?%]oY1+b CHټOQxC[)%w~Wե0L*-O߮F1+ ħ!v#ed=c MA19uQFQCb2dyA顢'^e\E1jBX8Z6: ?~0Lxުpw;Wn`ߒt\FYW-V^paU{B;_]iN+A$A2z ()+',8M'U.T>+NTG7`#ymEJji <P'd0) $'QΓ' -,"2ttM_\{,?oeIut:ʁ`Ql^i Qo1)H R@kLPRa04vŎgW,T[*W/bA$F)9F溣F&k X R6EeaMtJ~~M;!7Z#[JJUReDwW)[>@ @9q? qOFLF#z0?x'fYpCi.+ n &zBT@8v߷Gofvk{R eO'V؞v;D(,c@0ָ"je7M[EL]b4:q,24w t%ebw|qnx$OH/|ߝc`zve$d3#}m#Aao܈ynQd7nܨL{t ?a^Bw_b[C-l <󞭾/pb/cuM3NUzJԚI@K+ LBWKKXoޘ Ds RA-nrL$Ky{y#/+ID@7=|}z| x8Ϋ'[a[8~]+VW$~|| 1qJaaa VL#@JN;;;I-zpHgOK N9D澳<| fK7qf3$`a+i^ap7*VZ6YC12hf8<\ʿ. K:y 9 8 uzB*SòmҠps 3eH?xA7Š#u[] {kɑvb*51{ OqD W{mo"\Ś+όWisU83iŽg&+;zte*&b]FC[&ۥyQPpyyRKujEN\,qĬ)* 4,vEÐ*ik+[>uefՏn`yI%0>0qOIubI1,5VxwS_k7|-bi~~!:6Z&20  '*PLO}&8z@_&ayk{,n. Ǩ{ dĴB׬K~f؞,!EL62ZP79eKAVVje7d3s =,+FE ym9+?t?t\8`T ۛ!׉"];~0]k<<+bo#Q6[C E]I:ł`Gex1;fFN10N045]+ ; }aiVx>x:Jж}MQ \)Y,d@* )zR9ޫDh.AIY%߸ עv$uev_Z[[0ֺo߾+Л!iU$&)Kȗ_#J)G k"n#Oi+ @|^|ӯ`]$TW!y<χMR*!A`8D_3}{ @zx mlg*O* *$5Xs5mrCmAVxA%dh<hc!QJQyEUZCnξ*ej* uj:-( σ@V6Q%ܽpuhg8Gǿg+Q 4y.en{o\Tmu[=^h[5zRBJʢ'"!FX$z{ RPRAAA)W RʑkbG{8qxJE9Dﴀv0)P|k}[8A'pU+{9J0RYt#rB]JqjqU1Ul (*jAN&@Rq୮"xQi4@4tN`m ΨOJf}[xuC3džx~K쓐.cil`&&IQYH,(<|o+=<| zGа a<<>4d-&}`Z϶-4A~˷2U9o?r^X2%d+[j2\,^Y* QpMYღ kNj_Ł ~P@Y8Ie(BC.$@CzVG2dv9_ԄV[^?Dqo\n;!@<`O]Bh C8 6c@ YYlI(Y¬ +,bQ/|<8en|i"#!S AuMC6pȰ&u\QX N<(&u;d .h>ĄdT.ٽF<=z)^;o,-(s' `8{DD޽|_YXh:!(;G Z9UθP1 Æ|Kwf-0W'W֑apȤNvϭ]0v;zֲȚRuZQ2,®kd]f쾈>(+ 4%df?y*V R<("9/e vԿHwvp+޺&Ukp# ppo%e# 2} eI}/O9,@X\3A6u0E+(C.S.PuА/[\w褦nG)P5>‡Q3Y떉nS*B$B YAz^ͧkk"8~ti`^? 0AձVq@ sN`ς }@BE5"OBQI[ #.XRABTnE 钎gvN>q]Ji`pYRD, 'g<#l_MCW@6>GQ2CYۂ %-$ 3zPI@xׯ{8qX&I8R¬WjmB oSj9?c2hr7=Cֶ(Jq@i͗TH6T]859Nѝ%TFN,pxCe=QAFMc&|e|f" Ӂw0TT 0 *;}t˸)., ﱞ0d~YO:k5gO.mwaLn~d1eH4gK Lj|Lٽ'FUݷ"%UʔVij(kWA42@/z1̈́ xv><6>xx#ܤR-^bX]G9KTRˆqzٲY8Kcpm:g#]|&"m!"G44e-&,*111BK%rʑ?Xa`x;tc30~bߴ WhAkkrep@ΔboD@ehJOw}xܻGZm.Ykn rq)C$4-z̤GrJϱ~ $¿6`a#LQqlBU_T$2u!p`JXp>׌(-HLJWs-ws"eQ:+CxS1 KЫ i؆@13m||B GWvR}xG@z;]!eyY@ loVm HꭏNjo7~q$55ʆ*,O9<3^eF:OfQ An$@箃۸Io~jR&ps+jxV \&0C6.bpˍlUXXXKGYXJW3 (A<ܦ)ދ0`:peNjӧsרd eO>:D\&T~DUG S@:ls1%p2;ø D ^o\bcK$?& Hyv z-^Y(V$>n 7/>k .Ҩ]\^]KbsP9֡UD>'TEr9eCRw 2GǏgZpr -⢤_Ot_5G΢**2LLa (#P%IK<2<S?*|n"'6 ΜcTcIG:^_rCQY5&PIxE!aA0$ w}9YifPT[e¹?6D߿<ԑ贲gW|[e8II}l-?7"6^)!yY5(yc~ΓH–N]ِ #^`RW= Z]-*Q7,{Ux ]"?5jX$(b?Wf3NJ2Tf#i v/*OS+,U0yRR/߿gXEx-aM)_܉ ,rf#+񏍼 b<@]g@WVoq{R>@[nu3uz3I^w>5U7B(Jĭ3U6jarxC)N>w?b F"`{0)Ys ~T"f+p:NӷGo@|3_[jyw' чO||sӇ6ZǶ* LWE\ #ejT`TB$)jwYz?_%p޽@-u (ʺL.PtN`P2^x?>$)}m4 ޶OlDSCEdzQӂ5K?}lt][u-/J<+ޗBKxu7nqt+Tq+=CR69PN(tpp@#<ްJ"寮,y5-O[6|mM ɽ{:5ɩHW;Q{95=Na(y.l`Y$gEǼQ և<' "K; _oޑ^x| B#u{82鼧`U<޼">|vo\.z9Bf'etkݧl$^čr4%6WpY9P rKLʆ 꺒#Sgw `"] 'pb7;a+eSAft,secQU7('"НS<OØ(.\+܋̗;@  І!q2qi߆ހNjoWm_cۣ5}| r)Ek>Ǟ3LJ8{b(,/{x5 ;zMXe|qA[$<X )*)36"XbL^?9F{7TJUZ;`U"*.3Ç<9ɩjpe  OL{a4eM㴶'孭JӚ Ce)GeB)=+[/+}\n'ٱ.8"Bubk4ŹԸMxUzkoyb띖7ЪXu{w&yy R{;||:gm|+KCsX=oVŕ([3 6@C i U$&ʱmSt\8sycxs꿛aItԙ3O *I :כe)WE(lRc A2RtD! &u"m\{%Z:k"Q$K`Zº|C`s[`sM6acXl& +ك+C&57^W|K奐`ϢbgAayIbiA /=+s ^2ZY:6&J_"g^8P׮50[C0}^3*kkWAAZ&M Ǡ,KBeZ5:83g t@}vmm.yW^ C"2*,6sl1Ҋ9c'e{B'so@zq@Ǘ ?RZ#9/KV8wr_{zϼ;zUܡ\Qއz5/_s`y JJ\m#HZy})r hJ( IqQg]܄Q;POza}vg[bt&ՙA>=EҊ{m~gcФZ }llDuRԨ)x?,A;ΌO뱼(;oԡ#Owpmsz0 oxg mwFde{8Z,$X~elj֚Ȝ29%%rrq7z=UF wLzVkQxr},cU(Ⱥǭ2-&"J"39YZT&wFgocBī%Y*^cP/߱7=cp@yW'- >;!90\ߚTPX@~et,$ DU&@@|B_S?B 1ls^ۗ8uxv5yl^Cmw>DÈM?޸2Y|}.;%q籭\2YNjK6Pc $=JٱIꏒXXtlB(!0xi^tv)1]'v6_@>k7nY&#ϱ8}LKh1({!@ᕳ'Xd|MnGl[Ty_K'*o^mD Yhom 1F_}dBsg[WO;X5%7j W8q0 f0 <| 'i>_|=Et 럋Z,'ʕ/'/<a>ڮ!&Ƨ! bYa~^@5Zq\C` p ʐ½Yv!BDWrw!OF0#Tc&к?DhpAf>YgaKXC[g{g` aEW1b?~9`iA9 N PA+oyWx!FvKb!/yGdR}8y+7ZMC9~Uzǝ'x>VGݗ 7[KB;oaeQ5ƳI\缂YEKVFi5\h Ü]$='*kȵ&괜5tϜA0v\W7 cmm,}h] yma6r`؇#g5~3G@=v*(nÐߐ}7 y,}_-0:>G}ok6}V"T~|4r_{m \]P{@}  8y4G;P+7 yxUK^miK,,v$ץN"$p(^@ la_) ~C'TE$V%v`^U$ءgyW9 VC`~O0~X%r} s ϣ "t@쾂Ȱ)?Bq8$g.9.Oy ˂e]ۘRP `7 \۽U<O~RF1Xd$]Z J7& 3!K,p-//yqt&zk=< v+,i!|m+(\xgxUKOL Uf([GCVOy]z}5z-ٶQ8*Eb0 ހv!bO)s'6 jeEs?2>; wOKqyk-#2,dT*'}W_SOVޤe]!~4!ki;V+|j%jG#Eyz \|^\@Q `Kmn+Q0 [w3@pGQe(/+XhK{|+KHdDVdqGPO "Dyrpx ~G |/Aa"o?0%k]$0b.9Og!B ҥMvydtz^i쑝z&Vxػ$ݿuA9H@֏LFʘ(Zdy 7'@¯/#Ԕ𡸸 Tdܴ-\YtiιsSźV~͞âwSQ/)o!bf5-y="2ɡ ]oHj&wpe1rwq۫j giyt>=}wS+ iXq$`ʹdT(| t@xQ ,E ȪB9dhw5"\igww)4,"ǩ0JoY:+yseˤ#FdEqv3?W gQ4҆};kW;xRC%KPiQ"9uxζW e0LǶ(xR/_4VRXf,9C%~$V|P4`ƀc`azgxNi㑅glޥ˰6%K\Ηj8xƝC7zivއ_W}ޓ5l,klLU8P `ژ.#"ŭ9(?τHbQ'uv?E qx 'v~ދ(R{X^@2Q7đ 8 {|c}u|aS_tL",Q70";^Xm߫%#;h{do&FTl*&^M+tNAT]@|eBJf' JK%K NQ`x2CF dUAy`_CV`>hPۭU? ?V'}Ƶw xvY;1$6u w,{AV f6V Y{\KQڧ V-WbF )wGV8k[.oMKGy8i  uZ/ocxz _mhMtZ+2-賔6̖y7N^9cev/ : xֿnPIȎ(2,E+.2qVi/X\;Og\tqQ{u݋kkk V2>(?9(Cf+*LZStEu.VfB*Cpe $D(ueXU脢)%GXpho\c^ PZ mNKb# /aﮎ7%.<?v;큈Ph{-_)}Ǘ8g|?.o4Qٞ/7 쎀VȃCrƽf>R&߹do<#̊V ۏ> <%nb/drw\[[ƝWWq_9,NXpG1TTe H1q V}t ^|N(Qk&&8KMk(wou/]hTUA>[ r\#%:(^/}/5WV\f.H8eő2Rb Uv(2(ﻲA$3/qVW.Z/.bG!WW|v30طod I/t&dꡝ(@ MQ"SIq>Pt#K+G8 $6In"*UN .TRzWXYHYLi5qXMm9]yO8N;)9&2MU2r Un|ҟ\)2UHzF42~hr<|*8zrZrWxes@dzC9<&)Es ;u .L3O#;hGd6~;dgӯ ̖=rmgK2 W3 WϰR,jp<$wf4 Oix$TFQ) 5, _ #Ϗ{@ R@"Ksڭ&l{KWb&Jp%k iպ~AC֎cryUlLJ";1Q%O΄ڷ3'ES%bBD8ѕʶyKԡb F*!.^8K29R׼ob,TK'6 erjPU01Z#Pf2`JUQUo2{9fuƓn3~3>CeXt􆨠苪\ϑ GRWet.c-{,}*KqV"GOsRxQ8T9* }"ze($==J' lHv.ϪRPBC?,-HN< f;9}x1 *@8$tV5P&GL(O-"S*gxDAE&n2QS>hy, ÑI雪pQM)}嘯ax0{ ӧgm\%5ք6SqytV+sr&$Q}moC9X4D)m=}K/v PiW/V5:6/[&*XaJ|VTIkW;b7Fd$#x"dث~ yu sTl1I] ߨȎt|O\kʒѶܵes9Gy0J u%Փ=w8lj(L иUaB\\.M㎁)pW8X(KAa*cc= c,*#9Of~_xp֞wYםJVE&$WdD|)e*sXR!!e~;)~\}"Z#!6beP.ObFc#4r Y`ULo|<&~.ʍQQpm0NȜD`,tLDڐ{=Ṯn{xaV${ h~ X&ty*ߣl_٘Ϟ{i8)J$㞿UVBWjfn83+/b'fYIvɬ3CYE!;:0*] e,Ndcf]#9~xRw~cg{?sb~ @ϒv@%]֟,F0q 00qJ*wb߉ 3xQ$@:Q0RsDI1FTi y9oIuBIZ f20:@: HXXΞEc3]c3R;\__󟍌֬*n.ƍT6x1P̭re#-Cflka"@Ҏ_P/E1/g<}灉UE64AWe(&QǘP4RwydQֻ) ]޿ A$"v?  @ˈU(@,$Ue'NΝ=0ԑ#е؎''xOn׿!?6H! Epmq @YȀKS lx5B(bF,`co1_z~~J_xf@I( >.I@bpD,ajNT-4 @ AT=f~2f?(ڨYlApt)c %asB ZR3(òÿnx|'ĩS{ˆ?>{Ӻ4 `~NٳD)BdL,ۊ e"){8Q`&`FHB#+YV(`1A{["2Ln,fR(֭`Ő3#{q2Z}~hEDvNx.LCC< DT-HBς?]wCϞؿy؅<=i}| N;Oci\Xt +WоzR6-/qȌU&:9|e$y"aSK A#,vmZhQ>*klN75}ZMRV5!eIX}>B!aE8f&fTɜ/m.ljy |1o֠$s-cFxE.UU6)Dx ZYw/}=7 git־./c'׃ `PR" "D WJA{*P ,C9|bIs ! ܩ" n2 >WTH+bX65 A$ gוdÁ"jounS%g eR.eO+\lV h:P@,.%,.-I:~mmŒu6{b)Id0@y DBy2<_ D< ߗ"9<*U@d4HaA!UϬ kAٛ4UGSarևM+~YR  %}^ָ WoQp.w Fv>0Ⓜkd# ϟ~;Q4;T6ɧQ\ŮsΙČcUתڟpnW.[|9?9*=O{<q?$O $dL+tHbF_+4 ,nsb!vmTpsdn}4&능d#Ë(VcFu^j`Hhӱ~s(a|? %9k >g\__ߺnȿ~ni߷'l ׆bT/*j<.U:9FT3{Q% E˦hcBFUq6gH`|$@DFn'$}rI>9[hs^8L37LC/uK ߅虥T1#Y MPB%ga5V(ʁS޽-|2HXSrvKH(@NP 2{,&̈́'mc Oh#q=7M^n_o}}p7BB1Q.L%e PN}pA'2:¤@`E2O[__M'nkU3HŭnCeb%9tK_MZ{q`8a>8l a.A7-U "|One[qѯ- n7IJ0'!J;@=DJm'QHAEF4O8 DA0 eWlb=d\`?"*J6m C w@*RBH'AEJ?|ЃRzAJ/e`h 1@ H[F]!v+.=~pf!K|LW }Wbu^TIEvˀiٮ@^ >8]P(=PMA#t[;jx/2"-7j{n:3A~fdпIcDADz!|ןp! D$aIaB+D! =|#D  !d:yH<~PP< 0ҌBCL!OF@J^u@#!A)/>R^'`r4 a~x>2ec3 5g,# XAl $DH()0 ߬])@&0фc! 144DH_6" Ah$E}Tb@pC21p f0Lư'4X V[2 k@@`DABSgq `h@"%!Pb$@ {:/Ta_R`VH7 &Sv/gh ^}="ͩyHd2#?L{CK q$"Q?Eb8))*x>^)9] +*CN 8/H Ǯ 2 I@R(Br{\gw+0hywy 11_e)ioʝFYpZIaY?Iծ& }857B}˥H"eO+D۽>s`u4Bp6p{-N)C%Fr^Cyf4.TTjW$[(ÕM1 `jJ}J^mzzK)lhJɊX=k vʹ0Fs# ӵAب$c8`?},XX>g%|B(I7C,f']'Ìby)L_((_ύK\]bH+ Q^kd+K3RH+sу8;l u/x nM& J_IENDB`linux-show-player-0.5.1/dist/linuxshowplayer.desktop000066400000000000000000000005031323636106000227350ustar00rootroot00000000000000[Desktop Entry] Name=Linux Show Player Comment=Cue player for stage productions Comment[fr]=Lecteur de Cue pour production scénique Comment[it]=Cue player per produzioni sceniche Exec=linux-show-player -f %u Icon=linuxshowplayer Type=Application Categories=AudioVideo;Audio;Player; MimeType=application/x-linuxshowplayer; linux-show-player-0.5.1/dist/linuxshowplayer.png000066400000000000000000003154241323636106000220630ustar00rootroot00000000000000PNG  IHDRxbKGD pHYs  tIME &,iTXtCommentCreated with GIMPd.e IDATxkmYVιjniav"! -@Vpz@daaE,H6K#Gy 9 N=ozP][U}֭ǽ9ccZk}=u9g?^{9č7ρ~g_C|5^"&w9894 ipApG<^xù?9 ?}߁Oso @Vft {g -|Kh[mKh=m]usw~}fߗSwNIׇWܘyV7u4{ǣqޏgaP#nρ{_kԞw}{[߯{}KhIKhOvo%wg:n3ӛn2W 3^$,+Wu~ٟWLu~ ~w=s >roz',3tk#7s~jA|0 N76b^;- MS"~_iX>9n9muNQPoo{3pC8?" - @:֯{78g{$&ru?"/zϿsV}3[<@M&F5 `X@?9cT22@L'邁d'@D DH@K?d;qABvx$O@@}0`x}ܫ<xN_* |<873bڬ|<pQpNVN;fbƂMۥl~=w@ԧHoѯ` }=،C@6uv;F_G\%!3j "PCJz.;\}q[e9Pv@̀@:|AL$F^1odV=b3vBۡub:tyۺyZ_<(u~@FIFdf"BX)lE!=f}~UD*: ut` ʼxW߬ti~T/֢^HN+z 9w`.{|jko+<Hhv?"t vn,mMz%"ABT?'4]pcӐuNߑ(l1" M= _T?B:TxZrwk8 7xY/jU|tt;V1? } bozv8*o9'w^?cvv&GL!C71 7IH2`e>4 7P ضC쌟E=8]2ٽܭ8tD>h(" 0"o^&MĆo\ /cףqm$AJ8a ؐd:4Va Vi&aQz}\jw1'p^[3E {֡?)*2- 4>@CCd ie)c>Z>:::?O~d& MP|clSFIu6`2[L-u^*g%8 jso#8-X|@D#c.\sG+ _V9n ?ߞY)ocGh3),#w"nlDXm7"=xOj珎)*2! q柆hA,o8v/>`@#~`ɩ>^V pttw m"=V|ILޓU \vA0Y' ~| w@,hx|^ * wv<Ϲ2¤BAMɷ3ߧ ?I9zDQ|@!{z֝5Ԝ1?/9#Bߝ#&!l7X˖vYH# ,e ;:I$zwEH`H)$Q3Hszy!Cpl/~ߜBu^So/q7 :m it3:68m% [M~^Nϟ=RYkn/1?Եkמ9G?g/^AA").`MB]Q! , =|`t8~ePzns8?! TE! ˞Gqojwճ0΀ ?9ECX.>N`,= ihA=Jv(cN@u[b1HMb! '#[v1O )T4@n6W8;%<ňL5<6><^oBBc  _ꛎY;``WcMSS8%. [ H;%Afq4N"mLt9)]=jn$-| /xw!_JMg947Sh4{\h:#;8+zX'kum[.Bqj쎉緥p؈Ia0fЦ_W#h ̉l&N&]l]*;A1 PaH2y~x/%F',7^߈%t-XEk z66u+yz.8d}B(8y`[{)ry$Ek_[dJD:$XnmpưW mK=]K^:)io'KQ +qA~)ӓ3 SŲ d q6.J:;Gh:is25ҿ΅ 7 AtZoDZ*8E4ԥeIfC m?ZC>~,9+ޔ4 ߌ6-rܼtEgk lnPu$M&l1Ip,K& +P~#!p-j$c=+=?Pd9!,Q#po۝/;*_˸ zRJrSn`3wjfldhe\vl'h5@jࠟ @ ; ge Tc7?GwP>؋M[[7|OC NG.=xh/^._tz.AFį/n pZ}]u Wnop[8kP?Oֿ7$Bt]OrAbBrff863l6}-w߁OF.}BQ;JP=&~.F FIj}0%C3z@p)û} ~}4?|#G{ЉGsmMNO,k=W{ w}sWꇎVڳ^}%\}xr"wHcb^z*0>E1c*@&c)Pn)x 6I: HsS8ҒcD@ 7L?8{ ƤjPq4=r8" *P<#3Tω?GcB0r%"I^~ A={{A/ޭkjoj'۾9se\~9\}e\.dU)[0rfnXGjP,0p.aʱO) `"VC*FK/*Q޸ϢKJ%NtJ wGk) DCZg!z;r 2|CL=¤7zvdk%.h8H . tEr՚:<&{'=v`=ԟ.\\ɃjsGGG<hNSWl_ƅkpϣҋ/›o!B5M/ۆdGƂu{G$t$$j*e{ gb,`$$TҠXy`)=.YB&%Qi0 kX@#'Ra lC"߉V2_+H\"A2%o/;]zPyknߠY(5ɯi}GMt/PG'T6W!Q.R2揂 W$gAHblGYﱁH+{1NXdk{@so zEt?ڵkO->g>!6N,ށw? s,(>˥05S (@ Nw^OerT뷈Rdg,,.pYtݘ^.|q"2H&-L 'ff;Gd@ܨBS&p. @pK ɣ tH4(~r^8> ^8!D %P@_Gs8x)+WT>ÿuڵgl9yEEhNN$<) X7AA5@X};VL QΔLIJƬCòu[g/.zKFe@:F7J-)#x>0$90qt>W^27aυA{'l.!'Ꝿ 䨝Y8fRY=WbCTU0 =*@*oƦV]ZpH3?VnA"+)1^ eQ4J8* |1c7?+,|S8xAʳt"?=nx[> $UnQZ'=m4#2-&j);%j)ioGƄMAQC>380ϫ-mK L٨9ql9vZ$} }˞TG} P.h6J[%`9f$$Z#"uGԔ?MJmIx[JmnGŕO~>Agn.=9yeqrzaE7\i\xq} >[%vҾ]'qw~'l~꾀GY5% 5?gmlيlD;2d(mΪ`t !Q4l'"=kVV"IL^e&'N)q( emwm"3 r9k<sLq"qN$pVC@z"Te | V…?h[ /J`>t$yA39z`+^C}cC_?;C5RbX5m2ݸç¿] Rz IDAT~_v_u@7y[8y<p|\ pT ՛/yZLxUFw-W[VZ=sRNuH1m8;(HuK0mZӜȿ[CмO]9bo3f6(K-]?oDມ0`p!0@uR$P-} U ߊ ,3IaI(UTF;N9S}m 7 #5;\X۵aN$cȂ=ǜ3.l^p}ӺJK.Qd?7)i jb;p!70HJ "J$am2;G^2jX .gۑ{kUw˗FpO-{Ok \=eK2̥JBרXڴkw2Qk -as"}HV G$8 g:`l&rJ% W10>&)2?,6%ԍK':("[da?n!OV`ukCIp\||x?)4O=5԰L-msy8on>)){nZ ٽxQZk5P/hh A'x_.Ǒ1Uoĸ%"S fk bhOrnc0,?*ٌ At rM1fģNؖZ?~k}SO^{m_wuj|bo&n(Z$ۭMcg2TB_r O U<6|f:&AX}oIςԥ˚1 MQ`F9k aNKUn95g+):%D&I^+j񿰐!=c #Vy!x,iTP;aZj08) 7p7|Vp.]˟<ͨѡLzᆴ"0؈ArNAsH t dTRKD@HMo p/܂Mq I8}[\c$@ɮ}"HM K'Q=G2$CfbRq.]jMXμfMAFf"iq[.#qDžk%۝;oaW}#_V)$+/V~){Ky-.0҇w\2zaN>xZ UzVJoK)W72HzRXt]sEk 2(Q #߽EeH5Ayn?p^B[e3ɇ'nikpGT.wvj38e:>ƻ>\znYpsڥ6-=>(= #@K5kCx(!GRpۘ(KyI`_'qS=Y*aJ 1އ(Ӑ^oJ@ߌA1B)9ҿ 5"I_h/l 3#;}t pW-9B.ɈN 67c+8M?*Krdp oUta.],q-6蝨iYږE)Vlܑ\0%]5SRX1"65/$@v(-g-*g&3 ۔tuGBvaWAFr|g5@8| )҆Ag[x.[o#9 ࣝ}Z7NܺFHrDf5a졮Yo[QStn`Ysf߆!ƢY)ժ#(/ 4.$ 90#$c Bi4pMv6 !&,.=@%C~1i]/-Sr0AhfIw̎1.v9otx_i|bw||s82MM縬q+;8D @kPQas>qOL]Jkӻ};Ci}w=.  [-xjjki d-i8x W2V$莤UMƌx:bf@?Nvhݗm9oCj'$]9=l%k}wK m:g%ḻxKr2!9skTu{t:m'MWE#esc$BbpQĥ ZEAqVKHCGB'6PsSΠcBs,= `N4x ]#ZSB^u6 2ۗ@OBd`~0C.洽 lr_Fʆ9mS8D^spI'3ϜbG t}W]+a^2q}*a^Z=W8f"mRJMJXY0z)B2h02rMb+b MBaW%(Xe [J~l&V `'BzUq9_"VڙfKQ6qޮP `YSRj%j;gj]-}kNsv@o+>Oռ2~!r֚[{0v^5g* :(`P, 3ΚyCX $ Ҷwlk9C;2Xi~8Pr08VGf⹀vi= tA@d^YK"~ByGOk:5;ӧ}1; gix˨a#yO4*gwF(9C;F =v1rzZ )gkޔIs%1kCYCPl5 (^G,0(UZ|Q9#ʀ `Y3`IהwXc)$ 8L `)Z,eH^h3`d(.pP-c^A0퀖&{1'w xbm+MSPwg"XT!Ixo7YK'Ht8$wR5Gڱz['} o ȄNe(.Gss!@N! } ̄PFj5g4aq~詣A nqg'gٹy#'k_,^)$h؏?(M(Zof:q_G#?A p ǽj@"Ђ\gg&@5^=*|F_!8, 3zLʔ@6B~>kOVN/|$&d8pГdETt;TJ]P(U+f@=O^bb h(*\Æ-2mÜ$Om=ѩ8ÉNM;3?5ثt,0D_&M;Ț"S\c/|a׮cl~lhzjoE\pNȧ1G2VcI~m&<AFrd(hĻ UIϑ+'#eJm^bh;Qg \`f> k.p4d΅-ThR+!~R؋l" [aǑc0.`E ;g{S/m؜Gwz_;}?㵱#py(ž7RqΥ;%O Tyɂ"JnC\F]V 0=r0s _ 0u$@ 23gl0j灞`Lsyw Hd dĄ]6g>T"ή%Zu`"C-ap@v~t5[V]%?,HVK,AE(3N5;G``mp@xraa^[Hk9g8 "YB Zk~MMH[zLjNtY;Κ`LllwFJGbZG:%j%;*e3y.8Rq0]oE*ׯv;+}/~d`]*:̽+<_nP>z6ci^d15Kk,fH.;b{ D@ |_|"8?6lr ~al$>Nn[}w|K֐v0FL?oFkGndy.1|XA,2;!XC:~ 7{KaE&@֕@DhJXI6أbC lg4^b5z}V0:?K5zo) &R oXa̗.EtǁO x5{">o?n؏i|0\%w %Z͖<ђvqZ&gPq%J֐'(h_Fik`'p8lh44vMBO(Xe[NaIg dNZt' 'ƚ kdQ *,*D8,GVJ;mJO>Mѱ7n^|qj|E_n6]RfWsL_1"d.rj,2Y4Vsz-i`yWRVƯY ¡Sd4`Sr ajg"J+ȹ px g-\4a]:@z1Gk#C۱sSvږAZz-|{zβ[)RUi\]3&y_ ؈ _2"KZI (Þ8I]OCȅ2A UeZ>cp(ڹ{R`tFEo1(_c5(RЭz=B69xmM$bRN{>tS skzy`djO}҈#&hT C *= d5-x )*Ue~4XL3r%0Pdv=BV-Bh(M)7wZN@X_yfU`npUJ /ߕXDu|sk)o眃(f`@l ʒ y%sAH,]*ݎ =; Of9K )Uj0D Igk$fΚ@}gRbExQcs k&B2pjPFI Ir Y2#Zjf vlrcHTett1ħ{):%zz*t @g}h_+|N|vFsv]􁕷IjL/x!S` z}>SyĆ6A`)~΅=vt!f ee) `guPa j =ÜNnDoj4T hh HuXARD-F4j( ׏m0B1c`Do]=ڟ^0#A b9YF 92M4N@oyjsE/3¬O+ͱ fA/1p2 #H4hTap'eLX?T9FiE԰sͩ\MXC_ YVtyBo.-=#?w 瞛E\Ev#FPjLK rD~k hXepI6ß חN1p8Xesop$¶@爗rxi#6GPhnk//Na\o29K/FDMk`JT/IsAjE dhw"(ς;vs|6{9Ν]]2j2[0.Srd)S'udkR(5}М9%Bj9TkKAUo1K^:b5g{MMeZe9򪥼{`cש^ , )E| 2Z׬ZrS!U ץT IDAT-uiEmm&{|[G;vusEuHWSdžjV4cND@mXp$d/A{eUSE*봜eƨ'j$>uV<Pj ieB.(˒nia?P )&ߙKC1@YEΡhsWiMLD,!dPD0][-߾:o;?kҶ40/2U\dI_ix 1~@aڗ58VF0Fxa+`%xkp0X՜'=[hs2J 'W5|Nq#Ȑgn q&=اQC1Sk(\K!Tp$-O  w]ܾv}N^HB5KrjSFT̬Zx<.XccڝכӆdIvsn{_X "1fsj $ @%gΧ49d2Bc01LN#ҿքƚ`>V@<#~VT)cw ];}u}e;cWeνpAmws=0F*K J1԰yظ[$@=N N&:|>3DFwMQ 6QsM„=dKcЀ0F"?^: \rn6dZ};Yi ,!h}%  @CoA!!A\7W Y/*[fkeuN Iu@2|A3j2&҉c\FApd~##`LV=4jܯR!ɳD.hײ&Fv9ʃAw(%ܹK:Z?˽}`^ڶ:ʲtKE|gb dXaF NC%㶕DC܃+81䞘МlhJ``IFRK#ReZNtxu bvLtqزıEXd 20t r ʌFJ5cL?J̝SM8[C}TTM_")rFN$dt 2Mi">2wXs`|XrV|){*DpAx#ݫI,m.YSѹչh”@E|n(v,g\ϝ^ 1qH1t=6Aq:JF?u>V`4F9 ! pzUT;@%naC^N*׽Bw^XQ'Q$_`Q, "R[SAYbA֕8yZ qsǮrx*77nn*R;tn7'@2S}* Bѽj(S&KE}U@qh5CDuc1B8Pt0Y]*8ؤ<}Nk0g2455DPFXZbn_9$ "KUr@4B3-Aؖ9#xz֝[qE,y 6 pv+4$WtןU9D 68C S= g,/=_=SP-W,s-/KB)e3}&ՒV`se.2 A_[MQnZFnC qeP-t5N\j#M).)e-}kJSjKt| ukZ^&[ [5pdK 40"r>,iY$A #G6QhDVw>?'gRXidR5uLi}劽CXd 'Wl-q9Ԝ_ r`R)w\ܾ n~V2"wuҗ] 0&ԇ;8Kʵh#kdF@cBcT1fx,74*9 gv@%XhNR\Sd.= ?ϡ6Zب;w&DQ6pA%&5[:BruY)rO F""b4 J} @#gzтds5ȧ5(qt4\taz9`>, HC",rj%|F?e1X֌dVޝAZ*vKWVKէ/x\xL34]\Naݕ;SS2ZEr c) CCO}Tީ,i-KdhQSH1mfJƖah"K\ԠGZzrFz2wˈMwLt,ZP?k"' HP jU_c-'^+w XwjJ0W|a {Ϋ"1>!GvZGDŮ, 0H9*5@\xgrL!; h^SK\t@d`9|gmc;tYgנUef%Ǧ#2<2oʭZ7j):SK5cZ "cC˚Gz"! eDGP289D!;^:dg1nԫּWwN8DR@(fgj%:g3tD @F?Ry.a קΦrnϑ!Z <_rK$o2fﻔP|~4ÁA%/ ?y[')mKwHFg9jTCS (sm %a 4Z?WGѿ\q tt$9S~x/s'M- sAqoK˄.dc.o!!v+],u1k"m@Vp Xm5xrhLcZsvxөv%Ĥ$)d`4ml gK?E yD i Z2T890Mhe?`LSPD?=໯][ey+|ʠ" 1B8c˖KtimKMpA]~ie{9$j/75| NoI`^E"eDld qV[&jߠH\~Ö,6@θκu~PghcZ[z*7Kh >O,I C!41ĹE'vlGo:56P*% UӆAjCmCv~OMm$.8.8I NlHb zm9ˌVKV ,b-|XtD\(\JgyD?[mg Ēs^0"VOXP,Η>AHP o,}.` CY%Fim 7i2 # 'Q2Xyr}Mي~@1$Z%J8ST!ŦB(A\J2wHT({9gb8BØtÖG_lTq#7SPb -MwܴM?d=Y7t@! ""]/&sd@g+R "FGҿss<L=]>; 9Kc/uZpLvFp5Y{XgrViz_Bv+OY?%:F(V]踫2ܹܒ (LdRiu6 7n@и?"-jFHMJ$n zm ʄPPNsb,q\_0Uϧ{6HҴuZ}Kk|.S M2[;U^^lD-u<ٗ9S}l Nf_B *k kA Nիc6k}(?Ƃ@G(?"B:ǐ\Q: C̷+Lp xi! <]"fMiłwv؛$bQ`RTk/32KB$'FʚhH1QCk])@daΖ TV j5 @Xյ-"X)o7A8s8?C\qQ4CbT@ B t#EVbc)R;&Vҳ5ۮ+yC]&i=@^̉J&2$8pv\H-}T=%êZ S;%&C&Dz ~'1:`L{ZiX ]`?"[GK#D,,hݸn25B$I!=YD+%R#%ж?Y⤕}EAst(MAlrsډ N,gN&6M#o:^GڶE6ldkID' x-q} 1/#DoI#pٹ }zՋdYecal 9o,WBMW-J'0q1KFrfRFjլE9T.2aTQ/aLKx V hKc-?Ga` 3Ƙ*4/oDT;_g?pBZ'Z (`MdzxC^[,û-{-b.eOH+?w!V^?Y IDATeC.Kfӳ@if[lPCx.k@B\K]'0ohgp}Gf$ĺ%*`,ԔC.hX$,@}_89QH PҜê9%3]rP (7`{#pa\ #x bos Třs* Dbp, 5 ΊP!""C cm`}cЏg`)斕X.%vťx?R4Bɀ Is E'&oSDBkғ2R$"2f)}2;94"veh8乫2mgǔv#vH\!# 08Ů_IS\1;$lwo|{y Hx3gS6fxVBǶaE>$)PHBpsڴ北M5sTK\=hSٜN ml'oi^)"59)Hc%SDb7[nS&8M'tu0gE+?he!щFg` K)ĮArXZ$ms]Jk @  ? }V]_ri8mutn-TPgy0d @ʊOOs kdfiF'%ЩwM;V9DO7&M4hв -ZZZ33OZ\89E ''hčnITeFI-q+"L + בHbdԺ,YSENZxz'w2\N5Pl*M٧&K%u0o=|iC?2@AڜshhG`X ,oݒ+hGI1^y^y"^}7qr7Np^tOM x!f\89% '-.\>9;Ԟ 1TCuɎgvgЀ@duR*B );[;DJ 6-1 Rb!,ԛ*)s|p!~!.-\R0cF,Β6 qn\hȵT9D @'\yo^K_o\ko.b8K& ñ?z|wp'#q`i$T]t"1N]1 t Ւ[; cV~VoZ9G|( f`8'}{\BŵtoI wΤ]@Ӆ9'W}Wc3iȟ9aucGXHB}KѝhSa,uI=~?D[w~ϼ|/v_yc?u_[t /:こx۸7#م̉rn:WZ:8ᰤDh <ˌԿ(:`8Z޴a+8n\?~nƃm8^# "X(h6(NCͭq˙:!%AA#5'tzO+ǵ|RN9WM0Kjt`{zn2pq ;H@ Tg l Ed}/yߛ.K/^[w3wd:VΖ3Fu\8h;xmA) V,أ%Si<5Jd d94+Й*.7D&J9GYKj%r|י vkWC&`֬HGHn ]2 )"F!KЊaCi!2?!1!Z{]O]xtf״4 ?rݚq9{p--|۸DmtrPJtkx"5P!7\9<DTP2t̬?b82J2%uw2K SpsfN BIJ&hGRw0ŠK*ⷜ] ͙B776eWc _9O݋{31ϡ 2tq<'/݃W+'oyRQ<@w_ Mjmv22n ++g $"c< xN>S:QKW) !$Hk8sՋ6lNn8Iǜp%~ yZإ4y%Ԟ֭5 _w?5kaY8H"H:D ^yQ\xr?{>IVlfi9^po6p[maDsk'ح8PƗ Ƈ^:ןaD .(Rȸa/ˋK }//--!‚3V `iKj~^sWp7Ug97s}]09s}C+&.qGWx7.]}wn=Ȅc㧇cjM5%d [75l$(#j{)T`!R }9/BJuYz8iMaCUQM+ÍjL )bx8M! ?9e .t}QmP?)忯|GqD3?{o/YQ?U}wafaaf( bDĸdш+11hM DFMj eaan}oݧNU:[w;lU|O; 7mr }E̫MZ$u"Q>kjK 6б'?]Oax# \&i/Lݴ&ߩj'6u 4hA 5 9QB[u >w2.H?x+[USn8I~㏗≝G? 7難n_~!+%}ű.GpZp> Rկy (́}[kjw"sÜA=P<"G:Scs4L/]C `'5d9jFcD \hu2͵?&",3vq]~uSǠ|Q| zCCCp: .?p)'y04~q' Z:PQe \淮y#B;UԔ-@ɉ@=)mLa1wZUYn][VAQ1">Zxb^@'̄(6_PYJ@ !],-"𓥸wkй_w?N n>Ycq,P pGF|9f @YPȄPry_igN)JK%c3N$ @AH(bȒ@@zd7M9P Y'L"u4ټZ;G1ç„ܷ[k ZtM;Q{tQ_8pC#`Jk~W?.+0da{ zP'qh";~C0`J}71'~ 8 y4l|/ı˗G7#}}121cκI2$s -`V~Z5QCQ`v_7ֲ( tGEz9@c@(:osߍ+mSSќ7S&Nr(kfL6b4 .M 85k?Q*J^O|έZdЅ7tv~+ w04RX"P ix\}088尻ZD6ot>xHC/ 85iB"4`Nc!*zKwWu}>-fZ,ÎQy$`^;M:K ,lކ/~Q(SBU77 #=rE߂)$)_Orj\kQpNXx#' (Km$2@k)poNZB܏#}S~䎨_H /~qiQrW]~9ps r)_OiazoN<}"j?cZu}9X`vK%PTkHr` yxN1`&`. ] Jf~3i!k3-9t*` %@+JZ&" \EΟẁ5r75!Q% - Ng+VG7$UOАy5 tS7;~3ry ھE(0q[0H\oI'o=Y,b_FbR'/N᳁`اH4 ZpĶ&E_w$n 3?Es&`!Y8pcЁ5%QhuЛq.LJ7 MpuC%ko{|߾kL~2yo|T?s:|¢ŋ{>ewp?6(mB-Npuf@R\70+p籷2'D*Fkq N֦ǡe : yhNǗ1m)8>:W p}I: Yq5Yw >@, p}C khb5=C׽׽J>5㕫V[,YmoK"@q? kBsf@ Ҩ 4rl<.f,BOi4rnuu5ɳ7 Hu_ZAQ%wtJ@iFʼnD-*5:`cF$9b#\uթR1вe/~U;el`soqݿ5iJ?cפKI$(?|r\%W`& 1.8{$QQ. M~ٸqpe_z<5xoqf h%&%|1hMDXe IDATd`ҥ앯h_;!8_YZ!>7ڏ)\3pЃ\vo`]g?"VnC!l;ʒsϳ.H$89-i=0bk{:eOg۶<8|r0c S+~_\]7\s +g+P (`|~69gI᷺Vki4BJIzV-Z,F W*R I2])u$Z/ Ǣ]gU{Cl`K6i',hJAS:;A2*77 :կ̧e<8M(m\ ͏MC˿%^XL>I|rw{~"Ȱ/x{މJX1ւۿo +Py ,_qf@BoxMʡ]b\LPok% 0i[%Pب64.cfQD\醮aW!`<1״|'fj ѵ^xNs4ן[ 2CRɩw_ m_oj(.rz??CSY@PZ+b:n/ @!%PKi5 0Zdw" k5B;ЊCl5ؼ$raZ-Q=Q |0x>HVq4ʍ[3ۤI-e,+?8q!N?]pqk?~p$>9s\.>7LZe&O\/܏Fov0 yCP7ʵg˖ѷJWGx쁰\C糎٣EG&!>"f[b$[I4\l?qna}z% 9Do,F" jN۶o ЇgԝE o;z*OzpU8c?jo#("s}l/be!a/9ߴ aw4*: o% 7=,vFc sq u>&D¬]` /(h,Z`N?ۈ!r<o6@%ŷ~"W\_PRɌw:Ll?d?]z2Dx֔p07B ,ϤQJ+M , %-x hT_Pƍ86.?  i4P:(5T.= ?J%ێIC?@Lb?'0ǃ̀1xy\>]WRĔ??GO :B>aQ,_(ګ^ j)[Ѓ-!S9r9 G9 v Vr6`!(e];wbǎC)ch|<m]5s t>I~1aN9Fj|ž |CklxAnVEmŦ В V /tZ\oG>L?9?paʕ=bRԱqbJ\qeAsir/"|Co~شq0}v䦛} ;Iymw4 _vw=x}R@ hΎx'fuDZ {W?뼪s #Ňt)0k4w7 5#D7!$t 7#3ӱ߽gS3r~ɥׯ"VV}ffr+|l߾m xbp≸eIA{:|tײSp7>Q=l|O#F ;nMK^%K1@'tF755ٽ* {]}S>gfPcl筁lB*\pjDR. ¯ u U ]H˶|ΦGѐ1o&A8iz>Ti``|.&{G7U_-{IfiXH:wc )$%Mg4.e?Cx\#a) a`@Cv57ۿLs(~~ͯC5֭[.<;qч C[l#r_=S$}-`@H1чn [+߻@C`I LSC/ZcSl0 kA$2TJ`\Asz?4Y(¬M JK*M @t`r磫PĬ㦇1^>+WtU*Ɲ."|c;? >yC3_~?J_f fLv ބ-Olp$ `Xdon:e;uB-G3qy2鋶5)E.~ɼ{A`)W5 ićFETz@`<^ KK4? w+F Ӷ}E2k|^R{s~vO;oy+vޝ9ﻩ^Wpi˩tA= 7~VTO}Y-_ c%q{K\Dc+(ŁAqQKİ II.M}vG I$I?zQw5X:uiHzJQ,'8Gw14U?x=ݻvu# ~O)` rbAE :}LEЖj~X܊}}XaoG-_=ҥ#(]WgjU"PBtRs0- l.ΗZĴ2t[5bH7\ Khāka=&3M.I+Zp8 `AY^R \8$#x]H_اkfџ/ٽ($PDR\a_s#]DX&0~ʥEpQń"GpLR YCd M hUaFϛ$x]JG;Ad]6k„u>d@1'ZCD"X+?˶k!$Rwcffa3'R_/h~>ZA S9Ѳlxo$&HV1vdb2Eˇm]u0^Ь6E\j Eu&U7j %T,BV&wtkKi${,/ɹ>@huRځ`.|CTe1Og*38g[+ˑ>O`֭ #?޷/q)TtBSD@`˓[  žX(.Qliq9@o0HCvR ? 3'l(GپuRuQ];NiE\M`k mzGdt t|JHnd\˙CԧUh"0DysNjY/5k&8Q#f 4t>`@,nńkQ\:[E%x֡FgI|},ǡ25߰] F\@:S TohPDд!&gy2C m߾w̤VH#p EW4r'}u3۶9 ~vT04$z.ZbDNL+uDqؗ?7QƾhV\sv=Q`*S,"i{a( Q6uf2AXlӇ;J'>)ӱU("9|տ8EKi"Lm7j!?X'q!/+9u4gy&3z{ы_7甕G{JX|>/~]e0/;݀ѡGuGpڲg)|ddqλiY)?"\D4@XuJ͚[o&OU?4@K@-`~ ؿ_4Bi-/Y@fy3 =jWT@Us}wQJj. #5whJZw`PH:\_\$,1*gS,eSO ]z饙\.>KVnoYGr>Խu}qI@ mжMNNB~ ֞dL]r*7Ӂ9WGxi9^\FWNV"7 xa`;6*'oRC&!oM\˦/F1sD_ou;s=sORoM#)zJ0gYi!+5P0Ne;9Y%(N@šfe9"Lyj%D(Ot|`6cۂv;̔T*Eζg\L:BsSW@N(fDf K#4ou^7Kj_ *S ;4dIJg9ׇ,)GHo=3S s,]:pH-e_]wU7nLL.d 02:WQS>xJ_u>j4f@u4sqA@\ᠨߎ@w\+YH"FP,凣dD ½W nu-x}1 {޹bSm`өÑO !YV~e]P,Hd ,(z+"5`\^6feqe6JFD+0Ǝu1>GCޣom2eF`'- ?nf 3?33qs֓TP 5R_j pu2pC x Wr) ]aҘI|Y.7_ 8tėَJcs uS(<[Z ٽoaDZ "'xB`ߌ1]`~Y!6w`'tEvXx;M*ؖ{ż&Ȟ:~ϖS 6d@u9a! g=w<,ZvmOfdY2|95ZGP(`d%:Q\LsL_ܠi]!yu\.՛u"8* 4߉i`v >,Ttr6=v ,/|aG*;Ⱥ2ofbSN9YZ&6;$s=t(w쨁2lQ|gI\d=#'YN@} PEw^Q $j$ߤp1. =%x0*mN{ox{PY/xA$VEs3s%b`'~nTEۀtZ < $ȀD NWjk't]~?LN>i"g>*fyI?O4"G$'Cx-ОA!P40>T_(yձ'rVS, *Ue\|EZmq|9.ZPq@JpVG"(i$Oqu%(ߵhFN/ ̹%$0u#"d5Rρ@K$z4"UŚC}u0g_,&`yի_ݑQ tҘYnx""ᷪ?i*Mpyb`(tKe8묳ҳN@ ^[E'Vأ(Β8*p68"*<m9wF` k(+X62hϠ`MU,=RpʒX]b.䒮ukÁ/eD 1y\xх*`sH54=3,EJ˔F"&@!ھ,X1Wh`88$Tu W(/Xn!$Ǹea$$ektt:#35U^}ɥ3@><R;>9s9qZC:!.1GFFqKM׸eW'=£<,|I]j V6)B,}<8P}O2" jfEfEZnjy pKVea}%S343m2i5ke_ܱO{p_cqb|:G_U'lp;b.}^ M qxR|uDz7؝lm 3߈SqO nCg] @ e:}3&z5iɸc IDAT4$:r`!E:rW\y%Vp`x?d*[ry~=Y~LK?VIܙ_qV_? +TrX޴ JJok*3=dLJ奎NSD,=_ č [S a{OKLD'ϕCeSy2is}3_>I4&'&":  fA?m?&CX\%ҹN"DZL|-i Z@p\aPD ݋EϮM⬷4fb [F2('gZ&|PXXS$r@^"}4q5/=

%hpΚ*( q;o<ŋŋ,Lg9&wDDp괵]0Nqˠ=.DrrYI\ #N*n:Uȟz͙(\T 7?ar4FS6y=ԕ.K,vfX`{*>`||sHis"ۊKfh"hVºߦ[0:Ji=p|Ǎ?a81^~eK? * }> (b @#hjp"p  a@sɁ1C^AZEKKnِMFmCtр$UYZkQ~mݳVh]3Mw.!D.H"8aiEզz_yQJj. s9ʥ̞c.Z?[o׾Uz- wMoyU`%?__>`V}{0:2ܔn,fyoB .oYz$$`  vbZ(e?kjhk |ה- -z`NS=[6NdFل1nNl*| qD,L[aRGáCp~|۷mǎ8V\V'իꤓKQ>v [?S]pv@FDD(j?W6@D ]mkjWQ$kع{.QZi*8_S's2$:(OY]0h ;q@@̂xMtnLoCCC㪫_m߶)×/RTSAُmk?i&%Qk$ vB oØ<:m-]9`>X8Tġ33dx4q\/r@rl{-p,] oSd&AvVE6?15A}`+87LJ@қLTc8(ú5LL] u /Ygx`}x~3oGǕ)b/{Dwn3 J?̝wذe Bp/: @G<%7^֎'yn/j᣶Kѳ.gIzUG0A4RFFS.CT\\c8Kwov\@۸11q2}Zm62Dͥvܱ36 M~:TUO]$l⬳3mֻY$Q sl&$i|D nݝLcE>M=%5"S v=Dw>IV>ii oR±}۶Vt[Ήҿ.rl߾-rz6\g7LWqTS.${ 2 !lZSD!A7m3Bu\3Bky1"(يaHE^ݽ{7l/G }۶g[K*-t7B̖U9tp3PF!sR!򐀄BN襞/:=U)4IX=J @#>Lϱ4 4H,(\Dd-ĩ=ϖ A6CE&$g1Rcx[n֧2 <ϛ{H2*V߮ k AQ!|P0(FFG088Q&`ayK T &-19~kIBXŶhd:&6[4[欑N=HcDwe566m1x/rsE¹옹aϟ^KT07(AP.}pM zwyO6{8v܉];wb]صk'{s.LK_kdE#fUBja}<捌`td#(l jFGq BnZX*H~ "8 IS De3:@[G*m 9IfI%l8DOoqB%΅Q nw/;90ưwl:];w w5ީ&/o#r[4 g2_  * b?e س{w .i~`0:rs<:tH38!Gik$Eb@4D#5+J3b\z@xwK Clxјj,SXT:4Ѓn\&(1|h`Crqԯlg&1inm󐱱1Bt g/+@KHJT叠f8йgU3[hUO",$?k}]'? N~E0}r-ԟ\ာ 5)Kǥ[f&k791}nw}||rQqByXcqbֺDptNo"#6p"s"GNˮd~6&&A#082IpdbltP8x{ц#.cҥ8l2\.cզMriGuY3'ꗐe3 9pMpRI N$aQ[w*H(mֵm H 1#8MMK_ϕu )2MW<1` Oi͛65k2 }s=_޻-[R'xXrPEu,_ᘅ,>Q:l/%Y}Fu@y7MC^)v} r{`>|w~{8SbJ \s}ˆm\syAo'T[`D B6,t]CG(Q덦1K myå}%gQ:uD$%Aa  - p )WOc8ǶgswwJu8X1v \5ѡWT5G(Qz:_jq) Z OLZ) U[was}bρ"z?~F`zک vA`޽{N1KhlE pqn v{sbX:-_8,kE4L;@?0( ~tS Y -f9Ao\s^>q-`zzb1ֳ=ַq-uXVo9f2l6_uH)`ƒ5¯o wU%hI3 zF`Â!Nh:ls0'!ӫ,ؾ}[_Rk`ǢR>{zR? Y`QG?"nY0J1 ѩN{-t{@_q{Fl%0I1.9*1jq6 p-e_|݋;n_WgSSXOcbYyM4sW'' "ys(A~+$kic9`h)scfh#0YK~qu睸;A)IWcժU72<\ P2$ rm t>(fj'M8(6RbժxIOu(h#^Wz9`/n+K4'ׇVgq%5lق}wCn58Y93W5 ^6'r6gL (⼉)\nedR?:/8/|aYMܗ3;xfW1{ ?֠ yϳ6 Io9f":ULtky!Cpvnq4.g] t.D4AWWȧd$'6 Շ Z4Ob4i{;pѢg&&&pƍ7{;X;~IlSXj}Q(IX\N[`(6ǙK_jc:V.cgHJ1dZ-ZhnY|jcS@koww AAHXe؅^vV ]7)̱hl|D! ;*O:lB 3Gy zpo׾ػgo{qYS`} ',cmj񟟡;/S u]]/,hn6 t:f'Y-{8xPtdEK<C8P]i8nL޹9'J q8U!1 !%w9f"(D':Zu28a @%Rup"-:'#iiiD!Nj@i~8~<h wNU)s Ϛg0<۝P׏f| E@/V۸!..T-8 9e -@pp,uG!kh) GԸ`2d@D0U& BCyޓ0MlΦe3&Z6ҬȤ` t#`T8I@*8 9gt?43Y:p^x4 wܬ٪EJ_ַ:K@ ^/nu8h p WXwRWOaj⹽y<<c2_q:DI 6 {lut0,!i]QM>^NX?W5ω~;99({Zn+55%s!:i;em=iWK}DT?&k-v1צB|WD#5pӔ`6|̀J57aE"y-gqK3 e&v] ؾq?4m+KtˏtPŅo*(2:8Tp*R(3Q3'W s3GpSU8'aI%W%Q0 b_'0g rln)u*?t!P0B)G광J 1j:a6vYߡgܜŋ5>iK WE8YI&VWRE2H ~ŽF0Uu YHo :V9q9*FZ ^}>W?>Io|b/9MȪ@E?[m}jm"Ht)+_-Ya7)dij?8C-o8nSpnO mu:EN],v'!!<9z6&ž8ek Ui@Ҽ(tE,Nk5FjtH@c03J"BxX~l15qS۬ lQS? өNziO2 1Wh.v&1;H{_*|oN?QIᙔq٩+o,>w9+t)X|gՇgvΞ2(B).\SN;'|v.c 2".*3ݲnb3YH00^9K$i#E$ 2DPWܙCV'o۔v_ C([EԒ̫qo_('۟ ُ\ 7?vYGm-9h7U TCI_=Wģ[36Je̟?y#FRA܏BJs&?!41Q#>ftyKv逢 Qt8<fT%O-(:sv)v<*{4UisF3;GaRJAӎT~Ty_ \SYW+r,6d/= nV=t|0RQH^~p9 ^gFY ?5Lo۞B) cdtPVߏ>JY;iyr3&'&~!`/xL&B("C K6-iiqQV?^0$G#lL}_We@I*o@j_ XK$ǹykDžEdSa/?~/FqC<;p[j@ 7<'a@)L9I`H)*y#008jR b9\m#B066@q)&N[A۫KFn%dqll/8ްe96SͤrCԌِC*2H5ע{J=.,(1]X ݁3}[e*O>%Z" TϸI|m"8xMp'rNn^caҿڨRup:Qrrneb|0a<ÇN]4 e Q& M8cT]Ԁ!+geڬ 5.ikc1&#tW\KQS!7ÌG3Uyw|z_=9PEGJM5~B/"*J>i1Т"X bJάA(1YAX1 ՝& ?ۯsyx=X@7}IK דbjƝѹYwDžޢSrѣ+'̹oZ7`f̃hXis \aW`/N^68]񮿝=JpV@%u9~PCB"t'sx<2?ӱHo"5v3>"?AR?f/{ىr.g#D j(mK ֙Ck LLk[*$#}UaC]Yma褢?}X<=7Sӽp? 4Dw#䑡`"~rw<U83/}X\38Xj . D6`w)9n=A4"J&gv Pq& ꄂ1F,<.S%8 ch3zhvxG&saÑ\krxL7s?Г̓tı&n_P&KnvsS(YBNlǟ.:c6d8&IpQL[@D} /,./gk W|/>,|l>䁺EQ ۲0k;j+}82~RUg*"9}ǧr82o9Z=Lk S=9tIz..p.djf ˥h~".oG=);*cx-Z}vvkgOs=?w 5y΃5;lu^vg>EtEqxG>f2e/cD'x SLw8:Lqdߡ<O1ufho>H9<ђEMQ0?Qݤp04=2oݣ2Ƨ⍿yFpt-ػwQu} rIHNXm|{^)# 9ιǸ>۹%8LLY VE0Uˣ6GD=<Ƨ1]~_*|}A`=s^ ނ܉,€^/13\#"z#ie'jtrl>z:/k`>"- TO?k89|qC7OLIOYr@$ЄpmaG-godϣ1cQcyԼYmC}C+f:~|'5.Q!}rDxDe Cϯ랻! ]$Ҿ}.{eݹN)xE;%b1Z;k?/@ϕMN9PnXCrx+wE F/`|l(m}J%(RMªfxt ?-c+Kp%+WA;OiWv_Xnn%;.;vqBik5QOSKEi&vliΕ[{۸~h>>! N8ڎCSc&7`2׵U@+ T*TVPmn:j@culDZ=,{q>Cc~i?>8t߲$!Y?d]8~` $@j&]z 84`ym{QI\wsg:zL_\hΕA @"E@41q;39PE/~=rbuc@1}xN#L8]uc8O\v6Hn- :^ $b5F3$Q3Kv,ޜ't) #5@q&! &yq쨘N_eP&,^g9[h@'O#13AqD: g8EȎuՄQ4#M->f_xꩧ02:c.lk֮?9oDFo.f&s&Q,0e-B7-عl}IH@s=K-Y_rJUSZ"8q@uDU',L7gK23dɮĺ+NV̕%XH#M#9ɃwTh3Zr:*ܹd3t353$Y")Q%KVǖx]aS ;0Ge @OjɈ Eqtg1ϕZl_?3WP777rd;crr6mV"@ytrT?ݏ]B(ԏo4`qv,4xU׫~OC"wb&k_m4  4~m_m3_iKxLUh>O_鍂&>&\pž.9E{:h v{oQ/Nk۝EI_her[j am8yɳ'gysnۊn=Xd4YJ_XTt` s |e,0)3%`$u1 "sHMfX hVvOUqKc,pםw+@N333~OO?<g) gjxo;vnqѹ{J IDATs3޲5_ٯJ.7qjYYsUsn@Lk7qsZ-ZJT.iw]Q `0Ub 6T7vԤ̂<޻dfhXu   @*\̀q,&c`dFW-KSއ/u7>Vv@8~y;5`vcaIh5ZMBAh4Us_`XvCaXZa8y_*{ǵ]ZrrcVOf>u@OAAv+R3 9)}vM4?-iH5B//z8!ղQ4֝)lִcpI`mطcxxzAG wu'2=`PPIF!q<(c!cg8\7` \8wn8:5ЬZMBIh< / DBc)4tUZ{\V z/osJAdi0,_\MY [Il+ы3SXui k}M,JoOٛΟr o1|Pk{`qqO}s/tF lù4 1q7P a~Yaxry_X 9\xhZCpffU!RPh ~*Q)U<.}JH{eUHv_ >E b p5 UM2;&Í;-x:{/~'rfP-cA;]wǏ?~3̧?ǾūA`s3!hsN#U eڸۍМBWߏK.=u4SR)xA izQw?u1̀r/ٖi&M%W.pe3PeO`!Rh.$RPPJZ͢@7M9`iIO} >O/|fOxT|Rmy</FVOW9_q333CPs] 4E)JRĀv;/u?\G A_ 522{2qNM_^a/cY5?u'h{sDʳLyǢjʧ:<}\}5޿6@_>􋿀^z~}_g`z;7x< I0ky0oZطovލi%lAh1ЯnSG(4c` ʁѸ|`ޓ~@cX^p0@]#*e$֯iVB'EA*}޻ah~75{ދw6#1pQ^ЛO Yyk %*4$.ڿvdi/_sh4h4m4׋S*@S@@Epx>Gc130Y)d`3Hf9y+1 ք)0~D.M`çsB@3g=$PNf u+q?XX8o?>${ })8~$,>3] Կ2dd;yӦMq}عc;A40zZz>!-jo*tTw<0tj ⡹As\b:0Y>-[.WU.rUK dŀfV" c5 g8՜ŠS޷6x_~k BHY0Any-W1$:XXs =_x/zGȋ7Կy-9o߆m۷cӦMD Z jkdA.!c?4EX~uԪßZKm-`jn)w(gDx+RmKzբIcz4eEMŪhaAT0f'&q5yAxqM7miC.AK sN`~e]K/Du˘=ӧٓ;`~ fOc~~^g߆rTVMBƬll9Ė͛i&LIՀ [s]>[m^|r"m~VU!tjR3o4pm l_ֳspH]i/(J"[la!0K}q=<6L \rk8:1Sq+{ @[CA3@h L ^Q @$B-%30331A D2:%tetV!0{~ pp9t6ÄP/˭pZZ_ a@In}tcA`d&>) 8ښ3rvLj:0Y/%J8@hȶ^HIL؂>/y mC",82>7z^~pzi"̈́BEtV ~ 0 dUw0O5{Q^`"$QG? صk.87-fΪ!d8 lߥ iU A\!3`c E<54@0S20D5w̧?v俤I^}0Hx"xD1 0KFBl:mL ;w֓5 uZ&fcxfv,!EI\}n/@8d @/d@/O(biZ8޾K,v_???Z޿%dy @Lu ݸ ?q1֍+˵+{cxxvCso5?fK`Ba)׮%~abC!&0 r?X<" _2^zہFސ@^>;ٺ2 RFW #W`4 *:'/&6I1777%d\/u _L@(l$:@e(TlߠW0ȑ#xGt27DʷP 22K:LMȼM퉇! \h[^0kk"Ut¡ɡ;O'.DumV/OPr'"DOqֿjPWFB ^ NRKjR@kBb>[A+īlOտ&p5Xr{KHNӄI#KEApJ5V.txKHm#<>1c Ͽn"h)CVPd D$! qe;`9ri utCtA 3OL4s+:ֹ,%. HV&/m"_BkYxnrbD^>th j¸Z߯z H$ SoF`a / O,/jaQat#DS2O9mߓqbqmCEAl -~NCWS(`(?UXXEhxph?~ϽtX{Y'(}e4j@%/B?u s!B=db!E"@)||⟿C^g$ݯMS#[ r $Yr6~d`¦h+Y榲4 _o 6kns#$KB(y9j266>r_ w%UԼ}|qpɑ"ٯw9@*}/R\a(ek"+؂4̦M8pŁ5Gx>)f?i޿HY(#"b>qjѾ)Joܢ3.:,/={C v4' lcPN|Id(<~7uV`eqh_!fZƒ_sA#4jz|-J*j|zžU owRā@"/C Jwcد:Gsn ngLٟ=oOuTCd ݯ._PfrE(YākfO),|'3y0ۆcV_`M&\Pp(ULxv֑&ƛXBŚcc( jZʧZO#k$HjՕ.1=GwN^vyFX&!d|6gJׂ~Y²(z=FrZiby?C)n333#߃{x ϾR'#ppujtkc!.S!j쟷{?w'rh6L.\V4eScbWg_uҌh!憛ba> `e9>Moy3.?0Oÿq<p a=Jg?#owxKp&~y\qFu\+.CI- o˦)xe)xuzw:nR}Av]]m[GTof,f^u xoceS-"*z5 Eˠy ^ {ݫE Z'֕ehrU=[8 eVRTekV >0mjmiiu]ܿwk,ߕpAx>߹L*:pOk~+*lJbː)>]lƛoI,GD@e(TdEV#۰)^(U<d( ^wV0?HF**.>{2}{'daOHqV @1"Pa+Lˬ_ǀ yc KGEDck@u_u=&&'?i)^~/9 eQ4EW8N^bk=,bNe;\87;4n^5]/"y:2" q[_SG7ea?SY~=bJ,u]ƛnJTU+$l-clZXE+/Q\hp,G0]"4`!;=-C x v0}O@j5@c=]n+db#AeT?I'um`Rjݽ gyQ!< 7z1 g⯹1{f e¡ pS'S8^Xi3 _5 IDATN8ԟ؀{/|lh#Q * R$I4V{zwPx:cEKQ "r `2<2!sLBT$K@_$ECiTph?z[qǘFa6b"I.v?qG'|"=D7qAdlOHeEƦj2[ˀL+pZ"C R28p0-Ddžͣ3_4Q9_&{t"\kBSd%84JP= J,(\i+")]΀aOQ@Im ;XS #lv=Nnτ5wTLd 2Nv W6aHS{U@HRm#_LS<^",ߛf?le(OrEÚ:^X,ll:QC!`Tb<"ي&0Fxoæq<{PM@e|8|9✝[!D?Co<K+` \n/ved0 D;VWJMCl[9÷ne^{ر}sa%z-/Rsx;n3.xEVr߼S/ݱY̢AY6qY-$h4^zR*o6djqWLD´~kN $hHi|8rÀ>(jbQIA{ܼ8S310//PwΎp]Bpp}|@|<y!hǛXZW zH!0&0=9>^9z*y 6#1`o'I]۰dg003kONaϮmzH`sШע _Om:ᾏyv8FtPfjz}:evM7 RB4-fqOfzJp찁j'PLUV7g@vXAK+󘌬c۲G`eS9L.^{(~ |qqt8pL5YL7ҽpP XJ3?s\w'~n9yy>t=y6Sg 0 <TNQbj3\g|nqP3yjP}"7m^(ht>.,{< > [7zB6S>AOxr?ŵ2qi =#|ۏ@H8Ԓ?5/F{O3u&秞y¡Å:O?2p;E䩳h;>Ba'~1</X\?8R~ ݸt: eې!,_5օx;!S^{fgg1,/u]{~Ў7Rc"^&1$|}߇Dh $k^5) C` QrUV^[}/~׷J JcXT VPΐ>kЄ`&ϺJ& qm@WԱ]@Z`p( (VdZN1ԗm9QMit?v.8\NRN]Ϻu! 0b|~RcL ϒ޶XY:CULj!;_1x ( 8^ bU}Ι}8RZ⧗I7wA _)|2LQDPJl ʍ0[B,Bo} ԖTH ss3MQp, txe],чԳ?t!TEAQןEemr67<[T~-h:d=Wt^ϺkU?0Y'QUF Ҭ{, XJ~!l!|YpA=ʏ^iR+/D_s  ?뉋U؆ٳZ1S2O̊dGK+ e[_?)!Oߠ6d w_jnCΑ޼KH?~TNe@ƶM|+~jO`-Qe(j0M>.WgN`zan&?Ott]—> ?~IqRH Ć,c(+rE3{ KàY鿄1V5>lz-="a`[gObj! Ԥa?%`Dر}Fa2zzDcA`Gw_ zZx .T2419SDuLPNv|b1.(4>w^DyL{i"n8wf}&CB(sjU& J=deq>%N[%E*FX|%mCEZ!x7C8Mt@w>eΞNM`*_8p~GqhvH$HC/|- ߎO_gP w\%{pɅowXYY w 0Np!!s8 ^qLݷހ'~>g uk7\p.\{>9~ʺk\r>ncX\3\/&B}7<VutШ{n>\%n2W]څ2BW_o}It:vJn5Uj(hMT`ߺg§!R{o[A^!K/|>s/<{b|;5.|S ixZqC={ߪSiҿ~C>ֿ>(8Bx`QЁ{{D1$: xv<0AIUF 탣 $=JgFX'2ig.c q88s?z>^a|g1G I c9~3;p^E+ 5<0(N 8e")ǣ/Ԉ*;Ux) }oMo٣\+&VjM,-x,:?~Zn|߼!oK= Lӑw59[_}{+Gp@uD$/h>co?w݃Ng8N?2@_;}=_HvԤ>_sفe]IJ[f;nX/:/59ǥÏ> ϖڛĥfD_#xg{ 4 vո8~4>obvn=;7 yYбl.n\wnP̼P̈%0v? L)#ҚHGb[yP4}x600J`e򂔋@vB_>2hD'²aG0%zX͜@_aq>0;FM&KޓR?>;wE<~@?87|wܸ|gp/ߏٹ L(J 'x^pͥXXXqwtpO`xCxBԳppObɡ>4bo=VHQo7|ۋ|' 뭴;wǞJ\z%\{`?vڊx o>jGeΚ c F+Z' r%gNdb %kP(:d7gB@yʸlۅ؜& wjC?JwifH) eZ2UqfBVP崰\nSgsKKKKa_$"k@+:v_&ҿ]8Aa I{nƯI'StZiw")S;N++r.8Ф y盜L8A0$3dV'J{X7 (w ۋn3E.y'гnRu?A7ASk5!DUTRA%VO`\OUyP/fMF4'J [Ԕ9&9 D(2 Q1JLF'o/VxԶ!r2L/yNjC6=ѧ}%ol|pKkT@^;D8x_p(;ei&_ PG?RX*qe(!2  _S(oL$df Id;܉ LfB /v_͢ D4[Tůױ;똇xGNn\  fln9IR>& !ejyQHWHϵol\ `M d*g~zz:Uhsl~4 zͩ*3>iGcD1s?~5q֪OSQPשK o@c I LpՇ^es'TPe yw}&;@9Adwd\k2@`}]6KX6I}M8hU^{"F:4BF&U-3n"7qZFMŁ(@].[)ҩ _ 1#S8J?)PuU@iY Kl>kyfTz+8A_F6e"yy[ lG$"֠ *)d QplB/?oj6בczpKQ@h5kI~3j% 4uW^4bp\vW+!'Sx )hhSh߃}nOX-yMOSqBuUqn[i߼Ae` EI۪Yv( mPeъ79#Ywd$I~>fz>@@ U̵l7l(a]P@055eADґy,d9{V_ ;11d[U;uYZlDXk+`K^ȼ` T?ÜҧzC@$>#_HBZd6%1mݿ1xq5MMM'U 'JUPㆅN6m^uiOW+=,S,Fϴ 6yI&@ms~'%? _zB'F`/"Jc$r `m ` ̎c Ӊn<T=`z/= 9$PPBc+pHJx&`A%q Ż# Rh5{|'&BZ_nK =~Wk?ߘ֊)W%bɋNaJ#`jj:tXY _m(F[Q$s,zP0 KVh{P#g]ϟᤚe΁J ih?E3$9#e\$c?RWW_ /)~$IR'?H[w`0lC]{Ȏz# y!﩮`)26cY/kUV0ubrUn]gpQЫ"fs,, "9irÚW{U@jy%O52d<>\ /%VA?ƶBD@q1>> #)_:@П4all" ԢcyԒѶr٤<"lk!rf-b9.[!@? C?.(}RE_FT=R`^+I<6rXֹ9~@X& xy D^e *.+m[L @?kq}~AHDJFTWW‡sNHm( 0cAUt*hûB|dϕe ʌqÚ Xy9 _c$e{I6i^P,Gi4-Lv?,Id#$?%@ T =YedH,B' SS( PY چ#zE[s %So@)j"mVV@/!D?c?YT%`z5@k R@(ZEmEDŽ*du*_-jUIuEa=&/ }!v9f=B6x_RZDi0VF*,bߘ2x֪33 }Z Y],d!abN>HUd\ȉ9tW/a!*=s&VPc?k~W}i@R$/ 6,[+"r9+[l ڱ*>d~Q>fρթe ,f\OSՖ(?3EGGnd$A .Pj2 yʶm؊ AgB\\^ɩ"@ YJ/yC& x @^"D!0@&;je_&j d,|߇}BDwIkDԾ)ޣ); vq0 rpaE1PQ&;њ_AE@&z_XSv7JmĂw%ƬY޲MU=򔲔D 6\ ٵ*/kѕse2@ LOj|! E9x u/E`n+NABpuaaaa~4]} ']{ nBCp. 3$ʥ93 u"ՋZ*a[ZT_YDٮ&)kYB@14[y>s̒ GkjR@$EϟY/aa~ s;Y,.aqaKXZV汲'  `@'|WCHlt9/ܐtWә4O.KDyZgisly7f"ĐCYACs8GOq@\|= 4cEKc)ǐ6oc4Qp^Jc3b0j'd 5n4@Zh?xgC`(/9Q``є3mLI"(3 чI3/p1@ WSz2{^y:ld+{2' r9tW PE[bn (urZI05A'8T0R.hQ ͚@!/|=&yH,,stOT0F_ pw3  5:G?eCHMe݄8~~}5>g?68!P3eeA֦41-O0D@[9RV%n. h?^r<~eeV/g Թ@hUhzޭ@h5cusCq^4:Z6>nx]T}Pz<#'=6i{3= y`%(2؍ki(=Q``<{4ÏX7FU4][ELTυ))޿j4cNNOfD %o ;ږڙEPR[E;*#S:}2AQ+/n$,,nݢUTCw\p@ ԙ:h8MG fM`&Ш\ыb;pFrj}iPػxye7NZStp~J/j5B MP_mϟb9YKN=|Ya'Vb膓Q O_!l&"NG xQ+T-2yl)[`lcl,S:I»R%iZcv췋7rR)c p} q9P @ ˙K3ʣLF4Q(Ϻ,)YRѨ S=3lI*qMzHSB'Qe O^BIbVw:IZ<}]Fc~`q ,K)fџ@[L%5P X捗(9.{-L|5XX4EVXnFѶ'[c!)-E<Uւj` ~\v؇6lME.&5&7:JmB`(a$+sQ`F,Td '~v .2 prkt }߼O=߄k]~?Ls?7 3Zq2b  7ڃ^@iROTd!DFC+r\9YU0 Л*9truM8!RڲGi"TWq.zԑh QLDbT%6$g~?&x~;;b;,Q\{˟(Q%w1(nSB`( Ͻ'`'9 ]r=s;`B v妨[Iy4*'bw'/|&-SPeQ "%υ3K0*&ls1_ ?uh(ffAdUO{eӻPJ!ƫ7,%J?tЂd׮  pFS.4Ҙ~,%1n$:ތceU&T*qmr[@3()W."6eISbP,Sezǟ?^+Xh)\?(я,ŵ~-ʧw7x}wn$zW/F#/ h)O!^<T:EY,rw<*WVqTdFGRE,;9D^s]2LEd–%Gj 4 jꕎG@h~姮 cwn{I! IDATQbVX8Vjz}g2؎}N ?U.o]']OAAYI2"w%c9C;T5tJ:dJ]eR6QnkQɧr@݅YrN];4!jN1Ul}LeV'\!] k3~Ͼ/<Ȯ6DO 4?jdJ֘eC#(͎C$u~;Ktm;Clu29R0KgdHk? 1(Uii.j&5%oek Lc"p"E-3&(iVulG/rH^V9빺H}}l=an)?%]oY1+b CHټOQxC[)%w~Wե0L*-O߮F1+ ħ!v#ed=c MA19uQFQCb2dyA顢'^e\E1jBX8Z6: ?~0Lxުpw;Wn`ߒt\FYW-V^paU{B;_]iN+A$A2z ()+',8M'U.T>+NTG7`#ymEJji <P'd0) $'QΓ' -,"2ttM_\{,?oeIut:ʁ`Ql^i Qo1)H R@kLPRa04vŎgW,T[*W/bA$F)9F溣F&k X R6EeaMtJ~~M;!7Z#[JJUReDwW)[>@ @9q? qOFLF#z0?x'fYpCi.+ n &zBT@8v߷Gofvk{R eO'V؞v;D(,c@0ָ"je7M[EL]b4:q,24w t%ebw|qnx$OH/|ߝc`zve$d3#}m#Aao܈ynQd7nܨL{t ?a^Bw_b[C-l <󞭾/pb/cuM3NUzJԚI@K+ LBWKKXoޘ Ds RA-nrL$Ky{y#/+ID@7=|}z| x8Ϋ'[a[8~]+VW$~|| 1qJaaa VL#@JN;;;I-zpHgOK N9D澳<| fK7qf3$`a+i^ap7*VZ6YC12hf8<\ʿ. K:y 9 8 uzB*SòmҠps 3eH?xA7Š#u[] {kɑvb*51{ OqD W{mo"\Ś+όWisU83iŽg&+;zte*&b]FC[&ۥyQPpyyRKujEN\,qĬ)* 4,vEÐ*ik+[>uefՏn`yI%0>0qOIubI1,5VxwS_k7|-bi~~!:6Z&20  '*PLO}&8z@_&ayk{,n. Ǩ{ dĴB׬K~f؞,!EL62ZP79eKAVVje7d3s =,+FE ym9+?t?t\8`T ۛ!׉"];~0]k<<+bo#Q6[C E]I:ł`Gex1;fFN10N045]+ ; }aiVx>x:Jж}MQ \)Y,d@* )zR9ޫDh.AIY%߸ עv$uev_Z[[0ֺo߾+Л!iU$&)Kȗ_#J)G k"n#Oi+ @|^|ӯ`]$TW!y<χMR*!A`8D_3}{ @zx mlg*O* *$5Xs5mrCmAVxA%dh<hc!QJQyEUZCnξ*ej* uj:-( σ@V6Q%ܽpuhg8Gǿg+Q 4y.en{o\Tmu[=^h[5zRBJʢ'"!FX$z{ RPRAAA)W RʑkbG{8qxJE9Dﴀv0)P|k}[8A'pU+{9J0RYt#rB]JqjqU1Ul (*jAN&@Rq୮"xQi4@4tN`m ΨOJf}[xuC3džx~K쓐.cil`&&IQYH,(<|o+=<| zGа a<<>4d-&}`Z϶-4A~˷2U9o?r^X2%d+[j2\,^Y* QpMYღ kNj_Ł ~P@Y8Ie(BC.$@CzVG2dv9_ԄV[^?Dqo\n;!@<`O]Bh C8 6c@ YYlI(Y¬ +,bQ/|<8en|i"#!S AuMC6pȰ&u\QX N<(&u;d .h>ĄdT.ٽF<=z)^;o,-(s' `8{DD޽|_YXh:!(;G Z9UθP1 Æ|Kwf-0W'W֑apȤNvϭ]0v;zֲȚRuZQ2,®kd]f쾈>(+ 4%df?y*V R<("9/e vԿHwvp+޺&Ukp# ppo%e# 2} eI}/O9,@X\3A6u0E+(C.S.PuА/[\w褦nG)P5>‡Q3Y떉nS*B$B YAz^ͧkk"8~ti`^? 0AձVq@ sN`ς }@BE5"OBQI[ #.XRABTnE 钎gvN>q]Ji`pYRD, 'g<#l_MCW@6>GQ2CYۂ %-$ 3zPI@xׯ{8qX&I8R¬WjmB oSj9?c2hr7=Cֶ(Jq@i͗TH6T]859Nѝ%TFN,pxCe=QAFMc&|e|f" Ӂw0TT 0 *;}t˸)., ﱞ0d~YO:k5gO.mwaLn~d1eH4gK Lj|Lٽ'FUݷ"%UʔVij(kWA42@/z1̈́ xv><6>xx#ܤR-^bX]G9KTRˆqzٲY8Kcpm:g#]|&"m!"G44e-&,*111BK%rʑ?Xa`x;tc30~bߴ WhAkkrep@ΔboD@ehJOw}xܻGZm.Ykn rq)C$4-z̤GrJϱ~ $¿6`a#LQqlBU_T$2u!p`JXp>׌(-HLJWs-ws"eQ:+CxS1 KЫ i؆@13m||B GWvR}xG@z;]!eyY@ loVm HꭏNjo7~q$55ʆ*,O9<3^eF:OfQ An$@箃۸Io~jR&ps+jxV \&0C6.bpˍlUXXXKGYXJW3 (A<ܦ)ދ0`:peNjӧsרd eO>:D\&T~DUG S@:ls1%p2;ø D ^o\bcK$?& Hyv z-^Y(V$>n 7/>k .Ҩ]\^]KbsP9֡UD>'TEr9eCRw 2GǏgZpr -⢤_Ot_5G΢**2LLa (#P%IK<2<S?*|n"'6 ΜcTcIG:^_rCQY5&PIxE!aA0$ w}9YifPT[e¹?6D߿<ԑ贲gW|[e8II}l-?7"6^)!yY5(yc~ΓH–N]ِ #^`RW= Z]-*Q7,{Ux ]"?5jX$(b?Wf3NJ2Tf#i v/*OS+,U0yRR/߿gXEx-aM)_܉ ,rf#+񏍼 b<@]g@WVoq{R>@[nu3uz3I^w>5U7B(Jĭ3U6jarxC)N>w?b F"`{0)Ys ~T"f+p:NӷGo@|3_[jyw' чO||sӇ6ZǶ* LWE\ #ejT`TB$)jwYz?_%p޽@-u (ʺL.PtN`P2^x?>$)}m4 ޶OlDSCEdzQӂ5K?}lt][u-/J<+ޗBKxu7nqt+Tq+=CR69PN(tpp@#<ްJ"寮,y5-O[6|mM ɽ{:5ɩHW;Q{95=Na(y.l`Y$gEǼQ և<' "K; _oޑ^x| B#u{82鼧`U<޼">|vo\.z9Bf'etkݧl$^čr4%6WpY9P rKLʆ 꺒#Sgw `"] 'pb7;a+eSAft,secQU7('"НS<OØ(.\+܋̗;@  І!q2qi߆ހNjoWm_cۣ5}| r)Ek>Ǟ3LJ8{b(,/{x5 ;zMXe|qA[$<X )*)36"XbL^?9F{7TJUZ;`U"*.3Ç<9ɩjpe  OL{a4eM㴶'孭JӚ Ce)GeB)=+[/+}\n'ٱ.8"Bubk4ŹԸMxUzkoyb띖7ЪXu{w&yy R{;||:gm|+KCsX=oVŕ([3 6@C i U$&ʱmSt\8sycxs꿛aItԙ3O *I :כe)WE(lRc A2RtD! &u"m\{%Z:k"Q$K`Zº|C`s[`sM6acXl& +ك+C&57^W|K奐`ϢbgAayIbiA /=+s ^2ZY:6&J_"g^8P׮50[C0}^3*kkWAAZ&M Ǡ,KBeZ5:83g t@}vmm.yW^ C"2*,6sl1Ҋ9c'e{B'so@zq@Ǘ ?RZ#9/KV8wr_{zϼ;zUܡ\Qއz5/_s`y JJ\m#HZy})r hJ( IqQg]܄Q;POza}vg[bt&ՙA>=EҊ{m~gcФZ }llDuRԨ)x?,A;ΌO뱼(;oԡ#Owpmsz0 oxg mwFde{8Z,$X~elj֚Ȝ29%%rrq7z=UF wLzVkQxr},cU(Ⱥǭ2-&"J"39YZT&wFgocBī%Y*^cP/߱7=cp@yW'- >;!90\ߚTPX@~et,$ DU&@@|B_S?B 1ls^ۗ8uxv5yl^Cmw>DÈM?޸2Y|}.;%q籭\2YNjK6Pc $=JٱIꏒXXtlB(!0xi^tv)1]'v6_@>k7nY&#ϱ8}LKh1({!@ᕳ'Xd|MnGl[Ty_K'*o^mD Yhom 1F_}dBsg[WO;X5%7j W8q0 f0 <| 'i>_|=Et 럋Z,'ʕ/'/<a>ڮ!&Ƨ! bYa~^@5Zq\C` p ʐ½Yv!BDWrw!OF0#Tc&к?DhpAf>YgaKXC[g{g` aEW1b?~9`iA9 N PA+oyWx!FvKb!/yGdR}8y+7ZMC9~Uzǝ'x>VGݗ 7[KB;oaeQ5ƳI\缂YEKVFi5\h Ü]$='*kȵ&괜5tϜA0v\W7 cmm,}h] yma6r`؇#g5~3G@=v*(nÐߐ}7 y,}_-0:>G}ok6}V"T~|4r_{m \]P{@}  8y4G;P+7 yxUK^miK,,v$ץN"$p(^@ la_) ~C'TE$V%v`^U$ءgyW9 VC`~O0~X%r} s ϣ "t@쾂Ȱ)?Bq8$g.9.Oy ˂e]ۘRP `7 \۽U<O~RF1Xd$]Z J7& 3!K,p-//yqt&zk=< v+,i!|m+(\xgxUKOL Uf([GCVOy]z}5z-ٶQ8*Eb0 ހv!bO)s'6 jeEs?2>; wOKqyk-#2,dT*'}W_SOVޤe]!~4!ki;V+|j%jG#Eyz \|^\@Q `Kmn+Q0 [w3@pGQe(/+XhK{|+KHdDVdqGPO "Dyrpx ~G |/Aa"o?0%k]$0b.9Og!B ҥMvydtz^i쑝z&Vxػ$ݿuA9H@֏LFʘ(Zdy 7'@¯/#Ԕ𡸸 Tdܴ-\YtiιsSźV~͞âwSQ/)o!bf5-y="2ɡ ]oHj&wpe1rwq۫j giyt>=}wS+ iXq$`ʹdT(| t@xQ ,E ȪB9dhw5"\igww)4,"ǩ0JoY:+yseˤ#FdEqv3?W gQ4҆};kW;xRC%KPiQ"9uxζW e0LǶ(xR/_4VRXf,9C%~$V|P4`ƀc`azgxNi㑅glޥ˰6%K\Ηj8xƝC7zivއ_W}ޓ5l,klLU8P `ژ.#"ŭ9(?τHbQ'uv?E qx 'v~ދ(R{X^@2Q7đ 8 {|c}u|aS_tL",Q70";^Xm߫%#;h{do&FTl*&^M+tNAT]@|eBJf' JK%K NQ`x2CF dUAy`_CV`>hPۭU? ?V'}Ƶw xvY;1$6u w,{AV f6V Y{\KQڧ V-WbF )wGV8k[.oMKGy8i  uZ/ocxz _mhMtZ+2-賔6̖y7N^9cev/ : xֿnPIȎ(2,E+.2qVi/X\;Og\tqQ{u݋kkk V2>(?9(Cf+*LZStEu.VfB*Cpe $D(ueXU脢)%GXpho\c^ PZ mNKb# /aﮎ7%.<?v;큈Ph{-_)}Ǘ8g|?.o4Qٞ/7 쎀VȃCrƽf>R&߹do<#̊V ۏ> <%nb/drw\[[ƝWWq_9,NXpG1TTe H1q V}t ^|N(Qk&&8KMk(wou/]hTUA>[ r\#%:(^/}/5WV\f.H8eő2Rb Uv(2(ﻲA$3/qVW.Z/.bG!WW|v30طod I/t&dꡝ(@ MQ"SIq>Pt#K+G8 $6In"*UN .TRzWXYHYLi5qXMm9]yO8N;)9&2MU2r Un|ҟ\)2UHzF42~hr<|*8zrZrWxes@dzC9<&)Es ;u .L3O#;hGd6~;dgӯ ̖=rmgK2 W3 WϰR,jp<$wf4 Oix$TFQ) 5, _ #Ϗ{@ R@"Ksڭ&l{KWb&Jp%k iպ~AC֎cryUlLJ";1Q%O΄ڷ3'ES%bBD8ѕʶyKԡb F*!.^8K29R׼ob,TK'6 erjPU01Z#Pf2`JUQUo2{9fuƓn3~3>CeXt􆨠苪\ϑ GRWet.c-{,}*KqV"GOsRxQ8T9* }"ze($==J' lHv.ϪRPBC?,-HN< f;9}x1 *@8$tV5P&GL(O-"S*gxDAE&n2QS>hy, ÑI雪pQM)}嘯ax0{ ӧgm\%5ք6SqytV+sr&$Q}moC9X4D)m=}K/v PiW/V5:6/[&*XaJ|VTIkW;b7Fd$#x"dث~ yu sTl1I] ߨȎt|O\kʒѶܵes9Gy0J u%Փ=w8lj(L иUaB\\.M㎁)pW8X(KAa*cc= c,*#9Of~_xp֞wYםJVE&$WdD|)e*sXR!!e~;)~\}"Z#!6beP.ObFc#4r Y`ULo|<&~.ʍQQpm0NȜD`,tLDڐ{=Ṯn{xaV${ h~ X&ty*ߣl_٘Ϟ{i8)J$㞿UVBWjfn83+/b'fYIvɬ3CYE!;:0*] e,Ndcf]#9~xRw~cg{?sb~ @ϒv@%]֟,F0q 00qJ*wb߉ 3xQ$@:Q0RsDI1FTi y9oIuBIZ f20:@: HXXΞEc3]c3R;\__󟍌֬*n.ƍT6x1P̭re#-Cflka"@Ҏ_P/E1/g<}灉UE64AWe(&QǘP4RwydQֻ) ]޿ A$"v?  @ˈU(@,$Ue'NΝ=0ԑ#е؎''xOn׿!?6H! Epmq @YȀKS lx5B(bF,`co1_z~~J_xf@I( >.I@bpD,ajNT-4 @ AT=f~2f?(ڨYlApt)c %asB ZR3(òÿnx|'ĩS{ˆ?>{Ӻ4 `~NٳD)BdL,ۊ e"){8Q`&`FHB#+YV(`1A{["2Ln,fR(֭`Ő3#{q2Z}~hEDvNx.LCC< DT-HBς?]wCϞؿy؅<=i}| N;Oci\Xt +WоzR6-/qȌU&:9|e$y"aSK A#,vmZhQ>*klN75}ZMRV5!eIX}>B!aE8f&fTɜ/m.ljy |1o֠$s-cFxE.UU6)Dx ZYw/}=7 git־./c'׃ `PR" "D WJA{*P ,C9|bIs ! ܩ" n2 >WTH+bX65 A$ gוdÁ"jounS%g eR.eO+\lV h:P@,.%,.-I:~mmŒu6{b)Id0@y DBy2<_ D< ߗ"9<*U@d4HaA!UϬ kAٛ4UGSarևM+~YR  %}^ָ WoQp.w Fv>0Ⓜkd# ϟ~;Q4;T6ɧQ\ŮsΙČcUתڟpnW.[|9?9*=O{<q?$O $dL+tHbF_+4 ,nsb!vmTpsdn}4&능d#Ë(VcFu^j`Hhӱ~s(a|? %9k >g\__ߺnȿ~ni߷'l ׆bT/*j<.U:9FT3{Q% E˦hcBFUq6gH`|$@DFn'$}rI>9[hs^8L37LC/uK ߅虥T1#Y MPB%ga5V(ʁS޽-|2HXSrvKH(@NP 2{,&̈́'mc Oh#q=7M^n_o}}p7BB1Q.L%e PN}pA'2:¤@`E2O[__M'nkU3HŭnCeb%9tK_MZ{q`8a>8l a.A7-U "|One[qѯ- n7IJ0'!J;@=DJm'QHAEF4O8 DA0 eWlb=d\`?"*J6m C w@*RBH'AEJ?|ЃRzAJ/e`h 1@ H[F]!v+.=~pf!K|LW }Wbu^TIEvˀiٮ@^ >8]P(=PMA#t[;jx/2"-7j{n:3A~fdпIcDADz!|ןp! D$aIaB+D! =|#D  !d:yH<~PP< 0ҌBCL!OF@J^u@#!A)/>R^'`r4 a~x>2ec3 5g,# XAl $DH()0 ߬])@&0фc! 144DH_6" Ah$E}Tb@pC21p f0Lư'4X V[2 k@@`DABSgq `h@"%!Pb$@ {:/Ta_R`VH7 &Sv/gh ^}="ͩyHd2#?L{CK q$"Q?Eb8))*x>^)9] +*CN 8/H Ǯ 2 I@R(Br{\gw+0hywy 11_e)ioʝFYpZIaY?Iծ& }857B}˥H"eO+D۽>s`u4Bp6p{-N)C%Fr^Cyf4.TTjW$[(ÕM1 `jJ}J^mzzK)lhJɊX=k vʹ0Fs# ӵAب$c8`?},XX>g%|B(I7C,f']'Ìby)L_((_ύK\]bH+ Q^kd+K3RH+sу8;l u/x nM& J_IENDB`linux-show-player-0.5.1/dist/linuxshowplayer.xml000066400000000000000000000004471323636106000220730ustar00rootroot00000000000000 Linux Show Player session file linux-show-player-0.5.1/docs/000077500000000000000000000000001323636106000160545ustar00rootroot00000000000000linux-show-player-0.5.1/docs/user/000077500000000000000000000000001323636106000170325ustar00rootroot00000000000000linux-show-player-0.5.1/docs/user/Makefile000066400000000000000000000011501323636106000204670ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = LinuxShowPlayer SOURCEDIR = source 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)linux-show-player-0.5.1/docs/user/source/000077500000000000000000000000001323636106000203325ustar00rootroot00000000000000linux-show-player-0.5.1/docs/user/source/about.rst000066400000000000000000000025001323636106000221730ustar00rootroot00000000000000.. toctree:: :hidden: :maxdepth: 1 About ===== Linux Show Player (or LiSP for short) is a free cue player designed for sound-playback in stage productions. The goal of the project is to provide a complete playback software for musical plays, theater shows and similar. Features -------- Here a list of the main functionality offered by LiSP: * Cart layout (buttons matrix) suited for touchscreens * List layout suited for keyboards * Large media-format support thanks to GStreamer * Realtime sound effects: equalization, pitch shift, speed control, compression, ... * Peak and ReplayGain normalization * Undo/Redo changes * Remote control over network, between two or more sessions * ArtNet Timecode (via `OLA`_) * MIDI support for cue triggering * MIDI cues (send MIDI messages) * Multi-language support (see `transifex`_ for a list of supported languages) Project Status -------------- Currently only GNU/Linux systems are supported. The application is quite stable, is already been used for multiple performances by different people, but, due to the heterogeneous nature of the GNU/Linux ecosystem, my suggestion is to test it in a "working" environment to detect possible problems with some configuration. .. _transifex: https://www.transifex.com/linux-show-player/linux-show-player/ .. _OLA: https://www.openlighting.org/linux-show-player-0.5.1/docs/user/source/cart_layout.rst000066400000000000000000000052341323636106000234160ustar00rootroot00000000000000.. toctree:: :hidden: Cart Layout =========== The Cart Layout organize all the cues in grid-like tabs, cues are shown as buttons, as in the following image: .. image:: media/cart_layout_main_view.png :alt: Linux Show Player - Cart Layout :align: center If the cue provides a duration, the current cue time is shown at the bottom of the button. Layout Operations ----------------- Adding/Removing Pages ^^^^^^^^^^^^^^^^^^^^^ Pages will be added automatically when needed (this behavior can be disabled), or manually, to do so two options are provided, in the top-bar ``Layout > Add page`` and ``Layout > Add pages``, the first will add a single page (at the end), the second will show a dialog that allow to insert a custom number of pages. To remove a page, select the page to be removed, then ``Layout > Remove current page``, a confirmation dialog will be shown, if ``Yes`` is clicked, then the page (and cues) will be deleted. Change page ^^^^^^^^^^^ Pages can be switched using the tab bar on top of the layout or directional keys. Cues Execution ^^^^^^^^^^^^^^ A cue can be start/stopped simply ``Left-Clicking`` on it. Cues Editing ^^^^^^^^^^^^ The setting dialog for a cue can be opened in two ways: ``Right-Click > Edit cue`` or ``SHIFT+Right-Click``. Cues can be selected/deselected for multi-editing with ``Right-Click > Select`` or ``CTRL+Left-Click``. Move and Copy Cues ^^^^^^^^^^^^^^^^^^ Cues can be copied or moved (into free spaces) inside a page or between different pages: * **Move:** cues can be moved with ``CTRL+Drag&Drop`` * **Copy:** cues can be copied with ``SHIFT+Drag&Drop`` Layout Options -------------- In the application settings (``File > Preferences``) various options are provided: * **Countdown mode:** when enabled the current cue time is displayed as a countdown * **Show seek-bars:** when enabled a slider able to change the current playing position of media cues (for media cues) * **Show bB-meters:** when enabled, a db level indicator is shown (for media-cues) * **Show accurate time:** when enabled the cue time is displayed including tens of seconds * **Show volume:** when enabled a volume slider is shown (for media-cues) * **Automatically add new page:** when enabled new pages will be created when the current pages are full and new cues are added * **Grid size:** define the number of rows & columns per page. (require to reload the session) .. Warning:: When the grid size is changed, cues will be visually shifted to keep their logical positioning. .. image:: media/cart_layout_settings.png :alt: Linux Show Player - Cart Layout settings :align: center .. Note:: Cart Layout does not support cues "next-action".linux-show-player-0.5.1/docs/user/source/command_cue_examples.rst000066400000000000000000000024171323636106000252400ustar00rootroot00000000000000Command Cues Examples ===================== Examples of ``Command cues`` to control other programs using LiSP: LibreOffice ----------- Impress ^^^^^^^ * **Start slideshow:** ``xdotool key --window "$(xdotool search --class Libreoffice | head -n1)" F5`` (requires xdotool) * **Next slide:** ``xdotool key --window "$(xdotool search --class Libreoffice | head -n1)" Right`` (requires xdotool) VLC --- * **Playback a file (full-screen):** ``vlc -f `` MPV PLAYER ---------- Allows to use a single mpv player instance, which is controlled through their json IPC interface. Use "Start Player" to init the player, you have to edit --geometry according to your display setup, "Load File" to play a Video file (you have to fill in the $MEDIA variable at the beginning of this command). The videos are played and stays open until next file is started. Images work too, so it is possible add add black images between the videos if needed. * **Start Player:** ``mpv --input-ipc-server=/tmp/mpvsocket --idle --keep-open=always --osc=no --fullscreen --geometry=1920:0`` * **Load File:** ``MEDIA=""; printf '{ "command": ["loadfile", "%s"] }\n' $MEDIA|socat - /tmp/mpvsocket`` * *A complete set of commands can be downloaded and imported as preset in the GitHub releases page.* linux-show-player-0.5.1/docs/user/source/conf.py000066400000000000000000000110131323636106000216250ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Linux Show Player documentation build configuration file, created by # sphinx-quickstart on Thu Feb 23 10:55:18 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # 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('.')) # import sphinx_rtd_theme # -- 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' # General information about the project. project = 'Linux Show Player' copyright = '2017, Francesco Ceruti' author = 'Francesco Ceruti' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.5' # The full version, including alpha/beta/rc tags. release = '0.5' # 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 = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- 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" # html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # 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 = {} # 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'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'LinuxShowPlayerdoc' # -- Options for LaTeX output --------------------------------------------- latex_toplevel_sectioning = 'section' latex_elements = { 'preamble': '''\ \\let\\oldsection\\section \\renewcommand\\section{\\clearpage\\oldsection}''', 'figure_align': 'H' } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'LinuxShowPlayer.tex', 'Linux Show Player User Documentation', 'Francesco Ceruti', 'article'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'linuxshowplayer', 'Linux Show Player Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'LinuxShowPlayer', 'Linux Show Player Documentation', author, 'LinuxShowPlayer', 'Cue player for stage productions', 'Miscellaneous'), ] linux-show-player-0.5.1/docs/user/source/cues.rst000066400000000000000000000161741323636106000220340ustar00rootroot00000000000000Cues ==== Cues are the main component of every show/session. There are multiple types of cues able to perform different tasks, those can be subdivided in two main categories: * **Media cues:** used to play multimedia contents, usually related to some media file or stream * **Action cues:** used to accomplish more complex interaction (e.g. start multiple cues at one), fading other cues parameters or interact with external devices or application. A cue can perform different *actions* depending on its current *state* **Actions:** * ``start:`` Perform the cue task * ``stop:`` Stop the running cue * ``pause:`` Pause the running cue if possible * ``interrupt:`` Stop the running cue, other cues/functions will ignore this event * ``fade:`` Decrease/Increase gradually a predefined cue parameter (e.g. volume) Every action (except for fading) can be performed with or without a fadein/out. Cue options ----------- Cue options can edited via a dialog, the way to access this dialog is described in the layouts pages. Options are organized in tabs depending on their context. Two tabs are provided for all the cues (excluding plugins): Appearance ^^^^^^^^^^ Visual options *(some of them can be ignored by the layout)* * **Cue name:** The name that identify the cue * **Description/Note:** A text for writing notes about the cue * **Font size:** The font used to display the name * **Font color:** The color of the font used to display the name * **Background color:** The background color of the cue Cue ^^^ General options for the cue, organized in 3 sub-tabs Behaviors """"""""" Define the default actions used by the cue, this allow to disable fades by default, or to pause instead of stopping. * **Start:** Action used to start the cue * **Stop:** Action used to stop the cue Pre/Post Wait """"""""""""" * **Pre wait:** Add a delay before the cue is started * **Post wait:** Delay before ``Next action`` is executed * **Next action:** What to do after ``Post wait`` **(can be ignored by the layout)** * *Do Nothing:* You know ... * *Auto Next:* Execute the next cue * *Auto Follow:* When the cue end, execute the next cue (``Post wait`` value is ignored) Fade In/Out """"""""""" * **Fade In:** Gradually increase a predefined value on faded(in)-actions * **Duration:** How long the fade should last before reaching a maximum value * **Curve:** How the value should increase in time * **Fade Out:** Gradually decrease a predefined value on faded(out)-actions * **Duration:** How long the fade should last before reaching a minimum value * **Curve:** How the value should decrease in time -------------------------------------------------------------------------------- Media Cues ---------- Audio Cues ^^^^^^^^^^ Audio cues allow you to playback audio files. A media cue doesn't playback directly the media, but rely on other components, those can be configured adding and removing effects or controls (e.g. volume, equalizer, speed). Options """"""" * **Media-Cue** * **Start time:** Time to skip from the media beginning * **Stop time:** Time to skip before the media end * **Loop:** Number of repetitions after first play (-1 is infinite) * **Media Settings:** Media options provided by the backend * :doc:`GStreamer backend ` *Video playback support is already planed but not yet implemented* -------------------------------------------------------------------------------- Action Cues ----------- Collection Cue ^^^^^^^^^^^^^^ As the name suggest this cue allow to execute multiple cues at once, for each cue a different action can be specified. Options (Edit Collection) """"""""""""""""""""""""" You can Add/Remove cues to the collection via the provided buttons, ``Double-Click`` values to edit them. -------------------------------------------------------------------------------- Stop All ^^^^^^^^ This cue simply stop all the running cues, alternately can be configured to execute different actions. -------------------------------------------------------------------------------- Seek Action ^^^^^^^^^^^ This cue allow to seek a media cue to a specified point. Options (Seek Settings) """"""""""""""""""""""" * **Cue:** The target media-cue (a button is provided to select the target) * **Seek:** The time-point to reach -------------------------------------------------------------------------------- Volume Control ^^^^^^^^^^^^^^ A Volume Control cue allows to trigger a volume change/fade-in/out on a selected media cue. Options (Volume Settings) """"""""""""""""""""""""" * **Cue:** The target media-cue (a button is provided to select the target) * **Volume:** The volume to reach * **Fade:** Volume fading options * **Duration:** The volume fade duration in duration (if 0 the change is instantaneous) * **Curve:** The fade curve -------------------------------------------------------------------------------- MIDI Cue ^^^^^^^^ A MIDI cue allow to send a MIDI message to the MIDI output device used by the application (can be selected in the application preferences). Options (MIDI Settings) """"""""""""""""""""""" * **MIDI Message:** Set what type of message to send * **(message attributes):** Depending on the message type different attribute can be edited Supported MIDI messages """"""""""""""""""""""" * ``note*on`` * ``note*off`` * ``control*change`` * ``program*change`` * ``polytouch`` * ``pitchwheel`` * ``song*select`` * ``songpos`` * ``start`` * ``stop`` * ``continue`` -------------------------------------------------------------------------------- Command Cue ^^^^^^^^^^^ This cue allow to execute a shell command, until the command runs the cue is ``running`` and can be stopped, doing so will terminate the command. To see the command output, LiSP should be launched from a terminal, and ``Discard command output`` must be disabled. Options (Command Cue) """"""""""""""""""""" * **Command:** the command line to be executed (as in a shell) * **Discard output:** when enabled the command output is discarded * **Ignore command errors:** when enabled errors are not reported * **Kill instead of terminate:** when enable, on stop, the command is killed (abruptly interrupted by the OS) For examples of commands to control external programs, see :doc:`here `. -------------------------------------------------------------------------------- Index Action ^^^^^^^^^^^^ This cue give the ability to execute a specific action on a cue in a given position in the layout. Options (Action Settings) """"""""""""""""""""""""" * **Index** * **Use a relative index:** When toggled the position is considered relative to the current cue position. * **Target index:** The position of the target (the UI will enforce a valid index) * **Action:** The action to execute -------------------------------------------------------------------------------- Editing multiple cues --------------------- You can select all cues at once using ``Edit > Select All`` (``CTRL+A``), while multiple cues are selected, you can use ``Edit > Edit selected media`` [``CTRL+SHIFT+E``], to edit multiple cues at once. The available options will depend on the types of the selected cues. linux-show-player-0.5.1/docs/user/source/getting_started.rst000066400000000000000000000032751323636106000242620ustar00rootroot00000000000000.. toctree:: :hidden: :maxdepth: 1 Getting Started =============== Before diving into the different aspects of LiSP you need to understand the main concepts that set the basis on how the application works. Cues ---- First, and probably the most important components you will find in LiSP, are the **cues**, a cue is used to execute a specific action in a repeatable manner, every cue allow customization of its behaviours independently. Cues are at the heart of every show, allowing to play sounds, send MIDI messages, controls other cues and so on. Layouts ------- When creating a new show in LiSP you have the ability to chose a *layout*, this will affect how cues will be displayed and eventually provides different sets of features. Currently two layouts are provided: * **List Layout:** arrange the cues in a list and provided a mouse/keyboard oriented UI * **Cart Layout:** arrange the cues as buttons in one or more grids and provide a more touch-oriented UI. Menus ----- Most of the functionality are accessible via the top-bar menu, here a small explanation on what you will find: * **File:** Operation related to the current session or the global application * **Edit:** Functions mainly related to adding/editing cues (accessible right-clicking on empty areas of the layout) * **Layout:** Functions provided by the current layout * **Tools:** Utility to make life easier Cues provides a contextual (right-click) menu to access cue-specific options. Plugins ------- Linux Show Player is heavily based on plugins, while this is almost always hidden from the user, most of the functionality are provided via plugins. From time to time the documentation may refer to those as plugins or modules. linux-show-player-0.5.1/docs/user/source/gst_custom_elements.rst000066400000000000000000000035531323636106000251550ustar00rootroot00000000000000GStreamer Backend - Custom Elements =================================== One of the most used functionality of GStreamer is the ability to create pipelines from a text description, usually this is done from a CLI interface (e.g. on a terminal) using the ``gst-launch`` program, in LiSP it's possible to create a custom media-element using this functionality. Element Syntax -------------- From this point ``element(s)`` refer to a GStreamer component and not to LiSP. Properties ^^^^^^^^^^ *PROPERTY=VALUE* Sets the property to the specified value. You can use ``gst-inspect`` to find out about properties and allowed values of different elements. Elements ^^^^^^^^ *ELEMENT-TYPE [PROPERTY_1 ...]* Creates an element of type *ELEMENT-TYPE* and sets its *PROPERTIES*. Links ^^^^^ *ELEMENT_1 ! ELEMENT_2 ! ELEMENT_3* The simplest link (exclamation mark) connects two adjacent elements. The elements are connect starting from the left. Examples ^^^^^^^^ The examples below assume that you have the correct plug-ins available. Keep in mind that different elements might accept different formats, so you might need to add converter elements like ``audioconvert`` and ``audioresample`` (for audio) in front of the element to make things work. **Add an echo effect to the audio:** ``audioecho delay=500000000 intensity=0.2 feedback=0.3`` **Add a reverb effect to the audio:** ``audioecho delay=20000000 intensity=0.4 feedback=0.45`` **Removes voice from sound (or at least try to do so):** ``audiokaraoke filter-band=200 filter-width=120`` **Remove voice from sound and (then) apply a reverb effect:** ``audiokaraoke filter-band=200 filter-width=120 ! audioecho delay=20000000 intensity=0.4 feedback=0.45`` -------------------------------------------------------------------------------- Extracted from the `GStreamer SDK docs `_ linux-show-player-0.5.1/docs/user/source/gst_media_settings.rst000066400000000000000000000116131323636106000247420ustar00rootroot00000000000000.. toctree:: :hidden: GStreamer Backend - Media Settings ================================== Media Cues relay on a backend to provide playback capabilities. LiSP currently have only a GStreamer backend. A backend provide a "media object" to the cue, this object is composed by multiple elements that can be added/removed and configured, every element process data and feed the result to the next element. In GStreamer the set of active elements is referred as "pipeline". The elements and their settings can be changed in a specific tab in the cue settings: | .. image:: media/gst_media_settings.png :alt: Linux Show Player - GStreamer Backed settings :align: center | The active elements can be changed using the *Change Pipeline* button .. image:: media/gst_edit_pipeline.png :alt: Linux Show Player - GStreamer Backed edit pipeline :align: center | The default elements can be changed via ``File > Preferences > GStreamer settings`` Input elements -------------- Feed the pipeline with data URI Input ^^^^^^^^^ Read and decode data from a URI (usually a URL), can be a local file or a remote one (eg: ``_) * **Source:** the URI to look for data (a "find file" button is provided for searching local files) * **Buffering:** buffering options (for slower random access media such as a network file server) * **Use Buffering:** enable buffering * **Attempt download on network:** attempt to download the entire file on disk * **Buffer size:** buffer size in bytes, -1 will use the default value Auto Src ^^^^^^^^ Get the audio from the system-default input device (eg: microphone), no option is provided Preset Src ^^^^^^^^^^ Generate some tone using some wired functions. Just Fun :-) *Don't try to use this in combination with the speed element or bad things will happen* .. Note:: To use ``Auto Src`` and ``Preset Src`` you need to create a media cue with some random file, then change the source element. Plugins elements ---------------- Used for audio-processing or data-probing, in some case the order affect the results Volume ^^^^^^ Allow to change the volume level, or mute the media. * **Volume:** volume level in dB (can be muted) * **Normalized Volume:** parameter used by other components (e.g. ReplayGain) to normalize the volume level without affecting user values, you can only reset the value (to 0dB). 10 Bands Equalizer ^^^^^^^^^^^^^^^^^^ Allow to equalize the media with 10 frequency bands [30Hz-15KHz]. dB Meter ^^^^^^^^ Allow external components to get the current sound level, used for UI visualization. * **Time between levels:** millisecond between one extracted value and the next *lower values will use a more CPU* * **Peak TTL:** Time To Live of decay peak before it falls back (in milliseconds) * **Peak falloff:** Decay rate of decay peak after TTL (in dB/sec) Speed ^^^^^ Speedup or slowdown the media, without affecting the pitch. Pitch ^^^^^ Allow to change the media pitch by semitones. Compressor/Expander ^^^^^^^^^^^^^^^^^^^ Provide `Dynamic range compression `_. * **Type** * *Compressor* * *Expander* * **Curve shape:** Selects how the ratio should be applied * *Hard Knee* * *Soft Knee* * **Ratio:** Ratio that should be applied * **Threshold:** minimum value from which the filter is activated (in dB) Audio Pan ^^^^^^^^^ Allow to control stereo panorama (left <-> right). .. Note:: When used the audio will be forced to stereo Custom Element ^^^^^^^^^^^^^^ Allow to manually create a custom GStreamer "elements" using the framework syntax, some instruction and example can be found :doc:`here `. Output elements --------------- Push data to an output device Auto sink ^^^^^^^^^ Use the system-default output device, no option is provided. ALSA sink ^^^^^^^^^ Output to an ALSA device * **ALSA device:** the output device to be used (parsed form asound configuration file) PulseAudio sink ^^^^^^^^^^^^^^^ Output to the default pulseaudio output device, no option is provided. Jack sink ^^^^^^^^^ Output to `Jack `_ server An editable view of the current connections is shown, on the left the cue outputs, on the right the available jack inputs. Selecting one input an one output it's possible to connect/disconnect using the provided button. .. Note:: * Unless the cue is created with ``Jack sink`` as output, by default all channels are disconnected * The connections to Jack are opened/closed when the cue start/stop * If no instance of a Jack-server is found a new one is started .. Warning:: This element can cause problems depending on the jack server configuration and/or status. Currently it's quite hard to debug those problems since the element is partially based on a GStreamer element that allow little control over the used client.linux-show-player-0.5.1/docs/user/source/index.rst000066400000000000000000000007161323636106000221770ustar00rootroot00000000000000.. Linux Show Player documentation master file, created by sphinx-quickstart on Thu Feb 23 10:55:18 2017. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Table of Contents ================= .. toctree:: :glob: :maxdepth: 2 about getting_started menus cart_layout list_layout cues gst_media_settings gst_custom_elements command_cue_examples plugins/* linux-show-player-0.5.1/docs/user/source/list_layout.rst000066400000000000000000000057331323636106000234440ustar00rootroot00000000000000.. toctree:: :hidden: List Layout =========== The List Layout, as the name suggest, organize the cues in a (single) list, a sidebar to monitor and interact with the running cues is also provided. .. image:: media/list_layout_main_view.png :alt: Linux Show Player - List Layout :align: center User Interface -------------- Top Panel ^^^^^^^^^ * **Top left:** we can find the ``GO`` button, this will execute the selected cue and go forward; * **Top center:** name and description of the current cue are displayed here; * **Top right:** list-control commands are provided, those allow to stop, pause, restart, interrupt and fade all the cues in the list. Left List ^^^^^^^^^ All the cues are shown here in a list-like view, the following column are shown: * **(status):** the first column show the current state of the cue running/paused/error and a selection indicator * **#:** The cue index * **Cue:** The cue name * **Pre wait:** Pre wait indicator * **Action:** Cue time indicator * **Post wait:** Post wait indicator * **(next action):** What should be done after "post wait" Right (running) List ^^^^^^^^^^^^^^^^^^^^ The running cues are shown here, you can stop, pause/restart, interrupt and fade single cues. Layout Commands --------------- Navigate the cues ^^^^^^^^^^^^^^^^^ To change the current cue, directional keys can be used to go up and down into the list, alternatively ``Left-Clicking`` the cue will set it as current. Cues Execution ^^^^^^^^^^^^^^ To execute the current cue, press ``Space``, (can be changed in the layout options) or use the ``GO`` button. Cues Editing ^^^^^^^^^^^^ The setting dialog for a cue can be opened in two ways: ``Right-Click > Edit cue`` or ``Double-Click`` the cue. Cues can be selected/deselected with ``Right-Click > Select``, ``CTRL+Space`` or ``CTRL+Click`` Move and Copy Cues ^^^^^^^^^^^^^^^^^^ * **Move:** cues can be moved with a simple ``Drag&Drop`` * **Copy:** cues can be copied using ``CTRL+Drag&Drop`` Layout Options -------------- In the application settings (``File > Preferences``) various options are provided: * **Show playing cues:** show/hide the "Right List" and list control buttons * **Show dB-Meters:** show/hide the db-meters for running media-cues * **Show accurate time:** show/hide tens of seconds for running media-cues * **Show seek-bars:** show/hide seek bars for running media-cues * **Auto-select next cue:** if disabled the next cue will not be selected automatically * **At list end:** * **Stop:** the selection doesn't change when the last cue is executed * **Restart:** the selection is moved back to the first cue * **Go key:** define up to 4 key-combinations that can be used to execute the current cue, to do so, double click the edit-area, then enter your keys combinations * **Use fades:** when disabled the corresponding buttons on the right-panel executes their action without fades. | .. image:: media/list_layout_settings.png :alt: Linux Show Player - List Layout settings :align: centerlinux-show-player-0.5.1/docs/user/source/media/000077500000000000000000000000001323636106000214115ustar00rootroot00000000000000linux-show-player-0.5.1/docs/user/source/media/cart_layout_main_view.png000066400000000000000000001124601323636106000265070ustar00rootroot00000000000000PNG  IHDRV/GsBIT|dtEXtSoftwaregnome-screenshot> IDATxwx\՝TFe%$Mm6ŀKo avvIcYB M,c2FrɒmY.*HVI3CăؒU~=4{ܫ3r93~r{;W7$V D$V D$V D$V Dz)˶m2BKgh5͜!dDh|@~ ^{Cͺb&`0ŕ~l JK :)hFTTV-;uT|>bc>R5Lnw~o&[c-;mY52#]f]xг(ͷ%aajX~<.zzdlݩm-7,T# p%+qz Iųe4uLR't;2z`}e|~:ڵsM0W@2?W*;_r)԰.'Wc[0g|~ +{~]sNwI66U~ZsfNfb:?wW 9#5nlN#U[1 8+͕28]>ױbE?ޮ_H{ǵ<~wnɣ?ZSjt5 ]5o(^߽贈coju_J}+W`0蓜}:~DݴHƳY={s5meAζ~cN*IƏւٗK~SJl2i#42 4z`M?Jv}YA.y }nbi;kT|JNWѻdiؐ4Mj)0|>}['OKbbc9=r.˶mA:-?7!sw_7dٖ-+;Xt}k刊i隫+2"Qu3btk<93'NZ;ҵfJ t|}v@/]|m<5fP]tm*gWTw>R% [ցܣl$iT` ԑ\&}QQ-I:VxZ) &U}~jt#$9>h`+&{n"gK{ka=\(WtT(EF՛ta=(˴j|>IUjhl?y͚|>Ffd^AiO0UUV,n:'sZG#22.͈U~,!̩*.qNI) 2LrDG*)!FUN:-o1tzk&9#4wdD= AZjف3.?58=EYqFƩFWnͼb 1u 4b }CWM&94:VxRI 1?ZQ#S^A~ۗ%5/^5l%ZZMJ$L JMTee&ue^QvQCt\ .t FH{뷪N%wO'7msWoɧ;4sTL&͜>U9UtT#ʠ׆GMYF$5W *cdA E0 IJ-sc[Ӓ/+5od2j14nlۣk;nhl_~)&:RFD I+1>VNjJTب4rX>y@.Tǎkެ,SejxZ6͟sY4Cv nm޺[eUx死5l%2:hJJ,v:(/g}wFʌHב'u\i2LJ[wkTFzmnA2M SSG%r\?՘C%I$ރ*9ؘhIS4gw`՞3:d5; ?+?}dNI >f.͈UVmzU34rx6U Nk>ld96j΃k  5Tle yvkڏUS[0Hגl2i ncv~+ŬǏҥc$9dZ#$I1liԈ]jL&f^1^^KV߽ͪRQQv$JVIUnq-;5-7^ sg2s@y|M3g(O%K' >/lj9KuK|,X莯^ۡkRgk jXq<.RLS%3jXk<.SÊ$[XlS*fOۡqnPYeyL$oBLj* <0wyGk׮OSf=A#{cccqFY, 2D>}][馛$I^z}1&I;wTTTiKl믿wyG?s,ZHorrr4Vx _^WҥK;\o]bo~Wƍb =cz|wQFFƏߪܿꗿjժ駟Vaa,V\kÆ Zdz-Iܹs+7|S"׷{ ڶ޽{WPPcܸq]JII9vƍZZlV^TwO<#G޽{URRҧ7GRVVN8QFQ7o$mڴI'Od:`ۋ#++KVU'OVxxVZ%׫Çu;&/ۭCiРA5ydEGGK.\7j„ z7UTTUViŽ:;Nv 6LA%%%׼yuV555)++K^zbccܹSn[5>зumIJJʕ+ۊet:C=uq$$$ȑ#|Uww0뮻tW+**JSuu٣ٳg?+ɓ'u>1mѩuQq=y %$$`0%z 4o<{ェ?طa-\PG999r\/{JJJKKޫ(}ڵkn:͝;WO=VX'NSL0a͛&IŋuM7XMLL޽{@-Wv*!!AWBB,X I2 4hPw2>fW_-IPTTTe4x`n˒U__:EFFjĈ}7oW\kV7n$ݻWaaae0|+ZfJKKp84j(YVM::w߃fSRR^vܩz9siڽ{w *Iٚ8q$IGVff>>зuzO>~a=ݻw VSS+USS{L)))zя~۵/_om)S gFV{;vq8qz@}*餪$7.0 Sܹ}_'8ZOGDDDhz'{*s׮mg:W1``SuK.ŋ|CpH@ГH@H@H@H@H@툌bp\zr/XSee兎=,,,L6MEuuu/9bQ}}Ruu5Tbcc{;)=}$V/bUUUzPLL xbJz_S.˥XXXXXXXXXXXX{;|~xTQQbYFnw/FukҥZկ~W_}ЍtbTXX &(33S>|>_o(ɤgZ` Coo|zGN]%SRRtk:tN٣{N'Nv'N˗kJMMUyy/̙3#Ebu˖-ze0L.\Cѣ^T\\W_}U3gNo!!bh/y~WG%%%InxbM>]_URRQl6?i=NMMWUMX?ԩSuw??{;>?cǣzJaaaڶmjjj{NF҄ cǎ*j4p84gIRtUTT(''G ;wz)q˓$=C[uay^}ߐhH555QFFN%$$?q񕕕`__~***v@|Ȑ!:r|A͛7/(y/J}&ϿoΛ7Uw]+WЪU4uTeddk*qGA󕟟iӦ)<}&N m߾O\SEbb(..Na}ᇁ2.Kqqq7n+IJMM$ 6LfYO֦M566kĈVMM233)ISttJbb;w6l*y׿U$=A\˖-ӂ %ϿFr-zz$Is _\s~iv)tvmW_}O\ӳx=1bx㍠mO?N^0@ƍbԭjĘI͏wlJHH$EGG__ݏuv򼦦Fԇ~ʸ\.Ƿ<>|fN:,I A9rU]]/fla=CX,z6,>k7=z}=}zK1{\TTv]OK4+x j<v-y~ij=zTuuu|xP9hԿۿi:qℾﴚ/v###Ciڵ:x땖;CTPPgO>DO=kҤI:y?{ *+Fz&N|رCW\qEPw}W_|n >\ÇWaadUWWfTv]v]{E#IZp^xVtg/cm߾C7HNNVMMM y>h ]s5O?7CC7J*--MG`/Ko+ȾG}T .޽{РI&)22R7n Zb=Ub_ԋ/a1v!k̘12:v}Km_b'NvA.==jz梚 xlz 1ngXt=9V DX1q 1ngX >C@|^eeem$V/Rm1ޠ&XX'OQ]]\.WwDŽTddv"##;\g!ջgϞs: ?c_*eWǣVv8šCJ:NjҤI2 VXXX'RSSZUUU)??_C jmlWܞ\=?zR U}]>jN$ ӻ!ݢVۭuu/-U~hKH\-^8 Vttti8p@_~yo.zRC3Q׫St.'V?I& p.RSS%5 2i /.Sǎ;v(@jǏkpot9ze0o0!&Ijll锟sdXxz;6p@_W@epdFII8$suLeew"O-Cccw9t:澸8ŵru9s-U__a .F mg{Ս&Uv+27Wւ 'Kkhr.'Vnw*""B_~Fet.HRXXnwo&. j_6j6M5HAUͦ ;.hQ|Wdqqqh |P\\ @30GPMzFFyVyšVF5IO{Ĉ1X5 /~_&I&IfYf9e Ќ pFƌij4גTٚZ1[Yk\g_vDD Ch/-?gNjY@30yd6?o6ggg5XY,HjZMJqΉՖ  CG=[~Z} 0f``8 ?F&KtVK61Ppv@`,6|666JjCe|J~ C`? fodzehjo MM|@9suu sN:Ny$$i2Ϟ잨>4/L[^M>Gƿ-Lz%IWƦ&<%Ynz1bT~_gΜ< nR}̙k`? )27W&OfOF[&KKF[fOQ=GJ_m6[*gNjN3E@30Y t%yno~/$ϧ'd-(Xt^UI:},5Tjg\,@30!SeƌQX݊%RJEDDfIj^e=bBZPGMJԼqQĪxg]]꺫Jߢ? TR"SIIoG\px\H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@H@HQFQFXl a>;٬3g*%%En[v҉'$IÆ ӄ drSNunM6lPyyyqEGGkڴiJLLjUuuvڥ'OTODDΝ7xCnǭXBVU~?m֭*((S4p zU͞=[NSoZYFUUU 6tjСꪫE^GcJLLŋk.mݺUG !%F[W^98VZʐUmm9H(<<\+"ϧJhȑڱc;([TT$(|Nո8͚5Kv]NS[lQMMMegϞѡ$ͦ%Kĉ=z߯ph̙r8zڿrsse}M/ L!Cl6tjڵ|]nfӍ7ިl6k߾}ڻw,X4%''kĉZnm{GuIRBBfϞ-ݮbA(o$V^ʠD_|ѣGĉu\c4xbeggPǏל9sf͚Ve####G[_bb=_]>OFQK,сt!9-]TA#N4j(\R )"**J6mRVVt+77W6mҢETTT.xb޽[VLL:]# V׻\.^=ZSNUXXn*4hW$''`0㒤BM6MFUb3**JcSRR$I$UWW+//OڹsgbQLLJJJTWW9-[/,_ǣ6l6,u̙Vt*ЋeZYV566^)//OC/`쪈imuuu2L~uuu /?^SSmO`544h:}t5ǪbiU`0[{moKK]v]]Nbr@GbEd LVeUXX.o6xР*Zcjkkt:5lذ9^;R__mr:ѣOwZU=+VPSSV\)>o<9sFWDDΝ7x՜:uJKK1+Vj`0D7oVCCCriʕAF-_\AZ[{9Pdeei֬Y[XIl6+55U2LUAA"""4a>}iӦ)%%Eڼys`$5c effJe˖6c*++{ァiӦiԩX,ݻ)hzn:͚5K'OQNNʂZ`,, nzO>ѩS:Gт 4deee$x<ڸq.\S6r>uW ǵy댏WffT]];v[Ҷ+Vhڵgϖԛo)ákVk֬QUU~֭[kQUU;h4jҤIA ݜVJ󮧫Z戴ɓm۶;lѣ]z=vuv׽(<>6ggg+;;>5^^^w}qUWWkÆ mj[ee{VK/Iow|g-**ҟúꫯd2Ju By}fUTT_*{7 j}gڴinT__ߓA!%V5x`+|TAAF;vN=sJJJԥjNN͛#G9fk_Z AYf)<<\ҶmZF(EDDh:x,X4%''kĉZn[ph̙r8zڿrsset7D2ڷoiUXXx`0hѢE <(͞=[v]ŲgΝTAAdZ5qDm߾]SL鴍 ꪫ#[Fnhlm]ۚ6hZ|)p!G3lIIIhG^s5|Κ5K111Zf$)55U3fʕ+}<Į|F-YD555OYfntFRb5>>^/_hѭ )77KuWUUi߾}={{W6LZhu몫 *sQegg+99Y\si&-ZHEEEe:t.]jUVV***J6mRVVt+77W.j̘1:zdUUU%ɤ˗kȐ!A_ЍF/^ݻwÊQRRR穨ЩS4aڵK'Nԉ'k{׶6vTgg:g֨QT-*--UW]]vǏմ4bI:~xؕϗ3f`0O? |dggPǏל9s`@BZ*<<՜.Kaaa^vet>ijݻW6/gKII`PAA~[URR"ϧNGzHNN$:tHRyyy)++SYY<"""ڭoٲe7oYqqqAWUUIjB7)))X,˓ә3gz׮]˔1ch׮]!#6K]2t -ZH_RSS4h FĨ>0Tj^eTlGQƍSFF>#|>%''`0*,,Trrr`<@BjUlj14;6^n؞NG -""555k`h9V-`_^0aF!*j"ݮGﶥVyyykuA9Ns;Q Cm N誮~f 6LmN ׫C)77WC ќ9s{NuuuJJJRbbx,(//16WLqq 5}v]zEEEm}Z)))Ԑ!C{ri̙ihhJ>Smmmм-:kfm:thmҕ,I*ϧcǎIyP[HLL'|"ǣǫ:0 azޏ>H6M3foUUUiժU\ Bzί^'NФId0p84bĈZxn4d[jONNRSSBPaaa]p`0ciiL&F%y1c(??KbK.Qjj!#-ժXI#|>vJKKe6X,]nҡCZ=YNl6e2t%t_.>bccUXXgjոqv;V2EEE:tvTWW'׫c>}=&5J:thL{Ӷ.䡉YYY5kn555<{lk>}$)??_0aN>ݥG].56|>[Ng֔)St:e2#G` 2DYYY*)) zn:͚5K'OQNNdٺtp 2ׁͪt *11QzN%5lݻwnF]V7n… 5uTvQvءk60ʶK~>|6ZbVk[nmsT4Wt%hfk3l6+66V^zrtرe6uر";6Tw?|δذaCOd=zTׯ׌3)-[sLp10L2A#N/^| M7ݤ/ L&S$Ukʔ)A&O={trLGz +Vڵk[7b }}/ Tv_z;]j}\t#V0544v=_IU6M7xc`s٬}曌ѬY%ۭ]vr89s^߯\l6-YD'Nѣ~8p@qqq5kvNgRM&''QJNNd"(mڴIYYYJHH_\y<]}ڽ{>,wɒ%:p:$áKZJLLѣGh4jVaaƏ9sh͚5tp귉Uݮ3gbi͌zزe˂r|[VV&ǣl6y<>|XR’:tHT]]_\|Y}}}zR'Ul ?^n[$.׫nvtM"h]t;s?x%OxG )[n?am/^0W###7VJ ’IzJR*挤YiCIK%MA `@ҐJk>sI^RsueW1xeIIwNRIH*/1=* IDATrI7I1咾$o UFjIS/I:|eЕu%%:0"I+$ݡHȊ`*CSw%H"cn[WI[@tޕucoP$'igfIIjUI$OEfr#XRt"_'释l#VPuLqoԯȌ7\n`/_I?`"fEn`VJ p8HZgݰ"3`|"iV3k)")Oү$x6.EnVeJ ]Y7*iוe B_3+29[%-+YV"iWϘXR!i"5H]ңls'%-%H$IŒ٪ Iȵ[c+-JZ$}E.Q9IIu-G R4U|UZs%};]??Aq)`,"XVP*'.I#XV"U`,"XV"U`,"XV"U`,"XV"U`,"XV"U`,"XV"U`,"XV"U(`j`` $eD? rdZ“tx488Zqx]FL2-W^@j/( 馛n׿2&4^@:ͤ^PX&;]@@ i-DGv9I3L|ڪd 4nYҌ1)#$W",Xlr:pXPHp86 \L?(Iv]#9[/MgM@V\]>w^JU!p~5zՓn+-(T)u_^%g_U(M.C> 3{Aʏ!!s5Vsg_gyӏIjn7g^nITMEt̹0z!Y^Ȭ 臩/7a%˞zd}e=ی͘ D:cg^nIxKTTa̤^ )y|Ӊ~,Xu}0TǥKǛ:,D 3KWm<ny@&Eoof **Xv&Swfr\E?@a*X7jˑN};?KW3霖q|z> M>vj~s:͔2hjj2_XYYq\.z<2kfY}mZtꙗ[4|KxRթ\9x $n:I~GI2 IMMMJ< 73V͓ΐi6Xs:ǥ*3CYz3yL, S UPxBS3V,ޜp翽9{\,9Rə)egE?.y@E`4;:.:z~C 9]@Y]*O^X}}SZ?r/tHLt/=K/6~@*].v@TSTy3cuYk=ʾ>=rKuŪA동xoUg:_$gB:ge ~tzduqV빼^v)@ڝz>f&`Dt2u>iTtHLt/=;0& U?⩨пg #9rUV&4*++;nҥ/p&31(pd}>kzlh(ۥiCb-\lºXY$fI# ɰ'`1>ޑ=8p8s^JUiiv=Z]T!􁤟;:v UYdaƪfn|\(R0~@&|ޮ@ alLYMQ<^*+Uf&}p8 ؘ#`,"XV"Uȑʠ3g( jhhH~kB*))QII*++nj*KS,0^@:^W=>R3_v9Wϖ#ˡr->OmmmZr PYY\. hppP}}}:vUTTsd?^91Hi&@ }pq5. Gަh^Ü7biƍQVV2544SmݖN3L|v䈾AݸLig\oßYtޮ+WT$`U}}yؘj&I2){ _u=MOMu<'5&`5 0 Ð$4dt: ]FL2-W^@j/+SvٝmHre ]$ <14(lؓ>`󩬬,溪*UUUMpmzzzӓtA2 gdZL^Wm\s++p aVS/3G\TS &}h̛JpilVgggEr4::2bi2-W{ _ żQ)sBB@4Xa 8!O 3\1:oEF.6UUU4Malq?pXij*rD @~sz#jpTQ+?W2v@ڤ@lvvG:(/ ^!1)3T80*A)Ɍ3CA9;#_z^1n6江kg'GA/V80r!)Q8(!B#rF>S Ða931@zoTMS) 02C@wɌng ]v@ZLP JN^8ޠ4MfaH2d6ɰɰ;%Ia,ӕm Ca{qVk%`4qɓ!؇왦)7=U9~" m9ݲ B͔)IfXR$X!vNٜnm Kʗ0MS===" B B  >TD?X~" zvl"pH6L{L{dsJFdaw(Ǐ|U)v lHn|E?JyJ>xN6æP(pfV CvS!t єUITUULK|pFzoîzlr w VmNzBU)RdvRoo^n LƮwVjRl˒"7 <|aZU)Ρ! M.~" 6 PRy*U`,"XV"U`,"XV"U`,"XV"U`,"XV"U`,"XV"U`,"XV"U`,"XV"U`,"XV"U`,"X͒;wnJcm6͛7v'v@L;ӗ%-]P0. ֮]:UK _u ''xBھ}{zg>MqGI?VXꭷ`}VWWYfR8VZZZtܹt?{+( Fy8ڲeBn*i%z˵~zUTThttTɓ''?.߯W^yEp8fi˖-2 C/Bt{UTT$4esN]|yұWVwws22V\vvk) f Vׯ_/ϧ^zIzoONSwӧ-wҜ9srP488 r0%:ܬm۶ippPeeeJ?0t7vءQ͞=;6M+WԾ}vf>}Z e^KL.auuuU]]~Xx,Y#GD||(ikWGZj{IUVVNGXˬ*..ǧ .X-..uVaҢg.\PV0Tcc>躚_^GΝ ^UVVf[;wT8Vmm֭[b]tIzt%}_s=')2kڵ*//W(6' i7b ۷/| $O˥6B!I7SW{{{4drڲe^~emڴI.\М9sҢz͛7OaS{{{t á>`9?6>G}$)vyfuuu^C<]o޽;'743gjjjsh:k&{<N:p8 .(r%۷O:~t뭷WSSV߿yzIn:y<|>ڵ+^֩SxbIg \ͦu֩A[on?n=n[w:::d6566jÆ jkkեy橽]g֯~ _2 #1T\\>H>o8%%+˥-[D nǝ9vձoJ}ھ}Ag2cU*} 3I.]Reev{45kVtNz"͞=[?|EtR1')2Q~֮]+)u%2<aF5]t֪Xr8@tFz 5::"UVVJ|fEǸ\nM7$)2nҥ:vt=eHDc8oYavkѢEJx>[n <-[6NR}}/^SN\X8Y]]sNzǩּw^׫.:=ҋ/ٶmFFF/j`` { K 2 #zaǣs _~>|8eUmEww<,X]vq:3g''ۭjv͙3g`P2 #:#eSGE7cUvޭuS f\<~~_ihhH^W+VPggh .p8߯nÇo볟V^謝?XfE|> K4|7~z555Ŝ- ojݺuZjݫϧtw>JPpڵڲevN88p8TYYoY:y… or~***d~_khhhk|M4n*5~ٳGwuWRu$$z-1D߶mtnѣGxR~8 Z~" j;7 IDAT,)2z׮]q|sir8֡C!Ijiiу>:44'Vccvޭˬs@ k/~iy\d455M([ee帻%Klܸ1c#0dS*#<{ٳ\Yھ}-[j>äƏmʘD/_:[Ǻn=J !z!S}.d\|ڪo=p|k&|ވJx)l2MsJ].._<͞={ݡ{&֌0BFW9V?vVit:5w\>}:ۥ$m& :@>+kGk׮T8Ν;1i`uuużO5Vsɓ'$رc:vXGFF2~}թ [K9`W:ul$1cH|{v=uH6]shcK&G?P8n3gr͹9ُ?TYl6fͻI%哖_sԁ//AJVR.$`wio}I+zT?ءKyk;_?7G/d C[2M ]Pu"ye5eӿZuϗUZYKPTͶ<WIiͽ**j Ӛ&oy? _ꋻ/oyu$5M0,ѼT>^xc Y͞4R@A}?j[2M.4g IҜE+뻨oGΟt 7R?#{Y4z'`R5Z[-c GqE?Ϳ/xnjU .9 )Ւ5 (Ir?ѯ$Isܦ~?'ܯȥ?]1l6 MshXK>}_ZUHV8J*fi,6&,/.зpܲ?/I߶+ȝ׎^킛"涉Yq) K 6iD E`*XD E`*XD E`*XD E`*XD E`*XD E`*XD E`*XD E`Q@:kPIIIˈ~@j? ȴ\'`hpp0ddZL^PxVKKKuҥtקlL~i # +++uutth޼y Ç544?*I2Yf\!i7VMiL<2)^@&r/(immշ0eݯRSSӸeRK3V466V ^o^4cft gaB!(pU2p$vhn 75Ye|ru#w\mz*~PXV9GGVOݳ3/Ly\e|>1^֫ B1~ȇ z!sfz/H3)ujԙH2Xj~p+RZ̸kR.u&{K9sRy rnS0腩oMϼܒ3\?dk沧Yg_٧e 6c3f`̸46c'Q+u5g*dL\8腙m*3/d:ǎix|dijk'Tufq|yr|y> U^Lg_"*XL XVّԑv@ UUȑM*wt\E?@a*X7jˑN};?KW3B|t>RE?@zMMM +++;nRς'2VX|zY~t,MˑN=rޟ|oԙOND~?|^x^ $s)uS~Gsf cUjjj,VV*ьUȔootLߵuTǥj&י!f,|Lc^ B|ČU+3V**,ޜ{sJNu\fr||Lc^ *@R`*XD N#[#[2?`#Dw< \E?@ɫϾoJ{\fzz|Lc^ * UT|ldDêMz:,5}e_nO?FO=ty喘˧α),T7#As3]bod0nHm27r<`r}f7.v)@u<,Ǔy5c5x' ~s*Ф5ȅp50̈́}B[LO}#eq_q%?hjj2_XYYq\..5|A;*g|>~ \5Y?䫏ӊSMKB &Jϯ_X\\ql64ҫ`q {Ѵ 3ك 1Pd _:K򔩸2eS}N|k~PUJfl6ȇh υB! zj~w V v9UT㲫 TS@ȑ6X$Ӧ[<*vYeg a5-4vA)ն6mܸ1L ԡCtme, ڑ#mӫ]0e xpGrZ_tjSj{{V\+%Eλ,W@a:s]]0ߦwЮR˞VC È0 y^nIȈ|>LL0t* f # ;MCPD~, 侊FC~ःUϧ몪TUU5!x5MS===I@2 g @n\gUVa4[ ` St:::FUzp8 WMӔfSuun:;;S.ritt4eP.bި8yFNW(BS _YaGydp2vjd( rOJ^Wi*f 2MS^WUUUS9rqV#+K˦~~ǕM k].6)aDR4eep8p8]c5֥0 ߔ~gg! 6IrzeƸcaϵ3Wnn*Sp.3h9jȟv)&8l C)n[Rda.031|ȍ’†I!£ea#ye tU(DTH\_uU^C5Go^e È  EL!ي"lfd |>97 VǮj|>T 3Kd8L E 2!i ,^@|)4#)rP(P(`0`0},p鉆 SnٜeHΠ W SA9\Fdn B~Jyƪ Kn^ Ø0sf>/`2gwht P Q$$3ɜݝZtR*IRUUU4H"Xfr{klUp.MJ5$'$Gc7*ޔU)2sW^Wn[RVca@~1gw>CeYY2 LK*E?44%ra,J] q)߼ *XD E`*XD E`*XD E`*XD E`*XD E`*XD E`*XTP֓O>iy]&18Zv4::VuttۦX[lQ(֭['?"p8}G:yTM2YFfRQQڪ3gYz'L5`u|z饗T^^|Po477jhhHzz{{p84gmذAG}TKvfƍڪwyG`P555-OR.6S V5|mݺUpX:~nFH̙ܹR:& Cox;vL*//ڵkU^^P(M555{ /Hl6?qtwu رc[UUui׮]Yw^?~<;}R9~}nm޼Y]]]Ё~IREE֭[lvSGG,Yj֬YjhhPmmnV\3`Z Be.\В%K$EBuwM*.\ ϧjuwwӡCtaV:zvޭ*=C7fiƍڳgN:˗kÆ z7&쫤DeeeObʾ;wWڱcvޭmڴImm{d}w,XƠ Ȓ,h+7FXUlQNJL$u3niutيb#e87Ev$@ V {A`/59<ys@駟 /Ԝ̛7/;ẉ>T*+۷>˫f̘qА$馛ӟŲdI[&I:۷k~^{ _OGGG-ZtOMMMvڕ$y3֞~Z[[ݝJrckFFss#CCC@3 ^Hرcz=7oyy]zL>mӧOѣGښo1=y FMssiʜ9sٙӎАox[OOO244tsO޳<Ҿζfo4/9WISSSzzz.9ߙ`9znˁhѢ ~闒$uuuill̇?Y&Eǚ3gNZ[[s$ǯ|QFqҶ#GyϹ;K/5R<|Y__KuSsBy ٽ{w&fʵ^۷g˖-yGGGMRM7tծgRWW dyח.K,Ir<.]4;vHooo3wᵧ:Ν+"/R344߿?MMMYx3g8O<+WK}}}jkk3o޼̞=z1O=H넎466&.&9~YZL޿5ۿwh`wCPG曳w+PO| J%J%ʓO>;w&9$׮]o=˖-@֯_?|SO=;ٳgIi˪UҒJ~Ïoڴ)sOoeݺuy;ߙ+V$9~_g#7sme婯ϡCO#cJ?ҾG|nݺ\2+VHR͛$O~;3-O<}> ӷnԾo[~E9kێddNɢ[s\9W6jU-=F|ΧoݔYuܘi5+[R3;q.*zEkW:Դ Cmmmg{w>;J>qՆ~{g^x+ǯ؜wq~9}HGkY; Gg>7ՖN$?M')۲<`+_ۑ͹7xZ?#)o]=;_${ҵX޺[ ,on^awd3 OIDAT"|{ȷw\xcScf}I~3kNT/sqnp)9{d_6|ֺ|[$oxP+nn΁]}y;Y 7ԥ6]]򾭹=OYwc_?ᡴΫϝݽ_2a)鷿yiX_ޗgYў\~cSg3Rsw,s<9vt(mOOT*9-J_Pf-G.j!?ߞM`9sqg0Wڙ}\yKR#?{n<`̘U$Itw9>ko[Oi=-ۿw(K+ϭ=#C93_?|?^$`zwdO72tΥi7e̺8VlvرޡݙwmY;M+Vw+cʿlΜ9[3w4447/B6o|?˚5k24446w_jjj#\\?|Yv7{^0=$K7w>'sjrzSm>o~$[VyqU3~m~~Oeo,erٴ]?ih˒;fsȯxq~GKկޗ[Ʀ$նjvg靳rc=jOKmmm=rK|7dYfeʕ5kVy]]]3gΨ?auz{{sȑ\uUyWǏtww_zÇx%U(7:ZJ%?яjժ,^8ӦMKmmm.Q/[nͱcǪc˒%KҥKcǎ s(ٳ'֭˳r_Bڵksgٲeё1Hj+nlkk;[7nܘիW`$֭K{{Iۖ-[gyfTO<50mܸ1{ex'pZ{:ՙZiVńUB*@!a PHX($V UB*@!a PHX($V UB*@!a PHX($V UB*@!a PHX($V UB*@!a PHX($uXmnnNWWŜNՕY5eϖ=\>ۛ9 ^?Ԕ:MyYaKӫNӌӨjkkk8P:c83Ϯ14ͬz>-zvޝ+hlݺ5===Ypf$I?\s'~^k Lemmmg使?wǁ ؕr͢Uca5I/^-[dpp0 ={vf̘Q፺rС5L__xRAc/.d9әzЙZiRxDwKGsI\W2Śc'p骶CuXs^h|NTX *@!a=cDUB{KDf3.,=hjr*@K&>mo>9s>Su՚s|Vn'piCus^H|N5S7m ⏌`c7,C-ݰpd[M9Q\2a`UB{ ƈ45 1MM_Z5Q|^T_Ln5YDp\z9,fć>x[>ˋUbxj=qFj%&K(J.+VK6~K\svα<6YdVD9AM{{{ԍmmm IDATxy\gaP6q E11I\BEV2+eyr.˯KiPv#34DoZ l5wpEEa`f~Ez?}#9y19s8իgvZ B!Q2Ls]xW&ݻ7Vիאi#(6eee B! Rs<%yڴnБ !BzΟM9>PV;6t@B!~JJ*IB!&;;;GUC!B!3 ɞB!Df!B څc._?}`v֭\yWaVD3|h[yV8^Vf,ڡ( yٺc?_d2B.<ɗGغc?R-;aȠ^بw5KyD%Jp.l2vT]Ύc묧؄p"i ?m؎o;2$ZŅKܼf_[F%L~A!?s zWܝA+OuW#ueqV~V@јgNdΞ^4sOut׷;jlxmY:EQpiD;p.;ǩѦ'Xz)kB.5qXRXhUҳ'C)K=|[; Z)iG ʚl\oC)KXÃx;=~=w_!Z>NZGEK/wsmyܗUK unC֜:}u\|{;zv{Ng6IړF( 8;iyj@O4vU_x_شiq›Kڃ?Z${BzRP~IO;+]:V~=ߓtk[e}R#|S nm,-\hڛ(*.FbVa2Xa;zt!kG$f^{i>¥yvH_CyNtk:t?~<5j2JKKkbUK=K3'`ք I*/=.ߙlFӲ%c'+ =jU'øB8mEsQڶVC ftl߆NdZ[ * ٗ).*.~(BG6 \UY5Z;6j5.͜twBs.Sf43EQؠut>FCYk 0͸6g!(ռ ? lѴlIӨDT(|ժgOqר*zvᮝر+ v=q;Z4w ުCi)_E[9:SZZZUյFclfJ5т??֛_FccCHSؠ4sU\JW$Bujwee;OWXDj֙r:̫/ 0zjNw|l% ;ڶiBFEs[Sp()}_(5֐a\!D+.6tKa,+@'> ZV?;Ww-CGNеK*˵vXj$qf3&7 7xc2If3Ʋ2X_YYWR|ZzuG/^Bٿ{uK/Jߨ>7釰x̓[zu} S܇Xޗ(6ٔ@~;;:vh>C^.үômUeOZ͓#Ė$NEh܅'G 2Ѭ|UZmCTj];w O`~S"EEEѩCnMGi>my"l ʿQvcUݲvcI{;ۆG!CqXC͋*(,,BP4}=a\!Bk:ڃC}2eSڷfG:)ɞB!}ĬbxOV-l 'Bфɜ=!B&L=!B&L=!B&L=!B&L=!B&Lٳ<+BDIϞB!D&ɞB!D&ɞB!D&ɞB!D&ɞB!D&ɞB!D&ɞB!D&ɞB!D&ɞB!DfS ZMht `61e! Ufs666pҥ{wV EQ(--mpB{dOVs5|gqW~/>>> BqȜ=!B&L=!B&L=!B&^Q83Ƨbr%}KevN :]_Q!~VUϤɋJ1 Pk)L7.<m|ݽbB!ɞoNwWaA6tc\' Hx,ͻ";qm3dnpjoeU'}K?AMCp{?*=^_9[9PVpN!HtڵkٰaC߿bccC߾}Q)~LUQC69`,*l2:}q^@m cQ~K_Cέ%4N x3T;-ǾIT{:<$eS;֥?-Z9F!hHcǎɓ888гgOfϞW͛i&4ѡC.\P/u-ϟ^L!+gϞpvUu}-+3L桉+:q:^_|||())iP7o;'O `ҥ >N3gݝΝٳg:u*Fڵk2g^yڷoF!55wyN:pBZf޽ 8Z͖-[ b޼yR\\N7$++Blٲ_~^zQ\\̲e)Bz5kիQT,^RK,aϞ=|w<7`ӦMv1}tڴi``ժUDGGJDD;wdԨQDEE?0aBCCŋL8QFN={A<{{<%&7# XC" ŋx{{J$s۷o{*;}4f#Ghݺu1 >| BCCrAL&Ɲ@׮]i޼9*eK-0ulfڵfaȐ!2QQQ L&8::닢(ܕGԤz.K`l5t(BѠݹtm9=QT 6 gggzOE5z+Ʉ`@Qj'99~qYrrr8|8ѣٸqcӓ2ߏ^ťhHMjWRdVqn뺯 Ʉd̙3|'5󊊊 cڴiL<(6gΜJ?k׮[/r^ҥKݻlz=+Voa„ 2o< yHIIt|f͚7eeeۖojM8p-ZpJ &My)))!""*{}||7ohZ6nÇk*5%BTd2vzKXƆJ^Q;M&S)BpcnBXad:uTᘏ-ɩkLRU/YRE=!w͝v񜽬,y j,oyD~bB!;N,T*LBll, ^ ak}*_~% ޽O>kƌc)׼ysRRRh4oߞ(غu+>,=IIIkj5OYfwB!^޽;O=?8W^ɓYl[njƛL&ϟɓ'e۶m 2M61|pVZc=fYsŊ|DEEfΜ9N>wB!^^)((@燢('0͔Nv툋#88СC_ٳ'Pl|=B!H5;vlذG}TL8XvM~P呜̠Aprr{رooo233+LfcB!hl}uֱa̲ex̼ c=O?+qF hdΝs%Zn]a5VZYz*B!zspp,{nt:nnn@›۷uU[PXXN˹͛7¨Q_8pq :Сall4h4lll|i4®dĉW؂P!Q'{B4J (xţ-_XZӡJJھ cު*tBxx8̟?":w\wCA.!#dO{LA_EcB1o9srHyP}>Ea֬Yi&tڵciUK.,\вZf޽ 8Zʕ+III!88V\ʕ+7oz"00^z7|,|AfΜݽ>S6nh}ƌZfԩs+WWWV\ɑ#Gٳ'zḲ>Jbر؀̙3qww'''srY_B{QٻOn0(((qxMs8C2F }~.UMhݺ5k5 l߾#FƄ  5VZvZFɛo믿Nf͘={6{eҥ5,4 }֭'`ʕCBB!!!ϧwtGGGBBB1b_f Æ c̙3xF_MXXX֦p1b ̚5T!:'{DFFHzz:?n&D(߇qRA&}C{ۇQo_li~d2Uy[nf֮]l&++ bU̇h4ra\qpp &&2;k!11>}VҥKXћpAL&w隸ݻwc2سg>>>VSQTضoNe!}Nø `̟?z|Zn݊O&EQPK}, [{gT}? MesˋsU8w9:vXfsQs*Κb&''@HJJ>}p)9zhYqqq7>ךbMX{Ъ^:Sռ{L2}dffZ{Ņ 6mCrr2}ёH"##k">>o|||akk-C2B,C7JZז\oBsø5'{?-[T:Odۛ/b6qpp6Q+W^e]5cBBB,sy$!!^PSl5˜:uc޵؄>X=FqFÊ+X|9#..[ηmۖ(K/ԩSquu@ 0`_5&Jhh(=zzHJtg8ejKf3cԩ<hZj5ÇGQ|||=z47nŋЩS'lmmwCQQm۶ERVIMMޞaÆjqvv1 gΜ!;;|InӶNMԦ 8p PwۺuB=,gΜIzzHPP*͛%88TF#iiizZliu_|',8k> !{w󫬴ұIzz:*Y-[0}tZl``ҤI/!&&p"""ի,]>h BvvUm+k֬[n0c Yd z}15pQٳg-)) OOO8PϹ.j6رcY~=111w5V!JϞ=+UZ?LJXd9|0 2[fn_FZ'''/ҩS:]Vy9r$͚5ٙHVZUz59PJǚK;}a!vj#{BRakk[i\c$h\VVɕ IDATʕ+ 8m۶UY&77Ǐ3bĈJܪ 2{~ׯ3gk!Dj(]u[iqttp#?&%EMMB?qM&o6>CZM``LJJ :(~fxPXXNJJJ0xzzZ,!2#..}}@;\\QVflB4zuZz%11ERqq&NHQQfW^yw}3go>NZczN׭Y@mFNNz~z}YΜ9S qיL&zeޗBqY=gO! !N~4҄B!DE !B4a !B4a !B4a !B4a !B4adƆPB!hk׎O?;wFLL  נ"##T~'zeյSLaРA5aѢE899kBh4r5r.^u5FAC!ݺu/dɒ%̞=Bz! - Z_ͧ~ʤIt<\xѪ9w\DzxG-f!0888سRf0puuW! !hbS{< رcMf̀kڵ$&&zjmѢ|w\r[a '$$ӧopss#!!?]vʄ , _%99+W}v}QT*SL!66V^MrgT\RFM/JJBwpuueԂ{.7 !`uײeKڵkdž |FÊ+X|9#..WY677C=T&OLRR .$44ӧOWyVm۶%**K/1uT\]]cٲeDFFһwo>lmm޽;O=>, ,,LۄUT*X?IHH`۶mX=z!h Nڵk1LuIPP*͛7Kpp0jRY /@||<˖-cڵꪋTF#iiizZlIpp0*SVVơC~:hZPESEU{u*O>;v0zh ҥK|]oBΝe˖(lMpuue۶mcϟNW`ocȐ!̟?:UWfbEÃ'NTcǎ;Ε+WX`;wOekӼys\\\`0pJO?9W>}:mڴ`0j*Oٸq#6m`ƌkԩSy+GR1aBCCŋL8ш3gݝΝٳgj>z,eߏ`ddd ӧ1 9B֭-DEEa00LնA!:=>zAyMԩSqtt̙3|G8qEj* ILL@y+2sL<:uj{888ϋ/H-gƍ,YĪ6l@DDfƌ$''W:v [ZZʸqX`aaa`gg@֭ GբjYniiiu8\<`V͞=hV\Yd`0( ^^^;wBsر#H@@IIIѧON:#GTɓ'Yp!s%//?}in!;;{{0~xԩg׮]$9}4-Z@R/2l0qrr>>*x{{[ֵܱc!!!t҅ R\\?Off& >i&bcc߿?~!cƌ˜:ucV*_ӹ ܌3pqq'?yxx0`Ǐ'??iӦx]UmuB4 W\i0D#XnWmɞ3>e~;_IOO54>|8ѣٸq# 4Μ9Cvv6%%%<$$$TY獞2ߏ^Ņ 8p峸1Zӹǖ-[(**unV_z֭[BףprrϯkB4F p-z*N"(({{ < Q7V???~i7oNAAqqq`00i$O/LII dddpQٺu/))'|TYVƍ9|0f0Mɓ!s̡s73,^>8 +???YfYԾ]n:.^X/mյA!#gϞ3u~믣j1̝; 8PrRLVSVVfUYCi)%ZKF/[>BBHgͭ$8 ilm*:H !cdOqʌF\5êQtB!"IL&z޺=!NƵ!44NB!:%{lٲ.*ԩGraXf +}|}2e  񘏏-ɩ+Bq?sϞ=ֶB!ŋy޽;=}m… XVX BRyfbcc 4RSS1iٲ%T*֯_OYY5'Bq?dܹs㏬ZۺW;vwyp6l>jՕm۶Ϸ~qppXne6)..FQ<<<8qe(kB!O?gs[?iƪ&׭[dž }_aeCݻwpss#%%OOO o (}T*nUǬo>lll9r$...U1\vz]vM !#uQVVdɓ,XZnMxx8ZV˺uHKKl6+2sL<):u*6l ""3c +;qU2n8,X@XX999Utfk\RFC :WW{B&FٳgqI;;;yР cΝ #JJJꭾlNs"Z)++Pmʖ-[ :Է~g}Ʈ],!hV mrqqʕ+ hT*[Y[ߟI&@YYgΜaҥaǎ=aÆtR._|כrƎK~1byyy?ܷCߓ{ !Bzɜ9sjF(..nD#ڲi޼9...DGGc0ћ>}:?0^^^|^v1}tڴi``ժUDGG駟qF6m3^ 88SOW׃>̙3&X/_&>>aÆU>WWWV\ɑ#Gٳ'zKlfر???fΜ;999̝;g2oXm8^xMFXXvvvh4"""ؾ};#F ,, &DBB!!!{MΝqtt $$Jh4|G['x+WRZZZgѾ}{F]jVXf Æ c̙3xF_MXXDEE1b5kgf޽,]QFUcyz=F~k׮ >^{#G;H'I'=(߇q6ol6osN/^̊+TrAL&t ڵk1deeÐ!CHLLO>j|}}t)))=Kw111qرJK,}ر???Ο?@߾}^x L&vMqq1wd2gNƐ SXXX)Wɓ,\s璗̾}mB$ٻBBBBu24[9{V'/ڼe u[dٙg}ֲ;_IOO54>|8ѣٸq# 4Μ9Cvv6%%%<[MMMЅVٹʲ#3p@nݺ|QQm۶S[[IYYG[vBUz  ?>on|U||L4 N?l%dܹsۊFz,++Gy^&[]zj+kooO?4͛78"##k`00i$O/LII dddpQٺu.))'|TYgII 3f`?^_W_}d\VZU/ ƴiӘoΜ92o< yHII|||7ohZ6nhBѐACViӪ]ӓ;w`Yf 2b7n׮]㫯">>o|X{=pӦM#33J_|߿0!##޽{Xh}eT*~mlPmnnnUs3JEXXC ޞƌhު>iҤ*?={111$''ӷo_ b޼yhт#Gжm[fϞݻO4RJ n^bGJV(Bzp';hX=Fq\8@dd$=P'O&)) ӧ' 0`_5+V|rG\\'ϟ?PBCCѣC m۶|7Ջŋyfg}ٳjxnս{wz)}Y @XXFzj{uڶmKTT祗^bԩbggDzeˈw|hxZ{;[t_+Va$BN<<<˳9s&閗#f^xx-[ڵk۷U PTl޼(z ,p f3%%%Ӯ];K=;vPTTĎ;0L$&&ҪU+Z5[jCQK/5ގTF#iiizZlIpp0*SVVơC,[ThxeF#.͚@;>Pv4kF$B!s233iժ GG sV | }C a|V WWWmf9vy(**,hڬY33STTTaƱcxwʕ+,X;wXN1͖e&<<<,Ig2 !w^NNW\aF6mڄNc֬YuJrss9~8#Ft3=O?4ׯ_v"fU֭cÆ ѣG- ĢEprrSDEEAjj*?z)S0hР4LXGR({L!$@pP"Bq#R*HM_! "T lAQ!!%$ hcRw BN3̗sH2d~yu1k=y>kȘگر/^ m޼Y5IM0Au}˖-/+00.zUi{LPNN{YZZZVAE|MM8Qyyy/~'OVk;ѣ.;|:IRuUlwkڴiS.]jt[颳?vpif͚_ZhC}v 4Ƚmǎm۶?.,:?Tff>Sw曵b effjӦMzG~Zvvܩ8Wk+hB!!!uYl6ܹ=*kԩSvڕlܹ?ZǏwգG}gꫯl2mٲE/e6kÆ ̉Uy VbbΝ;'ݮݻwåM:U+W͛xZxRRR'$:t݋/~gϞZjUzfϟd^s\|ZdRRRrJ)-TJJ_Ţ,)$$D˖-O?T=qgΜK>Զm[IRkjҥjӦMoyۇKɚ5kҔA饗^Һu딚}PWm۶￯KVo4GoM&IV||yG7nԬY$I>>>_%&&O>z衇sN66lP>}OW^wyG+WT޽dW9N>]viɒ%ˬI&)33SgVurXB}~; .]%K֜9s#I֭z!=#ׯbbbTRRr7&"\uqeTٳ֜9stmUnƍz4eWފӖ-[4|phѣ222S&Iw}:u꤀IRttˌt:pB1B#FP׮]տ(..NׯOj.JΝUPP#F裏>piӦiΝZhFÇ[ ,Њ+4|pedd(66qjZի5d%''kJOO{)&&Fj4.ԕz{Ɨ_~)á[P[nԶmԪU+IXf ԳgOY,CJHHR^^.\ݻKVX!˥hʕ6lN??O?ʱrc)==]K.UBBz^_j믿VIIUPPnA={lVrr[O/*00P۷dވd4n_Ur/oxٳLݻwXwTXXv*>|XIIIuWow٩SԺukL&/VZ:}JX)))Qjj%KԺ8u'IZf֮]Aiҥ:t:TuvSչBrOVWII>skҤI冽+>}ZVWDD;|mݺUъٳUTTG}TRFFF0`맧zJ.\Д)S$IgΜQhhh-w\g̙3ڿƎ[euSyiسgWL&vRfs(""Bv풟z!L&nZo,$5iDM6r5mT?"""d21cƸ#|2UǗ_~)///>+88X﯈9m߾]yyyj֬YFû|d&^PPy? BCC5rH}7U֓-___ 6L&IVU<֯_/I*77W6MFRFFF}lRS&MԾ}{X!!!|||^'O*88Xz|||h\ҌB۶m߿j|۾j:.=zꩧt=쳒.M}iǎk ׮]3%%E?֬Yyٳ$ͦ{N?G)::ΝӢE[o)11Q.Ԏƕ.W;rH^Z]vUaabbb4vX%''+))IOSlPsVUk|*ŕjpj{ql*&],(ȗ|T@#U5W3MٜNati*ʣh<e2(jP@@z `T {FӣG;wNW=Wh<%% nT2.K6՝a)ө7CTƓPƣW~ ``=#a{MTRR<U>~jҤj @˓咿@K3/tmSHHHC0qTd[e6e6S QVVVAh 5@cő=J]:v쨉'sr8:x-Zݻw_K  $!Nhf6]6mԥK9sӸ@34o\JLLԹsd۵{n>|TSjʕڼy~aoQ/VJJ>=7|SCu{oSV*S… 5zh_ZbEc])22R)))EYYY RHH5k,)))I K/u)55U}uo۾}{kڵZtڴiS FCgK&LMϞ=l͙3Gv[6nܨ{LSLQLL|}}8mٲEÇWLLƏ=z(##CnuIh;N 7V:VMjJW֐!Cӧ+==]Ç{ァI,X+VhPlll#  d2gs\zg_7P||zYݻU\\ݻwt*,,L]vRBB\.>$ :rN8cǎ)**±j˟Wxx{z\)??c@cC{j:W^H)*))|M4ܰwӧOjd2CXDDN<)IںuٳgH>: 9rݸq #Ghƍ2%Rj̙3ڿƎ[>#{5yU#(<<\&I9r*Ζ &$ժx@ׯtiJv +ͦQF)##~322twhڴiScv9ɓ ֭*wYNNBCC5`I>֭[ת/h84&Gj77o/jƍZdI}vM8QSNO,_\{:$fL&SNw_pJmڗ/kTτ ]ݸ&WٲeK l:瀰44$IܩwL&ENpL^vRT>}h׮]޽6mZnbGj1;{Wyyy ZfIMZCΑ2$@uY7i:|L=ܣܹSw/TJJbQVV4sLu]zgmJnF-^X)))OJ4MYf)--MIII4h^z%[N۷KG0ϟd^WcƛoYK,QJJV\~U_E.\#G7kL7owv{mi׿T@lݕ.f֯_͛7kܸq۷W||֮]KM67|SCu{oSV*w͛7^PBBRRR4|*5**J}];+@@dI&kUuvQ5 *pZָۚLUMԥKm߾]6lUϴiӴsN-ZH#FÇ8mٲEÇWLLƏ=zHZjիWkȐ!JNNӕÇSLL$tj…1bF]y%i>||I޽*k}UffK_ooo͟?_k֬ѯk-[̽meӪU+%$$G=3jڴ`XBÇWFFbcc%Inɤ[:uR@@$)::Z״iSkњ:u&OvU>>>z7l2 2Do|||ZQЀL&qSvk_*.q_Qh_d-KtU碅~̶mf͚ժƮ]r)!!A.KVRRRy9|m߾]EEEھ}Nv!nw\.v}wjݺuGEEP)))r\ŋk_FFu8p6o\n֭$á^?$U"~JJJw^*""BQQQ2жeuME۶mS^dXԮ];:uJ{qž$}Wvl6>Ծ}tGE5h ЀL97r>S>~AߗTӉh[{Gw}I 0@5e˖:z\.{ѣGաC2mʼ\lָq4dI&׿Uaaa:yd5履~Ҟ={ ~2eJ ? YfUr,+88Snn'N(**J;wVff իW/߿_ڷo_-Z}DDD2 P~SO… 9sjܒܹٳgZVٺ8s߯cǖ~֭VddfϞ"=:t2226}?U4.ЀLW\y{뇯>w׿޽9c;vPTTZh'O*88Xz|||Ӏm۶2ͲX,Ζ &$ժx@ׯѾlRS&MԾ} ǼRvvBBBz(<*77W6MFRFFF}jJt뭷W^ڸqcgY,4m=]U=]n_Q\TfٕT{Qfffe.\з~ҹsh"[JLLna9RWV׮]e5qDw}JJJ҂ kJJ~GYFٳg+J6M=q%%%飏>Rttt(>>^BCCn۶Ο?{;_|Q:m6^|Ehݺu7o \Wرc$=퓟v>▙pڵҾcbb'^_VŚ8q~a%''kܹ 4Խ{2s:~'j-w:,GŲًPҤKvG>>ޕEƎ(M}XXXN/Ks8g?;y~9j/{eǯ0zdZe?꜓\fӥdl?$qkf2z@N'Aѻ``=#a{F00 Y,yy ~ ~ K6MNt)@Zd?00PM4dRqqq4qbX?z 5'O4Fa{F00Ӻukne\BBB呱###k{۾+Z~6oެqIf&LUV)99YK.Y<{4?d=] wGuѷ$]7nOɓ'k5jbbbuVwd͘1C:tВ%K'Nh7o6oެ1c(66VǏt7jĈ*)))S(!!Amڴ;CÇkΜ9ԩ/_2(..NK.պu \iiժbcc5sLuIZfn& 6LGQ`4<, @w>#O{ ш#jݻt:}vir:ڱcVkYYYڻwrrrԿEEEl6ؖ-[ԭ[7Y,v+Vn/7vU.K r\:|4x*kRaaRRRr/^S|*))޽{UXX+ @ڵd҉'j\Ccđ=~_i޽ڿKWJMMՆ Էo_͝;Wcƌё#GmӸtMj߾>3OũgϞ;N<`zQ=iVZI:v쨻K6mRNNBCC5`I aÆd2j뫵mHHB WڧnPhhh>SxxTPP:zÑ=~h͚5駟4oZ9sk޽u:Խ{2J}}}uqO;ZbpTَ~άVk7D T5a{F00R\\@OU\\2Z# (((Hf3_; TӦM=]pM@sGh,{@q\LIF00 ``\ 4 "//v@cae2rܷO~ K6MNt)@Z:~x?00PM4dIhbя?2@~~N<ɓoШ ``=#a{N֭պukOq] QVVGƎTBB֮]{#5{*;;[_}KPKiiiJJJo导Ǝ롪? JKKt9&aڼypOxzռys}d!ԫW/uYKpϟGyDrK###~oX (99Yfr%4h^z%[N۷of̘4]Vw{ykjҥjӦKӿ˖-O?T=:oF-^X)))OJ^xuI'O֪UYj7|SCuo/vٳVZ%٬ &hժUJNNҥKyQ iW_}Ue=]QwGuѷ$]7nOɓ'k5jbbbuVwd͘1C:tВ%K'Nh7o6oެ1c(66VǏ$uY7nԈ#TRRRooo>PBBڴiwyG֜9sԩS'-_\W_FF*ɤ[͚5S@@ tuEÆ ѣuyEDD[;*Ƒ=Cz)hǎ.@y#Fjݻwtj***t:cYRmd۵w^lv-[[nX,VX!^nڵ\.rta%%%iއ+m۶W^X,j׮N:={G{ Pvd2tĉZ|?o}z8qN'xB6MqqqɩUUo>iӦM#5jv%IZ9sk޽Խ{2ǒ}}}uqO;ZjjZY,9*ϙjן.Tuהi\#a{FHqq=] Tqqj4 |"00PM6t5>{@vm pY  r1hp'a{F00 ``=L&O0(1mpSe9N7.뚞BtF00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{F00 ``=#a{Vn+..V```CԻ@{ LGd6sMz Wy ].$)""ASqqNKTh00 ``=#a{F00 ``=#a{L&\.tzjlombjRO+[7p1hە/*((Ҷ 5J=PբE IpEf͚d2D^^Ǧڿs@UTTf͚yZ fp}C_U\#{^9N9ml<޿aԻz?ws qY Vzz˨7#_VUnM:r_6hmrtmo=ԩݫ%K(77&))I111:p$i?~&Lgֺ.@EaSNzs窰P;vT~~~Mg/~_~Y-Ҍ3dXԿ-^X&M_~uU3gԺ{#L&^x-^Xiii~KWk馛͜9Sz7;>C}nI111w}W5ydɓzuرz?٬͛7O۶ms/_vjɥi۶^}U|\GP+\]~jjƍw8Zx~귿:wh\.YVYF?&OzJAAA7}o~gyF{쑗fϞ>H=mۦ^x=VE5^Pm۶̺TC^^^5k9s(--MvzDbZu 9 Ct:eٴo>YVwٻwwߩPaaaUTTT9Nźt"٬[r)##CQQQ2n:uJN̺|+<{jҤI=qGe˖ސl6k̘1{@}eڹ\.l6L&ԴiS\ҽĉ۝‰'Z)00POv/۹s^uzoI=P s:}͛7Y߷o_G&Lŋsϕ>].Μ9-ZYwYKaaaQ-Էo_wS6MC͛7i\ás_T>}뫀W{-XT{Ϝ9~['Ov=(}zgԷo_}Onкu?jժUsUٟfӔ)SciZbzB =zzjw$jٲeW6mt+M2EV^?Pn$ө#G^s ݻ_ŋռysOqMΝ;ryb*j]C S#zW^ڹsgu]UN]VIј\m\/GbDӥԊᨴ޿a+٬b͍& NpWU=Wk:h4ddٮ p8T\\Vخoma "K_&mcM`wOFbu+=`{s1oZZkK_<ߝy(|u?ϸ=@bb 1HL$vexm;<IENDB`linux-show-player-0.5.1/docs/user/source/media/controller_settings_keyboard.png000066400000000000000000000534031323636106000301070ustar00rootroot00000000000000PNG  IHDR{.wsBIT|dtEXtSoftwaregnome-screenshot> IDATxw\S?W dPurmPݽU[ѫjoU Gmu!.j*QD\2EfH 9?A9yW9I$wsU[4o@""""jZgNt=p}D>"""" L@( `əނ+#"""z#vUY$,]/JU,e#"""'333 """CGDDD$b&.&),?`r9Z&(Ai, /#3%ZN05g?RӸ|&Lrݰ!S}jKWo>~פh׍n~*TT琚DsϦxoWH(BDOIE(T* ̠| ־z:B_((,Ʀm`cc 2ܷ+:k]>32r`*7Aν/(aZ8t <]`i0h\} q[|4ǐ %5?`vm[ܘK&ش9RA7(?xցoO4 %\}Tn&6.^ӵ)--^*g<]׍;ecm7z zpym^ -d2Хc'3/EzGRX^yгῂpHj7Qe޽ VcS37g$&]=V %?kK\MNAPURcg/٭}a/v6capwAϮ~y!&8lhZ8; hKPULo`oVY {Lqt_  `me8\KN3F3WXd܇LZWH$(Qj7PR};/ҷk]&C.ݚҁx8/a遲Q>.QAi\c {DTkŰPWfa@#gJKK8| *5:=!"CRRб?QR) ;%%t0I.-4z|%&\хSݺd2zu'G;hmُG >( VU%H$zj P#՗{ 4uiZ-:|Z`Xp`sAX(ѩnym튢"%~ى^pqjRUFD K/]ʾ @޴)ג+\5raץc[LdhJ [#m[;jD[+86{=`*x8צ9BXҷ+ IJU*鸋8s3zzk<ܝպ'4-v6"{(Qѫ[H$X[g?\HK&2lmhyE {D"$[䧥(<r?^ md9k7՚?!VTTk˿/>\ڿ[iUk)̺+nxk)ž!LѪ]OAPZA@\%$]"h4ZiVi|CB*G?ٿkʾl&&ݳ#|ZWƪ%Y;kU}_\nHriP/i WQM3HRuěgp~^qplb7/'L[§M 'PK>,_ .q?/D&"q4q,=_O+IWmQg^qj%Z3б782 rle5+q篠XYȾIΧK<\+\6uvr)n?q[y"n..^JF;.(( JKːuONEB_P*Kh}jui}i4Dΰ0G(7^n>2Mplb%N]o'o"jTx,k !] Pz"L\+.{v3{Dd/vBqXa7Lrҷ<=AP\-QV^]KNA/ ä Zxx9#Vr Z,ߴyƭT,_kk :5F{S'7,8PZZ&xWG9Ao_EyJx{yuOKD;[ѿOn^Xĝ :=J{Y)'r5c&VJ+YX(ճV}4IPl/U_LDDDؔB@9 z5("8 W!hQzBc#"""/^%"""1=""""c#"""1=""""c#"""1=""""WDgDaHDaHDaHDaHDPfee\n2DIRA@VCC6DDt0QjkRD (,,4v9DD$R5^51a|^r RTS""zLr|G"<9a Ϻѳk4%<76kLoCh.x˓{!QTU'^!"""1=""""c#"""1=zbgg8cADDT+̛7{ѣGvZիQLg̘1ݻw=U[={ zDb*huCC_WJCu5eH޾1YPСC?1y0،;~~~DJJ VXSN ڵkaܹP*h۶mDiM T|h4r۶mT*EppooA&L@vhpm,Y5;zh$&&ɓU{ҺrزuVx:VuoW_}ը5zh$%%Uh+2\޽{#>>A~~~}u\k׮-Z0̛7H$1cBBB~c d2Ƣ(((@֭1}t8::"33gƝ;w 4oӦM'j56n܈paڵ8wv Bkbڵ3gw|?~< 26lMY233Ð!CcݰaÆA*4 4ȠqR)~'/9s& Z兌j=I] AC[W˱ݘ6m._v1#TpմX+2LGA 8p mۆXW7G_~m۶a 9A兟vŠ+A HpBDDD`߾}!`kk_#Gľ}{U6""f޽{q >\7-[bؽ{76mڄ>}T[Sm Y0}t <<666^#}] &&Fצ}zkժV^Gww* FoRAE2A) !&&+Z}N8ׯndXhv؁W_} JKK+mG?eggSj>}:N>ŋ#88n;w_ŋ AAA9r$0sLݺrl1}u=Mlٲw}h <k֬Ij&NQF! uT_I}ܼysvr .ԻT\2[~ng̘Q4>L^>}p j?~bOT*$%%ׯ_ϕGBDGGsΐJ7n܀VՍoݺuЫmbb"JKKqE(J8;;JvVEaa!kzxx ==]7q}Z[pqqy|akkpDDD`ٲeȀqqqAjjjSSST PTjHxҥK;Æ k t ;wDYY^A[ YlgϞFh48}4jon V[n!<<ծ,oWݾSsٿf}d*3dG0`ݸq>;-_.==fffP*1b^}UW\۟T*U[APRR_TTTTUe;o}Po}Z[/ÇUP@"Tܸq~a 77 quuEVVٳ HKKCDDV\͛7#33\CGT*oؾ};o;v/Ryuo&MpJgA_sGnn.BBB^]57溊)swww[E0RYaRCUgggܹsvux{{Wz_Aw|y70u{VVVNj/RAob۶mzW3}.''x*MsׯF||yԺml8:: %UdCݻw+zQ(?:OUkm 9sp1m۶(..FVVlmm퍛7oSNqѣH$pwwGjjIHHg4mCԩSkRf͚A*Vy氺?舜AU{V:W I&vTOii)w9](Oggg#??hݺu i $jVC\p}m۶Ø6m6mcɒ%X|G'-J%&M?vBDD>'ZƄ 0x`DDD $$HLL( 6 ۶mCbY֭Cf ZvO bƍ +?W  V+U?diiNNN3oΟ?_oVVVV)PZZX鞫rl15mW7 A7xvvzOm\UΞ= LW_} 6665>wy8::b>?offfpvvFYYn?5h0u>CUh .`ؿ?/A_4W7ӧOǗ_~ ̙3sNoӽݾ!m`ܸq1cLVkb֭UdC6?ƤI0~xXXXXt)իWNBzz:A@qqqj۷ocʔ)z+}\2d|g?xҧf`ʼn'зo mU+t4ēv]6PRR/ , JFMO9sssiPPP(,^V}0|fٳg+ qㆮ_ZѣGøq㐙 333XΟ?Ğ={pc_W}lym}뿪aU[?f̘1}JJJ`$$$@먡.iߩQ4c?$H*-]{{zrY{{{ANNN,g!\G:ekk{~uQR/ƭNU[jzN̞q_;wFnn.n޼Ν; nݪuU? Wuܯb3m~f&ꗓXZZ SLRany=]U޳w-##~-Zh4˶E/x4!r4 WE|m2S?GqM4\W0\^TU#wO7""/^<ygՇNT DDDD1111X_Lrˠ@RA@Vgs {"aeeV_c !QXXhrȈ2 s1r j T*x~f:=+++W=]5@uKDDDD zHDaH^!..Owի^""gaψpL=8p v GGG4`ٲe8~8ftLk̘1ݻw釈3 {QQQضmd2Yrؽ{wa$** 111UVa~4<sŞ={_={=Z/̝:u”)S0qDz!Jxb:t( %K<򪗠\_{?{aff!C`ǎaÆ TZ}>|8P(3f [lqڵâEyATm۶u)///dddԩck޼9͛3fƍ̞=wO?Ǐ֟=v؁wy0{lt%%%Xr%ݫδi Z7"<<R ,@V qy|Bhh(bbb0dlذ6m /ӧ gϞ2988- IDATPJM;+VmOUkggWvڡ{#%%ZԩSB!;;GPP@&!66Ǵi*QF_~033CVVƌzH ˗cĉDqq10bO+'qq |Xd ݫ m/'H0}t 22R73fQpaЅ>}ɓ(--ڵ gF6mdff"447nիW#%%qqq駟p-r_~/:!C r-ʕ+k.xyy}z޽{HHH?z:t .?,Yh*~zZ-,X'N ""`jjP]൰T*gc̙С#77 zDDd:_jW\ӧ1|phZ|G8q_^oo*hZ899zkd Ett4'bԨQV;n݊ 7cǎ5OӧOcƭ[j닢"cӦM5_>Ͼ ȑ#3gBVz>/_kpHR;v pQt 2 ǏGNt hݸqqqPHJJBbb":@l߾ %% ܼy @VҥKarԩ """hp57A0n8`Xf vZ]||Ho[V Z DRm䄬ZJrr2ϟٳgc֭[$"3++Wb̄C ~_PkWW*kuvvƝ;w*;wۻBS߿_aܹV(lӦM .֭[g$%%!''7n~wÇc(++Cll,T* EvNNNHHH@vv6 Htꊬ,}ȑ#SVY{X(++ÇQTT'bڵն{? P(Cdd$ЧOpΝZBDD7VBHH~Z;._\홽4ܽ{޽ 77 7mziސك^{ +WĢET앏˗믿T*Ebb"ѿ{|cЭ[7k.;_{ҽ{w9r 033CPP$ 0tP۷...(..Faa!кu*=<1h %{gggH$8::bذapB˥zT*ѬY3HRd2$$$}>gggdee055E.]h4Cqq1lmmk""ru>gbbƍ PGG+):vٳgcٲex5kBBB`jjh(JkEEE8w4mo&M CY^g 3 gjm_]%%%h4hҤ `kk,Ƣ666O|?v؁ [زe &MSbĉwrssnݺa>1c Boc6aL6 | T*BCC[n;wDVVݻWe* _}͛c"-- zۚux:taaa5. Z]evvvzljœ9sзo_̙3Νɓ1uTL<Z6l@xx8,Y˗>x?yyy3g ,--o>$%%8DDD$N #77Ƒ ǏO?_U9___,ZHwm?B s7oqZ -ZI ܾ}K.ʼn'ЦM]H)))Uo>ĉ0?W\A߾}1|`֬YqFƎaÆa֬Y0a,,,`ii={`>k״iZ_~3f9T SSS%uZme""j:9쥧OT'nnnE}8~xaQ1_ƥ=z9s] #!!ቾ7-=2޽{u?FDDD /X^˖-"zJ]Q]Ya_@pq$"j?FDDDD 111115 vvv3vDD5>>>Xl?hY;w~*3f z`!"zZL]HR,^k̙֬3QXX???;;nnnH$a8::V. Ek`LD1Q`mm{ΐH$pttİap…MHH H憡Cb߾}UT*ѬY3HRd2$$$}>gggdee055E.]h4Cqq1lmmp}@C*Θ7FcP{sssno6PPPC!,,qj5&LiӦO>JBhh(aggw(̙3}Ŝ9sp9L<SNɓjacɒ%X|9*Ü9sP(`ii}!))ɠe oyf=z Cxfxݘ`̙صk8MbܹHOO۷ `ɒ%U3DO 5x'Nĕ+WЯ_?lݺUwl۶-R)N:Ap) >ROg}lӳgO>>xwuK4h8޾mAߙ{ٹp'''Tj/"c`أr~z='Oƕ+W=zԩSH$puuEFFO!mt-_=p cҤIåK`ccnݺ!66rAII RB|2LMM/prr?2=zI={Ԩ8pZ?QRR?:t(VZ+Wwѵõk8#VMDZ޽q̙ qUP՘={6z-\K,A.]ǏǠA|rm۶Bj} +Wb֬YXf>=HP톽=rsskd2Y~Lr5'ovaΜ9.\~)7ջ\]qssխCGue$$())A~~>F۷o=X[[_~c;`ժUСCo޼i9%cqssJ]mQ]sQj1w\$'' DddK#.;;gϞEXX-Z O8+Wđ#Gep}ǜSNC߾}QVVVw|"c={h]~ @R… h޼K"F@ >ǎʕ+}vի;Z jZiNDxf%L?:lll`mm$cEDZƺui&bܹؼy313٣F)00o={bǎ.2DFFoСClc5f {(K"F#F+$  >>^צZT*L&3DİGҶmp}DGGc1vIDH( ~Å sNܾ}}ݻСCܹǜ'2&~ Q=jh_3{DDDD"ưGDDD$b {DDDD"ưGDDD$b {DDDD"ưGDDD$b Z%k}ec=""""c#"""1=""""c#"""1=""""c#"""1=""""k߳GTU1NN /[kr)L[ D ˦ 1Ф+FnѺw0|z?PV [cDD !4m<ڍ\skߌMQ(xJG3cȎߏ5vIDH䠴0n}?tgȽ;KaXBqf2mݛ9sJ #vfot/mi Z 5g%MI!o ݸ].Sgض>A oRط7K+hJqUfȽ(-zNuaZ~=Y#ahHMpQZpeQc!f4i?K>F`^9Y(g #@"K+4ͦ}ސº?l[wEO{Nj˸(= /q{<<ӗ 2"j,&p?n}spu *7 Dpj;t:?iNd=jtʔK>~="2D&s (q} {v.(w]+ s7UnMT[KNι zy:+#Ƣ8GVCyYH;tmdf(κABЖ֫+dl% |S726N'sg>oD=jtS L,laӲ3#T5`s`|}K!zr3Gصڦ ^o~ V(-P/|̛xi& hooGm;LFSc;333?jlKDDPI~*إnnnPT5S|T\˸DDDD"ưGDDD$b {DDDD"ưGDDTGr+ޯG 1XG?LDDDDOgDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDa~pt}IDATHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDaHDa{u}qu;[z=Ji٘a.ʏ*TZh0CD0):Yt&0p Uf]݀Hk[3ʀ^qr߶>x F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0b`={# F0`0[^(i\ǣP(4V'I^WNĉG{qz,Kt 7L& )+T]'XR{# F0~W,˒ݯ.;\.N},5}H]uuuv+''WpՂ:;;z>>g*n233%,PneddpŋrݱW 'lOO222F{ zbHsF}SR$^;t:^~@"  =S(F&#{wVW㭷R~~8[JIIQMMvڸ,+zp TWW(tꫯo~}嗣=5`T{yСC3g}N+,e'бcxxbԲeF{j]T\\jjΝѱoYeeeRsss0~ٳGJNNV дiӢSNBǏWzzl٢={[oow-ө7xC$M:Uk֬ѷ-A{ц 駟JtwkʕZremג}ڍM0A3fPcc>#E\.***TSSzHxcsxO?-}:tHРG}Tjղ,K3gΔeYڱc,ҿ/ԨDUVVI&… Z`|ɸãl{@IIΝbIRjj.\>@YYY:v؀Xo SOMtAuttH5qDǶ+%%-@@C4e?~?N8}{|jjj_ԩS:qbn't@o==i֬YZxB${ҥKU]]gxcXp87_W]]:::tYiŊ='==={iשSdYݫb}ڻw,ˊmZ=nY`0z_CC4yd>|XɺdYRSS&˲q7;ϴE{ճ>+˲tQeffF>X9d=nA>v%˲TWWٳgk…tٍ̺b[III7.|jmmUQQ^z%=ڱc֯_nI;`ڼy󕟟n￯*-_|ȯ sPUU6nܨ7|S---N577ܹs:q$%lP94##CΝW+iɃ>p:31Ozh9]i ˸fcR8y.0;LF0VGtE%%%T"ݧHsFbt* #ĈD"qww$``[JJp8P(.%''|p`#{z ɎUs:r\Ww P9#v'In[n{`aT: " F0`0b`F,Hm ֜N}|r${'eYD"#=˲GbSm# F0`0b`= C3221\Ês%j8 `0b`={# F0XU1ƾco0q F0`0b`={# F0؀ߍ v+ +tHJJ$X;#o(ƾn `0N|>y޸:b2ވ=l* v+33SdY(Ϩ/ۭ 9%''8#JHkơ`0ўƠ^BP񱲎X춾G)˲p8l$tƝXYG,v[`j g:b=l1#;k$)==]F{#usdw(%%E555jjjڵkkq*kꩧx[[n>xsκ=V\[nEpX---ڼyN<}LuuJKKu1IRQQ~gٳgz^ŋyСC3g[nEںu6nܨn|#vjZٳ/7+$-ZH6mҚ5k}ۭުիWTgΜ9]B0FZ@@ڹsԇ~so;w^Ӿ}H&Mm۶?>#ármڴI{gIo꫺vuQWі-[t~0ayUVVJnF*''G~_o>M6MeeeҩSTQQǏ'd-q:*--o555rO>ϙ:u֭[uE { ypM>6a͘1C裏TTTg|ĉK/i՚:uc0zWCCudgg+77W~pX6mc={L3gԂ dYrssc=*++O_=! "(--Mfŋ{Ztl}C… uEWOOOBqI>}Z-Rmmeㅅ*((3<#ϧg}^kZ3g(33󲱳gϪM+Vl;1xz'7(??_ }Ɩ,YCOR$~;UVVj׮]jmm9]i\llSxjnns\CCɑeY馛{  Ú˥l=zT%EO _wqF *((PrrRSSukŊo/=s挶l٢QA`)))ڪ}W۵n:+joo;ݭ~[[nշm?^;uk>LVRaa*++{n=s_W?^6lؠA_/׏~#UUUi۶m{ݭr=zUUU˗KeNO?T?.]*;Z`~i}W1W]]H$zhspq~1:x&Mݻwk޼yCȲet뭷?yutt8v/|=lj#uM`c1cjkku=P7Vy}66xt]wW_&9wuװ9f}\u{`@-Ç5lvY`S~_JJJ6XYG,v[`#{T$Q0HD#XXG,v[`鏏^^m pXPH]]]x<1gub]|4.6DF{q8r8Wv]G,v[Ps qnW#Ӹ# F0`0b`={# F00'0IENDB`linux-show-player-0.5.1/docs/user/source/media/controller_settings_midi.png000066400000000000000000000625561323636106000272420ustar00rootroot00000000000000PNG  IHDR{.wsBIT|dtEXtSoftwaregnome-screenshot> IDATxyX; aeWTTVjTG[kwb֣h{cZRU\Wܭ U(#eIQ !޿y}ygd"`c_hDDDD(T*޽{i{ӸQHHȠDfJK$aC1Tt܅Ħ>M DQjjFX"6t@DDDDTryi:""""&{DDDD&DE< Asrm2??F3E\"5Ap4oޅ=v@{Ƶ0JÆ/\֩,Zv zǤTpV2Z7HT7 8v"ea~!lм' b1/Q0rV[r<ej_gFDO?N]D^~!y# vta@no]:s`.5C/(~Ze8p,zJi5/(i/s |/c'/ Z\JfWj<\ {}t>~<%ZS*x/ R34qGpt/u2XKuR_V;nJ;.{;|uShצ9 $ \ѵSǮ3/EŲ:LDɱz!GGV[ATTTyfj-#,`gyJM{10lGIU\g/m[3 lėG:`Kع/PTpwsư}`eiQ)2%^{Ik `Ǟ?eiaӗD"/cWR2n޾3İyy$9`&k|#D"J -|pv*J ts}٥j"2ғ'+]5-RӀ|4`0kꃲ?z_"i(n&{DTkŰ4 Xk8x,dr:uh^݃ ֒wN\/ƺZj Ry ->J |&"vѵ.'P'{3psqü"&{D&H {тE(ǐ}tsϝ_$AfGDfgk"Yo˺+FH5+)q}Jɞ>,,ѪV-} +;TsD/(R_fզwvG~A1T*b1w>x6Z 쎽OAjf=:u6.:;ڡH}}R]7n03VV`H̀Ҳ&{DTknΐX}^JZxhF&WT/\a 99Wg2c}(piV_۴ˉ(U*צb1nJAxBWmZ+KK4q@Bmtl﫱վmKitDyWyEP "Md"2 &ΐ=`dyx@p`S*MS6.՚DN(-S"a>_?f\uJJMiǣeS7WgXXNrcٺUSdfIo킂bI`ee2dd8H$p2Y J2C(YRăDt%~Fs)pTjpUy&ƫ_Ow^ _ ^ J/_Gek#"8a.⹾3A@q eeJX[Ybs=ꯤdmZiX$o u3M5 [ %5^v_ܺsWn5<\@|c'/"%o/W9k+;L&Cim}uGޯ%vÝilWn_쏘q8fR3tؖ#zJUNUZ/PieiҞ=jU7A]MM "LDDDؔuрz%zQQ'OZ"$99 PڳkjR=""""˸DDDD& cGDDDd˜0&{DDDD& +DDDD&gL=""""dȄ1#"""2aLL=""""dȄ1#"""2aLL fkk Tj0L\.RB l~0#lmmRkPL,--!JQXXhpDx̌J*a,\<9%"z5;do}y|=[[[6t<&ѱ,WGGGCh,}iט_c.GxxȘrcWL=""""dȄ1##8w "")صk95k֠gϞ'wuavZfffٳ'D"CX2c_W Ř|;c^TwVVV8pΜ9_uU-QbCArr2.]SN:J%K`֬Ydh׮]EEZZZEYbbbѣGCTKRDFFB,#,,wwwW_}^zIeO>P*{.~g\pi'L+W 66VǍːu]k ͛7BP/˯ Uoׄ P|cܮHu.\@pp0푟_WU?1o<,Y?C%POJ$!<<سgz˗<ٳgcذaD8hݺ5MdddCJJ@1e4m ׯǶm5kŋ֭fY3gDHH0f|q >֭Æ ߐ,,,0|plݺU=lȑd}jj*4b ,U0}tCήHOO9q2ƶ5}˨QGGGcʔ)vzѣG iyUX+ҏֽ zHšo߾8t DFF"::Æ q Ŋ+c,]>>>"sETTvލXz5}޽βQQQ믱k.:to-[bňƆ ꌩ/}-L6 ضm>|\K.!)) ۷GttX,ٳgŮXk>>>طoޝP*"""֭CXX;jG*b8rgaڵ+/?~!1}tÑ @qq1M6Xw,^ƍA ƎKVOWh׮֮]{b̙pqq888ĉ:1999[n!qy$''w}|ؾ};6mqXŸj~qF`غu+uH &GTT.]q~b5ue,}W''"&&۷oǀ;wݻѧOZ-omf͚aѢEƖ-[uTWKR*o}cѣaoo'NtQSӸVZay&?6mRobX"hMPVVVСN8}a=z4&O/͛79N" ""k׮СCqQL6 * eee@XX}BR! EEE Æ t͛1l0L4 ~!`ffc׮]:t(y;wNgLtږuuuSU ՟Џ9Ν;C"N`` A-[ A\zeeeHHHL&S_fݺuP(PTq^Aqi[PTxqqܼyS]> H0o.]@,Cܺu *J=kתCW\Aii)^ L777A&aǎPT(,,D~~~1'Z6Xk+-- um6DEE_~Azz:,--nݫ{Z zŪOxE+C9r$/^\>ЩS'XYYa(++Cbb"4+mOŗJĉyaժU֭z|MQ2iyܹ3b1Rk׮!?? hѢ ==}cZW=z~[Fog9'Ο?R'O'ORӧUꊤJ_ԏj~*شilق~aƌغu+VZUX~kmҤ n߾] qf͚@\\\k*ouuĉj26qa2pLnu̓RRR*mV:2U^Atn;...ZGMmvHydAAAիJKKK/H+ : )) j6P;:ujz*׿Z- IO{XTW֖ 9s&GEII ڵkbdffm۶۷ѹsgW\ #G@$ӻK.Æ Ν;/kLf͚A,k=so ''GoyA믿"55vrrrФIjmWG5m?b(,,+W5}jtBSUʪtTտ;wĞ={зo_̛70ue '9CSm,xyyU:Q]-IVm/UqXffzޫ.[]㲳"GGGGGGc ży0j(C߳ׯ_?={r\=,66:ut * m۶EϞ=o>.^N}) χ5Zn]VO{8::w133Θ^RSLСCamm HhiiiJ*$R,V}\|ƍChh("##qAL2x~g,^X}2 }|MرQQQxwBO>CETT"""0|\rcbb0rHDFF"00Pc]cʕXv-5kײ{>#//O=|箫;wcX[[ݻXh˗NBZZA@qqqĪwbǕ?Dׯwީv0nܸj>|X[Xqq۷xM߲V?,]T-K}<L4>PRR?s'|{ڶQSmڴ ggg && .Uܽ{7fϞ~믿 u:.]ۯB 7`ҤIȀG,gϞ kkk`Ν|^˿r޷בz9885K|Wm[jgʞOԭ.] 77oF.]`aa;wr溪\ik۩=xj>Q\]]1c ؠ 'OL&~jyvs;w עEFl[h"ƣ1mƌQ?\^ؤz?GqRUUr= gSFDDuEq[=5"" Nh """"j0&{DDDD& cGDDDd˜0&{DDDD& PڲT*X1r(J( UDɞ-T*,,,`ii TBCCDDDF.JRU,r9xv4z˸d<&=}xy13!>4td$xy۸D&H.#??)cGDDDd˜0&{s: """2a ߿[lD"4\*bǎعsgO<Ǐȑ#X|9_~]pY={|8q"zDu GGG={]v-zt5= >Ұ^z "Hto&Xz5K+j+Wę3gǜ9spqqy: %&&Rrs!::Ώ&~~~_pQ:t+WD.]ꥭ &I_Wkdo`mm O;˖-2 1~x,/l۶ yyyP(|2nݺh޼9~ܹ[nň#<:c?ݻqa3g"$$&MBTT5kGGGYƍÞ={k鬿"X'bرc-[3y bΝ1yd|i6X,Ƃ `Ĉ2d~gdggK{u7DDm IDATdʉoŋc̘1U'^,Z˹8^*bX~=lقMbHNN͛7p̜9ڵʕ+}vL>sQr`(++r:tak!77(++kT*7mf´i*ӺukL6 ~,\ǎÖ-[<Վm۶aԨQo!!!())ҥKk.)SYfP(XnmXsUV033åK0c b8q~ 6l@WWW;w...ڼ;;;[lBJG/ѩS'cҥشixwUV2e QXX%K ++ ?# H$8s BCCZ=)))?~>>HOOךBlٲ 99QQQ4h:z*G L&󵶹n:;w~(**5Zl>%f}~K8;;cƌسgΜ9WO8|0x >#22"66VPEEEoA۶mtR### ,uf͚aŊHNNٳg;w@*"** 8{,~ fff%Kc_yA||EVV.\3f@$!>>...߿zC{:t(8P R%vڡ{ؿ?aaa_|鉑#GbǎpwwGqq1 `cc֭[kC y믿777.5 ϠU<æ+^mZ&yDŸx" A Jt888֭[caa777!..EEEppp=""5Qppp#rssk999!==;{7nQ%6lΝ;~zcڴi/Μ;wӧOWOb t }Aqq1k.Tiѣ6m <<())ݻw_}:v숌 ƍؼy3?:TB{`ooy!66R9777L4 ]v3  B.ɩZOƨQ5^M@߾}1{l믿p8;;CRa͚5ؼy3}](**©Sн{w9y!fϞ kkk`Ν3gN9OOOnDDO my=koرҥ >kX,9JJJۛ(SB[^fGPhvؿA.*Jdo1bgB*񲵵E^0c FDDd왐J{9\pyyy =>&{&uVlݺA"""'dτ*Udτl!rrr2tDD LLADDdC{ """"=""""dȄ1#"""2aLL=""""dȄ1#"""2aLL=""""dȄ1#"""2aL$߿[lD"4\*bǎعsgO<Ǐȑ#X|9_DD "+>|8nݪK/A$7DRRӧ{}LDTxfLŋ`kkcbٲe^&!669Ə[[ A0#"81c 66nҫdeeϯ>$"jPLȤ,^FBǎ1b,ȲGGGDIIKKCtt4/^۷#++qqqyi AD&gŊHMM޽{kmuQcGD&'??7o{:sss믿Ʋeː_5Kbq? "R٠Z * * w… '3*RTWk 5N\rb[U*cHDDDdZD"bq>>i^ _DL􈈈_C+o0&{DDDD& cGDDDd˜0&{DDDD& 3KH$033tR(DAWʪ ˑePbcc[[[D":5+HCCADDD"dff&XՙufHMMàFB4C&{a`r/ 5vFӍdLc1b#_h&H/r9ML݂ Dii_7n2<2h۶-M6!))СQ#rJO?BP裏;vXkD?>C%իqe4U &MI&>å'dɞJ裏/[ ?#n޼i_ZnplܸK,AII |}}Q\\7CϣQLaj"HK/af5kbYApp0СCؠi ;C:`쫯B\\bbb<|h"|X`;N:;:h޼9ƏgggdggcѢEHOO7P-D"L0WƱcï_qơ}pqqM{nD"L6 M6]ğ;۷oǶm`oou~d*V^>ǏGrr2xBh߿}Ю];1}7LH^w^r#дiS@QQ6n܈^zUX|9F@{iCh=S$K08q{ƾ}xzzb֬YHKKܹs1uT;w9O>ڶm~ RR3fƍqXYYA$!..Z( ZS{}Nm(ғ3TPu4N> @NRp.]B7nڵk Av N:bO}{qwwGvv6T*yz*JKKqU(J899A@RA.͛pwwWO2_dpqq9~ؗCml{{{ 8pAAw^T*bݻcL@(--ř3gкuk۫sII :J"j]'m#Sy3=4+((nݺ!..~~~`iiYlJJ ѤIaɒ%qٰF.++ ...ةJBYYD"b1Fg}ƭ[M#BՏ)--ŢE0m49s8dddTshѢƺ1}APPz љݻ#&&ѻN}M=KkU?Ƣ{(--E||}4:uP8qRt-[DΝqq\~޽筡cxJ… 1n8VVVhӦ |||tc&M PTTkkk4o\r^8]H.W|X_<<\Сzصk`nnxt`~q!xyybS1ҽ{w-!.\'3@݋פI}?˘홽]^^/4wEӦM!͛,~> !! ;:u*~m_yyyZ;x ڶmK"''[Dk?*))Add$ΝiӦQuX|yr | ƏQFAP`ʕHLL;v _|BBBpB$$$pcz] Baa!ѫW/ڵ }Əw} `طo_>CW**@# :99!77Ɖk[N"f,,,˛#F3g2چ 0tPGf\rssd0`!}Pms///m~y^%O=\DDDDeqw}FΝ1J7TK2!}>_3dI=j>|V֫W/EdܞBa!}Pcgo&"""zrgȄ1#"""2aLL=""""dȄ1#"""2aLL=""""dH}߿àFfȐ!ɓ'o> UXZZb8z(v}S( ֭Õ+W["$$D=IM<3t?xdCP:,2"ȬY6l+$CL @"`Xh>SNTycm!  NNNͭqږH$P*50IsrrBTTΞ=={K,%KZ·~-Z@.cظq#"""0h ??cǎ۷HKK×_~wvj7TW"$$IU#RBn (++ٳ )57778q? j:6ݽ{+Zn sss;w}j*=z ~Wj<"///vKm'x5k ֭ó>1c`pttT*ʕ+>}=zO?Ell,fϞPܾ}[]~ӧ8YfؾvtG[w!::iT#11*>>Rvv6Ο?%Kcǎk:6T*̚5  E.]0h @PPзo_^Z05xҥK(..'֭ }66n܈aÆik׮ؿ? &&ݺuD"vm5^ n݊#F ..>BOM}I&(((T.??NNN x뭷pQ,[ [lAϞ=k͛r/_F~W( T j{ ())H$޽ A*zrr25NGGG>|X=,55VVV(,,ڦ>T7oF˖-b H$> ;;JEIP(vZlذ ¬YqFH$|xaoo;;;$$$4pT&{D"u"lܸqaaaAp-ٳGzU#GE۶mann_+((TYYكBkM bԨQ7|Rm2?x!ѣGc۶mbj b/^yH$tq!!0`HR@ӦMѿ822Hff&9I&A"e˖xyV)booc"nnnxqu&///vvvFa03\cbȑ8z(VXYfŋhk8ptb{xq1=z&MR˗FUc;d:,,,ЫW/߿X~=eHGN ///g[Q*M3t(Ԉd߅Őem^1tXdDtY$]3Qh88Z2䫸[ܹİlV#m<>5FLTQuX5ij00a9,=}q/.~^aGR48֞m$TJ\:c ,,=ex|j|գ8&c,)B!l$adY!Zs<{F'` Eאf²7ڽ=Vn-jC[;5GkPMBU*R^dtk6l{W0B2҂«F=ZO3q{=dT%h1_=PTbWdw"{(H uH,lpyh:h۸8{8ZEh,\³kUY)./x _]KdtjҏoYa< 'TJCDF}F4W~ ?!zIjsqjb3sصCnȾ*$A, @+Hm)jT0E[=̲r̬6W;˳k*3o· T"#>Bo,}!qdx=[|=x"?>B'-;J@J߃nj4o;d"D"X{­P$_!>B5Ip: fVvdO<7v-5˸ *E OJ :xx Fq- DŽ*ȲB5(+ǽC!ML[ V2UO9Ȳ S (ܺ5=xf@R ~0 ẇXj ߗoP ǟ'!R mB񣵸!ItGP&ǩ)Tkh>hy*#qR@$׳oHCFF>ryېeBYQ f[+~SCQ*d(w Di}Yí[Z8Y]FoP~]&>ç(88 ZNNNͭqږH$P*kiiih~ƲDDDdJ  ={yyA.8Mm'xȄ1#"""2aLL="""R[~=cdȄ1#"""2aF__&"""3{DDDD& cGDDDd˜0&{DDDD& cGDDDd˜k΃(f2cB&!HDpn-e!.Tm!B<ӵf͚Ν;kj۶Gmj:uԩS~ɓ={;D-X@_}nV>P^twt7Զm$I]wLkF~_+VPvvΝ/R;C&LЄ }mr5{vcjSVԭ[7ܹS}r4{leggŋU^^^<=iYӅG#GT~~r4g-[LFҖ-[~nZZVZx@k֬џ'ꡇҢE4i$Y^Oż^xAum)''Gu+''f2gٳ Wր}v~m۶MJLLԹsԣGBkϲ,yPy x_|Qe{r:͕eYڼyx 9`Ϟ=;vhڹs$i׮]]g4j|x'*:t|>^~e=O4gJR\~T3fV\L76m>effj޼yzw/I*..V^^Ξ=ǏKR^/G޽/IФ$={'u:]|ZTT6mԺt:uUW^29sFUc j\}.=4K`ڣv5~Ɉ=-&=^TTC``0X>Hml{NS@@N'DF(q~@$նi ڻq F  *))Qttt˱ u4[ٓx`NS.N?`Ϲl{vvzZ;LeӸ,b`={O:Y%׫P(L\.9:***J.v;v{nrG{ U\\{r?^0Q\\@S'V v,Ib͋VRR˫=rs]0UZZZS[:+++SRRRSAzdlud/ u@$8NТ*$ND`0[]vr4d9%%%ѣM=$HKKĉյkWy<}ZpvՠM0A/"}hĉZtZuE+VnTTrssD-X@|pcjAB!9kI99%''+٫ϙɌ)--M .SPPP($ϧÇ+---j:tۧAw?KҥK ս{w9Nʲ,m޼Y={lSV~~cǎz?O>r:Z~$2~~سg˵w^(55U{$-ZHeѣZt~6xlU}mbbn jxub#{0ˉ'Ծ}jot:5vX}݊Z)I*((P6m"2v)!!AK. ?VXXyވ٫۫u֕~(رcjժU$'˲TVV&á:__}uVWZ>pLi~p8xO.]8ĉ:uꔆ ROkРAz'UTTg}Vҥc_СCU> !^ſpiTyXN_#G^2/ ?k W_Ǐ{l]7`ofbbbdYnwHeS(ҬY4e{jT׮]u+%%E%%%*..QN*EYjj,M7ݤ~iƍ,Kkp8t:uI%$$覛nV^離[WrrT>f2o4ϧrHJJJԩS'9NEEE)//O)))1b_NC{nhԨQr8kGVۺr#˥۫|b9p&Mt-^X֭ӴiӔիWܹsZbΝ3gTzɓl2͘1C3g _a=Zdw﮳g;C˗k5^CQZZ 3F+WTffƍw9 0HIIϟO?T;vTvvƌ 6W^*))ф 裏jڴiz2ϧGyD>6mڤ>HfR^^$5jlhi uN9z}ɷIII:{lOrQQQu:YTTkмnZ֭Sz(03g_hNK:k5˸fT>@{%b=^TT˫'Zgφ?h`0>@fs: r:9 2BPx6x< M=JlUU111||\Q*nPRRLxøIt > t:rJLİ,( 3Ş$n,۝rH={# F0`+{J9+WʡP",˺bqEbϲ,+R F0`0b`={kM"1\gFj 8 `0b`={# F0X7U1*cO0q F0`0b`={# F0X v+* 5p*$ֺ:v۾B6vyn 9߯b)>>eձg<")/ۭdIeYM>^@e;cm<6eY폄9\:v۾s1bkt1S:v>bsDԇ)Q;oIRbb6møLn`c51Qvvvޭ_|1bu9Rմδ4M8Q]v j׮]GMckvoQ&LP.] _'N/SѣCדO>ӧO7x\מ={tm)..NEEEy+{]tٳpB͛7Oܹ^;5z9c[oիw}W3gTTT kʔ)*((?b[4ydM:U?ST6l5kHŋaWW]u>cYyz׵uV}駒֭[kѢE~W~p(##CÏ8p@/?ukcǎrڿ׿hڷozVZiɒ%Zx$cǎ:uRSSzGi֭ԩOmɓ={;mԩSoj˖-dzvSOi镞suiƌ1cF(_#>NZRnݴsN}g:th ոq/kɺjW0`v!_xڵSZZ6lP`0?oYeYJKK'|ѣGkzG/˥sjݺuz衇4i$riΜ9ZlF-[(##Z7tRRRԶm[mٲyk׮U>}rOJJ믿kݍυ=l ۷k۶mڵ+ݻu!۷O <˲eSqqq,K#Ciii*,,T0v B|:|¯@ TڵS=TVVk* ?^{r:͕eYڼyzYuT BzzJII ^Ç5bĈ=i\ᑙIDATÇo߾6l$)66VvXAAڴiSy?ݫ;S[n7߬^z)p o߾;N;Vw}xtȑK,K>O#|bڵSBB.]~P111ݦ m۶Un_LL<N:~lΝ={^}U=cz7"2b*Խ{ws=.ʪڵkCk^NNr?QYYYDĉ:uꔆ K槧kРAz'UTTg}Һ.^eY駟|ɼӧO@Ǐuc]UǏׯvQiވ#gϞJ_˗+ ŋvZڴi>ٷo_1S = 4k,M2E{bcct:յkW]JIIQIIxԩSJu[}uk\.ڵk+999|)ዷ;R 5o< 4h_?~zJ8O?i>}z Ђ >\۶mXQQc-\P^{Ν;O>ھ}eٖhҤIJOOŋn:M6MZzΝ;+Vhܹ:sL|z?AZhN*##Ccƌʕ+qI%I_~{9Ԓ%K4p@=o}^VVB|Fѻwo~-XQQQդuZn_yU;vn=5.wW9a'v۾s1rYu״6*ݺuSNNNg5Wv>bkxN0`0b`={# F0`0b`[#IENDB`linux-show-player-0.5.1/docs/user/source/media/gst_edit_pipeline.png000066400000000000000000000406741323636106000256210ustar00rootroot00000000000000PNG  IHDR+ UsBIT|dtEXtSoftwaregnome-screenshot> IDATx{\Te?ϙ #rQJ^PY/ rMVRWjuՌm۬(+V753ۚ0R[^ h7$%er~cV3s_/^9sΜG1/Zӫg CX,˗ӌ9H"""0]?"""jL. 4qp3݃?$w͈KJ%%2ܭ+DDDDmSc0e s"""Rd] """7":(ڻDDD#AW en. : SH#CtWe|NX**,~=bpmS~@P/苳_ qOسu[U'<@wBjHM܄9LRP8 `hx USiQ#um*gBn~P"3(|hڷwIE** ?Cz)^]7"j=ʼnpݘM\O p-P=tC3s50oެDE Ξ|^Y}e^yu_QkViЉIw u Z|/џ#ȿE()d2cWeGb7obמQ-0aL)j&$~}TjƎ7:슄a*|߄{L/~(sD͸ǚ-NdgչB$tx`v/=!Fo:Hϩ'"ݕ?o ,䩟Ppzk&Ǣ Sջ3{;>D ŀtl۱^xUcGEeeVo |5'q`_Sg(CC`zEʀ\g\q4#}z|{K~d~ zw{tE,Z aAl]I NJ<ا;Μ7TW A`pwnzW(rttoW/vsuls;Fy \CP0xϖF@'jz@f'k[Mt/5F#VV:7WF-_T* b3BO'TFiiNj7E]j7t?=So(hHS] r? ގ57!|>-YDW N"0WU7R!["L6FM@opsd4XY(ۧ eOhXgyHH.^.ֿ~o"6U*q%fCIIIX`zLݎ;;v`6K=Ž;pxyy!44* Ç^G>} 4ղX,0 P(Ciiu8P܊+ ˗/>Cjjj޽{1jԨ:O߫wyEoJJJ/Ɔ PYY,rjXlƎ8tCDDb6a4QZZ*w!<<ܦ_|{w5 =ʯQ\ZM0OQ1Љ$NDD$E9Ixbb$3Ty6tQ/B&/.Z3t`)9fm5A G e,UXr+lDDDx'"":0Љ$NDD$ t""" r gf%Q!wgy/T(0 v횭&$Z wwwiwֱg\.ofb$S]uߝ BDDD0Љ$FAqqq{W辠j1zC=11Z .|*+V@NN;m۶1(lݺ&L:tOFaa穧jrwM{OŠ+s`&L3Q"rGy0|TVV",, ]v߿ߺl(**jZT8 =-- {F޽FJ=zЧO_سgbccL9'Q\\ OOOh4dffb8|02220qD|GA^^Ǝk7$$BRRziO!>|شiQSS#GXޚ~hx޽٘6muݶm0gᥗ^xd7oRSS$(F7׆ ?8ۇ&?w}_}Ap{4s…MY&!11BaL?S梠/Xaaaj8p-[___>H!{n.8q"Z-DQRڵkH̘1[u=z`ݺu͛d!22_|ϟYHOOGBBMLt @Ϟ=j\\oi?///aĉxW#880`TVVb믛 ԩSѣGcܹ0L.o wĉsf?%KO<DGG[X- h <Ǐ~ׯGTT^x Jk֬M0bZΓڏj@nCEcӦMx'[}l6caXnݺ L4@jj* \nz쉒X,׷EMM N8Ç[C +b4+**V AphtyKpCFFFk:U*z=!>> .Ļヒ7o6sAE ?~cAAL&;^ 6 nnnHJJlƩSP^^eɻHakӧPVV3g}_… u^wExwiUUUY兽{Zח:$"nM㆜={]vmp]S.]ׯc…ity[Y&L,oq%!==6r̚5 111􄇇 m/"!|}}QTT` 7yQ@]0qDt:׮]CPPP@nݬ ͭEKٳ￷.?r\\\0uT L>[n`d/\///BR{|Z:"#Qo#>>O?4!1`7ۢGPDEEaǎ nTwuu?f3h]6h4 8P[Nx|㏡Vls`` *++ၐfsa"&&OOf?/:Eҏ?F .… /",X^{ HHH@~~>`ʕؾ};bccxblذBIII1c olPѣG1}t/A\\pysm{w QorAAAXt)j5j5RRRp17\Vam^JJ f.z#55 駟F\\-[h; 0{Eii)ʚ쪫1{l,_qqqxϓGm*&CgD@ Fgh48zhvTx"rrr{Y&AR5u:{wNbLDdg6mj*4b0%ÿ˝@'"":9rnKcDDD@'""zZmYѠwGGG7*L"gWN4zϞ=B|uֹ`ѢE1c4ZΡCpi̙3o}w#Yڟzʮ ŋnN:D3gܹs(,,DQQ9xND@W*طo>zp X~=g6['ł#;;!!!m>guD~=44gΜ ׯAC=X Ftt4l'GX~= / ..^^^P*Xf 6mڄ#F`ժU?: S"66Gܹsa2ZվN:!116m°aÐJ~M!mϟ˗/テRڵkfDFF"==  J=_uFZbj4iRk?޽ڵkH̘1kv&Aн{w9.\h9j@7v֭&'ij8y$RSSK.Yם;w(`0uf*((d±cǠaÆ III08uP ;ZֹR0tP6X~sjKc:u  >>UUU2dd2:th_}jjj-ƍ}h4b޽xGХKfk?)<<~zblڴ O>d駟cOؼy3֟3:v7Qgnܸ>'OFQQ1p@,[ QXXYf!&&@aaaEQDuu5A/ "{Xt)_ 69BmTM-jf͚3gXG-Ugƒ>D1i$5 ܾbDZq&kiDž }E6o[~v9j@?uz!t 555n$t:t ƍôipM|͖QVVPP+%%Zcǎի1q:S6ڵkMǷ-=z/Xgx gϞɓmߖ*#,,:\O"667k]v AAAu֭[g~kK?#}_~}0{lr111HJJjv_777 >#G?@TVVB!!!͖qa"&&n}80˃Nku>_~eZ~~>|}}% Т{?~<~:ߏ!Cǧ㝎9L: ((ӧO֭[[U׶3:*]'&&W^ܹ3\]]g?ocؽ{7***[o5y WWW\v طo>`޽(--EYYYٳ|rŋ ,]jj)))8vXbx饗h"zׯcذa*++w}`[z=V\۷y6<<>(xL6 g|'1c ?2l=vUg͛7QPP'x֭kx'_| ,kj$$$ ??Uum9#!<u ړ={w' 8?I HDDD_[52""{+t""" `I@VѣDDhP\\ jPB?tN>B9s| vw3p{:S36e޼y3f=՝ֳgOX9998vmvO;yNyꩧr9!X~ׯ_or{L>βL&ǧM%=#HNNFvv6&MCbzjtt='l۶an)N 0hZ8p˖-.Z׮]CZZ,oɒ%3gj5BkXtiBBB,$%%YctR5 o222лw&h4ضm̙ L8}rrrcs[:_͕駟"77W>7!T*flڴ #FULPK}@&A.7=5OEۈO? wwwr 0r  Je lF^^t:5G N Aӧc֭vk`dRݟ ՘={6y૯^MJJ ?cǎO?Ebb"wO1cfΜLddd`Z>,1xf'9z(O#==GŇ~ \~/Ɔ w^L4 %%% $&&"##{q1`/gyHLLDBBm҆sROLL z=V\۷&{9M$*!/LJyh;5ݡ/X, s""";0o#""cIHDDD@'"":9B@ttt_]h8Q3DdS3gĹsPXX"9r ŋ7oƌ5&:ܶmЯ_?< """0e\xÆ CEE9bFHH4 Μ9Xt)Fz ݻ7ƍ]vaܸq>Cڵ viQG@'"ݻwȑ#͘6gdggcѢEFqq1:u֭[S}~L<*ϟG7rDtT*a4͛7CV7ߐ!C둜 t=xf~~~ NJt?bm߾of/^lf555mqȝ:2w5ڍ^G>} qh4;ЩS's-ڍVų>t <՘9s&^}Uddd 55S'j!!<<\e* W\eD@ F=;Љ$NDD$ t""" `IHDDD@'"Ij=zt{WaDdsq `˖-hjI'g!"믱b N/wՈ$WDdSƍqu 8pϟp{*|cƴiӬ 99YYYHJJBϞ=[.,, Ze8 wDdSeee8rVZBZZ&NW^y>T*vZYHOOGBB4n͚5شiFUVh4:DlJE<ի#F.775558q>cȐ!dHKKbСM6lܐٌSNܡ&joND6WSSuaƍ?~<i&ZϞ=]^^^ػwu]II \]]\닢"X,bݘL&ܹ:NǏ gϞɓm:QDNCDdSxA{G]=Ž;___L0 >|!%(8:Q!0w\w7nݺ;v>Cuu54 =4CE,\w+t"'o߾7oDTTzuo#"@'j$''N 77v9I7)S >@'r t~;:1ЉDkd2!)) F>(HDVVVjBDvƯ9^-999رc`d2|xwCӡ7nĊ+0|@XXrӳtX翵b@R9wȑ#Xj߹s'qǍ SNEll,Fsd2aΜ9ƢEb(J]k֬Add$ӑ`-GXn"""yf,Yiii_|a u ~~~HOO.99#GDNlC%""0ͰX,dE(l6l6CEx{{CRbص.v tQ###WFrr2Fa]Cs &`׮]ZFpp0A+W=Ɛ!C ɐHMMСC!˭`s9j@E(J… 0]===/ ==j&-- FSO=]v}ٌ1c ٌBZ W^nm6̝;G}/2^}U\~@'""h,틅 bڵOQUUTVV:l۞>p@X|9,X\h\s<#x1w\mӝDDd7 ..+WDZZuɓ'~O>A^T*q |GP/qq <ظq#6lիΝTVV"11ӧ|Mt .\O&aܹϐe]jT*1{luѣ>C|֫uf%}+"==f+W1}tF( DJJ o&fΜ ( ,^v´ik!?? -·~S"++ qqqc5V;׷}ڵ+۹s' Ba-_;pȑ{DDd R`` JKKa6 b`0 Zaa!F#~'TUUaaaΝ;aXJ߿?d2Q@&-p5X,z*++QYY ___kӧ1a49NDDvsU5RL{ƍj5m'" AشiuYii)\\\lۨ;k׮ jeeee… O,Yb0Љ& W ػwoQQQī uʺLQ믿ۻ޺ׯx饗s΍ֱʕ+u κ &eIIIX,? 6`Ν駟\;qȝlacł̙3?8 gϞ^NZF>}CТ(kW('퍨(f]t鐻lƧ~Jx KXtikW|x7WNDDɓ'k!** 6l]0o<~o͛7cŸqF /?ׯLj#PUU8 a=d0ɜSutOC'"? IDATxy\T0, &neJvmQoe23VfeͥE-%QQ7WpA`d~0 C3|39s>Gm۶ݬ~]v`@B!z… zsȈhE۶m{kP8tO!BTC^>(v{fp͆L!B؍zLz9KRw@B!¾rsr !BTMNNN.B!B  ɞB!DP!BQdf޶ QF~Hyaps-Yy._q63 %s8:i*F!uocgpTDP23f/ͅ>]t:='NҠ6 } js߶ p3#OwWj׬ANQ*墈l8:fy$_ ?e)6!Cb}"NP _ySk5n`s.v -&p/ٯ֬l-1qD5'vEHm=oO7o&R'zMGp0ߗy4@6'|Ta3NǾySkTeF8|ܸL^^>Nj+u %ݕ }.@9 VR,g'Gw`(]R8y;M^\[2ĕ8T4m܀mP(˜Qԯ3ɥitnz6KC~E>{;9 Ŗ-ɞf3q8LsqՐYU^^;ac.9<O<Bvw9MCLNki7޹ÐWA$7/ȴ=QH]8jLLaZT*ڷizKq ojύq8j,B0[V{ zO7n@q&p%P#[4@׳l&ԡD2x\44oޏ6MN㯑4kRj~dBJB}뼿nB0 k )KGJ'{s{SV=AEAh9,]MgS^ O7|yrBo[6=\N_H`oέL<=8v2nZ J 8Џ72ēкv>L}3z/=ߋ e5WN[pw|#/s'7mP(pw]P&0&{}qPp׋$ R^aZP79O3YFvݦ%Bͅ,bYYwpwew]hڨ>ΦZLJt?ܽd$W''SW7j7ؽo!v:Gjy(Joj<7k exTYq;j:{Epv&^n&d}̲ܯOv`pp@f|o{tH'Y;6;*[|6'HYZnh< oA~-+v&jѰ~MԻ[F!uY&.ҥc+Bg9vEӆ2pںxG&Wi3L^v6-OB!'YA TY!mB?gK聜B!PYY1Tii|}oB^v\\)$B!2B!D&ɞB!D&ɞB!D&ɞB!D&ɞB!D&ɞB!Dhٲ "BQEə=!B*L=!B*L=!B*L=!B*L=!B*L=!B*L=!B*L=!B*L=!B*̡Bj` !*I(gnnnznܸQޡ pvvFVY!D{AA!ʒZ&;;Vp-9*,frrYI!.x(Iw3칹a0y惎4Zb̼;QO?9]ҝ^ƕJB!HrrrȐ C!BTa !BTa !BTa !X`۷ۣP(;$5.!$NصkWgÆ ,_Je2]Vj*V^}_gZ0sLټy3sΥE6wޡCV6hkO+r >"haΝU>|8O?;"Te~ChHˆ?4N{rNMMgϞ%zR~`ܹ;LBCCIKKŋV&MFF @j-[R$""0n8{R EQ-K.z\c[7>oMrr n^ػw/-[à {Pkܸ1SL믿F裏VȝAZV_>.]Sek֬Y1u֑/oq 0f|}}|2&L4jԈ1cǞ={\v-۷GT2ydիZf7|x|||dŊw^e,_жm[xxx7L0żyطo[F0o<͛@XX1>>>;v kvͰaܹ3NNN\rwyH}\b޿J^Jrlyb9r8-..k{6$*GPݻl2ԩq'6m>`ٲeDEEѷob ׯϜ9sXj?  =+Wd͚5|嗨T*<==:t(k֬aV]r%}6mW^1nݺ̚5(~w:vh5&[JҶcƌaԩX7oÁ8u5"**ʸRd׮]Abŭ`0PV-f̘ATT˗/g5no> &жm[Ndd$5k4=Y+mV=%u1xWxHLLej5SNe…DDD'|b7e"##y駙7oyyyE֣K~ׯ͚5sEKOO|4jԨ|k;$ON~HII1;^AAA,_gy{Çၣ#'Of޼yՋ3f@hh(}孷g1Yx7h4 \]]ڵk[R:u*ϧO>1fz=L:"""h֬:uBӤIlPPK.o߾ >www6m}_gVc*Fj~~~vZ+x]Uc-n=*iӦyfÈ#6l-[3;v?ٳgnOŕ_}~\f^iӦ4R4yT*7oFaʕt:?έ[̮̙3 rss .`we֭L>&Meܹٿ?;w:!66͛iѢJӧ˛?z-{!8|0ZjZ ^Off&d_\xX[+x]Uc-n=͚5`0tRz=gϞeŊgnnO_}~\/^$**YfիWM/\3gr%ԩS&Jd֮]?n1Aeҥxر3b@NNժUٳfԩSL4 &`ڴiSut1c/"ߟYfټ,+Iу=zB׮]Y|ɆTXg<-_ŋ899jEln/[655{աߪkq&IiذϼpYb-iœ9sHMMeݺuE楥qi^~"EPXK.tԉC~XlLlܸ,FyboiiiVvZر#}?mQ`0ʕ+Yx1/_8>ǩSLn0Z:mݺȝAAAb}wV5jۄ:X:[}F-`0PZ5Μ9SG` ڥ{*<ʕ+ƺ۶]vSNe6(֬YCΝ2e /پ>^.]صk999iѣGӧO\\\P(4i҄:upE<==iذ!jVZ˯}DDDիUVYz1L ^ZwAQZsYj9_~UVrJx ?#F_Ovvv2V^͛77$AOoqybcCߟe˖ѬY3lǿo^|EVZw}gA#00Sɟɺu EQC7n`ѢEܺu8`۳޽ۘz\\\pww/rtǖ֧p-^[㿥}gd2{WҴ={Rxꩧ0 QK׮]e܄bnNNNORRYYYxzz^d-[,r6A1sLbbb4>ozܹs_~5k7KyN4iiii|g>}N@RSSZ"##omm^Y cf YgKOd^UV[b@XX7oܹsd@O2Aǖ򥧧'fomXr75:G2շ2*D???F 784}>KAW-qgΜE89926OTl앃T6nƍˤl'CڱdT{駟J~~>FB;OTle~7==Gyt2*ă }H;̃jkײv[EUL >aҎ%#%ŻqYV9ȱQQљ>xfO|”!B!*?IB!0IB!0IB!0IB!0IB!0IB!0IB!0*JRpaw( JGi-?^MɸjON8zwf\q!BQ?S:Hc&3SW*s"#'y"s3~ l !+4jtǠzAϭS{85Kw/oɥ92's=9V8urqqo*'`c=&!BBB9s&l޼sҢEKqʤ^ ro]7F)1b>u'}NFojM~g7w4NhӏgKځhB^G-!J%?seرdffJZZZy&-[͍7}/^'ۊ]A˼NPrC̗ $''xf5Lڵ=z45k$77Eb 7n̤I۷/*$tJbڴilݺ~pB~wիLJ~HPPӠAƌ//_f„ ?A6B's[5jTdZfڴiȑ#6lZ*&MM~?pttdڴiY ޽{QL: A||<|IYTW${B!0 lݺɓ'3w\Znm߬Y3 ˗/`0ʕ+ѣM/\\z=aaahZV^` ++۷oR$..X7oJ** P* !rssYt)+VsΌ;+V0o<pB#\pGyʕ+EɊ+.]3YYY9${globN:66!ٸq#YYY1yq5Q(Ƅz\rF1gMZZf>}_~ua%qBGPK9x ɉ}P( ge͚5\rOOO6lc8;}:/]t@Pl(!D1r7=U*( ;E$ă_??? F-?3zhaܹ_W_}W_}lhӦ  BRq" `FF׳pBVX>ʇ~Hjػw/y/Dem:n K[Jٻw/uֵ[BoT=BB@T%l9s0|"ӿR2) IB؝իyM988гgO:!Baݓ 6Pn]K۷'++{P^=.\Hbb"6mbEiڴ)*3gၷ7L>ݻwG޽/غu+۷o[n|rXd k׶wB!*$'{lܸ^޽`0V;w.ф3d}ڵkWԪUӶm[.]ɓٰa̚5c7{lÉaĉvB!DEU&CDEE{/l;`̙3,^<Œعs':-[jٲe zhժJ 6M֭.-!B< |wԫW`]Ʊcǀp;wΤSbJJ 7.Zmd͛SSSh4dffzB!A${[޽{SfM%\Wl2zPP!\\\\lݒk׮q """J]B!DeUfOXz5}G^8}Ϟ=8;;GF0`+V ''|x"^^^4n''+o>ٳ'pw AC!29c9wΝ3NaL0Çs&NȾ}9s&Vb|7,Z,HMM-q 23fn_QF٧BRp&:++ʍRɩeU*evxB +77]+Nze~Rl\բV1>y%33,-.@NN7o| !Dꊛ b/wLq,o[;tj<+/:{Qx{{P(ϷxNRI'xheeeE```ybTN=QܹwyQLBK a'z Je(U${BQUOBT> aG !hϞmR?3mڴ)<==+zKiӦlܸ}Y:wBۛ3g<8lU}dspp0n[UQU(}EN; )^9t_dd$6l`ƿvѣh4[?[g-fk1|pbbbر#{o|ŊAK:p(\]vq"(WǓXe*3&M"..իWӵkWrSLa-‘#G3gNW $!!СCK]oFeڵk3l0"""ؼy3׿󜝝i۶-ofʔ)&ϭ-l׮]ppp`Ν9f*'r2[gOr2[?!СC͞Qׯi<==-,TVH Ν3;WNHH[lys+aGY}s3 `ѢEi/}OR37މ're|Iׯ΋/ȩSpssclڴɦvJRR?8ܺuD٣~uԉ'App0.3$$ .-Fq̞Z&66ȼ'ru|I}ׯo4iBxx8{erUWoDj4~Ixzz0f/_Κ5k?i\6$$_~HƍgϯWz9s&K.~ ((t2c񄆆gM8veCBBX|9 ,Yx5jDƍՋXZm?aݺu۷W_}դ.\Hbb"6mbݤ4>>ӧݛ/[}vuVl=d}V\Ɉ#ؾ};:J9sppp0w߱m6ϰaÌyGILLdݺutj|JhYdqLEKeYj3k?ydF+pwHÇ3uT2M659˧R8s 6ٶl-ǓO>ԩSt?~H{9تO>,X3E>sK 믿ҥ W.~/'**7n8w;wX:u*:t`Qn][`<&L`۷'::baQ&ɓ'ٰaCiz*6l0(큯iӦDEEĴiӌۭJ={0|6nHN=hc۷oorӼ+.{ZW@"##y:t(888_EYh;880i$y5j.?Ç3~xzIrrj5seلĉ~:޽{vZ2l_;رcqvv6?::p o|6wZ?>m۶eҥL< 6άY;vl0 #++N:o8q";wsδhт=zZp!;v^cԨQxyyȯʊ+'))j|͛7gtԉ#G_lYR'''h<>lڴǏ6e),l-q;vdlӤI4h+W$**~b̮۷w7dŅ%K9rqC/ڵgUEj FcX%L0ǣji׮]ѣM:A/6#oȐ!>} V ___Μ9SK>???<<FCtl߾L-el͛7?ŋxyyѸqcLZӖ[jKs^rX}]T*ugaɒ%(  `Fxxٛz;LoٲVZ|6ӜڶmK^Xn`Oꫯٳ'JEXXqKeYklիRDRz*ԵW^p)RSSᥗ^"&&غkM4qqq]v<:uTݻwhxgk=<<ٹs'jX,LP0~x~'_FrO?_L4eĒj̛7s˨Q4h˗/gٲek ̜9UVϐ!C:t(wԬY=zm62N&''ӿ3g'Nd߾}%z4˖-l޼ٳg4՝;w:t(qqqDGGӵkW3gشiV˲fkt-Z͛ӧV㴥R1lܸiӦ1zhΝ;J~[XƍǰaÌ1Ok2֭[߿zTwB%%%f,Ym%!!C2h رc׿xwj˲QQQ 4ZhQ>pa4 T^;v[wsmPlٲtooo999= ;gԩ'˗/sYx 4 &Le˖ܾ}ɓ'o]vNCpU,XO?;_|aaa\|k׮qQ-Z޽{{ꫯӶm[ONnn.)))xzz2i$.^ԩSquuՕH&Nh,F |駬ZF1i$tu?$>>aÆ1l0HHHCl]Oȑ#y6lK.%$$VZl2RRR7lڴPƏosNFwvyXTR*䳶E:[ۛRё;wzcKFK{8p )))&]-lV*bLeյ+*6[{lLNTtgF)e5eA&{3@perƍrMHAe{*XsBQ<B!3{BJE~~~W5:Q!Iʘ-ㅉATwrzd///WW:BT$E[3999Tɾ{:<} {OzWWWnŽ#33%|Jw(wDXBQJ4@=~D]v؅B!*rTA&M'=={ 0dK/T>A!!!A!BʨLMERRӦM+lW^eÆ X-oɌ1WWWnÇ3uTBBBX|9 ,Y\ةSҡCFM\\afʕ1۷W{!B'{jٳgxbڷoϏ?hBaÆ 8͛7[-399D oͦM8~zΝٳ '&&'0b4i;w̙3V #++N:o١eB!<'{mڴŅ%K8rHEEEqa Ŗ;yd^y7oΠA2eVZT*ٰaѴnx`,/[B!( ߍǭ&H?~777{1Mo-… ,]E1o<._l/ ///h4233gmy!B޵kCP;Xff&lݺmZMf̘Yr8q'VX[ۦ2B!*:_ݽ{7gy777#IЮ];x N:Ul7oddddo>ٳ'pO` ԫWRJ*vy! S(B*FT>~fΝ;L>QFBvv2t:4 W^eΜ9z2dǏg̘1ܹQFw N6nݺGk. IDAT Q@ˣB؝`x (ZlY$.rS9NNN\xSa!eRVf+z$zB!LM !BTa !BTa !BTa !BTa !BTa !BTa !BTa !(ƍ|rٳg%@呄B!?.M!Dŷat\~K2qD׍7A̝;(~gj֬ 7dڵ 4///mfj*ƏϚ5kؼy3106l?+hӦl)!*'I!P(W>,.rQ[^x>c'N`ԩ,\OoҤ ׏?HAAA,_gy{Çၣ#'Of޼yՋ3fh׶>{BZ`:FCzz:}R$..X{=T*q Z-$''j^:(J֯_`ѣdddwB< $B/̩Spuu%44/#FWxzzb K.\ 999TVgϖOB=!xeeeΝ;iѢ{AѠP(J\qi^~"󼼼JcZZIB ZM6={+WIÆ qttUV6u!|}}ҥ pO`pp}Ǹo>T*z.WJR}N+~Wz=k׮h"n݊``̚5ltMejZFɇ~h$w^>RX //ߌ;aÆqʕRߠQMחjE˖-ۛ7nf'''.^X& a/~?T=/^̔)Sعsgy"D( JeR&q(BDO æBTT${B!*0j׮m)))BQ)1zh\\\y|TUwݤU6 P*260 6L6#"66 (:#NP46AP,&lTJMI%Rz|νQoVIIyj]\+{ªUjժ.0 `0{#`0x F%%%*((йDEG<¨@>_ lA;}[JMMR%1LatVt]v]A_eɲ,1!t͛7׼yh"իUN:U!afYl6[ȯh۶m)~G+VhӦMZ~6mڤ_]kiۇ~X999ׯVXQ窨[}Y9K= ll_5]lY R.]Էo_:uJiڵkke~#GW^*((!.@V, >\s… onhҤf̘E 7R'Nڵk.Ĩ(m۶MIII=z6mQFiܹz饗Jܼyu*++KC ҥKu})55U6m ,_p{9-YD|@[֜9sb ume {@lpN&-[T>}4tPOdWXG-rԿ͙3L]w͛~iȑ1b_~YYYYݻ^2p#adY  Ff)??_Og,ڵk%Ik֬Q6m3gjݺu;w4i#GTP5k<|>_m޽[%%%ڳgT^=iFeo|>ۧ3gT^0 ^kBp!z(K߯I&iܸq:uꔦO[*##C)))?~`<ũ pe˖zc6 *~N:ͭ5p-!a ZSz:z7n={oҤ>yҥZ|v)Sh:q8AUy,ڲenݪ[oU۷oW||l6[UEuT'NPZZ% q0tnW(g.\cǪA,KZ-ISFF^m&˥ڵKiiiѣG믿>$$$]v۔G*%%E7|bbbԮ]KuȎ;޽{K\v$|pux;c l6M6Mu7|Sk֬$9N?^r8Zd#߯L3&0g׻+ϧx?~\gƍk/\.l٢XTTT:BQ\\wz5l0=z4"mۖUN:UƱ/Ka@85^"**J^7u=r{+4k\B(nP￯iӦi֭5] T*1 8Ru8b2WvHNNp a-)QJr2C:vuܹ+TZnӧOS֭tY1{@|>\.\.嚑c*!!A%%%zg5m!ZJV2 b F0a`= F0¨D:C}bch~0@+>>¯KN{>^7nԴi,>|:w\{WBMcq5 \m{@$l!BoK.ھ}ڶmRn&UޕPSX\MWB]7T={Լyvս{w-ZH4~xuA[C=#GjȐ!e;x4i?JKKS~~ƍC)55UYYYڻwڶm+˥^{M۷w!˲4qD[N?-[:(66VE[ˌYftm߾ԇ7ިcnٳ5|Zd.]*Iz'ԨQ# :TԾ}{=ZC QVVvء+>>^YYY*SG,/ƍnkΝzgTRRRa~Rce;cdž kWqF_f 'IQQQzWuaM8X,ҰaԽ{wѣ>|{29sL?^;f!V_IIIc \ 84l*5LHHP˖-'h˞~imٲE﯃n+Yfo߾SO=h|޽{k…zvZWZv>;VFRFbv]ӦMӂ _BYYY*..,{WfW6lڵk4f߮M*!!AԹsg]60y_~9rFQlhE}|>M>]WժU+u޽gCcS޺E[h!˥kΜ9:m6F(M28Zl>}hСׯ_ @{sq?71;B>jGf1+u 6XׯWUV*ֺukYDk֬Q6mX>'|sO>͛t:K駟h޽iӦ㕝-׫Z?$UV7o~}nҸ+oQQQbݮn}@"a5{DjJݺu L}*'N4hPe}_zzː=ydJ I:~NgeѣG%I֭SΝռysM4IΝ߯N999!Pѣu̙33fLWp>ڟx _Xt/_]jʔ)8p:Tu]N}@bg,u]}Yk6oެ֭[N:"5h@e.~o׮]JKKS=^8UU]w$oVǎrʠ\hΝS޽%ICIII?\ӧl6N-YD;SU^^n?C999xzVa_u֕RAAդIJkXec ʎiy"}xoݺUVRQQ[fy\\222zm6\.* v̪a*ث,S6l(ޙ3g{nu]˗뗿>jժAi…o~Kkff>CM0A\qpq*9}t\9= F0a`= ͯ G6M~?k"J@DGGvرc5] 2N~jp(11Q6Mao@x0 ITTN>]eWLaa=*d`0{#U__eD͛k޼yZhzU!::Z:uf {oy3gTN*]055U۶mX~233TQ WЍ7ި^zI{џ.h+VЦM~zmڴIj׮Ҷ?rrrԯ_?XϗCݺupUaa\.Wuz=SZn&L >>|:wg޽/~.%{d۵fM:K  R.]Էo_:uJiڵkk݁>_pKakӲ,=ʪp=ݮ77 [ >馛̙3Zx.%{7hŊ<'Nhڵ3.͛7G}Xm۶)))IGVӦM5j(͝;W/R7o&MhƌZh~mp ~ Ґ!CtRw}ej,KÇܹspBۊVjj6m$Ij߾O?T{$c 8PO= &IjѢV\Zl>}өST^2}߿$Czꩧh"v+AX6m* zUa5m4Zpn&r-ej+oN>]?w7޽{+))Iꫯ.9sF~tC)??_Gvߡ)666I=@+,,Ԗ-[uVzھ}e٪|'t 4̲ԐXt/_]jʔ)8pΞ=[j뮻NwyN< r[Ujѣu̙33f$_q)ʕ+\ LBQ8-R tj)DkNvrssuQoVLLڵkr[vRZZz!k2  y^m۶M.K)))ֱl3fJMV}Yf!V;w*..N{$9M Unݺr\*((Pbb4i"I#!H 4eYAsrrt뭷;ի++q֫WO~_U۹3{;aÆJIIQ||V^\7Ҁ)>>^ǏٳqF~k_"˥-[(///61c?3=!mt:5~xphɒ%ڳgOתU+qjذ'IZn^}*cxбcǂ4m45h@IIIӇ~y@O<'jĈ:rH}U/}Zl ѣ`?Cr4c հaô|r?^ݺucǎ2={V۷oW:ukQ8;u꤭[W˜gUN:UƱ!y)}GEEVTNݹWw {zꫯM˲s΅MX?Pƍg}vE 3&ӸD3gO>a4G+G׮]u+q`Ǐף>.x7oցj#t ׄs5]F1 `0&r85]pE9p= dYZp8\ear)իJp5r:W]z~A387`0{#`0{@l6[M0eYIDAT_N_= YŵkZz`kٔ]˨huA6۸phDk̞СCլY39:tH3fW_}%)0` :TGQQQ*,,իk)==]cǎ߯Kjx~X״iSM8Q3fД)STTTg*,,,z"׊+4aY%өqֲeԿjGv{ r1ͦѣG7Њ+޽[OәVZ)==]>CY &aÆڵkƏ/ázK_|nVٳ%Ir~+55U|NNm۶q5jtQM8QGp >Ol뮻RJJfϞ+W}QVV,Y"I!wq{R5}tjʕAבիWk#F(&&F^Wo Eܹ~N,X_5j$vM0Ag=ܣzKv]~_њ4i̙_WZ~F \^=mV.<99Y֭C=?9r4hPiqV#i\ө|yr}Ν;vN::rrss%In[%I{$ݻWEEEJOOWZdYVX!߯JgϞW˖-eY֭['߯=裲,L}~_={T.]dU\\kJHHs5}/ԥKٳ'h`cKNN~9SݺuC~feY8pz쩤$9}erݲl]rss|`z+..#V\_|#rssUv Յcj 2}f///OǏWY~YڵtÇٳz+ĉJKK+wɓ'Gy$۷ʝ/_Xє}^J}͞K/{L{VBB,Rft7;?gddr@C7.fΝ,K={Wbbk.թSG]v lt:/ \~}~5iDvV^]an[^Wk.ki\;oGG}TCUBB:ӧnxb5o\sձctJ*..֨QOh:vbbb$IEEE=zq$tL^lȑjذ~ƍ|I _QQ4c =!q9m۶eN}ԪUKNtgϖ:;fΜӧO? {۩Zl:vM?()) ߌf/$%%~,)5v¬u:};nZ:x aٻ4j(%$$D=nwMQպfr)>>^QQQ8^T=˲T\\,˪\5Z)-..Nn[nQ^rc}^RRUPP@Cİ,K]˸L7g`1ӢE}v=arMөCYfr8:tf̘[jθ+niӦzڳg|MVff[IR=4l0 >\'O$ѱcGܹSmڴQbbΞ=dkڴ&N3fhʔ)***~3^Y>^{57NQQQ޽x =c lժU+9R:q%taԝwީlnuUK,ӴٳrJo^111Ғ%K*\vɓ'kÆ Zp$)55Uf=S~Tfiz7bŊw]&LPÆ e۵k.?^Co z뭊{ァٳgK6lLկ__zwa5nXFRZZ='ȑ#aKy,RffN_hv~jԨQiР^x |ծ#,3>ռysmٲEVR=J-ONNֺuCF T|k֬Q׮]qڼy<O\8t:rr{^0` -Zsr:Z`~_kԨQ_Zl)˲n:~u֥YW~};vL>̲B*###O?}W^4.WK۷םw)IJHHPnӮUڵC^??WNahBO?tXǐu} eY8pz쩤$9}erݲl)ڋ+99Y~|ŅuLWZZZ㋋mٲE'NԳ>!C_K="Xy!(11Q-[w~?ro8HOOח_~Yeu릒m۶MΝ 8tqu]q]vU.]4|p={V?x.nĉSNe'OTnny21Tm̙3۴yRz꥝;wڧ'ϧӧkZt{54.).]hǎۼynRgׯ/߯&Mnի+\vzUvTgիW p/u饗^c=޽{+!!AeYf!˥95nܸT__(555pm_ttӵk.թS'p^L'xB]tQll//zG+jt'[oiԨQ\5䮻ƍKwYݻW=z7rH͙3G/ƍ )++K3f 7ܠӧOk޽j׮6mtYƲ{n1B]vٳl2Wxb>}Zs՟'v5f=4k,uIEEE=z>͛7O|z!I*3pO5fO|{=uY>曠egg闿ekm6rkٳg^gWT-[L;vҲ 8PZرc+\~PRRR.u$WQ=="إ\G욶ʖy㏫u=[UZE{D {C:tЄ .K۲eKtСZ}^I2.8 {\ݻw^a j s"R5{DB%$$(**KUaa]~#H_e\3{D('sY>O6W8UVx *))IGEEEyUqq\.G"}DBbL j2l6l6[#RL4.ʔqc`= F0a`= F0a`= F0Z-758IENDB`linux-show-player-0.5.1/docs/user/source/media/list_layout_main_view.png000066400000000000000000002312501323636106000265300ustar00rootroot00000000000000PNG  IHDRV/GsBIT|dtEXtSoftwaregnome-screenshot> IDATxy|Tl' $}Gvd_jj]j~[ZmmB[Z+";A(%aF!2|f&l1s{&şH;T5Rl5)e2^)EZo2A1QuWRZ5);ԩC"#}ǟmP.ɚ9e&2K}B.}zv֜t:UXT%Ska 'qҊ /}g Ђ3\SҶ4} &U%)Ǿ{_ʻX1*,,D!vNV}>wC DGi5=t.+7 %+Uqbc.T%24hl*VZ{ԙ,ŷVժ5Yez۪UiởG:y*Cد&Nyǫ֜ۋut4kI2 O~}u.[w6Y+ (22\{u:vlyUl2ƏVTPPRwQfIԨe0'kէgg [mgv= +*>#߮o)UU NSKW|\9%kYhM֥z$p起;ߺYUUUb6_*TTdN^T^Q>ZA{iCwW3Z!áu.#Kd.˖-7/22_ܩz+dٲM';6-X4Z5>i"_ZT\=yo}Gu>b5T.hǞC1yKh0趛iZeJ;|R7aU~V}Jݙ p8uJnϿ]w}cҎ钤=:錚1OUEM $INg*9)Τԥb> 1H߿v}oBuMn:rtM3rmUTd>lG>K9IRn%{gkC뼿h _.?t)>Z׶CGe,'r2wc'j19ҠXh\~l&.IIjI 2LRX]*(Q\c٤G6(&:R7htHn}Ӧ{ca͜zQΝd&.Fqq*(,QyM啕5b a?zgZiv*m*)-Ӏu9KU3ݳۘ?^}WWu^#Rڮm$d2cr[jZnKeБ_] SAzv՛uX9lB,1ƇmY>t?[wh2L5r֙sDƋ~mk)I?%_TOA5D tJ-KѝNܼ| =16 /g+o,***Vlt WTLdP8}u&[ޭzu뤭;tݵt,ZsTjw8ڹ9¢Ya!%Y'jJ?~Fu2.oeߖԌ&&շ@fymٶ&:zVY+ 1ђ,٪ۧX% :8eWtJnV|Xڗ^i]L&nw9Κ-.jӻGg:r [:$/6 EGG)CmC8uN7fOPTdRwԅ۫+!!V1QF0B11V=xl+*2>Ymp2~eo#Sf~cTy!:!cE[dr̹lU:q^*]٢qc7h]*,r}jyyR6KUT\ͻխK{Luq4))AY;vTIiLjI'NSfv:uHdRJټ[{tj5WT\\R&fRv΅~wRVNv=J'%V}t:p:ww{$AqђsYښfU_ۧ֯Me۟&_ϯ˶?M椤\ӷAcS }dujnP K8hMe6ԹCboԝ`5jm ;*ԧ+6Xᡡգp}M&1{VOνGdu׷[M61X.>V&IԳ[G8~F{vnu^d2iԈ'5sh诋jPRxvIRHEݻ$HWpx1{֦jֽ2oo2qFS&|Q7(?[ IOz_yBXlnh؆7b߁c;#СsA/w *NU ߠq h<ݭUvRXNaaJJl4Iݮǿ葃 Q5ue6)/OdOHPU>AΈFIbhg&F@pFFrDUNc=VH@#XF" Db0l0gUh$H$VH@#XFJbl6kԨQ2 Ν;1W5jT?>w1W^(gyF'N/K~^q,X@+VxNbbbMk^։'=VSxZ>#%%%IUYYYsz!Lo:u)=kjʕz /믿^>>SuqFҥKb ׿l֟ZqqqZ~,t_]˖-nMԯ_?-[ɤ;wjZycWޓK7ߔܾɓ'kJMMոq\?Zz>3͜9ޘ~ x5?UŢ?3<zgЛoKG8pKzWi&}>pON>-Ţ%KhܸqZvN?XtM7gҥzիW/ڷo~ 5bwզMi&}uSM2%`_{II"""ԭ[7 egg7;cjͲlڴi맸Z}vܩJD999pBͱ|sK}XxBbM֯_?=zT5m{U5y暄?uرc5},Xqiܸqk5eIURRcBBBMK,ј1ctw(55֜uwΝ;n&͛7O?ٲf:w>F`Xh"3f֬Y 4kHUTTTPqqq.}ǎ||:~WtjС뮻?t:UQQת|뭷TYY)áÇT/Tqq 5ݫ*۷Oj߾}S-$$D?}Y5:Çh4jՒUVsٓqU>/^jfZuE7nԠA[z핱;>+c}򗿔t9{-]T :7**JgV=j ֚ [zSX>l)rrra=2L֭fϞ?+_tIURRbYV8vR\\&M$IIIMϓLƪ_~ 7Ps`0gOnCEEԮ];ٳGڵԩSkUZ@=]RUxn'}=M:6wo%U%wְ&ժwQQQ7ozY)@_OcׅUk…ڵbbbuӺӟT?֮]"=ꫯ|G}kVnnǾw{N?t:[o/hx\pA/{=hӦMȐ$ 6LǏW=o~Sn:7RZtoг>g۷?q𦘘͛7O=VFGGk޼yjӦ>ða\ҮEEEСC ⁏eddZ-44c5%M NJ\( >c)))JII6n87IRvvAK\`ZښWyXmibuY;{=VVժ LK,jl_klZ5sgΜ?cؤVfSdddl={V?n p֐TH*VUF#_ @DFF*:::a$W[KRUb+\<)))Z:&.Qg`~s}ҮQj17VKƖoII]VYYs=u!NG4$eZ= h"1:X dh4GҥզMEDD(,,Leee***Raa#GҥKWշo_uY PhhTRR,={Viii*..h7mVw_Ij"4QQQ;vn& 8P:?''GRjj6mڤE_;vرc5vXݻATUUiڸq6oެG @j765D-9޶mjr9VZZ*I.+~Xֽޫٳgb4yD%&&jz'ڿGz5xFk65b IDAT1BO>VZ7|S^D͟?_.JKKh"I[]r5`U٬9s{mPpcX,=ZG֪U`_zFc3>}ƌEO?wSoZ`sW-1޺%%% .t7""Bw.\h~v h"=#^O^ȑ#^o߾?n+I+EEE믿8 ޠ$eggkѢEnX}}Ղ L +55U;vPNNu%b(**JVU]tQϞ=էO v]өS"I ȑ#5i$=ZA˛1c_|r?.\Ѕ $I[no^ziܹf|5sTݵk͛EyLz^xA? m۶+mf4UUU9nWeee|*SRp(77ץ=//;SZ y{X=w~}糳oܹs-.z%ða\/**RRR& oN:[n^|&%S}),,L .Tǎ]}z76K/!C[n~_yejTkM6~.^6Gk<_X}'&UWXU'{1I^KJ+m~ih4jڴiںuN:A馛nrhԩSkqqqJJJwvکO>UVV(9L&M>];wTzzzͣFQm۶Չ'KCb ӧ6mh֬Y:֯_)S̙3:r$J~Wǹ~>|Xњ={ tEYV[N_~t뭷êhւ*ĺ$xct6]yfOddwCA=ncEGGI&%%`0ӒӧO}.[DHReXt9]pAcdX+bIRbbt19NUVVfn{Lv*;;JٳgMp(77W6MQQQ dnt_Kve;jK`&uIjvܶ=zyEvܾٽ2[bEEE`04:P͝;HfVŋQ8/^?^JMMUFFF͖ސzEFF5*>>ޥTUUWh4ct:=w%0XT׺$6@xbccݶ_xϑx722Rfٯ "[n:y>fm⵴T.]Ғ%KꝧTMرc:~t颩Sj5yCddrssWRR⒄ZZ$CBB<dZa]5 N Y%VKKKp8|1WKKbbbtt:_jʔ)2:} ͦb)>>^j߾}͹ϟWDDuSN`0j}yUvvfzt($$qfBCCURR UVV*,,L999 S.]W_d2),,]SmڱcGJ#XNNfzcǎ)**J\sV^jfSII {Hhl(ƺk.~K6jcǎ~x֯_7|>iX&V e˖iĈdXTPP]v)??_۷o7,ͦsլfʕ+u7jȑ,/પҺu4qD]wu***fwժ &bb(==]r:ZjnFp r::pǪ۫9;aÆ8~&N.]hӦMϯ9nkʕ=zZUUUi۶m:|\j; |Xb6lK> q=v}ݍ{?8-_\/R}.=õښh.?Қ<=fCCCz Gt֛4L0]|cjsJ~1tyƎͲrW!jOoH /u@K`Š%}"G2fOs .W2"wլTTT㍚}l9-KYpXu8n_|Xq7x ͛7Qs5jTΑ۷ӧ=^s%%%mAyy9 9Np:Vl6h4*44~&!jFF={6*OYYYpY Hb.m_*--bQe4dB\R***s DXԐu*諯riw, 6Miii. @4ݽ{w"7v,HIHHڴi#Ѭ&!q%Ng>o}s={q9oРA>jU.]\bٽ{V^^6m:%2K`&uV$9sj_*++մ.ܽ{wGG[ӧOwͦ-|p 2d]5 Nuj%)5}t_MlR~~Kٳ-Ҷ}vUVV5,z%oWS6f>eʔ:7kV]vui2?twyǥk׮馛|9m,YDc|nŢ'|ҥСCn{M4=,aM^^t:}v>|e짞zJqqq~Ɯ%}.c|6lO{q[{|Z*Z3<#wQQQK3:tŋ6U?՗XZ~~2dHͯCBBԫW/ 0@ `tSW/Pһwo7O gG?&WJNNUQܶmF;S >'33SiiiǛ<ǭ|/0[n<رh͝;WsΕnɓ'n+::Zj߾v*o]xQ˗/o?^x=xTTf̘3fLS^^ԦM+!!A;vt[zz'y5w\9sFWVFFFCjuV\p%%%\WС jƯ'tth4C۷91dvwt:UV5+n_W;}ϟg}VFd2W^͚zg۬q***D׷mJ+h{=z4z .W_՚5k+-I0>Z__L;wkƍڲeNj]-[o~Rj4URR;vxJK.kڪN.p?h ?+ӧOWhhhK wxy@vmݺU_8p@.IDr k͚5luq ۷V۠AԳgOZzAj۷oN8|U0XИҴrJ]x_tjժU\pvՠ>sGaaa5mfҫ* VY,͞=V[yyVXq7jkh:} ={6СJ'NЉ'Խ{wIV]re*_\\իW[niKHH?6p8?U&ʛ+>>^wqe0\L&UV}m So&IUZ5 2r::w>ܨqRSSn2\>E:u̙3u7߬Ν;l6W`|bJJJvZm߾-]ܹsJKK%5ZԒ%K裏t':::I ㊉ipuY! pR:tHM6"Q}YFŋu& 6h„ k׮&ڵռ6lPff# $Vw}7a|7T^^^YfN:D}Nm(;;[IIIuщ'UX]˺>}+㤧+==+cJ:~x@c6^,V@- [ĺ$P D*|Xbd2n;nd tH"HuX`MS]bh4fhdpԙX IDAT$hTUUE@t:=w%0XT׺MH"sCCeeeZu3$Vf2d6UUUOutXbMSC0l04l6UTTC g4e6Zg? L2٪ )!? H&!R˫,,Cpr:l5C!8.5 .&@#XF" Db*4$a*++e0t:t:=# X߹f***tyZޝ5u&!5***DZ;ZunTjQmgN(uLVձ}vq:+K7T@pKXBY~+<Ϲϝs<9@ @uuu}X "[XUHGxrDDDDDDDd?|}}uK$@ .F ZFSXXFj%"%ǚk?`'d)yyyС$""""όöxK<B!b1||?...Ш(S(PXXBj9& j50[Kg동m m->b;}h5hxzz P\\Jn4YX0D~)QQQHOO-Ŗiss-ՒEPP4 PV`Z\ꗞnR_c9e}]2j>Z5]GzB5חa"jM}w]P51~ĪV݌ol""GDl߾ׯRD׮]Q^^B"&dZZ>sðapVˡ!k"gh\z{;ʚ\>OUKK&Zk׊dWBn8y!&&ј3g 'Ob͚5NȪ̙-[ --M<++ {gONa۱c~CP 44gφd2֮]<"Bբ/_Fhh(R)}\p#F?FG/YG#GfBǎ郙3gbܹXh:u'''dffbʕpwwǗ_~)S`޼yBDD^|E,[Lwbطo*++1p@:u7xP(ؾ};\o)toQUU={JO>}vΝ;kh| ˈD"xbh׊>X `'''Vʈ:rg}X`N8GbሊŠ+̊IDhS$!117oɓ'1a̙3G傈-h͑f57@VEnp1̞=*ʤ3g ::ׯR)\\\PQQh9s[lA^^b1֮]ʕ+?3,\ΝáCZef𮮮޽;@,cȑHNN`HMM+N>]oIRO}ؿ?|r#77۷ǧ~5k֠k׮qFʶ<^"}V]]Çcܹ(--?T*Q]{XADZ4M7k߾=d2Ys56VEDDB!N< V'Obڴi HqذaT*N:dvޭ۫W/ߣϜ9_~B(..ƽ{лwo9s111XbZ-޽ Ѿ}{\|}nS 0ΝCuu5Μ99s@*LjRԍ`U(z6GjطoZ-pA 2o\v A.ңn\K^? t(**scykگ"tNxɭnoY}SQӊ@`m___xzzbe2 ) "jiiiXjU5Ly.**L&CDDwgϢǝ;wׯC(_3<͛vlС!C<96qqq8p|||cz VVEUUۛ^Ynn.0|<~_}.]dZi}Dii)NO!X;v@(bܸqV̐l?kL4rDX[prrP(q \ҤDD4h?^]E{4%%%sϟߪ9ْif̴-L~>}4bbbЭ[7_0apih4 <… QVVYf̭XussC=K/ARxRh3f ߏx{{7y]ݟ[ m4>4bѢE7b sH$׿ƿ/ݲI&qV"SmTS! ~)`…8~8 >>ӵhZ^-3N:Jt(**'p=DFFʂpIyy*"jK1jc9n3=xwhp]hZTWWƍj닊 ( !44>}R`ha>.. u.\y]vȀT*Ell,N> X ///O333x$''GG}dPg gZkպ 3gggxzzt|Z9֭[sb߾}(//^{5|红5䢫-ҍX^lnJ"$vDDd=YYYXp!vZlݺgF@@?~߰~z<3ȔJ%>C7ƍSZ`{!Hp]ٳ]p'99?Ɔ ѣG JKKØ1cn:DDDX٨!Cٳzp5 <UUUXl&O$Y111 ﳪ*,]ÇGRR/^͛7U |HJJ둖l=wZi}׮]ӧ1}tcǎM rJ|7?~<ҐhDGGוD"A~~>'۪ccgFa[x>>>PTXjvXb2])SH/Fhh(fϞxxjpwwMFLfjjP(h4N899A(B"k}`o9ʗ9|rܿڵ+mۆSƍF?rHǎÜ9spʕJ~AT"992d;\nc8C1V1c 1|p y=x.44nݺk֭F BnnnWԭnjQ`jV_360233ѽ{w8;;>EEEիۆd-@\|\ȶ=~RToMv#ma=z;OnfhxڵkHH$ 4Ȩ/@ hUlxg?(JG@NkM("<<bت1ڜoF.bӦMHLLԻX@ K/777O pCEJJJhxBQQ1t`h4jh5h|\Zv6kzr9:t`kGtD.#((Ȭ84oFFd2YPk׮ر#rssqfK܏ s߾ HOO7omō9U"""""""},XXbEEUj}ij>kt#Gshh.}z7n'޺uYyP k Vڠ\^Z;sj-zu?sSNbcc1vX]Ç9h4}*QiYraذaZSNҥK.C5 vÇ_ȁVqFjZ>|,ɓ'~IϘ\HZs>U""""""""5XTƨx9}* jKs^_P[^VVDҦ5 DDDDDDDds82mMPcȐ!3fL#GVf'ǣ{S21XX%"""""""ԵkW<D),,ā ) 1zho߾j5N87nX$gQt#F]T?jJ_c,b3v f2d Ņ PXXXMnG(Ha-MЫW/CpիʓX%"""""""0Ic>"--Mw?55Z#GmjBB IJJBRRRlsrkc8*Q-EilFm6w27+WLk #Voݺܿijj*[nIII-@&$$4xY)oלc,)))N$7o͛7 zjOPSi"94ƶc:2gYj_[sUQ  k7{NOouKckf~ľVHTUk718b Ftt44 .\=TKsgKSX=LsQy1a {rfDm׹sSacZ^x eee7n> Ri =ǜɾYsUVD"( 2BP#77׊Q HZ HdTU7?[ٖs8R\: IMk . !!/9sZ;͛78r䈵S!""""#ЫW/(**jc8/All,r9VZocaO??\o/RRR?`-D,ȑ#XjS1[nvDDDDd郪*9r.]BTT<<b$%%ᣏ>kaٲe6m'd݋xT*h4|HMM?ݻhjDDDDdR)r94n٣GЩS'mu***Ю];bX"GӳgO䠲RҥK4iJJJ_#//mb,^K,1zɓsNTUU=h[Mؘ>@ X X"[=L˚s:zꩧjm6hZܾ}=ƎDǾ} HdC0`9<6oތ*ØmH$9* 4 nܸjcX"G\׫Ć tsYʕ+8}Q9߬$DGG7^3Ӑ111 6lPzs19Riuj@`` ܹ_;w ""?ƩS0|p"??wSO=///c\xqΜ9NJ ̚5 |?Ξ=!?>S#11Νç~ Ǐ789$""""peÆ J˗P( K.xJKKXd ۇ2,Zd̜9'ODffYS|}}bĉ{n #50Rի{_M&aP*VʎjS* yxxNNOKs|m͹sSa _wsC].H+9 ,X@7궴| d2Yk.RCC@QC@@Vjײz5___r~~~~3f@ΝvZݴ7lr[naӦMfXۃtD.C͎*69Ocina%skn-_74o7f"==ݤ7>f%z"""""""j12 9yի-ZKkzlSrQd?8Rb"dSmXj@Znuev,[푨5Gͱ(DcXДfJbܹsGB`D;v,+SrJ֭T*z}Ǝ HP([|jko*sGZ455s34oj5 DDDDDDD䐌-50ךETVVbz˺uwܹ3p%+eװ1c Z8y$J^>} ,, uVX!Se:nj_cJDDDDDDD)-- F7ڱ!GQQQm8y$]_Vѣε!-fgg#33={ĨQUpii닞={B~Rrssɓ_b dgg[>a{5Rc 5 DDDDDDDrrr5Q3j}{E׮]#G)--Ş={htd2^#FUUUطo_kHv R5V851 V>W\AiiUsngϞݻw-90clT*ń tUP۷ocͺ>B^cbJQcǎX,BCkα*QXj?ϟ(QcdDDDZ-˱m6ػw/&O^z[nζZuEDD 22w^[*-r`Z7V451XX%""""""jcƍ7S;w⧟~rfTEʟ~ !!!H$8sLtDGGK.>}:tmزe *++[>>e7oŋ,Q:}}W!?~!C^g}V.~-[#=ݻoENNnܸa0GGaRk׮!""BwT*Eff(Vk1goM͛jn_c8wL&VEff&Ξ=z%K 99ǎÔ)StmwܹsqI.]ocǐ 6/&&ٳgW_!556)>?"++ ZrwEHH6WWW3^^^fǰDXr%j5k.L4 pu-=-kαЅD""##M`vpA<3g/^pO* 6 [lX,͛q :3g;#A4O" )) ׯG\\ٹxyyTwx! UUU͛(//7;%p4vղ/^^z\[l{<\/9N;v듙@ 2"@x4D"J[V!ta[u%P8Zpjn_c8TM6 8q֬s;wF7n|7juoߎq!++K/::~"BPw_~?nr'OWȖ9;;C?Q]] gggm5#y8__zzXno+WԩS޽{븹^ =|o&>Jfhx<P(JjSE0VxB!!8FcW9͗CO!MoJ_c!C0m4L8Ɂ_mƝ;wN1i?_`'""""PUU''bUUUl1#())2OOOcǎ>}:Ǝks=W͛ ;v G!,0]hh("""ݺuޭ[72H-rZ̝i4h4TTT=_jjڮrn,_r ͛3ϔpj=ӧO@\|"t@WСWTT6Scx1R)B!4 'SU=z`-p޽;u㨨(\zGFEEvH$Y^^i[n5*P(Đ!Co9 aʔ)ٳg푑DFFFtY'P(l~ prrjWlj[Ά%WXjPjJ_c8ta5006mBbbb& э }g|?...ǜ~՝hTSá D$&&v܉kך'11j?>-[7|J˗/Gzz:/?k̛7߇D"1*>9tDEE!>>* P(\]]ѭ[7d2<~j1… l2$''C.cӥ7mڄ0xyy)))}6}$$$hrSL].x뫋q X,իW1sL0C9:)JX-B@@^[QQVXRi숈Lg9VeK$^.\@ΝǾ}׿ǏjDDDDDD;5} CC@QC@@Vjײ`tm5___?F"r|G/ IޘtV\\CXu^^^(..vDDDDDD ga+V4YT%xAR!!!3qZ ƟoJ_cjbbbPRR7o"&&...}"""""""իW~'2՝;w_|dܽ{᭜%c`ҥpwwJ¼y8 Y\ffS ;#L2#F ))3yS*,C̯w^ݻշKDDDDDDDDdOޏ,]DDDDDDDD"DX; 2)NjWQsjTin_cJDDDDDDD6EUǓo7ZXYj)5 DDDDDDDDDdlvUX܌DDDDDDDQ8*;Y[;sjuQkչ9VM:#k@VYYiȁqU""""""""""DDDDDDDDDDмu5 DDDDDDDDd2߿cƌiСCj嬨y-,BCC۽YQ["}*=zvDV#vmy͙šG33g΄?r9>#8q¤'N'4 D"Ξ=w}<0;;b9r$͎CDDDD_ \UVȑ#M 8rΝkT|K =P*8r<<<'O`-h늋z*jHMM3\]]͎'ɐu[lAjj*8S?iii8{,:tKѻwoD"dggC*;voGW_5qmñzjj`Ϟ=x ٳ'ƍHNNFuu-i걺zuGcLٗ=@"}ΆV\.ǽ{бcGm1bR1,?. %KO?f֭L+vblܸ1k,̝;qqqN:a۶m̙31|xyym*!}B@||R)>D"jb_F+d26lxꩧjm6hZܾ}=Ǝk׮nd˗Q^^ r]lC1&sۼyI쇏gG(--JgVs["GcP^^ úup-dffYb_!HUTpvv6ئhpƍfŰDDDd?`ǎ Æ  ?fUDEE>ìY;w@޹sbhZ(J """z<==QRRb[3{cݾ}nŋqAWBff:_Z:JbeNNN2f1>Z-nݺ42?~'OĀPXX;KQPP`țb+Qېpm}AFF6Ko<셩Tךrxxx@(ڵCii6[ADDa ÇrN IDATwcǎxg.e*777aРAy&!H0qDtSNݻi(FAAڵk={B"5*9"aΜ9D cmx\4%~h =V@)S ! A|||8N>8xyy;wkaðeݻǏGK.GH~DDDDd9CUUqyMb G|r >>>tڵɶj5f̘CbΜ9Xp!zk={6+Wbpvv[իxgu_WCJJIy8[ynAD69|auРAի6mdv^xGbg.fs`(QV0д9h_Oi=zGyȲV6ME0OPt[5EtN3q "~x{47\{5cݘ;w.N<)S48v)SsDEEa޽HLLDff&N<իWC"Xoüy󐜜2 `.XyСCĎ; Hj* 8 ,@FFv ֭[qqaĉ>YYY8wfΜǗ\|& ԩ՘Ba-QBtt4VZ<ڵ /՘/ؽ{7JKKa2p%9saaa& iiidpttZWhh(b{ADDͮR,YN׬NNNӧ=d9"J*҆ڷoEsvСطoF#u C\\\zv Tkn(דO>qaĉDFFƍJظq#:Aaԩ7o~i@NuVcذa8pRD```5x|r :  44|LC |||`0pITTT/ ?b1A7":t sfo""""MMM ܹ@D"8;;cǎϷT*mΝ;HOOkAеkW3;vd6my%xbD"DGGC*:v!Cܹsur@.]6F{ADDn-aĈHOO+//ǹss5;ZFvv6޽[nT*9r}޽;iӦᥗ^BFFN:ٳg3աC$&&"##iiiسgΟ?HNNK/Ç#,, Z*Ǝ $&&">>gϞm&""""۝={ ۷/Ξ=J1\ݻjhѢECjj*`s'O; /FZZrHIIDZm6$''curk.jF{ADDIn\.%DDDDDDDdE`0h4h0`4h0 F\CQQ!c\JDDDDDDDDDd#6Vl*X%"""""""""DDDDDDDDDD6bcFlوU""""""""""IR飪(^%9\JDDDDDDDDDd#6Vl*X%"""""""""DDDDDDDDDD6bcFlوG@"gH$jR^?QKv>4M86Q(ɓ'1c E||< Fc)++ j#F`0KRúu0x`̟?˖-7C .Dzzzk7mADxƪZyF/'N@llyIII={6;W^y .>3+Ʈ] ~zѣXj$'$$`prr(J̜9?4w/A@^^\\\ec޽۷oGzz:mۆΝ;1'Ѿ}{d2Aয়~B bbb-Q!Czj \zx|}}q puB[ Dii)?Cff&{z0`iӦ&ho8?uc&L@RRv܉Çۜ 8v:9BBBPYYhlٲoќÁ`4b 66}8rssiӦOt\z|9ݧd26l؀ݻwcȐ!8q"N> TO?7nDTTRSSlٲFEDDDD sqqFh4ݿ...VcZ7n@uuusDgϞz*Zy STزe r |||A쌚9V*wҥK-7DD캱7رc:u*{=7Utt4222둞`xxxXqFt:,4 ::/& Z.\@NѧOL0bCl0TWWcΝ0LZFxx8A@jj*ׯAh.\^gUcmm-d2՘hס뛝%7uRpwwjX~}WӕaΜ9Xh_wtu2e ~dgg7F{z~yS[[*!"j]vU_}s!%%QQQtRs >o۷7r:u C A~~>PPPA0uT5 pvvFnn\oyf|(..FvlLqz]]]-6/,,B@EEů:'JB*ZI$t:T*(J1R Je5f'N`ȑ柗/_^烎x L4 cƌF{Vۏ}bQ5DDǮd랫JO>$1b^|f7V|Pyy9Ο?Cb߾}ͪMV2"$$)))(..Fiiir\C9ݻ+Wbͨĉ'`̙Xd -Zш7b1c~m,\pDDDD<999ݻ7Z䠲jLPwޅZn護»ヒo&|}}1uT>ؼysuz_S: ƈȾ555HMM3ǏCX >>ňFn__a0? wl޼W^Ņ ,r8;;cHKKk</Ƒ#G0pzrX޴6ɮ1cV\[na4 GRX,Fbb"233qI^rJyݱ{n̝;'OĔ)S  Ç;w.?>qFcMŖ;u-[믱rJ`РAXv-z-Z?5+j5 y Vc:?#%7AAAr ZyܹsիWcA@PP_JM66svvK/ݻw#99#F0N*Ò%K{ADDn^^^0Lx˗/BhEEE\v & Z999M^at:Fb)))C_~Z-5>}GEuu5= шcǎ'C&bLC&Y=\h:썧g˷j5;i$ܾ}YYYjXvy;W_}.]j^:m48uZaKwDDvUT X~=.\:`…+W۷Gqq1A30f@Tŋf|aa! ņ4\]]]gHb%NWg&T Ng5sJRi1T*R,I&aر0Lul۶}YY8OC5_٭[7|7 hDDDD< 44ťKp1BHdeer#Be˖!<<+WDff& 114Hܹ;wDrr2&O<,Xqqq0PTwH=m6MaF{F Fe2Yl4 J͟OT9{?}k]7V就JD~VDDDDDDDDDD6Vl*X%"""""""""DDDDDDDDDD6bcFlوU""""""""""IR飪(^%9\JDDDDDDDDDd#6Vl*X%"""""""""DDDDDDDDDD6bcFlوGL"gH$jRl{%IZJPPZyzz"-- _C.~@pp0ZywXz5l6l4 ^u={6ʂZƈ#`0RX,nVM~{ W^^G^^JJJK,SO=FkykԩO#G"##s5>#DDD<6qDZƂ бcGa͚5HII{VۜDD']Wll,pȑVd|rǏ{饗 _NDDDD`t:ddd s,[ * CFc?'J:1''',^/ϰn: <Dzel6nv!55EbطoҰm6H$`˖-HOO޽{www\prJDDD+RSSѥKb_iii8z(VZeW̞=NNNR3gZm(g޽n>NHJJٳq1+7tGؾ};ӱm6tܹp"""Lj\.G푗F~ :tS(KsD C իa0pU$''矷ʼn'гgOב ^_NNNPV Dii)?Cff&{9u""jmMcuܸqعs't:]ӧƎ?񏈎oZd2lذwƐ!C0qD>}É'G!&&7oބhĊ+̫lϟ,L6 0}tիclPHH*++-[XR~)6n܈(bٲeA'j|G|k1h~-Q-Z՘VtoUUUxѥK]7nKǔaΜ9Xh|||PRR?j󃈈ZcX6lrssq nW[[ TgRDb1//>#T*X'ODqmkSNŨQ gggZMaa![l޼_|-٘_j8a} |%"""jJc:j-* JbLTBRY⫯29 **ʢ 'Nȑ#?/_%%%-VA[yns~=q,^4`۷ѽ{w{[n޽{7݋!C`ڵ3f JJJm9ccc_~xwt5k֠6tttH$dΆ_ii)]f{8=jKܠVb{pe@&c͵5X,3<9sۿImQ[yns~=~.] b-?X'ٳ_x̀ QYY 777={`OR:ѵkWb___TUUJݻwoRغuk@7pssCϞ=!1`&?. ݿgۼH$jt+""""_MM JKKH'''jL,Us؛dffb֬Y]tѣd5&0eʔ:+II$aptt3"""nC$aprr 5k޽u""jmvbuرؽ{Xv-D"֯_vڡVÇO<V\ GGG899aϞ=Ɂd̙3d,ZF7nġC,wXr%xb| AJJ QZZr޻w+W͛QYY'N4iC}1c Xp!̙36999ݻ7Z䠲jLPwޅZn護»ヒo&|}}1uT}@!55=|I(Fcm1=JBtt4͛+V[n~`0O YfaѢEݻ7@*"==?aNSmADxƪ78z(1rHxhh(*++1x`lڴgќ:t(T7СC4hNy駟SSii){[ur~)֭[?L&F#22 C\\\a]} Ç#>>0w\?~|"##qƍz:g}Z;j ,|`sXCȑ# 16lNh:Fhh(<==!bcc~n Ʉ7n૯¨Qd20i$dddO?ERRys_~سg .\rsڵk0Ljɱ"o߾HII:t 6L&[n/%N-[/D\\W_aݺu272I̘1cƌ J%.^`۷fP(h}-q>""""{l$Jb{R썩T*R2p@L4 cǎ{CiNWmADƪ?$ 233@@@FƪRDXX{5 'NlvcxÑ}JJJСCD"'xEEEVsbŋn:"CqqqQ^^KZ_ZZWb;%GDDDd/Xl ߷k9KУGd2sc,44/^O`` >CL<榧:+W߳ "z|V/"$ 1x`nr8:ubGo߾>cذa8pᅦƍH:`„ عsgۻW_D"/ӧ!H0f0~~~DEEJ%Zٳ6 +,.㯪?br_5tm|DDDDܹsDpvvFǎo5&Tڪ9͝;w^{ k׮3f va5&0m4JR$&&bkҹ{ku""jm:לFWJ㭷BN`2c^ξ ٸq#8ob믿w}{ݽޱSNY4e2GXl|||PSS5k`Ϟ=uj߾=^u 0Pطoѭ[7̙3?}|2>?~<&N2w}L>}4x !C,\gΜ3vʕY{7 44ťKp1BHdeer#Be˖!<<+W4m(dL<HLL->jΝ_$&&K.puuB@qq1n޼z":j7a4`4_&F REEE@ٷ~ucxqĉ.~X%QCv+{vDDDDDDDDDvU;շo_wpp͛7[(""""""""";!i퍥K xѯƪڿ?e%n@DDDDDDDDDd#6Vl*X%"""""""""J"""""""jz}k@DXm\ͻ%=v DDD DDDDDdo Lh~Sln{وU""""""""""V&`L V!X5x8B!UzTBuN5plQ*Ui/xB"I AU+C\q/9zLlQ&3[l2\\.-^z%ŏ#"""Ppp0t:222pyٹX[a>7jUÞL:1pe_%>cٲePT:t(ϟx~a…?>"##kELL+66EEE8rHM[yns~=mcU0#Nb%Ui ըp@CUzb'18$X Dii)?Cff&{9NHHٳP*9sf۷oGzz:mۆΝ;|.""""r9ڷo /w'dWxXk]pzc::腓ٳ'0d^W^Err2yԩSj*a4v!55󃈈Zۣ]r)R"&FJjj͍UTZI-b#LbDRR@n۩\DDD@8;;~~~6,L6 0}tիcR)>SlܸQQQHMMŲel>C...h4jLƍnvq ey1hcwI +Qៗ7={իW'AϚ4ոqsNt: G#RVV9s`ѢEAII jjjpf㏱gddd`„ 5jڵkgC>CYJ.C[B&YF\~Wh:썷'z59xX]A_Xjh,sj_ >˗ ÇرVZoo8?=Z@ä7AdAlCR+\/s3k!!@lC0 IDATd7A҉'0rHÇ˗Ixaa![l޼;v@qqE}puuEJJ RSSi&BP4|DDDDDZRbL"@YTTi1*U韴RTZP*PTqÆ 6lƍ777̘1|Æ Cnn.nܸh~{VۜDDGbUd\]䐺Ja`?$BjxVdzR+:WA-3`Μ9αf 99N׮]˽uh48;;C,/%vssZk9yer 13gUMqe@&kŝ;w̏eAA0r7_}Uۛ "z|="PS C2 Z8j\ jg8j}X5`TQS lkD"L<NNNfݻw˱uz<{,k>N}.""""" "NNNCAA՘X,?$I7Ew0{H@x4}mvrAyAST*QRRL̚5 K.=z4sN1=d2LbK.@JJyZ~{ADDMn\.GQQQKZ &HCh7/DA"Z) J .67Vr9^u@" 77K,ݻwmʓW^yyyyub{իի~mxyyΜ9 t>""""{;jkkqZ) DDDPխQw7?D5OgàUs+q(`otww+% V&|z ٻ(lN'$!@@AFÚ Q+W1΀fqA82" n A@e6"K~p!&c$~=O?:UNUBoi]w^l6͚5K Paa,X:O3gԠAO?TSxx>C6$%&&OgϮTյ[_}=p΍9Nev: YVeff***wMYM.xcU !k_ZJ́fgݐ)gS}]& ]/5% *欦ctsLIveϒMdmyk=;j䊆*dasc!Fc}DcEk]hf6V_wMԡ?_%@!!!!'R[fVVhyvF.>4VhfL&4K 43kUiZHʼnaש]_JѤk* X!8tK^׆g7vYpQ;VKr/){J^_uŜReGk؂ij7֜' TWy10;+. 짬6C|I?W_ʝo Zc绦M7 T *P_PQ?hߴS?7D0ɹ'5+& QVrXM<שbG9mM|LmlB qΈ^׵jUՀȮj=`Lky'uj41> ꮶWަ7.SN7=ëQZ(I(בO_P1y é/jke8ЪJv.I*whӓWh3i{셹Z`|?g/ҟ?CGW$I*8C?5:+@*uz}ǫ\ԉtW E'hӓ/:WSQ&VɬR:K-r e+=Y Qii3wr8ç+%mME:{CrK2$ȵYm 5նGThÑRUl7Q|uݗ=.d(ا Pw^XI l]fkZH*r׊/gEvp"߮s6k+բJtJr݈iM\>GrNإCtt]9;]}N+rରk S'ksպ5Ul'UZ?w"e+vVZ QʪC*w{sյmLSw)Q%$]G*I7^ojWnnkw[>꓿Ec+uX.ug,4.򂟪|*{Anst m}vZ׉uTyJDv$~ 駒# 4VɮEE\u,ݧIH7f,*ͬv9+ʕ66I_%u&Ia C$VYҘOEfJT&l}/ux6+?䵰Pma漥W~*veVcb }SC-Ө:p[ﶺ}'}`59@je-qkZYNbpYtTQ_iZEIÿU{EƎw]Yj8:eeoY3(-TP*?-0@yIf$Y%R;^5ɒ6O*ZWS Cn,2U5LSkҙRXMM=?QKxotjR]O?=bU}kod7ZNowXS׌vVq+ShERQrV+uiT˿uE\y[O>Oۓu+wט75ʓ|52(+ C2U}cI_~ԚzFQS+o#d22LIGuk9S b}i~8ewN>zbXXjy|6Ie/qr*ug9 VdSr,RhV}uӅ2gT9PtO@HXin,$_E'՘'od/O;64*;cįeEUxl2ޗah{3v._S fLfe+@e+g箼%9=U}tl0W3{md8~j,FmN;X$IQB}}ۘbҝ}]w.W_7:??If=p~Om껭?}'9{\4-y-8Xjnke_Uw|qJNTEQ%PtV)PSƒ?hSc ֥*[[wd?Mg _X|C8>CNKgI&:\=YW%Q! &VђԿת8=IR~W_:^?OjgtYIRh+zPk/7XkU6fO[TQ%6bC*Wy:_6&ТrpʮGͬ уg՚%ZN9C޸5\2^{ZمZk6WTtEf;u<ϡ*>jo:Ns\4OFC O-K͝iUVSff:MOnpPFIRGQ*IRTTtr:aT4dZ(U\5@eLKV 5hrÕkӁ\Ԍ;VhIC (XXXXX ;B XXXXXXXXmB|||4l0Lf~b4:hĉGyN1wӕ믿^5jj29rd9sh*((hdSNնm۔vkYEzRVdە'Ok9fΜ+BZpnw} SEE^z%\F &(<<\={=4c'cj*--M .(V???y睕ܰرcmjuڵէO+--M;vP~Tk)III5\\sQΝkphҤIӔ)S4m4ի=C1b^xGuxrls~@U(%%EvݣXm.\)S(00Pɓ'^pc6h"]V_},X iٲe2e֯__ԥKJMMi׮]n\޽b(##C!!!2͚:u>S]VK,qmWll{1(&&5am۶Ȑa*((6fٔzh:Mxx/phZjnVHmܸQ=z$ZJ9992 C7|;ի%0*___l6ux@ceϞ=JOO< I8qRSS~y)77WsզM_&]YYVki>>>*//wk9Ann+M VnnXMbccu=auhٲeѢEdXqrls~@Ec/;UVU5J {~<'OT6mܮ˓|2LվZr>cW^M7TҚ(((HfU-Z(??m)S.]jG{qNnsiUb?g:dS֭󨎋YS99?b(wީHPڵkymۦ0K|}}Q|?Zh=zOW^y+f!á͛7H-Z%D:ulbh˕*''G]tdR``t11٬Ν;ǧQsx'OjݺuzeX1chٲencﺓm۶Zhz)eddTZGMsL&Źֈ#cux@c3 8m~~~̬򕅦nѢEQHHl6uaM4m̝tUHR޽/jȑ 3<}*++K999{ァ͛7[nU9sZl)ө7|SZ~z]c_~J'N<"mܸQW]un&iFW@@Giܹ2 Kȑ#5|hȨsMhE{VXX***+''mf)66V[lQ~~~F6MfҀTXX 諯rԇ~k޽ztUWUzՊ+ꫯ裏\9:D >\VUahҥzd Džsnب(4t:]/èij*33SQQQvshj;UUhhj;P!!!!!!!!wA~whhhhhhh6 >\&邭oҥ ;#Fv:TӧOo_Χ hH;vɓ5f}嗚2e+vmiܸqjٲ***pBX֜[nUpp|||eM:U?Sl߾Ν#Gy[nQaa$U&Ţ={h̙ڵkW7**JsU|| <^~ΝZpx egg7hm Ţ}uֲڷoj5_< oӗ_~)H]sHR\\|AEEE)??_<?K/R[_|S֚ߛ4c'cj*55U_ƏCݻNynj^ziС:uy! 44Uf[J/ʕ+ /4=+^MUIӲe4a„ ѿ+%%E~ ZcM173grssD͝;W\r$#uqWhJLLTll&OJ޽[#FpF'Nhڵ6Mɫ?֬Y#^%rJ0 ݻW[ne]Q'Oj͚5j۶kZΝ;hÆ ZvƎ+I Ӻu4o}a|ERHH´rJ=#ڴi~W[s^d67T/((թS'Kwe˖iZt:vڦm۶i֬YJIIц twb?',,L D%''kݺuZt|||ܮO>Cp l_ڷo PAA;:l6=Zuxpkr8Њ+tKz$22R7oVϞ=k1i$͟?_G$9rDNm]Sxx֬YSk~ohl^X+Ţ={u^d2[n;vk4VUo5tPM0A~iuҥz J\ IDAT;Ccǎ5uTUTTԺ+00P۷uxPp>}Znc:x𠊋띣!6={2״۷W^n[oǏaX4|p_ޣ.-[LbhlͲN?4Uk.%''{:v$iwyGaСCzt7ݾ}***c+22Rp_Wyyybm۶W$iŊڹso߮kҤI72Zf$)99Y_~,kt]}յSPP@uE&USms:7WvSuxVZU*??_aaan++++|9ez)oٳejnA/bhl^2d~nauZf̘1PPP﯅ j񊈈Б#G*9zzY%a*--vOrԇU[tXBׯWNN-ZTĉ5ۿZn]k9s,XSNƍݮP}vxrIXV5 77ؘmVUh"=䓊֓O>wj:x`xh*64_ͪڽ{w/ѸqƢP6lƍ5h ޽[2LhvC&'Ot# RS9996p8SO魷Қ5kO?)''Gט1c_ݧڹsgjYbVZkܮO:;6kLL М)$$Df-Z̙3ncM17ػwu&___Wc_~KrdeenѣG_覛n1]wݥ%K4hrls~@lŋ5}tedd+G@@X8p@~uwd2)::Zw}/_^rdffEٳ4޽[}1c}駚1c$i۶mjӦIgǖ=aR\SÆ ӧ~Zk6Mr8ڴi ]r(9)--Uvvw.ɤ o^Gq3ҥVk6JMMC=$ŢN:馛nҥK.x@nsv}dXԲeKxo*8'&&F]tիq#@c3 8ʭ~~~uT,^X111 fSVV>k2d?z饗ܺup8dtIԢEdu릤$EDDT/>#SN*7n󕜜@4ydMI{5rH? Y,O-[TEEݫl1ͦ#F(==]yyyl6%%%i*((n:I5_̘1CVҸqg93+>c͝;WrH?.???%%%չFoqEEE)((@N!zF埝NCVU@]KMǼzX͘1CNSgEyk,֮]'xB7o Ԍ*樦cf(xAK+''*f"""O~ o Ά~z c4h.\_~w߾}2*>4i%%%:|pcT͛uUW5vr 6*Дq*x*x*x*xëV녪4QvK&;VC4VC4VC4VC4VC4VC4VC4VYXX222hذa2L*4v C8qnGqF &(<<\={711QSPP>^xA_}կ)H͙3GGVAAGN:U۶mSZZگS.Ţ^zUVɓ'k5_3gW\-\]|M'xB۷ӧ/k͚5Ur̛7O***rM;vrrr\7h*64O^uǪjUZZ.\Xm҈# /|}}k٧OJOO7߬Jg֏?Wq1zT]*<}T^^4رam$%%)77W\sq͙3G;w5s]7ЫÇWRRR95}t%$$^999qk*64O^X=pRRRd۫V^,Tfg2^K.ՙ3gT^^Nl֢EvZ}WZ`|||kjڴiRRRtgQZZ֯_J7o6mڤTr-k.<:u[oT%''뮻+66V=RRRSco~ײÇ׊+$Iݺu￯T-YD;vtյl2M2Eׯ^֭֭[5qDIqpOm۶UFF PAA?h1ͦ;GCm_|QCתUt뭷EFFjƍѣMwﮜmذAukRK``=p~W5VC5k>幝722R:t\mtjܹJHHШQԿ5JԾ}{-YDC |^)))ӫ'|Re˖iz5k,Wժ_]_|4i$=#+ԕ7j޼yJHHÇk>s,IW_}>sYVkz75rH())5_߾}UTTxۮm~w &GUhh@N5̙3 q+++ӡCTRRR Qѣ߯2״;vgϞnczuĉJ~~m+""BX, Rii[˖-uuZ4fX;tR|ڲe,;tJ܁dʴk.uۺu6lؠR_^NS7n _rܹS۷oWBBBe]&00 >|X׿tyuڵ۷Zj%Ţx}8p,RRR$I_|.J7TyyycUTThΝ*..Vddd m 9N8p@v9oӲe*t+,,mL-tiu&O??ڼyw*//R|k„ zgSOGn6֬hٲeѢEdX8!$0-&LoQ Ҟ={WRRRRuUGѣG+qQ]znsVk^^kȑ#Աcte)44@'Nus PiiL&S*+++j4GncM17UpppiuNM&7n 7o>s۵f9Rv򨎋YS99?jVUlСCJNN0'NPvvF?J|ԨQJHH=ܣ<= Rc6mwUJMި:?D]}Z9rĵ999O $P-6sx}K.u5={դk٬J?ps>u\ʱW d2)..IattF;v]0 ͘1C3f7߬ Y,W\r"##U\\Bk׮}={*66Vɕ۶mn6L&kNwuV\YmuIfYmk׮_ZWvM6PM&Sߐ 9)--UNNt"ɤ@EEEرcncfY;wO6'OԺub(&&FcƌѲe|}}^nML&ƍ@Y,=駟s*9NH:Xp~Wݱh"($$D6M:|~a V ҥKG՚s۶m{cGU@@9 ?T߾}feee)''^u)))IQQQr8zǔYв2M8QO?|A۫'h֚-[(<<|II&Mz[VyV]4~hvܩ޽{k᮱싊l6:w~I͘1CfBԑ#G"##5amڴIW裏={h2 CZrؿ.}r8:}z!ñm8?4p*)33X09(4t:]/èij*33SQQQ*++64e5kC@C       qhp*x*x*x*x*x*x*x*xȧ hH;vɓ5f}嗚2eJyZnkת\_~yv]SNU^Q˖-믿^:m6}Z͇bQ߾}պukv۷OYYYƚbogyF RAAUy__5ǵ^I&M6WRR6n(n4WES99?y;VVRSS׿yOћÇ뭷g}1ch8qvQ:wz/*//WJJ[ 0@ƚbo0g*>>^;w.JcɊՂ ꫯWRݮij.5zrls~@U~Ak֬n6>x`K/S>Ţ?JLLԪUti99rD[l$f-^X֭ӦM/GfYJNNֺutRh2dx SNΝ;wц vZ;Vui޼yJOO5y[h}0 رcСۘfѣZ Q W||,X á XB~k9"##yfS+++KahڵU@@گiΩi-8?ͫXV=쳚9sLNdZ^0TZZ*ѶS^^.o٭Vƚbo[el`z|ͱtR_RNxbY,-]W4cfq5\-[Liii?-[*--uiut)9yj5Jwy?[bzԱcjs4cfX]xk #FhܸqՈ#T\\\rNSO=fϞkF6MEs"*88Xݻw$l6EDDphӦM*,,t}X;wlbѷ~+q2Lw߭˗;MZiiս{wL&}:rۘlV.]dZ5Vjjz!Y,uI7t.]B[rŊQhhl6ta?|ݺuӒ%Kt)o>}cK/lSWVpp?_~RNNۧd-X@ Ԋ+4g8-\P'OԓO>[[nJJJRDDJKK/룏>RXX;uU֭[5n8ٳAslRڻwl6Ft5Zodٔ@׺uw5GddVZqz5rHYV9N{zWeF43fpسgk 8?.(4t:]/èij*33SQQQvyW5V*樦cb(hH4VC4VC4VC4VC4VC4VC4VC>VB(%@!!!!!!!͘ &إ.!uA'N 7ܠ4=#ؼy"״c*''1JSj۶mJKKUGFFjΜ9=z Tm8bW^jժv222tZcM17̙3uW@ .t'v;-ܢOaaaK/+WJrMsORΝUXX+99٣:AS99?y;VVҴp*@M>] WSnJR׮]?vbcc=nJ~m8O>*//WZZvء~)((XS kF?̙Ν;;ФI)Shڴiի$4={; 6L?l6Guxrls~@U(%%EvJ,00P3͚:u>S]VK,´vZ-X@6mRJJZ=3JKKөS'[JMMUrr.Iryn:mݺU'N$͟?_zǔf-ZHk׮W_} MǗ]v>kZf*0ڵ:Γx e+VHu_Zd:v/˖-Ӕ)S~znp1S۶m!0TPPǏ+::mf)!!A!!!uxp_j*zncڸqz!IZjrrrdۧoF 䰹 IDAT|mڴQrr***k.UTT(44mކؼNPPn-[L˗/u]W2mݦO6m***$I۷ג%K4d}z畒8z'%__|8M4I<򈮼JWw}W0a}Q*11Q7nԼy󔐐ÇtjܹJHHШQԿ5J~~~zWkiС_*___Ir;_muϓ>35ʵW_?\VU|M9R)))JJJr׷o_)>>^o}UӺ.v!!!*((tM;sBBBt!;GCmz5mǎٳXnn~m8qJNŢ=zղxb=ݻ޽GE] 38EO`Dt9|JJJt]|Y!!!tIvoU˖-ս{wFmذAW:]2uVf%&&sjܸL&bbbi&uM&I[n$mٲE=zdr[bʼ)ﱺٱ:OO\,))bql:y򤊋:p75a\8jҥ7}MڳgKݻWZ`yo5՛KG:Gǎ+wNZZ^z%k… oʕ+e~/((p vXFFnv`0ܴɤqi=@GcŮJvE}7֙3gty={V]v9Ν;"?V+,,l.3ᡢ"ژ׷̘rrrѣGGfV\3gjΝׯ{=͘1JuU4V{[N6lPtt~m :ԥZԼys NjPO.ܷo_jѺx̙8FPPPc\bYgyIW_}̙3ڴi$);;[?Mzrtg#OÆ 4VsǏ+""Bԩ=4v3m۶իcǺ dҎ;$I7oVDD|A.QՖ6z`Ј#%QQQJJJr:j*88XJMMեK԰aC}Ayzzjذa2 j֬FuU8ʕ+ hdRHH._|M6d2i$Ǟ=b&Iѣ맯q ^wvl@ي`BCCuY1Ѩ֭[ãFs .hSΝLeggK5~xz駕)OO c&]i޽ tr&NYf9k߾}]uݑ#GԱcG˱KƬVZnY5⋚;w6mڤ| :sXHHƍ={رcS``y=󒤵kjɒ%̝;LcSO=%ͦ5k8jt7@M2tχ{zz7cÆ ?vR ߻%\ffsne6uyy6+X >/r @h]*<kڴ̙#oooh*((0u bU{@5 . . .p95?b\Dc\Dc\Dc\Dc\Dc\Dc\Dct.kʣ NZҤI4dm۶MO>dxtt&OPjܷoF[YôitAm۶O9j7ɤΝ;I&*..ǕYa6p 4мytw+//Oo{E# 0@'NTPPrss csv,g5f}@VWf%%%_!v]wi̙6m4i$?t]v 2PKy***֭[uuEjcw0|(&&FӦM… umIr~>{&MRTTdY,JŮqv,g5f}@V~A *..!6qD-ZH3gfU۱۴iUVi׮]ڴi+Ѩ˗kڳg|Myxxh4jڴiڼyo߮իWCs)99Yᒮ6[׬Y;wjjժUnZ?k.%&&jԨQv_hРuqvٳjٲӘjU_Q TLLUZZ4]V_%>To^i&effn+11QE^^^ƮWޱ*ѝ>5ͭ1LիvyK[,{ճgO >\l?z޽{k׮ׯtÇkԨQիNM2Ev҂ Իwo>}ZfY+Vлᆱ={j˖-?zټyzqiڴi{o\:~W;ԩS|rsTG}UXX;t:tt^NNV\y6dZR\\֯_~ұ[Qc]ԴzX n׬YW^jݻw˗fv+77Wwv ujJyyyVDD - w.ѨI͛գGLrtMSv]OG}WbXqq,ص+^Qiܸʌ* BێsI9r>3=JMM-YVX>5^4Vf$iҥC= { O?tødwm޼Y{QϞ=e2ٳg+>>^ׯWTTT6m j۶mJNN|s9m ̙3펱 5mQQffjcwsޘq9իգG_?x;5vˣ}*..hfSFFl٢CVKl0ޯ_?W#Fŋ5oWO?գ>*ɤFiڿs ( @~$88XtvIVUZZ={(??$|Zn-(ɤ*((H<$`08jР.͛GG v`0G-ZЙ3gƌF"""d6k4RRRx L&kСZzyEǏ bbbkѢu!s܊"ufgq V233cS=#OOOmذA .a۷oF4M:U#FШQ^v:uҼyԨQ#v-[L_~GTvv?͛7+>>^ڵk5|vEGGkźpf̘}cǎ;w%I{%LM=gŢ8@o[1LTFTRRcǎ)++ijwڽ{.^Xc9ܑjU\\u림<-ZH۷oTȋ/өSϪO>2Ͳl裏dyzz v8zsg5ǟ/44T>>^lv{m6fΟ?2_vPw̭֡ >*X UpUpUpUpUpUpUplTqqqMWhhhhhhh"JKK?صEM>@uS˖-5a 4Hɚ2e$}2mܸ38͹{nfd2i߾}>}~[v?ꊩSJNNRܖdRԸqc+--M.\0Vs hٺ뮻ŋ;^;L&M_Hw$顇ң>֭[w}ф |X0Vs8г>unfƌJJJ*3VZZ'*::ZO>O:0G1c}Y[O<~*QՖ6'jCjժ<D-X@۷o׾}4a„?tlI￯$mذA2ZtcCFQSNƍ?PZh3h֭ rMZryf9RyzziӦJKKnW^^~G5oij*66V~~~UQu@EGG7TiiN8v QJJG{G۷ʕ+cΖnǵ~uZƍx={Vtl6uUc?\EEEc0ԦM1l;C+VO>ںuquN-j*hܸq߿Jǖ$Ţe˖iݺu֨Qw^l6-\qEwaÆGULLOM6M)))z*?˖-Ӗ-['jʔ){*/Oyyyl~M~~~Nc:}\RQ;t :>;׹s$]} wÇ룏>|hϞ=ѣ?~pvvvs\,w/\X=Z/^Ԝ9suiÆ oC̙3RSVV7o.xz/@}'FG6l\ژ?~\X,XNtQOW\,V]5֭[5x`]rEm۶իc޴!"c~Y%&&jРAZjUp^m2WTKs^:uJTPP+>-[t]RWT7IW_+$$D/_V~~|}}զMIWNViiRSSu%5lPth4d2U԰ad0ԬY39R֭s1p7VDD g:FnZ5\pA۷oɓe2!Ch͚5NcEcƌ{=:7N999Օ+WԴiS-]TfRZZ(..ѣGd2QF8p8w4CnoBmtROVUYYYJOOĉ%IӦM^yJܽ{TZZ*///eeeiժUZlv:tYf9ӌ3$IO=G}TC q](55Um۶-SرcuرJ;==Lގ;jjԨl6VXJSΝl)!!A-_h…ӧ-Zl͜9SaX4g@K,ц *;3LرTRR^NcVUQQQڻwrssk,;Z;wt|kǎNc!!!׶m[\Rw$w}+IZv29<==5{l}ƍki繶Wlhh||delvf?^ePzPw̭XPw^mՁ*******YϪ XXXXXXXXxxxw2 8W@@N>] UU꼟@mQTVZiҤI2dmۦ'| um)??_ .ԦM*Kݻ5vX … :wogҤIR||,Y"RaΝ;8n:M2E{ј1c%IƌѧOmذAFQ˗/۵gp~AqUH҅ M:ڵk5khΝZzZj8ƴiӴyfm߾]WuSrrϪhРuqvٳjٲӘjU_Q TLLUZZ4]V_%>To߾׋T``nmڴIJLLbK:Ucՙe˖_VNc)55R֝wީ;vh4hPxdd.]^ziʕ]q2x@7nfNwUsZ[U mVFr|l6kŊzwճgOmٲEϗ$uEÇרQԫW/M:U%%%2evڥ w7ZPrssUZZ_4VTTSNUQuWaacСCСy999Zr~Gr9Rk֬QQQQ[j8_^[kXVok{N ,Լ}*))Iڶm:wFͲeTTT$tLl٢H5nX&I}W_}%;v]:rQg\~z}ڼyڵkgJw.x'|ѣL& ?R[UbXqq,ص+^Qiܸ ^* Bͭt/// 4HqyG>L?RSSe2\c]Դzl٬>@3fЎ;Կ>}vtAtk^ڿZJ\xQwVllu9eddd2iĉ:ti.W 2Diiiѝwީŋkرjڴ6lXfCsj*--MgV||~꫎M6ܨfjcwsޘɩ w}SNkO˗d2)++XWԖ6zXmݺ<<<}vIW#РA6V}}}յkWuɱO5jԨ*7V<:s6n(Iׯ#Fŋ7o^y2Gk.ѣGu 2䦷_v֯_X;oEm/OFI ~s9cԶm[Y,Gc,22ҥ *cȑ+gu)}Wj߾puEmyn>[9sF&I#GԫW/_r:_~ol>cu]Ug˖-տmڴIK.)??_j׮MDAAA{^^^{'O rj0۷Z Vii٣|Lj._֭[h4d29FUk(++Kڵ`Zh3g8FEDDl6hw$=2L СCzj,Ə/__J SDD0 l$hB:tPkXVW._\aaajUbb5vX=cz5evf8pyŋu! 0@6lRJMMU``222$I~"##m6eff*;;s/_zK_|Ə_9׬]VZp႖/_$v7Ns / IڻwO͛+>>^ڵkuaIWl]xbcc5c cU*22R111*))u%1ժ6m(;;[/^W\\nݪ<=s&Yy#/&L=ziI1b֮][ 7nq)EEE_lfG}uUXa}j[nzzz%EK6]6n/flZ捂Pw^lՉ*******Yl6YuZKZ+VE4VE4VE4VE4VE4VE4VE4Vk/`RTGMPZl &hРAJNN֔)SN:S-믿ꭷRBBB V!!!?ﯼHT9A\\rrrgbՙjs[5VO<[xv픝]vIҴ}v 0iYFO>vܩVǎ不dRZZ$I]v՗_~ݻwkJHH}4oVh"EEEg֭[&I ʕ+͛7kȑTӦM&ݮ+WT9Gun8qBÇ}Nc999zuܹ*縞pSZj6wV{_O?^p tJ_bEn׽{wFmذAv]}rss+Uk׮Zjv'ho*u *))bql:yQuF9&77WNcZtq=g7X5fn`09`nQ/A9~W}@޽J󽼼d0nRPP˱deeye憆꧟~r9ꎼ>^;vp g}cرc8ǩS8nXk{6n1quwA$.ݦR[+U Uի.]@y{{wƪd҅ t[~~~*))qXMe:UZZaJ@mV߱r.?ŗW@u . . .N(,,Na+pUpUpUpUpUpUpUpUpUpUpUpUpUpUpUpUpUpUpUpô"jn]IENDB`linux-show-player-0.5.1/docs/user/source/media/list_layout_settings.png000066400000000000000000001145051323636106000264150ustar00rootroot00000000000000PNG  IHDR{.wsBIT|dtEXtSoftwaregnome-screenshot> IDATx{\TϞa+[(J* z =deW[$Xf:YfeiiiaEWDEQdfsDn⍞wyZ^ZkzsզMK@A!Bܑf%''/8v֭k޽{Ge*PL&ӭO!B\PՀlȃ?dl<تe[B!7 sAԩa;R=۵"r`o- ]>]醶'uag/NK^_uJ{c@묧؄qZ] I)jፋ?X__ CVq: WߚbPtIK/)87g졅!D]ً?~8?R2I>'F= __8:c־պyE+]:#' XWB^(bYB[M)֖]NW.V (ZMuKϞ^ e|"b \\ؙulI,,U5k01k:qx=u$4 zC FddSЯ' /[(P3I)+ ~kQmRN | ڵmc(ڹXg_p'Gzu{gXHߒŮ(ՅǢT_>!-ٴЭ`ZlfɊљ.9p6sC/g Ҟ.ۖ_CyN\*ѿ Ֆ2ãi/O@:2?{̬<5vBNN7&4lA40N~łys T;pdOq7D _O^|&:y  g3m|y< ~XEnQZ7^C&i߶ӳ ;T*s (-ӵ?޿Zזu;ƮxysᢖܼLF#!=Q{.N5֧h0\x B2,D:_XlM>әʨ T !nZEϠݥ6d 0" עYS7.iʈ6zɑ5;, Z7rԻYS}ޞ}}uf4vvM@5B4vPf_dOqé*w `[~L&uW-q5L8ֈF>dS]5/kKX,zjS?E\V[xrkB;YSԧsw`w/|X[0ޕHIL&ugצ9j9_x E.UY2#Yb`X(bfž?`X0L^(> E^8;ӕږcPὺ7eKT_|ITSGꖞ=!D-JX[wҾm+{D@T.@vzkmق9-Bu *{j=7֥l%m.TfM5DUk.i6BVѥc;B蒖())DvsB4e}CqظDi ʗs0]y#e}Cm[vB./2O`Bn>X"* !Bi])$8:T_ѱN0B!m%blڂQ`9XT${B! fӂɶ9{B! B!h$B!h$B!h$B!h$B!h={ӸB! !B4` !B4` !B4` !B4` !B4` !B4` !B4` !B4` !B4`v՝PU{Z;`@Q, l#⯡l^ϙ3gnvgѢEb0رcBR7ߐ͛/I&$&&2vX6o1c ˜0a)))p5̟?Ç[5mڔ;whh۶-񤧧SOеkWӭרj;Fƍ+F!Bۅ={͛7M6XfSrINN&22͛7ĪUh4b63g;?SիW3d.\}GJJ qqq|7s]wd?Nqqq;cB!]ܳצMN:lÇX,zvM6mΝ`z[UNRR!!!0h ~Wz @||<cǎh" rq!BlNrrrh޼9Ty^V3f֬Y͛ׯjB2228p tޝ 6É'*lw&BqS*GFFr}?\oAΪU()))|7L:;wr9>#.\Hrr2> NsBq+1~q3|bbbx'liӦݻRa˵j׮]ύt) TCzB4$JϞ=+}w;88X94F"88_~V"nS[bڵȑ#c̚5!CP\\?'NÃ<{=N<:t(.\@/Ҷm[4 vbtЁiӦYlSlݺVYnL2JKK)..'uwwgݺuݛR̙/Pm[d w}7|OT*O^)O?-[?p=W_}իWӷo_ &ЪU+  .$!!wwwfΜƍ:t(㏌=g̘1ƛiQ;AݭCBWo>Μ9C@@wfƌ|$''3|pz-FLǎKU}?~FCBB6?i$ONZZ5]|9۷'66,$>S5k*NJJbȐ!do$%%UC0sL.\ҥKiժ#;;ÇHRRCh4ҥK O?Maa!>>>^Օ~|CB[*??PTdnt޽yǎb`0ؿ?-[!m߾}سg6ŚIYYf<==;55.]дiST*[BݺubtR, $&&i-o]:L쌟wC>!S4h۷oV"gΜ 777rssqttDVFR2x`5j+;vf͚f3EQ ɓ'ԩS[&''|Ŝڷo_eGaڴi{石m۶z45&{~omYqԋL yV סCZhQG2bĈZ0`5"bbbP^⸚'YYYT qCɓ'+ ={__ C~~~^5kп>ɓ'rBT jW! f̙3"ك .AUooot:Ÿ ѡCk L& ܒZn]U-Z ޽{)V[HMMG 8ҵYYY8880dEח~UVUٖ#^^^L&oߎN$A qT*YU\7tM}ff3Ǐ/WRRBtt4111;(Vgʕt҅e˖Ϲsef͚W_}Nc֭w1zh֬YÔ)Sgʔ)ܹR;EEE[`2xslՖ.]Ď;h֬Y.3 kL0^x^̙3ٳgOL2'''\\\Xj1ڭf5׸-áSHY_v6wJUA^> !W<7Z 0qAhOpic=8sXo&[bjH'↹޹=gɳ | ;5Ӟ:mbB!NʊϓXMƶ`Pv~SB I-?js_<˖lɾ9Ț9S/KH ]v}ַqzC<g^ld(_Q!vUh$o㏄L^Q3JϟBQkl~nѼuSQT?f87zLF"-GP]G*%Z?1vNvնs=1 !Bܮu;GWLz?biz׹y{PTDx= nŨxx#@gAQpnK9^bB!kҢ#z߼))#봘MXȦ=8k + c%i8yU`'ϻ_~L!B wd%gLNk`ZCe9q臷dٴiSHHH 233ήPn„ ,Zdx 6m_rJ~gyfϞo-o_[߇xJ񸻻Yt)+W$**R9Jg}Yz5|vvv|c:uTm$FILL 888h9s&ׯ'**hFMpp0Yӧ;v0RRRlqƤO3aWl?gС :nݺu8p\IOO-Z`ҥ}:qqqT*IYYf<==֭KbX&11H EVǙ3gعs'@@FFEDDD2ǎbX˵lْTwN^ 8*ػw/F}QRRݻwGRoa69pEEEU^DII +WbjtRwPP*zׯ{jZ-( yyy5%7C:u*o&EEEloo ((.P:hs(?-Fw1 ekb0Xx1 DDD0i$X`Af(x{{srrh߾=D`` 锔ѣGqvvʱchڴic*g}ӨQ#\]]9x /^dΝJ׮]ɩ-uf͚q TOOOm˼ȑ#G6m|l۶N !D}9Sռ7:oĉ8q> oooN>MLL 'NI&$&&A߾}qvv&66ZoIII'`ܹ̟?s=۷o'::?!n sw#¹a\ۗ1[VرcLtY|}}QŚX 6FΝ6m<38qTfMqyzzT86`5jEEEX]5lݺ^_cw<<<*SmefXprr=ʈ#sլY'0|pNˋ|С֡k={i˖{l;::dbt:=<8 IDATB7{<==),,8q"O>}>}sY֮-_v͚5L0Zm-k.Ny渻zܹs+saz=wM6l޼VUJ<^[舿??8M6ҥK$%%Y{kb0xט0a/z3gg8#+==aÆcǎ뎎ɓ ..qѥK-[F~~>Ν^[XXȾ}ի'N>TVVkƤI=z4>y׉_l6OBBf⫯BӱuVk%%%DGGرc.L2'''\\\XjSBQߔ={Vpppf˜ٷoDFF2uJs`|A4 gCuNV[vkܸ15"66 V*+|}}C]66pry4(Ji8:coPo www֭[W7Z1 ]wL->sB;V]~\枽<Ν;ǀV9{,gkj5iҤںz]dd$w?8/^wߵ6\\\Pŗ~:Rygg)+3n?]tnu7n\adB!Jlg6y4hNNNjev܉u{EQhӦMu:___Z-4jԈ|Hh4em qSLFwWU65dB>}HKK냂?CpppȴBUi镴4Fſ/R?3f %%%X,^|EygѢEb0رck׮[˫j;FƍkҥO?Umfpww~ܹsznZa1caaaL0<֤I<@&MHMMO>aӦMڵѣG[ ~### ~zT*ƍc͚5O?agWYih4UhXzmզ;۷o;6=}1!_^iӦ +Vs#8͛G~HJJbԩU={,;v 66]֩cǒδiӈرcUZ֭\:tɓ'3c Ν;LJ~ƍ})(86#pKhT(8-3=JI>BO7m7770 dfV~c„ }x{{_O?M0VZa0Xp! ̞=UVzjx x a<ԩ'Nӓ;vT`?www,Xٳ':YfbRa8q"{qILB޽ 'Ֆwwwg̙lܸCϏ?ѣ|ƌ#Bۚ={9992UO:?{,'^%gFիWo_W]̙3xyyU;~ٲe׏Yf1gΜj! q(((赅Kz/(/`eֲRs?SNՖKJJbȑ3g~zfjT>}бcG #%%R;>e˖`ʪ,ڶm?\aoUZh%KɓO>ILL ǏٙǏgqa>#.\V%--\/;0qD<?~|6gYfj*>SZb3g{7 ##ұÇteee<|DGG-[dƌ²eʪ)ͻJޱm{b6./lM /&!!&MDBB ,Tl6c0Pooorrr*LUɡ}撗GPPSRRBhh(GٙTÃ#G8wĈ9rtرcٴiʺWx_ZZZ:}Ynn.hJqT*Gaڴi{石m۶jS!nu^+ѣ&Rll,Uۻw/Æ |xTWrrrx:@aa!~~~]+rA2 aݻk'++  (j*RSS8p ?~\z=Æ #55:wڅ 5jTeYgggիW4՞={`@ݲeKZnmQ[9::dbt:!]*=z`嗒kUJ<^[舿??8M6ҥK$%%U~%kƄ x̜9={p߭t 6;vTY^7?W^ԩSto1899qY.\X/OMLL cǎʧ@RMSL!<<)Ss_ח)S䄋 Vb߾}BHJϞ=+888Ⱥqu+hޫGC|}}VZd2T`(Co(é'ׯdXi8:coPo*J}9vB!.k&ɞ˹ɞFcNC}dgg)+3\OB!pד07dͭ1^^6zBq]$&2tJs؄B/4uZB!NRd/##uUZ^ѐZaˣ :t`طoYYY,YҶkWƍc5壏>յB!Թgё'|±gy檢 $44wyg}, OOeggӫW/.]tB!M>c/+0c t$''K/Wت5@qƱfRSS駟+jҥKIKK㧟~;c ˜0a)))UyI&}__\t Qź_pp0*k+㇄TOXWvh4Ny愄RX|9&{rB!\SŋYp!-"//C1ydf̘+{׺LJJ =Nbb|*OOO>\'Bq;egɓ$&&^:uUV6+l2VX˜9s9{,'QQQ~]9sƺ|U _U8q%BQk^g… |w:{{{BCC;w.3gδV L&7o&MsN4hPY4AmۢRCUŶm۰㡇 77*F .o… 2T!ui.[ Ʉlȑ#|/^ײeKf̘ ...,[,, /"'Nʓb fΜɽoAFFFc)2y>CJqq1f''󾼔wwBFٳgqIyР+SLaƍ:QGz.Sb<& okݝu|uի7orjBԷ_zƹsnuRP-/IMM%998zq1fzooo[Bۙk`Ο?ѣG ёǏĝHlݏنb*/8&MDqq1]tlHڵkw]W_)|a!IϞ/HII+--a;RjӴiSHHH 233ήPn„ ,Zdx 6m_rJ~gyZ7믿 aŕQT|g,_իWX۶mKll,+WdѢEW{sά\ZZf4jwww,XK/իy駫mwʔ)ݛW_}DZn ?qqqX9sЪU*?ߪwwwgӦM@0˙:u*k׮%11{w7|_~իWӿk}+E=^x^RSSouH(MlEAQj6=wYYY|tԩrIII9qpp@0sL֯_OTTь=`RSSCЧO:v숳3aaaTjl63tPJn݈ޞ3gj*x̬xmt :v'M֭[5kC%;;FÌ3'**Tz*۩hт%K0x`/_λKJJ QQQ̟?h:+E=!n"EQwWm, *7ndR\ff&eeedffb6[nX,.]b!;;D"##IKK#44ZgΜaΝ=K;b`0~ZlIPP%%%\łVҥKE||<\mU BRY_~=ݻwx.7o͛7c6ٲeuѮBF q)uxֲŋ`JHH`ʚf MNNN'Jsrrh߾=D`` 锔ѣGqvvWT< >={k.~gzm=?n8X?aGz7垽jK5jSO=eÃG}ݻwOVV 2EQfժU2p@<==9~8z VUooot:Ÿom=^^^͍:`oo_:յ PRRB֭ٳ `T7[ת !D}SYz5QQQtޝ^zɺcZf[zG}Ta& OO;DU~lze%qgeڒ=GGGIIIax > ̘13gg8#[lŎ;sʕ\pe˖駟Zף<$&&?V3k,+$77{]5k裏duFII ь1˗ /PmW_ڮBwPդc]rj^^^lܸ@ C3f 22Rxp~-)))<̝;8xp T*s{{{oNtt45"11={ЧO裏۷/ @Ron:joҤI\IRMdd$1|pFcVuﯽZǖ-[I&$&&A߾}qvv&66X|)SЬY3O֭4i7o6>qkw07պ]ctZh#T!wDرXvZرcIOOgڴiDDDp1 5Vk9h41o|\\\ptR.*T ؜yzzRXXh}?qDvm}9;;cX9r$)))̙3Kҷo_;wu `T*k׮ʇPBBBse>bA׳{nڴicg۶mL&6l@II 6ll6F-jxv%\\\GQk/-^]va4BѼysBBBPT,_޽{xb[d4ָ1~w]5j[Ƙ$QBql~ĉhd||3{XUUz IDAT>f!K-X~S-j'%:VS $jIYj90H PrzZa?rF˗rJY`V=:"$$@lb_vqgaرW+jjjʰYc;ꫤr9z-Ftl6c:vhOzO=Պ^G׷u(B!n'{'OܹsIfl6N#%%Yޙ3gIJJ= <#\py9\#w:֮]Knn.w=zoӧOl(k(#Gz\B!8>>fm밄Bq9{Ӟ4QTVq|ViC!z5reO'2t:jH6#㸭s?L&~.oBIp"NfF0?bX?bH!tqqqJnn.#Fh0깙h=nn4 nnn ~4w>]7|HYY kN}_58mڴ}o>>9r$_mXW^X={k.֬YàA^bȑ. sc&RP嚟'd`g٣G}زe zHB0nBBb}Yc_qLW|'{ĎIMM59b? Mvm/h:̙êUزe ?;uֱfƏ{ѣ^~e>C իţR1cW&''tedd$撞NN5 ZͲex=&o7n$77|ŋ!//7|777OycĉB7PΝ;|r իywظq#Æ >`ܹh4222裏6l6mb, ?LII}| SO=K/wߍFᣏ>bժU 24L&Sm֙3gرciiiݻz(..f…q5tVXY ÃtϾ-bBD"b$f 3*[k/(wsα{n~mk۴iO<g&994 K,$>}:C+œ9sԩVK2n8ƍG>}.O7? QA_MMM _5V[rXT*p~~> @Vӿz=l6t:/^$66+V`8|0Vb̘1 8ooo , 9|mf't2332d}}sj]va6ٽ{7zw 0JENN{r.]("FlE0.>RgBQkWfk&̙3oxw`ʕc2(//jұcGfJILLd֭}ݨj"""8}4;wj@QN< @LL *ʾMAA}EV7G}QF1gf3C y'e0۷={ؓÇc0|۷YbFz}B%jjj+$$@lb_qjTUUի/44#G`YЕDEEj9pVv7,_+WȂ Xjiiiͮl6(Bǎ駟W_%55s[o7\w(ވPuwO?ĎfcjN7lcF#W&++8ΝKVV~iVш(pر:?_ǎ[nTUUqIbbbիpws!ٿ<… y78!D{ԦPΜ9Ï?HRRR뮞owӧ GQ oĉ9s;YH[1HII!--uӧj ڵk#==ѣGsȑVK}hxx;Nκø?l6yfyLv|oF:t(QQQ,\Zq9BQQQX呟Yh&Mٳ:tɓ'+غ+ /L@@/"?5;{n1bӦMŋ̞=ڇG:ԦBLZm;wD2j(ʍ;vC9 eǎxzz( 2Gfߧ"44ttẅ]} k*Q̄ h2ݻw1cP0z!֯_@QQ#GcǎTUUa0ʕ:BYYzCpp0cqeuWyfjjjCT=z0h +BBBt:|}}!h4#G`00uT&L@aa!1 ,`Ν2sLRRRo^oG|TTTvZ9¼y+77'i&ZԩS2e EEE@xx81|W{#\IzT[o\'|'p&obÿ?<> ٤dcߏ'~_qq1Z;v4XgXX]5k_o>jjjHNNf䐝͓O> 身l6y}Y|||lŋ_ɓ'YnXv-.Νq>!D{hדU* /(|aaax7rjфh˻ץF_];Bv9rGYVMPncW%Eѣtѱx{{c2[92!%=Wӿ~:Dg~X,fj;:te`0P[[B"IV˼yl6, @;eZB!Dks9{B4- !-q=6W!B.IB!\${B!.L=!B&ɞB! dO!…s4QTVq|ƶG!D;$ɞNd6t:𜿈8nĹ˝BW$ɞNlxyy74=G [>Vw։Q !p%2gO'5psӠhpsskѸ308-?r@: !%H'T*E xb6_zyy7߰xf3c hsmw{=VB[${B8( ]c?Akl6ōMz6l;v 66ڵ+-ؑqc%I')(x+i"MρPPp!vLjjQJHH 33ˣXnj2?> b̙dggӹsg}֭[ǚ5k?~5UT̘1իWCzz:nnGFFAnn.tԩ:+ץKXnVbĈ׌j믳~zl”)Sl_wߵϩ;w._jZ6=hqBdO'RCym"bH'1T/PZ{YEiz{{'11ѡxΝKII ˖-cܸqTVVhXd $%%߿uDGG3fyƎ˫lFѐʊ+HJJz7Vݝ%K~zxꩧ(//o0~dff2vX{FLKK[n{׏{%K8mI!ZJ=!HQT#/^l{&.}Xmf{ڽbĈc2غu+QQQtСE1Fff&6JILLdȐ!?O<x{{(jjj mM^ϛoɬY;w.o/^tqBGDʯs$pUqK8lV ?F6&!!\OVVVc رcl6cǎѭ[7mVg opy.]Jii)Z:1TUUYguؑSN5{b0|ڬf?~///n%6B\/Ip"Us*+6'wwQuqO|}}ӧ#Fd2f兢(uk9s aaauʇ6呟Yh&Mٳ:tɓ'+hrgϞm"k஻"44777 ֭[x4B\/‰s5|_qJ(//'z~-111q)޽;йsgT*jݻw1cP0z!֯_`hZ, eeezسg=<<шb!88rw&00Çintww'%%~E+P׊Ac!č ɞNt%qkzSmeWos- YvE˽/²e"11*{|&L?Oh$;;T,Yž={l?,,T֮]˚5k/㒓>>(%ʑ !pU Dڎ]3 :):!H=!j Bq= !B&ɞB! dO!…I'B$B!pa !B0yBZM~ !h4( 6 n(-%-`mEQ0Lږ !D juBvSNR~*&ɞB! dO!…I'B$B!pa юRVV\tȐ!(Qg}Ɛ!C: !WdO'oa͘1C:TOHH>>>kNzŊ+سgvb͚5 4Ⱦ^bȑmV%Ɇ Ǝ;ߟ/uڕ'N8T5jTShZ'|{?N㮻ԩS2=zرcm5j<<<-=(|5'%$$III qqqgРA̜9l:wh=l۶ JŌ3Xz5999pdddKzz::u믿ٲe SLowwga`U 6н{9TFPP\sa0()) 55C2g K.Xb+{1:tΝ;yټy3<#m{Bl6f3&|mZ$"ɞNMtt4۷o'??DsRRR²e7n͘1cxg;v,*f١:5 X$HIIdر/=h4/^ڵkyOO?<9UUUqڏ3gΰcݻw/,\8>F!##| SO=K/w \N7nѣygy׈t !dXZT*l6V͆bb` $‰FAqq1&[Ej@QN<111T* (((o߾j{{b6ٷo555ҷo_bp.\\~'|¥K{O>>tЁÇIL $Ӈ#F؇;?~u'{yyy3|p-ZĤI8zh۝={C1yzݹs+.3HIIfwio7ӄRQQjq !n Fmm-l64;&10NGyy=o!((|VΝQTuQ0X,zWx<==jX,8T={&>><ڵ OOO~|||ݝ___GMԩS EQZ-&M^FӥKaǎxzzۏ>Z'rCMTTÇgN7!DYV nnnpIH[dO'IHHβ/w^^0a?ӧO:z=,_NBFjj*k׮e͚5|۷ϡ:kjjHNNf䐝͓O> ^~e׿Żヒ^.MOjǼѣ9]#G0o<{\&NȦMׯS2a Y`;woW_}ҥKli IDATy9~x[, &'OyV[Pbcc덿xxxs a643PTS[[֡8UkСG!\9ȕ=!uZ\'${B!.L=!B&ɞBdΟ?/7${B& CюyWkdO!ZJ~ !%oB֋6DL&,S^9)ɞBfsB\/B!pa !B0IB!\${B!.L=!B&w !D jרF#`rj-[J!Z ӧ:!D;ワ/cdW!Z@VBS՜:u)odO!…I'B$B!pa !B0Ih)++k0B+B8Ydd$< =zߟSNn:VX֡W^+`X8x _)))iЄ(Ip!C0w\,Yt~;;vl VOxxtu]:uCBdW'Qռ+ksV+G^;gݺuY;Tex嗁W322%==N:tR&L`߮Clٲp6l@o^Z dʕ;w@II ?3pxܹ_͛7S\\#<J?۷vnݺrJ/IHHGdffuV 6o!M=!sh4kh4,YHNNf(¬YP,ZFCjj*+V ))"RRRؼy3#Go;|p9}49UUU7noAgΜaǎѻwqFFͳ>kFdd$V G\\#11wwwŰax)))AѐG}İaشi ,p !Ip:o\x),,ӧ6Ll6dggxz}QFŜ9s0ĠR(,,}V)**o߾0rHlقhO>ҥKz \f't2332dHr۶mh4gߟ~ ͆`;?:/ҿT*7n ??VBI;~;,Y’%K믿 $$cǎy)c֭56m555tޝ2Z-deeTUUɅ عs'C޽{3{[hd\D,XUHKKk?Hpp0jgycC6x5$$@lb_vqtBG=!/ :[6X̙3BF'/sY:ɓ,e(l6SRR`ft\3 DxGp.Lhz۝9sV!D'øB8je…&>>OOOj5QQQ2wÃ1cƠ( aaa B(.ĉm.,,^RX,GEEҭ[7T*b`ڵ+s!$$@FF6lWO`` 7o߼ŋ?~<:ubP^^nR}݇^'$$>3gr<"~O ŋ_w}Z:t@yy97n$22[o͛gpIΜ9Yl{f޼yvml6rJy)--e֬Ymq8-9皫I'n9m &OLLL ӟ:v%'${qF'øB܂LB$yB"ɞxv-B['n=)dΞBܤL&>>>md2z; !D RɯQ!D_] BWD!d2aXZޖ${B6)/BqdA!…I'B$B!pa !B0IB!\${B!.L=!B&ɞ7(mBRro yj+Bv]oڐdOdٰX,mB !B$B!pa !B0IB!\${B!.L=!B&ɞB! dO!…I'EEEInn.Frx>!CbdB !h;eeeYźunh[SNcDzqZ:IBn{bٮBݸB!ZLR1}tԩS̘1Ldd$ ɓ=z^f͢gϞOge޼y}z:~mzg}Faa!K.FѐE\\EEE,^trrrڵ+{hV{BjdW!DUWWMDDpIbbbPT䭠}Vl6F#=///X,8p .ܐpereO!D, 6^¤V1L|8-bҤI={C1yfψ#6m/^d;w`Ew'a\!הÜ9sܹ3*>}0m4DbX(++C={&>>EQw͐z=:___"##صkp] ʞ7JBQCFY,fo(,^ N8AZZ1|a۷Frr2g^y5u눎fڵ:us`0xy뭷x8~8z[2O܌VkKÃ'N\w`B܌0 7> !nh\=!B&ɞB! dO!…I'B$B!pa !B0IB!\${B!.L^&Mȧ "{7u8B!!Ip"ٌNց׵EDq[~L&\h;&1!"D'tXm6Q|C1Z,TlZY'F)97h4 tVB[2gO'5psӠhpsskѸ308-?[nnnT*T*WoFZ ɞNRP嚟'd`g٣G}زe Ͼ~ƌ :vpz-QPP~ʐ!CZ=ᚚ8| /n6h l (v@@Pƾ*(]c0-Hl7YJo̝;NGtt4gwk׮8q5(RSSIKKcгgOt:]'\د}Mh0l0vAll,\xZʞNDk4=N@AÇQ3b#OGqvm@VV Fr*++?> b̙dggӹsg}֭[ǚ5k?~Ͼ-bBD"b$f 3*[k/(wsα{n~mzΝKII ˖-cܸqTVVhXd $%%߿?2qD̙ß'"""Bxx86lhh4b (**"%%ߟw?7y&::۷Obb(!Z@=!HQTU#/^l{&.}Xmf{&l6fΜ7|;CFF ht>}`fQYYIvvvw}hg ^ cJZnv]o8#(..d2uVСcDH DU˫Gqج~,]n4Yz5YYY1w\O,±cǰleǎ[n ?|0v['N $$EQVkb OOOUh~7pyw$$$$‰OW7؀qEu]rlfTWW /\v$i:u;vwUUU9s{w͛7[oj0<{,bp] F}ח>}0bL&?^=t2+]z} kc=VEQ0a2555tJZfxxx0fE!,,z۷޽;w}76m׶fc̚5xj5z"""SN@qww{سgۏMxxu_~]o4F\\Do%&& F#:_ zc/z+m),, |Zb8TFPyD:\-;vd׏nK.i&9l0ϟٳg?>;wk׮̙3 lذ@6oLaa!l6.]_}fΜI^ѣm6L”)S0p@&NȥKٳ'g&((r͛бƒ%Kꫯͭ;6mb<3;ӧZs5%ɞeg40Mxy7J(|L_];o+' ڌ_FK_׾`rx#Hmz='(zt{7ޘLVLk0J) Dڎ]0 :):!ڎl&mGBh DV^^kЄHȜ=qi9{B!DK\ϹF"B$B!pa !B0IB!\${B!.L=!B&&{FQY:!${B8lFQs"㸭s?L&~.wBtB O|}}qsӘM^"t:6[/{ ahP#jughP.>2 t:‰j 4h4h4 | #'|OdV>G* JFU$00z_ q=ʞNRrs<B1_YM>>TVVm۶6KܜWs7ؼy3$''i,ɕ=!MЮ1@Q쟠{56 ނt&^ ǰaø{x'ׯ<@GȠI&1j(yN<٦qد}ñcUތ}`ذaرX:$|4w (({;j&55TVl(~eZHT*/&''<|M܈bݺuj5eeeR1cW&''t$H222%==N:5BJJ deeqF#:tӎp-J3ko\@ff&%%%5X  Dr$e>Ͼ-bBD"b$f 3*[k/(wSEQ c=zҥK7nƍO>MX3f < cǎW_l6hHMMeŊ$%%QTTDJJJuΆ sxՎpywN7l߾|!ZB=!HQTU#/^l{&.}Xmf{9G&R\\ʕ+9p>|͆h'<<Ѻ&""EQî111T* (((o߾zuSUUբyQiG7FAqq1&[E!ZJn‰_]9IU(ӏ.Y-X=]U16l_\R1e~~hpBx Ο?ҥK)--E@VVlUUTWWשĉ( 6֜vmp`v7IHH._鋏7${B8}Fx;Nκ+Z-ψ#6m/^dl6呟Yh&Mٳ:tɓ'7nUUgΜ{e7~shv7|}}ӧ#Fd2%Fqp+W/~9/usB Aף%22SN@qwwm<==jX,g˵l6g֬Y<VիߜvVhF\\Do%&& F#:_ q=ʞNtIbmp֭[Gtt4k׮ԩS;w_~e˖))) 0ϟ>>>_}aHNNfټ 3o޼ۮ駟f̙ו-_qkSbcc]ĉm.,, |Zb8Th4a0nPp꘾Ow57"T!nZFӯ}ץ}CkHk~K D>>>(ݥʑ Ѷ7|~FB4E=!b1VѡZ'E'D۱(}CM=!j{  BW!…I'B$B!pa !B0IB!\${B!.L=!B&&{FQY:!${B8lFQy#㸭s?L&~.wBtB O|}}qsӘM^"t:6[/{ ahP#jughP.>2 t:‰j 4h4h4 | #'|OdV>G* JFU@WHYYY'TF\<πP 5zP7nłZ~`…߿qΘ1 [upfΜItt4>>>TVVm۶+گ-מ7o%H')e;CtM`2 O<ѣy7?~|ڵ+'NhHMM%-- PSSCϞ=t-nWc := aÆcbccŋ-nSdW'SP WD5'C쨙TSY'4+Nܹ3DFFAnn.t |%eƌ^qssc 43gMΝQT,^x7qss#00O0IDAT?~<&No*=n6 l/#5 m^~$uT*CmR ^:e,ZJ:tC cBhiR[l$,H6rv&ss~9YҼyT^^*}g|סC4l0_>nkϞ=r\]WZzvڥ+L= ,EtF 5/kPmTzj 9wB r'+ár-YD555>}͛\#F}ݧiӦ̙38pZ[[5|-^Xuuu{ҥ:rTXX={hڲeLVNo+;;[yyyzw9F/r!IIII1b~(>>^SLѺu_f,KU'ګ;^5c2.PöW E{oZhĈ*--$5J6Mm۶;nWSS/˲tرqaB!|>8p@yyy}+WS0޼<۷Oڎh_ I0av!߯: 6Lx-f,KpC.\0E`@<"fE?}O֊+sN:uJTUUUۺn%&&? /{NgΜҥK{No{Rrr_gqQegg˲,B.OMMMW\zOƎ ]wݥbIg.aRU>Gd9rtʲ$A-\PկT[[ӧOɓ:tf̘16lЦM4~x-ZHӧOܡ]QQ&L{LgϞ՜9snN8'j֭B!ׯ0x>llDloHNNȑ#5a~IҤIpEXbEׇᆬ_t_W.hР-[駟$۷O*** pa)11QYYY ڳg<RSS%Ivxبdr-h|{PH ,PYY^9Nv >\:~RSS5dkc# }ӥFcP{ =IڹsFt|>edd\7p)= .\/Tm_Kmm_3fƌfhƌZv裏JrssU^^5k護ƍ IڴiNJ9Rׯק~5khɒ%ᥰKi|g5sLR[nܹsӧOkٲez饗TUUI&vKR>o46:{=+(.. gj8q<***k)###woftY&!!᪞!\rss'v|~y}~KP/OSRkظϥI c+ךg!)\tGu&%)))I~+zWdžclCb(hUjj2zjiiQu@ *5%EY  x}&Oŋرc11qSAARRRbP"c,KNʿ4Ωd)>OIo֙e.u&--Mk׮~3PMM~aI҂ 4f=֠A$Ir***n:+ZbfΜ 6hڴin6l֯_nkϞ=r\JKKSMMʴzj_^z.,KSyyg󩾾^ޑkc#qAqqV^]vK = ,˒%Kަ3jyYA)>叚Q_{T!\?{@ Y*m1]newa+ Z"}3R߯V544YݨQdT[[+Iڶmp+W) Fɻ+ϧo[3yyyrQO[ FƆ$M0A;vW]] G}<HC,1dvaׇᆬ_t_w^К5h l6v۷O*** חץs?~\2d5zmnIҐ!Cկ~U[l鴞B,X2{r:>|#ww (,,޽{AOvܩQF)==]>O@@a Ξ!ti&M:U9rURR3fhڵ֣>ڥs>}Z˖-K/*M4In6%%%z뭷?ixz:S__3gPںuΝ}C0666cǎ={VĉxTQQ^{M#} :-'$$ѣQrss#ޫUv]@ >_^_"$t~VԤĄx;ԘKKK֭[;bluZ1t:eY5;]$%%%pe@bl=P ЪeeeF5{zza`0(#{ @=\wz=\k6.{#`0{#tAvڲl :<9jpl6PH`'W) k#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{#`0{4~9X8)e3s\٘9NRRRz b*PH4p$߯@ `0ۥ3 {,u}`0{#`0{#`0{#`0{,R(RSS`o8l6%$$\m,וz.:Ghp(''G6[XX|jllԹsr"ERO[=У%IP+PeYZ[[ylU@ҢvWr^rwzcf`0/lËu.WO{=jЋֵ?qrrg\RSSU[[ef@4󕛛t:BwimW3;@ 7VII>,I***?YfԩSW\D1p04tP-\PZhu뭷)fK=nv=3Zl{9vjzꩧtȑ^#GԓO>}Z.}ѿH={ɽZaЮ]"3f̘*]I.~@KaJ\+#e*ۥ\@ XrG=ff Uwz뛟:3zz/2xpv_q.K~_}&l6EX+E1p8p8zse\ F0a`= F0Dn__,xIENDB`linux-show-player-0.5.1/docs/user/source/media/presets.png000066400000000000000000000574761323636106000236270ustar00rootroot00000000000000PNG  IHDR+ UsBIT|dtEXtSoftwaregnome-screenshot> IDATxy\l "k;.bJc霓s*5ҬNi~%++M_K,+*nAPA Ea~$@E|<2w{Rn}U_jP Bl\x1g S_9pE߾}"~d2tBQ @RǃW$:rǷ[&B)gٹ:J\ B!D zs!vspppVt#BX B!D!#Vf^gΡrs㆔cm]͓ B?2vd>ͽ|n2Iy۴DK6\~C\ٌ;zuC;?JJ|z^yqy["._l2Q[Oi0*N@3OQڹS˴Sۄx(E9Mpqv <R$үOmZCX,(cN9MGk]B'qܡ?m0MP.ЅA}7^}0h@wQOU oZhv B{}W:wxj9B fV0( NDUl a[ܯs2%R>GZl %t#&uÿٚ7ĘM+#P(9pLx*m48r {v*sv1+%F#ݺX@ :+U*tlÀ>]P(ltNr^4 bC Č)Oxb`]ObD/a6iՀ}7~-ӻ{f3_|>;R83rX_5iDuh$9EvxlPO [f]=Js7?xsj_J4{S8eK]?P}'2y,G+w3h@w nh9~*+W HN9Epr[PԘMMgHN ڷm }N\z̦qx7kK??qDiAzf_.`0ɻZ@FfM1-r/4kҰ9B>Ҕyiʟ ;F?Μ˴;2puqbS.:1cʟۣщf\υ0d`;_!dgldϡdΝZy]ly>{gkYh6pO{P(Px7ڵm| zΤgѡ]Kr.]@mP(mV ~=;SOFD̿Vgg_`̍%t_\dP#lJuu6mF&p*fNҵS[k.{MPT4#{k&B=j ''Rr/_kڗKpڷm@(5r,5 łb-Q/zYO\%e_b<~uӦeth_.D%Sw=zDڶZstǰZE&pP*.Nt Z=v?<1L}&JF'>}ZZ♐a?gPkҎi5pGSOWCT*m|)<ڴuNR1O%GX͙b2WOCޜIb^)d8?`(J6pO!dqس:+eF_t{GGWlY)Nq;q˟k)Bwꓩ8P(da!ă-; =uPiNM'rt) !RYZMƔv֯Q>LQ6gqvL B* 4jN}7vP8JU$CEPܓz[NГER\ѐ@RRʹs8|0 ,@R70cƌre/Zw}ʹɌ3عs'ȑ#,^WW!DSӁBqn=tGGG&LPɓ'NNh߾=O?4}'$221cƔIV9r$jf5lؐGbXʢO>v!dʕsŽ=,,W^y3f'Tl6IRR營5̀t9rnݚkגD\\>,p97|CBBcٲeY]t!""駟 ܹ3;v gVX?7oflذ-[V !J5߇;v?ԦVv 詩$%%1}t^|E8w\  8,z=ezDDD4 Wfǎ 4S2sLlf… :CңGh4|׬_b { ,ɓ,X#Gzzk Ν;Ypaun!RӁ~ѣtWW*1[VvFtt4L8QFѸq2iBBBh4ƍ^{5>spkbX`;}`lْ>}̆ 0͜>}k׮tzBT;3g* dSYBQW 6p &::zߟYfѨQ#RRR(Ϯŋlܸuֱfrss[2k֬ '$$dZnyyyu߮iӦdeeFsy:vJbڴiP~}HMMˋse4iϲqrrBV\!DVWmLǎY`((EXz5ѹsg, jsC/p6-))a۶m[v_2v6oޜ dXgѸq?>h^^iii7Z) 8{RRRž={={6\~.]Hdd$f4 X,w<0?[=ׯwqƍj双dرYi:@ii)^^^>|GGGy >>>L0ooot:Z7778tNNNJھMIIˋ#G7Ȥ8!5j۷og֭XhԨ霨R$++JzKnʳ>w}f̘Aqq1 .$%%_~]Onn.yyy3{lΟ?^k~_ԩSY`s̞=>7CQ'=,=LWWW:wLPPu# !<lsb}]!n= =ؔJ%գئsu]~~ЅA&π2LtNUh!nM ^ۧ !72.JvBd2;"]!#Rh,36tFݻ۷\Ǯ]2e Oرc~͛Jbʕ֝n0|{4R*Cu!# E9& ш^v;880~x֯_olĉ待FDD0gJ%-Z`ٲeԩSYboS bڴijfڶmŋiBqFV+A]T*Q6v~믿NDD:777Oŋ?Y.l&33{ϧ~ʿoZjEff&GՒR.'7oĉNGXXcȐ!T*,X@\\;wfٲe}͛7Epp0p}s+d"))bvލlfϞ=T:zJ"66;wһwo*eYY\5jT&Mdd$s̩0bb"-8w\JBd&MNLLlj.W!Wڗ_~Ʌ 9oII ;v`i #(*Gzz:O=]B!5YPPڵkqFGEE1fK.]:t~9_TTJJEJJ ^^^7h.mB!naAVVϟKW^%,,5k֠سgQQQ1|pz-:ĴiӘ7oub]rrr& !-tnxjJC5OW-bFۧQAFC d8QFII Z-Be& FCÆ yMhDPPZZZ]0-_ >*..ӳ!pnnnw婈Ѕ>2U~F=p\\\0(JYti]zrr29d\J" P̙`@PXXHdd$>+Wȑ#|e^hwyMBWb!44 \\\ bL4г[ё &n:g'O.hBxx8fBTҢE /_ΓO>Idd$/BV9r$SNW3B؉N#&&W_} [f֬Y4jԈ˗/h"qww端'''n:J%~!ZBp W_}ٳg֭z+WңGR?fϞ=30^y\\\͘1O>fL'&&???i Nȑ#{zz‚ %))߼(oDzePt҅$k* ׯRd̙ر6l`}ߟ͛7{n6l@˖-uۄ!RkۯŅqƑJqq1j><3޽ٳg[z{{e?3k,BCCqssd2_2a&L@Nx'fܹ$&&2~x֮]ˌ3X,w6[@OMM%))ӧ/WsB/$++ ^Oll,cƌ &"" &&`^|Eyڴilf… :CңGݻ32d^}UJKKh4^AsN.\Xͻ$?Xm۶O?ѱcG|M, ;wFTb!!!]T*ySSS1:u"7nb!33ٌ`̙3x{{[=z)..L&1{l&N͛ٴiZFTTׯ_gƍ,^:o]RTRj)f2lV@h4fWZv=mB[F+JFc}.l9oBG  ۣ5W_eС5_!Dego8qbtڕ.]0aCHHQQQIV "**^ͬQm۶-Y/[@OyqqqR IDATn>#>}:}Yf3ݻbcciժZ߿?Zr===INNf޼yl߾]v?\:RW_}E\\|'j2[6hЀd4 ڵ/vbݺuֵ===ټy3/2wp`RɫJtt4qqq[ZMXXd֬YkmSQ~!SNw^BCCxصkiiiW(пǗYxԨQDEEU:iݝ;w2n8^~e~my2if3/fĈҭ[7پ};AAAtÇ`ʕ^{X}kG}Nʵ[n111X+WXsqoWTTĴiӘ2e qqqK/U͛7端bΝl߾?2amF*m[E~_Q~0 6Qٳc<k5)SгgOBTh4]?ƅ4o>JF6 ɥ_mѵkWvQBn5ӻq̙3ɓ':u*!nz܅&qqq̟?^z1m4֯_o]{Dj5$$$o{9r !.cO׬hXhW&::???:wlvwe…ٳl}%'''SPPȑ#1L5 T* eΜ9 4 DFFrJ9_|QEa0,7/f2sLRRRKBq7[@_jUgtGGG"##1ͤQPPP.W݆0aB&O\np:uDy۷/O>$3LZZȑ#.|eΟ?O>}(,,n4nn !(kԩ1zШQ#222ds;[@ W^l݌3O*Lo6$)) bbb+3|=`t:G.]ÇeȐ!t҅$k:JEFFӓg'9s+:$%% KRRǏ/NT7ߐ}XljoI&Y5hЀ4 l޼ݻwaZli3<}Xpͽ+$%%G1tJgazxxCpp0/"mڴ)l6pBСCѣAAAO5ʚ.00кnի4h;wd…t]vE1d[K`@qRE;v X,8;;Z[iEPT*k|@{ݻ3qD.]Z.MHHǏԩSDGGs)ր~޽{Ć 0L>}k׮rػw/%%%8qCT.Mzz:iٲ%;ww޸0rHmF^P*w1wA޽좶j*JJJdHJQZ``޼ylٲ> ܹ'x?t>,/qF֭[ǚ5k-|z֭̚5 ,Y֭[C^^ux8w\45jT3JŴi ~ʵkHNNfذaܹݻK/1j(<<N﫶ܨ9rcR^=˜|\rŚ7??G2p@v"1uTBCCIHHGunBgϞU@NNνlOZ ,,mBCC !6ooo Cz=NNNw֣gg*KVgh~޽u]R*rn q;l>\]]4h,/+`qL&F#zUDA7rH:T퉂B шV.P*jl[g抄a`4 ]{E܅B:@BQH@B!{}rS5 qqqTLӧ9v߿yRXru}̟?B!,>qrZFDDеkWt„ ӧ!!!DEE\&Z&((({5S!ڧ~믿NDD:777Oŋ?Y.l&33{ϧ~ʿoZjEff&GՒR.'V"!!7Ү];{=4iBNNs%++ Rɿ/F#},[{ kӧի*͛7s N#,,~1dT* , ..ҺvVt=(CVT*m~O:޽{ O>^`׮]U^Pмys_z==˗0j(*}OG姟~bᔖhXr%~!111瞣[n<ӌ7|)--uo0sLNG}Č3xwygy7cBq;^FYfb(VKaa7iҥLriƎÇ9v[nl޼̰{UW^MII f={R`ΝJVٙGyBaӚɘL&(..fݘfكB[L& Z{rqh4xzzZNR\vv6?k֬'77FIɜ9s*̟ȢELJ<Ν;W4i;111eĹsxXd ,^}t}߳JukZB]4mڴ!pnnn\|aK~\p󖔔cFX,U_1//tz χɰaX|u/uggg7[B7oDݧT*m_a7]GEE1f)oJJ ^^^7ӷh'''6mdt:<<~7h޼9aaa8;;֭[9~80֬YNcϞ=dgg;-?B܍l"vtY i֬}n.]dL.u]mlFTҥKm, BqnX %##?>&M=$ !NGLL * ZnͬYhԨ/_fѢEdggW_}ٳg֭z+WңGR?fϞ=hтW^yJJJظq#,^d6m@~:u*SNEhw;Q{X,\]]7nh裏Xl f_b͇~Ȃ 4iseܹ|<̘1$k96l << .\@|||rE._LNN]tnNgPI@B[,L&K,!,,Dz*<򸻻[ϼ<}>oҤ /_bǰaøx"qqqX,;ː{rr26"h4$$$@hh(餦r9>̂ PT|7̘1\ً-wߵG3n̙ 6!ejz({jj*qqqX,N8AÆ }ЧO={6'Ndlڴo6YRR̙3 fӦM,Y?TZ-)))\vͺsͪR%''2w\N͍]vpB^}U ߟYfY[ͭW>Lpp0ً~! *mK.,_T*tJExx8 0gggVX+$<<'NЯ_?Z-/faDD5cѣr1z}>>q?.BqqtUѤI<<<~ZZe !UannsޒmFpp0wnwvvFPTLURG^^iii7GQT*JKKeNܑdw+]~7nT+dd$cǎ%((JAǎqppmrnRJ"%%///F ܜ'вeKKQ{)JFc`f~<==ʺWxb֭[G||ĭE{_q=R<Ôeԫ/{ !j^FYf(2P*~SRRVЦS=^omåy{JnpyZ"}dSRpMv>{=Bq/L&4 6eDOOO UvrW;էI?q?oa68>`~dži׏qj@aq~:OI*SϽ -X,fN|wa.-Mo:NYBAsoQ qoZ\SVdή}c\};QEvgAa\ڽߑ/;"}/\~@q7pރ'Sy+B蠵`ЧgK9ڙ$L:Z-FCE`+..iӦ5 sss5K7I\K=Qj19s4̅_qteoLL@}_߽ݥs\ڽ^7w6S(87moFFrI)-ւł\j-+m<,&m'h)N~)xKQw}7+_3H P;2?h6h*!l6]"ET*m_a~xXJŅ4>./}@AA,fq,R `.lD %ѸzޱCFTa2Eѿm~ܬ-JnREAAT4tQo|p >܏BD,fM< n-vq/ߠֿ|_AIk į[4_C~FmEQ{H0z7#qn֖A!sz.Fu`M\\ۡPT}Ro ©U3Zt/|p kqmљqȏQ9gKPT=kpjܪLzƭ^8Uk@@q,պ/BC:Ph7i!?DW1ܽpnGK_ CAX,Uhq0(8Ջ\9oAιu4NVb哾z ~ry42鋯^ı7( z!'wuǫ87.s랔\&/e Eyx탹:R*c2vfN'ܴ 9Q9RZt]¨ʱ̥%]!cQ9gmb1&}JHo%\ڳ,^4'xc]C{!3U[2!ăbn>^˖-YqqTYYcjȑSll:t?\gs5+Џ<CKvR"$JR՟Z hVTTR1B6l׏t+++ѣG5nܸzokذaz衇tYM<^?Ou|>EGG[j=mSb=)77W'NԭުY,uҥݫd >68Or\޽{rԩl6, Ou(>>^=zn׀jf= Vr4bWpԹsgM0AYYYQAA^{58p@ᆺ3zɓki׮]zվ}{M:U}щ'TVVÇkѢE2jݍl[n՝wީK.K;vuw:˲@#q9b:.Ρ7 4dȐ.{ZC8B#իWkҤI:reYW8_˥4~x۷O[nmv <,ƪ@ .Ԏ;]B@5][n***jh޽{+//O .ٳUYY^z"y:Ν;kĉTuuV\5kȲ,͜9S]vn޽{sϩFzRvvRRRk׮ރ?!!Aמ={4`EGGkҥZdI>,Ҹq4l09jҤIQkk)))Q^^?^@vI&)##CzuM7iРAׯƎS `?M/6MSLѼynݺڟ'΅uwH"""jԨQ*//o@Rb;v]fҊ+zjeffjܹ*((G}yرcZtRntUW5xÇtgլYtWW_՚5kt̙:deeiԨQ{ui_Z7nѣ5e=cuzpBۊf͛-[hڵu~_t:~.]~j \! /Y߾}oرcZfnf~=zT>On[T~?Ϫg}u^Iڷo<߯J맢Bҥ$X~_W]u,ҦMqF_e@맪*>v~_NzՔmil?508B0Ǐ ճgOYcjȑSll:9rI& ~nl6[>s͙3GO?N>s?TjjlٲڶŊ|v秭b4u)==Y TCc%b£ReddY.==]%%%>| zHgϞɓ%Ieee>+EkAZ~EGG[ŶEjx?} SbLٳGCF$uQ~󕖖&˥rƪ{ڽ{F)߯o߾u꿡zp(55U555ڹs\.w^%''k;ڣ}׫٣:hСOWVVSNl,+`?ϧ\M8QzbbbdYԥK(>>^=zn׀jMO-1n8p`r8*,, z%Mt:`g6zeڵk-[{Ln[JKKŋn:o^SNU>}t Zh͘1CGǏW||͛m۶[+/C 7PI&>GnݔhܹsիW/M>7Td0~$cǎh<^|>_ko$s@: 0],R?~P:Ulh3DFFʲ,9.Řz)cm`HuYg nB\_|j2UMtfnWRRRyPuuUQQ.jbL10t4qշ/ǮŲy։`sQDHUSy^YdlW|hmS/e j3Ƕ>}J!eF!cjsΡGw`=Lتӡ}>_Ȳ6sB=6g 8m5 J,- 1+7j /b^ӿUk /7VNXR_]>Z^5Uk|ڵkӟT]w]6NWݻw̙3ohժUQǎ#F˕"c`NO2M8VВ]6YΝtDi;֬_pnݺf46^]}zg+hƌЈ#4o{?y.O atC_;Xڵɲ2=szյKgJJJTɓ***-uWre+5+P?=讇$d.m ?>S]}r\Z` 뮻NeiΜ9ڲed_^ۇ/[oun;wĉj\Rk֬QBB^xm۶Mv/_, 4Hرc5uTp),ҤI4gm޼k֬n?:tYӧOuȑԼ_֍__衉ٲ,KqkpuI3OD ^^Ό*,,cteڼeOxL555zl}#owy=2 }uRSRT*22R|~vWLOӞQwGhgەԓZbywdYڶcgMԹSzbG~_NS3gTNNƎiӦiڴi3gg?}~kӦM;w ڻw 5k,XBWVffΝ>|XYYYڰaF-+ϧ\mٲEk׮m@(54)%%E7o~~~yEFFHOhݺu[(Yz;]UUUzч׼v{ßkOuݵJW_xa zCroY+)[RQQQ/l*)>{ɲ,m$i7""",]WJ۰Q~_.W***Ԑ@[Q~^Ξ=Ľ__s_?նmTUU۷jǎȨ?Tuu8={hذa۷o!ϧcǎi͚5k[lzs]CTFFN8!Wj UTT(--vL{ꩧtAr-j߾}!9ǻURrBQQQڶcg+8Ą$'[.ͮ .Tćՙ3Z_K{'JNNR\Z0ڶ'JOhWg@풒tIu'91Q Z-.|*ãG*))Iiii:~x ճgV+b4Й(ƪQm߾]yyyzgt_lZBrzJIIQdd ;?j]rR~s:uJiuvPZjNlZh6fkȠ'O/OX8eZ|0~vJСŞF /?T8p@Ȩ~zzJGYCp$-ih*,,ԙ3g4dm۶{r>cUUUlŊ|z嗵dk-Ren ikO ^_;Iw-[u!9n!ͦT:fmxoSrtff՛.$á$y^d\Uk))1Q_;Xҹ2:K=zJJ݁k Ͼp_(IVlllt)5y{!Cwў={p84j(IRǎu+??Z*++թS'l6YŔ;8z5{l=6lbbb|GƍK/TgL;?5I͟?_ٵqa5b yW.3CEںl64b;ZVQ4/9m<7K?pugun-ʞw@/򪪪_鍶_{>w>Okuk@+=c;ǨD>Z{3T\|\ٺ{v+h߾}Сͯ_^9996lrss{Pn>@SN߯)Si߾}0aF[zG?VZuuyY1.yҹ쥋~~8E3fSv.I;Qԙ|Ξ=C_믿SNBKMȳÐomi@ ?`:ƿ۷7ik˩@BMTN/>S֙O7ՙ3g9~ | /#""TSS_jȐ!-WSx.UKӜ10@G8K͐XK/xdYmr}> lzLmTeUF,G| TcھvQQQrrm꼷hr@RKmvYvʊ %e.Un_ԤMeY$=^ج@m`u@: 0t @`qRIENDB`linux-show-player-0.5.1/docs/user/source/media/replaygain_dialog.png000066400000000000000000000357011323636106000255770ustar00rootroot00000000000000PNG  IHDR|:>6sBIT|dtEXtSoftwaregnome-screenshot> IDATxwxTe936i$! %CP$ ºM]d( (ŶVP\])_QVZ@)D@$$L;#f$0BBK<=gSFzh޼ 4y̙c)zw5Gm'(NާٻH&M:k;2!9{rU 퀄B:VŬJB;oZA!$|!@!D ;v?|5;-gѳΝ(q]FTU qhV>V~>U꣰–9SW 2Ҽi#U-åáqijT햐e7Ӽi#unWfsiN`xgϾLtUip>77UU,}!,أ;ݧtnEҩ-פRU]-c qXIOC_/dP`.dC̅EUjq⇟hۺYml6vIR5fÖ<|5K~~QqX_ ttnȹ[kqlyvm3_7LCtBG 2=*{Y"L _aϾLWip!7sa'j_P|dR/lА;/YRPVRÿC9TU˹.._#Ahaj1?iWK:@טv]/Ɋ=wMӈ~j-7Gho >Cߝ`c0ԩ*IOWҺUNk׿$iϮ1wjw5L3|rb:&"A>'Ne`7Ovsy5iXji,^?Ղ_PG\̾BaQ=<Njs5L{w/2$e~3y8YYX+:GGa wk%5iz}EKӻG aor6E4G@ O~ݑRmG0t t:14jRn%|)!.Y3ޜE~7nTEsZ! EQhՔ3W~)gh ӦUS|C( :J0._ΣMd]|3mcXJݵoϜ33mg9{>Є^#$8pS(WrzY4!$8+OؐMJ|EV+=cPUt&RzwǠ/uNyu:Z5நu:"!ܯt:Yʹ*;?w^˥^+v{ bh'QߛY.[} qdKWm^H}Ƈ,s feqwJhN'{eq$yW p84j ?_Z4Ӗcߟ&a*yWhMe.18_Xvhu>Jցo3q_Ohp`E Bz]_-EQu8J#(BLt*l[LG F l_ڸYiQq=x*:oE Gi_wmg8|ߍDџo2\'۵iC0B $8Ȉ #'ԾuVgU8?q}Nc`nݸ%A9?'+yyvsE2f-^WLU݇z~8Gӹ_6&$8bXq'Ѩ c8N4+t8I\ǡF~~!Wub9ڶmf߁ôk(~27۾`1 p84.d**N>G`BBn\ιV75lh`г 4s!s67c~>ކt S`ȹtb[=?qsJ5!AlB߰y'{ʯ~+!GwnjO=uw#fcjv@UTw \w9a>Yb{vb7/~>|0wAA4 k@>wwٸQ/[vg΃lvL B+G^ΔgU;MQo( ']ZA۔tԖZV9kEwh]u:/޾gest"#qߐ~w$yNwe_Nc;]W8Ǝ/UQ|D??l}[bq7-|=8{ep/WDQ(|Onm+%qΞϦ][9:أ;RПϷF~~%{Iv#@ޝk;!(la.A d燧xu&M9vGLC+^'66>z>#]?|bcck8ݛC4i҄&MvUqלNr || :*NWj]n^׉_;vm6v'|Bv4;ʕ+IMM/sDDD+`4=oر۷0tP 07l{~_~.̙Ö-[شisΥk׮5ݻwm6vɆ 8qJӺuReff2l0nbիe˖,Z={yf~߸İh"vիH \0 uzBZsXb~`Ss:=UQh.tQFѯ_?^֮]˛oSll,fbƍ5A1a233+ٳ 2|lݺ5&!*<s{˗3fL}_b֭9xfϞMvvMJJ ׿5>(]veذaիo&Z~mK>}x駙1cԫWO?Xy|2obBܮ 'Of„ lٲT1k,ILL$&&86nȠA\iԨ={TgE}^q,Y#FSO1n8˴{饗HKKc$&&ri 3gd$$$O}dr ]tߟd4Mѣf8O?-7ɓO>Ibb"?9<|A7x0 l6RSSYres4 .ٳgoOn4ƌûKfHLLˌ74lؐ,ؿ-G,{<,`eEQ8]̅Exh=qڦ2<'Od޼yڵ8J;w??r9yd2zh "00#G۷/[lSNL4zѨQ#fΜ̙3 `֭YӉbqk@ 3gΔz@ҙ3ghӦM &ǏWf;j(?h$::S2~rUXVK-[F\\/˖-s3ꫯxWݞ_ M\d0?>=[n%>>>'uVoΐ!C\~m.^X[̙3^q_:Ǹ\~\F6 ݻӳgON:Uf}PP<(do97lY5~@?^Oxx8 `߾}(#3}YY|9YYY&/_LFF={^p-ߵk>,ߘ7w$%%UwxB10ㄯ9707Pu:UEQUTԺܼ<47]~=Zꫯ2j(Oü%"""xW0رc۷M1tP 07l~zvmرc> 33aÆvSuҶm[zA.]\G_h.DUu:=Juz z\VzG}IԔg2d֭[c2<#̛7mFE~HHHˌ3 Xz {gդпL>+V›oyy \cر=zٳgbܹ\>M;)SЫW/|Ii֬Ys,Ovt8p}̟?SVMjjj#իW3xۮlUS|||_>?Ο/Jp7p8PU0᫪p8n%SN1|^|ro(0 ̜9͛7@RRO<ݻwcǎfYh7fdzb ^{5RSSIHHO?uլ5Mc֬Y$&&HLL qqqƺgW۷zsαrJ 3f`$$$e^|rx饗HKKc$&&rz]t) ӧOg 6y{'(jՊ#G}v'N@dddsFf3]zZx(__hyuQ}_ϣTs7駟Jbbbu1118N,Y$''s,XZv߾}hΝ;)**bΝhƮ]J%'Ot:ZdffҤIɓyw(**sΨJjj*7oK.nߝ9^+<<ϻ^wq8=zR̟?[_ٳg]4M#ZP[ݎ W2 _6Q{C}ԩSywٺuku9sÂΜ9C6m껨9ѣ'((@9r>~a?ζmۀ²e\mΝ;79 RL&?^ zFF:u*Ǐ'33(NPGIDATן<6L)Oyq(,,}9ƭqsiFK6o̳>[C/^$22EQ\ aÆdeeyyȀ3f yyyL44nܘxzȵ,;;'NT"O瘓Cxxx&)(( --ݻwӵkW233QUf͚S뺂UT'ꅨ-t!!T:UWU::U*cp8< l̙у](3gNhhG_oժUDGG|r\Gfsg:ꏉ׿5-Z`Ĉlݺ3gĤI?~N[ѿivoh/+ʄWBx IB%$ !/^BBx IB%$ !/^BBx IB%$ !/^㇧v)rk|} D !Dm8 9(JTÃj!??ЪG)y\)X  z}?*۟[hh({ƺC'66VVz"ĝJ5|UUQw_3}U';}v1W^h4v(BԸUEqn7|CnRhuM֭1LGb%Ξ=ː!CϯPqU:m5x`,Yb!..+Wee˖vZzꅯ/Xre5k,lҥKWK.eȐ!eyL|8_1b+Y f̘ HHH`˖-e3gd͚5$$$co߾ cY|9Æ c޼y~`ƍYx1X^{TOIJJaif"11Dbbb06!*$|璎? J۶mCԫWT={`Z~9+[etJ΃ bӦMe∉tdN'O&99СCdddPXXHÆ 4VyDAAlEQ8<;wFUURSSؼy3]tAӕھv;wUVt:)((ի\Y?]tߟdG%77}o>4McΝsN4Mc׮]DFFV'Ot4iҤ؄<.(?>fӻwo 8R֯_u߿}e:uĤIl3gp:egΜM6e:N,\XdǏoopef͚ݻ '$$Ծ;w~~~]XXYYYn\Y?&Ǐi[}]K1=x ȑ#)D]Qi F ?00 }„w}ѺRn'--d}E"##Qŕt6lvdx:~JJ ֭>HPPP1fDDf|鸄U-wQC eƍ^Rٺ5Ν;3a„*㮚v*>>>ewژB<FL<7j8[!::+fX;Mj<ƘB@TTseʕ|G4mڴ8(UeX|M]۷g|WL:fر|嗬X>;'CCCYb*k֬aӦM=:j*W[N޽{ rm[o~z{xYz5)))߿Xoׯgʕw}ͻ$j^>}ر#qm"Da~a&MDRRtLbb"-`00c ,X@BB[l_@4f͚Ebb"``,_aÆ1o<]f >1_~^n7fɒ%1zq|ù5nܘŋϊ+xHMM%!!O?RWX}?ɓiڴi.o?g0xy嗱ZE%5k-1U{߷o6}iaaau ,ji;wFUURSSؼy3]tApIN'VL4iB.]'99ѣG< e˖(+СCv222(,,tpg~sNعs'k."##Kݻw/V ӉἯO3tvUۡQnٳt4Ms=<ᄄl2ײsGaa!G&>> 9r&Ǐ?oo˗5kwaNʼhP֠AJ-j_&M=z4Æ Pm̙3^q_:Ǹjiٜ8qQFYww3`ƌC^^&M ''zJRRRXnw塇t;&Ӊߌ0>>tظqc.zJ-jL\\g\t8IΫ#B4iǏ㯽V"::˗ENN{iӦ1n8Ξ=%dʔ)c4Yf t%fϞ|l&--sUi~yyy4lwy-ҭ[]UU)SG*|}}o3>=9Y'i${!PLB!<' _!$|!׳lCF$ @PP_-Dm1n=zuL!(zIΰl(>wڍWB.NF!$|!KHB/! _!$|!KHB/! _!$|!h!DQIAAv8n)yΒ <̶ !(l`0ШQ::J~~>W^%((Ҷ51?O⹞$|!Dp8 4h?.0 ԫWEQ͚n !꼢"իWaTYPP5=S9BM\5HUJ5=SIBSWnIBԘ=!ެ}~RB $$㖒#|!D822oȏ?ܹsٽ{wv3Gmۺuk{1ڵk ##?sι$''ɓ'8p O<cǎ%''q/1%v1m4Ν˻Kaa!wu5V& W g7@ǜ9s?~$&L(Mfxy]GK$7plOXXlذ9s??Otؑ}t:dd„ 3 z=?Xv-7nGoh"~߳m6&N]1Ll۶̺w^w_^=y֯_7|sS\OƔ"##9<v)4Mbpa"##] /##Fff&ѩS'HIIA4ףQU[t:ٲe ;wFU[5ją 4̺ wK/qa B```&|)!jչs爈%UUy衇>TU}4oޜpf3FZjU*^_p:|jz0iРw/)Y,ׯ֒w瞣_~o~3f꿤Ϳo&Lt 5|!Dw!ƍGYp!k׮gQF^+W_K.ݰ?¤IxYx1 , 66B&Nȃ>Ȓ%KXx1_(,,d޼y̝;MVäI1b//}o}%''iġtXqGz*ׯ0nʥK*|}m̯x#5|!Dj>?IBS^BLKKt^zݪp]UW&) _QRBi~5BatJ8Jc(HBUUlu˯iZ wxS7b`Xnr8l6f3Oy_Qclי*zޭkb~s=IBe00 -s;OJ:B%$ !/^BBx IB%$ !/^BBx IB%$ !/^BBx IB%$ !/^BBx IB%?spIENDB`linux-show-player-0.5.1/docs/user/source/media/synchronization_peers.png000066400000000000000000000325151323636106000265640ustar00rootroot00000000000000PNG  IHDRnw֥sBIT|dtEXtSoftwaregnome-screenshot> IDATx{\T?aa; ky4̴K<ǧ4b5=Y^iZYs2͟=ɼF*^KxCMA.^`x3V?k^{wF{F@ӽ{_.["5k @"""jԬVxq{OeV@X, @@g $;x6}Бd2N t\1 zɜަR (:""\}PʁP\ܺ5L裖ޥ"^Nn˯7Ǝ\nWyk*NścƇ4M[R4<];E7t(D Jq6l wPP669Nnh4xovۊJ"|d̤;{߯8y|CAtOS?:N`0k(Nd.:1!I{A׎օkt+: {\wcd.gr߯N~7ExmNR\C'@^ǯrеc.] 0#ؓ~ "~ܖKEZhgLNC-HLZX ;E}ۇq*wlޚq OY,V1bZgdd2h$DB7':kcj;MQc,۵BBbšF!!c[K(\Z;:rWWRA1,f3vL&R JPi4n oYp2g9&ɿTقc k;6DFC@-n N>j?=:RAT*aXqZDQVPJ,0圅"4ʰ0Ξs}ť.:^=;?#F`E$N2,+Z?Qc;J(:+rye:&F6o/OLj$ri}N:7cvV,6Gy;zx{NM|[Zcuxky=~( -k>DBLcQ d>.bB'@>࣮>Ed: PRzi{xA_a(Fz>jojKCjGr]Ev4>޸v](:h4O?GcڎhFҥvjRG]k* ۍją?.!!qTxn%0KT(BCa:iӻS +8NnWyLB//L&3 KjmQ‚ġ#`E& Mf:c\ X"gQaXpuAT,XE:}@8Q,TQr:B dcc2[tWo\'ϢmCNrbu2BWS=2im< LN@\ӱP׉lX6!˽/vI#^'$O@㣆xcJ]!!KJ 6E/Ͼ;w#mڶyq=!}sr9RNݦ9%usl޺Oui+:t(gj(Yзo__{Ebb"6m h޼9֬Yb׮]xꩧnpQ]v%Ξ=;vT$** EEEHKK %%l޼EEEE8x :uT:jƍO>.\Zl߾fǏlٳ'bbbb[z~>bANN6mڄZ?۷Gpp0vRխ^k ߯yh@ukj(>YB߲e 7x3f̀'&N={`…ӧΟ?_6Î;0p@-[|݈~ضm]5mIvv6BCC\TTT\.Gttm \{˗c޼yȑ#ۯJTbΜ95kFm{h>}o9rbbb 6h4TsBDwf_Fb7~7e<3RbAe85:r?p_ł&Mƾ}ǏǡCO`ɒ%/mvmsʕ+x71sL̬K/K.rEpp0DQ̙3ѵkWhZϘ;w.***c?FDD}YۮرcqqUV9700eeevm^~vۼ1h 2ĥzR ՍJ >Y"9%r+/s/B&< =wKJ0l xmmz߭^p6eh4bO[}GfϘ#oڻt2 ."Eqcb|2\TV_`@ظyMZ)RRcvu?`g.pضMؚXܫ| b؍oי3gTm -6k̞={.ZpảK/CJZwR*e˖aڴihڴ)M7x?ӦMCjj*|I|:um37m#GO?]kL---F_ѠnSO=,;wj'Q9s%h"_öMѿo5\~AA0[,P*=c_IR LBߧ [ y#?{}r]w ˿OYsn_a6k_z*DF<©9TPm::F{zhhhSO=k*A={ضn}ƍW-զ& j… عs'bccѢE ( ޽("11} t[0ذaRRRrJ %%޶VZö푕eW||###ѲeKl߾ݶY=焈^`?JJJ~,]5L>P\e_ ĥB{(/ֵ3ڠ3,_9>t\.*JQ}W]#Nۖگcq/뷭%$$??iӦն={jO-[BEL<yyy ë{"66Cyy'x6Xb"## ///$%%!77o°uV(Jdeea̘1_y!88ӧOܸzƍ8N`q$0g;鷦cqs=^t ʕ+h޼ݶvٞ'''#66֮궛R7Z.]B׮]ܘoA ^y1ϟ?ϯYqԶ`ij,;~8muJV9999***0e-ZHR=G焈.R֭0 (/%3J%8v$j|}*ZݻvFP*j}**$ $8OkcFyL>TNѳ{W8L&!TP(鵄6 AAehN]eeۖگcqmFb[pmsBT>Y0Uż<߷wc􄗗w!oEs>}c^~ (bӏ[`!^u4F_rt:geܸژʨ'ٿ3cFMoN/pX$}J}Y]ǯZmODTë-/ݤ}'xoT_Z f9~KqyF{;Q$ቈu"ѝaB'"r<ڦ [~B,9':̿ SxVu,~T E(`U:rCVacgT]9xD=#!:r3b9/db`uЉ$2f1{GCAdSDDDn 0&t"""7NDDx;2 (Bj6t8f AJ^.CDz=J% 䤺e4Q^^2h4IuЉ\dXT*E#"wT*L 4ͷDD.CAF GDD.Z5tIh0IdN:LXq EJJJwW_}nݺI.#{IpF7oFYYFbۮT*B.cذaw|(=[}ڏed H<<"##T*qq|PXl;N: =VZ6m`Μ9\.Ǯ]0p@s䣏>ž={lo:w瞃d(Nٌ֭[cʔ) Bff~Nn[bժUعs'v \[nh_aa!,X?ַb%K0|p >111(ǦM)S`رh4F۷쬽[|k.۶GEzz:F]U(X`0tPZ f(:-pwՇVEjj*F3gb„ B… f HKKԩSm8*EQQQdž LDD̈́."/_C"::C +%\XV :u }`2pITTT 88iB}(ݻ7vUkXxzz⧟~lƙ3gp5Çh4";;ǎC^жm[Tݻw}i6W^ (:܉K.a˖-? #Fh4Pɩ֎(0 dNs]GG={3fnPPΟ?_㇦8+ӸoCbնxzz:-kЉ$EQʕ+wիx avqUOEXVxyy!m'''lơCPYYY8jg;+smqWw`dgg׷ƾ܉$S(ڵkذaʪ@ףj-ZK,5YXXV-[BTSNg0`X`s׮]1`=z(T*QmwVj "Zlnݺ!)) ǏG``i{gewwSDD-[ ::֭˗QRRRk+W/?Oz8pN۫ʕ+_cv}ѣ(++kPIDAT\C'"5t^///z77])w"" A&]VUGVXgX`2R$HFɄr&us @PH]&&t""J%JeCAdSDDDn 0&t"""7NDDx;2 (Bj6t8f A|:ND$^RDXX?1hDyy9$ߋ΄ND"R@7xQR*L&lBzK""UVV߿àFq%ApND"j[C'A|:LX1INЉEJJJW_[n C}Ho޼jV ٳOS6.7םl3YYYؿmpΘЉ$ݤ "ƎCV'Y0bĈ:q޼ys\t2:)N;v`ҤIPT0 hѢL b˃/-[ӧOCꫯЩS' `ػw/ ""'NDӦMa4n:$$$`ѢE8x ֯_ѣƌ1c8*A0zh * EEE4ifm8/22&MBXXt:VX}K.ň# vZnm+̴}n@UXjv܉]+W֭[kZh1h Yv6Ipǭj5,TVVBP`…Xf ^xaԩb֭xaÆÛo Qm,6m^ytݻ޽{#99־o}DEEa0a1w\LZ۸P(h"$&&bذax7ҥK1|p\pv,X :Vlvx^Z-RSS1j(̜9&L@DDD}8{QQQdž wULDDK"11۶mCtt4}]mBBE޽۷ GbAzz:*++ł ,, ("&&(bӦMZpЯ_?!** ~~~dCrrr}tBf._~Ķm`ZmZM+xzz⧟~lƙ3gp50ƱcЫW/}zW^ Ro SDDdܸqqjzj۾FO3 d%|<(++Ç裏ŋ(,,D~~>bcc:w/^Yfի/pawͩkWosrss)>1Ip'Vł?| RSSQZZbߢ"moҤ !"зo_\xIIIEi5پ};v܉8,XFr9~g#00u꬝[#;;>\=_SDDYYYHJJ[oQqqָ <<.)8j֟;JBCC1x`l۶ ("55:tc=ZPTlơCja0`XtcǎqqqEx! v= JCEx{{%-[[nHJJrڇU_SΝ;\KR!??_r'DDp_QVVlٲ&Lskb8pZnɓ'ļym۶G4k K.SO=4ƍW_`0`ʕرc->vKG}Wռys̞=^^^_@Em3< Əoooio6`Zzjl޼>(fϞ|8z:vh`///ZK,AzzylSSS Qdz\\=_wDW2ݢPtѳgzu:Љ$hQb>5˘Љ$p?~ Љ\$a6!:W\ifX$1HL&'7Z:+\ 6ԸX,L&zT*Iu9B'"@d2I P(nW H"R Ra;`B'""rLDDDn 0^ND$L&(tZ BA$߃0IT*O:g4Q^^23b@T"00|K5Jd0P(\O|{IDJ7tth40 pND"j[C'A|:LX1INЉ"%%s#t"" byfjXVgO?ENNNRnOQ힓tLDD4DEcǎVO`֬Y1bDRט:=Daǎ4iT* Zh)S ((X`e˖Сz=+t =z Xx1 ĉѴiSF[ Xh<z1c`̘1J̟?P*8~8>_/VZ;wk׮ʕ+uVph"cРAXf ~=zJBQQ&M|:7GoZ#++ P(Xp!֬Y^xiii:unxx8~<غu+{=bذaoBE[;7˦MW^y;vDrr2zmkwHNN[K,1|p ..VEjj*F3gb„ pGm1FEEA#>>6l@TT & >>s΅dj*&t"" ҥKm۶!::.DQD۶m!RSS!"vލC[#Gb ==ȀbQQi&XV\p ׯ???d2!99־>rssaZa0p)KJpaFdggرcի8\9>WhbN5k(((hs~; SDDq!77˗/ǁPZZ VիmӳZU?y`0@&BBBgH ƣ>/uطNK1FZrssH9>p9,^fիW_wg}aB'"vFNuկbO>(--EII rss1zju|}}mukaaavۛ4iB$/^DRRDQtwU{F^o 'OCոevs۱sNa5j.^Xklhr'"^o>BRRz-Ǐ#00ж n*j̎~>vT* m6Tt=k#$$zPhѢDQ`bA@@sG7Zlnݺ!))iRJBpp0f3:^V)w""{K.ڵkѭ[78pSNɓ[o2331o#رv:/: jJaaa|2Vk2NN[1cN:LJ 1(((@PPd2Y2OOOjٶedd`֬Y8|0^}: 5qut֭ZـpTVVFk׮j?<㈊>Քt- >cL6 zJ7z)=}]95_\\e˖aʔ)eB'""j@7 2ׯ88{z7ojСC$YΝ]~R_'5Fᒿ_YYRI߉Љ\?˝ 5% v޽é 6Gv:Q-r9f3rygX$%)L&jt:I j5L&SCA 0L9jJN#h4ZVm0``Y,L&zT*Iu%oPI5v& VCFJd2%uAP($ݮv(""_)J(ʆF;'""rLDDDn 0&t"""7NDDЉ:`B'""rLDDDn3]uIENDB`linux-show-player-0.5.1/docs/user/source/media/timecode_cue_settings.png000066400000000000000000000557021323636106000264750ustar00rootroot00000000000000PNG  IHDR{.wsBIT|dtEXtSoftwaregnome-screenshot> IDATxy\T?l:, j*AftD^oy6UXKV~V_"P.)Z⊂ 3031 3"c9|=|ǰaÆӳG "Qd4_|8x6ѰaƦmHDRMK$:NsNBGGFDDDDF 'E.\K$.V3#"""rLήچvtDDDD'~)KF!WߥWE\" ܗ Ç^?:_OI&3-k|,i|pwųO=lwL.FZVY?KpR/7B,E"jco h 8;C31^g+FD=ٿR#U*5I񗨡js.I&EiuTVUþOj vd@?֜ʪj| @ã厲*D/\tƀ;z5_nKc_@rN I>~<%" w  ']=9?2IfNqV.8>gw\nS&J{>=p}PvD"R!w:+*QִJ|DxDg=oEB 7&CT]mS98wdٽ±o1`Ӗ a+WM;Ǒ>\tF~]0>>]R7د1-+R34 ȱ nY98UXߋPqAPa()D;bwB$Fۂ!A8]t5ZD mKDtSKҏ >`[Ak7h!tclLfU*5\. ɡwƪ?C᮰>-$(*P"r<\\WKCSS1t&?^8w”}CC"`Q*`4 #ogHXxʯWU w7g E"ٺz#7$}axyz%r>t|bшѿ_OL&ox j(5Xz3OW[: ٯ'<=~wA[7N6$+=q9[n]w@"W@h4Z|]=1VxC!eW/^ta,Odd ߮.3~FE ^8{1 H$"I<:zm9qFckА` F\;e0pD"xzb0-͔@R^U*p_+o\3N{ 7?^e$l՚۟u3W;F SŸ\RS ={8;;!gN^^Ah=FA@Nq(BeU5 #d.]!s(8Qnr<\kM@ 'pgf/,:o}{_(>w [jtze5<&29C.\炙oC] ;P;-gAmloͤ /v=Z+q8WDy#']G~%A .t_lq!(Rc 1ԐJ%˝Q[2ۊD"-~Dy๤jx/GAmw="KԽkOV' Dݍn~7'j {]p=;UX>,D3~/F3bɤ ¹ %qy|* n>xjXd e硶VeW/7.׷ٺ*А^HI™=jvGǶ]s2)ٗ#MՎ٦o^yUZ+#T7A =VTD"Tч O9\\J#"""!`P*#CpumF1#"""r\KDDD90&{DDDDcGDDD90Qdd$oBDDDxfȁ1#"""r`L=""""dȁ1#"""r`L=""""dȁI;:""C&ut4V NA]Q1#"vш d2T:f/Jd2㖧jQQQ3t,frV))) {޽{~|ȑxavZ>D r;vC0mڴ֪aԩ _";;Ckbmv^~ec߾}i۶m*++O`0d2$%%A,#66w?n _ `ٳg|r6oqu[ut'?~ܴ,33u}wpN3{/2 ٯՒ#G"77DeeekU}[0`/^+V`h4;nɃbmvzK.Rmqqqشiiل   . &&Ʈmb1.]իWwށJBXX^ju+ƪNgzoDP ###FBڵkqqDFFBVc:t(^b,X{b֬YP*|2͛sBBB0}tBRaŊB=`t:|HNNf͂> RiZ['0{˖-HHHj|g=>mA1f$%%2-?z IIIزe Ə:AлwoRSS_"(( @$aѢEHIIAzz:>H$xyya͚52e SOY-{[nŏ?󟦶{/[l7|QFYև=} f’%KׯC"//߿?lb*/?n툱6׎ ޽;>3lٲ7nģ>j,y0l0L:7oFppZYK//r9A&O/A};W_}7qkc6mڄ2hZ>|b-G]\om6,]6mСC!//RRR_}=={v(\0߿?ƌ`'vڅ'|o6z-:D%K૯¸q㐙Yfh4Bcɒ%Ell,шo w}W_ԩSTDlݺƍÿ/Xɞֲ>>> BFF=wXkG" 11v¸qoॗ^Bdd15k8e˖!66gΜ1?5WNK'NF< ݋ݦ~H$Xx16mڄzWFmmm?W^ő#G'_~fR,x+k… f;˖-A1~xL2Ǐ;Nw[խVrssa4dggh4DDD@,.޽ D"ADD4 QUUp7B#%%cǎŠA ˑSNٶaHKN꿧} d2,Zcm]u֙ٳg[l3,, xLammmXpdrfoԨQػw/t:0p@(s!hZȑ#=zuuݻ!vڅC,C>}FѴ]``z͕Gmm-;F___ Jfcj3AAAxuR_sGvސ;F9s;v1zOoϱӒmg0L0_|z]wA.c8y$***d0xWo>,^WСCMcK}om4hb1222`0믿 @R={\tjسgxoik555M} ^^^HNNFJJ >s\t ...AIIIp?>>>P*(,,4ZkVc:[mqH8gϞ;v,; q}OF.]1]sE8;;C`x衇 7778ql}bئ U4k1UWW_ Lmmc5Qcm___;wsзo_c^.K[=F\p)))Xr%[\|]t1֯>>>(,,ls>V 6`ƍƜ9si&^1u߱6]vEQQQ? ~G}>Xd {7v\( |T 1Ŝ{ 7n\]]!0p@/^ L!CwXk_~3bcc!c!55Fh4t|B_Z5550 ڵksQQQaZVzkNN鏓h+<<<1駟 ĉqƸq77Ʋn~mksww'F#Z>&MBjj*RRRsqzڴi|=A<==]w(J:뾴cMgggBכC///^;;vhPGŘ1cz- `̙p-W_}f[o;#͛7;Dzz:._R)[*fϞ~Fk׮w}g1&{س9r>,kgϞg}B,YP]]l\x @V;l͵+`xPSS "//ϴsA?xp&kjj,+WĆ 0yd={M6kkZSS7| .믿CV796;~길O>xꩧХKTUUa۶mXlMcܸ>lu<:/2ΝW_}/_3,?c iii8z]ǪNG<~W*_nJJ̙3_bz~}ݦ'N/믘a8>&퍋/Jطoh:Gj}:SYGX,6l޼|M,c:P^^atަ ڱZǕ♽:^R=Ygz)άhqGVgu:兲2[[GV65.4Αuۙbϭk(//GQQ ggg9sUcE;kN[WԶuz;Su`Μ9pss^o F*us#~giږ9{gΜp_Ϟ=;MvX;3sj={+;orCgԷ)Όyp#paQ[o2ܱeD""ǿd+K ,u~L=""""dȁ1#"""r`L=""""dȁ1#"""r`L=""""d:T#F@$ut(M[#F0̒vtDDut0 9s`̙0aT*UGIDԩ0#[ĉMNKKCBB?nZQunLSP(#P(vZ?~PX|9{b ,={5kJ%._yܹsL>PTXbУG$$$ 88:_5ǬYÇCTQG="bܹDll,֬YxL&Ò%K~z"++ g899!11鈍?\d2$&&b݈E||<^z% 22 /͛cڵm-"d:\Fdgg08pŦKwƠA HF4jTUU!<< `ƍHIIرc1h r`0ԩSh-"˸D4y^]___xyy.K4ϟ eϟG>}T*QXXd;kmUWW$"j &{DJKKqiL4\zD%%%(++RlΖ: /CχRDtt4@$!((BQF1yyypvv!G}8r\\\pssGmu>'oɛQǫG^Gh4o1oܹjR IDAT6mOiӦh4bHNN믿`ƌX`N .@V7ۖ8Ϗ&W!l7˛X, "b8995/HDԚD"bM-o7uW$1#ےhdGDmf=""""dȁ1#"""r`L=""""dȁ1#"""r`L=""""vӿk+^J֦.pwwTn!9vˤT*"bPTP("i˸5Z-Rd2RŇX,X,LdY֢P(cۅT*ň#&eݺu1bDGADDtikbDr۷o; iG}Vr9v܉C!>> =}m߾jߠ(,,ĺuZTo}III7ŬL};g̜9&LJj-"""x;!NֳBv<4i [R{paDFFV/^l\*ޢ8,[gĉ!!!Ǐ7-lQDDD15eoBڵk/`СXv-֮] X "$$2 Gkv޼y6ljjjrJlݺI5kJ%._yܹs3f 6nVѣGc˖-8~a0` #L{qDFFBVc:t(^b,X{iBBB0}tBRaŊB=`t:|HNNf͂> R!""rd6gO$#"ͅ7GkSFK.E\\ѣGKMMŸq0sLSQL&Ò%K~z"++ g϶+m۶aر 8jqqqذay~jLZKMMMug-&4ϟ eϟG>}T*QXXh61ODDtjd9{muhDEEaʔ)-A^^^e8}4&Ml[GTTRرcc8Çcx7|3מڋJKK̷sU@$> J:["""=hdj*  m>00Я_? 6 ?$JP*6=z4rssM8pM֧hн{wb 1tP <شlY)// Fpc/C$! >(q &&^?}?oˣ6Yz5233M$4\~7oƢEPVVfZWYYٳg#99ǜ9spejZ _}|||I&!55)))x̶5f̘&ıcb۶mÄ pe***h"8F2g-6[mmbj6m&Olذ#GN믿q!%%K,Abb"j1c c֭XhjMv#/FyyyK$73FW[ ٟKn̯S'̦#d #5߲ۜ=777DjLpuuENƑQge45ݒ=^/OOtfO9Mjdh4BVVQk/hQcGDDD90&{DDDDcGDDD90&{DDDDnRPTJ-D""""nJQ Z4FN JB^!9v[B*A&A*Z|bbdN6lm 999vg$J1bĈf=Jy+BDD7]bD"ؾ};~'ݻ?V^;_Iپ};zd7|O>Xhmۆ ,XNNNɁSz Y~?GVVvڅUVa[+#G{=YDDD}! I&رc?le˖~c=z իbw}K,cٲeسg}Q`(--mԪz R٢.\>JfmuvMDvg/JttPZ [lʕ+ e]vƍ&ub,^~hHHHVX4|fKb̘1 ,Q]] $''cժUoqݍK.Brr2]N\[g޼yؾ};lقq.] &x{{c׮]dM^m\W^w}T\Rǰaꫯ"%%ݻwX ?KcZz֮]^xx[ԑH$X|9f̘Ѥ}BTHOOǮ]3hz&W" ''>#l߾)))x0sLlݺ5jTc X?uvKD?R8[/BȾ<<<0aCB&aɒ%X~=bccٳg{zz"33O?4[oW^ 4Xt)p=NNNHLLDzz:bcc#776R&0lذ&nmոʐEm'55ƍ̙3`̨ܹQo>6R6_#KcژzZ8lذ}H$o DO?lbƍxGkaԩlvbܹDll,֬YÃmnoDDԱ1qV^Lڵ aaax뭷޽ D"1m?C3hAS DDD@ -- UUU6غuw>}ʕ+vj* 6Ss%^}U߿ .ĪU0tfɁNCAA1zhdeeaРAǮ]no+zH˗/[ɖ:4X~=t:F8k'ă>zm;v zh460FMM a4q@ck>޸ۭWDų[v|8vNR ???RwwwP(LڝkSKy=...pssøq/b…HHH0}I!<<SLAJJMၧz DP*0a=j5ƺ>ׯ _-^µԿ999Ph4kH$Vl\>Kcjz{m͵s!ܹ없))) ''' 2Į37Qj˸}e|gشi ">>ӧOo|sǣW^..]Xj+ ͛QRRbj1m4L>ӦMh둜l:tMW^y3g΄`c78j*D",^]vŋb ޽ۦ:}4BCCOK.Ž;b WVVb`9sLQPPf2}ZZm, χ\.Mg0mۆ#** ϷZgEEEuƴ1k4,S?! .ŋcSv/_/j46QEFF6f7H$0 65VWϥ7ЩpqvS[uB;wnҤI0} cke)k3{nnnPUUX82jKaaa/QkdϠ|Ԛo6 u.nnn{0nɞhZZn&EGG#//{Qi9{DDDDd׮KDDDDcGDDD90&{DDDDn" 3$c0`4!ɍn d2Ӎqvvn㷭H$J^?Qg.b1z}{4EDdb0xUnk|$"""r`L=""""dȁ1#V'!;: "( ttnXq} z=Ξ=k׮uD(DDtKJJ?$t: ॗ^BAAAGGtdO>tɁ\.Gxx8= Z-lĉ!!!Ǐ:v.]ٳjW\_{BDDBڵk / ##O?4b1/^Tddd?TzFHHVX4|jPD"1c mGzz:vڅgy0`5'''>#l߾)))x0sLlݺ5jT͛۷c˖-7niyhh(VZ-[`ʕDu=sssZh4-[{BDn^zFY7unZF\\6lшK"..qqqѣD#66?FbdffvލAA"X"֚NבH$JNL4x^wlXgy111;N< Xnʔ)h4۷M߈mo4y.,/**B׮] ///$'']t ...v@vO XlJޡFTTLJL>PZZ Riq3f ӦM?Ύ \HAn5ー<Ә4iR2 U$qu:]`D"rZEDDd+???jT*#44Ba2|}}M:t;wFAll]m } rS!00Я_? 6 ?#T* àjnr~ `SxZEZZ°yf j1m4L>ӦMhk.Ӷ `…Xx1v؁jڼv/_/jĥKZee%fϞ  ̙3/_cx7ܘ8wCQddd(//ovcDb5-@D>;{DDm|RƟK#"""r`L=""""dȁKg0ᛈH$%3LI$HR&{Dt[k AQh-76ݲQ[߾%"""jcGDDD90&{DDDDcGDDD9ND"QkADDDDbpSuMF#b$"""j O>T'rDDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcGDDD90&{DDDDcG.s|;:nױipk έEI;:9S`v]Q|{;Gzxv+Pg?J5[fZխ^< QRi7CohQݍ57k~\7'cѭ͖(2͎U9@7]t{nX`{߯RkWTXH!^Jꋜsu&{ܪ 8_q .ypkGvSy0{t,IbT,`Θ׆~0 "Vwb3;nn=vGicex? Ε`ۉ.p /:ICOjS=-yjK_?+ F3d*KA|9 WFǻbANR#^y#{UC+*ww0y/j\pƢ`*@.5l|W58|~ UZ C{/Cn67 5l-ǻ Pi%`jx<*b+P șv0j S}zbXJL*6-Oࠪ&uk΂hn,Zl1|+=tŠ@]qABC.3"=k TA&1W~lZ}9T"4N+8@/-N*-?5kZ[g>jmhcR=X4*1WzlkjY\"UI"w|5w,CO,X8~]j-SMo8[ Me2LO iBs:Í1pĿvoG^_Ƹ[sf#VKkT]d=l QW1iH+ӃY$l ArWUNps2OADx[Ou|2.['/aaӐ, ˮ7!0n`vꂴ%F[ޤ\\X)6UP>;/FŤPE7$UՑkR uߌ~jlrY>j?߮Rc567'x lo IvǾelWB&Ls\+Pm'd>h sZsdi=b;U~X{C*ƨ fVi8]&?‡;cdH^|:ʚ!?[5WGd׍j#{]Uy7xE4QQRZ/`AFw9N[:u\#-}m]K;֪L/:KVRGZEA؊!rrOHVIB px|>9{g盽9Ң˕>A(-jS+3?zlw2<XRߝ{\uSq+ƖX,-t} %淺_s/=<~Z'^GZc{۔wҦ(>,~M_; :~M#F6Ӳ}Fmglϯ.GSUX#Z"~QXfXQFܝ"˲ޞGFmc#lGoj>bζ!Qڣ?=cY_LӸ4n8}"beuIL=a{#̣j⚉qxfwzw_ag׿$)?n>SDD?.S^߱/w\v\6nke=@Q dpQ/*hqlJmm{؜O8lHKL=~GmHXޏl :ok=OMYg@ w +2kI舉uۋ;eY˺\}NǍ{R5}]?=cYoewώӸ-K{5!6n;*vGD?%>?:(ֿt_͉5]/>>vD濼;5 tW\-=,n" W%[xd/X9"8eK̜6cTd__{il?ZO~{{[7}yO:5>vx#Nq7Nޒuٞ_}F=lzgq]uƸm_+KWK@ڗqP| ڲk'T݅7z2_5begx& mƖ7zw)o9?먚:B-7-bG}A|wdpKwǿ_6,j-2=|'m1륪.&>bw[bKٿ`k]A(nz{ g-"x+|hsl~'[2>w_M=m߇ņ_/G k ㈲QMoIw24o(/XŅUDmv/gl8]>6y[;?kٶž-6*ʲ8nD}|q^69"a Q5!)o3P?2 n]?~K߉G?2^|kxǑ[OA#w5;fp ou4{U2>&suegÇ6cwĢ5na%ngቓS헿wƬ⑿dDؑmQה6<~Y_7*(k'nM-yƛuMStkK5բnG޳=gʋc699ρwgP^^۶e4@&Wƃ*צppx=*/"xzȸcp`o> ==<;.źJ4.0 q[cٻYOr;$v!㩕#z},Lbۣ'@*#sۯcqïOC5{ pHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHXArco۶mzN$L$L$L$L$L$L$L$L$L$L$,㿍 hjjhmm=bРAQ\\}sye<=a;w¨:!v튚(++d,]4ok폣T==gUUU|_GIII]6|x|=̻/|!ƍ---bŊ~6lϜ9sbƌoGDĴi+_J\e˖~+B@N)";xW3όҨ:7nܸ뮻{7#HS3>яmw_q1hР:uj̚5+nXfMwǍ73f̈͛7{L{=8D]tE1gΜhhh)SO?%gϞ=\L0!=P<=.{ ~>|x8Y :4N>XdI<1mڴ.;찘?~\s5|'n3fL /SLxI&ŋ1x3{.򖖖5kV\uUqUW)\pAEUUU|31seeeQPPw}w̝;7>O 7˖-~>h|S 7eo1jԨX`^˞y8s{g}6.]әIbѢE/R?> %.]oc}Z ,38#JKK-L/€PUUUTWWGKKK]fMFCCCZ*:^oŊ+WN;-g֎^SO?~ŋ/gqFث7Fkk^jkk6FzkZ*OD9 /8&L]tQDDDž^qڵ5kĈ#lǎk .SN9%nÆ qWǥ^eeeQRRW~mmmyyyh;,~_vV]]C 9uV]]F8!CDIIIlڴ%K]wv[|K_29,SƩ]vYs\rI\y1gΜ87|s͛7/.hnnW^y%t6lM6ԩSc޼y{-2eJL<9]s͛cȑ{-۲eKY&ڽ^gذaYW~عsgL81/^eW_>c?Oc3ʕ+==ɓcٲe][xqvi]UVVF[[[p 1q㲆hii#Ft0aBL>S=㦛nQ\\1~8cbѱ{صkWرcVokk˗;+((xcȑ!FD)ĽַbQTTqǵ^?O<ӽ7o?fΜqT5{!rK/嶚XreL6o1}Ѹ;C=>`}ѱ}Xres9hѢ27x#nᆘ2eJ̞=;Ν72zؾ}{W;n544-~8?o9>O<?x\s5{{ [n%xG . x뭷>nΜ9'?>=C&uv=>|x̝;7;}ZW_~z|~[n;\ki<ݹfrX#vM[o29c޼yz}yCU.O@;WRR{ny%,Yc=R2:ujkaPȕfrTmmmǠAPzQZZq2lrm~;G GFcc~߁yyy=.?MͯtW GEcccZZZ)v%%%Y&t4.hmm=K^^^9 IDATxy\UߙdTRIʉH!&rK[k{-ޫ1ڵ,KWˤ03MP((*^5!pe8 +2xPO^ٛ!BqRL>3P;HV;:2!Bhg(Ξ/UTvMB!h\ R=!BNA!B;H'Bь:!BR4۷>|[w/L |EyWa/bЀ`ZxTn0P((R死9sFWWgzvcڒs[wPTMicm#[ޯjb!k7d-g>vGBP8D5eCG߹u6RlB\MiٴΖl7ÃQ;lks0L_iozt|G>!DC`>\EKlK'|2fO(C\ǥ߯;+4jnmZ8NP@s;g>( E!hjҪ;z=W\k2arR;{BFU˘4/XYkeoauBiIRe2a۬NWE+x̿-O+3J@Ns:wq:>]OwK~e n-iʋPV^WV)1XOt$#>zu[=ɅKt~Ώj@(./OQZZF7t\qu*wc~T TVV[_=k~g' akks~kBj+A)L&4-[;zZa=B>K$];e\|żȱ|4;>,!g|'iN<Ү5G2٭3V5Js/Q^VA~( kMI+E%kIZ7\-|% z=A:P(Ҩ>F`bL&.N,D3{g [[kxZrjBCΝφQ{y; UGwv%Wuep%Z2sh^x+E݋Gڵ67 t$%ST\5ò^>]VA.^qxz'MR_i;ڇRCWdZצQq c>T*%mOp:+*.eureeeTVо5~]g!DQٻ۲OƌKb!w/4kS%l:!c]C*(2B!F߹e^ 4(B!g;wh@#.]憾?{akP !BCL -!cB!1'BьI'BьI'BьI'BьI'Bь)u&O !B4SrgO!dO!dO!dO!dO!dO!dO!dO!dO!S׵CRV׹[NCP`20d!Z9ZMEE.\qBB!ZqU*W\۱qGRPPR)B<8SO!dO!dO!kp˂ ضm$%%'L@~-M\wc$F!B{YVڵ+_5~)ӧO.]P\\9sLB!c=B~|e(,,Dӱg=R믿&==,>sj5$%%1~xobcc fɤѶm[%K0l0sz{hh׮ dffyf^~etBffJʼn'prrB!kٲ%YFh?E@@֭#44^hd…l޼˗7(INNfРA,]ŋIHHfŊSRRRHNN&&&ooje6nHk=֖m۶1wzYbddd{non~??_|餦xbx 3f nlvL4o g/''^zp6mDNN?iժ+V'55K.[ך5k̦MGͯCξ}xꩧزe 1^x4kٻw//_f,]TٳgB4O<Ά xyٵk| ڵCnprrСC !$$3rH4 PkR֭[y爈`7L}ջٳꚉOѭ[7Ӎݜ_ݻ3jԨEܣh0LT?_g Oo?~F=1k׮eРAddd9[֮]ɓ9t...$''ӻwo\\\СCt V ⩧BT֭[cڴiϬYh׮&MLJHOOחɓ'Ӻukt:K.eժU>>İ{nFMXX0fzMB4T[.-00܅+"///HOOoڵ++z밳sdeeBxxxÊ+`ռ{ɒ%K@KBB;`eeżyXn94 c˖-DFFѣ޽;s璘ȳ>K||<7mF/_&77?{>}:;wd 2ӧO׺krJرcy뭷prrʊ9sODD ,0^;w̠A5jfƌ;Y%{ԡ!D|||pBYjUodffRYYIFF;vյQh4Eyy9YYYFvaSR4ߙܲe eee]Dii)tʕ+1L>}$ ֖$ Gի7mF&a۶m̙3ŋdߐz(++ˋ@J%6lh4ra(--ΎmۢP(ϿOK 0ݻw1 !Ľ󸻻P(j$|666888PPPPogϞ׷o_shc(//8;;Wk^k9s99sGy777?^\UZZZNdժU1}tVZE|||jkshт'O֚?~>YfQXXg}Ʈ]n}!jӬ~M"ܹs\z^z}jyrrrW@׮] 5wu7jWK.믿2|ZŋzyyQPP˗qss5z~gJKK?~<\׭vj;'dRRRӧ| Æ uYu !h4 {{{ȨQ3gNLJcNvA@@-Zp۷/yj+\\\\ӧP5Ã\4h ooo{9֭[Ǿ}!""{{{o֍y@P /-6mTrmػw/*|NNNN槦mll`0{nZy(!['DSQ*2`04]vo3fN`9ݝoZYzu2EEE͓O>Iqqqqww!F!|}}?[[[:Wwm'{9r2 &Bzz:˗/GVKpp0'O&--mZR_Mzz:YYY|j,Y°az!݋F]v$$$͛yҥ cT*'NbB!jNtt4III̘1CVw]>СCygF3~x.\͛Y|y4DGGsqHMM%<m"BqiyYf O?4 .gԩS\_xx8_իUZnzmFyy9.\UVf,1es6s!BQ` ++Zv5xroooJKK)))7̐!CX~={ƆCgF饗XjΝŅ;bmmM^u1 !Bܫ|g/11ڤ~cƌ1ܪU+bccޞDrssXf 駟fʔ)dgg[TQ 55|.^h￳o>z)PQQ#5koDGGw^fϞҥK)--%##gϚ4F!Bm^uwΨQ:qh4T*-j~3]WGNOw ! 4w qPnAٶsyJ.̹ !hdLԡa&U œ ԯ(/tBќ4wۀؽ{7EEEM)@֠hPյ4+|v<:}wŅݻw7xߝhRj޽{כ@ !ă꾾O?O?5uB4R7)y 8{QQpwչqF爎&99m ÇojJJJظq#si===? /PJ9B =!K @]m8?_XZxӾ[$znYRkqÇw Ƒ̙3Q*jՊ>g}ۮٳ 0B~@C :}?ãOVt?rzF FuKzjfΜɺuHMMW_0Ν;իẆ~ZfbƍYZ/f͚5,\֭[4&ӧٵk~~~uؑkך߫T*vލ#lܸ?UVOozB jVɞ+'Nh0B@BRvDۀpI"N4U(n+Wd;z '''F#}C aȐ!tڕ0qWfL:ɓ'H4 $$$Izz:Eۛ ~7ʊ9sODD ,ʪA,GFF'%%_N&DP(.pT <0&L`p)N:e~|'ΝcҤI:u WWWΦwG\\MoHKKO-bɒ%,Z???ؽ{7QQQxY3]׍;]ZrUuޙDKhh(:EEEL4βt+ÇoL&(IݥKpssuBY܍۶m[4MDFŋW_¦M6oӦ Ӈ^{'r(--%44%K`4&,,0x oq\wxOJ}w*DRRRC%|||/b=z`t: nnn8p777k>뻁-u}8;;ӡC޽޽{QTDDD$sww~ڴiK={4ٸjؔ&O\m Ͼ}EҲeK\\\nzܢEs1***ؿ?deeY|B4k[m &{| F>&&;vY~ڵtܙD |y_QQ^^^ }]Xx1~-G&**I&1~xrrrx,V[_jٹs'ϟq1}tFMAAAh!En u IDATjX[[sܹjۼIIIk׮պPˣSNMAAAcF!''mۚgggꫯҡCR5j GGGXtiBۻQp5dqj ]%v/U]Rl4Fβe˘;w.vjPjg͍,˗۷/x" 5YwC \zwh*(ZJ-[;;;*+uw8S!5Ya۷ѣG޽;6l ;;xlSO=Rd„ Q;PyEjFZX㩨qxjz}ӡ ...޽ۻSn'>KAc5w}G޽: !]bqײeK|}}YfMh4,^6mDttte/^Ȟ={K. kdffGƉ'jv6mڐ@>}xט8q"...X[[pBٳ'1VVV2tP^~eBCCB)J EG{ g~gQ7n}56n`|7<裍NC3`;q VXAFF޽|222c\!dחgb4HQ*lXu"%% T*U&W^y4.\ʕ+}mH] o>z=hZZlIPPJիWc08x W^{{{P(P(j$=  ϣI+ٳg0`%%%RߍNHH_?۷N\CBHHϟgȑiiiw !,w>>GzzzcڴigϞjIp}m !9s|?YήA/^_~!,,fə^'99w}{.\ã170|.\X8D!Q((PPQZH^7BnhN@7)]݉ EΐdeeŜ9s'"" '\i&^y&MDTT /@KBB;йsg ĨQӧsNϟϐ!C8}t7kLJ+W2x`Ǝ[oe||||Xb^{4"##Yd QQQfVVV̛7uȑ#ɩ|4 s%11g}x*++oږSd21uTfΜ?*8w...tkkkze>v޽xxx0`ɉ#FBÃaÆcQ]ZvڡT*]mĮ]P <ggg @VV%%%Z\x0)ƅ r8-z=5 6Pi۲Io.g<^GQ.00RɆ 0>|jerrr$''ш;{&--zWHMMs *Jزe T*JKKm۶( 󁪧M&+Wd2qio-iz(++۽v=(//'++ Ȏ;fvZL&{>ڒ`ѣ(%h>hNN/&MbĉqIΝ˱cǘ={6K. Ξ= T%[#Gd̙L6 J&NX [[[yWiѢEEE[O?Ԣ֬Yüyx駙2e 5;v̢󭬬dĈE~~GV{{{m ͻ?# d4ˮ^Ow]ٛ>|8Ǐ￯QE3vڅ'gΜߙ3gjM\oUC0LTTT4Z5_=wkqss㵎>O!Ľsզ&^\\qqq;x _u 2eJe+55SmmVdȑ#.d/l5{X7qޥ%^m_U7nNq%nim}F#111|礥s%~W^1ɤЧO> ŋW$d2akk{KaiMkvwwuoB4ri|BR\?q(Z5vw^T*@ЉvM^^6mѷo_[j  vލVٙ\4h ooo{9֭[N`0FAAt++chӦM7kwrssqqqO>@>zg߾}ؘ8::޴-!D!Mtޝvڙ6 q-qm~=CeymX*++7nӟXz5|I4nǂ ѣ=z(իIJJ+Plall,O_B{=fOVLzzzNA!mM9fO!YӠqkLchHOO'33Z:>|0yyybŊˮ]_1L0~ջۛٳgh !BK< /Rm_P**22N:ѫW/KfΜɫ-{N>͓O>IqqC!)58ٛ3gǏZ"66֢Z-= Pd„ QrJ222X|yX",,'NԺFmڴ!!!>}k1qD\\\f…ѳgO>c$u'Bq/4̙+B`` ;w-5~9-S\\=~~~( #ݻwGTqFj j߾}zrssjlْ J%W`0pA^Zo|B![o>,I~~~e=ʌ3İm6<==qqq!55\ٳRRRXnd2(/Zݝcǎn] !J!0⁢P(L;>#-Z`IIItc=F֭˻iD֬YO?… ygx" ~C\p/ŒN:uB@VSQQ :!{{{P(TVVѶnʕ+|Y5VVVՋE1osăϤggg._a!BK= uޝvڙɓMB!MHg!>sc˛:,LjzBhPjݻwS!ɤFJNN&99+VT=ŭ/}]N /(D 6n܈C1&[nmxƌl岳qttرcټy3 6GV^ʀաhQTߵI']2tPk׮e:tȼ--- Ǜ: ڷo/G"## lllx饗Xty_2ͅx0T*=4M\VVv'R${B\\\HNNw޸ϡC֭ZSO=R$&&|ϏiӦF~~>f~]vL4 JJJ#==___&OL֭t,]UVUO6 wwwك9jwqqa޼yl۶!C@rrCT2gڵkFa߾}̘13gңGx;v,3vJ*ĽiΜ9L6$JKKqtt䭷"::O4S& `NL&SORakkKyy]M+=FVvOw+V ""իW{Fdd$K,1Xi4bccIHH 22tyjyn:"##9r$999h4͛ǖ-[$**ѣGӽ{w4 s%11g}xЩS'Z-C ~h0|g 2!CеkW˜>}:;wd 2ӧOSXX?o%=yyydff2zh|M6o,c%T*1LFsw-lѢƻ[=`믛wԉ8}駟ѣy ׯ_Doٳqpp{LNNF,h4c@TlB`` *Xv-&Rڵ+&+Wb28}4III-III =j^妾wߑAFFׯkt Zr=N8dBq!ZjUkl:%KP\\_֬YC^^yyyر3g fܹ]/5oh4ԍJLL L:"Zj'P_d,Xq())\?gΜiܳOv7n;}4O>Mn|껼|gggs,籱ݽڿk<==9sL?BgΜG͍ǏJKKk1{oRW_%""GGG8rE{ك~G.]J||<7RBԮ[[[z= {{{\]]9qDdqR0a۷o7o?uNEߣعslll,tޝ7|#FpӟĢEXx1|xzzr9&MĩSP*,Z???ؽ{7QQQ8::āٳ'%%%̞=޽{ӷo_J%.?3@J%QQQcccC~~>Æ CYom>nܸZǎ;Օ$ݻ7vvvTӢE :D6m>}:YYYu'K.믿2|Z]?/m EAA/_ͭ>Kjڿ]}%44_"&Mtuۂ HJJjPd2Q^^ & FNjՖ,m۶-Zw/gҥKǏ'33>0s@ii),YFŋꫯ aӦMDGGUcr #,,'xӦM?Ȝ9sظq#!!!|L>kF :_~P[۹u=צMӧ'Nkkk.\H\\={?ʪDs۷/Pup277T 77kkk Bۛ{uֱo>lllGGǛ<==jy_YYmڴATR/ h\·~KQQQS" F j572 eqNaaiؿeggdW^!-- rJ'c]hyNQ*lܸc;dkg׮] nJYY[nh4M1coo <%ފ}EҲeKP*^cO4?eeeDEE1|pV^MRR_#o6#F ))~`t:ƍc$%%˼y8pL2(駟joVCZv-W\!11O?ڒ))) X]+Rm$&&V,ڵkS$PFee%MenSNcz#//\NNtt4˖-3w=ZRSSΞ=-eee5䄣c)++ڸѣG1c\|mVo1Q[ wwwski|1hРj\bbt'O <|0]k;1bDǏ7ި"""1>nt׶AIDATQ\\̴ijm3##PcM'((Ȣ}uȄDϚ^~~>/_o߾Ւz)))wiPwE~"##kHyz*ZREu$&&f~i.\3!}_aÆN˸qRT2v؜tIyG^ٙ &]7ӟG}4cǎGdŵAmooOGGG,YL8q'!Cdܸq<%Kdȑns_ϣ?=Psq%YqK:>ggo93s'˃>|39Օ!Cdĉ馛2szyV:vwO=ܓ8tvv3șg?w_=$+/`{ケ3 ڶ>wk}ݗ_[v|iSO=5~y~*xj]Կ뮻r>o7]Q v;=oқ[qi>|Jwl zzz:ÀͮIm:L0̞={pRGkkk YAKeرW,_<'Ov%yT'~禮kvpOˀ-ѳ_?5{lq\@@6I{{&<}r~!Β=g~vQCRu;3_̈]{r72|X㲥 ^̬٢>3gcw?Ҷӿ=*mnڑ z7ߝ=ϼ2-myG^:#_p[Fv9z}uy߬XS>vsSijZVf;ߝǯD?7sk֞g|?#]>żf]gv)97/ziz:ӝǾZv<v#/|Y䵿<)OYszJn'jCyQihm:*J5#<2K5I虇Rf$Yq*x3R-ϢgJCЌ;I!mi:,ᤒlwINI'oϢ?HmYgqb*liz:J%NIF>G}^ 0lRihʰOCKkz7S6m:1  [ +lt,̌ [2ʲ/ydj~uN)y!cvʒL¹im- }5i[ƭNK3_MM1l6XjSKz-z9w]ikn_ϓל]oU< Sԓ6qi[ rЗ~ϺmWޮomW|eh+M6jt/ҺT2!my斋1l6Fq^rsJqm=y|O!2p'h5#M80y޵bAy$yPz,?ܝd5|ݯ]ޝڲ󛛓z=K_y>s_qڲЭI]ukU;__yu_Y襼p2|뵭|/k}v[>zH6EYޱ(Iһ+ݯIژߛӱ` oP54ޟQt-%IqK%{{]O^}Nz;һ#=>v#7U<ծknn΋//`Mktv`);?ٯtwwP`(JZl%IO4a["c4y8s{m vA%kX? ޥ44fO|o_vkfJ=N(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((Xӓ=paf#{mmmV6l`6$ɸq6`` 7Zmcϩ.-{{{{{{{{{{{{{{{kT*HVᬗT477c~27 x455eV˖-˒%Kxⴵc~27{MSSSF$<Ԕ#GRdil?6w<2b-]4#GaltwwqߺƳ*GUiUյ^緮JnK V[96\ >imm?k6=fۛ9:.937O}z;#MMM9s3eʔ޳;䢋.E]wMcl`-x1cƤ=sO{{{sWN '+~xzs뭷hL3<3mmmille]+Gr9GMccco䦛nʇ?L>=SNך:cfљ>}jtAillȑ#s~757{/X3w1jfϞZ̚5+}O'O>d2f̘YtijNj]JZͯ~w}SV,Ə_~9Zmuرc/})fGzŞӸ3gNv5~!qZI'ikkKkkk~^WݝJwvUcƌɰaoܹsҲi'sѣ_KKKZ[[3o޼e3fȴi/9'o~sC3gN͛:*{j'M#8"g}v/^~+mkm̟??FZm+ٳgO_m?Ç_W}k΃>Һ>:tҾe7|sjZnȝwޙ'|r:qߩZK/4~9 :4j5{Gvqnj;6YdIZ[[3a„jӚz=3g̈#kll̘1cceԨQ4iR_?%ݝl64nooo|G :4{lN?|^i?~L2蘒k-?s9'&M 7ܐ+2~?… ӟ4]vY,Xuww .)[n%_};tuueԩ9,rK>'IrukoI .q[n%7xc?|ӟ3P9ߺx/6l3xS,X~ 6Ufp [y~bpЌ3C9dm^7{ؒbncU'А˗aQz{{:=ugUbPj5===V/jk=ugU[l1ZZZݝ MOOO:;;ܼmxV0ӓ%Kl1WVظ^_q9!y#lMMMijja (((((((o@(=IENDB`linux-show-player-0.5.1/docs/user/source/media/triggers_cue_settings.png000066400000000000000000000464631323636106000265360ustar00rootroot00000000000000PNG  IHDR{.wsBIT|dtEXtSoftwaregnome-screenshot> IDATxwxTṲB]A׶V\]QAUGD]+@H^ i2?F$!߯2s=ޙOν3I ҥK{6'&H.:tP=0tժU m]t7J6ª*&pH]?֬]~-&Prv[rrjl;ALA*%`0`̒ٶC'$I($$({7Y!kp1> ,ҏkUK۪UBcw?{~驺1%,MzoBC;Z>s%4k(;_Ԓt(Nfd*<4X 5=;nΘ%9/ﯜ[oRA6eoK}' J}wשl}6}BUN$)?@Y[7+s8SY -oY95_U84uB%4oz"Ct"J࠷dzԊT떍K_/[aTϕse|z agg(*2L:Ҧ-;C+g׭{_aoJГ+7eN.á]ڡylYgȈ?e)pWeemfYА?A|~ٺs^ޗ+,4XwPӡ*י}ZWjH.kwswiޚ'uf&zuk[綒~״jȃ˔|\.jRW*0H_ =z[JeY3 Ӳ7i㖝٤А`ڿ[V;vЮz 5L-a]>ڵnfS3OMid:ԮuS]ӳ.U>|W(rgT"'F Oz*?:gKm٩ly.(8PgXkݏkSuyDK(OWnSuD+4$PZy $;~R9z` qؕ_psV n6w۾Z4k áWLt\.KSM[vm')+$YBVl[ iǝUxXNf(00@RIN(u$ri*nKY"cn٬YYY9,oTcʺm/s:A>-˲[v.rIg6;7.R7jEM&ŶsA5mRW6Ma! ׮ԶwV~E_>Gkޤ|R  ]tMΒؘHIaSeן>[%4;tT"B\̬m-,-r~ jɑr'F O\n$idEPf>:kS~ANfhݦߔYYqsr%IN/[wuBɗpԋ+r46@Y:r4M[~ݭ-Nԩl89Ql]ͦkӦ;B;|mle}}:!ePqqњu[,ee[ئo7eBuj+'%ķy|b]ЪlryyG-i>4I~eSwY}|*(0@}s}@͛/fSFc/fF,|}}ԬI=L>jϾCz(?{XNؠe+7)?@QẲ{ՍQllb89*''GjѴ59}k%ڷ?Ez\f t WkUZq||}tI|'߽t"C\b({2_|lU=$l|2@MSжr]*>gIR@@@bʒv9$FGuwTJ G0q F0a`= F0SN| 0a`= F0a`= F0OUwx"__ߪFr:*,,T^^,?U=\.UuW̪NRe\ UWUݍ*t:%&9jb^HH,ɓ'/vQcƵ&&ckw0Q]@ We\W`0{I{lUT"%ZfMUwM2Eݺu/յ_U頋:o}tM>]K.ҥKvZXs6m[vz|kߘoVW?f!^zzGԶm[zwrʪy.V_+c=/e˖y޷pBeddoWaay___M>]v]k׮g}VrK>!!A>ZnB߿_o֯__j._U3jk˗_~8Igc駟QDe[SO_-qd]{kB^ {W\q֯_N:),,LjOuz5ydQ˖- keiӦ:|zYu7j̙nfONNV߾}Un7Ї~ѣG+33Sm۶ձcT? WuP5ns?))IO=~7s?cfЄ |rxO5o<͛7Oݻw$%&&jĈUo߮u_ի=5kL#GTttRSS /Ů?נAtUW_G?OTW˲ݻOUVgϞkذa>}ԯ_R,RӦMkΜ9zwT^=Y%ͦ^{Mgܹs/p(<<\}|A͝;WwqegϞ{N|{ 0ƍVRR>3c(Jȑ#5~x}W:yN6mڤݻwUVJJJr/o۵fbcY4hI&)))I3f7V>ԥK=#5kׯ_م֑~[=eY5p@;E4-[Ԕ)S4|%IZ|s>EFF*<<\3gԉ't:n:8pԾ7%3*m߾M 7̙3չsgl6 xyK6moQy2???7NI&ϯ2___?^~%KhԨQ%m۶ׯ~ap zg zİr T۶m|r-X@{.Rŋo5l05lc5e]5rH\.h߿۫gϞr\jӦԿ}g[KOC #C'OُcǎiƍzWP~)/ō}It7N}飉'OeM6ׯ|AOGV^^ޟj_մז{L|ӧfϞ}~zJO%˛>Sq;nkr\ڶm*Dv,?;pά,qƲlJMM6(CK.U^^,Y6m(""o%W֭[qF]uU/i{=~yΝ sfI&ڶmoYovǵC ԬYTPP۷+==>tp\?e˖_ׇ~Ν;K%Jڗgޔ͛BoȐeYTPP5j$I:|[n駟܏Ԩז֭SaaVX\XB_65^CTTW:=*<<\_}fϞzKV@@@ٽ{Ǝ^xA|.2o *`}Q׮]էOIRPPj͘1xƞ={TV-e111i3RRRﯜ 8P]wm۶۞n/Ӳe)77W;U*&''oL.Nqp_SZzbccu"s͘1Cz3<3g?,>.i_FEEi޽ZX;wj̘1zkժUesO},mW˗/W\QOu֭1-+1SlRO羧}?h8lϞ={k;o<-\P=zЫKƠ %&&/W~~$o߾[4}0nڽ{ٻwo]uU8p2224bĈ"W_]e|1EGG{@ԧ(O:z-Zv?{Lki9zN:E^%_ ,_%''o9/zǏ+**u?עE'xB|AY OӾ>^.K-ZPݵ`e6lp}Y>==]7oV޽5|IҰaԴiSY#F(99Y%IҐ!C4rH 6LvZ=ZfRv4w\oU%_e!ChԨQ>|\.>c}%<ƍC=)((HפI{n?^rJȲ,eggSFC=\7N6mro%͝;WcǎU^siϞ=-[b?JgOu>=knnx 7N>:΍Ο3ԼysqUN: jĉeu<Oo^^^C Qjj%~;vZ7o.}uFMzm9^EgJ^=%#oׅׯ_O<g?wkW$i׮]?yڴie*۶mP:٣;S\ۂu3.22R)))^YAdd-[V7w{*3U||ƶդdU9v]~~~L5k,+`wRY(>>^iii^mΝCjΝ*LU65U3{ޚ:>{JLLɓ'~%&&}O2To^4'%]n)d5i{kR_k2ٻ.i޽K_F;)&&FO=TPPg}VN2To*P&&c+&&F<󌂃UPPÇ+''+mq;}w.CVqO8͛d5EMkM8{יYW8O|5ilkR_k2ƹ`_y\<%~T|dϊ;VJ d%j>{#`0{#/U.+! 7fɲ 49N=z" fS~~S˸C'OH(FVV9"bwykΝ`dխ[TK@񒓓╶ `d@Zثȍ(áDmذq!O@8?ch7xCђ}s"GGGkȐ!2d/z*..Nw}Zh@?׆ kE>H7oK/T:wyo߮uI=DUᆱW^yEv*qT 8bt ^Q5kQFiڴiҒ%Ko۶M Oꡇt2Ռ3tw+++si.r͟?_NS]t._|A)++KӦM_DlR7t^xeffy[|A+//OIIIZh+_UvYf髯G ٫D˲Լys-[L RAABCCe~~~1b}-]T7O?-˲[bШQhʕ4x`=3Ů[1_劎V\\,YRX}>swEzW޽{モ$gѴi?(00P6MV҈#vZ}NO2HF9shSƌ߿_kָqoiӦ;v.\XE\>p\8qz!Kj޼իG*44T5ڶm/ <*G׮]e统[~Zj e]&:}/^TT,RnnOn,KEw/v?&.pz5du]:~J-=ú$I[nl[ضmF{Ws{Njʔ)z7ѣ8`+YLm߾]_~ 4h~YYfiZd .]hĉ:x𠻍<=4hn6>WP:ut^|TZZZM6.țo{O7n,W*GZZr:8?⥥3V||NguJo^#$$D'O,w00@֭CuSrr,XJT.]=ss'*[q6wC>g`= F0a`= F0a咔={Vu7t>cxU @ DثfڴiO?T6nܨ3gK.'xBW_}_ j̘1ںu&N6׬YN󾾾Zd-[Vd-Zm6mݺU6muuםKڞI&iڴifϞ_ *c̀? [ܼPqC}&MG}TС9^&!!AuT>W?vڥ:x݀~:u[v;v(((H]v՘1c?uo^^{5=ZYYYj׮N:U9[e`:[^5}g:qℜNVZ}IƏ+BO=~G5nXv],Y+WjĉQddfϞCjʕ/>Ig̘K/PÆ }j׮j*M0AU14ٵk-Z|;n8 :TP=#?~|gggkz衇+446M/^|EM6MiiiӺuck׮Ȍ޽{&w3tUނꃰW;vL֭ɓծ]ʇe˖iرꪫw^\.3FW]u*]r%ӧ$)11QYYYٳ>b>{ァ+R~ƌ#L{ァiӦ{3GcƌѴi4ybw8zu 7(,,LںukWvmEDDh璓Xر9t萾KM:URSSSvj׮RUNl6YUK:233_%-^{TAA͛L5O>ݻn6/z;;wŖ\9֤ItA͞=jJ/СCJMMu]yLPPPǐc@X-^[ifSll._޽Lvv4i".áxeee)33SJHHsoذAk%TڵknA⾱ɓ'5eeddu0a=.oYFߺ馛"áD5kL)))P֭nݺz:xOUzaf TBB^EEE)##Csk^&))I&L5\~ZӧOWbb/^T;v:έf=#GJV^Ç+77WC ћoÇή1xըQ#+00Pۧ￿J3k,riz7ߔu'}_׮]+hԩҥK,CI^qۘ5EUuse_)))1Tv]ɩi}ǚ5k_\r:.WR~2.JrzP\j'xK BjǏWu5q F0a`=yGF3^k= F0a`= F0aOb[~h]ߩ#?Ϭnj |2*'U!~.***uŗ:Ç?uB6]R6?(p]bڮ|~,WڽlǿPpݖE1G-UE>|qrӞ(czRPjjUm:[egy`vN$ LMn$I{fUxNj_\QK|EdsCiӄ;\7A U]W~R~=KY,c3X3['ж)C<7OTA)m~j{/֩}ϫTIUm.={5Xα{u{iN՞(?\<^[_Y}8} gJOQVB=TUS(N ڠ"[] ޒ$Qp|B kQ Z!]e ݱ$)u:c,Wz6MQ/oC_mJTdF9]V }.e{ YEd dy2U-= N*?VM2 9rO:Cuz:Iټf@T}չmw^}#(/hq?e˙,YVXt_e`:w؆>e{\P#g r3ҠE5g X9ӏtrՃe%mUնQD7P~j;=~?F;Mr%1 l_^O>5?%ƅY ׎F]›1PDёճ$I9T!I kIyGtl [r+y2U'ޫ{^OZGp)mr2jq{`)a3J]9]9d wxg)VJ۶]ӱX3T:{r,KyGd›tr/R=,,WrO# Amqn}T/+={-Zmz7@m Ҟ( ANwv~6JG޼Z |c=xOT^QvM^V5UIǘͦ:ъܿLuÛvRa3竗g*tf)(0LQuM#aܭ Qd 'X6f@MQC|۹/Q7D7-T€WUەwVBuq֏hޥ7 `ԩuJKK+RRRxԊRgY.r^z<ߩ7e'tzXq f/(]h=PpZ7A.ʏ~a1U@ e\ F0a`^ꃙ= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`= F0a`=U(+88[}Y_6*<* Bo VXXXHe˲$IqqqB\ SWѩET`0{#Bl,KYYYnVE*[yԶ`lN:))33SNRhhh_e+JTXX(___EEEI*T6___EFFf@>>G_e+T\EFFVCCCt:K,UҶ,%r\{⪂nx^UeAU UW{{we]V2T 3{#O3gJJJҺu4bĈR9(Oمo n׮]kǎ^GMFy `ݺuƍձcGԩSjϛaϲ,=ڻwէO=쳺뮻ʽ.W_ٳgtG;w,!!AÆ Stt6l.*CVV-Z{Lr:jҤG/dwաCeggw%\]n^%I 4СCU^=/TRR^yYFӧO$uU<x]gxt%ֳjݺVZNzr_VRRnfM:U]k͕֭Ǝ?\z.]Çk̙5w\9R?n6}zG2vXwO?_;vŋգGw=zŋK]wy4=QI!/׊+˗UV eYj׮_@;wTzzze{'O5o ۹q-gKhnnbGTTlٲ(BVCBEc^{{{;w^6@XXؤ~5߭m< ^uTTT@E`ڵHLLDuussIBjjӨc ::: ٌCa޽(--űc`2\:}4bbbɄ\ر.]BII 222f|Ołc۶m())AAAN:v0144.ӥXnݤ9Y_T*h"hlAt9o!px[O(˧vfKrưGDDDnI=LI={DDDD2'{DDD䒗?~ //y_sw}sma\R*ZP*g1f ;]\o<ffV###\}smO[:VO-P)Jx{{O#Q\ISa#"")T*T.%7KDDD$c {DDDD2ưGDDD$c {DDDD2ưGDDD$c {DDDD2ưGDDD$c {DDDD2O""""NӮqdaHdaHdaHdaHdaHdw4T*XVFl.ǁ@VOypEjͤ$ѣGPTP* gX0<< N\)I=1IbJBPP@yȑJB@@ f3|||'>\Zӭg, 滌)t:XV WTL'{DDD%" 䟄)J5.>\ZS3-Ԁ4\pE1ICLȥW?|ɽo>#""0wO|}}Q^^&|k.RgXXvڅph4_(**BccpWۓV\ AގӧO~NYYq=@rr2>C޽ `#""4w!bƍu^~ehZ 4ʕ+"d2ᥗ^h|jKs֮]ÇԩS8rB۷~V^,dgg5a#""ZRRRPVVٌ\r˒Ÿvh"={W\q;6މ'PWW˗/q93z; (,,իWo߾ O=z˗/JB[[ h49sxWxb?˗#;;zF}b 88ǃ<ҋ3JꫯP[[k?^^^J?999,[ yyy˳?{:򺲲2l6O\bݺu~""1r;ƍg4Ν;zj|nNs:6>Djg"#""#sm1gVZ'6*)ǰGDD$a4 6l؀GIkhhp{͆ hΧi6>ȩ$<װlH?#""(Z //.-A`4j/>\ZS3IfbzfBp;pEjMUD| Dt:X,L&I~@XV@Ѹ IDATxy\̰ʮi(iMT-iuӾL{ˌͮLzqlJn( 3 03?2?fA\ǃG99o9(=z<ҬY@!BX`0Ϝ9sG{xE="6nڲzB!(Js{zIPVƺL!>Μ={LqJUB!xt$WB!\OYA!BOIi؄4i[=Z?7y@Jɹ{7?c4_@dg!ur8xhaIuJ :ɒ5XB<<=1CJrUXXĕk7 'xi 4mTǶBӝvu.R n\&W> y{шXV-#XBzUKX <"sR+}GyO7._&׿ 3 y&c'Nqu=4 9}ß̴#nnd8J.m˽VVB/qEtlS;p󦚄x*ӫ{{ ܔ[˾C:ط^@PP+b5{\]],wш`F#뷤re ~yv@(Nyt 2r3u/\q wzۃF )l<޶%{=LVThϝ~w*7-=3noDQo^q/Su|ǂ!z\OhNܼ&I\DU!R*F':>֐1j!YK 䝹@^ϙ(Jz6'"<3$)m?+N++ܠD'_!9W7kJeGG`4R˾CthTM3p&QT4 '9WZ=6ƤVkՙ.\j*תE_¯YGy#4 hHIq rN`41\c*Tq(ymYT)>9(Cq!1,!p+bʍ> |'-[Xx87+qSppPѴ?^^ޗ[=Wڷ~BAV_:o z X%[j\ihQT*K|neс[2 {_oT*̓r2VMjgY*^۳2.?sQzi,'N7)mɻIBPҠ/ksBWܫ';3L$ԏ bΏBqV-wrBj:p8هOڰȺEQMrp]F ѨB g/\Vͫ.(gklMBTK^ϱDEQC%ڢn~1TWoؐ6zlS,!A^xFetsߟK5d B!,!B;K!$B!3IB!L,!B;StEE(BaG2%Bag` !Bؙ$XB!v& B!I%Bag` !Bؙ$XB!v& B!I%Bag` !Bؙ;T*lM!xP(0t ၯo⺓ݦ !55{U[#echРqqqPm4hǏ^z<DGGǒ%K0Қ ̙3޽{2ŝ*ۯT*/^?_Wj5;vŋwYn'NDTҴiSΝs=z,eW||i{zz:.Qwgnû޼3b>g=Q*̘1NJAAmڴʕ+vwٳ 2Įux{{`̙3cxyy35ZUt:T*K.R7n`ٲe7gggƎŋYhրرcZִmԩlݺLFiޢE bbbHOOg1=ٳٷoDFF_sN222xMvZXj͚53STTڵ?'>>޽{n:>222x7Q*,\222qpp07߰k._b:N˖-Y|9lݺ~YOT2aIIIaժU{k\YR$~m>C,YY۷'==TFRΝ5k{?&99͛ñcǬRetLKmϘ?i}L>±4h7˗/իt:vͩSʕ{TnڴiX{^%,1 :utBBBʽfxb^yS듙z答5竔3a956s|OWu}ksgkKm$**m۶K/Tsv?ˋsu*hт9szj{7n ѣGdV\i|UM߲zjbbbLfXr%| k׮e>eUV,Y]#\;'O2g_^iGGGLoN3w:p.\m۶4GUu|@zz:_5fsX^1-9s&[fу A˗/~͛Gg%114P( ]vSOh &MOrrooow?'88Z^R?W^|嗴jժ|׬\CƤI0nݚH} ƴi(..6]iKo㭷ޢSNFcذaL81cQ!Yfqqf͚믿˵U{$2w9r$-[իf/o5>vYvI|}}i۶mm{졸={K.`4eŊ!**ӧgq F#:㏬]^zU(ge턅Rŋٳo߾}M}|Çټy3fUyHLL[n)uV띟7[Wu>Sӧ٦~Baub&fko-mMV_]vQTTٷoYe``ddd͜9sܹvڡT*IMMh4BP*j7… F&V5>ζ-kyGyWy-p?˖-̙39s[տRSSOBSTTܞ=}t`hӦM2j<IJJ2~Y\]]QUTJ;à F#( 8|2;~8SLa֬Y\zӧsNuUgߟ .X\SپFu{MAz,tR/_NDDѬXyU(kٳ;w.]СCoߎV%,,'NơC*gL8/0y~:{o߾$&&ҩS'Ǝˀ>l}FJJ ?x,fM̶\/˾o-mMV_'N_ *..&66 6Ylxzzp...|L27n?~ٳWΝe˖+&>Ԏ볔mCfѣ[n5H///9‘#Gl*vIȪU*{%նq6teN8av T*""~K/qM 986lO?͏?Hddٺ3kںuk4ibJi.zĖy6wڕ@۷o*SRR–-[P|駕&XeUumF߾}yǙ2e ZѣGᅮX'[_ƍ~Oбc*a)~\\\:t( NllM5cBQnA-1 ZGEcZD@@\]] @דZl]̚:99ѳgOϟϿon޼iNsq;woooڴis5`7Uh4hRY=XjDEE_P)OOOx L kǯ󒘘Ȁ~ٳt:^}*_UFQRRCBB{fȐ!lݺzkUuKUzins}~g٫Lwʾj{ӦMhӦ aaal޼Z^RٔC!Cp!F#A@F#RRR¾}h4xzz^6ln' *4)u:z[l׬YVё^{W(ȑ#qttˋ˗/[: 5N"""صkjڪ:7x^xd.\Htt46W0zhƌCJJ ɼ{ՊԚ5kvIII,X+WTOaa!cƌaر$''O߾}-… INNf_RYjk\\ٳ'2}t,XX>z*3f`ٲe$%%?g`UeÆ $&&ҹsrY?m۶xr)ٴiGeҤIpuu%$$+WM\\OjˡCpuu%--tرc/V,FÜ9sX~=w6yvYYY<رô-iu>S`=caubs^/m#{u xiҤIV-O?e|w|պ\\\xG={6 ,X3g bҤIˬ]5k0j(5j?OV^ʕ+fUTTĄ d͚5|7̞=%1եjYd -aÆfQk׮VY~=F ׿^xnݺѭ[7yƏ?FM6q-'X.]Xs#Fg|mc-S["}qw3];Tk9_w߽>>>վF] DUZ~~>׿ݿ]fS~~KI{1 fޜEEE]{]~l\q%x8͙3֭[Wסă`t}R"ck_УGZnMÆ Q*5;w{UyسfCP/lKVKrrr<Q,!Ͼ}?B؃J?;U|L"B!jFյN~hZիW녅(Jj\%%%t\Fqss֍B{-Tt,`1uqq1ɩF7@^`0jmZ՘"@B!ă OO ϑ,n'#[QյMT`sr6N* ӺB]qq1 ^oUG IDATYN' +Qdh8 *B9::XaDB!L,!B;K!$B!3IB!L,!B;K!$B!3IB!L,!oooWa{ {=ڵkyyy/vӸƎKvv6uHHH^Jرc|=zC"IDmڴa֬Y̛7hZ-=jCGܹsu.zpww'22""( >Sf͚Ell,7oޤl~wڴiƍMU*ooo,Yo͖-[x+̢Eذa?#M4nݯ_(6oLRR#G/G{[MBZ͛iڴ)΀kIRR&MbڵlܸAUST׳e+n=hтyqFVXA>},S$?\۶mFaȐ!\m̚5 DJJ ~ƍvZϸqd޽ٳg3df!Ã^x骼xzz/ĉi޼y: }C aȐ!<ㄇĿo6o̠A=z4k$X Ο?`v111:T*INN`ǎt Je*!JJJAPx~dh׮'NZw^8|0 <}0fnݺG}ܾN5lذB)DY2E(1|L4gy777T*m۶y\x///Zj]vaÆ>^x5kWyB?@=ѣUג)Spy4 -bҥdffr oMӏ:?7xuֱrJz]x(tR; mB:Ͷm۪5.=B!I%Bag` !BؙB!3B!3IB!L,!B;K!$B!3RppgD !jOQQ*.#!Dmzt'Lt:.]dsBa-777Q(WZFGBQel"TTܸq\x%JGBQed B!I%Bag`[U !B`ҿ{V)Bq߱[@HHR!j^Qo߾=۶m#--Au8-Q(uY{P.%X `ǎhZӶSuV2339ri{-!==۷3b0%%ٳgo>/dΝdddO aڵj*5kf!sz٘ƍGbb"aaalܸ#mݻw/۶mCR+HJJ >N`` 3fݮ=z=Jll,ڵQc+22-[۶i& رc\W_}毾)//ݻ_:*wРAmۖnݺn:)>vI|}}i۶-ٳbك^ߟ.]h$77+VϖOדV%55@ZZ7k׮(J[nBB<|1EEEѮ];>sݻ7֭> ##7|R… III!##S\| v"++/lْ˗֭[ׯJ%&L >>VZe;9s&|nnnxxx0n8f͚e*Ӿ}rY*\<==ñc,\_ZT~~>4o*oʶiҥL6±ă. VIMMET^ӧŖ|kZ}0~xJJJ\M9~U\eR_' ZlR(DEER/j%8p [nK * qƜ;wΦc^|'N>0?jڦz{qD\Lw?>EEE N3gΰzj-[Ɗ+pa.βl/sѩS':DZZ/__Gкuk-_5~ }DDvzHo0m4ƍGaa!dffcq5 G&**O>I!ăaZʪeGKɈ-SK/͛7R|2~~~6dž x4[WU}VU[֭[W+WwUq!2^>c$$$p7wR|g}п֯__~;w6~M:t_|>}ЧOIx݋#T*00j5X{''',J@@z j5>>>fgִƍ,]\bTܹsx{{ӦM˭257ח{6olZog_jΝlٲ^|E'X+V`ΝE!j#/,\4n1cƐBrr2^b*f]FRR ,ʕ+USXXȘ1c;v,ӷo_pBپ};ׯfgmkW2c -[FRR9{8i6󟄆һwo7wF#SNe„ 5w)tb Xm:B DUZeף#FwOw/T[oezTI,[,!-&Xv}سB!K!$B!ZI|||ͭk}l۶4BMϵBFqqM55g^Q6qƑHXX7nȄ5R)B憧N !55V6& Wq9 !McZ):u*ݺu# YfdZhԩSyGt,X+Vúu8x O<j3fЫW/z)J%}۶m $$s9>#)**v2m4&NHrr2Æ c,^cxxxn:K^Wc޼ylْ/MϷ~KbbJ%Ǐ'"".\+BIIٺ+hB-v˖-mڴA1vX&O ,ZxBCC=z4&L0=iӦ,]=zzjfΜIBB̝;z,X@hh(DGG)**CEɡCЧO/^` ::pܹ3bbb cԨQL4 ooo駟%44C{nuԉC2b))).s}&B{],___ڶmKrrr{졸={K.`4eŊ|86733???- i:֘JRPPZÃO? suu% ^OFFju٫τBqwx+""]vV*x76mƍh233ѣG'|^5i$c*f:t@RR.\USXXȘ1c/JCnnnmT]#۷g۶mú !Fc?qqq$$$?9shٲX:=le+66t{E٘ƍGbb"aaalܸ#mݻw/۶m3=#)))W8̘1ww+c)I3f   |MJ% .$%% {Lq}7ڵ,򗿘ӲeK/_Nzz:[n_~S*L0xRRRXje2g̙|1n8f͚e*Ӿ}rY*\<==ñc,\_ !xU5d4qsscРAPXX_5+WdС1i$Ӿ2j(>3}6mZnK3gi۶-{3fuV^z%ƍGfffǼg###ٲeKm6mbРA;ɓ'₣#-">>PF̈́ ٳ'M6eҥуիW3sL eܹL<Tς  %11h1EEEq!߿?999tЁŋ1 DGGNxx8;w&""WLL aaa5I&퍓?2tPvm1N:1tPFA>}?~<%%%UeNNdwe?~ܪsj.βl/B<,%(c֭lٲ6mHvP*b4IIIC(JӾ裡#GMhhh)SNa0t=z@ڷoOaa![l`0Vϯ/m۶Ϟ=ziԨ111FrssYb>,G-_jj*G6M{gеkWJ% Rfc~ICi֬deep4 5FڵkLٳqss#88{?ԵkJz'YFFŶΜ9͛7/̀'YB;éS?>{ڵk<ӓ+V^p쒣SNQ~r۔J% 777?ncԸvIOjj*z=EEEBO. ѦM *kx{{dzٳV2*wy鉇999F Q(q e,wqL¬Yz*ӧOgΝf몪,̙3^e˖d.\`ue.βlm_ !xX)o9s&\vWr)| xyyUח#G #44cǒχ~+WhРA,2E8p@njUK.T+7!җ/_ĉ)ҟ[b*A~x饗ٳ' e4kl]UYUm>}:sέz꙽BeqZB5aFҠALe%E5h4Lپ};:^OCѠVqssEFwFx{b ;v,7RcqqqaС( >|86733???m  [c*HAAjBBBji񽓓suu% ^OFFjuY3kz .]ʭ[*v9iӦ uӚW/B<<͛G޽;ZI&/vZ֬YèQʕYr%SNeڴi\pV˒%KXhYYYܸqիW3c N㣏>_g͚5ЫW/Y5"`׮]Xx76mƍh233mzFaDEE'nM4JY:ą |r2f &Ohd,_l|AAA̚5 777܈{TV>6lXZի̘1e˖QPP@ZZ+K!?s=ɓ3)BM^u +"-Ǘ}͓]?#&5~O^EL~*k**GuZm4z<ŝ-NoG_LoӦ捡= %$ӔE^i%|vML̶A~Bh6R?"ǗWv|n{ WGhJtB!`DKJuD/yrfL`ˠPk4x}FKCpk{PkOبf<#JqhWK^Q4^'xv&qšJcj9K4]v@r3,ODBB$ PRk㚺NH A"* jڊlJ_c:~룷2זΖ[oZq-u>G[]C*s JkXTJ!9?drN^ǣǽ~r2^ss|cWh bW_Csi-3#! h|G l}7ozϘB4p6]Mf,k/1ڿlm? j;CHo7^fR5Ke䬅vj@]q.B̈́J=:5*8{Dlo""Raw\{Sq{K9ER7Jy3Rޓ QD5Ô5 ۿp0'!wC""1<.A!TPڛn@G^Q϶onǟOp=  `ך<eOC[1td PiD}[ }tM 5㗾ϳ"ph8n_ I cy[3^KQF#5CN>=,R|c 9'_1&!pXtD@P]G2O@kE@]0[/A;\qE"$zfmKkPq > ù]P}W^w ;c Gfz[ j΢O 6Trk)W eee _jNqޅ҄Y TݾHJOv\7Oק>ζV\9E6^ĕ\DݽG󆎛zUS֦:h.\>뽟;fnnBé|$~E>G[ lA>&ڡ[$ǒ[3_zՄa=NAãapmIN_ZK_ m:"g⫽s/5'D\\=GrN"""%VC_uoras/4Zܾ + h<v\uo1 o[DDDrHq:EX{GƀEDDD~ Xz5551tL2())Ajj@j5!xb s~u|DDOр+9d\Szz:9s?ʔws(((@@a4 PZZye :TzϞ=j={yyy<} 9NN֮]c=cذan ho]yy9&LC!>}ωb+** 111(..Vj>ZSxx8*++!"DW _bɒ%n=#P?{=zSSSٳgcx7T G?r{>99nGDD`0L=s""Xg͛qaTTTGu=>n8梴FK.qjo6׿d2ax饗pQs>%%%سgnVɚ2220yd0 HHHn:'JBvv6PVVz jU믿cǎO>kc׮](--Ç1o'wSN/:[om?3՛g_bk8tc| RSSflܸZ;v`@bb"VX3999뮻w^dee!??7k۷#11GAffdM8s 2220|TWW;bARR}8Ndff"99ɘ1c1csÆ @OB^^Ǐ7}taҥHJJ3:SO=m?3՛sDD Xvĉ8qF3;nЛ QQSSݻwc…ns8(..FKK t:QRRQFf͚J|̞=u͑TM]{hkk|WE6 UUUnG*++ގSNjbȑ5kV+>#ٌ7n܌quuu> !!^kꍃ'*6/)/+`(..j@[[k_kCjkku]O6b˗ &Oj*?Ehh(t:m'"Z[[!"##qʕnw9 غu+ꫯѣcy[3^/]{bΝpUDDDRuLf_}u*N;=ӰZ4i$7 Xr%T*m6+%%.\|^knG;)9g7O#"WHzgvF A\0F舜ׯ_CIMRRR0o<B$UZp8i&GCClRwZv-°i&,\555hjjBjj*, F6lٳga4]+5Wo""ϧz=MvFɓ'jA0zh,Yyyy= :Ӛ:EGGbl6C!&&Ƨ~zB@DEE(8l6CK%f"''-uCXXbccv T/{)|駮kѣGq!X,,^Xv~g}aCCjkk_Fѭol6e_O(7?y瀕cǎl6f?Ed2!;;ѼV+Vʕ+QTT-?DSS }v444x}Mkk+V\5kd2`0{oΆdhāp)ɱ֬vjllĖ-[sN'? ._,[/KkHLLDBBlRLEl޼ׯGjjvmؑ#G:V۶mÁ0vX_ IJ}fODD 3grXt)jkk]3cMzzz~hl. +왈Ha XDDDD c""""R/Kף?)S%%%Nީj$''ۡ >uﺎh)\\>X\Szz:9s?ʔws(((pFAQQJKK{=Ott4lقC*ZٳgQ]]g"//'OΟN=Ο?aÆ=n0֣yw0aB:?E(--dƒ>Nכ v(ĠX!kMᨬ(3ۼy3> <裮Ǎ\h4bҥ:N_WL&,X/=2= &&}JJJg׽<Քɓ'_`@BBߏu֡ O<T*QTT2[Pծ^u;v x']?vBii)>y֧R~z aϞ=PղcIgeeaݺu t:c֭mLv4+ 555 S/_j-܌>UUU~O nԩSm [ou}Lf~\~(/@yy9f̘{غu+G։hP,`-Xr{O>Ajj*֬Y7BB`ǎ0 HLLĊ+~zMnju]ػw/Dps۷#11GAffdM8s 2220|TWW;bARR}8Ndff"99ɘ1c1csÆ @OB^^Ǐ7}taҥHJJ3}:~a^NRuLfw߾}Bee%MUVlrR"===͟dM&V+***AB5Q]]ĉ˗s>˗/Ǖ+Wp13`wGqq1Vt\7߸]R[[nz͵#F ,,._`f5yUV?)BCCP]]m;Q A+WtFs^֭[؈W_}Gۚz%ݻ;w|W"""§fR5˭T> k۷%%%SqrK駟jŤIPVV&9`ʕ+Rm۶^)))p/ZSov;yGɹ>S='o&$$`XhDQDSSt:]{"" X<Ӷ׮]ѣ!ƨQz|ׯ_CIMRRR0o<B$UZp8i&GCClRwZv-°i&,\555hjjBjj*, F6lٳga4]+5Woo똘kxG#N0>"6mۑ9'OVEZZAѣdhފ DFF-tFOk  N %[_pp0p8PVV ^/9ܚ~`ԩaaaEPP5DRuҿZRS駟 _jNGšC`Xxb }YY /KFjlhoo ~=gƛr:** xݎ>ڵksJIIc`6}fǢE`2LTTTh^Պ+V`ʕ(**drQOk ؾ};+WĚ5k`2`0p=7zhdggd2h48uXrk^;566b˖-عs' /_ӗz_{5$&&"!!Ao&"6oތ#55Ur|DEEu^ȑ# sbcՊm۶;vo$>3f#^ˈ?шts=hח 0sL('((iK۵9i0_ׁwWZGGG[D +왈Ha XDDDD c""""RƀEDDD0,""""1`)Ha XDDDD c""""RƀEDDD0,""""1`)Ha XDDDD c""""RƀEDDD0,""""1`)Ha XDDDD c""""RƀEDDD0,""""1`)Ha XDDDD c""""RƀEDDD0,""""1`)Ha XDDDD c""""RƀEDDD0,""""1`)Ha XDDDD c""""RƀEDDD0,""""1`)Ha XDDDD c""""R_ IDATƀEDDD0,""""1`)Ha XDDDD c""""RƀEDDD0,""""1`)Ha XDDDD c""""RԽyQxx J(EQˆ_ p:hoot=8UMT*h4rK8F!h4JF'vVk a6p8!Nuh4;VDGGs82 R0tP :uuuAA@T* f tYDD}ҫhׇ!*jN5).*jfT8ѠЫp[o]y!GY6X G`PE4xT3^w:2ɏN'EDzVꔮ4p:0Ur:vCCB\v ?܃ED~bʕ:1B隈W__f^> #XDz} nGcaX>|8Шj?,"""?} r,"""R̰a`2#XDDD~FOtt4VZI&!$$/^Ď;pĉjm}9 %c՘8q"N'jkks,"""?#@&NW_};v믿bNWRTزe rssyfX,ƢaPRd"""6lmېz̙3:+cBx饗?񏨪Œ3]vaΝcgȑ#aXR7q-*._oz 6 n#WR+(//LJ~3gVX+VkψDGGȑ#w8ضm,Y%K .. Eطo~׿5V\ NZ-[x衇 jǟg6lp%UFWh0abܹΝB=WpH .tfٳvjv|hiiADDLV:tNuۘɓ'CR(wy'T*U,ӉgyeeeĶm0c `ҤI 孇,"$""r F!#* ˖-üysuNEl6:uUDDBCC{ncW^VU.v;pA$&&gŁsNqIҥKG]]L"كbs XDDD~Sr _dv{~ܹHLLĚ5k܌_WncuSE444 <?p(ٟ((..ƴiӐB(O˙3gիWcժU2d.^۷O>All,݋k׮x6 o~z;vZ\xQ޴Z-nv,^zfFg6QQQpZZZ${P0sDDDQss3>eISSt:kr5{#XDDD~f01`O㲯뮻}c"""lCi @{{;^q8 QTP󗖜NlXyٓS=yja`bpՊ S `N`_. In order to work, this plugin needs `OLA `_ installed and a running OLA session on your computer. How to use ---------- First make sure that OLA is up and running, ``_ gives you the OLA interface. Timecode Preferences -------------------- .. image:: ../media/timecode_settings.png :alt: Timecode - Preferences :align: center | To enable and setup the plugin go to ``File > Preferences > Timecode Settings``. * **Enable Plugin:** enables/disable the plugin * **High Resolution Timecode:** enables sending every single frame, to get more accurate timing * **Timecode Format:** choose between ``SMPTE``, ``FILM`` and ``EBU``. The format has to match the timecode used by the software which receives it. Timecode Cue Settings --------------------- .. image:: ../media/timecode_cue_settings.png :alt: Timecode - Cue Settings :align: center | For media cues you can decide if it sends timecode or not. This can be set in the ``Cue-Settings > Timecode`` Tab. * **Enable ArtNet Timecode:** enables sending timecode for this cue * **Replace HOURS by a static track number:** if checked, the ``HOURS`` field in the timecode is replaced by a static number, which can be used to identify which track currently sends timecode to your lighting software. .. Note:: If you work with multiple cuelists on your lighting desk you than can choose the following setup as example: * cuelist 1 refers to track 1 and uses HOUR=1 * cuelist 2 refers to track 2 and uses HOUR=2 * ... and so on linux-show-player-0.5.1/docs/user/source/plugins/cue_controls.rst000066400000000000000000000027271323636106000252540ustar00rootroot00000000000000Cue Controls ============ Provide control over cues using keyboard and MIDI. How to use ---------- Settings are provided per cue, and are accessible through a specific tab in the cue settings window: Keyboard -------- .. image:: ../media/controller_settings_keyboard.png :alt: Linux Show Player - Controller settings - Keyboard :align: center | * **Key:** the key (character) that trigger the cue * **Action:** the action to be executed when the key is pressed (Start/Stop/...) New keys can be added/removed using the buttons at the table bottom. A key can be any single character that the keyboard is able to insert, so special keys are excluded, Upper/lower cases are considered, so "A" is not the same of "a". In general, what is taken in account, it's not the pressed key, but the typed character. MIDI ---- .. image:: ../media/controller_settings_midi.png :alt: Linux Show Player - Controller settings - MIDI :align: center | * **Type:** The message type (only *note_on/off*) * **Channel:** MIDI message "channel" * **Note:** MIDI message "note" * **Action:** Action to execute when a matching message is received * *The MIDI Note "velocity" is ignored* New MIDI messages can be added/removed manually using the provided buttons, or can be captured directly from the device, when doing so, a filter is provided to select the type of messages to be captured. The used MIDI device can be changed in the application settings ``File > Preferences > MIDI Settings``linux-show-player-0.5.1/docs/user/source/plugins/presets.rst000066400000000000000000000023521323636106000242340ustar00rootroot00000000000000Presets ======= Allow to create, edit, import and export presets for cues. How to use ---------- The main interface is accessible via ``Tools > Presets`` .. image:: ../media/presets.png :alt: Linux Show Player - Presets :align: center | On the left the list of the available presets (sorted by name), double-click to edit a preset. Multiple preset can be selected using the ``CTRL`` and ``SHIFT``. On the right a series of buttons gives access to the following: * **Add:** allow to manually create a preset * **Rename:** rename the selected preset * **Edit:** edit the selected preset * **Remove:** remove the selected preset * **Create Cue:** create a cue from the selected presets * **Load on selected Cues:** load a preset on selected cues On the bottom: * **Export selected:** export the selected presets to a custom archive * **Import:** import from an exported preset .. Note:: The archive use a custom extension to easily filer others files, but it's a standard zip file. The following options are provided in the cue context menu (right-click): * **Load preset:** load a preset on the cue * **Save as preset:** save the cue settings as a preset .. Note:: Preset are saved under ``$HOME/.linux-show-player/presets/`` linux-show-player-0.5.1/docs/user/source/plugins/replaygain.rst000066400000000000000000000026601323636106000247040ustar00rootroot00000000000000ReplayGain & Normalization ========================== This module provide a simple utility to calculate a `Normalized`_ / `ReplayGain`_ volume for media cues. The values are used as *Normalized Volume* for the ``Volume`` media-element, if a ReplayGain value is already stored in the media-file metadata (e.g ID3 tags) it will be used. .. Note:: The original files are left untouched. How to use ---------- Via ``Tools > ReplayGain / Normalization`` menu the following options are provided: * **Calculate**: Open a dialog to set some option and start the calculation * **Reset all**: Reset to 0dB the normalized volumes of all cues * **Reset selected**: Reset to 0dB the normalized volumes, only for the selected cues .. image:: ../media/replaygain_dialog.png :alt: Linux Show Player - ReplayGain dialog :align: center | * **ReplayGain**: Use ReplayGain normalization using the reference value in dB SPL (89 is the standard default) * **Normalize**: Use a simple normalization to the reference value in dB (0 is the maximum value) * **Only selected cues**: apply only to the currently selected cues * **Thread number**: Number of concurrent/parallel calculations (default to the cup cores) .. Note:: that the process may require some time, depending on the CPU, the number and size of the files involved. .. _Normalized: https://en.wikipedia.org/wiki/Audio_normalization .. _ReplayGain: https://en.wikipedia.org/wiki/ReplayGainlinux-show-player-0.5.1/docs/user/source/plugins/session_uri_editor.rst000066400000000000000000000015041323636106000264550ustar00rootroot00000000000000Session URI Editor ================== This module provide a simple utility that allow to replace the media-files "paths" stored into a session file, this allow to move a show to a different PC with media-files located in different directories. How to use ---------- In the menu ``Tools > Uri session change`` open the following window: .. image:: ../media/uri_session_change.png :alt: Linux Show Player - URI Session change :align: center | On the top, the ``Session file`` button allow to search for a file, on the right a ``reload`` button allow to reset all the changes. On the left, a list of (split) directories is shown, those can be selected, and edited, when the ``replace`` button is pressed the list is updated to reflect the changes. The ``save`` button will allow to save the session file to a new location. linux-show-player-0.5.1/docs/user/source/plugins/synchronization.rst000066400000000000000000000030171323636106000260070ustar00rootroot00000000000000Synchronization =============== The goal of this plugin is to allow two or more LiSP sessions running on different PCs to be synchronized during a performance (no edit synchronization). The two session are supposed to be identical, so, only user interaction with the cues are replicated on other sessions. How to use ---------- The plugin usage is quite simple, the only thing to do, is to add the remote session you want to "control" to the peer-list, this can be done via ``Tools > Synchronization > Mange connected peers`` .. image:: ../media/synchronization_peers.png :alt: Linux Show Player - Manage synchronization peers :align: center | On the left you can find the peers list, on the right the following buttons: * **Discover peers:** Allow to search for other sessions in the network; * **Manually add peer:** Allow to manually add a peer, using it's IP address; * **Remove selected peer:** Remove the selected peer; * **Remove all peers:** Remove all the peers. To easily retrieve the (local) IP address ``Tools > Synchronization > Show your IP`` will display the current IP address of the PC. How it works ------------ Once a session is connected, user actions are replicated on the connected one. This is achieved by sending some info over the network, by default the ``8070`` and ``50000`` (for the discovery) ports are used, those values can be changed in the configuration file ``$HOME/.linux-show-player/config.cfg``. Two or more sessions can be mutually connected, this way all the sessions share the same "state". linux-show-player-0.5.1/docs/user/source/plugins/triggers.rst000066400000000000000000000011331323636106000243710ustar00rootroot00000000000000Triggers ======== This plugin allow to create triggers based on cues events, and to assign them a specific action on another cue. How to use ---------- Triggers can be added/removed and modified from the cue-settings dialog of the cue from which the event is generated (source): .. image:: ../media/triggers_cue_settings.png :alt: Linux Show Player - Triggers cue settings :align: center | Events ------ The following events are available: * **Started:** The cue is started (after pre-wait) * **Stopped:** The cue is stopped * **Paused:** The cue is paused * **Ended:** The cue is ended linux-show-player-0.5.1/i18n_update.py000077500000000000000000000101661323636106000176260ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import argparse import os import re import subprocess import sys try: from os import scandir except ImportError: from scandir import scandir parser = argparse.ArgumentParser(description='i18n utility for LiSP') parser.add_argument('locales', nargs='*', help='Locales of witch generate translations') parser.add_argument('-n', '--noobsolete', help='Discard obsolete strings', action='store_true') parser.add_argument('-q', '--qm', help='Release .qm files', action='store_true') args = parser.parse_args() # pylupdate command with arguments PYLUPDATE_CMD = ['pylupdate5'] if args.noobsolete: PYLUPDATE_CMD.append('-noobsolete') def existing_locales(): for entry in scandir('lisp/i18n'): if entry.name.startswith('lisp_') and entry.name.endswith('.ts'): yield entry.name[5:-3] # Locales of which generate translations files LOCALES = args.locales if not LOCALES: LOCALES = list(existing_locales()) print('>>> UPDATE EXISTING:', ', '.join(LOCALES)) def search_files(root, exclude=(), extensions=()): exc_regex = '^(' + '|'.join(exclude) + ').*' if exclude else '$^' ext_regex = '.*\.(' + '|'.join(extensions) + ')$' if extensions else '.*' for path, directories, filenames in os.walk(root): if re.match(exc_regex, path): continue path = os.path.relpath(path, root) for filename in filenames: if re.match(ext_regex, filename): if path: filename = os.path.join(path, filename) yield filename def create_pro_file(root, exclude=(), extensions=('py',)): base_name = os.path.basename(os.path.normpath(root)) translations = 'TRANSLATIONS = ' for local in LOCALES: translations += os.path.join('i18n', base_name + '_' + local + '.ts ') files = 'SOURCES = ' + ' '.join(search_files(root, exclude, extensions)) with open(os.path.join(root, base_name + '.pro'), mode='w') as pro_file: pro_file.write(translations) pro_file.write(os.linesep) pro_file.write(files) def generate_for_submodules(path, qm=False): # Here "modules" is used generically modules = [entry.path for entry in scandir(path) if entry.is_dir()] for module in modules: if os.path.exists(os.path.join(module, 'i18n')): create_pro_file(module) p_file = os.path.join(module, os.path.basename(module) + '.pro') if qm: subprocess.run(['lrelease', p_file], stdout=sys.stdout, stderr=sys.stderr) else: subprocess.run(PYLUPDATE_CMD + [p_file], stdout=sys.stdout, stderr=sys.stderr) print('>>> UPDATE TRANSLATIONS FOR APPLICATION') create_pro_file('lisp', exclude=('lisp/modules/', 'lisp/plugins/')) if args.qm: subprocess.run(['lrelease', 'lisp/lisp.pro'], stdout=sys.stdout, stderr=sys.stderr) else: subprocess.run(PYLUPDATE_CMD + ['lisp/lisp.pro'], stdout=sys.stdout, stderr=sys.stderr) print('>>> UPDATE TRANSLATIONS FOR MODULES') generate_for_submodules('lisp/modules', qm=args.qm) print('>>> UPDATE TRANSLATIONS FOR PLUGINS') generate_for_submodules('lisp/plugins', qm=args.qm) linux-show-player-0.5.1/linux-show-player000077500000000000000000000000731323636106000204610ustar00rootroot00000000000000#!/usr/bin/env python3 from lisp.main import main main() linux-show-player-0.5.1/lisp/000077500000000000000000000000001323636106000160735ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/__init__.py000066400000000000000000000003771323636106000202130ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Linux Show Player Cue player for live shows """ __author__ = 'Francesco Ceruti' __email__ = 'ceppofrancy@gmail.com' __url__ = 'https://github.com/FrancescoCeruti/linux-show-player' __license__ = 'GPLv3' __version__ = '0.5.1' linux-show-player-0.5.1/lisp/application.py000066400000000000000000000151211323636106000207500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import json from os.path import exists from PyQt5.QtWidgets import QDialog, qApp from lisp import layouts from lisp import modules from lisp import plugins from lisp.core import configuration as cfg from lisp.core.actions_handler import MainActionsHandler from lisp.core.memento_model import AdapterMementoModel from lisp.core.singleton import Singleton from lisp.cues.cue_factory import CueFactory from lisp.cues.cue_model import CueModel from lisp.ui import elogging from lisp.ui.layoutselect import LayoutSelect from lisp.ui.mainwindow import MainWindow from lisp.ui.settings.app_settings import AppSettings from lisp.ui.settings.pages.app_general import AppGeneral from lisp.ui.settings.pages.cue_app_settings import CueAppSettings class Application(metaclass=Singleton): def __init__(self): self._mainWindow = MainWindow() self._app_conf = {} self._layout = None self._memento_model = None self._cue_model = CueModel() # Connect mainWindow actions self._mainWindow.new_session.connect(self.new_session_dialog) self._mainWindow.save_session.connect(self._save_to_file) self._mainWindow.open_session.connect(self._load_from_file) # Register general settings widget AppSettings.register_settings_widget(AppGeneral) AppSettings.register_settings_widget(CueAppSettings) # Show the mainWindow maximized self._mainWindow.showMaximized() @property def layout(self): """:rtype: lisp.layouts.cue_layout.CueLayout""" return self._layout @property def cue_model(self): """:rtype: lisp.cues.cue_model.CueModel""" return self._cue_model def start(self, session_file=''): if exists(session_file): self._load_from_file(session_file) elif cfg.config['Layout']['Default'].lower() != 'nodefault': layout = layouts.get_layout(cfg.config['Layout']['Default']) self._new_session(layout) else: self.new_session_dialog() def new_session_dialog(self): """Show the layout-selection dialog""" try: # Prompt the user for a new layout dialog = LayoutSelect() if dialog.exec_() != QDialog.Accepted: if self._layout is None: # If the user close the dialog, and no layout exists # the application is closed self.finalize() qApp.quit() exit() else: return # If a valid file is selected load it, otherwise load the layout if exists(dialog.filepath): self._load_from_file(dialog.filepath) else: self._new_session(dialog.selected()) except Exception as e: elogging.exception('Startup error', e) qApp.quit() exit(-1) def _new_session(self, layout): self._delete_session() self._layout = layout(self._cue_model) self._memento_model = AdapterMementoModel(self.layout.model_adapter) self._mainWindow.set_layout(self._layout) self._app_conf['layout'] = layout.NAME plugins.init_plugins() def _delete_session(self): if self._layout is not None: MainActionsHandler.clear() plugins.reset_plugins() self._app_conf.clear() self._cue_model.reset() self._layout.finalize() self._layout = None self._memento_model = None self._cue_model.reset() def finalize(self): modules.terminate_modules() self._delete_session() self._mainWindow.deleteLater() def _save_to_file(self, session_file): """Save the current session into a file.""" session = {"cues": [], "plugins": {}, "application": []} # Add the cues for cue in self._cue_model: session['cues'].append(cue.properties(only_changed=True)) # Sort cues by index, allow sorted-models to load properly session['cues'].sort(key=lambda cue: cue['index']) session['plugins'] = plugins.get_plugin_settings() session['application'] = self._app_conf # Write to a file the json-encoded dictionary with open(session_file, mode='w', encoding='utf-8') as file: file.write(json.dumps(session, sort_keys=True, indent=4)) MainActionsHandler.set_saved() self._mainWindow.update_window_title() def _load_from_file(self, session_file): """ Load a saved session from file """ try: with open(session_file, mode='r', encoding='utf-8') as file: session = json.load(file) # New session self._new_session( layouts.get_layout(session['application']['layout'])) # Get the application settings self._app_conf = session['application'] # Load cues for cue_conf in session['cues']: cue_type = cue_conf.pop('_type_', 'Undefined') cue_id = cue_conf.pop('id') try: cue = CueFactory.create_cue(cue_type, cue_id=cue_id) cue.update_properties(cue_conf) self._cue_model.add(cue) except Exception as e: elogging.exception('Unable to create the cue', e) MainActionsHandler.set_saved() self._mainWindow.update_window_title() # Load plugins settings plugins.set_plugins_settings(session['plugins']) # Update the main-window self._mainWindow.filename = session_file self._mainWindow.update() except Exception as e: elogging.exception('Error during file reading', e) self.new_session_dialog() linux-show-player-0.5.1/lisp/backend/000077500000000000000000000000001323636106000174625ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/backend/__init__.py000066400000000000000000000020161323636106000215720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . # In the current state set/get_backend are quite useless __backend = None def set_backend(backend): global __backend __backend = backend def get_backend(): """ :rtype: lisp.backends.base.backend.Backend """ return __backend linux-show-player-0.5.1/lisp/backend/audio_utils.py000066400000000000000000000053341323636106000223620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import aifc import math import sunau import urllib.parse import wave # Decibel value to be considered -inf MIN_VOLUME_DB = -144 # Linear value of MIN_VOLUME_DB MIN_VOLUME = 6.30957344480193e-08 # Maximum linear value for the volume, equals to 1000% MAX_VOLUME = 10 # Decibel value of MAX_VOLUME MAX_VOLUME_DB = 20 def db_to_linear(value, min_db_zero=True): """dB value to linear value conversion.""" if min_db_zero and value <= MIN_VOLUME_DB: return 0 return 10 ** (value / 20) def linear_to_db(value): """Linear value to dB value conversion.""" return 20 * math.log10(value) if value > MIN_VOLUME else MIN_VOLUME_DB def fader_to_slider(value): """Inverse function of `slider_to_fader`. Note:: If converting back to an integer scale use `round()` instead of `int()` """ return (value / 3.16227766) ** (1 / 3.7) def slider_to_fader(value): """Convert a slider linear value to a fader-like scale. The formula used is the following: (10db) * (x ^ 3.7) Where 10db = 3.16227766 And 0.0 <= x <= 1.0 :param value: The value to scale [0-1] :type value: float """ if value > 1.0: value = 1.0 elif value < 0.0: value = 0 return 3.16227766 * (value ** 3.7) def python_duration(path, sound_module): """Returns audio-file duration using the given standard library module.""" duration = 0 try: with sound_module.open(path, 'r') as file: frames = file.getnframes() rate = file.getframerate() duration = int(frames / rate * 1000) finally: return duration def uri_duration(uri): """Return the audio-file duration, using the given uri""" protocol, path = uri.split('://') path = urllib.parse.unquote(path) if protocol == 'file': for mod in [wave, aifc, sunau]: duration = python_duration(path, mod) if duration > 0: return duration return 0 linux-show-player-0.5.1/lisp/backend/backend.py000066400000000000000000000034251323636106000214270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import abstractmethod from lisp.core.singleton import ABCSingleton class Backend(metaclass=ABCSingleton): """Common interface that any backend must provide. An __init__() method can be defined if the backend require initialization. """ @abstractmethod def uri_duration(self, uri): """Return the file duration in milliseconds. :param uri: The URI of the file :type uri: str :rtype: int """ @abstractmethod def uri_tags(self, uri): """Return a dictionary containing the file metadata/tags. :param uri: The URI of the file :type uri: str :rtype: dict """ @abstractmethod def supported_extensions(self): """Return file extensions supported by the backend. Extensions will be categorized in 'audio' and 'video', optionally the backend can create others categories. e.g. {'audio': ['wav', 'mp3', ...], 'video': ['mp4', 'mov', ...]} :rtype: dict """ linux-show-player-0.5.1/lisp/backend/media.py000066400000000000000000000103451323636106000211160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import abstractmethod from enum import Enum from lisp.core.has_properties import HasProperties, Property from lisp.core.signal import Signal class MediaState(Enum): """Identify the current media state""" Error = -1 Null = 0 Playing = 1 Paused = 2 Stopped = 3 class Media(HasProperties): """Interface for Media objects. Media(s) provides control over multimedia contents. To control various parameter of the media, MediaElement(s) should be used. .. note:: The play/stop/pause functions must be non-blocking functions. """ loop = Property(default=0) duration = Property(default=0) start_time = Property(default=0) stop_time = Property(default=0) def __init__(self): super().__init__() self.paused = Signal() """Emitted when paused (self)""" self.played = Signal() """Emitted when played (self)""" self.stopped = Signal() """Emitted when stopped (self)""" self.interrupted = Signal() """Emitted after interruption (self)""" self.eos = Signal() """End-of-Stream (self)""" self.on_play = Signal() """Emitted before play (self)""" self.on_stop = Signal() """Emitted before stop (self)""" self.on_pause = Signal() """Emitted before pause (self)""" self.sought = Signal() """Emitted after a seek (self, position)""" self.error = Signal() """Emitted when an error occurs (self, error, details)""" self.elements_changed = Signal() """Emitted when one or more elements are added/removed (self)""" @property @abstractmethod def state(self): """ :return: the media current state :rtype: MediaState """ @abstractmethod def current_time(self): """ :return: the current playback time in milliseconds or 0 :rtype: int """ @abstractmethod def element(self, class_name): """ :param name: The element class-name :type name: str :return: The element with the specified class-name or None :rtype: lisp.core.base.media_element.MediaElement """ @abstractmethod def elements(self): """ :return: All the MediaElement(s) of the media :rtype: list """ @abstractmethod def elements_properties(self): """ :return: Media elements configurations :rtype: dict """ @abstractmethod def input_uri(self): """ :return: The media input uri (e.g. "file:///home/..."), or None :rtype: str """ @abstractmethod def interrupt(self): """Interrupt the playback (no fade) and go in STOPPED state.""" @abstractmethod def pause(self): """The media go in PAUSED state and pause the playback.""" @abstractmethod def play(self): """The media go in PLAYING state and starts the playback.""" @abstractmethod def seek(self, position): """Seek to the specified point. :param position: The position to be reached in milliseconds :type position: int """ @abstractmethod def stop(self): """The media go in STOPPED state and stop the playback.""" @abstractmethod def update_elements(self, settings): """Update the elements configuration. :param settings: Media-elements settings :type settings: dict """linux-show-player-0.5.1/lisp/backend/media_element.py000066400000000000000000000031451323636106000226270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from enum import Enum from lisp.core.has_properties import HasProperties class ElementType(Enum): """The type of the media-element""" Input = 0 Output = 1 Plugin = 2 class MediaType(Enum): """The media-type that the element handle (Audio/Video)""" Audio = 0 Video = 1 AudioAndVideo = 2 class MediaElement(HasProperties): """Base media-element class A MediaElement object control specific media's parameters (e.g. volume). Every MediaElement provides two kind of properties: 1) The one defined via class:`HasProperties`; 2) runtime only properties, those are reset to the previous value at playback end. Runtime properties are defined using the following naming convention: runtime_. """ ElementType = None MediaType = None Name = 'Undefined' linux-show-player-0.5.1/lisp/core/000077500000000000000000000000001323636106000170235ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/core/__init__.py000066400000000000000000000000001323636106000211220ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/core/action.py000066400000000000000000000031151323636106000206520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import abstractmethod class Action: """Base class for actions. Action provides the ability to revert the changes done in the "do" method, via "undo" method, and redo them via the "redo" method. An action could provide, via the "log" function, a simple log message. .. warning:: Actions may reference external objects, preventing gc. """ __slots__ = () @abstractmethod def do(self): """Do something""" @abstractmethod def undo(self): """Revert what do function has done""" def redo(self): """Redo after reverting The default implementation call the "do" function. """ self.do() def log(self): """Used for logging :return: A log message :rtype: str """ return '' linux-show-player-0.5.1/lisp/core/actions_handler.py000066400000000000000000000075611323636106000225430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import logging from collections import deque from lisp.core import configuration as cfg from lisp.core.action import Action from lisp.core.signal import Signal class ActionsHandler: """Provide a classic undo/redo mechanism based on stacks.""" MaxStackSize = int(cfg.config['Actions']['MaxStackSize']) def __init__(self): super().__init__() self.action_done = Signal() self.action_undone = Signal() self.action_redone = Signal() self._undo = deque() self._redo = deque() if self.MaxStackSize > 0: self._undo.maxlen = self.MaxStackSize self._redo.maxlen = self.MaxStackSize self._saved_action = None def clear(self): """Clear the `undo` and `redo` stacks.""" self._undo.clear() self._redo.clear() self._saved_action = None def do_action(self, action: Action): """Execute the action, and add it the `undo` stack. When an action is executed: * is logged * is appended to the `undo` stack * the `redo` stack is cleared to maintain consistency * the `action_done` signal is emitted """ action.do() self._logging(action, 'Last action: ') self._undo.append(action) # Clean the redo stack for maintain consistency self._redo.clear() self.action_done.emit(action) def undo_action(self): """Undo the last executed action, and add it to the `redo` stack. When an action is undone: * is removed from the `undo` stack * is logged * is appended to the `redo` stack * the signal `action_undone` is emitted """ if self._undo: action = self._undo.pop() action.undo() self._logging(action, 'Undo: ') self._redo.append(action) self.action_undone.emit(action) def redo_action(self): """Redo the last undone action, and add it back to the `undo` stack. When an action is redone: * is remove from the `redo` stack * is logged * is added to the `undo` stack * the `action_redone` signal is emitted """ if self._redo: action = self._redo.pop() action.redo() self._logging(action, 'Redo: ') self._undo.append(action) self.action_redone.emit(action) def set_saved(self): """Set the action at the _top_ of the `undo` stack as `save-point`.""" if self._undo: self._saved_action = self._undo[-1] def is_saved(self) -> bool: """Return True if the action at the _top_ of the `undo` stack is the `save-point`. """ if self._undo: return self._undo[-1] is self._saved_action else: return True @staticmethod def _logging(action: Action, pre: str): message = action.log() if message.strip() == '': message = type(action).__name__ logging.info(pre + message) MainActionsHandler = ActionsHandler() # "global" action-handler linux-show-player-0.5.1/lisp/core/class_based_registry.py000066400000000000000000000050041323636106000235670ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . class ClassBasedRegistry: """Allow to register/un-register and filter items using a reference class. The filter "check" whenever the filter-class is a subclass of the one used to register the item or exactly the the same. .. highlight:: reg = ClassBasedRegistry() reg.add_item('Object-Item', object) reg.add_item('List-Item', list) list(reg.filter(object)) # ['Object-Item', 'List-Item'] list(reg.filter(list)) # ['List-Item'] """ def __init__(self): self._registry = {} def add_item(self, item, ref_class=object): """Register a item for ref_class.""" if ref_class not in self._registry: self._registry[ref_class] = [item] elif item not in self._registry[ref_class]: self._registry[ref_class].append(item) def remove_item(self, item): """Remove all the occurrences of the given item.""" for ref_class in self._registry: try: self._registry[ref_class].remove(item) except ValueError: pass def filter(self, ref_class=object): """Return an iterator over items registered with ref_class or subclasses. The items are sorted by ref_class name. """ for class_ in sorted(self._registry.keys(), key=str): if issubclass(ref_class, class_): yield from self._registry[class_] def clear_class(self, ref_class=object): """Remove all the items for ref_class.""" self._registry[ref_class].clear() def ref_classes(self): """Return a view-like object of all the registered references.""" return self._registry.keys() def clear(self): self._registry.clear() linux-show-player-0.5.1/lisp/core/clock.py000066400000000000000000000032171323636106000204730ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from threading import Lock from PyQt5.QtCore import QTimer class Clock(QTimer): """Clock based on Qt.QTimer class. The class provide two functions to add and remove callbacks, in this way the clock is running only when there's one, or more, callbacks. """ def __init__(self, timeout): super().__init__() self.setInterval(timeout) self.__lock = Lock() self.__clients = 0 def add_callback(self, callback): with self.__lock: self.timeout.connect(callback) self.__clients += 1 if self.__clients == 1: self.start() def remove_callback(self, callback): with self.__lock: self.timeout.disconnect(callback) self.__clients -= 1 if self.__clients == 0: self.stop() Clock_10 = Clock(10) Clock_100 = Clock(100) linux-show-player-0.5.1/lisp/core/configuration.py000066400000000000000000000046201323636106000222460ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import os from configparser import ConfigParser from shutil import copyfile DEFAULT_CFG_PATH = os.path.join(os.path.dirname(__file__), '../default.cfg') CFG_DIR = os.path.expanduser("~") + '/.linux_show_player' CFG_PATH = CFG_DIR + '/config.cfg' config = ConfigParser() def load_config(): # Check if the current user configuration is up-to-date check_user_config() # Read the user configuration config.read(CFG_PATH) def write_config(): with open(CFG_PATH, 'w') as f: config.write(f) def check_user_config(): update = True if not os.path.exists(CFG_DIR): os.makedirs(CFG_DIR) elif os.path.exists(CFG_PATH): default = ConfigParser() default.read(DEFAULT_CFG_PATH) current = ConfigParser() current.read(CFG_PATH) current_version = current['Version']['Number'] update = current_version != default['Version']['Number'] if update: copyfile(CFG_PATH, CFG_PATH + '.old') print('Configuration file backup: {}.old'.format(CFG_PATH)) if update: copyfile(DEFAULT_CFG_PATH, CFG_PATH) print('Create new configuration file: {}'.format(CFG_PATH)) def config_to_dict(): conf_dict = {} for section in config.keys(): conf_dict[section] = {} for option in config[section].keys(): conf_dict[section][option] = config[section][option] return conf_dict def update_config_from_dict(conf): for section in conf.keys(): for option in conf[section].keys(): config[section][option] = conf[section][option] write_config() # Load configuration load_config()linux-show-player-0.5.1/lisp/core/decorators.py000066400000000000000000000120611323636106000215420ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import logging import traceback from functools import wraps, partial from threading import Thread, Lock, RLock def async(target): """Decorator. Make a function asynchronous. The decorated function is executed in a differed thread. """ @wraps(target) def wrapped(*args, **kwargs): Thread(target=target, args=args, kwargs=kwargs, daemon=True).start() return wrapped def async_in_pool(pool): """Decorator. Make a function asynchronous in a specified pool. The decorated function is executed in the specified threads-pool. .. Usage:: class MyClass: __MyPool = ThreadPoolExecutor(10) @async_in_pool(__MyPool) def do_some_task(self): pass """ def decorator(target): @wraps(target) def wrapped(*args, **kwargs): pool.submit(target, *args, **kwargs) return wrapped return decorator def locked_function(target=None, *, lock=None, blocking=True, timeout=-1): """Decorator. Make a *function* "synchronized". Only one thread at time can access the decorated function. :param target: the function to decorate :param lock: the lock to be used (if not specified an RLock is created) :param blocking: if True the lock-acquirement is blocking :param timeout: timeout for the lock-acquirement """ # If called with (keywords) arguments if target is None: return partial(locked_function, lock=lock, blocking=blocking, timeout=timeout) if lock is None: target.__lock__ = RLock() else: target.__lock__ = lock @wraps(target) def locked(*args, **kwargs): try: if target.__lock__.acquire(blocking=blocking, timeout=timeout): return target(*args, **kwargs) else: return finally: try: target.__lock__.release() except RuntimeError: pass return locked def locked_method(target=None, *, blocking=True, timeout=-1): """Decorator. Make a *method* synchronized. Only one thread at time can access the decorated method. :param target: the function to decorate :param blocking: if True the lock-acquirement is blocking :param timeout: timeout for the lock-acquirement """ # If called with (keywords) arguments if target is None: return partial(locked_method, blocking=blocking, timeout=timeout) # generate a lock_name like "__method_name_lock__" lock_name = '__' + target.__name__ + '_lock__' target.__meta_lock__ = Lock() @wraps(target) def locked(self, *args, **kwargs): with target.__meta_lock__: lock = getattr(self, lock_name, None) # If the lock is not defined, then define it if lock is None: lock = RLock() setattr(self, lock_name, lock) try: if lock.acquire(blocking=blocking, timeout=timeout): return target(self, *args, **kwargs) else: return finally: try: lock.release() except RuntimeError: pass return locked def suppress_exceptions(target=None, *, log=True): """Decorator. Suppress all the exception in the decorated function. :param log: If True (the default) exceptions are logged as warnings. """ if target is None: return partial(suppress_exceptions, print_exc=log) @wraps(target) def wrapped(*args, **kwargs): try: return target(*args, **kwargs) except Exception: logging.warning('Exception suppressed:\n' + traceback.format_exc()) return wrapped def memoize(callable_): """Decorator. Caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned (not reevaluated). .. Note:: This works for any callable object. The arguments are cached (as strings) in object.cache. """ cache = callable_.cache = {} @wraps(callable_) def memoizer(*args, **kwargs): key = str(args) + str(kwargs) if key not in cache: cache[key] = callable_(*args, **kwargs) return cache[key] return memoizer linux-show-player-0.5.1/lisp/core/fade_functions.py000066400000000000000000000041011323636106000223600ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Python porting of qtractor fade functions # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see """ Functor arguments normalization: t = (x - x0) / (x1 - x0) where 'x' is the time (x0 initial, x1 final) a = y1 - y0 where 'y' is the value (y0 initial, y1 final) b = y0 """ from enum import Enum from lisp.core.util import FunctionProxy def fade_linear(t, a, b): """Linear fade.""" return a * t + b def fadein_quad(t, a, b): """Quadratic (t^2) fade in: accelerating from zero velocity.""" return a * (t ** 2) + b def fadeout_quad(t, a, b): """Quadratic (t^2) fade out: decelerating to zero velocity.""" return a * (t * (2 - t)) + b def fade_inout_quad(t, a, b): """Quadratic (t^2) fade in-out: acceleration until halfway, then deceleration. """ t *= 2 if t < 1.0: return 0.5 * a * (t ** 2) + b else: t -= 1 return 0.5 * a * (1 - (t * (t - 2))) + b def ntime(time, begin, duration): """Return normalized time.""" return (time - begin) / (duration - begin) class FadeInType(Enum): Linear = FunctionProxy(fade_linear) Quadratic = FunctionProxy(fadein_quad) Quadratic2 = FunctionProxy(fade_inout_quad) class FadeOutType(Enum): Linear = FunctionProxy(fade_linear) Quadratic = FunctionProxy(fadeout_quad) Quadratic2 = FunctionProxy(fade_inout_quad) linux-show-player-0.5.1/lisp/core/fader.py000066400000000000000000000115741323636106000204660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from threading import Event from lisp.core.decorators import locked_method from lisp.core.fade_functions import ntime, FadeInType, FadeOutType from lisp.core.util import rsetattr, rgetattr class Fader: """Allow to perform fades on "generic" objects attributes. * Fades have a resolution of `1-hundredth-of-second` * To be able to fade correctly the attribute must be numeric, if not, the `fade` function will fail * When fading, the `fade` function cannot be entered by other threads, if this happens the function will simply return immediately * To execute a fader, the `prepare` function must be called first, this will also stop the fader * After calling `prepare` the fader is considered as running * The `stop` function wait until all "pending" target changes are applied * Changing the target will also stop the fader """ def __init__(self, target, attribute): """ :param target: The target object :type target: object :param attribute: The target attribute (name) to be faded :type attribute: str """ self._time = 0 # current fade time in hundredths-of-seconds self._target = target self._attribute = attribute self._is_ready = Event() self._is_ready.set() self._running = Event() self._running.set() self._pause = Event() self._pause.set() @property def target(self): return self._target @target.setter def target(self, target): self.stop() self._target = target @property def attribute(self): return self._attribute @attribute.setter def attribute(self, target_property): self.stop() self._attribute = target_property def prepare(self): self.stop() self._running.clear() self._is_ready.clear() @locked_method(blocking=False) def fade(self, duration, to_value, fade_type): """ :param duration: How much the fade should be long (in seconds) :type duration: float :param to_value: The value to reach :type to_value: float :param fade_type: The fade type :type fade_type: FadeInType | FadeOutType :return: False if the fade as been interrupted, True otherwise :rtype: bool """ if duration <= 0: return if not isinstance(fade_type, (FadeInType, FadeOutType)): raise AttributeError( 'fade_type must be one of FadeInType or FadeOutType member,' 'not {}'.format(fade_type.__class__.__name__)) try: self._time = 0 begin = 0 functor = fade_type.value duration = int(duration * 100) base_value = rgetattr(self._target, self._attribute) value_diff = to_value - base_value if value_diff == 0: return while self._time <= duration and not self._running.is_set(): rsetattr(self._target, self._attribute, functor(ntime(self._time, begin, duration), value_diff, base_value)) self._time += 1 self._running.wait(0.01) self._pause.wait() finally: interrupted = self._running.is_set() self._running.set() self._is_ready.set() self._time = 0 return not interrupted def stop(self): if not self._running.is_set() or not self._pause.is_set(): self._running.set() self._pause.set() self._is_ready.wait() def pause(self): if self.is_running(): self._pause.clear() def restart(self): # TODO: change to resume self._pause.set() def is_paused(self): return not self._pause.is_set() def is_running(self): return not self._running.is_set() and not self.is_paused() def current_time(self): # Return the time in millisecond return self._time * 10 linux-show-player-0.5.1/lisp/core/has_properties.py000066400000000000000000000203341323636106000224260ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import ABCMeta from copy import deepcopy from lisp.core.signal import Signal from lisp.core.util import subclasses class Property: """Descriptor to be used in HasProperties subclasses to define properties. .. warning:: To be able to save properties into a session, the stored value MUST be JSON-serializable. .. warning:: If extended any subclass *MUST*: 1) if the __get__ method receive a None instance return self; 2) if the __get__ is called while the value is not set, set it with a safe copy of 'default' and return. 3) After the value is changed call the __changed__ method. """ def __init__(self, default=None): self.name = 'unnamed_property' self.default = default def __get__(self, instance, owner=None): if instance is None: return self elif self.name not in instance.__dict__: instance.__dict__[self.name] = deepcopy(self.default) return instance.__dict__.get(self.name) def __set__(self, instance, value): if instance is not None: # Only change the value if different if value != instance.__dict__.get(self.name, self.default): instance.__dict__[self.name] = value self.__changed__(instance, value) def changed(self, instance): if instance is not None: value = self.__get__(instance) return value != self.default, value return False, self.default def __changed__(self, instance, value): instance.property_changed.emit(instance, self.name, value) # Get the related signal property_signal = instance.changed_signals.get(self.name) if property_signal is not None: property_signal.emit(value) class WriteOnceProperty(Property): """Property that can be modified only once. Obviously this is not really "write-once", but if used as normal attribute will ignore any change when the stored value is different from default. """ def __set__(self, instance, value): if self.__get__(instance) == self.default: super().__set__(instance, value) class NestedProperties(Property): """Simplify retrieving the properties of nested HasProperties objects. The goal is to avoid the reimplementation of HasProperties.properties() and HasProperties.update_properties(). ..note:: When getting or setting a single property of the nested object is better to access it directly instead that using the nested-property. """ def __init__(self, provider_name, default=None): super().__init__(default=default) self.provider_name = provider_name def __get__(self, instance, owner=None): if instance is None: return self else: provider = instance.__dict__.get(self.provider_name) if isinstance(provider, HasProperties): return provider.properties() def __set__(self, instance, value): if instance is not None: provider = instance.__dict__.get(self.provider_name) if isinstance(provider, HasProperties): provider.update_properties(value) self.__changed__(instance, value) def changed(self, instance): if instance is not None: provider = instance.__dict__.get(self.provider_name) if isinstance(provider, HasProperties): properties = provider.properties(only_changed=True) # If no properties is changed (empty dict) return false return bool(properties), properties return False, {} class HasPropertiesMeta(ABCMeta): """Metaclass for HasProperties classes. This metaclass manage the 'propagation' of the properties in all subclasses, this process involves overwriting __properties__ with a set containing all the properties names. ..note:: This metaclass is derived form :class:`abc.ABCMeta`, so abstract classes can be created without using an intermediate metaclass """ def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) # Use a set for avoiding repetitions cls.__properties__ = set() # Populate with all the properties for name, attribute in vars(cls).items(): if isinstance(attribute, Property): cls.__properties__.add(name) attribute.name = name for base in cls.__bases__: cls.__properties__.update(getattr(base, '__properties__', ())) class HasProperties(metaclass=HasPropertiesMeta): """Base class providing a simple way to mange object properties. Using the Property descriptor, subclasses, can specify a set of properties, that can be easily retrieved and updated via :func:`properties` and :func:`update_properties`. .. Usage:: class MyClass(HasProperties): prop1 = Property(default=100) prop2 = Property() """ __properties__ = set() def __init__(self): self.property_changed = Signal() #: Emitted after property change (self, name, value) self.changed_signals = {} """Contains signals that are emitted after the associated property is changed, the signal are create only when requested the first time. """ @classmethod def register_property(cls, name, prop): """Register a new property with the given name. :param str name: Property name :param BaseProperty prop: The property """ if name not in cls.__properties__: prop.name = name setattr(cls, name, prop) cls.__properties__.add(name) for subclass in subclasses(cls): subclass.__properties__.add(name) def changed(self, property_name): """ :param property_name: The property name :return: The property change-signal :rtype: Signal """ if property_name not in self.__class__.__properties__: raise ValueError('no property "{0}" found'.format(property_name)) signal = self.changed_signals.get(property_name, None) if signal is None: signal = Signal() self.changed_signals[property_name] = signal return signal def properties(self, only_changed=False): """ :param only_changed: when True only "changed" properties are collected :type only_changed: bool :return: The properties as a dictionary {name: value} :rtype: dict """ if only_changed: properties = {} for name in self.__properties__: changed, value = getattr(self.__class__, name).changed(self) if changed: properties[name] = value return properties return {name: getattr(self, name) for name in self.__properties__} @classmethod def properties_defaults(cls): """ :return: The default properties as a dictionary {name: default_value} :rtype: dict """ return {name: getattr(cls, name).default for name in cls.__properties__} def update_properties(self, properties): """Set the given properties. :param properties: The element properties :type properties: dict """ for name, value in properties.items(): if name in self.__properties__: setattr(self, name, value) linux-show-player-0.5.1/lisp/core/loading.py000066400000000000000000000076351323636106000210250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import logging import os.path import re import traceback try: from os import scandir except ImportError: from scandir import scandir class load_classes: """Generator for iterating over classes in a package. The class name must be the same as the module name, optionally suffixes and prefixes lists can be provided. .. highlight:: modules ├── module1.py | └──Module1 └── module_extra.py └── ModuleExtra """ def __init__(self, pkg, pkg_path, pre=('',), suf=('',), exclude=()): """ :param pkg: dotted name of the package :param pkg_path: path of the package to scan :param pre: iterable of prefixes :param suf: iterable suffixes :param exclude: iterable of excluded modules names `pre` and `suf` must have the same length to work correctly, if some of the classes to load have no prefix/suffix an empty one should be used. """ self.pkg = pkg self.prefixes = pre self.suffixes = suf self.excluded = exclude self.pkg_path = pkg_path def __iter__(self): return self.load() def load(self): """Generate lists of tuples (class-name, class-object).""" for entry in scandir(self.pkg_path): # Exclude __init__, __pycache__ and likely if re.match('^__.*', entry.name): continue mod_name = entry.name if entry.is_file(): # Split filename and extension mod_name, ext = os.path.splitext(entry.name) # Exclude all non-python files if not re.match('.py[cod]?', ext): continue # Exclude excluded ¯\_(ツ)_/¯ if mod_name in self.excluded: continue mod_path = self.pkg + '.' + mod_name try: # Import module module = import_module(mod_path) # Load class from imported module for prefix, suffix in zip(self.prefixes, self.suffixes): name = self._class_name(mod_name, prefix, suffix) if hasattr(module, name): cls = getattr(module, name) yield (name, cls) except ImportError: logging.warning('Cannot load module: {0}'.format(mod_name)) logging.debug(traceback.format_exc()) @staticmethod def _class_name(mod_name, pre='', suf=''): """Return the supposed class name from loaded module. Substitutions: * Remove `underscores` * First letters to uppercase version For example: * For "module", the result will be "Module" * For "module_name", the result will be "ModuleName" """ # Capitalize the first letter of each word base_name = ''.join(word.title() for word in mod_name.split('_')) # Add prefix and suffix to the base name return pre + base_name + suf def import_module(module_path): return __import__(module_path, globals(), locals(), ['*']) linux-show-player-0.5.1/lisp/core/memento_model.py000066400000000000000000000050111323636106000222160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.core.actions_handler import MainActionsHandler from lisp.core.memento_model_actions import AddItemAction, RemoveItemAction, \ MoveItemAction from lisp.core.proxy_model import ReadOnlyProxyModel class MementoModel(ReadOnlyProxyModel): """ProxyModel that allow to register models changes in an ActionHandler The changes (add/remove) of the base model are registered as actions that can be undone/redone. If no handler is specified the MainActionHandler is used. ..note:: The methods, locks and unlock, allow to avoid reentering when an action is undone/redone. """ def __init__(self, model, handler=None): super().__init__(model) if handler is None: handler = MainActionsHandler self._handler = handler self._locked = False def _item_added(self, item): if not self._locked: self._handler.do_action(AddItemAction(self, self.model, item)) def _item_removed(self, item): if not self._locked: self._handler.do_action(RemoveItemAction(self, self.model, item)) def lock(self): self._locked = True def unlock(self): self._locked = False def _model_reset(self): """Reset cannot be reverted""" class AdapterMementoModel(MementoModel): """Extension of the MementoModel that use a ModelAdapter as a base-model""" def __init__(self, model_adapter, handler=None): super().__init__(model_adapter, handler) self.model.item_moved.connect(self._item_moved) def _item_moved(self, old_index, new_index): if not self._locked: self._handler.do_action(MoveItemAction(self, self.model, old_index, new_index)) linux-show-player-0.5.1/lisp/core/memento_model_actions.py000066400000000000000000000051121323636106000237400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import abstractmethod from lisp.core.action import Action class MementoAction(Action): """Actions created by the MementoModel to register model changes.""" __slots__ = ('_m_model', '_model') def __init__(self, m_model, model): super().__init__() self._m_model = m_model self._model = model def do(self): pass def undo(self): try: self._m_model.lock() self.__undo__() finally: self._m_model.unlock() def redo(self): try: self._m_model.lock() self.__redo__() finally: self._m_model.unlock() @abstractmethod def __undo__(self): pass @abstractmethod def __redo__(self): pass class AddItemAction(MementoAction): __slots__ = '__item' def __init__(self, m_model, model, item): super().__init__(m_model, model) self.__item = item def __undo__(self): self._model.remove(self.__item) def __redo__(self): self._model.add(self.__item) class RemoveItemAction(MementoAction): __slots__ = '__item' def __init__(self, m_model, model, item): super().__init__(m_model, model) self.__item = item def __undo__(self): self._model.add(self.__item) def __redo__(self): self._model.remove(self.__item) class MoveItemAction(MementoAction): __slots__ = ('__old_index', '__new_index') def __init__(self, m_model, model_adapter, old_index, new_index): super().__init__(m_model, model_adapter) self.__old_index = old_index self.__new_index = new_index def __undo__(self): self._model.move(self.__new_index, self.__old_index) def __redo__(self): self._model.move(self.__old_index, self.__new_index) linux-show-player-0.5.1/lisp/core/model.py000066400000000000000000000040431323636106000204760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import abstractmethod from collections import Sized, Iterable, Container from lisp.core.signal import Signal class Model(Sized, Iterable, Container): """A model is a data container that provide signal to observe changes. The base model do not provide a way to change directly the data, this to remove useless restrictions to models that will store complex objects where attribute changes but not objects directly. Setter methods and signals can be simply implemented in subclass, where needed. A model should be as generic as possible in the way its store data, working like a normal data structure, for more ui-oriented tasks a ModelAdapter should be used. __iter__ must provide an iterator over the items __len__ must return the number of stored items __contains__ must return True/False if the given item is in/not in the model """ def __init__(self): self.item_added = Signal() self.item_removed = Signal() self.model_reset = Signal() @abstractmethod def add(self, item): pass @abstractmethod def remove(self, item): pass @abstractmethod def reset(self): pass class ModelException(Exception): """Exception for illegal operations on models""" linux-show-player-0.5.1/lisp/core/model_adapter.py000066400000000000000000000025331323636106000222000ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import abstractmethod from lisp.core.proxy_model import ProxyModel from lisp.core.signal import Signal class ModelAdapter(ProxyModel): """Base-class for mutable ProxyModel(s) with index-based operations""" def __init__(self, model): super().__init__(model) self.item_moved = Signal() @abstractmethod def insert(self, item, index): pass @abstractmethod def item(self, index): pass @abstractmethod def pop(self, index): pass @abstractmethod def move(self, old_index, new_index): pass linux-show-player-0.5.1/lisp/core/module.py000066400000000000000000000017311323636106000206640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.core.singleton import ABCSingleton class Module(metaclass=ABCSingleton): """Interface for modules.""" def terminate(self): """Finalize the module (e.g. free resources).""" linux-show-player-0.5.1/lisp/core/plugin.py000066400000000000000000000026511323636106000206770ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . class Plugin: """Interface for plugins. The "init" and "reset" functions are called respectively after the session layout is created (init) and before (reset). """ Name = 'Plugin' def init(self): """Initialize the session-related components of the plugin.""" def reset(self): """Reset the session-related components of plugin.""" def settings(self): """Return the plugin settings. :rtype: dict """ return {} def load_settings(self, settings): """Load the plugin settings. :param settings: the settings to be loaded :type settings: dict """ linux-show-player-0.5.1/lisp/core/proxy_model.py000066400000000000000000000050541323636106000217420ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import abstractmethod from lisp.core.model import Model, ModelException class ProxyModel(Model): """Proxy that wrap a more generic model to extend its functionality. The add, remove, __iter__, __len__ and __contains__ default implementations use the the model ones. .. note: The base model cannot be changed. Any ProxyModel could provide it's own methods/signals. """ def __init__(self, model): super().__init__() if not isinstance(model, Model): raise TypeError('ProxyModel model must be a Model object, not {0}' .format(model.__class__.__name__)) self.__model = model self.__model.item_added.connect(self._item_added) self.__model.item_removed.connect(self._item_removed) self.__model.model_reset.connect(self._model_reset) def add(self, item): self.__model.add(item) def remove(self, item): self.__model.remove(item) def reset(self): self.__model.reset() @property def model(self): return self.__model @abstractmethod def _model_reset(self): pass @abstractmethod def _item_added(self, item): pass @abstractmethod def _item_removed(self, item): pass def __iter__(self): return self.__model.__iter__() def __len__(self): return len(self.__model) def __contains__(self, item): return item in self.__model class ReadOnlyProxyModel(ProxyModel): def add(self, item): raise ModelException('cannot add items into a read-only model') def remove(self, item): raise ModelException('cannot remove items from a read-only model') def reset(self): raise ModelException('cannot reset read-only model') linux-show-player-0.5.1/lisp/core/qmeta.py000066400000000000000000000016521323636106000205100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import ABCMeta from PyQt5.QtCore import QObject class QMeta(type(QObject), type): pass class QABCMeta(type(QObject), ABCMeta): pass linux-show-player-0.5.1/lisp/core/rwait.py000066400000000000000000000074161323636106000205330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import time from threading import Event from lisp.core.signal import Signal class RWait: """Provide a resumeable-wait mechanism.""" def __init__(self): self._elapsed = 0 self._start_time = 0 self._ended = False self._waiting = Event() self._waiting.set() self._is_waiting = Event() self._is_waiting.set() self.start = Signal() self.ended = Signal() self.paused = Signal() self.stopped = Signal() def wait(self, timeout, lock=None): """Block until the timeout is elapsed or `pause` or `stop` are called. If the wait is paused, the next time this function is called the timeout will be `total_timeout - elapsed_time`. :param timeout: time to wait :param lock: lock to release before and re-acquired after the wait :return: True if the wait has not been interrupted by `pause` or `stop` :rtype: bool """ if self._is_waiting.is_set(): # Clear the events self._waiting.clear() self._is_waiting.clear() # Set the start-time self._start_time = time.monotonic() - self._elapsed self.start.emit() if lock is not None: lock.release() # Wait for the "remaining" time self._ended = not self._waiting.wait(timeout - self._elapsed) # If the wait is ended by timeout if self._ended: self._elapsed = 0 self.ended.emit() # Notify that we are not waiting anymore self._is_waiting.set() if lock is not None: lock.acquire() return self._ended return False def stop(self): """Stop the wait.""" if self.current_time() > 0: self._waiting.set() self._is_waiting.wait() # Check self._ended to ensure that the wait is not ended by timeout # before `self._waiting.set()` is called in this method. if not self._ended: self._elapsed = 0 self.stopped.emit() def pause(self): """Pause the wait.""" if not self._is_waiting.is_set(): # Calculate elapsed time ASAP elapsed = time.monotonic() - self._start_time self._waiting.set() self._is_waiting.wait() # Check self._ended to ensure that the wait is not ended by timeout # before `self._waiting.set()` is called in this method. if not self._ended: self._elapsed = elapsed self.paused.emit() def current_time(self): """Return the currently elapsed time.""" if self._is_waiting.is_set(): return self._elapsed return time.monotonic() - self._start_time def is_waiting(self): return not self._is_waiting.is_set() def is_paused(self): return self._is_waiting.is_set() and self.current_time() > 0 linux-show-player-0.5.1/lisp/core/signal.py000066400000000000000000000155321323636106000206600ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import inspect import logging import traceback import weakref from enum import Enum from threading import RLock from types import MethodType, BuiltinMethodType from PyQt5.QtCore import QEvent, QObject from PyQt5.QtWidgets import QApplication from lisp.core.decorators import async from lisp.core.util import weak_call_proxy __all__ = ['Signal', 'Connection'] def slot_id(slot_callable): """Return the id of the given slot_callable. This function is able to produce unique id(s) even for bounded methods, and builtin-methods using a combination of the function id and the object id. """ if isinstance(slot_callable, MethodType): return id(slot_callable.__func__), id(slot_callable.__self__) elif isinstance(slot_callable, BuiltinMethodType): return id(slot_callable), id(slot_callable.__self__) else: return id(slot_callable) class Slot: """Synchronous slot.""" def __init__(self, slot_callable, callback=None): if isinstance(slot_callable, MethodType): self._reference = weakref.WeakMethod(slot_callable, self._expired) elif callable(slot_callable): self._reference = weakref.ref(slot_callable, self._expired) else: raise TypeError('slot must be callable') self._callback = callback self._slot_id = slot_id(slot_callable) self._no_args = len(inspect.signature(slot_callable).parameters) == 0 def call(self, *args, **kwargs): """Call the callable object within the given parameters.""" try: if self.is_alive(): if self._no_args: self._reference()() else: self._reference()(*args, **kwargs) except Exception: logging.error(traceback.format_exc()) def is_alive(self): return self._reference() is not None def _expired(self, reference): self._callback(self._slot_id) class AsyncSlot(Slot): """Asynchronous slot, NOT queued, any call is performed in a new thread.""" @async def call(self, *args, **kwargs): super().call(*args, **kwargs) class QtSlot(Slot): """Qt direct slot, execute the call inside the qt-event-loop.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Create a QObject and move it to mainloop thread self._invoker = QObject() self._invoker.moveToThread(QApplication.instance().thread()) self._invoker.customEvent = self._custom_event def call(self, *args, **kwargs): QApplication.instance().sendEvent(self._invoker, self._event(*args, **kwargs)) def _event(self, *args, **kwargs): return QSlotEvent(self._reference, *args, **kwargs) def _custom_event(self, event): super().call(*event.args, **event.kwargs) class QtQueuedSlot(QtSlot): """Qt queued (safe) slot, execute the call inside the qt-event-loop.""" def call(self, *args, **kwargs): QApplication.instance().postEvent(self._invoker, self._event(*args, **kwargs)) class QSlotEvent(QEvent): EVENT_TYPE = QEvent.Type(QEvent.registerEventType()) def __init__(self, reference, *args, **kwargs): QEvent.__init__(self, QSlotEvent.EVENT_TYPE) self.reference = reference self.args = args self.kwargs = kwargs class Connection(Enum): """Available connection modes.""" Direct = Slot Async = AsyncSlot QtDirect = QtSlot QtQueued = QtQueuedSlot def new_slot(self, slot_callable, callback=None): return self.value(slot_callable, callback) class Signal: """Signal/slot implementation. A signal object can be connected/disconnected to a callable object (slot), the connection can have different modes, any mode define the way a slot is called, those are defined in :class:`Connection`. .. note:: * Any slot can be connected only once to a specific signal, if reconnected, the previous connection is overridden. * Internally, weak-references are used, so disconnection is not needed before delete a slot-owner object. * Signals with "arguments" can be connected to slot without arguments .. warning:: Because of weakrefs, connecting like the following can't work: signal.connect(lambda: some_operation)) signal.connect(NewObject().my_method) signal.connect(something_not_referenced) """ def __init__(self): self.__slots = {} self.__lock = RLock() def connect(self, slot_callable, mode=Connection.Direct): """Connect the given slot, if not already connected. :param slot_callable: The slot (a python callable) to be connected :param mode: Connection mode :type mode: Connection :raise ValueError: if mode not in Connection enum """ if mode not in Connection: raise ValueError('invalid mode value: {0}'.format(mode)) with self.__lock: # Create a new Slot object, use a weakref for the callback # to avoid cyclic references. callback = weak_call_proxy(weakref.WeakMethod(self.__remove_slot)) self.__slots[slot_id(slot_callable)] = mode.new_slot(slot_callable, callback) def disconnect(self, slot=None): """Disconnect the given slot, or all if no slot is specified. :param slot: The slot to be disconnected or None """ if slot is not None: self.__remove_slot(slot_id(slot)) else: with self.__lock: self.__slots.clear() def emit(self, *args, **kwargs): """Emit the signal within the given arguments""" with self.__lock: for slot in self.__slots.values(): try: slot.call(*args, **kwargs) except Exception: traceback.print_exc() def __remove_slot(self, id_): with self.__lock: self.__slots.pop(id_, None) linux-show-player-0.5.1/lisp/core/singleton.py000066400000000000000000000036141323636106000214030ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import ABCMeta from PyQt5.QtCore import QObject from lisp.core.qmeta import QABCMeta class Singleton(type): def __call__(cls, *args, **kwargs): try: return cls.__instance except AttributeError: cls.__instance = super(Singleton, cls).__call__(*args, **kwargs) return cls.__instance class ABCSingleton(ABCMeta): def __call__(cls, *args, **kwargs): try: return cls.__instance except AttributeError: cls.__instance = super(ABCSingleton, cls).__call__(*args, **kwargs) return cls.__instance class QSingleton(type(QObject)): def __call__(cls, *args, **kwargs): try: return cls.__instance except AttributeError: cls.__instance = super(QSingleton, cls).__call__(*args, **kwargs) return cls.__instance class QABCSingleton(QABCMeta): def __call__(cls, *args, **kwargs): try: return cls.__instance except AttributeError: cls.__instance = super(QABCSingleton, cls).__call__(*args, **kwargs) return cls.__instance linux-show-player-0.5.1/lisp/core/util.py000066400000000000000000000134061323636106000203560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import re import socket from collections import Mapping from enum import Enum from os import listdir from os.path import isdir, exists, join import functools def deep_update(d1, d2): """Recursively update d1 with d2""" for key in d2: if key not in d1: d1[key] = d2[key] elif isinstance(d2[key], Mapping) and isinstance(d1[key], Mapping): d1[key] = deep_update(d1[key], d2[key]) else: d1[key] = d2[key] return d1 def find_packages(path='.'): """List the python packages in the given directory.""" return [d for d in listdir(path) if isdir(join(path, d)) and exists(join(path, d, '__init__.py'))] def time_tuple(millis): """Split the given time in a tuple. :param millis: Number of milliseconds :type millis: int :return (hours, minutes, seconds, milliseconds) """ seconds, millis = divmod(millis, 1000) minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) return hours, minutes, seconds, millis def strtime(time, accurate=False): """Return a string from the given milliseconds time. :returns: hh:mm:ss when > 59min mm:ss:00 when < 1h and accurate=False mm:ss:z0 when < 1h and accurate=True """ # Cast time to int to avoid formatting problems time = time_tuple(int(time)) if time[0] > 0: return '{:02}:{:02}:{:02}'.format(*time[:-1]) elif accurate: return '{:02}:{:02}.{}0'.format(time[1], time[2], time[3] // 100) else: return '{:02}:{:02}.00'.format(*time[1:3]) def compose_http_url(url, port, directory='/'): """Compose an http URL.""" return 'http://' + url + ':' + str(port) + directory def greatest_common_superclass(instances): classes = [type(x).mro() for x in instances] for x in classes[0]: if all(x in mro for mro in classes): return x def get_lan_ip(): """Return active interface LAN IP, or localhost if no address is assigned. From: http://stackoverflow.com/a/28950776/5773767 """ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # Doesn't have to be reachable s.connect(('10.255.255.255', 0)) ip = s.getsockname()[0] except: ip = '127.0.0.1' finally: s.close() return ip def subclasses(cls): for subclass in cls.__subclasses__(): yield from subclasses(subclass) yield subclass def weak_call_proxy(weakref): def proxy(*args, **kwargs): if weakref() is not None: weakref()(*args, **kwargs) return proxy def natural_keys(text): """Turn a string into a list of string and number chunks. "z23a" -> ["z", 23, "a"] From: http://stackoverflow.com/a/5967539/5773767 .. highlight:: l = ['something1', 'something17', 'something4'] l.sort(key=natural_keys) # sorts in human order ['something1', 'something4', 'something17'] """ return [int(c) if c.isdigit() else c for c in re.split('(\d+)', text)] def rhasattr(obj, attr): """Check object's attribute, can use dot notation. .. highlight:: class A: pass a = A() a.b = A() a.b.c = 42 hasattr(a, 'b.c') # True """ return functools.reduce(hasattr, attr.split('.'), obj) def rsetattr(obj, attr, value): """Set object's attribute, can use dot notation. From: http://stackoverflow.com/a/31174427/5773767 .. highlight:: class A: pass a = A() a.b = A() a.b.c = 0 rsetattr(a, 'b.c', 42) a.b.c # 42 """ pre, _, post = attr.rpartition('.') setattr(rgetattr(obj, pre) if pre else obj, post, value) rgetattr_sentinel = object() def rgetattr(obj, attr, default=rgetattr_sentinel): """Get object's attribute, can use dot notation. From: http://stackoverflow.com/a/31174427/5773767 .. highlight:: class A: pass a = A() a.b = A() a.b.c = 24 rgetattr(a, 'b.c') # 42 """ if default is rgetattr_sentinel: _getattr = getattr else: def _getattr(obj, name): return getattr(obj, name, default) return functools.reduce(_getattr, attr.split('.'), obj) class EqEnum(Enum): """Value-comparable Enum. EqEnum members can be compared for equality with their values: .. highlight:: class E(EqEnum): A = 10 class E2(EqEnum): A2 = 10 # Equality NOT identity E.A == 10 # True E.A is 10 # False E.A == E.A2 # False """ def __eq__(self, other): if not isinstance(other, Enum): return self.value == other return super().__eq__(other) class FunctionProxy: """Allow to mask a function as an Object. Can be useful in enum.Enum (Python enumeration) to have callable values. """ def __init__(self, function): self.function = function def __call__(self, *args, **kwargs): return self.function(*args, **kwargs) linux-show-player-0.5.1/lisp/cues/000077500000000000000000000000001323636106000170325ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/cues/__init__.py000066400000000000000000000000001323636106000211310ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/cues/cue.py000066400000000000000000000442201323636106000201620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from threading import Lock from uuid import uuid4 from lisp.core.configuration import config from lisp.core.decorators import async from lisp.core.fade_functions import FadeInType, FadeOutType from lisp.core.has_properties import HasProperties, Property, WriteOnceProperty from lisp.core.rwait import RWait from lisp.core.signal import Signal from lisp.core.util import EqEnum class CueState: Invalid = 0 Error = 1 Stop = 2 Running = 4 Pause = 8 PreWait = 16 PostWait = 32 PreWait_Pause = 64 PostWait_Pause = 128 IsRunning = Running | PreWait | PostWait IsPaused = Pause | PreWait_Pause | PostWait_Pause IsStopped = Error | Stop class CueAction(EqEnum): Default = 'Default' FadeIn = 'FadeIn' FadeOut = 'FadeOut' FadeInStart = 'FadeInStart' FadeOutStop = 'FadeOutStop' FadeOutPause = 'FadeOutPause' FadeOutInterrupt = 'FadeOutInterrupt' Interrupt = 'Interrupt' Start = 'Start' Stop = 'Stop' Pause = 'Pause' class CueNextAction(EqEnum): DoNothing = 'DoNothing' AutoNext = 'AutoNext' AutoFollow = 'AutoFollow' class Cue(HasProperties): """Cue(s) are the base component for implement any kind of live-controllable element (live = during a show). A cue implement his behavior(s) reimplementing __start__, __stop__, __pause__ and __interrupt__ methods. Cue provide **(and any subclass should do the same)** properties via HasProperties/Property specifications. :ivar _type_: Cue type (class name). Should NEVER change after init. :ivar id: Identify the cue uniquely. Should NEVER change after init. :ivar index: Cue position in the view. :ivar name: Cue visualized name. :ivar description: Cue text description. :ivar stylesheet: Cue style, used by the view. :ivar duration: The cue duration in milliseconds. (0 means no duration) :ivar pre_wait: Cue pre-wait in seconds. :ivar post_wait: Cue post-wait in seconds. :ivar next_action: What do after post_wait. :ivar fadein_type: Fade-In type :ivar fadeout_type: Fade-Out type :ivar fadein_duration: Fade-In duration in seconds :ivar fadeout_duration: Fade-Out duration in seconds :ivar default_start_action: action to execute to start :ivar default_stop_action: action to execute to stop :cvar CueActions: actions supported by the cue (default: CueAction.Start) A cue should declare CueAction.Default as supported only if CueAction.Start and CueAction.Stop are both supported. If CueAction.Stop is supported, CueAction.Interrupt should be supported. .. Note:: If 'next_action' is AutoFollow or DoNothing, the postwait is not performed. """ Name = 'Cue' _type_ = WriteOnceProperty() id = WriteOnceProperty() name = Property(default='Untitled') index = Property(default=-1) description = Property(default='') stylesheet = Property(default='') duration = Property(default=0) pre_wait = Property(default=0) post_wait = Property(default=0) next_action = Property(default=CueNextAction.DoNothing.value) fadein_type = Property(default=FadeInType.Linear.name) fadeout_type = Property(default=FadeOutType.Linear.name) fadein_duration = Property(default=0) fadeout_duration = Property(default=0) default_start_action = Property(default=CueAction.Start.value) default_stop_action = Property(default=CueAction.Stop.value) CueActions = (CueAction.Start,) def __init__(self, id=None): super().__init__() self.id = str(uuid4()) if id is None else id self._type_ = self.__class__.__name__ self._st_lock = Lock() self._state = CueState.Stop self._prewait = RWait() self._postwait = RWait() # Pre-Wait signals self.prewait_start = self._prewait.start self.prewait_ended = self._prewait.ended self.prewait_paused = self._prewait.paused self.prewait_stopped = self._prewait.stopped # Post-Wait signals self.postwait_start = self._postwait.start self.postwait_ended = self._postwait.ended self.postwait_paused = self._postwait.paused self.postwait_stopped = self._postwait.stopped # Fade signals self.fadein_start = Signal() self.fadein_end = Signal() self.fadeout_start = Signal() self.fadeout_end = Signal() # Status signals self.interrupted = Signal() # self self.started = Signal() # self self.stopped = Signal() # self self.paused = Signal() # self self.error = Signal() # self, error, details self.next = Signal() # self self.end = Signal() # self self.changed('next_action').connect(self.__next_action_changed) def execute(self, action=CueAction.Default): """Execute the specified action, if supported. .. Note:: Even if not specified in Cue.CueActions, when CueAction.Default is given, a "default" action is selected depending on the current cue state, if this action is not supported nothing will be done. :param action: the action to be performed :type action: CueAction """ if action == CueAction.Default: if self._state & CueState.IsRunning: action = CueAction(self.default_stop_action) else: action = CueAction(self.default_start_action) if action is CueAction.Interrupt: self.interrupt() elif action is CueAction.FadeOutInterrupt: self.interrupt(fade=True) elif action in self.CueActions: if action == CueAction.Start: self.start() elif action == CueAction.FadeInStart: self.start(fade=self.fadein_duration > 0) elif action == CueAction.Stop: self.stop() elif action == CueAction.FadeOutStop: self.stop(fade=self.fadeout_duration > 0) elif action == CueAction.Pause: self.pause() elif action == CueAction.FadeOutPause: self.pause(fade=self.fadeout_duration > 0) elif action == CueAction.FadeOut: duration = config['Cue'].getfloat('FadeActionDuration') fade_type = FadeOutType[config['Cue'].get('FadeActionType')] self.fadeout(duration, fade_type) elif action == CueAction.FadeIn: duration = config['Cue'].getfloat('FadeActionDuration') fade_type = FadeInType[config['Cue'].get('FadeActionType')] self.fadein(duration, fade_type) @async def start(self, fade=False): """Start the cue.""" # If possible acquire the state-lock, otherwise return if not self._st_lock.acquire(blocking=False): return try: # If we are already running release and return if self._state & CueState.IsRunning: return state = self._state # PreWait if self.pre_wait and state & (CueState.IsStopped | CueState.PreWait_Pause): self._state = CueState.PreWait # Start the wait, the lock is released during the wait and # re-acquired after if not self._prewait.wait(self.pre_wait, lock=self._st_lock): # PreWait interrupted, check the state to be correct if self._state & CueState.PreWait: self._state ^= CueState.PreWait return # Cue-Start (still locked), the __start__ function should not block if state & (CueState.IsStopped | CueState.Pause | CueState.PreWait_Pause): running = self.__start__(fade) self._state = CueState.Running self.started.emit(self) if not running: self._ended() # PostWait (still locked) if state & (CueState.IsStopped | CueState.PreWait_Pause | CueState.PostWait_Pause): if self.next_action == CueNextAction.AutoNext: self._state |= CueState.PostWait if self._postwait.wait(self.post_wait, lock=self._st_lock): # PostWait ended self._state ^= CueState.PostWait self.next.emit(self) elif self._state & CueState.PostWait: # PostWait interrupted, check the state to be correct self._state ^= CueState.PostWait # If the cue was only post-waiting we remain with # an invalid state if not self._state: self._state = CueState.Stop finally: self._st_lock.release() def restart(self, fade=False): """Restart the cue if paused.""" # TODO: change to resume if self._state & CueState.IsPaused: self.start(fade) def __start__(self, fade=False): """Implement the cue `start` behavior. Long running task should not block this function (i.e. the fade should be performed in another thread). When called from `Cue.start()`, `_st_lock` is acquired. If the execution is instantaneous should return False, otherwise return True and call the `_ended` function later. :param fade: True if a fade should be performed (when supported) :type fade: bool :return: False if the cue is already terminated, True otherwise (e.g. asynchronous execution) :rtype: bool """ return False @async def stop(self, fade=False): """Stop the cue.""" # If possible acquire the state-lock, otherwise return if not self._st_lock.acquire(blocking=False): return try: # Stop PreWait (if in PreWait(_Pause) nothing else is "running") if self._state & (CueState.PreWait | CueState.PreWait_Pause): self._state = CueState.Stop self._prewait.stop() else: # Stop PostWait if self._state & (CueState.PostWait | CueState.PostWait_Pause): # Remove PostWait or PostWait_Pause state self._state = ( (self._state ^ CueState.PostWait) & (self._state ^ CueState.PostWait_Pause) ) self._postwait.stop() # Stop the cue if self._state & (CueState.Running | CueState.Pause): # Here the __stop__ function should release and re-acquire # the state-lock during a fade operation if not self.__stop__(fade): # Stop operation interrupted return # Remove Running or Pause state self._state = ( (self._state ^ CueState.Running) & (self._state ^ CueState.Pause) ) self._state |= CueState.Stop self.stopped.emit(self) finally: self._st_lock.release() def __stop__(self, fade=False): """Implement the cue `stop` behavior. Long running task should block this function (i.e. the fade should "block" this function), when this happen `_st_lock` must be released and then re-acquired. If called during a `fadeout` operation this should be interrupted, the cue stopped and return `True`. :param fade: True if a fade should be performed (when supported) :type fade: bool :return: False if interrupted, True otherwise :rtype: bool """ return False @async def pause(self, fade=False): """Pause the cue.""" # If possible acquire the state-lock, otherwise return if not self._st_lock.acquire(blocking=False): return try: # Pause PreWait (if in PreWait nothing else is "running") if self._state & CueState.PreWait: self._state ^= CueState.PreWait self._state |= CueState.PreWait_Pause self._prewait.pause() else: # Pause PostWait if self._state & CueState.PostWait: self._state ^= CueState.PostWait self._state |= CueState.PostWait_Pause self._postwait.pause() # Pause the cue if self._state & CueState.Running: # Here the __pause__ function should release and re-acquire # the state-lock during a fade operation if not self.__pause__(fade): return self._state ^= CueState.Running self._state |= CueState.Pause self.paused.emit(self) finally: self._st_lock.release() def __pause__(self, fade=False): """Implement the cue `pause` behavior. Long running task should block this function (i.e. the fade should "block" this function), when this happen `_st_lock` must be released and then re-acquired. If called during a `fadeout` operation this should be interrupted, the cue paused and return `True`. If during the execution the fade operation is interrupted this function must return `False`. :param fade: True if a fade should be performed (when supported) :type fade: bool :return: False if interrupted, True otherwise :rtype: bool """ return False @async def interrupt(self, fade=False): """Interrupt the cue. :param fade: True if a fade should be performed (when supported) :type fade: bool """ with self._st_lock: # Stop PreWait (if in PreWait(_Pause) nothing else is "running") if self._state & (CueState.PreWait | CueState.PreWait_Pause): self._state = CueState.Stop self._prewait.stop() else: # Stop PostWait if self._state & (CueState.PostWait | CueState.PostWait_Pause): # Remove PostWait or PostWait_Pause state self._state = ( (self._state ^ CueState.PostWait) & (self._state ^ CueState.PostWait_Pause) ) self._postwait.stop() # Interrupt the cue if self._state & (CueState.Running | CueState.Pause): self.__interrupt__(fade) # Remove Running or Pause state self._state = ( (self._state ^ CueState.Running) & (self._state ^ CueState.Pause) ) self._state |= CueState.Stop self.interrupted.emit(self) def __interrupt__(self, fade=False): """Implement the cue `interrupt` behavior. Long running task should block this function without releasing `_st_lock`. :param fade: True if a fade should be performed (when supported) :type fade: bool """ def fadein(self, duration, fade_type): """Fade-in the cue. :param duration: How much the fade should be long (in seconds) :type duration: float :param fade_type: The fade type :type fade_type: FadeInType """ def fadeout(self, duration, fade_type): """Fade-out the cue. :param duration: How much the fade should be long (in seconds) :type duration: float :param fade_type: The fade type :type fade_type: FadeOutType """ def _ended(self): """Remove the Running state, if needed set it to Stop.""" locked = self._st_lock.acquire(blocking=False) if self._state == CueState.Running: self._state = CueState.Stop else: self._state ^= CueState.Running self.end.emit(self) if locked: self._st_lock.release() def _error(self, message, details): """Remove Running/Pause/Stop state and add Error state.""" locked = self._st_lock.acquire(blocking=False) self._state = ( (self._state ^ CueState.Running) & (self._state ^ CueState.Pause) & (self._state ^ CueState.Stop) ) | CueState.Error self.error.emit(self, message, details) if locked: self._st_lock.release() def current_time(self): """Return the current execution time if available, otherwise 0. :rtype: int """ return 0 def prewait_time(self): return self._prewait.current_time() def postwait_time(self): return self._postwait.current_time() @property def state(self): """Return the current state. :rtype: int """ return self._state def __next_action_changed(self, next_action): self.end.disconnect(self.next.emit) if next_action == CueNextAction.AutoFollow: self.end.connect(self.next.emit) linux-show-player-0.5.1/lisp/cues/cue_actions.py000066400000000000000000000042321323636106000217010ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from ast import literal_eval from lisp.core.action import Action from lisp.ui.ui_utils import translate class UpdateCueAction(Action): __slots__ = ('__cue', '__new', '__old') def __init__(self, properties, cue): self.__cue = cue self.__new = properties self.__old = str(cue.properties()) def do(self): self.__cue.update_properties(self.__new) self.__new = str(self.__new) def undo(self): self.__cue.update_properties(literal_eval(self.__old)) def redo(self): self.__new = literal_eval(self.__new) self.do() def log(self): return translate('CueActionLog', 'Cue settings changed: "{}"').format(self.__cue.name) class UpdateCuesAction(Action): __slots__ = ('__cues', '__new', '__old') def __init__(self, properties, cues): self.__cues = cues self.__new = properties self.__old = str([cue.properties() for cue in cues]) def do(self): for cue in self.__cues: cue.update_properties(self.__new) self.__new = str(self.__new) def undo(self): for cue, old in zip(self.__cues, literal_eval(self.__old)): cue.update_properties(old) def redo(self): self.__new = literal_eval(self.__new) self.do() def log(self): return translate('CueActionLog', 'Cues settings changed.') linux-show-player-0.5.1/lisp/cues/cue_factory.py000066400000000000000000000054011323636106000217070ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from copy import deepcopy class CueFactory: """Provide a generic factory to build different cues types. Cues can be register via `register_factory` function. """ __REGISTRY = {} # Register methods @classmethod def register_factory(cls, cue_type, factory): """Register a new cue-type in the factory. :param cue_type: The cue class name :type cue_type: str :param factory: The cue class or a factory function """ cls.__REGISTRY[cue_type] = factory @classmethod def has_factory(cls, cue_type): """Return True if there is a factory for `cue_type` :param cue_type: The cue type to check :rtype cue_type: str :rtype: bool """ return cue_type in cls.__REGISTRY @classmethod def remove_factory(cls, cue_type): """Remove the registered cue from the factory :param cue_type: the cue class name (the same used for registration) """ cls.__REGISTRY.pop(cue_type) # Create methods @classmethod def create_cue(cls, cue_type, cue_id=None, **kwargs): """Return a new cue of the specified type. ..note: Some factory can take keyword-arguments (e.g. URIAudio) :param cue_id: The id to use with the new cue :param cue_type: The cue type :rtype: lisp.cues.cue.Cue """ factory = cls.__REGISTRY.get(cue_type) if not callable(factory): raise Exception( 'Cue not available or badly registered: {}'.format(cue_type)) return factory(id=cue_id, **kwargs) @classmethod def clone_cue(cls, cue): """Return a copy of the given cue. The id is not copied. :param cue: the cue to be copied :rtype: lisp.cues.cue.Cue """ properties = deepcopy(cue.properties()) properties.pop('id') cue = cls.create_cue(cue.__class__.__name__) cue.update_properties(properties) return cue linux-show-player-0.5.1/lisp/cues/cue_model.py000066400000000000000000000045471323636106000213520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.core.model import Model from lisp.cues.cue import Cue class CueModel(Model): """Simple model to store cue(s), as (cue.id, cue) pairs. The model can be iterated to retrieve the cues, to get id-cue pairs use the items() function, to get only the id(s) use the keys() function. """ def __init__(self): super().__init__() self.__cues = {} def add(self, cue): if cue.id in self.__cues: raise ValueError('the cue is already in the layout') self.__cues[cue.id] = cue self.item_added.emit(cue) def remove(self, cue): self.pop(cue.id) def pop(self, cue_id): cue = self.__cues.pop(cue_id) self.item_removed.emit(cue) return cue def get(self, cue_id, default=None): return self.__cues.get(cue_id, default) def items(self): """Return a set-like object proving a view on model items (id, cue)""" return self.__cues.items() def keys(self): """Return a set-like object proving a view on of model keys (cue id)""" return self.__cues.keys() def reset(self): self.__cues.clear() self.model_reset.emit() def filter(self, cue_class=Cue): """Return an iterator over cues that are instances of the given class""" for cue in self.__cues.values(): if isinstance(cue, cue_class): yield cue def __iter__(self): return self.__cues.values().__iter__() def __len__(self): return len(self.__cues) def __contains__(self, cue): return cue.id in self.__cues linux-show-player-0.5.1/lisp/cues/cue_time.py000066400000000000000000000120711323636106000211770ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from enum import IntEnum from weakref import WeakValueDictionary from lisp.core.clock import Clock_100 from lisp.core.decorators import locked_method from lisp.core.signal import Connection, Signal from lisp.cues.cue import CueState class MetaCueTime(type): """Allow "caching" of CueTime(s) objects.""" __Instances = WeakValueDictionary() @locked_method def __call__(cls, cue, *args, **kwargs): instance = MetaCueTime.__Instances.get((cls, cue.id)) if instance is None: instance = super().__call__(cue, *args, **kwargs) MetaCueTime.__Instances[(cls, cue.id)] = instance return instance class CueTime(metaclass=MetaCueTime): """Provide timing for a Cue. Once created the notify signal provide timing for the given cue. The current time is queried using `Cue.current_time()`. .. note:: The notify signal is emitted only when the cue is running. """ _Clock = Clock_100 def __init__(self, cue): self.notify = Signal() self._clock = self._Clock self._active = False self._cue = cue self._cue.changed('duration').connect(self.__init) self.__init() def __init(self, *args): if self._cue.duration > 0 and not self._active: # Cue "status" signals self._cue.started.connect(self.start, Connection.QtQueued) self._cue.paused.connect(self.stop, Connection.QtQueued) self._cue.stopped.connect(self.stop, Connection.QtQueued) self._cue.end.connect(self.stop, Connection.QtQueued) self._cue.error.connect(self.stop, Connection.QtQueued) if self._cue.state & CueState.Running: self.start() self._active = True elif self._cue.duration < 0 and self._active: self._cue.started.disconnect(self.start) self._cue.paused.disconnect(self.stop) self._cue.stopped.disconnect(self.stop) self._cue.end.disconnect(self.stop) self._cue.error.disconnect(self.stop) self.stop() self._active = False def __notify(self): """Notify the cue current-time""" if self._cue.state & (CueState.Running ^ CueState.Pause): self.notify.emit(self._cue.current_time()) def start(self): self._clock.add_callback(self.__notify) def stop(self): try: self._clock.remove_callback(self.__notify) except Exception: # TODO: catch only the exception when the callback is not registered pass class CueWaitTime: """Provide timing for Cue pre/post waits. Once created the notify signal provide timing for the specified wait for the given cue. The time since the wait start is calculated internally using the :mod:`time` module functions. """ class Mode(IntEnum): Pre = 0 Post = 1 def __init__(self, cue, mode=Mode.Pre): self.notify = Signal() self._clock = Clock_100 self._start_time = 0 self._last = 0 self._cue = cue self._mode = mode if self._mode == CueWaitTime.Mode.Pre: self._cue.prewait_ended.connect(self.stop, Connection.QtQueued) self._cue.prewait_start.connect(self.start, Connection.QtQueued) self._cue.prewait_paused.connect(self.stop, Connection.QtQueued) self._cue.prewait_stopped.connect(self.stop, Connection.QtQueued) elif self._mode == CueWaitTime.Mode.Post: self._cue.postwait_ended.connect(self.stop, Connection.QtQueued) self._cue.postwait_start.connect(self.start, Connection.QtQueued) self._cue.postwait_paused.connect(self.stop, Connection.QtQueued) self._cue.postwait_stopped.connect(self.stop, Connection.QtQueued) def __notify(self): if self._mode == CueWaitTime.Mode.Pre: self.notify.emit(int(self._cue.prewait_time() * 100) * 10) else: self.notify.emit(int(self._cue.postwait_time() * 100) * 10) def start(self): self._clock.add_callback(self.__notify) def stop(self): try: self._clock.remove_callback(self.__notify) except Exception: # TODO: catch only the exception when the callback is not registered pass linux-show-player-0.5.1/lisp/cues/media_cue.py000066400000000000000000000154101323636106000213200ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from threading import Lock from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.core.configuration import config from lisp.core.decorators import async from lisp.core.fade_functions import FadeInType, FadeOutType from lisp.core.fader import Fader from lisp.core.has_properties import NestedProperties from lisp.cues.cue import Cue, CueAction, CueState class MediaCue(Cue): Name = QT_TRANSLATE_NOOP('CueName', 'Media Cue') _media_ = NestedProperties('media', default={}) CueActions = (CueAction.Default, CueAction.Start, CueAction.FadeInStart, CueAction.Stop, CueAction.FadeOutStop, CueAction.Pause, CueAction.FadeOut, CueAction.FadeIn, CueAction.FadeOutPause, CueAction.Interrupt, CueAction.FadeOutInterrupt) def __init__(self, media, id=None): super().__init__(id=id) self.default_start_action = CueAction.FadeInStart.value self.default_stop_action = CueAction.FadeOutStop.value self.media = media self.media.changed('duration').connect(self._duration_change) self.media.elements_changed.connect(self.__elements_changed) self.media.error.connect(self._on_error) self.media.eos.connect(self._on_eos) self.__in_fadein = False self.__in_fadeout = False self.__volume = self.media.element('Volume') self.__fader = Fader(self.__volume, 'current_volume') self.__fade_lock = Lock() def __elements_changed(self): self.__volume = self.media.element('Volume') self.__fader.target = self.__volume def __start__(self, fade=False): if fade and self._can_fade(self.fadein_duration): self.__volume.current_volume = 0 self.media.play() if fade: self._on_start_fade() return True def __stop__(self, fade=False): if self.__in_fadeout: self.__fader.stop() else: if self.__in_fadein: self.__fader.stop() if self._state & CueState.Running and fade: self._st_lock.release() ended = self._on_stop_fade() self._st_lock.acquire() if not ended: return False self.media.stop() return True def __pause__(self, fade=False): if self.__in_fadeout: self.__fader.stop() else: if self.__in_fadein: self.__fader.stop() if fade: self._st_lock.release() ended = self._on_stop_fade() self._st_lock.acquire() if not ended: return False self.media.pause() return True def __interrupt__(self, fade=False): self.__fader.stop() if self._state & CueState.Running and fade: self._on_stop_fade(interrupt=True) self.media.interrupt() @async def fadein(self, duration, fade_type): if not self._st_lock.acquire(timeout=0.1): return if self._state & CueState.Running: self.__fader.stop() if self.__volume is not None: if duration <= 0: self.__volume.current_volume = self.__volume.volume else: self._st_lock.release() self.__fadein(duration, self.__volume.volume, fade_type) return self._st_lock.release() @async def fadeout(self, duration, fade_type): if not self._st_lock.acquire(timeout=0.1): return if self._state & CueState.Running: self.__fader.stop() if self.__volume is not None: if duration <= 0: self.__volume.current_volume = 0 else: self._st_lock.release() self.__fadeout(duration, 0, fade_type) return self._st_lock.release() def __fadein(self, duration, to_value, fade_type): ended = True if self._can_fade(duration): with self.__fade_lock: self.__in_fadein = True self.fadein_start.emit() try: self.__fader.prepare() ended = self.__fader.fade(duration, to_value, fade_type) finally: self.__in_fadein = False self.fadein_end.emit() return ended def __fadeout(self, duration, to_value, fade_type): ended = True if self._can_fade(duration): with self.__fade_lock: self.__in_fadeout = True self.fadeout_start.emit() try: self.__fader.prepare() ended = self.__fader.fade(duration, to_value, fade_type) finally: self.__in_fadeout = False self.fadeout_end.emit() return ended def current_time(self): return self.media.current_time() def _duration_change(self, value): self.duration = value def _on_eos(self, *args): with self._st_lock: self.__fader.stop() self._ended() def _on_error(self, media, message, details): with self._st_lock: self.__fader.stop() self._error(message, details) def _can_fade(self, duration): return self.__volume is not None and duration > 0 @async def _on_start_fade(self): if self.__volume is not None: self.__fadein(self.fadein_duration, self.__volume.volume, FadeInType[self.fadein_type]) def _on_stop_fade(self, interrupt=False): if interrupt: duration = config['Cue'].getfloat('InterruptFade') fade_type = config['Cue'].get('InterruptFadeType') else: duration = self.fadeout_duration fade_type = self.fadeout_type return self.__fadeout(duration, 0, FadeOutType[fade_type]) linux-show-player-0.5.1/lisp/default.cfg000066400000000000000000000021361323636106000202020ustar00rootroot00000000000000#LiSP (Linux Show Player) configuration [Version] #Don't change this section values Number = 20 [Cue] FadeActionDuration = 3 FadeActionType = Linear InterruptFade = 3 InterruptFadeType = Linear [Theme] Theme = Dark Icons = numix [Backend] Default = gst [Gst] Pipeline = Volume, Equalizer10, DbMeter, AutoSink [Layout] Default = NoDefault [MIDI] InputDevice = SysDefault OutputDevice = SysDefault Backend = mido.backends.rtmidi [Remote] BindIp = 0.0.0.0 BindPort = 8070 DiscoverPort = 50000 DiscoverMagic = L1SPR3m0t3 [Actions] MaxStackSize = 0 [CartLayout] GridColumns = 7 GridRows = 4 ShowSeek = False ShowDbMeters = False ShowAccurate = False ShowVolume = False CountDown = True AutoAddPage = True [ListLayout] ShowDbMeters = True ShowSeek = True ShowAccurate = False ShowPlaying = True AutoContinue = True EndList = Stop GoKey = Space StopCueFade = True PauseCueFade = True ResumeCueFade = True InterruptCueFade = True StopAllFade = False PauseAllFade = False ResumeAllFade = False InterruptAllFade = True [DbMeter] dBMax = 0 dbMin = -60 dbClip = 0 [Timecode] Enabled = True Format = FILM HRes = True linux-show-player-0.5.1/lisp/i18n/000077500000000000000000000000001323636106000166525ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/i18n/lisp_cs.qm000066400000000000000000000365021323636106000206530ustar00rootroot00000000000000ҳ& n-L6.z6z^(n,M2 c&\.0K7TH5'WF F$+ +%I^. ~ '5.a>W9kv:c3 q9M#cs3;R_Υ Cfٰs7";Q& r r%wt8wt)@Pʨ$Tis]΀$Ύ$ή%\ - % >*R S&/- b5 h>* la,f ve(J v0 W./ N. W - 9 gc gd gd) s> k% t u  n n!' u )+ ;  ;  GuN bC'{ bO' bc( wD wD) 6 $ 15 M@0o E ,u" ,"a ," RY.0 W[ ^}T5 ztR #? 5# 2# / 7 *K Īel 0 Us  Us&, P ? P* _D $ i yu <7u 4v")/L6z  -gk1r32hPi78O programu Linux Show PlayerAbout Linux Show PlayerAbout AutoYiAuthorsAboutPYispvatel ContributorsAboutPYekladatel TranslatorsAboutPYispvatel Contributors AboutDialogInformaceInfo AboutDialogLicenceLicense AboutDialogLinux Show Player je pYehrva zznamo s nar~kami navr~en pro vrobu scn.ALinux Show Player is a cue-player designed for stage productions. AboutDialogZdrojov kd Source code AboutDialog&U~ivatelsk skupina Users group AboutDialogStrnkyWeb site AboutDialogMotiv programuApplication themeAppGeneralSettings,Rozvr~en pYi spuatnStartup layoutAppGeneralSettings4Pou~t dialog pYi spuatnUse startup dialogAppGeneralSettingsNastaven LiSPLiSP preferences AppSettingsPYidat stranuAdd page CartLayoutPYidat strany Add pages CartLayoutHJste si jist, ~e chcete pokra ovat?Are you sure to continue? CartLayout>Automaticky pYidat novou stranuAutomatically add new page CartLayout$Re~im odpo tvnCountdown mode CartLayoutVchoz chovnDefault behaviors CartLayoutUpravit nar~kuEdit cue CartLayoutLKa~d nar~ka na stran bude ztracena.#Every cue in the page will be lost. CartLayoutVelikost mY~ky Grid size CartLayoutPo et stran:Number of Pages: CartLayoutPo et sloupcoNumber of columns CartLayoutPo et YdkoNumber of rows CartLayout StranaPage CartLayoutPozastavitPause CartLayoutPYehrtPlay CartLayoutOdstranitRemove CartLayout0Odstranit nynja stranuRemove current page CartLayout0Nastavit znovu hlasitost Reset volume CartLayout VybratSelect CartLayout"Ukzat pYesn  asShow accurate time CartLayout.Ukzat mYidla decibeloShow dB-meters CartLayout2Ukzat prohledvac pruhyShow seek-bars CartLayout Ukzat hlasitost Show volume CartLayoutZastavitStop CartLayoutVarovnWarning CartLayoutVchozDefault CueAction6Za tek postupnho zeslen FadeInStart CueAction@Pozastaven postupnho zeslaben FadeOutPause CueAction4Konec postupnho zeslaben FadeOutStop CueActionPozastavitPause CueActionSpustitStart CueActionZastavitStop CueAction>Nastaven nar~ky zmnna: "{}"Cue settings changed: "{}" CueActionLog4Nastaven nar~ky zmnna.Cues settings changed. CueActionLog BarvaColorCueAppearanceSettingsNzev nar~kyCue nameCueAppearanceSettingsPopis/PoznmkaDescription/NoteCueAppearanceSettingsBez nzvuNoNameCueAppearanceSettings&Vybrat barvu pozadSelect background colorCueAppearanceSettings$Vybrat barvu psmaSelect font colorCueAppearanceSettings.Nastavit velikost psma Set Font SizeCueAppearanceSettings4Vzhled zvis na rozvr~en$The appearance depends on the layoutCueAppearanceSettings"Nar~ka v zznamu Media CueCueNameChovn Behaviours CueSettingsHVchoz  innost pro spuatn nar~kyDefault action to start the cue CueSettingsJVchoz  innost pro zastaven nar~kyDefault action to stop the cue CueSettings$ innost prolnn Fade Action CueSettingsFPostupn zeslen/zeslaben signlu Fade In/Out CueSettings&Prolnn pYeruaenInterrupt Fade CueSettingsDala  innost Next action CueSettings ekn po Post wait CueSettings ekn pYedPre wait CueSettings ekn pYed/po Pre/Post Wait CueSettings  innost spuatn Start action CueSettings" innost zastaven Stop action CueSettings4 ekat po proveden nar~kyWait after cue execution CueSettings: ekat pYed provedenm nar~kyWait before cue execution CueSettingsLinernLinearFadeKvadratick QuadraticFadeKvadratick 2 Quadratic2Fade KYivkaCurveFadeEditDoba trvn (s)Duration (sec)FadeEdit2Postupn zeslen signluFade In FadeSettings4Postupn zeslaben signluFade Out FadeSettingsLUspoYdat nar~ky v mY~ce jako strany Organize cues in grid like pagesLayoutDescription6UspoYdat nar~ky v seznamuOrganize the cues in a listLayoutDescriptionFCtrl + klepnut pro vybrn nar~kyCTRL + Click to select a cue LayoutDetailspCtrl + klepnut levm tla tkem myai pro vybrn nar~ky CTRL + Left Click to select cues LayoutDetailsJKlepnout na nar~ku pro jej spuatnClick a cue to run it LayoutDetailsJShift + klepnut pro upraven nar~kySHIFT + Click to edit a cue LayoutDetailsvShift + mezernk nebo dvojit klepnut pro upraven nar~ky+SHIFT + Space or Double-Click to edit a cue LayoutDetailsPro zkoprovn nar~ek je thnte za sou asnho dr~en klvesy Ctrl*To copy cues drag them while pressing CTRL LayoutDetailsPro zkoprovn nar~ek je thnte za sou asnho dr~en klvesy Shift+To copy cues drag them while pressing SHIFT LayoutDetails@Pro pYesunut nar~ek je thnteTo move cues drag them LayoutDetailsVbr rozvr~enLayout selection LayoutSelectOtevYt soubor Open file LayoutSelect Vybrat rozvr~en Select layout LayoutSelect"Na konci seznamu: At list end: ListLayout@Vybrat dala nar~ku automatickyAuto-select next cue ListLayoutVchoz chovnDefault behaviors ListLayoutUpravit nar~kuEdit cue ListLayout(Postupn zeslit vae Fade-In all ListLayout*Postupn zeslabit vae Fade-Out all ListLayout(Klvesa pro pYechod:Go key: ListLayoutPYeruait vae Interrupt All ListLayout*Nar~ka pro pYeruaen Interrupt Cue ListLayoutPYeruait vae Interrupt all ListLayoutPozastavit vae Pause All ListLayout.Nar~ka pro pozastaven Pause Cue ListLayoutPozastavit vae Pause all ListLayoutOdstranitRemove ListLayout2Nastavit znovu na vchozRestart ListLayout"Spustit vae znovu Restart All ListLayout:Nar~ka pro optovn spuatn Restart Cue ListLayout"Spustit vae znovu Restart all ListLayout VybratSelect ListLayout"Ukzat pYesn  asShow accurate time ListLayout.Ukzat mYidla decibeloShow dB-meters ListLayout2Ukzat pYehrvan nar~kyShow playing cues ListLayout2Ukzat prohledvac pruhyShow seek-bars ListLayoutZastavitStop ListLayoutZastavit vaeStop All ListLayout*Nar~ka pro zastavenStop Cue ListLayoutZastavit vaeStop all ListLayout Pou~t prolnnUse fade ListLayout innostActionListLayoutHeaderNar~kaCueListLayoutHeader ekn po Post waitListLayoutHeader ekn pYedPre waitListLayoutHeaderPopis nar~kyCue descriptionListLayoutInfoPanelNzev nar~kyCue nameListLayoutInfoPanel LadnDebugLoggingPodrobnosti:Details:Logging ChybaErrorLoggingInformace InformationLoggingVarovnWarningLogging&O programu&About MainWindowp&ravy&Edit MainWindow&Soubor&File MainWindow&Rozvr~en&Layout MainWindow&Nstroje&Tools MainWindowO programuAbout MainWindowO QtAbout Qt MainWindow CTRL+ICTRL+I MainWindowCtrl+Shift+A CTRL+SHIFT+A MainWindowCtrl+Shift+E CTRL+SHIFT+E MainWindowZavYt sezen Close session MainWindow0Odzna it vaechny nar~ky Deselect all MainWindowZahodit zmny?Discard the changes? MainWindowUpravit vybran Edit selected MainWindowUpravit vbrEdit selection MainWindowUkon itExit MainWindow$Na celou obrazovku Full Screen MainWindowObrtit vbrInvert selection MainWindowNov sezen New session MainWindowOtevYtOpen MainWindowNastaven Preferences MainWindow ZnovuRedo MainWindowUdlno znovu: Redone:  MainWindowUlo~it jakoSave as MainWindowUlo~it sezen Save session MainWindowVybrat vae Select all MainWindow@Vybrat vaechny nar~ky v zznamuSelect all media cues MainWindow8Nynja sezen nen ulo~eno.!The current session is not saved. MainWindowZptUndo MainWindowVrceno zpt: Undone:  MainWindow8Zvukov nar~ka (ze souboru)Audio cue (from file) MediaCueMenus2Vybrat soubory se zznamySelect media files MediaCueMenus Smy kaLoopMediaCueSettingslOpakovn po prvnm pYehrn (-1 = po nekone nou dobu)+Repetition after first play (-1 = infinite)MediaCueSettings.Poloha spuatn zznamuStart position of the mediaMediaCueSettings as spuatn Start timeMediaCueSettings0Poloha zastaven zznamuStop position of the mediaMediaCueSettings as zastaven Stop timeMediaCueSettings~Klepnut pravm tla tkem myai pro nastaven na vchoz hodnotuRight click to reset QColorButton Vzhled AppearanceSettingsPageNameNar~kaCueSettingsPageName"Nastaven nar~ky Cue SettingsSettingsPageName ObecnGeneralSettingsPageNamelinux-show-player-0.5.1/lisp/i18n/lisp_cs.ts000066400000000000000000001063741323636106000206710ustar00rootroot00000000000000 About Authors Autoři Contributors Přispěvatelé Translators Překladatelé About Linux Show Player O programu Linux Show Player AboutDialog Linux Show Player is a cue-player designed for stage productions. Linux Show Player je přehrávač záznamů s narážkami navržený pro výrobu scén. Web site Stránky Users group Uživatelská skupina Source code Zdrojový kód Info Informace License Licence Contributors Přispěvatelé AppGeneralSettings Startup layout Rozvržení při spuštění Use startup dialog Použít dialog při spuštění Application theme Motiv programu AppSettings LiSP preferences Nastavení LiSP CartLayout Add page Přidat stranu Add pages Přidat strany Remove current page Odstranit nynější stranu Countdown mode Režim odpočítávání Show seek-bars Ukázat prohledávací pruhy Show dB-meters Ukázat měřidla decibelů Show volume Ukázat hlasitost Show accurate time Ukázat přesný čas Edit cue Upravit narážku Remove Odstranit Select Vybrat Play Přehrát Pause Pozastavit Stop Zastavit Reset volume Nastavit znovu hlasitost Number of Pages: Počet stran: Warning Varování Every cue in the page will be lost. Každá narážka na straně bude ztracena. Are you sure to continue? Jste si jistý, že chcete pokračovat? Page Strana Default behaviors Výchozí chování Automatically add new page Automaticky přidat novou stranu Grid size Velikost mřížky Number of columns Počet sloupců Number of rows Počet řádků CueAction Default Výchozí Pause Pozastavit Start Spustit Stop Zastavit FadeInStart Začátek postupného zesílení FadeOutStop Konec postupného zeslabení FadeOutPause Pozastavení postupného zeslabení CueActionLog Cue settings changed: "{}" Nastavení narážky změněna: "{}" Cues settings changed. Nastavení narážky změněna. CueAppearanceSettings The appearance depends on the layout Vzhled závisí na rozvržení Cue name Název narážky NoName Bez názvu Description/Note Popis/Poznámka Set Font Size Nastavit velikost písma Color Barva Select background color Vybrat barvu pozadí Select font color Vybrat barvu písma CueName Media Cue Narážka v záznamu CueSettings Pre wait Čekání před Wait before cue execution Čekat před provedením narážky Post wait Čekání po Wait after cue execution Čekat po provedení narážky Next action Další činnost Interrupt Fade Prolínání přerušení Fade Action Činnost prolínání Behaviours Chování Pre/Post Wait Čekání před/po Fade In/Out Postupné zesílení/zeslabení signálu Start action Činnost spuštění Default action to start the cue Výchozí činnost pro spuštění narážky Stop action Činnost zastavení Default action to stop the cue Výchozí činnost pro zastavení narážky Fade Linear Lineární Quadratic Kvadratický Quadratic2 Kvadratický 2 FadeEdit Duration (sec) Doba trvání (s) Curve Křivka FadeSettings Fade In Postupné zesílení signálu Fade Out Postupné zeslabení signálu LayoutDescription Organize cues in grid like pages Uspořádat narážky v mřížce jako strany Organize the cues in a list Uspořádat narážky v seznamu LayoutDetails Click a cue to run it Klepnout na narážku pro její spuštění SHIFT + Click to edit a cue Shift + klepnutí pro upravení narážky CTRL + Click to select a cue Ctrl + klepnutí pro vybrání narážky SHIFT + Space or Double-Click to edit a cue Shift + mezerník nebo dvojité klepnutí pro upravení narážky To copy cues drag them while pressing CTRL Pro zkopírování narážek je táhněte za současného držení klávesy Ctrl To copy cues drag them while pressing SHIFT Pro zkopírování narážek je táhněte za současného držení klávesy Shift CTRL + Left Click to select cues Ctrl + klepnutí levým tlačítkem myši pro vybrání narážky To move cues drag them Pro přesunutí narážek je táhněte LayoutSelect Layout selection Výběr rozvržení Select layout Vybrat rozvržení Open file Otevřít soubor ListLayout Show playing cues Ukázat přehrávané narážky Show dB-meters Ukázat měřidla decibelů Show seek-bars Ukázat prohledávací pruhy Show accurate time Ukázat přesný čas Auto-select next cue Vybrat další narážku automaticky Edit cue Upravit narážku Remove Odstranit Select Vybrat Stop all Zastavit vše Pause all Pozastavit vše Restart all Spustit vše znovu Stop Zastavit Restart Nastavit znovu na výchozí Default behaviors Výchozí chování At list end: Na konci seznamu: Go key: Klávesa pro přechod: Interrupt all Přerušit vše Fade-Out all Postupně zeslabit vše Fade-In all Postupně zesílit vše Use fade Použít prolínání Stop Cue Narážka pro zastavení Pause Cue Narážka pro pozastavení Restart Cue Narážka pro opětovné spuštění Interrupt Cue Narážka pro přerušení Stop All Zastavit vše Pause All Pozastavit vše Restart All Spustit vše znovu Interrupt All Přerušit vše ListLayoutHeader Cue Narážka Pre wait Čekání před Action Činnost Post wait Čekání po ListLayoutInfoPanel Cue name Název narážky Cue description Popis narážky Logging Information Informace Debug Ladění Warning Varování Error Chyba Details: Podrobnosti: MainWindow &File &Soubor New session Nové sezení Open Otevřít Save session Uložit sezení Preferences Nastavení Save as Uložit jako Full Screen Na celou obrazovku Exit Ukončit &Edit Úp&ravy Undo Zpět Redo Znovu Select all Vybrat vše Select all media cues Vybrat všechny narážky v záznamu Deselect all Odznačit všechny narážky CTRL+SHIFT+A Ctrl+Shift+A Invert selection Obrátit výběr CTRL+I CTRL+I Edit selected Upravit vybrané CTRL+SHIFT+E Ctrl+Shift+E &Layout &Rozvržení &Tools &Nástroje Edit selection Upravit výběr &About &O programu About O programu About Qt O Qt Undone: Vráceno zpět: Redone: Uděláno znovu: Close session Zavřít sezení The current session is not saved. Nynější sezení není uloženo. Discard the changes? Zahodit změny? MediaCueMenus Audio cue (from file) Zvuková narážka (ze souboru) Select media files Vybrat soubory se záznamy MediaCueSettings Start time Čas spuštění Stop position of the media Poloha zastavení záznamu Stop time Čas zastavení Start position of the media Poloha spuštění záznamu Loop Smyčka Repetition after first play (-1 = infinite) Opakování po prvním přehrání (-1 = po nekonečnou dobu) QColorButton Right click to reset Klepnutí pravým tlačítkem myši pro nastavení na výchozí hodnotu SettingsPageName Appearance Vzhled General Obecné Cue Narážka Cue Settings Nastavení narážky linux-show-player-0.5.1/lisp/i18n/lisp_en.qm000066400000000000000000000333501323636106000206460ustar00rootroot00000000000000ҳ! n'6.zz^#]n'#M, " \+.p0K TH5W WFF+ + I^)x ~'/.a>W9v:c %9M#cs-kRH_Υ C4ٰs0;0Q& r r!wt2wt$ʨ_s΀ lΎ ,ή - % >*R S&/(c b/ h>%^ la& ve#& v+ W.* N) W! -  gc gd gd# s> k% t q ~ n nY u  )&Y ;  ;  Gu bC" bO" bc" wD wD$ 0c $h + M@* E ,u ,G , RY.+H W[: ^}T0 zt  5S 2  7 $ Īe *r Us @ Us!r P = P% _D   y <1 4j))6ziT ( gFk+3L2 Ji1W.About Linux Show PlayerAbout Linux Show PlayerAboutAuthorsAuthorsAboutContributors ContributorsAboutTranslators TranslatorsAboutContributors Contributors AboutDialogInfoInfo AboutDialogLicenseLicense AboutDialogLinux Show Player is a cue-player designed for stage productions.ALinux Show Player is a cue-player designed for stage productions. AboutDialogSource code Source code AboutDialogUsers group Users group AboutDialogWeb siteWeb site AboutDialog"Application themeApplication themeAppGeneralSettingsStartup layoutStartup layoutAppGeneralSettings$Use startup dialogUse startup dialogAppGeneralSettings LiSP preferencesLiSP preferences AppSettingsAdd pageAdd page CartLayoutAdd pages Add pages CartLayout2Are you sure to continue?Are you sure to continue? CartLayout4Automatically add new pageAutomatically add new page CartLayoutCountdown modeCountdown mode CartLayout"Default behaviorsDefault behaviors CartLayoutEdit cueEdit cue CartLayoutFEvery cue in the page will be lost.#Every cue in the page will be lost. CartLayoutGrid size Grid size CartLayout Number of Pages:Number of Pages: CartLayout"Number of columnsNumber of columns CartLayoutNumber of rowsNumber of rows CartLayoutPagePage CartLayout PausePause CartLayoutPlayPlay CartLayout RemoveRemove CartLayout&Remove current pageRemove current page CartLayoutReset volume Reset volume CartLayout SelectSelect CartLayout$Show accurate timeShow accurate time CartLayoutShow dB-metersShow dB-meters CartLayoutShow seek-barsShow seek-bars CartLayoutShow volume Show volume CartLayoutStopStop CartLayoutWarningWarning CartLayoutDefaultDefault CueActionFadeInStart FadeInStart CueActionFadeOutPause FadeOutPause CueActionFadeOutStop FadeOutStop CueAction PausePause CueAction StartStart CueActionStopStop CueAction4Cue settings changed: "{}"Cue settings changed: "{}" CueActionLog,Cues settings changed.Cues settings changed. CueActionLog ColorColorCueAppearanceSettingsCue nameCue nameCueAppearanceSettings Description/NoteDescription/NoteCueAppearanceSettings NoNameNoNameCueAppearanceSettings.Select background colorSelect background colorCueAppearanceSettings"Select font colorSelect font colorCueAppearanceSettingsSet Font Size Set Font SizeCueAppearanceSettingsHThe appearance depends on the layout$The appearance depends on the layoutCueAppearanceSettingsMedia Cue Media CueCueNameBehaviours Behaviours CueSettings>Default action to start the cueDefault action to start the cue CueSettings<Default action to stop the cueDefault action to stop the cue CueSettingsFade Action Fade Action CueSettingsFade In/Out Fade In/Out CueSettingsInterrupt FadeInterrupt Fade CueSettingsNext action Next action CueSettingsPost wait Post wait CueSettingsPre waitPre wait CueSettingsPre/Post Wait Pre/Post Wait CueSettingsStart action Start action CueSettingsStop action Stop action CueSettings0Wait after cue executionWait after cue execution CueSettings2Wait before cue executionWait before cue execution CueSettings LinearLinearFadeQuadratic QuadraticFadeQuadratic2 Quadratic2Fade CurveCurveFadeEditDuration (sec)Duration (sec)FadeEditFade InFade In FadeSettingsFade OutFade Out FadeSettings@Organize cues in grid like pages Organize cues in grid like pagesLayoutDescription6Organize the cues in a listOrganize the cues in a listLayoutDescription8CTRL + Click to select a cueCTRL + Click to select a cue LayoutDetails@CTRL + Left Click to select cues CTRL + Left Click to select cues LayoutDetails*Click a cue to run itClick a cue to run it LayoutDetails6SHIFT + Click to edit a cueSHIFT + Click to edit a cue LayoutDetailsVSHIFT + Space or Double-Click to edit a cue+SHIFT + Space or Double-Click to edit a cue LayoutDetailsTTo copy cues drag them while pressing CTRL*To copy cues drag them while pressing CTRL LayoutDetailsVTo copy cues drag them while pressing SHIFT+To copy cues drag them while pressing SHIFT LayoutDetails,To move cues drag themTo move cues drag them LayoutDetails Layout selectionLayout selection LayoutSelectOpen file Open file LayoutSelectSelect layout Select layout LayoutSelectAt list end: At list end: ListLayout(Auto-select next cueAuto-select next cue ListLayout"Default behaviorsDefault behaviors ListLayoutEdit cueEdit cue ListLayoutFade-In all Fade-In all ListLayoutFade-Out all Fade-Out all ListLayoutGo key:Go key: ListLayoutInterrupt All Interrupt All ListLayoutInterrupt Cue Interrupt Cue ListLayoutInterrupt all Interrupt all ListLayoutPause All Pause All ListLayoutPause Cue Pause Cue ListLayoutPause all Pause all ListLayout RemoveRemove ListLayoutRestartRestart ListLayoutRestart All Restart All ListLayoutRestart Cue Restart Cue ListLayoutRestart all Restart all ListLayout SelectSelect ListLayout$Show accurate timeShow accurate time ListLayoutShow dB-metersShow dB-meters ListLayout"Show playing cuesShow playing cues ListLayoutShow seek-barsShow seek-bars ListLayoutStopStop ListLayoutStop AllStop All ListLayoutStop CueStop Cue ListLayoutStop allStop all ListLayoutUse fadeUse fade ListLayout ActionActionListLayoutHeaderCueCueListLayoutHeaderPost wait Post waitListLayoutHeaderPre waitPre waitListLayoutHeaderCue descriptionCue descriptionListLayoutInfoPanelCue nameCue nameListLayoutInfoPanel DebugDebugLoggingDetails:Details:Logging ErrorErrorLoggingInformation InformationLoggingWarningWarningLogging &About&About MainWindow &Edit&Edit MainWindow &File&File MainWindow&Layout&Layout MainWindow &Tools&Tools MainWindow AboutAbout MainWindowAbout QtAbout Qt MainWindow CTRL+ICTRL+I MainWindowCTRL+SHIFT+A CTRL+SHIFT+A MainWindowCTRL+SHIFT+E CTRL+SHIFT+E MainWindowClose session Close session MainWindowDeselect all Deselect all MainWindow(Discard the changes?Discard the changes? MainWindowEdit selected Edit selected MainWindowEdit selectionEdit selection MainWindowExitExit MainWindowFull Screen Full Screen MainWindow Invert selectionInvert selection MainWindowNew session New session MainWindowOpenOpen MainWindowPreferences Preferences MainWindowRedoRedo MainWindowRedone: Redone:  MainWindowSave asSave as MainWindowSave session Save session MainWindowSelect all Select all MainWindow*Select all media cuesSelect all media cues MainWindowBThe current session is not saved.!The current session is not saved. MainWindowUndoUndo MainWindowUndone: Undone:  MainWindow*Audio cue (from file)Audio cue (from file) MediaCueMenus$Select media filesSelect media files MediaCueMenusLoopLoopMediaCueSettingsVRepetition after first play (-1 = infinite)+Repetition after first play (-1 = infinite)MediaCueSettings6Start position of the mediaStart position of the mediaMediaCueSettingsStart time Start timeMediaCueSettings4Stop position of the mediaStop position of the mediaMediaCueSettingsStop time Stop timeMediaCueSettings(Right click to resetRight click to reset QColorButtonAppearance AppearanceSettingsPageNameCueCueSettingsPageNameCue Settings Cue SettingsSettingsPageNameGeneralGeneralSettingsPageNamelinux-show-player-0.5.1/lisp/i18n/lisp_en.ts000066400000000000000000001040161323636106000206550ustar00rootroot00000000000000 About Authors Authors Contributors Contributors Translators Translators About Linux Show Player About Linux Show Player AboutDialog Linux Show Player is a cue-player designed for stage productions. Linux Show Player is a cue-player designed for stage productions. Web site Web site Users group Users group Source code Source code Info Info License License Contributors Contributors AppGeneralSettings Startup layout Startup layout Use startup dialog Use startup dialog Application theme Application theme AppSettings LiSP preferences LiSP preferences CartLayout Add page Add page Add pages Add pages Remove current page Remove current page Countdown mode Countdown mode Show seek-bars Show seek-bars Show dB-meters Show dB-meters Show volume Show volume Show accurate time Show accurate time Edit cue Edit cue Remove Remove Select Select Play Play Pause Pause Stop Stop Reset volume Reset volume Number of Pages: Number of Pages: Warning Warning Every cue in the page will be lost. Every cue in the page will be lost. Are you sure to continue? Are you sure to continue? Page Page Default behaviors Default behaviors Automatically add new page Automatically add new page Grid size Grid size Number of columns Number of columns Number of rows Number of rows CueAction Default Default Pause Pause Start Start Stop Stop FadeInStart FadeInStart FadeOutStop FadeOutStop FadeOutPause FadeOutPause CueActionLog Cue settings changed: "{}" Cue settings changed: "{}" Cues settings changed. Cues settings changed. CueAppearanceSettings The appearance depends on the layout The appearance depends on the layout Cue name Cue name NoName NoName Description/Note Description/Note Set Font Size Set Font Size Color Color Select background color Select background color Select font color Select font color CueName Media Cue Media Cue CueSettings Pre wait Pre wait Wait before cue execution Wait before cue execution Post wait Post wait Wait after cue execution Wait after cue execution Next action Next action Interrupt Fade Interrupt Fade Fade Action Fade Action Behaviours Behaviours Pre/Post Wait Pre/Post Wait Fade In/Out Fade In/Out Start action Start action Default action to start the cue Default action to start the cue Stop action Stop action Default action to stop the cue Default action to stop the cue Fade Linear Linear Quadratic Quadratic Quadratic2 Quadratic2 FadeEdit Duration (sec) Duration (sec) Curve Curve FadeSettings Fade In Fade In Fade Out Fade Out LayoutDescription Organize cues in grid like pages Organize cues in grid like pages Organize the cues in a list Organize the cues in a list LayoutDetails Click a cue to run it Click a cue to run it SHIFT + Click to edit a cue SHIFT + Click to edit a cue CTRL + Click to select a cue CTRL + Click to select a cue SHIFT + Space or Double-Click to edit a cue SHIFT + Space or Double-Click to edit a cue To copy cues drag them while pressing CTRL To copy cues drag them while pressing CTRL To copy cues drag them while pressing SHIFT To copy cues drag them while pressing SHIFT CTRL + Left Click to select cues CTRL + Left Click to select cues To move cues drag them To move cues drag them LayoutSelect Layout selection Layout selection Select layout Select layout Open file Open file ListLayout Show playing cues Show playing cues Show dB-meters Show dB-meters Show seek-bars Show seek-bars Show accurate time Show accurate time Auto-select next cue Auto-select next cue Edit cue Edit cue Remove Remove Select Select Stop all Stop all Pause all Pause all Restart all Restart all Stop Stop Restart Restart Default behaviors Default behaviors At list end: At list end: Go key: Go key: Interrupt all Interrupt all Fade-Out all Fade-Out all Fade-In all Fade-In all Use fade Use fade Stop Cue Stop Cue Pause Cue Pause Cue Restart Cue Restart Cue Interrupt Cue Interrupt Cue Stop All Stop All Pause All Pause All Restart All Restart All Interrupt All Interrupt All ListLayoutHeader Cue Cue Pre wait Pre wait Action Action Post wait Post wait ListLayoutInfoPanel Cue name Cue name Cue description Cue description Logging Information Information Debug Debug Warning Warning Error Error Details: Details: MainWindow &File &File New session New session Open Open Save session Save session Preferences Preferences Save as Save as Full Screen Full Screen Exit Exit &Edit &Edit Undo Undo Redo Redo Select all Select all Select all media cues Select all media cues Deselect all Deselect all CTRL+SHIFT+A CTRL+SHIFT+A Invert selection Invert selection CTRL+I CTRL+I Edit selected Edit selected CTRL+SHIFT+E CTRL+SHIFT+E &Layout &Layout &Tools &Tools Edit selection Edit selection &About &About About About About Qt About Qt Undone: Undone: Redone: Redone: Close session Close session The current session is not saved. The current session is not saved. Discard the changes? Discard the changes? MediaCueMenus Audio cue (from file) Audio cue (from file) Select media files Select media files MediaCueSettings Start time Start time Stop position of the media Stop position of the media Stop time Stop time Start position of the media Start position of the media Loop Loop Repetition after first play (-1 = infinite) Repetition after first play (-1 = infinite) QColorButton Right click to reset Right click to reset SettingsPageName Appearance Appearance General General Cue Cue Cue Settings Cue Settings linux-show-player-0.5.1/lisp/i18n/lisp_es.qm000066400000000000000000000360141323636106000206530ustar00rootroot00000000000000;ҳ%a n,6.8z0z^'Wn+cM1 %\.0KTH5OW F F#5+ +$rI^- ~'4.a>6W9v:cM9M#Jcs2+R"_Υ Cٰs6 ;r r$wtwt'nʨ#jgs΀#Ύ#ή$, - % >*R S&/, b4 h>)r la+$ ve' v/ W..s N-Z W - O gc gd gd' s> k% t  n3 n a u )* ;  ;  Gu^ bC&] bO& bc& wD wD( 5 $( 0 M@/U E ,u! ,! ,"1 RY./ W[ ^}T5. zt "} 5" 2"  7 ) Īep . Us R Us% P P) _D N  y <6Y 4!_). 6[z ,`gk0X32 i66Acerca de Linux Show PlayerAbout Linux Show PlayerAboutAutoresAuthorsAboutContribuidores ContributorsAboutTraductores TranslatorsAboutContibuidores Contributors AboutDialogInformacinInfo AboutDialogLicenciaLicense AboutDialogLinux Show Player es un reproductor de Cues diseado para producciones escnicasALinux Show Player is a cue-player designed for stage productions. AboutDialogCdigo fuente Source code AboutDialog"Grupo de usuarios Users group AboutDialogSitio WebWeb site AboutDialog*Tema de la aplicacinApplication themeAppGeneralSettings Layout de inicioStartup layoutAppGeneralSettings,Usar dilogo de inicioUse startup dialogAppGeneralSettings(Preferencias de LiSPLiSP preferences AppSettingsAadir pginaAdd page CartLayoutAadir pginas Add pages CartLayout4Est seguro de continuar?Are you sure to continue? CartLayout:Aadir pgina automticamenteAutomatically add new page CartLayout0Modo de cuenta regresivaCountdown mode CartLayout6Comportamientos por defectoDefault behaviors CartLayoutEditar cueEdit cue CartLayoutNTodos los cues en la pgina se perdern#Every cue in the page will be lost. CartLayout(Tamao de cuadrcula Grid size CartLayout$Nmero de pginas:Number of Pages: CartLayout$Nmero de columnasNumber of columns CartLayoutNmero de filasNumber of rows CartLayout PginaPage CartLayout PausaPause CartLayoutReproducirPlay CartLayoutEliminarRemove CartLayout,Eliminar pgina actualRemove current page CartLayout(Reestablecer volumen Reset volume CartLayoutSeleccionarSelect CartLayout,Mostrar tiempo precisoShow accurate time CartLayout.Mostrar medidores de dBShow dB-meters CartLayout2Mostrar barra de bsquedaShow seek-bars CartLayoutMostrar volumen Show volume CartLayoutDetenerStop CartLayoutAdvertenciaWarning CartLayoutPor defectoDefault CueAction PausaPause CueAction InicioStart CueActionDetenerStop CueAction<Ajustes de Cue cambiados: "{}"Cue settings changed: "{}" CueActionLog6Ajustes de Cue han cambiadoCues settings changed. CueActionLog ColorColorCueAppearanceSettingsNombre del CueCue nameCueAppearanceSettings"Descripcin/NotasDescription/NoteCueAppearanceSettingsSin nombreNoNameCueAppearanceSettings:Seleccionar el color de fondoSelect background colorCueAppearanceSettingsBSeleccionar el color de la fuenteSelect font colorCueAppearanceSettingsBEstablecer el tamao de la fuente Set Font SizeCueAppearanceSettings@La apariencia depende del Layout$The appearance depends on the layoutCueAppearanceSettingsCue de Media Media CueCueNameComportamientos Behaviours CueSettingsRAccin predeterminada para iniciar el CueDefault action to start the cue CueSettingsRAccin predeterminada para detener el CueDefault action to stop the cue CueSettings"Accin de Fundido Fade Action CueSettings2Fundido de Ingreso/Salida Fade In/Out CueSettings&Interrumpir FundidoInterrupt Fade CueSettings Siguiente accin Next action CueSettingsPost wait Post wait CueSettingsPre waitPre wait CueSettingsPre/Post Wait Pre/Post Wait CueSettingsIniciar Accin Start action CueSettingsDetener Accin Stop action CueSettingsHWait despus de la ejecucin del cueWait after cue execution CueSettingsDWait antes de la ejecucin del cueWait before cue execution CueSettings LinearLinearFadeCuadrtico QuadraticFadeCuadrtico2 Quadratic2Fade CurvaCurveFadeEditDuracin (seg.)Duration (sec)FadeEdit$Fundido de ingresoFade In FadeSettings"Fundido de salidaFade Out FadeSettingsROrganizar cues en cuadrcula como pginas Organize cues in grid like pagesLayoutDescription6Organizar cues en una listaOrganize the cues in a listLayoutDescriptionHCTRL + Click para seleccionar un cueCTRL + Click to select a cue LayoutDetailsXCTRL + Click Izquierdo para seleccionar Cues CTRL + Left Click to select cues LayoutDetailsJHacer click en un cue para ejecutarloClick a cue to run it LayoutDetails@SHIFT + Click para editar un cueSHIFT + Click to edit a cue LayoutDetailstSHIFT + Barra espaciadora o Doble click para editar un cue+SHIFT + Space or Double-Click to edit a cue LayoutDetailsfPara copiar cues arrstrelos mientras presiona CTRL*To copy cues drag them while pressing CTRL LayoutDetailshPara copiar Cues arrstrelos mientras presiona SHIFT+To copy cues drag them while pressing SHIFT LayoutDetails6Para mover Cues arrstrelosTo move cues drag them LayoutDetails&Seleccin de LayoutLayout selection LayoutSelectAbrir archivo Open file LayoutSelect$Seleccionar Layout Select layout LayoutSelect*Al final de la lista: At list end: ListLayoutXSeleccionar automticamente el siguiente cueAuto-select next cue ListLayout4Comportamiento por defectoDefault behaviors ListLayoutEditar cueEdit cue ListLayout8Fundido de Entrada para Todo Fade-In all ListLayout6Fundido de Salida para Todo Fade-Out all ListLayoutTecla de GOGo key: ListLayout Interrumpir todo Interrupt All ListLayoutInterrumpir Cue Interrupt Cue ListLayout Interrumpir todo Interrupt all ListLayoutPausar Todo Pause All ListLayoutPausar Cue Pause Cue ListLayoutPausar todo Pause all ListLayoutEliminarRemove ListLayoutReiniciarRestart ListLayoutReiniciar Todo Restart All ListLayoutReiniciar Cue Restart Cue ListLayoutReiniciar todo Restart all ListLayoutSeleccionarSelect ListLayout,Mostrar tiempo precisoShow accurate time ListLayout*Mostrar medidor de dBShow dB-meters ListLayout:Mostrar los cues en ejecucinShow playing cues ListLayout4Mostrar barras de bsquedaShow seek-bars ListLayoutDetenerStop ListLayoutDetener TodoStop All ListLayoutDetener CueStop Cue ListLayoutDetener todoStop all ListLayoutUsar fundidoUse fade ListLayout AccinActionListLayoutHeaderCueCueListLayoutHeaderPost wait Post waitListLayoutHeaderPre waitPre waitListLayoutHeader&Descripcin del cueCue descriptionListLayoutInfoPanelNombre del CueCue nameListLayoutInfoPanelDepurarDebugLoggingDetalles:Details:Logging ErrorErrorLoggingInformacin InformationLoggingAdvertenciaWarningLogging&Acerca&About MainWindow&Editar&Edit MainWindow&Archivo&File MainWindow&Layout&Layout MainWindow&Herramientas&Tools MainWindow AcercaAbout MainWindowAcerca de QTAbout Qt MainWindow CTRL+ICTRL+I MainWindowCTRL+SHIFT+A CTRL+SHIFT+A MainWindowCTRL+SHIFT+E CTRL+SHIFT+E MainWindowCerrar sesin Close session MainWindow$Deseleccionar todo Deselect all MainWindow&Descartar cambios?Discard the changes? MainWindow&Editar seleccionado Edit selected MainWindow Editar seleccinEdit selection MainWindow SalirExit MainWindow"Pantalla completa Full Screen MainWindow$Invertir seleccinInvert selection MainWindowNueva sesin New session MainWindow AbrirOpen MainWindowPreferencias Preferences MainWindowRehacerRedo MainWindowRehacerRedone:  MainWindowGuardar comoSave as MainWindowGuardar sesin Save session MainWindow Seleccionar todo Select all MainWindow@Seleccionar todos los Media cuesSelect all media cues MainWindowFLa seccin actual no se ha guardado!The current session is not saved. MainWindowDeshacerUndo MainWindowDeshacerUndone:  MainWindow8Cue de audio (desde archivo)Audio cue (from file) MediaCueMenus<Seleccionar archivos de mediosSelect media files MediaCueMenus BucleLoopMediaCueSettingsxRepeticiones despues de la primera ejecucin (-1 = infinito)+Repetition after first play (-1 = infinite)MediaCueSettings8Posicin de inicio del mediaStart position of the mediaMediaCueSettings Tiempo de inicio Start timeMediaCueSettings>Posicin de detencin del mediaStop position of the mediaMediaCueSettings&Tiempo de detencin Stop timeMediaCueSettings8Click derecho para reiniciarRight click to reset QColorButtonApariencia AppearanceSettingsPageNameCueCueSettingsPageNameAjustes de Cue Cue SettingsSettingsPageNameGeneralGeneralSettingsPageNamelinux-show-player-0.5.1/lisp/i18n/lisp_es.ts000066400000000000000000001054711323636106000206700ustar00rootroot00000000000000 About Authors Autores Contributors Contribuidores Translators Traductores About Linux Show Player Acerca de Linux Show Player AboutDialog Linux Show Player is a cue-player designed for stage productions. Linux Show Player es un reproductor de Cues diseñado para producciones escénicas Web site Sitio Web Users group Grupo de usuarios Source code Código fuente Info Información License Licencia Contributors Contibuidores AppGeneralSettings Startup layout Layout de inicio Use startup dialog Usar diálogo de inicio Application theme Tema de la aplicación AppSettings LiSP preferences Preferencias de LiSP CartLayout Add page Añadir página Add pages Añadir páginas Remove current page Eliminar página actual Countdown mode Modo de cuenta regresiva Show seek-bars Mostrar barra de búsqueda Show dB-meters Mostrar medidores de dB Show volume Mostrar volumen Show accurate time Mostrar tiempo preciso Edit cue Editar cue Remove Eliminar Select Seleccionar Play Reproducir Pause Pausa Stop Detener Reset volume Reestablecer volumen Number of Pages: Número de páginas: Warning Advertencia Every cue in the page will be lost. Todos los cues en la página se perderán Are you sure to continue? ¿Está seguro de continuar? Page Página Default behaviors Comportamientos por defecto Automatically add new page Añadir página automáticamente Grid size Tamaño de cuadrícula Number of columns Número de columnas Number of rows Número de filas CueAction Default Por defecto Pause Pausa Start Inicio Stop Detener FadeInStart FadeOutStop FadeOutPause CueActionLog Cue settings changed: "{}" Ajustes de Cue cambiados: "{}" Cues settings changed. Ajustes de Cue han cambiado CueAppearanceSettings The appearance depends on the layout La apariencia depende del Layout Cue name Nombre del Cue NoName Sin nombre Description/Note Descripción/Notas Set Font Size Establecer el tamaño de la fuente Color Color Select background color Seleccionar el color de fondo Select font color Seleccionar el color de la fuente CueName Media Cue Cue de Media CueSettings Pre wait Pre wait Wait before cue execution Wait antes de la ejecución del cue Post wait Post wait Wait after cue execution Wait después de la ejecución del cue Next action Siguiente acción Interrupt Fade Interrumpir Fundido Fade Action Acción de Fundido Behaviours Comportamientos Pre/Post Wait Pre/Post Wait Fade In/Out Fundido de Ingreso/Salida Start action Iniciar Acción Default action to start the cue Acción predeterminada para iniciar el Cue Stop action Detener Acción Default action to stop the cue Acción predeterminada para detener el Cue Fade Linear Linear Quadratic Cuadrático Quadratic2 Cuadrático2 FadeEdit Duration (sec) Duración (seg.) Curve Curva FadeSettings Fade In Fundido de ingreso Fade Out Fundido de salida LayoutDescription Organize cues in grid like pages Organizar cues en cuadrícula como páginas Organize the cues in a list Organizar cues en una lista LayoutDetails Click a cue to run it Hacer click en un cue para ejecutarlo SHIFT + Click to edit a cue SHIFT + Click para editar un cue CTRL + Click to select a cue CTRL + Click para seleccionar un cue SHIFT + Space or Double-Click to edit a cue SHIFT + Barra espaciadora o Doble click para editar un cue To copy cues drag them while pressing CTRL Para copiar cues arrástrelos mientras presiona CTRL To copy cues drag them while pressing SHIFT Para copiar Cues arrástrelos mientras presiona SHIFT CTRL + Left Click to select cues CTRL + Click Izquierdo para seleccionar Cues To move cues drag them Para mover Cues arrástrelos LayoutSelect Layout selection Selección de Layout Select layout Seleccionar Layout Open file Abrir archivo ListLayout Show playing cues Mostrar los cues en ejecución Show dB-meters Mostrar medidor de dB Show seek-bars Mostrar barras de búsqueda Show accurate time Mostrar tiempo preciso Auto-select next cue Seleccionar automáticamente el siguiente cue Edit cue Editar cue Remove Eliminar Select Seleccionar Stop all Detener todo Pause all Pausar todo Restart all Reiniciar todo Stop Detener Restart Reiniciar Default behaviors Comportamiento por defecto At list end: Al final de la lista: Go key: Tecla de GO Interrupt all Interrumpir todo Fade-Out all Fundido de Salida para Todo Fade-In all Fundido de Entrada para Todo Use fade Usar fundido Stop Cue Detener Cue Pause Cue Pausar Cue Restart Cue Reiniciar Cue Interrupt Cue Interrumpir Cue Stop All Detener Todo Pause All Pausar Todo Restart All Reiniciar Todo Interrupt All Interrumpir todo ListLayoutHeader Cue Cue Pre wait Pre wait Action Acción Post wait Post wait ListLayoutInfoPanel Cue name Nombre del Cue Cue description Descripción del cue Logging Information Información Debug Depurar Warning Advertencia Error Error Details: Detalles: MainWindow &File &Archivo New session Nueva sesión Open Abrir Save session Guardar sesión Preferences Preferencias Save as Guardar como Full Screen Pantalla completa Exit Salir &Edit &Editar Undo Deshacer Redo Rehacer Select all Seleccionar todo Select all media cues Seleccionar todos los Media cues Deselect all Deseleccionar todo CTRL+SHIFT+A CTRL+SHIFT+A Invert selection Invertir selección CTRL+I CTRL+I Edit selected Editar seleccionado CTRL+SHIFT+E CTRL+SHIFT+E &Layout &Layout &Tools &Herramientas Edit selection Editar selección &About &Acerca About Acerca About Qt Acerca de QT Undone: Deshacer Redone: Rehacer Close session Cerrar sesión The current session is not saved. La sección actual no se ha guardado Discard the changes? ¿Descartar cambios? MediaCueMenus Audio cue (from file) Cue de audio (desde archivo) Select media files Seleccionar archivos de medios MediaCueSettings Start time Tiempo de inicio Stop position of the media Posición de detención del media Stop time Tiempo de detención Start position of the media Posición de inicio del media Loop Bucle Repetition after first play (-1 = infinite) Repeticiones despues de la primera ejecución (-1 = infinito) QColorButton Right click to reset Click derecho para reiniciar SettingsPageName Appearance Apariencia General General Cue Cue Cue Settings Ajustes de Cue linux-show-player-0.5.1/lisp/i18n/lisp_fr.qm000066400000000000000000000371721323636106000206610ustar00rootroot00000000000000C)4.D-D-pT'>ҳ'q n.:6.zz^)qn-M3 '\.0K;TH5W[F F%++ +&tI^0 ~'76.a>W9v:cw 9M#cs4R_Υ `Cjٰs8Z;FQ& r 9r&wtwt*ʨ%^ks9΀%Ύ%ή&, - % >*R S&/. b6z h>+ la-H ve)( v1 W.0 N/ W 1 -9 _ gc0 gd gd) s> k%{ t   nc n" u ; ), ;  ;  Gul bC(g bO( bc( wD wD* 7 $ 2o M@1 E ) ,u# ,#O ,# RY.2 W[t ^}T7| zt^ $7 5$ 2$  7 += Īe 1( Us Us' P P+ _D   y <8 4\#)0V6z  .g>k2328i8: propos de Linux Show PlayerAbout Linux Show PlayerAboutAuteursAuthorsAboutContributeurs ContributorsAboutTraducteurs TranslatorsAboutContributeurs Contributors AboutDialogInfoInfo AboutDialogLicenceLicense AboutDialogLinux Show Player est un logiciel de lecture de cue pens pour le thtre ou l'vnementiel.ALinux Show Player is a cue-player designed for stage productions. AboutDialogCode source Source code AboutDialog*Groupe d'utilisateurs Users group AboutDialogSite webWeb site AboutDialog,Thme de l'applicationApplication themeAppGeneralSettings"tat de dmarrageStartup layoutAppGeneralSettingsBUtilisez le dialogue du dmarrageUse startup dialogAppGeneralSettings&Prfrences de LiSPLiSP preferences AppSettings Ajouter une pageAdd page CartLayout"Ajouter des pages Add pages CartLayout@Voulez-vous vraiment continuer ?Are you sure to continue? CartLayout@Ajouter une page automatiquementAutomatically add new page CartLayout"Compte rebours Countdown mode CartLayout2Comportements par dfaut Default behaviors CartLayoutditer la cueEdit cue CartLayoutXToutes les cues dans la pages seront perdues#Every cue in the page will be lost. CartLayout&Taille de la grille Grid size CartLayout"Nombre de pages :Number of Pages: CartLayout$Nombre de colonnesNumber of columns CartLayout Nombre de lignesNumber of rows CartLayoutPagePage CartLayout PausePause CartLayoutLecturePlay CartLayoutRetirerRemove CartLayout0Retirer la page actuelleRemove current page CartLayout4Rinitialisation du volume Reset volume CartLayoutSlectionnerSelect CartLayout.Afficher le temps exactShow accurate time CartLayout4Afficher le mesureur de dBShow dB-meters CartLayout:Montrer le barre de rechercheShow seek-bars CartLayout$Afficher le volume Show volume CartLayoutStopStop CartLayoutAttentionWarning CartLayout DfautDefault CueAction6DmarrageDuFondulOuverture FadeInStart CueAction0PauseduFondulaFermeture FadeOutPause CueAction0ArrtduFondulaFermeture FadeOutStop CueAction PausePause CueActionDmarrerStart CueActionStopStop CueActionBParamtres de cue modifis : "{}"Cue settings changed: "{}" CueActionLog8Paramtres de cues modifis.Cues settings changed. CueActionLogCouleurColorCueAppearanceSettingsNom de la cueCue nameCueAppearanceSettings$Description / noteDescription/NoteCueAppearanceSettingsPas de nomNoNameCueAppearanceSettings>Slectionner la couleur de fondSelect background colorCueAppearanceSettingsHSlectionner la couleur de la policeSelect font colorCueAppearanceSettings<Dfinir la taille de la police Set Font SizeCueAppearanceSettingsDL'apparence dpend de l'agencement$The appearance depends on the layoutCueAppearanceSettingsCue de mdia Media CueCueNameComportement Behaviours CueSettingsLAction par dfaut pour dmarrer le cueDefault action to start the cue CueSettingsJAction par dfaut pour arrter le cueDefault action to stop the cue CueSettingsAction de fondu Fade Action CueSettings:Fondu l'ouverture/fermeture Fade In/Out CueSettings(Interrompre le fonduInterrupt Fade CueSettings Prochaine action Next action CueSettingsPost-attente Post wait CueSettingsPr-attentePre wait CueSettings Attente pr/post Pre/Post Wait CueSettingsLancer l'action Start action CueSettings Arrter l'action Stop action CueSettingsFAttente aprs l'excution de la cueWait after cue execution CueSettingsFAttente avant l'excution de la cueWait before cue execution CueSettingsLinaireLinearFadeDu second degr QuadraticFade"Du second degr 2 Quadratic2Fade CourbeCurveFadeEditDure (sec)Duration (sec)FadeEdit&Fondu l'ouvertureFade In FadeSettings(Fondu la fermetureFade Out FadeSettingsXOrganiser les cues en grille comme les pages Organize cues in grid like pagesLayoutDescriptionBOrganiser les cues dans une listeOrganize the cues in a listLayoutDescriptionJCTRL + clic pour slectionner une cueCTRL + Click to select a cue LayoutDetailsZCTRL + clic-gauche pour slectionner les cues CTRL + Left Click to select cues LayoutDetailsFCliquer sur une cue pour l'excuterClick a cue to run it LayoutDetails<MAJ + clic pour diter une cueSHIFT + Click to edit a cue LayoutDetails^MAJ + Espace ou double-clic pour diter une cue+SHIFT + Space or Double-Click to edit a cue LayoutDetailsdPour copier les cues, glissez-les en pressant CTRL*To copy cues drag them while pressing CTRL LayoutDetailsfPour copier les cues, glissez-les en pressant SHIFT+To copy cues drag them while pressing SHIFT LayoutDetailsHPour dplacer des cues, dplacez-lesTo move cues drag them LayoutDetails2Slection de l'agencementLayout selection LayoutSelect"Ouvrir un fichier Open file LayoutSelect2Slectionner l'agencement Select layout LayoutSelect, la fin de la liste : At list end: ListLayoutDAuto-slectionner la prochaine cueAuto-select next cue ListLayout2Comportements par dfaut Default behaviors ListLayoutditer la cueEdit cue ListLayout2Tout fondre l'ouverture Fade-In all ListLayout4Tout fondre la fermeture Fade-Out all ListLayout(Touche pour lancer :Go key: ListLayout Tout interrompre Interrupt All ListLayout$Interrompre la cue Interrupt Cue ListLayout Tout interrompre Interrupt all ListLayout(Tout mettre en pause Pause All ListLayout,Mettre la cue en pause Pause Cue ListLayout(Tout mettre en pause Pause all ListLayoutRetirerRemove ListLayoutRedmarrerRestart ListLayoutTout redmarrer Restart All ListLayout"Redmarrer la cue Restart Cue ListLayoutTout redmarrer Restart all ListLayoutSlectionnerSelect ListLayout.Afficher le temps exactShow accurate time ListLayout4Afficher le mesureur de dBShow dB-meters ListLayout2Montrer les cues en coursShow playing cues ListLayout<Afficher la barre de rechercheShow seek-bars ListLayoutStopStop ListLayoutTout arrterStop All ListLayoutArrter la cueStop Cue ListLayoutTout stopperStop all ListLayout"Utiliser le fonduUse fade ListLayout ActionActionListLayoutHeaderCueCueListLayoutHeaderPost-attente Post waitListLayoutHeaderPr-attentePre waitListLayoutHeader*Description de la cueCue descriptionListLayoutInfoPanelNom de la cueCue nameListLayoutInfoPanelDboguageDebugLoggingDtails :Details:Logging ErreurErrorLoggingInformations InformationLoggingAttentionWarningLogging &propos&About MainWindowdit&er&Edit MainWindow&Fichier&File MainWindow &tat&Layout MainWindowOu&tils&Tools MainWindow proposAbout MainWindow propos de QtAbout Qt MainWindow CTRL+ICTRL+I MainWindowCTRL+MAJ+A CTRL+SHIFT+A MainWindowCTRL+MAJ+E CTRL+SHIFT+E MainWindow"Fermer la session Close session MainWindow(Tout dslectionner  Deselect all MainWindow2Annuler les changements ?Discard the changes? MainWindow&diter la slection Edit selected MainWindow&diter la slectionEdit selection MainWindowQuitterExit MainWindowPlein cran Full Screen MainWindow*Inverser la slectionInvert selection MainWindow Nouvelle session New session MainWindow OuvrirOpen MainWindowPrfrences Preferences MainWindowRefaireRedo MainWindowRefaire :Redone:  MainWindow Sauvegarder sousSave as MainWindow,Sauvegarder la session Save session MainWindow"Tout slectionner Select all MainWindowDSlectionner toutes les cues mdiaSelect all media cues MainWindowRLa session actuelle n'est pas sauvegarde!The current session is not saved. MainWindowAnnulerUndo MainWindowDfaire :Undone:  MainWindow:Cue audio (depuis un fichier)Audio cue (from file) MediaCueMenus@Slectionner des fichiers mdiasSelect media files MediaCueMenus BoucleLoopMediaCueSettingsfRptition aprs la premire lecture (-1 = infinie)+Repetition after first play (-1 = infinite)MediaCueSettings<Position de dmarrage du mdiaStart position of the mediaMediaCueSettingsTemps de dpart Start timeMediaCueSettings0Position de fin du mdiaStop position of the mediaMediaCueSettingsTemps de fin Stop timeMediaCueSettings:Clic-droit pour rinitialiserRight click to reset QColorButtonApparence AppearanceSettingsPageNameCueCueSettingsPageName"Paramtres de cue Cue SettingsSettingsPageNameGnraleGeneralSettingsPageNamelinux-show-player-0.5.1/lisp/i18n/lisp_fr.ts000066400000000000000000001062261323636106000206670ustar00rootroot00000000000000 About Authors Auteurs Contributors Contributeurs Translators Traducteurs About Linux Show Player À propos de Linux Show Player AboutDialog Linux Show Player is a cue-player designed for stage productions. Linux Show Player est un logiciel de lecture de cue conçu pour le théâtre ou l'événementiel. Web site Site web Users group Groupe d'utilisateurs Source code Code source Info Info License Licence Contributors Contributeurs AppGeneralSettings Startup layout État de démarrage Use startup dialog Utilisez le dialogue du démarrage Application theme Thème de l'application AppSettings LiSP preferences Préférences de LiSP CartLayout Add page Ajouter une page Add pages Ajouter des pages Remove current page Retirer la page actuelle Countdown mode Compte à rebours Show seek-bars Montrer les barres de position Show dB-meters Afficher le mesureur de dB Show volume Afficher le volume Show accurate time Afficher le temps exact Edit cue Éditer la cue Remove Retirer Select Sélectionner Play Lecture Pause Pause Stop Stop Reset volume Réinitialisation du volume Number of Pages: Nombre de pages : Warning Attention Every cue in the page will be lost. Toutes les cues dans la pages seront perdues Are you sure to continue? Voulez-vous vraiment continuer ? Page Page Default behaviors Comportements par défaut Automatically add new page Ajouter une page automatiquement Grid size Taille de la grille Number of columns Nombre de colonnes Number of rows Nombre de lignes CueAction Default Défaut Pause Pause Start Démarrer Stop Stop FadeInStart DémarrageDuFonduÀlOuverture FadeOutStop ArrêtduFonduÀlaFermeture FadeOutPause PauseduFonduÀlaFermeture CueActionLog Cue settings changed: "{}" Paramètres de cue modifiés : "{}" Cues settings changed. Paramètres de cues modifiés. CueAppearanceSettings The appearance depends on the layout L'apparence dépend de l'agencement Cue name Nom de la cue NoName Pas de nom Description/Note Description / note Set Font Size Définir la taille de la police Color Couleur Select background color Sélectionner la couleur de fond Select font color Sélectionner la couleur de la police CueName Media Cue Cue de média CueSettings Pre wait Pré-attente Wait before cue execution Attente avant l'exécution de la cue Post wait Post-attente Wait after cue execution Attente après l'exécution de la cue Next action Prochaine action Interrupt Fade Interrompre le fondu Fade Action Action de fondu Behaviours Comportement Pre/Post Wait Attente pré/post Fade In/Out Fondu à l'ouverture/fermeture Start action Lancer l'action Default action to start the cue Action par défaut pour démarrer la cue Stop action Arrêter l'action Default action to stop the cue Action par défaut pour arrêter la cue Fade Linear Linéaire Quadratic Du second degré Quadratic2 Du second degré 2 FadeEdit Duration (sec) Durée (sec) Curve Courbe FadeSettings Fade In Fondu à l'ouverture Fade Out Fondu à la fermeture LayoutDescription Organize cues in grid like pages Organiser les cues en grille comme les pages Organize the cues in a list Organiser les cues dans une liste LayoutDetails Click a cue to run it Cliquer sur une cue pour l'exécuter SHIFT + Click to edit a cue MAJ + clic pour éditer une cue CTRL + Click to select a cue CTRL + clic pour sélectionner une cue SHIFT + Space or Double-Click to edit a cue MAJ + Espace ou double-clic pour éditer une cue To copy cues drag them while pressing CTRL Pour copier les cues, glissez-les en pressant CTRL To copy cues drag them while pressing SHIFT Pour copier les cues, glissez-les en pressant SHIFT CTRL + Left Click to select cues CTRL + clic-gauche pour sélectionner les cues To move cues drag them Pour déplacer des cues, déplacez-les LayoutSelect Layout selection Sélection de l'agencement Select layout Sélectionner l'agencement Open file Ouvrir un fichier ListLayout Show playing cues Montrer les cues en cours Show dB-meters Afficher le mesureur de dB Show seek-bars Montrer les barres de position Show accurate time Afficher le temps exact Auto-select next cue Auto-sélectionner la prochaine cue Edit cue Éditer la cue Remove Retirer Select Sélectionner Stop all Tout stopper Pause all Tout mettre en pause Restart all Tout redémarrer Stop Stop Restart Redémarrer Default behaviors Comportements par défaut At list end: À la fin de la liste : Go key: Touche pour lancer : Interrupt all Tout interrompre Fade-Out all Tout fondre à la fermeture Fade-In all Tout fondre à l'ouverture Use fade Utiliser le fondu Stop Cue Arrêter la cue Pause Cue Mettre la cue en pause Restart Cue Redémarrer la cue Interrupt Cue Interrompre la cue Stop All Tout arrêter Pause All Tout mettre en pause Restart All Tout redémarrer Interrupt All Tout interrompre ListLayoutHeader Cue Cue Pre wait Pré-attente Action Action Post wait Post-attente ListLayoutInfoPanel Cue name Nom de la cue Cue description Description de la cue Logging Information Informations Debug Déboguage Warning Attention Error Erreur Details: Détails : MainWindow &File &Fichier New session Nouvelle session Open Ouvrir Save session Sauvegarder la session Preferences Préférences Save as Sauvegarder sous Full Screen Plein écran Exit Quitter &Edit Édit&er Undo Annuler Redo Refaire Select all Tout sélectionner Select all media cues Sélectionner toutes les cues média Deselect all Tout désélectionner CTRL+SHIFT+A CTRL+MAJ+A Invert selection Inverser la sélection CTRL+I CTRL+I Edit selected Éditer la sélection CTRL+SHIFT+E CTRL+MAJ+E &Layout É&tat &Tools Ou&tils Edit selection Éditer la sélection &About À &propos About À propos About Qt À propos de Qt Undone: Défaire : Redone: Refaire : Close session Fermer la session The current session is not saved. La session actuelle n'est pas sauvegardée Discard the changes? Annuler les changements ? MediaCueMenus Audio cue (from file) Cue audio (depuis un fichier) Select media files Sélectionner des fichiers médias MediaCueSettings Start time Temps de départ Stop position of the media Position de fin du média Stop time Temps de fin Start position of the media Position de démarrage du média Loop Boucle Repetition after first play (-1 = infinite) Répétition après la première lecture (-1 = infinie) QColorButton Right click to reset Clic-droit pour réinitialiser SettingsPageName Appearance Apparence General Générale Cue Cue Cue Settings Paramètres de cue linux-show-player-0.5.1/lisp/i18n/lisp_it.qm000066400000000000000000000361521323636106000206630ustar00rootroot00000000000000ҳ% n,<6.zz^'sn+M1 a%\.0K_TH5WF F#q+ z+$I^. ~'5.a>W9qv:c O9M#cs29R_Υ Cٰs6L;@Q& r r$wtBwt(Pʨ#esM΀$Ύ#ή$\ - % >*R S&/, b4 h>) la+: ve'. v/ W.. N- W - ) gc gd gd' s> k% t o  n n u )* ;  ;  GuB bC&{ bO& bc& wDS wD( 5 $ 05 M@/y E ,u" ,! ,"e RY./ W[ ^}T5V ztj " 5" 2#- # 7' )+ Īe^ / Us Us%2 P 5 P) _D  7 ye <6 4z!).J6z ,gk0|3z2(6i64Riguardo Linux Show PlayerAbout Linux Show PlayerAbout AutoriAuthorsAboutContributori ContributorsAboutTraduttori TranslatorsAboutContributori Contributors AboutDialogInformazioniInfo AboutDialogLicenzaLicense AboutDialogLinux Show Player un cue-player pensato per produzioni teatrali.ALinux Show Player is a cue-player designed for stage productions. AboutDialogCodice sorgente Source code AboutDialog*Gruppo di discussione Users group AboutDialogSito webWeb site AboutDialog$Stile applicazioneApplication themeAppGeneralSettingsLayout inizialeStartup layoutAppGeneralSettings&Usa layout inizialeUse startup dialogAppGeneralSettings$Preferenze di LiSPLiSP preferences AppSettingsAggungi paginaAdd page CartLayoutAggiungi pagine Add pages CartLayout>Sei sicuro di voler continuare?Are you sure to continue? CartLayoutJAggiungi automaticamente nuove pagineAutomatically add new page CartLayout$Modalit countdownCountdown mode CartLayout2Comportamenti predefinitiDefault behaviors CartLayoutModifica CueEdit cue CartLayoutBOgni cue nella pagina sar persa.#Every cue in the page will be lost. CartLayout$Dimensione griglia Grid size CartLayout"Numero di Pagine:Number of Pages: CartLayout"Numero di colonneNumber of columns CartLayoutNumero di righeNumber of rows CartLayout PaginaPage CartLayout PausaPause CartLayoutRiproduciPlay CartLayoutRimuoviRemove CartLayout.Rimuovi pagina correnteRemove current page CartLayoutResetta volume Reset volume CartLayoutSelezionaSelect CartLayout*Mostra tempo accuratoShow accurate time CartLayout(Mostra indicatori dBShow dB-meters CartLayout6Mostra barre di avanzamentoShow seek-bars CartLayoutMostra volume Show volume CartLayout FermaStop CartLayoutAttenzioneWarning CartLayoutAutomaticaDefault CueAction*Avvio con Dissolvenza FadeInStart CueAction*Pausa con Dissolvenza FadeOutPause CueAction*Ferma con Dissolvenza FadeOutStop CueAction PausaPause CueAction AvviaStart CueAction FermaStop CueAction<Impostazioni cue cambiate "{}"Cue settings changed: "{}" CueActionLogBImpostazioni di pi cue cambiate.Cues settings changed. CueActionLog ColoreColorCueAppearanceSettingsNome della cueCue nameCueAppearanceSettings Descrizione/NoteDescription/NoteCueAppearanceSettings NoNameNoNameCueAppearanceSettings4Selezione colore di sfondoSelect background colorCueAppearanceSettings<Seleziona colore del carattereSelect font colorCueAppearanceSettings(Dimensione carattere Set Font SizeCueAppearanceSettings8L'aspetto dipende dal layout$The appearance depends on the layoutCueAppearanceSettings Cue Multimediale Media CueCueNameComportamenti Behaviours CueSettingsFAzione standard per avviare la cue Default action to start the cue CueSettingsJAzione standard per arrestare la cue Default action to stop the cue CueSettings2Operazioni di Dissolvenza Fade Action CueSettings6Dissolvenza Ingresso/Uscita Fade In/Out CueSettings0Dissolvenza InterruzioniInterrupt Fade CueSettings"Azione successiva Next action CueSettingsPost wait Post wait CueSettingsPre waitPre wait CueSettings"Attesa Prima/Dopo Pre/Post Wait CueSettingsAzione d'avvio Start action CueSettings"Azione di arresto Stop action CueSettingsDAttesa dopo l'esecuzione della cueWait after cue execution CueSettingsLAttesa prima dell'esecuzione della cueWait before cue execution CueSettingsLineareLinearFadeQuadratica QuadraticFadeQuadratica2 Quadratic2Fade CurvaCurveFadeEditDurata (sec)Duration (sec)FadeEdit(Dissolvenza IngressoFade In FadeSettings$Dissolvenza UscitaFade Out FadeSettingsVOrganizza le cue in pagine simili a tabelle Organize cues in grid like pagesLayoutDescription:Organizza le cue in una listaOrganize the cues in a listLayoutDescriptionHCTRL + Click per selezionare una cueCTRL + Click to select a cue LayoutDetailsZCTRL + Click Sinistro per selezionare una cue CTRL + Left Click to select cues LayoutDetails:Clicca una cue per eseguirla Click a cue to run it LayoutDetailsHSHIFT + Click per modificare una cueSHIFT + Click to edit a cue LayoutDetailsfSHIFT + Spazio o Doppio Click per modifcare una cue+SHIFT + Space or Double-Click to edit a cue LayoutDetails^Per copiare le cue trascinale mentre premi CTRL*To copy cues drag them while pressing CTRL LayoutDetails`Per copiare le cue trascinale mentre premi SHIFT+To copy cues drag them while pressing SHIFT LayoutDetails>Per spostare una cue trascinalaTo move cues drag them LayoutDetails(Selezione del layoutLayout selection LayoutSelectApri file Open file LayoutSelect Seleziona layout Select layout LayoutSelectA fine lista: At list end: ListLayout6Auto-seleziona prossima cueAuto-select next cue ListLayout2Comportamenti predefinitiDefault behaviors ListLayoutModifica CueEdit cue ListLayout.Sfuma tutte in ingresso Fade-In all ListLayout*Sfuma tutte in uscita Fade-Out all ListLayoutTasto "Go":Go key: ListLayout Interrompi Tutte Interrupt All ListLayoutInterrompi Cue Interrupt Cue ListLayout$Interrompiti tutte Interrupt all ListLayoutPausa Tutte Pause All ListLayoutPausa Cue Pause Cue ListLayoutSospendi tutte Pause all ListLayoutRimuoviRemove ListLayoutRiavviaRestart ListLayoutRiavvia Tutte Restart All ListLayoutRiavvia Cue Restart Cue ListLayoutRiavvia tutte Restart all ListLayoutSelezionaSelect ListLayout*Mostra tempo accuratoShow accurate time ListLayout(Mostra indicatori dBShow dB-meters ListLayout4Mostra cue in riproduzioneShow playing cues ListLayout6Mostra barre di avanzamentoShow seek-bars ListLayout FermaStop ListLayoutFerma TutteStop All ListLayoutFerma CueStop Cue ListLayoutFerma tutteStop all ListLayoutUsa dissolvenzaUse fade ListLayout AzioneActionListLayoutHeaderCueCueListLayoutHeaderPost wait Post waitListLayoutHeaderPre waitPre waitListLayoutHeader(Descrizone della cueCue descriptionListLayoutInfoPanelNome della cueCue nameListLayoutInfoPanel DebugDebugLoggingDettagli:Details:Logging ErroreErrorLoggingInformazione InformationLogging AvvisoWarningLogging &About&About MainWindow&Modifica&Edit MainWindow &File&File MainWindow&Layout&Layout MainWindow&Strumenti&Tools MainWindowInformazioniAbout MainWindow$Informazioni su QtAbout Qt MainWindow CTRL+ICTRL+I MainWindowCTRL+SHIFT+A CTRL+SHIFT+A MainWindowCTRL+SHIFT+E CTRL+SHIFT+E MainWindowChiudi sessione Close session MainWindow"Deseleziona tutti Deselect all MainWindow,Scartare la modifiche?Discard the changes? MainWindow(Modifica selezionati Edit selected MainWindow&Modifica slezionatiEdit selection MainWindowEsciExit MainWindowSchermo Intero Full Screen MainWindow"Inverti selezioneInvert selection MainWindowNuova sessione New session MainWindowApriOpen MainWindowPreferenze Preferences MainWindow RipetiRedo MainWindowRipetuto: Redone:  MainWindowSalva comeSave as MainWindowSalva sessione Save session MainWindowSeleziona tutti Select all MainWindow8Seleziona tutte le media-cueSelect all media cues MainWindowFLa sessione corrente non salvata.!The current session is not saved. MainWindowAnnullaUndo MainWindowAnnullato: Undone:  MainWindow&Cue audio (da file)Audio cue (from file) MediaCueMenus6Seleziona file multimedialiSelect media files MediaCueMenusLoopLoopMediaCueSettingslRipetizioni dopo la prima riproduzione (-1 = infinite)+Repetition after first play (-1 = infinite)MediaCueSettingsPPosizione da cui la traccia deve partireStart position of the mediaMediaCueSettings$Posizione iniziale Start timeMediaCueSettingsVPosizione in cui la traccia si deve fermareStop position of the mediaMediaCueSettings Posizione finale Stop timeMediaCueSettingsVClicca con il pulsante destro per resettareRight click to reset QColorButtonAspetto AppearanceSettingsPageNameCueCueSettingsPageName Impostazioni Cue Cue SettingsSettingsPageNameGeneraleGeneralSettingsPageNamelinux-show-player-0.5.1/lisp/i18n/lisp_it.ts000066400000000000000000001053621323636106000206740ustar00rootroot00000000000000 About Authors Autori Contributors Contributori Translators Traduttori About Linux Show Player Riguardo Linux Show Player AboutDialog Linux Show Player is a cue-player designed for stage productions. Linux Show Player è un cue-player pensato per produzioni teatrali. Web site Sito web Users group Gruppo di discussione Source code Codice sorgente Info Informazioni License Licenza Contributors Contributori AppGeneralSettings Startup layout Layout iniziale Use startup dialog Usa layout iniziale Application theme Stile applicazione AppSettings LiSP preferences Preferenze di LiSP CartLayout Add page Aggungi pagina Add pages Aggiungi pagine Remove current page Rimuovi pagina corrente Countdown mode Modalità countdown Show seek-bars Mostra barre di avanzamento Show dB-meters Mostra indicatori dB Show volume Mostra volume Show accurate time Mostra tempo accurato Edit cue Modifica Cue Remove Rimuovi Select Seleziona Play Riproduci Pause Pausa Stop Ferma Reset volume Resetta volume Number of Pages: Numero di Pagine: Warning Attenzione Every cue in the page will be lost. Ogni cue nella pagina sarà persa. Are you sure to continue? Sei sicuro di voler continuare? Page Pagina Default behaviors Comportamenti predefiniti Automatically add new page Aggiungi automaticamente nuove pagine Grid size Dimensione griglia Number of columns Numero di colonne Number of rows Numero di righe CueAction Default Automatica Pause Pausa Start Avvia Stop Ferma FadeInStart Avvio con Dissolvenza FadeOutStop Ferma con Dissolvenza FadeOutPause Pausa con Dissolvenza CueActionLog Cue settings changed: "{}" Impostazioni cue cambiate "{}" Cues settings changed. Impostazioni di più cue cambiate. CueAppearanceSettings The appearance depends on the layout L'aspetto dipende dal layout Cue name Nome della cue NoName NoName Description/Note Descrizione/Note Set Font Size Dimensione carattere Color Colore Select background color Selezione colore di sfondo Select font color Seleziona colore del carattere CueName Media Cue Cue Multimediale CueSettings Pre wait Pre wait Wait before cue execution Attesa prima dell'esecuzione della cue Post wait Post wait Wait after cue execution Attesa dopo l'esecuzione della cue Next action Azione successiva Interrupt Fade Dissolvenza Interruzioni Fade Action Operazioni di Dissolvenza Behaviours Comportamenti Pre/Post Wait Attesa Prima/Dopo Fade In/Out Dissolvenza Ingresso/Uscita Start action Azione d'avvio Default action to start the cue Azione standard per avviare la cue Stop action Azione di arresto Default action to stop the cue Azione standard per arrestare la cue Fade Linear Lineare Quadratic Quadratica Quadratic2 Quadratica2 FadeEdit Duration (sec) Durata (sec) Curve Curva FadeSettings Fade In Dissolvenza Ingresso Fade Out Dissolvenza Uscita LayoutDescription Organize cues in grid like pages Organizza le cue in pagine simili a tabelle Organize the cues in a list Organizza le cue in una lista LayoutDetails Click a cue to run it Clicca una cue per eseguirla SHIFT + Click to edit a cue SHIFT + Click per modificare una cue CTRL + Click to select a cue CTRL + Click per selezionare una cue SHIFT + Space or Double-Click to edit a cue SHIFT + Spazio o Doppio Click per modifcare una cue To copy cues drag them while pressing CTRL Per copiare le cue trascinale mentre premi CTRL To copy cues drag them while pressing SHIFT Per copiare le cue trascinale mentre premi SHIFT CTRL + Left Click to select cues CTRL + Click Sinistro per selezionare una cue To move cues drag them Per spostare una cue trascinala LayoutSelect Layout selection Selezione del layout Select layout Seleziona layout Open file Apri file ListLayout Show playing cues Mostra cue in riproduzione Show dB-meters Mostra indicatori dB Show seek-bars Mostra barre di avanzamento Show accurate time Mostra tempo accurato Auto-select next cue Auto-seleziona prossima cue Edit cue Modifica Cue Remove Rimuovi Select Seleziona Stop all Ferma tutte Pause all Sospendi tutte Restart all Riavvia tutte Stop Ferma Restart Riavvia Default behaviors Comportamenti predefiniti At list end: A fine lista: Go key: Tasto "Go": Interrupt all Interrompiti tutte Fade-Out all Sfuma tutte in uscita Fade-In all Sfuma tutte in ingresso Use fade Usa dissolvenza Stop Cue Ferma Cue Pause Cue Pausa Cue Restart Cue Riavvia Cue Interrupt Cue Interrompi Cue Stop All Ferma Tutte Pause All Pausa Tutte Restart All Riavvia Tutte Interrupt All Interrompi Tutte ListLayoutHeader Cue Cue Pre wait Pre wait Action Azione Post wait Post wait ListLayoutInfoPanel Cue name Nome della cue Cue description Descrizone della cue Logging Information Informazione Debug Debug Warning Avviso Error Errore Details: Dettagli: MainWindow &File &File New session Nuova sessione Open Apri Save session Salva sessione Preferences Preferenze Save as Salva come Full Screen Schermo Intero Exit Esci &Edit &Modifica Undo Annulla Redo Ripeti Select all Seleziona tutti Select all media cues Seleziona tutte le media-cue Deselect all Deseleziona tutti CTRL+SHIFT+A CTRL+SHIFT+A Invert selection Inverti selezione CTRL+I CTRL+I Edit selected Modifica selezionati CTRL+SHIFT+E CTRL+SHIFT+E &Layout &Layout &Tools &Strumenti Edit selection Modifica slezionati &About &About About Informazioni About Qt Informazioni su Qt Undone: Annullato: Redone: Ripetuto: Close session Chiudi sessione The current session is not saved. La sessione corrente non è salvata. Discard the changes? Scartare la modifiche? MediaCueMenus Audio cue (from file) Cue audio (da file) Select media files Seleziona file multimediali MediaCueSettings Start time Posizione iniziale Stop position of the media Posizione in cui la traccia si deve fermare Stop time Posizione finale Start position of the media Posizione da cui la traccia deve partire Loop Loop Repetition after first play (-1 = infinite) Ripetizioni dopo la prima riproduzione (-1 = infinite) QColorButton Right click to reset Clicca con il pulsante destro per resettare SettingsPageName Appearance Aspetto General Generale Cue Cue Cue Settings Impostazioni Cue linux-show-player-0.5.1/lisp/i18n/lisp_sl_SI.qm000066400000000000000000000351031323636106000212530ustar00rootroot00000000000000%ҳ$7 n*6.@zfz^&n*M/ )$\.0KTH5WoF tF"+ X+#fI^,b ~m'2.a>W9Iv:c1 9kM#cs0R_Υ Cٰs4 ;Q& r r#wtwt&0ʨ"6cs΀"Ύ"xή# - % >*R S&/+U b2> h>(> la) ve% v. W., N+ W - A gc gd] gd& s> k%C t 7  n ni u ))E ;  ;  GuL bC%# bO%^ bc% wD wD'V 3 $ . M@- E ,u , w , RY..D W[X ^}T3J zt !A 5! 2!  7W ' ĪeR -\ Us Us# P P({ _D   yq <4q 4 1),6zU +gk.32 i42Opis Linux Show Player-jaAbout Linux Show PlayerAboutAvtorjiAuthorsAboutPrispevkarji ContributorsAboutPrevajalci TranslatorsAboutPrispevkarji Contributors AboutDialogPodrobnostiInfo AboutDialogLicencaLicense AboutDialogLinux Show Player je predvajalnik vrst, prilagojen za odrsko/gledalia no raboALinux Show Player is a cue-player designed for stage productions. AboutDialogIzvorna koda Source code AboutDialog&Skupina uporabnikov Users group AboutDialogSpletna stranWeb site AboutDialogTema aplikacijeApplication themeAppGeneralSettings(Za etna razporeditevStartup layoutAppGeneralSettings,Uporabi za etni dialogUse startup dialogAppGeneralSettingsLiSP nastavitveLiSP preferences AppSettingsDodaj stranAdd page CartLayoutDodaj strani Add pages CartLayout<Ste prepri ani v nadaljevanje?Are you sure to continue? CartLayout4Samodejno dodaj novo stranAutomatically add new page CartLayout Odatevalni na inCountdown mode CartLayout$Privzeto obnaaanjeDefault behaviors CartLayoutUredi vrstoEdit cue CartLayoutFVse vrste na strani bodo izgubljene#Every cue in the page will be lost. CartLayoutVelikost mre~e Grid size CartLayout`tevilo strani:Number of Pages: CartLayout `tevilo stolpcevNumber of columns CartLayout`tevilo vrsticNumber of rows CartLayout StranPage CartLayout PremorPause CartLayoutPredvajajPlay CartLayoutOdstraniRemove CartLayout.Odstrani trenutno stranRemove current page CartLayout$Ponastavi glasnost Reset volume CartLayout IzberiSelect CartLayout"Prika~i to en  asShow accurate time CartLayout Prika~i dB metreShow dB-meters CartLayout0Prika~i iskalne stolpi eShow seek-bars CartLayout Prika~i glasnost Show volume CartLayout UstaviStop CartLayoutOpozoriloWarning CartLayoutPrivzetoDefault CueAction*Oja evanje ob za etku FadeInStart CueAction(Pojemanje ob premoru FadeOutPause CueAction,Pojemanje ob ustavitvi FadeOutStop CueAction PremorPause CueAction ZagonStart CueAction UstaviStop CueActionDNastavitev vrste spremenjene: "{}"Cue settings changed: "{}" CueActionLog8Nastavitev vrst spremenjene.Cues settings changed. CueActionLog BarvaColorCueAppearanceSettingsIme vrsteCue nameCueAppearanceSettingsOpis/ZapiskiDescription/NoteCueAppearanceSettingsBrez imenaNoNameCueAppearanceSettings&Izberi barvo ozadjaSelect background colorCueAppearanceSettings&Izberi barvo pisaveSelect font colorCueAppearanceSettings.Nastavi velikost pisave Set Font SizeCueAppearanceSettingsZPrikaz je odvisen od uporabljene razporeditve$The appearance depends on the layoutCueAppearanceSettingsMedijska vrsta Media CueCueNameObnaaanja Behaviours CueSettings<Privzeta akcija za zagon vrsteDefault action to start the cue CueSettingsDPrivzeta akcija ob ustavitvi vrsteDefault action to stop the cue CueSettingsAkcija prehoda Fade Action CueSettingsPrehod v/iz Fade In/Out CueSettingsPrekini prehodInterrupt Fade CueSettings Naslednja akcija Next action CueSettingsPo  akanje Post wait CueSettingsPred  akanjePre wait CueSettingsPred/Po  akanje Pre/Post Wait CueSettingsZa etna akcija Start action CueSettingsUstavi akcijo Stop action CueSettings0Po akaj po izvedbi vrsteWait after cue execution CueSettings8Po akaj preden izvedea vrstoWait before cue execution CueSettingsLinearnoLinearFadeKvadratno QuadraticFadeKvadratno2 Quadratic2FadeKrivuljaCurveFadeEditTrajanje (s)Duration (sec)FadeEditOja evanjeFade In FadeSettingsPojemanjeFade Out FadeSettingsLRazporedi vrste v strani podobni mre~i Organize cues in grid like pagesLayoutDescription0Razporedi vrste v seznamOrganize the cues in a listLayoutDescription6CTRL + Klik, za izbor vrsteCTRL + Click to select a cue LayoutDetails>CTRL + Levi klik, za izbor vrst CTRL + Left Click to select cues LayoutDetails,Klik na vrsto za zagonClick a cue to run it LayoutDetails>SHIFT + Klik, za ureditev vrsteSHIFT + Click to edit a cue LayoutDetailsbSHIFT + Preslednica ali dvoklik za ureditev vrste+SHIFT + Space or Double-Click to edit a cue LayoutDetails`Za kopiranje vrst, med vle enjem pridr~ite SHIFT*To copy cues drag them while pressing CTRL LayoutDetails`Za kopiranje vrst, med vle enjem pridr~ite SHIFT+To copy cues drag them while pressing SHIFT LayoutDetails@Za premikanje vrst jih povleciteTo move cues drag them LayoutDetails$Izbor razporeditveLayout selection LayoutSelectOdpri datoteko Open file LayoutSelect&Izberi razporeditev Select layout LayoutSelect"Ob koncu seznama: At list end: ListLayout@Samodejno izberi naslednjo vrstoAuto-select next cue ListLayout$Privzeto obnaaanjeDefault behaviors ListLayoutUredi vrstoEdit cue ListLayoutOja evanje vse Fade-In all ListLayoutPojemanje vse Fade-Out all ListLayout Spro~ilni klju :Go key: ListLayoutPrekini vse Interrupt All ListLayoutPrekini vrsto Interrupt Cue ListLayoutPrekini vse Interrupt all ListLayoutV premor vse Pause All ListLayoutPremor vrste Pause Cue ListLayoutV premor vse Pause all ListLayoutOdstraniRemove ListLayoutPonovno za~eniRestart ListLayout$Ponovno za~eni vse Restart All ListLayout(Ponovno za~eni vrsto Restart Cue ListLayout$Ponovno za~eni vse Restart all ListLayout IzberiSelect ListLayout"Prika~i to en  asShow accurate time ListLayout Prika~i dB metreShow dB-meters ListLayout0Prika~i predvajane vrsteShow playing cues ListLayout0Prika~i iskalne stolpi eShow seek-bars ListLayout UstaviStop ListLayoutUstavi vseStop All ListLayoutUstavi vrstoStop Cue ListLayoutUstavi vseStop all ListLayoutUporabi prehodUse fade ListLayout AkcijaActionListLayoutHeader VrstaCueListLayoutHeaderPo  akanje Post waitListLayoutHeaderPred  akanjePre waitListLayoutHeaderOpis vrsteCue descriptionListLayoutInfoPanelIme vrsteCue nameListLayoutInfoPanelRazhroa evanjeDebugLoggingPodrobnosti:Details:Logging NapakaErrorLoggingInformacije InformationLoggingOpozoriloWarningLogging &Opis&About MainWindow &Uredi&Edit MainWindow&Datoteka&File MainWindow&Razporeditev&Layout MainWindow&Orodja&Tools MainWindowOpisAbout MainWindowOpis QtAbout Qt MainWindow CTRL+ICTRL+I MainWindowCTRL+SHIFT+A CTRL+SHIFT+A MainWindowCTRL+SHIFT+E CTRL+SHIFT+E MainWindowZapri sejo Close session MainWindowOd izberi vse Deselect all MainWindow$Zavr~em spremembe?Discard the changes? MainWindowUredi izbrano Edit selected MainWindowUredi izborEdit selection MainWindow IzhodExit MainWindowPolni zaslon Full Screen MainWindow Invertiraj izborInvert selection MainWindowNova seja New session MainWindow OdpriOpen MainWindowNastavitve Preferences MainWindow ObnoviRedo MainWindowUveljavljeno:Redone:  MainWindowShrani kotSave as MainWindowShrani sejo Save session MainWindowIzberi vse Select all MainWindow2Izberi vse medijske vrsteSelect all media cues MainWindow6Trenutna seja ni shranjena.!The current session is not saved. MainWindowRazveljaviUndo MainWindowRazveljavljeno:Undone:  MainWindow2Avdio vrsta (iz datoteke)Audio cue (from file) MediaCueMenus0Izberi medijske datotekeSelect media files MediaCueMenusPonavljajLoopMediaCueSettings^Ponovitev po prvem predvajanju (-1 = neskon no)+Repetition after first play (-1 = infinite)MediaCueSettings.Za etna pozicija medijaStart position of the mediaMediaCueSettings as za etka Start timeMediaCueSettings:Zaustavitvena pozicija medijaStop position of the mediaMediaCueSettings  as zaustavitve  Stop timeMediaCueSettings4Desno klik za ponastavitevRight click to reset QColorButton Prikaz AppearanceSettingsPageName VrstaCueSettingsPageName Nastavitve vrste Cue SettingsSettingsPageNameSploanoGeneralSettingsPageName !!$linux-show-player-0.5.1/lisp/i18n/lisp_sl_SI.ts000066400000000000000000001047701323636106000212730ustar00rootroot00000000000000 About Authors Avtorji Contributors Prispevkarji Translators Prevajalci About Linux Show Player Opis Linux Show Player-ja AboutDialog Linux Show Player is a cue-player designed for stage productions. Linux Show Player je predvajalnik vrst, prilagojen za odrsko/gledališčno rabo Web site Spletna stran Users group Skupina uporabnikov Source code Izvorna koda Info Podrobnosti License Licenca Contributors Prispevkarji AppGeneralSettings Startup layout Začetna razporeditev Use startup dialog Uporabi začetni dialog Application theme Tema aplikacije AppSettings LiSP preferences LiSP nastavitve CartLayout Add page Dodaj stran Add pages Dodaj strani Remove current page Odstrani trenutno stran Countdown mode Odštevalni način Show seek-bars Prikaži iskalne stolpiče Show dB-meters Prikaži dB metre Show volume Prikaži glasnost Show accurate time Prikaži točen čas Edit cue Uredi vrsto Remove Odstrani Select Izberi Play Predvajaj Pause Premor Stop Ustavi Reset volume Ponastavi glasnost Number of Pages: Število strani: Warning Opozorilo Every cue in the page will be lost. Vse vrste na strani bodo izgubljene Are you sure to continue? Ste prepričani v nadaljevanje? Page Stran Default behaviors Privzeto obnašanje Automatically add new page Samodejno dodaj novo stran Grid size Velikost mreže Number of columns Število stolpcev Number of rows Število vrstic CueAction Default Privzeto Pause Premor Start Zagon Stop Ustavi FadeInStart Ojačevanje ob začetku FadeOutStop Pojemanje ob ustavitvi FadeOutPause Pojemanje ob premoru CueActionLog Cue settings changed: "{}" Nastavitev vrste spremenjene: "{}" Cues settings changed. Nastavitev vrst spremenjene. CueAppearanceSettings The appearance depends on the layout Prikaz je odvisen od uporabljene razporeditve Cue name Ime vrste NoName Brez imena Description/Note Opis/Zapiski Set Font Size Nastavi velikost pisave Color Barva Select background color Izberi barvo ozadja Select font color Izberi barvo pisave CueName Media Cue Medijska vrsta CueSettings Pre wait Pred čakanje Wait before cue execution Počakaj preden izvedeš vrsto Post wait Po čakanje Wait after cue execution Počakaj po izvedbi vrste Next action Naslednja akcija Interrupt Fade Prekini prehod Fade Action Akcija prehoda Behaviours Obnašanja Pre/Post Wait Pred/Po čakanje Fade In/Out Prehod v/iz Start action Začetna akcija Default action to start the cue Privzeta akcija za zagon vrste Stop action Ustavi akcijo Default action to stop the cue Privzeta akcija ob ustavitvi vrste Fade Linear Linearno Quadratic Kvadratno Quadratic2 Kvadratno2 FadeEdit Duration (sec) Trajanje (s) Curve Krivulja FadeSettings Fade In Ojačevanje Fade Out Pojemanje LayoutDescription Organize cues in grid like pages Razporedi vrste v strani podobni mreži Organize the cues in a list Razporedi vrste v seznam LayoutDetails Click a cue to run it Klik na vrsto za zagon SHIFT + Click to edit a cue SHIFT + Klik, za ureditev vrste CTRL + Click to select a cue CTRL + Klik, za izbor vrste SHIFT + Space or Double-Click to edit a cue SHIFT + Preslednica ali dvoklik za ureditev vrste To copy cues drag them while pressing CTRL Za kopiranje vrst, med vlečenjem pridržite SHIFT To copy cues drag them while pressing SHIFT Za kopiranje vrst, med vlečenjem pridržite SHIFT CTRL + Left Click to select cues CTRL + Levi klik, za izbor vrst To move cues drag them Za premikanje vrst jih povlecite LayoutSelect Layout selection Izbor razporeditve Select layout Izberi razporeditev Open file Odpri datoteko ListLayout Show playing cues Prikaži predvajane vrste Show dB-meters Prikaži dB metre Show seek-bars Prikaži iskalne stolpiče Show accurate time Prikaži točen čas Auto-select next cue Samodejno izberi naslednjo vrsto Edit cue Uredi vrsto Remove Odstrani Select Izberi Stop all Ustavi vse Pause all V premor vse Restart all Ponovno zaženi vse Stop Ustavi Restart Ponovno zaženi Default behaviors Privzeto obnašanje At list end: Ob koncu seznama: Go key: Sprožilni ključ: Interrupt all Prekini vse Fade-Out all Pojemanje vse Fade-In all Ojačevanje vse Use fade Uporabi prehod Stop Cue Ustavi vrsto Pause Cue Premor vrste Restart Cue Ponovno zaženi vrsto Interrupt Cue Prekini vrsto Stop All Ustavi vse Pause All V premor vse Restart All Ponovno zaženi vse Interrupt All Prekini vse ListLayoutHeader Cue Vrsta Pre wait Pred čakanje Action Akcija Post wait Po čakanje ListLayoutInfoPanel Cue name Ime vrste Cue description Opis vrste Logging Information Informacije Debug Razhroščevanje Warning Opozorilo Error Napaka Details: Podrobnosti: MainWindow &File &Datoteka New session Nova seja Open Odpri Save session Shrani sejo Preferences Nastavitve Save as Shrani kot Full Screen Polni zaslon Exit Izhod &Edit &Uredi Undo Razveljavi Redo Obnovi Select all Izberi vse Select all media cues Izberi vse medijske vrste Deselect all Od izberi vse CTRL+SHIFT+A CTRL+SHIFT+A Invert selection Invertiraj izbor CTRL+I CTRL+I Edit selected Uredi izbrano CTRL+SHIFT+E CTRL+SHIFT+E &Layout &Razporeditev &Tools &Orodja Edit selection Uredi izbor &About &Opis About Opis About Qt Opis Qt Undone: Razveljavljeno: Redone: Uveljavljeno: Close session Zapri sejo The current session is not saved. Trenutna seja ni shranjena. Discard the changes? Zavržem spremembe? MediaCueMenus Audio cue (from file) Avdio vrsta (iz datoteke) Select media files Izberi medijske datoteke MediaCueSettings Start time Čas začetka Stop position of the media Zaustavitvena pozicija medija Stop time Čas zaustavitve Start position of the media Začetna pozicija medija Loop Ponavljaj Repetition after first play (-1 = infinite) Ponovitev po prvem predvajanju (-1 = neskončno) QColorButton Right click to reset Desno klik za ponastavitev SettingsPageName Appearance Prikaz General Splošno Cue Vrsta Cue Settings Nastavitve vrste linux-show-player-0.5.1/lisp/layouts/000077500000000000000000000000001323636106000175735ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/layouts/__init__.py000066400000000000000000000004641323636106000217100ustar00rootroot00000000000000from lisp.layouts.cart_layout.layout import CartLayout from lisp.layouts.list_layout.layout import ListLayout __LAYOUTS__ = [CartLayout, ListLayout] def get_layouts(): return __LAYOUTS__ def get_layout(name): for layout in __LAYOUTS__: if layout.NAME == name: return layout linux-show-player-0.5.1/lisp/layouts/cart_layout/000077500000000000000000000000001323636106000221215ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/layouts/cart_layout/__init__.py000066400000000000000000000000001323636106000242200ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/layouts/cart_layout/cart_layout_settings.py000066400000000000000000000123731323636106000267470ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QCheckBox, QGridLayout, \ QSpinBox, QLabel from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class CartLayoutSettings(SettingsPage): Name = 'Cart Layout' def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.behaviorsGroup = QGroupBox(self) self.behaviorsGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.behaviorsGroup) self.countdownMode = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.countdownMode) self.showSeek = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.showSeek) self.showDbMeters = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.showDbMeters) self.showAccurate = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.showAccurate) self.showVolume = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.showVolume) self.autoAddPage = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.autoAddPage) self.gridSizeGroup = QGroupBox(self) self.gridSizeGroup.setLayout(QGridLayout()) self.layout().addWidget(self.gridSizeGroup) self.columnsSpin = QSpinBox(self.gridSizeGroup) self.columnsSpin.setRange(1, 16) self.gridSizeGroup.layout().addWidget(self.columnsSpin, 0, 0) self.columnsLabel = QLabel(self.gridSizeGroup) self.gridSizeGroup.layout().addWidget(self.columnsLabel, 0, 1) self.rowsSpin = QSpinBox(self.gridSizeGroup) self.rowsSpin.setRange(1, 16) self.gridSizeGroup.layout().addWidget(self.rowsSpin, 1, 0) self.rowsLabel = QLabel(self.gridSizeGroup) self.gridSizeGroup.layout().addWidget(self.rowsLabel, 1, 1) self.gridSizeGroup.layout().setColumnStretch(0, 5) self.gridSizeGroup.layout().setColumnStretch(1, 3) self.retranslateUi() def retranslateUi(self): self.behaviorsGroup.setTitle( translate('CartLayout', 'Default behaviors')) self.countdownMode.setText(translate('CartLayout', 'Countdown mode')) self.showSeek.setText(translate('CartLayout', 'Show seek-bars')) self.showDbMeters.setText(translate('CartLayout', 'Show dB-meters')) self.showAccurate.setText(translate('CartLayout', 'Show accurate time')) self.showVolume.setText(translate('CartLayout', 'Show volume')) self.autoAddPage.setText( translate('CartLayout', 'Automatically add new page')) self.gridSizeGroup.setTitle(translate('CartLayout', 'Grid size')) self.columnsLabel.setText(translate('CartLayout', 'Number of columns')) self.rowsLabel.setText(translate('CartLayout', 'Number of rows')) def get_settings(self): conf = { 'gridcolumns': str(self.columnsSpin.value()), 'gridrows': str(self.rowsSpin.value()), 'showdbmeters': str(self.showDbMeters.isChecked()), 'showseek': str(self.showSeek.isChecked()), 'showaccurate': str(self.showAccurate.isChecked()), 'showvolume': str(self.showVolume.isChecked()), 'countdown': str(self.countdownMode.isChecked()), 'autoaddpage': str(self.autoAddPage.isChecked()) } return {'CartLayout': conf} def load_settings(self, settings): settings = settings.get('CartLayout', {}) if 'gridcolumns' in settings: self.columnsSpin.setValue(int(settings['gridcolumns'])) if 'gridrows' in settings: self.rowsSpin.setValue(int(settings['gridrows'])) if 'showseek' in settings: self.showSeek.setChecked(settings['showseek'] == 'True') if 'showdbmeters' in settings: self.showDbMeters.setChecked(settings['showdbmeters'] == 'True') if 'showaccurate' in settings: self.showAccurate.setChecked(settings['showaccurate'] == 'True') if 'showvolume' in settings: self.showVolume.setChecked(settings['showvolume'] == 'True') if 'countdown' in settings: self.countdownMode.setChecked(settings['countdown'] == 'True') if 'autoaddpage' in settings: self.autoAddPage.setChecked(settings['autoaddpage'] == 'True') linux-show-player-0.5.1/lisp/layouts/cart_layout/cue_cart_model.py000066400000000000000000000100151323636106000254350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from sortedcontainers import SortedDict from lisp.core.model import ModelException from lisp.core.model_adapter import ModelAdapter class CueCartModel(ModelAdapter): def __init__(self, model, rows, columns): super().__init__(model) self.__cues = SortedDict() self.__rows = rows self.__columns = columns def flat(self, index): """If index is multidimensional return a flatted version. :rtype: int """ try: page, row, column = index page *= self.__rows * self.__columns row *= self.__columns return page + row + column except TypeError: return index def first_empty(self): """Return the first empty index.""" n = -1 for n, index in enumerate(self.__cues.keys()): if n != index: return n return n + 1 def item(self, index): index = self.flat(index) try: return self.__cues[index] except KeyError: raise IndexError('index out of range') def insert(self, item, index): index = self.flat(index) if index not in self.__cues: item.index = index self.add(item) def pop(self, index): index = self.flat(index) try: cue = self.__cues[index] except KeyError: raise IndexError('index out of range') self.model.remove(cue) return cue def move(self, old_index, new_index): old_index = self.flat(old_index) new_index = self.flat(new_index) if new_index not in self.__cues: self.__cues[new_index] = self.__cues.pop(old_index) self.__cues[new_index].index = new_index self.item_moved.emit(old_index, new_index) else: raise ModelException('index already used {}'.format(new_index)) def page_edges(self, page): start = self.flat((page, 0, 0)) end = self.flat((page, self.__rows, self.__columns)) return start, end def remove_page(self, page, lshift=True): start, end = self.page_edges(page) for index in range(start, end + 1): cue = self.__cues.get(index) if cue is not None: self.remove(cue) if lshift: page_size = self.__rows * self.__columns for index in self.__cues.irange(minimum=end + 1): new_index = index - page_size self.__cues[new_index] = self.__cues.pop(index) self.__cues[new_index].index = new_index def iter_page(self, page): """Iterate over the cues of the given page""" start, stop = self.page_edges(page) for index in self.__cues.irange(start, stop): yield self.__cues[index] def _item_added(self, item): if item.index == -1 or item.index in self.__cues: item.index = self.first_empty() self.__cues[item.index] = item self.item_added.emit(item) def _item_removed(self, item): self.__cues.pop(item.index) self.item_removed.emit(item) def _model_reset(self): self.__cues.clear() self.model_reset.emit() def __iter__(self): return iter(self.__cues.values()) linux-show-player-0.5.1/lisp/layouts/cart_layout/cue_widget.py000066400000000000000000000346621323636106000246250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QMimeData, pyqtSignal, QPoint from PyQt5.QtGui import QColor, QDrag from PyQt5.QtWidgets import QProgressBar, QLCDNumber, QLabel, QHBoxLayout, \ QWidget, QGridLayout, QSizePolicy from lisp.backend.audio_utils import slider_to_fader, fader_to_slider from lisp.core.signal import Connection from lisp.core.util import strtime from lisp.cues.cue import CueState from lisp.cues.cue_time import CueTime from lisp.cues.media_cue import MediaCue from lisp.layouts.cart_layout.page_widget import PageWidget from lisp.ui.ui_utils import pixmap_from_icon from lisp.ui.widgets import QClickLabel, QClickSlider, QDbMeter,\ QDetailedMessageBox class CueWidget(QWidget): ICON_SIZE = 14 SLIDER_RANGE = 1000 context_menu_request = pyqtSignal(object, QPoint) edit_request = pyqtSignal(object) cue_executed = pyqtSignal(object) def __init__(self, cue, **kwargs): super().__init__(**kwargs) self.cue = None self._selected = False self._accurate_timing = False self._show_dbmeter = False self._show_volume = False self._countdown_mode = True self._dbmeter_element = None self._fade_element = None self._volume_element = None self.setAttribute(Qt.WA_TranslucentBackground) self.setLayout(QGridLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(2) self.layout().setColumnStretch(0, 6) self.layout().setRowStretch(0, 4) self.nameButton = QClickLabel(self) self.nameButton.setObjectName('ButtonCueWidget') self.nameButton.setWordWrap(True) self.nameButton.setAlignment(Qt.AlignCenter) self.nameButton.setFocusPolicy(Qt.NoFocus) self.nameButton.clicked.connect(self._clicked) self.nameButton.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.layout().addWidget(self.nameButton, 0, 0) self.statusIcon = QLabel(self.nameButton) self.statusIcon.setStyleSheet('background-color: transparent') self.statusIcon.setPixmap( pixmap_from_icon('led-off', CueWidget.ICON_SIZE)) self.seekSlider = QClickSlider(self.nameButton) self.seekSlider.setOrientation(Qt.Horizontal) self.seekSlider.setFocusPolicy(Qt.NoFocus) self.seekSlider.setVisible(False) self.volumeSlider = QClickSlider(self.nameButton) self.volumeSlider.setObjectName('VolumeSlider') self.volumeSlider.setOrientation(Qt.Vertical) self.volumeSlider.setFocusPolicy(Qt.NoFocus) self.volumeSlider.setRange(0, CueWidget.SLIDER_RANGE) self.volumeSlider.setPageStep(10) self.volumeSlider.valueChanged.connect( self._change_volume, Qt.DirectConnection) self.volumeSlider.setVisible(False) self.dbMeter = QDbMeter(self) self.dbMeter.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.dbMeter.setVisible(False) self.timeBar = QProgressBar(self) self.timeBar.setTextVisible(False) self.timeBar.setLayout(QHBoxLayout()) self.timeBar.layout().setContentsMargins(0, 0, 0, 0) self.timeDisplay = QLCDNumber(self.timeBar) self.timeDisplay.setStyleSheet('background-color: transparent') self.timeDisplay.setSegmentStyle(QLCDNumber.Flat) self.timeDisplay.setDigitCount(8) self.timeDisplay.display('00:00:00') self.timeBar.layout().addWidget(self.timeDisplay) self.timeBar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.timeBar.setVisible(False) self._set_cue(cue) @property def selected(self): return self._selected @selected.setter def selected(self, value): self._selected = value self._update_style(self.cue.stylesheet) def contextMenuEvent(self, event): self.context_menu_request.emit(self, event.globalPos()) def mouseMoveEvent(self, event): if (event.buttons() == Qt.LeftButton and (event.modifiers() == Qt.ControlModifier or event.modifiers() == Qt.ShiftModifier)): mime_data = QMimeData() mime_data.setText(PageWidget.DRAG_MAGIC) drag = QDrag(self) drag.setMimeData(mime_data) drag.setPixmap(self.grab(self.rect())) if event.modifiers() == Qt.ControlModifier: drag.exec_(Qt.MoveAction) else: drag.exec_(Qt.CopyAction) event.accept() else: event.ignore() def set_countdown_mode(self, mode): self._countdown_mode = mode self._update_time(self.cue.current_time()) def set_accurate_timing(self, enable): self._accurate_timing = enable if self.cue.state & CueState.Pause: self._update_time(self.cue.current_time(), True) elif not self.cue.state & CueState.Running: self._update_duration(self.cue.duration) def show_seek_slider(self, visible): if isinstance(self.cue, MediaCue): self.seekSlider.setVisible(visible) self.update() def show_dbmeters(self, visible): if isinstance(self.cue, MediaCue): self._show_dbmeter = visible if self._dbmeter_element is not None: self._dbmeter_element.level_ready.disconnect(self.dbMeter.plot) self._dbmeter_element = None if visible: self._dbmeter_element = self.cue.media.element('DbMeter') if self._dbmeter_element is not None: self._dbmeter_element.level_ready.connect(self.dbMeter.plot) self.layout().addWidget(self.dbMeter, 0, 2) self.layout().setColumnStretch(2, 1) self.dbMeter.show() else: self.layout().removeWidget(self.dbMeter) self.layout().setColumnStretch(2, 0) self.dbMeter.hide() self.update() def show_volume_slider(self, visible): if isinstance(self.cue, MediaCue): self._show_volume = visible if self._volume_element is not None: self._volume_element.changed('volume').disconnect( self.reset_volume) self._volume_element = None if visible: self.volumeSlider.setEnabled(self.cue.state & CueState.Running) self._volume_element = self.cue.media.element('Volume') if self._volume_element is not None: self.reset_volume() self._volume_element.changed('volume').connect( self.reset_volume, Connection.QtQueued) self.layout().addWidget(self.volumeSlider, 0, 1) self.layout().setColumnStretch(1, 1) self.volumeSlider.show() else: self.layout().removeWidget(self.volumeSlider) self.layout().setColumnStretch(1, 0) self.volumeSlider.hide() self.update() def reset_volume(self): if self._volume_element is not None: self.volumeSlider.setValue(round(fader_to_slider( self._volume_element.volume) * CueWidget.SLIDER_RANGE)) def _set_cue(self, cue): self.cue = cue # Cue properties changes self.cue.changed('name').connect( self._update_name, Connection.QtQueued) self.cue.changed('stylesheet').connect( self._update_style, Connection.QtQueued) self.cue.changed('duration').connect( self._update_duration, Connection.QtQueued) self.cue.changed('description').connect( self._update_description, Connection.QtQueued) # Fade enter/exit self.cue.fadein_start.connect(self._enter_fadein, Connection.QtQueued) self.cue.fadein_end.connect(self._exit_fade, Connection.QtQueued) self.cue.fadeout_start.connect(self._enter_fadeout, Connection.QtQueued) self.cue.fadeout_end.connect(self._exit_fade, Connection.QtQueued) # Cue status changed self.cue.interrupted.connect(self._status_stopped, Connection.QtQueued) self.cue.started.connect(self._status_playing, Connection.QtQueued) self.cue.stopped.connect(self._status_stopped, Connection.QtQueued) self.cue.paused.connect(self._status_paused, Connection.QtQueued) self.cue.error.connect(self._status_error, Connection.QtQueued) self.cue.end.connect(self._status_stopped, Connection.QtQueued) # DbMeter connections if isinstance(cue, MediaCue): self.cue.media.elements_changed.connect( self._media_updated, Connection.QtQueued) self.cue.paused.connect(self.dbMeter.reset, Connection.QtQueued) self.cue.stopped.connect(self.dbMeter.reset, Connection.QtQueued) self.cue.end.connect(self.dbMeter.reset, Connection.QtQueued) self.cue.error.connect(self.dbMeter.reset, Connection.QtQueued) self.seekSlider.sliderMoved.connect(self.cue.media.seek) self.seekSlider.sliderJumped.connect(self.cue.media.seek) self._cue_time = CueTime(self.cue) self._cue_time.notify.connect(self._update_time, Connection.QtQueued) self._update_name(cue.name) self._update_style(cue.stylesheet) self._update_duration(self.cue.duration) def _media_updated(self): self.show_dbmeters(self._show_dbmeter) self.show_volume_slider(self._show_volume) def _update_name(self, name): self.nameButton.setText(name) def _update_description(self, description): self.nameButton.setToolTip(description) def _change_volume(self, new_volume): self._volume_element.current_volume = slider_to_fader( new_volume / CueWidget.SLIDER_RANGE) def _clicked(self, event): if not (self.seekSlider.geometry().contains(event.pos()) and self.seekSlider.isVisible()): if event.button() != Qt.RightButton: if event.modifiers() == Qt.ShiftModifier: self.edit_request.emit(self.cue) elif event.modifiers() == Qt.ControlModifier: self.selected = not self.selected else: self.cue_executed.emit(self.cue) self.cue.execute() def _update_style(self, stylesheet): stylesheet += 'text-decoration: underline;' if self.selected else '' self.nameButton.setStyleSheet(stylesheet) def _enter_fadein(self): p = self.timeDisplay.palette() p.setColor(p.WindowText, QColor(0, 255, 0)) self.timeDisplay.setPalette(p) def _enter_fadeout(self): p = self.timeDisplay.palette() p.setColor(p.WindowText, QColor(255, 50, 50)) self.timeDisplay.setPalette(p) def _exit_fade(self): self.timeDisplay.setPalette(self.timeBar.palette()) def _status_stopped(self): self.statusIcon.setPixmap( pixmap_from_icon('led-off', CueWidget.ICON_SIZE)) self.volumeSlider.setEnabled(False) self._update_time(0, True) self.reset_volume() def _status_playing(self): self.statusIcon.setPixmap( pixmap_from_icon('led-running', CueWidget.ICON_SIZE)) self.volumeSlider.setEnabled(True) def _status_paused(self): self.statusIcon.setPixmap( pixmap_from_icon('led-pause', CueWidget.ICON_SIZE)) self.volumeSlider.setEnabled(False) def _status_error(self, cue, error, details): self.statusIcon.setPixmap( pixmap_from_icon('led-error', CueWidget.ICON_SIZE)) self.volumeSlider.setEnabled(False) self.reset_volume() QDetailedMessageBox.dcritical(self.cue.name, error, details) def _update_duration(self, duration): # Update the maximum values of seek-slider and time progress-bar if duration > 0: if not self.timeBar.isVisible(): self.layout().addWidget(self.timeBar, 1, 0, 1, 3) self.layout().setRowStretch(1, 1) self.timeBar.show() self.timeBar.setMaximum(duration) self.seekSlider.setMaximum(duration) else: self.timeBar.hide() self.layout().setRowStretch(1, 0) if not self.cue.state & CueState.Running: self._update_time(duration, True) def _update_time(self, time, ignore_visibility=False): if ignore_visibility or not self.visibleRegion().isEmpty(): # If the given value is the duration or < 0 set the time to 0 if time == self.cue.duration or time < 0: time = 0 # Set the value the seek slider self.seekSlider.setValue(time) # If in count-down mode the widget will show the remaining time if self._countdown_mode: time = self.cue.duration - time # Set the value of the timer progress-bar if self.cue.duration > 0: self.timeBar.setValue(time) # Show the time in the widget self.timeDisplay.display( strtime(time, accurate=self._accurate_timing)) def resizeEvent(self, event): self.update() def update(self): super().update() self.layout().activate() s_width = self.nameButton.width() - 8 s_height = self.seekSlider.height() s_ypos = self.nameButton.height() - s_height self.seekSlider.setGeometry(4, s_ypos, s_width, s_height) self.statusIcon.setGeometry(4, 4, CueWidget.ICON_SIZE, CueWidget.ICON_SIZE) linux-show-player-0.5.1/lisp/layouts/cart_layout/layout.py000066400000000000000000000431261323636106000240160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QTabWidget, QAction, QInputDialog, qApp, \ QMessageBox from lisp.core.configuration import config from lisp.core.signal import Connection from lisp.cues.cue import Cue from lisp.cues.cue_factory import CueFactory from lisp.cues.media_cue import MediaCue from lisp.layouts.cart_layout.cart_layout_settings import CartLayoutSettings from lisp.layouts.cart_layout.cue_cart_model import CueCartModel from lisp.layouts.cart_layout.cue_widget import CueWidget from lisp.layouts.cart_layout.page_widget import PageWidget from lisp.layouts.cue_layout import CueLayout from lisp.ui.mainwindow import MainWindow from lisp.ui.settings.app_settings import AppSettings from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.settings.pages.cue_appearance import Appearance from lisp.ui.settings.pages.cue_general import CueGeneralSettings from lisp.ui.settings.pages.media_cue_settings import MediaCueSettings from lisp.ui.ui_utils import translate AppSettings.register_settings_widget(CartLayoutSettings) class CartLayout(QTabWidget, CueLayout): NAME = 'Cart Layout' DESCRIPTION = translate('LayoutDescription', 'Organize cues in grid like pages') DETAILS = [ QT_TRANSLATE_NOOP('LayoutDetails', 'Click a cue to run it'), QT_TRANSLATE_NOOP('LayoutDetails', 'SHIFT + Click to edit a cue'), QT_TRANSLATE_NOOP('LayoutDetails', 'CTRL + Click to select a cue'), QT_TRANSLATE_NOOP('LayoutDetails', 'To copy cues drag them while pressing CTRL'), QT_TRANSLATE_NOOP('LayoutDetails', 'To copy cues drag them while pressing SHIFT') ] def __init__(self, cue_model, **kwargs): super().__init__(cue_model=cue_model, **kwargs) self.tabBar().setObjectName('CartTabBar') self.__columns = int(config['CartLayout']['GridColumns']) self.__rows = int(config['CartLayout']['GridRows']) self.__pages = [] self.__context_widget = None self._show_seek = config['CartLayout'].getboolean('ShowSeek') self._show_dbmeter = config['CartLayout'].getboolean('ShowDbMeters') self._show_volume = config['CartLayout'].getboolean('ShowVolume') self._accurate_timing = config['CartLayout'].getboolean('ShowAccurate') self._countdown_mode = config['CartLayout'].getboolean('CountDown') self._auto_add_page = config['CartLayout'].getboolean('AutoAddPage') self._model_adapter = CueCartModel(cue_model, self.__rows, self.__columns) self._model_adapter.item_added.connect(self.__cue_added, Connection.QtQueued) self._model_adapter.item_removed.connect(self.__cue_removed, Connection.QtQueued) self._model_adapter.item_moved.connect(self.__cue_moved, Connection.QtQueued) self._model_adapter.model_reset.connect(self.__model_reset) # Add layout-specific menus self.new_page_action = QAction(self) self.new_page_action.triggered.connect(self.add_page) self.new_pages_action = QAction(self) self.new_pages_action.triggered.connect(self.add_pages) self.rm_current_page_action = QAction(self) self.rm_current_page_action.triggered.connect(self.remove_current_page) self.countdown_mode = QAction(self) self.countdown_mode.setCheckable(True) self.countdown_mode.setChecked(self._countdown_mode) self.countdown_mode.triggered.connect(self.set_countdown_mode) self.show_seek_action = QAction(self) self.show_seek_action.setCheckable(True) self.show_seek_action.setChecked(self._show_seek) self.show_seek_action.triggered.connect(self.set_seek_visible) self.show_dbmeter_action = QAction(self) self.show_dbmeter_action.setCheckable(True) self.show_dbmeter_action.setChecked(self._show_dbmeter) self.show_dbmeter_action.triggered.connect(self.set_dbmeter_visible) self.show_volume_action = QAction(self) self.show_volume_action.setCheckable(True) self.show_volume_action.setChecked(self._show_volume) self.show_volume_action.triggered.connect(self.set_volume_visible) self.show_accurate_action = QAction(self) self.show_accurate_action.setCheckable(True) self.show_accurate_action.setChecked(self._accurate_timing) self.show_accurate_action.triggered.connect(self.set_accurate) layoutMenu = MainWindow().menuLayout layoutMenu.addAction(self.new_page_action) layoutMenu.addAction(self.new_pages_action) layoutMenu.addAction(self.rm_current_page_action) layoutMenu.addSeparator() layoutMenu.addAction(self.countdown_mode) layoutMenu.addAction(self.show_seek_action) layoutMenu.addAction(self.show_dbmeter_action) layoutMenu.addAction(self.show_volume_action) layoutMenu.addAction(self.show_accurate_action) # TODO: maybe can be moved outside the layout # Add cue preferences widgets CueSettingsRegistry().add_item(CueGeneralSettings, Cue) CueSettingsRegistry().add_item(MediaCueSettings, MediaCue) CueSettingsRegistry().add_item(Appearance) # Cue(s) context-menu actions self.edit_action = QAction(self) self.edit_action.triggered.connect(self._edit_cue_action) self.cm_registry.add_item(self.edit_action) self.sep1 = self.cm_registry.add_separator() self.remove_action = QAction(self) self.remove_action.triggered.connect(self._remove_cue_action) self.cm_registry.add_item(self.remove_action) self.select_action = QAction(self) self.select_action.triggered.connect(self.select_context_cue) self.cm_registry.add_item(self.select_action) self.sep2 = self.cm_registry.add_separator(MediaCue) # MediaCue(s) context-menu actions self.play_action = QAction(self) self.play_action.triggered.connect(self._play_context_cue) self.cm_registry.add_item(self.play_action, MediaCue) self.pause_action = QAction(self) self.pause_action.triggered.connect(self._pause_context_cue) self.cm_registry.add_item(self.pause_action, MediaCue) self.stop_action = QAction(self) self.stop_action.triggered.connect(self._stop_context_cue) self.cm_registry.add_item(self.stop_action, MediaCue) self.reset_volume_action = QAction(self) self.reset_volume_action.triggered.connect(self._reset_cue_volume) self.cm_registry.add_item(self.reset_volume_action, MediaCue) self.setAcceptDrops(True) self.retranslateUi() self.add_page() def retranslateUi(self): self.new_page_action.setText(translate('CartLayout', 'Add page')) self.new_pages_action.setText(translate('CartLayout', 'Add pages')) self.rm_current_page_action.setText( translate('CartLayout', 'Remove current page')) self.countdown_mode.setText(translate('CartLayout', 'Countdown mode')) self.show_seek_action.setText(translate('CartLayout', 'Show seek-bars')) self.show_dbmeter_action.setText( translate('CartLayout', 'Show dB-meters')) self.show_volume_action.setText(translate('CartLayout', 'Show volume')) self.show_accurate_action.setText( translate('CartLayout', 'Show accurate time')) self.edit_action.setText(translate('CartLayout', 'Edit cue')) self.remove_action.setText(translate('CartLayout', 'Remove')) self.select_action.setText(translate('CartLayout', 'Select')) self.play_action.setText(translate('CartLayout', 'Play')) self.pause_action.setText(translate('CartLayout', 'Pause')) self.stop_action.setText(translate('CartLayout', 'Stop')) self.reset_volume_action.setText(translate('CartLayout', 'Reset volume')) @CueLayout.model_adapter.getter def model_adapter(self): return self._model_adapter def add_pages(self): pages, accepted = QInputDialog.getInt( self, translate('CartLayout', 'Add pages'), translate('CartLayout', 'Number of Pages:'), value=1, min=1, max=10) if accepted: for _ in range(pages): self.add_page() def add_page(self): page = PageWidget(self.__rows, self.__columns, self) page.move_drop_event.connect(self._move_widget) page.copy_drop_event.connect(self._copy_widget) self.addTab(page, 'Page ' + str(self.count() + 1)) self.__pages.append(page) def select_context_cue(self): self.__context_widget.selected = not self.__context_widget.selected def select_all(self, cue_class=Cue): for widget in self.widgets(): if isinstance(widget.cue, cue_class): widget.selected = True def deselect_all(self, cue_class=Cue): for widget in self.widgets(): if isinstance(widget.cue, cue_class): widget.selected = False def invert_selection(self): for widget in self.widgets(): widget.selected = not widget.selected def contextMenuEvent(self, event): # For some reason the currentWidget geometry does not include the # vertical offset created by the tabBar. geometry = self.currentWidget().geometry().translated(0, self.tabBar().geometry().height()) if geometry.contains(event.pos()): self.show_context_menu(event.globalPos()) def dragEnterEvent(self, event): if qApp.keyboardModifiers() == Qt.ControlModifier: event.setDropAction(Qt.MoveAction) event.accept() elif qApp.keyboardModifiers() == Qt.ShiftModifier: event.setDropAction(Qt.MoveAction) event.accept() else: event.ignore() def dragMoveEvent(self, event): if self.tabBar().contentsRect().contains(event.pos()): self.setCurrentIndex(self.tabBar().tabAt(event.pos())) event.accept() def dropEvent(self, e): e.ignore() def keyPressEvent(self, event): self.key_pressed.emit(event) event.ignore() def get_context_cue(self): if self.__context_widget is not None: return self.__context_widget.cue def get_selected_cues(self, cue_class=Cue): # w -> widget cues = [] for widget in self.widgets(): if widget.selected: cues.append(widget.cue) return cues def remove_current_page(self): if self.__pages: confirm = QMessageBox() confirm.setIcon(confirm.Question) confirm.setWindowTitle(translate('CartLayout', 'Warning')) confirm.setText( translate('CartLayout', 'Every cue in the page will be lost.')) confirm.setInformativeText( translate('CartLayout', 'Are you sure to continue?')) confirm.setStandardButtons(QMessageBox.Yes | QMessageBox.No) confirm.setDefaultButton(QMessageBox.No) if confirm.exec_() == QMessageBox.Yes: self.remove_page(self.currentIndex()) def remove_page(self, page): if len(self.__pages) > page >= 0: self._model_adapter.remove_page(page) self.removeTab(page) self.tabRemoved(page) page_widget = self.__pages.pop(page) page_widget.move_drop_event.disconnect() page_widget.copy_drop_event.disconnect() # Rename every successive tab accordingly text = translate('CartLayout', 'Page') + ' {}' for n in range(page, self.count()): self.setTabText(n, text.format(n + 1)) def widgets(self): for page in self.__pages: for widget in page.widgets(): yield widget def set_countdown_mode(self, mode): self._countdown_mode = mode for widget in self.widgets(): widget.set_countdown_mode(mode) def set_accurate(self, enable): self._accurate_timing = enable for widget in self.widgets(): widget.set_accurate_timing(enable) def set_seek_visible(self, visible): self._show_seek = visible for widget in self.widgets(): widget.show_seek_slider(visible) def set_dbmeter_visible(self, visible): self._show_dbmeter = visible for widget in self.widgets(): widget.show_dbmeters(visible) def set_volume_visible(self, visible): self._show_volume = visible for widget in self.widgets(): widget.show_volume_slider(visible) def to_3d_index(self, index): page_size = self.__rows * self.__columns page = index // page_size row = (index % page_size) // self.__columns column = (index % page_size) % self.__columns return page, row, column def to_1d_index(self, index): try: page, row, column = index page *= self.__rows * self.__columns row *= self.__columns return page + row + column except(TypeError, ValueError): return -1 def finalize(self): MainWindow().menuLayout.clear() # Disconnect menu-actions signals self.edit_action.triggered.disconnect() self.remove_action.triggered.disconnect() self.select_action.triggered.disconnect() self.play_action.triggered.disconnect() self.pause_action.triggered.disconnect() self.stop_action.triggered.disconnect() self.reset_volume_action.disconnect() # Remove context-items self.cm_registry.remove_item(self.edit_action) self.cm_registry.remove_item(self.sep1) self.cm_registry.remove_item(self.remove_action) self.cm_registry.remove_item(self.select_action) self.cm_registry.remove_item(self.sep2) self.cm_registry.remove_item(self.play_action) self.cm_registry.remove_item(self.pause_action) self.cm_registry.remove_item(self.stop_action) self.cm_registry.remove_item(self.reset_volume_action) # Delete the layout self.deleteLater() def _move_widget(self, widget, to_row, to_column): new_index = self.to_1d_index((self.currentIndex(), to_row, to_column)) self._model_adapter.move(widget.cue.index, new_index) def _copy_widget(self, widget, to_row, to_column): new_index = self.to_1d_index((self.currentIndex(), to_row, to_column)) new_cue = CueFactory.clone_cue(widget.cue) self._model_adapter.insert(new_cue, new_index) def _play_context_cue(self): self.get_context_cue().media.play() def _pause_context_cue(self): self.get_context_cue().media.pause() def _stop_context_cue(self): self.get_context_cue().media.stop() def _on_context_menu(self, widget, position): self.__context_widget = widget self.show_cue_context_menu(position) def _edit_cue_action(self): self.edit_cue(self.get_context_cue()) def _remove_cue_action(self): self._model_adapter.remove(self.get_context_cue()) self.__context_widget = None def _reset_cue_volume(self): self.__context_widget.reset_volume() def __cue_added(self, cue): page, row, column = self.to_3d_index(cue.index) widget = CueWidget(cue) widget.cue_executed.connect(self.cue_executed.emit) widget.context_menu_request.connect(self._on_context_menu) widget.edit_request.connect(self.edit_cue) widget.set_accurate_timing(self._accurate_timing) widget.set_countdown_mode(self._countdown_mode) widget.show_dbmeters(self._show_dbmeter) widget.show_seek_slider(self._show_seek) widget.show_volume_slider(self._show_volume) if page >= len(self.__pages): self.add_page() self.__pages[page].add_widget(widget, row, column) self.setCurrentIndex(page) def __cue_removed(self, cue): if isinstance(cue, MediaCue): cue.media.interrupt() else: cue.stop() page, row, column = self.to_3d_index(cue.index) widget = self.__pages[page].take_widget(row, column) widget.cue_executed.disconnect() widget.context_menu_request.disconnect() widget.edit_request.disconnect() widget.deleteLater() def __cue_moved(self, old_index, new_index): o_page, o_row, o_column = self.to_3d_index(old_index) n_page, n_row, n_column = self.to_3d_index(new_index) if o_page == n_page: self.__pages[n_page].move_widget(o_row, o_column, n_row, n_column) else: widget = self.__pages[o_page].take_widget(o_row, o_column) self.__pages[n_page].add_widget(widget, n_row, n_column) def __model_reset(self): self.__context_widget = None for page in self.__pages: page.reset() linux-show-player-0.5.1/lisp/layouts/cart_layout/page_widget.py000066400000000000000000000125011323636106000247510ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import math from PyQt5.QtCore import pyqtSignal, Qt from PyQt5.QtWidgets import QWidget, QGridLayout, QSizePolicy, qApp from sortedcontainers import SortedDict class PageWidget(QWidget): move_drop_event = pyqtSignal(object, int, int) copy_drop_event = pyqtSignal(object, int, int) DRAG_MAGIC = 'LiSP_Drag&Drop' def __init__(self, rows, columns, *args): super().__init__(*args) self.setAcceptDrops(True) self.__rows = rows self.__columns = columns self.__widgets = SortedDict() self.setLayout(QGridLayout()) self.layout().setContentsMargins(4, 4, 4, 4) self.init_layout() def init_layout(self): for row in range(0, self.__rows): self.layout().setRowStretch(row, 1) # item = QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding) # self.layout().addItem(item, row, 0) for column in range(0, self.__columns): self.layout().setColumnStretch(column, 1) # item = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum) # self.layout().addItem(item, 0, column) def add_widget(self, widget, row, column): self._check_index(row, column) if (row, column) not in self.__widgets: widget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.__widgets[(row, column)] = widget self.layout().addWidget(widget, row, column) widget.show() else: raise IndexError('cell {} already used'.format((row, column))) def take_widget(self, row, column): self._check_index(row, column) if (row, column) in self.__widgets: widget = self.__widgets.pop((row, column)) widget.hide() self.layout().removeWidget(widget) return widget else: raise IndexError('cell {} is empty'.format((row, column))) def move_widget(self, o_row, o_column, n_row, n_column): widget = self.take_widget(o_row, o_column) self.add_widget(widget, n_row, n_column) def widget(self, row, column): self._check_index(row, column) return self.__widgets.get((row, column)) def index(self, widget): for index, f_widget in self.__widgets.items(): if widget is f_widget: return index return -1, -1 def widgets(self): return iter(self.__widgets.values()) def reset(self): self.__widgets.clear() def dragEnterEvent(self, event): if event.mimeData().hasText(): if event.mimeData().text() == PageWidget.DRAG_MAGIC: event.accept() else: event.ignore() else: event.ignore() def dragLeaveEvent(self, event): event.ignore() def dropEvent(self, event): row, column = self._event_index(event) if self.layout().itemAtPosition(row, column) is None: if qApp.keyboardModifiers() == Qt.ControlModifier: event.setDropAction(Qt.MoveAction) event.accept() self.move_drop_event.emit(event.source(), row, column) elif qApp.keyboardModifiers() == Qt.ShiftModifier: event.setDropAction(Qt.CopyAction) self.copy_drop_event.emit(event.source(), row, column) event.accept() event.ignore() def dragMoveEvent(self, event): row, column = self._event_index(event) if self.layout().itemAtPosition(row, column) is None: event.accept() else: event.ignore() def _check_index(self, row, column): if not isinstance(row, int): raise TypeError('rows index must be integers, not {}' .format(row.__class__.__name__)) if not isinstance(column, int): raise TypeError('columns index must be integers, not {}' .format(column.__class__.__name__)) if not 0 <= row < self.__rows or not 0 <= column < self.__columns: raise IndexError('index out of bound {}'.format((row, column))) def _event_index(self, event): # Margins and spacings are equals space = self.layout().horizontalSpacing() margin = self.layout().contentsMargins().right() r_size = (self.height() + margin * 2) // self.__rows + space c_size = (self.width() + margin * 2) // self.__columns + space row = math.ceil(event.pos().y() / r_size) - 1 column = math.ceil(event.pos().x() / c_size) - 1 return row, column linux-show-player-0.5.1/lisp/layouts/cue_layout.py000066400000000000000000000130451323636106000223210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import abstractmethod from PyQt5.QtWidgets import QAction, QMenu, qApp from lisp.core.actions_handler import MainActionsHandler from lisp.core.signal import Signal from lisp.core.util import greatest_common_superclass from lisp.cues.cue import Cue, CueAction from lisp.cues.cue_actions import UpdateCueAction, \ UpdateCuesAction from lisp.layouts.cue_menu_registry import CueMenuRegistry from lisp.ui.mainwindow import MainWindow from lisp.ui.settings.cue_settings import CueSettings class CueLayout: # Layout name NAME = 'Base' # Layout short description DESCRIPTION = 'No description' # Layout details (some useful info) DETAILS = '' cm_registry = CueMenuRegistry() def __init__(self, cue_model): self._cue_model = cue_model self.cue_executed = Signal() # After a cue is executed self.focus_changed = Signal() # After the focused cue is changed self.key_pressed = Signal() # After a key is pressed @property def cue_model(self): """:rtype: lisp.core.cue_model.CueModel""" return self._cue_model @property @abstractmethod def model_adapter(self): """:rtype: lisp.core.model_adapter.ModelAdapter""" def current_cue(self): """Return the current cue, or None. :rtype: lisp.cues.cue.Cue """ if self.model_adapter: try: return self.model_adapter.item(self.current_index()) except IndexError: pass def current_index(self): """Return the current index, or -1.""" return -1 def set_current_cue(self, cue): """Set the current cue.""" self.set_current_index(cue.index) def set_current_index(self, index): """Set the current cue by index""" def go(self, action=CueAction.Default, advance=1): """Execute the current cue and go ahead. .. Note:: The advance value can be ignored by the layout. :param action: the action the cue should execute :param advance: number of index to advance (with negative will go back) :rtype: lisp.cues.cue.Cue """ @abstractmethod def finalize(self): """Destroy all the layout elements""" def edit_cue(self, cue): edit_ui = CueSettings(cue, parent=MainWindow()) def on_apply(settings): action = UpdateCueAction(settings, cue) MainActionsHandler.do_action(action) edit_ui.on_apply.connect(on_apply) edit_ui.exec_() def edit_selected_cues(self): cues = self.get_selected_cues() if cues: # Use the greatest common superclass between the selected cues edit_ui = CueSettings(cue_class=greatest_common_superclass(cues)) def on_apply(settings): action = UpdateCuesAction(settings, cues) MainActionsHandler.do_action(action) edit_ui.on_apply.connect(on_apply) edit_ui.exec_() @abstractmethod def get_context_cue(self): """Return the last cue in the context-menu scope, or None""" return None @abstractmethod def get_selected_cues(self, cue_class=Cue): """Return a list of all selected cues, filtered by cue_class""" return [] @abstractmethod def invert_selection(self): """Invert selection""" @abstractmethod def select_all(self, cue_class=Cue): """Select all the cues""" @abstractmethod def deselect_all(self, cue_class=Cue): """Deselect all the cues""" @staticmethod def show_context_menu(position): menu_edit = MainWindow().menuEdit menu_edit.move(position) menu_edit.show() # Adjust the menu position desktop = qApp.desktop().availableGeometry() menu_rect = menu_edit.geometry() if menu_rect.bottom() > desktop.bottom(): menu_edit.move(menu_edit.x(), menu_edit.y() - menu_edit.height()) if menu_rect.right() > desktop.right(): menu_edit.move(menu_edit.x() - menu_edit.width(), menu_edit.y()) def show_cue_context_menu(self, position): menu = QMenu(self) cue_class = self.get_context_cue().__class__ for item in self.cm_registry.filter(cue_class): if isinstance(item, QAction): menu.addAction(item) elif isinstance(item, QMenu): menu.addMenu(item) menu.move(position) menu.show() # Adjust the menu position desktop = qApp.desktop().availableGeometry() menu_rect = menu.geometry() if menu_rect.bottom() > desktop.bottom(): menu.move(menu.x(), menu.y() - menu.height()) if menu_rect.right() > desktop.right(): menu.move(menu.x() - menu.width(), menu.y()) linux-show-player-0.5.1/lisp/layouts/cue_menu_registry.py000066400000000000000000000034711323636106000237020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtWidgets import QAction, QMenu from lisp.core.class_based_registry import ClassBasedRegistry from lisp.cues.cue import Cue class CueMenuRegistry(ClassBasedRegistry): def add_item(self, item, ref_class=Cue): if not isinstance(item, (QAction, QMenu)): raise TypeError('items must be QAction(s) or QMenu(s), not {0}' .format(item.__class__.__name__)) if not issubclass(ref_class, Cue): raise TypeError('ref_class must be Cue or a subclass, not {0}' .format(ref_class.__name__)) return super().add_item(item, ref_class) def add_separator(self, ref_class=Cue): """Register a separator menu-entry for ref_class and return it.""" separator = QAction(None) separator.setSeparator(True) self.add_item(separator, ref_class) return separator def filter(self, ref_class=Cue): return super().filter(ref_class) def clear_class(self, ref_class=Cue): return super().filter(ref_class) linux-show-player-0.5.1/lisp/layouts/list_layout/000077500000000000000000000000001323636106000221435ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/layouts/list_layout/__init__.py000066400000000000000000000000001323636106000242420ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/layouts/list_layout/control_buttons.py000066400000000000000000000113441323636106000257560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QSize from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QWidget, QGridLayout, QSizePolicy from lisp.ui.ui_utils import translate from lisp.ui.widgets.qiconpushbutton import QIconPushButton class ShowControlButtons(QWidget): def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QGridLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(5) # Row 0 self.pauseButton = self.newButton( QIcon.fromTheme('media-playback-pause')) self.layout().addWidget(self.pauseButton, 0, 0) self.stopButton = self.newButton(QIcon.fromTheme('media-playback-stop')) self.layout().addWidget(self.stopButton, 0, 1) self.interruptButton = self.newButton(QIcon.fromTheme('window-close')) self.layout().addWidget(self.interruptButton, 0, 2) # Row 1 self.resumeButton = self.newButton( QIcon.fromTheme('media-playback-start')) self.layout().addWidget(self.resumeButton, 1, 0) self.fadeOutButton = self.newButton(QIcon.fromTheme('fadeout-generic')) self.layout().addWidget(self.fadeOutButton, 1, 1) self.fadeInButton = self.newButton(QIcon.fromTheme('fadein-generic')) self.layout().addWidget(self.fadeInButton, 1, 2) self.retranslateUi() def retranslateUi(self): # Row 0 self.pauseButton.setToolTip(translate('ListLayout', 'Pause all')) self.stopButton.setToolTip(translate('ListLayout', 'Stop all')) self.interruptButton.setToolTip( translate('ListLayout', 'Interrupt all')) # Row 1 self.resumeButton.setToolTip(translate('ListLayout', 'Resume all')) self.fadeOutButton.setToolTip(translate('ListLayout', 'Fade-Out all')) self.fadeInButton.setToolTip(translate('ListLayout', 'Fade-In all')) def newButton(self, icon): button = QIconPushButton(self) button.setFocusPolicy(Qt.NoFocus) button.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Expanding) button.setIcon(icon) button.setIconSize(QSize(32, 32)) return button class CueControlButtons(QWidget): def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QGridLayout()) self.layout().setContentsMargins(1, 1, 1, 1) self.layout().setSpacing(2) # Start/Pause self.pauseButton = self.newButton( QIcon.fromTheme('media-playback-pause')) self.layout().addWidget(self.pauseButton, 0, 0, 2, 1) self.startButton = self.newButton( QIcon.fromTheme('media-playback-start')) self.startButton.hide() # Row 0 self.stopButton = self.newButton( QIcon.fromTheme('media-playback-stop')) self.layout().addWidget(self.stopButton, 0, 1) self.interruptButton = self.newButton(QIcon.fromTheme('window-close')) self.layout().addWidget(self.interruptButton, 0, 2) # Row 1 self.fadeOutButton = self.newButton(QIcon.fromTheme('fadeout-generic')) self.layout().addWidget(self.fadeOutButton, 1, 1) self.fadeInButton = self.newButton(QIcon.fromTheme('fadein-generic')) self.layout().addWidget(self.fadeInButton, 1, 2) self.layout().setColumnStretch(0, 3) self.layout().setColumnStretch(1, 2) self.layout().setColumnStretch(2, 2) def pauseMode(self): self.layout().addWidget(self.pauseButton, 0, 0, 2, 1) self.pauseButton.show() self.startButton.hide() def startMode(self): self.layout().addWidget(self.startButton, 0, 0, 2, 1) self.startButton.show() self.pauseButton.hide() def newButton(self, icon): button = QIconPushButton(self) button.setFocusPolicy(Qt.NoFocus) button.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) button.setIcon(icon) button.setIconSize(QSize(32, 32)) return button linux-show-player-0.5.1/lisp/layouts/list_layout/cue_list_item.py000066400000000000000000000035211323636106000253430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QTreeWidgetItem from lisp.core.signal import Connection from lisp.ui.ui_utils import load_icon class CueListItem(QTreeWidgetItem): def __init__(self, cue): super().__init__() self.cue = cue self.num_column = 1 self.name_column = 2 self._selected = False self.cue.changed('name').connect( self._update_name, Connection.QtQueued) self.cue.changed('index').connect( self._update_index, Connection.QtQueued) self._update_name(self.cue.name) self._update_index(self.cue.index) self.setTextAlignment(self.num_column, Qt.AlignCenter) @property def selected(self): return self._selected @selected.setter def selected(self, value): self._selected = value self.setIcon(0, load_icon('mark-location' if value else '')) def _update_index(self, index): self.setText(self.num_column, str(index)) def _update_name(self, name): self.setText(self.name_column, name) linux-show-player-0.5.1/lisp/layouts/list_layout/cue_list_model.py000066400000000000000000000077571323636106000255240ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.core.model_adapter import ModelAdapter from lisp.core.proxy_model import ReadOnlyProxyModel from lisp.cues.media_cue import MediaCue class CueListModel(ModelAdapter): def __init__(self, model): super().__init__(model) self.__cues = [] def item(self, index): return self.__cues[index] def insert(self, item, index): item.index = index self.add(item) def pop(self, index): cue = self.__cues[index] self.model.remove(cue) return cue def move(self, old_index, new_index): if old_index != new_index: if new_index >= len(self.__cues): new_index = len(self.__cues) - 1 cue = self.__cues.pop(old_index) self.__cues.insert(new_index, cue) if old_index < new_index: min_index = old_index max_index = new_index else: min_index = new_index max_index = old_index self._update_indices(min_index, max_index + 1) self.item_moved.emit(old_index, new_index) def _model_reset(self): self.__cues.clear() self.model_reset.emit() def _item_added(self, item): if not isinstance(item.index, int) or not 0 <= item.index <= len(self.__cues): item.index = len(self.__cues) self.__cues.insert(item.index, item) self._update_indices(item.index) self.item_added.emit(item) def _item_removed(self, item): self.__cues.pop(item.index) self._update_indices(item.index) self.item_removed.emit(item) def _update_indices(self, start, stop=-1): """Update the indices of cues from start to stop-1""" if not 0 <= stop <= len(self.__cues): stop = len(self.__cues) for index in range(start, stop): self.__cues[index].index = index def __iter__(self): return self.__cues.__iter__() class RunningCueModel(ReadOnlyProxyModel): def __init__(self, model): super().__init__(model) self.__playing = [] def _item_added(self, cue): cue.end.connect(self._remove) cue.started.connect(self._add) cue.error.connect(self._remove) cue.stopped.connect(self._remove) cue.interrupted.connect(self._remove) def _item_removed(self, cue): cue.end.disconnect(self._remove) cue.started.disconnect(self._add) cue.error.disconnect(self._remove) cue.stopped.disconnect(self._remove) cue.interrupted.disconnect(self._remove) self._remove(cue) def _model_reset(self): for cue in self.model: self._item_removed(cue) self.model_reset.emit() def _add(self, cue): if cue.duration > 0 and cue not in self.__playing: self.__playing.append(cue) self.item_added.emit(cue) def _remove(self, cue, *args): try: self.__playing.remove(cue) self.item_removed.emit(cue) except ValueError: pass def __len__(self): return len(self.__playing) def __iter__(self): yield from self.__playing def __contains__(self, item): return item in self.__playing linux-show-player-0.5.1/lisp/layouts/list_layout/cue_list_view.py000066400000000000000000000153311323636106000253610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtCore import pyqtSignal, Qt, QDataStream, QIODevice, \ QT_TRANSLATE_NOOP from PyQt5.QtGui import QKeyEvent, QContextMenuEvent, QMouseEvent from PyQt5.QtWidgets import QTreeWidget, QHeaderView, qApp from lisp.core.signal import Connection from lisp.cues.cue_factory import CueFactory from lisp.layouts.list_layout.cue_list_item import CueListItem from lisp.layouts.list_layout.listwidgets import CueStatusIcon, PreWaitWidget, \ CueTimeWidget, NextActionIcon, PostWaitWidget # TODO: here we should build a custom qt model/view from lisp.ui.ui_utils import translate class CueListView(QTreeWidget): key_event = pyqtSignal(QKeyEvent) context_event = pyqtSignal(QContextMenuEvent) select_cue_event = pyqtSignal(QMouseEvent) drop_move_event = QtCore.pyqtSignal(int, int) drop_copy_event = QtCore.pyqtSignal(int, int) HEADER_NAMES = ['', '#', QT_TRANSLATE_NOOP('ListLayoutHeader', 'Cue'), QT_TRANSLATE_NOOP('ListLayoutHeader', 'Pre wait'), QT_TRANSLATE_NOOP('ListLayoutHeader', 'Action'), QT_TRANSLATE_NOOP('ListLayoutHeader', 'Post wait'), ''] HEADER_WIDGETS = [CueStatusIcon, None, None, PreWaitWidget, CueTimeWidget, PostWaitWidget, NextActionIcon] def __init__(self, cue_model, parent=None): """ :type cue_model: lisp.layouts.list_layout.cue_list_model.CueListModel """ super().__init__(parent) self._model = cue_model self._model.item_added.connect(self.__cue_added, Connection.QtQueued) self._model.item_moved.connect(self.__cue_moved, Connection.QtQueued) self._model.item_removed.connect(self.__cue_removed, Connection.QtQueued) self._model.model_reset.connect(self.__model_reset) self.__item_moving = False self.setHeaderLabels( [translate('ListLayoutHeader', h) for h in CueListView.HEADER_NAMES]) self.header().setDragEnabled(False) self.header().setStretchLastSection(False) self.header().setSectionResizeMode(QHeaderView.Fixed) self.header().setSectionResizeMode(1, QHeaderView.ResizeToContents) self.header().setSectionResizeMode(2, QHeaderView.Stretch) self.setColumnWidth(0, 40) self.setColumnWidth(len(CueListView.HEADER_NAMES) - 1, 18) self.setSelectionMode(self.SingleSelection) self.setDragDropMode(self.InternalMove) self.setAlternatingRowColors(True) self.setVerticalScrollMode(self.ScrollPerItem) self.setIndentation(0) self.currentItemChanged.connect(self.__current_changed) self.__guard = False self.verticalScrollBar().rangeChanged.connect(self.__update_range) def dropEvent(self, event): # Decode mimedata information about the drag&drop event, since only # internal movement are allowed we assume the data format is correct data = event.mimeData().data('application/x-qabstractitemmodeldatalist') stream = QDataStream(data, QIODevice.ReadOnly) # Get the starting-item row start_index = stream.readInt() new_index = self.indexAt(event.pos()).row() if not 0 <= new_index <= len(self._model): new_index = len(self._model) if qApp.keyboardModifiers() == Qt.ControlModifier: cue = self._model.item(start_index) new_cue = CueFactory.clone_cue(cue) self._model.insert(new_cue, new_index) else: self._model.move(start_index, new_index) def contextMenuEvent(self, event): if self.itemAt(event.pos()) is not None: self.context_event.emit(event) else: super().contextMenuEvent(event) def keyPressEvent(self, event): self.key_event.emit(event) if qApp.keyboardModifiers() == Qt.ControlModifier: # Prevent items to be deselected event.ignore() else: super().keyPressEvent(event) def mousePressEvent(self, event): if qApp.keyboardModifiers() == Qt.ControlModifier: # For cue selection self.select_cue_event.emit(event) else: super().mousePressEvent(event) def mouseMoveEvent(self, event): super().mouseMoveEvent(event) if qApp.keyboardModifiers() == Qt.ControlModifier: # Prevent items to be deselected if self.state() == self.DragSelectingState: item = self.itemAt(event.pos()) self.setCurrentItem(item) def __current_changed(self, current_item, previous_item): self.scrollToItem(current_item) def __cue_added(self, cue): item = CueListItem(cue) item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled) self.insertTopLevelItem(cue.index, item) self.__init_item(item, cue) # Select the added item and scroll to it self.setCurrentItem(item) # Ensure that the focus is set self.setFocus() def __cue_moved(self, start, end): item = self.takeTopLevelItem(start) self.insertTopLevelItem(end, item) self.setCurrentItem(item) self.__init_item(item, self._model.item(end)) def __cue_removed(self, cue): self.takeTopLevelItem(cue.index) index = cue.index if index > 0: index -= 1 self.setCurrentIndex(self.model().index(index, 0)) def __model_reset(self): self.reset() self.clear() def __init_item(self, item, cue): item.name_column = CueListView.HEADER_NAMES.index('Cue') for index, widget in enumerate(CueListView.HEADER_WIDGETS): if widget is not None: self.setItemWidget(item, index, widget(cue)) self.updateGeometries() def __update_range(self, min_, max_): if not self.__guard: self.__guard = True self.verticalScrollBar().setMaximum(max_ + 1) self.__guard = False linux-show-player-0.5.1/lisp/layouts/list_layout/info_panel.py000066400000000000000000000052271323636106000246350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QWidget, QTextEdit, QLineEdit, QVBoxLayout from lisp.ui.ui_utils import translate class InfoPanel(QWidget): def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.__cue = None # cue name self.cueName = QLineEdit(self) self.cueName.setFocusPolicy(Qt.NoFocus) self.cueName.setReadOnly(True) self.layout().addWidget(self.cueName) # cue description self.cueDescription = QTextEdit(self) self.cueDescription.setObjectName('InfoPanelDescription') self.cueDescription.setFocusPolicy(Qt.NoFocus) self.cueDescription.setReadOnly(True) self.layout().addWidget(self.cueDescription) self.retranslateUi() def retranslateUi(self): self.cueName.setPlaceholderText( translate('ListLayoutInfoPanel', 'Cue name')) self.cueDescription.setPlaceholderText( translate('ListLayoutInfoPanel', 'Cue description')) def cue_changed(self, cue): if self.__cue is not None: self.__cue.changed('name').disconnect(self.__name_changed) self.__cue.changed('description').disconnect(self.__descr_changed) self.__cue = cue if self.__cue is not None: self.__cue.changed('name').connect(self.__name_changed) self.__cue.changed('description').connect(self.__descr_changed) self.__name_changed(self.__cue.name) self.__descr_changed(self.__cue.description) else: self.cueName.clear() self.cueDescription.clear() def __name_changed(self, name): self.cueName.setText(str(self.__cue.index) + ' - ' + name) def __descr_changed(self, description): self.cueDescription.setText(description) linux-show-player-0.5.1/lisp/layouts/list_layout/layout.py000066400000000000000000000401451323636106000240360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from enum import Enum from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtGui import QKeySequence from PyQt5.QtWidgets import QWidget, QAction, qApp, QGridLayout, \ QPushButton, QSizePolicy from lisp.core.configuration import config from lisp.core.signal import Connection from lisp.cues.cue import Cue, CueAction from lisp.cues.media_cue import MediaCue from lisp.layouts.cue_layout import CueLayout from lisp.layouts.list_layout.control_buttons import ShowControlButtons from lisp.layouts.list_layout.cue_list_model import CueListModel, \ RunningCueModel from lisp.layouts.list_layout.cue_list_view import CueListView from lisp.layouts.list_layout.info_panel import InfoPanel from lisp.layouts.list_layout.list_layout_settings import ListLayoutSettings from lisp.layouts.list_layout.playing_list_widget import RunningCuesListWidget from lisp.ui.mainwindow import MainWindow from lisp.ui.settings.app_settings import AppSettings from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.settings.pages.cue_appearance import Appearance from lisp.ui.settings.pages.cue_general import CueGeneralSettings from lisp.ui.settings.pages.media_cue_settings import MediaCueSettings from lisp.ui.ui_utils import translate AppSettings.register_settings_widget(ListLayoutSettings) class EndListBehavior(Enum): Stop = 'Stop' Restart = 'Restart' class ListLayout(QWidget, CueLayout): NAME = 'List Layout' DESCRIPTION = QT_TRANSLATE_NOOP('LayoutDescription', 'Organize the cues in a list') DETAILS = [ QT_TRANSLATE_NOOP('LayoutDetails', 'SHIFT + Space or Double-Click to edit a cue'), QT_TRANSLATE_NOOP('LayoutDetails', 'CTRL + Left Click to select cues'), QT_TRANSLATE_NOOP('LayoutDetails', 'To copy cues drag them while pressing CTRL'), QT_TRANSLATE_NOOP('LayoutDetails', 'To move cues drag them') ] def __init__(self, cue_model, **kwargs): super().__init__(cue_model=cue_model, **kwargs) self.setLayout(QGridLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self._model_adapter = CueListModel(self._cue_model) self._model_adapter.item_added.connect(self.__cue_added) self._model_adapter.item_removed.connect(self.__cue_removed) self._playing_model = RunningCueModel(self._cue_model) self._context_item = None self._next_cue_index = 0 self._show_dbmeter = config['ListLayout'].getboolean('ShowDbMeters') self._seek_visible = config['ListLayout'].getboolean('ShowSeek') self._accurate_time = config['ListLayout'].getboolean('ShowAccurate') self._auto_continue = config['ListLayout'].getboolean('AutoContinue') self._show_playing = config['ListLayout'].getboolean('ShowPlaying') self._go_key = config['ListLayout']['GoKey'] self._go_key_sequence = QKeySequence(self._go_key, QKeySequence.NativeText) try: self._end_list = EndListBehavior(config['ListLayout']['EndList']) except ValueError: self._end_list = EndListBehavior.Stop # Add layout-specific menus self.showPlayingAction = QAction(self) self.showPlayingAction.setCheckable(True) self.showPlayingAction.setChecked(self._show_playing) self.showPlayingAction.triggered.connect(self.set_playing_visible) self.showDbMeterAction = QAction(self) self.showDbMeterAction.setCheckable(True) self.showDbMeterAction.setChecked(self._show_dbmeter) self.showDbMeterAction.triggered.connect(self.set_dbmeter_visible) self.showSeekAction = QAction(self) self.showSeekAction.setCheckable(True) self.showSeekAction.setChecked(self._seek_visible) self.showSeekAction.triggered.connect(self.set_seek_visible) self.accurateTimingAction = QAction(self) self.accurateTimingAction.setCheckable(True) self.accurateTimingAction.setChecked(self._accurate_time) self.accurateTimingAction.triggered.connect(self.set_accurate_time) self.autoNextAction = QAction(self) self.autoNextAction.setCheckable(True) self.autoNextAction.setChecked(self._auto_continue) self.autoNextAction.triggered.connect(self.set_auto_next) MainWindow().menuLayout.addAction(self.showPlayingAction) MainWindow().menuLayout.addAction(self.showDbMeterAction) MainWindow().menuLayout.addAction(self.showSeekAction) MainWindow().menuLayout.addAction(self.accurateTimingAction) MainWindow().menuLayout.addAction(self.autoNextAction) # GO-BUTTON (top-left) self.goButton = QPushButton('GO', self) self.goButton.setFocusPolicy(Qt.NoFocus) self.goButton.setFixedWidth(120) self.goButton.setFixedHeight(100) self.goButton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.goButton.setStyleSheet('font-size: 48pt;') self.goButton.clicked.connect(self.__go_slot) self.layout().addWidget(self.goButton, 0, 0) # INFO PANEL (top center) self.infoPanel = InfoPanel() self.infoPanel.setFixedHeight(100) self.layout().addWidget(self.infoPanel, 0, 1) # CONTROL-BUTTONS (top-right) self.controlButtons = ShowControlButtons(parent=self) self.controlButtons.setFixedHeight(100) self.controlButtons.stopButton.clicked.connect(self.stop_all) self.controlButtons.pauseButton.clicked.connect(self.pause_all) self.controlButtons.fadeInButton.clicked.connect(self.fadein_all) self.controlButtons.fadeOutButton.clicked.connect(self.fadeout_all) self.controlButtons.resumeButton.clicked.connect(self.restart_all) self.controlButtons.interruptButton.clicked.connect(self.interrupt_all) self.layout().addWidget(self.controlButtons, 0, 2) # CUE VIEW (center left) self.listView = CueListView(self._model_adapter, self) self.listView.itemDoubleClicked.connect(self.double_clicked) self.listView.currentItemChanged.connect(self.__current_changed) self.listView.context_event.connect(self.context_event) self.listView.key_event.connect(self.onKeyPressEvent) self.listView.select_cue_event.connect(self.select_event) self.layout().addWidget(self.listView, 1, 0, 1, 2) # PLAYING VIEW (center right) self.playView = RunningCuesListWidget(self._playing_model, parent=self) self.playView.dbmeter_visible = self._show_dbmeter self.playView.accurate_time = self._accurate_time self.playView.seek_visible = self._seek_visible self.playView.setMinimumWidth(300) self.playView.setMaximumWidth(300) self.layout().addWidget(self.playView, 1, 2) self.set_playing_visible(self._show_playing) # TODO: maybe can be moved outside the layout # Add cue preferences widgets CueSettingsRegistry().add_item(CueGeneralSettings, Cue) CueSettingsRegistry().add_item(MediaCueSettings, MediaCue) CueSettingsRegistry().add_item(Appearance) # Context menu actions self.edit_action = QAction(self) self.edit_action.triggered.connect(self.edit_context_cue) self.remove_action = QAction(self) self.remove_action.triggered.connect(self.remove_context_cue) self.select_action = QAction(self) self.select_action.triggered.connect(self.select_context_cue) self.cm_registry.add_item(self.edit_action) self.sep1 = self.cm_registry.add_separator() self.cm_registry.add_item(self.remove_action) self.cm_registry.add_item(self.select_action) self.retranslateUi() def retranslateUi(self): self.showPlayingAction.setText( translate('ListLayout', 'Show playing cues')) self.showDbMeterAction.setText( translate('ListLayout', 'Show dB-meters')) self.showSeekAction.setText(translate('ListLayout', 'Show seek-bars')) self.accurateTimingAction.setText( translate('ListLayout', 'Show accurate time')) self.autoNextAction.setText( translate('ListLayout', 'Auto-select next cue')) self.edit_action.setText(translate('ListLayout', 'Edit cue')) self.remove_action.setText(translate('ListLayout', 'Remove')) self.select_action.setText(translate('ListLayout', 'Select')) @CueLayout.model_adapter.getter def model_adapter(self): return self._model_adapter def current_index(self): return self.listView.currentIndex().row() def set_current_index(self, index): if self._end_list == EndListBehavior.Restart: index %= len(self.model_adapter) if 0 <= index < self.listView.topLevelItemCount(): next_item = self.listView.topLevelItem(index) self.listView.setCurrentItem(next_item) def go(self, action=CueAction.Default, advance=1): current_cue = self.current_cue() if current_cue is not None: current_cue.execute(action) self.cue_executed.emit(current_cue) if self._auto_continue: self.set_current_index(self.current_index() + advance) def current_item(self): if self._model_adapter: return self.listView.currentItem() def select_context_cue(self): self._context_item.selected = not self._context_item.selected def set_accurate_time(self, accurate): self._accurate_time = accurate self.playView.accurate_time = accurate def set_auto_next(self, enable): self._auto_continue = enable def set_seek_visible(self, visible): self._seek_visible = visible self.playView.seek_visible = visible def set_dbmeter_visible(self, visible): self._show_dbmeter = visible self.playView.dbmeter_visible = visible def set_playing_visible(self, visible): self._show_playing = visible self.playView.setVisible(visible) self.controlButtons.setVisible(visible) def onKeyPressEvent(self, e): if not e.isAutoRepeat(): keys = e.key() modifiers = e.modifiers() if modifiers & Qt.ShiftModifier: keys += Qt.SHIFT if modifiers & Qt.ControlModifier: keys += Qt.CTRL if modifiers & Qt.AltModifier: keys += Qt.ALT if modifiers & Qt.MetaModifier: keys += Qt.META if QKeySequence(keys) in self._go_key_sequence: self.go() elif e.key() == Qt.Key_Space: if qApp.keyboardModifiers() == Qt.ShiftModifier: cue = self.current_cue() if cue is not None: self.edit_cue(cue) elif qApp.keyboardModifiers() == Qt.ControlModifier: item = self.current_item() if item is not None: item.selected = not item.selected else: self.key_pressed.emit(e) e.accept() def double_clicked(self, event): cue = self.current_cue() if cue is not None: self.edit_cue(cue) def select_event(self, event): item = self.listView.itemAt(event.pos()) if item is not None: item.selected = not item.selected def context_event(self, event): self._context_item = self.listView.itemAt(event.pos()) if self._context_item is not None: self.show_cue_context_menu(event.globalPos()) def contextMenuEvent(self, event): if self.listView.geometry().contains(event.pos()): self.show_context_menu(event.globalPos()) def remove_context_cue(self): self._model_adapter.remove(self.get_context_cue()) def edit_context_cue(self): self.edit_cue(self.get_context_cue()) def stop_all(self): fade = config['ListLayout'].getboolean('StopAllFade') for cue in self._model_adapter: cue.stop(fade=fade) def interrupt_all(self): fade = config['ListLayout'].getboolean('InterruptAllFade') for cue in self._model_adapter: cue.interrupt(fade=fade) def pause_all(self): fade = config['ListLayout'].getboolean('PauseAllFade') for cue in self._model_adapter: cue.pause(fade=fade) def restart_all(self): # TODO: change to resume fade = config['ListLayout'].getboolean('ResumeAllFade') for cue in self._model_adapter: cue.restart(fade=fade) def fadein_all(self): for cue in self._model_adapter: cue.execute(CueAction.FadeIn) def fadeout_all(self): for cue in self._model_adapter: cue.execute(CueAction.FadeOut) def get_selected_cues(self, cue_class=Cue): cues = [] for index in range(self.listView.topLevelItemCount()): item = self.listView.topLevelItem(index) if item.selected and isinstance(item.cue, cue_class): cues.append(item.cue) return cues def finalize(self): MainWindow().menuLayout.clear() # Disconnect menu-actions signals self.edit_action.triggered.disconnect() self.remove_action.triggered.disconnect() self.select_action.triggered.disconnect() # Remove context-items self.cm_registry.remove_item(self.edit_action) self.cm_registry.remove_item(self.sep1) self.cm_registry.remove_item(self.remove_action) self.cm_registry.remove_item(self.select_action) # Delete the layout self.deleteLater() def get_context_cue(self): return self._context_item.cue def select_all(self, cue_class=Cue): for index in range(self.listView.topLevelItemCount()): if isinstance(self.model_adapter.item(index), cue_class): self.listView.topLevelItem(index).selected = True def deselect_all(self, cue_class=Cue): for index in range(self.listView.topLevelItemCount()): if isinstance(self.model_adapter.item(index), cue_class): self.listView.topLevelItem(index).selected = False def invert_selection(self): for index in range(self.listView.topLevelItemCount()): item = self.listView.topLevelItem(index) item.selected = not item.selected def __go_slot(self): self.go() def __current_changed(self, new_item, current_item): try: index = self.listView.indexOfTopLevelItem(new_item) cue = self.model_adapter.item(index) self.infoPanel.cue_changed(cue) except IndexError: self.infoPanel.cue_changed(None) def __cue_added(self, cue): cue.next.connect(self.__cue_next, Connection.QtQueued) def __cue_removed(self, cue): if isinstance(cue, MediaCue): cue.media.interrupt() else: cue.stop() def __cue_next(self, cue): try: next_index = cue.index + 1 if next_index < len(self._model_adapter): next_cue = self._model_adapter.item(next_index) next_cue.execute() if self._auto_continue and next_cue == self.current_cue(): self.set_current_index(next_index + 1) except(IndexError, KeyError): pass linux-show-player-0.5.1/lisp/layouts/list_layout/list_layout_settings.py000066400000000000000000000200541323636106000270060ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeySequence from PyQt5.QtWidgets import QDoubleSpinBox from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QCheckBox, QComboBox, \ QHBoxLayout, QLabel, QKeySequenceEdit, QGridLayout from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate from lisp.ui.widgets import FadeComboBox class ListLayoutSettings(SettingsPage): Name = 'List Layout' def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.behaviorsGroup = QGroupBox(self) self.behaviorsGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.behaviorsGroup) self.showPlaying = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.showPlaying) self.showDbMeters = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.showDbMeters) self.showAccurate = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.showAccurate) self.showSeek = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.showSeek) self.autoNext = QCheckBox(self.behaviorsGroup) self.behaviorsGroup.layout().addWidget(self.autoNext) self.endListLayout = QHBoxLayout() self.behaviorsGroup.layout().addLayout(self.endListLayout) self.endListLabel = QLabel(self.behaviorsGroup) self.endListLayout.addWidget(self.endListLabel) self.endListBehavior = QComboBox(self.behaviorsGroup) self.endListBehavior.addItem(translate('ListLayout', 'Stop'), 'Stop') self.endListBehavior.addItem(translate('ListLayout', 'Restart'), 'Restart') self.endListLayout.addWidget(self.endListBehavior) self.endListLayout.setStretch(0, 2) self.endListLayout.setStretch(1, 5) self.goKeyLayout = QHBoxLayout() self.behaviorsGroup.layout().addLayout(self.goKeyLayout) self.goKeyLabel = QLabel(self.behaviorsGroup) self.goKeyLayout.addWidget(self.goKeyLabel) self.goKeyEdit = QKeySequenceEdit(self.behaviorsGroup) self.goKeyLayout.addWidget(self.goKeyEdit) self.goKeyLayout.setStretch(0, 2) self.goKeyLayout.setStretch(1, 5) self.useFadeGroup = QGroupBox(self) self.useFadeGroup.setLayout(QGridLayout()) self.layout().addWidget(self.useFadeGroup) # per-cue self.stopCueFade = QCheckBox(self.useFadeGroup) self.useFadeGroup.layout().addWidget(self.stopCueFade, 0, 0) self.pauseCueFade = QCheckBox(self.useFadeGroup) self.useFadeGroup.layout().addWidget(self.pauseCueFade, 1, 0) self.resumeCueFade = QCheckBox(self.useFadeGroup) self.useFadeGroup.layout().addWidget(self.resumeCueFade, 2, 0) self.interruptCueFade = QCheckBox(self.useFadeGroup) self.useFadeGroup.layout().addWidget(self.interruptCueFade, 3, 0) # all self.stopAllFade = QCheckBox(self.useFadeGroup) self.useFadeGroup.layout().addWidget(self.stopAllFade, 0, 1) self.pauseAllFade = QCheckBox(self.useFadeGroup) self.useFadeGroup.layout().addWidget(self.pauseAllFade, 1, 1) self.resumeAllFade = QCheckBox(self.useFadeGroup) self.useFadeGroup.layout().addWidget(self.resumeAllFade, 2, 1) self.interruptAllFade = QCheckBox(self.useFadeGroup) self.useFadeGroup.layout().addWidget(self.interruptAllFade, 3, 1) self.retranslateUi() def retranslateUi(self): self.behaviorsGroup.setTitle( translate('ListLayout', 'Default behaviors')) self.showPlaying.setText(translate('ListLayout', 'Show playing cues')) self.showDbMeters.setText(translate('ListLayout', 'Show dB-meters')) self.showAccurate.setText(translate('ListLayout', 'Show accurate time')) self.showSeek.setText(translate('ListLayout', 'Show seek-bars')) self.autoNext.setText(translate('ListLayout', 'Auto-select next cue')) self.endListLabel.setText(translate('ListLayout', 'At list end:')) self.goKeyLabel.setText(translate('ListLayout', 'Go key:')) self.useFadeGroup.setTitle(translate('ListLayout', 'Use fade')) self.stopCueFade.setText(translate('ListLayout', 'Stop Cue')) self.pauseCueFade.setText(translate('ListLayout', 'Pause Cue')) self.resumeCueFade.setText(translate('ListLayout', 'Resume Cue')) self.interruptCueFade.setText(translate('ListLayout', 'Interrupt Cue')) self.stopAllFade.setText(translate('ListLayout', 'Stop All')) self.pauseAllFade.setText(translate('ListLayout', 'Pause All')) self.resumeAllFade.setText(translate('ListLayout', 'Resume All')) self.interruptAllFade.setText(translate('ListLayout', 'Interrupt All')) def get_settings(self): settings = { 'showplaying': str(self.showPlaying.isChecked()), 'showdbmeters': str(self.showDbMeters.isChecked()), 'showseek': str(self.showSeek.isChecked()), 'showaccurate': str(self.showAccurate.isChecked()), 'autocontinue': str(self.autoNext.isChecked()), 'endlist': str(self.endListBehavior.currentData()), 'gokey': self.goKeyEdit.keySequence().toString( QKeySequence.NativeText), 'stopcuefade': str(self.stopCueFade.isChecked()), 'pausecuefade': str(self.pauseCueFade.isChecked()), 'resumecuefade': str(self.resumeCueFade.isChecked()), 'interruptcuefade': str(self.interruptCueFade.isChecked()), 'stopallfade': str(self.stopAllFade.isChecked()), 'pauseallfade': str(self.pauseAllFade.isChecked()), 'resumeallfade': str(self.resumeAllFade.isChecked()), 'interruptallfade': str(self.interruptAllFade.isChecked()), } return {'ListLayout': settings} def load_settings(self, settings): settings = settings.get('ListLayout', {}) self.showPlaying.setChecked(settings.get('showplaying') == 'True') self.showDbMeters.setChecked(settings.get('showdbmeters') == 'True') self.showAccurate.setChecked(settings.get('showaccurate') == 'True') self.showSeek.setChecked(settings.get('showseek') == 'True') self.autoNext.setChecked(settings.get('autocontinue') == 'True') self.endListBehavior.setCurrentText( translate('ListLayout', settings.get('endlist', ''))) self.goKeyEdit.setKeySequence( QKeySequence(settings.get('gokey', 'Space'), QKeySequence.NativeText)) self.stopCueFade.setChecked(settings.get('stopcuefade') == 'True') self.pauseCueFade.setChecked(settings.get('pausecuefade') == 'True') self.resumeCueFade.setChecked(settings.get('resumecuefade') == 'True') self.interruptCueFade.setChecked( settings.get('interruptcuefade') == 'True') self.stopAllFade.setChecked(settings.get('stopallfade') == 'True') self.pauseAllFade.setChecked(settings.get('pauseallfade') == 'True') self.resumeAllFade.setChecked(settings.get('resumeallfade') == 'True') self.interruptAllFade.setChecked( settings.get('interruptallfade') == 'True')linux-show-player-0.5.1/lisp/layouts/list_layout/listwidgets.py000066400000000000000000000230601323636106000250600ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QSize from PyQt5.QtWidgets import QLabel, QProgressBar from lisp.core.signal import Connection from lisp.core.util import strtime from lisp.cues.cue import CueNextAction, CueState from lisp.cues.cue_time import CueTime, CueWaitTime from lisp.ui.ui_utils import pixmap_from_icon class CueStatusIcon(QLabel): STYLESHEET = 'background: transparent; padding-left: 20px;' SIZE = 16 def __init__(self, cue, *args): super().__init__(*args) self.setStyleSheet(CueStatusIcon.STYLESHEET) self.cue = cue self.cue.interrupted.connect(self._stop, Connection.QtQueued) self.cue.started.connect(self._start, Connection.QtQueued) self.cue.stopped.connect(self._stop, Connection.QtQueued) self.cue.paused.connect(self._pause, Connection.QtQueued) self.cue.error.connect(self._error, Connection.QtQueued) self.cue.end.connect(self._stop, Connection.QtQueued) def _start(self): self.setPixmap(pixmap_from_icon('led-running', self.SIZE)) def _pause(self): self.setPixmap(pixmap_from_icon('led-pause', self.SIZE)) def _error(self, *args): self.setPixmap(pixmap_from_icon('led-error', self.SIZE)) def _stop(self): self.setPixmap(pixmap_from_icon('', self.SIZE)) def sizeHint(self): return QSize(self.SIZE, self.SIZE) class NextActionIcon(QLabel): STYLESHEET = 'background: transparent; padding-left: 1px' SIZE = 16 def __init__(self, cue, *args): super().__init__(*args) self.setStyleSheet(self.STYLESHEET) self.cue = cue self.cue.changed('next_action').connect( self._update_icon, Connection.QtQueued) self._update_icon(self.cue.next_action) def _update_icon(self, next_action): next_action = CueNextAction(next_action) pixmap = pixmap_from_icon('', self.SIZE) if next_action == CueNextAction.AutoNext: pixmap = pixmap_from_icon('auto-next', self.SIZE) self.setToolTip(CueNextAction.AutoNext.value) elif next_action == CueNextAction.AutoFollow: pixmap = pixmap_from_icon('auto-follow', self.SIZE) self.setToolTip(CueNextAction.AutoFollow.value) else: self.setToolTip('') self.setPixmap(pixmap) def sizeHint(self): return QSize(self.SIZE + 2, self.SIZE) class TimeWidget(QProgressBar): def __init__(self, cue, *args): super().__init__(*args) self.setObjectName('ListTimeWidget') self.setValue(0) self.setTextVisible(True) self.show_zero_duration = False self.accurate_time = True self.cue = cue def _update_time(self, time): self.setValue(time) self.setFormat(strtime(time, accurate=self.accurate_time)) def _update_duration(self, duration): if duration > 0 or self.show_zero_duration: # Display as disabled if duration < 0 self.setEnabled(duration > 0) self.setTextVisible(True) self.setFormat(strtime(duration, accurate=self.accurate_time)) # Avoid settings min and max to 0, or the the bar go in busy state self.setRange(0 if duration > 0 else -1, duration) else: self.setTextVisible(False) def _update_style(self, state): self.setProperty('state', state) self.style().unpolish(self) self.style().polish(self) self.update() def _running(self): self._update_style('running') def _pause(self): self._update_style('pause') self._update_time(self.value()) def _stop(self): self._update_style('stop') self.setValue(self.minimum()) def _error(self): self._update_style('error') self.setValue(self.minimum()) class CueTimeWidget(TimeWidget): def __init__(self, cue, *args): super().__init__(cue, *args) self.cue.interrupted.connect(self._stop, Connection.QtQueued) self.cue.started.connect(self._running, Connection.QtQueued) self.cue.stopped.connect(self._stop, Connection.QtQueued) self.cue.paused.connect(self._pause, Connection.QtQueued) self.cue.error.connect(self._error, Connection.QtQueued) self.cue.end.connect(self._stop, Connection.QtQueued) self.cue.changed('duration').connect( self._update_duration, Connection.QtQueued) self.cue_time = CueTime(self.cue) self.cue_time.notify.connect(self._update_time, Connection.QtQueued) if cue.state & CueState.Running: self._running() elif cue.state & CueState.Pause: self._pause() elif cue.state & CueState.Error: self._error() else: self._stop() def _stop(self): super()._stop() self._update_duration(self.cue.duration) class PreWaitWidget(TimeWidget): def __init__(self, cue, *args): super().__init__(cue, *args) self.show_zero_duration = True self.cue.prewait_start.connect(self._running, Connection.QtQueued) self.cue.prewait_stopped.connect(self._stop, Connection.QtQueued) self.cue.prewait_paused.connect(self._pause, Connection.QtQueued) self.cue.prewait_ended.connect(self._stop, Connection.QtQueued) self.cue.changed('pre_wait').connect( self._update_duration, Connection.QtQueued) self._update_duration(self.cue.pre_wait) self.wait_time = CueWaitTime(self.cue, mode=CueWaitTime.Mode.Pre) self.wait_time.notify.connect(self._update_time, Connection.QtQueued) def _update_duration(self, duration): # The wait time is in seconds, we need milliseconds super()._update_duration(duration * 1000) def _stop(self): super()._stop() self._update_duration(self.cue.pre_wait) class PostWaitWidget(TimeWidget): def __init__(self, cue, *args): super().__init__(cue, *args) self.show_zero_duration = True self.cue.changed('next_action').connect( self._next_action_changed, Connection.QtQueued) self.wait_time = CueWaitTime(self.cue, mode=CueWaitTime.Mode.Post) self.cue_time = CueTime(self.cue) self._next_action_changed(self.cue.next_action) def _update_duration(self, duration): if self.cue.next_action != CueNextAction.AutoFollow.value: # The wait time is in seconds, we need milliseconds duration *= 1000 super()._update_duration(duration) def _next_action_changed(self, next_action): self.cue.postwait_start.disconnect(self._running) self.cue.postwait_stopped.disconnect(self._stop) self.cue.postwait_paused.disconnect(self._pause) self.cue.postwait_ended.disconnect(self._stop) self.cue.interrupted.disconnect(self._stop) self.cue.started.disconnect(self._running) self.cue.stopped.disconnect(self._stop) self.cue.paused.disconnect(self._pause) self.cue.error.disconnect(self._stop) self.cue.end.disconnect(self._stop) self.cue_time.notify.disconnect(self._update_time) self.wait_time.notify.disconnect(self._update_time) self.cue.changed('post_wait').disconnect(self._update_duration) self.cue.changed('duration').disconnect(self._update_duration) if next_action == CueNextAction.AutoFollow.value: self.cue.interrupted.connect(self._stop, Connection.QtQueued) self.cue.started.connect(self._running, Connection.QtQueued) self.cue.stopped.connect(self._stop, Connection.QtQueued) self.cue.paused.connect(self._pause, Connection.QtQueued) self.cue.error.connect(self._stop, Connection.QtQueued) self.cue.end.connect(self._stop, Connection.QtQueued) self.cue.changed('duration').connect( self._update_duration, Connection.QtQueued) self.cue_time.notify.connect(self._update_time, Connection.QtQueued) self._update_duration(self.cue.duration) else: self.cue.postwait_start.connect(self._running, Connection.QtQueued) self.cue.postwait_stopped.connect(self._stop, Connection.QtQueued) self.cue.postwait_paused.connect(self._pause, Connection.QtQueued) self.cue.postwait_ended.connect(self._stop, Connection.QtQueued) self.cue.changed('post_wait').connect( self._update_duration, Connection.QtQueued) self.wait_time.notify.connect( self._update_time, Connection.QtQueued) self._update_duration(self.cue.post_wait) def _stop(self): super()._stop() if self.cue.next_action == CueNextAction.AutoFollow.value: self._update_duration(self.cue.duration) else: self._update_duration(self.cue.post_wait)linux-show-player-0.5.1/lisp/layouts/list_layout/playing_list_widget.py000066400000000000000000000071421323636106000265620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QListWidget, QListWidgetItem from lisp.core.signal import Connection from lisp.layouts.list_layout.playing_mediawidget import get_running_widget class RunningCuesListWidget(QListWidget): def __init__(self, running_model, **kwargs): super().__init__(**kwargs) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setFocusPolicy(Qt.NoFocus) self.setSelectionMode(self.NoSelection) self._running_cues = {} self._running_model = running_model self._running_model.item_added.connect( self._item_added, Connection.QtQueued) self._running_model.item_removed.connect( self._item_removed, Connection.QtQueued) self._running_model.model_reset.connect( self._model_reset, Connection.QtQueued) self.__dbmeter_visible = False self.__seek_visible = False self.__accurate_time = False @property def dbmeter_visible(self): return self.__dbmeter_visible @dbmeter_visible.setter def dbmeter_visible(self, visible): self.__dbmeter_visible = visible for item in self._running_cues.values(): try: self.itemWidget(item).set_dbmeter_visible(visible) except AttributeError: pass @property def seek_visible(self): return self.__seek_visible @seek_visible.setter def seek_visible(self, visible): self.__seek_visible = visible for item in self._running_cues.values(): try: self.itemWidget(item).set_seek_visible(visible) except AttributeError: pass @property def accurate_time(self): return self.__accurate_time @accurate_time.setter def accurate_time(self, accurate): self.__accurate_time = accurate for item in self._running_cues.values(): self.itemWidget(item).set_accurate_time(accurate) def _item_added(self, cue): widget = get_running_widget(cue, parent=self) widget.set_accurate_time(self.__accurate_time) try: widget.set_dbmeter_visible(self.__dbmeter_visible) widget.set_seek_visible(self.__seek_visible) except AttributeError: pass item = QListWidgetItem() item.setSizeHint(widget.size()) self.addItem(item) self.setItemWidget(item, widget) self._running_cues[cue] = item def _item_removed(self, cue): item = self._running_cues.pop(cue) widget = self.itemWidget(item) row = self.indexFromItem(item).row() self.removeItemWidget(item) self.takeItem(row) widget.deleteLater() def _model_reset(self): for cue in list(self._running_cues.keys()): self._item_removed(cue) linux-show-player-0.5.1/lisp/layouts/list_layout/playing_mediawidget.py000066400000000000000000000212301323636106000265210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel, QSizePolicy, \ QLCDNumber, QHBoxLayout from lisp.cues.cue import CueAction from lisp.core.configuration import config from lisp.core.signal import Connection from lisp.core.util import strtime from lisp.cues.cue_time import CueTime from lisp.cues.media_cue import MediaCue from lisp.layouts.list_layout.control_buttons import CueControlButtons from lisp.ui.widgets import QClickSlider, QDbMeter def get_running_widget(cue, **kwargs): if isinstance(cue, MediaCue): return RunningMediaCueWidget(cue, **kwargs) else: return RunningCueWidget(cue, **kwargs) class RunningCueWidget(QWidget): def __init__(self, cue, **kwargs): super().__init__(**kwargs) self.setGeometry(0, 0, self.parent().viewport().width(), 80) self.setFocusPolicy(Qt.NoFocus) self.setLayout(QHBoxLayout(self)) self.layout().setContentsMargins(0, 0, 0, 1) self._accurate_time = False self.cue = cue self.cue_time = CueTime(cue) self.cue_time.notify.connect(self._time_updated, Connection.QtQueued) # Use this to avoid transparent background self.gridLayoutWidget = QWidget(self) self.gridLayout = QGridLayout(self.gridLayoutWidget) self.gridLayout.setContentsMargins(3, 3, 3, 3) self.gridLayout.setSpacing(2) self.layout().addWidget(self.gridLayoutWidget) self.nameLabel = QLabel(self.gridLayoutWidget) self.nameLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.nameLabel.setText(cue.name) self.nameLabel.setToolTip(cue.name) self.gridLayout.addWidget(self.nameLabel, 0, 0, 1, 2) self.controlButtons = CueControlButtons(parent=self.gridLayoutWidget) self.gridLayout.addWidget(self.controlButtons, 1, 0) if CueAction.Stop in cue.CueActions: self.controlButtons.stopButton.clicked.connect(self._stop) else: self.controlButtons.stopButton.setEnabled(False) if CueAction.Pause in cue.CueActions: self.controlButtons.pauseButton.clicked.connect(self._pause) self.controlButtons.startButton.clicked.connect(self._resume) else: self.controlButtons.pauseButton.setEnabled(False) self.controlButtons.startButton.setEnabled(False) if CueAction.FadeIn in cue.CueActions: self.controlButtons.fadeInButton.clicked.connect(self._fadein) else: self.controlButtons.fadeInButton.setEnabled(False) if CueAction.FadeOut in cue.CueActions: self.controlButtons.fadeOutButton.clicked.connect(self._fadeout) else: self.controlButtons.fadeOutButton.setEnabled(False) if CueAction.Interrupt in cue.CueActions: self.controlButtons.interruptButton.clicked.connect(self._interrupt) else: self.controlButtons.interruptButton.setEnabled(False) self.timeDisplay = QLCDNumber(self.gridLayoutWidget) self.timeDisplay.setStyleSheet('background-color: transparent') self.timeDisplay.setSegmentStyle(QLCDNumber.Flat) self.timeDisplay.setDigitCount(8) self.timeDisplay.display(strtime(cue.duration)) self.gridLayout.addWidget(self.timeDisplay, 1, 1) self.gridLayout.setRowStretch(0, 1) self.gridLayout.setRowStretch(1, 3) self.gridLayout.setColumnStretch(0, 7) self.gridLayout.setColumnStretch(1, 5) cue.changed('name').connect(self.name_changed, Connection.QtQueued) cue.started.connect(self.controlButtons.pauseMode, Connection.QtQueued) cue.paused.connect(self.controlButtons.startMode, Connection.QtQueued) # Fade enter/exit cue.fadein_start.connect(self.enter_fadein, Connection.QtQueued) cue.fadein_end.connect(self.exit_fade, Connection.QtQueued) cue.fadeout_start.connect(self.enter_fadeout, Connection.QtQueued) cue.fadeout_end.connect(self.exit_fade, Connection.QtQueued) def enter_fadein(self): p = self.timeDisplay.palette() p.setColor(p.Text, QColor(0, 255, 0)) self.timeDisplay.setPalette(p) def enter_fadeout(self): p = self.timeDisplay.palette() p.setColor(p.Text, QColor(255, 50, 50)) self.timeDisplay.setPalette(p) def exit_fade(self): self.timeDisplay.setPalette(self.palette()) def name_changed(self, name): self.nameLabel.setText(name) self.nameLabel.setToolTip(name) def set_accurate_time(self, enable): self._accurate_time = enable def _time_updated(self, time): if not self.visibleRegion().isEmpty(): # If the given value is the duration or < 0 set the time to 0 if time == self.cue.duration or time < 0: time = 0 self._update_timers(time) def _update_timers(self, time): self.timeDisplay.display(strtime(self.cue.duration - time, accurate=self._accurate_time)) def _pause(self): self.cue.pause(fade=config['ListLayout'].getboolean('PauseCueFade')) def _resume(self): self.cue.restart(fade=config['ListLayout'].getboolean('ResumeCueFade')) def _stop(self): self.cue.stop(fade=config['ListLayout'].getboolean('StopCueFade')) def _interrupt(self): self.cue.interrupt( fade=config['ListLayout'].getboolean('InterruptCueFade')) def _fadeout(self): self.cue.execute(CueAction.FadeOut) def _fadein(self): self.cue.execute(CueAction.FadeIn) class RunningMediaCueWidget(RunningCueWidget): def __init__(self, cue, **kwargs): super().__init__(cue, **kwargs) self.setGeometry(0, 0, self.width(), 110) self._dbmeter_element = None self.seekSlider = QClickSlider(self.gridLayoutWidget) self.seekSlider.setOrientation(Qt.Horizontal) self.seekSlider.setRange(0, cue.duration) self.seekSlider.setFocusPolicy(Qt.NoFocus) self.seekSlider.sliderMoved.connect(self._seek) self.seekSlider.sliderJumped.connect(self._seek) self.seekSlider.setVisible(False) self.dbmeter = QDbMeter(self.gridLayoutWidget) self.dbmeter.setVisible(False) cue.changed('duration').connect(self._update_duration, Connection.QtQueued) def set_seek_visible(self, visible): if visible and not self.seekSlider.isVisible(): self.gridLayout.addWidget(self.seekSlider, 2, 0, 1, 2) self.gridLayout.setRowStretch(2, 1) elif not visible and self.seekSlider.isVisible(): self.gridLayout.removeWidget(self.seekSlider) self.gridLayout.setRowStretch(2, 0) self.seekSlider.setVisible(visible) def set_dbmeter_visible(self, visible): if self._dbmeter_element is not None: self._dbmeter_element.level_ready.disconnect(self.dbmeter.plot) self._dbmeter_element = None if visible: self._dbmeter_element = self.cue.media.element('DbMeter') if self._dbmeter_element is not None: self._dbmeter_element.level_ready.connect(self.dbmeter.plot) # Add/Remove the QDbMeter in the layout if visible and not self.dbmeter.isVisible(): self.gridLayout.addWidget(self.dbmeter, 0, 2, 3, 1) self.gridLayout.setColumnStretch(2, 1) elif not visible and self.dbmeter.isVisible(): self.gridLayout.removeWidget(self.dbmeter) self.gridLayout.setColumnStretch(2, 0) self.dbmeter.setVisible(visible) def _seek(self, position): self.cue.media.seek(position) def _update_duration(self, duration): self.seekSlider.setMaximum(duration) def _update_timers(self, time): super()._update_timers(time) self.seekSlider.setValue(time)linux-show-player-0.5.1/lisp/main.py000066400000000000000000000101721323636106000173720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import argparse import logging import sys from itertools import chain from os import path from PyQt5.QtCore import QTranslator, QLocale, QLibraryInfo from PyQt5.QtGui import QFont, QIcon from PyQt5.QtWidgets import QApplication from lisp import modules from lisp import plugins from lisp.application import Application from lisp.core.configuration import config from lisp.ui.styles import styles def main(): # Parse the command-line arguments parser = argparse.ArgumentParser(description='Linux Show Player') parser.add_argument('-f', '--file', default='', nargs='?', const='', help='Session file path') parser.add_argument('-l', '--log', choices=['debug', 'info', 'warning'], default='warning', help='Log level') parser.add_argument('--locale', default='', help='Force specified locale') args = parser.parse_args() # Set the logging level if args.log == 'debug': log = logging.DEBUG # If something bad happen at low-level (e.g. segfault) print the stack import faulthandler faulthandler.enable() elif args.log == 'info': log = logging.INFO else: log = logging.WARNING logging.basicConfig( format='%(asctime)s.%(msecs)03d %(levelname)s:: %(message)s', datefmt='%H:%M:%S', level=log ) # Create the QApplication qt_app = QApplication(sys.argv) qt_app.setApplicationName('Linux Show Player') qt_app.setQuitOnLastWindowClosed(True) # Force light font, for environment with "bad" QT support. appFont = qt_app.font() appFont.setWeight(QFont.Light) qt_app.setFont(appFont) # Set icons and theme from the application configuration QIcon.setThemeSearchPaths(styles.IconsThemePaths) QIcon.setThemeName(config['Theme']['icons']) styles.apply_style(config['Theme']['theme']) # Set application icon (from the theme) qt_app.setWindowIcon(QIcon.fromTheme('linux-show-player')) # Get/Set the locale locale = args.locale if locale: QLocale().setDefault(QLocale(locale)) logging.info('Using {} locale'.format(QLocale().name())) # Main app translations translator = QTranslator() translator.load(QLocale(), 'lisp', '_', path.join(path.dirname(path.realpath(__file__)), 'i18n')) qt_app.installTranslator(translator) ui_translators = [translator] # Qt platform translation translator = QTranslator() translator.load(QLocale(), 'qt', '_', QLibraryInfo.location(QLibraryInfo.TranslationsPath)) qt_app.installTranslator(translator) ui_translators.append(translator) # Modules and plugins translations for tr_file in chain(modules.translations(), plugins.translations()): translator = QTranslator() translator.load(QLocale(), tr_file, '_') qt_app.installTranslator(translator) ui_translators.append(translator) # Create the application lisp_app = Application() # Load modules and plugins modules.load_modules() plugins.load_plugins() # Start/Initialize LiSP Application lisp_app.start(session_file=args.file) # Start Qt Application (block until exit) exit_code = qt_app.exec_() # Finalize the application lisp_app.finalize() # Exit sys.exit(exit_code) if __name__ == '__main__': main() linux-show-player-0.5.1/lisp/modules/000077500000000000000000000000001323636106000175435ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/__init__.py000066400000000000000000000035661323636106000216660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import os from lisp.core.loading import load_classes from lisp.ui import elogging __MODULES = {} def load_modules(): for name, module in load_classes(__package__, os.path.dirname(__file__)): try: __MODULES[name] = module() elogging.debug('MODULES: Loaded "{0}"'.format(name)) except Exception as e: elogging.exception('Failed "{0}" lading'.format(name), e) def translations(): base_path = os.path.dirname(os.path.realpath(__file__)) for module in next(os.walk(base_path))[1]: i18n_dir = os.path.join(base_path, module, 'i18n') if os.path.exists(i18n_dir): yield os.path.join(i18n_dir, module) def terminate_modules(): for module_name in __MODULES: try: __MODULES[module_name].terminate() elogging.debug('MODULES: Terminated "{0}"'.format(module_name)) except Exception as e: elogging.exception('Failed "{0}" termination'.format(module_name), e) def check_module(modname): return modname.lower() in [mod.lower() for mod in __MODULES] linux-show-player-0.5.1/lisp/modules/action_cues/000077500000000000000000000000001323636106000220375ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/action_cues/__init__.py000066400000000000000000000041521323636106000241520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import logging from os import path from lisp.application import Application from lisp.core.loading import load_classes from lisp.core.module import Module from lisp.cues.cue_factory import CueFactory from lisp.ui.mainwindow import MainWindow from lisp.ui.ui_utils import translate class ActionCues(Module): def __init__(self): for name, cue in load_classes(__package__, path.dirname(__file__)): # Register the action-cue in the cue-factory CueFactory.register_factory(cue.__name__, cue) # Register the menu action for adding the action-cue add_function = ActionCues.create_add_action_cue_method(cue) MainWindow().register_cue_menu_action( translate('CueName', cue.Name), add_function, 'Action cues') logging.debug('ACTION-CUES: Loaded "' + name + '"') @staticmethod def create_add_action_cue_method(cue_class): def method(): try: cue = CueFactory.create_cue(cue_class.__name__) Application().cue_model.add(cue) except Exception: # TODO: Display a message to the user import logging, traceback logging.error('Cannot create cue {}', cue_class.__name__) logging.debug(traceback.format_exc()) return method linux-show-player-0.5.1/lisp/modules/action_cues/collection_cue.py000066400000000000000000000135561323636106000254120ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QVBoxLayout, QSizePolicy, QDialogButtonBox,\ QDialog, QAbstractItemView, QHeaderView, QTableView from lisp.application import Application from lisp.core.has_properties import Property from lisp.cues.cue import Cue, CueAction from lisp.ui.cuelistdialog import CueSelectDialog from lisp.ui.qdelegates import CueActionDelegate, CueSelectionDelegate from lisp.ui.qmodels import CueClassRole, SimpleCueListModel from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class CollectionCue(Cue): Name = QT_TRANSLATE_NOOP('CueName', 'Collection Cue') targets = Property(default=[]) def __init__(self, **kwargs): super().__init__(**kwargs) self.name = translate('CueName', self.Name) def __start__(self, fade=False): for target_id, action in self.targets: cue = Application().cue_model.get(target_id) if cue is not self: cue.execute(action=CueAction[action]) return False class CollectionCueSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Edit Collection') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout(self)) self.cue_dialog = CueSelectDialog( cues=Application().cue_model, selection_mode=QAbstractItemView.ExtendedSelection) self.collectionModel = CollectionModel() self.collectionView = CollectionView(self.cue_dialog, parent=self) self.collectionView.setModel(self.collectionModel) self.collectionView.setAlternatingRowColors(True) self.layout().addWidget(self.collectionView) # Buttons self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.layout().addWidget(self.dialogButtons) self.addButton = self.dialogButtons.addButton( translate('CollectionCue', 'Add'), QDialogButtonBox.ActionRole) self.addButton.clicked.connect(self._add_dialog) self.delButton = self.dialogButtons.addButton( translate('CollectionCue', 'Remove'), QDialogButtonBox.ActionRole) self.delButton.clicked.connect(self._remove_selected) def load_settings(self, settings): for target_id, action in settings.get('targets', []): target = Application().cue_model.get(target_id) if target is not None: self._add_cue(target, CueAction(action)) def get_settings(self): targets = [] for target_id, action in self.collectionModel.rows: targets.append((target_id, action.value)) return {'targets': targets} def _add_cue(self, cue, action): self.collectionModel.appendRow(cue.__class__, cue.id, action) self.cue_dialog.remove_cue(cue) def _add_dialog(self): if self.cue_dialog.exec_() == QDialog.Accepted: for target in self.cue_dialog.selected_cues(): self._add_cue(target, target.CueActions[0]) def _remove_selected(self): row = self.collectionView.currentIndex().row() cue_id = self.collectionModel.rows[row][0] self.collectionModel.removeRow(row) self.cue_dialog.add_cue(Application().cue_model.get(cue_id)) class CollectionView(QTableView): def __init__(self, cue_select, **kwargs): super().__init__(**kwargs) self.setSelectionBehavior(QTableView.SelectRows) self.setSelectionMode(QTableView.SingleSelection) self.setShowGrid(False) self.setAlternatingRowColors(True) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.horizontalHeader().setHighlightSections(False) self.verticalHeader().sectionResizeMode(QHeaderView.Fixed) self.verticalHeader().setDefaultSectionSize(26) self.verticalHeader().setHighlightSections(False) self.delegates = [ CueSelectionDelegate(cue_select), CueActionDelegate() ] for column, delegate in enumerate(self.delegates): self.setItemDelegateForColumn(column, delegate) class CollectionModel(SimpleCueListModel): def __init__(self): # NOTE: The model does fixed-indices operations based on this list super().__init__([translate('CollectionCue', 'Cue'), translate('CollectionCue', 'Action')]) def setData(self, index, value, role=Qt.DisplayRole): result = super().setData(index, value, role) if result and role == CueClassRole: if self.rows[index.row()][1] not in value.CueActions: self.rows[index.row()][1] = value.CueActions[0] self.dataChanged.emit(self.index(index.row(), 1), self.index(index.row(), 1), [Qt.DisplayRole, Qt.EditRole]) return result CueSettingsRegistry().add_item(CollectionCueSettings, CollectionCue) linux-show-player-0.5.1/lisp/modules/action_cues/command_cue.py000066400000000000000000000137241323636106000246720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import subprocess from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QVBoxLayout, QGroupBox, QLineEdit, QCheckBox from lisp.core.decorators import async from lisp.core.has_properties import Property from lisp.cues.cue import Cue, CueState, CueAction from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class CommandCue(Cue): """Cue able to execute system commands. Implemented using :class:`subprocess.Popen` with *shell=True* """ Name = QT_TRANSLATE_NOOP('CueName', 'Command Cue') CueActions = (CueAction.Default, CueAction.Start, CueAction.Stop) command = Property(default='') no_output = Property(default=True) no_error = Property(default=True) kill = Property(default=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.name = translate('CueName', self.Name) self.__process = None self.__stopped = False def __start__(self, fade=False): self.__exec_command() return True @async def __exec_command(self): if not self.command.strip(): return # If no_output is True, discard all the outputs std = subprocess.DEVNULL if self.no_output else None # Execute the command self.__process = subprocess.Popen(self.command, shell=True, stdout=std, stderr=std) rcode = self.__process.wait() if rcode == 0 or rcode == -9 or self.no_error: # If terminate normally, killed or in no-error mode if not self.__stopped: self._ended() self.__process = None self.__stopped = False elif not self.no_error: # If an error occurs and not in no-error mode self._error( translate('CommandCue', 'Process ended with an error status.'), translate('CommandCue', 'Exit code: ') + str(rcode)) def __stop__(self, fade=False): if self.__process is not None: self.__stopped = True if self.kill: self.__process.kill() else: self.__process.terminate() return True __interrupt__ = __stop__ class CommandCueSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Command') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.group = QGroupBox(self) self.group.setLayout(QVBoxLayout(self.group)) self.layout().addWidget(self.group) self.commandLineEdit = QLineEdit(self.group) self.group.layout().addWidget(self.commandLineEdit) self.noOutputCheckBox = QCheckBox(self) self.layout().addWidget(self.noOutputCheckBox) self.noErrorCheckBox = QCheckBox(self) self.layout().addWidget(self.noErrorCheckBox) self.killCheckBox = QCheckBox(self) self.layout().addWidget(self.killCheckBox) self.retranslateUi() def retranslateUi(self): self.group.setTitle(translate('CommandCue', 'Command')) self.commandLineEdit.setPlaceholderText( translate('CommandCue', 'Command to execute, as in a shell')) self.noOutputCheckBox.setText( translate('CommandCue', 'Discard command output')) self.noErrorCheckBox.setText( translate('CommandCue', 'Ignore command errors')) self.killCheckBox.setText( translate('CommandCue', 'Kill instead of terminate')) def enable_check(self, enabled): self.group.setCheckable(enabled) self.group.setChecked(False) self.noOutputCheckBox.setTristate(enabled) if enabled: self.noOutputCheckBox.setCheckState(Qt.PartiallyChecked) self.noErrorCheckBox.setTristate(enabled) if enabled: self.killCheckBox.setCheckState(Qt.PartiallyChecked) self.killCheckBox.setTristate(enabled) if enabled: self.killCheckBox.setCheckState(Qt.PartiallyChecked) def load_settings(self, settings): self.commandLineEdit.setText(settings.get('command', '')) self.noOutputCheckBox.setChecked(settings.get('no_output', True)) self.noErrorCheckBox.setChecked(settings.get('no_error', True)) self.killCheckBox.setChecked(settings.get('kill', False)) def get_settings(self): settings = {} if not (self.group.isCheckable() and not self.group.isChecked()): if self.commandLineEdit.text().strip(): settings['command'] = self.commandLineEdit.text() if self.noOutputCheckBox.checkState() != Qt.PartiallyChecked: settings['no_output'] = self.noOutputCheckBox.isChecked() if self.noErrorCheckBox.checkState() != Qt.PartiallyChecked: settings['no_error'] = self.noErrorCheckBox.isChecked() if self.killCheckBox.checkState() != Qt.PartiallyChecked: settings['kill'] = self.killCheckBox.isChecked() return settings CueSettingsRegistry().add_item(CommandCueSettings, CommandCue) linux-show-player-0.5.1/lisp/modules/action_cues/i18n/000077500000000000000000000000001323636106000226165ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/action_cues/i18n/action_cues_cs.qm000066400000000000000000000067531323636106000261510ustar00rootroot00000000000000 CollectionCue Add Přidat Remove Odstranit Cue Narážka Action Činnost CommandCue Process ended with an error status. Postup skončil s chybovým stavem. Exit code: Kód ukončení: Command Příkaz Command to execute, as in a shell Příkaz k provedení, jako v terminálu Discard command output Zahodit výstup příkazu Ignore command errors Přehlížet chyby příkazu Kill instead of terminate Zabít namísto ukončit CueName Command Cue Narážka příkazu MIDI Cue Narážka MIDI Volume Control Ovládání hlasitosti Seek Cue Narážka prohledávání Collection Cue Narážka sbírky Stop-All Zastavit vše Index Action Činnost indexu IndexActionCue Index Index Use a relative index Použít poměrný index Target index Cílový index Action Činnost MIDICue MIDI Message Zpráva MIDI Message type Typ zprávy SeekCue Cue Narážka Click to select Klepnout pro vybrání Not selected Nevybráno Seek Hledat Time to reach Čas k dosažení SettingsPageName Command Příkaz MIDI Settings Nastavení MIDI Volume Settings Nastavení hlasitosti Seek Settings Nastavení prohledávání Edit Collection Upravit sbírku Action Settings Nastavení činnosti Stop Settings Nastavení zastavení StopAll Stop Action Činnost zastavení VolumeControl Error during cue execution Chyba během provádění narážky Cue Narážka Click to select Klepnout pro vybrání Not selected Nevybráno Volume to reach Hlasitost k dosažení Fade Prolínat linux-show-player-0.5.1/lisp/modules/action_cues/i18n/action_cues_en.qm000066400000000000000000000064471323636106000261460ustar00rootroot00000000000000 CollectionCue Add Add Remove Remove Cue Cue Action Action CommandCue Process ended with an error status. Process ended with an error status. Exit code: Exit code: Command Command Command to execute, as in a shell Command to execute, as in a shell Discard command output Discard command output Ignore command errors Ignore command errors Kill instead of terminate Kill instead of terminate CueName Command Cue Command Cue MIDI Cue MIDI Cue Volume Control Volume Control Seek Cue Seek Cue Collection Cue Collection Cue Stop-All Stop-All Index Action Index Action IndexActionCue Index Index Use a relative index Use a relative index Target index Target index Action Action MIDICue MIDI Message MIDI Message Message type Message type SeekCue Cue Cue Click to select Click to select Not selected Not selected Seek Seek Time to reach Time to reach SettingsPageName Command Command MIDI Settings MIDI Settings Volume Settings Volume Settings Seek Settings Seek Settings Edit Collection Edit Collection Action Settings Action Settings Stop Settings Stop Settings StopAll Stop Action Stop Action VolumeControl Error during cue execution Error during cue execution Cue Cue Click to select Click to select Not selected Not selected Volume to reach Volume to reach Fade Fade linux-show-player-0.5.1/lisp/modules/action_cues/i18n/action_cues_es.qm000066400000000000000000000071071323636106000261450ustar00rootroot00000000000000 CollectionCue Add Añadir Remove Eliminar Cue Cue Action Acción CommandCue Process ended with an error status. El proceso terminó con un estado de error Exit code: Código de salida: Command Comando Command to execute, as in a shell Comando a ejecutar, como en una línea de comandos Discard command output Descartar salida de comando Ignore command errors Ignorar errores de comandos Kill instead of terminate Matar en vez de terminar CueName Command Cue Cue de comando MIDI Cue Cue de MIDI Volume Control Control de volumen Seek Cue Cue de búsqueda Collection Cue Cue de Colección Stop-All Deterner Todo Index Action Índice de Acciones IndexActionCue Index Índice Use a relative index Usar índice relativo Target index Índice de Target Action Acción MIDICue MIDI Message Mensaje MIDI Message type Tipo de mensaje SeekCue Cue Cue Click to select Click para seleccionar Not selected No seleccionado Seek Búsqueda Time to reach Tiempo a alcanzar SettingsPageName Command Comando MIDI Settings Ajustes MIDI Volume Settings Ajustes de volumen Seek Settings Ajustes de búsqueda Edit Collection Editar Colección Action Settings Ajustes de Acción Stop Settings Ajustes de Detención StopAll Stop Action Detener Acción VolumeControl Error during cue execution Error durante la ejecución del cue Cue Cue Click to select Click para seleccionar Not selected No seleccionado Volume to reach Volumen a alcanzar Fade Fundido linux-show-player-0.5.1/lisp/modules/action_cues/i18n/action_cues_fr.qm000066400000000000000000000071631323636106000261470ustar00rootroot00000000000000Ignorer les erreurs de commandeIgnore command errors CommandCue4Killer au lieu de terminerKill instead of terminate CommandCuePLe process s'est termin par une erreur #Process ended with an error status. CommandCue"Collection de cueCollection CueCueNameCue de commande Command CueCueNameIndex d'action Index ActionCueNameCue MIDIMIDI CueCueName Recherche de CueSeek CueCueNameTout arrterStop-AllCueName$Contrle du VolumeVolume ControlCueName ActionActionIndexActionCue IndexIndexIndexActionCueIndex cible Target indexIndexActionCue2Utiliser un index relatifUse a relative indexIndexActionCueMessage MIDI MIDI MessageMIDICueType de message Message typeMIDICue2Cliquer pour slectionnerClick to selectSeekCueCueCueSeekCueNon slectionn Not selectedSeekCueRechercheSeekSeekCue"Temps atteindre Time to reachSeekCue&Paramtres d'actionAction SettingsSettingsPageNameCommandeCommandSettingsPageName(Editer la collectionEdit CollectionSettingsPageName Prfrences MIDI MIDI SettingsSettingsPageName0Prfrences de recherche Seek SettingsSettingsPageName$Paramtres d'arrt Stop SettingsSettingsPageName*Prfrences de volumeVolume SettingsSettingsPageNameAction d'arrt Stop ActionStopAll2Cliquer pour slectionnerClick to select VolumeControlCueCue VolumeControlHErreur lors de l'excution de la cueError during cue execution VolumeControl FonduFade VolumeControlNon slectionn Not selected VolumeControl$Volume atteindreVolume to reach VolumeControllinux-show-player-0.5.1/lisp/modules/action_cues/i18n/action_cues_fr.ts000066400000000000000000000201701323636106000261510ustar00rootroot00000000000000 CollectionCue Add Ajouter Remove Retirer Cue Cue Action Action CommandCue Process ended with an error status. Le process s'est terminé par une erreur Exit code: Code de sortie : Command Commande Command to execute, as in a shell Commande à exécuter, comme dans un terminal Discard command output Abandonner la sortie de commande Ignore command errors Ignorer les erreurs de commande Kill instead of terminate Killer au lieu de terminer CueName Command Cue Cue de commande MIDI Cue Cue MIDI Volume Control Contrôle du volume Seek Cue Recherche de cue Collection Cue Collection de cues Stop-All Tout arrêter Index Action Index d'action IndexActionCue Index Index Use a relative index Utiliser un index relatif Target index Index cible Action Action MIDICue MIDI Message Message MIDI Message type Type de message SeekCue Cue Cue Click to select Cliquer pour sélectionner Not selected Non sélectionné Seek Recherche Time to reach Temps à atteindre SettingsPageName Command Commande MIDI Settings Préférences MIDI Volume Settings Préférences de volume Seek Settings Préférences de recherche Edit Collection Editer la collection Action Settings Paramètres d'action Stop Settings Paramètres d'arrêt StopAll Stop Action Action d'arrêt VolumeControl Error during cue execution Erreur lors de l'exécution de la cue Cue Cue Click to select Cliquer pour sélectionner Not selected Non sélectionné Volume to reach Volume à atteindre Fade Fondu linux-show-player-0.5.1/lisp/modules/action_cues/i18n/action_cues_it.qm000066400000000000000000000071351323636106000261530ustar00rootroot00000000000000 CollectionCue Add Aggiungi Remove Rimuovi Cue Cue Action Azione CommandCue Process ended with an error status. Processo terminato con uno stato errore. Exit code: Codice di uscita: Command Comando Command to execute, as in a shell Comando da eseguire, come in una shell Discard command output Scarta l'output del comando Ignore command errors Ignora errori Kill instead of terminate Uccidi invece di terminare CueName Command Cue Cue Comando MIDI Cue Cue MIDI Volume Control Controllo Volume Seek Cue Seek Cue Collection Cue Collezione Stop-All Ferma Tutto Index Action Azione Posizionale IndexActionCue Index Posizione Use a relative index Usa una posizione relativa Target index Posizione bersaglio Action Azione MIDICue MIDI Message Messaggio Midi Message type Tipo di messaggio SeekCue Cue Cue Click to select Clicca per selezionare Not selected Non slezionata Seek Posizionamento Time to reach Posizione da raggiungere SettingsPageName Command Comando MIDI Settings Impostazioni MIDI Volume Settings Impostazioni Volume Seek Settings Impostazioni Posizione Edit Collection Modifica Collezione Action Settings Impostazioni Azione Stop Settings Impostazioni di Arresto StopAll Stop Action Azione di arresto VolumeControl Error during cue execution Errore durante l'esecuzione della cue Cue Cue Click to select Clicca per selezionare Not selected Non slezionata Volume to reach Volume da raggiungere Fade Dissolvenza linux-show-player-0.5.1/lisp/modules/action_cues/i18n/action_cues_sl_SI.qm000066400000000000000000000066561323636106000265570ustar00rootroot00000000000000 CollectionCue Add Dodaj Remove Odstrani Cue Vrsta Action Akcija CommandCue Process ended with an error status. Procesiranje se je končalo z napako. Exit code: Izhodni ukaz: Command Ukaz Command to execute, as in a shell Ukaz za izvršitev, kot v ukazni lupini Discard command output Zavrži izhod ukaza Ignore command errors Ignoriraj napake ukaza Kill instead of terminate Kill namesto terminate CueName Command Cue Ukazna vrsta MIDI Cue MIDI vrsta Volume Control Krmiljenje glasnosti Seek Cue Vrsta iskanj Collection Cue Zbirke v vrsti Stop-All Ustavi vse Index Action Akcija indeksa IndexActionCue Index Indeks Use a relative index Uporabi relativni indeks Target index Ciljni indeks Action Akcija MIDICue MIDI Message MIDI sporočilo Message type Tip sporočila SeekCue Cue Vrsta Click to select Klikni za izbor Not selected Ni izbrano Seek Iskanje Time to reach Čas za dosego SettingsPageName Command Ukaz MIDI Settings MIDI nastavitve Volume Settings Nastavitve glasnosti Seek Settings Nastavitve iskanja Edit Collection Uredi zbirko Action Settings Nastavitve akcije Stop Settings Nastavitve ustavitve StopAll Stop Action Akcija ustavitve VolumeControl Error during cue execution Napaka med izvajanjem vrste Cue Vrsta Click to select Klikni za izbor Not selected Ni izbrano Volume to reach Glasnost za dosego Fade Pojenjanje linux-show-player-0.5.1/lisp/modules/action_cues/index_action_cue.py000066400000000000000000000140631323636106000257150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QCheckBox, QComboBox, QGroupBox, QLabel, QSpinBox,\ QGridLayout, QVBoxLayout from lisp.application import Application from lisp.core.has_properties import Property from lisp.cues.cue import Cue, CueAction from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate from lisp.ui.widgets import CueActionComboBox class IndexActionCue(Cue): Name = QT_TRANSLATE_NOOP('CueName', 'Index Action') target_index = Property(default=0) relative = Property(default=True) action = Property(default=CueAction.Stop.value) def __init__(self, **kwargs): super().__init__(**kwargs) self.name = translate('CueName', self.Name) def __start__(self, fade=False): if self.relative: index = self.index + self.target_index else: index = self.target_index try: cue = Application().layout.model_adapter.item(index) if cue is not self: cue.execute(CueAction(self.action)) except IndexError: pass class IndexActionCueSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Action Settings') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self._cue_index = 0 self._target_class = Cue self.indexGroup = QGroupBox(self) self.indexGroup.setLayout(QGridLayout()) self.layout().addWidget(self.indexGroup) self.relativeCheck = QCheckBox(self.indexGroup) self.relativeCheck.stateChanged.connect(self._relative_changed) self.indexGroup.layout().addWidget(self.relativeCheck, 0, 0, 1, 2) self.targetIndexSpin = QSpinBox(self) self.targetIndexSpin.editingFinished.connect(self._update_action_combo) self.indexGroup.layout().addWidget(self.targetIndexSpin, 1, 0) self.targetIndexLabel = QLabel(self) self.indexGroup.layout().addWidget(self.targetIndexLabel, 1, 1) self.actionGroup = QGroupBox(self) self.actionGroup.setLayout(QVBoxLayout(self.actionGroup)) self.layout().addWidget(self.actionGroup) self.actionCombo = CueActionComboBox(self._target_class, mode=CueActionComboBox.Mode.Value, parent=self.actionGroup) self.actionGroup.layout().addWidget(self.actionCombo) self.retranslateUi() def retranslateUi(self): self.indexGroup.setTitle(translate('IndexActionCue', 'Index')) self.relativeCheck.setText( translate('IndexActionCue', 'Use a relative index')) self.targetIndexLabel.setText( translate('IndexActionCue', 'Target index')) self.actionGroup.setTitle(translate('IndexActionCue', 'Action')) def enable_check(self, enabled): self.indexGroup.setChecked(enabled) self.indexGroup.setChecked(False) self.actionGroup.setCheckable(enabled) self.actionGroup.setChecked(False) def get_settings(self): conf = {} checkable = self.actionGroup.isCheckable() if not (checkable and not self.indexGroup.isChecked()): conf['relative'] = self.relativeCheck.isChecked() conf['target_index'] = self.targetIndexSpin.value() if not (checkable and not self.actionGroup.isChecked()): conf['action'] = self.actionCombo.currentData() return conf def load_settings(self, settings): self._cue_index = settings.get('index', -1) self.relativeCheck.setChecked(settings.get('relative', True)) self.targetIndexSpin.setValue(settings.get('target_index', 0)) self.actionCombo.setCurrentText( translate('CueAction', settings.get('action', ''))) def _update_action_combo(self): if self.relativeCheck.isChecked(): index = self._cue_index + self.targetIndexSpin.value() else: index = self.targetIndexSpin.value() try: target = Application().layout.model_adapter.item(index) target_class = target.__class__ except IndexError: target_class = Cue if target_class is not self._target_class: self._target_class = target_class self.actionGroup.layout().removeWidget(self.actionCombo) self.actionCombo.deleteLater() self.actionCombo = CueActionComboBox( self._target_class, mode=CueActionComboBox.Mode.Value, parent=self.actionGroup) self.actionGroup.layout().addWidget(self.actionCombo) def _relative_changed(self): max_index = len(Application().cue_model) - 1 if not self.relativeCheck.isChecked(): self.targetIndexSpin.setRange(0, max_index) else: if self._cue_index >= 0: self.targetIndexSpin.setRange(-self._cue_index, max_index - self._cue_index) else: self.targetIndexSpin.setRange(-max_index, max_index) CueSettingsRegistry().add_item(IndexActionCueSettings, IndexActionCue) linux-show-player-0.5.1/lisp/modules/action_cues/midi_cue.py000066400000000000000000000117751323636106000242020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QGridLayout, QLabel, \ QComboBox, QSpinBox, QFrame from lisp.core.has_properties import Property from lisp.cues.cue import Cue from lisp.modules.midi.midi_output import MIDIOutput from lisp.modules.midi.midi_utils import str_msg_to_dict, dict_msg_to_str, \ MIDI_ATTRIBUTES, MIDI_MESSAGES from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class MidiCue(Cue): Name = QT_TRANSLATE_NOOP('CueName', 'MIDI Cue') message = Property(default='') def __init__(self, **kwargs): super().__init__(**kwargs) self.name = translate('CueName', self.Name) midi_out = MIDIOutput() if not midi_out.is_open(): midi_out.open() def __start__(self, fade=False): if self.message: MIDIOutput().send_from_str(self.message) return False class MidiCueSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'MIDI Settings') OFFSETS = {'channel': 1} def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.msgGroup = QGroupBox(self) self.msgGroup.setLayout(QGridLayout()) self.layout().addWidget(self.msgGroup) # Message type self.msgTypeLabel = QLabel(self.msgGroup) self.msgGroup.layout().addWidget(self.msgTypeLabel, 0, 0) self.msgTypeCombo = QComboBox(self.msgGroup) self.msgTypeCombo.addItems(sorted(MIDI_MESSAGES.keys())) self.msgTypeCombo.currentTextChanged.connect(self.__type_changed) self.msgGroup.layout().addWidget(self.msgTypeCombo, 0, 1) line = QFrame(self.msgGroup) line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.msgGroup.layout().addWidget(line, 1, 0, 1, 2) # Data widgets self._data_widgets = [] for n in range(2, 5): dataLabel = QLabel(self.msgGroup) dataSpin = QSpinBox(self.msgGroup) self.msgGroup.layout().addWidget(dataLabel, n, 0) self.msgGroup.layout().addWidget(dataSpin, n, 1) self._data_widgets.append((dataLabel, dataSpin)) self.__type_changed(self.msgTypeCombo.currentText()) self.retranslateUi() def retranslateUi(self): self.msgGroup.setTitle(translate('MIDICue', 'MIDI Message')) self.msgTypeLabel.setText(translate('MIDICue', 'Message type')) def __type_changed(self, msg_type): for label, spin in self._data_widgets: label.setEnabled(False) label.setText('') spin.setEnabled(False) for label, spin, attr_name in self.__attributes(msg_type): label.setEnabled(True) label.setText(attr_name.title()) spin.setEnabled(True) min_, max_, def_ = MIDI_ATTRIBUTES.get(attr_name, (0, 0, 0)) # Add an offset for displaying purposes off = MidiCueSettings.OFFSETS.get(attr_name, 0) spin.setRange(min_ + off, max_ + off) spin.setValue(def_) def get_settings(self): msg_type = self.msgTypeCombo.currentText() msg_dict = {'type': msg_type} for label, spin, attr_name in self.__attributes(msg_type): if spin.isEnabled(): offset = MidiCueSettings.OFFSETS.get(attr_name, 0) msg_dict[attr_name] = spin.value() - offset return {'message': dict_msg_to_str(msg_dict)} def __attributes(self, msg_type): for (l, s), a in zip(self._data_widgets, MIDI_MESSAGES[msg_type]): yield l, s, a def load_settings(self, settings): message = settings.get('message', '') if message: dict_msg = str_msg_to_dict(message) self.msgTypeCombo.setCurrentText(dict_msg['type']) for label, spin, attr_name in self.__attributes(dict_msg['type']): offset = MidiCueSettings.OFFSETS.get(attr_name, 0) spin.setValue(dict_msg.get(label.text().lower(), 0) + offset) CueSettingsRegistry().add_item(MidiCueSettings, MidiCue) linux-show-player-0.5.1/lisp/modules/action_cues/seek_cue.py000066400000000000000000000113261323636106000241770ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtCore import QTime, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QVBoxLayout, QGroupBox, QPushButton, QLabel, \ QHBoxLayout, QTimeEdit from lisp.application import Application from lisp.core.has_properties import Property from lisp.cues.cue import Cue, CueState from lisp.cues.media_cue import MediaCue from lisp.ui.cuelistdialog import CueSelectDialog from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class SeekCue(Cue): Name = QT_TRANSLATE_NOOP('CueName', 'Seek Cue') target_id = Property() time = Property(default=-1) def __init__(self, **kwargs): super().__init__(**kwargs) self.name = translate('CueName', self.Name) def __start__(self, fade=False): cue = Application().cue_model.get(self.target_id) if isinstance(cue, MediaCue) and self.time >= 0: cue.media.seek(self.time) return False class SeekCueSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Seek Settings') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(QtCore.Qt.AlignTop) self.cue_id = -1 self.cueDialog = CueSelectDialog( cues=Application().cue_model.filter(MediaCue), parent=self) self.cueGroup = QGroupBox(self) self.cueGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.cueGroup) self.cueButton = QPushButton(self.cueGroup) self.cueButton.clicked.connect(self.select_cue) self.cueGroup.layout().addWidget(self.cueButton) self.cueLabel = QLabel(self.cueGroup) self.cueLabel.setAlignment(QtCore.Qt.AlignCenter) self.cueGroup.layout().addWidget(self.cueLabel) self.seekGroup = QGroupBox(self) self.seekGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.seekGroup) self.seekEdit = QTimeEdit(self.seekGroup) self.seekEdit.setDisplayFormat('HH.mm.ss.zzz') self.seekGroup.layout().addWidget(self.seekEdit) self.seekLabel = QLabel(self.seekGroup) self.seekLabel.setAlignment(QtCore.Qt.AlignCenter) self.seekGroup.layout().addWidget(self.seekLabel) self.retranslateUi() def retranslateUi(self): self.cueGroup.setTitle(translate('SeekCue', 'Cue')) self.cueButton.setText(translate('SeekCue', 'Click to select')) self.cueLabel.setText(translate('SeekCue', 'Not selected')) self.seekGroup.setTitle(translate('SeekCue', 'Seek')) self.seekLabel.setText(translate('SeekCue', 'Time to reach')) def select_cue(self): if self.cueDialog.exec_() == self.cueDialog.Accepted: cue = self.cueDialog.selected_cue() if cue is not None: self.cue_id = cue.id self.seekEdit.setMaximumTime( QTime.fromMSecsSinceStartOfDay(cue.media.duration)) self.cueLabel.setText(cue.name) def enable_check(self, enabled): self.cueGroup.setCheckable(enabled) self.cueGroup.setChecked(False) self.seekGroup.setCheckable(enabled) self.seekGroup.setChecked(False) def get_settings(self): return {'target_id': self.cue_id, 'time': self.seekEdit.time().msecsSinceStartOfDay()} def load_settings(self, settings): if settings is not None: cue = Application().cue_model.get(settings.get('target_id')) if cue is not None: self.cue_id = settings['target_id'] self.seekEdit.setMaximumTime( QTime.fromMSecsSinceStartOfDay(cue.media.duration)) self.cueLabel.setText(cue.name) self.seekEdit.setTime( QTime.fromMSecsSinceStartOfDay(settings.get('time', 0))) CueSettingsRegistry().add_item(SeekCueSettings, SeekCue) linux-show-player-0.5.1/lisp/modules/action_cues/stop_all.py000066400000000000000000000072441323636106000242350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QComboBox from PyQt5.QtWidgets import QVBoxLayout, QGroupBox from lisp.application import Application from lisp.core.has_properties import Property from lisp.cues.cue import Cue, CueAction from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class StopAll(Cue): Name = QT_TRANSLATE_NOOP('CueName', 'Stop-All') action = Property(default=CueAction.Stop.value) def __init__(self, **kwargs): super().__init__(**kwargs) self.name = translate('CueName', self.Name) def __start__(self, fade=False): for cue in Application().cue_model: action = self.__adjust_action(cue, CueAction(self.action)) if action: cue.execute(action=action) return False def __adjust_action(self, cue, action, fade=False): if action in cue.CueActions: return action elif action is CueAction.FadeOutPause: return self.__adjust_action(cue, CueAction.Pause, True) elif action is CueAction.Pause and fade: return self.__adjust_action(cue, CueAction.FadeOutStop) elif action is CueAction.FadeOutInterrupt: return self.__adjust_action(cue, CueAction.Interrupt) elif action is CueAction.FadeOutStop: return self.__adjust_action(cue, CueAction.Stop) return None class StopAllSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Stop Settings') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.group = QGroupBox(self) self.group.setLayout(QVBoxLayout(self.group)) self.layout().addWidget(self.group) self.actionCombo = QComboBox(self.group) for action in [CueAction.Stop, CueAction.FadeOutStop, CueAction.Pause, CueAction.FadeOutPause, CueAction.Interrupt, CueAction.FadeOutInterrupt]: self.actionCombo.addItem( translate('CueAction', action.name), action.value) self.group.layout().addWidget(self.actionCombo) self.retranslateUi() def retranslateUi(self): self.group.setTitle(translate('StopAll', 'Stop Action')) def enable_check(self, enabled): self.group.setCheckable(enabled) self.group.setChecked(False) def get_settings(self): conf = {} if not (self.group.isCheckable() and not self.group.isChecked()): conf['action'] = self.actionCombo.currentData() return conf def load_settings(self, settings): self.actionCombo.setCurrentText( translate('CueAction', settings.get('action', ''))) CueSettingsRegistry().add_item(StopAllSettings, StopAll) linux-show-player-0.5.1/lisp/modules/action_cues/volume_control.py000066400000000000000000000211711323636106000254620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QGroupBox, \ QPushButton, QDoubleSpinBox, QGridLayout from lisp.application import Application from lisp.backend.audio_utils import MIN_VOLUME_DB, MAX_VOLUME_DB, \ linear_to_db, db_to_linear from lisp.core.decorators import async from lisp.core.fade_functions import FadeInType, FadeOutType from lisp.core.fader import Fader from lisp.core.has_properties import Property from lisp.cues.cue import Cue, CueAction from lisp.cues.media_cue import MediaCue from lisp.ui.cuelistdialog import CueSelectDialog from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate from lisp.ui.widgets import FadeComboBox from lisp.ui.widgets.fade_edit import FadeEdit class VolumeControl(Cue): Name = QT_TRANSLATE_NOOP('CueName', 'Volume Control') target_id = Property() fade_type = Property(default=FadeInType.Linear.name) volume = Property(default=.0) CueActions = (CueAction.Default, CueAction.Start, CueAction.Stop, CueAction.Pause, CueAction.Interrupt) def __init__(self, **kwargs): super().__init__(**kwargs) self.name = translate('CueName', self.Name) self.__fader = Fader(None, 'current_volume') self.__init_fader() def __init_fader(self): cue = Application().cue_model.get(self.target_id) if isinstance(cue, MediaCue): volume = cue.media.element('Volume') if volume is not None: if volume is not self.__fader.target: self.__fader.target = volume return True return False def __start__(self, fade=False): if self.__init_fader(): if self.__fader.is_paused(): self.__fader.restart() return True if self.duration > 0: if self.__fader.target.current_volume > self.volume: self.__fade(FadeOutType[self.fade_type]) return True elif self.__fader.target.current_volume < self.volume: self.__fade(FadeInType[self.fade_type]) return True else: self.__fader.target.current_volume = self.volume return False def __stop__(self, fade=False): self.__fader.stop() return True def __pause__(self, fade=False): self.__fader.pause() return True __interrupt__ = __stop__ @async def __fade(self, fade_type): try: self.__fader.prepare() ended = self.__fader.fade(round(self.duration / 1000, 2), self.volume, fade_type) if ended: # to avoid approximation problems self.__fader.target.current_volume = self.volume self._ended() except Exception as e: self._error( translate('VolumeControl', 'Error during cue execution'), str(e) ) def current_time(self): return self.__fader.current_time() class VolumeSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Volume Settings') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.__v_edit_flag = False self.cue_id = -1 cues = Application().cue_model.filter(MediaCue) self.cueDialog = CueSelectDialog(cues=cues, parent=self) self.cueGroup = QGroupBox(self) self.cueGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.cueGroup) self.cueLabel = QLabel(self.cueGroup) self.cueLabel.setAlignment(QtCore.Qt.AlignCenter) self.cueLabel.setStyleSheet('font-weight: bold;') self.cueGroup.layout().addWidget(self.cueLabel) self.cueButton = QPushButton(self.cueGroup) self.cueButton.clicked.connect(self.select_cue) self.cueGroup.layout().addWidget(self.cueButton) self.volumeGroup = QGroupBox(self) self.volumeGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.volumeGroup) self.volumeEdit = QDoubleSpinBox(self.volumeGroup) self.volumeEdit.setDecimals(6) self.volumeEdit.setMaximum(100) self.volumeGroup.layout().addWidget(self.volumeEdit) self.percentLabel = QLabel('%', self.volumeGroup) self.volumeGroup.layout().addWidget(self.percentLabel) self.volumeDbEdit = QDoubleSpinBox(self.volumeGroup) self.volumeDbEdit.setRange(MIN_VOLUME_DB, MAX_VOLUME_DB) self.volumeDbEdit.setValue(MIN_VOLUME_DB) self.volumeGroup.layout().addWidget(self.volumeDbEdit) self.dbLabel = QLabel('dB', self.volumeGroup) self.volumeGroup.layout().addWidget(self.dbLabel) self.volumeEdit.valueChanged.connect(self.__volume_change) self.volumeDbEdit.valueChanged.connect(self.__db_volume_change) # Fade self.fadeGroup = QGroupBox(self) self.fadeGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.fadeGroup) self.fadeEdit = FadeEdit(self.fadeGroup) self.fadeGroup.layout().addWidget(self.fadeEdit) self.retranslateUi() def retranslateUi(self): self.cueGroup.setTitle(translate('VolumeControl', 'Cue')) self.cueButton.setText(translate('VolumeControl', 'Click to select')) self.cueLabel.setText(translate('VolumeControl', 'Not selected')) self.volumeGroup.setTitle(translate('VolumeControl', 'Volume to reach')) self.fadeGroup.setTitle(translate('VolumeControl', 'Fade')) def select_cue(self): if self.cueDialog.exec_() == self.cueDialog.Accepted: cue = self.cueDialog.selected_cue() if cue is not None: self.cue_id = cue.id self.cueLabel.setText(cue.name) def enable_check(self, enabled): self.cueGroup.setCheckable(enabled) self.cueGroup.setChecked(False) self.volumeGroup.setCheckable(enabled) self.volumeGroup.setChecked(False) self.fadeGroup.setCheckable(enabled) self.volumeGroup.setChecked(False) def get_settings(self): conf = {} checkable = self.cueGroup.isCheckable() if not (checkable and not self.cueGroup.isChecked()): conf['target_id'] = self.cue_id if not (checkable and not self.volumeGroup.isCheckable()): conf['volume'] = self.volumeEdit.value() / 100 if not (checkable and not self.fadeGroup.isCheckable()): conf['duration'] = self.fadeEdit.duration() * 1000 conf['fade_type'] = self.fadeEdit.fadeType() return conf def load_settings(self, settings): cue = Application().cue_model.get(settings.get('target_id', '')) if cue is not None: self.cue_id = settings['target_id'] self.cueLabel.setText(cue.name) self.volumeEdit.setValue(settings.get('volume', 0) * 100) self.fadeEdit.setDuration(settings.get('duration', 0) / 1000) self.fadeEdit.setFadeType(settings.get('fade_type', '')) def __volume_change(self, value): if not self.__v_edit_flag: try: self.__v_edit_flag = True self.volumeDbEdit.setValue(linear_to_db(value / 100)) finally: self.__v_edit_flag = False def __db_volume_change(self, value): if not self.__v_edit_flag: try: self.__v_edit_flag = True self.volumeEdit.setValue(db_to_linear(value) * 100) finally: self.__v_edit_flag = False CueSettingsRegistry().add_item(VolumeSettings, VolumeControl) linux-show-player-0.5.1/lisp/modules/gst_backend/000077500000000000000000000000001323636106000220075ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/gst_backend/__init__.py000066400000000000000000000002061323636106000241160ustar00rootroot00000000000000from lisp.core.configuration import config if config['Backend']['Default'].lower() == 'gst': from .gst_backend import GstBackend linux-show-player-0.5.1/lisp/modules/gst_backend/elements/000077500000000000000000000000001323636106000236235ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/gst_backend/elements/__init__.py000066400000000000000000000033201323636106000257320ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from os.path import dirname from lisp.backend.media_element import ElementType from lisp.core.loading import load_classes __INPUTS = {} __OUTPUTS = {} __PLUGINS = {} def load(): for _, element_class in load_classes(__package__, dirname(__file__)): if element_class.ElementType == ElementType.Input: __INPUTS[element_class.__name__] = element_class elif element_class.ElementType == ElementType.Output: __OUTPUTS[element_class.__name__] = element_class elif element_class.ElementType == ElementType.Plugin: __PLUGINS[element_class.__name__] = element_class # Getter functions def inputs(): return __INPUTS def input_name(class_name): return __INPUTS[class_name].Name def outputs(): return __OUTPUTS def output_name(class_name): return __OUTPUTS[class_name].Name def plugins(): return __PLUGINS def plugin_name(class_name): return __PLUGINS[class_name].Namelinux-show-player-0.5.1/lisp/modules/gst_backend/elements/alsa_sink.py000066400000000000000000000026531323636106000261470ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement, GstProperty class AlsaSink(GstMediaElement): ElementType = ElementType.Output MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'ALSA Out') device = GstProperty('alsa_sink', default='') def __init__(self, pipe): super().__init__() self.alsa_sink = Gst.ElementFactory.make('alsasink', 'sink') pipe.add(self.alsa_sink) def sink(self): return self.alsa_sink linux-show-player-0.5.1/lisp/modules/gst_backend/elements/audio_dynamic.py000066400000000000000000000042521323636106000270050ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from enum import Enum from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement, GstProperty class AudioDynamic(GstMediaElement): ElementType = ElementType.Plugin MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'Compressor/Expander') class Mode(Enum): Compressor = 'compressor' Expander = 'expander' class Characteristics(Enum): HardKnee = 'hard-knee' SoftKnee = 'soft-knee' mode = GstProperty('audio_dynamic', default=Mode.Compressor.value) characteristics = GstProperty('audio_dynamic', default=Characteristics.HardKnee.value) ratio = GstProperty('audio_dynamic', default=1) threshold = GstProperty('audio_dynamic', default=0) def __init__(self, pipe): super().__init__() self.audio_dynamic = Gst.ElementFactory.make('audiodynamic', None) self.audio_converter = Gst.ElementFactory.make('audioconvert', None) pipe.add(self.audio_dynamic) pipe.add(self.audio_converter) self.audio_dynamic.link(self.audio_converter) self.update_properties(self.properties()) def sink(self): return self.audio_dynamic def src(self): return self.audio_converterlinux-show-player-0.5.1/lisp/modules/gst_backend/elements/audio_pan.py000066400000000000000000000032241323636106000261350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement, GstProperty class AudioPan(GstMediaElement): ElementType = ElementType.Plugin MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'Audio Pan') pan = GstProperty('panorama', default=.0, gst_name='panorama') def __init__(self, pipe): super().__init__() self.panorama = Gst.ElementFactory.make("audiopanorama", None) self.audio_convert = Gst.ElementFactory.make("audioconvert", None) pipe.add(self.panorama) pipe.add(self.audio_convert) self.panorama.link(self.audio_convert) def sink(self): return self.panorama def src(self): return self.audio_convert linux-show-player-0.5.1/lisp/modules/gst_backend/elements/auto_sink.py000066400000000000000000000025611323636106000261750ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement class AutoSink(GstMediaElement): ElementType = ElementType.Output MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'System Out') def __init__(self, pipe): super().__init__() self.auto_sink = Gst.ElementFactory.make('autoaudiosink', 'sink') pipe.add(self.auto_sink) def sink(self): return self.auto_sinklinux-show-player-0.5.1/lisp/modules/gst_backend/elements/auto_src.py000066400000000000000000000024671323636106000260250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import MediaType from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstSrcElement class AutoSrc(GstSrcElement): MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'System Input') def __init__(self, pipe): super().__init__() self.auto_src = Gst.ElementFactory.make("autoaudiosrc", "src") pipe.add(self.auto_src) def src(self): return self.auto_src linux-show-player-0.5.1/lisp/modules/gst_backend/elements/db_meter.py000066400000000000000000000054331323636106000257630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.core.signal import Signal from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement, GstProperty class DbMeter(GstMediaElement): ElementType = ElementType.Plugin MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'dB Meter') interval = GstProperty('level', default=50 * Gst.MSECOND) peak_ttl = GstProperty('level', default=Gst.SECOND, gst_name='peak-ttl') peak_falloff = GstProperty('level', default=20, gst_name='peak-falloff') def __init__(self, pipeline): super().__init__() self.level_ready = Signal() self.pipeline = pipeline self.level = Gst.ElementFactory.make('level', None) self.level.set_property('post-messages', True) self.level.set_property('interval', 50 * Gst.MSECOND) self.level.set_property('peak-ttl', Gst.SECOND) self.level.set_property('peak-falloff', 20) self.audio_convert = Gst.ElementFactory.make('audioconvert', None) self.pipeline.add(self.level) self.pipeline.add(self.audio_convert) self.level.link(self.audio_convert) bus = self.pipeline.get_bus() bus.add_signal_watch() self._handler = bus.connect('message::element', self.__on_message) def dispose(self): bus = self.pipeline.get_bus() bus.remove_signal_watch() bus.disconnect(self._handler) def sink(self): return self.level def src(self): return self.audio_convert def __on_message(self, bus, message): if message.src == self.level: structure = message.get_structure() if structure is not None and structure.has_name('level'): self.level_ready.emit(structure.get_value('peak'), structure.get_value('rms'), structure.get_value('decay')) linux-show-player-0.5.1/lisp/modules/gst_backend/elements/equalizer10.py000066400000000000000000000041151323636106000263400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement, GstProperty class Equalizer10(GstMediaElement): ElementType = ElementType.Plugin MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', '10 Bands Equalizer') band0 = GstProperty('equalizer', default=0) band1 = GstProperty('equalizer', default=0) band2 = GstProperty('equalizer', default=0) band3 = GstProperty('equalizer', default=0) band4 = GstProperty('equalizer', default=0) band5 = GstProperty('equalizer', default=0) band6 = GstProperty('equalizer', default=0) band7 = GstProperty('equalizer', default=0) band8 = GstProperty('equalizer', default=0) band9 = GstProperty('equalizer', default=0) def __init__(self, pipe): super().__init__() self.equalizer = Gst.ElementFactory.make("equalizer-10bands", None) self.audio_converter = Gst.ElementFactory.make("audioconvert", None) pipe.add(self.equalizer) pipe.add(self.audio_converter) self.equalizer.link(self.audio_converter) def sink(self): return self.equalizer def src(self): return self.audio_converter linux-show-player-0.5.1/lisp/modules/gst_backend/elements/jack_sink.py000066400000000000000000000124521323636106000261350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import jack import logging from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.core.has_properties import Property from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement, GstProperty class JackSink(GstMediaElement): ElementType = ElementType.Output MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'JACK Out') CLIENT_NAME = 'linux-show-player' CONNECT_MODE = 'none' _ControlClient = None _clients = [] connections = Property(default=[[] for _ in range(8)]) def __init__(self, pipeline): super().__init__() if JackSink._ControlClient is None: JackSink._ControlClient = jack.Client('LinuxShowPlayer_Control') self.pipeline = pipeline self.audio_resample = Gst.ElementFactory.make('audioresample') self.jack_sink = Gst.ElementFactory.make('jackaudiosink', 'sink') self._client_id = JackSink.__register_client_id() self._client_name = JackSink.CLIENT_NAME + '-' + str(self._client_id) self.jack_sink.set_property('client-name', self._client_name) self.jack_sink.set_property('connect', JackSink.CONNECT_MODE) self.pipeline.add(self.audio_resample) self.pipeline.add(self.jack_sink) self.audio_resample.link(self.jack_sink) self.connections = self.default_connections(JackSink._ControlClient) self.changed('connections').connect(self.__prepare_connections) bus = self.pipeline.get_bus() bus.add_signal_watch() self._handler = bus.connect('message', self.__on_message) def sink(self): return self.audio_resample def dispose(self): try: self.pipeline.get_bus().disconnect(self._handler) JackSink._clients.remove(self._client_id) finally: if not JackSink._clients: JackSink._ControlClient.deactivate() JackSink._ControlClient.close() JackSink._ControlClient = None @classmethod def default_connections(cls, client): # Up to 8 channels connections = [[] for _ in range(8)] if isinstance(client, jack.Client): # Search for default input ports input_ports = client.get_ports(name_pattern='^system:', is_audio=True, is_input=True) for n, port in enumerate(input_ports): if n < len(connections): connections[n].append(port.name) else: break return connections def __prepare_connections(self, value): if (self.pipeline.current_state == Gst.State.PLAYING or self.pipeline.current_state == Gst.State.PAUSED): self.__jack_connect() @classmethod def __register_client_id(cls): n = 0 for n, client in enumerate(cls._clients): if n != client: break if n == len(cls._clients) - 1: n += 1 cls._clients.insert(n, n) return n def __jack_connect(self): out_ports = JackSink._ControlClient.get_ports( name_pattern='^' + self._client_name + ':.+', is_audio=True) for port in out_ports: for conn_port in JackSink._ControlClient.get_all_connections(port): try: JackSink._ControlClient.disconnect(port, conn_port) except jack.JackError as e: logging.error('GST: JACK-SINK: {}'.format(e)) for output, in_ports in enumerate(self.connections): for input_name in in_ports: if output < len(out_ports): try: JackSink._ControlClient.connect(out_ports[output], input_name) except jack.JackError as e: logging.error('GST: JACK-SINK: {}'.format(e)) else: break def __on_message(self, bus, message): if message.src == self.jack_sink: if message.type == Gst.MessageType.STATE_CHANGED: change = message.parse_state_changed() # The jack ports are available when the the jackaudiosink # change from READY to PAUSED state if (change[0] == Gst.State.READY and change[1] == Gst.State.PAUSED): self.__jack_connect() linux-show-player-0.5.1/lisp/modules/gst_backend/elements/pitch.py000066400000000000000000000032221323636106000253030ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement, GstProperty class Pitch(GstMediaElement): ElementType = ElementType.Plugin MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'Pitch') pitch = GstProperty('gst_pitch', gst_name='pitch', default=1.0) def __init__(self, pipe): super().__init__() self.gst_pitch = Gst.ElementFactory.make('pitch', None) self.audio_converter = Gst.ElementFactory.make('audioconvert', None) pipe.add(self.gst_pitch) pipe.add(self.audio_converter) self.gst_pitch.link(self.audio_converter) def sink(self): return self.gst_pitch def src(self): return self.audio_converter linux-show-player-0.5.1/lisp/modules/gst_backend/elements/preset_src.py000066400000000000000000000100121323636106000263400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import math from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import MediaType from lisp.core.has_properties import Property from lisp.modules.gst_backend.gi_repository import Gst, GstApp from lisp.modules.gst_backend.gst_element import GstSrcElement class PresetSrc(GstSrcElement): MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'Preset Input') FREQ = 8000 SILENCE = lambda t: 0 PRESETS = { 'The 42 melody': lambda t: t * (42 & t >> 10), 'Mission': lambda t: (~t >> 2) * ((127 & t * (7 & t >> 10)) < (245 & t * (2 + (5 & t >> 14)))), "80's": lambda t: (t << 3) * [8 / 9, 1, 9 / 8, 6 / 5, 4 / 3, 3 / 2, 0][[0xd2d2c8, 0xce4088, 0xca32c8, 0x8e4009][t >> 14 & 3] >> (0x3dbe4688 >> (18 if (t >> 10 & 15) > 9 else t >> 10 & 15) * 3 & 7) * 3 & 7], 'Game 1': lambda t: (t * (0xCA98 >> (t >> 9 & 14) & 15) | t >> 8), 'Game 2': lambda t: t * 5 & (t >> 7) | t * 3 & (t * 4 >> 10) - int(math.cos(t >> 3)) * 10 | t >> 5 & int(math.sin(t >> 4)) | t >> 4, 'Club': lambda t: (((t * (t ^ t % 255) | (t >> 4)) >> 1) if (t & 4096) else (t >> 3) | (t << 2 if (t & 8192) else t)) + t * (((t >> 9) ^ ((t >> 9) - 1) ^ 1) % 13), 'Laser 1': lambda t: t * (t >> 5 | t >> 15) & 80 & t * 4 >> 9, 'Laser 2': lambda t: (t * (t >> 5 | t >> 23) & 43 & t >> 9) ^ (t & t >> 20 | t >> 9), 'Generator': lambda t: (t * (t >> 22 | t >> 3) & 43 & t >> 8) ^ (t & t >> 12 | t >> 4) } preset = Property(default='The 42 melody') def __init__(self, pipe): super().__init__() self.n_sample = 0 self.caps = 'audio/x-raw,format=U8,channels=1,layout=interleaved,' \ 'rate=' + str(PresetSrc.FREQ) self.app_src = Gst.ElementFactory.make('appsrc', 'appsrc') self.app_src.set_property('stream-type', GstApp.AppStreamType.SEEKABLE) self.app_src.set_property('format', Gst.Format.TIME) self.app_src.set_property('caps', Gst.Caps.from_string(self.caps)) self.app_src.connect('need-data', self.generate_samples) self.app_src.connect('seek-data', self.seek) self.audio_converter = Gst.ElementFactory.make('audioconvert', None) pipe.add(self.app_src) pipe.add(self.audio_converter) self.app_src.link(self.audio_converter) def stop(self): self.n_sample = 0 def interrupt(self): self.stop() def generate_samples(self, src, need_bytes): remaining = int(self.duration / 1000 * PresetSrc.FREQ - self.n_sample) if remaining <= 0: self.n_sample = 0 src.emit('end-of-stream') else: if need_bytes > remaining: need_bytes = remaining function = PresetSrc.PRESETS.get(self.preset, PresetSrc.SILENCE) sample = [] while len(sample) < need_bytes: value = function(self.n_sample) sample.append(int(value % 256)) self.n_sample += 1 buffer = Gst.Buffer.new_wrapped(bytes(sample)) src.emit('push-buffer', buffer) def seek(self, src, time): self.n_sample = int(abs(time / Gst.SECOND) * PresetSrc.FREQ) return True def src(self): return self.audio_converter linux-show-player-0.5.1/lisp/modules/gst_backend/elements/pulse_sink.py000066400000000000000000000027001323636106000263500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement class PulseSink(GstMediaElement): ElementType = ElementType.Output MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'PulseAudio Out') def __init__(self, pipe): super().__init__() self.pulse_sink = Gst.ElementFactory.make('pulsesink', 'sink') self.pulse_sink.set_property('client-name', 'Linux Show Player') pipe.add(self.pulse_sink) def sink(self): return self.pulse_sink linux-show-player-0.5.1/lisp/modules/gst_backend/elements/speed.py000066400000000000000000000057521323636106000253060ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.core.has_properties import Property from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement class Speed(GstMediaElement): ElementType = ElementType.Plugin MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'Speed') speed = Property(default=1.0) def __init__(self, pipeline): super().__init__() self.pipeline = pipeline self.scale_tempo = Gst.ElementFactory.make("scaletempo", None) self.audio_convert = Gst.ElementFactory.make("audioconvert", None) self.pipeline.add(self.scale_tempo) self.pipeline.add(self.audio_convert) self.scale_tempo.link(self.audio_convert) bus = self.pipeline.get_bus() bus.add_signal_watch() self._handler = bus.connect("message", self.__on_message) self._old_speed = self.speed self.changed('speed').connect(self.__prepare_speed) def __prepare_speed(self, value): if self._old_speed != value: self._old_speed = value if self.pipeline.current_state == Gst.State.PLAYING: self.__change_speed() def sink(self): return self.scale_tempo def src(self): return self.audio_convert def dispose(self): bus = self.pipeline.get_bus() bus.remove_signal_watch() bus.disconnect(self._handler) def __on_message(self, bus, message): if (message.type == Gst.MessageType.STATE_CHANGED and message.src == self.scale_tempo and message.parse_state_changed()[1] == Gst.State.PLAYING): self.__change_speed() def __change_speed(self): current_position = self.scale_tempo.query_position(Gst.Format.TIME)[1] self.scale_tempo.seek(self.speed, Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.ACCURATE, Gst.SeekType.SET, current_position, Gst.SeekType.NONE, 0) linux-show-player-0.5.1/lisp/modules/gst_backend/elements/uri_input.py000066400000000000000000000057211323636106000262200ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from concurrent.futures import ThreadPoolExecutor from os import path from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import MediaType from lisp.core.decorators import async_in_pool from lisp.core.has_properties import Property from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstProperty, \ GstSrcElement from lisp.modules.gst_backend.gst_utils import gst_uri_duration class UriInput(GstSrcElement): MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'URI Input') uri = GstProperty('decoder', default='') download = GstProperty('decoder', default=False) buffer_size = GstProperty('decoder', default=-1, gst_name='buffer-size') use_buffering = GstProperty('decoder', default=False, gst_name='use-buffering') _mtime = Property(default=-1) def __init__(self, pipe): super().__init__() self.decoder = Gst.ElementFactory.make("uridecodebin", None) self.audio_convert = Gst.ElementFactory.make("audioconvert", None) self._handler = self.decoder.connect("pad-added", self.__on_pad_added) pipe.add(self.decoder) pipe.add(self.audio_convert) self.changed('uri').connect(self.__uri_changed) def input_uri(self): return self.uri def dispose(self): self.decoder.disconnect(self._handler) def src(self): return self.audio_convert def __on_pad_added(self, *args): self.decoder.link(self.audio_convert) def __uri_changed(self, value): # Save the current mtime (file flag for last-change time) mtime = self._mtime # If the uri is a file, then update the current mtime if value.split('://')[0] == 'file': if path.exists(value.split('//')[1]): self._mtime = path.getmtime(value.split('//')[1]) else: mtime = None # If something is changed or the duration is invalid if mtime != self._mtime or self.duration < 0: self.__duration() @async_in_pool(pool=ThreadPoolExecutor(1)) def __duration(self): self.duration = gst_uri_duration(self.uri) linux-show-player-0.5.1/lisp/modules/gst_backend/elements/user_element.py000066400000000000000000000065371323636106000266770ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.core.has_properties import Property from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement class UserElement(GstMediaElement): ElementType = ElementType.Plugin MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'Custom Element') bin = Property(default='') def __init__(self, pipeline): super().__init__() self.pipeline = pipeline self.audio_convert_sink = Gst.ElementFactory.make("audioconvert", None) # A default assignment for the bin self.gst_bin = Gst.ElementFactory.make("identity", None) self.gst_bin.set_property("signal-handoffs", False) self.audio_convert_src = Gst.ElementFactory.make("audioconvert", None) pipeline.add(self.audio_convert_sink) pipeline.add(self.gst_bin) pipeline.add(self.audio_convert_src) self.audio_convert_sink.link(self.gst_bin) self.gst_bin.link(self.audio_convert_src) self._old_bin = self.gst_bin self.changed('bin').connect(self.__prepare_bin) def sink(self): return self.audio_convert_sink def src(self): return self.audio_convert_src def __prepare_bin(self, value): if value != '' and value != self._old_bin: self._old_bin = value # If in playing we need to restart the pipeline after unblocking playing = self.gst_bin.current_state == Gst.State.PLAYING # Block the stream pad = self.audio_convert_sink.sinkpads[0] probe = pad.add_probe(Gst.PadProbeType.BLOCK, lambda *a: 0, "") # Unlink the components self.audio_convert_sink.unlink(self.gst_bin) self.gst_bin.unlink(self.audio_convert_src) self.pipeline.remove(self.gst_bin) # Create the bin, when fail use a do-nothing element try: self.gst_bin = Gst.parse_bin_from_description(value, True) except Exception: self.gst_bin = Gst.ElementFactory.make("identity", None) self.gst_bin.set_property("signal-handoffs", False) # Link the components self.pipeline.add(self.gst_bin) self.audio_convert_sink.link(self.gst_bin) self.gst_bin.link(self.audio_convert_src) # Unblock the stream pad.remove_probe(probe) if playing: self.pipeline.set_state(Gst.State.PLAYING)linux-show-player-0.5.1/lisp/modules/gst_backend/elements/volume.py000066400000000000000000000044021323636106000255040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.backend.media_element import ElementType, MediaType from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_element import GstMediaElement, GstProperty, \ GstRuntimeProperty class Volume(GstMediaElement): ElementType = ElementType.Plugin MediaType = MediaType.Audio Name = QT_TRANSLATE_NOOP('MediaElementName', 'Volume') mute = GstProperty('gst_volume', default=False) volume = GstProperty('gst_volume', default=1.0) normal_volume = GstProperty('gst_normal_volume', default=1.0, gst_name='volume') current_mute = GstRuntimeProperty('gst_volume', 'mute') current_volume = GstRuntimeProperty('gst_volume', 'volume') def __init__(self, pipe): super().__init__() self.gst_volume = Gst.ElementFactory.make("volume", None) self.gst_normal_volume = Gst.ElementFactory.make("volume", None) self.audio_convert = Gst.ElementFactory.make("audioconvert", None) pipe.add(self.gst_normal_volume) pipe.add(self.gst_volume) pipe.add(self.audio_convert) self.gst_volume.link(self.gst_normal_volume) self.gst_normal_volume.link(self.audio_convert) def sink(self): return self.gst_volume def src(self): return self.audio_convert def stop(self): self.current_mute = self.mute self.current_volume = self.volume def interrupt(self): self.stop() linux-show-player-0.5.1/lisp/modules/gst_backend/gi_repository.py000066400000000000000000000007131323636106000252600ustar00rootroot00000000000000"""Utility module for importing and checking gi.repository packages once""" # "Solution" for https://bugzilla.gnome.org/show_bug.cgi?id=736260 import sys sys.modules["gi.overrides.Gst"] = None sys.modules["gi.overrides.GstPbutils"] = None import gi gi.require_version('Gst', '1.0') gi.require_version('GstPbutils', '1.0') gi.require_version('GstApp', '1.0') # noinspection PyUnresolvedReferences from gi.repository import Gst, GstPbutils, GObject, GstApp linux-show-player-0.5.1/lisp/modules/gst_backend/gst_backend.py000066400000000000000000000051561323636106000246340ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp import backend from lisp.backend.backend import Backend as BaseBackend from lisp.core.decorators import memoize from lisp.core.module import Module from lisp.cues.media_cue import MediaCue from lisp.modules.gst_backend.gst_utils import gst_parse_tags_list from lisp.modules.gst_backend.gst_utils import gst_uri_metadata, gst_mime_types, \ gst_uri_duration from lisp.ui.settings.app_settings import AppSettings from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.modules.gst_backend import elements, settings from lisp.modules.gst_backend.gi_repository import Gst from lisp.modules.gst_backend.gst_cue_factories import register_factories from lisp.modules.gst_backend.gst_media_settings import GstMediaSettings from lisp.modules.gst_backend.gst_settings import GstSettings class GstBackend(Module, BaseBackend): def __init__(self): # Initialize GStreamer Gst.init(None) # Register GStreamer settings widgets AppSettings.register_settings_widget(GstSettings) # Add MediaCue settings widget to the CueLayout CueSettingsRegistry().add_item(GstMediaSettings, MediaCue) # Register the GstMedia cue builder register_factories() elements.load() settings.load() backend.set_backend(self) def uri_duration(self, uri): return gst_uri_duration(uri) def uri_tags(self, uri): tags = gst_uri_metadata(uri).get_tags() if tags is not None: return gst_parse_tags_list(tags) return {} @memoize def supported_extensions(self): extensions = {'audio': [], 'video': []} for gst_mime, gst_extensions in gst_mime_types(): for mime in ['audio', 'video']: if gst_mime.startswith(mime): extensions[mime].extend(gst_extensions) return extensions linux-show-player-0.5.1/lisp/modules/gst_backend/gst_cue_factories.py000066400000000000000000000033441323636106000260550ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.core.configuration import config from lisp.cues.cue_factory import CueFactory from lisp.cues.media_cue import MediaCue from lisp.modules.gst_backend.gst_media import GstMedia def gst_media(id=None, pipeline=None): media = GstMedia() if pipeline is not None: media.pipe = pipeline return MediaCue(media, id=id) def uri_audio(id=None, uri=None): cue = gst_media(id=id, pipeline=compose_pipeline('UriInput')) if uri is not None: cue.media.element('UriInput').uri = uri return cue def capture_audio(id=None): return gst_media(id=id, pipeline=compose_pipeline('AutoSrc')) def compose_pipeline(input_element): return (input_element,) +\ tuple(config['Gst']['Pipeline'].replace(' ', '').split(',')) def register_factories(): CueFactory.register_factory('MediaCue', gst_media) CueFactory.register_factory('URIAudioCue', uri_audio) CueFactory.register_factory('CaptureAudioCue', capture_audio) linux-show-player-0.5.1/lisp/modules/gst_backend/gst_element.py000066400000000000000000000065241323636106000246760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.backend.media_element import MediaElement, ElementType from lisp.core.has_properties import Property class GstProperty(Property): def __init__(self, element_name, default=None, gst_name=None, adapter=None): super().__init__(default=default) self.element_name = element_name self.gst_name = gst_name self.adapter = adapter def __set__(self, instance, value): super().__set__(instance, value) if instance is not None: if self.adapter is not None: value = self.adapter(value) name = self.gst_name if self.gst_name is not None else self.name getattr(instance, self.element_name).set_property(name, value) class GstRuntimeProperty: def __init__(self, element_name, property_name, adapter=None): self.element_name = element_name self.property_name = property_name self.adapter = adapter def __get__(self, instance, cls): if instance is None: return self else: element = getattr(instance, self.element_name) return element.get_property(self.property_name) def __set__(self, instance, value): if self.adapter is not None: value = self.adapter(value) getattr(instance, self.element_name).set_property(self.property_name, value) class GstMediaElement(MediaElement): """All the subclass must take the pipeline as first __init__ argument""" def interrupt(self): """Called before Media interrupt""" def stop(self): """Called before Media stop""" def pause(self): """Called before Media pause""" def play(self): """Called before Media play""" def dispose(self): """Clean up the element""" def sink(self): """Return the GstElement used as sink""" return None def src(self): """Return the GstElement used as src""" return None def link(self, element): if self.src() is not None: sink = element.sink() if sink is not None: return self.src().link(sink) return False def unlink(self, element): if self.src() is not None: sink = element.sink() if sink is not None: return self.src().unlink(sink) return False class GstSrcElement(GstMediaElement): ElementType = ElementType.Input duration = Property(default=0) def input_uri(self): """Return the input uri or None""" return Nonelinux-show-player-0.5.1/lisp/modules/gst_backend/gst_media.py000066400000000000000000000253501323636106000243220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import weakref from lisp.backend.media import Media, MediaState from lisp.core.has_properties import Property from lisp.modules.gst_backend import elements from lisp.modules.gst_backend.gi_repository import Gst def validate_pipeline(pipe, rebuild=False): # The first must be an input element if pipe[0] not in elements.inputs().keys(): return False # The middle elements must be plugins elements if rebuild: if not isinstance(pipe, list): pipe = list(pipe) pipe[1:-1] = set(pipe[1:-1]).intersection( set(elements.plugins().keys())) else: if len(set(pipe[1:-1]) - set(elements.plugins().keys())) != 0: return False # The last must be an output element if pipe[-1] not in elements.outputs().keys(): return False return pipe if rebuild else True class GstMedia(Media): """Media implementation based on the GStreamer framework.""" pipe = Property(default=()) def __init__(self): super().__init__() self._state = MediaState.Null self._elements = [] self._old_pipe = '' self._loop_count = 0 self._gst_pipe = Gst.Pipeline() self._gst_state = Gst.State.NULL self._time_query = Gst.Query.new_position(Gst.Format.TIME) bus = self._gst_pipe.get_bus() bus.add_signal_watch() # Use a weakref instead of the method or the object will not be # garbage-collected on_message = weakref.WeakMethod(self.__on_message) handler = bus.connect('message', lambda *args: on_message()(*args)) weakref.finalize(self, self.__finalizer, self._gst_pipe, handler, self._elements) self.changed('loop').connect(self.__prepare_loops) self.changed('pipe').connect(self.__prepare_pipe) @Media.state.getter def state(self): return self._state def __prepare_loops(self, loops): self._loop_count = loops def __prepare_pipe(self, pipe): if pipe != self._old_pipe: self._old_pipe = pipe # If the pipeline is invalid raise an error pipe = validate_pipeline(pipe, rebuild=True) if not pipe: raise ValueError('Invalid pipeline "{0}"'.format(pipe)) # Build the pipeline elements_properties = self.elements_properties() self.__build_pipeline() self.update_elements(elements_properties) self._elements[0].changed('duration').connect( self.__duration_changed) self.__duration_changed(self._elements[0].duration) def current_time(self): ok, position = self._gst_pipe.query_position(Gst.Format.TIME) return position // Gst.MSECOND if ok else 0 def play(self): if self.state == MediaState.Stopped or self.state == MediaState.Paused: self.on_play.emit(self) for element in self._elements: element.play() self._state = MediaState.Playing self._gst_pipe.set_state(Gst.State.PLAYING) self._gst_pipe.get_state(Gst.SECOND) if self.start_time > 0 or self.stop_time > 0: self.seek(self.start_time) self.played.emit(self) def pause(self): if self.state == MediaState.Playing: self.on_pause.emit(self) for element in self._elements: element.pause() self._state = MediaState.Paused self._gst_pipe.set_state(Gst.State.PAUSED) self._gst_pipe.get_state(Gst.SECOND) # FIXME: the pipeline is not flushed (fucking GStreamer) self.paused.emit(self) def stop(self): if self.state == MediaState.Playing or self.state == MediaState.Paused: self.on_stop.emit(self) for element in self._elements: element.stop() self.interrupt(emit=False) self.stopped.emit(self) def __seek(self, position): # FIXME: not working when in pause (fix or disallow) if self.state == MediaState.Playing or self.state == MediaState.Paused: max_position = self.duration if 0 < self.stop_time < self.duration: max_position = self.stop_time if position < max_position: # Query segment info for the playback rate query = Gst.Query.new_segment(Gst.Format.TIME) self._gst_pipe.query(query) rate = Gst.Query.parse_segment(query)[0] # Check stop_position value stop_type = Gst.SeekType.NONE if self.stop_time > 0: stop_type = Gst.SeekType.SET # Seek the pipeline result = self._gst_pipe.seek( rate if rate > 0 else 1, Gst.Format.TIME, Gst.SeekFlags.FLUSH, Gst.SeekType.SET, position * Gst.MSECOND, stop_type, self.stop_time * Gst.MSECOND) return result return False def seek(self, position): if self.__seek(position): self.sought.emit(self, position) def element(self, class_name): for element in self._elements: if type(element).__name__ == class_name: return element def elements(self): return self._elements.copy() def elements_properties(self, only_changed=False): properties = {} for element in self._elements: e_properties = element.properties(only_changed) if e_properties: properties[type(element).__name__] = e_properties return properties def input_uri(self): try: return self._elements[0].input_uri() except Exception: pass def interrupt(self, dispose=False, emit=True): for element in self._elements: element.interrupt() state = self._state self._gst_pipe.set_state(Gst.State.NULL) if dispose: self._state = MediaState.Null else: self._gst_pipe.set_state(Gst.State.READY) self._state = MediaState.Stopped self._loop_count = self.loop if emit and (state == MediaState.Playing or state == MediaState.Paused): self.interrupted.emit(self) def properties(self, only_changed=False): properties = super().properties(only_changed).copy() properties['elements'] = self.elements_properties(only_changed) return properties def update_elements(self, properties): for element in self._elements: if type(element).__name__ in properties: element.update_properties(properties[type(element).__name__]) def update_properties(self, properties): elements_properties = properties.pop('elements', {}) super().update_properties(properties) self.update_elements(elements_properties) if self.state == MediaState.Null or self.state == MediaState.Error: self._state = MediaState.Stopped @staticmethod def _pipe_elements(): tmp = {} tmp.update(elements.inputs()) tmp.update(elements.outputs()) tmp.update(elements.plugins()) return tmp def __append_element(self, element): if self._elements: self._elements[-1].link(element) self._elements.append(element) def __remove_element(self, index): if index > 0: self._elements[index - 1].unlink(self._elements[index]) if index < len(self._elements) - 1: self._elements[index - 1].link(self._elements[index + 1]) self._elements[index].unlink(self._elements[index]) self._elements.pop(index).dispose() def __build_pipeline(self): # Set to NULL the pipeline self.interrupt(dispose=True) # Remove all pipeline children for __ in range(self._gst_pipe.get_children_count()): self._gst_pipe.remove(self._gst_pipe.get_child_by_index(0)) # Remove all the elements for __ in range(len(self._elements)): self.__remove_element(len(self._elements) - 1) # Create all the new elements pipe_elements = self._pipe_elements() for element in self.pipe: self.__append_element(pipe_elements[element](self._gst_pipe)) # Set to Stopped/READY the pipeline self._state = MediaState.Stopped self._gst_pipe.set_state(Gst.State.READY) self.elements_changed.emit(self) def __on_message(self, bus, message): if message.src == self._gst_pipe: if message.type == Gst.MessageType.STATE_CHANGED: self._gst_state = message.parse_state_changed()[1] elif message.type == Gst.MessageType.EOS: self.__on_eos() elif message.type == Gst.MessageType.CLOCK_LOST: self._gst_pipe.set_state(Gst.State.PAUSED) self._gst_pipe.set_state(Gst.State.PLAYING) if message.type == Gst.MessageType.ERROR: err, debug = message.parse_error() self._state = MediaState.Error self.interrupt(dispose=True, emit=False) self.error.emit(self, str(err), str(debug)) def __on_eos(self): if self._loop_count != 0: self._loop_count -= 1 self.seek(self.start_time) else: self._state = MediaState.Stopped self.eos.emit(self) self.interrupt(emit=False) def __duration_changed(self, duration): self.duration = duration @staticmethod def __finalizer(pipeline, connection_handler, media_elements): # Allow pipeline resources to be released pipeline.set_state(Gst.State.NULL) bus = pipeline.get_bus() bus.remove_signal_watch() bus.disconnect(connection_handler) for element in media_elements: element.dispose() linux-show-player-0.5.1/lisp/modules/gst_backend/gst_media_settings.py000066400000000000000000000107651323636106000262460ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from copy import deepcopy from PyQt5.QtCore import QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QGridLayout, QListWidget, QPushButton, \ QListWidgetItem from lisp.modules.gst_backend.gst_pipe_edit import GstPipeEditDialog from lisp.modules.gst_backend.settings import pages_by_element from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class GstMediaSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Media Settings') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QGridLayout()) self._pages = [] self._current_page = None self._settings = {} self._check = False self.listWidget = QListWidget(self) self.layout().addWidget(self.listWidget, 0, 0) self.pipeButton = QPushButton( translate('GstMediaSettings', 'Change Pipeline'), self) self.layout().addWidget(self.pipeButton, 1, 0) self.layout().setColumnStretch(0, 2) self.layout().setColumnStretch(1, 5) self.listWidget.currentItemChanged.connect(self.__change_page) self.pipeButton.clicked.connect(self.__edit_pipe) def load_settings(self, settings): settings = settings.get('_media_', {}) # Create a local copy of the configuration self._settings = deepcopy(settings) # Create the widgets pages = pages_by_element() for element in settings.get('pipe', ()): page = pages.get(element) if page is not None and issubclass(page, SettingsPage): page = page(parent=self) page.load_settings( settings.get('elements', {}) .get(element, page.ELEMENT.properties_defaults())) page.setVisible(False) self._pages.append(page) item = QListWidgetItem(translate('MediaElementName', page.Name)) self.listWidget.addItem(item) self.listWidget.setCurrentRow(0) def get_settings(self): settings = {'elements': {}} for page in self._pages: page_settings = page.get_settings() if page_settings: settings['elements'][page.ELEMENT.__name__] = page_settings # The pipeline is returned only if check is disabled if not self._check: settings['pipe'] = self._settings['pipe'] return {'_media_': settings} def enable_check(self, enabled): self._check = enabled for page in self._pages: page.enable_check(enabled) def __change_page(self, current, previous): if current is None: current = previous if self._current_page is not None: self.layout().removeWidget(self._current_page) self._current_page.hide() self._current_page = self._pages[self.listWidget.row(current)] self._current_page.show() self.layout().addWidget(self._current_page, 0, 1, 2, 1) def __edit_pipe(self): # Backup the settings self._settings = self.get_settings()['_media_'] # Show the dialog dialog = GstPipeEditDialog(self._settings.get('pipe', ()), parent=self) if dialog.exec_() == dialog.Accepted: # Reset the view self.listWidget.clear() if self._current_page is not None: self.layout().removeWidget(self._current_page) self._current_page.hide() self._current_page = None self._pages.clear() # Reload with the new pipeline self._settings['pipe'] = dialog.get_pipe() self.load_settings({'_media_': self._settings}) self.enable_check(self._check) linux-show-player-0.5.1/lisp/modules/gst_backend/gst_pipe_edit.py000066400000000000000000000146461323636106000252130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QDialog, QGridLayout, QComboBox, QListWidget, \ QAbstractItemView, QVBoxLayout, QPushButton, QDialogButtonBox, QWidget, \ QListWidgetItem from lisp.modules.gst_backend import elements from lisp.ui.ui_utils import translate class GstPipeEdit(QWidget): def __init__(self, pipe, app_mode=False, **kwargs): super().__init__(**kwargs) self.setLayout(QGridLayout()) self.layout().setAlignment(Qt.AlignTop) self._app_mode = app_mode # Input selection self.inputBox = QComboBox(self) self.layout().addWidget(self.inputBox, 0, 0, 1, 3) self.__init_inputs() # Current plugins list self.currentList = QListWidget(self) self.currentList.setDragEnabled(True) self.currentList.setDragDropMode(QAbstractItemView.InternalMove) self.layout().addWidget(self.currentList, 1, 0) # Available plugins list self.availableList = QListWidget(self) self.layout().addWidget(self.availableList, 1, 2) # Output selection self.outputBox = QComboBox(self) self.layout().addWidget(self.outputBox, 4, 0, 1, 3) self.__init_outputs() # Add/Remove plugins buttons self.buttonsLayout = QVBoxLayout() self.layout().addLayout(self.buttonsLayout, 1, 1) self.layout().setAlignment(self.buttonsLayout, Qt.AlignHCenter) self.addButton = QPushButton(self) self.addButton.setIcon(QIcon.fromTheme('go-previous')) self.addButton.clicked.connect(self.__add_plugin) self.buttonsLayout.addWidget(self.addButton) self.buttonsLayout.setAlignment(self.addButton, Qt.AlignHCenter) self.delButton = QPushButton(self) self.delButton.setIcon(QIcon.fromTheme('go-next')) self.delButton.clicked.connect(self.__remove_plugin) self.buttonsLayout.addWidget(self.delButton) self.buttonsLayout.setAlignment(self.delButton, Qt.AlignHCenter) # Load the pipeline self.set_pipe(pipe) def set_pipe(self, pipe): if pipe: if not self._app_mode: self.inputBox.setCurrentText( translate('MediaElementName', elements.input_name(pipe[0]))) self.outputBox.setCurrentText( translate('MediaElementName', elements.output_name(pipe[-1]))) self.__init_current_plugins(pipe) self.__init_available_plugins(pipe) def get_pipe(self): pipe = [] if self._app_mode else [self.inputBox.currentData()] for n in range(self.currentList.count()): pipe.append(self.currentList.item(n).data(Qt.UserRole)) pipe.append(self.outputBox.currentData()) return tuple(pipe) def __init_inputs(self): if self._app_mode: self.inputBox.setEnabled(False) else: inputs_by_name = {} for key, input in elements.inputs().items(): inputs_by_name[translate('MediaElementName', input.Name)] = key for name in sorted(inputs_by_name): self.inputBox.addItem(name, inputs_by_name[name]) self.inputBox.setEnabled(self.inputBox.count() > 1) def __init_outputs(self): outputs_by_name = {} for key, output in elements.outputs().items(): outputs_by_name[translate('MediaElementName', output.Name)] = key for name in sorted(outputs_by_name): self.outputBox.addItem(name, outputs_by_name[name]) self.outputBox.setEnabled(self.outputBox.count() > 1) def __init_current_plugins(self, pipe): self.currentList.clear() # If not in app_mode, the first pipe element is the input # the last the output start = 0 if self._app_mode else 1 for plugin in pipe[start:-1]: item = QListWidgetItem( translate('MediaElementName', elements.plugin_name(plugin))) item.setData(Qt.UserRole, plugin) self.currentList.addItem(item) def __init_available_plugins(self, pipe): self.availableList.clear() for plugin in elements.plugins(): if plugin not in pipe: item = QListWidgetItem( translate('MediaElementName', elements.plugin_name(plugin))) item.setData(Qt.UserRole, plugin) self.availableList.addItem(item) def __add_plugin(self): item = self.availableList.takeItem(self.availableList.currentRow()) self.currentList.addItem(item) def __remove_plugin(self): item = self.currentList.takeItem(self.currentList.currentRow()) self.availableList.addItem(item) class GstPipeEditDialog(QDialog): def __init__(self, pipe, app_mode=False, **kwargs): super().__init__(**kwargs) self.setWindowTitle(translate('GstPipelineEdit', 'Edit Pipeline')) self.setWindowModality(Qt.ApplicationModal) self.setMaximumSize(500, 400) self.setMinimumSize(500, 400) self.resize(500, 400) self.setLayout(QVBoxLayout()) # GstPipeEdit self.pipeEdit = GstPipeEdit(pipe, app_mode=app_mode, parent=self) self.layout().addWidget(self.pipeEdit) # Confirm/Cancel buttons self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.layout().addWidget(self.dialogButtons) self.dialogButtons.accepted.connect(self.accept) self.dialogButtons.rejected.connect(self.reject) def get_pipe(self): return self.pipeEdit.get_pipe() linux-show-player-0.5.1/lisp/modules/gst_backend/gst_settings.py000066400000000000000000000036161323636106000251040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QVBoxLayout, QGroupBox from lisp.modules.gst_backend.gst_pipe_edit import GstPipeEdit from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class GstSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'GStreamer settings') def __init__(self, **kwargs): super().__init__() self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.pipeGroup = QGroupBox(self) self.pipeGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.pipeGroup) self.pipeEdit = GstPipeEdit([], app_mode=True) self.pipeGroup.layout().addWidget(self.pipeEdit) self.retranslateUi() def retranslateUi(self): self.pipeGroup.setTitle(translate('GstSettings', 'Pipeline')) def get_settings(self): return {'Gst': {'pipeline': ', '.join(self.pipeEdit.get_pipe())}} def load_settings(self, settings): pipe = tuple(settings['Gst']['pipeline'].replace(' ', '').split(',')) self.pipeEdit.set_pipe(pipe) linux-show-player-0.5.1/lisp/modules/gst_backend/gst_utils.py000066400000000000000000000042221323636106000243760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see from urllib.parse import unquote, quote from lisp.backend.audio_utils import uri_duration from lisp.modules.gst_backend.gi_repository import Gst, GstPbutils def gst_uri_duration(uri): # First try to use the base implementation, because it's faster duration = uri_duration(uri) try: if duration <= 0: duration = gst_uri_metadata(uri).get_duration() // Gst.MSECOND finally: return duration if duration >= 0 else 0 def gst_mime_types(): for gtf in Gst.TypeFindFactory.get_list(): caps = gtf.get_caps() if caps is not None: for i in range(caps.get_size()): mime = caps.get_structure(i).to_string() extensions = gtf.get_extensions() yield mime, extensions def gst_uri_metadata(uri): """Discover media-file metadata using GStreamer.""" discoverer = GstPbutils.Discoverer() uri = uri.split("://") info = discoverer.discover_uri(uri[0] + "://" + quote(unquote(uri[1]))) return info # Adaption of the code found in https://github.com/ch3pjw/pyam def gst_parse_tags_list(gst_tag_list): """Takes a GstTagList object and returns a dict.""" parsed_tags = {} def parse_tag(gst_tag_list, tag_name, parsed_tags): parsed_tags[tag_name] = gst_tag_list.get_value_index(tag_name, 0) gst_tag_list.foreach(parse_tag, parsed_tags) return parsed_tagslinux-show-player-0.5.1/lisp/modules/gst_backend/i18n/000077500000000000000000000000001323636106000225665ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_cs.qm000066400000000000000000000132211323636106000260550ustar00rootroot00000000000000s4C" إ!C"%?'Lej5Ua ʉ ?:Ef a4$ eK d t Y   1 P Ku  UC\  W  3 e^ a uI!]R Yfjf Ils{9J܋&cS TZehNitZaYzen ALSA ALSA deviceAlsaSinkSettings~ZaYzen ALSA, jak jsou stanovena v souboru s nastavenm asound8ALSA devices, as defined in an asound configuration fileAlsaSinkSettingsStla ova  CompressorAudioDynamicSettings$Stla ova /Rozpna Compressor/ExpanderAudioDynamicSettingsTvar kYivky Curve ShapeAudioDynamicSettingsRozpna ExpanderAudioDynamicSettingsTvrd koleno Hard KneeAudioDynamicSettings PomrRatioAudioDynamicSettingsMkk koleno Soft KneeAudioDynamicSettingsPrh (dB)Threshold (dB)AudioDynamicSettingsTypTypeAudioDynamicSettingsVyv~en zvuku Audio PanAudioPanSettingsNa stYedCenterAudioPanSettings VlevoLeftAudioPanSettings VpravoRightAudioPanSettings4Nastaven mYidla decibeloDbMeter settingsDbMeterSettings0Opadvn vrcholu (dB/s)Peak falloff (dB/sec)DbMeterSettings Ttl vrcholu (ms) Peak ttl (ms)DbMeterSettings. as mezi hladinami (ms)Time between levels (ms)DbMeterSettings:Desetipsmov ekvalizr (IIR)10 Bands Equalizer (IIR)Equalizer10Settings4Zmnit komunika n spojenChange PipelineGstMediaSettings6Upravit komunika n spojen Edit PipelineGstPipelineEdit&Komunika n spojenPipeline GstSettings SpojitConnectJackSinkSettingsSpojen ConnectionsJackSinkSettingsOdpojit DisconnectJackSinkSettingsUpravit spojenEdit connectionsJackSinkSettings Vstupn pYpojky Input portsJackSinkSettings"Vstupn pYpojky Output portsJackSinkSettings.Desetipsmov ekvalizr10 Bands EqualizerMediaElementNameVstup ALSAALSA OutMediaElementNameVyv~en zvuku Audio PanMediaElementName$Stla ova /Rozpna Compressor/ExpanderMediaElementNameVlastn prvekCustom ElementMediaElementNameVstup JACKJACK OutMediaElementNameVaka tnuPitchMediaElementName&Vstup pYednastaven Preset InputMediaElementName"Vstup PulseAudioPulseAudio OutMediaElementNameRychlostSpeedMediaElementNameVstup systmu System InputMediaElementNameVstup systmu System OutMediaElementNameVstup URI URI InputMediaElementNameHlasitostVolumeMediaElementName MYidlo decibelodB MeterMediaElementNameVaka tnuPitch PitchSettingsPoltny {0:+}{0:+} semitones PitchSettingsPYednastavenPresetsPresetSrcSettings.Nastaven pro GStreamerGStreamer settingsSettingsPageNameNastaven mdiMedia SettingsSettingsPageNameRychlostSpeed SpeedSettingsVaechny soubory All filesUriInputSettingsRPokusit se o sta~en na seovch proudech#Attempt download on network streamsUriInputSettings`Velikost vyrovnvac pamti (-1 vchoz hodnota)Buffer size (-1 default value)UriInputSettings<Ukldn do vyrovnvac pamti BufferingUriInputSettingsVybrat soubor Choose fileUriInputSettingsNajt soubor Find FileUriInputSettings ZdrojSourceUriInputSettingsJPou~t ukldn do vyrovnvac pamti Use BufferingUriInputSettings<Jen pro pokro ilho u~ivatele!Only for advanced user!UserElementSettings4U~ivatelem stanoven prvkyUser defined elementsUserElementSettings.Normalizovan hlasitostNormalized volumeVolumeSettingsNastavit znovuResetVolumeSettingsHlasitostVolumeVolumeSettingslinux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_cs.ts000066400000000000000000000311021323636106000260640ustar00rootroot00000000000000 AlsaSinkSettings ALSA device Zařízení ALSA ALSA devices, as defined in an asound configuration file Zařízení ALSA, jak jsou stanovena v souboru s nastavením asound AudioDynamicSettings Compressor Stlačovač Expander Rozpínač Soft Knee Měkké koleno Hard Knee Tvrdé koleno Compressor/Expander Stlačovač/Rozpínač Type Typ Curve Shape Tvar křivky Ratio Poměr Threshold (dB) Práh (dB) AudioPanSettings Audio Pan Vyvážení zvuku Center Na střed Left Vlevo Right Vpravo DbMeterSettings DbMeter settings Nastavení měřidla decibelů Time between levels (ms) Čas mezi hladinami (ms) Peak ttl (ms) Ttl vrcholu (ms) Peak falloff (dB/sec) Opadávání vrcholu (dB/s) Equalizer10Settings 10 Bands Equalizer (IIR) Desetipásmový ekvalizér (IIR) GstMediaSettings Change Pipeline Změnit komunikační spojení GstPipelineEdit Edit Pipeline Upravit komunikační spojení GstSettings Pipeline Komunikační spojení JackSinkSettings Connections Spojení Edit connections Upravit spojení Output ports Výstupní přípojky Input ports Vstupní přípojky Connect Spojit Disconnect Odpojit MediaElementName Compressor/Expander Stlačovač/Rozpínač Audio Pan Vyvážení zvuku PulseAudio Out Výstup PulseAudio Volume Hlasitost dB Meter Měřidlo decibelů System Input Vstup systému ALSA Out Výstup ALSA JACK Out Výstup JACK Custom Element Vlastní prvek System Out Výstup systému Pitch Výška tónu URI Input Vstup URI 10 Bands Equalizer Desetipásmový ekvalizér Speed Rychlost Preset Input Vstup přednastavení PitchSettings Pitch Výška tónu {0:+} semitones Půltóny {0:+} PresetSrcSettings Presets Přednastavení SettingsPageName GStreamer settings Nastavení pro GStreamer Media Settings Nastavení médií SpeedSettings Speed Rychlost UriInputSettings Source Zdroj Find File Najít soubor Buffering Ukládání do vyrovnávací paměti Use Buffering Použít ukládání do vyrovnávací paměti Attempt download on network streams Pokusit se o stažení na síťových proudech Buffer size (-1 default value) Velikost vyrovnávací paměti (-1 výchozí hodnota) Choose file Vybrat soubor All files Všechny soubory UserElementSettings User defined elements Uživatelem stanovené prvky Only for advanced user! Jen pro pokročilého uživatele! VolumeSettings Volume Hlasitost Normalized volume Normalizovaná hlasitost Reset Nastavit znovu linux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_en.qm000066400000000000000000000122351323636106000260560ustar00rootroot00000000000000s64C` إ!C " G'IL ej5Ga ʉ ?:E a4 eK d t   ] 1 P  Ku X UC"   3 e^  uI]R +fZf l]{9mF܋&cS TZehNiALSA device ALSA deviceAlsaSinkSettingspALSA devices, as defined in an asound configuration file8ALSA devices, as defined in an asound configuration fileAlsaSinkSettingsCompressor CompressorAudioDynamicSettings&Compressor/ExpanderCompressor/ExpanderAudioDynamicSettingsCurve Shape Curve ShapeAudioDynamicSettingsExpanderExpanderAudioDynamicSettingsHard Knee Hard KneeAudioDynamicSettings RatioRatioAudioDynamicSettingsSoft Knee Soft KneeAudioDynamicSettingsThreshold (dB)Threshold (dB)AudioDynamicSettingsTypeTypeAudioDynamicSettingsAudio Pan Audio PanAudioPanSettings CenterCenterAudioPanSettingsLeftLeftAudioPanSettings RightRightAudioPanSettings DbMeter settingsDbMeter settingsDbMeterSettings*Peak falloff (dB/sec)Peak falloff (dB/sec)DbMeterSettingsPeak ttl (ms) Peak ttl (ms)DbMeterSettings0Time between levels (ms)Time between levels (ms)DbMeterSettings010 Bands Equalizer (IIR)10 Bands Equalizer (IIR)Equalizer10SettingsChange PipelineChange PipelineGstMediaSettingsEdit Pipeline Edit PipelineGstPipelineEditPipelinePipeline GstSettingsConnectConnectJackSinkSettingsConnections ConnectionsJackSinkSettingsDisconnect DisconnectJackSinkSettings Edit connectionsEdit connectionsJackSinkSettingsInput ports Input portsJackSinkSettingsOutput ports Output portsJackSinkSettings$10 Bands Equalizer10 Bands EqualizerMediaElementNameALSA OutALSA OutMediaElementNameAudio Pan Audio PanMediaElementName&Compressor/ExpanderCompressor/ExpanderMediaElementNameCustom ElementCustom ElementMediaElementNameJACK OutJACK OutMediaElementName PitchPitchMediaElementNamePreset Input Preset InputMediaElementNamePulseAudio OutPulseAudio OutMediaElementName SpeedSpeedMediaElementNameSystem Input System InputMediaElementNameSystem Out System OutMediaElementNameURI Input URI InputMediaElementName VolumeVolumeMediaElementNamedB MeterdB MeterMediaElementName PitchPitch PitchSettings{0:+} semitones{0:+} semitones PitchSettingsPresetsPresetsPresetSrcSettings$GStreamer settingsGStreamer settingsSettingsPageNameMedia SettingsMedia SettingsSettingsPageName SpeedSpeed SpeedSettingsAll files All filesUriInputSettingsFAttempt download on network streams#Attempt download on network streamsUriInputSettings<Buffer size (-1 default value)Buffer size (-1 default value)UriInputSettingsBuffering BufferingUriInputSettingsChoose file Choose fileUriInputSettingsFind File Find FileUriInputSettings SourceSourceUriInputSettingsUse Buffering Use BufferingUriInputSettings.Only for advanced user!Only for advanced user!UserElementSettings*User defined elementsUser defined elementsUserElementSettings"Normalized volumeNormalized volumeVolumeSettings ResetResetVolumeSettings VolumeVolumeVolumeSettingslinux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_en.ts000066400000000000000000000303171323636106000260700ustar00rootroot00000000000000 AlsaSinkSettings ALSA device ALSA device ALSA devices, as defined in an asound configuration file ALSA devices, as defined in an asound configuration file AudioDynamicSettings Compressor Compressor Expander Expander Soft Knee Soft Knee Hard Knee Hard Knee Compressor/Expander Compressor/Expander Type Type Curve Shape Curve Shape Ratio Ratio Threshold (dB) Threshold (dB) AudioPanSettings Audio Pan Audio Pan Center Center Left Left Right Right DbMeterSettings DbMeter settings DbMeter settings Time between levels (ms) Time between levels (ms) Peak ttl (ms) Peak ttl (ms) Peak falloff (dB/sec) Peak falloff (dB/sec) Equalizer10Settings 10 Bands Equalizer (IIR) 10 Bands Equalizer (IIR) GstMediaSettings Change Pipeline Change Pipeline GstPipelineEdit Edit Pipeline Edit Pipeline GstSettings Pipeline Pipeline JackSinkSettings Connections Connections Edit connections Edit connections Output ports Output ports Input ports Input ports Connect Connect Disconnect Disconnect MediaElementName Compressor/Expander Compressor/Expander Audio Pan Audio Pan PulseAudio Out PulseAudio Out Volume Volume dB Meter dB Meter System Input System Input ALSA Out ALSA Out JACK Out JACK Out Custom Element Custom Element System Out System Out Pitch Pitch URI Input URI Input 10 Bands Equalizer 10 Bands Equalizer Speed Speed Preset Input Preset Input PitchSettings Pitch Pitch {0:+} semitones {0:+} semitones PresetSrcSettings Presets Presets SettingsPageName GStreamer settings GStreamer settings Media Settings Media Settings SpeedSettings Speed Speed UriInputSettings Source Source Find File Find File Buffering Buffering Use Buffering Use Buffering Attempt download on network streams Attempt download on network streams Buffer size (-1 default value) Buffer size (-1 default value) Choose file Choose file All files All files UserElementSettings User defined elements User defined elements Only for advanced user! Only for advanced user! VolumeSettings Volume Volume Normalized volume Normalized volume Reset Reset linux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_es.qm000066400000000000000000000130751323636106000260660ustar00rootroot00000000000000s4C إ!C"I['Lej5a ʉI ?:E6 a4 eK d t s   1W P  Ku  UC  c  3 e^ e uI=]R cff Wl{9P܋&cS TZeShNDi$ Dispositivo ALSA ALSA deviceAlsaSinkSettingsDispositivos ALSA, segn estn definidos en un archivo de configuracin asound8ALSA devices, as defined in an asound configuration fileAlsaSinkSettingsCompresor CompressorAudioDynamicSettings$Compresor/ExpansorCompressor/ExpanderAudioDynamicSettings"Forma de la curva Curve ShapeAudioDynamicSettingsExpansorExpanderAudioDynamicSettingsDuro Hard KneeAudioDynamicSettingsProporcinRatioAudioDynamicSettings Suave Soft KneeAudioDynamicSettingsUmbral (dB)Threshold (dB)AudioDynamicSettingsTipoTypeAudioDynamicSettingsPaneo de audio Audio PanAudioPanSettings CentroCenterAudioPanSettingsIzquierdaLeftAudioPanSettingsDerechaRightAudioPanSettings,Ajustes del medidor dBDbMeter settingsDbMeterSettings<Decaimiento de picos (dB/seg.)Peak falloff (dB/sec)DbMeterSettingsPicos ttl (ms) Peak ttl (ms)DbMeterSettings8Intrvalo entre niveles (ms)Time between levels (ms)DbMeterSettings<Equalizador de 10 bandas (IIR)10 Bands Equalizer (IIR)Equalizer10Settings Cambiar PipelineChange PipelineGstMediaSettingsEditar Pipeline Edit PipelineGstPipelineEditPipelinePipeline GstSettingsConectarConnectJackSinkSettingsConexiones ConnectionsJackSinkSettingsDesconectar DisconnectJackSinkSettings"Editar conexionesEdit connectionsJackSinkSettings$Puertos de entrada Input portsJackSinkSettings"Puertos de salida Output portsJackSinkSettings0Ecualizador de 10 bandas10 Bands EqualizerMediaElementNameSalida ALSAALSA OutMediaElementNamePaneo de audio Audio PanMediaElementName$Compresor/ExpansorCompressor/ExpanderMediaElementName,Elemento personalizadoCustom ElementMediaElementNameSalida JACKJACK OutMediaElementNameTonoPitchMediaElementName$Preset de entradas Preset InputMediaElementName(Salida de PulseAudioPulseAudio OutMediaElementNameVelocidadSpeedMediaElementName$Entrada de sistema System InputMediaElementName"Salida de sistema System OutMediaElementNameEntrada URI URI InputMediaElementNameVolumenVolumeMediaElementNameMedidor de dBdB MeterMediaElementNameTonoPitch PitchSettings{0:+} semitonos{0:+} semitones PitchSettingsPresetsPresetsPresetSrcSettings(Ajustes de GStreamerGStreamer settingsSettingsPageName Ajustes de MediaMedia SettingsSettingsPageNameVelocidadSpeed SpeedSettings$Todos los archivos All filesUriInputSettingsHIntentar descargar en streams de red#Attempt download on network streamsUriInputSettingsPTamao del buffer (-1 valor por defecto)Buffer size (-1 default value)UriInputSettingsBuffering BufferingUriInputSettingsElegir archivo Choose fileUriInputSettings"Encontrar archivo Find FileUriInputSettings OrigenSourceUriInputSettingsUsar Buffering Use BufferingUriInputSettings<Slo para usuarios avanzados!Only for advanced user!UserElementSettingsDElementos definidos por el usuarioUser defined elementsUserElementSettings&Volumen normalizadoNormalized volumeVolumeSettingsReestablecerResetVolumeSettingsVolumenVolumeVolumeSettingslinux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_es.ts000066400000000000000000000306471323636106000261030ustar00rootroot00000000000000 AlsaSinkSettings ALSA device Dispositivo ALSA ALSA devices, as defined in an asound configuration file Dispositivos ALSA, según están definidos en un archivo de configuración asound AudioDynamicSettings Compressor Compresor Expander Expansor Soft Knee Suave Hard Knee Duro Compressor/Expander Compresor/Expansor Type Tipo Curve Shape Forma de la curva Ratio Proporción Threshold (dB) Umbral (dB) AudioPanSettings Audio Pan Paneo de audio Center Centro Left Izquierda Right Derecha DbMeterSettings DbMeter settings Ajustes del medidor dB Time between levels (ms) Intérvalo entre niveles (ms) Peak ttl (ms) Picos ttl (ms) Peak falloff (dB/sec) Decaimiento de picos (dB/seg.) Equalizer10Settings 10 Bands Equalizer (IIR) Equalizador de 10 bandas (IIR) GstMediaSettings Change Pipeline Cambiar Pipeline GstPipelineEdit Edit Pipeline Editar Pipeline GstSettings Pipeline Pipeline JackSinkSettings Connections Conexiones Edit connections Editar conexiones Output ports Puertos de salida Input ports Puertos de entrada Connect Conectar Disconnect Desconectar MediaElementName Compressor/Expander Compresor/Expansor Audio Pan Paneo de audio PulseAudio Out Salida de PulseAudio Volume Volumen dB Meter Medidor de dB System Input Entrada de sistema ALSA Out Salida ALSA JACK Out Salida JACK Custom Element Elemento personalizado System Out Salida de sistema Pitch Tono URI Input Entrada URI 10 Bands Equalizer Ecualizador de 10 bandas Speed Velocidad Preset Input Preset de entradas PitchSettings Pitch Tono {0:+} semitones {0:+} semitonos PresetSrcSettings Presets Presets SettingsPageName GStreamer settings Ajustes de GStreamer Media Settings Ajustes de Media SpeedSettings Speed Velocidad UriInputSettings Source Origen Find File Encontrar archivo Buffering Buffering Use Buffering Usar Buffering Attempt download on network streams Intentar descargar en streams de red Buffer size (-1 default value) Tamaño del buffer (-1 valor por defecto) Choose file Elegir archivo All files Todos los archivos UserElementSettings User defined elements Elementos definidos por el usuario Only for advanced user! ¡Sólo para usuarios avanzados! VolumeSettings Volume Volumen Normalized volume Volumen normalizado Reset Reestablecer linux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_fr.qm000066400000000000000000000132311323636106000260600ustar00rootroot00000000000000s4C0 إ!C-"A'Lej5a ʉW ?:Et a4. eK d t   1 P - Ku#  UC  m : 3 e^ s uIS]R ff ]l{9R܋1&cS4TZehNLi"Priphrique ALSA ALSA deviceAlsaSinkSettingsPriphriques ALSA, comme dfinis dans le fichier de configuration asound8ALSA devices, as defined in an asound configuration fileAlsaSinkSettingsCompresseur CompressorAudioDynamicSettings.Compresseur / expandeurCompressor/ExpanderAudioDynamicSettings$Forme de la courbe Curve ShapeAudioDynamicSettingsExpandeurExpanderAudioDynamicSettingsCoude dur Hard KneeAudioDynamicSettings RatioRatioAudioDynamicSettingsCoude lger Soft KneeAudioDynamicSettingsSeuil (dB)Threshold (dB)AudioDynamicSettingsTypeTypeAudioDynamicSettings"Panoramique audio Audio PanAudioPanSettings CentreCenterAudioPanSettings GaucheLeftAudioPanSettings DroitRightAudioPanSettings:Prfrences du mesureur de dBDbMeter settingsDbMeterSettings,Crte falloff (dB/sec)Peak falloff (dB/sec)DbMeterSettingsCrte ttl (ms) Peak ttl (ms)DbMeterSettings8Temps entre les niveaux (ms)Time between levels (ms)DbMeterSettings4galiseur 10 bandes (IIR) 10 Bands Equalizer (IIR)Equalizer10Settings$Changer le bitoducChange PipelineGstMediaSettings"diter le bitoduc Edit PipelineGstPipelineEditBitoducPipeline GstSettingsConnecterConnectJackSinkSettingsConnexions ConnectionsJackSinkSettingsDconnecter DisconnectJackSinkSettings*diter les connexionsEdit connectionsJackSinkSettingsPorts d'entre Input portsJackSinkSettingsPorts de sortie Output portsJackSinkSettings&galiseur 10 bandes10 Bands EqualizerMediaElementNameSortie ALSAALSA OutMediaElementName"Panoramique audio Audio PanMediaElementName.Compresseur / expandeurCompressor/ExpanderMediaElementName(lment personnalisCustom ElementMediaElementNameSortie JACKJACK OutMediaElementNameHauteur de notePitchMediaElementName.Entre pr-slectionne Preset InputMediaElementName"Sortie PulseAudioPulseAudio OutMediaElementNameVitesseSpeedMediaElementNameEntre systme System InputMediaElementNameSortie systme System OutMediaElementNameEntre URI URI InputMediaElementName VolumeVolumeMediaElementNameMesureur de dBdB MeterMediaElementNameHauteur de notePitch PitchSettings{0:+} demi-tons{0:+} semitones PitchSettingsPr-slectionsPresetsPresetSrcSettings*Prfrences GStreamerGStreamer settingsSettingsPageName,Prfrences des mdiasMedia SettingsSettingsPageNameVitesseSpeed SpeedSettings"Tous les fichiers All filesUriInputSettingsPEssayer de tlcharger en flux de rseau#Attempt download on network streamsUriInputSettingsNTaille du tampon (valeur par dfaut -1)Buffer size (-1 default value)UriInputSettings Tampon BufferingUriInputSettings$Choisir un fichier Choose fileUriInputSettings$Trouver le fichier Find FileUriInputSettings SourceSourceUriInputSettings$Utiliser le tampon Use BufferingUriInputSettingsJSeulement pour utilisateurs avancs !Only for advanced user!UserElementSettingsHlments spcifis par l'utilisateurUser defined elementsUserElementSettings Volume normalisNormalized volumeVolumeSettingsRinitialiserResetVolumeSettings VolumeVolumeVolumeSettingslinux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_fr.ts000066400000000000000000000310041323636106000260670ustar00rootroot00000000000000 AlsaSinkSettings ALSA device Périphérique ALSA ALSA devices, as defined in an asound configuration file Périphériques ALSA, comme définis dans le fichier de configuration asound AudioDynamicSettings Compressor Compresseur Expander Expandeur Soft Knee Coude léger Hard Knee Coude dur Compressor/Expander Compresseur / expandeur Type Type Curve Shape Forme de la courbe Ratio Ratio Threshold (dB) Seuil (dB) AudioPanSettings Audio Pan Panoramique audio Center Centre Left Gauche Right Droit DbMeterSettings DbMeter settings Préférences du mesureur de dB Time between levels (ms) Temps entre les niveaux (ms) Peak ttl (ms) Crête ttl (ms) Peak falloff (dB/sec) Crête falloff (dB/sec) Equalizer10Settings 10 Bands Equalizer (IIR) Égaliseur 10 bandes (IIR) GstMediaSettings Change Pipeline Changer le bitoduc GstPipelineEdit Edit Pipeline Éditer le bitoduc GstSettings Pipeline Bitoduc JackSinkSettings Connections Connexions Edit connections Éditer les connexions Output ports Ports de sortie Input ports Ports d'entrée Connect Connecter Disconnect Déconnecter MediaElementName Compressor/Expander Compresseur / expandeur Audio Pan Panoramique audio PulseAudio Out Sortie PulseAudio Volume Volume dB Meter Mesureur de dB System Input Entrée système ALSA Out Sortie ALSA JACK Out Sortie JACK Custom Element Élément personnalisé System Out Sortie système Pitch Hauteur de note URI Input Entrée URI 10 Bands Equalizer Égaliseur 10 bandes Speed Vitesse Preset Input Entrée pré-sélectionnée PitchSettings Pitch Hauteur de note {0:+} semitones {0:+} demi-tons PresetSrcSettings Presets Pré-sélections SettingsPageName GStreamer settings Préférences GStreamer Media Settings Préférences des médias SpeedSettings Speed Vitesse UriInputSettings Source Source Find File Trouver le fichier Buffering Tampon Use Buffering Utiliser le tampon Attempt download on network streams Essayer de télécharger en flux de réseau Buffer size (-1 default value) Taille du tampon (valeur par défaut -1) Choose file Choisir un fichier All files Tous les fichiers UserElementSettings User defined elements Éléments spécifiés par l'utilisateur Only for advanced user! Seulement pour utilisateurs avancés ! VolumeSettings Volume Volume Normalized volume Volume normalisé Reset Réinitialiser linux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_it.qm000066400000000000000000000127411323636106000260720ustar00rootroot00000000000000s4C إ!C"/''L`ej5ka {ʉ9 ?:E a4 eK d t 7   1 P Ku  UCj  3  3 e^ Y uI#]R 'fxf lQ{9P܋&cS TZehN(i Dispositivo ALSA ALSA deviceAlsaSinkSettingsDispositivi ALSA, come definiti nel file di configurazione asound8ALSA devices, as defined in an asound configuration fileAlsaSinkSettingsCompressore CompressorAudioDynamicSettings*Compressore/EspansoreCompressor/ExpanderAudioDynamicSettingsForma curva Curve ShapeAudioDynamicSettingsEspansoreExpanderAudioDynamicSettingsDura Hard KneeAudioDynamicSettingsRapportoRatioAudioDynamicSettingsMorbida Soft KneeAudioDynamicSettingsSoglia (dB)Threshold (dB)AudioDynamicSettingsTipoTypeAudioDynamicSettingsBilanciamento Audio PanAudioPanSettings CentroCenterAudioPanSettingsSinistraLeftAudioPanSettings DestraRightAudioPanSettings4Impostazioni indicatore dBDbMeter settingsDbMeterSettings6Decadimento picchi (dB/sec)Peak falloff (dB/sec)DbMeterSettings$Durata picchi (ms) Peak ttl (ms)DbMeterSettings>Intervallo tra due livelli (ms)Time between levels (ms)DbMeterSettings8Equalizzatore 10 bande (IIR)10 Bands Equalizer (IIR)Equalizer10Settings"Modifica PipelineChange PipelineGstMediaSettings"Modifica Pipeline Edit PipelineGstPipelineEditPipelinePipeline GstSettingsConnettiConnectJackSinkSettingsConnessioni ConnectionsJackSinkSettingsDisconnetti DisconnectJackSinkSettings(Modifica connessioniEdit connectionsJackSinkSettingsIngressi Input portsJackSinkSettings Uscite Output portsJackSinkSettings,Equalizzatore 10 bande10 Bands EqualizerMediaElementNameUscita ALSAALSA OutMediaElementNameAudio Pan Audio PanMediaElementName*Compressore/EspansoreCompressor/ExpanderMediaElementName.Elemento PersonalizzatoCustom ElementMediaElementNameUscita JACKJACK OutMediaElementNameTonalitPitchMediaElementNameIngresso Preset Preset InputMediaElementName"Uscita PulseAudioPulseAudio OutMediaElementNameVelocitSpeedMediaElementName&Ingresso di Sistema System InputMediaElementName"Uscita di Sistema System OutMediaElementNameIngresso URI URI InputMediaElementName VolumeVolumeMediaElementNameIndicatore dBdB MeterMediaElementNameTonalitPitch PitchSettings{0:+} semitoni{0:+} semitones PitchSettings PresetPresetsPresetSrcSettings,Impostazioni GStreamerGStreamer settingsSettingsPageName$Impostazioni MediaMedia SettingsSettingsPageNameVelocitSpeed SpeedSettingsTutti i file All filesUriInputSettingsHProva a scaricare con flussi di rete#Attempt download on network streamsUriInputSettingsRDimensione buffer (-1 valore predefinito)Buffer size (-1 default value)UriInputSettingsBuffering BufferingUriInputSettingsSeleziona file Choose fileUriInputSettingsSeleziona file Find FileUriInputSettingsSorgenteSourceUriInputSettingsUsare buffering Use BufferingUriInputSettings2Solo per utenti avanzati!Only for advanced user!UserElementSettings:Elementi definiti dall'utenteUser defined elementsUserElementSettings&Volume normalizzatoNormalized volumeVolumeSettings AzzeraResetVolumeSettings VolumeVolumeVolumeSettingslinux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_it.ts000066400000000000000000000305721323636106000261050ustar00rootroot00000000000000 AlsaSinkSettings ALSA device Dispositivo ALSA ALSA devices, as defined in an asound configuration file Dispositivi ALSA, come definiti nel file di configurazione asound AudioDynamicSettings Compressor Compressore Expander Espansore Soft Knee Morbida Hard Knee Dura Compressor/Expander Compressore/Espansore Type Tipo Curve Shape Forma curva Ratio Rapporto Threshold (dB) Soglia (dB) AudioPanSettings Audio Pan Bilanciamento Center Centro Left Sinistra Right Destra DbMeterSettings DbMeter settings Impostazioni indicatore dB Time between levels (ms) Intervallo tra due livelli (ms) Peak ttl (ms) Durata picchi (ms) Peak falloff (dB/sec) Decadimento picchi (dB/sec) Equalizer10Settings 10 Bands Equalizer (IIR) Equalizzatore 10 bande (IIR) GstMediaSettings Change Pipeline Modifica Pipeline GstPipelineEdit Edit Pipeline Modifica Pipeline GstSettings Pipeline Pipeline JackSinkSettings Connections Connessioni Edit connections Modifica connessioni Output ports Uscite Input ports Ingressi Connect Connetti Disconnect Disconnetti MediaElementName Compressor/Expander Compressore/Espansore Audio Pan Audio Pan PulseAudio Out Uscita PulseAudio Volume Volume dB Meter Indicatore dB System Input Ingresso di Sistema ALSA Out Uscita ALSA JACK Out Uscita JACK Custom Element Elemento Personalizzato System Out Uscita di Sistema Pitch Tonalità URI Input Ingresso URI 10 Bands Equalizer Equalizzatore 10 bande Speed Velocità Preset Input Ingresso Preset PitchSettings Pitch Tonalità {0:+} semitones {0:+} semitoni PresetSrcSettings Presets Preset SettingsPageName GStreamer settings Impostazioni GStreamer Media Settings Impostazioni Media SpeedSettings Speed Velocità UriInputSettings Source Sorgente Find File Seleziona file Buffering Buffering Use Buffering Usare buffering Attempt download on network streams Prova a scaricare con flussi di rete Buffer size (-1 default value) Dimensione buffer (-1 valore predefinito) Choose file Seleziona file All files Tutti i file UserElementSettings User defined elements Elementi definiti dall'utente Only for advanced user! Solo per utenti avanzati! VolumeSettings Volume Volume Normalized volume Volume normalizzato Reset Azzera linux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_sl_SI.qm000066400000000000000000000126661323636106000264750ustar00rootroot00000000000000sb4CH jإ!Cm"! 'Lej5ma Gʉ ?:E a4 eK\ d B t   1 P Ku  UCV  ` 3= e^  uI=]R fff l{9H܋&cS xTZehN"iALSA naprava ALSA deviceAlsaSinkSettings|ALSA naprava, kot definirana v asound konfiguracijski datoteki8ALSA devices, as defined in an asound configuration fileAlsaSinkSettingsKompresor CompressorAudioDynamicSettings.Kompresor/RazairjevalecCompressor/ExpanderAudioDynamicSettingsOblika krivulje Curve ShapeAudioDynamicSettingsRazairjevalecExpanderAudioDynamicSettingsTrdo koleno Hard KneeAudioDynamicSettingsRazmerjeRatioAudioDynamicSettingsMehko koleno Soft KneeAudioDynamicSettingsPrag (dB)Threshold (dB)AudioDynamicSettingsTipTypeAudioDynamicSettingsAvdio nagib Audio PanAudioPanSettingsSredinaCenterAudioPanSettingsLevoLeftAudioPanSettings DesnoRightAudioPanSettings&Nastavitve Db MetraDbMeter settingsDbMeterSettings(Vrhovni padec (dB/s)Peak falloff (dB/sec)DbMeterSettings Vrhovni TTL (ms) Peak ttl (ms)DbMeterSettings& as med nivoji (ms)Time between levels (ms)DbMeterSettings>10 kanalni Izena evalnik (IIR) 10 Bands Equalizer (IIR)Equalizer10Settings Spremeni cevovodChange PipelineGstMediaSettingsUredi cevovod Edit PipelineGstPipelineEditCevovodPipeline GstSettings Pove~iConnectJackSinkSettingsPovezave ConnectionsJackSinkSettingsPrekini DisconnectJackSinkSettingsUredi povezaveEdit connectionsJackSinkSettingsVhodna vrata Input portsJackSinkSettingsIzhodna vrata Output portsJackSinkSettings010 kanalni Izena evalnik10 Bands EqualizerMediaElementNameALSA izhodALSA OutMediaElementNameAvdio nagib Audio PanMediaElementName.Kompresor/RazairjevalecCompressor/ExpanderMediaElementNameElement po meriCustom ElementMediaElementNameJACK izhodJACK OutMediaElementName ViainaPitchMediaElementName*Pred-nastavljeni vhod Preset InputMediaElementName PulseAudio izhodPulseAudio OutMediaElementNameHitrostSpeedMediaElementNameSistemski vhod System InputMediaElementNameSistemski izhod System OutMediaElementNameURI vhod URI InputMediaElementNameGlasnostVolumeMediaElementNamedB meterdB MeterMediaElementName ViainaPitch PitchSettings{0:+} poltoni{0:+} semitones PitchSettingsPred-nastavitvePresetsPresetSrcSettings(GStreamer nastavitveGStreamer settingsSettingsPageName"Nastavitve medijaMedia SettingsSettingsPageNameHitrostSpeed SpeedSettingsVse datoteke All filesUriInputSettings:Poizkusi prenesti omre~ni tok#Attempt download on network streamsUriInputSettings^Velikost pred-pomnilnika (-1 privzeta vrednost)Buffer size (-1 default value)UriInputSettingsPred-pomnjenje BufferingUriInputSettingsIzberi datoteko Choose fileUriInputSettingsNajdi datoteko Find FileUriInputSettings IzvorSourceUriInputSettings,Uporabi pred-pomnjenje Use BufferingUriInputSettings4Le za napredne uporabnike!Only for advanced user!UserElementSettings>Uporabniako definirani elementiUser defined elementsUserElementSettings,Normalizirana glasnostNormalized volumeVolumeSettingsPonastaviResetVolumeSettingsGlasnostVolumeVolumeSettings !!$linux-show-player-0.5.1/lisp/modules/gst_backend/i18n/gst_backend_sl_SI.ts000066400000000000000000000305461323636106000265030ustar00rootroot00000000000000 AlsaSinkSettings ALSA device ALSA naprava ALSA devices, as defined in an asound configuration file ALSA naprava, kot definirana v asound konfiguracijski datoteki AudioDynamicSettings Compressor Kompresor Expander Razširjevalec Soft Knee Mehko koleno Hard Knee Trdo koleno Compressor/Expander Kompresor/Razširjevalec Type Tip Curve Shape Oblika krivulje Ratio Razmerje Threshold (dB) Prag (dB) AudioPanSettings Audio Pan Avdio nagib Center Sredina Left Levo Right Desno DbMeterSettings DbMeter settings Nastavitve Db Metra Time between levels (ms) Čas med nivoji (ms) Peak ttl (ms) Vrhovni TTL (ms) Peak falloff (dB/sec) Vrhovni padec (dB/s) Equalizer10Settings 10 Bands Equalizer (IIR) 10 kanalni Izenačevalnik (IIR) GstMediaSettings Change Pipeline Spremeni cevovod GstPipelineEdit Edit Pipeline Uredi cevovod GstSettings Pipeline Cevovod JackSinkSettings Connections Povezave Edit connections Uredi povezave Output ports Izhodna vrata Input ports Vhodna vrata Connect Poveži Disconnect Prekini MediaElementName Compressor/Expander Kompresor/Razširjevalec Audio Pan Avdio nagib PulseAudio Out PulseAudio izhod Volume Glasnost dB Meter dB meter System Input Sistemski vhod ALSA Out ALSA izhod JACK Out JACK izhod Custom Element Element po meri System Out Sistemski izhod Pitch Višina URI Input URI vhod 10 Bands Equalizer 10 kanalni Izenačevalnik Speed Hitrost Preset Input Pred-nastavljeni vhod PitchSettings Pitch Višina {0:+} semitones {0:+} poltoni PresetSrcSettings Presets Pred-nastavitve SettingsPageName GStreamer settings GStreamer nastavitve Media Settings Nastavitve medija SpeedSettings Speed Hitrost UriInputSettings Source Izvor Find File Najdi datoteko Buffering Pred-pomnjenje Use Buffering Uporabi pred-pomnjenje Attempt download on network streams Poizkusi prenesti omrežni tok Buffer size (-1 default value) Velikost pred-pomnilnika (-1 privzeta vrednost) Choose file Izberi datoteko All files Vse datoteke UserElementSettings User defined elements Uporabniško definirani elementi Only for advanced user! Le za napredne uporabnike! VolumeSettings Volume Glasnost Normalized volume Normalizirana glasnost Reset Ponastavi linux-show-player-0.5.1/lisp/modules/gst_backend/settings/000077500000000000000000000000001323636106000236475ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/gst_backend/settings/__init__.py000066400000000000000000000023031323636106000257560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from os.path import dirname from lisp.core.loading import load_classes # Use a set() for avoiding duplication __PAGES = set() def load(): for _, page in load_classes(__package__, dirname(__file__), suf=('Settings', )): __PAGES.add(page) def pages(): return list(__PAGES) def pages_by_element(): return {s.ELEMENT.__name__: s for s in __PAGES} linux-show-player-0.5.1/lisp/modules/gst_backend/settings/alsa_sink.py000066400000000000000000000063001323636106000261640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QComboBox, QLabel, \ QVBoxLayout from lisp.modules.gst_backend.elements.alsa_sink import AlsaSink from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class AlsaSinkSettings(SettingsPage): ELEMENT = AlsaSink Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.devices = self._discover_pcm_devices() self.devices['default'] = 'default' self.deviceGroup = QGroupBox(self) self.deviceGroup.setTitle(translate('AlsaSinkSettings', 'ALSA device')) self.deviceGroup.setGeometry(0, 0, self.width(), 100) self.deviceGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.deviceGroup) self.device = QComboBox(self.deviceGroup) self.device.addItems(self.devices.keys()) self.device.setCurrentText('default') self.device.setToolTip( translate('AlsaSinkSettings', 'ALSA devices, as defined in an ' 'asound configuration file')) self.deviceGroup.layout().addWidget(self.device) self.label = QLabel(translate('AlsaSinkSettings', 'ALSA device'), self.deviceGroup) self.label.setAlignment(QtCore.Qt.AlignCenter) self.deviceGroup.layout().addWidget(self.label) def enable_check(self, enable): self.deviceGroup.setCheckable(enable) self.deviceGroup.setChecked(False) def load_settings(self, settings): device = settings.get('device', 'default') for name, dev_name in self.devices.items(): if device == dev_name: self.device.setCurrentText(name) break def get_settings(self): if not ( self.deviceGroup.isCheckable() and not self.deviceGroup.isChecked()): return {'device': self.devices[self.device.currentText()]} return {} def _discover_pcm_devices(self): devices = {} with open('/proc/asound/pcm', mode='r') as f: for dev in f.readlines(): dev_name = dev[7:dev.find(':', 7)].strip() dev_code = 'hw:' + dev[:5].replace('-', ',') devices[dev_name] = dev_code return devices linux-show-player-0.5.1/lisp/modules/gst_backend/settings/audio_dynamic.py000066400000000000000000000120531323636106000270270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import math from PyQt5 import QtCore from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGroupBox, QGridLayout, QComboBox, QDoubleSpinBox, \ QLabel, QVBoxLayout from lisp.modules.gst_backend.elements.audio_dynamic import AudioDynamic from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate MIN_dB = 0.000000312 # -100dB class AudioDynamicSettings(SettingsPage): ELEMENT = AudioDynamic Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.groupBox = QGroupBox(self) self.groupBox.setGeometry(0, 0, self.width(), 240) self.groupBox.setLayout(QGridLayout()) self.layout().addWidget(self.groupBox) # AudioDynamic mode self.modeComboBox = QComboBox(self.groupBox) self.modeComboBox.addItem( translate('AudioDynamicSettings', 'Compressor'), 'compressor') self.modeComboBox.addItem( translate('AudioDynamicSettings', 'Expander'), 'expander') self.groupBox.layout().addWidget(self.modeComboBox, 0, 0, 1, 1) self.modeLabel = QLabel(self.groupBox) self.modeLabel.setAlignment(QtCore.Qt.AlignCenter) self.groupBox.layout().addWidget(self.modeLabel, 0, 1, 1, 1) # AudioDynamic characteristic self.chComboBox = QComboBox(self.groupBox) self.chComboBox.addItem( translate('AudioDynamicSettings', 'Soft Knee'), 'soft-knee') self.chComboBox.addItem( translate('AudioDynamicSettings', 'Hard Knee'), 'hard-knee') self.groupBox.layout().addWidget(self.chComboBox, 1, 0, 1, 1) self.chLabel = QLabel(self.groupBox) self.chLabel.setAlignment(QtCore.Qt.AlignCenter) self.groupBox.layout().addWidget(self.chLabel, 1, 1, 1, 1) # AudioDynamic ratio self.ratioSpin = QDoubleSpinBox(self.groupBox) self.groupBox.layout().addWidget(self.ratioSpin, 2, 0, 1, 1) self.ratioLabel = QLabel(self.groupBox) self.ratioLabel.setAlignment(QtCore.Qt.AlignCenter) self.groupBox.layout().addWidget(self.ratioLabel, 2, 1, 1, 1) # AudioDynamic threshold self.thresholdSpin = QDoubleSpinBox(self.groupBox) self.thresholdSpin.setMaximum(0) self.thresholdSpin.setMinimum(-100) self.thresholdSpin.setSingleStep(1) self.groupBox.layout().addWidget(self.thresholdSpin, 3, 0, 1, 1) self.thresholdLabel = QLabel(self.groupBox) self.thresholdLabel.setAlignment(QtCore.Qt.AlignCenter) self.groupBox.layout().addWidget(self.thresholdLabel, 3, 1, 1, 1) self.retranslateUi() def retranslateUi(self): self.groupBox.setTitle( translate('AudioDynamicSettings', 'Compressor/Expander')) self.modeLabel.setText(translate('AudioDynamicSettings', 'Type')) self.chLabel.setText(translate('AudioDynamicSettings', 'Curve Shape')) self.ratioLabel.setText(translate('AudioDynamicSettings', 'Ratio')) self.thresholdLabel.setText( translate('AudioDynamicSettings', 'Threshold (dB)')) def enable_check(self, enable): self.groupBox.setCheckable(enable) self.groupBox.setChecked(False) def get_settings(self): settings = {} if not (self.groupBox.isCheckable() and not self.groupBox.isChecked()): settings['ratio'] = self.ratioSpin.value() settings['threshold'] = math.pow(10, self.thresholdSpin.value() / 20) settings['mode'] = self.modeComboBox.currentData() settings['characteristics'] = self.chComboBox.currentData() return settings def load_settings(self, settings): self.modeComboBox.setCurrentText( translate('AudioDynamicSettings', settings.get('mode', 'compressor'))) self.chComboBox.setCurrentText( translate('AudioDynamicSettings', settings.get('characteristics', 'soft-knee'))) if settings.get('threshold', 0) == 0: settings['threshold'] = MIN_dB self.thresholdSpin.setValue(20 * math.log10(settings['threshold'])) self.ratioSpin.setValue(settings.get('ratio', 1)) linux-show-player-0.5.1/lisp/modules/gst_backend/settings/audio_pan.py000066400000000000000000000056471323636106000261740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QSlider, QLabel, QVBoxLayout from lisp.modules.gst_backend.elements.audio_pan import AudioPan from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class AudioPanSettings(SettingsPage): ELEMENT = AudioPan Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.panBox = QGroupBox(self) self.panBox.setGeometry(0, 0, self.width(), 80) self.panBox.setLayout(QHBoxLayout(self.panBox)) self.layout().addWidget(self.panBox) self.panSlider = QSlider(self.panBox) self.panSlider.setRange(-10, 10) self.panSlider.setPageStep(1) self.panSlider.setOrientation(Qt.Horizontal) self.panSlider.valueChanged.connect(self.pan_changed) self.panBox.layout().addWidget(self.panSlider) self.panLabel = QLabel(self.panBox) self.panLabel.setAlignment(Qt.AlignCenter) self.panBox.layout().addWidget(self.panLabel) self.panBox.layout().setStretch(0, 5) self.panBox.layout().setStretch(1, 1) self.retransaleUi() def retransaleUi(self): self.panBox.setTitle(translate('AudioPanSettings', 'Audio Pan')) self.panLabel.setText(translate('AudioPanSettings', 'Center')) def enable_check(self, enable): self.panBox.setCheckable(enable) self.panBox.setChecked(False) def get_settings(self): if not (self.panBox.isCheckable() and not self.panBox.isChecked()): return {'pan': self.panSlider.value() / 10} return {} def load_settings(self, settings): self.panSlider.setValue(settings.get('pan', 0.5) * 10) def pan_changed(self, value): if value < 0: position = translate('AudioPanSettings', 'Left') elif value > 0: position = translate('AudioPanSettings', 'Right') else: position = translate('AudioPanSettings', 'Center') self.panLabel.setText('{0} - {1}'.format(value, position)) linux-show-player-0.5.1/lisp/modules/gst_backend/settings/db_meter.py000066400000000000000000000075371323636106000260160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGroupBox, QGridLayout, QSpinBox, QLabel, \ QVBoxLayout from lisp.modules.gst_backend.elements.db_meter import DbMeter from lisp.modules.gst_backend.gi_repository import Gst from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class DbMeterSettings(SettingsPage): ELEMENT = DbMeter Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.groupBox = QGroupBox(self) self.groupBox.setGeometry(0, 0, self.width(), 180) self.groupBox.setLayout(QGridLayout()) self.layout().addWidget(self.groupBox) # Time (sec/100) between two levels self.intervalSpin = QSpinBox(self.groupBox) self.intervalSpin.setRange(1, 1000) self.intervalSpin.setMaximum(1000) self.intervalSpin.setValue(50) self.groupBox.layout().addWidget(self.intervalSpin, 0, 0) self.intervalLabel = QLabel(self.groupBox) self.intervalLabel.setAlignment(QtCore.Qt.AlignCenter) self.groupBox.layout().addWidget(self.intervalLabel, 0, 1) # Peak ttl (sec/100) self.ttlSpin = QSpinBox(self.groupBox) self.ttlSpin.setSingleStep(10) self.ttlSpin.setRange(10, 10000) self.ttlSpin.setValue(500) self.groupBox.layout().addWidget(self.ttlSpin, 1, 0) self.ttlLabel = QLabel(self.groupBox) self.ttlLabel.setAlignment(QtCore.Qt.AlignCenter) self.groupBox.layout().addWidget(self.ttlLabel, 1, 1) # Peak falloff (unit per time) self.falloffSpin = QSpinBox(self.groupBox) self.falloffSpin.setRange(1, 100) self.falloffSpin.setValue(20) self.groupBox.layout().addWidget(self.falloffSpin, 2, 0) self.falloffLabel = QLabel(self.groupBox) self.falloffLabel.setAlignment(QtCore.Qt.AlignCenter) self.groupBox.layout().addWidget(self.falloffLabel, 2, 1) self.retranslateUi() def retranslateUi(self): self.groupBox.setTitle(translate('DbMeterSettings', 'DbMeter settings')) self.intervalLabel.setText( translate('DbMeterSettings', 'Time between levels (ms)')) self.ttlLabel.setText(translate('DbMeterSettings', 'Peak ttl (ms)')) self.falloffLabel.setText( translate('DbMeterSettings', 'Peak falloff (dB/sec)')) def load_settings(self, settings): self.intervalSpin.setValue(settings.get('interval', 50) / Gst.MSECOND) self.ttlSpin.setValue(settings.get('peak_ttl', 500) / Gst.MSECOND) self.falloffSpin.setValue(settings.get('peak_falloff', 20)) def get_settings(self): settings = {} if not (self.groupBox.isCheckable() and not self.groupBox.isChecked()): settings['interval'] = self.intervalSpin.value() * Gst.MSECOND settings['peak_ttl'] = self.ttlSpin.value() * Gst.MSECOND settings['peak_falloff'] = self.falloffSpin.value() return settings linux-show-player-0.5.1/lisp/modules/gst_backend/settings/equalizer10.py000066400000000000000000000064171323636106000263730ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtCore import Qt from PyQt5.QtGui import QFontMetrics from PyQt5.QtWidgets import QGroupBox, QGridLayout, QLabel, QSlider, QVBoxLayout from lisp.modules.gst_backend.elements.equalizer10 import Equalizer10 from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class Equalizer10Settings(SettingsPage): ELEMENT = Equalizer10 Name = ELEMENT.Name FREQ = ['30Hz', '60Hz', '120Hz', '240Hz', '475Hz', '950Hz', '1900Hz', '3800Hz', '7525Hz', '15KHz'] def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.groupBox = QGroupBox(self) self.groupBox.resize(self.size()) self.groupBox.setTitle( translate('Equalizer10Settings', '10 Bands Equalizer (IIR)')) self.groupBox.setLayout(QGridLayout()) self.groupBox.layout().setVerticalSpacing(0) self.layout().addWidget(self.groupBox) self.sliders = {} for n in range(10): label = QLabel(self.groupBox) label.setMinimumWidth(QFontMetrics(label.font()).width('000')) label.setAlignment(QtCore.Qt.AlignCenter) label.setNum(0) self.groupBox.layout().addWidget(label, 0, n) slider = QSlider(self.groupBox) slider.setRange(-24, 12) slider.setPageStep(1) slider.setValue(0) slider.setOrientation(QtCore.Qt.Vertical) slider.valueChanged.connect(label.setNum) self.groupBox.layout().addWidget(slider, 1, n) self.groupBox.layout().setAlignment(slider, QtCore.Qt.AlignHCenter) self.sliders['band' + str(n)] = slider fLabel = QLabel(self.groupBox) fLabel.setStyleSheet('font-size: 8pt;') fLabel.setAlignment(QtCore.Qt.AlignCenter) fLabel.setText(self.FREQ[n]) self.groupBox.layout().addWidget(fLabel, 2, n) def enable_check(self, enable): self.groupBox.setCheckable(enable) self.groupBox.setChecked(False) def get_settings(self): settings = {} if not (self.groupBox.isCheckable() and not self.groupBox.isChecked()): for band in self.sliders: settings[band] = self.sliders[band].value() return settings def load_settings(self, settings): for band in self.sliders: self.sliders[band].setValue(settings.get(band, 0)) linux-show-player-0.5.1/lisp/modules/gst_backend/settings/jack_sink.py000066400000000000000000000257231323636106000261660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import jack from PyQt5.QtCore import Qt from PyQt5.QtGui import QPainter, QPolygon, QPainterPath from PyQt5.QtWidgets import QGroupBox, QWidget, \ QHBoxLayout, QTreeWidget, QTreeWidgetItem, QGridLayout, QDialog, \ QDialogButtonBox, QPushButton, QVBoxLayout from lisp.modules.gst_backend.elements.jack_sink import JackSink from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class JackSinkSettings(SettingsPage): ELEMENT = JackSink Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.jackGroup = QGroupBox(self) self.jackGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.jackGroup) self.connectionsEdit = QPushButton(self.jackGroup) self.connectionsEdit.clicked.connect(self.__edit_connections) self.jackGroup.layout().addWidget(self.connectionsEdit) self.__jack_client = jack.Client('LiSP_SettingsControl') self.connections = JackSink.default_connections(self.__jack_client) self.retranlsateUi() def retranlsateUi(self): self.jackGroup.setTitle(translate('JackSinkSettings', 'Connections')) self.connectionsEdit.setText( translate('JackSinkSettings', 'Edit connections')) def closeEvent(self, event): print('COLSE') self.__jack_client.deactivate() self.__jack_client.close() super().closeEvent(event) def get_settings(self): settings = {} if not (self.jackGroup.isCheckable() and not self.jackGroup.isChecked()): settings['connections'] = self.connections return settings def load_settings(self, settings): self.connections = settings.get('connections', self.connections).copy() def enable_check(self, enable): self.jackGroup.setCheckable(enable) self.jackGroup.setChecked(False) def __edit_connections(self): dialog = JackConnectionsDialog(self.__jack_client, parent=self) dialog.set_connections(self.connections.copy()) dialog.exec_() if dialog.result() == dialog.Accepted: self.connections = dialog.connections class ClientItem(QTreeWidgetItem): def __init__(self, client_name): super().__init__([client_name]) self.name = client_name self.ports = {} def add_port(self, port_name): port = PortItem(port_name) self.addChild(port) self.ports[port_name] = port class PortItem(QTreeWidgetItem): def __init__(self, port_name): super().__init__([port_name[:port_name.index(':')]]) self.name = port_name class ConnectionsWidget(QWidget): """Code ported from QJackCtl (http://qjackctl.sourceforge.net)""" def __init__(self, output_widget, input_widget, parent=None, **kwargs): super().__init__(parent) self._output_widget = output_widget self._input_widget = input_widget self.connections = [] def paintEvent(self, QPaintEvent): yc = self.y() yo = self._output_widget.y() yi = self._input_widget.y() x1 = 0 x2 = self.width() h1 = self._output_widget.header().sizeHint().height() h2 = self._input_widget.header().sizeHint().height() painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) for output, out_conn in enumerate(self.connections): y1 = int(self.item_y(self._output_widget.topLevelItem(output)) + ( yo - yc)) for client in range(self._input_widget.topLevelItemCount()): client = self._input_widget.topLevelItem(client) for port in client.ports: if port in self.connections[output]: y2 = int(self.item_y(client.ports[port]) + (yi - yc)) self.draw_connection_line(painter, x1, y1, x2, y2, h1, h2) painter.end() @staticmethod def draw_connection_line(painter, x1, y1, x2, y2, h1, h2): # Account for list view headers. y1 += h1 y2 += h2 # Invisible output ports don't get a connecting dot. if y1 > h1: painter.drawLine(x1, y1, x1 + 4, y1) # Setup control points spline = QPolygon(4) cp = int((x2 - x1 - 8) * 0.4) spline.setPoints(x1 + 4, y1, x1 + 4 + cp, y1, x2 - 4 - cp, y2, x2 - 4, y2) # The connection line path = QPainterPath() path.moveTo(spline.at(0)) path.cubicTo(spline.at(1), spline.at(2), spline.at(3)) painter.strokePath(path, painter.pen()) # painter.drawLine(x1 + 4, y1, x2 - 4, y2) # Invisible input ports don't get a connecting dot. if y2 > h2: painter.drawLine(x2 - 4, y2, x2, y2) @staticmethod def item_y(item): tree_widget = item.treeWidget() parent = item.parent() if parent is not None and not parent.isExpanded(): rect = tree_widget.visualItemRect(parent) else: rect = tree_widget.visualItemRect(item) return rect.top() + rect.height() / 2 class JackConnectionsDialog(QDialog): def __init__(self, jack_client, parent=None, **kwargs): super().__init__(parent) self.resize(600, 400) self.setLayout(QGridLayout()) self.output_widget = QTreeWidget(self) self.input_widget = QTreeWidget(self) self.connections_widget = ConnectionsWidget(self.output_widget, self.input_widget, parent=self) self.output_widget.itemExpanded.connect(self.connections_widget.update) self.output_widget.itemCollapsed.connect(self.connections_widget.update) self.input_widget.itemExpanded.connect(self.connections_widget.update) self.input_widget.itemCollapsed.connect(self.connections_widget.update) self.input_widget.itemSelectionChanged.connect( self.__input_selection_changed) self.output_widget.itemSelectionChanged.connect( self.__output_selection_changed) self.layout().addWidget(self.output_widget, 0, 0) self.layout().addWidget(self.connections_widget, 0, 1) self.layout().addWidget(self.input_widget, 0, 2) self.layout().setColumnStretch(0, 2) self.layout().setColumnStretch(1, 1) self.layout().setColumnStretch(2, 2) self.connectButton = QPushButton(self) self.connectButton.clicked.connect(self.__disconnect_selected) self.connectButton.setEnabled(False) self.layout().addWidget(self.connectButton, 1, 1) self.dialogButtons = QDialogButtonBox( QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.dialogButtons.accepted.connect(self.accept) self.dialogButtons.rejected.connect(self.reject) self.layout().addWidget(self.dialogButtons, 2, 0, 1, 3) self.retranslateUi() self.__jack_client = jack_client self.__selected_in = None self.__selected_out = None self.connections = [] self.update_graph() def retranslateUi(self): self.output_widget.setHeaderLabels( [translate('JackSinkSettings', 'Output ports')]) self.input_widget.setHeaderLabels( [translate('JackSinkSettings', 'Input ports')]) self.connectButton.setText(translate('JackSinkSettings', 'Connect')) def set_connections(self, connections): self.connections = connections self.connections_widget.connections = self.connections self.connections_widget.update() def update_graph(self): input_ports = self.__jack_client.get_ports(is_audio=True, is_input=True) self.output_widget.clear() for port in range(8): self.output_widget.addTopLevelItem( QTreeWidgetItem(['output_' + str(port)])) self.input_widget.clear() clients = {} for port in input_ports: client_name = port.name[:port.name.index(':')] if client_name not in clients: clients[client_name] = ClientItem(client_name) self.input_widget.addTopLevelItem(clients[client_name]) clients[client_name].add_port(port.name) def __input_selection_changed(self): if self.input_widget.selectedItems(): self.__selected_in = self.input_widget.selectedItems()[0] else: self.__selected_in = None self.__check_selection() def __output_selection_changed(self): if self.output_widget.selectedItems(): self.__selected_out = self.output_widget.selectedItems()[0] else: self.__selected_out = None self.__check_selection() def __check_selection(self): if self.__selected_in is not None and self.__selected_out is not None: output = self.output_widget.indexOfTopLevelItem(self.__selected_out) self.connectButton.clicked.disconnect() self.connectButton.setEnabled(True) if self.__selected_in.name in self.connections[output]: self.connectButton.setText( translate('JackSinkSettings', 'Disconnect')) self.connectButton.clicked.connect(self.__disconnect_selected) else: self.connectButton.setText( translate('JackSinkSettings', 'Connect')) self.connectButton.clicked.connect(self.__connect_selected) else: self.connectButton.setEnabled(False) def __connect_selected(self): output = self.output_widget.indexOfTopLevelItem(self.__selected_out) self.connections[output].append(self.__selected_in.name) self.connections_widget.update() self.__check_selection() def __disconnect_selected(self): output = self.output_widget.indexOfTopLevelItem(self.__selected_out) self.connections[output].remove(self.__selected_in.name) self.connections_widget.update() self.__check_selection() linux-show-player-0.5.1/lisp/modules/gst_backend/settings/pitch.py000066400000000000000000000056471323636106000253440ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import math from PyQt5 import QtCore from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QSlider, QLabel, QVBoxLayout from lisp.modules.gst_backend.elements.pitch import Pitch from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class PitchSettings(SettingsPage): ELEMENT = Pitch Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.groupBox = QGroupBox(self) self.groupBox.setLayout(QHBoxLayout()) self.layout().addWidget(self.groupBox) self.pitchSlider = QSlider(self.groupBox) self.pitchSlider.setMinimum(-12) self.pitchSlider.setMaximum(12) self.pitchSlider.setPageStep(1) self.pitchSlider.setValue(0) self.pitchSlider.setOrientation(QtCore.Qt.Horizontal) self.pitchSlider.setTickPosition(QSlider.TicksAbove) self.pitchSlider.setTickInterval(1) self.groupBox.layout().addWidget(self.pitchSlider) self.pitchLabel = QLabel(self.groupBox) self.pitchLabel.setAlignment(QtCore.Qt.AlignCenter) self.groupBox.layout().addWidget(self.pitchLabel) self.groupBox.layout().setStretch(0, 3) self.groupBox.layout().setStretch(1, 1) self.pitchSlider.valueChanged.connect(self.pitch_changed) self.retranslateUi() def retranslateUi(self): self.groupBox.setTitle(translate('PitchSettings', 'Pitch')) self.pitch_changed(0) def enable_check(self, enable): self.groupBox.setCheckable(enable) self.groupBox.setChecked(False) def get_settings(self): if not (self.groupBox.isCheckable() and not self.groupBox.isChecked()): return {'pitch': math.pow(2, self.pitchSlider.value() / 12)} return {} def load_settings(self, settings): self.pitchSlider.setValue( round(12 * math.log(settings.get('pitch', 1), 2))) def pitch_changed(self, value): self.pitchLabel.setText( translate('PitchSettings', '{0:+} semitones').format(value)) linux-show-player-0.5.1/lisp/modules/gst_backend/settings/preset_src.py000066400000000000000000000051001323636106000263660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QTime from PyQt5.QtWidgets import QGroupBox, QComboBox, QVBoxLayout, QTimeEdit from lisp.modules.gst_backend.elements.preset_src import PresetSrc from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class PresetSrcSettings(SettingsPage): ELEMENT = PresetSrc Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.functionGroup = QGroupBox(self) self.functionGroup.setTitle(translate('PresetSrcSettings', 'Presets')) self.functionGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.functionGroup) self.functionCombo = QComboBox(self.functionGroup) self.functionGroup.layout().addWidget(self.functionCombo) for function in sorted(PresetSrc.PRESETS.keys()): self.functionCombo.addItem(function) self.functionDuration = QTimeEdit(self.functionGroup) self.functionDuration.setDisplayFormat('HH.mm.ss.zzz') self.functionGroup.layout().addWidget(self.functionDuration) def enable_check(self, enabled): self.functionGroup.setCheckable(enabled) self.functionGroup.setChecked(False) def get_settings(self): if not (self.functionGroup.isCheckable() and not self.functionGroup.isChecked()): return {'preset': self.functionCombo.currentText(), 'duration': self.functionDuration.time(). msecsSinceStartOfDay()} return {} def load_settings(self, settings): self.functionCombo.setCurrentText(settings.get('preset', '')) self.functionDuration.setTime( QTime.fromMSecsSinceStartOfDay(settings.get('duration', 0))) linux-show-player-0.5.1/lisp/modules/gst_backend/settings/speed.py000066400000000000000000000055021323636106000253230ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QSlider, QLabel, QVBoxLayout from lisp.modules.gst_backend.elements.speed import Speed from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class SpeedSettings(SettingsPage): ELEMENT = Speed Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.groupBox = QGroupBox(self) self.groupBox.setLayout(QHBoxLayout()) self.layout().addWidget(self.groupBox) self.speedSlider = QSlider(self.groupBox) self.speedSlider.setMinimum(1) self.speedSlider.setMaximum(1000) self.speedSlider.setPageStep(1) self.speedSlider.setValue(100) self.speedSlider.setOrientation(QtCore.Qt.Horizontal) self.speedSlider.setTickPosition(QSlider.TicksAbove) self.speedSlider.setTickInterval(10) self.groupBox.layout().addWidget(self.speedSlider) self.speedLabel = QLabel(self.groupBox) self.speedLabel.setAlignment(QtCore.Qt.AlignCenter) self.groupBox.layout().addWidget(self.speedLabel) self.groupBox.layout().setStretch(0, 3) self.groupBox.layout().setStretch(1, 1) self.speedSlider.valueChanged.connect(self.speedChanged) self.retranslateUi() def retranslateUi(self): self.groupBox.setTitle(translate('SpeedSettings', 'Speed')) self.speedLabel.setText('1.0') def enable_check(self, enable): self.groupBox.setCheckable(enable) self.groupBox.setChecked(False) def get_settings(self): if not (self.groupBox.isCheckable() and not self.groupBox.isChecked()): return {'speed': self.speedSlider.value() / 100} return {} def load_settings(self, settings): self.speedSlider.setValue(settings.get('speed', 1) * 100) def speedChanged(self, value): self.speedLabel.setText(str(value / 100.0)) linux-show-player-0.5.1/lisp/modules/gst_backend/settings/uri_input.py000066400000000000000000000107671323636106000262520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QStandardPaths, Qt from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QPushButton, QLineEdit, \ QGridLayout, QCheckBox, QSpinBox, QLabel, QFileDialog, QVBoxLayout from lisp.modules.gst_backend.elements.uri_input import UriInput from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class UriInputSettings(SettingsPage): ELEMENT = UriInput Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.fileGroup = QGroupBox(self) self.fileGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.fileGroup) self.buttonFindFile = QPushButton(self.fileGroup) self.fileGroup.layout().addWidget(self.buttonFindFile) self.filePath = QLineEdit('file://', self.fileGroup) self.fileGroup.layout().addWidget(self.filePath) self.bufferingGroup = QGroupBox(self) self.bufferingGroup.setLayout(QGridLayout()) self.layout().addWidget(self.bufferingGroup) self.useBuffering = QCheckBox(self.bufferingGroup) self.bufferingGroup.layout().addWidget(self.useBuffering, 0, 0, 1, 2) self.download = QCheckBox(self.bufferingGroup) self.bufferingGroup.layout().addWidget(self.download, 1, 0, 1, 2) self.bufferSize = QSpinBox(self.bufferingGroup) self.bufferSize.setRange(-1, 2147483647) self.bufferSize.setValue(-1) self.bufferingGroup.layout().addWidget(self.bufferSize, 2, 0) self.bufferSizeLabel = QLabel(self.bufferingGroup) self.bufferSizeLabel.setAlignment(Qt.AlignCenter) self.bufferingGroup.layout().addWidget(self.bufferSizeLabel, 2, 1) self.buttonFindFile.clicked.connect(self.select_file) self.retranslateUi() def retranslateUi(self): self.fileGroup.setTitle(translate('UriInputSettings', 'Source')) self.buttonFindFile.setText(translate('UriInputSettings', 'Find File')) self.bufferingGroup.setTitle(translate('UriInputSettings', 'Buffering')) self.useBuffering.setText( translate('UriInputSettings', 'Use Buffering')) self.download.setText(translate('UriInputSettings', 'Attempt download on network streams')) self.bufferSizeLabel.setText( translate('UriInputSettings', 'Buffer size (-1 default value)')) def get_settings(self): settings = {} checkable = self.fileGroup.isCheckable() if not (checkable and not self.fileGroup.isChecked()): settings['uri'] = self.filePath.text() if not (checkable and not self.bufferingGroup.isChecked()): settings['use_buffering'] = self.useBuffering.isChecked() settings['download'] = self.download.isChecked() settings['buffer_size'] = self.bufferSize.value() return settings def load_settings(self, settings): self.filePath.setText(settings.get('uri', '')) def enable_check(self, enable): self.fileGroup.setCheckable(enable) self.fileGroup.setChecked(False) def select_file(self): path = QStandardPaths.writableLocation(QStandardPaths.MusicLocation) file, ok = QFileDialog.getOpenFileName(self, translate('UriInputSettings', 'Choose file'), path, translate('UriInputSettings', 'All files') + ' (*)') if ok: self.filePath.setText('file://' + file) linux-show-player-0.5.1/lisp/modules/gst_backend/settings/user_element.py000066400000000000000000000047451323636106000267220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QPlainTextEdit, QLabel from lisp.modules.gst_backend.elements.user_element import UserElement from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class UserElementSettings(SettingsPage): ELEMENT = UserElement Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.groupBox = QGroupBox(self) self.groupBox.setGeometry(self.geometry()) self.groupBox.setLayout(QVBoxLayout()) self.layout().addWidget(self.groupBox) self.textEdit = QPlainTextEdit(self.groupBox) self.groupBox.layout().addWidget(self.textEdit) self.warning = QLabel(self.groupBox) self.warning.setAlignment(QtCore.Qt.AlignCenter) self.warning.setStyleSheet('color: #FF2222; font-weight: bold') self.groupBox.layout().addWidget(self.warning) self.retranslateUi() def retranslateUi(self): self.groupBox.setTitle( translate('UserElementSettings', 'User defined elements')) self.warning.setText( translate('UserElementSettings', 'Only for advanced user!')) def enable_check(self, enable): self.groupBox.setCheckable(enable) self.groupBox.setChecked(False) def load_settings(self, settings): self.textEdit.setPlainText(settings.get('bin', '')) def get_settings(self): if not (self.groupBox.isCheckable() and not self.groupBox.isChecked()): return {'bin': self.textEdit.toPlainText().strip()} return {} linux-show-player-0.5.1/lisp/modules/gst_backend/settings/volume.py000066400000000000000000000105051323636106000255310ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QSlider, QLabel, QCheckBox, \ QVBoxLayout from lisp.backend.audio_utils import db_to_linear, linear_to_db from lisp.modules.gst_backend.elements.volume import Volume from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.widgets import QMuteButton from lisp.ui.ui_utils import translate class VolumeSettings(SettingsPage): ELEMENT = Volume Name = ELEMENT.Name def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.normal = 1 self.volumeBox = QGroupBox(self) self.volumeBox.setLayout(QHBoxLayout()) self.layout().addWidget(self.volumeBox) self.muteButton = QMuteButton(self.volumeBox) self.volumeBox.layout().addWidget(self.muteButton) self.volume = QSlider(self.volumeBox) self.volume.setRange(-1000, 100) self.volume.setPageStep(1) self.volume.setOrientation(Qt.Horizontal) self.volume.valueChanged.connect(self.volume_changed) self.volumeBox.layout().addWidget(self.volume) self.volumeLabel = QLabel(self.volumeBox) self.volumeLabel.setAlignment(Qt.AlignCenter) self.volumeBox.layout().addWidget(self.volumeLabel) self.volumeBox.layout().setStretch(0, 1) self.volumeBox.layout().setStretch(1, 4) self.volumeBox.layout().setStretch(2, 1) self.normalBox = QGroupBox(self) self.normalBox.setLayout(QHBoxLayout()) self.layout().addWidget(self.normalBox) self.normalLabel = QLabel(self.normalBox) self.normalLabel.setAlignment(Qt.AlignCenter) self.normalBox.layout().addWidget(self.normalLabel) self.normalReset = QCheckBox(self.normalBox) self.normalBox.layout().addWidget(self.normalReset) self.normalBox.layout().setAlignment(self.normalReset, Qt.AlignCenter) self.retranslateUi() def retranslateUi(self): self.volumeBox.setTitle(translate('VolumeSettings', 'Volume')) self.volumeLabel.setText('0.0 dB') self.normalBox.setTitle( translate('VolumeSettings', 'Normalized volume')) self.normalLabel.setText('0.0 dB') self.normalReset.setText(translate('VolumeSettings', 'Reset')) def enable_check(self, enable): for box in [self.normalBox, self.volumeBox]: box.setCheckable(enable) box.setChecked(False) def get_settings(self): settings = {} checkable = self.volumeBox.isCheckable() if not (checkable and not self.volumeBox.isChecked()): settings['volume'] = db_to_linear(self.volume.value() / 10) settings['mute'] = self.muteButton.isMute() if not (checkable and not self.normalBox.isChecked()): if self.normalReset.isChecked(): settings['normal_volume'] = 1 # If the apply button is pressed, show the correct value self.normalLabel.setText('0 dB') else: settings['normal_volume'] = self.normal return settings def load_settings(self, settings): self.volume.setValue(linear_to_db(settings.get('volume', 1)) * 10) self.muteButton.setMute(settings.get('mute', False)) self.normal = settings.get('normal_volume', 1) self.normalLabel.setText( str(round(linear_to_db(self.normal), 3)) + ' dB') def volume_changed(self, value): self.volumeLabel.setText(str(value / 10.0) + ' dB') linux-show-player-0.5.1/lisp/modules/media_cue_menus.py000066400000000000000000000055301323636106000232420ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import os from PyQt5.QtCore import QStandardPaths, Qt from PyQt5.QtGui import QCursor from PyQt5.QtWidgets import QFileDialog, QApplication, QMessageBox from lisp.backend import get_backend from lisp.application import Application from lisp.core.module import Module from lisp.cues.cue_factory import CueFactory from lisp.ui.mainwindow import MainWindow from lisp.ui.ui_utils import qfile_filters, translate class MediaCueMenus(Module): """Register menus to add MediaCue to layouts""" def __init__(self): MainWindow().register_cue_menu_action( translate('MediaCueMenus', 'Audio cue (from file)'), self.add_uri_audio_media_cue, category='Media cues', shortcut='CTRL+M') @staticmethod def add_uri_audio_media_cue(): """Add audio MediaCue(s) form user-selected files""" if get_backend() is None: QMessageBox.critical(MainWindow(), 'Error', 'Backend not loaded') return # Default path to system "music" folder path = QStandardPaths.writableLocation(QStandardPaths.MusicLocation) # Get the backend extensions and create a filter for the Qt file-dialog extensions = get_backend().supported_extensions() filters = qfile_filters(extensions, anyfile=False) # Display a file-dialog for the user to choose the media-files files, _ = QFileDialog.getOpenFileNames(MainWindow(), translate('MediaCueMenus', 'Select media files'), path, filters) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) # Create media cues, and add them to the Application cue_model for file in files: cue = CueFactory.create_cue('URIAudioCue', uri='file://' + file) # Use the filename without extension as cue name cue.name = os.path.splitext(os.path.basename(file))[0] Application().cue_model.add(cue) QApplication.restoreOverrideCursor() linux-show-player-0.5.1/lisp/modules/media_info/000077500000000000000000000000001323636106000216355ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/media_info/__init__.py000066400000000000000000000000411323636106000237410ustar00rootroot00000000000000from .media_info import MediaInfolinux-show-player-0.5.1/lisp/modules/media_info/i18n/000077500000000000000000000000001323636106000224145ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/media_info/i18n/media_info_cs.qm000066400000000000000000000006031323636106000255310ustar00rootroot00000000000000 MediaInfo Media Info Údaje o záznamu Error Chyba No info to display Žádné údaje k zobrazení Info Informace Value Hodnota linux-show-player-0.5.1/lisp/modules/media_info/i18n/media_info_en.qm000066400000000000000000000005351323636106000255320ustar00rootroot00000000000000 MediaInfo Media Info Media Info Error Error No info to display No info to display Info Info Value Value linux-show-player-0.5.1/lisp/modules/media_info/i18n/media_info_es.qm000066400000000000000000000006351323636106000255400ustar00rootroot00000000000000 MediaInfo Media Info Información del Media Error Error No info to display Ninguna información para mostrar Info Información Value Valor linux-show-player-0.5.1/lisp/modules/media_info/i18n/media_info_fr.qm000066400000000000000000000005651323636106000255420ustar00rootroot00000000000000 MediaInfo Media Info Info sur le média Error Erreur No info to display Pas d'info à afficher Info Info Value Valeur linux-show-player-0.5.1/lisp/modules/media_info/i18n/media_info_it.qm000066400000000000000000000006351323636106000255450ustar00rootroot00000000000000 MediaInfo Media Info Informazioni Media Error Errore No info to display Nessuna informazione da mostrare Info Informazione Value Valore linux-show-player-0.5.1/lisp/modules/media_info/i18n/media_info_sl_SI.qm000066400000000000000000000006361323636106000261430ustar00rootroot00000000000000 MediaInfo Media Info Podrobnosti o mediju Error Napaka No info to display Brez podrobnosti za prikaz Info Podrobnosti Value Vrednost linux-show-player-0.5.1/lisp/modules/media_info/media_info.py000066400000000000000000000125251323636106000243060ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from urllib.request import unquote from PyQt5 import QtCore from PyQt5.QtWidgets import QAction, QMessageBox, QDialog, QVBoxLayout, \ QTreeWidget, QAbstractItemView, QDialogButtonBox, QTreeWidgetItem from PyQt5.QtWidgets import QHeaderView from lisp.application import Application from lisp.modules.gst_backend.gst_utils import gst_uri_metadata, \ gst_parse_tags_list from lisp.core.module import Module from lisp.cues.media_cue import MediaCue from lisp.layouts.cue_layout import CueLayout from lisp.ui.mainwindow import MainWindow from lisp.ui.ui_utils import translate class MediaInfo(Module): Name = 'MediaInfo' def __init__(self): self.menuAction = QAction(None) self.menuAction.triggered.connect(self.show_info) self.menuAction.setText(translate('MediaInfo', 'Media Info')) CueLayout.cm_registry.add_separator(MediaCue) CueLayout.cm_registry.add_item(self.menuAction, MediaCue) def show_info(self, clicked): media_uri = Application().layout.get_context_cue().media.input_uri() if not media_uri: QMessageBox.critical(MainWindow(), translate('MediaInfo', 'Error'), translate('MediaInfo', 'No info to display')) else: gst_info = gst_uri_metadata(media_uri) info = {'URI': unquote(gst_info.get_uri())} # Audio streams info for stream in gst_info.get_audio_streams(): name = stream.get_stream_type_nick().capitalize() info[name] = { 'Bitrate': str(stream.get_bitrate() // 1000) + ' Kb/s', 'Channels': str(stream.get_channels()), 'Sample rate': str(stream.get_sample_rate()) + ' Hz', 'Sample size': str(stream.get_depth()) + ' bit' } # Video streams info for stream in gst_info.get_video_streams(): name = stream.get_stream_type_nick().capitalize() info[name] = { 'Height': str(stream.get_height()) + ' px', 'Width': str(stream.get_width()) + ' px', 'Framerate': str(round(stream.get_framerate_num() / stream.get_framerate_denom())) } # Media tags info['Tags'] = {} tags = gst_info.get_tags() if tags is not None: tags = gst_parse_tags_list(tags) for tag in tags: if type(tags[tag]).__str__ is not object.__str__: info['Tags'][tag.capitalize()] = str(tags[tag]) if not info['Tags']: info.pop('Tags') # Show the dialog dialog = InfoDialog(MainWindow(), info, Application().layout.get_context_cue().name) dialog.exec_() class InfoDialog(QDialog): def __init__(self, parent, info, title): super().__init__(parent) self.setWindowTitle( translate('MediaInfo', 'Media Info') + ' - ' + title) self.setWindowModality(QtCore.Qt.ApplicationModal) self.setMinimumSize(550, 300) self.resize(550, 500) self.vLayout = QVBoxLayout(self) self.infoTree = QTreeWidget(self) self.infoTree.setColumnCount(2) self.infoTree.setHeaderLabels([translate('MediaInfo', 'Info'), translate('MediaInfo', 'Value')]) self.infoTree.setAlternatingRowColors(True) self.infoTree.setSelectionMode(QAbstractItemView.NoSelection) self.infoTree.setEditTriggers(QAbstractItemView.NoEditTriggers) self.infoTree.header().setStretchLastSection(False) self.infoTree.header().setSectionResizeMode( QHeaderView.ResizeToContents) self.vLayout.addWidget(self.infoTree) self.__generate_items(info) self.infoTree.expandAll() self.buttonBox = QDialogButtonBox(self) self.buttonBox.setStandardButtons(QDialogButtonBox.Close) self.vLayout.addWidget(self.buttonBox) self.buttonBox.rejected.connect(self.close) def __generate_items(self, info, parent=None): for key in sorted(info.keys()): if isinstance(info[key], dict): widget = QTreeWidgetItem([key]) self.__generate_items(info[key], widget) else: widget = QTreeWidgetItem([key, info[key]]) if parent is not None: parent.addChild(widget) else: self.infoTree.addTopLevelItem(widget) linux-show-player-0.5.1/lisp/modules/midi/000077500000000000000000000000001323636106000204655ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/midi/__init__.py000066400000000000000000000000271323636106000225750ustar00rootroot00000000000000from .midi import Midilinux-show-player-0.5.1/lisp/modules/midi/i18n/000077500000000000000000000000001323636106000212445ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/midi/i18n/midi_cs.qm000066400000000000000000000005251323636106000232140ustar00rootroot00000000000000 MIDISettings MIDI default devices Výchozí zařízení MIDI Input Vstup Output Výstup SettingsPageName MIDI settings Nastavení MIDI linux-show-player-0.5.1/lisp/modules/midi/i18n/midi_en.qm000066400000000000000000000005151323636106000232100ustar00rootroot00000000000000 MIDISettings MIDI default devices MIDI default devices Input Input Output Output SettingsPageName MIDI settings MIDI settings linux-show-player-0.5.1/lisp/modules/midi/i18n/midi_es.qm000066400000000000000000000005411323636106000232140ustar00rootroot00000000000000 MIDISettings MIDI default devices Dispositivos MIDI por defecto Input Entrada Output Salida SettingsPageName MIDI settings Ajustes MIDI linux-show-player-0.5.1/lisp/modules/midi/i18n/midi_fr.qm000066400000000000000000000005471323636106000232220ustar00rootroot00000000000000 MIDISettings MIDI default devices Périphériques MIDI par défaut Input Entrée Output Sortie SettingsPageName MIDI settings Préférences MIDI linux-show-player-0.5.1/lisp/modules/midi/i18n/midi_it.qm000066400000000000000000000005531323636106000232240ustar00rootroot00000000000000 MIDISettings MIDI default devices Dispositivi MIDI predefiniti Input Ingresso Output Uscita SettingsPageName MIDI settings Impostazioni MIDI linux-show-player-0.5.1/lisp/modules/midi/i18n/midi_sl_SI.qm000066400000000000000000000005261323636106000236210ustar00rootroot00000000000000 MIDISettings MIDI default devices Privzete MIDI naprave Input Vhod Output Izhod SettingsPageName MIDI settings MIDI nastavitve linux-show-player-0.5.1/lisp/modules/midi/midi.py000066400000000000000000000026651323636106000217720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import mido from lisp.core.configuration import config from lisp.core.module import Module from lisp.modules.midi.midi_settings import MIDISettings from lisp.ui.settings.app_settings import AppSettings class Midi(Module): """Provide MIDI I/O functionality""" def __init__(self): # Register the settings widget AppSettings.register_settings_widget(MIDISettings) bk_name = config['MIDI']['Backend'] try: # Load the backend and set as current mido backend mido.set_backend(mido.Backend(bk_name, load=True)) except Exception: raise RuntimeError('Backend loading failed: {0}'.format(bk_name)) linux-show-player-0.5.1/lisp/modules/midi/midi_common.py000066400000000000000000000033051323636106000233320ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from abc import abstractmethod from lisp.core.singleton import ABCSingleton class MIDICommon(metaclass=ABCSingleton): def __init__(self, port_name='AppDefault'): """ :param port_name: the port name The port name can be: * SysDefault - the system default port * AppDefault - the app default port defined in the config file * - the port name """ self._bakend = None self._port_name = port_name self._port = None def change_port(self, port_name): self._port_name = port_name self.close() self.open() @abstractmethod def open(self): """Open the port""" def close(self): """Close the port""" if self._port is not None: self._port.close() def is_open(self): if self._port is not None: return not self._port.closed return False linux-show-player-0.5.1/lisp/modules/midi/midi_input.py000066400000000000000000000030451323636106000232020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.core.signal import Signal from lisp.modules.midi.midi_common import MIDICommon from lisp.modules.midi.midi_utils import mido_backend, mido_port_name class MIDIInput(MIDICommon): def __init__(self, port_name='AppDefault'): super().__init__(port_name=port_name) self.alternate_mode = False self.new_message = Signal() self.new_message_alt = Signal() def open(self): port_name = mido_port_name(self._port_name, 'I') self._port = mido_backend().open_input(name=port_name, callback=self.__new_message) def __new_message(self, message): if self.alternate_mode: self.new_message_alt.emit(message) else: self.new_message.emit(message) linux-show-player-0.5.1/lisp/modules/midi/midi_output.py000066400000000000000000000024731323636106000234070ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import mido from lisp.modules.midi.midi_common import MIDICommon from lisp.modules.midi.midi_utils import mido_backend, mido_port_name class MIDIOutput(MIDICommon): def __init__(self, port_name='AppDefault'): super().__init__(port_name=port_name) def send_from_str(self, str_message): self.send(mido.parse_string(str_message)) def send(self, message): self._port.send(message) def open(self): port_name = mido_port_name(self._port_name, 'O') self._port = mido_backend().open_output(port_name) linux-show-player-0.5.1/lisp/modules/midi/midi_settings.py000066400000000000000000000073131323636106000237050ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QComboBox, QGridLayout, QLabel from lisp.modules import check_module from lisp.modules.midi.midi_input import MIDIInput from lisp.modules.midi.midi_output import MIDIOutput from lisp.modules.midi.midi_utils import mido_backend from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class MIDISettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'MIDI settings') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.midiGroup = QGroupBox(self) self.midiGroup.setTitle( translate('MIDISettings', 'MIDI default devices')) self.midiGroup.setLayout(QGridLayout()) self.layout().addWidget(self.midiGroup) self.inputLabel = QLabel(translate('MIDISettings', 'Input'), self.midiGroup) self.midiGroup.layout().addWidget(self.inputLabel, 0, 0) self.inputCombo = QComboBox(self.midiGroup) self.midiGroup.layout().addWidget(self.inputCombo, 0, 1) self.outputLabel = QLabel(translate('MIDISettings', 'Output'), self.midiGroup) self.midiGroup.layout().addWidget(self.outputLabel, 1, 0) self.outputCombo = QComboBox(self.midiGroup) self.midiGroup.layout().addWidget(self.outputCombo, 1, 1) self.midiGroup.layout().setColumnStretch(0, 2) self.midiGroup.layout().setColumnStretch(1, 3) if check_module('Midi'): try: self._load_devices() except Exception: self.setEnabled(False) else: self.setEnabled(False) def get_settings(self): conf = {} if self.isEnabled(): conf['inputdevice'] = self.inputCombo.currentText() MIDIInput().change_port(conf['inputdevice']) if self.isEnabled(): conf['outputdevice'] = self.outputCombo.currentText() MIDIOutput().change_port(conf['outputdevice']) return {'MIDI': conf} def load_settings(self, settings): if 'inputdevice' in settings['MIDI']: self.inputCombo.setCurrentText('AppDefault') self.inputCombo.setCurrentText(settings['MIDI']['inputdevice']) if 'outputdevice' in settings['MIDI']: self.outputCombo.setCurrentText('AppDefaut') self.outputCombo.setCurrentText(settings['MIDI']['outputdevice']) def _load_devices(self): backend = mido_backend() self.inputCombo.clear() self.inputCombo.addItems(['AppDefault', 'SysDefault']) self.inputCombo.addItems(backend.get_input_names()) self.outputCombo.clear() self.outputCombo.addItems(['AppDefault', 'SysDefault']) self.outputCombo.addItems(backend.get_output_names()) linux-show-player-0.5.1/lisp/modules/midi/midi_utils.py000066400000000000000000000061251323636106000232050ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import mido from lisp.core.configuration import config #: Define MIDI message attributes as {name : (min, max, default)} MIDI_ATTRIBUTES = { 'channel': (0, 15, 0), 'note': (0, 127, 0), 'velocity': (0, 127, 64), 'control': (0, 127, 0), 'program': (0, 127, 0), 'value': (0, 127, 0), 'song': (0, 127, 0), 'pitch': (-8192, 8191, 0), 'pos': (0, 16383, 0) } #: Define MIDI message types as {name : (attribute1, ..., attributeN)} MIDI_MESSAGES = { 'note_on': ('channel', 'note', 'velocity'), 'note_off': ('channel', 'note', 'velocity'), 'control_change': ('channel', 'control', 'value'), 'program_change': ('channel', 'program',), 'polytouch': ('channel', 'note', 'value'), 'pitchwheel': ('channel', 'pitch',), 'song_select': ('song',), 'songpos': ('pos',), 'start': (), 'stop': (), 'continue': (), } def str_msg_to_dict(str_message): message = mido.parse_string(str_message) dict_message = {'type': message.type} for name in MIDI_MESSAGES.get(message.type, ()): dict_message[name] = getattr(message, name) return dict_message def dict_msg_to_str(dict_message): msg_type = dict_message.pop('type') message = mido.Message(msg_type, **dict_message) return mido.format_as_string(message, include_time=False) def mido_backend(): """Return the current backend object, or None""" backend = None if hasattr(mido, 'backend'): backend = mido.backend if backend is None: raise RuntimeError('MIDI backend not loaded') return backend def mido_port_name(port_name, mode): """Transform application default-port to appropriate values for mido :param port_name: port-name to be checked :param mode: port I/O mode ['I'=input, 'O'=output] :return: the port-name to give to mido to open the right port """ port_name = port_name if port_name == 'AppDefault': if mode.upper() == 'I': port_name = config['MIDI']['InputDevice'] elif mode.upper() == 'O': port_name = config['MIDI']['OutputDevice'] if port_name == 'SysDefault': return None elif mode.upper() == 'I' and port_name in mido_backend().get_input_names(): return port_name elif mode.upper() == 'O' and port_name in mido_backend().get_output_names(): return port_name linux-show-player-0.5.1/lisp/modules/presets/000077500000000000000000000000001323636106000212305ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/presets/__init__.py000066400000000000000000000000341323636106000233360ustar00rootroot00000000000000from .presets import Presetslinux-show-player-0.5.1/lisp/modules/presets/i18n/000077500000000000000000000000001323636106000220075ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/presets/i18n/presets_cs.qm000066400000000000000000000050331323636106000245210ustar00rootroot00000000000000Nelze nahrt pYednastaven "{}"Cannot load preset "{}"PresetsHNelze pYejmenovat pYednastaven "{}"Cannot rename preset "{}"Presets>Nelze ulo~it pYednastaven "{}"Cannot save preset "{}"Presets:Nelze prohledat pYednastavenCannot scan presetsPresetsTyp nar~kyCue typePresetsUpravitEditPresetsFChyba pYi mazn pYednastaven "{}" Error while deleting preset "{}"Presets Vybrno vyvedenExport selectedPresetsVyvedenImportPresets(Nahrt pYednastaven Load presetPresetsHPYednastaven ji~ existuje. PYepsat?!Preset already exists, overwrite?Presets&Nzev pYednastaven Preset namePresetsPYednastavenPresetsPresetsOdstranitRemovePresetsPYejmenovatRenamePresets2Ulo~it jako pYednastavenSave as presetPresets(Vybrat pYednastaven Select PresetPresetsXNkter pYednastaven ji~ existuj. PYepsat?'Some presets already exists, overwrite?Presets8Stejn nzev se ji~ pou~v!The same name is already used!PresetsVarovnWarningPresetslinux-show-player-0.5.1/lisp/modules/presets/i18n/presets_cs.ts000066400000000000000000000122011323636106000245250ustar00rootroot00000000000000 Preset Create Cue Vytvořit narážku Load on selected Cues Nahrát na vybraných narážkách Presets Presets Přednastavení Load preset Nahrát přednastavení Save as preset Uložit jako přednastavení Cannot scan presets Nelze prohledat přednastavení Error while deleting preset "{}" Chyba při mazání přednastavení "{}" Cannot load preset "{}" Nelze nahrát přednastavení "{}" Cannot save preset "{}" Nelze uložit přednastavení "{}" Cannot rename preset "{}" Nelze přejmenovat přednastavení "{}" Select Preset Vybrat přednastavení Preset already exists, overwrite? Přednastavení již existuje. Přepsat? Preset name Název přednastavení Add Přidat Rename Přejmenovat Edit Upravit Remove Odstranit Export selected Vybráno vyvedení Import Vyvedení Warning Varování The same name is already used! Stejný název se již používá! Cannot create a cue from this preset: {} Nelze vytvořit narážku z tohoto přednastavení: "{}" Cannot export correctly. Nelze vyvést správně. Some presets already exists, overwrite? Některá přednastavení již existují. Přepsat? Cannot import correctly. Nelze zavést správně. Cue type Typ narážky linux-show-player-0.5.1/lisp/modules/presets/i18n/presets_en.qm000066400000000000000000000044311323636106000245170ustar00rootroot00000000000000 Preset Create Cue Create Cue Load on selected Cues Load on selected Cues Presets Presets Presets Load preset Load preset Save as preset Save as preset Cannot scan presets Cannot scan presets Error while deleting preset "{}" Error while deleting preset "{}" Cannot load preset "{}" Cannot load preset "{}" Cannot save preset "{}" Cannot save preset "{}" Cannot rename preset "{}" Cannot rename preset "{}" Select Preset Select Preset Preset already exists, overwrite? Preset already exists, overwrite? Preset name Preset name Add Add Rename Rename Edit Edit Remove Remove Export selected Export selected Import Import Warning Warning The same name is already used! The same name is already used! Cannot create a cue from this preset: {} Cannot create a cue from this preset: {} Cannot export correctly. Cannot export correctly. Some presets already exists, overwrite? Some presets already exists, overwrite? Cannot import correctly. Cannot import correctly. Cue type Cue type linux-show-player-0.5.1/lisp/modules/presets/i18n/presets_es.qm000066400000000000000000000051151323636106000245240ustar00rootroot00000000000000No se puede guardar Preset "{}"Cannot save preset "{}"Presets:No se pueden escanear PresetsCannot scan presetsPresetsTipo de CueCue typePresets EditarEditPresets6Error al borrar Preset "{}" Error while deleting preset "{}"Presets,Exportar seleccionadosExport selectedPresetsImportarImportPresetsCargar Preset Load presetPresetsXEl Preset ya existe, desea sobreescribirlo?!Preset already exists, overwrite?Presets"Nombre del Preset Preset namePresetsPresetsPresetsPresetsEliminarRemovePresetsCambiar nombreRenamePresets&Guardar como PresetSave as presetPresets$Seleccionar Preset Select PresetPresetshAlgunos Presets ya existen, desea sobreescribirlos?'Some presets already exists, overwrite?PresetsLEl mismo nombre ya est siendo usado!The same name is already used!PresetsAdvertenciaWarningPresetslinux-show-player-0.5.1/lisp/modules/presets/i18n/presets_es.ts000066400000000000000000000121151323636106000245330ustar00rootroot00000000000000 Preset Create Cue Crear Cue Load on selected Cues Abrir en Cues seleccionadas Presets Presets Presets Load preset Cargar Preset Save as preset Guardar como Preset Cannot scan presets No se pueden escanear Presets Error while deleting preset "{}" Error al borrar Preset "{}" Cannot load preset "{}" No se puede cargar Preset "{}" Cannot save preset "{}" No se puede guardar Preset "{}" Cannot rename preset "{}" No se puede cambiar el nombre del Preset "{}" Select Preset Seleccionar Preset Preset already exists, overwrite? El Preset ya existe, ¿desea sobreescribirlo? Preset name Nombre del Preset Add Añadir Rename Cambiar nombre Edit Editar Remove Eliminar Export selected Exportar seleccionados Import Importar Warning Advertencia The same name is already used! ¡El mismo nombre ya está siendo usado! Cannot create a cue from this preset: {} No se puede crear un Cue desde este Preset: {} Cannot export correctly. No se puede exportar correctamente. Some presets already exists, overwrite? Algunos Presets ya existen, ¿desea sobreescribirlos? Cannot import correctly. No se puede importar correctamente. Cue type Tipo de Cue linux-show-player-0.5.1/lisp/modules/presets/i18n/presets_fr.qm000066400000000000000000000053551323636106000245320ustar00rootroot00000000000000Sauvegarder un pr-rglage sousSave as presetPresets6Slectionner le pr-rglage Select PresetPresetsdCertains pr-rglages existent dj, les craser ?'Some presets already exists, overwrite?Presets<Le mme nom est dj utilis !The same name is already used!PresetsAttentionWarningPresetslinux-show-player-0.5.1/lisp/modules/presets/i18n/presets_fr.ts000066400000000000000000000123171323636106000245370ustar00rootroot00000000000000 Preset Create Cue Créer une cue Load on selected Cues Charger les cues sélectionnées Presets Presets Préréglages Load preset Charger un préréglage Save as preset Sauvegarder un préréglage sous Cannot scan presets Impossible d'examiner les préréglages Error while deleting preset "{}" Erreur lors de la suppression du préréglage "{}" Cannot load preset "{}" Impossible de charger le préréglage "{}" Cannot save preset "{}" Impossible de sauvegarder le préréglage "{}" Cannot rename preset "{}" Impossible de renommer le préréglage "{}" Select Preset Sélectionner le préréglage Preset already exists, overwrite? Le préréglage existe déjà, l'écraser ? Preset name Nom du préréglage Add Ajouter Rename Renommer Edit Éditer Remove Supprimer Export selected Exporter le sélectionné Import Importer Warning Attention The same name is already used! Le même nom est déjà utilisé ! Cannot create a cue from this preset: {} Impossible de créer une cue depuis ce préréglage : {} Cannot export correctly. Impossible d'exporter correctement Some presets already exists, overwrite? Certains préréglages existent déjà, les écraser ? Cannot import correctly. Impossible d'importer correctement. Cue type Type de cue linux-show-player-0.5.1/lisp/modules/presets/i18n/presets_it.qm000066400000000000000000000047671323636106000245450ustar00rootroot00000000000000 Preset Create Cue Crea Cue Load on selected Cues Carica sulle cue selezionate Presets Presets Preset Load preset Carica preset Save as preset Salva come preset Cannot scan presets Impossibile cercare i preset Error while deleting preset "{}" Errore durate l'eliminazione del preset "{}" Cannot load preset "{}" Impossible caricare il preset "{}" Cannot save preset "{}" Impossibile salvare il preset "{}" Cannot rename preset "{}" Impossible rinominare il preset "{}" Select Preset Seleziona Preset Preset already exists, overwrite? Preset esistente, sovrascrivere? Preset name Nome preset Add Aggiungi Rename Rinomina Edit Modifica Remove Rimuovi Export selected Esporta selezionati Import Importa Warning Avviso The same name is already used! Lo stesso nome è già in uso! Cannot create a cue from this preset: {} Impossibile creare cue da questo preset: {} Cannot export correctly. Impossibile esportare correttamente Some presets already exists, overwrite? Alcuni preset esistono già, sovrascriverli? Cannot import correctly. Impossible importare correttamente. Cue type Tipo di cue linux-show-player-0.5.1/lisp/modules/presets/i18n/presets_sl_SI.qm000066400000000000000000000050741323636106000251320ustar00rootroot00000000000000 Preset Create Cue Ustvari vrsto Load on selected Cues Naloži izbrane vrste Presets Presets Pred-nastavitve Load preset Naloži pred-nastavitve Save as preset Shrani kot pred-nastavitev Cannot scan presets Ne morem prebrat pred-nastavitve Error while deleting preset "{}" Napaka med brisanjem pred-nastavitve "{}" Cannot load preset "{}" Ne morem naložit pred-nastavitve "{}" Cannot save preset "{}" Ne morem shranit pred-nastavitev "{}" Cannot rename preset "{}" Ne morem preimenovat pred-nastavitve "{}" Select Preset Izberi pred-nastavitve Preset already exists, overwrite? Pred-nastavitev že obstaja, prepišem? Preset name Ime pred-nastavitve Add Dodaj Rename Preimenuj Edit Uredi Remove Odstrani Export selected Izvozi izbrano Import Uvozi Warning Opozorilo The same name is already used! Ime je že v uporabi! Cannot create a cue from this preset: {} Ne morem ustvarit vrste iz te pred-nastavitve: "{}" Cannot export correctly. Ne morem pravilno izvozit. Some presets already exists, overwrite? Nekatere pred-nastavitve že obstajajo, prepišem? Cannot import correctly. Ne morem pravilno uvozit. Cue type Tip vrste linux-show-player-0.5.1/lisp/modules/presets/lib.py000066400000000000000000000135231323636106000223540ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import json import os from zipfile import ZipFile, BadZipFile from lisp.core import configuration from lisp.core.actions_handler import MainActionsHandler from lisp.cues.cue_actions import UpdateCueAction, UpdateCuesAction try: from os import scandir except ImportError: from scandir import scandir PRESETS_DIR = os.path.join(configuration.CFG_DIR, 'presets') def preset_path(name): """Return the preset-file path. :param name: Name of the preset :type name: str """ return os.path.join(PRESETS_DIR, name) def scan_presets(): """Iterate over presets. Every time this function is called a search in `PRESETS_DIR` is performed. """ for entry in scandir(PRESETS_DIR): if entry.is_file(): yield entry.name def preset_exists(name): """Return True if the preset already exist, False otherwise. :param name: Name of the preset :type name: str :rtype: bool """ return os.path.exists(preset_path(name)) def load_on_cue(preset_name, cue): """Load the preset with the given name on cue. Use `UpdateCueAction` :param preset_name: The preset to be loaded :type preset_name: str :param cue: The cue on which load the preset :type cue: lisp.cue.Cue """ MainActionsHandler.do_action( UpdateCueAction(load_preset(preset_name), cue)) def load_on_cues(preset_name, cues): """ Use `UpdateCuesAction` :param preset_name: The preset to be loaded :type preset_name: str :param cues: The cues on which load the preset :type cues: typing.Iterable[lisp.cue.Cue] """ MainActionsHandler.do_action( UpdateCuesAction(load_preset(preset_name), cues)) def load_preset(name): """Load the preset with the given name and return it. :param name: The preset name :type name: str :rtype: dict """ path = preset_path(name) with open(path, mode='r') as in_file: return json.load(in_file) def write_preset(name, preset): """Write a preset with the given name in `PRESET_DIR`. :param name: The preset name :type name: str :param preset: The content of the preset :type preset: dict :return: True when no error occurs, False otherwise :rtype: bool """ path = preset_path(name) with open(path, mode='w') as out_file: json.dump(preset, out_file) def rename_preset(old_name, new_name): """Rename an existing preset, if the new name is not already used. :param old_name: The preset (old) name :param new_name: The new preset name :return: True if the preset as been renamed successfully, False otherwise :rtype: bool """ os.rename(preset_path(old_name), preset_path(new_name)) def delete_preset(name): """Delete a preset form the file-system. :param name: The preset to be deleted :type name: str """ path = preset_path(name) if os.path.exists(path): os.remove(path) class PresetImportError(Exception): """ Raised when an error occur during presets import. """ class PresetExportError(Exception): """ Raised when an error occur during presets export. """ def export_presets(names, archive): """Export presets-files into an archive. :param names: The presets to be exported :type names: typing.Iterable[Str] :param archive: The path of the archive :type archive: str """ if names: try: with ZipFile(archive, mode='w') as archive: for name in names: archive.write(preset_path(name), name) except(OSError, BadZipFile) as e: raise PresetExportError(str(e)) def import_presets(archive, overwrite=True): """Import presets for an archive. :param archive: The archive path :type archive: str :param overwrite: Overwrite existing files :type overwrite: bool """ try: with ZipFile(archive) as archive: for member in archive.namelist(): if not (preset_exists(member) and not overwrite): archive.extract(member, path=PRESETS_DIR) except(OSError, BadZipFile) as e: raise PresetImportError(str(e)) def import_has_conflicts(archive): """Verify if the `archive` files conflicts with the user presets. :param archive: path of the archive to analyze :type archive: str :rtype: bool """ try: with ZipFile(archive) as archive: for member in archive.namelist(): if preset_exists(member): return True except(OSError, BadZipFile) as e: raise PresetImportError(str(e)) return False def import_conflicts(archive): """Return a list of conflicts between `archive` files and user presets. :param archive: path of the archive to analyze :type archive: str """ conflicts = [] try: with ZipFile(archive) as archive: for member in archive.namelist(): if preset_exists(member): conflicts.append(member) except(OSError, BadZipFile) as e: raise PresetImportError(str(e)) return conflicts linux-show-player-0.5.1/lisp/modules/presets/presets.py000066400000000000000000000065421323636106000232760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import os from PyQt5.QtWidgets import QAction, QMenu from lisp.application import Application from lisp.core.module import Module from lisp.layouts.cue_layout import CueLayout from lisp.modules.presets.lib import PRESETS_DIR, load_on_cue, preset_exists from lisp.modules.presets.presets_ui import select_preset_dialog, \ PresetsDialog, save_preset_dialog, check_override_dialog, write_preset, \ load_preset_error, write_preset_error from lisp.ui.mainwindow import MainWindow from lisp.ui.ui_utils import translate class Presets(Module): def __init__(self): super().__init__() if not os.path.exists(PRESETS_DIR): os.makedirs(PRESETS_DIR, exist_ok=True) # Entry in mainWindow menu self.manageAction = QAction(MainWindow()) self.manageAction.triggered.connect(self.__edit_presets) self.manageAction.setText(translate('Presets', 'Presets')) self.menu_action = MainWindow().menuTools.addAction(self.manageAction) self.loadOnCueAction = QAction(None) self.loadOnCueAction.triggered.connect(self.__load_on_cue) self.loadOnCueAction.setText(translate('Presets', 'Load preset')) self.createFromCueAction = QAction(None) self.createFromCueAction.triggered.connect(self.__create_from_cue) self.createFromCueAction.setText(translate('Presets', 'Save as preset')) CueLayout.cm_registry.add_separator() CueLayout.cm_registry.add_item(self.loadOnCueAction) CueLayout.cm_registry.add_item(self.createFromCueAction) CueLayout.cm_registry.add_separator() @staticmethod def __edit_presets(): ui = PresetsDialog(parent=MainWindow()) ui.show() @staticmethod def __load_on_cue(): preset_name = select_preset_dialog() if preset_name is not None: try: load_on_cue(preset_name, Application().layout.get_context_cue()) except OSError as e: load_preset_error(e, preset_name, parent=MainWindow()) @staticmethod def __create_from_cue(): cue = Application().layout.get_context_cue() name = save_preset_dialog(cue.name) if name is not None: if not (preset_exists(name) and not check_override_dialog(name)): preset = cue.properties(only_changed=True) # Discard id and index preset.pop('id') preset.pop('index') try: write_preset(name, preset) except OSError as e: write_preset_error(e, name, parent=MainWindow()) linux-show-player-0.5.1/lisp/modules/presets/presets_ui.py000066400000000000000000000373661323636106000240030ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QComboBox from PyQt5.QtWidgets import QDialog, QInputDialog, QMessageBox, QListWidget, \ QPushButton, QVBoxLayout, QGridLayout, QDialogButtonBox, QWidget, QLabel, \ QLineEdit from PyQt5.QtWidgets import QFileDialog from PyQt5.QtWidgets import QHBoxLayout from lisp.application import Application from lisp.core.util import natural_keys from lisp.cues.cue import Cue from lisp.cues.cue_factory import CueFactory from lisp.modules.presets.lib import preset_exists, export_presets, \ import_presets, import_has_conflicts, PresetExportError,\ PresetImportError, scan_presets, delete_preset, write_preset,\ rename_preset, load_preset, load_on_cues from lisp.ui.mainwindow import MainWindow from lisp.ui.settings.cue_settings import CueSettings, CueSettingsRegistry from lisp.ui.ui_utils import translate from lisp.ui.widgets import QDetailedMessageBox def preset_error(exception, text, parent=None): QDetailedMessageBox.dcritical( translate('Presets', 'Presets'), text, str(exception), parent=parent, ) def scan_presets_error(exception, parent=None): preset_error( exception, translate('Presets', 'Cannot scan presets'), parent=parent ) def delete_preset_error(exception, name, parent=None): preset_error( exception, translate('Presets', 'Error while deleting preset "{}"').format(name), parent=parent ) def load_preset_error(exception, name, parent=None): preset_error( exception, translate('Presets', 'Cannot load preset "{}"').format(name), parent=parent ) def write_preset_error(exception, name, parent=None): preset_error( exception, translate('Presets', 'Cannot save preset "{}"').format(name), parent=parent ) def rename_preset_error(exception, name, parent=None): preset_error( exception, translate('Presets', 'Cannot rename preset "{}"').format(name), parent=parent ) def select_preset_dialog(): try: presets = tuple(sorted(scan_presets(), key=natural_keys)) if presets: item, confirm = QInputDialog.getItem( MainWindow(), translate('Presets', 'Select Preset'), '', presets ) if confirm: return item except OSError as e: scan_presets_error(e, parent=MainWindow()) def check_override_dialog(preset_name): answer = QMessageBox.question( MainWindow(), translate('Presets', 'Presets'), translate('Presets', 'Preset already exists, overwrite?'), buttons=QMessageBox.Yes | QMessageBox.Cancel ) return answer == QMessageBox.Yes def save_preset_dialog(base_name=''): name, confirm = QInputDialog.getText( MainWindow(), translate('Presets', 'Presets'), translate('Presets', 'Preset name'), text=base_name ) if confirm: return name class PresetsDialog(QDialog): def __init__(self, **kwargs): super().__init__(**kwargs) self.resize(500, 400) self.setMaximumSize(self.size()) self.setMinimumSize(self.size()) self.setModal(True) self.setLayout(QGridLayout()) # TODO: natural sorting (QStringListModel + QListView + ProxyModel) self.presetsList = QListWidget(self) self.presetsList.setAlternatingRowColors(True) self.presetsList.setFocusPolicy(Qt.NoFocus) self.presetsList.setSortingEnabled(True) self.presetsList.setSelectionMode(QListWidget.ExtendedSelection) self.presetsList.itemSelectionChanged.connect(self.__selection_changed) self.presetsList.itemDoubleClicked.connect(self.__edit_preset) self.layout().addWidget(self.presetsList, 0, 0) # Preset buttons self.presetsButtons = QWidget(self) self.presetsButtons.setLayout(QVBoxLayout()) self.presetsButtons.layout().setContentsMargins(0, 0, 0, 0) self.layout().addWidget(self.presetsButtons, 0, 1) self.layout().setAlignment(self.presetsButtons, Qt.AlignTop) self.addPresetButton = QPushButton(self.presetsButtons) self.addPresetButton.clicked.connect(self.__add_preset) self.presetsButtons.layout().addWidget(self.addPresetButton) self.renamePresetButton = QPushButton(self.presetsButtons) self.renamePresetButton.clicked.connect(self.__rename_preset) self.presetsButtons.layout().addWidget(self.renamePresetButton) self.editPresetButton = QPushButton(self.presetsButtons) self.editPresetButton.clicked.connect(self.__edit_preset) self.presetsButtons.layout().addWidget(self.editPresetButton) self.removePresetButton = QPushButton(self.presetsButtons) self.removePresetButton.clicked.connect(self.__remove_preset) self.presetsButtons.layout().addWidget(self.removePresetButton) self.cueFromSelectedButton = QPushButton(self.presetsButtons) self.cueFromSelectedButton.clicked.connect(self.__cue_from_selected) self.presetsButtons.layout().addWidget(self.cueFromSelectedButton) self.loadOnSelectedButton = QPushButton(self.presetsButtons) self.loadOnSelectedButton.clicked.connect(self.__load_on_selected) self.presetsButtons.layout().addWidget(self.loadOnSelectedButton) # Import/Export buttons self.ieButtons = QWidget(self) self.ieButtons.setLayout(QHBoxLayout()) self.ieButtons.layout().setContentsMargins(0, 0, 0, 0) self.layout().addWidget(self.ieButtons, 1, 0) self.layout().setAlignment(self.ieButtons, Qt.AlignLeft) self.exportSelectedButton = QPushButton(self.ieButtons) self.exportSelectedButton.clicked.connect(self.__export_presets) self.ieButtons.layout().addWidget(self.exportSelectedButton) self.importButton = QPushButton(self.ieButtons) self.importButton.clicked.connect(self.__import_presets) self.ieButtons.layout().addWidget(self.importButton) # Dialog buttons self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setStandardButtons(QDialogButtonBox.Ok) self.dialogButtons.accepted.connect(self.accept) self.layout().addWidget(self.dialogButtons, 1, 1) self.layout().setColumnStretch(0, 4) self.layout().setColumnStretch(1, 1) self.retranslateUi() self.__populate() self.__selection_changed() def retranslateUi(self): self.addPresetButton.setText(translate('Presets', 'Add')) self.renamePresetButton.setText(translate('Presets', 'Rename')) self.editPresetButton.setText(translate('Presets', 'Edit')) self.removePresetButton.setText(translate('Presets', 'Remove')) self.cueFromSelectedButton.setText(translate('Preset', 'Create Cue')) self.loadOnSelectedButton.setText(translate('Preset', 'Load on selected Cues')) self.exportSelectedButton.setText(translate('Presets', 'Export selected')) self.importButton.setText(translate('Presets', 'Import')) def __populate(self): self.presetsList.clear() try: self.presetsList.addItems(scan_presets()) except OSError as e: scan_presets_error(e, parent=self) def __remove_preset(self): for item in self.presetsList.selectedItems(): try: delete_preset(item.text()) self.presetsList.takeItem(self.presetsList.currentRow()) except OSError as e: delete_preset_error(e, item.text(), parent=self) def __add_preset(self): dialog = NewPresetDialog(parent=self) if dialog.exec_() == QDialog.Accepted: preset_name = dialog.get_name() cue_type = dialog.get_type() exists = preset_exists(preset_name) if not (exists and not check_override_dialog(preset_name)): try: write_preset(preset_name, {'_type_': cue_type}) if not exists: self.presetsList.addItem(preset_name) except OSError as e: write_preset_error(e, preset_name, parent=self) def __rename_preset(self): item = self.presetsList.currentItem() if item is not None: new_name = save_preset_dialog(base_name=item.text()) if new_name is not None and new_name != item.text(): if preset_exists(new_name): QMessageBox.warning( self, translate('Presets', 'Warning'), translate('Presets', 'The same name is already used!') ) else: try: rename_preset(item.text(), new_name) item.setText(new_name) except OSError as e: rename_preset_error(e, item.text(), parent=self) def __edit_preset(self): item = self.presetsList.currentItem() if item is not None: try: preset = load_preset(item.text()) if preset is not None: try: cue_class = CueFactory.create_cue(preset.get('_type_')) cue_class = cue_class.__class__ except Exception: cue_class = Cue edit_dialog = CueSettings(cue_class=cue_class) edit_dialog.load_settings(preset) if edit_dialog.exec_() == edit_dialog.Accepted: preset.update(edit_dialog.get_settings()) try: write_preset(item.text(), preset) except OSError as e: write_preset_error(e, item.text(), parent=self) except OSError as e: load_preset_error(e, item.text(), parent=self) def __cue_from_preset(self, preset_name): try: preset = load_preset(preset_name) if preset is not None: if CueFactory.has_factory(preset.get('_type_')): cue = CueFactory.create_cue(preset['_type_']) cue.update_properties(preset) Application().cue_model.add(cue) else: QMessageBox.warning( self, translate('Presets', 'Warning'), translate('Presets', 'Cannot create a cue from this ' 'preset: {}').format(preset_name) ) except OSError as e: load_preset_error(e, preset_name, parent=self) def __cue_from_selected(self): for item in self.presetsList.selectedItems(): self.__cue_from_preset(item.text()) def __load_on_selected(self): item = self.presetsList.currentItem() if item is not None: preset_name = item.text() try: cues = Application().layout.get_selected_cues() if cues: load_on_cues(preset_name, cues) except OSError as e: load_preset_error(e, preset_name, parent=MainWindow()) def __export_presets(self): names = [item.text() for item in self.presetsList.selectedItems()] archive, _ = QFileDialog.getSaveFileName( self, directory='archive.presets', filter='*.presets' ) if archive != '': if not archive.endswith('.presets'): archive += '.presets' try: export_presets(names, archive) except PresetExportError as e: QDetailedMessageBox.dcritical( translate('Presets', 'Presets'), translate('Presets', 'Cannot export correctly.'), str(e), parent=self ) def __import_presets(self): archive, _ = QFileDialog.getOpenFileName(self, filter='*.presets') if archive != '': try: if import_has_conflicts(archive): answer = QMessageBox.question( self, translate('Presets', 'Presets'), translate('Presets', 'Some presets already exists, ' 'overwrite?'), buttons=QMessageBox.Yes | QMessageBox.Cancel) if answer != QMessageBox.Yes: return import_presets(archive) self.__populate() except PresetImportError as e: QDetailedMessageBox.dcritical( translate('Presets', 'Presets'), translate('Presets', 'Cannot import correctly.'), str(e), parent=self ) def __selection_changed(self): selection = len(self.presetsList.selectedIndexes()) self.editPresetButton.setEnabled(selection == 1) self.renamePresetButton.setEnabled(selection == 1) self.loadOnSelectedButton.setEnabled(selection == 1) self.removePresetButton.setEnabled(selection > 0) self.cueFromSelectedButton.setEnabled(selection > 0) self.exportSelectedButton.setEnabled(selection > 0) class NewPresetDialog(QDialog): def __init__(self, **kwargs): super().__init__(**kwargs) self.resize(320, 105) self.setMaximumSize(self.size()) self.setMinimumSize(self.size()) self.setModal(True) self.setLayout(QGridLayout()) self.nameLabel = QLabel(self) self.layout().addWidget(self.nameLabel, 0, 0) self.nameLineEdit = QLineEdit(self) self.layout().addWidget(self.nameLineEdit, 0, 1) self.typeLabel = QLabel(self) self.layout().addWidget(self.typeLabel, 1, 0) self.typeComboBox = QComboBox(self) for cue_class in CueSettingsRegistry().ref_classes(): self.typeComboBox.addItem(translate('CueName', cue_class.Name), cue_class.__name__) self.layout().addWidget(self.typeComboBox, 1, 1) self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.dialogButtons.accepted.connect(self.accept) self.dialogButtons.rejected.connect(self.reject) self.layout().addWidget(self.dialogButtons, 2, 0, 1, 2) self.layout().setColumnStretch(0, 1) self.layout().setColumnStretch(1, 3) self.retranslateUi() def retranslateUi(self): self.nameLabel.setText(translate('Presets', 'Preset name')) self.typeLabel.setText(translate('Presets', 'Cue type')) def get_name(self): return self.nameLineEdit.text() def get_type(self): return self.typeComboBox.currentData() linux-show-player-0.5.1/lisp/modules/remote/000077500000000000000000000000001323636106000210365ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/remote/__init__.py000066400000000000000000000000331323636106000231430ustar00rootroot00000000000000from .remote import Remote linux-show-player-0.5.1/lisp/modules/remote/controller.py000066400000000000000000000056751323636106000236100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import logging import socket from http.client import HTTPConnection from xmlrpc.client import ServerProxy, Fault, Transport from xmlrpc.server import SimpleXMLRPCServer from lisp.core.decorators import async from lisp.core.singleton import Singleton from lisp.modules.remote.discovery import Announcer from lisp.modules.remote.dispatcher import RemoteDispatcher class TimeoutTransport(Transport): timeout = 2.0 def set_timeout(self, timeout): self.timeout = timeout def make_connection(self, host): return HTTPConnection(host, timeout=self.timeout) class RemoteController(metaclass=Singleton): """ Provide control over a SimpleXMLRPCServer. """ def __init__(self, ip='localhost', port=8070): try: self.server = SimpleXMLRPCServer((ip, port), allow_none=True, logRequests=False) self._announcer = Announcer() except OSError as error: # If address already in use if error.errno == 98: raise Exception( "Only one application instance can use this module") else: raise error self.server.register_introspection_functions() self.server.register_instance(RemoteDispatcher()) @async def start(self): logging.info('REMOTE: Session started at ' + str(self.server.server_address)) self._announcer.start() self.server.serve_forever() # Blocking self._announcer.stop() logging.info('REMOTE: Session ended') def stop(self): self.server.shutdown() @staticmethod def connect_to(uri): proxy = ServerProxy(uri, transport=TimeoutTransport()) try: # Call a fictive method. proxy._() except Fault: # Connected, the method doesn't exist, which is expected. pass except socket.error: # Not connected, socket error mean that the service is unreachable. raise OSError('Session at ' + uri + ' is unreachable') # Just in case the method is registered in the XmlRPC server return proxy linux-show-player-0.5.1/lisp/modules/remote/discovery.py000066400000000000000000000064061323636106000234250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import socket from threading import Thread from lisp.core.configuration import config from lisp.core.signal import Signal IP = config['Remote']['BindIp'] PORT = int(config['Remote']['DiscoverPort']) # To avoid conflicts with other applications MAGIC = config['Remote']['DiscoverMagic'] class Announcer(Thread): def __init__(self): super().__init__(daemon=True) # Create UDP socket self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) self._socket.bind((IP, PORT)) self._running = True def run(self): while self._running: data, addr = self._socket.recvfrom(1024) if data is not None and addr is not None: data = str(data, 'utf-8') if data == MAGIC: data += socket.gethostname() self._socket.sendto(bytes(data, 'utf-8'), addr) def stop(self): self._running = False try: self._socket.shutdown(socket.SHUT_RDWR) except Exception: pass self._socket.close() self.join() class Discoverer(Thread): def __init__(self): super().__init__() self.discovered = Signal() # Create UDP socket self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) self._running = True self._cache = [] def run(self): self._socket.sendto(bytes(MAGIC, 'utf-8'), ('', PORT)) while self._running: data, addr = self._socket.recvfrom(1024) if data is not None and addr is not None: data = str(data, 'utf-8') # Take only the IP, discard the port addr = addr[0] # Check if valid and new announcement if data.startswith(MAGIC) and addr not in self._cache: # Get the host name host = data[len(MAGIC):] # Append the adders to the cache self._cache.append(addr) # Emit self.discovered.emit((addr, host)) def stop(self): self._running = False try: self._socket.shutdown(socket.SHUT_RDWR) except Exception: pass self._socket.close() self.join() def get_discovered(self): return self._cache.copy() linux-show-player-0.5.1/lisp/modules/remote/dispatcher.py000066400000000000000000000025621323636106000235430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.application import Application from lisp.cues.cue import Cue class RemoteDispatcher: # Layout functions def get_cue_at(self, index): cue = Application().layout.model_adapter.item(index) if cue is not None: return cue.properties() return {} def get_cues(self, cue_class=Cue): cues = Application().cue_model.filter(cue_class) return [cue.properties() for cue in cues] # Cue function def execute(self, index): cue = Application().layout.model_adapter.item(index) if cue is not None: cue.execute() linux-show-player-0.5.1/lisp/modules/remote/remote.py000066400000000000000000000025371323636106000227120ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import socket from lisp.core.configuration import config from lisp.core.module import Module from lisp.modules.remote.controller import RemoteController class Remote(Module): """Provide remote control over cues via RPCs calls.""" def __init__(self): ip = config['Remote']['BindIp'] port = int(config['Remote']['BindPort']) RemoteController(ip=ip, port=port) RemoteController().start() def terminate(self): RemoteController().stop() def compose_uri(url, port, directory='/'): return 'http://' + url + ':' + str(port) + directorylinux-show-player-0.5.1/lisp/modules/replay_gain/000077500000000000000000000000001323636106000220355ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/replay_gain/__init__.py000066400000000000000000000000431323636106000241430ustar00rootroot00000000000000from .replay_gain import ReplayGainlinux-show-player-0.5.1/lisp/modules/replay_gain/gain_ui.py000066400000000000000000000116561323636106000240330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from os import cpu_count from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QRadioButton, QSpinBox, \ QCheckBox, QFrame, QLabel, QGridLayout, QButtonGroup, QProgressDialog from lisp.ui.ui_utils import translate class GainUi(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setWindowModality(Qt.ApplicationModal) self.setMaximumSize(380, 210) self.setMinimumSize(380, 210) self.resize(380, 210) self.setLayout(QGridLayout()) # ReplayGain mode self.gainRadio = QRadioButton(self) self.gainRadio.setChecked(True) self.layout().addWidget(self.gainRadio, 0, 0) self.gainSpinBox = QSpinBox(self) self.gainSpinBox.setMaximum(150) self.gainSpinBox.setValue(89) self.layout().addWidget(self.gainSpinBox, 0, 1) # Normalize mode self.normalizeRadio = QRadioButton(self) self.layout().addWidget(self.normalizeRadio, 1, 0) self.normalizeSpinBox = QSpinBox(self) self.normalizeSpinBox.setRange(-100, 0) self.normalizeSpinBox.setEnabled(False) self.layout().addWidget(self.normalizeSpinBox, 1, 1) # All/Only selected self.selectionMode = QCheckBox(self) self.layout().addWidget(self.selectionMode, 2, 0, 1, 2) self.line = QFrame(self) self.line.setFrameShape(QFrame.HLine) self.line.setFrameShadow(QFrame.Sunken) self.layout().addWidget(self.line, 3, 0, 1, 2) # Max threads (up to cpu count) self.cpuLabel = QLabel(self) self.layout().addWidget(self.cpuLabel, 4, 0) self.cpuSpinBox = QSpinBox(self) max_cpu = cpu_count() max_cpu = max_cpu if max_cpu is not None else 1 self.cpuSpinBox.setRange(1, max_cpu) self.cpuSpinBox.setValue(max_cpu) self.layout().addWidget(self.cpuSpinBox, 4, 1) self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.layout().addWidget(self.dialogButtons, 5, 0, 1, 2) self.normalizeRadio.toggled.connect(self.normalizeSpinBox.setEnabled) self.normalizeRadio.toggled.connect(self.gainSpinBox.setDisabled) self.dialogButtons.accepted.connect(self.accept) self.dialogButtons.rejected.connect(self.reject) self.modeGroup = QButtonGroup() self.modeGroup.addButton(self.gainRadio) self.modeGroup.addButton(self.normalizeRadio) self.retranslateUi() def retranslateUi(self): self.setWindowTitle( translate('ReplayGain', 'ReplayGain / Normalization')) self.cpuLabel.setText(translate('ReplayGain', 'Threads number')) self.selectionMode.setText( translate('ReplayGain', 'Apply only to selected media')) self.gainRadio.setText( translate('ReplayGain', 'ReplayGain to (dB SPL)')) self.normalizeRadio.setText( translate('ReplayGain', 'Normalize to (dB)')) def mode(self): return 0 if self.gainRadio.isChecked() else 1 def only_selected(self): return self.selectionMode.isChecked() def norm_level(self): return self.normalizeSpinBox.value() def ref_level(self): return self.gainSpinBox.value() def threads(self): return self.cpuSpinBox.value() class GainProgressDialog(QProgressDialog): def __init__(self, maximum, parent=None): super().__init__(parent) self.setWindowModality(Qt.ApplicationModal) self.setWindowTitle(translate('ReplayGain', 'Processing files ...')) self.setMaximumSize(320, 110) self.setMinimumSize(320, 110) self.resize(320, 110) self.setMaximum(maximum) self.setLabelText('0 / {0}'.format(maximum)) def on_progress(self, value): if value == -1: # Hide the progress dialog self.setValue(self.maximum()) self.deleteLater() else: self.setValue(self.value() + value) self.setLabelText('{0} / {1}'.format(self.value(), self.maximum())) linux-show-player-0.5.1/lisp/modules/replay_gain/i18n/000077500000000000000000000000001323636106000226145ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/replay_gain/i18n/replay_gain_cs.qm000066400000000000000000000016711323636106000261370ustar00rootroot00000000000000Pou~t pouze na vybran zznamyApply only to selected media ReplayGainSpo tat Calculate ReplayGain0Normalizovat na (dB SPL)Normalize to (dB) ReplayGain4Zpracovvaj se soubory...Processing files ... ReplayGain@Vyrovnn hlasitosti/NormalizaceReplayGain / Normalization ReplayGain<Vyrovnat hlasitost na (dB SPL)ReplayGain to (dB SPL) ReplayGain$Nastavit vae znovu Reset all ReplayGain,Nastavit vybran znovuReset selected ReplayGainPo et vlkenThreads number ReplayGainlinux-show-player-0.5.1/lisp/modules/replay_gain/i18n/replay_gain_cs.ts000066400000000000000000000034511323636106000261460ustar00rootroot00000000000000 ReplayGain ReplayGain / Normalization Vyrovnání hlasitosti/Normalizace Calculate Spočítat Reset all Nastavit vše znovu Reset selected Nastavit vybrané znovu Threads number Počet vláken Apply only to selected media Použít pouze na vybrané záznamy ReplayGain to (dB SPL) Vyrovnat hlasitost na (dB SPL) Normalize to (dB) Normalizovat na (dB SPL) Processing files ... Zpracovávají se soubory... linux-show-player-0.5.1/lisp/modules/replay_gain/i18n/replay_gain_en.qm000066400000000000000000000015351323636106000261330ustar00rootroot00000000000000 ReplayGain ReplayGain / Normalization ReplayGain / Normalization Calculate Calculate Reset all Reset all Reset selected Reset selected Threads number Threads number Apply only to selected media Apply only to selected media ReplayGain to (dB SPL) ReplayGain to (dB SPL) Normalize to (dB) Normalize to (dB) Processing files ... Processing files ... linux-show-player-0.5.1/lisp/modules/replay_gain/i18n/replay_gain_es.qm000066400000000000000000000016551323636106000261430ustar00rootroot00000000000000 ReplayGain ReplayGain / Normalization ReplayGain / Normalización Calculate Calcular Reset all Reestablecer todo Reset selected Reestablecer seleccionados Threads number Número de procesos Apply only to selected media Aplicar solamente a los media seleccionados ReplayGain to (dB SPL) ReplayGain a (dB SPL) Normalize to (dB) Normalizar a (dB) Processing files ... Procesando archivos ... linux-show-player-0.5.1/lisp/modules/replay_gain/i18n/replay_gain_fr.qm000066400000000000000000000017231323636106000261370ustar00rootroot00000000000000Gain de lecture / normalisationReplayGain / Normalization ReplayGain4Gain de lecture (dB SPL)ReplayGain to (dB SPL) ReplayGain$Tout rinitialiser Reset all ReplayGain4Rinitialiser la slctionReset selected ReplayGainNombre de filsThreads number ReplayGainlinux-show-player-0.5.1/lisp/modules/replay_gain/i18n/replay_gain_fr.ts000066400000000000000000000034641323636106000261540ustar00rootroot00000000000000 ReplayGain ReplayGain / Normalization Gain de lecture / normalisation Calculate Calculer Reset all Tout réinitialiser Reset selected Réinitialiser la sélection Threads number Nombre de fils Apply only to selected media N'appliquer qu'au média sélectionné ReplayGain to (dB SPL) Gain de lecture à (dB SPL) Normalize to (dB) Normaliser à (dB) Processing files ... Fichier en cours de traitement ... linux-show-player-0.5.1/lisp/modules/replay_gain/i18n/replay_gain_it.qm000066400000000000000000000015751323636106000261510ustar00rootroot00000000000000 ReplayGain ReplayGain / Normalization ReplayGain / Normalizzazione Calculate Calcola Reset all Resetta tutto Reset selected Resetta selezionati Threads number Numero di processi Apply only to selected media Applica solo alle traccie selezionate ReplayGain to (dB SPL) ReplayGain a (dB SPL) Normalize to (dB) Normalizza a (dB) Processing files ... Processando ... linux-show-player-0.5.1/lisp/modules/replay_gain/i18n/replay_gain_sl_SI.qm000066400000000000000000000016501323636106000265400ustar00rootroot00000000000000 ReplayGain ReplayGain / Normalization Ojačanje predvajanja / Normalizacija Calculate Izračunaj Reset all Ponastavi vse Reset selected Ponastavi izbrano Threads number Število niti Apply only to selected media Uveljavi samo na izbranih medijih ReplayGain to (dB SPL) Ojačaj predvajanje na (dB SPL) Normalize to (dB) Normaliziraj na (dB) Processing files ... Procesiram datoteke ... linux-show-player-0.5.1/lisp/modules/replay_gain/replay_gain.py000066400000000000000000000230751323636106000247100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import logging from concurrent.futures import ThreadPoolExecutor from concurrent.futures import as_completed as futures_completed from math import pow from threading import Thread, Lock import gi from lisp.ui.ui_utils import translate gi.require_version('Gst', '1.0') from gi.repository import Gst from PyQt5.QtWidgets import QMenu, QAction, QDialog from lisp.application import Application from lisp.core.action import Action from lisp.core.actions_handler import MainActionsHandler from lisp.core.module import Module from lisp.core.signal import Signal, Connection from lisp.cues.media_cue import MediaCue from lisp.ui.mainwindow import MainWindow from .gain_ui import GainUi, GainProgressDialog class GainAction(Action): __slots__ = ('__media_list', '__new_volumes', '__old_volumes') def __init__(self): self.__media_list = [] self.__new_volumes = [] self.__old_volumes = [] def add_media(self, media, new_volume): volume = media.element('Volume') if volume is not None: self.__media_list.append(media) self.__new_volumes.append(new_volume) self.__old_volumes.append(volume.normal_volume) def do(self): for n, media in enumerate(self.__media_list): volume = media.element('Volume') if volume is not None: volume.normal_volume = self.__new_volumes[n] def undo(self): for n, media in enumerate(self.__media_list): volume = media.element('Volume') if volume is not None: volume.normal_volume = self.__old_volumes[n] def redo(self): self.do() def log(self): return 'Replay gain volume adjusted' class GainMainThread(Thread): MAX_GAIN = 20 # dB def __init__(self, files, threads, mode, ref_level, norm_level): super().__init__() self.setDaemon(True) self._futures = {} self._running = False self._action = GainAction() # file -> media {'filename1': [media1, media2], 'filename2': [media3]} self.files = files self.threads = threads self.mode = mode self.ref_level = ref_level self.norm_level = norm_level self.on_progress = Signal() def stop(self): self._running = False for future in self._futures: self._futures[future].stop() future.cancel() def run(self): self._running = True with ThreadPoolExecutor(max_workers=self.threads) as executor: for file in self.files.keys(): gain = GstGain(file, self.ref_level) self._futures[executor.submit(gain.gain)] = gain for future in futures_completed(self._futures): if self._running: try: self._post_process(*future.result()) except Exception: # Call with the value stored in the GstGain object self._post_process(*self._futures[future].result) else: break if self._running: MainActionsHandler.do_action(self._action) else: logging.info('REPLY-GAIN:: Stopped by user') self.on_progress.emit(-1) self.on_progress.disconnect() def _post_process(self, gained, gain, peak, uri): if gained: if gain > self.MAX_GAIN: gain = self.MAX_GAIN if self.mode == 0: volume = min(1 / peak, pow(10, gain / 20)) else: # (self.mode == 1) volume = 1 / peak * pow(10, self.norm_level / 20) for media in self.files[uri]: self._action.add_media(media, volume) logging.info('REPLY-GAIN:: completed ' + uri) else: logging.error('REPLY-GAIN:: failed ' + uri) self.on_progress.emit(1) class ReplayGain(Module): Name = 'ReplayGain' def __init__(self): self._gain_thread = None # Entry in mainWindow menu self.menu = QMenu(translate('ReplayGain', 'ReplayGain / Normalization')) self.menu_action = MainWindow().menuTools.addMenu(self.menu) self.actionGain = QAction(MainWindow()) self.actionGain.triggered.connect(self.gain) self.actionGain.setText(translate('ReplayGain', 'Calculate')) self.menu.addAction(self.actionGain) self.actionReset = QAction(MainWindow()) self.actionReset.triggered.connect(self._reset_all) self.actionReset.setText(translate('ReplayGain', 'Reset all')) self.menu.addAction(self.actionReset) self.actionResetSelected = QAction(MainWindow()) self.actionResetSelected.triggered.connect(self._reset_selected) self.actionResetSelected.setText(translate('ReplayGain', 'Reset selected')) self.menu.addAction(self.actionResetSelected) def gain(self): gainUi = GainUi(MainWindow()) gainUi.exec_() if gainUi.result() == QDialog.Accepted: files = {} if gainUi.only_selected(): cues = Application().layout.get_selected_cues(MediaCue) else: cues = Application().cue_model.filter(MediaCue) for cue in cues: media = cue.media uri = media.input_uri() if uri is not None: if uri not in files: files[uri] = [media] else: files[uri].append(media) # Gain (main) thread self._gain_thread = GainMainThread(files, gainUi.threads(), gainUi.mode(), gainUi.ref_level(), gainUi.norm_level()) # Progress dialog self._progress = GainProgressDialog(len(files)) self._gain_thread.on_progress.connect(self._progress.on_progress, mode=Connection.QtQueued) self._progress.show() self._gain_thread.start() def stop(self): if self._gain_thread is not None: self._gain_thread.stop() def terminate(self): self.stop() MainWindow().menuTools.removeAction(self.menu_action) def _reset_all(self): self._reset(Application().cue_model.filter(MediaCue)) def _reset_selected(self): self._reset(Application().cue_model.filter(MediaCue)) def _reset(self, cues): action = GainAction() for cue in cues: action.add_media(cue.media, 1.0) MainActionsHandler.do_action(action) class GstGain: def __init__(self, uri, ref_level): self.__lock = Lock() self.uri = uri self.ref_level = ref_level self.result = (False, 0, 0, uri) self.gain_pipe = None # Create a pipeline with a fake audio output and get the gain levels def gain(self): pipe = 'uridecodebin uri="{0}" ! audioconvert ! rganalysis \ reference-level={1} ! fakesink'.format(self.uri, self.ref_level) self.gain_pipe = Gst.parse_launch(pipe) gain_bus = self.gain_pipe.get_bus() gain_bus.add_signal_watch() gain_bus.connect('message', self._on_message) logging.info('REPLY-GAIN:: started ' + str(self.uri)) self.gain_pipe.set_state(Gst.State.PLAYING) # Block here until EOS self.__lock.acquire(False) self.__lock.acquire() # Reset the pipe self.gain_pipe = None # Return the computation result return self.result def stop(self): if self.gain_pipe is not None: self.gain_pipe.send_event(Gst.Event.new_eos()) def _on_message(self, bus, message): try: if message.type == Gst.MessageType.EOS and self.__lock.locked(): self.__lock.release() elif message.type == Gst.MessageType.TAG: tags = message.parse_tag() tag = tags.get_double(Gst.TAG_TRACK_GAIN) peak = tags.get_double(Gst.TAG_TRACK_PEAK) if tag[0] and peak[0]: self.gain_pipe.set_state(Gst.State.NULL) self.result = (True, tag[1], peak[1], self.uri) if self.__lock.locked(): self.__lock.release() elif message.type == Gst.MessageType.ERROR: logging.debug('REPLY-GAIN:: ' + str(message.parse_error())) self.gain_pipe.set_state(Gst.State.NULL) if self.__lock.locked(): self.__lock.release() except Exception: if self.__lock.locked(): self.__lock.release() linux-show-player-0.5.1/lisp/modules/uri_changer/000077500000000000000000000000001323636106000220315ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/uri_changer/__init__.py000066400000000000000000000000431323636106000241370ustar00rootroot00000000000000from .uri_changer import UriChangerlinux-show-player-0.5.1/lisp/modules/uri_changer/i18n/000077500000000000000000000000001323636106000226105ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/modules/uri_changer/i18n/uri_changer_cs.qm000066400000000000000000000007101323636106000261200ustar00rootroot00000000000000 UriChanger Session URI change Změna URI sezení Current Nynější Replace Nahradit Session file Soubor se sezením Reload Nahrát znovu Error Chyba linux-show-player-0.5.1/lisp/modules/uri_changer/i18n/uri_changer_en.qm000066400000000000000000000006601323636106000261210ustar00rootroot00000000000000 UriChanger Session URI change Session URI change Current Current Replace Replace Session file Session file Reload Reload Error Error linux-show-player-0.5.1/lisp/modules/uri_changer/i18n/uri_changer_es.qm000066400000000000000000000007241323636106000261270ustar00rootroot00000000000000 UriChanger Session URI change Cambiar URI de la sesión Current Actual Replace Reemplazar Session file Archivo de la sesión Reload Recargar Error Error linux-show-player-0.5.1/lisp/modules/uri_changer/i18n/uri_changer_fr.qm000066400000000000000000000007401323636106000261250ustar00rootroot00000000000000 UriChanger Session URI change Modifier l'URI de la session Current Actuel Replace Remplacer Session file Fichier de la session Reload Recharger Error Erreur linux-show-player-0.5.1/lisp/modules/uri_changer/i18n/uri_changer_it.qm000066400000000000000000000007241323636106000261340ustar00rootroot00000000000000 UriChanger Session URI change Modifica URI di sessione Current Corrente Replace Sostituisci Session file File di sessione Reload Ricarica Error Errore linux-show-player-0.5.1/lisp/modules/uri_changer/i18n/uri_changer_sl_SI.qm000066400000000000000000000006771323636106000265400ustar00rootroot00000000000000 UriChanger Session URI change Sprememba URI seje Current Trenutno Replace Zamenjaj Session file Datoteka seje Reload Osveži Error Napaka linux-show-player-0.5.1/lisp/modules/uri_changer/json_utils.py000066400000000000000000000037241323636106000246020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . def json_deep_search(iterable, field): """Takes a JSON like data-structure and search for the occurrences of the given field/key. """ fields_found = [] if isinstance(iterable, dict): for key, value in iterable.items(): if key == field: fields_found.append(value) elif isinstance(value, (dict, list)): fields_found.extend(json_deep_search(value, field)) elif isinstance(iterable, list): for item in iterable: fields_found.extend(json_deep_search(item, field)) return fields_found def json_deep_replace(iterable, field, replace_function): """Takes a JSON like data-structure and replace the value for all the occurrences of the given field/key with the given replace-function. """ if isinstance(iterable, dict): for key, value in iterable.items(): if key == field: iterable[key] = replace_function(value) elif isinstance(value, (dict, list)): json_deep_replace(value, field, replace_function) elif isinstance(iterable, list): for item in iterable: json_deep_replace(item, field, replace_function) linux-show-player-0.5.1/lisp/modules/uri_changer/session.py000066400000000000000000000036721323636106000240760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import json from lisp.modules.uri_changer.json_utils import json_deep_search, \ json_deep_replace class Session: def __init__(self, file): self.file = file self.session = {} self.prefixes = set() self.load() def load(self): # Read the file content with open(self.file, mode='r', encoding='utf-8') as file: self.session = json.load(file) def analyze(self): self.prefixes = set() for uri in json_deep_search(self.session, 'uri'): prefix = '' # The uri should be like "protocol://some/url/here" for token in uri[uri.find('://')+4:].split('/')[:-1]: prefix += '/' + token self.prefixes.add(prefix) return self.prefixes def replace(self, old, new): def replace(value): if isinstance(value, str): return value.replace(old, new) return value json_deep_replace(self.session, 'uri', replace) def save(self, file): with open(file, mode='w', encoding='utf-8') as new_file: new_file.write(json.dumps(self.session, sort_keys=True, indent=4)) linux-show-player-0.5.1/lisp/modules/uri_changer/uri_changer.py000066400000000000000000000027421323636106000246760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtWidgets import QAction from lisp.core.module import Module from lisp.ui.mainwindow import MainWindow from lisp.ui.ui_utils import translate from .uri_changer_dialog import UriChangerDialog class UriChanger(Module): Name = 'URI Changer' def __init__(self): self.menuAction = QAction(translate('UriChanger', 'Session URI change'), MainWindow()) self.menuAction.triggered.connect(self.show_dialog) MainWindow().menuTools.addAction(self.menuAction) def show_dialog(self): dialog = UriChangerDialog(parent=MainWindow()) dialog.exec_() def terminate(self): MainWindow().menuTools.removeAction(self.menuAction) linux-show-player-0.5.1/lisp/modules/uri_changer/uri_changer_dialog.py000066400000000000000000000135341323636106000262160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import os from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, \ QPushButton, QGridLayout, QLabel, QListWidget, QDialogButtonBox, \ QFileDialog, QMessageBox from lisp.ui.ui_utils import translate from .session import Session class UriChangerDialog(QDialog): def __init__(self, **kwargs): super().__init__(**kwargs) self.resize(600, 300) self.setLayout(QVBoxLayout()) # FILE SELECT self.sessionLayout = QHBoxLayout() self.layout().addLayout(self.sessionLayout) self.sessionFileEdit = QLineEdit(self) self.sessionFileEdit.editingFinished.connect(self.session_init) self.sessionLayout.addWidget(self.sessionFileEdit) self.sessionFileSelect = QPushButton(self) self.sessionFileSelect.clicked.connect(self.session_select) self.sessionLayout.addWidget(self.sessionFileSelect) self.sessionReload = QPushButton(self) self.sessionReload.setIcon(QIcon.fromTheme('view-refresh')) self.sessionReload.clicked.connect(self.session_init) self.sessionLayout.addWidget(self.sessionReload) # REPLACE self.replaceLayout = QGridLayout() self.layout().addLayout(self.replaceLayout) self.currentLabel = QLabel(self) self.currentLabel.setAlignment(Qt.AlignCenter) self.replaceLayout.addWidget(self.currentLabel, 0, 0) self.replaceLabel = QLabel(self) self.replaceLabel.setAlignment(Qt.AlignCenter) self.replaceLayout.addWidget(self.replaceLabel, 0, 1) self.prefixList = QListWidget(self) self.prefixList.currentTextChanged.connect(self.prefix_changed) self.replaceLayout.addWidget(self.prefixList, 1, 0, 2, 1) self.newPrefixLayout = QVBoxLayout() self.replaceLayout.addLayout(self.newPrefixLayout, 1, 1) self.prefixEdit = QLineEdit(self) self.newPrefixLayout.addWidget(self.prefixEdit) self.replaceLayout.setAlignment(self.prefixEdit, Qt.AlignTop) self.replaceApply = QPushButton(self) self.replaceApply.clicked.connect(self.session_replace) self.newPrefixLayout.addWidget(self.replaceApply) self.newPrefixLayout.setAlignment(self.replaceApply, Qt.AlignRight) self.buttons = QDialogButtonBox(self) self.buttons.setStandardButtons(QDialogButtonBox.Cancel) self.buttons.rejected.connect(self.reject) self.layout().addWidget(self.buttons) self.saveButton = self.buttons.addButton(QDialogButtonBox.Save) self.saveButton.clicked.connect(self.session_save) self.retranslateUi() self.session = None def retranslateUi(self): self.currentLabel.setText(translate('UriChanger', 'Current')) self.replaceLabel.setText(translate('UriChanger', 'Replace')) self.sessionFileSelect.setText(translate('UriChanger', 'Session file')) self.sessionReload.setToolTip(translate('UriChanger', 'Reload')) self.replaceApply.setText(translate('UriChanger', 'Replace')) def prefix_changed(self, prefix): if self.prefixEdit.text() == '': self.prefixEdit.setText(prefix) else: # Search if the prefixEdit text is one of the listed prefixes lower = 0 upper = self.prefixList.count() - 1 search = self.prefixEdit.text() while lower <= upper: middle = (lower + upper) // 2 item = self.prefixList.item(middle).text() if item < search: lower = middle + 1 elif item > search: upper = middle - 1 else: self.prefixEdit.setText(prefix) break def session_select(self): file, _ = QFileDialog.getOpenFileName(self, filter='*.lsp', directory=os.getenv('HOME')) if file != '': self.sessionFileEdit.setText(file) self.session_init() def session_analyze(self): self.prefixList.clear() self.prefixList.addItems(self.session.analyze()) self.prefixList.sortItems() def session_init(self): file = self.sessionFileEdit.text() if os.path.exists(file): self.session = Session(file) self.session_analyze() elif file.strip() != '': QMessageBox.critical(self, translate('UriChanger', 'Error'), translate('UriChanger', 'Session file "{}" not found'.format( file))) def session_replace(self): self.session.replace(self.prefixList.currentItem().text(), self.prefixEdit.text()) self.session_analyze() def session_save(self): file, ok = QFileDialog.getSaveFileName(self, filter='*.lsp', directory=os.getenv('HOME')) if ok: self.session.save(file) self.accept() linux-show-player-0.5.1/lisp/plugins/000077500000000000000000000000001323636106000175545ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/__init__.py000066400000000000000000000057741323636106000217020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import os from lisp.core.loading import load_classes from lisp.ui import elogging __PLUGINS = {} def load_plugins(): """Load available plugins.""" for name, plugin in load_classes(__package__, os.path.dirname(__file__)): try: __PLUGINS[name] = plugin() elogging.debug('PLUGINS: Loaded "{0}"'.format(name)) except Exception as e: elogging.exception('PLUGINS: Failed "{0}" load'.format(name), e) def translations(): base_path = os.path.dirname(os.path.realpath(__file__)) for module in next(os.walk(base_path))[1]: i18n_dir = os.path.join(base_path, module, 'i18n') if os.path.exists(i18n_dir): yield os.path.join(i18n_dir, module) def init_plugins(): """Initialize all the plugins.""" failed = [] for plugin in __PLUGINS: try: __PLUGINS[plugin].init() elogging.debug('PLUGINS: Initialized "{0}"'.format(plugin)) except Exception as e: failed.append(plugin) elogging.exception('PLUGINS: Failed "{0}" init'.format(plugin), e) for plugin in failed: __PLUGINS.pop(plugin) def reset_plugins(): """Resets all the plugins.""" for plugin in __PLUGINS: try: __PLUGINS[plugin].reset() elogging.debug('PLUGINS: Reset "{0}"'.format(plugin)) except Exception as e: elogging.exception('PLUGINS: Failed "{0}" reset'.format(plugin), e) def set_plugins_settings(settings): for plugin in __PLUGINS.values(): if plugin.Name in settings: try: plugin.load_settings(settings[plugin.Name]) except Exception as e: elogging.exception('PLUGINS: Failed "{0}" settings load' .format(plugin.Name), e) def get_plugin_settings(): plugins_settings = {} for plugin in __PLUGINS.values(): try: settings = plugin.settings() if settings is not None and len(settings) > 0: plugins_settings[plugin.Name] = settings except Exception as e: elogging.exception('PLUGINS: Failed "{0}" settings retrieve' .format(plugin.Name), e) return plugins_settings linux-show-player-0.5.1/lisp/plugins/controller/000077500000000000000000000000001323636106000217375ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/controller/__init__.py000066400000000000000000000000421323636106000240440ustar00rootroot00000000000000from .controller import Controllerlinux-show-player-0.5.1/lisp/plugins/controller/controller.py000066400000000000000000000071271323636106000245030ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.application import Application from lisp.core.has_properties import Property from lisp.core.plugin import Plugin from lisp.cues.cue import Cue, CueAction from lisp.plugins.controller import protocols from lisp.plugins.controller.controller_settings import ControllerSettings from lisp.ui.settings.cue_settings import CueSettingsRegistry class Controller(Plugin): Name = 'Controller' def __init__(self): super().__init__() self.__map = {} self.__actions_map = {} self.__protocols = {} # Register a new Cue property to store settings Cue.register_property('controller', Property(default={})) # Listen cue_model changes Application().cue_model.item_added.connect(self.__cue_added) Application().cue_model.item_removed.connect(self.__cue_removed) # Register settings-page CueSettingsRegistry().add_item(ControllerSettings) # Load available protocols self.__load_protocols() def init(self): for protocol in self.__protocols.values(): protocol.init() def reset(self): self.__map.clear() self.__actions_map.clear() for protocol in self.__protocols.values(): protocol.reset() def cue_changed(self, cue, property_name, value): if property_name == 'controller': self.delete_from_map(cue) for protocol in self.__protocols: for key, action in value.get(protocol, []): if key not in self.__map: self.__map[key] = set() self.__map[key].add(cue) self.__actions_map[(key, cue)] = CueAction(action) def delete_from_map(self, cue): for key in self.__map: self.__map[key].discard(cue) self.__actions_map.pop((key, cue), None) def perform_action(self, key): for cue in self.__map.get(key, []): cue.execute(self.__actions_map[(key, cue)]) def __cue_added(self, cue): cue.property_changed.connect(self.cue_changed) self.cue_changed(cue, 'controller', cue.controller) def __cue_removed(self, cue): cue.property_changed.disconnect(self.cue_changed) self.delete_from_map(cue) def __load_protocols(self): protocols.load() for protocol_class in protocols.Protocols: try: protocol = protocol_class() protocol.protocol_event.connect(self.perform_action) self.__protocols[protocol_class.__name__.lower()] = protocol except Exception as e: import logging import traceback logging.error('CONTROLLER: cannot setup protocol "{}"'.format( protocol_class.__name__)) logging.debug(traceback.format_exc()) linux-show-player-0.5.1/lisp/plugins/controller/controller_settings.py000066400000000000000000000041461323636106000264210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QHBoxLayout, QTabWidget from lisp.plugins.controller import protocols from lisp.ui.settings.settings_page import CueSettingsPage from lisp.ui.ui_utils import translate class ControllerSettings(CueSettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Cue Control') def __init__(self, cue_class, **kwargs): super().__init__(cue_class, **kwargs) self.setLayout(QHBoxLayout()) self._pages = [] self.tabWidget = QTabWidget(self) self.layout().addWidget(self.tabWidget) for page in protocols.ProtocolsSettingsPages: page_widget = page(cue_class, parent=self) self.tabWidget.addTab(page_widget, translate('SettingsPageName', page.Name)) self._pages.append(page_widget) self.tabWidget.setCurrentIndex(0) def enable_check(self, enabled): for page in self._pages: page.enable_check(enabled) def get_settings(self): settings = {} for page in self._pages: settings.update(page.get_settings()) return {'controller': settings} def load_settings(self, settings): settings = settings.get('controller', {}) for page in self._pages: page.load_settings(settings) linux-show-player-0.5.1/lisp/plugins/controller/i18n/000077500000000000000000000000001323636106000225165ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/controller/i18n/controller_cs.qm000066400000000000000000000026201323636106000257250ustar00rootroot00000000000000V eDz^z^BFzBCy  TU / s> ]X gi innostActionControllerKeySettingsKlvesaKeyControllerKeySettings"Klvesov zkratky ShortcutsControllerKeySettings innostActionControllerMidiSettingsZachytvnCaptureControllerMidiSettings KanlChannelControllerMidiSettings(Filtr (nota vypnuta"Filter "note off"ControllerMidiSettings(Filtr "nota zapnuta"Filter "note on"ControllerMidiSettings8Naslouch se zprvm MIDI...Listening MIDI messages ...ControllerMidiSettingsMIDIMIDIControllerMidiSettingsNotaNoteControllerMidiSettingsTypTypeControllerMidiSettings PYidatAddControllerSettingsOdstranitRemoveControllerSettings Ovldn nar~ky Cue ControlSettingsPageName"Klvesov zkratkyKeyboard ShortcutsSettingsPageNameOvldn MIDI MIDI ControlsSettingsPageNamelinux-show-player-0.5.1/lisp/plugins/controller/i18n/controller_cs.ts000066400000000000000000000064301323636106000257410ustar00rootroot00000000000000 ControllerKeySettings Key Klávesa Action Činnost Shortcuts Klávesové zkratky ControllerMidiSettings MIDI MIDI Type Typ Channel Kanál Note Nota Action Činnost Filter "note on" Filtr "nota zapnuta" Filter "note off" Filtr (nota vypnuta" Capture Zachytávání Listening MIDI messages ... Naslouchá se zprávám MIDI... ControllerSettings Add Přidat Remove Odstranit SettingsPageName Cue Control Ovládání narážky MIDI Controls Ovládání MIDI Keyboard Shortcuts Klávesové zkratky linux-show-player-0.5.1/lisp/plugins/controller/i18n/controller_en.qm000066400000000000000000000025121323636106000257220ustar00rootroot00000000000000 ControllerKeySettings Key Key Action Action Shortcuts Shortcuts ControllerMidiSettings MIDI MIDI Type Type Channel Channel Note Note Action Action Filter "note on" Filter "note on" Filter "note off" Filter "note off" Capture Capture Listening MIDI messages ... Listening MIDI messages ... ControllerSettings Add Add Remove Remove SettingsPageName Cue Control Cue Control MIDI Controls MIDI Controls Keyboard Shortcuts Keyboard Shortcuts linux-show-player-0.5.1/lisp/plugins/controller/i18n/controller_es.qm000066400000000000000000000025601323636106000257320ustar00rootroot00000000000000 gi AccinActionControllerKeySettings TeclaKeyControllerKeySettings"Atajos de teclado ShortcutsControllerKeySettings AccinActionControllerMidiSettingsCapturaCaptureControllerMidiSettings CanalChannelControllerMidiSettings$Filtrar "Nota OFF"Filter "note off"ControllerMidiSettings"Filtrar "Nota ON"Filter "note on"ControllerMidiSettings8Escuchando mensajes MIDI ...Listening MIDI messages ...ControllerMidiSettingsMIDIMIDIControllerMidiSettingsNotaNoteControllerMidiSettingsTipoTypeControllerMidiSettings AadirAddControllerSettingsEliminarRemoveControllerSettingsControl de Cue Cue ControlSettingsPageName"Atajos de tecladoKeyboard ShortcutsSettingsPageNameControles MIDI MIDI ControlsSettingsPageNamelinux-show-player-0.5.1/lisp/plugins/controller/i18n/controller_es.ts000066400000000000000000000063731323636106000257510ustar00rootroot00000000000000 ControllerKeySettings Key Tecla Action Acción Shortcuts Atajos de teclado ControllerMidiSettings MIDI MIDI Type Tipo Channel Canal Note Nota Action Acción Filter "note on" Filtrar "Nota ON" Filter "note off" Filtrar "Nota OFF" Capture Captura Listening MIDI messages ... Escuchando mensajes MIDI ... ControllerSettings Add Añadir Remove Eliminar SettingsPageName Cue Control Control de Cue MIDI Controls Controles MIDI Keyboard Shortcuts Atajos de teclado linux-show-player-0.5.1/lisp/plugins/controller/i18n/controller_fr.qm000066400000000000000000000025761323636106000257410ustar00rootroot00000000000000 ControllerKeySettings Key Touche Action Action Shortcuts Raccourcis ControllerMidiSettings MIDI MIDI Type Type Channel Canal Note Note Action Action Filter "note on" Filtrer les "note on" Filter "note off" Filtrer les "note off" Capture Capture Listening MIDI messages ... Écoute des messages MIDI ... ControllerSettings Add Ajouter Remove Retirer SettingsPageName Cue Control Contrôle de la cue MIDI Controls Contrôles MIDI Keyboard Shortcuts Raccourcis-clavier linux-show-player-0.5.1/lisp/plugins/controller/i18n/controller_it.qm000066400000000000000000000025501323636106000257360ustar00rootroot00000000000000 ControllerKeySettings Key Tasto Action Azione Shortcuts Scorciatoie ControllerMidiSettings MIDI MIDI Type Tipo Channel Canale Note Nota Action Azione Filter "note on" Filtro "note on" Filter "note off" Filtro "note off" Capture Cattura Listening MIDI messages ... Ascoltando messaggi MIDI ... ControllerSettings Add Aggiungi Remove Rimuovi SettingsPageName Cue Control Controllo Cue MIDI Controls Controlli MIDI Keyboard Shortcuts Scorciatoie Tastiera linux-show-player-0.5.1/lisp/plugins/controller/i18n/controller_sl_SI.qm000066400000000000000000000026251323636106000263360ustar00rootroot00000000000000 ControllerKeySettings Key Ključ Action Akcija Shortcuts Bližnjica ControllerMidiSettings MIDI MIDI Type Tip Channel Kanal Note Beležka Action Akcija Filter "note on" Filter "Beležka aktivna" Filter "note off" Filter "Beležka neaktivna" Capture Zajemi Listening MIDI messages ... Poslušam MIDI sporočila ... ControllerSettings Add Dodaj Remove Odstrani SettingsPageName Cue Control Krmiljenje vrste MIDI Controls Krmiljenje MIDI Keyboard Shortcuts Bližnjice na tipkovnici linux-show-player-0.5.1/lisp/plugins/controller/protocols/000077500000000000000000000000001323636106000237635ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/controller/protocols/__init__.py000066400000000000000000000023601323636106000260750ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from os.path import dirname from lisp.core.loading import load_classes from lisp.ui.settings.settings_page import SettingsPage Protocols = [] ProtocolsSettingsPages = [] def load(): for _, protocol in load_classes(__package__, dirname(__file__), pre=('', ''), suf=('', 'Settings',)): if issubclass(protocol, SettingsPage): ProtocolsSettingsPages.append(protocol) else: Protocols.append(protocol) linux-show-player-0.5.1/lisp/plugins/controller/protocols/keyboard.py000066400000000000000000000111761323636106000261430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QGroupBox, QGridLayout, QTableView, QHeaderView, \ QPushButton, QVBoxLayout from lisp.application import Application from lisp.plugins.controller.protocols.protocol import Protocol from lisp.ui.qdelegates import ComboBoxDelegate, LineEditDelegate, \ CueActionDelegate from lisp.ui.qmodels import SimpleTableModel from lisp.ui.settings.settings_page import CueSettingsPage from lisp.ui.ui_utils import translate class Keyboard(Protocol): def init(self): Application().layout.key_pressed.connect(self.__key_pressed) def reset(self): Application().layout.key_pressed.disconnect(self.__key_pressed) def __key_pressed(self, key_event): if not key_event.isAutoRepeat() and key_event.text() != '': self.protocol_event.emit(key_event.text()) class KeyboardSettings(CueSettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Keyboard Shortcuts') def __init__(self, cue_class, **kwargs): super().__init__(cue_class, **kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.keyGroup = QGroupBox(self) self.keyGroup.setLayout(QGridLayout()) self.layout().addWidget(self.keyGroup) self.keyboardModel = SimpleTableModel([ translate('ControllerKeySettings', 'Key'), translate('ControllerKeySettings', 'Action')]) self.keyboardView = KeyboardView(cue_class, parent=self.keyGroup) self.keyboardView.setModel(self.keyboardModel) self.keyGroup.layout().addWidget(self.keyboardView, 0, 0, 1, 2) self.addButton = QPushButton(self.keyGroup) self.addButton.clicked.connect(self.__new_key) self.keyGroup.layout().addWidget(self.addButton, 1, 0) self.removeButton = QPushButton(self.keyGroup) self.removeButton.clicked.connect(self.__remove_key) self.keyGroup.layout().addWidget(self.removeButton, 1, 1) self.retranslateUi() def retranslateUi(self): self.keyGroup.setTitle(translate('ControllerKeySettings', 'Shortcuts')) self.addButton.setText(translate('ControllerSettings', 'Add')) self.removeButton.setText(translate('ControllerSettings', 'Remove')) def enable_check(self, enabled): self.keyGroup.setCheckable(enabled) self.keyGroup.setChecked(False) def get_settings(self): settings = {} if not (self.keyGroup.isCheckable() and not self.keyGroup.isChecked()): settings['keyboard'] = self.keyboardModel.rows return settings def load_settings(self, settings): for key, action in settings.get('keyboard', []): self.keyboardModel.appendRow(key, action) def __new_key(self): self.keyboardModel.appendRow('', self._cue_class.CueActions[0].name) def __remove_key(self): self.keyboardModel.removeRow(self.keyboardView.currentIndex().row()) class KeyboardView(QTableView): def __init__(self, cue_class, **kwargs): super().__init__(**kwargs) self.setSelectionBehavior(QTableView.SelectRows) self.setSelectionMode(QTableView.SingleSelection) self.setShowGrid(False) self.setAlternatingRowColors(True) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.horizontalHeader().setHighlightSections(False) self.verticalHeader().sectionResizeMode(QHeaderView.Fixed) self.verticalHeader().setDefaultSectionSize(24) self.verticalHeader().setHighlightSections(False) self.delegates = [LineEditDelegate(max_length=1), CueActionDelegate(cue_class=cue_class, mode=CueActionDelegate.Mode.Name)] for column, delegate in enumerate(self.delegates): self.setItemDelegateForColumn(column, delegate) linux-show-player-0.5.1/lisp/plugins/controller/protocols/midi.py000066400000000000000000000164711323636106000252700ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QGroupBox, QPushButton, QComboBox, QVBoxLayout, \ QMessageBox, QTableView, QTableWidget, QHeaderView, QGridLayout from lisp.modules import check_module from lisp.modules.midi.midi_input import MIDIInput from lisp.plugins.controller.protocols.protocol import Protocol from lisp.ui.qdelegates import ComboBoxDelegate, SpinBoxDelegate, \ CueActionDelegate from lisp.ui.qmodels import SimpleTableModel from lisp.ui.settings.settings_page import CueSettingsPage from lisp.ui.ui_utils import translate class Midi(Protocol): def __init__(self): super().__init__() if check_module('midi'): midi_input = MIDIInput() if not midi_input.is_open(): midi_input.open() MIDIInput().new_message.connect(self.__new_message) def __new_message(self, message): if message.type == 'note_on' or message.type == 'note_off': self.protocol_event.emit(Midi.str_from_message(message)) @staticmethod def str_from_message(message): return Midi.str_from_values(message.type, message.channel, message.note) @staticmethod def str_from_values(m_type, channel, note): return '{} {} {}'.format(m_type, channel, note) @staticmethod def from_string(message_str): m_type, channel, note = message_str.split() return m_type, int(channel), int(note) class MidiSettings(CueSettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'MIDI Controls') def __init__(self, cue_class, **kwargs): super().__init__(cue_class, **kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.midiGroup = QGroupBox(self) self.midiGroup.setTitle(translate('ControllerMidiSettings', 'MIDI')) # self.midiGroup.setEnabled(check_module('midi')) self.midiGroup.setLayout(QGridLayout()) self.layout().addWidget(self.midiGroup) self.midiModel = SimpleTableModel([ translate('ControllerMidiSettings', 'Type'), translate('ControllerMidiSettings', 'Channel'), translate('ControllerMidiSettings', 'Note'), translate('ControllerMidiSettings', 'Action')]) self.midiView = MidiView(cue_class, parent=self.midiGroup) self.midiView.setModel(self.midiModel) self.midiGroup.layout().addWidget(self.midiView, 0, 0, 1, 2) self.addButton = QPushButton(self.midiGroup) self.addButton.clicked.connect(self.__new_message) self.midiGroup.layout().addWidget(self.addButton, 1, 0) self.removeButton = QPushButton(self.midiGroup) self.removeButton.clicked.connect(self.__remove_message) self.midiGroup.layout().addWidget(self.removeButton, 1, 1) self.midiCapture = QPushButton(self.midiGroup) self.midiCapture.clicked.connect(self.capture_message) self.midiGroup.layout().addWidget(self.midiCapture, 2, 0) self.msgTypeCombo = QComboBox(self.midiGroup) self.msgTypeCombo.addItem( translate('ControllerMidiSettings', 'Filter "note on"')) self.msgTypeCombo.setItemData(0, 'note_on', Qt.UserRole) self.msgTypeCombo.addItem( translate('ControllerMidiSettings', 'Filter "note off"')) self.msgTypeCombo.setItemData(1, 'note_off', Qt.UserRole) self.midiGroup.layout().addWidget(self.msgTypeCombo, 2, 1) self.retranslateUi() self._default_action = self._cue_class.CueActions[0].name def retranslateUi(self): self.addButton.setText(translate('ControllerSettings', 'Add')) self.removeButton.setText(translate('ControllerSettings', 'Remove')) self.midiCapture.setText(translate('ControllerMidiSettings', 'Capture')) def enable_check(self, enabled): self.midiGroup.setCheckable(enabled) self.midiGroup.setChecked(False) def get_settings(self): settings = {} checkable = self.midiGroup.isCheckable() if not (checkable and not self.midiGroup.isChecked()): messages = [] for row in self.midiModel.rows: message = Midi.str_from_values(row[0], row[1]-1, row[2]) messages.append((message, row[-1])) if messages: settings['midi'] = messages return settings def load_settings(self, settings): if 'midi' in settings: for options in settings['midi']: m_type, channel, note = Midi.from_string(options[0]) self.midiModel.appendRow(m_type, channel+1, note, options[1]) def capture_message(self): handler = MIDIInput() handler.alternate_mode = True handler.new_message_alt.connect(self.__add_message) QMessageBox.information(self, '', translate('ControllerMidiSettings', 'Listening MIDI messages ...')) handler.new_message_alt.disconnect(self.__add_message) handler.alternate_mode = False def __add_message(self, msg): if self.msgTypeCombo.currentData(Qt.UserRole) == msg.type: self.midiModel.appendRow(msg.type, msg.channel+1, msg.note, self._default_action) def __new_message(self): message_type = self.msgTypeCombo.currentData(Qt.UserRole) self.midiModel.appendRow(message_type, 1, 0, self._default_action) def __remove_message(self): self.midiModel.removeRow(self.midiView.currentIndex().row()) class MidiView(QTableView): def __init__(self, cue_class, **kwargs): super().__init__(**kwargs) self.delegates = [ ComboBoxDelegate(options=['note_on', 'note_off']), SpinBoxDelegate(minimum=1, maximum=16), SpinBoxDelegate(minimum=0, maximum=127), CueActionDelegate(cue_class=cue_class, mode=CueActionDelegate.Mode.Name) ] self.setSelectionBehavior(QTableWidget.SelectRows) self.setSelectionMode(QTableView.SingleSelection) self.setShowGrid(False) self.setAlternatingRowColors(True) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.horizontalHeader().setHighlightSections(False) self.verticalHeader().sectionResizeMode(QHeaderView.Fixed) self.verticalHeader().setDefaultSectionSize(24) self.verticalHeader().setHighlightSections(False) for column, delegate in enumerate(self.delegates): self.setItemDelegateForColumn(column, delegate) linux-show-player-0.5.1/lisp/plugins/controller/protocols/protocol.py000066400000000000000000000027371323636106000262070ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.core.signal import Signal class Protocol: """Base interface for protocols. The init() and reset() functions are called when the respective functions of the main-plugin are called. When an event that can trigger a cue is "detected", the protocol_event signal should be emitted with the event representation. To be loaded correctly the class should follow the ClassesLoader specification. To define the settings, only define a class with same name plus 'Settings' as suffix (e.g. Protocol -> ProtocolSettings), in the same file. """ def __init__(self): self.protocol_event = Signal() def init(self): pass def reset(self): pass linux-show-player-0.5.1/lisp/plugins/synchronizer/000077500000000000000000000000001323636106000223115ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/synchronizer/__init__.py000066400000000000000000000000461323636106000244220ustar00rootroot00000000000000from .synchronizer import Synchronizerlinux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/000077500000000000000000000000001323636106000230705ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_cs.qm000066400000000000000000000026571323636106000266630ustar00rootroot00000000000000V -) c1D2i AdresaAddressSyncPeerDialogJi~ pYipojenAlready connectedSyncPeerDialog,Nelze pYidat protjaekCannot add peerSyncPeerDialog"Objevit protjakyDiscover peersSyncPeerDialog ChybaErrorSyncPeerDialog6Spravovat spojen protjakyManage connected peersSyncPeerDialog,PYidat protjaek ru nManually add a peerSyncPeerDialog Adresa protjakuPeer IPSyncPeerDialog6Odstranit vaechny protjakyRemove all peersSyncPeerDialog6Odstranit vybran protjaekRemove selected peerSyncPeerDialog0Objevuj se protjaci...Discovering peers ... Synchronizer6Spravovat spojen protjakyManage connected peers Synchronizer.Ukzat vaai adresu (IP) Show your IP SynchronizerSeYzenSynchronization Synchronizer"Vaae adresu (IP): Your IP is: Synchronizerlinux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_cs.ts000066400000000000000000000056611323636106000266720ustar00rootroot00000000000000 SyncPeerDialog Manage connected peers Spravovat spojené protějšky Discover peers Objevit protějšky Manually add a peer Přidat protějšek ručně Remove selected peer Odstranit vybraný protějšek Remove all peers Odstranit všechny protějšky Address Adresa Peer IP Adresa protějšku Error Chyba Already connected Již připojen Cannot add peer Nelze přidat protějšek Synchronizer Synchronization Seřízení Manage connected peers Spravovat spojené protějšky Show your IP Ukázat vaši adresu (IP) Your IP is: Vaše adresu (IP): Discovering peers ... Objevují se protějšci... linux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_en.qm000066400000000000000000000024711323636106000266520ustar00rootroot00000000000000 - c1D2iAddressAddressSyncPeerDialog"Already connectedAlready connectedSyncPeerDialogCannot add peerCannot add peerSyncPeerDialogDiscover peersDiscover peersSyncPeerDialog ErrorErrorSyncPeerDialog,Manage connected peersManage connected peersSyncPeerDialog&Manually add a peerManually add a peerSyncPeerDialogPeer IPPeer IPSyncPeerDialog Remove all peersRemove all peersSyncPeerDialog(Remove selected peerRemove selected peerSyncPeerDialog*Discovering peers ...Discovering peers ... Synchronizer,Manage connected peersManage connected peers SynchronizerShow your IP Show your IP SynchronizerSynchronizationSynchronization SynchronizerYour IP is: Your IP is: Synchronizerlinux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_en.ts000066400000000000000000000055251323636106000266660ustar00rootroot00000000000000 SyncPeerDialog Manage connected peers Manage connected peers Discover peers Discover peers Manually add a peer Manually add a peer Remove selected peer Remove selected peer Remove all peers Remove all peers Address Address Peer IP Peer IP Error Error Already connected Already connected Cannot add peer Cannot add peer Synchronizer Synchronization Synchronization Manage connected peers Manage connected peers Show your IP Show your IP Your IP is: Your IP is: Discovering peers ... Discovering peers ... linux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_es.qm000066400000000000000000000026211323636106000266540ustar00rootroot00000000000000Z -' c1D2iDireccinAddressSyncPeerDialog"Ya est conectadoAlready connectedSyncPeerDialog*Imposible aadir peerCannot add peerSyncPeerDialogDescubrir peersDiscover peersSyncPeerDialog ErrorErrorSyncPeerDialog4Gestionar peers conectadosManage connected peersSyncPeerDialog4Aadir un peer manualmenteManually add a peerSyncPeerDialogIP del peerPeer IPSyncPeerDialog0Eliminar todos los peersRemove all peersSyncPeerDialog:Eliminar el peer seleccionadoRemove selected peerSyncPeerDialog,Descubriendo peers ...Discovering peers ... Synchronizer4Gestionar peers conectadosManage connected peers SynchronizerMostrar su IP Show your IP SynchronizerSincronizacinSynchronization SynchronizerSu IP es: Your IP is: Synchronizerlinux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_es.ts000066400000000000000000000056061323636106000266730ustar00rootroot00000000000000 SyncPeerDialog Manage connected peers Gestionar peers conectados Discover peers Descubrir peers Manually add a peer Añadir un peer manualmente Remove selected peer Eliminar el peer seleccionado Remove all peers Eliminar todos los peers Address Dirección Peer IP IP del peer Error Error Already connected Ya está conectado Cannot add peer Imposible añadir peer Synchronizer Synchronization Sincronización Manage connected peers Gestionar peers conectados Show your IP Mostrar su IP Your IP is: Su IP es: Discovering peers ... Descubriendo peers ... linux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_fr.qm000066400000000000000000000027271323636106000266630ustar00rootroot00000000000000 -W c1D2i>AdressesAddressSyncPeerDialogDj connectAlready connectedSyncPeerDialog<Impossible d'ajouter une paireCannot add peerSyncPeerDialog(Dcouvrir les pairesDiscover peersSyncPeerDialog ErreurErrorSyncPeerDialog:Gestion des paires connectesManage connected peersSyncPeerDialog:Ajouter manuelement une paireManually add a peerSyncPeerDialogIP de la pairePeer IPSyncPeerDialog2Retirer toutes les pairesRemove all peersSyncPeerDialog:Retirer la paire slctionneRemove selected peerSyncPeerDialog2Dcouverte des paires ...Discovering peers ... Synchronizer6Grer les paiers connectesManage connected peers Synchronizer"Afficher votre IP Show your IP SynchronizerSynchronisationSynchronization SynchronizerVotre IP est :  Your IP is: Synchronizerlinux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_fr.ts000066400000000000000000000056651323636106000267000ustar00rootroot00000000000000 SyncPeerDialog Manage connected peers Gestion des paires connectées Discover peers Découvrir les paires Manually add a peer Ajouter manuellement une paire Remove selected peer Retirer la paire sélectionnée Remove all peers Retirer toutes les paires Address Adresses Peer IP IP de la paire Error Erreur Already connected Déjà connecté Cannot add peer Impossible d'ajouter une paire Synchronizer Synchronization Synchronisation Manage connected peers Gérer les paires connectées Show your IP Afficher votre IP Your IP is: Votre IP est : Discovering peers ... Découverte des paires ... linux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_it.qm000066400000000000000000000025771323636106000266730ustar00rootroot00000000000000P~ z>@ - c1D2iIndirizzoAddressSyncPeerDialogGi connessoAlready connectedSyncPeerDialog<Impossibile aggiungere il peerCannot add peerSyncPeerDialogRicerca peerDiscover peersSyncPeerDialog ErrorErrorSyncPeerDialog,Gestisci peer connessiManage connected peersSyncPeerDialog2Aggiungi peer manualmenteManually add a peerSyncPeerDialogIP del peerPeer IPSyncPeerDialog(Rimuovi tutti i peerRemove all peersSyncPeerDialog0Rimuovi peer selezionatoRemove selected peerSyncPeerDialog2Ricerca peer in corso ...Discovering peers ... Synchronizer,Gestisci peer connessiManage connected peers SynchronizerVisualizza IP Show your IP Synchronizer SincronizzazioneSynchronization SynchronizerIl tuo IP : Your IP is: Synchronizerlinux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_it.ts000066400000000000000000000055721323636106000267020ustar00rootroot00000000000000 SyncPeerDialog Manage connected peers Gestisci peer connessi Discover peers Ricerca peer Manually add a peer Aggiungi peer manualmente Remove selected peer Rimuovi peer selezionato Remove all peers Rimuovi tutti i peer Address Indirizzo Peer IP IP del peer Error Error Already connected Già connesso Cannot add peer Impossibile aggiungere il peer Synchronizer Synchronization Sincronizzazione Manage connected peers Gestisci peer connessi Show your IP Visualizza IP Your IP is: Il tuo IP è: Discovering peers ... Ricerca peer in corso ... linux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_sl_SI.qm000066400000000000000000000026101323636106000272540ustar00rootroot00000000000000> - c1D2~i NaslovAddressSyncPeerDialog}e povezanAlready connectedSyncPeerDialog0Ne morem dodat sole~nikaCannot add peerSyncPeerDialog Odkrij sole~nikeDiscover peersSyncPeerDialog NapakaErrorSyncPeerDialog8Upravljaj povezane sole~nikeManage connected peersSyncPeerDialog(Ro no dodaj sole~nikManually add a peerSyncPeerDialogIP sole~nikaPeer IPSyncPeerDialog,Odstrani vse sole~nikeRemove all peersSyncPeerDialog2Odstrani izbrani sole~nikRemove selected peerSyncPeerDialog,Odkrivam sole~nike ...Discovering peers ... Synchronizer8Upravljaj povezane sole~nikeManage connected peers SynchronizerPrika~i tvoj IP Show your IP SynchronizerSinhronizacijaSynchronization SynchronizerTvoj IP je: Your IP is: Synchronizer !!$linux-show-player-0.5.1/lisp/plugins/synchronizer/i18n/synchronizer_sl_SI.ts000066400000000000000000000056101323636106000272700ustar00rootroot00000000000000 SyncPeerDialog Manage connected peers Upravljaj povezane soležnike Discover peers Odkrij soležnike Manually add a peer Ročno dodaj soležnik Remove selected peer Odstrani izbrani soležnik Remove all peers Odstrani vse soležnike Address Naslov Peer IP IP soležnika Error Napaka Already connected Že povezan Cannot add peer Ne morem dodat soležnika Synchronizer Synchronization Sinhronizacija Manage connected peers Upravljaj povezane soležnike Show your IP Prikaži tvoj IP Your IP is: Tvoj IP je: Discovering peers ... Odkrivam soležnike ... linux-show-player-0.5.1/lisp/plugins/synchronizer/peers_dialog.py000066400000000000000000000117671323636106000253340ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QDialog, QHBoxLayout, QListWidget, QVBoxLayout, \ QPushButton, QDialogButtonBox, QInputDialog, QMessageBox from lisp.core.configuration import config from lisp.core.util import compose_http_url from lisp.modules.remote.remote import RemoteController from lisp.ui import elogging from lisp.ui.ui_utils import translate from .peers_discovery_dialog import PeersDiscoveryDialog class PeersDialog(QDialog): def __init__(self, peers, **kwargs): super().__init__(**kwargs) self.peers = peers self.setWindowModality(Qt.ApplicationModal) self.setMaximumSize(500, 200) self.setMinimumSize(500, 200) self.resize(500, 200) self.setLayout(QHBoxLayout()) self.listWidget = QListWidget(self) self.listWidget.setAlternatingRowColors(True) self.layout().addWidget(self.listWidget) for peer in self.peers: self.listWidget.addItem(peer['uri']) self.buttonsLayout = QVBoxLayout() self.layout().addLayout(self.buttonsLayout) self.discoverPeersButton = QPushButton(self) self.addPeerButton = QPushButton(self) self.removePeerButton = QPushButton(self) self.removeAllButton = QPushButton(self) self.discoverPeersButton.clicked.connect(self.discover_peers) self.addPeerButton.clicked.connect(self.add_peer) self.removePeerButton.clicked.connect(self.remove_peer) self.removeAllButton.clicked.connect(self.remove_all) self.buttonsLayout.addWidget(self.discoverPeersButton) self.buttonsLayout.addWidget(self.addPeerButton) self.buttonsLayout.addWidget(self.removePeerButton) self.buttonsLayout.addWidget(self.removeAllButton) self.buttonsLayout.addSpacing(70) self.dialogButton = QDialogButtonBox(self) self.dialogButton.setStandardButtons(self.dialogButton.Ok) self.dialogButton.accepted.connect(self.accept) self.buttonsLayout.addWidget(self.dialogButton) self.layout().setStretch(0, 2) self.layout().setStretch(1, 1) self.retranslateUi() def retranslateUi(self): self.setWindowTitle( translate('SyncPeerDialog', 'Manage connected peers')) self.discoverPeersButton.setText( translate('SyncPeerDialog', 'Discover peers')) self.addPeerButton.setText( translate('SyncPeerDialog', 'Manually add a peer')) self.removePeerButton.setText( translate('SyncPeerDialog', 'Remove selected peer')) self.removeAllButton.setText( translate('SyncPeerDialog', 'Remove all peers')) def add_peer(self): ip, ok = QInputDialog.getText(self, translate('SyncPeerDialog', 'Address'), translate('SyncPeerDialog', 'Peer IP')) if ok: self._add_peer(ip) def _add_peer(self, ip): port = config['Remote']['BindPort'] uri = compose_http_url(ip, port) for peer in self.peers: if peer['uri'] == uri: QMessageBox.critical(self, translate('SyncPeerDialog', 'Error'), translate('SyncPeerDialog', 'Already connected')) return try: peer = {'proxy': RemoteController.connect_to(uri), 'uri': uri} self.peers.append(peer) self.listWidget.addItem(peer['uri']) except Exception as e: elogging.exception(translate('SyncPeerDialog', 'Cannot add peer'), str(e)) def discover_peers(self): dialog = PeersDiscoveryDialog(parent=self) if dialog.exec_() == dialog.Accepted: for peer in dialog.get_peers(): self._add_peer(peer) def remove_peer(self): if len(self.listWidget.selectedIndexes()) != 0: self.peers.pop(self.current_index()) self.listWidget.takeItem(self.current_index()) def remove_all(self): self.peers.clear() self.listWidget.clear() def current_index(self): return self.listWidget.selectedIndexes()[0].row() linux-show-player-0.5.1/lisp/plugins/synchronizer/peers_discovery_dialog.py000066400000000000000000000050261323636106000274120ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QVBoxLayout, QListWidget, QDialogButtonBox, \ QListWidgetItem, QDialog from lisp.core.signal import Connection from lisp.modules.remote.discovery import Discoverer from lisp.ui.ui_utils import translate class PeersDiscoveryDialog(QDialog): def __init__(self, **kwargs): super().__init__(**kwargs) self.setWindowModality(Qt.ApplicationModal) self.setLayout(QVBoxLayout()) self.setMaximumSize(300, 200) self.setMinimumSize(300, 200) self.resize(300, 200) self.listWidget = QListWidget(self) self.listWidget.setAlternatingRowColors(True) self.listWidget.setSelectionMode(self.listWidget.MultiSelection) self.layout().addWidget(self.listWidget) self.dialogButton = QDialogButtonBox(self) self.dialogButton.setStandardButtons(self.dialogButton.Ok) self.dialogButton.accepted.connect(self.accept) self.layout().addWidget(self.dialogButton) self.retranslateUi() self._discoverer = Discoverer() self._discoverer.discovered.connect(self._new_peer, Connection.QtQueued) def retranslateUi(self): self.setWindowTitle(translate('Synchronizer', 'Discovering peers ...')) def accept(self): self._discoverer.stop() return super().accept() def reject(self): self._discoverer.stop() return super().reject() def exec_(self): self._discoverer.start() return super().exec_() def get_peers(self): return [item.address for item in self.listWidget.selectedItems()] def _new_peer(self, peer): item = QListWidgetItem(peer[1] + ' - ' + peer[0]) item.address = peer[0] self.listWidget.addItem(item) linux-show-player-0.5.1/lisp/plugins/synchronizer/synchronizer.py000066400000000000000000000052251323636106000254240ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import logging import socket import traceback from PyQt5.QtWidgets import QMenu, QAction, QMessageBox from lisp.application import Application from lisp.core.plugin import Plugin from lisp.core.signal import Connection from lisp.core.util import get_lan_ip from lisp.ui.mainwindow import MainWindow from lisp.ui.ui_utils import translate from .peers_dialog import PeersDialog class Synchronizer(Plugin): Name = 'Synchronizer' def __init__(self): self.syncMenu = QMenu(translate('Synchronizer', 'Synchronization')) self.menu_action = MainWindow().menuTools.addMenu(self.syncMenu) self.addPeerAction = QAction( translate('Synchronizer', 'Manage connected peers'), MainWindow()) self.addPeerAction.triggered.connect(self.manage_peers) self.syncMenu.addAction(self.addPeerAction) self.showIpAction = QAction( translate('Synchronizer', 'Show your IP'), MainWindow()) self.showIpAction.triggered.connect(self.show_ip) self.syncMenu.addAction(self.showIpAction) self.peers = [] self.cue_media = {} def init(self): Application().layout.cue_executed.connect(self.remote_execute, mode=Connection.Async) def manage_peers(self): manager = PeersDialog(self.peers, parent=MainWindow()) manager.exec_() def show_ip(self): ip = translate('Synchronizer', 'Your IP is:') + ' ' + str(get_lan_ip()) QMessageBox.information(MainWindow(), ' ', ip) def reset(self): self.peers.clear() self.cue_media.clear() def remote_execute(self, cue): for peer in self.peers: try: peer['proxy'].execute(cue.index) except Exception: logging.error('REMOTE: remote control failed') logging.debug('REMOTE: ' + traceback.format_exc()) linux-show-player-0.5.1/lisp/plugins/timecode/000077500000000000000000000000001323636106000213455ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/timecode/__init__.py000066400000000000000000000017511323636106000234620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # Copyright 2016 Thomas Achtner # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . try: import ola except ImportError: ola = False raise ImportError('OLA module not found, plugin not loaded.') if ola: from .timecode import Timecode linux-show-player-0.5.1/lisp/plugins/timecode/i18n/000077500000000000000000000000001323636106000221245ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_cs.qm000066400000000000000000000032111323636106000247360ustar00rootroot00000000000000 SettingsPageName Timecode Settings Nastavení časového kódu Timecode Časový kód Timecode Cannot send timecode. Nelze poslat časový kód OLA has stopped. OLA zastavil. TimecodeSettings OLA Timecode Settings Nastavení časového kódu OLA Enable Plugin Povolit přídavný modul High-Resolution Timecode Časový kód pro velké rozlišení Timecode Format: Formát časového kódu: OLA status Stav OLA OLA is not running - start the OLA daemon. OLA neběží - spustit démona OLA. Replace HOURS by a static track number Nahradit HODINY stálým číslem stopy Enable ArtNet Timecode Povolit časový kód ArtNet Track number Číslo stopy To send ArtNet Timecode you need to setup a running OLA session! Pro poslání časového kódu ArtNet je potřeba zřídit běžící sezení OLA! linux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_en.qm000066400000000000000000000031231323636106000247350ustar00rootroot00000000000000iTimecodeTimecodeSettingsPageName"Timecode SettingsTimecode SettingsSettingsPageName*Cannot send timecode.Cannot send timecode.Timecode OLA has stopped.OLA has stopped.Timecode,Enable ArtNet TimecodeEnable ArtNet TimecodeTimecodeSettingsEnable Plugin Enable PluginTimecodeSettings0High-Resolution TimecodeHigh-Resolution TimecodeTimecodeSettings*OLA Timecode SettingsOLA Timecode SettingsTimecodeSettingsTOLA is not running - start the OLA daemon.*OLA is not running - start the OLA daemon.TimecodeSettingsOLA status OLA statusTimecodeSettingsLReplace HOURS by a static track number&Replace HOURS by a static track numberTimecodeSettings Timecode Format:Timecode Format:TimecodeSettingsTo send ArtNet Timecode you need to setup a running OLA session!@To send ArtNet Timecode you need to setup a running OLA session!TimecodeSettingsTrack number Track numberTimecodeSettingslinux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_en.ts000066400000000000000000000057531323636106000247610ustar00rootroot00000000000000 SettingsPageName Timecode Settings Timecode Settings Timecode Timecode Timecode Cannot send timecode. Cannot send timecode. OLA has stopped. OLA has stopped. TimecodeSettings OLA Timecode Settings OLA Timecode Settings Enable Plugin Enable Plugin High-Resolution Timecode High-Resolution Timecode Timecode Format: Timecode Format: OLA status OLA status OLA is not running - start the OLA daemon. OLA is not running - start the OLA daemon. Replace HOURS by a static track number Replace HOURS by a static track number Enable ArtNet Timecode Enable ArtNet Timecode Track number Track number To send ArtNet Timecode you need to setup a running OLA session! To send ArtNet Timecode you need to setup a running OLA session! linux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_es.qm000066400000000000000000000035031323636106000247440ustar00rootroot00000000000000Activar Cdigo de Tiempo ArtNetEnable ArtNet TimecodeTimecodeSettingsActivar Plugin Enable PluginTimecodeSettingsFCdigo de Tiempo de Alta ResolucinHigh-Resolution TimecodeTimecodeSettings>Ajustes de Cdigo de Tiempo OLAOLA Timecode SettingsTimecodeSettingsbOLA no se est ejecutanto - iniciar el daemon OLA*OLA is not running - start the OLA daemon.TimecodeSettingsEstado de OLA OLA statusTimecodeSettings`Reemplazar HORAS por un nmero de track esttico&Replace HOURS by a static track numberTimecodeSettings8Formato del Cdigo de TiempTimecode Format:TimecodeSettingsPara mandar Cdigo de Tiempo ArtNet necesita configurar una sesin OLA en ejecucin!@To send ArtNet Timecode you need to setup a running OLA session!TimecodeSettingsNmero de Track Track numberTimecodeSettingslinux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_es.ts000066400000000000000000000061641323636106000247630ustar00rootroot00000000000000 SettingsPageName Timecode Settings Ajustes de Código de Tiemo Timecode Código de Tiempo Timecode Cannot send timecode. No se puede mandar Código de Tiempo OLA has stopped. OLA se ha detenido TimecodeSettings OLA Timecode Settings Ajustes de Código de Tiempo OLA Enable Plugin Activar Plugin High-Resolution Timecode Código de Tiempo de Alta Resolución Timecode Format: Formato del Código de Tiempò OLA status Estado de OLA OLA is not running - start the OLA daemon. OLA no se está ejecutanto - iniciar el daemon OLA Replace HOURS by a static track number Reemplazar HORAS por un número de track estático Enable ArtNet Timecode Activar Código de Tiempo ArtNet Track number Número de Track To send ArtNet Timecode you need to setup a running OLA session! ¡Para mandar Código de Tiempo ArtNet necesita configurar una sesión OLA en ejecución! linux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_fr.qm000066400000000000000000000034531323636106000247500ustar00rootroot00000000000000Paramtres du code temporel OLAOLA Timecode SettingsTimecodeSettingsZOLA n'est pas dmarr - dmarrez le dmon OLA*OLA is not running - start the OLA daemon.TimecodeSettingsStatus OLA OLA statusTimecodeSettings\Remplace HOURS par un nombre de piste statique&Replace HOURS by a static track numberTimecodeSettings.Format de code temporelTimecode Format:TimecodeSettingsPour envoyer un code temporel ArtNet, vous devez paramtrer et lancer une session OLA !@To send ArtNet Timecode you need to setup a running OLA session!TimecodeSettingsNumro de piste Track numberTimecodeSettingslinux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_fr.ts000066400000000000000000000061611323636106000247600ustar00rootroot00000000000000 SettingsPageName Timecode Settings Paramètres de code temporel Timecode Code temporel Timecode Cannot send timecode. Impossible d'envoyer un code temporel. OLA has stopped. OLA s'est arrêté TimecodeSettings OLA Timecode Settings Paramètres du code temporel OLA Enable Plugin Activer le greffon High-Resolution Timecode Code temporel de haute-résolution Timecode Format: Format de code temporel OLA status Status OLA OLA is not running - start the OLA daemon. OLA n'est pas démarré - démarrez le démon OLA Replace HOURS by a static track number Remplace HOURS par un nombre de piste statique Enable ArtNet Timecode Active le code temporel ArtNet Track number Numéro de piste To send ArtNet Timecode you need to setup a running OLA session! Pour envoyer un code temporel ArtNet, vous devez paramétrer et lancer une session OLA ! linux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_it.qm000066400000000000000000000032511323636106000247510ustar00rootroot00000000000000 SettingsPageName Timecode Settings Impostazioni Timecode Timecode Timecode Timecode Cannot send timecode. Impossibile inviare il timecode. OLA has stopped. OLA si è fermato. TimecodeSettings OLA Timecode Settings Impostazioni Timecode OLA Enable Plugin Attiva Plugin High-Resolution Timecode Timecode ad alta risoluzione. Timecode Format: Formato Timecode: OLA status Stato OLA OLA is not running - start the OLA daemon. OLA non è in esecuzione - avvia il demone OLA Replace HOURS by a static track number Sostiture "ORE" con un numero di traccia statico Enable ArtNet Timecode Attiva ArtNet Timecode Track number Numero di traccia To send ArtNet Timecode you need to setup a running OLA session! Per utilizzare il Timecode ArtNet devi avviare una sessione OLA! linux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_sl_SI.qm000066400000000000000000000033261323636106000253510ustar00rootroot00000000000000 asovni zapisTimecodeSettingsPageName6Nastavitve  asovnega zapisaTimecode SettingsSettingsPageName@Ne morem poslat  asovnega zapisaCannot send timecode.Timecode&OLA se je zaustavilOLA has stopped.Timecode8Omogo i ArtNet  asovni zapisEnable ArtNet TimecodeTimecodeSettingsOmogo i vti nik Enable PluginTimecodeSettings4Zelo podobni  asovni zapisHigh-Resolution TimecodeTimecodeSettings@Nastavitve OLA  asovnega zapisaOLA Timecode SettingsTimecodeSettingsHOLA ni zagnan - za~enite OLA daemon.*OLA is not running - start the OLA daemon.TimecodeSettingsOLA stanje OLA statusTimecodeSettingsLZamenjaj URE s stati no atevilko sledi&Replace HOURS by a static track numberTimecodeSettings0Format  asovnega zapisa:Timecode Format:TimecodeSettingsZa poailjanje ArtNet  asovnega zapisa, rabite nastavit aktivno OLA sejo!@To send ArtNet Timecode you need to setup a running OLA session!TimecodeSettings`tevilka sledi Track numberTimecodeSettings !!$linux-show-player-0.5.1/lisp/plugins/timecode/i18n/timecode_sl_SI.ts000066400000000000000000000060741323636106000253650ustar00rootroot00000000000000 SettingsPageName Timecode Settings Nastavitve časovnega zapisa Timecode Časovni zapis Timecode Cannot send timecode. Ne morem poslat časovnega zapisa OLA has stopped. OLA se je zaustavil TimecodeSettings OLA Timecode Settings Nastavitve OLA časovnega zapisa Enable Plugin Omogoči vtičnik High-Resolution Timecode Zelo podobni časovni zapis Timecode Format: Format časovnega zapisa: OLA status OLA stanje OLA is not running - start the OLA daemon. OLA ni zagnan - zaženite OLA daemon. Replace HOURS by a static track number Zamenjaj URE s statično številko sledi Enable ArtNet Timecode Omogoči ArtNet časovni zapis Track number Številka sledi To send ArtNet Timecode you need to setup a running OLA session! Za pošiljanje ArtNet časovnega zapisa, rabite nastavit aktivno OLA sejo! linux-show-player-0.5.1/lisp/plugins/timecode/timecode.py000066400000000000000000000163241323636106000235160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # Copyright 2016 Thomas Achtner # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import logging from collections import namedtuple from ola.OlaClient import OLADNotRunningException, OlaClient from lisp.application import Application from lisp.core.clock import Clock from lisp.core.configuration import config from lisp.core.has_properties import Property from lisp.core.plugin import Plugin from lisp.core.signal import Connection from lisp.core.util import time_tuple from lisp.cues.cue import Cue from lisp.cues.cue_time import CueTime from lisp.cues.media_cue import MediaCue from lisp.plugins.timecode.timecode_settings import TimecodeCueSettings, \ TimecodeSettings from lisp.ui import elogging from lisp.ui.settings.app_settings import AppSettings from lisp.ui.settings.cue_settings import CueSettingsRegistry from lisp.ui.ui_utils import translate TcFormatDef = namedtuple('TimecodeDef', ['format', 'millis']) TcFormat = { 'FILM': TcFormatDef(format=OlaClient.TIMECODE_FILM, millis=1000 / 24), 'EBU': TcFormatDef(format=OlaClient.TIMECODE_EBU, millis=1000 / 25), 'SMPTE': TcFormatDef(format=OlaClient.TIMECODE_SMPTE, millis=1000 / 30) } class HRCueTime(CueTime): _Clock = Clock(30) # 1000 /30 = 33.3333 milliseconds class OlaTimecode: def __init__(self): try: self.__client = OlaClient() except OLADNotRunningException: self.__client = None self.__cue = None self.__cue_time = None self.__track = 0 self.__hres = config['Timecode'].getboolean('hres') self.__format = TcFormat[config['Timecode']['format']].format self.__millis = TcFormat[config['Timecode']['format']].millis self.__replace_hours = False self.__last_frame = -1 @property def cue(self): return self.__cue def status(self): return bool(self.__client) def start_timecode(self, cue): """Start the timecode, using the given cue.""" # Test and create client, if needed, return on fail if not self.__client: try: self.__client = OlaClient() except OLADNotRunningException: logging.debug('TIMECODE: Cannot track cue, OLA not running.') return # Load cue settings, if enabled, otherwise return if not cue.timecode['enabled']: return # Stop the currently "running" timecode self.stop_timecode() # Reload format settings self.__hres = config['Timecode'].getboolean('hres') self.__format = TcFormat[config['Timecode']['format']].format self.__millis = TcFormat[config['Timecode']['format']].millis # Setup new cue and options self.__cue = cue self.__cue_time = HRCueTime(cue) if self.__hres else CueTime(cue) self.__replace_hours = cue.timecode['replace_hours'] self.__track = cue.timecode['track'] # Start watching the new cue self.__cue_time.notify.connect( self.__send_timecode, Connection.QtQueued) def stop_timecode(self, rclient=False, rcue=False): """Stop the timecode :param rclient: Reset the client :param rcue: Reset the cues """ if self.__cue_time is not None: self.__cue_time.notify.disconnect(self.__send_timecode) self.__last_frame = -1 if rclient: self.__client = None if rcue: self.__cue = None self.__cue_time = None def __send_timecode(self, time): tt = time_tuple(time) frame = int(tt[3] / self.__millis) if self.__hres: if self.__last_frame == frame: return self.__last_frame = frame try: if not self.__replace_hours: track = tt[0] else: track = self.__track self.__client.SendTimeCode(self.__format, track, tt[1], tt[2], frame) except OLADNotRunningException: self.stop_timecode(rclient=True, rcue=True) elogging.error( translate('Timecode', 'Cannot send timecode.'), details=translate('Timecode', 'OLA has stopped.')) except Exception as e: self.stop_timecode(rclient=True, rcue=True) elogging.exception('Cannot send timecode.', e, dialog=False) class Timecode(Plugin): Name = 'Timecode' def __init__(self): super().__init__() self.__client = OlaTimecode() self.__cues = set() # Register a new Cue property to store settings Cue.register_property('timecode', Property(default={})) # Register cue-settings-page CueSettingsRegistry().add_item(TimecodeCueSettings, MediaCue) # Register pref-settings-page AppSettings.register_settings_widget(TimecodeSettings) # Watch cue-model changes Application().cue_model.item_added.connect(self.__cue_added) Application().cue_model.item_removed.connect(self.__cue_removed) def init(self): if not config['Timecode'].getboolean('enabled'): logging.info('TIMECODE: disabled by application settings') elif not self.__client.status(): logging.info('TIMECODE: disabled, OLA not running') def reset(self): self.__cues.clear() self.__client.stop_timecode(rclient=True, rcue=True) def __cue_changed(self, cue, property_name, value): if property_name == 'timecode': if value.get('enabled', False): if cue.id not in self.__cues: self.__cues.add(cue.id) cue.started.connect(self.__cue_started, Connection.QtQueued) else: self.__cue_removed(cue) def __cue_added(self, cue): cue.property_changed.connect(self.__cue_changed) self.__cue_changed(cue, 'timecode', cue.timecode) def __cue_removed(self, cue): try: self.__cues.remove(cue.id) if self.__client.cue is cue: self.__client.stop_timecode(rcue=True) cue.started.disconnect(self.__cue_started) cue.property_changed.disconnect(self.__cue_changed) except KeyError: pass def __cue_started(self, cue): if config['Timecode'].getboolean('enabled'): if cue is not self.__client.cue: self.__client.start_timecode(cue) elif cue is self.__client.cue: self.__client.stop_timecode(rcue=True) linux-show-player-0.5.1/lisp/plugins/timecode/timecode_settings.py000066400000000000000000000145021323636106000254320ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # Copyright 2016 Thomas Achtner # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP, Qt from PyQt5.QtWidgets import QGridLayout from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QVBoxLayout, QGroupBox, QLabel,\ QCheckBox, QComboBox, QHBoxLayout, QSpinBox from ola.OlaClient import OLADNotRunningException, OlaClient from lisp.ui.mainwindow import MainWindow from lisp.ui.settings.settings_page import CueSettingsPage,\ SettingsPage from lisp.ui.ui_utils import translate class TimecodeSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Timecode Settings') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.groupBox = QGroupBox(self) self.groupBox.setLayout(QGridLayout()) self.layout().addWidget(self.groupBox) self.activateBox = QCheckBox(self.groupBox) self.groupBox.layout().addWidget(self.activateBox, 0, 0) self.hresBox = QCheckBox(self.groupBox) self.groupBox.layout().addWidget(self.hresBox, 1, 0) self.formatLabel = QLabel(self.groupBox) self.groupBox.layout().addWidget(self.formatLabel, 2, 0) self.formatBox = QComboBox(self.groupBox) self.formatBox.addItem('FILM') self.formatBox.addItem('EBU') self.formatBox.addItem('SMPTE') self.groupBox.layout().addWidget(self.formatBox, 2, 1) self.retranslateUi() def retranslateUi(self): self.groupBox.setTitle( translate('TimecodeSettings', 'OLA Timecode Settings')) self.activateBox.setText(translate('TimecodeSettings', 'Enable Plugin')) self.hresBox.setText( translate('TimecodeSettings', 'High-Resolution Timecode')) self.formatLabel.setText( translate('TimecodeSettings', 'Timecode Format:')) def testOla(self): if self.activateBox.isChecked(): try: client = OlaClient() del client except OLADNotRunningException: QMessageBox.warning( MainWindow(), translate('TimecodeSettings', 'OLA status'), translate('TimecodeSettings', 'OLA is not running - start the OLA daemon.') ) def get_settings(self): return {'Timecode': { 'enabled': str(self.activateBox.isChecked()), 'hres': str(self.hresBox.isChecked()), 'format': self.formatBox.currentText() }} def load_settings(self, settings): settings = settings.get('Timecode', {}) self.activateBox.setChecked(settings.get('enabled') == 'True') self.hresBox.setChecked(settings.get('hres') == 'True') self.formatBox.setCurrentText(settings.get('format', '')) self.activateBox.stateChanged.connect(self.testOla) class TimecodeCueSettings(CueSettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Timecode') def __init__(self, cue_class, **kwargs): super().__init__(cue_class, **kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) self.groupBox = QGroupBox(self) self.groupBox.setLayout(QGridLayout()) self.layout().addWidget(self.groupBox) # enable / disable timecode self.enableCheck = QCheckBox(self.groupBox) self.enableCheck.setChecked(False) self.groupBox.layout().addWidget(self.enableCheck, 0, 0) # Hours can be replaced by cue number h:m:s:frames -> CUE:m:s:frames self.useHoursCheck = QCheckBox(self.groupBox) self.useHoursCheck.setChecked(True) self.groupBox.layout().addWidget(self.useHoursCheck, 1, 0) self.trackSpin = QSpinBox(self) self.trackSpin.setMinimum(0) self.trackSpin.setMaximum(99) self.useHoursCheck.stateChanged.connect(self.trackSpin.setEnabled) self.groupBox.layout().addWidget(self.trackSpin, 2, 0) self.trackLabel = QLabel(self.groupBox) self.trackLabel.setAlignment(Qt.AlignCenter) self.groupBox.layout().addWidget(self.trackLabel, 2, 1) self.layout().addSpacing(50) self.warnLabel = QLabel(self) self.warnLabel.setAlignment(Qt.AlignCenter) self.warnLabel.setStyleSheet('color: #FFA500; font-weight: bold') self.layout().addWidget(self.warnLabel) self.retranslateUi() def retranslateUi(self): self.groupBox.setTitle('Timecode') self.useHoursCheck.setText( translate('TimecodeSettings', 'Replace HOURS by a static track number')) self.enableCheck.setText( translate('TimecodeSettings', 'Enable ArtNet Timecode')) self.trackLabel.setText( translate('TimecodeSettings', 'Track number')) self.warnLabel.setText( translate('TimecodeSettings', 'To send ArtNet Timecode you need to setup a running OLA' ' session!')) def get_settings(self): settings = { 'enabled': self.enableCheck.isChecked(), 'replace_hours': self.useHoursCheck.isChecked(), 'track': self.trackSpin.value() } return {'timecode': settings} def load_settings(self, settings): settings = settings.get('timecode', {}) self.enableCheck.setChecked(settings.get('enabled', False)) self.useHoursCheck.setChecked(settings.get('replace_hours', False)) self.trackSpin.setValue(settings.get('track', 0)) linux-show-player-0.5.1/lisp/plugins/triggers/000077500000000000000000000000001323636106000214025ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/triggers/__init__.py000066400000000000000000000000361323636106000235120ustar00rootroot00000000000000from .triggers import Triggerslinux-show-player-0.5.1/lisp/plugins/triggers/i18n/000077500000000000000000000000001323636106000221615ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/plugins/triggers/i18n/triggers_cs.qm000066400000000000000000000012611323636106000250330ustar00rootroot00000000000000 CueTriggers Started Spuštěno Paused Pozastaveno Stopped Zastaveno Ended Skončeno SettingsPageName Triggers Spouštěče TriggersSettings Add Přidat Remove Odstranit Trigger Spouštěč Cue Narážka Action Činnost linux-show-player-0.5.1/lisp/plugins/triggers/i18n/triggers_en.qm000066400000000000000000000011751323636106000250340ustar00rootroot00000000000000 CueTriggers Started Started Paused Paused Stopped Stopped Ended Ended SettingsPageName Triggers Triggers TriggersSettings Add Add Remove Remove Trigger Trigger Cue Cue Action Action linux-show-player-0.5.1/lisp/plugins/triggers/i18n/triggers_es.qm000066400000000000000000000012431323636106000250350ustar00rootroot00000000000000 CueTriggers Started Iniciado Paused Pausado Stopped Detenido Ended Terminado SettingsPageName Triggers Disparadores TriggersSettings Add Añadir Remove Eliminar Trigger Disparador Cue Cue Action Acción linux-show-player-0.5.1/lisp/plugins/triggers/i18n/triggers_fr.qm000066400000000000000000000012271323636106000250370ustar00rootroot00000000000000 CueTriggers Started Démarré Paused En pause Stopped Arrêté Ended Fini SettingsPageName Triggers Déclencheurs TriggersSettings Add Ajouter Remove Retirer Trigger Déclencheur Cue Cue Action Action linux-show-player-0.5.1/lisp/plugins/triggers/i18n/triggers_it.qm000066400000000000000000000012131323636106000250370ustar00rootroot00000000000000 CueTriggers Started Avviata Paused Sospesa Stopped Fermata Ended Finita SettingsPageName Triggers Triggers TriggersSettings Add Aggiungi Remove Rimuovi Trigger Evento Cue Cue Action Azione linux-show-player-0.5.1/lisp/plugins/triggers/i18n/triggers_sl_SI.qm000066400000000000000000000012341323636106000254370ustar00rootroot00000000000000 CueTriggers Started Zagnan Paused V premoru Stopped Ustavljen Ended Končan SettingsPageName Triggers Prožilci TriggersSettings Add Dodaj Remove Odstrani Trigger Prožilec Cue Vrsta Action Akcija linux-show-player-0.5.1/lisp/plugins/triggers/triggers.py000066400000000000000000000043711323636106000236070ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from lisp.application import Application from lisp.core.has_properties import Property from lisp.core.plugin import Plugin from lisp.cues.cue import Cue from lisp.plugins.triggers.triggers_handler import CueHandler from lisp.plugins.triggers.triggers_settings import TriggersSettings from lisp.ui.settings.cue_settings import CueSettingsRegistry class Triggers(Plugin): Name = 'Triggers' def __init__(self): super().__init__() self.__handlers = {} # Register a Cue property to store settings Cue.register_property('triggers', Property({})) # Cue.triggers -> {trigger: [(target_id, action), ...]} # Register SettingsPage CueSettingsRegistry().add_item(TriggersSettings) Application().cue_model.item_added.connect(self.__cue_added) Application().cue_model.item_removed.connect(self.__cue_removed) def reset(self): self.__handlers.clear() def __cue_changed(self, cue, property_name, value): if property_name == 'triggers': if cue.id in self.__handlers: self.__handlers[cue.id].triggers = cue.triggers else: self.__handlers[cue.id] = CueHandler(cue, cue.triggers) def __cue_added(self, cue): cue.property_changed.connect(self.__cue_changed) self.__cue_changed(cue, 'triggers', cue.triggers) def __cue_removed(self, cue): cue.property_changed.disconnect(self.__cue_changed) self.__handlers.pop(cue.id, None) linux-show-player-0.5.1/lisp/plugins/triggers/triggers_handler.py000066400000000000000000000041341323636106000253010ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from enum import Enum from PyQt5.QtCore import QT_TRANSLATE_NOOP from lisp.application import Application from lisp.core.signal import Connection from lisp.cues.cue import CueAction class CueTriggers(Enum): Started = QT_TRANSLATE_NOOP('CueTriggers', 'Started') Paused = QT_TRANSLATE_NOOP('CueTriggers', 'Paused') Stopped = QT_TRANSLATE_NOOP('CueTriggers', 'Stopped') Ended = QT_TRANSLATE_NOOP('CueTriggers','Ended') class CueHandler: def __init__(self, cue, triggers): self.triggers = triggers self.cue = cue self.cue.started.connect(self.__started, Connection.Async) self.cue.paused.connect(self.__paused, Connection.Async) self.cue.stopped.connect(self.__stopped, Connection.Async) self.cue.end.connect(self.__ended, Connection.Async) def __paused(self): self.__execute(CueTriggers.Paused.value) def __started(self): self.__execute(CueTriggers.Started.value) def __stopped(self): self.__execute(CueTriggers.Stopped.value) def __ended(self): self.__execute(CueTriggers.Ended.value) def __execute(self, trigger): for target_id, action in self.triggers.get(trigger, []): target = Application().cue_model.get(target_id) if target is not None: target.execute(CueAction(action)) linux-show-player-0.5.1/lisp/plugins/triggers/triggers_settings.py000066400000000000000000000135201323636106000255230ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP, Qt from PyQt5.QtWidgets import QVBoxLayout, QDialogButtonBox, QSizePolicy, \ QHeaderView, QTableView from lisp.application import Application from lisp.cues.cue import CueAction from lisp.plugins.triggers.triggers_handler import CueTriggers from lisp.ui.cuelistdialog import CueSelectDialog from lisp.ui.qdelegates import ComboBoxDelegate, CueActionDelegate, \ CueSelectionDelegate from lisp.ui.qmodels import CueClassRole, SimpleCueListModel from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class TriggersSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Triggers') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout(self)) self.layout().setAlignment(Qt.AlignTop) self.cue_select = CueSelectDialog(cues=Application().cue_model) self.triggersModel = TriggersModel() self.triggersView = TriggersView(self.cue_select, parent=self) self.triggersView.setModel(self.triggersModel) self.layout().addWidget(self.triggersView) self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.layout().addWidget(self.dialogButtons) self.addButton = self.dialogButtons.addButton( translate('TriggersSettings', 'Add'), QDialogButtonBox.ActionRole) self.addButton.clicked.connect(self._add_trigger) self.delButton = self.dialogButtons.addButton( translate('TriggersSettings', 'Remove'), QDialogButtonBox.ActionRole) self.delButton.clicked.connect(self._remove_trigger) def _add_trigger(self): if self.cue_select.exec(): cue = self.cue_select.selected_cue() if cue is not None: self.triggersModel.appendRow(cue.__class__, CueTriggers.Started.value, cue.id, cue.CueActions[0]) def _remove_trigger(self): self.triggersModel.removeRow(self.triggersView.currentIndex().row()) def load_settings(self, settings): # Remove the edited cue from the list of possible targets edited_cue = Application().cue_model.get(settings.get('id')) if edited_cue: self.cue_select.remove_cue(edited_cue) for trigger, targets in settings.get('triggers', {}).items(): for target, action in targets: target = Application().cue_model.get(target) if target is not None: self.triggersModel.appendRow(target.__class__, trigger, target.id, CueAction(action)) def get_settings(self): triggers = {} for trigger, target, action in self.triggersModel.rows: action = action.value if trigger not in triggers: triggers[trigger] = [] # Avoid duplicate if (target, action) not in triggers[trigger]: triggers[trigger].append((target, action)) return {'triggers': triggers} class TriggersView(QTableView): def __init__(self, cue_select, **kwargs): super().__init__(**kwargs) self.setSelectionBehavior(QTableView.SelectRows) self.setSelectionMode(QTableView.SingleSelection) self.setShowGrid(False) self.setAlternatingRowColors(True) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.horizontalHeader().setHighlightSections(False) self.verticalHeader().sectionResizeMode(QHeaderView.Fixed) self.verticalHeader().setDefaultSectionSize(26) self.verticalHeader().setHighlightSections(False) self.delegates = [ ComboBoxDelegate(options=[e.value for e in CueTriggers], tr_context='CueTriggers'), CueSelectionDelegate(cue_select), CueActionDelegate() ] for column, delegate in enumerate(self.delegates): self.setItemDelegateForColumn(column, delegate) class TriggersModel(SimpleCueListModel): def __init__(self): # NOTE: The model does fixed-indices operations based on this list super().__init__([translate('TriggersSettings', 'Trigger'), translate('TriggersSettings', 'Cue'), translate('TriggersSettings', 'Action')]) self.rows_cc = [] def setData(self, index, value, role=Qt.DisplayRole): result = super().setData(index, value, role) if result and role == CueClassRole: if self.rows[index.row()][2] not in value.CueActions: self.rows[index.row()][2] = value.CueActions[0] self.dataChanged.emit(self.index(index.row(), 2), self.index(index.row(), 2), [Qt.DisplayRole, Qt.EditRole]) return result linux-show-player-0.5.1/lisp/ui/000077500000000000000000000000001323636106000165105ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/__init__.py000066400000000000000000000000001323636106000206070ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/about.py000066400000000000000000000141171323636106000202000ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see from collections import OrderedDict from PyQt5 import QtCore from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QDialog, QGridLayout, QLabel, QWidget, QTabWidget, \ QTextBrowser, QDialogButtonBox import lisp from lisp.ui.ui_utils import translate class About(QDialog): LICENSE = '''

Linux Show Player is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Linux Show Player is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

''' DESCRIPTION = QT_TRANSLATE_NOOP('AboutDialog', 'Linux Show Player is a cue-player designed for stage productions.') WEB_SITE = 'http://linux-show-player.sourceforge.net' USER_GROUP = 'http://groups.google.com/group/linux-show-player---users' SOURCE_CODE = 'https://github.com/FrancescoCeruti/linux-show-player' CONTRIBUTORS = OrderedDict({ QT_TRANSLATE_NOOP('About', 'Authors'): [ ('Francesco Ceruti', 'ceppofrancy@gmail.com') ], QT_TRANSLATE_NOOP('About', 'Contributors'): [ ('Yinameah', 'https://github.com/Yinameah'), ('nodiscc', 'https://github.com/nodiscc'), ('Thomas Achtner', 'info@offtools.de') ], QT_TRANSLATE_NOOP('About', 'Translators'): [ ('aroomthedoomed', 'https://github.com/aroomthedoomed'), ('fri', 'https://www.transifex.com/user/profile/fri'), ('Luis García-Tornel', 'tornel@gmail.com'), ('miharix', 'https://github.com/miharix'), ('Olivier Humbert - français', 'trebmuh@tuxfamily.org') ], }) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setWindowModality(QtCore.Qt.ApplicationModal) self.setWindowTitle(translate('About', 'About Linux Show Player')) self.setMaximumSize(500, 420) self.setMinimumSize(500, 420) self.resize(500, 420) self.setLayout(QGridLayout()) self.iconLabel = QLabel(self) self.iconLabel.setPixmap( QIcon.fromTheme('linux-show-player').pixmap(100, 100)) self.layout().addWidget(self.iconLabel, 0, 0) self.shortInfo = QLabel(self) self.shortInfo.setAlignment(Qt.AlignCenter) self.shortInfo.setText('

Linux Show Player {0}

' 'Copyright © Francesco Ceruti' .format(str(lisp.__version__))) self.layout().addWidget(self.shortInfo, 0, 1) self.layout().addWidget(QWidget(), 1, 0, 1, 2) # Information tabs self.tabWidget = QTabWidget(self) self.layout().addWidget(self.tabWidget, 2, 0, 1, 2) self.info = QTextBrowser(self) self.info.setOpenExternalLinks(True) self.info.setHtml('''

{0}

{2}
{4}
{6}
'''.format( translate('AboutDialog', self.DESCRIPTION), self.WEB_SITE, translate('AboutDialog', 'Web site'), self.USER_GROUP, translate('AboutDialog', 'Users group'), self.SOURCE_CODE, translate('AboutDialog', 'Source code')) ) self.tabWidget.addTab(self.info, translate('AboutDialog', 'Info')) self.license = QTextBrowser(self) self.license.setOpenExternalLinks(True) self.license.setHtml(self.LICENSE) self.tabWidget.addTab(self.license, translate('AboutDialog', 'License')) self.contributors = QTextBrowser(self) self.contributors.setOpenExternalLinks(True) self.contributors.setHtml(self.__contributors()) self.tabWidget.addTab(self.contributors, translate('AboutDialog', 'Contributors')) # Ok button self.buttons = QDialogButtonBox(QDialogButtonBox.Ok) self.buttons.accepted.connect(self.accept) self.layout().addWidget(self.buttons, 3, 1) self.layout().setColumnStretch(0, 1) self.layout().setColumnStretch(1, 3) self.layout().setRowStretch(0, 6) self.layout().setRowStretch(1, 1) self.layout().setRowStretch(2, 16) self.layout().setRowStretch(3, 3) self.buttons.setFocus() def __contributors(self): text = '' for section, people in self.CONTRIBUTORS.items(): text += '{0}:
'.format(translate('About', section)) for person in people: text += person[0] if '://' in person[1]: text += ' - {1}'.format( person[1], person[1][person[1].index('://')+3:]) elif person[1]: text += ' - {0}'.format(person[1]) text += '
' text += '
' return text linux-show-player-0.5.1/lisp/ui/cuelistdialog.py000066400000000000000000000067401323636106000217210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QDialog, QTreeWidget, QHeaderView, QVBoxLayout, \ QDialogButtonBox, QTreeWidgetItem from lisp.ui import elogging class CueSelectDialog(QDialog): def __init__(self, cues=None, properties=('index', 'name'), selection_mode=QTreeWidget.SingleSelection, **kwargs): super().__init__(**kwargs) self.setMinimumSize(600, 400) self._properties = list(properties) self._cues = {} self.list = QTreeWidget(self) self.list.setSelectionMode(selection_mode) self.list.setSelectionBehavior(QTreeWidget.SelectRows) self.list.setAlternatingRowColors(True) self.list.setIndentation(0) self.list.setHeaderLabels([prop.title() for prop in properties]) self.list.header().setSectionResizeMode(QHeaderView.Fixed) self.list.header().setSectionResizeMode(1, QHeaderView.Stretch) self.list.header().setStretchLastSection(False) self.list.sortByColumn(0, Qt.AscendingOrder) self.list.setSortingEnabled(True) if cues is not None: self.add_cues(cues) self.setLayout(QVBoxLayout()) self.layout().addWidget(self.list) self.buttons = QDialogButtonBox(self) self.buttons.addButton(QDialogButtonBox.Cancel) self.buttons.addButton(QDialogButtonBox.Ok) self.layout().addWidget(self.buttons) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) def add_cue(self, cue): item = QTreeWidgetItem() item.setTextAlignment(0, Qt.AlignCenter) for n, prop in enumerate(self._properties): try: item.setData(n, Qt.DisplayRole, getattr(cue, prop, 'Undefined')) except Exception as e: elogging.exception('Cannot display {0} property'.format(prop), e, dialog=False) self._cues[cue] = item item.setData(0, Qt.UserRole, cue) self.list.addTopLevelItem(item) def add_cues(self, cues): self.list.setSortingEnabled(False) for cue in cues: self.add_cue(cue) self.list.setSortingEnabled(True) def remove_cue(self, cue): index = self.list.indexOfTopLevelItem(self._cues.pop(cue)) self.list.takeTopLevelItem(index) def reset(self): self.list.clear() self._cues.clear() def selected_cues(self): cues = [] for item in self.list.selectedItems(): cues.append(item.data(0, Qt.UserRole)) return cues def selected_cue(self): items = self.list.selectedItems() if items: return items[0].data(0, Qt.UserRole) linux-show-player-0.5.1/lisp/ui/elogging.py000066400000000000000000000045311323636106000206600ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . """ Module providing a simple proxy over python-logging default module and optionally showing a dialog to the user. """ import logging import traceback from lisp.ui.widgets import QDetailedMessageBox from lisp.ui.ui_utils import translate def info(msg, details='', dialog=False): logging.info(_log_msg(msg, details)) if dialog: _dialog(translate('Logging', 'Information'), msg, details, QDetailedMessageBox.Information) def debug(msg, details='', dialog=False): logging.debug(_log_msg(msg, details)) if dialog: _dialog(translate('Logging', 'Debug'), msg, details, QDetailedMessageBox.Information) def warning(msg, details='', dialog=True): logging.warning(_log_msg(msg, details)) if dialog: _dialog(translate('Logging', 'Warning'), msg, details, QDetailedMessageBox.Warning) def error(msg, details='', dialog=True): logging.error(_log_msg(msg, details)) if dialog: _dialog(translate('Logging', 'Error'), msg, details, QDetailedMessageBox.Critical) def exception(msg, exception, dialog=True): logging.error(_log_msg(msg, traceback.format_exc())) if dialog: _dialog(translate('Logging', 'Error'), msg, str(exception), QDetailedMessageBox.Critical) def _log_msg(msg, details): if details.strip() != '': details = translate('Logging', 'Details:') + ' ' + details return msg + '\n' + details return msg def _dialog(title, msg, details, type_): QDetailedMessageBox.dgeneric(title, msg, details, type_) linux-show-player-0.5.1/lisp/ui/layoutselect.py000066400000000000000000000066171323636106000216110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import os from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QDialog, QComboBox, QPushButton, QFrame, QTextBrowser, QFileDialog, QGridLayout from lisp import layouts from lisp.ui.ui_utils import translate class LayoutSelect(QDialog): def __init__(self, parent=None): super().__init__(parent) self.filepath = '' self.setWindowModality(Qt.ApplicationModal) self.setWindowTitle(translate('LayoutSelect', 'Layout selection')) self.setMaximumSize(675, 300) self.setMinimumSize(675, 300) self.resize(675, 300) self.setLayout(QGridLayout(self)) self.layout().setContentsMargins(5, 5, 5, 5) self.layoutCombo = QComboBox(self) self.layoutCombo.currentIndexChanged.connect(self.show_description) self.layout().addWidget(self.layoutCombo, 0, 0) self.confirmButton = QPushButton(self) self.confirmButton.setText(translate('LayoutSelect', 'Select layout')) self.layout().addWidget(self.confirmButton, 0, 1) self.fileButton = QPushButton(self) self.fileButton.setText(translate('LayoutSelect', 'Open file')) self.layout().addWidget(self.fileButton, 0, 2) self.layout().setColumnStretch(0, 3) self.layout().setColumnStretch(1, 2) self.layout().setColumnStretch(2, 1) line = QFrame(self) line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.layout().addWidget(line, 1, 0, 1, 3) self.description = QTextBrowser(self) self.layout().addWidget(self.description, 2, 0, 1, 3) for layout_class in layouts.get_layouts(): self.layoutCombo.addItem(layout_class.NAME, layout_class) if self.layoutCombo.count() == 0: raise Exception('No layout installed!') self.confirmButton.clicked.connect(self.accept) self.fileButton.clicked.connect(self.open_file) def selected(self): return self.layoutCombo.currentData() def show_description(self, index): layout = self.layoutCombo.currentData() details = '
    ' for detail in layout.DETAILS: details += '
  • ' + translate('LayoutDetails', detail) details += '
' self.description.setHtml( '

' + layout.NAME + '

' '

' + translate('LayoutDescription', layout.DESCRIPTION) + '

' + details) def open_file(self): path, _ = QFileDialog.getOpenFileName(self, filter='*.lsp', directory=os.getenv('HOME')) self.filepath = path self.accept() linux-show-player-0.5.1/lisp/ui/mainwindow.py000066400000000000000000000320371323636106000212430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import os from PyQt5 import QtCore from PyQt5.QtCore import pyqtSignal from PyQt5.QtGui import QKeySequence from PyQt5.QtWidgets import QMainWindow, QStatusBar, QMenuBar, QMenu, QAction, \ qApp, QFileDialog, QDialog, QMessageBox, QVBoxLayout, QWidget from lisp.core import configuration from lisp.core.actions_handler import MainActionsHandler from lisp.core.singleton import QSingleton from lisp.cues.media_cue import MediaCue from lisp.ui import about from lisp.ui.settings.app_settings import AppSettings from lisp.ui.ui_utils import translate class MainWindow(QMainWindow, metaclass=QSingleton): new_session = pyqtSignal() save_session = pyqtSignal(str) open_session = pyqtSignal(str) def __init__(self): super().__init__() self.setMinimumSize(500, 400) self.setCentralWidget(QWidget()) self.centralWidget().setLayout(QVBoxLayout()) self.centralWidget().layout().setContentsMargins(5, 5, 5, 5) self._cue_add_menu = {} self.layout = None # Status Bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) MainActionsHandler.action_done.connect(self._action_done) MainActionsHandler.action_undone.connect(self._action_undone) MainActionsHandler.action_redone.connect(self._action_redone) # Menubar self.menubar = QMenuBar(self) self.menubar.setGeometry(QtCore.QRect(0, 0, 0, 25)) self.menubar.setContextMenuPolicy(QtCore.Qt.PreventContextMenu) self.menuFile = QMenu(self.menubar) self.menuEdit = QMenu(self.menubar) self.menuLayout = QMenu(self.menubar) self.menuTools = QMenu(self.menubar) self.menuAbout = QMenu(self.menubar) self.menubar.addMenu(self.menuFile) self.menubar.addMenu(self.menuEdit) self.menubar.addMenu(self.menuLayout) self.menubar.addMenu(self.menuTools) self.menubar.addMenu(self.menuAbout) self.setMenuBar(self.menubar) # menuFile self.newSessionAction = QAction(self) self.newSessionAction.triggered.connect(self._new_session) self.openSessionAction = QAction(self) self.openSessionAction.triggered.connect(self._load_from_file) self.saveSessionAction = QAction(self) self.saveSessionAction.triggered.connect(self._save) self.saveSessionWithName = QAction(self) self.saveSessionWithName.triggered.connect(self._save_with_name) self.editPreferences = QAction(self) self.editPreferences.triggered.connect(self._show_preferences) self.fullScreenAction = QAction(self) self.fullScreenAction.triggered.connect(self._fullscreen) self.fullScreenAction.setCheckable(True) self.exitAction = QAction(self) self.exitAction.triggered.connect(self._exit) self.menuFile.addAction(self.newSessionAction) self.menuFile.addAction(self.openSessionAction) self.menuFile.addSeparator() self.menuFile.addAction(self.saveSessionAction) self.menuFile.addAction(self.saveSessionWithName) self.menuFile.addSeparator() self.menuFile.addAction(self.editPreferences) self.menuFile.addSeparator() self.menuFile.addAction(self.fullScreenAction) self.menuFile.addSeparator() self.menuFile.addAction(self.exitAction) # menuEdit self.actionUndo = QAction(self) self.actionUndo.triggered.connect(MainActionsHandler.undo_action) self.actionRedo = QAction(self) self.actionRedo.triggered.connect(MainActionsHandler.redo_action) self.multiEdit = QAction(self) self.selectAll = QAction(self) self.selectAllMedia = QAction(self) self.deselectAll = QAction(self) self.invertSelection = QAction(self) self.cueSeparator = self.menuEdit.addSeparator() self.menuEdit.addAction(self.actionUndo) self.menuEdit.addAction(self.actionRedo) self.menuEdit.addSeparator() self.menuEdit.addAction(self.selectAll) self.menuEdit.addAction(self.selectAllMedia) self.menuEdit.addAction(self.deselectAll) self.menuEdit.addAction(self.invertSelection) self.menuEdit.addSeparator() self.menuEdit.addAction(self.multiEdit) # menuAbout self.actionAbout = QAction(self) self.actionAbout.triggered.connect(about.About(self).show) self.actionAbout_Qt = QAction(self) self.actionAbout_Qt.triggered.connect(qApp.aboutQt) self.menuAbout.addAction(self.actionAbout) self.menuAbout.addSeparator() self.menuAbout.addAction(self.actionAbout_Qt) # Set component text self.retranslateUi() # The save file name self.filename = '' def retranslateUi(self): self.setWindowTitle('Linux Show Player') # menuFile self.menuFile.setTitle(translate('MainWindow', '&File')) self.newSessionAction.setText(translate('MainWindow', 'New session')) self.newSessionAction.setShortcut(QKeySequence.New) self.openSessionAction.setText(translate('MainWindow', 'Open')) self.openSessionAction.setShortcut(QKeySequence.Open) self.saveSessionAction.setText(translate('MainWindow', 'Save session')) self.saveSessionAction.setShortcut(QKeySequence.Save) self.editPreferences.setText(translate('MainWindow', 'Preferences')) self.editPreferences.setShortcut(QKeySequence.Preferences) self.saveSessionWithName.setText(translate('MainWindow', 'Save as')) self.saveSessionWithName.setShortcut(QKeySequence.SaveAs) self.fullScreenAction.setText(translate('MainWindow', 'Full Screen')) self.fullScreenAction.setShortcut(QKeySequence.FullScreen) self.exitAction.setText(translate('MainWindow', 'Exit')) # menuEdit self.menuEdit.setTitle(translate('MainWindow', '&Edit')) self.actionUndo.setText(translate('MainWindow', 'Undo')) self.actionUndo.setShortcut(QKeySequence.Undo) self.actionRedo.setText(translate('MainWindow', 'Redo')) self.actionRedo.setShortcut(QKeySequence.Redo) self.selectAll.setText(translate('MainWindow', 'Select all')) self.selectAllMedia.setText( translate('MainWindow', 'Select all media cues')) self.selectAll.setShortcut(QKeySequence.SelectAll) self.deselectAll.setText(translate('MainWindow', 'Deselect all')) self.deselectAll.setShortcut(translate('MainWindow', 'CTRL+SHIFT+A')) self.invertSelection.setText( translate('MainWindow', 'Invert selection')) self.invertSelection.setShortcut(translate('MainWindow', 'CTRL+I')) self.multiEdit.setText(translate('MainWindow', 'Edit selected')) self.multiEdit.setShortcut(translate('MainWindow', 'CTRL+SHIFT+E')) # menuLayout self.menuLayout.setTitle(translate('MainWindow', '&Layout')) # menuTools self.menuTools.setTitle(translate('MainWindow', '&Tools')) self.multiEdit.setText(translate('MainWindow', 'Edit selection')) # menuAbout self.menuAbout.setTitle(translate('MainWindow', '&About')) self.actionAbout.setText(translate('MainWindow', 'About')) self.actionAbout_Qt.setText(translate('MainWindow', 'About Qt')) def set_layout(self, layout): if self.layout is not None: self.layout.hide() self.centralWidget().layout().removeWidget(self.layout) self.multiEdit.triggered.disconnect() self.selectAll.triggered.disconnect() self.selectAllMedia.triggered.disconnect() self.deselectAll.triggered.disconnect() self.invertSelection.triggered.disconnect() self.layout = layout self.centralWidget().layout().addWidget(self.layout) self.layout.show() self.multiEdit.triggered.connect(self.layout.edit_selected_cues) self.selectAll.triggered.connect(lambda: self.layout.select_all()) self.selectAllMedia.triggered.connect( lambda: self.layout.select_all(MediaCue)) self.deselectAll.triggered.connect(lambda: self.layout.deselect_all()) self.invertSelection.triggered.connect(self.layout.invert_selection) def closeEvent(self, event): self._exit() event.ignore() def register_cue_menu_action(self, name, function, category='', shortcut=''): '''Register a new-cue choice for the edit-menu param name: The name for the MenuAction param function: The function that add the new cue(s) param category: The optional menu where insert the MenuAction param shortcut: An optional shortcut for the MenuAction ''' action = QAction(self) action.setText(translate('MainWindow', name)) action.triggered.connect(function) if shortcut != '': action.setShortcut(translate('MainWindow', shortcut)) if category != '': if category not in self._cue_add_menu: menu = QMenu(category, self) self._cue_add_menu[category] = menu self.menuEdit.insertMenu(self.cueSeparator, menu) self._cue_add_menu[category].addAction(action) else: self.menuEdit.insertAction(self.cueSeparator, action) def update_window_title(self): saved = MainActionsHandler.is_saved() if not saved and not self.windowTitle()[0] == '*': self.setWindowTitle('*' + self.windowTitle()) elif saved and self.windowTitle()[0] == '*': self.setWindowTitle(self.windowTitle()[1:]) def _action_done(self, action): self.statusBar.showMessage(action.log()) self.update_window_title() def _action_undone(self, action): self.statusBar.showMessage( translate('MainWindow', 'Undone: ') + action.log()) self.update_window_title() def _action_redone(self, action): self.statusBar.showMessage( translate('MainWindow', 'Redone: ') + action.log()) self.update_window_title() def _save(self): if self.filename == '': self._save_with_name() else: self.save_session.emit(self.filename) def _save_with_name(self): filename, _ = QFileDialog.getSaveFileName(parent=self, filter='*.lsp', directory=os.getenv('HOME')) if filename != '': if not filename.endswith('.lsp'): filename += '.lsp' self.filename = filename self._save() def _show_preferences(self): prefUi = AppSettings(configuration.config_to_dict(), parent=self) prefUi.exec_() if prefUi.result() == QDialog.Accepted: configuration.update_config_from_dict(prefUi.get_configuraton()) def _load_from_file(self): if self._check_saved(): path, _ = QFileDialog.getOpenFileName(self, filter='*.lsp', directory=os.getenv('HOME')) if os.path.exists(path): self.open_session.emit(path) self.filename = path def _new_session(self): if self._check_saved(): self.new_session.emit() def _check_saved(self): if not MainActionsHandler.is_saved(): msgBox = QMessageBox(self) msgBox.setIcon(QMessageBox.Warning) msgBox.setWindowTitle(translate('MainWindow', 'Close session')) msgBox.setText( translate('MainWindow', 'The current session is not saved.')) msgBox.setInformativeText( translate('MainWindow', 'Discard the changes?')) msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Save) result = msgBox.exec_() if result == QMessageBox.Cancel: return False elif result == QMessageBox.Save: self._save() return True def _fullscreen(self, enable): if enable: self.showFullScreen() else: self.showMaximized() def _exit(self): if self._check_saved(): qApp.quit() linux-show-player-0.5.1/lisp/ui/qdelegates.py000066400000000000000000000162141323636106000212040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QEvent from PyQt5.QtWidgets import QStyledItemDelegate, QComboBox, QSpinBox, \ QLineEdit, QStyle, QDialog from lisp.application import Application from lisp.cues.cue import CueAction from lisp.ui.qmodels import CueClassRole from lisp.ui.ui_utils import translate from lisp.ui.widgets import CueActionComboBox class LabelDelegate(QStyledItemDelegate): def _text(self, painter, option, index): return '' def paint(self, painter, option, index): # Add 4px of left an right padding option.rect.adjust(4, 0, 4, 0) text = self._text(painter, option, index) text = option.fontMetrics.elidedText(text, Qt.ElideRight, option.rect.width()) painter.save() if option.state & QStyle.State_Selected: painter.setBrush(option.palette.highlight()) pen = painter.pen() pen.setBrush(option.palette.highlightedText()) painter.setPen(pen) painter.drawText(option.rect, option.displayAlignment, text) painter.restore() class ComboBoxDelegate(LabelDelegate): def __init__(self, options=(), tr_context=None, **kwargs): super().__init__(**kwargs) self.options = options self.tr_context = tr_context def _text(self, painter, option, index): return translate(self.tr_context, index.data()) def paint(self, painter, option, index): option.displayAlignment = Qt.AlignHCenter | Qt.AlignVCenter super().paint(painter, option, index) def createEditor(self, parent, option, index): editor = QComboBox(parent) editor.setFrame(False) for option in self.options: editor.addItem(translate(self.tr_context, option), option) return editor def setEditorData(self, comboBox, index): value = index.model().data(index, Qt.EditRole) comboBox.setCurrentText(translate(self.tr_context, value)) def setModelData(self, comboBox, model, index): model.setData(index, comboBox.currentData(), Qt.EditRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class SpinBoxDelegate(QStyledItemDelegate): def __init__(self, minimum=-100, maximum=100, step=1, **kwargs): super().__init__(**kwargs) self.minimum = minimum self.maximum = maximum self.step = step def createEditor(self, parent, option, index): editor = QSpinBox(parent) editor.setRange(self.minimum, self.maximum) editor.setSingleStep(self.step) return editor def setEditorData(self, spinBox, index): value = index.model().data(index, Qt.EditRole) if isinstance(value, int): spinBox.setValue(value) def setModelData(self, spinBox, model, index): spinBox.interpretText() model.setData(index, spinBox.value(), Qt.EditRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class LineEditDelegate(QStyledItemDelegate): def __init__(self, max_length=-1, validator=None, **kwargs): super().__init__(**kwargs) self.max_length = max_length self.validator = validator def createEditor(self, parent, option, index): editor = QLineEdit(parent) if self.max_length > 0: editor.setMaxLength(self.max_length) if self.validator is not None: editor.setValidator(self.validator) editor.setFrame(False) return editor def setEditorData(self, lineEdit, index): value = index.model().data(index, Qt.EditRole) lineEdit.setText(str(value)) def setModelData(self, lineEdit, model, index): model.setData(index, lineEdit.text(), Qt.EditRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class CueActionDelegate(LabelDelegate): Mode = CueActionComboBox.Mode def __init__(self, cue_class=None, mode=Mode.Action, **kwargs): super().__init__(**kwargs) self.cue_class = cue_class self.mode = mode def _text(self, painter, option, index): value = index.data(Qt.EditRole) if self.mode == CueActionDelegate.Mode.Action: name = value.name elif self.mode == CueActionDelegate.Mode.Name: name = value else: name = CueAction(value).name return translate('CueAction', name) def paint(self, painter, option, index): option.displayAlignment = Qt.AlignHCenter | Qt.AlignVCenter super().paint(painter, option, index) def createEditor(self, parent, option, index): if self.cue_class is None: self.cue_class = index.data(CueClassRole) editor = CueActionComboBox(self.cue_class, mode=self.mode, parent=parent) editor.setFrame(False) return editor def setEditorData(self, comboBox, index): value = index.data(Qt.EditRole) if self.mode == CueActionDelegate.Mode.Action: action = value elif self.mode == CueActionDelegate.Mode.Name: action = CueAction[value] else: action = CueAction(value) comboBox.setCurrentText(translate('CueAction', action.name)) def setModelData(self, comboBox, model, index): model.setData(index, comboBox.currentData(), Qt.EditRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class CueSelectionDelegate(LabelDelegate): def __init__(self, cue_select_dialog, **kwargs): super().__init__(**kwargs) self.cue_select = cue_select_dialog def _text(self, painter, option, index): cue = Application().cue_model.get(index.data()) if cue is not None: return '{} | {}'.format(cue.index, cue.name) return 'UNDEF' def editorEvent(self, event, model, option, index): if event.type() == QEvent.MouseButtonDblClick: if self.cue_select.exec_() == QDialog.Accepted: cue = self.cue_select.selected_cue() if cue is not None: model.setData(index, cue.id, Qt.EditRole) model.setData(index, cue.__class__, CueClassRole) return True return super().editorEvent(event, model, option, index) linux-show-player-0.5.1/lisp/ui/qmodels.py000066400000000000000000000100071323636106000205240ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QAbstractTableModel, QModelIndex, Qt from lisp.cues.cue import Cue # Application defended data-roles CueClassRole = Qt.UserRole + 1 class SimpleTableModel(QAbstractTableModel): """ Simple implementation of a QAbstractTableModel .. warning:: Currently this is a partial implementation of a complete R/W Qt model intended for a simple and straightforward usage. """ def __init__(self, columns): super().__init__() self.columns = columns self.rows = [] def appendRow(self, *values): row = len(self.rows) - 1 self.beginInsertRows(QModelIndex(), row, row) self.rows.append(list(values)) self.endInsertRows() def removeRow(self, row, parent=QModelIndex()): if -1 < row < len(self.rows): self.beginRemoveRows(parent, row, row) self.rows.pop(row) self.endRemoveRows() return True return False def rowCount(self, parent=QModelIndex()): return len(self.rows) def columnCount(self, parent=QModelIndex()): return len(self.columns) def headerData(self, section, orientation, role=Qt.DisplayRole): if role == Qt.DisplayRole and orientation == Qt.Horizontal: if section < len(self.columns): return self.columns[section] else: return section + 1 if role == Qt.SizeHintRole and orientation == Qt.Vertical: return 0 def data(self, index, role=Qt.DisplayRole): if index.isValid(): if role == Qt.DisplayRole or role == Qt.EditRole: return self.rows[index.row()][index.column()] elif role == Qt.TextAlignmentRole: return Qt.AlignCenter def setData(self, index, value, role=Qt.DisplayRole): if index.isValid(): if role == Qt.DisplayRole or role == Qt.EditRole: self.rows[index.row()][index.column()] = value self.dataChanged.emit(self.index(index.row(), 0), self.index(index.row(), index.column()), [Qt.DisplayRole, Qt.EditRole]) return True return False def flags(self, index): return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable class SimpleCueListModel(SimpleTableModel): """Extension of SimpleTableModel supporting the CueClassRole for rows.""" def __init__(self, columns): super().__init__(columns) self.rows_cc = [] def appendRow(self, cue_class, *values): self.rows_cc.append(cue_class) super().appendRow(*values) def removeRow(self, row, parent=None): if super().removeRow(row): self.rows_cc.pop(row) def data(self, index, role=Qt.DisplayRole): if role == CueClassRole and index.isValid: return self.rows_cc[index.row()] return super().data(index, role) def setData(self, index, value, role=Qt.DisplayRole): if role == CueClassRole and index.isValid(): if issubclass(value, Cue): self.rows_cc[index.row()] = value return True return False return super().setData(index, value, role) linux-show-player-0.5.1/lisp/ui/settings/000077500000000000000000000000001323636106000203505ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/settings/__init__.py000066400000000000000000000000001323636106000224470ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/settings/app_settings.py000066400000000000000000000061031323636106000234220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtWidgets import QDialog, QListWidget, QStackedWidget, \ QDialogButtonBox from lisp.core.util import deep_update from lisp.ui.ui_utils import translate class AppSettings(QDialog): SettingsWidgets = [] def __init__(self, conf, **kwargs): super().__init__(**kwargs) self.conf = conf self.setWindowTitle(translate('AppSettings', 'LiSP preferences')) self.setWindowModality(QtCore.Qt.ApplicationModal) self.setMaximumSize(635, 530) self.setMinimumSize(635, 530) self.resize(635, 530) self.listWidget = QListWidget(self) self.listWidget.setGeometry(QtCore.QRect(5, 10, 185, 470)) self.sections = QStackedWidget(self) self.sections.setGeometry(QtCore.QRect(200, 10, 430, 470)) for widget in self.SettingsWidgets: widget = widget(parent=self) widget.resize(430, 465) widget.load_settings(self.conf) self.listWidget.addItem(translate('SettingsPageName', widget.Name)) self.sections.addWidget(widget) if self.SettingsWidgets: self.listWidget.setCurrentRow(0) self.listWidget.currentItemChanged.connect(self._change_page) self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setGeometry(10, 495, 615, 30) self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.dialogButtons.rejected.connect(self.reject) self.dialogButtons.accepted.connect(self.accept) def get_configuraton(self): conf = {} for n in range(self.sections.count()): widget = self.sections.widget(n) newconf = widget.get_settings() deep_update(conf, newconf) return conf @classmethod def register_settings_widget(cls, widget): if widget not in cls.SettingsWidgets: cls.SettingsWidgets.append(widget) @classmethod def unregister_settings_widget(cls, widget): if widget in cls.SettingsWidgets: cls.SettingsWidgets.remove(widget) def _change_page(self, current, previous): if not current: current = previous self.sections.setCurrentIndex(self.listWidget.row(current)) linux-show-player-0.5.1/lisp/ui/settings/cue_settings.py000066400000000000000000000105761323636106000234270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from copy import deepcopy from PyQt5 import QtCore from PyQt5.QtWidgets import QDialog, QTabWidget, QDialogButtonBox from lisp.core.class_based_registry import ClassBasedRegistry from lisp.core.singleton import Singleton from lisp.core.util import deep_update from lisp.cues.cue import Cue from lisp.ui.settings.settings_page import SettingsPage, CueSettingsPage from lisp.ui.ui_utils import translate class CueSettingsRegistry(ClassBasedRegistry, metaclass=Singleton): def add_item(self, item, ref_class=Cue): if not issubclass(item, SettingsPage): raise TypeError('item must be a SettingPage subclass, ' 'not {0}'.format(item.__name__)) if not issubclass(ref_class, Cue): raise TypeError('ref_class must be Cue or a subclass, not {0}' .format(ref_class.__name__)) return super().add_item(item, ref_class) def filter(self, ref_class=Cue): return super().filter(ref_class) def clear_class(self, ref_class=Cue): return super().filter(ref_class) class CueSettings(QDialog): on_apply = QtCore.pyqtSignal(dict) def __init__(self, cue=None, cue_class=None, **kwargs): """ :param cue: Target cue, or None for multi-editing :param cue_class: when cue is None, used to specify the reference class """ super().__init__(**kwargs) if cue is not None: cue_class = cue.__class__ cue_properties = deepcopy(cue.properties()) self.setWindowTitle(cue_properties['name']) else: cue_properties = {} if cue_class is None: cue_class = Cue self.setWindowModality(QtCore.Qt.ApplicationModal) self.setMaximumSize(635, 530) self.setMinimumSize(635, 530) self.resize(635, 530) self.sections = QTabWidget(self) self.sections.setGeometry(QtCore.QRect(5, 10, 625, 470)) def sk(widget): # Sort-Key function return translate('SettingsPageName', widget.Name) for widget in sorted(CueSettingsRegistry().filter(cue_class), key=sk): if issubclass(widget, CueSettingsPage): settings_widget = widget(cue_class) else: settings_widget = widget() settings_widget.load_settings(cue_properties) settings_widget.enable_check(cue is None) self.sections.addTab(settings_widget, translate('SettingsPageName', settings_widget.Name)) self.dialogButtons = QDialogButtonBox(self) self.dialogButtons.setGeometry(10, 490, 615, 30) self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Apply) self.dialogButtons.rejected.connect(self.reject) self.dialogButtons.accepted.connect(self.accept) apply = self.dialogButtons.button(QDialogButtonBox.Apply) apply.clicked.connect(self.apply) def load_settings(self, settings): for n in range(self.sections.count()): self.sections.widget(n).load_settings(settings) def get_settings(self): settings = {} for n in range(self.sections.count()): deep_update(settings, self.sections.widget(n).get_settings()) return settings def apply(self): self.on_apply.emit(self.get_settings()) def accept(self): self.apply() super().accept() linux-show-player-0.5.1/lisp/ui/settings/pages/000077500000000000000000000000001323636106000214475ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/settings/pages/__init__.py000066400000000000000000000000001323636106000235460ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/settings/pages/app_general.py000066400000000000000000000065511323636106000243050ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QCheckBox, QComboBox from lisp import layouts from lisp.ui.styles import styles from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class AppGeneral(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'General') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) # Startup layout self.layoutGroup = QGroupBox(self) self.layoutGroup.setTitle( translate('AppGeneralSettings', 'Startup layout')) self.layoutGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.layoutGroup) self.startupDialogCheck = QCheckBox(self.layoutGroup) self.startupDialogCheck.setText( translate('AppGeneralSettings', 'Use startup dialog')) self.layoutGroup.layout().addWidget(self.startupDialogCheck) self.layoutCombo = QComboBox(self.layoutGroup) self.layoutCombo.addItems([lay.NAME for lay in layouts.get_layouts()]) self.layoutGroup.layout().addWidget(self.layoutCombo) self.startupDialogCheck.clicked.connect( lambda check: self.layoutCombo.setEnabled(not check)) # Application style self.themeGroup = QGroupBox(self) self.themeGroup.setTitle( translate('AppGeneralSettings', 'Application theme')) self.themeGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.themeGroup) self.themeCombo = QComboBox(self.themeGroup) self.themeCombo.addItems(styles.styles()) self.themeGroup.layout().addWidget(self.themeCombo) def get_settings(self): conf = {'Layout': {}, 'Theme': {}} if self.startupDialogCheck.isChecked(): conf['Layout']['default'] = 'NoDefault' else: conf['Layout']['default'] = self.layoutCombo.currentText() conf['Theme']['theme'] = self.themeCombo.currentText() styles.apply_style(self.themeCombo.currentText()) return conf def load_settings(self, settings): if 'default' in settings['Layout']: if settings['Layout']['default'].lower() == 'nodefault': self.startupDialogCheck.setChecked(True) self.layoutCombo.setEnabled(False) else: self.layoutCombo.setCurrentText(settings['Layout']['default']) if 'theme' in settings['Theme']: self.themeCombo.setCurrentText(settings['Theme']['theme']) linux-show-player-0.5.1/lisp/ui/settings/pages/cue_app_settings.py000066400000000000000000000055441323636106000253650ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QT_TRANSLATE_NOOP, Qt from PyQt5.QtWidgets import QVBoxLayout, QGroupBox from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate from lisp.ui.widgets.fade_edit import FadeEdit class CueAppSettings(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Cue Settings') def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) # Interrupt self.interruptGroup = QGroupBox(self) self.interruptGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.interruptGroup) self.interruptFadeEdit = FadeEdit(self.interruptGroup) self.interruptGroup.layout().addWidget(self.interruptFadeEdit) # Action self.actionGroup = QGroupBox(self) self.actionGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.actionGroup) self.fadeActionEdit = FadeEdit(self.actionGroup) self.actionGroup.layout().addWidget(self.fadeActionEdit) self.retranslateUi() def retranslateUi(self): self.interruptGroup.setTitle(translate('CueSettings', 'Interrupt Fade')) self.actionGroup.setTitle(translate('CueSettings', 'Fade Action')) def load_settings(self, settings): # Interrupt self.interruptFadeEdit.setDuration( float(settings['Cue'].get('interruptfade', 0))) self.interruptFadeEdit.setFadeType( settings['Cue'].get('interruptfadetype', '')) # FadeAction self.fadeActionEdit.setDuration( float(settings['Cue'].get('fadeactionduration', 0))) self.fadeActionEdit.setFadeType( settings['Cue'].get('fadeactiontype', '')) def get_settings(self): return {'Cue': { 'interruptfade': str(self.interruptFadeEdit.duration()), 'interruptfadetype': self.interruptFadeEdit.fadeType(), 'fadeactionduration': str(self.fadeActionEdit.duration()), 'fadeactiontype': self.fadeActionEdit.fadeType() }} linux-show-player-0.5.1/lisp/ui/settings/pages/cue_appearance.py000066400000000000000000000140671323636106000247640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QVBoxLayout, QGroupBox, QHBoxLayout, QTextEdit, \ QSpinBox, QLabel, QLineEdit from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.widgets import QColorButton from lisp.ui.ui_utils import translate class Appearance(SettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Appearance') def __init__(self, **kwargs): super().__init__() self.setLayout(QVBoxLayout()) # Name self.cueNameGroup = QGroupBox(self) self.cueNameGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.cueNameGroup) self.cueNameEdit = QLineEdit(self.cueNameGroup) self.cueNameGroup.layout().addWidget(self.cueNameEdit) # Description self.cueDescriptionGroup = QGroupBox(self) self.cueDescriptionGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.cueDescriptionGroup) self.cueDescriptionEdit = QTextEdit(self.cueNameGroup) self.cueDescriptionGroup.layout().addWidget(self.cueDescriptionEdit) # Font self.fontSizeGroup = QGroupBox(self) self.fontSizeGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.fontSizeGroup) self.fontSizeSpin = QSpinBox(self.fontSizeGroup) self.fontSizeSpin.setValue(QLabel().fontInfo().pointSize()) self.fontSizeGroup.layout().addWidget(self.fontSizeSpin) # Color self.colorGroup = QGroupBox(self) self.colorGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.colorGroup) self.colorBButton = QColorButton(self.colorGroup) self.colorFButton = QColorButton(self.colorGroup) self.colorGroup.layout().addWidget(self.colorBButton) self.colorGroup.layout().addWidget(self.colorFButton) # Warning self.warning = QLabel(self) self.warning.setText(translate('CueAppearanceSettings', 'The appearance depends on the layout')) self.warning.setAlignment(Qt.AlignCenter) self.warning.setStyleSheet("color: #FFA500; font-weight: bold") self.layout().addWidget(self.warning) self.retranslateUi() def retranslateUi(self): self.cueNameGroup.setTitle( translate('CueAppearanceSettings', 'Cue name')) self.cueNameEdit.setText(translate('CueAppearanceSettings', 'NoName')) self.cueDescriptionGroup.setTitle( translate('CueAppearanceSettings', 'Description/Note')) self.fontSizeGroup.setTitle( translate('CueAppearanceSettings', 'Set Font Size')) self.colorGroup.setTitle(translate('CueAppearanceSettings', 'Color')) self.colorBButton.setText( translate('CueAppearanceSettings', 'Select background color')) self.colorFButton.setText( translate('CueAppearanceSettings', 'Select font color')) def enable_check(self, enable): self.cueNameGroup.setCheckable(enable) self.cueNameGroup.setChecked(False) self.cueDescriptionGroup.setChecked(enable) self.cueDescriptionGroup.setChecked(False) self.fontSizeGroup.setCheckable(enable) self.fontSizeGroup.setChecked(False) self.colorGroup.setCheckable(enable) self.colorGroup.setChecked(False) def get_settings(self): conf = {} style = {} checkable = self.cueNameGroup.isCheckable() if not (checkable and not self.cueNameGroup.isChecked()): conf['name'] = self.cueNameEdit.text() if not (checkable and not self.cueDescriptionGroup.isChecked()): conf['description'] = self.cueDescriptionEdit.toPlainText() if not (checkable and not self.colorGroup.isChecked()): if self.colorBButton.color() is not None: style['background'] = self.colorBButton.color() if self.colorFButton.color() is not None: style['color'] = self.colorFButton.color() if not (checkable and not self.fontSizeGroup.isChecked()): style['font-size'] = str(self.fontSizeSpin.value()) + 'pt' if style: conf['stylesheet'] = dict_to_css(style) return conf def load_settings(self, settings): if 'name' in settings: self.cueNameEdit.setText(settings['name']) if 'description' in settings: self.cueDescriptionEdit.setText(settings['description']) if 'stylesheet' in settings: settings = css_to_dict(settings['stylesheet']) if 'background' in settings: self.colorBButton.setColor(settings['background']) if 'color' in settings: self.colorFButton.setColor(settings['color']) if 'font-size' in settings: # [:-2] for removing "pt" self.fontSizeSpin.setValue(int(settings['font-size'][:-2])) def css_to_dict(css): dict = {} css = css.strip() for attribute in css.split(';'): try: name, value = attribute.split(':') dict[name.strip()] = value.strip() except: pass return dict def dict_to_css(css_dict): css = '' for name in css_dict: css += name + ':' + str(css_dict[name]) + ';' return css linux-show-player-0.5.1/lisp/ui/settings/pages/cue_general.py000066400000000000000000000246511323636106000243020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QLabel, QComboBox,\ QVBoxLayout, QDoubleSpinBox, QTabWidget, QWidget from lisp.cues.cue import CueNextAction, CueAction from lisp.ui.settings.settings_page import CueSettingsPage from lisp.ui.ui_utils import translate from lisp.ui.widgets import FadeComboBox from lisp.ui.widgets.fade_edit import FadeEdit class CueGeneralSettings(CueSettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Cue') def __init__(self, cue_class, **kwargs): super().__init__(cue_class, **kwargs) self.setLayout(QVBoxLayout()) self.tabWidget = QTabWidget(self) self.layout().addWidget(self.tabWidget) # TAB 1 (Behaviours) self.tab_1 = QWidget(self.tabWidget) self.tab_1.setLayout(QVBoxLayout()) self.tabWidget.addTab(self.tab_1, '1') # Start-Action self.startActionGroup = QGroupBox(self.tab_1) self.startActionGroup.setLayout(QHBoxLayout()) self.tab_1.layout().addWidget(self.startActionGroup) self.startActionCombo = QComboBox(self.startActionGroup) for action in [CueAction.Start, CueAction.FadeInStart]: if action in cue_class.CueActions: self.startActionCombo.addItem( translate('CueAction', action.name), action.value) self.startActionCombo.setEnabled(self.startActionCombo.count() > 1) self.startActionGroup.layout().addWidget(self.startActionCombo) self.startActionLabel = QLabel(self.startActionGroup) self.startActionLabel.setAlignment(Qt.AlignCenter) self.startActionGroup.layout().addWidget(self.startActionLabel) # Stop-Action self.stopActionGroup = QGroupBox(self.tab_1) self.stopActionGroup.setLayout(QHBoxLayout()) self.tab_1.layout().addWidget(self.stopActionGroup) self.stopActionCombo = QComboBox(self.stopActionGroup) for action in [CueAction.Stop, CueAction.Pause, CueAction.FadeOutStop, CueAction.FadeOutPause]: if action in cue_class.CueActions: self.stopActionCombo.addItem( translate('CueAction', action.name), action.value) self.stopActionCombo.setEnabled(self.stopActionCombo.count() > 1) self.stopActionGroup.layout().addWidget(self.stopActionCombo) self.stopActionLabel = QLabel(self.stopActionGroup) self.stopActionLabel.setAlignment(Qt.AlignCenter) self.stopActionGroup.layout().addWidget(self.stopActionLabel) self.tab_1.layout().addSpacing(150) self.tab_1.setEnabled(self.stopActionCombo.isEnabled() and self.startActionCombo.isEnabled()) # TAB 2 (Pre/Post Wait) self.tab_2 = QWidget(self.tabWidget) self.tab_2.setLayout(QVBoxLayout()) self.tabWidget.addTab(self.tab_2, '2') # Pre wait self.preWaitGroup = QGroupBox(self.tab_2) self.preWaitGroup.setLayout(QHBoxLayout()) self.tab_2.layout().addWidget(self.preWaitGroup) self.preWaitSpin = QDoubleSpinBox(self.preWaitGroup) self.preWaitSpin.setMaximum(3600 * 24) self.preWaitGroup.layout().addWidget(self.preWaitSpin) self.preWaitLabel = QLabel(self.preWaitGroup) self.preWaitLabel.setAlignment(Qt.AlignCenter) self.preWaitGroup.layout().addWidget(self.preWaitLabel) # Post wait self.postWaitGroup = QGroupBox(self.tab_2) self.postWaitGroup.setLayout(QHBoxLayout()) self.tab_2.layout().addWidget(self.postWaitGroup) self.postWaitSpin = QDoubleSpinBox(self.postWaitGroup) self.postWaitSpin.setMaximum(3600 * 24) self.postWaitGroup.layout().addWidget(self.postWaitSpin) self.postWaitLabel = QLabel(self.postWaitGroup) self.postWaitLabel.setAlignment(Qt.AlignCenter) self.postWaitGroup.layout().addWidget(self.postWaitLabel) # Next action self.nextActionGroup = QGroupBox(self.tab_2) self.nextActionGroup.setLayout(QHBoxLayout()) self.tab_2.layout().addWidget(self.nextActionGroup) self.nextActionCombo = QComboBox(self.nextActionGroup) for action in CueNextAction: self.nextActionCombo.addItem( translate('CueNextAction', action.name), action.value) self.nextActionGroup.layout().addWidget(self.nextActionCombo) # TAB 3 (Fade In/Out) self.tab_3 = QWidget(self.tabWidget) self.tab_3.setLayout(QVBoxLayout()) self.tabWidget.addTab(self.tab_3, '3') # FadeIn self.fadeInGroup = QGroupBox(self.tab_3) self.fadeInGroup.setEnabled( CueAction.FadeInStart in cue_class.CueActions ) self.fadeInGroup.setLayout(QHBoxLayout()) self.tab_3.layout().addWidget(self.fadeInGroup) self.fadeInEdit = FadeEdit(self.fadeInGroup, mode=FadeComboBox.Mode.FadeIn) self.fadeInGroup.layout().addWidget(self.fadeInEdit) # FadeOut self.fadeOutGroup = QGroupBox(self.tab_3) self.fadeOutGroup.setEnabled( CueAction.FadeOutPause in cue_class.CueActions or CueAction.FadeOutStop in cue_class.CueActions ) self.fadeOutGroup.setLayout(QHBoxLayout()) self.tab_3.layout().addWidget(self.fadeOutGroup) self.fadeOutEdit = FadeEdit(self.fadeOutGroup, mode=FadeComboBox.Mode.FadeOut) self.fadeOutGroup.layout().addWidget(self.fadeOutEdit) self.retranslateUi() def retranslateUi(self): # Tabs self.tabWidget.setTabText(0, translate('CueSettings', 'Behaviours')) self.tabWidget.setTabText(1, translate('CueSettings', 'Pre/Post Wait')) self.tabWidget.setTabText(2, translate('CueSettings', 'Fade In/Out')) # Start-Action self.startActionGroup.setTitle( translate('CueSettings', 'Start action')) self.startActionLabel.setText( translate('CueSettings', 'Default action to start the cue')) # Stop-Action self.stopActionGroup.setTitle( translate('CueSettings', 'Stop action')) self.stopActionLabel.setText( translate('CueSettings', 'Default action to stop the cue')) # PreWait self.preWaitGroup.setTitle(translate('CueSettings', 'Pre wait')) self.preWaitLabel.setText( translate('CueSettings', 'Wait before cue execution')) # PostWait self.postWaitGroup.setTitle(translate('CueSettings', 'Post wait')) self.postWaitLabel.setText( translate('CueSettings', 'Wait after cue execution')) # NextAction self.nextActionGroup.setTitle(translate('CueSettings', 'Next action')) # FadeIn/Out self.fadeInGroup.setTitle(translate('FadeSettings', 'Fade In')) self.fadeOutGroup.setTitle(translate('FadeSettings', 'Fade Out')) def load_settings(self, settings): self.startActionCombo.setCurrentText( translate('CueAction', settings.get('default_start_action', ''))) self.stopActionCombo.setCurrentText( translate('CueAction', settings.get('default_stop_action', ''))) self.preWaitSpin.setValue(settings.get('pre_wait', 0)) self.postWaitSpin.setValue(settings.get('post_wait', 0)) self.nextActionCombo.setCurrentText( translate('CueNextAction', settings.get('next_action', ''))) self.fadeInEdit.setFadeType(settings.get('fadein_type', '')) self.fadeInEdit.setDuration(settings.get('fadein_duration', 0)) self.fadeOutEdit.setFadeType(settings.get('fadeout_type', '')) self.fadeOutEdit.setDuration(settings.get('fadeout_duration', 0)) def enable_check(self, enable): self.startActionGroup.setCheckable(enable) self.startActionGroup.setChecked(False) self.stopActionGroup.setCheckable(enable) self.stopActionGroup.setChecked(False) self.preWaitGroup.setCheckable(enable) self.preWaitGroup.setChecked(False) self.postWaitGroup.setCheckable(enable) self.postWaitGroup.setChecked(False) self.nextActionGroup.setCheckable(enable) self.nextActionGroup.setChecked(False) self.fadeInGroup.setCheckable(enable) self.fadeInGroup.setChecked(False) self.fadeOutGroup.setCheckable(enable) self.fadeOutGroup.setChecked(False) def get_settings(self): conf = {} checkable = self.preWaitGroup.isCheckable() if not (checkable and not self.startActionGroup.isChecked()): if self.startActionCombo.isEnabled(): conf['default_start_action'] = self.startActionCombo.currentData() if not (checkable and not self.stopActionGroup.isChecked()): if self.stopActionCombo.isEnabled(): conf['default_stop_action'] = self.stopActionCombo.currentData() if not (checkable and not self.preWaitGroup.isChecked()): conf['pre_wait'] = self.preWaitSpin.value() if not (checkable and not self.postWaitGroup.isChecked()): conf['post_wait'] = self.postWaitSpin.value() if not (checkable and not self.nextActionGroup.isChecked()): conf['next_action'] = self.nextActionCombo.currentData() if not (checkable and not self.fadeInGroup.isChecked()): conf['fadein_type'] = self.fadeInEdit.fadeType() conf['fadein_duration'] = self.fadeInEdit.duration() if not (checkable and not self.fadeInGroup.isChecked()): conf['fadeout_type'] = self.fadeOutEdit.fadeType() conf['fadeout_duration'] = self.fadeOutEdit.duration() return conf linux-show-player-0.5.1/lisp/ui/settings/pages/media_cue_settings.py000066400000000000000000000115401323636106000256550ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QTime, Qt from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QTimeEdit, QLabel, \ QSpinBox, QVBoxLayout from lisp.ui.settings.settings_page import SettingsPage from lisp.ui.ui_utils import translate class MediaCueSettings(SettingsPage): Name = 'Media-Cue' def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout(self)) # Start time self.startGroup = QGroupBox(self) self.startGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.startGroup) self.startEdit = QTimeEdit(self.startGroup) self.startEdit.setDisplayFormat('HH.mm.ss.zzz') self.startGroup.layout().addWidget(self.startEdit) self.startLabel = QLabel(self.startGroup) self.startLabel.setAlignment(Qt.AlignCenter) self.startGroup.layout().addWidget(self.startLabel) # Stop time self.stopGroup = QGroupBox(self) self.stopGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.stopGroup) self.stopEdit = QTimeEdit(self.stopGroup) self.stopEdit.setDisplayFormat('HH.mm.ss.zzz') self.stopGroup.layout().addWidget(self.stopEdit) self.stopLabel = QLabel(self.stopGroup) self.stopLabel.setAlignment(Qt.AlignCenter) self.stopGroup.layout().addWidget(self.stopLabel) # Loop self.loopGroup = QGroupBox(self) self.loopGroup.setLayout(QHBoxLayout()) self.layout().addWidget(self.loopGroup) self.spinLoop = QSpinBox(self.loopGroup) self.spinLoop.setRange(-1, 1000000) self.loopGroup.layout().addWidget(self.spinLoop) self.loopLabel = QLabel(self.loopGroup) self.loopLabel.setAlignment(Qt.AlignCenter) self.loopGroup.layout().addWidget(self.loopLabel) self.retranslateUi() def retranslateUi(self): self.startGroup.setTitle(translate('MediaCueSettings', 'Start time')) self.stopLabel.setText( translate('MediaCueSettings', 'Stop position of the media')) self.stopGroup.setTitle(translate('MediaCueSettings', 'Stop time')) self.startLabel.setText( translate('MediaCueSettings', 'Start position of the media')) self.loopGroup.setTitle(translate('MediaCueSettings', 'Loop')) self.loopLabel.setText( translate('MediaCueSettings', 'Repetition after first play ' '(-1 = infinite)')) def get_settings(self): conf = {'_media_': {}} checkable = self.startGroup.isCheckable() if not (checkable and not self.startGroup.isChecked()): time = self.startEdit.time().msecsSinceStartOfDay() conf['_media_']['start_time'] = time if not (checkable and not self.stopGroup.isChecked()): time = self.stopEdit.time().msecsSinceStartOfDay() conf['_media_']['stop_time'] = time if not (checkable and not self.loopGroup.isChecked()): conf['_media_']['loop'] = self.spinLoop.value() return conf def enable_check(self, enable): self.startGroup.setCheckable(enable) self.startGroup.setChecked(False) self.stopGroup.setCheckable(enable) self.stopGroup.setChecked(False) self.loopGroup.setCheckable(enable) self.loopGroup.setChecked(False) def load_settings(self, settings): if '_media_' in settings: if 'loop' in settings['_media_']: self.spinLoop.setValue(settings['_media_']['loop']) if 'start_time' in settings['_media_']: t = self._to_qtime(settings['_media_']['start_time']) self.startEdit.setTime(t) if 'stop_time' in settings['_media_']: t = self._to_qtime(settings['_media_']['stop_time']) self.stopEdit.setTime(t) t = self._to_qtime(settings['_media_'].get('duration', 0)) self.startEdit.setMaximumTime(t) self.stopEdit.setMaximumTime(t) def _to_qtime(self, m_seconds): return QTime.fromMSecsSinceStartOfDay(m_seconds) linux-show-player-0.5.1/lisp/ui/settings/settings_page.py000066400000000000000000000024031323636106000235550ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtWidgets import QWidget class SettingsPage(QWidget): Name = 'Page' def enable_check(self, enabled): """Enable option check""" def get_settings(self): """Return the current settings.""" return {} def load_settings(self, settings): """Load the settings.""" class CueSettingsPage(SettingsPage): Name = 'Cue page' def __init__(self, cue_class, **kwargs): super().__init__(**kwargs) self._cue_class = cue_class linux-show-player-0.5.1/lisp/ui/styles/000077500000000000000000000000001323636106000200335ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/__init__.py000066400000000000000000000000001323636106000221320ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/dark/000077500000000000000000000000001323636106000207545ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/dark/__init__.py000066400000000000000000000000001323636106000230530ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/dark/assetes.py000066400000000000000000001775621323636106000230170ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Resource object code # # Created by: The Resource Compiler for PyQt5 (Qt v5.5.1) # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore qt_resource_data = b"\ \x00\x00\x03\x25\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x02\xa2\x49\x44\ \x41\x54\x38\x8d\xa5\x93\xcb\x6a\x53\x61\x10\xc7\xff\x33\xf3\x9d\ \xef\x9c\xd0\xa6\x4d\xf1\x52\x51\x62\x6b\x4c\x94\xd2\x14\xc5\x82\ \x88\xba\x88\x16\x4b\x25\x1b\x05\x15\x74\xe1\x13\x78\xdb\xb8\xd3\ \x8a\x3e\x81\x97\x37\x70\x23\xa8\xe0\xb6\x44\xb4\x20\xfa\x00\x62\ \xba\xaa\x0a\x56\xbc\x2b\xa5\x69\x4f\x6e\xe7\x9c\x6f\x5c\x34\xb6\ \xf5\xb2\x73\x60\x18\x18\x7e\x33\xcc\xf0\x9f\x21\x55\xc5\xff\x98\ \xf9\x33\xb1\xae\x50\xe8\x09\x9a\xcd\x4b\x44\x7c\x04\x8a\x5d\x00\ \x00\xc2\x4b\x55\xf7\xb8\x19\x04\x37\x7f\xcc\xce\xd6\xd6\xf2\xf4\ \x6b\x02\x22\xa2\x2d\x5b\xb6\x1e\x06\xe9\x3d\x11\xe9\x11\x61\x1f\ \xa0\x0e\xa6\x48\x12\xd7\x4a\x92\xa4\x06\xa5\xd3\x1f\x3e\xcc\x3d\ \xd5\x4e\x21\xa9\x2a\x88\x88\x37\x6f\xde\x3a\xc6\x82\x47\x9e\xe7\ \x75\x19\x63\xc0\xcc\x20\x5a\x6e\xa0\xaa\x70\xce\x21\x8e\x63\x44\ \x51\x14\xba\x04\xc7\x3f\x7e\x9c\x7b\xa2\xaa\x8e\x00\xd0\xa6\x7c\ \x7e\xbd\x34\x5b\x33\xd6\xf7\x37\x78\x9e\x07\x11\xf9\xe7\xbe\x49\ \x92\x20\x8a\x22\xb4\x5b\xad\x6f\x49\xe0\x0f\x7f\x7e\xfd\xfa\xbb\ \x8c\x8e\x8e\x7a\xcd\xda\xd2\x65\x31\xe6\xb0\xb5\xd6\x18\x63\x40\ \x20\xf4\x6e\x18\xc4\xce\xfd\x67\xd0\xbf\x6d\x14\x8d\xc5\x6f\x68\ \xd7\x6b\xcb\x53\x81\xa0\xaa\x86\xa3\x38\x2e\x14\xf2\xcf\x39\x97\ \xcb\x19\x62\x1a\x33\x46\x7c\x11\x01\x11\x21\xd3\xbf\x1d\x7b\x8f\ \x5d\xc1\xe0\xd0\x3e\x0c\x0e\xed\xc7\xde\x63\x57\xd1\xbb\x71\x1b\ \x88\x08\x22\x02\x63\xc4\x27\xa6\xb1\x5c\x2e\x67\xd8\x5a\xeb\x11\ \xd1\x08\xb3\xac\xec\x3d\xb0\xfb\x28\x52\xbe\x45\x77\xe0\xa3\x3b\ \xe5\xa3\x2b\x65\x31\xb0\x6b\x02\x44\x04\x66\x06\xb3\x80\x88\x46\ \xac\xb5\x9e\xc9\x66\xb3\xd2\x91\x6a\xc5\x3b\xc0\xaa\x54\xc4\x60\ \x36\xc0\xaf\x5c\x27\x66\xb3\x59\xe1\x4c\x26\xe3\x58\xb8\xba\x0a\ \x13\xe6\x5e\x55\x10\x36\x5a\x08\x9b\xed\x65\xaf\x37\xf0\xbe\x5a\ \xe9\xd4\x29\x00\x05\x0b\x57\x33\x99\x8c\x33\xe9\x74\x3a\x09\x6c\ \xf0\x2c\x8a\xa3\x3d\x00\x7c\x22\xc2\xc2\x97\x59\xbc\xac\xdc\xc4\ \x40\x71\x1c\xaa\x8a\xb9\x6a\x05\x0b\x5f\xdf\xac\xca\xea\x5c\x2b\ \xb0\xc1\xb3\x74\x3a\x9d\xd0\xf4\xf4\xb4\xa9\x54\x2a\xb9\xfb\x0f\ \x1e\xbe\xe8\xea\xea\x5e\x2f\x22\x70\xce\xfd\x53\x46\x66\x46\xe2\ \x12\x84\x4b\x4b\xdf\x4f\x9d\x3c\x71\x60\x7c\x7c\xfc\x2d\x97\x4a\ \xa5\xb8\x5c\x2e\x7f\xda\x51\xc8\x9f\xab\xd7\xc3\x50\x55\x61\x3c\ \x0f\x2c\x02\x62\x06\x31\x83\x45\x60\x3c\x0f\xaa\x8a\x7a\x18\x86\ \x3b\x0a\xf9\x73\xe5\x72\xf9\x53\xa9\x54\x8a\x57\x4e\xb9\x5a\xad\ \xf6\x4f\x4e\x5e\x3f\x54\x9d\x79\x75\xc7\x78\x5e\xda\x5a\xeb\x8b\ \x98\xce\x01\xc5\x68\xb7\xdb\xad\x38\x8a\x16\x8b\xc3\x23\xe7\x6f\ \xdc\xb8\x36\x5d\x2c\x16\xbf\xfc\xf6\x0b\x00\x30\x3f\x3f\xdf\x3b\ \x35\x35\x35\x70\xeb\xf6\xed\xb3\xb5\x85\xda\xc1\x38\x49\x86\x01\ \xc0\x88\xcc\xf4\xf4\xf6\x3c\xbf\x78\xe1\xc2\xdd\x89\x89\x89\x77\ \x7d\x7d\x7d\x0b\x7f\x3d\xd3\x1a\xb3\x00\xd2\x8d\x46\x23\x60\xe6\ \x00\x00\x9c\x73\xcd\x54\x2a\xd5\x04\xb0\x08\xa0\xbd\x16\xfe\x09\ \x98\x59\x08\x23\xbf\xfa\x7e\x03\x00\x00\x00\x00\x49\x45\x4e\x44\ \xae\x42\x60\x82\ \x00\x00\x01\xe7\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\x64\x49\x44\ \x41\x54\x38\x8d\x8d\x93\xbf\x2f\x44\x41\x10\xc7\x3f\xfb\xbc\xe4\ \xdd\x49\x5e\x84\x8b\x46\x45\xad\xe1\xd4\xae\x90\x88\x44\x14\x34\ \x14\x7a\xaa\xab\x65\xee\x1f\x98\x5e\x47\x34\x1a\xb9\xe8\x88\x46\ \x27\xfe\x00\x12\xf4\x12\x74\x7e\x85\x27\xee\x5d\xac\x3d\xc5\xed\ \x89\x3c\xc7\xbd\xed\x66\x67\x3e\xdf\x99\xd9\xd9\x31\xd5\x6a\x35\ \x8a\xe3\xb8\xe2\x9c\x2b\x03\x45\xf2\x1d\x6b\x8c\x29\x5a\x6b\x37\ \xc3\x38\x8e\x2b\x40\x09\xd8\x2e\x14\x0a\x49\x4f\xd2\xda\xd0\x5a\ \xbb\x0e\x4c\x84\x61\xb8\x11\x3a\xe7\xca\xc6\x98\x1d\x55\x7d\xee\ \x05\x8b\x88\x01\x96\x80\xc1\x56\xab\x75\x05\x04\x01\x50\x8c\xa2\ \xe8\x25\x67\xe9\x33\xc0\xb8\x6f\x61\x17\x70\x41\x4e\x10\x11\x99\ \x04\xa6\xbd\x79\x10\x45\xd1\x2d\x40\x2e\x01\x11\x19\x05\x16\xbc\ \x79\xa2\xaa\x97\x1d\x5f\x90\x09\x9c\x10\x91\xa9\xcc\x5d\x09\x58\ \x01\xfa\x80\x0b\x55\x3d\xf9\xe9\x0f\x7e\x04\x8e\x01\x8b\xc0\xbc\ \xcf\x88\x88\xf4\x03\xab\xb4\xc7\x7b\x03\x1c\x66\xab\xfb\x16\x50\ \xd5\x6b\xe0\xd2\x67\x5a\x16\x91\x61\x9f\x79\x08\x78\x02\xea\xaa\ \x6a\xb3\x02\x61\xc6\x3e\xa4\xfd\x27\x46\x80\x75\xef\x6f\x00\x7b\ \xaa\xfa\x9e\x85\x7f\xbd\x81\xaa\x7e\x00\x75\xe0\xcd\xc3\x9f\xc0\ \xbe\xaa\x3e\x74\x83\x7f\x09\x78\x91\x57\x2f\x62\x81\x23\xdf\xda\ \x9f\xa7\xeb\x18\x55\xf5\x0e\xd8\x52\xd5\xf3\xff\xe0\x8e\x40\xa3\ \xd9\x6c\x0e\x74\x11\xb9\xff\x0f\xf4\x4c\xc3\xd4\x6a\xb5\x59\xa0\ \xe4\x9c\x3b\xce\xb3\x4c\x00\x69\x9a\xc6\x41\x10\xcc\x01\x8f\x61\ \x92\x24\xa7\x7e\x23\xd7\xd2\x34\xcd\xbb\xce\x0d\xe0\x2c\x49\x92\ \xd3\x2f\x7b\x24\x80\xb0\x0b\x30\xeb\x22\x00\x00\x00\x00\x49\x45\ \x4e\x44\xae\x42\x60\x82\ \x00\x00\x01\x59\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x00\xd6\x49\x44\ \x41\x54\x38\x8d\xc5\x93\x3d\x6a\xc3\x40\x10\x85\xbf\x9d\x88\x95\ \x14\x08\x29\x7c\x8a\xb4\x6e\x55\x1b\x1f\x21\x17\x08\xbe\x83\xb6\ \x52\x23\x74\x87\x90\x3a\x77\x08\xa9\xd5\xfa\x00\xb9\x43\x30\x26\ \x85\x56\x03\x1e\xa5\x51\x61\x42\x7e\x56\x10\xf0\xeb\xa6\xf8\xde\ \x7b\x0c\x3c\xd7\x34\xcd\xb5\xaa\x56\x66\xb6\x06\x4a\xd2\x34\x88\ \xc8\xde\x7b\xdf\x67\xaa\x5a\x01\x2b\xe0\xb1\x28\x8a\x8f\x14\x3a\ \xc6\x78\x03\x6c\x55\xb5\xca\xcc\x6c\xed\x9c\x7b\xea\xba\xee\x90\ \x98\x0e\x70\x08\x21\xbc\x4e\xd3\xf4\x20\x40\x99\xe7\xf9\x71\x01\ \x0c\xc0\xcc\x94\xb2\x14\xfc\xaa\xcb\x1b\x64\xe7\x47\x5d\xd7\xf7\ \xc0\xdd\xaf\x89\x22\x6f\x6d\xdb\x3e\x7f\xdb\x40\x44\xae\xfe\x4a\ \x34\xb3\xd3\x8f\x0d\xce\x9d\x53\x75\xf9\x27\xfe\x8b\xc1\x30\x8e\ \xe3\xed\x52\x70\x66\x06\x17\x42\xd8\x00\x2b\x33\x7b\x59\x32\x26\ \x11\xd9\x02\xef\x99\xf7\xbe\x9f\x17\xb9\x8b\x31\x26\xcf\x19\xd8\ \x7b\xef\xfb\x4f\xf0\xe2\x4c\x9c\x38\x97\xa4\xcd\x00\x00\x00\x00\ \x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x01\x60\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x0c\x00\x00\x00\x10\x08\x06\x00\x00\x00\x22\x61\x9e\x07\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\ \x0d\xd7\x01\x42\x28\x9b\x78\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdf\x07\x0b\x0a\x20\x07\x4c\xea\x6b\x9c\x00\x00\x00\xed\x49\x44\ \x41\x54\x28\xcf\xd5\x92\xb1\x4a\xc4\x50\x10\x45\xcf\x84\x6c\xd8\ \x15\x15\x53\x6c\xe3\x76\xc2\x22\x58\x8a\x60\x61\x61\x23\x82\x3f\ \x61\x91\x8f\x48\x93\x22\x1f\x10\x48\xb3\x4d\xba\x34\xe9\x62\x9b\ \x26\xf8\x17\xa9\x12\x08\x3c\xb0\xb5\x10\x02\x11\x22\xbc\x67\x93\ \x85\xb0\x20\xba\xb0\x8d\xb7\x9b\xe1\x9e\x99\xe1\x32\x62\x8c\x61\ \x1f\x59\xec\xa9\x83\x01\x47\xc0\xec\xaf\xc0\x59\x51\x14\xb7\x41\ \x10\x5c\x00\xf6\x6f\xc0\xbc\x69\x9a\xcb\x3c\xcf\xaf\xda\xb6\xbd\ \x4b\xd3\x74\xb9\xeb\x99\x16\x76\xd7\x75\xeb\x38\x8e\xaf\xfb\xbe\ \x57\x5a\xeb\xb7\xb2\x2c\x1f\xab\xaa\x3a\x01\x64\x6b\x92\x49\xac\ \x6b\xdf\xf7\xef\x95\x52\x1d\xf0\x3e\x9e\xb3\x74\x1c\xc7\xca\xb2\ \xec\x05\xe8\xa7\x1b\x56\x51\x14\xdd\x28\xa5\x6c\x60\x00\xe6\xc0\ \x31\x60\x0f\xc3\x70\xee\x79\xde\xd3\xd8\x43\x8c\x31\x56\x18\x86\ \x0f\x75\x5d\x3f\x6b\xad\x3f\x80\xaf\x71\xc8\x0c\x58\x00\x0b\x11\ \x39\x75\x5d\x77\x93\x24\xc9\xab\x18\x63\x64\x8c\x51\x7e\x88\x78\ \xdb\x37\xc0\xa7\xfc\x9f\xd7\x38\x1c\xf0\x0d\x04\x64\x51\x6a\x74\ \x9b\xd0\xd6\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x00\x88\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x18\x00\x00\x00\x11\x04\x03\x00\x00\x00\x35\x56\x71\x03\ \x00\x00\x00\x15\x50\x4c\x54\x45\xff\xff\xff\x63\x56\x4d\x63\x56\ \x4d\x63\x56\x4d\x63\x56\x4d\x63\x56\x4d\x63\x56\x4d\x05\x5f\x5b\ \xea\x00\x00\x00\x07\x74\x52\x4e\x53\x00\x4a\x46\x33\x36\x52\x53\ \x94\x83\x9f\x4e\x00\x00\x00\x1b\x49\x44\x41\x54\x08\x5b\x63\x64\ \x00\x01\x46\xf9\x07\x60\x8a\x16\x9c\x64\x64\x4e\x10\x71\x7a\xa8\ \xc4\x01\x00\x7e\xd2\x11\x89\x99\xbb\x07\xa2\x00\x00\x00\x00\x49\ \x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x01\x7c\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x00\xf9\x49\x44\ \x41\x54\x38\x8d\xa5\x93\x41\x4e\xc3\x30\x10\x45\xdf\x4c\x27\x51\ \x9d\x33\x64\x89\xaa\xf6\x1a\xac\x52\xf5\x34\x2c\x7a\x0a\x16\xbd\ \x0e\xab\x9c\xa3\x42\x2c\x73\x02\xa8\x40\x38\x55\x86\x45\x42\x12\ \x04\xb4\x46\x8c\xe5\x8d\xf5\xff\xf7\xff\x63\x8f\xb8\x3b\xff\x29\ \x03\x28\xcb\x72\x25\x22\x07\x87\x0a\xd0\x2b\x9c\x4e\xe0\xc1\xdd\ \xef\x9a\xa6\x79\x34\x00\x11\x39\x64\x79\xbe\x5d\x2c\x4c\x44\xe4\ \x57\xa6\xaa\x02\x68\x8c\xef\xdb\x36\x46\x80\x9d\x01\x38\x54\x66\ \x99\x98\x59\x92\xed\x2c\xcb\x25\xc6\x58\x8d\x11\x7a\x71\xe5\xd2\ \xed\xf3\x1a\x70\x3a\x17\x00\x01\x12\x05\x98\xc1\x6c\x3a\xeb\x57\ \x1a\x7f\xc2\x7d\x36\xb1\xa7\xa7\x46\x40\x10\xd1\xb9\x80\x0e\x11\ \x92\xf8\x20\x63\x1f\x26\x07\x48\xba\x83\x39\xf6\x6b\x84\x3f\xf4\ \xe0\x47\x07\xe9\xaf\xf0\x5d\xa0\x03\x34\x39\xc2\xc4\xe9\x3f\x43\ \x28\x42\x7d\x3e\xb7\x3e\xc6\xb9\xb0\x01\xda\x36\x7a\x28\x42\x3d\ \x3a\x58\x6d\x36\xfb\xa7\xe3\xf1\xfe\xf4\xf2\x7c\x4b\xc2\x30\x85\ \xb0\xac\x6f\xd6\xeb\x3d\x80\x0c\xe3\xac\x40\x01\x04\xae\x3f\xa6\ \x03\x6f\xc0\x2b\xd0\x7d\x00\x96\xec\x37\x38\xa7\x56\xbd\x12\x00\ \x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\x20\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\x9d\x49\x44\ \x41\x54\x38\x8d\xad\x93\x31\x8b\x1a\x51\x14\x85\xcf\x79\x6f\x64\ \xd2\x86\x05\x41\x05\x41\xec\x6d\x6d\x52\x0c\x24\xfd\x56\x09\x04\ \xf2\x03\xf6\x17\x8c\x30\xf0\x8a\x61\x60\xd0\xc1\x26\xdd\xfe\x83\ \x6d\xd2\xa4\x96\x88\xa0\x29\xec\x82\x45\x9a\x20\x82\xa0\xcb\xca\ \x6e\x1d\xc6\x99\xf7\x6e\x9a\x5d\x30\xe0\x8a\x59\xf2\xd5\xf7\xbb\ \xe7\x16\xf7\x50\x44\x70\x48\x14\x45\x17\x24\x3b\xd6\xda\x86\x52\ \xaa\x0e\x00\xce\xb9\xad\xd6\x7a\x23\x22\x8b\x34\x4d\x1f\x0e\xe7\ \xf9\xb4\x80\x24\xc3\x30\xec\x2a\xa5\xde\x8a\xc8\x12\xc0\xda\x5a\ \x7b\x0b\x00\x5a\xeb\x1a\x80\x26\xc9\xb6\x73\xee\x5b\x96\x65\x73\ \x79\x14\x29\x22\x20\xc9\x28\x8a\x3e\x8a\x48\x55\x6b\xfd\x35\x49\ \x92\x15\x8e\x60\x8c\x69\x59\x6b\x2f\x49\xee\xd2\x34\xbd\x11\x11\ \x51\x00\x10\x86\x61\x57\x44\xaa\xbe\xef\x5f\x3f\x27\x03\x40\x92\ \x24\x2b\xdf\xf7\xaf\x45\xa4\x1a\x86\x61\x17\x00\xf4\x7e\xbf\xbf\ \x20\xf9\xc1\xf3\xbc\x2f\x71\x1c\xdf\x3f\x27\x3f\x11\x04\x41\x39\ \x99\x4c\xee\x44\xe4\x72\x3c\x1e\xff\x54\x24\x3b\x22\xb2\x3c\x95\ \x7c\xec\x12\x11\x59\x92\xec\x28\x6b\x6d\x03\xc0\xfa\x5c\xf9\x80\ \xb5\xb5\xb6\xe1\x29\xa5\xea\x45\x51\x7c\x07\x80\x5e\xaf\x57\x27\ \xf9\x9e\xe4\xeb\x63\x86\x88\x3c\x94\x65\x79\x33\x1c\x0e\xef\xad\ \xb5\xb7\x95\x4a\xe5\x8d\x7a\x41\xf2\x5f\x78\x8f\x4f\x52\x03\xb0\ \x1a\x0c\x06\x5b\x00\x9f\xcf\x11\xb5\xd6\x35\xe7\xdc\x56\x69\xad\ \x37\x00\x9a\x2f\x08\x6f\x6a\xad\x37\x4a\x44\x16\x24\xdb\xc6\x98\ \xd6\xb9\xa6\x31\xa6\x45\xb2\x2d\x22\x0b\x3d\x9d\x4e\x7f\x8f\x46\ \xa3\x02\xc0\xbb\xd9\x6c\xf6\x23\x08\x82\xf2\x94\x1c\xc7\xf1\xab\ \xa2\x28\x3e\x39\xe7\xa6\xfd\x7e\xff\x97\x02\x80\x2c\xcb\xe6\x24\ \x77\x79\x9e\x5f\x9d\xba\xc4\x18\xd3\xca\xf3\xfc\x8a\xe4\x2e\xcb\ \xb2\x39\xf0\xbf\xca\x74\xc8\xbf\xd6\xf9\x0f\xe1\x47\xf7\x59\xc8\ \x36\x3a\x51\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x00\x98\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\ \x0d\xd7\x01\x42\x28\x9b\x78\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdd\x09\x02\x0e\x0c\x34\x42\xf2\x99\x18\x00\x00\x00\x25\x49\x44\ \x41\x54\x38\xcb\x63\x60\x18\x05\xc3\x00\x30\xa2\x0b\xec\xdb\xb7\ \xef\x3f\x3e\x0d\x4e\x4e\x4e\x28\x7a\x98\x46\xc3\x70\x14\x30\x30\ \x30\x00\x00\xcc\x01\x04\x04\x65\x8e\x02\xea\x00\x00\x00\x00\x49\ \x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\xb6\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x02\x33\x49\x44\ \x41\x54\x38\x8d\xa5\x53\x41\x4f\x53\x41\x10\xfe\x66\x66\x77\xfb\ \x88\x79\xa5\x44\x8d\x06\x52\x4c\x9a\x96\x43\x5b\xe2\x81\xab\x87\ \x0a\x31\x21\x69\x42\xea\xd1\x8b\xbf\x00\xd1\x8b\x57\x8c\xfc\x02\ \xc5\x3b\x07\x2f\x1e\xbd\x92\x18\x35\x21\xfc\x01\x63\x39\xc1\x45\ \x12\x40\xa3\x86\xd0\x5a\x78\xed\x7b\x6f\xc7\x83\x15\x28\x29\x5e\ \xfc\x36\xdf\x65\xf7\xdb\x6f\x67\x76\x66\x48\x55\xf1\x3f\x30\x17\ \x37\xae\x96\x4a\xd9\x20\x8a\x9e\x10\xf1\x3d\x28\x6e\x03\x00\x08\ \x9f\x54\xfd\xbb\x28\x08\x5e\xfc\xdc\xde\x6e\x9d\xd7\xd3\xdf\x08\ \x88\x88\x26\x26\x26\x67\x41\xfa\x46\x44\xb2\x22\x9c\x01\xa8\x2f\ \x53\xa4\xa9\xef\xa6\x69\xda\x82\xd2\x83\xbd\xbd\xdd\x0f\xda\xbf\ \x48\xaa\x0a\x22\xe2\xf1\xf1\xc9\x39\x16\xbc\xb5\xd6\x5e\x31\xc6\ \x80\x99\x41\xf4\xc7\x40\x55\xe1\xbd\x47\x92\x24\x88\xe3\xb8\xe3\ \x53\xdc\xdf\xdf\xdf\x7d\xaf\xaa\x9e\x00\xd0\xcd\x62\xf1\x9a\x44\ \xdd\x2d\x97\xc9\x5c\xb7\xd6\x42\x44\x86\xe6\x9b\xa6\x29\xe2\x38\ \x46\xaf\xdb\xfd\x9e\x06\x99\xca\xd7\x9d\x9d\x1f\x32\x33\x33\x63\ \xa3\xd6\xaf\xa7\x62\xcc\xac\x73\xce\x18\x63\x40\x97\x2c\x66\x06\ \x81\xa0\xaa\x86\xe3\x24\x29\x95\x8a\x9b\x5c\x28\x14\x0c\x31\xcd\ \x19\x23\x19\x11\x01\x11\xfd\x93\x22\x02\x63\x24\x43\x4c\x73\x85\ \x42\xc1\x18\xe7\x9c\x25\xa2\x69\x66\x19\xc8\xfb\x32\x30\x33\x98\ \x05\x44\xc9\xb4\x73\xce\x9a\x7c\x3e\x2f\xfd\x52\x9d\x71\x00\x43\ \x0c\xfb\x8f\xe4\xf3\x79\xe1\x5c\x2e\xe7\x59\xb8\x79\x76\x76\x31\ \x6c\x0c\x10\x50\x00\x0a\x16\x6e\xe6\x72\x39\xcf\x61\x18\xa6\x81\ \x0b\x36\xd4\xfb\xee\x70\x83\x41\x02\x80\x7a\xdf\x0d\x5c\xb0\x11\ \x86\x61\xca\xe5\x72\x39\x6a\x34\x16\xd6\x54\xb5\x2d\x2c\x10\x96\ \x4b\xab\x20\x2c\x10\x11\xa8\x6a\xbb\xd1\x58\x58\x2b\x97\xcb\x11\ \xd7\x6a\xb5\xa4\x5e\xaf\x1f\x4c\x95\x8a\x8b\xc7\xc7\x9d\x8e\xaa\ \xc2\x58\x0b\x16\x01\x31\x83\x98\xc1\x22\x30\xd6\x42\x55\x71\xdc\ \xe9\x74\xa6\x4a\xc5\xc5\x7a\xbd\x7e\x50\xab\xd5\x92\xd3\x56\x6e\ \x36\x9b\x37\x96\x97\x9f\xdf\x6d\x6e\x7d\x7e\x65\xac\x0d\x9d\x73\ \x19\x11\xd3\x6f\xa0\x04\xbd\x5e\xaf\x9b\xc4\x71\xbb\x5a\x99\x7e\ \xb4\xb2\xf2\xec\x63\xb5\x5a\xfd\x36\x30\x0b\x00\x70\x78\x78\x38\ \xba\xbe\xbe\x7e\xeb\xe5\xea\xea\xc3\xd6\x51\xeb\x4e\x92\xa6\x15\ \x00\x30\x22\x5b\xd9\xd1\xec\xe6\xe3\xa5\xa5\xd7\xf3\xf3\xf3\x5f\ \xc6\xc6\xc6\x8e\x4e\x3f\x7d\xc8\x38\x3b\x00\xe1\xc9\xc9\x49\xc0\ \xcc\x01\x00\x78\xef\xa3\x91\x91\x91\x08\x40\x1b\x40\xef\xbc\xf8\ \x37\xe6\x1c\xd9\x82\x94\x85\x27\x9a\x00\x00\x00\x00\x49\x45\x4e\ \x44\xae\x42\x60\x82\ \x00\x00\x00\x98\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\ \x0d\xd7\x01\x42\x28\x9b\x78\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdd\x09\x02\x0e\x0c\x2d\x26\x99\x31\xd8\x00\x00\x00\x25\x49\x44\ \x41\x54\x38\xcb\x63\x60\x18\x05\xc3\x00\x30\xa2\x0b\xd8\xd8\xd8\ \xfc\xc7\xa7\xe1\xc8\x91\x23\x28\x7a\x98\x46\xc3\x70\x14\x30\x30\ \x30\x00\x00\x9b\x41\x04\x04\xd6\x20\x9e\x3b\x00\x00\x00\x00\x49\ \x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x01\x5f\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x0c\x00\x00\x00\x10\x08\x06\x00\x00\x00\x22\x61\x9e\x07\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\ \x0d\xd7\x01\x42\x28\x9b\x78\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdf\x07\x0b\x0a\x20\x09\xab\x52\x46\x9b\x00\x00\x00\xec\x49\x44\ \x41\x54\x28\xcf\xd5\x92\xb1\x4a\xc4\x50\x10\x45\xcf\x84\x2c\x64\ \x17\x15\x9b\x4d\xa1\xdd\xc2\x22\x58\x8a\x20\xc4\xc2\x46\x04\xeb\ \x74\x29\xf2\x09\x29\xf2\x13\xf9\x00\xc9\x17\xa4\x0a\xf8\x09\x92\ \x36\x55\xe0\x15\x29\x5e\x29\xd8\x0a\x11\x02\x1a\x94\xc7\xb3\xc9\ \x42\x58\x10\x5d\xd8\xc6\xdb\xcd\x70\xcf\xcc\x70\x19\xb1\xd6\xb2\ \x8b\x1c\x76\xd4\xde\x80\x05\x30\xfb\x2b\x70\x5c\x14\xc5\x55\x14\ \x45\x2b\xc0\xfd\x0d\xf0\x94\x52\x67\x79\x9e\x9f\xb7\x6d\x7b\x9d\ \x65\xd9\x72\xdb\x33\x2d\xdc\xae\xeb\xd6\x69\x9a\x5e\xf4\x7d\xff\ \x6c\x8c\x79\x29\xcb\xf2\xae\xae\xeb\x43\x40\x36\x26\x99\xc4\xba\ \x0e\xc3\xf0\x46\x6b\xdd\x03\xaf\xe3\x39\x4b\xcf\xf3\x9c\xa6\x69\ \x1e\x81\xf7\xe9\x86\xd3\x24\x49\x2e\xb5\xd6\x2e\xf0\x09\x78\xc0\ \x01\xe0\x0e\xc3\x70\x12\x04\xc1\xfd\xd8\x43\xac\xb5\x4e\x1c\xc7\ \xb7\x4a\xa9\xd8\x18\xf3\x06\x7c\x8d\x43\x66\xc0\x1c\x98\x8b\xc8\ \x91\xef\xfb\x0f\x55\x55\x3d\x89\xb5\x56\xc6\x18\xe5\x87\x88\x37\ \x7d\x0b\x7c\xc8\xff\x79\x8d\xfd\x01\xdf\x56\x3f\x4e\x5e\xbf\xfb\ \xfb\xf7\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x00\x7c\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x51\x00\x00\x00\x3a\x02\x03\x00\x00\x00\xb5\xd2\x5d\x3c\ \x00\x00\x00\x0c\x50\x4c\x54\x45\xff\xff\xff\x63\x56\x4d\x63\x56\ \x4d\x63\x56\x4d\xd5\xa7\x24\x2d\x00\x00\x00\x04\x74\x52\x4e\x53\ \x00\x7c\x3a\x33\x74\x01\x48\xc1\x00\x00\x00\x1b\x49\x44\x41\x54\ \x38\x4f\x63\x60\x80\x01\xce\x18\x06\x2c\x60\x54\x74\x54\x74\x54\ \x74\x54\x74\x38\x8a\x02\x00\x11\x99\x16\xe3\x77\x55\xa6\x6c\x00\ \x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x01\xc8\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\ \x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\ \x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\ \x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\ \x95\x2b\x0e\x1b\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\ \x0b\x03\x24\x4f\xed\x0a\xe0\x00\x00\x01\x48\x49\x44\x41\x54\x58\ \xc3\xed\x57\x41\x8e\x83\x30\x0c\xb4\x21\xd0\x3e\x10\x3f\x8e\x23\ \xe7\x20\xbe\xb3\x3f\xd9\x55\x03\x9b\xd9\x4b\x2a\x45\x10\x96\x24\ \x04\xf5\x82\xcf\x2e\x63\x4f\xc6\x63\x97\xe8\xc3\xc1\x57\x7c\x54\ \x6b\x6d\x98\xb9\x3a\xca\x03\xb0\xa8\x0b\x9b\xab\x23\x72\x7e\xab\ \x2b\x90\x45\xa4\x05\xb0\xc4\xe4\x56\x57\xb5\x2f\x22\x0d\x80\xf9\ \x28\x6f\xf3\x04\xe3\x38\x2e\x39\x80\x00\xac\x88\xb4\xa9\xbf\x53\ \x9e\x70\x7e\x98\xb9\xcd\x64\x65\x03\xae\xb5\x9e\x99\x59\x45\x3f\ \xc1\x19\x70\x00\x26\x30\x05\x2a\x9a\x01\x47\x7b\x16\x78\xd7\x75\ \x75\x42\xe7\x76\x8d\x73\x46\x84\xa9\x9d\x5b\x00\x66\x3d\x1d\xca\ \x5a\x4b\xd3\x34\xad\x05\x35\xa7\x0a\xea\xa8\x73\x9f\x29\x57\x68\ \x43\x44\x54\x0d\xc3\xf0\x05\x60\x23\xea\x0c\xe7\x53\xb1\x4c\x39\ \x9f\x30\x44\x64\xd9\xd3\x40\xed\x31\x60\x44\xe4\x51\xba\xf3\xe2\ \x46\x94\xda\x79\x94\x11\x15\x8a\xe0\x74\x38\xbf\x78\x14\x63\x60\ \xc7\xf3\x93\xa6\xe3\x34\x03\x22\xd2\x78\xaa\x4e\xf5\x85\x32\xcb\ \xe8\xad\xea\x1c\x47\x2c\xa6\x81\xd0\xd4\xc4\x1c\x25\x97\xad\xe3\ \xd8\xb8\x0b\xb8\x0b\xb8\x0b\xa8\x4a\xff\x61\xe9\xfb\xbe\x71\x26\ \xf4\x1d\x58\xf3\x71\x46\xc4\xcc\x2a\xf7\x3a\x76\xeb\x9d\x00\x44\ \x19\x91\x7a\x9f\xd4\xcc\x5c\xaf\x18\xa8\xcf\x50\xcb\xcc\xa1\x23\ \x07\xbb\x54\xc7\x9e\xd1\xb9\x01\xe0\x25\x22\xcf\x7f\xdf\xda\xbf\ \xd5\xca\x62\xc3\x84\xc0\x83\x62\xd3\x5a\xbf\x4a\x17\xb0\x07\x4e\ \x44\xf4\x07\xae\xf5\xd5\xa6\x1d\xd1\x22\x08\x00\x00\x00\x00\x49\ \x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x00\xbb\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x3f\x00\x00\x00\x07\x08\x06\x00\x00\x00\xbf\x76\x95\x1f\ \x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\ \x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\ \x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\ \x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\ \x09\x35\x2b\x55\xca\x52\x6a\x00\x00\x00\x3b\x49\x44\x41\x54\x38\ \xcb\x63\x60\x18\x05\x23\x13\x30\x12\xa3\xa8\xbe\x7d\x2a\x25\x76\ \xfc\xa7\x97\x3b\xd1\xc1\xaa\xa5\x73\x18\xae\x5f\x39\x8f\x53\x9e\ \x69\x34\xe6\x09\x00\x4d\x1d\xc3\x21\x19\xf3\x0c\x0c\x0c\x78\x63\ \x7e\x14\x8c\x54\x00\x00\x69\x64\x0b\x05\xfd\x6b\x58\xca\x00\x00\ \x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x00\xc3\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x40\x00\x00\x00\x40\x08\x06\x00\x00\x00\xaa\x69\x71\xde\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\ \x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdc\x0b\x07\x09\x2e\x37\xff\x44\xe8\xf0\x00\x00\x00\x1d\x69\x54\ \x58\x74\x43\x6f\x6d\x6d\x65\x6e\x74\x00\x00\x00\x00\x00\x43\x72\ \x65\x61\x74\x65\x64\x20\x77\x69\x74\x68\x20\x47\x49\x4d\x50\x64\ \x2e\x65\x07\x00\x00\x00\x27\x49\x44\x41\x54\x78\xda\xed\xc1\x01\ \x0d\x00\x00\x00\xc2\xa0\xf7\x4f\x6d\x0e\x37\xa0\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x77\x03\x40\x40\ \x00\x01\xaf\x7a\x0e\xe8\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\ \x60\x82\ \x00\x00\x01\x66\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x0c\x00\x00\x00\x10\x08\x06\x00\x00\x00\x22\x61\x9e\x07\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\ \x0d\xd7\x01\x42\x28\x9b\x78\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdf\x07\x0b\x0a\x1e\x12\x60\xf2\x94\x0a\x00\x00\x00\xf3\x49\x44\ \x41\x54\x28\xcf\xcd\x92\x31\x4b\xc4\x50\x10\x84\xbf\xe7\x4b\xe4\ \x14\x02\x17\x11\x52\x09\x36\x62\xca\x40\xaa\xc4\xca\xa4\xb4\x15\ \x42\x7e\x84\x7f\x25\x45\x48\x99\xea\xf5\x29\x6c\xc5\x46\x3b\x1b\ \xff\xc0\x15\x56\x82\xc4\x2a\x1e\xdc\x05\x94\x5b\x9b\x57\x48\xf0\ \x8a\xeb\x6e\x61\x9a\x9d\x59\x96\x9d\x1d\x25\x22\xec\x52\x07\xec\ \x58\x7b\x38\xe0\x00\x1a\x38\x02\x94\xed\x6d\x73\x41\x80\x95\x03\ \x90\x65\xd9\x55\xdf\xf7\x77\x22\xf2\x05\xac\x2d\xbe\xad\xd0\xd5\ \x5a\xcf\xa3\x28\x32\xc6\x98\x47\x65\x6d\x9d\xa5\x69\x7a\x33\x0c\ \xc3\x05\xf0\x01\x2c\x81\x11\x38\x04\x82\x3c\xcf\x87\xba\xae\x9f\ \x81\x77\xf5\xe7\x0f\xc7\x71\x1c\xdf\x8e\xe3\xb8\x01\x3e\x81\x1f\ \xe0\x34\x0c\x43\xaf\xeb\xba\x27\x60\x31\x3d\x7a\xdd\x34\xcd\xbd\ \xeb\xba\x1a\xd8\x00\xb3\x20\x08\x4e\xda\xb6\x7d\x01\xde\xfe\x73\ \x49\x92\x24\x59\x16\x45\xf1\xa0\xb5\x3e\xf3\x3c\xef\xbc\xaa\xaa\ \x57\xdf\xf7\x17\x76\x9b\x55\x89\x4c\xe1\x94\x65\x79\x69\x8c\xb9\ \x16\x91\xf9\x94\x57\x5b\xb2\xe4\x5a\xac\xa6\x84\xda\xbf\xf0\xfd\ \x02\xa9\xff\x6b\xef\x5e\xd2\x55\x2c\x00\x00\x00\x00\x49\x45\x4e\ \x44\xae\x42\x60\x82\ \x00\x00\x02\x3c\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xb9\x49\x44\ \x41\x54\x38\x8d\xad\x53\x3d\x6b\xdb\x50\x14\x3d\xe7\x3d\x83\xba\ \x96\x80\xc1\x0e\x18\x1c\xef\xce\xe8\xec\x9d\x4d\xe7\xf6\x17\x18\ \xb2\xcb\x92\x40\x18\x23\x90\x6d\xed\x01\xff\x82\x76\x2e\x9e\xbb\ \xc7\x63\xbc\xdb\x01\x83\x13\x30\x94\xae\x11\xf8\xe9\x74\xa8\x54\ \x84\x69\x4a\x0b\x3d\xd3\xfd\x3a\xf7\xdd\x8f\x77\x29\x09\x75\x44\ \x51\x74\x41\xb2\xef\x9c\xbb\x34\xc6\xb4\x01\xa0\x28\x8a\x27\x6b\ \xed\x41\xd2\x26\x4d\xd3\x6f\xf5\x78\x56\x09\x48\xd2\xf7\xfd\x81\ \x31\xe6\x9d\xa4\x2d\x80\xbd\x73\xee\x19\x00\xac\xb5\x2d\x00\x1d\ \x92\xbd\xa2\x28\xbe\x66\x59\xb6\x56\x49\xa4\x24\x90\x64\x14\x45\ \x1f\x24\x35\xad\xb5\x5f\x92\x24\x79\xc4\x6f\x10\xc7\x71\xd7\x39\ \xf7\x9e\xe4\x31\x4d\xd3\xcf\x92\x64\x00\xc0\xf7\xfd\x81\xa4\xa6\ \xe7\x79\xcb\xd7\xc8\x00\x90\x24\xc9\xa3\xe7\x79\x4b\x49\x4d\xdf\ \xf7\x07\x00\xc0\x30\x0c\x2f\x24\x8d\x1a\x8d\xc6\xa7\x3a\x39\x0c\ \xc3\xb7\x00\xae\x4a\x75\x37\x9b\xcd\xbe\xd7\x2b\x39\x9d\x4e\x1f\ \x49\x2e\x0d\xc9\xbe\xa4\x6d\x9d\x1c\x04\xc1\xb5\xa4\x5b\x00\x43\ \x00\x43\x49\xb7\x41\x10\x5c\xd7\x2b\x91\xb4\x25\xd9\x37\xce\xb9\ \x4b\x00\xfb\xb3\x97\x87\x24\x1b\xbf\x26\xfd\x53\x1e\x96\xbe\x0a\ \xfb\x72\x53\xa6\x5d\x4d\xbb\xc4\x55\x9d\x7c\x96\xa4\x6a\x09\xce\ \xb9\x67\x63\x4c\xdb\xbc\x36\xb0\xbf\x85\x29\x3f\x49\xab\x66\xdb\ \x49\x3a\x9d\x07\x96\xb6\x5d\xa5\x5b\x6b\x5b\x45\x51\x3c\x19\x6b\ \xed\x01\x40\xa7\x72\x94\xd3\x5e\xd5\x93\x94\xf2\xaa\xbe\x09\x00\ \x1d\x6b\xed\xc1\x48\xda\x90\xec\xc5\x71\xdc\xad\x3c\xf3\xf9\xfc\ \x81\xe4\x1d\x80\x15\x80\x15\xc9\xbb\xf9\x7c\xfe\x50\xf9\xe3\x38\ \xee\x92\xec\x49\xda\x50\x12\xc6\xe3\xf1\x8d\xb5\xf6\xc6\xf3\xbc\ \xe5\x64\x32\x79\xf9\x53\xcf\xd3\xe9\xf4\x4d\x9e\xe7\x23\xe7\xdc\ \xfd\x62\xb1\xb8\x37\x00\x90\x65\xd9\x9a\xe4\x31\xcf\xf3\x51\xbd\ \x92\x73\xc4\x71\xdc\xcd\xf3\x7c\x44\xf2\x98\x65\xd9\x1a\xf8\x5f\ \xc7\x54\xc7\xbf\x9e\xf3\x0f\x21\x2d\x12\x95\xf0\x65\xef\x7c\x00\ \x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x00\xa1\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\ \x0d\xd7\x01\x42\x28\x9b\x78\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdd\x09\x02\x0e\x0c\x12\x90\xff\x1c\xe5\x00\x00\x00\x2e\x49\x44\ \x41\x54\x38\xcb\x63\x60\x18\xde\xc0\xc6\xc6\xe6\xbf\x8d\x8d\xcd\ \x7f\x7c\x6a\x98\x28\xb5\x64\xe0\x0d\x60\x44\xf7\x33\x31\x9a\x8e\ \x1c\x39\xc2\x48\x35\x17\x8c\xc6\xc2\xb0\x00\x00\x12\xb8\x0a\xdc\ \x9c\x51\xad\x44\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \ \x00\x00\x00\xce\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x07\x00\x00\x00\x3f\x08\x06\x00\x00\x00\x2c\x7b\xd2\x13\ \x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\ \x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\ \x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\ \x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\ \x09\x2f\x07\xd7\x3f\xc4\x52\x00\x00\x00\x4e\x49\x44\x41\x54\x38\ \xcb\x63\x60\x18\x36\x80\x19\xc6\xa8\x6f\x9f\xca\xe0\xe0\xe2\xcd\ \xf0\xea\xe5\x73\x86\x37\xaf\x5e\x30\x30\x30\x30\x30\x30\xe1\xd3\ \x39\x2a\x49\x48\x92\x05\x89\xfd\x1f\x89\xcd\x38\x1a\x42\xa3\x92\ \x83\x27\x69\x32\x8e\x86\x10\xa9\x92\xf0\x20\xd3\xd4\x31\x84\x0b\ \x5e\xbf\x72\x9e\x61\x14\x40\x01\x00\x13\x4d\x0c\x46\x89\x2a\x0a\ \x20\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x01\x3c\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x08\x00\x00\x00\x0c\x08\x06\x00\x00\x00\x5f\x9e\xfc\x9d\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x01\x23\x00\x00\ \x01\x23\x01\x72\x41\x77\xde\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdf\x07\x0b\x0a\x1a\x31\xa6\xf9\x20\x7c\x00\x00\x00\xc9\x49\x44\ \x41\x54\x18\xd3\x6d\x90\xbd\x4a\xc4\x40\x14\x46\xcf\xdc\x09\xb8\ \x0f\x90\x6e\x61\x7d\x95\x68\x67\x19\x32\x93\xa7\xb0\xd8\xd2\xc2\ \x56\xb0\x10\x0b\x41\xb0\xf2\x05\x2e\xa4\xb3\x12\x24\xcd\xbe\xc7\ \x36\x5b\xa5\xb5\xd8\x8d\xe4\x8e\x4d\x90\xb0\xce\x85\x53\x7d\x3f\ \x70\x3f\x9a\xa6\xb9\x0b\x21\xac\x52\x4a\xe4\x10\xe0\x66\x9a\xa6\ \xd7\xb6\x6d\x37\x64\x4e\x00\x27\x22\x97\x66\xf6\x12\x42\xb8\xce\ \x1a\x00\x67\x66\x2b\x60\x1b\x63\xbc\xad\xaa\xea\xe2\x9f\x41\x44\ \x30\x33\x97\x52\xba\x2a\xcb\xf2\x21\xc6\xb8\x06\x70\x75\x5d\xef\ \x44\x64\x04\x7e\x66\xc6\x99\x6f\xe0\x5d\xe6\x96\xbf\xa6\x33\x28\ \x44\x24\x27\x1c\x9c\x73\x4f\xaa\x7a\x28\x96\x82\x99\x39\xef\xfd\ \xd7\x30\x0c\x6f\x7d\xdf\x9f\x00\x96\x86\x11\x78\x56\xd5\xcf\xe5\ \x9b\xc5\x9c\xdc\x8b\xc8\x7d\xd7\x75\xfb\xf3\x1d\x0a\xe0\xc3\x7b\ \xff\xa8\xaa\xc7\xdc\x92\xbf\x5c\x3a\x52\xef\x6d\xe4\x98\x5b\x00\ \x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x01\x67\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x0c\x00\x00\x00\x10\x08\x06\x00\x00\x00\x22\x61\x9e\x07\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\ \x0d\xd7\x01\x42\x28\x9b\x78\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdf\x07\x0b\x0a\x1e\x18\x80\x27\x7d\x14\x00\x00\x00\xf4\x49\x44\ \x41\x54\x28\xcf\xcd\x92\xa1\x6a\xc3\x60\x14\x85\xbf\xfc\x49\x43\ \x17\x08\x94\x98\xa9\x41\xcc\x88\x8a\x8c\x9a\x9a\x89\xd9\x5b\x44\ \xc5\xcc\x04\x7e\x02\x81\x88\xbc\x48\xc4\xde\xa0\x10\x28\x8c\xa9\ \xb9\xc1\xd8\x0b\x4c\x4c\x75\xcc\x85\xad\xa1\x2d\xac\xec\xce\xfc\ \x35\xa1\x15\x75\x3d\x70\xcc\xbd\xe7\x70\x39\x87\x6b\x89\x08\xa7\ \x40\x71\x22\xce\xd0\xe0\x00\x36\x70\x01\x58\x66\x76\xac\x05\x01\ \xd6\x0e\x40\x9e\xe7\x37\x7d\xdf\xdf\x8b\xc8\x0f\xb0\x31\xfc\x35\ \xc2\x89\x52\x6a\x16\x45\xd1\x43\xd3\x34\x4f\x96\xa9\x75\x9a\x65\ \xd9\xdd\x30\x0c\xd7\xc0\x17\xb0\x02\xb6\x80\x0b\x5c\x26\x49\xf2\ \xad\xb5\x7e\x06\x96\xfb\x0c\xdb\xb6\x6d\x17\xae\xeb\x7e\x02\x3b\ \x60\xd8\x1b\xc2\x30\xdc\x69\xad\x5f\x81\xe5\x38\xf4\xa6\x2c\xcb\ \xb9\x6d\xdb\x36\xf0\x07\x4c\x83\x20\x08\xea\xba\x7e\x01\x3e\x0e\ \xb5\x24\x71\x1c\xaf\xd2\x34\x7d\x54\x4a\x5d\x79\x9e\x17\x16\x45\ \xf1\xe6\xfb\xfe\xbb\xb9\x6a\x54\x22\x63\x3a\x55\x55\x45\x5d\xd7\ \xdd\x8a\xc8\x6c\xbc\xb7\x8e\xfc\xd2\xc4\x70\x3d\x5e\x58\xe7\xf7\ \x7c\xff\x55\x59\x68\xdb\xc7\xf2\x0d\xdb\x00\x00\x00\x00\x49\x45\ \x4e\x44\xae\x42\x60\x82\ \x00\x00\x00\xe1\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x0a\x00\x00\x00\x36\x08\x06\x00\x00\x00\xfe\x8a\x08\x6b\ \x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\ \x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\ \x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\ \x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\ \x09\x33\x22\x7a\x4c\x4d\x48\x00\x00\x00\x61\x49\x44\x41\x54\x48\ \xc7\x63\xfc\xcf\x40\x1c\x60\x62\xa0\x99\x42\x63\x23\x23\x06\x63\ \x23\x23\x0c\x36\x1d\xac\x1e\x55\x38\xc8\x14\x32\xfe\x67\x60\x60\ \x68\x68\x9f\x8a\x33\x59\xae\x5a\x3a\x87\xe1\xda\x95\xf3\x8c\x34\ \xb2\x5a\x4b\xc7\x10\x6f\x8e\xb8\x76\xe5\x3c\x23\xe3\x7f\xa4\x84\ \xcb\xc0\xc0\xc0\x70\xf6\xdc\x39\x14\xf6\x68\x80\x8f\x06\xf8\x68\ \x80\x8f\x96\x66\x43\x27\x3d\x0e\x72\x37\x02\x00\x10\x30\x40\xb3\ \x35\xa0\x7c\x49\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \ \x00\x00\x02\x76\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xf3\x49\x44\ \x41\x54\x38\x8d\x95\x93\xc1\x6b\x13\x51\x10\xc6\x7f\xf3\xf2\x36\ \xee\xd6\x88\x85\x08\x56\x0d\x42\xad\xa6\xad\xe6\x20\x04\xda\x82\ \x17\x15\xc1\x4a\xab\x08\x7a\xf3\x2e\x08\x0a\x7a\xe8\xc5\xbf\x40\ \xf0\x50\xf0\x66\xbd\x55\xbc\x08\xb5\x57\x2f\xa6\x54\x72\x90\x28\ \x1e\xa4\xb1\x69\xa3\x05\x45\xa2\xe8\xa1\x68\xdb\xa4\x9b\xdd\xec\ \xf3\x90\x74\xd3\x68\x1a\x71\x60\x2e\xf3\xe6\x9b\xf9\xbe\x79\x33\ \x62\x8c\xe1\x7f\xed\xee\xa3\x85\xe9\xcd\xaa\x37\x1e\x04\xc1\x3d\ \x31\xc6\x90\x48\x24\x92\x22\x32\x69\x60\x14\x50\x9d\xc0\xbd\xa9\ \xd3\x66\x78\xfc\x8e\xf1\x6b\x81\x8b\x48\x4a\x03\x88\xc8\xa4\x15\ \x8d\x5e\x88\x44\xb4\x88\xc8\x8e\xe0\x58\xf7\x7e\x86\xc7\x6e\x49\ \xd5\xf7\x24\xff\xf2\x49\xf1\xc5\xcc\x83\x15\x05\x60\x60\x54\x6b\ \x4b\x2c\xcb\x42\x6b\xdd\xd6\x2d\x2b\xca\xd0\xd8\x6d\x5c\xb7\x4a\ \x69\xf9\x15\x4b\xb9\xd9\x14\x80\x6e\x14\x57\x4a\x29\x3a\x75\x1f\ \x18\xb9\x82\xbd\xb7\x07\xb7\x52\xa6\x90\x7d\xcc\x96\xd4\xa6\x5e\ \x01\x44\xe8\xe9\x1b\xa2\x7f\xe4\x2a\x88\x84\x1e\x3f\x34\x40\x5f\ \xfa\x12\x46\x34\x0b\x73\x53\xf8\x5e\x39\x84\xe9\x26\x5e\x70\x62\ \x71\x4e\x9c\xb9\x8e\xa5\x15\x62\x60\x29\xf7\x0c\x6b\x57\x17\xe9\ \xf3\x37\xa9\x54\x36\xf8\xf2\x7e\x9e\xd5\x52\x01\x41\x5a\x0b\x88\ \x08\x82\xe0\x6e\xac\x92\xcf\x3c\x24\x75\xee\x06\xbd\xe9\x8b\x54\ \xdd\x75\xe2\x07\xfb\xf1\x8d\xa2\xbc\xf6\x83\x95\x37\xb3\x61\xae\ \x48\x9d\xbc\xaa\x17\x50\x0d\x09\xf0\xfd\xd3\x5b\x0a\xd9\x69\x3c\ \xbf\xc6\xe0\xa9\x6b\xec\x3b\x7c\x12\x89\x58\xe4\xe7\xa6\x08\x8c\ \x1f\xe6\x6d\xcd\x2b\x64\x80\x48\x18\xfc\xba\x9c\x25\x6a\xef\xe1\ \x48\xfa\x32\x26\xa8\x51\xcc\x3d\xa5\xfc\xf3\x5b\x73\xc8\xdb\x72\ \x5b\x24\x6c\xd7\xf6\xf9\xdd\x73\x2c\x3b\xc6\xee\xee\x03\x94\x16\ \xe7\x5b\xde\xea\x12\xda\x30\xe0\x8f\x6f\xfc\xf8\x7a\x06\x1d\x75\ \xfe\x8a\xb7\x63\x10\x00\xaa\xdd\x1e\xd4\xbc\xcd\xb6\xfb\xd1\xc0\ \xd4\x87\xe8\xd8\x4e\xc6\xf7\x3c\x13\xca\xe9\xe0\x00\x5e\xb5\x6a\ \x1c\xdb\xc9\x84\x0c\x92\xc9\xe3\x13\xc5\x0f\x8b\xf7\xd7\xd7\x7e\ \x9d\xe5\x1f\xc7\x04\x04\xb6\x6d\x67\x8e\x1d\x1d\x9c\x00\x90\xc6\ \x39\x2b\xa0\x0b\x70\x80\x9d\xf7\xb9\x6e\x06\xa8\x00\x65\x20\xf8\ \x0d\xae\xfd\x9c\x7a\xf5\xeb\x41\x84\x00\x00\x00\x00\x49\x45\x4e\ \x44\xae\x42\x60\x82\ \x00\x00\x01\x38\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x0c\x00\x00\x00\x08\x08\x06\x00\x00\x00\xcd\xe4\x1e\xf1\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x01\x23\x00\x00\ \x01\x23\x01\x72\x41\x77\xde\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdf\x07\x0b\x0a\x1a\x2f\x5c\xf6\x1d\x1f\x00\x00\x00\xc5\x49\x44\ \x41\x54\x18\xd3\x5d\x8e\x41\x4a\x03\x41\x14\x44\xdf\xff\x03\x39\ \x87\x67\x70\xed\x29\x66\xe6\x1c\x7a\x11\x51\x71\xa3\x92\x1b\x34\ \xcd\x6c\x03\x0a\xd2\x0b\x2f\x11\x10\x95\x6c\x7b\x9b\x85\x99\x40\ \x75\x36\x33\xa1\x33\x05\xb5\xa9\x5f\xf5\xab\xac\xeb\xba\x37\xe0\ \x1a\x28\x33\x25\x15\x40\xee\x5e\x6a\x1d\xd8\xb8\x99\xdd\x49\xda\ \x02\xe3\x4c\x77\x1f\xdd\x7d\xac\x35\x49\xdf\x66\xf6\x64\xa5\x14\ \xfa\xbe\xbf\x02\x5e\x80\xd5\xe2\xa3\x24\xe1\xee\xff\xee\x7e\x1b\ \x42\xd8\x39\x40\x8c\xf1\x4f\xd2\x83\xa4\x23\x70\xa6\xa4\xe3\xd4\ \xf4\x1c\x42\xd8\x01\x38\x13\x86\x61\xf8\x68\x9a\x66\xb3\x9c\x66\ \x66\xef\x31\xc6\xcf\xd9\x77\x0e\x00\xe4\x9c\x5f\x81\x9f\x2a\xf4\ \x9b\x73\x5e\xd7\x9e\x8b\x40\x4a\xe9\x60\x66\xf7\xc0\x1e\xd8\x9b\ \xd9\x63\x4a\xe9\x50\x7b\xac\x94\xc2\x12\x6d\xdb\xde\x4c\x33\xbf\ \x96\xb7\x13\xc1\xd5\x6a\x8f\x71\xe6\xb7\x55\x00\x00\x00\x00\x49\ \x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x01\x27\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x00\xa4\x49\x44\ \x41\x54\x38\x8d\xed\x93\x31\x0e\x82\x40\x14\x05\x67\x37\xe4\x03\ \x26\xc6\x82\x9b\xd0\x52\x1b\x2e\x62\x3c\xc4\x56\x74\xdc\xc1\x78\ \x11\x63\x4d\xcb\x4d\x0c\x31\x16\x2c\xbf\xf8\xd8\x60\x63\x05\x3d\ \xaf\x9f\x79\xd5\xb8\xa6\x69\x0e\xaa\x5a\x99\x59\x09\xe4\xac\xdb\ \xe8\xbd\xef\x45\xa4\x4b\x54\xb5\x02\x0a\xe0\x96\x65\xd9\x67\x0d\ \x1d\x63\x3c\x02\xb5\xaa\x56\x89\x99\x95\xce\xb9\x7b\xdb\xb6\xc3\ \xca\x77\x80\x21\x84\xf0\x9c\xe7\xf9\xe2\x81\x3c\x4d\xd3\xf7\x06\ \x18\x80\x85\xc9\xfd\x56\xf0\x7f\xbb\x60\x17\xfc\x04\xe3\x34\x4d\ \xa7\xad\xe0\xc2\x8c\x2e\x84\x70\x06\x0a\x33\x7b\x6c\x89\xc9\x7b\ \x5f\x03\xaf\x44\x44\xba\xa5\xc8\x6b\x8c\x71\x75\xce\x40\x2f\x22\ \xdd\x17\x19\xae\x41\x63\xfb\x88\xbf\xfe\x00\x00\x00\x00\x49\x45\ \x4e\x44\xae\x42\x60\x82\ \x00\x00\x03\x25\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x02\xa2\x49\x44\ \x41\x54\x38\x8d\xa5\x93\xcb\x6a\x53\x61\x10\xc7\xff\x33\xf3\x9d\ \xef\x9c\xd0\xa6\x4d\xf1\x52\x51\x62\x6b\x4c\x94\xd2\x14\xc5\x82\ \x88\xba\x88\x16\x4b\x25\x1b\x05\x15\x74\xe1\x13\x78\xdb\xb8\xd3\ \x8a\x3e\x81\x97\x37\x70\x23\xa8\xe0\xb6\x44\xb4\x20\xfa\x00\x62\ \xba\xaa\x0a\x56\xbc\x2b\xa5\x69\x4f\x6e\xe7\x9c\x6f\x5c\x34\xb6\ \xf5\xb2\x73\x60\x18\x18\x7e\x33\xcc\xf0\x9f\x21\x55\xc5\xff\x98\ \xf9\x33\xb1\xae\x50\xe8\x09\x9a\xcd\x4b\x44\x7c\x04\x8a\x5d\x00\ \x00\xc2\x4b\x55\xf7\xb8\x19\x04\x37\x7f\xcc\xce\xd6\xd6\xf2\xf4\ \x6b\x02\x22\xa2\x2d\x5b\xb6\x1e\x06\xe9\x3d\x11\xe9\x11\x61\x1f\ \xa0\x0e\xa6\x48\x12\xd7\x4a\x92\xa4\x06\xa5\xd3\x1f\x3e\xcc\x3d\ \xd5\x4e\x21\xa9\x2a\x88\x88\x37\x6f\xde\x3a\xc6\x82\x47\x9e\xe7\ \x75\x19\x63\xc0\xcc\x20\x5a\x6e\xa0\xaa\x70\xce\x21\x8e\x63\x44\ \x51\x14\xba\x04\xc7\x3f\x7e\x9c\x7b\xa2\xaa\x8e\x00\xd0\xa6\x7c\ \x7e\xbd\x34\x5b\x33\xd6\xf7\x37\x78\x9e\x07\x11\xf9\xe7\xbe\x49\ \x92\x20\x8a\x22\xb4\x5b\xad\x6f\x49\xe0\x0f\x7f\x7e\xfd\xfa\xbb\ \x8c\x8e\x8e\x7a\xcd\xda\xd2\x65\x31\xe6\xb0\xb5\xd6\x18\x63\x40\ \x20\xf4\x6e\x18\xc4\xce\xfd\x67\xd0\xbf\x6d\x14\x8d\xc5\x6f\x68\ \xd7\x6b\xcb\x53\x81\xa0\xaa\x86\xa3\x38\x2e\x14\xf2\xcf\x39\x97\ \xcb\x19\x62\x1a\x33\x46\x7c\x11\x01\x11\x21\xd3\xbf\x1d\x7b\x8f\ \x5d\xc1\xe0\xd0\x3e\x0c\x0e\xed\xc7\xde\x63\x57\xd1\xbb\x71\x1b\ \x88\x08\x22\x02\x63\xc4\x27\xa6\xb1\x5c\x2e\x67\xd8\x5a\xeb\x11\ \xd1\x08\xb3\xac\xec\x3d\xb0\xfb\x28\x52\xbe\x45\x77\xe0\xa3\x3b\ \xe5\xa3\x2b\x65\x31\xb0\x6b\x02\x44\x04\x66\x06\xb3\x80\x88\x46\ \xac\xb5\x9e\xc9\x66\xb3\xd2\x91\x6a\xc5\x3b\xc0\xaa\x54\xc4\x60\ \x36\xc0\xaf\x5c\x27\x66\xb3\x59\xe1\x4c\x26\xe3\x58\xb8\xba\x0a\ \x13\xe6\x5e\x55\x10\x36\x5a\x08\x9b\xed\x65\xaf\x37\xf0\xbe\x5a\ \xe9\xd4\x29\x00\x05\x0b\x57\x33\x99\x8c\x33\xe9\x74\x3a\x09\x6c\ \xf0\x2c\x8a\xa3\x3d\x00\x7c\x22\xc2\xc2\x97\x59\xbc\xac\xdc\xc4\ \x40\x71\x1c\xaa\x8a\xb9\x6a\x05\x0b\x5f\xdf\xac\xca\xea\x5c\x2b\ \xb0\xc1\xb3\x74\x3a\x9d\xd0\xf4\xf4\xb4\xa9\x54\x2a\xb9\xfb\x0f\ \x1e\xbe\xe8\xea\xea\x5e\x2f\x22\x70\xce\xfd\x53\x46\x66\x46\xe2\ \x12\x84\x4b\x4b\xdf\x4f\x9d\x3c\x71\x60\x7c\x7c\xfc\x2d\x97\x4a\ \xa5\xb8\x5c\x2e\x7f\xda\x51\xc8\x9f\xab\xd7\xc3\x50\x55\x61\x3c\ \x0f\x2c\x02\x62\x06\x31\x83\x45\x60\x3c\x0f\xaa\x8a\x7a\x18\x86\ \x3b\x0a\xf9\x73\xe5\x72\xf9\x53\xa9\x54\x8a\x57\x4e\xb9\x5a\xad\ \xf6\x4f\x4e\x5e\x3f\x54\x9d\x79\x75\xc7\x78\x5e\xda\x5a\xeb\x8b\ \x98\xce\x01\xc5\x68\xb7\xdb\xad\x38\x8a\x16\x8b\xc3\x23\xe7\x6f\ \xdc\xb8\x36\x5d\x2c\x16\xbf\xfc\xf6\x0b\x00\x30\x3f\x3f\xdf\x3b\ \x35\x35\x35\x70\xeb\xf6\xed\xb3\xb5\x85\xda\xc1\x38\x49\x86\x01\ \xc0\x88\xcc\xf4\xf4\xf6\x3c\xbf\x78\xe1\xc2\xdd\x89\x89\x89\x77\ \x7d\x7d\x7d\x0b\x7f\x3d\xd3\x1a\xb3\x00\xd2\x8d\x46\x23\x60\xe6\ \x00\x00\x9c\x73\xcd\x54\x2a\xd5\x04\xb0\x08\xa0\xbd\x16\xfe\x09\ \x98\x59\x08\x23\xbf\xfa\x7e\x03\x00\x00\x00\x00\x49\x45\x4e\x44\ \xae\x42\x60\x82\ \x00\x00\x03\x25\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x02\xa2\x49\x44\ \x41\x54\x38\x8d\x7d\x93\x31\x48\x63\x59\x14\x86\xbf\x73\x73\x79\ \x93\x97\xbc\xbc\x65\x92\x0d\x82\x85\x85\x85\x56\xf1\x39\xca\x0a\ \x22\x53\x2e\x58\xd8\x2d\x29\xdd\x6e\xaa\x61\x4a\x19\x1c\x2c\x05\ \x17\x84\x34\x4b\xaa\x2d\x96\x45\xa6\x5c\xb0\xb0\xdb\x29\x1c\x64\ \xb1\x90\x25\x68\x67\x22\x0b\x76\x03\x4f\x31\xfa\x7c\x49\xde\xf3\ \xe5\xde\x6d\x34\xc4\x61\x77\x6f\xf9\xdf\x73\xcf\x7f\xce\xb9\xe7\ \x13\x6b\x2d\x5f\x1f\x11\x11\x40\x6d\x6c\x6c\x54\x01\x76\x77\x77\ \x43\xc0\xd8\x7f\x09\x96\x71\x4d\x44\x64\x6b\x6b\x6b\x7a\x76\x76\ \xf6\xe7\x5c\x2e\xf7\x9d\xef\xfb\x2f\x00\xa2\x28\x4a\xb2\x2c\x3b\ \x39\x3f\x3f\x7f\xb7\xbd\xbd\xfd\xf7\x78\x22\x3d\xf6\x58\x35\x9b\ \xcd\x37\xd5\x6a\xf5\xa7\x95\x95\x95\x6f\x7c\xdf\x97\xa7\xbb\x2c\ \xcb\x4a\x83\xc1\x60\xb5\x50\x28\xfc\xd5\x6c\x36\xdf\x8b\xc8\x2f\ \xd6\x5a\x33\xaa\x40\x44\x64\x67\x67\xe7\xfb\x20\x08\x7e\x5f\x5e\ \x5e\xf6\xac\xb5\x0c\x87\x43\x00\xac\xb5\x44\x51\x84\xd6\x1a\xcf\ \xf3\x38\x39\x39\xb9\x6f\xb5\x5a\x3f\x6c\x6e\x6e\xfe\x61\xad\xb5\ \x0a\xa0\x5e\xaf\xbf\x9c\x98\x98\xf8\x75\x69\x69\xc9\xeb\x76\xbb\ \xc4\x71\xcc\x98\x3b\xd6\x5a\xee\xef\xef\x09\xc3\x90\xc5\xc5\x45\ \x6f\x72\x72\xf2\xb7\x7a\xbd\xfe\x12\x40\x89\x88\xd4\x6a\xb5\xd5\ \x85\x85\x85\x6f\xa3\x28\x22\x8e\x63\xfa\xfd\x3e\x59\x96\x8d\x12\ \xa4\x69\x8a\x31\x86\x87\x87\x07\xe2\x38\x26\x08\x82\x72\xad\x56\ \x5b\x15\x11\x51\x80\xf2\x7d\xff\x75\xa5\x52\x71\x06\x83\x01\xc6\ \x18\x06\x83\x01\xdd\x6e\x17\x6b\x2d\x49\x92\x8c\xaa\xb0\xd6\xd2\ \xef\xf7\xa9\x54\x2a\x8e\xef\xfb\xaf\x01\xa5\x81\x9c\xd6\xfa\x95\ \xe3\x38\xa4\x69\xca\xd3\x80\x93\x24\xa1\xdb\xed\x62\x8c\xc1\x18\ \x33\x6a\x29\x4d\x53\x1c\xc7\x41\x6b\xfd\x0a\xc8\x29\x80\x5e\xaf\ \xd7\xc9\xb2\x0c\xa5\xd4\xc8\xe9\x71\xb8\xb8\xae\xfb\x4c\x57\x4a\ \x91\x24\x09\xbd\x5e\xaf\x03\xa0\x80\x61\x18\x86\x9f\x6f\x6f\x6f\ \x87\x8e\xe3\x8c\x1c\x45\x04\xcf\xf3\x70\x5d\x97\x52\xa9\x84\xd6\ \x1a\x63\x0c\x8e\xe3\x70\x7d\x7d\x9d\x85\x61\xf8\x19\x18\x2a\xc0\ \x74\x3a\x9d\x4f\x87\x87\x87\xb7\xa5\x52\x09\x00\x63\x0c\xae\xeb\ \xa2\xb5\x46\x44\xc8\xe7\xf3\x14\x0a\x05\x8a\xc5\x22\xbe\xef\x73\ \x7c\x7c\x7c\xd7\xe9\x74\x3e\x01\x46\x5b\x6b\xad\x88\x7c\x99\x9e\ \x9e\xfe\xd0\x6a\xb5\x1a\xf3\xf3\xf3\x85\xbb\xbb\x3b\xf2\xf9\xfc\ \xa8\x6f\xad\x35\xae\xeb\x52\x2e\x97\x39\x3a\x3a\x8a\x2f\x2e\x2e\ \x3e\xec\xef\xef\x7f\xb1\xd6\xda\xa7\x4d\x4c\x1a\x8d\xc6\xc7\x62\ \xb1\x38\x7f\x75\x75\xf5\xe3\xda\xda\x5a\x41\x29\x85\x52\x0a\x00\ \xa5\x14\x22\xc2\xc1\xc1\x41\xdc\x6e\xb7\xf7\x1a\x8d\xc6\x47\x20\ \x79\xc6\xc2\xe3\x97\x16\xd6\xd7\xd7\x57\x66\x66\x66\x9a\xe5\x72\ \xb9\x3a\x35\x35\xa5\x00\x2e\x2f\x2f\xcd\xcd\xcd\x4d\xd8\x6e\xb7\ \xdf\xee\xed\xed\xfd\x09\xf4\x9e\xad\xf2\x57\x14\xbe\x00\x9c\x20\ \x08\x4a\x73\x73\x73\x01\xc0\xd9\xd9\xd9\xe9\xe9\xe9\x69\x04\xa4\ \x40\x32\x0e\x93\xfc\x1f\xce\x40\xee\x51\x1a\xf2\x1f\x38\xff\x03\ \xf0\x04\x62\x01\xa1\x58\x6a\x98\x00\x00\x00\x00\x49\x45\x4e\x44\ \xae\x42\x60\x82\ \x00\x00\x01\xfa\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\x77\x49\x44\ \x41\x54\x38\x8d\xad\x93\xb1\x6a\x1b\x41\x14\x45\xef\x9d\x11\x6c\ \x7a\x83\x40\x32\x08\x84\x7a\xb5\x6a\x17\x92\xde\x5d\x20\x90\x0f\ \xf0\x17\x6c\xb1\x30\xc5\xb2\xb0\x88\xf9\x02\xff\x41\x9a\x34\xae\ \x45\x84\x40\x6a\xd4\x05\x15\x69\x82\x30\x08\x64\x13\x91\xd4\x61\ \x25\xcf\xdc\x34\x12\x28\x60\x0b\xcb\xf8\xd4\xef\xcc\x7d\xbc\x37\ \x8f\x92\x70\x4c\x9e\xe7\x17\x24\xfb\x21\x84\x4b\x63\x4c\x1b\x00\ \x62\x8c\xf7\xd6\xda\xb5\xa4\x45\x55\x55\x7f\x8e\xeb\x79\x78\x80\ \x24\xb3\x2c\x1b\x18\x63\xde\x4b\x5a\x02\x58\x85\x10\x1e\x00\xc0\ \x5a\xdb\x02\xd0\x21\xd9\x8b\x31\x7e\xf3\xde\xcf\xb5\x17\x29\x09\ \x24\x99\xe7\xf9\x27\x49\x4d\x6b\xed\x6d\x59\x96\x77\x78\x02\xe7\ \x5c\x37\x84\x70\x45\x72\x53\x55\xd5\x17\x49\x32\x00\x90\x65\xd9\ \x40\x52\x33\x49\x92\x9b\xe7\x64\x00\x28\xcb\xf2\x2e\x49\x92\x1b\ \x49\xcd\x2c\xcb\x06\x00\x60\xb7\xdb\xed\x05\xc9\x8f\x8d\x46\xe3\ \x6b\x51\x14\xbf\x9f\x93\x0f\xa4\x69\xfa\x38\x99\x4c\x7e\x49\xba\ \x1a\x8f\xc7\x3f\x0c\xc9\xbe\xa4\xe5\xa9\xe4\xa7\x3a\x91\xb4\x24\ \xd9\x37\x21\x84\x4b\x00\xab\x97\xca\x47\xac\xf6\x9b\x32\xed\xc3\ \xb4\xcf\x21\x84\xf0\x60\x8c\x69\x9b\x57\x24\xff\x87\xd9\x7f\x92\ \xd6\xb9\xa2\xb5\xb6\x15\x63\xbc\x37\xd6\xda\x35\x80\xce\x2b\xc2\ \x3b\xd6\xda\xb5\x91\xb4\x20\xd9\x73\xce\x75\x5f\x6a\x3a\xe7\xba\ \x24\x7b\x92\x16\x76\x3a\x9d\xfe\x1d\x8d\x46\x3b\x00\x1f\x66\xb3\ \xd9\xf7\x34\x4d\x1f\x4f\xc9\x45\x51\xbc\xdb\xed\x76\x9f\x63\x8c\ \xd3\xe1\x70\xf8\xd3\x00\x80\xf7\x7e\x4e\x72\x53\xd7\xf5\xf5\xa9\ \x4e\x9c\x73\xdd\xba\xae\xaf\x49\x6e\xbc\xf7\x73\xe0\xad\x8e\xe9\ \x98\x73\xcf\xf9\x1f\xbc\xbd\xe7\x61\x1d\xe4\x9f\x24\x00\x00\x00\ \x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\x71\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\ \x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\ \x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\ \x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\ \x95\x2b\x0e\x1b\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\ \x0b\x01\x28\x74\x6d\x24\x49\x00\x00\x01\xf1\x49\x44\x41\x54\x58\ \xc3\xcd\x57\x41\x4e\x02\x31\x14\xfd\xfd\xb5\x6e\x70\x21\x89\xb8\ \x11\x8d\x89\x5b\xef\x80\x3b\xb7\xde\x80\x36\x70\x2c\xa0\xbd\x86\ \x3b\xc2\x19\xd8\xb9\x21\x11\x13\x23\x0b\x4d\x94\x49\xb4\x69\xeb\ \xc6\xea\xa4\x61\x86\xb6\x40\xf0\xef\xc8\xd0\x79\x7d\xef\xbf\x79\ \xed\x07\xd8\x73\x91\xaa\x07\x4a\xa9\x57\x00\x00\xe7\xdc\x52\x08\ \xd1\xce\x79\xb9\x94\x72\x4e\x08\x69\x00\x00\x70\xce\x9b\x51\x1b\ \x18\x0c\x06\x53\xc6\xd8\xa5\x5f\x08\x00\x60\xad\x5d\x70\xce\x4f\ \x53\xc0\x95\x52\x2f\x88\xd8\xf2\xbf\x9d\x73\x4b\xad\xf5\xac\xdf\ \xef\x5f\x97\xff\x87\xe1\xc2\x10\x1c\x00\x00\x11\x5b\x52\xca\x79\ \x0a\xf3\x32\x38\x00\x00\x21\xa4\xc1\x18\xbb\xac\x54\x60\x15\xf3\ \xb0\xb4\xd6\xf3\x5e\xaf\x77\x5e\x07\x3e\x1c\x0e\x1f\x19\x63\x95\ \x2d\x0b\x95\xc0\x3a\xe6\x61\x51\x4a\x6b\x95\x90\x52\xce\x29\xa5\ \xad\x5a\xd3\x05\x4a\x10\x6f\x38\x44\x3c\x8e\x95\x78\x95\x27\xc2\ \x9e\x47\xbc\xe3\x8d\x73\xde\xc4\x1c\x77\x87\x9e\x58\xd5\xf3\xd8\ \x3a\xf0\x7d\x01\x80\xe3\x94\x85\x94\xd2\x33\xa5\xd4\x8b\xdf\x50\ \x2a\xf0\x0f\xe6\x9f\x09\x53\x25\xdc\xa4\xca\x2d\xfc\x6d\x01\xe7\ \xfc\xd4\x18\xf3\xb4\x6b\x70\x63\xcc\x53\xd9\x3f\x64\x5d\x80\xec\ \x8a\x79\x65\x10\xed\x4a\x89\x90\x79\xcc\x59\xb0\x35\x25\xea\xa2\ \xbc\xf2\x33\xdc\x96\x12\x55\xcc\xd7\x2a\xb0\x0d\x25\x62\x0e\x31\ \x84\x3d\x17\xa6\x9e\x6a\x9b\x24\x66\xea\x85\x64\x7f\x26\xdc\x94\ \x79\x8a\x12\xff\x2b\x88\xb6\xcd\x3c\x46\x89\xff\x71\x18\xe5\x32\ \xb7\xd6\x2e\xac\xb5\x8b\x4d\x94\x40\x7f\x4d\xca\x4d\xb8\xdc\xc4\ \xf4\x98\x98\x2b\x61\x79\x56\x10\x42\xb4\x73\x94\xf8\xdd\x00\xe7\ \xbc\xe9\x6f\x28\xb9\xd9\x9e\xa2\x84\x73\x6e\xe9\x07\x15\x2c\x5d\ \xb9\x67\xeb\x36\x11\x32\x0f\x4b\x08\xd1\x36\xc6\x3c\x03\x80\xad\ \x21\xf0\xa1\xb5\x9e\x55\xe6\x80\x94\xf2\x1d\x11\x1b\x84\x10\x52\ \x02\xfe\x72\xce\xdd\x0a\x21\xc6\x91\x83\xc9\x0d\x21\xe4\x1e\x11\ \x0f\x43\xe6\xdd\x6e\xf7\xa8\x36\x09\xa7\xd3\xe9\x95\xb5\x76\x59\ \x02\xff\x2c\x8a\xe2\x2e\x16\xfc\x47\x89\x71\x51\x14\x77\xd6\xda\ \xcf\x70\x20\x49\x1e\x4e\x27\x93\xc9\xc5\x68\x34\x7a\xcf\x31\x58\ \xa7\xd3\x39\x11\x42\x3c\xd4\x0d\xa7\x7b\xaf\x6f\x1a\x2b\x5a\x88\ \xa9\x6c\x88\x2e\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \ \x00\x00\x01\xaf\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\x2c\x49\x44\ \x41\x54\x38\x8d\xa5\x93\x4d\x4e\x02\x41\x10\x85\xbf\x6a\x7a\x88\ \x8d\x1c\x81\x44\x49\x94\xc0\xc2\xa5\x17\x70\x05\x31\xf1\x08\xde\ \x81\x05\xa7\x60\xc1\x0d\xbc\x82\x5b\x57\x5c\xc1\xa5\xc4\x18\x7f\ \x92\x39\x81\x8e\x1a\x7b\x06\xca\xc5\xfc\x30\x46\x85\x36\x56\xa7\ \x37\x9d\xf7\x5e\xd7\x7b\xd5\x2d\xaa\xca\x7f\xca\x02\x74\x3a\x9d\ \x9e\x88\xcc\x14\x86\x80\xd9\xc2\x59\x09\x5c\xa9\xea\x38\x8e\xe3\ \x5b\x0b\x20\x22\xb3\xa8\xd9\x1c\x35\x1a\x56\x44\xe4\x57\xa6\x31\ \x06\xc0\x78\xff\x31\x4a\xbd\x07\x38\xb5\x00\x0a\x43\x6b\x23\xb1\ \xd6\x06\xb5\x1d\x45\x4d\xf1\xde\x0f\x2b\x0b\xb9\xb8\x61\xd3\xed\ \xf5\x2a\x70\xa6\x2e\x00\x02\x04\x0a\x50\x83\xd9\xf5\x59\xbe\xc2\ \xf8\x6b\x5c\x19\x62\x4e\x17\xe1\xec\x7c\xcc\xe1\xd1\xf1\x8f\xc4\ \xfb\x9b\x6b\x2e\x2f\xa6\x05\x36\x1f\x96\xc9\x05\x4c\x61\x01\x1a\ \x1b\x82\x5c\x2e\xb3\x0a\x57\xe6\x25\xaa\xca\xde\x7e\x57\x77\xdb\ \x6d\x42\xa7\x90\x65\x19\xaf\x49\xc2\xd3\xe3\x83\x7c\xb5\xf0\x87\ \x0c\xca\x0e\x2a\x01\xca\x1d\xa4\xf0\x5d\x60\x05\x98\xd0\x77\x50\ \xe3\xe4\x21\xba\x96\x9b\x67\x59\xaa\x95\x9d\x0d\x1b\x20\x4d\xbd\ \xba\x96\x9b\x57\x1d\xf4\x06\x83\xc9\xdd\x62\x31\x4d\x5e\x9e\x4f\ \x08\xf8\x4c\xce\xed\xcc\x0f\xfa\xfd\x09\x14\x53\x28\x48\x2d\xc0\ \xc1\xd6\x24\x15\x78\x07\xde\x80\xd5\x27\xee\x62\x4d\x25\xdc\xe9\ \x0e\xda\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x03\x37\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ \x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\ \x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ \x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ \x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x02\xb4\x49\x44\ \x41\x54\x38\x8d\x7d\x93\xcf\x4b\x23\x67\x18\xc7\x3f\xef\xfc\xca\ \x30\x6a\xe2\x38\x82\xc4\xc1\x10\x54\x44\x09\xd8\xde\x64\xc5\x43\ \x61\x8f\xee\x5e\x14\xc4\x93\xe2\x7f\xb0\xd0\x5b\x17\x4a\xa1\xb4\ \xb7\xc2\x6e\xcf\x82\x1e\xed\xd9\x82\x17\xcb\x42\xc1\xc3\xde\xec\ \xa1\x44\x41\x17\x36\x20\x9a\x61\x93\x38\x99\x09\x99\x0c\x33\xef\ \xdb\x4b\x13\x6c\x97\xf6\x81\xe7\xf2\xf0\x3c\x9f\x87\x87\xe7\xfb\ \x15\x4a\x29\xfe\x1d\x42\x08\x0d\xb0\x17\x17\x17\xcb\x00\x37\x37\ \x37\xf7\x40\xa2\x94\x92\x9f\xf5\x3e\x05\x08\x21\xb4\x95\x95\x95\ \x2f\x17\x16\x16\x7e\x32\x4d\xf3\x8b\xd9\xd9\xd9\x02\x40\xb3\xd9\ \x1c\x24\x49\xf2\xc7\xed\xed\xed\xd7\xf5\x7a\xfd\xf2\x29\xc8\x78\ \x32\x5c\x58\x5f\x5f\x7f\x35\x3f\x3f\xff\x7a\x6f\x6f\x6f\xc2\x75\ \x5d\x34\x4d\x03\x20\xcb\x32\x27\x8e\xe3\xaf\x4e\x4e\x4e\x7e\x77\ \x5d\xf7\x7b\x21\xc4\x1b\xa5\xd4\x60\x04\x10\x42\x68\xb5\x5a\x6d\ \x6b\x6d\x6d\xed\xdb\xdd\xdd\x5d\x47\x08\x81\x52\x8a\x3c\xcf\x51\ \x4a\x11\x86\x21\x4a\x29\xf6\xf7\xf7\xc7\x4e\x4f\x4f\xbf\x0b\xc3\ \xb0\x21\x84\xf8\x45\x29\x25\x35\x80\x52\xa9\x54\xad\x56\xab\x6f\ \xb7\xb7\xb7\x9d\xc7\xc7\x47\x7a\xbd\xde\xe8\xac\x2c\xcb\xc8\xf3\ \x9c\x4e\xa7\xc3\xc3\xc3\x03\x9b\x9b\x9b\xf6\xd2\xd2\xd2\xcf\xa5\ \x52\xa9\x0a\xa0\x09\x21\x34\xdf\xf7\x5f\xec\xec\xec\xb8\x61\x18\ \xd2\x6a\xb5\x88\xa2\x88\x34\x4d\x47\x80\x38\x8e\x91\x52\x92\xa6\ \x29\x51\x14\xb1\xb5\xb5\xe5\xfa\xbe\xff\x42\x08\xa1\x69\x80\x5d\ \x2c\x16\x9f\xf9\xbe\x6f\xf4\x7a\x3d\xa4\x94\x74\xbb\x5d\x9a\xcd\ \x26\x79\x9e\xd3\xed\x76\x19\x0c\x06\x48\x29\x91\x52\xd2\xeb\xf5\ \x28\x97\xcb\x7a\xb1\x58\x7c\x06\xd8\x06\x60\x5b\x96\x55\xb3\x2c\ \x8b\x24\x49\x18\x7e\x25\x8e\x63\xee\xef\xef\x47\x27\x0c\x23\x49\ \x12\x4c\xd3\xc4\xb2\xac\x1a\x60\x6b\x00\xfd\x7e\xff\x63\x96\x65\ \xe8\xba\x3e\xda\x24\xa5\xc4\xb2\x2c\xa6\xa7\xa7\x31\x0c\x63\x54\ \xd3\x75\x9d\x34\x4d\xe9\xf7\xfb\x1f\x01\x34\x20\x09\x82\xe0\x7d\ \xab\xd5\x92\xb6\x6d\xff\x63\x78\x72\x72\x92\xf1\xf1\x71\x66\x66\ \x66\x70\x1c\x07\x29\x25\xb6\x6d\x13\x04\x81\x0c\x82\xe0\x3d\x90\ \x68\x40\xd2\x6e\xb7\xdf\x1d\x1e\x1e\xc6\x53\x53\x53\x0c\x5f\xe8\ \xba\x2e\xa6\x69\x02\x30\x36\x36\x86\xeb\xba\x78\x9e\x87\xe7\x79\ \x1c\x1f\x1f\x47\xed\x76\xfb\x1d\x90\x68\x4a\x29\x19\x45\xd1\x87\ \x7a\xbd\xfe\xe6\xfc\xfc\x7c\x50\xa9\x54\xf0\x3c\x8f\x42\xa1\x80\ \x10\x02\x00\x5d\xd7\x71\x1c\x87\x72\xb9\xcc\xd9\xd9\x59\x72\x7d\ \x7d\xfd\x36\x8a\xa2\x0f\x4a\x29\x29\x94\x52\x43\xed\x2f\x2c\x2f\ \x2f\xff\xb8\xb1\xb1\xf1\xf2\xe0\xe0\xa0\x60\x18\x06\xba\xae\x0f\ \x55\x4a\xbf\xdf\xe7\xe8\xe8\x28\xb9\xb8\xb8\xf8\xf5\xea\xea\xea\ \x1b\xe0\x76\x04\x18\x4a\x19\xa8\xb8\xae\xfb\xbc\x52\xa9\xbc\x9e\ \x9b\x9b\x73\x57\x57\x57\x75\x80\xcb\xcb\xcb\xfc\xee\xee\xae\xd3\ \x68\x34\x7e\xe8\x74\x3a\xbf\x01\x8d\xa1\x94\x3f\x33\x13\x30\x03\ \x4c\x19\x86\x31\x3d\x31\x31\x51\x03\x88\xa2\xe8\xcf\x2c\xcb\x3e\ \x01\x6d\xa0\xf9\xd4\x4c\xe2\xff\xec\xfc\x77\x02\x24\xfc\x87\x9d\ \xff\x02\xc2\xc8\x68\xcd\x8c\xad\x32\xab\x00\x00\x00\x00\x49\x45\ \x4e\x44\xae\x42\x60\x82\ \x00\x00\x00\xe4\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x36\x00\x00\x00\x0a\x08\x06\x00\x00\x00\xff\xfd\xad\x0b\ \x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\ \x06\x62\x4b\x47\x44\x00\x7f\x00\x87\x00\x95\xe6\xde\xa6\xaf\x00\ \x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\ \x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\ \x09\x2a\x2b\x98\x90\x5c\xf4\x00\x00\x00\x64\x49\x44\x41\x54\x48\ \xc7\x63\xfc\xcf\x30\x3c\x01\x0b\xa5\x06\x34\xb4\x4f\x85\x87\xcd\ \xaa\xa5\x73\x18\xae\x5d\x39\xcf\x48\x2b\x35\x14\x79\xcc\xd8\xc8\ \x88\x24\x03\x7c\x89\xd0\x4f\x2d\x35\x84\xc0\xd9\x73\xe7\xe0\x6c\ \x26\x86\x91\x92\x14\x91\x7d\x4d\x54\x52\x0c\x4d\x26\xa8\x9f\x5a\ \x6a\x46\x93\xe2\x68\x52\x1c\x82\x49\x91\x91\xd2\x7a\x4c\x4b\xc7\ \x10\xc5\x08\x6c\xc5\x34\xb5\xd4\xd0\xd5\x63\x83\x15\x00\x00\x7a\ \x30\x4a\x09\x71\xea\x2d\x6e\x00\x00\x00\x00\x49\x45\x4e\x44\xae\ \x42\x60\x82\ \x00\x00\x00\xb6\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x51\x00\x00\x00\x3a\x04\x03\x00\x00\x00\x3a\x92\xa8\x9c\ \x00\x00\x00\x21\x50\x4c\x54\x45\xff\xff\xff\x63\x56\x4d\x63\x56\ \x4d\x63\x56\x4d\x63\x56\x4d\x63\x56\x4d\x63\x56\x4d\x63\x56\x4d\ \x63\x56\x4d\x63\x56\x4d\x63\x56\x4d\x09\xc8\xec\xe3\x00\x00\x00\ \x0b\x74\x52\x4e\x53\x00\x7c\x17\x59\x3a\x33\x65\x78\x2f\x5c\x3f\ \x1d\x78\xc5\x55\x00\x00\x00\x39\x49\x44\x41\x54\x48\xc7\x63\x60\ \xc0\x04\x8e\x82\x01\x0c\xc4\x81\x51\x95\xa3\x2a\x47\x55\x8e\x24\ \x95\x8b\x94\x30\x80\x02\x76\x95\x85\x82\x18\x40\x80\x32\x95\x6d\ \x69\x93\x8d\x31\x80\x01\xc3\x28\x18\x05\xa3\x60\x14\x0c\x59\x00\ \x00\x54\x93\x1c\x9a\x82\xb1\x8a\x9f\x00\x00\x00\x00\x49\x45\x4e\ \x44\xae\x42\x60\x82\ \x00\x00\x00\x81\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x01\x03\x00\x00\x00\x25\x3d\x6d\x22\ \x00\x00\x00\x06\x50\x4c\x54\x45\x00\x00\x00\xae\xae\xae\x77\x6b\ \xd6\x2d\x00\x00\x00\x01\x74\x52\x4e\x53\x00\x40\xe6\xd8\x66\x00\ \x00\x00\x29\x49\x44\x41\x54\x78\x5e\x05\xc0\xb1\x0d\x00\x20\x08\ \x04\xc0\xc3\x58\xd8\xfe\x0a\xcc\xc2\x70\x8c\x6d\x28\x0e\x97\x47\ \x68\x86\x55\x71\xda\x1d\x6f\x25\xba\xcd\xd8\xfd\x35\x0a\x04\x1b\ \xd6\xd9\x1a\x92\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \ \x00\x00\x00\xa2\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\ \x0d\xd7\x01\x42\x28\x9b\x78\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ \xdd\x09\x02\x0e\x0c\x22\xb6\x26\x2c\x49\x00\x00\x00\x2f\x49\x44\ \x41\x54\x38\xcb\x63\x60\x18\xde\x60\xdf\xbe\x7d\xff\xf7\xed\xdb\ \xf7\x1f\x9f\x1a\x26\x4a\x2d\x19\x78\x03\x18\xd1\xfd\x4c\x8c\x26\ \x27\x27\x27\x46\xaa\xb9\x60\x34\x16\x86\x05\x00\x00\x06\x79\x10\ \xf4\xa1\x46\x89\x2c\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\ \x82\ " qt_resource_name = b"\ \x00\x06\ \x06\x8a\x9c\xb3\ \x00\x61\ \x00\x73\x00\x73\x00\x65\x00\x74\x00\x73\ \x00\x11\ \x04\x25\x6c\x07\ \x00\x72\ \x00\x61\x00\x64\x00\x69\x00\x6f\x00\x2d\x00\x63\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \ \x00\x1d\ \x05\x05\x33\x47\ \x00\x63\ \x00\x68\x00\x65\x00\x63\x00\x6b\x00\x62\x00\x6f\x00\x78\x00\x2d\x00\x63\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x65\x00\x64\x00\x2d\ \x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x1b\ \x0b\xdf\xfe\x87\ \x00\x63\ \x00\x68\x00\x65\x00\x63\x00\x6b\x00\x62\x00\x6f\x00\x78\x00\x2d\x00\x6d\x00\x69\x00\x78\x00\x65\x00\x64\x00\x2d\x00\x64\x00\x69\ \x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x15\ \x0b\xf3\xc4\x67\ \x00\x75\ \x00\x70\x00\x2d\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2d\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\ \x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0f\ \x06\x16\x91\xe7\ \x00\x62\ \x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x2d\x00\x6d\x00\x6f\x00\x72\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x16\ \x01\x75\xd9\x07\ \x00\x63\ \x00\x68\x00\x65\x00\x63\x00\x6b\x00\x62\x00\x6f\x00\x78\x00\x2d\x00\x75\x00\x6e\x00\x63\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x65\ \x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x18\ \x08\xe8\x54\xc7\ \x00\x72\ \x00\x61\x00\x64\x00\x69\x00\x6f\x00\x2d\x00\x6d\x00\x69\x00\x78\x00\x65\x00\x64\x00\x2d\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\ \x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0d\ \x02\x41\x71\xc7\ \x00\x73\ \x00\x70\x00\x69\x00\x6e\x00\x2d\x00\x64\x00\x6f\x00\x77\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x13\ \x08\xc8\x89\x67\ \x00\x72\ \x00\x61\x00\x64\x00\x69\x00\x6f\x00\x2d\x00\x75\x00\x6e\x00\x63\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x65\x00\x64\x00\x2e\x00\x70\ \x00\x6e\x00\x67\ \x00\x16\ \x01\x06\xe8\x27\ \x00\x73\ \x00\x70\x00\x69\x00\x6e\x00\x2d\x00\x64\x00\x6f\x00\x77\x00\x6e\x00\x2d\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\ \x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0c\ \x06\xf0\x26\x67\ \x00\x75\ \x00\x70\x00\x2d\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x09\ \x00\x48\xad\x27\ \x00\x76\ \x00\x6c\x00\x69\x00\x6e\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0a\ \x05\x95\xde\x27\ \x00\x75\ \x00\x6e\x00\x64\x00\x6f\x00\x63\x00\x6b\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x11\ \x08\xc4\x6a\xa7\ \x00\x56\ \x00\x73\x00\x65\x00\x70\x00\x61\x00\x72\x00\x74\x00\x6f\x00\x6f\x00\x6c\x00\x62\x00\x61\x00\x72\x00\x2e\x00\x70\x00\x6e\x00\x67\ \ \x00\x0f\ \x0c\xe2\x68\x67\ \x00\x74\ \x00\x72\x00\x61\x00\x6e\x00\x73\x00\x70\x00\x61\x00\x72\x00\x65\x00\x6e\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0e\ \x04\xac\x3c\xa7\ \x00\x64\ \x00\x6f\x00\x77\x00\x6e\x00\x2d\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x1a\ \x02\xbf\x71\x27\ \x00\x72\ \x00\x61\x00\x64\x00\x69\x00\x6f\x00\x2d\x00\x63\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x65\x00\x64\x00\x2d\x00\x64\x00\x69\x00\x73\ \x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x14\ \x09\xd3\x75\x87\ \x00\x73\ \x00\x70\x00\x69\x00\x6e\x00\x2d\x00\x75\x00\x70\x00\x2d\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\x00\x2e\ \x00\x70\x00\x6e\x00\x67\ \x00\x11\ \x08\x8c\x6a\xa7\ \x00\x48\ \x00\x73\x00\x65\x00\x70\x00\x61\x00\x72\x00\x74\x00\x6f\x00\x6f\x00\x6c\x00\x62\x00\x61\x00\x72\x00\x2e\x00\x70\x00\x6e\x00\x67\ \ \x00\x11\ \x0b\x76\x30\xa7\ \x00\x62\ \x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x2d\x00\x63\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \ \x00\x17\ \x08\xab\x53\x67\ \x00\x64\ \x00\x6f\x00\x77\x00\x6e\x00\x2d\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2d\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\ \x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x10\ \x01\x00\xca\xa7\ \x00\x48\ \x00\x6d\x00\x6f\x00\x76\x00\x65\x00\x74\x00\x6f\x00\x6f\x00\x6c\x00\x62\x00\x61\x00\x72\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x14\ \x0d\x2c\xd1\xc7\ \x00\x63\ \x00\x68\x00\x65\x00\x63\x00\x6b\x00\x62\x00\x6f\x00\x78\x00\x2d\x00\x63\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x65\x00\x64\x00\x2e\ \x00\x70\x00\x6e\x00\x67\ \x00\x0f\ \x06\x53\x91\xa7\ \x00\x62\ \x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x2d\x00\x6f\x00\x70\x00\x65\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x1f\ \x04\x1e\x25\x07\ \x00\x63\ \x00\x68\x00\x65\x00\x63\x00\x6b\x00\x62\x00\x6f\x00\x78\x00\x2d\x00\x75\x00\x6e\x00\x63\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x65\ \x00\x64\x00\x2d\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0f\ \x08\xb2\xbe\xa7\ \x00\x72\ \x00\x61\x00\x64\x00\x69\x00\x6f\x00\x2d\x00\x6d\x00\x69\x00\x78\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x1a\ \x0b\x12\xe2\xa7\ \x00\x73\ \x00\x6c\x00\x69\x00\x64\x00\x65\x00\x72\x00\x2d\x00\x68\x00\x61\x00\x6e\x00\x64\x00\x6c\x00\x65\x00\x2d\x00\x64\x00\x69\x00\x73\ \x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x1c\ \x0e\x10\x4a\x47\ \x00\x72\ \x00\x61\x00\x64\x00\x69\x00\x6f\x00\x2d\x00\x75\x00\x6e\x00\x63\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x65\x00\x64\x00\x2d\x00\x64\ \x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x09\ \x06\x98\x83\x27\ \x00\x63\ \x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x12\ \x06\x1b\xa7\x07\ \x00\x63\ \x00\x68\x00\x65\x00\x63\x00\x6b\x00\x62\x00\x6f\x00\x78\x00\x2d\x00\x6d\x00\x69\x00\x78\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\ \x00\x67\ \x00\x11\ \x02\x6b\x21\x67\ \x00\x73\ \x00\x6c\x00\x69\x00\x64\x00\x65\x00\x72\x00\x2d\x00\x68\x00\x61\x00\x6e\x00\x64\x00\x6c\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\ \ \x00\x10\ \x01\x07\x4a\xa7\ \x00\x56\ \x00\x6d\x00\x6f\x00\x76\x00\x65\x00\x74\x00\x6f\x00\x6f\x00\x6c\x00\x62\x00\x61\x00\x72\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0e\ \x0d\x4e\x7b\x47\ \x00\x62\ \x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x2d\x00\x65\x00\x6e\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0c\ \x06\x41\x40\x87\ \x00\x73\ \x00\x69\x00\x7a\x00\x65\x00\x67\x00\x72\x00\x69\x00\x70\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0b\ \x04\x37\xb7\x67\ \x00\x73\ \x00\x70\x00\x69\x00\x6e\x00\x2d\x00\x75\x00\x70\x00\x2e\x00\x70\x00\x6e\x00\x67\ " qt_resource_struct = b"\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x23\x00\x00\x00\x02\ \x00\x00\x02\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x11\x5a\ \x00\x00\x03\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x1c\xf8\ \x00\x00\x01\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x5b\ \x00\x00\x05\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x32\x71\ \x00\x00\x01\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x08\x61\ \x00\x00\x01\x72\x00\x00\x00\x00\x00\x01\x00\x00\x0c\x05\ \x00\x00\x05\x34\x00\x00\x00\x00\x00\x01\x00\x00\x2f\x36\ \x00\x00\x02\xae\x00\x00\x00\x00\x00\x01\x00\x00\x16\x96\ \x00\x00\x04\x12\x00\x00\x00\x00\x00\x01\x00\x00\x21\x93\ \x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x05\xc2\x00\x00\x00\x00\x00\x01\x00\x00\x34\x98\ \x00\x00\x02\x8c\x00\x00\x00\x00\x00\x01\x00\x00\x15\x2c\ \x00\x00\x00\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x03\x29\ \x00\x00\x02\x26\x00\x00\x00\x00\x00\x01\x00\x00\x11\xda\ \x00\x00\x00\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x07\xd5\ \x00\x00\x05\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x2d\x83\ \x00\x00\x05\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x34\x13\ \x00\x00\x03\xee\x00\x00\x00\x00\x00\x01\x00\x00\x20\x57\ \x00\x00\x04\xf2\x00\x00\x00\x00\x00\x01\x00\x00\x2b\x0e\ \x00\x00\x01\xf0\x00\x00\x00\x00\x00\x01\x00\x00\x0f\xf7\ \x00\x00\x03\x16\x00\x00\x00\x00\x00\x01\x00\x00\x19\x7b\ \x00\x00\x03\x66\x00\x00\x00\x00\x00\x01\x00\x00\x1b\x8d\ \x00\x00\x04\x56\x00\x00\x00\x00\x00\x01\x00\x00\x22\xbe\ \x00\x00\x02\x40\x00\x00\x00\x00\x00\x01\x00\x00\x13\xa6\ \x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xa1\ \x00\x00\x01\x3c\x00\x00\x00\x00\x00\x01\x00\x00\x09\xe1\ \x00\x00\x02\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x18\xd6\ \x00\x00\x04\x7a\x00\x00\x00\x00\x00\x01\x00\x00\x25\xe7\ \x00\x00\x03\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x1a\x4d\ \x00\x00\x00\x7a\x00\x00\x00\x00\x00\x01\x00\x00\x05\x14\ \x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x06\x71\ \x00\x00\x02\x68\x00\x00\x00\x00\x00\x01\x00\x00\x14\x65\ \x00\x00\x03\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xdd\ \x00\x00\x05\x82\x00\x00\x00\x00\x00\x01\x00\x00\x33\x59\ \x00\x00\x04\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x29\x10\ " def qInitResources(): QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) def qCleanupResources(): QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) qInitResources() linux-show-player-0.5.1/lisp/ui/styles/dark/assetes.rcc000066400000000000000000000027461323636106000231250ustar00rootroot00000000000000 assets/branch-closed.png assets/branch-end.png assets/branch-more.png assets/branch-open.png assets/checkbox-checked-disabled.png assets/checkbox-checked.png assets/checkbox-mixed-disabled.png assets/checkbox-mixed.png assets/checkbox-unchecked-disabled.png assets/checkbox-unchecked.png assets/close.png assets/down-arrow-disabled.png assets/down-arrow.png assets/Hmovetoolbar.png assets/Hsepartoolbar.png assets/radio-checked-disabled.png assets/radio-checked.png assets/radio-mixed-disabled.png assets/radio-mixed.png assets/radio-unchecked-disabled.png assets/radio-unchecked.png assets/sizegrip.png assets/slider-handle-disabled.png assets/slider-handle.png assets/spin-down-disabled.png assets/spin-down.png assets/spin-up-disabled.png assets/spin-up.png assets/transparent.png assets/undock.png assets/up-arrow-disabled.png assets/up-arrow.png assets/vline.png assets/Vmovetoolbar.png assets/Vsepartoolbar.png linux-show-player-0.5.1/lisp/ui/styles/dark/assets/000077500000000000000000000000001323636106000222565ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/dark/assets/Hmovetoolbar.png000066400000000000000000000003411323636106000254230ustar00rootroot00000000000000PNG  IHDR 6ksRGBbKGD pHYs  tIME 3"zLMHaIDATHc@`bBc##c## 6U82g``hh3YZ:ڕ4ZKov<#p9hhfC'=r70@5|IIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/Hsepartoolbar.png000066400000000000000000000003161323636106000255710ustar00rootroot00000000000000PNG  IHDR?,{sRGBbKGD pHYs  tIME /?RNIDAT8c`6ƨos7^0000009*IH8B'i2 1 ^ra@M F* IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/Vmovetoolbar.png000066400000000000000000000003441323636106000254440ustar00rootroot00000000000000PNG  IHDR6  sRGBbKGDަ pHYs  tIME *+\dIDATHc0< 4Oͪs]9H+5yȈ$|O-5sl&}MTR M&ZjFhRIzLKl4cz0J q-nIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/Vsepartoolbar.png000066400000000000000000000002731323636106000256110ustar00rootroot00000000000000PNG  IHDR?vsRGBbKGD pHYs  tIME 5+URj;IDAT8c`#0}*%v;s_9Si4 M! xc~Tid kXIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/branch-closed.png000066400000000000000000000004741323636106000254750ustar00rootroot00000000000000PNG  IHDR _bKGD pHYs##rAwtIME 1 |IDATmJ@F na}hg2V A.$;6[؍M΅S}?p? !RJf׶m7dN'"fBgf+`cAD03R*!Ƹpu]Dd~fƙo]斿3(D$'sOz(90 o}ߟxVŜ܋}u {ܒ\:Rm[IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/branch-end.png000066400000000000000000000002661323636106000247710ustar00rootroot00000000000000PNG  IHDRQ::!PLTEcVMcVMcVMcVMcVMcVMcVMcVMcVMcVM tRNS|Y:3ex/\?xU9IDATHc` āQ*GU$0v@2mi1(` YTIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/branch-more.png000066400000000000000000000002101323636106000251520ustar00rootroot00000000000000PNG  IHDR5VqPLTEcVMcVMcVMcVMcVMcVM_[tRNSJF36RSNIDAT[cdF`ddNqz~IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/branch-open.png000066400000000000000000000004701323636106000251610ustar00rootroot00000000000000PNG  IHDR bKGD pHYs##rAwtIME /\IDAT]AJAD9gp)fzQq4l /l{@u633_7(3%@^jظILw}5Ifd^$~B9@O҃#pB8ahfff1w_*s^מ@J`f؛cJP{mL3jqUIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/checkbox-checked-disabled.png000066400000000000000000000007471323636106000277130ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<dIDAT8/DA?I^FEԮD4ze^G4F't~']=<ǽfg>ߙ1j5+Ek)Zk78+@ . IOZLa:ƘU}VuP%g3oapANyE-@.y_ ] X U=~ψ{fPkgZayxja>'Fuo{~uß>t xW/b#ڟUR@ltLjY;γLiAa$~#4ͻ ,I/{$ 0"IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/checkbox-checked.png000066400000000000000000000011661323636106000261420ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8kQ6ֈV B ڂJz. zſ@PfUW/Tr(iEhۤthq`.y3bͪ7=1ƐH$"2i`PfxkHJȤ^DȎX~nI$Ĩ``TkK,Bk-+m\JiKnWJ):uR}̖Ԧ^D*?4@_F4 sS^9&^pbqNb`) kW7T6~RAZ n<$uTu񍢼7aHP [ i@DJ4ӏ ΝSu'0RpfB+3{Y2&1&{OL8IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/checkbox-mixed.png000066400000000000000000000006571323636106000256660ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<,IDAT8MNAjzDI¥p1ށ` [W\9{0F6V7^{-t:Y \8[ "5VDW1x1J8 Ck#EM+ a*p. PYk\bN|k./6La\. W%~WwmBeI| *]`wP!gY  MWb1M^OL S(H-$xހ'bM%IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/checkbox-unchecked-disabled.png000066400000000000000000000004471323636106000302530ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT81@g7&ƂR.b>ƹ6z(B=:Xm6|K0o= @?o+}78VIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/close.png000066400000000000000000000011611323636106000240700ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs+tIME (tm$IIDATXWAN1np![;ހ6p,;ع!# MIia@y}ysJWRyrNipΛQ Sإ_`]pOSR/sK_[Ry 28!T`^w^>c- :aQJkR)ZJo8D$q4}TJ]yEjT"c:UUE]݊lp=^X|UYh IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/down-arrow.png000066400000000000000000000005461323636106000250700ustar00rootroot00000000000000PNG  IHDR "abKGD pHYs B(xtIME ` IDAT(͒1KPKR 6b@ʤB~%EH)lF;V*[WHnaY%"RX{88msAeUw"-Zϣ(2ƘGemiz3 ,8<χwqߎ>4 C'`1=z4ͽ Nڶ}sI$YE><JWvULeyiW[Zڿk^U,IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/radio-checked-disabled.png000066400000000000000000000010741323636106000272150ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8S=kP==M˒@#mv.c0tTiJ =:ݏw) uDQtA4ƴ('kA&MoxV H1杤-s-(fYVI$dE$5_$yoq91Mϒdy$ɣyKIM0 /$Ƨ:9 ÷Ju7;+9NI. ɾm[CCIA\+%7ιK$&S rS]MU|j ιgcLۼ6)?IfI:][k[EQ<k@r^Փ kHڐqܭ<ɻ|P8IPd2ySM#b7eٚ1Qsq|De_Tǿ!-e|IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/radio-checked.png000066400000000000000000000014451323636106000254520ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8jSa3ЦMRQbkLłK%tx۸ӊ>7p#D b V+iOno\4s`~3!U3P KD|]KU7k"-[=aHJ>=N!*7o:ƂGuc Znp!cDQ?~{Ц|~4[37xI "[oI~ze1氵c@ ngпmohkS8.9b3F|!ӿ{]> cWѻq"c'\.gZ=(REw;+e1kDfFfґj;T`6\'fYL&X ^U6Ze7Z) W33t: l,=|"—Y@qj _߬\+t:T*^/"pSFfFKKOHUk5I`iYk/I4QaWD_?'@$+Ea~ /q?'?A9LDr<T$;"<|Y(km\)EQ|^W'c9~,$wy_.˲9tȿGY6:QIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/radio-mixed.png000066400000000000000000000014451323636106000251720ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8jSa3ЦMRQbkLłK%tx۸ӊ>7p#D b V+iOno\4s`~3!U3P KD|]KU7k"-[=aHJ>=N!*7o:ƂGuc Znp!cDQ?~{Ц|~4[37xI "[oI~ze1氵c@ ngпmohkS8.9b3F|!ӿ{]> cWѻq"c'\.gZ=(REw;+e1kDfFfґj;T`6\'fYL&X ^U6Ze7Z) W33t: l,=|"—Y@qj _߬\+t:T*^/"pSFfFKKO/RqC y\.Yyvv1l P nr9a 6pAz \ar9j4T-,K ,jXX+j^LǝX 1"0BUqtJz~PՒVn67mn}~e so^qZ~cZ60 pxx8~QN0"[㥥_ƎN?}8;Ix@@7ق'IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/sizegrip.png000066400000000000000000000002011323636106000246110ustar00rootroot00000000000000PNG  IHDR%=m"PLTEwk-tRNS@f)IDATx^ X pm(GhUqo%5 IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/slider-handle-disabled.png000066400000000000000000000014451323636106000272500ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8}1HcYssye V9 "S.X-)naJ,4K-E\)d%hg" vO1|Im4awosιk-_@mllTvwwC qMDdkkkzvv\./(J,;9??x"=X57jo|ߗ,J`P(l6ߋ/Z3@Ddgg ~_^^ CDQ899oZ?lnna ^uiivq̘;Z ÐEorrz@jՅo("c>Yi18&rV[Q}uRqnk-IT*8iӀ$b3j)MSAk )^ɲ qLWJ$ ^aoooEp]Rc p}}a*t:OR c 뢵FD "s|||t>F[k|j󅻻;o5R.9::/..>ڧMLb8uuZA)R "AnG yWfffr:55.//Mn W Jsssi@2@Q8bXjIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/slider-handle.png000066400000000000000000000014671323636106000255070ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8}K#g?0j8TD dCa^ē[JnςقBDA6 a8 3Kl<J)B 777@>!/~2M@$Iz)x2\X__5??zooou]4M 2'NNN~w]{!`BhZmkmm]GR$IJ,1 cTu4M4 }Ւmcxrrqfffp)%m =h@nSSS _.i066xyGvhJ)Eчz|PTmLw$h͌2IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/spin-down-disabled.png000066400000000000000000000002301323636106000264420ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME  -&1%IDAT8c`0 ǧȑ#(zFp000A ;IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/spin-down.png000066400000000000000000000002301323636106000246750ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME  4B%IDAT8c`0 ۷?> NNN(zFp000eIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/spin-up-disabled.png000066400000000000000000000002411323636106000261210ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME  .IDAT8c`濍|j(d `D319H5° ܜQDIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/spin-up.png000066400000000000000000000002421323636106000243550ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIME  "&,I/IDAT8c``߾}&J-xL&'''F`4yF,IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/transparent.png000066400000000000000000000003031323636106000253210ustar00rootroot00000000000000PNG  IHDR@@iqbKGD pHYs  tIME  .7DiTXtCommentCreated with GIMPd.e'IDATx  Om7w@@zIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/undock.png000066400000000000000000000007101323636106000242450ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs+tIME $O HIDATXWA0 !>?# ?UK*E$.cOcW|Tkm: #r~+EVW/" (o8.9Sp~deYE?p&0*G{xuuBvsF[f=ZK45 s)WhCDT `# SL90Dd@1`DQFyt8xc`4"xN2G,%ظ Jaq&XqF*:vDz\P̡#TǞѹ%"ڿbÄbZJNDզ"IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/up-arrow-disabled.png000066400000000000000000000005401323636106000263040ustar00rootroot00000000000000PNG  IHDR "abKGD pHYs B(xtIME LkIDAT(ՒJPEτlSlv"X`aa#?aH"HM4b&<"g យ2baY쩃GYQA\oi<ϯڶKtvu8WZ뷲,:dkIkR>tʲVQ(l`1`pyC1Vu]?k?q X 9u]w$ɫcdQ~x78 dQjtIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/up-arrow.png000066400000000000000000000005371323636106000245450ustar00rootroot00000000000000PNG  IHDR "abKGD pHYs B(xtIME RFIDAT(ՒJPEτ,dM"X Ft) ) 6U)^) dzBX]ppֲvހ0+p\UE+ Rgym{er3-ܮi^}ly)C@6&ĺFk9Ki$I.. xpCNǷJ|CfȑUU=V凈7} |yV?N^IENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/assets/vline.png000066400000000000000000000001741323636106000241030ustar00rootroot00000000000000PNG  IHDRQ:]< PLTEcVMcVMcVMէ$-tRNS|:3tHIDAT8Oc`,`TtTtTtTt8wUlIENDB`linux-show-player-0.5.1/lisp/ui/styles/dark/style.py000066400000000000000000000022151323636106000224660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtGui import QColor from PyQt5.QtGui import QPalette from PyQt5.QtWidgets import qApp # Import resources # noinspection PyUnresolvedReferences from lisp.ui.styles.dark import assetes # Change link color palette = qApp.palette() palette.setColor(QPalette.Link, QColor(65, 155, 230)) palette.setColor(QPalette.LinkVisited, QColor(43, 103, 153)) qApp.setPalette(palette) linux-show-player-0.5.1/lisp/ui/styles/dark/style.qss000066400000000000000000000362301323636106000226500ustar00rootroot00000000000000/* * Copyright 2012-2014 Ceruti Francesco & contributors * * This file is part of LiSP (Linux Show Player). * Based on ColinDuquesnoy works at: https://github.com/ColinDuquesnoy/QDarkStyleSheet/ * The MIT License (MIT) * * Copyright (c) <2013-2014> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ QWidget { color: white; background-color: #333; selection-background-color: #419BE6; selection-color: black; outline: none; } QWidget:disabled { color: #707070; background-color: #333; } QTreeView:branch:selected, QTreeView:branch:selected:hover, QWidget:item:selected, QWidget:item:selected:hover { background-color: #419BE6; } QProgressBar { border-radius: 2px; } QProgressBar:horizontal { text-align: center; border: 1px solid #3A3A3A; background: #202020; } QProgressBar:chunk:horizontal { border-radius: 2px; background-color: rgba(40, 90, 150, 255); } QStatusBar { border-top: 1px solid #3A3A3A; } QToolTip { border: 1px solid transparent; border-radius: 5px; background-color: #1A1A1A; color: white; padding: 2px; opacity: 200; } QMenuBar:item { background: transparent; } QMenuBar:item:selected { background: transparent; } QMenuBar:item:pressed { border: 1px solid #3A3A3A; background-color: #419BE6; color: black; margin-bottom:-1px; padding-bottom:1px; } QMenu { border: 1px solid #3A3A3A; color: white; } QMenu:item { padding: 2px 20px 2px 20px; } QMenu:item:selected{ color: black; } QAbstractItemView { alternate-background-color: #252525; color: white; border-radius: 3px; padding: 1px; } QTreeView::branch:hover, QAbstractItemView:item:hover { background-color: none; } QTabWidget:focus, QCheckBox:focus, QRadioButton:focus { border: none; } QLineEdit { background-color: #202020; padding: 2px; border: 1px solid #3A3A3A; border-radius: 3px; color: white; } QGroupBox { border:1px solid #363636; border-radius: 2px; margin-top: 2ex; /* NOT px */ } QGroupBox:title { subcontrol-origin: margin; subcontrol-position: top center; padding-left: 10px; padding-right: 10px; } QAbstractScrollArea { color: white; background-color: #202020; border: 1px solid #3A3A3A; border-radius: 3px; } QScrollBar:add-line, QScrollBar:sub-line, QScrollBar:right-arrow, QScrollBar:left-arrow { width: 0px; height: 0px; } QScrollBar:add-page, QScrollBar:sub-page { background: none; } QScrollBar:horizontal { height: 12px; margin: 0px; border: 1px solid #3A3A3A; border-radius: 6px; background-color: QLinearGradient( x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #333, stop: 1 #484846); } QScrollBar:handle:horizontal { background-color: QLinearGradient( x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #605F5F, stop: 1 #787876); min-width: 5px; border-radius: 5px; } QScrollBar:vertical { background-color: QLinearGradient( x1: 1, y1: 0, x2: 0, y2: 0, stop: 0 #333, stop: 1 #484846); width: 12px; margin: 0px; border: 1px solid #3A3A3A; border-radius: 6px; } QScrollBar:handle:vertical { background-color: QLinearGradient( x1: 1, y1: 0, x2: 0, y2: 0, stop: 0 #605F5F, stop: 1 #787876); min-height: 5px; border-radius: 5px; } QTextEdit[readOnly="true"] { background-color: #333; border: none; } QHeaderView { border: none; text-align: center; qproperty-defaultAlignment: AlignCenter; } QHeaderView:up-arrow { image: url(:/assets/up-arrow.png); width: 24px; height: 24px; } QHeaderView:down-arrow { image: url(:/assets/down-arrow.png); width: 24px; height: 24px; } QHeaderView:section { border: none; border-right: 1px solid #3A3A3A; border-bottom: 1px solid #3A3A3A; } QHeaderView:section, QTableView QTableCornerButton:section { background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #565656, stop: 0.1 #525252, stop: 0.5 #4e4e4e, stop: 0.9 #4a4a4a, stop: 1 #464646); color: white; } QHeaderView:section:checked { font-weight: bold; } QSizeGrip { image: url(:/assets/sizegrip.png); width: 12px; height: 12px; } QMainWindow:separator { background-color: #333; color: white; padding-left: 4px; spacing: 2px; border: 1px dashed #3A3A3A; } QMainWindow:separator:hover { background-color: QLinearGradient(x1:0, y1:0, x2:0, y2:1, stop:0 #58677b, stop:0.5 #419BE6 stop:1 #58677b); color: white; padding-left: 4px; border: 1px solid #3A3A3A; spacing: 2px; } QMenu:separator { height: 1px; background-color: #3A3A3A; color: white; padding-left: 4px; margin-left: 10px; margin-right: 5px; } QStackedWidget { border: none; } QToolBar { border: 1px solid #393838; background: 1px solid #333; font-weight: bold; } QToolBar:handle:horizontal { image: url(:/assets/Hmovetoolbar.png); } QToolBar:handle:vertical { image: url(:/assets/Vmovetoolbar.png); } QToolBar:separator:horizontal { image: url(:/assets/Hsepartoolbar.png); } QToolBar:separator:vertical { image: url(:/assets/Vsepartoolbars.png); } QPushButton { color: white; background-color: QLinearGradient( x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #333, stop: 1 #444); border: 1px solid #262626; border-radius: 4px; padding: 3px; padding-left: 5px; padding-right: 5px; } QPushButton:focus { background: #4B5157; } QPushButton:pressed { background: #35393d; } QComboBox:focus, QPushButton:focus { border: 1px solid #419BE6; } QComboBox { selection-background-color: #419BE6; background-color: #202020; border-style: solid; border: 1px solid #3A3A3A; border-radius: 3px; padding: 2px; } QComboBox:on { background-color: #626873; padding-top: 3px; padding-left: 4px; selection-background-color: #4a4a4a; } QComboBox:drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 15px; border-left-width: 0px; border-left-color: darkgray; border-left-style: solid; border-top-right-radius: 3px; border-bottom-right-radius: 3px; } QComboBox:down-arrow { image: url(:/assets/down-arrow.png); } QAbstractSpinBox { padding-top: 2px; padding-bottom: 2px; padding-right: 25px; border: 1px solid #3A3A3A; background-color: #202020; border-radius: 3px; color: white; } QAbstractSpinBox:up-button { background-color: transparent; border-left: 1px solid #3A3A3A; padding: 6px; right: 24px; width: 12px; height: 15px; subcontrol-origin: border; subcontrol-position: center right; } QAbstractSpinBox:down-button { background-color: transparent; border-left: 1px solid #3A3A3A; padding: 6px; width: 12px; height: 15px; subcontrol-origin: border; subcontrol-position: center right; } QAbstractSpinBox:up-arrow,QAbstractSpinBox:up-arrow:disabled,QAbstractSpinBox:up-arrow:off { image: url(:/assets/spin-up-disabled.png); } QAbstractSpinBox:down-arrow,QAbstractSpinBox:down-arrow:disabled,QAbstractSpinBox:down-arrow:off { image: url(:/assets/spin-down-disabled.png); } QAbstractSpinBox:up-arrow:hover { image: url(:/assets/spin-up.png); } QAbstractSpinBox:down-arrow:hover { image: url(:/assets/spin-down.png); } QLabel { border: 0px solid black; } QTabBar::tab { color: white; border: 1px solid #444; border-bottom-style: none; background-color: #333; padding-left: 10px; padding-right: 10px; padding-top: 3px; padding-bottom: 2px; margin-right: -1px; } QTabWidget::pane { border: 1px solid #444; top: -1px; } QTabBar::tab:only-one, QTabBar::tab:last { margin-right: 0px; border-top-right-radius: 3px; } QTabBar::tab:first:!selected { margin-left: 0px; border-top-left-radius: 3px; } QTabBar::tab:!selected { border-bottom-style: solid; margin-top: 3px; background-color: QLinearGradient(x1:0, y1:0, x2:0, y2:1, stop:1 #212121, stop:.4 #343434); } QTabBar::tab:selected { border-top-left-radius: 3px; border-top-right-radius: 3px; } QDockWidget { color: white; titlebar-close-icon: url(:/assets/close.png); titlebar-normal-icon: url(:/assets/undock.png); } QDockWidget:title { border: 1px solid #3A3A3A; border-bottom: #333; text-align: left; spacing: 2px; background-color: QLinearGradient(x1:0, y1:0, x2:0, y2:1, stop:1 #333, stop:0 #3A3A3A);; background-image: none; padding-left: 10px; } QDockWidget { border: 1px solid lightgray; titlebar-close-icon: url(:/assets/close.png); titlebar-normal-icon: url(:/assets/undock.png); } QDockWidget:close-button, QDockWidget:float-button { border: 1px solid transparent; border-radius: 5px; background: transparent; icon-size: 10px; } QDockWidget:close-button:hover, QDockWidget:float-button:hover { background: #3A3A3A; } QDockWidget:close-button:pressed, QDockWidget:float-button:pressed { padding: 1px -1px -1px 1px; } QTreeView, QListView, QTableView { border: 1px solid #3A3A3A; background-color: #202020; } QTreeView::branch:has-siblings:adjoins-item, QTreeView::branch:has-siblings:!adjoins-item, QTreeView::branch:!has-children:!has-siblings:adjoins-item { border-image: none; } QTreeView::branch:has-children:!has-siblings:closed, QTreeView::branch:closed:has-children:has-siblings { border-image: none; image: url(:/assets/branch-closed.png); } QTreeView::branch:open:has-children:!has-siblings, QTreeView::branch:open:has-children:has-siblings { border-image: none; image: url(:/assets/branch-open.png); } QSlider { background: none; } QSlider:disabled { background: none; } QSlider:groove { border: 1px solid #3A3A3A; border-radius: 1px; background: #202020; } QSlider:groove:disabled { background: #2D2D2D; } QSlider:groove:horizontal { height: 3px; margin: 0px 3px; } QSlider:groove:vertical { width: 3px; margin: 3px 0px; } QSlider:add-page:vertical:disabled, QSlider:sub-page:horizontal:disabled { background-color: white; } QSlider:sub-page:horizontal { height: 2px; border-radius: 1px; border: none; background-color: #80AAD5; } QSlider:add-page:vertical { width: 2px; border-radius: 1px; border: none; background-color: #80AAD5; } QSlider:handle { background-color: #666; border: 1px solid black; border-radius: 2px; } QSlider:handle:horizontal { margin: -6px -4px; width: 8px; height: 8px; } QSlider:handle:vertical { margin: -4px -6px; width: 8px; height: 8px; } QSlider:handle:disabled { background-color: #CCC; } QToolButton { background-color: #3A3A3A; } QToolButton:pressed { background-color: #3A3A3A; } QToolButton:hover { background-color: #3A3A3A; } QCheckBox, QRadioButton { background-color: transparent; } QCheckBox:indicator, QGroupBox:indicator, QMenu:indicator, QTreeWidget:indicator { image: url(':/assets/checkbox-unchecked.png'); } QCheckBox:indicator:disabled, QGroupBox:indicator:disabled, QMenu:indicator:disabled, QTreeWidget:indicator:disabled { image: url(':/assets/checkbox-unchecked-disabled.png'); } QCheckBox:indicator:checked, QGroupBox:indicator:checked, QMenu:indicator:checked, QTreeWidget:indicator:checked { image: url(':/assets/checkbox-checked.png'); } QCheckBox:indicator:checked:disabled, QGroupBox:indicator:checked:disabled, QMenu:indicator:checked:disabled, QTreeWidget:indicator:checked:disabled { image: url(':/assets/checkbox-checked-disabled.png'); } QCheckBox:indicator:indeterminate, QTreeWidget:indicator:indeterminate { image: url(':/assets/checkbox-mixed.png'); } QCheckBox:indicator:indeterminate:disabled, QTreeWidget:indicator:indeterminate:disabled { image: url(':/assets/checkbox-mixed-disabled.png'); } QRadioButton:indicator { image: url(':/assets/radio-unchecked.png'); } QRadioButton:indicator:disabled { image: url(':/assets/radio-unchecked-disabled.png'); } QRadioButton:indicator:checked { image: url(':/assets/radio-checked.png'); } QRadioButton:indicator:checked:disabled { image: url(':/assets/radio-checked-disabled.png'); } /* ****************** * CUSTOM WIDGETS * ****************** */ #CartTabBar::tab { height: 35px; width: 100px; border: 1px solid #444; border-bottom-style: none; background-color: #333; padding-left: 10px; padding-right: 10px; padding-top: 3px; padding-bottom: 2px; margin-right: -1px; font-size: 13pt; font-weight: bold; color: white; } #CartTabBar::tab:only-one, #CartTabBar::tab:last { margin-right: 0px; border-top-right-radius: 3px; } #CartTabBar::tab:first:!selected { border-top-left-radius: 3px; } #CartTabBar::tab:!selected { margin-top: 3px; border-bottom-style: solid; background-color: QLinearGradient(x1:0, y1:0, x2:0, y2:1, stop:1 #212121, stop:.4 #343434); } #CartTabBar::tab:selected { height: 40px; margin-bottom: -5px; border-top-left-radius: 3px; border-top-right-radius: 3px; } #ButtonCueWidget { background-color: #464646; color: #AAAAAA; border: 1 solid black; border-radius: 6px; /*font-size: 11pt;*/ } #ListTimeWidget { height: 25px; border-radius: 0px; margin: 1px; background-color: transparent; } #ListTimeWidget:horizontal { text-align: center; border: none; background: transparent; } #ListTimeWidget:chunk:horizontal { border-radius: 0px; background-color: transparent; } /* running */ #ListTimeWidget:horizontal[state="running"] { border: 1px solid #00FF00; } #ListTimeWidget:chunk:horizontal[state="running"] { background-color: #00A222; } /* pause */ #ListTimeWidget:horizontal[state="pause"] { border: 1px solid #FFAA00; } #ListTimeWidget:chunk:horizontal[state="pause"] { background-color: #FF8800; } /* error */ #ListTimeWidget:horizontal[state="error"] { border: 1px solid #FF0000; } #ListTimeWidget:chunk:horizontal[state="error"] { background-color: #CC0000; } #InfoPanelDescription[readOnly="true"] { border: 1px solid #3A3A3A; } #VolumeSlider:sub-page:horizontal { border: none; background-color: none; } #VolumeSlider:add-page:vertical { border: none; background-color: none; }linux-show-player-0.5.1/lisp/ui/styles/icons/000077500000000000000000000000001323636106000211465ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/LICENSE000066400000000000000000000011651323636106000221560ustar00rootroot00000000000000* Fade functions icons from Qtractor - http://qtractor.sourceforge.net/qtractor-index.html * icons/lisp/16/fadein-linear.png * icons/lisp/16/fadein-quadratic.png * icons/lisp/16/fadein-quadratic2.png * icons/lisp/16/fadeout-linear.png * icons/lisp/16/fadeout-quadratic.png * icons/lisp/16/fadeout-quadratic2.png * Numix Project icons - https://numixproject.org - https://github.com/numixproject * /icons/numix/ * Based on Numix Project icons: * /icons/lisp/scalable/auto-follow.svg * /icons/lisp/scalable/auto-next.svg * All the not previously specified icons are part of Linux Show Player linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/000077500000000000000000000000001323636106000221155ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/16/000077500000000000000000000000001323636106000223435ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/16/fadein-linear.png000066400000000000000000000004451323636106000255520ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME cqIDAT8Փ= 0HrWqh73J !s;u]$ ]L&ϓ䃂N#I p^988id$yIaY6P($u]u]50)y۶qI]Jֽ05%϶gJj9!s#3m]ۥsE+Zxg7&2E]ƞIENDB`linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/16/fadein-quadratic.png000066400000000000000000000007711323636106000262570ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME'!XT.FIDAT8˕kZq?C?.$cCfKR1 W(:e%_ޓrι˅X/%N}F ȖCM+T*~1aH&AR#IR< $$nS7M$I:֞rxqS,K j'l<"YqG8s|ލ2d2xjQ%|\CRt_}MI$\/ỞC v-7p8]c.?[Bϼ$IF~o[ƘmkXd_ռ_Q+.eIENDB`linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/16/fadein-quadratic2.png000066400000000000000000000010531323636106000263330ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME!PrIDAT8ˍPib@$JC1n (0]n8lgmpp)]ˆ4L8wq?8sns#r$$ݓ\R\.Apn?j}Dz,$ݑBRx.xX~h4[V_\e^3Lq?߁?G- IENDB`linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/16/fadeout-linear.png000066400000000000000000000004321323636106000257470ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME#2^)IDAT8͓1 @D%{\-=EEaklKc" @#"Pd0   pFY!|_!"'eyMG/lNto@<+`_t:5q r2Jvy$Z쌼tOo$N$ɔ&P^ C+ .|>oqT%Q,3I_z=Xa &d2V@Bl6'I|i4[J2A7\-HUv7W (nA>oT*%?g rVZvnGb\.Gr |0|N`@RD,pO&S ϓ3IENDB`linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/16/fadeout-quadratic2.png000066400000000000000000000010641323636106000265360ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  tIME(%IDAT8ˍAkA6e14'^ -RoCz+/P$,(h1"փ =crBoT\E@׋l7n;~ofx/"cN$"KL&sh4j&Ow?.8pJZMu# )$Ar@ Tz,t' ȣeYbQ!hyP, @m-)n~2x`0oiaxTE\7c۶$-0LS$#<̪ze\.<< G JX,v]VmW$/HdRiv""hTZU&QIENDB`linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/512/000077500000000000000000000000001323636106000224245ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/512/linux-show-player.png000066400000000000000000003154241323636106000265520ustar00rootroot00000000000000PNG  IHDRxbKGD pHYs  tIME &,iTXtCommentCreated with GIMPd.e IDATxkmYVιjniav"! -@Vpz@daaE,H6K#Gy 9 N=ozP][U}֭ǽ9ccZk}=u9g?^{9č7ρ~g_C|5^"&w9894 ipApG<^xù?9 ?}߁Oso @Vft {g -|Kh[mKh=m]usw~}fߗSwNIׇWܘyV7u4{ǣqޏgaP#nρ{_kԞw}{[߯{}KhIKhOvo%wg:n3ӛn2W 3^$,+Wu~ٟWLu~ ~w=s >roz',3tk#7s~jA|0 N76b^;- MS"~_iX>9n9muNQPoo{3pC8?" - @:֯{78g{$&ru?"/zϿsV}3[<@M&F5 `X@?9cT22@L'邁d'@D DH@K?d;qABvx$O@@}0`x}ܫ<xN_* |<873bڬ|<pQpNVN;fbƂMۥl~=w@ԧHoѯ` }=،C@6uv;F_G\%!3j "PCJz.;\}q[e9Pv@̀@:|AL$F^1odV=b3vBۡub:tyۺyZ_<(u~@FIFdf"BX)lE!=f}~UD*: ut` ʼxW߬ti~T/֢^HN+z 9w`.{|jko+<Hhv?"t vn,mMz%"ABT?'4]pcӐuNߑ(l1" M= _T?B:TxZrwk8 7xY/jU|tt;V1? } bozv8*o9'w^?cvv&GL!C71 7IH2`e>4 7P ضC쌟E=8]2ٽܭ8tD>h(" 0"o^&MĆo\ /cףqm$AJ8a ؐd:4Va Vi&aQz}\jw1'p^[3E {֡?)*2- 4>@CCd ie)c>Z>:::?O~d& MP|clSFIu6`2[L-u^*g%8 jso#8-X|@D#c.\sG+ _V9n ?ߞY)ocGh3),#w"nlDXm7"=xOj珎)*2! q柆hA,o8v/>`@#~`ɩ>^V pttw m"=V|ILޓU \vA0Y' ~| w@,hx|^ * wv<Ϲ2¤BAMɷ3ߧ ?I9zDQ|@!{z֝5Ԝ1?/9#Bߝ#&!l7X˖vYH# ,e ;:I$zwEH`H)$Q3Hszy!Cpl/~ߜBu^So/q7 :m it3:68m% [M~^Nϟ=RYkn/1?Եkמ9G?g/^AA").`MB]Q! , =|`t8~ePzns8?! TE! ˞Gqojwճ0΀ ?9ECX.>N`,= ihA=Jv(cN@u[b1HMb! '#[v1O )T4@n6W8;%<ňL5<6><^oBBc  _ꛎY;``WcMSS8%. [ H;%Afq4N"mLt9)]=jn$-| /xw!_JMg947Sh4{\h:#;8+zX'kum[.Bqj쎉緥p؈Ia0fЦ_W#h ̉l&N&]l]*;A1 PaH2y~x/%F',7^߈%t-XEk z66u+yz.8d}B(8y`[{)ry$Ek_[dJD:$XnmpưW mK=]K^:)io'KQ +qA~)ӓ3 SŲ d q6.J:;Gh:is25ҿ΅ 7 AtZoDZ*8E4ԥeIfC m?ZC>~,9+ޔ4 ߌ6-rܼtEgk lnPu$M&l1Ip,K& +P~#!p-j$c=+=?Pd9!,Q#po۝/;*_˸ zRJrSn`3wjfldhe\vl'h5@jࠟ @ ; ge Tc7?GwP>؋M[[7|OC NG.=xh/^._tz.AFį/n pZ}]u Wnop[8kP?Oֿ7$Bt]OrAbBrff863l6}-w߁OF.}BQ;JP=&~.F FIj}0%C3z@p)û} ~}4?|#G{ЉGsmMNO,k=W{ w}sWꇎVڳ^}%\}xr"wHcb^z*0>E1c*@&c)Pn)x 6I: HsS8ҒcD@ 7L?8{ ƤjPq4=r8" *P<#3Tω?GcB0r%"I^~ A={{A/ޭkjoj'۾9se\~9\}e\.dU)[0rfnXGjP,0p.aʱO) `"VC*FK/*Q޸ϢKJ%NtJ wGk) DCZg!z;r 2|CL=¤7zvdk%.h8H . tEr՚:<&{'=v`=ԟ.\\ɃjsGGG<hNSWl_ƅkpϣҋ/›o!B5M/ۆdGƂu{G$t$$j*e{ gb,`$$TҠXy`)=.YB&%Qi0 kX@#'Ra lC"߉V2_+H\"A2%o/;]zPyknߠY(5ɯi}GMt/PG'T6W!Q.R2揂 W$gAHblGYﱁH+{1NXdk{@so zEt?ڵkO->g>!6N,ށw? s,(>˥05S (@ Nw^OerT뷈Rdg,,.pYtݘ^.|q"2H&-L 'ff;Gd@ܨBS&p. @pK ɣ tH4(~r^8> ^8!D %P@_Gs8x)+WT>ÿuڵgl9yEEhNN$<) X7AA5@X};VL QΔLIJƬCòu[g/.zKFe@:F7J-)#x>0$90qt>W^27aυA{'l.!'Ꝿ 䨝Y8fRY=WbCTU0 =*@*oƦV]ZpH3?VnA"+)1^ eQ4J8* |1c7?+,|S8xAʳt"?=nx[> $UnQZ'=m4#2-&j);%j)ioGƄMAQC>380ϫ-mK L٨9ql9vZ$} }˞TG} P.h6J[%`9f$$Z#"uGԔ?MJmIx[JmnGŕO~>Agn.=9yeqrzaE7\i\xq} >[%vҾ]'qw~'l~꾀GY5% 5?gmlيlD;2d(mΪ`t !Q4l'"=kVV"IL^e&'N)q( emwm"3 r9k<sLq"qN$pVC@z"Te | V…?h[ /J`>t$yA39z`+^C}cC_?;C5RbX5m2ݸç¿] Rz IDAT~_v_u@7y[8y<p|\ pT ՛/yZLxUFw-W[VZ=sRNuH1m8;(HuK0mZӜȿ[CмO]9bo3f6(K-]?oDມ0`p!0@uR$P-} U ߊ ,3IaI(UTF;N9S}m 7 #5;\X۵aN$cȂ=ǜ3.l^p}ӺJK.Qd?7)i jb;p!70HJ "J$am2;G^2jX .gۑ{kUw˗FpO-{Ok \=eK2̥JBרXڴkw2Qk -as"}HV G$8 g:`l&rJ% W10>&)2?,6%ԍK':("[da?n!OV`ukCIp\||x?)4O=5԰L-msy8on>)){nZ ٽxQZk5P/hh A'x_.Ǒ1Uoĸ%"S fk bhOrnc0,?*ٌ At rM1fģNؖZ?~k}SO^{m_wuj|bo&n(Z$ۭMcg2TB_r O U<6|f:&AX}oIςԥ˚1 MQ`F9k aNKUn95g+):%D&I^+j񿰐!=c #Vy!x,iTP;aZj08) 7p7|Vp.]˟<ͨѡLzᆴ"0؈ArNAsH t dTRKD@HMo p/܂Mq I8}[\c$@ɮ}"HM K'Q=G2$CfbRq.]jMXμfMAFf"iq[.#qDžk%۝;oaW}#_V)$+/V~){Ky-.0҇w\2zaN>xZ UzVJoK)W72HzRXt]sEk 2(Q #߽EeH5Ayn?p^B[e3ɇ'nikpGT.wvj38e:>ƻ>\znYpsڥ6-=>(= #@K5kCx(!GRpۘ(KyI`_'qS=Y*aJ 1އ(Ӑ^oJ@ߌA1B)9ҿ 5"I_h/l 3#;}t pW-9B.ɈN 67c+8M?*Krdp oUta.],q-6蝨iYږE)Vlܑ\0%]5SRX1"65/$@v(-g-*g&3 ۔tuGBvaWAFr|g5@8| )҆Ag[x.[o#9 ࣝ}Z7NܺFHrDf5a졮Yo[QStn`Ysf߆!ƢY)ժ#(/ 4.$ 90#$c Bi4pMv6 !&,.=@%C~1i]/-Sr0AhfIw̎1.v9otx_i|bw||s82MM縬q+;8D @kPQas>qOL]Jkӻ};Ci}w=.  [-xjjki d-i8x W2V$莤UMƌx:bf@?Nvhݗm9oCj'$]9=l%k}wK m:g%ḻxKr2!9skTu{t:m'MWE#esc$BbpQĥ ZEAqVKHCGB'6PsSΠcBs,= `N4x ]#ZSB^u6 2ۗ@OBd`~0C.洽 lr_Fʆ9mS8D^spI'3ϜbG t}W]+a^2q}*a^Z=W8f"mRJMJXY0z)B2h02rMb+b MBaW%(Xe [J~l&V `'BzUq9_"VڙfKQ6qޮP `YSRj%j;gj]-}kNsv@o+>Oռ2~!r֚[{0v^5g* :(`P, 3ΚyCX $ Ҷwlk9C;2Xi~8Pr08VGf⹀vi= tA@d^YK"~ByGOk:5;ӧ}1; gix˨a#yO4*gwF(9C;F =v1rzZ )gkޔIs%1kCYCPl5 (^G,0(UZ|Q9#ʀ `Y3`IהwXc)$ 8L `)Z,eH^h3`d(.pP-c^A0퀖&{1'w xbm+MSPwg"XT!Ixo7YK'Ht8$wR5Gڱz['} o ȄNe(.Gss!@N! } ̄PFj5g4aq~詣A nqg'gٹy#'k_,^)$h؏?(M(Zof:q_G#?A p ǽj@"Ђ\gg&@5^=*|F_!8, 3zLʔ@6B~>kOVN/|$&d8pГdETt;TJ]P(U+f@=O^bb h(*\Æ-2mÜ$Om=ѩ8ÉNM;3?5ثt,0D_&M;Ț"S\c/|a׮cl~lhzjoE\pNȧ1G2VcI~m&<AFrd(hĻ UIϑ+'#eJm^bh;Qg \`f> k.p4d΅-ThR+!~R؋l" [aǑc0.`E ;g{S/m؜Gwz_;}?㵱#py(ž7RqΥ;%O Tyɂ"JnC\F]V 0=r0s _ 0u$@ 23gl0j灞`Lsyw Hd dĄ]6g>T"ή%Zu`"C-ap@v~t5[V]%?,HVK,AE(3N5;G``mp@xraa^[Hk9g8 "YB Zk~MMH[zLjNtY;Κ`LllwFJGbZG:%j%;*e3y.8Rq0]oE*ׯv;+}/~d`]*:̽+<_nP>z6ci^d15Kk,fH.;b{ D@ |_|"8?6lr ~al$>Nn[}w|K֐v0FL?oFkGndy.1|XA,2;!XC:~ 7{KaE&@֕@DhJXI6أbC lg4^b5z}V0:?K5zo) &R oXa̗.EtǁO x5{">o?n؏i|0\%w %Z͖<ђvqZ&gPq%J֐'(h_Fik`'p8lh44vMBO(Xe[NaIg dNZt' 'ƚ kdQ *,*D8,GVJ;mJO>Mѱ7n^|qj|E_n6]RfWsL_1"d.rj,2Y4Vsz-i`yWRVƯY ¡Sd4`Sr ajg"J+ȹ px g-\4a]:@z1Gk#C۱sSvږAZz-|{zβ[)RUi\]3&y_ ؈ _2"KZI (Þ8I]OCȅ2A UeZ>cp(ڹ{R`tFEo1(_c5(RЭz=B69xmM$bRN{>tS skzy`djO}҈#&hT C *= d5-x )*Ue~4XL3r%0Pdv=BV-Bh(M)7wZN@X_yfU`npUJ /ߕXDu|sk)o眃(f`@l ʒ y%sAH,]*ݎ =; Of9K )Uj0D Igk$fΚ@}gRbExQcs k&B2pjPFI Ir Y2#Zjf vlrcHTett1ħ{):%zz*t @g}h_+|N|vFsv]􁕷IjL/x!S` z}>SyĆ6A`)~΅=vt!f ee) `guPa j =ÜNnDoj4T hh HuXARD-F4j( ׏m0B1c`Do]=ڟ^0#A b9YF 92M4N@oyjsE/3¬O+ͱ fA/1p2 #H4hTap'eLX?T9FiE԰sͩ\MXC_ YVtyBo.-=#?w 瞛E\Ev#FPjLK rD~k hXepI6ß חN1p8Xesop$¶@爗rxi#6GPhnk//Na\o29K/FDMk`JT/IsAjE dhw"(ς;vs|6{9Ν]]2j2[0.Srd)S'udkR(5}М9%Bj9TkKAUo1K^:b5g{MMeZe9򪥼{`cש^ , )E| 2Z׬ZrS!U ץT IDAT-uiEmm&{|[G;vusEuHWSdžjV4cND@mXp$d/A{eUSE*봜eƨ'j$>uV<Pj ieB.(˒nia?P )&ߙKC1@YEΡhsWiMLD,!dPD0][-߾:o;?kҶ40/2U\dI_ix 1~@aڗ58VF0Fxa+`%xkp0X՜'=[hs2J 'W5|Nq#Ȑgn q&=اQC1Sk(\K!Tp$-O  w]ܾv}N^HB5KrjSFT̬Zx<.XccڝכӆdIvsn{_X "1fsj $ @%gΧ49d2Bc01LN#ҿքƚ`>V@<#~VT)cw ];}u}e;cWeνpAmws=0F*K J1԰yظ[$@=N N&:|>3DFwMQ 6QsM„=dKcЀ0F"?^: \rn6dZ};Yi ,!h}%  @CoA!!A\7W Y/*[fkeuN Iu@2|A3j2&҉c\FApd~##`LV=4jܯR!ɳD.hײ&Fv9ʃAw(%ܹK:Z?˽}`^ڶ:ʲtKE|gb dXaF NC%㶕DC܃+81䞘МlhJ``IFRK#ReZNtxu bvLtqزıEXd 20t r ʌFJ5cL?J̝SM8[C}TTM_")rFN$dt 2Mi">2wXs`|XrV|){*DpAx#ݫI,m.YSѹչh”@E|n(v,g\ϝ^ 1qH1t=6Aq:JF?u>V`4F9 ! pzUT;@%naC^N*׽Bw^XQ'Q$_`Q, "R[SAYbA֕8yZ qsǮrx*77nn*R;tn7'@2S}* Bѽj(S&KE}U@qh5CDuc1B8Pt0Y]*8ؤ<}Nk0g2455DPFXZbn_9$ "KUr@4B3-Aؖ9#xz֝[qE,y 6 pv+4$WtןU9D 68C S= g,/=_=SP-W,s-/KB)e3}&ՒV`se.2 A_[MQnZFnC qeP-t5N\j#M).)e-}kJSjKt| ukZ^&[ [5pdK 40"r>,iY$A #G6QhDVw>?'gRXidR5uLi}劽CXd 'Wl-q9Ԝ_ r`R)w\ܾ n~V2"wuҗ] 0&ԇ;8Kʵh#kdF@cBcT1fx,74*9 gv@%XhNR\Sd.= ?ϡ6Zب;w&DQ6pA%&5[:BruY)rO F""b4 J} @#gzтds5ȧ5(qt4\taz9`>, HC",rj%|F?e1X֌dVޝAZ*vKWVKէ/x\xL34]\Naݕ;SS2ZEr c) CCO}Tީ,i-KdhQSH1mfJƖah"K\ԠGZzrFz2wˈMwLt,ZP?k"' HP jU_c-'^+w XwjJ0W|a {Ϋ"1>!GvZGDŮ, 0H9*5@\xgrL!; h^SK\t@d`9|gmc;tYgנUef%Ǧ#2<2oʭZ7j):SK5cZ "cC˚Gz"! eDGP289D!;^:dg1nԫּWwN8DR@(fgj%:g3tD @F?Ry.a קΦrnϑ!Z <_rK$o2fﻔP|~4ÁA%/ ?y[')mKwHFg9jTCS (sm %a 4Z?WGѿ\q tt$9S~x/s'M- sAqoK˄.dc.o!!v+],u1k"m@Vp Xm5xrhLcZsvxөv%Ĥ$)d`4ml gK?E yD i Z2T890Mhe?`LSPD?=໯][ey+|ʠ" 1B8c˖KtimKMpA]~ie{9$j/75| NoI`^E"eDld qV[&jߠH\~Ö,6@θκu~PghcZ[z*7Kh >O,I C!41ĹE'vlGo:56P*% UӆAjCmCv~OMm$.8.8I NlHb zm9ˌVKV ,b-|XtD\(\JgyD?[mg Ēs^0"VOXP,Η>AHP o,}.` CY%Fim 7i2 # 'Q2Xyr}Mي~@1$Z%J8ST!ŦB(A\J2wHT({9gb8BØtÖG_lTq#7SPb -MwܴM?d=Y7t@! ""]/&sd@g+R "FGҿss<L=]>; 9Kc/uZpLvFp5Y{XgrViz_Bv+OY?%:F(V]踫2ܹܒ (LdRiu6 7n@и?"-jFHMJ$n zm ʄPPNsb,q\_0Uϧ{6HҴuZ}Kk|.S M2[;U^^lD-u<ٗ9S}l Nf_B *k kA Nիc6k}(?Ƃ@G(?"B:ǐ\Q: C̷+Lp xi! <]"fMiłwv؛$bQ`RTk/32KB$'FʚhH1QCk])@daΖ TV j5 @Xյ-"X)o7A8s8?C\qQ4CbT@ B t#EVbc)R;&Vҳ5ۮ+yC]&i=@^̉J&2$8pv\H-}T=%êZ S;%&C&Dz ~'1:`L{ZiX ]`?"[GK#D,,hݸn25B$I!=YD+%R#%ж?Y⤕}EAst(MAlrsډ N,gN&6M#o:^GڶE6ldkID' x-q} 1/#DoI#pٹ }zՋdYecal 9o,WBMW-J'0q1KFrfRFjլE9T.2aTQ/aLKx V hKc-?Ga` 3Ƙ*4/oDT;_g?pBZ'Z (`MdzxC^[,û-{-b.eOH+?w!V^?Y IDATeC.Kfӳ@if[lPCx.k@B\K]'0ohgp}Gf$ĺ%*`,ԔC.hX$,@}_89QH PҜê9%3]rP (7`{#pa\ #x bos Třs* Dbp, 5 ΊP!""C cm`}cЏg`)斕X.%vťx?R4Bɀ Is E'&oSDBkғ2R$"2f)}2;94"veh8乫2mgǔv#vH\!# 08Ů_IS\1;$lwo|{y Hx3gS6fxVBǶaE>$)PHBpsڴ北M5sTK\=hSٜN ml'oi^)"59)Hc%SDb7[nS&8M'tu0gE+?he!щFg` K)ĮArXZ$ms]Jk @  ? }V]_ri8mutn-TPgy0d @ʊOOs kdfiF'%ЩwM;V9DO7&M4hв -ZZZ33OZ\89E ''hčnITeFI-q+"L + בHbdԺ,YSENZxz'w2\N5Pl*M٧&K%u0o=|iC?2@AڜshhG`X ,oݒ+hGI1^y^y"^}7qr7Np^tOM x!f\89% '-.\>9;Ԟ 1TCuɎgvgЀ@duR*B );[;DJ 6-1 Rb!,ԛ*)s|p!~!.-\R0cF,Β6 qn\hȵT9D @'\yo^K_o\ko.b8K& ñ?z|wp'#q`i$T]t"1N]1 t Ւ[; cV~VoZ9G|( f`8'}{\BŵtoI wΤ]@Ӆ9'W}Wc3iȟ9aucGXHB}KѝhSa,uI=~?D[w~ϼ|/v_yc?u_[t /:こx۸7#م̉rn:WZ:8ᰤDh <ˌԿ(:`8Z޴a+8n\?~nƃm8^# "X(h6(NCͭq˙:!%AA#5'tzO+ǵ|RN9WM0Kjt`{zn2pq ;H@ Tg l Ed}/yߛ.K/^[w3wd:VΖ3Fu\8h;xmA) V,أ%Si<5Jd d94+Й*.7D&J9GYKj%r|י vkWC&`֬HGHn ]2 )"F!KЊaCi!2?!1!Z{]O]xtf״4 ?rݚq9{p--|۸DmtrPJtkx"5P!7\9<DTP2t̬?b82J2%uw2K SpsfN BIJ&hGRw0ŠK*ⷜ] ͙B776eWc _9O݋{31ϡ 2tq<'/݃W+'oyRQ<@w_ Mjmv22n ++g $"c< xN>S:QKW) !$Hk8sՋ6lNn8Iǜp%~ yZإ4y%Ԟ֭5 _w?5kaY8H"H:D ^yQ\xr?{>IVlfi9^po6p[maDsk'ح8PƗ Ƈ^:ןaD .(Rȸa/ˋK }//--!‚3V `iKj~^sWp7Ug97s}]09s}C+&.qGWx7.]}wn=Ȅc㧇cjM5%d [75l$(#j{)T`!R }9/BJuYz8iMaCUQM+ÍjL )bx8M! ?9e .t}QmP?)忯|GqD3?{o/YQ?U}wafaaf( bDĸdш+11hM DFMj eaan}oݧNU:[w;lU|O; 7mr }E̫MZ$u"Q>kjK 6б'?]Oax# \&i/Lݴ&ߩj'6u 4hA 5 9QB[u >w2.H?x+[USn8I~㏗≝G? 7難n_~!+%}ű.GpZp> Rկy (́}[kjw"sÜA=P<"G:Scs4L/]C `'5d9jFcD \hu2͵?&",3vq]~uSǠ|Q| zCCCp: .?p)'y04~q' Z:PQe \淮y#B;UԔ-@ɉ@=)mLa1wZUYn][VAQ1">Zxb^@'̄(6_PYJ@ !],-"𓥸wkй_w?N n>Ycq,P pGF|9f @YPȄPry_igN)JK%c3N$ @AH(bȒ@@zd7M9P Y'L"u4ټZ;G1ç„ܷ[k ZtM;Q{tQ_8pC#`Jk~W?.+0da{ zP'qh";~C0`J}71'~ 8 y4l|/ı˗G7#}}121cκI2$s -`V~Z5QCQ`v_7ֲ( tGEz9@c@(:osߍ+mSSќ7S&Nr(kfL6b4 .M 85k?Q*J^O|έZdЅ7tv~+ w04RX"P ix\}088尻ZD6ot>xHC/ 85iB"4`Nc!*zKwWu}>-fZ,ÎQy$`^;M:K ,lކ/~Q(SBU77 #=rE߂)$)_Orj\kQpNXx#' (Km$2@k)poNZB܏#}S~䎨_H /~qiQrW]~9ps r)_OiazoN<}"j?cZu}9X`vK%PTkHr` yxN1`&`. ] Jf~3i!k3-9t*` %@+JZ&" \EΟẁ5r75!Q% - Ng+VG7$UOАy5 tS7;~3ry ھE(0q[0H\oI'o=Y,b_FbR'/N᳁`اH4 ZpĶ&E_w$n 3?Es&`!Y8pcЁ5%QhuЛq.LJ7 MpuC%ko{|߾kL~2yo|T?s:|¢ŋ{>ewp?6(mB-Npuf@R\70+p籷2'D*Fkq N֦ǡe : yhNǗ1m)8>:W p}I: Yq5Yw >@, p}C khb5=C׽׽J>5㕫V[,YmoK"@q? kBsf@ Ҩ 4rl<.f,BOi4rnuu5ɳ7 Hu_ZAQ%wtJ@iFʼnD-*5:`cF$9b#\uթR1вe/~U;el`soqݿ5iJ?cפKI$(?|r\%W`& 1.8{$QQ. M~ٸqpe_z<5xoqf h%&%|1hMDXe IDATd`ҥ앯h_;!8_YZ!>7ڏ)\3pЃ\vo`]g?"VnC!l;ʒsϳ.H$89-i=0bk{:eOg۶<8|r0c S+~_\]7\s +g+P (`|~69gI᷺Vki4BJIzV-Z,F W*R I2])u$Z/ Ǣ]gU{Cl`K6i',hJAS:;A2*77 :կ̧e<8M(m\ ͏MC˿%^XL>I|rw{~"Ȱ/x{މJX1ւۿo +Py ,_qf@BoxMʡ]b\LPok% 0i[%Pب64.cfQD\醮aW!`<1״|'fj ѵ^xNs4ן[ 2CRɩw_ m_oj(.rz??CSY@PZ+b:n/ @!%PKi5 0Zdw" k5B;ЊCl5ؼ$raZ-Q=Q |0x>HVq4ʍ[3ۤI-e,+?8q!N?]pqk?~p$>9s\.>7LZe&O\/܏Fov0 yCP7ʵg˖ѷJWGx쁰\C糎٣EG&!>"f[b$[I4\l?qna}z% 9Do,F" jN۶o ЇgԝE o;z*OzpU8c?jo#("s}l/be!a/9ߴ aw4*: o% 7=,vFc sq u>&D¬]` /(h,Z`N?ۈ!r<o6@%ŷ~"W\_PRɌw:Ll?d?]z2Dx֔p07B ,ϤQJ+M , %-x hT_Pƍ86.?  i4P:(5T.= ?J%ێIC?@Lb?'0ǃ̀1xy\>]WRĔ??GO :B>aQ,_(ګ^ j)[Ѓ-!S9r9 G9 v Vr6`!(e];wbǎC)ch|<m]5s t>I~1aN9Fj|ž |CklxAnVEmŦ В V /tZ\oG>L?9?paʕ=bRԱqbJ\qeAsir/"|Co~شq0}v䦛} ;Iymw4 _vw=x}R@ hΎx'fuDZ {W?뼪s #Ňt)0k4w7 5#D7!$t 7#3ӱ߽gS3r~ɥׯ"VV}ffr+|l߾m xbp≸eIA{:|tײSp7>Q=l|O#F ;nMK^%K1@'tF755ٽ* {]}S>gfPcl筁lB*\pjDR. ¯ u U ]H˶|ΦGѐ1o&A8iz>Ti``|.&{G7U_-{IfiXH:wc )$%Mg4.e?Cx\#a) a`@Cv57ۿLs(~~ͯC5֭[.<;qч C[l#r_=S$}-`@H1чn [+߻@C`I LSC/ZcSl0 kA$2TJ`\Asz?4Y(¬M JK*M @t`r磫PĬ㦇1^>+WtU*Ɲ."|c;? >yC3_~?J_f fLv ބ-Olp$ `Xdon:e;uB-G3qy2鋶5)E.~ɼ{A`)W5 ićFETz@`<^ KK4? w+F Ӷ}E2k|^R{s~vO;oy+vޝ9ﻩ^Wpi˩tA= 7~VTO}Y-_ c%q{K\Dc+(ŁAqQKİ II.M}vG I$I?zQw5X:uiHzJQ,'8Gw14U?x=ݻvu# ~O)` rbAE :}LEЖj~X܊}}XaoG-_=ҥ#(]WgjU"PBtRs0- l.ΗZĴ2t[5bH7\ Khāka=&3M.I+Zp8 `AY^R \8$#x]H_اkfџ/ٽ($PDR\a_s#]DX&0~ʥEpQń"GpLR YCd M hUaFϛ$x]JG;Ad]6k„u>d@1'ZCD"X+?˶k!$Rwcffa3'R_/h~>ZA S9Ѳlxo$&HV1vdb2Eˇm]u0^Ь6E\j Eu&U7j %T,BV&wtkKi${,/ɹ>@huRځ`.|CTe1Og*38g[+ˑ>O`֭ #?޷/q)TtBSD@`˓[  žX(.Qliq9@o0HCvR ? 3'l(GپuRuQ];NiE\M`k mzGdt t|JHnd\˙CԧUh"0DysNjY/5k&8Q#f 4t>`@,nńkQ\:[E%x֡FgI|},ǡ25߰] F\@:S TohPDд!&gy2C m߾w̤VH#p EW4r'}u3۶9 ~vT04$z.ZbDNL+uDqؗ?7QƾhV\sv=Q`*S,"i{a( Q6uf2AXlӇ;J'>)ӱU("9|տ8EKi"Lm7j!?X'q!/+9u4gy&3z{ы_7甕G{JX|>/~]e0/;݀ѡGuGpڲg)|ddqλiY)?"\D4@XuJ͚[o&OU?4@K@-`~ ؿ_4Bi-/Y@fy3 =jWT@Us}wQJj. #5whJZw`PH:\_\$,1*gS,eSO ]z饙\.>KVnoYGr>Խu}qI@ mжMNNB~ ֞dL]r*7Ӂ9WGxi9^\FWNV"7 xa`;6*'oRC&!oM\˦/F1sD_ou;s=sORoM#)zJ0gYi!+5P0Ne;9Y%(N@šfe9"Lyj%D(Ot|`6cۂv;̔T*Eζg\L:BsSW@N(fDf K#4ou^7Kj_ *S ;4dIJg9ׇ,)GHo=3S s,]:pH-e_]wU7nLL.d 02:WQS>xJ_u>j4f@u4sqA@\ᠨߎ@w\+YH"FP,凣dD ½W nu-x}1 {޹bSm`өÑO !YV~e]P,Hd ,(z+"5`\^6feqe6JFD+0Ǝu1>GCޣom2eF`'- ?nf 3?33qs֓TP 5R_j pu2pC x Wr) ]aҘI|Y.7_ 8tėَJcs uS(<[Z ٽoaDZ "'xB`ߌ1]`~Y!6w`'tEvXx;M*ؖ{ż&Ȟ:~ϖS 6d@u9a! g=w<,ZvmOfdY2|95ZGP(`d%:Q\LsL_ܠi]!yu\.՛u"8* 4߉i`v >,Ttr6=v ,/|aG*;Ⱥ2ofbSN9YZ&6;$s=t(w쨁2lQ|gI\d=#'YN@} PEw^Q $j$ߤp1. =%x0*mN{ox{PY/xA$VEs3s%b`'~nTEۀtZ < $ȀD NWjk't]~?LN>i"g>*fyI?O4"G$'Cx-ОA!P40>T_(yձ'rVS, *Ue\|EZmq|9.ZPq@JpVG"(i$Oqu%(ߵhFN/ ̹%$0u#"d5Rρ@K$z4"UŚC}u0g_,&`yի_ݑQ tҘYnx""ᷪ?i*Mpyb`(tKe8묳ҳN@ ^[E'Vأ(Β8*p68"*<m9wF` k(+X62hϠ`MU,=RpʒX]b.䒮ukÁ/eD 1y\xх*`sH54=3,EJ˔F"&@!ھ,X1Wh`88$Tu W(/Xn!$Ǹea$$ektt:#35U^}ɥ3@><R;>9s9qZC:!.1GFFqKM׸eW'=£<,|I]j V6)B,}<8P}O2" jfEfEZnjy pKVea}%S343m2i5ke_ܱO{p_cqb|:G_U'lp;b.}^ M qxR|uDz7؝lm 3߈SqO nCg] @ e:}3&z5iɸc IDAT4$:r`!E:rW\y%Vp`x?d*[ry~=Y~LK?VIܙ_qV_? +TrX޴ JJok*3=dLJ奎NSD,=_ č [S a{OKLD'ϕCeSy2is}3_>I4&'&":  fA?m?&CX\%ҹN"DZL|-i Z@p\aPD ݋EϮM⬷4fb [F2('gZ&|PXXS$r@^"}4q5/=

%hpΚ*( q;o<ŋŋ,Lg9&wDDp괵]0Nqˠ=.DrrYI\ #N*n:Uȟz͙(\T 7?ar4FS6y=ԕ.K,vfX`{*>`||sHis"ۊKfh"hVºߦ[0:Ji=p|Ǎ?a81^~eK? * }> (b @#hjp"p  a@sɁ1C^AZEKKnِMFmCtр$UYZkQ~mݳVh]3Mw.!D.H"8aiEզz_yQJj. s9ʥ̞c.Z?[o׾Uz- wMoyU`%?__>`V}{0:2ܔn,fyoB .oYz$$`  vbZ(e?kjhk |ה- -z`NS=[6NdFل1nNl*| qD,L[aRGáCp~|۷mǎ8V\V'իꤓKQ>v [?S]pv@FDD(j?W6@D ]mkjWQ$kع{.QZi*8_S's2$:(OY]0h ;q@@̂xMtnLoCCC㪫_m߶)×/RTSAُmk?i&%Qk$ vB oØ<:m-]9`>X8Tġ33dx4q\/r@rl{-p,] oSd&AvVE6?15A}`+87LJ@қLTc8(ú5LL] u /Ygx`}x~3oGǕ)b/{Dwn3 J?̝wذe Bp/: @G<%7^֎'yn/j᣶Kѳ.gIzUG0A4RFFS.CT\\c8Kwov\@۸11q2}Zm62Dͥvܱ36 M~:TUO]$l⬳3mֻY$Q sl&$i|D nݝLcE>M=%5"S v=Dw>IV>ii oR±}۶Vt[Ήҿ.rl߾-rz6\g7LWqTS.${ 2 !lZSD!A7m3Bu\3Bky1"(يaHE^ݽ{7l/G }۶g[K*-t7B̖U9tp3PF!sR!򐀄BN襞/:=U)4IX=J @#>Lϱ4 4H,(\Dd-ĩ=ϖ A6CE&$g1Rcx[n֧2 <ϛ{H2*V߮ k AQ!|P0(FFG088Q&`ayK T &-19~kIBXŶhd:&6[4[欑N=HcDwe566m1x/rsE¹옹aϟ^KT07(AP.}pM zwyO6{8v܉];wb]صk'{s.LK_kdE#fUBja}<捌`td#(l jFGq BnZX*H~ "8 IS De3:@[G*m 9IfI%l8DOoqB%΅Q nw/;90ưwl:];w w5ީ&/o#r[4 g2_  * b?e س{w .i~`0:rs<:tH38!Gik$Eb@4D#5+J3b\z@xwK Clxјj,SXT:4Ѓn\&(1|h`Crqԯlg&1inm󐱱1Bt g/+@KHJT叠f8йgU3[hUO",$?k}]'? N~E0}r-ԟ\ာ 5)Kǥ[f&k791}nw}||rQqByXcqbֺDptNo"#6p"s"GNˮd~6&&A#082IpdbltP8x{ц#.cҥ8l2\.cզMriGuY3'ꗐe3 9pMpRI N$aQ[w*H(mֵm H 1#8MMK_ϕu )2MW<1` Oi͛65k2 }s=_޻-[R'xXrPEu,_ᘅ,>Q:l/%Y}Fu@y7MC^)v} r{`>|w~{8SbJ \s}ˆm\syAo'T[`D B6,t]CG(Q덦1K myå}%gQ:uD$%Aa  - p )WOc8ǶgswwJu8X1v \5ѡWT5G(Qz:_jq) Z OLZ) U[was}bρ"z?~F`zک vA`޽{N1KhlE pqn v{sbX:-_8,kE4L;@?0( ~tS Y -f9Ao\s^>q-`zzb1ֳ=ַq-uXVo9f2l6_uH)`ƒ5¯o wU%hI3 zF`Â!Nh:ls0'!ӫ,ؾ}[_Rk`ǢR>{zR? Y`QG?"nY0J1 ѩN{-t{@_q{Fl%0I1.9*1jq6 p-e_|݋;n_WgSSXOcbYyM4sW'' "ys(A~+$kic9`h)scfh#0YK~qu睸;A)IWcժU72<\ P2$ rm t>(fj'M8(6RbժxIOu(h#^Wz9`/n+K4'ׇVgq%5lق}wCn58Y93W5 ^6'r6gL (⼉)\nedR?:/8/|aYMܗ3;xfW1{ ?֠ yϳ6 Io9f":ULtky!Cpvnq4.g] t.D4AWWȧd$'6 Շ Z4Ob4i{;pѢg&&&pƍ7{;X;~IlSXj}Q(IX\N[`(6ǙK_jc:V.cgHJ1dZ-ZhnY|jcS@koww AAHXe؅^vV ]7)̱hl|D! ;*O:lB 3Gy zpo׾ػgo{qYS`} ',cmj񟟡;/S u]]/,hn6 t:f'Y-{8xPtdEK<C8P]i8nL޹9'J q8U!1 !%w9f"(D':Zu28a @%Rup"-:'#iiiD!Nj@i~8~<h wNU)s Ϛg0<۝P׏f| E@/V۸!..T-8 9e -@pp,uG!kh) GԸ`2d@D0U& BCyޓ0MlΦe3&Z6ҬȤ` t#`T8I@*8 9gt?43Y:p^x4 wܬ٪EJ_ַ:K@ ^/nu8h p WXwRWOaj⹽y<<c2_q:DI 6 {lut0,!i]QM>^NX?W5ω~;99({Zn+55%s!:i;em=iWK}DT?&k-v1צB|WD#5pӔ`6|̀J57aE"y-gqK3 e&v] ؾq?4m+KtˏtPŅo*(2:8Tp*R(3Q3'W s3GpSU8'aI%W%Q0 b_'0g rln)u*?t!P0B)G광J 1j:a6vYߡgܜŋ5>iK WE8YI&VWRE2H ~ŽF0Uu YHo :V9q9*FZ ^}>W?>Io|b/9MȪ@E?[m}jm"Ht)+_-Ya7)dij?8C-o8nSpnO mu:EN],v'!!<9z6&ž8ek Ui@Ҽ(tE,Nk5FjtH@c03J"BxX~l15qS۬ lQS? өNziO2 1Wh.v&1;H{_*|oN?QIᙔq٩+o,>w9+t)X|gՇgvΞ2(B).\SN;'|v.c 2".*3ݲnb3YH00^9K$i#E$ 2DPWܙCV'o۔v_ C([EԒ̫qo_('۟ ُ\ 7?vYGm-9h7U TCI_=Wģ[36Je̟?y#FRA܏BJs&?!41Q#>ftyKv逢 Qt8<fT%O-(:sv)v<*{4UisF3;GaRJAӎT~Ty_ \SYW+r,6d/= nV=t|0RQH^~p9 ^gFY ?5Lo۞B) cdtPVߏ>JY;iyr3&'&~!`/xL&B("C K6-iiqQV?^0$G#lL}_We@I*o@j_ XK$ǹykDžEdSa/?~/FqC<;p[j@ 7<'a@)L9I`H)*y#008jR b9\m#B066@q)&N[A۫KFn%dqll/8ްe96SͤrCԌِC*2H5ע{J=.,(1]X ݁3}[e*O>%Z" TϸI|m"8xMp'rNn^caҿڨRup:Qrrneb|0a<ÇN]4 e Q& M8cT]Ԁ!+geڬ 5.ikc1&#tW\KQS!7ÌG3Uyw|z_=9PEGJM5~B/"*J>i1Т"X bJάA(1YAX1 ՝& ?ۯsyx=X@7}IK דbjƝѹYwDžޢSrѣ+'̹oZ7`f̃hXis \aW`/N^68]񮿝=JpV@%u9~PCB"t'sx<2?ӱHo"5v3>"?AR?f/{ىr.g#D j(mK ֙Ck LLk[*$#}UaC]Yma褢?}X<=7Sӽp? 4Dw#䑡`"~rw<U83/}X\38Xj . D6`w)9n=A4"J&gv Pq& ꄂ1F,<.S%8 ch3zhvxG&saÑ\krxL7s?Г̓tı&n_P&KnvsS(YBNlǟ.:c6d8&IpQL[@D} /,./gk W|/>,|l>䁺EQ ۲0k;j+}82~RUg*"9}ǧr82o9Z=Lk S=9tIz..p.djf ˥h~".oG=);*cx-Z}vvkgOs=?w 5y΃5;lu^vg>EtEqxG>f2e/cD'x SLw8:Lqdߡ<O1ufho>H9<ђEMQ0?Qݤp04=2oݣ2Ƨ⍿yFpt-ػwQu} rIHNXm|{^)# 9ιǸ>۹%8LLY VE0Uˣ6GD=<Ƨ1]~_*|}A`=s^ ނ܉,€^/13\#"z#ie'jtrl>z:/k`>"- TO?k89|qC7OLIOYr@$ЄpmaG-godϣ1cQcyԼYmC}C+f:~|'5.Q!}rDxDe Cϯ랻! ]$Ҿ}.{eݹN)xE;%b1Z;k?/@ϕMN9PnXCrx+wE F/`|l(m}J%(RMªfxt ?-c+Kp%+WA;OiWv_Xnn%;.;vqBik5QOSKEi&vliΕ[{۸~h>>! N8ڎCSc&7`2׵U@+ T*TVPmn:j@culDZ=,{q>Cc~i?>8t߲$!Y?d]8~` $@j&]z 84`ym{QI\wsg:zL_\hΕA @"E@41q;39PE/~=rbuc@1}xN#L8]uc8O\v6Hn- :^ $b5F3$Q3Kv,ޜ't) #5@q&! &yq쨘N_eP&,^g9[h@'O#13AqD: g8EȎuՄQ4#M->f_xꩧ02:c.lk֮?9oDFo.f&s&Q,0e-B7-عl}IH@s=K-Y_rJUSZ"8q@uDU',L7gK23dɮĺ+NV̕%XH#M#9ɃwTh3Zr:*ܹd3t353$Y")Q%KVǖx]aS ;0Ge @OjɈ Eqtg1ϕZl_?3WP777rd;crr6mV"@ytrT?ݏ]B(ԏo4`qv,4xU׫~OC"wb&k_m4  4~m_m3_iKxLUh>O_鍂&>&\pž.9E{:h v{oQ/Nk۝EI_her[j am8yɳ'gysnۊn=Xd4YJ_XTt` s |e,0)3%`$u1 "sHMfX hVvOUqKc,pםw+@N333~OO?<g) gjxo;vnqѹ{J IDATs3޲5_ٯJ.7qjYYsUsn@Lk7qsZ-ZJT.iw]Q `0Ub 6T7vԤ̂<޻dfhXu   @*\̀q,&c`dFW-KSއ/u7>Vv@8~y;5`vcaIh5ZMBAh4Us_`XvCaXZa8y_*{ǵ]ZrrcVOf>u@OAAv+R3 9)}vM4?-iH5B//z8!ղQ4֝)lִcpI`mطcxxzAG wu'2=`PPIF!q<(c!cg8\7` \8wn8:5ЬZMBIh< / DBc)4tUZ{\V z/osJAdi0,_\MY [Il+ы3SXui k}M,JoOٛΟr o1|Pk{`qqO}s/tF lù4 1q7P a~Yaxry_X 9\xhZCpffU!RPh ~*Q)U<.}JH{eUHv_ >E b p5 UM2;&Í;-x:{/~'rfP-cA;]wǏ?~3̧?ǾūA`s3!hsN#U eڸۍМBWߏK.=u4SR)xA izQw?u1̀r/ٖi&M%W.pe3PeO`!Rh.$RPPJZ͢@7M9`iIO} >O/|fOxT|Rmy</FVOW9_q333CPs] 4E)JRĀv;/u?\G A_ 522{2qNM_^a/cY5?u'h{sDʳLyǢjʧ:<}\}5޿6@_>􋿀^z~}_g`z;7x< I0ky0oZطovލi%lAh1ЯnSG(4c` ʁѸ|`ޓ~@cX^p0@]#*e$֯iVB'EA*}޻ah~75{ދw6#1pQ^ЛO Yyk %*4$.ڿvdi/_sh4h4m4׋S*@S@@Epx>Gc130Y)d`3Hf9y+1 ք)0~D.M`çsB@3g=$PNf u+q?XX8o?>${ })8~$,>3] Կ2dd;yӦMq}عc;A40zZz>!-jo*tTw<0tj ⡹As\b:0Y>-[.WU.rUK dŀfV" c5 g8՜ŠS޷6x_~k BHY0Any-W1$:XXs =_x/zGȋ7Կy-9o߆m۷cӦMD Z jkdA.!c?4EX~uԪßZKm-`jn)w(gDx+RmKzբIcz4eEMŪhaAT0f'&q5yAxqM7miC.AK sN`~e]K/Du˘=ӧٓ;`~ fOc~~^g߆rTVMBƬll9Ė͛i&LIՀ [s]>[m^|r"m~VU!tjR3o4pm l_ֳspH]i/(J"[la!0K}q=<6L \rk8:1Sq+{ @[CA3@h L ^Q @$B-%30331A D2:%tetV!0{~ pp9t6ÄP/˭pZZ_ a@In}tcA`d&>) 8ښ3rvLj:0Y/%J8@hȶ^HIL؂>/y mC",82>7z^~pzi"̈́BEtV ~ 0 dUw0O5{Q^`"$QG? صk.87-fΪ!d8 lߥ iU A\!3`c E<54@0S20D5w̧?v俤I^}0Hx"xD1 0KFBl:mL ;w֓5 uZ&fcxfv,!EI\}n/@8d @/d@/O(biZ8޾K,v_???Z޿%dy @Lu ݸ ?q1֍+˵+{cxxvCso5?fK`Ba)׮%~abC!&0 r?X<" _2^zہFސ@^>;ٺ2 RFW #W`4 *:'/&6I1777%d\/u _L@(l$:@e(TlߠW0ȑ#xGt27DʷP 22K:LMȼM퉇! \h[^0kk"Ut¡ɡ;O'.DumV/OPr'"DOqֿjPWFB ^ NRKjR@kBb>[A+īlOտ&p5Xr{KHNӄI#KEApJ5V.txKHm#<>1c Ͽn"h)CVPd D$! qe;`9ri utCtA 3OL4s+:ֹ,%. HV&/m"_BkYxnrbD^>th j¸Z߯z H$ SoF`a / O,/jaQat#DS2O9mߓqbqmCEAl -~NCWS(`(?UXXEhxph?~ϽtX{Y'(}e4j@%/B?u s!B=db!E"@)||⟿C^g$ݯMS#[ r $Yr6~d`¦h+Y榲4 _o 6kns#$KB(y9j266>r_ w%UԼ}|qpɑ"ٯw9@*}/R\a(ek"+؂4̦M8pŁ5Gx>)f?i޿HY(#"b>qjѾ)Joܢ3.:,/={C v4' lcPN|Id(<~7uV`eqh_!fZƒ_sA#4jz|-J*j|zžU owRā@"/C Jwcد:Gsn ngLٟ=oOuTCd ݯ._PfrE(YākfO),|'3y0ۆcV_`M&\Pp(ULxv֑&ƛXBŚcc( jZʧZO#k$HjՕ.1=GwN^vyFX&!d|6gJׂ~Y²(z=FrZiby?C)n333#߃{x ϾR'#ppujtkc!.S!j쟷{?w'rh6L.\V4eScbWg_uҌh!憛ba> `e9>Moy3.?0Oÿq<p a=Jg?#owxKp&~y\qFu\+.CI- o˦)xe)xuzw:nR}Av]]m[GTof,f^u xoceS-"*z5 Eˠy ^ {ݫE Z'֕ehrU=[8 eVRTekV >0mjmiiu]ܿwk,ߕpAx>߹L*:pOk~+*lJbː)>]lƛoI,GD@e(TdEV#۰)^(U<d( ^wV0?HF**.>{2}{'daOHqV @1"Pa+Lˬ_ǀ yc KGEDck@u_u=&&'?i)^~/9 eQ4EW8N^bk=,bNe;\87;4n^5]/"y:2" q[_SG7ea?SY~=bJ,u]ƛnJTU+$l-clZXE+/Q\hp,G0]"4`!;=-C x v0}O@j5@c=]n+db#AeT?I'um`Rjݽ gyQ!< 7z1 g⯹1{f e¡ pS'S8^Xi3 _5 IDATN8ԟ؀{/|lh#Q * R$I4V{zwPx:cEKQ "r `2<2!sLBT$K@_$ECiTph?z[qǘFa6b"I.v?qG'|"=D7qAdlOHeEƦj2[ˀL+pZ"C R28p0-Ddžͣ3_4Q9_&{t"\kBSd%84JP= J,(\i+")]΀aOQ@Im ;XS #lv=Nnτ5wTLd 2Nv W6aHS{U@HRm#_LS<^",ߛf?le(OrEÚ:^X,ll:QC!`Tb<"ي&0Fxoæq<{PM@e|8|9✝[!D?Co<K+` \n/ved0 D;VWJMCl[9÷ne^{ر}sa%z-/Rsx;n3.xEVr߼S/ݱY̢AY6qY-$h4^zR*o6djqWLD´~kN $hHi|8rÀ>(jbQIA{ܼ8S310//PwΎp]Bpp}|@|<y!hǛXZW zH!0&0=9>^9z*y 6#1`o'I]۰dg003kONaϮmzH`sШע _Om:ᾏyv8FtPfjz}:evM7 RB4-fqOfzJp찁j'PLUV7g@vXAK+󘌬c۲G`eS9L.^{(~ |qqt8pL5YL7ҽpP XJ3?s\w'~n9yy>t=y6Sg 0 <TNQbj3\g|nqP3yjP}"7m^(ht>.,{< > [7zB6S>AOxr?ŵ2qi =#|ۏ@H8Ԓ?5/F{O3u&秞y¡Å:O?2p;E䩳h;>Ba'~1</X\?8R~ ݸt: eې!,_5օx;!S^{fgg1,/u]{~Ў7Rc"^&1$|}߇Dh $k^5) C` QrUV^[}/~׷J JcXT VPΐ>kЄ`&ϺJ& qm@WԱ]@Z`p( (VdZN1ԗm9QMit?v.8\NRN]Ϻu! 0b|~RcL ϒ޶XY:CULj!;_1x ( 8^ bU}Ι}8RZ⧗I7wA _)|2LQDPJl ʍ0[B,Bo} ԖTH ss3MQp, txe],чԳ?t!TEAQןEemr67<[T~-h:d=Wt^ϺkU?0Y'QUF Ҭ{, XJ~!l!|YpA=ʏ^iR+/D_s  ?뉋U؆ٳZ1S2O̊dGK+ e[_?)!Oߠ6d w_jnCΑ޼KH?~TNe@ƶM|+~jO`-Qe(j0M>.WgN`zan&?Ott]—> ?~IqRH Ć,c(+rE3{ KàY鿄1V5>lz-="a`[gObj! Ԥa?%`Dر}Fa2zzDcA`Gw_ zZx .T2419SDuLPNv|b1.(4>w^DyL{i"n8wf}&CB(sjU& J=deq>%N[%E*FX|%mCEZ!x7C8Mt@w>eΞNM`*_8p~GqhvH$HC/|- ߎO_gP w\%{pɅowXYY w 0Np!!s8 ^qLݷހ'~>g uk7\p.\{>9~ʺk\r>ncX\3\/&B}7<VutШ{n>\%n2W]څ2BW_o}It:vJn5Uj(hMT`ߺg§!R{o[A^!K/|>s/<{b|;5.|S ixZqC={ߪSiҿ~C>ֿ>(8Bx`QЁ{{D1$: xv<0AIUF 탣 $=JgFX'2ig.c q88s?z>^a|g1G I c9~3;p^E+ 5<0(N 8e")ǣ/Ԉ*;Ux) }oMo٣\+&VjM,-x,:?~Zn|߼!oK= Lӑw59[_}{+Gp@uD$/h>co?w݃Ng8N?2@_;}=_HvԤ>_sفe]IJ[f;nX/:/59ǥÏ> ϖڛĥfD_#xg{ 4 vո8~4>obvn=;7 yYбl.n\wnP̼P̈%0v? L)#ҚHGb[yP4}x600J`e򂔋@vB_>2hD'²aG0%zX͜@_aq>0;FM&KޓR?>;wE<~@?87|wܸ|gp/ߏٹ L(J 'x^pͥXXXqwtpO`xCxBԳppObɡ>4bo=VHQo7|ۋ|' 뭴;wǞJ\z%\{`?vڊx o>jGeΚ c F+Z' r%gNdb %kP(:d7gB@yʸlۅ؜& wjC?JwifH) eZ2UqfBVP崰\nSgsKKKKa_$"k@+:v_&ҿ]8Aa I{nƯI'StZiw")S;N++r.8Ф y盜L8A0$3dV'J{X7 (w ۋn3E.y'гnRu?A7ASk5!DUTRA%VO`\OUyP/fMF4'J [Ԕ9&9 D(2 Q1JLF'o/VxԶ!r2L/yNjC6=ѧ}%ol|pKkT@^;D8x_p(;ei&_ PG?RX*qe(!2  _S(oL$df Id;܉ LfB /v_͢ D4[Tůױ;똇xGNn\  fln9IR>& !ejyQHWHϵol\ `M d*g~zz:Uhsl~4 zͩ*3>iGcD1s?~5q֪OSQPשK o@c I LpՇ^es'TPe yw}&;@9Adwd\k2@`}]6KX6I}M8hU^{"F:4BF&U-3n"7qZFMŁ(@].[)ҩ _ 1#S8J?)PuU@iY Kl>kyfTz+8A_F6e"yy[ lG$"֠ *)d QplB/?oj6בczpKQ@h5kI~3j% 4uW^4bp\vW+!'Sx )hhSh߃}nOX-yMOSqBuUqn[i߼Ae` EI۪Yv( mPeъ79#Ywd$I~>fz>@@ U̵l7l(a]P@055eADґy,d9{V_ ;11d[U;uYZlDXk+`K^ȼ` T?ÜҧzC@$>#_HBZd6%1mݿ1xq5MMM'U 'JUPㆅN6m^uiOW+=,S,Fϴ 6yI&@ms~'%? _zB'F`/"Jc$r `m ` ̎c Ӊn<T=`z/= 9$PPBc+pHJx&`A%q Ż# Rh5{|'&BZ_nK =~Wk?ߘ֊)W%bɋNaJ#`jj:tXY _m(F[Q$s,zP0 KVh{P#g]ϟᤚe΁J ih?E3$9#e\$c?RWW_ /)~$IR'?H[w`0lC]{Ȏz# y!﩮`)26cY/kUV0ubrUn]gpQЫ"fs,, "9irÚW{U@jy%O52d<>\ /%VA?ƶBD@q1>> #)_:@П4all" ԢcyԒѶr٤<"lk!rf-b9.[!@? C?.(}RE_FT=R`^+I<6rXֹ9~@X& xy D^e *.+m[L @?kq}~AHDJFTWW‡sNHm( 0cAUt*hûB|dϕe ʌqÚ Xy9 _c$e{I6i^P,Gi4-Lv?,Id#$?%@ T =YedH,B' SS( PY چ#zE[s %So@)j"mVV@/!D?c?YT%`z5@k R@(ZEmEDŽ*du*_-jUIuEa=&/ }!v9f=B6x_RZDi0VF*,bߘ2x֪33 }Z Y],d!abN>HUd\ȉ9tW/a!*=s&VPc?k~W}i@R$/ 6,[+"r9+[l ڱ*>d~Q>fρթe ,f\OSՖ(?3EGGnd$A .Pj2 yʶm؊ AgB\\^ɩ"@ YJ/yC& x @^"D!0@&;je_&j d,|߇}BDwIkDԾ)ޣ); vq0 rpaE1PQ&;њ_AE@&z_XSv7JmĂw%ƬY޲MU=򔲔D 6\ ٵ*/kѕse2@ LOj|! E9x u/E`n+NABpuaaaa~4]} ']{ nBCp. 3$ʥ93 u"ՋZ*a[ZT_YDٮ&)kYB@14[y>s̒ GkjR@$EϟY/aa~ s;Y,.aqaKXZV汲'  `@'|WCHlt9/ܐtWә4O.KDyZgisly7f"ĐCYACs8GOq@\|= 4cEKc)ǐ6oc4Qp^Jc3b0j'd 5n4@Zh?xgC`(/9Q``є3mLI"(3 чI3/p1@ WSz2{^y:ld+{2' r9tW PE[bn (urZI05A'8T0R.hQ ͚@!/|=&yH,,stOT0F_ pw3  5:G?eCHMe݄8~~}5>g?68!P3eeA֦41-O0D@[9RV%n. h?^r<~eeV/g Թ@hUhzޭ@h5cusCq^4:Z6>nx]T}Pz<#'=6i{3= y`%(2؍ki(=Q``<{4ÏX7FU4][ELTυ))޿j4cNNOfD %o ;ږڙEPR[E;*#S:}2AQ+/n$,,nݢUTCw\p@ ԙ:h8MG fM`&Ш\ыb;pFrj}iPػxye7NZStp~J/j5B MP_mϟb9YKN=|Ya'Vb膓Q O_!l&"NG xQ+T-2yl)[`lcl,S:I»R%iZcv췋7rR)c p} q9P @ ˙K3ʣLF4Q(Ϻ,)YRѨ S=3lI*qMzHSB'Qe O^BIbVw:IZ<}]Fc~`q ,K)fџ@[L%5P X捗(9.{-L|5XX4EVXnFѶ'[c!)-E<Uւj` ~\v؇6lME.&5&7:JmB`(a$+sQ`F,Td '~v .2 prkt }߼O=߄k]~?Ls?7 3Zq2b  7ڃ^@iROTd!DFC+r\9YU0 Л*9truM8!RڲGi"TWq.zԑh QLDbT%6$g~?&x~;;b;,Q\{˟(Q%w1(nSB`( Ͻ'`'9 ]r=s;`B v妨[Iy4*'bw'/|&-SPeQ "%υ3K0*&ls1_ ?uh(ffAdUO{eӻPJ!ƫ7,%J?tЂd׮  pFS.4Ҙ~,%1n$:ތceU&T*qmr[@3()W."6eISbP,Sezǟ?^+Xh)\?(я,ŵ~-ʧw7x}wn$zW/F#/ h)O!^<T:EY,rw<*WVqTdFGRE,;9D^s]2LEd–%Gj 4 jꕎG@h~姮 cwn{I! IDATQbVX8Vjz}g2؎}N ?U.o]']OAAYI2"w%c9C;T5tJ:dJ]eR6QnkQɧr@݅YrN];4!jN1Ul}LeV'\!] k3~Ͼ/<Ȯ6DO 4?jdJ֘eC#(͎C$u~;Ktm;Clu29R0KgdHk? 1(Uii.j&5%oek Lc"p"E-3&(iVulG/rH^V9빺H}}l=an)?%]oY1+b CHټOQxC[)%w~Wե0L*-O߮F1+ ħ!v#ed=c MA19uQFQCb2dyA顢'^e\E1jBX8Z6: ?~0Lxުpw;Wn`ߒt\FYW-V^paU{B;_]iN+A$A2z ()+',8M'U.T>+NTG7`#ymEJji <P'd0) $'QΓ' -,"2ttM_\{,?oeIut:ʁ`Ql^i Qo1)H R@kLPRa04vŎgW,T[*W/bA$F)9F溣F&k X R6EeaMtJ~~M;!7Z#[JJUReDwW)[>@ @9q? qOFLF#z0?x'fYpCi.+ n &zBT@8v߷Gofvk{R eO'V؞v;D(,c@0ָ"je7M[EL]b4:q,24w t%ebw|qnx$OH/|ߝc`zve$d3#}m#Aao܈ynQd7nܨL{t ?a^Bw_b[C-l <󞭾/pb/cuM3NUzJԚI@K+ LBWKKXoޘ Ds RA-nrL$Ky{y#/+ID@7=|}z| x8Ϋ'[a[8~]+VW$~|| 1qJaaa VL#@JN;;;I-zpHgOK N9D澳<| fK7qf3$`a+i^ap7*VZ6YC12hf8<\ʿ. K:y 9 8 uzB*SòmҠps 3eH?xA7Š#u[] {kɑvb*51{ OqD W{mo"\Ś+όWisU83iŽg&+;zte*&b]FC[&ۥyQPpyyRKujEN\,qĬ)* 4,vEÐ*ik+[>uefՏn`yI%0>0qOIubI1,5VxwS_k7|-bi~~!:6Z&20  '*PLO}&8z@_&ayk{,n. Ǩ{ dĴB׬K~f؞,!EL62ZP79eKAVVje7d3s =,+FE ym9+?t?t\8`T ۛ!׉"];~0]k<<+bo#Q6[C E]I:ł`Gex1;fFN10N045]+ ; }aiVx>x:Jж}MQ \)Y,d@* )zR9ޫDh.AIY%߸ עv$uev_Z[[0ֺo߾+Л!iU$&)Kȗ_#J)G k"n#Oi+ @|^|ӯ`]$TW!y<χMR*!A`8D_3}{ @zx mlg*O* *$5Xs5mrCmAVxA%dh<hc!QJQyEUZCnξ*ej* uj:-( σ@V6Q%ܽpuhg8Gǿg+Q 4y.en{o\Tmu[=^h[5zRBJʢ'"!FX$z{ RPRAAA)W RʑkbG{8qxJE9Dﴀv0)P|k}[8A'pU+{9J0RYt#rB]JqjqU1Ul (*jAN&@Rq୮"xQi4@4tN`m ΨOJf}[xuC3džx~K쓐.cil`&&IQYH,(<|o+=<| zGа a<<>4d-&}`Z϶-4A~˷2U9o?r^X2%d+[j2\,^Y* QpMYღ kNj_Ł ~P@Y8Ie(BC.$@CzVG2dv9_ԄV[^?Dqo\n;!@<`O]Bh C8 6c@ YYlI(Y¬ +,bQ/|<8en|i"#!S AuMC6pȰ&u\QX N<(&u;d .h>ĄdT.ٽF<=z)^;o,-(s' `8{DD޽|_YXh:!(;G Z9UθP1 Æ|Kwf-0W'W֑apȤNvϭ]0v;zֲȚRuZQ2,®kd]f쾈>(+ 4%df?y*V R<("9/e vԿHwvp+޺&Ukp# ppo%e# 2} eI}/O9,@X\3A6u0E+(C.S.PuА/[\w褦nG)P5>‡Q3Y떉nS*B$B YAz^ͧkk"8~ti`^? 0AձVq@ sN`ς }@BE5"OBQI[ #.XRABTnE 钎gvN>q]Ji`pYRD, 'g<#l_MCW@6>GQ2CYۂ %-$ 3zPI@xׯ{8qX&I8R¬WjmB oSj9?c2hr7=Cֶ(Jq@i͗TH6T]859Nѝ%TFN,pxCe=QAFMc&|e|f" Ӂw0TT 0 *;}t˸)., ﱞ0d~YO:k5gO.mwaLn~d1eH4gK Lj|Lٽ'FUݷ"%UʔVij(kWA42@/z1̈́ xv><6>xx#ܤR-^bX]G9KTRˆqzٲY8Kcpm:g#]|&"m!"G44e-&,*111BK%rʑ?Xa`x;tc30~bߴ WhAkkrep@ΔboD@ehJOw}xܻGZm.Ykn rq)C$4-z̤GrJϱ~ $¿6`a#LQqlBU_T$2u!p`JXp>׌(-HLJWs-ws"eQ:+CxS1 KЫ i؆@13m||B GWvR}xG@z;]!eyY@ loVm HꭏNjo7~q$55ʆ*,O9<3^eF:OfQ An$@箃۸Io~jR&ps+jxV \&0C6.bpˍlUXXXKGYXJW3 (A<ܦ)ދ0`:peNjӧsרd eO>:D\&T~DUG S@:ls1%p2;ø D ^o\bcK$?& Hyv z-^Y(V$>n 7/>k .Ҩ]\^]KbsP9֡UD>'TEr9eCRw 2GǏgZpr -⢤_Ot_5G΢**2LLa (#P%IK<2<S?*|n"'6 ΜcTcIG:^_rCQY5&PIxE!aA0$ w}9YifPT[e¹?6D߿<ԑ贲gW|[e8II}l-?7"6^)!yY5(yc~ΓH–N]ِ #^`RW= Z]-*Q7,{Ux ]"?5jX$(b?Wf3NJ2Tf#i v/*OS+,U0yRR/߿gXEx-aM)_܉ ,rf#+񏍼 b<@]g@WVoq{R>@[nu3uz3I^w>5U7B(Jĭ3U6jarxC)N>w?b F"`{0)Ys ~T"f+p:NӷGo@|3_[jyw' чO||sӇ6ZǶ* LWE\ #ejT`TB$)jwYz?_%p޽@-u (ʺL.PtN`P2^x?>$)}m4 ޶OlDSCEdzQӂ5K?}lt][u-/J<+ޗBKxu7nqt+Tq+=CR69PN(tpp@#<ްJ"寮,y5-O[6|mM ɽ{:5ɩHW;Q{95=Na(y.l`Y$gEǼQ և<' "K; _oޑ^x| B#u{82鼧`U<޼">|vo\.z9Bf'etkݧl$^čr4%6WpY9P rKLʆ 꺒#Sgw `"] 'pb7;a+eSAft,secQU7('"НS<OØ(.\+܋̗;@  І!q2qi߆ހNjoWm_cۣ5}| r)Ek>Ǟ3LJ8{b(,/{x5 ;zMXe|qA[$<X )*)36"XbL^?9F{7TJUZ;`U"*.3Ç<9ɩjpe  OL{a4eM㴶'孭JӚ Ce)GeB)=+[/+}\n'ٱ.8"Bubk4ŹԸMxUzkoyb띖7ЪXu{w&yy R{;||:gm|+KCsX=oVŕ([3 6@C i U$&ʱmSt\8sycxs꿛aItԙ3O *I :כe)WE(lRc A2RtD! &u"m\{%Z:k"Q$K`Zº|C`s[`sM6acXl& +ك+C&57^W|K奐`ϢbgAayIbiA /=+s ^2ZY:6&J_"g^8P׮50[C0}^3*kkWAAZ&M Ǡ,KBeZ5:83g t@}vmm.yW^ C"2*,6sl1Ҋ9c'e{B'so@zq@Ǘ ?RZ#9/KV8wr_{zϼ;zUܡ\Qއz5/_s`y JJ\m#HZy})r hJ( IqQg]܄Q;POza}vg[bt&ՙA>=EҊ{m~gcФZ }llDuRԨ)x?,A;ΌO뱼(;oԡ#Owpmsz0 oxg mwFde{8Z,$X~elj֚Ȝ29%%rrq7z=UF wLzVkQxr},cU(Ⱥǭ2-&"J"39YZT&wFgocBī%Y*^cP/߱7=cp@yW'- >;!90\ߚTPX@~et,$ DU&@@|B_S?B 1ls^ۗ8uxv5yl^Cmw>DÈM?޸2Y|}.;%q籭\2YNjK6Pc $=JٱIꏒXXtlB(!0xi^tv)1]'v6_@>k7nY&#ϱ8}LKh1({!@ᕳ'Xd|MnGl[Ty_K'*o^mD Yhom 1F_}dBsg[WO;X5%7j W8q0 f0 <| 'i>_|=Et 럋Z,'ʕ/'/<a>ڮ!&Ƨ! bYa~^@5Zq\C` p ʐ½Yv!BDWrw!OF0#Tc&к?DhpAf>YgaKXC[g{g` aEW1b?~9`iA9 N PA+oyWx!FvKb!/yGdR}8y+7ZMC9~Uzǝ'x>VGݗ 7[KB;oaeQ5ƳI\缂YEKVFi5\h Ü]$='*kȵ&괜5tϜA0v\W7 cmm,}h] yma6r`؇#g5~3G@=v*(nÐߐ}7 y,}_-0:>G}ok6}V"T~|4r_{m \]P{@}  8y4G;P+7 yxUK^miK,,v$ץN"$p(^@ la_) ~C'TE$V%v`^U$ءgyW9 VC`~O0~X%r} s ϣ "t@쾂Ȱ)?Bq8$g.9.Oy ˂e]ۘRP `7 \۽U<O~RF1Xd$]Z J7& 3!K,p-//yqt&zk=< v+,i!|m+(\xgxUKOL Uf([GCVOy]z}5z-ٶQ8*Eb0 ހv!bO)s'6 jeEs?2>; wOKqyk-#2,dT*'}W_SOVޤe]!~4!ki;V+|j%jG#Eyz \|^\@Q `Kmn+Q0 [w3@pGQe(/+XhK{|+KHdDVdqGPO "Dyrpx ~G |/Aa"o?0%k]$0b.9Og!B ҥMvydtz^i쑝z&Vxػ$ݿuA9H@֏LFʘ(Zdy 7'@¯/#Ԕ𡸸 Tdܴ-\YtiιsSźV~͞âwSQ/)o!bf5-y="2ɡ ]oHj&wpe1rwq۫j giyt>=}wS+ iXq$`ʹdT(| t@xQ ,E ȪB9dhw5"\igww)4,"ǩ0JoY:+yseˤ#FdEqv3?W gQ4҆};kW;xRC%KPiQ"9uxζW e0LǶ(xR/_4VRXf,9C%~$V|P4`ƀc`azgxNi㑅glޥ˰6%K\Ηj8xƝC7zivއ_W}ޓ5l,klLU8P `ژ.#"ŭ9(?τHbQ'uv?E qx 'v~ދ(R{X^@2Q7đ 8 {|c}u|aS_tL",Q70";^Xm߫%#;h{do&FTl*&^M+tNAT]@|eBJf' JK%K NQ`x2CF dUAy`_CV`>hPۭU? ?V'}Ƶw xvY;1$6u w,{AV f6V Y{\KQڧ V-WbF )wGV8k[.oMKGy8i  uZ/ocxz _mhMtZ+2-賔6̖y7N^9cev/ : xֿnPIȎ(2,E+.2qVi/X\;Og\tqQ{u݋kkk V2>(?9(Cf+*LZStEu.VfB*Cpe $D(ueXU脢)%GXpho\c^ PZ mNKb# /aﮎ7%.<?v;큈Ph{-_)}Ǘ8g|?.o4Qٞ/7 쎀VȃCrƽf>R&߹do<#̊V ۏ> <%nb/drw\[[ƝWWq_9,NXpG1TTe H1q V}t ^|N(Qk&&8KMk(wou/]hTUA>[ r\#%:(^/}/5WV\f.H8eő2Rb Uv(2(ﻲA$3/qVW.Z/.bG!WW|v30طod I/t&dꡝ(@ MQ"SIq>Pt#K+G8 $6In"*UN .TRzWXYHYLi5qXMm9]yO8N;)9&2MU2r Un|ҟ\)2UHzF42~hr<|*8zrZrWxes@dzC9<&)Es ;u .L3O#;hGd6~;dgӯ ̖=rmgK2 W3 WϰR,jp<$wf4 Oix$TFQ) 5, _ #Ϗ{@ R@"Ksڭ&l{KWb&Jp%k iպ~AC֎cryUlLJ";1Q%O΄ڷ3'ES%bBD8ѕʶyKԡb F*!.^8K29R׼ob,TK'6 erjPU01Z#Pf2`JUQUo2{9fuƓn3~3>CeXt􆨠苪\ϑ GRWet.c-{,}*KqV"GOsRxQ8T9* }"ze($==J' lHv.ϪRPBC?,-HN< f;9}x1 *@8$tV5P&GL(O-"S*gxDAE&n2QS>hy, ÑI雪pQM)}嘯ax0{ ӧgm\%5ք6SqytV+sr&$Q}moC9X4D)m=}K/v PiW/V5:6/[&*XaJ|VTIkW;b7Fd$#x"dث~ yu sTl1I] ߨȎt|O\kʒѶܵes9Gy0J u%Փ=w8lj(L иUaB\\.M㎁)pW8X(KAa*cc= c,*#9Of~_xp֞wYםJVE&$WdD|)e*sXR!!e~;)~\}"Z#!6beP.ObFc#4r Y`ULo|<&~.ʍQQpm0NȜD`,tLDڐ{=Ṯn{xaV${ h~ X&ty*ߣl_٘Ϟ{i8)J$㞿UVBWjfn83+/b'fYIvɬ3CYE!;:0*] e,Ndcf]#9~xRw~cg{?sb~ @ϒv@%]֟,F0q 00qJ*wb߉ 3xQ$@:Q0RsDI1FTi y9oIuBIZ f20:@: HXXΞEc3]c3R;\__󟍌֬*n.ƍT6x1P̭re#-Cflka"@Ҏ_P/E1/g<}灉UE64AWe(&QǘP4RwydQֻ) ]޿ A$"v?  @ˈU(@,$Ue'NΝ=0ԑ#е؎''xOn׿!?6H! Epmq @YȀKS lx5B(bF,`co1_z~~J_xf@I( >.I@bpD,ajNT-4 @ AT=f~2f?(ڨYlApt)c %asB ZR3(òÿnx|'ĩS{ˆ?>{Ӻ4 `~NٳD)BdL,ۊ e"){8Q`&`FHB#+YV(`1A{["2Ln,fR(֭`Ő3#{q2Z}~hEDvNx.LCC< DT-HBς?]wCϞؿy؅<=i}| N;Oci\Xt +WоzR6-/qȌU&:9|e$y"aSK A#,vmZhQ>*klN75}ZMRV5!eIX}>B!aE8f&fTɜ/m.ljy |1o֠$s-cFxE.UU6)Dx ZYw/}=7 git־./c'׃ `PR" "D WJA{*P ,C9|bIs ! ܩ" n2 >WTH+bX65 A$ gוdÁ"jounS%g eR.eO+\lV h:P@,.%,.-I:~mmŒu6{b)Id0@y DBy2<_ D< ߗ"9<*U@d4HaA!UϬ kAٛ4UGSarևM+~YR  %}^ָ WoQp.w Fv>0Ⓜkd# ϟ~;Q4;T6ɧQ\ŮsΙČcUתڟpnW.[|9?9*=O{<q?$O $dL+tHbF_+4 ,nsb!vmTpsdn}4&능d#Ë(VcFu^j`Hhӱ~s(a|? %9k >g\__ߺnȿ~ni߷'l ׆bT/*j<.U:9FT3{Q% E˦hcBFUq6gH`|$@DFn'$}rI>9[hs^8L37LC/uK ߅虥T1#Y MPB%ga5V(ʁS޽-|2HXSrvKH(@NP 2{,&̈́'mc Oh#q=7M^n_o}}p7BB1Q.L%e PN}pA'2:¤@`E2O[__M'nkU3HŭnCeb%9tK_MZ{q`8a>8l a.A7-U "|One[qѯ- n7IJ0'!J;@=DJm'QHAEF4O8 DA0 eWlb=d\`?"*J6m C w@*RBH'AEJ?|ЃRzAJ/e`h 1@ H[F]!v+.=~pf!K|LW }Wbu^TIEvˀiٮ@^ >8]P(=PMA#t[;jx/2"-7j{n:3A~fdпIcDADz!|ןp! D$aIaB+D! =|#D  !d:yH<~PP< 0ҌBCL!OF@J^u@#!A)/>R^'`r4 a~x>2ec3 5g,# XAl $DH()0 ߬])@&0фc! 144DH_6" Ah$E}Tb@pC21p f0Lư'4X V[2 k@@`DABSgq `h@"%!Pb$@ {:/Ta_R`VH7 &Sv/gh ^}="ͩyHd2#?L{CK q$"Q?Eb8))*x>^)9] +*CN 8/H Ǯ 2 I@R(Br{\gw+0hywy 11_e)ioʝFYpZIaY?Iծ& }857B}˥H"eO+D۽>s`u4Bp6p{-N)C%Fr^Cyf4.TTjW$[(ÕM1 `jJ}J^mzzK)lhJɊX=k vʹ0Fs# ӵAب$c8`?},XX>g%|B(I7C,f']'Ìby)L_((_ύK\]bH+ Q^kd+K3RH+sу8;l u/x nM& J_IENDB`linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/index.theme000066400000000000000000000002061323636106000242460ustar00rootroot00000000000000[Icon Theme] Name=lisp Directories=16, scalable [16] Size=16 Type=fixed [512] Size=512 Type=fixed [scalable] Size=96 Type=scalable linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/000077500000000000000000000000001323636106000236635ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/auto-follow.svg000066400000000000000000000060501323636106000266550ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/auto-next.svg000066400000000000000000000042421323636106000263320ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/fadein-generic.svg000066400000000000000000000155221323636106000272510ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/fadeout-generic.svg000066400000000000000000000155131323636106000274520ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/led-error.svg000066400000000000000000000077641323636106000263150ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/led-off.svg000066400000000000000000000077541323636106000257350ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/led-pause.svg000066400000000000000000000077701323636106000262760ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/led-running.svg000066400000000000000000000077651323636106000266450ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/lisp/scalable/mixer-handle.svg000066400000000000000000000164311323636106000267660ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/000077500000000000000000000000001323636106000223065ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/index.theme000066400000000000000000000015701323636106000244440ustar00rootroot00000000000000[Icon Theme] Name=Numix Comment=Icon theme from Numix Project Inherits=lisp,hicolor Directories=scalable/actions,scalable/apps,scalable/categories,scalable/devices,scalable/emblems,scalable/mimetypes,scalable/places,scalable/status [scalable/actions] Size=16 MinSize=16 MaxSize=256 Context=Actions Type=Scalable [scalable/apps] Size=16 MinSize=16 MaxSize=256 Context=Applications Type=Scalable [scalable/categories] Size=16 MinSize=16 MaxSize=256 Context=Categories Type=Scalable [scalable/devices] Size=16 MinSize=16 MaxSize=256 Context=Devices Type=Scalable [scalable/emblems] Size=16 MinSize=16 MaxSize=256 Context=Emblems Type=Scalable [scalable/mimetypes] Size=16 MinSize=16 MaxSize=256 Context=MimeTypes Type=Scalable [scalable/places] Size=16 MinSize=16 MaxSize=256 Context=Places Type=Scalable [scalable/status] Size=16 MinSize=16 MaxSize=256 Context=Status Type=Scalable linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/000077500000000000000000000000001323636106000240545ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/000077500000000000000000000000001323636106000255145ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/action-unavailable.svg000066400000000000000000000012111323636106000317660ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/bookmark-add.svg000066400000000000000000000004431323636106000305710ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/bookmark-new.svg000066400000000000000000000013231323636106000306300ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/browser-download.svg000066400000000000000000000006441323636106000315310ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/call-end.svg000066400000000000000000000021051323636106000277120ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/call-start.svg000066400000000000000000000016631323636106000303110ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/contact-new.svg000066400000000000000000000022401323636106000304550ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/content-loading.svg000066400000000000000000000006701323636106000313250ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-export.svg000066400000000000000000000007361323636106000314000ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-import.svg000066400000000000000000000010651323636106000313650ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-new.svg000066400000000000000000000003741323636106000306460ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-open-recent.svg000066400000000000000000000021561323636106000322740ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-open.svg000066400000000000000000000016111323636106000310110ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-page-setup.svg000066400000000000000000000012121323636106000321170ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-properties.svg000066400000000000000000000021031323636106000322410ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-save-as.svg000066400000000000000000000005421323636106000314110ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-save.svg000066400000000000000000000004531323636106000310110ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/document-send.svg000066400000000000000000000022171323636106000310040ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/drive-multidisk.svg000066400000000000000000000031061323636106000313510ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-clear-all.svg000066400000000000000000000013361323636106000310170ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-clear-rtl.svg000066400000000000000000000016161323636106000310510ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-clear.svg000066400000000000000000000016111323636106000302450ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-copy.svg000066400000000000000000000005121323636106000301300ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-cut.svg000066400000000000000000000024511323636106000277550ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-delete.svg000066400000000000000000000017201323636106000304220ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-find.svg000066400000000000000000000020021323636106000300720ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-paste.svg000066400000000000000000000015351323636106000303000ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-redo-rtl.svg000066400000000000000000000012701323636106000307100ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-redo.svg000066400000000000000000000012671323636106000301170ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-select-all.svg000066400000000000000000000012631323636106000312070ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-select.svg000066400000000000000000000014161323636106000304410ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-undo-rtl.svg000066400000000000000000000012671323636106000307320ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/edit-undo.svg000066400000000000000000000012701323636106000301250ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/find-location.svg000066400000000000000000000025671323636106000307750ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-indent-less-rtl.svg000066400000000000000000000012631323636106000325510ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-indent-less.svg000066400000000000000000000011431323636106000317470ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-indent-more-rtl.svg000066400000000000000000000012641323636106000325460ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-indent-more.svg000066400000000000000000000011251323636106000317430ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-justify-center.svg000066400000000000000000000004361323636106000325010ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-justify-fill.svg000066400000000000000000000004341323636106000321450ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-justify-left.svg000066400000000000000000000004361323636106000321530ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-justify-right.svg000066400000000000000000000004361323636106000323360ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-text-bold.svg000066400000000000000000000022641323636106000314310ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-text-direction-ltr.svg000066400000000000000000000021621323636106000332650ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-text-direction-rtl.svg000066400000000000000000000021601323636106000332630ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-text-italic.svg000066400000000000000000000012421323636106000317510ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-text-larger.svg000066400000000000000000000005611323636106000317630ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-text-smaller.svg000066400000000000000000000007511323636106000321470ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-text-strikethrough.svg000066400000000000000000000010501323636106000334030ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/format-text-underline.svg000066400000000000000000000013311323636106000324700ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-bottom.svg000066400000000000000000000012171323636106000301450ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-down.svg000066400000000000000000000011341323636106000276060ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-first-rtl.svg000066400000000000000000000011171323636106000305660ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-first.svg000066400000000000000000000011541323636106000277700ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-home.svg000066400000000000000000000014561323636106000275760ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-jump-rtl.svg000066400000000000000000000013031323636106000304070ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-jump.svg000066400000000000000000000013061323636106000276130ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-last-rtl.svg000066400000000000000000000011561323636106000304050ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-last.svg000066400000000000000000000011171323636106000276030ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-next-rtl.svg000066400000000000000000000010171323636106000304140ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-next.svg000066400000000000000000000011341323636106000276150ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-previous-rtl.svg000066400000000000000000000010141323636106000313070ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-previous.svg000066400000000000000000000011341323636106000305130ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-top.svg000066400000000000000000000012421323636106000274410ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/go-up.svg000066400000000000000000000011341323636106000272630ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/help-info.svg000066400000000000000000000020471323636106000301210ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/image-crop.svg000066400000000000000000000004761323636106000302670ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/image-red-eye.svg000066400000000000000000000026501323636106000306520ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/insert-image.svg000066400000000000000000000013461323636106000306250ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/insert-link.svg000066400000000000000000000027731323636106000305050ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/insert-object.svg000066400000000000000000000012661323636106000310120ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/insert-text.svg000066400000000000000000000021201323636106000305160ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/list-add.svg000066400000000000000000000003471323636106000277420ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/list-remove-all.svg000066400000000000000000000010551323636106000312520ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/list-remove.svg000066400000000000000000000003021323636106000304760ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/mail-forward.svg000066400000000000000000000007451323636106000306270ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/mail-mark-important.svg000066400000000000000000000022411323636106000321210ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/mail-reply-sender.svg000066400000000000000000000007351323636106000315730ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/mail-send-receive.svg000066400000000000000000000017561323636106000315370ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/mail-send.svg000066400000000000000000000027031323636106000301100ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/mark-location.svg000066400000000000000000000007511323636106000310000ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-eject.svg000066400000000000000000000010141323636106000304000ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-eq.svg000066400000000000000000000015411323636106000277200ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-playback-pause.svg000066400000000000000000000003711323636106000322140ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-playback-start-rtl.svg000066400000000000000000000005021323636106000330270ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-playback-start.svg000066400000000000000000000005031323636106000322310ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-playback-stop.svg000066400000000000000000000002351323636106000320630ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-record.svg000066400000000000000000000006411323636106000305710ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-seek-backward-rtl.svg000066400000000000000000000013041323636106000326120ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-seek-backward.svg000066400000000000000000000013021323636106000320110ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-seek-forward-rtl.svg000066400000000000000000000013021323636106000324760ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-seek-forward.svg000066400000000000000000000013041323636106000317010ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-skip-backward-rtl.svg000066400000000000000000000014061323636106000326340ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-skip-backward.svg000066400000000000000000000014141323636106000320340ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-skip-forward-rtl.svg000066400000000000000000000014521323636106000325230ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/media-skip-forward.svg000066400000000000000000000015021323636106000317200ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/object-flip-horizontal.svg000066400000000000000000000015161323636106000326250ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/object-flip-vertical.svg000066400000000000000000000015631323636106000322470ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/object-inverse.svg000066400000000000000000000034211323636106000311540ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/object-rotate-left.svg000066400000000000000000000011731323636106000317310ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/object-rotate-right.svg000066400000000000000000000011661323636106000321160ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/object-select.svg000066400000000000000000000004561323636106000307650ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/pane-hide.svg000066400000000000000000000003631323636106000300710ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/pane-show.svg000066400000000000000000000003371323636106000301410ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/process-stop.svg000066400000000000000000000013751323636106000307040ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/send-to.svg000066400000000000000000000010521323636106000276040ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/system-devices.svg000066400000000000000000000026431323636106000312060ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/system-run.svg000066400000000000000000000122421323636106000303640ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/system-shutdown.svg000066400000000000000000000012721323636106000314340ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/tab-new.svg000066400000000000000000000006371323636106000276000ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-calendar-day.svg000066400000000000000000000261771323636106000315460ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-calendar-list.svg000066400000000000000000000247761323636106000317470ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-calendar-month.svg000066400000000000000000000274771323636106000321220ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-calendar-week.svg000066400000000000000000000336721323636106000317220ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-calendar-workweek.svg000066400000000000000000000346361323636106000326260ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-column.svg000066400000000000000000000003501323636106000305000ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-continuous.svg000066400000000000000000000011631323636106000314140ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-coverflow.svg000066400000000000000000000015361323636106000312200ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-dual.svg000066400000000000000000000024101323636106000301270ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-filter.svg000066400000000000000000000005101323636106000304660ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-fullscreen.svg000066400000000000000000000053311323636106000313510ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-grid.svg000066400000000000000000000013361323636106000301350ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-list-compact.svg000066400000000000000000000005741323636106000316120ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-list-images.svg000066400000000000000000000010341323636106000314210ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-list-video.svg000066400000000000000000000003671323636106000312720ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-list.svg000066400000000000000000000007641323636106000301670ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-more.svg000066400000000000000000000005421323636106000301500ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-paged.svg000066400000000000000000000010661323636106000302700ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-refresh.svg000066400000000000000000000013461323636106000306470ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-restore.svg000066400000000000000000000022611323636106000306710ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/view-sidebar.svg000066400000000000000000000005301323636106000306140ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/window-close.svg000066400000000000000000000014111323636106000306440ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/window-maximize.svg000066400000000000000000000004141323636106000313640ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/window-minimize.svg000066400000000000000000000003371323636106000313660ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/window-restore.svg000066400000000000000000000004161323636106000312260ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/zoom-fit-best.svg000066400000000000000000000010071323636106000307320ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/zoom-in.svg000066400000000000000000000007271323636106000276330ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/zoom-original.svg000066400000000000000000000007311323636106000310240ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/actions/zoom-out.svg000066400000000000000000000006631323636106000300330ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/categories/000077500000000000000000000000001323636106000262015ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/categories/bluetooth.svg000066400000000000000000000037211323636106000307320ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/categories/preferences-other.svg000066400000000000000000000060061323636106000323440ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/categories/preferences-system-old.svg000066400000000000000000000015441323636106000333250ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/categories/preferences-system.svg000066400000000000000000000053201323636106000325450ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/categories/system-users.svg000066400000000000000000000032701323636106000314070ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/000077500000000000000000000000001323636106000254765ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/audio-headphones.svg000066400000000000000000000013211323636106000314310ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/audio-headset.svg000066400000000000000000000022751323636106000307410ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/audio-speakers.svg000066400000000000000000000027741323636106000311450ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/camera-photo.svg000066400000000000000000000040451323636106000306010ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/camera-web.svg000066400000000000000000000013341323636106000302230ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/drive-harddisk.svg000066400000000000000000000063431323636106000311250ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/drive-optical.svg000066400000000000000000000011051323636106000307560ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/drive-removable-media-usb-1.svg000066400000000000000000000102511323636106000333030ustar00rootroot00000000000000 image/svg+xml drive-removable-media-usb-old.svg000066400000000000000000000013101323636106000336360ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/drive-removable-media-usb.svg000066400000000000000000000043031323636106000331460ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/headphones.svg000066400000000000000000000013211323636106000303320ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/input-gaming.svg000066400000000000000000000044501323636106000306210ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/input-keyboard-1.svg000066400000000000000000000043661323636106000313230ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/input-keyboard-old.svg000066400000000000000000000012231323636106000317260ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/input-keyboard.svg000066400000000000000000000040271323636106000311570ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/input-mouse.svg000066400000000000000000000007271323636106000305120ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/media-floppy.svg000066400000000000000000000020021323636106000305770ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/media-memory-sd.svg000066400000000000000000000043621323636106000312150ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/media-optical-cd-audio.svg000066400000000000000000000044601323636106000324160ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/media-optical.svg000066400000000000000000000013271323636106000307320ustar00rootroot00000000000000 multimedia-player-apple-ipod-touch.svg000066400000000000000000000040651323636106000347410ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/multimedia-player.svg000066400000000000000000000017361323636106000316520ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/phone.svg000066400000000000000000000067521323636106000273420ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/printer.svg000066400000000000000000000015401323636106000277020ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/scanner.svg000066400000000000000000000020551323636106000276520ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/devices/video-display.svg000066400000000000000000000040321323636106000307670ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/000077500000000000000000000000001323636106000255005ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-default.svg000066400000000000000000000004761323636106000311130ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-documents.svg000066400000000000000000000010761323636106000314650ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-favorite.svg000066400000000000000000000007611323636106000313030ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-important.svg000066400000000000000000000006171323636106000315010ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-music.svg000066400000000000000000000014151323636106000306010ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-ok.svg000066400000000000000000000012001323636106000300620ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-photos.svg000066400000000000000000000047011323636106000307760ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-shared.svg000066400000000000000000000015041323636106000307260ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-synchronizing.svg000066400000000000000000000021041323636106000323610ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-system.svg000066400000000000000000000004271323636106000310070ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/emblems/emblem-videos.svg000066400000000000000000000007301323636106000307510ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/000077500000000000000000000000001323636106000253235ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/folder-documents.svg000066400000000000000000000047301323636106000313220ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/folder-download.svg000066400000000000000000000100061323636106000311210ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/folder-images.svg000066400000000000000000000063251323636106000305700ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/folder-music.svg000066400000000000000000000070311323636106000304360ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/folder-pictures.svg000066400000000000000000000063251323636106000311610ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/folder-templates.svg000066400000000000000000000014221323636106000313120ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/folder-videos.svg000066400000000000000000000041541323636106000306120ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/folder.svg000066400000000000000000000037331323636106000273250ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/network-server.svg000066400000000000000000000053501323636106000310440ustar00rootroot00000000000000 image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/network-workgroup.svg000066400000000000000000000054771323636106000316070ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/user-desktop.svg000066400000000000000000000045211323636106000304730ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/user-home.svg000066400000000000000000000043051323636106000277520ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/user-images.svg000066400000000000000000000063251323636106000302730ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/user-pictures.svg000066400000000000000000000063251323636106000306640ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/user-trash-full.svg000066400000000000000000000053051323636106000311040ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/places/user-trash.svg000066400000000000000000000051541323636106000301460ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/000077500000000000000000000000001323636106000253775ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/audio-input-microphone.svg000066400000000000000000000011351323636106000325170ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/audio-volume-high.svg000066400000000000000000000027741323636106000314550ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/audio-volume-low.svg000066400000000000000000000023331323636106000313260ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/audio-volume-medium.svg000066400000000000000000000023151323636106000320050ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/audio-volume-muted.svg000066400000000000000000000010261323636106000316410ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/avatar-default-old.svg000066400000000000000000000032711323636106000315770ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/avatar-default.svg000066400000000000000000000057161323636106000310310ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-caution-charging.svg000066400000000000000000000035121323636106000330130ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-caution.svg000066400000000000000000000032501323636106000312320ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-empty-charging.svg000066400000000000000000000031621323636106000325100ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-empty.svg000066400000000000000000000030701323636106000307260ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-full-charged.svg000066400000000000000000000032541323636106000321310ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-full-charging.svg000066400000000000000000000003501323636106000323100ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-full.svg000066400000000000000000000002411323636106000305270ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-good-charging.svg000066400000000000000000000005601323636106000323010ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-good.svg000066400000000000000000000003431323636106000305200ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-low-charging.svg000066400000000000000000000005351323636106000321540ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-low.svg000066400000000000000000000003421323636106000303700ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/battery-missing.svg000066400000000000000000000056151323636106000312500ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/bluetooth-active.svg000066400000000000000000000010761323636106000314020ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/bluetooth-disabled.svg000066400000000000000000000011111323636106000316640ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/changes-prevent.svg000066400000000000000000000012611323636106000312110ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/changes-secure.svg000066400000000000000000000012611323636106000310140ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/dialog-error.svg000066400000000000000000000005051323636106000305060ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/dialog-information.svg000066400000000000000000000005331323636106000317030ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/dialog-password.svg000066400000000000000000000017611323636106000312240ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/dialog-question.svg000066400000000000000000000005431323636106000312260ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/dialog-warning.svg000066400000000000000000000005401323636106000310210ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/display-brightness.svg000066400000000000000000000025211323636106000317330ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/indicator-cpufreq-100.svg000066400000000000000000000064651323636106000320500ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/indicator-cpufreq-25.svg000066400000000000000000000066711323636106000317750ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/indicator-cpufreq-50.svg000066400000000000000000000066631323636106000317740ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/indicator-cpufreq-75.svg000066400000000000000000000066641323636106000320040ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/indicator-cpufreq.svg000066400000000000000000000064651323636106000315520ustar00rootroot00000000000000 image/svg+xml linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/network-wired.svg000066400000000000000000000003371323636106000307240ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/network-wireless-acquiring.svg000066400000000000000000000027001323636106000334230ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/network-wireless-connected.svg000066400000000000000000000024671323636106000334150ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/network-wireless-encrypted.svg000066400000000000000000000007411323636106000334410ustar00rootroot00000000000000 network-wireless-signal-excellent.svg000066400000000000000000000024671323636106000346320ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/network-wireless-signal-good.svg000066400000000000000000000023571323636106000336540ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/network-wireless-signal-none.svg000066400000000000000000000024501323636106000336550ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/network-wireless-signal-ok.svg000066400000000000000000000024021323636106000333240ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/network-wireless-signal-weak.svg000066400000000000000000000024251323636106000336470ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/user-available.svg000066400000000000000000000007701323636106000310200ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/user-away.svg000066400000000000000000000014011323636106000300310ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/user-busy.svg000066400000000000000000000013341323636106000300570ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/user-idle.svg000066400000000000000000000013361323636106000300140ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/user-invisible.svg000066400000000000000000000010031323636106000310520ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/user-offline.svg000066400000000000000000000015701323636106000305210ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/user-status.svg000066400000000000000000000016321323636106000304210ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-clear-night.svg000066400000000000000000000007641323636106000317610ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-clear.svg000066400000000000000000000033751323636106000306530ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-few-clouds-night.svg000066400000000000000000000130701323636106000327350ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-few-clouds.svg000066400000000000000000000141501323636106000316260ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-fog.svg000066400000000000000000000016251323636106000303340ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-overcast.svg000066400000000000000000000142041323636106000314040ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-severe-alert.svg000066400000000000000000000112611323636106000321540ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-showers-scattered.svg000066400000000000000000000124471323636106000332330ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-showers.svg000066400000000000000000000140271323636106000312530ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-snow.svg000066400000000000000000000340771323636106000305560ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/weather-storm.svg000066400000000000000000000133721323636106000307270ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/xn-emblem-system.svg000066400000000000000000000007341323636106000313320ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/icons/numix/scalable/status/xn-playlist.svg000066400000000000000000000020041323636106000304000ustar00rootroot00000000000000 linux-show-player-0.5.1/lisp/ui/styles/styles.py000066400000000000000000000051521323636106000217330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . import sys import os.path from collections import namedtuple from PyQt5.QtGui import QPalette, QColor from PyQt5.QtWidgets import QStyleFactory, qApp try: from os import scandir except ImportError: from scandir import scandir StylesPath = os.path.abspath(os.path.dirname(__file__)) IconsThemePaths = [os.path.join(StylesPath, 'icons')] LiSPStyles = {} Style = namedtuple('Style', ['path', 'has_qss', 'has_py']) def styles(): """Returns all available styles (Qt and installed).""" return QStyleFactory.keys() + list(LiSPStyles.keys()) def scan_styles(): """Scan for "installed" styles.""" LiSPStyles.clear() for entry in scandir(StylesPath): if entry.is_dir(): has_qss = os.path.exists(os.path.join(entry.path, 'style.qss')) has_py = os.path.exists(os.path.join(entry.path, 'style.py')) if has_qss or has_py: LiSPStyles[entry.name.title()] = Style( path=entry.path, has_qss=has_qss, has_py=has_py ) def __load_qss_style(path): """Read and load the stylesheet file.""" with open(path, mode='r', encoding='utf-8') as f: style = f.read() qApp.setStyleSheet(style) def __load_py_style(module): """Load the python style module.""" __import__(module, globals(), locals(), ['*']) def apply_style(name): """Load a style given its name.""" style = LiSPStyles.get(name.title()) if isinstance(style, Style): if style.has_py: module = __package__ + '.' + os.path.basename(style.path) + '.style' __load_py_style(module) if style.has_qss: __load_qss_style(os.path.join(style.path, 'style.qss')) else: qApp.setStyleSheet('') qApp.setStyle(QStyleFactory.create(name)) # Search for styles scan_styles() linux-show-player-0.5.1/lisp/ui/ui_utils.py000066400000000000000000000057471323636106000207340ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from itertools import chain from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QApplication from lisp.core.decorators import memoize @memoize def load_icon(icon_name): """Return a QIcon from the icon theme. The loaded icons are cached. .. warning: QIcons should be loaded only from the QT main loop. """ return QIcon.fromTheme(icon_name) @memoize def pixmap_from_icon(icon_name, size): """Return a QPixmap of "size x size" pixels from the icon theme. The returned pixmaps are cached. .. warning: QPixmap should be created only from the QT main loop. """ return load_icon(icon_name).pixmap(size, size) def qfile_filters(extensions, allexts=True, anyfile=True): """Create a filter-string for a FileChooser. The result will be something like this: ' (*.ext1 *.ext2);; (*.ext1)' :param extensions: The extensions as a dictionary {group: [extensions]} :type extensions: dict :param allexts: Add a group composed by all the given groups :type allexts: bool :param anyfile: Add the "Any File" group :type anyfile: bool :return: A QFileDialog filter-string :rtype: str """ filters = [] for key in extensions: filters.append(key.title() + ' (' + ' *.'.join(extensions[key]) + ')') filters.sort() if allexts: filters.insert(0, 'All supported (' + ' *.'.join(chain(*extensions.values())) + ')') if anyfile: filters.append('Any file (*)') return ';;'.join(filters) def translate(context, text, disambiguation=None, n=-1): return QApplication.translate(context, text, disambiguation, n) def translate_many(context, texts): """Return a translate iterator.""" for item in texts: yield translate(context, item) def tr_sorted(context, iterable, key=None, reverse=False): """Return a new sorted list from the items in iterable. The sorting is done using translated versions of the iterable values. """ if key is not None: def tr_key(item): translate(context, key(item)) else: def tr_key(item): translate(context, item) return sorted(iterable, key=tr_key, reverse=reverse)\ linux-show-player-0.5.1/lisp/ui/widgets/000077500000000000000000000000001323636106000201565ustar00rootroot00000000000000linux-show-player-0.5.1/lisp/ui/widgets/__init__.py000066400000000000000000000006741323636106000222760ustar00rootroot00000000000000from .cueaction_combobox import CueActionComboBox from .fade_combobox import FadeComboBox from .qclicklabel import QClickLabel from .qclickslider import QClickSlider from .qcolorbutton import QColorButton from .qdbmeter import QDbMeter from .qmessagebox import QDetailedMessageBox from .qmutebutton import QMuteButton from .qsteptimeedit import QStepTimeEdit from .qstyledslider import QStyledSlider from .qvertiacallabel import QVerticalLabel linux-show-player-0.5.1/lisp/ui/widgets/cueaction_combobox.py000066400000000000000000000044071323636106000243770ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from enum import Enum from PyQt5.QtCore import QT_TRANSLATE_NOOP from PyQt5.QtWidgets import QComboBox from lisp.cues.cue import Cue, CueAction from lisp.ui.ui_utils import translate QT_TRANSLATE_NOOP('CueAction', 'Default') QT_TRANSLATE_NOOP('CueAction', 'FadeInStart') QT_TRANSLATE_NOOP('CueAction', 'FadeOutStop') QT_TRANSLATE_NOOP('CueAction', 'FadeOutPause') QT_TRANSLATE_NOOP('CueAction', 'Start') QT_TRANSLATE_NOOP('CueAction', 'Stop') QT_TRANSLATE_NOOP('CueAction', 'Pause') class CueActionComboBox(QComboBox): class Mode(Enum): Action = 0 Value = 1 Name = 2 def __init__(self, cue_class, mode=Mode.Action, **kwargs): super().__init__(**kwargs) self.mode = mode if issubclass(cue_class, Cue): for action in cue_class.CueActions: if mode is CueActionComboBox.Mode.Value: value = action.value elif mode is CueActionComboBox.Mode.Name: value = action.name else: value = action self.addItem(translate('CueAction', action.name), value) def currentAction(self): return self.currentData() def setCurrentAction(self, action): if self.mode is CueActionComboBox.Mode.Value: name = CueAction(action).name elif self.mode is CueActionComboBox.Mode.Action: name = action.name else: name = action self.setCurrentText(translate('CueAction', name)) linux-show-player-0.5.1/lisp/ui/widgets/fade_combobox.py000066400000000000000000000041171323636106000233220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from enum import Enum from PyQt5.QtCore import QT_TRANSLATE_NOOP from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QComboBox, QStyledItemDelegate from lisp.ui.ui_utils import translate QT_TRANSLATE_NOOP('Fade', 'Linear') QT_TRANSLATE_NOOP('Fade', 'Quadratic') QT_TRANSLATE_NOOP('Fade', 'Quadratic2') class FadeComboBox(QComboBox): FadeOutIcons = { 'Linear': QIcon.fromTheme('fadeout-linear'), 'Quadratic': QIcon.fromTheme('fadeout-quadratic'), 'Quadratic2': QIcon.fromTheme('fadeout-quadratic2') } FadeInIcons = { 'Linear': QIcon.fromTheme('fadein-linear'), 'Quadratic': QIcon.fromTheme('fadein-quadratic'), 'Quadratic2': QIcon.fromTheme('fadein-quadratic2') } class Mode(Enum): FadeIn = 0 FadeOut = 1 def __init__(self, *args, mode=Mode.FadeOut, **kwargs): super().__init__(*args, **kwargs) self.setItemDelegate(QStyledItemDelegate()) if mode == self.Mode.FadeIn: items = self.FadeInIcons else: items = self.FadeOutIcons for key in sorted(items.keys()): self.addItem(items[key], translate('Fade', key), key) def setCurrentType(self, type): self.setCurrentText(translate('Fade', type)) def currentType(self): return self.currentData() linux-show-player-0.5.1/lisp/ui/widgets/fade_edit.py000066400000000000000000000043451323636106000224420ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QDoubleSpinBox, QGridLayout, QLabel, QWidget from lisp.ui.ui_utils import translate from lisp.ui.widgets import FadeComboBox class FadeEdit(QWidget): def __init__(self, *args, mode=FadeComboBox.Mode.FadeOut, **kwargs): super().__init__(*args, **kwargs) self.setLayout(QGridLayout()) self.fadeDurationSpin = QDoubleSpinBox(self) self.fadeDurationSpin.setRange(0, 3600) self.layout().addWidget(self.fadeDurationSpin, 0, 0) self.fadeDurationLabel = QLabel(self) self.fadeDurationLabel.setAlignment(Qt.AlignCenter) self.layout().addWidget(self.fadeDurationLabel, 0, 1) self.fadeTypeCombo = FadeComboBox(self, mode=mode) self.layout().addWidget(self.fadeTypeCombo, 1, 0) self.fadeTypeLabel = QLabel(self) self.fadeTypeLabel.setAlignment(Qt.AlignCenter) self.layout().addWidget(self.fadeTypeLabel, 1, 1) self.retranslateUi() def retranslateUi(self): self.fadeDurationLabel.setText(translate('FadeEdit', 'Duration (sec)')) self.fadeTypeLabel.setText(translate('FadeEdit', 'Curve')) def duration(self): return self.fadeDurationSpin.value() def setDuration(self, value): self.fadeDurationSpin.setValue(value) def fadeType(self): return self.fadeTypeCombo.currentType() def setFadeType(self, fade_type): self.fadeTypeCombo.setCurrentType(fade_type) linux-show-player-0.5.1/lisp/ui/widgets/qclicklabel.py000066400000000000000000000021271323636106000230000ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import pyqtSignal, QEvent from PyQt5.QtWidgets import QLabel class QClickLabel(QLabel): clicked = pyqtSignal(QEvent) def __init(self, parent): super().__init__(parent) def mouseReleaseEvent(self, e): if(self.contentsRect().contains(e.pos())): self.clicked.emit(e) linux-show-player-0.5.1/lisp/ui/widgets/qclickslider.py000066400000000000000000000040241323636106000232010ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtWidgets import QSlider from PyQt5.QtWidgets import QStyle, QStyleOptionSlider class QClickSlider(QSlider): sliderJumped = pyqtSignal(int) def mousePressEvent(self, e): cr = self._control_rect() if e.button() == Qt.LeftButton and not cr.contains(e.pos()): # Set the value to the minimum value = self.minimum() # zmax is the maximum value starting from zero zmax = self.maximum() - self.minimum() if self.orientation() == Qt.Vertical: # Add the current position multiplied for value/size ratio value += (self.height() - e.y()) * (zmax / self.height()) else: value += e.x() * (zmax / self.width()) if self.value() != value: self.setValue(value) self.sliderJumped.emit(self.value()) e.accept() else: e.ignore() else: super().mousePressEvent(e) def _control_rect(self): opt = QStyleOptionSlider() self.initStyleOption(opt) return self.style().subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle) linux-show-player-0.5.1/lisp/ui/widgets/qcolorbutton.py000066400000000000000000000042201323636106000232610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import pyqtSignal, Qt from PyQt5.QtGui import QColor from PyQt5.QtWidgets import QPushButton, QColorDialog from lisp.ui.ui_utils import translate class QColorButton(QPushButton): """Custom Qt Widget to show a chosen color. Left-clicking the button shows the color-chooser. Right-clicking resets the color to None (no-color). """ colorChanged = pyqtSignal() def __init__(self, *args): super().__init__(*args) self._color = None self.setToolTip(translate('QColorButton', 'Right click to reset')) self.pressed.connect(self.onColorPicker) def setColor(self, color): if self._color != color: self._color = color self.colorChanged.emit() if self._color is not None: self.setStyleSheet( 'QColorButton {{ background-color: {0}; }}'.format(self._color)) else: self.setStyleSheet('') def color(self): return self._color def onColorPicker(self): dlg = QColorDialog() if self._color is not None: dlg.setCurrentColor(QColor(self._color)) if dlg.exec_() == dlg.Accepted: self.setColor(dlg.currentColor().name()) def mousePressEvent(self, e): if e.button() == Qt.RightButton: self.setColor(None) return super(QColorButton, self).mousePressEvent(e)linux-show-player-0.5.1/lisp/ui/widgets/qdbmeter.py000066400000000000000000000111611323636106000223330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtGui import QLinearGradient, QColor, QPainter from PyQt5.QtWidgets import QWidget from lisp.core.configuration import config from lisp.core.decorators import suppress_exceptions class QDbMeter(QWidget): DB_MIN = int(config["DbMeter"]["dbMin"]) DB_MAX = int(config["DbMeter"]["dbMax"]) DB_CLIP = int(config["DbMeter"]["dbClip"]) def __init__(self, parent): super().__init__(parent) db_range = abs(self.DB_MIN - self.DB_MAX) yellow = abs(self.DB_MIN + 20) / db_range # -20 db red = abs(self.DB_MIN) / db_range # 0 db self.grad = QLinearGradient() self.grad.setColorAt(0, QColor(0, 255, 0)) # Green self.grad.setColorAt(yellow, QColor(255, 255, 0)) # Yellow self.grad.setColorAt(red, QColor(255, 0, 0)) # Red self.reset() def reset(self): self.peaks = [self.DB_MIN, self.DB_MIN] self.rmss = [self.DB_MIN, self.DB_MIN] self.decPeak = [self.DB_MIN, self.DB_MIN] self.clipping = {} self.repaint() def plot(self, peaks, rms, decPeak): self.peaks = peaks self.rmss = rms self.decPeak = decPeak self.repaint() @suppress_exceptions def paintEvent(self, e): if not self.visibleRegion().isEmpty(): # Stretch factor mul = (self.height() - 4) mul /= (self.DB_MAX - self.DB_MIN) peaks = [] for n, peak in enumerate(self.peaks): if peak > self.DB_CLIP: self.clipping[n] = True if peak < self.DB_MIN: peak = self.DB_MIN elif peak > self.DB_MAX: peak = self.DB_MAX peaks.append(round((peak - self.DB_MIN) * mul)) rmss = [] for n, rms in enumerate(self.rmss): if rms < self.DB_MIN: rms = self.DB_MIN elif rms > self.DB_MAX: rms = self.DB_MAX rmss.append(round((rms - self.DB_MIN) * mul)) dPeaks = [] for dPeak in self.decPeak: if dPeak < self.DB_MIN: dPeak = self.DB_MIN elif dPeak > self.DB_MAX: dPeak = self.DB_MAX dPeaks.append(round((dPeak - self.DB_MIN) * mul)) qp = QPainter() qp.begin(self) qp.setBrush(QColor(0, 0, 0, 0)) xpos = 0 xdim = self.width() / len(peaks) for n, (peak, rms, dPeak) in enumerate(zip(peaks, rmss, dPeaks)): # Maximum "peak-rect" size maxRect = QtCore.QRect(xpos, self.height() - 2, xdim - 2, 2 - self.height()) # Set QLinearGradient start and final-stop position self.grad.setStart(maxRect.topLeft()) self.grad.setFinalStop(maxRect.bottomRight()) # Draw peak (audio peak in dB) rect = QtCore.QRect(xpos, self.height() - 2, xdim - 2, -peak) qp.setOpacity(0.6) qp.fillRect(rect, self.grad) qp.setOpacity(1.0) # Draw rms (in db) rect = QtCore.QRect(xpos, self.height() - 2, xdim - 2, -rms) qp.fillRect(rect, self.grad) # Draw decay peak decRect = QtCore.QRect(xpos, (self.height() - 3) - dPeak, xdim - 2, 2) qp.fillRect(decRect, self.grad) # Draw Borders if self.clipping.get(n, False): qp.setPen(QColor(200, 0, 0)) else: qp.setPen(QColor(100, 100, 100)) qp.drawRect(maxRect) xpos += xdim qp.end() linux-show-player-0.5.1/lisp/ui/widgets/qiconpushbutton.py000066400000000000000000000024701323636106000240000ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2017 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import QSize from PyQt5.QtWidgets import QPushButton class QIconPushButton(QPushButton): """QPushButton that resize dynamically it's icon.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.__icon_margin = 5 def setIconMargin(self, margin): self.__icon_margin = int(margin) def iconMargin(self): return self.__icon_margin def resizeEvent(self, event): size = min(self.width(), self.height()) - 8 self.setIconSize(QSize(size, size)) linux-show-player-0.5.1/lisp/ui/widgets/qmessagebox.py000066400000000000000000000050641323636106000230530ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtWidgets import QMessageBox class QDetailedMessageBox(QMessageBox): """QMessageBox providing statics methods to show detailed-text""" @staticmethod def dcritical(title, text, detailed_text, parent=None): """MessageBox with "Critical" icon""" QDetailedMessageBox.dgeneric(title, text, detailed_text, QMessageBox.Critical, parent) @staticmethod def dwarning(title, text, detailed_text, parent=None): """MessageBox with "Warning" icon""" QDetailedMessageBox.dgeneric(title, text, detailed_text, QMessageBox.Warning, parent) @staticmethod def dinformation(title, text, detailed_text, parent=None): """MessageBox with "Information" icon""" QDetailedMessageBox.dgeneric(title, text, detailed_text, QMessageBox.Information, parent) @staticmethod def dgeneric(title, text, detail_text, icon, parent=None): """Build and show a MessageBox with detailed text""" messageBox = QMessageBox(parent) messageBox.setIcon(icon) messageBox.setWindowTitle(title) messageBox.setText(text) # Show the detail text only if not an empty string if detail_text.strip() != '': messageBox.setDetailedText(detail_text) messageBox.addButton(QMessageBox.Ok) messageBox.setDefaultButton(QMessageBox.Ok) messageBox.exec_() linux-show-player-0.5.1/lisp/ui/widgets/qmutebutton.py000066400000000000000000000026711323636106000231250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QPushButton class QMuteButton(QPushButton): def __init__(self, *args): super().__init__(*args) # Use the button-check behaviors as mute-unmute self.setCheckable(True) self.setChecked(False) # Set the icon self.onToggle() # More explicit names self.isMute = self.isChecked self.setMute = self.setChecked self.toggled.connect(self.onToggle) def onToggle(self): if self.isChecked(): self.setIcon(QIcon.fromTheme('audio-volume-muted')) else: self.setIcon(QIcon.fromTheme('audio-volume-high')) linux-show-player-0.5.1/lisp/ui/widgets/qsteptimeedit.py000066400000000000000000000027411323636106000234150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtWidgets import QTimeEdit class QStepTimeEdit(QTimeEdit): def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self._sections_steps = {} def setSectionStep(self, section, step): if not isinstance(section, QTimeEdit.Section): raise AttributeError('Not a QTimeEdit.Section') if step <= 0: raise AttributeError('Step value must be >= 0') self._sections_steps[section] = step def stepBy(self, value): step = self._sections_steps.get(self.currentSection(), 1) if abs(value) != step: sign = 1 if value >= 0 else -1 value = sign * step super().stepBy(value) linux-show-player-0.5.1/lisp/ui/widgets/qstyledslider.py000066400000000000000000000064061323636106000234260ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor from PyQt5.QtWidgets import QSlider, QStylePainter, QStyleOptionSlider, QStyle class QStyledSlider(QSlider): """QSlider able to paint ticks when using stylesheets.""" def paintEvent(self, event): super().paintEvent(event) painter = QStylePainter(self) painter.setPen(QColor("#a5a294")) option = QStyleOptionSlider() self.initStyleOption(option) tick = 5 handle = self.style().subControlRect(QStyle.CC_Slider, option, QStyle.SC_SliderHandle) # Draw tick marks # Do this manually because they are very badly behaved with stylesheets interval = self.tickInterval() if interval == 0: interval = self.pageStep() slide_range = self.maximum() - self.minimum() if self.orientation() == Qt.Horizontal: no_handle_size = self.width() - handle.width() handle_half_size = handle.width() / 2 else: no_handle_size = self.height() - handle.height() handle_half_size = handle.height() / 2 if self.tickPosition() != QSlider.NoTicks: for i in range(self.minimum(), self.maximum() + 1, interval): y = 0 x = round((i - self.minimum()) / slide_range * no_handle_size + handle_half_size ) - 1 if self.orientation() == Qt.Vertical: x, y = y, x # QSlider.TicksAbove == QSlider.TicksLeft if self.tickPosition() == QSlider.TicksBothSides or self.tickPosition() == QSlider.TicksAbove: if self.orientation() == Qt.Horizontal: y = self.rect().top() painter.drawLine(x, y, x, y + tick) else: x = self.rect().left() painter.drawLine(x, y, x + tick, y) # QSlider.TicksBelow == QSlider.TicksRight if self.tickPosition() == QSlider.TicksBothSides or self.tickPosition() == QSlider.TicksBelow: if self.orientation() == Qt.Horizontal: y = self.rect().bottom() painter.drawLine(x, y, x, y - tick) else: x = self.rect().right() painter.drawLine(x, y, x - tick, y) linux-show-player-0.5.1/lisp/ui/widgets/qvertiacallabel.py000066400000000000000000000025561323636106000236730ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is part of Linux Show Player # # Copyright 2012-2016 Francesco Ceruti # # Linux Show Player is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linux Show Player is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linux Show Player. If not, see . from PyQt5 import QtCore from PyQt5.QtGui import QPainter from PyQt5.QtWidgets import QLabel class QVerticalLabel(QLabel): def paintEvent(self, event): painter = QPainter(self) painter.rotate(-90) region = QtCore.QRect(-self.height(), 0, self.height(), self.width()) hint = painter.drawText(region, self.alignment(), self.text()) painter.end() self.setMaximumWidth(hint.height()) self.setMinimumWidth(0) self.setMaximumHeight(16777215) self.setMinimumHeight(hint.width()) def sizeHint(self): return self.size() linux-show-player-0.5.1/setup.py000066400000000000000000000022471323636106000166430ustar00rootroot00000000000000#!/usr/bin/env python3 import os import re from setuptools import find_packages, setup import lisp def find_files(directory, regex='.*', rel=''): """Search for files that match `regex` in `directory`.""" paths = [] for path, directories, file_names in os.walk(directory): for filename in file_names: file_path = os.path.join(path, filename) if re.match(regex, file_path): paths.append(file_path[len(rel):]) return paths # List the directories with icons to be installed lisp_icons = find_files('lisp/ui/styles/icons', rel='lisp/ui/styles/') # Setup function setup( name='linux-show-player', author=lisp.__author__, author_email=lisp.__email__, version=lisp.__version__, license=lisp.__license__, url=lisp.__email__, description='Cue player for live shows', install_requires=[ 'sortedcontainers', 'mido', 'python-rtmidi', 'JACK-Client', 'scandir;python_version<"3.5"' ], packages=find_packages(), package_data={ '': ['i18n/*.qm', '*.qss', '*.cfg'], 'lisp.ui.styles': lisp_icons, }, scripts=['linux-show-player'] )