urwid-1.3.1/0000775000175000017500000000000012615524621012214 5ustar ianian00000000000000urwid-1.3.1/MANIFEST.in0000664000175000017500000000022512615524560013753 0ustar ianian00000000000000recursive-include examples *.py *.tac recursive-include docs *.rst *.py *.py.xdotool *.png *.html *.sh Makefile include COPYING CHANGELOG README.rst urwid-1.3.1/README.rst0000664000175000017500000000175512615524560013715 0ustar ianian00000000000000.. image:: https://travis-ci.org/wardi/urwid.png?branch=master :alt: build status :target: https://travis-ci.org/wardi/urwid/ `Development version documentation `_ .. content-start Urwid is a console user interface library for Python. It includes many features useful for text console application developers including: - Applications resize quickly and smoothly - Automatic, programmable text alignment and wrapping - Simple markup for setting text attributes within blocks of text - Powerful list box with programmable content for scrolling all widget types - Your choice of event loops: Twisted, Glib, Tornado or select-based loop - Pre-built widgets include edit boxes, buttons, check boxes and radio buttons - Display modules include raw, curses, and experimental LCD and web displays - Support for UTF-8, simple 8-bit and CJK encodings - 256 and 88 color mode support - Compatible with Python 2.6, 2.7, 3.2+ and PyPy Home Page: http://urwid.org/ urwid-1.3.1/PKG-INFO0000664000175000017500000000420312615524621013310 0ustar ianian00000000000000Metadata-Version: 1.1 Name: urwid Version: 1.3.1 Summary: A full-featured console (xterm et al.) user interface library Home-page: http://urwid.org/ Author: Ian Ward Author-email: ian@excess.org License: LGPL Description: Urwid is a console user interface library for Python. It includes many features useful for text console application developers including: - Applications resize quickly and smoothly - Automatic, programmable text alignment and wrapping - Simple markup for setting text attributes within blocks of text - Powerful list box with programmable content for scrolling all widget types - Your choice of event loops: Twisted, Glib, Tornado or select-based loop - Pre-built widgets include edit boxes, buttons, check boxes and radio buttons - Display modules include raw, curses, and experimental LCD and web displays - Support for UTF-8, simple 8-bit and CJK encodings - 256 and 88 color mode support - Compatible with Python 2.6, 2.7, 3.2+ and PyPy Home Page: http://urwid.org/ Keywords: curses ui widget scroll listbox user interface text layout console ncurses Platform: unix-like Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: Console :: Curses Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Operating System :: MacOS :: MacOS X Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Widget Sets Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: PyPy urwid-1.3.1/setup.py0000664000175000017500000000641712615524560013740 0ustar ianian00000000000000#!/usr/bin/python # # Urwid setup.py exports the useful bits # Copyright (C) 2004-2014 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ try: PYTHON3 = not str is bytes except NameError: PYTHON3 = False try: from setuptools import setup, Extension # distribute required for Python 3 have_setuptools = True except ImportError: if PYTHON3: raise from distutils.core import setup, Extension have_setuptools = False import os exec(open(os.path.join("urwid","version.py")).read()) release = __version__ setup_d = { 'name':"urwid", 'version':release, 'author':"Ian Ward", 'author_email':"ian@excess.org", 'ext_modules':[Extension('urwid.str_util', sources=['source/str_util.c'])], 'packages':['urwid', 'urwid.tests'], 'url':"http://urwid.org/", 'license':"LGPL", 'keywords':"curses ui widget scroll listbox user interface text layout console ncurses", 'platforms':"unix-like", 'description': "A full-featured console (xterm et al.) user interface library", 'long_description':open("README.rst").read().split('.. content-start\n',1)[1], 'classifiers':[ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: Console :: Curses", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Operating System :: POSIX", "Operating System :: Unix", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Widget Sets", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: PyPy", ], } if have_setuptools: setup_d['zip_safe'] = False setup_d['test_suite'] = 'urwid.tests' if PYTHON3: setup_d['use_2to3'] = True if __name__ == "__main__": try: setup(**setup_d) except (IOError, SystemExit) as e: import sys if "test" in sys.argv: raise import traceback traceback.print_exc() print("Couldn't build the extension module, trying without it...") del setup_d["ext_modules"] setup(**setup_d) urwid-1.3.1/COPYING0000664000175000017500000006350212615524560013257 0ustar ianian00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! urwid-1.3.1/urwid.egg-info/0000775000175000017500000000000012615524621015040 5ustar ianian00000000000000urwid-1.3.1/urwid.egg-info/top_level.txt0000664000175000017500000000000612615524621017566 0ustar ianian00000000000000urwid urwid-1.3.1/urwid.egg-info/PKG-INFO0000664000175000017500000000420312615524621016134 0ustar ianian00000000000000Metadata-Version: 1.1 Name: urwid Version: 1.3.1 Summary: A full-featured console (xterm et al.) user interface library Home-page: http://urwid.org/ Author: Ian Ward Author-email: ian@excess.org License: LGPL Description: Urwid is a console user interface library for Python. It includes many features useful for text console application developers including: - Applications resize quickly and smoothly - Automatic, programmable text alignment and wrapping - Simple markup for setting text attributes within blocks of text - Powerful list box with programmable content for scrolling all widget types - Your choice of event loops: Twisted, Glib, Tornado or select-based loop - Pre-built widgets include edit boxes, buttons, check boxes and radio buttons - Display modules include raw, curses, and experimental LCD and web displays - Support for UTF-8, simple 8-bit and CJK encodings - 256 and 88 color mode support - Compatible with Python 2.6, 2.7, 3.2+ and PyPy Home Page: http://urwid.org/ Keywords: curses ui widget scroll listbox user interface text layout console ncurses Platform: unix-like Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: Console :: Curses Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Operating System :: MacOS :: MacOS X Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Widget Sets Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: PyPy urwid-1.3.1/urwid.egg-info/dependency_links.txt0000664000175000017500000000000112615524621021106 0ustar ianian00000000000000 urwid-1.3.1/urwid.egg-info/not-zip-safe0000664000175000017500000000000112261121332017253 0ustar ianian00000000000000 urwid-1.3.1/urwid.egg-info/SOURCES.txt0000664000175000017500000001340712615524621016731 0ustar ianian00000000000000COPYING MANIFEST.in README.rst setup.py docs/Makefile docs/changelog.rst docs/conf.py docs/index.rst docs/urwid-logo.png docs/examples/bigtext.py docs/examples/bigtext.py.xdotool docs/examples/bigtext1.png docs/examples/bigtext2.png docs/examples/bigtext3.png docs/examples/browse.py docs/examples/browse.py.xdotool docs/examples/browse1.png docs/examples/browse2.png docs/examples/edit.py docs/examples/edit.py.xdotool docs/examples/edit1.png docs/examples/edit2.png docs/examples/graph.py docs/examples/graph.py.xdotool docs/examples/graph1.png docs/examples/graph2.png docs/examples/index.rst docs/examples/palette_test.py docs/examples/palette_test.py.xdotool docs/examples/palette_test1.png docs/examples/palette_test2.png docs/examples/pop_up.py docs/examples/pop_up.py.xdotool docs/examples/pop_up1.png docs/examples/pop_up2.png docs/examples/real_browse.py docs/examples/real_edit.py docs/examples/subproc.py docs/examples/subproc.py.xdotool docs/examples/subproc1.png docs/examples/subproc2.png docs/examples/subproc2.py docs/examples/tour.py docs/examples/tour.py.xdotool docs/examples/tour1.png docs/examples/tour2.png docs/manual/bright_combinations.py docs/manual/bright_combinations.py.xdotool docs/manual/bright_combinations1.png docs/manual/canvascache.rst docs/manual/displayattributes.rst docs/manual/displaymodules.rst docs/manual/encodings.rst docs/manual/index.rst docs/manual/mainloop.rst docs/manual/overview.rst docs/manual/safe_combinations.py docs/manual/safe_combinations.py.xdotool docs/manual/safe_combinations1.png docs/manual/textlayout.rst docs/manual/userinput.rst docs/manual/wanat.py docs/manual/wanat_multi.py docs/manual/wcur1.py docs/manual/wcur2.py docs/manual/widgets.rst docs/manual/wmod.py docs/manual/wsel.py docs/manual/images/display_modules.png docs/manual/images/introduction.png docs/manual/images/urwid_widgets_1.png docs/manual/images/urwid_widgets_2.png docs/manual/images/widget_layout.png docs/reference/attrspec.rst docs/reference/canvas.rst docs/reference/command_map.rst docs/reference/constants.rst docs/reference/deprecated.rst docs/reference/display_modules.rst docs/reference/exceptions.rst docs/reference/global_settings.rst docs/reference/index.rst docs/reference/list_walkers.rst docs/reference/main_loop.rst docs/reference/meta.rst docs/reference/signals.rst docs/reference/text_layout.rst docs/reference/widget.rst docs/tools/compile_pngs.sh docs/tools/screenshots.sh docs/tools/templates/indexcontent.html docs/tools/templates/indexsidebar.html docs/tools/templates/localtoc.html docs/tutorial/adventure.py docs/tutorial/adventure.py.xdotool docs/tutorial/adventure1.png docs/tutorial/adventure2.png docs/tutorial/adventure3.png docs/tutorial/adventure4.png docs/tutorial/attr.py docs/tutorial/attr.py.xdotool docs/tutorial/attr1.png docs/tutorial/attr2.png docs/tutorial/attr3.png docs/tutorial/attr4.png docs/tutorial/cmenu.py docs/tutorial/cmenu.py.xdotool docs/tutorial/cmenu1.png docs/tutorial/cmenu2.png docs/tutorial/cmenu3.png docs/tutorial/cmenu4.png docs/tutorial/highcolors.py docs/tutorial/highcolors.py.xdotool docs/tutorial/highcolors1.png docs/tutorial/hmenu.py docs/tutorial/hmenu.py.xdotool docs/tutorial/hmenu1.png docs/tutorial/hmenu2.png docs/tutorial/hmenu3.png docs/tutorial/hmenu4.png docs/tutorial/index.rst docs/tutorial/input.py docs/tutorial/input.py.xdotool docs/tutorial/input1.png docs/tutorial/input2.png docs/tutorial/input3.png docs/tutorial/input4.png docs/tutorial/input5.png docs/tutorial/menu25.png docs/tutorial/minimal.py docs/tutorial/minimal.py.xdotool docs/tutorial/minimal1.png docs/tutorial/multiple.py docs/tutorial/multiple.py.xdotool docs/tutorial/multiple1.png docs/tutorial/multiple2.png docs/tutorial/multiple3.png docs/tutorial/multiple4.png docs/tutorial/qa.py docs/tutorial/qa.py.xdotool docs/tutorial/qa1.png docs/tutorial/qa2.png docs/tutorial/qa3.png docs/tutorial/sig.py docs/tutorial/sig.py.xdotool docs/tutorial/sig1.png docs/tutorial/sig2.png docs/tutorial/sig3.png docs/tutorial/sig4.png docs/tutorial/smenu.py docs/tutorial/smenu.py.xdotool docs/tutorial/smenu1.png docs/tutorial/smenu2.png docs/tutorial/smenu3.png docs/tutorial/new/adventure.py docs/tutorial/new/adventure.py.xdotool docs/tutorial/new/adventure1.png docs/tutorial/new/adventure2.png docs/tutorial/new/adventure3.png docs/tutorial/new/adventure4.png docs/tutorial/new/lbscr1.png docs/tutorial/new/minimal1.png examples/asyncio_socket_server.py examples/bigtext.py examples/browse.py examples/calc.py examples/dialog.py examples/edit.py examples/fib.py examples/graph.py examples/input_test.py examples/lcd_cf635.py examples/palette_test.py examples/pop_up.py examples/subproc.py examples/subproc2.py examples/terminal.py examples/tour.py examples/treesample.py examples/twisted_serve_ssh.py examples/twisted_serve_ssh.tac source/str_util.c urwid/__init__.py urwid/canvas.py urwid/command_map.py urwid/compat.py urwid/container.py urwid/curses_display.py urwid/decoration.py urwid/display_common.py urwid/escape.py urwid/font.py urwid/graphics.py urwid/html_fragment.py urwid/lcd_display.py urwid/listbox.py urwid/main_loop.py urwid/monitored_list.py urwid/old_str_util.py urwid/raw_display.py urwid/signals.py urwid/split_repr.py urwid/text_layout.py urwid/treetools.py urwid/util.py urwid/version.py urwid/vterm.py urwid/web_display.py urwid/widget.py urwid/wimp.py urwid.egg-info/PKG-INFO urwid.egg-info/SOURCES.txt urwid.egg-info/dependency_links.txt urwid.egg-info/not-zip-safe urwid.egg-info/top_level.txt urwid/tests/__init__.py urwid/tests/test_canvas.py urwid/tests/test_container.py urwid/tests/test_decoration.py urwid/tests/test_doctests.py urwid/tests/test_event_loops.py urwid/tests/test_graphics.py urwid/tests/test_listbox.py urwid/tests/test_str_util.py urwid/tests/test_text_layout.py urwid/tests/test_util.py urwid/tests/test_vterm.py urwid/tests/test_widget.py urwid/tests/util.pyurwid-1.3.1/source/0000775000175000017500000000000012615524621013514 5ustar ianian00000000000000urwid-1.3.1/source/str_util.c0000664000175000017500000005343112615524560015535 0ustar ianian00000000000000/* Urwid unicode character processing tables Copyright (C) 2006 Rebecca Breu. This file contains rewritten code of utable.py by Ian Ward. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Urwid web site: http://excess.org/urwid/ */ #define PY_SSIZE_T_CLEAN #include #define ENC_UTF8 1 #define ENC_WIDE 2 #define ENC_NARROW 3 #if PY_MAJOR_VERSION >= 3 #define PYTHON3 #endif #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5 #define Py_ssize_t int #define FMT_N "i" #else #define FMT_N "n" #endif static int widths_len = 2*38; static const int widths[] = { 126, 1, 159, 0, 687, 1, 710, 0, 711, 1, 727, 0, 733, 1, 879, 0, 1154, 1, 1161, 0, 4347, 1, 4447, 2, 7467, 1, 7521, 0, 8369, 1, 8426, 0, 9000, 1, 9002, 2, 11021, 1, 12350, 2, 12351, 1, 12438, 2, 12442, 0, 19893, 2, 19967, 1, 55203, 2, 63743, 1, 64106, 2, 65039, 1, 65059, 0, 65131, 2, 65279, 1, 65376, 2, 65500, 1, 65510, 2, 120831, 1, 262141, 2, 1114109, 1 }; static short byte_encoding = ENC_UTF8; static PyObject * to_bool(int val) { if (val) Py_RETURN_TRUE; else Py_RETURN_FALSE; } //====================================================================== static char get_byte_encoding_doc[] = "get_byte_encoding() -> string encoding\n\n\ Get byte encoding ('utf8', 'wide', or 'narrow')."; static PyObject * get_byte_encoding(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "")) return NULL; if (byte_encoding == ENC_UTF8) return Py_BuildValue("s", "utf8"); if (byte_encoding == ENC_WIDE) return Py_BuildValue("s", "wide"); if (byte_encoding == ENC_NARROW) return Py_BuildValue("s", "narrow"); Py_RETURN_NONE; // should never happen } //====================================================================== static char set_byte_encoding_doc[] = "set_byte_encoding(string encoding) -> None\n\n\ Set byte encoding. \n\n\ encoding -- one of 'utf8', 'wide', 'narrow'"; static PyObject * set_byte_encoding(PyObject *self, PyObject *args) { char * enc; if (!PyArg_ParseTuple(args, "s", &enc)) return NULL; if (strcmp(enc, "utf8") == 0) byte_encoding = ENC_UTF8; else if (strcmp(enc, "wide") == 0) byte_encoding = ENC_WIDE; else if (strcmp(enc, "narrow") == 0) byte_encoding = ENC_NARROW; else { // got wrong encoding PyErr_SetString(PyExc_ValueError, "Unknown encoding."); return NULL; } Py_RETURN_NONE; } //====================================================================== static char get_width_doc[] = "get_width(int ord) -> int width\n\n\ Return the screen column width for unicode ordinal ord.\n\n\ ord -- ordinal"; static int Py_GetWidth(long int ord) { int i; if ((ord == 0xe) || (ord == 0xf)) return 0; for (i=0; i= 0) { if ((text[pos]&0xc0) != 0x80) { Py_DecodeOne(text, text_len, pos, subret); ret[0] = subret[0]; ret[1] = pos-1; return; } pos-=1; if (pos == pos-4) //error { ret[0] = '?'; ret[1] = pos - 1; return; } } } static PyObject * decode_one_right(PyObject *self, PyObject *args) { PyObject *py_text; Py_ssize_t pos, text_len; char *text; Py_ssize_t ret[2] = {'?',0}; if (!PyArg_ParseTuple(args, "O" FMT_N, &py_text, &pos)) return NULL; #ifndef PYTHON3 PyString_AsStringAndSize(py_text, &text, &text_len); #else PyBytes_AsStringAndSize(py_text, &text, &text_len); #endif Py_DecodeOneRight((const unsigned char *)text, text_len, pos, ret); return Py_BuildValue("(" FMT_N ", " FMT_N ")", ret[0], ret[1]); } //====================================================================== static char within_double_byte_doc[] = "within_double_byte(strint text, int line_start, int pos) -> int withindb\n\n\ Return whether pos is within a double-byte encoded character.\n\n\ str -- string in question\n\ line_start -- offset of beginning of line (< pos)\n\ pos -- offset in question\n\n\ Return values:\n\ 0 -- not within dbe char, or double_byte_encoding == False\n\ 1 -- pos is on the 1st half of a dbe char\n\ 2 -- pos is on the 2nd half of a dbe char"; static int Py_WithinDoubleByte(const unsigned char *str, Py_ssize_t line_start, Py_ssize_t pos) { Py_ssize_t i; if ((str[pos] >= 0x40) && (str[pos] < 0x7f)) { //might be second half of big5, uhc or gbk encoding if (pos == line_start) return 0; if (str[pos-1] >= 0x81) { if ((Py_WithinDoubleByte(str, line_start, pos-1)) == 1) return 2; else return 0; } } if (str[pos] < 0x80) return 0; for (i=pos-1; i>=line_start; i--) if (str[i] < 0x80) break; if ((pos-i) & 1) return 1; else return 2; } static PyObject * within_double_byte(PyObject *self, PyObject *args) { const unsigned char *str; Py_ssize_t str_len, line_start, pos; Py_ssize_t ret; if (!PyArg_ParseTuple(args, "s#" FMT_N FMT_N, &str, &str_len, &line_start, &pos)) return NULL; if (line_start < 0 || line_start >= str_len) { PyErr_SetString(PyExc_IndexError, "is_wide_char: Argument \"line_start\" is outside of string."); return NULL; } if (pos < 0 || pos >= str_len) { PyErr_SetString(PyExc_IndexError, "is_wide_char: Argument \"pos\" is outside of string."); return NULL; } if (pos < line_start) { PyErr_SetString(PyExc_IndexError, "is_wide_char: Argument \"pos\" is before \"line_start\"."); return NULL; } ret = Py_WithinDoubleByte(str, line_start, pos); return Py_BuildValue(FMT_N, ret); } //====================================================================== char is_wide_char_doc[] = "is_wide_char(string/unicode text, int offs) -> bool iswide\n\n\ Test if the character at offs within text is wide.\n\n\ text -- string or unicode text\n\ offs -- offset"; static int Py_IsWideChar(PyObject *text, Py_ssize_t offs) { const unsigned char *str; Py_UNICODE *ustr; Py_ssize_t ret[2], str_len; if (PyUnicode_Check(text)) //text_py is unicode string { ustr = PyUnicode_AS_UNICODE(text); return (Py_GetWidth((long int)ustr[offs]) == 2); } #ifndef PYTHON3 if (!PyString_Check(text)) { #else if (!PyBytes_Check(text)) { #endif PyErr_SetString(PyExc_TypeError, "is_wide_char: Argument \"text\" is not a string."); return -1; } #ifndef PYTHON3 str = (const unsigned char *)PyString_AsString(text); str_len = (int) PyString_Size(text); #else str = (const unsigned char *)PyBytes_AsString(text); str_len = (int) PyBytes_Size(text); #endif if (byte_encoding == ENC_UTF8) { Py_DecodeOne(str, str_len, offs, ret); return (Py_GetWidth(ret[0]) == 2); } if (byte_encoding == ENC_WIDE) return (Py_WithinDoubleByte(str, offs, offs) == 1); return 0; } static PyObject * is_wide_char(PyObject *self, PyObject *args) { PyObject *text; Py_ssize_t offs; int ret; if (!PyArg_ParseTuple(args, "O" FMT_N, &text, &offs)) return NULL; ret = Py_IsWideChar(text, offs); if ( ret == -1) // error return NULL; return Py_BuildValue("O", to_bool(ret)); } //====================================================================== char move_prev_char_doc[] = "move_prev_char(string/unicode text, int start_offs, int end_offs) -> int pos\n\n\ Return the position of the character before end_offs.\n\n\ text -- string or unicode text\n\ start_offs -- start offset\n\ end_offs -- end offset"; static Py_ssize_t Py_MovePrevChar(PyObject *text, Py_ssize_t start_offs, Py_ssize_t end_offs) { Py_ssize_t position; unsigned char *str; if (PyUnicode_Check(text)) //text_py is unicode string return end_offs-1; else #ifndef PYTHON3 str = (unsigned char *)PyString_AsString(text); #else str = (unsigned char *)PyBytes_AsString(text); #endif if (byte_encoding == ENC_UTF8) //encoding is utf8 { position = end_offs - 1; while ((str[position]&0xc0) == 0x80) position -=1; return position; } else if ((byte_encoding == ENC_WIDE) && (Py_WithinDoubleByte(str, start_offs, end_offs-1) == 2)) return end_offs-2; else return end_offs-1; } static PyObject * move_prev_char(PyObject *self, PyObject *args) { PyObject *text; Py_ssize_t start_offs, end_offs; Py_ssize_t ret; if (!PyArg_ParseTuple(args, "O" FMT_N FMT_N, &text, &start_offs, &end_offs)) return NULL; ret = Py_MovePrevChar(text, start_offs, end_offs); return Py_BuildValue(FMT_N, ret); } //====================================================================== char move_next_char_doc[] = "move_next_char(string/unicode text, int start_offs, int end_offs) -> int pos\n\n\ Return the position of the character after start_offs.\n\n\ text -- string or unicode text\n\ start_offs -- start offset\n\ end_offs -- end offset"; static Py_ssize_t Py_MoveNextChar(PyObject *text, Py_ssize_t start_offs, Py_ssize_t end_offs) { Py_ssize_t position; unsigned char * str; if (PyUnicode_Check(text)) //text_py is unicode string return start_offs+1; else #ifndef PYTHON3 str = (unsigned char *)PyString_AsString(text); #else str = (unsigned char *)PyBytes_AsString(text); #endif if (byte_encoding == ENC_UTF8) //encoding is utf8 { position = start_offs + 1; while ((position < end_offs) && ((str[position]&0xc0) == 0x80)) position +=1; return position; } else if ((byte_encoding == ENC_WIDE) && (Py_WithinDoubleByte(str, start_offs, start_offs) == 1)) return start_offs+2; else return start_offs+1; } static PyObject * move_next_char(PyObject *self, PyObject *args) { PyObject *text; Py_ssize_t start_offs, end_offs; Py_ssize_t ret; if (!PyArg_ParseTuple(args, "O" FMT_N FMT_N, &text, &start_offs, &end_offs)) return NULL; ret = Py_MoveNextChar(text, start_offs, end_offs); return Py_BuildValue(FMT_N, ret); } //====================================================================== char calc_width_doc[] = "calc_width(string/unicode text, int start_off, int end_offs) -> int width\n\n\ Return the screen column width of text between start_offs and end_offs.\n\n\ text -- string or unicode text\n\ start_offs -- start offset\n\ end_offs -- end offset"; static Py_ssize_t Py_CalcWidth(PyObject *text, Py_ssize_t start_offs, Py_ssize_t end_offs) { unsigned char * str; Py_ssize_t i, ret[2], str_len; int screencols; Py_UNICODE *ustr; if (PyUnicode_Check(text)) //text_py is unicode string { ustr = PyUnicode_AS_UNICODE(text); screencols = 0; for(i=start_offs; i pref_col) { ret[0] = i; ret[1] = screencols; return 0; } screencols += width; } ret[0] = i; ret[1] = screencols; return 0; } #ifndef PYTHON3 if (!PyString_Check(text)) #else if (!PyBytes_Check(text)) #endif { PyErr_SetString(PyExc_TypeError, "Neither unicode nor string."); return -1; } #ifndef PYTHON3 str = (unsigned char *)PyString_AsString(text); str_len = (int) PyString_Size(text); #else str = (unsigned char *)PyBytes_AsString(text); str_len = PyBytes_Size(text); #endif if (byte_encoding == ENC_UTF8) { i = start_offs; screencols = 0; while (i pref_col) { ret[0] = i; ret[1] = screencols; return 0; } i = dummy[1]; screencols += width; } ret[0] = i; ret[1] = screencols; return 0; } // "wide" and "narrow" i = start_offs + pref_col; if (i>= end_offs) { ret[0] = end_offs; ret[1] = end_offs - start_offs; return 0; } if (byte_encoding == ENC_WIDE) if (Py_WithinDoubleByte(str, start_offs, i)==2) i -= 1; ret[0] = i; ret[1] = i - start_offs; return 0; } static PyObject * calc_text_pos(PyObject *self, PyObject *args) { PyObject *text; Py_ssize_t start_offs, end_offs, ret[2]; int pref_col, err; if (!PyArg_ParseTuple(args, "O" FMT_N FMT_N "i", &text, &start_offs, &end_offs, &pref_col)) return NULL; err = Py_CalcTextPos(text, start_offs, end_offs, pref_col, ret); if (err==-1) //an error occured return NULL; return Py_BuildValue("(" FMT_N FMT_N ")", ret[0], ret[1]); } //====================================================================== static PyMethodDef Str_UtilMethods[] = { {"get_byte_encoding", get_byte_encoding, METH_VARARGS, get_byte_encoding_doc}, {"set_byte_encoding", set_byte_encoding, METH_VARARGS, set_byte_encoding_doc}, {"get_width", get_width, METH_VARARGS, get_width_doc}, {"decode_one", decode_one, METH_VARARGS, decode_one_doc}, {"decode_one_right", decode_one_right, METH_VARARGS, decode_one_right_doc}, {"within_double_byte", within_double_byte, METH_VARARGS, within_double_byte_doc}, {"is_wide_char", is_wide_char, METH_VARARGS, is_wide_char_doc}, {"move_prev_char", move_prev_char, METH_VARARGS, move_prev_char_doc}, {"move_next_char", move_next_char, METH_VARARGS, move_next_char_doc}, {"calc_width", calc_width, METH_VARARGS, calc_width_doc}, {"calc_text_pos", calc_text_pos, METH_VARARGS, calc_text_pos_doc}, {NULL, NULL, 0, NULL} // Sentinel }; #ifndef PYTHON3 PyMODINIT_FUNC initstr_util(void) { Py_InitModule("str_util", Str_UtilMethods); } int main(int argc, char *argv[]) { //Pass argv[0] to the Python interpreter: Py_SetProgramName(argv[0]); //Initialize the Python interpreter. Py_Initialize(); //Add a static module: initstr_util(); return 0; } #else static struct PyModuleDef Str_UtilModule = { PyModuleDef_HEAD_INIT, "str_util", NULL, -1, Str_UtilMethods }; PyMODINIT_FUNC PyInit_str_util(void) { return PyModule_Create(&Str_UtilModule); } #endif urwid-1.3.1/docs/0000775000175000017500000000000012615524621013144 5ustar ianian00000000000000urwid-1.3.1/docs/tutorial/0000775000175000017500000000000012615524621015007 5ustar ianian00000000000000urwid-1.3.1/docs/tutorial/qa.py.xdotool0000664000175000017500000000020512615524560017450 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 21 7 type --window $RXVTWINDOWID 'Arthur, King of the Britons' key --window $RXVTWINDOWID Return urwid-1.3.1/docs/tutorial/smenu2.png0000664000175000017500000000173712615524560016740 0ustar ianian00000000000000‰PNG  IHDRØ¥÷l.bKGDÿ‡Ì¿ pHYs  ÒÝ~üƒIDATxÚíár„ „óþï™÷èŒs* •d»öG9m=w¾%“kEô÷õÕ~lðYP$Fb$Fb$Fb$Fb$Fbæ Än¾íšgVV—˜ÿÚ²gœ™˜4ÈHÂ]X71Y_mæ\l~-î™`ŽIÆO$Õ¼ ²=£+ ÓLØq0Á»+ hE,1dtñ & ’ VœcŽýÖ`Û°d¼7¬ø”˜%s˜­{­(ƒ ë'†3W/1¦-s(ë $Fb$Fb$Fb$Fb$Fbs£¿kÅÓ£ûãØå+•$ý¤©ì>11^‚­+ÞÄDMafµe‰LF!&JÙ]bbî9L«-bòVö˜XEXö2~brGöxŽI[ØZD+ËEéz¥%LlbŽe qŠõp¿Uq‹sl?Ô+¬¥ëÑÊ£vƒ^ýö;}¨OXíââÁo,Ê kÅWV›ÿˆÓ–9”ù¥-Ь"Ó줦-ŠîÀq&&Ú&Yo˜t‹ÁÒ–Ü–Á,¨´¥)LÂÒ–ýº·ÜdoL¿;y–¶h«+Ì÷.m© x’L[î ‚t±Ú2¯2V[hʼn­hDŵGxÏ_öèÑ€Ó–üG°cÓ 0dÚ’ÜzσԜ§úK”²€´¥>ÐR™ÂO™oÚ""£óN[® Kª-2Eµå¢0ñ#öBÚÒæBlÄ´eþjË׊ vE˜ ¶¹Yq|a­ø;]~òýÕábœ¢bj¼K2ŽG«þÄTFf5c='¦U_……ÎKõ%¦¶-’öŠ §®ÄÂämaÏç˜ÞzP﨨7‰aƒGÏ}LïÍ1l¸ïZy7è-*öh .ï•Ç8׊Ÿ$Æ zeýÕèŸ!±.Á…˜ÜxŸ÷”uW[^_mÙw®ŽÌ?äbÖèj˶·òÄbºƒ«-aÆcF·jKMØvÅYѯÚÒ$a0Õ–†°€9ª¶è1*nZâ"«-\+NHŒVü® ©¬„õ†•/Á‹Ø&±@0îÄ*}@a¨&±Ó‚ðÔ&:aÚršcÅNà_TóÙS˜Â„y7‰Ø!„á?’jÓ¤ ",>m9Z±œ­ø cÚµâ„ÄhEZ‘ÄHŒÄHŒÄHŒÄHŒÄHlpb…ßð?Ÿ’‰‘‰‘‰‘‰‘‰M>øNp¤j¼,¡IEND®B`‚urwid-1.3.1/docs/tutorial/qa3.png0000664000175000017500000000133712615524560016207 0ustar ianian00000000000000‰PNG  IHDR½iJ}ÕbKGDÿ‡Ì¿ pHYs  ÒÝ~üƒIDATxÚíš në „çþ÷ä•¢˜û¬í`ÒAz­UƒùXÆ0ÞÒΤ'=éIOzÒ“žô¤'=鈟Q`·±Tô÷Mn£ÇÖô¯G¿þÊÞ—ù"Wwo¥æÖqY¹Pë»Nd/v¤J?ù§&*$Q3õ¡7‡6}…èõ£&}<ý±Ú´ôrÊGèVEó{齨§Pìë»_§‡M=Ú§˜ôÃûNI/×)œ¼2éˈñœb=+W(Ñ×ñqúu§©°|[ú¤H2E‡¼˜W -ì´- a¬Oè›I„>0•H®鵕ÍÃôž£/w»ÆÐ"µöîD7;Sn^îDÅš£ÉÞÕM³¥OÂgg‡…ZÒèÉVsí%±_77l êùƒ˜;ˆ;n¸½êE´Þ-zTŸ7i€¾£œz/öÕ1û3ôùnëX¯„Qú‘·¶¡G^‚û9zœ O¨Í÷gÑèÐWYñÌkŽÑ;Ì=&X&ÍÔ ¦äßÒO4…kˆ×Ä~}Ù>‡¬~yoCïMÁÃGÑÉ€ïA_»ß¼‘Èlpßµ®Ò}³Ã¿ Ãt\•§f”ãÑCK7ÞEo¼Ví¦bŸ¾{`çÔq ŸÞÐ}4­ÑHž9¢ÜKA‡sÑ_9§p[9E°ü¿~è1IOzÒ“žô¤'=éIOúß¡ÿÇKMÝúÀŽIEND®B`‚urwid-1.3.1/docs/tutorial/adventure1.png0000664000175000017500000000144612615524560017602 0ustar ianian00000000000000‰PNG  IHDRÏð‚ÆoûbKGDÿ‡Ì¿ pHYs  ÒÝ~üÊIDATxÚíÛkvƒ †áoÿûœ}ôô4*ÃEË`Ð×­‘‹>'‰ì^›ðàÁƒ:ò¼qÿÜ«=¶éÜÉz_"žvê©»+ÇíÌæñÜÁ;_è•çìUéfž~ó÷«í,Öó?Ê.¶PæùrÛmφö·sž¤æËû·iBÒ_ƃsó…JËë×xÐs¾àÚöÈ3É|a˳Wah|«$N5lx”ÇæûÆžù–Ϙe®§WN‹ñªgΞþŠeS _zºì$s~uVsÜÌÙÒ×kc£p}žsÅ2öÍåFy_'¸9{®W—÷i}ì¡ò׿M§ÉhT™dN3ð2çÏô_¨Ñ7Þn¼·Ö/<•|A/[¢NÛÊ=zôèÑ£G}$ýÑϘ'¢Ñ£G=zôèÑ£G=zôèÑ£G=zôèÑ£G=zôèÑÿ?½ó‚ƒ÷ÂFL¶Öç#H/Œ[é8}k,¾ ÷[¾œÑ4/Ýô+#Nþk ·»­:š~œ9}Òk¾j㎽šÊãèy •Û¿[U5ç*5!jŽ¢-Â:=zôèÑ£G=zô?Þþïç5Ĭ‰ÅÅIEND®B`‚urwid-1.3.1/docs/tutorial/sig4.png0000664000175000017500000000164412615524560016372 0ustar ianian00000000000000‰PNG  IHDR½iJ}ÕbKGDÿ‡Ì¿ pHYs  ÒÝ~üHIDATxÚí› ’ã Dûþ÷Ô=¶j6ýOpð®R™I cxrh@O~áÙôÀß&o¸“mP[Nï¤ÝGÿSS^Ý2ýn±Fÿî¨W—Áé:‘¦¡¯YÅ­d•Y×Õ (òŽéz¯6j9£—{ ÿ{ƒÎ¬ëjoy'ü¢áƒ~³Ü*Ý~±¹m]>ÆÈs=z4·ˆGmì9kôﺸ ´¢‡Ã`÷ëZGsŽë9Kôrl,Lܾõ+±Û^ù½tpv÷˜ÆïiÞöŽ©üñÏÆ™Àh Rž£ vÿˆ~Ã}ã’pãÚr0ýDTr2ý7^O§_Y²Æ¡ï`ý\éÓ_ì`=‰è—¢EŠº—é…L:#Ï“¤p·}_¢éÉ ªâ×h›´ö Ênjôð<0µ°¨ªé݆ ôÓ{…h2çvKOûémˆêóœ¾ BµÆGé ‰íÝr†¶—Wo¤WNkmï G&!UJ³’ oã'§')äB¸9EOGV&ÏP‰$#¡GŸ!_ï¯.¢¿]ð=î˜ñpú\’1¿2W„|=.)(¾TcJûxÑG·7íáô<IÖ–öi´ÊaŠ §­ ùƒ¶G´®³EÎê‡ãN¯= ­{Ž0AJDFÑn`œˆbwÐ"ãœÞ(‚Uxr}8üú䡤¸‰ÞŠžsÛsÝ<E1yO—›lOÐ-TPØ<®¢˜µS©¶6Ð?÷UôE_ôE_ôE_ôE_ôE_ôE,=ü:Ÿþ9ÝâœC²Óy$¶ Š“Š=vó$zqâÆ§§]˜Wƒï>bgw`GÆ9þ€ž7ÇÎDx™3`ÑcÖø,(#1#1#1#1#1µQc·ÞãšWVÖ–XþØï¡qebRÑ oö˜ØÛ… “ýY2ço’ä×ê–ú˜üDŽšbËÛ‰Õ…¡–7ècw…Z1–XäèbALH.Xq>¶Õ`©Y3Þ +>%¦É|Íc¸V”— 'g®QbŒ-k(öoר08œÇš'èÝ Ûõ]cÂôu#•ÇÍw÷<™Í©gûŸˆ1A¯¡ìI‚–Ê) …QßXm%U຀‹Êîýiì cb‚®°£9µ†»²»Ä]aQ+YĔ؀0D{ÞÇúVÌ‚ç_çeß¼(Ö£bwðý•†£‹˜ŠÝá¾æ°p±§ëQ塟 ¯7œtq¶e]eœm¡¶¢2*B²¥´Ç¬Mpl)_â†=6¶ ‹Œ-‡Sï¹q4ç^4ŽÚ3>¶´Øk«á + ¶ˆÈ[„YÇ–«ÂR¢[. ;bbKO˜ ±7Æ‹‹Œ-¬$F+~׊ZlÙ÷L»Ä4/-ÎS6[& ó-Èê@Èy‹LR6[P”Kš tŽ-ЯrÏf[Z’ÄÓ‹Þ±e«ÖÁÍbK·ÍÑe[ ð™q¾¨‹±e]eŒ-´âÂV,.-Jäú°êG0!&7Þgž2‹Ø2åŽ%±%U¢Ý@Ìì±%ÝèÊ Ä|*,ÿØr¦4bÌh[TaÅ"±8]&±¥MÌi„‰™mésècA±ù¨˜´ºŠŒ-¬$F+~׊َʵC¿µaõ`EìÎ"1G0æÄë<…E-;„§µa‚c‹”3še1årÿÌÀ¿mQ…!L˜õ"±»aaÛ"±Ä‚‰¡6Ûâ)Ì?¶äV¬§{aŒ-¬$F+ÒŠ$Fb$Fb$Fb$Fb$Fb/'ÖPøÿó)‰‘‰‘‰‘‰‘‰‘Øâ?¸Qíà4‚ IEND®B`‚urwid-1.3.1/docs/tutorial/hmenu2.png0000664000175000017500000000301012615524560016707 0ustar ianian00000000000000‰PNG  IHDRдŠsëˇPLTEåååÍÿÿÿÍMMM===EEE ÏËËõååú--ÖÎÎÍ'''888"""222(((+++DDÚ‚‚æ»»òoo㧧çé>>>  ÐÌÌõ//ÖÓÐÒÑÑÃQ*ÀbKGD Lò pHYs  ÒÝ~üIDATxÚí݉r›F€al i›6MÓûLôîû?_eIÀ^`I•ÌáïFÂbÁ“Éç5Æ“ª’$I’$I’$I’$I’$IÒsên£õÁû†.Ð@ 4Ð@ 4Ð@ 4Ðh h h Z@ 4Ð@ 4Ð@ 4Ð@ h 7º žcp ƒ—º®ÇœÔý®ñ1§œèeƒ‡³FÐu=%úô@¯ t55%ÿŸÁ ýÀp”â Fgd ôE3ôáÏQÅ‘G·+9&ܩ朻'@<îž÷ºÛ®c­ÁuwD}øRè'øhLtž`ÐË-*ª i?¸ÖY躛®‡9;]rtR‡|É‘§ÿ@/ô4}—ƒŽÆg Ÿ¡“t‚²zì„–K yt7x  ‚ó@×ý2âqЖ ̵ӠïFfômÍÐÁ‡'€¾ý7è^ºÚ2è3ÖÐÇ×hE¯¡Ë²^èÃåŠø Æ(è˜þðõ\å–ÉÅûh•RMØ– ½‰&@'²ïrah Ð@½QЫ] h h h 4Ð@ 4Ð@ 4Ð@ ´$I’$©Øæ¿ëÐ@ h 4Ðh- Ð@ h 4ÐZ@- Ð@ h ÖdMìZQþeî($¯@?ÃÚv|Oûè˜SÎ33èº¾Ž³kè{ž}ú¥ƒ~ø/~€~Vóó(ÅŒ>=ã3A?0|2Š@/Áóþyÿh»í6Ö¼ÑvG´‡/…~‚ÆDç ==èƒåýsÝÍÕÑFÿFn4ýá xÊ=[ï½xÿƒÐm7]svºäè¤ù’#;Oÿ)>ܽ¼Ü=^ÞôG¿ú¤ ºo øÞn“ì:0¾* G½Þ½|Ú4oÞýô}Ö¶ŸÍÐÉF:™f‹ ÇN¸ë‹ªúò«ªúú›[‚þ¶®¿+‚îçÒdczW¸Z Y‡G}ß4?üØ4oz†úçt†>.ÎÝöˈÇA÷KŽ_vŸþתz÷î– ûýÕ1èãÊ ¤¶[4dKŽIÐéQ6Í_7Í?ÿ=—ÍÐÁ‡'€® ³ûÍ@®¡Ç¦áü"F¶ö(ÌÐ7½ôåU@Ÿ±†>¾F+ê| ]–=è‘5tÄ·¸†NífG½ÐzôêD›^ܨ¢UJaL4a/û*G=y•#] z±²«y/,ßô£h µIÐ㻀ÖÒs?´€zÛÝìÐ@ÏâyxÙ 4Ðk}ÉN ^2èüç ñ­¡ûŸ•D÷ ôü¥·|  ÷i?Í><®~#(Ð@_Vzûh/6¿Û3¹££×¼{\ýFP ¾p†NnÍAGwe“xúê7‚ ô5¹Û³pÏÝ¢Ñ@«@¹Šm*YC‡¿p4Ð+ÜöY•¯n ¿ 4Ð+œ°×К¦¼²Ÿ­)Ñ«»§hm* 4Ðh ´€h 4Ðh ´€Ð@ h 4Ðh- Ð@ h 4ÐZ@- Ð@ h ´€Z@- Ð@ h ´€Z@- Ðh ´€Z@ 4Ðh ´€Z@ 4Ðh ´€Z@ h 4Ðh ´€Ð@ h 4Ðh- –$I’$I’$I’$I’$I’VÛúõÜÊþ»ÜIEND®B`‚urwid-1.3.1/docs/tutorial/input.py0000664000175000017500000000040512615524560016521 0ustar ianian00000000000000import urwid def show_or_exit(key): if key in ('q', 'Q'): raise urwid.ExitMainLoop() txt.set_text(repr(key)) txt = urwid.Text(u"Hello World") fill = urwid.Filler(txt, 'top') loop = urwid.MainLoop(fill, unhandled_input=show_or_exit) loop.run() urwid-1.3.1/docs/tutorial/hmenu1.png0000664000175000017500000000230112615524560016710 0ustar ianian00000000000000‰PNG  IHDRдŠsëËPLTEÍÿÿÿåååÍ ÏËËõååú--ÖÎÎÍDDÚ‚‚æ»»òoo㧧çé ÐÌÌõ//ÖÓÐÒÑÑÚÚÚ///½½½áááãã㨨¨ppp===OOOoooeeeÙÙÙ...»»»ÉÉÉØØØÍÍÍÔÔÔÒÒÒÈå†ÔbKGDf |d pHYs  ÒÝ~üÊIDATxÚíÚys“@Àa² Uë}_µõ¾õû;Ó&,Ç' >¿‰9È6þÁÓ7L³L’$I’$I’$I’$I’$IÿS«#Í™h ´€Z@- 4Ðh ´€Z@ h 4Ðh ´€Ð@ká C¨î[G.Z³¶w£@w,Zó}Õ(7;-Z“€ÞÞ¶\ãF¼}+ ,®¯™vqfNŒ&.C<Ú½8ÎÕ: 5Ÿz•€n‹k[xû Кô*n¶ÕÌÑ :Ž›Å@k¦ û(÷€úq 5ÐcwèUõ°~$­¹€^Å¢=†@¯Ò5¡¾s­i@GÎ,Ð@ h 4Ðh- Ð@ h 4ÐZ@- Ð@K’$IÒTåGš3 4Ðh ´€Z@ h 4Ðh ´€Ð@ h 4ÐhuV$ ¼µ œY K ­G u| Cè“â[ýkÆ|Ð:è†D@´fú’a/ÅFÆh G€Þx\ß_ÝBù<4µÖ„ò'ÂæW!nð5Ï©-Zû}íúÉСܮ«=»=r”R«'éÈ‘|Nü/n®nÅé)ÐÚèÛ!ÜéÛ¡[ORЭm¶tß®»[÷îч@k_ =>yÒ½vâñwÐqäxZϞŋ—@k_ ‡fèñ k/G€Þ÷—Ggè1 w˜¡·‰:¡»e­íнW'BûâFјR:Ö46lW9t0Ðéõä .,- ôÐ_Û­¹çï¡4ÐZ6èåÙwfÐQ»ïyh ´&]>ÉêCHõ)µ#ùôó‰3 tìÕë³7 &èÖËú‘òv¾~¹þwq´¦ý6ËÞuIAçC ßçù‡yþé3Кô—¯gߺtä¨@—ŸWÞ¾çùŸyþë7Кtÿ—¬ù-09ê›´ZKuÌÐÐZèjÀ¸|hyÖºÊÑܰÖü@wì×3Ï™z¬e u4;ô?nê@kž “3 4Ðh ´€Z@ h 4Ðh ´€Ð@ h 4Ðh- Ð@ h 4ÐZ@- Ð@ h ´€Z@- Ð@ h ´€Z@- h ´€Z@- 4Ðh ´€Z@ h 4Ðh ´€Ð@ h 4Ðh- Ð@ h 4ÐZ’$I’$I’$I’$I’$IÒòû€¼9?dϹâIEND®B`‚urwid-1.3.1/docs/tutorial/highcolors1.png0000664000175000017500000000047612615524560017750 0ustar ianian00000000000000‰PNG  IHDRê‡êÐØùPLTE×_¯_‡‡_¯_×ÿÿ¯àÃlN pHYs  ÒÝ~üÒIDAThÞíØë  @á®à ® #À÷î¿JyU°&˜˜k‘zNú£BÂ×bŒËBDDDDDDÓv*****êaêuD¨¨¨¨¨¨‡©·~w÷qUXõ銩„[m–®Sé“nƒ¦ª÷ÄìÔr?½êUUã^·S>ï~š2R5,+®\åý¶×ŸÇµŽ«Ý»¾S¥Žî°¦Mí©î ª´§i´zQÕø Ç]~Ü”Ç(#’Ýþ§ïa{•ߨ¨¨¨¨çVùõÜ*MÛ ¤X1·ŸIEND®B`‚urwid-1.3.1/docs/tutorial/attr.py0000664000175000017500000000070512615524560016337 0ustar ianian00000000000000import urwid def exit_on_q(key): if key in ('q', 'Q'): raise urwid.ExitMainLoop() palette = [ ('banner', 'black', 'light gray'), ('streak', 'black', 'dark red'), ('bg', 'black', 'dark blue'),] txt = urwid.Text(('banner', u" Hello World "), align='center') map1 = urwid.AttrMap(txt, 'streak') fill = urwid.Filler(map1) map2 = urwid.AttrMap(fill, 'bg') loop = urwid.MainLoop(map2, palette, unhandled_input=exit_on_q) loop.run() urwid-1.3.1/docs/tutorial/cmenu4.png0000664000175000017500000000261212615524560016713 0ustar ianian00000000000000‰PNG  IHDR)Ò3Ði0bKGDÿ‡Ì¿ pHYs  ÒÝ~ü.IDATxÚíÝ ’Û €aÝÿžÜ£3ÝlÖ6/„màwg:Lã8ÉWAlY lºM @êüùÌÅ¥ü BÊjm)O¨Ï_‹m¥Ö %_±…¥œC )¤¼G«•Ç©GE )¤šCJ/#R|7ï$–TÛ'ë"í~)ï\‚üÅh¥Zv^NêóçsÅüãñ}HÎ,ñÎß}údTZ*ê_"qÿÿìüÝí»Ï{¤FåäœÚ“PÂä¼ÔØDêå©Kýîü>©aY—8:ÊR!ƒo‘—K¸|T¤êR?_bçﵬTbŸïÓ•"뢧)¤zÊ)¤T´9ëT±RÎPÛJùWpP£–Z3”ÔºPÜ¡­à@jk©!Y.žRH!…Ô¤R’;tö5þN|5e —2Ê`.R’;téf‹´½Ã—HÙr ’;tqâȆR’;ôá®çµ²å ) ‘cC¢â™¸B&ȵœf¬”)ë"¡.%×F(}Ä#aö€·ÄžS.A#,)”RçF˜CªÐûÒ-5©C¯«JÝÛûÌYq)i‰©pgïsÈ%ˆç8u¨™‘)3Ê‘½^*sæYúî“rã¯ÿ&÷y¤÷Ý‘u¹­› §zŸŠRÏIm¼!Õ+%H[Öe{(mÖ)åuPÚ\P&©ÍÊ` ½o³2ȾYq‡á,ao©–3ÏÍÊ` W3›Ý^F )¤ZHª½ ¦ñ­¾» F/ÕU3gLuÍ›1–Ál(U/ƒ©ÝøËÞïË•ÁÜs°Ô{šfYŠ;4 õÛË7KÙæÍ¨¥Bv­‰ôC¹°î_<Úk†‘¶ æôr*5»tºxˆ“ܳn•2Ï0R–Áž/6ž–Ê´·H9Ì›•”Ô—|ÑJɬRµ2˜¾ï¾K_M7n”"뢧Š#…ROI"…RH!…RF©Ë`I­NH!…RH!…RH=,µø¼$?©Õç%¹I-?/ÉKjýyINRÌK:HUr Hå].zŸÔN#ºQj£³«Ô>gžf)b )¤Bê=RÅÚâ*G­TûnÔo]45 ¢X¸Yª\1[ûlµ²bw){Ø>#å2ùh©JmqùŤôÐaÞBHÒ²Daâ?ã:Séÿ"†§)KÉ{;¥4K†RàT­ðc­…euËSâjøïÎ×åF%û–æ“ º)E©• nR%cÖÅ"uY ;-%ç9Ke©è±¥Šd̺˜¤¢àÊÅTÐIu|#;K². ãT­á$eˆ©òØä3í=µ¾n8?¤š©”%¯iìDç=ýKÏʽ\Í …RH-)e˺l$e˺ …T$eʺì$eÊ% õiXÊ`–“jФcª )¤rR½P\Í´=)¤z¡Ô>“µ‘JJõþœdU*Ä¿*ýë;‰ÒRÝ?’X“ ‰WSHI¸U*ȼR‡ÊDGjþ9ɲT¢ÔÛ½B$Õ¥ R’š`Ìošâ»ñ*Õécê}Ó TG©Þ@²èsô½û¤Jg o·ŠÎºÇ&û™ç«©2gž\ÍdÁBjB©u6¤º¤d]Ö”‘K@jk©!Y—¥Æäú6*' JyEÒò1å…ÔæR ¸ši;4RH!…RH/hžúyÂû¥|¡BÊŠ\B{©M¥ü}È%8w µÔˆ±‰«™¦C#…RH!õˆY¥¹¤œ¥Èº(¥È%<'µØöûáˆ$e`…”³Pê˜bC ©G¶PÞ—ö39…IEND®B`‚urwid-1.3.1/docs/tutorial/input1.png0000664000175000017500000000035112615524560016736 0ustar ianian00000000000000‰PNG  IHDRl-n ÑØbKGDÿ‡Ì¿ pHYs  ÒÝ~üIDATXÃíÔK€ EÑ»ÿ}²~(µƒ óÐÚâ¤LlÌÅ8£€ë$“Œ£í(:˜yÁÚ÷_À ÖÏ¥šÇö¶üu”ÃØÒ‰¦L²Ì}¨Q>ñÊcq~‚Q‹5Œ•»X¾A^ÇÖ«‹ÑÛæ{.Çê:ê<‡þ… &L˜0a„ &LXÒ¶Iu!vëIEND®B`‚urwid-1.3.1/docs/tutorial/minimal1.png0000664000175000017500000000045312615524560017230 0ustar ianian00000000000000‰PNG  IHDR½iJ}ÕbKGDÿ‡Ì¿ pHYs  ÒÝ~üÏIDATxÚíÔAƒ Ðÿ{z.Ú R¬44] Ã„Çd4ËÌ#“ëózŠ$ÍdçÔýè6+£õÕãýö=ý'ú¤´L³wjõÏPêŽ+YWëóØ?½¢f§Ò©îõ½õTû¯õýõ£ô)ýqZ¿üL¿ÿÕþŸþ~ê3üŸóf²6kÖPuqígôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôôóëoœÅ©¤HÔšIEND®B`‚urwid-1.3.1/docs/tutorial/multiple.py0000664000175000017500000000172512615524560017223 0ustar ianian00000000000000import urwid def question(): return urwid.Pile([urwid.Edit(('I say', u"What is your name?\n"))]) def answer(name): return urwid.Text(('I say', u"Nice to meet you, " + name + "\n")) class ConversationListBox(urwid.ListBox): def __init__(self): body = urwid.SimpleFocusListWalker([question()]) super(ConversationListBox, self).__init__(body) def keypress(self, size, key): key = super(ConversationListBox, self).keypress(size, key) if key != 'enter': return key name = self.focus[0].edit_text if not name: raise urwid.ExitMainLoop() # replace or add response self.focus.contents[1:] = [(answer(name), self.focus.options())] pos = self.focus_position # add a new question self.body.insert(pos + 1, question()) self.focus_position = pos + 1 palette = [('I say', 'default,bold', 'default'),] urwid.MainLoop(ConversationListBox(), palette).run() urwid-1.3.1/docs/tutorial/multiple3.png0000664000175000017500000000204012615524560017431 0ustar ianian00000000000000‰PNG  IHDRÏÃ(øbKGDÿ‡Ì¿ pHYs  ÒÝ~üÄIDATxÚírã Euÿ{ríf,@76bÓfÚ  l#þXÒ^Ivä‘}Ðäõ+;ñH:~7à‘ƒK~ÒëŸ÷±yD,ª¹ ûìÁ#¿î¥ ×á|ûì6~âóì1¿í•àž <ïíø\²•g›÷;3Ÿ.ðòÅŽ*9Î*>Áónà,O¾ˆŠ¾°6¹%™‡þŸ£*•ûµ=Oþ;H>ôú±k/ÆjHRõ˜r W‡Žó¨þñÚy|[V3âiÏ|‚'g´êÐÈÅ¡|ݺs¼.o2÷ìs"³$ußVñxžÒçù-¥Š_äÉ'< åÝbŠ9¦}Ì‘=´Oyô+öé\Ä?†¿™C«?Uq+÷1TIz^êŽŸŽ¿å³ÕÏ †Uý©/÷œQ¼9èÌå7žb~³çƒô\rÝæÂåJž#ñ[ާÿ|ån‚çxàxàxàž ñÝ}<§c —xît«<Õê–ØËoGiÐÍHj3×uá‰ô!‚©V]3×{“2hBLm]Fxâ#‘6.з¶·ÐÜ[’nr·uÙ͘÷w‹Ç èš<›ãQ±ƒ*<ñÙð+1¶÷÷,O9Æ&.=žr-\ÇÝÑÁ€Gšñ“®ØÇèN{®ÑnfÌ}~È`ào•qtTbžç ׌'“ü)Éó2þ®klšãyauc¢ŠÍÓ l²v;ûäke ­_ÈwoãH`ùŒ²Ï.ú…žÅý-öø‘z“Oìùm¯<ð\àY_¿àn2ö2Ïð<°ß¿]™ð,®_ðÖʇ<+ëŠ3«ð„5~–×/ˆÔÂë æèú…©Ìaõ gy–Ô/Tcº;~Òêú…vÂôç·ô\B¿°*úxàxàxàx*ô ÷á|Púô èÐ/s"úô C¿pú…ÅyÖHòt%Þ¶KìæÊ³‹?jžc¯ÚšÝ>ÇMüF±y$kš¢óT»+ žx/bàþ xàÉ<Þ¿0ñDáý Ý¥ ƒ'„~!Íó,ýþ…ªµÞ]˜÷/T óŠ„yÿBñ¯ £_P òDÐ/$'`1æYM¿ cxmÀÂäYZ¿ jš€EVKÏ%ô «ò _€xàxàxà§âA¿pÎÕ¡_@¿€~ýB1'¢_@¿Ðñ7ô 7ð _øt^n0×4xàxàxày‚g¯ûëÞþ†èÒpIEND®B`‚urwid-1.3.1/docs/tutorial/cmenu2.png0000664000175000017500000000230612615524560016711 0ustar ianian00000000000000‰PNG  IHDR)Ò3Ði0bKGDÿ‡Ì¿ pHYs  ÒÝ~üjIDATxÚíÝkÒ›0 †QíŸÚG|iø&l °yèLGÓ gl/N*Êb[¤öÇ7\,.å…RÞPkKyB}þZl ”Z³)ùŠ-,åÜ´BÊ{´Zyœ Ø+RH!…ÔR’y‘ê§y&±äjŸÔEÎü$RÞY‚üÚX¥Î¬¼œÔçÏçŽùÏãû’ìYÒ•¿ëÜ>ôIT^*é_"iÿÿ¬ü]í»Îs¤¢²ÙG{¢µ!Löë'ÅK¤6)O[êÿÊÏ“ K]ÒÖQ—ÒB|ŠT\–p8T¤ÚR'±ýy­(•Yç»ù­R¤.öq*`¯H!…Ô]þH!eꤧS ªRH9C½VÊÓ`ÌRk6¥€¹.Lî°Îà@êÕR!© 7ÏŠRH!5©””v]|ß…¯ešÂÍS¦Á¤¤´ëÚÃ9÷ "5–%Hi×Õ/޼PJJ»Þ<õ<ÎlÙhʯÙ’LžIgȨ§ÓÄJ ¥.¢m)9Zô‘Ž„Å^Òöœ²‹”f,+¥F©}¡sHUz_~BKKjÓëšR×ö¾áÔE\Û”œiSzeïsÈÄsœÚÌ™-L3*‘=^ªpåY;÷I½øõßì:·ô¾+R—˺Ið8Õ»)RHÝ'5û‚Ô Rò© Ôe=© Ô)ã}ß‚RA©ËzRQ© R¯í}a©ËjRq© R/½òd®‹¹MuoŠRWK­3m©gIÁÔë‰òRÎß0ÚÕERù®÷ûá—“…~çÀèþ‡,¶…3ÕþHÝ¿aôÿ9°”¤ú Ùðka‡ÞT]>‡Á|\J’¢þ’¨Ô¥dV©´÷}^Îqè·e:]:Äii«ÀÞõ £Lwh6Éý{Mª°UȈö½™ÌgoFÒ§ŠR»Î¸™¹u(†®b¥öTƒç¾C_-û|¤’+ϸÔå)—Eƒ]û>¤&”ZgA ©H©ˆÔeI©,©7KŤ. Jñk%H9Kñ».F)~­)g)溘ÛT÷¦H!…RH=QŠÔÅ(E–€”³©‹Qª7Kyì³¢”­c¯$Õ›º¼Nª;K@ ©üáô§.¯kS›"…RH=Eª÷zkb©žÔeà*b^©®,)³TÿõÖ´Rƒ©K¥XLjtRu©š©‹ÿGc­þ&ÇRYRF©úØDêBêâ½)RH!…ÔS¤H]”Ô¥ Eê+Eꢤ.©€Ôe±eLjù–äÒ¶(c”] (uI]X^Ó~B ©e¤.^_RN© Å^ õK]^_ uF K!  ¤ìR Ø îf¬ RH!…Ô,RœÝZY©KŒí‚,ÁZ uF KA–`-²K`+¸›±.H!…R³Hqvkd ¤.1Rh´ ²kÔ)4,Y‚µ@Ê.‚­ànƺ …RHÍ"ÅÙ­U%ºÄH¡Ñ.ȬRg¤Ð°d Ö)»¶‚»ë‚RÞË?,ël%s .èIEND®B`‚urwid-1.3.1/docs/tutorial/cmenu.py0000664000175000017500000000437212615524560016500 0ustar ianian00000000000000import urwid def menu_button(caption, callback): button = urwid.Button(caption) urwid.connect_signal(button, 'click', callback) return urwid.AttrMap(button, None, focus_map='reversed') def sub_menu(caption, choices): contents = menu(caption, choices) def open_menu(button): return top.open_box(contents) return menu_button([caption, u'...'], open_menu) def menu(title, choices): body = [urwid.Text(title), urwid.Divider()] body.extend(choices) return urwid.ListBox(urwid.SimpleFocusListWalker(body)) def item_chosen(button): response = urwid.Text([u'You chose ', button.label, u'\n']) done = menu_button(u'Ok', exit_program) top.open_box(urwid.Filler(urwid.Pile([response, done]))) def exit_program(button): raise urwid.ExitMainLoop() menu_top = menu(u'Main Menu', [ sub_menu(u'Applications', [ sub_menu(u'Accessories', [ menu_button(u'Text Editor', item_chosen), menu_button(u'Terminal', item_chosen), ]), ]), sub_menu(u'System', [ sub_menu(u'Preferences', [ menu_button(u'Appearance', item_chosen), ]), menu_button(u'Lock Screen', item_chosen), ]), ]) class CascadingBoxes(urwid.WidgetPlaceholder): max_box_levels = 4 def __init__(self, box): super(CascadingBoxes, self).__init__(urwid.SolidFill(u'/')) self.box_level = 0 self.open_box(box) def open_box(self, box): self.original_widget = urwid.Overlay(urwid.LineBox(box), self.original_widget, align='center', width=('relative', 80), valign='middle', height=('relative', 80), min_width=24, min_height=8, left=self.box_level * 3, right=(self.max_box_levels - self.box_level - 1) * 3, top=self.box_level * 2, bottom=(self.max_box_levels - self.box_level - 1) * 2) self.box_level += 1 def keypress(self, size, key): if key == 'esc' and self.box_level > 1: self.original_widget = self.original_widget[0] self.box_level -= 1 else: return super(CascadingBoxes, self).keypress(size, key) top = CascadingBoxes(menu_top) urwid.MainLoop(top, palette=[('reversed', 'standout', '')]).run() urwid-1.3.1/docs/tutorial/hmenu4.png0000664000175000017500000000314512615524560016722 0ustar ianian00000000000000‰PNG  IHDRдŠsëË]PLTEåååMMM===EEE'''888"""222(((+++>>> Íÿÿÿ ÏËËõååú--ÖDDÚ‚‚æ ÐÌÌõ//ÖÏ0bKGDåØù£ pHYs  ÒÝ~ü¡IDATxÚí܉v›F@Ñnéž6u›®ÿÿ™uKÌŠ@¶aî;9 ‘Íd®'X’ A’$I’$I’$I’$I’$I÷Ôp£àÍ h 6ß´ù6@ Í·m¾Ú6ß´ù6@ Í·m¾Ú6ß´ù6@ Í·m¾Úït˜ù,ç­%ÇÔë|‡×ú,@t˜™ð%Ž{úçX=À£Í7ÐA‡9”·:tr@o:œ~Ãóç Âô÷Ó]§ß’î3}œ7ŸŠE,~ÉF8¬ýt— C¾¯lah݆Æ>ÓLµèë6†ªÃßd\@¿h…®ÌSô°tº±×|—‡ÖÜ5„aô¶ó ôEЧ{ÖŽN5.‚Þù”#`2¨œ9 Cr¦1§ß¡õ( ;ÝXPçAG]:ûbØt± ¶)¾}tãQ@wzÍ9ôój–NzXð²;èÐ&Þ8‡n‚vÝ7è¡ýìD}cú/¸ºO/§åa¬ÛH>I}è~@g?ò9[/} ´m¾ ð0 Í·m¾ hóm€@›o 6@ Í·m¾ hóm€@ 4дù6À€ÖÑZ@- Ð@ h Z7 zÕŸ‡+”]Ükæñ@«ÐËh­½êòª@«{ÐCˆ/R6W´Jï:}”ÐÞ(EF9œ®‚6¤e­^lh]º~]«êå%aç/p•_ë̺¼j\vY õÖ ‡e×C›=4A@«ÐÙµëë Ïw…éö&èèã­ÍAKõÜS( @+´¶–]‹õ5@[¡õbÐÕkÃÏräg«žåˆAçg,É-k¯:kf¾©Ì,Ð@ h 4к'ÐãØ×ÇyíÑ}nÕÞɈvÐWÎø ƒž=°âžô†ôãúºÙî–b wü‚úÊÉ~úýüŸm²q¾aŒ7Âù†x§tÉ_ó_ý& §cžŽ<;Àé¯ãÓÓ¸¢Ë±½è/¾üêë:èód'Ó~šàôæ1Ù¡¸ûÓïß<þñîñ×»Môæ,,‡ï©ë§µ±½èoÇñ»*èì;¡…wÅóOü¨ïCøáÇ~úyÐùË‹¾mhžrI¿.¶:Súòö>_¡Ÿ¥šÚÓzUœrÌ‚ÎõËãgý5„¶Y¡ß7VèøéŽæ÷y@ßÌ9tk.æº<÷¨,„å£v¬80+ô€nœC'|«çÐ9†âQ€.¿7¨°L¾š+Ëykì@÷ºñ,GòäÆXy$Zõêt(ΗÊ'áÇx¨!9ÝJŸ¾Ùæ‹hÝT@ h 4Ðڠߊfî:Pfè…ìO ï­…o{Ó~ÏÔ,ýðØç?¾Ó/Ú§wП?Yè þÖ §Õè;¿ísŒ_ÐŒÞSzÞy<èc fry‹Þ>:ž^œÏ^¢Ó—µwõ¾ú÷?>þÙ}EÓå-xûhôæŒt"¸KÐ=<üÝ}”“LW¬Ð—ß>ºt彦€þçßÿ}×å.€®.Ø}€ž=‡vÊq? ó¿º ºÏ:>mç›ÂûÆä§dcеŸÄ)ß>Ú)èô…•CˆfòN[úx™Y ÖM‚nß´zÏû¡4Ðh ´€h 4Ðh ´€Ð@ h 4Ðh- Ð@ h 4ÐZ@- Ð@ h ´€Z@- Ð@ h ´€Z@- Ðh ´€Z@ 4Ðh ´€Z@ 4Ðh ´€Z@ h 4Ðh ´€Ð@ h 4Ðh- –$I’$I’$I’$I’$IÒíö?ëâ¶_!C=IEND®B`‚urwid-1.3.1/docs/tutorial/cmenu3.png0000664000175000017500000000274612615524560016722 0ustar ianian00000000000000‰PNG  IHDR)Ò3Ði0bKGDÿ‡Ì¿ pHYs  ÒÝ~üŠIDATxÚíÝa’£ àwÿ{ö=öÇfhPá¹[[ÔÆÉèW€æÙÛPêºÃÍ¥ü (E)o¨½¥<¡>ÿl¶L”Ú³+ùŠm,åܵ(E)ïÙjçyj»RŠR”¢Ô;¤ù5@qkžIŒ\Û'uAûοDÊ;KÀ¯ÿÀ*Õ²òvRŸ?ŸOÌÿ=¾/áÊ’®ü]çö©3 òRÉøÒñÿYù»ÚwçHÍÊpö ¥) ×õ“Æ!RAÊS—ú[ùyRÓR—´w”¥DéƒO‘š—%D»J©ºÔÿƒØõ¸¦JeÖùþø­RL]ìóÔ„w¥¥(u—?¥(e¤Í© ¡Š JQÊêX)ÿ –Á˜¥öìJj]XÜa­à ÔÑRSR~xJQŠR”z©´·VÇïÄ×R¦ps)Ã@L$í­K[ж…‘Ë ½uñÆ‘¥ ½upÕ3®l 4ñkaIñLZ!#ˆËiæJ ¥.ºâ†‹>Ò™P}Ã%}Ï)K°HIfDzRb”º6äR…Ñ—/h©I£®*µvô §.píShéS²rô9d 𜧂šˆRf¤‘=^J9ó,ûPnüÆov[FߊÔeÙ0™5cw)E)JQj©S¦.RL]¤˜º0u隺l¶¤NîIö¾B!¥ìR„2Aí¿ëþ†\(E)J=\jðÍ ÁØ T›5ê ÁØ T‹5, ÁØ ”]ж?ÍXJQŠR”z‹nµ³¦.s¤¨Qo0K°6(Õ"E KƒY‚µA)»l ~š±.”¢”÷òôa²Èx¡IEND®B`‚urwid-1.3.1/docs/tutorial/minimal.py.xdotool0000664000175000017500000000005112615524560020474 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 21 7 urwid-1.3.1/docs/tutorial/sig.py.xdotool0000664000175000017500000000023012615524560017627 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 21 7 type --window $RXVTWINDOWID 'Tim t' type --window $RXVTWINDOWID 'he Enchanter' key --window $RXVTWINDOWID Down urwid-1.3.1/docs/tutorial/qa.py0000664000175000017500000000101112615524560015755 0ustar ianian00000000000000import urwid def exit_on_q(key): if key in ('q', 'Q'): raise urwid.ExitMainLoop() class QuestionBox(urwid.Filler): def keypress(self, size, key): if key != 'enter': return super(QuestionBox, self).keypress(size, key) self.original_widget = urwid.Text( u"Nice to meet you,\n%s.\n\nPress Q to exit." % edit.edit_text) edit = urwid.Edit(u"What is your name?\n") fill = QuestionBox(edit) loop = urwid.MainLoop(fill, unhandled_input=exit_on_q) loop.run() urwid-1.3.1/docs/tutorial/minimal.py0000664000175000017500000000016612615524560017014 0ustar ianian00000000000000import urwid txt = urwid.Text(u"Hello World") fill = urwid.Filler(txt, 'top') loop = urwid.MainLoop(fill) loop.run() urwid-1.3.1/docs/tutorial/index.rst0000664000175000017500000003750512615524560016664 0ustar ianian00000000000000.. _urwid-tutorial: ****************** Urwid Tutorial ****************** .. currentmodule:: urwid Minimal Application ------------------- .. image:: minimal1.png This program displays the string ``Hello World`` in the top left corner of the screen and will run until interrupted with *CTRL+C* (*^C*). .. literalinclude:: minimal.py :linenos: * The *txt* :class:`Text` widget handles formatting blocks of text, wrapping to the next line when necessary. Widgets like this are called "flow widgets" because their sizing can have a number of columns given, in this case the full screen width, then they will flow to fill as many rows as necessary. * The *fill* :class:`Filler` widget fills in blank lines above or below flow widgets so that they can be displayed in a fixed number of rows. This Filler will align our Text to the top of the screen, filling all the rows below with blank lines. Widgets which are given both the number of columns and number of rows they must be displayed in are called "box widgets". * The :class:`MainLoop` class handles displaying our widgets as well as accepting input from the user. The widget passed to :class:`MainLoop` is called the "topmost" widget. The topmost widget is used to render the whole screen and so it must be a box widget. In this case our widgets can't handle any user input so we need to interrupt the program to exit with *^C*. Global Input ------------ .. image:: input1.png .. image:: input2.png .. image:: input3.png .. image:: input4.png .. image:: input5.png This program initially displays the string ``Hello World``, then it displays each key pressed, exiting when the user presses *Q*. .. literalinclude:: input.py :linenos: * The :class:`MainLoop` class has an optional function parameter *unhandled_input*. This function will be called once for each keypress that is not handled by the widgets being displayed. Since none of the widgets being displayed here handle input, every key the user presses will be passed to the *show_or_exit* function. * The :exc:`ExitMainLoop` exception is used to exit cleanly from the :meth:`MainLoop.run` function when the user presses *Q*. All other input is displayed by replacing the current Text widget's content. Display Attributes ------------------ .. image:: attr1.png .. image:: attr2.png .. image:: attr3.png .. image:: attr4.png This program displays the string ``Hello World`` in the center of the screen. It uses different attributes for the text, the space on either side of the text and the space above and below the text. It waits for a keypress before exiting. The screenshots above show how these widgets react to being resized. .. literalinclude:: attr.py :linenos: * Display attributes are defined as part of a palette. Valid foreground, background and setting values are documented in :ref:`foreground-background` A palette is a list of tuples containing: 1. Name of the display attribute, typically a string 2. Foreground color and settings for 16-color (normal) mode 3. Background color for normal mode 4. Settings for monochrome mode (optional) 5. Foreground color and settings for 88 and 256-color modes (optional, see next example) 6. Background color for 88 and 256-color modes (optional) * A :class:`Text` widget is created containing the string ``" Hello World "`` with display attribute ``'banner'``. The attributes of text in a Text widget is set by using a (*attribute*, *text*) tuple instead of a simple text string. Display attributes will flow with the text, and multiple display attributes may be specified by combining tuples into a list. This format is called :ref:`text-markup`. * An :class:`AttrMap` widget is created to wrap the text widget with display attribute ``'streak'``. :class:`AttrMap` widgets allow you to map any display attribute to any other display attribute, but by default they will set the display attribute of everything that does not already have a display attribute. In this case the text has an attribute, so only the areas around the text used for alignment will be have the new attribute. * A second :class:`AttrMap` widget is created to wrap the :class:`Filler` widget with attribute ``'bg'``. When this program is run you can now clearly see the separation of the text, the alignment around the text, and the filler above and below the text. .. seealso:: :ref:`using-display-attributes` High Color Modes ---------------- .. image:: highcolors1.png This program displays the string ``Hello World`` in the center of the screen. It uses a number of 256-color-mode colors to decorate the text, and will work in any terminal that supports 256-color mode. It will exit when *Q* is pressed. .. literalinclude:: highcolors.py :linenos: This palette only defines values for the high color foregroundand backgrounds, because only the high colors will be used. A real application should define values for all the modes in their palette. Valid foreground, background and setting values are documented in :ref:`foreground-background`. * Behind the scenes our :class:`MainLoop` class has created a :class:`raw_display.Screen` object for drawing the screen. The program is put into 256-color mode by using the screen object's :meth:`set_terminal_properties() ` method. This example also demonstrates how you can build the widgets to display in a top-down order instead of the usual bottom-up order. In some places we need to use a *placeholder* widget because we must provide a widget before the correct one has been created. * We change the topmost widget used by the :class:`MainLoop` by assigning to its :attr:`MainLoop.widget` property. * :ref:`decoration-widgets` like :class:`AttrMap` have an ``original_widget`` property that we can assign to to change the widget they wrap. * :class:`Divider` widgets are used to create blank lines, colored with :class:`AttrMap`. * :ref:`container-widgets` like :class:`Pile` have a ``contents`` property that we can treat like a list of (*widget*, *options*) tuples. :attr:`Pile.contents` supports normal list operations including ``append()`` to add child widgets. :meth:`Pile.options` is used to generate the default options for the new child widgets. Question and Answer ------------------- .. image:: qa1.png .. image:: qa2.png .. image:: qa3.png This program asks for your name then responds ``Nice to meet you, (your name).`` .. literalinclude:: qa.py :linenos: The :class:`Edit` widget is based on the :class:`Text` widget but it accepts keyboard input for entering text, making corrections and moving the cursor around with the *HOME*, *END* and arrow keys. Here we are customizing the :class:`Filler` decoration widget that is holding our :class:`Edit` widget by subclassing it and defining a new ``keypress()`` method. Customizing decoration or container widgets to handle input this way is a common pattern in Urwid applications. This pattern is easier to maintain and extend than handling all special input in an *unhandled_input* function. * In *QuestionBox.keypress()* all keypresses except *ENTER* are passed along to the default :meth:`Filler.keypress` which sends them to the child :meth:`Edit.keypress` method. * Note that names containing *Q* can be entered into the :class:`Edit` widget without causing the program to exit because :meth:`Edit.keypress` indicates that it has handled the key by returning ``None``. See :meth:`Widget.keypress` for more information. * When *ENTER* is pressed the child widget ``original_widget`` is changed to a :class:`Text` widget. * :class:`Text` widgets don't handle any keyboard input so all input ends up in the *unhandled_input* function *exit_on_q*, allowing the user to exit the program. Signal Handlers --------------- .. image:: sig1.png .. image:: sig2.png .. image:: sig3.png .. image:: sig4.png This program asks for your name and responds ``Nice to meet you, (your name)`` *while* you type your name. Press *DOWN* then *SPACE* or *ENTER* to exit. .. literalinclude:: sig.py :linenos: * An :class:`Edit` widget and a :class:`Text` reply widget are created, like in the previous example. * The :func:`connect_signal` function is used to attach our *on_ask_change()* function to our :class:`Edit` widget's ``'change'`` signal. Now any time the content of the :class:`Edit` widget changes *on_ask_change()* will be called and passed the new content. * Finally we attach our *on_exit_clicked()* function to our exit :class:`Button`'s ``'click'`` signal. * *on_ask_change()* updates the reply text as the user enters their name and *on_exit_click()* exits. Multiple Questions ------------------ .. image:: multiple1.png .. image:: multiple2.png .. image:: multiple3.png .. image:: multiple4.png This program asks for your name and responds ``Nice to meet you, (your name).`` It then asks again, and again. Old values may be changed and the responses will be updated when you press *ENTER*. *ENTER* on a blank line exits. .. literalinclude:: multiple.py :linenos: :class:`ListBox` widgets let you scroll through a number of flow widgets vertically. It handles *UP*, *DOWN*, *PAGE UP* and *PAGE DOWN* keystrokes and changing the focus for you. :ref:`listbox-contents` are managed by a "list walker", one of the list walkers that is easiest to use is :class:`SimpleFocusListWalker`. :class:`SimpleFocusListWalker` is like a normal python list of widgets, but any time you insert or remove widgets the focus position is updated automatically. Here we are customizing our :class:`ListBox`'s keypress handling by overriding it in a subclass. * The *question()* function is used to build widgets to communicate with the user. Here we return a :class:`Pile` widget with a single :class:`Edit` widget to start. * We retrieve the name entered with :attr:`ListBox.focus` to get the :class:`Pile` in focus, the standard :ref:`container widget ` method ``[0]`` to get the first child of the pile and :attr:`Edit.edit_text` to get the user-entered text. * For the response we use the fact that we can treat :attr:`Pile.contents` like a list of (*widget*, *options*) tuples to create or replace any existing response by assigning a one-tuple list to *contents[1:]*. We create the default options using :meth:`Pile.options`. * To add another question after the current one we treat our :class:`SimpleFocusListWalker` stored as :attr:`ListBox.body` like a normal list of widgets by calling *insert()*, then update the focus position to the widget we just created. Simple Menu ----------- .. image:: smenu1.png .. image:: smenu2.png .. image:: smenu3.png We can create a very simple menu using a list of :class:`Button` widgets. This program lets you choose an option then repeats what you chose. .. literalinclude:: smenu.py :linenos: * *menu()* builds a :class:`ListBox` with a *title* and a sequence of :class:`Button` widgets. Each button has its ``'click'`` signal attached to *item_chosen*, with item name is passed as data. The buttons are decorated with an :class:`AttrMap` that applies a display attribute when a button is in focus. * *item_chosen()* replaces the menu displayed with text indicating the users' choice. * *exit_program()* causes the program to exit on any keystroke. * The menu is created and decorated with an :class:`Overlay` using a :class:`SolidFill` as the background. The :class:`Overlay` is given a miniumum width and height but is allowed to expand to 60% of the available space if the user's terminal window is large enough. Cascading Menu -------------- .. image:: cmenu1.png .. image:: cmenu2.png .. image:: cmenu3.png .. image:: cmenu4.png A nested menu effect can be created by having some buttons open new menus. This program lets you choose an option from a nested menu that cascades across the screen. You may return to previous menus by pressing *ESC*. .. literalinclude:: cmenu.py :linenos: * *menu_button()* returns an :class:`AttrMap`-decorated :class:`Button` and attaches a *callback* to the the its ``'click'`` signal. This function is used for both sub-menus and final selection buttons. * *sub_menu()* creates a menu button and a closure that will open the the menu when that button is clicked. Notice that :ref:`text markup ` is used to add ``'...'`` to the end of the *caption* passed to *menu_button()*. * *menu()* builds a :class:`ListBox` with a *title* and a sequence of widgets. * *item_chosen()* displays the users' choice similar to the previous example. * *menu_top* is the top level menu with all of its child menus and options built using the functions above. This example introduces :class:`WidgetPlaceholder`. :class:`WidgetPlaceholder` is a :ref:`decoration widget ` that does nothing to the widget it decorates. It is useful if you need a simple way to replace a widget that doesn't involve knowing its position in a :ref:`container `, or in this case as a base class for a widget that will be replacing its own contents regularly. * *CascadingBoxes* is a new widget that extends :class:`WidgetPlaceholder`. It provides an *open_box()* method that displays a box widget *box* "on top of" all the previous content with an :class:`Overlay` and a :class:`LineBox`. The position of each successive box is shifted right and down from the previous one. * *CascadingBoxes.keypress()* intercepts *ESC* keys to cause the current box to be removed and the previous one to be shown. This allows the user to return to a previous menu level. Horizontal Menu --------------- .. image:: hmenu1.png .. image:: hmenu2.png .. image:: hmenu3.png .. image:: hmenu4.png This example is like the previous but new menus appear on the right and push old menus off the left side of the screen. The look of buttons and other menu elements are heavily customized and new widget classes are used instead of factory functions. .. literalinclude:: hmenu.py :linenos: * *MenuButton* is a customized :class:`Button` widget. :class:`Button` uses :class:`WidgetWrap` to create its appearance and this class replaces the display widget created by :class:`Button` by the wrapped widget in *self._w*. * *SubMenu* is implemented with a *MenuButton* but uses :class:`WidgetWrap` to hide the implementation instead of inheriting from *MenuButton*. The constructor builds a widget for the menu that this button will open and stores it in *self.menu*. * *Choice* is like *SubMenu* but displays the item chosen instead of another menu. The *palette* used in this example includes an entry with the special name ``None``. The foreground and background specified in this entry are used as a default when no other display attribute is specified. * *HorizontalBoxes* arranges the menus displayed similar to the previous example. There is no special handling required for going to previous menus here because :class:`Columns` already handles switching focus when *LEFT* or *RIGHT* is pressed. :class:`AttrMap` with the *focus_map* dict is used to change the appearance of a number of the display attributes when a menu is in focus. Adventure Game -------------- .. image:: adventure1.png .. image:: adventure2.png .. image:: adventure3.png .. image:: adventure4.png We can use the same sort of code to build a simple adventure game. Instead of menus we have "places" and instead of submenus and parent menus we just have "exits". This example scrolls previous places off the top of the screen, allowing you to scroll back to view but not interact with previous places. .. literalinclude:: adventure.py :linenos: This example starts to show some separation between the application logic and the widgets that have been created. The *AdventureGame* class is responsible for all the changes that happen through the game and manages the topmost widget, but isn't a widget itself. This is a good pattern to follow as your application grows larger. urwid-1.3.1/docs/tutorial/hmenu3.png0000664000175000017500000000351512615524560016722 0ustar ianian00000000000000‰PNG  IHDRдŠsëË{PLTEåååÍÿÿÿMMM===EEEÚÚÚ///½½½'''888"""222(((+++¨¨¨ppp>>> ÙÙÙ...»»»Í ÏËËõååú--ÖDDÚ‚‚æ ÐÌÌõ//Ö¼<bKGD Lò pHYs  ÒÝ~ükIDATxÚíÝ‹v›6€aQè.ݺû½Ûºu×÷Â¥qIH€³Øòý''ñláû‹¢v‚$I’$I’$I’$I’$I’žSÍA{>;øâ  4Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@¢ÏãÓW{^ ƒw:Ìl·´Ëkž–[ƒnÛ§QõT÷³9Ðõçu— ÃÌ#kwzÃ3tÛ>‘Äc€gÌ=g Þ è0;Gïô{†7¤¸½:ôq)ÐáÈ ÏYCGÏF¨}Ÿ×dßte ð-®¡s»“­ö úô+{z£ º0fØ|»3týèDùÂðËqyÌV– G9ÚÙ£ù¥¸Õž@¢U;x•µÁõA# h >èCì Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@ 4Ð@ ô³-I’$IÏ«ÃÿÖ+ Ð@ h 4ÐZ@- Ð@ h ´€Z@- Ð@ h tT×ÕoéǬ¹Ÿ÷Tìú;xŽŽæ1ƒã÷Ž: è®›½ýÖAÏíà@7sïRæÿ;¬¼›~ÿ*U_©/áfçåU;¸ñ€~ä¬s÷ùþ£ë/w©Ö芮ߢ;Iæ¿dLr?Ñ Ûx~øœ?øqòa—ßtÚ‹®´ƒWد3A÷ÛDoÖ˜ßKúæ¹ý»ÔEʲû Y<áSôòƒ?ª€îúÙlœÒò%Gÿ¦KŽÉý ÿÄÇw_^Ý}¼º¤êü䦃{âÉwEa«ËíW Ã'Ÿ¾þ¬ :z'Ŧöþ #Úhõ²ï‹ñ~Âeæí'|Š>ïº/j3tþ¢N@gÓQtíïú2„¯¾á›o/ za‹;5sS¼Ïżè~•0|Â÷K ›â,ÌÐ¥·ž‚n6úåùöðƒó<Ð]ôCz ôð£ùÇ»þ§Þ¼¹è =¿ƒéNuùÊi¼bt¾Õåö«„áç_^ÿZmš'½ß5ôzÐÉÂtt(ÌîWkqÎF†ÂÚcñÇÔ¥:o â+Ðg¬¡ÇÕeX^b\te ]ùfNQ¨? Ûª,‹G CºÙafèêщnr| Y¥T„tÉqcЕ£]˜;Ê‘/PŠ[môô(G39L1Þñý—ÿž8 ®Ù!èâ\m¤æ  4Ð@ýœAkƒ½4sÓŽòÊÝSȾwÓCúÿ Û‡ Õ,o»'гSAßÇ ½Fä̘‚~ÌÚèÇYß1èéßAÒSCïÿ’ü Y[ý°ò¸ûtºÔž.FëŠÓMãReÿ ó³+§§ãÎÓˆþ|ú¸Ê‰ *‚þí÷w”Aßn{½m;~ cÚhLtåŽAçgWb§'ñdgt šï>®r"¨Š ÿlÛ¿Š ÇÏãŒ=>èüìÊ)è䤅hA_åDPAÿýÏ»+ ûÃk@GÇFµ†.€gåÂ9wÑ·]]C.W€.NØÇù¥0>u4[Cg§Y½YÐã²x-èCÎÐÉY‘¡|tcü_bÞ0èáÀÅtºé¯In:Ô’#›°µ/лëj”yú03´s:vº~ÐÚz·Ð@ h 4Ðh Ð@ h 4ÐZ@- Ð@ h ´€Z@- Ð@ h ´€Z@- Ðh ´€Z@- 4Ðh ´€Z@ h 4Ðh 4Ð@ h 4Ðh 4Ð@ h 4Ðh- Ð@ h 4ÐZ@- Ð@ h ´€Z’$I’$I’$I’$I’$IÇí?QÄþ·ävIEND®B`‚urwid-1.3.1/docs/tutorial/highcolors.py0000664000175000017500000000170612615524560017530 0ustar ianian00000000000000import urwid def exit_on_q(key): if key in ('q', 'Q'): raise urwid.ExitMainLoop() palette = [ ('banner', '', '', '', '#ffa', '#60d'), ('streak', '', '', '', 'g50', '#60a'), ('inside', '', '', '', 'g38', '#808'), ('outside', '', '', '', 'g27', '#a06'), ('bg', '', '', '', 'g7', '#d06'),] placeholder = urwid.SolidFill() loop = urwid.MainLoop(placeholder, palette, unhandled_input=exit_on_q) loop.screen.set_terminal_properties(colors=256) loop.widget = urwid.AttrMap(placeholder, 'bg') loop.widget.original_widget = urwid.Filler(urwid.Pile([])) div = urwid.Divider() outside = urwid.AttrMap(div, 'outside') inside = urwid.AttrMap(div, 'inside') txt = urwid.Text(('banner', u" Hello World "), align='center') streak = urwid.AttrMap(txt, 'streak') pile = loop.widget.base_widget # .base_widget skips the decorations for item in [outside, inside, streak, inside, outside]: pile.contents.append((item, pile.options())) loop.run() urwid-1.3.1/docs/tutorial/multiple.py.xdotool0000664000175000017500000000026312615524560020706 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 23 13 key --window $RXVTWINDOWID A b e Return B o b key --window $RXVTWINDOWID Return C a r l Return key --window $RXVTWINDOWID D a v e Return urwid-1.3.1/docs/tutorial/hmenu.py0000664000175000017500000000525712615524560016510 0ustar ianian00000000000000import urwid class MenuButton(urwid.Button): def __init__(self, caption, callback): super(MenuButton, self).__init__("") urwid.connect_signal(self, 'click', callback) self._w = urwid.AttrMap(urwid.SelectableIcon( [u' \N{BULLET} ', caption], 2), None, 'selected') class SubMenu(urwid.WidgetWrap): def __init__(self, caption, choices): super(SubMenu, self).__init__(MenuButton( [caption, u"\N{HORIZONTAL ELLIPSIS}"], self.open_menu)) line = urwid.Divider(u'\N{LOWER ONE QUARTER BLOCK}') listbox = urwid.ListBox(urwid.SimpleFocusListWalker([ urwid.AttrMap(urwid.Text([u"\n ", caption]), 'heading'), urwid.AttrMap(line, 'line'), urwid.Divider()] + choices + [urwid.Divider()])) self.menu = urwid.AttrMap(listbox, 'options') def open_menu(self, button): top.open_box(self.menu) class Choice(urwid.WidgetWrap): def __init__(self, caption): super(Choice, self).__init__( MenuButton(caption, self.item_chosen)) self.caption = caption def item_chosen(self, button): response = urwid.Text([u' You chose ', self.caption, u'\n']) done = MenuButton(u'Ok', exit_program) response_box = urwid.Filler(urwid.Pile([response, done])) top.open_box(urwid.AttrMap(response_box, 'options')) def exit_program(key): raise urwid.ExitMainLoop() menu_top = SubMenu(u'Main Menu', [ SubMenu(u'Applications', [ SubMenu(u'Accessories', [ Choice(u'Text Editor'), Choice(u'Terminal'), ]), ]), SubMenu(u'System', [ SubMenu(u'Preferences', [ Choice(u'Appearance'), ]), Choice(u'Lock Screen'), ]), ]) palette = [ (None, 'light gray', 'black'), ('heading', 'black', 'light gray'), ('line', 'black', 'light gray'), ('options', 'dark gray', 'black'), ('focus heading', 'white', 'dark red'), ('focus line', 'black', 'dark red'), ('focus options', 'black', 'light gray'), ('selected', 'white', 'dark blue')] focus_map = { 'heading': 'focus heading', 'options': 'focus options', 'line': 'focus line'} class HorizontalBoxes(urwid.Columns): def __init__(self): super(HorizontalBoxes, self).__init__([], dividechars=1) def open_box(self, box): if self.contents: del self.contents[self.focus_position + 1:] self.contents.append((urwid.AttrMap(box, 'options', focus_map), self.options('given', 24))) self.focus_position = len(self.contents) - 1 top = HorizontalBoxes() top.open_box(menu_top.menu) urwid.MainLoop(urwid.Filler(top, 'middle', 10), palette).run() urwid-1.3.1/docs/tutorial/smenu.py.xdotool0000664000175000017500000000016612615524560020204 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 24 11 key --window $RXVTWINDOWID Down Down Down key --window $RXVTWINDOWID Return urwid-1.3.1/docs/tutorial/sig3.png0000664000175000017500000000163712615524560016373 0ustar ianian00000000000000‰PNG  IHDR½iJ}ÕbKGDÿ‡Ì¿ pHYs  ÒÝ~üCIDATxÚí› ’¬ Eïþ÷™}LÕ{-æ8bãTºz¦-Äpá£\AoþàÝôÀÿ/x’­SZNï¤=Gÿ¯¤¼¸iú'ÃbŽþh¨O“Ái:‘¦¢ŸYÁͲʬË:(òöéúYlTsF)NŸ)üï8‚άËj_yâ¢âv³Ü*ÝØÜ¶,£¹=ZXĽ6Žœ9ú£,Ít·Ûìz]joÌq#gŠ^ö‰û¤o-À,ž¾W梸—ήîÓÃÄ=ûÞq•ßÿyÀ8ã DJ'r”ãÁ®ïÑ/£œœ[6¦X•ìL;;ßG?@š™2S‰§°Ž~Ì:úôEObõKÑ$Eg”é‰L#Ï“¤ð°=N}&èißãø}U«à™AYV½‡ç)…¥@MGõÐSLïÑdÎå–žÖÓÛ%ò=Ú­”¶gè\ëµ³¾§Izô|Oëèq‰Þ8¾Ç ô²éÜÛqv\‚—G¦èn£±Yú-?EÿEúW¯1gڧ苾苾苾苾苾èÿ}¸‹¶bô”[ïÊ÷M]øm|CM’°”ã6z¯YˆïÑ’x 3× yz\RP|wQ!íãÅ]^µ—ÓóµH2·´_£USìrÚ ’oô=¢yMrV?§ØåôÜÐ|äø &H‰ÂU´»0ND±+è‘qNoÁjyò}Øýúä¦ä4¸ˆÞŠžsßsÝ<E1yw—‹|OÐ-TPØ<®¢˜ÕS©¶п÷SôE_ôE_ôE_ôE_ôE_ôE_ôEo+÷Õß}±1Þ›Ô¾vó&zñÆ(ÞvaAµ ¾÷ò{rMS¯à<ŽïÒóCM½}96Š6¢G¿×îë{¨‘Ç¡â~ƒîÑwîÈÓ@| ¾Ö9EñóSþ%1Ne®OIEND®B`‚urwid-1.3.1/docs/tutorial/smenu3.png0000664000175000017500000000106112615524560016727 0ustar ianian00000000000000‰PNG  IHDRØ¥÷l.bKGDÿ‡Ì¿ pHYs  ÒÝ~üÕIDATxÚíÝán‚0Ðûþïyßc1ÓXÁÒ–˜Nür¾¶ýXD>¿zdgƒn¡ˆ#FŒ1bĈ[,ÙÝoÛVÅ;'+G$FŒ1bĈ#F¬k1UTEbĈ#FŒ1bÄSEU$FŒ1bĈ#F¬q1U첊1zÜpŽ]Ÿè±MÞ;þþl±ƒM_‡Žð‡Å#vœîÁŽÏ±×ŽWsbþ†]¯vÅú`,~²·gbg²ýË}LçÁ†]1~Íò`åÈKç:wUÜ,×óäç`y4Øùb‹Á†–ÆB½V#Åö.~I,gtÛÅZšcŸ¹gŽå5bÓõí-ÙxÁœ4°´*ÎX\B답½¹Wì@LU‘1bĈ#FŒ±ÆÅ¶‹Æ¶jUl?ØÁ*>7?øìÙ³!– §È‹ƒEÖËh!ØðZbÅ`yi²Šb¥`—./YU¬,/Ÿf5çØz¿0ɪ®ŠÙ†XÖ¿Že s,O¸Ž/Ðye®Úwílî»óô=’ù1bĈ#FŒ±,¦ŠªHŒ1bĈ#FŒXãbª¨ŠÄˆ#FŒ1bĈ5.VHØÇÀ>%FŒ1bĈ#Fìæƒbv±e(=IEND®B`‚urwid-1.3.1/docs/tutorial/sig.py0000664000175000017500000000113712615524560016147 0ustar ianian00000000000000import urwid palette = [('I say', 'default,bold', 'default', 'bold'),] ask = urwid.Edit(('I say', u"What is your name?\n")) reply = urwid.Text(u"") button = urwid.Button(u'Exit') div = urwid.Divider() pile = urwid.Pile([ask, div, reply, div, button]) top = urwid.Filler(pile, valign='top') def on_ask_change(edit, new_edit_text): reply.set_text(('I say', u"Nice to meet you, %s" % new_edit_text)) def on_exit_clicked(button): raise urwid.ExitMainLoop() urwid.connect_signal(ask, 'change', on_ask_change) urwid.connect_signal(button, 'click', on_exit_clicked) urwid.MainLoop(top, palette).run() urwid-1.3.1/docs/tutorial/smenu.py0000664000175000017500000000206012615524560016510 0ustar ianian00000000000000import urwid choices = u'Chapman Cleese Gilliam Idle Jones Palin'.split() def menu(title, choices): body = [urwid.Text(title), urwid.Divider()] for c in choices: button = urwid.Button(c) urwid.connect_signal(button, 'click', item_chosen, c) body.append(urwid.AttrMap(button, None, focus_map='reversed')) return urwid.ListBox(urwid.SimpleFocusListWalker(body)) def item_chosen(button, choice): response = urwid.Text([u'You chose ', choice, u'\n']) done = urwid.Button(u'Ok') urwid.connect_signal(done, 'click', exit_program) main.original_widget = urwid.Filler(urwid.Pile([response, urwid.AttrMap(done, None, focus_map='reversed')])) def exit_program(button): raise urwid.ExitMainLoop() main = urwid.Padding(menu(u'Pythons', choices), left=2, right=2) top = urwid.Overlay(main, urwid.SolidFill(u'\N{MEDIUM SHADE}'), align='center', width=('relative', 60), valign='middle', height=('relative', 60), min_width=20, min_height=9) urwid.MainLoop(top, palette=[('reversed', 'standout', '')]).run() urwid-1.3.1/docs/tutorial/multiple1.png0000664000175000017500000000076712615524560017445 0ustar ianian00000000000000‰PNG  IHDRÏÃ(øbKGDÿ‡Ì¿ pHYs  ÒÝ~ü›IDATxÚíÖ Ž‚0йÿ=¹Ç&» ÓbA!KÑHjaú:ÓBL÷:ânžˆ¿Ÿ)âÍ ÎÛp´×žNÛyžßH£á†=g–ÎQÏ3½DG'áE—hè1¤¡Ìw®:×±–)-úîñ$Ì2µÙIã.þ^Zò÷yuç:Öü™Š¾»ÖO“³‘íVRµ·'mï6VãõÞóÄ\LëûÁz½yž±r™Ì·Þ±|#ÒëqlíoÝzò”klà±ñÊ3g)ÅXòSX[?åBIWo{¢Y?Ó‘üt¦³¿×ä2ëì}‘ÆEËF½Uɉtý¸ç ÏŒ+ø¨ärÎó³çmë žÈäÿ?–gÏž›½_߃ÁÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃsøøßo?+9`Ý·IEND®B`‚urwid-1.3.1/docs/tutorial/input5.png0000664000175000017500000000024412615524560016743 0ustar ianian00000000000000‰PNG  IHDRl-n ÑØbKGDÿ‡Ì¿ pHYs  ÒÝ~üHIDATXÃíÑ1 ÀþÿŸý‡ ‹ Ž-ˆ—!ëµ$²1ÑŽ-1f*oر£ßDzœ9±m³ìù »` ƒÁ`0 ƒÁ`°O°Þõâlä-XvIEND®B`‚urwid-1.3.1/docs/tutorial/sig2.png0000664000175000017500000000133012615524560016360 0ustar ianian00000000000000‰PNG  IHDR½iJ}ÕbKGDÿ‡Ì¿ pHYs  ÒÝ~ü|IDATxÚíœ nà …ßýïÉ=&­ ÿI›ÄÙêÔ(!øÃƒÆÛPž\ðlzàõU€Å®d›XÓ;÷®£ÿµ46·›þʰØG¿ Ô{Èà ]W¦£o‹ÂpmYUÖ¶š»ºszÞÌF=”ÝãvGþlWЕµ­ú)]Ý…¸G×ñɸYnuß^ØÚÖ–1‹\5,âYGÎ>úÍ– ÚôtÚâ}mu–sÜÈÙEßω»Ñ×-6߫梸ï\¼=§‡‰û²î{ÇUþü—ãä#tw&‘£ñþŒþ„}á’páÚ’˜~aWr7=d>KPp¨vú]«C:ú5þTz½ÈèÙšÝ÷ÕÃЄOˆÒ“þ zdÏ9¯½–G˜7çì®Múè½ÇÌVHOzÒ“žô¤'=éIOzÒÿ!úð-bô[n}*?oêÀ§ðª’„S9¾Fï K‘g´EA…žÎ#G&žŽ$Cú «íê#xÐíØÃf-“è¥>½•d,DN§ Qô¶ªmÇÕˆ¨¦Š‘,`/}úhÌÇôUøª5¾J_0ð½ÛÎÔ÷ýÓ éUÐZß»S‘Iô*¥UI†È‹ô¥rÙ ³@8éÈÊ$â 5d èÑ2äûsë"úéò€û¸cÆäôcIÆúÊÌ2éÓçÜÕÿúTçPŒÒ“~™þ¡=è6©¤n!=éIOzÒ“žô¤'=éIOzÒ“ÞvîŸþæÅÆúx$é…àFy}GØ:þ ‹{ñáxS³Ú™‘'þÐËKM>ŽE‰è1Ÿµy}•yú îô ãöW«.ç(uUxîsH°üà±—üKIEND®B`‚urwid-1.3.1/docs/tutorial/new/0000775000175000017500000000000012615524621015600 5ustar ianian00000000000000urwid-1.3.1/docs/tutorial/new/adventure1.png0000664000175000017500000000146212261107530020360 0ustar ianian00000000000000‰PNG  IHDRÏð‚ÆoûbKGDÿ‡Ì¿ pHYs  ÒÝ~üÖIDATxÚíÛá–‚ †áïþïsîcÏž=&à j.`ÚË2Eä œ Hö¬$³LžºìºœWÙé=Zåß9ù÷QUæâpíQ(ð¥ÊN뱃žv·Iò¨¾„l¦§èg=v•GcÚçϲ±¶ö<îŸ{µ‡G.Š…øöªk#ômx,XÇÒþžq銱žïõiè³ië‘Î]¬wñ´žú°SßÈŽžû%æ 7jŸîó…^yÎÖJóô›/¸=hcy°î1_ð£ìø·…˜çÃ=n³=_˜ÚßÎyŠX/ﯘÎÍ’þV—¯)ñ ç|Áµížç&ó…5ÏVCã[rðVƯò\:òá¹_ƒ‚T`œ;úÙD4± Êÿàã¸ÄU$b3´§ÞÇ*SVƒG4'’\ÊÊÝV² 1æ.„ âĪÂB´/BàÄ€qÆ×¨º\œ`%¨Ei:®¯×/¼}!Dˉ\õàþBD&Hñ†`x“ñˆêg™Ct¢ÁR£Ü·Ä&CFXé!Z1µ7òh•,GúAÉ" „…§sETˆx!™y!x!goˆM:Ä\³8/DvYäD†Ó¡R÷ZH¨¢€G/°$¨ÅçPØœ›ÑH|¦O37ã(†–jŒX{ñö(þâÅ^3~ˆÑ ½+@…ˆ4úhn½3Üë½Eô"ËQ!bú ¡¸ñ'ˆ<ó¡W.¾Ct¥¢Ùe4z~%TˆßñØ Rq3Ì#âÝn…B—¹Õ~BägØÂ –SÁËÿÙ—ç u+Aì­f‘¦ ßGHwÓùh›¶}É¿®ÎËæt<‚Ô[BšzÕÔm›o/u ù$gä² Õç9]Š µ×,¾TO#+AZFF‡¦–T%Ÿ¨µîŸ7C/žðK>k†&Ÿ7C>k†q-#3Œ‘‘"'ša ÌA†f8ÁãÖ2~céÑÎIEND®B`‚urwid-1.3.1/docs/tutorial/new/adventure4.png0000664000175000017500000000245112261107533020365 0ustar ianian00000000000000‰PNG  IHDRÏð‚ÆoûbKGDÿ‡Ì¿ pHYs  ÒÝ~üÍIDATxÚímã EÙÿ>ÙÇœ93mT ~0IŸ¿ÚÔ 7*Cü®DàxÀð€<àxÀó3<“DD²ûwÑ&Z’Ý¿žqú›ÔT^©3SqM©+w‘”Ìö•‹<ßâ©~rT?Gj2?+<_Ôº=äíª@Oä±» €**‡8ާè+<$ljÍó)+‡ÒÛ‡ãx>Žö¡Ïàø!»›yðP¥Å„~ûÖÕP}ç<_ýVèEývM ÏÄ“xÀð€<àxÀð€<à‰å¡=ÅÆ ¦´*åðLóñõ³þFíš¿Ü)(d(kt®›Jö?k¯Ý)(Pÿ_)× Ùs!™‡%Ou…¯.‚®ö7_Îç­Uí–<ŽÇì|Ž5GŠ·•Ë[¿ÆeaB(?’§ÈèôS¼Xíþ¼…g è go to ", name], self.enter_place)) self.heading = urwid.Text([u"\nLocation: ", name, "\n"]) self.choices = choices # create links back to ourself for child in choices: getattr(child, 'choices', []).insert(0, self) def enter_place(self, button): game.update_place(self) class Thing(urwid.WidgetWrap): def __init__(self, name): super(Thing, self).__init__( ActionButton([u" * take ", name], self.take_thing)) self.name = name def take_thing(self, button): self._w = urwid.Text(u" - %s (taken)" % self.name) game.take_thing(self) def exit_program(button): raise urwid.ExitMainLoop() map_top = Place(u'porch', [ Place(u'kitchen', [ Place(u'refrigerator', []), Place(u'cupboard', [ Thing(u'jug'), ]), ]), Place(u'garden', [ Place(u'tree', [ Thing(u'lemon'), Thing(u'bird'), ]), ]), Place(u'street', [ Place(u'store', [ Thing(u'sugar'), ]), Place(u'lake', [ Place(u'beach', []), ]), ]), ]) class AdventureGame(object): def __init__(self): self.log = urwid.SimpleFocusListWalker([]) self.top = urwid.ListBox(self.log) self.inventory = set() self.update_place(map_top) def update_place(self, place): if self.log: # disable interaction with previous place self.log[-1] = urwid.WidgetDisable(self.log[-1]) self.log.append(urwid.Pile([place.heading] + place.choices)) self.top.focus_position = len(self.log) - 1 self.place = place def take_thing(self, thing): self.inventory.add(thing.name) if self.inventory >= set([u'sugar', u'lemon', u'jug']): response = urwid.Text(u'You can make lemonade!\n') done = ActionButton(u' - Joy', exit_program) self.log[:] = [response, done] else: self.update_place(self.place) game = AdventureGame() urwid.MainLoop(game.top, palette=[('reversed', 'standout', '')]).run() urwid-1.3.1/docs/tutorial/new/adventure2.png0000664000175000017500000000230212261107531020354 0ustar ianian00000000000000‰PNG  IHDRÏð‚ÆoûbKGDÿ‡Ì¿ pHYs  ÒÝ~üfIDATxÚíÝk’« †áoÿûì}œ:5ãh¼M7jòúc*}[È>kÖNÓ·á±6ú-÷ä-w\Káù^áÁƒÌ/£´Ñ=XïˆÊ«luï&—Oœ§xÙ†Ö·kžU ¬»÷/a‚72"¿=¸/8õ­Î_CÚƒÈx¡(Û=ÏKâ…e›­ SÛ7g%Ïžêáùøó==×3b¼sôü!]ðÔž|œØ2 |,çØnóœ:Æ7xêþ´€åè•uåóô 8å8Ïv¹L{Ù$·94øÝiL{ОPó‘É­œíªù%ùôjyQ¦š7šS=Îã&o_¬<Õª„ú6ÂcEíT¬Çœï¥z¬,ŸXO·´zûóù3-•'äü1ÿ;ÚoÄŠæÈú©~z»}+Ê'¶}sÊçï— —rÀ³=tÉ:ÒcŸµàÁƒžC¬Ýû?f‚¯ú쥿Õò‘¢<í…¢m¯x±>eΆê7å$×fåáúyÈCò‡<ä!yÈCò''.?Òˆ@†È|B#ÿßÌãŸiûÈšIòbó#å2„æGÈ›)?!4?’ä2ÄæG:è·¤@ŽßÈCò‡<ä!yÈCò‡<ä!ÏŒ¹mœ`¤Ui¥üHñ8åGÊßvÁ|Pþþ¶]å‘–§øF=‡£â“·)¸©:ó#ÍÂã¿0`ÛÁ-?Rþ¶ ' ZÏñ}üO_üÂÄ<ë´:ïƒfhß>qö—Arè,‰—ñxåSmNA9eÜB1.` WäãëÖ:hŸE ë, i.·ÊDóˆÔÖNÑNWåñ±ñ(ÐM2%å^·xÜâ›ËƒÎôíT’))ÞP]š=!>x|OóòdÄ;çYý ½óô\ü-Ã×’yÂײyœó#m÷9mjÎιêÑ£Ýçã?NØj´Âç’&çê\<êåg!ªgËà±s®èƒ/ÖnòœÌÙÏãœéÎûs’sõkž°|ªWúÍȹê¡”“ßfqlx×`þ Ž'ƒò‡<ä!yÈCò‡<äùž¸üH.1<ÎíHžïüH#¢ó#e/4ÄæGª%Ä/4„æGzÄã³Ð›éÓBCt~¤ì…†ÈüH#rÇ;3y8çý‡Á]¥µ6­æ‘~þ$éàî¬Ûpi}OðÚ}žï’F‹öÜÙt®z>áý ´‚€‡ÈÑë`ª’Ï\\—µ]ÒâØ3ƒÙ*Òº:¦ÞÅîíûûùOõÁuYù'ÇžÊg'Ú^R½îÿñGû²âjŒ·÷ȣܘÚýA»½y>eÙf’O}"}%sƺ{ý[ØÞ†ÁåŒûÛÌ‚¾O¦~*^ÙioUpdÞ?îù‡{Æ“›þTò8çãsæÓÖ›=ïØ–ôhÚw[Õü¥íw6×ö¹é ]ÕeLÀf WŸ5<Ê_s æmpíø¬–?ó{ÖèßÖÚðàÁsÁÓ¯yW-V¯k>væsõ¹rÑ5àÑ@OxܨöAO¾‰ÊÞXÝÑJá®`‚ 9V×™Œè4¶jV¢™?á(xã¢ld?VJRÃÓj)}OàoÎSŒVÑÍ4¿ÑÈfNŸ0³wãSîý—øtî?jçOÐÞÂÔ &Êy¾£“¦_êæO§½Ù©%׿)¼]ŸŸ h÷†Éˆž§žu <é¹­Ùl.Ü®ôœ¤]ëé<ýoÀW>Mð}ΉâX¿ÀúÖ/°~¡èY¿Àú…N{cý Ö/Üî‘æ{’TöÍå :ª¹=®Ê xŠ:¯Ÿ|¯LöóióÇ=ˆ=y|܃ò“çžYÚÛÜù£ú!Ÿ¹û·ý}x^êYìóõŒgǚȣÅû“˜h‹ƒ?¿bD蘌‹ˆ™~ŠÇìfOæy^¢¾0XÔÒ0¿}òô\«. Þ‰—ñé ú§Æ%§C¹€¯€‘¬¯µvP,xL¿ŒÖAo(ïžl‘VÛ©ÚéˆGNðÈ9ž}¡h µ€SJ[¨×vbxCáx,&è þË[Yž—Õ]{ybô….貫{+o¨Nž }Ak–BçúvÖ÷xÞPC3{š–’WÄOñŒé /[õ…çð`ji9õ<×¾0™'ݾ0›'D_¨7Ù*k‚µ5gª aþ½áûñ›¶­°=Ri¸ùø^ȇƒÙßfð(5!’'оp’ggÏþ:O}¡gül£ÅP.ò„é møÃÑü&J-͉_ˆZüÞűámR+!ßÐz>ROŒ¡!÷|¤ž CCöùH³ ™ç#­04Ì]ïÜÉÃ<ïçyC‘‡<ä!yÈCò‡<ä!Ïýx.rÚglÁ1ñ +>SàÆËx¢âeögÎUܸüìÏ.äòÌÿì¹þvåüª³<1Ÿ]89 Æ/ÌÿìÂNÿ²þïŽ_˜ýÙ!ì|¤b~›ôÙ…óÍ“~ŠgF¼óTžç%¬‚E<'úÛ˜R€5<2ðÈOÐ‘ÐÆ¡È²¡—J‡?šÅ„¹„§9¾ÇZÔÛíuQ«dóÇÿDñ`Ï9æ1íóUÔAI·ã±”â–NûÈ]ÛÇ“Ñúá9‡…¥. ×£³ÐnµÞGz~» Ùa:y¤c¬ã¹Q:ÖtÎ×äÔÈCò‡<ä!yÈCòç©<Œw&ÏÆ;3Þ¹}ŠñÎxï<>~ï<ãweyiä!Ï»jŒ<ä!yÈCò‡<äù!¦Ç$ ã¶¿ÜHz.Èž®bžÀÓ–óoD¶¼])ü1µŒOäƒ}sãÌ]åäÉ÷Å Xiß¾¼ R‹koþl97äÙêÙä©ÀÔ‰ýmG´<ŸçÓSxÔ°•²äµrxÜYÈ㑞ñ#~7ËáÑÂüIl›½öç·ÒNUÍfí=9ãÇ|Ïïmñ¶IEND®B`‚urwid-1.3.1/docs/tutorial/input4.png0000664000175000017500000000024412615524560016742 0ustar ianian00000000000000‰PNG  IHDRl-n ÑØbKGDÿ‡Ì¿ pHYs  ÒÝ~üHIDATXÃíÔ1 @QïOïACÐÔ ¼?¸>l,Ú±%Ƭr‡ ;çÿX–30ØåÙŽº+ƒÁ`0 ƒÁ`o`x¬ß½”¾IEND®B`‚urwid-1.3.1/docs/tutorial/cmenu1.png0000664000175000017500000000221612615524560016710 0ustar ianian00000000000000‰PNG  IHDR)Ò3Ði0bKGDÿ‡Ì¿ pHYs  ÒÝ~ü2IDATxÚíÛ[’ã DÑÚÿ>ÙG´m‘tSBàËDLTx4¶tŒ¤ÁMkRéñý»Ø\jRH†Ú[j$Ôë¯Íš£Ôž]i¬ØÆRƒ»RH¾Zí|rxW¤B ©5¤¬ð1f—{óLb+ÕcRë?øE¤Fg vôS¥z6ÞNêõç5bþõøü“¥,ùÆŸm¦_ú̪,•_fùùÿÚø³Ùg›çHye –F{®.a–nŸ_"¥¯Ô¥Ö/ºûTèéS¯üç+º[–PØå¿]§^{œÞ;ƒBŸ=¡õô¶ì)ÁW*¥ºº÷…ëâØñòm±töýK*{òôK]¤ç¢ÇyfûBê/Rû4¤ž%u^`d]ñqÅsË\+|ºÝùÍ ºc‡…u¥ŠGá%e—ŸmñùÝiŽW&/βzï<×%?L³Óè0ÿ‰þ´’ä Tþ38ŠG™.D³Ên}™Tå0­tö»õ¡M—²ÚYè0×¥vE/Ý!­xÑ›¾$øg ªDÊ ×)«HÅ!à©ð‡ºi®KåÉ3 \Žó.~%_cz«”¾à¡b£™eÆ<3Ç}k§Ž×ÿÍ–Zª!…”§+ŒD)ÖÍ 5XŠF¢ëf,Å #QŠu3H –b…‘ܧ<Þ)¤B ©iR¤.¢YRƒ¥H]D)²¤K‘ºˆRd H –"u‘û”Ç»"…RH!5MŠÔE”"K@j°©‹(å%lÖü¤6m© Pj–€ÔWK¹¤.´yRH!õp)ŸçÚ ¡'K€C–B#t¦.AœÁAq-…†Rb”.‚V0šQRH!…Ô*RÜÝZY©‹í‚,A-ê‘BC)ÈÔ)] ­`4£6¤B ©U¤¸»µ ²R)4ÚY‚Z Õ#…†R%¨RºZÁhFmH!…R«Hqwkd ¤.>Rh´ ²µ@ªG ¥ KP ¤t)´‚ÑŒÚB )¤V‘âîÖ*ÈH]|¤Ðhd jTJA– HéR h£µ!…RH­"ÅÝ­U%ºøH¡Ñ.ÈÔ©)4”‚,A-Ò¥@Ð F3jC ©Ñí·ô ;yÁIEND®B`‚urwid-1.3.1/docs/tutorial/attr2.png0000664000175000017500000000037012615524560016553 0ustar ianian00000000000000‰PNG  IHDRZ‡ëf¦` PLTEÍÍååå¶<% pHYs  ÒÝ~ü’IDATHÇíÑ1„ …ah³Þð‘ŒýXpŽ@Áì@aD4 ‰ÅóWæ 1øœ&˲¬Á>Ú‚sYæ_péÎÁ ~ßRq+—Ä®x Ëù'÷òpc¾%'p¹Ïè>¯øª7DÙ9?4îw9ï,µë÷êž±q*Þž×_ØíûáÕû— óòÎüön–eYÖ_ögv.$h‘hIEND®B`‚urwid-1.3.1/docs/tutorial/adventure.py.xdotool0000664000175000017500000000023712615524560021051 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 23 16 key --window $RXVTWINDOWID Return Down Down key --window $RXVTWINDOWID Return Down key --window $RXVTWINDOWID Return urwid-1.3.1/docs/tutorial/attr.py.xdotool0000664000175000017500000000024412615524560020024 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 21 7 windowsize --usehints $RXVTWINDOWID 10 9 windowsize --usehints $RXVTWINDOWID 30 3 windowsize --usehints $RXVTWINDOWID 15 2 urwid-1.3.1/docs/tutorial/adventure.py0000664000175000017500000000500712615524560017362 0ustar ianian00000000000000import urwid class ActionButton(urwid.Button): def __init__(self, caption, callback): super(ActionButton, self).__init__("") urwid.connect_signal(self, 'click', callback) self._w = urwid.AttrMap(urwid.SelectableIcon(caption, 1), None, focus_map='reversed') class Place(urwid.WidgetWrap): def __init__(self, name, choices): super(Place, self).__init__( ActionButton([u" > go to ", name], self.enter_place)) self.heading = urwid.Text([u"\nLocation: ", name, "\n"]) self.choices = choices # create links back to ourself for child in choices: getattr(child, 'choices', []).insert(0, self) def enter_place(self, button): game.update_place(self) class Thing(urwid.WidgetWrap): def __init__(self, name): super(Thing, self).__init__( ActionButton([u" * take ", name], self.take_thing)) self.name = name def take_thing(self, button): self._w = urwid.Text(u" - %s (taken)" % self.name) game.take_thing(self) def exit_program(button): raise urwid.ExitMainLoop() map_top = Place(u'porch', [ Place(u'kitchen', [ Place(u'refrigerator', []), Place(u'cupboard', [ Thing(u'jug'), ]), ]), Place(u'garden', [ Place(u'tree', [ Thing(u'lemon'), Thing(u'bird'), ]), ]), Place(u'street', [ Place(u'store', [ Thing(u'sugar'), ]), Place(u'lake', [ Place(u'beach', []), ]), ]), ]) class AdventureGame(object): def __init__(self): self.log = urwid.SimpleFocusListWalker([]) self.top = urwid.ListBox(self.log) self.inventory = set() self.update_place(map_top) def update_place(self, place): if self.log: # disable interaction with previous place self.log[-1] = urwid.WidgetDisable(self.log[-1]) self.log.append(urwid.Pile([place.heading] + place.choices)) self.top.focus_position = len(self.log) - 1 self.place = place def take_thing(self, thing): self.inventory.add(thing.name) if self.inventory >= set([u'sugar', u'lemon', u'jug']): response = urwid.Text(u'You can make lemonade!\n') done = ActionButton(u' - Joy', exit_program) self.log[:] = [response, done] else: self.update_place(self.place) game = AdventureGame() urwid.MainLoop(game.top, palette=[('reversed', 'standout', '')]).run() urwid-1.3.1/docs/tutorial/input2.png0000664000175000017500000000032212615524560016735 0ustar ianian00000000000000‰PNG  IHDRl-n ÑØbKGDÿ‡Ì¿ pHYs  ÒÝ~üvIDATXÃíÔA €@…áwÿ{¾{D‘aSÔ"3‚ßÅ ºøFaFn µcjºç ¦Ÿc‘ϱÔ"‰Jjy­<\¼â”Æ™·–ƒ¬Ã|¹‹­`J{,yg:Œ8LæLo`Þ¯JQË­:¬ç»û›ú´¢é"äeIEND®B`‚urwid-1.3.1/docs/tutorial/adventure2.png0000664000175000017500000000226412615524560017602 0ustar ianian00000000000000‰PNG  IHDRÏð‚ÆoûbKGDÿ‡Ì¿ pHYs  ÒÝ~üXIDATxÚíÝm’¤ €áÜÿž¹ÇÖÔ¬-à×$ öëY·ôi‘" ´è»Áƒkónóùdà%öÈòWêoNêïQš‹Í­GL†•—<×£=ýbãì#í!DGzŠrèÑYɹ>s<ËÊz}dÏsàþÙ¹W#š|}â<Õj?^ZÞ®yŠØvï_ÃodD~}p-^pÊ[›¿ ©"ã…êÚîy/¬ûle˜Z¿9y¿pWïðàÁƒU³IœMC8Á…ô=y@ûÚFv೺Ã:ëàBMòÞ>ÙÕ6Ú©®ÓžGxô˜'f¼sU&ÝÂŽÇL¦d{CÉþ½˜2?Rïä½z¸¼âzÖ/œò8# ÍdJαyÂÆ;›äN@×íÛéL¦äô†ºT³ü€ùžîë1Þ9zþ–Ç,z£#èpÞßs*—'xÚl~k@©F¬[ÖÆŠšTºdö»òùĤ*v®Z?%¼8!‹Õò5´L¾¬­+uªÏNŸT·ó¸ÉíJáñ7E–·­J§ÄzÚ\ÖVa–GëëëÑÞÿ{ýóý³Þ-•'äþ±ylTbUu¤ýT¿'½]¿U×'¶~s€šðEÍYÞæ‘ &¸Ž>å÷,xðàÁƒ¦Þ›I:*äÇÔô±¦ºúv É # yWÁëxÎ×ÃÕŽä¹ÏƒÿŠúÇ_ÊÊ~«e×rvÚéÎ<Ø>Q>9”ÏUåÃášÀJ;=–GnòØÝF©ƒúLžC?sä‘Q<ˆiŸ1<Û—OûàŠçÆûsñ®zð Ðb~ÛïÕP}'<Ò ¬u©?O\1–"ÏïòyÈCò‡<ä!yÈCò'…î"Í™\dð<» žÀžG—Y§ùs›C½ €v²®”ñï,£²¶ôà²Ð€ËþgÍú¡¾ûã WWn§NBx€Z\…gÍ-»ó(·üœ­<›gë‹!åà,ÐL¸©ºìå,ÃmG‚yüì…CC5<Èâq²w&œ©¾P} D—ÈA©Ÿâé´FZ´Öáy[¬½àU§÷®ð2?{¡ˆzÀ‰{0Ö°ÊQ6ÌùÑàöñã)¾ÚöBjëã9ôÀÚ½ÿc&h‘ñú Ï^Pú[-)úÀÓ^(ÚöŠg{áSçL`¨~Sr}aV®/‡<ä!yÈCò‡<ä!yÒxâò#dˆÌ'4ÂñÿÍ<þù‘¶Ï¼@†ÐüH’È›)?!4?Ò€@†ØüHù ¡ù‘$?!6?ÒA¿%2püFò‡<ä!yÈCò‡<ä!yžð`Ìeã#í–VÊ4S~¤ümÌåáï?`Û…PiyŠ_$Ðs8*Þ9y›‚›ú 3?Ò,P~?l˜Å±ám<^ƒù78ž *ä!yÈCò‡<ä!yÈó;¸‹LÛvAì¿0b›C0^Æãï,ÙÛ.Ü{pýþñÙÛ.Äòäo»p¯¿}“¿ê.϶ 7õAgüBþ¶ 'ýKûûqüBö¶ *‚[~¤ƒ~KÚvá„A½³eÊOñ¬¸¿3ËBE¾¬0U{Ëöïäþ—êŸÿ/Þã=<¨õC9è‘§2ªóeßF?“¶l$XS_š/“¾?òÏ)kðX÷wìyRþ0;(Fûà”óý‡-Ÿ×æ¯M\¾?¸nŸ9UÞn§ÙúMQkK¨DÓ“¶Ò/ò(oÿ€þöDÉŠ„d ÛIEND®B`‚urwid-1.3.1/docs/tutorial/attr4.png0000664000175000017500000000035712615524560016562 0ustar ianian00000000000000‰PNG  IHDR‡{àå PLTEÍåååÍô>6t pHYs  ÒÝ~ü‰IDAT8ËíÑ1 Ã0 P_¢÷êòÖî ¹z¢3ZIÐR»4é˜j°?âa!œRº`¥®Éë+B( ÕèŠk„õ[‰óEèk'¤dcÁÓÑ*ÙIH27—˜ ÷á KHbMËxKõÖsY‚l%:rt[7|!QüV2M‰„_GV6RzPôÊò÷o„$I’„èðbô…TÙ°IEND®B`‚urwid-1.3.1/docs/tutorial/qa1.png0000664000175000017500000000061412615524560016202 0ustar ianian00000000000000‰PNG  IHDR½iJ}ÕbKGDÿ‡Ì¿ pHYs  ÒÝ~ü0IDATxÚíÖ …áwÿ{öÆ(¬ƒâ:uÌO·±~”Â&[¹ =zôèÑ£G=zôèÑ£G=zôèÑ£ÿS½Ê(rѼñêÞ>Ñÿrjt,ø#}™©2eêç®þï.iû)Ý˹~5lËög,©ÚÝ­A_ ã­n2U‹ºÕãcùÃ]JÔ}Ô-¥·¬>jB/;¯ß*f¨Q9#}œ‚Ë•Àöè£eé•Ƚ«œ¡^ƒÜ[Z¯:µÑ¤ê^ïé5Ò'¶%µ9{ìÚï1í$ÞŽÔÝ% ŠÕÔR6÷ìÑ“_ _{Ò²z;ü*¹@_›-Ø”{©¡G=zôèÑ£Ÿ¬_úsE4zôèÑ£G=zôèÑ£G=zôèÑ£G=zôèÑ£GýôvÍ*øÛIEND®B`‚urwid-1.3.1/docs/tutorial/cmenu.py.xdotool0000664000175000017500000000022512615524560020160 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 33 14 key --window $RXVTWINDOWID Return key --window $RXVTWINDOWID Return Down key --window $RXVTWINDOWID Return urwid-1.3.1/docs/tutorial/multiple4.png0000664000175000017500000000204312615524560017435 0ustar ianian00000000000000‰PNG  IHDRÏÃ(øbKGDÿ‡Ì¿ pHYs  ÒÝ~üÇIDATxÚí rà Euÿ{rN§‰ÑÊb'6¢ß3í4ž%D‚ø5•½Úއ~mé¯÷´`ØÅ>oÏûóÁôãç0Q^+1ûTžÄ^Gx÷·Üãçf{Ä·½ð€<x^Á™ý^°‹*Ü6úùÑ£<ç/Ý(ÖÁ1šhð)q‰AžÃªõ#·1´˜|å@$*ªÖa|ßaŽ7Û"Þ¿ˆ&|"ÑyÇrÙúÀë:ìþDý¬ge3‘®º”ÃsôÚàÈÚÔ¥„¯phŸ±ÂTÄð„¡¥ÉóªÅª_ç/LaÔö*ŒÚG¾û(2°µ;´äøQÕ½Òu°"Tx\Òñ`püÔ«±°&ÿ0–úŽ;çT·k|¼°Îíôc w3'öÅ)ƒŽ¿)ãðtÂ<ÏæŒ'ú(Éó2>×ZbÓ¾/,ÏÓp”|Û.©³›7+Ï.þÈyêþ^¶5)»}ê‡øxÊ<ÄnƒñCÒ>¹yèÐ4eçQ»+ÏŸÉ&y¶:Àð\àY_¿0õ&~¡½4áð¤Ð/”yž•õ º·Ñ] ý‚q< «dÑ/È—q‚(~u¨Ë“A¿P‚„EŸg5ýÏáÙ„…˳´~-Ô˜„…M«•çèVå~<àxÀð€<àxÀ£x _¸çDsÐ/@¿ýô "&B¿ýBÃß _¸ú…ÅyÖ8þÃó’óle&JýßÕÛ¡Sx†g‰ç/ôà³=agiý_š³ÕÅøI¡_àWµÕ[fg»?"!n‘ž\@=õÔSO=õÔSO=õÔSO=õÔSO=õÔSO=õ/Õ#÷"Ö|¯¿ø”#ú;§}Á‹ôy¦ò”AÏ]y¯¾Âö”«çOPÕ+Íê¶’ü$õˆ£áÔ…30µµšL”‹êê©ÛªÕW¸·ª…ô)ª7»Ð#ë·ˆqõ;‘ãéí!ÈmU!°ºwÙYzÆ>EŽ«‡3ö)¬GéCÝj£ Å=æôðôe í˜ý®kL»ƒØËÔ1ÖXm5±û Öè/o §é±úÔÍJ–ÖßP^ Ç]«w‰±ŠÎስ u fõS©WTŒèÝi\?‡Çôb¯–‰qÉcwrÝvªPi°Ôø¹ŒnݿɂØäš(ÙÂN®k†/¼êÖE⼬îÜzNÀ_Ï®/8©þ]zs·Ãcô{ãz¯õgèeö[6õëÊZ׉ûf‡«þV S~!fn…¸Gêëå‹•ân8©ö*‘3§_3r´~ä, ×¹jý+.ô¿¢VŠœsêSƒþÑ9æ½…zê©§žzê©§žzê©§žzê©§žzê©§žú+Ë:–« ¸›RIEND®B`‚urwid-1.3.1/docs/tutorial/menu25.png0000664000175000017500000000055612615524560016640 0ustar ianian00000000000000‰PNG  IHDRÏxp%ùNbKGDÿ‡Ì¿ pHYs  ÒÝ~üIDATxÚíÕQ‚0Àwÿ{ö&*–UDBN¿Ú- »,)×áááááááááááááááááááááááááááááááááááááéxòë+~ãã®7ñ§òüžþ-ž{PÊu<5GÏêÈü¨º4TPú“ùN~N’òŠ e~Î6OSrYòÔ¥Œ÷,O‡W—׿IHÚ»æOé3ÊZOézÊ9<µ––ºY&.Íg›NS‹ðxÏ,Q½ü”už–Ÿ¬˜ìäÙ#?m§š€šzÉÖþ6öL˵¹ÒÞâ¸ÄéÏ{nêÛÅ­–h÷@IEND®B`‚urwid-1.3.1/docs/tutorial/highcolors.py.xdotool0000664000175000017500000000005112615524560021207 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 26 9 urwid-1.3.1/docs/tutorial/attr1.png0000664000175000017500000000037712615524560016561 0ustar ianian00000000000000‰PNG  IHDR½iÊš PLTEÍÍååå¶<% pHYs  ÒÝ~ü™IDATXÃíÒA à …á­—èÑz‰7 {ñ>#x€,´:ÅE[b¡M!‹÷m"âdYˆˆˆˆÎä:`æ6V]~ ˆðjkÔÙ£ôùY ÀSàªö@ª¾¸Ø&cÞ |Mbïn›$D´×ýÞOŽ”êöàCðøèG°No)n¾Ø-…Ð@›É‡ý ˆˆˆˆÎäQÉ”¾ÀïIEND®B`‚urwid-1.3.1/docs/changelog.rst0000664000175000017500000010271612615524560015636 0ustar ianian00000000000000 Changelog --------- Urwid 1.3.1 =========== 2015-11-01 * Fix for screen not getting reset on exception regression (by Rian Hunter) * AttrSpec objects are now comparable (by Random User) * MonitoredList now has a clear method if list has a clear method (by neumond) * Fix for BarGraph hlines sort order (by Heiko Noordhof) * Fix for final output not appearing on exit with some terminals now that extra newline was removed (by Jared Winborne) * Fix for a resizing bug in raw_display (by Esteban null) Urwid 1.3.0 =========== 2014-10-17 * New AsyncioEventLoop for Python 3.4, Python 3.x with asyncio package or Python 2 with trollius package (by Alex Munroe, Jonas Wielicki, with earlier work by Kelketek Rritaa) * Screen classes now call back to MainLoop using event loop alarms instead of passing timeout values to MainLoop (by Alex Munroe) * Add support for bright backgrounds on linux console (by Russell Warren) * Allow custom sorting of MonitoredList (by Tony Cebzanov) * Fix support for negative indexes with MonitoredFocusList (by Heiko Noordhof) * Documentation fixes (by Ismail, Matthew Mosesohn) Urwid 1.2.2 =========== 2014-10-05 * Fix for a serious raw_display performance regression (by Anton Khirnov) * Fix for high color palette detection (by extempo) * Small changes to enable windows support (by Jeanpierre Devin) Urwid 1.2.1 =========== 2014-04-04 * Fix false failures of event loop tests * Remove extra newline generated on exit of raw_display * Documentation fixes (by Paul Ivanov) Urwid 1.2.0 =========== 2014-02-09 * Add support for PyPy, drop support for Python 2.4, 2.5 * Signals now support using weakly referenced arguments to help avoid leaking objects when a signal consumer is no longer referenced (by Matthijs Kooijman) * Add TornadoEventLoop class (by Alexander Glyzov) * Update GlibEventLoop to use python-gi for Python3 compatibility (by Israel Garcia) * Automate testing with Python 2.6, 2.7, 3.2, 3.3 and PyPy using travis-ci * New container method get_focus_widgets() (by Matthijs Kooijman) * Add support for double and triple click mouse events (by Igor KotrasiÅ„ski) * Allow disabling and re-enabling of mouse tracking (by Jim Garrison) * Create section in docs for example program screenshots generated as images like the tutorial examples * Add suggested basic color combination images to manual * Fall back to 80x24 if screen size detection fails * Fix screen.stop(), screen.start() disabling mouse events * Fix to make GridFlow v_sep argument behave as documented * Fix for registering high palette entries in the form "hX" where X > 15 so that basic colors are applied in 88-color mode * Fix for raw_display clear-right escape not working with standout attribute on some terminals * Fix for Terminal widget select loop: retry when interrupted Urwid 1.1.2 =========== 2013-12-30 * Move to urwid.org and use sphinx docs for generating whole site, move changelog to docs/changelog.rst * Fix encoding exceptions when unicode used on non-UTF-8 terminal * Fix for suspend and resume applications with ^Z * Fix for tmux and screen missing colors on right bug * Fix Pile zero-weighted items and mouse_event when empty * Fix Terminal select() not retrying when interrupted by signal * Fix for Padding.align and width change not invalidating Urwid 1.1.1 =========== 2012-11-15 * Fix for Pile not changing focus on mouse events * Fix for Overlay.get_cursor_coords() Urwid 1.1.0 =========== 2012-10-23 * New common container API: focus, focus_position, contents, options(), get_focus_path(), set_focus_path(), __getitem__, __iter__(), __reversed__() implemented across all included container widgets A full description doesn't fit here, see the Container Widgets section in the manual for details * New Sphinx-based documentation now included in source: Tutorial rewritten, manual revised and new reference based on updated docstrings (by Marco Giusti, Patrick Totzke) * New list walker SimpleFocusListWalker like SimpleListWalker but updates focus position as items are inserted or removed * New decoration widget WidgetDisable to disable interaction with the widgets it wraps * SelectableIcon selectable text widget used by button widgets is now documented (available since 0.9.9) * Columns widget now tries to keep column in focus visible, hiding columns on the left when necessary * Padding widget now defaults to ('relative', 100) instead of 'pack' so that left and right parameters are more useful and more child widgets are supported * New list walker "API Version 2" that is simpler for many list walker uses; "API Version 1" will still continue to be supported * List walkers may now allow iteration from the absolute top or bottom of the list if they provide a positions() method * raw_display now erases to the end of the line with EL escape sequence to improve copy+paste behavior for some terminals * Filler now has top and bottom parameters like Padding's left and right parameters and accepts 'pack' instead of None as a height value for widgets that calculate their own number of rows * Pile and Columns now accepts 'pack' instead of 'flow' for widgets that calculate their own number of rows or columns * Pile and Columns now accept 'given' instead of 'fixed' for cases where the number of rows or columns are specified by the container options * Pile and Columns widgets now accept any iterable to their __init__() methods * Widget now has a default focus_position property that raises an IndexError when read to be consistent with new common container API * GridFlow now supports multiple cell widths within the same widget * BoxWidget, FlowWidget and FixedWidget are deprecated, instead use the sizing() function or _sizing attribute to specify the supported sizing modes for your custom widgets * Some new shift+arrow and numpad input sequences from RXVT and xterm are now recognized * Fix for alarms when used with a screen event loop (e.g. curses_display) * Fix for raw_display when terminal width is 1 column * Fixes for a Columns.get_cursor_coords() regression and a SelectableIcon.get_cursor_coords() bug * Fixes for incorrect handling of box columns in a number of Columns methods when that column is selectable * Fix for Terminal widget input handling with Python 3 Urwid 1.0.3 =========== 2012-11-15 * Fix for alarms when used with a screen event loop (e.g. curses_display) * Fix for Overlay.get_cursor_coords() Urwid 1.0.2 =========== 2012-07-13 * Fix for bug when entering Unicode text into Edit widget with bytes caption * Fix a regression when not running in UTF-8 mode * Fix for a MainLoop.remove_watch_pipe() bug * Fix for a bug when packing empty Edit widgets * Fix for a ListBox "contents too long" error with very large Edit widgets * Prevent ListBoxes from selecting 0-height selectable widgets when moving up or down * Fix a number of bugs caused by 0-height widgets in a ListBox Urwid 1.0.1 =========== 2011-11-28 * Fix for Terminal widget in BSD/OSX * Fix for a Filler mouse_event() position bug * Fix support for mouse positions up to x=255, y=255 * Fixes for a number of string encoding issues under Python 3 * Fix for a LineBox border __init__() parameters * Fix input input of UTF-8 in tour.py example by converting captions to unicode * Fix tutorial examples' use of TextCanvas and switch to using unicode literals * Prevent raw_display from calling tcseattr() or tcgetattr() on non-ttys * Disable curses_display external event loop support: screen resizing and gpm events are not properly supported * Mark PollingListWalker as deprecated Urwid 1.0.0 =========== 2011-09-22 * New support for Python 3.2 from the same 2.x code base, requires distribute instead of setuptools (by Kirk McDonald, Wendell, Marien Zwart) everything except TwistedEventLoop and GLibEventLoop is supported * New experimental Terminal widget with xterm emulation and terminal.py example program (by aszlig) * Edit widget now supports a mask (for passwords), has a insert_text_result() method for full-field validation and normalizes input text to Unicode or bytes based on the caption type used * New TreeWidget, TreeNode, ParentNode, TreeWalker and TreeListBox classes for lazy expanding/collapsing tree views factored out of browse.py example program, with new treesample.py example program (by Rob Lanphier) * MainLoop now calls draw_screen() just before going idle, so extra calls to draw_screen() in user code may now be removed * New MainLoop.watch_pipe() method for subprocess or threaded communication with the process/thread updating the UI, and new subproc.py example demonstrating its use * New PopUpLauncher and PopUpTarget widgets and MainLoop option for creating pop-ups and drop-downs, and new pop_up.py example program * New twisted_serve_ssh.py example (by Ali Afshar) that serves multiple displays over ssh from the same application using Twisted and the TwistedEventLoop * ListBox now includes a get_cursor_coords() method, allowing nested ListBox widgets * Columns widget contents may now be marked to always be treated as flow widgets for mixing flow and box widgets more easily * New lcd_display module with support for CF635 USB LCD panel and lcd_cf635.py example program with menus, slider controls and a custom font * Shared command_map instance is now stored as Widget._command_map class attribute and may be overridden in subclasses or individual widgets for more control over special keystrokes * Overlay widget parameters may now be adjusted after creation with set_overlay_parameters() method * New WidgetPlaceholder widget useful for swapping widgets without having to manipulate a container widget's contents * LineBox widgets may now include title text * ProgressBar text content and alignment may now be overridden * Use reactor.stop() in TwistedEventLoop and document that Twisted's reactor is not designed to be stopped then restarted * curses_display now supports AttrSpec and external event loops (Twisted or GLib) just like raw_display * raw_display and curses_display now support the IBMPC character set (currently only used by Terminal widget) * Fix for a gpm_mev bug preventing user input when on the console * Fix for leaks of None objects in str_util extension * Fix for WidgetWrap and AttrMap not working with fixed widgets * Fix for a lock up when attempting to wrap text containing wide characters into a single character column Urwid 0.9.9.2 ============= 2011-07-13 * Fix for an Overlay get_cursor_coords(), and Text top-widget bug * Fix for a Padding rows() bug when used with width=PACK * Fix for a bug with large flow widgets used in an Overlay * Fix for a gpm_mev bug * Fix for Pile and GraphVScale when rendered with no contents * Fix for a Python 2.3 incompatibility (0.9.9 is the last release to claim support Python 2.3) Urwid 0.9.9.1 ============= 2010-01-25 * Fix for ListBox snapping to selectable widgets taller than the ListBox itself * raw_display switching to alternate buffer now works properly with Terminal.app * Fix for BoxAdapter backwards incompatibility introduced in 0.9.9 * Fix for a doctest failure under powerpc * Fix for systems with gpm_mev installed but not running gpm Urwid 0.9.9 =========== 2009-11-15 * New support for 256 and 88 color terminals with raw_display and html_fragment display modules * New palette_test example program to demonstrate high color modes * New AttrSpec class for specifying specific colors instead of using attributes defined in the screen's palette * New MainLoop class ties together widgets, user input, screen display and one of a number of new event loops, removing the need for tedious, error-prone boilerplate code * New GLibEventLoop allows running Urwid applications with GLib (makes D-Bus integration easier) * New TwistedEventLoop allows running Urwid with a Twisted reactor * Added new docstrings and doctests to many widget classes * New AttrMap widget supports mapping any attribute to any other attribute, replaces AttrWrap widget * New WidgetDecoration base class for AttrMap, BoxAdapter, Padding, Filler and LineBox widgets creates a common method for accessing and updating their contained widgets * New left and right values may be specified in Padding widgets * New command_map for specifying which keys cause actions such as clicking Button widgets and scrolling ListBox widgets * New tty_signal_keys() method of raw_display.Screen and curses_display.Screen allows changing or disabling the keys used to send signals to the application * Added helpful __repr__ for many widget classes * Updated all example programs to use MainLoop class * Updated tutorial with MainLoop usage and improved examples * Renamed WidgetWrap.w to _w, indicating its intended use as a way to implement a widget with other widgets, not necessarily as a container for other widgets * Replaced all tabs with 4 spaces, code is now more aerodynamic (and PEP 8 compliant) * Added saving of stdin and stdout in raw_display module allowing the originals to be redirected * Updated BigText widget's HalfBlock5x4Font * Fixed graph example CPU usage when animation is stopped * Fixed a memory leak related to objects listening for signals * Fixed a Popen3 deprecation warning Urwid 0.9.8.4 ============= 2009-03-13 * Fixed incompatibilities with Python 2.6 (by Friedrich Weber) * Fixed a SimpleListWalker with emptied list bug (found by Walter Mundt) * Fixed a curses_display stop()/start() bug (found by Christian Scharkus) * Fixed an is_wide_character() segfault on bad input data bug (by Andrew Psaltis) * Fixed a CanvasCache with render() used in both a widget and its superclass bug (found by Andrew Psaltis) * Fixed a ListBox.ends_visible() on empty list bug (found by Marc Hartstein) * Fixed a tutorial example bug (found by Kurtis D. Rader) * Fixed an Overlay.keypress() bug (found by Andreas Klöckner) * Fixed setuptools configuration (by Andreas Klöckner) Urwid 0.9.8.3 ============= 2008-07-14 * Fixed a canvas cache memory leak affecting 0.9.8, 0.9.8.1 and 0.9.8.2 (found by John Goodfellow) * Fixed a canvas fill_attr() bug (found by Joern Koerner) Urwid 0.9.8.2 ============= 2008-05-19 * Fixed incompatibilities with Python 2.3 * Fixed Pile cursor pref_col bug, WidgetWrap rows caching bug, Button mouse_event with no callback bug, Filler body bug triggered by the tutorial and a LineBox lline parameter typo. Urwid 0.9.8.1 ============= 2007-06-21 * Fixed a Filler render() bug, a raw_display start()/stop() bug and a number of problems triggered by very small terminal window sizes. Urwid 0.9.8 =========== 2007-03-23 * Rendering is now significantly faster. * New Widget base class for all widgets. It includes automatic caching of rows() and render() methods. It also adds a new __super attribute for accessing methods in superclasses. Widgets must now call self._invalidate() to notify the cache when their content has changed. To disable caching in a widget set the class variable no_cache to a list that includes the string "render". * Canvas classes have been reorganized: Canvas has been renamed to TextCanvas and Canvas is now the base class for all canvases. New canvas classes include BlankCanvas, SolidCanvas and CompositeCanvas. * External event loops may now be used with the raw_display module. The new methods get_input_descriptors() and get_input_nonblocking() should be used instead of get_input() to allow input processing without blocking. * The Columns, Pile and ListBox widgets now choose their first selectable child widget as the focus widget by defaut. * New ListWalker base class for list walker classes. * New Signals class that will be used to improve the existing event callbacks. Currently it is used for ListWalker objects to notify their ListBox when their content has changed. * SimpleListWalker now behaves as a list and supports all list operations. This class now detects when changes are made to the list and notifies the ListBox object. New code should use this class to wrap lists of widgets before passing them to the ListBox constructor. * New PollingListWalker class is now the default list walker that is used when passing a simple list to the ListBox constructor. This class is intended for backwards compatibility only. When this class is used the ListBox object is unable to cache its render() method. * The curses_display module can now draw in the lower-right corner of the screen. * All display modules now have start() and stop() methods that may be used instead of calling run_wrapper(). * The raw_display module now uses an alternate buffer so that the original screen can be restored on exit. The old behaviour is available by seting the alternate_buffer parameter of start() or run_wrapper() to False. * Many internal string processing functions have been rewritten in C to improve their performance. * Compatible with Python >= 2.2. Python 2.1 is no longer supported. Urwid 0.9.7.2 ============= 2007-01-03 * Improved performance in UTF-8 mode when ASCII text is used. * Fixed a UTF-8 input bug. * Added a clear() function to the the display modules to force the screen to be repainted on the next draw_screen() call. Urwid 0.9.7.1 ============= 2006-10-03 * Fixed bugs in Padding and Overlay widgets introduced in 0.9.7. Urwid 0.9.7 =========== 2006-10-01 * Added initial support for fixed widgets - widgets that have a fixed size on screen. Fixed widgets expect a size parameter equal to (). Fixed widgets must implement the pack(..) function to return their size. * New BigText class that draws text with fonts made of grids of character cells. BigText is a fixed widget and doesn't do any alignment or wrapping. It is intended for banners and number readouts that need to stand out on the screen. Fonts: Thin3x3Font, Thin4x3Font, Thin6x6Font (full ascii) UTF-8 only fonts: HalfBlock5x4Font, HalfBlock6x5Font, HalfBlockHeavy6x5Font, HalfBlock7x7Font (full ascii) New function get_all_fonts() may be used to get a list of the available fonts. * New example program bigtext.py demonstrates use of BigText. * Padding class now has a clipping mode that pads or clips fixed widgets to make them behave as flow widgets. * Overlay class can now accept a fixed widget as the widget to display "on top". * New Canvas functions: pad_trim() and pad_trim_left_right(). * Fixed a bug in Filler.get_cursor_coords() that causes a crash if the contained widget's get_cursor_coords() function returns None. * Fixed a bug in Text.pack() that caused an infinite loop when the text contained a newline. This function is not currently used by Urwid. * Edit.__init__() now calls set_edit_text() to initialize its text. * Overlay.calculate_padding_filler() and Padding.padding_values() now include focus parameters. Urwid 0.9.6 =========== 2006-08-22 * Fixed Unicode conversion and locale issues when using Urwid with Python < 2.4. The graph.py example program should now work properly with older versions of Python. * The docgen_tutorial.py script can now write out the tutorial example programs as individual files. * Updated reference documentation table of contents to show which widgets are flow and/or box widgets. * Columns.set_focus(..) will now accept an integer or a widget as its parameter. * Added detection for rxvt's HOME and END escape sequences. * Added support for setuptools (improved distutils). Urwid 0.9.5 =========== 2006-06-14 * Some Unicode characters are now converted to use the G1 alternate character set with DEC special and line drawing characters. These Unicode characters should now "just work" in almost all terminals and encodings. When Urwid is run with the UTF-8 encoding the characters are left as UTF-8 and not converted. The characters converted are: \u00A3 (£), \u00B0 (°), \u00B1 (±), \u00B7 (·), \u03C0 (Ï€), \u2260 (≠), \u2264 (≤), \u2265 (≥), \u23ba (⎺), \u23bb (⎻), \u23bc (⎼), \u23bd (⎽), \u2500 (─), \u2502 (│), \u250c (┌), \u2510 (â”), \u2514 (â””), \u2518 (┘), \u251c (├), \u2524 (┤), \u252c (┬), \u2534 (â”´), \u253c (┼), \u2592 (â–’), \u25c6 (â—†) * New SolidFill class for filling an area with a single character. * New LineBox class for wrapping widgets in a box made of line- drawing characters. May be used as a box widget or a flow widget. * New example program graph.py demonstrates use of BarGraph, LineBox, ProgressBar and SolidFill. * Pile class may now be used as a box widget and contain a mix of box and flow widgets. * Columns class may now contain a mix of box and flow widgets. The box widgets will take their height from the maximum height of the flow widgets. * Improved the smoothness of resizing with raw_display module. The module will now try to stop updating the screen when a resize event occurs during the update. * The Edit and IntEdit classes now use their set_edit_text() and set_edit_pos() functions when handling keypresses, so those functions may be overridden to catch text modification. * The set_state() functions in the CheckBox and RadioButton classes now have a do_callback parameter that determines if the callback function registered will be called. * Fixed a newly introduced incompatibility with python < 2.3. * Fixed a missing symbol in curses_display when python is linked against libcurses. * Fixed mouse handling bugs in the Frame and Overlay classes. * Fixed a Padding bug when the left or right has no padding. Urwid 0.9.4 =========== 2006-05-30 * Enabled mouse handling across the Urwid library. Added a new mouse_event() method to the Widget interface definition and to the following widgets: Edit, CheckBox, RadioButton, Button, GridFlow, Padding, Filler, Overlay, Frame, Pile, Columns, BoxAdapter and ListBox. Updated example programs browse.py, calc.py, dialog.py, edit.py and tour.py to support mouse input. * Released the files used to generate the reference and tutorial documentation: docgen_reference.py, docgen_tutorial.py and tmpl_tutorial.html. The "docgen" scripts write the documentation to stdout. docgen_tutorial.py requires the Templayer HTML templating library to run: http://excess.org/templayer/ * Improved Widget and List Walker interface documentation. * Fixed a bug in the handling of invalid UTF-8 data. All invalid characters are now replaced with '?' characters when displayed. Urwid 0.9.3 =========== 2006-05-14 * Improved mouse reporting. The raw_display module now detects gpm mouse events by reading /usr/bin/mev output. The curses_display module already supports gpm directly. Mouse drag events are now reported by raw_display in terminals that provide button event tracking and on the console with gpm. Note that gpm may report coordinates off the screen if the user drags the mouse off the edge. Button release events now report which button was released if that information is available, currently only on the console with gpm. * Added display of raw keycodes to the input_test.py example program. * Fixed a text layout bug affecting clipped text with blank lines, and another related to wrapped text starting with a space character. * Fixed a Frame.keypress() bug that caused it to call keypress on unselectable widgets. Urwid 0.9.2 =========== 2006-03-18 * Preliminary mouse support was added to the raw_display and curses_display modules. A new Screen.set_mouse_tracking() method was added to enable mouse tracking. Mouse events are returned alongside keystrokes from the Screen.get_input() method. The widget interface does not yet include mouse handling. This will be addressed in the next release. * A new convenience function is_mouse_event() was added to help in separating mouse events from keystrokes. * Added a new example program input_test.py. This program displays the keyboard and mouse input it receives. It may be run as a CGI script or from the command line. On the command line it defaults to using the curses_display module, use input_test.py raw to use the raw_display module instead. * Fixed an Edit.render() bug that caused it to render the cursor in a different location than that reported by Edit.get_cursor_coords() in some circumstances. * Fixed a bug preventing use of UTF-8 characters with Divider widgets. Urwid 0.9.1 =========== 2006-03-06 * BarGraph and ProgressBar can now display data more accurately by using the UTF-8 vertical and horizontal eighth characters. This behavior will be enabled when the UTF-8 encoding is detected and "smoothed" attributes are passed to the BarGraph or ProgressBar constructors. * New get_encoding_mode() function to determine how Urwid will treat raw string data. * New raw_display.signal_init() and raw_display.signal_restore() methods that may be overridden by threaded applications that need to call signal.signal() from their main thread. * Fixed a bug that prevented the use of UTF-8 strings in text markup. * Removed some forgotten asserts that broke 8-bit and CJK input. Urwid 0.9.0 =========== 2006-02-18 * New support for UTF-8 encoding including input, display and editing of narrow and wide (CJK) characters. Preliminary combining (zero-width) character support is included, but full support will require terminal behavior detection. Right-to-Left input and display are not implemented. * New raw_display module that handles console display without relying on external libraries. This module was written as a work around for the lack of UTF-8 support in the standard version of ncurses. Eliminates "dead corner" in the bottom right of the screen. Avoids use of bold text in xterm and gnome-terminal for improved text legibility. * Fixed Overlay bug related to UTF-8 handling. * Fixed Edit.move_cursor_to_coords(..) bug related to wide characters in UTF-8 encoding. Urwid 0.9.0-pre3 ================ 2006-02-13 * Fixed Canvas attribute padding bug related to -pre1 changes. Urwid 0.9.0-pre2 ================ 2006-02-10 * Replaced the custom align and wrap modes in example program calc.py with a new layout class. * Fixed Overlay class call to Canvas.overlay() broken by -pre1 changes. * Fixed Padding bug related to Canvas -pre1 changes. Urwid 0.9.0-pre1 ================ 2006-02-08 * New support for UTF-8 encoding. Unicode strings may be used and will be converted to the current encoding when output. Regular strings in the current encoding may still be used. PLEASE NOTE: There are issues related to displaying UTF-8 characters with the curses_display module that have not yet been resolved. * New set_encoding() function replaces util.set_double_byte_encoding(). * New supports_unicode() function to query if unicode strings with characters outside the ascii range may be used with the current encoding. * New TextLayout and StandardTextLayout classes to perform text wrapping and alignment. Text widgets now have a layout parameter to allow use of custom TextLayout objects. * New layout structure replaces line translation structure. Layout structure now allows arbitrary reordering/positioning of text segments, inclusion of UTF-8 characters and insertion of text not found in the original text string. * Removed util.register_align_mode() and util.register_wrap_mode(). Their functionality has been replaced by the new layout classes. Urwid 0.8.10 ============ 2005-11-27 * Expanded tutorial to cover advanced ListBox usage, custom widget classes and the Pile, BoxAdapter, Columns, GridFlow and Overlay classes. * Added escape sequence for "shift tab" to curses_display. * Added ListBox.set_focus_valign() to allow positioning of the focus widget within the ListBox. * Added WidgetWrap class for extending existing widgets without inheriting their complete namespace. * Fixed web_display/mozilla breakage from 0.8.9. Fixed crash on invalid locale setting. Fixed ListBox slide-back bug. Fixed improper space trimming in calculate_alignment(). Fixed browse.py example program rows bug. Fixed sum definition, use of long ints for python2.1. Fixed warnings with python2.1. Fixed Padding.get_pref_col() bug. Fixed Overlay splitting CJK characters bug. Urwid 0.8.9 =========== 2005-10-21 * New Overlay class for drawing widgets that obscure parts of other widgets. May be used for drop down menus, combo boxes, overlapping "windows", caption text etc. * New BarGraph, GraphVScale and ProgressBar classes for graphical display of data in Urwid applications. * New method for configuring keyboard input timeouts and delays: curses_display.Screen.set_input_timeouts(). * Fixed a ListBox.set_focus() bug. Urwid 0.8.8 =========== 2005-06-13 * New web_display module that emulates a console display within a web browser window. Application must be run as a CGI script under Apache. Supports font/window resizing, keepalive for long-lived connections, limiting maximum concurrent connections, polling and connected update methods. Tested with Mozilla Firefox and Internet Explorer. * New BoxAdapter class for using box widgets in places that usually expect flow widgets. * New curses_display input handling with better ESC key detection and broader escape code support. * Shortened resize timeout on gradual resize to improve responsiveness. Urwid 0.8.7 =========== 2005-05-21 * New widget classes: Button, RadioButton, CheckBox. * New layout widget classes: Padding, GridFlow. * New dialog.py example program that behaves like dialog(1) command. * Pile widgets now support selectable items, focus changing with up and down keys and setting the cursor position. * Frame widgets now support selectable items in the header and footer. * Columns widgets now support fixed width and relative width columns, a minimum width for all columns, selectable items within columns containing flow widgets (already supported for box widgets), focus changing with left and right keys and setting the cursor position. * Filler widgets may now wrap box widgets and have more alignment options. * Updated tour.py example program to show new widget types and features. * Avoid hogging cpu on gradual window resize and fix for slow resize with cygwin's broken curses implementation. * Fixed minor CJK problem and curs_set() crash under MacOSX and Cygwin. * Fixed crash when deleting cells in calc.py example program. Urwid 0.8.6 =========== 2005-01-03 * Improved support for CJK double-byte encodings: BIG5, UHC, GBK, GB2312, CN-GB, EUC-KR, EUC-CN, EUC-JP (JISX 0208 only) and EUC-TW (CNS 11643 plain 1 only) * Added support for ncurses' use_default_colors() function to curses_display module (Python >= 2.4). register_palette() and register_palette_entry() now accept "default" as foreground and/or background. If the terminal's default attributes cannot be detected black on light gray will be used to accomodate terminals with always-black cursors. "default" is now the default for text with no attributes. This means that areas with no attributes will change from light grey on black (curses default) to black on light gray or the terminal's default. * Modified examples to not use black as background of Edit widgets. * Fixed curses_display curs_set() call so that cursor is hidden when widget in focus has no cursor position. Urwid 0.8.5 =========== 2004-12-15 * New tutorial covering basic operation of: curses_display.Screen, Canvas, Text, FlowWidget, Filler, BoxWidget, AttrWrap, Edit, ListBox and Frame classes * New widget class: Filler * New ListBox functions: get_focus(), set_focus() * Debian packages for Python 2.4. * Fixed curses_display bug affecting text with no attributes. Urwid 0.8.4 =========== 2004-11-20 * Improved support for Cyrillic and other simple 8-bit encodings. * Added new functions to simplify taking screenshots: html_fragment.screenshot_init() and html_fragment.screenshot_collect() * Improved urwid/curses_display.py input debugging * Fixed cursor in screenshots of CJK text. Fixed "end" key in Edit boxes with CJK text. Urwid 0.8.3 =========== 2004-11-15 * Added support for CJK double-byte encodings. Word wrapping mode "space" will wrap on edges of double width characters. Wrapping and clipping will not split double width characters. curses_display.Screen.get_input() may now return double width characters. Text and Edit classes will work with a mix of regular and double width characters. * Use new method Edit.set_edit_text() instead of Edit.update_text(). * Minor improvements to edit.py example program. Urwid 0.8.2 =========== 2004-11-08 * Re-released under GNU Lesser General Public License. Urwid 0.8.1 =========== 2004-10-29 * Added support for monochrome terminals. see curses_display.Screen.register_palette_entry() and example programs. set TERM=xterm-mono to test programs in monochrome mode. * Added unit testing code test_urwid.py to the examples. * Can now run urwid/curses_display.py to test your terminal's input and colour rendering. * Fixed an OSX browse.py compatibility issue. Added some OSX keycodes. Urwid 0.8.0 =========== 2004-10-17 * Initial Release urwid-1.3.1/docs/reference/0000775000175000017500000000000012615524621015102 5ustar ianian00000000000000urwid-1.3.1/docs/reference/deprecated.rst0000664000175000017500000000031412615524560017734 0ustar ianian00000000000000Deprecated Classes ------------------ .. currentmodule:: urwid .. autoclass:: FlowWidget .. autoclass:: BoxWidget .. autoclass:: FixedWidget .. autoclass:: AttrWrap .. autoclass:: PollingListWalker urwid-1.3.1/docs/reference/attrspec.rst0000664000175000017500000000014112615524560017457 0ustar ianian00000000000000Raw Display Attributes ====================== .. currentmodule:: urwid .. autoclass:: AttrSpec urwid-1.3.1/docs/reference/list_walkers.rst0000664000175000017500000000054412615524560020344 0ustar ianian00000000000000List Walker Classes =================== .. currentmodule:: urwid ListWalker ---------- .. autoclass:: ListWalker List-like List Walkers ---------------------- .. autoclass:: SimpleFocusListWalker .. autoclass:: SimpleListWalker TreeWalker and Nodes -------------------- .. autoclass:: TreeWalker .. autoclass:: TreeNode .. autoclass:: ParentNode urwid-1.3.1/docs/reference/global_settings.rst0000664000175000017500000000024412615524560021016 0ustar ianian00000000000000Global Settings =============== .. currentmodule:: urwid .. autofunction:: set_encoding .. autofunction:: get_encoding_mode .. autofunction:: supports_unicode urwid-1.3.1/docs/reference/signals.rst0000664000175000017500000000137212615524560017301 0ustar ianian00000000000000Signal Functions ================ .. currentmodule:: urwid The :func:`urwid.\*_signal` functions use a shared Signals object instance for tracking registered and connected signals. There is no reason to instantiate your own Signals object. .. function:: connect_signal(obj, name, callback, user_arg=None, weak_args=None, user_args=None) .. automethod:: Signals.connect .. function:: disconnect_by_key(obj, name, key) .. automethod:: Signals.disconnect_by_key .. function:: disconnect_signal(obj, name, callback, user_arg=None, weak_args=None, user_args=None) .. automethod:: Signals.disconnect .. function:: register_signal(sig_cls, signals) .. automethod:: Signals.register .. function:: emit_signal(obj, name, \*args) .. automethod:: Signals.emit urwid-1.3.1/docs/reference/index.rst0000664000175000017500000000042212615524560016743 0ustar ianian00000000000000.. _urwid-reference: ############### Urwid Reference ############### .. toctree:: main_loop widget display_modules list_walkers signals global_settings attrspec canvas text_layout command_map constants exceptions meta deprecated urwid-1.3.1/docs/reference/constants.rst0000664000175000017500000001616012615524560017656 0ustar ianian00000000000000Constants ========= .. currentmodule:: urwid .. note:: These constants may be used, but using the string values themselves in your program is equally supported. These constants are used internally by urwid just to avoid possible misspelling, but the example programs and tutorials tend to use the string values. Widget Sizing Methods --------------------- One or more of these values returned by :meth:`Widget.sizing` to indicate supported sizing methods. .. data:: FLOW :annotation: = 'flow' Widget that is given a number of columns by its parent widget and calculates the number of rows it requires for rendering e.g. :class:`Text` .. data:: BOX :annotation: = 'box' Widget that is given a number of columns and rows by its parent widget and must render that size e.g. :class:`ListBox` .. data:: FIXED :annotation: = 'fixed' Widget that knows the number of columns and rows it requires and will only render at that exact size e.g. :class:`BigText` Horizontal Alignment -------------------- Used to horizontally align text in :class:`Text` widgets and child widgets of :class:`Padding` and :class:`Overlay`. .. data:: LEFT :annotation: = 'left' .. data:: CENTER :annotation: = 'center' .. data:: RIGHT :annotation: = 'right' Veritcal Alignment ------------------ Used to vertically align child widgets of :class:`Filler` and :class:`Overlay`. .. data:: TOP :annotation: = 'top' .. data:: MIDDLE :annotation: = 'middle' .. data:: BOTTOM :annotation: = 'bottom' Width and Height Settings ------------------------- Used to distribute or set widths and heights of child widgets of :class:`Padding`, :class:`Filler`, :class:`Columns`, :class:`Pile` and :class:`Overlay`. .. data:: PACK :annotation: = 'pack' Ask the child widget to calculate the number of columns or rows it needs .. data:: GIVEN :annotation: = 'given' A set number of columns or rows, e.g. ('given', 10) will have exactly 10 columns or rows given to the child widget .. data:: RELATIVE :annotation: = 'relative' A percentage of the total space, e.g. ('relative', 50) will give half of the total columns or rows to the child widget .. data:: RELATIVE_100 :annotation: = ('relative', 100) .. data:: WEIGHT :annotation: = 'weight' A weight value for distributing columns or rows, e.g. ('weight', 3) will give 3 times as many columns or rows as another widget in the same container with ('weight', 1). Text Wrapping Modes ------------------- .. data:: SPACE :annotation: = 'space' wrap text on space characters or at the boundaries of wide characters .. data:: ANY :annotation: = 'any' wrap before any wide or narrow character that would exceed the available screen columns .. data:: CLIP :annotation: = 'clip' clip before any wide or narrow character that would exceed the available screen columns ad don't display the remaining text on the line Foreground and Background Colors -------------------------------- Standard background and foreground colors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. data:: BLACK :annotation: = 'black' .. data:: DARK_RED :annotation: = 'dark red' .. data:: DARK_GREEN :annotation: = 'dark green' .. data:: BROWN :annotation: = 'brown' .. data:: DARK_BLUE :annotation: = 'dark blue' .. data:: DARK_MAGENTA :annotation: = 'dark magenta' .. data:: DARK_CYAN :annotation: = 'dark cyan' .. data:: LIGHT_GRAY :annotation: = 'light gray' Standard foreground colors (not safe to use as background) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. data:: DARK_GRAY :annotation: = 'dark gray' .. data:: LIGHT_RED :annotation: = 'light red' .. data:: LIGHT_GREEN :annotation: = 'light green' .. data:: YELLOW :annotation: = 'yellow' .. data:: LIGHT_BLUE :annotation: = 'light blue' .. data:: LIGHT_MAGENTA :annotation: = 'light magenta' .. data:: LIGHT_CYAN :annotation: = 'light cyan' .. data:: WHITE :annotation: = 'white' User's terminal configuration default foreground or background ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. note:: There is no way to tell if the user's terminal has a light or dark color as their default foreground or background, so it is highly recommended to use this setting for both foreground and background when you do use it. .. data:: DEFAULT :annotation: = 'default' 256 and 88 Color Foregrounds and Backgrounds ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Constants are not defined for these colors. .. seealso:: :ref:`high-colors` Signal Names ------------ .. data:: UPDATE_PALETTE_ENTRY :annotation: = 'update palette entry' sent by :class:`BaseScreen` (and subclasses like :class:`raw_display.Screen`) when a palette entry is changed. :class:`MainLoop` handles this signal by redrawing the whole screen. .. data:: INPUT_DESCRIPTORS_CHANGED :annotation: = 'input descriptors changed' sent by :class:`BaseScreen` (and subclasses like :class:`raw_display.Screen`) when the list of input file descriptors has changed. :class:`MainLoop` handles this signal by updating the file descriptors being watched by its event loop. Command Names ------------- Command names are used as values in :class:`CommandMap` instances. Widgets look up the command associated with keypresses in their :meth:`Widget.keypress` methods. You may define any new command names as you wish and look for them in your own widget code. These are the standard ones expected by code included in Urwid. .. data:: REDRAW_SCREEN :annotation: = 'redraw screen' Default associated keypress: 'ctrl l' :meth:`MainLoop.process_input` looks for this command to force a screen refresh. This is useful in case the screen becomes corrupted. .. data:: ACTIVATE :annotation: = 'activate' Default associated keypresses: ' ' (space), 'enter' Activate a widget such as a :class:`Button`, :class:`CheckBox` or :class:`RadioButton`. .. data:: CURSOR_UP :annotation: = 'cursor up' Default associated keypress: 'up' Move the cursor or selection up one row. .. data:: CURSOR_DOWN :annotation: = 'cursor down' Default associated keypress: 'down' Move the cursor or selection down one row. .. data:: CURSOR_LEFT :annotation: = 'cursor left' Default associated keypress: 'left' Move the cursor or selection left one column. .. data:: CURSOR_RIGHT :annotation: = 'cursor right' Default associated keypress: 'right' Move the cursor or selection right one column. .. data:: CURSOR_PAGE_UP :annotation: = 'cursor page up' Default associated keypress: 'page up' Move the cursor or selection up one page. .. data:: CURSOR_PAGE_DOWN :annotation: = 'cursor page down' Default associated keypress: 'page down' Move the cursor or selection down one page. .. data:: CURSOR_MAX_LEFT :annotation: = 'cursor max left' Default associated keypress: 'home' Move the cursor or selection to the leftmost column. .. data:: CURSOR_MAX_RIGHT :annotation: = 'cursor max right' Default associated keypress: 'end' Move the cursor or selection to the rightmost column. urwid-1.3.1/docs/reference/canvas.rst0000664000175000017500000000070512615524560017113 0ustar ianian00000000000000Canvas Classes and Functions ============================ .. currentmodule:: urwid Canvas Classes -------------- .. autoclass:: Canvas .. autoclass:: TextCanvas .. autoclass:: BlankCanvas .. autoclass:: SolidCanvas .. autoclass:: CompositeCanvas CompositeCanvas Builders ------------------------ .. autofunction:: CanvasCombine .. autofunction:: CanvasJoin .. autofunction:: CanvasOverlay CanvasCache ----------- .. autoclass:: CanvasCache urwid-1.3.1/docs/reference/widget.rst0000664000175000017500000000376712615524560017136 0ustar ianian00000000000000.. _widget-classes: Widget Classes ============== .. currentmodule:: urwid Widget Base Classes ------------------- Widget ~~~~~~ .. autoclass:: Widget :private-members: _invalidate, _emit WidgetWrap ~~~~~~~~~~ .. autoclass:: WidgetWrap WidgetDecoration ~~~~~~~~~~~~~~~~ .. autoclass:: WidgetDecoration WidgetContainerMixin ~~~~~~~~~~~~~~~~~~~~ .. autoclass:: WidgetContainerMixin Basic Widget Classes -------------------- Text ~~~~ .. autoclass:: Text Edit ~~~~ .. autoclass:: Edit IntEdit ~~~~~~~ .. autoclass:: IntEdit Button ~~~~~~ .. autoclass:: Button CheckBox ~~~~~~~~ .. autoclass:: CheckBox RadioButton ~~~~~~~~~~~ .. autoclass:: RadioButton TreeWidget ~~~~~~~~~~ .. autoclass:: TreeWidget SelectableIcon ~~~~~~~~~~~~~~ .. autoclass:: SelectableIcon Decoration Widget Classes ------------------------- AttrMap ~~~~~~~ .. autoclass:: AttrMap Padding ~~~~~~~ .. autoclass:: Padding Filler ~~~~~~ .. autoclass:: Filler Divider ~~~~~~~ .. autoclass:: Divider LineBox ~~~~~~~ .. autoclass:: LineBox SolidFill ~~~~~~~~~ .. autoclass:: SolidFill PopUpLauncher ~~~~~~~~~~~~~ .. autoclass:: PopUpLauncher PopUpTarget ~~~~~~~~~~~ .. autoclass:: PopUpTarget WidgetPlaceholder ~~~~~~~~~~~~~~~~~ .. autoclass:: WidgetPlaceholder WidgetDisable ~~~~~~~~~~~~~ .. autoclass:: WidgetDisable Container Widget Classes ------------------------ Frame ~~~~~ .. autoclass:: Frame ListBox ~~~~~~~ .. autoclass:: ListBox TreeListBox ~~~~~~~~~~~ .. autoclass:: TreeListBox Columns ~~~~~~~ .. autoclass:: Columns Pile ~~~~ .. autoclass:: Pile GridFlow ~~~~~~~~ .. autoclass:: GridFlow BoxAdapter ~~~~~~~~~~ .. autoclass:: BoxAdapter Overlay ~~~~~~~ .. autoclass:: Overlay Graphic Widget Classes ---------------------- BarGraph ~~~~~~~~ .. autoclass:: BarGraph GraphVScale ~~~~~~~~~~~ .. autoclass:: GraphVScale ProgressBar ~~~~~~~~~~~ .. autoclass:: ProgressBar BigText ~~~~~~~ .. autoclass:: BigText .. autofunction:: get_all_fonts Terminal ~~~~~~~~ .. autoclass:: Terminal urwid-1.3.1/docs/reference/text_layout.rst0000664000175000017500000000020012615524560020207 0ustar ianian00000000000000Text Layout Classes =================== .. currentmodule:: urwid .. autoclass:: TextLayout .. autoclass:: StandardTextLayout urwid-1.3.1/docs/reference/meta.rst0000664000175000017500000000020312615524560016557 0ustar ianian00000000000000Metaclasses =========== .. currentmodule:: urwid .. autoclass:: WidgetMeta .. autoclass:: MetaSuper .. autoclass:: MetaSignals urwid-1.3.1/docs/reference/display_modules.rst0000664000175000017500000000123512615524560021034 0ustar ianian00000000000000Display Modules =============== .. currentmodule:: urwid .. autoclass:: BaseScreen raw_display ----------- .. module:: urwid.raw_display .. autoclass:: Screen curses_display -------------- .. module:: urwid.curses_display .. autoclass:: Screen web_display ----------- .. module:: urwid.web_display .. autoclass:: Screen html_fragment ------------- .. module:: urwid.html_fragment .. autoclass:: HtmlGenerator .. autofunction:: screenshot_init .. autofunction:: screenshot_collect lcd_display ----------- .. module:: urwid.lcd_display .. autoclass:: LCDScreen .. autoclass:: CFLCDScreen .. autoclass:: CF635Screen .. autoclass:: KeyRepeatSimulator urwid-1.3.1/docs/reference/command_map.rst0000664000175000017500000000011512615524560020106 0ustar ianian00000000000000Command Map ----------- .. currentmodule:: urwid .. autoclass:: CommandMap urwid-1.3.1/docs/reference/main_loop.rst0000664000175000017500000000070012615524560017610 0ustar ianian00000000000000MainLoop and Event Loops ======================== .. currentmodule:: urwid MainLoop -------- .. autoclass:: MainLoop SelectEventLoop --------------- .. autoclass:: SelectEventLoop GLibEventLoop ------------- .. autoclass:: GLibEventLoop TwistedEventLoop ---------------- .. autoclass:: TwistedEventLoop TornadoEventLoop ---------------- .. autoclass:: TornadoEventLoop AsyncioEventLoop ---------------- .. autoclass:: AsyncioEventLoop urwid-1.3.1/docs/reference/exceptions.rst0000664000175000017500000000062412615524560020021 0ustar ianian00000000000000Exceptions ========== .. currentmodule:: urwid .. autoexception:: ExitMainLoop .. autoexception:: WidgetError .. autoexception:: ListBoxError .. autoexception:: ColumnsError .. autoexception:: PileError .. autoexception:: GridFlowError .. autoexception:: BoxAdapterError .. autoexception:: OverlayError .. autoexception:: TextError .. autoexception:: EditError .. autoexception:: CanvasError urwid-1.3.1/docs/manual/0000775000175000017500000000000012615524621014421 5ustar ianian00000000000000urwid-1.3.1/docs/manual/wcur2.py0000664000175000017500000000030712615524560016037 0ustar ianian00000000000000 def get_pref_col(self, (maxcol,)): return self.cursor_x def move_cursor_to_coords(self, (maxcol,), col, row): assert row == 0 self.cursor_x = col return True urwid-1.3.1/docs/manual/displaymodules.rst0000664000175000017500000001252212615524560020215 0ustar ianian00000000000000.. _display-modules: ******************* Display Modules ******************* .. currentmodule:: urwid Urwid's display modules provide a layer of abstraction for drawing to the screen and reading user input. The display module you choose will depend on how you plan to use Urwid. .. image:: images/display_modules.png Typically you will select a display module by passing it to your :class:`MainLoop` constructor, eg: :: loop = MainLoop(widget, ..., screen=urwid.curses_display.Screen()) If you don't specify a display module, the default main loop will use :class:`raw_display.Screen` by default :: # These are the same loop = MainLoop(widget, ...) loop = MainLoop(widget, ..., screen=urwid.raw_display.Screen()) Raw and Curses Display Modules ============================== Urwid has two display modules for displaying to terminals or the console. The :class:`raw_display.Screen` module is a pure-python display module with no external dependencies. It sends and interprets terminal escape sequences directly. This is the default display module used by :class:`MainLoop`. The :class:`curses_display.Screen` module uses the curses or ncurses library provided by the operating system. The library does some optimization of screen updates and uses termcap to adjust to the user's terminal. The (n)curses library will disable colors if it detects a monochrome terminal, so a separate set of attributes should be given for monochrome mode when registering a palette with :class:`curses_display.Screen` High colors will not be used by the :class:`curses_display.Screen` module. See :ref:`setting-a-palette` below. This table summarizes the differences between the two modules: ============================== =========== ============== .. raw_display curses_display ============================== =========== ============== optimized C code no YES compatible with any terminal no YES [1]_ UTF-8 support YES YES [2]_ bright foreground without bold YES [3]_ no 88- or 256-color support YES no mouse dragging support YES no external event loop support YES no ============================== =========== ============== .. [1] if the termcap entry exists and TERM environment variable is set correctly .. [2] if python is linked against the wide version of ncurses .. [3] when using xterm or gnome-terminal Other Display Modules ===================== CGI Web Display Module ``web_display`` -------------------------------------- The :mod:`urwid.web_display` module lets you run your application as a CGI script under Apache instead of running it in a terminal. This module is a proof of concept. There are security and responsiveness issues that need to be resolved before this module is recommended for production use. The tour.py_ and calc.py_ example programs demonstrate use of this module. .. _tour.py: http://excess.org/urwid/browser/examples/tour.py .. _calc.py: http://excess.org/urwid/browser/examples/calc.py Screenshot Display Module ``html_fragment`` ------------------------------------------- Screenshots of Urwid interfaces can be rendered in plain HTML. The :class:`html_fragment.HtmlGenerator` display module lets you do this by simulating user input and capturing the screen as fragments of HTML each time :meth:`html_fragment.HtmlGenerator.draw_screen` is called. These fragments may be included in HTML documents. They will be rendered properly by any browser that uses a monospaced font for text that appears in ``
`` tags. HTML screenshots have text that is searchable and selectable in
a web browser, and they will shrink and grow when a user changes their
browser's text size.

The `example screenshots`_ are generated with this display module.

.. _`example screenshots`: http://excess.org/urwid/examples.html


LCD Display Module ``lcd_display``
----------------------------------

Almost any device that displays characters in a grid can be used as a
screen.  The :mod:`lcd_display` module has some base classes for simple
LCD character display devices and a complete implementation of a
:class:`lcd_display.CF635Screen` for Crystal Fontz 635 USB displays with
6 buttons.

The lcd_cf635.py_ example program demonstrates use of this module.

.. _lcd_cf635.py: http://excess.org/urwid/browser/examples/lcd_cf635.py

.. seealso:: `Urwid on a Crystalfontz 635 LCD `_


.. _setting-a-palette:

Setting a Palette
=================

The :class:`MainLoop` constructor takes a *palette* parameter that it passes
to the :meth:`register_palette() ` method of your display module.

A palette is a list of :ref:`display attribute ` names and foreground
and background settings. Display modules may be run in monochrome, normal or
high color modes and you can set different foregrounds and backgrounds for each
mode as part of your palette. eg:

::

    loop = MainLoop(widget, palette=[
        ('headings', 'white,underline', 'black', 'bold,underline'), # bold text in monochrome mode
        ('body_text', 'dark cyan', 'light gray'),
        ('buttons', 'yellow', 'dark green', 'standout'),
        ('section_text', 'body_text'), # alias to body_text
        ])

The :ref:`display-attributes` section of this manual describes all the options
available.
urwid-1.3.1/docs/manual/wmod.py0000664000175000017500000000103212615524560015737 0ustar  ianian00000000000000import urwid

class QuestionnaireItem(urwid.WidgetWrap):
    def __init__(self):
        self.options = []
        unsure = urwid.RadioButton(self.options, u"Unsure")
        yes = urwid.RadioButton(self.options, u"Yes")
        no = urwid.RadioButton(self.options, u"No")
        display_widget = urwid.GridFlow([unsure, yes, no], 15, 3, 1, 'left')
        urwid.WidgetWrap.__init__(self, display_widget)

    def get_state(self):
        for o in self.options:
            if o.get_state() is True:
                return o.get_label()
urwid-1.3.1/docs/manual/displayattributes.rst0000664000175000017500000002552112615524560020736 0ustar  ianian00000000000000.. _display-attributes:

**********************
  Display Attributes
**********************

.. currentmodule:: urwid

Urwid supports a number of common display attributes in monochrome, 16-color,
88-color and 256-color modes.

You are encouraged to provide support for as many of these modes as you like, while
allowing your interface to degrade gracefully by  providing command line arguments
or other interfaces to switch modes.

When setting up a palette with :class:`MainLoop` (or directly
on your screen instance), you may specify attributes for 16-color, monochrome
and high color modes. You can then switch between these modes with
:meth:`screen.set_terminal_properties() `,
where ``screen`` is your screen instance or :attr:`MainLoop.screen`.

.. seealso::
   :meth:`register_palette() reference `,

.. _using-display-attributes:

Using Display Attributes
========================

Once you have defined a palette you may use the its display attribute names
anywhere that expects a display attribute.  When no display attribute is defined
``None`` is used as a default display attribute.

``None`` will typically be rendered with the terminal's default foreground and
background colors.

You can also specify an exact foreground and background using an
:class:`AttrSpec` instance instead of a display attribute name.
Using :class:`AttrSpec` instances in your code may be trickier than using your
screen's palette because you must know which mode (number of colors) the screen is in.

.. _text-markup:

Text Markup
-----------

A :class:`Text` widget can specify which display attributes each part of the
text will use with the format defined in :class:`Text class reference `.
Some examples:

::

    Text(u"a simple string with default attribute")

The string and space around will use the ``None`` default display attribute
which usually appears in the terminal's default foreground and background.

::

    Text(('attr1', u"a string in display attribute attr1"))

The string will appear with foreground and backgrounds specified in the display
module's palette for ``'attr1'``, but the space around (before/after) the text
will appear with the default display attribute.

::

    Text([u"a simple string ", ('attr1', u"ending with attr1")])

The first three words have the default display attribute and the last three words have
display attribute ``'attr1'``.

::

    Text([('attr1', u"start in attr1 "), ('attr2', u"end in attr2")])

The first three words have display attribute ``'attr1'`` and the last three words have
display attribute ``'attr2'``.

::

    Text(('attr1', [u"nesting example ", ('attr2', u"inside"), u" outside"]))

When markup is nested only the innermost attribute applies. Here ``"inside"``
has attribute ``'attr2'`` and all the rest of the text has attribute
``'attr1'``.


Assigning Display Attributes with AttrMap
-----------------------------------------

If you want a whole widget to be assigned a display attribute, or if you want to change
one or more display attributes to other display attributes, you can wrap your widget
in an :class:`AttrMap` widget.  :class:`Text` widgets have no way to specify
a display attribute for the whitespace around the text caused by alignment and wrapping
so :class:`AttrMap` may be used. Some examples:

::

    AttrMap(Text(u"hello"), 'attr1')

The whole :class:`Text` widget will have display attribute ``'attr1'`` including
whitespace around the ``"hello"`` text.

::

    AttrMap(Text(('attr1', u"hello")), 'attr2')

The ``u"hello"`` text will appear with display attribute ``'attr1'`` and all surrounding
whitespace will appear with display attribute ``'attr2'``.

::

    AttrMap(Text([('attr1', u"hello"), u" world"]), {'attr1': 'attr2'})

The :class:`AttrMap` widget will apply display attribute ``'attr2'`` to all parts of
the :class:`Text` widget that are using ``'attr1'``.  The result is the ``"hello"``
text appearing with display attribute ``'attr2'`` and all other text and whitespace
appearing in the default display attribute.


:class:`AttrMap` can also change display attributes differently when they are in focus.
This can be used to "highlight" one or more widgets to make your interface more
user friendly.  To use this feature set the ``focus_map`` parameter when creating the
:class:`AttrMap` widget.


.. _foreground-background:

Foreground and Background Settings
==================================

.. list-table::
   :header-rows: 1
   :widths: 23 15 10 10 15

   * - Supported by Terminal
     - xterm / gnome-term
     - rxvt
     - linux console
     - others
   * - :ref:`16 standard foreground colors <16-standard-foreground>`
     - YES
     - YES
     - YES
     - very widely supported
   * - :ref:`8 standard background colors <8-standard-background>`
     - YES
     - YES
     - YES
     - very widely supported
   * - :ref:`default foreground/background `
     - YES
     - YES
     - YES
     - widely supported
   * - :ref:`bold, underline, standout `
     - YES
     - YES
     - standout
     - widely supported
   * - :ref:`"bright" background colors `
     - YES
     - urxvt
     -
     - some support
   * - :ref:`256-color foreground/background <256-foreground-background>`
     - YES
     -
     -
     - some support
   * - :ref:`88-color foreground/background <88-foreground-background>`
     - w/palette setting
     - urxvt
     -
     - limited support
   * - :ref:`RGB palette setting `
     - YES
     -
     -
     - limited support


.. _16-standard-foreground:

16 Standard Foreground Colors
-----------------------------

* ``'black'``
* ``'dark red'``
* ``'dark green'``
* ``'brown'``
* ``'dark blue'``
* ``'dark magenta'``
* ``'dark cyan'``
* ``'light gray'``
* ``'dark gray'``
* ``'light red'``
* ``'light green'``
* ``'yellow'``
* ``'light blue'``
* ``'light magenta'``
* ``'light cyan'``
* ``'white'``

.. _8-standard-background:

8 Standard Background Colors
----------------------------

* ``'black'``
* ``'dark red'``
* ``'dark green'``
* ``'brown'``
* ``'dark blue'``
* ``'dark magenta'``
* ``'dark cyan'``
* ``'light gray'``

.. _default-foreground-background:

Default Foreground and Background
---------------------------------

* ``'default'`` (or simply ``''``)

``'default'`` may be specified as a foreground or background to use a
terminal's default color. For terminals with transparent backgrounds
``'default'`` is the only way to show the transparent background. There is no
way to tell what the default colors are, so it is best to use default
foregrounds and backgrounds together (not with other colors) to ensure good
contrast.

.. _bold-underline-standout:

Bold, Underline, Standout
-------------------------

* ``'bold'``
* ``'underline'``
* ``'standout'``

These settings may be tagged on to foreground colors using commas, eg: ``'light
gray,underline,bold'``

For monochrome mode combinations of these are the only values that may be used.

Many terminals will turn foreground colors into their bright versions when you
use bold, eg: ``'dark blue,bold'`` might look the same as ``'light blue'``.
Some terminals also will display bright colors in a bold font even if you don't
specify bold. To inhibit this you can try setting ``bright_is_bold=False`` with
:meth:`BaseScreen.set_terminal_properties`, but it is not always supported.

``'standout'`` is usually displayed as the foreground and background colors reversed.

.. _bright-background:

"Bright" Background Colors
--------------------------

.. warning::
   Terminal support for bright background colors is spotty, and they generally
   should be avoided. If you are in a high-color mode you might have better luck
   using the high-color versions ``'h8'``, ``'h9'``, ``'h10'``, ..., ``'h15'``.

* ``'dark gray'``
* ``'light red'``
* ``'light green'``
* ``'yellow'``
* ``'light blue'``
* ``'light magenta'``
* ``'light cyan'``
* ``'white'``


.. _high-colors:

.. _256-foreground-background:

256-Color Foreground and Background Colors
------------------------------------------

In 256-color mode you have the 16 basic colors, a 6 * 6 * 6 color cube and a gray
scale with 24 entries (white and black not included).

The color cube is weighted towards the brighter colors, with RGB points at ``0``,
``0x5f``, ``0x87``, ``0xaf``, ``0xd7`` and ``0xff``.
The hex characters ``'0'``, ``'6'``, ``'8'``, ``'a'``, ``'d'`` and
``'f'`` are used as short-forms for these values.

High colors may be specified by their index ``'h0'``, ..., ``'h255'`` or with the
shortcuts for the color cube ``'#000'``, ``'#006'``, ``'#008'``, ..., ``'#fff'`` or
gray scale entries ``'g0'`` (black from color cube) , ``'g3'``, ``'g7'``, ...
``'g100'`` (white from color cube).

.. seealso::
   The palette_test.py_ example program

.. _palette_test.py: http://excess.org/urwid/browser/examples/palette_test.py

.. _88-foreground-background:

88-Color Foreground and Background Colors
-----------------------------------------

In 88-color mode you have the 16 basic colors, a 4 * 4 * 4 color cube and a gray
scale with 8 entries (white and black not included).

The color cube is weighted towards the brighter colors, with RGB points at ``0``,
``0x8b``, ``0xcd``, and ``0xff``. The hex characters ``'0'``, ``'8'``, ``'c'``
and ``'f'`` are used as short-forms for these values.

High colors may be specified by their index ``'h0'``, ..., ``'h87'`` or with the
shortcuts for the color cube ``'#000'``, ``'#008'``, ``'#00c'``, ..., ``'#fff'`` or
gray scale entries ``'g0'`` (black from color cube), ``'g19'``, ``'g35'``, ...
``'g100'`` (white from color cube).

.. seealso::
   The palette_test.py_ example program

.. _palette_test.py: http://excess.org/urwid/browser/examples/palette_test.py


.. _rgb-palette-setting:

RGB Palette Setting
-------------------

A few terminals have the ability to customize the terminal palette's RGB
values with :meth:`raw_display.Screen.modify_terminal_palette`.
There is no automatic way to tell if this is supported by a user's
terminal, so this feature shouldn't be relied on.

:meth:`raw_display.Screen.reset_default_terminal_palette` is used to
reset the palette in the ``palette_test.py`` example program when switching modes.


Recommended Combinations
========================

Neutral Backgrounds
-------------------

.. image:: safe_combinations1.png

Choose colors that are fairly neutral with medium contrast for most of
your application. It is good to use one background as a default for text,
another for edit boxes and a third for selected edit boxes.

Foreground colors shown here in bold text will appear as bold text on
many terminals. Bold fonts are often more difficult to read so those
foreground colours should be used sparingly.

Bright Backgrounds
------------------

.. image:: bright_combinations1.png

Use bright colors to draw attention to small areas with important
information. They are good for buttons and selected widgets (other than
edit boxes).


urwid-1.3.1/docs/manual/textlayout.rst0000664000175000017500000001107112615524560017377 0ustar  ianian00000000000000.. _text-layout:

***************
  Text Layout
***************

.. currentmodule:: urwid

Mapping a text string to screen coordinates within a widget is called text
layout. The :class:`Text` widget's default layout class supports
aligning text to the left, center or right, and can wrap text on space
characters, at any location, or clip text that is off the edge.

::

    Text("Showing some different alignment modes", align=...)

    align='left' (default)
    +----------------+   +------------------------+
    |Showing some    |   |Showing some different  |
    |different       |   |alignment modes         |
    |alignment modes |   +------------------------+
    +----------------+

    align='center'
    +----------------+   +------------------------+
    |  Showing some  |   | Showing some different |
    |   different    |   |    alignment modes     |
    |alignment modes |   +------------------------+
    +----------------+

    align='right'
    +----------------+   +------------------------+
    |    Showing some|   |  Showing some different|
    |       different|   |         alignment modes|
    | alignment modes|   +------------------------+
    +----------------+

::

    Text("Showing some different wrapping modes\nnewline", wrap=...)

    wrap='space' (default)
    +----------------+   +------------------------+
    |Showing some    |   |Showing some different  |
    |different       |   |wrapping modes          |
    |wrapping modes  |   |newline                 |
    |newline         |   +------------------------+
    +----------------+

    wrap='any'
    +----------------+   +------------------------+
    |Showing some dif|   |Showing some different w|
    |ferent wrapping |   |rapping modes           |
    |modes           |   |newline                 |
    |newline         |   +------------------------+
    +----------------+

    wrap='clip'
    +----------------+   +------------------------+
    |Showing some dif|   |Showing some different w|
    |newline         |   |newline                 |
    +----------------+   +------------------------+

If this is good enough for your application feel free to skip the rest of this
section.

.. seealso::
   :class:`Text widget reference `


Custom Text Layouts
===================

The :class:`StandardTextLayout` is set as the class variable
:attr:`Text.layout`. Individual :class:`Text`
widgets may use a different layout class, or you can change the default by
setting the :attr:`Text.layout` class variable itself.

A custom text layout class should extend the
:class:`TextLayout` base class and return text layout
structures from its ``layout()`` method.

.. seealso::
   :class:`TextLayout reference `


Text Layout Structures
======================

::

    "This is how a string of text might be displayed"
    0----5---10---15---20---25---30---35---40---45--

    0----5---10---15---+   right_aligned_text_layout = [
    |     This is how a|     [(5, 0), (13, 0, 13)],
    |    string of text|     [(4, 13), (14, 14, 28)],
    |might be displayed|     [(18, 29, 47)]
    +------------------+   ]

The mapping from a text string to where that text will be displayed in the
widget is expressed as a text layout structure.

Text layout structures are used both for rendering :class:`Text`
widgets and for mapping ``(x, y)`` positions within a widget back to the
corresponding offsets in the text. The latter is used when moving the cursor in
:class:`Edit` widgets up and down or by clicking with the mouse.

A text layout structure is a list of one or more line layouts. Each line layout
corresponds to a row of text in the widget, starting from its top.

A line layout is a list zero or more of the following tuples, each expressing
text to be displayed from left to right:

A. (*column width*, *starting text offset*, *ending text offset*)
B. (*column width of space characters to insert*, *text offset* or ``None``)
C. (*column width*, *text offset*, *new text to insert*)``

Tuple A displays a segment of text from the :class:`Text` widget.
Column width is explicitly specified because some characters within the text
may be zero width or double width.

Tuple B inserts any number of space characters, and if those characters
correspond to an offset within the text, that may be specified.

Tuple C allows insertion of arbitrary text. This could be used for hyphenating
split words or any other effect not covered by A or B. The
:class:`StandardTextLayout` does not currently use this
tuple in its line layouts.

.. seealso::
   :class:`TextLayout reference `,
   :class:`StandardTextLayout reference `

urwid-1.3.1/docs/manual/overview.rst0000664000175000017500000000600412615524560017023 0ustar  ianian00000000000000Library Overview
================

.. currentmodule:: urwid

Urwid is a console user interface library for `Python`_. Urwid offers an
alternative to using Python's curses module directly and handles many of the
difficult and tedious tasks for you.

.. _Python: http://www.python.org/

.. image:: images/introduction.png

Each Urwid component is loosely coupled and designed to be extended by the
user.

:ref:`Display modules ` are responsible for accepting
:ref:`user input ` and converting escape sequences to lists of
keystrokes and mouse events. They also draw the screen contents and convert
attributes used in the canvases rendered to the actual colors that appear on
screen.

The included widgets are simple building blocks and examples that try not to
impose a particular style of interface.
It may be helpful to think of Urwid as a console widget construction set rather
than a finished UI library like GTK or Qt. The :class:`Widget base class
` describes the widget interface and :ref:`widget layout
` describes how widgets are nested and arranged on the screen.

Text is the bulk of what will be displayed in any console user interface.
Urwid supports a number of :ref:`text encodings ` and Urwid
comes with a configurable :ref:`text layout ` that handles the
most of the common alignment and wrapping modes. If you need more flexibility
you can also write your own text layout classes.

Urwid supports a range of common :ref:`display attributes
`, including 256-color foreground and background settings,
bold, underline and standount settings for displaying text. Not all of these
are supported by all terminals, so Urwid helps you write applications that
support different color modes depending on what the user's terminal supports
and what they choose to enable.

:class:`ListBox` is one of Urwid's most powerful widgets,
and you may control of the :ref:`listbox contents ` by using
a built-in list walker class or by writing one yourself. This is very useful
for scrolling through lists of any significant length, or with nesting, folding
and other similar features.

When a widget renders a canvas to be drawn on screen, a weak reference to it is
stored in the :ref:`canvas cache `. This cache is used any time a
widget needs to be rendered again, reducing the amount of work required to
update the screen. Since only weak references are used, Urwid's display modules
will hold on to a reference to the canvas that they are currently displaying as
a way to keep the cache alive and populated with current data.

Urwid's :ref:`main loop ` simplifies handling of input and updating
the screen.  It also lets you use one of a number of :ref:`the event loops
`, allowing integration with Twisted_'s reactor or Glib_'s event
loop if desired.

.. _Glib: http://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html
.. _Twisted: http://twistedmatrix.com/documents/current/core/howto/reactor-basics.html
urwid-1.3.1/docs/manual/wsel.py0000664000175000017500000000167512615524560015760 0ustar  ianian00000000000000import urwid

class SelectablePudding(urwid.Widget):
    _sizing = frozenset(['flow'])
    _selectable = True

    def __init__(self):
        self.pudding = "pudding"

    def rows(self, size, focus=False):
        return 1

    def render(self, size, focus=False):
        (maxcol,) = size
        num_pudding = maxcol / len(self.pudding)
        pudding = self.pudding
        if focus:
            pudding = pudding.upper()
        return urwid.TextCanvas([pudding * num_pudding],
            maxcol=maxcol)

    def keypress(self, size, key):
        (maxcol,) = size
        if len(key) > 1:
            return key
        if key.lower() in self.pudding:
            # remove letter from pudding
            n = self.pudding.index(key.lower())
            self.pudding = self.pudding[:n] + self.pudding[n+1:]
            if not self.pudding:
                self.pudding = "pudding"
            self._invalidate()
        else:
            return key
urwid-1.3.1/docs/manual/index.rst0000664000175000017500000000034612615524560016267 0ustar  ianian00000000000000.. _urwid-manual:

################
  Urwid Manual
################

.. toctree::
   :maxdepth: 2

   overview
   mainloop
   displaymodules
   widgets
   userinput
   textlayout
   encodings
   displayattributes
   canvascache


urwid-1.3.1/docs/manual/userinput.rst0000664000175000017500000000775612615524560017232 0ustar  ianian00000000000000.. vim: set fileencoding=utf-8:

.. _user-input:

**************
  User Input
**************

.. currentmodule:: urwid

All input from the user is parsed by a display module, and returned from either
the :meth:`get_input() ` or
:meth:`get_input_nonblocking() ` methods as a list.
Window resize events are also included in the user input.

The :class:`MainLoop` class will take this input and pass each
item to the widget methods :meth:`keypress() ` or
:meth:`mouse_event() `. You may
filter input (possibly removing or altering it) before it is passed to the
widgets, or can catch unhandled input by passing functions into the
:class:`MainLoop` constructor. If the window was resized
:class:`MainLoop` will query the new display size and update
the screen.

There may be more than one keystroke or mouse event processed at a time, and
each is sent as a separate item in the list.

.. _keyboard-input:

Keyboard Input
==============

Not all keystrokes are sent by a user's terminal to the program, and which keys
are sent varies from terminal to terminal, but Urwid will report any keys that
are sent.

============= =================
Key pressed   Input returned
============= =================
H             ``'h'``
SHIFT+H       ``'H'``
SPACE         ``' '``
ENTER         ``'enter'``
UP            ``'up'``
PAGE DOWN     ``'page down'``
F5            ``'f5'``
SHIFT+F5      ``'shift f5'``
CTRL+SHIFT+F5 ``'shift ctrl f5'``
ALT+J         ``'meta j'``
============= =================

With Unicode :ref:`text encoding ` you will also receive
Unicode strings for any non-ASCII characters:

=========== ==============
Key pressed Input returned
=========== ==============
é           ``u'é'``
Ж           ``u'Ж'``
ã‚«          ``u'ã‚«'``
=========== ==============

With non-Unicode :ref:`text encoding ` characters will be sent
as-is in the original encoding.

=========== =========================================
Key pressed Input returned (each in its own encoding)
=========== =========================================
é           ``'é'``
Ж           ``'Ж'``
ã‚«          ``'ã‚«'`` (two bytes)
=========== =========================================

Urwid does not try to convert this text to Unicode to avoid losing any
information. If you want the input converted to Unicode in all cases you may
create an input filter to do so.


.. _mouse-input:

Mouse Input
===========

Mouse input is sent as a (*event*, *button*, *x*, *y*) tuple. *event* is a string
describing the event. If the *SHIFT*, *ALT* or *CTRL* keys are held when a mouse
event is sent then *event* may be prefixed by ``'shift '``, ``'meta '`` or
``'ctrl'``. *button* is a number from 1 to 5. *x* and *y* are character
coordinates starting from ``(0, 0)`` at the top-left of the screen.

Support for the right-mouse button and use of modifier keys is poor in many
terminals and some users don't have a middle mouse button, so these shouldn't
be relied on.

``'mouse press'`` Events
------------------------

A mouse button was pressed.

=============== ======================
`button` number Mouse button
=============== ======================
1               Left button
2               Middle button
3               Right button
4               Scroll wheel up [#first]_
5               Scroll wheel down [#first]_
=============== ======================

.. [#first] typically no corresponding release event is sent

``'mouse release'`` Events
--------------------------

Mouse release events will often not have information about which button was
released. In this case *button* will be set to 0.

``'mouse drag'`` Events
-----------------------

In the rare event that your user is using a terminal that can send these events
you can use them to track their mouse dragging from one character cell to the
next across the screen. Be aware that you might see *x* and/or *y* coordinates
one position off the screen if the user drags their mouse to the edge.
urwid-1.3.1/docs/manual/bright_combinations.py0000775000175000017500000000215712615524560021031 0ustar  ianian00000000000000#!/usr/bin/env python

import urwid

RED_FGS = ('black', 'light gray', 'white', 'light cyan', 'light red',
    'light green', 'yellow', 'light magenta')
GREEN_FGS = ('black', 'light gray', 'dark blue', 'white', 'light cyan',
    'light red', 'light green', 'yellow')
BROWN_FGS = ('black', 'dark blue', 'dark gray', 'white',
    'light blue', 'light cyan', 'light red', 'light green', 'yellow')
MAGENTA_FGS = ('black', 'light gray', 'dark blue', 'white', 'light cyan',
    'light red', 'light green', 'yellow', 'light magenta')

BG_FGS =[
    ('dark red', RED_FGS),
    ('dark green', GREEN_FGS),
    ('brown', BROWN_FGS),
    ('dark magenta', MAGENTA_FGS),
    ]

body = urwid.SimpleFocusListWalker([])

for bg, fgs in BG_FGS:
    spec = urwid.AttrSpec(fgs[0], bg)
    def s(w):
        return urwid.AttrMap(w, spec)
    body.append(s(urwid.Divider()))
    body.append(s(
        urwid.GridFlow(
            [urwid.AttrMap(urwid.Text("'{0}' on '{1}'".format(fg, bg)),
                urwid.AttrSpec(fg, bg)) for fg in fgs],
            35, 0, 0, 'left')))
    body.append(s(urwid.Divider()))

urwid.MainLoop(urwid.ListBox(body)).run()
urwid-1.3.1/docs/manual/safe_combinations1.png0000664000175000017500000002211112615524560020672 0ustar  ianian00000000000000‰PNG


IHDRv•B¡{*PLTEåååÍÍÍÍÍÍÿÿÿÿÿÿÿÿÿÿÿÿÍäYIbKGDaf¸}	pHYsÒÝ~ü#¸IDATxÚí;oWûÀLJK(vÖ	%ï,aùØ’?.Ü¿I1]¤tn#ªm"A9RD9Ò?iG‘ ŠÝ""„ù.ÿçvn3;»s[³Àyvw|æ¹s~û\&ï[8¼xñâÅ‹/^¼xñâÅ‹/^¼xñâå›”Ql…ðskÄ÷[«¶j÷¡‘«Ô×.²m]IV:Wä@¢¢ƒ ¼ÏAvŽ­UÇ]Á.ØTvg·I쎊w4Ú‚K†¡XPƒ¶-Ðl9H³e—舢Fl‚=Fh‰¢í`;Š
TÁ¸¡Å¶]UÍh(M°¡µÏ ìøœ[Ø÷,FtŠƒËýÙYIhgßn;
¾ÿ6qŽ ¬¾álÚÛ¥Ü,i";û¶{ŸaÙv£zvN	Jì0ƒØm3ÄqYEgØvÍ.0û¬Ÿ•asvâôÏ:a·-uf²E³CScvâ–u†v/—õ³‘!ÕŒÝhè¼;à¼äÝA›¼;XgÞšÝHáPì¶F[ú!p1»Ð°Û¶ßAz	;·ß‘v{%»È°Û¸ßé±(ÃtK—•‰kv F#7ª”wfttÎ(vÖœ
P‹&—”Œ`›¦¯´Ï°sÖ-ºešQ°Yr°1ÿ’ì¶6ŒÝö&±Û
Ã¥š0ܘª”ó†•/W Ϭ+ʵEN×jôH±ÇÏ–ìÙY®[W’“U'uûÐ&×Ï–mþ¥Ø_;‡”g÷%Ù!™ãgÏ‚c¨ºg׎
¦kX‡×Ž¡¯ÙÎàsÍ.Ñg胤Äù™:„œ]‡Z;ƒCž ¦sýìä„4h09Q%Íuˆ`Rgu½78:öµgëâãla A
×pé$xX4íì#ôÏì€þ©wÆ	…§=	ÎT~‘á:Ñ$
gvÒ-МØÙÇQ×O`;<¼B‡H»k%vÍ31’Ýu†…‡?9,v'6»“
»ªÆf™ØŸüø˜êUgÓmsvר90ëäÀ×ϸ^ƒ³Àe‡xvæ³æìÄyˆ¬³p;+ïT>6d÷lmywÂy˜¼£îu"ÃA³»ëçÝ5nj×T×+³;^Æîذ»¶¦~w‚Ir"]Oò.hÉîİ;®ßÉœ•¼Ó\ˆÛâ9ëæ¯f^?gO;=ge˜9ë< T44gÅùúpsök”“Ïz^<»¡åºó$ìÅËw$Ï­+ÊNãÈÚ
i·‹çK~KC‰ùÇÿ3×’©$àÃnñÿʾnø—a÷¼!»`ì€ì~ðìlvîjØ!„‹çσ¨±ç;HdG´;ÏárÕw±C‹p„Ò<·ÃÅdH¡’ÚQÎÏ;€ãCãû‡ÿa	Â燘±ÅîÐÄ?Ä6»XôtĈ½×Ã=§<ÚA||ƒ·Ï	"^ùðÈR1ØqMJ.ìì£/ã9ê:	±Ó‡çM±­1Î1»Æ?X“È9wˆÌŽ [Ä.¨gWÑØì0»þã0鸻Ø.XN´ŠFW.éã8X+;¬E<¦BÇì è˜Ý/„nvtκ…ìâ¸;ð»v;’w›w‚U%¦°ÛišwÏ×”wAvʬ·fwð„’wN¿“ËŽ—°»0ìvz÷;›´0·ß9ìT¿C?éwÁÚOæ¬äž³ÄQà5‚wdòZáåLTãÕLðns6дà¢gll?øÒœuóN
d‚Îsv}‰·m›ÿØðâÙ
%Γ°/^¾¨Üø>—²@¹,[Aõ«­»¬Ûgóe`íÜA¦øùM 7[Pã0•nN+¾Ó~ì‚
;uÃ?o´awchv7+ì¦Ùëa·ð¸Ã.ØhvXk——¿þz	|ãR*ïÆ¯¸`.ð¾¼´ØÝÓåK›8cσÎ첬+;ˆü‚ì ‚™/„Ý£Þ}¥(ö¡è
8ӏLS+
&0óvõ’ó¾SÏûRà ‘© ðP4hã_±ŒéBvNö0‡¶ì)Bš šk¤gWÈ4£-q€’áDÁ³ÍB9s!àˆÝ~97gû³j®)ç™ÍˆÑ‹Cv[%v˜‰a‰ÝV=»­*»pPvrr<欰-cÖ³+ìf°ÃÊ}vX³Á`ìö®W•uevº»²£JïÅn«mÞ…ª÷‘iŽl
™w3λ°>ïÂFì°Õ¹ì¨/ÎLTç~‡ª¦ßÕæf7RΚ]àîÓ£ßͰKͤë-ìwÍØÍt˜Ù“šwÞŸíë‡Àæy§Æë(ØÚª›³Mi(›9«kxCÍÙÙLgŸ«´ ù8›-˜³»}ã¬Lû…eâšÅ}fmÙu—QåÁ®¬éÏnH™Uв¬Ùv[_»ý«dÎW4£ŠùËʾ=h*æu²ûŽÅ³ëÅnB‹‰V—ùOj1$BS´Ômå>UÙµ®$G‹¼ŽjôxŒ¦ãeᥭ®žcš¬Úè
Ù9¦£ÒvžÝc‡§Œ&“0‚ª›Œ#<²œš4xC‹(²âªÄ1ŽÈ4&Ó8Š&ÊyL;›
[Éññn¸{|G†òÓ˜ŽvÑt¦£#Û|ŽK8ЗöAÓîññ‘ÙYïs´”i-;8Ï„eŒøø&dˆrÔ1¶2'7K^3÷	›ÆÆ™~;´f·+Ýê(D|GªuíÂiwCÒìÙλèQÎ5Òs°aDšÝ#qèÌN†Ç:U‰ÂnRa7©g7©²{°“ÒÃÓ[va‰ÝQ=»£*»pvQDõª²Îf‡¦þìÔ>]ØírU™Iíñì°fû°KÞ…Õ¼·É»0ŠB—iÆ¥&Ð’Ý‘ÜÉ;=
±C—ivMTç~‡é%'tûi[åf76ß°‹¸¶îwº/™~gØí6Í;ÍÎß»æWwa'svl²CÓq4–Aéâ‰YÖŒ£È6™9«kvÌv˜³GŠpÙUSuÁœuرϱm2sV×,þŠNy·TZu\‰(kÚ²Hv+veÍÐì&C³›l*»£ÁÙ£IËcT‰p4í7Jô#ñbÍnÅÜ¿f½xvë`wJ‹S­Ú[Sã;ì¡ù´ß>ŽÜµ®$çv^»áš.šnx~5ìÓ^iß«aw·;Çt^úžÝ±ÃSœž†gg§á)TÝž>ÕÙÙiÈ´w&p´Î^ÑPàÙ)oH¦³³³PÿнPí³”iY..î†w/.à€Plçê0çh:Óù9-Îч5N8›\w/dg¥×ù]Ùù|)SawÊMjœî©Žµ'†S¢	ºS¸Ùs­¤áõ)Å…l:ÕÎps&­ØÝ•Þ€`}®Õù]‚x—Nx¡9í¹.&7•HÏÛF¤¹{níÜ€TjÏjö||Ã.¬°ëÙ…v§ÙI¡áYÎu¯°;¯gw^Ïî¼Ê.lÍIÏIsØ=4Ágv´Yvw¹†ÌxdvPpçl¾0L	veê±Û“¼uÞ…Ø«ö,"acvØê\vÔOMTKvçrL7ï빞¼:o˜wáÅEè²#Í]Õ°ßévd÷»®ìŒó©=ÇOõïÚkÙïtrú˜B‡Ý]vn–wV”ž·wÍ/½XÍNæìž"¥ç¬4¨33÷Js¶¢)9ïY¦Sý»N[ÏÙsÅN(ÐB`‹øV“׳;=‹(c2¿´AÞ-–½f`‹ä´R”eÍi8”œ·ù…S¨^³ìö¾vçÝÙQõu“=õH\£©˜{ˆû$ÜJìy¼@s÷â¼3;/¡g·.v÷~âUl]E”©$èÿdœkÜVîÓDü¼’ÈýWK<“…¹ŸX±I“_Ú‘]܀ݽ
»xmìîWØ%mÙ…võ_Bgvavá²û³sÐÅŽÞñ=8=~â˜?Yg
8üTbÇñ=2Qb
mô“Ú¼7;,ºûIrÿþ+¨à$‘Ó%÷á?P×;Ô$÷“2;òK^½B쥲5¸O"_@cvÏGŒuÝÆvÞÀËÑ蚨1›bMï1€öW:<žø~:B~Ýu?±Ãë•“YòÀ(¿â•Ú>§(¡F>|Ëû´íw:)öéhØ÷,FtŠƒËýÙÙhfœÅ3ħ:(û(vûÍ“NgGÄÇ>," ‡sy8?,åfE3?¤­æˆoŠ‚}»ÃVIWۯgW¸ì¤ ãÌ>=ÙÍùüxÔÃÈ*.«h˜š·^vT¡ÍØ…\Üzn—Ø…aØÝ<âz-¢¢ÌMØE\å*ëÊì¢(êÅŽþâ¼ÃŽzÕ¾!µ’ÝLò®¨Ï»¢=»CÉŒywØ0ïæ’wE}ÞõìwåCî«ö²™I¦vè#í±¾ßµfqÃ:”®gá í¼;ò‘¼«íw]ØÍøœëa:3ϲdÚ×3t¿®ÉDf§j¶œwÅ ì>R[ÈnÚ0ïT¦fËyW´d‡d³ã^æö»_J¦òNÍ
ÕïPƒ¦¡ó®Ð즺ß዆€KJz—«A•wjÕïhë{iÈNhqòŒýÇ©7š³N2"ë8Vy§ŸN8/4g™nìþ}]ÿ&°ÄJRçʧ$I901!x÷ºêÜPRü¼•ȧ·M£ªÂ±ÖzIþŸÒªsvE…Ý"HÙýÛݧ
»tìŠ윳m»b£Ùae%Éë×	Tð¿‰ÔÙ¿¯“ÕX×6;Ѐ鵅#yMa„¿E¢Â_+èðN’®ì° >¥é§Oo¡‚ÓTe!kà.%;˜¨ò£)ô>Ÿ0þ-E}z+„t½1ª;}x9œÖwøB¯­„Wbc{ù‘H<û(v´O{Q5‹§‚C¦†
j
AД¾µ½P#®#±b—®Ìë:v¸N,vüIÜ‚]¬¨œ–V™«wÒ2éªìðŒttÍŽoÐJ¦Ô.½@å´TÙmÞÊg v ØßRvI².v PÀi#v踌îÓžö»ÅyW4a'…Y¦fËyWô`÷‰Z”ÍîS*dRñ
v©Î;]³¥¼k&•¼+;9½X’ýÎa‡ŽÚ¥ì‘¸ýŽÂiŸ>yWhvÖduJYèR½ËÕ¼å.IQjV¨~‡IÉÙܘÖV\pWRÕ•°ý5ÏY“fIù‘ç,–¶°SO'©Â“¤Ã¬ø”ÊhýDó‘ðIQ±‰NŸ¾¥,tI}b³£A*£†`‡uô9Ï?þœçrº9Ø@M&¸€é3Ý@_1Ÿ1|`Cˆø „Ø'—7F-eÇ ðèxDÅ.£—>¤ùÛÆ ÷üŠVí“é]zŠªY<œ-7HàüxW2À)|æ#çöW &[ó|ðnÀZòQìòå	nØ™,S™˜i®­¨œ–ªZÍ[ùÅNÈ8ì
fÇ&q+•ž19Êi©BÌ[ù4`—eØý‡QKØyÝì ‚™/„Ýg]ØõìÈg	»<ϲ³gEcv™Î;]³¥¼ë/Š5¶
;EÁA\yƒ¼ËuÞéš-å]±²fMçrûe¢ÓïÊðoœ¤f…êw˜”8-Ì»B³Ëmv9=75+ÐíwE©ª.YX³Bõ;ü:Lõ/c§&&â‚X`	þçL3yñü­Ø©§œ³\ÿD¶÷¬ ±™ó0•šU5&5KyÇWR†fdnïñ¡Äî3E}Pìô—AÑx!²«gEQƒך%¯°ËW>ó­AT¿ËŠÍ—dW0¯@>×½ÏË'â:Ùyñì<;ÏγóâÙyvžgçųóì<;ÏγóâÙ­‘]䥫xvžgçÙyv^<;Ïγóì¼xvžgçÙyv^<;Ïn£ÙmÓ–Û–~Ûº–åÀº.q‹Ên]dŸpŒQŒWEŒk÷QוûLZ³‹6•sÎñªÃyv_	;<|0‚\P½ÛÒvo‚r>ˆlgÖÀ
-‚íaœs‚5†“h†pÊÑIƒ6ÔŒC“&ÍCÁ‹œk0“pÜøŸT³ï“eÛž"¤	lš‰n–ó6‡oó>Û}“ÎΑIÄ'#‹Ÿy,;òíüšHÓÄ=&¼“l>žˆßx2nšt&ïJì0£»í;vÃ3ÎÛlÚ6ßÁàìÆUvQ-»q‰tt‡îæÂ.êÎjÙÑjvRÜÆY³“}®ŠÖc؈݄‹ÛÌmÃM}Øm·É»mλhAÞm¯#ï¢kÐb„š‰!µšÝXø/È»q·¼»ƒº~·]éwèÌìÜ~GÚƒµäfgÍ͉êeã(\Á.4í±ÔïH;é”wj¼ÂhÜ®›³%Í;oG‘;g#Ô¢i0vXf8e˜âÛÌY]³X•9ë<‹°ÏX±³æl„Z4u`7°lGk‘êÁÊšI÷ÍÇíC†gwðÅØ;³›l»(XÛÿ¾V\M›çÚÊ>]yéœ$AvsZ̵êÐYÐßR62_¸Ë¡Xç•ÝVKÌ?ýl®%SI*žnÐ"çµ±+õ‹³3ÇõìÚ°sW›ÀŽ
s>ð/+Ï¡D5þêôÆ¿®lW/9—4‡äs¡Iác„ó†QKvø~ôsDŽϣ˜±ÅîhâG±…ãøƒÏσB"M¤M?ÃŽ6§Œ8D||ƒy^ÀÑáç¥ÜF%MÁ[!¬yT蜊ߜ7Œ:à4±©Yƒƒíð*kðž%M5&v~Vö“@ŸÙá	WØL…U¼†]Ô‰œžóE±‹í‚%}U#P9-uk“ZÄ®àòRYWfWÅJv‡×kTDevhŒ]¯d÷}³CÓ°ì%ï¢ú¼‹V±›+ÇjÞ͇ͻh»XçªÙØ6
ÉîpN
ëPu½…ýn»‚ûæ\ºžõ­ö°+;&ª×Q/sû]TÒ GŠ@â(¦áØÉœ•¼sæì!ÿ1õÃEsÖyaŸ¹bgÍÙµhêÆNhQ­ÉŒcëÜ4gvˆæ2Š2ù~Ö¦áØ­]æÑÆHodWÊîpsعÕþ5ä]QlLÖ9ÕþuÔì7)ž]v_DÑã),q2]³ØÃÕ&î²Æ¹¤øy#'ošFU…c­–mÖà1»¨ÂnÙᮚݓ
»tÓØ9h6Ž]´Ñ즰˜N_¼˜B?žNåt°Bë”TÜŠÝcðaÂñx
W}üœ¿Ð™ŒšH›^t`÷$ÅOúäɨà4UYȸKɦ'©ÅÎh"½ÏŒCQOÞ(B¤‰´éMCv:•äx/ÔA•N‹?hñÂ>4ê*¼ç'T§´åcf=Ú‰ªÙOžòJ‘Åû7Oè¡)}cz¡F\)Æt‚”Ø=aÖi£W•dÙ«¸¦Êaj,ùW5•Ó²´y$;³SÙaØñ
ZÉ”Ú[£¨œ–vv³I-d7…‚kÂj»ŽšÖÄ
8mÄkØ¡©#;lH5ìjHÕhtš:ì¦ýòî	µ(›Ý“TȤâìRwªfSÛÔ=ï"ÅNNÈK.)îweÍœ*²Ÿl!Ù̦^yiv©™§¢N)RÜïÊš7Ü%)J éf™ò0iÃËŒFëTMUX™9KÇÆrsI=VIjióÅN™pgejÏ’‹Gë=
U±±I%e¡Kê	›
æ›ÔL8›Úæ]©ÙW¥íl\ mÓ­"iåÁ-ŽÄ	Þ\±›ö?·IÂMegµÑNì¨f|Ú?íôÃvÓ¥é›Uš>_MÛ½üÿŽÒ]<»>ìžþeÝ>Íà’Ùš([ÇQV¬³ÍBço‘zêÙyvWÁŽßYf±{
…›Eò–rÍÓÌÂñ4£¨¿žþ¡OÿR„HiÓ_ß0;GÇ#*v½ì¤#Í_6¹ç@ISÍdCb}“Ü\v&ËT&fš‡ëck*§¥]õlR‹o]–u`÷£³CÓ÷®”5ÍØe:ï”of›¾}v¦s¹ýŽ2Ñéw倞*v‰; ˜¾vjb".¸–àSg2˜Ék4X˜)vÊÄálúØ­á1âÛEæ²393¸o÷Ѥ”wY6p–¾á&׬Ïγóì<;/žgçÙyvžÏγóì<;/žÝºäÿgô–ï•pVôIEND®B`‚urwid-1.3.1/docs/manual/bright_combinations1.png0000664000175000017500000002047712615524560021250 0ustar  ianian00000000000000‰PNG


IHDRv†ÑN-PLTEÍåååÿÿÿÿÿÿÿÿÿÿÿÍÍÍÍMMMÿÍÍ;]äbKGDLò	pHYsÒÝ~ü «IDATxÚíKki€Û|~êÒ^!„Þe„Z
2‹ÚÈ€¸©…ˆ«Ä]!ˆË^	ÆËPùi! ã(Øú&ÿ`È"‰7²¨ßðÛ{«êêºvÒ‰ïéîê·Î{ÎÛ9OŸK;›¼xñâÅ‹/^¼xñâÅ‹/^¼xñâå\ÊUzë®R_órÁº–›]-so$+ð­ãEëUë¥ç¨k¹Ùzóg¹¬ìœxÖs<»³Êã9Ú¸Bõº‘“†lŒæ_ØÆW/ˆû…áÕÀY!­`ÍŽF+ƒõÑnF"ip5ëSÒŒFŽf]ÁŠŒ“v_­¯·øã†vö]Àœº::‰vAlŒfÈÉ5´/\Õîúe‡XVp5XŒH³².;²Ùy´"MÏXá“H
î+âÞ†ÝÕ;ÌÄAŽÝÕ;6CJÆø‚A?X»õ"»A)»õ;éh¸>²Î(·`eÈ솃jv¤¸1º‚vxÒì°èFµØ­pq›¹M>+ìÞÝÕ&yw•ónàäÊǰŒF—iV©jvëÂßÉ;=‘:õ;ìS%ýîj¡ßQS»*]/Ïn1ýN³³ææŠêeëƒQ»‘i¦ßvíúÝО%s6§¹ÀÆWí,» Œ{³<õ0ŧ™³ºf±sÖI%¶YWìdkÅ|D«9{6d¥ðÃ.¯Y9·±/šÝºg7¿xçhVFç·æ¼x1òŸëüX×ÜVN
–ÁìcKÜIˆ¯ßdrñ·
‡ƒÐºæ—¶oåùì‚%cw±À.\Jvƒee7XRvôþð´¸n±
\·P×Aê
¡6äî=³»â+¼xñ7¨à0T‘‡áo0-°¸uð`ëöCì%bÇÏ$±Ø]†ÂMœàIs9±P]NÈëÑåGàzù‘ÐKÐ_Pûý±»2Æ×øÊ•§PÁã±õt{ ¦-¸ÀÖº¾(8²eP]AW°Áã©Ð£áò|ZƒÂÇÁ+v	=ìÈIóÈN/¹ç·ËÝ2ùºŽà쬠H®`üVL?Þã•6àˆÍS;r½ekž’
¾á˜M‘æ¸ÖGìL–©LL4ׯIX†Êii2²`Ü;!ã°0;Þ³\Áš-G#P9-MFŒ«Ù%Iv—ѫȴ'Æ*˜ÙñBØ]Ñ…]ÎŽlŠìô9MònМ]¢óN×ì£ÊÇžÙQc+°SĹN_Ân¬óîéØÉ»Zƒ"ß¹Ü~G™èô»|`—;i…Âhßy7ÐìÆ6»1‡>65+Ðíwƒ\T]r`f…|Þ×ìwzb".,8”—É`&¯íuñ	ðİ»Lõÿ¨v46Ç¢.»¾¤—,+•­Â»¼f«]Ro´û{λ3Íîy¿„çïÍÖ¨
„­š¿„ÍîçÏ®»ZìhÕ¶³Øž8ö;3OÙvËÌÒ"¿½øÅ\s[9)XºNå±v6‘e`gPxvMع«e`G…¹³“N&;é”è¶fêTnwÀÆ®^2Îi¶Éf;Å-EO&
¹½j°Ãç‹_@¯-+æ 	^ª`6¿€W
¡®/HCîÆ«#;m‡gññMŠoÛ†ÝN.7HN3á£ßN:QùEÛä^ôª‹‚LÍj²¼ïù
p)í¥áÍœWkv©°ÃW®¨IÙ¶&©ÅnÇf·Ó˜D1ò“^,¤/j*'˜b÷K¨/£àՉ݄
.UY—g7Á‚«`·=ázM'©Ë.%÷müˆ>ØA%»hS·ûc·-y—–ç]ZÅnGê¼£–¹“ª¼69Þ1ïÒ*vÎ;U³/±
Ôé‰ÝöŽô%éz3û]»	÷Íézfžvd‡ib±“våö»4§ù…»[j5Ç 5ÜÊÂ^ØÉœ•¼sæ¬ÌÇíYsÖ¡À6;Šž³2ÌœmÊNhQÊŒ
«ÇÓÄt(àœ}ø4)É;Y<°ûÉ»ÅIcR‹“ޤ<»³Än»Ñ/á…ʧÒÏ»ó,ž]v/o¦éË–¸@	«|f°»:Ä]69Ç•_wdòêÎËhæ»XŽågXZ›]Z`7/ð“f÷ªÀ.Z6vNHKÇ.]jv!,ÂðæÍ*øeJä°ÂÝTÜ*æ—`Ã…êeWp}yŒ_Þzàù’4)¾ŒWv¯"|E¯^Ý
Ž"‰.zwø‚º¶Ø¡&zY¨^E¯Ðí¸G zr zF¶Wmv: RŒü&†n’;ZÜ´“	u
ÞóùÉJÛ9¯*Q5!‚(u ¥ü‘
wìô’{~{uG¡§HÛ9¯Æì$ËnZ…*ƒÐN²/j*'˜ÆŸJ¢…E¯&ì04
ÓJ)Ô8[¢¨œ`ŠŸ…ÆE¯þØ…P‹uØA…–²yY8§+;¨àZìаŒHñœfì°W•°+!U¢ÑiªZ¦Ê;'‘²{E½jFÞåżLäþ©†v$VQ:ëœÆy—*vB—<rùª$uúw7:Oa>!4
´EÞ¥š]dïPÃÊ÷;‡BDÃä•b'M-²¦wÍì°Ìh´†jªÒH4Isk6óK•¤–1ß
)9B–¸Ðƒ»6ºHFë+”„OªŽpö܉l;9v8g_¡&eN&€4¯£Vy—köEi&³|»¸[ÉÓ °8°»0mò“â'eG5[”—a§ØÍínB%Ö§t<ÐÿwÏîtؽ¾Ÿ¦¯cXâ%®ã§ŒsçOˆÛ£%qn§éŸŽæÏÇs¼’’cfHÉ9uØ¥vU1
;Šp)Ù9ázvØÅ°ˆãû÷c¨à×q¬²5÷Ó×¼ö¸Ð1kFß'ÿ”¼bE¯êœrvôü“êU±K\óSìP“<¶P%Ñ+ItM½$yü˜4t öjÉNÇaÀ"¿Ï+•p§üx
[¯c;™fjÄ”|ä ªsªðQðŠÝŸñaLšÄN/¹—·Dlá-
oæ¼:²Ãuì°ãq‚»¸eeÇÊiYëœ
vV–%J“X쌛°„‡ÓR³#+2.z-ˆ]U¯zìи„ÝìsªØ%IÒ‚z•°Ã-ü°õÆ»’ËŽ4¯Uc2ˆç³#
{¼¾_ëœê¼³fE]v¤a'µ£¾Ý:ûÌ»T±ãÀè*jÒåHQçÊkbînì§uΙ;¥s¹ýŽ2Ñéwù˜pwc§ÄÌSÑG÷¼‹c­¸à†ëù(Iˆ£3ã)ž˜®æ>Ö¬Ê;µUuμē‰ù'ÒJ°ü’Äà°æ¬C½°$5)±“åŸ|Îãîy§s&OýçK·s’´'é@êL²Kzw쨲ª4í¤ý9&½¥×ì‚Øyñì<;ÏγóâÙyvžgçÙyñì<;ÏγóâÙõÏnꥭxvžgçÙyv^<;Ïγóì¼xvžgçÙyv^<;Ïn©Ù]£çt:¼fé¯Y×¼¼¥gÙV¿òѺ’¼q“7Eゼ±¼Š[½°›žAvSÏî̲CFÃ!“z‹‹!Tï5iÈF1z‹š·–1i#“ÉÇéÇÉÐ@‰jo@#\>¢…Œ?Úš7ÊÏ'»O»³ÚÙGium:Ì%ÚЦùöhÞ^ÓÆ°¸ÖÒélšpŽ`Øo¦óD%²û˜ÏÍ7ߨš‰q‡-4žtL:“w9v˜‰ù"u2‘4o5è·²X»Lä
å‹S‰åì
v§­7çrKvP³Ìn8­ÅnzRì>N¸^§“éLv»KÙMÉîZü;1v&ø²¼›ž»¡a÷¶¬ß]+ö;½²Pv¤‚/éwsÙMv =æ¯0,¯•ÍY‡NÕ·jë-ƒ\;™³oLð«tB£¶ŠsÖÍ»‰Ì†ÁØ;/žgçÙyv^æ±ûô¾óuš[¤dože[]$à·¿~5×ÜVNÀÆ5s¶~nv€gׄ»ZvŸöື÷ϧöö@!„>íí}"
oMù)HÐnï‹Ý'ÒìÉ®?íõŸý@àðú+ E`±û4Á_ÍŽ5¿ò–vïŸÇNpø
¢VD>)ÍtÏ7%¾¸iåY‘ñ'½÷ϼn†Ð¦fÿúÕÆ€[­!cºðC¹÷-©D
“a|RéDù£8ºìöfj¦{zk¾½^ØAÖp%*v]°œhØ’o0§{aZÆ·–Š],;¼®ÙO{þÞòåÝtYØQ£ãî&½LáPìhk/×ï\vÿ¨$ý‡s¸?v 6;âäúÃŽúݯjK»/†ÎÙO-/1U³DÖnýj˜Å'ÅjOR°/vB.zÆÒ ú5ßaE³V¹/ˆÝlÉßÏØïÓœµH¨ajåݾ°¢YË£¥3;“Gë;=Uß©ù‰[H
9vz˜Å;Å*”ì'ï좛.«”†ÚiZþÌì¬öwªì¦ö]:¡â4<´óÿų;%vÿ1þÁ(Q™-(›œDå^Q·¿/Æ×Y€¼P׫(à[æ^ïØ™ì¦v³ »÷vñ²±s¢]:vÓ¥fÁ"Šþø#‚
þ;Šþ^‚‰°®mv ­?,vGƘ,#ê=±{ã+~ÿþTp«,d
ÜÅ´[ïc‹Ñhv¬yÀ[`Iš¶ìtv0šˆ8à`ýa¥ <"y)cf÷GI–6U³1 bÃ5ÐßÓcŠ[xc“ÏiÈ.ü˜Ê­¤ÈŽðo+¥8­œ‚­±ŒñÙ9éŠìpÝYìø†3ÓÊ.ØÍûj‹ží’®6;%gŸ";ìw³ónºìÎÔe÷>2±ƒø´ònªØ	Ù‰fô;—÷DÕ
áKX@ÞM5»XO¥¦Næ’ÒM-×ï¨-ÌÖ®ì¢HF+.8æ(2T)iÎTQ>ÿŽ´1§`_ì ¹x´â‚C}?°¶ˆÎ)=L­¯!V4k¨&Ð%ït:M{š‹¸ðÃ-ž¶’=ˆg×;ªÙ¥ªÐ
Í)²óâÙ»ÿ³n?$pIlÍ4™é^Ž£»õS²#R<;Ïî$Øñ3I,v p“©<¥œAó!±Ù±æÚ˜4?;†]B;éHó?ÌÔ|H¦‰6¦›ŸG;“e*R:‰fÓâSc<§Ï9»$ñìÚçÝÔ³kÌNu²$×ï(~ç°£~÷¿©1þ)Ù}HhÎâÛ¿ŒÎöd°&¯Î»DX)㟒ÝOôâov\®^Zå]’ø´k_³^<;Ïγóì¼xvžgçÙyv^<;Ïγóì¼xvc—yi+žgçÙyvžÏγóì<;/žgçÙyvžÏγ[jvkô̲ᚥ_³®y9°®sÌfÉZÓ¿ï^£M¼ˆb³Êc³ôu­wNµ¥Å.[»ƒn윿~3Ç`Qì=»³Ã†CìC¨Þ5'NÒÑñAf×Äý`¸¶¦½X3lÝXà¯?Äš³ÍÑnFip5›SÒŒFŽæ]ÁŠŒÊhSÜG››Úë5£Š~‡1ÛÙw€‰²–
¼9£r
mãµ²ÁëåuÐ2éìoþž›Ø÷#ÒnŠÃŽìGvÖJÓÄ3ù$º=´Ü×auzTT;ÌÄ,Çn-ǎ͈Œ6.²[ë›Ýf‘]VÊn3ÇN:nêáS`·ÙŽ–×db%»)ncLì†Pk¼=4ìÈxQì°èFµØrq›¹Íì”û!/ä(Óê°[k’wkœw™›w‚uM%¦°[ëžwÙˆkÐb„šCCªšÝ¦ðwóN°nêÑÄ«Í:y74ìÊúÝZ¡ß¡1³³ûle»6îœw:0knª^¶™*ØL{túlå?‚Œ+ónX˜Å9›Ó°ñZæþÊYÓ#ØòR'·=òFz>š9«k§0gÄa›MÅN¶h¡F°ûd\Å®wéR¡¥rXøa—×v9~³É?6λÍóÁ®Ë/áùÅ;Gs8ÚìrzÅ/ác÷Sˆg׉Ý.-vµêØYOûÝ™§»Žef³¤Â2à·£æšÛÊIÁÒuªñYý±³‰ôÇî¸5;ƒ¢'vUžvuB=YvT˜»»Ùd²›íB‰k Îävlìê%ãœæ˜lŽ3ÜRP&»666¬™Ôª#|Ý Dx´¬P@Š#°›à€BèÜ@íj‚àH{©o´eìR6#>¾ÉðíØ°ÛÍå&0Êi&|âÛÍ„ËîqÆ'ãYÆë¸A[$v¾©šÕ8dy
Þó-®%3^rhSp;‰):®„]Q“±!l©”*²ÛmÌîˆ3F‡)Éhðä4•ÓҰ˘Ý
ã%л±›PÁe*ëòì&Xgì¨Q§«‘ØáÉ»¼=1ìŽí›±‚JvGhSdZfÇawd؂ݱä]VžwY»]eèä`ÝU‰)ìv»ä]VÅ.Ðyg׬ªSØòîx—Ö±êz3û]»	÷Í]éz6»‰T®aw¬šl5;›ÝÑ
·=qïÊiTS³š£°˜e`jV:`Kv2%ïœ9KÓPÍÐÜœu‡mv;óÓg׸/5ykˆÐ¢|‘Öd ‰é°Ã9{ÜPìdKjVÎ9
qš¼7Z³;ÙmòÅJ»
õìÎ$»Z¿„OFŽœ’?ìΡxv]Ø}¹•e_BXâ%¬ò™mÀîêwÙê@’_weòõîËhæ»XŽågÌúô*vYݼÀ{bgö+Ø}-°‹ÊÎì×fçD²tì²¥fÂ"oÝ
¡‚¿„¡D+Ü
yAÅ­Bý6¬Q(¾„p×/·ÀøË-¡sµè¯/Ú‹?‚/Õì¾FøŠ¾~½ETôîðum±CMô5²P|¾¢Û]p@!tî¢ö+jàdí7x ]*Øéo„°naè&)±£Å-;™PWÐà=¿‘iqÅç|	Á¾UÝ^uÍbX›)ãàˆlàð¸k§—ÜóÛ×»ý]vŽôIhE°ïV€›ÉN²ì–UX¡2í!û¢F rZvìþå–ñ’hÄNBsRê.o…Z¢¨œ–†]Æìî/Þ#»Ê«;¨ÅYìØ·
;2îÈ*¸;4œÁŽÝyÛ°ûªûB]vÔ~f³+!U¢ÑiªØ)¬Ö—ñå–Îèzì¨3Íʻܠ˜—‰*Ï4;…Õù†æLžyy§Ó%…K.)î\yjj™u³™ehjV:`ý¼Ë4»ÈÞÁt,ô;'øˆ†ÉWÅNU%³‹˜edjV:`-va(s/TS5Sµ„¹Ba‡a.Ëxbæ4ˆù–b'[R³”w·ÌàÎèCoU²£¹ñ4”šUUG0iTüÖœuØáœýÝUìdKj–òŽ.{ÑgÝ­›w¹f_”êì¨!a·sêÔÑIž\‹]ØàßW?-»löØûö’vÖ/îVÂ?Z!íNöÿ¥½xv]Ø}{˜eßbXâ%®ã§ŒsçOˆëþ!%&ÎÍ“,ûîh¾?™ã•”3OJœÃ.+°+	¥wvqvØBØ%Ù9áxvØÅ°ˆã‡c¨àoq¬²5³o¼ö¸Ð¡jF?$ÿŒ¼b¡ó
Œè0:P{Ű ã9ìèùêU±K\óSìP“<±P$OÐ+ÉtM„Îw0Bã'øz¢½XqCv:føëá	‘?ä•J¸3~|ƒ­o±L35bJ>rBSºÇP^{~–;
^±ûþvœ¤Iìô’{yKÄH‘+Þ'OŒÁnn&;ŽÈfÇãwqËËŽ34•ÓÒ°ËØ8¶¼â¬²C`hV–%J“X¡7a	*§¥a—±qby%¶;(¯‡ø?•¯Å‹ì¾±;m[ìbÝ ÊÙ%XgÙ¡W‘Æìx!ìè#Ú³ã†äTj¾©gÏgGgš`}è|U¹KóΚuÙ‘Fç™f'XŸHb
;×½UÞeŠÇCWQë†e…J+¯QM¼b›Ý·X¨óŽçü‰ºs¹ýÎI“™P5µ,3³‚Ù}g–Òø8ɸ;wßb™{ܰT±ÑÅó Ì¥	o;š‡˜\*ï2õPÍÒ–}Íâxþ/"51¿'j$&‰ÁaÍY'qÐ+ITÞe*s©fi‹®tR:¹]ÞéœÉ—OUA5–V&YßÒ¼BÏ$»¤{”'ÁŽªBÓQø=é?í2®Ù^ÙyñìNˆÝgYöcK\ Œ ¼r2Î5îxàÒ²Ë
ìª"÷Í®êÀåfçDáÙ5b7†ÅxüìÙ*øÇxÌ!üøñlLjÚ‚ØÓÍ+}l…ª¶Šñ3:(ƒÁCèÔ?ð°Ó¡Â
Oø¯$Æ?ð~L[¸üAˆ§Ù²4c²¡·±ØÖ>ð,²ã@vh ·”Y®¾Ì–­¨œ–Í<'ì –$T^H¨ãñ¸’ÚÙÕ9ð¬±ã>T`'ž9ˆ³"©γFžÁ¼Ë;CBýÁ¡KŸâ”¡†åö»g¹¨š:ÎxVØá”û1–qÇ}j<¶KìÙ˜'áX
c;Mhb:‰Ã^*ï2ë˨uàYÉ;*¥UÓ{Aµ
õìÇŽëh¶ÌÙj'½xÊì¼xvžgçÙyñì<;Ïγóì¼xvžgçÙyñìz—ÿƒ¢ŠûÔ¿ÊIEND®B`‚urwid-1.3.1/docs/manual/bright_combinations.py.xdotool0000664000175000017500000000005212615524560022505 0ustar  ianian00000000000000windowsize --usehints $RXVTWINDOWID 70 26
urwid-1.3.1/docs/manual/mainloop.rst0000664000175000017500000001164612615524560017003 0ustar  ianian00000000000000.. _main-loop:

*************
  Main Loop
*************

.. currentmodule:: urwid

The :class:`MainLoop` class ties together a :ref:`display
module `, a set of widgets and an :ref:`event loop
`. It handles passing input from the display module to the
widgets, rendering the widgets and passing the rendered canvas to the display
module to be drawn.

You may filter the user's input before it is passed to the widgets with your
own code by using :meth:`MainLoop.input_filter`, or have
special code to handle input not handled by the widgets by using
:func:`MainLoop.unhandled_input`.

You may set alarms to create timed events using
:meth:`MainLoop.set_alarm_at` or
:meth:`MainLoop.set_alarm_in`. These methods automatically add
a call to :func:`MainLoop.draw_screen` after calling your
callback. :meth:`MainLoop.remove_alarm` may be used to remove
alarms.

When the main loop is running, any code that raises an
:exc:`ExitMainLoop` exception will cause the loop to
exit cleanly. If any other exception reaches the main loop code, it will shut
down the screen to avoid leaving the terminal in an unusual state then re-raise
the exception for normal handling.

Using :class:`MainLoop` is highly recommended, but if it does
not fit the needs of your application you may choose to use your own code
instead. There are no dependencies on :class:`MainLoop` in
other parts of Urwid.

Widgets Displayed
=================

The topmost widget displayed by :class:`MainLoop` must be
passed as the first parameter to the constructor. If you want to change the
topmost widget while running, you can assign a new widget to the
:class:`MainLoop` object's
:attr:`MainLoop.widget` attribute. This is useful for
applications that have a number of different modes or views.

The displayed widgets will be handling user input, so it is better to extend
the widgets that are displayed with your application-specific input handling so
that the application's behaviour changes when the widgets change. If all your
custom input handling is done from :meth:`MainLoop.unhandled_input`,
it will be difficult to extend as your application gets more complicated.


.. _event-loops:

Event Loops
===========

Urwid's event loop classes handle waiting for things for the
:class:`MainLoop`. The different event loops allow you to
integrate with Twisted_, Glib_, Tornado_, Asyncio_ libraries,
or use a simple ``select``-based
loop. Event loop classes abstract the particulars of waiting for input and
calling functions as a result of timeouts.

You will typically only have a single event loop in your application, even if
you have more than one :class:`MainLoop` running.

You can add your own files to watch to your event loop, with the
:meth:`watch_file() ` method.
Using this interface gives you the special handling
of :exc:`ExitMainLoop` and other exceptions when using Glib_, Twisted_ or
Tornado_ callbacks.

.. _Twisted: http://twistedmatrix.com/trac/
.. _Glib: http://developer.gnome.org/glib/stable/
.. _Tornado: http://www.tornadoweb.org/
.. _Asyncio: https://docs.python.org/3/library/asyncio.html

``SelectEventLoop``
-------------------

This event loop is based on ``select.select()``. This is the default event loop
created if none is passed to :class:`~urwid.main_loop.MainLoop`.

::

    # same as urwid.MainLoop(widget, event_loop=urwid.SelectEventLoop())
    loop = urwid.MainLoop(widget)

.. seealso::

  :class:`SelectEventLoop reference `

``TwistedEventLoop``
--------------------

This event loop uses Twisted's reactor. It has been set up to emulate
:class:`SelectEventLoop`'s behaviour and will start the
reactor and stop it on an error. This is not the standard way of using
Twisted's reactor, so you may need to modify this behaviour for your
application.

::

    loop = urwid.MainLoop(widget, event_loop=urwid.TwistedEventLoop())

.. seealso::

  :class:`TwistedEventLoop reference `

``GLibEventLoop``
-----------------

This event loop uses GLib's event loop. This is useful if you are building an
application that depends on DBus events, but don't want to base your
application on Twisted.

::

    loop = urwid.MainLoop(widget, event_loop=urwid.GLibEventLoop())

.. seealso::

  :class:`GLibEventLoop reference `

``TornadoEventLoop``
--------------------

This event loop integrates with Tornado.

::

    from tornado.ioloop import IOLoop
    evl = urwid.TornadoEventLoop(IOLoop())
    loop = urwid.MainLoop(widget, event_loop=evl)

.. seealso::

  :class:`TornadoEventLoop reference `

``AsyncioEventLoop``
--------------------

This event loop integrates with the asyncio module in Python 3.4,
the asyncio package available for Python 3.3 or the trollius
package available for Python 2.

::

    import asyncio
    evl = urwid.AsyncioEventLoop(loop=asyncio.get_event_loop())
    loop = urwid.MainLoop(widget, event_loop=evl)

.. seealso::

  :class:`AsyncioEventLoop reference `

urwid-1.3.1/docs/manual/wanat.py0000664000175000017500000000115712615524560016113 0ustar  ianian00000000000000import urwid

class Pudding(urwid.Widget):
    _sizing = frozenset(['flow'])

    def rows(self, size, focus=False):
        return 1

    def render(self, size, focus=False):
        (maxcol,) = size
        num_pudding = maxcol / len("Pudding")
        return urwid.TextCanvas(["Pudding" * num_pudding], maxcol=maxcol)


class BoxPudding(urwid.Widget):
    _sizing = frozenset(['box'])

    def render(self, size, focus=False):
        (maxcol, maxrow) = size
        num_pudding = maxcol / len("Pudding")
        return urwid.TextCanvas(["Pudding" * num_pudding] * maxrow,
                                maxcol=maxcol)
urwid-1.3.1/docs/manual/safe_combinations.py.xdotool0000664000175000017500000000005212615524560022144 0ustar  ianian00000000000000windowsize --usehints $RXVTWINDOWID 70 27
urwid-1.3.1/docs/manual/encodings.rst0000664000175000017500000000632612615524560017135 0ustar  ianian00000000000000.. vim: set fileencoding=utf-8:

.. _text-encodings:

***********************
  Encodings Supported
***********************

.. currentmodule:: urwid

Urwid has a single global setting for text encoding that is set on start-up
based on the configured locale. You may change that setting with the
:meth:`set_encoding` method. eg.

::

    urwid.set_encoding("UTF-8")

There are two distinct modes of handling encodings with Urwid: Unicode or
Pass-through. The mode corresponds to using Unicode strings or normal strings
in your widgets.

::

    txt_a = urwid.Text(u"El Niño")
    txt_b = urwid.Text("El Niño")

``txt_a`` will be automatically encoded when it is displayed (Unicode mode).

``txt_b`` is **assumed** to be in the encoding the user is expecting and passed
through as-is (Pass-through mode). If the encodings are different then the
user will see "mojibake" (garbage) on their screen.

The only time it makes sense to use pass-through mode is if you're handling an
encoding that does not round-trip to Unicode properly, or if you're absolutely
sure you know what you're doing.

Unicode Support
===============

Urwid has a basic understanding of character widths so that the text layout
code can properly wrap and display most text. There is currently no support for
right-to-left text.

You should be able to use any valid Unicode characters that are present in the
global encoding setting in your widgets, with the addition of some common DEC
graphic characters:

::

    \u00A3 (£), \u00B0 (°), \u00B1 (±), \u00B7 (·), \u03C0 (π),
    \u2260 (≠), \u2264 (≤), \u2265 (≥), \u23ba (⎺), \u23bb (⎻),
    \u23bc (⎼), \u23bd (⎽), \u2500 (─), \u2502 (│), \u250c (┌),
    \u2510 (â”), \u2514 (â””), \u2518 (┘), \u251c (├), \u2524 (┤),
    \u252c (┬), \u2534 (┴), \u253c (┼), \u2592 (▒), \u25c6 (◆)

If you use these characters with a non-UTF-8 encoding they will be sent using
the alternate character set sequences supported by some terminals.

Pass-through Support
====================

Supported encodings for pass-through mode:

* UTF-8 (narrow and wide characters)
* ISO-8859-*
* EUC-JP (JISX 0208 only)
* EUC-KR
* EUC-CN (aka CN-GB)
* EUC-TW (CNS 11643 plain 1 only)
* GB2312
* GBK
* BIG5
* UHC

In pass-through mode Urwid must still calculate character widths. For UTF-8
mode the widths are specified in the Unicode standard. For ISO-8859-* all
bytes are assumed to be 1 column wide characters. For the remaining supported
encodings any byte with the high-bit set is considered to be half of a 2-column
wide character.

The additional plains in EUC are not currently supported.

Future Work
===========

Text encoding should be a per-screen (display module) setting, not a global
setting. It should be possible to simultaneously support different encodings on
different screens with Urwid. Making this work involves possibly changing the
function signature of many widget methods, because encoding needs to be
specified along with size and focus.

Device-specific encodings should also be possible for Unicode mode. The LCD
display module in development drives a device with a non-standard mapping of
Unicode code points to 8-bit values, but it should still be possible to use a
Unicode text to display the characters it supports.
urwid-1.3.1/docs/manual/widgets.rst0000664000175000017500000007334412615524560016636 0ustar  ianian00000000000000***********
  Widgets
***********

.. currentmodule:: urwid

.. _widget-layout:

Widget Layout
=============

Urwid uses widgets to divide up the available screen space. This makes it easy
to create a fluid interface that moves and changes with the user's terminal and
font size.

.. image:: images/widget_layout.png

The result of rendering a widget is a canvas suitable for displaying on the
screen. When we render the topmost widget:

1. The topmost widget *(a)* is rendered the full size of the screen
2. *(a)* renders *(b)* any size up to the full size of the screen
3. *(b)* renders *(c)*, *(d)* and *(e)* dividing its available screen columns
   between them
4. *(e)* renders *(f)* and *(g)* dividing its available screen rows between
   them
5. *(e)* combines the canvases from *(f)* and *(g)* and returns them
6. *(b)* combines the canvases from *(c)*, *(d)* and *(e)* and returns them
7. *(a)* possibly modifies the canvas from *(b)* and returns it

Widgets *(a)*, *(b)* and *(e)* are called container widgets because they
contain other widgets. Container widgets choose the size and position of their
contained widgets.

Container widgets must also keep track of which one of their contained widgets
is in focus. The focus is used when handling keyboard input. If in the above
example *(b)* 's focus widget is *(e)* and *(e)* 's focus widget is
*(f)* then keyboard input will be handled this way:

1. The keypress is passed to the topmost widget *(a)*
2. *(a)* passes the keypress to *(b)*
3. *(b)* passes the keypress to *(e)*, its focus widget
4. *(e)* passes the keypress to *(f)*, its focus widget
5. *(f)* either handles the keypress or returns it
6. *(e)* has an opportunity to handle the keypress if it was returned from
   *(f)*
7. *(b)* has an opportunity to handle the keypress if it was returned from
   *(e)*
8. *(a)* has an opportunity to handle the keypress if it was returned from
   *(b)*

Box, Flow and Fixed Widgets
===========================

The size of a widget is measured in screen columns and rows. Widgets that are
given an exact number of screen columns and rows are called box widgets. The
topmost widget is always a box widget.

Much of the information displayed in a console user interface is text and the
best way to display text is to have it flow from one screen row to the next.
Widgets like this that require a variable number of screen rows are called flow
widgets. Flow widgets are given a number of screen columns and can calculate
how many screen rows they need.

Occasionally it is also useful to have a widget that knows how many screen
columns and rows it requires, regardless of the space available. This is
called a fixed widget.

.. list-table:: How a Widget's Size is Determined
   :widths: 20 40 40
   :header-rows: 1

   * - sizing mode
     - width
     - height
   * - ``'box'``
     - container decides
     - container decides
   * - ``'flow'``
     - container decides
     - widget's :meth:`rows() ` method
   * - ``'fixed'``
     - widget's :meth:`pack() ` method
     - widget's :meth:`pack() ` method

It is an Urwid convention to use the variables :attr:`maxcol` and
:attr:`maxrow` to store a widget's size. Box widgets require both of ``(maxcol,
maxrow)`` to be specified.

Flow widgets expect a single-element tuple ``(maxcol,)`` instead because they
calculate their :attr:`maxrow` based on the :attr:`maxcol` value.

Fixed widgets expect the value ``()`` to be passed in to functions that take
a size because they know their :attr:`maxcol` and :attr:`maxrow` values.

.. _basic-grafic-widgets:

Included Widgets
================

:ref:`Widget class reference `

.. image:: images/urwid_widgets_1.png

Basic and graphic widgets are the content with which users interact. They may
also be used as part of custom widgets you create.

.. image:: images/urwid_widgets_2.png

.. _decoration-widgets:

Decoration Widgets
==================

Decoration widgets alter the appearance or position of a single other widget.
The widget they wrap is available as the
:attr:`original_widget ` property.
If you might be using more than one decoration widget you may use the
:attr:`base_widget ` property to access the
"most" original_widget.
:attr:`Widget.base_widget` points to ``self`` on all non-decoration widgets, so
it is safe to use in any situation.

.. _container-widgets:

Container Widgets
=================

Container widgets divide their available space between their child widgets.
This is how widget layouts are defined. When handling selectable widgets
container widgets also keep track of which of their child widgets is in focus.
Container widgets may be nested, so the actual widget in focus may be many
levels below the topmost widget.

Urwid's container widgets have a common API you can use, regardless of the
container type.  Backwards compatibility is still maintained for the old
container-specific ways of accessing and modifying contents, but this API
is now the preferred way of modifying and traversing containers.

::

  container.focus

is a read-only property that returns the widget in focus for this container.
Empty containers and non-container widgets (that inherit from Widget)
return ``None``.

::

  container.focus_position

is a read/write property that provides access to the position of the
container's widget in focus.  This will often be a integer value but may be
any object.
:class:`Columns`, :class:`Pile`, :class:`GridFlow`, :class:`Overlay` and
:class:`ListBox` with a :class:`SimpleListWalker` or :class:`SimpleFocusListWalker`
as its body use integer positions.  :class:`Frame` uses ``'body'``, ``'header'``
and ``'footer'``;  :class:`ListBox` with a custom list walker will use the
positions the list walker returns.

Reading this value on an empty container or on any non-container widgets
(that inherit from Widget) raises an IndexError.  Writing to this property with
an invalid position will also raise an IndexError.  Writing a new value
automatically marks this widget to be redrawn and will be reflected in
``container.focus``.

::

  container.contents

is a read-only property (read/write in some cases) that provides access to a
mapping- or list-like object that contains the child widgets and the options
used for displaying those widgets in this container.  The mapping- or list-like
object always allows reading from positions with the usual ``__getitem__()``
method and may support assignment and deletion with ``__setitem__()`` and
``__delitem__()`` methods.  The values are ``(child widget, option)`` tuples.
When this object or its contents are modified the widget is automatically
flagged to be redrawn.

:class:`Columns`, :class:`Pile` and :class:`GridFlow` allow assigning an
iterable to ``container.contents`` to overwrite the values in
with the ones provided.

:class:`Columns`, :class:`Pile`, :class:`GridFlow`, :class:`Overlay` and
:class:`Frame` support ``container.contents`` item assignment and deletion.

::

  container.options(...)

is a method that returns options objects for use in items added to
``container.contents``.  The arguments are specific to the container type,
and generally match the ``__init__()`` arguments for the container.
The objects returned are currently tuples of strings and integers or ``None``
for containers without child widget options.  This method exists to allow
future versions of Urwid to add new options to existing containers.  Code
that expects the option tuples to remain the same size will fail when new
options are added, so defensive programming with options tuples is strongly
encouraged.

::

  container.__getitem__(x)
  # a.k.a.
  container[x]

is a short-cut method behaving identically to:
``container.contents[x][0].base_widget``.
Which means roughly "give me the child widget at position *x* and skip all
the decoration widgets wrapping it".  Decoration widgets include
:class:`Padding`, :class:`Filler`, :class:`AttrMap` etc.

::

  container.get_focus_path()

is a method that returns the focus position for this container *and* all child
containers along the path defined by their focus settings.  This list of
positions is the closest thing we have to the singular widget-in-focus in
other UI frameworks, because the ultimate widget in focus in Urwid depends
on the focus setting of all its parent container widgets.

::

  container.set_focus_path(p)

is a method that assigns to the focus_position property of each container
along the path given by the list of positions *p*.  It may be used to restore
focus to a widget as returned by a previous call to ``container.get_focus_path()``.

::

  container.get_focus_widgets()

is a method that returns the ``.focus`` values starting from this container
and proceeding along each child widget until reaching a leaf
(non-container) widget.

Note that the list does not contain the topmost container widget
(i.e, on which this method is called), but does include the
lowest leaf widget.

::

  container.__iter__()
  # typically
  for x in container: ...

  container.__reversed__()
  # a.k.a
  reversed(container)

are methods that allow iteration over the *positions* of this container.
Normally the order of the positions generated by __reversed__() will be the
opposite of __iter__().  The exception is the case of :class:`ListBox` with
certain custom list walkers, and the reason goes back to the original way list
walker interface was defined.  Note that a custom list walker might also generate
an unbounded number of positions, so care should be used with this interface and
:class:`ListBox`.


Pile Widgets
------------

:class:`Pile` widgets are used to combine multiple widgets by
stacking them vertically. A Pile can manage selectable widgets by keeping track
of which widget is in focus and it can handle moving the focus between widgets
when the user presses the *UP* and *DOWN* keys. A Pile will also work well when
used within a :class:`ListBox`.

A Pile is selectable only if its focus widget is selectable. If you create a
Pile containing one Text widget and one Edit widget the Pile will choose the
Edit widget as its default focus widget.


Columns Widgets
---------------

:class:`Columns` widgets may be used to arrange either flow
widgets or box widgets horizontally into columns. Columns widgets will manage
selectable widgets by keeping track of which column is in focus and it can
handle moving the focus between columns when the user presses the *LEFT* and
*RIGHT* keys. Columns widgets also work well when used within a
:class:`ListBox`.

Columns widgets are selectable only if the column in focus is selectable. If a
focus column is not specified the first selectable widget will be chosen as the
focus column.


GridFlow Widgets
----------------

The :class:`GridFlow` widget is a flow widget designed for use
with :class:`Button`, :class:`CheckBox` and
:class:`RadioButton` widgets. It renders all the widgets it
contains the same width and it arranges them from left to right and top to
bottom.

The GridFlow widget uses Pile, Columns, Padding and Divider widgets to build a
display widget that will handle the keyboard input and rendering. When the
GridFlow widget is resized it regenerates the display widget to accommodate the
new space.


Overlay Widgets
---------------

The :class:`Overlay` widget is a box widget that contains two
other box widgets. The bottom widget is rendered the full size of the Overlay
widget and the top widget is placed on top, obscuring an area of the bottom
widget. This widget can be used to create effects such as overlapping "windows"
or pop-up menus.

The Overlay widget always treats the top widget as the one in focus. All
keyboard input will be passed to the top widget.

If you want to use a flow widget for the top widget, first wrap the flow
widget with a :class:`Filler` widget.


.. _listbox-contents:

ListBox Contents
================

:class:`ListBox` is a box widget that contains flow widgets.
Its contents are displayed stacked vertically, and the
:class:`ListBox` allows the user to scroll through its content.
One of the flow widgets displayed in the :class:`ListBox` is its
focus widget.

ListBox Focus and Scrolling
---------------------------

The :class:`ListBox` is a box widget that contains flow widgets.
Its contents are displayed stacked vertically, and the
:class:`ListBox` allows the user to scroll through its content.
One of the flow widgets displayed in the :class:`ListBox` is the
focus widget. The :class:`ListBox` passes key presses to the
focus widget to allow the user to interact with it. If the focus widget does
not handle a keypress then the :class:`ListBox` may handle the
keypress by scrolling and/or selecting another widget to become the focus
widget.

The :class:`ListBox` tries to do the most sensible thing when
scrolling and changing focus. When the widgets displayed are all
:class:`Text` widgets or other unselectable widgets then the
:class:`ListBox` will behave like a web browser does when the
user presses *UP*, *DOWN*, *PAGE UP* and *PAGE DOWN*: new text is immediately
scrolled in from the top or bottom. The :class:`ListBox` chooses
one of the visible widgets as its focus widget when scrolling. When scrolling
up the :class:`ListBox` chooses the topmost widget as the focus,
and when scrolling down the :class:`ListBox` chooses the
bottommost widget as the focus.

The :class:`ListBox` remembers the location of the widget in
focus as either an "offset" or an "inset". An offset is the number of rows
between the top of the :class:`ListBox` and the beginning of the
focus widget. An offset of zero corresponds to a widget with its top aligned
with the top of the :class:`ListBox`. An inset is the fraction
of rows of the focus widget that are "above" the top of the
:class:`ListBox` and not visible. The
:class:`ListBox` uses this method of remembering the focus
widget location so that when the :class:`ListBox` is resized the
text displayed will stay roughly aligned with the top of the
:class:`ListBox`.

When there are selectable widgets in the :class:`ListBox` the
focus will move between the selectable widgets, skipping the unselectable
widgets. The :class:`ListBox` will try to scroll all the rows of
a selectable widget into view so that the user can see the new focus widget in
its entirety. This behavior can be used to bring more than a single widget into
view by using composite widgets to combine a selectable widget with other
widgets that should be displayed at the same time.


Dynamic ListBox with ListWalker
-------------------------------

While the :class:`ListBox` stores the location of its focus
widget, it does not directly store the actual focus widget or other contents of
the :class:`ListBox`. The storage of a
:class:`ListBox`'s content is delegated to a "List Walker"
object. If a list of widgets is passed to the :class:`ListBox`
constructor then it creates a :class:`SimpleListWalker` object
to manage the list.

When the :class:`ListBox` is `rendering a canvas`_ or `handling
input`_ it will:

.. _rendering a canvas: :meth:`ListBox.render`
.. _handling input: :meth:`ListBox.keypress`

1. Call the :meth:`get_focus` method of its list walker object. This method
   will return the focus widget and a position object.
2. Optionally call the :meth:`get_prev` method of its List Walker object one or
   more times, initially passing the focus position and then passing the new
   position returned on each successive call. This method will return the
   widget and position object "above" the position passed.
3. Optionally call the :meth:`get_next` method of its List Walker object one or
   more times, similarly, to collect widgets and position objects "below" the
   focus position.
4. Optionally call the :meth:`set_focus` method passing one of the position
   objects returned in the previous steps.

This is the only way the :class:`ListBox` accesses its contents,
and it will not store copies of any of the widgets or position objects beyond
the current rendering or input handling operation.

The :class:`SimpleListWalker` stores a list of widgets, and uses
integer indexes into this list as its position objects. It stores the focus
position as an integer, so if you insert a widget into the list above the focus
position then you need to remember to increment the focus position in the
:class:`SimpleListWalker` object or the contents of the
:class:`ListBox` will shift.

A custom List Walker object may be passed to the
:class:`ListBox` constructor instead of a plain list of widgets.
List Walker objects must implement the :ref:`list-walker-interface`.

The fib.py_ example program demonstrates a custom list walker that doesn't
store any widgets. It uses a tuple of two successive Fibonacci numbers as its
position objects and it generates Text widgets to display the numbers on the
fly. The result is a :class:`ListBox` that can scroll through an
unending list of widgets.

The edit.py_ example program demonstrates a custom list walker that loads lines
from a text file only as the user scrolls them into view. This allows even
huge files to be opened almost instantly.

The browse.py_ example program demonstrates a custom list walker that uses a
tuple of strings as position objects, one for the parent directory and one for
the file selected. The widgets are cached in a separate class that is accessed
using a dictionary indexed by parent directory names. This allows the
directories to be read only as required. The custom list walker also allows
directories to be hidden from view when they are "collapsed".

.. _fib.py: http://excess.org/urwid/browser/examples/fib.py
.. _edit.py: http://excess.org/urwid/browser/examples/edit.py
.. _browse.py: http://excess.org/urwid/browser/examples/browse.py


Setting the Focus
-----------------

The easiest way to change the current :class:`ListBox` focus is
to call the :meth:`ListBox.set_focus` method. This method doesn't
require that you know the :class:`ListBox`'s current dimensions
``(maxcol, maxrow)``. It will wait until the next call to either keypress or
render to complete setting the offset and inset values using the dimensions
passed to that method.

The position object passed to :meth:`set_focus` must be compatible with the
List Walker object that the :class:`ListBox` is using. For
:class:`SimpleListWalker` the position is the integer index of
the widget within the list.

The *coming_from* parameter should be set if you know that the old position is
"above" or "below" the previous position. When the
:class:`ListBox` completes setting the offset and inset values
it tries to find the old widget among the visible widgets. If the old widget is
still visible, if will try to avoid causing the :class:`ListBox`
contents to scroll up or down from its previous position. If the widget is not
visible, then the :class:`ListBox` will:

* Display the new focus at the bottom of the :class:`ListBox` if
  *coming_from* is "above".
* Display the new focus at the top of the :class:`ListBox` if
  *coming_from* is "below".
* Display the new focus in the middle of the :class:`ListBox` if
  coming_from is ``None``.

If you know exactly where you want to display the new focus widget within the
:class:`ListBox` you may call
:meth:`ListBox.set_focus_valign`.  This method lets you specify
the *top*, *bottom*, *middle*, a relative position or the exact number of rows
from the top or bottom of the :class:`ListBox`.

List Walkers
------------

:class:`ListBox` does not manage the widgets it displays
directly, instead it passes that task to a class called a "list walker". List
walkers keep track of the widget in focus and provide an opaque position object
that the :class:`ListBox` may use to iterate through widgets
above and below the focus widget.

A :class:`SimpleFocusListWalker`
is a list walker that behaves like a normal Python list. It may be used any
time you will be displaying a moderate number of widgets.

If you need to display a large number of widgets you should implement your own
list walker that manages creating widgets as they are requested and destroying
them later to avoid excessive memory use.

List walkers may also be used to display tree or other structures within a
:class:`ListBox`. A number of the `example programs
`_ demonstrate the use of custom list
walker classes.

.. seealso:: :class:`ListWalker base class reference `


.. _list-walker-interface:

List Walker Interface
---------------------

List Walker API Version 1
~~~~~~~~~~~~~~~~~~~~~~~~~

This API will remain available and is still the least restrictive option for
the programmer.  Your class should subclass :class:`ListWalker`.
Whenever the focus or content changes you are responsible for
calling :meth:`ListWalker._modified`.

.. currentmodule:: MyV1ListWalker

.. method:: get_focus()

   return a ``(widget, position)`` tuple or ``(None, None)`` if empty

.. method:: set_focus(position)

   set the focus and call ``self._modified()`` or raise an :exc:`IndexError`.

.. method:: get_next(position)

   return the ``(widget, position)`` tuple below *position* passed
   or ``(None, None)`` if there is none.

.. method:: get_prev(position)

   return the ``(widget, position)`` tuple above *position* passed
   or ``(None, None)`` if there is none.

List Walker API Version 2
~~~~~~~~~~~~~~~~~~~~~~~~~

This API is an attempt to remove some of the duplicate code that V1 requires for
many users.  List walker API V1 will be implemented automatically by
subclassing :class:`ListWalker` and implementing the V2 methods.
Whenever the focus or content changes you are responsible for
calling :meth:`ListWalker._modified`.

.. currentmodule:: MyV2ListWalker

.. method:: __getitem__(position)

   return widget at *position* or raise an :exc:`IndexError` or :exc:`KeyError`

.. method:: next_position(position)

   return the position below passed *position* or raise an :exc:`IndexError` or :exc:`KeyError`

.. method:: prev_position(position)

   return the position above passed *position* or raise an :exc:`IndexError` or :exc:`KeyError`

.. method:: set_focus(position)

   set the focus and call ``self._modified()`` or raise an :exc:`IndexError`.

.. attribute:: focus

   attribute or property containing the focus position, or define
   :meth:`MyV1ListWalker.get_focus` as above

List Walker Iteration
~~~~~~~~~~~~~~~~~~~~~

There is an optional iteration helper method that may be defined in any list walker.
When this is defined it will be used by :meth:`ListBox.__iter__` and
:meth:`ListBox.__reversed__`:

.. method:: positions(reverse=False)

   return a forward or reverse iterable of positions

.. currentmodule:: urwid



Custom Widgets
==============

Widgets in Urwid are easiest to create by extending other widgets. If you are
making a new type of widget that can use other widgets to display its content,
like a new type of button or control, then you should start by extending
:class:`WidgetWrap` and passing the display widget to its constructor.

The :class:`Widget` interface is described in detail in the
:class:`Widget base class reference ` and is useful if you're looking to modify
the behavior of an existing widget,
build a new widget class from scratch or just want a better understanding of
the library.

One Urwid design choice that stands out is that widgets typically have no
size. Widgets don't store their size on screen, and instead are
passed that information when they need it.

This choice has some advantages:

* widgets may be reused in different locations
* reused widgets only need to be rendered once per size displayed
* widgets don't need to know their parents
* less data to store and update
* no worrying about widgets that haven't received their size yet
* same widgets could be displayed at different sizes to different users
  simultaneously

It also has disadvantages:

* difficult to determine a widget's size on screen
* more parameters to parse
* duplicated size calculations across methods

For determining a widget's size on screen it is possible to look up the size(s)
it was rendered at in the :class:`CanvasCache`. There are plans
to address some of the duplicated size handling code in the container widgets
in a future Urwid release.

The same holds true for a widget's focus state, so that too is passed in to
functions that need it.


Modifying Existing Widgets
--------------------------

The easiest way to create a custom widget is to modify an existing widget.
This can be done by either subclassing the original widget or by wrapping it.
Subclassing is appropriate when you need to interact at a very low level with
the original widget, such as if you are creating a custom edit widget with
different behavior than the usual Edit widgets. If you are creating a custom
widget that doesn't need tight coupling with the original widget then
wrapping is more appropriate.

The :class:`WidgetWrap` class simplifies wrapping existing
widgets. You can create a custom widget simply by creating a subclass of
WidgetWrap and passing a widget into WidgetWrap's constructor.


This is an example of a custom widget that uses WidgetWrap:

.. literalinclude:: wmod.py
   :linenos:

The above code creates a group of RadioButtons and provides a method to
query the state of the buttons.


Widgets from Scratch
--------------------

Widgets must inherit from :class:`Widget`.
Box widgets must implement :meth:`Widget.selectable` and :meth:`Widget.render`
methods, and flow widgets must implement :meth:`Widget.selectable`,
:meth:`Widget.render` and :meth:`Widget.rows` methods.

The default :meth:`Widget.sizing` method returns a set of sizing modes supported
from ``self._sizing``, so we define ``_sizing`` attributes for our flow and
box widgets below.

.. literalinclude:: wanat.py
   :linenos:

The above code implements two widget classes. Pudding is a flow widget and
BoxPudding is a box widget. Pudding will render as much "Pudding" as will fit
in a single row, and BoxPudding will render as much "Pudding" as will fit into
the entire area given.

Note that the rows and render methods' focus parameter must have a default
value of False. Also note that for flow widgets the number of rows returned by
the rows method must match the number of rows rendered by the render method.

To improve the efficiency of your Urwid application you should be careful of
how long your ``rows()`` methods take to execute. The ``rows()`` methods may be called many
times as part of input handling and rendering operations. If you are using a
display widget that is time consuming to create you should consider caching it
to reduce its impact on performance.

It is possible to create a widget that will behave as either a flow widget or
box widget depending on what is required:

.. literalinclude:: wanat_multi.py
   :linenos:

MultiPudding will work in place of either Pudding or BoxPudding above. The
number of elements in the size tuple determines whether the containing widget
is expecting a flow widget or a box widget.


Selectable Widgets
------------------

Selectable widgets such as Edit and Button widgets allow the user to interact
with the application. A widget is selectable if its selectable method returns
True. Selectable widgets must implement the :meth:`Widget.keypress` method to
handle keyboard input.

.. literalinclude:: wsel.py

The SelectablePudding widget will display its contents in uppercase when it is
in focus, and it allows the user to "eat" the pudding by pressing each of the
letters *P*, *U*, *D*, *D*, *I*, *N* and *G* on the keyboard. When the user has
"eaten" all the pudding the widget will reset to its initial state.

Note that keys that are unhandled in the keypress method are returned so that
another widget may be able to handle them. This is a good convention to follow
unless you have a very good reason not to. In this case the *UP* and *DOWN*
keys are returned so that if this widget is in a
:class:`ListBox` the :class:`ListBox` will behave
as the user expects and change the focus or scroll the
:class:`ListBox`.


Widget Displaying the Cursor
----------------------------

Widgets that display the cursor must implement the
:meth:`Widget.get_cursor_coords` method.
Similar to the rows method for flow widgets, this method lets other widgets
make layout decisions without rendering the entire widget. The
:class:`ListBox` widget in particular uses get_cursor_coords to
make sure that the cursor is visible within its focus widget.

.. literalinclude:: wcur1.py
   :linenos:

CursorPudding will let the user move the cursor through the widget by pressing
*LEFT* and *RIGHT*. The cursor must only be added to the canvas when the widget
is in focus. The get_cursor_coords method must always return the same cursor
coordinates that render does.

A widget displaying a cursor may choose to implement :meth:`Widget.get_pref_col`.
This method
returns the preferred column for the cursor, and is called when the focus is
moving up or down off this widget.

Another optional method is :meth:`Widget.move_cursor_to_coords`. This method allows other
widgets to try to position the cursor within this widget. The
:class:`ListBox` widget uses :meth:`Widget.move_cursor_to_coords` when
changing focus and when the user pressed *PAGE UP* or *PAGE DOWN*. This method
must return ``True`` on success and ``False`` on failure. If the cursor may be
placed at any position within the row specified (not only at the exact column
specified) then this method must move the cursor to that position and return
``True``.

.. literalinclude:: wcur2.py
   :linenos:


Widget Metaclass
================

The :class:`Widget` base class has a metaclass defined that
creates a ``__super`` attribute for calling your superclass:
``self.__super`` is the same as the usual ``super(MyClassName, self)``.
This shortcut is of little use with Python 3's new ``super()`` syntax, but
will likely be retained for backwards compatibility in future versions.

This metaclass also uses :class:`MetaSignal`
to allow signals to be defined as a list of signal names
in a ``signals`` class attribute.  This is equivalent to calling
:func:`register_signal` with the class name and list of signals and all those
defined in superclasses after the class definition.

.. seealso::

    :class:`Widget metaclass WidgetMeta `


urwid-1.3.1/docs/manual/wanat_multi.py0000664000175000017500000000073612615524560017327 0ustar  ianian00000000000000import urwid

class MultiPudding(urwid.Widget):
    _sizing = frozenset(['flow', 'box'])

    def rows(self, size, focus=False):
        return 1

    def render(self, size, focus=False):
        if len(size) == 1:
            (maxcol,) = size
            maxrow = 1
        else:
            (maxcol, maxrow) = size
        num_pudding = maxcol / len("Pudding")
        return urwid.TextCanvas(["Pudding" * num_pudding] * maxrow,
                                maxcol=maxcol)
urwid-1.3.1/docs/manual/canvascache.rst0000664000175000017500000000543012615524560017416 0ustar  ianian00000000000000.. _canvas-cache:

****************
  Canvas Cache
****************

.. currentmodule:: urwid

In an Urwid application each time the screen is redrawn typically only part of
the screen actually needs to be updated. A canvas cache is used to store
visible, unchanged canvases so that not all of the visible widgets need to be
rendered for each update.

The :class:`Widget` base class uses some metaclass magic to
capture the canvas objects returned when :meth:`Widget.render` is called and return
them the next time :meth:`Widget.render` is called again with the same parameters. The
:meth:`Widget._invalidate` method is provided as a way to remove cached widgets so
that changes to the widget are visible the next time the screen is redrawn.

Similar metaclass magic is used for flow widgets' :meth:`Widget.rows` method. If a
canvas for that widget with the same parameters is cached then the rows of that
canvas are returned instead of calling the widget's actual :meth:`Widget.rows` method.

Composite Canvases
==================

When container and decoration widgets are rendered, they collect the canvases
returned by their children and arrange them into a composite canvas. Composite
canvases are nested to form a tree with the topmost widget's :meth:`Widget.render`
method returning the root of the tree. That canvas is sent to the display
module to be rendered on the screen.

Composite canvases reference the content and layout from their children,
reducing the number of copies required to build them. When a canvas is removed
from the cache by a call to :meth:`Widget._invalidate` all the direct parents of that
canvas are removed from the cache as well, forcing those widgets to be re-drawn
on the next screen update. This cascade-removal happens only once per update
(the canvas is then no longer in the cache) so batched changes to visible
widgets may be made efficiently. This is important when a user's input gets
ahead of the screen updating -- Urwid handles all the pending input first then
updates the screen with the final result, instead of falling further and
further behind.

Cache Lifetime
==============

The canvases "stored" in the canvas cache are actually weak references to the
canvases. The canvases must have a real reference somewhere for the cache to
function properly. Urwid's display modules store the currently displayed
topmost canvas for this reason. All canvases that are visible on the screen
will remain in the cache, and others will be garbage collected.

Future Work
===========

A updating method that invalidates regions of the display without redrawing
parent widgets would be more efficient for the common case of a single change
on the screen that does not affect the screen layout. Send an email to the
mailing list if you're interested in helping with this or other display
optimizations.
urwid-1.3.1/docs/manual/safe_combinations.py0000775000175000017500000000225112615524560020463 0ustar  ianian00000000000000#!/usr/bin/env python

import urwid

BLACK_FGS = ('light gray', 'dark cyan', 'dark red', 'dark green',
    'dark magenta', 'white', 'light blue', 'light cyan', 'light red',
    'light green', 'yellow', 'light magenta')
GRAY_FGS = ('black', 'dark blue', 'dark cyan', 'dark red', 'dark green',
    'dark magenta', 'white', 'light red', 'yellow',
    'light magenta')
BLUE_FGS = ('light gray', 'dark cyan', 'white',
    'light cyan', 'light red', 'light green', 'yellow',
    'light magenta')
CYAN_FGS = ('black', 'light gray', 'dark blue', 'white', 'light cyan',
    'light green', 'yellow')

BG_FGS =[
    ('black', BLACK_FGS),
    ('light gray', GRAY_FGS),
    ('dark blue', BLUE_FGS),
    ('dark cyan', CYAN_FGS),
    ]

body = urwid.SimpleFocusListWalker([])

for bg, fgs in BG_FGS:
    spec = urwid.AttrSpec(fgs[0], bg)
    def s(w):
        return urwid.AttrMap(w, spec)
    body.append(s(urwid.Divider()))
    body.append(s(
        urwid.GridFlow(
            [urwid.AttrMap(urwid.Text("'{0}' on '{1}'".format(fg, bg)),
                urwid.AttrSpec(fg, bg)) for fg in fgs],
            35, 0, 0, 'left')))
    body.append(s(urwid.Divider()))

urwid.MainLoop(urwid.ListBox(body)).run()
urwid-1.3.1/docs/manual/images/0000775000175000017500000000000012615524621015666 5ustar  ianian00000000000000urwid-1.3.1/docs/manual/images/display_modules.png0000664000175000017500000003746412615524560021611 0ustar  ianian00000000000000‰PNG


IHDRH"n–[sBIT|dˆtEXtSoftwarewww.inkscape.org›î< IDATxœíÝyxL×ãÇñ÷d‘ˆ 	b‰¥¨%%Öˆ’±V©½ª¨¢ø•ÒjµßZºXJ«ÕM«EK•¶øª-ö­jß÷5DD"d‘åþþÈ×ÔT,µÔr?¯çéódæž9÷Ì3÷~î9gÔÈcË0Ë픳³³»b†ãýn<0¹
ø|«B‹EçƒÇØížÔoïs.\|0­‘ûª]>×Û.k±³³üC.gçûØ"y:ÈŸ™•™yÛåu>x<ý“ó¨<®rêv """òP11‡ݹ¿æÌ™c3××®]»Ûš#”LJýOÓ§§Î™3çÚ§s·k×î–käñ¢óÀõý@#"æsûDä±§  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb
"""&¦  ""bb¦
ééÄFFZÇ9Cú•+wUgRìyR““ï¶iò1ƒs§Na‰çb¸|éÒ]Õy)1‘äøø{Ñ<ÓKMN&)öü]דráâbÈÌÈàüéÓw]gld$ééw]Ï£.áìYÒRSï[ýÿä;y1!áž|¶+SÈÇXßúøÝÆ8¹gÏ]Õùý A¬ùóÝ6M2W._¦Ÿo%kÈû¢W/¶,XpWuÎÿ|¿}/šgzüö+ßöï×õ¬š>©o¾	ÀùˆÖ¬~×u®[‡è°ãw]Ï£n\çNìY½*Çm).°lÊ仪ÿËWz³é÷ßoYnl§Ž¬Y¯ú¼rWû{ÐmÚÈáÍ›ïKÝ÷¥ÖGÄë?L£ø“O>èfÈ# ë¨Q¸yy=èfÈ}äîíÍ»ÿ÷ ›a
ÉññÌ|ÿ=š¼Üó¾îçì‰ìY½Ši§Ïàèät_÷u¿m
ÅÎÞž'ýýïyÝÿjXóóÏxù”àäž=Þ¼™n£ÇÃúÙ¿q>"÷"EhÞ§EË•`öØz¾3ž%Jsò$‹¿ý†¶ƒ“ßÓ‹3G²sù2Z¾zó»‚­‹²ñ¿ÿÅÝ»U…ØlÛ³z
&wÞ¼ÄFF²èë¯8{òyòç§n›vTkÒ„ðýû8ðÇ-ÿ$ëû…<ùÝh5pÞÞ×íëB\,+~øð½{qtr¢zÓ¦ÔmÛ€u¿Ì"¯»Õ7¶–_?û7r»æ¥F³fw{h:§ìgå?{ú4ž>>´8÷"E˜õá4ïÛ—ü=ع|9i)—¨Óú9bÂÃùsöoø5`õOÓñ©\™FÝ_"tâDŽnÛŠ½ƒ¥ýüh=p=Õ³lò÷Ù²…|4{¥EË•²O‹¿™ÈùÓäuw§~ÇNøݴ͇6mdÕ´qvÍK£îÝm¶Ù¼™2ÕªãæUˆK‰‰,üêKN8€SîÜTiLƒ.]Hˆ‰aɤo©ÝòYVL@³>}ð©Tùº}]¹|™S§plDz23©X·.!=^ÆÞÞžÝ+W’xîA;[Ëï]³šó§#	îÚõN?’‡Br\¿OøŒ?	À®+8ºm+ÿó.}²³ØÙQ½iS6ÏÿíKBÁb¡^ûTilSßæóÙ<>îÞExnàëäõð¸éþ333	ø5Ƕoç‰êÕùßÌi—.±cÉ*ÔàðæÍ¬šþ#Éññ¸ñ¦yŸ>+ÿ$›çÿN.çÜœ‹8ÅÁ
ð©\™gûÈñBsêÀ~ÖÍšÉÙ'ÉW° !/õ ŒŸ¿Œü Î/P¸tikùÙ! M[k?~Ôe\¹Âœqc9µ?ëÖ¥I¯ÞØÙÙ1ÿó	¤¥¤0uÈ[´}óMN:DÜ™3ärvfË‚¸{¡Ã;ï¾oK¿ÿ§Ü¹i3øM
•,y[ûŽ	ç—‘bgoÏOÇQÆÏÊõê³rÚÔhÖœ•?þ€wÙ²„¼ÔƒS§¶k†aP¹~}‚»uÇÎ.{À<õâEþûÉÇÄ„‡ãH~//,X¨Ù¢gŽe[èbÊ׮ͪéÓppp¤ýÛïp%5•ùŸOàrJ
Í_éC¹Zµ¬í:ºmëfÍ$9>ž*
ÒðÅ®X,⢢X1u
þ­[³ô»ïÈH¿B³Þ}(ãçljݻٷn-;;Ò†¤R¤Lšõ¾w#ÿêÔÀ®åËøºoÒÓÒîÚ
ç*š\ÎÎ4ëÓÿV­˜ýÑG¬ù9{ÚÀÉÅ…ÇYËf¤§óãÛo“ï'®GÑÑ­[Ö¤1^>>´8ˆ’•}IŽ`ÅÔ©¤$&]Svûÿø€„³Ñü>á3æ>M›Q®f-|>­‹ѨûK4îÙÓúåÝ®-a»vÒ´woJU©Ê°&­k@ÆthO.ggZ½6š-ž!åÂ…›¶ùøŽŒíÔßÀÔjÑ‚iÿùÍö-ræÈ~xû-N:Dó>}hÐ¥éWÒHŽcÁŸ3{ìžîЕ*1¬Iãç&S“/’œLp×®4|±+æÎåבP Ha¦yÓ¦Íúðìý¼<
°jú4ÎÍ>–¡“¾aþ„ψ‹Š`Ñį­Çó·1£™ÿùžnß‘€Öm˜ôÚ¶/Yb­ëÈ–-ì[»–Æ/¿Ìå‹—Þ¼YYY7Ýÿ÷ƒ²cÙ2šôê…½£#‹¿ùÚºíRRK¿ÿÈ,#ŸkEÙ5ynÐ<éïOJRvÚÿÇL|µ).дW/ŽlÙ̽r¾³=sô(%*V¦Õk)S­>׊³'Ndï#>žÐIßZ˞ܻ—ÅßLijD‰tLfsÆŽÅû(ÏwfÑįY3cåkׯÁÑ¿F!ø5
ÁÙ%'víâ§aC	ß·†/¾HØÎ]Œ}¾‹¾þІ]ºàèìÌÇ/<Ûûvus£\­Z889á×(ŸÊ•Iü€±±,øòæOÖ­)Z¶¶~–Ô‹)P¸0…J–¢PÉRø5
¡lõ÷ê#ÀÔÀS
òÜëoX7èÒ…¬¬,¢£q-àÆöÐPv¯\EàóÏãÄî•+iöJö­[KÓW^aïšÕuî̾µkiÒóæÃJ‹¾þŠNC‡S³E’Οç—?̱lÔñã<Û€õnàZ†aÐ狯ÈåìLÅ€ºìY³šMóæÙܱ”ôõ¥¤¯/H:žà®]Ù8o.
^xšÍ[0eðÞ¼™'ýýÙº7//›¤ø¸˜;þcZ¾Úß:ZóO†²2®\¡ß×É“??¡ßN¤DÅŠøáàèHåzÙk<nø“³'Â:ïwììì¨P—°;XóóÚ¾ù1'OP%8˜
unk¿K¾û–Æ=^&ðùì½£#û֮ɱlÔñãÔlñ•êÕ·	&WÛßó“O)X¬•ëÕçØöí¬øa*‡°)—ßÓ‹öCÞ&59™Ä˜ZôëÇŒáÃè<â=|*U¦D…Šlœ;—]ºpêÀ~¢Ž' M›Û>Ž+;;;*Õ«ÏÞ5k(Tª4'÷ì!ðùÎì]³†ºmÛrtÛVÿ4ƒ´”æ}:ž/wí¡`±btJÆ’IßXGМ]óðò'ã±³³ãIÿ:¼ZÅ—½kÖP588Ç}§\¸À꟦óÍþƒx-J¥§ëqdË–ËÆEGa±³Ã¯Qž%JP¾vm›í¥«V¥Íƒ(þdz•/KÜ™3x-jS.à¹6†AbL®Üð­ȶŋhÙMzödXÓ&tyÿr9;³â‡©u~\ÎÎwuŒ&õ:v¤A—.D;ʾuk	îÚ•'ýë`ïèˆ_ˆí(­—Ï@V–ÁGÚñéÓävuåÉ:tö*Hr|EØÎ×Õq))‰_èL\Ô
—*EfF&ñÑÑÖíMzö$tÒ·4èÒ…?ü@àó›ÄSAAì^µŠRUªPºjUüBBØ<>E½)V¾<®
¾W®ðA«–Ö×¥§¥‘Û5¯õqÑrå¬AÌÎÎŽ•*}üØ
ƒÀ¹S§pvuµ¹XûT®Ì郯+[²²/
º¼Èk5«ãS©µžiIó¾ýpÊ€b׬-Êëá»·7ÑaǯÛ/fÊ[ƒñðö&¯‡‘‡ãñ¿þQ¢b%Š?Yÿý/uZ·æÏÙ¿1fuÎáóQåýÄÖ¿óº{p1áæ¿šñò)yMywòzxÛÕ€\ÎÎ8çÉÃÅ„„Û
7’×£ MHŽ‹cÜI:žB%}ȸ’NÂÿ®A1'Ãñ,ácýܳߓí5¬@á"8æÊõ¿º³ëõºfT'Ÿ‡‡õ×B§bÆðaü2räÿ¶¤§¥q)1ñ¯¶]óÞòº»s1!áŽßëíú׃€Ý5Û™™™L8€Ö®£D…ŠŒéØž«wEžx‚Ü®®,›ü=¥«V%ŸGAÜ‹aåS)Z®Ü-;Cžüù¹”ø×A¼””tòõ;v¢n»öÛ¶E_ÅGÛóɆM¤üíçñþ[ ˜=vÕ›6³&Úõ³ã÷O?µnoÔý%^«QýöpdëVÞ˜>ã¦íTåuwçbbÎ×ÞÑôô¿~²™zñ¢Ív;{{›ÇÅŸ¬Àø›9{âæÎáƒg[òÕº»S¢R%F¯\ã~÷x™à®Ý8¼y¿OøŒÏºwçýÐ%9–…ì¾rñ_F€”›ô•Êõê3qßÂ÷ícå?0¼YS&Ëž¦º’šJfF†uÿRb.×›«–~ÿù<‚ØÙÛóɆM7@—“mûÎ¥„ÄõUyòç'íÒ¥ë>Ÿé1v/Œx=«Wñë¨Q$ÅÆÒ}ô˜÷’”D·×Õ1yðëôÿî{ëHÖÄÿëǵšôêÅÒï&‘•™‰Oe_ëú¨Ç…ÅîŸÍ>[,–›>¾ìlÏ1‹¿™ˆ—O	>\ºÈ^+ôQÇ@ö…ýBl¬Mù‹		(ü×MEŽm¼A»óº»3àû)9’FD`gwó÷k\»¨åz ?ÌÊÈ ãÊÒ/gÏ	F:Ⱦ5¶‰Ø7(ˆyãÇ[O¾AÌýøc|ƒ‚nYÕàF¬š>Ã00ƒUÓ§åXÎ0¢Ã°ÿߊÌFÝ_">꯻³³aaì[·Ⱦ«Ø½j%Ur¸ëHKIåÊåË@öb°åS¦Øl÷ðöæ© |üÂó<÷.ùòÝò=<Šj=Ó’%“¾µÎ‹e¤§[ÿöò)É¡
ÙókÉqql]´ð¦uE?Ža.]šýþ§y2¾AAäqs£@‘"¬ž>ß ¸)BéªU™÷éxëë232¬óëG¶n!|ß>Â÷íãÄžÝÖnN
/N‘2O°æçì~)1‘ÍóççXöB\,prq¡Ö3-ñ	!áš›-’x.{}ц¹sprq±%¸*-%…+©ÙŸóùˆˆëú»«ÖD‡`öØ14~LGs’}“–h3
ö \NI±^ƒ233Yòí_ë6JU©‚SVü0€{öX×­Ý	ÿV­™÷Ùxëy²×‘ÜŽÏS
nYÇwßel§Ž¨îG.ggªshãÆëÊeeeñ~Ë8çq%w^WΊ ûGY·—zê)–~÷3?xŸèãÇi?äW‚·ì?€ÑíÛ²wuö]ª_ãÆìZ¾Ü¦L“ž=ù°u+B^zù–íT=ójbÂÃù¿§|ñ*éC|t4¯ÿ8'ýýéøî»|Ú­+˧LÁ)•ž~ú¦uÍûìSö¬^…gñD;ÆÓmÛQÚÏ;;;Oÿ‰oôÇ)·X²“z¿¯'òDõ¼Œkwˆ;s†Þ>¿é~¿Ü“ƒ7òU|)P¨O5hxòßèO\Ôܼ
uìG¼G77⢣p-P€ÓòNà Ί ö³ÏRë™–×ÕÒãe†7k«~UpÊÿg[åXæ÷	ŸÑiè°ë¶=ê|ƒ‚عl¹õ{äÄ¢¯¾¤B:Ö2§üÀW}ûüï7îÄGEñL¿Wi5p êòÓ°wIIN&êØ1z~òéuCóײX,¼úí$ÆuîÄŠ©SÈÊ̤rýúÖõµÎž8ÉØN)X¬(WR/“‘‘ÎàŸþÁó
bL‡öX,ÎGœfÐ?Z‡‡¯Õiè0>éÒŸÊ•qpÌ…_Hc›í޹rѰKVNŸ†«Öÿì >Â\ eÿ¼QÇ;{{F¯Ìùßø74íÙ‹-š1 º޹œ¨Õ²%{ÖdŸÃüÓÏ|ó}ùuô(Êת«V8¹ä¹£}u:ŒÉo¢oåŠx/N|t4Oú×aÐ?Þòµõ:tdË‚ùô(]’JO×ãÍ÷î߯±ÆœoYð~JŽøåOîTBLù<WfÏžmû\»v9Ž=Ù;8¤ÏˆŽq¸—óÐéii$œ=‹[¡B6ûéiiÿ^+|[õ¤&'s!.Ž|äΛ÷ºíIçÏ‘•™E~//›Å{	11d¤¥áîí}Û+î“ãâÈåâb3'˜“‹		\JJ­P!kÙˆCÞ¬)?†GšœLVV–Íz‡¿ËÌÈ áìY
)‚ýߦD ûgFcÚ·cÒá#wµ> “G¦ýø#¹þºPe®íÚµ»î
h±XøùàïR““¹˜˜ˆ[¡B9^l“bÏã’7ßmÿN<++‹Ä˜Ü‹¹i¹ÌÌLÏžÅbgGÂ…­ßçÉo¼N77ž6œ„³gÉçé™ãçwÕ¥¤$2Ò¯X2ûw_õéM>OOº~8ê¶Ú§þÉùàaì÷SFzzvŸðö¾nð߽܀æ¯ô¡^‡Žw¼¿«çÆüžž7½žÜ9õƒ‡â÷Hw³ðãv(tãEb×Êïyó0ævÚéèä”ã]ÉÅ„ÖÌø‰…_E¯ñŸÝV{uŽNNxùøäøü톀Üyóæ®ºÑçv»Ÿûµn7Œº(€kë焯ºY{¯²wp°YXx•a,™ô-+ü‘Öƒ^l	Þ©[~þ7¸ÀÞˆÝ-C€½½ýMG€ÛêÇ7
ƒ°qÞ<¶…†òÙ–m·¬GîGÇ¿‹½žçÜ©p
.ÂÁ?ÿ$íR
þ­Ÿ»«ýÝèÜø <Aàn|ùJ/Žmß~ÝóÍz¿B³WúÜuýÅÊ—'¸k·»®'33ƒË—.Ñû³ÏË@èQð˨‘lüïÜ랯޴ÝFÝý?ý›ßÓÓúÝ
Ã0¸˜@ëAƒxº}‡»®ÏLÃàµÕrÜ6àû)èfÈC ]>×Ýù)ˆ•aº	rX,råÊuÛåÕO‹åA7ARš11S1ûÝyx(ˆ˜P»ví.?è6ˆÈÃAA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄDDDLLA@DDÄÄD®±}ûv¶oß@ll,sæÌ±nûõ×_IHHxPM“»ÎÒ¥Kïy½©©©„‡‡“–––ãö¬¬,"""8{öì=߷Ƚ   rß~ûY³fpâÄ	Þxã
ë¶þýûù š&wiëÖ­Œ3æ®ê:t(ãÇ ::šÖ­[S @‚ƒƒ)X° Mš4!,,ÌZþ‹/¾ÀÛÛ›š5kR»vmÜÜÜxõÕWIII`Ô¨Q<óÌ3wÕ&‘»¥  rµjÕâÔ©Sºòùý÷ßiÕª©©©4lØGGGΞ=KXX‰‰‰¼ðÂ=z€1cÆðÉ'Ÿ0þ|bbb8uêaaaØÛÛ“˜˜ø€ß‰È_ä®ÅÆÆâççÇÂ…yê©§(^¼¸Í—a|óÍ7øúúâåå…ŸŸ 003gÎXË~õÕWŒ7€;vкuk¾üòKÊ•+Ç Aƒ8sæÍ›7ÇËËoooÚ´ic}mXX­[·¦xñâøúúZïìæÏŸ¯¯/žžž”.]šÉ“'ßò}íß¿ŸfÍšÙ<·eËüýý)^¼8¯¿þ:W®\`ýúõtîÜ™qãÆQ¦L†
FXXMš4¡X±b/^œÞ½{séÒ%k]Õ«WgîܹԨQƒêÕ«3bľøâ›ý½öÚkLŸ>ý–m}ÔDEEÑ¢EŠ/N±bÅèÖ­›ÍÅ100Ÿþ™ÚµkS¼xqLzz:§OŸ¦yóæÖ×¾ôÒK\¸pÁúÚˆˆÚ·oOñâÅñööfРA6û5j%K–¤B…
„††ZŸOMMeÈ!”+WŽÒ¥K3hÐ ._¾lÝFVVO<ñ¿þú+‰‰‰L›6
777ìííâ	VºIDATéÚµ+Íš5#55•Q£F1~üxj×®m­ÃÃÃÏ?ÿooï{{@Eî‚‚€ÜµŒŒvïÞÍÒ¥KÙ²eË—/g̘18p€iÓ¦1nÜ8f̘Á¹sç˜;w®õä¹ÿ~›¹ÕèèhëðûÅ‹	

%66–;vðá‡2räHŠ-Jxx82È>‰7jÔˆgŸ}–S§N1oÞ<̶mÛÈÊÊ¢{÷îŒ7Ž˜˜vîÜI:unù¾RRR¬åªéÓ§³dÉöî݈
¬ÃÄ.\`Μ9†Áž={2dÎÎÎŒ=šS§N±oß>=z´µ®={ö0}út.\ÈÚµkiРŸ|ò	YYYœ?žÉ“'Ó¨Q£;ýhZ>œ“'Orøða,Æ
³nß·oßÿ=Ë–-cÏž=¬]»–	&+W.Þ{ï=Nž<É¡C‡HOOç½÷Þ²ûbóæÍñññáÈ‘#„‡‡Ó©S'k½›7o¦P¡B;vŒ#Fн{w233xýõ×	gûöí8p€ððp>øàëk¯Ž@vð«[·....9¾¿Ý»wséÒ¥Çò³“Ç‚€Ü3ï½÷¹sç¦B…
øûû³k×.&OžÌ»ï¾K•*U(]ºômß¹ºº2bÄòæÍ‹««+öööÖ€ƒƒƒõnkéÒ¥äÍ›—†
ƒƒÍ›7gþüùØÙÙ±}ûvΟ?››•*Uº£÷8dÈ
(@øÏþÃÌ™3­Û¼¼¼2d®®®¸ººR´hQŠ/΂øõ×_)T¨ëÖ­³©oذa)R„¼yó„«««õ.uÚ´i4nÜø±¼{ôòò¢lÙ²,^¼˜™3gR°`ÁëŽÍ;#››îîî¼óÎ;Öc]¨P!Ê”)âE‹˜5kžžž¬]»€M›6ËØ±cqqq!W®\6wä¥K—¦gÏž8::Ò©S'’““‰ŒŒ$##ƒ)S¦ðÚk¯OLL]ºtá÷ß·¾vþüù<ûì³$''ãááqÃ÷—œœŒ½½½5ð¼üòËT­Z•ªU«²bÅŠ»>†"÷Š‚€Ü3žžžÖ¿]\\¬Ãà‘‘‘øøøÜqvvuÓ‘#GR©R%Z·n··7}ô=|îÜ9úöíký/22’‚bggGhh([·n¥L™2Ô¬Y“7ÞQ{
.lý»D‰DEEY*TȦìÚµk©R¥
kÖ¬!99™¬¬,âããoX@Ÿ>}˜4ißÿ=½{÷¾£v>ì¶mÛFÅŠY¾|9IIIdeegSæFÇzóæÍTªT‰•+W’””„aÖãI±bŰ··Ïq¿^^^6sçÎMJJ
ÑÑѤ§§óÁXûÏäÉ“­166–°°0k¨ðòò²™ÒÊi?™™™ÄÄÄXŸ1b¿üò©©©Z# ‡ÝyüùøøFHHÈuÛrçÎm3o«««õ±Åb±)ïææÆçŸÎçŸÎúõëiÔ¨Ï<ó¥K—ÆÃÃ%K–äØ†Úµk³páB.]ºÄÈ‘#éÛ·/{öìùÇï%22’êÕ«pòäIŠ+vöNš4‰Áƒ[y0uêTëë^Ó­[7†ÊŒ3¸rå
Mš4ùÇm|L™2…W^y…÷߀9sæð믿ڔ‰ŒŒ´Ž"]{¬¿ÿþ{^}õU†
À¬Y³¬wî>>>DDDžžŽ££ãm·ÇÛÛggg&L˜À“O>yÝö…Ò¼ysk(mÖ¬íÛ·'**꺛ŒŒ|}})Z´(³fͲ®Q(Q¢NNN·Ý.‘ƒFä¾ëׯ£GfóæÍ¤¦¦²sçN"""ðóócÒ¤I$$$°|ùr,XpÓºæÌ™ÃñãÇÉÈÈÀÎÎ{{{rçÎMãÆÉÈÈ`ذa$&&’˜˜ÈŠ+صkÉÉÉ̘1ƒ„„ÃÀÞÞþ†s»·2vìX"##‰ŒŒdäÈ‘tíÚõ†e=<úè#Μ9ÃéÓ§5j”õX{xx°zõjÎ;ÇÑ£G­‹K›…œqqqÄÇdzråÊ[¶ÇÞÞž¾}ûÒ§OŽ?Njj*ûöícÞ¼y€í´@óæÍ			¡qãÆ,[¶Œ3gΰmÛ6úõëÇܹs±··ç믿æ½÷Þã“O>áСCœ={–µk×rþüùë Èƒôxžeä_•+W.‚‚‚lž«\¹²õN©cÇŽŒ=š7Þxƒ
*0xð`ëjì	&pôèQüýýY´h|ðeË–²ïþkÕªeSodd$:uâ‰'žàõ×_çǤL™2899±fÍΜ9C@@µk׿Ûo¿%W®\X,/^L:u¨R¥
à§Ÿ~Êñ½”.]š'žx€|ùòáïïoÝV·n]Þzë-zôèAHHÍ›7gàÀ@öÅéêHÁUÇÇÑÑ‘:uê0tèPFMÍš5­Ûs¼;|ñÅIOO§G·<öªÁƒS¨P!êÖ­Ë Aƒøàƒ°)Ó·o_ºwïNãÆiݺ5ýû÷àí·ßÆÍÍ€€ÌÈ‘#­Ÿ“K–,!%%…ºuëÀúõëìáúªU«Úìãé§Ÿ¶†Â±cÇÒ¬Y3:wîLÅŠyõÕWIMM%55•
6Ø,ü³X,Ì™3‡.]ºðî»ïR§N^}õU¼½½iÞ¼9­ZµbÉ’%lذ-ZȘ1c?~¼õ×.>>>øúúÞ‡#,rû,€1çÂÅݹÚåseöìٶϵk—ã­ˆÅb1ÃøWÚ%7÷î»ïrüøñë†Êï”Åbyäú»»;6l B…
º)¬Y³†Ÿ~ú‰©S§>è¦Ü•Út]x<åt]Б‡Dxx8ýû÷gÓ¦Müù矺9ò?
4 Aƒº"÷‚€ÈC¢`Á‚¼ùæ›T¨PÁæf´páÂ;þ¥‰ˆü3
"	WWWêׯÿ ›ñP¨[·îƒn‚ˆih± ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰9´Ëçú Û!÷Iûöíÿþ”q£²‹åþ6F	êº.˜‰€aÜðÚ "¸za×ùàñ¤~ s?ÐÔ€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚€ˆˆˆ‰)ˆˆˆ˜˜‚ÀCløðá|úé§dßûöí#==ý_ÛßÏ?ÿÌ+¯¼r×õœ;wŽÒ¥K߃ÉÃhÏž=Ô­[÷¶Ë·k׎¥K—ÞÇÉ¿á£>bäÈ‘7Ü~ðàA._¾|ÇõOœ8‘·Þzë†Û'OžŒŸŸ%K–äÌ™3”)Sƺ­mÛ¶,[¶ìŽ÷ý0PxˆÅÅÅ‘˜˜ø@öýôÓOý¯í¯|ùò4lØð®ëÉÌÌ$<<üî$¥´´4"##o»|tt4)))÷±EòoHHH >>þ†Û›6mʱcÇî¸þ¤¤$bccsÜ–’’B¿~ýøòË/9|ø0ùòå£[·nÖíCsxÐ
x…‡‡³nÝ:.\¸@Íš5ñ÷÷²/2‹/&""‚Ê•+d}ÍêÕ«yê©§Ø·o‡"00J•*Y·ïÝ»—õë×P±bE4h`Ýö矲{÷nŠ+F‹-ptṯ]†a°råJŽ9BÅŠozá4ƒåË—sìØ1Ê–-KãÆ±X,lذNŸ>ÍŽ;èÑ£...6íÉÈÈ`Ù²exzzR³fMŠ-Ša¬^½šÃ‡S¾|y5jd}ÍòåË©Q£;vìàØ±ctëÖ5kÖHhh(/^¤cÇŽ¸¸¸°`Á¢££iÙ²%ÅŠ `Á‚deepùòe–/_N£FX¼x1.\ U«V,X€+W®ðçŸrðàAòæÍKÓ¦M)T¨Ð­?Xy .\H£FÈ;7çÎcãÆ<óÌ3888pöìYŽ?ÎÓO?
@bb"Ë—/'!!
P®\9›ºâââX²d	´jՊܹsßtßk×®åðáÃR¡B.^¼Èºuë¨W¯Ë–-ÃÙÙ™–-[rþüy–-[FJJ
AAAÖ}oݺOOOJ•*À‚¨V­ÅŠÃ0,X@‹-ppp`ëÖ­lÙ²ªV­J:u¬mÙµk›7oÆËË‹gžy'''¶oߎ»»;‰‰‰lÚ´‰N:áááqoþcâèÑ£¬^½šR¥JѤI¶lÙBjj*«W¯&,,???òçÏÏ–-[¨U«¡¡¡dddбcGìííùý÷߉§uëÖ· ++‹ÆGll,¯¼ò
¯½öšõuýû÷§sçÎÌœ9“cÇŽÀÖ­[X²d	!!!œžþyÞyçâââxûí·éÔ©“uÛðáÃéÒ¥ãÆ#""‚´´4›×ÈÈHêׯςˆŠŠ¢C‡têÔ‰
6pöìY¨R¥
+W®$,,Œ:uêXË/^¼˜?þȾ µnÝš¯¿þÚÚæàààÀ´iÓhß¾=QQQœ={–O>ùÄÚ–ÿüç?tëÖ¸¸8æÍ›‡¿¿?©©©|òÉ'tïÞwß}—Ó§OsñâÅ{rÜ›6mbðàÁDFFÒ¯_?ëTÁ‘#GHKKcïÞ½lÞ¼™sçÎqüøq:wîL=8tè“&M¢C‡¼ðÂlÚ´‰uëÖQ³fÍ[N'¤¦¦²k×.Ã`óæÍ>|˜øøxÚ´i“cù¨¨(ªU«Æ¼yóˆçù矷ö‘‡!ÙÒÓÓ
OOOcÑ¢E×m›={¶Q¦L#==Ý0ÃˆŠŠ2œ}ûö†a+V4†j-?pà@càÀ†aÆ€Œ\WçÉ“'
gggãÔ©S†aFff¦Q¡BcÊ”)†aF¿~ýŒaƆa_ýµÑ¨Q#ëkSSSÂ…û÷ï¿®Þ?þøÃððð0’““
Ã0ŒäädÃÝÝÝXµj•a†Ñ°aC㥗^ºé±È—/Ÿµ]†a“'O6êׯo}œ––f+VÌØ¹s§a†áããc|øá‡ÖíÇ7cïÞ½Ö68::_|ñ…µL:uŒY³f†aß|óѺukÃ0ãôéÓ`¬_¿ÞZ¶B…
Æ’%Krlë;ï¼c<Ø0ŒìÏÅb±Üô½™
`Ìž=Ûæ?Ã0Èé¿ûy>xóÍ7×_Ý0ÃhÞ¼¹Ñ¶m[cĈ†ad÷ɹsç†a!!!ÆW_}e}Ýš5kŒ'žxÂ0ÃØ²e‹M¿ÊÊÊ2j×®m|÷Ýw9î3  À¦¯Ïœ9Ó¨P¡‚a†qäÈ0¶nÝjÝÞ½{w£oß¾ÖÇ3fÌ0|||Ã0ŒuëÖåÊ•3Ã0&Nœh´mÛÖ¨^½ºa†ñé§ŸZ÷Ó±cGcäȑ׵eïÞ½†§§§‘””d}®eË–ÆÄ‰­¯kÑ¢Åà]zXúÁxë­·???#++Ë0ÃX¹r¥Q¦LëöâÅ‹[û„aƶmÛ;;;#<<Ü0ȉ‰1cúôéÖ2מSF}ÃsbXX˜áââb}|æÌÃÎÎÎú¸N:Æÿû_Ã0£W¯^ÖsÑÕ׿ɓÇHKK»ã÷~¯åÔ45p'N@³fͮ۶mÛ66lˆƒCö!+R¤¾¾¾ìÞ½›Ê•+Ø,b*Uª6l M›6<ûì³:tˆ–-[Ò¾}{
.Ì®]»([¶,%J”ÀÎÎŽF±sçNzôèa³ÿ7gsn°™‚¸ÚÖ€€\]]puu¥nݺìÚµË:P¯^½tl6lØ@RR’Íþ³²²8pà~~~9Öéä䄯¯¯µ
W§®=FQQQ9îÏÎÎÎf8õÚ²çÎã?ÿù›6m"==äädþÑû‘_pp0C†!==]»v±páBÀÛo¿ÍæÍ›™3g}ççââ¶mۀ쩠°°0ëÈ•‡‡‡µ_Y,5jÄöíÛéÕ«WŽû½v®qãÆtîÜÙ:§ëäädÓ'·mÛÆ¨Q£lÊwéÒ…„„üýý‰ŒŒ$22’U«VÑ·o_^~ùeâââXµjÏ?ÿ<;v¤k×®lÙ²…-Zо}{ÜÝÝÙ¼y3ŽŽŽ0ÀZDD°>þ§ßK3	°No–*Uê–k˜¼¼¼ðññ±þíââB­Zµ¬Ûovþ¹S6l H‘"6çÉ+W®pêÔ)Ê–-{O÷u/)\ÃÞÞþÚDlÃÎÎÎ:‡}•aØÙý5»’+W.ëß‹ÅZO`` 'Nœ`ñâÅÌ™3‡aÆqàÀììì®ÛWVV–M×ÖW³fM›€Ð·o_›Õ«7«÷ê¼äí²X,T«VÍfeß¾}­ó¥ÎÎÎ6¯¹𮲳³³y.§czí6{{{›ý_}Oýû÷§páÂlݺ•yõµvvväÊ•‹zõê±|ùr6mÚÄŒ3

bÙ²eüñÇ|ÿý÷<÷Üs;vŒ…ò믿òþûïsèÐ!Š-Jß¾}möéééiýûïß!ùËί7ò÷óÅb±yîvêø§,Íš5³®uìódÑ¢Eïé~î5­¸FÉ’%)T¨óçÏ¿n[:uXµjW®\²“üþýû©^½ú-ëÍÊÊÂÃî]»²`ÁÜÝÝÙ¹s'Õ«W',,Œ“'OÙë–/_n“Z¯
dûöíÔ¨QƒÚµk[ÿsww¿®¬¿¿?6làÂ…@öŠÛ
6ØÜùÜŠ‹‹111×í¿Zµj6û¿º€ïßtäÈÈ“'½C~...Ô®]›áÇŒÅb¡^½zŒ1‚àà` ûDZ¿~}Ž=jÓÏjÖ¬i
†ñññìØ±Èþn-[¶ì¦}{ùòåÖ¿CCC©T©Ò
úûûj}¼páBÊ–-Kþüù¬ëtž|òIœ	f̘1/^œ"EŠXÛT¸pazõêŲe˸téGŽ¡^½z=z”bÅŠÙ¼·’%KÞùAëÔ-00}ûö]×w¯]Œý0ÒˆÀ5ìíí™û,Ï?ÿ<Õ«WgàÀtéÒ…Å‹S£F
š4iBrr2kÖ¬!44ô_ÿÝ~ûöíyã7ضmû÷ïÿW÷-w'88ØæÂÌo¿ýÆøñã­e>ûì3š7oÎÞ½{)W®œ;wŽ•+WÙ¿0yçw¨Zµ*Û·oÇÉɉ_|ñ†ûŒŠŠ¢}ûö-Z”3fX%ædèСÒ¡C<==ùí·ßlÊóÖ[oY§‚ƒƒéÚµ+ýû÷·–iÓ¦
®®®”*UŠ­[·Rºti|}}É;7ï¼óþþþ´mÛÈžŠxõÕW­Ó
rgZµjÅË/¿L­ZµèÓ§ùòå{ íxÿý÷iܸ1!!!T¯^³gϲ}ûö‡þvµ?dff²~ýzN:…‡‡O?ý´õºð0È©(ˆ<æå€Ü;ê9÷­11S11S11S11S11S11S11S11S11S11S11S11S11S11S11S11S11S11S11S11S11S11S11s°X,º"òÐù@@ýÀLðù´çÌ™c<¨ÆˆÈƒ¥ó€úÙhj@DDÄÄDDDLLA@DDÄÄþ±æT´ÑÂIEND®B`‚urwid-1.3.1/docs/manual/images/urwid_widgets_2.png0000664000175000017500000016615012615524560021510 0ustar  ianian00000000000000‰PNG


IHDR@h~›0sBIT|dˆ	pHYs
×
×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝw\UõÿÀñׇ©€ÈPq €\¸·æ(͆Vfe¶¾™eý¬4ÓÊÌJSsefjŽÒ,M[Vj¥æÞEAQD‘)ˆÈÎïs¹lÈy?—{Ïù|·{Ÿûyßó
ÐB!„BˆJÀ@Ó4eê‚!„B!Äݤ”ÒÌL]!„B!„(+	!„B!*
	€„B!„•†@B!„BˆJC !„B!D¥!B!„¢ÒH!„BQiH$„B!„¨4$B!„BT	!„B!*
	€„B!„•†@B!„BˆJC !„B!D¥!B!„¢Ò°0u(¥U4M;fê²Ü
J©ú€#¤iZú-¦ñÐ4íäÝ,›B!ÄíRJi¦.Ce¦iš*Iz¥çQÂL”²¾*ðrp8„jšv¶$ǨȔR–€¹¦iiÅl?4Ñ4Íä©Rª0ˆÕ4íÃ"¶×&žNÕ4-¼ˆ}&u€O5M»¨”Z
šjš|‹å¸ iZ;úCîRʰ24MË.Ëc!„¢bPJi¿í‰6u1*¥AÝJ)¥J­ÁmŒ¸ÉÁ65M;\JǬHÖý•REšº07”R³5MK(°½7¹çú(°(ïF¥TUàCà:0ê.—õnx˜<¬0qY„B!D)+í1@W€ª† ððð pH)õf)ó^Ðp7u!@¿ì@¿6º±K =PêQÄöN€°GÓ´Ãk#€Z@h©X!„BˆÛPê“ hš–fø¹¢iÚ1MÓ~Ó4íaàq@¦*¥¼ŠK¯tõ•RηzL¥Tu¥T¥”ãMöËÉû†û‘ÎF)åkèê—÷u[¥”·Rª¶RêŽoÅiš¯iÚ
áv;ùÊÝ@)u»çy›á±gÛzûЃ¤â¶çÍõ¥iZV1å43”ó–ïHþ¶z·º¿!MÎù¯q;éŠÉ«ªRÊK)Õ¨4òB!„e£ÌfÓ4íWà;ô»CŸÜ®”rSJý$¡ŠSJ]PJ=WT~J)¥Ôû†±"‰@pY)¤”êS`ßêJ©o
û3ì¡”zµˆ|»*¥•RS
¿3”éàeØg¬R*HB€‹À5¥Ô\¥TµSØR`/`oØw¹Rª¶¦iÓòìoÞÀîø£OÀp¨<46öµŽ
Ñ»â­3çôFnsMÓ^ÏS :z÷¯×•€3zÀðpX…Þ vBïê7
h
ä4„³€iÀ@cC9¯¶Åä9¦=z—Á¼ï‰°èlxo~ÌÑǦ|túçIbf(·/z€²xèh(ÛLô j57w¸´TJ9hš–hx=§ËÛvôà*çµC™«íà@žül
e3/pœ•ÀSèçç; 
lA”S¼'5€ƒ€°Ø	ÔFíǰ)¦¡á½p–~·C?'Ë”>.k²a÷ÐïR@¿VŽæÉ*Eé$üþ¯ö±è>ô<B!„åZY@gu•R6š¦¥žÏE0î×4mKÎÎJ©…è
Ö	J©y&x=øù
x*ÏXлØYçy>=øYªiÚËyò^†ÞHU)õMSPw†kš¶¤ˆ¿ã¾NdDŸŒRâ|ÊBYw«ƒ>Eò•<ICÃcà¾bÒ¥¡áÈ™ùëÂ-àR1Û/Ø/¯Ø¢Æýƒ^ÖSèAÔeôñ*.èãQlŠJ{«AŠn˜–:MÓR”RWŠ)wr¯>u5‡Sœô¿«•RÊžüãrì0<öPJE )èãtn&gÚïBﳦiJ©‚ÿ‰®†Ç‚ëå(ê|å\[½€îŤË{mÝŠ§WÑLJ3üĺkN.n±[!„BT.½BÃÆ-¨Y»>ë×.¦}·ŒÛOûzò(ÕœiÓ¹/ÖUª’Ãéàc´é܇œ¹«îÁÑÙÚõ<óåq6„ìì,ê{êßãÞ…S
7Üëëß1Ý¿•F¾­±´´ÆÃË7_ÚÀ#„:N£fù†åš’Ì£i­;öælHžMZbg¯ßø–|ÿýÛH¾z…f­;ã^OÿüÄ‘]d¤§±ÇFZwì…¥Õ­~ï^¶ÊzÌBÎìlyïå!5M«zƒŸ—ûå>µnáx9ãmŠ›>Ú­À~·âôàg±¦iM4M®iÚ8MÓ&‘{¨D]øâÉmôçc˜lÀžÛ+÷í–ACvÌÑÇõDïv¸'Ïn‡Ð¿žè“-TvßlüAÎy/4ã›RÊ’ÂAIÎy/î\µŽRÎ1>¾ÉµUäLƒEÑ4-Î0iB}ôëùæ(ÜõP!„•\Zj
gNív}ãk¬^Ä„‘	?Äšogóîÿ 5%;{G~Z:“?V/àðÞÍÌšø*6vö…ò=ÀÒ/ô™éL~g(+M 9)‘©ï>‡¹¹Wâ˜ñAîh?V-ä£QOpþì)æ~ü:wçÞ™ÊÌHgü+øcõ"Nùó阧™=é5.„ëK9žfôó½Ùµeág‚xÿÕGØ·m=çNŸ$3#ƒ‡	9q˜ÌÌB£TÊ2»¤”r¦žÎ˳)ÈðØ}Æ´Ò4íŠR*ðVJ¹ÝhýMÓ’”R1€§RÊQÓ´‚wÚogΜñ=?±­}¯>	ôñ&·*訔òÕ4-°À¶vèã¨îö¢ۀAèwz‡5M»–³Ñp§fŸa[hž4·"=°j…>ÑC^~Î5MKVú”ç>J)gº{˜÷ÚºÕέ·t®c•¶[”RÓÑÿþ'Ð}B!D%7ùg±°´är\4ÞM[ñæÄù¤\»ÊßLå½iËiÙ¾š¦ñÖ°ž¬_³„'^x‹w&/æÝ—ûQÓ݃ÓßaôÇqpr)”‹¶ÝøjêÛdf¤tüÍÛt#äÄa´ìlŽì³i+ªTµåê•ÜæojJ2«–LgÂÌðõ댖o|ÐÖkÈÎÎbÖ·›8uâã_`ÜþݼIôìÿ$O¿ü.>-;²|þ'tì9€‡ž|ÿÛ62ß°ûr©Lî)¥ õpþÔ4íû<›¶= ”z¬˜ôU•Ry»•ÍGïJ7­¨™·
¼¶}f²w
ìc‹>C@¾Ù¾n"gë^òs+xŒ<ÎÛÞÆq–?È»~¡Þ„ûÜ-Û£“Ù^Ä>;ÐÇd=W ÍÍä”ý=•gM#ÃßúA1iæ¢&Sóžc¥TôéÁ:Š~Ǫ·RêÉ¢24\[¶y^*ö\)}=©¢º¦¢w1,®û¡B!*™1Ÿ,dΊÿ˜¹ìj¸ÕæsÃ]˜ˆ³§ðñë€R
¿÷€[ízýÓÆ¿@ßG†Ò¢m·"ówpv¥–»AÇpüÐNÚvîCoÂB8~hg‘é"ÏŸAÓ4š4ׇØ+33šµîbÜ@óÖ]ϽATŽà€ƒœ	>Η“GñåäQìÛ¾žèÈp23òÎ?Vþ•ö ÔĠèã0\ëèÝ…ÞÏ›@Ó´l¥Ôÿо¯UJ-EoD' ÷i
AïntÈl&ð$úZ.
•R«€p 6ú¬_[ÉíŽôð,útǵÑ×wqBÃá|¯iZQñ‹óútÌ£
çÍ褎GïÚW£ˆ4ÿæ(¥@odGkšVhæ²<– 5y°3üè3¡Ý‡>ÂO·Qî;€Þ/g,Ͷ"öÉ䉾8è­ŒÿAÓ´­†…G•Rߠϰö<úy¹B‰Ðg‚>ƒ\;¥/ˆêŽ>‹Û2ôéÑ3±å¹¶ö«
•nEÛTýNÓô÷’íFŸÞûúCÇ
å˜aØo¦Rê{Ã{¾æÔôÀ,o`/„BˆJ¬ª¶vÕ±õªÎ—Æ2jh7/ÇbffNvvþ‰c³²®£Ìr¿¿?qd7vöœ;uÃc4oÛã‡vrüÐÞš8ŸkÉWÏw2rü¬Bû+33²³³ÉÖ4ã ðììlr¾j·´´"9)w²ãÌŒŒ|]Ù4M£MçÞxÆ6<òÔÌ-ÊzZ’)í;@–è
ØçÑ‹¶è
â¹€¦i#Šèº„¡‹—úz3/¡/H¹XˆüCžIÝ:¡7ˆ;¢O¼=hhœÎ³o"zCw£¡\?£bÑ}ávþ@MÓ¢7ÀútÜлîýB1ÝŸ4MÛˆ¾Hj"0ØpÜ×nrœë@Wô»S¢w¹[ÞmlúšI…&H(M†üsœœi¯Ú‡>6`Ï-ŽÿÉñ8ð-z@·ýÜÔ5êeôn¡ÀÚAš¦£_[¿£Ÿë•ä^[CMäÞõÉ™}ú»Ý€‰èçË}ÑÛôf@~çòÿ̰¯B!D>AÇ÷ãìZ‹jÕ©Û ––Vœô׿{ͺžÉÑ}[ñnÚ
€]›×qtÿV¾üa1—γ~íâbómѶûwl ñr,µëyÒ¢mw¶ý½–ø˜HûîxT»®'––VÓ¿ûÏʺΉ#»ŒÛ}ý:sìà’õæúÞ­’u=·iפy;}òß6Æï¦~˜™™ãìR‹kW“ÈÎ΢¼SÖ½4uArf@óF¿Ksˆ(j­Ÿ<û[ OílØÿlqaZl_ôoøC
N}›å´C¢®£¯týNóº…cUAodgA7z?*"Ã{ÙýœÜÑ<Œ†i­çOkšVäb¯J)+ôkË}‰ówò^fÅó@¿F£sy×2B!ĽM)¥ý¶§Øaè<Öµ|Zcc[˜¨d¤§òôðqÜ×_ï‘¿éXúÅtèñ §bïàÌäù¿{‰qÃûóá¬Uxûøq1<”ñ#bÒkðlÒ²ÐqR’“Ö¯	=ú=Á¨	_’Ű~MhìÛ†‰sôæPlÔ^Ò‰5ÛõÕcþù}ß~9‘n}tü N.ÔªãÁëïÍAÓ4f4‚ƒ»þ¥ŽG#ìœ9À3VâÕ´—"ÂøtÌ3XW©J‹vÝIˆ‹&ñr,ŸÌû€Éc†’t%·Úõ1ösìª9*sI
êìFIb¥”Vî !nD)e]`¡[”R.èë6Ùžš¦9…¹B!Di¸Yxt9_Ç;»Ô¤V݆…ö‰<†ÓÁǨîàL³Ö1·°$êâ9ÒRSððò1îwá\(JaœÞº ÀÃ8Õ¨E
·Ú„:Ž=5Ý=ÈÌÌ $ð0¾­:Óœ;}’ˆ³§ðjÚ
K+2ÒSÓY\ºp4
”bä“ùö¯Ɖ23Ò9é¿øØK8ÖpçeG¬«äÎIÔÅs4nÖKË›¼“·O Qé(¥Ñ»ÝA'Ôx}ìÕxMÓ¦›°xB!„¨nUdYFƒF͹’ÇÆ_–afnÁÄÙ«L],£Ò€*Öˆ%!ô	
¢½[`(0RÓ´µ&+•B!Ä= 33“ߘOvv>­:ñÈS¯šºH¥Nî‰
ɰ®”y¯‰B!Dùv/ß*ï䨴³û%ÞtG!„B!ò(“…P…B!„¢<.pB!„BÜ¥Ô]]‹QܘtB!„¢É̓ŠMºÀ	!„B!*
	€„B!„•†@B!„BˆJC !„B!D¥!B!„¢ÒH!„BQiH$„B!„¨4J¼Ðï{cîÙ… êA]"L]Œ;f[#ÛÙ%ËÄqp‰æ¹¿—¯;¹ÊõQ©?(qý³÷÷{öúàÚi ÔwIE¯ ä×ÇÝ&¡šVI×a’…P‹QÑ+Ri¼!îˆÔBS©èõOE½ç7S¡Rrë<¨ÄyH¸"TôÊC/B˜ŽÔBS©èõeE *zå!!LGê!„©TôúGˆ²$P½òÆ‹¦#õ‡ÂT*zý#DY“È ¢WÒxÂt¤þB˜JE¯„0	€¨ø•‡4^„0©?„¦RÑë!L¥Ò@½òÆ‹¦#õ‡ÂT*zý#„)ÝÕi°ÓÓRù퇯ŒÏÍ”Ž5jÒ¼Mjº{ÜQž±Qø`䣘›[°`íþ"÷ùõû/ùû·åôì7˜g^ÏŽeåÂ)´jß“‘ãg÷«è•GEo¼¼>òòÒ;(¥9¤×33˜öÍzœjÔdâÎØÉKðöñcî'oè¿——F}BÇžÊø/eå—sÉÌÌ J}=ß¶èÈpR®]¥†«;Õª;_?wú$š–MFXZZÝ0ÿ°ìü玲hÓ¹¯qÛ@Çððò¤þ0µÔ´t¾ú!w
ZKsœªÑ³}+êÕv+õã}ûëFâ® P¼ùüXZ˜»ïÎCÇy볯ð®_‡Õs&–Êñ“SR9w1
k++¼ë»—Jž÷²¼Ÿ/>þöÎù¶ç´ú>ò,ƒ_x»T޹ÇFΆž@¡¨j[
÷úžøu¸3³â¯—;QÑëŸÊ`Ö²5dkùëØ·žLtÜežó);øÒD%ÓeegÓþ‰WÙ²|6Õì
mÿkÛ^þÙyyŽ*ñq‚ÃÎãëåQ¢|JÛ]€RøiéÌB¯››[0bìtú>òìm癕uب7¬L’¯^!6êW“033Çʪ
yA½ò¨è(þúòâP
++kÌ”Jéë]ÅÇEuŒŒ4/ÇuÔÔkeVnQ¶‚ŽígåÂÏŒÏ[¶ïAïfÆç‹g½Ïá½›yåiôìEãëï¾ÜÌŒt¾Zµ÷úÞ7ÙÙYØÚU¿å¼ÒRSð?°ø˜‹8»ºÓ¦So,­¬¸Æù³§pqs§šƒ3þû·áíÓ
/ßÒýƒD©ÙºQoäÚÚUçZò¶m\c€‚ 1Aÿ 9Èþ©éîAtd8ÙÙúÿÈñC;¹~šF¾­ÉÌÈàlè	œjÔÄÙµG÷ýG½†MÇRJ{‰}ÛÖÓ¥÷£ìÞ²Ž+	q(¥ŒßºDP-)ˆMÁ\ˆŽåúõ,êÔtá¾~T±Ö¿d9MàésÔrq¦¦‹[÷ÅÞÖ–û»¶»áƒ²p/Ö›¿›Em×¼þñlÝ”…«Ö1küHãö³
!3ó:¾Þ´mրȘxŽŸÆ¶jº·Óëõ£'C‰Š»ŒgÝÚ4jP€µ·£iÕíl¹’|5·
€OŸãàñ`š7nX¨|ÙÙ‡Nœâôù‹$]½†³£=Z45Þ©ºž•Ŧ݇èÝ©5Ûöû{9‘û:¶¦¶«31ñ‰ôFÉÆz2$\¹ÊÎÃDÇ'РNMîëà‡¹¡=yúá‘ÑxÕ¯ƒR°ëP½:úÝ•»dI›N}¨U§µê4 æR„±np«]Ã{6ã^ß›F¾­ó¥‹‹Žää±}$'%P¯aSšµî\(ïûú?Ékãfrp×?̘0œ­~bèˆ÷ŒAXzZ*G÷o%>æ"Ž5jÒ²]wãgÜÑ}ÿ‘‘‘Ncß688»’|5‘À£{133§m—¾ÔS$ø©@žø-Ѝ
:~*ŒÃ§pªnOßÎm°©Z…ëYYlÞs˜û»´ÃÌLω0îïÚÐ?g._¹J«¦^ùò:M|b~>ú‡NœÂÚÊ’æôrìõ¤aÝÚ¸:9ÐÌ»y¾:ÎþcAøq·&3ó:vìçZj=Û·"âRõj»RËE¿®ÓÒ3øoߢãh×¼	ͼõÿ­Ý‡õ»¢9õV'?ß"ï8•µ2€œ\jQ·A#ºöÄ®ÍëHOK%9)‘UK¦óßúÕXZZ‘™™èËgÿÄÜÜ‚À£{˜üÎPÒRSðõ+\Ù|=m›þX	€•uê5l’oûÑý[YøùXÚwëÇ×Ó's:d/ŒŸF-gl«Váôù‹töóå·ù“X·e7#'ÍázV[ú°ïØIêÕvãàÏïΛtîÅÆ€­¶vöXYW5¾þù/q=3“o~;‚‹ÛÍ¿Ñ=wú$“Ç[ôó¾ÿ…ìlÍøÚ€žY2ù]í혲à{BÃ/²|ú{4¬S‹GG~@Õ*Öl]>ǸÿOÿÓó3œQŸ~ɦ=‡H¸rÇêÕXµ~£§Î7£S«üõÉ•«É<üê{(¥°07#ózæffÌðîד”Ôt^?
Ð?‡ö
À¡š?Ïû˜K1ñÌXº€èøã¾—výÊî#¼ IDAT§˜±t5¾™Ž½
ïÍ^LÍN´jêŲŸ7ðÕ¿±gõ|֭ż•¿áêìP(ºÇ;Ó¿fÏêùÿp&nÎŽü½äs²²³yîÝÏøwÙL²5ÆO#hÃrœìùõߌžö5ÝßUmÁÒÒ;½=¦iCÞþ„¨¸xútnËâ5‘’šÆû¯>Ë£½»p)6žÁ£>¢n-W¼=ê2ûÛ5Œ~ñI^|¬?'ÏœCÓ4Ÿ e¯r•Ù§ð®-¿óÛʯø~ÁÜj×ÃÞÁ™>Ïò
'Y³=‚åNâV»>!Gðß¿€e_~DZj
Ýúâ‹ï·Q½ÀmìÓÁþlúc%J)ÆN^̇³Vs©è
¢*©ù*K±ñ<Ô«ë¿™†mÕ*ìõ?It|š¦ñáK¹ž•Åð'â¿sÐЊ̳,Ý«€Éï<ËOwå§»òùÿ»ã|~>–øØK<ñü›¬Þr–Çžý?.ž?ÍJÃu—ãt?=x‚Is×T‹òaÿޤ\»Šg“–të;ˆZux9ÖX?Œ?‹¦-õoÁîMf/ß gß`Ίÿ°0ÜÖ?í;f/ßBóÖ]Œùž
=A»®÷3é‹5´îÔÛøº¥•5}~–àãøû×ï8ìÏŸÃÂB¿³ãÌez´kÉÁŸraûZNoú>Úw™þØœ¯ü§Ã/0uôp~ýêœìÙëÈŸÿí¹{oØ
ÜËõÇ“oM¢ûÐ7ÙëHm·Œ1€#!Ì]þ3fÊŒåÓßã¯ES©YÉõÛö±zýªV±fѧï`eiɘi_óêG³IÏÈdÞ„QÔv«èßž†E\ÂÅÉA}»Ñ¥Ms23¯óÛæ]€þ­ç¤/¿#;[ãÍçgËòÙddfæ+ŸMÕ*ü62v¬%|Ûæ¼÷:YÙÙÌ]þs¡¿¥NMW¶®˜Cß.mI¼šÌôo~¤“Ÿ/Ÿ}w7¶,ŸÍ–å³Q
FM™GbR2SǼBØæU<ñ@ކðå÷ù»›fä3YóÅ$š4¨Wè¸Bq6„'_ÍÄ٫ػí/²®g’”x™ofŽ##=gýÈÊBðõë̾íØö÷Ú|yìùïF<Þ–Óß`øèϨj£7øÍG\t$]z?ÊüÕ{èûȳ\IˆcÞ”·0x8íºÞOàÑ=|<ú)öm[OóÖ]ýâ`	~* #?Àûþ¡xß?”7§Ì+´ýZjŸ-\ÉÒ)ï2ã]}<޵¥%ßüô'ÝÛ¶`Ç¡ã€>¶p@ŽìÌó¼{ÛÂ=’Ú5kLdL<‘1ñœ>‘š5œˆ½œHRr
Ç‚NcogKƒ:µ
¥ûø«åÌ7’ÙãGòϲù¾¨Û²ï'BÂØùã<>õ"+g¼Ï¹‹QÆíŸ/YM»æMX5{"ŸŒz‘¥SÆñéüd^×ÛÑJ)&ŒÆ„‘èíê\èØ¦Pfw€Ö,Ë3ù@ƒFŒú0çBÐøaÑTBO%æRiiúžèÈó\ÏÌ4(0øeê{6¥ÿã/±gëŸÆ¼NŸô7ä٘ν e»îìÜ”;86GURó=··³áÝ—ŸÆÜÌ¯úî>ƒÿÉP|¼<ˆŽ×Çýß°ÇpsväÙGú²ÿXPé¼wà^n¼ô}d(Õìõoák¸Õ¾£<23Ò	=y€cwtü )Éú7ïGvåÛ×½¾7/¿=¥P¢|ÙºAïþÖ½ïctéó(?÷[7®¥Mç¾Ôt÷ÀÆFÿÞÙµ–±kœ³K-”Ò+ï:õ½
rv©ÅÈñ³Ïs®€~½Àï?Ìgñì÷1·°äϰ{C¾ôU¬­Xòóކ~Ô´tÂ/EçÛ¯y£†ì£wËíÖ¶ë6ïâP@°ñµ²r¯×£†=F5;[ÖoÝ‹ði^Ÿ4‡•3'°ïØIü|¼é×­=÷uðcÕú-dìbðÓ=Ø~´wÌÍÌØ»;cÍÆ­¼ôxÎ^¸DâÕd}Ò–¡q¨fÇàþ=9bÌÃÚÊ’#!,\ýǃÏpõšþ9Søoyîq¼ë»óüÀØ´ûOœ¢š­
î5
yY»–„GF­O[·yþ·›Øý®Ãî#ùòíÒº9ã_y¦äoö=®¾gSãXB»j$_Mäâù3\Ž‹"-5KK+~]©O®¥$Gvs߃CŒyxûøÑ­ïc\ŠãŸßW°ô‹	xûøQ¿aNàá!¯P»ž'Á¦?Vr1<”kÉW°µ«ÎïÏå­a=9v`;Õª;2óã‰Ô7»XÆï„(
+gNÀ×Û+‹ÂMîÐs¸ž•E—Öúg—RŠû:øèŸ?mØÊsïçrÒU>xíYVüþ/}:·&:>Ö¾
åiiiAÇ–>ìS®ä÷Í»xüîŒxêaÖlÜÆ¿»ª{²²rÿo³®ëÛÌJqÐõ­¨õÇà~=iÔ .íš7fàÈ	lÙw„¤äcý~=ïçƒalXÞo7Ã/\2þr.‚ìl
33EzF&ëwz¾ûõoVüþ¯±7ÀÑ“¡œ>1_>Ù†ó}=+ÿû½iÏa>ýzÞõÝ÷ÊÓ$&%3iÞw…®€ìì¬|e¾QwI³Kug"#ÎÓ˜›[àÕTo¨ü±j!!Gøó§oòå›3@ñbx([þú‘C»7qüÐãöºDàhè¶r«\hê©O¶0~Ö"VüþW­»ý?ºT†ÆKi±°´¤Isý›[+ë*~ámž>ŽÖzçûPòõ×åÓ¶¿×¢egãR³z>H‡ýéÔóAÝÈÌHg÷úØ :zÃàøÁÞeì»_×C¨wm^lj#{ÈÌH7æmv“ó?ø…·yèÉá¼þÜ"»ìó×ï,ìÝO‚΄™Oàés¬Z¿…M»±ë°þ|ûMŠÜ÷n¨,õÇþãAlܱŸù?ü€µ•6U­éܺffŠãÁgøõßlÝ”­ûõ»}[éÓ™ÿ½ó‹×®§qƒzÜßµÛcÞ÷¿èÛvìçJò5œìp_'ú÷èÀƒ=:»¬Ù¸
:5qr°GÓ4f}»†ƒÁü´þ¿|åÛ{TïÉТ‰'ý»w älñ]™f»–#!,ýY¿ëؾ…>IGάv‘1ñü³óþÁ§©SÓÅ8–ÇÙÁžw_~šw^BÇV>xãc¦*o·ùÏ•üúý—ÆŸkÉWn;ÏÆ-°±­FzZ*õ=}xöÕxü¹7ñðö-49S|ì%Žîû¿Ö,æl¨>ž«š½ææø¶êèmšÓÁþüþƒ>NÃÃËÛjdf¤3cÂpÒRSxnèsdggñÊÄY$%§”ð]å‘w}wl¬­ãþ®geñß¾£Æq=5k8Q·–+_,ÿ™nm[PÅÚŠ†uk³è§?éVÄœ9Ðá!´kÞ„®mš³eïÓµMátÎöÔ­åÊöú˜ZMÓØqð¸q{g¿f6Î^·óÐñ|3ĵoÑ„Ã'BhãÛÈøã×Ô›*ÖVT³µ¡šmUãXØò¢ÌîÅÁÙ•.½a׿ßyõ‰ö88»R§¾7çNŸ4îóâÿ}̧cžaÿŽìß±‘m»åËÃÃË×8èù«ÏÞÆºJUê{úx;’ï¸Ïììñ#ynÜT¶8Æž£<Ö·á‘ÑX˜—݇Hei¼”¦×ÆÏdê»ÏóïºïÙôÇJãÌ]}Êý3qéÄíÈéþöÄóoå;w¿¬˜ËÊ…Ÿ±uãú>ò,{=Ì–¿V±oûömßÀïÏ¡÷CÏгÿ“Dœ=Åê¥3XôË¡[>vÃÆ-èÑØ±ØúãƒðÙ•{÷3ªX[á×Ô›ˆK…»35iX)V?(z´oɃÝ;Þr9J¢2ÕïL_`üݱz5¦yssš7jȯcÚ7?ðÚ¤ÜI
žy¨7ßßÈè8Þœ2+KK~ü6µ\œéñì›L_¼ŠŽ­|ùi£ÞýmØ#}yÿÕÜe~Û´“W?šÍÏoç½Wžaò›ÿcÔä/Y²v=KÖ®§k›æùÊ÷ÌÃ}øö׿ùåŸüòÏz¶/þtBÒUú€“ƒ=ã†?
@m·ôêèÇûŽòܸ©4õ¬Ï¶ï¿`Á¤·>a³–­aö·ku^N:¬^”ïy—ÞÞvvöüßs™?u4ó§¾Í×ÓFßë·'-È·¯ÿmøØè]šÚwëÇý>Àˆ±Ÿ3eì0c›À¥fþoÂ\–Îýð3A|d 3^„£ù5æ®ø…ÑSç³dÊØÛ.·(ߪV±æã7_ä¥÷§Ó¿{Ÿ8…£}5†Î]×°{Û,ýy]Û67>ŸúÍ7€šy7 -#ƒæbmeI-g,ÌÍðp¯…«³C‘i>}ó%^›4‡t&gÇÜÙw[5õâ¥'úÓñÉ×hÑØ“¬ìl7¨g¼õÁkÃxêíOè4d$½;µ!)ùÇO…±íû/073ã‘^]x|ÔGx{ÔáýCóuŸ3h𦩛îYŒß÷Æ;;@ÖõL‚Žô>±ÖUª¹_XHÑ‘áø¶êDbBI	ñԪ㳫>$>&’ ãpr©‰W“V„A©ü3Â?@\L$›µ%;;u”.Y4¬[‹èø΄_ÄÉÁž&
ë‘x5™“¡ç°±©B«&z”ÆÕäš4¬‡“ƒ=©i阛›rîî5ù|Éj­þƒ¾Û°ræ„;}»nY¹i¼8¾ãkJ~}úïEËÖhܼ-––V„ž^Ì÷šI‚ŸÒrO@¥Yy´hÜeŸ+•¼n•4^„0Ò¬?úwï@ÿîJ%¯[%õGÅ•³N¨¼$øåÝSzñÔ€^¦.F©¹g¦…©è•‡4^„0©?„¦RÑë!*¢{"ªè•‡4^„0©?„¦RÑë!*ª
ß®¢WÒxÂt¤þB˜JE¯„>_TL%žN!„B!*¥”vOtB!„Bˆ[!B!„¢ÒH!„BQiH$„B!„¨4$B!„BT	!„B!*
	€„B!„•F‰BUJi¥QQ>•t(¹>îmr}ˆ‘ëC܈\âFÊû•rý™VI¯@†B”F6…(¥ømOô]Éû^Pó´éÜÿ¥án•O˜Vi]ò?^öÊbúÒZ!]®{Ó În¥’|¾Ü›JëóånÓ.¯1u*%åôd‰ó.pT=ÎS‡¦.†¢‚)‹àG!„(ÏJå([õ	Ç‹¦.†¢‚‘àG!„¨Âñ൉4u1„Œ?B!„NºÀU ü”oYYY,Z´ÈÔÅ¢	~„Bˆ\U
8+ÁO97uêT&NœÈ™3gL]!Œ$øB!ò“¨hÀYjqÉÔÅ7ÀŠ+ˆ‰‰aäÈ‘¦.Ž€?B!DQdP9×0jeêbˆ›xíµ×

`ß¾}¬_¿ž˜¸T¢2“àGTfîîî|õÕWLŸ>€Ö­[3mÚ4î¿ÿ~ã>7nä£>âÀ¼óÎ;XZZ2uêTüüü˜1c}úô1¦Ù°aü1û÷ï`̘1X[[óÙgŸвeKæÌ™C¯^½Œiþúë/¦L™ÂÞ½{xûí·±µµeòäÉ´hÑ‚¹sçrß}÷ÓüùçŸL:•={öðæ›obooϧŸ~
@³f͘?>=zô0¦Y·n3fÌ`×®]Œ5
GGG>þøc|}}Y°`Ý»w7¦ùí·ß˜={6;wîà7ÞÀÅÅ…>ú€¦M›²xñbºvíjLó믿2wî\¶oßÀë¯¿Ž››'N I“&,]º”.]ºÓüüóÏÌŸ?Ÿ­[·úgæ{ï½Gdä½Û³å“Ï&»À4íï¿=ˆFíÞäðÖi8;U+µcí=ÂÄÏ~bÓoÞq›¶çë¥ÿðÛ÷c‹Ü>ò%tlëÍsOõ(rû­JH¼Fâ•k4¨ïZ¢|JJZIRJi²ÐÝáÉܸñßïÖyÐ]]¨4ª»××iÐ49sæÆO?ýÄ믿NÛ¶my衇L]´»ª´®Êü?~·”‡àÇP7Éõ!Š4¨³Û]ý|177'&&†ˆýÿÀÎÎŽ
`ܧY³f„‡‡sõêUêÔ©ƒRê¦iΟ?ORR’1™™çÏŸ7¦iذ!Ç/6»»;æææÆ4¶¶¶xzzæKãëëKDDÄ
ÓxyyqìØ±|i.\¸À•+WŒi,,,/6/^4¦©]»6–––Æ4666x{{ß4••çÎ3¦iÔ¨þþþùÒDFF’˜˜hLS³fM²³³‹<¥ñùr·)¥´­d^c/ëM­šŽÆ×>ýSfÿÊØÿ{[ëR+ËÖ¼>v	'÷͹ã¼1¼ßàûŸv°vÝ^þøqÜ硜ž,Ñõ¡”ÒäP9u+Á(”RŒ=šàà`6oÞ̤I“L]$Q‰•‡àGˆòÀÙÙggç|¯µjÕ*ßsOOÏ"ÓÝ(MÆ
¥qrr*i4hP(££c¾çw+MÁ}
>÷ðð(”æ^7â…¾´n™ÿ½jÕÌs3RÓ2øgË1úôlŽmüΑy=‹v~úuy"(‚Ýûƒqt°ã¡ZcS57h:s6šÍÛÓ ¾+ææÅ–aïÁÜk9Q¯N
4Mã‡h߯‹ZnŽdd\gãæ£<Ò¿-ÕímhäYÛ˜NÓ4þÞâÏ…ÈËôîÞ¬P¾‘Q	lØt';úÞׂ-ÛOðp¿6˜›ë£k.DƳeû	4M£_ŸVÔtu éj*‡…ÈïëbmmAÿ>~wþ—€Œ*‡¼8-Áâ¶Ið#„å—¦i6ƒ+I)T­bÅoëðژńž¹DŸAŸ÷öÅï2•S¡‘,Y±…Ö=Æ‘t5Ðïø´ê>–{‚øüË?øð³ŸŠ=æ¯îgîÂ
;Πa3Y²â?ö8ŸI? ”bÿ¡PÞýh¥1Ýó#çóÒŽ ÿàÏ
É]òlx~=Þå—?öóç?‡ytèç6ƒôŒLþüû0ú¼ÏνAlÞ€_÷w	8yž”ÔtΜ&áÊ5ö
á°X)½³·Oî•3^œÆ•SCQÁHð#„åK‡>2ÞôP{–Í{-ßö¯g¾L»^ãùfùf,û—	c£Ÿ'gÃc˜4}-Áû¿À£ž}}Ê×Kÿaü[yÿÓyô Þ{{ÏŽ˜GBbr‘eèÓ³ã&éÍ–<öP{¶ìàñ³eÇ	úôl^(Í‘cgùñç]Ä„,ÁÉÑŽ‰cŸÀµÑËÆí3æýAïîÍøqñ›,Xö/[w••Í«£¿á«ÏÿÇ ‡ÚðÞ'?òñôµü¼|OìÄÚu{™öÑÐ;~_Kƒ@å„BËӸkê¢!*	~„¢üùkõxZ6«€•Uá&·­5k¿MËncé×»o½¦OžtðèlªZ1izî£ø„dƒ#Ð4
ÿ€s|9ý%ã¶>=šsäXÑwSºvlBph$qñWÙ²ýŒyŒ!/Í!%5-ÛûJs4à,~-àäh€“£­[ävå;zü,/=›;ÑÇ}Ý|¿‡GÄr):‘_ÿÚϺ¸y™‘—oþ†•!	€Ê…†7¡Ô ÎÔEw ï$ñññLš4©RL‚ Ê	~„¢|ªfW‡ê¶ÆçEMÚ±so0Õím;MJj:6U­ÉÎÎÆ¦ª5¯¾x¾}
‰RЬ¬Ü	$²Š™Lô «C/þùÏÿ€sthãE—Ù°é(‡…ѳ«o¡4fò/xkkK®&§Ÿ_MN3þž­¡<ûdwªÛÛä¦)"4%dbüT|J)8Àüù󉋋cΜ9(U®'¯÷	~„¢â:v"œ&¯bç†OhÖ´o¼»€ö­½ˆ‰ÓgÙëØÖÛøãÙ &J)Úúy²ikî¬y/JïîÍ™öÅï´iÕsz÷hÎÇÓ×Ò·>޶…öoëçÉñÀpbbõ2ÄÆ%ápθ½G~_Ô´4Mcõ/»ÛÔw¥¦«¡g.å+{ËfÔuw&&6éŽÞ¯ÒT¾Â±JF¡Ñˆœ‰7uQD	-X°€#GŽJÇŽe
 q×Ið#DÉÈUw×½¾FI%_KãÉg3ó“aø4®Ã’/_ůǻ¬\³“gŸìÆœ)/ðÀã“éÕ½
ê¹räøYž~¼#^èËôICé÷ÄüOœ#:&1ßìpEéÝ£9M[ÃÿÝÖzwoΈ·¿aÜ›¹sŸzŒx¡/>FóÄ#Ù¶+¾õÛG|ˆ¿7ûãÑòuœìèØÎssÌÍÍX¾à
ž>—µëöÒ¾µ!g.Q§¶3ógü.šŸp•>ƒ>ÅÓÃEs^)¥wôöÈ:@&RZÁ¬T~Lž<™yóæ±gÏž"§V½×È:@¦S‚YHÜHY¬”••u³ô÷l£$J£~¹•¶‰™™Ù=½ÐöÝ'»ö‚ IDATiÓª¡qŠëÛvÒ¥C.'&sælÛ76n‹¸OtL"m
Ó`Ÿ¿Ç#§ÉȸNÓFîøå‡q1ž]û‚ñlàF#ÏZ„†E§Ï.èúõ,ví¦e3ãŸ{‚hì]7—êÄ_¾JxD\¾i»wì	":6‘nšr9!™êö6¸×Ò§bÏÊÊ&(ä"NŽvì9pŠÿ{w—‚¿1¦½œÌž§¸œLƒú®tj׋Ü麃C/r9!9ßß«Jc 	€L@¡Ñ˜S8Qòa•YYY,Y²„#F˜º(eB Ó¨ÁH$nL ò©´ê	€îmié™,úv]:4æDP³æÿÉC´aêÄgÊäø²jTšÁ(_ÌÍÍ+Mð#L£¢?BˆŠGêq«ÌÍ̸Ϥék©ZÅŠ×_îÇ+Ï÷1u±n‹@eH¡Ñ„`I0uQ„Œ4N„¸u‘‘‘¦.B…bŠúåÒ¥Kezœ”àGÜùpºs	—c˜ôÖÍ£¾ùl;>ÆÌ_á¹Ð;N?eì³ÄF](ôúÿ³wßqU•Ç?{8PEEP÷ÖÊ̲҆íli¿Ì†£lXfYj¦¦©eZ®²l82+Ss
ÊR‘½Îïs¹²A† |߯×}]9÷<ç<÷Þã9Ï÷žçù>[þþ…5+æ—êû¿Ïòµÿ–ºNBˆšA®/Bä&P9É~js½²«"nCrq*ZfFz¡¯%%^§¾}#¦}ûW®åééiùÖývõá|Ë
Z/-5¥È}–Æ'sWãàäfü;3#-+€ºÄçúþÓÒÞwfVYYšñï«ñ	ü¾a{¹ÖSQ½ÈõEˆüdP9àG”…\œ
—–šÂ²ù“	=€R0hèºôHÀ‘ÝüºdXZY3ê½YL}çy¦-ü‹¤Äë,šõ>ÏŸÅÂÒ’´ÔF½7WwO^}¬ß®:D ÿ~V,œšFRRM=Z3êý/˜ûé›\‰&#-7Ö¼4zr±õܱq5¡gOòÌ+ïóÇÏØ´îæü¸ƒ¨‹¡,šù>füȤÑCõÞlÝØ°fÿ®]ŽRŠf.ø6­ÀÑSg™4w	™YYx¸¹pää¶.ÿMÓ˜¹øWöûŸàÞ>]6dóW¬!0$Œaã§Ð¯K{ž{螊û2„·¹¾Q0	€ÊÈŒZs’Z$TvUÄmH.NE;¼÷?ÒÓR˜ö΄ž
 u»nØØÖÎWæØÁíô¼k0õ8Ò¤™WÛönß+k[šyúpî´?ÎnìØ¸š}Ûÿ&-5…èˆ0NÞUlTß¾i©\&11ž¾Áÿàvüî`ÈS¯æZ÷ä±½´ò鈇å%\¹HßÎíÈ4t…;ròÚ´ _×ö|ûË:ÖmÙMPH8ÃÆOàòÕkì8èO«fn!D^r}¢h5>:º+ÞíºanaYè:§Ží£qSOj×­g\fFÞ`Kb‘Û$33“M—[ÅíO.N%ãàäFì¥HãßWbchÐЙëñqØØÖ*°ŒK“æD] ++“˜¨‚?g3óÿ畉	š¦‘˜pµ+æ3cÉ&LLL™ñá2
#TŸ=ù}ÙZùt¤m§Þü´p!AÇñhÙ6×zö.$^
1~ÿ‘—®àÐÀGûú\º‡cƒz„^¸1I`gGœÖg䓃sm+ôbYZV‰ê'„¨ZJÛþ(NöõEÚB®Æ'AØþï*’“ŠbîÚÈÕØhãß%
~6ï=Ì_Ûö–¹ž¢úà§äºô¹—V-áðžÍüòý윊mtéuÿ®YÆÆ?~äëiã07·@Q²	»ÍÍ-INJ$ðø6­[Á)ÿ%®«o§ÞlX³”¶zãÔØs§ýiâá…2É}šíîn͵ØH–®ÞÀšM;Ùºÿ¨ñµ‡ôaôgsYù×ý¶ÞXï!ý{±bÝ&~ß°Ó!a¬ükѱWqsräòÕklرŸsa%®«¢ò•¦ýQœœ×iQ¸j]оHXp`®eÁ§ýIINâØþm\¿v•ŸxÛZ7ºÑ„œ9AÈ™ÄÅÆpå²þ샞ÀÁÉ€°Ó‡ñH9Äáƒ{¸–pãÄ•ž‘É~ÿ@¶í?Fzzéç
Õ›?7Çͽ%¾ø‘ÀãhÔØ	_¬ÀÑÉný׳¶©Å€ÁÏàÒ¤ŸÌ[MVf£vÝz8»ê]Ø|r$
¹ÒãŽûåÛwéG/,,­ýÑ|vÿ·Ž¤Äë¼ñá\Z´ö ï=bW¿a¡uõñëÁÃϾAkß®<9|<ƒa|ý®ûŸÄ³v®„óç‚)Ä]O 8<‚9^£G{†?z¾:Œä”Tžt'¾^4°«ÃŸßN%>1‘+ÿ$!)™:¶6˜˜(¾ûômB/F.Ù""*÷ÿCE´?‚Oû“š’ŒÿÁ$&\3–ËÌH'Ð?û£QzÈ-xwå#22²ø•„¨ 
Ð4M+ÙÏ£m@)MÓ´âW,ݶY½»ä¿|ä4÷Ó7ILŒÇ¶V]b"Î3ñË•˜š™óÌ=-ñjÛ™¦Í½é7p(_|0‚÷¿øAO£ûÞ˜™™cnaIXp Ýû
â¡g^gê;Ï1äéQøøøò̽ÞtoçES—FlÜ}ˆ¹¼o+Vü¹™Ð‘XYZ°aÇf¾÷*ÞÍ›²ð×õ$%%óưGÊùÓÑ9vBE~þe96ÛÐJûV7Õ-ø1{UîøHOOc׿µ¸¸5g÷p9&‚±/(×}”FI¾ÿgB¸O–¦1gÙ*ž{踣û-ªaùªªÇ‡¨†tw,—ã£,׿²´1ŠRí–>v¯­Ûu£‘KSíÞÈÌÅ£•/›ÿ\Aâ,Sniû£(em›”GûCˆÂ(¥´j9èjl4a!|¾è¾œô*'ŽìÁ·SoÒÓRyüÅ·h–§_~tDׯ]eò¼ÕL?,×ëÙÝÞ2ÒÓyëÅÇñòhB‹¦Y¿m/¾­æÇÆÊе›wáݼiÅ¿Yq[¨nÁOUfjjJRB<»6¯Å·So|;—~òÓ‚L{ï²23s-{þõI4riZh™’~ÿvµkñïÎddf2uìp<Ý]ËZ]!Ä-Tíléé<þâ[4ñð¢qÓìݶV¾<7¨®4•ö‡7¡Z@'îÅ£•¯ñoOo?NÕO@–VÖ¸{¶ÉWæ\àQš6÷6þÝ´Å+4šŒ
M±²´ÀË£	nNlݧ÷ß_·e7?þ±	7g’SÓˆŒ¹\QoOÜf$ø¹µLLL¹÷‘+lûã?ûþ¦Ö¿™ï¿q£†Œy~hiª%„ÈÁÅÅ…‹/Þòý–wû#'K+šxèY-œÜ8ºo+®„stËJÞº
ÛÎÎΕÞUQÔ\ÕrPKŸN„0þ}6ð­ÚtôÆ‘RùïªzùváÌɳğ	ÐÿmAuˆÇ’Ô"÷9ïÇ5Ìxç>ý^Íܨ ^iâ6#ÁOÍ&ß¿•#**ªø•*@y¶?ŠcC®„ß¶íÊúŽ„€jzÈÞÑ'Wf|8‚ÚuêvŽ6zY¦^GZ´nÏGoÅÌÔœ””$¬-Ìð&S2‹,ÐÁÛ“O¿ùÆŽ
9x"ójùÑŠ› ßò‘˜p×0`HÁÝBrŠæÄ‘=ôà)²²ôÿ·&&¦ìÜ´g7šðlI¤§§annQèßy­žÿÓG!:ö*lÞÅð¡ƒ
]7¯ìyLMªåoTBT[åÕþ0·°*²Œ1ØHûCˆÒ¨ðÿ%)))DFFâîž{"Áœœœ°²*ú?yi™ô
BÏp=Žã¦—Ÿ²8×z#ß™Aº
xiô§Ä^ŠÄÌÌœŸeˆŸÖ$óîËOâì`Àâ)ãe}<›1î…ÇøäÙ{4€”´tF>5˜ˆhýô ¾]Éʪ¼y:*ëó/oi©)œ<¶—vû¹Þþÿй×=¥ÚljûiæéƒM­:¥*ïpžÞ°²¶Éü„^Œ"%5M&­,¥õ¿,ÂÙ­y‰Ö515ÃÊÚ€Õ?ÌÅÊÚ†ûÀÉcû053+u4rh¾ùu?¦fæŒâÇâ?O¸®+á¸ØèX\üu6ìÜѵ=vµkÑÚ0Ñ©a\r-!‘˜Ø¸\Ää”
yßÙ*ûó/O)ÉIlÿwU±ëý»fy©÷ñÛ²/¹]ú>ã;7­!)1>ߟíޱnËîRmóbô%ÞžþM©ët»KOKeǦÕtëw¯ZŒf¸ oX½”ˆ°s€ÞH;‡©©–V6$%ÄpdÇlgÕò9DG„š’Ì?/à¯ß¾#)ñzûܹi-K¾úˆüÀõkW=8NJL`õóÙ°f‡÷l&%9‰UËç°åï_HOOcã?pˆÕóߢֵ“Ô®e“k»ÿî<Àç‹~"<2Ƹlñª¿ÉÊÒû«\ºÇÿí&)9…ýÙ}äs–¯"䂞*6:ö*sXͦ=‡Œe’’SøõŸ­|0û{f/û¤
>§T7›ÿ\AffÑwøwoYGB|\©¶¿ç¿uÛ¿­TeAŸóBhP¾å—c"øuÉÌRowòاJ]V”LiÛß}9”ä$Ü[è©ñÝÜ[bc[ÛX>ûúRÕÛBTurhß¾}Œ;–#GŽ””Ä–-[ò¥Cܺu+AAA¼ùæ›,]º”3fÐ¥K—ЍN‰ÝóÐs„>Ìó½\iÕ¸~¹n{ÍÆœ8“;?ÿÝ=:Ò¿GÇrÝÜžŸÿÙSGqpr£Žþ¹'%^çÚÕËXZYræí:õa°a€¬¬LŽîÅÙµɉ	88¹baiÅ“#ô;tÉI	ÄÅÆ`ecË…ógñn×Õø+oÂõ8Φn=û|Ùx
“=]½ŽÄÇ]áÚÕ˸º{¢eeq>8¦Í[3è±xÕMÄ}ÝÀà0Ò
˜*îzþçðóöä|Dt®l=gÃ.bnfFgGNžãЉ Ѱ^]ÜËkаà@88cjªŸªíÚDKï88¹ñÃ7Ÿqï#/ðÄðñü²x¯½?‡èˆPvlüö]ûáèÒK|üzP»ŽëV~Ëà'GÌü©c÷É·ùö™•žýs!ô“F?ÆßÿkÜ–w»®XÙØbc[3ss|üz`emKZj2ßÏþýïæÙ»ý077cÖ’_yñ‘{ð<ÇŽCÇééçÃ3oÆW¼NÏfÌY¾Š§¸S"/]aåúÿØ»M;‘™™I?êÕ­ÍÎCÇ™¹øW^{fǃBøcó.æLx+בš–ÎÐ}	‹ˆ!-#›|ï¨fŠºJVf&Înú\Jš¦ræNÝ	ôßO«¶úÂXÌwô@?YXYck[3sêÖ³çÞ‡_4Îÿ|Ú'×fÆÓÛÏxW(== €CdeeÑÚ·‹ñx-JJr"W.EáìæAzz!A'ðôÖçœ:î®îžtì9KKkc™¸Ø.œ?‹M޹c@ÏptM›·æjl®îžÆ:ÄÇÅNóV툉çtÀA‚aaiMÓæ­Kû‹rtÏCÏ|ڟνî)ôŽai»UßÊö‡·é(šC£vÜÕݡ؄¥1lÈ€rßfupíêe¦ŒFs¯ö„‡ÒªM'ž>žs§ýY0}<\šÒªM'Ü[ø0û“ט±x#—¢/2õçhã׃5¡A\
âý/~Äͽ%ŸŒ}’Åê“ÉÍŸ2—&Íiب1ß9™K6£LLøyÑÔ·wàjlßÎx—)þ,p`jNçýÙ¿ãoF¾3“u¿,`Óº|ÿ‡?ç‚üY½|.o}ºˆï¦Ž¢ÛÄ‘àìÈë“çŸDZ6‡/4«7î`ÁÊuôêІ?¯#äb$»žGjZ:/¾ÿ9.Žödff‘™™É¬÷FN|b»Ÿ §{€ÂCƒr¥—nÛ±þwÐÈ¥)÷>ò'í%-5…¸+—ppråR”Þ807·ÀÞÁ+k<½;Ë÷ºk0½ú!++“uDÓ´|ß}ßrêØ>âãHIJ$òBNÝ137ÇÓÛÏØÎÌÌܸíÄ„kdf¤1ó¡ØXçïVª”bÒkÏcb¢ˆŽ½Ê?;öÓÆ³YïÙÜÌ×F
IÏȤƒ·'¿ý³ïìAZ¶ôðóáçõ›IIM#!)™„¤d¬­,¹¯o×ÒÐÕÌçh©™&¦¦$^gÂ?™‘ÁÄס¥OG<¼|qóðbê;ÏñÕŠ˜[X2iôcÔ·oDzZ*ÏŸeÀaü,¾Ï+o޳«Œz¿8»y°pÆ»¼÷ù2œÝ<øwÍ2®]½Œ‰©)KæLäíϾÃѹI‘uÌHOgê»Ï1çÇ?¸ƒOßz†9+¶Óȹ	ÓÞ{ù+÷ð×oßáÖ¬ýå¯ß¾cÇÆÕxútàÜ©c˜šé—ïóçN1ç“×h×¹/kúš ãÌþauì°hÖ®_‹ÅÞÁ…eó>á½Ï—y!„ŒôtNÞE;{	€ªˆz
éн¡¯—eL©´?„ȯB .]º°sçN.\È_|A¿~ý˜8qb®u&MšDdd$ãÆcøðáQ›bE
ÞTHðs«ÝNŸÿþðëzC_‹–•ÅÿíÌ£Ï 9ñ:¾øÀ8+6ÀþíÑ«ÿ?9’ôô4ž¾»EÛNKKáÝiK}î– “‡iéÓ‘—FO&==ˆ°sÄD†sæäá\ä‚x·ïÆ÷s>àÔ±ý´íГóçNâ`;m;õÆ•p,IàòÕkœgÃ÷Óxñ½ÏÛY¶f>KgGþÙ±ŸçéõÛ¼ç>-ÜygÄ“<óög\Œ¾Ä=½:³q×A^æ¡›û`«	K+kÒRotÕhÛ©7KçN":2Œ»=IàñÚ³‰Ö¾%kü;ºèRSÐ44-¥nŒÿHOKeüKøð¸º·Ä¶v].E](v‡iâäP`ðàâh‰‰h¹:9°çèIŠì”Méùïf;|žzuks-!€Çﻓ¤äÆ‚_þÞÊøéHÏÈ`ùô÷©[˶DŸEuu!ôÉI	Lür%“Ç=͹Àc4ñhMÂõ8†‚£sîñx!ADZ´´æõ	s˜0rpÛNNJ`ø˜)Ø;:S¯¾ûwn`ð“#¹ïÑ—Ð4ˆp½Kæž­ësݱ.H­:vXXZsårþwÐoàPüî Þ#ÏÖ~ùÖß°z)Sþ…µM-Ö­ü–ƒ»þ`çæ5zlý%>.–çîÓS(ÇÅÆȤ9¿°jùìÜ@¯þC°²¶á¡g^/éG**™$Ô¢üUè áÇsìØ1ž}öÙ|¯=ûì³;v¬Òß~œÔ”d wð³dÕ?¬úw{©¶y-!1ßmæÊT•?ÿl'îÁÃKŸ3A™˜àâ֜г@þþÒÙÎúûG››[и©gëµðjoü·ƒ“«qÈ™ï1ûãQlßð;	ñqœ;í_l=­mjQ×®ÁAÇ137§SÏ{ð?°ÿƒ;бy®‹Óဠڴ¼ñ뾯×üÁá‘Æîm>-n4ª·8ƶÇ6~
ÃÆOAËÊ"8<²ØzUwnÍZyáÆÿ©&^D„ŸãìÉ£4ólƒ_~^4¶ógYª[¯!‰	ó)Là‰ƒ¸y´âž‡ž£U›N¹Æ_äÝžµmm2ÒÓõ„\¤¨›ˆ§‚Ï«'Ú²ï(wtÕMOwΆzœÍ¾žñ‰IÆ¿¸£N
ëóú3ñú3ñÊRß®J™ðôýùý«ñpsaï‘€›z¿ÕQÀÑ=4÷Ê=KÀѽØ5pÈüœ;í»§ñïÂæa©[¿!öŽúØPýœr€Íëbò¸§Ø°z)‘B.Á9 M‡žøÜÁé€C<úühüê甼ÇsÂu}Rv—»æ9æ™Éy>¬c×€úö=)Ë¥¨L?Œ)ã‡xüñq·Çü0Õ]ÎöGNÿ¬Z’o¬kIƒŸªÖþ¢ª«ð«VVVù2¸»»Wj²Gž{s¬HÁ‡Æ;?W®Åw=±TÛ<Ƭſ–g5ˬª~þÙZµéDÈi=“––•EDø9ãDoÙ]<òòòíjœ'!%9‰°àSo¼€iÜ•Kœ:¶qŸ|Ë3#'`naAI'MhÛ±+¾†Oû´íØ“Ã{7}žîs¯çëÕ<×…èDP°ñßÞ-šr&ô‡n4®»·÷¡K[/–N{—¥ÓÞå‡/&Ыc[l¬-IMO/Qýª#gW2ÒÒŒ
@¥žÞ~88¹¢LLðíÔ›ðÓ¦™m×¹/'îáƒQC8x´Dûkݶ3—c"ùxÌ|0jMrtêÿÀÓ|þþ‹Ìœø?î~ài¦¼ù_M¿Øí¶twãùw¦rÿÿÞ%&ö*zvà™ïæO¿â¡Qrþb´qý^Ûrä䆌ú€Çyü¾;8uî<¼>‘aã§0ôÍùÑLý1Ï¿;˜ËWé×µ}A»¯QZµéDpŽyXÎ5þ˜bVØ9¥m‚r̽p¨ÀõG¹«˜ÇØIßðŸàÔØ=ߘËÂøvêÍÎk°­UGç&\½ÍÑý[ñíÔ;×zµjÛ‘¥e‘’¬ÅÁAǯµn{c™ØK‘Ä^Ò8ñnߺõìywÚRÞ¶”÷>_n̈haiEfFÍ=¯T¶ìöG^ñ×®xýF›¹óSÛBTeÕrÐÎMk±°´¤s¯{Øúϯ„Ÿæ™‘ˆ¼ÂÎMkxô¹Ñü·~%mZµÄÇòGýý™÷ãj,-Ìq¨o‡]}€iDL,óW¬!08ŒGô!08Œ^{€ÿöáçõ›±«S‹¹—–în|ýÓZŽœ<ðñSxðÎ{ëžþŸD7kÙ–c¶ÑÀö8§»îŠOÆÆÕØhf,ÙÄ¿k–‘––ʃO¼Â«C»°yÉXªtî6†åÓß'33“ÇÞœÄÃá…‡2lü¸£w÷ìÈÛÓp!*†ußLa¿ s–ÿΜ÷_#îz"¯|4“
ßMgß±S|»rß}öv|sì>¤Ä¿6Þ,¥e96ÛЊû5Mãø¡8¹6£¡£ *¿z9†ÆMõñ=ééœ?w]?.ž?ƒ]Þq_­Ø	@àñ´jÓ‰¤„xb/Eáê®w‹ºªwc«gOJr"G÷mÅÁÉ•ÚuëcjfF}ûF„ž=I#—¦ÆydòÊÌHç”ÿZ·ëB“RB÷`kc³ƒ>Ãñ `<›ºbi¡5OœÓĸ:9žžAèÅ(‚Ã#ØqПɣ_2î#($œãA!x·hjœ7(+K#àl6VVx¸åoUñWáÇGVV&A'Ѫmç²ìª\IŸü¢ÙÚgRËó‘[r|&ôl™Æs†¦iùÆû¦…W;”aÂÙKѱ¶¶åó÷^àµ	shب1!gNàì꥕5§O¤¥þ#D|œþk½“k3²²29~p'(E3Ï6\»z™ÆM[p)ê‚ñüR˜3'àäêN­Úv\¹EÂõk¸¹· :â<–V6Æ,tÑç	>WÛÎ\޹HÓæ7ºê…‡a[»SÆcúwŒËãbcð?¸'W<}:³aF†“˜Os¯Ü?Ü*Cº;–ËñQØõÏÔÔ´Øç¥mc¥$í‘C»2cÉ&”RŒv'ïO_Nff&“Þ|ŒG†½Áˆ‡û3aüÈ*ßþ(JIÚ&&&&…ÎSTí!
£”Ҫ堖>˜3ùuÒRSHKM¡mÇ^œ	8̱ÛyìÅqØ„©XÆÎƒÇéÓÙ—Mô†wŸÎúÅ2==ƒSçγdê;(¥xêþ»˜¶pk7ï¤n-[~X·	€ÄäüO\Q,¥T¾>ï¶µêb[«®ño3sscC&5%™…3ßÃÅ̓#{ÿ£ï=×ËîêbS«N®	Msf³²¶¥kßûòÕ#;RÜ•KùºÅÕ¶«©™9>~Ýo4~›æîû–7«WGŸ–ùöñßÞ#ìó?‰–¥±íÀ1>“{–§»+žî¹'=41Q…f«	LLL%ø¹ØÚgbk_ù“/æ »ûdîd'Ùi§–Îû»ú
	:qˆZuêѰ‘þÿ;{|
`~êØÕ7¦í711Å·sãkµëÖ0n#!>ŽŒô´\û¶¶­¥•5-Z߸‹Xß¾Q®`)o&9Gç&Æeµw
bc"ø}Ù88qhÏfú?ðt®2v
è=àaòrr­Þ甈ˆˆJÙoqíœüîÄ·s\šè?ôùvîƒWi”R#Ú‘‘2ÎUTžj™™›ãìêÁ?«—ÒªmgZûvæÈþ­„Òº¹;Þ{rgdfafz#”©áß&¦ú/‚š¦%II»qñJMK§e37zøéÆ~>4qqät°4ŠnK+kxüeb"ù{ð3¹¥òðË÷3ÈÊÊýËáÃÃÞ¤¡£K™¿=;ø`cmIff&ãG<‰©I…ÃåH‚Ÿ¢U•à§4î{ä%BÏнßýÆÀ¥¼ü·þgc†¸l=îL¿eÞvý†NÜõÀS\æ¾G‡z»¦qt¬œ)Šjä
ʳ23rÍUÇ4…zÄÕ˜öGe}GB@5
€@°þû²ÙŒýxžÞ~ÌŸ:Ÿ¶~ø¨“˜scðgïNmùô›åœˆ&33‹-{ÐÆ³¦&&øy{òíÊuÜÛ§?¬Ýhœ/ä‘{ú0gÙïåêµëÔ­]
¤ä”[²O¸½ƒÐÇŒegx+o<ñ¿
Ù.èw¶šy¶ÚTØ>ÄÍ)¨ýѪmç|s޵íÔ›åß|JtÄy2/°ïNºz>,í!njûós‡îwÑÄï¶±²¶¥[Ï;xú._cðãçÝ3SSjÛÚ0uìÆNÏįóâ£÷âÔPïÖ0ã‘diY|¶àGzthƒ›“þkE—¶^<ý@Þúüî>žéßýLff
ëÛñÈ=}>a:+·§Eõ ¿üW?V/åŸUz¢‚Y½Jè™ü)¤îú—å_ZæïÓîCLþz¹þï=‡8Tq]V~íCb㮑•™Åc>!£˜qåáv~DõãââRiûÎÛþèÔónzÞõ ñõÞ~˜ššac[›c§òýÔQ|óÕ”×þpv®:cZEÍS-“ äeK"Þ`FáY€
KÃzuILNá½Yг3ÞYö.å©:$A¨ê$ø)Ø­J‚-#=3CÖ¼œ23Ò15˽<==ÌŒt¬¬oL
ªee‘”tÝØe2»›£‰‰)“Ç=Í/½EÖ˜š™mݳõOBm૱O”ø=eei¤¤¦æšõ¯m{ÙvàÓÆ½Ì¤¹KñnÑ”GôÉU.=#s3Ó¼› !)™Z6ÖE®—–žŽ…¹9w<;š•_N¤a};f.þ…Æ2t`¿×ÿfü¨úCåü!
U]“ ܬ®/·Cû£(’ATeÕ6	BN¥
~Ξ¿À›Ÿ}…™©)ƒúvc`ïª3[ÜüT®í~g×p9&eBÇý‰·þó+›þøMÓhÙ¦#Ïüï}”‰	s>yø¸+deeÒ¢u{ž>žÕ?ÌåØmXXZajfÎøÏ¾çïß¿'33‹€õ¿.âb˜>Nã‰áoÓ®s_ì‰å2×IIMã®çÇòßÒ™X˜›ŸÄ€DZýǯr#wdÞk°±¶D¡˜8jX®¤¡£ø{û>v>κÿvóÑkÏaanÆßÿBdL,æŒöí[·àË%¿qæüÎ…GÐÃχÿ=þŸ/ú™ðÈÌÌLùä`º·÷æJ\<fGè…(êÔ²%)%Õ¸¿A}»ñîÌ…É!J¯¨ë‹´?„¨XÕ:ªE­9Yªà w'_zwò-~ÅJbk_ñ][j2	~*_fV&!gN0÷§]˜™›óÁ«ѳÿ`šðë’YÌX¼	KK+¦¾û§Žï§¡ccb"Ù<M®ílüã¾Z±#ע̌Ì\É.¬mk3má_\Š¾È”·ŸåþÎ\á2V–ôêІõ[÷2¤/Vý»½»ä»Ó¯k{ã\MÛöãÇ?71éµó¬4uiÄÀÞ]rÝúè«%ÜÝ£#{wábô%ÆO_À_L #3“ˆK±¬ÿv*¦&&Ì\ü}Z2ýíÿq%.ž'LgõÜOX³yÍ;3âhNœ	áÎacŒûkâÒˆsa“
K‚!J¯¸ëKUoq»«¶P-ð&Sªg Š%ÁOÕѦCOã¤íºôåðžÿhâáE3OcÆ«Ž=îæÐ®M<3rõìû|ºõÄÝ>M»Ü9è	Þ|¶~]ï`Ààa8»yäÛO‡nwÐÐÑ3-óËÇr½>lÈ=¼;s!Cú÷âÇuùö“qù¶q1ú2³ÿJÔå+˜˜(bbãò­“ן[÷p.<‚Ÿ×ÿÀá“gHIÕ³>ÝÙÕϘ)pÝ–=85¬Ï_Ûöp:8Œ+qñlÛ”ÑÏé©à}Z¸ãØ žqÛ–æ$§¦¢iZ¾Øe!ç!JO®/BT¾jÕæ:­9)Á(¹8U-±—"sý»]ç¾88¹{)ʸürôEœô®fc?^@\l›×ÿÄìO^ãƒ+xøÙ7¸÷‘9´{3yœy+÷°}{µ0R¯Q¿ní\¯·jæFff&¿oØNÝÚµpoì”o³—ýÎýwtçÎn~ì>À;_|›oÓ<ýÞ›8;òñëϸ½œÜš8;2æù¡´m™{þG{"/ÅÒΫ9)©i\O0¾v-!‘zujKð#D!×!ª†jIð#ÊB.NUOHÐq6¬^Šmm;ŽîÛÊs£>ÂÊÚ†ô´Tþüe!öŽ.lÛð;Ÿ}óBÏzö$M<¼°kà€mmRS’Ùò×J¼|»P·¾=¶µí
6ÿ¹‚Ö,Ø|p3~ÞžXpaØà{3u_}ðFuµ±²äÀñ@l¬-Yðó®Ó¶•lÞ…³ƒ=í¼šóø}w0aÖwŒ|êAlm¬	
á©úç+÷Ä}w0iî^æaêÕ­Í‘“g6dzvfÚÂÔ²±fýÖ½˜šÞHîNO÷’~ÔÅ’ó¥'×!ªŽjÕ!/NIð#JE.NUS¿‘––Êåà“Lýv½±ÛÛ§óײýßU„;Å”ë¨×À¥Îqtß<¼|>f
fff¤¤$±nå·4riʸ`bbJË6YŠzßý¾.Öìß²O÷ƼûòS´lꊕ¥…±.½:¶¥¶­
{w)°®cŸÊë6ñ÷öýŒ{ñ1ž8
@‹¦®˜ª½:cmiÁ~ÿS4oâ£÷ô¥W~ùë?’RRéÛ¹ÝýôIs³
èÕ™–ÍÜXù×ââ¯Ó£ƒ>ïKŸÎ¾Ô·«ÍÊõÿqg7?¼<š`kÈ@·âÏÍz¹|rþ¢ôäú"DÕRmÒ`×ÔàÇj¶BöY“Ò`ËÅéæÝŠ4Ø[þþ…ðÓ<;òƒ²ì¦X%ùþC/F1ÅZ4iÌð¡ƒ*´>å!9%•÷f-bÖ»¯–y[¥	~$
¶(JMJƒ]¯/’[TeÕ&
v]®ÑŠÀüˆòQ/N·7¬mjUè>Júýÿ·÷-š4æù‡Vh}Ê‹µ•e¥?B\_„¨šnû¨.×ðâ&TÏ´4>*–\œª6Oïºý›ùþ_¸MŸò$ç!JO®/BT]·u$ÁÏíaHwÇÊ®‚ùHã¤hÕåü#Deó‹UÛmÙG+%ø¹
TÔ%Q¹Ê3µò­&“¢U§ó¨™""*fà’óKÉDFF¿’ä¶€êq•–œ–àGqÓ¤qR49ÿˆêÀѱrzÈù¥ä*ë;À¤øUª	~„¥%“¢ÉùGˆÒ“ó‹·Û*ªÏéö&„(iœMÎ?¢:qqq¹¥û“óËÍsvv®ì*ˆì¶	€ês…–œFQ=Ç“Hããö—™™É‚*»¢Ò8)šœDuuËö%ç—Ò¹•ß‘yÝPb%øUÞ”)SøðÃ9wî\eWEä “¢ÉùGˆÒ“ó‹·§*Ÿ¡±x$Á¨Ò®^½Ê²eˈ‰‰aäÈ‘lذ¡²«$ÆIqäü#j:™¦Aˆš©Ê@üˆÛÁ+¯¼Â™3gØ»w/ëׯç¾ûî«äZÕlüMÎ?¢¦“)„¨¹ª|$Á¨ê4M£sçÎØÛÛ³råJ^}õU¹°V2	~Š&ç!„5Y•€ª#i|T/J)ÆŒC`` ›6mâ£>ªì*ÕhüMÎ?B!jºÛ"	Bu"!*Ž?E“óB!Ð-%!*Ž?E“óB¡“è‘ƇG‚Ÿ¢ÉùG!„¸AÆÝÒø¨Þ4McÖ¬YËG}DÇŽ4hPeW­Fà§hrþB!r“;@LÕŸRŠýû÷3oÞ<._¾Ì¬Y³PJUvµj	~Š&ç!„"?¹T¤ñQs|ýõ×>|˜3gÎеkW™èà§hrþ5YDD«W¯fÚ´iøùù1uêTî¾ûnã:ÿý7'NdÿþýŒ7sss¦L™@ûöí™>}:wÝu—±Ì_ýŤI“Ø·ocÇŽÅÒÒ’Ï>û___fÍšÅwÜa,óçŸòé§Ÿ²gÏF­­-“'O mÛ¶Ìž=›~ýúˬ[·Ž)S¦°{÷nÞxã
êÔ©Ã'Ÿ|€óæÍ£OŸ>Æ2k×®eúôéìܹ€×_zõê1iÒ$¼½½ùúë¯éÝ»·±ÌêÕ«™9s&;vì`Ô¨Q4l؉'àååÅÂ…éÙ³§±ÌªU«˜={6Û¶màÕW_ÅÑÑ‘?ü€V­ZñÝwßÑ£Gc™ß~ûyóæ±eË@Ÿ;/22²ð/Pˆ
¦MÓ´Rÿ\­”Ò*jΥѻWWȶ+Ú­j|¨úC+lÎ¥e96Û¨°ã£ª™„¨8üMÎ?B!ÄÍ)—$åXQÅ”Ç Õòª‹¨zäøE‘ãCEŽQI‚ *J¹dB!„BˆÛdB!„BÔ(	!„B!j	€„B!„5†@B!„BˆC !„B!D!B!„¢ÆH!„BQcH$„B!„¨1$B!„BÔ	!„B!j	€„B!„5†@B!„BˆC !„B!D!B!„¢ÆH!„BQcH$„B!„¨1$B!„BÔ	!„B!j	€„B!„5†@B!„BˆC !„B!D!B!„¢ÆH!„BQcHt“”RfJ©vJ©f•]—Ê¢”r5|Ö7Q¦•RªMEÖK!„Bˆâ(@Ó4M•Û•2ÚÞ@C kšR^û*a}L CÓ´ŒrØž3pø[Ó´{˺½Š¦”ª
Lâ5M{»€×ëSÎÔ4-¨€uÆÍ隦SJ-^:išv°„õišfWÊ·R*J)SÀH×4-óVî[!„BT-J)­\ï)¥¼€=Àr`,Ð	°g”R_—ç>K 7|q‹÷[%hšv¸xK)åRÀ*½—
ûó¾hh'¢<ÑXÕŠò"ú÷ÿreWD!„BT¾r€”R>Àa #°pÑ4Í^Ó4o 6ÐØ,¯}V’(À	xª²+r¶žûðZ =¸)èu? pPÓ´ò1èŸÁÑò­¦B!„«<ïͬ€÷4M{^Ó´ˆì4MKÕ4mŸ¦iýQ…m@)e¯”r½™*¥Ì•R†;¥¦t†±*µ
[OÓ´,MÓ¢4M»Z‚mº)¥ê–pÿ¦J©fJ©:7Yog¥T“bVÛjxî[Àk}Ñ™^†.ƒy_Ϲ
4M‹7|v)4|–îJ)‹bꕳŒµRªiI×ÏQÎU)åp³å
ØŽ•áóo©”jXÖí	!„Bˆª©\ ¥Ô`ô»¡ÀŒ¢ÖÕ4íÏâÐÇ}]SJmRJ5*¬b†Ïs#„(¥.)¥¾TJµ0ì#_—I¥T}¥ÔJ©k@­”ŠRJ½’g½_€/
~‘çûobX§±Rê7 ýób”RaJ©«·B!„¸=•é®IÝ
Ï‹5MK»É²Ë€§“èãt€G€ÿ¡ß‘è iZjŽõíôît—€è]ÒF£2“
ënÃ܆@o ¸w?ÀUÀ}ÌÈZ¥Ôƒš¦ý‘£œ	P½[XN6†åSЃŠEècOžGïþ·N)å£iZVv¥T`z2Šyèwb‘À_J©§4Mû©€}LCïjøzeE‰.-”RNš¦E^êex?ÛÐþ ²‡
õ3zÀ®ê‘÷øY¼œ¾RÐblòÝÙ2ÜíÚ¸¢ÿ¡'Í´1ìÃ6O`z¼¥ÀôqfOó•RM4M{ǰú¯†:A¿Ãµ7ǦâÏ«Ñ?ËU†÷…~|Ý
ÔØLB!„Õ™¦ieyk
xò&Ëõ4”;ÔɱÜøÙðÚØ¢Æ7ùY/7”}"DzèãêþŽÖæx½“¡Ìž<ÛZdXÞ1Ç2 8ÔαÜXoX?.Ïv&–”gy=ô;;°,Ïk+ˇäYnŠ@¥-r,aXdŸISÃk[ùÌLÊúCò‡<ä!yÈCUç”[¸ìî]¡7Yî%ÃódMÓâ³jú]’	èÓá”Ó¯“£ÌYô.LvJ)û’V@Ó´$MÓÒX†~ÉKÝÄ|7è©¢ãƒ4ý“Þ`ø³EŽõ¢ßåX¨iZ@ž}Ç_£ß‰èVÀ>fjšvá&ê'BèÏQßíäÔ'OÙ¢¼ˆìMÕôÌshú8¡)ó†)¯K7Q4MVJ…-
I	:ê³-ÇjÛ
Ï}”Rþè]óŽÿ)LöC1ì[SJE¡wmËfþ™–M¯ ÷˜ý™õDWU ~±µ½áô±A£Ð箋ž¸b!z×¼¤›Ø–B!„¨âÊ+:mxîE]—Š>Þ=›Y^Žè
ð|êr4=è¢iZ®»J©6èPEÈú†išösí#¯­À3è]Û:¢w%ÜžãõãèI_`'z‚=š¦%–`Û‘èw”ìÑ3ºåå€ÞÝ,[úwëXÀºp# Ê»€ÚDebèn7M)5=áã†Ç[†:ÜNó=	!„Bˆb”× eèé¨QJùµbž¹aΞó†l_Î@h»#eg¥Ë7ŽÇåÌ8]@ðcŠ>	hE9exî[ûÈk«á¹á iZlö‹†ñJ;ÑÙ~yÊ';«^Þ4Ú(¥Z“ç.šá;=‹Þű `§_ËJó™úýç­¦iÛ4M…þÒ€!*ÿ¼HB!„â6V.;MӢѓ(`¥RªÀ»&J©AäϲÔðüV“f~h¨ßRÊ&Ìðœ¯N†qEIèÝÂò6Â_EÏVQþDOñ¢¡^>J©ºªŒ¼æ±Õð<=¸ÛVÀ:ÛÑ»Ýý/O™âdOãø.'ä]Ù`6zÒ‚i9ß§RÊ={[.š¦FOLÑE)õBATJY*¥jçXTè÷¯”ª“sÞ¨ÒлÒ%I"!„Bˆê¥<×_£„
Ø­”ZƒÞ¥*
=Ergôqg³hš¶I)µxØ¢”Z„žìQô¹€B€™e¬×EôÄí”R›ÐçI~0dŽ[žlµRj	zêé{Ñ»Š¥€;åAÓ´D¥ÔpôÑÛ•Rs
uKBOºÐ
x=)C‰:³Ïìq@M
‹¶°Zv—8ô.k%ÿƒ¦i”R+Ç€MJ©ùèAÄSèßû%ô`'§ï¯?ø*¥þCïw°ýXÊ€ß)¥úÑ?ŸÆèwó7<×¹¿› IDAT6Ö?`x}°Rêôï4=øê,RJ-7,DONñ(zŒÙ%yïB!„âöQnánʆÀg:z*âGs¬oX>#OÑGOÐ'1ÍžP5}bÊáe„n€ÿ8ð9úÀÿ~èw–ö¢c£ÑÍ?ˆ>ï
èsù܃žr¹B CÝ6)¥Ú£cÐï e‹EŸ©°„¥µ=¸ƒÜã²FBmƒ%ÿ“íiôz@ïFúä²ýÐïx5ʹ²¦iéJ©;OÑïJ
¢>†ç‹yÊ„*¥¼Ðƒ“'r¼ЃÇ
䲯+¥Ÿ¡bƒÐ?çeès-…£9Td§æ.—qFB!„¢êPb„rß°>†¦9Ð}|H¤aŒIaë›­ÐÇjÜêì[†9fšg5M+,+]Eîß=õµ
z£?Vç*ú]°áèÁÏd	~„B!Dy’;@¢R)¥æ¡khX”‰ÞeòMÓ–WZÅ„B!Dµ#]àD•¡”ªÔC+vÓãx„B!„(Ž@B!„BˆC)¥É,÷B!„BˆC !„B!D!B!„¢ÆH!„BQcH$„B!„¨1$B!„BÔ	!„B!j	€„B!„5†@B!„BˆC !„B!DaVÖ
¬Ù£•GEª#WÂq%¼²«Q&Ý«²”—ã£pr|ÈñQÝ
îæ Ç‡(TY˜=käø¨Æäú"ŠRÖó‡Üª Õ¡q+*ŽB!„•C 
 [Q9>„B!*@åL·¢(r|!„BT.	€Ê‘4nEQäøB!„¨|•iÜŠ¢Èñ!„BQ5HT¤q+Š"LJB!DÕ!PIãVEŽ!„Bˆª¥Ìó•DjJ2;7­&<$ˆ„ë×häÜ„w=ˆSc÷bË^füð{133gþ/{oAmKN·e“š’Ìêçÿ6Q&Ô³oD›=häÒ€›Ö²lþÇ´íØ›QïÍ"Ð?3?z·æLüre%Õ¼däø(›¼ÇGN½0¥Ê4€¸Íå<>î}øêØ5ÈõúªåsøgõRú?ð4>7ºÈmeeerþÜ)šz´F™è¿
ž9y„C{6 PXZYcïØ˜N=ïÆÒʺ¼ß’¨iéé¬Ý¼‹ÓÁáÄ\¹Š}½ºtôiÉ]ý°²´(´Üª·óé7?зs;f¼3²Àuž;™À0f¼3’¾Û±iÏ!Žœ<“o½îí½éá׆ūþfî«yðÎ|øê°r{¢ôâã®°kóÂC‚037§IóÖôºk–V¾ßq/ôàÛU‡*t_¢`>qŒàRôE”RhšÆ¯dþÊ=Å–ÏÌÈäRÔLÍÌ+ºª7E·e—š’ÄÊï¾È·ÜÔÔŒ—ßšFÿžÆÔÌ+ÌÌôC5--•KQ°¶¶½ÕÕ½)r|”]aÇÀcÏ	€j´œÇG;Ì™˜šaaa…©©i±ÛJ¼ϘawðÓæ`¬ç— €CƒÍZ¶eÆâe}¢‚
<ˈ	_p>"¸Ñþxïå§xcØ#…–551ÁÊÂó›IQ—¯p!êÉ)©lÚuˆÅ«þηޘç‡Òï
ñ	I\ˆºÄ•¸ëey[¢œÙû³&äúµ«¹–ÿ¶äKÞ™º„&^¶ï¬,½m+*O…@ÉI	Æà§¹W;~ö
:t»“‹agÙ³u½q=MÓð?°‹ag±­]—Ö¾]iبqÛÌÌÌàà.ýÂÓ©çݘ˜˜Hä…œ\›áæÞ’èˆó„ž=ÉÿÙ»ïøšÎ?€ãŸ›!SöŽ$"C†‰½µGj¥¥¨jµ¿ªYUT•ZE—YªÔniµ6Aìì%$‘ˆì}\nÜ&”Hž÷ëå%çÜç<ç¹÷œ{Îó=ϸ†&æèêtõÖv¸¶hOvf—OÓØÁ•&Žnò|.¢´´÷V‰½BZrm»öCS«¡<ÍÝ;D„\¥4%'C	R¬-LëèÓ{µ,\³=Cöo[Íù“òóšyô0
{§Œý`ú†&Ü>&2˜Ø¨P@‚Kó6XÚ8<›‚WC?µïÓ/×cja-_–()r•»Ù™Ø;·àNF*‘¡¾¼Ö$9Ù™DG‘•ŽªjÙ:âÚ¢½|Û¨pîd¦Ñ¤©;w2R¹N³–°°¶ãVL7®âÑþ5ŒÌÊ@Ü0TTTpiÑþ±Z°…çdz}OÌÙ*§;™²ó$3-#S\=ÑÓ7’·òø]8j5šyt¯³nâÄÊ-ÇIJˆeÖÄ>Ä^!Ôÿ4­T*ÅÇ/„èø$t´5ië¹ì~MrZ&Íl±27!&>‰¨›	´p¶ÇÜØ7‰Ž¿½µ%«¯3	Õ+ÈËá»Å“{÷ží{2öƒyäÜÍbÛ_Ìw_}ÄŠ-Lj
àîìšchbÀí[7H¼‰¹¶͈Ž".*‰D	çæm°´¶dÇ÷ªÏQZ¶íNˆŸÙYé´êØ«J™nF_ãft89ÙYhj5¤‰c3š4uG*•â{þR©Ïö=QQ•5„]"/'›¦®žèýGI¨ªN ßóÇå-?“g.ÃÞ©í]ilï
È‚¤ãƵ@ùvªª
xoÆ2z]%Ï’â"¾™3€Ý§o¢¦®Á©vñ×®õyëCÞùp>¾çóóšyXÛ6%#=™‚¼¼ÆLÅûÈ^²3Ó0b>YÀÚ¯¦’“…{«Î„øù ¾j+·žÀÒÚžˆà+Ì2€ªª””–Ò®¹®ûº¶?¶W’±9V¶ŽtêéÅù“R\TH^N6Á¾çøqé4<;ôbÞÊÕnûãÒiœ<´SÞ%JII™wÿ·ˆ~Ã&<Ë·ˆà§®hji£u¯¢Ø@MVqùmãRÂ.Ò¢M7‚}Ï"•JiÓ¹VÌ&èêTUPZZÈ–Ì]¾€½[¾ÅÿÒIœÜZꀚº^oMeïÖo©¨(GYY…YK¶Ð¦so¤R)«æOæÂ©?åOUTUyÖ
zôõ>
áqxÙÃ?0hädƼˆÀ˧Yô©ìxÝ?7<Ú÷àƒ9«X»hª|»Uó'°bË1ùºò²2îd¦‘s7iE*÷z%äå°àãáDGÉÓ«6Pãý™Ëy­ÿHÊÊJY>wú†&¬Ùq†c¶±cýZwz¶]úÖùçðª:yÉŸ›·Sh ªÊ¦Å311ÔÀÂÄ1ƒ*+ŸWC"™4ovÖ”–•Ÿ”ʇo
¡±¥3—¯§Oç6l[öy…ôo‘±ñ4¶4£¸¤´Ú}7PUAG»²—‚^Ãê{,ä1âñ»þÀ¶ª,ùt"c¿ÎÙ+A,Ùð“F`ñ'øvë^ö;˧ãG0{Ò(¾Ù¸“¿Ï\bâé"zB~O’•Èê§÷º¿5ù3MIlT(±×C	¸Èo–ÐÛëÞŸ¹€
+fpi×aëÐŒïŒ÷á=
u‰Ó¾¦Ïã¨(/“×Y[¶íNàoÌ­šT-ý6iÉ	
÷®A£ÞgüG_rø÷-_=ËŒ¯6Ò±Ç`Š
X4m$ålù+´®?®—RN‚pSö¥62µ?ÿ¶wë·Ü¸HG7VÿêÍÄi_SZZÂÆUsä'gMÝŽá½éKøæ{Øñ½¾Å'~àü©?åÍá÷•––ðÍÆðlß“¢Â|Ïàġߨ½z>	g÷zhë#›Ï…'sþÔAìøí÷JSë*]ZªsåÜNÚ‰¡‰þðgݾ+¨6hÀÖïȃïgE?ugñŒ1LÕ‰©£:±üsÅÀ6:2ˆqS2oÕohëè1|ü4~;~ƒ½gØx í†zøž?~¯…°RvV:×ìÅÉ­µ|<ÉÌÅ›èíõååe\ò>À™£û¸pêO,¬íØügßýæƒT
›¿KNvÖ3û„§sâ/Ù5|éúCì=›Àæ?ƒôædôôXüãyº¥þæÛm§°¶u’¯»Íä¡­˜?õ
JKKè5è-œ›·`Ï–•DGaçÔœ5ÛÏ0þãE”–³qÕr²3qiÞŽã?%3=™oæŒg׿™Zðñ¼ïŸíðŠ	¿q;kyð³qÏ!f._ÏÌåëùnû
écâ“ðpq`÷êùîÑ©J~?ï?Ldl<-Í8¼iïDrzfµû>}9N£¦Êÿ]
¯6ÝÚmûñ»Ž‹}cNÿºšof¼GIi)s¿ÝLJF]ÛÈêM—ƒ®p)èÊJJ\½·ŽD"¡“§[µùw¿~jfÙX¡Ç‘k‹ö¨ªÊƆ%ÅÇðZÿ7QRRæÂ©?)/+%3-‰° KhëèѾû.zÂûðŒÍ±ñ~Üs	e¶¬ý‚Ìôd…}ÞÉLeú¢
LšVýƒó?[Íï[ì=›ÀŠŸ¡¤¤Ì¡Ý(,È£ÿ°‰œøKö ØïÂqJŠ‹hÓ¹/
uõ«ÍOx´:mRSÓ  ?²ÒRy³Ýƒ®ÉÆuí3œÆö.Ø4qbצåäçÝ%.*«nBOªIS7ºö†¾¡)‡ölDYY…¡oÿ
ÔX»h*Ù™id¤%É»*5…¦ÍZѼMWü/”·LunÑïÃ0fÆ×4s´¥G{½Ö±ÆeíݲJþ·•­#ñx•ƒûçOEy¹ü)®’’2e¥\õU8¶uI?u«× ·h¨cȨ<¨{ßõ¾|¹´¸˜-kçuÍŸÌôŠ
òHKŠWèöÚ¹—ÍÛt%Øï‘¡¾Ø;·¤]·þ¨kjqìÀ6ùwÿþ9V\TÈ·¦ ¢¢BQa7"ðlß³îÞ¸Pk\[¶ãÒ™¿™7õ
욺ãÙ¡'_ˆ²Š*ÖM*ûú7¶w‘ºOßЯ1²{ƉC;8yh'.-ÚÓ­Ïpƒd“ótï;;g¬lÙ½y9…yÄݧyë.?`¿s„^D"‘0}ÑF´uôžéûÕhÜ›à )5ƒÒ²rTU”¹ÉYß rò
hálÏÇcß§×ÖÔà‡/þ‡ê½1?Á‘Ñ
ùÝŸÜ O—6xº:âéêÈòÍ»ÉÊΩ²oKútn+_nliV%
È€a½»âjß;–mÚÅ»¹„^¥G{Oôjǵè›ÜNMgDßî8éCpd™Ù9¸Ø7ÆH_÷)>©W“ZYO‚‚ü\¤R©¼õ¦¸¨²ò2¨©c`d†g‡øž?Žÿ¥Ó$ÅG#­¨ {ߨª6àÚ½ïyYk}ÈÆ1çq=̶ûÈ÷9vÊ<<ÚËÆV÷€ÿî~Xò	7®ÊZ›¥H¥RÒSiÕ¡'¦6„øùšt‹§þ Gÿ‘uô	½üê4jlïȺ„ø“ø))ɨVÜ;á¤H©¨(Wx­ºôee%¨¡Áí[1Õîÿþ˜û7³†ºúò™{î·üÜïÎpßýþâ÷ÝK+*°"výZÐÂb‡N_Â74’U[ö²óÐ)lBIIÆ~ZŸ~¹3ËÆ™b`lþØ3|I$²FLm]ldÝ*ïÿo`\ýM§¶‰à§î
|ó}¬l«}ÍÞ¹¥üïÒ’bÖ,ú»Ù™Œ™ü–6l\9‡¬ŒÊï]cîû÷õá~°,AvîUHe׆û瘎®A•sLWϨVÞŸP÷ú}—F6Ž\ô>Äõ0?vmZÆ™£ûk2=yO‚‚‚\ŽþñçOþI·>ÃQº7c\y¹ì¾%•J‘Þ;w”îMÀP\T(ïz-•JIˆ»Ž“[ëZB%ûÆÜÍËç¼ÝÛ¶dó×3ÙwôS­­&½<ø©Žòýã\Vyœ+Ê+ªMëlgüÆþg•ïÕgÊî;€x¯ú·ŒÔ$Ö~õjjêŒy.ºÆ|»`
åe¥”——!QR¢ïÐñüòýBþÚ½ÿK§046§eÛîµòy¼Šê´œgû¸4oÀ·¦°yõ<‚®žáè¿°töÛòA¤gŽî#2ä*wþDaAêZØ:VmÖUSן¨§íâÄ_¿êïSgïAƒ¬HàÄ_tj³èãÙ·v!Fúº¤ges'GÌæRÛ»âàÒC‹'šÞØÍSÖ
—•N—ׇ2nêú
}í†z4²©¾Â\›Dðóüݯ€$ÞºAvV:ZZ:ô C˜•‘òTùß¿FådgÒ½ïÆM]@¯wP×ÐÄʶéSå-ÔŽ“‡vðÇöïäÿòóîVIsõÜQôM˜ôéR®Ù‹¦VC2RoSX‡¶Žºú²`ÖûÈ^Â.*t.,È#ðòiNý³¿²nÑ
ïµà¸yv–mwx‘¡¾ØþE…hj5ÄöÞ½jݲ$'ÆÑkдuôؼúsâc#ëô3yÕõhïI»æ²‡°S®æóÕ›ññ!8²ú¦J’GW‡<›Éî'‡Ï]ÅÇ/„;HvnÞS•ñ~×µßãJpëvýÉݼ|45ÔqoÚ€®­eÝàþ:}m­°45柳²–ÕŒg‡ò‡ôßÌÇ‘?¶²wË*6®š@»®ýäÝ<;ô@ßД+gs3úNn­±¾wí¿Ⱦ“A×ÞÃ7u}ߦ¶+NÆôà½êß®‡ùR^VЉ¹]z%#å6åeŠcÌzšºG~ßBiI1ÝûOÙ/<¹:m’()1sñ&Ö-ŸÉUŸ£ü³oÿìÛ€ƒ‹,öÎ'D] øêY>{ šÚ|0ç[tôÈHMª’o÷¾#Øöã"¶~¿@v“qhÆõ0¿:yšpðÔþ8~NaÍQý{`¨'fðyž<;ôbð¨)Ú³‘Yû(Rï=äí:Ý·~^<¶ÍpmÑžð KŒëUL-¬IMНqž{yx‘ãngúø^òsLµCßþ_-–^¨©¿voPXîØcp•4ÞGöpÕç˜Â5¼ÿð‰hhjУÿ(îü‰+e ý>•cÓ’ä(4qtã­És>n7®êžÏ&@S«!Î]¶Ž'íÄçÄì[0yæ2<ڽƲ¹ï²ò‹I¬øù¸ø=¡:¢¤$aÓâ™Ì\¾Ž£>WÙ¼ï6ï“Í>«ª¢Œ‡Ë“Í:ΫNør=–a/ÀÎÚS#’R3j\ÆÆ¾_Øu¼¯2hŠì|ÒÒPgåì)Èì.­eNE…”v-duGfì=⊲2í[¸Ôxÿ¯2eef.ÞÌw‹?æz˜Ÿü{Ю[¦ÌZ©öµþ#ùýWYËa¯A•3üµé܇o¾Çßû63sBïÊûƒjú?ÁDLmºôÅÄÜŠ›Ñ×óº#NîmP×Ф¨°@žFK[—n}†sì௼&&áy*@*•Jk܇ëà¥4é§’Mx;>†‚ü\LÍ­iêÖJ¡;[tDllm]]=äàKKK¸ê‡DI¢0•mFjQá~8¹·¡¤¸ˆŒÔ$ŒÍ,1µ°!3=™ä„8tõ°²u¤° ˜ÈTTUåÝÂ/"•BÓfž¨6P#2Ô—²ÒRì[ ®¡‰zFEñ~êëÐÔÖš‚Â"‚¯Çp;5uµx¸8baò߃ôë;“öCžªߣÎò²R"Bd³p9¸´¬¶"™Fâ­hêêccç|olX8êšØ;WN¬‘šOÜ0
ósÑ72ÅÉ­u•~üµI?2Ïóü¸N^Î]¬låOïï‹õ%';×–íH½}‹‚ü<¬›4EGÏ[1äÞ½C#{ôMHOI$5)}Cc,mÈËÍææk¨©kÈÔ$'Æq3:œ¢ÂŒÌprk-*¯ÿaH{“gr~ü[Ófžde¤žr#SÌ,SXGtd0™iIhhjãè꾡âÏäçÝåfô5¤R\[¶'+#…ä„8ùë*ªª™abnUeŸ7®’”ƒvC]\<ÑÑ3¯/.*¤QcôŒÙÔØ%ÅÅòsòUõ´çGÚ¥ƒUÿ¸që61ñ·ÉÉËÇÔÐ÷¦MÐוýÄEfv×cãÑi¨E3‡Ê)ÓS3ïsë6z:85‘u—-.)åœo0åthÙŒ¸Ädò
qjbž±	ɤ¤gb¤¯‹£mÕsävj:·n§bb¤½uåøÔ ÈhbnɦÁnéâPeLÏ¥ p¤R7ÂØ@OžºZ<\ë¾§ÃóR—÷—û¤R)q7ÂHŠEEU«ÆÕþŒÆýûTÖ”št‹¸áæçb`dFS·Ö¨kh"•J	”u³unÞeeY»Ãƒ×¯ûSîWT”âçƒT*¥YËÄDSVV†½ssy}&ðŠ7‹¦Äµe…É[^EOsýH$ÒgÕ'¢r[éY\€êq~Tç‡ð(u	õß³
€„úIÜ_*••–²cý×ø]8ARb,K×¢i³VÏ»XÏÕÓ@uÚ®>•[áQÄù!‚ ³$ûå›1ôí_ùà§6ˆè¢r+<Š8?AAxÖ¨©óÃîÏ»/1}Ä=¢r+<Š8?AA^"BTn…Gç‡ ‚ ÂËã•€DåVxq~‚ ‚ ¼\žz8AAA„ú@"‘H_ù AAA^"AAá•! AAA^"AAá•! AAA^"AAá•! AAA^*O›D"‘ÖFA„ÓÓþF”8?^nâüEœ£ˆóCxq~ò´çÇS@.¦ÖF6µÎ«ƒé[¶ÚR—ïÑ«ƒi­ä󲃚°"+žw1žŠi¯ZÉ'õâZÉGx±ˆóCx”Ú:?¤ÒšÕq%I½:·L;xÕ«ò‚¬ÌOs|jƒ¨¼œj£~*ºÀ	Â3ö2?‚ ‚ õ•€áÁ ‚ Âó% AxFDð#‚ ‚ðü‰Hžü‚ ‚ð"¨¨(çØÁ_Ÿw1ž+	BÁ ‚ /ŠýÛÖ²kÓ2Rnß|ÞEynD$uH?‚ ‚ ¼(òr³9stwïd°aÅìç]œçF@‚PGDð#‚ /®“‡v’óØé¥R)åe¥]®6¬˜ErB,Qáþø_<ñœKô|ˆHx¡]õ9ZíúèÈ ²2Rêl¿É	±$ÄEp=Ì»w2{ÛÀ˧‰8ù3êY¡òueåå$$§)ü&BlB2Ož' <ªö
þñ
䫟j¿rE…¯ç=u>‹ÚN\br¶MLI'>©ò÷)6íû‡¿N_|ê2=èvj:|¹€óþ¡lýãH­æÿ¼ÝÍËçb`x·/.)ÅûJà#ÓDÄÜâàÉó„\UX;5â’ÊJOaQ1OžçŸ3—k\Až½`ß³de<þoݾÍ—Ÿ¼)_Ž¹Ê·¦ÔEÑê„T*ÅÁÙƒ¾CßEGÏ€#'Sßjª÷D$¼ÐŽÜþÐõÑÁ5Ê3øêYþÞ»é‘i¯xsùì?\>MFê퇦ýmãRb£*ƒßŽZA¢|9ìFÍM`âç+èüÖǤddÉ_»s‹-¿¿\ÓÚRPXLjÆZÏWŠ”¸Ä§ž“Ó3	Œ¸m#ómäÜžº _îÙÞƒVÍš>U™þ­¤´ŒÄ”tÚµpa÷ß§*íõ]bJ:k·í¯ñöÙ¹y,þ©úëË}NžçB@˜|¹ °ˆ!ÌcìÌ%¸|—“—üÒÏX¾®Æåê—óþ¡äpÔçêCÓ—”rðäyž<¯°>¿°Há^p%8‚ƒ'Ï“œVû¾çiË]Q!¥´´Laݳ(w]ʹ›É¾_VsîøTT”Ë×çÞ½Ã_»7pÑûåå²÷|Õçi)‰ü±ý;‚®žáªÏQoÝàíßq-Xö$';“?w­ãʹ#òü2Ó“¹|ö0—ÏüÃöu_ÄÝ;üþëZÎÿã‘­HG~ß"xzä­òq;Á¾çHNŒ{¢÷*‘H4ê}ú{—†:Œœ0“V{=Q/	ÏMrbù¹¤§$’ž"u-kW IDAT
òä_ðÑï)öOõ­öŸšODðòr³«¼DNvåæz¸?×Ãüˆ
÷¬–n}†cnÕ€²ÒRƒ.áwáÙYéäåfæ/ûîyYª”ѯK[Œ
ô˜¿v+K¦M䨖ôëÚŽ¶ÿ@+s:´t}œê•URZʼ՛úÑ|~?vN¾>-3›Õ¿ìcôô¯Øyè¤ü†¼óïSýh>oM_ÌŽ¿*›õsò
øvë^¼>œÇ¾#göáÉÇ‹¿ã£¯¾Ãÿ^k\jææ·•o·î¥ï¤Ùä*lóëÁã}½‹|ùVR*¿ÿ…·g/åï3—¨¨Ý¬æ¬ÜÈîNóÆÔù,Y¿ƒÌìîæåóÛ¡“ì;z†wf/% <
¿°ëDÆÆqç.k~ÙÏÈi‹øí¯ò å»í°ïèÆÌXÌ”…«¹qK”'§g2ñóù`_¬ÝRm«”в2]Z»óç©óU^«ï’Ò2¹&ÿÌïËÍ/À74²Ê/Ñß¹›‹_ÅÅ%òuÁ‘1UZgïæåÐÒÙ÷¦²ïÿo‡NbajÈ©mß²{õ|>[µ	©TІºCzvBYIÜR_+6ï–? ™±ìáoN^~•–ì‡Nâá5‰aÍç½/VR^Q!í÷ãç¸Tó–Íÿò4åñÉBZžÀ >¯’¾®Ë]—þØþ=V¹Á²ÏÞ +#…9“ûchlFÒ­æ}è@£ÆMÑÖÑ£™GG,¬ìhd눮ž!Í<:bjnMjÒ->ÿ`0&æVÄF…Ê[‹ÒSùqÉ4nFaëàÊòÏÞeÛ_biã€ÿÅü³ËCËwÅç(ñ±‘ädgòÛú¥œ;.«Cìþy
ÔÔëøÓyy‰«µðÜøÿƒsÇ~`Ê٬[>€sÇÿàâé¿øjúhJKŠ™;eÞ‡÷°eíD†V>¹úmãRÖ-›ÎEïC,™õ6;7~Ⱥ±-üd—¼ÿfÊYüíG⮓™žLXÀ…ÇêF·kÓ2b"ƒ©¨(gö¤¾„\$>6‚KÞ‡ÈÍÎ"';“¸a$Ƭì¦Â¶å\

£s+wºµiñTÝv^5'.úãõz6,šÎ·[÷Ê+¥.ZCK~ùf±ñò–ukݬ){×.dÃWÓ9ês•ðè›|»u/R)ìX9°•rRZ&óÖüÌœ÷ÞbîûoñÙª—PXȚ½‡°³²à¯uKÐTW¼É„^Á¡q#ùò{_¬äµv-YýÙ‡lýý—‚d­WC"¸ƶes01ÔgÁw[ÑÕÖâ­=Þ§Û–}†‡«#±	É$¥É‚ñßmEOG›Í_Ïäjh$;ÿ>	@ø8ž8ÏšÏ?¢o—¶¬Ø¼
55O›ÀÁŸÓ­M–mÚUígéи×¢o=í!y¡Ä%&óÉ’ï9âs•¾gÉŸ¨oùýcf|Íá³Wè3a–ýæ'y>kÝÏÅ@Ù1+¯¨àÍi_R^V^e>þ¡tòtG"‘ÐÒÅìœ\n§>~÷XáÅÏ™«AÜk%YkÍ•àˆ*Avu²²s8qÁ‹a””Ê^èjk1¤g'@˜µn;Þ¿®Áû×5¤fÜáä?Ú6wÆñëÊ‹Tn€5s?b×êùU¶}šr¿:õB»ný;åsâ®ST˜¯Ï1ºöFǃ6îJŠ‹ÈLKª	ššÚ8ºzbbn…E£&4Ô5ÀÑÕC®œ;J£ißm£&Î";3œìLŒÍ1|Ü4:õ‚±™í{Ò®k?šBˆŸÏCË×¼U‚}ÏâwžoN"<ð"E…ùäå`h\³^‚€„çȽUgBü|(/+%??‡¢Â|ÊJK	ñóÁ½U…´aidãÀ”Ù+ù|ÅJŠeùŠŠrΟ8È‚Õ{˜ðÉbÜ<+/Ö§ì¡×8Æ~0‹7qþÔŸTT”Ó¦sìšóÆØ±uhöØå½“‘
	}ÞÏc?¦ïÐw1·j‚¥Ã^ïÌc{ ¡®¦°MvN
551ÐÓÀÞÚ’Ì윚~d¯œŽ-]ñtuÄH_—6îN\
‰$ãÎ]¢o%r=–õ»Q^^Ο÷º“©ªª°nçŸ|òõ$§eÊ+g®1aX?´4ÔÿF_yþ‡N_ÀX_—ýÇβïèY¨ªrêR&FîÙ	Ue””$
åŠML¦‘™1 ¢¤R)]Z7ÇPO‡Ñzrä\e€>vðë4ÔÒä!½¹þÈÊHYy9—ƒ¯1fp/´55˜0¬?GÈkôÀžéëÒ¯k[®†D £­Åå kÌ\¾ž_ã¬oõ]C™™p+éñûº×©™wرâs¾úß»ôíÒ–#ç®°eÿ?ìúöL}‡·õäàIYåbÛcl]:›ù¾Ã›ýºËóçÕ‡mpê¢?ž®Žòïìƒ2ïÜŶ‘™|ÙÎÚ’Œì»uù…g$%#‹ASæ²iïßø‡EñçiÙ5eãžCLY¸š3Wƒúñ|b>îÏ?<ŠÑ3›Ó—«}๑&†¨ªªÐ¾¥+Ÿ¢åäY•ÀÂİÆå|‘™˜[ÉÿÖ34!#5‰¤„…õ¦æÖ$=Fw³oglf%ïÑbji#_¯ÕP³{ËZ
uÉÏÍ~hž÷ëJ!~gi×µ?å\:…kËý…ªTžw„W—ƒ«Ñ‘AD„øÒÔÕ‰’¡W‰‹
Å®©»BÚ¨0ì[È—íœd§$ÞÄÐÄɽ®'¶ö®ò]‚¯ž%úZÞ‡÷ §oLfZÍ­šXðúà±,øx(ºÆŒœ0'·ÖhR€1i€q•mji[P@Aašê$¥gÐP[³ÆexÕèé4”ÿ­Ö %%¥”””¢©®NGÊàUKCÖBóÑWk™óÞhÆíË–ßs箬‹eyEJ÷ÎeåÊç>E%¥4¶4“çÕÑ£Öæ&ä=òfojd@zÖ]Ì
)//—ç
 ¢¬Dyyeë¼[”D‚)ÒŠg'w¿–D"‘çUö@^ú:Ú÷Ö+Ë×9w…Ó—˜=iº:4í3¶J·/€ô¬lLõºïúÈÙΆªª4w¶cï‘3ôíÒ4ﮎÌ_»•òŠ
n§e`n,;®-œìåùtnåÎçk~&3;‡í`ʨÁÕîOG[Sa\ZrZ&:ZZuõö„gèà‰óôíÒVáØ—WT°ïèŽl^ŽŠ²2G}®²ÿØfMUm~¡‘tiåΤPQVdß»efç(Œt°iôŸ“q¼å~™]ñ¦C÷äåf“•N£Æx´ïÉéûéÖg8ÅE…ÄF…àìÖ†‚ü\

òäÛêꑟ_ùPÓ³}O|Ï¥C÷ä‘x+
{çD…Ô¸|M݈àîtlìœqiÑž=[V1jÒ“Oa-•J9´{)I·ÈÍÉb÷Ï+°wjñJŽ-@Âs£¬¬‚ugîü‰æ­»Ð¼uþܹ;gy@sŸ½K…‰âîýmnÕ„¬ŒùŨk•7N¼Ö$Ÿ-ÛÆg˶1oÕo›5BM]“ÒÒj¢÷·Yý«7#ßÁÖïæcEFêò.ÿÖ@U;kK®ÇɦˊÞqö-ÈX˜ajdÀœ\Z:;àæh‹ªŠ
eåå¤dÜ¡™cróøódå$ÝÚ´à—G).)eÛcòõÃzwájh$¦†úxº:bn,{*û_\ílˆKHÀÊÜ„ŠŠ
®G›ÇοOÑ·K[yÚí ¨¸„=‡OÓ®¹*ÊÊ4±² 6>I!¸PUQ¦µ›{{ST\–ß(äUèøÛØYY`flÈŽ¿NPò¯Ê÷Å%$ãØØªÚ×ê«ëq	òñ_¡Qqxº:bfd@Ii)…EÅGDÓÚ½)ÊJJ˜šyç^úÊ™Ý$	£ú¿Æê­{‰OJ}èØ¼f¶\‹‘=ϸs—¢b™Õå[ž¿Ú¸;+¬¿q“äô,&Ì]Î;³—²óïS=üÞñf¿×ÈÍ/ ãÈ©ÌZ±^þæA
µ4&?HJÏ@ç)Š=«r¿Ìòss˜ó^æOʘ÷çЬe{´´t˜÷Áæ4”á㦡¢ªŠŽží]ùbª‡ölÄÂÚ--¾˜êÅ©¿wÒ¢MW$%æ}èÅÂÿ`Ô¤Ù())?Uù$JJØ95Ǽ‘-‰„æ­»p3ŠfOÞ$‘H¸À‘ß·“Å¡ÝHþ{»—‘hªÇ^†ß˜qoÕ™íë¾bæâMH$–ÌËø¿¬’ÎÍ£»7¯`óêyd¦'qÿ+‘H0|"ŸOŒ±™%9ÙY™ZÐcÀ(~X:بPŒÌˆõeæâM8»·á—ï°cýÚwëSóÇ*kdÈUÎÛ31ÁtôlŽ	tjåÆš_öãɇ£‡TÙnÖÄQŒÿl^½:³ÿض¯¨:€T¨JWGKaœM+sõeÝ’¾ÿâ#vüu’Õ[÷Ñ@U…‰Ã`gmÁgïæ­é‹ÑÖÔ`ôÀž¨ÜkíùtÜp~øíƒ¦Ìeô€žò×,M™ÿá;|½~·n§`jdÀÒé“PWk€›c“‡–m䀬ùe?ƒïõÿqÁ46î9D\b2#úv££§›<­‹
^S¿ ¥³=?@‡–®\	‰`Ô§_1ãÝ7±¶0ÁHO€…ãç}ÿ0â“…ìÞ·ö ©­:
+[Z»90z@æ­ù™þïÍa`÷ôïÚ Ê{8yÉŸ_¾™Sƒ#ñâÒ×iÈ;s–âjߘӗÙ¾\Vyy{poFÏXL[wgN\ðã§…Ÿ0vP/ÆÍù†nmZp9øšB^£ú÷À}ÐæMóÐýÚ“?#¿ ï+|üöPy”P¿µoéŠØu<]åëœílÐk¨Åæ¯g¡ªòß•X=m–N²òr¾úéWööfhoÅîÜÎv6DÇߦ¼¢e%%B£âèÒÊý!9¾8å~YM_´€¢Â|TT rïû¬¬¢Ê{3¾¡¨055
…‡²Sç®VÈcæ×›–§Ì^)ÛN]SÞšïäÖ'·Öò4s—WÎ@ijaÃÒ
?²œ³—TN’àÒ¼œ¯yo–É3—s=”ä„X]=ñìðêµþ€€ê­—åG6_ëÿ&ÎîmPS×`ÉúC˜[ÙÊ_Ÿ½t+
ÔÔY¾ù(—Ocdj‰ŠŠ*
u
è7l]û#?7ïÃ{äýo-¬íX²î/®_æNF*ã>Z@C]}Öî8KÜ0ŒÍ¨NÛ®ý¨¸÷t~Äøéè™Ð@Mƒ²²2ÒS™<Ú‹®¶²å°Þ]éÞ¶%×ãP½wñ¼––¦j
TôZšÚ6""&žIÃûca*{bœWPøJu3xR-œìº)½÷æ@ùß–¦ÆÌž4ŠÙ“»t¼ñzÞx½ê[_·!_|ð¶|ù¯Þò¿;yºÑé€å¾%ŸNzhÙš9ØRV^NbJ:ÌŒq°±dŬ÷«M;¤g'& °NC]¹“ß’/·m^ù×ÌȀϧŒ­’ϧãG(,o]*ëþ`l Ç†EÓ«¤776äëi¸NKŒôuúžê3v¬˜‹¦†:‘±ñÌš4Z^Ù{Ô †õéJpd3&¼)ïÖ3z`Oº·kIPD4“GäÖíÊ1QÚš˜éóf¿×ö“u7—ìœ<ôt´±01äÄÖ•œ÷eXï®´tqd]Ž’R3ªíz(ÔoôêÌøÏ–qóv
6¦˜êãÕ«3£ôäíY_Ó­M²só1ÔÓaâðþÕæñÝö?(.)Á@W‡ËA×X1«êïÃX˜âÕ³3ý&ÍÆ¹‰
×¢oòíœ^ørü°ãAÑÄ'¥2sùzÞx½3í[¼3™ªkTß•õaëkšß£$ÄEq+FñÁŒ¾‘)®-Úר£ÝPn}†sxÿÏLž¹¬Vó®ODT½,ÁȾˆ.-åËŽóž˜H$<Ú÷¨’DZÛÈËÍ&#-‰˜È`æ­üMáu—æíªl£Ú@
GWOŠ‹
ÉÎTüí•
fViÔØAþw3Õ~þ†z:òn3ÃûveÏaoÆ¿Ñ;kšÚZÓÔÖZa›°qGÆÐ½mK„úgãWÓ‘HÝ‹ØÚÂT^ù~žZ5s¤»Óó.F­ÒÔP—w'mlYõA†‘¾.=Ú{TYonl(¤ç$SåŦ½3²ßkèÝgÐÆÝ‰3W‚¸Fÿn²ëˆ¡žƒ{tTÈ37¿€
{1z@Õë“P?X˜qlË
.†“™}—.­e=>|koyÓ—p¶³¡ms¾þt¢|œàý‡£ôÀ74’Ò²rv­ú=Ò³²))+#!9
+s–NŸÄÅÀ0îææ³xÚ´5e3îÜ%'¯à…-w×6ÍñpqàÝ¡²‰dîeªI¹…çkØ;ÿCWß3ËÆÏ»(ÏJ¥Ò÷”H$Ò_Ì™…¼:˜ò¢–­¦þ]ù6íàUgïÑ«ƒ)OsnÀ³9?
òr¸q--]ìZü÷ö=Ç%ïC
ë5vdÀˆêŸþ¿LÁçÓ2íàU+çGêŵU$áRŸÎÈØxîääÒÆÝYü–Ï3R[çGM[Ý$	ÏâÜÊ+(ä›;XüÉ„G¦Ýyè$×bn1¬wWZ8Û+¼fÚÁë™”÷¾Ú(÷½c\£ýK$’zQÿž§­ŸJ$©hªGDå»zšÚ:4oÓµFÛÞŸ|áqˆÏ_^NNM¬ÿ;‘ Ô€¶¦Æ÷¾7ÞïEP_Ë-K<êª'DåûùŸ¿ ‚ ÂËA@õ€¨|?_âóAAxyˆ.p/¸—¡òíÕÁôyAx™vðzÞE^`âüAj›€^`/Cð¼tQÔ–ú~|k«b*ΗSm=ø“d¼œD`+¼èÄÃÛºõ¼ïý/mTŸ+–Pÿ+Ç£‰ã+‚ /¶ç]IžEýäExòRŽªï•Ëú^~áÑÄñAáEó*ÕO^º¨¾¼ú^~áÑÄñAáEóªÕO^ª¨¾¼ú^~áÑÄñAáEó*ÖO^š1@õýàÕ÷ò&ޝ ‹åE‡ð$ê[y…úáU­Ÿ¼P}?xõ½ü£‰ã+‚ðâ©O3švðªWå°Õ¯rý¤Þw«ï¯¾—_x4q|AAxѼêõ“zÕ÷ƒWßË/<š8¾‚ ‚ ¼hDýä9@iÉ	ÜŒ¯öµ«>G;ŸguðÊËJñ¿t²Ú×®_&/'»Fù¾è'Ÿ&Ï»ÀÃωèÈ ²2Rêl¿É	±$ÄEp=Ì»w2{ÛÀ˧‰8ù3êY¡
ëKJK–c’9xò<áQO_`AxÅÜÍËçb`õ÷’ÇQ\RŠ÷•ÀG¦‰ˆ¹ÅÁ“ç	¹«°¾´¬œŠ
©|¹°¨˜ƒ'ÏóÏ™Ë5. ³ð¢×?Ÿ•g%ÞŒ""øJµ¯­[>ó±ò¨îàÍY¹‘Ä”ô§.ß¿•––pæÈÞj_;øÛ$'Æ=qžV$àrû}ÚâÕ	%*päŨ”?¸ý¡ë£#‚k”gðÕ³ü½wÓ#Ó^ñæòÙ¸|šŒÔÛMûÛÆ¥ÄFU;;¾ÿµ‚DùòòÍ»h5t26Ýß$¿°HaÛk1·Øòû‘š¼
Ax¥%¦¤³vÛþoŸ›Ç⟪¿¾Üwàäy.„É—}üBè8r*M{áÀ	Ÿ*ég,_Wãò‚ Ô5üTªÕ¨¢¢\¡u':"ˆ’bY…/åöM
ò°wnI«N½åi
òö=GaAžB^R©”ð K¤%'œGA~. ;x†…7¸!—qç.þá×ñ»Žÿ>MO¼yƒÒ’b’ãä­
y9Ù¤§Þ¦š:CßþŸb¹/’‘šTm^QáäÞ½CzJ¢Â6‘¡¾äçbEfe7ñ«,ovn^•¼ž§&Ä>“ kzJ¢ü3+,È#åöMF¿7[a›ÈPßjƒÎÔ¤x"‚¯—›]åõèˆ r²³äË×Ãý¹æGT¸ÿcµìtë3s«&”•–t	¿'ÈÎJ'/7›¨0Ù¿pÌËâP¥Œ~]Úbl À;^}¸²oº
µòmbeN‡–®ÿ¹A.)-“‹a
-2¹ùø†F"•*®¿s7¿Š‹Käë‚#cÒÅ&$s7/€–Îö¸7•}ÿÝì8±u%ƒ{tTÈSC]!=;¡¬T¯{•‚ðÁ¢Z½Z+))óÍœñ’‘šÄìIýäÝÇV}1™Ò’ü/äÈï[ð¿t’yzâ{ŽUóß—ç“—›Í¬‰}¸äý7›WÏåëcˆ½Š	Û·žÉ¾å¬oC?žOtümR2²¸››O@Ä
…§uãŸý›ñ¿tJVÆù“Ù±îkÙú}›	õó¡° •óÞ ';“Ù“úréÌa~\:Ä›7äù¬[6ƒíë¾âôáÝ,ž1šC{6²®Z_~2‚€K§X¿p<çÿù…ÒÒRn&&sóv
ÂÈÊέá'^ûLHÄ´g²/ŸãpîØïlX1[Þxîø\<ý_M
@iI1s§Âûð¶¬ý‚ÈЫò|~Û¸”u˦sÑûKf½ÍÎß²nl?Á%ï¿Ù°bû€„¸ëd¦'pᱺÑíÚ´Œ˜È`**Ê™=©/á‰à’÷!r³³ÈÉÎ$îF	‡1+»Ye{SC}Q1„:—˜Ì'K¾çˆÏUúNœEJ†ìAÇ–ß0fÆ×>{…>fqã–¬wßÑ3ŒšþÞWùô›Ÿäù¬ýu?e÷ŽòŠ
Þœö%åeåUö§«­…¦†ú3xg‚ µG?UÕú4Ø.ÍÛr…¬ôº÷AˆŸn¨V £g öä¡L™½{§ûžcÍ¢¼ì›g'ÞþàÆtÄ4,+¤ì9|šÃ›–£ª¢Ì‰~ì;z†ÏÞ{K3cÆzË'*¯{«Î„øùàÒ¼ºúF$Þ’5!~>L[¨ØÁÿâIZ¶íΨI³)//clï¦äåêËÚ².më–ÍosìÀ6FMšMÏf¦µeèGóÕ¿[¹SRVÆGcÞx¢òÖ%M
hBì'¬%î­:ó×î
ôôùù9H$ÊJK	ñóÁë­Ò†^¤‘Sf¯àýa­Y«ãùùiÏ%$JJìÚ¼œ¤øNÙC¯q´ëÚ©TÊÌ	½4ê}ÚtîÃk¼1öã'*ïŒTHèóÆx…sÙÒÆa¯wfpKã§ù8AxB©™w8¿ë{¨ª²æ—ý9w…ñoôeËþ8¾e%šêüzðOú0sÂH¶8ÆÖ¥³176dßÑ3¬ß%{Ð2ΫÛ§£‡§.úãéꈞÎs~w‚ OO?Õ«õÈí^@‘™žÌˆqÓønñÿñ÷ÁݳS•´1AØ:4ÀÎɽr}d0MšV.;ÛÛaB±$¥e2ñóåò×72{ºòztbצe„µl›g'nE_#&2˜¼œ;™ZŸwWžöz¸?íz ¬¬"/{\t8ÖMšV–ÉÁ•äY |õ,%Ù‰œAÖPOG›¼‚§*s]¸?îG‰Šg¶OW¢#ƒˆñ¥©«'%%"B¯ŠÝÇ *Ì{çòe;'Ùß)‰714±@r¯…ÅÖÞU_=Kôµ ¼ï@Oߘ̴ä—×ÐÄ‚×eÁÇCÑ50fä„™8¹µF“ŒID$Ï’³

TUhîlÇÞ#gèÛ¥-***ò–WGæ¯ÝJyE·Ó2076 …“½<ŸÎ­Üù|ÍÏdfç°ý¯L5øÙ¿A„Z&‚Ÿ‡«õȽUgþÞ»	,m046çô?{è?ìÝ*imݸAG7â¢*»®¹´hGˆßyº¼þ–·ˆˆúÑÔÖ
}m6/ž‰ªªbѵÔÕ(.)áIiëèÑ@MƒÓ‡÷òÖä9èê±kó
\Z¶¯’ÖÁ¹qQ¡´éÜ[6Þ)æöN-¸}MžîFxÚ:²ñm=[òn¿6tôpSÈKSC¼ÌšÍ WžÕ¸Ÿ)+«`ÝÄ™ƒ;¢ÿ°w‘()ñçÎuØØ9Ëšûì]Zà{þ¸|9.*”®½‡anÕ„¬ŒÊËËPVV!êZ€<›G';¸Ò¹—â±Åݧ´ôÉÏ€ÞCÞ¦÷·‰¹ÊÖïæ³sÓFŒÔ+ªÌð&BÝ»—@iiªª*„FÅáéꈙ‘%¥¥£¡®FpD4­Ý›¢¬¤„™‘©™w05Ô'4ª²µ["‘0ªÿk¬Þº—ø¤T16O„zO?V롱9ÅE…غɺ(5oÝ…Ík>gÆW«¤í1`?.ý”Ö^'*¼²âêÞªÇnç§/ÆŸ•„¹±!
¨¢¢¬Ì[ƒz1vÖº·mAvn>º
™4b[¹óõºíx¸:2sÂÈ'*³{«Îœú{í]ÑÕ7⻯>bÖ’Ÿ«¤óìЋEÓFR\\H|L$ZÚ².jêtê1„yA[Gü¼»è™aEzufá¿Ð¡e(º
µˆŠK`õÜ©´káÊ»s—QRVÊð>ݰ·~²®{"Ü IDATµéYŽûù7÷VÙ¾î+f.Þ„D"aÉ̱ŒÿøË*éÜ<:±{ó
6¯žGfzH$€¬â2`øD>Ÿ2c3Kr²³02•}–=Œâ‡¥Óˆ
ÅÀÈŒÈP_f.Þ„³{~ù~;Ö/¡}·þØ95¬²F†\å̱ýØØ9LGÏæX‘@§Vn¬ùe?¾¡‘|8zH•íŽù\åä¥

‹˜·æg<\;øõ§øÔAÐ×iÈ;s–âjߘӗÙ¾|.oîÍè‹iëî̉~ü´ðÆêŸ9ßЭM._SÈkTÿ¸šÀ¼)cº¿Ä”tÖþú;¾¡‘$¤¤ãÉ73Þ«»7(¼°Îû‡ÒÒÅ-
uŽú\¥Oç6Õ¦+.)åÈ9ÙÌ·CzÊz”WTœ†¥‰‘ÂÃÜ+Á$§gâéꈕ¹ÉWîÒ²rSÒ01ÔGë±pϢܓÁÏ«õ`Ö’ŸÑÒj@ç^^4iꎺ†&-ÛvÇ¥y[Út[®]bà›ïÉ'P×Ðdêo(M¸Š‰¡}&̤yS;¦ŒÌØÁ¯sêRNMlh×€É#1¸g'bž¼‹Ó ‘ïÓ¹—‰#3–®?„­£Û½²hñ¿ù? g`ÌŠ-Çð¿xŠ^ߢ´´c³F¼9a}ÞGYY)û~YMwÙ	èêÈÁ¿ÆÇ/„Ü‚†¾ÞKŽm^NØ›ê>¿¾æÏzÜÏ¿½ÖÿMœÝÛ ¦®À’õ‡0·²•¿>{éV¨©³|óQ/ŸÆÈÔUêÊÆáô6®}†‘Ÿ›ƒ÷á=˜˜[`amÇ’uq-ø2w2R÷ÑBêê³vÇYân„a`\}ʶ]ûQQ.=bütôLh ¦AYY驉LíEW[Y×›a½»Ò½mK®Ç% z¯;Îí´´45Pk Š³

µ4ñºw1Ô—"BÒ³^œVÀQZrù94¶¯ÙùìÌ4ÒSoãàÒ²–K&_ýô+_|ð6×¢o2qÞ
l,LIJˤo—¶Ìyo´<ýïÇÏQRZZgDMËWPHïwgÒÄÚ‚¤Ô7bý—Ÿ>Q¹Ÿåñ©NiI1Y©˜ZX+¬OMŠÇÀÈÕjuºÿgåE~´ŒªN0ÏþøÔIdm[9FS[G¡ò¡g 8NBGÏ€vÝúдY+@vð¾]2‡Æðñ¡g‡V
OI´55ªLC
`fd€™‘A•õÿEÏÀX¡\Nî•ee…ò++«Ð¦soþíÀŽH$$Þ¼AFÂ5†|Ò_þš’’„®mª¶2hj¨ÓÆÝé‰Ë[[žÇ¸ŸÓn¨§ðù>8ÎÀé^K"ÈZ{<Ú÷¨’DZÛÈËÍ&#-‰˜È`æ­üMáu—æíªl£Ú@
GWOŠ‹
ÉÎTlýRiÐCcsùr£Æò¿›yt¨öâb¨§#ï63¼oWööfü}±³¶ÀÚÂkÓ*e»GpdÝÛŠÊùÄ]"!îz ¸aœ?õ§€^Bšê¸Ø7 ±eÕFúºôhïQe½¹±¡|ž“ljzÿð(6íý›‘ý^CO§rºú6îNœ¹Ä…€0úwk‡¶¦<šUÉ37¿€
{1z@Õë“ðllÚ´‰•+W2zôh,X ðÚ¯¿þÊÎ;™1c“&Mzd>‘±ñ¤ddÑÆÍI>Ž,¿°ˆ°¨8Z»9¡t/è}˜¬ìüãÐÒP§•›¬.¤«­%oE±43æäÖUhj¨SZVN‡‘2vp/,MiÛÜ™ã|kôþëºÜÚš\Øýƒ<}Ÿ‰³ÂÃÕñ±Ê][ǧ¦Nüµƒƒ;¢K¯7xsÂ…×ÎÙ˹0dôôôðàú >?ZFUëœÏãøÔIô4î¼ÇxŸÄ›ý^ðÌÆÓcÀh¢#é×Þ•nMF<ïâ<–ç1î§.tîåÅk4oÓ•É3–=Ѷ‘¡¾\ò>¤°®QcGŒ¨þ"ü8—ÇÙ¯]sÚ5wy¼‚¾ânߺAA~^•@&+#…»w2ä’Ü—št‹;©THeÚ²ÒRoÝ ±}åç}3:œF6ލÜkµ^]Zê¼ãÕ›6îÎ
ë{¶÷¤g{ÏÿÜ^¯¡6‹?™PWÅáÊ•+LŸ>ÀÀ@


ðöö®ÒwæÌ¢¢¢øä“Oضm«V­ª’OJFï}±›FX˜‘œžÉ¨þ=ظççÂpµoÌ7›v²jö4±2¯²=ÈéÏWofpŽdþ¿½;‹ª|8þöA6PqÉ}É-µMË%++µ,3I3µÒÈpéÍÌܲ2·¬ô—YšV–k*ˆ²È¢ˆ" "ˆàÂ>#¸3÷纺bÎ<çœûpžè¹Ïy–‹yX˜›áêTO£Œ­•¥úgCŒ
¸z­ð¡¯¿ºâ¾áÒå+œÈã•ïb~«Êº?Ë˯5a_mQw±jÞºC¹.V˜ÇÅìL­t»v5Ÿì,ÕØu###{dëW†Ó¼M'VýßL†¿ò.­ÚveëˉÚOcO?r.œÇ¡¾+¯Lž[aûä‡ßÿÆ­¾Ï
ìÉä¹K066âûð÷ظí

WÁ„MUånÉ6ïOµ$@?¿ˆ†½	ìГù3Æ2úõ4ÆXÜzó"bñ÷lŒ©‰*ãí5f2ÎöÌ}
‡ºv³xÍ&ΤŸç‰žé¬3²ö×?Ùøûßôï‹O>þHñž8Åî?óÂòÇÿVcbjF—>OUX63ý4
Ïãæ¤šyÖ¢oˆ;™ÂKOö£WÇ Ò2³øçÐ1RÎf0jpo\Tcâ“Ï0{ÉjŒŒXúHñVdëÆìøå{uñËO&QXXÀÛ3¿À‹Di2ý»´gÂóƒï~ !É–Ý:KŸ‡OKŽý«—5'Fhæ×†¸¨ý8¹¸Sßµ‘:±iê@ÔÁ]tï7Œi¯
`ðóصm#/ù¥Z¯CQu^~ùeFŒAFFù‰FŽIhhè]Ù·oåGDLmü¼ÔÛ|<ܱ³¶dÅœ)ê	6îÆÎÆŠ°·ÇRRZÊG_~džßþfhïò=¾Ý´í{²úÓ÷ÔëX=¬êŒûC89pµàþ»î=êýyT=>O—>O‘s!³Üw]ú>ͯke„:öNxúÐÔ§•zÉŽ?þ·šW&ÏÅû%ñzòËú¥´jÛ·ÆÍ÷î&>×»Ëq¸Y•[×9¨%ßlÚÆS}»PPXÄ•k””–òï¡£L~ñ™j»¾û}ó£ûS-	Ð¥‹¸ê˜À€g^ÁÚ¦Žú»Û—Ó,gÕÇ¡¸\ïš~>›?¾ž§þ~òÜ%—”0°[“ç.aé‡oÓÆÏ‹áý»SRRBföÅGŽ·°à*ÙYçU)ƒ;ÿ9úÇ*r¬Jºžtede3qäPõº?‘qIœ=wžßþ9@¯ŽAêÈ»ICæ¿;Žá“>zäx+âå×CÛ·7¨cJKŠÕã~Fì‰g#·*9÷í‡Tï«VQ³ÜºFØ©Ähúy†½Y½dŽz{Òñ(ü[‡ÐÀÝ“si)êíɉÑ꟭mëФY¾]ü!M¼ü±­Sqw¡[œBä!¸?fffåf*Üv»!=;ñÂÔ¹¤¤ÃÝÅ	Gû:îÙ‰aý{0rʺ«–Þ°·³á¥§úUxŒÏWÿLaQumm8Gø”×Ê•9x,ž©ó—1¤Wg¦¦Znã¹=4æ}Õ÷þ¨XÖü²OwWbO¤˜r–ð)¯>P¬r*ƒ±‰i¹Æ€
·iSBÌa{©Æµ6õiÅñèƒêïšÞò@Ð׳1çbþÆ·]ùI~‚ü›ñÆìÏùïèq‚[xSX\¡èxâ“Ïàw}ò˜ªö ÝÞªûþT{¸=;7ãèÜS3þøf±GöQ\\«Ã¢N¥fðVØblm¬XöáÛûæ]¾Êæ{Hܾs3SNœIgݯj<ù¨l'ŽGalbŠCýÞ»ƒ¿|O~ÞE¼›ñê3ØùÇŒŒø÷ÐQB_.ß¿v@×t
aT\G¯:ž¾­4Œuìu}ÜÏ	žسÚbÙ´¯|F/ôÿÍRu5L¯]»Âü¯`imKvÖ9|Zc``ˆ§os§ÁÞÁ…¤¸HFޛޑ±1ÁúðÑÛÃqkäEÌ‘}¸{ÜØÞwðh¦¾:€¿ø¹Zb.sß&m‡ ª€®%¶.NõØþu8ûŽÄ’{‰ÎAª†æøçž`ä½øk$>î´½>qÍœI/á⨚MðF/Žáý»s(:žâ’RÖÍŸºv6dåäRTRBjÆyÜœñtwåÇÏ5q7¸þ`ôÂÅKä]~°‰‰ª+î@ÕŒµ©çéÕ1wu·¸ÉÔ̂⢛oÓšúpúäq{ú“œx/ß›2$_ èF*)'âhå3¼ÜñŒðpsaéú_xåÙ³øûM4÷j\-]¿uqÌÏíª=Jˆ‰ ¨°€¼¸m¤8ʆ…3)S–‘“›£½ÝœY8u¼ú
ЭÒ2³pv¨‹¹™ê5˜oSwv>V¥ñžKKÁÔÌ¥RÉê/?"lÙV,,­1ÉúõYœ>yœ‹Ù™ñ:ùy7ß
Û;ºàêîIóÖå§ÑBˆËÜÊÚÒ‚A×§ƒ¾Áßóæ[‹ æªä ^[úvn«QÎÜÌ”.Á,ݰ…Ùo¾H[ë
§RøcÏ!LMŒñzˆÞU·±‘!íühPþ<·ÿÖXýål2ÓOÓwÈô8‚/?y›Àžܳá/OQ—=”u^çÊ…3´òñ¤Ž­õÛ9¨%Ÿ®XÇÊC))-ethaoWýÂÍúü€–&Ap&FN¥|tŠO–¯epŽO¸DüX˜™Q\\R•¡ª)
ê:8³ò³éêÖž§Ûºú1€ZÖû7Iòsÿn_7ìvÎ
ãÜ |÷	wõ›;ÕÓÎC{þàë–0âµ÷*?P!„¨€•…ù}O‘>|@*ŽæþékÜú¨E`'f}þ§OÇÌÂ’àN½i؉˜È½>3suÙ1Ï=ƒ¿Xšàs÷n“Ï
èA‡Öþ˜šcŠ1[¾
ãŠgWÖ—äÀ ºOhJ.dàd_‡ß, qƒú„Î[Ê—k7ßs_g{N¦¦SRªZEöøÉÓÔwxð…OÖŒ…ëÒ³#ÿù•Á㧠Ѓ$¨¦¬÷SHò£=VÖ¶Œ™ø!m;÷Õv(BÇ¥ef¾r½¶ÃBÔ–V¶ø¶l‡™¹j(3s;ôÔH~¬É§éк¹:ù))-å|v®Æ?7¦0·³±ÒH’ZùzbceAUѧäªù
©˜ êçxö\ÖVëׇºvlùk
]ˆ?•Za8G{;Zù4åÛMÛØ-„M;vóòÓý«%ö¢ÂŒ3#x*Ø•~ÍÇÐ}ô$JËÊhìæÌGã(-+ÃР|>YXTÌ¥ü+—p1ï29¹yÔ­Æ…]kÒz?úN’íòiÙöÞ…„RÏe1oåÞyñYm‡"„¸‘Ê}Zcgm¥±=óBŸ}û“Æ6C>™\õ]Ýn¥oÉTSäìÚÏ:JÜH¥E3LML8qú,aËÖPV¦Ä£¡“Ǩ¦å{õÙA¬úé7–mØÂú…3ÊkɬIL	ÿŠ5¿ì`@·v©ôx-­miظõÝ061Á©øýßl>ÌÍé^]>œø"†tmÛŠè„džyófŒUîX‡câ™·rÆF†|¹f®N|1cb¥Ç\÷£;$ùB´ké+“/!tÂöƒ›gùîÞ®N<_eÓÇäª)zyHwuãïFVêhoÇ–¯ÂÊ•mѬ	§MP.,*âÙŸ3sÂhìílhPßµóß/·ß¦»ÙðÛßtiðÈñ6jê§^c¤s¯!êÊWÑym¬,˜>n„ƶE«7Q\RJ—à:´n®žûVI§Ó_±î‘c½÷£;$ùB¿¤ef±ö×?å
B«t½ý ¯ÉTr´gçfb"÷jlëØÒ“ÞÏxr?"~^Ž%Öwï·øøcíèÖ¾5f&&÷}ìÔS‰lݸ\c›¹¥5£Æß|óô •ï³i(*)ÁÜôîq4nPŸðÐ×02¼÷"eCÆýè]ÿã%„(OºÀ	!´M×Ûúœü@%'@{îWŸ¾Cöùô»–Yµh&×®P\\Į߸c¹«WòY0óæ Ä”=ü´áõç{ÓeÄ›¼·`]F¼É÷¿ìÐØÉÚÿ‘|6ãá.DQéntBˆê¤ëíÏš’ü@L‚ ë7ï^ô9~}÷SVVÊ™ä5õ )îšúblbJÆÙSÔ±wÄ˯5EMo®<}õJ>IqGhêÓRãXJ¥’¸¨ý8:7¤¤¸;{GÌ-TÓE\»BJR,ÍüQ›“EBL	±ÔË:‡—_ë»ÆibjÆÐ‘7gîËÍ>ObܬmìðòoÃÙ”$c#HŒÀÓ¦GÎãêTíÛª¹÷ÿ]ó9F††dåäÒkÌdžêÓScz´oÃêÍTÊïSQ9¤\íà2XÛ!<}‹W<]oÖ¤ä*9Òõ›w/ú¿>Žû100$,t$_¬ÛKÎ…LÞÛа¯	îÔ‡yÓ_æƒE?rhÏv2Φðü«Ó8´çÖ¯' è1~Yÿ•ú8ù—.òá¤giæDfz
é©ÉŒŸºß–íØ²a1‘{hÔÔ5ËÂ÷î®]Éçê•<’b#ÉL;}ÏèÚÕËÌ›>–/Öï%bß6­ù’àŽ½‰½]]RS(¸v•ÔÈßplhI[kýëÕ±UÿlfjÂÕk…(•ÊÛO#„¢éS’ë2X¯âIØ„®·?kZò•˜éúÍ»}Ž_ŸÇýx7">úçÒNÓµïÓ;¼ïæAbem§Qvç–ï™0m!=ý9rà/‡M òÀŸ´zŒç_†R©dt?Õ£²²RvmÛHøÊmòß¿¿³kÛF†½4{Gz
³[“Š7>úz2à™±(®/|kcWßÖ~Îû#º°?*¶Â}•J%oÏ]¸áƒ0»Ç,B!„¨ùt½ýY“¨¤1@º~óîEßã×çq?-;qìð¿;ü/CG¾Á‰ãQ;¼‡m:•+{2!šFªîrÞ7×{:q<ŠÆžª¤G¡P¨»ÔJŒ!çÂ9æNCXè(þÚºŽ¢Â‚GŠ·ïÐ1œMIbÜ3íYõù
®]Á•4Œ(¾ç¾Ó?[‰­•%G=ùH1!„Bÿézû³¦&?PIo€tùæÝ‹®W¾û¡Oã~n×"è1¶múCœÝš`W×]Û62à™±åÊ6jêÇ™S	¸{øp*1Z½Ý7 ñчèÐ}ee¥œŒ?v½¼/–V¶L™³C#Í)ÒÍÌ,(..zàxëÖ«Ï„i)).fɧ“Iܵšv]¼(--½ë~3­âjA!Þ÷ÀçB!DÍ¢ëíÏšœü@L‚ Ot½òÕN®\É¿„ë€êÐ7‹f1eΊre{ôÆ¢9	ìГĘõö€à.ìܲ–ù3Ær1;{‡ú˜˜˜ahdL÷~Ï2gÊH‚»p9ï"vuxüÉi؉ï„—_kžóö}Ç»fY&&fXXZq61’¾/tÁÂÜǺu˜¾pA-|p´×캷~ë_|óó6žìýS—ðÖ¨'qqª÷0¿²gpˆ“¶C:LÆ!j]oÖôäjq¤ë•¯6y÷“o°´VMðXŸ'ñòkƒ‰©mÚ÷ ðz·µv]úáÐŽ¸¨xf,gS’0·°âýùkÉHMÆÎÞ‘w^ìCc/??ÞƒG¹ÿ/Ü=|ðmÙ€'žO§^C8wöÔãšøþ˜[ªf’›8ãúy„˜ÃÔ#›)‹g¨³Ý´ø#â“Ï``  çR>×

ÉÈÊÆÙÁžÎ-X7ÿ}cÛÚ¨Ž›‘•͵ÂÂGÿ%ê1}Ø+îOe%.2aHͤP(´‚Z¡ëíÏÚü@-M€t½òÕ6
›x«¶´²¥©ÏÍñ=vöŽemììi×¥ÍüÕÛÍ™ˆ[#/ŽÞMÛN}04¼Yµ-,­éØcP¹óÚ;8cïàL^ne¥%ßYÚØiÄáéÛ
Pu{ºKËrõG¡PàãáÀéôL5pæ‡ßþfâõ7=wzÛóÃï»ðj䆓}
¿B!DÍ ëíÏÚ’ü@-L€t½ò‰‡3äù	¤§ž¢[¿aØØÕ} }ýay¹ÙÛzEcOÿreï§þ¸»81ûÍïëÜG½ÿ@…B¡—t½ýY›’¨e	®W>ñð\Ý=qu÷|¨}‡}÷¾ÊIýB°'"šV¾žXš›±m÷Aút
®°\aQ1¿ÿûOôè¨ñ]Ii)J%ðßÑãddeÓÆÏ7gÇrÇúK×Ûµ-ùJš[èzåºMê¢*•––²téRm‡!îSøŠõd^¸Àä¹KîX.ïò>úò»rÛ‹KJé=æF¼3GcûOü{ǵä„~ÒõöCmL~ –$@º^ù„n“ú#„¨jaaa̘1ƒ“'Oj;”Z->ù»FqõÚÍ5ã®\+à¿£Ç)+»÷„9¹yìØ{˜}‘1«Ö‡³µ²,÷öç³o7ÒZ³›uÛ–>x5jP	W!t…®·jkòµ œ®W¾Ú@¦9Bˆ;»xñ"ß}÷çÏŸgܸqlß¾]Û!Õ:ç.ä0öýyxº7ÀűYÙë×e¶°'2¿¦ødùZ懎£‰›s…LjˆMä½…+Ô½Ùó°07õ‚	pŽŸ<ÍÉ3éŒ|¢7I)g«úÒÄ#öKÍU+ ¡]›öej;­3 ŽcË%m‡Rid}!*Çk¯½FR’jZÿ°uëVúõë§å¨j—Í;öзs[^vsÆÐÒ²26nÛÅï+>ÅÈÐm»òãö]LyiX…Ç8OçÀ¼ütŒUãz²rr5Ê”–•1ãóU,žñ&'ΤU݉G&m—š­VtB›Œ(ÁØ•üT—â’RöEÆ›¨Õ8öEÆPRZZiÇÊ»|µRŽuCjÆybO¤ª§ÐWné¾#t›R©$88˜ñãÇS¯^=Þzë-YûHv>Fpm±I)ddåðâ´OÆÚ_ÿäZAÑñÌãÝÈ¿r•ÏN`JøW\¼”_®ÌêÍàìP—˜¤S‰K"çRZþû&jŸ²²R¶o.?6­6‘Hˆ*d—7SÞ‘IDATL1~ÄbMùÿŠ{9e{"£j±Ø­»°vËÎGŽá|v.Ÿ}÷£ú‰îƒ
[¶†èÄä›qýs€‹y•[öGÅòÓö8“žÉÊ«Ô㋪£P(˜4i&LÀÞÞžY³fÑ¿m‡Uë´oåGDL‚Æ6wì¬-Y1g
ßÎÊws§2s¨;ÃÎÆŠ°·Ç²wý˜›™²á·¿Ë•qwu±nöEÆ“tŠ‹y—9– ã¾DõúñÛÿcÝò¹œKKÑv(Z#	¨‘²2ÓX¿2\«1˜P„?1XrE«q諘¤S?I÷ömðn¢Zd¶¸¸„Gã(.Ñ|SZVÆGs­ Pý9"6ñ‰DÄ&j<‰:~‚ìÜ<õçœÜ<Ò2³HÍ8ÏîÃÇÊÅñåÚÍŒ|¢·Æ¶Sg3HNÍP.+Sª3—òù÷ÐQŠ‹Uëææ_&"&QõOl"Å%¥ŒÜGcáÛóÙ¹KHÖ8~tb2…EìŒ&ç–XKJK‰ˆMäïÿŽPXT\áï­×6ïØ­þ]!îmHÏNü´ý_¦-XÎÒõ¿°iÇnŒÖ¿#§Ìaéú_˜»|+6n½ã1>_ý3á+×óí¦íˆŠ+7É@×¶­˜>nÓÇ`Ä ^x¸¹ð¾÷Ž}<ùÆLÒÏgãóøÍ­YŸœ»óÐÇ5Óåü\vmÛÈ¥‹Xªíp´F Q#eKeÃÊyZ;¿4's®i-}“x=	ˆá|öEvî`ÀkÓøs_ýǾˮƒQ€*¡yüåPvì=Ì×gðÓö)--%95ƒÔŒóìŒáBn§Ó3yúÍYlù{ïÎ[Êç«àÏ‘ŒœÆŒÏW‘pªü˜Á{Ó±usõçÑï~§+ÖóéŠuŒ
 °¨ˆ¡¯Ï`ìŒùüsè(}_åXB29¹ùdçæ“tн‘1”””0uÁrRÒÎ0sÑ*ÞúøÖlÙAÿW¦ª»Æ
ý„q,äÏý‘y}{#£Øú÷~¶îÚϱø“<1~:ÿ=^.^c#Cšº»räø‰J¼BÔl.NõØþu8ý»†àâTní[0þ¹'XöÑdê׫Kû_FêÀœI/áâhÀª0U#rxÿîø{6Æ¡®ëæ¿O‹fM(*)!5ã|¹sú{6fÆø‘êÏ.^zàî±AþÍøà¨WdžïæNU·úÓ÷°·µy ã‰šoiø2RUÝc#ˆØ·CËiGŸAÔN¾-Ûim£Wñ%îÜW\ÜÛ¨Á½	_¹ž7F lÙZ§¼Js¯&ô9Îò~¥Kp~û‹I£Ÿ¢w§`R3Î3vÆ|†öîL—à.\¼¤Þ?|åzžØ‹ÝBP*•ô~ñÆ
W
x622T7bnUT\LN^>v6V€jö¦ââ¾ùDµxî³o}H쉚4p&7ï2óB_ÃÝʼnÍö²qÛ.>š8OwW†ôêLH+?c_¹VÀŸû#Ù³vïÿß×üu R=]îØgЮ¥/þÍØò×~:´nΠÔ£#)iç°¯c˦»iÛRsÜ@#×úœ8}¶Ü9…wWÑ3Ö–ºmkÏÆêŸƒš{P¯Ž-};·Õ(gnfJ—à–nØÂì7_ÔøÎÆÊߦԟÿØsSc¼¹Ýw¼v6Vê¿O&ÆÆêX‚[xßi7QK)•J<}ZcmkÏÞ?7ÓwèjëCI€D”•™ÆŸ¿®åÙß©ÖóZq_â0¢¤ZÏ[DÄ$¨­|›rð˜êíÇþ¨8&Žz7gGÒ3/TØ=쟃G9¯+ãPÇŽô̆[c|ËØŸýQq´ôñPníçÉþ#±4iàŒµî.ª)S|šòåÚÍw½ž#qIø4i¨þÜÆÏ‹ýQ±ê¨•§úšN§«Þýýß–¬ûn.(Š;Ž051¡ Pp} T*Y¸p!ÉÉÉdgg3kÖ,eP
aea^.ñ¹“ázTq4¢¶S(ö*i§“8vèßjo#éé'j$mt³!?b%ù©"ÞMIH>À±„dZûyªä%:ñ陨_¯.¦&ÆX˜›ª"èÐÆŸ§útáÛ¹SùvîTÖÌŸNÃë	‹±QÅÏ‚¬--022R/ŠÔ¼™ú\GŸT'O¹ù—9{.Pá	ôo€…™™F7´ôö þ–.wQÇOh$b
Eùx¾Z÷³&Œ&ìí±´ônÊÜ¥¤£©»,¨¨
dñâÅ\¸p…¢¨èæ!„¨4òHÔHÕÝ®iFÔΕ«Ãˆ'zñVØbz†²}ÏABÇà™Ç»2uþ2¢Ž'ñ÷#ŒÚ€v-}ùü»Ÿ@¡`h¯N<ûx7&ÎYD܉Ӹ8Ús *Ž•O¹çyÛør4á$íühîÕ„’’ÞœóJ”—–ÐÒÛƒk…ØZY2é“Åú7ã÷²àÝqtlÎgßüÈ¡èxÆB}\kK:´ògThî®õ9Ë[/¶g®ÿ
x¨}+«~Èb¦K;DXèh¾X¿WÛ¡<”Á!NT?
…RÞ‰éF¸ªN€œÈ¤	ɵ.ù©.·Í±47£g‡ÀråŒèÒ¦Üvs3ÓrǸ}`°C]»»Æàݤ!Áͽ))-U¯ÔÜ«I…eìëЯK»rÛíílÔƒ«ýnôàîâT.ùjs½{¨®ÙÇC5
¸B¡ÐˆÿFvë5dçæóúˆ¡w½&¡{¦NŠƒƒC­H~„Ú¡T*Ù²~)çÒO“Ÿ—Ãú•á4õ °COm‡Víd¨‘ª£œéxpR’ŸZ१úÝu!Tc>~ë¥jŒèÎvQOÏ+ô‡¡¡!¯¼òжÃBÔ`
…‚¤ã‘üþÓ×äåæ°eýÒ
Ǜ֒	ñr†F¤h;¡#
*|3%„Bè’WÞùg7Uï/¿6´	©}o@ !X#RhÀYm‡!„Bñ@¬¬íèÒç)lëÔã•wæj;­‘1@B<€œ¥.9rs¾¥ºÜ­ÿ®h›B!„6=9j"¶uêQßµ‘¶CÑšJ™®ã:¦2fa©¬X„î‘ú!îFꇸ©ân¤~ˆ»yÔÜå‘ !„B!„Ð
…B)c€„B!„µ†$@B!„BˆZC !„B!D­!	B!„¢ÖH!„BQkH$„B!„¨5$B!„BÔ’	!„B!j
I€„B!„µ†$@B!„BˆZC !„B!D­!	B!„¢ÖH!„BQkH$„B!„¨5€RÛA!„B!Duø yë'Žé¼IEND®B`‚urwid-1.3.1/docs/manual/images/introduction.png0000664000175000017500000006174512615524560021134 0ustar  ianian00000000000000‰PNG


IHDR°ØŠž¶©sBIT|dˆ	pHYs
×
×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwTWÀáßR¤ Ø»Ø(Š ¢bïŠ=jì%±7Œ%Š5–Ø1Qc¢Iì]ÁÞAA”"bEQAÀBïì~ð¹JlˆÀ²zŸs<ǽ÷Ývß½eîH‚ ‚ dÔd2‘ÃAå!‘HPQt‚ ‚"	‚ JI$0AA)‰&‚ (%‘ÀA¥$˜ ‚ ”DA”’H`‚ ‚R	LAPJ"	‚ JI$0AA)‰&‚ (%‘ÀA¥$˜ ‚ ”DA”’H`‚ ‚R	LAPJ"	‚ JI$0AA)‰&‚ (%‘ÀA¥$˜ ‚ ”Ô€ $‰D¢è”šL&St‚ðA"	_½(¯CŠA)™ØuWt‚ðQ¢QAPJ"	‚ JI$0AA)‰&‚ (%‘ÀA¥$˜ ‚ ”DA”’H`‚ ‚R	LþãÈ/.øø¿³="ê9‡ÏzòüUÜG_õŸÀÛŸUgÀPn…>ü¬×·N$0Aø±ó×0ð§ÅÄ'&çØ¾~ça~p^ɽ°Ç}}pèCöž¸ðYunw=Ã᳞Ÿ« |ËD„÷¨Y©<'/ùÈK¥2N_¾†©qÉwö}O–T*ܦICVΓc©TöNBü\z½Löá²3³²HIMû¢z¡¨k!
Â{tkmÛy/úth€—ÿM,jWçVH˜|Ÿ­‡O±úŸ}èéòàq³GdTß.¹à…ëÙËüõË4†ÍZFéR%8qчäÔ4¬jWgë²Y¨«çþ£wÞÛŸ5ÿî#úE,‰É),súN¶¬þgûOzœšJ£’l˜?…Ц&Œ[°	—®!ABk;+~öcþ$AP0Ñ„÷hikÉõà{ò–ÍÁ3—èѦ©üù„¤d6îvãøŸËpß¶†]«æ°tÓN¤YR223HÏÈäúÍ{xíþ€Ãq7ì	ž~A¹Ž#úE,#g/gêоxï]Ïæ_~bÌüÕ<Šaÿ)ö¿À¡ßqíà&l-j3a¡™™‡<ÄcÛZ»ŽË¯Ã#E‚H`‚ðÅŠÑÒÆ’“—|ÈÈÌÂóÚ
ZÙZÉŸ×ÓÑÆc»a#ùkß1v»@RJ*Ré»·éÔ¢1ZšhijP«Jy^Æ&ä:ŽÀ»÷)UÜ€æÌ°17£JyS|ƒîrùz›Û`\ÒU9¶ÃçÆmÒÒ3h߬úºÚhkiÒÑÁÏk¹Oœ‚ D„pü7¢»?M¬êæèö{—@óï'°vëÒÒ3èØÜï&°·ÇÍTTT޻χ¤¤¦a¨¯›c›®É©iêëÉ·ëéh£ª¢"¿‡—q‰âòçtµµä‰M¾"	Â84² àv(ÛÜÎÐý­îC/ÿ`ÔÕÔØ³f.c8¢ª’ýQJÏç$aVµ"AwȧîÇ%&át‡:Õ+S»Z%Îy]—ï{ÞÛÊåË ©Q€K×nÈŸóð
IJvõ|MMLâ„PWW£¥­%ç½ýi²dzŽçš6¬Ï”%¿3cÅ&ŠS'äác´µ4yŸ˜çúöŸºˆoÐùãš•+°Ôi#út¦å Étr°åÄÅ«ŒØêË2¼WGŸõ¤ÃÈéÔ¨TŽSž¾üýVœ¾AwøÞi™YÄ&$2¼WÇ<Ç&E‘‰Û†_+‰DòÙwdö¼…yͪhijðìùKbã©U¥·C©\¾º:¤¤¦qÙÿ&ÚššØš×&èÞ*šš%•ó2–ZU*pçA8%
õ1*aðÎã×î‡GõüeŽmzºÚÔ«QȾ¶,ìI$õkT¡‚©‰|ŸŒŒL¼o‘–‘A£zfèëjðƒóJ[Ô¡FåòX׫…ºšêg»îˆï¡¨’H$"	_·¼$°¯ÁÎ+±³¬Ëàîíò\†H`BQ&‘HĘ |ì,ëR­¢©¢Ã„%ÆÀá+ô%-/AP¢&‚ (%‘ÀA¥$˜ ‚ ”DA”’H`‚ ‚R	LAPJ"	‚ JI$0AA)‰¥¤„¯šD"QtJM|7E•D"+q_7ñ,_/Ñ…(‚ (%‘ÀA¥$˜ ‚ ”DA”’H`‚ ‚R	LAPJ"	E–§§§¢C¾8‡BA	L(rŽ=еµ5“'O&..NÑá˜yóæ1oÞsçÎÅ××E‡%䑾¾¾Ì;—ùóç‹D&ä;‘À…{_âêܹ³¢ÃòIçÎE"
„H`‚ˆÄõm‰LÈo"	…N$®o›HdB~	L(4"q	o‰LøR"	N$.ácD"òJ$0¡ÀˆÄ%|‘È„Ï%˜ïD⾄HdBn‰&䑸„ü$™ð)"	_L$.¡ ‰D&|ˆH`Bž‰Ä%&‘È„ÿ	Løl"q	Š$™ðšH`ÂEFFrøðaùc‘¸„¢äS‰ìðáÃDFF*0B¡ I™L&StBÔ¦Mž?΂X°`sçÎIë………±uëV.\¸@‹-4h•+WVpdÊíèѣ̟?gggœ)UªgΜQpdBAH$"	ï·~ýzœœœHKK£B…
¬[·N$®|’ššŠ¹¹9÷îÝ F¢©©©àȾGeüøñ„‡‡£¡¡ÁŠ+3fŒ¢Ãò™D"]ˆÂ»¢££Y¼x1)))H¥R

éÔ©“¢Ãújhjjâää„¶¶6ÚÚÚ899‰ä•:uê„¡¡!R©”””/^Ltt´¢Ã
€š¢Šž´´4–,Y³gÏˆŠŠ"66–'OžP¾|yE‡öÕ9r$ÿþû¯üÿBþyòä	
4 U«V˜˜˜PºtiÒÒÒ–PD¢ (ˆ666
ŽD”A”’A”–H`‚ ‚R	LAPJ"	‚ J高FÍŠ•+ñòò&0ПĄE‡ôMÐÕÓÃÜÜ;;[œ¦NÅØØX¡ñDGG³rå
¼½½ð÷$!!Q¡ñ|-ôôt±´4ÇÖÖŽ©SŠÄy^µr>>^øù/ÎsnèëëbeiŽSŠÀyÌJ?q×®]L˜0	Û–]1·v š™†%ŒÖ7!öe¡·ôuÇû¼..kèׯŸBbÙµk'M cW[ìÌ©oQRF†
‰åkó<&–¡xºrÜÍ›µk\zž'OOG[Ú¶¨‹µU5LŒ‹²‰Š‰Ã×/”Ón²÷°7«×¬SØyÌJ?~×®]8ý4“Éó7QÍÌBÑá|ÓBo°zî(Vüº¤Ð?»víbÆL'ÖnšL}‹j…Z÷·æF@(G­fé’
9Ï?ÏrbÏæñX[V-Ôº¿6¾þ÷é;|¿,.üó˜_”:EGGS§N=¦/Û&’Wz;€eÓThÝÑÑÑÔ­W‡MÛ¦‹äUHn„2jà2nêy®_¯6Gv9‰ä•O|ýïÓ¥ß
nÝRÊîD¥¾lÅʕضì*’WRÍÌÛ–]Y±re¡Õ¹rå
:vµÉ«Õ·¨FÇ®¶¬\¹¢Ðê\µr}mEòÊGÖ–Uéãh˪B<ùMi˜——7æÖŠCøsk¼¼¼­>oo/ìÌ­>!›½ƒ9ÞÞ^…VŸm[Ô-´ú¾m[ÔÅǧðÎc~SÚè/Z_EP53ý­>ÿ@ÑúR€úÕð÷,´úüü±¶ç9¿Y[UïÏc~SÚiô‰		mþàáî ¢ªŠžA	jÔ¶BCSK¾×…#4²ošºúg×/ø:ÅKabZñóƒÿk—Ï •fѨiûÛãc_pãÚ%jÖ³ÆÈ¤l®Ë{ùüO…R¯ýG÷ó¹x‹F9ŽÉ—2,aT¨—1$$$~r¶all">—oÒ®“­|Ûý§Ü¹FÇ®MH$<~EÄÓçT«QŽ»·a×´þ;eù_»K‰’úT¬\&Ï1gef¡ª¦úÎvs~˜Õ­Œ±Iñ<—ý¥²²¤¨¨HäÇäCJêe
ññ‰¹šmø*6‰SçÞû\G;TT>þ¾ò"33µÿœÏW±I¸{Ó½s£|¯/?™(õeJÛû”Ëç\Ùúû.9Äùc{øÇe.?ôlHÀUwù>;7-#=-%OåÛ¿™ ë—ó%Ö¿VÏbÉôÁDE<ʱý쑬tþ;7®~Vy¡·Ù³ùÓýÚë—N%!îÕg•­ŒÔÕT?j1ѱòmëVîaêØµÜ

“oûçÏ£\8{Ga‘ìøçä{Ëú÷¯cxzäý«ÿõ{Ìž¶ñ½Ï-™ÿ/Á7ä¹ìüЫãtRR”÷Ö#ãùþ‡uì>èõÎ?©TšïõMŸ·ƒSçßý{xð0Š‘“þÈ÷ú„œ”¶–õ­›3nÖjùãÓ®ÛX»`ëvz¢«oÈo»^7éß}	ñÉ|×íg:4ŸDk»±<
‹”—éãu‡F?âØÆ‰Es¶ÐÎ~¸pbk¶¹óÛ./ÒRßüÂÏÊÌÈÑu’™™ùNWʃ{Alÿc	k¶¹óÇk´èØ—µ³[‚·º0tÂ|Vo=ÏÌe[¹qíÒgó¢ I3s®]ÍN`ÏûѬ…%
m̸FbB2¾Þ·hШêH¥222²¿P9o¡R•2¸ûnä¯í³¹ü€ôôÆXΜEÃ9幎ªÕËò™LÆ€PÖ.ßÍIµx\ûƒ®=š2m‚eLK1uÖì›[°hù¹ŽýVP×|náqõÎ\þqSú°Áåê´é`ƒëþ‹@öXŸ§Gm;ذláV^¾Là¸û.^ßÄËñü¾z_vìi¼}éçëÇ¿®ªª
û/¥d)å]Ý"=#Çï—çø·pùú÷²gÛž‹ò}ÿÝíÁ÷}šqáR0ë6 äša¿Ó¶¥9?NÉþñ™‘™Åù‹7¹éµ’§Áðâe"GO]gìˆvØ6¬ŽËÒ¡tlc™ëø&Ìø›ÌÌ,‚~@̳'X4røPPصìŠ×ù#\q?Jã]äO?¼U7ïdw9V7³ä¦ÿnúyaní€AñRhhjåx]nø]9O)£2\<½ŸCÛבœϽ`?’ã¨ß°›×ÌaÓÊ™¤$%0rê’Ï*»¨°lP“Ga‘$'§âqÞŸf--QWW£¡×|nã㌽û³Z½=ƒèìØUU*U)#ïZ
CMM•–màØÛÿwyœó£Œi)íóàu‡HJLÁÏ÷.ÉI©yнNý*lÛ?ŸËÙôÛ!<ÝHˆÏþ‘Ò«_KÜ^D&“qôÐ%Z¶µFGW‹+žAôìÛMÍbèèjѽws._¼‘§ú•ªª
Cú9äø×¦Eö„œïz4á¬Gñ	)\ö¹‹ž®&Væ•9~ÆreK²yûy–¯s#--ƒ³7ÈÌÌ [GkôtµPWWżnEž¿ÈûD¥—‚6 %êèп—=ç.|ç	R©ŒÞÝP¯v7ª»ç-LK§uózèe¯ÿ}’’ÓhÙ´.¦oaüô-$§¤ã²tè—>¥òM%0™LFxØÝwÆ•ê5°gùæ“”¯T·]ÓÇ–èÈìnÃo®P×ÒÑ#++“¬¬,ù¶´Ô¦hÏö‹ILˆÃ®eWd2Èd¨ª©Ó¼]O.ž>ˆûÉ}4o߉ʇy“V]¹|Þûw1*]}ÃòçÒÓRÑÕ3Ìñz½ìþîÔ”$tõÞüb.i”s†\ŽØ{ÆÉ’“âÑÒÑCG×]Š—0æÇi¿"AB¯!“˜º`#êêêløÕ‰™?tFWnQSWÅʺ^oððAõ-ªÐÌÁ¿kwñõ¹EÓ÷$°¤¤TßÃ*]&ûœ¨¨HPSS“ÿ*¦®†šzöØiB|zúÚèè o ƒ±I	,û!ÏÇíέ‡´°Í=ÐÖÑÄÞÁB^–…U
4µ4ðó½Ë¡½îôú®=.gXü͘­ž¾oÿl{ûoâukók¡ª¢‚c'ëÿlfŸo}=-:µµb¿›7Ûö\dØ€–ÄÆ%c¨¯¡†:Tª`ŒËÒadfe§r¦%åå«©©"•æý3œ’F‰âoþ¦^©%'§a ¯ªªÊ;Ï”6y36­­­Déé™ÌÞ››& ªªÂˆ	iÖi®R~Fóâ›J`®»6”‹íÿ[1¯;º“;A¾ô2‰å[NQʤ,wo^àN/éiÙ¿œ}=¨T­NŽi÷÷ïò"&’_6¸ÒkðD4µ²ú32³h[uîÏÕ‹'¸â~”–ú~4¾jµ,HJˆãà¶uït5–¯TƒÈ'a<{ú0»üô4‚®{R©Zª™Yä˜]týM7Ÿ†–6¯þ?n'•fÉ»OßV©Z¢"ÃiÝuíº¦Ž¥wƒ¯£¥­Ëæ5³ÑÐÒfè„¬ÙæÎƒ»AïTMšÕgËGhl_O>ºiK®xñ"&Žšfï^Qß²Ýߌox_¾™½Ý¢2™Œ[ÿŸôàs%˜¤Äì­fu*ñ8<š¾ß·¡ÿàv4´1#Ð?mMÔÔTå¿ês븛Í[Zá²i*ßí@rRjޤӻ+þýë/žÇac—}±o­Ú•¸pöš|Ÿg¯cV§2Ú:DG½àé“yk@UU•¬ÌüŸ­W”íß‚û.ávâzg_jbQ¯áOžóãÐ6ü8´
Væ•	ºþÞ	oSSS%+ëóŽWý:9vÚOþøø?ÌëTĬfYŸ<çNÈS ;9u¼nöße@ÐC^¼Ìnù¹{S±¼Å
u?}úÚ¬Y<„ÀK˹êJTÌ×9†ù__õ,ÄëWÎ2{lw²23xñˆbÅ4˜¹l«<ɼVÛ¢1?îÆ½`?ÒRSHMIÂܺ9·® ©¥Ó°6Ô¬ÛŸ‹'˜»zOŽ×Ö¨ÝUU~ûeúÅKòà^ú†%Iˆ{‰‘IY*V5CM­šÚ”­ðé1íZvåèÞMŒûyMŽí%Mé3t
ÓGv¤IË®\¿r–VûS£ŽUkÕçÒéƒLÙÓŠ„…ÜÄÀ°ìÛ±xú`b‰zúè½×|5míˆûÉ}LÐŒ[qõÒ)z™„DE…êµ­X1{6ÍÚLÃ&mÞiá)‹&ÍÌùÅùo–®~3Ó³rUS¢Ÿ½Ä¦ÉûWy˜½`ßuû™!O	
y‚ÆÿgóI$&ýô}ºÌ²aM´µ5ÑÔ,F1
uºölÎáýth>f-¬8{ò*§õE"‘P®‚1W<ƒøuÑ6~š=ðú–ÿ²?×’?îìØ”6í1ä»,[¸•¸W‰Dó¦÷¦iGgú8Úqò\ß÷iJ›š¸{ÞB_OûÎ4³3c¿›7·:ÐȪ½†¬Â±£5~7ÂèÞ©¥¿;1(íb¾‰„C^Q|>:ò±¼PEU…â%Œ)]®rŽñ¯[ÞÔ¬ÛUU522Ò¹ä‹4+‹šu¢©¥·û1ŽØÂèé+xò0„Zõ¬Ñ3ȾÈôÉÃtõ0,iLFF:·ü¯ QQ¡Ž…-†`XÂH~¡µË¢	˜Õ·¡M×ïõ^°e+VEG×€øØ—Ä<{LÕZÙË#=
¦xIŠg'¤ð°»<}B¥ju(S®²¼™TJÈí’“â©R£>/b"¨\=ûK9òIBoQ¹F=$	ªjj”4*à _ªÖ2G]½2™Œ»A¾Äǽ¤¶¹mŽ©ôI‰qÜ
ðÁ xIª›Y~´ »I¡uaH$îGúôŽÿçãLÝúUÐÑ}“ÈïÝ	GO_›2¦ÙÇ8>.‰'áQÔ®—ÝÕœœ”ŠW0eËQ¬˜:ZÚ”*e@ø£(uIˆO¢\,ª
  tªª*Èd2®_½CllÖ6µsL¥
%11…ÆöõrÄv# ””äœãd¦e(_Ñ„ØW	øzß´¬uêWÁÇ+˜ÖµPSÏîÎjÖ`»Ý~¡\ù7]Þ±±‰\¿z}},Ô@]ýÍïU?ß;¼z•ÛãGQT­QMÍbÄDÇr# k›Úè¿g*ùÛªšt/Ôó,{¹÷“û%&¥rÍÿþ{Ÿ³¬_ýì¯wBž¢§«EÙ2oºé¥R—®Ü&>!…fvfÜ÷íÇÉ)ix\¾Eª¦T­lòÉ8¬­ª¡£­AÌóx®øÞ£TI=¬-«¡®þæBè[wŸpûÞSêÕ®@ªÙ?/\
fꜭÚæD@ÐC[×Àø­»_Å&áé}c#}YUû¬©ô’}”²ËQ©W£×Ó×gÝ®+zï¯×	lÁºyz}TD8w‚®²mÃ/ü¶ëò;-¿¯QìËÆ÷kLB||¡Ô§¯¯ÇÙ+ërﯮm¦2tTêYTc϶3„Þ{Ìß»5†›÷9yì
÷n‡³iÛ¬B«÷yL,­'>¾pV]10ÐãžïêoöÞ_¯˜Ÿû²|-7*&ŽÖ“‰‹S¾›+õjôææ–„Þ~ÿ’1ùŨt9,mZäùõÏ£žpéô!¦ÌßðM$/Ⱦ¥Š¹yî§)KKsn„Z}oûuíxî?äç-hjcÝŸN…ƒ·×MÂF±xÕ˜B­÷F@(––…·ˆ²•¥9¾~Š9ÏEAiCºvh˜ïåúú…bUˆç1¿)í˜-¾î4lÒ¦Àê¨ZË\Þ•—u,í¨ci—}¾îØÙÙ~zÇ|bkk‡§{ |:{aªU»3ç
)ôzß6bt7…Ôë鈭máýmÛØØqúÂM:·kPhu%f5hø' IDATÊ2ozï|/÷ô…›ØØ(ïw”Òv!ŠZ=↖ßqC˯ƒ¸¡¥ãⲆÕsGxW¢ði¡·X=w..k
õÃ`llÌÚ5.LµZa]‰ß’¡Lµšµk\
ý<¯^³Ž¾Ã×áûIBîùúß§ïðu¬^³N)“×kJÛ…Я_?&LˆmË®˜[;PÍÌ¢@'voľŒ!ôv¾îxŸwÃÅeüœ¦×uŽ8Ž]m±w0§¾E5…Lìø=‰åF@(žîwófí…žç.ýÆÓÇÑ–¶-êbmUí›Øñ¹¢bâðõåô…›ì=ìÍê5ëró“Òv!¾-::š+WâååM` ¡Þê[¦«§‡¹¹%vv¶8Mªð_rÑÑѬ\¹oo/üýõ~U_3==],-ͱµµcêT§"qžW­\~þJ}?«Â¤¯¯‹•¥966vL)çñK)õ4zAáÛ¥Ôc`‚ ·M$0AA)‰&‚ (%‘ÀA¥$˜ ‚ ”DA”’H`®è„",.Nñ7
,
1|«bcc‰Uhßúw”H`àéé‰RÿÌ›7yóæ):Œ¯VëÖ­ñôôTXýžžž´nÝZaõëÖ¬YÚ5k>½c	ÇÆÆF¡ƒŠ&Ø{xzzÒ»woöíÛG…
ŽP=z{{{…Åðºîױߖ
*°oß>z÷îýÍ&1‘Àþãíä¥È/'¡h›?>sçÎUtÌ;—ùóç+:AAìíí¿é$&Ø[Dòrãu‹§sçÎ
ŽäM¢öíú–“˜H`ÿ'’—[E¥õõšh…	ßjû¦ؾ}û‘¼„Ü+J­¯×D+L€o3‰}³	,66–ñãÇÓ¿‘¼„\+j­¯×D+L€o/‰}³	ÌÍ͘˜vïÞM‹-Dò>©(¶¾^­0áµo)‰}³	ÌÕÕ©TŠD"áüùóòîDAø¢ÚúzM´Â„×¾•$öÍ&°¨¨(zôèÁßÿÍ;wèÝ»·¢Cа¢ÜúzM´Â„·}ILMÑ(Ê×zB…‚QÔ[_¯½n…åD+ž·“Ø×8ÎÿͶÀ!·”¡õõšh…	ÿõ5·ÄD„OP–Ö×kb,Lø¯¯5‰)]KLLäÞ½{¸»»³sçNV¯^ÍæÍ›9qâÄÄÄ “ɦ ä?~ŒL&SªÖ×ko·Âd2?VpDBQð¾$¦ì“×$€¬¨~á¿zõŠãÇsð+<{T*¥”qJ–*M	£Òè‘’œÈ«ç‘¼|þŒçÑÏHJLÀÈÄ„Š+Ñ¥SG»aff¦è·ShÂÂÂØºu+.\ E‹4ˆÊ•++82å1bÄ®_¿Nll,...téÒEÑ!}–#GŽ0aÂ

iРýõ—¢Cúj(ûçëõÂ
-Z´àüùóܹsCCCE‡õÙ$IћĆ«›&Àß‹FÍhФ=-zN¤D)´uõ?YFFF:¯žGùøÞWΰî÷vhjhн»#Ý»agg‡ŠŠÒ5>s­L™2ìܹ“{÷îÉôéÓ•r144$  œÉÊÊÂÑÑQÑaåÊáÇ™?>ááá<|øV­Z):¤¯Š²¾ìííiÑ¢»wïF"‘àææÆ AƒVž™oq777Ì-¬hаÇÏùдÓ0¶¹ÉÔ…›qhß›r•ªç*y¨«øLyÌ5gèÄE¬ßwqÎyú
†Ž‘±	óæÏ'))©€ß•bhjjâää„¶¶6ÚÚÚ899¡©©©è°”Љ‰	”*U
G”{666”*U



àÍ{ò‡²¾öíÛÇùóç‘H$H¥R\]]Rž)¼ÑÛÛ›)S§ù,šïFÍÂÚ¾-**ªZgäãìû{·¼˜7o.#†GM­È5F¿Øë)³_Ó maÙ¶m3gÎdÖ¬YŒ3FÑáäÉúõëY¼x1K–,aàÀŠ竣̟¯ØØXÜÜÜpuu%**J)߃D"Q\		á§é3ðòò¦×P'Zuþ®À×=¸{ƒ÷"’å¿.¥{÷î…ZAóññPªÖCQñøñc444066Vt(_$::š´´4Ê—/¯èP¾:âó¥X
I`2™ŒÙsœY¿~=ûþHç>£ÐÐÔ*´úß'àª;;7.¢T	öíÝMÙ²e ‚ðq…žÀé×á/˜²`ÅKJ½¹!“É8²g#'÷ÿÉ¡ƒį*A„"L"‘Þ$ް°0¬Ù’¥^çÕ{ŠTò‚ìƒÑõ»ÑŸú+;uaÛöíŠIAøˆBiyxxЧïwt8‰ö=†h]ùáqØ=–ÍD¿ïz³lé’¯zʽ ‚2*”.Ä;v2qò&ÎÝ@=«&VO~Kˆ{ÅŠÙè`ZŠC|•³AÈääd"""ˆˆˆ 22’/^P²dILMM155¥L™2hkk+:ÌoJ'°Ë—/ãØ½'óÖ¤l…jRGAÊÊÌ`éôA4´¨Å†õ¿QY‰$Ÿ¢*šòëoèk?NŸò©ãøµŸ¢²*Ppp0‡ÂÍõwï…––Ži™R˜–.AÙ2%(Y\‡ç/‰xöŠˆg/‰ˆ|ކF1ÌjÕ k·tëæHíÚµý6¾jšÀÂÃÃidcËè™.˜[7Ë÷òKrb<3Gu`æt'~üñ‡<—#‘H8ä•‘ÝíLò5Ý:”/e)›ª&Ýs•À¾Öã“›÷_®]»Æž=»8|è ©©ÉtëÐn­h`^…Åu?ùú—¯¹ø€ÃÇ®ãzâÚÚº8vïAŸ>ßѰaÃBxß–K`ÉÉÉØØÚa×¶/zÏײ!òñféÊÁûhÞ¼yžÊ	,w¾æ/èO	L1	ìöíÛ̘îD` ûØÓ­CZVý¢2e2×pø˜/;\ÁÜÜŠ¥ËVP«V­|Š:¥¤¤pñâEˆŒŒ$2â	OˆÇÄÄ„2¦å(S¦,¦¦¦Ô¯_KKKE‡\0	L&“ѳWoRe:Œš¶<ßÊU´À«üöË8®úxçiÑN‘Àrçkþ‚þ‘À
7EDD0wîlޏ¹2}bÆk‹††z¾×“žžÉúͧYºöŽÝ»3oÞBJ—.ïõ|®èèhŽ=Š›ëA<<.bi^•j•M0-­ié☖.޾ž6Q1±DD¾""*–ˆgq\ó@bR:]»u£kWGZ´hA±bÅ
=þYÌwÉҥ܉ó꽟Ü÷N/OàyôSÊWªIûC0*].¿CÊæšãøý:uîJ`€êêùÿ‡.BáX»v
¿,ZȨÁ-¹ç»}½‚[L¡X15&îÈÐ,YãJ½ºµqž;ñã'XóðáCæÌžÅÑ£Gi×Ê’¾],ù×¥7ú¹Ÿ„rï~$®Ç¯±pžßxÊô37n|¡¯	™¯óÃ###Y±b%æüŽªÚÇ¿à}/²tÆKÓ¢c_R’qÖ†èÈ¢{ï¢N½G¢­_Šõë7(:Aò --¡C±kû&ü=³èç>š¼Þf ¯ÍRç~\;¿ˆ·üÎðaCHOO/”ºž?ÎäÉidm…Y	·7°û¯qôëÙä³’@ªe˜6¾žÇ¹|rW.ºR³F5þùç¤Ri½ƒwåk›ã<—Ö]¾§¤±é'÷½rá-:ô¡ÏÐ)4vèÌ(§¥ÔkД ¿7‹J¦¦$áï}žÈ'aòmÃî‘Ëu¯3ÄǾ >ö%W/âV 7²ÿ¼'C¸â~”ˆðûòmñ±/ˆŽ|Ì‹˜H¼=Žç(ÿSŒžÃÂE‹ˆÏõkAP¼gϞѡ)éÉḙMÙ2%GÅòF\:îLbì}ZµlNttt×¹k×NjÕ¬NVò}n]YÁ¬ÉŽhiæO·_ªe8ðïDvÿ5šÍ›VÒÀÊœäKÙŸ’oc`wîÜ¡i3Ö¶ŽÞ'÷?íº½[VÒsðDš¶îŽ®~Ϊ……Üdî„^X4r &ê)*×dôôÌÛŒôT233iݹ•ªÕaùì‘ÔkЄG÷ocXˆ9«v¡ªªÆÞ-+9í¶K®^:I¯Á“éÒwçŽîäøþ-H¥R*V5Ã×ó“æ®ÇÚ¾m®Þëo‹ÆccQ_~Y”ëã#ÆÀrçkãù1Vpc`~~~8vë¨ÁÍ™=µè,Ú½`ùAþÞéÉa×£˜››ç{ù2™Œ9s~fïîmÚ6™:µ
~Qç¿¶ÇyÉvîÚ‹ƒƒCÕ“¯c`Ó~šã÷ãs•¼ÚvˆL&ãôá­l^=›êµ-iÕ¹?­»ô`ã¯?ÑäÚ÷BFz.‹&œ˜Ýê©mјÁc³ëÖŽ~#§Ó¦ëd2Îã{páø^ê5°çÀ6þØï‹aIcü0‹Ñ½Ñ´uöM	?¼ÇÖwÐÔÒf÷æå\¹p$×	¬ïÈLÚŠ±cÇ`júéÖ¦ ŠóèÑ#:ulËÒïéÝ­±¢ÃÉÁyZjU/Cûvmðöñ¥bÅŠùVvRR¿ïOBìc|Î,¤¸¡N¾•ý1#¶¤VuS¾З9Îóùᇬ®|I`—/_&àFk¦}ÞžíÑÎq1QO¹vù4;7-%>îÝú&,ä&Sæg5©Ó`ê‚?䯫Z³>}çåû÷nàqrW.àyT!·üÐÒÖAUU
—Eo”J
 ¶¹
šZÙý¾¥ŒËòèþí\ÇmdR–Vû3gŽ3›7‹[µBQ•œœŒc·ÎLŸÐ©È%¯×ú86æiä+»uæ²—O¾¬è‘””DófM°kXžU›~BM­poUeo[¯“óè:`‘‘Ì›· @êÉ—6oþBúûé“7Þ¶`òw7—ŠUÍ02)K‡CÑÑÑç´Ûvº÷‹L&%9)A¾ÿÃÐ[˜˜V ˜FöL‰	º?–Òe+É÷ÕÖÕçö«è–`ø¤…9ê-ilÊås®h½ÕRÌËâ=Mdl.\ Za‚PÉd2úkS&î¨èp>jòèŽÝzÂàAß³wß/ZqE&“1ðûþ44/ËÒAùåç©P®çÿL£Ös¨U«6ß}÷]¾×ñÅ“8âââðññƶùçý”0*Ãæ5³ydOظ|ÞjµÌ‘¨¨PÇ¢1^ÿoUŽzÎÂ)ýJ³r”¡ª¦Nm®_9O™òU(]®2ë~™DÈ­jÖmÈ«Ñ<Š lÅê,˜ÒÌÌŒÆû2Ç„ÑÑ5 AãV¸¹¹}Öû¡p,Z¸€çQøý×¢¿€8ÀƕÈ|zE¿¬µ2ož3/Ÿ?dÝÒ!ùØ(Q\×S˜4qׯ_Ï÷ò¿¸vìØ1ÌÚ£^Lã³^÷ƒÓRþX>ñý›¢¦®NFz:MZv¥ÿ¨ÙÏOû•_g
çò97444<Ö]ƒwÊ3c+àÇ^(¦¡E]ËÆ4jÚ€‰sÖ±rî”4*ƒT*eÀ³ÐÕ3|§Œ·yž=Ä…ã{Y¾åÔ'ßCûö8¸Ì¿>^™TÊãG!—./ïâ>L&“ñ<&ŽRFïüjMNNE&•¡£›»iÒ©©é$Ä'Ù­ò%
PQùºÖLJL!99í½Ï×E]ýëX´ÚÏÏM›Öà±uõ¼wŸeffñòU"ÆFo¾{†Ç £­Q)} ûo0ôÁ3ÔÔT©\1ïwð.VLƒÿN¼Ù:wéš§Õ.8Àö­[ð9³ð‹Þw~ªS«<®Aî]ñ½æŸ¯w9ÿâYˆ=zö¦œYSZvÊ[óP&“ñòù3K¡ªúî‡'1!ö“I )1-m]TTrž4™LFRBÜ;³?$++“?–OgÌŒ•ŸÜ75%‰‘Ý̉ˆxŠžÞÇ'¯äfâö‹9íº•â%Œ‰}C:
˜™
.øiöÀ<Çó¶üœ…غ•ýk3üû_TNðÇØ´žEâ“mòm=¯dᬾԮYŽƒG|;m3†:¤¥g÷î,œÕ—½›æ¹Î
[ÎpøÔ}N>÷Y¯KLL¤FõªÙ9…Uò\A™>/
ùó¯-ùRÞßÐ2--³gNÓ°Iîfï}(ˆ’FeÞ›¼€\%/ÈîÒûoòz]~n“Àý;të—»•¦–u,m9yòd®Ëÿ,Æßû<.Û/²vÇE6»Þ@SK›ÖÍýⲿUý·c‚ÓÇXw½LLÔ+ùc;ûz„ì èÁ.|nþM|\o:RСš)3²ƒ€4kaÁŒ¹Cä?'y„?|ƹS¾iÞ:uŠˆ§Ò/oë–~Lff÷B#¨]³žÞw>a#¶Nå¶ÏjøÿÆ®¿&2~úNœõÏs#µäaXçÎ}^[¾üWZ;Ô)’Éàç)Ž9âÊíÛ¹Ÿ0÷)_”ÀΞ=K¥êµÑ7TÌ¡Fò1³ÜhФ=þ¢:³23pÛýðdvóZM]‘S–PÍÌ™TŠTš…Û®ÌãȤ¬_:•”äDþZý3ÞîÇX0ù;¦
kÇi×ì_‹G÷þ‰çÙ7±=º›õK§p/ø:Kga€¦Ì›Ø›»7¯Ù-Y—E²·OêC°¿×½7Eºx!€ã®—ì–V?ÇÙtt˜ÄìiIˆOæ’{îçýØñÏIÜ^|çõºzÚ˜[U'1!Y¾íôqoÆ[F÷vÓøkƒ+YYRb_%ðà%Üy
ÀÕ+Á8w!3#ë2‹²è¨W8[K‡æ?r9Og_`ûïŸÇpY±©TÆÌÉ¿sÉ=€…s6ñ4†)cV+2ì¤R)Ӛʒ9}PUÍÿÑ^ñ½Gcë8/Ùì)ݱkTSþ¼Mƒê,žÓŸ_VÌsjjª,™Ó‡ÓrÝ"ŒŒä·u.,œÙ+Ïõ4}=-fNêÆŒéNùVæaW·#Xٵ˯X”’µ};N8AVVÞ¿¬†ÞBUU
Ur®T­oX’¶Ý"QQáì‘\>ïÊøÙ.üôËfÂîÝäÄì¦øÝ kìý{ýFNçûgñ×ꟉŽ|L¹JÕqÝõfÙ«sGwR¼¤1ɉñÌŸÔ—¶ÝòËz7jÖ³fíÂñìû{5jjj,Þp„Ž=‡³zþX²²2óüÞéIx¡!ÉÊ’2vø¯Lúé;v\@B|›7ºbm[›ÆuèÚ«9­Û7àqx]²~í~fOÛˆÇ9?èÀ¡}î,[¸•Á#:³`Ù=ìÉ¿ݸ
m̘>i¯^Æ3e̺öhŠZƒÈ
©TÆð‹0)S‚¿w9S·~UõžGzz]{6e÷¶Óx]ºÁ†µûyE“f挛Ò“Ò%˜½°èÜqb×®]èhA·ŽÖRþÉs´oe@àÍG4mlöÎ>Í›ÔæzÀ2¾àL.6¨«¦³ÿþ\í?wî†p by£<×YFkÃÍ›xzz~zç\ø¢vï^ˆüš¬o•a	#TÕ‹ñìÙ³<—‘œœøÉI0MZueöŠhëèñâù3Kñ<:Rþ¼CûÞT¯m‰y£æ”)[‰èÈǘ7lFìËhž†‡"•fqéìaZvú
-mÖl÷ ~Ãf¼ˆ‰@WÏP>TW¿8¡·ñ½|šZõ¬ùë°ÿ»w•…ŠŠ==޹^æÞpVý>‰I?õCS³ZZèéjÉÇϲ2¥ÄÇ'‘ŸLZZ:ñqI\¿€ë~z÷k]êYTcÈÈÎÚëÀˆ1ÝÐÒÒÀ±í4:w·§YÅßnâsܾÆÃû8´j@däÙÕ%--k>·)^BŸ_]&0m‚;þ9ɪõ“QQ‘P¼¸ªªª”(©¯èðåþÞ²‰©cÚXùçaeLIMË !1å‹êrÛM|úÚÚ””vïÞÍô	]¾¨¾ÂP¬˜SÇt`ýïëò¥¼/úfŠˆŒ¤D)“|	D™•2*MDDeË–ÍÓë«Ö¨GBÜKãcsŒ×eeeráø^ìZváÙ“‡¬Y0™T†™y#232ÉÞ¬ûX±Ú›»¿ÓÐ"++‰Š
-:ôÅãä~jÕ³¦\Åj˜˜V$3#ƒípÕƒu¬([¡š|
ÉÇ¡©¥Í·í¬_:Ûæ™0ç7ÔÕÿv	ùE"‘°ýÀ|þÞt„©c×’’’Æ‚e£èÔÍþ}+U)“cRÂÏ &ŒZA‡.vÄDÇbV§’ü¹šf‰K’×Ѧƒ
ógýI‹ÖÊwóÂȈ稩«²mË	ù¶Öf¨ªf·"­Ö$+3³:•16)®¨0?êÕ«W\õ½ŽëÖ‘R~tLjò…o-êVâöÝ'Ô¨Z&Ç~·î>¡rEã\ÝócÚµ²`èø?ˆ‹‹ÃÀàÝد>}ËúUå³"‹:ÇN˜½ø'233QSû²Ç_Ô‹ŠŒ xÉ'°ÐÛÞ‘óDäãl\þÃîå©N·]yøÿÕ4
Ãѽòà^ÐG÷)aT†ˆˆˆ<ס­«Y}ï\Ÿcû©CÿòïïóQW/ƶ
‹phß›ßv_fìÌÕ/iLÖ'®ihÕ¹žç\ñ8}€V³—éºâ~”‡!Álv
äçåÛ±¶oKæÿ»	¯]>C]«&,ÞàÆ_‡ö÷æNPѨÿÉI©\8{¹‹Gâéÿ'?ÍÈš_wg?)‘ð±a-m
RSÒ‘Ie”+oŒ·×Mùs>^7©^3{m¹Ç¢ømÕ^æ.Ɍɿ‘ô…¿¾›i9#’“S™»x.›¦â²i*6vu¨Z=ûGÙ¢9[hÕ®q±‰ìÙqÈÛéèÑ£8Ø×CGûó.éÉ­Ónжśõ
»wnÄ’Õ‡IOÓÅ.•ÊøeåA:´þò¸Ž¶Í›ÔåĉÝïðá8vTž¿iéâT¯bÊÅ‹ïŽ;®<§¿””Ò32ÐÖýpÖxüŸ‹'p06ûqø}æŒëA×~?R¾r<Õ{Õó¥ËU¢RµÏ›1•W¾ž§(i\†*5ê}pŸâ%M¾(L™¿©CÛð":‚Ú¹to÷cŒ¹
õbT®^—ÛW	ò»Ìý;øyŸ§~ÃOOÕ51­HI£Òø{ŸgÌôìK*U«ÃËçQ\ó:ƒD"ÉNœ2éi¤¦$ã²p<½†L"öE4YYT¬R4ï"û¶
käsêÖóÍ4mMN½Â£‡Ïhæ`‰ûY?6Ê»(QBŸ³§®Rß*{âΣ‡ÏX»<;¹ÅÇ%qâ膌쌚º*Ã~ì¸Ë14ÔEK[“-Ýp^<‚¬,)SÆ®aÔ¸îÞ‘[70ÿç¿øuíøB<_¦vÝÊØ7·`üÈåÞ	Ÿ+ÁÜð¡ß vœ=yON^táé“h¾ëú3¶vu)^RŸˆ'1ìü÷ý+~,üð¡ýtë`Q`åŸ<ÀäÑä§OìÆÝеžÉ°-QWWeûÞKèëi±ú—ÁùRg·¸>ðÁU,²²²8zä(sÎÌRM¥[KÜÜÓ²eË/*'Ï	,""#ãÜßUôix(ÎãzÒkðD:ô&ßž•™Á­WIKIÆÌ¼:º$&Äò<êiŽ$õôQÅßPF>	ãyÔSj›ÛäXÂ*)1Ž`ÿ+”«XÓ
onžž–JÈmâ^=§RÕÚò瞆‡b`XŠ»ÁרV˃â¥Hˆ{ÅíW?š´ÞfX²ô'°’Ʀ¬Ùz×.qçæ5jÕkÄà±så3<{•Gðñ8NõÚ–,ÝtŒÀ«´uHé²oýïã£fõ
ÍìzËW®ÁOK¶àçum]}f,ù›‹§’˜KÓ6Ý1,aD ïEŠÓ`é¦ãè–ü¢÷V4µ4˜àÔ÷½Ï5hd&o	mø{:Î\ç’‡?Í[ZÒ»_+è„d3DE¾ nýª8öÊN|‰„²åX½~26vÙ‡6vuÙ²sÇx‘””Êo›¢¾E5„>¥u;k†ýЀŸçãïMGˆ}•€añ¢{
_×ͨTåÍ2hü3“=;Îàãu“ºõ«2Ñ©/**žE¾ÀåO'´u4©^³‹W%äîcZ·oļ%#¹{ûR©L¡}§¦¦ræì96,]“¯å—2`Ö””66ÄÊüÍÝØ%	›]Fã}í§Î’’’Îl§´lZ—bÅògܸK»LsžJFFÆ{o¢DÉzT©¤\C9ÛX0àÇ/_G6Ï2_ºt‰ñ“§3o݇§_<}¶0nÖjfëAÿ‘3hÓu€üù”äDæŒë††ú†%¹åÏÜ5{Ñ3(Î=òç!?ô
K’™‘ÁðnõYõÏ9VÏKZJ2%ŒJó2æ*ªª,úýêÅ4غ~!žgS¿a3|=OÒ¥Ïô2‰ÑÌЛ*5롦^Œ«O2najÏ\ IDATÖjl:±pjâ^’••E³¶=¨UÏšù“úbÓ¬÷nù‘’”ÈÈ©‹iìÐùƒïóì‘$FÞàŸ¿?|ž¸Jî|Í·ùq;•¼_ÈìïïÏ ï{ä¹4Ÿ£R¼zö3غ}ÿ{Wæ8vìë]plwΩé—}î²mÏû»èúõl‘“×™íÔCƒÂY¡þ¿S0­=š„„¤<—ñE·S‰‰‰ÉÕ/ó1‘8ï2)o-Îplï_–0föŠíÙ³‰­ëðóòíX4rÀóìa:öŽïåST©Q_~£ÌÊ5ê0fÆ*¦iÍùc»©ZËœ#{þàßc·ÐÖÕ'&ʉ{Zc߯‘èÈÇ´í6®ÿ¿@y÷æåx{ÇÖ!»; š™%£¦.‘—7|ÒBZuîOrb<ƒ:¼;Mö¿Š—â~`L.œ ù-""‚r¦E·§àK”3-IDDÄ{Xdd$åLßTc ¯M­êÙß—®'®‘•%¥GçìKEŠf¯R@·^Ë=]-TT$$&&¢«›÷É.yN`Å‹')!î“û½ŒyƤ¹¿c\¦Îã{PDz1Uke„Þ¹ygO²`rvÿnrRÏž> Uçþغ–޽†sáøžKUYÙ¶’ÿ¿¾u3îß½Šªu,ËÇäŒLÊR±ªî߯¦YRS’زvCoñäafõÉ˨Z3»«03#ƒGîPß:»I[WŸêu¬>ùã_Q¢xÑœ™%ß‚ˆˆÊ–ÉýŠ;ʤlÃQDFFPî=ï»®YyêšeO0zñ’´ô&½5~7¤Ÿº:šÄÅ'‡¾ž>×Bh`QåíÝy\MéÀñOQ*ÒNÝ’l„‰²dÍ.TÊ6ÃØwJ–²ŒìŒ}7c7вd'D‹-;‘Ò­´¯Ê-÷÷G”¦(íæwÞ¯WÜ{ÎyÎs“ûœóœïóý¢'ÒÀÿq‘ñ´oÝ ÛÔðm¿ˆCchnR‘vÆwÞçÜwý_cXK„¾½áé1†µtø¹¡A®}×U!44”:uòŸ8âß
<€‰D"¢#óž«Sߘ֟ŠHZ
Ǫ¹£Yµç§¼…24iaNW묔ÿ22‘MÍ:²Ùy/ŸÞãÙC_eÍ—¦¥eEý$ÆÇPIY9yyÿ5 &ÄG£¬¢ÆÉƒ[8ïö76ƒ'Ñ£ïHn\:ÁËÇ÷2÷“¯ñl¨\¹rÈÉÉ“”‡VÕŒè«üDúEG†Q­ZÁBèAá…†Ši=ÔüG&ÒV!444×mâ`L¾?R×>‹¹ï¹ÿÇALuÜ‹ªŠº:ê¿‰!Úr÷A åË˲d\æÐwèjž<¡I㌜²•¿¶L {'cVorç¦÷3$’tÚµª¾;ö]¤K‡Æ,p>†ew–Ìù%Gtu2î,3€8Œ^$e[H›ý†ÙQQY…-+¦ÐȤ-wo_FE]Ýêu¸qÉ
×a÷åÊ•§}×>lužAËö=³-ôõ<œ”÷IÄÇFs÷Öe~nnÎO
Myð˜×/2œïû\#!.ƒÚ
xýâ!f,1ïÞ
-|®ŸãÇœ¹ede1jbÆUcH¥RÂB	xú s{XH q1‘9Ž‹
GW¨	&”qÈÛÌ;‚ÿ‘¶âà\·½ŸŒ’báÖh†„Fsö˜ÇöÚÓ®U}üqíÔ|Οƒ·ßK"£p;ë‡ï½\_ɾÍ8²k*cì¶g¶¡©®Ì}Ϭ_6w?¦O²bí’¡œ=æHõjš¹žWI©ïßn¹IïÀ”••‘"%å}r¾Ë~”+Wž©ó6a7´#—O¦»í0_§Š¶
Š…{Øš‹‚bEÊçÉô£JLH&,4ŠÚ†ÕJ»+eNdD,jê•‹%`Y&F§mþ—ÖÄÄ&efÿ’r%Åb[GVP:UU9{5÷µ¯Ú:ºˆÃ
VÓ Jæâl}-4>EΖ+'‹ºZ%¢bðòyŽD’ŽE¿Œïb©‚ÞF‘1ëÕ¤qVáI£,4z=«6ºaÙÝ„‘C:åz^qX4ÚÚùdÏM¡b=µµEÄD…££W#×ím»ØÐ¶‹M¶÷Dúµ8t)0óõ§µŒžîLzš$Ç—´T*E¿æO6hšùÞ¢X©)ï‘“—Ï–¾cÏt°@Rb|¶Œuê³~¿g¶LƒÇÍ`ΪÙΩ¢¦É²m§HNŒÏ±Æ­ÏЩÄF½Ëñ9c"˼*óþmKñpÙ‹ªºq1‘ԩׄ©ó7åZ-?Úÿ€Ó1n^¸òeA˜8ŠI£WòâY0Uªª“”øž‘ã¬ùmd¼þŠðÐ(Ž¼È»~nÃóÊ=Þ'§ÐÅ¢EÛ(*͆qÕw+zÕŠ®öÒ÷HNNaó!fý1´DΧ¤T‘ä÷ò½ÿß×pýÖÓáîNö68L´,Ò¾…Gıi§ógìo+)9%¥Ü/`uttyóüiaº‡¢Bö;¸Ü–ÈÊÊШAuV/Î^áY]µRŽ6¬{˜òîÅÎ_~Àa—›45ŸAÈã­(TÈ~ñü6$ìYL¾W¡.ÏtttˆŽ,xÀÏäääs^ÇÿZÏÊ9#é=hB®ÇTPP̽|ЬìW˧|OY•ÜhÇÇFgF.~)ò¸H°ƒÛ—ã{ý<ëÿ¾Æúýžì<ñ€ŠÊ•Ù½N(­Ï >séÐÙŸG{ñð\Ïßÿ,`ÝÊC<¸÷²Àíú?À÷váÊ<¸»^'â]l¡Úø¯GqÆ­ä*ˆtõ	þ®c–ÿñ+±{²ýõààÿ8ˆK׿½ãWˆÃbéêåºM$šw0]auhcĵ›Q¨ ÏOutyöB̰ñ›(_>çÒsÀ2Ü=î`eaʆ忓˜”BBBö©Â´´t¢¢ãÐÒ*\òáB
`5kÔ øÕ³BuàkTÔµ0ÂöÝúKûѺ“ò²MLNŒ'))¡ÐWŸ}ü˜Î‰ƒ›™•¿wŸa€•ƒlçråBFiø÷ïS9hqæþ	ñÉ™¯ÎÙIdD,ã‡;—Èç‰ô‡ÍÅÃeÏG8-:˜ùúÇ4¬~u&6.‰Ä¤¦8î¡qº÷]‚ß½Œ× ·‘Lœ±‹µ›OaÒa&=ú/åÑÓ`Þ§|`Ö‚æôÑŒ/ÞÝëçñúÅCìæo¡YÛn¬œ;:3’ò܉¿èn;Œ_G;²iÙ4"ÃÅH>¤2o¢-uL˜»ú *ª,™>¤È&µû~/06©›ãý¶Œil\©TÊØaË
cúX´`PŸyˆC"HˆObÏvw¢£ãùs»U´ÕY8g'•”•6²'uëé3qZbcd3‡n=[²ýo'>¤J°¿€ÀWb8î «E6lŸÆ«p9r™æ­Œ0m^›~í1ïl²ù{Ñ7ÐæˆûR:woÎôÉE“}»°NŸ¼É_»Î°xåX¶ýåHHpÛ6º¢o ÍÓÇo¸u##ïçÛàwœqó°n5æÍÜÆ•‹~,Y5Ž!Ã-˜6q=w|žò1ý#×®dn”HÒ2_O´ë‡šFe,]"ŸKGGqØ÷݉\¹ñ˜µ›Oeû‰ŠNÀ¸‘wxû)Qóéów‰KBU¥"#&m!2*“¦3À¦ý—ö.–Ĥ6ï:Ç«7ï8´c
ujé0aú.*Èá0ÑÃZ"9
àÔ¹;¸œòæÔá™ìÜ0†¥k\yðèÍ7û)‹ûêrýúõyø$÷)_Ÿ>]¹p0–ÿží½È—;ÑÕQ§[ÇŸñ:—u²vÉPfL¶Ê|x#ukgÌ.9Úõ&ñí_ܹ²ßKËha’=¸~Ù0æ8Øfcj\Ÿ‹Kñ½´ŒÇ[±Ÿ3KþMïg˜˜4Íñþ÷*ÔÖ­[7žÜ÷Î,¬øÿÈïúYlz[å½c>%'%P^N>×+›ÏÌÌ{2wõ!*VR!*"5*DFd¬ñ<œÞ¿N@G¯-Û÷dæ²=™Çõè3]ýÚ42iC
C#‚ŸqßçéééüÔÈ”¨ˆPš·íNЫ'„¼)øt\qJHHBAáëQWoƒßq×÷³£«§E¿¨[OS·‘•aü”¾èhceÛ–·AáÈÊÊPYµ"òòr¨ªVÂã”U´Õ©YGwá1ô°nÍ¥ó>ÄÅfü›4¯G§nͨY[—vŒ	
GAAÅ
TRVBIIUue|n=æúÕ{Xôj…û¥²Qô±}Ç&pYˆºFeBC"ÑÔR%LœqÇm; 'þÉÈÞàrä
–6m‘‘•Åí¸'íûS³¶.],ZЩ›).G¯|ó<ªjÊ”“•EC³dBÛE"ÑwßEÇ$‘íçƒ$
U•ŠôèÒ„#®^üuäÚ“”œÊW/l{5'4<–ºuDÔ©©Í?'ow·³ím¨]S›ÁýÚò*0ÔÕ*!'W
ueÔT+òVÅÑ·••%øáf5¨þ­n~ºËý…ºº:MŒæüå¹n/«Nœ¹‡¥•MÞ;æ¡PAÊÊʘµjÍÝ[—0ëPôsÇeDò;·/sìÀö¼wΧš†
IJŒ'>6*[¦“l¥UÄoX;éééÔoÜɇH?~$9)øØhôke%ßÕ¯‘u·òyJ@A±"’©DG„!•Jq;¼5s›I«Î¤åcý[i0jT‹‡r¼ÿèÁ+bcPRR ª¶F¶„_–=ÑÕ«‚ò§ˆ+yÒÒ>æh+4$’÷É©ìÞê–ù^÷^­Hü”W±^ƒ¬ %ùlÙÈ?›9÷7vn9ÁŽM'˜6a=¶:°Ðyô7/LJBhhSF¯"))ÓõIMù€ì§¨;Û~æX˜Oaþ²Q?r™Í»gŸ„D’FÝzY_²uëUϬ‘ö¥ôôœ¿Ë’R½zuž¿|‹T*Í÷ïØ¦g3&Žêžë¶a¿š³Àù}­ZrõÆcömžÀ[q²²2vÉz¶§'ÒÈ,›¢]E•*Z¶¢¢B¶Ð)“---™1sééi®Üëáº99y:YþŠªzÖ•ÈÉC[ð¹~Ž=~¡ŠNÙ[ëã{ý,¶6ÖEÞî”y›˜ö{¢"B12nÅ3¼®¸3vÆJ*((f”VyQZåÕ³øy] ±Ö¿ŽgߦE$&ÄñòÉ]R’“¾Y³Í¸¹9:z5X1{{Äë²;ï“èÙ¯xŠV}£LšÖŸßúÏ§Ï€Ž¨«WÆÍåéi錛ÚUÕJXÙ¶ÅnÜZ†ŽìÁ«““£u»Æ¼yýõ…÷êê•yþ4ˆ“ǯÑú5×Ã~üZ,,[qdÿ´ªªQ®|Ψ×/©©WæÂYoŒ×"â]×3b¬/žSYµÕª—lHûî­nTþ"YkçîÍ©gTƒWïáuÝŸÏ‚9sò&õŒ²î(ûþÒ	ûñk™5/«ȨñÖ,ž»‹È^<æÙ“7Ì^ø;òòrÔ6¬ÆÜ[iÙº.G.SþSL5ÊD¼‹eïöS…ZÞð=¬{ÛâzÊ—æMó—ÙáÌ…{DÅdüaXK‡}Z###Ão¿´gÉêã\qûȨÏ5y´£¦leæk‚ÞF²iç9y­æ­8ê«çÑÔ¨ÌÓ!ì;t•¶fõéÖg1û£RY‰ÇÏÞ2nø×KÑœ8í—çT›¸õú‡i¾>{iڵߓþI[…¾ÓÖÖÆÐ°.÷¼¯ªÆfx]qÏ|”Gpà4«dŸûMLˆÅÛó,¡ÁYQ]q1‘„‹³„ÆF½Ëöúéon^vËv@B\Þž<ºç•Y‘8¿$Rñö<‹•UÑ=ÿúLCK‡5{/Ñ´e'^¿xH݆&l>ê™’«ÏÐ)´êhÉí«§3×­}Àúü6™¾Ã¦òì¡5ë6bÚ¢mtê5(Ûz½ýÑ­^YÙr,ÚäŠQ“Öøû]§©Y'&ÏÍ»Œyi:²'›wϤR%E‚ƒÂ™æ4ˆÓWסúiMÊŠ
“é;°#¾·ŸÐ QMö»,¤²JEªh«3tDÖ—é—¯71dÌD[_…¢¨XWþT¯ëþØèÀœEÃhÙ¦­ÛgÕœúòõ°Q½hظ6ï¢6²'c'Ùâ{û	R¤¸œY޼|É-"Ÿì0 ÛàõÙï£{Ñw`'ι‚¢<ÇÏ:Óæ‹ÏÓ®ƒ122`iÛ6ó½	vý˜:s ýÐÐTáà‰ET«žqQ´÷ð<´´Ôxñ,ˆùËF1Ñ>£´ªj%–­ÀÛàwH$9§X‹ƒµuo\OßÉ×¾ƒû·¥YÓÚßÜgäŽ,˜Õ³fYÓð«
ÁÑΆÛ~/Q®¤È³©¬¬ˆ–Feì'd+|ùºQ}Ìêϳ—bôµ¸à:—˜Ø$|ï°cݺuüz
3×Ó~XZæý3Ábf-<’ë´eYâs7/ŸFS$í¸œÊ—ÜÜܘ6s˶{hê ·YUÆÏZÃU£,ü3c¡òE÷„pý‚+ÓnǰAölø¯+î4lÚoÏ3X‡ÍàIx¸ìåùã;Ltʈs;¼À—˜è´ŽK§üú9µëýŒ·çY~9óîýxöÐgÇá40nIðëç(WVeÞÚÃÙj‹}‹Ëþ?‰yˆ«Ëñ|í/”SÉŸÿr¹¼”v9•””Ø{–‡÷X½ij±œã[
SN2¦oE:U¹âæ”9÷#{ù*Œv½ó6$4_ß«;´£oϺŒÖ¹zW0í-1tøT†Zè¶ddd
ЫW/4T•ñ<_ðÿX
›¶âí›—ÄFg”%ñ¼àJ›Î½3·?{èËév±îï«Lp\ÃŠçØ¿uYžÏÇ|oœgø”EŒ˜º˜™Ëö øiÁô®usé7Ì»ù[X³ï²åÊqÁýà7Ûú,1>·›p^þß«=$øÿõ‹•‡ÿ:ÃìÁ¥Ý•‘‘‘ÁÊÚŠ#.·J»+Eâï£×±îÝ;ß7+V®aÁ
“RйgãîáGdt*C†É{ç|*š²¡ÀêU+èÓïÌÌ{(ßž¬l9š·íŽ×wÌÌ{N:F™Û_<¨I«ÌŒUEúèÔ!èÕ·Ó¨X
‡ÓXKj6¤y»ît±Lzš„€§÷)_^ŽÛ×Îð.4˜ïfËŒÿ5ÇÿZGŸ>¶~÷çÊ*¥Ý…B›6m­Ìš3~D—ÌèÀÑ»ˆ86î8oþ¦Dš4iB·nLžµëGcï¾_Dd<gîcËÖÝ…^¼ü¥"ÀZ´hAÓ&Æx¸ì¡GZw´æð® •Òªcöyßòòò$%d_瑃²ŠQ¡¤QbåËuiÖÇÑ©×@ün^àòéÃx]rgùŽÓ #ƒÕÀ±èêg…§*VÌ»ü{DØ[.:ÈÓ'ôAñ©]»6ýú÷gñjWV-TÚÝ)°ùÎ.2ƒï:îÏ›iݪk7ŸfÊØ¢O0^I:¶C×òÛÐtíúõ€•‚(ÒTÕÎË—âºCfZ£ïÕàç„¿ÆÃum:eî«×°¯žûó& #_ÝÝ[—xŸœHõZõPVQãMÀ¤?’ž&É(IOOc€VD†‹i×µ¿ŒœAä»deËadÜ’;·.¡S­&:Õj²q™}føù·Þ±œ	ãÇ:‹²@ (sçÎgß!Oƒ~Ì*éÏB9ìr'§9ß}¬’’®'ÜqÞpšs—ïCï¾ßøé{ÐЪÁ¼yó‹¼í"»¨[·.ôgǪLš»)ïþEFV–æí,xþÐj5³mé×b”ý2ÇôB£Ši	sV@A±"MZtÀíÐVFX£¨T‰†&­ùšB¹rå4Æ‘µóÇ!'W˜¨pFO[ÀØ+Y9gclM‘WP¢~ãf¹&êý’·çYž>ðÂåÐŽoî'J––S¦LÅiñöo_ÚÝùn³ÆÎ~yïœ}}}9F[kNrÀÄøë‹ ‹ÛâÕ®xùáuË»XÖIâ—$	íÍ;R»q;zšXdí–¶7OX0¥/Î{`llüÝÇQˆù#D!–^bi+lâ—’““ihTåóúÐDzôËÛä×c7˜½Ä…‡ž ¤”¿:‹_ãææÆˆáÃX·t0lÌŠ¨‡ù“š*a´Ý.üŸFpÒí4ººE_±¾È¢¿$''Ç	×ã\=µoÏÂeè(+âc£qžõ,Ðà%JÖç©´	Ó÷r÷ÁëÒîN¾øÞ
`Šã_œ8é^èÁ2¢Ã/^º‚ã¢ã8-:\b	ºÃ#â0·ZL²DÏë^Å2x}V,åZ555qs;ÁŽUÓóŒ,ëÒÓ$¬š3œß‡ýFß¾e§´‹@ ø¶†
²uÛNzY“Y9¸¬
{‹ÍokÙ¾c
6Ìû€|222ÂÛÇOïP:Û,ÅÿqP‘µýoR©”Ç®cÒÁ‰®ÝûpøÈ±"ˆ¿¥Øê7lØm[7³Âq(ñ±Å_¯¦¸ìX=‹Õª²`þ¥Ý@𬬬5j<½¯!5µl&¨NM•Ð{ðÆŒ™P,™}455¹xé
–6CéÔ{)¿OÜöÝÅ?órõÆcšušËºí×Ùà(óþ˜_"ù0‹üØ¿-wvfóÖÌXº/G`FY–&‘°m¥Qâ—\»z™Šs/é_¥…¼¸å3°ÿgùyö_V\ßEƒ
Dü„£»'•©õaQÑ	ôû}zÕ°wßßÅ~¾øøx–-[¶­[`ÓÛ^ÍhÓò'Êç‘ç37QÑ	¸{Üáàñ[¼|É’¥Îôëׯz;™âÀö8€Ý4Æ9®§±iÛ¼(eq1‘¬túŸêTgßÞ=(**–v—A!H¥Rœœfqôð~N°§žañ=—ɯGOƒ±úu5¿Æ,(Ñ‹“víÚ‰«Ë1Þ¼	¢G—¦ôìÚ˜:5uÐi ©‘}M¬D’NhxoÅQøÜ	ÀõÌ]|ï¾ ƒy{llû1pàÀ"+ê›_%6€øøøÐÛÆ‹þc±°^ìç+¨×/²ÂqcÇŒdÎìÙ¥Ý@P„ØÝÔÉìÚ0
‹Î¥uâ´£¦îdý†Môïß¿ÔúŒ««+çIDATqó†'jJ±ÿÍœ[WN•äé¿*$è%+œ†qtÇ._º ^Á˜©©)Þ>~¼‹«„¡©뷞͵ªvQJM•°òOwM§’(ÑÄ×ïn™¼¨Y³&ÆÆÆèè蔹Áë³½ûÒåË—±³w@òQ–cfS¿qÉ/6ŒŽàèî•x_;ͬ™3™0a<òòò%Þ@P:=z„ã¬é<|xŸÅN‹žÐð5iié:~“ÙKŽbllÊ’¥Ë©W¯^‘µÿÿ¬Ä§ÿM*•räÈfÎrD¤oÈ€QNT«QüÞSÞ'sòà&<\v3bÄgÍDEE¥ØÏ+ʦ7nàä8ÿ‡Xti‚Uwcºuü™J¾»­Ä¤Î^¼Ç‰3w9}î5dñ’嘙•l6ŒÿºRÀ>“H$lÚ´™ÅK–PU¤O³®4kÓ
Ýêù+
ž‰	±Ü¹y‘;^ç¸ç}+++/Zˆžž^‘C üØÄb1'Ožä„ë1nzݦu‹ú4m¬®Ž"muDÚjèŠÔÑPS&2:qhâ°ÄaÑ„„Æàw?ˆ·ŸÐʬ–V¶ôêÕ‘èÇ/®Y•™ì³´´4®]»Æñ㮸žpE¶œ<&­»bÚºuê7ù®:cR©”pñ|oœçÎM^>¹O{óô±íMÏž=QWW/ÆO"~t‰‰‰xxxàïï8$±ø-b±˜ÐÐp¢¢cÐPWC$ÒF$!é¡#Ò£qãÆtéÒ¥ÐëFy+sؿݽ{WW\\NðòÅs•Ь¢ºfUT5´QQ¯ŠŠšÉI‰ÄG¿#6:Œ˜ÈwDE„º†;wÆÖ¦7]ºtÖs	ÁD™Àþ-::±Xœí'44UÕÊŸ®‚²~ªV­JùòEZ-F eÄ7€	@¥°L ‚¢"`@ ø!	˜@ ~HÂ&‚’0€	à‡$`@ ø!	˜@ ~HÂ&‚’0€	à‡$`@ ø!	˜@ ~HÂ&‚’ dòÁç•ÚÆÞD¶IEND®B`‚urwid-1.3.1/docs/manual/images/widget_layout.png0000664000175000017500000004566712615524560021300 0ustar  ianian00000000000000‰PNG


IHDRXwyt5¢sBIT|dˆtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwXTgÚð{½÷Ž"ÅÒ[4–$šl6Å$¦›M¯›Þ{¢1½~›ÞËnš±ÅØ+¨(½÷Þ‡:íûcäÀ83€q„ûw]^áœ÷œ÷<3„áá­"‘ÉH@©hí8ˆˆˆˆÆ©ÌâÑ‚ˆˆˆh¼a‚EDDDdbL°ˆˆˆˆLŒ	‘‰1Á""""21&XDDDD&Æ‹ˆˆˆÈĘ`™,""""c‚EDDDdbL°ˆˆˆˆLLúwoÔh4hiiA[[;:::¡P( P(´•J¥Éd°³³…½½ìììL0ÑXwÚ	V[[
‹QS[+$TC±´´„·7`ffvÚAKD4JEÛj4dg碠°P¯L"‘ÀÖÆR©Ý==Ëåë033ì™3àáá~¦qIR™Ý𬌌()-Õ9'‰
?H$á|kk+REWW—^=b±ñq1pvv>ã@DDD4ÖHevÃäÞÜܬ—\Ú±V~~¾:ÉØÛÛcZx˜ÁºÔj523sþF¸DDDDç†a%XÕÕµÏ+
äå,suu1Z_Kk‹ÁÖ-"""¢ñ`X	Voo¯Ñ²êšƒç¥R©^ËÖ@­­CwK‹†•`98Ø-“IeÆËdÆË†;‘ˆˆˆè\3¬eüü|QVV–Öób±aa!ëÁ*•êoÝ×ç¹ç^†ƒe,_Ѝ¨È3ªÿlikkC]]=‚‚
–§¤AVV6®¿~µpî§Ÿ~FGG‡Þ9Wøx{áÊU×áè‘ýë[·îMtwwãÉ'9£¸;;;QVVà3ª‡ˆˆh"V–X,FbbÂÃBáêâggNžŒ…æÁÅE6`ww7jjj¡T*M°!¯¿þ

ŠFäYgjÏÞý¸éæÛŒ–÷ö*pÿB­Vçžxò9<úØÓ:çâY455ÃÁÁW^q™ÑúZZ[ÐØÔtÆqgdœÀ¥ÿ\uÆõMÃ^hT"‘ 0p2'땵··£±±	MMÍhjnFgg§Iƒ4ä©§¾þÏ>ÃUW]ŽeKÏ”—WàóÏ¿‚F£Á¢Eáëëhllı´Ìœ1[·nƒ¹¹9.ºh9,,,ÇŽ¥ÃÆÆ8pà""f">>¥¥eظq||½±â¢tâ8pàÒÒ2àáé/X*,¤ªT*±ióVÃÑÑsç·‡;’“£±±	¿þöÄb‘^}11³ÑÛÛ‹´´DFÎBEE%d2)BC§êœ+**Ƽ¹s`nn†ði¡:uìÛw™™YHHˆÓ{ßZZZ°aÃ&H¥R,]ºÉ)G+¬¶ßØØˆ¿þÚ‰¶¶v,\8“¡V«±gÏ~Èåøõ·?^°RéßÞ€ˆˆh\û[{j4Ô×7 =ý8¶l݆»ö ãø	TTVŽHr5˜””#˜5GSÓ–~³£æàС@vvV¯¾	W_}Š‹Kñå—ßbÞü% þwßû7¯¹¯®}å¸àÂK±víX³æÔÔÖâᇟijϽ$<ë™g_ÄêkoF]}=>üà?˜7	zzz«¯½¯½ö&äò?‘…¯¾ú½½½ÈÍÉC{»ÉÉ)HI9¢¿™™âc±{÷^ÀîÝ{‘””ˆ¤¤Dsáá¡pqqFyy%®»îáþ^x7Ý|êêêñøãÏ`ûö]BY{{;ââ`ãÆ-ÈÏ/ÄêÕ7cÕªëQ\¬]‚ãèÑcˆŽ™‹CɇQYU……ç-Ç–­Û´Kkde£««ÉÉ)HNN9ã.^""¢ñì´š ”J%ŠŠŠQ\R*$I$¸¹ºÂÅÅŽŽHN9bðº³éÉ'ŸÃ}÷Þ‰Gý7ÀËÓ=ü8öìÞhhhÄÛo¯GHH04
¢c’ðí·?ã›ñÝ·ŸÌÍÍñæ[à,--±`Á<ÜvÛ=xú©ÇPUUuëÞDFz²ÐÊ=Ÿ~ú%n»m
¶lÙ†Û7!"b¦N|×^w5jëêðòKÏ}
óæ%aÏžý¸ï¾»°{Ï>,˜?®n®xïÝ„sóæ%éÝ×ÐЈW^]ô´CBL³"ú[±>þøsNÀ÷ß	@ÛbÓ_Ïý<Œ'Ÿx7Üp- &&
>ú–¦Äm·ÞŒ#GŽ7i
;Á*++GVvŽÁ%,--2žž:K3ˆD"ÓDyIÅK/=+Ÿþyxñ¥µÂ±‡‡»0P[$aáÂù8––ëO–0Éáa¡°´´ŽëëiéðóóºLÅb1-Zˆcié€k®¹K–®ÄÅ_ˆå˗⢗
ºlÅ@óæ%áõ7ÞZ­ÆîÝûðÔ“ÂÑÑ×\s£pnàkì“••ww7˜æÎ#”§¥Gbb¼p1SxmJ¥ÉÉGàîæ†={ºººÆðd"""2lX	V~~²sr
–ÙÛÙ#!!vÐ%F’X,Ö®Ñht½e}åbqOéÀqEb±Xï¸ïþSŸsj]ï¼½kn¾ü±>ú¾ûöüðÃWÃz
11³ÑÓÓƒM›·B­VÃÇÇ8›6oEaaæ
HœŒ½ö¾˜ú˜››¡WÑŸ «Õj‰"‘W\y|¼½„s÷Ý{ç°b&""¢~CŽÁjnn6š\ÀŒáF“«Ñ§ƒ-[¶	Çü±1ѳ…ãÚÚ:?ž)Ä÷çŸ!úo,é1k&*+«››@Û´eËŸˆ‰Ž M^f̘†Ç{~ð6vîÒŽŸ²´´D]]àu÷ÃzþùW””(œOJJÀóÏ¿"Œ¿:Uxx›„˜T*vîÜ#”ÏMJĆ
›„VÈß7lÖ#“J¥HLˆCaAbc£…Ñѳ!‰`ii‰úúÆÓ~Ÿˆˆˆ&¢![°ú@bffGGGƒe½½½£²˜è‹/>ƒeË/FqI)D"6oÞŠMÊÝÜ\ñÀ rö,$'†““#.¿üÒÓ~Ž»»ž}æq,Yº—^z1RRŽÀÃÝ×^{z{{.X
klذ	×\s%`vä,tvvbáyËàíí¯¾üØ`ýóæ%áég^Àm·Þ,œKJJÄk¯½…Ûo¿Åà=ŽŽŽBLW^ùO¤Mƒ³³“P¾jÕåøßÿ~Ãô1
œG8883ßzë5¬¼ør=šŠÉ(..EgG'~ÿý'L:..Θ“´¾¾>øô“„îE"""Ò% Q*Œo[³cçnÈårƒe8ñyË*+«p4õ˜Ñz§…‡aòä€Ó
Ö˜ƒ“1uê89i“‰††FìÝws“…Öž}û⦛oÃá”=ؽg$	·@H0rsó`ii	??_ÚÖ®ÚÚ:̘1
ÐÓӃÇS1gNÿ8¦ÌÌl¤§k—i˜?/Iè",,,Bjjºººª3¶«««™™ÙèììÂܹý-TÕ×7 3+‘3…%ºººœrÁS‚àåå	@»è±cHLìÌžšš†¼¼|ÄÆFжdõ-lªÑhŸ_©T
''G¸ºù£¹©666B}ûöDuU5¼¼½0'1^H¤zzz••ƒÖ¶6$ÍIö˜2""¢‰D*³:ÁúkûÎA—^Xºd± ´wï4·4½oJPBC§žVÀgª/ÁÊÍIÑçŽ%ï½÷"#gA£Ñ`ýú· •J‡=6Œˆˆˆ†&•Ù
ÝEhan1h‚•‹™3§ÇÇgš\@Ee%ÐÐЈú†ÌIŒ?ëåíííuVŸ1ÖuwwcíÚ7 ±±1¸ûnã«ÊÑß3dVqq	ŽŸÈ´GG¸¸8C­V£¶®NèR´¶¶†X,F{{ûàAˆDXºdñ˜™‰HDDDôw
«kÒ$TVV£©Ùø~vÍ-Íz-V®..ˆŠŠDkk<4è3&MbrEDDDãÆ-X€v	‚¬¬”•—ë­³t*L	
öÿ´{¦g7xoÀ¤I˜6-lT%%"""2µa
r¨··55µhkkGoo/Ôj5¤R)¤R)¬­­àìì$Ìx;UOOÊË+ —w@¥RÁÆÆÞÞ^Âì5"""¢ñà´,""""œTf7ôJîDDDDtz˜`™,""""c‚EDDDdbL°ˆˆˆˆLŒ	‘‰1Á""""21&XDDDD&Æ‹ˆˆˆÈĘ`™,""""c‚EDDDdbÒyˆÌn$CDDD4$¥¢í¬?cD,èlÏ©Gde<"Ïa!‘‰1Á""""21&XDDDD&6bc°ú´Ë;ÐÚÒ…R	…B	¥B¡ý¯R…Rûµö¼îî.˜93l¤C$""":##ž`UUÕ¢  xX×j4š³
‘鱋ˆˆˆÈÄF¼kJÐ$Lò…R©Dcc32ŽgtDDDDgÕˆ·`‰Åb˜™É`ee	__/ØÚÚŒtDDDDgÕ¨wZX˜vDDDD&5ê	–X<ê!™Ô¨g7"ˆF;""""“õ‹ˆˆˆh¼a‚EDDDdbL°ˆˆˆˆLlÄ×Á:Síòtvt¢§§b±æ°³³…™™l´C#"""p%Xee•((,EWW—^™H$‚³³#‚'ÁÙÙq¢#"""ê7æ,…B‰ÔcÇÑÐÐdôFƒ††&444ÁËÓ3f„B"‘Œ`”DDDDýÆô,¥J…”Ãiƒ&W§ªª®ErJ”JÕYŒŒˆˆˆÈ¸1Ý‚U[[/|íëãoXXX ««ÅÅ娪®5x_ssŽÏFDÄ´‘
•ˆˆˆH0¦[°úbÆŒPØÛÛÁÜÜöˆˆ˜oO£÷TU×¢ººîŒžûÒËæÌê0…Ï>ÿ©ÇNÇ
…/¾ôŽÁsÍxýÿॗß5Zßü…—áDfîÇUQYƒºúÆ!¯{쉵økû>ƒe˜6]]ÝgOQQZ[Û…ãÖÖvL
›…B1¬û;::±go2Þzû¼ûþ:e*•
+/¹	Íg'c>Á²³³EP ¿Á² A·ÚÉ/(>£g¿øò;¨©©ú³ìhêq|ñåOÂqjê	¬[ÿ¡Þ¹·ÞùvˆˆGTÔ£õUVÖ §§÷Œãzmý‡øüó½&;»lü$,W«Õ(/¯‚F£9ãxn»ã1ìÞsèo×ýÃpÿÏaÃÆíxï”K"‘༅‰X»îƒ3Ž“ˆˆÆ¿1ÝE“ü} ÞNÇÜÜn®.¨©5ÜÊÔÞ.Gss+íÏ8ެì|€T"Áî=ÉðÅ¢óæå[¶îBB|:в²*,:/~B¹Z­ÆöûQXTŠðÐ`$%ÅèÜ7É)ÇPQQo¸BçÙs“bðÊÚ÷…ã½ûRpíêK±w_ŠÎ¹„øÙH$ðpwƒJÝ?­³³7m‡R©Âù‹ç꽶̬<ìßAAþ˜6-ÙÙù˜77N(?‘™‹C‡RáèhåËÂÒÒee•()©@kk;~ß°
®ˆ‰ž¥W÷ÇŸ|‡+.»H'nhh¦Í;aggƒøøÙz÷TTÖ`×®Ðh4X¼x.<Ü]hg’ÖÔÖÃÝÍ;v€‡‡+–.™‘H„̬<466ãð‘tÀ” xxhïëîîŦÍ;ÑÜÜŠåËÂÝÍEï™pã
WàÆ®ÀÖ?wãÞûŸÕ+¿òÊ•ˆ˜½O=y/¬­­ÖADDœ-X..Nƒ–;:
ž<ÕŸÆùÁ|õõϸýŽÇðØ¯¢²ªwÝó”NkÆMkÄu7܇­[wãø‰lÄ%®Dqq §§‹—^ÿûÏ7hkmÇO­Ã}~ß´æA\½ú.ü¾a**«õž”‹œœB¡;nÏÞd¬ºb%ººztÎÍMŠ|ûý¯øâmëVWW7œw~úïF•áºîCGgÿR[ÿÜ¥ËW£¼¢
¿þ¶W]sþýàBù+¯¾‡k¯¿õõMؼeç^‚öv9êêQ[[ŠÊ¤NC~¾áÖÂ
ÿ‚ùñÂqyyâVâPr*Ž¥ebÍ-é\¿qÓ,\tòò‹‘•]€9Iÿº3ÿÚ¾ÿºíQÜvÇ㨩©Ç˯¼‡Õ×Ý(-­D[»%H9œ†òŠ*¡Î[o‡¤c×îCˆ‹_öv¹áoòÜ\áë㥓Ø2¦[°ÌÍÍaii1è56C´$´´´š,…B‰Ÿ~ø"‘s£qÿ¿ŸÃCÞ&”/Y2·Þr
 ¹¹
?ÿ²ÜþïãoagkƒŸ~øpÇí×!$|î¸ý:M,]ºwÞ~Áçzz¸!(höíKÁŠ‹#-=³gOÇœÄháÜÁC©xò‰{ôîýêëŸack¿×&ƒÇÒ2‘˜t‰Pþ̳¯ãµµOàŠË/<üèËØ±c? °°o¿ûާmÖ»öúûðɧßãÞ{nFll¼<ÝuÞƒ›QQQààɹ7ÞüK–ÌÃ{ïh“¸ï¾ÿMŸ¥R©p÷=OáËÏß@bb4ÀÛÛ/½ü¾ýZ;¦¬¦¦»wü;;Üuçõ	_€ä”4,_¶o½ý	V]¹+.Z|ò{ ýÞßpýX¼(	˜t	þÚ¾—\¼Ä`ÌC	žŒŒŒl,]2ÿoÝODDØN°¬¬,‡¼Æbˆ¬»»ÇTá >n¶Ð]0ÉWo}€€þòääTÔÕ5`Í¿ÊE" ;§@H°¢}öܤXìÙ›ooO„†L™™æ$FçD""
ÌšLKÏļ¹±ÂqĬpØÚZÐ&4'2ó©óú¬#G3 •JðÈc¯å……%°°04Ö>ryÀÎÎF8w,=·ßººÿu
ˆ­¬¬Õ5uøüËÿâó/ÿ¨«k@iY¥pͬ™áB}66Öˆ™…´´LÄÆèwO|M}|Q]cxöépØÙÙ ©ÙtI;Oc:Á²0úùPŠ*JS…©¬ÿí‰Dzƒ§eR™Ñò˜èYXµj¥p¼ææUœÜ?x¨×:7)¯®ý¾>^˜3GÛº3'1o¿ó)|}¼„ñW§‹ÅP«uãì;‹ÅɤèííŸe×Û«;øÝÝÍkn^¥sÎÙix«åÛÚj¡¶6¹ÐÕ{j<šSb“H$¸ñúË!‘ö¿33³þë5ú¯e°‰ “
üß\ÿûv:ÚÚäð÷óþÛ÷ÑÄ0¦Ç`‰Å†·ë\cd|cäGRÒœXK;¨Ù3=1ѳ5NNï#)Ù9øù—͘s²û,0Ð--møù—ÍÂø«SÍŽœŽ;IErJ:::hߛĄ(üüËfáú_~Ý"|Ò²J¸º8	qÇDÏB@€/ÀÒÒõƒ,Óàää??oääç¢"§cûÉ2:_OšäOO7ÔÖ5èx7¯y›6ï„‹‹òó‹àíí7_×¾>_O\yõˆ‰À–-»°rÅùB×èòeñüoá¯íûpÉÅK°x‘þŒÉÁdeçãÞûŸAsS+jjêpþ²«1%(@/V[×€ªêZ!Á%""2F@£T´Õ‡HevèlÏ3Xväh†ÎŠíy{y`Ö¬ðAënk—cïÞd£åžü{+ºïÝ›‚ˆˆpØØX£¸¸
0y²vé…îî=š!Æ>xð(fÎÆ•””C¥R#pÀ^‡¤#?¿VV–ˆ€§‡›Á{ÉÌÊCGG§Îr¥¥(-«DbB”ÐExê³{zz…–¢¹I1ÈÌÊGXhÐ…×ÕÕܼ"Lò÷Á?nÀŽûñÃwýËB”•UâÈÑ(J„„bæŒþœúú&–ÀÖÎáaÁz1çåáŸWÜŠ´£[„„¹¥¥
;v€““=âb#‘r8
s£…ò¦¦J>†ææLò÷All¤R)>ýìlÚ¼ï¿÷öí;G½–»ŠŠj”–VÀË˾¾^8pð(’æÄ-™ÙÙ°³··—‡^¬mmr¤¥gꜳ±±¸·ßù5µ
xé…‡ôî%"¢sƒ•m0F"ïõ+åpšÑn77DGÍ´îúúF¤N3Z>mZÇÌ¢  ‡’S1mZJJÊñàC/àõõO㢙ì?¹æ'è¬öwô%Xÿýñ#E6|*•
ÿøç-øì“õ§ÕµKDDcËH%X£ÞE8Ø€ãÎë5#?9žÈ˜¡Öњ謬-‘r8
?þô\]ñö[ÏaÙÒ&}Ƌϛ¦ÅÇÃÃ
ááSMR×é’H$øí—OFåÙDDtîõëÔnuttB¡Pž2LWs“ñ)ó®®Î°ÆR™—§;Þ~ó¹ÑcX–/[€åËL›ü
£>‹P­V-Óh4FÇgÚn›ÁfO	8£ØˆˆˆˆþŽ1`@aQ©ÑkJJ+ T^ç*((g¾!Ñéõkà"—€¶[Ïýäæ¾€v5ðŒŒl½$«¡¡	yyEëôôpgëšQƒ5påp+++DÌš‰DŒcÇ2QS«Ýj¦²ªM-ðôp…D*Ekk›Ñ®Á  L°÷ÑHÕ«»»Gh™’J¤ˆŠš!hŒœ†¢â2äçC¥R¡»»Å%åFërvvDhHìííF$v""""cF9Áê†ùÉ}æ¦O­µP&‰8Ù¾>^¨¬ªAScºº»ÑÓÓFss3XX˜ÃÙÙ®®Î:÷¦Q_h”ˆˆˆh¤ŒÔB££>Ȉˆˆh¼a‚EDDDdbL°ˆˆˆˆLŒ	‘‰1Á""""21&XDDDD&Æ‹ˆˆˆÈĘ`™,""""õÍžMÅÊ6x´C ³Fb'…ñò3È÷Šht—_ÆM‚õÕ¿vDcŽ«çŠ{Ö¹þ3È÷ŠhtäÏàÙÆ.B""""c‚EDDDdbL°ˆˆˆˆLl\Á"¢ñA¥RC¡Pjÿ)UP)UP(•P)µç•*”
”*
‚§øÀÖÖj´Ã&"0Á"¢1§¸¤•þ> Àó,FCDtúØEHDDDdblÁ"¢1ÇÓÃv¶VP©ÕPô*QZV‹ŽŽîÑ‹ˆhؘ`јcmmkkãÃGrG1""¢ÓÃ.B"ó¬¬,†¾ˆˆha‚EDcžX,툈N,"óDb~Tѹ…ŸZD4æ‰À,":·LøAî
…
MMmhll…¼£½½J(
@$‚L*…••9ìì¬ááîËÑ—ˆˆˆÎ6ÁêììFII
**ë¡Vk^Óäò.ÔÕµ   ÎNvžê{;뎖ˆ¨_ww/êë[ÐÐØŠîî^ôö*¡V« “É`n.ƒ££-œíàè`;Ú¡MX.ÁÒh4(,ªBaa%4ò*''[ØÛÛ@$š›ÛÑÜ,ʛڜœ…™3‚àîî8‚Qo==
¨Tj˜™É “IF;¤1©µ­ùùhhh5XÞÓ£„\Þ…ÆÆ6TÂÖÖA>ü¼"*ÁR©ÔHMÍCcS›^™¹¹3gN“£î_|騮nŽÕj
Ò3
νψ΀R©BUUªkÑÒ"×ùƒÇÜBwWGøû{謇5Qi4äT ¸¸ZïC™LwwGXX˜C.ïBmm“pM{{Ž¥åÃ×Ç¡¡“8“hM˜K£Ñ -½À`r%³fMÑkNW©ÔhkëÔ»^­Ö ¸¤3¦žµx‰Æ³êšFää–¡§[a°¼§[²ò:TTÖcJ||ÜF8±C£Ñ =£55MzeööÖ˜33™p®©¹GŽäè}(¯¨‡B©Â¬™A#3M Y„yù¨¯o1Xæííbp¬B~A…Ñí9
7ÑÑàòòË‘ž^h4¹H­Ö 7¯#ÙØ”_Pa0¹’HĈŒ˜¢“\€“£-üüÜõ®¯©iBIIÍY‹“ˆtMˆ,¹¼%%ÕFË}}õ?Œ ²²Þè=½½Ê3Ž‹h¢ÉË/GQ‘áŸE‘
ò†¯d2)ZZåÈÉ)CkkJËjG8Ò±¡¡¡Õèûåãã
ss3ƒežžÎ“©‚Â
x{»rŒÑ˜-XÅ%ñ!Í
 IDATúãú˜[ÈŒÎ
l¼‚¥¥¹)Búõߣ¦Vÿ¯Ó‘öÕ7"=½@8V(”XûÚwÏ55µá÷þ‡uë¿7ZßÒDVvÉÇUUÕ€úÃ-=óÜgعë˜Á²ŽÎnDDß„®®ž3ާ¸¤mmÂq[["¢o‚B1¼„» °ï¼÷?Ü}ï[xêÙO‘™U,”©T*\qÕ3h2Ð=46O`Ú´Éœì
33D"l
{û‰;k77¯Ìh™»»“Ñ2[+ˆ||)•j”•³‹h$ŒûK¥Rl^ïcce|m+wg£e~FZ½N׺õß¡¶¶Ù$u‰´´||ýݶþãô¼õÎOzçÞÿðWØÛ[cæŒ DFL1Z_Uuz{†îÊ›ïü„¯¿ùsÐkrs˰ek2æÍi°\£V£¢¢Þh’}:î½ÿìÛ\8V«5'ë^å<ö*+
3™K–ÿÉ)Y‰D‚ysgá·~<ó@ÇFƒÌ¬£åNN¶ðörÑ;/‘ˆ1}Úä³ÙØU[ÛŒöö.£åvƒL²‹EÉwPöyHD¦3î»[[åP©ÔFË-­7±@p°/z
Y„b±“&y`Ò$“Æ	9¹¥©D‚}ûÃßßæGåÛþ:‚¸Ø0$§d£¢¢óçG`’jµ»v§¡¸¤!Sý‘˜0MçÞØ˜P>’ƒÊª\{Íg'$LÃëoöÿb?pðV]¹žÐ9
‰Dw7G¨Ôýïkgg¶ü™•R…óÎÖ{mÙ9¥8t(“'{!,lrsË0'q†Pž•]‚Çs`ï`%‹c`iiŽòò:”•Õ¢­­›6‚»»#fGNÕ«ûó/·à—̃xÀv*mغ-v¶Vˆ‰	Õ»§ªª{ö¦C
.˜
w7í4öòò:ÔÕ7ÃÕÕ»÷¤ÃÝ݋ϋ‚H$BvN)ššÚp458ÙKhEèéQàÏm‡ÑÜ"Ç’ó£áæjxZü÷ß<
©´¿{¦²ªÿûe7bc—ýs>’nÇ#_ëq´ÁqMm:;· úúÄncc	gg;46ŽÏ–=cjëŒ'BffRÿ1–ô··w¡W¡„™‘ŒˆLcÜ·`ÉåÆÿ©Ôø‡ŒD"ÆÌA˜›4‘S5óçG xН©Ã|÷ÃvÜ{ÿ»xú¹ÏPUÝ€?ô¾NkÆíw½Ž[n[‡¿¶Á‰¬b,XtJJµÍý==
¬¸äQ|úù&´µuà¹>ÇÃ~¨sïk^Ŧ͇PUÕ ÷ìÄ„éÈË+ºãö8ŽË.î®^s‰	Ó?þw'¾ùVÛºÕÕÕƒå=ˆ_~Ý‹â’ÜrÛkèììŸð×ö£¸øÒÇPQY
àÆ›_ÁcOüG(_ÿÆXsë:Ô7´`Û_G°héýË;ÑÐЊººTU5àÈÑ\T|ß6o=„¹Iý­Wõ˜¿èn>œôŒBÜy÷›:×oý3ËW<„‚ÂJää–aÑ’û„îÌ»á®{ß½÷¿ƒÚÚ&¼¶þ{¬ù×:@Yy-Úå(,ªÂ‘£¹¨0FïîûÞÂÑÔ<ìÛ—çݹ\ö)½_Šmm°µéo‰puq€·+8qê­ç´ŠrããE"ÀÅÙaÐûûà‰ÄØZW}ÊËëŒþËÊ*B¡2zïPŸ‹DtæÆýŸ0=½ƒwSI$Cç˜VV°¡Ö¥R‰¯¿x"‘ñqÓðèãá¾{.Ê…›n¸ÐÚ"Çïöáî;ÿ‰Ï¾Ø[[+|ýÅ€[Ö¬@dôM¸eÍ
Nö,^…­Yað¹îNœì…Oà‚eñÈ8^ˆˆYS.œKIÉÆ#]­wï÷?l‡%¾úüq@zF!-¹O(镯ðÒó·àÒÌ<õÌ'ص;
PT\…>ú)>‚““6ö[×á‹/·âŽÛ/ATÔTxz8ë¼55µ¡²²S‚|„sï¾ÿ3Ÿ…×_»ðÓw
ã³T*xè}üçÃðòtÁºõßá³ íšÙºñ5ØÚZáÖ­DdôÍ8r4KÇàý~Ååÿ\€åËâ--Ú…hW_½Dj¿GKïÇÎ]i¸èƒ1÷Ù¹ë’S²ðÚ«·ëœŸäƒ™EX¼(jÐûÏjµÍ-íFË­¬,†tmï`cê°Æ4…B5èDšÞ^å ]®CÖÏI:DgݸO°D†Fz Tÿ+o4ÄD‡
1ûû»ë—èëJÒ–{å‡ä ¾¾wÞÓßZ#‰—W&$Xq±áƒ>;1a:ö8o/L
öƒ™™ñqáÂ9ˆD˜9Cí¯Œã…:Ý}3g
û6ªT*de— &º¿›.6&LH°ŽˇT*ÁSÏ~*”Wu*y‡ö/q[Ûþ±tÇqóM
Çc+¯¨Gmm¾ùn¾99¾¬¾¾eåý³ÔfL‘µ±¶Dôìdd!jvˆÑ8bc|_üÜQSÛ8hÜÇŽåãÖ;^Ãÿ}ð <=uÇúÙÚZ¡¹EnäÎsO[{‡Ñí¨€áM±²?Ý¥ÃÑÓÓ{VëW(™`mã>Á2?e˜SõÑÂ5Ò¤ÆEˆD"½q²]LÚòþ²Ù‘Sqùe„ã®[†€IžÂ±…ùàïEBÂ4¼ñæOðñvEB¼6‹ÇþoWaüÕ©Äb1ÔjÝqnš“¿PÅb1d2)z̲;µUÑÍÕ7\·L眣ãðöPëë^ko³ìä3EÂóèÅ&‘ˆ±úêóuºë>õ=WkÔC®€­ó}hÐõé…XµúY¬{åv¡Õk ööÎAÇ$k†ZïÊÌlè!©tÜfÐ1Ô¤	KKsDÍÖ8\§®ED¦7î?µ,,o	1¶¨1µµÍc®å+1~:Ò3´Ýz³#§bväTDF;Q´-X¹yeø}Ã~ÄÇiÈOðBK«¿oØ/Œ¿:Õ¬YS°{OºðKáÈÑtœƒ%‰†ß7ì®ßðÇ~áëè蔕×ÁÙÙ^ˆ{väTað¾¥¥ù cQmáëã¦3=bÖ¡…víéÿÚßÏN¨¯oÑyÞ´ðáš´ô47k»´ÚÚ:r8³´3&­¬ÌÑ0Œe#Œ9‘YŒ+¯z/¿ø/¬¸(Ñà5¹yåãjæÜPÄHÄC‰D"ƒËŒWæCü1¤Ö¨ammñ·ÿq,¢³oÜ·`9:ÚB,í¢Ë» Tª†œ‘Ó§¾¾ÇÒò!‹ÆÔ¦Ï×®^‚í;Žâüe`Nâ´·wbï¾lÜð*\]@ܧoVfV‰NwX|l~ý}Ö¾r›Áû®¸l¾ùv.½üI̘ˆ™Åp0fæùgoÂeW>]»ÓÐÑÙЩþ¼Ÿ¯;{ä\¸ò\tA$1Ò3
pÅåqÍUçã¼³qó¿Ö¢¤¤ññá¸óöè=ù²8ìÝ—Ž„xmRxÇíÿÀ=Œ5ÿZWWE"Þ~ãÜ~רúça8;Û¡ °^^ÎXû²öõùx»âú›^BÔìlûë.\™'·Y²8¯¬ý;vÃE&à¼ú3&sÇ]o@©Ráó/6áó/6fG†à©'®ÔÕ7£º¦QHpÇ…!#õ0–¸Ðh4&Yfã\af&ƒ™™Ôè8¬ÞÔj
÷$ÃD4JÅÙþ,•Ù¡³=ï¬>ÃÊ6õÕ¿,;ššgt›˜1c2¼<õ×à1äPr&ZZ: óæÎ²…l(ûœÀÌ™°±¶DIi
4Э×ÝÝ‹ciùÂ`ìä”,LŸ++형Ҳ¨TjLðêK=–‡‚‚JXY™#**'—8õ^c²sJÑÙÙ­³BYy-ÊËë&tžúìžvíÑ$OL˜ŽœœR„LõƒÍÉ.¼®®äTÀßÏÿûyví9†/?{\xFyyRÓò T¨ì«Ó‚ÓÐЊ¢â*ØÚZ!4Ä_/æ‚‚
\sÝ8°÷}a©†ÖV9vïI‡££
¢£Bq45ñqáByss;RŽd£¥Y??wDG…@*•à˯·bëŸ)xóõ»pð`&œíôZî*+ëQV^OOgøx»"9%	ñÓ„±s¹¹e°³³Ö[Õ÷ý9uÁSGG[„…N¼ÿᯨ«kÆ3OÝ0è÷ét¸z®8ë?€ñŸÁÚÚfKË7zŸ»»£ÐBhL¯B‰;R–ÇÄ„êmÔþwŒö{5ЉÌ"TTèÏøí“?
vvÃßp¾««YÙ¥ðõq…««ÃãS‰FÃHüZÙc$òžqß‚^ƒ&XEEÕðôpò§¼¢--ÚU¼=<œÎ8¹ ³VÕÀ5­m÷f_rèp?ýµ¸"#‚¬wþÔ{1”Àøùºë-¬zê³ÍÍeX²8F8ØVXT…dz6	{÷eàÍw~«/ýKç~__7£ãŽ\\ìáâbo4æ  ,[‡Ý{Ò…uÃìímtºàNM’muâ=•«‹ƒÑ.ª16«×Tœíè…ÂÂ*ƒå--Øà8B¦úÁÍÍQgÐ{OO/ŠŠªQV^+|†…„øñC¢2a,0“I5EÅU(.®†R©¿G¡F£í2”CẇX,Bh¨?|}˜\.GÛA÷¬®n„·«°š…B…'Ї¬àÖLãÍ” ¨TjÙ°uw÷"-½R©ÖÖ–J$èîéÕëZžâÃÏ/¢4¡,@Ûì8Ù~¾¨¨¨Cm]Z[;ŒN—JÅpww” “j'š¨fLŸŒýN\z@£ÑÎö
	ñƒ§‡3Äb1ZZÚ‘•]ŠŽŽnH¥bØØXmÉÊ/¨@ggÄ'·¾òóuÖ
ñ犩~pu±Ç‰Ì½Y¨}”J5Z[õß33)ÂB'ÁÃÃél‡IDL¸«L&A@€'<¡P¨ÐÞÞÞ^%
% dR),,Í`ogÍéÌD&`nn†ÈÈ`¤¦æL²T*523K™Y¢s¾o͹šÚ&£	–R©FiYÿvG®®ã*Á´Ý…s“f ®®åäŒævƒ­ð}¬­-àíå??÷a¯óGD¦3a¬d2‰^×™žƒ½
âãÂqâD±Ñ±™›K1sFœœìPSk|×D!‰àîîwwGh4Èå]èêêB©‚J©‚X"†¹™¶¶Vlq'eL°ˆhDYZš#::uuͨªnDCC‹^KŒµµ¼¼œáçë!Ìsws„­$R1$1¤	¤R	Ä'¿–HÄH%J$b…s‘H[[+acr"[˜`Ѩpss„››¶%F¡T¡·G‘Hss™Á.-7·±±-Ñp0Á"¢Q%‰`&“ÂLÆ#"?†ÞÆžˆˆˆˆN,""""c‚EDDDdbL°ˆˆˆˆLŒ	‘‰1Á""""21&XDDDD&Æ‹ˆˆˆÈĸ²Ñ8äê¹b´C q¨¾ú÷ÑáœÁ‹ˆhœ*Í[?Ú!Ð8âüÀh‡pNa!‘‰1Á""""21&XDDDD&Æ‹ˆˆˆÈÄ8ȈˆÕÕÕ‹¦æôö*¡P¨ •Šamm'GkÈd’ÑhLb‚EDD54¶£°°mí]ËÅb1¼½8Ù‰Ñ)˜`‘µZ™•¨­kÕ+37—¡·W	FµZòŠF45w`vä$˜›ñW
QŽÁ"""Z­FjZ©^r%“I€9	S`mm®SÖÑѼ¼ê‘“hÌc‚EDD‚ü‚Z47wèõ‚££5
Šê —wë•×ÖµA©TDˆDç&XDDhmëBYy£Þy33)\]lUU-ïÕh4èéQžÕøˆÎ%L°ˆˆ&W`cc‘HP©Ô¯‰D0ã,",""‚J¥68¨$âþ_n®¶¯qw·çLB¢˜`ZZ;¡Qk–I$"áë©^pu±Ó)wu±CèTϳѹ†í¹DD„––N£e"q‚%“I0k¦äònt÷(`meKK³‘‘èœÂ‹ˆˆÐÛk|€º"½s66°±±8›!ÓØEHDDP(¸Ä‘)±‹ˆhª®iAgg¯pÜn`m«e…Euzç]œm`oouVâ#:×1Á""š€ªª[ÐÔ$Öµíí]h7°¡L*a‚Ed»‰ˆˆˆLŒ-XDDPÐd7ôø8	ÇÅÅõh3ÐJŽŽÖðóuÖ;o{¹ñõ>ÄÅbjðè.û°åÏã°²2ÃÜ9S…s¶ÁS<ôÎ-œŠìœjìØõ¯\i°¾»îÿ+.˜…ÅçM;£¸ššäèêVÀÛËñŒê¡³-XDD½½Ü\í„ææ2£×ZZ˜é\Û÷ïl,ÏðÅ×û‘›WcòzOWYy#>úx§pÜÔ$ÇÚõ›ôÎ=ÿòï°´4Ã$ÌI˜b´¾††vtóöwýº!ëßÚrÆõÐÙÇ,""“**›P_ß{ìÚ“{{+,Y4
‰¶m 9¥“&¹¢¬¬Ù¹ÕˆŠ@X¨—N‡#+»>ÞNX0/b±î½¥¥
ÈÌ®ÄÕWÆëlõˆõomB¡‚L&Á¡ÃEX¾töìËÓ9ççëO¨Õøû¹÷«Tjlß™…¦æÌ¬÷ÚÊ+š°w_.\]mˆ”ÃEX´0\(/+kÄÁ”H¥,œ
GGk47w '·UUÍØºí8¬­Í1'A¿nÆU‚åê¹b´C ""Ù³/Ÿ}¹^ž˜>Í_}síÈÄ믮¼¼îXY™ÃÃÃÎN6X»~þïý¸çoPQÕŒsCðÕ·ûñùWûðå'k ‰ðòº?`n.ƒ››<ÜíõöXõ†L*AZF¢gàPrâ§ ¡Q®s.>6°ÿ`>6nNǯÜ|Û§h—w#)1<ü=*«š…ºsr«qåµ`å…()kÀç_íGÊáBäg®üòûQ¼öÆfüceÚå]X÷úfüøÍ퀊Êf46uàXZ)œm˜`aã&ÁêlÏ툈ÈÄššäøý¿÷ÀÒÒWü3󿌵/^©T»ïað<óÄÅs~ùí(â‚°cW6NdU`ë†C*•@£Ñ`ÙÊõع;ç‡
÷>ÿô?>W"#&j2%h“©”BÜtý\ÔֶꜻuÍB½{÷íÏCVvöíx2™׬’#~îóBùÛïoÃuW'â¾»—¾þîR:;{ðôs¿à¿ß݉à);[K¼ÿÑv¼òÂåX´0'²*ñȃšè¦³eÜ$XDD4þ„‡yc½|}œ R©ÑÔÜ7Wí~ˆÑ³„k}}q¢p$µJ¥=þ£PÞÑÑ‹¼ü!ÁЉê¿×¸Ø@ìÞ›‹kVu µµþ~.ˆ‹	Ä›ïnÃ5«:›W#´`
t"«Q³„ͯl„d	²²«pÕåqÂñÀ×_P‹®®^|8`¬WUU3z{¹칆	Y}-U}D"‘ΦÔRiÿ\-±HõÉ2‘H„ @w¬^• ”¯^•Oáxà˜+CúÆaí;‡è“ÉØŒ~8‘U}ò„ñW§‹Ezgk4ýÇffôX9à×"‘ææ2\³*Agƒ"î÷xîa‚EDDãN\Ìdü÷çÞâkksá¼Z­ä.]}ã°>úd®¼,`n&ÅÔ)øè“][¯`æt_üß'»ÐÝ£€…¹µumÈÍ[Ò…–´›Ó…²à`H¤bôô(¤·…¹Ã[ –F,""w’§bÉâiX~ñëX´0½½J>Z„ž¹Q‘ƒw
ö釵}W^_»J8ˆ·ß߆¯›kð¾Ø˜@ÄÆâÒ+ÞAÒœ©H=Vïþ5Çî¼m®¾þ#\té›°°!Àß²“­iæ2¬}ñrÜuß×X´0¶¶–È˯Ax˜7º9âã‚ðêë›pÍ
aj°'ž|”“»Æ*RÑvV"•Ùq:Ñ8geŒúêßG;Œ3âê¹bD>«Fâ½rõ\Ò¼õþ>-½õ
†xy:"<ÌÛT¡
*=£ÞÞŽpq¶Em]ZZ:t=”RˆÈYþ03“âDV¼<àäd¨ohGCC;BCú—jÈ˯AVN¤1¦…û`’¿v)…Sï5¦¬¬U5-ˆž‘HÛi×ÐØŽü‚ZL÷ÍÉÅVëêÛÐÔÔ©ÚX5
öÈG›¼qÑ“QSÛ77[¸8ÛÐn®]PXWW[dçTáÙÃ_›žÛÐØŽÔc¥h—wc’¿"fú	KL´µw¡°°"‘³fúÑû}:üƒ‘ÿoÏöÏ •m0F"ïa3gô'învpw³Ó)‹éï–›æ£SæêbW[sÁSZŒÚÚV$&LAôìÉB}66øè½ëq(¹±1HJºç%…`ûæ‡PRÚ té}üÁ
ðõqî¿âŸ±FZ}ùùËp4µuõm°³µÄËÏ_w7;¡|מDÌò‡Ÿ¯³Éß'û˜`јagk‰»n[„›ÓqÕÚV,s-®™1IçKK3Ì3Õh¶6X|Þ4ƒe.ζpq¶Ž£"u×÷˜lªwà:X§Ú¸9Ý¿Üh9oL°ˆˆhLY}U¢Iê±³µÄ¬~&©ëïxcÝU£öl}äNDDãRX¨“5L°ˆˆˆˆLŒ	‘‰1Á""""21&XDDDD&Æ‹ˆˆˆÈĘ`™,""""c‚EDDDdbL°ˆˆˆˆLŒ	‘‰1Á""""21&XDDDD&Æ‹ˆˆˆÈĘ`™,""""c‚EDDDdbL°ˆˆˆˆLŒ	‘‰1Á""""21&XDDDD&Æ‹ˆˆˆÈĘ`™,""""c‚EDDDdbL°ˆˆˆˆLL:ÚÑÙáüÀh‡@4a1Á""‡ê«íˆ&4v™,""""c‚EDDDdbL°ˆˆˆˆLŒ	‘‰1Á""""21&XDDDD&Æ‹ˆˆˆÈĘ`Ñÿ·w·ËM\w‡ÿ+i%Û²cƒÁ`“)yù—ét:½‰ÜL®£7“kh“v¦Ihš„„°%ÛØ–_õÖ”¤Æ’›–ÂÞçùfíj}ðŒG?ÎYŸ%1˜ÀHL`$&°X‰	,€Ä@b 1˜ÀHL`$&°X‰	,€Ä@b•Q€ô.\þxÔCàj<útÔC81À)õÃí?Žzœ"×n|2ê!œ(–X‰Y" ‰~¿Ë+›±¶ÖŠýýNŒWcáÊtLNŽzhðÊ	,^X«µ7¿ú)¶·÷½¾´´¾¿³³S#Œ†Ààˆ~¿ÛÛû±³{v7º½~”˥ˣ>Q‹Zí—ÕµV|yóAt»½׉¸w¿!°(@D<ªFs+–>‰õõíÁôÌd½ssgâ왉¸ùÕÓ¸Êór”Ë¥ØÛk:·Û~8­±þd;nÝz­ç–øžÉ²§³QÏ´¶÷£u¯ñó×µZ%~÷Û7cu­ßÜztè½ç¦ë/eÌð:X÷ÓÒZÜúöÑ¡€zfqa&®-ÎÄØX­Ö^|}ëallì9/˲èõû1y:ÍV4›[q~º¿yëâËþ'ÀkG`X³¹udÆé™+óçâí—~þzrr,>úàZüùóï¢Ýî:wo¯ûûýøÃïߊ>XŒÝƒˆˆ˜¯¾¼ÁÃkÌ>XÕëõãŸß<züÚâì‘×ò¼ssgžpЉo¾}kãUqE¡	,€‚ZilÆÁAgà±J¥õzmà±³gƇ^³ÑØ<2»E$°
êÉÆÎÐcÕêð;HjÇë÷#ZÛ{/4.8
@Aíïž½ŠxºeÃ0•JùØëvÚ¶ePPyeøGÀÁÁðe¾N÷ø%Àãf¿ (@AMM
¿—ªÛí
½?kgç`èûò¼SSž=  .ÍryøÇÀ³½¬ž×hl}ÏÕ+ÓQ*e/<68é@Aåy9Þù}®ž÷ýÍè<÷¸œå•ÍX]Ûx~½^‹7Þ¸tŒpRY((°ùùéèt{qû»ÇGvrßÙ9ˆ¿üõNÌÏOG5/ÇÖÖ^,=\x©É±øðƒÅ¨3#E"°
nqa&ÎO×ãν•h6·…Öîn;îÞ]úÞYP$  ®Î$¿f»Ý/¾ü1vw‡ÏŽA,€‚Z\8‹é#«ÓéÅÝ{Ã÷΂"p@AeYo߸ssgâÁƒµX^Ù8²›ûÿ«ÑÜŠ~¿èy(3X¶¹¹®ÇÆæN²¸Šx:‹µ¿ßIwA8aÌ`Ô½ûÁ¹ze:ææÎ}_·Ó‹n¯»»íøii-ööÚÏóèŠL`Ðêjk`\]¾t6Þ}gþW_çâÅ©øìó;G^ÏsûcQlþ{P@Ë+_Ÿ>WÿŸ®SŸ¨
©K—†Ï€A,~¶ºþë6}¦ÙÜŠv»{èµjµo,Φœ8 €.Ìžøúòòf|qóÇX²½Þà»Þ{½~llìÆ··Ç—ÿxpèXž—ãÃ÷cl,O>f8I܃P@.LÅõ7/ĽïGŽ5[ÑhlE©”Ež—£šW"²ˆn·N/ÚíÎÀ¿8œŠ÷Þ™ZÍGø-(¨·®_Œ¹‹gâÇ«±Ò8ºÔ×ëõc¿sìv“õZÌÌLÆ•ùé¨×k/{Èpb,€›œ‹÷޽ア³{[[{±¿ßŽƒƒNtº½èu{Ñëõ£TÊ¢\.G¥RбZãÕ˜¬×¢Zõ1ƒøÍ ""&Æ«11^õ0àTp“;@b 1˜ÀHL`$&°X‰	,€Ä@bvr8¥®ÝødÔC€ÂX§Pãѧ£š%B€Ä@b 1˜ÀHL`$&°X‰	,€Ä@b 1˜ÀHL`$&°X‰	,€Ä@b 1˜ÀHL`$&°«ŒzÀéqáòÇ£‰ág§›À’ØÙº=ê!œ~VpúY"HL`$&°X‰	,€Ä@b 1˜ÀHL`$&°X‰	,€Ä@b 1˜ÀHL`$&°X‰	,€Ä@b ±Ê«úFS7^Õ·©WXöæ«ø6¯K„‰	,€Ä@b 1˜ÀHL`$&°X‰	,€Ä@b 1˜ÀHL`$&°X‰	,€Ä@b 1˜ÀHL`$–EDÔƒ8MþsäÙêÞTçIEND®B`‚urwid-1.3.1/docs/manual/images/urwid_widgets_1.png0000664000175000017500000013672712615524560021516 0ustar  ianian00000000000000‰PNG


IHDR@h~›0sBIT|dˆ	pHYs
×
×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœìÝwxUÀáßIHBHè½÷Þ¤HQÀ†"6,ˆbÿEDDTTTTlØbGibA¤J5´Ð[ 	!„ä~ÜÙÍ&Ùš@€œ÷yòlvîÌ»³³»snJ)¥”RJ)•ä0ÆHnD)¥”RJ)¥.$1>¹]¥”RJ)¥”ºX4RJ)¥”RJå)¥”RJ)¥ò
€”RJ)¥”Ry†@J)¥”RJ©DÄGDJ‹H)'"~^ÊqÔsÈ“z–|œ}–‘ó=óÇ^Ò:ÇÑ@u)“!½-ö˜/4Ƨœ	N9OŸ¥œD$ð<ˇˆä‘*ÿâ5¹¶+!"åþÍ6Yäã甹ö¿<¶J)¥”RJ]PëÂtóxÒs¡ˆT‘)رB‡€-@¼ˆl‘ë½e$"/c[u"À> NDÆdXoªˆÄ‰H	/yt‘Ny¶ƒÎxœsY$W‰HþiíÅÀ<çy‡é®çó=Êò”SÎî^Êù¸ˆD{€c"²RDêgU0)("ß'€Øã2IDE䈈Ìõ²M€ˆ¼'"1@°ODŽŠÈ[ž¥ˆ¼Ìpž>á”Ùõ×ÙY§ˆ|áì°	{l£DD»Ì)¥”RJ©\wÁgs&HøŸótF†äš8›±]èê³D¤£1æo¼ž†áÀç1hÔÊw!ìLié‚<yxˆ†9û-\
´>×ë1Æ$‹ÈlkV+œ`GDªe€€•Àil@ôƒÇæ®nqó=–åwÊ韡œÏ#ÀKØàä&츣L£îœ	
þrÊ4ø;aÁýØãΰM~`)vLÒoÎvÉØ	^êÝœÕç¡Ø±^«€éYíp?wö7ÏY'P{lkf,³RJ)¥”R[N@œYÐ\jcƒŠ$ày2O
°(oŒIñ\("_b[†-<’îÎÝŒ1;2lsÎÖ,ivmŒ1=’?‘€såá˜
€:¹µg1&QD–{,CDŠ`
Ïñ?Y•³(ðгnKcÌ~'éKùï܃
~~z¸ºØ9-có°3þe4Ð)ÓKƘw<–wZr‘ë1³1‹ÈlfŒ–¡Ì¾ÀmÀ^àFcL¢Gò‡Ú
N)¥”RJ]
rú¢Ô;šëï:lKÌ^`oÆ@ÇŸq™³|°h”áÂùˆSfoc~²ëãáììlƒ3?®<3oâÕ|çÑs¢ƒö@<æ<_Ô‘RÎóLã΢;¶Uè3àÇåMlëRF¹Ò=ó7Æ$aÉtœ£Ø®‡Ã½äçZvç9ÊêÞ¶+£?^Ϋó|”RJ)¥.{“¿|‰ãG0qüæÍœÌ‘Ãí®%™ÄƤÏ=Ywrúcêx¾ûlh¶÷“œ”Ⱦ]ÙÎçb¸Ð³Àö‚œ&‰H¦i°q@ï‹È|Ù!"‡Dä³¶[™Ë7N™W;c|î‘ÐQ¾:Îã¼³®un®q@-=ZÚÿxÜÇÕÚÕ!Ããù컺ó¸w/ïy’ý{¶°wçú?Ø‘Åseώͼô¿®,›oç8Û½}ÉIID„‡FrrRÉœsÁgóà
NâœûÚÔVdl¹ìÌnYr&A	Œ‘ØÒ®&e³Í@Sì¬dÓþÃkð4ßylìÇv‰[™a…Ø íìØ£óÿ¶ìx&8ÁT
/ÛlÁÏ6džlâ/ëoÇŸ­DÄß+t.®uÎÚ­Íya@˜ˆ¼ìÆD•°P(¥”RJ]ц¸Ÿ|~~9EõÚxæÕOH8y‚ƼËþ¡a‹öcè׳3&ãÎ^ý0t,½Re+ñùðô}4Á!™ëî4kË'ï}ƒ–ºps÷ÇY»b>={¾ Ç3']” çþ:%°c„ÂʼnÎ_C/ãxॕã,ã}Ž9ñç(ÊgØq-o8³²eÌÿß•Øûµ:K1ÉÖq"/;óÏ3ï)Ø€ê)gF8OÏÞnû‘óø–ˆ¸§»vnÆúbÆ•±J_`߯#ßÄÞp6ÄcÑ^ç±™—uÄûMl
ö¾@pî÷G)¥”RêŠðÜ£ùàÛ¿ùålŠ•,ûN+̾][¨Ó¸"Bã«®ag„í$T²Lëÿ6Ãõ¢s×4hÖÖkþÁ¡%(]¶›×¯`ýªE4k݉r•ª³3bëW-òº]äÞc¨U¿¹Ý·õš´q§ïŒØ@ý&W»ŸWs‚(—-V²cËzF
í˨¡}Y¶`Q‘{HN:ßyÄ.
9Ý("_{Ò1Æ,‘ÏÞÀ:ùÛ*TÛrÜ{>/Ôã~@±5|îeµÅØñL®N›óÏ3ï(¼,‘wI»ÐÀ6ìD	ž³½ÍwŽáƒÀ™‰m©¹	Û*ÖÇ)‹§±3õ=ïÜ`u
v|Q)ì{v/ð.ð‰³þA`-Ð@Dæ`'ƒH&8eY-"?˰ÁR ö@³®J)¥”RW¼…(X¨«á§o¶Ä‰ÁÇÇ—ÔÔô—d))gŸ´zøðÕÿP((˜½;·žuõ›µcýªE¬_µ~¯~ÊÉøcÎóEôô^¦õÅLJÔÔTRÁ5f"55× ??âǹ×ONJJוÍCÓÖ©Z3mDK×{žÀ7ßÅìT–}9Ý䇽wýÝ„½èþh`Œù%Ãú½YØ{}½yg;l‹ÊZ/ùÏÃÞg40{Ï›>Ø	n0Æœð²M:Ƙ>Ø{Ù#€ß±ð=ñ>ƒÚÙÌ÷øAÆDg²טŸóÿãÚvð¶ÛØ—Ø	"nÅÓMÎj’a›^Ø.w‡Ø éàUgÝÖš8ëtÆco†úÐ{!þ8=o¨Eûî¤ïàQ¤¦¦Ðó†ZÔ¬Û”W?˜@Ì¡ýô¹»“ì`ö/ßòÕ¨WiÛù66¯_IpHqJ—«DŸ?ÀÃû¯=ÁÊÅR®R
‚‚CÙ±—G|OµÚ8¸o'o>wùРy;ŽŽ"îHo|üCŸëÁñc±”,S'ž—B…ÿÍdÂçç¶Ö%ÉNì""æ’€TΑ€ŒAœsŸ¢ÙØÉÚc{ÝX)¥”RJ—s@×,Á5íUhñR”._%Ó:‘{w°}Ë:ЇR¯Ik|óùqèÀnNŸJ Rµ:îõöïÞ†îé­3ŠØFH±Ò+io¹sëzQªl%’““ˆØFÝF­ÜÛìÞ¾‰}»¶R­v#òùù“”xÊ=5ÀÁý»À¡w÷–|õ{¸{"†ä¤D6­]FlÌAŠ+I†-	ÈŸ6?Vlt$‡ì¦f½æäóó;Ç‘ü÷4RéˆÈ$ÀÛÚsÛ¢óvÎTcLÖwÁRJ)¥”Rçå\ÐålæO_R¹F}Ž=ÌÌŸ¾ÄÇ7¯¾ÿcnË-' ËkÄ’:—UÀÓ@7ç¹ÁNÑ=;e¸RJ)¥”RYJNNæ—	Ÿ’ššBF­èzÏÿr»H9N[€®@ÎtÔŘÿ2ŽG)¥”RJeíJnºÔiòÊ	z4ðQJ)¥”R*ƒ‹r#T¥”RJ)¥”ºh8¥”RJ)¥þ1ç^K](ÚN)¥”RJ©‹H.oÚN)¥”RJ)•gh¤”RJ)¥”Ê34RJ)¥”RJå)¥”RJ)¥ò
€”RJ)¥”Ry†@J)¥”RJ©ÿ•cGóü#×ãããÃè©+è}w+Î$'1lÌBŠ•úÏÇC¥ñüþð÷ó£xH0%Bƒ©W½2¥Š…¸×ß¶‹_x‡2%B™>úsæ»býžò>Õ*”eÒ‡¯y]ç…‘_0gIÏöº‹û»vΙ¤.;ï}9™T“šnY¿ï"êðî}îMM•K%³RRSiqçÿ˜ûÍû.”)ý÷ùK™½h%¿Ò7ÛûÙ²s/u«UÊV>9íÂ@ñ	Œ?)ËôB²™4O¾ÿ™^·ßÀðOüç|TîI8˜å9òÜCwã%þáXüIöŠáHÜ	|}|Èï=•ãNÑñÁþDüù=E
¼0…W\V燰pÂÇT¯XÖËV™-[·™û¥få
,œð‘{ùç?þÊèãÑ»ºðÖ³æX¹sÚåR΋-åL2¯ô¹­á«Œ±÷%œ¶ø r•I‰§‰9´Ÿ‚…Š8Kÿüøùd¹Müñ£ÄÚωãGØ·k“ÆÌ´^†-éØå^ÄÇÇÉÓ€ÔÔbíOWÁshg’“III9¯×®Î-«ïüþôéÑ߃à#B~üýüÎ+__ßô¿9ÞÄ=ÎþC1?ùŸË¯.#¿œH[:S²XÑtËϤ¤°ÿPL.•*÷t¹–€,Îý“	§‰9—íýœ8™@ÇûsèŸiÙÎ+']°¨xH0Œ@Äî}ô{ë~þäMüñõñ!áÔiæ¯XËèXÊ–¥c«¦øû‘’šÊœV‘jW7­Oႌ‰eíæí,Ÿâ!ÁìÜw€½‘QÌ\¸œÐà"´hPëB½u~½ºZ}Ÿ´‹ƒÅaع/’6MêgÚ®aͪîÝ“â!Á$%'3gÉ*wÚœÂ,@«Æu½Ön¨ËÇœ¯ß£L‰bôyýCæ-_Ãèå½A½IJNfîÒÕˆ7´mÀÆí»ÙEõJå(X€°ð­œHH`æÂåøˆP¾t	ví?Àž‡˜¹p9ÅŠ¡y}û²}ïVmØÊéÄ$jW­ÈU
k»Ë²býbãŽÑ¨v5Ž;ÁºÍÛéØº)%CÓ~äNœL lc»öQ´HaêU¯Líª³|}¢bX¶v3q'â©S­"­Õ`ÓöÝg-çÞÈ(VnØJlÜ1Ê—.Aûæ
	,?§û%mgD8[ÃWZ‚ácþ ´DiØÍÊÅâY{’œ”Èšåó‰9´—à4hÖŽÂEŠzͳHÑPzöL¾|é×,ŸGÌ¡ý4lÞ.Ëò,T„W?øÑý¼@`aÀ¶õì=˜üù³órU6Œ~½?§“’˜µps–„ñÞ—“)R¨ OÜÓ•2%‹1¸wO
ä·Aï¢Uë‰O8Eˆu(Zľ‡"v²ÿPÕ*–£RÙRîÝ“ÂÓÞÏ”ÔTþ\¼’£Çã¹¶ec¯eHLJfþò5ì=C©bE騲‰û³ºï`4áÛvQ2´(eJcÞò5Ô¬Tž&uk\à#£.´º]OƒšUιÞú­;	Û¸•"AtnÝ”Àù9“’œ%a\צ9>>Bdt,á;¹îêæ€ýþ?rìjWK—×ÞÈ(bãŽÓ¸NuV…o%Àßú5l9–®ÝH•òe(b[Dóùúº·Ý¼cË×m¦Ž—Öšää3ü±p9'O¦C‹Fì;M…2%Ü=N'&ñ÷²ÕDÅ¥yýZÔ«^€Â˜¹p9À%sMvÁ ?¿|4u>¼žÝשNülÞ±‡6÷>Edt,>>Bjª¡j…2üôñ”.Ê?k6òÅÄ߸ë†|øòS<ôâpÖlÚÆ¨Á}ùsñJþX°€¿—­áïekhÓ¤>Ó>yãB½u*HÓZS Àß½üåÆ1nÊÀvc¨W£rºí…m ïÐQ\sUc>~¥/OùÀÖûuûÿc‡»ÏEuy:w¿|œNLÀß©}=z<ž^ƒ†!"îÚ¥¯~šÉw¿þÉÀGï¥f•ò|ðõ"£ÓkÐ0|}|x ÛõÌ^´€¿–„ñ×’0Ú·hÈä‡ðÁ×S1n")©i]:µjÊ7ï¾H>__Þ3%«ÃiÓ¤>K׆“šjÈçëË×ÃѹM3¢cãhvÇã$&%àïGbR2Á…±uöw^_Û3æòˆ/HLJvv½¶5cÞÀ·¿ü™e9ÇNþ×?ý†ää3î¼¾6ˆÛ]•ÃGÿÒT¨°mµI<•@øêhØ¢=e+T£ì}i‡ìfÈ3݉ŠÜã±]0Ï¿5ŽÍÚfÊ36æ Ãõ"°PþÜÀ¨¡}™÷‡mIÈ_€ò•jz-ø¹Ÿ.b»YسaƒzQ²Lw—7uqÕ­V‰•Ëso—Ž<òÒ»ü>)}ûOÜÓ•{#é5håJ'lÚ~Ÿ¿”¯§ÍâÅÇ{Я×<ñê{ìØÉ/Ÿ
åPL,½
s·(§¦îíÿV¬ h‘”I`ïÚîý^god”û3^¾t	¦ŽzJeK1oùžw4u«U"æhѱq¼üdO
€òˆqSf0ôóï¸ë†„…oeÄø‰ü1f8A…yñý±”*B£ÚÕørê|2ág–Lü”*åKóñ÷?S"48S´?ê0†Æ’‰ŸðØ+#)Z”YãÞ%%5•¾ÍŸ_Ž$Õz
Ææ?¾!$8ˆi.¤ÿ°Ï¸ýºvüøû\üüòQ(°Æî~ö
Ž¥SëfŒü;	§NóÒÿîçÖŽm8Ë]}_£|éT¯Tž÷¿šLÿ‡ºóÐí7²iÇnŒ1„…GаVµ+;:—#FKßž·Óÿ¡î¼ÿÕdF}7¡Ÿ}ǧ¯õã•'{²tM8SfÍ'úÈQÖlÚÆÝ7]ÃÝ7]C»æ
8‘pЉ3þ¦ëµ­yæÁ;Ýo’º<Ý?`¨ûÿFµ«1{ü¶ìÜËø©ðÙkÏR¶T1žxå½,ó(Z¤0¿}þ6]Ÿ|	€_?‹B¨Z¾Ì…-¼ºàº÷âþ¿LÉbz¢Çym×®YCÞzöQ^þ`•Ê–büÛŠ&>áSfͧ[§«yºçí.Ȧí»>ÖÖâú<åKçÑÁ#˜³4Œ¯§ÍâÑ»ÒÆ–DFfÒ¯ñç╌2ƒéó–йM3¦ÎžObR2CžîÅ“÷ÞÊñø‡­÷Z¾ÃG1hä’’Ï0éÃ×hR§÷??”ßþ^Â
mÑ÷;¼–sÛžùøkΤ¤ðJï¸ó†öˆ:LPÁ¼ÓÊPº|·¼–5Ëþæã·ž v븹ûc´¾æÆ}0˜¨È=4kÓ™^OaÖ´¯ù}òXF
íËØŸWŸs[ÃW1ïIøøøòüб
bÄ`ïÝãÇñÔ½W»Ÿwëчû¼š¯Tå¤;®oÇïó—wœèØÌÝ{zÜÒ™¯§ÍbêŸè×ëNÖnÞÎŽ½‘T«P–Vê²hUúÏòïó—²`Å:Š*ÈØ¡Ïs*1‘G_~7Ý:/0޽‘Q£ƒ„ó¤»nè@Êåi^¿&Ýzfî²ÕOp÷]6Æp&%…|¾¾ìØ{à?ïÇÛ9çª%Ë8£`år¥ÜãELª€ß¸Nuþür$Óÿ^ÂÒµ?eã&ÿÎºßÆS<$8]>bóÌçëK]'¸q=Ö9˘¡´r¦zMO>“Bjj*ùòù^±3!¦¤œÁ×7M[w¦iëÎ$''1¤ï]lZ·Œ‹gS¥fÄ9¾©)¶›`ªG§Ïy×:®í]ûõ&  ={Ÿ;(W¹'%5•É3çP½bÙtãx<õèÚ‰A#Ç0é¿ùeÎbüüòÑýÆk¼®ëú|¥œñøÎðøþ÷yT¹\iJ8c›;c–=káÖ®¦ÁϦP`³VÄúøøêLÞâr&%Å}Î\Ý´>Ï
ûŒyËÖЦq=Z4¨Mß¡³`Å:Z5ªƒ_>_oÙÒ¶Ywôð7q0&–E«Ö³eç^zÜÒÉk9 ýù˜’švûûç#Þi¨p9yê´û“jhո׶lâ^Öëö(U,„ÓÎuü¥&W~ýòùÒ¼TàO¿ïä…Çî¥c«&T.g#ÊØ¸ã<9ä|ÄÇÝä±WF’ì|ÉT¯X°µ/V¬c÷C¹ñRTù~úF}7Íýw,þ$ëÔ@DØw0šo~žÍÜ¥«™·|ÍYó)\°€»&|ÊÌù,YmÇh¨ËÛòõ›™¹p9ŸNø°­9
r×BMøm_M›é®±rqÍw *†?¯dÝ–Ôp¾CÂ6F°`Å:öDFѪ‘­áÚºkgüÍ¢Uëùë;±FËFuΫ¬ËÖmâTb"ýº‹É
¡qꤤ¦²c_æ‡FµªR°@~Nž:M½ê•üdOúö¼ƒ:U+º_—·r¶iZ?¿|ìŒâí/&±kW®cÓöÝ<õƇTèÐÝ}¼®D[ÃWÑÿÁŽLŸ4†µ+æ³ôï騻€"ÁvPnýf¶KÚï“DZ}óZ~úÖÎX²LEB‹—>ç>jÔk
Àž[˜7s2+Í&|Í’ñrÔôýô9<ýæ(êvéåþ~x³ß#Y®ÇuíÉàÏ×Ófqøè1njw¡ÁA^×mZÏÖÀ/[·‰™—óÓì…„GìJ·Nk§vß××—§{Þ΋Oôà†¶-¨X¶Tº


~òœêËÀ’5üü½l{\O©b!”/]‚¿™JÛf
ÈàO•òeøbÒtÚziÉqq@aá4¯_‹«›ÖgîÒլܰ…«›fÞ.48ˆò¥K¸Ç²cX¸2­»gëÆõX¹a‹{öºE«Ö§›!®EƒZ„…Gдn
÷_ãÚÕÉàOá‚.X€#qdzÀrP®zï…Þ<0ð-&üö?LŸãž¾ô¾›;ÒóÖëxúÍ8tøˆ{ â¡ÃGøù¯E¼õùwyºZ7¥tñPÖmÙA÷~Cxâž®¼Ñ÷¡Üz9*›¾ÈpϨ[;¶¡zŲô¾ïV>ðGŒ&Àß5«¦ë‘ˆpw—kùôûŸyùƒqì?™ÿó›âT]šÿÜýÑ"…þÜãîÖŸ»oº†wÇMdàˆÑ¢nõʬݼݽ~¥²¥¸ºi}‡m çÀ·iX«*~9’NmšQªXk6m£{¿!ôéÑWû<ÈëÏ<ÌŸ|Ã3o}ìÎãöëÚÑã–ó»ŸÇòu›y{ô÷@ÚÔÌjW£eÃÌTHp¾ü4Ï
û”gÞú˜~oâþ.óæ€,Ë9bàÿxéýq|ôÍTw÷¹¯‡
ò:{Ï•(þ@"÷íàË^I·¼ZíF\Ûå^yf(‡öïbíŠù¬]1€b¥è÷Ú§çµjµq}·˜ýË·Œzóiò(H…*5‰ØxîñCêÒáùûR·Z%Þì÷°×YE]‚
rË5­™âêþv–{ùÜжÛ4ã¯VÑkÐ0Bƒƒ¨Q¹<›w¤M¼1¬ÿcì;Í´?2íÏ…îå·\ÓšÇî:¿{V©+Sü¼þÌC<üÒpnlwaá[)T8ÝyÑ®YÆOýƒ«›Õw?gÌ„³@õªWætRõkT!ÀßÒÅCÉçëC¥²¥)ìu›7Ÿy˜'‡|ÀÂ•ëØ¼c¡EÓÆ¡5ª]‡ï¼‘–ÝŸ¤Aͪ¤¤¦R³rwÔËOöäžgß ÕÝ½éØª)ÇãO²~ëNæ÷!¾>>t½¶
wô}ê•ÊñÒ=ÒuŸË-cÌ®vˆ^úË9«×ãN±Þ©umÙ¨®{Šã3))¬
ßÊÞÈ(üý©Q©µ«V$áÔi{#Bˆuðñwâc—¹š‰·îÚKìÑã”)Yì’8 Wš­ºe«JêlçGò™V®ßì5­i½šî %lc{#£hZ7­E¨D±¢T«P–˜#qlÛ½Ÿà Bé.üŽÇ'°qû.LªIwΩœu1ÏñB‹Q©\©L÷ìØw0šµ[¶sUƒ:œ8™@Ôá#T(S’r¥Š»×‰ŒŽe÷þƒ,@ÃZUÝË·ìÜË‘¸ã”-UœŠÎTì‘ѱ¬sÆçÔ®Z‘ZU*¸×߸}7ÇŽÇS£ryŠ-ÂñøÂ#vR ëT'9ùáÛv¹[¥ÖªF•ògomˆ;ÏêÛˆŠ=BÑ Â4ª]-Ó˜oå<|ôk6o'öè1Ê•*Nóúµð÷cÛîýĉ£bÙ’”-YÜÛ./Šìž¿,>ëïKrrÛ7­ápt$))g(Yºµ´H×(55…ÍëW}pECKP«~sò°]RâOı{Û&òR­V#’O±q5¾¾¾Ôn˜6nkÓºe‰9DÍúÍI9“Ìá¨HBK”¦t¹Ê‹eïέäóó£Výæ™Ê˜pò;·nÀ? ?5ê6áLr2[6¬Dê6¶ãÌ6®]ŠI5Ô¬ß?¿ÌãR®TÝZ•¸(ßþþÎPC‚ÝÓ]»¸®-üÓÍÇö=ûA„VÒ®9ŽÅŸdcÄ.äw×Ò§¦­Zϱø“´jT‡£ÇNpøè1*•-åÓššjX¾•=‘‡ðÏ—*ÊP¯zeD„¨Ø£ìØs€à tß5y]v¿?.41QK¼ßÌ`Éš4tZù=%&%³fó¶t•b;÷díæm„Ѧq½t÷™:tø{#£Ý·z9wœ-»ö¥;/½Ù±“BÜݵ·îÚ‹ é†,YNóµÝÌŽ½‘¬Û²:Õ*DÌ‘¸t×V¢b8™pš’ÅC¨Û¥3¾æþ=MIMeùÚMì;CH‘Â\Õ°A…Òº™>zŒˆÝûiP³J¶'.+Ùú6²»ˆˆ¹(º|]È\uùÓóCÍ…€ÔåíB@êòw¹@WšgÌ¥RÙRœNLâû_ÿb×þƒÌùúý\©`Ή(׺À)¥”RJ)¥.}‚0nò“’¨S­#_xò²î]£RJ)¥”R*K÷t¹–{º\›ÛÅÈ1Wæü¨J)¥”RJ)å…@J)¥”RJ©¹	Û:’Ü›!­9öÜÛ<™!­*p¶ÅæÙiűÑ?N¹<ÓAÎëÆUyâ¤ùbÏ÷ÅÀÿÄ+åÁulz:ûðô
¶e©‹S6Oý€½@cÏñ@ŽžÎc(ö³£r@JJ
_|ñEnCý¯¿;…ƒ‡Ž¦[&"ôº¯ùóûçè¾NŸNæÀÁ#ÙÊ£B¹bt¹.ãW]š˜ÃÇ9~â¼Ú-Îê÷Ùa<óâWç^ñË‘¡fÅ©!v€%¶6Ìõ¼šˆôæc;_ÔOakÜãE¤6PûCQxLDbŒ1Ã3îGì@Ö{T`¬1&Úù¢¿HÁÖ`%8-I½°µ·c–8­5€ûE¤-ð¡1æ´ˆÔº3Œ1«}uÁÖ^í:ñ½1æ$¶oum§–n±1f±Gùn"Œ1;œþÔùŒ13E¤"PÇùÿ)ìTwìÅVUìk,¶‹…«KÇ=À§\k_1Æ©åj…1NÿqÏ–¸FØšÎÎr_à 2¶Æî’ã´Ý
”Á¶þâ,/ƒý¡]Ì6Ƥ:ýÑc]1cÌÈyÄž'øÑ£I0ð8‡íêr{Nu
9ïíl°Ühl~vk'¿N™§yv)ÉP–æÀ
ÀOƘMÒŠW	"ü`ŒÙ+"å±ÝXVsœ÷ýI`ª1&Æésß;¡&ÐCD®>:ßÖÖ+sÎ<ÌòüzXàñ=4ûYþÅùlÍ1Æv}WcÆ`ßÿÎû?Û½§„ó|†1fƒóYî=öóœ÷¦.P(‹
~¿6vÜFÆò¾Ä`ßÇã9w$òŒ·€“Ø÷q
0
p
ÊŒ´F߈È-Îûsö½1vlÐD¹Ú“,"í¾Øñ5×bÇÃ41ÆœpÞר®h¥<cŠHYà+ì÷Lð—ˆ,7Æl‘"Ø.lOcÏ“•"²Ðós¾M¶ð|
¬‘yƘiN‹ÊwNú``…ˆô1Æ|ê¼þO±¿i÷s±AkÒ„7°ZgìX¥O€‡œcÓû»wð6v¬ÔMα¹ûÓxÄyý­1I"Ò„5ÇV²Lr^ÿ1çwûleZvùä:uêDÕªãQu©z¢Wgš4L_·Ò¨^%òùúpêt³ç®£S‡ú*hÛÖnØMò™š7¶ïqøæ}ü³|EƒqóõM,àÎgÇ®(æ,XOåŠ%ÈçëKV–®Œ lé*”+†1†ßf®¢EÓj”.Y”¤¤3Ìœ³†®76£HP 5ª–qogŒaÖܵì_–Ý2ÿ
±3~õ‘€±ï™×à¨,v¶«'±èW8ËÃÖèƒýÞpõØ„í
5{|·`+Aæ‡D¤öÂÛ5ÎÄUMÛø{!¸ûÙöf"6ÀŸ/"?ŠÈžµø*kNåROìøTìÅzG¹W옚W€»î«°K§Eãsàc'†ýæ´„L5Æì3Æ|ƒý­ã´¶L^2Ƭ7ÆÌÆ<œßÑÆ˜¿J³!Àdçó9˜kŒùѳÛÂû£ˆ„`ƒ³CÀûÆNÞpð…X¿€ýäTdtÞ‘Fb»¾]
<îü†Þ<"v¦»ë°­é=œ´Ç€¶"ÒSDcƒ£îNž/`'LèT¤|ÜkŒ9â”-
!¶íGg»1°ãˆÆŠmy›¼jŒYcŒ™‹ý}úAÏçì9zô(ß}÷ÑÑÑôîÝ;·‹£²ÁÃm=GpìxòûóóŒ<ùÜX¶í8H§ÛÞt¯;ìÃ_èr÷;lÝɸoçÒ¤ýî˜y‹6Ò¨Ýó,\²™wGýÆ+oOÊrŸÓ¦/ç£Ñ°.|·õɸoÿ`ÉŠ­¼0d"ÂòUÛøš{¨#öþ”‡Ÿúœ[öqã]o³9"­cמh·ÈO¿-gúì0níñ.·õAb’½Ü˜>+Œ«:½Ä¢¥›™³`Û
dæ½$œJdÇ®(Ž;ɲU„­M×Ó÷¢º -@gñö"%ø	øxÃó™ØéE—cÖˆÈ	`1f«—¼zb¿ŒÝiN+Ë2cÌ8çù5؉5Ø è9§…§p£1f˜ˆ6c–9ÛÜ‹½=½°@Ú…ö·Æ˜)Ní±kÁv Öµ}s±?fb[⽎Î>À^p
2Ætö?h­½<ˆ­Ýœæ¬;Þ3]DfÛ=~\Ö»úx‹H;à	ÇvEpÍÒ3¸GDF51·:ëWÇö½”Pûüܣ5å~ì{rûãÛË#Ø[eŒ%"Î9ØïnØc°ÛYvXDZ9ùÏ7Î ^'ÐÈh;všû½‘ï±ÝŠ"RÂ
@cv‰ÈQl°ûÇv¾ÀÍØZØTìù?ð³kçÜÜö8{bÜxl@ûˆä3ÆLv.Àð-óœõ›²8¯("2;ûKàvcLÔ96©€í†T{ÑpÖµíû_Ïã½Øx<¿fŒ™!"ŸÉŸÉGE¤¶É0îÈiÝ
u.Ú{‹ÈëÆ˜Ë¹[ígŒ™‰}]Ïã€*«óHKĶèxK3ØïX—röãÙµµn†´=ž¶Ëö1ð±óôÎi?a?ÿ`[›<Ó®.iÜ?WÚfìw:Ø
™o=Ò"±A½Ki´ã@5´P´$l%šKq4ƒmõt©¡¬x<­Ÿ!í¿HPnO>ù$Û¶m`Ù²e̘1ƒ.]ºœc+u)hókî‘ÛnnÁ—§ëùÊg#¥ùµƒóÍ>ÿòO?w;ÍWeמh†ŸÂ–åR©‚ý8v¾íM>?›AýºñÒ›?ðRÿÛxñY[zÿs4.Þk:uhÀCl`3wán¿¹snà•çï`îÂp:u¨Ÿi›ÕëvñÃÔÅDGŒ#¤h!^}þNJÔxÔ>âãßèØ®?Œµ—°Ÿù'óm %%•ÿõÃ'ï>Âm7·àÅ7~àõáS˜úÍstïÖŠ)¿.eØk=þóqÍ	¹ÕÁ^l»ï\ÌÁÖöß,<ϼª’¹Å¥F†e;ek°ÜO;Ëb[ZÒqZ[Êa[\~ÉŸëBõ\ã0ÆlÛ}éFlËÄil÷§ÊÛaWðãø;ÐFì`ÓJi{œ|ψÈ)l
³{¹c7öÇ.û>wp–Ÿ¶b_wT†õs#:„Šˆ¯Ið_Øåtk»è
¼("S1C°]#cH{Ý_agcÛ%
챫ýpg›b¤‡…ξ›þ\ñüß+±]æc[a±-vå±­HÉØVKW^Õ2l^ty^(ýy®}:å íbïKlðuû™yøû<ò¹-Æ^”]‘éçèòçÙn ¶¢¶rÄÕòßtЮ}?pº%vqéY½u”´s6©€­i…ý®Úö/Ê ”ºchѢŊcÒ¤IôéÓ£?\6~Ÿ8ˆ†õ*àïŸù’»``S¾îOöÏsCÇFô{Ò¶+×ì °€?C†§M²{4ž[öaŒaí†ÝŒž6oK§öõY½Î{kÊÕ-k±e[$‡cO0wA8/?w;w?ü	§™»`Ï?Ý5Ó6k6ì¢qƒÊ„-@HÑB4iÖ•oÍú]<|ÿµîç×´M«Ú³/†ƒQqLû}9¿Î\	ÀÈ#ìÌÞ¥œ–[ÐÏØÖŠßÀÎXå\Ì_;q6(jã\ÈDa/½™Ïã9óÍØû¼Ží®t¶ÿÙ¸÷aŒ9)"‹°ãV¹Êx¾Ûga¶;ÆØ äl—¬LœnÅ1#ÅŽßyÛ¥Ïå:lw™ŠÀIgü@§œ)Ø>Ø`/€œP×ñN‘’"ìÔšf5[ß…
 ®8]L®F;µè{Œ1ÅN;»ÃyO§§_㞨ÁM=Ïÿ}Qü8[Òa2õ¯
iîÃ×2<,·×¿X/Úùíø¢ý6øÿF3‰›É”\«ÜRhϳ÷:|>î^xù½ßä½z.Ç­w=Í?NÂ̸áæá¿É_´ówš›·Þùd²º¯¼Âb,ܹŸ}>ãýýÌÐ@¨r¯8ÍbÄzºˆY“Ifömá\+ü÷Mª>çT•û“ÍïLqí¯
¦a„Rt ¾û;«
”·nû~ЦR}åžÓÆ[!žÜûPK3ûñèô<ÛÚ–3]Ï&Š¥Yš­,îJ˜*¶1³ÏkQŸæ¸ü'拦KçÛá#÷SÛEUë~*É:„"[Sþyñhïß×âšsáëOÆMmžú¢!ÛGC³BíÍ×6oÛñžh¬ÌŠí#©;ê°}ÔU•5£Fbë­·fÔ¨Q3º*
‚f8@Í;îÀÓž<™83£Y‡øô‹iÓ¦ÝÖ?œ#ÜšþÞ€o¾ýU{ÆqGlÏÎÛ¯Ëy—ÜÇ‘'\džë­Àâ‹tâ¹—Þe§m{²×n›ðÄÓo²évÙxý•øô³¯hÓº~<ž×žT±.ßzƒu6;ŠAwåÀ}¶àw?eÉnûsøüm-Îmw?ÍIƒnåÉû=Êþ‡^Ê[F°ÝV=xdØ«´nÝ’=vÞýþ²)_3Þ}Nä½1Ÿ3ÏÜsÒc¥¸âºG˜8ö:Z¶œ}™?ýålº.Ý™î«-É›ï|ÂBvà¼Óöà§Ÿ~a¹µbÑ…ç¥ËbóqÑ ¿ÖþÞ7ö@¨IÝSR€Ì앲ó{K˜YmGŸkºV~À$Õ’í#©‰lIM¤T;Rj|LIztøkt[e‰ß\\—xdØ«ô\sY¾øê;Þyw,kw_æ7Ù˜Æóég_±z¸ÁþàÃqŒ|îm&Mú™®KwfÕÂ:œ1gØ“£è²ø|,ÝeÞ=ö7÷Ùåüüó/{r+¯°Øo3>xe–Zùæu ñ_|ËûcÆM¦°=6âu>ýü+Ö]«+_|ùíÚ¶¡ónÿË/¿òú›1Oû91ò
ö?ì2>õŸßò~ñåwŒù_|ù‹/Ú‰µÖXšÙf«r9ê­øâËï&ûýSK*@M¹;ço+̆´šWšÉ™ŽkåLR-Ù>’šÈö‘ÔD*@S‡™1hÐ Fý›„ÕW_-·ÜrFW­^™ Y™'þÄE—ßOÏ5—á•×ÇpÆyw°åÿu㤣ÿ4åÌu@](@3j
P2˜Ù—•Ì´Ì쇺T~’$I;’“tZ˜Ã"çIkÊcC)©S!ýÁò9­%*…P’í!i;IÍ%'©[AÖ7fÙ‘t¨<´BIÖ[ÒA±¿—Ü©EIÖCÒÑQ¯åq{J²®’N’ÔJÒæ’ö/È”t†¤v’zJúgAÖ6dIZQÒ@…#I-£Ìå$-"étIsîÍQ’Ö’Ô>ʘ¿PîòøX³K:YP¸$ë/i{IÍ$+ä\’m+©xv€üx<Çž2”»þ÷ûî¹)ÿµÓŒ®V­˜© è˜æ‰ýÙåë!äzuXfËè˜Ûȱ#iîB6oÍ%üÖ‘uœRºY…êr—Ôšø`™R¬™Ú–9Süö$©OÌì=|Mßà8u$nà¹Xƒ÷3(X’ú{ã1â~ÀËÜïäMñXLÃb]å«ÀÐP@ºáNH†Å5FâL;KZw‚QŠ·5¸DÒòñŽŠ{<µÈª¤õâÝ?ø ÖdE«9ltB8ÄxØFR)fÜ%ÀÜáØç<¶Ñ‰!;
÷døº™}€»þ/9ù8÷†úl¬»ˆ{K•<ÎØ~ÀˆXƒù!0$”³M€“âÞüŠ;}ýÖ*¸ó¢Ò
è§ðÀ§Ëã°]ç’éà‚.`©¥ÜÙh=2P2ÃiÑ¢9§ß;o8‚¡WÌÞý7¡Y³™K1Ÿ©Làä.f¯2³žñÂ^ÉÌR¾é¸ÞÒÀ%f¶^Ùù…€
ÍìªÊ9«-o7Ü3ØxÔñ_ÍìIÿN7³’ÆšÙüòþÓÌÆ~[<àçÒåéê’ºx¶uTÆûx¬•VÀ.æn‘Ô::%ç
Š’ÆICµ˜áx÷œøg`u÷çr'*/âÞ/72³
yoÁciý°³™=P{ë\	8ÆÌ®)Èþ…‡˜jf§d»ã1È>F™ÙÙfÀEÀÜÐöÙêxX‡;p×úZ•“›.x¿1w³ßÝÌ&„l^ÜSáP /°jÉIJ(ZOãž+wŽ|%÷ûÍñ@Ò¯E¾ÞVl¹÷Íãwö7³{²sð Ý][’ÙxàÔÀíföo*P‡íczŠ˜i8á„ÁgŒ®)“ýØøŸu)Öç2I{âŠÓVe²{$Äc×u.“=#i>³d±3³wä.øOz–”Ÿ}.7‘»èk‘f6!fŽî()?!ûEÒÞøl×)Eå'ø®ÄÝPT~‚Ãð´çŠÊOp
Þ×MŒú&uÀ‘GI§Nš„ò“$
…™ÓºMM~¼“y,¶c
çÇñU€•Ëòt®ÀMzƹá!ÿ>ò>%nšð4°mœßø>?4[ÈzáYïÅc§tŒóëÅõFDÝ«ð{†àã­øŒNÛé-ÿÈØ?öOþWxÿ‰ý7ð£gâ3g·â#¬ÝñQÑûã¹_SÍ5‰vt§]œ?)žý“øÒ®Õäßx$Ú喝"ð.p1nþq°>*ú>"{+°®°mYÖ&¼óÄù{q3›á‘wÝ8¿6ÏꮸF8ßo5]ϧ¾ÿ÷§¦ŒÜfÝ­!ÚÀxþò
кL~+þNt(“¿CÆ‹—Éþ…ÿ/~¬V&Ûÿ?ü_™ls\qx›²w
°F\ëEàe².!{
8·L6/n–6Œ²ÿ{ 
>‹óxlͲæ…ó¯á)ż7E™áÁ·‹²sðwãx K™ì¼¬Q&Ûxï#·¨ï÷G2kRí£¾·ý~mê[<»é/d
òvÀ˸ùÒløGär!û‰²Î£ï||¶¤t܌ɠ~Ài±9>$ =þòý›¨ƒð—ýú‘ç)¢3ÄG«ŒýG€®±?Ê
Pw|j¿t¼n.\9M(Zeù^-\óP`@ìר•Ÿk°BŸ…üToûËã£wõòlë¨}¬Qº—øÇÿp<8åñxpHpe¯>búlÙsøŽ*…æ^`Å
ט­°ðçØ?=ÚD©Ý¼R!oüCgŽHw+°^È>v‹ý­€ËcÿßÀî…2.þûÏ‹Äþ)À^±ÿnΰ>¶¼î+ã#Õ0‹(@¹åVÓ65m,þîÇûˆ›‹²ðÀÓ­ðÀÚw
Y_vÅû”¹B¶<þá¿n:7X dO
Õ"ݲ!k‡÷
;‹ãÊNϵŒw஬õ	™âÝvþn|Ÿé*ýÆRÿ×ï¿dãÝÒ0»¼ Û_ÏÓ„Ï •îMà`¼¯zWz÷|,†›ަêÜ5~óªx¿ø)ÐyZŸíT¶d$û—ÜêsÄ\o\‰˜hnÆvnýÁjòm‰ÏÐ`… •Õp9_âÌ+Åùáföµù/¾Ø4Öö,\*éV`Ç8ßY¿yïžÊ߸¾ÆÃÌ!¾ô˜Ê¼ÓÊûVèy [˜Jm¿k °~œo¬<¬$©3>Òú0°.ÞyV×6Š3_(q*¤YKÒu’îÁïÏÙÝ…vÓ:Ú@‘u€ÌìûhCwPÕ~¡ªÜÛÊW‹¤ðˆâÔmeeÝYáwÌ#é?Q÷ãÊêž$MšXSÓ_¿ó+>˜µ‘¤bMÍQÀæ&ª‡àkzŽ55;ÆÿþÉøìòɱ¦æZ`O3cfWâ3Lÿ‰÷ÃÀ‘fö’™Ý‡+ ׆ùêõÀ…fö™=ܦܗšÙõfö.°îLa|0f,p¦¹ó†?IZ8WÒŽ07áÝ8^Ò*r¯rë57™û°‡ÜÓ]o|¦êÏ!û°®¤~’VÅ™¶2Ç&Î.Â-¾ˆº}
œ&Î}}\ï=3»g_,Åp#p´™=ofâÖ×Åz£$I’FCC¬zŸÉmÆg~°
ë~‚1øZ›w¦ò:ÅkÌOéÏWv¾3>ª5ŸQÙº +­µ¤–f6©,o‘Ÿ™Üƒ^ùo\(®S—”+€“
û†/¨5Iï{›ÙØ:¾~½`f¿Jzï€ÄÍ*vÀG[?.K^~ß¡Â}¨p™ÓñÑÔb­ÀªµÈ_©ý¾_8îŒwÂmâ««'øÈ蜪Z¹[^V©.Åzü÷Ìt…¤eqS–$I3»ï'JÇ_áƒ[%:d™|€¤(3|6§ÄBe×Ùµp¸|™ì…ÃõÊdçâ3OàëF‹²›ñ+ðÿó¢ìqÜì
\9;¹ {Ÿ%_^U}Œ÷%(ȾÁM¤Kt(È&á³ñ%æ-ÈŒÉj)«ë…ÃËdG‘$IÒièi`)IÅÍѶÀÍ
¦Ä5À`I'ã„M!ýŽ’^V>3³q1ñ±˜¤Cq3¦q/@ßHzAÒ)¸Y@; ™Ý%éAàtI·ãk—*ñ°´¤-€×q³¨û$½†¯ùø©0Ê_W¼^ä^ÆÍ1ªã*àãUú•èuê†/ô=· {_‚™'é1üC½%nrØØ¹
kñ
€¤Ã((¸}zi¦p[ܬ²9n³_ôø nFWÎî±ý‚;”(îþÉÛÐ9L>#Tb}¼M¬¬cfŸd§á÷úR«ò”tOÔ·'®¨ÝA•Òº+n†·1¾Æí8%nWÞÞKÏwpÔ}‡¸/Æùðv“$I’$I’Ô‚™*P
u¸¸1Ì!Šç{ã¶á»TΙÔDÆihÌk¦IŸ†‰H“cV~¶Iã ÛØ¬KÆJjbfˆ”̼Hj'
ÁÃT^s3÷€“$õÁUÙû&I’$I’Ì\Ì3@Iý3@IMä³Mê›lc³.uÕ¿ÔU}’ÆGþï'õ…$k'I’$ɘY›ü€Iê›lc³&Ù7$õÍLe'i ¤Ù$Í-©FOr’º„ç¹J²ã".C]Õk»ˆ9Q~~aIûÅ~ób,I}Â9DRGHjYØï$iJÖ’F€™)·Yokˆ¶#i1I§Iš#Ž%éIkJê éLI
éŽ9­%*iÉ‚lx—7>¢[AÖWÒ±¨¤
²Þá^I{IÚº ë!éè¨×Ž·§$ë*é¤ðl¹¹¤ý²%!©¤ž’þYµ
ÙB’VŒ~±EÈZF™ËIZDÒé’æ,Ü›£$­%©}”1¡Ü%m*ivI'Ëãå•dý%m/©™¤c%­Qm+é/±?@î¨$ÛXÒ!µ{ªI’$õÏL¥án´
˜ƒ²˜
˜ŸÉã:Ù	÷VWüüTá|Gªâ'ìGÔ.±°ôïr$Ó„<@ßó…S¿ßÍ ê$IÒ˜Ù{ø»~pœ:÷úœy@ÑŸ«âã¿?°7ð¤yðÏBiØ8¬üð*04nÀ…À°¸ÆH<€igIK×O†l8p‰¤å%u†/™™EþS%­'¨:øÀ<>ÑpàÀP´šãÞ-'˜y~ØFîŠàÜk懸GÐnQwp¯”+¯›‡bèˆ|w»¿ð¬yð׉ÀÕqoúûá1Ç~>†„r¶	pRÜ›_ñ@ßCå‘«àÁN‡Ç5žÂŸ.,¨z}œK’$iT4ˆ	\¼\WÁ_Ö·˜ÙW’z˜Ù“‘¦°¢™Ý*i{ ;îBøæèÈÀ ò²Ûã.­ÆÜof?ä}®À%öûÀšDg³%°¾¨ý³2ùÀÚfv³xpä»Íì2Ù¼yËÌ®)“ýGµÄg]Šõ¹|Y	Ÿu)ÊîÁgþüµLöÞíô™¨’ì+7'>s”$IÒèh(è*`>`.`IšÙó@¹
÷lÀ©ú0½W†:m$-U]Á1*7Éÿ8Ê)ñ™ýÓÌþ‹ènUÌ+iE\9y†ú>V,ðQØCo„wváãÃeå5Ã?Ð2³Ûñ2b4ò}`Œ™=æÿŽõT`å’÷8ÅÌîÄGæf7³ÓÍìZàGI#ÍU¸bØè$i¡¸O$­,©5nšwC¥4$’V¾5³cÍì©Ò‡†™=„»1_Wð¶.d{ÂÌþcfç㳄Ýq󇸿oM~úçšÙmf6X-fåÀGaO5³¡Às@®ËJ’™ˆè?ŽÆMÌþï·"Çãƒ,»†©l‘£ñ–­$-^&€Zu—´Z™l7|f><0s±>›‹âx;•ÉÖÀ+¿+_EY\{WfвyqågxÔ«(kƒÐäkM›Ç„lïÂ{¯DéÞô‹k9
'nQaðð@<ôĪÅõ@A¿øÛ4L’$itÔ»$i.\AY™ÿ9ÿPß·Ù~Ü̾‘;'xXŸ•ùX¬†ò7Â&€
eƒ+%Þ–drVZáÒúÀgxÇT΃øÌÁ2Àù¸ò³Qœ/2ð}aäî½êêŒ7cÀ±©qÌPÁûª°>SÕ^¾àõ	|fèU|ægÑHs!>Ú׸#ìàg4]r…IÿÆã&m‹ÄSz®å,ÍäÏâüþÀä÷ðKü&I2ó0Ÿýí…¿+Î)	bÝÌ¢øûúVàÊ0S.™GoÛøz ’3^¸UÂqedHôeHZëìe.²ÎÀ帰=p¦¤eCÖŸáÙÐ ©gÈZ†ì¤¨ÏÖ’ú„Lx7ØXZÒ¾…ßð"Þ‡ý‚+5%ŽÇµõ—©Z+E”±t”y#±V*d}¢Ž›'Çïo²ž¸¶uÜ£!ñÛÔ¿}gà²ÂÀ\’$I£ÂÌŒiݦ”7»4öÛázÄñüx‡u°aœëÜû­ñýMâø}\aéŒ/ä8رp­oc¿g\kî8¾Ø*ößÄgGÚâBóB}[Tø
kGº+âø1ÜœjÎ8ìûÏcÿ4àžØß	8©Pæi@¿Âñ[À\±¿N5õxèXø­—dà
Ýf…zÎ…Bö,ý¶¸ß#å§÷ÙÖQû˜7?k]v~$°hìþû‡à#¤Íqs¹ðQØ–À›…üKÅþ.¸>øÌبØß¢Ô6ãøbàÓû››ÊÖí#·™wk ÷ÇføÌÿ|q<7>¨±°:nn¼tÈZÅûyÐ%d¥¾HÀ}¸"4/î`«Âu®ÄÍŠÛàK{d'áëO[Dßp\A¶?¾îpv¼Ÿ»¸ ÛŸE™WÚî²uÏqåíÜì¹EȺâC«Ä»­Øw,ŒÅ•šÞø`Ï!k‹ðõV2–
YËx¯Ž6}NU¿!Ü”úl|Vç`›Âï¸W Zãýä¾Ù	ø¬æž]½·ÜfÞ-Ÿmnõ¹
èAà¨X¯27Þ”Z÷XIoãÚKæd#€%݃¿lK_
×ÿ‘´þBŸP½Ü/é'\a¹¿˜Ñ|Æi¾öc<þ!}*pGÙ5FâQi-мs¨äeì(àIßãO‰‡€þ’þUÃ︟1«äYnJ<÷»=>2Xºß?IºXÏÌ^†²ë3û6žÁ#’ÆàÛâž…n–4ïp‹màSü™bfŸHº=îïcø:¡·†¬%Uk³’™Ióáëâ&˜ÙÁäƒð³#Íì‹
ò9ñ‡…€—Íì©8Š™^õû]9a–´INž¶Æ1;Óa¾¦fÁÂñWøš¾²‰x_SIfLî5t¡²ëìZ8\¾LV|Ÿ¬W&;87·+“ÝL•Cœ¿—ÉÇ1𘓲×ñ¾|誂ìc|`±ÄÙ7L>[Þ¡ ›„¯Ã,1oAfTy3…É­,0³=
‡+–ÉŽ"I’¤"‘Ì40•Ñœ%u²2kSH?/0.^¾SJÛŸé_A¦}ùûœ“¥ë|frÓJؤÏaî´¶y»ÿ0³ÍJÝ9 IDAT]¦˜¸ær*ÞoI—â³)C¦¢Œéjµ)#žSgàÂ’½º¹—·RºRL‰s€V¦².sÕ&}R3
Ù>jÈ¿,îxdëjÚ{'Ü‘Æ6VX²îøÈõ‘fMà]3û‡¤±fVîeZê÷»r”j>sÜØ8ajþ'g&CûH/Ù>’šÈg›Ô'’d€Ú(?‘þóZ¤ýŸý©$3|mÇ”Êøhêk7Ų~Æ×äLc€}§˜jÊu(wç=n‚1‰*g†xN–û¾šä¥ËIÕɫɓÊϬɤêÞ/föYÌOF(Ü—á3C×Îw(ìwÅM©Fà®Ó-ÎwÆÍˆžÇJççvÄ×—Ý`f/–]³7>«ûLÔùˆ8¿)n–5$ŽçŽòÇán”'ÉC¼lf¯ÇÿòîÀ`+x
K’$I’dê˜Ù¡Îò˜Ùwõô¡þ¾NéS3«Öˆ9?¶$™Æ×‰ÝYÃÓÿˆkã
I—ã
Ñíášù~ÜËÖÅS}“f¡}$—†hñ^
yðS®è¨áÜÁÑ\qü!îpgJyÁ.Jy~¥Ê9ÄwøÿüJ5äùwY^Êó=ðNYžW£5åyX¹,ÏB¸ÇÝRžŸ©
…Q)Ïkø»§”çc\	,噀{œRžIT…«˜`foʃó,HUökZ‘ÿûI}Ò k€ªáe&÷X3Ãôg`XùBé
œ…¯¥¹»þk•$I}`fã%Ý‚Çb9ÊÜa+`M3{¬†¬7ãÍìI-̽+Þ‚¤–ÇwG|›¤VfviùÂø‡ÉÏø{åpIà{ûKêˆ+Y=Ë%=nf£¦éÇ'I!LZË׿PvüN…¬SÊ3šßSna1£ò¼[!Où:èúÊ3Y3+Ïó^…ª
`ÙFÒ1£t&°_8Oy
ÿHxxpùCs¼™=
ì	\+©õôýü™I‹I:­ä1RÎ1’Ö”ÔAÒ™á	°”þ`I›Hj-éTIKd{HÚ.úã$u+ÈúJÚ#ö•´AAÖ[ÒA±¿—¤­²’ŽŽzí(i—‚¬«¤“$µ’´¹¤ý²%!©¤ž’þYµ
ÙB’V”4PU\[F™ËIZDÒérWï¥{s”¤µ$µ2æ/”{ |]Úì’N–´tAÖ_Òö’šI:VÒÙ¶’þûäÁÉK²Uå¹3I’¤Qa6Á„¦"Í0|±ñ#L$mlü·—½ïø7ó}q›ØGñ„VÊî‚GÉ~ ®S
ÞvXä{8(ÎÍ…A‚/n¾Ÿ^^ŸÂ}>wá„?È
7wù¿Øñ0_¬¼ìôÜËÆ¶MoÛ¨«2rkœ[chÀ²¸½ùŸª‘ÿ	á]´†2ZâƒÍjqÝ9y+œoSéü4ü®6T93m
Õ>âýyìÿ3Þí¥ ¡§Æ{^@ÿxÿ—‚†ˆ{äk	lŠÇm›?dÛã£ïíðØAãeBÖ6Ú94X+d+Äñò¸#Œ1¸‹v¢ÅcµÁÍ¢ö	Y»h§}ñõaâ®ÑÁ׉=Ç7R\\ñûN‰ã³q¥¼Põ
àêØ?w°Ñ2Žÿý›p¯ƒomC¶¾þ¥³j,°`ÈúàJúÜx0ÖñÀr![_²0UUבí#·™sËg›[}nTÅÇœ¾B¦"Ílñ·Y¼ÈKÇ%è¯xì›bžfÑYµôuÀfÊ>ŸÂÇOä[WHfÃ;·ñEÏmãG¯iÏúÆþÍ„òT¬sìŸlû—›Çþ{¥kãniO˜Ñµ®Hc(#·Æ¹5†ö+"[[T#ß"ä­gôýjj[Cµh¯Çûüs
Ênô㟬R–÷|vm,°q™ìü¿ì\&û> ÷4pX™lwܼû^à¬2Ùfx`ç€Ëd«ãÊÃ%¸Ô¼ ë|†¸½´)ÈæÅ­3ãoÇ‚¬
¾.ä,\1[¢ kŽú]÷¦[Y}†âÊå'Ä dAv`úM`·2Ù¸GÄ'q7ó3´}ä6snùls«ÏT€ŽÆgwéjq¾¤-/Ëû𑨖¸W–;eìˆÇ½(/ûƒò`oÜ\¤t|jtJmqÏO¥ó»'Ç~¹´%>Òvns|œ/W€J#f«wÏè‡Z×
¤1”‘[ãܲ}äVßÏvjËÀgpø[ÙJ!;±‚l~|Á÷ed­qg÷V5Ãgˆ^$f[Êä#p«‚–d7áfsWƒ/¨_¢‚ìˆøkWí²í*È6Ùß+È–ÙÉdóâÛ¯ª ›_‹ò@™ðEó¯Tº73¢}ä6ómùls«Ï
°z_$iM`e3ÛÊÌþ€{HiYLcfï™YàÜã>øHÖ‚…d•”§#Òu®&oÑ{›QµêçÒ~¬%:	ØÝÌ6Ã6OVç¥òŠe%I’$
@¼¯ÆM ÿVamÔñø,Ю*º
ŽÆ­¶’´x™l®ät—´Z™l7\qšwrQ¬Ï渭	x Ý¢l
ÜýùÛx?W”uÁM6ŸÆÝ(eóâfiã^EY\9 wÛ^’5Ç] ö.­•*Pº7ýâEŽÂÿ¶ˆº9|\µ¸(è;à.£“$I
ñÁþ=°¨¤î’ŽÆG•&#€n„|¾6³O€ocÁé–øÂß[*”
0XR/IëÅ‹úA`=I;Kê¬Áä¾ò+ñ,°ƒ¤õñ¬æÀ:’vÀgƒ’$I’ÆÇ@¼Ÿé…¯Ë<§$g‹áë;¯”¤õÅ?Ð7Çc¹ÜPp&Ðø;> w0DáÈGÒòÀiøz]£ÌBÖ¸W¶Δ»jGR;|ýéþ¸YæI=CÖ2d'E}¶–Ô'dÂMцàëq–V•3
ð,/âŠÕ/TÅb!ö
ÙËxŒ—Ò½Ù¼	nípUáÞô‰:nޝ‡u$ê\
ü÷µYW<ÆÍöx̬Ëâž$I’4:lz§‘¦"Í6¸=õޱ-牿kâÐ…ø‹³äp Þ‘,\Cù«â/Ý“â\{ü%} ЮPÞþ…|+›Äþ\x‡vnú°bÔ韸“„#Ý¥bÿ@ªÖ3ÍO™øÌ¾MoÛ¨«2rkœ[¶ÜêûÙN©|MÍÇÀ|q<7>k³¾¦f°tÈZá]ð55ã€!n‚}>H÷!°Uá:W×Sµ¦f¯‚ì$܉O|MÍqÙþxËÙqÓ·‹²mñY”yp¥íNªœ¬K¬gÂgwž¢Ê±CWb=núöUŽÄ×3m‚ÏL}RèÛâ3Oý¢ÏO8îÁ-žÆ%,×îY¸7wá:D·)üŽKqª5®d@Ùz¦†l¹Í¼[>ÛÜêsl†BM7uñl³}̺dûHj"ÛGRÙ>’šÈg›Ô'jP“$iâH²]‡$I’$Iš©%I2ÃȾ$I¦‡@I’dZH(I’$I’™’°çOf1ÂG’Ô3Ôm³¤V5WHJü]LÒ>5¥­·yÑ=èô"éà’WœZæÛ,<Í%I’$I’$IÒÀÌè¸5¿Åõ	÷›ïM!ý7ñw><ð]m8:ÝHZXÓÌ&M1ñïé¬PõH’$I’$I’¤v4ˆ	œ¤~¸ËÎW€[Ìì+I½9$|†»ä,¿»탻4Ý
8øº¬ÜmqeâR3û0Îígfƒca`5Ü5éºÀ„ˆåp½™½òñ@x˜™Ij×]w¯z¶™M,ûI‡êÑ—°.0ÂÌþçWÄc%ÜefÏUsozqÌì8·àu“¸EVŒãA–óþI’$I’$IRkj¨CA¸bð&0	x?ðZáø…Ès7è.<ÀÛ¿
e®‡Çyx	øŸ¤eâ|1ÍâÀŸ€wñx	_FpÕKpågàÜÈó<–•øŒT%%q]þûãfÐOFò1pC(
›'Ãâýú*04nxîaq‘Àõ’:KZïGžÙpàIËKê^ŠûaÀ©’Ö“Ô&d„…ÁpàÀP´šã†Ììk|nIÄ5.櫇Wp³êCv°ðº™}€÷e%k…Ãð~òY3û˜\÷¦°nÁPê/‡„r¶	ìu˜™ýŠ:•4·¤U€ÿDýÁ¶^'iaI‹Åïxj*e’$IƒRï&pr“³øË+¸Â±ÞùL‰7Ìì§jdïößã^MÃjrR°®t-Ç—áQ¾ú—JúØÂÌ&òMbòûÖŸY*gÉB?ÄóoÈ(Ìmf_Å©ÑÀÒ…$/ö÷öÄ;–¯ñå?×ðÛ’$Iš
ûOK:Ÿñ_½Ðo	<
œìldfߘÙY’zÿìlfcCv£ÜY͸Bq@ÉDÙÌ•4¸˜8ÕÌžÙ+’n>n6³[Cöa(a×à}âkfvAȾ–[Üu86dCöDÌX-t™…òò\ÌõV-˜Hÿ­povºÖ®<Œ+H}Þ¥=3÷æ`c`73û8dÿ{s%Þÿ
0³×B6LÒ \)l›k?>u1I’¤a13cZ·)å¶Ä×耛µýôˆã×ðÙ”RÚwÅþBÀ3ee¿kâ£WóÄñE@ŸØèû§ã3)Ž,”u°gá¸EñoìßlXá7½ÌûVei.¶‰ýÍ€kcÿ_À~±'ÞY—êwrì_	ü_yÝbÿ>`U|v¬×ô<»é}¶
UFns««ö‘Û¬»5TÃâð·
²•BvbÙüÀOÀed­¯€{+ÈšáƒV/}V™|>Èײ‚ì&à{|¬\vð+°DÙñ;Ö® Û%dÛUm²¿W-²“+ÈæÅü®ª ›ø_;[.>(øJ¥{S×ïdÖ¤.ÚGn¹U·Ö&p+Hº75{)^¸àSçwKº4Ž/Ф)ñ2p¯¤áøº›{ãüùÀ’¡jv\qØPÒÃ’VÕ¦u%Ý/évà¶H÷_IwKú/þ’´Âµo60³qøâã’n’tm¤9ØCÒƒÀ¸iB9''GšÍ£N•x@Ò’n¾0³çq³¸;ª½;I2“`fÊmÖÛªý„éòѸ‰Ùß$µ.Kr›‹€ÊdkëãÖû”ɺÂׄT&›7Qõ*ÊÚàÊÑ0àÂ=Äþ1!Û;L¸‹”îM¿¸F‘£ðµ®[DÝŠ|¬Z\ô‹¿ð~-©~ùå.º¨ºO„$I¦«IKšÒ6µùNÓsjÊоÂùVÀœSYÆ\@DzsóíjÈÓx¨B]š•o;u˜š4sŽ7N©ë{:-϶¾ËÈ­qnÙ>r«ïg;5e§÷ã³27dÏGŸp.>ë^²2è‹›O·Çß3õ	UÊY[|æ©_((Eå¬%>ótx(6ŸS¥œ	_›t6®œ}@˜w‡üRÜj%åìÜŠ¢y=¶Yž/¾øÂ–Zj)¬wïÞ3º:
Bö
¹Õ瘢‘M³¹‚¤éÊŸ4^êâÙfû˜uÉö‘ÔD¶¤&ê°}ÔU•-;î¸#C† mÛ¶\wÝul±Å3¸Võ‹$ò?©/$YƒBM’$I’$Ij‡™Ñ½{w:vìÈ!CØwß}i
J_’Ô73Å¤y€¯­ño$µ¾1³_$ÍkfŸ×qfÇM¾­EžŽæNÊÏÏ	übf?HšÇ̾¨ËºN+9‚›ÔD¶¤&²}$5‘3@µcÔ¨Ql½õÖŒ5jFW¥AÈ ¤>‘Ô ^àê‚;q[èÚð_ªâê¼\SÂi¤/îA¨6<]ÁCø"ÓR„ðÓ*xJ’$I’$I’¤h
¤’~§éKjUSžjÎÏ^Aö;婺²«+·&¦µ¬†ZÎÙÀ?k[‡$iÊH:LR³²sÍ"(euyÖ•´©¤žÕü/Néš½Âå1’öŒ •5¥_#®×[Ò|SyÙŠ¿+Þ•â½$I’$3+õÞ‘JZZÒÿ
LJHÚ/öï“t!îºóYIëÄù¹%]<1rÚòo#é.àIƒKʇ¤Ñ’ÎÅ]y®]V7"ÍZ’Š2‡Iº¢PîÚ’nŠr¯’´Sf9IÏDYÇÊÚ9â=(éÔb\†Bš#$=‹{óÀÌ^Âã&µ-Ï“$Éï‘ÔXÆÌ~-žãe%­YMÖKmðØ+/KÚ¦–—^èû€Ÿ§þT|¦wcàúxÇM‰A¸·°Wâqd’$I’$™FÂ	B3ÜÅfñš¥ë¶ƘÙÞ’6öƶm
|ffý%-M•Ó8XÃÌ&I:wƒz+îfô)3Û?Òë0{¡.Ý€…Ìì[IJZÖÌF';šÙÇ’vvÇc(ÔÄ
À²x´ì‡$
ÁÝŒ…Ç€˜Ü‚ÇMx¬”IÒBñ{àñ^î.”;XwKš$IÍŽ®ÄùÀ‘x\“JœafoJÚsKÖÜ÷ò,p»™}&¬{à.‹|üišáï¥å«Íì“Bº‹Íìáx—Á 	À>fv~ä_Xx
_Í(–v’ÔlfßIZ;s£™½el‰»+Þø
¸Ö̾¯þ&IÒ13
ÄèÑ£?~<Ç{,«¯¾:[n¹åŒ®Z’ÌÔ4SŠ;ãïóT¦ö&‚èÐߎóàqn”tkoV¡¬šx¬à¸ày [($+çG¹»áî¦ÄÃföC8g¸ÿØXxÁ̾ՙwåþ*7î7³ŸÍl"ð`™ü3`þ©¸~’$“ëÅjd/ÝkÊ3´«_Ç©Ùñ‰ë€Žxü—Oâïͯ€ý
çÿŒ·_ØxŸ^±n0›;¦ü}\ëÈBš?F}>ÄãÎ<‚Çüx!Ž'J샪¼LÒvQÆŽÀÀ»À’À™5݃™I‹I:MÒq,IÇHZSRIgJêTH°¤M$µŽÙù%²=$m'©¹¤ã$u+ÈúJÚ#ö»’¬·¤ƒb/I[d=$õÚQÒ.YWI'Ij%isIûdJ:CR»0ÑügAÖ6dIZQÒÀP¨‘Ô2Ê\NÒ"’N—;Ù)Ý›£Â¢}”1¡Üå&š³K:9K²þ’¶—›•+i‚l[I‰ý’6*È6–tHížjRD#GŽä¼óÎcܸq4¨|€7I’i !f€¾Âƒv–Xx½p<)þU
Ù<¢61[²—xË̶¦2ßLE}&öKׇëSnF3:—í?¼_v~á8Wd°^YÞâÜ¢À;µ¨G’4Iäki¾7³Šægfö“¤I’Ú™Ù×’܆¢üW‚ˆY•›€^øûaåøÀ^kfçĵ×+/LÒ\xpÉ­ã¸3nfWrIJJ\oy`L|¸Vtcõøç=å^/÷Ö4³o$ýˆ+C7EWšÙP¹Ép}8‚™á˜Ù{ñ?è+’›ÿŽgÿ3p•¤Íð­½náqócà†ÂÚ‰Àªá9ôU`¨¤UqòBªL«GCBAj…+ÊÙpÜtû-\Y
ìof&iðŒ¤÷p…u(pž™M”48WÒX\¾¬ûZnf}¶¤ïÌìlàà[3ûPÒGøÀá‰øLèiQß×ãšqE¸p°pJXPL®–ÔØWèW3³%}¿±ÞW²_%½÷fP’4õ®™ÙXIHÚw»Ý›É JÜŽ¿ð?ÃgOšEYoɊϸt¾3³Ç§³Ž?Jz8SÒÕÀœÀÜfv›¤ñ(ÛTÈºŠ¤}€/ðÙŸÃ㣥…¤pŧ“+;àæpJú3={MbÆKn>³U³^I’TϪL\«£0±Ù÷ð܃€È×ÿ݇;$y	øŸ‘íiK÷K,Ц”ÏP•¸ØÌ¯EÜø“3Uäëç2³ÒÀÏhª<_–Ž1³	¡Àͪìƒ{Ø<Ÿ‰[ÝÌ~
Ù‘À£ÀyÀÀF%3;KR/àb|Æg3²%­\¬`fo„ìQIƒk9€SÍ쉽"w¼qðp³™Ý²%õ®F¯•ú•Ptvî¢ÊúàØMÙò«åˆYÍPrúÏ…2ÝWâJJõß
÷fg »™•Ƥ¾@ïR[2³Áqo.ÁÛènföqÈþ÷æJ +0ÀÌ^Ù0Iƒp¥°0hzûçÚ·oO¿~ýÀ_€/ñQºãü@)>ÏÀ¹f6\ÒnÀžxçõJ䨨­ú¸:ΟÁä#©Å²O¿C
iþ‡Òw¢ëFý¾+¤ë¯K*çE\¹Yï ×.­À)ïç×)¬8øÉÌ~Ž‘µ¿ã³V;~ßÀ-…N*I’jˆµ|“$ÍQiK˜ÿühf?VÈ^*cRXŒ’tn¢z·™]*ia|=ø»è¤øÐü…
ÊK|Ï_˜qÚ‚É×÷•êÕ
À3ãU¯lSªf³?eòôߎãÃøeI«šÙóÕ]kV'Âàìkfïd?Ç Õ‹À@3{¡,û>øŒüÕfö@™lð	ð¤™]S&û7¾Nô[|Ö¥XŸË$퉿ÿ·*“Ý#i$>cÔ¹LöŒ|é~À’Vˆ{gfïH:Ÿ‰éif
²Ïå&rW}­k.”߀ûq%ntAö‹¤½qËSÌìÙ²ßø7\‰»ÁÌî-“|!›_³úð©™m[µÁMêJ¿ký’ò$_ßöHœï¿ñûBÞ›ðÙÍÅãÞ|^ƒÏ4-…ϽS¯3[ØÔÌž.ÈvÁg—æö4³»¨@¶é)"i¤(¡&õˆ$W€˜ÖmzóçÖx·ºx¶Ù>fÝ­±´|$ýîjd÷§±ÜNÕœŸh5ùçªåõÚ­§±®mgt{˜QíŸq¸7•¾73,ÉÀ‡V¸…Á€BÖW(ÚÿÀ•!ë…;£YØx«ô<ñõ[ãñžÿÃM(´ÅOñ5E«EºeCÖWˆwÂŽqøl¸Ùã3ø¬S|VªOÈÜ‹[9´Æ×sí[ø—ãkmJÊÎÀ‚l ¾6µ9nÕpyA¶/nâÙw·~OáÞôÁ-&æÁu?´YOܺb1Üäp4Ð.d]ã7¯Š[k|ZÝÿ_¶d¤.ÚGn¹U·c6½…Ìè’[ýl
õ“Û̹5¦ö{_lVv®n‚:ÃïUSÜ¢}à>æ‹ã¹JÆê¡d,²V¸[óøz®q@	_÷u0/n^½Uá:W†’Ñ7Û« ;)”ŒøLÔqÙþ¡dÌŽ;§(*gÛ”Œs˜\9[7”ŒE#˜\9+)«àîÚ‹ÊÙ‚¸B¶	¾Þöª”³¶øÚÒ~¡ •³–xØ…ÃC±ùœ*åLøÚ¤³qåì`›Â︸‘ÊÊÙ	¸éhózlÉ,H~;äVŸ`i—TKc1qJ'Ù>’šÈö‘ÔDšÀ%5‘&pI}"ÉC $I’$I’$I’! $I’$I’$Iš©%I’$I’$IÒdH(I’$I’$I’&CB-ùÓN’JdûHj"ÛGRÙ>’šr|’$µgº½À%I’$I’$I’Ì¤¸$I’$I’$Iš©%I’$I’$IÒdH(I’$I’$I’&C*@I’$I’$I’4RJ’$I’$I’¤É
P’$I’$I’$M†T€’$I’$I’$i2¤”$I’$I’$I“! $I’$I’$Iš©%I’$I’$IÒdH(I’$I’$I’&C*@I’$I’$I’4RJ’$I’$I’¤É
P’$I’$I’$M†T€’$I’$I’$i2¤”$I’$I’$I“! $I’$I’$Iš©%I’$I’$IÒdH(I’$I’$I’&C*@I’$I’$I’4RJ’$I’$I’¤É
P’$I’$I’$M†T€’$I’$I’$i2¤Hê$iIs×"Ï‘g¶ú¬Ûô"iÞ¨gûZäY|fømI’$I’$IRê\’ÔLÒ’N–t»¤ç$Ý)é\I}$5¯ëkÖ{Ͻk‘çüÈÓ±.+"éIHjSü,IJZ¯y¿¯§vznQ‹j¢~Û”3{*^I’$I’$I}P§
¤å'€€ÃõÙ€õ€ý€›w$m\—×Y	ØX«\ ià`/ _5ùù÷õUÁzd]ààÌ]‘$I’$I’d֣ΠI«ÏÝ«Å̬­™­´–À•¢ŽÀêuuÝÌNÀÀgu\î#ñ·WYéÜÇ•äaæ¶ððRœ>¯çMuZË$I’$I’$™É¨H’ðì–À!f¶‹™½_’›ó®™
t†OE™KZ¨ü:±VgYIsÖ²ŽsIê\Ë<HªÖÌ̾4³±fök
e´’´Œ¤ù%Míý~$þ®_A¶>ð
p!°”¤ËäëâÏõ13³¨ç„¨ç5Ôs‘êLîªI?[¬ªUŠç·Ð”SN±œQ箵¼·I’$I’$I¦®>ûâæZog×”ÐÌÆ˜ÙãÅs’®–ôU,¼?CÒXàà²/"é|VãSàuà[IoIúCù5â£ø+IIZ]ÒóÀWÀ‡’>‘ô§šê(iwIïâ³,ŸK%i
énŠëtª ÛJÒ«¸Ú(ààI‡Ôtí`$0XSÒìe²^À0àá8^¿L^:~¤P—ý¢žÛW¨ç_%}¼|-éiI+VW1IsHºøxøJÒIm$}!éÁ
yZÅsý~c$})i ¤…tùÿöî;<Šjÿãøû›F
¡÷Þ«T°`û)öŠ×rmx¯Ø¸vE¯lXÑ+(*EÁ‚ˆ¢TAz—" ]ªÔÐ9¿?f’@`³ÙMöózžó$;gæÌÙ™ÉÙýfΜ£ý—wûuNIçûë5³÷ýý¯~Ç;¶›Íìþ¬ê-""""Þó99¡µÿs°sîð)l_¯›Ü{xÝ·†ëÓä×:Ÿá}áÝ4Ä{Îå3û?çÜwiÖöË;¸ø/0«
Ü1³8çÜÇ™Ôå6 
ð!°	¸hü`f5s»Ò¬[ÔßOº@ÒÌ^¶½ð¶Ò@{ 'àœK2³©Àyxåx¿Ü:@Eÿ½ÌàDŸ¥Ù<¥[Ü„4Ë
úõŒËPχ׀
ÀxÁÉÅÀÏþïdX߀Ÿü:F€›ñ—â@|†m
â=ÖøÆß.	¸Üßg# ‹¿úÏ@)¼ó:ï¼¥Xéÿ|ÏßßxýU@9¼c[/cEDDDD2rÎ9Ix_lpÃ)nÿ•¿ýŸ@ÙLò±™,¯†wggQ†åýòðt†¼ºxwW6Ó,Â_/P3Ã6ŸúyÝ3,ÿÁ_^>ͲæÀa¼/æ2©sl“'ý²ŸK³ìNÙþë	À²4ù	À`;`i–?âowsše%üc·¨œaßo¥9~ißÛ
þ²Ï2”‡×­Ñ32”õŒ¿üñLÞãû~^ç4ËÎö—½ÉúÑþùY“Ùq¢½–•””””””””òo\Nu«éÿ\“1ÃÌZ˜ÙÇÒËY”ÓË9wÌ€Î{†%)“åkðî4̤«x]æ^ϰÍr`ޔ̆…èœ[•aÙþÏ:YÔ;­îx_ÔŸrÎý•If£È| „sð€ÙþëI@]3+ï¿>æùŸã¸/`êçœ[Ÿ!ï¿xw—2º-%?mùιCxwºÒñï=‚w'­w&å¥,»úuMÝ^ÐG&Ý7ÝqžÅœë÷·ÿ3³ID«âÍC“Ö2¼.bÍËjfV
o(íV@¼îgÅó÷³<Ãfsû2)n*Þ0Ó™42Y–2hC¹¬ê—FCÿçøã®ub)ϵ1³~àtð«;ÚÍp’ÿ³ÞÐãNbß)ï}zÆçÜ63[Ž×1­ÚÀ!¼gš2š“ɲªx]âÖ÷{ñP:†Ô4ÈF}qÎ%›Ù'ÀcxÏ}|
üâœÛŸ2DDDD$²åT´ïîCÍŒιQx_tñïTsW$­™-4³6xwDïŽÏ÷À.¼î^—-ñºÉe”ÕðÔýŸ™
·7“e)wm²3‰k5¼.p›²±n–œ÷ЯÀùxAÐz 2Þh{)¦ùû:/ÊìùŸ¬¤¼÷㣌PY`gw—2;w)×C] «»~É?м¨G…˜˜’éœK4³Ù@3+†÷üÑv`a6ÊNyï':Fi­Ãz;Ú9w$C^fÁdÊ>¾sÎuÉ$ÿ¤9o(ïÌl и
ïÙ¤̬u6ºÿ‰ˆˆˆH„Ê©g€>Á–øZ? É1fƒw'bi&ÁO4Þ—à¬44³¸L–§LÄšñYŸœ°ÄÿÙ>Êšàÿ<ÇOûðFKkP/ˆ&{ÏÿÀÑ÷Þ,c†LÕÍd›¥þ>ÎÌ$¯c&ËVàøÖ6‹ó™CþÏBÇ[Éyf;çžðëºï¼VÏæ~DDDD$åHä?ìÿ¬_ÞP3kŪ'½?ÿy—}@3«!ûßxÏ™d¥^©T~wÛ8:çLNê‡÷\Ëóf–1ó$'윉7P[à\`Z&ƒA¤<ô¤ÿsB6ËŽw\ÿef%2ä=ˆ74yF)s<½hf©Ã]û“±óL—îÞÇ»#öBf•0³(3+™fÑZÿg«LÖ-`f™ÕËáàyF çºÀ¼ƒ÷ü½ÀT3ûX„7K Þœ:«O²ìQx]œ¾6³ÿá
a}	pÞÀ	ÇÜÅð-zû(ü‚,=‰7p¿s{²Øî”9禙Ù{@7`¾™õÅ»+”€wç¤$^—­ì”•2Ðùx£Ö½—ÉjS€d¼ãÙ€œs›ÍìU¼Àuª™½ÂÑy€nÁ›Ô¶^p‘²Í3„w.šÙ¼;5Cûüº¤õ8pÐß`u8ÞÝšòxƒܼÂÑ®“áÓÓÌlÞ`‡ðæ†rÀ3ûø
/Xª‰w-ÔÆ:ç2}ŽLDDDDr0òÿÛŸ™}÷…¶3G'¸¯+ÔlüÉKO²øðæ­¹orSð†Ü¾oR̬ ÙxŸzøËöÝœs™9Â9wŸ™ÍÇúÕ4Y{È0,w6LÀ€ Íó?iöµÓÌMÉþó?)Ûö4³}x|ä/^Ð<‚Y†mn5³EÀõxèB Ðo”¾
Ößkf-ð†Ö¾8Œ7bÝŒ4ë;3K	ŠZáu­‹ò×™ÌnÅ»ŽÒ–3or‘,þwÎ/ØëêUoä²uÀÚLœ?Ù2«â²ö‡s.ËQÖ̬1Þó!ι›ýe5ñFé òçÁ©Šw¶«Nb \cf±xÔçÜÚ­ŸEíÉÀëιG²X'
¨wMlÁ»&Oa_ñŽiy`°Ú9·ëTê-""""‘ÃÌ\РPÊ,’œ‘fN¢tË€±x5œåœ›’ʉˆˆˆˆ‡™¹œ|H"Ã`D·ÉxÏëÔÀ{f¨0BÁˆˆˆˆ„3@r²fá¾—ò|—Ã{nè1àµPUJDDDD$;òk¸8¼ÑÁvùCtKó‡£.l=•çxDDDDDr[¾}HDDDDD$#3s92ªˆˆˆˆˆH^ HDDDDD"† ‰
€DDDDD$b(‘ˆ¡HDDDDD"† ‰
€DDDDD$b(‘ˆ¡HDDDDD"FL Œš¶ÅåDE$Ø1\×G~VâšÀ®‘S¤;@"""""1‰ˆˆˆˆHÄP$"""""CˆˆˆˆˆD@"""""1‰ˆˆˆˆHÄP$"""""#ày€ŽçàýŒÒ÷è΢c‰/^’f­;P®bÕÙ‡sŽ™SƲrÙ¶nZOÑøª×nDë³.¤h±â9²¬|ýù{Œ>Ž_Ç
ÿüOP÷•Ÿ%%âן¿fݪeìø{	%JS¯q+Z´éD\‚AÝw¿^1oÆDºÞûgEº<ç_z‹ÃG’èxÑué®ÙÍ×2~ÌP¢£c¸ú–îXTógLdÎô_سóoŠ/E•u9½}gŠ/p=»]×–ÃI‡è5`4%K—¸¼¼`ÿƒô22õu”EQ¾t	ÎlÙ„ê•òß1رkGŒN}Cé	tjÛ‚
e¿†ò›}ûòÊÛߤ¾Ž‰¦t©x.èØ”ÕÊpà`õ[?À¢©¯S´ÈñÛ“5ë¶ò¿Ï&d™_¾lqî¹íüS®óêµ[Ù¹+‘JJR¦t±S.GDDähC?|í˜åÅ;C&Q©Z€Êß¾õ/^yâv–/žã•k†sÞ¼yíÏ»œ‡ŸPù'’¸g[7­gÏ®AÝO~¶bé<^{ê.6o\¤?‡7ÝýWÿ£{P÷¿kÇ6¶nZÏþ}{É33–.šÅì©?qèànéötjÞ_ÂWŸ¼M‹¶çbQQ|9ø->íÿÒ1ï¡Ç‹i×ñÿ®çÖMë8œ”Ä‘#G.+¯Øwà ¯}8ô˜å1ÑÑôîq77_vê_DÃÑö]»3}¿q±±Ìü²?åK—A­ÂWbâAžë=ü˜åQQÆâioP¿N%
ˆÍv™«×nÍ´ÌMW(êñÌ'Œøæ7^}¾+ü+ðvADDNMP ´^ÿx¥ËVäÍçîcîôñ|ýyº=ö:+—Îgíª¥ìÙ½“"ñŨӠ9UkÖOÝvö´qNJ¢IËöü±xÖ®àâ«ïàÝ—dùâ9T¨\ƒ?ùuµ qïnÏ›ÆÖM8°?‘ù3'Cãí™óÛÏ8ç8ãì‹Xºp&×®b⊗,CÃfm)]®"‡`îôñ˜EѼMGæLû™½»wвíy/Uö˜÷·}ËFæÍ˜@íÍ©V«A.Ѽïàý¼þôÝlÞ¸††MÛpÏ^¥RµZìØ¶™9¿ýB¡Âñü½mü>—b	%©T­6³§ýLÙò•©U¿)KÎdó†5ªÔ¨G“gbQQ,_<‡õ«—³w÷NâJP·Q‹þƒ`û–,ž÷{wï jÍ4nÑ.€³Þ†½Ù“²¥ŠÓgо7…§Þü.=—¿¶lgáòU”-U‚ÊåËðËos¨[½
-Õåð‘#L˜1Õë7QºDíš7¦l©£w…%%1vÊLöí?H§6Íùsý&¶ïÜÅiõjR©\ýñ'ëþÚBÝêUHNNfÊœ…\pf+*•+Lš9ŸMÛvP­BY:µmALttjÙk7nfæÂeìØ½‡ÊåËЪq=J—ð®Ë-Ûw2sáR6lÞJ¹Ò%iÙ¨.•Ë—I÷~g5€¸ØXn{¼³-cà°Ñ<Õ­«—·x9ËW¯c×îDJ$ÄÓªq=jU­˜ºí“gàœ£Cëfü:w›¶þï‚ÅŒæLèM劥èzÏ;Œýe>o¼ûÞ¼›¸Øz={>šòÛR-YG»Öu)R¸ _KÅ
%hÑ´Ó~|€qðôKC)Y¢(£‡>@‘Âض}ã§,â¯M;©]³<žÛŒ¨(cÏÞýü÷îæåGÿAttÉ.—œLá"ñ¼<`4UkÔcåÒyôzìVJ—«Httlê]¦*5êÒkÀ÷.ŸºI?~Å€×ãÈ‘ÃXTõ|öçuáô³.¤h|q¶oÙÈ¢¹SiÒ²=¿Ïÿ­›7P¸H¾ü.
jUcÇ®=lÚö7Ýu#•Ë—áêûŸeùŸëRË(Z¸}ŸéÎEgŸARÒaºt{ŠÙ‹—P®T	
,Àê
›èóÄ¿¸ñÒsùèË1ùæ'Úµh̬…Ë8””D­*Y¿i·=Þ‹í;we$';šÔ­É—}Ÿ'¡hþ÷Õ{mQQFtt4II‡¹ýª‹xùỘ4s>×tï	xwv%%qNë¦{³gº÷»mÇ.âbc8x(	€ØX¯©>’œÌÅw>Š™ã•e¼øàÜ~•w-þãÑ—èxFsÆOŸKåòeò}´íï=ÄÅŰÿ€÷ù篃‡’¸¢ë«ìZ3ˆbñ…xæå¡ü÷UïÑÑQ´nQ›i3—sÅ¥­ùjð#´iåµíkÖm¼®u)ËÆM\È9—>ËŽ‰©ç¾M«:ŒõñE1è‹	Œ=“g½†‡º]ÊW¾ÀúÛ7òi^÷;¦ÎXÀ Ï'2èó‰Üqs'¾}Oî(rq„ž\K÷›Îfñ¼i”.W‘›î~,5ï?/~Ȱ‰ë:a-Ͻ=€ƒÞÄ%'§+cÓ†ÕÜÿÔÛ<ðl?V¯X@ttõ›œÀÔ_¾¡ÿ+=èÿJ>xã‰tÛØ¿¨¨hï=ˆ+»ÞO|B	^~ÿ;FLZÏÐñ«¹½û9r8‰/¿•n»ää#T«Õ€>ƒ¡IËöìÞ¹¡½žnµ.宇_æþ§ß`ÊO£ràˆå«ÿðÎaŪµRƒŸo‡H=‡_}òvºõ7®]I†-x¦Ï´?÷rª×nÄÛC&3|Òz¾øùO.¸¼+û÷ðÝÐ÷Óm·móF.¾êvz0†ŠUk±îÏåüðÕÿÒ­³yãjžxe0Wÿ£;.9™_þ€ØØ8Î<ïr&üàu™4Öûòtæ¹—W€‹¯º‹ŠâÛ¡x k_}òvjP<éǯøyôç,T„‡ŸÀ ïç‰W>¡`ÁÂ<óÆç›°Žaã×òxïÁ8çö¿ô×Yн»wÒÿ•ÿpèàžzíS>»œÆ-Ú1}Ò~slªü`ÔÏSèûéH^|ïªV,G©âGŸ¡X²r
Ïhΰ7{rAûÓùﻃYþç:ÚµhÌ”ÏÞááÛ¯eï¾ý<ðR_<İ1㙽x9åJ•`T¿xòÞ®¬Þ°)Ó}ÏZ¸Œw½‚aoö¤vµÊ<ðÒ;lß¹›gÿõVûœ›/;Ÿ…ËWñÆÿ†0ä›qDEsF~Àº	ÃømX?®8ÿl>ýv#ß}u‡1ÿ›évc—cöÙùŽt¼åA._Ej•¸¿ë•D™1ìÍž¬›8œµã‡1ðÅ$';ú|<ì˜2þÚº÷z>Hïwpäó†®|ÆífÒÔ%T©TŠž¼>ÓõÖoÜÎË}¼ö¹ßkÿdÒèçRïÊœHr²ãÖnï²cg"ý^û'{Ö
æ¦kÎâ·YðÊÛ^{ñáÛ÷R¹b)^|ý+ºÜü
kÖmå™WÓ¡}#ÞîuçÓ€d¹_áÙG¯Éw/""'+×î]Ùõ~ŠgÚøÑ¬X:>=ïã©×>`ëæ
¼ùÜ}¬X2—½{v^´oI÷À÷UÿèNÇ‹¯`Ö¯?päÈa¶nZOùJÕY³r	SÇËž];ˆã·^JW‡{}•JUk^`³pöFz‹Uˤ>²yãÚcê~ímQ²tyÎû¿›X8{
ËÍJ—ߨY[Îé|5I‡òöÿͦ
«Ù»{gÐaÈëâ
¼%åÜÒ3˜7s"ûöî¦vƒf\ÙõþÔõ.J÷§ûëueÙ³kcGâßç°fÕØ{‹/Åÿ]7fF›³/æ«Oß9æ¶?ï
Z´=—ø„’Œôü>75¯ÓÅ×1vä ¦ÿŽÛ»?ÏÔ_¾ ƒßý
 óÿ IËö|;ló¦O`Ó†Õ|òÞ‹ìߟÈMw=΂Y“h}ö…´÷ªÓÛ_ºý_ëÿdì¨Á¬\2ÄÄ=€èìKÜ“îNÀÊe8°?‘˜ØXFéÇÈ!ýغɻӱhö¯œ{É
'uò‚×?:ú¿n*¼óôýéòK—H Ï÷afL›ç×·\~uªW¦Û]xóãìܽ—k70Ë¿ósnÛ´mÖˆ¶Íñæ á¬Z÷×1û>§uSþóOï˜nÙ¾3u1“¦óÓ¯³Ø¾Ó;_¿Î^@›f
Y¸|m®íF‹Fu¹àÌV\ÞéLÚ6kÈ×ã¦pm÷giR¯ç¶iÁåçžyÌ>½ó¢£¢9n
KV®¡Ç+ýéûLwÌŒåkÖóñȘ·t{öîK­×ÁCIˆ;ÚÍëñ»oâ³ZŸì¡Î“{ 	Å
óå·Ó™5w%7ßýß}ñØ1ëÍš»ŠÃ‡P®L÷Þîýý]yikú¼7ú˜u3Zµz3þú€/¾ú•¡#§²y‹÷y5~òÌJ8~IDATbž{J–(Ê÷Óáÿz2~òbÎ9³!O=r5ª•¥x‚×õ¶b…’êú&"B¹u¸ðªÔ¨K½&§óT·.ÌùígöíÝÍö­›è×ë!J”æúþ‡B…ŠÐç¹nÇ<ð]§AóÔß«Õjú°ùoFÓå¦û¸áÎGiÛñR¼¥Ó1û/T¸hjððëÏßðÙ€^T¯Ýˆ®ÝžbÛæ
|6 É™}‡æm:qçC/3æDÆ}ûÙ1×Mròp̲<‡2œÃ´ç¯n£–TªZ›
kWð^ïìݳ“r«Òà´3R×9rä0«ÖâîGz0òÓ¾î÷_fNËMw=N”ß-íÈáÃǼ¯Kæ1àµÇ(U¶"×Ýþ0`ôëõWL®Ç”²¢¢¢©Q§@êÏ*5ê³~~Ðÿ¹‡¨^©<åJ—¤B™’©NЦõk¥[í£ÃþñKNv8\j^A?PHÜ u›Ä}GO«yƒ£Ý]£¢Žî£^ª,àuå=ûôÓ(_€§»ÝB»æ3i3,¡ç;3aú<†¾ù,·téL­*ùvü4f-ZÆ«~ÁÐ1ã™9¢º}Þ|Ù”-Uœº5ªpëc½ønÂ4Þ|ò_ÌYüOõHåòeèqûu$>Â^íŸú^pôï£YƒÚDŠ®×MÃz•i׺ç\ú,cÆÍe×î}©]áR,èŸýqäH2ÑÑQìÙ›ùyÏ(åÜ›T%6ÖkC:ŸÛ”òeþ³ëÏ5[RAY³n+{öî'¡Xá€ß£ˆˆäœ\ë·dÁt¦Oè!ïW€…
óû¼i8ç¨X¥g×…
kWdY†ÙÑê–)_™ÎWü€Ï¾BŸçº1{êO,ž;-óm3<±xîTê5nI«vç³vÕ’,÷;ôÃ×X¾x6?~íu¿ipZdüW5ØZ¶=—†MÛЧç½ìó$fMfåÒù™®e™ŸÃ–mÏ¥Ái­Y½â÷L·Û»{'_~òçüÊo½ÿôÖ?ÉsØñbïnOJ׸s.¼&Ýîºv`p¿ÿòÛ„ÑÌž6Žù³&¤Ý¢”Ïœ2–±£ó×ú?™üÓH÷îb‘ÿ>ªÖ¨Ç™.cÚ?Ž[—uS¸h1<@Õšõ¹ùž'¹ê–îÔ¨Ó˜r«ÔûÊ+Õ®Nó†u¨X¶Ô1Áx]ÃÒjßê4ËœÅËysÐp’“eJ§VÕŠthÝ€qSgÓ÷Ó‘<úÚûlÞžyW¨´AOé	Ô­Q€„ø"›af©Ïeå‚Ë»2~Ì0ÆŽÌØQƒ³ÜoTT4[6®áÑ;/ D©r\sÛCÙ®£dÍ¢¢èñ¼÷JfLþÑÃ2zø@¢cb©Ó°Åq·¿øê;øã÷¹ìó$½õMZÛ• L¹JŒ3”!ï{]"«×nDç.·œT]Ïé|
CôJ}.­cšîoà]s#?í›nYÑøâÜt·7F›—pÙõwóí°èÿJÔu>üf>/º–/½ÅÜéã³õ7P4¾8÷?õ6}_|€~½æ½Þ¤þÇùgß=©÷•_=}oW–¬\ÃŒK¹èÎG(Y¼}ŸéN\l,ÏjÍM—Ïo~â¿ýsz“úÔ«Q•e®%&úøCß§»sû½éûéHÞ2*õا<§Ó÷Ó‘,]µ6Ýpè÷\#ÆNdô„ßÒåÝzå…$-ÂÖ¿w¦îãžgßHý½l©âôy»#zÝ%è;d$?N™IÝnN
ä"ÝÝò TÉxÞ}õoˆÃéï O(Bÿ7î⎿ÇGCÆSºT<ÎnÌÈïf¤Å/3fÆgÜϵ·õá¹ÞÃyþ•©çð…'¯çÀÁ$®»ýM÷䳺sm—¶,ü}-_~;~Ž¥Û¹òÒ3è÷á|ÿÓ\¾ÿi.ïô¾ÝyaÎ9.œsîØ©fÓ¨i[\VyG'±dÁÌ£;‹2Š%”¢|åêĦîÈá$æÍ˜@l\A6mÃÒ…Þ6õš´"66Ž¥fpøðaê4lN‚…ŽÙÏÖÍX·j);ÿÞJBÉÒT­Ù€2å¼ÿ´:x€å‹ç:R\Šýûö2oÆJ•©@ÕšõY±d>111Ô?­5Û6oäÎ+šËÐ_þdÑœ©$îÝE“–gŸP€-­cË_ë(U¦<ªÔ`Ñï¿ù
N;è˜ìÏ?®º´-{Ê×ÿúHkÚ?ذv%‰{wS²T9jÖ;-õ8ïÞ¹µ«–Q$¾5ê4N·ÝÎí[X4wÕj5 >¡ëWÿ‘ºÞì©?ñÂ#7S«~SžçKÍ™Š™Ñôô³S'X]»j)»wþM¥jµ(Qªö'²bÉ|bãâ¨×¸Uº}-_<‡C›i€½öÏel\»’Ľ»(^¢4
›µ¥Pá¢éÖÙ´a5þ±ˆƒöS©jmj7h†™‘”tˆyÓ'P°pê79e½g”4mMtt‹çMÃ%»Ô¿	ðº._<‡¿·m¢h|qj7hF©2²s¸sL ×Ç–i£²¼>’aæïÎló†u(TðØÿèoÙ¾“kÖS"!žµÒßýrÎ1kÑ2VoØD©â	´l\—„¢G‡?ßà ;vïeß”.‘@ãKn%éð¾ÿ 7-ÕeåÚlÞö7U+–;f˜êCIIÌ\¸”õ›¶Q¸PÔªFíª^›³s÷^æ-]Áæm;(]"
ëP"Á{Ž+qÿæ/YÁ†-Û(R¨ -ÕMßçÀÁCÌñŸMˆŠŽ¢TñªW*OlÌÑ/ç%1~ú\Š)L«&õ˜µÐYìŒf
‰ŽŠbêÜÅà§7©Ÿ:z\¨”mÛ% ëƒó¾>’Žðëô¥©¯£¢¢(]*žZÕËQÀò:9Ù1iªwgø¬¶
ˆŽŽbßþƒ>œÌªÕ›iX¯2^ó"ã'/æÉ‡¯L7xÂÖm»Y¼tqq1´k}´kéþ‡˜6c9k×o#>¾MU£vÍòlÿ{_K±´=½nº2
ŒKI.9Ù1oájvïÙGZ¨T!‚çw*qM`ׇˆÈ)03Ô(/K˜´>ÔÕ	™Ü
€‚!môÚG?†ªùZ0 `ëÐõjT®@ÁqLš9Ÿm;vѾe†¿õ\ºnorꂪžø˜yWS«z9~›õ¿/[O‰âE˜?ù5ªT*•Ó»“ãQ$"!`f.´ÿc±qq4jÞŽ˜¢¼*>¡$š·£RÕZ¡®Š„¡K;´eò¬ìNÜG³úµ9§uSnéÒYÁO>×鬯,_ñ³æ­¢l™:wjʃÝ.Uð#"AtHŽ+/ß’àËËw€$øÂñ„Ý‘039ɈˆˆˆHÄS$"""""CˆˆˆˆˆDŒ€ŸÉôˆˆˆˆˆD@"""""1‰ˆˆˆˆHÄP$"""""CˆˆˆˆˆD@"""""1‰ˆˆˆˆHĈ	´3s9Q	OÎ¥ë#ÓbLj?ò7µ"*@ F,¿Ê©/º>ò'}9•Ü ö#Rû!"¡¤.p"""""1‰ˆˆˆˆHÄP$"""""CˆˆˆˆˆD@"""""1‰ˆˆˆˆHÄP$"""""CˆˆˆˆˆD@"""""1‰ˆˆˆˆHÄP$"""""CˆˆˆˆˆD@"""""1IžbfÅÍ,ö$Ö6³Á¬“ˆˆˆˆä
€$¤Ì¬£™½t›šŸÄúu/ý}U2³¾f6ÑÌn;™zŠˆˆˆHþê
HÄ+ÔÉ,ÃÌ
Î9w(“¼8 É9ç2,ö·IΤHü$•­¸ˆˆˆˆä=
€$,™Ù[@} ÚÌVw¦	vîêùëõtÎý`fQÀ3@[ù—ιiËtÎm¾6³Æ€åÎ;‘p¢HÂÕÃιÃfö!Ð
˜éç%:çÚ˜Y%`ðШêœëlfL6³ÏCQq	_
€$\]ff7Eðº«uâhô=€snƒ™1³*ÀÕ@c3å¯S8X–»Õ‘p¦HÂŽ™žZ9ç˜Ù{@Ú‘ß*ùëlV«sod(«AîÔZDDDDò@ª˜Ùõi^
í̬"p>ðqšüÛüç‚Îæ8çöûÝÝÆ›Ù&`^—¹1iwâœp%ÐÄ{i×_;çöçm‰ˆˆˆH¸Ñ0Øj¿ã=ÃS?MŠ®þïnÏ­À$ýÏîÀyÀJàzçÜfàL „Ÿ_Ø
lÅ: Ú/)°Äÿ=Ûs
‰ˆˆˆHÞgxCŸòˆXfÐö¾râÜêúÈ¿rêúÈ©úHø‰ôöÃÌ®š;çžñ_7nÍ8„¤ÉëçVDò.3s
€$K
€äxt}ÈñèúH—l*ð80˜tsÎM
iÅÂ@^?·"’w™™Ó3@"""Aàœ;bf·#±À8?""¡§HDD$HœsKÌìà V¨ë#""ADD$h̬4Þ€.[€¶!®Žˆˆ HDD$˜ú¯7ý̬Hˆë#"ñ‰ˆˆ™]	”ú;çC€—C[+Q$"aÉÌJšYÛËZ›Y3«bfM3äu4³"fVÏÌj§Ycf˜YÐÚ;óœafw˜Ùiòš™Ù…iRÑ4y¤­«¿ìº`ÕSr]Yàö4C^÷6šYáÖID$â):3»ÂÌžO󺹙
23Ë|‘¶xÇÌÎ0³ÓþòD`„™Õôó®sÎ%úÛŽ6³bþï—9ç’ƒX׫ýý”^6³!iòžÄ›°·ƒŸŠøu~o2Þ^)A“™]
Tb=#J´¿›;Ò¼n4öçPù""rŠœsŽSMnî	ˆ¦€8`Ð.·òCüÞ>·ùýúˆä”×ÞÆßñ‹…@ë4y—
ÀJ Jš¼€Ó€%@‘ Kó{°(ç¿´Êd›ñþ	up?Pøˆ
õ¹Í+×G6¶iûÎí{¨“>”””B•M„šfÖ€£ó8$9çÉÍüPÑD†r<¹u}˜Ùã@7àSçÜãò>ΞuÎ}”fy0¨tuÎM¤ž'Ã̪â}é­éœÛofÃÚÀ
`(ð¥sΙÙkÀd¼@î#ÿçÏι_r«®Á.íG¨ÛßpmßCMŸ
"*š5›Ü	æqv¾H„[
T~Î"¯<0!íBç\²™­Åën47¸Õ;ʶãàç\J7§ž@J]úãuá<
t>¶U€µföŽ¿Î‹îh—>9E¡nÕ¾‹ˆ„=”
'šÇ!Øù"‘ÊÌÊ/àý}¼•öáq3k܈D|˜ö¹
3»¨ô^Ï¥ºFƒsCS–;ç;çö8çfo7øË÷;çøw|^žþ¼…Ø]šõÎïBÝþª}	?
€²çDó8;_$Rõ^vΆáC˜Y^·±{s_€{ü¼â@à6ÿg#3;?˜•Lü|ãœëŸI^ŠÀ”ù]€Yιõx£†mõS¹`Ö9‚„ºýUû."f€`‡`ç‹D*3»ˆwÎ
ô½t4³vÀ£ÀçÜx?¯;ð°™UÞÞuέpÞÈo·}Í,>ˆÕ½ïA÷™ÙR?µòó›ÙÏf¶(€×å-å=ÂÜúø‹>Æúî¾	b}#B¨Û_µï""áIƒ œ€™ÝŒqέñ_Ço;çö;?wßmzáòs†òŠ‰Î¹¤l®
sÎíÈ©:ˆ'¯päÈPØêœ;˜!¯0Þu[Ó,+pÎÉÝšæ¬p¸>BÝþ†sûj‘ð·/"áÉÌœîØ‰æqv~¾fÞä•/Ä&ƒ€æ'±~]àK_™Ù3[bfšYý“©«È©pÎ%;çÖg~ü¼}iƒYb^~Â…s®Jðá¿>ìœë•|œ(?æ	:nû~¢òs`ÿ	÷ú‰ˆœ*@'ö
ÐÙÌ:™Yð?à}ç$žùù]) NffVÀ?&™åÅeö!kfÑþÜ3³ïô†xÿzjU‘hûœ×?½~""§D]à²Á4PÐÊ0oæûëœs×dXþPo"ÁÕÀÎ9gf_;zþª=s?øAÏ3eéKçÜÿؾëœë”¡ü²À"ç\Ù@Þ_$‡.N¾òËõhûœ×?‚U¿p8·"™LóeÓ<@¡ð°sî0€™}´fúy‰Î¹6fV	ü´ª:ç:ûw†&›Ùç™ì{	oN‘,Ú>çõχp¯ŸˆÈ©P¸l0Í
—™Ùp3û/øI{ç{çÜàˆ™U®›Ù(¼ÿF–Îͬ`3{(<ŸY¾ˆHŠ@Ûç¼þùîõ9º”=)ó8LF˜Y+—~†ö`çGóæMyhåœ;`fï±iV©ä¯g@°
X¬vν‘¡¬^?´®N¹Ã$"r¶Ïyýó!Üë'"rÒtèLóå†*fv}JÂj
íÌìf ã$–·™YgàE`Žsn?ÞÜ*ÿ4³ͬ±™Ýjfé&’4³=ðF…»Êߟþ	 "™
´}ÎëŸá^?‘S¥èÄÊ·§Õ¦°Ñ¼ù;r#?¿ûïžúiRp^·ŠJÀ­xÿ]/Ð霬®pÎmÎÄëÚÖ(ì¶â

Þ´ýšiö¥¿33§”ÿR¨¯«hûœ×?½~""§Ì9ç8Õt¢í+€çÓ¼nŽ÷…Ô²“êhýÃ=?s›ׇRÞM:·JÁN9ðù²ö3'¶vù¡üüUû¡¤¤ªäÈÌq·ÇÆx:ÞCìqÀ< ]vóC­¸çrns«¥ðL:·JÁN9ðù²ö3'¶vù¡üüUû¡¤¤ª¸\™ÈÂ|žƒ	´þážœ÷е‘SeHxÒ¹•`Ë¡6(¤óðûó-Üëwœýªý‘°ÜšÈåñy­¸ç‹ˆäW¡n?ƒÝþ†{ýDDÂQ®<ny|@ëîù""ùU¨ÛÏ`·¿á^?‘p”[]à¾&âÏ€7¿KbvóC-Ðú‡{þqÞ·ºÀI–tn%Ør¨
Iû™SÛ»üP}þªý‘P13ô;@–Çç´þáž/"’_…ºývûîõ	W¹Ñ.¯Ï#êy€òú<""¡êö3Øío¸×OD$,åF´¸#Íë&@`6óC-Ðú‡{¾ˆHždfW˜Ùói^77³Affιþι5)ùιÃι^ι}ÙÉ'ðö3Øío¸×OD$l9àXÚ'È×<@aœȹͭ2”Â3éÜ*;úù’ûëyv½~œ[%%%¥`%4Pö„zžÍ$áHçV‚-'>_r a=ÏN¸×ï8ûUû!"!¡y€²)Ðú‡{¾ˆH^ìö-Ðò#½~""áHóeC¨çé	õ<""á*ÒçÙ	÷ú‰ˆ„#Í”
¡ž§GóI8Ò¹•`ˉϗ¨CXϳîõ;Î~Õ~ˆHHh lõ<=¡žÇBD$\Eú<;á^?‘p¥y€N,Ôóô„z‘péóì„{ýDDÂRØÏt¢y‚hýó@~H™Y93kvëßkfÕObýòfö ÿ{13»ÄÌî4³ÓN¾¶"f"}žp¯ŸˆHØrŽ¥}‚ü°ž'Òó9·9t}\
?‰ò¾ZŸÄú
€_üß;o?âuûÉôù!åÄõ¡¤t¼èçKì?¬çÙ	÷úrn•”””‚•È+ó)?ÿÎdfW×9ç®É°¼=p°/@Zå/ÿøh,F8ç’ý¼xàFà ð…sî€ÿÞßuÎuÊP~<°(åü¿99zˆY‚-'>_r a=ÏN¸×ï8ûUû!"!aye åGä<@U€ïâÀwfv¦sn‡Ÿ÷8^_õs€.ÀfVø	x(ü´ËX¨™•Î.*øÉۂݾZ~¤×OD$å‰y€”‘ó
’ÀZàÒ4yŸ8ç¾ÆëÊÖÖÌ
WãuÀ
`ÏZàuÛ‘I¾ˆä!Ánß-?Òë'"ŽòÄ<@ÊÏ¿ó§Ü~çW«s/ø]àÞwÎ}ï¯7ÛÏ(ᯟâ;à™w‹Vmœsy‘J]X$Ørâó%êÖóì„{ý޳_µ"–æR~äÍdfE€ºÎ¹—1@í«tö×+pέÁ»ctÄ9×Ë9×è
,ÉPnÚ¡]ð‚£¿ƒó.D$؂ݾZ~¤×OD$\å…y€”ŸÿçêlfKSPgfãÙÀöëW4³)xÁÑþ²	À3›hf#€i@ÅÛ=bf³ýrÇ=sîU$ïŠôyv½~""a)ìçR~þžÈ97Â9WÌ9W?Mšãœ»﹞Öι«œs/øë_îw—»ØÏûÆ_îœsçÝñ†r]çœ[’ÒýÍ9÷<Þp¯78çZ:ç>Îýw,"9Å9×ß¿œòú°xäÊTable of contents
    
  • Manual
  • {%- if display_toc -%} {{ toc }} {%- endif %}
{%- elif pagename.startswith('reference/') -%}

Table of contents

  • Reference
  • {%- if display_toc -%} {{ toc }} {%- endif %}
{%- elif display_toc -%}

Table of contents

{{ toc }} {% endif %} urwid-1.3.1/docs/tools/templates/indexsidebar.html0000664000175000017500000000070612615524560021636 0ustar ianian00000000000000

Documentation

urwid-1.3.1/docs/tools/templates/indexcontent.html0000664000175000017500000000667112615524560021706 0ustar ianian00000000000000{% extends "!defindex.html" %} {% block body %}

Urwid{% if 'dev' in release %} development version{% endif %}

Console user interface library for Python

git clone https://github.com/wardi/urwid.git

Wiki:

Requirements

  • Python 2.6, 2.7, 3.2+ or PyPy
  • Linux, OSX, Cygwin or other unix-like OS
  • python-gi for GlibEventLoop (optional)
  • Twisted for TwistedEventLoop (optional)
  • Tornado for TornadoEventLoop (optional)
  • asyncio or trollius for AsyncioEventLoop (optional)
  • Apache for web_display module (optional)
  • ncurses for curses_display module (optional)
 
{% endblock %} urwid-1.3.1/docs/tools/compile_pngs.sh0000775000175000017500000000063312615524560017326 0ustar ianian00000000000000#!/bin/bash -e # args: scripts to capture DISPLAYNUM=5 SCREENSHOTS=`dirname $0`/screenshots.sh XVFB=$(which Xvfb) if [ -n $XVFB ]; then Xvfb :$DISPLAYNUM -screen 0 1024x768x24 & XVFBPID=$! DISPLAY=:$DISPLAYNUM # this still doesn't work trap "kill $XVFBPID" EXIT fi for script in $@; do echo "doing $script" if [ -f "${script}.xdotool" ]; then "$SCREENSHOTS" "$script" < "${script}.xdotool" fi done urwid-1.3.1/docs/tools/screenshots.sh0000775000175000017500000000126112615524560017205 0ustar ianian00000000000000#!/bin/bash # $1: python script to run # urxvt, xdotool and import are required to run this script CLASSNAME=$(head -c 6 /dev/urandom | base64 | tr -cd [:alnum:]) PYTHON=python urxvt -bg gray90 -b 0 +sb -fn '-misc-fixed-medium-*-*-*-*-140-*-*-*-*-*-*' \ -fb '-misc-fixed-bold-*-*-*-*-140-*-*-*-*-*-*' \ -name "$CLASSNAME" -e "$PYTHON" "$1" & RXVTPID=$! until RXVTWINDOWID=$(xdotool search --classname "$CLASSNAME"); do sleep 0.1 done export RXVTWINDOWID image=${1%.py} c=1 while read -r line; do # the echo trick is needed to expand RXVTWINDOWID variable echo $line | xdotool - echo "sending $line" import -window "$RXVTWINDOWID" "${image}$c.png" (( c++ )) done kill $RXVTPID urwid-1.3.1/docs/conf.py0000664000175000017500000002316612615524560014455 0ustar ianian00000000000000# -*- coding: utf-8 -*- # # Urwid documentation build configuration file, created by # sphinx-quickstart on Wed Nov 30 20:10:17 2011. # # 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. import sys, os # 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. #sys.path.insert(0, os.path.abspath('.')) # -- 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 = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage'] # Add any paths that contain templates here, relative to this directory. templates_path = ['tools/templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Urwid' copyright = u'2014, Ian Ward et al' # 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. FILE_PATH = os.path.dirname(__file__).decode('utf-8') VERSION_MODULE = os.path.abspath(os.path.join(FILE_PATH, '../urwid/version.py')) VERSION_VARS = {} execfile(VERSION_MODULE, VERSION_VARS) # The short X.Y version. version = '.'.join([str(x) for x in VERSION_VARS['VERSION'][:2]]) # The full version, including alpha/beta/rc tags. release = VERSION_VARS['__version__'] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- 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 = 'default' html_style = None # make readthedocs really use the default theme # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { 'sidebarbgcolor':'#263193', 'sidebarbtncolor':'#263193', 'footerbgcolor':'#181035', 'relbarbgcolor':'#181035', 'sidebarlinkcolor':'#aabee8', 'linkcolor':'#263193', 'visitedlinkcolor':'#263193', 'headtextcolor':'#181035', 'headlinkcolor':'#181035', 'collapsiblesidebar': True, } # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = "Urwid %s" % (release,) # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'urwid-logo.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['tools/static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. html_sidebars = { 'index': 'indexsidebar.html', } # Additional templates that should be rendered to pages, maps page names to # template names. html_additional_pages = { 'index': 'indexcontent.html', } # If false, no module index is generated. html_domain_indices = False # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Urwiddoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Urwid.tex', u'Urwid Documentation', u'Ian Ward', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'urwid', u'Urwid Documentation', [u'Ian Ward'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- 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 = [ ('index', 'Urwid', u'Urwid Documentation', u'Ian Ward', 'Urwid', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'Urwid' epub_author = u'Ian Ward' epub_publisher = u'Ian Ward' epub_copyright = u'2014, Ian Ward et al' # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # A tuple containing the cover image and cover page html template filenames. #epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. #epub_exclude_files = [] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True autoclass_content = "both" autodoc_member_order = "alphabetical" autodoc_default_flags = ["members"] urwid-1.3.1/docs/urwid-logo.png0000664000175000017500000000502712615524560015750 0ustar ianian00000000000000‰PNG  IHDR–2Fé…Û pHYsNN±Í3 tEXtComment‰* ´IDATxœí R90ÿˆml0£‘ñµÿYnd K áaÃrp$ت}ã>c»¥±¬Ñ­ñ˜#¡ª‹²Ç’Z­¾[=õo[ÏO§wƒà'öw>pHºß²™$ý ¾ÓÚŠ>WœüG÷®Òä`óßÿr8ùwø+~àŸÅs寴ÛüPý¡¾ô3±¥lcM}·@iµœPñ³-<·T™‡Ó³±D!*7€Ì|Ò§q–Љ5œÐ¿kå¸büÌ|Vž˜fö®~ÒÆ†Î’áL…£ ɧÉá=q%Ýo´þR~BîG)Ó«€%œ?ŠìëÔŽ2…´¿pΪ(KòšbÔÃ$@Ÿ$‰M˜Q(kŠåà/|ÎôqâÀ¿ZQ:ŽTÒú(3"m¾!ôoà¥x¢|€z˜£4§s^Ž(qyiÂ' ÃÁÞ”ƒV—å¯pÎpÚIû̃¯õ|6c~ý™[^À)J‡Ž©£—»¨‹yi¢õõá€þB=øð@˜v“%Ì þ´ù–û2óEèUšìð)Æ8±øÝÜ¦ŽŠ¾*ÃzžF|ºâ'š}ÑññÕÅsBŽ"èsàÖD|øòÏgóøú7ú9ÈîN9+±(!ïsS˜è8±yEÌæ„°²‚@®ÔNDFëÇçÒÂÞ•¼:±Ž!ä ^ÐЊ†}õ®ÝÒ¨Kêæ~€Qååà®ñÝ=< …üÑê’þ+­.ÚÄ. ¨E­ ÿè&LN¯…éä¦þ+­­u.ÑÍN½¦µe+ÙÕE/…1äMíÄÒnKlbœ´¹Ãc>ÝOp’ZƒpP<ÔÇCæ–Ù«ísJ)óÖ)Cv5"ú‘{kLm§“[$ý ‚NúH9ÌYM“w0W¸+…îØ]ùj@N< …Åà‰Â;Œ3«‹X?3èß°püÁfàô…྅I3ì>—ùê|‡´¿DSG¥®U[…#Ä0rŸ?ˆ§pËEaç2ÎoIW@f Á§E®¸JÈÑ(d§ÉÁB}@ÿF¢ÿ=@¡6øFÛ–<ÂD»ûÚê(<„LBDHÆ.ú|ý„Ø-ˆBYfü´Aܨݧy s1œ>±èÜÉaŽBIRô)ƒaÃx0:†l²û}ŽAð¡¯l tÁº‚"éñp†x_ñÝAašìû)D€Ô&=ÆìfbÌпM'_[£ùé=t ¤Kì‚ÄÏ‘ÓÚ2Pds IëÄC¡M`ôTÈ G­ã–RBOô‘JÒ(@.¹–@¡c[ú)8ÖMðAçÁ{M9þ€ÂÓGL!¡ÇŽ)…Òå{iRZŒBP6ìkè\€ RÈM%­¯Cìa§ðs4…:Ç S˜6÷Œú£‘!\ü•ñ'ÙÒHã±Ì+wƒÜ…ù9Š4¢ÏÆøSÞ–®¤W óXE®Mkšü©Aºß†Ÿ}Ášñ\°Â A³´K”øÆ1RŒáÑŸ™‡´²`ó¿ÆÒ“£•66eMPô‡Ï’-ž í3C×þʽ¥ë@Y=MN52 ÓéÝØ@4( c½%*…ƒ’&Köv`$ó•\J¡G¡¼°U<§È˜-ãf'†žuB•R¼Xwœ5v“ k˜zo9Íê’æßAu ¦{é¡Ç¼‰9Ûš@^Ô@R ~/¯áð¼å¬µIêþ0à\± DÃz—ç":qt¾¡ ³ÄÝÓtÎ1Î1c- ˜Ðz6y&fs’¦<”: ÁÍB…Eáç§ð§‡'>zxb¡ %@‡| ›&ŠO׃µ^ïc­¦ÑŒ¢,ìœCÄL¯Xxµ»¡ÇFtÎKl»€$Ótîs!ý6¼‘½„ð¾¶á#Ö›oÓä0ë…ì\”ÃBý¶&fÙþ^¢6ßòž´N0PÁ0ñޢݬËXO™‚¶×»r\£”Ã]­c>Ž…ò«¢b¸~ýY.ð°;#l:­o¨s1`± ÑiðV5l²ðI€ÞÇÇBÑü8>H§^7kë~Ç »¶ Ž@e ü“±2*åÉ;ƒ›F¡è]+½¥z*ÒvÎEChq «Ìg—…Ó{xAÖ>»3… ´"F¦S;Ø2ö—¤GÌñ_è;6É¢¬…2„…üµIÖt{^$œÅ7ê;DbH$k Ä›;mú÷c í;?µ¿ Š#øuÖØLÈá€ä[Zñ£H›o ,ÔØ#Úd9z¢ÇÂ(óâõ“׈û}¢Õe`€i¬!EAIC4^^¸o|Çæ—faÒ,L/ÌE³°ù†9é:ñ"ƒ|Ši¼ßy(,,rRa(Šl¾õ±ØÆlŠeÔž¢W=c¡|= Fmcì,4©ÑH,$‡zà‚ükž…†›Ø_†…Ý+}°Í÷˜#Rnå&Öh}¸úËìî_µ¸r³0Å䮉u­49`)—rüelI+Ÿ…ú˜{g¡-Õ‹<_W¬ÁMåXQ´ò=YXŸÊba1\z‰ƒ°q‡õÑ«eáb Iƒ$M°Ðظka!¶¯ ß9Å„¯¶ŒØÆ†;hæÿÂ8ÆqdØ0T]d–j»™ ¹Ä ï6´b9ßÓÏËÂVþ_#f¡­s¸DŠ—eƒµ µ%´ÒŒf‡oBx»uøAä+Ÿøÿ[Šõm³ÿ"0T‹Žµ·FŽD°jXØÆ°××"|¡¼¿´2—•~’w˜Å^³hMòÐð IØF{@ÿF%€È¥+.¤ gò»#vÆáƒu"RZµ¾°?Fö€ ­¯ã;ä=¦>£½×`ùœ’¤jžV—l-=¢6æhÚ Ýù`©°¼°sé½tއY¬k#‡QïïN°càÎÿ]Ãô.ÖrÓ#vY}‚§Ó>CÈzv3êåeÿ†ßê³Ø Êìà„oC»#R{|Êþk‡\a¡Ç Ðq¼_nl²¶×ÌÄE‡Àf‡îhyö.2óI¹i*¡ÀöAпEá®.9‚ቅåZæðv©‘ቅþèµ °ç¾IEND®B`‚urwid-1.3.1/docs/examples/0000775000175000017500000000000012615524621014762 5ustar ianian00000000000000urwid-1.3.1/docs/examples/pop_up2.png0000664000175000017500000000221312615524560017054 0ustar ianian00000000000000‰PNG  IHDR_S ü@ PLTEåååÍÿÿÿæïšwbKGD Lò pHYs  ÒÝ~üIDATxÚ혱nÜ0 †íö²gHÞÇyƒ¹¥ïÕ¥²Pî)@?J×@¼4 •¤ì‹ïNwÉ]œ"mø88’ü™¦(ÊTU™L&“Éd2™L&“Éd2™L&“Éd2™L&“Éd2™L&“Éd2™L'k¥¿×UÝTU½ßÝœ ¾Ôßzö[è>GÍqðê\®²ÖM]]4õº¯¯êõNÿ¹.fß߬ªqôåÍjîÙæEà*ƒë«j}»¸Þ åòæbݼÜU³mqvÅ“'Óµïã%Âm¥QqýéâvŒŠËzç/÷­n}7° l`¿Q08´+Ñx1Í~Ýö5žß·ò`x¸¹Ü€!w` ÀDˆç€3û8ÜßÿØÐ~ö|ÑR¢6ºZÈGIý™”Â/BðC—îÛè‰É“'ãÀFÊ3|àw‰à;µ´ßI‹‚}è={.Øav_ ï³+PšP[Ø»= »÷›þlpŠjqRp›vÁmšƒeð3Áâ?þÑbnß±X¦-ƒ»Ä'€£8Vü‡$WêcÈ>Ö“¢úX'žËìfW O|ò‘ZžŸ4*|”`p[˜Çq#QÁ‘£ /ix ëÌ æÿÊÇT|Ë0ã¸Ý‰Û°O¹­ †Á;¡QÔL˜ iø£¬ŒžS¿—%ÈËlÚ†˜s·ðPE0út/ Kî*YÌ™s¤.ØVW0§~Í4›= Õ¤ÆCYœ%ø }hyuSîôÝCÉ’sœ“äp“Çd+o«`ÊI5_Kv 9?õîw éÌÙSÁ)í‚“x u»™ƒSŒs‹ùr“ÅnÏbÝ>ÔfI›n²¸àcyqà}hôq`‹ÙÅ£s6°´ð?êcÊëS¹¥ ûK&„!´aŠ Ùm¢FE/q{ÝDHœ"d»‹ƒdT~Tz8¾¤q?L©Ù§f7<¼Üë€!¾ƒ´™g…\)½Mð<¬–”ïF”OÊ6, –ÈŸ”úḠxü¼“l9ôï,ߨi}¡p#M€A¢ñ5âø_Ê6° là·þ²þü:à¯ü·<˜|7öÓ±›ãÁöÁ>8÷-(vÀ¸SDùÞ§Òiì`,ÕÒÎÝÅ»V4¹D–²b L.W]Z7¶\›s]v¬B)|°d‹»@Zé'—í]ÇõÞtŽÇU»¯û‡C`-:Ýx4Á%¥¦ $‰+p<Ô–¨àG$9ÁÑj}ŒŠYúœRZÒ´uÏ‚¹bFX2»ÍÀm1° là¿6™L&“Éd2™–Ó„[Ì•Ï"IEND®B`‚urwid-1.3.1/docs/examples/subproc1.png0000664000175000017500000000275512615524560017241 0ustar ianian00000000000000‰PNG  IHDR_ KbKGDÿ‡Ì¿ pHYs  ÒÝ~ü‘IDATxÚíÚQvœ8ÐÚÿ>µ9'žnU%À&v&sßGÒ¡…$.B€:1ä¹Ä+Û(Oú&ÕGñÇùu>ÿÖqzÝ÷¹Aê%•9Ý‹ïú¾?~Ì×Áqæxmy—ÙöJ_×Îs…s§¶½Æ¡­}ÇþĦÂE[—*Üêa¯§Œ‹¾¯OÑœÙ8–™ FÚTžzÇÎõ¼QÆè>ä w݈ӶŠ2Eó5so¯Žß ¾£÷íÿ\ú®¬`íú–:§m]ò‘?9?¼‡@ºZ^[~“ïú«­?ý婇» k¾Ô/úîb<ë{atÜòyr»åÛÜ\¢ß˜Çï¸ë;ݾèÅü›*Hã7&гñ{Ù7ZßÓù·4§¾ë »ù7.οãuo?{~˜û±{"(î³Í–|w®ÆoúêÞóÃþë¶Š2‹ §Ç† Ïù4ÞLÜ*ÏVúeéŸò½õ‚ò¿ö««gQçÅ‹;KÿE_ëg?4¢…/_áË—¯ðåËWøòå+|ù _¾|…/_¾Â—/_áËWøòå+|ùò¾|ù _¾Â—/_áË—¯ðå+|ùò¾|ù _¾|…/_áË—¯ðåËWøòå+|ù _¾|…/_¾Â—/_áËWøòå+|ùò¾|ù _¾Â—/_áË—¯ðåËWøò¾|ù _¾|…/_¾Â—¯ðåËWøòå+|ùò¾|…/_¾Â—/_áË—¯ðå+|ùò¾|ù _¾|…/_áË—¯ðåËWøòå+|ù _¾|…/_¾Â—/_áËWøòå+|ùò¾|…/_¾Â—/_áË—¯ðå+|ùò¾|ù _¾|…/_áË—¯ðåËWøòå+|ù _¾|…/ß¿Ù7>òl­s}»&ö׿Â[ÓW[—÷}¯ËܯpŒº‰­­]5Q~Õ럽yLÍí¹]ÆûŽ·ˆúlEµ×ˆi¯˜úÕÉ. "Oͯnì´M\è|êj𮸖óÃzüvWÆè·ÄêbÌç ŽO w}ë뿺ÒjÐÍ=çÄ÷æüpvŸ]ÝÓ¯øã5Æ÷ùƸèy”•.·æß ÷Ùâ4JÍq=$¿_<´ä¾œù6œç‡1?l§§…~~X=?,^"¦oÎõ÷]MÝhßSæ»l÷v“Ë|©ó£¿”å©ðåËWøþ”ïûÞ7?¢O7Ê+ò¢_^\=$Lg;êç…Áq¬gq8ù X8ÍK—Œß|íAwšWü$;ª‡Ü©Ñˆòßya°YLWf,ÖÏ»§|óÀ‰‡y«µ¨ôÐ>·©7ÍÂàX/'¾ö»þvóÜìÙ.„\`m_GGõž7-AU¾Ç÷»8[&Žª ÕrW¤ó~õXú~aš˜6i_„KÞãšaä…Á49î7ʦ•Ʀñb}27Q.ùÎer監xjþùõ¬pØ+ªù¦ZÔÞ¯ÎìøÎòþÖ# 4QME绣xÆw÷ÂqÎ[,y\XåjV§ªûþië—–ÄŠ…ˆ¶ÛO¯´ëag#èißz˜>á;Ò5ù­¾‹Õ¿ÕšöZþª4Ú_Û⼕Åätú»YùãØ¢«¥Æ×æßz]ñt@”k†í–j­oL_¥VVï2Qþ/…ÜÄÉ(ºZiþ¼þLªÈ¤Û—°|s¾ÍÀN¥ðäšr¡vþoþá"éÒ'›ÚŽdž¦ø:ß‚ù¯ë|wæ{ da™?\“È,÷ëqÕ‘¥ÚÇ_ñÒK]“{s—àëOxþ=ͧ–C?Þ‰¹òxK$£¾ ܤäñ Ò¥c=òeäæ^§ËÔ•a¹BN?êûÄ#¹ÖErÊóx¹H;äûÂG"ÌBVTUdŽÇœ¾ìžæ;WóW÷—Rg+ÉÐÀ…±È礪háK¿…UýgO—xn“ùSßgВŲgdXDbÒ2œ¤eÊTS”é ß)-ÃI‚sž²/¹þ¸À d.UÇ^bŠ4÷K[My,¹.§¸ï™¤T:.;]¸~^SoÃyl—8M}§.—ÎÀ±Þ—Ž4ÒH/H±÷_ÆÞYGÐcÁ4MEÓÈÜÔ«‹-}_ÙµM±²2sCcåG)—êÒÜá62ûÚàÅe˜¼ƒ×º“ŽC‘ÌÃÂàvæR¹Úܸ/ à|Å6ð´)³æ‘hâqgK gѺÄÒé+ÊaÒ*´Œt?ë™+劇ï+ß8?©²,nY$j4_ñZËpqgú™kàÜz—B}í´pñBñSåŸòs:].MàiSæB¼NIÝÙ<©Žh’/›Tª¿…›Àæ•«¹Ð/YuÜ¿ëé%\‘0=•V'=àF·f´A‘áŸT¡ë§ßŠDªy’·«ÉŠ13˜±`ùŽS)̃{‰H”Ÿ÷iR¯·Òò^7åÀ\ëd”Ûÿ&]w_~(Öà³ÒÿðÀxà<€ðÀsºýþþ¶²!-¿hWÝx`èµðZ¤DÛi¨Ÿåð—ùxà‡\Àíñ X>hþ óõ=>||óÿ´|ˆ—Ÿ€>æ#hŽP˜“Oó$ÉCR3ÄzZÀËÏ=}àøƒ„ó¯ø™Oål3³¡EðÕ‘GÙ¹8JñÔÒñÊÏüݹLõto7%„йV"@0´€AƒL:ÀIH­ÛËmL’Ebùñ”æ# ³”: xÁl·0„|³P?†”^µ¹ja°ÀPˆÄ À÷,ÊKm¤e˜õížå¬¦p’(ìÉðv`î%Ÿr ÌÏòÒKäßöùÈüx‹Ho£‹Û^‚9“x‘êZÀ›¯SƯãéÛ!ú,àÿ<}5pžs_ÂY³XD Ɉ×ùÚ߇:ruivÐ6¯®¦—…óP?¼Z7t³/I0~0„û€qC‰W¿sú|àÃ:dïÁÍ]ÆöjÃÎÆ==Fƒ’êSØÌ¬Žà‘ø`«C²"4 P-931¤öÌlòRL.žì¢ylâA¬Ìã/±ÒG €­}¯0Z¢¶P¡Ê“õ ¶g¢é»)k}ÒÂÈ™±ÓÂZÃq€Q€Õ-¥°2Z 0´€Á†ýÀ!„8Ý4œ^'sæüʹ8™¸ÃSÀ¬Ü`1?`ÓD¨ägpÌf:Ñ6:¤<¯”/M‹& VÀ!-AÉp6~j{&“–aÈfqs£;ÝšÑ!ùɰm/!FËEóIJ—`ã§µgrfÓK¤NJÙ3+-¥94Óß)`+ý:})ðjŒ½´]$Þ5}<ð1¹]nì•ík4á0^Wtñù*>yV:ãXƒõ„uÒ*ãí\:|ž@ç¹ìÅŒiG"«¦±^b”&*ìQiN&¬)k•RNô”¹lÌŠ„Ð¥)´ è ‘ÐÀ„¢ôQ¡B4ÁÆ NίX ›˜onØlg4Ìtç - ½ÖÍ›“RÚháâ!; àYáL•…B†p°·ØN³H:2¼Ûz ù6©El¢DÛKh‘Pº¨™Ë¶Êdš÷{ =]¾¸Ÿh[:£·ÀMž5¥”þø=Ò‡£tê.÷k¼Õc¾ت:n´pºÀ¶K¬{urÿÂz®ö 4}70wïyØÊB¹®R­¥!RÖ OŒ;¬p.*ha%eáÜ,J`^D'¦'»Ò’Ìj%UŠLkˆ +ðâ´ùÕvQ±pnƬ­5€¡ÌmÚ&ìØE©ªy °^WYË Ps)ml‡XóIÀ`ôL2]G$À“ná¼b“[Î °]jKm`J‘¬¶é§Ì•…óP/¡ÖcÛ•–l¢L¥H­°E½3èé]QA »(‰…sk?ü¢tæÈì¦Wlõò €Ÿ3lÓßÃ+€ñ_®m•izºœ§F£›'†rLáSj¡¦ò ²A€­³ÝÉÎSŸÊ¥‚6ZC ŸâšÉx…ÒN¸¸´U":Ó¾%•è‡Æ)]³R°É›îÜla®‹2/ÃVÆOnÆLÍüzRzF¿…Q¹ª0Q¢y°ø”]÷Èo´³÷+'Ç­Rd¸\&áªZºižâ|Šk¦lFf`Ê Œwvk•­R¯xÁr匉zJ½”š­¾*"±¸ê?7¯W<{8ûG;Ïò;¿kúhàÙôÞÏ~sÝ{gµªS·­X´xqTìß×®xëž2Åñ`ø@àÙa|‘øá¶x‰&‰ù…ÛíqŠÜçŸÅ­Xg¾)¸E×ÒùO=N9íe޶ftc#ß–70Nä÷E`oräžRæßåO œ~~¥8·0]·¦ôÌàÎP7D"ß}`0À7a¾ß¤Ô=ß5s.î;³Ã(s©kÀóÝcàèK®ùÈYÀ¼öRÜÌwK3þþêÇð^ö$gYsøV`–na |/€I§R·[^>(¾kàZ\`j/xÎÇ}y¨ÓÕ'Ì|zðóW¼¥nñž° ¥âó).ÓJ/Á«Á;ËqÝÎsóØô_ ÍŸ ÜA| ð¦o\{¹×ÙU©×w—2nô¡çfè«k/Î>µ‰2þ¡lžRo¦±gZ7¢í-\¯½ô}\%¨ÙmãS,>ڠ㟨iÄb2w—Hk/}gŸÚ¥E¢£©40X`8 lÖ^6œ}ì’]¸­ âåëavðz Xôœ}p¸ÎcÖò¬;naoí¥ïìƒMàÂHË2lãÀHjº•:—=#]¿$8”ª§ï¤‘îÛ€;ûäó³äC½üCàáƒ:šçvñA§mka:ç°×¶_%~9ðÆðAknæV½lM»ë&¹B¬ÜˆºÀ[í¹™;!kªiwÊsŽ6Örý›æˆm ´æf^¨—î´;¹ª¬tðJø 57óB½t§Ýef¸rkC?|К›y¡^ºÓî¥[M L{D¢6áƒÖÜÌ­ ûÓî:ÒQ®Ð‰¨¹ ¼%|К›¹yQmO»s/!‚{Ù1·O´§&xFgûGwižïü‡éÃêßùóüÏ]O|ë´ÑžÙÊ`ÄGõZØ!XM¬Î;CCI£ À­s ­ïË€o2Í}/&¾S]­28ÆO7Rº˜5ì¢,ñú +‰² ç)ÂY€ï ³äÌÐÓ*ɨ¨œ BÃ)˜ XöFn€#¿…!ë9Y ¼¢UàäÊA]Çö2úŽZÀs3ï¶Z%Y“Z|‡µ¡Š:ÀÆë§(Õ¾»- ¿|E«ôZ¸ï‰_´p+ÀQøVÈð=Ï’G†­Ò6ùµ ’a¬€ƒõú±ŽÚ"q¿«^B&¾ï ÔÓ*a³ÙK”aÕ‰×g¯Ÿµ^¢““δWI;whþ&àÆ,ùn­òï€ß'}p¹·þÔοn‡<˜*• å­ª[Ftqǻؘæ. i §¢”Ø!er<;Œ¿Ä°1ÍMõæ#dM¹E$ñEë0þ¢nLÂÖÀì‰ ’qE! L,slÆ4-lý€´ØþGÀd3d",KÕÀì0þWÀÔfƒj°¶tÝÂ& ܛ掂ôZ?DÑåûØ%Ö^|vzÉŽ9X§Wlñ3ÔË<€ðÀxà<€?øÕ‹0Û†Ífxv\iaú“5T×ÓzüJà×,Âl6ýðìvåUø%‹0û†M/<;›ÒŠHÐé‹0û†M/<;!z6Ÿ¹³gØôó›õ™Û€áÄE˜Ãf#<;•÷h“HÀÇa¶ ›Ð ÏNrŠ6¿`¦kØh…gW[üô€'Ú81·f'l×ó¯ož._1l¶ëêåLkî˾dî–!çõj{)0®ä îwô½D¿ X¶GÜÒlø(1-˨wAo™ŽáÙíü{¬‡”zYmÙFÙš'®‡TÁ‚Ô†ì¶ãìµgóçV£öü;’ ÒnC¯§z°LÚªU”„q‘ª@ˆ° ØTˆ:ª|åü¾,½[æøê(Nå.°7mjó·/yã{ ­SÑÂг Ó†gï¶pñœÉî?d;ŽHÌ"0.6|ÔÀµ g‡qé[»óïhØlèõä]ÞÞ—žä¶î%8zÓ@·$µ‰yž½¤Xy¡×Óëí¶¡Û}ïÆD»³7Jü 0~0ìö9êånËØí€Þ¾…ËÇgŸ“ˆ#Ÿ|0:úÉûã'{æg´ð=tQqÕÀ¯1ž•Dqsè[·ÓÀxà<€ðÀxàmÀE˜NÚ$µ¹9=×Âô¼¯:í:üOW‹0±šÔ6 ,eÞÊ„"ìnGî9¼uwÔr*í8lB !Ù M ÜŽÜ CtP$¨ DI®S9'ÄP¾HSsS¡§€Íþ)µS9˜î:ξÇvÃ= Þ¾KºU¡4ß™CXÕYlG¾íß'=àJ†‹C ØßŽœO…€«­¸ZN妗°!†D$¼íÈU/qVXõ—¤ÝÎÚxoÚë]>ÔË<€ðÀxà<€? ;Ûî‰ûÃÆTç\sïšz°¬(¶0õ/iŽíßz7À´Ê[Ïnúd`6#»Ÿ/±­·Î’±a!û˜T vol᪛hê&‡Ñc`Šß€J¿ bå†LÚ²;s¬ÚÝSRb§;Îà¤K‘ÝQoèã£ÞB‰P1WÀЦŒç/yÎàTlYXìk :ئnõ6pÈ'åœÀ²§¤ \8ƒ“^|Kv›Hìµ0tZX6sÔÕ²µlaXiap€«hêëÀ”EyùcÙì[d˜ko¯èJ†aE†ÁÊ0)&½Md[†WYýh6stvŸ;f/aª{½„Ù‰ÒDSoöëiÝd·nÑ#Øk÷k ÍgÓ»¯;zÓ›¿Oú2õ²% •„‡µgñ 2éwžôÊozoÝ<Ô®‡ÚþtŸ L9Ö™ 5ÌVhM…£A3®¢ˆT&köE†Ab é=J”W¯Ñ<ËiÄ ÃæäxÕÚ—ü¸2™AåÀžE"G±QÞØèEÎ~â…Žõ%B™¬Ø30ªÿÕÍOó¬÷×jR8A™¬ØV ÈíZµ08¯ZuÔ[@>¡LÖì UØ,Õ¯bž7šçŠ cŠ¡eBT&kö4pPŽÄ>àüT{NÜ¡ê%Êç‹S•ÉÚý£†æn¸†ü/ÃgÛן¦±Q%*>¯êݽ iLP¼å-f ¸Ê¾8îKÄ-LŸŒ`öYƒB ,b a{¤£C.8 5“”ÃQ=¸`ißi è,„ާv¨£údSY*ÙÑ!¥…©¬G©²r-’æGòµ‹Œ(0¾)Ì“ªÈÕpÀ‰>DY$J{£×VP<ïR½(opÒýøæ*àöîá à ÞctèÌ ˜µSµû(Y`6óÖ-¬äó^*Ù×!ÁÛý‡EÂdV:8ûF–"‘¯È»~`ª€M„¢Ž™ÿpD‹UÙªq@k§`d˜0F°¿²\r¹ÚKlØýG©²ZáŒGPN‰HàJø²¿N[×4àÃiã"Ì¡^à<€ðÀxà3ÿyˆ RËq IEND®B`‚urwid-1.3.1/docs/examples/subproc.py0000777000175000017500000000000012615524560023301 2../../examples/subproc.pyustar ianian00000000000000urwid-1.3.1/docs/examples/browse.py0000775000175000017500000000015112615524560016637 0ustar ianian00000000000000#!/usr/bin/env python import real_browse import os os.chdir('/usr/share/doc/python') real_browse.main() urwid-1.3.1/docs/examples/tour2.png0000664000175000017500000000766612615524560016564 0ustar ianian00000000000000‰PNG  IHDR_wðdUéPLTEÍÿÿÿåååMMMÍÍÍ8BbKGDÿ-Þ pHYs  ÒÝ~ü:IDATxÚíœY²Û¬€í˜‡¼ß"è_@²‚P„÷¸ÊH©¼ÿ%\15 b’-O'MœcYbøÜ4M«A>(=81û²G§M¥ªéxŠ{eMëGÆXV––åìòñxb¨ÃÀõ&R>“o¸Pé øs±PÈÀšøE|Ó;'µÝ ìëØøhºÀ|:šÞ1}á e1G¯2¶¿–~DglµË'×´­‹;×–hOm`óÎÌ%{ÊỞ0•».‰qOX8†²|ß´«'~™Ø¹Gv`MŽ-`ÿBßÇ9Ķ1g‡C”Cè8ã`,YVU–Ù+Hø°˜™ž[ÃW/ÛR{#ÍÀÀ„aõ“!Ae>´%Œêߘr`#3ÄÐ÷aN³@÷Ž*Á°{maŠ9>@Oaöòä¶äx*{S*vV‚¹ VÂÊŠ…¹äh/ía±”¿ì›e¨Æ«·6'5‰ŒpO‰£oK)Q¢ô€ä¬¿{€çÆXfq*™«Þàª1kûrÓÆ\eyæŠ3Îr«¦ÁàV2—½ÁC‘•N×FÚMEe`˜F€ë™sçjp¥_*À¡Å:0ÊœÃLÄ`Þ‘pP­ƒ+íÿ¤:Œv¡ãLwêg^9W0}¡WÊÀaøÊ‚º•X{¤¡ÅãZ‡³žig^é2÷µ!áì†â´ÊÏÂ8e‡C˜ eμÁ#óîÎ0C†ˆÅ/ë]ªSYÂUàä–«ž9óÁuÜì¾ë)±E•H,v'KÀ³–xƒQ‡O¾ÂŒGª¾fï®XªÆÀÌö;²\#*™+X GÆ;cèö6Š#Þ×±pje‰sûštÜÜ<9ÃOKü³Òÿ˜€ ˜€ ˜€ ˜€ ˜€ÿmàï?–÷ŸËÿÆÁÏåèûŸ­ƒï?—œ?|ÿÑ8©§Ã³ðòÞ}½pO¼ßòç?3uÉJ0000000000000ð`éß/—³;˜Üûyº\.|ºk2)ÛL⎫`áKLö…Òy çëIŽ4<,†¥ouö¦À"ä¼,º±¨Ät9#`£-ËÉs€”\-…’ËË2ÿ½P‹ºØ3áûˆ%£=ã‹/T8cŠ»R8Ï8ðÀÎËá¢Ã *Sì^B ßED$¸´‡2›‹2|2l£ÒåQQ²:(ÊÀ‹t'ÀH%ð€bæ`(.¶+ß–Ê1nV¶?1°éáX••P.¸]·sóu%5"dXIép8³'ð”/ɵ¸$¬Ã,0°¹$Ræ+æX‡ÅF`k &8VÂŽf¦ƒñ“ÀâÓ€÷ô³ÔpJ·¿kúbÀiw©h«®ŠqE露Q²X­l¯ËŠ<“(]ÊÅ—ÆÜ"‰à^‚ññΤˆ.ß:z \Ǥ8„:cfÁÏ’É} pùqaGNE—O®ºù U×{Œ8·ÔlòDg²'aù\K‘z¿i”¬\u±?“‹B³ lú Øm¬TbÀuÄÅïR’éó–JT]GÜA{ƒî ,DªÃÝõ`)¸Ž¨¸(ÔÉÕ XŒY ?`½ïÚ@1Æàòy·0±F}×7ßne%¢h;V¢a{oK›=±‘DÀ8muo~ïôÙÀJV’¼h{!ŠÝnò%–Ab`Ó ªO²8ÞYí•«+²~¯Únç‘Åe#ñ}'#—/„19/y•Òw®* cº(–¹± ŽÝÔ˜ãHik•<NB  róãiéúl¼n± üCÙVÅÛ{€U˜ä“©ßw¥ê‡Õö °â~E>}´çF`+í—ÔÉo‹1 ge7'+à’Ç­¶Ù‚µ„§ÉäZ°”Ö»ÓmŒ¹{àí:œ®€§û*Þ‡/ƒï‡b•xM<Ù</ÝÆ¿“•XMC[ìä݋ʣì,ßXlÙn)î_¿ø]Ó–7u%<|ÞHæ‰Âì‰ovXin>Š…¨ä O›ò4ïù%À¡Ù&0/OcÀ"y<6F‚3)ñ’ìÏŒ?qý“£gî~†À>[ºLXS,Þä/Á3é¶”×–©/a¹Ú‚ñLŸ}€<"ñdèÿì9ÊÑœ1ƒ½rö¢ûgÒ/ŽÞ–;—t£,Í`ÑSð 8“p ÌWÀ<//^‡m¹iâ€Q°ÑÇ3Ò°rJ” ¨1àEmð…'ÿŸÓ=“2„“û¼5piÜ £ÏKx–0Š^ÊÕ¼”t˜ç›'W*áŽ#pXeBª œ¤]æjµr㙉J¶J‚S Çåb0L?_ðÀÀp&µæÃ˜•@ª±ÿÄ “'é>ø!{&û“ãÀ °Ã ËäÌ:X€^ˆ‚>–Äà÷]Ç‚ºÊ÷]ǧ#gÿXŠ³ê“Ÿ.çžë8ÝKÂ"8“*Ñá³Sà |•¦ë8Ý Xb÷ ü<ÎyŠ´ödÓuŠ‚î¤*žœ˜sà¦'6ÝX$ÌÏæ&àJt'`•þÎUP€L‡[®#ˆ‚ ¹›J™ŒVâ|öVÂ;-×Ñ“5£ »?$ÉŸá&à<‰:'÷’€ ˜€ ˜€ ˜€ ˜€ |½9ý}H=_XÛr³}&]Þ¥ž`_ý¶†æ5ð>õ kø²úºE4ËõÌz ðªž>0Ô>Ù(u½*¥GDó·\ÏRÍìêñV`õɼûTÿ-×3›ôu¸»ôÀ×»€çÇÏXu¸S%–Z6Ï·:ÔÊ€ëƒÎjÅ0°¾Ù¬ðwÌÚ0ð|ûÄa߇­Dgâ¶úŸ˜š ø¡Àç0000ð;“ÿ`ó˜Ü{önCë7¡µ{ØpW:«{oB‹QÇðpôR—ŠÞw›¯onR®ãÀwD/!ž0_—>Õ!@¡+‘ŠV¨êëQ¡¸Ñ„y6õ8ÉèJ=}`(iñLM¶­Rµxf=x½Æz’°¼ §À·G/cC:Æšæz<³¬WÀ×=€“®T^Ì^DJ…ƒ*õØ?V/*Àó=ƒîÛÐA@Ô›Ææ–„õ=fm ¬j:Ü^Be®ž xÖ·›µ¨f>z‰Tb®Y‰ÎÄõXë`êqv#6•Ö·OÝ)í!S³~”/ñàùÓ€¯8Þ½øãüa&`&`&`&`~gàO ~°Ž‘ŸìN&œ°&5v:«Âö™¹qC³x$ê¸ MÔoó‹wUsãîn3°¾¸HÙ¼[ôÒEµT íaºªò1(P\Bç®îŒ^^Ó¨£Ž½ ^¾%rŽØf~׳ßB›tÁ}ÑËkuÜ” €¯wçhEò¬àoQ k.p„Jï£yÔ1Ö ·—ð5tCç£òîèå ÀuÆÀÕ¨c¾‡sŽðû]g=ûÚõ}f-^j£ŽÚ‰ã½u¼f{8“RÖJ˜3:W ýð©Yß75ëçúó}ÀóÓQÇÍÅoÞ+=Í[#ภ2²÷ÿ €g<Ëð£€upJLz `ÏÖLÀU`õîÀ׸³Åº—o/á‚‹öIÀ Ã™|ûÅ‹€’˜€ ˜€ ˜€ ˜€ øÓ€/!©%ý¾lHçÿöÿßøÏ7Ûþ¯Ë…9`ö­ kʼ ˜]2à?}`SèUÀoð’çUÀ^ÀøQ‡æÏ·ž^°Wƒ81Æ~3Ão€|‘ªˆ_¼Hí×7'^ÿzKàD%Œ”Ù[ º_ßü€ê¿pÐ%fÚž³7™8þ°yQ‹ž•xéÄQššGL1oN¼µ7~ëDÀLÀLÀLÀLÀŸ¼i×aRò¿ö+n† ¿›‡÷í1Ø œïiüœo¬Ñ>Žð¦Ë[Qøãáù‹íÀa¯þŒUû±ÀÚ?Ѱz¬¥ <ã_”ž<Ï×ÛÓz8p|¢g3pòDÏÓ€õÍÆJ= x¶?•?ߢÃK‚®°{@Mi½ÝJÌù±*ñiSóÇ9?o ü׸ >ÑóÅPIEND®B`‚urwid-1.3.1/docs/examples/bigtext1.png0000664000175000017500000000610412615524560017222 0ustar ianian00000000000000‰PNG  IHDR_;ˆþŸþPLTEÍÿÿÿåååÍõÅó€bKGDÿ-Þ pHYs  ÒÝ~ü ÎIDATxÚíœ –«ª…“Äu'ðÅÞ ÜÅsþczUP(Fð'1÷–§»M¢~)·Å¶Ð~_Ï™†ã‰/½}êñÿ=ýþý¶ò›ÃvÂ{^Oüç›®3<_ÃBÃm 0Æ<Ó0k<ðŒøê¸ýÛ¡]„÷àæÂ§MÀ³Æ¯gH›e¹#÷ø,2f 5`Ÿv*nÑðð3kãñXD8ö µ?–À^Ã'ž ýÀ(B«¤a¦„¾ôHîŽ=^s ÓŠ´ Ôðƒ€×"üV:È‘<üöb']ÌãÔ!ì<$­{žqïþkH«¾R–@Ú`”ÄZ„ßžt;FÃÞ <”GôÍá¾hø¾?ð³ÇFl?ò˜#ËÝ—ë¥uðOBß}ÉþðÓAWNøFLÉ1’$†öûNþß[ÙLà÷½%‘ÍËwrvŒ˜ÚÛj‚üað¥Š;ËbU*²üõ[ËX€X€X€X€øÖÀö·–ÿ ° °·‹å¯Å -ïùðr×ûzX€X€ol?üG€X€X€X€X€X€?<üðŸ¿øZàáÇ4œüMà–Êä*ðre|ãÚ o^úÃßUölÔ%Öc>´®õg½gÏK{s€øàÁþ°```àMª ç™¶Ì^ºY„W¶Ìv!À,ÀüSÀX€X€Ï^ózöÍ{6ï½ÜS½œïk ø–_þÓ Ý–÷| ¸®Õ¹r%Âÿ" ° ° ° ° °ÿ‹ÇãËÿü–ÆÏ,ŸŒßŸvãèX‡ ?©GS¯Â–SéîÀŒÓéWGŸ@ßXÏù›¿ ¬§Cá+Ex)œØÊÀþ=Nŵ¾<{íˆÇ‘’ÇÅñç¡VþUý-à°ë‚¹æÚPÓ)è0þ^Óú;Àã;`7ÖÄìÉo ¬«À¡×¹ñ[ÀÓRÓð¤o—’œÎgŸúpåXâç,¡üoNçF8(KøÕ> ||àË?êÖäO? ° ° ° ° °ÿ°µF™³·kýV¯¥` voל´:°úA`kUÔ`¦H …>à°A¾©ë$`w 6Œï¸k£,õ´k8¬ qËi;§›¬2°Ê;2ë:_0Ì?ø¹6þÌŽjól)‹tK¢îZ½ 0 æNá1€O rNkذ¥A6G€á< ÓÀá%$  “Ïî]’È[Vgf õ[‹ÿ(°þµr«^Îçœ\$+Ì a )ì.àÅ ¯Š³{cžñÉÓ(¼± POJd²àà±:©=V'¯ßWìû ÀY zX=âù;tè*8¸Jð⼓; ì|ì6RwT|S€ `LS„  E¤¯ãÀšfZ³f‚|\l´ã˜<Vð)’Ẋë Ct•]eöW ™ÖvzÃN`×sÒE$cx$ 3œýá½: Ør`(4¬:5¼–ÖfÀ85ïVsÈpLè*cº0&d‰Ôs°Ãoh<Ñ1K¨Ö“î‡f1?ןuç‡ÍŽº€/Óp2 BÇä+ÒgÞ¨-°Ï^¦OÔn/Kàí‡6ƒŸ€O=fåìµ—NwÚË%p * ªúY7ÛKÝk/K ‡ë( `IÀ¡ U†6ॽt£î´—%°‰aÄeçÀ+6¶ xi/ýg]ö²œÂ¨¨Ÿ€ƒZT´o67šk^BwšŸEr²1ÄQÃ0‹p> gŠØ ’C—½œÛ@a 9ÂA?…c3Õ+Ž:pP·‹C°'>¦ ªãH¸Ž{ VÏÿgŒ‚lƒ!€0d·Ms‡›ºü`ˆv$—Æl½2l¤PÎ@¨¦ah2ð( d£ó)E8=Éâ˜`cÙÍ+Àx”CEŠÈùWAYJ¢Á£$ÔBn¬ =»Ùõ)[¸Þ÷Àh!ù¢`·”ÄxájZcH€‰Œ€ÓïéªÉÀ,­Ù·†Àj夫h˜ ›s`BQ1•FˆAº­&óR‰p=§‚Š“ÎÅç‰ôZas|‚ñ¹½½l¾“ù1ÿH·&ö2Ç).´†2E‘7¢A¢‰1Ìx6§¾Kìåèò{–yšSª>‚`J6³—.±—‹'h {É LUA£à 64ó†*SVn(LfV°T/«Ó4T[ãTé«öR¾ðG`c:;ª—»ç£ÆÊ¬½Â^¶ÛÌœŠ–‹°¹D”'s±y…½tyLÁR£†QŸ±Ô>æbà…¬ÙõþùöÒ;¥äñpv`±´³lì^à~{™þö3%°)ô€JÀÆ05¤êT?p·½ ÛRA)%00·V. †Yè½ÀýöRsŸÉM-KDàp4»¬Û>É^"°B`GÀ³âXÖ  j¬](Â4ýÀ]ö’g ìTZ§¡ ÍþcÀg†æ lzˆ¿d~þ ·6éãí¥ù5`:ß8xšiö«B­{¥˜·cŸÇ›U`üʃմ²ìÆSÒÓðÅÀúTà0p•f»p‰“ÏøJ! …á‰rZ«8^m÷ÝnF8WzS¹ 3c ”¥*• ÞZ}x ì9½ÇP‡‚Ìçµ5*Àšy}X1GºVªZžsáFwTY3àx猭+»X³«¤ãÀP­^’†O‰°>5ÂÀg+HÃÅܪ†»€uº½ù°$R*Xf æ<³ÉOW‚+ÕËeVŠ9Æ3òðÊ<åšuz›RÕÏ—÷×Ín¶»#ð ÝÚ8šßëS¬Ã9ÀÅÃ>ª^ªZÜ:¾òhO~ ãÀÚ¦8ÝU¸¼RÜÓcß‘8/_+…]Ü`ôîï··>'§nã}·þ‰VÙ~¨¡Çð<°‹:J Ø·pUíÉ3àíÀøÄXE•ŸÂó@®»RdUŠ0¼f?Æ•G{ò x³†9•á‚nj¥b ¤òå~àå“24^LÙ7Ûl!J`ù#’¨?×\©Ã7[.‰²Ü Åm»‹GQOº*°Ö=ÀIÃ`S$  «yiø ðZZ;þäxÊY–zŠê¥Q†ßš²Ø|rÜïÛù÷ëCs30¼P>f~š‚[«+–_¾LßøhÏìÞË7ß{Yõr—ë¹ãÕ½“ãu³üaàžÉñq«7í XõNާq»ˆúÇ%ÑóhçÔú«ÀªëўĪ¿Üùh¦øøäxÎ&î{G×½—ŽÖú°üa"þÐ"YT€X€X€X€øÖÀÿnæFuMj"IEND®B`‚urwid-1.3.1/docs/examples/palette_test1.png0000664000175000017500000000654412615524560020261 0ustar ianian00000000000000‰PNG  IHDRÇþ‚E¾å0PLTEåååÍÍîÍÍÍÍÍÍÿÿÿÿÿ\\ÿÿÿÿÿÿÿ•"Ž·bKGDºÙ pHYs  ÒÝ~ü ÍIDATxÚí=râL@䟵½ö ø8ª¸À{'Ή;#ÝÐá¦dމœ“ø{ª|joàoºgF! ö‚`ßCÈ£Q¦ÕcÛ€£ ¥KF7tʺÛíøÞNíûÝ.7”lbÉùP'(ïÔn’w-¹ÓêºFKÞûÝn$¹¥é £‰Á%MY& C&‚jÉ­ŽõB·•efo²“/!’‹A.‹ë!’WHVKϦ¤ñ]ɤ‹÷$›É.œ;’º‘ä<1„¡°l.9 ã¨+:WæjIïK¶,ådç°U¬&;eÉ!'£¹Š( tBPH— \™ÑÕ\QW]¸ê`/üÉ öBßòŸD`ÿp©•Üëÿ'¢í×Ñœ†ã?Ém/Ù6”^¿Òw»I’“hqÏØuŸsŠä5%o𜓰Þâ±ÛJîõƒd¯¶×KNÕMšÚJò`Ÿ’-ýž=%öz¦­¾±¶ôô}OÏôüÐÞ$’dI²d€t`— ŒIIªÄ*µ¯IªCI$Y†ô±ºOØyg’mܶţkȘ&“õ´]c’Ó( U«fñ,T6ÒD©“œ¸F.9-,ÙÎ;‘ª[ßè;¥í¦JÎÒlR<ˆ%üPd·ó ’û¹d#Y¤AéÂK–÷¼É$6‘¼ÃtÑï¯'yo§Âš_&ÙçÛÁ’ÿRº¨.á–%÷%¹º„+HΒɲä<''eÉéN%k ®º’‹C ’Î~¡ºHò E…3ª‹|ŸÒÎCòq^V§Í¹æNÝ‘¿Žä5žYã$ã,Üßxão+¹!r̤æÀ¦ —Žõ@¦:Ëgê/èÃ;›Ü€d©‘f:“5»Ó©Î ÉRI¦Ñ!%Í‹š’ä‚Ùu$'»œê,_=éõjR>àIN—[ÅZbMÉ»z^^òÒ<@IryJ°y’£Ë =þ&Mu–g´Ü[)¤¬dP5%Ø8ÉþpÜ›4jª³F²äjýÁYÅÁ (Ù“›«LLˆí†Lu–ÓEñ‰ä³­$$ÿó’O~|7ßO~óCƶ¥§†»»SszwgUßÝŠO§ôöôN†níÐm•äûû3svoýÝE²ó|?¼—¡¡µ1”}\Ï}Õw—¿ø–È'Ñ%;¤™^ý<§n&îj•iËÿXê»ÿ¦$’½gí‘ mÈ?H(3›óÑȪ=ˆdçù|ôð =2d÷±CÒ¨“üÝó‰x–¨VÉßkÃÛ†ñ Wñ|ë6l·~j´çô¶26ï] ‹ç¡ñ‡gj[Ög²Ï™ C$+Ãxà‚V<§nCûÒLrRˆö(ŒÛ=á¶xvÆÙî9ãmÓ£=ã‘‹añü`F!¤uà\µŸËn£q®nOL|âµ’Oä[Qz—wɦZò™ó& ‡!HË’‡ïJNœdÑ™òt±†dã%ÛFÛ;vv3ɽ:ÉçΪx}™HòC,ùa Éfmɧw.U„8vâmïÝ*ÉšÄ\–Tò½M C7|ŸKÖ«%'—*B/K u’û}M!ŽcÉ2´BòùÈ¥ 32EÉ’)FvxôÉ’o]$›B$‡¯•’M9’ÛïDòƒ‹d“G²ÍÉNêƒÉB}…äïÉ–Õ’ï\â½õ™yYrmN¶ö¼ä8'û!S|æv®ÎÉÖ¤—\—“MMN–€u’—r²ö®ˆd‰Òs/9ÏÉfÉRK¨Ô?œï’}uq$û¸=uCïTà ÙG©6Báaî³Â#ÔuÕE$ÇÕ…þ¥NZY]øH.Tîß9öë"ÙWArV]ø]^]l]àí„á/?J{‹Ç4\iÓ$÷þ É5—»Šã~o‹G¯¸ô8~óåqõ>ã_²rí‹_µ{-mOždåÚ—OÛáôYV®}õ\Ñ®ií#¿Îdõâ6f}ÓùoY¹öõïJþòSL¯Þç¢$y¼†äË’äÉêoSóJ\•$Ocɦ^²)Iïr]’<ß…d³•dóI’ÍÖ’ íÍ$›I~”勦РùñQÚºY%ùb,÷ñÅÅ/›<Æcot|a·änSJ•äˉÜ'——O6yL&ÞòÓÄŽÙn²+;t©öº*I–œp5^]=Ûä1z¯SMS¿\MŸ«$Ϭ×Ùìåef“Ç×Ùì%d’™ŒÎ\ãë¬Jòõ\îóëëß6yÌç!®]ݚ븺ž×K–[&ùËO¹ÕK–@[ŸVö8ZÝ’Ûů‹ºt!ú¬ÃIÉÖ±lËZìíI´?éƒ&5éB4Z«Ó(’Ÿe,Hž–cÚ§ »Ì^f/®F¾¾¨dm¼”"<¤‹¹ÕhÎóH–›²¯õfdH6ê$;¥A²ëy|Oò/Û¶Ë8Žqé×çd±é$'Ù ùÝLUR‰$‹T»Dé"_ÜP­d·!_ܨÛaV>-F’E¡êÌ$» Õ¡yM.Q¥Ÿ"Ù&-$Ûäá$»†—|™å”%Û,²…ä™Í [J¶ØÜ1_-ùg|â[G²Õi*#Ù˜’5ù–$kÊ^ '•eÉÖ¢YɵՅ¤âjÉú¬l-š¢äë¹—:/¼•5ÅÉÈQºÐœ¬±½*’M&yH€¯ÈÉ&“<‰%kŸœ}Àk–®‹d“Ižs² ñ©Fzm$› Ùkvͯ3—®WE²É$ÏMVeøî¹Æud{ "Õ…ÞÚÂâQ« w*,;û‚BN²A²x¹ºÐbaâJŸ..}åàÓ…F²+3Ük¡;?-I¾šú‚BNr¨#´ºÐZcú¬ãËŽg¾ ˜…Z¶òêBõÎfU‘lÃÕÒp’¯ç¿£!5=ÿ­q=¯¯ï7®ûªrÂLê äÉ»µsÄteI¼Š¸¬(‡úJÊçk—ÌÕµÚ¿%y¶ÆµÉG$K¦ØÍsY¾¾Xc¨ÌU¹zX—Ù¬*d¿ÎÞŸÏÐäðN4œ–®ºË=Û°¨lî‘oòõþ%Ä£ä=‹>~É ˆæV«Ûqb»¶Ñêv¥½çÅëÂÜ,fq³xµ’½æÅâõU{th‡ößìòíÏ›•kïßÞ´ñ¶7ÉÜjËXß–ômÌÍëXô_7¯¾×Êw=¶½sÉoò%¤‹oöÏ-³$ÙÆöŽC”ZɯÀ&“l2á;•lÃØJu‹ÞßtÙ³ä®MÛÛJ^ØìP#Y†ö,ùímï’[dk3D²^ÉÉ*Y‡öÉû"ËÉvé~0'» ¬’½MÝð’eèfw’-±dÍÈ{‹ä–/*lºh}´ºX,B$‡Dl³DHêyg’½V»Ê* m!7¯ÁvžUà¯J^,‡Ïa±ˆb÷fA Ôñ?Ãè6Dd´)‡IEND®B`‚urwid-1.3.1/docs/examples/subproc2.png0000664000175000017500000000412312615524560017231 0ustar ianian00000000000000‰PNG  IHDR_ KbKGDÿ‡Ì¿ pHYs  ÒÝ~ü÷IDATxÚík–¤ F³ÿ}fsÎô¨@ŠU=pó£ÆQ¼†Ÿh‹boš€àe¾òc¯Ü2ÙþöÉD'–ßÚ$dRøþf¾çæOÌøißeä8öœi®³Ì!=NÎ3Ì/å:K‹²Ò"ÊúÈH Mb§¬J†óù[ÜY)Ód Åìògns–*Ù5—‡ÎÄvÃf˜TCše9iÞôß¾ó«|CR*>Ö¨n¦òÒ,ëK|íÐâØóßú¡«>qsSäaIÔô—øoÝ;†øª–C|ƒ.IâÖõ›|ʼn¿6yÉW2-ÿíæ+!ßfüu¦ÉW^å«GßÞ?äõHFiãK[±ÝS´gõý×?$”˳<ïìm|˜K=ÌÍôW›8®,ÃÀ·ç"j­§’ÃH{i}ËñýFñ2]^‚¯Süf|ùS—>™M¶ã{Ͻô³S÷/|òugW#ÎT±¹úä|ójáƒÉ”RŠ›rl¨ÞÕ'÷ˆ¿Ù<½¼^ ^c¾ñ¯”yïÀ7‰F‘rD—Yú¤´ïãBþ«}þ;QŸÜo‹:UŸÜvü ™ä¨ÙøaŽ>¹‹ÿ~2£ŸÁ·+ø¾ç¿ôIô3 ¾ð…/_øbð…/|1øÂ¾|á _ ¾ðÅà _øbð…/|1øÂ¾|á‹Á¾ðÅà _øbð…/|1øÂƒ/|á‹Á¾ðÅà _ø‚¾ðÅà _øbð…/|1øÂƒ/|á‹Á¾ðÅà _øbð…/_øÂƒ/|á‹Á¾ðÅà _ ¾ð…/_øÂƒ/|á‹Á¾|á _ ¾ð…/_øÂƒ/|1øÂ¾|á»8_ù±¹¹æù%E¤ÿúyâ+ƒìÐUå´î~šñ Uý"®²’lÄM|ä,u–›7Í;3Ý+ǯÝÈ‹­E–X²Ûè§ÎPÔ-BÊ’¤^Ÿ¿Þû1¾NUc¾bÓhëX˜w3/±˜’*‰¯’7cÛT³=gû±A%i#Ѫ\˜ýw¯ØlJ8âÝû¼,ë¿âåUð Ý÷ˆ¿AcQ)ÜMOʤD“¨&i¸¼’QíLœÕÑ=ë:$YTtñ^¾‘FÒ«¬,FÛ"ÄÄ_[ySUŒSØÛk}*w· »”ó Ê.¿[⥒%Y}Ä»ÙΈØÐ|T#¡]ÑQySUzøºñ¡î¿QËÐxÔ£½RŽ$Ü~×ꩆêrè ÏiðŒ­~¶Ö§÷ðuüUôs|E;ùŠõ2—ËPüíègÛì •‚ëj Êã•A‹­K‹oPùs3š¶Íh!޵ñCe‘iõÓªšj„ó”¼—f76Í£ÊkÜ”±Y_øÂƒï·øž}_>DÏ:Êž +úYa°6HÈî¶øÿ·Â –ùT.ÇáÔJ—Sü×^@xÑÑF0õ°#Yõ¹Y¡"îÿ­0è¦eJE?«œ¦³øZÇ‘Éx=-Ê ÚóÒÅÔ&µ.'çõÏnæEÏPéÀNGÕ›çe”Ç·œßIK&¯ žÜ%æ~›§U¾ÂDþØ$œ»xKÍP¬0h‚cz¹â>8ȔƠpGŸ´E¸’ožÆV>¸ŠYñWòŸzT(Î/Þx¢vªæV>À©öo1DG /h8•®bßdÂ-ÒÆëH*W Nyý~³ô.IÌ"ÂjÏÖB=¬åA³ùún:ƒ¯š6ùQ¾õ¯Ö@ÍYÕ§J>m“v)•àÔ|næ>«TÕ¥ñ,þúºbÓ!\Í0Üãi}š2¥Ôæ2â®R°E4–F8UõhLèß°w ¾ð…/ß/òµ|’›å"‘Í1ï›|ÿùþ ýë'» ñ¡Á7ÇT_?醸z:XŸª>ßGø¶ãÃÐú>øð½±~¾£|{×O*ñ÷V|è^?iÇ/¼çµ_ ¾ðÅà ß%ùÎÖ'í³ËpcõQÇú¤]éTÙX¼½ O6ß•®¼·¶,ß³­¦÷ôÉa¾+÷/è“£|—žô½ OŽÇ‡ â¯ê4}¾>ßYú$|+|gè“#ã³ÕßÒ}EŸ˜_èâ¢&ócøÂƒ/|—å;§w^Ùî6l0~˜£ŸÕÞÄŒ‡½»Œ'ò½·±8_·Íé“Ev²ïN| cú¤û]κì+ÑZ†ïWôIõ>á¸E|x Ÿ…ïšÖß>Ý%><Õ'Í«â5¾³¿ò¿ð½¯O:_3iÏÖÇû-}ÒyS{]¾|á‹Á¾KòÒ…ß?°~òA~ÝË&á;˜ÝزIÖOvê“CúÙËS'¯Ÿ”ÿµß±\’ïD}R‡žVÈý0ôLJ'ëû†ø:ŸÚ\›ïóõ“ðmò}ªOvÏìW+·ˆ÷õɱõÎ7*—å‹Á¾|á»$ßy=xsü`>¡ÏúÉ‘ÜÚÃ^÷Í,ôÉÎÌúŽŠnÊWõ¹>)=úï±]þ¡œ øZwÒ'ãµÓÎ_š0(gY¾³ôI«/tÉ:¬ŸÔ>ýLòÓ‡d3ôÉ6_½ç¿›ñ}ô~wÏcc5ë'{ôÉ„[ß²Éâå,˃/|1øÂwI¾\?ýõÙ•ùÎ|ÿ8ͳ¢Ojõ5PøÖ’öë“éžÅùž-<Ýüþ¤ô¬ï³ïÐnÃ׺ۈ>™KŽ-}Òü.Ë÷“ë'wäûÉõ“[óýÀúIø¾¼~Ò~£r³øðîúI;Ùáý, ¾ðÅà _øbð…/|1øÂ¾|á‹Á¾ðÅàû›ìå¦ÚÔâíÊ­IEND®B`‚urwid-1.3.1/docs/examples/index.rst0000664000175000017500000000215612615524560016631 0ustar ianian00000000000000.. _example-programs: ******************** Example Programs ******************** .. currentmodule:: urwid These example programs may be found in the examples directory of your Urwid distribution. tour.py ------- .. image:: tour1.png .. image:: tour2.png Show how many of the standard widgets may be arranged on the screen graph.py -------- .. image:: graph2.png Demonstrate BarGraph widget and alarms used for animation edit.py ------- .. image:: edit1.png .. image:: edit2.png A simple text editor with lazy loading browse.py --------- .. image:: browse2.png A lazy directory browser with file selection, tree-view, custom widgets and list walker subproc.py ---------- .. image:: subproc1.png .. image:: subproc2.png Monitor and display results from a subprocess palette_test.py --------------- .. image:: palette_test2.png Show available colors in various screen modes pop_up.py --------- .. image:: pop_up1.png .. image:: pop_up2.png Create a pop-up/drop-down/window anchored to another widget bigtext.py ---------- .. image:: bigtext2.png .. image:: bigtext3.png demonstrate the BigText widget urwid-1.3.1/docs/examples/real_browse.py0000777000175000017500000000000012615524560023754 2../../examples/browse.pyustar ianian00000000000000urwid-1.3.1/docs/examples/palette_test.py0000777000175000017500000000000012615524560025341 2../../examples/palette_test.pyustar ianian00000000000000urwid-1.3.1/docs/examples/palette_test.py.xdotool0000664000175000017500000000015512615524560021523 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 79 34 key --window $RXVTWINDOWID Down Down Down Return Right Down Return urwid-1.3.1/docs/examples/pop_up.py.xdotool0000664000175000017500000000011312615524560020322 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 39 18 key --window $RXVTWINDOWID space urwid-1.3.1/docs/examples/real_edit.py0000777000175000017500000000000012615524560023024 2../../examples/edit.pyustar ianian00000000000000urwid-1.3.1/docs/examples/bigtext.py.xdotool0000664000175000017500000000047112615524560020475 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 39 21 key --window $RXVTWINDOWID BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace BackSpace e x a m p l e Down Down Down Down Down Down Return key --window $RXVTWINDOWID Up Up Up Return urwid-1.3.1/docs/examples/bigtext2.png0000664000175000017500000000604712615524560017231 0ustar ianian00000000000000‰PNG  IHDR_;ˆþŸþPLTEÍÿÿÿåååÍÍ´¿ï%bKGDÿ-Þ pHYs  ÒÝ~ü ®IDATxÚíœM’ã(…혨¨ý©ÙOCÔ¾ƒÐ¦uÿ«Œ€„lÐ-÷¤ÚÝM![úœ~Ê|JÉu¹ðrð"܃þ˜þ"Ž/×é§[óV¯b5Ýëõήî_®7²V\fÏ»‡Õ´ÿˆVà[,1 <Åéz»|½ áñÜG)n$Âvf’ÃÕoJø¹›WË…ÌLƒÛuZ1m-)<Çθ]\üßÛMØÏ|ÚêeÝÖDxÚ×M„^o×BÃþøU·‹ ¡3ö¿«Ý¿ÝVz²ÛŽ{ÎíŠìPàkÄõ&f¾<Ö°#Ì…†Éà‚G„ /Çí‹0ÛNØ…{nνÛ,ÂgÖáà‰Àn³$w¤›E `ÂP¶i§á ?¾&m\.³ûQ‹ðel5ìpü±ÐŒ"¤±Š&J¸àª‹ørœ±Â(5^v¾à¥ß•Ä%|ÈžÜýt#Ïã!¨ÂíÜ%[x¹Ÿ¹ú½Û‡ˆ/½Å, aƒ^K¾{Э¨†½XäŸèû ò}~àkx|I5‡—³/ÇKëà·„>û’üáͧƒ®œðŠ˜Ç$!6Øïg8úC|nE$3Ï-‰d6H–8s“côÀÄОVÁ:)Bª8³,¥ÂËÿq‘ïµüÅÀ ÌÀ ÌÀ ÌÀ ÌÀ§ÖïµüÃÀ ÌÀ ÌÀ ÌÀ ÌÀ ܲ|00303030303030ŸXÌ|-þžû«¶rñUµUw^E€ï<Çܳ¿fƒ…€|ô ôªç0030¬˜˜Ï |×ÎEWI|¦[ZŒâ:àöR7Døã‰Ö ÌÀ ÌÀÛ€?vÖ;0030ïb/;»—¸jæOuKÏÓ®züœç÷‡?¶n€˜˜˜˜˜˜ø‰Àã{-¿ÿ`#ß-†÷–r‡qzØähÌ4pÀvætÀƲP3ö1Œn§‡ç–RÈØ­:a„GaS‹ð :¯S±œiøäY¸Ëƪá”Yb>wÒ ÌÀÏæ_ýÄÀ ÌÀ ÌÀ ÌÀ ÌÀ üNÀZ+©öÞ®¶[=¤„%`X½]µÓvêÀò µ–^€˜f $ú€Ýév¦©€µßHP8Pvü®•Ôa¦]ÃîÕà··³°ŠÀ2Ë´#µ¬óåCùÆ÷Õ°²G¶W›D`²H·$rà®—7FXBáµx§ §´fuÖaG€Õ`ØOápXI@„ƒîU’H[–{f ù^ ¿)ððníVw Â/ÃþÀY²ÂÜà—2@¯6e{ÜÎú1½”f$^Y‹ƒÇÀõ¤Lì¼xYÊtuþ'àŠ}ß8)ax<Œ=âù*ºJv®¬8K€ur[ý5Äi¯ø¶K+‹iŒ°´ˆá±8^²Lšqò1~ÐŒ5¹–ð.’˜_&—æÛ€½«ï*#°=KH´ºÓv›žƒÎ#)E#©ˆáìïÑ&ÀšC¦a٩ᥴVÇÛKL'°Oè*}ºPÊe‰8³ðT"^vÏŸZšÙü ¼×m OVpìzè>Là HE$p“¯ˆïè ¶À:{ßQ»½Ì´-m ßžfÔÂ'°Ö^š¡Ó^ÎU¬Bà ªŸp³½zíe®aw¾á%D#Ø5´*ÀÐ<·—öF®>{™+Fœ‘º^ˆ°ÒmÀs{iŸÙg/+À1Œ2Ì`§éí›Nƒfàš—:ÍÏ,9ib¯a("œÃBke¸ós%°—-Â*xšÒT-K¯¶—ë"L%¡0Ìœ8Ý ê[Ví¥î³—• ¹<ìtççJ ÀМÛËqìµ—Õ²ÕPšÕkKsÿòøtn Þ XÿiÀÇi8Õ5ߨ‚` ´®@û>–¬iüuð‡YbHFrL…ظº‘õ3ã€Îƒ«SèÑâ€Ç‰LþO¦[•çhv3&+ͦ Læ(°ãT*+_šIâUè„s` ƒ ð¬Ò-›ÇÀà 2³`á@Rç“"ìô“96U=ã¨Ëð¥!“¨†ô5¢¬¦8 :øJ8ö{u–Ïþ§”„dƒÁ`Hn š <æS;†8[<`bÍ–€ý§ G  *T»#ªiš ™™Ÿ°G§ÑˆçOŒd<’ XvÍäà~{iðº9¹’ŽÀ",K G`ßÔê’D»½ôœeÛÍIÂwP—5 K’ð§*²GíöÒT¯›»ƒ.?Þ‡ƒî^–Øn/͸á´ë‡iMmKkëì¥É±W‰]^84öRáÀÃÐ×”þ±Î^J¹Õ^¾¶4³ùaàÍÀ¡+¨$¼@Ã*a:yÊJ‰iB-}@÷²z™&ôÖ(U|ÔV¥Vª¸£{¹¸¬;³ú{Ù¬slZ*l¦Qº˜‹Ã#ì¥I5 å€F}úV{LÅ@YÅùþþöÒ:¥èñpr ±µ³ ¬ôZà~{8Gb*r`•é•à€•"jˆÝ©~àn{é¶%Rr` n­œ5 “Ð{ûíå@}&Vµ,áÝyPqZ÷x'{‰ÀM.š É œPcïBLÕÜe/i–ÀI9 ±4ÆÒlß<§4'`ÕCü"óc(ð[¸µIïh/Õ»‡ã‚Ç+-@~”¨u k:€CqÛö~,°ZÅ/ÿ šV›q—´A4|0ð°+°+JÆ«]XAüÅg\…Rˆ¥Ð}£<¼ªØ¯M¿91E8uzc» ‹™Ò òV•Lo©?<¶œÖcÈMA¦Àeo-4`UÙ–Ä‘.µªæÇœ»‘ÀlUE’DìïœÑ5`©×ä,i;0T»—AûDxØ5Â@¯V g'p‹îâíÍ›%SÁ>>¸¸¸ÇÇÇÄÄÄ–––¾¾¾’’’eeeÆÆÆÅÅÅŒŒŒHHHœœœiiiˆˆˆhhh­­­ÃÃÃÏÏÏ®®®]]]---¬¬¬TTT ZZZ;;;111ÌÌÌJJJcccrrrkkkMMM777DDDÖÖÖ(((pppžžžÙÙÙxxx±±±××׿¿¿”””‘‘‘sssÜÜܵµµ///   ªªª555zzz+++OOOXXX»»» ÝÝÝ $$$:::{{{ ˜˜˜‡‡‡NNN333———VVVttt¢¢¢™™™………§§§„„„ÎÎÎËËËuuu†††ÑÑÑIII444ŽŽŽÔÔÔ···ÛÛÛŠŠŠ²²²}}}888qqq[[[%%%===mmm)))WWW©©©«««wwwÍÍÍlll\\\ooo¦¦¦|||bbbFFFSSSÍÍÍÿÿ°®Hª pHYs  ÒÝ~üêIDATxÚí]_Õú†qqA {! D$,AõR©”/)/êoM³LJ!EH1S4±ëö‚¥Q¦j&•Š–fzÕÔTìE»Þ´7º”Õ½÷ßøÍËΙ3sÎÌÎ.ËÛú|?|v‡3Ï9Ï™ï>癳ß=3€@ @ @ @ @ è 'åWz •^ÃHAx?y3,Bo4÷ý•·°pRâ` %¾ŒDÊœ1¼èÛ;˜5aJëÃ"=4¢ç¸!7Åzó[† ¹UéÐm ·ø•Šõá8ÄÈï°;’’‡§HH UJF¤ÊüFŽLBó{ç`Ò4gàèøÕ¼x³Éo×D¥­]⿃ÓGÝ•~÷èôèôô e昬þÙÙÙ÷ädg5«;nüßrïMH¸o|B‚S)‰¸BòÄ\ “<üæe?õ`Ôä)S§=4âwF~~ÁÈüüH«@dùUÿú¿……Ë GÍ,œUX8[Ýÿ®ððð9‡‡Ï%•Œùy޼ܘâ’áSKÒÕ’¹¥ÉŽôù"!%RüŠº¦)q>x3æQ±Âc µÆØ, {<:,,z‘®£’Õ{Cȶ ÚÑÔ@ÑÙè[£7d ]‰Á³Î˜?Æå²¤²²'ž\¼¤¼â)g™¿¥ù‹yùWjKg; 7fè2gå²åJIUb5$;Æ>½beÍ„$¹dÉ×¢šìšøUâö3«å¢g'8×Ü»–æ×Ó4›aµJüæÎ'Çcˆ_õð8ÁC.y=¯‚Ά›Ïf¼ðF˜`< fCQúwÝúçÆ?¼¡nØÆ¥`ÊóÅŠŠz<^|)×…©î(Ü‘›åv¿ï~Ðý¢\4¥&<ù¥ÐÐûÌ ­’mr_^T›g&¿²"¹¨T¶yuþ–™[â×ÃüzSsÖüy8$ ùÄg~5f~_S°BFg­GeC’²ÿõ7æFDD”o_\·ºÊ®7]·‹'µE°C-*†šÚÚU«ÝµµµÊ¹+²rU‘sdzICãê(ÏÐkÃÛw‚!~•èÕÚ6á—á“.üå—3ÒÉçì+èR”®g ¿™M;k¢d„¥R÷;øù®¼ †ïNMy''eZ’ /,»ãmÊHŠß›&íæáwµë^9´õùÁЧ°9Jþ {×:~“¸óÊ/˜Ìó-ò˜Ç/XÇ/À¨ÍÊvJ ÙŸ²×ívß“èv¯R3óýãþŒæ¥ï­ÓÚQ¶<.ÿýšQù±ãó”’†º_qïÛßüæ¿÷ø^¿ìÐðÄoÉ.ýÓ鉉B6írò¯Éçi'ÿ²æº]“i~—ÇÊ8@ø›$ò9Uµµ-L¦ñ <¬ô`mò¤æææåJIä kAwSdøž*ÍȪê‚ê~…ik›U~×%×ñòÝt­Kæ·>%O½lüoÚ mLüòóƒ-cíËk³‹øÍüp('?€@'ÉG ûí€äFq«ÐÃo\èØÔ­ñå4Î>"—||ÇÑ܇rŸÜØüÉ1•ßãÿ8qr¿1~Ù )m8¶îTÍFÓÑ'tjbëíÛ_!óû©[F´ÊïéD×´˜˜˜è3âK£z<ú)‘cý´³û ywFFÆJ…ߤ¨3#V6ø(À¹fÏ)0~4ÿmŠIšR48ºÎOùlPe¥>èŒè<˜|Ë=ï$¾Ty¡àsB}Xù§—t7¿•2òU~‡/€=˜Î¯Wu¶”Cò……… ¿­]îÈcY•0?mŸbÓ_äZä7óø–Öƒ[•¢Ïo–Nƒ³_™õ…y—¶Çd|™)oþª«ŽºÛ„,®~æ²U•µg—¬ë|ˆ> öT”¹[ÊH¡P1Doùb>8«Í“…Ùì‚™^ ËÖÁºc,O>$å“䔨õ))rÁå¨[j’£9ü¾Ì©3”è¿Ó ‡ªSQ›üõÄϪþ3#Ã$üh9—ºkMÜ•º×_­[â)i^¶šîš“òó,&Í7vÎÎ~£v=-õ½§êëëƒ,O((–&‚ß\­ý¤^þR­SGœx:bú–ÚOF7Ò —&AWBÛ šê§kE7*F]û°nË ¦Æ­ÔwLOÑ»5C'ç'.ÏoŸŸ§œ '%ŽËä_“jÔ:èüÀD0ó]ÏqáÛ”—¿ˆÝ¿aû˜¸²Òà\ãA.]ÇĸÅx~“*;ºrٲؕQ%<Ÿ–ÐBò¦AHfãWýßȯÀáW-Éø4444úTå­bz¨o –üËDãæ‹S fL=àðð;qYS,ïcñ¿5þMø5|ñÝÙ³g Iñ)Îïƒ&ñÒ§·KaaY°yÏÀå×¾âáwÒñÁWb¡!66¶fyþåð«ü˜aÁ¯jGå‡JBWoŽ+;´ðxhð+9¼ 楙%.—„•GhÙ•Ý07V¬³ÃõÙUß s~oùñ EΑ%óeÃcŽ¿ž_Íh²Â Îl­”~<ÙT+ó{_vö‘ßüüü:~¹_/رo˜ÿêÒ…ÖŠÜÕ)£«eŸ/»-ø8ËN&ÖÆ¢„³ÒØÜ—­ü ÔÄOàœezŒ_ÝZ¸øÁÏürÏfþðk4=z±nЩq`ü`oØvÉV^4sú úÓö$¿fç ÁÀ€>0 ™³†Wà¹ã•Y ü Ü¥plÔ*öPô’Ag’IçLâU—ñ 6‚éÎbÂþâ¼I¦À‰{Aèö,˜Œ@&d¸“#¿`Í¯ÑÆ¿†¿àõÎ òîç×Nü Lhrò ¿Æ™ø5cÓ"˜ó4,’SÏð &I‰™ê/ÿ íph¼'{ÞÄÏìTeu!Ü;øïüê×…˜äd°Á/tŽ_:ù0ÞèLJžà@ }if×k°mW7ùÒ¡ªgéÛ=Szqš,4µ•1ÁßÒÓkœM›î=⥖¾,¼ÿôïõù>×ÎK‰OØàMõdÖ°Dº$&ÁÜ&¼ý%}el Ǭ¥p9UWTzß®#÷{©æ—/ ïM9?_ò#ä¢ü´Ǭò*mTù ùÅÌfAs̪±î ä—6åSW-@ˆÔrD\ò\C–™ÞLÛð úRm4_üZî.Õ{±t‚]&ÕÍ»“ízÍ“¯¶´fòji6I寯”½¹z5E¾?È? ²=üVž5±‰+HtÊm6Å ¤ ÚÖ_ýÁ™»TÜ^©,4};ª WjùzÎÎìw¤ËùàבÙÕ«¦T›Ó=7½`}©6”/µ–…S÷C‡ybCÁ:®SR2Ø•UAw#f½sC¢´Zlò¡«—[î½›S‹Øhh?Z>¬í&q#3º¾—Ìs"à¿W¿âÚ´þ 6Ö‹µ‡Ë3/ÖÓÆ*>ÜoÜC¿AÜJ®§>(­Si=Pž´If3ò÷ˆŸ¥ÅO/¾žPøõØä=øSKÍÝ\_Ćòå)±pJ z?™—ušyNµZYMq ՘߫aã¿ÅT})¯æ4´åÔRm(§Ï÷«ÚØ"-*sŽ„¦kRø×ˆššg>˵¹Þ°BÊ›×ßøÏDʘ x@Z,-€¥›^>./(lÖ@È'âÛ#âÙ`€TBlæ>8&ϱ!¾H‰…SÕ»HìÄë0ïÏ)))ý]<§fÕQ݈)PŽô]ÑE~ ¤Ç©¥ÚhXwæBÂ4qsg+TÈŸú›r~+[ðËB®ÍY œ+3úÇ)c‚ÑÄù5ŒN€[Å}!°C¾Ý?)ïÍ€·øö£È”|~#6°äÒö,90¾ˆ ñ¥Õ2wªíóx+oÜ ëŠxNI‰ëžVpÕŒ£º1=àg±uâÉ`_kæÔRm4¼!“ÄëÊ ¿ŽÏ~[ÂÊ’&-ãÚ„¸33õ1)?Ì ‡S)cmdˆ|ýÀ²žƒªÃ/ÂCäZeðý3âÆ®7@á÷µèÝÐ.]eLlÄ øð_êeCz_Ćø¢j™:Õ¦!ï#HǸõžS­¤y,Lo¦»qßÚÌÔ61—¤I×@os¯àÔRm¨ø-’^Ç‹ÉlÉNOCµÿõÌÏàÖ«ñÇ\8v(*T^4Uù^ãúÛ)c ÃfBK¥{ŸxB=›õÇ—RÑ©«W'Jëפù…ߺ&ŸÄT›s%bJ3bŽ/ÒñEJ,œx¼/ÍÞ0æ¹<žS­ÖªÇ ]7N]UšˆŸ^&Öÿ˜[Kµ!ØöJ9ˆ¹h®H©˜žž.¥‹§®YÛPˆ“MË+ß5ÙUœ-æ±MÜ]%bFžø3ð}y‡…SjÎ$ÿñœjøé`®DWdTMŽª‰™ŒPITMªc½w¸G¹hÙ¨ˆU'ÃK³¤ûpRÕYÅRýˆö¨“ød]‘Q5¹¨§T‰G¨$ª&izíª²Úí{¤ûÙÙ¨ˆp[Zü ùü¦Šª®È*–šè§iªÄ§èŠFU“'ªÝà•DÕ$íôZ~ψI`¥r;E‹«Q="¤ª+òKUôÓ´GUâ³hP…•j)Tšª£½&~£KöÎY`Ә芊¥¦=ªŸX4èŸPÙk3ìx±mcUW´R,iíñ¬Ý¦­ôO¨D &A†ÞF/Õb½îp‚-|{[cü"¿È/ò‹B~‘_ä;„ümº¿×Üûü'¿¾Ñ+øæùõñPįŸƒ¦Oó+›œ1%Ô-ðy÷"Ò?j•jG0<þ[Ûð/'õ~~#ö|ZflüÇÜ &÷pbžU †'N3-C§’}ïç7D^÷2˜yü‚%¿à¿];{˜ßmÙŸî7MZ6 ŸŸ Ì#|åWÐ¥Œ æ×{Þcç;ɯÉ@¹±ø5yl7'~™{qZå_îÜoÈøÕ'ýã¿uVL-þ³ÂyÆ7æ¿ÝÕá ¿È/òÛü"¿È/ò‹üâá ¿È/òkÝzw/à»±øº½ÕŠ_¡ûÛE~‘ßÞN¯åÏúÈ/ò‹ü"¿È/ò‹ü=¿‚™¹~ •íðol~yOÙål‚·=È/—ãÒÚ¹7,¿ºeºÕ’ú• ÞXà즑_ޤӗùeÖO² œ@Э»ìñ˰ªidýÍ Ê¿ÌúI¿6–“Ú‰:v-õi×M¿ÌúIrg× úįEüš »Áȯ—ƒôŸ_‹ø5ÛüÒ1è_6Ëòøå=×8¨ùÕ/nÔ“äÓüÁä!Õ—_²C²ùC ›îþ–‘_äùE~‘_äùE~‘ß ×? ¿ÝÒ!\†ë'û4¿x8È/ò‹üâá ¿Ø!äùE~±CÈ/ò‹ü"¿x8Èo—wŸ/‚ñÛ—Ã@ nØ¿ÞÀf}¼âTF¦ÞØÛåTÞúÑ3ß´KEHA` æñ+Øq°4tªÿ»`<ùa¾ˆ)Œ[3×Þ:ã¼rÅéÜrÅ9ÈËÌu>¶øýsPfßâ·í®¹\·‚™go½™p`X×@‡yêùVWZ9½v†wïúÎð˽¬[Œ_OTÝVÌÝ9#ÔeJ¡`ÌçWkSǩɕ‚! k29£€ŸÎØdm‹_“HkN6gFÖ}twÛ¥næÕw]¬ôʯùS;Ø>Ûä—}Ø„àý˜/Ÿ?>¦Õ$~‹JxÕ½Åf'l6’­]gŽ‹ïS¹ ÌuÿfÞùõ;~™çÚØáwãèÊÕÓLø=ÿ–¡º ‘Ð/~yð$)i8±O|ŸÌmàá!þǯÿü‚ñ¾6ø|¡²=Ï:ÿZ<‹—¿ã·ñ4Ùü¶_ñβvý8…W[C\ä0<×lÜ)ñ°}ØàW˜Dì5M™™šñK}Î忉§¶%NðÚqåNS~9L{ÌøÆ/}÷ ã#o8ÇÜfh˜óý‚}ŽU?¬æ*ösˆ¾IÝ¿•©.ïürSrgøõ™®ÐÒ€7àoN†v"îO‘0Ê–±­N|-ž€-é~×}´ Zµ9þð‹èÒðEÜ€‘ƒÐ1ÒU]_âKð‹1­MêWVI¯I¡Iì®í+Ûgÿ·ÚÄì‚ß %¿ÙïàŽZó}ûsŽ‚ö2æ7W/½Ž|[/ao;´Cø7¯6šðû"S4Ø~9Õýjgñ©Àðkën°dòo_£‹l^X§Íù7ö¨Á2¿C¯¼MˆàòåÛ2 æ ¯`zÓm®¹yKk€èìüwmžÙ®ÒuEÞrš|u¾‚ÝŒ˜oÊ™¬ÕûìW¿âŽ3Ûõ¦{eOðk£I[#¢ BÕEU( LY½õÉn Zù ÞPÞöFðùE‚;‰_eÅÚõ, ¿]‚HY)°˜æ—s„¿˜ÐÀñ â׿k„Wìš °¢ ùí2|\^ºß.Céæ åz~¹’;Â_ü¬_ýeHOÀaýø*DùE ¿@ @ @ @ @ @ @ @ü…èJ ¿]Ío‡{æã¥,ýøŸêFàT6mY×ůyÓÖÿwXûF‡q£ÓüÒ[>´XžƒŸ_eÃ~³]ǯ:ô;T?læèÐe ù_ÕB3¦Ú±xh:˜áÌkÙójšÛ,ãWí|•äŒGp~©NÊxGJå¬=ùôÁÛŽ:Yé0t¥ƒŽÓ£Ò8“Iç;˜nwaüR¯F§LüvpŽœå÷/ø5ÿ—“”¬£À[üê?žn㗌߿:ͯ!?èø5ÏVù·‡ø¥;ÐÑSüšžŽ:x#¬Ã¯{šßS–¸3ß}µNƯ×áì#¿f Zåߎ®à—svf&Úx¤çZúáìW~øŸõüA 6æ& þåå(Èîp‚ü"¿}™ßÿð½’û›’êIEND®B`‚urwid-1.3.1/docs/examples/bigtext.py0000777000175000017500000000000012615524560023263 2../../examples/bigtext.pyustar ianian00000000000000urwid-1.3.1/docs/examples/graph2.png0000664000175000017500000000460012615524560016655 0ustar ianian00000000000000‰PNG  IHDRÇhIzK»PLTEÿÍÍåååÍÍÿÿÿ†ˆIbKGDaf¸} pHYs  ÒÝ~ü IDATxÚíknÜ6FSt ºŠ(øY­ Æxµÿ%”õ ž–G$% Ï…ã2ãÌ >=ùtuG2üaêÇOS,¢-šÕ/»dkñó¾a2&³ÀdLÆd˜ŒÉ,0“1™&cræ&÷¿T¸Z‹ Ȭµ¸€ &c2…ɘœ™É%¦¶LV¤Š-“ 2&c2&S˜ŒÉ9›<‡ü:ýjû ©ôû¿ÂZ\9 䵸²UxkL¾2dLÞ¥D‰É±!›…õ”e»Àä(Í•%Ö,09B&«rq2&ï†<¼&…,Æ&“Éq!— còW º‹¨&'†ŒÉÉLÎr•¦tÖqa¾ÿŽC:ȹÅEUÉD·BbrdÈ:†Ô™›<ÕÉ ggòd E¥É“#_É2dL6 ÙB–˜ ²Y™¸ˆ“›”˜òr 'MfhS˜rs2¢…”•¬$&‡|Âiu~&'©ÜM>òÑQçß¿vŸ‹ûA¾á¨ó~» LŽ ù£ÎÛA¾ã¨ó% WÍɈ¶r»™§p L¹u6§Õm‚ÈJ´Ó LÙŽ:¥›'·e`Ș\ S8n„!5˜¼pàë!ËöÓÁY}òB ×g²lÃ#(dúäaÔ)„îfžÝÅ!Èk§ÕAOûr7y­"BÎÏä 3êŒRŒ:—39"dZ87êœ+®19äµ)œÀä§!ïujL ÙB±Xý §Ô˜²»ªS7Ã8ø†É!!7WujoR2&WaûžH?áÄäà>Ïä “B^iá¢B¦OnGí=#}wòFÌ."@NRŒ:“CÎmÔù0õn~D­ÿ²Ž‹ÇããóÝ‚~Ôé ç÷ýóññÛÀv<>êúó1GþQbò³ [¹ãZw㪩ž¹É²øîC^(óÇ>>1ùIȆ黉‚÷G±vá`ö“—á ggrízä>“ëOóˆ—áâ"K“GýX®½”8"2&Oâb ¹®»¼8ÔâÑ]Œ|S“$2ÝÅj W»¾Í?bòs—OFLJ8ÈæwÝ£GÎFr?ã;á´:?“-`÷‹ÙELÈöᔓ:÷Nbÿ ùÄyr‚¸¸ äW¾ªó2ßH_^xË[Ì. yýÞêû½[}Èûo`?’'™›¼÷ªÎc7‘`òÄÔµ«:&‡ƒ¼rU'™òòUwüQ ?ðÍ/8Ääà-Ü ²Ää€W®ê¼å ì—üÊWu^r’ÊÝä ¯:÷¾Ú?7ƒœ¬öÄB^‹ c2&“1“ŒÉ˜ŒÉ˜ŒÉ˜ dLÆä¬L2qA\¼ä¢[”³/•˜²R›è19äb™ç1Ș<Ù|…yRa¡( »(í/÷ˆ° U–˜|²²ˆšU¸‹–[óQîy5Lþ²Ø†¬¾ “'„y²‹ŠC1ù+“&§€ÜdtÉá!7M…[ØÖ¹tm†ë.0ù‚§Õ˜œ2&'39/ÈrïߥÆÞ'hââ4È9Æ…î|ZÿFõ²ìž°ú¿Hbò²ì¹ÈÉÍ#Ò[x×oÄÖÞ“=Ⱥú²A–_Cv{crYzDõp‹™°û¤z7°½[¦ÚÛÐúg ¢1Ùƒ<ü ƒŠnãõÑd9ÙúÄõì‚ÄäÈÍOlé!ËÛ;UÝKì…œg&K?ƒµg¨y9.tû“sÜ/!G7kcòÖOv¯}à“ÃZî<ðeÚ'[8=l¼>0\o᤿pro·p™öÉ£“‘nvÙ ×OFfÝEû“_6OF25y_1»¸dLNfr>Ë4¥²Ž‹S çß{îÛïö¿eYcr$Èu‹¶þÝáÆäÐß:¶!cò–È]J¼}3-0ù Èå7EÆäô1yäº$“cøÌâH ‡É›*÷iñFŸ rņð‘¸Àä(…ÉÉ!cr2“/y÷ >ùÄKg/¯ùrqÉ@ÆdLÆdLÆdL2&c2&“1Ș|†Éw„¼÷5q‘äûÆ&“1“ŒÉ; '¹ w“(­²7ùȘŒÉ˜ dLÆä M2qr·‡EQªóŒ %&^ï ˆvç–ÂßK¤è·s)Ôœ'&ïƒ ¹9oöFj2"“Ãø”k3 Œ LCV‹&‹cqÉSÈí¨S‘ÉQNF\\´£N5lbòAÈcââ`aò 19™É@ZÄÅ ‰ LÆd c2&còKCæv~Ÿ„ŒÉ˜ŒÉ@ÆdLÎÐd Ä1“1ȘŒÉ@ÆdLÆd c2&c2&cò ˜œä*Mé¬ãâȹŅy@ºÇ…hZ&,1Y·÷«L81YF‡l^,s“µ§›¥arCÚì™Ö:w“å róPØ\–™›|äìLÖ ë컋îÀ§ãA–Ùw} 'ÇÃaÖôÉþɈRº³°»ôɾpÑŠÙEW"äŒMŽYš«:ygä!óÎ&c21“1ȘŒÉ@ÆdLÆd c2&c2£N₸2&c2&“1ȘŒÉ˜ dLÆdLÆdL~ “ç©qA%ˆ “1™ÂdLÎÃä’ S[&SÁjÝd*à˜Œû²bÿäÛ¢Çcí†÷z08îë;—c‰Í}†o!X±îÌé{Ååøþd4ò­ã˜žè_íä8ÌÆÇJ1v÷ïkÍ~Âcó?ƒ=9Ößýöé±¾y‰ç§ÂWL}™‡û‚ãûÖ{è¯:,Ç·ÛWÇWæxaw¬Çd}OÁñò{ì8¾Žws¼Ç¾ùÚ?Þ‹ãû÷§ÿS™p|³üñ>ï«p1</öAhŸÁ߯Fú¾Þ°›Î÷˜%7ÒWD_úD¤/}s4òôGœ†ã'(Ž/Áqp|_õy·óo}p±_„?ޏ5/=ïv¡v'žÛŒ¸«w33ã¹Íˆkçø2ïÃ2G68Ž8GŽwêqÎâ¹Íˆ3çxìã¹Íˆ›Ã±Ñãxn3âæq|Ø÷½‹ˆŽ#"|޳ïGœ=Ç+}xn3âz9޾tÄ­à8""8ŽˆŽ#"Åqøãˆ[ÀñhËŒÍÿßLÙgQxsD%~Âcð>|-~b—û‰®ql?\,ÞxóÊÏ8ñ“ÛëgÊûyþǵùÇo^ãàø€?UÑ—¾˜qzOúðp®¡é}Z^ûˆÇøý~®òùЉmà)xo¼yŒÙÿÞÙOåb,$ãŸì2ÇêÙ xãÍpÌSÍop|yŽ'¾BKo<øvu'?½àxÏŸëÚ:oåíéƒã}A=>ÜOuŸ}·{ "Çœë Kçøã`yùgºÈ±}¿ÍxãÍ+ƒ¬–êíLc¿bŸŸhô¥#nCÄsBÁqDÄ™pü`DÄÍà8"8ŽˆŽ#"‚㈈à8"8ŽˆŽ#"‚㈈à8"8ŽˆŽ#"‚㈈à8"8Žˆ¸¿ÓÕbòG?tè˜|¯‡¯#&ŸGŽ“oúÄ£Åä›>éøÇw‡c™cxHb^ìÃ1<Üp}Ã#“ €+s D™cx"8>'Žq:Ž_ÇÁñ]á8ýc¶ÁœÎ’äB­Ôz›³ÇÛeóX ¤DÑL·hrÿòŽ·É™¸üå…>JŒy*f“½/o‰˜ƒUŽ·¹ÊüUPJ » CÁîâöåKos3²ù« ¾”hšÙœ6P¥&Kos3²ùË ¾”hšÛdšC_ž¼UöãÖw"š;¾-¡9ûp\aäÊJ4‹;=¤nõ‹Wи„ÍâÎÃT„¼¬©B¥‡1çÇ•A®²D³¸ÓçÁãØ@?ã¸ÂÈ5˜hwžÈɆ$m"Ö8® r &šÅbºë=¢_Aò«8†¬ìÏñC’cbð „áø¡}8~XrLŒÆ(ìæøá«süˆäø‘Ʊ^Ìá‘rüDÉ1Á œ†Ãñ÷åøI’cBpœ†ŽŸtŽß郋‡²Zü¸U'_’c0cñðÃF‹%Çm2äWûC‹5ŽÁŒÅ#-ÞÅ1”±XãÂ4-pܾê’ƒ‹'=Éh±Ã1}xr¶CêñCc=Öú½?ÇzüðT%ýFÑ%Ǹ”Cêñ#—ÒãGöäRŸ¸¦Ç¸šCêñ“<=îÖz-¨\Á[ŽËÿ]…c§ í•wq¼·?îFÚ+8 ¾–cñ'ïã»Q†öÊëþø‘=ýq7ÊÐ^Ù÷ÇlÁw)ŽIG¥ûþøHÓ^q ^WدxV¸lدxXs¬ö+¤ Co\è]½§±÷~Å#šãÑ~x"¶;ö߯x¢æx°_ñÄ6—¾j/Žå~Å“`5ûrçìW\ŠãèK»¬ß±¾4NÓ—Æeö•ƒãàxáàøVplzwIUÓã¨Ï Aõ8Æñ;_-&ô““ïõ”ëˆÉßç—-&ßô©G‹É7}Úñ#8ŽïÇ`TÂ$WáD`þ×䨃ñUòº¯~Mƒ¡Wóü×9*Ç`èÁMÀ1q-Oÿ¡×É1 “Ãp 6ŸcX¦¯•ã /Ï1à¥Gæ¸Ñ †ô5pŒ–ão0ÇL?åÚ9Æ/¹ŽÑsœLQsm>ÇÛ¿wÅK ™Šq\íÄI8®&OL_ÇÕE@°{l_±}Óâ$ÐMű9F±xÚõrŒnˆìQñ-¯ÇåÌw‡q}z Oƒ¯Žñœct;ŒîÌ14»×¥ÇGã˜d8ëò |Œ _¯?Vã_aÆSøŠ»Áñ©üñys|Œ÷å¸DpŒãƒ`¼'ÇO =>4ǧØ?çÀña0¾ _ç¾Û¶GQ‚%‡õǧḷ?ŽÝYã¸þ}XrŽ¥8Çõ›^W$úÒÇà8úÒÁqpÇÁñäøÁˆˆ›ÁqDpGDÇÁqDpGDÇÁqDpGDÇÁqDp/Ä3âÑ…xæBž^Fˆ¤f Õ|<£ŒhÉ3X)¡šGË‘Ô,¡šg–Q\JX¹—{Ï4<Õd%¹£žÁ±à˜ÈíŽ fˆÙ¯ðú wãXç 8޾ô àø„ÇÁqpÇóxöBüÒ…x—…x×…x·…øe ñœ…xð–Æc'Œà88ŽÃñö/YKzIrœ *É‚ãt‹ôªà8ÝQI¾§[ŧ9ì¢'œ×4Çd)? ÇÝh/€OVòêSn3ÇÈdzÛX°lãô—¢[3Jˆcäã]ʈ–¼‹àùx×2B$Ä1òñne„Ï1³àôJŽ‘ç”QgæxÌ+®iÎqe·_ÇcVãÌjÃ1Á,1ÌjÃq‡yÌ14¾Çá;È1»+—p&'_Qæøç;‰ã2Çç8ßI—9ïªEÕ_`ã¸Ì·JN6ŽËÁq.°ä9eŽå¸Ú ‡c2y·[½î;8}Nûº6Wâ~£·˜¯xì8¶â,õXÍ9žèq)ô˜“ WÉ×|¬ÇTvõ¸\±d¨Ç”IÑÇ‚ïƒ#.Ù¨?᪃9cf/î„?F#—8nJ½“㯀fŸc¡ÉïºÀñ»9æ‰Ãqhªœ„ãW8~ðÀ?¦9~ì±ÛαòØÓWÇ#_Á9¶¾Â$C_Á9¶¾Âr<ñçÉñƒÌ|ÇWñCŽ/©Çï²[-ÇVq =>%ÇÍÏ8¦9t¡ÇäXí»={ºïfý1í»IŽ1ßw³þ˜öÝš-vü1í»Mü±Úw;¡?ʳúÉqÓc’cÜ~ŽEDõA`úy¦Òö+œ>Hs´_1냴ý §R·)ú~…Ó1û^ähû´a޵·_a}E…‡ÐãÞþÀ­ß¯ˆ¾ôB©ò¥æ\žãFpç\ÑÇÁñ¡Æåæ¬>´yG9~îB¼ûBÜ[ˆ÷Xˆ÷\ˆ÷Zˆ÷^ˆ÷Yˆ#Áqp/sœþåRIÉ8¯Û?o-aUÁqº¥ËqºE §;*±§[;9NsØ…Ïqšc²àø&qŒ|<·ŒIÍ6Nß½ÒÛÉe™cäã^!âùx2¢%R‘÷,#DB#ïUFø£1 A¯àùxŸ2ê,8¾‘¹añÀX\vŽ æNµá˜`Æ{ 9&˜;Õ†ãó˜cH|]Ž;ÂÁñ å¸ì‹sV'_Qæøç;‰ã2Çç¸Þz2Çç¸ÞzÏ2ç=µ(£ú‹íÖ{•9‚ãViÉ{—9‚ã\hIz}Ÿ2Çr\íÅv+à»izL¶ØrÌôXMå=†¯ÇÂÃÓcå1Öc–8z\®˜0õ¸»æàøæqlô¸)µöÝ¥[Ʋ¯ð9–&ÙçXúã«qüÞsŽ›@Ç7cò#l}…ËñN_±¯@óð|ºÜú –8¾BŒto·¯ŽožO8Vë¼}õ3_¡’Ù:c=6ÉL±à+z|C9í»¹‹Súc»ïV.àí»IŽé¾[õÊÞ¾[]âõÄÙw#Ü’á¾1ߎg}è~L¤íWÌú ÙUÀíƒ4§QÖ{˜÷A²«€ÛÉf=ñú b¿"ë±Óá¾"ôøFr}éèKÇÁqDpÇëñ¼…xß…øå ñ~ ñ+âýâW.Äÿ¸°cpÇGã8-ÌyÆyM•ä Áñ¶°oɈãtËM8¯©B X…óš*nÂyMEp©p^S…]ô ç5UTV^Çsàù?¯•J¶qú¾¨ÀÖŒâùøåm¬ ÇÈÇû•^’XÍǯ(#ZÂ+‰Õ|¼á%‰Õ|üÊ2Jk% "‚ãz&Vóñe”Y=dzä¸#l8&˜;Õ†c±¹sL0›¤sL0 Œ·±sL0›¤sÜaæocç¸ÃÜ’Véw„¿­8žÇå©ê!Ëqòe—a¦ÎÛÄq™ãs\o½_™ãrLz\渓—9ï¯EYèq™#8nÒã2Gpœ \ËËñ„Ÿ³Ãã¸+ðû2ž™LK=@3¬gzÜý±£ÇÆ=vüñ@Áý±Ñãꌹ?èqøãÃqhǃÁ8nîb?Ž?eøý±Ã±ñÇÐvÂñÇÄqèÐãsâXù L}…õÇıò0¾S_Aþxä+Xâø ëG¾‚-ÿ_a’™¯|ŽzŒ_1áX­óà$WZçáÊë<“Ìôxç:/8>gŽi¡Ç`žî»yþ˜ï¹‘B—Ýw3W—Üw«ÎxºïÖýñ®}·ðgÆ1ï~ ÑŒç Ž'}¶_!Û ²’ ¸têƒdWy„ö+f}Xç3ÇÑ—Ž¾tpGÇÁqp¼Güª…xþB¬ý ñ«âƒâƒâ×,ć,Ä " Žƒãàx_Ž‘ ]n™ nºÃ.ž¿ò$'çuÛhɲ„óš*6ä8(K8¯©`HŽSAŒ\⼦»¨HŽSAeÈIzlŽQáí·³e§Ï‡Âøù¼´q¼ûIÎÄj>>°5K«ùøÕe„I²#TF´äƒX’XÍÇ—&ÉzŒ|üš2JÑô•Ù´Ì©n×ʇ•9\†¥:oz\æ\c²ŽËœ×¢Ì+QæŽuå7”9‚ã\à—9–cVYyÚså½=W>Éý9eŽå¸Ú l—9–ã~u¶zìrLäV–Ç­¢õFÁ}Ň1ž›FWÜõX^¹´+²=&¼S1ÕãêŒôøi zÌžö\yoÏ•Orê1å=>kŽI—‰ãVQÿZ8Éžõ¸s !Ãè•Ër !Ì>ÇF¯Àñ_ÑzƱyÚså½=W>ÉÝḠ4UŽ›@Sr3}Å„cå+`|˜¯˜øcå+à$Wò˜ú ëG¾S_a9>ˆ¯À VÞÛså“Üg¾‚8¾Ý¾b‡Ãè1Œ¯¯ó.Íñh‡+¯óp =ÞÓW õØr¼ðIî;}Åsnª¯ µïæúc¹÷Ã1-ôÀ*ûí»Aì¹q…¾ä¾›¹ºä¾[¡Ó}7òÇ;÷Ýž¶°ïær<~oÏ•OrþXí»%Ǭ ¢û!Äqßr«û½Òö+D÷]ðÊž}˜>®ÖÁú ÙU`ÞiûÓ>deåiÏ•÷ö\ù$w·¢÷+¼>È à8úÒÑ—Žç+‚ãà88Žƒã«plã… ñâ7-Äâ#â7/ÄG-ÄG/ÄoYˆYˆ]ˆ-ÄÇ-ÄÇ/Ä',ij"8Žƒã#qœþ…¯åÂpŒ<;9NsxÂyM›@r¼UZò‘,á¼nsL¢8Þnµä£XÂyÝæH‚KóºÍéµ¢8ÞæPÆ^9¯ÛÊX…óºU(c¯œ×meŽ·9”õªàx›CK8¯©À.zé|8F> ±òì£ÂÛ9ngçù׳%‰Õ|<¡Œ0IÖcäã#ÛX°$±šßÜF`ãùø¨6Ö,I¬æã£Û(0.g\Áq=«ùø˜6¶Œ‰Õ||l[†^I¬æãEml;³ùø¸6¶ œcäããÛØ2pŽ‘OhcËГ ,ˆ\HŽKéŒ9]Œ9Fæ!ÇhcK:dzJ8ÇcwçØÇx;ÇcwçØÇx;ÇcŽwç£9nxwŽñ±šc|¬æ/Ò7¼;Ç Ü!ÇøøÝãvs &ÃgÊqYðstûE¾“8.sª›€ä¸ÞúMeN5ç˜ô¸Ìá2l9.sæ—9sŽËÎ1é1q\æŽe%q\æŽss\æXŽ?†q\æXŽYåEeŽå˜ñüqeŽå¸”|™c9&£‘8.s,Ç,yV™#8ÎÆö¹ê1çÅ+3=®,=†ÑcpŽÉw`ϵÜüq×cô@ hA¶Ôcí==þ¾s 1ÐcòÇšc¦Çä‰ã滓?sÌ/z‘ÐcòÇ®Yè1ùc0œ!õ˜ü±æØêq¹:kŽI—%Ç/4ÿƽ9ûãÎqÅšq …¯Çqs=±Ck±Ã±t>ÇPvÂ㸹‹&ÐÇÍ]4¦ 㸹 ž…?Fs€Xæ Ž›»hM¶™qÜœDhJFç¤*õMðÄ1ù ‡ãzOû xþ˜|ÅÈÃø ,ø 8‰öðü1´‹øh*|…˱òXð0¾ÆWp«LþXù _ã+ õØñXóþøùŠz £Çß=Öy0ë<,¬ó\ŽÕ: ë<8‰^çíÐã1Çjçr¬Öy.ÇjÇ9†ã+†«uöð7„c¹÷æûc¹÷Ã1°ç¾Û„ãɾ›åØî»Á®ñ̾›Ä—$Zì»I|áî»YŽí¾Ûå8v|…Ï18ƾÓ¾ÛûcÞñ°ýÈ>HÛ¯è}Z‘ÝðåÝZ¦‚…>H3=±},ôAšÁ(ë=¸}f0h¿Âéƒ@¯óœ>ô:¯UTd°Îm˜ýŠ¬Ç¢ w¿B¶Aë<Û9ßýŠèKG_:ž¯Žƒãà88ŽñO\ˆßº¿m!>i!~ûBüŽ…øä…ø ñ) ñâ…øÔ…ø´…øô…ø] ñ»â™ ÇÁñ‘8N«Sñ:àyêUN\pÓ-J 9N7ἦ %`Îkª¸ ç5UÁ¥ÂyMvÑ+œ×TQYy弦ŠÊÎk*¨¬¼r^SÅdšãtÇd)§;:Ë çuûö‹V’§‚F8—·cä#ã«ÎÎ1*¼cÏåL¬æC`\ÏVO¬æã“Ê/I¬æã·—-á•Äj>~Gá%‰Õ||r%ȵ’‘ Áq=«ùø”2ʬž‰Õ|¼¸Œ2CÓcäãSË(³z&fóñieÔYåùøô2ê¬rŒ|ü®2ª¬$X0rÇÈgB5Ï,£¹”ΘcÐŘcèÄámÄoÓÌ&éÌãmìÌ&éw˜9ÆÛØ9î0·¤U:ÇáÆo«tŽ;Âa¼XsÜnü¶Jç¸#<æ¸#<æ¸#<æXbìsÜa>[ŽKW£Û/ê­ßZæTÁ1éq™ÓL\ŽË.Öã2‡ë±å¸Ìqõ˜8.s„· q\æ=ÎÎq™cõ˜]¼¸Ì±zübÆq™cõ˜U>­Ì±z\íÅvëÓË«ÇÕ^`ã¸Ì±zÌ’ß]æŽs±ýÌ2GpÜ*­v®zü‰FÁô¸²Ì9&,õ‚ãæ ØOb<ô˜ü1š?¶zlý±Õcåáé1ùãʲ§ÇÌkŽ™ð’?¦—æIx»?†tÌÌWtW áš¹¯è®˜¿@ø æŠÇL9ÐàôçÌ1éò.Ž}=žq\M²ãg“C Lý±Ã±ôÇ>ÇÙç˜1hÅ1¹bè*ÓŽ?&žøch‡ã¦ÇB þ¸ tMÇ\rËM©g7]¾9¾ÌW`앯€ñ`¾bâÉW( ÏWLü1´‹ød*Y_aðu|…òÇ®¯Pþ˜Uº¯Pþ˜Uº¯Pþ˜|˜¯Pþ˜sL¾Bù㉯«¾¾â¬õØrŒ=¶ë<8¾Bëñ%Öy¸ò:c=žpˆYç9}î+²;}½_áõA Çb›î~…ÓÉ®âÌ9޾tô¥ãùŠà88Žƒãàø°ñ ñ?-Äÿ¼¿g!~ïBü/ ñûâ÷/Äg.Äÿº/YˆÏZˆÿm!>{!>g!]ˆà88ŽÄqZŠ×šhf‘ ^åÌ7Ýበîöå-qœnñdNpš3ãy†§{ìÕ'7Ýk¯qœn±WŸÜto'ÇiNϧ9,ἦ»è%Îëö_¡ÎÎk*¨¤dçÃ1ò‘©UgK2¹ƒxnzŒ|ŒëÙ’Äj>Æ<ÁÆ1ò!0®gK«ùð1.gŽë™X͇ÄXž‰Õ|HŒÙ™õùË31›‰ñKDmãô³ 1þ,VÛ8F>$Æõ¬ID.$Ç¥”PÍÇç´QbœÏ„j>-#DR³3æí¢%ljË1ÚØ‡cÈÇhcKf£Ž3ŽQ“ÇU“§ã3ws\5ùš8“áÇƆc"·#|n—:qüŒcÒã2‡L„Ëq™SMàr\æ47—ã2§špî—9ÕDÈ„8.sª‰è‰à¸Ì©&¢Ù‰ßÏ^ÇeN3 Ž?“q\æTÑì„äù%eN5ÕN Û‹­òYeN5ÕN Û l—9ÕCT;tòeŽÐã\`lN™#8Œ2>Zæ<ªE™Á|¶z,8& n" 9nþXê1ô8_  ­ƒù㑃%#=9ã±ÃãXé1ÇÍK=ç˜ü±Ôc¼ä%JŸaôŒãJ·Ñcxw_ñÙŒçÏç˜é±ðÇL£=æFùL9&]¶z,9–z\ë—åô˜c£ÇWàø÷Í9î=æ˜ t©8K]†Ëq×å1Ç] ¾L—[âøcºjJÍ8–î]—=Žé¬J}|ùcòøc__Áðú xþXù °dä+\Ž•¯À‚¯€ñ0¾‚ë1ùcå+`|Œ¯0zìø L}EÇWúcë+Ð|¦¾Bùã›á+ì:oÂ1‹=6ø:zlðuôZ†=Æ‚cÁWÀñZa|…]çÁø =†ï+„cÁWL8Vë<¹{1ô7‰ãɾùc¹÷f9¦…ž¿ïV5™ql÷ݪ&O÷ݪ&³Äî»I[ wß­j2ãØî» +1ãXè±õU“5Ç0cî«&Ïý1äZo7Çp9í»gºbû m¿¢÷A`ú ÙU`ÞÉ®lãÂéƒdWy$› ðÄöAÚ6EOl$» Á±íƒdWÁ9nNY6?0Xçñæ^b|EÖcÞü€ÐcÚ¯Í ôØöAÌ~ÅçØmŠü*8žõAΗãèKG_:ž¯Žƒãà88Ž/]ˆÏ]ˆ—-Äÿ¾Ÿ·ÿÇBü…øƒ ñ‡âóâ â â/ÄYˆ/Zˆg,DpÇGâ8­NÅë–ÀrŒ<õ*'šYä!ÅËjâpŒ<õ*'šYÈ+Àá"IsŽQFðWÍ,Êz…åeÕÌ¢ŒÀ„c”`Í,Ê|aO†ð¢Œ€ä84¹ÄyÝþK[ÒK’ãT ¬t>#™Zu¶z&’cÏåÌäBr u‘É…äÄs93¹pŒrf`1พ™\(Ž!ÏL.Ç`gÖc4x;Çg&Šc|¾¨er¡8Æ°ÚÆ1¼cÈ$‹ÇÈgB5¤Œ äRJ¨æã‹ÚX°,¡šg”-y+1ÇhxécèÄáX`¼ÇÐÉŒcÔqÆ1j2ã¸jò”cüÁÝWM>cŽ;ÌcŽƬÆ9&˜ñŒså¸tÕ_Êä×r\æTŸã2‡Ü„Ëq™SM„ÇeN5(\+Äq™SMŠׄ8.sª‰@çø0ŽË2èæ¢s\æTÎ1ãùóËœj&Ð9n•”A™SÍ:Ǭò…eNõèø2—ñ‡Ëœê!zÒ9N¾¢ÌzL•Jõ•9œc¥ÇÉW”9ŽÏ×WHŽ›?3Ÿ‹¹W–·¤’mõ¸²Ìü±ÕãÊ2c«Ç•e=®,{“ðV–Ç̓‰ð‚à˜ü1˜>ÇU¡»W–Ç­Òõ¸²ìq¬ô¸]sÜ}…ôÇL£¹[\9öô˜üñ¹rLºÜ9®—ŠãÏ]à8_A ñ¯Ç’ãÏ[à˜Ëðˆã|Eísœ¯º@—Šâ8_–yÚ£ù èÇùŠÚø 4r…@cÂqNª@ý1º.3ŽÁµu݇Ç ¤ð|s|˜¯˜pL¾/Sø:¾c í">JÖWL8&_é]_éY¥û HÌ*ÝW@úcò`¾ÒsŽÉW@ú㉯à[_Á`VÃú <ñ¸i¾âjzü²ÝzíWLú b¿¢Ñ¬ú f¿Â郘ý §bö+šSæ-³_aû þ~EÖã¾å6ܯ`[nÃý fÛ6…Ù¯pú ÙUŽEd°Î³}œí:/úÒÑ—Žç+‚ãà88ŽƒãcÅ“â.Ä[ˆ—/Ä_ˆw^ˆ?±÷âÿ\ˆ‹…xl!þäB|ñB|ÉB|éB<}!‚ãà88>Çiq*^k"€M¥ö VÀ¦’x­‰6•^î°©Ôó^À¦’LÊ«6•d‚œ`SE&åU›J*±§[*ÙrÉqºå%šYÈ$ÍÙÉqš#9N•”Œóºý4dò¥9ἦ‚JJv>#™Zu¶$±šL-;y!±šL­:[’XÍG¦ö唊B]pŽë™XÍG¦¶ž”Ô3±šLm=)AÓcä#S[OJꙘÍG¦·ž=i#…ØšRÒ8F>2´õÔI#Ž‘Ï„j>ç3¡š/)#DR³„j>¾´<ÉÇÈÇÓË‘ÔìŒ9F»h‰Ã±ÀxŽÑ.Z2ãuœqŒšÌ8®š<åb7ÇU“o8ÇDnGØpì`l8&r;ÂçÆqéªÇOö8.s·Çeq¬u9s\æ0n_þòV!ŽËœn%Š›€à¸Ì!ŽyB—9Ä1Oˆã2‡8IÓã2‡8I½uQæÇ"ÉwÇeqì%²Ì©…Ü–Tª¿¸Ì©B%ð/)s&zœ|E™3á8ùŠ2gÂñùúŠ'/èq~©dô˜ûã‘s1éqwÆc=v9Vz,9nþXê±Jª?–z,“r¡õ˜'E›­»“W–YB+=†H*Ç]\9fzL@w£\×y»ôø9&]žpLº\Gc©Ëp9îº<æ˜tc_AºÜÃ1é2K ÍcÒe)Г.w6“.÷Äø Òe“(Ž»Ë@Ñeìà¸éòÌ7¥f7wÁ’ÂóŒã¦Ô·ÂW`ê+¬?ù L}…åø ¾S_Aþx§¯ÀÔWÇ{ù ËñÈW€'ÆWŒü±õrÕw‡|ÅP'[_U_ñÇê+´O8žø ¬úŠ‹Kù ŸcÒcrÄûŠ Çjçr|#}½ìÜwãöÝþØÂ¾ÛËÉeŒ÷ݸ?í»‰d°ï¶/Ç£}7Ã1®ËWMf‰ãÇë¼½8í»gºâôAÐÛ!Ã>ˆ^çy}î+È3«>ˆÞ¯ðú °‰éƒhŽ›Smá:;aÇWd=fmÑ~oƒ 9î[nÃý ÑóÀƒ'Äñ¬ÝÏk£'Äñ¬r¾G_:úÒñ|EpÇÁqp±Bÿ—-Äÿµj!¾|!þôBü™…øŠ…øÊ…xöBL~ÁqpGXŽO{ `S©çµÅqªô¼°©ÔóÇiNÏ{"€MszÞ+šY¨K`ÀqºÃ3Îk*PVâ¼n·ZÒKÁññ9Þý´g]pŽÑô•ÙòÎq=3°èèÂåÄ,$¾Œc4fó Ç¥’Éňcä3¡šñW´RɪùøÊ2¢%_ÉJ Õ|<»5Ë‚ãëã¸ugãËvsŒšÜŽ;†c‚_9äX`ÌjÁñÑ9^xÚóËÊœn%š›à—9ÝJ7¡ÇeŽ@7P“ôúåeŽ@7Г?]æ0+‘Í:ÇÉW”9ÕC Û*•ê¯(sª‡¬'_Qæp=V'_QæL8_qj=öžöÔzÜñ–4,õ¸;c0aÖzÜ^€žh=&%æ“W–…0WŽ•CøãÊ1S`â™üqå˜é±:8¾ŽÇO{vŽI—IœÛeç˜tcLº,ÅYpLºLâ ã+H—Iœ[Eqüg8ÇM—=Žé¬íøcRç¦ÔŒãF.qÜ”:8>2Ç O{:¾ÒÃó0þØúŠ~Qï9¾Ârl}¤?æ¾¢s̬†ë+`ü1÷Êcì+z|z<áxà+Ç_1[çqޱÃWøz<áØê1ƾ‚ðµÖyð}Ep|^c²ï&ýñœcLöݸ?nÉý13Ê.Ç€·Îs|ÅÇjß-8>>Ç»Ÿömµ_Ñ,³hƒèý §âîWd=þrÓú0û¼ 2Ú¯=¾_!õ˜w?0XçÙ>Ì:O´AT$8>5ÇÑ—Ž¾tpÇÁqp¼o|ÕB|õB¼b!þìBœÙ+Ï:¿r!^µ_³_»_·n!þüB^]F´äÕ‚cäãëÛÈâùø†6Ö‚cäãÛèrŒÆ,„Ÿ5Ç»?É=±šL­:[=±šL­:[’ ÍGÆWcä£+ÏÎ1ò‘¡UgË2¹C]%Tó!0~^+•ì´ÌcÁñcƱÀX€Ü9Æ7îæXb|Ã86ŸðçpŒvorŒvчcÐŘcÐŘãŽð˜cCµÃqGø”—n8×cp£ã2Çç¸Þú†2G€KWõÖ7–9‚ãViÉ_(s¸¹€šà 9^ø$÷o*s¾‰É¯Ôäã2‡8þ‹ž—9]Òãzë/—9]Òã|'q\ætt'_Qæ›°'_QæTXŽOî+^MÈxÇWt¶‚¬Ç¼ãaû!P}ñ:¯oNÔý òÅcÞý@£Ï»FŽ£/}uŽ£/ÏWÇÁqpߎ_³¯]ˆo^ˆoYˆ¿¶}!Îì?ºc!þæB|ëB|ÛB|ûBüª…8ø38ŽïÇÛ:³_ŽÓJ0àx»eÅñv«%ßÂÎë6G\ œ×mN¿¨•sç8ýÛ+XA›Jí•'ØTi¯`l*µWŒ8N·Ú+zQrœnµW°ŠfyÚå–†cäC`¬®´ùxmÑ©ÇÈÇ7·Q'Ø8F>¾¥5K«ùøkm—3‹N®à¸žçÇ1ò‘©e'/$Vó‘©e'²#™ZvòB‚6_vJ=F> ±ýü6Á1ò‘¡e'¯dr!9ngË®‰cCuç˜`Æk‡ûocçX`,ðîûocçXbÌñ¾ Œ·ÑáXcìq,0ÞF‡c|ënŽ%Æ>Çøö݃ óé8.=ðæ& 8Î…Äq™ÃõÜhl·¾¹Ìq9&=.s8ÇVËÁq«—9‚ã\8g=.gÆ­Ãq™Ã¹µ—9ŒÛÎ1éq™C 9®·¾­Ì!Ž…ç;‰ã2‡¨ývÃqòeNõp8¾_‘¯8оâµÄ³ðÇzL@Üõxì»  î»s qÖþ`4<ÒãüRÉùch€aý14ÀþFóK%…e£ÇùE‘ݸ²|Fƒ€úcH“,8nî‚ëÁ%xà!Ý< (;qü1ér=ŽI—ëØ“Î1ér=Lº\GcÒå:6®9ǤËuìűÐã¦Ô×á+¾còÊ£ù _'Ѿž?V¾ÍW°åŸöp’ã+°à+Г¡¯óÇ#_†6ƾ‚s<òð|þøÔzlðuôØr ÇW 9Vë<,¬óà$zw#8ö|ÅP'{¾BsìùŠoå ¾¯êñ„ã‰_#ÇVáû Á1æ;¾â[|…À¾¯øâí»qŽGûnûr<Úw3cc\ŠcØ}·“÷AêîD߸ ý §Òw0¶W¼VµA ÛÁB¤oSl¯pû Í`ôýŠÐAo‡ û èípË,Ú ½2ìƒ@nSÔ~¾Uî°õm ðýŠo×»Ì)Ç}Ë­îW°>¢/}éèKÇÁqpÇgÎñã ñ ñº…ø[ ñ·âï,Äw.Ä ý+Ï:êmŸ¼/]ˆÏXˆO\ˆ.ÄSâ¢Ç·’cä!«¸àns§;”€U8¯Û-/á¼n•–ð çu«H‚K…óºUúE¯¬<í¹òÞž‡ú$wlúsÄkM°©$^·ŠãT¯5À¦’xõ8N÷Äk¹§{âµf§áÞÎ1Ù Ú|Œ#¯+#ZÂ+‰Õ|ü­6:Ib5»5«$VóñwÚ(0nzŒÊìwBˆ0zeåiÏ•÷ö<Ô'¹'Vó‘©UgK«ùÈÔª³Õ«ùÈÔª³% Ú|d|ÕÙ9F> ±òì#Zu¶ìš8Æã»9Æw 9&˜ÆÛØ9bÌ83¼9ÇCŒÇãŽ7çxü´çÊ{{ê“ÜŽÑ.ZâpŒv—9F»h‰Ã1èbÌ1èbÌqGø”—n8¹ Áq­|G™ÓÜǵòº2‡ë1^§õ¸Ì™s\æpŽI‰ã2Gp¬+ßYæŽss¼ð´çÊ{{ê“ÜŸ\þâøÉÇeÎK™üZŽËâø3ǤÇeG·_Ô[/,s8ºý"ßI—9]Ãñ©}EeyK*Ù­¢õ8_  î+^G<Üõ˜€V ×cíAþ¸ë±ðÇè®Çh WVžö\yoÏC}’»Öã'/è±ä¸ùc(€Çä¡Öz £Çœc¯lôøL8ÎWÃçÂ]ÀóÇFÇÍ]˜„q ­Å­Â8–î½Â8†°è••§=WÞÛóPŸäÞ%]žpLºÜ9®—cÒe«ÇÄ1éò„cÒeɱð¤ËÄq«œÖWàq…oçXù ¢½Ò}…òÇ,ѾN¢}œDû 4_Á–ÚWÀIVžö\yoÏC}’ûa|¦¾‚üñN_©¯ ŽÏÓWø“£û • ×y¯ó\ŽÕ:ÏåX­óà$zçr¼ð´çÊ{{ê“ܯî+´O8žø ¬úŠž­¯€á†cŽáûŠ×}Å>«uÞ˜c,pŒŽáûŠá{{ê“ÜŽwî»éý·Ç;÷ÝôþÛ'ÂåXî½ùþXí»¼Òö+×;ºû¶qÑ*^ã>H3&QmÙéƒ4ƒQVwèÕ߯h••§=WÞÛóPŸä.Ú ºâôAàôCtºâôAàôCõ˜w:ǨðvŽaôùóÇÈÇw•-ù.–$Vóñ÷Ê“d=F>þ~%ÈhzŒÊl!8FÓcTfóÁq=3° rásŒÆ,8½’cäãm ŽÏc¼~7Ç Ç³Æ˜qL0«„s Ž®È;Çæ–à»5Çæ–´Jç_—ch|ŽÇÇç¸tÌÉMŽë­¿[æ47Î1éq™ÃõØr\æ¸zL—9B[…8.s„çç¸Ìzœ \ˡǹ€š¤×ï+s„çzòŠ2GpÜ*ÅhǧÓãÊò– £ÇùÊøã®ÀÄs%ä»ðj òÇä+¤?Îåæ;°ŒçïF.7LÂû=ŒçïðÇä+¾—ñü½`ÂÌ|Å÷1ž¿è ×c᩟˜ã|Å.‡ƒiñÀ“:·dæÍC¼•«L;þ˜®ÖŽ?Æ‚?6ÃñÇtë+¤»`¶9à;2Çä+ ý1š¯€ñ趸ûcòʳ¤ûŠ‘?ó æZj•î+ m1Kº¯€¶ÅÝ“¯ |Õå^÷ßαòh¾"8¾V=ÖIÖà»{Ç9޹΃·Î3‰³Î³ÛuÖy&qÖy3=Ö2_Ç0Ãp,ôxæ+ÆÖy®¯¨k=cé+àr,}|Г‘¯ùce‹ÃŸŒc±;Ñí«èîØÆÅ¬Òw0ú ÷A²«@߸ðú m¿â»1îƒÐ~Ť"ö+²Û>ˆÙ¯xÅp›"ö+NÏqô¥£/ÇÁqpG\‰coXˆ°ÿp!þÑB|ÿBüß ñâŸ,Ä,ÄW/DàÇÁñ¾#)ÞP“ÇÛ–¸à¦;”`ÇÛœ ÇÈCŠ ÇÈ0ã £'œ×ô';Cp¼ý [ªÁñÑ9F…·s u‘X͇ÀX%‰Õ|ŒëÙê‰Õ|ø7=FeÖáMQ™u8®g»8cvÄ1òñe„Ï1òñÕm¬ ‚ãëäX`¼Çcc´ÿp7ÇøG»9Æ÷ïæ59<Çæ1Çcq8™ãòh¹ AuÓã2§™¸—9ÍD€ë1q\æ4!â¸Ìi&¢» Îq™SMDO„—9™\ždŽk埔9XždŽkòeŽÐã^Ù^ÇeŽRà†öv'8>W–·Äøc©ÇùŠ€nþXê1˜?ùc©Ç` ÈK=9ã-iþXê1Ès,õ¸]½¢õ¸]%ÌWHL2-õXûãÊrp|ZŽó¸Q†Ï1TâqLg¥yÊqQç!Ç9)D9žùcºZà˜'CÌÊŒcp-3ã‘9&_éY¢}QÍí+`ü1Œ¯KPÖ¾Bp\í+8Ç䕯׊ö.Çä+̵D+_' O¯Ç†cÖàÛ9&` ¾cÒct;¡æ$¬¿Öcƒ¯³ÎƒÑc=Æ‚C˰§ÇÁñyp αõU“ws ˜užá˜dcŽXŽ!ü±ñd‹M¢8Æ ¹Öóý±ë+Ÿ€ã¾;Q÷+Þ€q$» °‹–ÈîÄ:¯í`è¦t"û m›B&²Ò·)Zbû b›‚'|§MmSôý §R·)ú~…lƒÈ>HìWœšãèKG_:8Žƒãà88ޏÇ6~p!þéBüÐBüðBü? ñÿ.Ä?[ˆ¾ÿb!¾j!õkþ— ñÆ…xÓBüÈBü«…xl!‚ãà88¶#)~°'šYä¨W9ÓÌB^¥°C$ÛË1D’¦8Ã$–c8ÉnŠ!8Nß]%ùBp¼ýWÈ$_ãÒŽAMG§[íµf–ãt§¾ ¦–ãt£½ÖÌpœoµ×œy£½<¹:ǨðvŽ! Þ8F…·s â¹é1¼p ÎÀbÀ1šƒÁ«8®g»8cvÄ1ò1çùøe„Hˆcäã«ÚÈ“£q\if§Ã1ØÙFÍ1Ø9Òc°³Šc°‘'’c¢·!]“#p¬1ö8†Læã‡vsŒÞÍ1jr=ÌjÃñããq܆Ç1á;ámœpLø.s ßW ê1-¹2Çå2.Çe™hMÞ8.sÈD &à—9d"Àâ¸Ì©&E‚)©z\ædr·Wè$½þ³2'»½ÂIþy™“u“ã2Ç×ã|'q\æøç;‡ä8› 0Ž=Î&Bj±ÄøÅF-Æ›¤­xSqB‹޳‡€âÒos*µÇâ˜È­,oI%äÁM¸?ùã¦Ç•e&Æ Ü€­,‹¤ùã&¼•e–tÜô¸²Ì’&Ì]+ËN¢õ,©,s_aüqeYè1ÍÉÆ9_UþL‹ábüÆ¢¾þøMÒ]^Áýq8ÇhØ–Í*sŽÁŒñi8ÎWP‰âXêq­+ŽHp\ëŠã.Ã(,o‰â˜Û Ç£Ù hƒ¡8î.ƒ%;9Þáa}Es=9´¯p86zÜ9fzŒ78–ÞØçáªÐ†cfëOÁ1ù HÌ’î+0ðÇ`¾¢sÜJ­Ò}…ÃqMº¯°“?&_áp\ïu_1áXù °„8&_aý1q¬|œä(ÃŒŠcf‹ñÆÇøº+;ás<^ç]“Ž­[Ž­›dªÇ?¼[í:ÏrŒKé1´ ;z<áX­óŽÊñ¿Dw#ŽßØ6ÙðÆ7j“,ýñœã·>ÇÿJî±ùû ]¶“úã9ǰëˆìçQÓƒ„YµAªã±úZ¤ùà}èKߨ¾´qN_ÎÂÎ×cLûÒf—ÍéKÃî³™¾4D'/ž¯Žƒãàø–<'(á<'¤Ÿ¦ðžÒ.Â{NH?Má='¤]„÷œØBï8_o¬Ðÿ£ ñÿ-Ä›âÇâÕ q¨ŸÏ/ÄO,Ä[â'â­ ñ¯bòŸÇÁñ™pì=í©™…¼ޱÀ1Þl+.¸é[° ÁqºE zõו~Ë\ŽëœšŽ8Þ¦´W 8ÞnÉDs\¿WM}Žëœvu«9Þý´g&ŽQÎL.vq ¯Ï1ò!0þ1^Ú8F>^]F´äÕÇåX&p9|Ž!^ár “ ôìé1Ôw”cüÐnŽQÇ#r 6*Ž f‰ñõs …õOXŒg1þɯ䯷[žöüÑ2§š ¦JÕã2'#¼½¢&TIɛ˜ ìöŠšðÊ•9ÕMÔ¤aœ ‰ã2‡ë1¸ÑÀA9Î&¸ÜNH_‘Í6pÉV=Î&\‰ñ[ª‰Àœãl&0ç8› i'pW|ÅøiϮǕe!ÆÍ7=®äaÓãJ.æVÑz ækÎ}Å«‰çJv¹: ÇhÔŒ_ˆu^'—2ãѨÅ[˜-Pj‘Ïj‹aü18´ì‚s î† ã»Ä±û´§âøG¡¼±ôÇÐøŽ8~³áøÍ>ÇÙõǤÎ8´¯€ÄcƱôÆð9†k*$Ç-£äŠãÖ¯óžöì¾¢sÜJäÉWtŽ{©$ÝWtŽ[‰8V¾ÌÇä+”?>Çøñ][Ìô˜Æ[F7SŸœqŒ·îæÎÆÅÝÐc“8zlGMâè1Æzlðuôøÿ¸ÜdƒÅ¸ùã9Ç}·í-’hãç¿}—mÄñ¿ö7Ûî˜?6O{:þ¸eßWNâøc“(Ža||_!8>¸?ަâlSø¾Bp ÓQË»±¿õ­Ú]ˆ}·»·›ãÝO{ʦG߯뼾åÆ÷+xÂ7Øø~Od÷bGûN¤ï`c¿?ÎÚ½!"ôÔù ·Ýþ¨ŽÖc´î‡L„+ 6}n"î ÇÑ—ë1¦}iã"œ¾´·°óõÓ¾´qN_Ú"ÏWÇÁqp| žÒOSxÏ ÊF8Ï é§)¼ç„´‹ðž”pžR›'áø…ø©…8ÔonåYç³ÿÿBüÛ…xÍBê¿ým ñÓ ñ3 ñïâí ñïâ?,DpÇ’ãô¯J,ÇéÖI9öÞÛS3‹<õ*'šYä˜qŒ<¼â‚»ý5úÅ18Îÿ4·×œŽë­’8®sXb9®·j:áyØbÌ1ʘÂå¸~¯š–cäã2B$Ä1òñSeÄq9ÞýÞž™\HŽA<—3“ Éq;;ǨðvŽÛÙ9F>Æêêðƒ<±zŒ‚ôT‘¹ƒ>Ç_t¢=Ž ¾ê1D†£pL0wª ÇærlÞÛÓá:q8Fæ!ÇèÂ<äØP}lŽ1ö㟞rŒŸÙÍqGŽñöÝ##Œ¿›c(¬¯ÎqQz_ë­Ÿ*s„÷JIÆñÂ{{þ›2§šøz\æT7©ÇõÖ¿-sª›€ÔãZyM™ÓÜǹpH޳‡0øJ޳™€ÀØpœ=:Æi¶âgŠŸhÔBÁÜ8Î&]ƒAgÑtxÀq6ØÀ­¶âðwøcp_!ý1zrx=¿·gÞʲë›Wr?ÓãJ.óÇ­¢õ8_q q@Žáª0Ä:Ì7{¬9ÆÏt .ø‚—2Ç`âÛ|±öÇè¶˜á ±ÎëNtj r-;&ÇŒ±?>>Çî{{*Žwøc ü±áXè1¾™Ü×°ò‘8ÆÛvsLÙ„cž8ùc‰0Œß..‰6Éc2GôŽ?®÷º¯`0S©$‡÷ã÷Dì¾cL¾Ö×{ÝWÀøcâXù †ïQ9`|LŽ­?îƒö)ðöÇ%hÏâÄOÖy0zL›äˆzl9¶z¸?žbü¶Ž,vèq,‘zl×xÖWŽaú 0ûÇ=–Û‡ßw›ôA²«€ÛÉf=9Ç»ßÛ“·@Ú~ŤBû´Î«ÞiûÔiû¯Ñ»}ãâ8ûªý¡9þiê~´„7D˜ƒ!"z j„¸ë<ÙõPƒë1D¦»U™Þ‰¾ôÙô¥á)°«Ç˜ö¥ú:}iã"œ¾4ÞîY O1íKÏWÇÁqp|îÏ ©‡)Üç„”‰pŸÒ.Â{NP6ÂyNH?Má='¤]„÷œ lÄ9þ ñ³ ñŸâPô¯¼³íâõ ñøBŠZ,ÄÏ-Ä;â?/ÄÏ/Ä/,ÄYˆ™°ÇÁñæ8ý¼F§[nÂyMEp©êiÏ•Or×Ì")ÞP‡cä!Å„cä!«\Çc°;­uÎ.ŽaË1ÊXbÀ1êË>ÇùV{ÍÙÁ8F>ÆÿQ–«ùøÙ2ÂK«ùøOe” ×Ê¡žö\ù$÷L.$ÇP™\HŽaôÞÎ1ÙשÇXÐã]:±Wv1Óã /Ç`#OŽÂ1ب8&˜MÒ9î0sŒ·ñPO{®|’»Ã±ÀxŽñúÝãñóá ¾;|š c¬ÇøùÝãöä‡ôåÇSÝDMƵò³eÎ\ˡǺr¨§=W>Éý e¹ AuÓã2‡Ü„à¸Þz¼Ì!7!8®•ÓrœGL0þ¹â'ˆßÇÛœ \4;ë+²‡ÀÏ×£ð,lÅϱQ‹_sœ=„ƇõÇRaü1¸¯˜øcòÒƒûãÃ<í¹òIî]+Ë[büq#·²¼%ƒéqeyK*Ù­rRŽÁø¬û ¹SŒßQÅ܃I0˜½àþÜ÷‘äB¬Á82ÇÒãjþ þøj»Ÿä®8ÎWàF†ã|Å.=Žó„Á¸FŽGþøx›Üá˜Úå˜Ã‹ãù p\+ÝWLü1ù s-‘?>ÐÓž+ŸäÞ}¤?fI÷þÍW€ù <®ð½ã'çØø 8k¼£­ó_“\rg’C=í¹òI=Ö‰§Ç×Êñϩݷ±?žs\üñŽ}7f†GÿÛdqü_hM`|DŽí-pi_QñÔW\êiÏ•OrwýñÜWÀpŒ?æ_‹?îõxŠñ;²S=–ûǾ[vÅþ±ÝhÃá9æÝÈu^­\¶’]æ}Ë<í¹òIî¼R÷+Þ€q¤íW¼^ï`𠶺_ñ¸ÞÁ8½ƒ!ãý Ì9ÎzŒÿ\_·Œ©Çí¸ûhÝÊd?϶?T$úÒw­/Ɇ›ÔcLûÒpX÷¥{ÆbÿØì²9}iÏSÄóÁqpßøç„v?.Ä}Å;vúŠÙsBÚExÏ é§)¼ç„´‹8<Ç6þëBü·…øÅ…XyÖùÁˆ»ÁqDp,#ý³À“9ÁiÎ%8Æà½=ã_ŽcäC`\Ï–$Vóác\Î_üEäcÎñî÷öŒ_dp| ŽÑÆ–Ì8F÷ãxüÞžñ‹ Ž/ÅqYW¸z\æT¡“ªÇeΆë0YyoÏøEÇ—âXé1\,õ"iþXê1œdå½=ãŠã]þÊ{þxÇÃ÷öŒ_dp|)Ž•¯€ç•¯KÈ+_'YyoÏøEǗ☀5ø:ë¼F­Mº£É°IVÞÛ3~‘Áñ8ÆÜWTMf‰õU“då½=ã_ŽcÞýÀ`Ç›øoà‰íƒd'YyoÏøEÇWä8úÒÁqpÇWâøxô¯<ë¿¶ˆà8"8>Çj—cå½=ã×q½ï~Úså½=ã×qf›.àÊ{{Ư-âZ9^xÚså½=ã×q­/<í¹òÞžñk‹87Žwùcï½=ã×q­/<í¹òÞžñk‹¸VŽžö\yoÏøµEœǘûŠñ{{Ư-âz9Þý´çÊ{{Ư-âL8޾tDpGÇÁqDDpGÇÁqDDpGÇÁqDDpGÇÁqDDpq¹øï+Æê¦Å¼ëIEND®B`‚urwid-1.3.1/docs/examples/graph.py.xdotool0000664000175000017500000000013312615524560020123 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 79 24 key --window $RXVTWINDOWID Down Down Down Return urwid-1.3.1/docs/examples/edit1.png0000664000175000017500000001601212615524560016500 0ustar ianian00000000000000‰PNG  IHDR_,SÝ@ܸPLTEåå娨¨KKKÓÓÓÒÒÒÚÚÚÊÊÊááá“““ØØØ###GGG ÕÕÕ999¯¯¯***¶¶¶äääµµµ–––gggÌÌÌ<<<ÅÅʼn‰‰LLL"""•••ƒƒƒŸŸŸ~~~'''ÐÐÐ000YYY```âââ???@@@RRREEE!!!ÀÀÀÄÄÄuuu˜˜˜333ŠŠŠ××פ¤¤yyy666¼¼¼³³³ÁÁÁ^^^&&&...,,,›››ÈÈÈ‹‹‹CCCAAA€€€BBB222àààPPPfffnnn´´´ ¹¹¹___ËËËÜÜÜ­­­888¬¬¬pppMMMÇÇÇ555’’’ˆˆˆ‘‘‘444qqqUUUjjjaaa°°°ãããÉÉÉÞÞÞ£££¡¡¡ßßßššš¥¥¥ººº‚‚‚vvvddd»»»[[[%%%rrrªªªÑÑÑ………777ÏÏϽ½½QQQ>>>¸¸¸¾¾¾eeeÆÆÆŒŒŒHHHœœœiiihhhÃÃà  (((®®®]]]---TTT ZZZ;;;111JJJccckkkDDD———NNNÝÝÝÖÖÖžžžÙÙÙxxx±±±¿¿¿”””III sss///ÛÛÛ===VVVzzz+++OOOXXX $$$§§§:::{{{ ‡‡‡ttt¢¢¢™™™„„„ÎÎΆ††©©©ŽŽŽÔÔÔ···²²²}}}mmm)))WWW«««wwwÍÍÍlll\\\ooo¦¦¦|||bbbFFFSSSÍÍÍÿÿåúVÑ pHYs  ÒÝ~üøIDATxÚí‹CÕþÀÏ—‡¸‚¸ j@ô" ÄGh X"ä#ÊWùHñA‰¢]P¤€Y¦‰(”‰¤–aÚu+óQ¦¥æ£òQv5³ºÝt)«{+î¿ñ›ÇÎÌ™sÎÌÎ.»¼ü~ËÝÙ³ßs¾ç|æ;ß3ûåÌ !œJðDÅ.¿F¨%Ò¿ÈÊfD”¡G½ªÒS}‹ˆÔK\\ë½¢b¤÷Þ±}ô2wÛàÚ–$¾/¯Â•¦Äì ý¤—¾áЀ¹îM‰àN²nÉÊGŽ÷F"ä›<àæ”·Ü:à¶nWuîH½“á+›LÀòxWzÆ LYÒÂÕ’ÁC¾ÑC‡  ùÞÝW—Þ´ «&¾†ub ±ÝOYÃGdÞ“‘?23S)s[vFLŽ€/ÓÛ¦„Pÿ(Ø"ÀrißÜÑ÷æÞ7&7677OUI›ß³  àþqã­öÏ„‰>˜šúÐÄÔT·Z5irÆ”BY¦zùN+x8摘é3fÎzt6ÅwNbbÑÐÄÄh±#ZñÕþ·áË`™;dÞüäÅ=^¼Ð[R²h1”Þ»$qé7Fë ¢üÇöZè&ÒǨ²²Aåe£—•-/+[¡Uú¿ŠÈÈÈ%OFF®4|TýU« ã*«ͬÊÕJVVg¸rWKò”^"ûoŸ˜;aF¹êç}׸㞖*<³èΚ[.ˆˆx66""vÛ}Óq,ž/ŒÏ•ë%;Ïm¨ ¿§f£RP;shï)›¢fo®»gLŽy× 0β™/{8Á>—ù¦××?ÿ–…Yý_t׫þ[¸EM®¶u… ãnYänX´M-iLK‚ ×ø—¶ïÈžœ®”,P±.» ;e§´ýòb¥è•Éîùî¢ùz›¦‡Q§úoáj$ã¿Þ‘™i³Ä_;ezbڶĦ‰‰‰wËõSÓÆŽçâ¯E %LéøÀy0áw”1¿íùÚÄÊ'÷Ü¥êÌx½òï111ϦH/Y¦]«7á*ÛU˜ïñ¼‘âyÄs“R4#;2ãÍððI†‡7*:…o­«ýË2ÞÞžQ^­è<¾zó²@ñõ²3u–嫹‡Ù©a“äÖùðd\œç`œwJ­?´cÑ¢ø1¦¾>,õ0} ˜á±þ«}fù_Âñ}glÑ“¿\ MéªÂcï®ŒŠŠÊ:"½Tл™ª]ñ^Å”£Ò¤¶Žé$d×Õí\ì©««Sç®è†åUM9‹c¼‡þû{>¸ÿUÉm[ð9£YoÜ|fÑœ™'\^¾S•ÆsñÄ·ÿêø·àËìŠoBéÉìE"rûhͺÄñ®>õ’ùá¸ÌSú ¼°è®(%Ù·ï›zr —ïâŠ×6ǦWKÔøñª½ÿǧ#"òaÿ©ÞÛÎô~ÛËwêÙ¾ â¡)>>>»uüðU‚_MOFïW?dVéÍfnôx<÷§y<;55î÷Ǥ¼’­í6\¿-9ñãìщñ§©%ŸÜâyÛsn}É{ ½|aÚ³¾ü¡áõߪy¼o 1Ï4äÈ¢¸7—UíU1¾Vá» ¬ŒWŽ·‚HëóÁü¾â½Wã»-^‘:ßYñéÒA¾¤±®î0½3éú‘Õçë2¦–””l믖D¿±ëôô”FGžjôê mL*JêQ6lW‰ÆwwyF±(>ÐM×U(|k2§™{ÏÚìeÄûB(¾4Èç3KKë¾ì‘ø&&&žˆ´ äÿÕŽ}Â;ŒqÑI›Ð>½EÌÀÓe=ŽA†|šSæå›>~È”¢ÏÆ5]*¡”|~סÂG _Ø[rÏAïÙ\^ºžõ_~æ­n:¸ûJö^.H‹¨ÉDpU.¿¾j¿Ô·ÉE{´ù ÿÍÉÉù8’>ñ ‹Åù1×avvòý£H¬ÆwSZŬ¸¸¸Ø‹ÒKƒ¨ê®‘³®žƒŒ~yyy;T¾é1/þ²¯4†¹%Þ)0e|þ[-’ajQߨ:>ãË> ¦á}úã>ŸqÛý¦½Ùp­è3XBL±–=ÿ5—lLùjÃvå‡èᬠßc½{‘øöîÝ{i$‡‘ùLØPÏì?öˆ&Ì(h¾ 9Š$j|­½½½2„Òx6gAÆÃeeeE*ßÚãžèƒù °zØ9U§§ÄZâ›pvsíùjÑ·ÊÓàŠ·— –r4.ï›ekÌ?!pIhêIçR¾zì¹ÿŠÂ·0)é}‰ï„ š"¡]„ógŽêòZÒ4}ھΗÐ~íQOVî@úåíù»„vâ (íÅ·~7òßþÙñßNù2ií¿òò\ˆ$ø|G÷þõ']Ñ,Óa|E¸ w €ß|…³Y |­œ¦ý<˜X 7íjbõ×}Ëå/D4EÞ,è'˜§ ñ!Ø‘|­æ Â`ÏA¸¼l#2±8^ G͆¯¶ä†ˆv±•—ˆÖ|õƒÎ"ê³ðWv®4ˆ"£cý;Åæ<„_K ¢“L"ð{BÚý÷…Õ¯Îe„' ,_°çËê8ãË”":XÖÅ –:£ÿÎ5q…ó_qέhò³;‰ƒ†Mpê¾`”ø_î¢øk9_u^g­Cl~ƒëø`¹¸uÌ“Ã|ó5_ñe“Á_h_:øpÖØÈ×ñþ‹‚|‘/ òí:|£‚cÞ;pd^;Ù2IcÇòí·L~q[W,ZËŒÚNY—†5¥m¡ æ»K÷=8ÂG­ÀlÙXÿéß#ý®=-3%u¿:2ߤé¼b•|‰J\ªþ™Ó‰lyÓ\@)[ŠkùV5ÄTTýм“|T È–õÒq?Ÿö¿z¿˜td¾¸UgÕ9ß°_Ô^gÍ0nÌš²iPÊK³z-DE@˜ÜrTœö^Ü6-:·„Ö‰fKÓ1l‰k¹„_iÖ+å;#̳¨nݸ‚Šw¼ñê@m‚¨–¡£óMÿ,qdƒbmĆ ™ÊKþUTæåÛpU.è$¥¹•6KS·GÓʺ4Üð•;5~«´9¨A]øúALQ¡Üòõq' >”¯3„_‡ R/çÒt6÷Þƒ·¥éP¶´Z6FuñZ¿p¡é©ç÷íÕKúzòóûÓ݈éÞ“&¯/~aÃ(÷æïÔÒu ¾-‡²6ß,m&ÄfÁ²ºkI<çåµA¾DŠ×©½ôì­‘6ê.e%¯¡•5yòR þÉ;@¾Ðû¼|Z퉬ô} Íèߣ~–cÝtéNx^åëÕ™öÈO‡³ïÚÒu([Þ£ºhÖODOË¥i"£F­üRÈ;уêFÜïI°÷ßR¨Œ==-{4ÔÒt ¾µ¯÷hÜ{X¾\Ôu)JÏÈîŸ-í4-þ.{Eê¯s½ `»7¯ ðŸ)”².•§†ÕÃÖ"غà­³RÑEIùšJ3Âî‘Þž’fƒ^r‰®³ò‘•0µD¶tÝ–^bcTͺvÊuXuBdT/©þ]šSó‹©nÄ©#}U2‘¹ß ji:ßÝ`U¤Î’¶OÖBeðC~Sç·ú‘¿¬è|•ê\ñ”Ñ?ŸB)ë&©¸¿…1©p»Ô£¯å ;ì{Éü Ê·yæ‘Þ~”H)ó›® OÍW¢gK×Ñmµ¬ßy­K•÷öƒÝå"£zIÅýµP‘=êÆì€Ÿ¥nK“Á¹z8X"¨¥é|ßU“´ëêG•¯ëK•ïáˆúô©‹:‘'îNòŒ–GÂÞ™”²q8J;¾¦wý'¯A㥛àùJ­zøáeicÞ» ò}'¶´È—?ë:’|ú7í2&³-]G·EÕ²4jœ†x­í%ñÀË"£FIÉx˜]Bw#ê¡] Cš¥X2L¾8ûˆg» –¦Cùo¹ü6Q f Ozªû¯÷ü nßrÖ Ѓb•Ë!>Êy'¥lÈÀep¸ÁsNšP¯‹ùã¹èʆ Sä+SePøÂåï/œQ&1Mgn•äò±À–ÞŽnK/±1ª‹×úÖ‚£c_›&2jÔÚ9ÎÕ'ÎÔ+R—OÄ窗ê.¬¥éè|¼R,Z)!•ÂÓKƒ¤²\iãÊJI CI4XœÑ~xÇ«_UHqlŸð«*)"OùĶ|‹QCÒ•ÿEF ùé|¡r’,êF‚e-àÎgÿ8N>ú~н²¥d´ì¿KWoqÓN.Ðq"=Ç=¿_øUíÒ³›Åµ–^œ=Ë_[NŒÚÕÒŒÒÿ¥áSsì»!¨ÅóMèõçŸêðýZ_sy76™ŽtŽ‘Ž×Uñ‹ÿ´n§øì'‹ü·åĨèFi¹Zé£ÂZ _”P òíH¾=ü<Q©g5µv:µÿ=Ñ“N»YD¨Þš"çßô$¤žWä2–TÒOÓÑS|4_5¯Èg5)P­|¢RÏjëÔñáã*:í¦gë%ZsäßBzRË+òK#é§çõŸ)Ú*yE.«)HêÝà•zV“êXçå»uDj$vÓ³ˆ¹+d¾zRË+òK#é§çŸ&z^‘Ëj R F2“KTêYMªc—ï]žÑtÚMÏ"6.¬Î—o®'!µ¼"Ÿ±4’~zîÑ”âSDÏ+rYMa ÔÛ ªÄ›¨Ô³šz;ÐîWV8å[wà”|£]=í¦gáŽa)s”ùMKBjyE>ci$ýŒÜ£–â3DÏ+²YMQ Të† Q©g5õv:-ß‹RØ¡ÞçÑæžvÞ$¤–We,µ¤Ÿ‘{ÔR|6 jb—µMTZfG;ÿÆVm\²Æ¡ºžW´ÉX¹G-ÅçDl ,QÙiâoÜÀ³•Žõµ¼¢]Æ’Î=^uÚ´]ƒ%*; _ä‹|Q/òE¾(ÁàKº™t2¾¤{ùu‡ÜîK:_ô_ä‹|‘/òE¾Èù"_ä‹|ÙšíÞká}þº'_ÿð’¶÷éFãë×PƒÄ7Àƒ¦Kó%úMθêá–¢{™ŸKµCÌíPŤÎÏ7êÔõv ð¯6?TøKBó¥gIø–¡MÁ¾óó #ä1 ˆø‚-_ð‹o›ÙÙù)øb½e 0¢}¿|à®ê/_b Ýš¯ƒf˜¾NzÚíù1_ÿr÷â´‹¿„{Ðó ÊÌÁ@¿1/CÔêüÁ¼ALÊüùà ‚i<v/òE¾]oû ù"_ä‹|‘/òE¾ÈùÚÙlï|7_Òî­ÞP|Iû·‹|‘ogÇkûg}ä‹|‘/òE¾Èùv{¾ÄJݼ†Êñðol¾¢§ì 6Á׊ä+4È. ¸aùš–=˜VKšW2ø2(R ü&ËWÒéÊ|¹õ“ü' ¦u7Ä_ñƒU-=èhv£øË­Ÿñp°œÔ‰×ñkѨ½Á®›ê|¹õ“ú ίAõ‹¯ÿZ%v»#_ƒ œ¯ÿZ}|iôiØ’?Ý›t{¾æÅfH~?X<¤šùê_nvþjƒ¡mù"_ä‹|‘/òE¾Èùv`\ÿ€|Û¥C¸þ ×Ovi¾0Hä‹|‘/òE¾Èù"_ä‹|‘/òE¾Èù"ß/>_ý· û/>ß)Ä|Q/òEA¾Èù¢ _äÛÙPà4js¹ fe_—SùêIÇü0.Ñ ‚ XÌ—81°5|¦ŽÄiGB~8½ði¢$38³V¦}uƽ`Û½y»=_Üu>ŽøþÙ'¡kñm¾w¥Ð,±²ì«7“¯ ,öé¿Ìw˜§ž_aw¥•ÛggD÷®o _áeÝfÉûõ2@ã•Â/ç„WX"$l¼>_£MS‹+ÃRwq0G8œñÁÚ_ O«­o.‹.þì¾æÓ_Ý*ª_q¼Á'_ë§vð}vÈ—Øñ=æQÃÏŽ­µðßò*Qu_¾Ù½úÖ<€‹g¥÷™ÂVz~³ò ß|ö_î¹6NøîÓ°x–ßáï3Õ$ â»Î+/è%‡.Ÿ“Þ§ xòXXàþ8_`ï+à€ïôk -Óìã¯Í°Dñì¿9›ôÍïzTž¬ÿlÞ3Dõ4%wH|æ¹6ààN®µ-GÀ_B¸@lÅŽX_nÏ—ÚÏY¿ISÛÂÉ—EíTNK¾Ò>OfüãKß½‚}ä`ÌÍLÂßüCpìzbw®â<†˜›4}lRᛯ0$·…o`’P^ Á– þrâÚ‰š”)Ëh§(|wâ[iZ·0$|wz‚/Ž='¾"2¿Ó}ðbþ¬}ù†$Gw#´ÿ:49º®ÅË¿$)ñë{Ç9º†Fù5=<ÿjNËŽ–ÿM²°·¼è7¦ä—òrçÃ9VgýÝúqArGâ§¾ãïçè’kä×ÊsTшïjdÙØ-ùÜã9|oâŠúúÁWP= v¶\ _G‘×Oþç袛äׇwçüûÇx³Á ß½žbýK¡-|µ#øu3}æéÐðem™Èú‘ãx~œÛýĪG_iߨ|“‹mùÒã`¸Ø®ãü׺óÌ n¬š;Y`D™(AªÄ( luÖV |ÞøO3Õ„jjæ·À9>qùdìeŸþ»ªP’CtuªÄw;‚_ÕÓþmã»ÛáITJÛŸµ_,ÇüË•¹™ô.z€Ž…5¬Ôý‹Ný—n0a€üúÞ7ÎøZôdž¯ >°0Œ…Æ?š? ›]Ñ?Úà»rÒ7ýó_Q;‚?S™†9þ¨üÚ§Y¸3ˆCÿ»g‹ˆ2r¢Gj_üaÓ~¼þÑ3²ŸiièÿG~±ßž?ð÷s7ß{¸ú69JÈË¿eÜ^ƳþÁæ÷1ûÕü^€´õüw×4«¯ªw—ûŠiðjkâ8¢ç—òq.êAÒ9çÕx’­¾zϳ£#øúnÏÙ¤.”ˆ T·‹cë’BÚô5J@|É œŸl¾tŽù¶‘ï»êûÆ(1_ÜF¾¿*ëŠWù†„o´’)ïµ….ÜÀ%@¾0¹'€ëe`ü×ákŸ|çÍØ^|CÅ>ϪÞÈ7d|«÷_Ë2— Sî(ò…Ÿ÷³e~¬!DñÉצ ñ†–/ òír|Q/òEA¾Èù¢ _ä‹|Q/òE¾(Èù¢ _ä‹|Q/òE¾(Èù"_ä‹|Q/òE¾(Èù"_ä‹|‘/ òE¾(Èù"_ä‹|‘/ òE¾Èù"_ä‹|‘/ òE¾Èù"_ä‹‚|‘/ òE¾Èù"_ä‹‚|‘/òEA¾Èù"_ä‹‚|‘/òEi3ß¿PB)È7Ô|[Uq¦Î(·ªeAèÇÿ¼¢m¯AuÓÁÈBç¿ÖM·ÚnµUö»Ñf¾ô–­—s÷ç«n8o6t|µC¿U³ÃGŽVS¤P>j†2ÕŽãÀCãàgQËÞWËØfë¿Zç[© ÇŽ"è|©N3e¢‘R1«Õ Ÿ¼c ƒ%‹ƒéJ+í–.G…q.þêoåºBÿ¥^[Y£œÿ¶ FÎóý˾ÖAÉÞ |ù¯y÷´_êØhý+h|ÿj3_&>˜øZÏvñ·ƒøÒhí(¾–ÓQ«èk ¨ÁŽæÛjIIxæÛj®ÖFÿõy8ûÉתA»øÛ ¾‚Ù™;0ŽGúü¡ÕhÀ|8þgþ`öç þåcAä{cJk{A¾È·+óýø²°Ú5‰ñIEND®B`‚urwid-1.3.1/docs/examples/tour.py0000777000175000017500000000000012615524560022131 2../../examples/tour.pyustar ianian00000000000000urwid-1.3.1/docs/examples/browse1.png0000664000175000017500000000610312615524560017054 0ustar ianian00000000000000‰PNG  IHDRÇ•×ÉPLTEåååÍÍÍÿÿÿÿÿã›2îbKGDhÙQ pHYs  ÒÝ~ü ÊIDATxÚí[z£0…U÷»4Ù@wV0ù²<°¿xÿ[t/À$âˆS޹lÿ®¥c•ƒÁ`0 ƒÁ`0 ƒÁ~¿©]ö1Ú?Ûd/ds^IJ·ódéùä7@> ²”‚F’2.DÈB‡‹½—V@f5WÙ/0O! 8ónÈÇäïŸƒé ‡“zÈȲýüúTzA©¯¯°«YÖÿ'dßÍ¢‚W‡R8ƒ4@~÷ß¹¦'2 ÷ÙÆd¥É~r#È:»øTæª÷nÙ*¿«.جäw@n™§pã/…ذ ¹‚^ú_­.C6®.ÚòB–øÚ´=d€fÿò©íp )´[Bvãµ¹.ä1i~3K ×>\@n Y“O,“@þHÃ…DvÑ23@d@äa€Ü äµA÷¦g8{nš·äÈô¤|„7Êõ6×…üσ¬ ʤ¦£õ Wtæ̳ù-öCЂ—½›ÃõA— ëÒHéËP]ùä¸ÅS [,@©&u¬Â×OQWêGe©“È^)2 cbÚQ„Ì Ó·„‹eÈJ% ò+C¶1ÙNðDÈK1Y(_ên‹[9ùƒeʨœRX€R!‡ìB¤ ’AV&ß0J'”kCVüaþlªt ç!ËbȽ—½·ðäå.äÒ‹è$.´„ äŽ Ë­O’óp…/µ›Cžoç]xr3È/ že*lšq&0H5çI¾£áz.*•CͰ ÑMG¤dbz&ÅÙ;ÿI «Ëq¯Žrhȶy 9ª=震+)¬RÈ=Å’ÊeŲ̀Èqä§ SÈ]u+C^&1 %ËÜ(É“¸'+ÑÓ7Ou!¡`Ùbl¦>!»£”Ì}i³ ç®ΩžéDc“8ÄìÂ%²Ù‚ìb«o?Û²ðŠú>ùC–3½Snã·«HÑ2 ;‚ìe̫ϩ|Š'[ÈÈ€üê“ÛH;²%ȘcGCÚþ 1âꥴýäØevexòaȉ@ä¡’bš' 7€¬ù,ÈNóäyL–‰'«ùª˜dfT§ŒýW«–3²‹:FÉsæK[ÈOðä¤×!q3¨p€ ÈÌì÷žÄ‚¥û ›a(åt0aUÒ?™rcÈétp–¢ÿ$dáйÓRôód@n9™¢z2r{È2Þu½ÇRô—€†nRŸ¥è? yÂÖ2fô=2 ·Ÿre_~ŠÎL@^1Ú3{äŽ6€ È- œ¢Ó Û]´›JöJRç±):ÉžôÁû©d¯:L‹öOÑ©!w<šCVêùì‘2ŸAî¡Þä% +Χºh7•ì5cò¡):ý\¾MO•ìõ²‹StÆA¡Aíª’½â€cStÎKªû)²®*ÚB&@.öäƒStÎã¦ö…rïJ€üÌh— Gë»O‚|Pê¾çŠÜMÙ{7%ï/!u:µ¢'ûy?9ùs?"uÚ­RM!÷2ãËk¨pÝ™CîÄ‘_râÉÒϼ ÈÁJ®Ü„\`̵9zò1©3å2÷] ²‹i¸Xs½ò»QW)óËH€¼É“kÞ¨¯²w¨p€ ÈÑDH¤ûS'w€ä߯ H»ŒüwúŠ(ÌéD!‡kâEgF­ YúYà„½@€,¹d ·i š@fw,Rlê-@Þj.&O!³PÌÅOïÕâ*i•<9j>²TO»dä²p¦ÛKÃ.w_3È¥åÔ“UÆ“g·Üä"Èé…§p2&u*Äd©³e@.‡ì#À¤3"¸™ÜÑÔƒ2 È€ ÈKöDêTˆ*­K€\r^êäª)+uš®GèŒ_âBÀÏ’:E”:ÐA€\èÉ%R' ï R' ï…\ uº˜|ƧI"JáärÈËRgš]èzkÍÞùCÝêïÈ—„Œpä«BÎŒÙì ânj¸§DL.4ZéÀÍo:Wäh¶È; K@®yVG¦\ ïB+Ý|v˜–ŒQì[ÏëÖÓÉñ )_r:AD ìL¨‰.$ÓXA3È’!´ ìÏ"×3Uº9™xCŽIEwæ;qäÚ7§¹/CVI¸@ûÈ”+–Ã…É(`ß|‘¯R—¾]E…3Ù…ö²3Õ|Y„Új@î2 ص}Œö¯#€ ȉQ²Æ¾þ @®yÖGcâÛµïÒ·ò[ dä#Ã@MjÒYq:yÜ€|²¤ÜücN)J& %@ÞcÇ>ï¿dâcá¦7äÝž¼9\öºQ!^2s_xò!ÈägÝ”éHo6ª÷Bþ`Ù…›u“ÝWˆ’Q€|²;Ï•!ž™¹=dy¥1š?È=B¦d-'u^1xŸ.ur äR' ï€\&u†‘^¹Dꌕ쀼ÁÊ¥ÎôþD—R˜Ï•:ÃÈO@®™q$Å+7†·B.‘:C%; oƒ¼Kꌃ9/- uþÈ:O€ ©³-d ÷™’µ…Qb¶`VŸC>:ªç«êäEÈERçØi±µíáþDnKßÝ“³¥N_Þï…'”ä‰í•:i^ ÈÏ=¹LêÌB6‘÷AfØh 2Á“Ÿ@.“: K@ÎBÞ#u¦7gg }ÏÞ ©ó×@†ÔydHm!ùGÈ”¬ù„%fÕ /«pW¿*ž"uòAÈ[¤Nyñìã©S^<>Eê¼zÄ8Eêä† +AÞ u^k\}MÈ%Rç¥î*Ò2ÂÁI!užRg[È0@î2%k9©e¿‡ oSá¹ò Ÿ¹LêDû>È%R' Ø‹¬\êDûO.‘:QÀ^2e!›­¨­.‚\"u¢€½ò.©ìû ópkRç !u> ƒõaÃ0èß% ìÏ}üy<w³6.Œ›Æ•ñáav™6vóÝ,¹5ÝX·ñ+Km²'ôGÜŽZ½æ¬ú5›g'ÔOžK$»tCÿNùÉB“Aܾ3õÆ ä…íO ›Wh^çý ‡là<웸Né±Ô&wÂÉá³£žBñ‹²ù7~xÉ÷1šwÊŸ$¶òn¼Y ‡ »7n™Ü@ûÏ¿Í<ä•6É Ÿä¨)äÇÈÞ]³ïìFLjÜnú÷møúÿ¾oƒ]a¿[½n¶Ø]zmpG™Ã÷Cv»V!¯´YEø&§‚ìˆ:ºr#î³d,˜|²åÛ!!ËÄ1¹[ ë5d×sLN{â ËðÃ1y½…2Y†ðH ™>¹»qÝ…]¿þa˜œü²úœº˜|h@”r}&_™Qg–bÔ¹žÉ!ÓÂùQçRq“2-ÜÖN`röQ§Ád|Ôé°ÆNi09ñ¨³óy:|ÃdxÔB#dL¾„Ýs"Ä““Ÿø"“ï˜|òF —2}ru yè.R>3¨3K1ê,PŒ:‹CfÔI\¼F&ÓÂmŒ:S>‘J ·1…“¼‚HguJ^ §³:Ï…6&Ï nŒ:O=}ÉsSu€¼>ê”wLÎ=ê<×FÓ'¯µp ÈòŽÉŒ:Ÿ2£Î‹£Îâu¯‘É´p£Nn1K yûÞjZ¸ü7°§›ÂqY½9ê”wLNyóU<ý”ÔI&ëü£Nž~Òù_ՉɺĨ“s:¹ý dF )FÅ!?ݨóçƒõ5ÈÅê©ãâ!?]\`21“1“1“ŒÉ˜ŒÉ@ÆdL2&_a2‰ ââÅ 7ýB/>¤19d¥vÑcrÈÍ:Ïs1y&²}k­DcáÑ[h÷î·PZcòiÈÊ!êÞTã6[÷¦|5Lþ²Ø‡¬…ŒÉ³ÂæÁ²ŠS1ù3“&—€ÜetƒÉé!wM…_¸ÖYû6Ãw˜Ìì¢NȘ\Ìäº Ë£ñàïE\\¹Æ¸ãOÛüÌ ²ì°ù¿Hbò²¸ÈÙÍ#2ZD·oÄ6Ñ“#Èæþ9d3,?‡ì÷ Æä²Œˆšñ3áöIn`!G·L…ÛІGb09‚<þ £Š~ãõÉ#d9~±a'q³ø‡ 1yr÷[Èòäp§ªÿG!×™É2Î`C^ ¾sŽrr³6&ïød¿ñúç'>9®åÁ_¥}ò´…3ãÆë#ÃíNÆ /÷~ WiŸ<¹éw`—ùpûbdÑ]„ïü²{1R©ÉÌ.˜]ù«&×¹ÐßBU—@®-.ûüý ?kÝbr&Èm@ÛþêqcrjÈo=Û“1yOä>%ÞL Lþdý È˜\2&‚Üj29÷‰Ï.δp˜¼«òoôɹ/F,á3qÉY “‹CÆäb&€|”Éùçɽt¶@\Ô ¹X\`2&c2&“1“ŒÉ˜ dLÆdL2&?³É@&.ˆ c2&c21“Ÿ²Î_ªz“ (­ª7ùȘŒÉ˜ dLÆä M2q‘r¿‡E£U ÈuÆ…3¯ÃeZ@ïùGÔša;—F-ybò1ÈVMôãòOL~Ș˜<…¬VMçâ“çèS‘ÉY.F|\„Q§Z 619åeõƒ§9L~ Ș\Ìd '-ââÈÄ&c21“1ù¥!ëü…É…ªn“/€ŒÉ˜ŒÉ@ÆdL®Ðd Ä1“1ȘŒÉ@ÆdLÆd c2&c2&cò ˜\ä{™2UÇÅ%k‹ {@úãB„…‘‰ KL6áð°êÁ¤Ó“evÈö‹Un²‰ts4lnH—)ÓÚÔn²\@î¥ÍeY¹ÉW@®ÎdS²©¾»èO|&dY}w1´pr 9fCŸ_Œ!¥¿*1 » IŸ —­˜]ô%JA®Øäœe:/c’òS=3äÏŒ“1ȘŒÉ˜ dLÆdLÆdLÆd c2&We2‰ âȘŒÉ˜ dLÆd c2&c21“1“1ùL^B¦ ÄU .(LÆd “1¹“5•¦öL¦’Õ¶ÉTzȘŒÉ˜La2&WhòÙ-þ¦ÇM\³¸p‹ ’¸fqɘŒÉ&c2&S˜ŒÉ&c2&S˜ŒÉuš<Ì"Ëb,r,¾c2&c2 LÆdLfɘÌ“1“Y`2&W¾ø¼•æ8ÏQ³IEND®B`‚urwid-1.3.1/docs/examples/pop_up1.png0000664000175000017500000000115012615524560017052 0ustar ianian00000000000000‰PNG  IHDR_ KbKGDÿ‡Ì¿ pHYs  ÒÝ~ü IDATxÚíØmŠÂ0…Ñ»ÿ}v"ö+m…lBëyÄ3¾æI«teAÀ—¯øòå+¾|ùŠ/_ñåËW|ùò_¾|Å—¯øòå+¾|ùŠ/_¾âËW|ùò_¾|Å—/_ñå+¾|ùŠ/_¾âË—¯øò_¾|Å—/_ñåËW|ùŠ/_¾âË—¯øòå+¾|Å—/_ñåËW|ùò_¾âË—¯øòå+¾|ùŠ/_ñåËW|ùò_¾|Å—¯øòå+¾|ùŠ/_¾z®o¦ø^:žï5#W¾Ë&ÞîæfØžÉYýT.¢ó‚ûøß}ÇM¾lè„ïùóá”o^%ÓMGÝñðóaw}«Ú¿ß·Û,·ùÄßœ¿wYmû‘«û‡ù}÷äàþa~¬:þâóÅáxŸùÞÒ×÷gâË—¯øò_¾|Å—/_ñåËW|ùŠ/_¾âË—¯øòå+¾|Å—/_ñåËW|ùò_¾âË—¯øòå+¾|ùŠ/_ñåËW|ùò_¾âË—¯øòå+¾|ùŠ/_ñåËW|ùò_¾|Å—¯øòå+¾|ùŠ/_¾âËW|ùò_¾|Å—/_ñå+¾|ùŠ/_¾âË—¯øò_¾|Å—/_ñåËW|ùŠ/_¾âË—¯øòå+¾|UöŽ …_wbIEND®B`‚urwid-1.3.1/docs/examples/browse.py.xdotool0000664000175000017500000000016412615524560020327 0ustar ianian00000000000000windowsize --usehints $RXVTWINDOWID 79 19 key --window $RXVTWINDOWID Down plus Down space Down space Down Down Down urwid-1.3.1/docs/examples/graph.py0000777000175000017500000000000012615524560022351 2../../examples/graph.pyustar ianian00000000000000urwid-1.3.1/docs/examples/subproc2.py0000664000175000017500000000020212615524560017067 0ustar ianian00000000000000 import time time.sleep(1) print """factor: 1 factor: 1000003 factor: 2000029 factor: 3000073 factor: 4000159 factor: 5000101""" urwid-1.3.1/docs/examples/bigtext3.png0000664000175000017500000000617112615524560017230 0ustar ianian00000000000000‰PNG  IHDR_;ˆþŸþPLTEÍÿÿÿåååÍÍ´¿ï%bKGDÿ-Þ pHYs  ÒÝ~ü IDATxÚíœÝ’£8†“;ˆkjη,ö|ÇÔœoQ\À×}ÿ·òa[²åæ'!³òdº'Àƒx‘„ }»I;¹)÷âoãO¥BÿvŸÞ=V¯õ®6óØ­Þ6µ|»?اêV|oÑ› h¨µÀ¶šc8x²Óý±|(åñÜ¡Tfa;2ÉáîW¥üØÃ«åÆF¦Îã>}0­„Dß±#n7ÿÿñPö˜Ok½M½Ç OÛz(2éýqÏ4ìwÄô¸)nú0bÝíöíºâ—ÝzÜwwüg» —Q÷‡*4|{®aƘ3 ³Î ÏÅÇõ+(ÖC›pßÁÕ¹½ ÀŠŽÙJ ÏGHv«e¾#ŽX/’3†°u; ¯Ñð3à{ÔÆíVXبš…o%°Õ°ÃñçB;0ŠÛ*h˜)á†ÝiXáâ8b…‘k˜¤M †o­Üê¦ |ëŽNœúלˀ~ð—Çí¨ïó©´AãÌZè<¨;%J²ààÙi©¡©ò?WÒ÷€£º'ÀÝØ"žc¤Ã¬]V V€!Àfr{ý„v°Wüº©ŒÁ4XئˆôÚ¦,£fœ|ßYŒ19Ö ð!’(ç…ÙÔü:`ŸU‚Ï*°½Jˆ´}cnØ<´œtÉnIÃÎvóžmaÜs`H4¬5<çÖ2àp{ÉÐì]f•Þ]ã¼D9 x  /¹çO Í’üœ |Ô¡/6OpìçÐ|š†# H়"ì ðN­Á¶ô2ìÑúô2vÐ6´Ü<˜™#°5½ºÆô²vT)°!Ã@U?ðêô²kM/S »ë / ` À® U†uÀezioäjK/S`ã͈#ºÏg,lúuÀezi¿Ù–^V€ƒ5°S‹öé[;«k¹DטüΩ÷&ö†ÌÂñ4̱XÓŸ½lÖ0Ãs—fj^zsz¹ÍÂ\&óÌ‘Óu‚¾uxezé€ÛÒËŠÑœvº ã¥80´§éå8¶¦—Õ°µ"4›÷†æöö ørÙ|ðb¶ö÷÷ÔìèïïƒÛÿNVßö÷×ÏÃc\ó…* Ô¨t½¯céš&ÀσçÀ诉ú÷îÚôÙ¿”ûáß(¥p„ZØÃ食ù˜§0{ÄÆ×ðW I²`á@óÌ'ZØé'ÉØLåŠão»ÙßÊéX1`w”£‰öÚ°ïY})Zê«ö[uÖÏþ2FCLƒÁ0Äl ÊÞO?"°•¤Ýx¬ðÝ—ÿÐþR¤7Âv¡öG öÀ@PhïÎ@¨ºa(ø 0J5 Tfaûr_àÇ_¹wê{²«H9%”©$ò|xF_4•{U€qïTú$ë]ÆB^´æ¤‹§ØpY¸êÖ ##àð>\5ÈÜZ%pTÝšr.·zÒ1 ‡%PÃ9°=¡¨˜JÑ|'Zn«‰Àü€T,\ “½ÔwE8éðy úP‘—È€H|^šOþöÀ—H~ÌŸ—­}\>|ê%’ŸâÂôѧHü†Ohb =^]_‘^V.B»X§cgpe‰dº<öâw4Fg ”ã`h®xA—l²`E¹5}Gz.+?C¸«U5ذ2é°B+*MŸoT€‹BÊ,ð0\Ÿå·ÀÓñÌ » ÂâÔŒiÖôLúaºø”zR²h)6“î€i‹N£Ï_ép ¤#°ìŠÉ+€Ñw °“'ÙÙáQ‰Û×fÇϤ#ð ë\ÃØµš$ñÎ,LÀ')§«Íê:Iø 꼆aNþRE·HB’2à¢xXgçûs`:é–¼Dx(%Qÿ¨fá¸é§nÍìskîáôúIWÑpuÞG8éÒÀÑcÍ!< }Liöáô!–°CàÐ/QŸ7ÿÄÐ,É寧 Ñð  ¼£S—;Ý„ÉJýñée}𳵄*¼jÅ 6¦xmz¹8+³ýéå:à>2‡¢¥Á"l,ÅÉ\ìž‘^1¦` ìPèO_jÏ€¹x!+»Þ?>½´™RÈñpÌ  ÇÒÎ<°é··§—Ä9²¤"6‰P ئ†PjnN/ݺ´SJ ,[«'Ã(ôVàöô²ãy&65/áÝuPvY÷ø ô5œ gº ÆÚ…&LÓÜ”^r/ƒºëBh†¡Ùî¼&4G`ÓBü¦ägàÀ‘­MúøÄôÒ|0o<Ì´{«QëÀ>i¦à¶o,°™¦à—¨º•gÀÃxˆÛ`>¸;Ø£ÃlF?ù €¡B(tO”ÓRk€}ðÚõ—£…c¥7”Ë0˜™Þ@ZªÒ1Á›«—À–Óæz—‘9p^[£¬ÉëÚe¤s¥ªòœs·Ô {U%‘û;gú°î·wì*i?0T«—¤áC,Üja೤áänVÃMÀ]¸½y·$‚+(½ËQÂ*ÛÀï5ôøÞƒ}QGÙøhOœ_ŒOLA¯©ò“ä<ë®dY, ÀÅ£=q|µ†9•á)ÝÔJÅ@åËíÀå“24žLÙ¯îc ‘sËï‘Dý¹æ–¿1ž÷\i¹’Ûv‹GQöÓuoÔ0ôÁ’††u^^>ïÉñà%¢$zIª—F~kÊfàaç“ã&¹xîë¡ù•‹ka9 ¼,ùY l4\XŸÑ>ø4 Ÿ|À丮ýÍ¢7¯¾÷²šË\Dº®ur|¬&Ë/n™ÇXuy°nq;±úË%±~rÜsvÝ[uÃäxØÁÄ5¾¸irÕÛ½xÿäxô&ÃûGÓ½—-õ`ùÃDü¢&YT€X€X€X€øÒÀÿͲÅôGz;IEND®B`‚urwid-1.3.1/docs/examples/edit.py0000775000175000017500000000023512615524560016266 0ustar ianian00000000000000#!/usr/bin/env python import sys import os import real_edit real_edit.EditDisplay(os.path.join( os.path.dirname(sys.argv[0]), 'edit_text.txt')).main() urwid-1.3.1/docs/examples/browse2.png0000664000175000017500000000627612615524560017070 0ustar ianian00000000000000‰PNG  IHDRÇ•×ÉPLTEåååÍÍÍÍÿÿÿÿÿ;öÙebKGDøoéÇ pHYs  ÒÝ~ü BIDATxÚíK’£8†•íp¯¥`¦Oà_`áÔÆ{¯|ÿ# z¤ °ÉñgU¹0é$•–RB@ @ @ È÷‹Z$;ùWAž Èß ÙW’RÆõ4Yºÿ€¼ò/@~d)u$e(ÈB›‹¥V@Ž k®2"Mî (óbÈÿtÎß¿¦ƒì Æ/dÈÈ4y9Õd@ä/lM±…2lr?Ù8RÈPpÇí,´¿ï¢dV\i0¤dbÈÈÕ KÁHqÈ@n2h]Èù‹!‡P§oz  R²pšr§Ì¦ˆÖtÈÃP'› rk¹ &2 ·ÙG8¥c Èå!‡§q34p.@Ê™‹è¤dD8_Î*ù£‘ƒ% ·Y"dQ2ÁsäF ÛPgço€vMÈ.šÈe! !~™’°3b/0+CFî5lr}s!á]Ô„ 2 ·ù©-G@n òîtÜõÏ¿…[^õ²ŽÈ#Uvc{2 ¿²;mwÔ…îåt<šÂnwz}·¦«cyšMB×Ú”òóú{4?™¡Ý!v¹ûz k¯Ùt2xwG@žù´ÛE ÃSô ŸyÙ!µ=8Z‚ÚrM>%š|B> qäµlš#ÈÇD“Ol ä Ïö.N'ö. oã]ð&]¾N“­‘7µ±šUZ@Þdk.È€<*l$6á|Ûõ%wþ]2  dÆŒ"œº¡á ò"œ5!ÛfrˆpêöŸ ·á| dÚ o!.T²p ®8™ïI¹”hã»;…§]ÚA“Ëjr² un&ÂYY““'ë5‡:7ᬬÉаrãæ’@FÏÖïèŸ y#d›#7~__rÅ2¥‹€ ÈŸY13Y‰8iŽÉ¸¥Ç©J¡- Ñ[žŠ!“]©GªJ@®Y2 d%„äúšC–ä! Ôd—Ǥñ»S™‚Ü\’ð:šŒ¤’€Üй€T† ä6 ç³™3E†DF¯ßòú—E Õ<ä´ òg@ö¯ át­‡ÓMŸê o! ZE“)šŸÈgïL&Y6¹ãÌú-DAkAv$Õ8d­äÅ­•Г‰ï»@.9ìi²䂿‚#œJ ‹íDAkÅ.‚¹‚³w Ž‚²w±•((šÕ€ ȧ!_ µå?@þNÈê _.—óx•tÓYòl9äHf òÌ?²¶çsg8º¤\áÌ`ͦî¹ÛÕ;y©&wÜ:|gýbu!@>8àg³—C6lyQä —…|±†ár¹r=Èq C>¶¬Éæ1wð€¼P´/qÖB»Î\°Ý8Ä­¿q6•¹°–oM^ ù È/ÆíÂ(d„{´äç¥þІïýÇ\MË=ù;!›Žš$Ek&•ënaºn IÏÏö»wÖ€¬»¬Hr=‚lº×}HòoÜó“Â^öת5ÄLª2e!S¹áNq!«²éû È• Kî>KYMVÜû ç’RMV Yæ5Y5ß½³–wa ù>œÖ\[i×á“BÏ϶»wÖJÅà…¦'°ÞÄ@l@nò£þF»# È€hé¼ cÞföN4«!¯„üço'` È­BžrÌL£dpnÖä ÈôÀû¥Ñ•rºÎv!ÿȯƒL¶ ☒;sÊ8Âi›ÄÉ~OJ»› Ÿ$y rè¨I>ÔÙ­¡8ÂI½|2@”ÈG} ¶¹ÓÛ_ü '"Ÿ=’yƒ&ò3æb²RÑ@äφlm²$NÑ™ûÖ‰26Y¸}-d È9È#ïBéàfÆ eÈÕ™dYõ¿uädÿžMÍÝÈyÈr6däÙš<þ=‡{Ø…š!€Üdùì ¼Ó`þåç0äY‡ëã¦4¹d‡g¥ØŒæHUѬ©²‰p 7’= ‡š!í¢©¡$e SÏ$îÌuïLâ™äãr±V‡p¨Šºsr9D{<ÒG)¬RÈ-Ù’Âe›1Yoòeo¢"°ß)È^KýÐ_YFžÅ“mõ¦ÜR¢¥ožÊB¦^N¥z-6ÁÁL…Ýü^Š-s[±Œ‚Þ÷á 5Å °©xÄà]ø:2O•Ø.×jVÓàOOÚ€»+òCöë~º§w{CÚ…d@~|p÷¹o?mÈhro$5 ò§Bö-߫ӿê$—D‹‡«ò ¡8ziÓFRh2‡áꀼr b¨¤¢˜' W€¬ùU]ÌK@Žm²L49äØ*æ Þ…éÕ™äêôÃÕ¹”ÝP‰e -Û‰z% ¿@““VGë¹,ÞÈ_ Ù~ïI‘q t;òj1 ¥ìw&lo”ôÛ!“"@® 9êÞ¦šŠþNÈ‚r£CÑ?L“¹ äÐQëj2r}È’ç€ks(úG@ö]7©Í¡èï„Üã © y ƒßÈ€ ÈÏrb[>Eg¦ O-ɹ  r È+StJžŠ=‹63’½P¨s]ŠNâÝ“6x;#Ù‹vÓ¢å):Õ(äŒGuÈJ=Î)C×ñ䯛|dÅS±§!»fF²—´É«Rtr® ®ÓÒHörÞÅŠ¡Sh<'{;#Ù v X—¢s8¤ºAÖEƒö3! ÏÖä•):‡v©}!€Ü:䵡N@~$´( GÓ›_ye¨SpËÅr7ÃÞ›òþ¡Nm£ Éœ÷“ûšP§]+Ur+_># GAcÈ(òAN4Yræe@ö²2Ô醛3Ì"Rm@š¼.Ôé÷rž‡û®ÞEß\L©ÞüÙˆ¨)—ùcB€ü”&—œ¨­aïˆÂ2 Þî£N<;@òá·v¡ÎEBü¾"ò9È›bÿLÜhfÔÂ%gv.Yr1Èä§i êAŽf,RQê-@~VœMîCŽLqüd­[VH“Ċ!KÕÓd¿ÉC&@žg.|º=1Ùoróš @ž Yö5Ye4y0å Ï‚œ>øbN§Ny›,µ· Èó!³è5F¤ n&#šñ € d@†ô!C mÈõzÕ¯s*Ïß·î÷~¿ßÌRWèVu Ý¿»ÙdêØÕ7SrKº²®Ã cu²ä=n#{M¿_sTýžÍÙÃõ)ü¹D²IWä+æ«\Åþ'Y¯Ì@Yÿ²y‡æ}þÖïñî2dçn/âæ9¥7b¬NÝ{=„,Â)dóÓݼd‹»æJã“„Z×¼A×UÝ…[&· ýáËÌCž¨“ÐóIöêC¾/€Ìêš…|‹®4(Fà¶×¯ûëÐQøÙ_m¡ƒÝ½x¶zÙ¬±›ôÒÕíev_Ùmš„¦ÉŽç³…)Œ>)Ç Û ¿4ù÷šœ…0¯É·ÚŲ¿éÞ…s€­!ÞìZ{°ï™ ³Ñ©ý•÷ÊÉÝ¿!’‘ÇÕß1Q`êhy_b¢Nî€ý{4Ü+‚¬–¸pSG\8ë+8xo—¬gaÿL¥ŸøÁgKŽ®õ.Fhç½;È™¶C 0m2Üý[­ó¨12rä Èî¡w<·1rsdfK/'2»–žØ“D-©wäÙ2»¡Èóe½V»¹øö”zGŽå½°ëÌ)z^IEND®B`‚urwid-1.3.1/urwid/0000775000175000017500000000000012615524621013346 5ustar ianian00000000000000urwid-1.3.1/urwid/treetools.py0000664000175000017500000003631012615524560015745 0ustar ianian00000000000000#!/usr/bin/python # # Generic TreeWidget/TreeWalker class # Copyright (c) 2010 Rob Lanphier # Copyright (C) 2004-2010 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ """ Urwid tree view Features: - custom selectable widgets for trees - custom list walker for displaying widgets in a tree fashion """ import urwid from urwid.wimp import SelectableIcon class TreeWidgetError(RuntimeError): pass class TreeWidget(urwid.WidgetWrap): """A widget representing something in a nested tree display.""" indent_cols = 3 unexpanded_icon = SelectableIcon('+', 0) expanded_icon = SelectableIcon('-', 0) def __init__(self, node): self._node = node self._innerwidget = None self.is_leaf = not hasattr(node, 'get_first_child') self.expanded = True widget = self.get_indented_widget() self.__super.__init__(widget) def selectable(self): """ Allow selection of non-leaf nodes so children may be (un)expanded """ return not self.is_leaf def get_indented_widget(self): widget = self.get_inner_widget() if not self.is_leaf: widget = urwid.Columns([('fixed', 1, [self.unexpanded_icon, self.expanded_icon][self.expanded]), widget], dividechars=1) indent_cols = self.get_indent_cols() return urwid.Padding(widget, width=('relative', 100), left=indent_cols) def update_expanded_icon(self): """Update display widget text for parent widgets""" # icon is first element in columns indented widget self._w.base_widget.widget_list[0] = [ self.unexpanded_icon, self.expanded_icon][self.expanded] def get_indent_cols(self): return self.indent_cols * self.get_node().get_depth() def get_inner_widget(self): if self._innerwidget is None: self._innerwidget = self.load_inner_widget() return self._innerwidget def load_inner_widget(self): return urwid.Text(self.get_display_text()) def get_node(self): return self._node def get_display_text(self): return (self.get_node().get_key() + ": " + str(self.get_node().get_value())) def next_inorder(self): """Return the next TreeWidget depth first from this one.""" # first check if there's a child widget firstchild = self.first_child() if firstchild is not None: return firstchild # now we need to hunt for the next sibling thisnode = self.get_node() nextnode = thisnode.next_sibling() depth = thisnode.get_depth() while nextnode is None and depth > 0: # keep going up the tree until we find an ancestor next sibling thisnode = thisnode.get_parent() nextnode = thisnode.next_sibling() depth -= 1 assert depth == thisnode.get_depth() if nextnode is None: # we're at the end of the tree return None else: return nextnode.get_widget() def prev_inorder(self): """Return the previous TreeWidget depth first from this one.""" thisnode = self._node prevnode = thisnode.prev_sibling() if prevnode is not None: # we need to find the last child of the previous widget if its # expanded prevwidget = prevnode.get_widget() lastchild = prevwidget.last_child() if lastchild is None: return prevwidget else: return lastchild else: # need to hunt for the parent depth = thisnode.get_depth() if prevnode is None and depth == 0: return None elif prevnode is None: prevnode = thisnode.get_parent() return prevnode.get_widget() def keypress(self, size, key): """Handle expand & collapse requests (non-leaf nodes)""" if self.is_leaf: return key if key in ("+", "right"): self.expanded = True self.update_expanded_icon() elif key == "-": self.expanded = False self.update_expanded_icon() elif self._w.selectable(): return self.__super.keypress(size, key) else: return key def mouse_event(self, size, event, button, col, row, focus): if self.is_leaf or event != 'mouse press' or button!=1: return False if row == 0 and col == self.get_indent_cols(): self.expanded = not self.expanded self.update_expanded_icon() return True return False def first_child(self): """Return first child if expanded.""" if self.is_leaf or not self.expanded: return None else: if self._node.has_children(): firstnode = self._node.get_first_child() return firstnode.get_widget() else: return None def last_child(self): """Return last child if expanded.""" if self.is_leaf or not self.expanded: return None else: if self._node.has_children(): lastchild = self._node.get_last_child().get_widget() else: return None # recursively search down for the last descendant lastdescendant = lastchild.last_child() if lastdescendant is None: return lastchild else: return lastdescendant class TreeNode(object): """ Store tree contents and cache TreeWidget objects. A TreeNode consists of the following elements: * key: accessor token for parent nodes * value: subclass-specific data * parent: a TreeNode which contains a pointer back to this object * widget: The widget used to render the object """ def __init__(self, value, parent=None, key=None, depth=None): self._key = key self._parent = parent self._value = value self._depth = depth self._widget = None def get_widget(self, reload=False): """ Return the widget for this node.""" if self._widget is None or reload == True: self._widget = self.load_widget() return self._widget def load_widget(self): return TreeWidget(self) def get_depth(self): if self._depth is None and self._parent is None: self._depth = 0 elif self._depth is None: self._depth = self._parent.get_depth() + 1 return self._depth def get_index(self): if self.get_depth() == 0: return None else: key = self.get_key() parent = self.get_parent() return parent.get_child_index(key) def get_key(self): return self._key def set_key(self, key): self._key = key def change_key(self, key): self.get_parent().change_child_key(self._key, key) def get_parent(self): if self._parent == None and self.get_depth() > 0: self._parent = self.load_parent() return self._parent def load_parent(self): """Provide TreeNode with a parent for the current node. This function is only required if the tree was instantiated from a child node (virtual function)""" raise TreeWidgetError("virtual function. Implement in subclass") def get_value(self): return self._value def is_root(self): return self.get_depth() == 0 def next_sibling(self): if self.get_depth() > 0: return self.get_parent().next_child(self.get_key()) else: return None def prev_sibling(self): if self.get_depth() > 0: return self.get_parent().prev_child(self.get_key()) else: return None def get_root(self): root = self while root.get_parent() is not None: root = root.get_parent() return root class ParentNode(TreeNode): """Maintain sort order for TreeNodes.""" def __init__(self, value, parent=None, key=None, depth=None): TreeNode.__init__(self, value, parent=parent, key=key, depth=depth) self._child_keys = None self._children = {} def get_child_keys(self, reload=False): """Return a possibly ordered list of child keys""" if self._child_keys is None or reload == True: self._child_keys = self.load_child_keys() return self._child_keys def load_child_keys(self): """Provide ParentNode with an ordered list of child keys (virtual function)""" raise TreeWidgetError("virtual function. Implement in subclass") def get_child_widget(self, key): """Return the widget for a given key. Create if necessary.""" child = self.get_child_node(key) return child.get_widget() def get_child_node(self, key, reload=False): """Return the child node for a given key. Create if necessary.""" if key not in self._children or reload == True: self._children[key] = self.load_child_node(key) return self._children[key] def load_child_node(self, key): """Load the child node for a given key (virtual function)""" raise TreeWidgetError("virtual function. Implement in subclass") def set_child_node(self, key, node): """Set the child node for a given key. Useful for bottom-up, lazy population of a tree..""" self._children[key]=node def change_child_key(self, oldkey, newkey): if newkey in self._children: raise TreeWidgetError("%s is already in use" % newkey) self._children[newkey] = self._children.pop(oldkey) self._children[newkey].set_key(newkey) def get_child_index(self, key): try: return self.get_child_keys().index(key) except ValueError: errorstring = ("Can't find key %s in ParentNode %s\n" + "ParentNode items: %s") raise TreeWidgetError(errorstring % (key, self.get_key(), str(self.get_child_keys()))) def next_child(self, key): """Return the next child node in index order from the given key.""" index = self.get_child_index(key) # the given node may have just been deleted if index is None: return None index += 1 child_keys = self.get_child_keys() if index < len(child_keys): # get the next item at same level return self.get_child_node(child_keys[index]) else: return None def prev_child(self, key): """Return the previous child node in index order from the given key.""" index = self.get_child_index(key) if index is None: return None child_keys = self.get_child_keys() index -= 1 if index >= 0: # get the previous item at same level return self.get_child_node(child_keys[index]) else: return None def get_first_child(self): """Return the first TreeNode in the directory.""" child_keys = self.get_child_keys() return self.get_child_node(child_keys[0]) def get_last_child(self): """Return the last TreeNode in the directory.""" child_keys = self.get_child_keys() return self.get_child_node(child_keys[-1]) def has_children(self): """Does this node have any children?""" return len(self.get_child_keys())>0 class TreeWalker(urwid.ListWalker): """ListWalker-compatible class for displaying TreeWidgets positions are TreeNodes.""" def __init__(self, start_from): """start_from: TreeNode with the initial focus.""" self.focus = start_from def get_focus(self): widget = self.focus.get_widget() return widget, self.focus def set_focus(self, focus): self.focus = focus self._modified() def get_next(self, start_from): widget = start_from.get_widget() target = widget.next_inorder() if target is None: return None, None else: return target, target.get_node() def get_prev(self, start_from): widget = start_from.get_widget() target = widget.prev_inorder() if target is None: return None, None else: return target, target.get_node() class TreeListBox(urwid.ListBox): """A ListBox with special handling for navigation and collapsing of TreeWidgets""" def keypress(self, size, key): key = self.__super.keypress(size, key) return self.unhandled_input(size, key) def unhandled_input(self, size, input): """Handle macro-navigation keys""" if input == 'left': self.move_focus_to_parent(size) elif input == '-': self.collapse_focus_parent(size) elif input == 'home': self.focus_home(size) elif input == 'end': self.focus_end(size) else: return input def collapse_focus_parent(self, size): """Collapse parent directory.""" widget, pos = self.body.get_focus() self.move_focus_to_parent(size) pwidget, ppos = self.body.get_focus() if pos != ppos: self.keypress(size, "-") def move_focus_to_parent(self, size): """Move focus to parent of widget in focus.""" widget, pos = self.body.get_focus() parentpos = pos.get_parent() if parentpos is None: return middle, top, bottom = self.calculate_visible( size ) row_offset, focus_widget, focus_pos, focus_rows, cursor = middle trim_top, fill_above = top for widget, pos, rows in fill_above: row_offset -= rows if pos == parentpos: self.change_focus(size, pos, row_offset) return self.change_focus(size, pos.get_parent()) def focus_home(self, size): """Move focus to very top.""" widget, pos = self.body.get_focus() rootnode = pos.get_root() self.change_focus(size, rootnode) def focus_end( self, size ): """Move focus to far bottom.""" maxrow, maxcol = size widget, pos = self.body.get_focus() rootnode = pos.get_root() rootwidget = rootnode.get_widget() lastwidget = rootwidget.last_child() lastnode = lastwidget.get_node() self.change_focus(size, lastnode, maxrow-1) urwid-1.3.1/urwid/listbox.py0000664000175000017500000016426112615524560015420 0ustar ianian00000000000000#!/usr/bin/python # # Urwid listbox class # Copyright (C) 2004-2012 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ from urwid.util import is_mouse_press from urwid.canvas import SolidCanvas, CanvasCombine from urwid.widget import Widget, nocache_widget_render_instance, BOX, GIVEN from urwid.decoration import calculate_top_bottom_filler, normalize_valign from urwid import signals from urwid.signals import connect_signal from urwid.monitored_list import MonitoredList, MonitoredFocusList from urwid.container import WidgetContainerMixin from urwid.command_map import (CURSOR_UP, CURSOR_DOWN, CURSOR_PAGE_UP, CURSOR_PAGE_DOWN) class ListWalkerError(Exception): pass class ListWalker(object): __metaclass__ = signals.MetaSignals signals = ["modified"] def _modified(self): signals.emit_signal(self, "modified") def get_focus(self): """ This default implementation relies on a focus attribute and a __getitem__() method defined in a subclass. Override and don't call this method if these are not defined. """ try: focus = self.focus return self[focus], focus except (IndexError, KeyError, TypeError): return None, None def get_next(self, position): """ This default implementation relies on a next_position() method and a __getitem__() method defined in a subclass. Override and don't call this method if these are not defined. """ try: position = self.next_position(position) return self[position], position except (IndexError, KeyError): return None, None def get_prev(self, position): """ This default implementation relies on a prev_position() method and a __getitem__() method defined in a subclass. Override and don't call this method if these are not defined. """ try: position = self.prev_position(position) return self[position], position except (IndexError, KeyError): return None, None class PollingListWalker(object): # NOT ListWalker subclass def __init__(self, contents): """ contents -- list to poll for changes This class is deprecated. Use SimpleFocusListWalker instead. """ import warnings warnings.warn("PollingListWalker is deprecated, " "use SimpleFocusListWalker instead.", DeprecationWarning) self.contents = contents if not getattr(contents, '__getitem__', None): raise ListWalkerError("PollingListWalker expecting list like " "object, got: %r" % (contents,)) self.focus = 0 def _clamp_focus(self): if self.focus >= len(self.contents): self.focus = len(self.contents)-1 def get_focus(self): """Return (focus widget, focus position).""" if len(self.contents) == 0: return None, None self._clamp_focus() return self.contents[self.focus], self.focus def set_focus(self, position): """Set focus position.""" # this class is deprecated, otherwise I might have fixed this: assert type(position) == int self.focus = position def get_next(self, start_from): """ Return (widget after start_from, position after start_from). """ pos = start_from + 1 if len(self.contents) <= pos: return None, None return self.contents[pos],pos def get_prev(self, start_from): """ Return (widget before start_from, position before start_from). """ pos = start_from - 1 if pos < 0: return None, None return self.contents[pos],pos class SimpleListWalker(MonitoredList, ListWalker): def __init__(self, contents): """ contents -- list to copy into this object Changes made to this object (when it is treated as a list) are detected automatically and will cause ListBox objects using this list walker to be updated. """ if not getattr(contents, '__getitem__', None): raise ListWalkerError, "SimpleListWalker expecting list like object, got: %r"%(contents,) MonitoredList.__init__(self, contents) self.focus = 0 def _get_contents(self): """ Return self. Provides compatibility with old SimpleListWalker class. """ return self contents = property(_get_contents) def _modified(self): if self.focus >= len(self): self.focus = max(0, len(self)-1) ListWalker._modified(self) def set_modified_callback(self, callback): """ This function inherited from MonitoredList is not implemented in SimpleListWalker. Use connect_signal(list_walker, "modified", ...) instead. """ raise NotImplementedError('Use connect_signal(' 'list_walker, "modified", ...) instead.') def set_focus(self, position): """Set focus position.""" try: if position < 0 or position >= len(self): raise ValueError except (TypeError, ValueError): raise IndexError, "No widget at position %s" % (position,) self.focus = position self._modified() def next_position(self, position): """ Return position after start_from. """ if len(self) - 1 <= position: raise IndexError return position + 1 def prev_position(self, position): """ Return position before start_from. """ if position <= 0: raise IndexError return position - 1 def positions(self, reverse=False): """ Optional method for returning an iterable of positions. """ if reverse: return xrange(len(self) - 1, -1, -1) return xrange(len(self)) class SimpleFocusListWalker(ListWalker, MonitoredFocusList): def __init__(self, contents): """ contents -- list to copy into this object Changes made to this object (when it is treated as a list) are detected automatically and will cause ListBox objects using this list walker to be updated. Also, items added or removed before the widget in focus with normal list methods will cause the focus to be updated intelligently. """ if not getattr(contents, '__getitem__', None): raise ListWalkerError("SimpleFocusListWalker expecting list like " "object, got: %r"%(contents,)) MonitoredFocusList.__init__(self, contents) def set_modified_callback(self, callback): """ This function inherited from MonitoredList is not implemented in SimpleFocusListWalker. Use connect_signal(list_walker, "modified", ...) instead. """ raise NotImplementedError('Use connect_signal(' 'list_walker, "modified", ...) instead.') def set_focus(self, position): """Set focus position.""" self.focus = position def next_position(self, position): """ Return position after start_from. """ if len(self) - 1 <= position: raise IndexError return position + 1 def prev_position(self, position): """ Return position before start_from. """ if position <= 0: raise IndexError return position - 1 def positions(self, reverse=False): """ Optional method for returning an iterable of positions. """ if reverse: return xrange(len(self) - 1, -1, -1) return xrange(len(self)) class ListBoxError(Exception): pass class ListBox(Widget, WidgetContainerMixin): """ a horizontally stacked list of widgets """ _selectable = True _sizing = frozenset([BOX]) def __init__(self, body): """ :param body: a ListWalker subclass such as :class:`SimpleFocusListWalker` that contains widgets to be displayed inside the list box :type body: ListWalker """ if getattr(body, 'get_focus', None): self.body = body else: self.body = PollingListWalker(body) try: connect_signal(self.body, "modified", self._invalidate) except NameError: # our list walker has no modified signal so we must not # cache our canvases because we don't know when our # content has changed self.render = nocache_widget_render_instance(self) # offset_rows is the number of rows between the top of the view # and the top of the focused item self.offset_rows = 0 # inset_fraction is used when the focused widget is off the # top of the view. it is the fraction of the widget cut off # at the top. (numerator, denominator) self.inset_fraction = (0,1) # pref_col is the preferred column for the cursor when moving # between widgets that use the cursor (edit boxes etc.) self.pref_col = 'left' # variable for delayed focus change used by set_focus self.set_focus_pending = 'first selectable' # variable for delayed valign change used by set_focus_valign self.set_focus_valign_pending = None def calculate_visible(self, size, focus=False ): """ Returns the widgets that would be displayed in the ListBox given the current *size* and *focus*. see :meth:`Widget.render` for parameter details :returns: (*middle*, *top*, *bottom*) or (``None``, ``None``, ``None``) *middle* (*row offset*(when +ve) or *inset*(when -ve), *focus widget*, *focus position*, *focus rows*, *cursor coords* or ``None``) *top* (*# lines to trim off top*, list of (*widget*, *position*, *rows*) tuples above focus in order from bottom to top) *bottom* (*# lines to trim off bottom*, list of (*widget*, *position*, *rows*) tuples below focus in order from top to bottom) """ (maxcol, maxrow) = size # 0. set the focus if a change is pending if self.set_focus_pending or self.set_focus_valign_pending: self._set_focus_complete( (maxcol, maxrow), focus ) # 1. start with the focus widget focus_widget, focus_pos = self.body.get_focus() if focus_widget is None: #list box is empty? return None,None,None top_pos = focus_pos offset_rows, inset_rows = self.get_focus_offset_inset( (maxcol,maxrow)) # force at least one line of focus to be visible if maxrow and offset_rows >= maxrow: offset_rows = maxrow -1 # adjust position so cursor remains visible cursor = None if maxrow and focus_widget.selectable() and focus: if hasattr(focus_widget,'get_cursor_coords'): cursor=focus_widget.get_cursor_coords((maxcol,)) if cursor is not None: cx, cy = cursor effective_cy = cy + offset_rows - inset_rows if effective_cy < 0: # cursor above top? inset_rows = cy elif effective_cy >= maxrow: # cursor below bottom? offset_rows = maxrow - cy -1 if offset_rows < 0: # need to trim the top inset_rows, offset_rows = -offset_rows, 0 # set trim_top by focus trimmimg trim_top = inset_rows focus_rows = focus_widget.rows((maxcol,),True) # 2. collect the widgets above the focus pos = focus_pos fill_lines = offset_rows fill_above = [] top_pos = pos while fill_lines > 0: prev, pos = self.body.get_prev( pos ) if prev is None: # run out of widgets above? offset_rows -= fill_lines break top_pos = pos p_rows = prev.rows( (maxcol,) ) if p_rows: # filter out 0-height widgets fill_above.append( (prev, pos, p_rows) ) if p_rows > fill_lines: # crosses top edge? trim_top = p_rows-fill_lines break fill_lines -= p_rows trim_bottom = focus_rows + offset_rows - inset_rows - maxrow if trim_bottom < 0: trim_bottom = 0 # 3. collect the widgets below the focus pos = focus_pos fill_lines = maxrow - focus_rows - offset_rows + inset_rows fill_below = [] while fill_lines > 0: next, pos = self.body.get_next( pos ) if next is None: # run out of widgets below? break n_rows = next.rows( (maxcol,) ) if n_rows: # filter out 0-height widgets fill_below.append( (next, pos, n_rows) ) if n_rows > fill_lines: # crosses bottom edge? trim_bottom = n_rows-fill_lines fill_lines -= n_rows break fill_lines -= n_rows # 4. fill from top again if necessary & possible fill_lines = max(0, fill_lines) if fill_lines >0 and trim_top >0: if fill_lines <= trim_top: trim_top -= fill_lines offset_rows += fill_lines fill_lines = 0 else: fill_lines -= trim_top offset_rows += trim_top trim_top = 0 pos = top_pos while fill_lines > 0: prev, pos = self.body.get_prev( pos ) if prev is None: break p_rows = prev.rows( (maxcol,) ) fill_above.append( (prev, pos, p_rows) ) if p_rows > fill_lines: # more than required trim_top = p_rows-fill_lines offset_rows += fill_lines break fill_lines -= p_rows offset_rows += p_rows # 5. return the interesting bits return ((offset_rows - inset_rows, focus_widget, focus_pos, focus_rows, cursor ), (trim_top, fill_above), (trim_bottom, fill_below)) def render(self, size, focus=False ): """ Render ListBox and return canvas. see :meth:`Widget.render` for details """ (maxcol, maxrow) = size middle, top, bottom = self.calculate_visible( (maxcol, maxrow), focus=focus) if middle is None: return SolidCanvas(" ", maxcol, maxrow) _ignore, focus_widget, focus_pos, focus_rows, cursor = middle trim_top, fill_above = top trim_bottom, fill_below = bottom combinelist = [] rows = 0 fill_above.reverse() # fill_above is in bottom-up order for widget,w_pos,w_rows in fill_above: canvas = widget.render((maxcol,)) if w_rows != canvas.rows(): raise ListBoxError, "Widget %r at position %r within listbox calculated %d rows but rendered %d!"% (widget,w_pos,w_rows, canvas.rows()) rows += w_rows combinelist.append((canvas, w_pos, False)) focus_canvas = focus_widget.render((maxcol,), focus=focus) if focus_canvas.rows() != focus_rows: raise ListBoxError, "Focus Widget %r at position %r within listbox calculated %d rows but rendered %d!"% (focus_widget,focus_pos,focus_rows, focus_canvas.rows()) c_cursor = focus_canvas.cursor if cursor != c_cursor: raise ListBoxError, "Focus Widget %r at position %r within listbox calculated cursor coords %r but rendered cursor coords %r!" %(focus_widget,focus_pos,cursor,c_cursor) rows += focus_rows combinelist.append((focus_canvas, focus_pos, True)) for widget,w_pos,w_rows in fill_below: canvas = widget.render((maxcol,)) if w_rows != canvas.rows(): raise ListBoxError, "Widget %r at position %r within listbox calculated %d rows but rendered %d!"% (widget,w_pos,w_rows, canvas.rows()) rows += w_rows combinelist.append((canvas, w_pos, False)) final_canvas = CanvasCombine(combinelist) if trim_top: final_canvas.trim(trim_top) rows -= trim_top if trim_bottom: final_canvas.trim_end(trim_bottom) rows -= trim_bottom if rows > maxrow: raise ListBoxError, "Listbox contents too long! Probably urwid's fault (please report): %r" % ((top,middle,bottom),) if rows < maxrow: bottom_pos = focus_pos if fill_below: bottom_pos = fill_below[-1][1] if trim_bottom != 0 or self.body.get_next(bottom_pos) != (None,None): raise ListBoxError, "Listbox contents too short! Probably urwid's fault (please report): %r" % ((top,middle,bottom),) final_canvas.pad_trim_top_bottom(0, maxrow - rows) return final_canvas def get_cursor_coords(self, size): """ See :meth:`Widget.get_cursor_coords` for details """ (maxcol, maxrow) = size middle, top, bottom = self.calculate_visible( (maxcol, maxrow), True) if middle is None: return None offset_inset, _ignore1, _ignore2, _ignore3, cursor = middle if not cursor: return None x, y = cursor y += offset_inset if y < 0 or y >= maxrow: return None return (x, y) def set_focus_valign(self, valign): """Set the focus widget's display offset and inset. :param valign: one of: 'top', 'middle', 'bottom' ('fixed top', rows) ('fixed bottom', rows) ('relative', percentage 0=top 100=bottom) """ vt, va = normalize_valign(valign,ListBoxError) self.set_focus_valign_pending = vt, va def set_focus(self, position, coming_from=None): """ Set the focus position and try to keep the old focus in view. :param position: a position compatible with :meth:`self.body.set_focus` :param coming_from: set to 'above' or 'below' if you know that old position is above or below the new position. :type coming_from: str """ if coming_from not in ('above', 'below', None): raise ListBoxError("coming_from value invalid: %r" % (coming_from,)) focus_widget, focus_pos = self.body.get_focus() if focus_widget is None: raise IndexError("Can't set focus, ListBox is empty") self.set_focus_pending = coming_from, focus_widget, focus_pos self.body.set_focus(position) def get_focus(self): """ Return a `(focus widget, focus position)` tuple, for backwards compatibility. You may also use the new standard container properties :attr:`focus` and :attr:`focus_position` to read these values. """ return self.body.get_focus() def _get_focus(self): """ Return the widget in focus according to our :obj:`list walker `. """ return self.body.get_focus()[0] focus = property(_get_focus, doc="the child widget in focus or None when ListBox is empty") def _get_focus_position(self): """ Return the list walker position of the widget in focus. The type of value returned depends on the :obj:`list walker `. """ w, pos = self.body.get_focus() if w is None: raise IndexError, "No focus_position, ListBox is empty" return pos focus_position = property(_get_focus_position, set_focus, doc=""" the position of child widget in focus. The valid values for this position depend on the list walker in use. :exc:`IndexError` will be raised by reading this property when the ListBox is empty or setting this property to an invalid position. """) def _contents(self): class ListBoxContents(object): __getitem__ = self._contents__getitem__ return ListBoxContents() def _contents__getitem__(self, key): # try list walker protocol v2 first getitem = getattr(self.body, '__getitem__', None) if getitem: try: return (getitem(key), None) except (IndexError, KeyError): raise KeyError("ListBox.contents key not found: %r" % (key,)) # fall back to v1 w, old_focus = self.body.get_focus() try: try: self.body.set_focus(key) return self.body.get_focus()[0] except (IndexError, KeyError): raise KeyError("ListBox.contents key not found: %r" % (key,)) finally: self.body.set_focus(old_focus) contents = property(lambda self: self._contents, doc=""" An object that allows reading widgets from the ListBox's list walker as a `(widget, options)` tuple. `None` is currently the only value for options. .. warning:: This object may not be used to set or iterate over contents. You must use the list walker stored as :attr:`.body` to perform manipulation and iteration, if supported. """) def options(self): """ There are currently no options for ListBox contents. Return None as a placeholder for future options. """ return None def _set_focus_valign_complete(self, size, focus): """ Finish setting the offset and inset now that we have have a maxcol & maxrow. """ (maxcol, maxrow) = size vt,va = self.set_focus_valign_pending self.set_focus_valign_pending = None self.set_focus_pending = None focus_widget, focus_pos = self.body.get_focus() if focus_widget is None: return rows = focus_widget.rows((maxcol,), focus) rtop, rbot = calculate_top_bottom_filler(maxrow, vt, va, GIVEN, rows, None, 0, 0) self.shift_focus((maxcol, maxrow), rtop) def _set_focus_first_selectable(self, size, focus): """ Choose the first visible, selectable widget below the current focus as the focus widget. """ (maxcol, maxrow) = size self.set_focus_valign_pending = None self.set_focus_pending = None middle, top, bottom = self.calculate_visible( (maxcol, maxrow), focus=focus) if middle is None: return row_offset, focus_widget, focus_pos, focus_rows, cursor = middle trim_top, fill_above = top trim_bottom, fill_below = bottom if focus_widget.selectable(): return if trim_bottom: fill_below = fill_below[:-1] new_row_offset = row_offset + focus_rows for widget, pos, rows in fill_below: if widget.selectable(): self.body.set_focus(pos) self.shift_focus((maxcol, maxrow), new_row_offset) return new_row_offset += rows def _set_focus_complete(self, size, focus): """ Finish setting the position now that we have maxcol & maxrow. """ (maxcol, maxrow) = size self._invalidate() if self.set_focus_pending == "first selectable": return self._set_focus_first_selectable( (maxcol,maxrow), focus) if self.set_focus_valign_pending is not None: return self._set_focus_valign_complete( (maxcol,maxrow), focus) coming_from, focus_widget, focus_pos = self.set_focus_pending self.set_focus_pending = None # new position new_focus_widget, position = self.body.get_focus() if focus_pos == position: # do nothing return # restore old focus temporarily self.body.set_focus(focus_pos) middle,top,bottom=self.calculate_visible((maxcol,maxrow),focus) focus_offset, focus_widget, focus_pos, focus_rows, cursor=middle trim_top, fill_above = top trim_bottom, fill_below = bottom offset = focus_offset for widget, pos, rows in fill_above: offset -= rows if pos == position: self.change_focus((maxcol, maxrow), pos, offset, 'below' ) return offset = focus_offset + focus_rows for widget, pos, rows in fill_below: if pos == position: self.change_focus((maxcol, maxrow), pos, offset, 'above' ) return offset += rows # failed to find widget among visible widgets self.body.set_focus( position ) widget, position = self.body.get_focus() rows = widget.rows((maxcol,), focus) if coming_from=='below': offset = 0 elif coming_from=='above': offset = maxrow-rows else: offset = (maxrow-rows) // 2 self.shift_focus((maxcol, maxrow), offset) def shift_focus(self, size, offset_inset): """ Move the location of the current focus relative to the top. This is used internally by methods that know the widget's *size*. See also :meth:`.set_focus_valign`. :param size: see :meth:`Widget.render` for details :param offset_inset: either the number of rows between the top of the listbox and the start of the focus widget (+ve value) or the number of lines of the focus widget hidden off the top edge of the listbox (-ve value) or ``0`` if the top edge of the focus widget is aligned with the top edge of the listbox. :type offset_inset: int """ (maxcol, maxrow) = size if offset_inset >= 0: if offset_inset >= maxrow: raise ListBoxError, "Invalid offset_inset: %r, only %r rows in list box"% (offset_inset, maxrow) self.offset_rows = offset_inset self.inset_fraction = (0,1) else: target, _ignore = self.body.get_focus() tgt_rows = target.rows( (maxcol,), True ) if offset_inset + tgt_rows <= 0: raise ListBoxError, "Invalid offset_inset: %r, only %r rows in target!" %(offset_inset, tgt_rows) self.offset_rows = 0 self.inset_fraction = (-offset_inset,tgt_rows) self._invalidate() def update_pref_col_from_focus(self, size): """Update self.pref_col from the focus widget.""" # TODO: should this not be private? (maxcol, maxrow) = size widget, old_pos = self.body.get_focus() if widget is None: return pref_col = None if hasattr(widget,'get_pref_col'): pref_col = widget.get_pref_col((maxcol,)) if pref_col is None and hasattr(widget,'get_cursor_coords'): coords = widget.get_cursor_coords((maxcol,)) if type(coords) == tuple: pref_col,y = coords if pref_col is not None: self.pref_col = pref_col def change_focus(self, size, position, offset_inset = 0, coming_from = None, cursor_coords = None, snap_rows = None): """ Change the current focus widget. This is used internally by methods that know the widget's *size*. See also :meth:`.set_focus`. :param size: see :meth:`Widget.render` for details :param position: a position compatible with :meth:`self.body.set_focus` :param offset_inset: either the number of rows between the top of the listbox and the start of the focus widget (+ve value) or the number of lines of the focus widget hidden off the top edge of the listbox (-ve value) or 0 if the top edge of the focus widget is aligned with the top edge of the listbox (default if unspecified) :type offset_inset: int :param coming_from: either 'above', 'below' or unspecified `None` :type coming_from: str :param cursor_coords: (x, y) tuple indicating the desired column and row for the cursor, a (x,) tuple indicating only the column for the cursor, or unspecified :type cursor_coords: (int, int) :param snap_rows: the maximum number of extra rows to scroll when trying to "snap" a selectable focus into the view :type snap_rows: int """ (maxcol, maxrow) = size # update pref_col before change if cursor_coords: self.pref_col = cursor_coords[0] else: self.update_pref_col_from_focus((maxcol,maxrow)) self._invalidate() self.body.set_focus(position) target, _ignore = self.body.get_focus() tgt_rows = target.rows( (maxcol,), True) if snap_rows is None: snap_rows = maxrow - 1 # "snap" to selectable widgets align_top = 0 align_bottom = maxrow - tgt_rows if ( coming_from == 'above' and target.selectable() and offset_inset > align_bottom ): if snap_rows >= offset_inset - align_bottom: offset_inset = align_bottom elif snap_rows >= offset_inset - align_top: offset_inset = align_top else: offset_inset -= snap_rows if ( coming_from == 'below' and target.selectable() and offset_inset < align_top ): if snap_rows >= align_top - offset_inset: offset_inset = align_top elif snap_rows >= align_bottom - offset_inset: offset_inset = align_bottom else: offset_inset += snap_rows # convert offset_inset to offset_rows or inset_fraction if offset_inset >= 0: self.offset_rows = offset_inset self.inset_fraction = (0,1) else: if offset_inset + tgt_rows <= 0: raise ListBoxError, "Invalid offset_inset: %s, only %s rows in target!" %(offset_inset, tgt_rows) self.offset_rows = 0 self.inset_fraction = (-offset_inset,tgt_rows) if cursor_coords is None: if coming_from is None: return # must either know row or coming_from cursor_coords = (self.pref_col,) if not hasattr(target,'move_cursor_to_coords'): return attempt_rows = [] if len(cursor_coords) == 1: # only column (not row) specified # start from closest edge and move inwards (pref_col,) = cursor_coords if coming_from=='above': attempt_rows = range( 0, tgt_rows ) else: assert coming_from == 'below', "must specify coming_from ('above' or 'below') if cursor row is not specified" attempt_rows = range( tgt_rows, -1, -1) else: # both column and row specified # start from preferred row and move back to closest edge (pref_col, pref_row) = cursor_coords if pref_row < 0 or pref_row >= tgt_rows: raise ListBoxError, "cursor_coords row outside valid range for target. pref_row:%r target_rows:%r"%(pref_row,tgt_rows) if coming_from=='above': attempt_rows = range( pref_row, -1, -1 ) elif coming_from=='below': attempt_rows = range( pref_row, tgt_rows ) else: attempt_rows = [pref_row] for row in attempt_rows: if target.move_cursor_to_coords((maxcol,),pref_col,row): break def get_focus_offset_inset(self, size): """Return (offset rows, inset rows) for focus widget.""" (maxcol, maxrow) = size focus_widget, pos = self.body.get_focus() focus_rows = focus_widget.rows((maxcol,), True) offset_rows = self.offset_rows inset_rows = 0 if offset_rows == 0: inum, iden = self.inset_fraction if inum < 0 or iden < 0 or inum >= iden: raise ListBoxError, "Invalid inset_fraction: %r"%(self.inset_fraction,) inset_rows = focus_rows * inum // iden if inset_rows and inset_rows >= focus_rows: raise ListBoxError, "urwid inset_fraction error (please report)" return offset_rows, inset_rows def make_cursor_visible(self, size): """Shift the focus widget so that its cursor is visible.""" (maxcol, maxrow) = size focus_widget, pos = self.body.get_focus() if focus_widget is None: return if not focus_widget.selectable(): return if not hasattr(focus_widget,'get_cursor_coords'): return cursor = focus_widget.get_cursor_coords((maxcol,)) if cursor is None: return cx, cy = cursor offset_rows, inset_rows = self.get_focus_offset_inset( (maxcol, maxrow)) if cy < inset_rows: self.shift_focus( (maxcol,maxrow), - (cy) ) return if offset_rows - inset_rows + cy >= maxrow: self.shift_focus( (maxcol,maxrow), maxrow-cy-1 ) return def keypress(self, size, key): """Move selection through the list elements scrolling when necessary. 'up' and 'down' are first passed to widget in focus in case that widget can handle them. 'page up' and 'page down' are always handled by the ListBox. Keystrokes handled by this widget are: 'up' up one line (or widget) 'down' down one line (or widget) 'page up' move cursor up one listbox length 'page down' move cursor down one listbox length """ (maxcol, maxrow) = size if self.set_focus_pending or self.set_focus_valign_pending: self._set_focus_complete( (maxcol,maxrow), focus=True ) focus_widget, pos = self.body.get_focus() if focus_widget is None: # empty listbox, can't do anything return key if self._command_map[key] not in [CURSOR_PAGE_UP, CURSOR_PAGE_DOWN]: if focus_widget.selectable(): key = focus_widget.keypress((maxcol,),key) if key is None: self.make_cursor_visible((maxcol,maxrow)) return def actual_key(unhandled): if unhandled: return key # pass off the heavy lifting if self._command_map[key] == CURSOR_UP: return actual_key(self._keypress_up((maxcol, maxrow))) if self._command_map[key] == CURSOR_DOWN: return actual_key(self._keypress_down((maxcol, maxrow))) if self._command_map[key] == CURSOR_PAGE_UP: return actual_key(self._keypress_page_up((maxcol, maxrow))) if self._command_map[key] == CURSOR_PAGE_DOWN: return actual_key(self._keypress_page_down((maxcol, maxrow))) return key def _keypress_up(self, size): (maxcol, maxrow) = size middle, top, bottom = self.calculate_visible( (maxcol,maxrow), True) if middle is None: return True focus_row_offset,focus_widget,focus_pos,_ignore,cursor = middle trim_top, fill_above = top row_offset = focus_row_offset # look for selectable widget above pos = focus_pos widget = None for widget, pos, rows in fill_above: row_offset -= rows if rows and widget.selectable(): # this one will do self.change_focus((maxcol,maxrow), pos, row_offset, 'below') return # at this point we must scroll row_offset += 1 self._invalidate() while row_offset > 0: # need to scroll in another candidate widget widget, pos = self.body.get_prev(pos) if widget is None: # cannot scroll any further return True # keypress not handled rows = widget.rows((maxcol,), True) row_offset -= rows if rows and widget.selectable(): # this one will do self.change_focus((maxcol,maxrow), pos, row_offset, 'below') return if not focus_widget.selectable() or focus_row_offset+1>=maxrow: # just take top one if focus is not selectable # or if focus has moved out of view if widget is None: self.shift_focus((maxcol,maxrow), row_offset) return self.change_focus((maxcol,maxrow), pos, row_offset, 'below') return # check if cursor will stop scroll from taking effect if cursor is not None: x,y = cursor if y+focus_row_offset+1 >= maxrow: # cursor position is a problem, # choose another focus if widget is None: # try harder to get prev widget widget, pos = self.body.get_prev(pos) if widget is None: return # can't do anything rows = widget.rows((maxcol,), True) row_offset -= rows if -row_offset >= rows: # must scroll further than 1 line row_offset = - (rows-1) self.change_focus((maxcol,maxrow),pos, row_offset, 'below') return # if all else fails, just shift the current focus. self.shift_focus((maxcol,maxrow), focus_row_offset+1) def _keypress_down(self, size): (maxcol, maxrow) = size middle, top, bottom = self.calculate_visible( (maxcol,maxrow), True) if middle is None: return True focus_row_offset,focus_widget,focus_pos,focus_rows,cursor=middle trim_bottom, fill_below = bottom row_offset = focus_row_offset + focus_rows rows = focus_rows # look for selectable widget below pos = focus_pos widget = None for widget, pos, rows in fill_below: if rows and widget.selectable(): # this one will do self.change_focus((maxcol,maxrow), pos, row_offset, 'above') return row_offset += rows # at this point we must scroll row_offset -= 1 self._invalidate() while row_offset < maxrow: # need to scroll in another candidate widget widget, pos = self.body.get_next(pos) if widget is None: # cannot scroll any further return True # keypress not handled rows = widget.rows((maxcol,)) if rows and widget.selectable(): # this one will do self.change_focus((maxcol,maxrow), pos, row_offset, 'above') return row_offset += rows if not focus_widget.selectable() or focus_row_offset+focus_rows-1 <= 0: # just take bottom one if current is not selectable # or if focus has moved out of view if widget is None: self.shift_focus((maxcol,maxrow), row_offset-rows) return # FIXME: catch this bug in testcase #self.change_focus((maxcol,maxrow), pos, # row_offset+rows, 'above') self.change_focus((maxcol,maxrow), pos, row_offset-rows, 'above') return # check if cursor will stop scroll from taking effect if cursor is not None: x,y = cursor if y+focus_row_offset-1 < 0: # cursor position is a problem, # choose another focus if widget is None: # try harder to get next widget widget, pos = self.body.get_next(pos) if widget is None: return # can't do anything else: row_offset -= rows if row_offset >= maxrow: # must scroll further than 1 line row_offset = maxrow-1 self.change_focus((maxcol,maxrow),pos, row_offset, 'above', ) return # if all else fails, keep the current focus. self.shift_focus((maxcol,maxrow), focus_row_offset-1) def _keypress_page_up(self, size): (maxcol, maxrow) = size middle, top, bottom = self.calculate_visible( (maxcol,maxrow), True) if middle is None: return True row_offset, focus_widget, focus_pos, focus_rows, cursor = middle trim_top, fill_above = top # topmost_visible is row_offset rows above top row of # focus (+ve) or -row_offset rows below top row of focus (-ve) topmost_visible = row_offset # scroll_from_row is (first match) # 1. topmost visible row if focus is not selectable # 2. row containing cursor if focus has a cursor # 3. top row of focus widget if it is visible # 4. topmost visible row otherwise if not focus_widget.selectable(): scroll_from_row = topmost_visible elif cursor is not None: x,y = cursor scroll_from_row = -y elif row_offset >= 0: scroll_from_row = 0 else: scroll_from_row = topmost_visible # snap_rows is maximum extra rows to scroll when # snapping to new a focus snap_rows = topmost_visible - scroll_from_row # move row_offset to the new desired value (1 "page" up) row_offset = scroll_from_row + maxrow # not used below: scroll_from_row = topmost_visible = None # gather potential target widgets t = [] # add current focus t.append((row_offset,focus_widget,focus_pos,focus_rows)) pos = focus_pos # include widgets from calculate_visible(..) for widget, pos, rows in fill_above: row_offset -= rows t.append( (row_offset, widget, pos, rows) ) # add newly visible ones, including within snap_rows snap_region_start = len(t) while row_offset > -snap_rows: widget, pos = self.body.get_prev(pos) if widget is None: break rows = widget.rows((maxcol,)) row_offset -= rows # determine if one below puts current one into snap rgn if row_offset > 0: snap_region_start += 1 t.append( (row_offset, widget, pos, rows) ) # if we can't fill the top we need to adjust the row offsets row_offset, w, p, r = t[-1] if row_offset > 0: adjust = - row_offset t = [(ro+adjust, w, p, r) for (ro,w,p,r) in t] # if focus_widget (first in t) is off edge, remove it row_offset, w, p, r = t[0] if row_offset >= maxrow: del t[0] snap_region_start -= 1 # we'll need this soon self.update_pref_col_from_focus((maxcol,maxrow)) # choose the topmost selectable and (newly) visible widget # search within snap_rows then visible region search_order = ( range( snap_region_start, len(t)) + range( snap_region_start-1, -1, -1 ) ) #assert 0, repr((t, search_order)) bad_choices = [] cut_off_selectable_chosen = 0 for i in search_order: row_offset, widget, pos, rows = t[i] if not widget.selectable(): continue if not rows: continue # try selecting this widget pref_row = max(0, -row_offset) # if completely within snap region, adjust row_offset if rows + row_offset <= 0: self.change_focus( (maxcol,maxrow), pos, -(rows-1), 'below', (self.pref_col, rows-1), snap_rows-((-row_offset)-(rows-1))) else: self.change_focus( (maxcol,maxrow), pos, row_offset, 'below', (self.pref_col, pref_row), snap_rows ) # if we're as far up as we can scroll, take this one if (fill_above and self.body.get_prev(fill_above[-1][1]) == (None,None) ): pass #return # find out where that actually puts us middle, top, bottom = self.calculate_visible( (maxcol,maxrow), True) act_row_offset, _ign1, _ign2, _ign3, _ign4 = middle # discard chosen widget if it will reduce scroll amount # because of a fixed cursor (absolute last resort) if act_row_offset > row_offset+snap_rows: bad_choices.append(i) continue if act_row_offset < row_offset: bad_choices.append(i) continue # also discard if off top edge (second last resort) if act_row_offset < 0: bad_choices.append(i) cut_off_selectable_chosen = 1 continue return # anything selectable is better than what follows: if cut_off_selectable_chosen: return if fill_above and focus_widget.selectable(): # if we're at the top and have a selectable, return if self.body.get_prev(fill_above[-1][1]) == (None,None): pass #return # if still none found choose the topmost widget good_choices = [j for j in search_order if j not in bad_choices] for i in good_choices + search_order: row_offset, widget, pos, rows = t[i] if pos == focus_pos: continue if not rows: # never focus a 0-height widget continue # if completely within snap region, adjust row_offset if rows + row_offset <= 0: snap_rows -= (-row_offset) - (rows-1) row_offset = -(rows-1) self.change_focus( (maxcol,maxrow), pos, row_offset, 'below', None, snap_rows ) return # no choices available, just shift current one self.shift_focus((maxcol, maxrow), min(maxrow-1,row_offset)) # final check for pathological case where we may fall short middle, top, bottom = self.calculate_visible( (maxcol,maxrow), True) act_row_offset, _ign1, pos, _ign2, _ign3 = middle if act_row_offset >= row_offset: # no problem return # fell short, try to select anything else above if not t: return _ign1, _ign2, pos, _ign3 = t[-1] widget, pos = self.body.get_prev(pos) if widget is None: # no dice, we're stuck here return # bring in only one row if possible rows = widget.rows((maxcol,), True) self.change_focus((maxcol,maxrow), pos, -(rows-1), 'below', (self.pref_col, rows-1), 0 ) def _keypress_page_down(self, size): (maxcol, maxrow) = size middle, top, bottom = self.calculate_visible( (maxcol,maxrow), True) if middle is None: return True row_offset, focus_widget, focus_pos, focus_rows, cursor = middle trim_bottom, fill_below = bottom # bottom_edge is maxrow-focus_pos rows below top row of focus bottom_edge = maxrow - row_offset # scroll_from_row is (first match) # 1. bottom edge if focus is not selectable # 2. row containing cursor + 1 if focus has a cursor # 3. bottom edge of focus widget if it is visible # 4. bottom edge otherwise if not focus_widget.selectable(): scroll_from_row = bottom_edge elif cursor is not None: x,y = cursor scroll_from_row = y + 1 elif bottom_edge >= focus_rows: scroll_from_row = focus_rows else: scroll_from_row = bottom_edge # snap_rows is maximum extra rows to scroll when # snapping to new a focus snap_rows = bottom_edge - scroll_from_row # move row_offset to the new desired value (1 "page" down) row_offset = -scroll_from_row # not used below: scroll_from_row = bottom_edge = None # gather potential target widgets t = [] # add current focus t.append((row_offset,focus_widget,focus_pos,focus_rows)) pos = focus_pos row_offset += focus_rows # include widgets from calculate_visible(..) for widget, pos, rows in fill_below: t.append( (row_offset, widget, pos, rows) ) row_offset += rows # add newly visible ones, including within snap_rows snap_region_start = len(t) while row_offset < maxrow+snap_rows: widget, pos = self.body.get_next(pos) if widget is None: break rows = widget.rows((maxcol,)) t.append( (row_offset, widget, pos, rows) ) row_offset += rows # determine if one above puts current one into snap rgn if row_offset < maxrow: snap_region_start += 1 # if we can't fill the bottom we need to adjust the row offsets row_offset, w, p, rows = t[-1] if row_offset + rows < maxrow: adjust = maxrow - (row_offset + rows) t = [(ro+adjust, w, p, r) for (ro,w,p,r) in t] # if focus_widget (first in t) is off edge, remove it row_offset, w, p, rows = t[0] if row_offset+rows <= 0: del t[0] snap_region_start -= 1 # we'll need this soon self.update_pref_col_from_focus((maxcol,maxrow)) # choose the bottommost selectable and (newly) visible widget # search within snap_rows then visible region search_order = ( range( snap_region_start, len(t)) + range( snap_region_start-1, -1, -1 ) ) #assert 0, repr((t, search_order)) bad_choices = [] cut_off_selectable_chosen = 0 for i in search_order: row_offset, widget, pos, rows = t[i] if not widget.selectable(): continue if not rows: continue # try selecting this widget pref_row = min(maxrow-row_offset-1, rows-1) # if completely within snap region, adjust row_offset if row_offset >= maxrow: self.change_focus( (maxcol,maxrow), pos, maxrow-1, 'above', (self.pref_col, 0), snap_rows+maxrow-row_offset-1 ) else: self.change_focus( (maxcol,maxrow), pos, row_offset, 'above', (self.pref_col, pref_row), snap_rows ) # find out where that actually puts us middle, top, bottom = self.calculate_visible( (maxcol,maxrow), True) act_row_offset, _ign1, _ign2, _ign3, _ign4 = middle # discard chosen widget if it will reduce scroll amount # because of a fixed cursor (absolute last resort) if act_row_offset < row_offset-snap_rows: bad_choices.append(i) continue if act_row_offset > row_offset: bad_choices.append(i) continue # also discard if off top edge (second last resort) if act_row_offset+rows > maxrow: bad_choices.append(i) cut_off_selectable_chosen = 1 continue return # anything selectable is better than what follows: if cut_off_selectable_chosen: return # if still none found choose the bottommost widget good_choices = [j for j in search_order if j not in bad_choices] for i in good_choices + search_order: row_offset, widget, pos, rows = t[i] if pos == focus_pos: continue if not rows: # never focus a 0-height widget continue # if completely within snap region, adjust row_offset if row_offset >= maxrow: snap_rows -= snap_rows+maxrow-row_offset-1 row_offset = maxrow-1 self.change_focus( (maxcol,maxrow), pos, row_offset, 'above', None, snap_rows ) return # no choices available, just shift current one self.shift_focus((maxcol, maxrow), max(1-focus_rows,row_offset)) # final check for pathological case where we may fall short middle, top, bottom = self.calculate_visible( (maxcol,maxrow), True) act_row_offset, _ign1, pos, _ign2, _ign3 = middle if act_row_offset <= row_offset: # no problem return # fell short, try to select anything else below if not t: return _ign1, _ign2, pos, _ign3 = t[-1] widget, pos = self.body.get_next(pos) if widget is None: # no dice, we're stuck here return # bring in only one row if possible rows = widget.rows((maxcol,), True) self.change_focus((maxcol,maxrow), pos, maxrow-1, 'above', (self.pref_col, 0), 0 ) def mouse_event(self, size, event, button, col, row, focus): """ Pass the event to the contained widgets. May change focus on button 1 press. """ (maxcol, maxrow) = size middle, top, bottom = self.calculate_visible((maxcol, maxrow), focus=True) if middle is None: return False _ignore, focus_widget, focus_pos, focus_rows, cursor = middle trim_top, fill_above = top _ignore, fill_below = bottom fill_above.reverse() # fill_above is in bottom-up order w_list = ( fill_above + [ (focus_widget, focus_pos, focus_rows) ] + fill_below ) wrow = -trim_top for w, w_pos, w_rows in w_list: if wrow + w_rows > row: break wrow += w_rows else: return False focus = focus and w == focus_widget if is_mouse_press(event) and button==1: if w.selectable(): self.change_focus((maxcol,maxrow), w_pos, wrow) if not hasattr(w,'mouse_event'): return False return w.mouse_event((maxcol,), event, button, col, row-wrow, focus) def ends_visible(self, size, focus=False): """ Return a list that may contain ``'top'`` and/or ``'bottom'``. i.e. this function will return one of: [], [``'top'``], [``'bottom'``] or [``'top'``, ``'bottom'``]. convenience function for checking whether the top and bottom of the list are visible """ (maxcol, maxrow) = size l = [] middle,top,bottom = self.calculate_visible( (maxcol,maxrow), focus=focus ) if middle is None: # empty listbox return ['top','bottom'] trim_top, above = top trim_bottom, below = bottom if trim_bottom == 0: row_offset, w, pos, rows, c = middle row_offset += rows for w, pos, rows in below: row_offset += rows if row_offset < maxrow: l.append('bottom') elif self.body.get_next(pos) == (None,None): l.append('bottom') if trim_top == 0: row_offset, w, pos, rows, c = middle for w, pos, rows in above: row_offset -= rows if self.body.get_prev(pos) == (None,None): l.insert(0, 'top') return l def __iter__(self): """ Return an iterator over the positions in this ListBox. If self.body does not implement positions() then iterate from the focus widget down to the bottom, then from above the focus up to the top. This is the best we can do with a minimal list walker implementation. """ positions_fn = getattr(self.body, 'positions', None) if positions_fn: for pos in positions_fn(): yield pos return focus_widget, focus_pos = self.body.get_focus() if focus_widget is None: return pos = focus_pos while True: yield pos w, pos = self.body.get_next(pos) if not w: break pos = focus_pos while True: w, pos = self.body.get_prev(pos) if not w: break yield pos def __reversed__(self): """ Return a reversed iterator over the positions in this ListBox. If :attr:`body` does not implement :meth:`positions` then iterate from above the focus widget up to the top, then from the focus widget down to the bottom. Note that this is not actually the reverse of what `__iter__()` produces, but this is the best we can do with a minimal list walker implementation. """ positions_fn = getattr(self.body, 'positions', None) if positions_fn: for pos in positions_fn(reverse=True): yield pos return focus_widget, focus_pos = self.body.get_focus() if focus_widget is None: return pos = focus_pos while True: w, pos = self.body.get_prev(pos) if not w: break yield pos pos = focus_pos while True: yield pos w, pos = self.body.get_next(pos) if not w: break urwid-1.3.1/urwid/monitored_list.py0000775000175000017500000004057712615524560016775 0ustar ianian00000000000000#!/usr/bin/python # # Urwid MonitoredList class # Copyright (C) 2004-2011 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ from urwid.compat import PYTHON3 def _call_modified(fn): def call_modified_wrapper(self, *args, **kwargs): rval = fn(self, *args, **kwargs) self._modified() return rval return call_modified_wrapper class MonitoredList(list): """ This class can trigger a callback any time its contents are changed with the usual list operations append, extend, etc. """ def _modified(self): pass def set_modified_callback(self, callback): """ Assign a callback function with no parameters that is called any time the list is modified. Callback's return value is ignored. >>> import sys >>> ml = MonitoredList([1,2,3]) >>> ml.set_modified_callback(lambda: sys.stdout.write("modified\\n")) >>> ml MonitoredList([1, 2, 3]) >>> ml.append(10) modified >>> len(ml) 4 >>> ml += [11, 12, 13] modified >>> ml[:] = ml[:2] + ml[-2:] modified >>> ml MonitoredList([1, 2, 12, 13]) """ self._modified = callback def __repr__(self): return "%s(%r)" % (self.__class__.__name__, list(self)) __add__ = _call_modified(list.__add__) __delitem__ = _call_modified(list.__delitem__) if not PYTHON3: __delslice__ = _call_modified(list.__delslice__) __iadd__ = _call_modified(list.__iadd__) __imul__ = _call_modified(list.__imul__) __rmul__ = _call_modified(list.__rmul__) __setitem__ = _call_modified(list.__setitem__) if not PYTHON3: __setslice__ = _call_modified(list.__setslice__) append = _call_modified(list.append) extend = _call_modified(list.extend) insert = _call_modified(list.insert) pop = _call_modified(list.pop) remove = _call_modified(list.remove) reverse = _call_modified(list.reverse) sort = _call_modified(list.sort) if hasattr(list, 'clear'): clear = _call_modified(list.clear) class MonitoredFocusList(MonitoredList): """ This class can trigger a callback any time its contents are modified, before and/or after modification, and any time the focus index is changed. """ def __init__(self, *argl, **argd): """ This is a list that tracks one item as the focus item. If items are inserted or removed it will update the focus. >>> ml = MonitoredFocusList([10, 11, 12, 13, 14], focus=3) >>> ml MonitoredFocusList([10, 11, 12, 13, 14], focus=3) >>> del(ml[1]) >>> ml MonitoredFocusList([10, 12, 13, 14], focus=2) >>> ml[:2] = [50, 51, 52, 53] >>> ml MonitoredFocusList([50, 51, 52, 53, 13, 14], focus=4) >>> ml[4] = 99 >>> ml MonitoredFocusList([50, 51, 52, 53, 99, 14], focus=4) >>> ml[:] = [] >>> ml MonitoredFocusList([], focus=None) """ focus = argd.pop('focus', 0) super(MonitoredFocusList, self).__init__(*argl, **argd) self._focus = focus self._focus_modified = lambda ml, indices, new_items: None def __repr__(self): return "%s(%r, focus=%r)" % ( self.__class__.__name__, list(self), self.focus) def _get_focus(self): """ Return the index of the item "in focus" or None if the list is empty. >>> MonitoredFocusList([1,2,3], focus=2)._get_focus() 2 >>> MonitoredFocusList()._get_focus() """ if not self: return None return self._focus def _set_focus(self, index): """ index -- index into this list, any index out of range will raise an IndexError, except when the list is empty and the index passed is ignored. This function may call self._focus_changed when the focus is modified, passing the new focus position to the callback just before changing the old focus setting. That method may be overridden on the instance with set_focus_changed_callback(). >>> ml = MonitoredFocusList([9, 10, 11]) >>> ml._set_focus(2); ml._get_focus() 2 >>> ml._set_focus(0); ml._get_focus() 0 >>> ml._set_focus(-2) Traceback (most recent call last): ... IndexError: focus index is out of range: -2 """ if not self: self._focus = 0 return if index < 0 or index >= len(self): raise IndexError('focus index is out of range: %s' % (index,)) if index != int(index): raise IndexError('invalid focus index: %s' % (index,)) index = int(index) if index != self._focus: self._focus_changed(index) self._focus = index focus = property(_get_focus, _set_focus, doc=""" Get/set the focus index. This value is read as None when the list is empty, and may only be set to a value between 0 and len(self)-1 or an IndexError will be raised. """) def _focus_changed(self, new_focus): pass def set_focus_changed_callback(self, callback): """ Assign a callback to be called when the focus index changes for any reason. The callback is in the form: callback(new_focus) new_focus -- new focus index >>> import sys >>> ml = MonitoredFocusList([1,2,3], focus=1) >>> ml.set_focus_changed_callback(lambda f: sys.stdout.write("focus: %d\\n" % (f,))) >>> ml MonitoredFocusList([1, 2, 3], focus=1) >>> ml.append(10) >>> ml.insert(1, 11) focus: 2 >>> ml MonitoredFocusList([1, 11, 2, 3, 10], focus=2) >>> del ml[:2] focus: 0 >>> ml[:0] = [12, 13, 14] focus: 3 >>> ml.focus = 5 focus: 5 >>> ml MonitoredFocusList([12, 13, 14, 2, 3, 10], focus=5) """ self._focus_changed = callback def _validate_contents_modified(self, indices, new_items): return None def set_validate_contents_modified(self, callback): """ Assign a callback function to handle validating changes to the list. This may raise an exception if the change should not be performed. It may also return an integer position to be the new focus after the list is modified, or None to use the default behaviour. The callback is in the form: callback(indices, new_items) indices -- a (start, stop, step) tuple whose range covers the items being modified new_items -- an iterable of items replacing those at range(*indices), empty if items are being removed, if step==1 this list may contain any number of items """ self._validate_contents_modified = callback def _adjust_focus_on_contents_modified(self, slc, new_items=()): """ Default behaviour is to move the focus to the item following any removed items, unless that item was simply replaced. Failing that choose the last item in the list. returns focus position for after change is applied """ num_new_items = len(new_items) start, stop, step = indices = slc.indices(len(self)) num_removed = len(range(*indices)) focus = self._validate_contents_modified(indices, new_items) if focus is not None: return focus focus = self._focus if step == 1: if start + num_new_items <= focus < stop: focus = stop # adjust for added/removed items if stop <= focus: focus += num_new_items - (stop - start) else: if not num_new_items: # extended slice being removed if focus in range(start, stop, step): focus += 1 # adjust for removed items focus -= len(range(start, min(focus, stop), step)) return min(focus, len(self) + num_new_items - num_removed -1) # override all the list methods that modify the list def __delitem__(self, y): """ >>> ml = MonitoredFocusList([0,1,2,3,4], focus=2) >>> del ml[3]; ml MonitoredFocusList([0, 1, 2, 4], focus=2) >>> del ml[-1]; ml MonitoredFocusList([0, 1, 2], focus=2) >>> del ml[0]; ml MonitoredFocusList([1, 2], focus=1) >>> del ml[1]; ml MonitoredFocusList([1], focus=0) >>> del ml[0]; ml MonitoredFocusList([], focus=None) >>> ml = MonitoredFocusList([5,4,6,4,5,4,6,4,5], focus=4) >>> del ml[1::2]; ml MonitoredFocusList([5, 6, 5, 6, 5], focus=2) >>> del ml[::2]; ml MonitoredFocusList([6, 6], focus=1) >>> ml = MonitoredFocusList([0,1,2,3,4,6,7], focus=2) >>> del ml[-2:]; ml MonitoredFocusList([0, 1, 2, 3, 4], focus=2) >>> del ml[-4:-2]; ml MonitoredFocusList([0, 3, 4], focus=1) >>> del ml[:]; ml MonitoredFocusList([], focus=None) """ if isinstance(y, slice): focus = self._adjust_focus_on_contents_modified(y) else: focus = self._adjust_focus_on_contents_modified(slice(y, y+1 or None)) rval = super(MonitoredFocusList, self).__delitem__(y) self._set_focus(focus) return rval def __setitem__(self, i, y): """ >>> def modified(indices, new_items): ... print "range%r <- %r" % (indices, new_items) >>> ml = MonitoredFocusList([0,1,2,3], focus=2) >>> ml.set_validate_contents_modified(modified) >>> ml[0] = 9 range(0, 1, 1) <- [9] >>> ml[2] = 6 range(2, 3, 1) <- [6] >>> ml.focus 2 >>> ml[-1] = 8 range(3, 4, 1) <- [8] >>> ml MonitoredFocusList([9, 1, 6, 8], focus=2) >>> ml[1::2] = [12, 13] range(1, 4, 2) <- [12, 13] >>> ml[::2] = [10, 11] range(0, 4, 2) <- [10, 11] >>> ml[-3:-1] = [21, 22, 23] range(1, 3, 1) <- [21, 22, 23] >>> ml MonitoredFocusList([10, 21, 22, 23, 13], focus=2) >>> ml[:] = [] range(0, 5, 1) <- [] >>> ml MonitoredFocusList([], focus=None) """ if isinstance(i, slice): focus = self._adjust_focus_on_contents_modified(i, y) else: focus = self._adjust_focus_on_contents_modified(slice(i, i+1 or None), [y]) rval = super(MonitoredFocusList, self).__setitem__(i, y) self._set_focus(focus) return rval if not PYTHON3: def __delslice__(self, i, j): return self.__delitem__(slice(i,j)) def __setslice__(self, i, j, y): return self.__setitem__(slice(i, j), y) def __imul__(self, n): """ >>> def modified(indices, new_items): ... print "range%r <- %r" % (indices, list(new_items)) >>> ml = MonitoredFocusList([0,1,2], focus=2) >>> ml.set_validate_contents_modified(modified) >>> ml *= 3 range(3, 3, 1) <- [0, 1, 2, 0, 1, 2] >>> ml MonitoredFocusList([0, 1, 2, 0, 1, 2, 0, 1, 2], focus=2) >>> ml *= 0 range(0, 9, 1) <- [] >>> print ml.focus None """ if n > 0: focus = self._adjust_focus_on_contents_modified( slice(len(self), len(self)), list(self)*(n-1)) else: # all contents are being removed focus = self._adjust_focus_on_contents_modified(slice(0, len(self))) rval = super(MonitoredFocusList, self).__imul__(n) self._set_focus(focus) return rval def append(self, item): """ >>> def modified(indices, new_items): ... print "range%r <- %r" % (indices, new_items) >>> ml = MonitoredFocusList([0,1,2], focus=2) >>> ml.set_validate_contents_modified(modified) >>> ml.append(6) range(3, 3, 1) <- [6] """ focus = self._adjust_focus_on_contents_modified( slice(len(self), len(self)), [item]) rval = super(MonitoredFocusList, self).append(item) self._set_focus(focus) return rval def extend(self, items): """ >>> def modified(indices, new_items): ... print "range%r <- %r" % (indices, list(new_items)) >>> ml = MonitoredFocusList([0,1,2], focus=2) >>> ml.set_validate_contents_modified(modified) >>> ml.extend((6,7,8)) range(3, 3, 1) <- [6, 7, 8] """ focus = self._adjust_focus_on_contents_modified( slice(len(self), len(self)), items) rval = super(MonitoredFocusList, self).extend(items) self._set_focus(focus) return rval def insert(self, index, item): """ >>> ml = MonitoredFocusList([0,1,2,3], focus=2) >>> ml.insert(-1, -1); ml MonitoredFocusList([0, 1, 2, -1, 3], focus=2) >>> ml.insert(0, -2); ml MonitoredFocusList([-2, 0, 1, 2, -1, 3], focus=3) >>> ml.insert(3, -3); ml MonitoredFocusList([-2, 0, 1, -3, 2, -1, 3], focus=4) """ focus = self._adjust_focus_on_contents_modified(slice(index, index), [item]) rval = super(MonitoredFocusList, self).insert(index, item) self._set_focus(focus) return rval def pop(self, index=-1): """ >>> ml = MonitoredFocusList([-2,0,1,-3,2,3], focus=4) >>> ml.pop(3); ml -3 MonitoredFocusList([-2, 0, 1, 2, 3], focus=3) >>> ml.pop(0); ml -2 MonitoredFocusList([0, 1, 2, 3], focus=2) >>> ml.pop(-1); ml 3 MonitoredFocusList([0, 1, 2], focus=2) >>> ml.pop(2); ml 2 MonitoredFocusList([0, 1], focus=1) """ focus = self._adjust_focus_on_contents_modified(slice(index, index+1 or None)) rval = super(MonitoredFocusList, self).pop(index) self._set_focus(focus) return rval def remove(self, value): """ >>> ml = MonitoredFocusList([-2,0,1,-3,2,-1,3], focus=4) >>> ml.remove(-3); ml MonitoredFocusList([-2, 0, 1, 2, -1, 3], focus=3) >>> ml.remove(-2); ml MonitoredFocusList([0, 1, 2, -1, 3], focus=2) >>> ml.remove(3); ml MonitoredFocusList([0, 1, 2, -1], focus=2) """ index = self.index(value) focus = self._adjust_focus_on_contents_modified(slice(index, index+1 or None)) rval = super(MonitoredFocusList, self).remove(value) self._set_focus(focus) return rval def reverse(self): """ >>> ml = MonitoredFocusList([0,1,2,3,4], focus=1) >>> ml.reverse(); ml MonitoredFocusList([4, 3, 2, 1, 0], focus=3) """ rval = super(MonitoredFocusList, self).reverse() self._set_focus(max(0, len(self) - self._focus - 1)) return rval def sort(self, **kwargs): """ >>> ml = MonitoredFocusList([-2,0,1,-3,2,-1,3], focus=4) >>> ml.sort(); ml MonitoredFocusList([-3, -2, -1, 0, 1, 2, 3], focus=5) """ if not self: return value = self[self._focus] rval = super(MonitoredFocusList, self).sort(**kwargs) self._set_focus(self.index(value)) return rval if hasattr(list, 'clear'): def clear(self): focus = self._adjust_focus_on_contents_modified(slice(0, 0)) rval = super(MonitoredFocusList, self).clear() self._set_focus(focus) return rval def _test(): import doctest doctest.testmod() if __name__=='__main__': _test() urwid-1.3.1/urwid/old_str_util.py0000775000175000017500000002346312615524560016440 0ustar ianian00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # # Urwid unicode character processing tables # Copyright (C) 2004-2011 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ from __future__ import print_function import re from urwid.compat import bytes, B, ord2 SAFE_ASCII_RE = re.compile(u"^[ -~]*$") SAFE_ASCII_BYTES_RE = re.compile(B("^[ -~]*$")) _byte_encoding = None # GENERATED DATA # generated from # http://www.unicode.org/Public/4.0-Update/EastAsianWidth-4.0.0.txt widths = [ (126, 1), (159, 0), (687, 1), (710, 0), (711, 1), (727, 0), (733, 1), (879, 0), (1154, 1), (1161, 0), (4347, 1), (4447, 2), (7467, 1), (7521, 0), (8369, 1), (8426, 0), (9000, 1), (9002, 2), (11021, 1), (12350, 2), (12351, 1), (12438, 2), (12442, 0), (19893, 2), (19967, 1), (55203, 2), (63743, 1), (64106, 2), (65039, 1), (65059, 0), (65131, 2), (65279, 1), (65376, 2), (65500, 1), (65510, 2), (120831, 1), (262141, 2), (1114109, 1), ] # ACCESSOR FUNCTIONS def get_width( o ): """Return the screen column width for unicode ordinal o.""" global widths if o == 0xe or o == 0xf: return 0 for num, wid in widths: if o <= num: return wid return 1 def decode_one( text, pos ): """ Return (ordinal at pos, next position) for UTF-8 encoded text. """ assert isinstance(text, bytes), text b1 = ord2(text[pos]) if not b1 & 0x80: return b1, pos+1 error = ord("?"), pos+1 lt = len(text) lt = lt-pos if lt < 2: return error if b1 & 0xe0 == 0xc0: b2 = ord2(text[pos+1]) if b2 & 0xc0 != 0x80: return error o = ((b1&0x1f)<<6)|(b2&0x3f) if o < 0x80: return error return o, pos+2 if lt < 3: return error if b1 & 0xf0 == 0xe0: b2 = ord2(text[pos+1]) if b2 & 0xc0 != 0x80: return error b3 = ord2(text[pos+2]) if b3 & 0xc0 != 0x80: return error o = ((b1&0x0f)<<12)|((b2&0x3f)<<6)|(b3&0x3f) if o < 0x800: return error return o, pos+3 if lt < 4: return error if b1 & 0xf8 == 0xf0: b2 = ord2(text[pos+1]) if b2 & 0xc0 != 0x80: return error b3 = ord2(text[pos+2]) if b3 & 0xc0 != 0x80: return error b4 = ord2(text[pos+2]) if b4 & 0xc0 != 0x80: return error o = ((b1&0x07)<<18)|((b2&0x3f)<<12)|((b3&0x3f)<<6)|(b4&0x3f) if o < 0x10000: return error return o, pos+4 return error def decode_one_uni(text, i): """ decode_one implementation for unicode strings """ return ord(text[i]), i+1 def decode_one_right(text, pos): """ Return (ordinal at pos, next position) for UTF-8 encoded text. pos is assumed to be on the trailing byte of a utf-8 sequence. """ assert isinstance(text, bytes), text error = ord("?"), pos-1 p = pos while p >= 0: if ord2(text[p])&0xc0 != 0x80: o, next = decode_one( text, p ) return o, p-1 p -=1 if p == p-4: return error def set_byte_encoding(enc): assert enc in ('utf8', 'narrow', 'wide') global _byte_encoding _byte_encoding = enc def get_byte_encoding(): return _byte_encoding def calc_text_pos(text, start_offs, end_offs, pref_col): """ Calculate the closest position to the screen column pref_col in text where start_offs is the offset into text assumed to be screen column 0 and end_offs is the end of the range to search. text may be unicode or a byte string in the target _byte_encoding Returns (position, actual_col). """ assert start_offs <= end_offs, repr((start_offs, end_offs)) utfs = isinstance(text, bytes) and _byte_encoding == "utf8" unis = not isinstance(text, bytes) if unis or utfs: decode = [decode_one, decode_one_uni][unis] i = start_offs sc = 0 n = 1 # number to advance by while i < end_offs: o, n = decode(text, i) w = get_width(o) if w+sc > pref_col: return i, sc i = n sc += w return i, sc assert type(text) == bytes, repr(text) # "wide" and "narrow" i = start_offs+pref_col if i >= end_offs: return end_offs, end_offs-start_offs if _byte_encoding == "wide": if within_double_byte(text, start_offs, i) == 2: i -= 1 return i, i-start_offs def calc_width(text, start_offs, end_offs): """ Return the screen column width of text between start_offs and end_offs. text may be unicode or a byte string in the target _byte_encoding Some characters are wide (take two columns) and others affect the previous character (take zero columns). Use the widths table above to calculate the screen column width of text[start_offs:end_offs] """ assert start_offs <= end_offs, repr((start_offs, end_offs)) utfs = isinstance(text, bytes) and _byte_encoding == "utf8" unis = not isinstance(text, bytes) if (unis and not SAFE_ASCII_RE.match(text) ) or (utfs and not SAFE_ASCII_BYTES_RE.match(text)): decode = [decode_one, decode_one_uni][unis] i = start_offs sc = 0 n = 1 # number to advance by while i < end_offs: o, n = decode(text, i) w = get_width(o) i = n sc += w return sc # "wide", "narrow" or all printable ASCII, just return the character count return end_offs - start_offs def is_wide_char(text, offs): """ Test if the character at offs within text is wide. text may be unicode or a byte string in the target _byte_encoding """ if isinstance(text, unicode): o = ord(text[offs]) return get_width(o) == 2 assert isinstance(text, bytes) if _byte_encoding == "utf8": o, n = decode_one(text, offs) return get_width(o) == 2 if _byte_encoding == "wide": return within_double_byte(text, offs, offs) == 1 return False def move_prev_char(text, start_offs, end_offs): """ Return the position of the character before end_offs. """ assert start_offs < end_offs if isinstance(text, unicode): return end_offs-1 assert isinstance(text, bytes) if _byte_encoding == "utf8": o = end_offs-1 while ord2(text[o])&0xc0 == 0x80: o -= 1 return o if _byte_encoding == "wide" and within_double_byte(text, start_offs, end_offs-1) == 2: return end_offs-2 return end_offs-1 def move_next_char(text, start_offs, end_offs): """ Return the position of the character after start_offs. """ assert start_offs < end_offs if isinstance(text, unicode): return start_offs+1 assert isinstance(text, bytes) if _byte_encoding == "utf8": o = start_offs+1 while o= 0x40 and v < 0x7f: # might be second half of big5, uhc or gbk encoding if pos == line_start: return 0 if ord2(text[pos-1]) >= 0x81: if within_double_byte(text, line_start, pos-1) == 1: return 2 return 0 if v < 0x80: return 0 i = pos -1 while i >= line_start: if ord2(text[i]) < 0x80: break i -= 1 if (pos - i) & 1: return 1 return 2 # TABLE GENERATION CODE def process_east_asian_width(): import sys out = [] last = None for line in sys.stdin.readlines(): if line[:1] == "#": continue line = line.strip() hex,rest = line.split(";",1) wid,rest = rest.split(" # ",1) word1 = rest.split(" ",1)[0] if "." in hex: hex = hex.split("..")[1] num = int(hex, 16) if word1 in ("COMBINING","MODIFIER",""): l = 0 elif wid in ("W", "F"): l = 2 else: l = 1 if last is None: out.append((0, l)) last = l if last == l: out[-1] = (num, l) else: out.append( (num, l) ) last = l print("widths = [") for o in out[1:]: # treat control characters same as ascii print("\t%r," % (o,)) print("]") if __name__ == "__main__": process_east_asian_width() urwid-1.3.1/urwid/curses_display.py0000775000175000017500000004743412615524560016772 0ustar ianian00000000000000#!/usr/bin/python # # Urwid curses output wrapper.. the horror.. # Copyright (C) 2004-2011 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ """ Curses-based UI implementation """ import curses import _curses from urwid import escape from urwid.display_common import BaseScreen, RealTerminal, AttrSpec, \ UNPRINTABLE_TRANS_TABLE from urwid.compat import bytes, PYTHON3 KEY_RESIZE = 410 # curses.KEY_RESIZE (sometimes not defined) KEY_MOUSE = 409 # curses.KEY_MOUSE _curses_colours = { 'default': (-1, 0), 'black': (curses.COLOR_BLACK, 0), 'dark red': (curses.COLOR_RED, 0), 'dark green': (curses.COLOR_GREEN, 0), 'brown': (curses.COLOR_YELLOW, 0), 'dark blue': (curses.COLOR_BLUE, 0), 'dark magenta': (curses.COLOR_MAGENTA, 0), 'dark cyan': (curses.COLOR_CYAN, 0), 'light gray': (curses.COLOR_WHITE, 0), 'dark gray': (curses.COLOR_BLACK, 1), 'light red': (curses.COLOR_RED, 1), 'light green': (curses.COLOR_GREEN, 1), 'yellow': (curses.COLOR_YELLOW, 1), 'light blue': (curses.COLOR_BLUE, 1), 'light magenta': (curses.COLOR_MAGENTA, 1), 'light cyan': (curses.COLOR_CYAN, 1), 'white': (curses.COLOR_WHITE, 1), } class Screen(BaseScreen, RealTerminal): def __init__(self): super(Screen,self).__init__() self.curses_pairs = [ (None,None), # Can't be sure what pair 0 will default to ] self.palette = {} self.has_color = False self.s = None self.cursor_state = None self._keyqueue = [] self.prev_input_resize = 0 self.set_input_timeouts() self.last_bstate = 0 self._mouse_tracking_enabled = False self.register_palette_entry(None, 'default','default') def set_mouse_tracking(self, enable=True): """ Enable mouse tracking. After calling this function get_input will include mouse click events along with keystrokes. """ enable = bool(enable) if enable == self._mouse_tracking_enabled: return if enable: curses.mousemask(0 | curses.BUTTON1_PRESSED | curses.BUTTON1_RELEASED | curses.BUTTON2_PRESSED | curses.BUTTON2_RELEASED | curses.BUTTON3_PRESSED | curses.BUTTON3_RELEASED | curses.BUTTON4_PRESSED | curses.BUTTON4_RELEASED | curses.BUTTON1_DOUBLE_CLICKED | curses.BUTTON1_TRIPLE_CLICKED | curses.BUTTON2_DOUBLE_CLICKED | curses.BUTTON2_TRIPLE_CLICKED | curses.BUTTON3_DOUBLE_CLICKED | curses.BUTTON3_TRIPLE_CLICKED | curses.BUTTON4_DOUBLE_CLICKED | curses.BUTTON4_TRIPLE_CLICKED | curses.BUTTON_SHIFT | curses.BUTTON_ALT | curses.BUTTON_CTRL) else: raise NotImplementedError() self._mouse_tracking_enabled = enable def _start(self): """ Initialize the screen and input mode. """ self.s = curses.initscr() self.has_color = curses.has_colors() if self.has_color: curses.start_color() if curses.COLORS < 8: # not colourful enough self.has_color = False if self.has_color: try: curses.use_default_colors() self.has_default_colors=True except _curses.error: self.has_default_colors=False self._setup_colour_pairs() curses.noecho() curses.meta(1) curses.halfdelay(10) # use set_input_timeouts to adjust self.s.keypad(0) if not self._signal_keys_set: self._old_signal_keys = self.tty_signal_keys() super(Screen, self)._start() def _stop(self): """ Restore the screen. """ curses.echo() self._curs_set(1) try: curses.endwin() except _curses.error: pass # don't block original error with curses error if self._old_signal_keys: self.tty_signal_keys(*self._old_signal_keys) super(Screen, self)._stop() def _setup_colour_pairs(self): """ Initialize all 63 color pairs based on the term: bg * 8 + 7 - fg So to get a color, we just need to use that term and get the right color pair number. """ if not self.has_color: return for fg in xrange(8): for bg in xrange(8): # leave out white on black if fg == curses.COLOR_WHITE and \ bg == curses.COLOR_BLACK: continue curses.init_pair(bg * 8 + 7 - fg, fg, bg) def _curs_set(self,x): if self.cursor_state== "fixed" or x == self.cursor_state: return try: curses.curs_set(x) self.cursor_state = x except _curses.error: self.cursor_state = "fixed" def _clear(self): self.s.clear() self.s.refresh() def _getch(self, wait_tenths): if wait_tenths==0: return self._getch_nodelay() if wait_tenths is None: curses.cbreak() else: curses.halfdelay(wait_tenths) self.s.nodelay(0) return self.s.getch() def _getch_nodelay(self): self.s.nodelay(1) while 1: # this call fails sometimes, but seems to work when I try again try: curses.cbreak() break except _curses.error: pass return self.s.getch() def set_input_timeouts(self, max_wait=None, complete_wait=0.1, resize_wait=0.1): """ Set the get_input timeout values. All values have a granularity of 0.1s, ie. any value between 0.15 and 0.05 will be treated as 0.1 and any value less than 0.05 will be treated as 0. The maximum timeout value for this module is 25.5 seconds. max_wait -- amount of time in seconds to wait for input when there is no input pending, wait forever if None complete_wait -- amount of time in seconds to wait when get_input detects an incomplete escape sequence at the end of the available input resize_wait -- amount of time in seconds to wait for more input after receiving two screen resize requests in a row to stop urwid from consuming 100% cpu during a gradual window resize operation """ def convert_to_tenths( s ): if s is None: return None return int( (s+0.05)*10 ) self.max_tenths = convert_to_tenths(max_wait) self.complete_tenths = convert_to_tenths(complete_wait) self.resize_tenths = convert_to_tenths(resize_wait) def get_input(self, raw_keys=False): """Return pending input as a list. raw_keys -- return raw keycodes as well as translated versions This function will immediately return all the input since the last time it was called. If there is no input pending it will wait before returning an empty list. The wait time may be configured with the set_input_timeouts function. If raw_keys is False (default) this function will return a list of keys pressed. If raw_keys is True this function will return a ( keys pressed, raw keycodes ) tuple instead. Examples of keys returned: * ASCII printable characters: " ", "a", "0", "A", "-", "/" * ASCII control characters: "tab", "enter" * Escape sequences: "up", "page up", "home", "insert", "f1" * Key combinations: "shift f1", "meta a", "ctrl b" * Window events: "window resize" When a narrow encoding is not enabled: * "Extended ASCII" characters: "\\xa1", "\\xb2", "\\xfe" When a wide encoding is enabled: * Double-byte characters: "\\xa1\\xea", "\\xb2\\xd4" When utf8 encoding is enabled: * Unicode characters: u"\\u00a5", u'\\u253c" Examples of mouse events returned: * Mouse button press: ('mouse press', 1, 15, 13), ('meta mouse press', 2, 17, 23) * Mouse button release: ('mouse release', 0, 18, 13), ('ctrl mouse release', 0, 17, 23) """ assert self._started keys, raw = self._get_input( self.max_tenths ) # Avoid pegging CPU at 100% when slowly resizing, and work # around a bug with some braindead curses implementations that # return "no key" between "window resize" commands if keys==['window resize'] and self.prev_input_resize: while True: keys, raw2 = self._get_input(self.resize_tenths) raw += raw2 if not keys: keys, raw2 = self._get_input( self.resize_tenths) raw += raw2 if keys!=['window resize']: break if keys[-1:]!=['window resize']: keys.append('window resize') if keys==['window resize']: self.prev_input_resize = 2 elif self.prev_input_resize == 2 and not keys: self.prev_input_resize = 1 else: self.prev_input_resize = 0 if raw_keys: return keys, raw return keys def _get_input(self, wait_tenths): # this works around a strange curses bug with window resizing # not being reported correctly with repeated calls to this # function without a doupdate call in between curses.doupdate() key = self._getch(wait_tenths) resize = False raw = [] keys = [] while key >= 0: raw.append(key) if key==KEY_RESIZE: resize = True elif key==KEY_MOUSE: keys += self._encode_mouse_event() else: keys.append(key) key = self._getch_nodelay() processed = [] try: while keys: run, keys = escape.process_keyqueue(keys, True) processed += run except escape.MoreInputRequired: key = self._getch(self.complete_tenths) while key >= 0: raw.append(key) if key==KEY_RESIZE: resize = True elif key==KEY_MOUSE: keys += self._encode_mouse_event() else: keys.append(key) key = self._getch_nodelay() while keys: run, keys = escape.process_keyqueue(keys, False) processed += run if resize: processed.append('window resize') return processed, raw def _encode_mouse_event(self): # convert to escape sequence last = next = self.last_bstate (id,x,y,z,bstate) = curses.getmouse() mod = 0 if bstate & curses.BUTTON_SHIFT: mod |= 4 if bstate & curses.BUTTON_ALT: mod |= 8 if bstate & curses.BUTTON_CTRL: mod |= 16 l = [] def append_button( b ): b |= mod l.extend([ 27, ord('['), ord('M'), b+32, x+33, y+33 ]) if bstate & curses.BUTTON1_PRESSED and last & 1 == 0: append_button( 0 ) next |= 1 if bstate & curses.BUTTON2_PRESSED and last & 2 == 0: append_button( 1 ) next |= 2 if bstate & curses.BUTTON3_PRESSED and last & 4 == 0: append_button( 2 ) next |= 4 if bstate & curses.BUTTON4_PRESSED and last & 8 == 0: append_button( 64 ) next |= 8 if bstate & curses.BUTTON1_RELEASED and last & 1: append_button( 0 + escape.MOUSE_RELEASE_FLAG ) next &= ~ 1 if bstate & curses.BUTTON2_RELEASED and last & 2: append_button( 1 + escape.MOUSE_RELEASE_FLAG ) next &= ~ 2 if bstate & curses.BUTTON3_RELEASED and last & 4: append_button( 2 + escape.MOUSE_RELEASE_FLAG ) next &= ~ 4 if bstate & curses.BUTTON4_RELEASED and last & 8: append_button( 64 + escape.MOUSE_RELEASE_FLAG ) next &= ~ 8 if bstate & curses.BUTTON1_DOUBLE_CLICKED: append_button( 0 + escape.MOUSE_MULTIPLE_CLICK_FLAG ) if bstate & curses.BUTTON2_DOUBLE_CLICKED: append_button( 1 + escape.MOUSE_MULTIPLE_CLICK_FLAG ) if bstate & curses.BUTTON3_DOUBLE_CLICKED: append_button( 2 + escape.MOUSE_MULTIPLE_CLICK_FLAG ) if bstate & curses.BUTTON4_DOUBLE_CLICKED: append_button( 64 + escape.MOUSE_MULTIPLE_CLICK_FLAG ) if bstate & curses.BUTTON1_TRIPLE_CLICKED: append_button( 0 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 ) if bstate & curses.BUTTON2_TRIPLE_CLICKED: append_button( 1 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 ) if bstate & curses.BUTTON3_TRIPLE_CLICKED: append_button( 2 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 ) if bstate & curses.BUTTON4_TRIPLE_CLICKED: append_button( 64 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 ) self.last_bstate = next return l def _dbg_instr(self): # messy input string (intended for debugging) curses.echo() self.s.nodelay(0) curses.halfdelay(100) str = self.s.getstr() curses.noecho() return str def _dbg_out(self,str): # messy output function (intended for debugging) self.s.clrtoeol() self.s.addstr(str) self.s.refresh() self._curs_set(1) def _dbg_query(self,question): # messy query (intended for debugging) self._dbg_out(question) return self._dbg_instr() def _dbg_refresh(self): self.s.refresh() def get_cols_rows(self): """Return the terminal dimensions (num columns, num rows).""" rows,cols = self.s.getmaxyx() return cols,rows def _setattr(self, a): if a is None: self.s.attrset(0) return elif not isinstance(a, AttrSpec): p = self._palette.get(a, (AttrSpec('default', 'default'),)) a = p[0] if self.has_color: if a.foreground_basic: if a.foreground_number >= 8: fg = a.foreground_number - 8 else: fg = a.foreground_number else: fg = 7 if a.background_basic: bg = a.background_number else: bg = 0 attr = curses.color_pair(bg * 8 + 7 - fg) else: attr = 0 if a.bold: attr |= curses.A_BOLD if a.standout: attr |= curses.A_STANDOUT if a.underline: attr |= curses.A_UNDERLINE if a.blink: attr |= curses.A_BLINK self.s.attrset(attr) def draw_screen(self, (cols, rows), r ): """Paint screen with rendered canvas.""" assert self._started assert r.rows() == rows, "canvas size and passed size don't match" y = -1 for row in r.content(): y += 1 try: self.s.move( y, 0 ) except _curses.error: # terminal shrunk? # move failed so stop rendering. return first = True lasta = None nr = 0 for a, cs, seg in row: if cs != 'U': seg = seg.translate(UNPRINTABLE_TRANS_TABLE) assert isinstance(seg, bytes) if first or lasta != a: self._setattr(a) lasta = a try: if cs in ("0", "U"): for i in range(len(seg)): self.s.addch( 0x400000 + ord(seg[i]) ) else: assert cs is None if PYTHON3: assert isinstance(seg, bytes) self.s.addstr(seg.decode('utf-8')) else: self.s.addstr(seg) except _curses.error: # it's ok to get out of the # screen on the lower right if (y == rows-1 and nr == len(row)-1): pass else: # perhaps screen size changed # quietly abort. return nr += 1 if r.cursor is not None: x,y = r.cursor self._curs_set(1) try: self.s.move(y,x) except _curses.error: pass else: self._curs_set(0) self.s.move(0,0) self.s.refresh() self.keep_cache_alive_link = r def clear(self): """ Force the screen to be completely repainted on the next call to draw_screen(). """ self.s.clear() class _test: def __init__(self): self.ui = Screen() self.l = _curses_colours.keys() self.l.sort() for c in self.l: self.ui.register_palette( [ (c+" on black", c, 'black', 'underline'), (c+" on dark blue",c, 'dark blue', 'bold'), (c+" on light gray",c,'light gray', 'standout'), ]) self.ui.run_wrapper(self.run) def run(self): class FakeRender: pass r = FakeRender() text = [" has_color = "+repr(self.ui.has_color),""] attr = [[],[]] r.coords = {} r.cursor = None for c in self.l: t = "" a = [] for p in c+" on black",c+" on dark blue",c+" on light gray": a.append((p,27)) t=t+ (p+27*" ")[:27] text.append( t ) attr.append( a ) text += ["","return values from get_input(): (q exits)", ""] attr += [[],[],[]] cols,rows = self.ui.get_cols_rows() keys = None while keys!=['q']: r.text=([t.ljust(cols) for t in text]+[""]*rows)[:rows] r.attr=(attr+[[]]*rows) [:rows] self.ui.draw_screen((cols,rows),r) keys, raw = self.ui.get_input( raw_keys = True ) if 'window resize' in keys: cols, rows = self.ui.get_cols_rows() if not keys: continue t = "" a = [] for k in keys: if type(k) == unicode: k = k.encode("utf-8") t += "'"+k + "' " a += [(None,1), ('yellow on dark blue',len(k)), (None,2)] text.append(t + ": "+ repr(raw)) attr.append(a) text = text[-rows:] attr = attr[-rows:] if '__main__'==__name__: _test() urwid-1.3.1/urwid/escape.py0000664000175000017500000003267612615524560015200 0ustar ianian00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # # Urwid escape sequences common to curses_display and raw_display # Copyright (C) 2004-2011 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ """ Terminal Escape Sequences for input and display """ import re try: from urwid import str_util except ImportError: from urwid import old_str_util as str_util from urwid.compat import bytes, bytes3 within_double_byte = str_util.within_double_byte SO = "\x0e" SI = "\x0f" IBMPC_ON = "\x1b[11m" IBMPC_OFF = "\x1b[10m" DEC_TAG = "0" DEC_SPECIAL_CHARS = u'▮◆▒â‰âŒââŠÂ°Â±â¤â‹â”˜â”┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£·' ALT_DEC_SPECIAL_CHARS = u"_`abcdefghijklmnopqrstuvwxyz{|}~" DEC_SPECIAL_CHARMAP = {} assert len(DEC_SPECIAL_CHARS) == len(ALT_DEC_SPECIAL_CHARS), repr((DEC_SPECIAL_CHARS, ALT_DEC_SPECIAL_CHARS)) for c, alt in zip(DEC_SPECIAL_CHARS, ALT_DEC_SPECIAL_CHARS): DEC_SPECIAL_CHARMAP[ord(c)] = SO + alt + SI SAFE_ASCII_DEC_SPECIAL_RE = re.compile(u"^[ -~%s]*$" % DEC_SPECIAL_CHARS) DEC_SPECIAL_RE = re.compile(u"[%s]" % DEC_SPECIAL_CHARS) ################### ## Input sequences ################### class MoreInputRequired(Exception): pass def escape_modifier( digit ): mode = ord(digit) - ord("1") return "shift "*(mode&1) + "meta "*((mode&2)//2) + "ctrl "*((mode&4)//4) input_sequences = [ ('[A','up'),('[B','down'),('[C','right'),('[D','left'), ('[E','5'),('[F','end'),('[G','5'),('[H','home'), ('[1~','home'),('[2~','insert'),('[3~','delete'),('[4~','end'), ('[5~','page up'),('[6~','page down'), ('[7~','home'),('[8~','end'), ('[[A','f1'),('[[B','f2'),('[[C','f3'),('[[D','f4'),('[[E','f5'), ('[11~','f1'),('[12~','f2'),('[13~','f3'),('[14~','f4'), ('[15~','f5'),('[17~','f6'),('[18~','f7'),('[19~','f8'), ('[20~','f9'),('[21~','f10'),('[23~','f11'),('[24~','f12'), ('[25~','f13'),('[26~','f14'),('[28~','f15'),('[29~','f16'), ('[31~','f17'),('[32~','f18'),('[33~','f19'),('[34~','f20'), ('OA','up'),('OB','down'),('OC','right'),('OD','left'), ('OH','home'),('OF','end'), ('OP','f1'),('OQ','f2'),('OR','f3'),('OS','f4'), ('Oo','/'),('Oj','*'),('Om','-'),('Ok','+'), ('[Z','shift tab'), ('On', '.'), ('[200~', 'begin paste'), ('[201~', 'end paste'), ] + [ (prefix + letter, modifier + key) for prefix, modifier in zip('O[', ('meta ', 'shift ')) for letter, key in zip('abcd', ('up', 'down', 'right', 'left')) ] + [ ("[" + digit + symbol, modifier + key) for modifier, symbol in zip(('shift ', 'meta '), '$^') for digit, key in zip('235678', ('insert', 'delete', 'page up', 'page down', 'home', 'end')) ] + [ ('O' + chr(ord('p')+n), str(n)) for n in range(10) ] + [ # modified cursor keys + home, end, 5 -- [#X and [1;#X forms (prefix+digit+letter, escape_modifier(digit) + key) for prefix in ("[", "[1;") for digit in "12345678" for letter,key in zip("ABCDEFGH", ('up','down','right','left','5','end','5','home')) ] + [ # modified F1-F4 keys -- O#X form ("O"+digit+letter, escape_modifier(digit) + key) for digit in "12345678" for letter,key in zip("PQRS",('f1','f2','f3','f4')) ] + [ # modified F1-F13 keys -- [XX;#~ form ("["+str(num)+";"+digit+"~", escape_modifier(digit) + key) for digit in "12345678" for num,key in zip( (3,5,6,11,12,13,14,15,17,18,19,20,21,23,24,25,26,28,29,31,32,33,34), ('delete', 'page up', 'page down', 'f1','f2','f3','f4','f5','f6','f7','f8','f9','f10','f11', 'f12','f13','f14','f15','f16','f17','f18','f19','f20')) ] + [ # mouse reporting (special handling done in KeyqueueTrie) ('[M', 'mouse'), # report status response ('[0n', 'status ok') ] class KeyqueueTrie(object): def __init__( self, sequences ): self.data = {} for s, result in sequences: assert type(result) != dict self.add(self.data, s, result) def add(self, root, s, result): assert type(root) == dict, "trie conflict detected" assert len(s) > 0, "trie conflict detected" if ord(s[0]) in root: return self.add(root[ord(s[0])], s[1:], result) if len(s)>1: d = {} root[ord(s[0])] = d return self.add(d, s[1:], result) root[ord(s)] = result def get(self, keys, more_available): result = self.get_recurse(self.data, keys, more_available) if not result: result = self.read_cursor_position(keys, more_available) return result def get_recurse(self, root, keys, more_available): if type(root) != dict: if root == "mouse": return self.read_mouse_info(keys, more_available) return (root, keys) if not keys: # get more keys if more_available: raise MoreInputRequired() return None if keys[0] not in root: return None return self.get_recurse(root[keys[0]], keys[1:], more_available) def read_mouse_info(self, keys, more_available): if len(keys) < 3: if more_available: raise MoreInputRequired() return None b = keys[0] - 32 x, y = (keys[1] - 33)%256, (keys[2] - 33)%256 # supports 0-255 prefix = "" if b & 4: prefix = prefix + "shift " if b & 8: prefix = prefix + "meta " if b & 16: prefix = prefix + "ctrl " if (b & MOUSE_MULTIPLE_CLICK_MASK)>>9 == 1: prefix = prefix + "double " if (b & MOUSE_MULTIPLE_CLICK_MASK)>>9 == 2: prefix = prefix + "triple " # 0->1, 1->2, 2->3, 64->4, 65->5 button = ((b&64)/64*3) + (b & 3) + 1 if b & 3 == 3: action = "release" button = 0 elif b & MOUSE_RELEASE_FLAG: action = "release" elif b & MOUSE_DRAG_FLAG: action = "drag" elif b & MOUSE_MULTIPLE_CLICK_MASK: action = "click" else: action = "press" return ( (prefix + "mouse " + action, button, x, y), keys[3:] ) def read_cursor_position(self, keys, more_available): """ Interpret cursor position information being sent by the user's terminal. Returned as ('cursor position', x, y) where (x, y) == (0, 0) is the top left of the screen. """ if not keys: if more_available: raise MoreInputRequired() return None if keys[0] != ord('['): return None # read y value y = 0 i = 1 for k in keys[i:]: i += 1 if k == ord(';'): if not y: return None break if k < ord('0') or k > ord('9'): return None if not y and k == ord('0'): return None y = y * 10 + k - ord('0') if not keys[i:]: if more_available: raise MoreInputRequired() return None # read x value x = 0 for k in keys[i:]: i += 1 if k == ord('R'): if not x: return None return (("cursor position", x-1, y-1), keys[i:]) if k < ord('0') or k > ord('9'): return None if not x and k == ord('0'): return None x = x * 10 + k - ord('0') if not keys[i:]: if more_available: raise MoreInputRequired() return None # This is added to button value to signal mouse release by curses_display # and raw_display when we know which button was released. NON-STANDARD MOUSE_RELEASE_FLAG = 2048 # This 2-bit mask is used to check if the mouse release from curses or gpm # is a double or triple release. 00 means single click, 01 double, # 10 triple. NON-STANDARD MOUSE_MULTIPLE_CLICK_MASK = 1536 # This is added to button value at mouse release to differentiate between # single, double and triple press. Double release adds this times one, # triple release adds this times two. NON-STANDARD MOUSE_MULTIPLE_CLICK_FLAG = 512 # xterm adds this to the button value to signal a mouse drag event MOUSE_DRAG_FLAG = 32 ################################################# # Build the input trie from input_sequences list input_trie = KeyqueueTrie(input_sequences) ################################################# _keyconv = { -1:None, 8:'backspace', 9:'tab', 10:'enter', 13:'enter', 127:'backspace', # curses-only keycodes follow.. (XXX: are these used anymore?) 258:'down', 259:'up', 260:'left', 261:'right', 262:'home', 263:'backspace', 265:'f1', 266:'f2', 267:'f3', 268:'f4', 269:'f5', 270:'f6', 271:'f7', 272:'f8', 273:'f9', 274:'f10', 275:'f11', 276:'f12', 277:'shift f1', 278:'shift f2', 279:'shift f3', 280:'shift f4', 281:'shift f5', 282:'shift f6', 283:'shift f7', 284:'shift f8', 285:'shift f9', 286:'shift f10', 287:'shift f11', 288:'shift f12', 330:'delete', 331:'insert', 338:'page down', 339:'page up', 343:'enter', # on numpad 350:'5', # on numpad 360:'end', } def process_keyqueue(codes, more_available): """ codes -- list of key codes more_available -- if True then raise MoreInputRequired when in the middle of a character sequence (escape/utf8/wide) and caller will attempt to send more key codes on the next call. returns (list of input, list of remaining key codes). """ code = codes[0] if code >= 32 and code <= 126: key = chr(code) return [key], codes[1:] if code in _keyconv: return [_keyconv[code]], codes[1:] if code >0 and code <27: return ["ctrl %s" % chr(ord('a')+code-1)], codes[1:] if code >27 and code <32: return ["ctrl %s" % chr(ord('A')+code-1)], codes[1:] em = str_util.get_byte_encoding() if (em == 'wide' and code < 256 and within_double_byte(chr(code),0,0)): if not codes[1:]: if more_available: raise MoreInputRequired() if codes[1:] and codes[1] < 256: db = chr(code)+chr(codes[1]) if within_double_byte(db, 0, 1): return [db], codes[2:] if em == 'utf8' and code>127 and code<256: if code & 0xe0 == 0xc0: # 2-byte form need_more = 1 elif code & 0xf0 == 0xe0: # 3-byte form need_more = 2 elif code & 0xf8 == 0xf0: # 4-byte form need_more = 3 else: return ["<%d>"%code], codes[1:] for i in range(need_more): if len(codes)-1 <= i: if more_available: raise MoreInputRequired() else: return ["<%d>"%code], codes[1:] k = codes[i+1] if k>256 or k&0xc0 != 0x80: return ["<%d>"%code], codes[1:] s = bytes3(codes[:need_more+1]) assert isinstance(s, bytes) try: return [s.decode("utf-8")], codes[need_more+1:] except UnicodeDecodeError: return ["<%d>"%code], codes[1:] if code >127 and code <256: key = chr(code) return [key], codes[1:] if code != 27: return ["<%d>"%code], codes[1:] result = input_trie.get(codes[1:], more_available) if result is not None: result, remaining_codes = result return [result], remaining_codes if codes[1:]: # Meta keys -- ESC+Key form run, remaining_codes = process_keyqueue(codes[1:], more_available) if run[0] == "esc" or run[0].find("meta ") >= 0: return ['esc']+run, remaining_codes return ['meta '+run[0]]+run[1:], remaining_codes return ['esc'], codes[1:] #################### ## Output sequences #################### ESC = "\x1b" CURSOR_HOME = ESC+"[H" CURSOR_HOME_COL = "\r" APP_KEYPAD_MODE = ESC+"=" NUM_KEYPAD_MODE = ESC+">" SWITCH_TO_ALTERNATE_BUFFER = ESC+"7"+ESC+"[?47h" RESTORE_NORMAL_BUFFER = ESC+"[?47l"+ESC+"8" #RESET_SCROLL_REGION = ESC+"[;r" #RESET = ESC+"c" REPORT_STATUS = ESC + "[5n" REPORT_CURSOR_POSITION = ESC+"[6n" INSERT_ON = ESC + "[4h" INSERT_OFF = ESC + "[4l" def set_cursor_position( x, y ): assert type(x) == int assert type(y) == int return ESC+"[%d;%dH" %(y+1, x+1) def move_cursor_right(x): if x < 1: return "" return ESC+"[%dC" % x def move_cursor_up(x): if x < 1: return "" return ESC+"[%dA" % x def move_cursor_down(x): if x < 1: return "" return ESC+"[%dB" % x HIDE_CURSOR = ESC+"[?25l" SHOW_CURSOR = ESC+"[?25h" MOUSE_TRACKING_ON = ESC+"[?1000h"+ESC+"[?1002h" MOUSE_TRACKING_OFF = ESC+"[?1002l"+ESC+"[?1000l" DESIGNATE_G1_SPECIAL = ESC+")0" ERASE_IN_LINE_RIGHT = ESC+"[K" urwid-1.3.1/urwid/widget.py0000664000175000017500000016647312615524560015226 0ustar ianian00000000000000#!/usr/bin/python # # Urwid basic widget classes # Copyright (C) 2004-2012 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ from operator import attrgetter from urwid.util import (MetaSuper, decompose_tagmarkup, calc_width, is_wide_char, move_prev_char, move_next_char) from urwid.text_layout import calc_pos, calc_coords, shift_line from urwid import signals from urwid import text_layout from urwid.canvas import (CanvasCache, CompositeCanvas, SolidCanvas, apply_text_layout) from urwid.command_map import (command_map, CURSOR_LEFT, CURSOR_RIGHT, CURSOR_UP, CURSOR_DOWN, CURSOR_MAX_LEFT, CURSOR_MAX_RIGHT) from urwid.split_repr import split_repr, remove_defaults, python3_repr # define some names for these constants to avoid misspellings in the source # and to document the constant strings we are using # Widget sizing methods FLOW = 'flow' BOX = 'box' FIXED = 'fixed' # Text alignment modes LEFT = 'left' RIGHT = 'right' CENTER = 'center' # Filler alignment TOP = 'top' MIDDLE = 'middle' BOTTOM = 'bottom' # Text wrapping modes SPACE = 'space' ANY = 'any' CLIP = 'clip' # Width and Height settings PACK = 'pack' GIVEN = 'given' RELATIVE = 'relative' RELATIVE_100 = (RELATIVE, 100) WEIGHT = 'weight' class WidgetMeta(MetaSuper, signals.MetaSignals): """ Bases: :class:`MetaSuper`, :class:`MetaSignals` Automatic caching of render and rows methods. Class variable *no_cache* is a list of names of methods to not cache automatically. Valid method names for *no_cache* are ``'render'`` and ``'rows'``. Class variable *ignore_focus* if defined and set to ``True`` indicates that the canvas this widget renders is not affected by the focus parameter, so it may be ignored when caching. """ def __init__(cls, name, bases, d): no_cache = d.get("no_cache", []) super(WidgetMeta, cls).__init__(name, bases, d) if "render" in d: if "render" not in no_cache: render_fn = cache_widget_render(cls) else: render_fn = nocache_widget_render(cls) cls.render = render_fn if "rows" in d and "rows" not in no_cache: cls.rows = cache_widget_rows(cls) if "no_cache" in d: del cls.no_cache if "ignore_focus" in d: del cls.ignore_focus class WidgetError(Exception): pass def validate_size(widget, size, canv): """ Raise a WidgetError if a canv does not match size size. """ if (size and size[1:] != (0,) and size[0] != canv.cols()) or \ (len(size)>1 and size[1] != canv.rows()): raise WidgetError("Widget %r rendered (%d x %d) canvas" " when passed size %r!" % (widget, canv.cols(), canv.rows(), size)) def update_wrapper(new_fn, fn): """ Copy as much of the function detail from fn to new_fn as we can. """ try: new_fn.__name__ = fn.__name__ new_fn.__dict__.update(fn.__dict__) new_fn.__doc__ = fn.__doc__ new_fn.__module__ = fn.__module__ except TypeError: pass # python2.3 ignore read-only attributes def cache_widget_render(cls): """ Return a function that wraps the cls.render() method and fetches and stores canvases with CanvasCache. """ ignore_focus = bool(getattr(cls, "ignore_focus", False)) fn = cls.render def cached_render(self, size, focus=False): focus = focus and not ignore_focus canv = CanvasCache.fetch(self, cls, size, focus) if canv: return canv canv = fn(self, size, focus=focus) validate_size(self, size, canv) if canv.widget_info: canv = CompositeCanvas(canv) canv.finalize(self, size, focus) CanvasCache.store(cls, canv) return canv cached_render.original_fn = fn update_wrapper(cached_render, fn) return cached_render def nocache_widget_render(cls): """ Return a function that wraps the cls.render() method and finalizes the canvas that it returns. """ fn = cls.render if hasattr(fn, "original_fn"): fn = fn.original_fn def finalize_render(self, size, focus=False): canv = fn(self, size, focus=focus) if canv.widget_info: canv = CompositeCanvas(canv) validate_size(self, size, canv) canv.finalize(self, size, focus) return canv finalize_render.original_fn = fn update_wrapper(finalize_render, fn) return finalize_render def nocache_widget_render_instance(self): """ Return a function that wraps the cls.render() method and finalizes the canvas that it returns, but does not cache the canvas. """ fn = self.render.original_fn def finalize_render(size, focus=False): canv = fn(self, size, focus=focus) if canv.widget_info: canv = CompositeCanvas(canv) canv.finalize(self, size, focus) return canv finalize_render.original_fn = fn update_wrapper(finalize_render, fn) return finalize_render def cache_widget_rows(cls): """ Return a function that wraps the cls.rows() method and returns rows from the CanvasCache if available. """ ignore_focus = bool(getattr(cls, "ignore_focus", False)) fn = cls.rows def cached_rows(self, size, focus=False): focus = focus and not ignore_focus canv = CanvasCache.fetch(self, cls, size, focus) if canv: return canv.rows() return fn(self, size, focus) update_wrapper(cached_rows, fn) return cached_rows class Widget(object): """ Widget base class .. attribute:: __metaclass__ :annotation: = urwid.WidgetMeta See :class:`urwid.WidgetMeta` definition .. attribute:: _selectable :annotation: = False The default :meth:`.selectable` method returns this value. .. attribute:: _sizing :annotation: = frozenset(['flow', 'box', 'fixed']) The default :meth:`.sizing` method returns this value. .. attribute:: _command_map :annotation: = urwid.command_map A shared :class:`CommandMap` instance. May be redefined in subclasses or widget instances. .. method:: render(size, focus=False) .. note:: This method is not implemented in :class:`.Widget` but must be implemented by any concrete subclass :param size: One of the following, *maxcol* and *maxrow* are integers > 0: (*maxcol*, *maxrow*) for box sizing -- the parent chooses the exact size of this widget (*maxcol*,) for flow sizing -- the parent chooses only the number of columns for this widget () for fixed sizing -- this widget is a fixed size which can't be adjusted by the parent :type size: widget size :param focus: set to ``True`` if this widget or one of its children is in focus :type focus: bool :returns: A :class:`Canvas` subclass instance containing the rendered content of this widget :class:`Text` widgets return a :class:`TextCanvas` (arbitrary text and display attributes), :class:`SolidFill` widgets return a :class:`SolidCanvas` (a single character repeated across the whole surface) and container widgets return a :class:`CompositeCanvas` (one or more other canvases arranged arbitrarily). If *focus* is ``False``, the returned canvas may not have a cursor position set. There is some metaclass magic defined in the :class:`Widget` metaclass :class:`WidgetMeta` that causes the result of this method to be cached by :class:`CanvasCache`. Later calls will automatically look up the value in the cache first. As a small optimization the class variable :attr:`ignore_focus` may be defined and set to ``True`` if this widget renders the same canvas regardless of the value of the *focus* parameter. Any time the content of a widget changes it should call :meth:`_invalidate` to remove any cached canvases, or the widget may render the cached canvas instead of creating a new one. .. method:: rows(size, focus=False) .. note:: This method is not implemented in :class:`.Widget` but must be implemented by any flow widget. See :meth:`.sizing`. See :meth:`Widget.render` for parameter details. :returns: The number of rows required for this widget given a number of columns in *size* This is the method flow widgets use to communicate their size to other widgets without having to render a canvas. This should be a quick calculation as this function may be called a number of times in normal operation. If your implementation may take a long time you should add your own caching here. There is some metaclass magic defined in the :class:`Widget` metaclass :class:`WidgetMeta` that causes the result of this function to be retrieved from any canvas cached by :class:`CanvasCache`, so if your widget has been rendered you may not receive calls to this function. The class variable :attr:`ignore_focus` may be defined and set to ``True`` if this widget renders the same size regardless of the value of the *focus* parameter. .. method:: keypress(size, key) .. note:: This method is not implemented in :class:`.Widget` but must be implemented by any selectable widget. See :meth:`.selectable`. :param size: See :meth:`Widget.render` for details :type size: widget size :param key: a single keystroke value; see :ref:`keyboard-input` :type key: bytes or unicode :returns: ``None`` if *key* was handled by this widget or *key* (the same value passed) if *key* was not handled by this widget Container widgets will typically call the :meth:`keypress` method on whichever of their children is set as the focus. The standard widgets use :attr:`_command_map` to determine what action should be performed for a given *key*. You may modify these values to your liking globally, at some level in the widget hierarchy or on individual widgets. See :class:`CommandMap` for the defaults. In your own widgets you may use whatever logic you like: filtering or translating keys, selectively passing along events etc. .. method:: mouse_event(size, event, button, col, row, focus) .. note:: This method is not implemented in :class:`.Widget` but may be implemented by a subclass. Not implementing this method is equivalent to having a method that always returns ``False``. :param size: See :meth:`Widget.render` for details. :type size: widget size :param event: Values such as ``'mouse press'``, ``'ctrl mouse press'``, ``'mouse release'``, ``'meta mouse release'``, ``'mouse drag'``; see :ref:`mouse-input` :type event: mouse event :param button: 1 through 5 for press events, often 0 for release events (which button was released is often not known) :type button: int :param col: Column of the event, 0 is the left edge of this widget :type col: int :param row: Row of the event, 0 it the top row of this widget :type row: int :param focus: Set to ``True`` if this widget or one of its children is in focus :type focus: bool :returns: ``True`` if the event was handled by this widget, ``False`` otherwise Container widgets will typically call the :meth:`mouse_event` method on whichever of their children is at the position (*col*, *row*). .. method:: get_cursor_coords(size) .. note:: This method is not implemented in :class:`.Widget` but must be implemented by any widget that may return cursor coordinates as part of the canvas that :meth:`render` returns. :param size: See :meth:`Widget.render` for details. :type size: widget size :returns: (*col*, *row*) if this widget has a cursor, ``None`` otherwise Return the cursor coordinates (*col*, *row*) of a cursor that will appear as part of the canvas rendered by this widget when in focus, or ``None`` if no cursor is displayed. The :class:`ListBox` widget uses this method to make sure a cursor in the focus widget is not scrolled out of view. It is a separate method to avoid having to render the whole widget while calculating layout. Container widgets will typically call the :meth:`.get_cursor_coords` method on their focus widget. .. method:: get_pref_col(size) .. note:: This method is not implemented in :class:`.Widget` but may be implemented by a subclass. :param size: See :meth:`Widget.render` for details. :type size: widget size :returns: a column number or ``'left'`` for the leftmost available column or ``'right'`` for the rightmost available column Return the preferred column for the cursor to be displayed in this widget. This value might not be the same as the column returned from :meth:`get_cursor_coords`. The :class:`ListBox` and :class:`Pile` widgets call this method on a widget losing focus and use the value returned to call :meth:`.move_cursor_to_coords` on the widget becoming the focus. This allows the focus to move up and down through widgets while keeping the cursor in approximately the same column on screen. .. method:: move_cursor_to_coords(size, col, row) .. note:: This method is not implemented in :class:`.Widget` but may be implemented by a subclass. Not implementing this method is equivalent to having a method that always returns ``False``. :param size: See :meth:`Widget.render` for details. :type size: widget size :param col: new column for the cursor, 0 is the left edge of this widget :type col: int :param row: new row for the cursor, 0 it the top row of this widget :type row: int :returns: ``True`` if the position was set successfully anywhere on *row*, ``False`` otherwise """ __metaclass__ = WidgetMeta _selectable = False _sizing = frozenset([FLOW, BOX, FIXED]) _command_map = command_map def _invalidate(self): """ Mark cached canvases rendered by this widget as dirty so that they will not be used again. """ CanvasCache.invalidate(self) def _emit(self, name, *args): """ Convenience function to emit signals with self as first argument. """ signals.emit_signal(self, name, self, *args) def selectable(self): """ :returns: ``True`` if this is a widget that is designed to take the focus, i.e. it contains something the user might want to interact with, ``False`` otherwise, This default implementation returns :attr:`._selectable`. Subclasses may leave these is if the are not selectable, or if they are always selectable they may set the :attr:`_selectable` class variable to ``True``. If this method returns ``True`` then the :meth:`.keypress` method must be implemented. Returning ``False`` does not guarantee that this widget will never be in focus, only that this widget will usually be skipped over when changing focus. It is still possible for non selectable widgets to have the focus (typically when there are no other selectable widgets visible). """ return self._selectable def sizing(self): """ :returns: A frozenset including one or more of ``'box'``, ``'flow'`` and ``'fixed'``. Default implementation returns the value of :attr:`._sizing`, which for this class includes all three. The sizing modes returned indicate the modes that may be supported by this widget, but is not sufficient to know that using that sizing mode will work. Subclasses should make an effort to remove sizing modes they know will not work given the state of the widget, but many do not yet do this. If a sizing mode is missing from the set then the widget should fail when used in that mode. If ``'flow'`` is among the values returned then the other methods in this widget must be able to accept a single-element tuple (*maxcol*,) to their ``size`` parameter, and the :meth:`rows` method must be defined. If ``'box'`` is among the values returned then the other methods must be able to accept a two-element tuple (*maxcol*, *maxrow*) to their size paramter. If ``'fixed'`` is among the values returned then the other methods must be able to accept an empty tuple () to their size parameter, and the :meth:`pack` method must be defined. """ return self._sizing def pack(self, size, focus=False): """ See :meth:`Widget.render` for parameter details. :returns: A "packed" size (*maxcol*, *maxrow*) for this widget Calculate and return a minimum size where all content could still be displayed. Fixed widgets must implement this method and return their size when ``()`` is passed as the *size* parameter. This default implementation returns the *size* passed, or the *maxcol* passed and the value of :meth:`rows` as the *maxrow* when (*maxcol*,) is passed as the *size* parameter. .. note:: This is a new method that hasn't been fully implemented across the standard widget types. In particular it has not yet been implemented for container widgets. :class:`Text` widgets have implemented this method. You can use :meth:`Text.pack` to calculate the minumum columns and rows required to display a text widget without wrapping, or call it iteratively to calculate the minimum number of columns required to display the text wrapped into a target number of rows. """ if not size: if FIXED in self.sizing(): raise NotImplementedError('Fixed widgets must override' ' Widget.pack()') raise WidgetError('Cannot pack () size, this is not a fixed' ' widget: %s' % repr(self)) elif len(size) == 1: if FLOW in self.sizing(): return size + (self.rows(size, focus),) raise WidgetError('Cannot pack (maxcol,) size, this is not a' ' flow widget: %s' % repr(self)) return size base_widget = property(lambda self:self, doc=""" Read-only property that steps through decoration widgets and returns the one at the base. This default implementation returns self. """) focus = property(lambda self:None, doc=""" Read-only property returning the child widget in focus for container widgets. This default implementation always returns ``None``, indicating that this widget has no children. """) def _not_a_container(self, val=None): raise IndexError( "No focus_position, %r is not a container widget" % self) focus_position = property(_not_a_container, _not_a_container, doc=""" Property for reading and setting the focus position for container widgets. This default implementation raises :exc:`IndexError`, making normal widgets fail the same way accessing :attr:`.focus_position` on an empty container widget would. """) def __repr__(self): """ A friendly __repr__ for widgets, designed to be extended by subclasses with _repr_words and _repr_attr methods. """ return split_repr(self) def _repr_words(self): words = [] if self.selectable(): words = ["selectable"] + words if self.sizing() and self.sizing() != frozenset([FLOW, BOX, FIXED]): sizing_modes = list(self.sizing()) sizing_modes.sort() words.append("/".join(sizing_modes)) return words + ["widget"] def _repr_attrs(self): return {} class FlowWidget(Widget): """ Deprecated. Inherit from Widget and add: _sizing = frozenset(['flow']) at the top of your class definition instead. Base class of widgets that determine their rows from the number of columns available. """ _sizing = frozenset([FLOW]) def rows(self, size, focus=False): """ All flow widgets must implement this function. """ raise NotImplementedError() def render(self, size, focus=False): """ All widgets must implement this function. """ raise NotImplementedError() class BoxWidget(Widget): """ Deprecated. Inherit from Widget and add: _sizing = frozenset(['box']) _selectable = True at the top of your class definition instead. Base class of width and height constrained widgets such as the top level widget attached to the display object """ _selectable = True _sizing = frozenset([BOX]) def render(self, size, focus=False): """ All widgets must implement this function. """ raise NotImplementedError() def fixed_size(size): """ raise ValueError if size != (). Used by FixedWidgets to test size parameter. """ if size != (): raise ValueError("FixedWidget takes only () for size." \ "passed: %r" % (size,)) class FixedWidget(Widget): """ Deprecated. Inherit from Widget and add: _sizing = frozenset(['fixed']) at the top of your class definition instead. Base class of widgets that know their width and height and cannot be resized """ _sizing = frozenset([FIXED]) def render(self, size, focus=False): """ All widgets must implement this function. """ raise NotImplementedError() def pack(self, size=None, focus=False): """ All fixed widgets must implement this function. """ raise NotImplementedError() class Divider(Widget): """ Horizontal divider widget """ _sizing = frozenset([FLOW]) ignore_focus = True def __init__(self,div_char=u" ",top=0,bottom=0): """ :param div_char: character to repeat across line :type div_char: bytes or unicode :param top: number of blank lines above :type top: int :param bottom: number of blank lines below :type bottom: int >>> Divider() >>> Divider(u'-') >>> Divider(u'x', 1, 2) """ self.__super.__init__() self.div_char = div_char self.top = top self.bottom = bottom def _repr_words(self): return self.__super._repr_words() + [ python3_repr(self.div_char)] * (self.div_char != u" ") def _repr_attrs(self): attrs = dict(self.__super._repr_attrs()) if self.top: attrs['top'] = self.top if self.bottom: attrs['bottom'] = self.bottom return attrs def rows(self, size, focus=False): """ Return the number of lines that will be rendered. >>> Divider().rows((10,)) 1 >>> Divider(u'x', 1, 2).rows((10,)) 4 """ (maxcol,) = size return self.top + 1 + self.bottom def render(self, size, focus=False): """ Render the divider as a canvas and return it. >>> Divider().render((10,)).text # ... = b in Python 3 [...' '] >>> Divider(u'-', top=1).render((10,)).text [...' ', ...'----------'] >>> Divider(u'x', bottom=2).render((5,)).text [...'xxxxx', ...' ', ...' '] """ (maxcol,) = size canv = SolidCanvas(self.div_char, maxcol, 1) canv = CompositeCanvas(canv) if self.top or self.bottom: canv.pad_trim_top_bottom(self.top, self.bottom) return canv class SolidFill(BoxWidget): """ A box widget that fills an area with a single character """ _selectable = False ignore_focus = True def __init__(self, fill_char=" "): """ :param fill_char: character to fill area with :type fill_char: bytes or unicode >>> SolidFill(u'8') """ self.__super.__init__() self.fill_char = fill_char def _repr_words(self): return self.__super._repr_words() + [python3_repr(self.fill_char)] def render(self, size, focus=False ): """ Render the Fill as a canvas and return it. >>> SolidFill().render((4,2)).text # ... = b in Python 3 [...' ', ...' '] >>> SolidFill('#').render((5,3)).text [...'#####', ...'#####', ...'#####'] """ maxcol, maxrow = size return SolidCanvas(self.fill_char, maxcol, maxrow) class TextError(Exception): pass class Text(Widget): """ a horizontally resizeable text widget """ _sizing = frozenset([FLOW]) ignore_focus = True _repr_content_length_max = 140 def __init__(self, markup, align=LEFT, wrap=SPACE, layout=None): """ :param markup: content of text widget, one of: bytes or unicode text to be displayed (*display attribute*, *text markup*) *text markup* with *display attribute* applied to all parts of *text markup* with no display attribute already applied [*text markup*, *text markup*, ... ] all *text markup* in the list joined together :type markup: :ref:`text-markup` :param align: typically ``'left'``, ``'center'`` or ``'right'`` :type align: text alignment mode :param wrap: typically ``'space'``, ``'any'`` or ``'clip'`` :type wrap: text wrapping mode :param layout: defaults to a shared :class:`StandardTextLayout` instance :type layout: text layout instance >>> Text(u"Hello") >>> t = Text(('bold', u"stuff"), 'right', 'any') >>> t >>> print t.text stuff >>> t.attrib [('bold', 5)] """ self.__super.__init__() self._cache_maxcol = None self.set_text(markup) self.set_layout(align, wrap, layout) def _repr_words(self): """ Show the text in the repr in python3 format (b prefix for byte strings) and truncate if it's too long """ first = self.__super._repr_words() text = self.get_text()[0] rest = python3_repr(text) if len(rest) > self._repr_content_length_max: rest = (rest[:self._repr_content_length_max * 2 // 3 - 3] + '...' + rest[-self._repr_content_length_max // 3:]) return first + [rest] def _repr_attrs(self): attrs = dict(self.__super._repr_attrs(), align=self._align_mode, wrap=self._wrap_mode) return remove_defaults(attrs, Text.__init__) def _invalidate(self): self._cache_maxcol = None self.__super._invalidate() def set_text(self,markup): """ Set content of text widget. :param markup: see :class:`Text` for description. :type markup: text markup >>> t = Text(u"foo") >>> print t.text foo >>> t.set_text(u"bar") >>> print t.text bar >>> t.text = u"baz" # not supported because text stores text but set_text() takes markup Traceback (most recent call last): AttributeError: can't set attribute """ self._text, self._attrib = decompose_tagmarkup(markup) self._invalidate() def get_text(self): """ :returns: (*text*, *display attributes*) *text* complete bytes/unicode content of text widget *display attributes* run length encoded display attributes for *text*, eg. ``[('attr1', 10), ('attr2', 5)]`` >>> Text(u"Hello").get_text() # ... = u in Python 2 (...'Hello', []) >>> Text(('bright', u"Headline")).get_text() (...'Headline', [('bright', 8)]) >>> Text([('a', u"one"), u"two", ('b', u"three")]).get_text() (...'onetwothree', [('a', 3), (None, 3), ('b', 5)]) """ return self._text, self._attrib text = property(lambda self:self.get_text()[0], doc=""" Read-only property returning the complete bytes/unicode content of this widget """) attrib = property(lambda self:self.get_text()[1], doc=""" Read-only property returning the run-length encoded display attributes of this widget """) def set_align_mode(self, mode): """ Set text alignment mode. Supported modes depend on text layout object in use but defaults to a :class:`StandardTextLayout` instance :param mode: typically ``'left'``, ``'center'`` or ``'right'`` :type mode: text alignment mode >>> t = Text(u"word") >>> t.set_align_mode('right') >>> t.align 'right' >>> t.render((10,)).text # ... = b in Python 3 [...' word'] >>> t.align = 'center' >>> t.render((10,)).text [...' word '] >>> t.align = 'somewhere' Traceback (most recent call last): TextError: Alignment mode 'somewhere' not supported. """ if not self.layout.supports_align_mode(mode): raise TextError("Alignment mode %r not supported."% (mode,)) self._align_mode = mode self._invalidate() def set_wrap_mode(self, mode): """ Set text wrapping mode. Supported modes depend on text layout object in use but defaults to a :class:`StandardTextLayout` instance :param mode: typically ``'space'``, ``'any'`` or ``'clip'`` :type mode: text wrapping mode >>> t = Text(u"some words") >>> t.render((6,)).text # ... = b in Python 3 [...'some ', ...'words '] >>> t.set_wrap_mode('clip') >>> t.wrap 'clip' >>> t.render((6,)).text [...'some w'] >>> t.wrap = 'any' # Urwid 0.9.9 or later >>> t.render((6,)).text [...'some w', ...'ords '] >>> t.wrap = 'somehow' Traceback (most recent call last): TextError: Wrap mode 'somehow' not supported. """ if not self.layout.supports_wrap_mode(mode): raise TextError("Wrap mode %r not supported."%(mode,)) self._wrap_mode = mode self._invalidate() def set_layout(self, align, wrap, layout=None): """ Set the text layout object, alignment and wrapping modes at the same time. :type align: text alignment mode :param wrap: typically 'space', 'any' or 'clip' :type wrap: text wrapping mode :param layout: defaults to a shared :class:`StandardTextLayout` instance :type layout: text layout instance >>> t = Text(u"hi") >>> t.set_layout('right', 'clip') >>> t """ if layout is None: layout = text_layout.default_layout self._layout = layout self.set_align_mode(align) self.set_wrap_mode(wrap) align = property(lambda self:self._align_mode, set_align_mode) wrap = property(lambda self:self._wrap_mode, set_wrap_mode) layout = property(lambda self:self._layout) def render(self, size, focus=False): """ Render contents with wrapping and alignment. Return canvas. See :meth:`Widget.render` for parameter details. >>> Text(u"important things").render((18,)).text # ... = b in Python 3 [...'important things '] >>> Text(u"important things").render((11,)).text [...'important ', ...'things '] """ (maxcol,) = size text, attr = self.get_text() #assert isinstance(text, unicode) trans = self.get_line_translation( maxcol, (text,attr) ) return apply_text_layout(text, attr, trans, maxcol) def rows(self, size, focus=False): """ Return the number of rows the rendered text requires. See :meth:`Widget.rows` for parameter details. >>> Text(u"important things").rows((18,)) 1 >>> Text(u"important things").rows((11,)) 2 """ (maxcol,) = size return len(self.get_line_translation(maxcol)) def get_line_translation(self, maxcol, ta=None): """ Return layout structure used to map self.text to a canvas. This method is used internally, but may be useful for debugging custom layout classes. :param maxcol: columns available for display :type maxcol: int :param ta: ``None`` or the (*text*, *display attributes*) tuple returned from :meth:`.get_text` :type ta: text and display attributes """ if not self._cache_maxcol or self._cache_maxcol != maxcol: self._update_cache_translation(maxcol, ta) return self._cache_translation def _update_cache_translation(self,maxcol, ta): if ta: text, attr = ta else: text, attr = self.get_text() self._cache_maxcol = maxcol self._cache_translation = self._calc_line_translation( text, maxcol ) def _calc_line_translation(self, text, maxcol ): return self.layout.layout( text, self._cache_maxcol, self._align_mode, self._wrap_mode ) def pack(self, size=None, focus=False): """ Return the number of screen columns and rows required for this Text widget to be displayed without wrapping or clipping, as a single element tuple. :param size: ``None`` for unlimited screen columns or (*maxcol*,) to specify a maximum column size :type size: widget size >>> Text(u"important things").pack() (16, 1) >>> Text(u"important things").pack((15,)) (9, 2) >>> Text(u"important things").pack((8,)) (8, 2) """ text, attr = self.get_text() if size is not None: (maxcol,) = size if not hasattr(self.layout, "pack"): return size trans = self.get_line_translation( maxcol, (text,attr)) cols = self.layout.pack( maxcol, trans ) return (cols, len(trans)) i = 0 cols = 0 while i < len(text): j = text.find('\n', i) if j == -1: j = len(text) c = calc_width(text, i, j) if c>cols: cols = c i = j+1 return (cols, text.count('\n') + 1) class EditError(TextError): pass class Edit(Text): """ Text editing widget implements cursor movement, text insertion and deletion. A caption may prefix the editing area. Uses text class for text layout. Users of this class to listen for ``"change"`` events sent when the value of edit_text changes. See :func:``connect_signal``. """ # (this variable is picked up by the MetaSignals metaclass) signals = ["change"] def valid_char(self, ch): """ Filter for text that may be entered into this widget by the user :param ch: character to be inserted :type ch: bytes or unicode This implementation returns True for all printable characters. """ return is_wide_char(ch,0) or (len(ch)==1 and ord(ch) >= 32) def selectable(self): return True def __init__(self, caption=u"", edit_text=u"", multiline=False, align=LEFT, wrap=SPACE, allow_tab=False, edit_pos=None, layout=None, mask=None): """ :param caption: markup for caption preceeding edit_text, see :class:`Text` for description of text markup. :type caption: text markup :param edit_text: initial text for editing, type (bytes or unicode) must match the text in the caption :type edit_text: bytes or unicode :param multiline: True: 'enter' inserts newline False: return it :type multiline: bool :param align: typically 'left', 'center' or 'right' :type align: text alignment mode :param wrap: typically 'space', 'any' or 'clip' :type wrap: text wrapping mode :param allow_tab: True: 'tab' inserts 1-8 spaces False: return it :type allow_tab: bool :param edit_pos: initial position for cursor, None:end of edit_text :type edit_pos: int :param layout: defaults to a shared :class:`StandardTextLayout` instance :type layout: text layout instance :param mask: hide text entered with this character, None:disable mask :type mask: bytes or unicode >>> Edit() >>> Edit(u"Y/n? ", u"yes") >>> Edit(u"Name ", u"Smith", edit_pos=1) >>> Edit(u"", u"3.14", align='right') """ self.__super.__init__("", align, wrap, layout) self.multiline = multiline self.allow_tab = allow_tab self._edit_pos = 0 self.set_caption(caption) self.set_edit_text(edit_text) if edit_pos is None: edit_pos = len(edit_text) self.set_edit_pos(edit_pos) self.set_mask(mask) self._shift_view_to_cursor = False def _repr_words(self): return self.__super._repr_words()[:-1] + [ python3_repr(self._edit_text)] + [ 'caption=' + python3_repr(self._caption)] * bool(self._caption) + [ 'multiline'] * (self.multiline is True) def _repr_attrs(self): attrs = dict(self.__super._repr_attrs(), edit_pos=self._edit_pos) return remove_defaults(attrs, Edit.__init__) def get_text(self): """ Returns ``(text, display attributes)``. See :meth:`Text.get_text` for details. Text returned includes the caption and edit_text, possibly masked. >>> Edit(u"What? ","oh, nothing.").get_text() # ... = u in Python 2 (...'What? oh, nothing.', []) >>> Edit(('bright',u"user@host:~$ "),"ls").get_text() (...'user@host:~$ ls', [('bright', 13)]) >>> Edit(u"password:", u"seekrit", mask=u"*").get_text() (...'password:*******', []) """ if self._mask is None: return self._caption + self._edit_text, self._attrib else: return self._caption + (self._mask * len(self._edit_text)), self._attrib def set_text(self, markup): """ Not supported by Edit widget. >>> Edit().set_text("test") Traceback (most recent call last): EditError: set_text() not supported. Use set_caption() or set_edit_text() instead. """ # FIXME: this smells. reimplement Edit as a WidgetWrap subclass to # clean this up # hack to let Text.__init__() work if not hasattr(self, '_text') and markup == "": self._text = None return raise EditError("set_text() not supported. Use set_caption()" " or set_edit_text() instead.") def get_pref_col(self, size): """ Return the preferred column for the cursor, or the current cursor x value. May also return ``'left'`` or ``'right'`` to indicate the leftmost or rightmost column available. This method is used internally and by other widgets when moving the cursor up or down between widgets so that the column selected is one that the user would expect. >>> size = (10,) >>> Edit().get_pref_col(size) 0 >>> e = Edit(u"", u"word") >>> e.get_pref_col(size) 4 >>> e.keypress(size, 'left') >>> e.get_pref_col(size) 3 >>> e.keypress(size, 'end') >>> e.get_pref_col(size) 'right' >>> e = Edit(u"", u"2\\nwords") >>> e.keypress(size, 'left') >>> e.keypress(size, 'up') >>> e.get_pref_col(size) 4 >>> e.keypress(size, 'left') >>> e.get_pref_col(size) 0 """ (maxcol,) = size pref_col, then_maxcol = self.pref_col_maxcol if then_maxcol != maxcol: return self.get_cursor_coords((maxcol,))[0] else: return pref_col def update_text(self): """ No longer supported. >>> Edit().update_text() Traceback (most recent call last): EditError: update_text() has been removed. Use set_caption() or set_edit_text() instead. """ raise EditError("update_text() has been removed. Use " "set_caption() or set_edit_text() instead.") def set_caption(self, caption): """ Set the caption markup for this widget. :param caption: markup for caption preceeding edit_text, see :meth:`Text.__init__` for description of text markup. >>> e = Edit("") >>> e.set_caption("cap1") >>> print e.caption cap1 >>> e.set_caption(('bold', "cap2")) >>> print e.caption cap2 >>> e.attrib [('bold', 4)] >>> e.caption = "cap3" # not supported because caption stores text but set_caption() takes markup Traceback (most recent call last): AttributeError: can't set attribute """ self._caption, self._attrib = decompose_tagmarkup(caption) self._invalidate() caption = property(lambda self:self._caption) def set_edit_pos(self, pos): """ Set the cursor position with a self.edit_text offset. Clips pos to [0, len(edit_text)]. :param pos: cursor position :type pos: int >>> e = Edit(u"", u"word") >>> e.edit_pos 4 >>> e.set_edit_pos(2) >>> e.edit_pos 2 >>> e.edit_pos = -1 # Urwid 0.9.9 or later >>> e.edit_pos 0 >>> e.edit_pos = 20 >>> e.edit_pos 4 """ if pos < 0: pos = 0 if pos > len(self._edit_text): pos = len(self._edit_text) self.highlight = None self.pref_col_maxcol = None, None self._edit_pos = pos self._invalidate() edit_pos = property(lambda self:self._edit_pos, set_edit_pos) def set_mask(self, mask): """ Set the character for masking text away. :param mask: hide text entered with this character, None:disable mask :type mask: bytes or unicode """ self._mask = mask self._invalidate() def set_edit_text(self, text): """ Set the edit text for this widget. :param text: text for editing, type (bytes or unicode) must match the text in the caption :type text: bytes or unicode >>> e = Edit() >>> e.set_edit_text(u"yes") >>> print e.edit_text yes >>> e >>> e.edit_text = u"no" # Urwid 0.9.9 or later >>> print e.edit_text no """ text = self._normalize_to_caption(text) self.highlight = None self._emit("change", text) self._edit_text = text if self.edit_pos > len(text): self.edit_pos = len(text) self._invalidate() def get_edit_text(self): """ Return the edit text for this widget. >>> e = Edit(u"What? ", u"oh, nothing.") >>> print e.get_edit_text() oh, nothing. >>> print e.edit_text oh, nothing. """ return self._edit_text edit_text = property(get_edit_text, set_edit_text, doc=""" Read-only property returning the edit text for this widget. """) def insert_text(self, text): """ Insert text at the cursor position and update cursor. This method is used by the keypress() method when inserting one or more characters into edit_text. :param text: text for inserting, type (bytes or unicode) must match the text in the caption :type text: bytes or unicode >>> e = Edit(u"", u"42") >>> e.insert_text(u".5") >>> e >>> e.set_edit_pos(2) >>> e.insert_text(u"a") >>> print e.edit_text 42a.5 """ text = self._normalize_to_caption(text) result_text, result_pos = self.insert_text_result(text) self.set_edit_text(result_text) self.set_edit_pos(result_pos) self.highlight = None def _normalize_to_caption(self, text): """ Return text converted to the same type as self.caption (bytes or unicode) """ tu = isinstance(text, unicode) cu = isinstance(self._caption, unicode) if tu == cu: return text if tu: return text.encode('ascii') # follow python2's implicit conversion return text.decode('ascii') def insert_text_result(self, text): """ Return result of insert_text(text) without actually performing the insertion. Handy for pre-validation. :param text: text for inserting, type (bytes or unicode) must match the text in the caption :type text: bytes or unicode """ # if there's highlighted text, it'll get replaced by the new text text = self._normalize_to_caption(text) if self.highlight: start, stop = self.highlight btext, etext = self.edit_text[:start], self.edit_text[stop:] result_text = btext + etext result_pos = start else: result_text = self.edit_text result_pos = self.edit_pos try: result_text = (result_text[:result_pos] + text + result_text[result_pos:]) except: assert 0, repr((self.edit_text, result_text, text)) result_pos += len(text) return (result_text, result_pos) def keypress(self, size, key): """ Handle editing keystrokes, return others. >>> e, size = Edit(), (20,) >>> e.keypress(size, 'x') >>> e.keypress(size, 'left') >>> e.keypress(size, '1') >>> print e.edit_text 1x >>> e.keypress(size, 'backspace') >>> e.keypress(size, 'end') >>> e.keypress(size, '2') >>> print e.edit_text x2 >>> e.keypress(size, 'shift f1') 'shift f1' """ (maxcol,) = size p = self.edit_pos if self.valid_char(key): if (isinstance(key, unicode) and not isinstance(self._caption, unicode)): # screen is sending us unicode input, must be using utf-8 # encoding because that's all we support, so convert it # to bytes to match our caption's type key = key.encode('utf-8') self.insert_text(key) elif key=="tab" and self.allow_tab: key = " "*(8-(self.edit_pos%8)) self.insert_text(key) elif key=="enter" and self.multiline: key = "\n" self.insert_text(key) elif self._command_map[key] == CURSOR_LEFT: if p==0: return key p = move_prev_char(self.edit_text,0,p) self.set_edit_pos(p) elif self._command_map[key] == CURSOR_RIGHT: if p >= len(self.edit_text): return key p = move_next_char(self.edit_text,p,len(self.edit_text)) self.set_edit_pos(p) elif self._command_map[key] in (CURSOR_UP, CURSOR_DOWN): self.highlight = None x,y = self.get_cursor_coords((maxcol,)) pref_col = self.get_pref_col((maxcol,)) assert pref_col is not None #if pref_col is None: # pref_col = x if self._command_map[key] == CURSOR_UP: y -= 1 else: y += 1 if not self.move_cursor_to_coords((maxcol,),pref_col,y): return key elif key=="backspace": self.pref_col_maxcol = None, None if not self._delete_highlighted(): if p == 0: return key p = move_prev_char(self.edit_text,0,p) self.set_edit_text( self.edit_text[:p] + self.edit_text[self.edit_pos:] ) self.set_edit_pos( p ) elif key=="delete": self.pref_col_maxcol = None, None if not self._delete_highlighted(): if p >= len(self.edit_text): return key p = move_next_char(self.edit_text,p,len(self.edit_text)) self.set_edit_text( self.edit_text[:self.edit_pos] + self.edit_text[p:] ) elif self._command_map[key] in (CURSOR_MAX_LEFT, CURSOR_MAX_RIGHT): self.highlight = None self.pref_col_maxcol = None, None x,y = self.get_cursor_coords((maxcol,)) if self._command_map[key] == CURSOR_MAX_LEFT: self.move_cursor_to_coords((maxcol,), LEFT, y) else: self.move_cursor_to_coords((maxcol,), RIGHT, y) return else: # key wasn't handled return key def move_cursor_to_coords(self, size, x, y): """ Set the cursor position with (x,y) coordinates. Returns True if move succeeded, False otherwise. >>> size = (10,) >>> e = Edit("","edit\\ntext") >>> e.move_cursor_to_coords(size, 5, 0) True >>> e.edit_pos 4 >>> e.move_cursor_to_coords(size, 5, 3) False >>> e.move_cursor_to_coords(size, 0, 1) True >>> e.edit_pos 5 """ (maxcol,) = size trans = self.get_line_translation(maxcol) top_x, top_y = self.position_coords(maxcol, 0) if y < top_y or y >= len(trans): return False pos = calc_pos( self.get_text()[0], trans, x, y ) e_pos = pos - len(self.caption) if e_pos < 0: e_pos = 0 if e_pos > len(self.edit_text): e_pos = len(self.edit_text) self.edit_pos = e_pos self.pref_col_maxcol = x, maxcol self._invalidate() return True def mouse_event(self, size, event, button, x, y, focus): """ Move the cursor to the location clicked for button 1. >>> size = (20,) >>> e = Edit("","words here") >>> e.mouse_event(size, 'mouse press', 1, 2, 0, True) True >>> e.edit_pos 2 """ (maxcol,) = size if button==1: return self.move_cursor_to_coords( (maxcol,), x, y ) def _delete_highlighted(self): """ Delete all highlighted text and update cursor position, if any text is highlighted. """ if not self.highlight: return start, stop = self.highlight btext, etext = self.edit_text[:start], self.edit_text[stop:] self.set_edit_text( btext + etext ) self.edit_pos = start self.highlight = None return True def render(self, size, focus=False): """ Render edit widget and return canvas. Include cursor when in focus. >>> c = Edit("? ","yes").render((10,), focus=True) >>> c.text # ... = b in Python 3 [...'? yes '] >>> c.cursor (5, 0) """ (maxcol,) = size self._shift_view_to_cursor = bool(focus) canv = Text.render(self,(maxcol,)) if focus: canv = CompositeCanvas(canv) canv.cursor = self.get_cursor_coords((maxcol,)) # .. will need to FIXME if I want highlight to work again #if self.highlight: # hstart, hstop = self.highlight_coords() # d.coords['highlight'] = [ hstart, hstop ] return canv def get_line_translation(self, maxcol, ta=None ): trans = Text.get_line_translation(self, maxcol, ta) if not self._shift_view_to_cursor: return trans text, ignore = self.get_text() x,y = calc_coords( text, trans, self.edit_pos + len(self.caption) ) if x < 0: return ( trans[:y] + [shift_line(trans[y],-x)] + trans[y+1:] ) elif x >= maxcol: return ( trans[:y] + [shift_line(trans[y],-(x-maxcol+1))] + trans[y+1:] ) return trans def get_cursor_coords(self, size): """ Return the (*x*, *y*) coordinates of cursor within widget. >>> Edit("? ","yes").get_cursor_coords((10,)) (5, 0) """ (maxcol,) = size self._shift_view_to_cursor = True return self.position_coords(maxcol,self.edit_pos) def position_coords(self,maxcol,pos): """ Return (*x*, *y*) coordinates for an offset into self.edit_text. """ p = pos + len(self.caption) trans = self.get_line_translation(maxcol) x,y = calc_coords(self.get_text()[0], trans,p) return x,y class IntEdit(Edit): """Edit widget for integer values""" def valid_char(self, ch): """ Return true for decimal digits. """ return len(ch)==1 and ch in "0123456789" def __init__(self,caption="",default=None): """ caption -- caption markup default -- default edit value >>> IntEdit(u"", 42) """ if default is not None: val = str(default) else: val = "" self.__super.__init__(caption,val) def keypress(self, size, key): """ Handle editing keystrokes. Remove leading zeros. >>> e, size = IntEdit(u"", 5002), (10,) >>> e.keypress(size, 'home') >>> e.keypress(size, 'delete') >>> print e.edit_text 002 >>> e.keypress(size, 'end') >>> print e.edit_text 2 """ (maxcol,) = size unhandled = Edit.keypress(self,(maxcol,),key) if not unhandled: # trim leading zeros while self.edit_pos > 0 and self.edit_text[:1] == "0": self.set_edit_pos( self.edit_pos - 1) self.set_edit_text(self.edit_text[1:]) return unhandled def value(self): """ Return the numeric value of self.edit_text. >>> e, size = IntEdit(), (10,) >>> e.keypress(size, '5') >>> e.keypress(size, '1') >>> e.value() == 51 True """ if self.edit_text: return long(self.edit_text) else: return 0 def delegate_to_widget_mixin(attribute_name): """ Return a mixin class that delegates all standard widget methods to an attribute given by attribute_name. This mixin is designed to be used as a superclass of another widget. """ # FIXME: this is so common, let's add proper support for it # when layout and rendering are separated get_delegate = attrgetter(attribute_name) class DelegateToWidgetMixin(Widget): no_cache = ["rows"] # crufty metaclass work-around def render(self, size, focus=False): canv = get_delegate(self).render(size, focus=focus) return CompositeCanvas(canv) selectable = property(lambda self:get_delegate(self).selectable) get_cursor_coords = property( lambda self:get_delegate(self).get_cursor_coords) get_pref_col = property(lambda self:get_delegate(self).get_pref_col) keypress = property(lambda self:get_delegate(self).keypress) move_cursor_to_coords = property( lambda self:get_delegate(self).move_cursor_to_coords) rows = property(lambda self:get_delegate(self).rows) mouse_event = property(lambda self:get_delegate(self).mouse_event) sizing = property(lambda self:get_delegate(self).sizing) pack = property(lambda self:get_delegate(self).pack) return DelegateToWidgetMixin class WidgetWrapError(Exception): pass class WidgetWrap(delegate_to_widget_mixin('_wrapped_widget'), Widget): def __init__(self, w): """ w -- widget to wrap, stored as self._w This object will pass the functions defined in Widget interface definition to self._w. The purpose of this widget is to provide a base class for widgets that compose other widgets for their display and behaviour. The details of that composition should not affect users of the subclass. The subclass may decide to expose some of the wrapped widgets by behaving like a ContainerWidget or WidgetDecoration, or it may hide them from outside access. """ self._wrapped_widget = w def _set_w(self, w): """ Change the wrapped widget. This is meant to be called only by subclasses. >>> size = (10,) >>> ww = WidgetWrap(Edit("hello? ","hi")) >>> ww.render(size).text # ... = b in Python 3 [...'hello? hi '] >>> ww.selectable() True >>> ww._w = Text("goodbye") # calls _set_w() >>> ww.render(size).text [...'goodbye '] >>> ww.selectable() False """ self._wrapped_widget = w self._invalidate() _w = property(lambda self:self._wrapped_widget, _set_w) def _raise_old_name_error(self, val=None): raise WidgetWrapError("The WidgetWrap.w member variable has " "been renamed to WidgetWrap._w (not intended for use " "outside the class and its subclasses). " "Please update your code to use self._w " "instead of self.w.") w = property(_raise_old_name_error, _raise_old_name_error) def _test(): import doctest doctest.testmod() if __name__=='__main__': _test() urwid-1.3.1/urwid/raw_display.py0000664000175000017500000010464412615524560016251 0ustar ianian00000000000000#!/usr/bin/python # # Urwid raw display module # Copyright (C) 2004-2009 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ """ Direct terminal UI implementation """ import os import select import struct import sys import signal try: import fcntl import termios import tty except ImportError: pass # windows from urwid import util from urwid import escape from urwid.display_common import BaseScreen, RealTerminal, \ UPDATE_PALETTE_ENTRY, AttrSpec, UNPRINTABLE_TRANS_TABLE, \ INPUT_DESCRIPTORS_CHANGED from urwid import signals from urwid.compat import PYTHON3, bytes, B from subprocess import Popen, PIPE class Screen(BaseScreen, RealTerminal): def __init__(self, input=sys.stdin, output=sys.stdout): """Initialize a screen that directly prints escape codes to an output terminal. """ super(Screen, self).__init__() self._pal_escape = {} self._pal_attrspec = {} signals.connect_signal(self, UPDATE_PALETTE_ENTRY, self._on_update_palette_entry) self.colors = 16 # FIXME: detect this self.has_underline = True # FIXME: detect this self.register_palette_entry( None, 'default','default') self._keyqueue = [] self.prev_input_resize = 0 self.set_input_timeouts() self.screen_buf = None self._screen_buf_canvas = None self._resized = False self.maxrow = None self.gpm_mev = None self.gpm_event_pending = False self._mouse_tracking_enabled = False self.last_bstate = 0 self._setup_G1_done = False self._rows_used = None self._cy = 0 term = os.environ.get('TERM', '') self.fg_bright_is_bold = not term.startswith("xterm") self.bg_bright_is_blink = (term == "linux") self.back_color_erase = not term.startswith("screen") self._next_timeout = None # Our connections to the world self._term_output_file = output self._term_input_file = input # pipe for signalling external event loops about resize events self._resize_pipe_rd, self._resize_pipe_wr = os.pipe() fcntl.fcntl(self._resize_pipe_rd, fcntl.F_SETFL, os.O_NONBLOCK) def _on_update_palette_entry(self, name, *attrspecs): # copy the attribute to a dictionary containing the escape seqences a = attrspecs[{16:0,1:1,88:2,256:3}[self.colors]] self._pal_attrspec[name] = a self._pal_escape[name] = self._attrspec_to_escape(a) def set_input_timeouts(self, max_wait=None, complete_wait=0.125, resize_wait=0.125): """ Set the get_input timeout values. All values are in floating point numbers of seconds. max_wait -- amount of time in seconds to wait for input when there is no input pending, wait forever if None complete_wait -- amount of time in seconds to wait when get_input detects an incomplete escape sequence at the end of the available input resize_wait -- amount of time in seconds to wait for more input after receiving two screen resize requests in a row to stop Urwid from consuming 100% cpu during a gradual window resize operation """ self.max_wait = max_wait if max_wait is not None: if self._next_timeout is None: self._next_timeout = max_wait else: self._next_timeout = min(self._next_timeout, self.max_wait) self.complete_wait = complete_wait self.resize_wait = resize_wait def _sigwinch_handler(self, signum, frame): if not self._resized: os.write(self._resize_pipe_wr, B('R')) self._resized = True self.screen_buf = None def _sigcont_handler(self, signum, frame): self.stop() self.start() self._sigwinch_handler(None, None) def signal_init(self): """ Called in the startup of run wrapper to set the SIGWINCH and SIGCONT signal handlers. Override this function to call from main thread in threaded applications. """ signal.signal(signal.SIGWINCH, self._sigwinch_handler) signal.signal(signal.SIGCONT, self._sigcont_handler) def signal_restore(self): """ Called in the finally block of run wrapper to restore the SIGWINCH and SIGCONT signal handlers. Override this function to call from main thread in threaded applications. """ signal.signal(signal.SIGCONT, signal.SIG_DFL) signal.signal(signal.SIGWINCH, signal.SIG_DFL) def set_mouse_tracking(self, enable=True): """ Enable (or disable) mouse tracking. After calling this function get_input will include mouse click events along with keystrokes. """ enable = bool(enable) if enable == self._mouse_tracking_enabled: return self._mouse_tracking(enable) self._mouse_tracking_enabled = enable def _mouse_tracking(self, enable): if enable: self.write(escape.MOUSE_TRACKING_ON) self._start_gpm_tracking() else: self.write(escape.MOUSE_TRACKING_OFF) self._stop_gpm_tracking() def _start_gpm_tracking(self): if not os.path.isfile("/usr/bin/mev"): return if not os.environ.get('TERM',"").lower().startswith("linux"): return if not Popen: return m = Popen(["/usr/bin/mev","-e","158"], stdin=PIPE, stdout=PIPE, close_fds=True) fcntl.fcntl(m.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) self.gpm_mev = m def _stop_gpm_tracking(self): if not self.gpm_mev: return os.kill(self.gpm_mev.pid, signal.SIGINT) os.waitpid(self.gpm_mev.pid, 0) self.gpm_mev = None def _start(self, alternate_buffer=True): """ Initialize the screen and input mode. alternate_buffer -- use alternate screen buffer """ if alternate_buffer: self.write(escape.SWITCH_TO_ALTERNATE_BUFFER) self._rows_used = None else: self._rows_used = 0 fd = self._term_input_file.fileno() if os.isatty(fd): self._old_termios_settings = termios.tcgetattr(fd) tty.setcbreak(fd) self.signal_init() self._alternate_buffer = alternate_buffer self._next_timeout = self.max_wait if not self._signal_keys_set: self._old_signal_keys = self.tty_signal_keys(fileno=fd) signals.emit_signal(self, INPUT_DESCRIPTORS_CHANGED) # restore mouse tracking to previous state self._mouse_tracking(self._mouse_tracking_enabled) return super(Screen, self)._start() def _stop(self): """ Restore the screen. """ self.clear() signals.emit_signal(self, INPUT_DESCRIPTORS_CHANGED) self.signal_restore() fd = self._term_input_file.fileno() if os.isatty(fd): termios.tcsetattr(fd, termios.TCSADRAIN, self._old_termios_settings) self._mouse_tracking(False) move_cursor = "" if self._alternate_buffer: move_cursor = escape.RESTORE_NORMAL_BUFFER elif self.maxrow is not None: move_cursor = escape.set_cursor_position( 0, self.maxrow) self.write( self._attrspec_to_escape(AttrSpec('','')) + escape.SI + move_cursor + escape.SHOW_CURSOR) self.flush() if self._old_signal_keys: self.tty_signal_keys(*(self._old_signal_keys + (fd,))) super(Screen, self)._stop() def write(self, data): """Write some data to the terminal. You may wish to override this if you're using something other than regular files for input and output. """ self._term_output_file.write(data) def flush(self): """Flush the output buffer. You may wish to override this if you're using something other than regular files for input and output. """ self._term_output_file.flush() def get_input(self, raw_keys=False): """Return pending input as a list. raw_keys -- return raw keycodes as well as translated versions This function will immediately return all the input since the last time it was called. If there is no input pending it will wait before returning an empty list. The wait time may be configured with the set_input_timeouts function. If raw_keys is False (default) this function will return a list of keys pressed. If raw_keys is True this function will return a ( keys pressed, raw keycodes ) tuple instead. Examples of keys returned: * ASCII printable characters: " ", "a", "0", "A", "-", "/" * ASCII control characters: "tab", "enter" * Escape sequences: "up", "page up", "home", "insert", "f1" * Key combinations: "shift f1", "meta a", "ctrl b" * Window events: "window resize" When a narrow encoding is not enabled: * "Extended ASCII" characters: "\\xa1", "\\xb2", "\\xfe" When a wide encoding is enabled: * Double-byte characters: "\\xa1\\xea", "\\xb2\\xd4" When utf8 encoding is enabled: * Unicode characters: u"\\u00a5", u'\\u253c" Examples of mouse events returned: * Mouse button press: ('mouse press', 1, 15, 13), ('meta mouse press', 2, 17, 23) * Mouse drag: ('mouse drag', 1, 16, 13), ('mouse drag', 1, 17, 13), ('ctrl mouse drag', 1, 18, 13) * Mouse button release: ('mouse release', 0, 18, 13), ('ctrl mouse release', 0, 17, 23) """ assert self._started self._wait_for_input_ready(self._next_timeout) keys, raw = self.parse_input(None, None, self.get_available_raw_input()) # Avoid pegging CPU at 100% when slowly resizing if keys==['window resize'] and self.prev_input_resize: while True: self._wait_for_input_ready(self.resize_wait) keys, raw2 = self.parse_input(None, None, self.get_available_raw_input()) raw += raw2 #if not keys: # keys, raw2 = self._get_input( # self.resize_wait) # raw += raw2 if keys!=['window resize']: break if keys[-1:]!=['window resize']: keys.append('window resize') if keys==['window resize']: self.prev_input_resize = 2 elif self.prev_input_resize == 2 and not keys: self.prev_input_resize = 1 else: self.prev_input_resize = 0 if raw_keys: return keys, raw return keys def get_input_descriptors(self): """ Return a list of integer file descriptors that should be polled in external event loops to check for user input. Use this method if you are implementing your own event loop. """ if not self._started: return [] fd_list = [self._term_input_file.fileno(), self._resize_pipe_rd] if self.gpm_mev is not None: fd_list.append(self.gpm_mev.stdout.fileno()) return fd_list _current_event_loop_handles = () def unhook_event_loop(self, event_loop): """ Remove any hooks added by hook_event_loop. """ for handle in self._current_event_loop_handles: event_loop.remove_watch_file(handle) if self._input_timeout: event_loop.remove_alarm(self._input_timeout) self._input_timeout = None def hook_event_loop(self, event_loop, callback): """ Register the given callback with the event loop, to be called with new input whenever it's available. The callback should be passed a list of processed keys and a list of unprocessed keycodes. Subclasses may wish to use parse_input to wrap the callback. """ if hasattr(self, 'get_input_nonblocking'): wrapper = self._make_legacy_input_wrapper(event_loop, callback) else: wrapper = lambda: self.parse_input( event_loop, callback, self.get_available_raw_input()) fds = self.get_input_descriptors() handles = [] for fd in fds: event_loop.watch_file(fd, wrapper) self._current_event_loop_handles = handles _input_timeout = None _partial_codes = None def _make_legacy_input_wrapper(self, event_loop, callback): """ Support old Screen classes that still have a get_input_nonblocking and expect it to work. """ def wrapper(): if self._input_timeout: event_loop.remove_alarm(self._input_timeout) self._input_timeout = None timeout, keys, raw = self.get_input_nonblocking() if timeout is not None: self._input_timeout = event_loop.alarm(timeout, wrapper) callback(keys, raw) return wrapper def get_available_raw_input(self): """ Return any currently-available input. Does not block. This method is only used by the default `hook_event_loop` implementation; you can safely ignore it if you implement your own. """ codes = self._get_gpm_codes() + self._get_keyboard_codes() if self._partial_codes: codes = self._partial_codes + codes self._partial_codes = None # clean out the pipe used to signal external event loops # that a resize has occurred try: while True: os.read(self._resize_pipe_rd, 1) except OSError: pass return codes def parse_input(self, event_loop, callback, codes, wait_for_more=True): """ Read any available input from get_available_raw_input, parses it into keys, and calls the given callback. The current implementation tries to avoid any assumptions about what the screen or event loop look like; it only deals with parsing keycodes and setting a timeout when an incomplete one is detected. `codes` should be a sequence of keycodes, i.e. bytes. A bytearray is appropriate, but beware of using bytes, which only iterates as integers on Python 3. """ # Note: event_loop may be None for 100% synchronous support, only used # by get_input. Not documented because you shouldn't be doing it. if self._input_timeout and event_loop: event_loop.remove_alarm(self._input_timeout) self._input_timeout = None original_codes = codes processed = [] try: while codes: run, codes = escape.process_keyqueue( codes, wait_for_more) processed.extend(run) except escape.MoreInputRequired: # Set a timer to wait for the rest of the input; if it goes off # without any new input having come in, use the partial input k = len(original_codes) - len(codes) processed_codes = original_codes[:k] self._partial_codes = codes def _parse_incomplete_input(): self._input_timeout = None self._partial_codes = None self.parse_input( event_loop, callback, codes, wait_for_more=False) if event_loop: self._input_timeout = event_loop.alarm( self.complete_wait, _parse_incomplete_input) else: processed_codes = original_codes self._partial_codes = None if self._resized: processed.append('window resize') self._resized = False if callback: callback(processed, processed_codes) else: # For get_input return processed, processed_codes def _get_keyboard_codes(self): codes = [] while True: code = self._getch_nodelay() if code < 0: break codes.append(code) return codes def _get_gpm_codes(self): codes = [] try: while self.gpm_mev is not None and self.gpm_event_pending: codes.extend(self._encode_gpm_event()) except IOError as e: if e.args[0] != 11: raise return codes def _wait_for_input_ready(self, timeout): ready = None fd_list = [self._term_input_file.fileno()] if self.gpm_mev is not None: fd_list.append(self.gpm_mev.stdout.fileno()) while True: try: if timeout is None: ready,w,err = select.select( fd_list, [], fd_list) else: ready,w,err = select.select( fd_list,[],fd_list, timeout) break except select.error as e: if e.args[0] != 4: raise if self._resized: ready = [] break return ready def _getch(self, timeout): ready = self._wait_for_input_ready(timeout) if self.gpm_mev is not None: if self.gpm_mev.stdout.fileno() in ready: self.gpm_event_pending = True if self._term_input_file.fileno() in ready: return ord(os.read(self._term_input_file.fileno(), 1)) return -1 def _encode_gpm_event( self ): self.gpm_event_pending = False s = self.gpm_mev.stdout.readline().decode('ascii') l = s.split(",") if len(l) != 6: # unexpected output, stop tracking self._stop_gpm_tracking() signals.emit_signal(self, INPUT_DESCRIPTORS_CHANGED) return [] ev, x, y, ign, b, m = s.split(",") ev = int( ev.split("x")[-1], 16) x = int( x.split(" ")[-1] ) y = int( y.lstrip().split(" ")[0] ) b = int( b.split(" ")[-1] ) m = int( m.split("x")[-1].rstrip(), 16 ) # convert to xterm-like escape sequence last = next = self.last_bstate l = [] mod = 0 if m & 1: mod |= 4 # shift if m & 10: mod |= 8 # alt if m & 4: mod |= 16 # ctrl def append_button( b ): b |= mod l.extend([ 27, ord('['), ord('M'), b+32, x+32, y+32 ]) def determine_button_release( flag ): if b & 4 and last & 1: append_button( 0 + flag ) next |= 1 if b & 2 and last & 2: append_button( 1 + flag ) next |= 2 if b & 1 and last & 4: append_button( 2 + flag ) next |= 4 if ev == 20 or ev == 36 or ev == 52: # press if b & 4 and last & 1 == 0: append_button( 0 ) next |= 1 if b & 2 and last & 2 == 0: append_button( 1 ) next |= 2 if b & 1 and last & 4 == 0: append_button( 2 ) next |= 4 elif ev == 146: # drag if b & 4: append_button( 0 + escape.MOUSE_DRAG_FLAG ) elif b & 2: append_button( 1 + escape.MOUSE_DRAG_FLAG ) elif b & 1: append_button( 2 + escape.MOUSE_DRAG_FLAG ) else: # release if b & 4 and last & 1: append_button( 0 + escape.MOUSE_RELEASE_FLAG ) next &= ~ 1 if b & 2 and last & 2: append_button( 1 + escape.MOUSE_RELEASE_FLAG ) next &= ~ 2 if b & 1 and last & 4: append_button( 2 + escape.MOUSE_RELEASE_FLAG ) next &= ~ 4 if ev == 40: # double click (release) if b & 4 and last & 1: append_button( 0 + escape.MOUSE_MULTIPLE_CLICK_FLAG ) if b & 2 and last & 2: append_button( 1 + escape.MOUSE_MULTIPLE_CLICK_FLAG ) if b & 1 and last & 4: append_button( 2 + escape.MOUSE_MULTIPLE_CLICK_FLAG ) elif ev == 52: if b & 4 and last & 1: append_button( 0 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 ) if b & 2 and last & 2: append_button( 1 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 ) if b & 1 and last & 4: append_button( 2 + escape.MOUSE_MULTIPLE_CLICK_FLAG*2 ) self.last_bstate = next return l def _getch_nodelay(self): return self._getch(0) def get_cols_rows(self): """Return the terminal dimensions (num columns, num rows).""" y, x = 80, 24 try: buf = fcntl.ioctl(self._term_output_file.fileno(), termios.TIOCGWINSZ, ' '*4) y, x = struct.unpack('hh', buf) except IOError: # Term size could not be determined pass self.maxrow = y return x, y def _setup_G1(self): """ Initialize the G1 character set to graphics mode if required. """ if self._setup_G1_done: return while True: try: self.write(escape.DESIGNATE_G1_SPECIAL) self.flush() break except IOError: pass self._setup_G1_done = True def draw_screen(self, (maxcol, maxrow), r ): """Paint screen with rendered canvas.""" assert self._started assert maxrow == r.rows() # quick return if nothing has changed if self.screen_buf and r is self._screen_buf_canvas: return self._setup_G1() if self._resized: # handle resize before trying to draw screen return o = [escape.HIDE_CURSOR, self._attrspec_to_escape(AttrSpec('',''))] def partial_display(): # returns True if the screen is in partial display mode # ie. only some rows belong to the display return self._rows_used is not None if not partial_display(): o.append(escape.CURSOR_HOME) if self.screen_buf: osb = self.screen_buf else: osb = [] sb = [] cy = self._cy y = -1 def set_cursor_home(): if not partial_display(): return escape.set_cursor_position(0, 0) return (escape.CURSOR_HOME_COL + escape.move_cursor_up(cy)) def set_cursor_row(y): if not partial_display(): return escape.set_cursor_position(0, y) return escape.move_cursor_down(y - cy) def set_cursor_position(x, y): if not partial_display(): return escape.set_cursor_position(x, y) if cy > y: return ('\b' + escape.CURSOR_HOME_COL + escape.move_cursor_up(cy - y) + escape.move_cursor_right(x)) return ('\b' + escape.CURSOR_HOME_COL + escape.move_cursor_down(y - cy) + escape.move_cursor_right(x)) def is_blank_row(row): if len(row) > 1: return False if row[0][2].strip(): return False return True def attr_to_escape(a): if a in self._pal_escape: return self._pal_escape[a] elif isinstance(a, AttrSpec): return self._attrspec_to_escape(a) # undefined attributes use default/default # TODO: track and report these return self._attrspec_to_escape( AttrSpec('default','default')) def using_standout(a): a = self._pal_attrspec.get(a, a) return isinstance(a, AttrSpec) and a.standout ins = None o.append(set_cursor_home()) cy = 0 for row in r.content(): y += 1 if osb and y < len(osb) and osb[y] == row: # this row of the screen buffer matches what is # currently displayed, so we can skip this line sb.append( osb[y] ) continue sb.append(row) # leave blank lines off display when we are using # the default screen buffer (allows partial screen) if partial_display() and y > self._rows_used: if is_blank_row(row): continue self._rows_used = y if y or partial_display(): o.append(set_cursor_position(0, y)) # after updating the line we will be just over the # edge, but terminals still treat this as being # on the same line cy = y whitespace_at_end = False if row: a, cs, run = row[-1] if (run[-1:] == B(' ') and self.back_color_erase and not using_standout(a)): whitespace_at_end = True row = row[:-1] + [(a, cs, run.rstrip(B(' ')))] elif y == maxrow-1 and maxcol > 1: row, back, ins = self._last_row(row) first = True lasta = lastcs = None for (a,cs, run) in row: assert isinstance(run, bytes) # canvases should render with bytes if cs != 'U': run = run.translate(UNPRINTABLE_TRANS_TABLE) if first or lasta != a: o.append(attr_to_escape(a)) lasta = a if first or lastcs != cs: assert cs in [None, "0", "U"], repr(cs) if lastcs == "U": o.append( escape.IBMPC_OFF ) if cs is None: o.append( escape.SI ) elif cs == "U": o.append( escape.IBMPC_ON ) else: o.append( escape.SO ) lastcs = cs o.append( run ) first = False if ins: (inserta, insertcs, inserttext) = ins ias = attr_to_escape(inserta) assert insertcs in [None, "0", "U"], repr(insertcs) if cs is None: icss = escape.SI elif cs == "U": icss = escape.IBMPC_ON else: icss = escape.SO o += [ "\x08"*back, ias, icss, escape.INSERT_ON, inserttext, escape.INSERT_OFF ] if cs == "U": o.append(escape.IBMPC_OFF) if whitespace_at_end: o.append(escape.ERASE_IN_LINE_RIGHT) if r.cursor is not None: x,y = r.cursor o += [set_cursor_position(x, y), escape.SHOW_CURSOR ] self._cy = y if self._resized: # handle resize before trying to draw screen return try: for l in o: if isinstance(l, bytes) and PYTHON3: l = l.decode('utf-8') self.write(l) self.flush() except IOError as e: # ignore interrupted syscall if e.args[0] != 4: raise self.screen_buf = sb self._screen_buf_canvas = r def _last_row(self, row): """On the last row we need to slide the bottom right character into place. Calculate the new line, attr and an insert sequence to do that. eg. last row: XXXXXXXXXXXXXXXXXXXXYZ Y will be drawn after Z, shifting Z into position. """ new_row = row[:-1] z_attr, z_cs, last_text = row[-1] last_cols = util.calc_width(last_text, 0, len(last_text)) last_offs, z_col = util.calc_text_pos(last_text, 0, len(last_text), last_cols-1) if last_offs == 0: z_text = last_text del new_row[-1] # we need another segment y_attr, y_cs, nlast_text = row[-2] nlast_cols = util.calc_width(nlast_text, 0, len(nlast_text)) z_col += nlast_cols nlast_offs, y_col = util.calc_text_pos(nlast_text, 0, len(nlast_text), nlast_cols-1) y_text = nlast_text[nlast_offs:] if nlast_offs: new_row.append((y_attr, y_cs, nlast_text[:nlast_offs])) else: z_text = last_text[last_offs:] y_attr, y_cs = z_attr, z_cs nlast_cols = util.calc_width(last_text, 0, last_offs) nlast_offs, y_col = util.calc_text_pos(last_text, 0, last_offs, nlast_cols-1) y_text = last_text[nlast_offs:last_offs] if nlast_offs: new_row.append((y_attr, y_cs, last_text[:nlast_offs])) new_row.append((z_attr, z_cs, z_text)) return new_row, z_col-y_col, (y_attr, y_cs, y_text) def clear(self): """ Force the screen to be completely repainted on the next call to draw_screen(). """ self.screen_buf = None self.setup_G1 = True def _attrspec_to_escape(self, a): """ Convert AttrSpec instance a to an escape sequence for the terminal >>> s = Screen() >>> s.set_terminal_properties(colors=256) >>> a2e = s._attrspec_to_escape >>> a2e(s.AttrSpec('brown', 'dark green')) '\\x1b[0;33;42m' >>> a2e(s.AttrSpec('#fea,underline', '#d0d')) '\\x1b[0;38;5;229;4;48;5;164m' """ if a.foreground_high: fg = "38;5;%d" % a.foreground_number elif a.foreground_basic: if a.foreground_number > 7: if self.fg_bright_is_bold: fg = "1;%d" % (a.foreground_number - 8 + 30) else: fg = "%d" % (a.foreground_number - 8 + 90) else: fg = "%d" % (a.foreground_number + 30) else: fg = "39" st = ("1;" * a.bold + "4;" * a.underline + "5;" * a.blink + "7;" * a.standout) if a.background_high: bg = "48;5;%d" % a.background_number elif a.background_basic: if a.background_number > 7: if self.bg_bright_is_blink: bg = "5;%d" % (a.background_number - 8 + 40) else: # this doesn't work on most terminals bg = "%d" % (a.background_number - 8 + 100) else: bg = "%d" % (a.background_number + 40) else: bg = "49" return escape.ESC + "[0;%s;%s%sm" % (fg, st, bg) def set_terminal_properties(self, colors=None, bright_is_bold=None, has_underline=None): """ colors -- number of colors terminal supports (1, 16, 88 or 256) or None to leave unchanged bright_is_bold -- set to True if this terminal uses the bold setting to create bright colors (numbers 8-15), set to False if this Terminal can create bright colors without bold or None to leave unchanged has_underline -- set to True if this terminal can use the underline setting, False if it cannot or None to leave unchanged """ if colors is None: colors = self.colors if bright_is_bold is None: bright_is_bold = self.fg_bright_is_bold if has_underline is None: has_underline = self.has_underline if colors == self.colors and bright_is_bold == self.fg_bright_is_bold \ and has_underline == self.has_underline: return self.colors = colors self.fg_bright_is_bold = bright_is_bold self.has_underline = has_underline self.clear() self._pal_escape = {} for p,v in self._palette.items(): self._on_update_palette_entry(p, *v) def reset_default_terminal_palette(self): """ Attempt to set the terminal palette to default values as taken from xterm. Uses number of colors from current set_terminal_properties() screen setting. """ if self.colors == 1: return def rgb_values(n): if self.colors == 16: aspec = AttrSpec("h%d"%n, "", 256) else: aspec = AttrSpec("h%d"%n, "", self.colors) return aspec.get_rgb_values()[:3] entries = [(n,) + rgb_values(n) for n in range(self.colors)] self.modify_terminal_palette(entries) def modify_terminal_palette(self, entries): """ entries - list of (index, red, green, blue) tuples. Attempt to set part of the terminal palette (this does not work on all terminals.) The changes are sent as a single escape sequence so they should all take effect at the same time. 0 <= index < 256 (some terminals will only have 16 or 88 colors) 0 <= red, green, blue < 256 """ modify = ["%d;rgb:%02x/%02x/%02x" % (index, red, green, blue) for index, red, green, blue in entries] self.write("\x1b]4;"+";".join(modify)+"\x1b\\") self.flush() # shortcut for creating an AttrSpec with this screen object's # number of colors AttrSpec = lambda self, fg, bg: AttrSpec(fg, bg, self.colors) def _test(): import doctest doctest.testmod() if __name__=='__main__': _test() urwid-1.3.1/urwid/decoration.py0000775000175000017500000012113112615524560016053 0ustar ianian00000000000000#!/usr/bin/python # # Urwid widget decoration classes # Copyright (C) 2004-2012 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ from urwid.util import int_scale from urwid.widget import (Widget, WidgetError, BOX, FLOW, LEFT, CENTER, RIGHT, PACK, CLIP, GIVEN, RELATIVE, RELATIVE_100, TOP, MIDDLE, BOTTOM, delegate_to_widget_mixin) from urwid.split_repr import remove_defaults from urwid.canvas import CompositeCanvas, SolidCanvas from urwid.widget import Divider, Edit, Text, SolidFill # doctests class WidgetDecoration(Widget): # "decorator" was already taken """ original_widget -- the widget being decorated This is a base class for decoration widgets, widgets that contain one or more widgets and only ever have a single focus. This type of widget will affect the display or behaviour of the original_widget but it is not part of determining a chain of focus. Don't actually do this -- use a WidgetDecoration subclass instead, these are not real widgets: >>> WidgetDecoration(Text(u"hi")) > """ def __init__(self, original_widget): self._original_widget = original_widget def _repr_words(self): return self.__super._repr_words() + [repr(self._original_widget)] def _get_original_widget(self): return self._original_widget def _set_original_widget(self, original_widget): self._original_widget = original_widget self._invalidate() original_widget = property(_get_original_widget, _set_original_widget) def _get_base_widget(self): """ Return the widget without decorations. If there is only one Decoration then this is the same as original_widget. >>> t = Text('hello') >>> wd1 = WidgetDecoration(t) >>> wd2 = WidgetDecoration(wd1) >>> wd3 = WidgetDecoration(wd2) >>> wd3.original_widget is wd2 True >>> wd3.base_widget is t True """ w = self while hasattr(w, '_original_widget'): w = w._original_widget return w base_widget = property(_get_base_widget) def selectable(self): return self._original_widget.selectable() def sizing(self): return self._original_widget.sizing() class WidgetPlaceholder(delegate_to_widget_mixin('_original_widget'), WidgetDecoration): """ This is a do-nothing decoration widget that can be used for swapping between widgets without modifying the container of this widget. This can be useful for making an interface with a number of distinct pages or for showing and hiding menu or status bars. The widget displayed is stored as the self.original_widget property and can be changed by assigning a new widget to it. """ pass class AttrMapError(WidgetError): pass class AttrMap(delegate_to_widget_mixin('_original_widget'), WidgetDecoration): """ AttrMap is a decoration that maps one set of attributes to another. This object will pass all function calls and variable references to the wrapped widget. """ def __init__(self, w, attr_map, focus_map=None): """ :param w: widget to wrap (stored as self.original_widget) :type w: widget :param attr_map: attribute to apply to *w*, or dict of old display attribute: new display attribute mappings :type attr_map: display attribute or dict :param focus_map: attribute to apply when in focus or dict of old display attribute: new display attribute mappings; if ``None`` use *attr* :type focus_map: display attribute or dict >>> AttrMap(Divider(u"!"), 'bright') attr_map={None: 'bright'}> >>> AttrMap(Edit(), 'notfocus', 'focus') attr_map={None: 'notfocus'} focus_map={None: 'focus'}> >>> size = (5,) >>> am = AttrMap(Text(u"hi"), 'greeting', 'fgreet') >>> am.render(size, focus=False).content().next() # ... = b in Python 3 [('greeting', None, ...'hi ')] >>> am.render(size, focus=True).content().next() [('fgreet', None, ...'hi ')] >>> am2 = AttrMap(Text(('word', u"hi")), {'word':'greeting', None:'bg'}) >>> am2 attr_map={'word': 'greeting', None: 'bg'}> >>> am2.render(size).content().next() [('greeting', None, ...'hi'), ('bg', None, ...' ')] """ self.__super.__init__(w) if type(attr_map) != dict: self.set_attr_map({None: attr_map}) else: self.set_attr_map(attr_map) if focus_map is not None and type(focus_map) != dict: self.set_focus_map({None: focus_map}) else: self.set_focus_map(focus_map) def _repr_attrs(self): # only include the focus_attr when it takes effect (not None) d = dict(self.__super._repr_attrs(), attr_map=self._attr_map) if self._focus_map is not None: d['focus_map'] = self._focus_map return d def get_attr_map(self): # make a copy so ours is not accidentally modified # FIXME: a dictionary that detects modifications would be better return dict(self._attr_map) def set_attr_map(self, attr_map): """ Set the attribute mapping dictionary {from_attr: to_attr, ...} Note this function does not accept a single attribute the way the constructor does. You must specify {None: attribute} instead. >>> w = AttrMap(Text(u"hi"), None) >>> w.set_attr_map({'a':'b'}) >>> w attr_map={'a': 'b'}> """ for from_attr, to_attr in attr_map.items(): if not from_attr.__hash__ or not to_attr.__hash__: raise AttrMapError("%r:%r attribute mapping is invalid. " "Attributes must be hashable" % (from_attr, to_attr)) self._attr_map = attr_map self._invalidate() attr_map = property(get_attr_map, set_attr_map) def get_focus_map(self): # make a copy so ours is not accidentally modified # FIXME: a dictionary that detects modifications would be better if self._focus_map: return dict(self._focus_map) def set_focus_map(self, focus_map): """ Set the focus attribute mapping dictionary {from_attr: to_attr, ...} If None this widget will use the attr mapping instead (no change when in focus). Note this function does not accept a single attribute the way the constructor does. You must specify {None: attribute} instead. >>> w = AttrMap(Text(u"hi"), {}) >>> w.set_focus_map({'a':'b'}) >>> w attr_map={} focus_map={'a': 'b'}> >>> w.set_focus_map(None) >>> w attr_map={}> """ if focus_map is not None: for from_attr, to_attr in focus_map.items(): if not from_attr.__hash__ or not to_attr.__hash__: raise AttrMapError("%r:%r attribute mapping is invalid. " "Attributes must be hashable" % (from_attr, to_attr)) self._focus_map = focus_map self._invalidate() focus_map = property(get_focus_map, set_focus_map) def render(self, size, focus=False): """ Render wrapped widget and apply attribute. Return canvas. """ attr_map = self._attr_map if focus and self._focus_map is not None: attr_map = self._focus_map canv = self._original_widget.render(size, focus=focus) canv = CompositeCanvas(canv) canv.fill_attr_apply(attr_map) return canv class AttrWrap(AttrMap): def __init__(self, w, attr, focus_attr=None): """ w -- widget to wrap (stored as self.original_widget) attr -- attribute to apply to w focus_attr -- attribute to apply when in focus, if None use attr This widget is a special case of the new AttrMap widget, and it will pass all function calls and variable references to the wrapped widget. This class is maintained for backwards compatibility only, new code should use AttrMap instead. >>> AttrWrap(Divider(u"!"), 'bright') attr='bright'> >>> AttrWrap(Edit(), 'notfocus', 'focus') attr='notfocus' focus_attr='focus'> >>> size = (5,) >>> aw = AttrWrap(Text(u"hi"), 'greeting', 'fgreet') >>> aw.render(size, focus=False).content().next() [('greeting', None, ...'hi ')] >>> aw.render(size, focus=True).content().next() [('fgreet', None, ...'hi ')] """ self.__super.__init__(w, attr, focus_attr) def _repr_attrs(self): # only include the focus_attr when it takes effect (not None) d = dict(self.__super._repr_attrs(), attr=self.attr) del d['attr_map'] if 'focus_map' in d: del d['focus_map'] if self.focus_attr is not None: d['focus_attr'] = self.focus_attr return d # backwards compatibility, widget used to be stored as w get_w = WidgetDecoration._get_original_widget set_w = WidgetDecoration._set_original_widget w = property(get_w, set_w) def get_attr(self): return self.attr_map[None] def set_attr(self, attr): """ Set the attribute to apply to the wrapped widget >> w = AttrWrap(Divider("-"), None) >> w.set_attr('new_attr') >> w attr='new_attr'> """ self.set_attr_map({None: attr}) attr = property(get_attr, set_attr) def get_focus_attr(self): focus_map = self.focus_map if focus_map: return focus_map[None] def set_focus_attr(self, focus_attr): """ Set the attribute to apply to the wapped widget when it is in focus If None this widget will use the attr instead (no change when in focus). >> w = AttrWrap(Divider("-"), 'old') >> w.set_focus_attr('new_attr') >> w attr='old' focus_attr='new_attr'> >> w.set_focus_attr(None) >> w attr='old'> """ self.set_focus_map({None: focus_attr}) focus_attr = property(get_focus_attr, set_focus_attr) def __getattr__(self,name): """ Call getattr on wrapped widget. This has been the longstanding behaviour of AttrWrap, but is discouraged. New code should be using AttrMap and .base_widget or .original_widget instead. """ return getattr(self._original_widget, name) def sizing(self): return self._original_widget.sizing() class BoxAdapterError(Exception): pass class BoxAdapter(WidgetDecoration): """ Adapter for using a box widget where a flow widget would usually go """ no_cache = ["rows"] def __init__(self, box_widget, height): """ Create a flow widget that contains a box widget :param box_widget: box widget to wrap :type box_widget: Widget :param height: number of rows for box widget :type height: int >>> BoxAdapter(SolidFill(u"x"), 5) # 5-rows of x's height=5> """ if hasattr(box_widget, 'sizing') and BOX not in box_widget.sizing(): raise BoxAdapterError("%r is not a box widget" % box_widget) WidgetDecoration.__init__(self,box_widget) self.height = height def _repr_attrs(self): return dict(self.__super._repr_attrs(), height=self.height) # originally stored as box_widget, keep for compatibility box_widget = property(WidgetDecoration._get_original_widget, WidgetDecoration._set_original_widget) def sizing(self): return set([FLOW]) def rows(self, size, focus=False): """ Return the predetermined height (behave like a flow widget) >>> BoxAdapter(SolidFill(u"x"), 5).rows((20,)) 5 """ return self.height # The next few functions simply tack-on our height and pass through # to self._original_widget def get_cursor_coords(self, size): (maxcol,) = size if not hasattr(self._original_widget,'get_cursor_coords'): return None return self._original_widget.get_cursor_coords((maxcol, self.height)) def get_pref_col(self, size): (maxcol,) = size if not hasattr(self._original_widget,'get_pref_col'): return None return self._original_widget.get_pref_col((maxcol, self.height)) def keypress(self, size, key): (maxcol,) = size return self._original_widget.keypress((maxcol, self.height), key) def move_cursor_to_coords(self, size, col, row): (maxcol,) = size if not hasattr(self._original_widget,'move_cursor_to_coords'): return True return self._original_widget.move_cursor_to_coords((maxcol, self.height), col, row ) def mouse_event(self, size, event, button, col, row, focus): (maxcol,) = size if not hasattr(self._original_widget,'mouse_event'): return False return self._original_widget.mouse_event((maxcol, self.height), event, button, col, row, focus) def render(self, size, focus=False): (maxcol,) = size canv = self._original_widget.render((maxcol, self.height), focus) canv = CompositeCanvas(canv) return canv def __getattr__(self, name): """ Pass calls to box widget. """ return getattr(self.box_widget, name) class PaddingError(Exception): pass class Padding(WidgetDecoration): def __init__(self, w, align=LEFT, width=RELATIVE_100, min_width=None, left=0, right=0): """ :param w: a box, flow or fixed widget to pad on the left and/or right this widget is stored as self.original_widget :type w: Widget :param align: one of: ``'left'``, ``'center'``, ``'right'`` (``'relative'``, *percentage* 0=left 100=right) :param width: one of: *given width* integer number of columns for self.original_widget ``'pack'`` try to pack self.original_widget to its ideal size (``'relative'``, *percentage of total width*) make width depend on the container's width ``'clip'`` to enable clipping mode for a fixed widget :param min_width: the minimum number of columns for self.original_widget or ``None`` :type min_width: int :param left: a fixed number of columns to pad on the left :type left: int :param right: a fixed number of columns to pad on the right :type right: int Clipping Mode: (width= ``'clip'``) In clipping mode this padding widget will behave as a flow widget and self.original_widget will be treated as a fixed widget. self.original_widget will will be clipped to fit the available number of columns. For example if align is ``'left'`` then self.original_widget may be clipped on the right. >>> size = (7,) >>> def pr(w): ... for t in w.render(size).text: ... print "|%s|" % (t.decode('ascii'),) >>> pr(Padding(Text(u"Head"), ('relative', 20), 'pack')) | Head | >>> pr(Padding(Divider(u"-"), left=2, right=1)) | ---- | >>> pr(Padding(Divider(u"*"), 'center', 3)) | *** | >>> p=Padding(Text(u"1234"), 'left', 2, None, 1, 1) >>> p left=1 right=1 width=2> >>> pr(p) # align against left | 12 | | 34 | >>> p.align = 'right' >>> pr(p) # align against right | 12 | | 34 | >>> pr(Padding(Text(u"hi\\nthere"), 'right', 'pack')) # pack text first | hi | | there| """ self.__super.__init__(w) # convert obsolete parameters 'fixed left' and 'fixed right': if type(align) == tuple and align[0] in ('fixed left', 'fixed right'): if align[0]=='fixed left': left = align[1] align = LEFT else: right = align[1] align = RIGHT if type(width) == tuple and width[0] in ('fixed left', 'fixed right'): if width[0]=='fixed left': left = width[1] else: right = width[1] width = RELATIVE_100 # convert old clipping mode width=None to width='clip' if width is None: width = CLIP self.left = left self.right = right self._align_type, self._align_amount = normalize_align(align, PaddingError) self._width_type, self._width_amount = normalize_width(width, PaddingError) self.min_width = min_width def sizing(self): if self._width_type == CLIP: return set([FLOW]) return self.original_widget.sizing() def _repr_attrs(self): attrs = dict(self.__super._repr_attrs(), align=self.align, width=self.width, left=self.left, right=self.right, min_width=self.min_width) return remove_defaults(attrs, Padding.__init__) def _get_align(self): """ Return the padding alignment setting. """ return simplify_align(self._align_type, self._align_amount) def _set_align(self, align): """ Set the padding alignment. """ self._align_type, self._align_amount = normalize_align(align, PaddingError) self._invalidate() align = property(_get_align, _set_align) def _get_width(self): """ Return the padding width. """ return simplify_width(self._width_type, self._width_amount) def _set_width(self, width): """ Set the padding width. """ self._width_type, self._width_amount = normalize_width(width, PaddingError) self._invalidate() width = property(_get_width, _set_width) def render(self, size, focus=False): left, right = self.padding_values(size, focus) maxcol = size[0] maxcol -= left+right if self._width_type == CLIP: canv = self._original_widget.render((), focus) else: canv = self._original_widget.render((maxcol,)+size[1:], focus) if canv.cols() == 0: canv = SolidCanvas(' ', size[0], canv.rows()) canv = CompositeCanvas(canv) canv.set_depends([self._original_widget]) return canv canv = CompositeCanvas(canv) canv.set_depends([self._original_widget]) if left != 0 or right != 0: canv.pad_trim_left_right(left, right) return canv def padding_values(self, size, focus): """Return the number of columns to pad on the left and right. Override this method to define custom padding behaviour.""" maxcol = size[0] if self._width_type == CLIP: width, ignore = self._original_widget.pack((), focus=focus) return calculate_left_right_padding(maxcol, self._align_type, self._align_amount, CLIP, width, None, self.left, self.right) if self._width_type == PACK: maxwidth = max(maxcol - self.left - self.right, self.min_width or 0) (width, ignore) = self._original_widget.pack((maxwidth,), focus=focus) return calculate_left_right_padding(maxcol, self._align_type, self._align_amount, GIVEN, width, self.min_width, self.left, self.right) return calculate_left_right_padding(maxcol, self._align_type, self._align_amount, self._width_type, self._width_amount, self.min_width, self.left, self.right) def rows(self, size, focus=False): """Return the rows needed for self.original_widget.""" (maxcol,) = size left, right = self.padding_values(size, focus) if self._width_type == PACK: pcols, prows = self._original_widget.pack((maxcol-left-right,), focus) return prows if self._width_type == CLIP: fcols, frows = self._original_widget.pack((), focus) return frows return self._original_widget.rows((maxcol-left-right,), focus=focus) def keypress(self, size, key): """Pass keypress to self._original_widget.""" maxcol = size[0] left, right = self.padding_values(size, True) maxvals = (maxcol-left-right,)+size[1:] return self._original_widget.keypress(maxvals, key) def get_cursor_coords(self,size): """Return the (x,y) coordinates of cursor within self._original_widget.""" if not hasattr(self._original_widget,'get_cursor_coords'): return None left, right = self.padding_values(size, True) maxcol = size[0] maxvals = (maxcol-left-right,)+size[1:] if maxvals[0] == 0: return None coords = self._original_widget.get_cursor_coords(maxvals) if coords is None: return None x, y = coords return x+left, y def move_cursor_to_coords(self, size, x, y): """Set the cursor position with (x,y) coordinates of self._original_widget. Returns True if move succeeded, False otherwise. """ if not hasattr(self._original_widget,'move_cursor_to_coords'): return True left, right = self.padding_values(size, True) maxcol = size[0] maxvals = (maxcol-left-right,)+size[1:] if type(x)==int: if x < left: x = left elif x >= maxcol-right: x = maxcol-right-1 x -= left return self._original_widget.move_cursor_to_coords(maxvals, x, y) def mouse_event(self, size, event, button, x, y, focus): """Send mouse event if position is within self._original_widget.""" if not hasattr(self._original_widget,'mouse_event'): return False left, right = self.padding_values(size, focus) maxcol = size[0] if x < left or x >= maxcol-right: return False maxvals = (maxcol-left-right,)+size[1:] return self._original_widget.mouse_event(maxvals, event, button, x-left, y, focus) def get_pref_col(self, size): """Return the preferred column from self._original_widget, or None.""" if not hasattr(self._original_widget,'get_pref_col'): return None left, right = self.padding_values(size, True) maxcol = size[0] maxvals = (maxcol-left-right,)+size[1:] x = self._original_widget.get_pref_col(maxvals) if type(x) == int: return x+left return x class FillerError(Exception): pass class Filler(WidgetDecoration): def __init__(self, body, valign=MIDDLE, height=PACK, min_height=None, top=0, bottom=0): """ :param body: a flow widget or box widget to be filled around (stored as self.original_widget) :type body: Widget :param valign: one of: ``'top'``, ``'middle'``, ``'bottom'``, (``'relative'``, *percentage* 0=top 100=bottom) :param height: one of: ``'pack'`` if body is a flow widget *given height* integer number of rows for self.original_widget (``'relative'``, *percentage of total height*) make height depend on container's height :param min_height: one of: ``None`` if no minimum or if body is a flow widget *minimum height* integer number of rows for the widget when height not fixed :param top: a fixed number of rows to fill at the top :type top: int :param bottom: a fixed number of rows to fill at the bottom :type bottom: int If body is a flow widget then height must be ``'flow'`` and *min_height* will be ignored. Filler widgets will try to satisfy height argument first by reducing the valign amount when necessary. If height still cannot be satisfied it will also be reduced. """ self.__super.__init__(body) # convert old parameters to the new top/bottom values if isinstance(height, tuple): if height[0] == 'fixed top': if not isinstance(valign, tuple) or valign[0] != 'fixed bottom': raise FillerError("fixed bottom height may only be used " "with fixed top valign") top = height[1] height = RELATIVE_100 elif height[0] == 'fixed bottom': if not isinstance(valign, tuple) or valign[0] != 'fixed top': raise FillerError("fixed top height may only be used " "with fixed bottom valign") bottom = height[1] height = RELATIVE_100 if isinstance(valign, tuple): if valign[0] == 'fixed top': top = valign[1] valign = TOP elif valign[0] == 'fixed bottom': bottom = valign[1] valign = BOTTOM # convert old flow mode parameter height=None to height='flow' if height is None or height == FLOW: height = PACK self.top = top self.bottom = bottom self.valign_type, self.valign_amount = normalize_valign(valign, FillerError) self.height_type, self.height_amount = normalize_height(height, FillerError) if self.height_type not in (GIVEN, PACK): self.min_height = min_height else: self.min_height = None def sizing(self): return set([BOX]) # always a box widget def _repr_attrs(self): attrs = dict(self.__super._repr_attrs(), valign=simplify_valign(self.valign_type, self.valign_amount), height=simplify_height(self.height_type, self.height_amount), top=self.top, bottom=self.bottom, min_height=self.min_height) return remove_defaults(attrs, Filler.__init__) # backwards compatibility, widget used to be stored as body get_body = WidgetDecoration._get_original_widget set_body = WidgetDecoration._set_original_widget body = property(get_body, set_body) def selectable(self): """Return selectable from body.""" return self._original_widget.selectable() def filler_values(self, size, focus): """ Return the number of rows to pad on the top and bottom. Override this method to define custom padding behaviour. """ (maxcol, maxrow) = size if self.height_type == PACK: height = self._original_widget.rows((maxcol,),focus=focus) return calculate_top_bottom_filler(maxrow, self.valign_type, self.valign_amount, GIVEN, height, None, self.top, self.bottom) return calculate_top_bottom_filler(maxrow, self.valign_type, self.valign_amount, self.height_type, self.height_amount, self.min_height, self.top, self.bottom) def render(self, size, focus=False): """Render self.original_widget with space above and/or below.""" (maxcol, maxrow) = size top, bottom = self.filler_values(size, focus) if self.height_type == PACK: canv = self._original_widget.render((maxcol,), focus) else: canv = self._original_widget.render((maxcol,maxrow-top-bottom),focus) canv = CompositeCanvas(canv) if maxrow and canv.rows() > maxrow and canv.cursor is not None: cx, cy = canv.cursor if cy >= maxrow: canv.trim(cy-maxrow+1,maxrow-top-bottom) if canv.rows() > maxrow: canv.trim(0, maxrow) return canv canv.pad_trim_top_bottom(top, bottom) return canv def keypress(self, size, key): """Pass keypress to self.original_widget.""" (maxcol, maxrow) = size if self.height_type == PACK: return self._original_widget.keypress((maxcol,), key) top, bottom = self.filler_values((maxcol,maxrow), True) return self._original_widget.keypress((maxcol,maxrow-top-bottom), key) def get_cursor_coords(self, size): """Return cursor coords from self.original_widget if any.""" (maxcol, maxrow) = size if not hasattr(self._original_widget, 'get_cursor_coords'): return None top, bottom = self.filler_values(size, True) if self.height_type == PACK: coords = self._original_widget.get_cursor_coords((maxcol,)) else: coords = self._original_widget.get_cursor_coords( (maxcol,maxrow-top-bottom)) if not coords: return None x, y = coords if y >= maxrow: y = maxrow-1 return x, y+top def get_pref_col(self, size): """Return pref_col from self.original_widget if any.""" (maxcol, maxrow) = size if not hasattr(self._original_widget, 'get_pref_col'): return None if self.height_type == PACK: x = self._original_widget.get_pref_col((maxcol,)) else: top, bottom = self.filler_values(size, True) x = self._original_widget.get_pref_col( (maxcol, maxrow-top-bottom)) return x def move_cursor_to_coords(self, size, col, row): """Pass to self.original_widget.""" (maxcol, maxrow) = size if not hasattr(self._original_widget, 'move_cursor_to_coords'): return True top, bottom = self.filler_values(size, True) if row < top or row >= maxcol-bottom: return False if self.height_type == PACK: return self._original_widget.move_cursor_to_coords((maxcol,), col, row-top) return self._original_widget.move_cursor_to_coords( (maxcol, maxrow-top-bottom), col, row-top) def mouse_event(self, size, event, button, col, row, focus): """Pass to self.original_widget.""" (maxcol, maxrow) = size if not hasattr(self._original_widget, 'mouse_event'): return False top, bottom = self.filler_values(size, True) if row < top or row >= maxrow-bottom: return False if self.height_type == PACK: return self._original_widget.mouse_event((maxcol,), event, button, col, row-top, focus) return self._original_widget.mouse_event((maxcol, maxrow-top-bottom), event, button,col, row-top, focus) class WidgetDisable(WidgetDecoration): """ A decoration widget that disables interaction with the widget it wraps. This widget always passes focus=False to the wrapped widget, even if it somehow does become the focus. """ no_cache = ["rows"] ignore_focus = True def selectable(self): return False def rows(self, size, focus=False): return self._original_widget.rows(size, False) def sizing(self): return self._original_widget.sizing() def pack(self, size, focus=False): return self._original_widget.pack(size, False) def render(self, size, focus=False): canv = self._original_widget.render(size, False) return CompositeCanvas(canv) def normalize_align(align, err): """ Split align into (align_type, align_amount). Raise exception err if align doesn't match a valid alignment. """ if align in (LEFT, CENTER, RIGHT): return (align, None) elif type(align) == tuple and len(align) == 2 and align[0] == RELATIVE: return align raise err("align value %r is not one of 'left', 'center', " "'right', ('relative', percentage 0=left 100=right)" % (align,)) def simplify_align(align_type, align_amount): """ Recombine (align_type, align_amount) into an align value. Inverse of normalize_align. """ if align_type == RELATIVE: return (align_type, align_amount) return align_type def normalize_width(width, err): """ Split width into (width_type, width_amount). Raise exception err if width doesn't match a valid alignment. """ if width in (CLIP, PACK): return (width, None) elif type(width) == int: return (GIVEN, width) elif type(width) == tuple and len(width) == 2 and width[0] == RELATIVE: return width raise err("width value %r is not one of fixed number of columns, " "'pack', ('relative', percentage of total width), 'clip'" % (width,)) def simplify_width(width_type, width_amount): """ Recombine (width_type, width_amount) into an width value. Inverse of normalize_width. """ if width_type in (CLIP, PACK): return width_type elif width_type == GIVEN: return width_amount return (width_type, width_amount) def normalize_valign(valign, err): """ Split align into (valign_type, valign_amount). Raise exception err if align doesn't match a valid alignment. """ if valign in (TOP, MIDDLE, BOTTOM): return (valign, None) elif (isinstance(valign, tuple) and len(valign) == 2 and valign[0] == RELATIVE): return valign raise err("valign value %r is not one of 'top', 'middle', " "'bottom', ('relative', percentage 0=left 100=right)" % (valign,)) def simplify_valign(valign_type, valign_amount): """ Recombine (valign_type, valign_amount) into an valign value. Inverse of normalize_valign. """ if valign_type == RELATIVE: return (valign_type, valign_amount) return valign_type def normalize_height(height, err): """ Split height into (height_type, height_amount). Raise exception err if height isn't valid. """ if height in (FLOW, PACK): return (height, None) elif (isinstance(height, tuple) and len(height) == 2 and height[0] == RELATIVE): return height elif isinstance(height, int): return (GIVEN, height) raise err("height value %r is not one of fixed number of columns, " "'pack', ('relative', percentage of total height)" % (height,)) def simplify_height(height_type, height_amount): """ Recombine (height_type, height_amount) into an height value. Inverse of normalize_height. """ if height_type in (FLOW, PACK): return height_type elif height_type == GIVEN: return height_amount return (height_type, height_amount) def calculate_top_bottom_filler(maxrow, valign_type, valign_amount, height_type, height_amount, min_height, top, bottom): """ Return the amount of filler (or clipping) on the top and bottom part of maxrow rows to satisfy the following: valign_type -- 'top', 'middle', 'bottom', 'relative' valign_amount -- a percentage when align_type=='relative' height_type -- 'given', 'relative', 'clip' height_amount -- a percentage when width_type=='relative' otherwise equal to the height of the widget min_height -- a desired minimum width for the widget or None top -- a fixed number of rows to fill on the top bottom -- a fixed number of rows to fill on the bottom >>> ctbf = calculate_top_bottom_filler >>> ctbf(15, 'top', 0, 'given', 10, None, 2, 0) (2, 3) >>> ctbf(15, 'relative', 0, 'given', 10, None, 2, 0) (2, 3) >>> ctbf(15, 'relative', 100, 'given', 10, None, 2, 0) (5, 0) >>> ctbf(15, 'middle', 0, 'given', 4, None, 2, 0) (6, 5) >>> ctbf(15, 'middle', 0, 'given', 18, None, 2, 0) (0, 0) >>> ctbf(20, 'top', 0, 'relative', 60, None, 0, 0) (0, 8) >>> ctbf(20, 'relative', 30, 'relative', 60, None, 0, 0) (2, 6) >>> ctbf(20, 'relative', 30, 'relative', 60, 14, 0, 0) (2, 4) """ if height_type == RELATIVE: maxheight = max(maxrow - top - bottom, 0) height = int_scale(height_amount, 101, maxheight + 1) if min_height is not None: height = max(height, min_height) else: height = height_amount standard_alignments = {TOP:0, MIDDLE:50, BOTTOM:100} valign = standard_alignments.get(valign_type, valign_amount) # add the remainder of top/bottom to the filler filler = maxrow - height - top - bottom bottom += int_scale(100 - valign, 101, filler + 1) top = maxrow - height - bottom # reduce filler if we are clipping an edge if bottom < 0 < top: shift = min(top, -bottom) top -= shift bottom += shift elif top < 0 < bottom: shift = min(bottom, -top) bottom -= shift top += shift # no negative values for filler at the moment top = max(top, 0) bottom = max(bottom, 0) return top, bottom def calculate_left_right_padding(maxcol, align_type, align_amount, width_type, width_amount, min_width, left, right): """ Return the amount of padding (or clipping) on the left and right part of maxcol columns to satisfy the following: align_type -- 'left', 'center', 'right', 'relative' align_amount -- a percentage when align_type=='relative' width_type -- 'fixed', 'relative', 'clip' width_amount -- a percentage when width_type=='relative' otherwise equal to the width of the widget min_width -- a desired minimum width for the widget or None left -- a fixed number of columns to pad on the left right -- a fixed number of columns to pad on the right >>> clrp = calculate_left_right_padding >>> clrp(15, 'left', 0, 'given', 10, None, 2, 0) (2, 3) >>> clrp(15, 'relative', 0, 'given', 10, None, 2, 0) (2, 3) >>> clrp(15, 'relative', 100, 'given', 10, None, 2, 0) (5, 0) >>> clrp(15, 'center', 0, 'given', 4, None, 2, 0) (6, 5) >>> clrp(15, 'left', 0, 'clip', 18, None, 0, 0) (0, -3) >>> clrp(15, 'right', 0, 'clip', 18, None, 0, -1) (-2, -1) >>> clrp(15, 'center', 0, 'given', 18, None, 2, 0) (0, 0) >>> clrp(20, 'left', 0, 'relative', 60, None, 0, 0) (0, 8) >>> clrp(20, 'relative', 30, 'relative', 60, None, 0, 0) (2, 6) >>> clrp(20, 'relative', 30, 'relative', 60, 14, 0, 0) (2, 4) """ if width_type == RELATIVE: maxwidth = max(maxcol - left - right, 0) width = int_scale(width_amount, 101, maxwidth + 1) if min_width is not None: width = max(width, min_width) else: width = width_amount standard_alignments = {LEFT:0, CENTER:50, RIGHT:100} align = standard_alignments.get(align_type, align_amount) # add the remainder of left/right the padding padding = maxcol - width - left - right right += int_scale(100 - align, 101, padding + 1) left = maxcol - width - right # reduce padding if we are clipping an edge if right < 0 and left > 0: shift = min(left, -right) left -= shift right += shift elif left < 0 and right > 0: shift = min(right, -left) right -= shift left += shift # only clip if width_type == 'clip' if width_type != CLIP and (left < 0 or right < 0): left = max(left, 0) right = max(right, 0) return left, right def _test(): import doctest doctest.testmod() if __name__=='__main__': _test() urwid-1.3.1/urwid/util.py0000664000175000017500000003156212615524560014706 0ustar ianian00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # # Urwid utility functions # Copyright (C) 2004-2011 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ from urwid import escape from urwid.compat import bytes import codecs str_util = escape.str_util # bring str_util functions into our namespace calc_text_pos = str_util.calc_text_pos calc_width = str_util.calc_width is_wide_char = str_util.is_wide_char move_next_char = str_util.move_next_char move_prev_char = str_util.move_prev_char within_double_byte = str_util.within_double_byte def detect_encoding(): # Try to determine if using a supported double-byte encoding import locale try: try: locale.setlocale(locale.LC_ALL, "") except locale.Error: pass return locale.getlocale()[1] or "" except ValueError as e: # with invalid LANG value python will throw ValueError if e.args and e.args[0].startswith("unknown locale"): return "" else: raise if 'detected_encoding' not in locals(): detected_encoding = detect_encoding() else: assert 0, "It worked!" _target_encoding = None _use_dec_special = True def set_encoding( encoding ): """ Set the byte encoding to assume when processing strings and the encoding to use when converting unicode strings. """ encoding = encoding.lower() global _target_encoding, _use_dec_special if encoding in ( 'utf-8', 'utf8', 'utf' ): str_util.set_byte_encoding("utf8") _use_dec_special = False elif encoding in ( 'euc-jp' # JISX 0208 only , 'euc-kr', 'euc-cn', 'euc-tw' # CNS 11643 plain 1 only , 'gb2312', 'gbk', 'big5', 'cn-gb', 'uhc' # these shouldn't happen, should they? , 'eucjp', 'euckr', 'euccn', 'euctw', 'cncb' ): str_util.set_byte_encoding("wide") _use_dec_special = True else: str_util.set_byte_encoding("narrow") _use_dec_special = True # if encoding is valid for conversion from unicode, remember it _target_encoding = 'ascii' try: if encoding: u"".encode(encoding) _target_encoding = encoding except LookupError: pass def get_encoding_mode(): """ Get the mode Urwid is using when processing text strings. Returns 'narrow' for 8-bit encodings, 'wide' for CJK encodings or 'utf8' for UTF-8 encodings. """ return str_util.get_byte_encoding() def apply_target_encoding( s ): """ Return (encoded byte string, character set rle). """ if _use_dec_special and type(s) == unicode: # first convert drawing characters try: s = s.translate( escape.DEC_SPECIAL_CHARMAP ) except NotImplementedError: # python < 2.4 needs to do this the hard way.. for c, alt in zip(escape.DEC_SPECIAL_CHARS, escape.ALT_DEC_SPECIAL_CHARS): s = s.replace( c, escape.SO+alt+escape.SI ) if type(s) == unicode: s = s.replace(escape.SI+escape.SO, u"") # remove redundant shifts s = codecs.encode(s, _target_encoding, 'replace') assert isinstance(s, bytes) SO = escape.SO.encode('ascii') SI = escape.SI.encode('ascii') sis = s.split(SO) assert isinstance(sis[0], bytes) sis0 = sis[0].replace(SI, bytes()) sout = [] cout = [] if sis0: sout.append( sis0 ) cout.append( (None,len(sis0)) ) if len(sis)==1: return sis0, cout for sn in sis[1:]: assert isinstance(sn, bytes) assert isinstance(SI, bytes) sl = sn.split(SI, 1) if len(sl) == 1: sin = sl[0] assert isinstance(sin, bytes) sout.append(sin) rle_append_modify(cout, (escape.DEC_TAG.encode('ascii'), len(sin))) continue sin, son = sl son = son.replace(SI, bytes()) if sin: sout.append(sin) rle_append_modify(cout, (escape.DEC_TAG, len(sin))) if son: sout.append(son) rle_append_modify(cout, (None, len(son))) outstr = bytes().join(sout) return outstr, cout ###################################################################### # Try to set the encoding using the one detected by the locale module set_encoding( detected_encoding ) ###################################################################### def supports_unicode(): """ Return True if python is able to convert non-ascii unicode strings to the current encoding. """ return _target_encoding and _target_encoding != 'ascii' def calc_trim_text( text, start_offs, end_offs, start_col, end_col ): """ Calculate the result of trimming text. start_offs -- offset into text to treat as screen column 0 end_offs -- offset into text to treat as the end of the line start_col -- screen column to trim at the left end_col -- screen column to trim at the right Returns (start, end, pad_left, pad_right), where: start -- resulting start offset end -- resulting end offset pad_left -- 0 for no pad or 1 for one space to be added pad_right -- 0 for no pad or 1 for one space to be added """ spos = start_offs pad_left = pad_right = 0 if start_col > 0: spos, sc = calc_text_pos( text, spos, end_offs, start_col ) if sc < start_col: pad_left = 1 spos, sc = calc_text_pos( text, start_offs, end_offs, start_col+1 ) run = end_col - start_col - pad_left pos, sc = calc_text_pos( text, spos, end_offs, run ) if sc < run: pad_right = 1 return ( spos, pos, pad_left, pad_right ) def trim_text_attr_cs( text, attr, cs, start_col, end_col ): """ Return ( trimmed text, trimmed attr, trimmed cs ). """ spos, epos, pad_left, pad_right = calc_trim_text( text, 0, len(text), start_col, end_col ) attrtr = rle_subseg( attr, spos, epos ) cstr = rle_subseg( cs, spos, epos ) if pad_left: al = rle_get_at( attr, spos-1 ) rle_append_beginning_modify( attrtr, (al, 1) ) rle_append_beginning_modify( cstr, (None, 1) ) if pad_right: al = rle_get_at( attr, epos ) rle_append_modify( attrtr, (al, 1) ) rle_append_modify( cstr, (None, 1) ) return (bytes().rjust(pad_left) + text[spos:epos] + bytes().rjust(pad_right), attrtr, cstr) def rle_get_at( rle, pos ): """ Return the attribute at offset pos. """ x = 0 if pos < 0: return None for a, run in rle: if x+run > pos: return a x += run return None def rle_subseg( rle, start, end ): """Return a sub segment of an rle list.""" l = [] x = 0 for a, run in rle: if start: if start >= run: start -= run x += run continue x += start run -= start start = 0 if x >= end: break if x+run > end: run = end-x x += run l.append( (a, run) ) return l def rle_len( rle ): """ Return the number of characters covered by a run length encoded attribute list. """ run = 0 for v in rle: assert type(v) == tuple, repr(rle) a, r = v run += r return run def rle_append_beginning_modify(rle, a_r): """ Append (a, r) (unpacked from *a_r*) to BEGINNING of rle. Merge with first run when possible MODIFIES rle parameter contents. Returns None. """ a, r = a_r if not rle: rle[:] = [(a, r)] else: al, run = rle[0] if a == al: rle[0] = (a,run+r) else: rle[0:0] = [(al, r)] def rle_append_modify(rle, a_r): """ Append (a, r) (unpacked from *a_r*) to the rle list rle. Merge with last run when possible. MODIFIES rle parameter contents. Returns None. """ a, r = a_r if not rle or rle[-1][0] != a: rle.append( (a,r) ) return la,lr = rle[-1] rle[-1] = (a, lr+r) def rle_join_modify( rle, rle2 ): """ Append attribute list rle2 to rle. Merge last run of rle with first run of rle2 when possible. MODIFIES attr parameter contents. Returns None. """ if not rle2: return rle_append_modify(rle, rle2[0]) rle += rle2[1:] def rle_product( rle1, rle2 ): """ Merge the runs of rle1 and rle2 like this: eg. rle1 = [ ("a", 10), ("b", 5) ] rle2 = [ ("Q", 5), ("P", 10) ] rle_product: [ (("a","Q"), 5), (("a","P"), 5), (("b","P"), 5) ] rle1 and rle2 are assumed to cover the same total run. """ i1 = i2 = 1 # rle1, rle2 indexes if not rle1 or not rle2: return [] a1, r1 = rle1[0] a2, r2 = rle2[0] l = [] while r1 and r2: r = min(r1, r2) rle_append_modify( l, ((a1,a2),r) ) r1 -= r if r1 == 0 and i1< len(rle1): a1, r1 = rle1[i1] i1 += 1 r2 -= r if r2 == 0 and i2< len(rle2): a2, r2 = rle2[i2] i2 += 1 return l def rle_factor( rle ): """ Inverse of rle_product. """ rle1 = [] rle2 = [] for (a1, a2), r in rle: rle_append_modify( rle1, (a1, r) ) rle_append_modify( rle2, (a2, r) ) return rle1, rle2 class TagMarkupException(Exception): pass def decompose_tagmarkup(tm): """Return (text string, attribute list) for tagmarkup passed.""" tl, al = _tagmarkup_recurse(tm, None) # join as unicode or bytes based on type of first element text = tl[0][:0].join(tl) if al and al[-1][0] is None: del al[-1] return text, al def _tagmarkup_recurse( tm, attr ): """Return (text list, attribute list) for tagmarkup passed. tm -- tagmarkup attr -- current attribute or None""" if type(tm) == list: # for lists recurse to process each subelement rtl = [] ral = [] for element in tm: tl, al = _tagmarkup_recurse( element, attr ) if ral: # merge attributes when possible last_attr, last_run = ral[-1] top_attr, top_run = al[0] if last_attr == top_attr: ral[-1] = (top_attr, last_run + top_run) del al[-1] rtl += tl ral += al return rtl, ral if type(tm) == tuple: # tuples mark a new attribute boundary if len(tm) != 2: raise TagMarkupException("Tuples must be in the form (attribute, tagmarkup): %r" % (tm,)) attr, element = tm return _tagmarkup_recurse( element, attr ) if not isinstance(tm,(basestring, bytes)): raise TagMarkupException("Invalid markup element: %r" % tm) # text return [tm], [(attr, len(tm))] def is_mouse_event( ev ): return type(ev) == tuple and len(ev)==4 and ev[0].find("mouse")>=0 def is_mouse_press( ev ): return ev.find("press")>=0 class MetaSuper(type): """adding .__super""" def __init__(cls, name, bases, d): super(MetaSuper, cls).__init__(name, bases, d) if hasattr(cls, "_%s__super" % name): raise AttributeError("Class has same name as one of its super classes") setattr(cls, "_%s__super" % name, super(cls)) def int_scale(val, val_range, out_range): """ Scale val in the range [0, val_range-1] to an integer in the range [0, out_range-1]. This implementation uses the "round-half-up" rounding method. >>> "%x" % int_scale(0x7, 0x10, 0x10000) '7777' >>> "%x" % int_scale(0x5f, 0x100, 0x10) '6' >>> int_scale(2, 6, 101) 40 >>> int_scale(1, 3, 4) 2 """ num = int(val * (out_range-1) * 2 + (val_range-1)) dem = ((val_range-1) * 2) # if num % dem == 0 then we are exactly half-way and have rounded up. return num // dem class StoppingContext(object): """Context manager that calls ``stop`` on a given object on exit. Used to make the ``start`` method on `MainLoop` and `BaseScreen` optionally act as context managers. """ def __init__(self, wrapped): self._wrapped = wrapped def __enter__(self): return self def __exit__(self, *exc_info): self._wrapped.stop() urwid-1.3.1/urwid/compat.py0000664000175000017500000000301212615524560015201 0ustar ianian00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # # Urwid python compatibility definitions # Copyright (C) 2011 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ import sys try: # python 2.4 and 2.5 compat bytes = bytes except NameError: bytes = str PYTHON3 = sys.version_info > (3, 0) # for iterating over byte strings: # ord2 calls ord in python2 only # chr2 converts an ordinal value to a length-1 byte string # B returns a byte string in all supported python versions # bytes3 creates a byte string from a list of ordinal values if PYTHON3: ord2 = lambda x: x chr2 = lambda x: bytes([x]) B = lambda x: x.encode('iso8859-1') bytes3 = bytes else: ord2 = ord chr2 = chr B = lambda x: x bytes3 = lambda x: bytes().join([chr(c) for c in x]) urwid-1.3.1/urwid/version.py0000664000175000017500000000014112615524560015403 0ustar ianian00000000000000 VERSION = (1, 3, 1) __version__ = ''.join(['-.'[type(x) == int]+str(x) for x in VERSION])[1:] urwid-1.3.1/urwid/font.py0000775000175000017500000005727212615524560014710 0ustar ianian00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # # Urwid BigText fonts # Copyright (C) 2004-2006 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ from urwid.escape import SAFE_ASCII_DEC_SPECIAL_RE from urwid.util import apply_target_encoding, str_util from urwid.canvas import TextCanvas def separate_glyphs(gdata, height): """return (dictionary of glyphs, utf8 required)""" gl = gdata.split("\n") del gl[0] del gl[-1] for g in gl: assert "\t" not in g assert len(gl) == height+1, repr(gdata) key_line = gl[0] del gl[0] c = None # current character key_index = 0 # index into character key line end_col = 0 # column position at end of glyph start_col = 0 # column position at start of glyph jl = [0]*height # indexes into lines of gdata (gl) dout = {} utf8_required = False while True: if c is None: if key_index >= len(key_line): break c = key_line[key_index] if key_index < len(key_line) and key_line[key_index] == c: end_col += str_util.get_width(ord(c)) key_index += 1 continue out = [] for k in range(height): l = gl[k] j = jl[k] y = 0 fill = 0 while y < end_col - start_col: if j >= len(l): fill = end_col - start_col - y break y += str_util.get_width(ord(l[j])) j += 1 assert y + fill == end_col - start_col, \ repr((y, fill, end_col)) segment = l[jl[k]:j] if not SAFE_ASCII_DEC_SPECIAL_RE.match(segment): utf8_required = True out.append(segment + " " * fill) jl[k] = j start_col = end_col dout[c] = (y + fill, out) c = None return dout, utf8_required _all_fonts = [] def get_all_fonts(): """ Return a list of (font name, font class) tuples. """ return _all_fonts[:] def add_font(name, cls): _all_fonts.append((name, cls)) class Font(object): def __init__(self): assert self.height assert self.data self.char = {} self.canvas = {} self.utf8_required = False for gdata in self.data: self.add_glyphs(gdata) def add_glyphs(self, gdata): d, utf8_required = separate_glyphs(gdata, self.height) self.char.update(d) self.utf8_required |= utf8_required def characters(self): l = self.char.keys() l.sort() return "".join(l) def char_width(self, c): if c in self.char: return self.char[c][0] return 0 def char_data(self, c): return self.char[c][1] def render(self, c): if c in self.canvas: return self.canvas[c] width, l = self.char[c] tl = [] csl = [] for d in l: t, cs = apply_target_encoding(d) tl.append(t) csl.append(cs) canv = TextCanvas(tl, None, csl, maxcol=width, check_width=False) self.canvas[c] = canv return canv #safe_palette = u"┘â”┌└┼─├┤┴┬│" #more_palette = u"â•║╒╓╔╕╖╗╘╙╚╛╜â•╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬○" #block_palette = u"â–„#â–ˆ#â–€#â–Œ#â–#â––#â–—#â–˜#â–™#â–š#â–›#â–œ#â–#â–ž#â–Ÿ" class Thin3x3Font(Font): height = 3 data = [u""" 000111222333444555666777888999 ! ┌─┠┠┌─â”┌─┠â”┌─ ┌─ ┌─â”┌─â”┌─┠│ │ │ │ ┌─┘ ─┤└─┼└─â”├─┠┼├─┤└─┤ │ └─┘ â”´ └─ └─┘ â”´ ─┘└─┘ ┴└─┘ ─┘ . """, ur""" "###$$$%%%'*++,--.///:;==???[[\\\]]^__` " ┼┼┌┼â”O /' /.. _┌─â”┌ \ â”^ ` ┼┼└┼┠/ * ┼ ─ / ., _ ┌┘│ \ │ └┼┘/ O , ./ . â”” \ ┘ ── """] add_font("Thin 3x3",Thin3x3Font) class Thin4x3Font(Font): height = 3 data = Thin3x3Font.data + [u""" 0000111122223333444455556666777788889999 ####$$$$ ┌──┠┠┌──â”┌──┠â”┌── ┌── ┌──â”┌──â”┌──┠┼─┼┌┼┼┠│ │ │ ┌──┘ ─┤└──┼└──â”├──┠┼├──┤└──┤ ┼─┼└┼┼┠└──┘ â”´ └── └──┘ â”´ ──┘└──┘ ┴└──┘ ──┘ └┼┼┘ """] add_font("Thin 4x3",Thin4x3Font) class HalfBlock5x4Font(Font): height = 4 data = [u""" 00000111112222233333444445555566666777778888899999 !! ▄▀▀▄ â–„â–ˆ ▄▀▀▄ ▄▀▀▄ â–„ â–ˆ █▀▀▀ ▄▀▀ ▀▀▀█ ▄▀▀▄ ▄▀▀▄ â–ˆ â–ˆ â–ˆ â–ˆ â–„â–€ â–„â–€ █▄▄█ █▄▄ █▄▄ â–â–Œ ▀▄▄▀ ▀▄▄█ â–ˆ â–ˆ â–ˆ â–ˆ â–„â–€ â–„ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–€ ▀▀ ▀▀▀ ▀▀▀▀ ▀▀ â–€ ▀▀▀ ▀▀ â–€ ▀▀ ▀▀ â–€ """, u''' """######$$$$$$%%%%%&&&&&((()))******++++++,,,-----..////:::;; â–ˆâ–â–Œ â–ˆ â–ˆ ▄▀█▀▄ â–â–Œâ–â–Œ ▄▀▄ â–ˆ â–ˆ â–„ â–„ â–„ â–â–Œ ▀█▀█▀ ▀▄█▄ â–ˆ ▀▄▀ â–â–Œ â–â–Œ ▄▄█▄▄ ▄▄█▄▄ â–„â–„â–„â–„ â–ˆ â–€ â–€ ▀█▀█▀ â–„ â–ˆ â–ˆ â–▌▄ â–ˆ ▀▄▌â–â–Œ â–â–Œ ▄▀▄ â–ˆ â–â–Œ â–€ â–„â–€ â–€ â–€ ▀▀▀ â–€ â–€ ▀▀ â–€ â–€ â–„â–€ â–€ â–€ ''', ur""" <<<<<=====>>>>>?????@@@@@@[[[[\\\\]]]]^^^^____```{{{{||}}}}~~~~''´´´ â–„â–€ ▀▄ ▄▀▀▄ ▄▀▀▀▄ █▀▀ â–â–Œ ▀▀█ ▄▀▄ ▀▄ â–„â–€ â–ˆ ▀▄ â–„ â–ˆ â–„â–€ â–„â–€ ▀▀▀▀ ▀▄ â–„â–€ â–ˆ █▀█ â–ˆ â–ˆ â–ˆ â–„â–€ â–ˆ ▀▄ â–â–▌▌ ▀▄ ▀▀▀▀ â–„â–€ â–€ â–ˆ ▀▀▀ â–ˆ â–â–Œ â–ˆ â–ˆ â–ˆ â–ˆ â–€ â–€ â–€ â–€ ▀▀▀ ▀▀▀ â–€ ▀▀▀ ▀▀▀▀ â–€ â–€ â–€ """, u''' AAAAABBBBBCCCCCDDDDDEEEEEFFFFFGGGGGHHHHHIIJJJJJKKKKK ▄▀▀▄ █▀▀▄ ▄▀▀▄ █▀▀▄ █▀▀▀ █▀▀▀ ▄▀▀▄ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ █▄▄█ █▄▄▀ â–ˆ â–ˆ â–ˆ █▄▄ █▄▄ â–ˆ █▄▄█ â–ˆ â–ˆ █▄▀ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–„ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ ▀█ â–ˆ â–ˆ â–ˆ â–„ â–ˆ â–ˆ ▀▄ â–€ â–€ ▀▀▀ ▀▀ ▀▀▀ ▀▀▀▀ â–€ ▀▀ â–€ â–€ â–€ ▀▀ â–€ â–€ ''', u''' LLLLLMMMMMMNNNNNOOOOOPPPPPQQQQQRRRRRSSSSSTTTTT â–ˆ █▄ â–„â–ˆ ██ â–ˆ ▄▀▀▄ █▀▀▄ ▄▀▀▄ █▀▀▄ ▄▀▀▄ ▀▀█▀▀ â–ˆ â–ˆ â–€ â–ˆ â–ˆâ–▌█ â–ˆ â–ˆ █▄▄▀ â–ˆ â–ˆ █▄▄▀ ▀▄▄ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ ██ â–ˆ â–ˆ â–ˆ â–ˆ ▌█ â–ˆ â–ˆ â–„ â–ˆ â–ˆ ▀▀▀▀ â–€ â–€ â–€ â–€ ▀▀ â–€ ▀▀▌ â–€ â–€ ▀▀ â–€ ''', u''' UUUUUVVVVVVWWWWWWXXXXXXYYYYYYZZZZZ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ ▀▀▀█ â–ˆ â–ˆ â–â–Œ â–â–Œ â–ˆ â–„ â–ˆ ▀▄▀ ▀▄▀ â–„â–€ â–ˆ â–ˆ â–ˆ â–ˆ â–▌█â–â–Œ â–„â–€ ▀▄ â–ˆ â–ˆ ▀▀ â–€ â–€ â–€ â–€ â–€ â–€ ▀▀▀▀ ''', u''' aaaaabbbbbcccccdddddeeeeeffffggggghhhhhiijjjjkkkkk â–ˆ â–ˆ ▄▀▀ â–ˆ â–„ â–„ â–ˆ ▀▀▄ █▀▀▄ ▄▀▀▄ ▄▀▀█ ▄▀▀▄ ▀█▀ ▄▀▀▄ █▀▀▄ â–„ â–„ â–ˆ â–„â–€ ▄▀▀█ â–ˆ â–ˆ â–ˆ â–„ â–ˆ â–ˆ █▀▀ â–ˆ ▀▄▄█ â–ˆ â–ˆ â–ˆ â–ˆ █▀▄ ▀▀▀ ▀▀▀ ▀▀ ▀▀▀ ▀▀ â–€ â–„â–„â–€ â–€ â–€ â–€ â–„â–„â–€ â–€ â–€ ''', u''' llmmmmmmnnnnnooooopppppqqqqqrrrrssssstttt â–ˆ â–ˆ â–ˆ █▀▄▀▄ █▀▀▄ ▄▀▀▄ █▀▀▄ ▄▀▀█ █▀▀ ▄▀▀▀ ▀█▀ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ ▀▀▄ â–ˆ â–€ â–€ â–€ â–€ â–€ ▀▀ █▀▀ ▀▀█ â–€ ▀▀▀ â–€ ''', u''' uuuuuvvvvvwwwwwwxxxxxxyyyyyzzzzz â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–„ â–ˆ ▀▄ â–„â–€ â–ˆ â–ˆ ▀▀█▀ â–ˆ â–ˆ â–â–Œâ–â–Œ â–▌█â–â–Œ ▄▀▄ ▀▄▄█ â–„â–€ ▀▀ ▀▀ â–€ â–€ â–€ â–€ â–„â–„â–€ ▀▀▀▀ '''] add_font("Half Block 5x4",HalfBlock5x4Font) class HalfBlock6x5Font(Font): height = 5 data = [u""" 000000111111222222333333444444555555666666777777888888999999 ..:://// ▄▀▀▀▄ â–„â–ˆ ▄▀▀▀▄ ▄▀▀▀▄ â–„ â–ˆ █▀▀▀▀ ▄▀▀▀ ▀▀▀▀█ ▄▀▀▀▄ ▄▀▀▀▄ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–â–Œ â–ˆ â–ˆ â–ˆ â–ˆ â–€ â–â–Œ â–ˆ â–ˆ â–ˆ â–„â–€ ▀▀▄ ▀▀▀█▀ ▀▀▀▀▄ █▀▀▀▄ â–ˆ ▄▀▀▀▄ ▀▀▀█ â–„ â–ˆ â–ˆ â–ˆ â–ˆ â–„â–€ â–„ â–ˆ â–ˆ â–ˆ â–ˆ â–ˆ â–â–Œ â–ˆ â–ˆ â–ˆ â–â–Œ ▀▀▀ ▀▀▀ ▀▀▀▀▀ ▀▀▀ â–€ ▀▀▀▀ ▀▀▀ â–€ ▀▀▀ ▀▀▀ â–€ â–€ """] add_font("Half Block 6x5",HalfBlock6x5Font) class HalfBlockHeavy6x5Font(Font): height = 5 data = [u""" 000000111111222222333333444444555555666666777777888888999999 ..:://// ▄███▄ â–█▌ ▄███▄ ▄███▄ █▌ █████ ▄███▄ █████ ▄███▄ ▄███▄ █▌ █▌ â–â–ˆ ▀█▌ â–€ â–â–ˆ â–€ â–â–ˆ █▌ █▌ █▌ █▌ █▌ █▌ â–â–ˆ █▌ â–â–ˆ █▌ â–â–ˆ █▌ â–â–ˆ █▌ ▄█▀ ██▌ █████ ████▄ ████▄ â–â–ˆ â–███▌ ▀████ █▌ █▌ â–â–ˆ █▌ ▄█▀ â–„ â–â–ˆ █▌ â–â–ˆ █▌ â–â–ˆ █▌ █▌ â–â–ˆ â–â–ˆ █▌â–â–ˆ ▀███▀ ███▌ █████ ▀███▀ █▌ ████▀ ▀███▀ â–â–ˆ ▀███▀ ▀███▀ █▌ █▌ """] add_font("Half Block Heavy 6x5",HalfBlockHeavy6x5Font) class Thin6x6Font(Font): height = 6 data = [u""" 000000111111222222333333444444555555666666777777888888999999'' ┌───┠┠┌───┠┌───┠┠┌─── ┌─── ┌───┠┌───┠┌───┠│ │ │ │ │ │ ┌ │ │ │ │ │ │ │ │ │ / │ │ ┌───┘ ─┤ └──┼─ └───┠├───┠┼ ├───┤ └───┤ │ │ │ │ │ │ │ │ │ │ │ │ │ └───┘ â”´ └─── └───┘ â”´ ───┘ └───┘ â”´ └───┘ ───┘ """, ur''' !! """######$$$$$$%%%%%%&&&&&&((()))******++++++ │ ││ ┌ ┌ ┌─┼─┠┌┠/ ┌─┠/ \ │ ─┼─┼─ │ │ └┘ / │ │ │ │ \ / │ │ │ │ └─┼─┠/ ┌─\┘ │ │ ──X── ──┼── │ ─┼─┼─ │ │ / ┌┠│ \, │ │ / \ │ . ┘ ┘ └─┼─┘ / └┘ └───\ \ / ''', ur""" ,,-----..//////::;;<<<<=====>>>>??????@@@@@@ / ┌───┠┌───┠/ . . / ──── \ │ │┌──┤ ──── / / \ ┌─┘ ││ │ / . , \ ──── / │ │└──┘ , . / \ / . └───┘ """, ur""" [[\\\\\\]]^^^____``{{||}}~~~~~~ ┌ \ â” /\ \ ┌ │ ┠│ \ │ │ │ │ ┌─┠│ \ │ ┤ │ ├ └─┘ │ \ │ │ │ │ â”” \ ┘ ──── â”” │ ┘ """, u""" AAAAAABBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGGHHHHHHIIJJJJJJ ┌───┠┬───┠┌───┠┬───┠┬───┠┬───┠┌───┠┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├───┤ ├───┤ │ │ │ ├── ├── │ ──┬ ├───┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┬ │ â”´ â”´ ┴───┘ └───┘ ┴───┘ ┴───┘ â”´ └───┘ â”´ â”´ â”´ └───┘ """, u""" KKKKKKLLLLLLMMMMMMNNNNNNOOOOOOPPPPPPQQQQQQRRRRRRSSSSSS ┬ ┬ ┬ ┌─┬─┠┬─┠┬ ┌───┠┬───┠┌───┠┬───┠┌───┠│ ┌─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─┴┠│ │ │ │ │ │ │ │ │ ├───┘ │ │ ├─┬─┘ └───┠│ └┠│ │ │ │ │ │ │ │ │ │ â”│ │ └─┠│ â”´ â”´ ┴───┘ â”´ â”´ â”´ └─┴ └───┘ â”´ └──┼┘ â”´ â”´ └───┘ â”” """, u""" TTTTTTUUUUUUVVVVVVWWWWWWXXXXXXYYYYYYZZZZZZ ┌─┬─┠┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┌───┠│ │ │ │ │ │ │ └┠┌┘ │ │ ┌─┘ │ │ │ │ │ │ │ │ ├─┤ └─┬─┘ ┌┘ │ │ │ └┠┌┘ │ │ │ ┌┘ └┠│ ┌┘ â”´ └───┘ └─┘ └─┴─┘ â”´ â”´ â”´ └───┘ """, u""" aaaaaabbbbbbccccccddddddeeeeeefffgggggghhhhhhiijjj ┌─┠│ │ │ │ . . ┌───┠├───┠┌───┠┌───┤ ┌───┠┼ ┌───┠├───┠┠┠┌───┤ │ │ │ │ │ ├───┘ │ │ │ │ │ │ │ └───┴ └───┘ └───┘ └───┘ └───┘ â”´ └───┤ â”´ â”´ â”´ │ └───┘ ─┘ """, u""" kkkkkkllmmmmmmnnnnnnooooooppppppqqqqqqrrrrrssssss │ │ │ ┌─ │ ┬─┬─┠┬───┠┌───┠┌───┠┌───┠┬──┠┌───┠├─┴┠│ │ │ │ │ │ │ │ │ │ │ │ │ └───┠┴ └─ â”” â”´ â”´ â”´ â”´ └───┘ ├───┘ └───┤ â”´ └───┘ │ │ """, u""" ttttuuuuuuvvvvvvwwwwwwxxxxxxyyyyyyzzzzzz │ ─┼─ ┬ ┬ ┬ ┬ ┬ ┬ ─┠┌─ ┬ ┬ ────┬ │ │ │ └┠┌┘ │ │ │ ├─┤ │ │ ┌───┘ └─ └───┴ └─┘ └─┴─┘ ─┘ └─ └───┤ ┴──── └───┘ """] add_font("Thin 6x6",Thin6x6Font) class HalfBlock7x7Font(Font): height = 7 data = [u""" 0000000111111122222223333333444444455555556666666777777788888889999999''' ▄███▄ â–█▌ ▄███▄ ▄███▄ █▌ â–█████▌ ▄███▄ â–█████▌ ▄███▄ ▄███▄ â–â–ˆ â–â–ˆ █▌ ▀█▌ â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌ â–â–ˆ â–â–ˆ â–â–ˆ â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ â–â–ˆ ■█▌ █▌ █▌ â–██ â–█████▌â–████▄ â–████▄ █▌ █████ ▀████▌ â–â–ˆ â–Œ █▌ █▌ ▄█▀ █▌ █▌ █▌â–â–ˆ █▌ â–â–ˆ â–â–ˆ █▌ █▌ â–â–ˆ █▌ █▌ ▄█▀ â–â–ˆ █▌ █▌ █▌â–â–ˆ █▌ █▌ â–â–ˆ █▌ █▌ ▀███▀ ███▌ â–█████▌ ▀███▀ █▌ â–████▀ ▀███▀ â–â–ˆ ▀███▀ ▀███▀ """, u''' !!! """""#######$$$$$$$%%%%%%%&&&&&&&(((())))*******++++++ â–â–ˆ â–â–ˆ █▌ â–â–ˆ █▌ â–ˆ â–„ █▌ ▄█▄ █▌â–â–ˆ â–„â–„ â–„â–„ â–â–ˆ â–â–ˆ █▌â–█████▌ ▄███▄ â–█▌â–â–ˆ â–â–ˆ █▌ â–â–ˆ █▌ ▀█▄█▀ â–â–ˆ â–â–ˆ â–â–ˆ █▌ â–█▄█▄▄ â–€ █▌ ███ █▌ â–â–ˆ â–█████▌ ████▌ â–â–ˆ â–█████▌ ▀▀█▀█▌ â–â–ˆ â–„ ███▌▄ █▌ â–â–ˆ ▄█▀█▄ â–â–ˆ â–â–ˆ █▌ ▀███▀ █▌â–█▌â–â–ˆ █▌ â–â–ˆ █▌ ▀▀ ▀▀ â–â–ˆ â–ˆ â–â–ˆ â–€ ▀██▀█▌ █▌â–â–ˆ ''', u""" ,,,------.../////:::;;;<<<<<<<======>>>>>>>???????@@@@@@@ █▌ ▄█▌ â–█▄ ▄███▄ ▄███▄ â–â–ˆ â–â–ˆ â–â–ˆ ▄█▀ â–████▌ ▀█▄ â–â–ˆ █▌â–â–ˆ ▄▄█▌ â–████▌ █▌ â–██ ██▌ █▌ â–â–ˆâ–█▀█▌ â–â–ˆ â–â–ˆ â–â–ˆ ▀█▄ â–████▌ ▄█▀ █▌ â–â–ˆâ–█▄█▌ █▌ â–€ ▀█▌ â–█▀ â–â–ˆ ▀▀▀ â–â–ˆ â–â–ˆ â–â–ˆ █▌ ▀███▀ â–€ """, ur""" [[[[\\\\\]]]]^^^^^^^_____```{{{{{|||}}}}}~~~~~~~´´´ â–██▌â–â–ˆ â–██▌ â–█▌ â–â–ˆ █▌â–â–ˆ â–â–ˆ █▌ â–â–ˆ █▌ █▌ â–â–ˆ █▌ █▌ █▌ â–â–ˆ â–â–ˆ â–„â–„ â–â–ˆ â–â–ˆ â–â–ˆ █▌â–â–ˆ █▌ ▄█▌ â–â–ˆ â–█▄ â–▀▀█▄▄▌ â–â–ˆ █▌ █▌ ▀█▌ â–â–ˆ â–█▀ ▀▀ â–â–ˆ â–â–ˆ █▌ █▌ â–â–ˆ â–â–ˆ â–██▌ █▌â–██▌ █████ █▌â–â–ˆ â–â–ˆ """, u""" AAAAAAABBBBBBBCCCCCCCDDDDDDDEEEEEEEFFFFFFFGGGGGGGHHHHHHHIIIIJJJJJJJ ▄███▄ â–████▄ ▄███▄ â–████▄ â–█████▌â–█████▌ ▄███▄ â–â–ˆ █▌ ██▌ █▌ â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ â–â–ˆ █▌â–â–ˆ â–â–ˆ â–â–ˆ â–â–ˆ █▌ â–â–ˆ █▌ â–█████▌â–█████ â–â–ˆ â–â–ˆ █▌â–████ â–████ â–â–ˆ â–█████▌ â–â–ˆ █▌ â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ â–â–ˆ █▌â–â–ˆ â–â–ˆ â–â–ˆ ██▌â–â–ˆ █▌ â–â–ˆ █▌ â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ â–â–ˆ █▌â–â–ˆ â–â–ˆ â–â–ˆ █▌â–â–ˆ █▌ â–â–ˆ â–â–ˆ █▌ â–â–ˆ █▌â–████▀ ▀███▀ â–████▀ â–█████▌â–â–ˆ ▀███▀ â–â–ˆ █▌ ██▌ ▀███▀ """, u""" KKKKKKKLLLLLLLMMMMMMMMNNNNNNNOOOOOOOPPPPPPPQQQQQQQRRRRRRRSSSSSSS â–â–ˆ █▌â–â–ˆ ▄█▌â–█▄ â–██ █▌ ▄███▄ â–████▄ ▄███▄ â–████▄ ▄███▄ â–â–ˆ █▌ â–â–ˆ â–â–ˆ â–â–Œ █▌â–██▌ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ â–█▄█▌ â–â–ˆ â–â–ˆ â–â–Œ █▌â–â–ˆâ–â–ˆ █▌â–â–ˆ █▌â–████▀ â–â–ˆ █▌â–█████ ▀███▄ â–█▀█▌ â–â–ˆ â–â–ˆ █▌â–â–ˆ █▌█▌â–â–ˆ █▌â–â–ˆ â–â–ˆ █▌â–â–ˆ █▌ █▌ â–â–ˆ █▌ â–â–ˆ â–â–ˆ █▌â–â–ˆ â–██▌â–â–ˆ █▌â–â–ˆ â–â–ˆ █▌█▌â–â–ˆ █▌ █▌ â–â–ˆ █▌â–█████▌â–â–ˆ █▌â–â–ˆ ██▌ ▀███▀ â–â–ˆ ▀███▀ â–â–ˆ █▌ ▀███▀ ▀▀ """, u""" TTTTTTTUUUUUUUVVVVVVVWWWWWWWWXXXXXXXYYYYYYYZZZZZZZ █████▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌ █▌ █▌â–█████▌ █▌ â–â–ˆ █▌ █▌ â–â–ˆ â–â–ˆ █▌ â–â–ˆ █▌ â–â–ˆ â–â–ˆ █▌ █▌ â–â–ˆ █▌ â–â–ˆ █▌ â–â–ˆ █▌ â–█▌ â–██ █▌ █▌ â–â–ˆ █▌ ███ â–â–ˆ â–â–Œ █▌ ███ █▌ █▌ █▌ â–â–ˆ █▌ â–█▌ â–â–ˆ â–â–Œ █▌ █▌ â–â–ˆ █▌ █▌ █▌ ▀███▀ â–ˆ ▀█▌â–█▀ â–â–ˆ █▌ █▌ â–█████▌ """, u""" aaaaaaabbbbbbbcccccccdddddddeeeeeeefffffggggggghhhhhhhiiijjjj â–â–ˆ █▌ ▄█▌ â–â–ˆ █▌ █▌ â–â–ˆ █▌ â–â–ˆ â–â–ˆ ▄███▄ â–████▄ ▄███▄ ▄████▌ ▄███▄ â–███ ▄███▄ â–████▄ â–█▌ â–█▌ ▄▄▄█▌â–â–ˆ █▌â–â–ˆ â–â–ˆ █▌â–█▄▄▄█▌ â–â–ˆ â–â–ˆ █▌â–â–ˆ █▌ █▌ █▌ â–█▀▀▀█▌â–â–ˆ █▌â–â–ˆ â–â–ˆ █▌â–█▀▀▀ â–â–ˆ â–█▄▄▄█▌â–â–ˆ █▌ █▌ █▌ ▀████▌â–████▀ ▀███▀ ▀████▌ ▀███▀ â–â–ˆ ▀▀▀█▌â–â–ˆ █▌ █▌ █▌ ▀███▀ â–██ """, u""" kkkkkkkllllmmmmmmmmnnnnnnnooooooopppppppqqqqqqqrrrrrrsssssss â–â–ˆ ██ â–â–ˆ â–â–ˆ â–â–ˆ ▄█▌ â–â–ˆ ▄█▌â–█▄ â–████▄ ▄███▄ â–████▄ ▄████▌ ▄███▌ ▄███▄ â–█▄█▀ â–â–ˆ â–â–ˆ â–â–Œ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ â–█▄▄▄ â–█▀▀█▄ â–â–ˆ â–â–ˆ â–â–Œ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ ▀▀▀█▌ â–â–ˆ █▌ â–█▌â–â–ˆ █▌â–â–ˆ █▌ ▀███▀ â–████▀ ▀████▌â–â–ˆ ▀███▀ â–â–ˆ █▌ """, u""" tttttuuuuuuuvvvvvvvwwwwwwwwxxxxxxxyyyyyyyzzzzzzz █▌ █▌ ███▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–â–ˆ █▌â–█████▌ █▌ â–â–ˆ █▌ █▌ â–â–ˆ â–â–ˆ █▌ ▀█▄█▀ â–â–ˆ █▌ ▄█▀ █▌ â–â–ˆ █▌ ███ â–â–ˆ â–â–Œ █▌ ▄█▀█▄ â–█▄▄▄█▌ ▄█▀ █▌ ▀███▀ â–█▌ ▀█▌â–█▀ â–â–ˆ █▌ ▀▀▀█▌â–█████▌ ▀███▀ """] add_font("Half Block 7x7",HalfBlock7x7Font) if __name__ == "__main__": l = get_all_fonts() all_ascii = "".join([chr(x) for x in range(32, 127)]) print "Available Fonts: (U) = UTF-8 required" print "----------------" for n,cls in l: f = cls() u = "" if f.utf8_required: u = "(U)" print ("%-20s %3s " % (n,u)), c = f.characters() if c == all_ascii: print "Full ASCII" elif c.startswith(all_ascii): print "Full ASCII + " + c[len(all_ascii):] else: print "Characters: " + c urwid-1.3.1/urwid/main_loop.py0000775000175000017500000013034112615524560015704 0ustar ianian00000000000000#!/usr/bin/python # # Urwid main loop code # Copyright (C) 2004-2012 Ian Ward # Copyright (C) 2008 Walter Mundt # Copyright (C) 2009 Andrew Psaltis # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ import time import heapq import select import os from functools import wraps from weakref import WeakKeyDictionary try: import fcntl except ImportError: pass # windows from urwid.util import StoppingContext, is_mouse_event from urwid.compat import PYTHON3 from urwid.command_map import command_map, REDRAW_SCREEN from urwid.wimp import PopUpTarget from urwid import signals from urwid.display_common import INPUT_DESCRIPTORS_CHANGED PIPE_BUFFER_READ_SIZE = 4096 # can expect this much on Linux, so try for that class ExitMainLoop(Exception): """ When this exception is raised within a main loop the main loop will exit cleanly. """ pass class CantUseExternalLoop(Exception): pass class MainLoop(object): """ This is the standard main loop implementation for a single interactive session. :param widget: the topmost widget used for painting the screen, stored as :attr:`widget` and may be modified. Must be a box widget. :type widget: widget instance :param palette: initial palette for screen :type palette: iterable of palette entries :param screen: screen to use, default is a new :class:`raw_display.Screen` instance; stored as :attr:`screen` :type screen: display module screen instance :param handle_mouse: ``True`` to ask :attr:`.screen` to process mouse events :type handle_mouse: bool :param input_filter: a function to filter input before sending it to :attr:`.widget`, called from :meth:`.input_filter` :type input_filter: callable :param unhandled_input: a function called when input is not handled by :attr:`.widget`, called from :meth:`.unhandled_input` :type unhandled_input: callable :param event_loop: if :attr:`.screen` supports external an event loop it may be given here, default is a new :class:`SelectEventLoop` instance; stored as :attr:`.event_loop` :type event_loop: event loop instance :param pop_ups: `True` to wrap :attr:`.widget` with a :class:`PopUpTarget` instance to allow any widget to open a pop-up anywhere on the screen :type pop_ups: boolean .. attribute:: screen The screen object this main loop uses for screen updates and reading input .. attribute:: event_loop The event loop object this main loop uses for waiting on alarms and IO """ def __init__(self, widget, palette=(), screen=None, handle_mouse=True, input_filter=None, unhandled_input=None, event_loop=None, pop_ups=False): self._widget = widget self.handle_mouse = handle_mouse self.pop_ups = pop_ups # triggers property setting side-effect if not screen: from urwid import raw_display screen = raw_display.Screen() if palette: screen.register_palette(palette) self.screen = screen self.screen_size = None self._unhandled_input = unhandled_input self._input_filter = input_filter if not hasattr(screen, 'hook_event_loop' ) and event_loop is not None: raise NotImplementedError("screen object passed " "%r does not support external event loops" % (screen,)) if event_loop is None: event_loop = SelectEventLoop() self.event_loop = event_loop self._watch_pipes = {} def _set_widget(self, widget): self._widget = widget if self.pop_ups: self._topmost_widget.original_widget = self._widget else: self._topmost_widget = self._widget widget = property(lambda self:self._widget, _set_widget, doc= """ Property for the topmost widget used to draw the screen. This must be a box widget. """) def _set_pop_ups(self, pop_ups): self._pop_ups = pop_ups if pop_ups: self._topmost_widget = PopUpTarget(self._widget) else: self._topmost_widget = self._widget pop_ups = property(lambda self:self._pop_ups, _set_pop_ups) def set_alarm_in(self, sec, callback, user_data=None): """ Schedule an alarm in *sec* seconds that will call *callback* from the within the :meth:`run` method. :param sec: seconds until alarm :type sec: float :param callback: function to call with two parameters: this main loop object and *user_data* :type callback: callable """ def cb(): callback(self, user_data) return self.event_loop.alarm(sec, cb) def set_alarm_at(self, tm, callback, user_data=None): """ Schedule an alarm at *tm* time that will call *callback* from the within the :meth:`run` function. Returns a handle that may be passed to :meth:`remove_alarm`. :param tm: time to call callback e.g. ``time.time() + 5`` :type tm: float :param callback: function to call with two parameters: this main loop object and *user_data* :type callback: callable """ def cb(): callback(self, user_data) return self.event_loop.alarm(tm - time.time(), cb) def remove_alarm(self, handle): """ Remove an alarm. Return ``True`` if *handle* was found, ``False`` otherwise. """ return self.event_loop.remove_alarm(handle) def watch_pipe(self, callback): """ Create a pipe for use by a subprocess or thread to trigger a callback in the process/thread running the main loop. :param callback: function taking one parameter to call from within the process/thread running the main loop :type callback: callable This method returns a file descriptor attached to the write end of a pipe. The read end of the pipe is added to the list of files :attr:`event_loop` is watching. When data is written to the pipe the callback function will be called and passed a single value containing data read from the pipe. This method may be used any time you want to update widgets from another thread or subprocess. Data may be written to the returned file descriptor with ``os.write(fd, data)``. Ensure that data is less than 512 bytes (or 4K on Linux) so that the callback will be triggered just once with the complete value of data passed in. If the callback returns ``False`` then the watch will be removed from :attr:`event_loop` and the read end of the pipe will be closed. You are responsible for closing the write end of the pipe with ``os.close(fd)``. """ pipe_rd, pipe_wr = os.pipe() fcntl.fcntl(pipe_rd, fcntl.F_SETFL, os.O_NONBLOCK) watch_handle = None def cb(): data = os.read(pipe_rd, PIPE_BUFFER_READ_SIZE) rval = callback(data) if rval is False: self.event_loop.remove_watch_file(watch_handle) os.close(pipe_rd) watch_handle = self.event_loop.watch_file(pipe_rd, cb) self._watch_pipes[pipe_wr] = (watch_handle, pipe_rd) return pipe_wr def remove_watch_pipe(self, write_fd): """ Close the read end of the pipe and remove the watch created by :meth:`watch_pipe`. You are responsible for closing the write end of the pipe. Returns ``True`` if the watch pipe exists, ``False`` otherwise """ try: watch_handle, pipe_rd = self._watch_pipes.pop(write_fd) except KeyError: return False if not self.event_loop.remove_watch_file(watch_handle): return False os.close(pipe_rd) return True def watch_file(self, fd, callback): """ Call *callback* when *fd* has some data to read. No parameters are passed to callback. Returns a handle that may be passed to :meth:`remove_watch_file`. """ return self.event_loop.watch_file(fd, callback) def remove_watch_file(self, handle): """ Remove a watch file. Returns ``True`` if the watch file exists, ``False`` otherwise. """ return self.event_loop.remove_watch_file(handle) def run(self): """ Start the main loop handling input events and updating the screen. The loop will continue until an :exc:`ExitMainLoop` exception is raised. If you would prefer to manage the event loop yourself, don't use this method. Instead, call :meth:`start` before starting the event loop, and :meth:`stop` once it's finished. """ try: self._run() except ExitMainLoop: pass def _test_run(self): """ >>> w = _refl("widget") # _refl prints out function calls >>> w.render_rval = "fake canvas" # *_rval is used for return values >>> scr = _refl("screen") >>> scr.get_input_descriptors_rval = [42] >>> scr.get_cols_rows_rval = (20, 10) >>> scr.started = True >>> scr._urwid_signals = {} >>> evl = _refl("event_loop") >>> evl.enter_idle_rval = 1 >>> evl.watch_file_rval = 2 >>> ml = MainLoop(w, [], scr, event_loop=evl) >>> ml.run() # doctest:+ELLIPSIS screen.start() screen.set_mouse_tracking() screen.unhook_event_loop(...) screen.hook_event_loop(...) event_loop.enter_idle() event_loop.run() event_loop.remove_enter_idle(1) screen.unhook_event_loop(...) screen.stop() >>> ml.draw_screen() # doctest:+ELLIPSIS screen.get_cols_rows() widget.render((20, 10), focus=True) screen.draw_screen((20, 10), 'fake canvas') """ def start(self): """ Sets up the main loop, hooking into the event loop where necessary. Starts the :attr:`screen` if it hasn't already been started. If you want to control starting and stopping the event loop yourself, you should call this method before starting, and call `stop` once the loop has finished. You may also use this method as a context manager, which will stop the loop automatically at the end of the block: with main_loop.start(): ... Note that some event loop implementations don't handle exceptions specially if you manage the event loop yourself. In particular, the Twisted and asyncio loops won't stop automatically when :exc:`ExitMainLoop` (or anything else) is raised. """ self.screen.start() if self.handle_mouse: self.screen.set_mouse_tracking() if not hasattr(self.screen, 'hook_event_loop'): raise CantUseExternalLoop( "Screen {0!r} doesn't support external event loops") try: signals.connect_signal(self.screen, INPUT_DESCRIPTORS_CHANGED, self._reset_input_descriptors) except NameError: pass # watch our input descriptors self._reset_input_descriptors() self.idle_handle = self.event_loop.enter_idle(self.entering_idle) return StoppingContext(self) def stop(self): """ Cleans up any hooks added to the event loop. Only call this if you're managing the event loop yourself, after the loop stops. """ self.event_loop.remove_enter_idle(self.idle_handle) del self.idle_handle signals.disconnect_signal(self.screen, INPUT_DESCRIPTORS_CHANGED, self._reset_input_descriptors) self.screen.unhook_event_loop(self.event_loop) self.screen.stop() def _reset_input_descriptors(self): self.screen.unhook_event_loop(self.event_loop) self.screen.hook_event_loop(self.event_loop, self._update) def _run(self): try: self.start() except CantUseExternalLoop: try: return self._run_screen_event_loop() finally: self.screen.stop() try: self.event_loop.run() except: self.screen.stop() # clean up screen control raise self.stop() def _update(self, keys, raw): """ >>> w = _refl("widget") >>> w.selectable_rval = True >>> w.mouse_event_rval = True >>> scr = _refl("screen") >>> scr.get_cols_rows_rval = (15, 5) >>> evl = _refl("event_loop") >>> ml = MainLoop(w, [], scr, event_loop=evl) >>> ml._input_timeout = "old timeout" >>> ml._update(['y'], [121]) # doctest:+ELLIPSIS screen.get_cols_rows() widget.selectable() widget.keypress((15, 5), 'y') >>> ml._update([("mouse press", 1, 5, 4)], []) widget.mouse_event((15, 5), 'mouse press', 1, 5, 4, focus=True) >>> ml._update([], []) """ keys = self.input_filter(keys, raw) if keys: self.process_input(keys) if 'window resize' in keys: self.screen_size = None def _run_screen_event_loop(self): """ This method is used when the screen does not support using external event loops. The alarms stored in the SelectEventLoop in :attr:`event_loop` are modified by this method. """ next_alarm = None while True: self.draw_screen() if not next_alarm and self.event_loop._alarms: next_alarm = heapq.heappop(self.event_loop._alarms) keys = None while not keys: if next_alarm: sec = max(0, next_alarm[0] - time.time()) self.screen.set_input_timeouts(sec) else: self.screen.set_input_timeouts(None) keys, raw = self.screen.get_input(True) if not keys and next_alarm: sec = next_alarm[0] - time.time() if sec <= 0: break keys = self.input_filter(keys, raw) if keys: self.process_input(keys) while next_alarm: sec = next_alarm[0] - time.time() if sec > 0: break tm, callback = next_alarm callback() if self.event_loop._alarms: next_alarm = heapq.heappop(self.event_loop._alarms) else: next_alarm = None if 'window resize' in keys: self.screen_size = None def _test_run_screen_event_loop(self): """ >>> w = _refl("widget") >>> scr = _refl("screen") >>> scr.get_cols_rows_rval = (10, 5) >>> scr.get_input_rval = [], [] >>> ml = MainLoop(w, screen=scr) >>> def stop_now(loop, data): ... raise ExitMainLoop() >>> handle = ml.set_alarm_in(0, stop_now) >>> try: ... ml._run_screen_event_loop() ... except ExitMainLoop: ... pass screen.get_cols_rows() widget.render((10, 5), focus=True) screen.draw_screen((10, 5), None) screen.set_input_timeouts(0) screen.get_input(True) """ def process_input(self, keys): """ This method will pass keyboard input and mouse events to :attr:`widget`. This method is called automatically from the :meth:`run` method when there is input, but may also be called to simulate input from the user. *keys* is a list of input returned from :attr:`screen`'s get_input() or get_input_nonblocking() methods. Returns ``True`` if any key was handled by a widget or the :meth:`unhandled_input` method. """ if not self.screen_size: self.screen_size = self.screen.get_cols_rows() something_handled = False for k in keys: if k == 'window resize': continue if is_mouse_event(k): event, button, col, row = k if self._topmost_widget.mouse_event(self.screen_size, event, button, col, row, focus=True ): k = None elif self._topmost_widget.selectable(): k = self._topmost_widget.keypress(self.screen_size, k) if k: if command_map[k] == REDRAW_SCREEN: self.screen.clear() something_handled = True else: something_handled |= bool(self.unhandled_input(k)) else: something_handled = True return something_handled def _test_process_input(self): """ >>> w = _refl("widget") >>> w.selectable_rval = True >>> scr = _refl("screen") >>> scr.get_cols_rows_rval = (10, 5) >>> ml = MainLoop(w, [], scr) >>> ml.process_input(['enter', ('mouse drag', 1, 14, 20)]) screen.get_cols_rows() widget.selectable() widget.keypress((10, 5), 'enter') widget.mouse_event((10, 5), 'mouse drag', 1, 14, 20, focus=True) True """ def input_filter(self, keys, raw): """ This function is passed each all the input events and raw keystroke values. These values are passed to the *input_filter* function passed to the constructor. That function must return a list of keys to be passed to the widgets to handle. If no *input_filter* was defined this implementation will return all the input events. """ if self._input_filter: return self._input_filter(keys, raw) return keys def unhandled_input(self, input): """ This function is called with any input that was not handled by the widgets, and calls the *unhandled_input* function passed to the constructor. If no *unhandled_input* was defined then the input will be ignored. *input* is the keyboard or mouse input. The *unhandled_input* function should return ``True`` if it handled the input. """ if self._unhandled_input: return self._unhandled_input(input) def entering_idle(self): """ This method is called whenever the event loop is about to enter the idle state. :meth:`draw_screen` is called here to update the screen when anything has changed. """ if self.screen.started: self.draw_screen() def draw_screen(self): """ Render the widgets and paint the screen. This method is called automatically from :meth:`entering_idle`. If you modify the widgets displayed outside of handling input or responding to an alarm you will need to call this method yourself to repaint the screen. """ if not self.screen_size: self.screen_size = self.screen.get_cols_rows() canvas = self._topmost_widget.render(self.screen_size, focus=True) self.screen.draw_screen(self.screen_size, canvas) class SelectEventLoop(object): """ Event loop based on :func:`select.select` """ def __init__(self): self._alarms = [] self._watch_files = {} self._idle_handle = 0 self._idle_callbacks = {} def alarm(self, seconds, callback): """ Call callback() a given time from now. No parameters are passed to callback. Returns a handle that may be passed to remove_alarm() seconds -- floating point time to wait before calling callback callback -- function to call from event loop """ tm = time.time() + seconds heapq.heappush(self._alarms, (tm, callback)) return (tm, callback) def remove_alarm(self, handle): """ Remove an alarm. Returns True if the alarm exists, False otherwise """ try: self._alarms.remove(handle) heapq.heapify(self._alarms) return True except ValueError: return False def watch_file(self, fd, callback): """ Call callback() when fd has some data to read. No parameters are passed to callback. Returns a handle that may be passed to remove_watch_file() fd -- file descriptor to watch for input callback -- function to call when input is available """ self._watch_files[fd] = callback return fd def remove_watch_file(self, handle): """ Remove an input file. Returns True if the input file exists, False otherwise """ if handle in self._watch_files: del self._watch_files[handle] return True return False def enter_idle(self, callback): """ Add a callback for entering idle. Returns a handle that may be passed to remove_idle() """ self._idle_handle += 1 self._idle_callbacks[self._idle_handle] = callback return self._idle_handle def remove_enter_idle(self, handle): """ Remove an idle callback. Returns True if the handle was removed. """ try: del self._idle_callbacks[handle] except KeyError: return False return True def _entering_idle(self): """ Call all the registered idle callbacks. """ for callback in self._idle_callbacks.values(): callback() def run(self): """ Start the event loop. Exit the loop when any callback raises an exception. If ExitMainLoop is raised, exit cleanly. """ try: self._did_something = True while True: try: self._loop() except select.error as e: if e.args[0] != 4: # not just something we need to retry raise except ExitMainLoop: pass def _loop(self): """ A single iteration of the event loop """ fds = self._watch_files.keys() if self._alarms or self._did_something: if self._alarms: tm = self._alarms[0][0] timeout = max(0, tm - time.time()) if self._did_something and (not self._alarms or (self._alarms and timeout > 0)): timeout = 0 tm = 'idle' ready, w, err = select.select(fds, [], fds, timeout) else: tm = None ready, w, err = select.select(fds, [], fds) if not ready: if tm == 'idle': self._entering_idle() self._did_something = False elif tm is not None: # must have been a timeout tm, alarm_callback = self._alarms.pop(0) alarm_callback() self._did_something = True for fd in ready: self._watch_files[fd]() self._did_something = True class GLibEventLoop(object): """ Event loop based on GLib.MainLoop """ def __init__(self): from gi.repository import GLib self.GLib = GLib self._alarms = [] self._watch_files = {} self._idle_handle = 0 self._glib_idle_enabled = False # have we called glib.idle_add? self._idle_callbacks = {} self._loop = GLib.MainLoop() self._exc_info = None self._enable_glib_idle() def alarm(self, seconds, callback): """ Call callback() a given time from now. No parameters are passed to callback. Returns a handle that may be passed to remove_alarm() seconds -- floating point time to wait before calling callback callback -- function to call from event loop """ @self.handle_exit def ret_false(): callback() self._enable_glib_idle() return False fd = self.GLib.timeout_add(int(seconds*1000), ret_false) self._alarms.append(fd) return (fd, callback) def remove_alarm(self, handle): """ Remove an alarm. Returns True if the alarm exists, False otherwise """ try: self._alarms.remove(handle[0]) self.GLib.source_remove(handle[0]) return True except ValueError: return False def watch_file(self, fd, callback): """ Call callback() when fd has some data to read. No parameters are passed to callback. Returns a handle that may be passed to remove_watch_file() fd -- file descriptor to watch for input callback -- function to call when input is available """ @self.handle_exit def io_callback(source, cb_condition): callback() self._enable_glib_idle() return True self._watch_files[fd] = \ self.GLib.io_add_watch(fd,self.GLib.IO_IN,io_callback) return fd def remove_watch_file(self, handle): """ Remove an input file. Returns True if the input file exists, False otherwise """ if handle in self._watch_files: self.GLib.source_remove(self._watch_files[handle]) del self._watch_files[handle] return True return False def enter_idle(self, callback): """ Add a callback for entering idle. Returns a handle that may be passed to remove_enter_idle() """ self._idle_handle += 1 self._idle_callbacks[self._idle_handle] = callback return self._idle_handle def _enable_glib_idle(self): if self._glib_idle_enabled: return self.GLib.idle_add(self._glib_idle_callback) self._glib_idle_enabled = True def _glib_idle_callback(self): for callback in self._idle_callbacks.values(): callback() self._glib_idle_enabled = False return False # ask glib not to call again (or we would be called def remove_enter_idle(self, handle): """ Remove an idle callback. Returns True if the handle was removed. """ try: del self._idle_callbacks[handle] except KeyError: return False return True def run(self): """ Start the event loop. Exit the loop when any callback raises an exception. If ExitMainLoop is raised, exit cleanly. """ try: self._loop.run() finally: if self._loop.is_running(): self._loop.quit() if self._exc_info: # An exception caused us to exit, raise it now exc_info = self._exc_info self._exc_info = None raise exc_info[0], exc_info[1], exc_info[2] def handle_exit(self,f): """ Decorator that cleanly exits the :class:`GLibEventLoop` if :exc:`ExitMainLoop` is thrown inside of the wrapped function. Store the exception info if some other exception occurs, it will be reraised after the loop quits. *f* -- function to be wrapped """ def wrapper(*args,**kargs): try: return f(*args,**kargs) except ExitMainLoop: self._loop.quit() except: import sys self._exc_info = sys.exc_info() if self._loop.is_running(): self._loop.quit() return False return wrapper class TornadoEventLoop(object): """ This is an Urwid-specific event loop to plug into its MainLoop. It acts as an adaptor for Tornado's IOLoop which does all heavy lifting except idle-callbacks. Notice, since Tornado has no concept of idle callbacks we monkey patch ioloop._impl.poll() function to be able to detect potential idle periods. """ _ioloop_registry = WeakKeyDictionary() # { : { : }} _max_idle_handle = 0 class PollProxy(object): """ A simple proxy for a Python's poll object that wraps the .poll() method in order to detect idle periods and call Urwid callbacks """ def __init__(self, poll_obj, idle_map): self.__poll_obj = poll_obj self.__idle_map = idle_map self._idle_done = False self._prev_timeout = 0 def __getattr__(self, name): return getattr(self.__poll_obj, name) def poll(self, timeout): if timeout > self._prev_timeout: # if timeout increased we assume a timer event was handled self._idle_done = False self._prev_timeout = timeout start = time.time() # any IO pending wins events = self.__poll_obj.poll(0) if events: self._idle_done = False return events # our chance to enter idle if not self._idle_done: for callback in self.__idle_map.values(): callback() self._idle_done = True # then complete the actual request (adjusting timeout) timeout = max(0, min(timeout, timeout + start - time.time())) events = self.__poll_obj.poll(timeout) if events: self._idle_done = False return events @classmethod def _patch_poll_impl(cls, ioloop): """ Wraps original poll object in the IOLoop's poll object """ if ioloop in cls._ioloop_registry: return # we already patched this instance cls._ioloop_registry[ioloop] = idle_map = {} ioloop._impl = cls.PollProxy(ioloop._impl, idle_map) def __init__(self, ioloop=None): if not ioloop: from tornado.ioloop import IOLoop ioloop = IOLoop.instance() self._ioloop = ioloop self._patch_poll_impl(ioloop) self._pending_alarms = {} self._watch_handles = {} # { : } self._max_watch_handle = 0 self._exception = None def alarm(self, secs, callback): ioloop = self._ioloop def wrapped(): try: del self._pending_alarms[handle] except KeyError: pass self.handle_exit(callback)() handle = ioloop.add_timeout(ioloop.time() + secs, wrapped) self._pending_alarms[handle] = 1 return handle def remove_alarm(self, handle): self._ioloop.remove_timeout(handle) try: del self._pending_alarms[handle] except KeyError: return False else: return True def watch_file(self, fd, callback): from tornado.ioloop import IOLoop handler = lambda fd,events: self.handle_exit(callback)() self._ioloop.add_handler(fd, handler, IOLoop.READ) self._max_watch_handle += 1 handle = self._max_watch_handle self._watch_handles[handle] = fd return handle def remove_watch_file(self, handle): fd = self._watch_handles.pop(handle, None) if fd is None: return False else: self._ioloop.remove_handler(fd) return True def enter_idle(self, callback): self._max_idle_handle += 1 handle = self._max_idle_handle idle_map = self._ioloop_registry[self._ioloop] idle_map[handle] = callback return handle def remove_enter_idle(self, handle): idle_map = self._ioloop_registry[self._ioloop] cb = idle_map.pop(handle, None) return cb is not None def handle_exit(self, func): @wraps(func) def wrapper(*args, **kw): try: return func(*args, **kw) except ExitMainLoop: self._ioloop.stop() except Exception as exc: self._exception = exc self._ioloop.stop() return False return wrapper def run(self): self._ioloop.start() if self._exception: exc, self._exception = self._exception, None raise exc try: from twisted.internet.abstract import FileDescriptor except ImportError: FileDescriptor = object class TwistedInputDescriptor(FileDescriptor): def __init__(self, reactor, fd, cb): self._fileno = fd self.cb = cb FileDescriptor.__init__(self, reactor) def fileno(self): return self._fileno def doRead(self): return self.cb() class TwistedEventLoop(object): """ Event loop based on Twisted_ """ _idle_emulation_delay = 1.0/256 # a short time (in seconds) def __init__(self, reactor=None, manage_reactor=True): """ :param reactor: reactor to use :type reactor: :class:`twisted.internet.reactor`. :param: manage_reactor: `True` if you want this event loop to run and stop the reactor. :type manage_reactor: boolean .. WARNING:: Twisted's reactor doesn't like to be stopped and run again. If you need to stop and run your :class:`MainLoop`, consider setting ``manage_reactor=False`` and take care of running/stopping the reactor at the beginning/ending of your program yourself. You can also forego using :class:`MainLoop`'s run() entirely, and instead call start() and stop() before and after starting the reactor. .. _Twisted: http://twistedmatrix.com/trac/ """ if reactor is None: import twisted.internet.reactor reactor = twisted.internet.reactor self.reactor = reactor self._alarms = [] self._watch_files = {} self._idle_handle = 0 self._twisted_idle_enabled = False self._idle_callbacks = {} self._exc_info = None self.manage_reactor = manage_reactor self._enable_twisted_idle() def alarm(self, seconds, callback): """ Call callback() a given time from now. No parameters are passed to callback. Returns a handle that may be passed to remove_alarm() seconds -- floating point time to wait before calling callback callback -- function to call from event loop """ handle = self.reactor.callLater(seconds, self.handle_exit(callback)) return handle def remove_alarm(self, handle): """ Remove an alarm. Returns True if the alarm exists, False otherwise """ from twisted.internet.error import AlreadyCancelled, AlreadyCalled try: handle.cancel() return True except AlreadyCancelled: return False except AlreadyCalled: return False def watch_file(self, fd, callback): """ Call callback() when fd has some data to read. No parameters are passed to callback. Returns a handle that may be passed to remove_watch_file() fd -- file descriptor to watch for input callback -- function to call when input is available """ ind = TwistedInputDescriptor(self.reactor, fd, self.handle_exit(callback)) self._watch_files[fd] = ind self.reactor.addReader(ind) return fd def remove_watch_file(self, handle): """ Remove an input file. Returns True if the input file exists, False otherwise """ if handle in self._watch_files: self.reactor.removeReader(self._watch_files[handle]) del self._watch_files[handle] return True return False def enter_idle(self, callback): """ Add a callback for entering idle. Returns a handle that may be passed to remove_enter_idle() """ self._idle_handle += 1 self._idle_callbacks[self._idle_handle] = callback return self._idle_handle def _enable_twisted_idle(self): """ Twisted's reactors don't have an idle or enter-idle callback so the best we can do for now is to set a timer event in a very short time to approximate an enter-idle callback. .. WARNING:: This will perform worse than the other event loops until we can find a fix or workaround """ if self._twisted_idle_enabled: return self.reactor.callLater(self._idle_emulation_delay, self.handle_exit(self._twisted_idle_callback, enable_idle=False)) self._twisted_idle_enabled = True def _twisted_idle_callback(self): for callback in self._idle_callbacks.values(): callback() self._twisted_idle_enabled = False def remove_enter_idle(self, handle): """ Remove an idle callback. Returns True if the handle was removed. """ try: del self._idle_callbacks[handle] except KeyError: return False return True def run(self): """ Start the event loop. Exit the loop when any callback raises an exception. If ExitMainLoop is raised, exit cleanly. """ if not self.manage_reactor: return self.reactor.run() if self._exc_info: # An exception caused us to exit, raise it now exc_info = self._exc_info self._exc_info = None raise exc_info[0], exc_info[1], exc_info[2] def handle_exit(self, f, enable_idle=True): """ Decorator that cleanly exits the :class:`TwistedEventLoop` if :class:`ExitMainLoop` is thrown inside of the wrapped function. Store the exception info if some other exception occurs, it will be reraised after the loop quits. *f* -- function to be wrapped """ def wrapper(*args,**kargs): rval = None try: rval = f(*args,**kargs) except ExitMainLoop: if self.manage_reactor: self.reactor.stop() except: import sys print sys.exc_info() self._exc_info = sys.exc_info() if self.manage_reactor: self.reactor.crash() if enable_idle: self._enable_twisted_idle() return rval return wrapper class AsyncioEventLoop(object): """ Event loop based on the standard library ``asyncio`` module. ``asyncio`` is new in Python 3.4, but also exists as a backport on PyPI for Python 3.3. The ``trollius`` package is available for older Pythons with slightly different syntax, but also works with this loop. """ _we_started_event_loop = False _idle_emulation_delay = 1.0/256 # a short time (in seconds) def __init__(self, **kwargs): if 'loop' in kwargs: self._loop = kwargs.pop('loop') else: import asyncio self._loop = asyncio.get_event_loop() def alarm(self, seconds, callback): """ Call callback() a given time from now. No parameters are passed to callback. Returns a handle that may be passed to remove_alarm() seconds -- time in seconds to wait before calling callback callback -- function to call from event loop """ return self._loop.call_later(seconds, callback) def remove_alarm(self, handle): """ Remove an alarm. Returns True if the alarm exists, False otherwise """ existed = not handle._cancelled handle.cancel() return existed def watch_file(self, fd, callback): """ Call callback() when fd has some data to read. No parameters are passed to callback. Returns a handle that may be passed to remove_watch_file() fd -- file descriptor to watch for input callback -- function to call when input is available """ self._loop.add_reader(fd, callback) return fd def remove_watch_file(self, handle): """ Remove an input file. Returns True if the input file exists, False otherwise """ return self._loop.remove_reader(handle) def enter_idle(self, callback): """ Add a callback for entering idle. Returns a handle that may be passed to remove_idle() """ # XXX there's no such thing as "idle" in most event loops; this fakes # it the same way as Twisted, by scheduling the callback to be called # repeatedly mutable_handle = [None] def faux_idle_callback(): callback() mutable_handle[0] = self._loop.call_later( self._idle_emulation_delay, faux_idle_callback) mutable_handle[0] = self._loop.call_later( self._idle_emulation_delay, faux_idle_callback) return mutable_handle def remove_enter_idle(self, handle): """ Remove an idle callback. Returns True if the handle was removed. """ # `handle` is just a list containing the current actual handle return self.remove_alarm(handle[0]) _exc_info = None def _exception_handler(self, loop, context): exc = context.get('exception') if exc: loop.stop() if not isinstance(exc, ExitMainLoop): # Store the exc_info so we can re-raise after the loop stops import sys self._exc_info = sys.exc_info() else: loop.default_exception_handler(context) def run(self): """ Start the event loop. Exit the loop when any callback raises an exception. If ExitMainLoop is raised, exit cleanly. """ self._loop.set_exception_handler(self._exception_handler) self._loop.run_forever() if self._exc_info: raise self._exc_info[0], self._exc_info[1], self._exc_info[2] self._exc_info = None def _refl(name, rval=None, exit=False): """ This function is used to test the main loop classes. >>> scr = _refl("screen") >>> scr.function("argument") screen.function('argument') >>> scr.callme(when="now") screen.callme(when='now') >>> scr.want_something_rval = 42 >>> x = scr.want_something() screen.want_something() >>> x 42 """ class Reflect(object): def __init__(self, name, rval=None): self._name = name self._rval = rval def __call__(self, *argl, **argd): args = ", ".join([repr(a) for a in argl]) if args and argd: args = args + ", " args = args + ", ".join([k+"="+repr(v) for k,v in argd.items()]) print self._name+"("+args+")" if exit: raise ExitMainLoop() return self._rval def __getattr__(self, attr): if attr.endswith("_rval"): raise AttributeError() #print self._name+"."+attr if hasattr(self, attr+"_rval"): return Reflect(self._name+"."+attr, getattr(self, attr+"_rval")) return Reflect(self._name+"."+attr) return Reflect(name) def _test(): import doctest doctest.testmod() if __name__=='__main__': _test() urwid-1.3.1/urwid/text_layout.py0000664000175000017500000004157412615524560016316 0ustar ianian00000000000000#!/usr/bin/python # # Urwid Text Layout classes # Copyright (C) 2004-2011 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ from urwid.util import calc_width, calc_text_pos, calc_trim_text, is_wide_char, \ move_prev_char, move_next_char from urwid.compat import bytes, PYTHON3, B class TextLayout: def supports_align_mode(self, align): """Return True if align is a supported align mode.""" return True def supports_wrap_mode(self, wrap): """Return True if wrap is a supported wrap mode.""" return True def layout(self, text, width, align, wrap ): """ Return a layout structure for text. :param text: string in current encoding or unicode string :param width: number of screen columns available :param align: align mode for text :param wrap: wrap mode for text Layout structure is a list of line layouts, one per output line. Line layouts are lists than may contain the following tuples: * (column width of text segment, start offset, end offset) * (number of space characters to insert, offset or None) * (column width of insert text, offset, "insert text") The offset in the last two tuples is used to determine the attribute used for the inserted spaces or text respectively. The attribute used will be the same as the attribute at that text offset. If the offset is None when inserting spaces then no attribute will be used. """ raise NotImplementedError("This function must be overridden by a real" " text layout class. (see StandardTextLayout)") class CanNotDisplayText(Exception): pass class StandardTextLayout(TextLayout): def __init__(self):#, tab_stops=(), tab_stop_every=8): pass #""" #tab_stops -- list of screen column indexes for tab stops #tab_stop_every -- repeated interval for following tab stops #""" #assert tab_stop_every is None or type(tab_stop_every)==int #if not tab_stops and tab_stop_every: # self.tab_stops = (tab_stop_every,) #self.tab_stops = tab_stops #self.tab_stop_every = tab_stop_every def supports_align_mode(self, align): """Return True if align is 'left', 'center' or 'right'.""" return align in ('left', 'center', 'right') def supports_wrap_mode(self, wrap): """Return True if wrap is 'any', 'space' or 'clip'.""" return wrap in ('any', 'space', 'clip') def layout(self, text, width, align, wrap ): """Return a layout structure for text.""" try: segs = self.calculate_text_segments( text, width, wrap ) return self.align_layout( text, width, segs, wrap, align ) except CanNotDisplayText: return [[]] def pack(self, maxcol, layout): """ Return a minimal maxcol value that would result in the same number of lines for layout. layout must be a layout structure returned by self.layout(). """ maxwidth = 0 assert layout, "huh? empty layout?: "+repr(layout) for l in layout: lw = line_width(l) if lw >= maxcol: return maxcol maxwidth = max(maxwidth, lw) return maxwidth def align_layout( self, text, width, segs, wrap, align ): """Convert the layout segs to an aligned layout.""" out = [] for l in segs: sc = line_width(l) if sc == width or align=='left': out.append(l) continue if align == 'right': out.append([(width-sc, None)] + l) continue assert align == 'center' out.append([((width-sc+1) // 2, None)] + l) return out def calculate_text_segments(self, text, width, wrap): """ Calculate the segments of text to display given width screen columns to display them. text - unicode text or byte string to display width - number of available screen columns wrap - wrapping mode used Returns a layout structure without alignment applied. """ nl, nl_o, sp_o = "\n", "\n", " " if PYTHON3 and isinstance(text, bytes): nl = B(nl) # can only find bytes in python3 bytestrings nl_o = ord(nl_o) # + an item of a bytestring is the ordinal value sp_o = ord(sp_o) b = [] p = 0 if wrap == 'clip': # no wrapping to calculate, so it's easy. while p<=len(text): n_cr = text.find(nl, p) if n_cr == -1: n_cr = len(text) sc = calc_width(text, p, n_cr) l = [(0,n_cr)] if p!=n_cr: l = [(sc, p, n_cr)] + l b.append(l) p = n_cr+1 return b while p<=len(text): # look for next eligible line break n_cr = text.find(nl, p) if n_cr == -1: n_cr = len(text) sc = calc_width(text, p, n_cr) if sc == 0: # removed character hint b.append([(0,n_cr)]) p = n_cr+1 continue if sc <= width: # this segment fits b.append([(sc,p,n_cr), # removed character hint (0,n_cr)]) p = n_cr+1 continue pos, sc = calc_text_pos( text, p, n_cr, width ) if pos == p: # pathological width=1 double-byte case raise CanNotDisplayText( "Wide character will not fit in 1-column width") if wrap == 'any': b.append([(sc,p,pos)]) p = pos continue assert wrap == 'space' if text[pos] == sp_o: # perfect space wrap b.append([(sc,p,pos), # removed character hint (0,pos)]) p = pos+1 continue if is_wide_char(text, pos): # perfect next wide b.append([(sc,p,pos)]) p = pos continue prev = pos while prev > p: prev = move_prev_char(text, p, prev) if text[prev] == sp_o: sc = calc_width(text,p,prev) l = [(0,prev)] if p!=prev: l = [(sc,p,prev)] + l b.append(l) p = prev+1 break if is_wide_char(text,prev): # wrap after wide char next = move_next_char(text, prev, pos) sc = calc_width(text,p,next) b.append([(sc,p,next)]) p = next break else: # unwrap previous line space if possible to # fit more text (we're breaking a word anyway) if b and (len(b[-1]) == 2 or ( len(b[-1])==1 and len(b[-1][0])==2 )): # look for removed space above if len(b[-1]) == 1: [(h_sc, h_off)] = b[-1] p_sc = 0 p_off = p_end = h_off else: [(p_sc, p_off, p_end), (h_sc, h_off)] = b[-1] if (p_sc < width and h_sc==0 and text[h_off] == sp_o): # combine with previous line del b[-1] p = p_off pos, sc = calc_text_pos( text, p, n_cr, width ) b.append([(sc,p,pos)]) # check for trailing " " or "\n" p = pos if p < len(text) and ( text[p] in (sp_o, nl_o)): # removed character hint b[-1].append((0,p)) p += 1 continue # force any char wrap b.append([(sc,p,pos)]) p = pos return b ###################################### # default layout object to use default_layout = StandardTextLayout() ###################################### class LayoutSegment: def __init__(self, seg): """Create object from line layout segment structure""" assert type(seg) == tuple, repr(seg) assert len(seg) in (2,3), repr(seg) self.sc, self.offs = seg[:2] assert type(self.sc) == int, repr(self.sc) if len(seg)==3: assert type(self.offs) == int, repr(self.offs) assert self.sc > 0, repr(seg) t = seg[2] if type(t) == bytes: self.text = t self.end = None else: assert type(t) == int, repr(t) self.text = None self.end = t else: assert len(seg) == 2, repr(seg) if self.offs is not None: assert self.sc >= 0, repr(seg) assert type(self.offs)==int self.text = self.end = None def subseg(self, text, start, end): """ Return a "sub-segment" list containing segment structures that make up a portion of this segment. A list is returned to handle cases where wide characters need to be replaced with a space character at either edge so two or three segments will be returned. """ if start < 0: start = 0 if end > self.sc: end = self.sc if start >= end: return [] # completely gone if self.text: # use text stored in segment (self.text) spos, epos, pad_left, pad_right = calc_trim_text( self.text, 0, len(self.text), start, end ) return [ (end-start, self.offs, bytes().ljust(pad_left) + self.text[spos:epos] + bytes().ljust(pad_right)) ] elif self.end: # use text passed as parameter (text) spos, epos, pad_left, pad_right = calc_trim_text( text, self.offs, self.end, start, end ) l = [] if pad_left: l.append((1,spos-1)) l.append((end-start-pad_left-pad_right, spos, epos)) if pad_right: l.append((1,epos)) return l else: # simple padding adjustment return [(end-start,self.offs)] def line_width( segs ): """ Return the screen column width of one line of a text layout structure. This function ignores any existing shift applied to the line, represented by an (amount, None) tuple at the start of the line. """ sc = 0 seglist = segs if segs and len(segs[0])==2 and segs[0][1]==None: seglist = segs[1:] for s in seglist: sc += s[0] return sc def shift_line( segs, amount ): """ Return a shifted line from a layout structure to the left or right. segs -- line of a layout structure amount -- screen columns to shift right (+ve) or left (-ve) """ assert type(amount)==int, repr(amount) if segs and len(segs[0])==2 and segs[0][1]==None: # existing shift amount += segs[0][0] if amount: return [(amount,None)]+segs[1:] return segs[1:] if amount: return [(amount,None)]+segs return segs def trim_line( segs, text, start, end ): """ Return a trimmed line of a text layout structure. text -- text to which this layout structure applies start -- starting screen column end -- ending screen column """ l = [] x = 0 for seg in segs: sc = seg[0] if start or sc < 0: if start >= sc: start -= sc x += sc continue s = LayoutSegment(seg) if x+sc >= end: # can all be done at once return s.subseg( text, start, end-x ) l += s.subseg( text, start, sc ) start = 0 x += sc continue if x >= end: break if x+sc > end: s = LayoutSegment(seg) l += s.subseg( text, 0, end-x ) break l.append( seg ) return l def calc_line_pos( text, line_layout, pref_col ): """ Calculate the closest linear position to pref_col given a line layout structure. Returns None if no position found. """ closest_sc = None closest_pos = None current_sc = 0 if pref_col == 'left': for seg in line_layout: s = LayoutSegment(seg) if s.offs is not None: return s.offs return elif pref_col == 'right': for seg in line_layout: s = LayoutSegment(seg) if s.offs is not None: closest_pos = s s = closest_pos if s is None: return if s.end is None: return s.offs return calc_text_pos( text, s.offs, s.end, s.sc-1)[0] for seg in line_layout: s = LayoutSegment(seg) if s.offs is not None: if s.end is not None: if (current_sc <= pref_col and pref_col < current_sc + s.sc): # exact match within this segment return calc_text_pos( text, s.offs, s.end, pref_col - current_sc )[0] elif current_sc <= pref_col: closest_sc = current_sc + s.sc - 1 closest_pos = s if closest_sc is None or ( abs(pref_col-current_sc) < abs(pref_col-closest_sc) ): # this screen column is closer closest_sc = current_sc closest_pos = s.offs if current_sc > closest_sc: # we're moving past break current_sc += s.sc if closest_pos is None or type(closest_pos) == int: return closest_pos # return the last positions in the segment "closest_pos" s = closest_pos return calc_text_pos( text, s.offs, s.end, s.sc-1)[0] def calc_pos( text, layout, pref_col, row ): """ Calculate the closest linear position to pref_col and row given a layout structure. """ if row < 0 or row >= len(layout): raise Exception("calculate_pos: out of layout row range") pos = calc_line_pos( text, layout[row], pref_col ) if pos is not None: return pos rows_above = range(row-1,-1,-1) rows_below = range(row+1,len(layout)) while rows_above and rows_below: if rows_above: r = rows_above.pop(0) pos = calc_line_pos(text, layout[r], pref_col) if pos is not None: return pos if rows_below: r = rows_below.pop(0) pos = calc_line_pos(text, layout[r], pref_col) if pos is not None: return pos return 0 def calc_coords( text, layout, pos, clamp=1 ): """ Calculate the coordinates closest to position pos in text with layout. text -- raw string or unicode string layout -- layout structure applied to text pos -- integer position into text clamp -- ignored right now """ closest = None y = 0 for line_layout in layout: x = 0 for seg in line_layout: s = LayoutSegment(seg) if s.offs is None: x += s.sc continue if s.offs == pos: return x,y if s.end is not None and s.offs<=pos and s.end>pos: x += calc_width( text, s.offs, pos ) return x,y distance = abs(s.offs - pos) if s.end is not None and s.end>> import urwid >>> debug = urwid.Text('') >>> def handler(widget, newtext): ... debug.set_text("Edit widget changed to %s" % newtext) >>> edit = urwid.Edit('') >>> key = urwid.connect_signal(edit, 'change', handler) If you now build some interface using "edit" and "debug", the "debug" widget will show whatever you type in the "edit" widget. However, if you remove all references to the "debug" widget, it will still be kept alive by the signal handler. This because the signal handler is a closure that (implicitly) references the "edit" widget. If you want to allow the "debug" widget to be garbage collected, you can create a "fake" or "weak" closure (it's not really a closure, since it doesn't reference any outside variables, so it's just a dynamic function): >>> debug = urwid.Text('') >>> def handler(weak_debug, widget, newtext): ... weak_debug.set_text("Edit widget changed to %s" % newtext) >>> edit = urwid.Edit('') >>> key = urwid.connect_signal(edit, 'change', handler, weak_args=[debug]) Here the weak_debug parameter in print_debug is the value passed in the weak_args list to connect_signal. Note that the weak_debug value passed is not a weak reference anymore, the signals code transparently dereferences the weakref parameter before passing it to print_debug. Returns a key associated by this signal handler, which can be used to disconnect the signal later on using urwid.disconnect_signal_by_key. Alternatively, the signal handler can also be disconnected by calling urwid.disconnect_signal, which doesn't need this key. """ sig_cls = obj.__class__ if not name in self._supported.get(sig_cls, []): raise NameError("No such signal %r for object %r" % (name, obj)) # Just generate an arbitrary (but unique) key key = Key() signals = setdefaultattr(obj, self._signal_attr, {}) handlers = signals.setdefault(name, []) # Remove the signal handler when any of the weakref'd arguments # are garbage collected. Note that this means that the handlers # dictionary can be modified _at any time_, so it should never # be iterated directly (e.g. iterate only over .keys() and # .items(), never over .iterkeys(), .iteritems() or the object # itself). # We let the callback keep a weakref to the object as well, to # prevent a circular reference between the handler and the # object (via the weakrefs, which keep strong references to # their callbacks) from existing. obj_weak = weakref.ref(obj) def weakref_callback(weakref): o = obj_weak() if o: try: del getattr(o, self._signal_attr, {})[name][key] except KeyError: pass user_args = self._prepare_user_args(weak_args, user_args, weakref_callback) handlers.append((key, callback, user_arg, user_args)) return key def _prepare_user_args(self, weak_args, user_args, callback = None): # Turn weak_args into weakrefs and prepend them to user_args return [weakref.ref(a, callback) for a in (weak_args or [])] + (user_args or []) def disconnect(self, obj, name, callback, user_arg=None, weak_args=None, user_args=None): """ :param obj: the object to disconnect the signal from :type obj: object :param name: the signal to disconnect, typically a string :type name: signal name :param callback: the callback function passed to connect_signal :type callback: function :param user_arg: the user_arg parameter passed to connect_signal :param weak_args: the weak_args parameter passed to connect_signal :param user_args: the weak_args parameter passed to connect_signal This function will remove a callback from the list connected to a signal with connect_signal(). The arguments passed should be exactly the same as those passed to connect_signal(). If the callback is not connected or already disconnected, this function will simply do nothing. """ signals = setdefaultattr(obj, self._signal_attr, {}) if name not in signals: return handlers = signals[name] # Do the same processing as in connect, so we can compare the # resulting tuple. user_args = self._prepare_user_args(weak_args, user_args) # Remove the given handler for h in handlers: if h[1:] == (callback, user_arg, user_args): return self.disconnect_by_key(obj, name, h[0]) def disconnect_by_key(self, obj, name, key): """ :param obj: the object to disconnect the signal from :type obj: object :param name: the signal to disconnect, typically a string :type name: signal name :param key: the key for this signal handler, as returned by connect_signal(). :type key: Key This function will remove a callback from the list connected to a signal with connect_signal(). The key passed should be the value returned by connect_signal(). If the callback is not connected or already disconnected, this function will simply do nothing. """ signals = setdefaultattr(obj, self._signal_attr, {}) handlers = signals.get(name, []) handlers[:] = [h for h in handlers if h[0] is not key] def emit(self, obj, name, *args): """ :param obj: the object sending a signal :type obj: object :param name: the signal to send, typically a string :type name: signal name :param \*args: zero or more positional arguments to pass to the signal callback functions This function calls each of the callbacks connected to this signal with the args arguments as positional parameters. This function returns True if any of the callbacks returned True. """ result = False signals = getattr(obj, self._signal_attr, {}) handlers = signals.get(name, []) for key, callback, user_arg, user_args in handlers: result |= self._call_callback(callback, user_arg, user_args, args) return result def _call_callback(self, callback, user_arg, user_args, emit_args): if user_args: args_to_pass = [] for arg in user_args: if isinstance(arg, weakref.ReferenceType): arg = arg() if arg is None: # If the weakref is None, the referenced object # was cleaned up. We just skip the entire # callback in this case. The weakref cleanup # handler will have removed the callback when # this happens, so no need to actually remove # the callback here. return False args_to_pass.append(arg) args_to_pass.extend(emit_args) else: # Optimization: Don't create a new list when there are # no user_args args_to_pass = emit_args # The deprecated user_arg argument was added to the end # instead of the beginning. if user_arg is not None: args_to_pass = itertools.chain(args_to_pass, (user_arg,)) return bool(callback(*args_to_pass)) _signals = Signals() emit_signal = _signals.emit register_signal = _signals.register connect_signal = _signals.connect disconnect_signal = _signals.disconnect disconnect_signal_by_key = _signals.disconnect_by_key urwid-1.3.1/urwid/tests/0000775000175000017500000000000012615524621014510 5ustar ianian00000000000000urwid-1.3.1/urwid/tests/test_decoration.py0000664000175000017500000001531512615524560020257 0ustar ianian00000000000000import unittest import urwid class PaddingTest(unittest.TestCase): def ptest(self, desc, align, width, maxcol, left, right,min_width=None): p = urwid.Padding(None, align, width, min_width) l, r = p.padding_values((maxcol,),False) assert (l,r)==(left,right), "%s expected %s but got %s"%( desc, (left,right), (l,r)) def petest(self, desc, align, width): self.assertRaises(urwid.PaddingError, lambda: urwid.Padding(None, align, width)) def test_create(self): self.petest("invalid pad",6,5) self.petest("invalid pad type",('bad',2),5) self.petest("invalid width",'center','42') self.petest("invalid width type",'center',('gouranga',4)) def test_values(self): self.ptest("left align 5 7",'left',5,7,0,2) self.ptest("left align 7 7",'left',7,7,0,0) self.ptest("left align 9 7",'left',9,7,0,0) self.ptest("right align 5 7",'right',5,7,2,0) self.ptest("center align 5 7",'center',5,7,1,1) self.ptest("fixed left",('fixed left',3),5,10,3,2) self.ptest("fixed left reduce",('fixed left',3),8,10,2,0) self.ptest("fixed left shrink",('fixed left',3),18,10,0,0) self.ptest("fixed left, right", ('fixed left',3),('fixed right',4),17,3,4) self.ptest("fixed left, right, min_width", ('fixed left',3),('fixed right',4),10,3,2,5) self.ptest("fixed left, right, min_width 2", ('fixed left',3),('fixed right',4),10,2,0,8) self.ptest("fixed right",('fixed right',3),5,10,2,3) self.ptest("fixed right reduce",('fixed right',3),8,10,0,2) self.ptest("fixed right shrink",('fixed right',3),18,10,0,0) self.ptest("fixed right, left", ('fixed right',3),('fixed left',4),17,4,3) self.ptest("fixed right, left, min_width", ('fixed right',3),('fixed left',4),10,2,3,5) self.ptest("fixed right, left, min_width 2", ('fixed right',3),('fixed left',4),10,0,2,8) self.ptest("relative 30",('relative',30),5,10,1,4) self.ptest("relative 50",('relative',50),5,10,2,3) self.ptest("relative 130 edge",('relative',130),5,10,5,0) self.ptest("relative -10 edge",('relative',-10),4,10,0,6) self.ptest("center relative 70",'center',('relative',70), 10,1,2) self.ptest("center relative 70 grow 8",'center',('relative',70), 10,1,1,8) def mctest(self, desc, left, right, size, cx, innercx): class Inner: def __init__(self, desc, innercx): self.desc = desc self.innercx = innercx def move_cursor_to_coords(self,size,cx,cy): assert cx==self.innercx, desc i = Inner(desc,innercx) p = urwid.Padding(i, ('fixed left',left), ('fixed right',right)) p.move_cursor_to_coords(size, cx, 0) def test_cursor(self): self.mctest("cursor left edge",2,2,(10,2),2,0) self.mctest("cursor left edge-1",2,2,(10,2),1,0) self.mctest("cursor right edge",2,2,(10,2),7,5) self.mctest("cursor right edge+1",2,2,(10,2),8,5) def test_reduced_padding_cursor(self): # FIXME: This is at least consistent now, but I don't like it. # pack() on an Edit should leave room for the cursor # fixing this gets deep into things like Edit._shift_view_to_cursor # though, so this might not get fixed for a while p = urwid.Padding(urwid.Edit(u'',u''), width='pack', left=4) self.assertEqual(p.render((10,), True).cursor, None) self.assertEqual(p.get_cursor_coords((10,)), None) self.assertEqual(p.render((4,), True).cursor, None) self.assertEqual(p.get_cursor_coords((4,)), None) p = urwid.Padding(urwid.Edit(u'',u''), width=('relative', 100), left=4) self.assertEqual(p.render((10,), True).cursor, (4, 0)) self.assertEqual(p.get_cursor_coords((10,)), (4, 0)) self.assertEqual(p.render((4,), True).cursor, None) self.assertEqual(p.get_cursor_coords((4,)), None) class FillerTest(unittest.TestCase): def ftest(self, desc, valign, height, maxrow, top, bottom, min_height=None): f = urwid.Filler(None, valign, height, min_height) t, b = f.filler_values((20,maxrow), False) assert (t,b)==(top,bottom), "%s expected %s but got %s"%( desc, (top,bottom), (t,b)) def fetest(self, desc, valign, height): self.assertRaises(urwid.FillerError, lambda: urwid.Filler(None, valign, height)) def test_create(self): self.fetest("invalid pad",6,5) self.fetest("invalid pad type",('bad',2),5) self.fetest("invalid width",'middle','42') self.fetest("invalid width type",'middle',('gouranga',4)) self.fetest("invalid combination",('relative',20), ('fixed bottom',4)) self.fetest("invalid combination 2",('relative',20), ('fixed top',4)) def test_values(self): self.ftest("top align 5 7",'top',5,7,0,2) self.ftest("top align 7 7",'top',7,7,0,0) self.ftest("top align 9 7",'top',9,7,0,0) self.ftest("bottom align 5 7",'bottom',5,7,2,0) self.ftest("middle align 5 7",'middle',5,7,1,1) self.ftest("fixed top",('fixed top',3),5,10,3,2) self.ftest("fixed top reduce",('fixed top',3),8,10,2,0) self.ftest("fixed top shrink",('fixed top',3),18,10,0,0) self.ftest("fixed top, bottom", ('fixed top',3),('fixed bottom',4),17,3,4) self.ftest("fixed top, bottom, min_width", ('fixed top',3),('fixed bottom',4),10,3,2,5) self.ftest("fixed top, bottom, min_width 2", ('fixed top',3),('fixed bottom',4),10,2,0,8) self.ftest("fixed bottom",('fixed bottom',3),5,10,2,3) self.ftest("fixed bottom reduce",('fixed bottom',3),8,10,0,2) self.ftest("fixed bottom shrink",('fixed bottom',3),18,10,0,0) self.ftest("fixed bottom, top", ('fixed bottom',3),('fixed top',4),17,4,3) self.ftest("fixed bottom, top, min_height", ('fixed bottom',3),('fixed top',4),10,2,3,5) self.ftest("fixed bottom, top, min_height 2", ('fixed bottom',3),('fixed top',4),10,0,2,8) self.ftest("relative 30",('relative',30),5,10,1,4) self.ftest("relative 50",('relative',50),5,10,2,3) self.ftest("relative 130 edge",('relative',130),5,10,5,0) self.ftest("relative -10 edge",('relative',-10),4,10,0,6) self.ftest("middle relative 70",'middle',('relative',70), 10,1,2) self.ftest("middle relative 70 grow 8",'middle',('relative',70), 10,1,1,8) def test_repr(self): repr(urwid.Filler(urwid.Text(u'hai'))) urwid-1.3.1/urwid/tests/test_str_util.py0000664000175000017500000000235212615524560017772 0ustar ianian00000000000000import unittest from urwid.compat import B from urwid.escape import str_util class DecodeOneTest(unittest.TestCase): def gwt(self, ch, exp_ord, exp_pos): ch = B(ch) o, pos = str_util.decode_one(ch,0) assert o==exp_ord, " got:%r expected:%r" % (o, exp_ord) assert pos==exp_pos, " got:%r expected:%r" % (pos, exp_pos) def test1byte(self): self.gwt("ab", ord("a"), 1) self.gwt("\xc0a", ord("?"), 1) # error def test2byte(self): self.gwt("\xc2", ord("?"), 1) # error self.gwt("\xc0\x80", ord("?"), 1) # error self.gwt("\xc2\x80", 0x80, 2) self.gwt("\xdf\xbf", 0x7ff, 2) def test3byte(self): self.gwt("\xe0", ord("?"), 1) # error self.gwt("\xe0\xa0", ord("?"), 1) # error self.gwt("\xe0\x90\x80", ord("?"), 1) # error self.gwt("\xe0\xa0\x80", 0x800, 3) self.gwt("\xef\xbf\xbf", 0xffff, 3) def test4byte(self): self.gwt("\xf0", ord("?"), 1) # error self.gwt("\xf0\x90", ord("?"), 1) # error self.gwt("\xf0\x90\x80", ord("?"), 1) # error self.gwt("\xf0\x80\x80\x80", ord("?"), 1) # error self.gwt("\xf0\x90\x80\x80", 0x10000, 4) self.gwt("\xf3\xbf\xbf\xbf", 0xfffff, 4) urwid-1.3.1/urwid/tests/util.py0000664000175000017500000000022212615524560016035 0ustar ianian00000000000000import urwid class SelectableText(urwid.Text): def selectable(self): return 1 def keypress(self, size, key): return key urwid-1.3.1/urwid/tests/test_text_layout.py0000664000175000017500000002462212615524560020512 0ustar ianian00000000000000import unittest from urwid import text_layout from urwid.compat import B import urwid class CalcBreaksTest(object): def cbtest(self, width, exp): result = text_layout.default_layout.calculate_text_segments( B(self.text), width, self.mode ) assert len(result) == len(exp), repr((result, exp)) for l,e in zip(result, exp): end = l[-1][-1] assert end == e, repr((result,exp)) def test(self): for width, exp in self.do: self.cbtest( width, exp ) class CalcBreaksCharTest(CalcBreaksTest, unittest.TestCase): mode = 'any' text = "abfghsdjf askhtrvs\naltjhgsdf ljahtshgf" # tests do = [ ( 100, [18,38] ), ( 6, [6, 12, 18, 25, 31, 37, 38] ), ( 10, [10, 18, 29, 38] ), ] class CalcBreaksDBCharTest(CalcBreaksTest, unittest.TestCase): def setUp(self): urwid.set_encoding("euc-jp") mode = 'any' text = "abfgh\xA1\xA1j\xA1\xA1xskhtrvs\naltjhgsdf\xA1\xA1jahtshgf" # tests do = [ ( 10, [10, 18, 28, 38] ), ( 6, [5, 11, 17, 18, 25, 31, 37, 38] ), ( 100, [18, 38]), ] class CalcBreaksWordTest(CalcBreaksTest, unittest.TestCase): mode = 'space' text = "hello world\nout there. blah" # tests do = [ ( 10, [5, 11, 22, 27] ), ( 5, [5, 11, 17, 22, 27] ), ( 100, [11, 27] ), ] class CalcBreaksWordTest2(CalcBreaksTest, unittest.TestCase): mode = 'space' text = "A simple set of words, really...." do = [ ( 10, [8, 15, 22, 33]), ( 17, [15, 33]), ( 13, [12, 22, 33]), ] class CalcBreaksDBWordTest(CalcBreaksTest, unittest.TestCase): def setUp(self): urwid.set_encoding("euc-jp") mode = 'space' text = "hel\xA1\xA1 world\nout-\xA1\xA1tre blah" # tests do = [ ( 10, [5, 11, 21, 26] ), ( 5, [5, 11, 16, 21, 26] ), ( 100, [11, 26] ), ] class CalcBreaksUTF8Test(CalcBreaksTest, unittest.TestCase): def setUp(self): urwid.set_encoding("utf-8") mode = 'space' text = '\xe6\x9b\xbf\xe6\xb4\xbc\xe6\xb8\x8e\xe6\xba\x8f\xe6\xbd\xba' do = [ (4, [6, 12, 15] ), (10, [15] ), (5, [6, 12, 15] ), ] class CalcBreaksCantDisplayTest(unittest.TestCase): def test(self): urwid.set_encoding("euc-jp") self.assertRaises(text_layout.CanNotDisplayText, text_layout.default_layout.calculate_text_segments, B('\xA1\xA1'), 1, 'space' ) urwid.set_encoding("utf-8") self.assertRaises(text_layout.CanNotDisplayText, text_layout.default_layout.calculate_text_segments, B('\xe9\xa2\x96'), 1, 'space' ) class SubsegTest(unittest.TestCase): def setUp(self): urwid.set_encoding("euc-jp") def st(self, seg, text, start, end, exp): text = B(text) s = urwid.LayoutSegment(seg) result = s.subseg( text, start, end ) assert result == exp, "Expected %r, got %r"%(exp,result) def test1_padding(self): self.st( (10, None), "", 0, 8, [(8, None)] ) self.st( (10, None), "", 2, 10, [(8, None)] ) self.st( (10, 0), "", 3, 7, [(4, 0)] ) self.st( (10, 0), "", 0, 20, [(10, 0)] ) def test2_text(self): self.st( (10, 0, B("1234567890")), "", 0, 8, [(8,0,B("12345678"))] ) self.st( (10, 0, B("1234567890")), "", 2, 10, [(8,0,B("34567890"))] ) self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 2, 8, [(6, 0, B("\xA1\xA156\xA1\xA1"))] ) self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 3, 8, [(5, 0, B(" 56\xA1\xA1"))] ) self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 2, 7, [(5, 0, B("\xA1\xA156 "))] ) self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 3, 7, [(4, 0, B(" 56 "))] ) self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 0, 20, [(10, 0, B("12\xA1\xA156\xA1\xA190"))] ) def test3_range(self): t = "1234567890" self.st( (10, 0, 10), t, 0, 8, [(8, 0, 8)] ) self.st( (10, 0, 10), t, 2, 10, [(8, 2, 10)] ) self.st( (6, 2, 8), t, 1, 6, [(5, 3, 8)] ) self.st( (6, 2, 8), t, 0, 5, [(5, 2, 7)] ) self.st( (6, 2, 8), t, 1, 5, [(4, 3, 7)] ) t = "12\xA1\xA156\xA1\xA190" self.st( (10, 0, 10), t, 0, 8, [(8, 0, 8)] ) self.st( (10, 0, 10), t, 2, 10, [(8, 2, 10)] ) self.st( (6, 2, 8), t, 1, 6, [(1, 3), (4, 4, 8)] ) self.st( (6, 2, 8), t, 0, 5, [(4, 2, 6), (1, 6)] ) self.st( (6, 2, 8), t, 1, 5, [(1, 3), (2, 4, 6), (1, 6)] ) class CalcTranslateTest(object): def setUp(self): urwid.set_encoding("utf-8") def test1_left(self): result = urwid.default_layout.layout( self.text, self.width, 'left', self.mode) assert result == self.result_left, result def test2_right(self): result = urwid.default_layout.layout( self.text, self.width, 'right', self.mode) assert result == self.result_right, result def test3_center(self): result = urwid.default_layout.layout( self.text, self.width, 'center', self.mode) assert result == self.result_center, result class CalcTranslateCharTest(CalcTranslateTest, unittest.TestCase): text = "It's out of control!\nYou've got to" mode = 'any' width = 15 result_left = [ [(15, 0, 15)], [(5, 15, 20), (0, 20)], [(13, 21, 34), (0, 34)]] result_right = [ [(15, 0, 15)], [(10, None), (5, 15, 20), (0,20)], [(2, None), (13, 21, 34), (0,34)]] result_center = [ [(15, 0, 15)], [(5, None), (5, 15, 20), (0,20)], [(1, None), (13, 21, 34), (0,34)]] class CalcTranslateWordTest(CalcTranslateTest, unittest.TestCase): text = "It's out of control!\nYou've got to" mode = 'space' width = 14 result_left = [ [(11, 0, 11), (0, 11)], [(8, 12, 20), (0, 20)], [(13, 21, 34), (0, 34)]] result_right = [ [(3, None), (11, 0, 11), (0, 11)], [(6, None), (8, 12, 20), (0, 20)], [(1, None), (13, 21, 34), (0, 34)]] result_center = [ [(2, None), (11, 0, 11), (0, 11)], [(3, None), (8, 12, 20), (0, 20)], [(1, None), (13, 21, 34), (0, 34)]] class CalcTranslateWordTest2(CalcTranslateTest, unittest.TestCase): text = "It's out of control!\nYou've got to " mode = 'space' width = 14 result_left = [ [(11, 0, 11), (0, 11)], [(8, 12, 20), (0, 20)], [(14, 21, 35), (0, 35)]] result_right = [ [(3, None), (11, 0, 11), (0, 11)], [(6, None), (8, 12, 20), (0, 20)], [(14, 21, 35), (0, 35)]] result_center = [ [(2, None), (11, 0, 11), (0, 11)], [(3, None), (8, 12, 20), (0, 20)], [(14, 21, 35), (0, 35)]] class CalcTranslateWordTest3(CalcTranslateTest, unittest.TestCase): def setUp(self): urwid.set_encoding('utf-8') text = B('\xe6\x9b\xbf\xe6\xb4\xbc\n\xe6\xb8\x8e\xe6\xba\x8f\xe6\xbd\xba') width = 10 mode = 'space' result_left = [ [(4, 0, 6), (0, 6)], [(6, 7, 16), (0, 16)]] result_right = [ [(6, None), (4, 0, 6), (0, 6)], [(4, None), (6, 7, 16), (0, 16)]] result_center = [ [(3, None), (4, 0, 6), (0, 6)], [(2, None), (6, 7, 16), (0, 16)]] class CalcTranslateWordTest4(CalcTranslateTest, unittest.TestCase): text = ' Die Gedank' width = 3 mode = 'space' result_left = [ [(0, 0)], [(3, 1, 4), (0, 4)], [(3, 5, 8)], [(3, 8, 11), (0, 11)]] result_right = [ [(3, None), (0, 0)], [(3, 1, 4), (0, 4)], [(3, 5, 8)], [(3, 8, 11), (0, 11)]] result_center = [ [(2, None), (0, 0)], [(3, 1, 4), (0, 4)], [(3, 5, 8)], [(3, 8, 11), (0, 11)]] class CalcTranslateWordTest5(CalcTranslateTest, unittest.TestCase): text = ' Word.' width = 3 mode = 'space' result_left = [[(3, 0, 3)], [(3, 3, 6), (0, 6)]] result_right = [[(3, 0, 3)], [(3, 3, 6), (0, 6)]] result_center = [[(3, 0, 3)], [(3, 3, 6), (0, 6)]] class CalcTranslateClipTest(CalcTranslateTest, unittest.TestCase): text = "It's out of control!\nYou've got to\n\nturn it off!!!" mode = 'clip' width = 14 result_left = [ [(20, 0, 20), (0, 20)], [(13, 21, 34), (0, 34)], [(0, 35)], [(14, 36, 50), (0, 50)]] result_right = [ [(-6, None), (20, 0, 20), (0, 20)], [(1, None), (13, 21, 34), (0, 34)], [(14, None), (0, 35)], [(14, 36, 50), (0, 50)]] result_center = [ [(-3, None), (20, 0, 20), (0, 20)], [(1, None), (13, 21, 34), (0, 34)], [(7, None), (0, 35)], [(14, 36, 50), (0, 50)]] class CalcTranslateCantDisplayTest(CalcTranslateTest, unittest.TestCase): text = B('Hello\xe9\xa2\x96') mode = 'space' width = 1 result_left = [[]] result_right = [[]] result_center = [[]] class CalcPosTest(unittest.TestCase): def setUp(self): self.text = "A" * 27 self.trans = [ [(2,None),(7,0,7),(0,7)], [(13,8,21),(0,21)], [(3,None),(5,22,27),(0,27)]] self.mytests = [(1,0, 0), (2,0, 0), (11,0, 7), (-3,1, 8), (-2,1, 8), (1,1, 9), (31,1, 21), (1,2, 22), (11,2, 27) ] def tests(self): for x,y, expected in self.mytests: got = text_layout.calc_pos( self.text, self.trans, x, y ) assert got == expected, "%r got:%r expected:%r" % ((x, y), got, expected) class Pos2CoordsTest(unittest.TestCase): pos_list = [5, 9, 20, 26] text = "1234567890" * 3 mytests = [ ( [[(15,0,15)], [(15,15,30),(0,30)]], [(5,0),(9,0),(5,1),(11,1)] ), ( [[(9,0,9)], [(12,9,21)], [(9,21,30),(0,30)]], [(5,0),(0,1),(11,1),(5,2)] ), ( [[(2,None), (15,0,15)], [(2,None), (15,15,30),(0,30)]], [(7,0),(11,0),(7,1),(13,1)] ), ( [[(3, 6, 9),(0,9)], [(5, 20, 25),(0,25)]], [(0,0),(3,0),(0,1),(5,1)] ), ( [[(10, 0, 10),(0,10)]], [(5,0),(9,0),(10,0),(10,0)] ), ] def test(self): for t, answer in self.mytests: for pos,a in zip(self.pos_list,answer) : r = text_layout.calc_coords( self.text, t, pos) assert r==a, "%r got: %r expected: %r"%(t,r,a) urwid-1.3.1/urwid/tests/test_event_loops.py0000664000175000017500000001035512615524560020464 0ustar ianian00000000000000import os import unittest import platform import urwid from urwid.compat import PYTHON3 class EventLoopTestMixin(object): def test_event_loop(self): rd, wr = os.pipe() evl = self.evl out = [] def step1(): out.append("writing") os.write(wr, "hi".encode('ascii')) def step2(): out.append(os.read(rd, 2).decode('ascii')) raise urwid.ExitMainLoop handle = evl.alarm(0, step1) handle = evl.watch_file(rd, step2) evl.run() self.assertEqual(out, ["writing", "hi"]) def test_remove_alarm(self): evl = self.evl handle = evl.alarm(50, lambda: None) self.assertTrue(evl.remove_alarm(handle)) self.assertFalse(evl.remove_alarm(handle)) def test_remove_watch_file(self): evl = self.evl handle = evl.watch_file(5, lambda: None) self.assertTrue(evl.remove_watch_file(handle)) self.assertFalse(evl.remove_watch_file(handle)) _expected_idle_handle = 1 def test_run(self): evl = self.evl out = [] rd, wr = os.pipe() self.assertEqual(os.write(wr, "data".encode('ascii')), 4) def say_hello(): out.append("hello") def say_waiting(): out.append("waiting") def exit_clean(): out.append("clean exit") raise urwid.ExitMainLoop def exit_error(): 1/0 handle = evl.alarm(0.01, exit_clean) handle = evl.alarm(0.005, say_hello) idle_handle = evl.enter_idle(say_waiting) if self._expected_idle_handle is not None: self.assertEqual(idle_handle, 1) evl.run() self.assertTrue("hello" in out, out) self.assertTrue("clean exit"in out, out) handle = evl.watch_file(rd, exit_clean) del out[:] evl.run() self.assertEqual(out, ["clean exit"]) self.assertTrue(evl.remove_watch_file(handle)) handle = evl.alarm(0, exit_error) self.assertRaises(ZeroDivisionError, evl.run) handle = evl.watch_file(rd, exit_error) self.assertRaises(ZeroDivisionError, evl.run) class SelectEventLoopTest(unittest.TestCase, EventLoopTestMixin): def setUp(self): self.evl = urwid.SelectEventLoop() try: import gi.repository except ImportError: pass else: class GLibEventLoopTest(unittest.TestCase, EventLoopTestMixin): def setUp(self): self.evl = urwid.GLibEventLoop() try: import tornado except ImportError: pass else: class TornadoEventLoopTest(unittest.TestCase, EventLoopTestMixin): def setUp(self): from tornado.ioloop import IOLoop self.evl = urwid.TornadoEventLoop(IOLoop()) try: import twisted except ImportError: pass else: class TwistedEventLoopTest(unittest.TestCase, EventLoopTestMixin): def setUp(self): self.evl = urwid.TwistedEventLoop() # can't restart twisted reactor, so use shortened tests def test_event_loop(self): pass def test_run(self): evl = self.evl out = [] rd, wr = os.pipe() self.assertEqual(os.write(wr, "data".encode('ascii')), 4) def step2(): out.append(os.read(rd, 2).decode('ascii')) def say_hello(): out.append("hello") def say_waiting(): out.append("waiting") def exit_clean(): out.append("clean exit") raise urwid.ExitMainLoop def exit_error(): 1/0 handle = evl.watch_file(rd, step2) handle = evl.alarm(0.01, exit_clean) handle = evl.alarm(0.005, say_hello) self.assertEqual(evl.enter_idle(say_waiting), 1) evl.run() self.assertTrue("da" in out, out) self.assertTrue("ta" in out, out) self.assertTrue("hello" in out, out) self.assertTrue("clean exit" in out, out) try: import asyncio except ImportError: pass else: class AsyncioEventLoopTest(unittest.TestCase, EventLoopTestMixin): def setUp(self): self.evl = urwid.AsyncioEventLoop() _expected_idle_handle = None urwid-1.3.1/urwid/tests/test_container.py0000664000175000017500000006530112615524560020112 0ustar ianian00000000000000import unittest from urwid.tests.util import SelectableText import urwid class FrameTest(unittest.TestCase): def ftbtest(self, desc, focus_part, header_rows, footer_rows, size, focus, top, bottom): class FakeWidget: def __init__(self, rows, want_focus): self.ret_rows = rows self.want_focus = want_focus def rows(self, size, focus=False): assert self.want_focus == focus return self.ret_rows header = footer = None if header_rows: header = FakeWidget(header_rows, focus and focus_part == 'header') if footer_rows: footer = FakeWidget(footer_rows, focus and focus_part == 'footer') f = urwid.Frame(None, header, footer, focus_part) rval = f.frame_top_bottom(size, focus) exp = (top, bottom), (header_rows, footer_rows) assert exp == rval, "%s expected %r but got %r"%( desc,exp,rval) def test(self): self.ftbtest("simple", 'body', 0, 0, (9, 10), True, 0, 0) self.ftbtest("simple h", 'body', 3, 0, (9, 10), True, 3, 0) self.ftbtest("simple f", 'body', 0, 3, (9, 10), True, 0, 3) self.ftbtest("simple hf", 'body', 3, 3, (9, 10), True, 3, 3) self.ftbtest("almost full hf", 'body', 4, 5, (9, 10), True, 4, 5) self.ftbtest("full hf", 'body', 5, 5, (9, 10), True, 4, 5) self.ftbtest("x full h+1f", 'body', 6, 5, (9, 10), False, 4, 5) self.ftbtest("full h+1f", 'body', 6, 5, (9, 10), True, 4, 5) self.ftbtest("full hf+1", 'body', 5, 6, (9, 10), True, 3, 6) self.ftbtest("F full h+1f", 'footer', 6, 5, (9, 10), True, 5, 5) self.ftbtest("F full hf+1", 'footer', 5, 6, (9, 10), True, 4, 6) self.ftbtest("F full hf+5", 'footer', 5, 11, (9, 10), True, 0, 10) self.ftbtest("full hf+5", 'body', 5, 11, (9, 10), True, 0, 9) self.ftbtest("H full hf+1", 'header', 5, 6, (9, 10), True, 5, 5) self.ftbtest("H full h+1f", 'header', 6, 5, (9, 10), True, 6, 4) self.ftbtest("H full h+5f", 'header', 11, 5, (9, 10), True, 10, 0) class PileTest(unittest.TestCase): def ktest(self, desc, l, focus_item, key, rkey, rfocus, rpref_col): p = urwid.Pile( l, focus_item ) rval = p.keypress( (20,), key ) assert rkey == rval, "%s key expected %r but got %r" %( desc, rkey, rval) new_focus = l.index(p.get_focus()) assert new_focus == rfocus, "%s focus expected %r but got %r" %( desc, rfocus, new_focus) new_pref = p.get_pref_col((20,)) assert new_pref == rpref_col, ( "%s pref_col expected %r but got %r" % ( desc, rpref_col, new_pref)) def test_select_change(self): T,S,E = urwid.Text, SelectableText, urwid.Edit self.ktest("simple up", [S("")], 0, "up", "up", 0, 0) self.ktest("simple down", [S("")], 0, "down", "down", 0, 0) self.ktest("ignore up", [T(""),S("")], 1, "up", "up", 1, 0) self.ktest("ignore down", [S(""),T("")], 0, "down", "down", 0, 0) self.ktest("step up", [S(""),S("")], 1, "up", None, 0, 0) self.ktest("step down", [S(""),S("")], 0, "down", None, 1, 0) self.ktest("skip step up", [S(""),T(""),S("")], 2, "up", None, 0, 0) self.ktest("skip step down", [S(""),T(""),S("")], 0, "down", None, 2, 0) self.ktest("pad skip step up", [T(""),S(""),T(""),S("")], 3, "up", None, 1, 0) self.ktest("pad skip step down", [S(""),T(""),S(""),T("")], 0, "down", None, 2, 0) self.ktest("padi skip step up", [S(""),T(""),S(""),T(""),S("")], 4, "up", None, 2, 0) self.ktest("padi skip step down", [S(""),T(""),S(""),T(""), S("")], 0, "down", None, 2, 0) e = E("","abcd", edit_pos=1) e.keypress((20,),"right") # set a pref_col self.ktest("pref step up", [S(""),T(""),e], 2, "up", None, 0, 2) self.ktest("pref step down", [e,T(""),S("")], 0, "down", None, 2, 2) z = E("","1234") self.ktest("prefx step up", [z,T(""),e], 2, "up", None, 0, 2) assert z.get_pref_col((20,)) == 2 z = E("","1234") self.ktest("prefx step down", [e,T(""),z], 0, "down", None, 2, 2) assert z.get_pref_col((20,)) == 2 def test_init_with_a_generator(self): urwid.Pile(urwid.Text(c) for c in "ABC") def test_change_focus_with_mouse(self): p = urwid.Pile([urwid.Edit(), urwid.Edit()]) self.assertEqual(p.focus_position, 0) p.mouse_event((10,), 'button press', 1, 1, 1, True) self.assertEqual(p.focus_position, 1) def test_zero_weight(self): p = urwid.Pile([ urwid.SolidFill('a'), ('weight', 0, urwid.SolidFill('d')), ]) p.render((5, 4)) def test_mouse_event_in_empty_pile(self): p = urwid.Pile([]) p.mouse_event((5,), 'button press', 1, 1, 1, False) p.mouse_event((5,), 'button press', 1, 1, 1, True) class ColumnsTest(unittest.TestCase): def cwtest(self, desc, l, divide, size, exp, focus_column=0): c = urwid.Columns(l, divide, focus_column) rval = c.column_widths( size ) assert rval == exp, "%s expected %s, got %s"%(desc,exp,rval) def test_widths(self): x = urwid.Text("") # sample "column" self.cwtest( "simple 1", [x], 0, (20,), [20] ) self.cwtest( "simple 2", [x,x], 0, (20,), [10,10] ) self.cwtest( "simple 2+1", [x,x], 1, (20,), [10,9] ) self.cwtest( "simple 3+1", [x,x,x], 1, (20,), [6,6,6] ) self.cwtest( "simple 3+2", [x,x,x], 2, (20,), [5,6,5] ) self.cwtest( "simple 3+2", [x,x,x], 2, (21,), [6,6,5] ) self.cwtest( "simple 4+1", [x,x,x,x], 1, (25,), [6,5,6,5] ) self.cwtest( "squish 4+1", [x,x,x,x], 1, (7,), [1,1,1,1] ) self.cwtest( "squish 4+1", [x,x,x,x], 1, (6,), [1,2,1] ) self.cwtest( "squish 4+1", [x,x,x,x], 1, (4,), [2,1] ) self.cwtest( "fixed 3", [('fixed',4,x),('fixed',6,x), ('fixed',2,x)], 1, (25,), [4,6,2] ) self.cwtest( "fixed 3 cut", [('fixed',4,x),('fixed',6,x), ('fixed',2,x)], 1, (13,), [4,6] ) self.cwtest( "fixed 3 cut2", [('fixed',4,x),('fixed',6,x), ('fixed',2,x)], 1, (10,), [4] ) self.cwtest( "mixed 4", [('weight',2,x),('fixed',5,x), x, ('weight',3,x)], 1, (14,), [2,5,1,3] ) self.cwtest( "mixed 4 a", [('weight',2,x),('fixed',5,x), x, ('weight',3,x)], 1, (12,), [1,5,1,2] ) self.cwtest( "mixed 4 b", [('weight',2,x),('fixed',5,x), x, ('weight',3,x)], 1, (10,), [2,5,1] ) self.cwtest( "mixed 4 c", [('weight',2,x),('fixed',5,x), x, ('weight',3,x)], 1, (20,), [4,5,2,6] ) def test_widths_focus_end(self): x = urwid.Text("") # sample "column" self.cwtest("end simple 2", [x,x], 0, (20,), [10,10], 1) self.cwtest("end simple 2+1", [x,x], 1, (20,), [10,9], 1) self.cwtest("end simple 3+1", [x,x,x], 1, (20,), [6,6,6], 2) self.cwtest("end simple 3+2", [x,x,x], 2, (20,), [5,6,5], 2) self.cwtest("end simple 3+2", [x,x,x], 2, (21,), [6,6,5], 2) self.cwtest("end simple 4+1", [x,x,x,x], 1, (25,), [6,5,6,5], 3) self.cwtest("end squish 4+1", [x,x,x,x], 1, (7,), [1,1,1,1], 3) self.cwtest("end squish 4+1", [x,x,x,x], 1, (6,), [0,1,2,1], 3) self.cwtest("end squish 4+1", [x,x,x,x], 1, (4,), [0,0,2,1], 3) self.cwtest("end fixed 3", [('fixed',4,x),('fixed',6,x), ('fixed',2,x)], 1, (25,), [4,6,2], 2) self.cwtest("end fixed 3 cut", [('fixed',4,x),('fixed',6,x), ('fixed',2,x)], 1, (13,), [0,6,2], 2) self.cwtest("end fixed 3 cut2", [('fixed',4,x),('fixed',6,x), ('fixed',2,x)], 1, (8,), [0,0,2], 2) self.cwtest("end mixed 4", [('weight',2,x),('fixed',5,x), x, ('weight',3,x)], 1, (14,), [2,5,1,3], 3) self.cwtest("end mixed 4 a", [('weight',2,x),('fixed',5,x), x, ('weight',3,x)], 1, (12,), [1,5,1,2], 3) self.cwtest("end mixed 4 b", [('weight',2,x),('fixed',5,x), x, ('weight',3,x)], 1, (10,), [0,5,1,2], 3) self.cwtest("end mixed 4 c", [('weight',2,x),('fixed',5,x), x, ('weight',3,x)], 1, (20,), [4,5,2,6], 3) def mctest(self, desc, l, divide, size, col, row, exp, f_col, pref_col): c = urwid.Columns( l, divide ) rval = c.move_cursor_to_coords( size, col, row ) assert rval == exp, "%s expected %r, got %r"%(desc,exp,rval) assert c.focus_col == f_col, "%s expected focus_col %s got %s"%( desc, f_col, c.focus_col) pc = c.get_pref_col( size ) assert pc == pref_col, "%s expected pref_col %s, got %s"%( desc, pref_col, pc) def test_move_cursor(self): e, s, x = urwid.Edit("",""),SelectableText(""), urwid.Text("") self.mctest("nothing selectbl",[x,x,x],1,(20,),9,0,False,0,None) self.mctest("dead on",[x,s,x],1,(20,),9,0,True,1,9) self.mctest("l edge",[x,s,x],1,(20,),6,0,True,1,6) self.mctest("r edge",[x,s,x],1,(20,),13,0,True,1,13) self.mctest("l off",[x,s,x],1,(20,),2,0,True,1,2) self.mctest("r off",[x,s,x],1,(20,),17,0,True,1,17) self.mctest("l off 2",[x,x,s],1,(20,),2,0,True,2,2) self.mctest("r off 2",[s,x,x],1,(20,),17,0,True,0,17) self.mctest("l between",[s,s,x],1,(20,),6,0,True,0,6) self.mctest("r between",[x,s,s],1,(20,),13,0,True,1,13) self.mctest("l between 2l",[s,s,x],2,(22,),6,0,True,0,6) self.mctest("r between 2l",[x,s,s],2,(22,),14,0,True,1,14) self.mctest("l between 2r",[s,s,x],2,(22,),7,0,True,1,7) self.mctest("r between 2r",[x,s,s],2,(22,),15,0,True,2,15) # unfortunate pref_col shifting self.mctest("l e edge",[x,e,x],1,(20,),6,0,True,1,7) self.mctest("r e edge",[x,e,x],1,(20,),13,0,True,1,12) # 'left'/'right' special cases self.mctest("right", [e, e, e], 0, (12,), 'right', 0, True, 2, 'right') self.mctest("left", [e, e, e], 0, (12,), 'left', 0, True, 0, 'left') def test_init_with_a_generator(self): urwid.Columns(urwid.Text(c) for c in "ABC") def test_old_attributes(self): c = urwid.Columns([urwid.Text(u'a'), urwid.SolidFill(u'x')], box_columns=[1]) self.assertEqual(c.box_columns, [1]) c.box_columns=[] self.assertEqual(c.box_columns, []) def test_box_column(self): c = urwid.Columns([urwid.Filler(urwid.Edit()),urwid.Text('')], box_columns=[0]) c.keypress((10,), 'x') c.get_cursor_coords((10,)) c.move_cursor_to_coords((10,), 0, 0) c.mouse_event((10,), 'foo', 1, 0, 0, True) c.get_pref_col((10,)) class OverlayTest(unittest.TestCase): def test_old_params(self): o1 = urwid.Overlay(urwid.SolidFill(u'X'), urwid.SolidFill(u'O'), ('fixed left', 5), ('fixed right', 4), ('fixed top', 3), ('fixed bottom', 2),) self.assertEqual(o1.contents[1][1], ( 'left', None, 'relative', 100, None, 5, 4, 'top', None, 'relative', 100, None, 3, 2)) o2 = urwid.Overlay(urwid.SolidFill(u'X'), urwid.SolidFill(u'O'), ('fixed right', 5), ('fixed left', 4), ('fixed bottom', 3), ('fixed top', 2),) self.assertEqual(o2.contents[1][1], ( 'right', None, 'relative', 100, None, 4, 5, 'bottom', None, 'relative', 100, None, 2, 3)) def test_get_cursor_coords(self): self.assertEqual(urwid.Overlay(urwid.Filler(urwid.Edit()), urwid.SolidFill(u'B'), 'right', 1, 'bottom', 1).get_cursor_coords((2,2)), (1,1)) class GridFlowTest(unittest.TestCase): def test_cell_width(self): gf = urwid.GridFlow([], 5, 0, 0, 'left') self.assertEqual(gf.cell_width, 5) def test_basics(self): repr(urwid.GridFlow([], 5, 0, 0, 'left')) # should not fail def test_v_sep(self): gf = urwid.GridFlow([urwid.Text("test")], 10, 3, 1, "center") self.assertEqual(gf.rows((40,), False), 1) class WidgetSquishTest(unittest.TestCase): def wstest(self, w): c = w.render((80,0), focus=False) assert c.rows() == 0 c = w.render((80,0), focus=True) assert c.rows() == 0 c = w.render((80,1), focus=False) assert c.rows() == 1 c = w.render((0, 25), focus=False) c = w.render((1, 25), focus=False) def fwstest(self, w): def t(cols, focus): wrows = w.rows((cols,), focus) c = w.render((cols,), focus) assert c.rows() == wrows, (c.rows(), wrows) if focus and hasattr(w, 'get_cursor_coords'): gcc = w.get_cursor_coords((cols,)) assert c.cursor == gcc, (c.cursor, gcc) t(0, False) t(1, False) t(0, True) t(1, True) def test_listbox(self): self.wstest(urwid.ListBox([])) self.wstest(urwid.ListBox([urwid.Text("hello")])) def test_bargraph(self): self.wstest(urwid.BarGraph(['foo','bar'])) def test_graphvscale(self): self.wstest(urwid.GraphVScale([(0,"hello")], 1)) self.wstest(urwid.GraphVScale([(5,"hello")], 1)) def test_solidfill(self): self.wstest(urwid.SolidFill()) def test_filler(self): self.wstest(urwid.Filler(urwid.Text("hello"))) def test_overlay(self): self.wstest(urwid.Overlay( urwid.BigText("hello",urwid.Thin6x6Font()), urwid.SolidFill(), 'center', None, 'middle', None)) self.wstest(urwid.Overlay( urwid.Text("hello"), urwid.SolidFill(), 'center', ('relative', 100), 'middle', None)) def test_frame(self): self.wstest(urwid.Frame(urwid.SolidFill())) self.wstest(urwid.Frame(urwid.SolidFill(), header=urwid.Text("hello"))) self.wstest(urwid.Frame(urwid.SolidFill(), header=urwid.Text("hello"), footer=urwid.Text("hello"))) def test_pile(self): self.wstest(urwid.Pile([urwid.SolidFill()])) self.wstest(urwid.Pile([('flow', urwid.Text("hello"))])) self.wstest(urwid.Pile([])) def test_columns(self): self.wstest(urwid.Columns([urwid.SolidFill()])) self.wstest(urwid.Columns([(4, urwid.SolidFill())])) def test_buttons(self): self.fwstest(urwid.Button(u"hello")) self.fwstest(urwid.RadioButton([], u"hello")) class CommonContainerTest(unittest.TestCase): def test_pile(self): t1 = urwid.Text(u'one') t2 = urwid.Text(u'two') t3 = urwid.Text(u'three') sf = urwid.SolidFill('x') p = urwid.Pile([]) self.assertEqual(p.focus, None) self.assertRaises(IndexError, lambda: getattr(p, 'focus_position')) self.assertRaises(IndexError, lambda: setattr(p, 'focus_position', None)) self.assertRaises(IndexError, lambda: setattr(p, 'focus_position', 0)) p.contents = [(t1, ('pack', None)), (t2, ('pack', None)), (sf, ('given', 3)), (t3, ('pack', None))] p.focus_position = 1 del p.contents[0] self.assertEqual(p.focus_position, 0) p.contents[0:0] = [(t3, ('pack', None)), (t2, ('pack', None))] p.contents.insert(3, (t1, ('pack', None))) self.assertEqual(p.focus_position, 2) self.assertRaises(urwid.PileError, lambda: p.contents.append(t1)) self.assertRaises(urwid.PileError, lambda: p.contents.append((t1, None))) self.assertRaises(urwid.PileError, lambda: p.contents.append((t1, 'given'))) p = urwid.Pile([t1, t2]) self.assertEqual(p.focus, t1) self.assertEqual(p.focus_position, 0) p.focus_position = 1 self.assertEqual(p.focus, t2) self.assertEqual(p.focus_position, 1) p.focus_position = 0 self.assertRaises(IndexError, lambda: setattr(p, 'focus_position', -1)) self.assertRaises(IndexError, lambda: setattr(p, 'focus_position', 2)) # old methods: p.set_focus(0) self.assertRaises(IndexError, lambda: p.set_focus(-1)) self.assertRaises(IndexError, lambda: p.set_focus(2)) p.set_focus(t2) self.assertEqual(p.focus_position, 1) self.assertRaises(ValueError, lambda: p.set_focus('nonexistant')) self.assertEqual(p.widget_list, [t1, t2]) self.assertEqual(p.item_types, [('weight', 1), ('weight', 1)]) p.widget_list = [t2, t1] self.assertEqual(p.widget_list, [t2, t1]) self.assertEqual(p.contents, [(t2, ('weight', 1)), (t1, ('weight', 1))]) self.assertEqual(p.focus_position, 1) # focus unchanged p.item_types = [('flow', None), ('weight', 2)] self.assertEqual(p.item_types, [('flow', None), ('weight', 2)]) self.assertEqual(p.contents, [(t2, ('pack', None)), (t1, ('weight', 2))]) self.assertEqual(p.focus_position, 1) # focus unchanged p.widget_list = [t1] self.assertEqual(len(p.contents), 1) self.assertEqual(p.focus_position, 0) p.widget_list.extend([t2, t1]) self.assertEqual(len(p.contents), 3) self.assertEqual(p.item_types, [ ('flow', None), ('weight', 1), ('weight', 1)]) p.item_types[:] = [('weight', 2)] self.assertEqual(len(p.contents), 1) def test_columns(self): t1 = urwid.Text(u'one') t2 = urwid.Text(u'two') t3 = urwid.Text(u'three') sf = urwid.SolidFill('x') c = urwid.Columns([]) self.assertEqual(c.focus, None) self.assertRaises(IndexError, lambda: getattr(c, 'focus_position')) self.assertRaises(IndexError, lambda: setattr(c, 'focus_position', None)) self.assertRaises(IndexError, lambda: setattr(c, 'focus_position', 0)) c.contents = [ (t1, ('pack', None, False)), (t2, ('weight', 1, False)), (sf, ('weight', 2, True)), (t3, ('given', 10, False))] c.focus_position = 1 del c.contents[0] self.assertEqual(c.focus_position, 0) c.contents[0:0] = [ (t3, ('given', 10, False)), (t2, ('weight', 1, False))] c.contents.insert(3, (t1, ('pack', None, False))) self.assertEqual(c.focus_position, 2) self.assertRaises(urwid.ColumnsError, lambda: c.contents.append(t1)) self.assertRaises(urwid.ColumnsError, lambda: c.contents.append((t1, None))) self.assertRaises(urwid.ColumnsError, lambda: c.contents.append((t1, 'given'))) c = urwid.Columns([t1, t2]) self.assertEqual(c.focus, t1) self.assertEqual(c.focus_position, 0) c.focus_position = 1 self.assertEqual(c.focus, t2) self.assertEqual(c.focus_position, 1) c.focus_position = 0 self.assertRaises(IndexError, lambda: setattr(c, 'focus_position', -1)) self.assertRaises(IndexError, lambda: setattr(c, 'focus_position', 2)) # old methods: c = urwid.Columns([t1, ('weight', 3, t2), sf], box_columns=[2]) c.set_focus(0) self.assertRaises(IndexError, lambda: c.set_focus(-1)) self.assertRaises(IndexError, lambda: c.set_focus(3)) c.set_focus(t2) self.assertEqual(c.focus_position, 1) self.assertRaises(ValueError, lambda: c.set_focus('nonexistant')) self.assertEqual(c.widget_list, [t1, t2, sf]) self.assertEqual(c.column_types, [ ('weight', 1), ('weight', 3), ('weight', 1)]) self.assertEqual(c.box_columns, [2]) c.widget_list = [t2, t1, sf] self.assertEqual(c.widget_list, [t2, t1, sf]) self.assertEqual(c.box_columns, [2]) self.assertEqual(c.contents, [ (t2, ('weight', 1, False)), (t1, ('weight', 3, False)), (sf, ('weight', 1, True))]) self.assertEqual(c.focus_position, 1) # focus unchanged c.column_types = [ ('flow', None), # use the old name ('weight', 2), ('fixed', 5)] self.assertEqual(c.column_types, [ ('flow', None), ('weight', 2), ('fixed', 5)]) self.assertEqual(c.contents, [ (t2, ('pack', None, False)), (t1, ('weight', 2, False)), (sf, ('given', 5, True))]) self.assertEqual(c.focus_position, 1) # focus unchanged c.widget_list = [t1] self.assertEqual(len(c.contents), 1) self.assertEqual(c.focus_position, 0) c.widget_list.extend([t2, t1]) self.assertEqual(len(c.contents), 3) self.assertEqual(c.column_types, [ ('flow', None), ('weight', 1), ('weight', 1)]) c.column_types[:] = [('weight', 2)] self.assertEqual(len(c.contents), 1) def test_list_box(self): lb = urwid.ListBox(urwid.SimpleFocusListWalker([])) self.assertEqual(lb.focus, None) self.assertRaises(IndexError, lambda: getattr(lb, 'focus_position')) self.assertRaises(IndexError, lambda: setattr(lb, 'focus_position', None)) self.assertRaises(IndexError, lambda: setattr(lb, 'focus_position', 0)) t1 = urwid.Text(u'one') t2 = urwid.Text(u'two') lb = urwid.ListBox(urwid.SimpleListWalker([t1, t2])) self.assertEqual(lb.focus, t1) self.assertEqual(lb.focus_position, 0) lb.focus_position = 1 self.assertEqual(lb.focus, t2) self.assertEqual(lb.focus_position, 1) lb.focus_position = 0 self.assertRaises(IndexError, lambda: setattr(lb, 'focus_position', -1)) self.assertRaises(IndexError, lambda: setattr(lb, 'focus_position', 2)) def test_grid_flow(self): gf = urwid.GridFlow([], 5, 1, 0, 'left') self.assertEqual(gf.focus, None) self.assertEqual(gf.contents, []) self.assertRaises(IndexError, lambda: getattr(gf, 'focus_position')) self.assertRaises(IndexError, lambda: setattr(gf, 'focus_position', None)) self.assertRaises(IndexError, lambda: setattr(gf, 'focus_position', 0)) self.assertEqual(gf.options(), ('given', 5)) self.assertEqual(gf.options(width_amount=9), ('given', 9)) self.assertRaises(urwid.GridFlowError, lambda: gf.options( 'pack', None)) t1 = urwid.Text(u'one') t2 = urwid.Text(u'two') gf = urwid.GridFlow([t1, t2], 5, 1, 0, 'left') self.assertEqual(gf.focus, t1) self.assertEqual(gf.focus_position, 0) self.assertEqual(gf.contents, [(t1, ('given', 5)), (t2, ('given', 5))]) gf.focus_position = 1 self.assertEqual(gf.focus, t2) self.assertEqual(gf.focus_position, 1) gf.contents.insert(0, (t2, ('given', 5))) self.assertEqual(gf.focus_position, 2) self.assertRaises(urwid.GridFlowError, lambda: gf.contents.append(())) self.assertRaises(urwid.GridFlowError, lambda: gf.contents.insert(1, (t1, ('pack', None)))) gf.focus_position = 0 self.assertRaises(IndexError, lambda: setattr(gf, 'focus_position', -1)) self.assertRaises(IndexError, lambda: setattr(gf, 'focus_position', 3)) # old methods: gf.set_focus(0) self.assertRaises(IndexError, lambda: gf.set_focus(-1)) self.assertRaises(IndexError, lambda: gf.set_focus(3)) gf.set_focus(t1) self.assertEqual(gf.focus_position, 1) self.assertRaises(ValueError, lambda: gf.set_focus('nonexistant')) def test_overlay(self): s1 = urwid.SolidFill(u'1') s2 = urwid.SolidFill(u'2') o = urwid.Overlay(s1, s2, 'center', ('relative', 50), 'middle', ('relative', 50)) self.assertEqual(o.focus, s1) self.assertEqual(o.focus_position, 1) self.assertRaises(IndexError, lambda: setattr(o, 'focus_position', None)) self.assertRaises(IndexError, lambda: setattr(o, 'focus_position', 2)) self.assertEqual(o.contents[0], (s2, urwid.Overlay._DEFAULT_BOTTOM_OPTIONS)) self.assertEqual(o.contents[1], (s1, ( 'center', None, 'relative', 50, None, 0, 0, 'middle', None, 'relative', 50, None, 0, 0))) def test_frame(self): s1 = urwid.SolidFill(u'1') f = urwid.Frame(s1) self.assertEqual(f.focus, s1) self.assertEqual(f.focus_position, 'body') self.assertRaises(IndexError, lambda: setattr(f, 'focus_position', None)) self.assertRaises(IndexError, lambda: setattr(f, 'focus_position', 'header')) t1 = urwid.Text(u'one') t2 = urwid.Text(u'two') t3 = urwid.Text(u'three') f = urwid.Frame(s1, t1, t2, 'header') self.assertEqual(f.focus, t1) self.assertEqual(f.focus_position, 'header') f.focus_position = 'footer' self.assertEqual(f.focus, t2) self.assertEqual(f.focus_position, 'footer') self.assertRaises(IndexError, lambda: setattr(f, 'focus_position', -1)) self.assertRaises(IndexError, lambda: setattr(f, 'focus_position', 2)) del f.contents['footer'] self.assertEqual(f.footer, None) self.assertEqual(f.focus_position, 'body') f.contents.update(footer=(t3, None), header=(t2, None)) self.assertEqual(f.header, t2) self.assertEqual(f.footer, t3) def set1(): f.contents['body'] = t1 self.assertRaises(urwid.FrameError, set1) def set2(): f.contents['body'] = (t1, 'given') self.assertRaises(urwid.FrameError, set2) def test_focus_path(self): # big tree of containers t = urwid.Text(u'x') e = urwid.Edit(u'?') c = urwid.Columns([t, e, t, t]) p = urwid.Pile([t, t, c, t]) a = urwid.AttrMap(p, 'gets ignored') s = urwid.SolidFill(u'/') o = urwid.Overlay(e, s, 'center', 'pack', 'middle', 'pack') lb = urwid.ListBox(urwid.SimpleFocusListWalker([t, a, o, t])) lb.focus_position = 1 g = urwid.GridFlow([t, t, t, t, e, t], 10, 0, 0, 'left') g.focus_position = 4 f = urwid.Frame(lb, header=t, footer=g) self.assertEqual(f.get_focus_path(), ['body', 1, 2, 1]) f.set_focus_path(['footer']) # same as f.focus_position = 'footer' self.assertEqual(f.get_focus_path(), ['footer', 4]) f.set_focus_path(['body', 1, 2, 2]) self.assertEqual(f.get_focus_path(), ['body', 1, 2, 2]) self.assertRaises(IndexError, lambda: f.set_focus_path([0, 1, 2])) self.assertRaises(IndexError, lambda: f.set_focus_path(['body', 2, 2])) f.set_focus_path(['body', 2]) # focus the overlay self.assertEqual(f.get_focus_path(), ['body', 2, 1]) urwid-1.3.1/urwid/tests/test_util.py0000664000175000017500000001472512615524560017111 0ustar ianian00000000000000# -*- coding: utf-8 -*- import unittest import urwid from urwid import util from urwid.compat import B class CalcWidthTest(unittest.TestCase): def wtest(self, desc, s, exp): s = B(s) result = util.calc_width( s, 0, len(s)) assert result==exp, "%s got:%r expected:%r" % (desc, result, exp) def test1(self): util.set_encoding("utf-8") self.wtest("narrow", "hello", 5) self.wtest("wide char", '\xe6\x9b\xbf', 2) self.wtest("invalid", '\xe6', 1) self.wtest("zero width", '\xcc\x80', 0) self.wtest("mixed", 'hello\xe6\x9b\xbf\xe6\x9b\xbf', 9) def test2(self): util.set_encoding("euc-jp") self.wtest("narrow", "hello", 5) self.wtest("wide", "\xA1\xA1\xA1\xA1", 4) self.wtest("invalid", "\xA1", 1) class ConvertDecSpecialTest(unittest.TestCase): def ctest(self, desc, s, exp, expcs): exp = B(exp) util.set_encoding('ascii') c = urwid.Text(s).render((5,)) result = c._text[0] assert result==exp, "%s got:%r expected:%r" % (desc, result, exp) resultcs = c._cs[0] assert resultcs==expcs, "%s got:%r expected:%r" % (desc, resultcs, expcs) def test1(self): self.ctest("no conversion", u"hello", "hello", [(None,5)]) self.ctest("only special", u"£££££", "}}}}}", [("0",5)]) self.ctest("mix left", u"££abc", "}}abc", [("0",2),(None,3)]) self.ctest("mix right", u"abc££", "abc}}", [(None,3),("0",2)]) self.ctest("mix inner", u"a££bc", "a}}bc", [(None,1),("0",2),(None,2)] ) self.ctest("mix well", u"£a£b£", "}a}b}", [("0",1),(None,1),("0",1),(None,1),("0",1)] ) class WithinDoubleByteTest(unittest.TestCase): def setUp(self): urwid.set_encoding("euc-jp") def wtest(self, s, ls, pos, expected, desc): result = util.within_double_byte(B(s), ls, pos) assert result==expected, "%s got:%r expected: %r" % (desc, result, expected) def test1(self): self.wtest("mnopqr",0,2,0,'simple no high bytes') self.wtest("mn\xA1\xA1qr",0,2,1,'simple 1st half') self.wtest("mn\xA1\xA1qr",0,3,2,'simple 2nd half') self.wtest("m\xA1\xA1\xA1\xA1r",0,3,1,'subsequent 1st half') self.wtest("m\xA1\xA1\xA1\xA1r",0,4,2,'subsequent 2nd half') self.wtest("mn\xA1@qr",0,3,2,'simple 2nd half lo') self.wtest("mn\xA1\xA1@r",0,4,0,'subsequent not 2nd half lo') self.wtest("m\xA1\xA1\xA1@r",0,4,2,'subsequent 2nd half lo') def test2(self): self.wtest("\xA1\xA1qr",0,0,1,'begin 1st half') self.wtest("\xA1\xA1qr",0,1,2,'begin 2nd half') self.wtest("\xA1@qr",0,1,2,'begin 2nd half lo') self.wtest("\xA1\xA1\xA1\xA1r",0,2,1,'begin subs. 1st half') self.wtest("\xA1\xA1\xA1\xA1r",0,3,2,'begin subs. 2nd half') self.wtest("\xA1\xA1\xA1@r",0,3,2,'begin subs. 2nd half lo') self.wtest("\xA1@\xA1@r",0,3,2,'begin subs. 2nd half lo lo') self.wtest("@\xA1\xA1@r",0,3,0,'begin subs. not 2nd half lo') def test3(self): self.wtest("abc \xA1\xA1qr",4,4,1,'newline 1st half') self.wtest("abc \xA1\xA1qr",4,5,2,'newline 2nd half') self.wtest("abc \xA1@qr",4,5,2,'newline 2nd half lo') self.wtest("abc \xA1\xA1\xA1\xA1r",4,6,1,'newl subs. 1st half') self.wtest("abc \xA1\xA1\xA1\xA1r",4,7,2,'newl subs. 2nd half') self.wtest("abc \xA1\xA1\xA1@r",4,7,2,'newl subs. 2nd half lo') self.wtest("abc \xA1@\xA1@r",4,7,2,'newl subs. 2nd half lo lo') self.wtest("abc @\xA1\xA1@r",4,7,0,'newl subs. not 2nd half lo') class CalcTextPosTest(unittest.TestCase): def ctptest(self, text, tests): text = B(text) for s,e,p, expected in tests: got = util.calc_text_pos( text, s, e, p ) assert got == expected, "%r got:%r expected:%r" % ((s,e,p), got, expected) def test1(self): text = "hello world out there" tests = [ (0,21,0, (0,0)), (0,21,5, (5,5)), (0,21,21, (21,21)), (0,21,50, (21,21)), (2,15,50, (15,13)), (6,21,0, (6,0)), (6,21,3, (9,3)), ] self.ctptest(text, tests) def test2_wide(self): util.set_encoding("euc-jp") text = "hel\xA1\xA1 world out there" tests = [ (0,21,0, (0,0)), (0,21,4, (3,3)), (2,21,2, (3,1)), (2,21,3, (5,3)), (6,21,0, (6,0)), ] self.ctptest(text, tests) def test3_utf8(self): util.set_encoding("utf-8") text = "hel\xc4\x83 world \xe2\x81\x81 there" tests = [ (0,21,0, (0,0)), (0,21,4, (5,4)), (2,21,1, (3,1)), (2,21,2, (5,2)), (2,21,3, (6,3)), (6,21,7, (15,7)), (6,21,8, (16,8)), ] self.ctptest(text, tests) def test4_utf8(self): util.set_encoding("utf-8") text = "he\xcc\x80llo \xe6\x9b\xbf world" tests = [ (0,15,0, (0,0)), (0,15,1, (1,1)), (0,15,2, (4,2)), (0,15,4, (6,4)), (8,15,0, (8,0)), (8,15,1, (8,0)), (8,15,2, (11,2)), (8,15,5, (14,5)), ] self.ctptest(text, tests) class TagMarkupTest(unittest.TestCase): mytests = [ ("simple one", "simple one", []), (('blue',"john"), "john", [('blue',4)]), (["a ","litt","le list"], "a little list", []), (["mix",('high',[" it ",('ital',"up a")])," little"], "mix it up a little", [(None,3),('high',4),('ital',4)]), ([u"££", u"x££"], u"££x££", []), ([B("\xc2\x80"), B("\xc2\x80")], B("\xc2\x80\xc2\x80"), []), ] def test(self): for input, text, attr in self.mytests: restext,resattr = urwid.decompose_tagmarkup( input ) assert restext == text, "got: %r expected: %r" % (restext, text) assert resattr == attr, "got: %r expected: %r" % (resattr, attr) def test_bad_tuple(self): self.assertRaises(urwid.TagMarkupException, lambda: urwid.decompose_tagmarkup((1,2,3))) def test_bad_type(self): self.assertRaises(urwid.TagMarkupException, lambda: urwid.decompose_tagmarkup(5)) urwid-1.3.1/urwid/tests/test_vterm.py0000664000175000017500000002616712615524560017274 0ustar ianian00000000000000# Urwid terminal emulation widget unit tests # Copyright (C) 2010 aszlig # Copyright (C) 2011 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ import os import sys import unittest from itertools import dropwhile from urwid import vterm from urwid import signals from urwid.compat import B class DummyCommand(object): QUITSTRING = B('|||quit|||') def __init__(self): self.reader, self.writer = os.pipe() def __call__(self): # reset stdout = getattr(sys.stdout, 'buffer', sys.stdout) stdout.write(B('\x1bc')) while True: data = os.read(self.reader, 1024) if self.QUITSTRING == data: break stdout.write(data) stdout.flush() def write(self, data): os.write(self.writer, data) def quit(self): self.write(self.QUITSTRING) class TermTest(unittest.TestCase): def setUp(self): self.command = DummyCommand() self.term = vterm.Terminal(self.command) self.resize(80, 24) def tearDown(self): self.command.quit() def connect_signal(self, signal): self._sig_response = None def _set_signal_response(widget, *args, **kwargs): self._sig_response = (args, kwargs) self._set_signal_response = _set_signal_response signals.connect_signal(self.term, signal, self._set_signal_response) def expect_signal(self, *args, **kwargs): self.assertEqual(self._sig_response, (args, kwargs)) def disconnect_signal(self, signal): signals.disconnect_signal(self.term, signal, self._set_signal_response) def caught_beep(self, obj): self.beeped = True def resize(self, width, height, soft=False): self.termsize = (width, height) if not soft: self.term.render(self.termsize, focus=False) def write(self, data): data = B(data) self.command.write(data.replace(B('\e'), B('\x1b'))) def flush(self): self.write(chr(0x7f)) def read(self, raw=False): self.term.wait_and_feed() rendered = self.term.render(self.termsize, focus=False) if raw: is_empty = lambda c: c == (None, None, B(' ')) content = list(rendered.content()) lines = [list(dropwhile(is_empty, reversed(line))) for line in content] return [list(reversed(line)) for line in lines if len(line)] else: content = rendered.text lines = [line.rstrip() for line in content] return B('\n').join(lines).rstrip() def expect(self, what, desc=None, raw=False): if not isinstance(what, list): what = B(what) got = self.read(raw=raw) if desc is None: desc = '' else: desc += '\n' desc += 'Expected:\n%r\nGot:\n%r' % (what, got) self.assertEqual(got, what, desc) def test_simplestring(self): self.write('hello world') self.expect('hello world') def test_linefeed(self): self.write('hello\x0aworld') self.expect('hello\nworld') def test_linefeed2(self): self.write('aa\b\b\eDbb') self.expect('aa\nbb') def test_carriage_return(self): self.write('hello\x0dworld') self.expect('world') def test_insertlines(self): self.write('\e[0;0flast\e[0;0f\e[10L\e[0;0ffirst\nsecond\n\e[11D') self.expect('first\nsecond\n\n\n\n\n\n\n\n\nlast') def test_deletelines(self): self.write('1\n2\n3\n4\e[2;1f\e[2M') self.expect('1\n4') def test_movement(self): self.write('\e[10;20H11\e[10;0f\e[20C\e[K') self.expect('\n' * 9 + ' ' * 19 + '1') self.write('\e[A\e[B\e[C\e[D\b\e[K') self.expect('') self.write('\e[50A2') self.expect(' ' * 19 + '2') self.write('\b\e[K\e[50B3') self.expect('\n' * 23 + ' ' * 19 + '3') self.write('\b\e[K' + '\eM' * 30 + '\e[100C4') self.expect(' ' * 79 + '4') self.write('\e[100D\e[K5') self.expect('5') def edgewall(self): edgewall = '1-\e[1;%(x)df-2\e[%(y)d;1f3-\e[%(y)d;%(x)df-4\x0d' self.write(edgewall % {'x': self.termsize[0] - 1, 'y': self.termsize[1] - 1}) def test_horizontal_resize(self): self.resize(80, 24) self.edgewall() self.expect('1-' + ' ' * 76 + '-2' + '\n' * 22 + '3-' + ' ' * 76 + '-4') self.resize(78, 24, soft=True) self.flush() self.expect('1-' + '\n' * 22 + '3-') self.resize(80, 24, soft=True) self.flush() self.expect('1-' + '\n' * 22 + '3-') def test_vertical_resize(self): self.resize(80, 24) self.edgewall() self.expect('1-' + ' ' * 76 + '-2' + '\n' * 22 + '3-' + ' ' * 76 + '-4') for y in xrange(23, 1, -1): self.resize(80, y, soft=True) self.write('\e[%df\e[J3-\e[%d;%df-4' % (y, y, 79)) desc = "try to rescale to 80x%d." % y self.expect('\n' * (y - 2) + '3-' + ' ' * 76 + '-4', desc) self.resize(80, 24, soft=True) self.flush() self.expect('1-' + ' ' * 76 + '-2' + '\n' * 22 + '3-' + ' ' * 76 + '-4') def write_movements(self, arg): fmt = 'XXX\n\e[faaa\e[Bccc\e[Addd\e[Bfff\e[Cbbb\e[A\e[Deee' self.write(fmt.replace('\e[', '\e['+arg)) def test_defargs(self): self.write_movements('') self.expect('aaa ddd eee\n ccc fff bbb') def test_nullargs(self): self.write_movements('0') self.expect('aaa ddd eee\n ccc fff bbb') def test_erase_line(self): self.write('1234567890\e[5D\e[K\n1234567890\e[5D\e[1K\naaaaaaaaaaaaaaa\e[2Ka') self.expect('12345\n 7890\n a') def test_erase_display(self): self.write('1234567890\e[5D\e[Ja') self.expect('12345a') self.write('98765\e[8D\e[1Jx') self.expect(' x5a98765') def test_scrolling_region_simple(self): self.write('\e[10;20r\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\e[faa') self.expect('aa' + '\n' * 9 + '2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12') def test_scrolling_region_reverse(self): self.write('\e[2J\e[1;2r\e[5Baaa\r\eM\eM\eMbbb\nXXX') self.expect('\n\nbbb\nXXX\n\naaa') def test_scrolling_region_move(self): self.write('\e[10;20r\e[2J\e[10Bfoo\rbar\rblah\rmooh\r\e[10Aone\r\eM\eMtwo\r\eM\eMthree\r\eM\eMa') self.expect('ahree\n\n\n\n\n\n\n\n\n\nmooh') def test_scrolling_twice(self): self.write('\e[?6h\e[10;20r\e[2;5rtest') self.expect('\ntest') def test_cursor_scrolling_region(self): self.write('\e[?6h\e[10;20r\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\e[faa') self.expect('\n' * 9 + 'aa\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12') def test_relative_region_jump(self): self.write('\e[21H---\e[10;20r\e[?6h\e[18Htest') self.expect('\n' * 19 + 'test\n---') def test_set_multiple_modes(self): self.write('\e[?6;5htest') self.expect('test') self.assertTrue(self.term.term_modes.constrain_scrolling) self.assertTrue(self.term.term_modes.reverse_video) self.write('\e[?6;5l') self.expect('test') self.assertFalse(self.term.term_modes.constrain_scrolling) self.assertFalse(self.term.term_modes.reverse_video) def test_wrap_simple(self): self.write('\e[?7h\e[1;%dHtt' % self.term.width) self.expect(' ' * (self.term.width - 1) + 't\nt') def test_wrap_backspace_tab(self): self.write('\e[?7h\e[1;%dHt\b\b\t\ta' % self.term.width) self.expect(' ' * (self.term.width - 1) + 'a') def test_cursor_visibility(self): self.write('\e[?25linvisible') self.expect('invisible') self.assertEqual(self.term.term.cursor, None) self.write('\rvisible\e[?25h\e[K') self.expect('visible') self.assertNotEqual(self.term.term.cursor, None) def test_get_utf8_len(self): length = self.term.term.get_utf8_len(int("11110000", 2)) self.assertEqual(length, 3) length = self.term.term.get_utf8_len(int("11000000", 2)) self.assertEqual(length, 1) length = self.term.term.get_utf8_len(int("11111101", 2)) self.assertEqual(length, 5) def test_encoding_unicode(self): vterm.util._target_encoding = 'utf-8' self.write('\e%G\xe2\x80\x94') self.expect('\xe2\x80\x94') def test_encoding_unicode_ascii(self): vterm.util._target_encoding = 'ascii' self.write('\e%G\xe2\x80\x94') self.expect('?') def test_encoding_wrong_unicode(self): vterm.util._target_encoding = 'utf-8' self.write('\e%G\xc0\x99') self.expect('') def test_encoding_vt100_graphics(self): vterm.util._target_encoding = 'ascii' self.write('\e)0\e(0\x0fg\x0eg\e)Bn\e)0g\e)B\e(B\x0fn') self.expect([[ (None, '0', B('g')), (None, '0', B('g')), (None, None, B('n')), (None, '0', B('g')), (None, None, B('n')) ]], raw=True) def test_ibmpc_mapping(self): vterm.util._target_encoding = 'ascii' self.write('\e[11m\x18\e[10m\x18') self.expect([[(None, 'U', B('\x18'))]], raw=True) self.write('\ec\e)U\x0e\x18\x0f\e[3h\x18\e[3l\x18') self.expect([[(None, None, B('\x18'))]], raw=True) self.write('\ec\e[11m\xdb\x18\e[10m\xdb') self.expect([[ (None, 'U', B('\xdb')), (None, 'U', B('\x18')), (None, None, B('\xdb')) ]], raw=True) def test_set_title(self): self._the_title = None def _change_title(widget, title): self._the_title = title self.connect_signal('title') self.write('\e]666parsed right?\e\\te\e]0;test title\007st1') self.expect('test1') self.expect_signal(B('test title')) self.write('\e]3;stupid title\e\\\e[0G\e[2Ktest2') self.expect('test2') self.expect_signal(B('stupid title')) self.disconnect_signal('title') def test_set_leds(self): self.connect_signal('leds') self.write('\e[0qtest1') self.expect('test1') self.expect_signal('clear') self.write('\e[3q\e[H\e[Ktest2') self.expect('test2') self.expect_signal('caps_lock') self.disconnect_signal('leds') urwid-1.3.1/urwid/tests/test_graphics.py0000664000175000017500000000752512615524560017734 0ustar ianian00000000000000import unittest from urwid import graphics from urwid.compat import B import urwid class LineBoxTest(unittest.TestCase): def border(self, tl, t, tr, l, r, bl, b, br): return [bytes().join([tl, t, tr]), bytes().join([l, B(" "), r]), bytes().join([bl, b, br]),] def test_linebox_border(self): urwid.set_encoding("utf-8") t = urwid.Text("") l = urwid.LineBox(t).render((3,)).text # default self.assertEqual(l, self.border(B("\xe2\x94\x8c"), B("\xe2\x94\x80"), B("\xe2\x94\x90"), B("\xe2\x94\x82"), B("\xe2\x94\x82"), B("\xe2\x94\x94"), B("\xe2\x94\x80"), B("\xe2\x94\x98"))) nums = [B(str(n)) for n in range(8)] b = dict(zip(["tlcorner", "tline", "trcorner", "lline", "rline", "blcorner", "bline", "brcorner"], nums)) l = urwid.LineBox(t, **b).render((3,)).text self.assertEqual(l, self.border(*nums)) class BarGraphTest(unittest.TestCase): def bgtest(self, desc, data, top, widths, maxrow, exp ): rval = graphics.calculate_bargraph_display(data,top,widths,maxrow) assert rval == exp, "%s expected %r, got %r"%(desc,exp,rval) def test1(self): self.bgtest('simplest',[[0]],5,[1],1, [(1,[(0,1)])] ) self.bgtest('simpler',[[0],[0]],5,[1,2],5, [(5,[(0,3)])] ) self.bgtest('simple',[[5]],5,[1],1, [(1,[(1,1)])] ) self.bgtest('2col-1',[[2],[0]],5,[1,2],5, [(3,[(0,3)]), (2,[(1,1),(0,2)]) ] ) self.bgtest('2col-2',[[0],[2]],5,[1,2],5, [(3,[(0,3)]), (2,[(0,1),(1,2)]) ] ) self.bgtest('2col-3',[[2],[3]],5,[1,2],5, [(2,[(0,3)]), (1,[(0,1),(1,2)]), (2,[(1,3)]) ] ) self.bgtest('3col-1',[[5],[3],[0]],5,[2,1,1],5, [(2,[(1,2),(0,2)]), (3,[(1,3),(0,1)]) ] ) self.bgtest('3col-2',[[4],[4],[4]],5,[2,1,1],5, [(1,[(0,4)]), (4,[(1,4)]) ] ) self.bgtest('3col-3',[[1],[2],[3]],5,[2,1,1],5, [(2,[(0,4)]), (1,[(0,3),(1,1)]), (1,[(0,2),(1,2)]), (1,[(1,4)]) ] ) self.bgtest('3col-4',[[4],[2],[4]],5,[1,2,1],5, [(1,[(0,4)]), (2,[(1,1),(0,2),(1,1)]), (2,[(1,4)]) ] ) def test2(self): self.bgtest('simple1a',[[2,0],[2,1]],2,[1,1],2, [(1,[(1,2)]),(1,[(1,1),(2,1)]) ] ) self.bgtest('simple1b',[[2,1],[2,0]],2,[1,1],2, [(1,[(1,2)]),(1,[(2,1),(1,1)]) ] ) self.bgtest('cross1a',[[2,2],[1,2]],2,[1,1],2, [(2,[(2,2)]) ] ) self.bgtest('cross1b',[[1,2],[2,2]],2,[1,1],2, [(2,[(2,2)]) ] ) self.bgtest('mix1a',[[3,2,1],[2,2,2],[1,2,3]],3,[1,1,1],3, [(1,[(1,1),(0,1),(3,1)]),(1,[(2,1),(3,2)]), (1,[(3,3)]) ] ) self.bgtest('mix1b',[[1,2,3],[2,2,2],[3,2,1]],3,[1,1,1],3, [(1,[(3,1),(0,1),(1,1)]),(1,[(3,2),(2,1)]), (1,[(3,3)]) ] ) class SmoothBarGraphTest(unittest.TestCase): def sbgtest(self, desc, data, top, exp ): urwid.set_encoding('utf-8') g = urwid.BarGraph( ['black','red','blue'], None, {(1,0):'red/black', (2,1):'blue/red'}) g.set_data( data, top ) rval = g.calculate_display((5,3)) assert rval == exp, "%s expected %r, got %r"%(desc,exp,rval) def test1(self): self.sbgtest('simple', [[3]], 5, [(1, [(0, 5)]), (1, [((1, 0, 6), 5)]), (1, [(1, 5)])] ) self.sbgtest('boring', [[4,2]], 6, [(1, [(0, 5)]), (1, [(1, 5)]), (1, [(2,5)]) ] ) self.sbgtest('two', [[4],[2]], 6, [(1, [(0, 5)]), (1, [(1, 3), (0, 2)]), (1, [(1, 5)]) ] ) self.sbgtest('twos', [[3],[4]], 6, [(1, [(0, 5)]), (1, [((1,0,4), 3), (1, 2)]), (1, [(1,5)]) ] ) self.sbgtest('twof', [[4],[3]], 6, [(1, [(0, 5)]), (1, [(1,3), ((1,0,4), 2)]), (1, [(1,5)]) ] ) urwid-1.3.1/urwid/tests/test_listbox.py0000664000175000017500000006643012615524560017620 0ustar ianian00000000000000import unittest from urwid.compat import B from urwid.tests.util import SelectableText import urwid class ListBoxCalculateVisibleTest(unittest.TestCase): def cvtest(self, desc, body, focus, offset_rows, inset_fraction, exp_offset_inset, exp_cur ): lbox = urwid.ListBox(body) lbox.body.set_focus( focus ) lbox.offset_rows = offset_rows lbox.inset_fraction = inset_fraction middle, top, bottom = lbox.calculate_visible((4,5),focus=1) offset_inset, focus_widget, focus_pos, _ign, cursor = middle if cursor is not None: x, y = cursor y += offset_inset cursor = x, y assert offset_inset == exp_offset_inset, "%s got: %r expected: %r" %(desc,offset_inset,exp_offset_inset) assert cursor == exp_cur, "%s (cursor) got: %r expected: %r" %(desc,cursor,exp_cur) def test1_simple(self): T = urwid.Text l = [T(""),T(""),T("\n"),T("\n\n"),T("\n"),T(""),T("")] self.cvtest( "simple top position", l, 3, 0, (0,1), 0, None ) self.cvtest( "simple middle position", l, 3, 1, (0,1), 1, None ) self.cvtest( "simple bottom postion", l, 3, 2, (0,1), 2, None ) self.cvtest( "straddle top edge", l, 3, 0, (1,2), -1, None ) self.cvtest( "straddle bottom edge", l, 3, 4, (0,1), 4, None ) self.cvtest( "off bottom edge", l, 3, 5, (0,1), 4, None ) self.cvtest( "way off bottom edge", l, 3, 100, (0,1), 4, None ) self.cvtest( "gap at top", l, 0, 2, (0,1), 0, None ) self.cvtest( "gap at top and off bottom edge", l, 2, 5, (0,1), 2, None ) self.cvtest( "gap at bottom", l, 6, 1, (0,1), 4, None ) self.cvtest( "gap at bottom and straddling top edge", l, 4, 0, (1,2), 1, None ) self.cvtest( "gap at bottom cannot completely fill", [T(""),T(""),T("")], 1, 0, (0,1), 1, None ) self.cvtest( "gap at top and bottom", [T(""),T(""),T("")], 1, 2, (0,1), 1, None ) def test2_cursor(self): T, E = urwid.Text, urwid.Edit l1 = [T(""),T(""),T("\n"),E("","\n\nX"),T("\n"),T(""),T("")] l2 = [T(""),T(""),T("\n"),E("","YY\n\n"),T("\n"),T(""),T("")] l2[3].set_edit_pos(2) self.cvtest( "plain cursor in view", l1, 3, 1, (0,1), 1, (1,3) ) self.cvtest( "cursor off top", l2, 3, 0, (1,3), 0, (2, 0) ) self.cvtest( "cursor further off top", l2, 3, 0, (2,3), 0, (2, 0) ) self.cvtest( "cursor off bottom", l1, 3, 3, (0,1), 2, (1, 4) ) self.cvtest( "cursor way off bottom", l1, 3, 100, (0,1), 2, (1, 4) ) class ListBoxChangeFocusTest(unittest.TestCase): def cftest(self, desc, body, pos, offset_inset, coming_from, cursor, snap_rows, exp_offset_rows, exp_inset_fraction, exp_cur ): lbox = urwid.ListBox(body) lbox.change_focus( (4,5), pos, offset_inset, coming_from, cursor, snap_rows ) exp = exp_offset_rows, exp_inset_fraction act = lbox.offset_rows, lbox.inset_fraction cursor = None focus_widget, focus_pos = lbox.body.get_focus() if focus_widget.selectable(): if hasattr(focus_widget,'get_cursor_coords'): cursor=focus_widget.get_cursor_coords((4,)) assert act == exp, "%s got: %s expected: %s" %(desc, act, exp) assert cursor == exp_cur, "%s (cursor) got: %r expected: %r" %(desc,cursor,exp_cur) def test1unselectable(self): T = urwid.Text l = [T("\n"),T("\n\n"),T("\n\n"),T("\n\n"),T("\n")] self.cftest( "simple unselectable", l, 2, 0, None, None, None, 0, (0,1), None ) self.cftest( "unselectable", l, 2, 1, None, None, None, 1, (0,1), None ) self.cftest( "unselectable off top", l, 2, -2, None, None, None, 0, (2,3), None ) self.cftest( "unselectable off bottom", l, 3, 2, None, None, None, 2, (0,1), None ) def test2selectable(self): T, S = urwid.Text, SelectableText l = [T("\n"),T("\n\n"),S("\n\n"),T("\n\n"),T("\n")] self.cftest( "simple selectable", l, 2, 0, None, None, None, 0, (0,1), None ) self.cftest( "selectable", l, 2, 1, None, None, None, 1, (0,1), None ) self.cftest( "selectable at top", l, 2, 0, 'below', None, None, 0, (0,1), None ) self.cftest( "selectable at bottom", l, 2, 2, 'above', None, None, 2, (0,1), None ) self.cftest( "selectable off top snap", l, 2, -1, 'below', None, None, 0, (0,1), None ) self.cftest( "selectable off bottom snap", l, 2, 3, 'above', None, None, 2, (0,1), None ) self.cftest( "selectable off top no snap", l, 2, -1, 'above', None, None, 0, (1,3), None ) self.cftest( "selectable off bottom no snap", l, 2, 3, 'below', None, None, 3, (0,1), None ) def test3large_selectable(self): T, S = urwid.Text, SelectableText l = [T("\n"),S("\n\n\n\n\n\n"),T("\n")] self.cftest( "large selectable no snap", l, 1, -1, None, None, None, 0, (1,7), None ) self.cftest( "large selectable snap up", l, 1, -2, 'below', None, None, 0, (0,1), None ) self.cftest( "large selectable snap up2", l, 1, -2, 'below', None, 2, 0, (0,1), None ) self.cftest( "large selectable almost snap up", l, 1, -2, 'below', None, 1, 0, (2,7), None ) self.cftest( "large selectable snap down", l, 1, 0, 'above', None, None, 0, (2,7), None ) self.cftest( "large selectable snap down2", l, 1, 0, 'above', None, 2, 0, (2,7), None ) self.cftest( "large selectable almost snap down", l, 1, 0, 'above', None, 1, 0, (0,1), None ) m = [T("\n\n\n\n"), S("\n\n\n\n\n"), T("\n\n\n\n")] self.cftest( "large selectable outside view down", m, 1, 4, 'above', None, None, 0, (0,1), None ) self.cftest( "large selectable outside view up", m, 1, -5, 'below', None, None, 0, (1,6), None ) def test4cursor(self): T,E = urwid.Text, urwid.Edit #... def test5set_focus_valign(self): T,E = urwid.Text, urwid.Edit lbox = urwid.ListBox(urwid.SimpleFocusListWalker([ T(''), T('')])) lbox.set_focus_valign('middle') # TODO: actually test the result class ListBoxRenderTest(unittest.TestCase): def ltest(self,desc,body,focus,offset_inset_rows,exp_text,exp_cur): exp_text = [B(t) for t in exp_text] lbox = urwid.ListBox(body) lbox.body.set_focus( focus ) lbox.shift_focus((4,10), offset_inset_rows ) canvas = lbox.render( (4,5), focus=1 ) text = [bytes().join([t for at, cs, t in ln]) for ln in canvas.content()] cursor = canvas.cursor assert text == exp_text, "%s (text) got: %r expected: %r" %(desc,text,exp_text) assert cursor == exp_cur, "%s (cursor) got: %r expected: %r" %(desc,cursor,exp_cur) def test1_simple(self): T = urwid.Text self.ltest( "simple one text item render", [T("1\n2")], 0, 0, ["1 ","2 "," "," "," "],None) self.ltest( "simple multi text item render off bottom", [T("1"),T("2"),T("3\n4"),T("5"),T("6")], 2, 2, ["1 ","2 ","3 ","4 ","5 "],None) self.ltest( "simple multi text item render off top", [T("1"),T("2"),T("3\n4"),T("5"),T("6")], 2, 1, ["2 ","3 ","4 ","5 ","6 "],None) def test2_trim(self): T = urwid.Text self.ltest( "trim unfocused bottom", [T("1\n2"),T("3\n4"),T("5\n6")], 1, 2, ["1 ","2 ","3 ","4 ","5 "],None) self.ltest( "trim unfocused top", [T("1\n2"),T("3\n4"),T("5\n6")], 1, 1, ["2 ","3 ","4 ","5 ","6 "],None) self.ltest( "trim none full focus", [T("1\n2\n3\n4\n5")], 0, 0, ["1 ","2 ","3 ","4 ","5 "],None) self.ltest( "trim focus bottom", [T("1\n2\n3\n4\n5\n6")], 0, 0, ["1 ","2 ","3 ","4 ","5 "],None) self.ltest( "trim focus top", [T("1\n2\n3\n4\n5\n6")], 0, -1, ["2 ","3 ","4 ","5 ","6 "],None) self.ltest( "trim focus top and bottom", [T("1\n2\n3\n4\n5\n6\n7")], 0, -1, ["2 ","3 ","4 ","5 ","6 "],None) def test3_shift(self): T,E = urwid.Text, urwid.Edit self.ltest( "shift up one fit", [T("1\n2"),T("3"),T("4"),T("5"),T("6")], 4, 5, ["2 ","3 ","4 ","5 ","6 "],None) e = E("","ab\nc",1) e.set_edit_pos( 2 ) self.ltest( "shift down one cursor over edge", [e,T("3"),T("4"),T("5\n6")], 0, -1, ["ab ","c ","3 ","4 ","5 "], (2,0)) self.ltest( "shift up one cursor over edge", [T("1\n2"),T("3"),T("4"),E("","d\ne")], 3, 4, ["2 ","3 ","4 ","d ","e "], (1,4)) self.ltest( "shift none cursor top focus over edge", [E("","ab\n"),T("3"),T("4"),T("5\n6")], 0, -1, [" ","3 ","4 ","5 ","6 "], (0,0)) e = E("","abc\nd") e.set_edit_pos( 3 ) self.ltest( "shift none cursor bottom focus over edge", [T("1\n2"),T("3"),T("4"),e], 3, 4, ["1 ","2 ","3 ","4 ","abc "], (3,4)) def test4_really_large_contents(self): T,E = urwid.Text, urwid.Edit self.ltest("really large edit", [T(u"hello"*100)], 0, 0, ["hell","ohel","lohe","lloh","ello"], None) self.ltest("really large edit", [E(u"", u"hello"*100)], 0, 0, ["hell","ohel","lohe","lloh","llo "], (3,4)) class ListBoxKeypressTest(unittest.TestCase): def ktest(self, desc, key, body, focus, offset_inset, exp_focus, exp_offset_inset, exp_cur, lbox = None): if lbox is None: lbox = urwid.ListBox(body) lbox.body.set_focus( focus ) lbox.shift_focus((4,10), offset_inset ) ret_key = lbox.keypress((4,5),key) middle, top, bottom = lbox.calculate_visible((4,5),focus=1) offset_inset, focus_widget, focus_pos, _ign, cursor = middle if cursor is not None: x, y = cursor y += offset_inset cursor = x, y exp = exp_focus, exp_offset_inset act = focus_pos, offset_inset assert act == exp, "%s got: %r expected: %r" %(desc,act,exp) assert cursor == exp_cur, "%s (cursor) got: %r expected: %r" %(desc,cursor,exp_cur) return ret_key,lbox def test1_up(self): T,S,E = urwid.Text, SelectableText, urwid.Edit self.ktest( "direct selectable both visible", 'up', [S(""),S("")], 1, 1, 0, 0, None ) self.ktest( "selectable skip one all visible", 'up', [S(""),T(""),S("")], 2, 2, 0, 0, None ) key,lbox = self.ktest( "nothing above no scroll", 'up', [S("")], 0, 0, 0, 0, None ) assert key == 'up' key, lbox = self.ktest( "unselectable above no scroll", 'up', [T(""),T(""),S("")], 2, 2, 2, 2, None ) assert key == 'up' self.ktest( "unselectable above scroll 1", 'up', [T(""),S(""),T("\n\n\n")], 1, 0, 1, 1, None ) self.ktest( "selectable above scroll 1", 'up', [S(""),S(""),T("\n\n\n")], 1, 0, 0, 0, None ) self.ktest( "selectable above too far", 'up', [S(""),T(""),S(""),T("\n\n\n")], 2, 0, 2, 1, None ) self.ktest( "selectable above skip 1 scroll 1", 'up', [S(""),T(""),S(""),T("\n\n\n")], 2, 1, 0, 0, None ) self.ktest( "tall selectable above scroll 2", 'up', [S(""),S("\n"),S(""),T("\n\n\n")], 2, 0, 1, 0, None ) self.ktest( "very tall selectable above scroll 5", 'up', [S(""),S("\n\n\n\n"),S(""),T("\n\n\n\n")], 2, 0, 1, 0, None ) self.ktest( "very tall selected scroll within 1", 'up', [S(""),S("\n\n\n\n\n")], 1, -1, 1, 0, None ) self.ktest( "edit above pass cursor", 'up', [E("","abc"),E("","de")], 1, 1, 0, 0, (2, 0) ) key,lbox = self.ktest( "edit too far above pass cursor A", 'up', [E("","abc"),T("\n\n\n\n"),E("","de")], 2, 4, 1, 0, None ) self.ktest( "edit too far above pass cursor B", 'up', None, None, None, 0, 0, (2,0), lbox ) self.ktest( "within focus cursor made not visible", 'up', [T("\n\n\n"),E("hi\n","ab")], 1, 3, 0, 0, None ) self.ktest( "within focus cursor made not visible (2)", 'up', [T("\n\n\n\n"),E("hi\n","ab")], 1, 3, 0, -1, None ) self.ktest( "force focus unselectable" , 'up', [T("\n\n\n\n"),S("")], 1, 4, 0, 0, None ) self.ktest( "pathological cursor widget", 'up', [T("\n"),E("\n\n\n\n\n","a")], 1, 4, 0, -1, None ) self.ktest( "unselectable to unselectable", 'up', [T(""),T(""),T(""),T(""),T(""),T(""),T("")], 2, 0, 1, 0, None ) self.ktest( "unselectable over edge to same", 'up', [T(""),T("12\n34"),T(""),T(""),T(""),T("")],1,-1, 1, 0, None ) key,lbox = self.ktest( "edit short between pass cursor A", 'up', [E("","abcd"),E("","a"),E("","def")], 2, 2, 1, 1, (1,1) ) self.ktest( "edit short between pass cursor B", 'up', None, None, None, 0, 0, (3,0), lbox ) e = E("","\n\n\n\n\n") e.set_edit_pos(1) key,lbox = self.ktest( "edit cursor force scroll", 'up', [e], 0, -1, 0, 0, (0,0) ) assert lbox.inset_fraction[0] == 0 def test2_down(self): T,S,E = urwid.Text, SelectableText, urwid.Edit self.ktest( "direct selectable both visible", 'down', [S(""),S("")], 0, 0, 1, 1, None ) self.ktest( "selectable skip one all visible", 'down', [S(""),T(""),S("")], 0, 0, 2, 2, None ) key,lbox = self.ktest( "nothing below no scroll", 'down', [S("")], 0, 0, 0, 0, None ) assert key == 'down' key, lbox = self.ktest( "unselectable below no scroll", 'down', [S(""),T(""),T("")], 0, 0, 0, 0, None ) assert key == 'down' self.ktest( "unselectable below scroll 1", 'down', [T("\n\n\n"),S(""),T("")], 1, 4, 1, 3, None ) self.ktest( "selectable below scroll 1", 'down', [T("\n\n\n"),S(""),S("")], 1, 4, 2, 4, None ) self.ktest( "selectable below too far", 'down', [T("\n\n\n"),S(""),T(""),S("")], 1, 4, 1, 3, None ) self.ktest( "selectable below skip 1 scroll 1", 'down', [T("\n\n\n"),S(""),T(""),S("")], 1, 3, 3, 4, None ) self.ktest( "tall selectable below scroll 2", 'down', [T("\n\n\n"),S(""),S("\n"),S("")], 1, 4, 2, 3, None ) self.ktest( "very tall selectable below scroll 5", 'down', [T("\n\n\n\n"),S(""),S("\n\n\n\n"),S("")], 1, 4, 2, 0, None ) self.ktest( "very tall selected scroll within 1", 'down', [S("\n\n\n\n\n"),S("")], 0, 0, 0, -1, None ) self.ktest( "edit below pass cursor", 'down', [E("","de"),E("","abc")], 0, 0, 1, 1, (2, 1) ) key,lbox=self.ktest( "edit too far below pass cursor A", 'down', [E("","de"),T("\n\n\n\n"),E("","abc")], 0, 0, 1, 0, None ) self.ktest( "edit too far below pass cursor B", 'down', None, None, None, 2, 4, (2,4), lbox ) odd_e = E("","hi\nab") odd_e.set_edit_pos( 2 ) # disble cursor movement in odd_e object odd_e.move_cursor_to_coords = lambda s,c,xy: 0 self.ktest( "within focus cursor made not visible", 'down', [odd_e,T("\n\n\n\n")], 0, 0, 1, 1, None ) self.ktest( "within focus cursor made not visible (2)", 'down', [odd_e,T("\n\n\n\n"),], 0, 0, 1, 1, None ) self.ktest( "force focus unselectable" , 'down', [S(""),T("\n\n\n\n")], 0, 0, 1, 0, None ) odd_e.set_edit_text( "hi\n\n\n\n\n" ) self.ktest( "pathological cursor widget", 'down', [odd_e,T("\n")], 0, 0, 1, 4, None ) self.ktest( "unselectable to unselectable", 'down', [T(""),T(""),T(""),T(""),T(""),T(""),T("")], 4, 4, 5, 4, None ) self.ktest( "unselectable over edge to same", 'down', [T(""),T(""),T(""),T(""),T("12\n34"),T("")],4,4, 4, 3, None ) key,lbox=self.ktest( "edit short between pass cursor A", 'down', [E("","abc"),E("","a"),E("","defg")], 0, 0, 1, 1, (1,1) ) self.ktest( "edit short between pass cursor B", 'down', None, None, None, 2, 2, (3,2), lbox ) e = E("","\n\n\n\n\n") e.set_edit_pos(4) key,lbox = self.ktest( "edit cursor force scroll", 'down', [e], 0, 0, 0, -1, (0,4) ) assert lbox.inset_fraction[0] == 1 def test3_page_up(self): T,S,E = urwid.Text, SelectableText, urwid.Edit self.ktest( "unselectable aligned to aligned", 'page up', [T(""),T("\n"),T("\n\n"),T(""),T("\n"),T("\n\n")], 3, 0, 1, 0, None ) self.ktest( "unselectable unaligned to aligned", 'page up', [T(""),T("\n"),T("\n"),T("\n"),T("\n"),T("\n\n")], 3,-1, 1, 0, None ) self.ktest( "selectable to unselectable", 'page up', [T(""),T("\n"),T("\n"),T("\n"),S("\n"),T("\n\n")], 4, 1, 1, -1, None ) self.ktest( "selectable to cut off selectable", 'page up', [S("\n\n"),T("\n"),T("\n"),S("\n"),T("\n\n")], 3, 1, 0, -1, None ) self.ktest( "seletable to selectable", 'page up', [T("\n\n"),S("\n"),T("\n"),S("\n"),T("\n\n")], 3, 1, 1, 1, None ) self.ktest( "within very long selectable", 'page up', [S(""),S("\n\n\n\n\n\n\n\n"),T("\n")], 1, -6, 1, -1, None ) e = E("","\n\nab\n\n\n\n\ncd\n") e.set_edit_pos(11) self.ktest( "within very long cursor widget", 'page up', [S(""),e,T("\n")], 1, -6, 1, -2, (2, 0) ) self.ktest( "pathological cursor widget", 'page up', [T(""),E("\n\n\n\n\n\n\n\n","ab"),T("")], 1, -5, 0, 0, None ) e = E("","\nab\n\n\n\n\ncd\n") e.set_edit_pos(10) self.ktest( "very long cursor widget snap", 'page up', [T(""),e,T("\n")], 1, -5, 1, 0, (2, 1) ) self.ktest( "slight scroll selectable", 'page up', [T("\n"),S("\n"),T(""),S(""),T("\n\n\n"),S("")], 5, 4, 3, 0, None ) self.ktest( "scroll into snap region", 'page up', [T("\n"),S("\n"),T(""),T(""),T("\n\n\n"),S("")], 5, 4, 1, 0, None ) self.ktest( "mid scroll short", 'page up', [T("\n"),T(""),T(""),S(""),T(""),T("\n"),S(""),T("\n")], 6, 2, 3, 1, None ) self.ktest( "mid scroll long", 'page up', [T("\n"),S(""),T(""),S(""),T(""),T("\n"),S(""),T("\n")], 6, 2, 1, 0, None ) self.ktest( "mid scroll perfect", 'page up', [T("\n"),S(""),S(""),S(""),T(""),T("\n"),S(""),T("\n")], 6, 2, 2, 0, None ) self.ktest( "cursor move up fail short", 'page up', [T("\n"),T("\n"),E("","\nab"),T(""),T("")], 2, 1, 2, 4, (0, 4) ) self.ktest( "cursor force fail short", 'page up', [T("\n"),T("\n"),E("\n","ab"),T(""),T("")], 2, 1, 0, 0, None ) odd_e = E("","hi\nab") odd_e.set_edit_pos( 2 ) # disble cursor movement in odd_e object odd_e.move_cursor_to_coords = lambda s,c,xy: 0 self.ktest( "cursor force fail long", 'page up', [odd_e,T("\n"),T("\n"),T("\n"),S(""),T("\n")], 4, 2, 1, -1, None ) self.ktest( "prefer not cut off", 'page up', [S("\n"),T("\n"),S(""),T("\n\n"),S(""),T("\n")], 4, 2, 2, 1, None ) self.ktest( "allow cut off", 'page up', [S("\n"),T("\n"),T(""),T("\n\n"),S(""),T("\n")], 4, 2, 0, -1, None ) self.ktest( "at top fail", 'page up', [T("\n\n"),T("\n"),T("\n\n\n")], 0, 0, 0, 0, None ) self.ktest( "all visible fail", 'page up', [T("a"),T("\n")], 0, 0, 0, 0, None ) self.ktest( "current ok fail", 'page up', [T("\n\n"),S("hi")], 1, 3, 1, 3, None ) self.ktest( "all visible choose top selectable", 'page up', [T(""),S("a"),S("b"),S("c")], 3, 3, 1, 1, None ) self.ktest( "bring in edge choose top", 'page up', [S("b"),T("-"),S("-"),T("c"),S("d"),T("-")],4,3, 0, 0, None ) self.ktest( "bring in edge choose top selectable", 'page up', [T("b"),S("-"),S("-"),T("c"),S("d"),T("-")],4,3, 1, 1, None ) def test4_page_down(self): T,S,E = urwid.Text, SelectableText, urwid.Edit self.ktest( "unselectable aligned to aligned", 'page down', [T("\n\n"),T("\n"),T(""),T("\n\n"),T("\n"),T("")], 2, 4, 4, 3, None ) self.ktest( "unselectable unaligned to aligned", 'page down', [T("\n\n"),T("\n"),T("\n"),T("\n"),T("\n"),T("")], 2, 4, 4, 3, None ) self.ktest( "selectable to unselectable", 'page down', [T("\n\n"),S("\n"),T("\n"),T("\n"),T("\n"),T("")], 1, 2, 4, 4, None ) self.ktest( "selectable to cut off selectable", 'page down', [T("\n\n"),S("\n"),T("\n"),T("\n"),S("\n\n")], 1, 2, 4, 3, None ) self.ktest( "seletable to selectable", 'page down', [T("\n\n"),S("\n"),T("\n"),S("\n"),T("\n\n")], 1, 1, 3, 2, None ) self.ktest( "within very long selectable", 'page down', [T("\n"),S("\n\n\n\n\n\n\n\n"),S("")], 1, 2, 1, -3, None ) e = E("","\nab\n\n\n\n\ncd\n\n") e.set_edit_pos(2) self.ktest( "within very long cursor widget", 'page down', [T("\n"),e,S("")], 1, 2, 1, -2, (1, 4) ) odd_e = E("","ab\n\n\n\n\n\n\n\n\n") odd_e.set_edit_pos( 1 ) # disble cursor movement in odd_e object odd_e.move_cursor_to_coords = lambda s,c,xy: 0 self.ktest( "pathological cursor widget", 'page down', [T(""),odd_e,T("")], 1, 1, 2, 4, None ) e = E("","\nab\n\n\n\n\ncd\n") e.set_edit_pos(2) self.ktest( "very long cursor widget snap", 'page down', [T("\n"),e,T("")], 1, 2, 1, -3, (1, 3) ) self.ktest( "slight scroll selectable", 'page down', [S(""),T("\n\n\n"),S(""),T(""),S("\n"),T("\n")], 0, 0, 2, 4, None ) self.ktest( "scroll into snap region", 'page down', [S(""),T("\n\n\n"),T(""),T(""),S("\n"),T("\n")], 0, 0, 4, 3, None ) self.ktest( "mid scroll short", 'page down', [T("\n"),S(""),T("\n"),T(""),S(""),T(""),T(""),T("\n")], 1, 2, 4, 3, None ) self.ktest( "mid scroll long", 'page down', [T("\n"),S(""),T("\n"),T(""),S(""),T(""),S(""),T("\n")], 1, 2, 6, 4, None ) self.ktest( "mid scroll perfect", 'page down', [T("\n"),S(""),T("\n"),T(""),S(""),S(""),S(""),T("\n")], 1, 2, 5, 4, None ) e = E("","hi\nab") e.set_edit_pos( 1 ) self.ktest( "cursor move up fail short", 'page down', [T(""),T(""),e,T("\n"),T("\n")], 2, 1, 2, -1, (1, 0) ) odd_e = E("","hi\nab") odd_e.set_edit_pos( 1 ) # disble cursor movement in odd_e object odd_e.move_cursor_to_coords = lambda s,c,xy: 0 self.ktest( "cursor force fail short", 'page down', [T(""),T(""),odd_e,T("\n"),T("\n")], 2, 2, 4, 3, None ) self.ktest( "cursor force fail long", 'page down', [T("\n"),S(""),T("\n"),T("\n"),T("\n"),E("hi\n","ab")], 1, 2, 4, 4, None ) self.ktest( "prefer not cut off", 'page down', [T("\n"),S(""),T("\n\n"),S(""),T("\n"),S("\n")], 1, 2, 3, 3, None ) self.ktest( "allow cut off", 'page down', [T("\n"),S(""),T("\n\n"),T(""),T("\n"),S("\n")], 1, 2, 5, 4, None ) self.ktest( "at bottom fail", 'page down', [T("\n\n"),T("\n"),T("\n\n\n")], 2, 1, 2, 1, None ) self.ktest( "all visible fail", 'page down', [T("a"),T("\n")], 1, 1, 1, 1, None ) self.ktest( "current ok fail", 'page down', [S("hi"),T("\n\n")], 0, 0, 0, 0, None ) self.ktest( "all visible choose last selectable", 'page down', [S("a"),S("b"),S("c"),T("")], 0, 0, 2, 2, None ) self.ktest( "bring in edge choose last", 'page down', [T("-"),S("d"),T("c"),S("-"),T("-"),S("b")],1,1, 5,4, None ) self.ktest( "bring in edge choose last selectable", 'page down', [T("-"),S("d"),T("c"),S("-"),S("-"),T("b")],1,1, 4,3, None ) class ZeroHeightContentsTest(unittest.TestCase): def test_listbox_pile(self): lb = urwid.ListBox(urwid.SimpleListWalker( [urwid.Pile([])])) lb.render((40,10), focus=True) def test_listbox_text_pile_page_down(self): lb = urwid.ListBox(urwid.SimpleListWalker( [urwid.Text(u'above'), urwid.Pile([])])) lb.keypress((40,10), 'page down') self.assertEqual(lb.get_focus()[1], 0) lb.keypress((40,10), 'page down') # second one caused ListBox failure self.assertEqual(lb.get_focus()[1], 0) def test_listbox_text_pile_page_up(self): lb = urwid.ListBox(urwid.SimpleListWalker( [urwid.Pile([]), urwid.Text(u'below')])) lb.set_focus(1) lb.keypress((40,10), 'page up') self.assertEqual(lb.get_focus()[1], 1) lb.keypress((40,10), 'page up') # second one caused pile failure self.assertEqual(lb.get_focus()[1], 1) def test_listbox_text_pile_down(self): sp = urwid.Pile([]) sp.selectable = lambda: True # abuse our Pile lb = urwid.ListBox(urwid.SimpleListWalker([urwid.Text(u'above'), sp])) lb.keypress((40,10), 'down') self.assertEqual(lb.get_focus()[1], 0) lb.keypress((40,10), 'down') self.assertEqual(lb.get_focus()[1], 0) def test_listbox_text_pile_up(self): sp = urwid.Pile([]) sp.selectable = lambda: True # abuse our Pile lb = urwid.ListBox(urwid.SimpleListWalker([sp, urwid.Text(u'below')])) lb.set_focus(1) lb.keypress((40,10), 'up') self.assertEqual(lb.get_focus()[1], 1) lb.keypress((40,10), 'up') self.assertEqual(lb.get_focus()[1], 1) urwid-1.3.1/urwid/tests/test_widget.py0000664000175000017500000001236212615524560017412 0ustar ianian00000000000000# -*- coding: utf-8 -*- import unittest from urwid.compat import B import urwid class TextTest(unittest.TestCase): def setUp(self): self.t = urwid.Text("I walk the\ncity in the night") def test1_wrap(self): expected = [B(t) for t in "I walk the","city in ","the night "] got = self.t.render((10,))._text assert got == expected, "got: %r expected: %r" % (got, expected) def test2_left(self): self.t.set_align_mode('left') expected = [B(t) for t in "I walk the ","city in the night "] got = self.t.render((18,))._text assert got == expected, "got: %r expected: %r" % (got, expected) def test3_right(self): self.t.set_align_mode('right') expected = [B(t) for t in " I walk the"," city in the night"] got = self.t.render((18,))._text assert got == expected, "got: %r expected: %r" % (got, expected) def test4_center(self): self.t.set_align_mode('center') expected = [B(t) for t in " I walk the "," city in the night"] got = self.t.render((18,))._text assert got == expected, "got: %r expected: %r" % (got, expected) def test5_encode_error(self): urwid.set_encoding("ascii") expected = [B("? ")] got = urwid.Text(u'û').render((3,))._text assert got == expected, "got: %r expected: %r" % (got, expected) class EditTest(unittest.TestCase): def setUp(self): self.t1 = urwid.Edit(B(""),"blah blah") self.t2 = urwid.Edit(B("stuff:"), "blah blah") self.t3 = urwid.Edit(B("junk:\n"),"blah blah\n\nbloo",1) self.t4 = urwid.Edit(u"better:") def ktest(self, e, key, expected, pos, desc): got= e.keypress((12,),key) assert got == expected, "%s. got: %r expected:%r" % (desc, got, expected) assert e.edit_pos == pos, "%s. pos: %r expected pos: " % ( desc, e.edit_pos, pos) def test1_left(self): self.t1.set_edit_pos(0) self.ktest(self.t1,'left','left',0,"left at left edge") self.ktest(self.t2,'left',None,8,"left within text") self.t3.set_edit_pos(10) self.ktest(self.t3,'left',None,9,"left after newline") def test2_right(self): self.ktest(self.t1,'right','right',9,"right at right edge") self.t2.set_edit_pos(8) self.ktest(self.t2,'right',None,9,"right at right edge-1") self.t3.set_edit_pos(0) self.t3.keypress((12,),'right') assert self.t3.get_pref_col((12,)) == 1 def test3_up(self): self.ktest(self.t1,'up','up',9,"up at top") self.t2.set_edit_pos(2) self.t2.keypress((12,),"left") assert self.t2.get_pref_col((12,)) == 7 self.ktest(self.t2,'up','up',1,"up at top again") assert self.t2.get_pref_col((12,)) == 7 self.t3.set_edit_pos(10) self.ktest(self.t3,'up',None,0,"up at top+1") def test4_down(self): self.ktest(self.t1,'down','down',9,"down single line") self.t3.set_edit_pos(5) self.ktest(self.t3,'down',None,10,"down line 1 to 2") self.ktest(self.t3,'down',None,15,"down line 2 to 3") self.ktest(self.t3,'down','down',15,"down at bottom") def test_utf8_input(self): urwid.set_encoding("utf-8") self.t1.set_edit_text('') self.t1.keypress((12,), u'û') self.assertEqual(self.t1.edit_text, u'û'.encode('utf-8')) self.t4.keypress((12,), u'û') self.assertEqual(self.t4.edit_text, u'û') class EditRenderTest(unittest.TestCase): def rtest(self, w, expected_text, expected_cursor): expected_text = [B(t) for t in expected_text] get_cursor = w.get_cursor_coords((4,)) assert get_cursor == expected_cursor, "got: %r expected: %r" % ( get_cursor, expected_cursor) r = w.render((4,), focus = 1) text = [t for a, cs, t in [ln[0] for ln in r.content()]] assert text == expected_text, "got: %r expected: %r" % (text, expected_text) assert r.cursor == expected_cursor, "got: %r expected: %r" % ( r.cursor, expected_cursor) def test1_SpaceWrap(self): w = urwid.Edit("","blah blah") w.set_edit_pos(0) self.rtest(w,["blah","blah"],(0,0)) w.set_edit_pos(4) self.rtest(w,["lah ","blah"],(3,0)) w.set_edit_pos(5) self.rtest(w,["blah","blah"],(0,1)) w.set_edit_pos(9) self.rtest(w,["blah","lah "],(3,1)) def test2_ClipWrap(self): w = urwid.Edit("","blah\nblargh",1) w.set_wrap_mode('clip') w.set_edit_pos(0) self.rtest(w,["blah","blar"],(0,0)) w.set_edit_pos(10) self.rtest(w,["blah","argh"],(3,1)) w.set_align_mode('right') w.set_edit_pos(6) self.rtest(w,["blah","larg"],(0,1)) def test3_AnyWrap(self): w = urwid.Edit("","blah blah") w.set_wrap_mode('any') self.rtest(w,["blah"," bla","h "],(1,2)) def test4_CursorNudge(self): w = urwid.Edit("","hi",align='right') w.keypress((4,),'end') self.rtest(w,[" hi "],(3,0)) w.keypress((4,),'left') self.rtest(w,[" hi"],(3,0)) urwid-1.3.1/urwid/tests/__init__.py0000664000175000017500000000000012615524560016611 0ustar ianian00000000000000urwid-1.3.1/urwid/tests/test_doctests.py0000664000175000017500000000110012615524560017743 0ustar ianian00000000000000import unittest import doctest import urwid def load_tests(loader, tests, ignore): module_doctests = [ urwid.widget, urwid.wimp, urwid.decoration, urwid.display_common, urwid.main_loop, urwid.monitored_list, urwid.raw_display, 'urwid.split_repr', # override function with same name urwid.util, urwid.signals, ] for m in module_doctests: tests.addTests(doctest.DocTestSuite(m, optionflags=doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL)) return tests urwid-1.3.1/urwid/tests/test_canvas.py0000664000175000017500000003750012615524560017403 0ustar ianian00000000000000import unittest from urwid import canvas from urwid.compat import B import urwid class CanvasCacheTest(unittest.TestCase): def setUp(self): # purge the cache urwid.CanvasCache._widgets.clear() def cct(self, widget, size, focus, expected): got = urwid.CanvasCache.fetch(widget, urwid.Widget, size, focus) assert expected==got, "got: %s expected: %s"%(got, expected) def test1(self): a = urwid.Text("") b = urwid.Text("") blah = urwid.TextCanvas() blah.finalize(a, (10,1), False) blah2 = urwid.TextCanvas() blah2.finalize(a, (15,1), False) bloo = urwid.TextCanvas() bloo.finalize(b, (20,2), True) urwid.CanvasCache.store(urwid.Widget, blah) urwid.CanvasCache.store(urwid.Widget, blah2) urwid.CanvasCache.store(urwid.Widget, bloo) self.cct(a, (10,1), False, blah) self.cct(a, (15,1), False, blah2) self.cct(a, (15,1), True, None) self.cct(a, (10,2), False, None) self.cct(b, (20,2), True, bloo) self.cct(b, (21,2), True, None) urwid.CanvasCache.invalidate(a) self.cct(a, (10,1), False, None) self.cct(a, (15,1), False, None) self.cct(b, (20,2), True, bloo) class CanvasTest(unittest.TestCase): def ct(self, text, attr, exp_content): c = urwid.TextCanvas([B(t) for t in text], attr) content = list(c.content()) assert content == exp_content, "got: %r expected: %r" % (content, exp_content) def ct2(self, text, attr, left, top, cols, rows, def_attr, exp_content): c = urwid.TextCanvas([B(t) for t in text], attr) content = list(c.content(left, top, cols, rows, def_attr)) assert content == exp_content, "got: %r expected: %r" % (content, exp_content) def test1(self): self.ct(["Hello world"], None, [[(None, None, B("Hello world"))]]) self.ct(["Hello world"], [[("a",5)]], [[("a", None, B("Hello")), (None, None, B(" world"))]]) self.ct(["Hi","There"], None, [[(None, None, B("Hi "))], [(None, None, B("There"))]]) def test2(self): self.ct2(["Hello"], None, 0, 0, 5, 1, None, [[(None, None, B("Hello"))]]) self.ct2(["Hello"], None, 1, 0, 4, 1, None, [[(None, None, B("ello"))]]) self.ct2(["Hello"], None, 0, 0, 4, 1, None, [[(None, None, B("Hell"))]]) self.ct2(["Hi","There"], None, 1, 0, 3, 2, None, [[(None, None, B("i "))], [(None, None, B("her"))]]) self.ct2(["Hi","There"], None, 0, 0, 5, 1, None, [[(None, None, B("Hi "))]]) self.ct2(["Hi","There"], None, 0, 1, 5, 1, None, [[(None, None, B("There"))]]) class ShardBodyTest(unittest.TestCase): def sbt(self, shards, shard_tail, expected): result = canvas.shard_body(shards, shard_tail, False) assert result == expected, "got: %r expected: %r" % (result, expected) def sbttail(self, num_rows, sbody, expected): result = canvas.shard_body_tail(num_rows, sbody) assert result == expected, "got: %r expected: %r" % (result, expected) def sbtrow(self, sbody, expected): result = list(canvas.shard_body_row(sbody)) assert result == expected, "got: %r expected: %r" % (result, expected) def test1(self): cviews = [(0,0,10,5,None,"foo"),(0,0,5,5,None,"bar")] self.sbt(cviews, [], [(0, None, (0,0,10,5,None,"foo")), (0, None, (0,0,5,5,None,"bar"))]) self.sbt(cviews, [(0, 3, None, (0,0,5,8,None,"baz"))], [(3, None, (0,0,5,8,None,"baz")), (0, None, (0,0,10,5,None,"foo")), (0, None, (0,0,5,5,None,"bar"))]) self.sbt(cviews, [(10, 3, None, (0,0,5,8,None,"baz"))], [(0, None, (0,0,10,5,None,"foo")), (3, None, (0,0,5,8,None,"baz")), (0, None, (0,0,5,5,None,"bar"))]) self.sbt(cviews, [(15, 3, None, (0,0,5,8,None,"baz"))], [(0, None, (0,0,10,5,None,"foo")), (0, None, (0,0,5,5,None,"bar")), (3, None, (0,0,5,8,None,"baz"))]) def test2(self): sbody = [(0, None, (0,0,10,5,None,"foo")), (0, None, (0,0,5,5,None,"bar")), (3, None, (0,0,5,8,None,"baz"))] self.sbttail(5, sbody, []) self.sbttail(3, sbody, [(0, 3, None, (0,0,10,5,None,"foo")), (0, 3, None, (0,0,5,5,None,"bar")), (0, 6, None, (0,0,5,8,None,"baz"))]) sbody = [(0, None, (0,0,10,3,None,"foo")), (0, None, (0,0,5,5,None,"bar")), (3, None, (0,0,5,9,None,"baz"))] self.sbttail(3, sbody, [(10, 3, None, (0,0,5,5,None,"bar")), (0, 6, None, (0,0,5,9,None,"baz"))]) def test3(self): self.sbtrow([(0, None, (0,0,10,5,None,"foo")), (0, None, (0,0,5,5,None,"bar")), (3, None, (0,0,5,8,None,"baz"))], [20]) self.sbtrow([(0, iter("foo"), (0,0,10,5,None,"foo")), (0, iter("bar"), (0,0,5,5,None,"bar")), (3, iter("zzz"), (0,0,5,8,None,"baz"))], ["f","b","z"]) class ShardsTrimTest(unittest.TestCase): def sttop(self, shards, top, expected): result = canvas.shards_trim_top(shards, top) assert result == expected, "got: %r expected: %r" (result, expected) def strows(self, shards, rows, expected): result = canvas.shards_trim_rows(shards, rows) assert result == expected, "got: %r expected: %r" (result, expected) def stsides(self, shards, left, cols, expected): result = canvas.shards_trim_sides(shards, left, cols) assert result == expected, "got: %r expected: %r" (result, expected) def test1(self): shards = [(5, [(0,0,10,5,None,"foo"),(0,0,5,5,None,"bar")])] self.sttop(shards, 2, [(3, [(0,2,10,3,None,"foo"),(0,2,5,3,None,"bar")])]) self.strows(shards, 2, [(2, [(0,0,10,2,None,"foo"),(0,0,5,2,None,"bar")])]) shards = [(5, [(0,0,10,5,None,"foo")]),(3,[(0,0,10,3,None,"bar")])] self.sttop(shards, 2, [(3, [(0,2,10,3,None,"foo")]),(3,[(0,0,10,3,None,"bar")])]) self.sttop(shards, 5, [(3, [(0,0,10,3,None,"bar")])]) self.sttop(shards, 7, [(1, [(0,2,10,1,None,"bar")])]) self.strows(shards, 7, [(5, [(0,0,10,5,None,"foo")]),(2, [(0,0,10,2,None,"bar")])]) self.strows(shards, 5, [(5, [(0,0,10,5,None,"foo")])]) self.strows(shards, 4, [(4, [(0,0,10,4,None,"foo")])]) shards = [(5, [(0,0,10,5,None,"foo"), (0,0,5,8,None,"baz")]), (3,[(0,0,10,3,None,"bar")])] self.sttop(shards, 2, [(3, [(0,2,10,3,None,"foo"), (0,2,5,6,None,"baz")]), (3,[(0,0,10,3,None,"bar")])]) self.sttop(shards, 5, [(3, [(0,0,10,3,None,"bar"), (0,5,5,3,None,"baz")])]) self.sttop(shards, 7, [(1, [(0,2,10,1,None,"bar"), (0,7,5,1,None,"baz")])]) self.strows(shards, 7, [(5, [(0,0,10,5,None,"foo"), (0,0,5,7,None,"baz")]), (2, [(0,0,10,2,None,"bar")])]) self.strows(shards, 5, [(5, [(0,0,10,5,None,"foo"), (0,0,5,5,None,"baz")])]) self.strows(shards, 4, [(4, [(0,0,10,4,None,"foo"), (0,0,5,4,None,"baz")])]) def test2(self): shards = [(5, [(0,0,10,5,None,"foo"),(0,0,5,5,None,"bar")])] self.stsides(shards, 0, 15, [(5, [(0,0,10,5,None,"foo"),(0,0,5,5,None,"bar")])]) self.stsides(shards, 6, 9, [(5, [(6,0,4,5,None,"foo"),(0,0,5,5,None,"bar")])]) self.stsides(shards, 6, 6, [(5, [(6,0,4,5,None,"foo"),(0,0,2,5,None,"bar")])]) self.stsides(shards, 0, 10, [(5, [(0,0,10,5,None,"foo")])]) self.stsides(shards, 10, 5, [(5, [(0,0,5,5,None,"bar")])]) self.stsides(shards, 1, 7, [(5, [(1,0,7,5,None,"foo")])]) shards = [(5, [(0,0,10,5,None,"foo"), (0,0,5,8,None,"baz")]), (3,[(0,0,10,3,None,"bar")])] self.stsides(shards, 0, 15, [(5, [(0,0,10,5,None,"foo"), (0,0,5,8,None,"baz")]), (3,[(0,0,10,3,None,"bar")])]) self.stsides(shards, 2, 13, [(5, [(2,0,8,5,None,"foo"), (0,0,5,8,None,"baz")]), (3,[(2,0,8,3,None,"bar")])]) self.stsides(shards, 2, 10, [(5, [(2,0,8,5,None,"foo"), (0,0,2,8,None,"baz")]), (3,[(2,0,8,3,None,"bar")])]) self.stsides(shards, 2, 8, [(5, [(2,0,8,5,None,"foo")]), (3,[(2,0,8,3,None,"bar")])]) self.stsides(shards, 2, 6, [(5, [(2,0,6,5,None,"foo")]), (3,[(2,0,6,3,None,"bar")])]) self.stsides(shards, 10, 5, [(8, [(0,0,5,8,None,"baz")])]) self.stsides(shards, 11, 3, [(8, [(1,0,3,8,None,"baz")])]) class ShardsJoinTest(unittest.TestCase): def sjt(self, shard_lists, expected): result = canvas.shards_join(shard_lists) assert result == expected, "got: %r expected: %r" (result, expected) def test(self): shards1 = [(5, [(0,0,10,5,None,"foo"), (0,0,5,8,None,"baz")]), (3,[(0,0,10,3,None,"bar")])] shards2 = [(3, [(0,0,10,3,None,"aaa")]), (5,[(0,0,10,5,None,"bbb")])] shards3 = [(3, [(0,0,10,3,None,"111")]), (2,[(0,0,10,3,None,"222")]), (3,[(0,0,10,3,None,"333")])] self.sjt([shards1], shards1) self.sjt([shards1, shards2], [(3, [(0,0,10,5,None,"foo"), (0,0,5,8,None,"baz"), (0,0,10,3,None,"aaa")]), (2, [(0,0,10,5,None,"bbb")]), (3, [(0,0,10,3,None,"bar")])]) self.sjt([shards1, shards3], [(3, [(0,0,10,5,None,"foo"), (0,0,5,8,None,"baz"), (0,0,10,3,None,"111")]), (2, [(0,0,10,3,None,"222")]), (3, [(0,0,10,3,None,"bar"), (0,0,10,3,None,"333")])]) self.sjt([shards1, shards2, shards3], [(3, [(0,0,10,5,None,"foo"), (0,0,5,8,None,"baz"), (0,0,10,3,None,"aaa"), (0,0,10,3,None,"111")]), (2, [(0,0,10,5,None,"bbb"), (0,0,10,3,None,"222")]), (3, [(0,0,10,3,None,"bar"), (0,0,10,3,None,"333")])]) class CanvasJoinTest(unittest.TestCase): def cjtest(self, desc, l, expected): l = [(c, None, False, n) for c, n in l] result = list(urwid.CanvasJoin(l).content()) assert result == expected, "%s expected %r, got %r"%( desc, expected, result) def test(self): C = urwid.TextCanvas hello = C([B("hello")]) there = C([B("there")], [[("a",5)]]) a = C([B("a")]) hi = C([B("hi")]) how = C([B("how")], [[("a",1)]]) dy = C([B("dy")]) how_you = C([B("how"), B("you")]) self.cjtest("one", [(hello, 5)], [[(None, None, B("hello"))]]) self.cjtest("two", [(hello, 5), (there, 5)], [[(None, None, B("hello")), ("a", None, B("there"))]]) self.cjtest("two space", [(hello, 7), (there, 5)], [[(None, None, B("hello")),(None,None,B(" ")), ("a", None, B("there"))]]) self.cjtest("three space", [(hi, 4), (how, 3), (dy, 2)], [[(None, None, B("hi")),(None,None,B(" ")),("a",None, B("h")), (None,None,B("ow")),(None,None,B("dy"))]]) self.cjtest("four space", [(a, 2), (hi, 3), (dy, 3), (a, 1)], [[(None, None, B("a")),(None,None,B(" ")), (None, None, B("hi")),(None,None,B(" ")), (None, None, B("dy")),(None,None,B(" ")), (None, None, B("a"))]]) self.cjtest("pile 2", [(how_you, 4), (hi, 2)], [[(None, None, B('how')), (None, None, B(' ')), (None, None, B('hi'))], [(None, None, B('you')), (None, None, B(' ')), (None, None, B(' '))]]) self.cjtest("pile 2r", [(hi, 4), (how_you, 3)], [[(None, None, B('hi')), (None, None, B(' ')), (None, None, B('how'))], [(None, None, B(' ')), (None, None, B('you'))]]) class CanvasOverlayTest(unittest.TestCase): def cotest(self, desc, bgt, bga, fgt, fga, l, r, et): bgt = B(bgt) fgt = B(fgt) bg = urwid.CompositeCanvas( urwid.TextCanvas([bgt],[bga])) fg = urwid.CompositeCanvas( urwid.TextCanvas([fgt],[fga])) bg.overlay(fg, l, 0) result = list(bg.content()) assert result == et, "%s expected %r, got %r"%( desc, et, result) def test1(self): self.cotest("left", "qxqxqxqx", [], "HI", [], 0, 6, [[(None, None, B("HI")),(None,None,B("qxqxqx"))]]) self.cotest("right", "qxqxqxqx", [], "HI", [], 6, 0, [[(None, None, B("qxqxqx")),(None,None,B("HI"))]]) self.cotest("center", "qxqxqxqx", [], "HI", [], 3, 3, [[(None, None, B("qxq")),(None,None,B("HI")), (None,None,B("xqx"))]]) self.cotest("center2", "qxqxqxqx", [], "HI ", [], 2, 2, [[(None, None, B("qx")),(None,None,B("HI ")), (None,None,B("qx"))]]) self.cotest("full", "rz", [], "HI", [], 0, 0, [[(None, None, B("HI"))]]) def test2(self): self.cotest("same","asdfghjkl",[('a',9)],"HI",[('a',2)],4,3, [[('a',None,B("asdf")),('a',None,B("HI")),('a',None,B("jkl"))]]) self.cotest("diff","asdfghjkl",[('a',9)],"HI",[('b',2)],4,3, [[('a',None,B("asdf")),('b',None,B("HI")),('a',None,B("jkl"))]]) self.cotest("None end","asdfghjkl",[('a',9)],"HI ",[('a',2)], 2,3, [[('a',None,B("as")),('a',None,B("HI")), (None,None,B(" ")),('a',None,B("jkl"))]]) self.cotest("float end","asdfghjkl",[('a',3)],"HI",[('a',2)], 4,3, [[('a',None,B("asd")),(None,None,B("f")), ('a',None,B("HI")),(None,None,B("jkl"))]]) self.cotest("cover 2","asdfghjkl",[('a',5),('c',4)],"HI", [('b',2)],4,3, [[('a',None,B("asdf")),('b',None,B("HI")),('c',None,B("jkl"))]]) self.cotest("cover 2-2","asdfghjkl", [('a',4),('d',1),('e',1),('c',3)], "HI",[('b',2)], 4, 3, [[('a',None,B("asdf")),('b',None,B("HI")),('c',None,B("jkl"))]]) def test3(self): urwid.set_encoding("euc-jp") self.cotest("db0","\xA1\xA1\xA1\xA1\xA1\xA1",[],"HI",[],2,2, [[(None,None,B("\xA1\xA1")),(None,None,B("HI")), (None,None,B("\xA1\xA1"))]]) self.cotest("db1","\xA1\xA1\xA1\xA1\xA1\xA1",[],"OHI",[],1,2, [[(None,None,B(" ")),(None,None,B("OHI")), (None,None,B("\xA1\xA1"))]]) self.cotest("db2","\xA1\xA1\xA1\xA1\xA1\xA1",[],"OHI",[],2,1, [[(None,None,B("\xA1\xA1")),(None,None,B("OHI")), (None,None,B(" "))]]) self.cotest("db3","\xA1\xA1\xA1\xA1\xA1\xA1",[],"OHIO",[],1,1, [[(None,None,B(" ")),(None,None,B("OHIO")),(None,None,B(" "))]]) class CanvasPadTrimTest(unittest.TestCase): def cptest(self, desc, ct, ca, l, r, et): ct = B(ct) c = urwid.CompositeCanvas( urwid.TextCanvas([ct], [ca])) c.pad_trim_left_right(l, r) result = list(c.content()) assert result == et, "%s expected %r, got %r"%( desc, et, result) def test1(self): self.cptest("none", "asdf", [], 0, 0, [[(None,None,B("asdf"))]]) self.cptest("left pad", "asdf", [], 2, 0, [[(None,None,B(" ")),(None,None,B("asdf"))]]) self.cptest("right pad", "asdf", [], 0, 2, [[(None,None,B("asdf")),(None,None,B(" "))]]) def test2(self): self.cptest("left trim", "asdf", [], -2, 0, [[(None,None,B("df"))]]) self.cptest("right trim", "asdf", [], 0, -2, [[(None,None,B("as"))]]) urwid-1.3.1/urwid/wimp.py0000775000175000017500000005210612615524560014705 0ustar ianian00000000000000#!/usr/bin/python # # Urwid Window-Icon-Menu-Pointer-style widget classes # Copyright (C) 2004-2011 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: http://excess.org/urwid/ from urwid.widget import (Text, WidgetWrap, delegate_to_widget_mixin, BOX, FLOW) from urwid.canvas import CompositeCanvas from urwid.signals import connect_signal from urwid.container import Columns, Overlay from urwid.util import is_mouse_press from urwid.text_layout import calc_coords from urwid.signals import disconnect_signal # doctests from urwid.split_repr import python3_repr from urwid.decoration import WidgetDecoration from urwid.command_map import ACTIVATE class SelectableIcon(Text): _selectable = True def __init__(self, text, cursor_position=1): """ :param text: markup for this widget; see :class:`Text` for description of text markup :param cursor_position: position the cursor will appear in the text when this widget is in focus This is a text widget that is selectable. A cursor displayed at a fixed location in the text when in focus. This widget has no special handling of keyboard or mouse input. """ self.__super.__init__(text) self._cursor_position = cursor_position def render(self, size, focus=False): """ Render the text content of this widget with a cursor when in focus. >>> si = SelectableIcon(u"[!]") >>> si >>> si.render((4,), focus=True).cursor (1, 0) >>> si = SelectableIcon("((*))", 2) >>> si.render((8,), focus=True).cursor (2, 0) >>> si.render((2,), focus=True).cursor (0, 1) """ c = self.__super.render(size, focus) if focus: # create a new canvas so we can add a cursor c = CompositeCanvas(c) c.cursor = self.get_cursor_coords(size) return c def get_cursor_coords(self, size): """ Return the position of the cursor if visible. This method is required for widgets that display a cursor. """ if self._cursor_position > len(self.text): return None # find out where the cursor will be displayed based on # the text layout (maxcol,) = size trans = self.get_line_translation(maxcol) x, y = calc_coords(self.text, trans, self._cursor_position) if maxcol <= x: return None return x, y def keypress(self, size, key): """ No keys are handled by this widget. This method is required for selectable widgets. """ return key class CheckBoxError(Exception): pass class CheckBox(WidgetWrap): def sizing(self): return frozenset([FLOW]) states = { True: SelectableIcon("[X]"), False: SelectableIcon("[ ]"), 'mixed': SelectableIcon("[#]") } reserve_columns = 4 # allow users of this class to listen for change events # sent when the state of this widget is modified # (this variable is picked up by the MetaSignals metaclass) signals = ["change"] def __init__(self, label, state=False, has_mixed=False, on_state_change=None, user_data=None): """ :param label: markup for check box label :param state: False, True or "mixed" :param has_mixed: True if "mixed" is a state to cycle through :param on_state_change: shorthand for connect_signal() function call for a single callback :param user_data: user_data for on_state_change Signals supported: ``'change'`` Register signal handler with:: urwid.connect_signal(check_box, 'change', callback, user_data) where callback is callback(check_box, new_state [,user_data]) Unregister signal handlers with:: urwid.disconnect_signal(check_box, 'change', callback, user_data) >>> CheckBox(u"Confirm") >>> CheckBox(u"Yogourt", "mixed", True) >>> cb = CheckBox(u"Extra onions", True) >>> cb >>> cb.render((20,), focus=True).text # ... = b in Python 3 [...'[X] Extra onions '] """ self.__super.__init__(None) # self.w set by set_state below self._label = Text("") self.has_mixed = has_mixed self._state = None # The old way of listening for a change was to pass the callback # in to the constructor. Just convert it to the new way: if on_state_change: connect_signal(self, 'change', on_state_change, user_data) self.set_label(label) self.set_state(state) def _repr_words(self): return self.__super._repr_words() + [ python3_repr(self.label)] def _repr_attrs(self): return dict(self.__super._repr_attrs(), state=self.state) def set_label(self, label): """ Change the check box label. label -- markup for label. See Text widget for description of text markup. >>> cb = CheckBox(u"foo") >>> cb >>> cb.set_label(('bright_attr', u"bar")) >>> cb """ self._label.set_text(label) # no need to call self._invalidate(). WidgetWrap takes care of # that when self.w changes def get_label(self): """ Return label text. >>> cb = CheckBox(u"Seriously") >>> print cb.get_label() Seriously >>> print cb.label Seriously >>> cb.set_label([('bright_attr', u"flashy"), u" normal"]) >>> print cb.label # only text is returned flashy normal """ return self._label.text label = property(get_label) def set_state(self, state, do_callback=True): """ Set the CheckBox state. state -- True, False or "mixed" do_callback -- False to supress signal from this change >>> changes = [] >>> def callback_a(cb, state, user_data): ... changes.append("A %r %r" % (state, user_data)) >>> def callback_b(cb, state): ... changes.append("B %r" % state) >>> cb = CheckBox('test', False, False) >>> key1 = connect_signal(cb, 'change', callback_a, "user_a") >>> key2 = connect_signal(cb, 'change', callback_b) >>> cb.set_state(True) # both callbacks will be triggered >>> cb.state True >>> disconnect_signal(cb, 'change', callback_a, "user_a") >>> cb.state = False >>> cb.state False >>> cb.set_state(True) >>> cb.state True >>> cb.set_state(False, False) # don't send signal >>> changes ["A True 'user_a'", 'B True', 'B False', 'B True'] """ if self._state == state: return if state not in self.states: raise CheckBoxError("%s Invalid state: %s" % ( repr(self), repr(state))) # self._state is None is a special case when the CheckBox # has just been created if do_callback and self._state is not None: self._emit('change', state) self._state = state # rebuild the display widget with the new state self._w = Columns( [ ('fixed', self.reserve_columns, self.states[state] ), self._label ] ) self._w.focus_col = 0 def get_state(self): """Return the state of the checkbox.""" return self._state state = property(get_state, set_state) def keypress(self, size, key): """ Toggle state on 'activate' command. >>> assert CheckBox._command_map[' '] == 'activate' >>> assert CheckBox._command_map['enter'] == 'activate' >>> size = (10,) >>> cb = CheckBox('press me') >>> cb.state False >>> cb.keypress(size, ' ') >>> cb.state True >>> cb.keypress(size, ' ') >>> cb.state False """ if self._command_map[key] != ACTIVATE: return key self.toggle_state() def toggle_state(self): """ Cycle to the next valid state. >>> cb = CheckBox("3-state", has_mixed=True) >>> cb.state False >>> cb.toggle_state() >>> cb.state True >>> cb.toggle_state() >>> cb.state 'mixed' >>> cb.toggle_state() >>> cb.state False """ if self.state == False: self.set_state(True) elif self.state == True: if self.has_mixed: self.set_state('mixed') else: self.set_state(False) elif self.state == 'mixed': self.set_state(False) def mouse_event(self, size, event, button, x, y, focus): """ Toggle state on button 1 press. >>> size = (20,) >>> cb = CheckBox("clickme") >>> cb.state False >>> cb.mouse_event(size, 'mouse press', 1, 2, 0, True) True >>> cb.state True """ if button != 1 or not is_mouse_press(event): return False self.toggle_state() return True class RadioButton(CheckBox): states = { True: SelectableIcon("(X)"), False: SelectableIcon("( )"), 'mixed': SelectableIcon("(#)") } reserve_columns = 4 def __init__(self, group, label, state="first True", on_state_change=None, user_data=None): """ :param group: list for radio buttons in same group :param label: markup for radio button label :param state: False, True, "mixed" or "first True" :param on_state_change: shorthand for connect_signal() function call for a single 'change' callback :param user_data: user_data for on_state_change This function will append the new radio button to group. "first True" will set to True if group is empty. Signals supported: ``'change'`` Register signal handler with:: urwid.connect_signal(radio_button, 'change', callback, user_data) where callback is callback(radio_button, new_state [,user_data]) Unregister signal handlers with:: urwid.disconnect_signal(radio_button, 'change', callback, user_data) >>> bgroup = [] # button group >>> b1 = RadioButton(bgroup, u"Agree") >>> b2 = RadioButton(bgroup, u"Disagree") >>> len(bgroup) 2 >>> b1 >>> b2 >>> b2.render((15,), focus=True).text # ... = b in Python 3 [...'( ) Disagree '] """ if state=="first True": state = not group self.group = group self.__super.__init__(label, state, False, on_state_change, user_data) group.append(self) def set_state(self, state, do_callback=True): """ Set the RadioButton state. state -- True, False or "mixed" do_callback -- False to supress signal from this change If state is True all other radio buttons in the same button group will be set to False. >>> bgroup = [] # button group >>> b1 = RadioButton(bgroup, u"Agree") >>> b2 = RadioButton(bgroup, u"Disagree") >>> b3 = RadioButton(bgroup, u"Unsure") >>> b1.state, b2.state, b3.state (True, False, False) >>> b2.set_state(True) >>> b1.state, b2.state, b3.state (False, True, False) >>> def relabel_button(radio_button, new_state): ... radio_button.set_label(u"Think Harder!") >>> key = connect_signal(b3, 'change', relabel_button) >>> b3 >>> b3.set_state(True) # this will trigger the callback >>> b3 """ if self._state == state: return self.__super.set_state(state, do_callback) # if we're clearing the state we don't have to worry about # other buttons in the button group if state is not True: return # clear the state of each other radio button for cb in self.group: if cb is self: continue if cb._state: cb.set_state(False) def toggle_state(self): """ Set state to True. >>> bgroup = [] # button group >>> b1 = RadioButton(bgroup, "Agree") >>> b2 = RadioButton(bgroup, "Disagree") >>> b1.state, b2.state (True, False) >>> b2.toggle_state() >>> b1.state, b2.state (False, True) >>> b2.toggle_state() >>> b1.state, b2.state (False, True) """ self.set_state(True) class Button(WidgetWrap): def sizing(self): return frozenset([FLOW]) button_left = Text("<") button_right = Text(">") signals = ["click"] def __init__(self, label, on_press=None, user_data=None): """ :param label: markup for button label :param on_press: shorthand for connect_signal() function call for a single callback :param user_data: user_data for on_press Signals supported: ``'click'`` Register signal handler with:: urwid.connect_signal(button, 'click', callback, user_data) where callback is callback(button [,user_data]) Unregister signal handlers with:: urwid.disconnect_signal(button, 'click', callback, user_data) >>> Button(u"Ok")