First pass at switching to distutils
authorEd Page <eopage@byu.net>
Tue, 9 Aug 2011 03:23:57 +0000 (22:23 -0500)
committerEd Page <eopage@byu.net>
Tue, 9 Aug 2011 23:37:10 +0000 (18:37 -0500)
80 files changed:
Gonvert [new file with mode: 0755]
LICENSE
Makefile
README [deleted file]
data/gonvert-base.svg [new file with mode: 0644]
data/gonvert.colors [new file with mode: 0644]
data/gonvert.png [new file with mode: 0644]
data/gonvert.svg [new file with mode: 0644]
data/pixmaps/gonvert-icon_alternative.png [deleted file]
data/pixmaps/gonvert.png [deleted file]
data/template.desktop [new file with mode: 0644]
gonvert/__init__.py [new file with mode: 0644]
gonvert/constants.py [new file with mode: 0644]
gonvert/converters.py [new file with mode: 0644]
gonvert/gonvert_qt.py [new file with mode: 0755]
gonvert/unit_data.py [new file with mode: 0644]
gonvert/util/__init__.py [new file with mode: 0644]
gonvert/util/algorithms.py [new file with mode: 0644]
gonvert/util/concurrent.py [new file with mode: 0644]
gonvert/util/coroutines.py [new file with mode: 0755]
gonvert/util/go_utils.py [new file with mode: 0644]
gonvert/util/gtk_utils.py [new file with mode: 0644]
gonvert/util/hildonize.py [new file with mode: 0644]
gonvert/util/io.py [new file with mode: 0644]
gonvert/util/linux.py [new file with mode: 0644]
gonvert/util/misc.py [new file with mode: 0644]
gonvert/util/overloading.py [new file with mode: 0644]
gonvert/util/qml_utils.py [new file with mode: 0644]
gonvert/util/qore_utils.py [new file with mode: 0644]
gonvert/util/qt_compat.py [new file with mode: 0644]
gonvert/util/qtpie.py [new file with mode: 0755]
gonvert/util/qtpieboard.py [new file with mode: 0755]
gonvert/util/qui_utils.py [new file with mode: 0644]
gonvert/util/qwrappers.py [new file with mode: 0644]
gonvert/util/time_utils.py [new file with mode: 0644]
gonvert/util/tp_utils.py [new file with mode: 0644]
gonvert/windows.py [new file with mode: 0644]
setup.py [new file with mode: 0755]
src [new symlink]
src/__init__.py [deleted file]
src/constants.py [deleted file]
src/converters.py [deleted file]
src/gonvert.py [deleted file]
src/gonvert_qt.py [deleted file]
src/unit_data.py [deleted file]
src/util/__init__.py [deleted file]
src/util/algorithms.py [deleted file]
src/util/concurrent.py [deleted file]
src/util/coroutines.py [deleted file]
src/util/go_utils.py [deleted file]
src/util/gtk_utils.py [deleted file]
src/util/hildonize.py [deleted file]
src/util/io.py [deleted file]
src/util/linux.py [deleted file]
src/util/misc.py [deleted file]
src/util/overloading.py [deleted file]
src/util/qore_utils.py [deleted file]
src/util/qt_compat.py [deleted file]
src/util/qtpie.py [deleted file]
src/util/qtpieboard.py [deleted file]
src/util/qui_utils.py [deleted file]
src/util/qwrappers.py [deleted file]
src/util/time_utils.py [deleted file]
src/util/tp_utils.py [deleted file]
src/windows.py [deleted file]
support/THANKS [new file with mode: 0644]
support/TODO [new file with mode: 0644]
support/builddeb.py [deleted file]
support/doc/CHANGELOG [deleted file]
support/doc/COPYING [deleted file]
support/doc/FAQ [deleted file]
support/doc/INSTALL [deleted file]
support/doc/README [deleted file]
support/doc/THANKS [deleted file]
support/doc/TODO [deleted file]
support/fake_py2deb.py [deleted file]
support/gonvert.desktop [deleted file]
support/gonvert.spec [deleted file]
support/py2deb.py [deleted file]
support/scale.py [new file with mode: 0755]

diff --git a/Gonvert b/Gonvert
new file mode 100755 (executable)
index 0000000..64d5a50
--- /dev/null
+++ b/Gonvert
@@ -0,0 +1,7 @@
+#!/usr/bin/python
+
+from gonvert import gonvert_qt
+
+
+if __name__ == "__main__":
+       gonvert_qt.run_gonvert()
diff --git a/LICENSE b/LICENSE
index 5ab7695..d60c31a 100644 (file)
--- a/LICENSE
+++ b/LICENSE
-                 GNU LESSER GENERAL PUBLIC LICENSE
-                      Version 2.1, February 1999
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
 
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
 
   To protect your rights, we need to make restrictions that forbid
-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.
-\f
-  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.
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
 
   The precise terms and conditions for copying, distribution and
-modification follow.  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.
+modification follow.
 \f
-                 GNU LESSER GENERAL PUBLIC LICENSE
+                   GNU 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
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
 covered by this License; they are outside its scope.  The act of
-running 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.
-\f
-  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
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
 distribute such modifications or work under the terms of Section 1
 above, provided that you also meet all of these conditions:
 
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
+    a) You must cause the modified files 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.)
-
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
 These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
+identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
 themselves, then this License, and its terms, do not apply to those
 sections when you distribute them as separate works.  But when you
 distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
+on the Program, the distribution of the whole must be on the terms of
 this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
+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.
+collective works based on the Program.
 
-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
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
 a storage or distribution medium does not bring the other work under
 the scope of this License.
 
-  3. You may 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.
-\f
-  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
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
 compelled to copy the source along with the object code.
-
-  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.
-\f
-  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.
 \f
-  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
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
 signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
+distribute the Program or its derivative works.  These actions are
 prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
 all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
+the Program 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
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
 restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
+You are not responsible for enforcing compliance by third parties to
 this License.
-\f
-  11. If, as a consequence of a court judgment or allegation of patent
+
+  7. If, as a consequence of a court judgment or allegation of patent
 infringement or for any other reason (not limited to patent issues),
 conditions are imposed on you (whether by court order, agreement or
 otherwise) that contradict the conditions of this License, they do not
 excuse you from the conditions of this License.  If you cannot
 distribute so as to satisfy simultaneously your obligations under this
 License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
 all those who receive copies directly or indirectly through you, then
 the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
+refrain entirely from distribution of the Program.
 
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
+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
+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
@@ -401,104 +225,116 @@ 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.
 \f
-  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.
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
 
                            NO WARRANTY
 
-  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.
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
 
                     END OF TERMS AND CONDITIONS
 \f
-           How to Apply These Terms to Your New Libraries
+           How to Apply These Terms to Your New Programs
 
-  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).
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
 
-  To 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.
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
 
-    <one line to give the library's name and a brief idea of what it does.>
+    <one line to give the program's name and a brief idea of what it does.>
     Copyright (C) <year>  <name of author>
 
-    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 program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
 
-    This library is distributed in the hope that it will be useful,
+    This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-    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.
 
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
 You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
+school, if any, to sign a "copyright disclaimer" for the program, if
 necessary.  Here is a sample; alter the names:
 
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
 
-  <signature of Ty Coon>, 1 April 1990
+  <signature of Ty Coon>, 1 April 1989
   Ty Coon, President of Vice
 
-That's all there is to it!
-
-
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
index 0aeac1f..18be2a6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,19 @@
 PROJECT_NAME=gonvert
-SOURCE_PATH=src
+PACKAGE_NAME=gonvert
+
+SOURCE_PATH=$(PACKAGE_NAME)
 SOURCE=$(shell find $(SOURCE_PATH) -iname "*.py")
-PROGRAM=$(SOURCE_PATH)/$(PROJECT_NAME).py
-DATA_PATH=data
-DATA_TYPES=*.ini *.map *.glade *.png
-DATA=$(foreach type, $(DATA_TYPES), $(shell find $(DATA_PATH) -iname "$(type)"))
+
+PROGRAM=Gonvert
+ICON_SIZES=26 32 48 80
+ICONS=$(foreach size, $(ICON_SIZES), data/icons/$(size)/$(PROJECT_NAME).png)
+PACKAGE_VARIANTS=fremantle harmattan ubuntu
+DESKTOP_FILES=$(foreach variant, $(PACKAGE_VARIANTS), data/$(variant)/$(PROJECT_NAME).desktop)
+SETUP_FILES=$(foreach variant, $(PACKAGE_VARIANTS), ./setup.$(variant).py)
+DIST_BASE_PATH=./dist
+DIST_PATHS=$(foreach variant, $(PACKAGE_VARIANTS), $(DIST_BASE_PATH)_$(variant)) $(DIST_BASE_PATH)_diablo
+
 OBJ=$(SOURCE:.py=.pyc)
-BUILD_PATH=./build
 TAG_FILE=~/.ctags/$(PROJECT_NAME).tags
 TODO_FILE=./TODO
 
@@ -21,12 +28,13 @@ PROFILE_VIEW=python -m pstats .profile
 TODO_FINDER=support/todo.py
 CTAGS=ctags-exuberant
 
+
 .PHONY: all run profile debug test build lint tags todo clean distclean
 
 all: test
 
 run: $(OBJ)
-       $(SOURCE_PATH)/$(PROJECT_NAME)_qt.py
+       $(PROGRAM)
 
 profile: $(OBJ)
        $(PROFILE_GEN) $(PROGRAM)
@@ -38,33 +46,27 @@ debug: $(OBJ)
 test: $(OBJ)
        $(UNIT_TEST)
 
-package: $(OBJ)
-       rm -Rf $(BUILD_PATH)
-
-       mkdir -p $(BUILD_PATH)/generic
-       cp $(SOURCE_PATH)/constants.py  $(BUILD_PATH)/generic
-       cp $(SOURCE_PATH)/$(PROJECT_NAME).py  $(BUILD_PATH)/generic
-       $(foreach file, $(DATA), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; )
-       $(foreach file, $(SOURCE), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; )
-       cp support/$(PROJECT_NAME).desktop $(BUILD_PATH)/generic
-       cp support/builddeb.py $(BUILD_PATH)/generic
-       cp support/py2deb.py $(BUILD_PATH)/generic
-       cp support/fake_py2deb.py $(BUILD_PATH)/generic
-
-       mkdir -p $(BUILD_PATH)/diablo
-       cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/diablo
-       cd $(BUILD_PATH)/diablo ; python builddeb.py diablo
-       mkdir -p $(BUILD_PATH)/fremantle
-       cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/fremantle
-       cd $(BUILD_PATH)/fremantle ; python builddeb.py fremantle
-       mkdir -p $(BUILD_PATH)/debian
-       cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/debian
-       cd $(BUILD_PATH)/debian ; python builddeb.py debian
+package: $(OBJ) $(ICONS) $(SETUP_FILES) $(DESKTOP_FILES)
+       rm -Rf $(DIST_BASE_PATH)_*/*
+       ./setup.fremantle.py sdist_diablo \
+               -d $(DIST_BASE_PATH)_diablo \
+               --install-purelib=/usr/lib/python2.5/site-packages
+       ./setup.fremantle.py sdist_fremantle \
+               -d $(DIST_BASE_PATH)_fremantle \
+               --install-purelib=/usr/lib/python2.5/site-packages
+       ./setup.harmattan.py sdist_harmattan \
+               -d $(DIST_BASE_PATH)_harmattan
+               --install-purelib=/usr/lib/python2.6/dist-packages
+       ./setup.ubuntu.py sdist_ubuntu \
+               -d $(DIST_BASE_PATH)_ubuntu
+       mkdir $(DIST_BASE_PATH)_ubuntu/build
+       cd $(DIST_BASE_PATH)_ubuntu/build ; tar -zxvf ../*.tar.gz
+       cd $(DIST_BASE_PATH)_ubuntu/build ; dpkg-buildpackage -tc -rfakeroot -us -uc
 
 upload:
-       dput fremantle-extras-builder $(BUILD_PATH)/fremantle/$(PROJECT_NAME)*.changes
-       dput diablo-extras-builder $(BUILD_PATH)/diablo/$(PROJECT_NAME)*.changes
-       cp $(BUILD_PATH)/debian/*.deb ./www/$(PROJECT_NAME).deb
+       dput diablo-extras-builder $(DIST_BASE_PATH)_diablo/$(PROJECT_NAME)*.changes
+       dput fremantle-extras-builder $(DIST_BASE_PATH)_fremantle/$(PROJECT_NAME)*.changes
+       cp $(DIST_BASE_PATH)_ubuntu/*.deb www/ejpi.deb
 
 lint: $(OBJ)
        $(foreach file, $(SOURCE), $(LINT) $(file) ; )
@@ -75,18 +77,60 @@ todo: $(TODO_FILE)
 
 clean:
        rm -Rf $(OBJ)
-       rm -Rf $(BUILD_PATH)
        rm -Rf $(TODO_FILE)
+       rm -f $(ICONS) $(SETUP_FILES) $(DESKTOP_FILES)
+       rm -Rf $(DIST_PATHS)
 
-distclean:
-       rm -Rf $(OBJ)
-       rm -Rf $(BUILD_PATH)
-       rm -Rf $(TAG_FILE)
+distclean: clean
        find $(SOURCE_PATH) -name "*.*~" | xargs rm -f
        find $(SOURCE_PATH) -name "*.swp" | xargs rm -f
        find $(SOURCE_PATH) -name "*.bak" | xargs rm -f
        find $(SOURCE_PATH) -name ".*.swp" | xargs rm -f
 
+
+$(SETUP_FILES): VARIANT=$(word 2, $(subst ., ,$@))
+
+setup.fremantle.py: setup.py src/constants.py
+       cog.py -c \
+               -D DESKTOP_FILE_PATH=/usr/share/applications/hildon \
+               -D INPUT_DESKTOP_FILE=data/$(VARIANT)/$(PROJECT_NAME).desktop \
+               -D ICON_CATEGORY=hildon \
+               -D ICON_SIZES=26,32,48 \
+               -o $@ $<
+       chmod +x $@
+
+setup.harmattan.py: setup.py src/constants.py
+       cog.py -c \
+               -D DESKTOP_FILE_PATH=/usr/share/applications \
+               -D INPUT_DESKTOP_FILE=data/$(VARIANT)/$(PROJECT_NAME).desktop \
+               -D ICON_CATEGORY=hildon \
+               -D ICON_SIZES=32,80 \
+               -o $@ $<
+       chmod +x $@
+
+setup.ubuntu.py: setup.py src/constants.py
+       cog.py -c \
+               -D DESKTOP_FILE_PATH=/usr/share/applications \
+               -D INPUT_DESKTOP_FILE=data/$(VARIANT)/$(PROJECT_NAME).desktop \
+               -D ICON_CATEGORY=apps \
+               -D ICON_SIZES=32,48 \
+               -o $@ $<
+       chmod +x $@
+
+$(ICONS): SIZE=$(word 3, $(subst /, ,$@))
+$(ICONS): data/$(PROJECT_NAME).png support/scale.py
+       mkdir -p $(dir $@)
+       support/scale.py --input $< --output $@ --size $(SIZE)
+
+$(DESKTOP_FILES): VARIANT=$(word 2, $(subst /, ,$@))
+$(DESKTOP_FILES): data/template.desktop
+       mkdir -p $(dir $@)
+       cog.py -d \
+               -D VARIANT=$(VARIANT) \
+               -D PROGRAM=$(PROGRAM) \
+               -o $@ $<
+
+
 $(TAG_FILE): $(OBJ)
        mkdir -p $(dir $(TAG_FILE))
        $(CTAGS) -o $(TAG_FILE) $(SOURCE)
diff --git a/README b/README
deleted file mode 100644 (file)
index ac2fe23..0000000
--- a/README
+++ /dev/null
@@ -1,4 +0,0 @@
-Building a package
-===================
-Run
-       make package
diff --git a/data/gonvert-base.svg b/data/gonvert-base.svg
new file mode 100644 (file)
index 0000000..2d60dff
--- /dev/null
@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="158.89999"
+   height="158.89999"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="gonvert-base.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3876">
+      <stop
+         style="stop-color:#620a0a;stop-opacity:1;"
+         offset="0"
+         id="stop3878" />
+      <stop
+         style="stop-color:#f13434;stop-opacity:1;"
+         offset="1"
+         id="stop3880" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3866">
+      <stop
+         style="stop-color:#c0d17e;stop-opacity:1;"
+         offset="0"
+         id="stop3868" />
+      <stop
+         style="stop-color:#88aa00;stop-opacity:1;"
+         offset="1"
+         id="stop3870" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3854">
+      <stop
+         style="stop-color:#631e1e;stop-opacity:1;"
+         offset="0"
+         id="stop3856" />
+      <stop
+         id="stop3874"
+         offset="0.52424657"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         style="stop-color:#121212;stop-opacity:1;"
+         offset="1"
+         id="stop3858" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3836">
+      <stop
+         style="stop-color:#2d82b6;stop-opacity:1;"
+         offset="0"
+         id="stop3838" />
+      <stop
+         style="stop-color:#2d82b6;stop-opacity:0;"
+         offset="1"
+         id="stop3840" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3813">
+      <stop
+         style="stop-color:#539dcb;stop-opacity:1;"
+         offset="0"
+         id="stop3815" />
+      <stop
+         id="stop3834"
+         offset="1"
+         style="stop-color:#2d82b6;stop-opacity:1;" />
+    </linearGradient>
+    <pattern
+       inkscape:collect="always"
+       xlink:href="#pattern3095"
+       id="pattern4605"
+       patternTransform="matrix(0.77039422,0,0,0.77039422,-396.55415,198.23649)" />
+    <pattern
+       patternUnits="userSpaceOnUse"
+       width="137.68516"
+       height="102.49221"
+       patternTransform="translate(-259.51537,281.93555)"
+       id="pattern3095">
+      <path
+         transform="matrix(0.34491466,0,0,0.34491466,146.49126,-63.454854)"
+         d="m -90.887574,478.18354 -29.779156,-40.85689 -50.53918,1.36897 29.65494,-40.94713 -16.91943,-47.64259 48.10692,15.55016 40.082399,-30.8137 0.07677,50.55766 41.691717,28.59868 -48.059472,15.69618 z"
+         inkscape:randomized="0"
+         inkscape:rounded="0"
+         inkscape:flatsided="false"
+         sodipodi:arg2="2.0277357"
+         sodipodi:arg1="1.3994172"
+         sodipodi:r2="38.066818"
+         sodipodi:r1="76.133636"
+         sodipodi:cy="403.16522"
+         sodipodi:cx="-103.87151"
+         sodipodi:sides="5"
+         id="path3075"
+         style="color:#000000;fill:none;stroke:#ffffff;stroke-width:2.65499997;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         sodipodi:type="star" />
+      <path
+         transform="matrix(0.56327644,0,0,0.56327644,97.922089,-187.09579)"
+         d="m -90.887574,478.18354 -29.779156,-40.85689 -50.53918,1.36897 29.65494,-40.94713 -16.91943,-47.64259 48.10692,15.55016 40.082399,-30.8137 0.07677,50.55766 41.691717,28.59868 -48.059472,15.69618 z"
+         inkscape:randomized="0"
+         inkscape:rounded="0"
+         inkscape:flatsided="false"
+         sodipodi:arg2="2.0277357"
+         sodipodi:arg1="1.3994172"
+         sodipodi:r2="38.066818"
+         sodipodi:r1="76.133636"
+         sodipodi:cy="403.16522"
+         sodipodi:cx="-103.87151"
+         sodipodi:sides="5"
+         id="path3075-6"
+         style="color:#000000;fill:none;stroke:#ffffff;stroke-width:2.65499997;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         sodipodi:type="star" />
+    </pattern>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3813"
+       id="linearGradient3819"
+       x1="248.07614"
+       y1="433.95383"
+       x2="275.25555"
+       y2="433.95383"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(9.3621607e-6,-0.0049642)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3813"
+       id="linearGradient3842"
+       x1="248.13908"
+       y1="444.51163"
+       x2="275.31851"
+       y2="444.51163"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-1.4344049e-6,-0.00493514)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3876"
+       id="linearGradient3884"
+       x1="-8.75"
+       y1="34.876453"
+       x2="8.5369473"
+       y2="34.876453"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-10.225613,-4.7485267)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3866"
+       id="linearGradient3892"
+       x1="71.4375"
+       y1="73.3638"
+       x2="95.1875"
+       y2="73.3638"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7394636,0,0,0.85345997,22.615901,-5.0537099)" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="4"
+     inkscape:cx="44.237719"
+     inkscape:cy="89.803167"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1176"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3056"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-209.62387,-366.61954)">
+    <path
+       style="fill:url(#linearGradient3819);fill-opacity:1;fill-rule:nonzero;stroke:none"
+       d="m 257.69388,449.31907 19.36869,-25.47123 0.12183,13.31226 38.19727,-0.12284 -0.075,6.02405 -0.13449,6.08323 -18.87668,0.0947 -18.87663,0.0947 z"
+       id="path3009"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:url(#linearGradient3842);fill-opacity:1;stroke:none"
+       d="m 316.26518,453.7251 -21.12083,26.32995 0.52673,-13.79272 -38.16925,0.1963 -0.11101,-6.36511 -0.11103,-6.36516 19.37338,-0.0398 19.37339,-0.0397 z"
+       id="path3009-7"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       sodipodi:type="star"
+       style="fill:url(#linearGradient3884);fill-opacity:1;fill-rule:nonzero;stroke:none"
+       id="path3850"
+       sodipodi:sides="5"
+       sodipodi:cx="-0.25"
+       sodipodi:cy="34.14505"
+       sodipodi:r1="9.1001377"
+       sodipodi:r2="7.3621659"
+       sodipodi:arg1="2.7763912"
+       sodipodi:arg2="3.4047097"
+       inkscape:flatsided="true"
+       inkscape:rounded="0"
+       inkscape:randomized="0"
+       d="M -8.7500003,37.39505 -5.9675784,27.065375 4.7163425,26.51957 8.5369469,36.51192 0.21428928,43.233336 z"
+       transform="matrix(-1.145546,-2.2946702,2.3615557,-1.1131011,154.96455,489.28033)"
+       inkscape:transform-center-x="1.383275"
+       inkscape:transform-center-y="-0.89868886" />
+    <path
+       sodipodi:type="arc"
+       style="fill:url(#linearGradient3892);fill-opacity:1;fill-rule:nonzero;stroke:none"
+       id="path3862"
+       sodipodi:cx="83.3125"
+       sodipodi:cy="73.3638"
+       sodipodi:rx="11.875"
+       sodipodi:ry="10.90625"
+       d="m 95.1875,73.3638 a 11.875,10.90625 0 1 1 -23.75,0 11.875,10.90625 0 1 1 23.75,0 z"
+       transform="matrix(2.0605263,0,0,2.1117478,171.11251,295.74995)" />
+  </g>
+</svg>
diff --git a/data/gonvert.colors b/data/gonvert.colors
new file mode 100644 (file)
index 0000000..cf2c5ab
--- /dev/null
@@ -0,0 +1,3 @@
+Your icon's dominant color is #2d82b6
+A suggested disabled color is #94d8ff
+A suggested pressed color is #25506a
diff --git a/data/gonvert.png b/data/gonvert.png
new file mode 100644 (file)
index 0000000..ba1e811
Binary files /dev/null and b/data/gonvert.png differ
diff --git a/data/gonvert.svg b/data/gonvert.svg
new file mode 100644 (file)
index 0000000..27e8550
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="80" width="80">
+ <defs>
+  <linearGradient id="hicg_overlay_grad" gradientUnits="userSpaceOnUse" x1="39.9995" y1="5.1816" x2="39.9995" y2="58.8019">
+   <stop offset="0" style="stop-color:#FFFFFF"/>
+   <stop offset="1" style="stop-color:#000000"/>
+  </linearGradient>
+  <filter id="hicg_drop_shadow">
+    <feOffset in="SourceAlpha" dx="0" dy="4"/>
+    <feGaussianBlur stdDeviation="4"/>
+       <feColorMatrix type="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 0.5 0" result="shadow"/>
+    <feBlend in="SourceGraphic" in2="shadow" mode="normal"/>
+  </filter>
+ </defs>
+ <g>
+  <path id="hicg_background" fill="#1c1913" d="M79,40c0,28.893-10.105,39-39,39S1,68.893,1,40C1,11.106,11.105,1,40,1S79,11.106,79,40z"/>
+  <path id="hicg_highlight" fill="#fff" opacity="0.25" d="M39.999,1C11.105,1,1,11.106,1,40c0,28.893,10.105,39,38.999,39   C68.896,79,79,68.893,79,40C79,11.106,68.896,1,39.999,1z M39.999,78.025C11.57,78.025,1.976,68.43,1.976,40   c0-28.429,9.595-38.024,38.023-38.024c28.43,0,38.024,9.596,38.024,38.024C78.023,68.43,68.429,78.025,39.999,78.025z"/>
+  <path id="hicg_overlay" opacity="0.4" fill="url(#hicg_overlay_grad)" d="M78.977,40c0,28.893-10.1,39-38.977,39S1.023,68.893,1.023,40c0-28.894,10.1-39,38.977-39S78.977,11.106,78.977,40z"/>
+ </g>
+<g xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" transform="translate(12, 12) scale(0.35443037974684)" filter="url(#hicg_drop_shadow)">
+  <defs xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" id="defs4">
+    <linearGradient id="linearGradient3876">
+      <stop style="stop-color:#620a0a;stop-opacity:1;" offset="0" id="stop3878"/>
+      <stop style="stop-color:#f13434;stop-opacity:1;" offset="1" id="stop3880"/>
+    </linearGradient>
+    <linearGradient id="linearGradient3866">
+      <stop style="stop-color:#c0d17e;stop-opacity:1;" offset="0" id="stop3868"/>
+      <stop style="stop-color:#88aa00;stop-opacity:1;" offset="1" id="stop3870"/>
+    </linearGradient>
+    <linearGradient id="linearGradient3854">
+      <stop style="stop-color:#631e1e;stop-opacity:1;" offset="0" id="stop3856"/>
+      <stop id="stop3874" offset="0.52424657" style="stop-color:#000000;stop-opacity:1;"/>
+      <stop style="stop-color:#121212;stop-opacity:1;" offset="1" id="stop3858"/>
+    </linearGradient>
+    <linearGradient id="linearGradient3836">
+      <stop style="stop-color:#2d82b6;stop-opacity:1;" offset="0" id="stop3838"/>
+      <stop style="stop-color:#2d82b6;stop-opacity:0;" offset="1" id="stop3840"/>
+    </linearGradient>
+    <linearGradient id="linearGradient3813">
+      <stop style="stop-color:#539dcb;stop-opacity:1;" offset="0" id="stop3815"/>
+      <stop id="stop3834" offset="1" style="stop-color:#2d82b6;stop-opacity:1;"/>
+    </linearGradient>
+    <pattern inkscape:collect="always" xlink:href="#pattern3095" id="pattern4605" patternTransform="matrix(0.77039422,0,0,0.77039422,-396.55415,198.23649)"/>
+    <pattern patternUnits="userSpaceOnUse" width="137.68516" height="102.49221" patternTransform="translate(-259.51537,281.93555)" id="pattern3095">
+      <path transform="matrix(0.34491466,0,0,0.34491466,146.49126,-63.454854)" d="m -90.887574,478.18354 -29.779156,-40.85689 -50.53918,1.36897 29.65494,-40.94713 -16.91943,-47.64259 48.10692,15.55016 40.082399,-30.8137 0.07677,50.55766 41.691717,28.59868 -48.059472,15.69618 z" inkscape:randomized="0" inkscape:rounded="0" inkscape:flatsided="false" sodipodi:arg2="2.0277357" sodipodi:arg1="1.3994172" sodipodi:r2="38.066818" sodipodi:r1="76.133636" sodipodi:cy="403.16522" sodipodi:cx="-103.87151" sodipodi:sides="5" id="path3075" style="color:#000000;fill:none;stroke:#ffffff;stroke-width:2.65499997;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" sodipodi:type="star"/>
+      <path transform="matrix(0.56327644,0,0,0.56327644,97.922089,-187.09579)" d="m -90.887574,478.18354 -29.779156,-40.85689 -50.53918,1.36897 29.65494,-40.94713 -16.91943,-47.64259 48.10692,15.55016 40.082399,-30.8137 0.07677,50.55766 41.691717,28.59868 -48.059472,15.69618 z" inkscape:randomized="0" inkscape:rounded="0" inkscape:flatsided="false" sodipodi:arg2="2.0277357" sodipodi:arg1="1.3994172" sodipodi:r2="38.066818" sodipodi:r1="76.133636" sodipodi:cy="403.16522" sodipodi:cx="-103.87151" sodipodi:sides="5" id="path3075-6" style="color:#000000;fill:none;stroke:#ffffff;stroke-width:2.65499997;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" sodipodi:type="star"/>
+    </pattern>
+    <linearGradient inkscape:collect="always" xlink:href="#linearGradient3813" id="linearGradient3819" x1="248.07614" y1="433.95383" x2="275.25555" y2="433.95383" gradientUnits="userSpaceOnUse" gradientTransform="translate(9.3621607e-6,-0.0049642)"/>
+    <linearGradient inkscape:collect="always" xlink:href="#linearGradient3813" id="linearGradient3842" x1="248.13908" y1="444.51163" x2="275.31851" y2="444.51163" gradientUnits="userSpaceOnUse" gradientTransform="translate(-1.4344049e-6,-0.00493514)"/>
+    <linearGradient inkscape:collect="always" xlink:href="#linearGradient3876" id="linearGradient3884" x1="-8.75" y1="34.876453" x2="8.5369473" y2="34.876453" gradientUnits="userSpaceOnUse" gradientTransform="translate(-10.225613,-4.7485267)"/>
+    <linearGradient inkscape:collect="always" xlink:href="#linearGradient3866" id="linearGradient3892" x1="71.4375" y1="73.3638" x2="95.1875" y2="73.3638" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.7394636,0,0,0.85345997,22.615901,-6.0007927)"/>
+  </defs>
+  <sodipodi:namedview xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="4" inkscape:cx="44.237719" inkscape:cy="89.803167" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" showguides="true" inkscape:guide-bbox="true" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" inkscape:window-width="1920" inkscape:window-height="1176" inkscape:window-x="0" inkscape:window-y="24" inkscape:window-maximized="1">
+    <inkscape:grid type="xygrid" id="grid3056" empspacing="5" visible="true" enabled="true" snapvisiblegridlinesonly="true"/>
+  </sodipodi:namedview>
+  <metadata xmlns="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" id="metadata7">
+    <rdf:RDF>
+      <cc:Work rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+        <dc:title/>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-209.62387,-366.61954)">
+    <path style="fill:url(#linearGradient3819);fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 257.69388,449.31907 19.36869,-25.47123 0.12183,13.31226 38.19727,-0.12284 -0.075,6.02405 -0.13449,6.08323 -18.87668,0.0947 -18.87663,0.0947 z" id="path3009" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccc"/>
+    <path style="fill:url(#linearGradient3842);fill-opacity:1;stroke:none" d="m 316.26518,453.7251 -21.12083,26.32995 0.52673,-13.79272 -38.16925,0.1963 -0.11101,-6.36511 -0.11103,-6.36516 19.37338,-0.0398 19.37339,-0.0397 z" id="path3009-7" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccc"/>
+    <path sodipodi:type="star" style="fill:url(#linearGradient3884);fill-opacity:1;fill-rule:nonzero;stroke:none" id="path3850" sodipodi:sides="5" sodipodi:cx="-0.25" sodipodi:cy="34.14505" sodipodi:r1="9.1001377" sodipodi:r2="7.3621659" sodipodi:arg1="2.7763912" sodipodi:arg2="3.4047097" inkscape:flatsided="true" inkscape:rounded="0" inkscape:randomized="0" d="M -8.7500003,37.39505 -5.9675784,27.065375 4.7163425,26.51957 8.5369469,36.51192 0.21428928,43.233336 z" transform="matrix(-1.145546,-2.2946702,2.3615557,-1.1131011,154.96455,489.28033)" inkscape:transform-center-x="1.383275" inkscape:transform-center-y="-0.89868886"/>
+    <path sodipodi:type="arc" style="fill:url(#linearGradient3892);fill-opacity:1;fill-rule:nonzero;stroke:none" id="path3862" sodipodi:cx="83.3125" sodipodi:cy="73.3638" sodipodi:rx="11.875" sodipodi:ry="10.90625" d="m 95.1875,73.3638 a 11.875,10.90625 0 1 1 -23.75,0 11.875,10.90625 0 1 1 23.75,0 z" transform="matrix(2.0605263,0,0,2.1117478,171.11251,297.74995)"/>
+  </g>
+</g></svg>
diff --git a/data/pixmaps/gonvert-icon_alternative.png b/data/pixmaps/gonvert-icon_alternative.png
deleted file mode 100644 (file)
index c6c1dcd..0000000
Binary files a/data/pixmaps/gonvert-icon_alternative.png and /dev/null differ
diff --git a/data/pixmaps/gonvert.png b/data/pixmaps/gonvert.png
deleted file mode 100644 (file)
index acd3837..0000000
Binary files a/data/pixmaps/gonvert.png and /dev/null differ
diff --git a/data/template.desktop b/data/template.desktop
new file mode 100644 (file)
index 0000000..2c2428c
--- /dev/null
@@ -0,0 +1,22 @@
+[Desktop Entry]
+Name=Gonvert
+GenericName=Unit Converter
+Comment=Unit Converter
+#[[[cog
+#      if VARIANT == "fremantle":
+#              cog.outl("Exec=/usr/bin/run-standalone.sh /usr/local/bin/%s" % PROGRAM)
+#      elif VARIANT == "harmattan":
+#              cog.outl("Exec=/usr/bin/invoker --single-instance --type=e /usr/local/bin/%s" % PROGRAM)
+#      elif VARIANT == "ubuntu":
+#              cog.outl("Exec=/usr/local/bin/%s" % PROGRAM)
+#      else:
+#              raise RuntimeError("Unsupported desktop file flavor %r" % PROGRAM)
+#]]]
+Exec=/usr/local/bin/Gonvert
+#[[[end]]]
+Icon=gonvert
+Categories=Engineering;Science;Education;Utility;Qt;
+Type=Application
+Encoding=UTF-8
+Version=1.0
+X-Osso-Type=application/x-executable
diff --git a/gonvert/__init__.py b/gonvert/__init__.py
new file mode 100644 (file)
index 0000000..4265cc3
--- /dev/null
@@ -0,0 +1 @@
+#!/usr/bin/env python
diff --git a/gonvert/constants.py b/gonvert/constants.py
new file mode 100644 (file)
index 0000000..66e3e1f
--- /dev/null
@@ -0,0 +1,13 @@
+import os
+
+__pretty_app_name__ = "Gonvert"
+__app_name__ = "gonvert"
+__version__ = "1.1.4"
+__build__ = 0
+__app_magic__ = 0xdeadbeef
+_data_path_ = os.path.join(os.path.expanduser("~"), ".%s" % __app_name__)
+_user_settings_ = "%s/settings.json" % _data_path_
+_user_logpath_ = "%s/%s.log" % (_data_path_, __app_name__)
+
+PROFILE_STARTUP = False
+IS_MAEMO = True
diff --git a/gonvert/converters.py b/gonvert/converters.py
new file mode 100644 (file)
index 0000000..219df1d
--- /dev/null
@@ -0,0 +1,292 @@
+"""
+All classes for conversions are defined below:
+ each class should have one method for converting "to_base and another for converting "from_base"
+the return value is the converted value to or from base
+"""
+
+# For the sake of eval'ing some code
+import math
+
+
+# used for Computer numbers base definitions.
+ALPHA_NUMERIC = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+
+
+def makeBase(x, base = len(ALPHA_NUMERIC), table=ALPHA_NUMERIC):
+       """
+       Convert from base 10 to any other base.
+       >> makeBase(1, 10)
+       '1'
+       >> makeBase(11, 10)
+       '11'
+       >> makeBase(11, 16)
+       'b'
+       """
+       div, mod = divmod(x, base)
+       if not div:
+               return table[mod]
+       return makeBase(div, base, table) + table[mod]
+
+
+# roman numerals
+roman_group = {
+       1: ('i', 'v'),
+       10: ('x', 'l'),
+       100: ('c', 'd'),
+       1000: ('m', 'A'),
+       10000: ('B', 'C'),
+}
+
+
+# functions that convert Arabic digits to roman numerals
+roman_value = {
+       0: lambda i, v, x: '',
+       1: lambda i, v, x: i,
+       2: lambda i, v, x: i+i,
+       3: lambda i, v, x: i+i+i,
+       4: lambda i, v, x: i+v,
+       5: lambda i, v, x: v,
+       6: lambda i, v, x: v+i,
+       7: lambda i, v, x: v+i+i,
+       8: lambda i, v, x: v+i+i+i,
+       9: lambda i, v, x: i+x,
+}
+
+
+def toroman(n):
+       """
+       convert a decimal number in [1,4000) to a roman numeral
+       >>> toroman(0)
+       >>> toroman(4001)
+       >>> toroman(1)
+       'i'
+       >>> toroman(4)
+       'iv'
+       >>> toroman(5)
+       'v'
+       >>> toroman(10)
+       'x'
+       >>> toroman(15)
+       'xv'
+       """
+       if n < 0:
+               raise NotImplementedError("Value out of roman comprehension")
+       elif n == 0:
+               pass
+       elif n >= 4000:
+               raise NotImplementedError("Value Out of Range")
+
+       base = 1
+       s = ''
+       while n > 0:
+               i, v = roman_group[base]
+               base = base * 10
+               x, l = roman_group[base]
+               digit = n % 10
+               n = (n-digit)/10
+               s = roman_value[digit](i, v, x) + s
+       return s
+
+
+def fromroman(s, rbase = 1):
+       """
+       convert a roman numeral (in lowercase) to a decimal integer
+       >>> fromroman('')
+       0
+       >>> fromroman('x')
+       5
+       >>> fromroman('xv')
+       15
+       """
+       if len(s) == 0:
+               return 0
+       elif rbase > 1000:
+               return 0
+
+       i, v = roman_group[rbase]
+       x, l = roman_group[rbase*10]
+       conversions = [
+               (v+i+i+i, 8),
+               (i+i+i+i, 5),
+               (v+i+i, 7),
+               (i+i+i, 3),
+               (v+i, 6),
+               (i+x, 9),
+               (i+v, 4),
+               (i+i, 2),
+               (i, 1),
+               (v, 5),
+       ]
+       for conversion in conversions:
+               if s.endswith(conversion[0]):
+                       digit = conversion[1]
+                       s = s[:-len(conversion[0])]
+                       break
+       else:
+               digit = 0
+               s = s
+
+       return digit * rbase + fromroman(s, rbase*10)
+
+
+class simple_multiplier(object):
+
+       def to_base(self, value, multiplier):
+               return value * (multiplier)
+
+       def from_base(self, value, multiplier):
+               if multiplier == 0:
+                       return 0.0
+               else:
+                       return value / (multiplier)
+
+
+class simple_inverter(object):
+
+       def to_base(self, value, multiplier):
+               if value == 0:
+                       return 0.0
+               else:
+                       return (multiplier) / value
+
+       def from_base(self, value, multiplier):
+               if value == 0:
+                       return 0.0
+               else:
+                       return (multiplier) / value
+
+
+class simple_gain_offset(object):
+
+       def to_base(self, value, (gain, offset)):
+               return (value * (gain)) + offset
+
+       def from_base(self, value, (gain, offset)):
+               if gain == 0:
+                       return 0.0
+               else:
+                       return (value - offset) / gain
+
+
+class simple_offset_gain(object):
+
+       def to_base(self, value, (offset, gain)):
+               return (value + offset) * gain
+
+       def from_base(self, value, (offset, gain)):
+               if gain == 0:
+                       return 0.0
+               else:
+                       return (value / gain) - offset
+
+
+class slope_offset(object):
+       ''"convert using points on a graph''"
+
+       def to_base(self, value, ((low_in, high_in), (low_out, high_out))):
+               gain = (high_out-low_out)/(high_in-low_in)
+               offset = low_out - gain*low_in
+               return gain*value+offset
+
+       def from_base(self, value, ((low_out, high_out), (low_in, high_in))):
+               gain = (high_out-low_out)/(high_in-low_in)
+               offset = low_out - gain*low_in
+               return gain*value+offset
+
+
+class double_slope_offset(object):
+       ''"convert using points on a graph, graph split into two slopes''"
+
+       def to_base(self, value, ((low1_in, high1_in), (low1_out, high1_out), (low2_in, high2_in), (low2_out, high2_out))):
+               if low1_in <= value <= high1_in:
+                       gain = (high1_out-low1_out)/(high1_in-low1_in)
+                       offset = low1_out - gain*low1_in
+                       return gain*value+offset
+               if low2_in <= value <= high2_in:
+                       gain = (high2_out-low2_out)/(high2_in-low2_in)
+                       offset = low2_out - gain*low2_in
+                       return gain*value+offset
+               return 0.0
+
+       def from_base(self, value, ((low1_in, high1_in), (low1_out, high1_out), (low2_in, high2_in), (low2_out, high2_out))):
+               if low1_out <= value <= high1_out:
+                       gain = (high1_in-low1_in)/(high1_out-low1_out)
+                       offset = low1_in - gain*low1_out
+                       return gain*value+offset
+               if low2_out <= value <= high2_out:
+                       gain = (high2_in-low2_in)/(high2_out-low2_out)
+                       offset = low2_in - gain*low2_out
+                       return gain*value+offset
+               return 0.0
+
+
+class base_converter(object):
+
+       def to_base(self, value, base):
+               """
+               Convert from any base to base 10 (decimal)
+               """
+               # Protection against fractional values
+               value = value.split(".", 1)[0]
+
+               result = 0L #will contain the long base-10 (decimal) number to be returned
+               position = len(value) #length of the string that is to be converted
+               for x in value:
+                       position = position-1
+                       result = long(result + long(long(ALPHA_NUMERIC.find(x))*(long(base)**long(position))))
+               return result
+
+       def from_base(self, value, base):
+               """
+               Convert from decimal to any base
+               """
+               return makeBase(int(value), base)
+
+
+class roman_numeral(object):
+
+       def to_base(self, value, junk):
+               """
+               Convert from roman numeral to base 10 (decimal)
+               """
+               if value == "0":
+                       return 0L
+               else:
+                       return fromroman(value)
+
+       def from_base(self, value, junk):
+               """
+               Convert from decimal to roman numeral
+               """
+               return toroman(int(value))
+
+
+
+class function(object):
+       ''"defined simple function can be as complicated as you like, however, both to/from base must be defined.''"
+
+       #value is assumed to be a string
+       #convert from a defined function to base
+       def to_base(self, value, (to_base, from_base)):
+               leftOfX, rightOfX = to_base.split("x", 1)
+               y = 0 # "undefined" y was driving me nuts
+               exec "y=" + leftOfX + str(value) + rightOfX
+               return y
+
+       def from_base(self, value, (to_base, from_base)):
+               leftOfX, rightOfX = from_base.split("x", 1)
+               y = 0 # "undefined" y was driving me nuts
+               exec "y=" + leftOfX + str(value) + rightOfX
+               return y
+
+
+#--------- function definitions from classes ------------
+m = simple_multiplier()
+inv = simple_inverter()
+gof = simple_gain_offset()
+ofg = simple_offset_gain()
+slo = slope_offset()
+dso = double_slope_offset()
+b = base_converter()
+r = roman_numeral()
+f = function()
diff --git a/gonvert/gonvert_qt.py b/gonvert/gonvert_qt.py
new file mode 100755 (executable)
index 0000000..aa3d7e5
--- /dev/null
@@ -0,0 +1,1577 @@
+#!/usr/bin/env python
+# -*- coding: UTF8 -*-
+
+#@todo Research Fn
+#@todo Research optimizations
+
+from __future__ import with_statement
+
+import os
+import math
+import simplejson
+import logging
+import logging.handlers
+
+import util.qt_compat as qt_compat
+QtCore = qt_compat.QtCore
+QtGui = qt_compat.import_module("QtGui")
+
+import constants
+from util import qui_utils
+from util import misc as misc_utils
+import unit_data
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+def split_number(number):
+       if number == 0.0:
+               # Optimize the startup case
+               return "0.", "0"
+
+       try:
+               fractional, integer = math.modf(number)
+       except TypeError:
+               integerDisplay = number
+               fractionalDisplay = ""
+       else:
+               integerDisplay = str(integer)
+               fractionalDisplay = str(fractional)
+               if "e+" in integerDisplay:
+                       integerDisplay = number
+                       fractionalDisplay = ""
+               elif "e-" in fractionalDisplay:
+                       if 0.0 < integer:
+                               integerDisplay = number
+                               fractionalDisplay = ""
+                       else:
+                               integerDisplay = ""
+                               fractionalDisplay = number
+               else:
+                       integerDisplay = integerDisplay[0:-2] + "."
+                       fractionalDisplay = fractionalDisplay[2:]
+
+       return integerDisplay, fractionalDisplay
+
+
+class Gonvert(object):
+
+       _DATA_PATHS = [
+               os.path.dirname(__file__),
+               os.path.join(os.path.dirname(__file__), "../share"),
+               os.path.join(os.path.dirname(__file__), "../data"),
+               '/usr/share/gonvert',
+               '/opt/gonvert/share',
+       ]
+
+       def __init__(self, app):
+               self._dataPath = ""
+               self._app = app
+               self._appIconPath = ""
+               self._recent = []
+               self._hiddenCategories = set()
+               self._hiddenUnits = {}
+               self._clipboard = QtGui.QApplication.clipboard()
+
+               self._jumpWindow = None
+               self._recentWindow = None
+               self._mainWindow = None
+               self._catWindow = None
+               self._quickWindow = None
+
+               self._on_jump_close = lambda obj = None: self._on_child_close("_jumpWindow", obj)
+               self._on_recent_close = lambda obj = None: self._on_child_close("_recentWindow", obj)
+               self._on_cat_close = lambda obj = None: self._on_child_close("_catWindow", obj)
+               self._on_quick_close = lambda obj = None: self._on_child_close("_quickWindow", obj)
+
+               self._condensedAction = QtGui.QAction(None)
+               self._condensedAction.setText("Condensed View")
+               self._condensedAction.setCheckable(True)
+               self._condensedAction.triggered.connect(self._on_condensed_start)
+
+               self._jumpAction = QtGui.QAction(None)
+               self._jumpAction.setText("Quick Jump")
+               self._jumpAction.setStatusTip("Search for a unit and jump straight to it")
+               self._jumpAction.setToolTip("Search for a unit and jump straight to it")
+               self._jumpAction.setShortcut(QtGui.QKeySequence("CTRL+j"))
+               self._jumpAction.triggered.connect(self._on_jump_start)
+
+               self._recentAction = QtGui.QAction(None)
+               self._recentAction.setText("Recent Units")
+               self._recentAction.setStatusTip("View the recent units")
+               self._recentAction.setToolTip("View the recent units")
+               self._recentAction.setShortcut(QtGui.QKeySequence("CTRL+r"))
+               self._recentAction.triggered.connect(self._on_recent_start)
+
+               self._fullscreenAction = QtGui.QAction(None)
+               self._fullscreenAction.setText("Fullscreen")
+               self._fullscreenAction.setCheckable(True)
+               self._fullscreenAction.setShortcut(QtGui.QKeySequence("CTRL+Enter"))
+               self._fullscreenAction.toggled.connect(self._on_toggle_fullscreen)
+
+               self._showFavoritesAction = QtGui.QAction(None)
+               self._showFavoritesAction.setCheckable(True)
+               self._showFavoritesAction.setText("Favorites Only")
+
+               self._sortActionGroup = QtGui.QActionGroup(None)
+               self._sortByNameAction = QtGui.QAction(self._sortActionGroup)
+               self._sortByNameAction.setText("Sort By Name")
+               self._sortByNameAction.setStatusTip("Sort the units by name")
+               self._sortByNameAction.setToolTip("Sort the units by name")
+               self._sortByNameAction.setCheckable(True)
+               self._sortByValueAction = QtGui.QAction(self._sortActionGroup)
+               self._sortByValueAction.setText("Sort By Value")
+               self._sortByValueAction.setStatusTip("Sort the units by value")
+               self._sortByValueAction.setToolTip("Sort the units by value")
+               self._sortByValueAction.setCheckable(True)
+               self._sortByUnitAction = QtGui.QAction(self._sortActionGroup)
+               self._sortByUnitAction.setText("Sort By Unit")
+               self._sortByUnitAction.setStatusTip("Sort the units by unit")
+               self._sortByUnitAction.setToolTip("Sort the units by unit")
+               self._sortByUnitAction.setCheckable(True)
+
+               self._sortByNameAction.setChecked(True)
+
+               self._logAction = QtGui.QAction(None)
+               self._logAction.setText("Log")
+               self._logAction.setShortcut(QtGui.QKeySequence("CTRL+l"))
+               self._logAction.triggered.connect(self._on_log)
+
+               self._quitAction = QtGui.QAction(None)
+               self._quitAction.setText("Quit")
+               self._quitAction.setShortcut(QtGui.QKeySequence("CTRL+q"))
+               self._quitAction.triggered.connect(self._on_quit)
+
+               self._app.lastWindowClosed.connect(self._on_app_quit)
+               self.load_settings()
+
+               self.request_category()
+               if self._recent:
+                       self._mainWindow.select_category(self._recent[-1][0])
+
+       def request_category(self):
+               if self._condensedAction.isChecked():
+                       if self._catWindow is not None:
+                               self._catWindow.hide()
+
+                       if self._quickWindow is None:
+                               self._quickWindow = QuickConvert(None, self)
+                               self._quickWindow.window.destroyed.connect(self._on_quick_close)
+                       else:
+                               self._quickWindow.show()
+
+                       self._mainWindow = self._quickWindow
+               else:
+                       if self._quickWindow is not None:
+                               self._quickWindow.hide()
+
+                       if self._catWindow is None:
+                               self._catWindow = CategoryWindow(None, self)
+                               self._catWindow.window.destroyed.connect(self._on_cat_close)
+                       else:
+                               self._catWindow.window.show()
+
+                       self._mainWindow = self._catWindow
+
+               return self._mainWindow
+
+       def search_units(self):
+               import windows
+               jumpWindow = windows.QuickJump(None, self)
+               jumpWindow.window.destroyed.connect(self._on_jump_close)
+               self._jumpWindow = jumpWindow
+               return self._jumpWindow
+
+       def show_recent(self):
+               import windows
+               recentWindow = windows.Recent(None, self)
+               recentWindow.window.destroyed.connect(self._on_recent_close)
+               self._recentWindow = recentWindow
+               return self._recentWindow
+
+       def add_recent(self, categoryName, unitName):
+               catUnit = categoryName, unitName
+               try:
+                       self._recent.remove(catUnit)
+               except ValueError:
+                       pass # ignore if its not already in the recent history
+               assert catUnit not in self._recent
+               self._recent.append(catUnit)
+
+       def get_recent_unit(self, categoryName, fromMostRecent = 0):
+               recentUnitName = ""
+               for catName, unitName in reversed(self._recent):
+                       if catName == categoryName:
+                               recentUnitName = unitName
+                               if fromMostRecent <= 0:
+                                       break
+                               else:
+                                       fromMostRecent -= 1
+               return recentUnitName
+
+       def get_recent(self):
+               return reversed(self._recent)
+
+       @property
+       def hiddenCategories(self):
+               return self._hiddenCategories
+
+       def get_hidden_units(self, categoryName):
+               try:
+                       return self._hiddenUnits[categoryName]
+               except KeyError:
+                       self._hiddenUnits[categoryName] = set()
+                       return self._hiddenUnits[categoryName]
+
+       def load_settings(self):
+               try:
+                       with open(constants._user_settings_, "r") as settingsFile:
+                               settings = simplejson.load(settingsFile)
+               except IOError, e:
+                       _moduleLogger.info("No settings")
+                       settings = {}
+               except ValueError:
+                       _moduleLogger.info("Settings were corrupt")
+                       settings = {}
+
+               self._fullscreenAction.setChecked(settings.get("isFullScreen", False))
+
+               sortBy = settings.get("sortBy", "name")
+               if sortBy not in ["name", "value", "unit"]:
+                       _moduleLogger.info("Setting sortBy is not a valid value: %s" % sortBy)
+                       sortBy = "name"
+               if sortBy == "name":
+                       self._sortByNameAction.setChecked(True)
+                       self._sortByValueAction.setChecked(False)
+                       self._sortByUnitAction.setChecked(False)
+               elif sortBy == "value":
+                       self._sortByNameAction.setChecked(False)
+                       self._sortByValueAction.setChecked(True)
+                       self._sortByUnitAction.setChecked(False)
+               elif sortBy == "unit":
+                       self._sortByNameAction.setChecked(False)
+                       self._sortByValueAction.setChecked(False)
+                       self._sortByUnitAction.setChecked(True)
+               else:
+                       raise RuntimeError("How did this sortBy come about? %s" % sortBy)
+
+               recent = settings.get("recent", self._recent)
+               for category, unit in recent:
+                       self.add_recent(category, unit)
+
+               self._hiddenCategories = set(settings.get("hiddenCategories", set()))
+               self._hiddenUnits = dict(
+                       (catName, set(units))
+                       for (catName, units) in settings.get("hiddenUnits", {}).iteritems()
+               )
+
+               self._showFavoritesAction.setChecked(settings.get("showFavorites", True))
+
+               self._condensedAction.setChecked(settings.get("useQuick", True))
+
+       def save_settings(self):
+               if self._sortByNameAction.isChecked():
+                       sortBy = "name"
+               elif self._sortByValueAction.isChecked():
+                       sortBy = "value"
+               elif self._sortByUnitAction.isChecked():
+                       sortBy = "unit"
+               else:
+                       raise RuntimeError("Unknown sorting value")
+               settings = {
+                       "isFullScreen": self._fullscreenAction.isChecked(),
+                       "recent": self._recent,
+                       "hiddenCategories": list(self._hiddenCategories),
+                       "hiddenUnits": dict(
+                               (catName, list(units))
+                               for (catName, units) in self._hiddenUnits.iteritems()
+                       ),
+                       "showFavorites": self._showFavoritesAction.isChecked(),
+                       "useQuick": self._condensedAction.isChecked(),
+                       "sortBy": sortBy,
+               }
+               with open(constants._user_settings_, "w") as settingsFile:
+                       simplejson.dump(settings, settingsFile)
+
+       @property
+       def appIconPath(self):
+               return self._appIconPath
+
+       @property
+       def jumpAction(self):
+               return self._jumpAction
+
+       @property
+       def recentAction(self):
+               return self._recentAction
+
+       @property
+       def fullscreenAction(self):
+               return self._fullscreenAction
+
+       @property
+       def condensedAction(self):
+               return self._condensedAction
+
+       @property
+       def sortByNameAction(self):
+               return self._sortByNameAction
+
+       @property
+       def sortByValueAction(self):
+               return self._sortByValueAction
+
+       @property
+       def sortByUnitAction(self):
+               return self._sortByUnitAction
+
+       @property
+       def logAction(self):
+               return self._logAction
+
+       @property
+       def quitAction(self):
+               return self._quitAction
+
+       @property
+       def showFavoritesAction(self):
+               return self._showFavoritesAction
+
+       def _walk_children(self):
+               if self._catWindow is not None:
+                       yield self._catWindow
+               if self._quickWindow is not None:
+                       yield self._quickWindow
+               if self._jumpWindow is not None:
+                       yield self._jumpWindow
+               if self._recentWindow is not None:
+                       yield self._recentWindow
+
+       def _close_windows(self):
+               if self._catWindow is not None:
+                       self._catWindow.window.destroyed.disconnect(self._on_cat_close)
+                       self._catWindow.close()
+                       self._catWindow = None
+               if self._quickWindow is not None:
+                       self._quickWindow.window.destroyed.disconnect(self._on_quick_close)
+                       self._quickWindow.close()
+                       self._quickWindow = None
+               if self._jumpWindow is not None:
+                       self._jumpWindow.window.destroyed.disconnect(self._on_jump_close)
+                       self._jumpWindow.close()
+                       self._jumpWindow = None
+               if self._recentWindow is not None:
+                       self._recentWindow.window.destroyed.disconnect(self._on_recent_close)
+                       self._recentWindow.close()
+                       self._recentWindow = None
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_app_quit(self, checked = False):
+               self.save_settings()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_child_close(self, name, obj = None):
+               if not hasattr(self, name):
+                       _moduleLogger.info("Something weird going on when we don't have a %s" % name)
+                       return
+               setattr(self, name, None)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_toggle_fullscreen(self, checked = False):
+               for window in self._walk_children():
+                       window.set_fullscreen(checked)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_condensed_start(self, checked = False):
+               self.request_category()
+               if self._recent:
+                       self._mainWindow.select_category(self._recent[-1][0])
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_jump_start(self, checked = False):
+               self.search_units()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_recent_start(self, checked = False):
+               self.show_recent()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_log(self, checked = False):
+               with open(constants._user_logpath_, "r") as f:
+                       logLines = f.xreadlines()
+                       log = "".join(logLines)
+                       self._clipboard.setText(log)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_quit(self, checked = False):
+               self._close_windows()
+
+
+class QuickConvert(object):
+
+       def __init__(self, parent, app):
+               self._app = app
+               self._categoryName = ""
+               self._inputUnitName = ""
+               self._outputUnitName = ""
+               self._unitNames = []
+               self._favoritesWindow = None
+
+               self._inputUnitValue = QtGui.QLineEdit()
+               qui_utils.mark_numbers_preferred(self._inputUnitValue)
+               self._inputUnitValue.textEdited.connect(self._on_value_edited)
+               self._inputUnitSymbol = QtGui.QLabel()
+
+               self._outputUnitValue = QtGui.QLineEdit()
+               qui_utils.mark_numbers_preferred(self._outputUnitValue)
+               self._outputUnitValue.textEdited.connect(self._on_output_value_edited)
+               self._outputUnitSymbol = QtGui.QLabel()
+
+               self._conversionLayout = QtGui.QHBoxLayout()
+               self._conversionLayout.addWidget(self._inputUnitValue)
+               self._conversionLayout.addWidget(self._inputUnitSymbol)
+               self._conversionLayout.addWidget(self._outputUnitValue)
+               self._conversionLayout.addWidget(self._outputUnitSymbol)
+
+               self._categoryView = QtGui.QTreeWidget()
+               self._categoryView.setHeaderLabels(["Categories"])
+               self._categoryView.setHeaderHidden(False)
+               self._categoryView.setRootIsDecorated(False)
+               if not constants.IS_MAEMO:
+                       self._categoryView.setAlternatingRowColors(True)
+               self._categoryView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+               self._categoryView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
+               for catName in unit_data.UNIT_CATEGORIES:
+                       twi = QtGui.QTreeWidgetItem(self._categoryView)
+                       twi.setText(0, catName)
+               self._categorySelection = self._categoryView.selectionModel()
+               self._categorySelection.selectionChanged.connect(self._on_category_selection_changed)
+
+               self._inputView = QtGui.QTreeWidget()
+               self._inputView.setHeaderLabels(["From", "Name"])
+               self._inputView.setHeaderHidden(False)
+               self._inputView.header().hideSection(1)
+               self._inputView.setRootIsDecorated(False)
+               if not constants.IS_MAEMO:
+                       self._inputView.setAlternatingRowColors(True)
+               self._inputView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+               self._inputView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
+               self._inputSelection = self._inputView.selectionModel()
+               self._inputSelection.selectionChanged.connect(self._on_input_selection_changed)
+
+               self._outputView = QtGui.QTreeWidget()
+               self._outputView.setHeaderLabels(["To", "Name"])
+               self._outputView.setHeaderHidden(False)
+               self._outputView.header().hideSection(1)
+               self._outputView.setRootIsDecorated(False)
+               if not constants.IS_MAEMO:
+                       self._outputView.setAlternatingRowColors(True)
+               self._outputView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+               self._outputView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
+               self._outputWidgets = []
+               self._outputSelection = self._outputView.selectionModel()
+               self._outputSelection.selectionChanged.connect(self._on_output_selection_changed)
+
+               self._selectionLayout = QtGui.QHBoxLayout()
+               self._selectionLayout.addWidget(self._categoryView)
+               self._selectionLayout.addWidget(self._inputView)
+               self._selectionLayout.addWidget(self._outputView)
+
+               self._layout = QtGui.QVBoxLayout()
+               self._layout.addLayout(self._conversionLayout)
+               self._layout.addLayout(self._selectionLayout)
+
+               centralWidget = QtGui.QWidget()
+               centralWidget.setLayout(self._layout)
+
+               self._window = QtGui.QMainWindow(parent)
+               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
+               qui_utils.set_stackable(self._window, True)
+               self._window.setWindowTitle("%s - Quick Convert" % (constants.__pretty_app_name__, ))
+               self._window.setWindowIcon(QtGui.QIcon(app.appIconPath))
+               self._window.setCentralWidget(centralWidget)
+
+               self._chooseCatFavoritesAction = QtGui.QAction(None)
+               self._chooseCatFavoritesAction.setText("Select Categories")
+               self._chooseCatFavoritesAction.triggered.connect(self._on_choose_category_favorites)
+
+               self._chooseUnitFavoritesAction = QtGui.QAction(None)
+               self._chooseUnitFavoritesAction.setText("Select Units")
+               self._chooseUnitFavoritesAction.triggered.connect(self._on_choose_unit_favorites)
+               self._chooseUnitFavoritesAction.setEnabled(False)
+
+               self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
+
+               self._closeWindowAction = QtGui.QAction(None)
+               self._closeWindowAction.setText("Close Window")
+               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
+               self._closeWindowAction.triggered.connect(self._on_close_window)
+
+               if constants.IS_MAEMO:
+                       self._window.addAction(self._closeWindowAction)
+                       self._window.addAction(self._app.quitAction)
+                       self._window.addAction(self._app.fullscreenAction)
+
+                       fileMenu = self._window.menuBar().addMenu("&Units")
+                       fileMenu.addAction(self._chooseCatFavoritesAction)
+                       fileMenu.addAction(self._chooseUnitFavoritesAction)
+
+                       viewMenu = self._window.menuBar().addMenu("&View")
+                       viewMenu.addAction(self._app.showFavoritesAction)
+                       viewMenu.addAction(self._app.condensedAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.jumpAction)
+                       viewMenu.addAction(self._app.recentAction)
+               else:
+                       fileMenu = self._window.menuBar().addMenu("&Units")
+                       fileMenu.addAction(self._chooseCatFavoritesAction)
+                       fileMenu.addAction(self._chooseUnitFavoritesAction)
+                       fileMenu.addAction(self._closeWindowAction)
+                       fileMenu.addAction(self._app.quitAction)
+
+                       viewMenu = self._window.menuBar().addMenu("&View")
+                       viewMenu.addAction(self._app.showFavoritesAction)
+                       viewMenu.addAction(self._app.condensedAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.jumpAction)
+                       viewMenu.addAction(self._app.recentAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.fullscreenAction)
+
+               self._window.addAction(self._app.logAction)
+
+               self._update_favorites()
+               self.set_fullscreen(self._app.fullscreenAction.isChecked())
+               self._window.show()
+
+       @property
+       def window(self):
+               return self._window
+
+       def show(self):
+               self._window.show()
+
+       def hide(self):
+               self._window.hide()
+
+       def close(self):
+               self._window.close()
+
+       def set_fullscreen(self, isFullscreen):
+               if isFullscreen:
+                       self._window.showFullScreen()
+               else:
+                       self._window.showNormal()
+
+       def select_category(self, categoryName):
+               self._select_category(categoryName)
+
+               i = unit_data.UNIT_CATEGORIES.index(categoryName)
+               rootIndex = self._categoryView.rootIndex()
+               currentIndex = self._categoryView.model().index(i, 0, rootIndex)
+               self._categoryView.scrollTo(currentIndex)
+               self._categoryView.setItemSelected(self._categoryView.topLevelItem(i), True)
+
+               return self
+
+       def select_unit(self, name):
+               self.select_input(name)
+               return self
+
+       def select_input(self, name):
+               self._select_input(name)
+
+               i = self._unitNames.index(name)
+               rootIndex = self._inputView.rootIndex()
+               currentIndex = self._inputView.model().index(i, 0, rootIndex)
+               self._inputView.scrollTo(currentIndex)
+               self._inputView.setItemSelected(self._inputView.topLevelItem(i), True)
+
+       def select_output(self, name):
+               self._select_output(name)
+
+               i = self._unitNames.index(name)
+               rootIndex = self._outputView.rootIndex()
+               currentIndex = self._outputView.model().index(i, 0, rootIndex)
+               self._outputView.scrollTo(currentIndex)
+               self._outputView.setItemSelected(self._outputView.topLevelItem(i), True)
+
+       def _select_category(self, categoryName):
+               self._inputUnitName = ""
+               self._outputUnitName = ""
+               self._inputUnitValue.setText("")
+               self._inputUnitSymbol.setText("")
+               self._inputView.clear()
+               self._outputUnitValue.setText("")
+               self._outputUnitSymbol.setText("")
+               self._outputView.clear()
+               self._categoryName = categoryName
+               self._chooseUnitFavoritesAction.setEnabled(True)
+
+               unitData = unit_data.UNIT_DESCRIPTIONS[categoryName]
+               self._unitNames = list(unit_data.get_units(categoryName))
+               self._unitNames.sort()
+               for key in self._unitNames:
+                       conversion, unit, description = unitData[key]
+                       unit = key
+
+                       twi = QtGui.QTreeWidgetItem(self._inputView)
+                       twi.setText(0, unit)
+                       twi.setText(1, key)
+
+                       twi = QtGui.QTreeWidgetItem(self._outputView)
+                       twi.setText(0, unit)
+                       twi.setText(1, key)
+
+               defaultInputUnitName = self._app.get_recent_unit(categoryName)
+               if defaultInputUnitName:
+                       self.select_input(defaultInputUnitName)
+                       defaultOutputUnitName = self._app.get_recent_unit(categoryName, 1)
+                       assert defaultOutputUnitName
+                       self.select_output(defaultOutputUnitName)
+
+       def _select_input(self, name):
+               self._app.add_recent(self._categoryName, name)
+               self._inputUnitName = name
+
+               unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
+               conversion, unit, description = unitData[name]
+
+               self._inputUnitSymbol.setText(unit if unit else name)
+
+               if "" not in [self._categoryName, self._inputUnitName, self._outputUnitName]:
+                       self._update_output()
+
+       def _select_output(self, name):
+               # Add the output to recent but don't make things weird by making it the most recent
+               self._app.add_recent(self._categoryName, name)
+               self._app.add_recent(self._categoryName, self._inputUnitName)
+               self._outputUnitName = name
+
+               unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
+               conversion, unit, description = unitData[name]
+
+               self._outputUnitSymbol.setText(unit if unit else name)
+
+               if "" not in [self._categoryName, self._inputUnitName, self._outputUnitName]:
+                       self._update_output()
+
+       def _sanitize_value(self, userEntry):
+               if self._categoryName == "Computer Numbers":
+                       if userEntry == '':
+                               value = '0'
+                       else:
+                               value = userEntry
+               else:
+                       if userEntry == '':
+                               value = 0.0
+                       else:
+                               value = float(userEntry)
+               return value
+
+       def _update_output(self):
+               assert self._categoryName
+               assert self._inputUnitName
+               assert self._outputUnitName
+
+               userInput = str(self._inputUnitValue.text())
+               value = self._sanitize_value(userInput)
+
+               unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
+               inputConversion, _, _ = unitData[self._inputUnitName]
+               outputConversion, _, _ = unitData[self._outputUnitName]
+
+               func, arg = inputConversion
+               base = func.to_base(value, arg)
+
+               func, arg = outputConversion
+               newValue = func.from_base(base, arg)
+               self._outputUnitValue.setText(str(newValue))
+
+       def _update_input(self):
+               assert self._categoryName
+               assert self._inputUnitName
+               assert self._outputUnitName
+
+               userOutput = str(self._outputUnitValue.text())
+               value = self._sanitize_value(userOutput)
+
+               unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
+               inputConversion, _, _ = unitData[self._inputUnitName]
+               outputConversion, _, _ = unitData[self._outputUnitName]
+
+               func, arg = outputConversion
+               base = func.to_base(value, arg)
+
+               func, arg = inputConversion
+               newValue = func.from_base(base, arg)
+               self._inputUnitValue.setText(str(newValue))
+
+       def _update_favorites(self):
+               if self._app.showFavoritesAction.isChecked():
+                       assert self._categoryView.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
+                       for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
+                               if catName in self._app.hiddenCategories:
+                                       self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), True)
+                               else:
+                                       self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), False)
+
+                       for i, unitName in enumerate(self._unitNames):
+                               if unitName in self._app.get_hidden_units(self._categoryName):
+                                       self._inputView.setRowHidden(i, self._inputView.rootIndex(), True)
+                                       self._outputView.setRowHidden(i, self._outputView.rootIndex(), True)
+                               else:
+                                       self._inputView.setRowHidden(i, self._inputView.rootIndex(), False)
+                                       self._outputView.setRowHidden(i, self._outputView.rootIndex(), False)
+               else:
+                       for i in xrange(self._categoryView.topLevelItemCount()):
+                               self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), False)
+
+                       for i in xrange(len(self._unitNames)):
+                               self._inputView.setRowHidden(i, self._inputView.rootIndex(), False)
+                               self._outputView.setRowHidden(i, self._outputView.rootIndex(), False)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_window(self, checked = True):
+               self.close()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_show_favorites(self, checked = True):
+               if checked:
+                       assert self._categoryView.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
+                       for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
+                               if catName in self._app.hiddenCategories:
+                                       self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), True)
+
+                       for i, unitName in enumerate(self._unitNames):
+                               if unitName in self._app.get_hidden_units(self._categoryName):
+                                       self._inputView.setRowHidden(i, self._inputView.rootIndex(), True)
+                                       self._outputView.setRowHidden(i, self._outputView.rootIndex(), True)
+               else:
+                       for i in xrange(self._categoryView.topLevelItemCount()):
+                               self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), False)
+
+                       for i in xrange(len(self._unitNames)):
+                               self._inputView.setRowHidden(i, self._inputView.rootIndex(), False)
+                               self._outputView.setRowHidden(i, self._outputView.rootIndex(), False)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_choose_category_favorites(self, obj = None):
+               assert self._favoritesWindow is None
+               import windows
+               self._favoritesWindow = windows.FavoritesWindow(
+                       self._window,
+                       self._app,
+                       unit_data.UNIT_CATEGORIES,
+                       self._app.hiddenCategories
+               )
+               self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
+               return self._favoritesWindow
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_choose_unit_favorites(self, obj = None):
+               assert self._favoritesWindow is None
+               import windows
+               self._favoritesWindow = windows.FavoritesWindow(
+                       self._window,
+                       self._app,
+                       unit_data.get_units(self._categoryName),
+                       self._app.get_hidden_units(self._categoryName)
+               )
+               self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
+               return self._favoritesWindow
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_favorites(self, obj = None):
+               self._favoritesWindow = None
+               self._update_favorites()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_value_edited(self, *args):
+               self._update_output()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_output_value_edited(self, *args):
+               self._update_input()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_category_selection_changed(self, selected, deselected):
+               selectedNames = [
+                       str(item.text(0))
+                       for item in self._categoryView.selectedItems()
+               ]
+               assert len(selectedNames) == 1
+               self._select_category(selectedNames[0])
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_input_selection_changed(self, selected, deselected):
+               selectedNames = [
+                       str(item.text(1))
+                       for item in self._inputView.selectedItems()
+               ]
+               if selectedNames:
+                       assert len(selectedNames) == 1
+                       name = selectedNames[0]
+                       self._select_input(name)
+               else:
+                       pass
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_output_selection_changed(self, selected, deselected):
+               selectedNames = [
+                       str(item.text(1))
+                       for item in self._outputView.selectedItems()
+               ]
+               if selectedNames:
+                       assert len(selectedNames) == 1, selectedNames
+                       name = selectedNames[0]
+                       self._select_output(name)
+               else:
+                       pass
+
+
+class CategoryWindow(object):
+
+       def __init__(self, parent, app):
+               self._app = app
+               self._unitWindow = None
+               self._favoritesWindow = None
+
+               self._categories = QtGui.QTreeWidget()
+               self._categories.setHeaderLabels(["Categories"])
+               self._categories.itemClicked.connect(self._on_category_clicked)
+               self._categories.setHeaderHidden(True)
+               self._categories.setRootIsDecorated(False)
+               self._categories.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+               self._categories.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
+               if not constants.IS_MAEMO:
+                       self._categories.setAlternatingRowColors(True)
+               for catName in unit_data.UNIT_CATEGORIES:
+                       twi = QtGui.QTreeWidgetItem(self._categories)
+                       twi.setText(0, catName)
+
+               self._layout = QtGui.QVBoxLayout()
+               self._layout.addWidget(self._categories)
+
+               centralWidget = QtGui.QWidget()
+               centralWidget.setLayout(self._layout)
+
+               self._window = QtGui.QMainWindow(parent)
+               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
+               qui_utils.set_stackable(self._window, True)
+               self._window.setWindowTitle("%s - Categories" % constants.__pretty_app_name__)
+               self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
+               self._window.setCentralWidget(centralWidget)
+
+               self._chooseFavoritesAction = QtGui.QAction(None)
+               self._chooseFavoritesAction.setText("Select Favorites")
+               self._chooseFavoritesAction.setShortcut(QtGui.QKeySequence("CTRL+f"))
+               self._chooseFavoritesAction.triggered.connect(self._on_choose_favorites)
+
+               self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
+
+               self._closeWindowAction = QtGui.QAction(None)
+               self._closeWindowAction.setText("Close")
+               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
+               self._closeWindowAction.triggered.connect(self._on_close_window)
+
+               if constants.IS_MAEMO:
+                       fileMenu = self._window.menuBar().addMenu("&Units")
+                       fileMenu.addAction(self._chooseFavoritesAction)
+
+                       viewMenu = self._window.menuBar().addMenu("&View")
+                       viewMenu.addAction(self._app.showFavoritesAction)
+                       viewMenu.addAction(self._app.condensedAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.jumpAction)
+                       viewMenu.addAction(self._app.recentAction)
+
+                       self._window.addAction(self._closeWindowAction)
+                       self._window.addAction(self._app.quitAction)
+                       self._window.addAction(self._app.fullscreenAction)
+               else:
+                       fileMenu = self._window.menuBar().addMenu("&Units")
+                       fileMenu.addAction(self._chooseFavoritesAction)
+                       fileMenu.addAction(self._closeWindowAction)
+                       fileMenu.addAction(self._app.quitAction)
+
+                       viewMenu = self._window.menuBar().addMenu("&View")
+                       viewMenu.addAction(self._app.showFavoritesAction)
+                       viewMenu.addAction(self._app.condensedAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.jumpAction)
+                       viewMenu.addAction(self._app.recentAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.fullscreenAction)
+
+               self._window.addAction(self._app.logAction)
+
+               self._update_favorites()
+               self.set_fullscreen(self._app.fullscreenAction.isChecked())
+               self._window.show()
+
+       @property
+       def window(self):
+               return self._window
+
+       def walk_children(self):
+               if self._unitWindow is not None:
+                       yield self._unitWindow
+               if self._favoritesWindow is not None:
+                       yield self._favoritesWindow
+
+       def show(self):
+               self._window.show()
+               for child in self.walk_children():
+                       child.show()
+
+       def hide(self):
+               for child in self.walk_children():
+                       child.hide()
+               self._window.hide()
+
+       def close(self):
+               for child in self.walk_children():
+                       child.window.destroyed.disconnect(self._on_child_close)
+                       child.close()
+               self._window.close()
+
+       def select_category(self, categoryName):
+               self._select_category(categoryName)
+
+               i = unit_data.UNIT_CATEGORIES.index(categoryName)
+               rootIndex = self._categories.rootIndex()
+               currentIndex = self._categories.model().index(i, 0, rootIndex)
+               self._categories.scrollTo(currentIndex)
+               self._categories.setItemSelected(self._categories.topLevelItem(i), True)
+               return self._unitWindow
+
+       def set_fullscreen(self, isFullscreen):
+               if isFullscreen:
+                       self._window.showFullScreen()
+               else:
+                       self._window.showNormal()
+               for child in self.walk_children():
+                       child.set_fullscreen(isFullscreen)
+
+       def _select_category(self, categoryName):
+               for child in self.walk_children():
+                       child.window.destroyed.disconnect(self._on_child_close)
+                       child.close()
+               self._unitWindow = UnitWindow(self._window, categoryName, self._app)
+               self._unitWindow.window.destroyed.connect(self._on_child_close)
+
+       def _update_favorites(self):
+               if self._app.showFavoritesAction.isChecked():
+                       assert self._categories.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
+                       for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
+                               if catName in self._app.hiddenCategories:
+                                       self._categories.setRowHidden(i, self._categories.rootIndex(), True)
+                               else:
+                                       self._categories.setRowHidden(i, self._categories.rootIndex(), False)
+               else:
+                       for i in xrange(self._categories.topLevelItemCount()):
+                               self._categories.setRowHidden(i, self._categories.rootIndex(), False)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_show_favorites(self, checked = True):
+               if checked:
+                       assert self._categories.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
+                       for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
+                               if catName in self._app.hiddenCategories:
+                                       self._categories.setRowHidden(i, self._categories.rootIndex(), True)
+               else:
+                       for i in xrange(self._categories.topLevelItemCount()):
+                               self._categories.setRowHidden(i, self._categories.rootIndex(), False)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_choose_favorites(self, obj = None):
+               assert self._favoritesWindow is None
+               import windows
+               self._favoritesWindow = windows.FavoritesWindow(
+                       self._window,
+                       self._app,
+                       unit_data.UNIT_CATEGORIES,
+                       self._app.hiddenCategories
+               )
+               self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
+               return self._favoritesWindow
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_favorites(self, obj = None):
+               self._favoritesWindow = None
+               self._update_favorites()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_child_close(self, obj = None):
+               self._unitWindow = None
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_window(self, checked = True):
+               self.close()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_category_clicked(self, item, columnIndex):
+               categoryName = unicode(item.text(0))
+               self.select_category(categoryName)
+
+
+class UnitData(object):
+
+       HEADERS = ["Name", "Value", "", "Unit"]
+       ALIGNMENT = [QtCore.Qt.AlignLeft, QtCore.Qt.AlignRight, QtCore.Qt.AlignLeft, QtCore.Qt.AlignLeft]
+       NAME_COLUMN = 0
+       VALUE_COLUMN_0 = 1
+       VALUE_COLUMN_1 = 2
+       UNIT_COLUMN = 3
+
+       __slots__ = [
+               "_name", "_unit", "_description", "_conversion",
+               "_value", "_integerDisplay", "_fractionalDisplay",
+       ]
+
+       def __init__(self, name, unit, description, conversion):
+               self._name = name
+               self._unit = unit
+               self._description = description
+               self._conversion = conversion
+
+               self._value = 0.0
+               self._integerDisplay, self._fractionalDisplay = split_number(self._value)
+
+       @property
+       def name(self):
+               return self._name
+
+       @property
+       def value(self):
+               return self._value
+
+       def update_value(self, newValue):
+               self._value = newValue
+               self._integerDisplay, self._fractionalDisplay = split_number(newValue)
+
+       @property
+       def unit(self):
+               return self._unit
+
+       @property
+       def conversion(self):
+               return self._conversion
+
+       def data(self, column):
+               try:
+                       return [self._name, self._integerDisplay, self._fractionalDisplay, self._unit][column]
+               except IndexError:
+                       return None
+
+
+class UnitModel(QtCore.QAbstractItemModel):
+
+       def __init__(self, categoryName, parent=None):
+               super(UnitModel, self).__init__(parent)
+               self._categoryName = categoryName
+               self._unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
+               if self._categoryName == "Computer Numbers":
+                       self._sanitize_value = self._sanitize_alpha_value
+               else:
+                       self._sanitize_value = self._sanitize_numeric_value
+
+               self._children = []
+               for key in unit_data.get_units(self._categoryName):
+                       conversion, unit, description = self._unitData[key]
+                       self._children.append(UnitData(key, unit, description, conversion))
+               self._sortSettings = None
+
+       @misc_utils.log_exception(_moduleLogger)
+       def columnCount(self, parent):
+               if parent.isValid():
+                       return 0
+               else:
+                       return len(UnitData.HEADERS)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def data(self, index, role):
+               #if not index.isValid():
+               #       return None
+
+               if role == QtCore.Qt.DisplayRole:
+                       item = index.internalPointer()
+                       if isinstance(item, UnitData):
+                               return item.data(index.column())
+                       elif item is UnitData.HEADERS:
+                               return item[index.column()]
+               elif role == QtCore.Qt.TextAlignmentRole:
+                       return UnitData.ALIGNMENT[index.column()]
+               else:
+                       return None
+
+       @misc_utils.log_exception(_moduleLogger)
+       def sort(self, column, order = QtCore.Qt.AscendingOrder):
+               self._sortSettings = column, order
+               isReverse = order == QtCore.Qt.AscendingOrder
+               if column == UnitData.NAME_COLUMN:
+                       key_func = lambda item: item.name
+               elif column in [UnitData.VALUE_COLUMN_0, UnitData.VALUE_COLUMN_1]:
+                       key_func = lambda item: item.value
+               elif column == UnitData.UNIT_COLUMN:
+                       key_func = lambda item: item.unit
+               self._children.sort(key=key_func, reverse = isReverse)
+
+               self._all_changed()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def flags(self, index):
+               if not index.isValid():
+                       return QtCore.Qt.NoItemFlags
+
+               return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
+
+       @misc_utils.log_exception(_moduleLogger)
+       def headerData(self, section, orientation, role):
+               if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
+                       return UnitData.HEADERS[section]
+
+               return None
+
+       @misc_utils.log_exception(_moduleLogger)
+       def index(self, row, column, parent):
+               #if not self.hasIndex(row, column, parent):
+               #       return QtCore.QModelIndex()
+               #elif parent.isValid():
+               #       return QtCore.QModelIndex()
+
+               parentItem = UnitData.HEADERS
+               childItem = self._children[row]
+               if childItem:
+                       return self.createIndex(row, column, childItem)
+               else:
+                       return QtCore.QModelIndex()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def parent(self, index):
+               if not index.isValid():
+                       return QtCore.QModelIndex()
+
+               childItem = index.internalPointer()
+               if isinstance(childItem, UnitData):
+                       return QtCore.QModelIndex()
+               elif childItem is UnitData.HEADERS:
+                       return None
+
+       @misc_utils.log_exception(_moduleLogger)
+       def rowCount(self, parent):
+               if 0 < parent.column():
+                       return 0
+
+               if not parent.isValid():
+                       return len(self._children)
+               else:
+                       return len(self._children)
+
+       def get_unit(self, index):
+               assert 0 <= index
+               return self._children[index]
+
+       def get_unit_names(self):
+               for child in self._children:
+                       yield child.name
+
+       def index_unit(self, unitName):
+               for i, child in enumerate(self._children):
+                       if child.name == unitName:
+                               return i
+               else:
+                       raise RuntimeError("Unit not found")
+
+       def update_values(self, fromIndex, userInput):
+               value = self._sanitize_value(userInput)
+               func, arg = self._children[fromIndex].conversion
+               base = func.to_base(value, arg)
+               for i, child in enumerate(self._children):
+                       func, arg = child.conversion
+                       newValue = func.from_base(base, arg)
+                       child.update_value(newValue)
+
+               if (
+                       self._sortSettings is not None and
+                       self._sortSettings[0]  in [UnitData.VALUE_COLUMN_0, UnitData.VALUE_COLUMN_1]
+               ):
+                       # Sort takes care of marking everything as changed
+                       self.sort(*self._sortSettings)
+                       return True
+               else:
+                       self._values_changed()
+                       return False
+
+       def __len__(self):
+               return len(self._children)
+
+       def _values_changed(self):
+               topLeft = self.createIndex(0, UnitData.VALUE_COLUMN_0, self._children[0])
+               bottomRight = self.createIndex(len(self._children)-1, UnitData.VALUE_COLUMN_1, self._children[-1])
+               self.dataChanged.emit(topLeft, bottomRight)
+
+       def _all_changed(self):
+               topLeft = self.createIndex(0, 0, self._children[0])
+               bottomRight = self.createIndex(len(self._children)-1, len(UnitData.HEADERS)-1, self._children[-1])
+               self.dataChanged.emit(topLeft, bottomRight)
+
+       def _sanitize_alpha_value(self, userEntry):
+               if userEntry:
+                       value = userEntry
+               else:
+                       value = '0'
+               return value
+
+       def _sanitize_numeric_value(self, userEntry):
+               if userEntry:
+                       value = float(userEntry)
+               else:
+                       value = 0.0
+               return value
+
+
+class UnitWindow(object):
+
+       def __init__(self, parent, category, app):
+               self._app = app
+               self._categoryName = category
+               self._selectedIndex = 0
+               self._favoritesWindow = None
+
+               self._selectedUnitName = QtGui.QLabel()
+               self._selectedUnitValue = QtGui.QLineEdit()
+               self._selectedUnitValue.textEdited.connect(self._on_value_edited)
+               qui_utils.mark_numbers_preferred(self._selectedUnitValue)
+               self._selectedUnitSymbol = QtGui.QLabel()
+               self._updateDelayTimer = QtCore.QTimer()
+               self._updateDelayTimer.setInterval(100)
+               self._updateDelayTimer.setSingleShot(True)
+               self._updateDelayTimer.timeout.connect(self._on_value_edited_delayed)
+
+               self._selectedUnitLayout = QtGui.QHBoxLayout()
+               self._selectedUnitLayout.addWidget(self._selectedUnitName)
+               self._selectedUnitLayout.addWidget(self._selectedUnitValue)
+               self._selectedUnitLayout.addWidget(self._selectedUnitSymbol)
+
+               self._unitsModel = UnitModel(self._categoryName)
+               self._unitsView = QtGui.QTreeView()
+               self._unitsView.setModel(self._unitsModel)
+               self._unitsView.setUniformRowHeights(True)
+               self._unitsView.setSortingEnabled(True)
+               self._unitsView.setRootIsDecorated(False)
+               self._unitsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+               self._unitsView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+               self._unitsView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
+               self._unitsView.setHeaderHidden(True)
+               self._unitsView.clicked.connect(self._on_unit_clicked)
+               if not constants.IS_MAEMO:
+                       self._unitsView.setAlternatingRowColors(True)
+
+               viewHeader = self._unitsView.header()
+               viewHeader.setSortIndicatorShown(True)
+               viewHeader.setClickable(True)
+
+               viewHeader.setResizeMode(UnitData.NAME_COLUMN, QtGui.QHeaderView.ResizeToContents)
+               viewHeader.setResizeMode(UnitData.VALUE_COLUMN_0, QtGui.QHeaderView.ResizeToContents)
+               viewHeader.setResizeMode(UnitData.VALUE_COLUMN_1, QtGui.QHeaderView.ResizeToContents)
+               viewHeader.setResizeMode(UnitData.UNIT_COLUMN, QtGui.QHeaderView.ResizeToContents)
+               viewHeader.setStretchLastSection(False)
+
+               # Trying to make things faster by locking in the initial size of the immutable columns
+               nameSize = min(viewHeader.sectionSize(UnitData.NAME_COLUMN), 300)
+               viewHeader.setResizeMode(UnitData.NAME_COLUMN, QtGui.QHeaderView.Fixed)
+               viewHeader.resizeSection(UnitData.NAME_COLUMN, nameSize)
+               unitSize = min(viewHeader.sectionSize(UnitData.UNIT_COLUMN), 150)
+               viewHeader.setResizeMode(UnitData.UNIT_COLUMN, QtGui.QHeaderView.Fixed)
+               viewHeader.resizeSection(UnitData.UNIT_COLUMN, unitSize)
+
+               self._layout = QtGui.QVBoxLayout()
+               self._layout.addLayout(self._selectedUnitLayout)
+               self._layout.addWidget(self._unitsView)
+
+               centralWidget = QtGui.QWidget()
+               centralWidget.setLayout(self._layout)
+
+               self._window = QtGui.QMainWindow(parent)
+               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
+               qui_utils.set_stackable(self._window, True)
+               self._window.setWindowTitle("%s - %s" % (constants.__pretty_app_name__, category))
+               self._window.setWindowIcon(QtGui.QIcon(app.appIconPath))
+               self._window.setCentralWidget(centralWidget)
+
+               defaultUnitName = self._app.get_recent_unit(self._categoryName)
+               if defaultUnitName:
+                       self.select_unit(defaultUnitName)
+               else:
+                       self._select_unit(0)
+
+               if self._app.sortByNameAction.isChecked():
+                       sortColumn = UnitData.NAME_COLUMN
+               elif self._app.sortByValueAction.isChecked():
+                       sortColumn = UnitData.VALUE_COLUMN_0
+               elif self._app.sortByUnitAction.isChecked():
+                       sortColumn = UnitData.UNIT_COLUMN
+               else:
+                       raise RuntimeError("No sort column selected")
+               if sortColumn != 0:
+                       # By default it sorts by he first column (name)
+                       self._unitsModel.sort(sortColumn)
+
+               self._chooseFavoritesAction = QtGui.QAction(None)
+               self._chooseFavoritesAction.setText("Select Favorites")
+               self._chooseFavoritesAction.setShortcut(QtGui.QKeySequence("CTRL+f"))
+               self._chooseFavoritesAction.triggered.connect(self._on_choose_favorites)
+
+               self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
+
+               self._previousUnitAction = QtGui.QAction(None)
+               self._previousUnitAction.setText("Previous Unit")
+               self._previousUnitAction.setShortcut(QtGui.QKeySequence("Up"))
+               self._previousUnitAction.triggered.connect(self._on_previous_unit)
+
+               self._nextUnitAction = QtGui.QAction(None)
+               self._nextUnitAction.setText("Next Unit")
+               self._nextUnitAction.setShortcut(QtGui.QKeySequence("Down"))
+               self._nextUnitAction.triggered.connect(self._on_next_unit)
+
+               self._closeWindowAction = QtGui.QAction(None)
+               self._closeWindowAction.setText("Close Window")
+               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
+               self._closeWindowAction.triggered.connect(self._on_close_window)
+
+               if constants.IS_MAEMO:
+                       self._window.addAction(self._closeWindowAction)
+                       self._window.addAction(self._app.quitAction)
+                       self._window.addAction(self._app.fullscreenAction)
+
+                       fileMenu = self._window.menuBar().addMenu("&Units")
+                       fileMenu.addAction(self._chooseFavoritesAction)
+
+                       viewMenu = self._window.menuBar().addMenu("&View")
+                       viewMenu.addAction(self._app.showFavoritesAction)
+                       viewMenu.addAction(self._app.condensedAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.sortByNameAction)
+                       viewMenu.addAction(self._app.sortByValueAction)
+                       viewMenu.addAction(self._app.sortByUnitAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.jumpAction)
+                       viewMenu.addAction(self._app.recentAction)
+               else:
+                       fileMenu = self._window.menuBar().addMenu("&Units")
+                       fileMenu.addAction(self._chooseFavoritesAction)
+                       fileMenu.addAction(self._closeWindowAction)
+                       fileMenu.addAction(self._app.quitAction)
+
+                       viewMenu = self._window.menuBar().addMenu("&View")
+                       viewMenu.addAction(self._app.showFavoritesAction)
+                       viewMenu.addAction(self._app.condensedAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.sortByNameAction)
+                       viewMenu.addAction(self._app.sortByValueAction)
+                       viewMenu.addAction(self._app.sortByUnitAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.jumpAction)
+                       viewMenu.addAction(self._app.recentAction)
+                       viewMenu.addSeparator()
+                       viewMenu.addAction(self._app.fullscreenAction)
+
+               self._app.sortByNameAction.triggered.connect(self._on_sort_by_name)
+               self._app.sortByValueAction.triggered.connect(self._on_sort_by_value)
+               self._app.sortByUnitAction.triggered.connect(self._on_sort_by_unit)
+
+               self._window.addAction(self._app.logAction)
+               self._window.addAction(self._nextUnitAction)
+               self._window.addAction(self._previousUnitAction)
+               self._window.addAction(self._chooseFavoritesAction)
+
+               self._update_favorites()
+               self.set_fullscreen(self._app.fullscreenAction.isChecked())
+               self._window.show()
+
+       @property
+       def window(self):
+               return self._window
+
+       def show(self):
+               for child in self.walk_children():
+                       child.hide()
+               self._window.show()
+
+       def hide(self):
+               for child in self.walk_children():
+                       child.hide()
+               self._window.hide()
+
+       def close(self):
+               for child in self.walk_children():
+                       child.window.destroyed.disconnect(self._on_child_close)
+                       child.close()
+               self._window.close()
+
+       def set_fullscreen(self, isFullscreen):
+               if isFullscreen:
+                       self._window.showFullScreen()
+               else:
+                       self._window.showNormal()
+
+       def select_unit(self, unitName):
+               index = self._unitsModel.index_unit(unitName)
+               self._select_unit(index)
+
+               qindex = self._unitsModel.createIndex(index, 0, self._unitsModel.get_unit(index))
+               self._unitsView.scrollTo(qindex)
+
+       def walk_children(self):
+               if self._favoritesWindow is not None:
+                       yield self._favoritesWindow
+
+       def _select_unit(self, index):
+               unit = self._unitsModel.get_unit(index)
+               self._selectedUnitName.setText(unit.name)
+               self._selectedUnitValue.setText(str(unit.value))
+               self._selectedUnitSymbol.setText(unit.unit)
+
+               self._selectedIndex = index
+               self._app.add_recent(self._categoryName, self._unitsModel.get_unit(index).name)
+
+       def _update_favorites(self, force = False):
+               if self._app.showFavoritesAction.isChecked():
+                       unitNames = list(self._unitsModel.get_unit_names())
+                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
+                       for i, unitName in enumerate(unitNames):
+                               if unitName in hiddenUnits:
+                                       self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
+                               else:
+                                       self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
+               else:
+                       if force:
+                               for i in xrange(len(self._unitsModel)):
+                                       self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_show_favorites(self, checked = True):
+               if checked:
+                       unitNames = list(self._unitsModel.get_unit_names())
+                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
+                       for i, unitName in enumerate(unitNames):
+                               if unitName in hiddenUnits:
+                                       self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
+               else:
+                       for i in xrange(len(self._unitsModel)):
+                               self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_choose_favorites(self, obj = None):
+               assert self._favoritesWindow is None
+               import windows
+               self._favoritesWindow = windows.FavoritesWindow(
+                       self._window,
+                       self._app,
+                       unit_data.get_units(self._categoryName),
+                       self._app.get_hidden_units(self._categoryName)
+               )
+               self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
+               return self._favoritesWindow
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_favorites(self, obj = None):
+               self._favoritesWindow = None
+               self._update_favorites(force=True)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_previous_unit(self, checked = True):
+               index = self._selectedIndex - 1
+               unitData = self._unitsModel.get_unit(index)
+               unitName = unitData.name
+
+               if self._app.showFavoritesAction.isChecked():
+                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
+                       while unitName in hiddenUnits:
+                               index -= 1
+                               unitData = self._unitsModel.get_unit(index)
+                               unitName = unitData.name
+
+               self.select_unit(unitName)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_next_unit(self, checked = True):
+               index = self._selectedIndex + 1
+               unitData = self._unitsModel.get_unit(index)
+               unitName = unitData.name
+
+               if self._app.showFavoritesAction.isChecked():
+                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
+                       while unitName in hiddenUnits:
+                               index += 1
+                               unitData = self._unitsModel.get_unit(index)
+                               unitName = unitData.name
+
+               self.select_unit(unitName)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_window(self, checked = True):
+               self.close()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_sort_by_name(self, checked = False):
+               self._unitsModel.sort(UnitData.NAME_COLUMN, QtCore.Qt.DescendingOrder)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_sort_by_value(self, checked = False):
+               self._unitsModel.sort(UnitData.VALUE_COLUMN_0)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_sort_by_unit(self, checked = False):
+               self._unitsModel.sort(UnitData.UNIT_COLUMN, QtCore.Qt.DescendingOrder)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_unit_clicked(self, index):
+               self._select_unit(index.row())
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_value_edited(self, *args):
+               if not self._updateDelayTimer.isActive():
+                       self._updateDelayTimer.start()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_value_edited_delayed(self, *args):
+               userInput = str(self._selectedUnitValue.text())
+               orderChanged = self._unitsModel.update_values(self._selectedIndex, userInput)
+               if orderChanged:
+                       self._update_favorites()
+
+
+def run_gonvert():
+       try:
+               os.makedirs(constants._data_path_)
+       except OSError, e:
+               if e.errno != 17:
+                       raise
+
+       logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s'
+       logging.basicConfig(level=logging.DEBUG, format=logFormat)
+       rotating = logging.handlers.RotatingFileHandler(constants._user_logpath_, maxBytes=512*1024, backupCount=1)
+       rotating.setFormatter(logging.Formatter(logFormat))
+       root = logging.getLogger()
+       root.addHandler(rotating)
+       _moduleLogger.info("%s %s-%s" % (constants.__app_name__, constants.__version__, constants.__build__))
+       _moduleLogger.info("OS: %s" % (os.uname()[0], ))
+       _moduleLogger.info("Kernel: %s (%s) for %s" % os.uname()[2:])
+       _moduleLogger.info("Hostname: %s" % os.uname()[1])
+
+       app = QtGui.QApplication([])
+       handle = Gonvert(app)
+       if constants.PROFILE_STARTUP:
+               return 0
+       else:
+               return app.exec_()
+
+
+if __name__ == "__main__":
+       import sys
+       val = run_gonvert()
+       sys.exit(val)
diff --git a/gonvert/unit_data.py b/gonvert/unit_data.py
new file mode 100644 (file)
index 0000000..61770b5
--- /dev/null
@@ -0,0 +1,2312 @@
+import math
+
+import converters
+
+try:
+       raise ImportError()
+       import gettext
+except ImportError:
+       _ = lambda x: x
+else:
+       _ = gettext.gettext
+
+
+
+#----- main dictionary of unit descriptions below ------------
+# first entry defines base unit
+# remaining entries define unit specifications [(function, argument), units, description]
+#      where function can be m and argument is the multiplying factor to_base
+#      or function can be any other arbitrary function and argument can be a single argument
+UNIT_DESCRIPTIONS = {
+       _(u"Acceleration"): {
+               ".base_unit": _(u"meter per second squared"),
+               _(u"free fall"):
+               [(converters.m, 9.80665), _(u"gn"), _(u"The ideal falling motion of a body that is subject only to the earth's gravitational field.")],
+               _(u"meter per second squared"):
+               [(converters.m, 1.0), u"m/s\xb2", u''],
+               _(u"foot per second squared"):
+               [(converters.m, 30.48/100), u"ft/s\xb2", u''],
+               _(u"centimeter per second squared"):
+               [(converters.m, 1/100.0), u"cm/s\xb2", ''],
+               _(u"gal"):
+               [(converters.m, 1/100.0), _(u"Gal"), _(u"A unit of gravitational acceleration equal to one centimeter per second per second (named after Galileo)")],
+               _(u"millimeter per second squared"):
+               [(converters.m, 1/1000.0), u"mm/s\xb2", '']
+       },
+
+       _(u"Angle"): {
+               ".base_unit": _(u"radian"),
+               _(u"revolution / circle / perigon / turn"):
+               [(converters.m, 2.0*math.pi), "r", _(u"The act of revolving, or turning round on an axis or a center; the motion of a body round a fixed point or line; rotation; as, the revolution of a wheel, of a top, of the earth on its axis, etc.")],
+               _(u"right angle"):
+               [(converters.m, math.pi/2.0), "L", _(u"The angle formed by one line meeting another perpendicularly")],
+               _(u"radian"):
+               [(converters.m, 1.0), _(u"rad"), _(u"An arc of a circle which is equal in length to the radius, or the angle measured by such an arc.")],
+               _(u"degree"):
+               [(converters.m, math.pi/180.0), u"\xb0", _(u"1/360 of a complete revolution.")],
+               _(u"grad | grade | gon"):
+               [(converters.m, math.pi/200), _(u"g"), _(u"One-hundredth of a right angle.")],
+               _(u"milliradian"):
+               [(converters.m, 1/1000.0), _(u"mrad"), _(u"A unit of angular distance equal to one thousandth of a radian.")],
+               _(u"minute"):
+               [(converters.m, math.pi/(180.0*60)), "'", _(u"The sixtieth part of a degree; sixty seconds (Marked thus ('); as, 10deg 20').")],
+               _(u"second"):
+               [(converters.m, math.pi/(180.0*60*60)), '"', _(u"""One sixtieth of a minute.(Marked thus ("); as, 10deg 20' 30"). ''""")],
+               _(u"mil"):
+               [(converters.m, (2*math.pi)/6400), '', _(u"Used in artillery; 1/6400 of a complete revolution.")],
+               _(u"centesimal minute"):
+               [(converters.m, math.pi/20000), '', _(u"One hundredth of a grade, 0.01 grade")],
+               _(u"centesimal second"):
+               [(converters.m, math.pi/2000000), '', _(u"One ten-thousandth of a grade, 0.0001 grade")],
+               _(u"octant"):
+               [(converters.m, math.pi/4.0), '', _(u"The eighth part of a circle (an arc of 45 degrees).")],
+               _(u"quadrant"):
+               [(converters.m, math.pi/2.0), '', _(u"The fourth part of a circle (an arc of 90 degrees).")],
+               _(u"sextant"):
+               [(converters.m, math.pi/3.0), '', _(u"The sixth part of a circle (an arc of 60 degrees).")],
+               _(u"point"):
+               [(converters.m, math.pi/16.0), '', _(u"1/32 of a circle. Points are used on the face of a compass (32 points). Each point is labelled clockwise starting from North as follows: North, North by East, North Northeast, Northeast by North, and Northeast, etc.")],
+               _(u"sign"):
+               [(converters.m, math.pi/6.0), '', _(u"The twelfth part of a circle as in twelve signs of the zodiac (an arc of 30 degrees).")],
+       },
+
+       _(u"Angular Velocity / Frequency"): {
+               ".base_unit": _(u"radian per second"),
+               _(u"kiloradian per second"):
+               [(converters.m, 1000.0), "krad/s", ''],
+               _(u"revolution per second"):
+               [(converters.m, 2*math.pi), "rev/s", ''],
+               _(u"hertz"):
+               [(converters.m, 2*math.pi), "Hz", _(u"Named after the German physicist Heinrich Hertz (1857-1894) who was the first to produce electromagnetic waves artificially. Having a periodic interval of one second.")],
+               _(u"radian per second"):
+               [(converters.m, 1.0), "rad/s", ''],
+               _(u"milliradian per second"):
+               [(converters.m, 1/1000.0), "mrad/s", ''],
+               _(u"revolution per minute"):
+               [(converters.m, (2*math.pi)/60.0), "rpm", ''],
+               _(u"revolution per hour"):
+               [(converters.m, (2*math.pi)/3600.0), "rph", ''],
+               _(u"revolution per day"):
+               [(converters.m, (2*math.pi)/(3600.0*24)), "rpd", ''],
+               _(u"gigahertz"):
+               [(converters.m, 1e9*2*math.pi), "GHz", _(u"One billion hertz.")],
+               _(u"terahertz"):
+               [(converters.m, 1e12*2*math.pi), "THz", ''],
+               _(u"petahertz"):
+               [(converters.m, 1e15*2*math.pi), "PHz", ''],
+               _(u"exahertz"):
+               [(converters.m, 1e18*2*math.pi), "EHz", ''],
+               _(u"megahertz"):
+               [(converters.m, 1e6*2*math.pi), "MHz", _(u"One million hertz.")],
+               _(u"kilohertz"):
+               [(converters.m, 1e3*2*math.pi), "kHz", _(u"One thousand hertz.")],
+       },
+
+       _(u"Area"): {
+               ".base_unit": _(u"square meter"),
+               _(u"meter diameter circle"):
+               [(converters.f, ('math.pi*(x/2.0)**2', '2.0*(x/math.pi)**(0.5)')), "m dia.", _(u"Type the diameter of the circle in meters to find its area displayed in other fields.")],
+               _(u"centimeter diameter circle"):
+               [(converters.f, ('math.pi*(x/200.0)**2', '200.0*(x/math.pi)**(0.5)')), "cm dia.", _(u"Type the diameter of the circle in centimeters to find its area displayed in other fields.")],
+               _(u"inch diameter circle"):
+               [(converters.f, ('math.pi*(((x*(25.4/1000))/2.0) )**2', '1000/25.4 * 2.0*(x/math.pi)**(0.5)')), "in dia.", _(u"Type the diameter of the circle in inches to find its area displayed in other fields.")],
+               _(u"foot diameter circle"):
+               [(converters.f, ('math.pi*(((x*((12*25.4)/1000))/2.0) )**2', '1000/(12*25.4) * 2.0*(x/math.pi)**(0.5)')), "ft dia.", _(u"Type the diameter of the circle in feet to find its area displayed in other fields.")],
+               _(u"are"):
+               [(converters.m, 100.0), '', _(u"The unit of superficial measure, being a square of which each side is ten meters in length; 100 square meters, or about 119.6 square yards.")],
+               _(u"acre"):
+               [(converters.m, 4046.8564224), '', _(u"A piece of land, containing 160 square rods, or 4, 840 square yards, or 43, 560 square feet. This is the English statute acre. That of the United States is the same. The Scotch acre was about 1.26 of the English, and the Irish 1.62 of the English. Note: The acre was limited to its present definite quantity by statutes of Edward I., Edward III., and Henry VIII.")],
+               _(u"acre (Cheshire)"):
+               [(converters.m, 8561.97632), '', ''],
+               _(u"acre (Irish)"):
+               [(converters.m, 6555.26312), '', ''],
+               _(u"acre (Scottish)"):
+               [(converters.m, 5142.20257), '', ''],
+               _(u"arpent (French)"):
+               [(converters.m, 4088/1.196), '', _(u" 4, 088 sq. yards, or nearly five sixths of an English acre.")],
+               _(u"arpent (woodland)"):
+               [(converters.m, 16*25.29285264*10+16*25.29285264*2.5+(16*25.29285264*10)/160), '', _(u"1 acre, 1 rood, 1 perch")],
+               _(u"barn"):
+               [(converters.m, 1.0/1e28), '', _('Used in Nuclear physics to describe the apparent cross-sectional size of atomic sized objects that are bombarded with smaller objects (like electrons). 10^-28 square meters. 100 square femtometers. Originated from the semi-humorous idiom big as a barn and used by physicists to describe the size of the scattering object (Ex: That was as big as 5 barns!).')],
+               _(u"cho"):
+               [(converters.m, 16*25.29285264*10*2.45), '', _(u"Japanese. 2.45 acre")],
+               _(u"circular inch"):
+               [(converters.m, 1000000.0/(1e6*1550*1.273)), '', ''],
+               _(u"circular mil"):
+               [(converters.m, 1.0/(1e6*1550*1.273)), "cmil", ''],
+               _(u"desyatina | dessiatina"):
+               [(converters.m, 16*25.29285264*10*2.6996), '', _(u"Russian. 2.6996 acre. 2400 square sadzhens")],
+               _(u"flag"):
+               [(converters.m, 25/10.7639104167097), '', _(u"square pace (a pace is 5 feet).")],
+               _(u"hide | carucate"):
+               [(converters.m, 40468.71618), '', _(u"An ancient English measure of the amount of land required to support family")],
+               _(u"hectare"):
+               [(converters.m, 10000.0), "ha", _(u"A measure of area, or superficies, containing a hundred ares, or 10, 000 square meters, and equivalent to 2.471 acres.")],
+               _(u"homestead | quarter section"):
+               [(converters.m, 16*25.29285264*10*160), '', _(u"160 acres, 1/4 square mile, or 1/4 section. Use by the governments of North America early settlers in the western states and provinces were allowed to take title to a homestead of 160 acres of land by registering a claim, settling on the land, and cultivating it.")],
+               _(u"perch"):
+               [(converters.m, (16*25.29285264*10)/160), '', _(u"Used to measure land. A square rod; the 160th part of an acre.")],
+               _(u"sabin"):
+               [(converters.m, 1/10.7639104167097), '', _(u"A unit of acoustic absorption equivalent to the absorption by a square foot of a surface that absorbs all incident sound. 1ft\xb2.")],
+               _(u"square"):
+               [(converters.m, 100/10.7639104167097), '', _(u"Used in the construction for measuring roofing material, finished lumber, and other building materials. One square is equals 100 square feet.")],
+               _(u"section"):
+               [(converters.m, 2.59*1E6), '', _(u"Used in land measuring. One square mile. An area of about 640 acres")],
+               _(u"square league (land)"):
+               [(converters.m, 23309892.99), '', ''],
+               _(u"square mile"):
+                       [(converters.m, 2.59*1e6), u"mi\xb2", ''],
+               _(u"square kilometer"):
+                       [(converters.m, 1e6), u"km\xb2", ''],
+               _(u"rood"):
+                       [(converters.m, 16*25.29285264*2.5), '', _(u"The fourth part of an acre, or forty square rods.")],
+               _(u"shaku"):
+                       [(converters.m, 330.6/10000), '', _(u"A Japanese unit of area, the shaku equals 330.6 square centimeters (51.24 square inches). Note: shaku also means length and volume.")],
+               _(u"square chain (surveyor)"):
+                       [(converters.m, 16*25.29285264), u"ch\xb2", _(u"A unit for land measure equal to four rods square, or one tenth of an acre.")],
+               _(u"link"):
+                       [(converters.m, 4*25.29285264), '', _(u"4 rods square")],
+               _(u"square rod"):
+                       [(converters.m, 25.29285264), u"rd\xb2", ''],
+               _(u"square meter"):
+                       [(converters.m, 1.0), u"m\xb2", _(u"Also know as a centare is (1/100th of an are).")],
+               _(u"square yard"):
+                       [(converters.m, 1/1.19599004630108), u"yd\xb2", _(u"A unit of area equal to one yard by one yard square syn: sq yd")],
+               _(u"square foot"):
+                       [(converters.m, 1/10.7639104167097), u"ft\xb2", _(u"An area equal to that of a square the sides of which are twelve inches; 144 square inches.")],
+               _(u"square inch"):
+                       [(converters.m, 1/(10.7639104167097*144)), u"in\xb2", _(u"A unit of area equal to one inch by one inch square syn: sq in")],
+               _(u"square centimeter"):
+                       [(converters.m, 1.0/10000), u"cm\xb2", ''],
+               _(u"square micrometer"):
+                       [(converters.m, 1.0/1e12), u"\xb5m\xb2", ''],
+               _(u"square millimeter"):
+                       [(converters.m, 1.0/1e6), u"mm\xb2", ''],
+               _(u"square mil"):
+                       [(converters.m, 1.0/(1e6*1550)), u"mil\xb2", ''],
+               _(u"township"):
+                       [(converters.m, 1e6*2.59*36), '', _(u"A division of territory six miles square (36miles\xb2), containing 36 sections.")],
+               _(u"roll (wallpaper)"):
+                       [(converters.m, 30/10.7639104167097), '', ''],
+               _(u"square Scottish ell"):
+                       [(converters.m, 0.88323), '', ''],
+               _(u"fall (Scottish)"):
+                       [(converters.m, 31.79618), '', ''],
+               _(u"joch (German) | yoke"):
+                       [(converters.m, 5746.5577), '', _(u"joch (German) is 40 square klafters")],
+               _(u"labor (Texas)"):
+                       [(converters.m, 716862.83837), '', _(u"An area of land that could be cultivated by one farmer")],
+               _(u"barony"):
+                       [(converters.m, 16187486.47094), '', ''],
+               _(u"square pes (Roman)"):
+                       [(converters.m, 0.08741), '', ''],
+               _(u"square alen (Denmark)"):
+                       [(converters.m, .38121), '', ''],
+               _(u"ferfet (Iceland)"):
+                       [(converters.m, 0.09848), '', ''],
+               _(u"square vara (Spanish)"):
+                       [(converters.m, 0.59418), '', ''],
+               _(u"donum (Yugoslavia)"):
+                       [(converters.m, 699.99992), '', ''],
+               _(u"sahme (Egyptian)"):
+                       [(converters.m, 7.29106), '', ''],
+               _(u"tavola (Italian)"):
+                       [(converters.m, 37.62587), '', ''],
+               _(u"cuadra (Paraguay)"):
+                       [(converters.m, 7486.71249), '', ''],
+               _(u"acaena (Greek)"):
+                       [(converters.m, 9.19744), '', ''],
+               _(u"plethron (Greek)"):
+                       [(converters.m, 951.01483), '', ''],
+       },
+
+       _(u"Atomic Physics"): {
+               ".base_unit": _(u"radian per second"),
+               _(u"kilogram"):
+               [(converters.m, 2.997925e8**2*(1.0/1.054e-34)), "kg", ''],
+               _(u"joule"):
+               [(converters.m, 1.0/1.054e-34), '', _(u"Named after the English physicist James Prescott Joule (1818-1889). A unit of work which is equal to 10^7 units of work in the C. G. S. system of units (ergs), and is practically equivalent to the energy expended in one second by an electric current of one ampere in a resistance of one ohm. One joule is approximately equal to 0.738 foot pounds.")],
+               _(u"erg"):
+               [(converters.m, 1.0/1.054e-27), '', _(u"The unit of work or energy in the C. G. S. system, being the amount of work done by a dyne working through a distance of one centimeter; the amount of energy expended in moving a body one centimeter against a force of one dyne. One foot pound is equal to 13, 560, 000 ergs.")],
+               _(u"GeV Giga electronvolt"):
+               [(converters.m, 2.41796e23*2*math.pi), "Gev", ''],
+               _(u"neutron mass unit"):
+               [(converters.m, 1.00137*1836.11*3.75577e4*13.6058*2.41796e14*2*math.pi), '', ''],
+               _(u"proton mass unit"):
+               [(converters.m, 1836.11*3.75577e4*13.6058*2.41796e14*2*math.pi), '', ''],
+               _(u"atomic mass unit"):
+               [(converters.m, 1822.84*3.75577e4*13.6058*2.41796e14*2*math.pi), "amu", ''],
+               _(u"MeV Mega electronvolt"):
+               [(converters.m, 2.41796e20*2*math.pi), "MeV", ''],
+               _(u"electron rest mass"):
+               [(converters.m, 3.75577e4*13.6058*2.41796e14*2*math.pi), '', ''],
+               _(u"Rydberg constant"):
+               [(converters.m, 13.6058*2.41796e14*2*math.pi), '', _(u"Named after the Swedish physicist Johannes Robert Rydberg (1854-1919). A wave number characteristic of the wave spectrum of each element")],
+               _(u"electronvolt"):
+               [(converters.m, 2.41796e14*2*math.pi), "eV", _(u"A unit of energy equal to the work done by an electron accelerated through a potential difference of 1 volt.")],
+               _(u"kayser or cm^-1"):
+               [(converters.m, 2.997925e10*2*math.pi), "K", _('Named after the German physicist Heinrich Gustav Johannes Kayser (1853-1940). Used to measure light and other electromagnetic waves. The "wave number" in kaysers equals the number of wavelengths per centimeter.')],
+               _(u"kelvin"):
+               [(converters.m, 2.997925e8*2*math.pi/1.4387752e-2), "K", _(u"The basic unit of thermodynamic temperature adopted under the System International d'Unites")],
+               "m^-1":
+               [(converters.m, 2.997925e8*2*math.pi), '', ''],
+               _(u"millikayser"):
+               [(converters.m, 2.997925e7*2*math.pi), '', ''],
+               _(u"hertz"):
+               [(converters.m, 2*math.pi), "Hz", ''],
+               _(u"radian per second"):
+               [(converters.m, 1.0), "rad/s", ''],
+       },
+
+       _(u"Computer Data"): {
+               ".base_unit": _(u"bit"),
+               _(u"bit"):
+               [(converters.m, 1.0), '', _(u"One bit of data. Binary representation On/Off.")],
+               _(u"nibble | hexit | quadbit"):
+               [(converters.m, 4.0), '', _(u"One half a byte")],
+               _(u"byte"):
+               [(converters.m, 8.0), '', _(u"Eight bits")],
+               _(u"character"):
+               [(converters.m, 8.0), '', _('Usually described by one byte (256 possible characters can be defined by one byte).')],
+               _(u"kilobit"):
+               [(converters.m, 2.0**10.0), "kilobit", _(u"2^10 bits")],
+               _(u"megabit"):
+               [(converters.m, 2.0**20.0), "megabit", _(u"2^20 bits")],
+               _(u"kilobyte | kibi"):
+               [(converters.m, 1024.0*8), "K | Ki", _(u"2^10, 1024 bytes. 1024 comes from 2^10 which is close enough to 1000. kibi is the IEEE proposal.")],
+               _(u"megabyte | mebi"):
+               [(converters.m, 1024.0**2*8), "M | Mi", _(u"2^20, 1024^2 bytes. 1024 kilobytes. 1024 comes from 2^10 which is close enough to 1000. mebi is the IEEE proposal.")],
+               _(u"gigabyte | gibi"):
+               [(converters.m, 1024.0**3*8), "G | Gi", _(u"2^30, 1024^3. 1024 megabytes. 1024 comes from 2^10 which is close enough to 1000. gibi is the IEEE proposal.")],
+               _(u"terabyte | tebi"):
+               [(converters.m, 1024.0**4*8), "T | Ti", _(u"2^40, 1024^4. 1024 gigabytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
+               _(u"petabyte | pebi"):
+               [(converters.m, 1024.0**5*8), "P | Pi", _(u"2^50, 1024^5. 1024 terabytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
+               _(u"exabyte | exbi"):
+               [(converters.m, 1024.0**6*8), "E | Ei", _(u"2^60, 1024^6, 1024 petabytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
+               _(u"zebi | zettabyte"):
+               [(converters.m, 1024.0**7*8), "Zi", _(u"1024^7. 1024 exbibytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
+               _(u"yobi | yottabyte"):
+               [(converters.m, 1024.0**8*8), "Yi", _(u"1024^8. 1024 yobibytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
+       },
+
+       _(u"Computer Data flow rate"): {
+               ".base_unit": _('bits per second'),
+               _(u"baud: 1"):
+               [(converters.m, 1.0), "", _(u'Symbol rate for 1 bit per symbol. Named after the French telegraph engineer Jean-Maurice-\u00C9mile Baudot (1845 - 1903). Data transmission measured in symbols per second.')],
+               _(u"baud: 10"):
+               [(converters.m, 10.0), "", _(u'Symbol rate for 10 bits per symbol. Named after the French telegraph engineer Jean-Maurice-\u00C9mile Baudot (1845 - 1903). Data transmission measured in symbols per second.')],
+               _(u"baud: 8"):
+               [(converters.m, 8.0), "", _(u'Symbol rate for 8 bits per symbol. Named after the French telegraph engineer Jean-Maurice-\u00C9mile Baudot (1845 - 1903). Data transmission measured in symbols per second.')],
+               _(u"baud: 4"):
+               [(converters.m, 4.0), "", _(u'Symbol rate for 4 bits per symbol. Named after the French telegraph engineer Jean-Maurice-\u00C9mile Baudot (1845 - 1903). Data transmission measured in symbols per second.')],
+               _(u"bits per second"):
+               [(converters.m, 1.0), "bps", _(u" ")],
+               _(u"characters per second"):
+               [(converters.m, 10.0), "cps", _('Rate to transmit one character. The character is usually described as one byte with one stop bit and one start bit (10 bits in total).')],
+       },
+
+       _(u"Computer Numbers"): {
+               ".base_unit": _(u"base 10 decimal"),
+               _(u"base  2 binary"):
+               [(converters.b, 2), "base  2", _('Base two numbering system using the digits 0-1')],
+               _(u"base  3 ternary | trinary"):
+               [(converters.b, 3), "base  3", _(u"Base three numbering system using the digits 0-2. Russian Nikolay Brusentsov built a trinary based computer system.")],
+               _(u"base  4 quaternary | quadrary"):
+               [(converters.b, 4), "base  4", _(u"Base four numbering system using the digits 0-3.")],
+               _(u"base  5 quinary"):
+               [(converters.b, 5), "base  5", _(u"Base five numbering system using the digits 0-4.")],
+               _(u"base  6 senary | hexary"):
+               [(converters.b, 6), "base  6", _(u"Base six numbering system using the digits 0-5.")],
+               _(u"base  7 septenary | septary"):
+               [(converters.b, 7), "base  7", _(u"Base seven numbering system using the digits 0-6.")],
+               _(u"base  8 octonary | octal | octonal | octimal"):
+               [(converters.b, 8), "base  8", _(u"Base eight numbering system using the digits 0-7. Commonly used in older computer systems.")],
+               _(u"base  9 nonary"):
+               [(converters.b, 9), "base  9", _(u"Base nine numbering system using the digits 0-8.")],
+               _(u"base 10 decimal"):
+               [(converters.b, 10), "base 10", _(u"Base ten numbering system using the digits 0-9.")],
+               _(u"base 11 undenary"):
+               [(converters.b, 11), "base 11", _(u"Base eleven numbering system using the digits 0-9, a.")],
+               _(u"base 12 duodecimal"):
+               [(converters.b, 12), "base 12", _(u"Base twelve numbering system using the digits 0-9, a-b.")],
+               _(u"base 13 tridecimal"):
+               [(converters.b, 13), "base 13", _('Base Thirteen numbering system using the digits 0-9, a-c.')],
+               _(u"base 14 quattuordecimal"):
+               [(converters.b, 14), "base 14", _(u"Base Fourteen numbering system using the digits 0-9, a-d.")],
+               _(u"base 15 quindecimal"):
+               [(converters.b, 15), "base 15", _(u"Base Fifteen numbering system using the digits 0-9, a-e.")],
+               _(u"base 16 sexadecimal | hexadecimal | hex"):
+               [(converters.b, 16), "base 16", _(u"Base Sixteen numbering system using the digits 0-1, a-f. Commonly used in computer systems.")],
+               _(u"base 17 septendecimal"):
+               [(converters.b, 17), "base 17", _(u"Base Sixteen numbering system using the digits 0-1, a-g.")],
+               _(u"base 18 octodecimal"):
+               [(converters.b, 18), "base 18", _(u"Base Sixteen numbering system using the digits 0-1, a-h.")],
+               _(u"base 19 nonadecimal"):
+               [(converters.b, 19), "base 19", _(u"Base Sixteen numbering system using the digits 0-1, a-i.")],
+               _(u"base 20 vigesimal"):
+               [(converters.b, 20), "base 20", _(u"Base Twenty numbering system using the digits 0-1, a-j.")],
+               _(u"base 30 trigesimal"):
+               [(converters.b, 30), "base 30", _(u"Base Thirty numbering system using the digits 0-1, a-r.")],
+               _(u"base 36 hexatrigesimal"):
+               [(converters.b, 36), "base 36", _(u"Base Thirty-six numbering system using the digits 0-9, a-z.")],
+               _(u"base 40 quadragesimal"):
+               [(converters.b, 40), "base 40", _(u"Base Forty digits numbering system using the digits 0-1, a-f, A-C.")],
+               _(u"base 50 quinquagesimal"):
+               [(converters.b, 50), "base 50", _(u"Base Fifty digits numbering system using the digits 0-1, a-f, A-M.")],
+               _(u"base 60 sexagesimal"):
+               [(converters.b, 60), "base 60", _(u"Base Sixty numbering system using the digits 0-9, a-z, A-V.")],
+               _(u"base 62 duosexagesimal"):
+               [(converters.b, 62), "base 62", _('Base Sixty-two numbering system using the digits 0-9, a-z, A-Z. This is the highest numbering system that can be represented with all decimal numbers and lower and upper case English alphabet characters. Other number systems include septagesimal (base 70), octagesimal (base 80), nonagesimal (base 90), centimal (base 100), bicentimal (base 200), tercentimal (base 300), quattrocentimal (base 400), quincentimal (base 500).')],
+               _(u"roman numerals"):
+               [(converters.r, 0), '', _('A symbol set in the old Roman notation; I, V, X, L, C, D, M. Range 1 to 3999 (higher values cannot be represented with standard ASCII characters).')],
+       },
+       _('Density'): {
+               ".base_unit": _(u"kilogram/cubic meter"),
+               _('kilogram per cubic meter'):
+               [(converters.m, 1.0), u"kg/m\xb3", ''],
+               _('kg per cubic cm'):
+               [(converters.m, 1.0e6), u"kg/cm\xb3", _(u"kilograms per cubic centimeter.")],
+               _(u"pound mass per gallon (UK)"):
+               [(converters.m, 99.7763664739553), "lbm/gal", _(u"Pounds mass per US liquid gallon.")],
+               _(u"pound mass per gallon (US)"):
+               [(converters.m, 119.826427316897), "lbm/gal", _(u"Pounds mass per US liquid gallon.")],
+               _(u"slug per cubic ft"):
+               [(converters.m, 515.3788), u"slug/ft\xb3", ''],
+               _(u"gram per cubic cm "):
+               [(converters.m, 1000.0), u"g/cm\xb3", ''],
+               _(u"gram per cubic meter "):
+               [(converters.m, .001), u"g/m\xb3", ''],
+               _(u"milligram/cubic meter "):
+               [(converters.m, 1.0e-6), u"mg/m\xb3", ''],
+               _(u"kilogram per liter"):
+               [(converters.m, 1000.0), "kg/l", ''],
+               _(u"metric ton per cubic meter"):
+               [(converters.m, 1000.0), u"metric ton/m\xb3", ''],
+               _(u"pound per cubic foot"):
+               [(converters.m, 0.45359237/0.028316846592), u"lbm/ft\xb3", _(u"Pounds mass per cubic foot.")],
+               _(u"pound per cubic inch"):
+               [(converters.m, 0.45359237/0.000016387064), u"lbm/in\xb3", _(u"Pounds mass per cubic inch.")],
+               _(u"aluminum"):
+               [(converters.m, 2643.0), "Al", _(u"Enter 1 here to find the density of aluminum.")],
+               _(u"iron"):
+               [(converters.m, 7658.0), "Fe", _(u"Enter 1 here to find the density of iron.")],
+               _(u"copper"):
+               [(converters.m, 8906.0), "Cu", _(u"Enter 1 here to find the density of copper.")],
+               _(u"lead"):
+               [(converters.m, 11370.0), "Pb", _(u"Enter 1 here to find the density of lead.")],
+               _(u"gold"):
+               [(converters.m, 19300.0), "Au", _(u"Enter 1 here to find the density of gold.")],
+               _(u"silver"):
+               [(converters.m, 10510.0), "Ag", _(u"Enter 1 here to find the density of silver.")],
+               _(u"water at 4degC"):
+               [(converters.m, 1000.0), u"H20 at 4\xb0C", _(u"Enter 1 here to find the density of water at 4\xb0C. Water weighs 1 gram per cm\xb3.")],
+               _(u"ounces per gallon (UK)"):
+               [(converters.m, (6.23602290462221)), _(u"oz/gal"), ''],
+               _(u"ounces per gallon (US)"):
+               [(converters.m, (7.48915170730604)), _(u"oz/gal"), ''],
+               _(u"ton (UK | long) per cubic yard"):
+               [(converters.m, 2240.0 * 0.45359237/0.764554857984), u"ton/yard\xb3", ''],
+               _(u"ton (UK | long) per cubic foot"):
+               [(converters.m, 2240.0 * 0.45359237/0.764554857984*27.0), u"ton/ft\xb3", ''],
+               _(u"ton (US | short) per cubic yard"):
+               [(converters.m, 2000.0 * 0.45359237/0.764554857984), u"ton/yard\xb3", ''],
+               _(u"ton (US | short) per cubic foot"):
+               [(converters.m, 32040.0), u"ton/ft\xb3", ''],
+       },
+
+       _(u"Electrical Current"): {
+               ".base_unit": _(u"ampere"),
+               _(u"ampere"):
+               [(converters.m, 1.0), "A", u"Named after the French physicist Andr\x82 Marie Amp\x82re (1775-1836). The unit of electric current; -- defined by the International Electrical Congress in 1893 and by U. S. Statute as, one tenth of the unit of current of the C. G. S. system of electro-magnetic units, or the practical equivalent of the unvarying current which, when passed through a standard solution of nitrate of silver in water, deposits silver at the rate of 0.001118 grams per second."],
+               _(u"kiloampere"):
+               [(converters.m, 1.0e3), "kA", ''],
+               _(u"milliampere"):
+               [(converters.m, 1.0e-3), "mA", ''],
+               _(u"microampere"):
+               [(converters.m, 1.0e-6), u"\xb5A", ''],
+               _(u"nanoampere"):
+               [(converters.m, 1.0e-9), "nA", ''],
+               _(u"math.picoampere"):
+               [(converters.m, 1.0e-12), "pA", ''],
+               _(u"abampere"):
+               [(converters.m, 10.0), "abA", _(u"The CGS electromagnetic unit of current.")],
+               _(u"coulomb per second"):
+               [(converters.m, 1.0), '', ''],
+               _(u"statampere"):
+               [(converters.m, 1.e-9/3), '', _(u"The CGS electrostatic unit of current.")],
+               },
+
+       _(u"Electrical Charge"): {
+               ".base_unit": _(u"coulomb"),
+               _(u"faraday"):
+               [(converters.m, 96.5e3), '', _(u"Named after Michael Faraday the The English physicist and chemist who discovered electromagnetic induction (1791-1867). The amount of electric charge that liberates one gram equivalent of any ion from an electrolytic solution. ")],
+               _(u"kilocoulomb"):
+               [(converters.m, 1.0e3), "kC", ''],
+               _(u"ampere-hour"):
+               [(converters.m, 3.6e3), u"A\xb7h", _(u"Commonly used to describe the capacity of a battery.")],
+               _(u"abcoulomb"):
+               [(converters.m, 10.0), "abC", _(u"The CGS electromagnetic unit of charge.")],
+               _(u"coulomb (weber)"):
+               [(converters.m, 1.0), "C", _(u"Named after the French physicist and electrican Coulomb. (Physics) The standard unit of quantity in electrical measurements. It is the quantity of electricity conveyed in one second by the current produced by an electro-motive force of one volt acting in a circuit having a resistance of one ohm, or the quantity transferred by one amp`ere in one second. Formerly called weber.")],
+               _(u"microcoulomb"):
+               [(converters.m, 1.0e-6), u"\xb5C", ''],
+               _(u"nanocoulomb"):
+               [(converters.m, 1.0e-9), "nC", ''],
+               _(u"statcoulomb"):
+               [(converters.m, 1.0e-9/3), "sC", _(u"The CGS electrostatic unit of charge.")],
+               _(u"electron charge"):
+               [(converters.m, 1.0/(6.2414503832469e18)), '', ''],
+               },
+_(u"Electrical Voltage"): {".base_unit": _(u"volt"),
+               _(u"abvolt"):
+               [(converters.m, 1.0e-8), "abV", _(u"A unit of potential equal to one-hundred-millionth of a volt.")],
+               _(u"volt"):
+               [(converters.m, 1.0), "V", _(u"""Named after the Italian electrician Alessandro Volta. The unit of electro-motive force; -- defined by the International Electrical Congress in 1893 and by United States Statute as, that electro-motive force which steadily applied to a conductor whose resistance is one ohm will produce a current of one ampere. It is practically equivalent to 1000/1434 the electro-motive force of a standard Clark's cell at a temperature of 15deg C.""")],
+               _(u"gigavolt"):
+               [(converters.m, 1.0e9), "GV", _(u"One billion volts.")],
+               _(u"megavolt"):
+               [(converters.m, 1.0e6), "MV", _(u"One million volts.")],
+               _(u"kilovolt"):
+               [(converters.m, 1.0e3), "kV", _(u"One thousand volts.")],
+               _(u"millivolt"):
+               [(converters.m, 1.0e-3), "mV", _(u"One thousandth of an volt.")],
+               _(u"microvolt"):
+               [(converters.m, 1.0e-6), u"\xb5V", _(u"One millionth of an volt.")],
+               _(u"nanovolt"):
+               [(converters.m, 1.0e-9), "nV", _(u"One billionth of an volt.")],
+               _(u"statvolt"):
+               [(converters.m, 300.0), '', _(u"300 volts.")],
+       },
+
+       _(u"Electrical Resistance & Conductance"): {
+               ".base_unit": _(u"ohm"),
+               _(u"ohm"):
+               [(converters.m, 1.0), "ohm", _(u"Named after the German physicist Georg Simon Ohm (1787-1854). The standard unit in the measure of electrical resistance, being the resistance of a circuit in which a potential difference of one volt produces a current of one ampere. As defined by the International Electrical Congress in 1893, and by United States Statute, it is a resistance substantially equal to 10^9 units of resistance of the C.G.S. system of electro-magnetic units, and is represented by the resistance offered to an unvarying electric current by a column of mercury at the temperature of melting ice 14.4521 grams in mass, of a constant cross-sectional area, and of the length of 106.3 centimeters. As thus defined it is called the international ohm")],
+               _(u"siemens | mho"):
+               [(converters.inv, 1.0), "S", _(u"Named after Ernst Werner von Siemens (1816-1892). A unit describing how well materials conduct equal to the reciprocal of an ohm syn: mho, S")],
+               _(u"abmho"):
+               [(converters.inv, 1.0e-9), "abmho", ''],
+               _(u"millisiemens | millimho"):
+               [(converters.inv, 1.0e3), "mS", ''],
+               _(u"microsiemens | micromho"):
+               [(converters.inv, 1.0e6), u"\xb5S", ''],
+               _(u"statmho"):
+               [(converters.inv, 8.99e11), '', ''],
+               _(u"gigaohm"):
+               [(converters.m, 1.0e9), _(u"G ohm"), _(u"One billion ohms.")],
+               _(u"megaohm"):
+               [(converters.m, 1.0e6), _(u"M ohm"), _(u"One million ohms.")],
+               _(u"kilohm"):
+               [(converters.m, 1.0e3), _(u"k ohm"), _(u"One thousand ohms.")],
+               _(u"milliohm"):
+               [(converters.m, 1.0e-3), _(u"m ohm"), _(u"One thousandth of an ohm.")],
+               _(u"microhm"):
+               [(converters.m, 1.0e-6), u"\xb5 ohm", _(u"One millionth of an ohm.")],
+               _(u"nanohm"):
+               [(converters.m, 1.0e-9), "n ohm", _(u"One billionth of an ohm.")],
+               _(u"abohm"):
+               [(converters.m, 1.0e-9), "ab ohm", ''],
+               _(u"statohm"):
+               [(converters.m, 8.99e5*1e6), '', ''],
+       },
+
+       _(u"Electrical Inductance"): {
+               ".base_unit": _(u"henry"),
+               _(u"henry"):
+               [(converters.m, 1.0), "H", _(u"Named after the American physicist Joseph Henry (1797-1878). The unit of electric induction; the induction in a circuit when the electro-motive force induced in this circuit is one volt, while the inducing current varies at the rate of one ampere a second.")],
+               _(u"stathenry"):
+               [(converters.m, 8.99e11), '', ''],
+               _(u"ohm-second"):
+               [(converters.m, 1.0), u"ohm\xb7sec", ''],
+               _(u"millihenry"):
+               [(converters.m, 1.0e-3), "mH", ''],
+               _(u"microhenry"):
+               [(converters.m, 1.0e-6), u"\xb5H", ''],
+               _(u"nanohenry"):
+               [(converters.m, 1.0e-9), "nH", ''],
+               _(u"abhenry"):
+               [(converters.m, 1.0e-9), "abH", ''],
+               },
+       _(u"Electrical Capacitance"): {
+               ".base_unit": _(u"farad"),
+               _(u"farad"):
+               [(converters.m, 1.0), "F", _(u"Named after the English electrician Michael Faraday. The standard unit of electrical capacity; the capacity of a condenser whose charge, having an electro-motive force of one volt, is equal to the amount of electricity which, with the same electromotive force, passes through one ohm in one second; the capacity, which, charged with one coulomb, gives an electro-motive force of one volt.")],
+               _(u"abfarad"):
+               [(converters.m, 1e9), "abF", _(u"A capacitance unit equal to one billion farads")],
+               _(u"second/ohm"):
+               [(converters.m, 1.0), '', ''],
+               _(u"microfarad"):
+               [(converters.m, 1e-6), u"\xb5F", ''],
+               _(u"statfarad"):
+               [(converters.m, 1.0e-6/8.99e5), '', ''],
+               _(u"nanofarad"):
+               [(converters.m, 1e-9), "nF", ''],
+               _(u"picofarad"):
+               [(converters.m, 1e-12), "pF", ''],
+               },
+       _(u"Electromagnetic Radiation"): {
+               ".base_unit": _(u"hertz"),
+               _(u"hertz"):
+               [(converters.m, 1.0), 'Hz', _(u"""Named after the German physicist Heinrich Hertz (1857-1894) who was the first to produce electromagnetic waves artificially. Having a periodic interval of one second.""")],
+               _(u"meter"):
+               [(converters.inv, 299792458.0), "m", _(u"Equal to 39.37 English inches, the standard of linear measure in the metric system of weights and measures. It was intended to be, and is very nearly, the ten millionth part of the distance from the equator to the north pole, as ascertained by actual measurement of an arc of a meridian.")],
+               _(u"centimeter"):
+               [(converters.inv, 29979245800.0), "cm", ''],
+               _(u"millimeter"):
+               [(converters.inv, 299792458000.0), "mm", ''],
+               _(u"micrometer | micron"):
+               [(converters.inv, 299792458000000.0), u"\xb5m", _(u"A metric unit of length equal to one millionth of a meter. The thousandth part of one millimeter.")],
+               _(u"nanometer"):
+               [(converters.inv, 299792458000000000.0), "nm", _(u"A metric unit of length equal to one billionth of a meter.")],
+               _(u"angstrom"):
+               [(converters.inv, 2997924580000000000.0), u"\xc5", _(u"Equal to one ten billionth of a meter (or 0.0001 micron); used to specify wavelengths of electromagnetic radiation")],
+               _(u"kilohertz"):
+               [(converters.m, 1.0e3), "KHz", ''],
+               _(u"megahertz"):
+               [(converters.m, 1.0e6), "MHz", ''],
+               _(u"gigahertz"):
+               [(converters.m, 1.0e9), "GHz", ''],
+               _(u"terahertz"):
+               [(converters.m, 1.0e12), "THz", ''],
+               _(u"petahertz"):
+               [(converters.m, 1.0e15), "PHz", ''],
+               _(u"exahertz"):
+               [(converters.m, 1.0e18), "EHz", ''],
+               _(u"electron Volt"):
+               [(converters.m, 1/4.13566e-15), "eV", _(u"Energy. e=h\xf6f where h = Planks constant (4.13566 x 10^-15 electron volts/second). f = frequency in Hertz.")],
+       },
+
+       _(u"Energy | Work"): {
+               ".base_unit": _(u"joule | wattsecond | newton-meter"),
+               _(u"kiloton"):
+               [(converters.m, 4200.0e9), '', _(u"A measure of explosive power (of an atomic weapon) equal to that of 1000 tons of TNT")],
+               _(u"gigawatt-hour"):
+               [(converters.m, 3.6e12), "GWh", ''],
+               _(u"megawatt-hour"):
+               [(converters.m, 3.6e9), "MWh", ''],
+               _(u"kilowatt-hour"):
+               [(converters.m, 3.6e6), "kWh", ''],
+               _(u"horsepower-hour"):
+               [(converters.m, 2.686e6), u"hp\xb7h", ''],
+               _(u"gigajoule"):
+               [(converters.m, 1.0e9), "GJ", ''],
+               _(u"megajoule"):
+               [(converters.m, 1.0e6), "MJ", ''],
+               _(u"kg force meters"):
+               [(converters.m, 9.80665), u"kgf\xb7m", _(u"Work done by one kilogram of force acting through a distance of one meter.")],
+               _(u"kilojoule"):
+               [(converters.m, 1.0e3), "kJ", ''],
+               _(u"watt-hour"):
+               [(converters.m, 3.6e3), "Wh", ''],
+               _(u"British thermal unit"):
+               [(converters.m, 1.055e3), "Btu", ''],
+               _(u"joule | wattsecond | newton-meter"):
+               [(converters.m, 1.0), "J", _(u"Named after the English physicist James Prescott Joule(1818-1889). A unit of work which is equal to 10^7 units of work in the C. G. S. system of units (ergs), and is practically equivalent to the energy expended in one second by an electric current of one ampere in a resistance of one ohm. One joule is approximately equal to 0.738 foot pounds.")],
+               _(u"kilocalorie"):
+               [(converters.m, 4.184e3), "kcal", ''],
+               _(u"calorie"):
+               [(converters.m, 4.184), "cal", _(u"The unit of heat according to the French standard; the amount of heat required to raise the temperature of one kilogram (sometimes, one gram) of water one degree centigrade, or from 0deg to 1deg.")],
+               _(u"foot-poundals"):
+               [(converters.m, 0.04214), '', ''],
+               _(u"foot-pound force"):
+               [(converters.m, 1.356), u"ft\xb7lbf", _(u"A unit of work equal to a force of one pound moving through a distance of one foot")],
+               _(u"millijoule"):
+               [(converters.m, 1.0e-3), "mJ", ''],
+               _(u"microjoule"):
+               [(converters.m, 1.0e-6), u"\xb5J", ''],
+               _(u"attojoule"):
+               [(converters.m, 1.0e-18), "aJ", ''],
+               _(u"erg | dyne-centimeter"):
+               [(converters.m, 1.0e-7), '', _(u"The unit of work or energy in the C. G. S. system, being the amount of work done by a dyne working through a distance of one centimeter; the amount of energy expended in moving a body one centimeter against a force of one dyne. One foot pound is equal to 13, 560, 000 ergs.")],
+               _(u"GeV"):
+               [(converters.m, 1.0e-9/6.24), '', _(u"A billion electronvolts")],
+               _(u"MeV"):
+               [(converters.m, 1.0e-12/6.24), '', _(u"a million electronvolts")],
+               _(u"electron volt"):
+               [(converters.m, 1.0e-18/6.24), "eV", _(u"A unit of energy equal to the work done by an electron accelerated through a potential difference of 1 volt")],
+               #1 cubic foot of natural gas ... 1, 008 to 1, 034 Btu
+               _(u"therm of natural gas"):
+               [(converters.m, 1.055e8), "", '1 therm of natural gas = 100, 000 Btu'],
+               _(u"gallon of liquefied petroleum gas"):
+               [(converters.m, 1.055e3*95475), "LPG", '1 gallon of liquefied petroleum gas = 95, 475 Btu'],
+               _(u"gallon of crude oil"):
+                       [(converters.m, 1.055e3*138095), "", '1 gallon of crude oil = 138, 095 Btu'],
+               _(u"barrel of crude oil"):
+                       [(converters.m, 1.055e3*5800000), "", '1 barrel of crude oil = 5, 800, 000 Btu'],
+               _(u"gallon of kerosene or light distillate oil"):
+                       [(converters.m, 1.055e3*135000), "", '1 gallon of kerosene or light distillate oil = 135, 000 Btu '],
+               _(u"gallon middle distillate or diesel fuel oil"):
+                       [(converters.m, 1.055e3*138690), "", '1 gallon middle distillate or diesel fuel oil = 138, 690 Btu '],
+               _(u"gallon residential fuel oil"):
+                       [(converters.m, 1.055e3*149690), "", '1 gallon residential fuel oil = 149, 690 Btu'],
+               _(u"gallon of gasoline"):
+                       [(converters.m, 1.055e3*125000), "", '1 gallon of gasoline = 125, 000 Btu'],
+               _(u"gallon of ethanol"):
+                       [(converters.m, 1.055e3*84400), "", '1 gallon of ethanol = 84, 400 Btu'],
+               _(u"gallon of methanol"):
+                       [(converters.m, 1.055e3*62800), "", '1 gallon of methanol = 62, 800 Btu'],
+               _(u"gallon gasohol (10% ethanol, 90% gasoline)"):
+                       [(converters.m, 1.055e3*120900), "", '1 gallon gasohol (10% ethanol, 90% gasoline) = 120, 900 Btu'],
+#              _(u"pound of coal"):
+#                      [(converters.m, 1.055e3), "", 'pound of coal = 8, 100-13, 000 Btu'],
+#              _(u"ton of coal"):
+#                      [(converters.m, 1.055e3), "", '1 ton of coal = 16, 200, 00-26, 000, 000 Btu'],
+               _(u"ton of coke"):
+                       [(converters.m, 1.055e3*26000000), "", '1 ton of coke = 26, 000, 000 Btu'],
+# 1 ton of wood = 9, 000, 00-17, 000, 000 Btu
+#              _(u""):
+#                      [(converters.m, 1.055e3), "", ''],
+# 1 standard cord of wood = 18, 000, 000-24, 000, 000 Btu
+#              _(u""):
+#                      [(converters.m, 1.055e3), "", ''],
+# 1 face cord of wood = 6, 000, 000-8, 000, 000 Btu
+#              _(u""):
+#                      [(converters.m, 1.055e3), "", ''],
+
+# GJ to therm and MBTUs would be nice too.
+               _(u"therm"):
+                       [(converters.m, 1.055e-3*10000), "", '10^5 BTUs'],
+
+
+               _(u"Mega British thermal unit"):
+                       [(converters.m, 1.055e-3), "MBtu", 'Million British thermal units'],
+
+               _(u"pound of carbon (upper heating value)"):
+                       [(converters.m, 1.055e3*14550), "", '1 pound of carbon is 14, 550 btu (upper heating value).'],
+       },
+
+       _(u"Flow (dry)"): {
+               ".base_unit": "litres per second",
+               _(u"litres per second"):
+               [(converters.m, 1.0), "lps", _(u"A cubic decimeter of material moving past a point every second.")],
+               _(u"litres per minute"):
+               [(converters.m, 1.0/60), "lpm", _(u"A cubic decimeter of material moving past a point every minute.")],
+               _(u"cubic feet per minute"):
+               [(converters.m, 1/(60*0.0353146667215)), "cfm", _(u"Commonly used to describe the flow rate produced by a large fan or blower.")],
+               _(u"cubic feet per second"):
+               [(converters.m, 1/0.0353146667215), "cfs", ''],
+               _(u"cubic inches per minute"):
+               [(converters.m, 1/(60*61.0237440947)), u"in\xb3/m", ''],
+               _(u"cubic inches per second"):
+               [(converters.m, 1/61.0237440947), u"in\xb3/s", ''],
+       },
+
+       _(u"Flow (liquid)"): {
+               ".base_unit": "litres per second",
+               _(u"litres per second"):
+               [(converters.m, 1.0), "lps", _(u"A cubic decimeter of material moving past a point every second")],
+               _(u"litres per minute"):
+               [(converters.m, 1.0/60), "lpm", ''],
+               _(u"US gallons per minute"):
+               [(converters.m, 60*3.785411784), "gpm (US)", ''],
+               _(u"US gallons per second"):
+               [(converters.m, 3.785411784), "gps (US)", ''],
+               _(u"UK gallons per minute"):
+               [(converters.m, 60*4.54609028199), "gpm (UK)", ''],
+               _(u"UK gallons per second"):
+               [(converters.m, 4.54609028199), "gps (UK)", ''],
+       },
+
+       _(u"Force"): {
+               ".base_unit": "newton",
+               _(u"tonne of force"):
+               [(converters.m, 9806.65), '', _(u"Metric ton of force, 1000 kilonewtons.")],
+               _(u"ton of force"):
+               [(converters.m, 2000*4.4482216152605), "tnf", _(u"2000 pounds of force.")],
+               _(u"sthene"):
+               [(converters.m, 1.0e3), '', _(u"Named from the Greek word sthenos, strength. One sthene is the force required to accelerate a mass of one tonne at a rate of 1 m/s2. ")],
+               _(u"atomic weight"):
+               [(converters.m, 1.6283353926E-26), '', _(u"Generally understood as the weight of the hydrogen atom.")],
+               _(u"kip"):
+               [(converters.m, 4.4482216152605e3), '', _(u"Kilopounds of force.")],
+               _(u"kilonewton"):
+               [(converters.m, 1.0e3), "kN", ''],
+               _(u"kilogram force | kilopond"):
+               [(converters.m, 9.80665), "kgf", ''],
+               _(u"pound force"):
+               [(converters.m, 4.4482216152605), "lbf", ''],
+               _(u"newton"):
+               [(converters.m, 1.0), "N", _(u"Named after the English mathematician and physicist Sir Isaac Newton (1642-1727). A unit of force equal to the force that imparts an acceleration of 1 m/sec\xb2 to a mass of 1 kilogram; equal to 100, 000 dynes")],
+               _(u"ounce force"):
+               [(converters.m, 4.4482216152605/16), "ozf", ''],
+               _(u"poundal"):
+               [(converters.m, 0.138254954376), "pdl", _(u"A unit of force based upon the pound, foot, and second, being the force which, acting on a pound avoirdupois for one second, causes it to acquire by the of that time a velocity of one foot per second. It is about equal to the weight of half an ounce, and is 13, 825 dynes.")],
+               _(u"gram force"):
+               [(converters.m, 9.80665/1e3), "gf", ''],
+               _(u"millinewton"):
+               [(converters.m, 1.0e-3), "mN", ''],
+               _(u"dyne"):
+               [(converters.m, 1.0e-5), "dyn", _(u"The unit of force, in the C. G. S. (Centimeter Gram Second) system of physical units; that is, the force which, acting on a gram for a second, generates a velocity of a centimeter per second.")],
+               _(u"micronewton"):
+               [(converters.m, 1.0e-6), u"\xb5N", ''],
+       },
+
+       _(u"Length"): {
+               ".base_unit": "meter",
+               _(u"klafter | faden (German)"):
+               [(converters.m, 1.8965), '', _(u"Similar to the fathom.")],
+               _(u"klafter | faden (Switzerland)"):
+               [(converters.m, 1.8), '', _(u"Similar to the fathom.")],
+               _(u"earth diamater"):
+               [(converters.m, 12742630), '', _(u"Diameter for the Earth.")],
+               _(u"actus (roman actus)"):
+               [(converters.m, 35.47872), '', _(u"Land measurement, 120 Roman feet (pedes monetales). This was equivalent to 35.47872 meters.")],
+               _(u"angstrom"):
+               [(converters.m, 1.0e-10), u"\xc5", _(u"Equal to one ten billionth of a meter (or 0.0001 micron); used to specify wavelengths of electromagnetic radiation")],
+               _(u"arshin | arshine | archin"):
+               [(converters.m, 0.7112), '', _(u"Russian.  28 inches")],
+               _(u"arpentcan"):
+               [(converters.m, 44289.14688), '', _(u"arpentcan = 27.52 mile")],
+               _(u"arpent (Canadian)"):
+               [(converters.m, 58.471308), '', _(u"Canadian unit of land measurement. 191.835 ft")],
+               _(u"arpentlin | French arpent"):
+               [(converters.m, 30*6.395*12*(25.4/1000)), '', _(u"French unit of land measurement. 30 toises")],
+               _(u"assbaa"):
+               [(converters.m, 0.02), '', _(u"Arabian measure.")],
+               _(u"astronomical unit"):
+               [(converters.m, 149597871000.0), "AU", _(u"Used for distances within the solar system; equal to the mean distance between the Earth and the Sun (approximately 93 million miles or 150 million kilometers).")],
+               _(u"barleycorn"):
+               [(converters.m, 8.46666666666667E-03), '', _(u"Formerly, a measure of length, equal to the average length of a grain of barley; the third part of an inch.")],
+               _(u"bohr radius"):
+               [(converters.m, 52.9177/1e12), '', _(u"Named after the Danish physicist Niels Bohr (1885-1962), who explained the structure of atoms in 1913. The bohr radius represents the mean distance between the proton and the electron in an unexcited hydrogen atom. 52.9177 picometers. ")],
+               _(u"bolt"):
+               [(converters.m, 36.576), '', _(u"A compact package or roll of cloth, as of canvas or silk, often containing about forty yards.")],
+               _(u"bottom measure"):
+               [(converters.m, (25.4/1000)/40), '', _(u"One fortieth of an inch.")],
+               _(u"cable length"):
+               [(converters.m, 219.456), '', _(u"A nautical unit of depth. 720 feet.")],
+               _(u"caliber (gun barrel caliber)"):
+               [(converters.m, 0.000254), '', _(u"The diameter of round or cylindrical body, as of a bullet or column.")],
+               _(u"cane"):
+               [(converters.m, 3.84049), '', _(u"Persian")],
+               _(u"chain (surveyors | Gunters)"):
+               [(converters.m, 20.1168), '', _(u"A surveyors instrument which consists of links and is used in measuring land.One commonly in use is Gunter's chain, which consists of one hundred links, each link being seven inches and ninety-two one hundredths in length; making up the total length of rods, or sixty-six, feet; hence, a measure of that length; hence, also, a unit for land measure equal to four rods.")],
+               _(u"chain (engineers)"):
+               [(converters.m, 100*(12*25.4/1000)), '', _(u"100 ft.")],
+               _(u"charac"):
+               [(converters.m, 0.2601), '', _(u"Persian")],
+               _(u"chebel"):
+               [(converters.m, 21.03124), '', _(u"Persian")],
+               _(u"city block"):
+               [(converters.m, 100*(36*25.4/1000)), '', _(u"An informal measurement, about 100 yards")],
+               _(u"cubit (Biblical | Hebrew | English)"):
+               [(converters.m, 18.00*(25.4/1000)), '', _(u"A measure of length, being the distance from the elbow to the extremity of the middle finger. Note: The cubit varies in length in different countries, the English, Hebrew and Biblical cubits are 18 inches.")],
+               _(u"cubit (Indian) | hasta"):
+               [(converters.m, 0.64161), '', ''],
+               _(u"cubit (Roman)"):
+               [(converters.m, 17.47*(25.4/1000)), '', _(u"A measure of length, being the distance from the elbow to the extremity of the middle finger. Note: The cubit varies in length in different countries, the Roman cubit is 17.47 inches.")],
+               _(u"cubit (Greek) | pechya"):
+                       [(converters.m, 18.20*(25.4/1000)), '', _(u"A measure of length, being the distance from the elbow to the extremity of the middle finger. Note: The cubit varies in length in different countries, the Greek cubit is 18.20 inches.")],
+               _(u"cubit (Israeli)"):
+                       [(converters.m, 0.55372), '', _(u"A measure of length, being the distance from the elbow to the extremity of the middle finger. Note: The cubit varies in length in different countries, the Israeli cubit is 21.8 inches.")],
+               _(u"cloth finger"):
+                       [(converters.m, 4.5*(25.4/1000)), '', _(u"Used in sewing")],
+               _(u"cloth quarter"):
+                       [(converters.m, 9*(25.4/1000)), '', _(u"Used in sewing")],
+               _(u"compton wavelength of the electron"):
+                       [(converters.m, 1836.11*1.00138*1.31962/1e15), '', _(u"Named after Arthur Holly Compton (1892-1962)")],
+               _(u"compton wavelength of the proton"):
+                       [(converters.m, 1.00138*1.31962/1e15), '', _(u"Named after Arthur Holly Compton (1892-1962)")],
+               _(u"compton wavelength of the neutron"):
+                       [(converters.m, 1.31962/1e15), '', _(u"Named after Arthur Holly Compton (1892-1962)")],
+               _(u"classical electron radius"):
+                       [(converters.m, 2.13247*1.00138*1.31962/1e15), '', ''],
+               _(u"digit | digitus"):
+                       [(converters.m, 0.018542), '', _(u"A finger's breadth, commonly estimated to be three fourths of an inch.")],
+               _(u"decimeter"):
+                       [(converters.m, 1.0e-1), "dm", """The tenth part of a meter; a measure of length equal to rather more than 3.937 of an inch."""],
+               _(u"diamond (Typographical)"):
+                       [(converters.m, 4.5*0.35146e-3), '', _(u"4 1/2 pt in height.")],
+               _(u"pearl (Typographical)"):
+                       [(converters.m, 5*0.35146e-3), '', _(u"5 pt in height.")],
+               _(u"agate | ruby (Typographical)"):
+                       [(converters.m, 5.5*0.35146e-3), '', _(u"Used in typing. A kind of type, larger than pearl and smaller than nonpareil; in England called ruby. 5 1/2 pt in height.")],
+               _(u"nonpareil (Typographical)"):
+                       [(converters.m, 6*0.35146e-3), '', _(u"6 pt in height.")],
+               _(u"minion (Typographical)"):
+                       [(converters.m, 7*0.35146e-3), '', _(u"7 pt in height.")],
+               _(u"brevier (Typographical)"):
+                       [(converters.m, 8*0.35146e-3), '', _(u"8 pt in height.")],
+               _(u"bourgeois (Typographical)"):
+                       [(converters.m, 9*0.35146e-3), '', _(u"9 pt in height.")],
+               _(u"elite | long primer (Typographical)"):
+                       [(converters.m, 10*0.35146e-3), '', _(u"10 pt in height.")],
+               _(u"small pica (Typographical)"):
+                       [(converters.m, 11*0.35146e-3), '', _(u"11 pt in height.")],
+               _(u"pica (Typographical)"):
+                       [(converters.m, 12*0.35146e-3), '', _(u"A size of type next larger than small pica, and smaller than English.12 pt in height")],
+               _(u"english (Typographical)"):
+                       [(converters.m, 14*0.35146e-3), '', _(u"14 pt in height.")],
+               _(u"columbian (Typographical)"):
+                       [(converters.m, 16*0.35146e-3), '', _(u"16 pt in height.")],
+               _(u"great primer (Typographical)"):
+                       [(converters.m, 18*0.35146e-3), '', _(u"18 pt in height.")],
+               _(u"point (pica) (Typographical)"):
+                       [(converters.m, 0.35146e-3), "pt", _(u"Typographical measurement. This system was developed in England and is used in Great-Britain and the US. 1 pica equals 12 pica points.")],
+               _(u"point (didot) (Typographical)"):
+                       [(converters.m, 0.376065e-3), "pt", _(u"Typographical measurement. The didot system originated in France but was used in most of Europe")],
+               _(u"cicero (Typographical)"):
+                       [(converters.m, 12*0.376065e-3), '', _(u"Typographical measurement. 1 cicero equals 12 didot points.")],
+               _(u"point (PostScript) (Typographical)"):
+                       [(converters.m, (25.4/1000)/72), "pt", _(u"Typographical measurement. Created by Adobe. There are exactly 72 PostScript points in 1 inch.")],
+
+               _(u"ell (English)"):
+                       [(converters.m, 45*(25.4/1000)), '', _(u"A measure for cloth; -- now rarely used. It is of different lengths in different countries; the English ell being 45 inches, the Dutch or Flemish ell 27, the Scotch about 37.")],
+               _(u"ell (Dutch | Flemish)"):
+                       [(converters.m, 27*(25.4/1000)), '', _(u"A measure for cloth; -- now rarely used. It is of different lengths in different countries; the English ell being 45 inches, the Dutch or Flemish ell 27, the Scotch about 37.")],
+               _(u"ell (Scotch)"):
+                       [(converters.m, 37*(25.4/1000)), '', _(u"A measure for cloth; -- now rarely used. It is of different lengths in different countries; the English ell being 45 inches, the Dutch or Flemish ell 27, the Scotch about 37.")],
+               _(u"em"):
+                       [(converters.m, 0.0003514598), '', _(u"Used in typography. A quadrat, the face or top of which is a perfect square; also, the size of such a square in any given size of type, used as the unit of measurement for that type: 500 m's of pica would be a piece of matter whose length and breadth in pica m's multiplied together produce that number.")],
+               _(u"en"):
+                       [(converters.m, 0.0001757299), '', _(u"Used in typography. Half an em, that is, half of the unit of space in measuring printed matter.")],
+               _(u"fathom"):
+                       [(converters.m, 6*(12*25.4/1000)), '', _(u"6 feet. Approximately the space to which a man can extend his arms.")],
+               _(u"fathom (Greek)"):
+                       [(converters.m, 4*18.20*(25.4/1000)), '', _(u"4 Greek cubits.")],
+               _(u"fermi"):
+                       [(converters.m, 1e-15), '', _(u"a metric unit of length equal to one quadrillionth of a meter ")],
+               _(u"finger breadth"):
+                       [(converters.m, 0.875*(25.4/1000)), '', _(u"The breadth of a finger, or the fourth part of the hand; a measure of nearly an inch.")],
+               _(u"finger length"):
+                       [(converters.m, 4.5*(25.4/1000)), '', _(u"The length of finger, a measure in domestic use in the United States, of about four and a half inches or one eighth of a yard.")],
+               _(u"foot"):
+                       [(converters.m, 12*(25.4/1000)), "ft", _(u"Equivalent to twelve inches; one third of a yard. This measure is supposed to be taken from the length of a man's foot.")],
+               _(u"foot (Assyrian)"):
+                       [(converters.m, 2.63042), '', ''],
+               _(u"foot (Arabian)"):
+                       [(converters.m, 0.31919), '', ''],
+               _(u"foot (Roman) | pes"):
+                       [(converters.m, 0.2959608), '', ''],
+               _(u"foot (geodetic | survey)"):
+                       [(converters.m, 1200.0/3937), '', _(u"A former U.S. definition of the foot as exactly 1200/3937 meter or about 30.48006096 centimeters. This was the official U.S. definition of the foot from 1866 to 1959; it makes the meter equal exactly 39.37 inches. In 1959 the survey foot was replaced by the international foot, equal to exactly 30.48 centimeters. However, the survey foot remains the basis for precise geodetic surveying in the U.S.")],
+               _(u"furlong"):
+                       [(converters.m, 40*5.0292), '', 'The eighth part of a mile; forty rods; two hundred and twenty yards. From the Old English fuhrlang, meaning "the length of a furrow".'],
+               _(u"ghalva"):
+                       [(converters.m, 230.42925), '', _(u"Arabian measure")],
+               _(u"gradus (Roman)"):
+                       [(converters.m, 2.43*(12*25.4/1000)), '', ''],
+               _(u"hand"):
+                       [(converters.m, 0.1016), '', _(u"A measure equal to a hand's breadth, -- four inches; a palm. Chiefly used in measuring the height of horses.")],
+               _(u"inch"):
+                       [(converters.m, (25.4/1000)), "in", _(u"The twelfth part of a foot, commonly subdivided into halves, quarters, eights, sixteenths, etc., as among mechanics. It was also formerly divided into twelve parts, called lines, and originally into three parts, called barleycorns, its length supposed to have been determined from three grains of barley placed end to end lengthwise.")],
+               _(u"ken"):
+                       [(converters.m, 2.11836), '', _(u"Japanese fathom. The ken is the length of a traditional tatami mat.")],
+               _(u"league (land | statute)"):
+                       [(converters.m, 3*1609.344), '', _(u" Used as a land measure. 3 statute miles.")],
+               _(u"league (nautical)"):
+                       [(converters.m, 3*1852), '', _(u" Used as a marine measure. 3 nautical miles.")],
+               _(u"li"):
+                       [(converters.m, 644.652), '', _(u"A Chinese measure of distance, being a little more than one third of a mile.")],
+               _(u"light second"):
+                       [(converters.m, 299792458), '', _(u"The distance over which light can travel in one second; -- used as a unit in expressing stellar distances.")],
+               _(u"light year"):
+                       [(converters.m, 9.460528405106E+15), '', _(u"The distance over which light can travel in a year's time; -- used as a unit in expressing stellar distances. It is more than 63, 000 times as great as the distance from the earth to the sun.")],
+               _(u"line"):
+                       [(converters.m, (25.4/1000)/12), '', _(u"A measure of length; one twelfth of an inch.")],
+               _(u"link (Gunters | surveyors)"):
+                       [(converters.m, 0.201168), '', _(u"""Part of a surveyors instrument (chain) which consists of links and is used in measuring land. One commonly in use is Gunter's chain, which consists of one hundred links, each link being 7.92" in length.""")],
+               _(u"link (US | engineers)"):
+                       [(converters.m, 12*(25.4/1000)), '', _(u"Used by surveyors. In the U.S., where 100-foot chains are more common, the link is the same as the foot. ")],
+               _(u"marathon"):
+                       [(converters.m, 42194.988), '', _(u"a footrace of 26 miles 385 yards")],
+
+               _(u"megameter"):
+                       [(converters.m, 1.0e6), '', _(u"In the metric system, one million meters, or one thousand kilometers.")],
+               _(u"kilometer"):
+                       [(converters.m, 1.0e3), "km", _(u"Being a thousand meters. It is equal to 3, 280.8 feet, or 62137 of a mile.")],
+               _(u"meter"):
+                       [(converters.m, 1.0), "m", _(u"Equal to 39.37 English inches, the standard of linear measure in the metric system of weights and measures. It was intended to be, and is very nearly, the ten millionth part of the distance from the equator to the north pole, as ascertained by actual measurement of an arc of a meridian.")],
+               _(u"centimeter"):
+                       [(converters.m, 1.0e-2), "cm", _(u"""The hundredth part of a meter; a measure of length equal to rather more than thirty-nine hundredths (0.3937) of an inch.""")],
+               _(u"millimeter"):
+                       [(converters.m, 1.0e-3), "mm", _(u"A lineal measure in the metric system, containing the thousandth part of a meter; equal to .03937 of an inch.")],
+               _(u"micrometer | micron"):
+                       [(converters.m, 1.0e-6), u"\xb5m", _(u"A metric unit of length equal to one millionth of a meter. The thousandth part of one millimeter.")],
+               _(u"nanometer"):
+                       [(converters.m, 1.0e-9), "nm", _(u"A metric unit of length equal to one billionth of a meter.")],
+               _(u"picometer"):
+                       [(converters.m, 1.0e-12), '', _(u"A metric unit of length equal to one trillionth of a meter.")],
+               _(u"femtometer"):
+                       [(converters.m, 1.0e-15), '', _(u"A metric unit of length equal to one quadrillionth of a meter.")],
+               _(u"mil"):
+                       [(converters.m, (25.4/1e6)), "mil", _(u"Equal to one thousandth of an inch; used to specify thickness (e.g., of sheets or wire)")],
+               _(u"mile (Roman)"):
+                       [(converters.m, 1479.804), '', _(u"5000 Roman feet.")],
+               _(u"mile (statute)"):
+                       [(converters.m, 1609.344), "mi", _(u"Mile is from the Latin word for 1000 (mille). A mile conforming to statute, that is, in England and the United States, a mile of 5, 280 feet, as distinguished from any other mile.")],
+               _(u"mile (nautical | geographical)"):
+                       [(converters.m, 1852.0), "nmi", _(u"Geographical, or Nautical mile, one sixtieth of a degree of a great circle of the earth, or about 6080.27 feet.")],
+               _(u"nail (cloth)"):
+                       [(converters.m, 0.05715), '', _(u"Used for measuring cloth. 1/20 ell. The length of the last two joints (including the fingernail) of the middle finger. The nail is equivalent to 1/16 yard, 1/4 span.")],
+               _(u"naval shot"):
+                       [(converters.m, 15*6*(12*25.4/1000)), '', _(u"Equal to 15 fathoms")],
+               _(u"pace"):
+                       [(converters.m, 2.5*(12*25.4/1000)), '', _(u"The length of a step in walking or marching, reckoned from the heel of one foot to the heel of the other. Note: Ordinarily the pace is estimated at two and one half linear feet.")],
+               _(u"pace (Roman) | passus"):
+                       [(converters.m, 5*0.2959608), '', _(u" The Roman pace (passus) was from the heel of one foot to the heel of the same foot when it next touched the ground, five Roman feet.")],
+               _(u"pace (quick-time marching)"):
+                       [(converters.m, 30*(25.4/1000)), '', _(u"The regulation marching pace in the English and United States armies is thirty inches for quick time.")],
+               _(u"pace (double-time marching)"):
+                       [(converters.m, 36*(25.4/1000)), '', _(u"The regulation marching pace in the English and United States armies is thirty-six inches for double time. ")],
+               _(u"palm (Greek)"):
+                       [(converters.m, 7.71313333333333e-02), '', _(u"A lineal measure equal either to the breadth of the hand or to its length from the wrist to the ends of the fingers; a hand; -- used in measuring a horse's height. In Greece, the palm was reckoned at three inches. At the present day, this measure varies in the most arbitrary manner, being different in each country, and occasionally varying in the same. One third of a Greek span, ")],
+               _(u"palm (Roman lesser)"):
+                       [(converters.m, 2.91*(25.4/1000)), '', _(u"A lineal measure equal either to the breadth of the hand or to its length from the wrist to the ends of the fingers; a hand; -- used in measuring a horse's height. One of two Roman measures of the palm, the lesser palm is 2.91 inches. At the present day, this measure varies in the most arbitrary manner, being different in each country, and occasionally varying in the same.")],
+               _(u"palm (Roman greater)"):
+                       [(converters.m, 8.73*(25.4/1000)), '', _(u"A lineal measure equal either to the breadth of the hand or to its length from the wrist to the ends of the fingers; a hand; -- used in measuring a horse's height. One of two Roman measures of the palm, the greater palm is 8.73 inches. At the present day, this measure varies in the most arbitrary manner, being different in each country, and occasionally varying in the same.")],
+               _(u"parasang"):
+                       [(converters.m, 3.5*1609.344), '', _(u"A Persian measure of length, which, according to Herodotus and Xenophon, was thirty stadia, or somewhat more than three and a half miles. The measure varied in different times and places, and, as now used, is estimated at three and a half English miles.")],
+               _(u"parsec"):
+                       [(converters.m, 3.08567758767931e16), '', _(u"A unit of astronomical length based on the distance from  Earth at which stellar parallax is 1 second of arc; equivalent to 3.262 light years")],
+               _(u"rod | pole | perch"):
+                       [(converters.m, 5.0292), '', _(u"Containing sixteen and a half feet; -- called also perch, and pole.")],
+               _(u"ri"):
+                       [(converters.m, 3926.79936), '', _(u"Japanese league.")],
+               _(u"rope"):
+                       [(converters.m, 20*12*(25.4/1000)), '', _(u"20 feet")],
+               _(u"sadzhens | sagene | sazhen"):
+                       [(converters.m, 2.10312), '', _(u"Russian and East European. Used in previous centuries (until WWI or WWII). The distance between a grown man's spread of arms , from the finger- tips of one to hand to the finger-tips of the other hand. Equal to about 7 feet long (2.13 m).")],
+               _(u"shaku"):
+                       [(converters.m, 0.303022), '', _(u" A Japanese foot. Note: shaku also means area and volume.")],
+               _(u"skein"):
+                       [(converters.m, 120*3*12*(25.4/1000)), '', _(u"120 yards. A skein of cotton yarn is formed by eighty turns of the thread round a fifty-four inch reel.")],
+               _(u"soccer field"):
+                       [(converters.m, 100*3*12*(25.4/1000)), '', _(u"100 yards")],
+               _(u"solar diameter"):
+                       [(converters.m, 1391900000), '', _(u"Diameter of our sun.")],
+               _(u"span (Greek)"):
+                       [(converters.m, 0.231394), '', _(u"To measure by the span of the hand with the fingers extended, or with the fingers encompassing the object; as, to span a space or distance; to span a cylinder. One half of a Greek cubit.")],
+               _(u"span (cloth)"):
+                       [(converters.m, 9*(25.4/1000)), '', _(u"9 inches")],
+               _(u"spindle (cotten yarn)"):
+                       [(converters.m, 15120*3*12*(25.4/1000)), '', _(u"A cotten yarn measure containing 15, 120 yards.")],
+               _(u"spindle (linen yarn)"):
+                       [(converters.m, 14400*3*12*(25.4/1000)), '', _(u"A linen yarn measure containing 14, 400 yards.")],
+               _(u"stadia (Greek) | stadion"):
+                       [(converters.m, 185.1152), '', _(u"A Greek measure of length, being the chief one used for itinerary distances, also adopted by the Romans for nautical and astronomical measurements. It was equal to 600 Greek or 625 Roman feet, or 125 Roman paces, or to 606 feet 9 inches English. This was also called the Olympic stadium, as being the exact length of the foot-race course at Olympia.")],
+               _(u"stadium (Persian)"):
+                       [(converters.m, 214.57962), '', ''],
+               _(u"stadium (Roman)"):
+                       [(converters.m, 184.7088), '', _(u"A Greek measure of length, being the chief one used for itinerary distances, also adopted by the Romans for nautical and astronomical measurements. It was equal to 600 Greek or 625 Roman feet, or 125 Roman paces, or to 606 feet 9 inches English. This was also called the Olympic stadium, as being the exact length of the foot-race course at Olympia.")],
+               _(u"sun (Japanese)"):
+                       [(converters.m, 0.0303022), '', _(u"Japanese measurement.")],
+               _(u"toise (French)"):
+                       [(converters.m, 6.395*12*(25.4/1000)), '', _(u"French fathom.")],
+               _(u"vara (Spanish)"):
+                       [(converters.m, 33.385*(25.4/1000)), '', _(u"A Spanish measure of length equal to about one yard. 33.385 inches. ")],
+               _(u"vara (Mexican)"):
+                       [(converters.m, 0.837946), '', _(u"A Mexican measure of length equal to about one yard. 32.99 inches. ")],
+               _(u"verst | werst"):
+                       [(converters.m, 3500*12*(25.4/1000)), '', _(u"A Russian measure of length containing 3, 500 English feet.")],
+               _(u"yard"):
+                       [(converters.m, 3*12*(25.4/1000)), "yd", _(u"Equaling three feet, or thirty-six inches, being the standard of English and American measure.")],
+       },
+
+       _(u"Luminance"): {
+               ".base_unit": "candela per square meter",
+               _(u"magnitudes per square arcsecond"):
+               [(converters.f, ('108000*(10**(-0.4*x))', 'log((x/108000), 10)/-0.4')), "mags/arcsec2", _(u"Used by astronomers to define the darkness of the night sky. Stars are rated by brightness in magnitudes . A lower magnitude number is a brighter star. The star Vega has a magnitude of zero, and a measurement of 0 magnitudes per square arcsecond would be like having every square arcsecond in the sky will with the brightness of the star Vega.")],
+               _(u"candela per square centimeter"):
+               [(converters.m, 1.0e4), u"cd/cm\xb2", ''],
+               _(u"kilocandela per square meter"):
+               [(converters.m, 1.0e3), u"kcd/m\xb2", ''],
+               _(u"stilb"):
+               [(converters.m, 1.0e4), "sb", 'From a Greek word stilbein meaning "to glitter". Equal to one candela per square centimeter or 104 nits.'],
+               _(u"lambert"):
+               [(converters.m, 3183.09886183791), "L", _(u"Named after the German physicist Johann Heinrich Lambert (1728-1777).Equal to the brightness of a perfectly diffusing surface that emits or reflects one lumen per square centimeter")],
+               _(u"candela per square inch"):
+               [(converters.m, 1550.0031000062), u"cd/in\xb2", ''],
+               _(u"candela per square foot"):
+               [(converters.m, 10.7639104167097), u"cd/ft\xb2", ''],
+               _(u"foot lambert"):
+               [(converters.m, 3.42625909963539), "fL", ''],
+               _(u"millilambert"):
+               [(converters.m, 3.18309886183791), "mL", ''],
+               _(u"candela per square meter"):
+               [(converters.m, 1.0), u"cd/m\xb2", ''],
+               _(u"lumen per steradian square meter"):
+               [(converters.m, 1.0), '', ''],
+               _(u"nit"):
+               [(converters.m, 1.0), '', _(u"Named from the Latin niteo, to shine.")],
+               _(u"apostilb"):
+               [(converters.m, 3.18309886183791/10), "asb", 'Named from the Greek stilbein, to "glitter" or "shine, " with the prefix apo-, "away from." '],
+       },
+
+       _(u"Illumination"): {
+               ".base_unit": "lux",
+               _(u"phot"):
+               [(converters.m, 1.0e4), "ph", _(u"a unit of illumination equal to 1 lumen per square centimeter; 10, 000 phots equal 1 lux")],
+               _(u"lumen per square centimeter"):
+               [(converters.m, 1.0e4), u"lm/cm\xb2", ''],
+               _(u"foot candle"):
+               [(converters.m, 10.7639104167097), "fc", ''],
+               _(u"lumen per square foot"):
+               [(converters.m, 10.7639104167097), u"lm/ft\xb2", ''],
+               _(u"lux"):
+               [(converters.m, 1.0), "lx", _(u"Equal to the illumination produced by luminous flux of one lumen falling perpendicularly on a surface one meter square. Also called meter-candle.")],
+               _(u"metre-candle"):
+               [(converters.m, 1.0), "m-cd", _(u"Equal to the illumination produced by luminous flux of one lumen falling perpendicularly on a surface one meter square. Also called lux.")],
+               _(u"lumen per square meter"):
+               [(converters.m, 1.0), u"lm/m\xb2", ''],
+               _(u"candela steradian per square meter"):
+               [(converters.m, 1.0), '', ''],
+       },
+
+       _(u"Luminous Intensity (point sources)"): {
+               ".base_unit": "candela",
+               _(u"candela"):
+               [(converters.m, 1.0), "cd", _(u"The basic unit of luminous intensity adopted under the System International d'Unites; equal to 1/60 of the luminous intensity per square centimeter of a blackbody radiating at the temperature of 2, 046 degrees Kelvin syn: candle, cd, standard candle.")],
+               _(u"lumen per steradian"):
+               [(converters.m, 1.0), "lm/sr", ''],
+               _(u"hefner candle"):
+               [(converters.m, 0.92), "HC", _(u"Named after F. von Hefner-Altenack (1845-1904)")],
+       },
+
+       _(u"Luminous Flux"): {
+               ".base_unit": "lumen",
+               _(u"lumen"):
+               [(converters.m, 1.0), "lm", _(u"Equal to the luminous flux emitted in a unit solid angle by a point source of one candle intensity")],
+               _(u"candela steradian"):
+               [(converters.m, 1.0), u"cd\xb7sr", ''],
+       },
+
+       _(u"Magnetomotive force"): {
+               ".base_unit": "ampere",
+               _(u"ampere"):
+               [(converters.m, 1.0), "A", ''],
+               _(u"ampere-turn"):
+               [(converters.m, 1.0), "At", _(u"A unit of magnetomotive force equal to the magnetomotive force produced by the passage of 1 ampere through 1 complete turn of a coil.")],
+               _(u"gilbert"):
+               [(converters.m, 0.795775), "Gb", _(u"Named after the English scientist William Gilbert (1544-1603)")],
+               _(u"kiloampere"):
+               [(converters.m, 1e3), "kA", ''],
+               _(u"oersted-centimeter"):
+               [(converters.m, 0.795775), '', _(u"The same value as the gilbert.")],
+       },
+
+       _(u"Magnetic Flux"): {
+               ".base_unit": "weber",
+               _(u"weber"):
+               [(converters.m, 1.0), "Wb", _(u"From the name of Professor Weber, a German electrician. One volt second.")],
+               _(u"milliweber"):
+               [(converters.m, 1.0e-3), "mWb", ''],
+               _(u"microweber"):
+               [(converters.m, 1.0e-6), u"\xb5Wb", ''],
+               _(u"unit pole (electro magnetic unit)"):
+               [(converters.m, 4e-8*math.pi), '', ''],
+               _(u"maxwell"):
+               [(converters.m, 1.0e-8), "Mx", _(u"Named after the Scottish physicist James Clerk Maxwell (1831-1879). A cgs unit of magnetic flux equal to the flux perpendicular to an area of 1 square centimeter in a magnetic field of 1 gauss.")],
+               _(u"line of force"):
+               [(converters.m, 1.0e-8), '', _(u"Same as Maxwell")],
+       },
+
+       _(u"Magnetic Field strength"): {
+               ".base_unit": "ampere per meter",
+               _(u"oersted"):
+               [(converters.m, 1.0e3/(4*math.pi)), "Oe", _(u"Named after the Danish physicist and chemist Hans Christian Oersted (1777-1851). The C.G.S. unit of magnetic reluctance or resistance, equal to the reluctance of a centimeter cube of air (or vacuum) between parallel faces. Also, a reluctance in which unit magnetomotive force sets up unit flux.")],
+               _(u"ampere per meter"):
+               [(converters.m, 1.0), "A/m", ''],
+               _(u"ampere-turn per meter"):
+               [(converters.m, 1.0), "A/m", ''],
+               _(u"kiloampere per meter"):
+               [(converters.m, 1.0e3), "kA/m", ''],
+               _(u"ampere-turn per inch"):
+               [(converters.m, 39.3700787401575), "At/in", ''],
+               _(u"newton per weber"):
+               [(converters.m, 1.0), "N/Wb", _(u"Same as ampere per meter")],
+       },
+
+       _(u"Magnetic Flux Density"): {
+               ".base_unit": "tesla",
+               _(u"tesla"):
+               [(converters.m, 1.0), "T", _(u"Named after the Croatian born inventer Nikola Tesla (1856-1943). A unit of magnetic flux density equal to one weber per square meter.")],
+               _(u"millitesla"):
+               [(converters.m, 1.0e-3), "mT", ''],
+               _(u"microtesla"):
+               [(converters.m, 1.0e-6), u"\xb5T", ''],
+               _(u"nanotesla"):
+               [(converters.m, 1.0e-9), "nT", ''],
+               _(u"weber per square meter"):
+               [(converters.m, 1.0), u"Wb/m\xb2", ''],
+               _(u"kilogauss"):
+               [(converters.m, 1.0e-1), "kG", ''],
+               _(u"gauss"):
+               [(converters.m, 1.0e-4), "G", _(u"Named after German mathematician and astronomer Karl Friedrich Gauss (1777-1855). The C.G.S. unit of density of magnetic field, equal to a field of one line of force per square centimeter, being thus adopted as an international unit at Paris in 1900; sometimes used as a unit of intensity of magnetic field. It was previously suggested as a unit of magnetomotive force.")],
+               _(u"maxwell per square centimeter"):
+               [(converters.m, 1.0e-4), u"Mx/cm\xb2", ''],
+               _(u"maxwell per square inch"):
+               [(converters.m, 1.5500031000062E-05), u"Mx/in\xb2", ''],
+               _(u"line per square inch"):
+               [(converters.m, 1.5500031000062E-05), '', _(u"Same as Maxwell per square inch.")],
+               _(u"gamma"):
+               [(converters.m, 1.0e-9), '', _(u"one nanotesla.")],
+       },
+
+       _(u"Mass"): {
+               ".base_unit": "kilogram",
+               _(u"talanton"):
+               [(converters.m, 149.9985), '', _(u"Greek measure.")],
+               _(u"oka (Egyptian)"):
+               [(converters.m, 1.248), '', ''],
+               _(u"oka (Greek)"):
+               [(converters.m, 1.2799), '', ''],
+               _(u"okia"):
+               [(converters.m, 0.03744027), '', _(u"Egyptian measure.")],
+               _(u"kat"):
+               [(converters.m, 0.009331), '', _(u"Egyptian measure.")],
+               _(u"kerat"):
+               [(converters.m, 0.00019504), '', _(u"Egyptian measure.")],
+               _(u"pala"):
+               [(converters.m, 0.047173), '', _(u"Indian measure.")],
+               _(u"kona"):
+               [(converters.m, 0.00699828), '', _(u"Indian measure.")],
+               _(u"mast"):
+               [(converters.m, .9331), '', _(u"British")],
+               _(u"kilogram"):
+               [(converters.m, 1.0), "kg", _(u"A measure of weight, being a thousand grams, equal to 2.2046 pounds avoirdupois (15, 432.34 grains). It is equal to the weight of a cubic decimeter of distilled water at the temperature of maximum density, or 39deg Fahrenheit.")],
+               _(u"megagram"):
+               [(converters.m, 1.0e3), "Mg", ''],
+               _(u"gram"):
+               [(converters.m, 1.0e-3), "g", _(u"The unit of weight in the metric system. It was intended to be exactly, and is very nearly, equivalent to the weight in a vacuum of one cubic centimeter of pure water at its maximum density. It is equal to 15.432 grains.")],
+               _(u"milligram"):
+               [(converters.m, 1.0e-6), "mg", _(u"A measure of weight, in the metric system, being the thousandth part of a gram, equal to the weight of a cubic millimeter of water, or .01543 of a grain avoirdupois.")],
+               _(u"microgram"):
+               [(converters.m, 1.0e-9), u"\xb5g", _(u"A measure of weight, in the metric system, being the millionth part of a gram.")],
+               _(u"ton (UK | long | gross | deadweight)"):
+               [(converters.m, 2240 * 0.45359237), '', _(u"A British unit of weight equivalent to 2240 pounds")],
+               _(u"ton (US | short)"):
+               [(converters.m, 2000 * 0.45359237), "tn", _(u"A US unit of weight equivalent to 2000 pounds")],
+               _(u"tonne | metric ton"):
+               [(converters.m, 1.0e3), "t", _(u"A metric ton, One Megagram. 1000 kg")],
+               _(u"pound (avoirdupois)"):
+               [(converters.m, 0.45359237), "lb", _(u"The pound in general use in the United States and in England is the pound avoirdupois, which is divided into sixteen ounces, and contains 7, 000 grains. The pound troy is divided into twelve ounces, and contains 5, 760 grains. 144 pounds avoirdupois are equal to 175 pounds troy weight")],
+               _(u"pound (troy)"):
+               [(converters.m, 0.3732417216), '', ''],
+               _(u"hundredweight (short | net | US)"):
+               [(converters.m, 100*0.45359237), "cwt", _(u"A denomination of weight of 100 pounds. In most of the United States, both in practice and by law, it is 100 pounds avoirdupois.")],
+               _(u"hundredweight (long | English)"):
+               [(converters.m, 112*0.45359237), "cwt", _(u"A denomination of weight of 112 pounds")],
+               _(u"slug"):
+               [(converters.m, 14.5939029372064), '', _(u"One slug is the mass accelerated at 1 foot per second per second by a force of 1 pound.")],
+               _(u"ounce (troy)"):
+               [(converters.m, 0.0311034768), "ozt", _(u"A unit of apothecary weight equal to 480 grains.")],
+               _(u"ounce (avoirdupois)"):
+               [(converters.m, 0.45359237/16), "oz", _(u"A weight, the sixteenth part of a pound avoirdupois")],
+               _(u"dram (avoirdupois)"):
+               [(converters.m, (0.45359237/16)/16), '', _(u"A weight; in Avoirdupois weight, one sixteenth part of an ounce.")],
+       _(u"dram (troy | apothecary)"):
+               [(converters.m, (0.0311034768)/8), '', _(u"""A weight; in Apothecaries' weight, one eighth part of an ounce, or sixty grains.""")],
+               _(u"scruple (troy)"):
+                       [(converters.m, 20*(0.45359237/5760)), '', _(u"A weight of twenty grains; the third part of a troy dram.")],
+               _(u"carat"):
+                       [(converters.m, 0.0002), '', _(u"The weight by which precious stones and pearls are weighed.")],
+               _(u"grain"):
+                       [(converters.m, 0.00006479891), "gr", _(u"The unit of the English system of weights; -- so called because considered equal to the average of grains taken from the middle of the ears of wheat. 7, 000 grains constitute the pound avoirdupois and 5, 760 grains constitute the pound troy.")],
+               _(u"amu (atomic mass unit) | dalton"):
+                       [(converters.m, 1.66044E-27), "amu", _(u"Unit of mass for expressing masses of atoms or molecules.")],
+               _(u"catty | caddy | chin"):
+                       [(converters.m, (4.0/3)*0.45359237), '', _(u"An Chinese or East Indian Weight of 1 1/3 pounds.")],
+               _(u"cental"):
+                       [(converters.m, 100*0.45359237), '', _(u"British for 100 pounds. Also called hundredweight in the US.")],
+               _(u"cotton bale (US)"):
+                       [(converters.m, 500*0.45359237), '', _(u"US measurement. 500 pounds")],
+               _(u"cotton bale (Egypt)"):
+                       [(converters.m, 750*0.45359237), '', _(u"Egyptian measurement. 750 pounds")],
+               _(u"crith"):
+                       [(converters.m, 0.0000906), '', _(u"From the Greek word for barleycorn. The weight of a liter of hydrogen at 0.01\xb0 centigrade and with a and pressure of 1 atmosphere.")],
+               _(u"denarius"):
+                       [(converters.m, 60*(0.45359237/5760)), '', _(u"Roman weight measuring 60 troy grains")],
+               _(u"dinar"):
+                       [(converters.m, 4.2e-3), '', _(u"Arabian weight measuring 4.2 gram")],
+               _(u"doppelzentner"):
+                       [(converters.m, 100.0), '', _(u"Metric hundredweight = 100 kg")],
+               _(u"drachma (Greek)"):
+                       [(converters.m, 0.0042923), '', _(u"The weight of an old Greek drachma coin")],
+               _(u"drachma (Dutch)"):
+                       [(converters.m, 3.906e-3), '', _(u"The weight of an old Dutch drachma coin")],
+               _(u"earth mass"):
+                       [(converters.m, 5.983E+24), '', _(u"Mass of the Earth.")],
+               _(u"electron rest mass"):
+                       [(converters.m, 9.109558E-31), '', _(u"The mass of an electron as measured when the it is at rest relative to an observer, an inherent property of the body.")],
+               _(u"funt"):
+                       [(converters.m, 0.408233133), '', _(u"Russian, 0.9 pounds")],
+               _(u"obolos (Ancient Greece)"):
+                       [(converters.m, 0.0042923/6), '', _(u"Ancient Greek weight of an obol coin, 1/6 drachma")],
+               _(u"obolos (Modern Greece)"):
+                       [(converters.m, 1.0e-4), '', _(u"Modern Greek name for decigram.")],
+               _(u"hyl"):
+                       [(converters.m, 0.00980665), '', _(u"From an ancient Greek word for matter. One hyl is the mass that is accelerated at one meter per second per second by one kilogram of force. 0.00980665 kg.")],
+               _(u"pennyweight (troy)"):
+                       [(converters.m, 24*0.00006479891), '', _(u"A troy weight containing twenty-four grains, or the twentieth part of a troy ounce; as, a pennyweight of gold or of arsenic. It was anciently the weight of a silver penny.")],
+               _(u"bekah (Biblical)"):
+                       [(converters.m, 5*24*0.00006479891), '', _(u"1/2 shekel, 5 pennyweight.")],
+               _(u"shekel (Israeli)"):
+                       [(converters.m, 10*24*0.00006479891), '', _(u"The sixtieth part of a mina. Ten pennyweight. An ancient weight and coin used by the Jews and by other nations of the same stock.")],
+               _(u"mina (Greek) | minah (Biblical)"):
+                       [(converters.m, 60*10*24*0.00006479891), '', _(u"The weight of the ancient Greek mina coin. 60 shekels")],
+               _(u"talent (Roman)"):
+                       [(converters.m, 125*0.3265865064), '', _(u"125 Roman libra.")],
+               _(u"talent (silver)"):
+                       [(converters.m, 3000*10*24*0.00006479891), '', _(u"3, 000 shekels or 125 lbs.")],
+               _(u"talent (gold)"):
+                       [(converters.m, 6000*10*24*0.00006479891), '', _(u"2 silver talents, 250 lbs.")],
+               _(u"talent (Hebrew)"):
+                       [(converters.m, 26.332), '', ''],
+               _(u"kin"):
+                       [(converters.m, 0.60010270551), '', _(u"Japanese kin,  1.323 pound.")],
+               _(u"kwan"):
+                       [(converters.m, 3.7512088999), '', _(u"Japanese kwan. 8.27 pound")],
+               _(u"liang | tael"):
+                       [(converters.m, ((4.0/3)*0.45359237)/16), '', _(u"Chinese. 1/16 catty")],
+               _(u"libra | librae | as | pondus"):
+                       [(converters.m, 0.3265865064), '', _(u"Roman originator of the English pound (lb). 12 uncia")],
+               _(u"libra (Mexican)"):
+                       [(converters.m, 0.46039625555), '', ''],
+               _(u"libra (Spanish)"):
+                       [(converters.m, 0.45994266318), '', ''],
+               _(u"livre (French)"):
+                       [(converters.m, 0.49), '', ''],
+               _(u"quarter (long)"):
+                       [(converters.m, (112*0.45359237)/4), '', _(u"The fourth part of a long hundredweight. 28 pounds")],
+               _(u"quarter (short)"):
+                       [(converters.m, (100*0.45359237)/4), '', _(u"The fourth part of a short hundredweight. 25 pounds")],
+               _(u"mite (English)"):
+                       [(converters.m, 0.0000032399455), '', _(u"A small weight; one twentieth of a grain.")],
+               _(u"neutron rest mass"):
+                       [(converters.m, 1.67492E-27), '', _(u"The mass of a neutron as measured when the it is at rest relative to an observer, an inherent property of the body.")],
+               _(u"proton rest mass"):
+                       [(converters.m, 1.672614E-27), '', _(u"The mass of a proton as measured when the it is at rest relative to an observer, an inherent property of the body.")],
+               _(u"pfund (German)"):
+                       [(converters.m, 0.5), '', _(u"German pound. 500 grams. 16 unze.")],
+               _(u"unze (German)"):
+                       [(converters.m, 0.5/16), '', _(u"German ounce. 1/16 pfund.")],
+               _(u"lot (German)"):
+                       [(converters.m, 0.5/32), '', _(u"One half unze.")],
+               _(u"picul | tan | pecul | pecal (Chinese | Summatra))"):
+                       [(converters.m, 133.5*0.45359237), '', _(u"100 catty. 133 1/2 pounds")],
+               _(u"picul (Japan)"):
+                       [(converters.m, (400.0/3)*0.45359237), '', _(u"133 1/3 pounds")],
+               _(u"picul (Borneo)"):
+                       [(converters.m, (1085.0/8)*0.45359237), '', _(u"135 5/8 pounds")],
+               _(u"pood (Russian)"):
+                       [(converters.m, 16.3792204807), '', _(u"A Russian weight, equal to forty Russian pounds or about thirty-six English pounds avoirdupois.")],
+               _(u"quintal"):
+                       [(converters.m, 100.0), '', _(u"A metric measure of weight, being 100, 000 grams, or 100 kilograms")],
+               _(u"quintal (short UK)"):
+                       [(converters.m, 100*0.45359237), '', _(u"100 pounds")],
+               _(u"quintal (long UK)"):
+                       [(converters.m, 112*0.45359237), '', _(u"112 pounds")],
+               _(u"quintal (Spanish)"):
+                       [(converters.m, 45.994266318), '', _(u"Spanish hundredweight")],
+               _(u"scrupulum (Roman)"):
+                       [(converters.m, 0.0011359248923), '', ''],
+               _(u"stone (legal)"):
+                       [(converters.m, 14*0.45359237), '', _(u"14 pounds")],
+               _(u"stone (butchers)"):
+                       [(converters.m, 8*0.45359237), '', _(u"Meat or fish. 8 pounds")],
+               _(u"stone (cheese)"):
+                       [(converters.m, 16*0.45359237), '', _(u"16 pounds.")],
+               _(u"stone (hemp)"):
+                       [(converters.m, 32*0.45359237), '', _(u"32 pounds")],
+               _(u"stone (glass)"):
+                       [(converters.m, 5*0.45359237), '', _(u"5 pounds")],
+               _(u"uncia"):
+                       [(converters.m, 0.3265865064/12), '', _('Ancient Roman. A twelfth part, as of the Roman "as" or "libra"; an ounce. 420 grains')],
+       },
+
+       _(u"Musical notes"): {
+               ".base_unit": "breve",
+               _(u"whole note | semibreve"):
+               [(converters.m, 0.5), '', _(u"A note of half the time or duration of the breve; -- now usually called a whole note.")],
+               _(u"breve"):
+               [(converters.m, 1.0), '', _(u"A note or character of time, equivalent to two semibreves or four minims. When dotted, it is equal to three semibreves.")],
+               _(u"minim"):
+               [(converters.m, 0.25), '', _(u"A time note, a half note, equal to half a semibreve, or two quarter notes or crotchets.")],
+               _(u"crotchet"):
+               [(converters.m, 0.125), '', _(u"A time note, with a stem, having one fourth the value of a semibreve, one half that of a minim, and twice that of a quaver; a quarter note.")],
+               _(u"quaver"):
+               [(converters.m, 0.0625), '', _(u"An eighth note.")],
+       },
+
+       _(u"Power"): {
+               ".base_unit": "watt",
+               _(u"megawatt"):
+               [(converters.m, 1.0e6), "MW", ''],
+               _(u"kilowatt"):
+               [(converters.m, 1.0e3), "kW", ''],
+               _(u"watt"):
+               [(converters.m, 1.0), "W", _(u"Named after the Scottish engineer and inventor James Watt (1736-1819). A unit of power or activity equal to 10^7 C.G.S. units of power, or to work done at the rate of one joule a second.")],
+               _(u"milliwatt"):
+               [(converters.m, 1.0e-3), "mW", ''],
+               _(u"microwatt"):
+               [(converters.m, 1.0e-6), "uW", ''],
+
+               _(u"horsepower (boiler)"):
+               [(converters.m, 9.81e3), '', _(u"A unit of power representing the power exerted by a horse in pulling.")],
+               _(u"horsepower"):
+               [(converters.m, 746.0), "hp", ''],
+               _(u"ton of refrigeration"):
+               [(converters.m, 10.0/3*1055.05585262), "TR", ''],
+               _(u"btu per second"):
+               [(converters.m, 1055.05585262), "Btu/s", ''],
+               _(u"calorie per second"):
+               [(converters.m, 4.1868), "cal/s", ''],
+               _(u"kilcalorie per hour"):
+               [(converters.m, 4186.8/3600), "kcal/h", _(u"Useful for calculating heating facilities and kitchens.")],
+               _(u"frig per hour"):
+               [(converters.m, 4186.8/3600), "frig/h", _(u"The same as kcal/h, but used for air conditioning and refrigerating.")],
+               _(u"foot pound force per second"):
+               [(converters.m, 1.356), "lbf/s", ''],
+               _(u"joule per second"):
+               [(converters.m, 1.0), "J/s", ''],
+               _(u"newton meter per second"):
+               [(converters.m, 1.0), u"N\xb7m/s", ''],
+               _(u"btu per hour"):
+               [(converters.m, 0.293071070172222), "Btu/h", ''],
+               _(u"foot pound force per minute"):
+               [(converters.m, 0.0226), u"ft\xb7lbf/min", ''],
+               _(u"erg per second"):
+               [(converters.m, 1.0e-7), "erg/s", ''],
+               _(u"dyne centimeter per second"):
+               [(converters.m, 1.0e-7), '', ''],
+               _(u"lusec"):
+               [(converters.m, 0.000133322368421), '', _(u"Used to measure the leakage of vacuum pumps. A flow of one liter per second at a pressure of one micrometer of mercury.")],
+       },
+
+       _(u"Pressure and Stress"): {
+               ".base_unit": "pascal",
+               _(u"pascal"):
+               [(converters.m, 1.0), "Pa", _(u"Named after the French philosopher and mathematician Blaise Pascal (1623 - 1662). Equal to one newton per square meter.")],
+               _(u"hectopascal"):
+               [(converters.m, 100), "hPa", ''],
+               _(u"kilopascal"):
+               [(converters.m, 1.0e3), "kPa", ''],
+               _(u"megapascal"):
+               [(converters.m, 1.0e6), "MPa", ''],
+               _(u"atmosphere (absolute, standard)"):
+               [(converters.m, 101325), "atm", _(u"The average pressure of the Earth's atmosphere at sea level.")],
+               _(u"atmosphere (technical)"):
+               [(converters.m, 98066.5), "atm", _(u"A metric unit equal to one kilogram of force per square centimeter.")],
+               _(u"bar"):
+               [(converters.m, 1.0e5), "bar", _(u"From the Greek word baros.")],
+               _(u"pound force per square inch"):
+               [(converters.m, 6894.75729316836), "psi", ''],
+               _(u"ounces per square inch"):
+               [(converters.m, 6894.75729316836/16), u"oz/in\xb2", ''],
+               _(u"feet of water (60F, 15.5C)"):
+               [(converters.m, 12*133.322*1.866), "ftH20", ''],
+               _(u"inches of water (60F, 15.5C)"):
+               [(converters.m, 133.322*1.866), "inH20", ''],
+               _(u"meter of water (60F, 15.5C)"):
+               [(converters.m, 133.322*1.866/.0254), "mH20", ''],
+               _(u"centimeter of water (60F, 15.5C)"):
+               [(converters.m, 133.322*1.866/2.54), "cmH20", ''],
+               _(u"millimeter of water (60F, 15.5C)"):
+               [(converters.m, 133.322*1.866/25.4), "mmH20", ''],
+
+               _(u"feet of water (39.2F, 4C)"):
+               [(converters.m, 2988.9921933), "ftH20", ''],
+               _(u"inches of water (39.2F, 4C)"):
+               [(converters.m, 249.0826828), "inH20", ''],
+               _(u"meter of water (39.2F, 4C)"):
+               [(converters.m, 9806.4048337), "mH20", ''],
+               _(u"centimeter of water (39.2F, 4C)"):
+               [(converters.m, 98.0640483), "cmH20", ''],
+               _(u"millimeter of water (39.2F, 4C)"):
+               [(converters.m, 9.80640483), "mmH20", ''],
+
+               _(u"inches of mercury (60F, 15.5C)"):
+               [(converters.m, 3337.0), "inHg", ''],
+               _(u"millimeter of mercury (0C)"):
+               [(converters.m, 133.322368421), "mmHg", ''],
+               _(u"inches of mercury (0C)"):
+               [(converters.m, 133.322368421*25.4), "inHg", ''],
+               _(u"micrometer of mercury (0C)"):
+               [(converters.m, 0.133322368421), u"\xb5mHg", ''],
+               _(u"centimeter of mercury (0C)"):
+               [(converters.m, 1333.22368421), "cmHg", ''],
+               _(u"foot of mercury (0C)"):
+               [(converters.m, 1333.22368421*25.4), "ftHg", ''],
+               _(u"torricelli"):
+                       [(converters.m, 133.322368421), "torr", _(u"Named after Italian physicist and mathematician Evangelista Torricelli, (1608-1647). A unit of pressure equal to 0.001316 atmosphere.")],
+               _(u"micron"):
+                       [(converters.m, 133.322368421/1000), u"\xb5", _(u"Used in vacuum technology. Equal to 1 millitorr.")],
+               _(u"millibar"):
+                       [(converters.m, 1.0e2), "mbar", ''],
+               _(u"pound force per square foot"):
+                       [(converters.m, 47.8802589803358), u"lbf/ft\xb2", ''],
+               _(u"tons (UK) per square foot"):
+                       [(converters.m, 47.8802589803358*2240), u"tons(UK)/ft\xb2", ''],
+               _(u"tons (US) per square foot"):
+                       [(converters.m, 47.8802589803358*2000), u"tons(US)/ft\xb2", ''],
+               _(u"kilogram force per square meter"):
+                       [(converters.m, 9.80665), u"kgf/m\xb2", ''],
+               _(u"kilogram force per square centimeter"):
+                       [(converters.m, 9.80665e4), u"kgf/cm\xb2", _(u"Used for ground pressure and steel stress.")],
+               _(u"newton per square meter"):
+                       [(converters.m, 1.0), u"N/m\xb2", ''],
+               _(u"newton per square centimeter"):
+                       [(converters.m, 1.0e4), u"N/cm\xb2", ''],
+               _(u"newton per square millimeter"):
+                       [(converters.m, 1.0e6), u"N/mm\xb2", _(u"Used for concrete stress.")],
+               _(u"kiloNewton per square meter"):
+                       [(converters.m, 1.0e3), u"kN/m\xb2", _(u"Used for ground pressure.")],
+               _(u"kiloNewton per square centimeter"):
+                       [(converters.m, 1.0e7), u"kN/cm\xb2", _(u"Used for loads and concrete stress.")],
+               _(u"microbar"):
+                       [(converters.m, 1.0e-1), u"\xb5bar", ''],
+               _(u"dyne per square centimeter"):
+                       [(converters.m, 1.0e-1), u"dyn/cm\xb2", ''],
+               _(u"barie | barye"):
+                       [(converters.m, 0.1), '', ''],
+               _(u"pieze"):
+                       [(converters.m, 1.0e3), '', _(u"From the Greek word piezein (to press). The pieze is a pressure of one sthene per square meter. 1000 newtons per square meter.")],
+       },
+
+       _(u"Prefixes and Suffixes"): {
+               ".base_unit": "one | mono",
+               _(u"centillion (US)"):
+               [(converters.m, 1.0e303), '', _(u"10^303. Note: British word centillion means 10^600 (too big for this program to represent as floating point).")],
+               _(u"novemtrigintillion (US) | vigintillion (UK)"):
+               [(converters.m, 1.0e120), '', _(u"10^120. ")],
+               _(u"octotrigintillion (US)"):
+               [(converters.m, 1.0e117), '', _(u"10^117. ")],
+               _(u"septentrigintillion (US) | novemdecillion (UK)"):
+               [(converters.m, 1.0e114), '', _(u"10^114. ")],
+               _(u"sextrigintillion (US)"):
+               [(converters.m, 1.0e111), '', _(u"10^111. ")],
+               _(u"quintrigintillion (US) | octodecillion (UK)"):
+               [(converters.m, 1.0e108), '', _(u"10^108. ")],
+               _(u"quattuortrigintillion (US)"):
+               [(converters.m, 1.0e105), '', _(u"10^105. ")],
+               _(u"tretrigintillion (US) | septendecillion (UK)"):
+               [(converters.m, 1.0e102), '', _(u"10^102. ")],
+               _(u"googol"):
+               [(converters.m, 1.0e100), '', _(u"10^100 Ten dotrigintillion (US). Note: a googolplex is 10^10^10^2.")],
+               _(u"dotrigintillion (US)"):
+               [(converters.m, 1.0e99), '', _(u"10^99. ")],
+               _(u"untrigintillion (US) | sexdecillion (UK)"):
+               [(converters.m, 1.0e96), '', _(u"10^96. ")],
+               _(u"trigintillion (US)"):
+               [(converters.m, 1.0e93), '', _(u"10^93. ")],
+               _(u"novemvigintillion (US) | quindecillion (UK)"):
+               [(converters.m, 1.0e90), '', _(u"10^90. ")],
+               _(u"octovigintillion (US)"):
+               [(converters.m, 1.0e87), '', _(u"10^87. ")],
+               _(u"septenvigintillion (US) | quattuordecillion (UK)"):
+               [(converters.m, 1.0e84), '', _(u"10^84. ")],
+               _(u"sexvigintillion (US)"):
+               [(converters.m, 1.0e81), '', _(u"10^81. ")],
+               _(u"quinvigintillion (US) | tredecillion (UK)"):
+               [(converters.m, 1.0e78), '', _(u"10^78. ")],
+               _(u"quattuorvigintillion (US)"):
+               [(converters.m, 1.0e75), '', _(u"10^75. ")],
+               _(u"trevigintillion (US) | duodecillion (UK)"):
+               [(converters.m, 1.0e72), '', _(u"10^72. ")],
+               _(u"dovigintillion (US)"):
+               [(converters.m, 1.0e69), '', _(u"10^69. ")],
+               _(u"unvigintillion (US) | undecillion (UK"):
+               [(converters.m, 1.0e66), '', _(u"10^66. ")],
+               _(u"vigintillion (US)"):
+               [(converters.m, 1.0e63), '', _(u"10^63. ")],
+               _(u"novemdecillion (US) | decillion (UK)"):
+               [(converters.m, 1.0e60), '', _(u"10^60. ")],
+               _(u"octodecillion (US)"):
+               [(converters.m, 1.0e57), '', _(u"10^57. ")],
+               _(u"septendecillion (US) | nonillion (UK)"):
+               [(converters.m, 1.0e54), '', _(u"10^54. ")],
+               _(u"sexdecillion (US)"):
+               [(converters.m, 1.0e51), '', _(u"10^51. ")],
+               _(u"quindecillion (US) | octillion (UK)"):
+                       [(converters.m, 1.0e48), '', _(u"10^48. ")],
+               _(u"quattuordecillion (US)"):
+                       [(converters.m, 1.0e45), '', _(u"10^45. ")],
+               _(u"tredecillion (US) | septillion (UK)"):
+                       [(converters.m, 1.0e42), '', _(u"10^42. ")],
+               _(u"duodecillion (US) | chici"):
+                       [(converters.m, 1.0e39), "Ch", _(u"10^39. chici coined by Morgan Burke after Marx brother Chico Marx.")],
+               _(u"undecillion (US) | sextillion (UK) | gummi"):
+                       [(converters.m, 1.0e36), "Gm", _(u"10^36. gummi coined by Morgan Burke after Marx brother Gummo Marx.")],
+               _(u"una | decillion (US) | zeppi"):
+                       [(converters.m, 1.0e33), "Zp", _(u"10^33. zeppi coined by Morgan Burke after Marx brother Zeppo Marx.")],
+               _(u"dea | nonillion (US) | quintillion (UK) | grouchi"):
+                       [(converters.m, 1.0e30), "Gc", _(u"10^30. grouchi coined by Morgan Burke after Marx brother Groucho Marx.")],
+               _(u"nea | octillion (US) | quadrilliard (UK) | harpi"):
+                       [(converters.m, 1.0e27), "Hr", _(u"10^27. harpi coined by Morgan Burke after Marx brother Harpo Marx.")],
+               _(u"yotta | septillion (US) | quadrillion (UK)"):
+                       [(converters.m, 1.0e24), "Y", '10^24. Origin Latin penultimate letter y "iota".'],
+               _(u"zetta | sextillion (US) | trilliard (UK)"):
+                       [(converters.m, 1.0e21), "Z", '10^21. Origin Latin ultimate letter z "zeta".'],
+               _(u"exa | quintillion (US) | trillion (UK)"):
+                       [(converters.m, 1.0e18), "E", '10^18. Origin Greek for outside "exo" / Greek six hexa" as in 1000^6.'],
+               _(u"peta | quadrillion (US) | billiard (UK)"):
+                       [(converters.m, 1.0e15), "P", '10^15. Origin Greek for spread "petalos" / Greek five "penta" as in 1000^5. Note: British use the words "1000 billion".'],
+               _(u"tera | trillion (US) | billion (UK)"):
+                       [(converters.m, 1.0e12), "T", '10^12. Origin Greek for monster "teras" / Greek four "tetra" as in 1000^4. Note: British use the word billion.'],
+               _(u"giga"):
+                       [(converters.m, 1.0e9), "G", '10^9. Origin Greek for giant "gigas".'],
+               _(u"billion (US) | milliard (UK)"):
+                       [(converters.m, 1.0e9), '', '10^9.'],
+               _(u"mega | million"):
+                       [(converters.m, 1.0e6), "M", '10^6. One million times. Origin Greek for large, great "megas".'],
+               _(u"hectokilo"):
+                       [(converters.m, 1.0e5), "hk", _(u"10^5. 100 thousand times")],
+               _(u"myra | myria"):
+                       [(converters.m, 1.0e4), "ma", _(u"Ten thousand times, 10^4")],
+               _(u"kilo | thousand"):
+                       [(converters.m, 1.0e3), "k", 'One thousand times, 10^3.Origin Greek for thousand "chylioi".'],
+               _(u"gross"):
+                       [(converters.m, 144.0), '', _(u"Twelve dozen.")],
+               _(u"hecto | hundred"):
+                       [(converters.m, 1.0e2), '', 'One hundred times, 10^2.Origin Greek for hundred "hekaton".'],
+               _(u"vic"):
+                       [(converters.m, 20.0), '', _(u"Twenty times.")],
+               _(u"duodec"):
+                       [(converters.m, 12.0), '', _(u"Twelve times.")],
+               _(u"dozen (bakers | long)"):
+                       [(converters.m, 13.0), '', _(u"Thirteen items. The cardinal number that is the sum of twelve and one syn:  thirteen, 13, XIII, long dozen.")],
+               _(u"dozen"):
+                       [(converters.m, 12.0), '', _(u"Twelve items. Usually used to measure the quantity of eggs in a carton.")],
+               _(u"undec"):
+                       [(converters.m, 11.0), '', _(u"Eleven times.")],
+               _(u"deca | deka | ten"):
+                       [(converters.m, 1.0e1), '', '10^1. Ten times. Origin Greek for ten "deka".'],
+               _(u"sex | hexad"):
+                       [(converters.m, 6.0), '', _(u"Six times.")],
+               _(u"quin"):
+                       [(converters.m, 5.0), '', _(u"Five times.")],
+               _(u"quadr | quadri | quadruple"):
+                       [(converters.m, 4.0), '', _(u"Four times.")],
+               _(u"thrice | tri | triple"):
+                       [(converters.m, 3.0), '', _(u"Three times.")],
+               _(u"bi | double"):
+                       [(converters.m, 2.0), '', _(u"Two times.")],
+               _(u"sesqui | sesqu"):
+                       [(converters.m, 1.5), '', _(u"One and one half times.")],
+               _(u"one | mono"):
+                       [(converters.m, 1.0), '', _(u"Single unit value.")],
+               _(u"quarter"):
+                       [(converters.m, 0.25), '', _(u"One fourth.")],
+               _(u"demi | semi | half"):
+                       [(converters.m, 0.5), '', _(u"One half.")],
+               _(u"eigth"):
+                       [(converters.m, 0.125), '', _(u"One eigth.")],
+               _(u"deci"):
+                       [(converters.m, 1.0e-1), "d", '10^-1. Origin Latin tenth "decimus".'],
+               _(u"centi"):
+                       [(converters.m, 1.0e-2), "c", '10^-2. Origin Latin hundred, hundredth "centum".'],
+               _(u"percent"):
+                       [(converters.m, 1.0e-2), "%", _(u"10^-2. A proportion multiplied by 100")],
+               _(u"milli"):
+                       [(converters.m, 1.0e-3), "m", '10^-3. A prefix denoting a thousandth part of; as, millimeter, milligram, milliampere.Origin Latin thousand "mille".'],
+               _(u"decimilli"):
+                       [(converters.m, 1.0e-4), "dm", _(u"10^-4")],
+               _(u"centimilli"):
+                       [(converters.m, 1.0e-5), "cm", _(u"10^-5. ")],
+               _(u"micro"):
+                       [(converters.m, 1.0e-6), u"\xb5", '10^-6. A millionth part of; as, microfarad, microohm, micrometer.Origin Latin small "mikros".'],
+               _(u"parts per million | ppm"):
+                       [(converters.m, 1.0e-6), "ppm", _(u"10^-6. Parts per million usually used in measuring chemical concentrations.")],
+               _(u"nano"):
+                       [(converters.m, 1.0e-9), "n", '10^-9. Origin Greek dwarf "nanos".'],
+               _(u"pico"):
+                       [(converters.m, 1.0e-12), "p", '10^-12. Origin Italian tiny "piccolo".'],
+               _(u"femto"):
+                       [(converters.m, 1.0e-15), "f", '10^-15. Origin Old Norse fifteen "femten" as in 10^-15.'],
+               _(u"atto"):
+                       [(converters.m, 1.0e-18), "a", '10^-18. Origin Old Norse eighteen "atten" as in 10^-18.'],
+               _(u"zepto | ento"):
+                       [(converters.m, 1.0e-21), "z", '10^-21. zepto origin Latin ultimate letter z "zeta".'],
+               _(u"yocto | fito"):
+                       [(converters.m, 1.0e-24), "y", '10^-24. yocto origin Latin penultimate letter y "iota".'],
+               _(u"syto | harpo"):
+                       [(converters.m, 1.0e-27), "hr", _(u"10^-27. harpo coined by Morgan Burke after Marx brother Harpo Marx.")],
+               _(u"tredo | groucho"):
+                       [(converters.m, 1.0e-30), "gc", _(u"10^-30. groucho coined by Morgan Burke after Marx brother Groucho Marx.")],
+               _(u"revo | zeppo"):
+                       [(converters.m, 1.0e-33), "zp", _(u"10^-33. zeppo coined by Morgan Burke after Marx brother Zeppo Marx.")],
+               _(u"gummo"):
+                       [(converters.m, 1.0e-36), "gm", _(u"10^-36. Coined by Morgan Burke after Marx brother Gummo Marx.")],
+               _(u"chico"):
+                       [(converters.m, 1.0e-39), "ch", _(u"10^-39. Coined by Morgan Burke after Marx brother Chico Marx.")],
+       },
+
+       #There does not seem to be a "standard" for shoe sizes so some are left out for now until a decent reference can be found.
+       _(u"Shoe Size"): {
+               ".base_unit": "centimeter",
+               _(u"centimeter"):
+               [(converters.m, 1.0), "cm", _(u"The hundredth part of a meter; a measure of length equal to rather more than thirty-nine hundredths (0.3937) of an inch.")],
+               _(u"inch"):
+               [(converters.m, (2.54)), "in", _(u"The twelfth part of a foot, commonly subdivided into halves, quarters, eights, sixteenths, etc., as among mechanics. It was also formerly divided into twelve parts, called lines, and originally into three parts, called barleycorns, its length supposed to have been determined from three grains of barley placed end to end lengthwise.")],
+               #"European"):
+               #       [(converters.slo, ((35, 47), (22.5, 30.5))), '', _(u"Used by Birkenstock")],
+               _(u"Mens (US)"):
+               [(converters.slo, ((6.0, 13.0), ((2.54*(9+1.0/3)), (2.54*(11+2.0/3))))), '', 'Starting at 9 1/3" for size 6 and moving up by 1/6" for each half size to 11 2/3" for size 13. Beware that some manufacturers use different measurement techniques.'],
+               _(u"Womens (US)"):
+               [(converters.gof, (2.54*((2.0+2.0/6)/7.0), 2.54*(8.5-(5.0*((2.0+2.0/6)/7.0))))), '', 'Starting at 8 1/2" for size 5 and moving up by 1/6" for each half size to 10 5/6" for size 12. Beware that some manufacturers use different measurement techniques.'],
+               _(u"Childrens (US)"):
+               [(converters.dso, ((5.5, 13.5), ((2.54*(4.0+5.0/6)), (2.54*7.5)), (1.0, 5.0), (2.54*(7.0+2.0/3), 2.54*9.0))), '', 'Starting at 4 5/6" for size 5 1/2 up to 7 1/3" for size 13 then 7 2/3" for size 1 and going up to 9" for size 5.'],
+               _(u"Mens (UK)"):
+               [(converters.slo, ((5.0, 12.0), ((2.54*(9+1.0/3)), (2.54*(11+2.0/3))))), '', 'Starting at 9 1/3" for size 5 and moving up by 1/6" for each half size to 11 2/3" for size 12. Beware that some manufacturers use different measurement techniques.'],
+               _(u"Womens (UK)"):
+               [(converters.gof, (2.54*((2.0+2.0/6)/7.0), 2.54*(8.5-(3.0*((2.0+2.0/6)/7.0))))), '', 'Starting at 8 1/2" for size 3 and moving up by 1/6" for each half size to 10 5/6" for size 10. Beware that some manufacturers use different measurement techniques.'],
+               #"Asian"):
+               #       [(converters.slo, ((23.5, 31.5), (25.0, 30.5))), '', ''],
+       },
+
+       _(u"Speed | Velocity"): {
+               ".base_unit": "meter per second",
+               _(u"meter per second"):
+               [(converters.m, 1.0), "m/s", ''],
+               _(u"speed of light | warp"):
+               [(converters.m, 299792458.0), "c", _(u"The speed at which light travels in a vacuum; about 300, 000 km per second; a universal constant.")],
+               _(u"miles per second"):
+               [(converters.m, 1609.344), "mi/s", ''],
+               _(u"kilometer per second"):
+               [(converters.m, 1.0e3), "km/s", ''],
+               _(u"millimeter per second"):
+               [(converters.m, 1.0e-3), "mm/s", ''],
+               _(u"knot"):
+               [(converters.m, 0.514444444444444), '', _(u"Nautical measurement for speed as one nautical mile per hour. The number of knots which run off from the reel in half a minute, therefore, shows the number of miles the vessel sails in an hour.")],
+               _(u"miles per hour"):
+               [(converters.m, 0.44704), "mi/h", ''],
+               _(u"foot per second"):
+               [(converters.m, 0.3048), "ft/s", ''],
+               _(u"foot per minute"):
+               [(converters.m, 0.00508), "ft/min", ''],
+               _(u"kilometer per hour"):
+               [(converters.m, 0.277777777777778), "km/h", ''],
+               _(u"mile per day"):
+               [(converters.m, 1.86266666666667E-02), "mi/day", ''],
+               _(u"centimeter per second"):
+               [(converters.m, 1.0e-2), "cm/s", ''],
+               _(u"knot (admiralty)"):
+               [(converters.m, 0.514773333333333), '', ''],
+               _(u"mach (sea level & 32 degF)"):
+               [(converters.m, 331.46), '', ''],
+       },
+
+       _(u"Temperature"): {
+               ".base_unit": _(u"kelvin"),
+               _(u"kelvin"):
+               [(converters.m, 1.0), "K", _(u"Named after the English mathematician and physicist William Thomson Kelvin (1824-1907). The basic unit of thermodynamic temperature adopted under the System International d'Unites.")],
+               _(u"celsius (absolute)"):
+               [(converters.m, 1.0), u"\xb0C absolute", ''],
+               _(u"celsius (formerly centigrade)"):
+               [(converters.ofg, (273.15, 1.0)), u"\xb0C", _(u"Named after the Swedish astronomer and physicist Anders Celsius (1701-1744). The Celsius thermometer or scale. It is the same as the centigrade thermometer or scale. 0\xb0 marks the freezing point of water and 100\xb0 marks the boiling point of water.  ")],
+               _(u"fahrenheit"):
+               [(converters.f, ('((x-32.0)/1.8)+273.15', '((x-273.15)*1.8)+32.0')), u"\xb0F", _(u"Named after the German physicist Gabriel Daniel Fahrenheit (1686-1736). The Fahrenheit thermometer is so graduated that the freezing point of water is at 32\xb0 above the zero of its scale, and the boiling point at 212\xb0 above. It is commonly used in the United States and in England.")],
+
+               _(u"reaumur"):
+               [(converters.gof, (1.25, 273.15)), u"\xb0Re", _(u"Named after the French scientist Ren\u00E9-Antoine Ferchault de R\u00E9aumur (1683-1757). Conformed to the scale adopted by R\u00E9aumur in graduating the thermometer he invented. The R\u00E9aumur thermometer is so graduated that 0\xb0 marks the freezing point and 80\xb0 the boiling point of water.")],
+               _(u"fahrenheit (absolute)"):
+               [(converters.m, 1/1.8), u"\xb0F absolute", ''],
+               _(u"rankine"):
+               [(converters.m, 1/1.8), u"\xb0R", _(u"Named after the British physicist and engineer William Rankine (1820-1872). An absolute temperature scale in Fahrenheit degrees.")],
+       },
+
+       _(u"Temperature Difference"): {
+               ".base_unit": "temp. diff. in kelvin",
+               _(u"temp. diff. in kelvin"):
+               [(converters.m, 1.0), "K", ''],
+               _(u"temp. diff. in degrees Celsius"):
+               [(converters.m, 1.0), u"\xb0C", ''],
+               _(u"temp. diff. in degrees Reaumur"):
+               [(converters.m, 1.25), u"\xb0Re", ''],
+               _(u"temp. diff. in degrees Rankine"):
+               [(converters.m, 5.0/9), u"\xb0R", ''],
+               _(u"temp. diff. in degrees Fahrenheit"):
+               [(converters.m, 5.0/9), u"\xb0F", ''],
+       },
+
+       _(u"Time"): {
+               ".base_unit": "second",
+               _(u"year"):
+               [(converters.m, 365*86400.0), "a", _(u"The time of the apparent revolution of the sun trough the ecliptic; the period occupied by the earth in making its revolution around the sun, called the astronomical year; also, a period more or less nearly agreeing with this, adopted by various nations as a measure of time, and called the civil year; as, the common lunar year of 354 days, still in use among the Mohammedans; the year of 360 days, etc. In common usage, the year consists of 365 days, and every fourth year (called bissextile, or leap year) of 366 days, a day being added to February on that year, on account of the excess above 365 days")],
+               _(u"year (anomalistic)"):
+               [(converters.m, 365*86400.0+22428.0), '', _(u"The time of the earth's revolution from perihelion to perihelion again, which is 365 days, 6 hours, 13 minutes, and 48 seconds.")],
+               _(u"year (common lunar)"):
+               [(converters.m, 354*86400.0), '', _(u"The period of 12 lunar months, or 354 days.")],
+               _(u"year (embolismic | Intercalary lunar)"):
+               [(converters.m, 384*86400.0), '', _(u"The period of 13 lunar months, or 384 days.")],
+               _(u"year (leap | bissextile)"):
+               [(converters.m, 366*86400.0), '', _(u"Bissextile; a year containing 366 days; every fourth year which leaps over a day more than a common year, giving to February twenty-nine days. Note: Every year whose number is divisible by four without a remainder is a leap year, excepting the full centuries, which, to be leap years, must be divisible by 400 without a remainder. If not so divisible they are common years. 1900, therefore, is not a leap year.")],
+               _(u"year (sabbatical)"):
+               [(converters.m, 7*365*86400.0), '', _(u"Every seventh year, in which the Israelites were commanded to suffer their fields and vineyards to rest, or lie without tillage.")],
+               _(u"year (lunar astronomical)"):
+               [(converters.m, 354*86400.0+31716.0), '', _(u"The period of 12 lunar synodical months, or 354 days, 8 hours, 48 minutes, 36 seconds.")],
+               _(u"year (lunisolar)"):
+               [(converters.m, 532*365*86400.0), '', _(u"A period of time, at the end of which, in the Julian calendar, the new and full moons and the eclipses recur on the same days of the week and month and year as in the previous period. It consists of 532 common years, being the least common multiple of the numbers of years in the cycle of the sun and the cycle of the moon.")],
+               _(u"year (sidereal)"):
+               [(converters.m, 365*86400.0+22149.3), '', _(u"The time in which the sun, departing from any fixed star, returns to the same. This is 365 days, 6 hours, 9 minutes, and 9.3 seconds.")],
+               _(u"year (sothic)"):
+               [(converters.m, 365*86400.0+6*3600.0), '', _(u"The Egyptian year of 365 days and 6 hours, as distinguished from the Egyptian vague year, which contained 365 days. The Sothic period consists of 1, 460 Sothic years, being equal to 1, 461 vague years. One of these periods ended in July, a.d. 139.")],
+               _(u"year (tropic)"):
+               [(converters.m, 365*86400.0+20926.0), '', _(u"The solar year; the period occupied by the sun in passing from one tropic or one equinox to the same again, having a mean length of 365 days, 5 hours, 48 minutes, 46.0 seconds, which is 20 minutes, 23.3 seconds shorter than the sidereal year, on account of the precession of the equinoxes.")],
+               _(u"month"):
+               [(converters.m, 365*86400.0/12), '', _(u"One of the twelve portions into which the year is divided; the twelfth part of a year, corresponding nearly to the length of a synodic revolution of the moon, -- whence the name. In popular use, a period of four weeks is often called a month.")],
+               _(u"month (sidereal)"):
+               [(converters.m, 27.322*86400.0), '', _(u"Period between successive conjunctions with a star, 27.322 days")],
+               _(u"month (synodic | lunar month | lunation)"):
+               [(converters.m, 29.53059*86400.0), '', _(u"The period between successive new moons (29.53059 days) syn: lunar month, moon, lunation")],
+               _(u"day"):
+               [(converters.m, 86400.0), "d", _(u"The period of the earth's revolution on its axis. -- ordinarily divided into twenty-four hours. It is measured by the interval between two successive transits of a celestial body over the same meridian, and takes a specific name from that of the body. Thus, if this is the sun, the day (the interval between two successive transits of the sun's center over the same meridian) is called a solar day; if it is a star, a sidereal day; if it is the moon, a lunar day.")],
+               _(u"day (sidereal)"):
+               [(converters.m, 86164.09), '', _(u"The interval between two successive transits of the first point of Aries over the same meridian. The Sidereal day is 23 h. 56 m. 4.09 s. of mean solar time.")],
+               _(u"day (lunar | tidal)"):
+               [(converters.m, 86400.0+50*60.0), '', _(u"24 hours 50 minutes used in tidal predictions. ")],
+               _(u"hour"):
+               [(converters.m, 3600.0), "h", _(u"The twenty-fourth part of a day; sixty minutes.")],
+               _(u"minute"):
+               [(converters.m, 60.0), "min", _(u"The sixtieth part of an hour; sixty seconds.")],
+               _(u"second"):
+               [(converters.m, 1.0), "s", _(u"The sixtieth part of a minute of time.")],
+               _(u"millisecond"):
+               [(converters.m, 1.0e-3), "ms", _(u"One thousandth of a second.")],
+               _(u"microsecond"):
+               [(converters.m, 1.0e-6), u"\xb5s", _(u"One millionth of a second.")],
+               _(u"nanosecond"):
+               [(converters.m, 1.0e-9), "ns", ''],
+               _(u"picosecond"):
+               [(converters.m, 1.0e-12), "ps", ''],
+               _(u"millennium"):
+               [(converters.m, 1000*365*86400.0), '', _(u"A thousand years; especially, the thousand years mentioned in the twentieth chapter in the twentieth chapter of Revelation, during which holiness is to be triumphant throughout the world. Some believe that, during this period, Christ will reign on earth in person with his saints.")],
+               _(u"century"):
+               [(converters.m, 100*365*86400.0), '', _(u"A period of a hundred years; as, this event took place over two centuries ago. Note: Century, in the reckoning of time, although often used in a general way of any series of hundred consecutive years (as, a century of temperance work), usually signifies a division of the Christian era, consisting of a period of one hundred years ending with the hundredth year from which it is named; as, the first century (a. d. 1-100 inclusive); the seventh century (a.d. 601-700); the eighteenth century (a.d. 1701-1800). With words or phrases connecting it with some other system of chronology it is used of similar division of those eras; as, the first century of Rome (A.U.C. 1-100).")],
+               _(u"decade"):
+                       [(converters.m, 10*365*86400.0), '', _(u"A group or division of ten; esp., a period of ten years; a decennium; as, a decade of years or days; a decade of soldiers; the second decade of Livy.")],
+               _(u"week"):
+                       [(converters.m, 7*86400.0), '', _(u"A period of seven days, usually that reckoned from one Sabbath or Sunday to the next. Also seven nights, known as sennight.")],
+               _(u"fortnight"):
+                       [(converters.m, 14*86400.0), '', _(u"Fourteen nights, our ancestors reckoning time by nights and winters.  The space of fourteen days; two weeks.")],
+               _(u"novennial"):
+                       [(converters.m, 9*365*86400.0), '', _(u"Done or recurring every ninth year.")],
+               _(u"octennial"):
+                       [(converters.m, 8*365*86400.0), '', _(u"Happening every eighth year; also, lasting a period of eight years.")],
+               _(u"olympiad"):
+                       [(converters.m, 4*365*86400.0), '', _(u"A period of four years, by which the ancient Greeks reckoned time, being the interval from one celebration of the Olympic games to another, beginning with the victory of Coroebus in the foot race, which took place in the year 776 b.c.; as, the era of the olympiads.")],
+               _(u"pregnancy"):
+                       [(converters.m, 9*365*86400.0/12), '', _(u"The condition of being pregnant; the state of being with young. A period of approximately 9 months for humans")],
+               _(u"quindecennial"):
+                       [(converters.m, 15*365*86400.0), '', _(u"A period of 15 years.")],
+               _(u"quinquennial"):
+                       [(converters.m, 5*365*86400.0), '', _(u"Occurring once in five years, or at the end of every five years; also, lasting five years. A quinquennial event.")],
+               _(u"septennial"):
+                       [(converters.m, 7*365*86400.0), '', _(u"Lasting or continuing seven years; as, septennial parliaments.")],
+               _(u"cesium vibrations"):
+                       [(converters.m, 1.0/9192631770.0), "vibrations", _(u"It takes one second for hot cesium atoms to vibrate 9, 192, 631, 770 times (microwaves). This standard was adopted by the International System in 1967.")],
+       },
+
+       _(u"Viscosity (Dynamic)"): {
+               ".base_unit": "pascal-second",
+               _(u"pascal-second"):
+               [(converters.m, 1.0), u"Pa\xb7s", ''],
+               _(u"reyn"):
+               [(converters.m, 6894.75729316836), '', ''],
+               _(u"poise"):
+               [(converters.m, 0.1), "P", ''],
+               _(u"microreyn"):
+               [(converters.m, 6894.75729316836e-6), '', ''],
+               _(u"millipascal-second"):
+               [(converters.m, 1.0e-3), u"mPa\xb7s", ''],
+               _(u"centipoise"):
+               [(converters.m, 1.0e-3), "cP", ''],
+               _(u"micropascal-second"):
+               [(converters.m, 1.0e-6), u"\xb5Pa\xb7s", ''],
+       },
+
+       _(u"Viscosity (Kinematic)"): {
+               ".base_unit": "square meter per second",
+               _(u"square meter per second"):
+               [(converters.m, 1.0), u"m\xb2/s", ''],
+               _(u"square millimeter per second"):
+               [(converters.m, 1.0e-6), u"mm\xb2/s", ''],
+               _(u"square foot per second"):
+               [(converters.m, 0.09290304), u"ft\xb2/s", ''],
+               _(u"square centimetre per second"):
+               [(converters.m, 1.0e-4), u"cm\xb2/s", ''],
+               _(u"stokes"):
+               [(converters.m, 1.0e-4), "St", ''],
+               _(u"centistokes"):
+               [(converters.m, 1.0e-6), "cSt", ''],
+       },
+
+       _(u"Volume and Liquid Capacity"): {
+               ".base_unit": "millilitre",
+               _(u"can #10 can"):
+               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*109.43), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
+               _(u"can #2 can"):
+               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*20.55), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
+               _(u"can #2.5 can"):
+               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*29.79), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
+               _(u"can #5 can"):
+               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*59.1), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
+               _(u"can #1 can (Picnic)"):
+               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*10.94), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
+               _(u"can #1 can (Tall)"):
+               [(converters.m, 4*2*2*8*2*3*4.92892159375/128* 16.70), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
+               _(u"can #303 can"):
+               [(converters.m, 4*2*2*8*2*3*4.92892159375/128* 16.88), '', _(u"Taken from the Can Manufacturers Institute (CMI) http: //www.cancentral.com/standard.cfm#foodcan .")],
+               _(u"barrel (wine UK)"):
+               [(converters.m, 31.5*4*2*2.5*8*2*3*4.73551071041125), '', _(u"31.5 UK Gallons")],
+               _(u"barrel (UK)"):
+               [(converters.m, 36*4*2*2.5*8*2*3*4.73551071041125), '', _(u"36 UK Gallons")],
+               _(u"barrel of oil (US)"):
+               [(converters.m, 42*4*2*2*8*2*3*4.92892159375), "", _(u"Barrel of petroleum (oil), 42 US Gallons")],
+               _(u"barrel (US federal)"):
+               [(converters.m, 31*4*2*2*8*2*3*4.92892159375), "", _(u"31 US Gallons")],
+               _(u"barrel (US)"):
+               [(converters.m, 31.5*4*2*2*8*2*3*4.92892159375), "", _(u"31.5 US Gallons")],
+               _(u"caphite"):
+               [(converters.m, 1374.1046), '', _(u"Ancient Arabian")],
+               _(u"cantaro"):
+               [(converters.m, 13521.1108), '', _(u"Spanish")],
+               _(u"oxybaphon"):
+               [(converters.m, 66.245), '', _(u"Greek")],
+               _(u"cotula | hemina | kotyle"):
+               [(converters.m, 308.3505), '', _(u"Greek")],
+               _(u"cyathos"):
+               [(converters.m, 451.5132), '', _(u"Greek")],
+               _(u"cados"):
+               [(converters.m, 38043.3566), '', _(u"Greek")],
+               _(u"metertes | amphura"):
+               [(converters.m, 39001.092), '', _(u"Greek")],
+               _(u"mushti"):
+               [(converters.m, 60.9653), '', _(u"Indian")],
+               _(u"cab"):
+               [(converters.m, 2202.5036), '', _(u"Israeli")],
+               _(u"hekat"):
+               [(converters.m, 4768.6752), '', _(u"Israeli")],
+               _(u"bath | bu"):
+               [(converters.m, 36871.2), '', _(u"Israeli")],
+               _(u"acetabulum"):
+               [(converters.m, 66.0752), '', _('Roman')],
+               _(u"dash (UK)"):
+               [(converters.m, 4.73551071041125/16), '', _(u"one half of a pinch")],
+               _(u"pinch (UK)"):
+                       [(converters.m, 4.73551071041125/8), '', _(u"One eigth of a teaspoon")],
+               _(u"gallon (UK)"):
+                       [(converters.m, 4*2*2.5*8*2*3*4.73551071041125), '', _(u"A measure of capacity, containing four quarts; -- used, for the most part, in liquid measure, but sometimes in dry measure. The English imperial gallon contains 10 pounds avoirdupois of distilled water at 62\xb0F, and barometer at 30 inches, equal to 277.274 cubic inches.")],
+               _(u"quart (UK)"):
+                       [(converters.m, 2*2.5*8*2*3*4.73551071041125), '', _(u"The fourth part of a gallon; the eighth part of a peck; two pints. Note: In imperial measure, a quart is forty English fluid ounces; in wine measure, it is thirty-two American fluid ounces. The United States dry quart contains 67.20 cubic inches, the fluid quart 57.75. The English quart contains 69.32 cubic inches.")],
+               _(u"pint (UK)"):
+                       [(converters.m, 2.5*8*2*3*4.73551071041125), '', ''],
+               _(u"cup (UK)"):
+                       [(converters.m, 8*2*3*4.73551071041125), '', ''],
+               _(u"ounce - fluid ounce (UK)"):
+                       [(converters.m, 2*3*4.73551071041125), '', _(u"Contains 1 ounce mass of distilled water at 62\xb0F, and barometer at 30 inches")],
+               _(u"tablespoon (UK)"):
+                       [(converters.m, 3*4.73551071041125), '', _(u"One sixteenth of a cup. A spoon of the largest size commonly used at the table; -- distinguished from teaspoon, dessert spoon, etc.")],
+               _(u"teaspoon (UK)"):
+                       [(converters.m, 4.73551071041125), '', _(u"One third of a tablespoon. A small spoon used in stirring and sipping tea, coffee, etc., and for other purposes.")],
+               _(u"dash (US)"):
+                       [(converters.m, 4.92892159375/16), '', _(u"one half of a pinch")],
+               _(u"pinch (US)"):
+                       [(converters.m, 4.92892159375/8), '', _(u"One eigth of a teaspoon")],
+               _(u"keg (beer US)"):
+                       [(converters.m, 15.5*768*4.92892159375), "", """US standard size beer keg = 1/2 barrel = 15.5 US gallons; weighs approx. 29.7 pounds empty, 160.5 pounds full."""],
+               _(u"keg (wine US)"):
+                       [(converters.m, 12*768*4.92892159375), "", """12 US gallons."""],
+               _(u"ponykeg (US)"):
+                       [(converters.m, 7.75*768*4.92892159375), "", """1/2 US beerkeg, 7.75 US gallons."""],
+               _(u"barrel (beer US)"):
+                       [(converters.m, 31*768*4.92892159375), "", """Two US beerkegs, 31 US gallons."""],
+               _(u"barrel (wine US)"):
+                       [(converters.m, 31.5*768*4.92892159375), "", """31.5 US gallons."""],
+               _(u"hogshead (US)"):
+                       [(converters.m, 63*768*4.92892159375), "", """Equal to 2 barrels or 63 US gallons."""],
+               _(u"fifth (US)"):
+                       [(converters.m, .2*768*4.92892159375), "", """One fifth of a gallon."""],
+               _(u"jigger"):
+                       [(converters.m, 9*4.92892159375), "", """1.5 fluid ounces (US)"""],
+               _(u"shot"):
+                       [(converters.m, 9*4.92892159375), "", """1.5 fluid ounces (US)"""],
+               _(u"winebottle"):
+                       [(converters.m, 750*1.0), "", """750 milliliters"""],
+               _(u"wineglass (US)"):
+                       [(converters.m, 24*4.92892159375), "", """Equal to 4 fluid ounces (US)."""],
+               _(u"winesplit"):
+                       [(converters.m, 750.0/4), "", """1/4 winebottle"""],
+               _(u"magnum"):
+                       [(converters.m, 1500), "", """1.5 liters"""],
+               _(u"gallon (US)"):
+                       [(converters.m, 4*2*2*8*2*3*4.92892159375), '', _(u"A measure of capacity, containing four quarts; -- used, for the most part, in liquid measure, but sometimes in dry measure. Note: The standard gallon of the Unites States contains 231 cubic inches, or 8.3389 pounds avoirdupois of distilled water at its maximum density, and with the barometer at 30 inches. This is almost exactly equivalent to a cylinder of seven inches in diameter and six inches in height, and is the same as the old English wine gallon. The beer gallon, now little used in the United States, contains 282 cubic inches.")],
+               _(u"quart (US)"):
+                       [(converters.m, 2*2*8*2*3*4.92892159375), '', _(u"The fourth part of a gallon; the eighth part of a peck; two pints. Note: In imperial measure, a quart is forty English fluid ounces; in wine measure, it is thirty-two American fluid ounces. The United States dry quart contains 67.20 cubic inches, the fluid quart 57.75. The English quart contains 69.32 cubic inches.")],
+               _(u"pint (US)"):
+                       [(converters.m, 2*8*2*3*4.92892159375), '', ''],
+               _(u"cup (US)"):
+                       [(converters.m, 8*2*3*4.92892159375), '', ''],
+               _(u"ounce - fluid ounce (US)"):
+                       [(converters.m, 2*3*4.92892159375), '', ''],
+               _(u"beerbottle (US 12 ounce)"):
+                       [(converters.m, 12*2*3*4.92892159375), '', ''],
+               _(u"tablespoon (US)"):
+                       [(converters.m, 3*4.92892159375), '', _(u"One sixteenth of a cup. A spoon of the largest size commonly used at the table; -- distinguished from teaspoon, dessert spoon, etc.")],
+               _(u"teaspoon (US)"):
+                       [(converters.m, 4.92892159375), '', _(u"One third of a tablespoon. A small spoon used in stirring and sipping tea, coffee, etc., and for other purposes.")],
+               _(u"shaku"):
+                       [(converters.m, 18.04), '', _(u"A Japanese unit of volume, the shaku equals about 18.04 milliliters (0.61 U.S. fluid ounce). Note: shaku also means area and length.")],
+               _(u"cubic yard"):
+                       [(converters.m, 27*1728*16.387064), u"yd\xb3", ''],
+               _(u"acre foot"):
+                       [(converters.m, 1728*16.387064*43560), '', ''],
+               _(u"cubic foot"):
+                       [(converters.m, 1728*16.387064), u"ft\xb3", ''],
+               _(u"cubic inch"):
+                       [(converters.m, 16.387064), u"in\xb3", ''],
+               _(u"cubic meter"):
+                       [(converters.m, 1.0e6), u"m\xb3", ''],
+               _(u"cubic decimeter"):
+                       [(converters.m, 1.0e3), u"dm\xb3", ''],
+               _(u"litre"):
+                       [(converters.m, 1.0e3), "l", _(u"A measure of capacity in the metric system, being a cubic decimeter.")],
+               _(u"cubic centimeter"):
+                       [(converters.m, 1.0), u"cm\xb3", ''],
+               _(u"millilitre"):
+                       [(converters.m, 1.0), "ml", ''],
+               _(u"centilitre"):
+                       [(converters.m, 10*1.0), "cl", ''],
+               _(u"decilitre"):
+                       [(converters.m, 10*10*1.0), "dl", ''],
+               _(u"mil"):
+                       [(converters.m, 1.0), '', _(u"Equal to one thousandth of a liter syn: milliliter, millilitre, ml, cubic centimeter, cubic centimeter, cc")],
+               _(u"fluid dram"): 
+                       [(converters.m, 2*3*4.92892159375/8), '', _(u"Used in Pharmaceutical")],
+               _(u"minim"):
+                       [(converters.m, 2*3*4.92892159375/480), '', _(u"Used in Pharmaceutical to represent one drop. 1/60 fluid dram or 1/480 fluid ounce. A U.S. minim is about 0.003760 in\xb3 or 61.610 \xb5l. The British minim is about 0.003612 in\xb3 or 59.194 \xb5l. Origin of the word is from the Latin minimus, or small.")],
+       },
+
+       _(u"Volume and Dry Capacity"): {
+               ".base_unit": "cubic meter",
+               _(u"cubic meter"):
+               [(converters.m, 1.0), u"m\xb3", ''],
+               _(u"cubic decimeter"):
+               [(converters.m, 1.0e-3), u"dm\xb3", ''],
+               _(u"cubic millimeter"):
+               [(converters.m, 1.0e-9), u"mm\xb3", """"""],
+               _(u"cubic centimeter"):
+               [(converters.m, 1.0e-6), u"cm\xb3", """"""],
+               _(u"cord"):
+               [(converters.m, 3.624556363776), '', _(u"A pile of wood 8ft x 4ft x 4ft.")],
+               _(u"cubic yard"):
+               [(converters.m, 0.764554857984), u"yd\xb3", ''],
+               _(u"bushel (US)"):
+               [(converters.m, 4*2*0.00440488377086), "bu", _(u"A dry measure, containing four pecks, eight gallons, or thirty-two quarts. Note: The Winchester bushel, formerly used in England, contained 2150.42 cubic inches, being the volume of a cylinder 181/2 inches in internal diameter and eight inches in depth. The standard bushel measures, prepared by the United States Government and distributed to the States, hold each 77.6274 pounds of distilled water, at 39.8deg Fahr. and 30 inches atmospheric pressure, being the equivalent of the Winchester bushel. The imperial bushel now in use in England is larger than the Winchester bushel, containing 2218.2 cubic inches, or 80 pounds of water at 62deg Fahr.")],
+               _(u"bushel (UK | CAN)"):
+               [(converters.m, 4*2*4*1.1365225e-3), "bu", _(u"A dry measure, containing four pecks, eight gallons, or thirty-two quarts. Note: The Winchester bushel, formerly used in England, contained 2150.42 cubic inches, being the volume of a cylinder 181/2 inches in internal diameter and eight inches in depth. The standard bushel measures, prepared by the United States Government and distributed to the States, hold each 77.6274 pounds of distilled water, at 39.8deg Fahr. and 30 inches atmospheric pressure, being the equivalent of the Winchester bushel. The imperial bushel now in use in England is larger than the Winchester bushel, containing 2218.2 cubic inches, or 80 pounds of water at 62deg Fahr.")],
+               _(u"peck (US)"):
+               [(converters.m, 2*0.00440488377086), '', ''],
+               _(u"peck (UK | CAN)"):
+               [(converters.m, 2*4*1.1365225e-3), '', ''],
+               _(u"gallon (US dry)"):
+               [(converters.m, 0.00440488377086), "gal", ''],
+               _(u"gallon (CAN)"):
+               [(converters.m, 4*1.1365225e-3), "gal", ''],
+               _(u"quart (US dry)"):
+               [(converters.m, 1.101220942715e-3), "qt", ''],
+               _(u"quart (CAN)"):
+               [(converters.m, 1.1365225e-3), "qt", ''],
+               _(u"cubic foot"):
+               [(converters.m, 0.028316846592), u"ft\xb3", ''],
+               _(u"board foot"):
+               [(converters.m, 0.028316846592/12), '', _(u"lumber 1ft\xb2 and 1 in thick")],
+               _(u"litre"):
+               [(converters.m, 1.0e-3), "l", _(u"A measure of capacity in the metric system, being a cubic decimeter.")],
+               _(u"pint (US dry)"):
+               [(converters.m, 5.506104713575E-04), "pt", ''],
+               _(u"cubic inch"):
+               [(converters.m, 0.000016387064), u"in\xb3", ''],
+               _(u"coomb"):
+               [(converters.m, 4*4*2*4*1.1365225e-3), '', _(u"British. 4 bushels")],
+               _(u"peck"):
+               [(converters.m, 37.23670995671), '', _(u"The fourth part of a bushel; a dry measure of eight quarts")],
+               _(u"quart (dry)"):
+               [(converters.m, 4.65458874458874), '', _(u"The fourth part of a gallon; the eighth part of a peck; two pints. Note: In imperial measure, a quart is forty English fluid ounces; in wine measure, it is thirty-two American fluid ounces. The United States dry quart contains 67.20 cubic inches, the fluid quart 57.75. The English quart contains 69.32 cubic inches.")],
+       },
+
+       _(u"Thermal conductance (Area)"): {
+               ".base_unit": "Watts per square meter Kelvin",
+               _(u"watts per square meter Kelvin"):
+               [(converters.m, 1.0), u"W/m\xb2\xb7K", ''],
+               _(u"watts per square meter deg C"):
+               [(converters.m, 1.0), u"W/h.m\xb2\xb7\xb0C", ''],
+               _(u"kilocalories per hour square meter deg C"):
+               [(converters.m, 1.163), u"kcal/h\xb7m\xb7\xb0C", ''],
+               _(u"british thermal units per second square foot deg F"):
+               [(converters.m, 2.0428), u"Btu/sec\xb7ft\xb2\xb7\xb0F", ''],
+               _(u"british thermal units per hour square foot deg F"):
+               [(converters.m, 5.6782), u"Btu/h\xb7ft\xb2\xb7\xb0F", ''],
+       },
+
+       _(u"Thermal conductance (Linear)"): {
+               ".base_unit": "cubic meter",
+               _(u"watts per meter Kelvin"):
+               [(converters.m, 1.0), u"W/m\xb7K", ''],
+               _(u"watts per meter deg C"):
+               [(converters.m, 1.0), u"W/m\xb7\xb0C", ''],
+               _(u"kilocalories per hour meter deg C"):
+               [(converters.m, 1.163), u"kcal/h\xb7m\xb7\xb0C", ''],
+               _(u"british thermal units per second foot deg F"):
+               [(converters.m, 1.703), u"Btu/sec\xb7ft\xb7\xb0F", ''],
+       },
+
+       _(u"Thermal resistance"): {
+               ".base_unit": _(u"square meter kelvin per watt"),
+               _(u"square meter kelvin per watt"):
+               [(converters.m, 1.0), u"m\xb2\xb7K/W", ''],
+               _(u"square meter deg C per watt"):
+               [(converters.m, 1.0), u"m\xb2\xb7\xb0C/W", ''],
+               _(u"clo"):
+               [(converters.m, 1.50e-1), u"clo", _(u"Clo is the unit for effective clothing insulation. It is used to evaluate the expected comfort of users in certain humidity, temperature and workload conditions (and estimate air conditioning or heating loads, for instance.).")],
+               _(u"hour square foot deg F per BTU"):
+               [(converters.m, 1.761e-1), u"h\xb7ft\xb2\xb7\xb0F/Btu", ''],
+               _(u"hour square meter deg C per kilocalorie"):
+               [(converters.m, 8.62e-1), u"h\xb7m\xb2\xb7\xb0C/kcal", ''],
+       },
+
+       _(u"Specific Heat"): {
+               ".base_unit": "joule per kilogram kelvin",
+               _(u"joule per kilogram kelvin"):
+               [(converters.m, 1.0), u"J/kg\xb7K", ''],
+               _(u"joule per kilogram deg C"):
+               [(converters.m, 1.0), u"J/kg\xb7\xb0C", ''],
+               _(u"kilocalories per kilogram deg C"):
+               [(converters.m, 4.18855e3), u"kcal/kg\xb7\xb0C", ''],
+               _(u"btu per pound deg F"):
+               [(converters.m, 4.1868e3), u"BTU/lb\xb7\xb0F", ''],
+       },
+
+       _(u"Fuel consumption"): {
+               ".base_unit": "miles per gallon (US)",
+               _(u"miles per gallon (US)"):
+               [(converters.m, 1.0), u"mpg (US)", ''],
+               _(u"gallons  (US) per 100 miles"):
+               [(converters.inv, 100.0), u'', ''],
+               _(u"miles per gallon (Imperial)"):
+               [(converters.m, 1.0/1.20095), u"mpg (Imperial)", ''],
+               _(u"gallons (Imperial) per 100 miles"):
+               [(converters.inv, 100.0/1.20095), u'', ''],
+               _(u"liters per 100 kilometer"):
+               [(converters.inv, 62.1371192237/0.264172052358), u'', ''],
+               _(u"kilometers per liter"):
+               [(converters.m, .621371192237/0.264172052358), u'', ''],
+       },
+
+       _(u"Torque"): {
+               ".base_unit": "newton meter",
+               _(u"newton meter"):
+               [(converters.m, 1.0), u"N\xb7m", _(u"The SI unit of force that causes rotation.")],
+               _(u"joules"):
+               [(converters.m, 1.0), u"j", ''],
+               _(u"kilo newton meter"):
+               [(converters.m, 1000.0), u"kN\xb7m", ''],
+               _(u"mega newton meter"):
+               [(converters.m, 1000000.0), u"MN\xb7m", ''],
+               _(u"milli newton meter"):
+               [(converters.m, 1.0e-3), u"mN\xb7m", ''],
+               _(u"micro newton meter"):
+               [(converters.m, 1.0e-6), u"\xb5N\xb7m", ''],
+               _(u"dyne centimeter"):
+               [(converters.m, 1.0e-7), u"dyne\xb7cm", ''],
+               _(u"kilogram meter"):
+               [(converters.m, 9.80665), u"kg\xb7m", ''],
+               _(u"centimeter gram"):
+               [(converters.m, 9.80665/100000.0), u"cm\xb7g", ''],
+               _(u"kip"):
+               [(converters.m, 113.0), u"kip", _(u"One thousand inch pounds.")],
+               _(u"foot pounds"):
+               [(converters.m, 1356.0/1000.0), u"lbf\xb7ft", ''],
+               _(u"foot ounce"):
+               [(converters.m, 1356.0/1000.0/16.0), u"oz\xb7ft", ''],
+               _(u"meter kilopond"):
+               [(converters.m, 9.80665), u"mkp", ''],
+               _(u"newton centimeter"):
+               [(converters.m, .01), u"N\xb7cm", ''],
+               _(u"inch ounces"):
+               [(converters.m, 113.0/16.0*.001), u"in\xb7oz", ''],
+               _(u"inch pounds"):
+               [(converters.m, 113.0*.001), u"in\xb7lb", ''],
+               _(u"foot poundal"):
+               [(converters.m, 1.0/23.7303605), u'', ''],
+       },
+
+       _(u"Current Loop"): {".base_unit": "6400 to 32000",
+                       _(u"6400 to 32000"):
+                       [(converters.m, 1.0), u"counts", _(u"Many PLCs must scale the 4 to 20mA signal to an integer, this is commonly a value from 6400 to 32000, ")],
+                       _(u"4 to 20mA"):
+                       [(converters.m, 1600.0), u"mA", _(u"This range of current is commonly used in instrumentation. 0mA is an indication of a broken transmitter loop.")],
+                       _(u"V across 250 ohm"):
+                       [(converters.m, 6400.0), u"V", _(u"A common resistance for current loop instrumentation is 250 ohms. A voltage will be developed across this resistor, that voltage can be used to test the current loop.")],
+                       _(u"percent"):
+                       [(converters.gof, (((32000.0-6400.0)/100.0), 6400.0)), u"%", _(u"This is a percentage of the 4 to 20mA signal.")],
+                       },
+
+       _(u"Currency (UK)"): {
+               ".base_unit": "pound",
+               _(u"pound | quid | soverign"):
+               [(converters.m, 1.0), "", """The base monetary unit in UK."""],
+               _(u"soverign"):
+               [(converters.m, 1.0), "", """One pound."""],
+               _(u"quid"):
+               [(converters.m, 1.0), "", """One pound."""],
+               _(u"fiver"):
+               [(converters.m, 5*1.0), "", """Equal to five pounds."""],
+               _(u"tenner"):
+               [(converters.m, 10*1.0), "", """Equal to Ten pounds."""],
+               _(u"crown"):
+               [(converters.m, 5*1.0/20), "", """Equal to five shillings."""],
+               _(u"shilling"):
+               [(converters.m, 1.0/20), "", """Equal to one twentieth of a pound."""],
+               _(u"bob | shilling"):
+               [(converters.m, 1.0/20), "", """Equal to one twentieth of a pound."""],
+               _(u"penny | pence"):
+               [(converters.m, 1.0/100), "", """Equal to 1/100 of a pound."""],
+               _(u"penny | pence (old)"):
+               [(converters.m, 1.0/240), "", """Equal to 1/240 of a pound.  February 15, 1971 the English coinage system was changed to a decimal system and the old penny ceased to be legal tender August 31, 1971."""],
+               _(u"tuppence(old)"):
+               [(converters.m, 2*1.0/240), "", """Equal to two old pennies. February 15, 1971 the English coinage system was changed to a decimal system."""],
+               _(u"threepence (old)"):
+               [(converters.m, 3*1.0/240), "", """Equal to three old pence.  The threepence was demonitized August 31, 1971."""],
+               _(u"halfpenny | hapenny (old)"):
+               [(converters.m, 1.0/240/2), "", """The old halfpenny was demonetized on August 31, 1969."""],
+               _(u"guinea"):
+               [(converters.m, 21*1.0/20), "", """While the term is still used, the coins are no longer in use.  Webster's Revised Unabridged Dictionary (1913) - A gold coin of England current for twenty-one shillings sterling, ... but not coined since the issue of sovereigns in 1817."""],
+               _(u"farthing"):
+               [(converters.m, 1.0/240/4), "", """"""],
+               _(u"florin (or two bob bit)"):
+               [(converters.m, 2*1.0/20), "", """"""],
+               _(u"half crown (old)"):
+               [(converters.m, (2*1.0/20)+(6*1.0/240)), "", """ The half-crown was demonetized on 1st January 1970."""],
+               _(u"sixpence"):
+               [(converters.m, 6*1.0/240), "", """Equal to six old pence.  February 15, 1971 the English coinage system was changed to a decimal system ."""],
+               _(u"tanner | sixpence"):
+               [(converters.m, 6*1.0/240), "", """Equal to six old pence.  February 15, 1971 the English coinage system was changed to a decimal system ."""],
+               _(u"mark (old British mark)"):
+               [(converters.m, 160*1.0/240), "", """"""],
+               _(u"groat"):
+               [(converters.m, 4*1.0/240), "", """Equal to four old pence"""],
+               _(u"pony"):
+               [(converters.m, 25.0), "", """Equal to twenty five pounds sterling"""],
+       },
+
+       _(u"Radioactivity"): {
+               ".base_unit": "becquerel",
+               _(u"becquerel"):
+               [(converters.m, 1.0), u"Bq", 'The SI derived unit of radioactivity.'],
+               _(u"curie"):
+               [(converters.m, 3.7e10), u"Ci", 'Roughly the activity of 1 gram of the radium isotope 226 Ra.'],
+               _(u"pico curie"):
+               [(converters.m, 3.7e-2), u"pCi", ''],
+       },
+
+       _(u"Radiation dose"): {
+               ".base_unit": "Sievert",
+               _(u"Sievert"):
+               [(converters.m, 1.0), u"Sv", ''],
+               _(u"milli Sievert"):
+               [(converters.m, 1.0e-3), u"mSv", ''],
+               _(u"micro Sievert"):
+               [(converters.m, 1.0e-6), u"\xb5Sv", ''],
+               _(u"rem"):
+               [(converters.m, 0.01), u"rem", ''],
+               _(u"milli rem"):
+               [(converters.m, 1.0e-5), u"mrem", ''],
+               _(u"roentgen"):
+               [(converters.m, 1/1.07185e2), u"R", ''],
+       },
+}
+
+
+UNIT_CATEGORIES = sorted(UNIT_DESCRIPTIONS.iterkeys())
+
+
+def get_units(categoryName):
+       units = sorted(UNIT_DESCRIPTIONS[categoryName].iterkeys())
+
+       # do not display .base_unit description key
+       del units[0]
+
+       return units
+
+
+def get_units_from_category(category):
+       units = sorted(category.iterkeys())
+
+       # do not display .base_unit description key
+       del units[0]
+
+       return units
+
+
+def get_base_unit(categoryName):
+       return UNIT_DESCRIPTIONS[categoryName][".base_unit"]
+
+
+def get_base_unit_from_category(category):
+       return category[".base_unit"]
+
+
+future_dic = {
+       _(u"Wire Gauge"): {
+               ".base_unit": "circular mils",
+               _(u"circular mil"):
+               [(converters.m, 1.0), u"CM", ''],
+               _(u"square mil"):
+               [(converters.m, 1.0), u'', ''],
+               _(u"square milimetres"):
+               [(converters.m, 1.0), u"mm\xb2", ''],
+               _(u"AWG"):
+               [(converters.m, 1.0), "AWG", _(u"American Wire Gauge")],
+               _(u"Diameter mils"):
+               [(converters.m, 1.0), "mil", ''],
+               _(u"Diameter inches"):
+               [(converters.m, 1.0), "in", ''],
+               _(u"Diameter mm"):
+               [(converters.m, 1.0), "mm", ''],
+               _(u"Diameter m"):
+               [(converters.m, 1.0), "m", ''],
+               _(u"Ampacity Cu"):
+               [(converters.m, 1.0), "A", _(u"Copper wire ampacity")],
+               _(u"Ampacity Al"):
+               [(converters.m, 1.0), "A", _(u"Aluminum wire ampacity")],
+               _(u"Resistance of Cu wire at xxdegC"):
+               [(converters.m, 1.0), "ohms/kft", _(u"Copper wire resistance.")],
+               _(u"Resistance of Cu wire at xxdegC"):
+               [(converters.m, 1.0), "ohms/ft", _(u"Copper wire resistance.")],
+               _(u"Resistance of Cu wire at xxdegC"):
+               [(converters.m, 1.0), "ohms/m", _(u"Copper wire resistance.")],
+               _(u"Resistance of Cu wire at xxdegC"):
+               [(converters.m, 1.0), "ohms/km", _(u"Copper wire resistance.")],
+               _(u"Resistance of Al wire at xxdegC"):
+               [(converters.m, 1.0), "ohms/kft", _(u"Copper wire resistance.")],
+               _(u"Resistance of Al wire at xxdegC"):
+               [(converters.m, 1.0), "ohms/ft", _(u"Copper wire resistance.")],
+               _(u"Resistance of Al wire at xxdegC"):
+               [(converters.m, 1.0), "ohms/m", _(u"Copper wire resistance.")],
+               _(u"Resistance of Al wire at xxdegC"):
+               [(converters.m, 1.0), "ohms/km", _(u"Copper wire resistance.")],
+               _(u"Length per Weight Cu Wire"):
+               [(converters.m, 1.0), "ft/lb (Cu)", _(u"Length per weight Copper Wire.")],
+               _(u"Length per Weight Al Wire"):
+               [(converters.m, 1.0), "ft/lb (Al)", _(u"Length per weight Aluminum Wire.")],
+               _(u"Length per resistance Cu Wire"):
+               [(converters.m, 1.0), "ft/ohm (Cu)", _(u"Length per resistance Copper Wire.")],
+               _(u"Length per resistance Al Wire"):
+               [(converters.m, 1.0), "ft/ohm (Al)", _(u"Length per resistance Aluminum Wire.")],
+               _(u"Weight Cu wire"):
+               [(converters.m, 1.0), "kg/km (Cu)", _(u"Copper wire weight.")],
+               _(u"Weight Al wire"):
+               [(converters.m, 1.0), "kg/km (Al)", _(u"Aluminum wire weight.")],
+               _(u"Weight Cu wire"):
+               [(converters.m, 1.0), "lb/kft (Cu)", _(u"Copper wire weight in pounds per 1000 feet.")],
+               _(u"Weight Al wire"):
+               [(converters.m, 1.0), "lb/kft (Al)", _(u"Aluminum wire weight in pounds per 1000 feet.")],
+               _(u"Tensile strength"):
+               [(converters.m, 1.0), "kgf", _(u"Aluminum wire weight.")],
+               _(u"Turns per inch"):
+               [(converters.m, 1.0), "TPI", _(u"Turns per inch of bare wire, useful for winding coils. This value is approximate and will be reduced with insulated wire")],
+       },
+}
diff --git a/gonvert/util/__init__.py b/gonvert/util/__init__.py
new file mode 100644 (file)
index 0000000..4265cc3
--- /dev/null
@@ -0,0 +1 @@
+#!/usr/bin/env python
diff --git a/gonvert/util/algorithms.py b/gonvert/util/algorithms.py
new file mode 100644 (file)
index 0000000..e94fb61
--- /dev/null
@@ -0,0 +1,664 @@
+#!/usr/bin/env python
+
+"""
+@note Source http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66448
+"""
+
+import itertools
+import functools
+import datetime
+import types
+import array
+import random
+
+
+def ordered_itr(collection):
+       """
+       >>> [v for v in ordered_itr({"a": 1, "b": 2})]
+       [('a', 1), ('b', 2)]
+       >>> [v for v in ordered_itr([3, 1, 10, -20])]
+       [-20, 1, 3, 10]
+       """
+       if isinstance(collection, types.DictType):
+               keys = list(collection.iterkeys())
+               keys.sort()
+               for key in keys:
+                       yield key, collection[key]
+       else:
+               values = list(collection)
+               values.sort()
+               for value in values:
+                       yield value
+
+
+def itercat(*iterators):
+       """
+       Concatenate several iterators into one.
+
+       >>> [v for v in itercat([1, 2, 3], [4, 1, 3])]
+       [1, 2, 3, 4, 1, 3]
+       """
+       for i in iterators:
+               for x in i:
+                       yield x
+
+
+def product(*args, **kwds):
+       # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
+       # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
+       pools = map(tuple, args) * kwds.get('repeat', 1)
+       result = [[]]
+       for pool in pools:
+               result = [x+[y] for x in result for y in pool]
+       for prod in result:
+               yield tuple(prod)
+
+
+def iterwhile(func, iterator):
+       """
+       Iterate for as long as func(value) returns true.
+       >>> through = lambda b: b
+       >>> [v for v in iterwhile(through, [True, True, False])]
+       [True, True]
+       """
+       iterator = iter(iterator)
+       while 1:
+               next = iterator.next()
+               if not func(next):
+                       raise StopIteration
+               yield next
+
+
+def iterfirst(iterator, count=1):
+       """
+       Iterate through 'count' first values.
+
+       >>> [v for v in iterfirst([1, 2, 3, 4, 5], 3)]
+       [1, 2, 3]
+       """
+       iterator = iter(iterator)
+       for i in xrange(count):
+               yield iterator.next()
+
+
+def iterstep(iterator, n):
+       """
+       Iterate every nth value.
+
+       >>> [v for v in iterstep([1, 2, 3, 4, 5], 1)]
+       [1, 2, 3, 4, 5]
+       >>> [v for v in iterstep([1, 2, 3, 4, 5], 2)]
+       [1, 3, 5]
+       >>> [v for v in iterstep([1, 2, 3, 4, 5], 3)]
+       [1, 4]
+       """
+       iterator = iter(iterator)
+       while True:
+               yield iterator.next()
+               # skip n-1 values
+               for dummy in xrange(n-1):
+                       iterator.next()
+
+
+def itergroup(iterator, count, padValue = None):
+       """
+       Iterate in groups of 'count' values. If there
+       aren't enough values, the last result is padded with
+       None.
+
+       >>> for val in itergroup([1, 2, 3, 4, 5, 6], 3):
+       ...     print tuple(val)
+       (1, 2, 3)
+       (4, 5, 6)
+       >>> for val in itergroup([1, 2, 3, 4, 5, 6], 3):
+       ...     print list(val)
+       [1, 2, 3]
+       [4, 5, 6]
+       >>> for val in itergroup([1, 2, 3, 4, 5, 6, 7], 3):
+       ...     print tuple(val)
+       (1, 2, 3)
+       (4, 5, 6)
+       (7, None, None)
+       >>> for val in itergroup("123456", 3):
+       ...     print tuple(val)
+       ('1', '2', '3')
+       ('4', '5', '6')
+       >>> for val in itergroup("123456", 3):
+       ...     print repr("".join(val))
+       '123'
+       '456'
+       """
+       paddedIterator = itertools.chain(iterator, itertools.repeat(padValue, count-1))
+       nIterators = (paddedIterator, ) * count
+       return itertools.izip(*nIterators)
+
+
+def xzip(*iterators):
+       """Iterative version of builtin 'zip'."""
+       iterators = itertools.imap(iter, iterators)
+       while 1:
+               yield tuple([x.next() for x in iterators])
+
+
+def xmap(func, *iterators):
+       """Iterative version of builtin 'map'."""
+       iterators = itertools.imap(iter, iterators)
+       values_left = [1]
+
+       def values():
+               # Emulate map behaviour, i.e. shorter
+               # sequences are padded with None when
+               # they run out of values.
+               values_left[0] = 0
+               for i in range(len(iterators)):
+                       iterator = iterators[i]
+                       if iterator is None:
+                               yield None
+                       else:
+                               try:
+                                       yield iterator.next()
+                                       values_left[0] = 1
+                               except StopIteration:
+                                       iterators[i] = None
+                                       yield None
+       while 1:
+               args = tuple(values())
+               if not values_left[0]:
+                       raise StopIteration
+               yield func(*args)
+
+
+def xfilter(func, iterator):
+       """Iterative version of builtin 'filter'."""
+       iterator = iter(iterator)
+       while 1:
+               next = iterator.next()
+               if func(next):
+                       yield next
+
+
+def xreduce(func, iterator, default=None):
+       """Iterative version of builtin 'reduce'."""
+       iterator = iter(iterator)
+       try:
+               prev = iterator.next()
+       except StopIteration:
+               return default
+       single = 1
+       for next in iterator:
+               single = 0
+               prev = func(prev, next)
+       if single:
+               return func(prev, default)
+       return prev
+
+
+def daterange(begin, end, delta = datetime.timedelta(1)):
+       """
+       Form a range of dates and iterate over them.
+
+       Arguments:
+       begin -- a date (or datetime) object; the beginning of the range.
+       end   -- a date (or datetime) object; the end of the range.
+       delta -- (optional) a datetime.timedelta object; how much to step each iteration.
+                       Default step is 1 day.
+
+       Usage:
+       """
+       if not isinstance(delta, datetime.timedelta):
+               delta = datetime.timedelta(delta)
+
+       ZERO = datetime.timedelta(0)
+
+       if begin < end:
+               if delta <= ZERO:
+                       raise StopIteration
+               test = end.__gt__
+       else:
+               if delta >= ZERO:
+                       raise StopIteration
+               test = end.__lt__
+
+       while test(begin):
+               yield begin
+               begin += delta
+
+
+class LazyList(object):
+       """
+       A Sequence whose values are computed lazily by an iterator.
+
+       Module for the creation and use of iterator-based lazy lists.
+       this module defines a class LazyList which can be used to represent sequences
+       of values generated lazily. One can also create recursively defined lazy lists
+       that generate their values based on ones previously generated.
+
+       Backport to python 2.5 by Michael Pust
+       """
+
+       __author__ = 'Dan Spitz'
+
+       def __init__(self, iterable):
+               self._exhausted = False
+               self._iterator = iter(iterable)
+               self._data = []
+
+       def __len__(self):
+               """Get the length of a LazyList's computed data."""
+               return len(self._data)
+
+       def __getitem__(self, i):
+               """Get an item from a LazyList.
+               i should be a positive integer or a slice object."""
+               if isinstance(i, int):
+                       #index has not yet been yielded by iterator (or iterator exhausted
+                       #before reaching that index)
+                       if i >= len(self):
+                               self.exhaust(i)
+                       elif i < 0:
+                               raise ValueError('cannot index LazyList with negative number')
+                       return self._data[i]
+
+               #LazyList slices are iterators over a portion of the list.
+               elif isinstance(i, slice):
+                       start, stop, step = i.start, i.stop, i.step
+                       if any(x is not None and x < 0 for x in (start, stop, step)):
+                               raise ValueError('cannot index or step through a LazyList with'
+                                                               'a negative number')
+                       #set start and step to their integer defaults if they are None.
+                       if start is None:
+                               start = 0
+                       if step is None:
+                               step = 1
+
+                       def LazyListIterator():
+                               count = start
+                               predicate = (
+                                       (lambda: True)
+                                       if stop is None
+                                       else (lambda: count < stop)
+                               )
+                               while predicate():
+                                       try:
+                                               yield self[count]
+                                       #slices can go out of actual index range without raising an
+                                       #error
+                                       except IndexError:
+                                               break
+                                       count += step
+                       return LazyListIterator()
+
+               raise TypeError('i must be an integer or slice')
+
+       def __iter__(self):
+               """return an iterator over each value in the sequence,
+               whether it has been computed yet or not."""
+               return self[:]
+
+       def computed(self):
+               """Return an iterator over the values in a LazyList that have
+               already been computed."""
+               return self[:len(self)]
+
+       def exhaust(self, index = None):
+               """Exhaust the iterator generating this LazyList's values.
+               if index is None, this will exhaust the iterator completely.
+               Otherwise, it will iterate over the iterator until either the list
+               has a value for index or the iterator is exhausted.
+               """
+               if self._exhausted:
+                       return
+               if index is None:
+                       ind_range = itertools.count(len(self))
+               else:
+                       ind_range = range(len(self), index + 1)
+
+               for ind in ind_range:
+                       try:
+                               self._data.append(self._iterator.next())
+                       except StopIteration: #iterator is fully exhausted
+                               self._exhausted = True
+                               break
+
+
+class RecursiveLazyList(LazyList):
+
+       def __init__(self, prod, *args, **kwds):
+               super(RecursiveLazyList, self).__init__(prod(self, *args, **kwds))
+
+
+class RecursiveLazyListFactory:
+
+       def __init__(self, producer):
+               self._gen = producer
+
+       def __call__(self, *a, **kw):
+               return RecursiveLazyList(self._gen, *a, **kw)
+
+
+def lazylist(gen):
+       """
+       Decorator for creating a RecursiveLazyList subclass.
+       This should decorate a generator function taking the LazyList object as its
+       first argument which yields the contents of the list in order.
+
+       >>> #fibonnacci sequence in a lazy list.
+       >>> @lazylist
+       ... def fibgen(lst):
+       ...     yield 0
+       ...     yield 1
+       ...     for a, b in itertools.izip(lst, lst[1:]):
+       ...             yield a + b
+       ...
+       >>> #now fibs can be indexed or iterated over as if it were an infinitely long list containing the fibonnaci sequence
+       >>> fibs = fibgen()
+       >>>
+       >>> #prime numbers in a lazy list.
+       >>> @lazylist
+       ... def primegen(lst):
+       ...     yield 2
+       ...     for candidate in itertools.count(3): #start at next number after 2
+       ...             #if candidate is not divisible by any smaller prime numbers,
+       ...             #it is a prime.
+       ...             if all(candidate % p for p in lst.computed()):
+       ...                     yield candidate
+       ...
+       >>> #same for primes- treat it like an infinitely long list containing all prime numbers.
+       >>> primes = primegen()
+       >>> print fibs[0], fibs[1], fibs[2], primes[0], primes[1], primes[2]
+       0 1 1 2 3 5
+       >>> print list(fibs[:10]), list(primes[:10])
+       [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
+       """
+       return RecursiveLazyListFactory(gen)
+
+
+def map_func(f):
+       """
+       >>> import misc
+       >>> misc.validate_decorator(map_func)
+       """
+
+       @functools.wraps(f)
+       def wrapper(*args):
+               result = itertools.imap(f, args)
+               return result
+       return wrapper
+
+
+def reduce_func(function):
+       """
+       >>> import misc
+       >>> misc.validate_decorator(reduce_func(lambda x: x))
+       """
+
+       def decorator(f):
+
+               @functools.wraps(f)
+               def wrapper(*args):
+                       result = reduce(function, f(args))
+                       return result
+               return wrapper
+       return decorator
+
+
+def any_(iterable):
+       """
+       @note Python Version <2.5
+
+       >>> any_([True, True])
+       True
+       >>> any_([True, False])
+       True
+       >>> any_([False, False])
+       False
+       """
+
+       for element in iterable:
+               if element:
+                       return True
+       return False
+
+
+def all_(iterable):
+       """
+       @note Python Version <2.5
+
+       >>> all_([True, True])
+       True
+       >>> all_([True, False])
+       False
+       >>> all_([False, False])
+       False
+       """
+
+       for element in iterable:
+               if not element:
+                       return False
+       return True
+
+
+def for_every(pred, seq):
+       """
+       for_every takes a one argument predicate function and a sequence.
+       @param pred The predicate function should return true or false.
+       @returns true if every element in seq returns true for predicate, else returns false.
+
+       >>> for_every (lambda c: c > 5,(6,7,8,9))
+       True
+
+       @author Source:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52907
+       """
+
+       for i in seq:
+               if not pred(i):
+                       return False
+       return True
+
+
+def there_exists(pred, seq):
+       """
+       there_exists takes a one argument predicate     function and a sequence.
+       @param pred The predicate function should return true or false.
+       @returns true if any element in seq returns true for predicate, else returns false.
+
+       >>> there_exists (lambda c: c > 5,(6,7,8,9))
+       True
+
+       @author Source:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52907
+       """
+
+       for i in seq:
+               if pred(i):
+                       return True
+       return False
+
+
+def func_repeat(quantity, func, *args, **kwd):
+       """
+       Meant to be in connection with "reduce"
+       """
+       for i in xrange(quantity):
+               yield func(*args, **kwd)
+
+
+def function_map(preds, item):
+       """
+       Meant to be in connection with "reduce"
+       """
+       results = (pred(item) for pred in preds)
+
+       return results
+
+
+def functional_if(combiner, preds, item):
+       """
+       Combines the result of a list of predicates applied to item according to combiner
+
+       @see any, every for example combiners
+       """
+       pass_bool = lambda b: b
+
+       bool_results = function_map(preds, item)
+       return combiner(pass_bool, bool_results)
+
+
+def pushback_itr(itr):
+       """
+       >>> list(pushback_itr(xrange(5)))
+       [0, 1, 2, 3, 4]
+       >>>
+       >>> first = True
+       >>> itr = pushback_itr(xrange(5))
+       >>> for i in itr:
+       ...     print i
+       ...     if first and i == 2:
+       ...             first = False
+       ...             print itr.send(i)
+       0
+       1
+       2
+       None
+       2
+       3
+       4
+       >>>
+       >>> first = True
+       >>> itr = pushback_itr(xrange(5))
+       >>> for i in itr:
+       ...     print i
+       ...     if first and i == 2:
+       ...             first = False
+       ...             print itr.send(i)
+       ...             print itr.send(i)
+       0
+       1
+       2
+       None
+       None
+       2
+       2
+       3
+       4
+       >>>
+       >>> itr = pushback_itr(xrange(5))
+       >>> print itr.next()
+       0
+       >>> print itr.next()
+       1
+       >>> print itr.send(10)
+       None
+       >>> print itr.next()
+       10
+       >>> print itr.next()
+       2
+       >>> print itr.send(20)
+       None
+       >>> print itr.send(30)
+       None
+       >>> print itr.send(40)
+       None
+       >>> print itr.next()
+       40
+       >>> print itr.next()
+       30
+       >>> print itr.send(50)
+       None
+       >>> print itr.next()
+       50
+       >>> print itr.next()
+       20
+       >>> print itr.next()
+       3
+       >>> print itr.next()
+       4
+       """
+       for item in itr:
+               maybePushedBack = yield item
+               queue = []
+               while queue or maybePushedBack is not None:
+                       if maybePushedBack is not None:
+                               queue.append(maybePushedBack)
+                               maybePushedBack = yield None
+                       else:
+                               item = queue.pop()
+                               maybePushedBack = yield item
+
+
+def itr_available(queue, initiallyBlock = False):
+       if initiallyBlock:
+               yield queue.get()
+       while not queue.empty():
+               yield queue.get_nowait()
+
+
+class BloomFilter(object):
+       """
+       http://en.wikipedia.org/wiki/Bloom_filter
+       Sources:
+       http://code.activestate.com/recipes/577684-bloom-filter/
+       http://code.activestate.com/recipes/577686-bloom-filter/
+
+       >>> from random import sample
+       >>> from string import ascii_letters
+       >>> states = '''Alabama Alaska Arizona Arkansas California Colorado Connecticut
+       ... Delaware Florida Georgia Hawaii Idaho Illinois Indiana Iowa Kansas
+       ... Kentucky Louisiana Maine Maryland Massachusetts Michigan Minnesota
+       ... Mississippi Missouri Montana Nebraska Nevada NewHampshire NewJersey
+       ... NewMexico NewYork NorthCarolina NorthDakota Ohio Oklahoma Oregon
+       ... Pennsylvania RhodeIsland SouthCarolina SouthDakota Tennessee Texas Utah
+       ... Vermont Virginia Washington WestVirginia Wisconsin Wyoming'''.split()
+       >>> bf = BloomFilter(num_bits=1000, num_probes=14)
+       >>> for state in states:
+       ...     bf.add(state)
+       >>> numStatesFound = sum(state in bf for state in states)
+       >>> numStatesFound, len(states)
+       (50, 50)
+       >>> trials = 100
+       >>> numGarbageFound = sum(''.join(sample(ascii_letters, 5)) in bf for i in range(trials))
+       >>> numGarbageFound, trials
+       (0, 100)
+       """
+
+       def __init__(self, num_bits, num_probes):
+               num_words = (num_bits + 31) // 32
+               self._arr = array.array('B', [0]) * num_words
+               self._num_probes = num_probes
+
+       def add(self, key):
+               for i, mask in self._get_probes(key):
+                       self._arr[i] |= mask
+
+       def union(self, bfilter):
+               if self._match_template(bfilter):
+                       for i, b in enumerate(bfilter._arr):
+                               self._arr[i] |= b
+               else:
+                       # Union b/w two unrelated bloom filter raises this
+                       raise ValueError("Mismatched bloom filters")
+
+       def intersection(self, bfilter):
+               if self._match_template(bfilter):
+                       for i, b in enumerate(bfilter._arr):
+                               self._arr[i] &= b
+               else:
+                       # Intersection b/w two unrelated bloom filter raises this
+                       raise ValueError("Mismatched bloom filters")
+
+       def __contains__(self, key):
+               return all(self._arr[i] & mask for i, mask in self._get_probes(key))
+
+       def _match_template(self, bfilter):
+               return self.num_bits == bfilter.num_bits and self.num_probes == bfilter.num_probes
+
+       def _get_probes(self, key):
+               hasher = random.Random(key).randrange
+               for _ in range(self._num_probes):
+                       array_index = hasher(len(self._arr))
+                       bit_index = hasher(32)
+                       yield array_index, 1 << bit_index
+
+
+if __name__ == "__main__":
+       import doctest
+       print doctest.testmod()
diff --git a/gonvert/util/concurrent.py b/gonvert/util/concurrent.py
new file mode 100644 (file)
index 0000000..f5f6e1d
--- /dev/null
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+
+import os
+import errno
+import time
+import functools
+import contextlib
+import logging
+
+import misc
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class AsyncTaskQueue(object):
+
+       def __init__(self, taskPool):
+               self._asyncs = []
+               self._taskPool = taskPool
+
+       def add_async(self, func):
+               self.flush()
+               a = AsyncGeneratorTask(self._taskPool, func)
+               self._asyncs.append(a)
+               return a
+
+       def flush(self):
+               self._asyncs = [a for a in self._asyncs if not a.isDone]
+
+
+class AsyncGeneratorTask(object):
+
+       def __init__(self, pool, func):
+               self._pool = pool
+               self._func = func
+               self._run = None
+               self._isDone = False
+
+       @property
+       def isDone(self):
+               return self._isDone
+
+       def start(self, *args, **kwds):
+               assert self._run is None, "Task already started"
+               self._run = self._func(*args, **kwds)
+               trampoline, args, kwds = self._run.send(None) # priming the function
+               self._pool.add_task(
+                       trampoline,
+                       args,
+                       kwds,
+                       self.on_success,
+                       self.on_error,
+               )
+
+       @misc.log_exception(_moduleLogger)
+       def on_success(self, result):
+               _moduleLogger.debug("Processing success for: %r", self._func)
+               try:
+                       trampoline, args, kwds = self._run.send(result)
+               except StopIteration, e:
+                       self._isDone = True
+               else:
+                       self._pool.add_task(
+                               trampoline,
+                               args,
+                               kwds,
+                               self.on_success,
+                               self.on_error,
+                       )
+
+       @misc.log_exception(_moduleLogger)
+       def on_error(self, error):
+               _moduleLogger.debug("Processing error for: %r", self._func)
+               try:
+                       trampoline, args, kwds = self._run.throw(error)
+               except StopIteration, e:
+                       self._isDone = True
+               else:
+                       self._pool.add_task(
+                               trampoline,
+                               args,
+                               kwds,
+                               self.on_success,
+                               self.on_error,
+                       )
+
+       def __repr__(self):
+               return "<async %s at 0x%x>" % (self._func.__name__, id(self))
+
+       def __hash__(self):
+               return hash(self._func)
+
+       def __eq__(self, other):
+               return self._func == other._func
+
+       def __ne__(self, other):
+               return self._func != other._func
+
+
+def synchronized(lock):
+       """
+       Synchronization decorator.
+
+       >>> import misc
+       >>> misc.validate_decorator(synchronized(object()))
+       """
+
+       def wrap(f):
+
+               @functools.wraps(f)
+               def newFunction(*args, **kw):
+                       lock.acquire()
+                       try:
+                               return f(*args, **kw)
+                       finally:
+                               lock.release()
+               return newFunction
+       return wrap
+
+
+@contextlib.contextmanager
+def qlock(queue, gblock = True, gtimeout = None, pblock = True, ptimeout = None):
+       """
+       Locking with a queue, good for when you want to lock an item passed around
+
+       >>> import Queue
+       >>> item = 5
+       >>> lock = Queue.Queue()
+       >>> lock.put(item)
+       >>> with qlock(lock) as i:
+       ...     print i
+       5
+       """
+       item = queue.get(gblock, gtimeout)
+       try:
+               yield item
+       finally:
+               queue.put(item, pblock, ptimeout)
+
+
+@contextlib.contextmanager
+def flock(path, timeout=-1):
+       WAIT_FOREVER = -1
+       DELAY = 0.1
+       timeSpent = 0
+
+       acquired = False
+
+       while timeSpent <= timeout or timeout == WAIT_FOREVER:
+               try:
+                       fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
+                       acquired = True
+                       break
+               except OSError, e:
+                       if e.errno != errno.EEXIST:
+                               raise
+               time.sleep(DELAY)
+               timeSpent += DELAY
+
+       assert acquired, "Failed to grab file-lock %s within timeout %d" % (path, timeout)
+
+       try:
+               yield fd
+       finally:
+               os.unlink(path)
diff --git a/gonvert/util/coroutines.py b/gonvert/util/coroutines.py
new file mode 100755 (executable)
index 0000000..b1e539e
--- /dev/null
@@ -0,0 +1,623 @@
+#!/usr/bin/env python\r
+\r
+"""\r
+Uses for generators\r
+* Pull pipelining (iterators)\r
+* Push pipelining (coroutines)\r
+* State machines (coroutines)\r
+* "Cooperative multitasking" (coroutines)\r
+* Algorithm -> Object transform for cohesiveness (for example context managers) (coroutines)\r
+\r
+Design considerations\r
+* When should a stage pass on exceptions or have it thrown within it?\r
+* When should a stage pass on GeneratorExits?\r
+* Is there a way to either turn a push generator into a iterator or to use\r
+       comprehensions syntax for push generators (I doubt it)\r
+* When should the stage try and send data in both directions\r
+* Since pull generators (generators), push generators (coroutines), subroutines, and coroutines are all coroutines, maybe we should rename the push generators to not confuse them, like signals/slots? and then refer to two-way generators as coroutines\r
+** If so, make s* and co* implementation of functions\r
+"""\r
+\r
+import threading\r
+import Queue\r
+import pickle\r
+import functools\r
+import itertools\r
+import xml.sax\r
+import xml.parsers.expat\r
+\r
+\r
+def autostart(func):\r
+       """\r
+       >>> @autostart\r
+       ... def grep_sink(pattern):\r
+       ...     print "Looking for %s" % pattern\r
+       ...     while True:\r
+       ...             line = yield\r
+       ...             if pattern in line:\r
+       ...                     print line,\r
+       >>> g = grep_sink("python")\r
+       Looking for python\r
+       >>> g.send("Yeah but no but yeah but no")\r
+       >>> g.send("A series of tubes")\r
+       >>> g.send("python generators rock!")\r
+       python generators rock!\r
+       >>> g.close()\r
+       """\r
+\r
+       @functools.wraps(func)\r
+       def start(*args, **kwargs):\r
+               cr = func(*args, **kwargs)\r
+               cr.next()\r
+               return cr\r
+\r
+       return start\r
+\r
+\r
+@autostart\r
+def printer_sink(format = "%s"):\r
+       """\r
+       >>> pr = printer_sink("%r")\r
+       >>> pr.send("Hello")\r
+       'Hello'\r
+       >>> pr.send("5")\r
+       '5'\r
+       >>> pr.send(5)\r
+       5\r
+       >>> p = printer_sink()\r
+       >>> p.send("Hello")\r
+       Hello\r
+       >>> p.send("World")\r
+       World\r
+       >>> # p.throw(RuntimeError, "Goodbye")\r
+       >>> # p.send("Meh")\r
+       >>> # p.close()\r
+       """\r
+       while True:\r
+               item = yield\r
+               print format % (item, )\r
+\r
+\r
+@autostart\r
+def null_sink():\r
+       """\r
+       Good for uses like with cochain to pick up any slack\r
+       """\r
+       while True:\r
+               item = yield\r
+\r
+\r
+def itr_source(itr, target):\r
+       """\r
+       >>> itr_source(xrange(2), printer_sink())\r
+       0\r
+       1\r
+       """\r
+       for item in itr:\r
+               target.send(item)\r
+\r
+\r
+@autostart\r
+def cofilter(predicate, target):\r
+       """\r
+       >>> p = printer_sink()\r
+       >>> cf = cofilter(None, p)\r
+       >>> cf.send("")\r
+       >>> cf.send("Hello")\r
+       Hello\r
+       >>> cf.send([])\r
+       >>> cf.send([1, 2])\r
+       [1, 2]\r
+       >>> cf.send(False)\r
+       >>> cf.send(True)\r
+       True\r
+       >>> cf.send(0)\r
+       >>> cf.send(1)\r
+       1\r
+       >>> # cf.throw(RuntimeError, "Goodbye")\r
+       >>> # cf.send(False)\r
+       >>> # cf.send(True)\r
+       >>> # cf.close()\r
+       """\r
+       if predicate is None:\r
+               predicate = bool\r
+\r
+       while True:\r
+               try:\r
+                       item = yield\r
+                       if predicate(item):\r
+                               target.send(item)\r
+               except StandardError, e:\r
+                       target.throw(e.__class__, e.message)\r
+\r
+\r
+@autostart\r
+def comap(function, target):\r
+       """\r
+       >>> p = printer_sink()\r
+       >>> cm = comap(lambda x: x+1, p)\r
+       >>> cm.send(0)\r
+       1\r
+       >>> cm.send(1.0)\r
+       2.0\r
+       >>> cm.send(-2)\r
+       -1\r
+       >>> # cm.throw(RuntimeError, "Goodbye")\r
+       >>> # cm.send(0)\r
+       >>> # cm.send(1.0)\r
+       >>> # cm.close()\r
+       """\r
+       while True:\r
+               try:\r
+                       item = yield\r
+                       mappedItem = function(item)\r
+                       target.send(mappedItem)\r
+               except StandardError, e:\r
+                       target.throw(e.__class__, e.message)\r
+\r
+\r
+def func_sink(function):\r
+       return comap(function, null_sink())\r
+\r
+\r
+def expand_positional(function):\r
+\r
+       @functools.wraps(function)\r
+       def expander(item):\r
+               return function(*item)\r
+\r
+       return expander\r
+\r
+\r
+@autostart\r
+def append_sink(l):\r
+       """\r
+       >>> l = []\r
+       >>> apps = append_sink(l)\r
+       >>> apps.send(1)\r
+       >>> apps.send(2)\r
+       >>> apps.send(3)\r
+       >>> print l\r
+       [1, 2, 3]\r
+       """\r
+       while True:\r
+               item = yield\r
+               l.append(item)\r
+\r
+\r
+@autostart\r
+def last_n_sink(l, n = 1):\r
+       """\r
+       >>> l = []\r
+       >>> lns = last_n_sink(l)\r
+       >>> lns.send(1)\r
+       >>> lns.send(2)\r
+       >>> lns.send(3)\r
+       >>> print l\r
+       [3]\r
+       """\r
+       del l[:]\r
+       while True:\r
+               item = yield\r
+               extraCount = len(l) - n + 1\r
+               if 0 < extraCount:\r
+                       del l[0:extraCount]\r
+               l.append(item)\r
+\r
+\r
+@autostart\r
+def coreduce(target, function, initializer = None):\r
+       """\r
+       >>> reduceResult = []\r
+       >>> lns = last_n_sink(reduceResult)\r
+       >>> cr = coreduce(lns, lambda x, y: x + y, 0)\r
+       >>> cr.send(1)\r
+       >>> cr.send(2)\r
+       >>> cr.send(3)\r
+       >>> print reduceResult\r
+       [6]\r
+       >>> cr = coreduce(lns, lambda x, y: x + y)\r
+       >>> cr.send(1)\r
+       >>> cr.send(2)\r
+       >>> cr.send(3)\r
+       >>> print reduceResult\r
+       [6]\r
+       """\r
+       isFirst = True\r
+       cumulativeRef = initializer\r
+       while True:\r
+               item = yield\r
+               if isFirst and initializer is None:\r
+                       cumulativeRef = item\r
+               else:\r
+                       cumulativeRef = function(cumulativeRef, item)\r
+               target.send(cumulativeRef)\r
+               isFirst = False\r
+\r
+\r
+@autostart\r
+def cotee(targets):\r
+       """\r
+       Takes a sequence of coroutines and sends the received items to all of them\r
+\r
+       >>> ct = cotee((printer_sink("1 %s"), printer_sink("2 %s")))\r
+       >>> ct.send("Hello")\r
+       1 Hello\r
+       2 Hello\r
+       >>> ct.send("World")\r
+       1 World\r
+       2 World\r
+       >>> # ct.throw(RuntimeError, "Goodbye")\r
+       >>> # ct.send("Meh")\r
+       >>> # ct.close()\r
+       """\r
+       while True:\r
+               try:\r
+                       item = yield\r
+                       for target in targets:\r
+                               target.send(item)\r
+               except StandardError, e:\r
+                       for target in targets:\r
+                               target.throw(e.__class__, e.message)\r
+\r
+\r
+class CoTee(object):\r
+       """\r
+       >>> ct = CoTee()\r
+       >>> ct.register_sink(printer_sink("1 %s"))\r
+       >>> ct.register_sink(printer_sink("2 %s"))\r
+       >>> ct.stage.send("Hello")\r
+       1 Hello\r
+       2 Hello\r
+       >>> ct.stage.send("World")\r
+       1 World\r
+       2 World\r
+       >>> ct.register_sink(printer_sink("3 %s"))\r
+       >>> ct.stage.send("Foo")\r
+       1 Foo\r
+       2 Foo\r
+       3 Foo\r
+       >>> # ct.stage.throw(RuntimeError, "Goodbye")\r
+       >>> # ct.stage.send("Meh")\r
+       >>> # ct.stage.close()\r
+       """\r
+\r
+       def __init__(self):\r
+               self.stage = self._stage()\r
+               self._targets = []\r
+\r
+       def register_sink(self, sink):\r
+               self._targets.append(sink)\r
+\r
+       def unregister_sink(self, sink):\r
+               self._targets.remove(sink)\r
+\r
+       def restart(self):\r
+               self.stage = self._stage()\r
+\r
+       @autostart\r
+       def _stage(self):\r
+               while True:\r
+                       try:\r
+                               item = yield\r
+                               for target in self._targets:\r
+                                       target.send(item)\r
+                       except StandardError, e:\r
+                               for target in self._targets:\r
+                                       target.throw(e.__class__, e.message)\r
+\r
+\r
+def _flush_queue(queue):\r
+       while not queue.empty():\r
+               yield queue.get()\r
+\r
+\r
+@autostart\r
+def cocount(target, start = 0):\r
+       """\r
+       >>> cc = cocount(printer_sink("%s"))\r
+       >>> cc.send("a")\r
+       0\r
+       >>> cc.send(None)\r
+       1\r
+       >>> cc.send([])\r
+       2\r
+       >>> cc.send(0)\r
+       3\r
+       """\r
+       for i in itertools.count(start):\r
+               item = yield\r
+               target.send(i)\r
+\r
+\r
+@autostart\r
+def coenumerate(target, start = 0):\r
+       """\r
+       >>> ce = coenumerate(printer_sink("%r"))\r
+       >>> ce.send("a")\r
+       (0, 'a')\r
+       >>> ce.send(None)\r
+       (1, None)\r
+       >>> ce.send([])\r
+       (2, [])\r
+       >>> ce.send(0)\r
+       (3, 0)\r
+       """\r
+       for i in itertools.count(start):\r
+               item = yield\r
+               decoratedItem = i, item\r
+               target.send(decoratedItem)\r
+\r
+\r
+@autostart\r
+def corepeat(target, elem):\r
+       """\r
+       >>> cr = corepeat(printer_sink("%s"), "Hello World")\r
+       >>> cr.send("a")\r
+       Hello World\r
+       >>> cr.send(None)\r
+       Hello World\r
+       >>> cr.send([])\r
+       Hello World\r
+       >>> cr.send(0)\r
+       Hello World\r
+       """\r
+       while True:\r
+               item = yield\r
+               target.send(elem)\r
+\r
+\r
+@autostart\r
+def cointercept(target, elems):\r
+       """\r
+       >>> cr = cointercept(printer_sink("%s"), [1, 2, 3, 4])\r
+       >>> cr.send("a")\r
+       1\r
+       >>> cr.send(None)\r
+       2\r
+       >>> cr.send([])\r
+       3\r
+       >>> cr.send(0)\r
+       4\r
+       >>> cr.send("Bye")\r
+       Traceback (most recent call last):\r
+         File "/usr/lib/python2.5/doctest.py", line 1228, in __run\r
+           compileflags, 1) in test.globs\r
+         File "<doctest __main__.cointercept[5]>", line 1, in <module>\r
+           cr.send("Bye")\r
+       StopIteration\r
+       """\r
+       item = yield\r
+       for elem in elems:\r
+               target.send(elem)\r
+               item = yield\r
+\r
+\r
+@autostart\r
+def codropwhile(target, pred):\r
+       """\r
+       >>> cdw = codropwhile(printer_sink("%s"), lambda x: x)\r
+       >>> cdw.send([0, 1, 2])\r
+       >>> cdw.send(1)\r
+       >>> cdw.send(True)\r
+       >>> cdw.send(False)\r
+       >>> cdw.send([0, 1, 2])\r
+       [0, 1, 2]\r
+       >>> cdw.send(1)\r
+       1\r
+       >>> cdw.send(True)\r
+       True\r
+       """\r
+       while True:\r
+               item = yield\r
+               if not pred(item):\r
+                       break\r
+\r
+       while True:\r
+               item = yield\r
+               target.send(item)\r
+\r
+\r
+@autostart\r
+def cotakewhile(target, pred):\r
+       """\r
+       >>> ctw = cotakewhile(printer_sink("%s"), lambda x: x)\r
+       >>> ctw.send([0, 1, 2])\r
+       [0, 1, 2]\r
+       >>> ctw.send(1)\r
+       1\r
+       >>> ctw.send(True)\r
+       True\r
+       >>> ctw.send(False)\r
+       >>> ctw.send([0, 1, 2])\r
+       >>> ctw.send(1)\r
+       >>> ctw.send(True)\r
+       """\r
+       while True:\r
+               item = yield\r
+               if not pred(item):\r
+                       break\r
+               target.send(item)\r
+\r
+       while True:\r
+               item = yield\r
+\r
+\r
+@autostart\r
+def coslice(target, lower, upper):\r
+       """\r
+       >>> cs = coslice(printer_sink("%r"), 3, 5)\r
+       >>> cs.send("0")\r
+       >>> cs.send("1")\r
+       >>> cs.send("2")\r
+       >>> cs.send("3")\r
+       '3'\r
+       >>> cs.send("4")\r
+       '4'\r
+       >>> cs.send("5")\r
+       >>> cs.send("6")\r
+       """\r
+       for i in xrange(lower):\r
+               item = yield\r
+       for i in xrange(upper - lower):\r
+               item = yield\r
+               target.send(item)\r
+       while True:\r
+               item = yield\r
+\r
+\r
+@autostart\r
+def cochain(targets):\r
+       """\r
+       >>> cr = cointercept(printer_sink("good %s"), [1, 2, 3, 4])\r
+       >>> cc = cochain([cr, printer_sink("end %s")])\r
+       >>> cc.send("a")\r
+       good 1\r
+       >>> cc.send(None)\r
+       good 2\r
+       >>> cc.send([])\r
+       good 3\r
+       >>> cc.send(0)\r
+       good 4\r
+       >>> cc.send("Bye")\r
+       end Bye\r
+       """\r
+       behind = []\r
+       for target in targets:\r
+               try:\r
+                       while behind:\r
+                               item = behind.pop()\r
+                               target.send(item)\r
+                       while True:\r
+                               item = yield\r
+                               target.send(item)\r
+               except StopIteration:\r
+                       behind.append(item)\r
+\r
+\r
+@autostart\r
+def queue_sink(queue):\r
+       """\r
+       >>> q = Queue.Queue()\r
+       >>> qs = queue_sink(q)\r
+       >>> qs.send("Hello")\r
+       >>> qs.send("World")\r
+       >>> qs.throw(RuntimeError, "Goodbye")\r
+       >>> qs.send("Meh")\r
+       >>> qs.close()\r
+       >>> print [i for i in _flush_queue(q)]\r
+       [(None, 'Hello'), (None, 'World'), (<type 'exceptions.RuntimeError'>, 'Goodbye'), (None, 'Meh'), (<type 'exceptions.GeneratorExit'>, None)]\r
+       """\r
+       while True:\r
+               try:\r
+                       item = yield\r
+                       queue.put((None, item))\r
+               except StandardError, e:\r
+                       queue.put((e.__class__, e.message))\r
+               except GeneratorExit:\r
+                       queue.put((GeneratorExit, None))\r
+                       raise\r
+\r
+\r
+def decode_item(item, target):\r
+       if item[0] is None:\r
+               target.send(item[1])\r
+               return False\r
+       elif item[0] is GeneratorExit:\r
+               target.close()\r
+               return True\r
+       else:\r
+               target.throw(item[0], item[1])\r
+               return False\r
+\r
+\r
+def queue_source(queue, target):\r
+       """\r
+       >>> q = Queue.Queue()\r
+       >>> for i in [\r
+       ...     (None, 'Hello'),\r
+       ...     (None, 'World'),\r
+       ...     (GeneratorExit, None),\r
+       ...     ]:\r
+       ...     q.put(i)\r
+       >>> qs = queue_source(q, printer_sink())\r
+       Hello\r
+       World\r
+       """\r
+       isDone = False\r
+       while not isDone:\r
+               item = queue.get()\r
+               isDone = decode_item(item, target)\r
+\r
+\r
+def threaded_stage(target, thread_factory = threading.Thread):\r
+       messages = Queue.Queue()\r
+\r
+       run_source = functools.partial(queue_source, messages, target)\r
+       thread_factory(target=run_source).start()\r
+\r
+       # Sink running in current thread\r
+       return functools.partial(queue_sink, messages)\r
+\r
+\r
+@autostart\r
+def pickle_sink(f):\r
+       while True:\r
+               try:\r
+                       item = yield\r
+                       pickle.dump((None, item), f)\r
+               except StandardError, e:\r
+                       pickle.dump((e.__class__, e.message), f)\r
+               except GeneratorExit:\r
+                       pickle.dump((GeneratorExit, ), f)\r
+                       raise\r
+               except StopIteration:\r
+                       f.close()\r
+                       return\r
+\r
+\r
+def pickle_source(f, target):\r
+       try:\r
+               isDone = False\r
+               while not isDone:\r
+                       item = pickle.load(f)\r
+                       isDone = decode_item(item, target)\r
+       except EOFError:\r
+               target.close()\r
+\r
+\r
+class EventHandler(object, xml.sax.ContentHandler):\r
+\r
+       START = "start"\r
+       TEXT = "text"\r
+       END = "end"\r
+\r
+       def __init__(self, target):\r
+               object.__init__(self)\r
+               xml.sax.ContentHandler.__init__(self)\r
+               self._target = target\r
+\r
+       def startElement(self, name, attrs):\r
+               self._target.send((self.START, (name, attrs._attrs)))\r
+\r
+       def characters(self, text):\r
+               self._target.send((self.TEXT, text))\r
+\r
+       def endElement(self, name):\r
+               self._target.send((self.END, name))\r
+\r
+\r
+def expat_parse(f, target):\r
+       parser = xml.parsers.expat.ParserCreate()\r
+       parser.buffer_size = 65536\r
+       parser.buffer_text = True\r
+       parser.returns_unicode = False\r
+       parser.StartElementHandler = lambda name, attrs: target.send(('start', (name, attrs)))\r
+       parser.EndElementHandler = lambda name: target.send(('end', name))\r
+       parser.CharacterDataHandler = lambda data: target.send(('text', data))\r
+       parser.ParseFile(f)\r
+\r
+\r
+if __name__ == "__main__":\r
+       import doctest\r
+       doctest.testmod()\r
diff --git a/gonvert/util/go_utils.py b/gonvert/util/go_utils.py
new file mode 100644 (file)
index 0000000..61e731d
--- /dev/null
@@ -0,0 +1,274 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+
+import time
+import functools
+import threading
+import Queue
+import logging
+
+import gobject
+
+import algorithms
+import misc
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+def make_idler(func):
+       """
+       Decorator that makes a generator-function into a function that will continue execution on next call
+       """
+       a = []
+
+       @functools.wraps(func)
+       def decorated_func(*args, **kwds):
+               if not a:
+                       a.append(func(*args, **kwds))
+               try:
+                       a[0].next()
+                       return True
+               except StopIteration:
+                       del a[:]
+                       return False
+
+       return decorated_func
+
+
+def async(func):
+       """
+       Make a function mainloop friendly. the function will be called at the
+       next mainloop idle state.
+
+       >>> import misc
+       >>> misc.validate_decorator(async)
+       """
+
+       @functools.wraps(func)
+       def new_function(*args, **kwargs):
+
+               def async_function():
+                       func(*args, **kwargs)
+                       return False
+
+               gobject.idle_add(async_function)
+
+       return new_function
+
+
+class Async(object):
+
+       def __init__(self, func, once = True):
+               self.__func = func
+               self.__idleId = None
+               self.__once = once
+
+       def start(self):
+               assert self.__idleId is None
+               if self.__once:
+                       self.__idleId = gobject.idle_add(self._on_once)
+               else:
+                       self.__idleId = gobject.idle_add(self.__func)
+
+       def is_running(self):
+               return self.__idleId is not None
+
+       def cancel(self):
+               if self.__idleId is not None:
+                       gobject.source_remove(self.__idleId)
+                       self.__idleId = None
+
+       def __call__(self):
+               return self.start()
+
+       @misc.log_exception(_moduleLogger)
+       def _on_once(self):
+               self.cancel()
+               try:
+                       self.__func()
+               except Exception:
+                       pass
+               return False
+
+
+class Timeout(object):
+
+       def __init__(self, func, once = True):
+               self.__func = func
+               self.__timeoutId = None
+               self.__once = once
+
+       def start(self, **kwds):
+               assert self.__timeoutId is None
+
+               callback = self._on_once if self.__once else self.__func
+
+               assert len(kwds) == 1
+               timeoutInSeconds = kwds["seconds"]
+               assert 0 <= timeoutInSeconds
+
+               if timeoutInSeconds == 0:
+                       self.__timeoutId = gobject.idle_add(callback)
+               else:
+                       self.__timeoutId = timeout_add_seconds(timeoutInSeconds, callback)
+
+       def is_running(self):
+               return self.__timeoutId is not None
+
+       def cancel(self):
+               if self.__timeoutId is not None:
+                       gobject.source_remove(self.__timeoutId)
+                       self.__timeoutId = None
+
+       def __call__(self, **kwds):
+               return self.start(**kwds)
+
+       @misc.log_exception(_moduleLogger)
+       def _on_once(self):
+               self.cancel()
+               try:
+                       self.__func()
+               except Exception:
+                       pass
+               return False
+
+
+_QUEUE_EMPTY = object()
+
+
+class FutureThread(object):
+
+       def __init__(self):
+               self.__workQueue = Queue.Queue()
+               self.__thread = threading.Thread(
+                       name = type(self).__name__,
+                       target = self.__consume_queue,
+               )
+               self.__isRunning = True
+
+       def start(self):
+               self.__thread.start()
+
+       def stop(self):
+               self.__isRunning = False
+               for _ in algorithms.itr_available(self.__workQueue):
+                       pass # eat up queue to cut down dumb work
+               self.__workQueue.put(_QUEUE_EMPTY)
+
+       def clear_tasks(self):
+               for _ in algorithms.itr_available(self.__workQueue):
+                       pass # eat up queue to cut down dumb work
+
+       def add_task(self, func, args, kwds, on_success, on_error):
+               task = func, args, kwds, on_success, on_error
+               self.__workQueue.put(task)
+
+       @misc.log_exception(_moduleLogger)
+       def __trampoline_callback(self, on_success, on_error, isError, result):
+               if not self.__isRunning:
+                       if isError:
+                               _moduleLogger.error("Masking: %s" % (result, ))
+                       isError = True
+                       result = StopIteration("Cancelling all callbacks")
+               callback = on_success if not isError else on_error
+               try:
+                       callback(result)
+               except Exception:
+                       _moduleLogger.exception("Callback errored")
+               return False
+
+       @misc.log_exception(_moduleLogger)
+       def __consume_queue(self):
+               while True:
+                       task = self.__workQueue.get()
+                       if task is _QUEUE_EMPTY:
+                               break
+                       func, args, kwds, on_success, on_error = task
+
+                       try:
+                               result = func(*args, **kwds)
+                               isError = False
+                       except Exception, e:
+                               _moduleLogger.error("Error, passing it back to the main thread")
+                               result = e
+                               isError = True
+                       self.__workQueue.task_done()
+
+                       gobject.idle_add(self.__trampoline_callback, on_success, on_error, isError, result)
+               _moduleLogger.debug("Shutting down worker thread")
+
+
+class AutoSignal(object):
+
+       def __init__(self, toplevel):
+               self.__disconnectPool = []
+               toplevel.connect("destroy", self.__on_destroy)
+
+       def connect_auto(self, widget, *args):
+               id = widget.connect(*args)
+               self.__disconnectPool.append((widget, id))
+
+       @misc.log_exception(_moduleLogger)
+       def __on_destroy(self, widget):
+               _moduleLogger.info("Destroy: %r (%s to clean up)" % (self, len(self.__disconnectPool)))
+               for widget, id in self.__disconnectPool:
+                       widget.disconnect(id)
+               del self.__disconnectPool[:]
+
+
+def throttled(minDelay, queue):
+       """
+       Throttle the calls to a function by queueing all the calls that happen
+       before the minimum delay
+
+       >>> import misc
+       >>> import Queue
+       >>> misc.validate_decorator(throttled(0, Queue.Queue()))
+       """
+
+       def actual_decorator(func):
+
+               lastCallTime = [None]
+
+               def process_queue():
+                       if 0 < len(queue):
+                               func, args, kwargs = queue.pop(0)
+                               lastCallTime[0] = time.time() * 1000
+                               func(*args, **kwargs)
+                       return False
+
+               @functools.wraps(func)
+               def new_function(*args, **kwargs):
+                       now = time.time() * 1000
+                       if (
+                               lastCallTime[0] is None or
+                               (now - lastCallTime >= minDelay)
+                       ):
+                               lastCallTime[0] = now
+                               func(*args, **kwargs)
+                       else:
+                               queue.append((func, args, kwargs))
+                               lastCallDelta = now - lastCallTime[0]
+                               processQueueTimeout = int(minDelay * len(queue) - lastCallDelta)
+                               gobject.timeout_add(processQueueTimeout, process_queue)
+
+               return new_function
+
+       return actual_decorator
+
+
+def _old_timeout_add_seconds(timeout, callback):
+       return gobject.timeout_add(timeout * 1000, callback)
+
+
+def _timeout_add_seconds(timeout, callback):
+       return gobject.timeout_add_seconds(timeout, callback)
+
+
+try:
+       gobject.timeout_add_seconds
+       timeout_add_seconds = _timeout_add_seconds
+except AttributeError:
+       timeout_add_seconds = _old_timeout_add_seconds
diff --git a/gonvert/util/gtk_utils.py b/gonvert/util/gtk_utils.py
new file mode 100644 (file)
index 0000000..342feae
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+from __future__ import division
+
+import contextlib
+import logging
+
+import gtk
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+@contextlib.contextmanager
+def gtk_lock():
+       gtk.gdk.threads_enter()
+       try:
+               yield
+       finally:
+               gtk.gdk.threads_leave()
+
+
+def find_parent_window(widget):
+       while True:
+               parent = widget.get_parent()
+               if isinstance(parent, gtk.Window):
+                       return parent
+               widget = parent
+
+
+if __name__ == "__main__":
+       pass
+
diff --git a/gonvert/util/hildonize.py b/gonvert/util/hildonize.py
new file mode 100644 (file)
index 0000000..339eb2a
--- /dev/null
@@ -0,0 +1,766 @@
+#!/usr/bin/env python
+
+"""
+Open Issues
+       @bug not all of a message is shown
+       @bug Buttons are too small
+"""
+
+
+import gobject
+import gtk
+import dbus
+
+
+class _NullHildonModule(object):
+       pass
+
+
+try:
+       import hildon as _hildon
+       hildon  = _hildon # Dumb but gets around pyflakiness
+except (ImportError, OSError):
+       hildon = _NullHildonModule
+
+
+IS_HILDON_SUPPORTED = hildon is not _NullHildonModule
+
+
+class _NullHildonProgram(object):
+
+       def add_window(self, window):
+               pass
+
+
+def _hildon_get_app_class():
+       return hildon.Program
+
+
+def _null_get_app_class():
+       return _NullHildonProgram
+
+
+try:
+       hildon.Program
+       get_app_class = _hildon_get_app_class
+except AttributeError:
+       get_app_class = _null_get_app_class
+
+
+def _hildon_set_application_name(name):
+       gtk.set_application_name(name)
+
+
+def _null_set_application_name(name):
+       pass
+
+
+try:
+       gtk.set_application_name
+       set_application_name = _hildon_set_application_name
+except AttributeError:
+       set_application_name = _null_set_application_name
+
+
+def _fremantle_hildonize_window(app, window):
+       oldWindow = window
+       newWindow = hildon.StackableWindow()
+       if oldWindow.get_child() is not None:
+               oldWindow.get_child().reparent(newWindow)
+       app.add_window(newWindow)
+       return newWindow
+
+
+def _hildon_hildonize_window(app, window):
+       oldWindow = window
+       newWindow = hildon.Window()
+       if oldWindow.get_child() is not None:
+               oldWindow.get_child().reparent(newWindow)
+       app.add_window(newWindow)
+       return newWindow
+
+
+def _null_hildonize_window(app, window):
+       return window
+
+
+try:
+       hildon.StackableWindow
+       hildonize_window = _fremantle_hildonize_window
+except AttributeError:
+       try:
+               hildon.Window
+               hildonize_window = _hildon_hildonize_window
+       except AttributeError:
+               hildonize_window = _null_hildonize_window
+
+
+def _fremantle_hildonize_menu(window, gtkMenu):
+       appMenu = hildon.AppMenu()
+       window.set_app_menu(appMenu)
+       gtkMenu.get_parent().remove(gtkMenu)
+       return appMenu
+
+
+def _hildon_hildonize_menu(window, gtkMenu):
+       hildonMenu = gtk.Menu()
+       for child in gtkMenu.get_children():
+               child.reparent(hildonMenu)
+       window.set_menu(hildonMenu)
+       gtkMenu.destroy()
+       return hildonMenu
+
+
+def _null_hildonize_menu(window, gtkMenu):
+       return gtkMenu
+
+
+try:
+       hildon.AppMenu
+       GTK_MENU_USED = False
+       IS_FREMANTLE_SUPPORTED = True
+       hildonize_menu = _fremantle_hildonize_menu
+except AttributeError:
+       GTK_MENU_USED = True
+       IS_FREMANTLE_SUPPORTED = False
+       if IS_HILDON_SUPPORTED:
+               hildonize_menu = _hildon_hildonize_menu
+       else:
+               hildonize_menu = _null_hildonize_menu
+
+
+def _hildon_set_button_auto_selectable(button):
+       button.set_theme_size(hildon.HILDON_SIZE_AUTO_HEIGHT)
+
+
+def _null_set_button_auto_selectable(button):
+       pass
+
+
+try:
+       hildon.HILDON_SIZE_AUTO_HEIGHT
+       gtk.Button.set_theme_size
+       set_button_auto_selectable = _hildon_set_button_auto_selectable
+except AttributeError:
+       set_button_auto_selectable = _null_set_button_auto_selectable
+
+
+def _hildon_set_button_finger_selectable(button):
+       button.set_theme_size(hildon.HILDON_SIZE_FINGER_HEIGHT)
+
+
+def _null_set_button_finger_selectable(button):
+       pass
+
+
+try:
+       hildon.HILDON_SIZE_FINGER_HEIGHT
+       gtk.Button.set_theme_size
+       set_button_finger_selectable = _hildon_set_button_finger_selectable
+except AttributeError:
+       set_button_finger_selectable = _null_set_button_finger_selectable
+
+
+def _hildon_set_button_thumb_selectable(button):
+       button.set_theme_size(hildon.HILDON_SIZE_THUMB_HEIGHT)
+
+
+def _null_set_button_thumb_selectable(button):
+       pass
+
+
+try:
+       hildon.HILDON_SIZE_THUMB_HEIGHT
+       gtk.Button.set_theme_size
+       set_button_thumb_selectable = _hildon_set_button_thumb_selectable
+except AttributeError:
+       set_button_thumb_selectable = _null_set_button_thumb_selectable
+
+
+def _hildon_set_cell_thumb_selectable(renderer):
+       renderer.set_property("scale", 1.5)
+
+
+def _null_set_cell_thumb_selectable(renderer):
+       pass
+
+
+if IS_HILDON_SUPPORTED:
+       set_cell_thumb_selectable = _hildon_set_cell_thumb_selectable
+else:
+       set_cell_thumb_selectable = _null_set_cell_thumb_selectable
+
+
+def _hildon_set_pix_cell_thumb_selectable(renderer):
+       renderer.set_property("stock-size", 48)
+
+
+def _null_set_pix_cell_thumb_selectable(renderer):
+       pass
+
+
+if IS_HILDON_SUPPORTED:
+       set_pix_cell_thumb_selectable = _hildon_set_pix_cell_thumb_selectable
+else:
+       set_pix_cell_thumb_selectable = _null_set_pix_cell_thumb_selectable
+
+
+def _fremantle_show_information_banner(parent, message):
+       hildon.hildon_banner_show_information(parent, "", message)
+
+
+def _hildon_show_information_banner(parent, message):
+       hildon.hildon_banner_show_information(parent, None, message)
+
+
+def _null_show_information_banner(parent, message):
+       pass
+
+
+if IS_FREMANTLE_SUPPORTED:
+       show_information_banner = _fremantle_show_information_banner
+else:
+       try:
+               hildon.hildon_banner_show_information
+               show_information_banner = _hildon_show_information_banner
+       except AttributeError:
+               show_information_banner = _null_show_information_banner
+
+
+def _fremantle_show_busy_banner_start(parent, message):
+       hildon.hildon_gtk_window_set_progress_indicator(parent, True)
+       return parent
+
+
+def _fremantle_show_busy_banner_end(banner):
+       hildon.hildon_gtk_window_set_progress_indicator(banner, False)
+
+
+def _hildon_show_busy_banner_start(parent, message):
+       return hildon.hildon_banner_show_animation(parent, None, message)
+
+
+def _hildon_show_busy_banner_end(banner):
+       banner.destroy()
+
+
+def _null_show_busy_banner_start(parent, message):
+       return None
+
+
+def _null_show_busy_banner_end(banner):
+       assert banner is None
+
+
+try:
+       hildon.hildon_gtk_window_set_progress_indicator
+       show_busy_banner_start = _fremantle_show_busy_banner_start
+       show_busy_banner_end = _fremantle_show_busy_banner_end
+except AttributeError:
+       try:
+               hildon.hildon_banner_show_animation
+               show_busy_banner_start = _hildon_show_busy_banner_start
+               show_busy_banner_end = _hildon_show_busy_banner_end
+       except AttributeError:
+               show_busy_banner_start = _null_show_busy_banner_start
+               show_busy_banner_end = _null_show_busy_banner_end
+
+
+def _hildon_hildonize_text_entry(textEntry):
+       textEntry.set_property('hildon-input-mode', 7)
+
+
+def _null_hildonize_text_entry(textEntry):
+       pass
+
+
+if IS_HILDON_SUPPORTED:
+       hildonize_text_entry = _hildon_hildonize_text_entry
+else:
+       hildonize_text_entry = _null_hildonize_text_entry
+
+
+def _hildon_window_to_portrait(window):
+       # gtk documentation is unclear whether this does a "=" or a "|="
+       flags = hildon.PORTRAIT_MODE_SUPPORT | hildon.PORTRAIT_MODE_REQUEST
+       hildon.hildon_gtk_window_set_portrait_flags(window, flags)
+
+
+def _hildon_window_to_landscape(window):
+       # gtk documentation is unclear whether this does a "=" or a "&= ~"
+       flags = hildon.PORTRAIT_MODE_SUPPORT
+       hildon.hildon_gtk_window_set_portrait_flags(window, flags)
+
+
+def _null_window_to_portrait(window):
+       pass
+
+
+def _null_window_to_landscape(window):
+       pass
+
+
+try:
+       hildon.PORTRAIT_MODE_SUPPORT
+       hildon.PORTRAIT_MODE_REQUEST
+       hildon.hildon_gtk_window_set_portrait_flags
+
+       window_to_portrait = _hildon_window_to_portrait
+       window_to_landscape = _hildon_window_to_landscape
+except AttributeError:
+       window_to_portrait = _null_window_to_portrait
+       window_to_landscape = _null_window_to_landscape
+
+
+def get_device_orientation():
+       bus = dbus.SystemBus()
+       try:
+               rawMceRequest = bus.get_object("com.nokia.mce", "/com/nokia/mce/request")
+               mceRequest = dbus.Interface(rawMceRequest, dbus_interface="com.nokia.mce.request")
+               orientation, standState, faceState, xAxis, yAxis, zAxis = mceRequest.get_device_orientation()
+       except dbus.exception.DBusException:
+               # catching for documentation purposes that when a system doesn't
+               # support this, this is what to expect
+               raise
+
+       if orientation == "":
+               return gtk.ORIENTATION_HORIZONTAL
+       elif orientation == "":
+               return gtk.ORIENTATION_VERTICAL
+       else:
+               raise RuntimeError("Unknown orientation: %s" % orientation)
+
+
+def _hildon_hildonize_password_entry(textEntry):
+       textEntry.set_property('hildon-input-mode', 7 | (1 << 29))
+
+
+def _null_hildonize_password_entry(textEntry):
+       pass
+
+
+if IS_HILDON_SUPPORTED:
+       hildonize_password_entry = _hildon_hildonize_password_entry
+else:
+       hildonize_password_entry = _null_hildonize_password_entry
+
+
+def _hildon_hildonize_combo_entry(comboEntry):
+       comboEntry.set_property('hildon-input-mode', 1 << 4)
+
+
+def _null_hildonize_combo_entry(textEntry):
+       pass
+
+
+if IS_HILDON_SUPPORTED:
+       hildonize_combo_entry = _hildon_hildonize_combo_entry
+else:
+       hildonize_combo_entry = _null_hildonize_combo_entry
+
+
+def _null_create_seekbar():
+       adjustment = gtk.Adjustment(0, 0, 101, 1, 5, 1)
+       seek = gtk.HScale(adjustment)
+       seek.set_draw_value(False)
+       return seek
+
+
+def _fremantle_create_seekbar():
+       seek = hildon.Seekbar()
+       seek.set_range(0.0, 100)
+       seek.set_draw_value(False)
+       seek.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+       return seek
+
+
+try:
+       hildon.Seekbar
+       create_seekbar = _fremantle_create_seekbar
+except AttributeError:
+       create_seekbar = _null_create_seekbar
+
+
+def _fremantle_hildonize_scrollwindow(scrolledWindow):
+       pannableWindow = hildon.PannableArea()
+
+       child = scrolledWindow.get_child()
+       scrolledWindow.remove(child)
+       pannableWindow.add(child)
+
+       parent = scrolledWindow.get_parent()
+       if parent is not None:
+               parent.remove(scrolledWindow)
+               parent.add(pannableWindow)
+
+       return pannableWindow
+
+
+def _hildon_hildonize_scrollwindow(scrolledWindow):
+       hildon.hildon_helper_set_thumb_scrollbar(scrolledWindow, True)
+       return scrolledWindow
+
+
+def _null_hildonize_scrollwindow(scrolledWindow):
+       return scrolledWindow
+
+
+try:
+       hildon.PannableArea
+       hildonize_scrollwindow = _fremantle_hildonize_scrollwindow
+       hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
+except AttributeError:
+       try:
+               hildon.hildon_helper_set_thumb_scrollbar
+               hildonize_scrollwindow = _hildon_hildonize_scrollwindow
+               hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
+       except AttributeError:
+               hildonize_scrollwindow = _null_hildonize_scrollwindow
+               hildonize_scrollwindow_with_viewport = _null_hildonize_scrollwindow
+
+
+def _hildon_request_number(parent, title, range, default):
+       spinner = hildon.NumberEditor(*range)
+       spinner.set_value(default)
+
+       dialog = gtk.Dialog(
+               title,
+               parent,
+               gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+               (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
+       )
+       dialog.set_default_response(gtk.RESPONSE_CANCEL)
+       dialog.get_child().add(spinner)
+
+       try:
+               dialog.show_all()
+               response = dialog.run()
+
+               if response == gtk.RESPONSE_OK:
+                       return spinner.get_value()
+               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+                       raise RuntimeError("User cancelled request")
+               else:
+                       raise RuntimeError("Unrecognized response %r", response)
+       finally:
+               dialog.hide()
+               dialog.destroy()
+
+
+def _null_request_number(parent, title, range, default):
+       adjustment = gtk.Adjustment(default, range[0], range[1], 1, 5, 0)
+       spinner = gtk.SpinButton(adjustment, 0, 0)
+       spinner.set_wrap(False)
+
+       dialog = gtk.Dialog(
+               title,
+               parent,
+               gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+               (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
+       )
+       dialog.set_default_response(gtk.RESPONSE_CANCEL)
+       dialog.get_child().add(spinner)
+
+       try:
+               dialog.show_all()
+               response = dialog.run()
+
+               if response == gtk.RESPONSE_OK:
+                       return spinner.get_value_as_int()
+               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+                       raise RuntimeError("User cancelled request")
+               else:
+                       raise RuntimeError("Unrecognized response %r", response)
+       finally:
+               dialog.hide()
+               dialog.destroy()
+
+
+try:
+       hildon.NumberEditor # TODO deprecated in fremantle
+       request_number = _hildon_request_number
+except AttributeError:
+       request_number = _null_request_number
+
+
+def _hildon_touch_selector(parent, title, items, defaultIndex):
+       model = gtk.ListStore(gobject.TYPE_STRING)
+       for item in items:
+               model.append((item, ))
+
+       selector = hildon.TouchSelector()
+       selector.append_text_column(model, True)
+       selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
+       selector.set_active(0, defaultIndex)
+
+       dialog = hildon.PickerDialog(parent)
+       dialog.set_selector(selector)
+
+       try:
+               dialog.show_all()
+               response = dialog.run()
+
+               if response == gtk.RESPONSE_OK:
+                       return selector.get_active(0)
+               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+                       raise RuntimeError("User cancelled request")
+               else:
+                       raise RuntimeError("Unrecognized response %r", response)
+       finally:
+               dialog.hide()
+               dialog.destroy()
+
+
+def _on_null_touch_selector_activated(treeView, path, column, dialog, pathData):
+       dialog.response(gtk.RESPONSE_OK)
+       pathData[0] = path
+
+
+def _null_touch_selector(parent, title, items, defaultIndex = -1):
+       parentSize = parent.get_size()
+
+       model = gtk.ListStore(gobject.TYPE_STRING)
+       for item in items:
+               model.append((item, ))
+
+       cell = gtk.CellRendererText()
+       set_cell_thumb_selectable(cell)
+       column = gtk.TreeViewColumn(title)
+       column.pack_start(cell, expand=True)
+       column.add_attribute(cell, "text", 0)
+
+       treeView = gtk.TreeView()
+       treeView.set_model(model)
+       treeView.append_column(column)
+       selection = treeView.get_selection()
+       selection.set_mode(gtk.SELECTION_SINGLE)
+       if 0 < defaultIndex:
+               selection.select_path((defaultIndex, ))
+
+       scrolledWin = gtk.ScrolledWindow()
+       scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+       scrolledWin.add(treeView)
+
+       dialog = gtk.Dialog(
+               title,
+               parent,
+               gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+               (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
+       )
+       dialog.set_default_response(gtk.RESPONSE_CANCEL)
+       dialog.get_child().add(scrolledWin)
+       dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
+
+       scrolledWin = hildonize_scrollwindow(scrolledWin)
+       pathData = [None]
+       treeView.connect("row-activated", _on_null_touch_selector_activated, dialog, pathData)
+
+       try:
+               dialog.show_all()
+               response = dialog.run()
+
+               if response == gtk.RESPONSE_OK:
+                       if pathData[0] is None:
+                               raise RuntimeError("No selection made")
+                       return pathData[0][0]
+               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+                       raise RuntimeError("User cancelled request")
+               else:
+                       raise RuntimeError("Unrecognized response %r", response)
+       finally:
+               dialog.hide()
+               dialog.destroy()
+
+
+try:
+       hildon.PickerDialog
+       hildon.TouchSelector
+       touch_selector = _hildon_touch_selector
+except AttributeError:
+       touch_selector = _null_touch_selector
+
+
+def _hildon_touch_selector_entry(parent, title, items, defaultItem):
+       # Got a segfault when using append_text_column with TouchSelectorEntry, so using this way
+       try:
+               selector = hildon.TouchSelectorEntry(text=True)
+       except TypeError:
+               selector = hildon.hildon_touch_selector_entry_new_text()
+       defaultIndex = -1
+       for i, item in enumerate(items):
+               selector.append_text(item)
+               if item == defaultItem:
+                       defaultIndex = i
+
+       dialog = hildon.PickerDialog(parent)
+       dialog.set_selector(selector)
+
+       if 0 < defaultIndex:
+               selector.set_active(0, defaultIndex)
+       else:
+               selector.get_entry().set_text(defaultItem)
+
+       try:
+               dialog.show_all()
+               response = dialog.run()
+       finally:
+               dialog.hide()
+
+       if response == gtk.RESPONSE_OK:
+               return selector.get_entry().get_text()
+       elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+               raise RuntimeError("User cancelled request")
+       else:
+               raise RuntimeError("Unrecognized response %r", response)
+
+
+def _on_null_touch_selector_entry_entry_changed(entry, result, selection, defaultIndex):
+       custom = entry.get_text().strip()
+       if custom:
+               result[0] = custom
+               selection.unselect_all()
+       else:
+               result[0] = None
+               selection.select_path((defaultIndex, ))
+
+
+def _on_null_touch_selector_entry_entry_activated(customEntry, dialog, result):
+       dialog.response(gtk.RESPONSE_OK)
+       result[0] = customEntry.get_text()
+
+
+def _on_null_touch_selector_entry_tree_activated(treeView, path, column, dialog, result):
+       dialog.response(gtk.RESPONSE_OK)
+       model = treeView.get_model()
+       itr = model.get_iter(path)
+       if itr is not None:
+               result[0] = model.get_value(itr, 0)
+
+
+def _null_touch_selector_entry(parent, title, items, defaultItem):
+       parentSize = parent.get_size()
+
+       model = gtk.ListStore(gobject.TYPE_STRING)
+       defaultIndex = -1
+       for i, item in enumerate(items):
+               model.append((item, ))
+               if item == defaultItem:
+                       defaultIndex = i
+
+       cell = gtk.CellRendererText()
+       set_cell_thumb_selectable(cell)
+       column = gtk.TreeViewColumn(title)
+       column.pack_start(cell, expand=True)
+       column.add_attribute(cell, "text", 0)
+
+       treeView = gtk.TreeView()
+       treeView.set_model(model)
+       treeView.append_column(column)
+       selection = treeView.get_selection()
+       selection.set_mode(gtk.SELECTION_SINGLE)
+
+       scrolledWin = gtk.ScrolledWindow()
+       scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+       scrolledWin.add(treeView)
+
+       customEntry = gtk.Entry()
+
+       layout = gtk.VBox()
+       layout.pack_start(customEntry, expand=False)
+       layout.pack_start(scrolledWin)
+
+       dialog = gtk.Dialog(
+               title,
+               parent,
+               gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+               (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
+       )
+       dialog.set_default_response(gtk.RESPONSE_CANCEL)
+       dialog.get_child().add(layout)
+       dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
+
+       scrolledWin = hildonize_scrollwindow(scrolledWin)
+
+       result = [None]
+       if 0 < defaultIndex:
+               selection.select_path((defaultIndex, ))
+               result[0] = defaultItem
+       else:
+               customEntry.set_text(defaultItem)
+
+       customEntry.connect("activate", _on_null_touch_selector_entry_entry_activated, dialog, result)
+       customEntry.connect("changed", _on_null_touch_selector_entry_entry_changed, result, selection, defaultIndex)
+       treeView.connect("row-activated", _on_null_touch_selector_entry_tree_activated, dialog, result)
+
+       try:
+               dialog.show_all()
+               response = dialog.run()
+
+               if response == gtk.RESPONSE_OK:
+                       _, itr = selection.get_selected()
+                       if itr is not None:
+                               return model.get_value(itr, 0)
+                       else:
+                               enteredText = customEntry.get_text().strip()
+                               if enteredText:
+                                       return enteredText
+                               elif result[0] is not None:
+                                       return result[0]
+                               else:
+                                       raise RuntimeError("No selection made")
+               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+                       raise RuntimeError("User cancelled request")
+               else:
+                       raise RuntimeError("Unrecognized response %r", response)
+       finally:
+               dialog.hide()
+               dialog.destroy()
+
+
+try:
+       hildon.PickerDialog
+       hildon.TouchSelectorEntry
+       touch_selector_entry = _hildon_touch_selector_entry
+except AttributeError:
+       touch_selector_entry = _null_touch_selector_entry
+
+
+if __name__ == "__main__":
+       app = get_app_class()()
+
+       label = gtk.Label("Hello World from a Label!")
+
+       win = gtk.Window()
+       win.add(label)
+       win = hildonize_window(app, win)
+       if False and IS_FREMANTLE_SUPPORTED:
+               appMenu = hildon.AppMenu()
+               for i in xrange(5):
+                       b = gtk.Button(str(i))
+                       appMenu.append(b)
+               win.set_app_menu(appMenu)
+               win.show_all()
+               appMenu.show_all()
+               gtk.main()
+       elif False:
+               print touch_selector(win, "Test", ["A", "B", "C", "D"], 2)
+       elif False:
+               print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "C")
+               print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "Blah")
+       elif False:
+               import pprint
+               name, value = "", ""
+               goodLocals = [
+                       (name, value) for (name, value) in locals().iteritems()
+                       if not name.startswith("_")
+               ]
+               pprint.pprint(goodLocals)
+       elif False:
+               import time
+               show_information_banner(win, "Hello World")
+               time.sleep(5)
+       elif False:
+               import time
+               banner = show_busy_banner_start(win, "Hello World")
+               time.sleep(5)
+               show_busy_banner_end(banner)
diff --git a/gonvert/util/io.py b/gonvert/util/io.py
new file mode 100644 (file)
index 0000000..4198f4b
--- /dev/null
@@ -0,0 +1,231 @@
+#!/usr/bin/env python
+
+
+from __future__ import with_statement
+
+import os
+import pickle
+import contextlib
+import itertools
+import codecs
+from xml.sax import saxutils
+import csv
+try:
+       import cStringIO as StringIO
+except ImportError:
+       import StringIO
+
+
+@contextlib.contextmanager
+def change_directory(directory):
+       previousDirectory = os.getcwd()
+       os.chdir(directory)
+       currentDirectory = os.getcwd()
+
+       try:
+               yield previousDirectory, currentDirectory
+       finally:
+               os.chdir(previousDirectory)
+
+
+@contextlib.contextmanager
+def pickled(filename):
+       """
+       Here is an example usage:
+       with pickled("foo.db") as p:
+               p("users", list).append(["srid", "passwd", 23])
+       """
+
+       if os.path.isfile(filename):
+               data = pickle.load(open(filename))
+       else:
+               data = {}
+
+       def getter(item, factory):
+               if item in data:
+                       return data[item]
+               else:
+                       data[item] = factory()
+                       return data[item]
+
+       yield getter
+
+       pickle.dump(data, open(filename, "w"))
+
+
+@contextlib.contextmanager
+def redirect(object_, attr, value):
+       """
+       >>> import sys
+       ... with redirect(sys, 'stdout', open('stdout', 'w')):
+       ...     print "hello"
+       ...
+       >>> print "we're back"
+       we're back
+       """
+       orig = getattr(object_, attr)
+       setattr(object_, attr, value)
+       try:
+               yield
+       finally:
+               setattr(object_, attr, orig)
+
+
+def pathsplit(path):
+       """
+       >>> pathsplit("/a/b/c")
+       ['', 'a', 'b', 'c']
+       >>> pathsplit("./plugins/builtins.ini")
+       ['.', 'plugins', 'builtins.ini']
+       """
+       pathParts = path.split(os.path.sep)
+       return pathParts
+
+
+def commonpath(l1, l2, common=None):
+       """
+       >>> commonpath(pathsplit('/a/b/c/d'), pathsplit('/a/b/c1/d1'))
+       (['', 'a', 'b'], ['c', 'd'], ['c1', 'd1'])
+       >>> commonpath(pathsplit("./plugins/"), pathsplit("./plugins/builtins.ini"))
+       (['.', 'plugins'], [''], ['builtins.ini'])
+       >>> commonpath(pathsplit("./plugins/builtins"), pathsplit("./plugins"))
+       (['.', 'plugins'], ['builtins'], [])
+       """
+       if common is None:
+               common = []
+
+       if l1 == l2:
+               return l1, [], []
+
+       for i, (leftDir, rightDir) in enumerate(zip(l1, l2)):
+               if leftDir != rightDir:
+                       return l1[0:i], l1[i:], l2[i:]
+       else:
+               if leftDir == rightDir:
+                       i += 1
+               return l1[0:i], l1[i:], l2[i:]
+
+
+def relpath(p1, p2):
+       """
+       >>> relpath('/', '/')
+       './'
+       >>> relpath('/a/b/c/d', '/')
+       '../../../../'
+       >>> relpath('/a/b/c/d', '/a/b/c1/d1')
+       '../../c1/d1'
+       >>> relpath('/a/b/c/d', '/a/b/c1/d1/')
+       '../../c1/d1'
+       >>> relpath("./plugins/builtins", "./plugins")
+       '../'
+       >>> relpath("./plugins/", "./plugins/builtins.ini")
+       'builtins.ini'
+       """
+       sourcePath = os.path.normpath(p1)
+       destPath = os.path.normpath(p2)
+
+       (common, sourceOnly, destOnly) = commonpath(pathsplit(sourcePath), pathsplit(destPath))
+       if len(sourceOnly) or len(destOnly):
+               relParts = itertools.chain(
+                       (('..' + os.sep) * len(sourceOnly), ),
+                       destOnly,
+               )
+               return os.path.join(*relParts)
+       else:
+               return "."+os.sep
+
+
+class UTF8Recoder(object):
+       """
+       Iterator that reads an encoded stream and reencodes the input to UTF-8
+       """
+       def __init__(self, f, encoding):
+               self.reader = codecs.getreader(encoding)(f)
+
+       def __iter__(self):
+               return self
+
+       def next(self):
+               return self.reader.next().encode("utf-8")
+
+
+class UnicodeReader(object):
+       """
+       A CSV reader which will iterate over lines in the CSV file "f",
+       which is encoded in the given encoding.
+       """
+
+       def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
+               f = UTF8Recoder(f, encoding)
+               self.reader = csv.reader(f, dialect=dialect, **kwds)
+
+       def next(self):
+               row = self.reader.next()
+               return [unicode(s, "utf-8") for s in row]
+
+       def __iter__(self):
+               return self
+
+class UnicodeWriter(object):
+       """
+       A CSV writer which will write rows to CSV file "f",
+       which is encoded in the given encoding.
+       """
+
+       def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
+               # Redirect output to a queue
+               self.queue = StringIO.StringIO()
+               self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
+               self.stream = f
+               self.encoder = codecs.getincrementalencoder(encoding)()
+
+       def writerow(self, row):
+               self.writer.writerow([s.encode("utf-8") for s in row])
+               # Fetch UTF-8 output from the queue ...
+               data = self.queue.getvalue()
+               data = data.decode("utf-8")
+               # ... and reencode it into the target encoding
+               data = self.encoder.encode(data)
+               # write to the target stream
+               self.stream.write(data)
+               # empty queue
+               self.queue.truncate(0)
+
+       def writerows(self, rows):
+               for row in rows:
+                       self.writerow(row)
+
+
+def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
+       # csv.py doesn't do Unicode; encode temporarily as UTF-8:
+       csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
+                                                       dialect=dialect, **kwargs)
+       for row in csv_reader:
+               # decode UTF-8 back to Unicode, cell by cell:
+               yield [unicode(cell, 'utf-8') for cell in row]
+
+
+def utf_8_encoder(unicode_csv_data):
+       for line in unicode_csv_data:
+               yield line.encode('utf-8')
+
+
+_UNESCAPE_ENTITIES = {
+ "&quot;": '"',
+ "&nbsp;": " ",
+ "&#39;": "'",
+}
+
+
+_ESCAPE_ENTITIES = dict((v, k) for (v, k) in zip(_UNESCAPE_ENTITIES.itervalues(), _UNESCAPE_ENTITIES.iterkeys()))
+del _ESCAPE_ENTITIES[" "]
+
+
+def unescape(text):
+       plain = saxutils.unescape(text, _UNESCAPE_ENTITIES)
+       return plain
+
+
+def escape(text):
+       fancy = saxutils.escape(text, _ESCAPE_ENTITIES)
+       return fancy
diff --git a/gonvert/util/linux.py b/gonvert/util/linux.py
new file mode 100644 (file)
index 0000000..4e77445
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+
+
+import os
+import logging
+
+try:
+       from xdg import BaseDirectory as _BaseDirectory
+       BaseDirectory = _BaseDirectory
+except ImportError:
+       BaseDirectory = None
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+_libc = None
+
+
+def set_process_name(name):
+       try: # change process name for killall
+               global _libc
+               if _libc is None:
+                       import ctypes
+                       _libc = ctypes.CDLL('libc.so.6')
+               _libc.prctl(15, name, 0, 0, 0)
+       except Exception, e:
+               _moduleLogger.warning('Unable to set processName: %s" % e')
+
+
+def get_new_resource(resourceType, resource, name):
+       if BaseDirectory is not None:
+               if resourceType == "data":
+                       base = BaseDirectory.xdg_data_home
+                       if base == "/usr/share/mime":
+                               # Ugly hack because somehow Maemo 4.1 seems to be set to this
+                               base = os.path.join(os.path.expanduser("~"), ".%s" % resource)
+               elif resourceType == "config":
+                       base = BaseDirectory.xdg_config_home
+               elif resourceType == "cache":
+                       base = BaseDirectory.xdg_cache_home
+               else:
+                       raise RuntimeError("Unknown type: "+resourceType)
+       else:
+               base = os.path.join(os.path.expanduser("~"), ".%s" % resource)
+
+       filePath = os.path.join(base, resource, name)
+       dirPath = os.path.dirname(filePath)
+       if not os.path.exists(dirPath):
+               # Looking before I leap to not mask errors
+               os.makedirs(dirPath)
+
+       return filePath
+
+
+def get_existing_resource(resourceType, resource, name):
+       if BaseDirectory is not None:
+               if resourceType == "data":
+                       base = BaseDirectory.xdg_data_home
+               elif resourceType == "config":
+                       base = BaseDirectory.xdg_config_home
+               elif resourceType == "cache":
+                       base = BaseDirectory.xdg_cache_home
+               else:
+                       raise RuntimeError("Unknown type: "+resourceType)
+       else:
+               base = None
+
+       if base is not None:
+               finalPath = os.path.join(base, name)
+               if os.path.exists(finalPath):
+                       return finalPath
+
+       altBase = os.path.join(os.path.expanduser("~"), ".%s" % resource)
+       finalPath = os.path.join(altBase, name)
+       if os.path.exists(finalPath):
+               return finalPath
+       else:
+               raise RuntimeError("Resource not found: %r" % ((resourceType, resource, name), ))
diff --git a/gonvert/util/misc.py b/gonvert/util/misc.py
new file mode 100644 (file)
index 0000000..9b8d88c
--- /dev/null
@@ -0,0 +1,900 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+
+import sys
+import re
+import cPickle
+
+import functools
+import contextlib
+import inspect
+
+import optparse
+import traceback
+import warnings
+import string
+
+
+class AnyData(object):
+
+       pass
+
+
+_indentationLevel = [0]
+
+
+def log_call(logger):
+
+       def log_call_decorator(func):
+
+               @functools.wraps(func)
+               def wrapper(*args, **kwds):
+                       logger.debug("%s> %s" % (" " * _indentationLevel[0], func.__name__, ))
+                       _indentationLevel[0] += 1
+                       try:
+                               return func(*args, **kwds)
+                       finally:
+                               _indentationLevel[0] -= 1
+                               logger.debug("%s< %s" % (" " * _indentationLevel[0], func.__name__, ))
+
+               return wrapper
+
+       return log_call_decorator
+
+
+def log_exception(logger):
+
+       def log_exception_decorator(func):
+
+               @functools.wraps(func)
+               def wrapper(*args, **kwds):
+                       try:
+                               return func(*args, **kwds)
+                       except Exception:
+                               logger.exception(func.__name__)
+                               raise
+
+               return wrapper
+
+       return log_exception_decorator
+
+
+def printfmt(template):
+       """
+       This hides having to create the Template object and call substitute/safe_substitute on it. For example:
+
+       >>> num = 10
+       >>> word = "spam"
+       >>> printfmt("I would like to order $num units of $word, please") #doctest: +SKIP
+       I would like to order 10 units of spam, please
+       """
+       frame = inspect.stack()[-1][0]
+       try:
+               print string.Template(template).safe_substitute(frame.f_locals)
+       finally:
+               del frame
+
+
+def is_special(name):
+       return name.startswith("__") and name.endswith("__")
+
+
+def is_private(name):
+       return name.startswith("_") and not is_special(name)
+
+
+def privatize(clsName, attributeName):
+       """
+       At runtime, make an attributeName private
+
+       Example:
+       >>> class Test(object):
+       ...     pass
+       ...
+       >>> try:
+       ...     dir(Test).index("_Test__me")
+       ...     print dir(Test)
+       ... except:
+       ...     print "Not Found"
+       Not Found
+       >>> setattr(Test, privatize(Test.__name__, "me"), "Hello World")
+       >>> try:
+       ...     dir(Test).index("_Test__me")
+       ...     print "Found"
+       ... except:
+       ...     print dir(Test)
+       0
+       Found
+       >>> print getattr(Test, obfuscate(Test.__name__, "__me"))
+       Hello World
+       >>>
+       >>> is_private(privatize(Test.__name__, "me"))
+       True
+       >>> is_special(privatize(Test.__name__, "me"))
+       False
+       """
+       return "".join(["_", clsName, "__", attributeName])
+
+
+def obfuscate(clsName, attributeName):
+       """
+       At runtime, turn a private name into the obfuscated form
+
+       Example:
+       >>> class Test(object):
+       ...     __me = "Hello World"
+       ...
+       >>> try:
+       ...     dir(Test).index("_Test__me")
+       ...     print "Found"
+       ... except:
+       ...     print dir(Test)
+       0
+       Found
+       >>> print getattr(Test, obfuscate(Test.__name__, "__me"))
+       Hello World
+       >>> is_private(obfuscate(Test.__name__, "__me"))
+       True
+       >>> is_special(obfuscate(Test.__name__, "__me"))
+       False
+       """
+       return "".join(["_", clsName, attributeName])
+
+
+class PAOptionParser(optparse.OptionParser, object):
+       """
+       >>> if __name__ == '__main__':
+       ...     #parser = PAOptionParser("My usage str")
+       ...     parser = PAOptionParser()
+       ...     parser.add_posarg("Foo", help="Foo usage")
+       ...     parser.add_posarg("Bar", dest="bar_dest")
+       ...     parser.add_posarg("Language", dest='tr_type', type="choice", choices=("Python", "Other"))
+       ...     parser.add_option('--stocksym', dest='symbol')
+       ...     values, args = parser.parse_args()
+       ...     print values, args
+       ...
+
+       python mycp.py  -h
+       python mycp.py
+       python mycp.py  foo
+       python mycp.py  foo bar
+
+       python mycp.py foo bar lava
+       Usage: pa.py <Foo> <Bar> <Language> [options]
+
+       Positional Arguments:
+       Foo: Foo usage
+       Bar:
+       Language:
+
+       pa.py: error: option --Language: invalid choice: 'lava' (choose from 'Python', 'Other'
+       """
+
+       def __init__(self, *args, **kw):
+               self.posargs = []
+               super(PAOptionParser, self).__init__(*args, **kw)
+
+       def add_posarg(self, *args, **kw):
+               pa_help = kw.get("help", "")
+               kw["help"] = optparse.SUPPRESS_HELP
+               o = self.add_option("--%s" % args[0], *args[1:], **kw)
+               self.posargs.append((args[0], pa_help))
+
+       def get_usage(self, *args, **kwargs):
+               params = (' '.join(["<%s>" % arg[0] for arg in self.posargs]), '\n '.join(["%s: %s" % (arg) for arg in self.posargs]))
+               self.usage = "%%prog %s [options]\n\nPositional Arguments:\n %s" % params
+               return super(PAOptionParser, self).get_usage(*args, **kwargs)
+
+       def parse_args(self, *args, **kwargs):
+               args = sys.argv[1:]
+               args0 = []
+               for p, v in zip(self.posargs, args):
+                       args0.append("--%s" % p[0])
+                       args0.append(v)
+               args = args0 + args
+               options, args = super(PAOptionParser, self).parse_args(args, **kwargs)
+               if len(args) < len(self.posargs):
+                       msg = 'Missing value(s) for "%s"\n' % ", ".join([arg[0] for arg in self.posargs][len(args):])
+                       self.error(msg)
+               return options, args
+
+
+def explicitly(name, stackadd=0):
+       """
+       This is an alias for adding to '__all__'.  Less error-prone than using
+       __all__ itself, since setting __all__ directly is prone to stomping on
+       things implicitly exported via L{alias}.
+
+       @note Taken from PyExport (which could turn out pretty cool):
+       @li @a http://codebrowse.launchpad.net/~glyph/
+       @li @a http://glyf.livejournal.com/74356.html
+       """
+       packageVars = sys._getframe(1+stackadd).f_locals
+       globalAll = packageVars.setdefault('__all__', [])
+       globalAll.append(name)
+
+
+def public(thunk):
+       """
+       This is a decorator, for convenience.  Rather than typing the name of your
+       function twice, you can decorate a function with this.
+
+       To be real, @public would need to work on methods as well, which gets into
+       supporting types...
+
+       @note Taken from PyExport (which could turn out pretty cool):
+       @li @a http://codebrowse.launchpad.net/~glyph/
+       @li @a http://glyf.livejournal.com/74356.html
+       """
+       explicitly(thunk.__name__, 1)
+       return thunk
+
+
+def _append_docstring(obj, message):
+       if obj.__doc__ is None:
+               obj.__doc__ = message
+       else:
+               obj.__doc__ += message
+
+
+def validate_decorator(decorator):
+
+       def simple(x):
+               return x
+
+       f = simple
+       f.__name__ = "name"
+       f.__doc__ = "doc"
+       f.__dict__["member"] = True
+
+       g = decorator(f)
+
+       if f.__name__ != g.__name__:
+               print f.__name__, "!=", g.__name__
+
+       if g.__doc__ is None:
+               print decorator.__name__, "has no doc string"
+       elif not g.__doc__.startswith(f.__doc__):
+               print g.__doc__, "didn't start with", f.__doc__
+
+       if not ("member" in g.__dict__ and g.__dict__["member"]):
+               print "'member' not in ", g.__dict__
+
+
+def deprecated_api(func):
+       """
+       This is a decorator which can be used to mark functions
+       as deprecated. It will result in a warning being emitted
+       when the function is used.
+
+       >>> validate_decorator(deprecated_api)
+       """
+
+       @functools.wraps(func)
+       def newFunc(*args, **kwargs):
+               warnings.warn("Call to deprecated function %s." % func.__name__, category=DeprecationWarning)
+               return func(*args, **kwargs)
+
+       _append_docstring(newFunc, "\n@deprecated")
+       return newFunc
+
+
+def unstable_api(func):
+       """
+       This is a decorator which can be used to mark functions
+       as deprecated. It will result in a warning being emitted
+       when the function is used.
+
+       >>> validate_decorator(unstable_api)
+       """
+
+       @functools.wraps(func)
+       def newFunc(*args, **kwargs):
+               warnings.warn("Call to unstable API function %s." % func.__name__, category=FutureWarning)
+               return func(*args, **kwargs)
+       _append_docstring(newFunc, "\n@unstable")
+       return newFunc
+
+
+def enabled(func):
+       """
+       This decorator doesn't add any behavior
+
+       >>> validate_decorator(enabled)
+       """
+       return func
+
+
+def disabled(func):
+       """
+       This decorator disables the provided function, and does nothing
+
+       >>> validate_decorator(disabled)
+       """
+
+       @functools.wraps(func)
+       def emptyFunc(*args, **kargs):
+               pass
+       _append_docstring(emptyFunc, "\n@note Temporarily Disabled")
+       return emptyFunc
+
+
+def metadata(document=True, **kwds):
+       """
+       >>> validate_decorator(metadata(author="Ed"))
+       """
+
+       def decorate(func):
+               for k, v in kwds.iteritems():
+                       setattr(func, k, v)
+                       if document:
+                               _append_docstring(func, "\n@"+k+" "+v)
+               return func
+       return decorate
+
+
+def prop(func):
+       """Function decorator for defining property attributes
+
+       The decorated function is expected to return a dictionary
+       containing one or more of the following pairs:
+               fget - function for getting attribute value
+               fset - function for setting attribute value
+               fdel - function for deleting attribute
+       This can be conveniently constructed by the locals() builtin
+       function; see:
+       http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183
+       @author http://kbyanc.blogspot.com/2007/06/python-property-attribute-tricks.html
+
+       Example:
+       >>> #Due to transformation from function to property, does not need to be validated
+       >>> #validate_decorator(prop)
+       >>> class MyExampleClass(object):
+       ...     @prop
+       ...     def foo():
+       ...             "The foo property attribute's doc-string"
+       ...             def fget(self):
+       ...                     print "GET"
+       ...                     return self._foo
+       ...             def fset(self, value):
+       ...                     print "SET"
+       ...                     self._foo = value
+       ...             return locals()
+       ...
+       >>> me = MyExampleClass()
+       >>> me.foo = 10
+       SET
+       >>> print me.foo
+       GET
+       10
+       """
+       return property(doc=func.__doc__, **func())
+
+
+def print_handler(e):
+       """
+       @see ExpHandler
+       """
+       print "%s: %s" % (type(e).__name__, e)
+
+
+def print_ignore(e):
+       """
+       @see ExpHandler
+       """
+       print 'Ignoring %s exception: %s' % (type(e).__name__, e)
+
+
+def print_traceback(e):
+       """
+       @see ExpHandler
+       """
+       #print sys.exc_info()
+       traceback.print_exc(file=sys.stdout)
+
+
+def ExpHandler(handler = print_handler, *exceptions):
+       """
+       An exception handling idiom using decorators
+       Examples
+       Specify exceptions in order, first one is handled first
+       last one last.
+
+       >>> validate_decorator(ExpHandler())
+       >>> @ExpHandler(print_ignore, ZeroDivisionError)
+       ... @ExpHandler(None, AttributeError, ValueError)
+       ... def f1():
+       ...     1/0
+       >>> @ExpHandler(print_traceback, ZeroDivisionError)
+       ... def f2():
+       ...     1/0
+       >>> @ExpHandler()
+       ... def f3(*pargs):
+       ...     l = pargs
+       ...     return l[10]
+       >>> @ExpHandler(print_traceback, ZeroDivisionError)
+       ... def f4():
+       ...     return 1
+       >>>
+       >>>
+       >>> f1()
+       Ignoring ZeroDivisionError exception: integer division or modulo by zero
+       >>> f2() # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+       Traceback (most recent call last):
+       ...
+       ZeroDivisionError: integer division or modulo by zero
+       >>> f3()
+       IndexError: tuple index out of range
+       >>> f4()
+       1
+       """
+
+       def wrapper(f):
+               localExceptions = exceptions
+               if not localExceptions:
+                       localExceptions = [Exception]
+               t = [(ex, handler) for ex in localExceptions]
+               t.reverse()
+
+               def newfunc(t, *args, **kwargs):
+                       ex, handler = t[0]
+                       try:
+                               if len(t) == 1:
+                                       return f(*args, **kwargs)
+                               else:
+                                       #Recurse for embedded try/excepts
+                                       dec_func = functools.partial(newfunc, t[1:])
+                                       dec_func = functools.update_wrapper(dec_func, f)
+                                       return dec_func(*args, **kwargs)
+                       except ex, e:
+                               return handler(e)
+
+               dec_func = functools.partial(newfunc, t)
+               dec_func = functools.update_wrapper(dec_func, f)
+               return dec_func
+       return wrapper
+
+
+def into_debugger(func):
+       """
+       >>> validate_decorator(into_debugger)
+       """
+
+       @functools.wraps(func)
+       def newFunc(*args, **kwargs):
+               try:
+                       return func(*args, **kwargs)
+               except:
+                       import pdb
+                       pdb.post_mortem()
+
+       return newFunc
+
+
+class bindclass(object):
+       """
+       >>> validate_decorator(bindclass)
+       >>> class Foo(BoundObject):
+       ...      @bindclass
+       ...      def foo(this_class, self):
+       ...              return this_class, self
+       ...
+       >>> class Bar(Foo):
+       ...      @bindclass
+       ...      def bar(this_class, self):
+       ...              return this_class, self
+       ...
+       >>> f = Foo()
+       >>> b = Bar()
+       >>>
+       >>> f.foo() # doctest: +ELLIPSIS
+       (<class '...Foo'>, <...Foo object at ...>)
+       >>> b.foo() # doctest: +ELLIPSIS
+       (<class '...Foo'>, <...Bar object at ...>)
+       >>> b.bar() # doctest: +ELLIPSIS
+       (<class '...Bar'>, <...Bar object at ...>)
+       """
+
+       def __init__(self, f):
+               self.f = f
+               self.__name__ = f.__name__
+               self.__doc__ = f.__doc__
+               self.__dict__.update(f.__dict__)
+               self.m = None
+
+       def bind(self, cls, attr):
+
+               def bound_m(*args, **kwargs):
+                       return self.f(cls, *args, **kwargs)
+               bound_m.__name__ = attr
+               self.m = bound_m
+
+       def __get__(self, obj, objtype=None):
+               return self.m.__get__(obj, objtype)
+
+
+class ClassBindingSupport(type):
+       "@see bindclass"
+
+       def __init__(mcs, name, bases, attrs):
+               type.__init__(mcs, name, bases, attrs)
+               for attr, val in attrs.iteritems():
+                       if isinstance(val, bindclass):
+                               val.bind(mcs, attr)
+
+
+class BoundObject(object):
+       "@see bindclass"
+       __metaclass__ = ClassBindingSupport
+
+
+def bindfunction(f):
+       """
+       >>> validate_decorator(bindfunction)
+       >>> @bindfunction
+       ... def factorial(thisfunction, n):
+       ...      # Within this function the name 'thisfunction' refers to the factorial
+       ...      # function(with only one argument), even after 'factorial' is bound
+       ...      # to another object
+       ...      if n > 0:
+       ...              return n * thisfunction(n - 1)
+       ...      else:
+       ...              return 1
+       ...
+       >>> factorial(3)
+       6
+       """
+
+       @functools.wraps(f)
+       def bound_f(*args, **kwargs):
+               return f(bound_f, *args, **kwargs)
+       return bound_f
+
+
+class Memoize(object):
+       """
+       Memoize(fn) - an instance which acts like fn but memoizes its arguments
+       Will only work on functions with non-mutable arguments
+       @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52201
+
+       >>> validate_decorator(Memoize)
+       """
+
+       def __init__(self, fn):
+               self.fn = fn
+               self.__name__ = fn.__name__
+               self.__doc__ = fn.__doc__
+               self.__dict__.update(fn.__dict__)
+               self.memo = {}
+
+       def __call__(self, *args):
+               if args not in self.memo:
+                       self.memo[args] = self.fn(*args)
+               return self.memo[args]
+
+
+class MemoizeMutable(object):
+       """Memoize(fn) - an instance which acts like fn but memoizes its arguments
+       Will work on functions with mutable arguments(slower than Memoize)
+       @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52201
+
+       >>> validate_decorator(MemoizeMutable)
+       """
+
+       def __init__(self, fn):
+               self.fn = fn
+               self.__name__ = fn.__name__
+               self.__doc__ = fn.__doc__
+               self.__dict__.update(fn.__dict__)
+               self.memo = {}
+
+       def __call__(self, *args, **kw):
+               text = cPickle.dumps((args, kw))
+               if text not in self.memo:
+                       self.memo[text] = self.fn(*args, **kw)
+               return self.memo[text]
+
+
+callTraceIndentationLevel = 0
+
+
+def call_trace(f):
+       """
+       Synchronization decorator.
+
+       >>> validate_decorator(call_trace)
+       >>> @call_trace
+       ... def a(a, b, c):
+       ...     pass
+       >>> a(1, 2, c=3)
+       Entering a((1, 2), {'c': 3})
+       Exiting a((1, 2), {'c': 3})
+       """
+
+       @functools.wraps(f)
+       def verboseTrace(*args, **kw):
+               global callTraceIndentationLevel
+
+               print "%sEntering %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
+               callTraceIndentationLevel += 1
+               try:
+                       result = f(*args, **kw)
+               except:
+                       callTraceIndentationLevel -= 1
+                       print "%sException %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
+                       raise
+               callTraceIndentationLevel -= 1
+               print "%sExiting %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
+               return result
+
+       @functools.wraps(f)
+       def smallTrace(*args, **kw):
+               global callTraceIndentationLevel
+
+               print "%sEntering %s" % ("\t"*callTraceIndentationLevel, f.__name__)
+               callTraceIndentationLevel += 1
+               try:
+                       result = f(*args, **kw)
+               except:
+                       callTraceIndentationLevel -= 1
+                       print "%sException %s" % ("\t"*callTraceIndentationLevel, f.__name__)
+                       raise
+               callTraceIndentationLevel -= 1
+               print "%sExiting %s" % ("\t"*callTraceIndentationLevel, f.__name__)
+               return result
+
+       #return smallTrace
+       return verboseTrace
+
+
+@contextlib.contextmanager
+def nested_break():
+       """
+       >>> with nested_break() as mylabel:
+       ...     for i in xrange(3):
+       ...             print "Outer", i
+       ...             for j in xrange(3):
+       ...                     if i == 2: raise mylabel
+       ...                     if j == 2: break
+       ...                     print "Inner", j
+       ...             print "more processing"
+       Outer 0
+       Inner 0
+       Inner 1
+       Outer 1
+       Inner 0
+       Inner 1
+       Outer 2
+       """
+
+       class NestedBreakException(Exception):
+               pass
+
+       try:
+               yield NestedBreakException
+       except NestedBreakException:
+               pass
+
+
+@contextlib.contextmanager
+def lexical_scope(*args):
+       """
+       @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/520586
+       Example:
+       >>> b = 0
+       >>> with lexical_scope(1) as (a):
+       ...     print a
+       ...
+       1
+       >>> with lexical_scope(1,2,3) as (a,b,c):
+       ...     print a,b,c
+       ...
+       1 2 3
+       >>> with lexical_scope():
+       ...     d = 10
+       ...     def foo():
+       ...             pass
+       ...
+       >>> print b
+       2
+       """
+
+       frame = inspect.currentframe().f_back.f_back
+       saved = frame.f_locals.keys()
+       try:
+               if not args:
+                       yield
+               elif len(args) == 1:
+                       yield args[0]
+               else:
+                       yield args
+       finally:
+               f_locals = frame.f_locals
+               for key in (x for x in f_locals.keys() if x not in saved):
+                       del f_locals[key]
+               del frame
+
+
+def normalize_number(prettynumber):
+       """
+       function to take a phone number and strip out all non-numeric
+       characters
+
+       >>> normalize_number("+012-(345)-678-90")
+       '+01234567890'
+       >>> normalize_number("1-(345)-678-9000")
+       '+13456789000'
+       >>> normalize_number("+1-(345)-678-9000")
+       '+13456789000'
+       """
+       uglynumber = re.sub('[^0-9+]', '', prettynumber)
+       if uglynumber.startswith("+"):
+               pass
+       elif uglynumber.startswith("1"):
+               uglynumber = "+"+uglynumber
+       elif 10 <= len(uglynumber):
+               assert uglynumber[0] not in ("+", "1"), "Number format confusing"
+               uglynumber = "+1"+uglynumber
+       else:
+               pass
+
+       return uglynumber
+
+
+_VALIDATE_RE = re.compile("^\+?[0-9]{10,}$")
+
+
+def is_valid_number(number):
+       """
+       @returns If This number be called ( syntax validation only )
+       """
+       return _VALIDATE_RE.match(number) is not None
+
+
+def make_ugly(prettynumber):
+       """
+       function to take a phone number and strip out all non-numeric
+       characters
+
+       >>> make_ugly("+012-(345)-678-90")
+       '+01234567890'
+       """
+       return normalize_number(prettynumber)
+
+
+def _make_pretty_with_areacode(phonenumber):
+       prettynumber = "(%s)" % (phonenumber[0:3], )
+       if 3 < len(phonenumber):
+               prettynumber += " %s" % (phonenumber[3:6], )
+               if 6 < len(phonenumber):
+                       prettynumber += "-%s" % (phonenumber[6:], )
+       return prettynumber
+
+
+def _make_pretty_local(phonenumber):
+       prettynumber = "%s" % (phonenumber[0:3], )
+       if 3 < len(phonenumber):
+               prettynumber += "-%s" % (phonenumber[3:], )
+       return prettynumber
+
+
+def _make_pretty_international(phonenumber):
+       prettynumber = phonenumber
+       if phonenumber.startswith("1"):
+               prettynumber = "1 "
+               prettynumber += _make_pretty_with_areacode(phonenumber[1:])
+       return prettynumber
+
+
+def make_pretty(phonenumber):
+       """
+       Function to take a phone number and return the pretty version
+       pretty numbers:
+               if phonenumber begins with 0:
+                       ...-(...)-...-....
+               if phonenumber begins with 1: ( for gizmo callback numbers )
+                       1 (...)-...-....
+               if phonenumber is 13 digits:
+                       (...)-...-....
+               if phonenumber is 10 digits:
+                       ...-....
+       >>> make_pretty("12")
+       '12'
+       >>> make_pretty("1234567")
+       '123-4567'
+       >>> make_pretty("2345678901")
+       '+1 (234) 567-8901'
+       >>> make_pretty("12345678901")
+       '+1 (234) 567-8901'
+       >>> make_pretty("01234567890")
+       '+012 (345) 678-90'
+       >>> make_pretty("+01234567890")
+       '+012 (345) 678-90'
+       >>> make_pretty("+12")
+       '+1 (2)'
+       >>> make_pretty("+123")
+       '+1 (23)'
+       >>> make_pretty("+1234")
+       '+1 (234)'
+       """
+       if phonenumber is None or phonenumber == "":
+               return ""
+
+       phonenumber = normalize_number(phonenumber)
+
+       if phonenumber == "":
+               return ""
+       elif phonenumber[0] == "+":
+               prettynumber = _make_pretty_international(phonenumber[1:])
+               if not prettynumber.startswith("+"):
+                       prettynumber = "+"+prettynumber
+       elif 8 < len(phonenumber) and phonenumber[0] in ("1", ):
+               prettynumber = _make_pretty_international(phonenumber)
+       elif 7 < len(phonenumber):
+               prettynumber = _make_pretty_with_areacode(phonenumber)
+       elif 3 < len(phonenumber):
+               prettynumber = _make_pretty_local(phonenumber)
+       else:
+               prettynumber = phonenumber
+       return prettynumber.strip()
+
+
+def similar_ugly_numbers(lhs, rhs):
+       return (
+               lhs == rhs or
+               lhs[1:] == rhs and lhs.startswith("1") or
+               lhs[2:] == rhs and lhs.startswith("+1") or
+               lhs == rhs[1:] and rhs.startswith("1") or
+               lhs == rhs[2:] and rhs.startswith("+1")
+       )
+
+
+def abbrev_relative_date(date):
+       """
+       >>> abbrev_relative_date("42 hours ago")
+       '42 h'
+       >>> abbrev_relative_date("2 days ago")
+       '2 d'
+       >>> abbrev_relative_date("4 weeks ago")
+       '4 w'
+       """
+       parts = date.split(" ")
+       return "%s %s" % (parts[0], parts[1][0])
+
+
+def parse_version(versionText):
+       """
+       >>> parse_version("0.5.2")
+       [0, 5, 2]
+       """
+       return [
+               int(number)
+               for number in versionText.split(".")
+       ]
+
+
+def compare_versions(leftParsedVersion, rightParsedVersion):
+       """
+       >>> compare_versions([0, 1, 2], [0, 1, 2])
+       0
+       >>> compare_versions([0, 1, 2], [0, 1, 3])
+       -1
+       >>> compare_versions([0, 1, 2], [0, 2, 2])
+       -1
+       >>> compare_versions([0, 1, 2], [1, 1, 2])
+       -1
+       >>> compare_versions([0, 1, 3], [0, 1, 2])
+       1
+       >>> compare_versions([0, 2, 2], [0, 1, 2])
+       1
+       >>> compare_versions([1, 1, 2], [0, 1, 2])
+       1
+       """
+       for left, right in zip(leftParsedVersion, rightParsedVersion):
+               if left < right:
+                       return -1
+               elif right < left:
+                       return 1
+       else:
+               return 0
diff --git a/gonvert/util/overloading.py b/gonvert/util/overloading.py
new file mode 100644 (file)
index 0000000..89cb738
--- /dev/null
@@ -0,0 +1,256 @@
+#!/usr/bin/env python
+import new
+
+# Make the environment more like Python 3.0
+__metaclass__ = type
+from itertools import izip as zip
+import textwrap
+import inspect
+
+
+__all__ = [
+       "AnyType",
+       "overloaded"
+]
+
+
+AnyType = object
+
+
+class overloaded:
+       """
+       Dynamically overloaded functions.
+
+       This is an implementation of (dynamically, or run-time) overloaded
+       functions; also known as generic functions or multi-methods.
+
+       The dispatch algorithm uses the types of all argument for dispatch,
+       similar to (compile-time) overloaded functions or methods in C++ and
+       Java.
+
+       Most of the complexity in the algorithm comes from the need to support
+       subclasses in call signatures.  For example, if an function is
+       registered for a signature (T1, T2), then a call with a signature (S1,
+       S2) is acceptable, assuming that S1 is a subclass of T1, S2 a subclass
+       of T2, and there are no other more specific matches (see below).
+
+       If there are multiple matches and one of those doesn't *dominate* all
+       others, the match is deemed ambiguous and an exception is raised.  A
+       subtlety here: if, after removing the dominated matches, there are
+       still multiple matches left, but they all map to the same function,
+       then the match is not deemed ambiguous and that function is used.
+       Read the method find_func() below for details.
+
+       @note Python 2.5 is required due to the use of predicates any() and all().
+       @note only supports positional arguments
+
+       @author http://www.artima.com/weblogs/viewpost.jsp?thread=155514
+
+       >>> import misc
+       >>> misc.validate_decorator (overloaded)
+       >>>
+       >>>
+       >>>
+       >>>
+       >>> #################
+       >>> #Basics, with reusing names and without
+       >>> @overloaded
+       ... def foo(x):
+       ...     "prints x"
+       ...     print x
+       ...
+       >>> @foo.register(int)
+       ... def foo(x):
+       ...     "prints the hex representation of x"
+       ...     print hex(x)
+       ...
+       >>> from types import DictType
+       >>> @foo.register(DictType)
+       ... def foo_dict(x):
+       ...     "prints the keys of x"
+       ...     print [k for k in x.iterkeys()]
+       ...
+       >>> #combines all of the doc strings to help keep track of the specializations
+       >>> foo.__doc__  # doctest: +ELLIPSIS
+       "prints x\\n\\n...overloading.foo (<type 'int'>):\\n\\tprints the hex representation of x\\n\\n...overloading.foo_dict (<type 'dict'>):\\n\\tprints the keys of x"
+       >>> foo ("text")
+       text
+       >>> foo (10) #calling the specialized foo
+       0xa
+       >>> foo ({3:5, 6:7}) #calling the specialization foo_dict
+       [3, 6]
+       >>> foo_dict ({3:5, 6:7}) #with using a unique name, you still have the option of calling the function directly
+       [3, 6]
+       >>>
+       >>>
+       >>>
+       >>>
+       >>> #################
+       >>> #Multiple arguments, accessing the default, and function finding
+       >>> @overloaded
+       ... def two_arg (x, y):
+       ...     print x,y
+       ...
+       >>> @two_arg.register(int, int)
+       ... def two_arg_int_int (x, y):
+       ...     print hex(x), hex(y)
+       ...
+       >>> @two_arg.register(float, int)
+       ... def two_arg_float_int (x, y):
+       ...     print x, hex(y)
+       ...
+       >>> @two_arg.register(int, float)
+       ... def two_arg_int_float (x, y):
+       ...     print hex(x), y
+       ...
+       >>> two_arg.__doc__ # doctest: +ELLIPSIS
+       "...overloading.two_arg_int_int (<type 'int'>, <type 'int'>):\\n\\n...overloading.two_arg_float_int (<type 'float'>, <type 'int'>):\\n\\n...overloading.two_arg_int_float (<type 'int'>, <type 'float'>):"
+       >>> two_arg(9, 10)
+       0x9 0xa
+       >>> two_arg(9.0, 10)
+       9.0 0xa
+       >>> two_arg(15, 16.0)
+       0xf 16.0
+       >>> two_arg.default_func(9, 10)
+       9 10
+       >>> two_arg.find_func ((int, float)) == two_arg_int_float
+       True
+       >>> (int, float) in two_arg
+       True
+       >>> (str, int) in two_arg
+       False
+       >>>
+       >>>
+       >>>
+       >>> #################
+       >>> #wildcard
+       >>> @two_arg.register(AnyType, str)
+       ... def two_arg_any_str (x, y):
+       ...     print x, y.lower()
+       ...
+       >>> two_arg("Hello", "World")
+       Hello world
+       >>> two_arg(500, "World")
+       500 world
+       """
+
+       def __init__(self, default_func):
+               # Decorator to declare new overloaded function.
+               self.registry = {}
+               self.cache = {}
+               self.default_func = default_func
+               self.__name__ = self.default_func.__name__
+               self.__doc__ = self.default_func.__doc__
+               self.__dict__.update (self.default_func.__dict__)
+
+       def __get__(self, obj, type=None):
+               if obj is None:
+                       return self
+               return new.instancemethod(self, obj)
+
+       def register(self, *types):
+               """
+               Decorator to register an implementation for a specific set of types.
+
+               .register(t1, t2)(f) is equivalent to .register_func((t1, t2), f).
+               """
+
+               def helper(func):
+                       self.register_func(types, func)
+
+                       originalDoc = self.__doc__ if self.__doc__ is not None else ""
+                       typeNames = ", ".join ([str(type) for type in types])
+                       typeNames = "".join ([func.__module__+".", func.__name__, " (", typeNames, "):"])
+                       overloadedDoc = ""
+                       if func.__doc__ is not None:
+                               overloadedDoc = textwrap.fill (func.__doc__, width=60, initial_indent="\t", subsequent_indent="\t")
+                       self.__doc__ = "\n".join ([originalDoc, "", typeNames, overloadedDoc]).strip()
+
+                       new_func = func
+
+                       #Masking the function, so we want to take on its traits
+                       if func.__name__ == self.__name__:
+                               self.__dict__.update (func.__dict__)
+                               new_func = self
+                       return new_func
+
+               return helper
+
+       def register_func(self, types, func):
+               """Helper to register an implementation."""
+               self.registry[tuple(types)] = func
+               self.cache = {} # Clear the cache (later we can optimize this).
+
+       def __call__(self, *args):
+               """Call the overloaded function."""
+               types = tuple(map(type, args))
+               func = self.cache.get(types)
+               if func is None:
+                       self.cache[types] = func = self.find_func(types)
+               return func(*args)
+
+       def __contains__ (self, types):
+               return self.find_func(types) is not self.default_func
+
+       def find_func(self, types):
+               """Find the appropriate overloaded function; don't call it.
+
+               @note This won't work for old-style classes or classes without __mro__
+               """
+               func = self.registry.get(types)
+               if func is not None:
+                       # Easy case -- direct hit in registry.
+                       return func
+
+               # Phillip Eby suggests to use issubclass() instead of __mro__.
+               # There are advantages and disadvantages.
+
+               # I can't help myself -- this is going to be intense functional code.
+               # Find all possible candidate signatures.
+               mros = tuple(inspect.getmro(t) for t in types)
+               n = len(mros)
+               candidates = [sig for sig in self.registry
+                               if len(sig) == n and
+                                       all(t in mro for t, mro in zip(sig, mros))]
+
+               if not candidates:
+                       # No match at all -- use the default function.
+                       return self.default_func
+               elif len(candidates) == 1:
+                       # Unique match -- that's an easy case.
+                       return self.registry[candidates[0]]
+
+               # More than one match -- weed out the subordinate ones.
+
+               def dominates(dom, sub,
+                               orders=tuple(dict((t, i) for i, t in enumerate(mro))
+                                                       for mro in mros)):
+                       # Predicate to decide whether dom strictly dominates sub.
+                       # Strict domination is defined as domination without equality.
+                       # The arguments dom and sub are type tuples of equal length.
+                       # The orders argument is a precomputed auxiliary data structure
+                       # giving dicts of ordering information corresponding to the
+                       # positions in the type tuples.
+                       # A type d dominates a type s iff order[d] <= order[s].
+                       # A type tuple (d1, d2, ...) dominates a type tuple of equal length
+                       # (s1, s2, ...) iff d1 dominates s1, d2 dominates s2, etc.
+                       if dom is sub:
+                               return False
+                       return all(order[d] <= order[s] for d, s, order in zip(dom, sub, orders))
+
+               # I suppose I could inline dominates() but it wouldn't get any clearer.
+               candidates = [cand
+                               for cand in candidates
+                                       if not any(dominates(dom, cand) for dom in candidates)]
+               if len(candidates) == 1:
+                       # There's exactly one candidate left.
+                       return self.registry[candidates[0]]
+
+               # Perhaps these multiple candidates all have the same implementation?
+               funcs = set(self.registry[cand] for cand in candidates)
+               if len(funcs) == 1:
+                       return funcs.pop()
+
+               # No, the situation is irreducibly ambiguous.
+               raise TypeError("ambigous call; types=%r; candidates=%r" %
+                                               (types, candidates))
diff --git a/gonvert/util/qml_utils.py b/gonvert/util/qml_utils.py
new file mode 100644 (file)
index 0000000..fbbca93
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+
+"""
+QML Tips:
+       Large images:
+               QML asynchronous = true; cache = false; [1]
+       Insert properties at top of element declarations [1]
+       Non-visible items: set opacity to 0 [2]
+       Use Loader [1]
+       Keep QML files small [1]
+
+[1] http://sf2011.meego.com/program/sessions/performance-tips-and-tricks-qtqml-applications-0
+[2] http://doc.qt.nokia.com/4.7/qdeclarativeperformance.html
+"""
+
+from __future__ import with_statement
+from __future__ import division
+
+import logging
+
+import util.qt_compat as qt_compat
+QtCore = qt_compat.QtCore
+QtGui = qt_compat.import_module("QtGui")
+QtDeclarative = qt_compat.import_module("QtDeclarative")
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class DeclarativeView(QtDeclarative.QDeclarativeView):
+
+       def __init__(self):
+               QtDeclarative.QDeclarativeView.__init__(self)
+
+       closing = qt_compat.Signal()
+
+       def closeEvent(self, event):
+               self.closing.emit()
+               event.ignore()
+
+
+def disable_default_window_painting(view):
+       """
+       See http://doc.qt.nokia.com/4.7-snapshot/qdeclarativeperformance.html
+       """
+       view.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
+       view.setAttribute(QtCore.Qt.WA_NoSystemBackground)
+       view.viewport().setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
+       view.viewport().setAttribute(QtCore.Qt.WA_NoSystemBackground)
+
+
+if __name__ == "__main__":
+       pass
+
diff --git a/gonvert/util/qore_utils.py b/gonvert/util/qore_utils.py
new file mode 100644 (file)
index 0000000..8f085ac
--- /dev/null
@@ -0,0 +1,422 @@
+#!/usr/bin/env python
+
+import contextlib
+import logging
+
+import qt_compat
+QtCore = qt_compat.QtCore
+
+import misc
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class QThread44(QtCore.QThread):
+       """
+       This is to imitate QThread in Qt 4.4+ for when running on older version
+       See http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong
+       (On Lucid I have Qt 4.7 and this is still an issue)
+       """
+
+       def __init__(self, parent = None):
+               QtCore.QThread.__init__(self, parent)
+
+       def run(self):
+               self.exec_()
+
+
+class _WorkerThread(QtCore.QObject):
+
+       _taskComplete  = qt_compat.Signal(object)
+
+       def __init__(self, futureThread):
+               QtCore.QObject.__init__(self)
+               self._futureThread = futureThread
+               self._futureThread._addTask.connect(self._on_task_added)
+               self._taskComplete.connect(self._futureThread._on_task_complete)
+
+       @qt_compat.Slot(object)
+       def _on_task_added(self, task):
+               self.__on_task_added(task)
+
+       @misc.log_exception(_moduleLogger)
+       def __on_task_added(self, task):
+               if not self._futureThread._isRunning:
+                       _moduleLogger.error("Dropping task")
+
+               func, args, kwds, on_success, on_error = task
+
+               try:
+                       result = func(*args, **kwds)
+                       isError = False
+               except Exception, e:
+                       _moduleLogger.error("Error, passing it back to the main thread")
+                       result = e
+                       isError = True
+
+               taskResult = on_success, on_error, isError, result
+               self._taskComplete.emit(taskResult)
+
+
+class FutureThread(QtCore.QObject):
+
+       _addTask = qt_compat.Signal(object)
+
+       def __init__(self):
+               QtCore.QObject.__init__(self)
+               self._thread = QThread44()
+               self._isRunning = False
+               self._worker = _WorkerThread(self)
+               self._worker.moveToThread(self._thread)
+
+       def start(self):
+               self._thread.start()
+               self._isRunning = True
+
+       def stop(self):
+               self._isRunning = False
+               self._thread.quit()
+
+       def add_task(self, func, args, kwds, on_success, on_error):
+               assert self._isRunning, "Task queue not started"
+               task = func, args, kwds, on_success, on_error
+               self._addTask.emit(task)
+
+       @qt_compat.Slot(object)
+       def _on_task_complete(self, taskResult):
+               self.__on_task_complete(taskResult)
+
+       @misc.log_exception(_moduleLogger)
+       def __on_task_complete(self, taskResult):
+               on_success, on_error, isError, result = taskResult
+               if not self._isRunning:
+                       if isError:
+                               _moduleLogger.error("Masking: %s" % (result, ))
+                       isError = True
+                       result = StopIteration("Cancelling all callbacks")
+               callback = on_success if not isError else on_error
+               try:
+                       callback(result)
+               except Exception:
+                       _moduleLogger.exception("Callback errored")
+
+
+def create_single_column_list_model(columnName, **kwargs):
+       """
+       >>> class Single(object): pass
+       >>> SingleListModel = create_single_column_list_model("s")
+       >>> slm = SingleListModel([Single(), Single(), Single()])
+       """
+
+       class SingleColumnListModel(QtCore.QAbstractListModel):
+
+               def __init__(self, l = None):
+                       QtCore.QAbstractListModel.__init__(self)
+                       self._list = l if l is not None else []
+                       self.setRoleNames({0: columnName})
+
+               def __len__(self):
+                       return len(self._list)
+
+               def __getitem__(self, key):
+                       return self._list[key]
+
+               def __setitem__(self, key, value):
+                       with scoped_model_reset(self):
+                               self._list[key] = value
+
+               def __delitem__(self, key):
+                       with scoped_model_reset(self):
+                               del self._list[key]
+
+               def __iter__(self):
+                       return iter(self._list)
+
+               def __repr__(self):
+                       return '<%s (%s)>' % (
+                               self.__class__.__name__,
+                               columnName,
+                       )
+
+               def rowCount(self, parent=QtCore.QModelIndex()):
+                       return len(self._list)
+
+               def data(self, index, role):
+                       if index.isValid() and role == 0:
+                               return self._list[index.row()]
+                       return None
+
+       if "name" in kwargs:
+               SingleColumnListModel.__name__ = kwargs["name"]
+
+       return SingleColumnListModel
+
+
+def create_tupled_list_model(*columnNames, **kwargs):
+       """
+       >>> class Column0(object): pass
+       >>> class Column1(object): pass
+       >>> class Column2(object): pass
+       >>> MultiColumnedListModel = create_tupled_list_model("c0", "c1", "c2")
+       >>> mclm = MultiColumnedListModel([(Column0(), Column1(), Column2())])
+       """
+
+       class TupledListModel(QtCore.QAbstractListModel):
+
+               def __init__(self, l = None):
+                       QtCore.QAbstractListModel.__init__(self)
+                       self._list = l if l is not None else []
+                       self.setRoleNames(dict(enumerate(columnNames)))
+
+               def __len__(self):
+                       return len(self._list)
+
+               def __getitem__(self, key):
+                       return self._list[key]
+
+               def __setitem__(self, key, value):
+                       with scoped_model_reset(self):
+                               self._list[key] = value
+
+               def __delitem__(self, key):
+                       with scoped_model_reset(self):
+                               del self._list[key]
+
+               def __iter__(self):
+                       return iter(self._list)
+
+               def __repr__(self):
+                       return '<%s (%s)>' % (
+                               self.__class__.__name__,
+                               ', '.join(columnNames),
+                       )
+
+               def rowCount(self, parent=QtCore.QModelIndex()):
+                       return len(self._list)
+
+               def data(self, index, role):
+                       if index.isValid() and 0 <= role and role < len(columnNames):
+                               return self._list[index.row()][role]
+                       return None
+
+       if "name" in kwargs:
+               TupledListModel.__name__ = kwargs["name"]
+
+       return TupledListModel
+
+
+class FileSystemModel(QtCore.QAbstractListModel):
+       """
+       Wrapper around QtGui.QFileSystemModel
+       """
+
+       FILEINFOS = [
+               "fileName",
+               "isDir",
+               "filePath",
+               "completeSuffix",
+               "baseName",
+       ]
+
+       EXTINFOS = [
+               "type",
+       ]
+
+       ALLINFOS = FILEINFOS + EXTINFOS
+
+       def __init__(self, model, path):
+               QtCore.QAbstractListModel.__init__(self)
+               self._path = path
+
+               self._model = model
+               self._rootIndex = self._model.index(self._path)
+
+               self._child = None
+               self.setRoleNames(dict(enumerate(self.ALLINFOS)))
+               self._model.directoryLoaded.connect(self._on_directory_loaded)
+
+       childChanged = QtCore.Signal(QtCore.QObject)
+
+       def _child(self):
+               assert self._child is not None
+               return self._child
+
+       child = QtCore.Property(QtCore.QObject, _child, notify=childChanged)
+
+       backendChanged = QtCore.Signal()
+
+       def _parent(self):
+               finfo = self._model.fileInfo(self._rootIndex)
+               return finfo.fileName()
+
+       parent = QtCore.Property(str, _parent, notify=backendChanged)
+
+       @QtCore.Slot(str)
+       def browse_to(self, path):
+               if self._child is None:
+                       self._child = FileSystemModel(self._model, path)
+               else:
+                       self._child.switch_to(path)
+               self.childChanged.emit()
+               return self._child
+
+       @QtCore.Slot(str)
+       def switch_to(self, path):
+               with scoped_model_reset(self):
+                       self._path = path
+                       self._rootIndex = self._model.index(self._path)
+               self.backendChanged.emit()
+
+       def __len__(self):
+               return self._model.rowCount(self._rootIndex)
+
+       def __getitem__(self, key):
+               return self._model.index(key, 0, self._rootIndex)
+
+       def __iter__(self):
+               return (self[i] for i in xrange(len(self)))
+
+       def rowCount(self, parent=QtCore.QModelIndex()):
+               return len(self)
+
+       def data(self, index, role):
+               if index.isValid() and 0 <= role and role < len(self.ALLINFOS):
+                       internalIndex = self._translate_index(index)
+                       info = self._model.fileInfo(internalIndex)
+                       if role < len(self.FILEINFOS):
+                               field = self.FILEINFOS[role]
+                               value = getattr(info, field)()
+                       else:
+                               role -= len(self.FILEINFOS)
+                               field = self.EXTINFOS[role]
+                               if field == "type":
+                                       return self._model.type(internalIndex)
+                               else:
+                                       raise NotImplementedError("Out of range that was already checked")
+                       return value
+               return None
+
+       def _on_directory_loaded(self, path):
+               if self._path == path:
+                       self.backendChanged.emit()
+                       self.reset()
+
+       def _translate_index(self, externalIndex):
+               internalIndex = self._model.index(externalIndex.row(), 0, self._rootIndex)
+               return internalIndex
+
+
+@contextlib.contextmanager
+def scoped_model_reset(model):
+       model.beginResetModel()
+       try:
+               yield
+       finally:
+               model.endResetModel()
+
+
+def create_qobject(*classDef, **kwargs):
+       """
+       >>> Car = create_qobject(
+       ...     ('model', str),
+       ...     ('brand', str),
+       ...     ('year', int),
+       ...     ('inStock', bool),
+       ...     name='Car'
+       ... )
+       >>> print Car
+       <class '__main__.AutoQObject'>
+       >>>  
+       >>> c = Car(model='Fiesta', brand='Ford', year=1337)
+       >>> print c.model, c.brand, c.year, c.inStock
+       Fiesta Ford 1337 False
+       >>> print c
+       <Car (model='Fiesta', brand='Ford', year=1337, inStock=False)>
+       >>>  
+       >>> c.inStock = True
+       >>>  
+       >>> print c.model, c.brand, c.year, c.inStock
+       Fiesta Ford 1337 True
+       >>> print c
+       <Car (model='Fiesta', brand='Ford', year=1337, inStock=True)>
+       """
+
+       class AutoQObject(QtCore.QObject):
+
+               def __init__(self, **initKwargs):
+                       QtCore.QObject.__init__(self)
+                       for key, val in classDef:
+                               setattr(self, '_'+key, initKwargs.get(key, val()))
+
+               def __repr__(self):
+                       values = (
+                               '%s=%r' % (key, getattr(self, '_'+key))
+                               for key, value in classDef
+                       )
+                       return '<%s (%s)>' % (
+                               kwargs.get('name', self.__class__.__name__),
+                               ', '.join(values),
+                       )
+
+               for key, value in classDef:
+                       nfy = locals()['_nfy_'+key] = QtCore.Signal()
+
+                       def _get(key):
+                               def f(self):
+                                       return self.__dict__['_'+key]
+                               return f
+
+                       def _set(key):
+                               def f(self, value):
+                                       setattr(self, '_'+key, value)
+                                       getattr(self, '_nfy_'+key).emit()
+                               return f
+
+                       setter = locals()['_set_'+key] = _set(key)
+                       getter = locals()['_get_'+key] = _get(key)
+
+                       locals()[key] = QtCore.Property(value, getter, setter, notify=nfy)
+               del nfy, _get, _set, getter, setter
+
+       return AutoQObject
+
+
+class QObjectProxy(object):
+       """
+       Proxy for accessing properties and slots as attributes
+
+       This class acts as a proxy for the object for which it is
+       created, and makes property access more Pythonic while
+       still allowing access to slots (as member functions).
+
+       Attribute names starting with '_' are not proxied.
+       """
+
+       def __init__(self, rootObject):
+               self._rootObject = rootObject
+               m = self._rootObject.metaObject()
+               self._properties = [
+                       m.property(i).name()
+                       for i in xrange(m.propertyCount())
+               ]
+
+       def __getattr__(self, key):
+               value = self._rootObject.property(key)
+
+               # No such property, so assume we call a slot
+               if value is None and key not in self._properties:
+                       return getattr(self._rootObject, key)
+
+               return value
+
+       def __setattr__(self, key, value):
+               if key.startswith('_'):
+                       object.__setattr__(self, key, value)
+               else:
+                       self._rootObject.setProperty(key, value)
+
+
+if __name__ == "__main__":
+       import doctest
+       print doctest.testmod()
diff --git a/gonvert/util/qt_compat.py b/gonvert/util/qt_compat.py
new file mode 100644 (file)
index 0000000..409c00d
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+from __future__ import division
+
+_TRY_PYSIDE = False
+
+try:
+       if not _TRY_PYSIDE:
+               raise ImportError()
+       import PySide.QtCore as _QtCore
+       QtCore = _QtCore
+       USES_PYSIDE = True
+except ImportError:
+       import sip
+       sip.setapi('QString', 2)
+       sip.setapi('QVariant', 2)
+       import PyQt4.QtCore as _QtCore
+       QtCore = _QtCore
+       USES_PYSIDE = False
+
+
+def _pyside_import_module(moduleName):
+       pyside = __import__('PySide', globals(), locals(), [moduleName], -1)
+       return getattr(pyside, moduleName)
+
+
+def _pyqt4_import_module(moduleName):
+       pyside = __import__('PyQt4', globals(), locals(), [moduleName], -1)
+       return getattr(pyside, moduleName)
+
+
+if USES_PYSIDE:
+       import_module = _pyside_import_module
+
+       Signal = QtCore.Signal
+       Slot = QtCore.Slot
+       Property = QtCore.Property
+else:
+       import_module = _pyqt4_import_module
+
+       Signal = QtCore.pyqtSignal
+       Slot = QtCore.pyqtSlot
+       Property = QtCore.pyqtProperty
+
+
+if __name__ == "__main__":
+       pass
+
diff --git a/gonvert/util/qtpie.py b/gonvert/util/qtpie.py
new file mode 100755 (executable)
index 0000000..6b77d5d
--- /dev/null
@@ -0,0 +1,1094 @@
+#!/usr/bin/env python
+
+import math
+import logging
+
+import qt_compat
+QtCore = qt_compat.QtCore
+QtGui = qt_compat.import_module("QtGui")
+
+import misc as misc_utils
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+_TWOPI = 2 * math.pi
+
+
+def _radius_at(center, pos):
+       delta = pos - center
+       xDelta = delta.x()
+       yDelta = delta.y()
+
+       radius = math.sqrt(xDelta ** 2 + yDelta ** 2)
+       return radius
+
+
+def _angle_at(center, pos):
+       delta = pos - center
+       xDelta = delta.x()
+       yDelta = delta.y()
+
+       radius = math.sqrt(xDelta ** 2 + yDelta ** 2)
+       angle = math.acos(xDelta / radius)
+       if 0 <= yDelta:
+               angle = _TWOPI - angle
+
+       return angle
+
+
+class QActionPieItem(object):
+
+       def __init__(self, action, weight = 1):
+               self._action = action
+               self._weight = weight
+
+       def action(self):
+               return self._action
+
+       def setWeight(self, weight):
+               self._weight = weight
+
+       def weight(self):
+               return self._weight
+
+       def setEnabled(self, enabled = True):
+               self._action.setEnabled(enabled)
+
+       def isEnabled(self):
+               return self._action.isEnabled()
+
+
+class PieFiling(object):
+
+       INNER_RADIUS_DEFAULT = 64
+       OUTER_RADIUS_DEFAULT = 192
+
+       SELECTION_CENTER = -1
+       SELECTION_NONE = -2
+
+       NULL_CENTER = QActionPieItem(QtGui.QAction(None))
+
+       def __init__(self):
+               self._innerRadius = self.INNER_RADIUS_DEFAULT
+               self._outerRadius = self.OUTER_RADIUS_DEFAULT
+               self._children = []
+               self._center = self.NULL_CENTER
+
+               self._cacheIndexToAngle = {}
+               self._cacheTotalWeight = 0
+
+       def insertItem(self, item, index = -1):
+               self._children.insert(index, item)
+               self._invalidate_cache()
+
+       def removeItemAt(self, index):
+               item = self._children.pop(index)
+               self._invalidate_cache()
+
+       def set_center(self, item):
+               if item is None:
+                       item = self.NULL_CENTER
+               self._center = item
+
+       def center(self):
+               return self._center
+
+       def clear(self):
+               del self._children[:]
+               self._center = self.NULL_CENTER
+               self._invalidate_cache()
+
+       def itemAt(self, index):
+               return self._children[index]
+
+       def indexAt(self, center, point):
+               return self._angle_to_index(_angle_at(center, point))
+
+       def innerRadius(self):
+               return self._innerRadius
+
+       def setInnerRadius(self, radius):
+               self._innerRadius = radius
+
+       def outerRadius(self):
+               return self._outerRadius
+
+       def setOuterRadius(self, radius):
+               self._outerRadius = radius
+
+       def __iter__(self):
+               return iter(self._children)
+
+       def __len__(self):
+               return len(self._children)
+
+       def __getitem__(self, index):
+               return self._children[index]
+
+       def _invalidate_cache(self):
+               self._cacheIndexToAngle.clear()
+               self._cacheTotalWeight = sum(child.weight() for child in self._children)
+               if self._cacheTotalWeight == 0:
+                       self._cacheTotalWeight = 1
+
+       def _index_to_angle(self, index, isShifted):
+               key = index, isShifted
+               if key in self._cacheIndexToAngle:
+                       return self._cacheIndexToAngle[key]
+               index = index % len(self._children)
+
+               baseAngle = _TWOPI / self._cacheTotalWeight
+
+               angle = math.pi / 2
+               if isShifted:
+                       if self._children:
+                               angle -= (self._children[0].weight() * baseAngle) / 2
+                       else:
+                               angle -= baseAngle / 2
+               while angle < 0:
+                       angle += _TWOPI
+
+               for i, child in enumerate(self._children):
+                       if index < i:
+                               break
+                       angle += child.weight() * baseAngle
+               while _TWOPI < angle:
+                       angle -= _TWOPI
+
+               self._cacheIndexToAngle[key] = angle
+               return angle
+
+       def _angle_to_index(self, angle):
+               numChildren = len(self._children)
+               if numChildren == 0:
+                       return self.SELECTION_CENTER
+
+               baseAngle = _TWOPI / self._cacheTotalWeight
+
+               iterAngle = math.pi / 2 - (self.itemAt(0).weight() * baseAngle) / 2
+               while iterAngle < 0:
+                       iterAngle += _TWOPI
+
+               oldIterAngle = iterAngle
+               for index, child in enumerate(self._children):
+                       iterAngle += child.weight() * baseAngle
+                       if oldIterAngle < angle and angle <= iterAngle:
+                               return index - 1 if index != 0 else numChildren - 1
+                       elif oldIterAngle < (angle + _TWOPI) and (angle + _TWOPI <= iterAngle):
+                               return index - 1 if index != 0 else numChildren - 1
+                       oldIterAngle = iterAngle
+
+
+class PieArtist(object):
+
+       ICON_SIZE_DEFAULT = 48
+
+       SHAPE_CIRCLE = "circle"
+       SHAPE_SQUARE = "square"
+       DEFAULT_SHAPE = SHAPE_SQUARE
+
+       BACKGROUND_FILL = "fill"
+       BACKGROUND_NOFILL = "no fill"
+
+       def __init__(self, filing, background = BACKGROUND_FILL):
+               self._filing = filing
+
+               self._cachedOuterRadius = self._filing.outerRadius()
+               self._cachedInnerRadius = self._filing.innerRadius()
+               canvasSize = self._cachedOuterRadius * 2 + 1
+               self._canvas = QtGui.QPixmap(canvasSize, canvasSize)
+               self._mask = None
+               self._backgroundState = background
+               self.palette = None
+
+       def pieSize(self):
+               diameter = self._filing.outerRadius() * 2 + 1
+               return QtCore.QSize(diameter, diameter)
+
+       def centerSize(self):
+               painter = QtGui.QPainter(self._canvas)
+               text = self._filing.center().action().text()
+               fontMetrics = painter.fontMetrics()
+               if text:
+                       textBoundingRect = fontMetrics.boundingRect(text)
+               else:
+                       textBoundingRect = QtCore.QRect()
+               textWidth = textBoundingRect.width()
+               textHeight = textBoundingRect.height()
+
+               return QtCore.QSize(
+                       textWidth + self.ICON_SIZE_DEFAULT,
+                       max(textHeight, self.ICON_SIZE_DEFAULT),
+               )
+
+       def show(self, palette):
+               self.palette = palette
+
+               if (
+                       self._cachedOuterRadius != self._filing.outerRadius() or
+                       self._cachedInnerRadius != self._filing.innerRadius()
+               ):
+                       self._cachedOuterRadius = self._filing.outerRadius()
+                       self._cachedInnerRadius = self._filing.innerRadius()
+                       self._canvas = self._canvas.scaled(self.pieSize())
+
+               if self._mask is None:
+                       self._mask = QtGui.QBitmap(self._canvas.size())
+                       self._mask.fill(QtCore.Qt.color0)
+                       self._generate_mask(self._mask)
+                       self._canvas.setMask(self._mask)
+               return self._mask
+
+       def hide(self):
+               self.palette = None
+
+       def paint(self, selectionIndex):
+               painter = QtGui.QPainter(self._canvas)
+               painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
+
+               self.paintPainter(selectionIndex, painter)
+
+               return self._canvas
+
+       def paintPainter(self, selectionIndex, painter):
+               adjustmentRect = painter.viewport().adjusted(0, 0, -1, -1)
+
+               numChildren = len(self._filing)
+               if numChildren == 0:
+                       self._paint_center_background(painter, adjustmentRect, selectionIndex)
+                       self._paint_center_foreground(painter, adjustmentRect, selectionIndex)
+                       return self._canvas
+               else:
+                       for i in xrange(len(self._filing)):
+                               self._paint_slice_background(painter, adjustmentRect, i, selectionIndex)
+
+               self._paint_center_background(painter, adjustmentRect, selectionIndex)
+               self._paint_center_foreground(painter, adjustmentRect, selectionIndex)
+
+               for i in xrange(len(self._filing)):
+                       self._paint_slice_foreground(painter, adjustmentRect, i, selectionIndex)
+
+       def _generate_mask(self, mask):
+               """
+               Specifies on the mask the shape of the pie menu
+               """
+               painter = QtGui.QPainter(mask)
+               painter.setPen(QtCore.Qt.color1)
+               painter.setBrush(QtCore.Qt.color1)
+               if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
+                       painter.drawRect(mask.rect())
+               elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
+                       painter.drawEllipse(mask.rect().adjusted(0, 0, -1, -1))
+               else:
+                       raise NotImplementedError(self.DEFAULT_SHAPE)
+
+       def _paint_slice_background(self, painter, adjustmentRect, i, selectionIndex):
+               if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
+                       currentWidth = adjustmentRect.width()
+                       newWidth = math.sqrt(2) * currentWidth
+                       dx = (newWidth - currentWidth) / 2
+                       adjustmentRect = adjustmentRect.adjusted(-dx, -dx, dx, dx)
+               elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
+                       pass
+               else:
+                       raise NotImplementedError(self.DEFAULT_SHAPE)
+
+               if self._backgroundState == self.BACKGROUND_NOFILL:
+                       painter.setBrush(QtGui.QBrush(QtCore.Qt.transparent))
+                       painter.setPen(self.palette.highlight().color())
+               else:
+                       if i == selectionIndex and self._filing[i].isEnabled():
+                               painter.setBrush(self.palette.highlight())
+                               painter.setPen(self.palette.highlight().color())
+                       else:
+                               painter.setBrush(self.palette.window())
+                               painter.setPen(self.palette.window().color())
+
+               a = self._filing._index_to_angle(i, True)
+               b = self._filing._index_to_angle(i + 1, True)
+               if b < a:
+                       b += _TWOPI
+               size = b - a
+               if size < 0:
+                       size += _TWOPI
+
+               startAngleInDeg = (a * 360 * 16) / _TWOPI
+               sizeInDeg = (size * 360 * 16) / _TWOPI
+               painter.drawPie(adjustmentRect, int(startAngleInDeg), int(sizeInDeg))
+
+       def _paint_slice_foreground(self, painter, adjustmentRect, i, selectionIndex):
+               child = self._filing[i]
+
+               a = self._filing._index_to_angle(i, True)
+               b = self._filing._index_to_angle(i + 1, True)
+               if b < a:
+                       b += _TWOPI
+               middleAngle = (a + b) / 2
+               averageRadius = (self._cachedInnerRadius + self._cachedOuterRadius) / 2
+
+               sliceX = averageRadius * math.cos(middleAngle)
+               sliceY = - averageRadius * math.sin(middleAngle)
+
+               piePos = adjustmentRect.center()
+               pieX = piePos.x()
+               pieY = piePos.y()
+               self._paint_label(
+                       painter, child.action(), i == selectionIndex, pieX+sliceX, pieY+sliceY
+               )
+
+       def _paint_label(self, painter, action, isSelected, x, y):
+               text = action.text()
+               fontMetrics = painter.fontMetrics()
+               if text:
+                       textBoundingRect = fontMetrics.boundingRect(text)
+               else:
+                       textBoundingRect = QtCore.QRect()
+               textWidth = textBoundingRect.width()
+               textHeight = textBoundingRect.height()
+
+               icon = action.icon().pixmap(
+                       QtCore.QSize(self.ICON_SIZE_DEFAULT, self.ICON_SIZE_DEFAULT),
+                       QtGui.QIcon.Normal,
+                       QtGui.QIcon.On,
+               )
+               iconWidth = icon.width()
+               iconHeight = icon.width()
+               averageWidth = (iconWidth + textWidth)/2
+               if not icon.isNull():
+                       iconRect = QtCore.QRect(
+                               x - averageWidth,
+                               y - iconHeight/2,
+                               iconWidth,
+                               iconHeight,
+                       )
+
+                       painter.drawPixmap(iconRect, icon)
+
+               if text:
+                       if isSelected:
+                               if action.isEnabled():
+                                       pen = self.palette.highlightedText()
+                                       brush = self.palette.highlight()
+                               else:
+                                       pen = self.palette.mid()
+                                       brush = self.palette.window()
+                       else:
+                               if action.isEnabled():
+                                       pen = self.palette.windowText()
+                               else:
+                                       pen = self.palette.mid()
+                               brush = self.palette.window()
+
+                       leftX = x - averageWidth + iconWidth
+                       topY = y + textHeight/2
+                       painter.setPen(pen.color())
+                       painter.setBrush(brush)
+                       painter.drawText(leftX, topY, text)
+
+       def _paint_center_background(self, painter, adjustmentRect, selectionIndex):
+               if self._backgroundState == self.BACKGROUND_NOFILL:
+                       return
+               if len(self._filing) == 0:
+                       if self._backgroundState == self.BACKGROUND_NOFILL:
+                               painter.setBrush(QtGui.QBrush(QtCore.Qt.transparent))
+                       else:
+                               if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
+                                       painter.setBrush(self.palette.highlight())
+                               else:
+                                       painter.setBrush(self.palette.window())
+                       painter.setPen(self.palette.mid().color())
+
+                       painter.drawRect(adjustmentRect)
+               else:
+                       dark = self.palette.mid().color()
+                       light = self.palette.light().color()
+                       if self._backgroundState == self.BACKGROUND_NOFILL:
+                               background = QtGui.QBrush(QtCore.Qt.transparent)
+                       else:
+                               if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
+                                       background = self.palette.highlight().color()
+                               else:
+                                       background = self.palette.window().color()
+
+                       innerRadius = self._cachedInnerRadius
+                       adjustmentCenterPos = adjustmentRect.center()
+                       innerRect = QtCore.QRect(
+                               adjustmentCenterPos.x() - innerRadius,
+                               adjustmentCenterPos.y() - innerRadius,
+                               innerRadius * 2 + 1,
+                               innerRadius * 2 + 1,
+                       )
+
+                       painter.setPen(QtCore.Qt.NoPen)
+                       painter.setBrush(background)
+                       painter.drawPie(innerRect, 0, 360 * 16)
+
+                       if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
+                               pass
+                       elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
+                               painter.setPen(QtGui.QPen(dark, 1))
+                               painter.setBrush(QtCore.Qt.NoBrush)
+                               painter.drawEllipse(adjustmentRect)
+                       else:
+                               raise NotImplementedError(self.DEFAULT_SHAPE)
+
+       def _paint_center_foreground(self, painter, adjustmentRect, selectionIndex):
+               centerPos = adjustmentRect.center()
+               pieX = centerPos.x()
+               pieY = centerPos.y()
+
+               x = pieX
+               y = pieY
+
+               self._paint_label(
+                       painter,
+                       self._filing.center().action(),
+                       selectionIndex == PieFiling.SELECTION_CENTER,
+                       x, y
+               )
+
+
+class QPieDisplay(QtGui.QWidget):
+
+       def __init__(self, filing, parent = None, flags = QtCore.Qt.Window):
+               QtGui.QWidget.__init__(self, parent, flags)
+               self._filing = filing
+               self._artist = PieArtist(self._filing)
+               self._selectionIndex = PieFiling.SELECTION_NONE
+
+       def popup(self, pos):
+               self._update_selection(pos)
+               self.show()
+
+       def sizeHint(self):
+               return self._artist.pieSize()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def showEvent(self, showEvent):
+               mask = self._artist.show(self.palette())
+               self.setMask(mask)
+
+               QtGui.QWidget.showEvent(self, showEvent)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def hideEvent(self, hideEvent):
+               self._artist.hide()
+               self._selectionIndex = PieFiling.SELECTION_NONE
+               QtGui.QWidget.hideEvent(self, hideEvent)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def paintEvent(self, paintEvent):
+               canvas = self._artist.paint(self._selectionIndex)
+               offset = (self.size() - canvas.size()) / 2
+
+               screen = QtGui.QPainter(self)
+               screen.drawPixmap(QtCore.QPoint(offset.width(), offset.height()), canvas)
+
+               QtGui.QWidget.paintEvent(self, paintEvent)
+
+       def selectAt(self, index):
+               oldIndex = self._selectionIndex
+               self._selectionIndex = index
+               if self.isVisible():
+                       self.update()
+
+
+class QPieButton(QtGui.QWidget):
+
+       activated = qt_compat.Signal(int)
+       highlighted = qt_compat.Signal(int)
+       canceled = qt_compat.Signal()
+       aboutToShow = qt_compat.Signal()
+       aboutToHide = qt_compat.Signal()
+
+       BUTTON_RADIUS = 24
+       DELAY = 250
+
+       def __init__(self, buttonSlice, parent = None, buttonSlices = None):
+               # @bug Artifacts on Maemo 5 due to window 3D effects, find way to disable them for just these?
+               # @bug The pie's are being pushed back on screen on Maemo, leading to coordinate issues
+               QtGui.QWidget.__init__(self, parent)
+               self._cachedCenterPosition = self.rect().center()
+
+               self._filing = PieFiling()
+               self._display = QPieDisplay(self._filing, None, QtCore.Qt.SplashScreen)
+               self._selectionIndex = PieFiling.SELECTION_NONE
+
+               self._buttonFiling = PieFiling()
+               self._buttonFiling.set_center(buttonSlice)
+               if buttonSlices is not None:
+                       for slice in buttonSlices:
+                               self._buttonFiling.insertItem(slice)
+               self._buttonFiling.setOuterRadius(self.BUTTON_RADIUS)
+               self._buttonArtist = PieArtist(self._buttonFiling, PieArtist.BACKGROUND_NOFILL)
+               self._poppedUp = False
+               self._pressed = False
+
+               self._delayPopupTimer = QtCore.QTimer()
+               self._delayPopupTimer.setInterval(self.DELAY)
+               self._delayPopupTimer.setSingleShot(True)
+               self._delayPopupTimer.timeout.connect(self._on_delayed_popup)
+               self._popupLocation = None
+
+               self._mousePosition = None
+               self.setFocusPolicy(QtCore.Qt.StrongFocus)
+               self.setSizePolicy(
+                       QtGui.QSizePolicy(
+                               QtGui.QSizePolicy.MinimumExpanding,
+                               QtGui.QSizePolicy.MinimumExpanding,
+                       )
+               )
+
+       def insertItem(self, item, index = -1):
+               self._filing.insertItem(item, index)
+
+       def removeItemAt(self, index):
+               self._filing.removeItemAt(index)
+
+       def set_center(self, item):
+               self._filing.set_center(item)
+
+       def set_button(self, item):
+               self.update()
+
+       def clear(self):
+               self._filing.clear()
+
+       def itemAt(self, index):
+               return self._filing.itemAt(index)
+
+       def indexAt(self, point):
+               return self._filing.indexAt(self._cachedCenterPosition, point)
+
+       def innerRadius(self):
+               return self._filing.innerRadius()
+
+       def setInnerRadius(self, radius):
+               self._filing.setInnerRadius(radius)
+
+       def outerRadius(self):
+               return self._filing.outerRadius()
+
+       def setOuterRadius(self, radius):
+               self._filing.setOuterRadius(radius)
+
+       def buttonRadius(self):
+               return self._buttonFiling.outerRadius()
+
+       def setButtonRadius(self, radius):
+               self._buttonFiling.setOuterRadius(radius)
+               self._buttonFiling.setInnerRadius(radius / 2)
+               self._buttonArtist.show(self.palette())
+
+       def minimumSizeHint(self):
+               return self._buttonArtist.centerSize()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def mousePressEvent(self, mouseEvent):
+               lastSelection = self._selectionIndex
+
+               lastMousePos = mouseEvent.pos()
+               self._mousePosition = lastMousePos
+               self._update_selection(self._cachedCenterPosition)
+
+               self.highlighted.emit(self._selectionIndex)
+
+               self._display.selectAt(self._selectionIndex)
+               self._pressed = True
+               self.update()
+               self._popupLocation = mouseEvent.globalPos()
+               self._delayPopupTimer.start()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_delayed_popup(self):
+               assert self._popupLocation is not None, "Widget location abuse"
+               self._popup_child(self._popupLocation)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def mouseMoveEvent(self, mouseEvent):
+               lastSelection = self._selectionIndex
+
+               lastMousePos = mouseEvent.pos()
+               if self._mousePosition is None:
+                       # Absolute
+                       self._update_selection(lastMousePos)
+               else:
+                       # Relative
+                       self._update_selection(
+                               self._cachedCenterPosition + (lastMousePos - self._mousePosition),
+                               ignoreOuter = True,
+                       )
+
+               if lastSelection != self._selectionIndex:
+                       self.highlighted.emit(self._selectionIndex)
+                       self._display.selectAt(self._selectionIndex)
+
+               if self._selectionIndex != PieFiling.SELECTION_CENTER and self._delayPopupTimer.isActive():
+                       self._on_delayed_popup()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def mouseReleaseEvent(self, mouseEvent):
+               self._delayPopupTimer.stop()
+               self._popupLocation = None
+
+               lastSelection = self._selectionIndex
+
+               lastMousePos = mouseEvent.pos()
+               if self._mousePosition is None:
+                       # Absolute
+                       self._update_selection(lastMousePos)
+               else:
+                       # Relative
+                       self._update_selection(
+                               self._cachedCenterPosition + (lastMousePos - self._mousePosition),
+                               ignoreOuter = True,
+                       )
+               self._mousePosition = None
+
+               self._activate_at(self._selectionIndex)
+               self._pressed = False
+               self.update()
+               self._hide_child()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def keyPressEvent(self, keyEvent):
+               if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
+                       self._popup_child(QtGui.QCursor.pos())
+                       if self._selectionIndex != len(self._filing) - 1:
+                               nextSelection = self._selectionIndex + 1
+                       else:
+                               nextSelection = 0
+                       self._select_at(nextSelection)
+                       self._display.selectAt(self._selectionIndex)
+               elif keyEvent.key() in [QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab]:
+                       self._popup_child(QtGui.QCursor.pos())
+                       if 0 < self._selectionIndex:
+                               nextSelection = self._selectionIndex - 1
+                       else:
+                               nextSelection = len(self._filing) - 1
+                       self._select_at(nextSelection)
+                       self._display.selectAt(self._selectionIndex)
+               elif keyEvent.key() in [QtCore.Qt.Key_Space]:
+                       self._popup_child(QtGui.QCursor.pos())
+                       self._select_at(PieFiling.SELECTION_CENTER)
+                       self._display.selectAt(self._selectionIndex)
+               elif keyEvent.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space]:
+                       self._delayPopupTimer.stop()
+                       self._popupLocation = None
+                       self._activate_at(self._selectionIndex)
+                       self._hide_child()
+               elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]:
+                       self._delayPopupTimer.stop()
+                       self._popupLocation = None
+                       self._activate_at(PieFiling.SELECTION_NONE)
+                       self._hide_child()
+               else:
+                       QtGui.QWidget.keyPressEvent(self, keyEvent)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def resizeEvent(self, resizeEvent):
+               self.setButtonRadius(min(resizeEvent.size().width(), resizeEvent.size().height()) / 2 - 1)
+               QtGui.QWidget.resizeEvent(self, resizeEvent)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def showEvent(self, showEvent):
+               self._buttonArtist.show(self.palette())
+               self._cachedCenterPosition = self.rect().center()
+
+               QtGui.QWidget.showEvent(self, showEvent)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def hideEvent(self, hideEvent):
+               self._display.hide()
+               self._select_at(PieFiling.SELECTION_NONE)
+               QtGui.QWidget.hideEvent(self, hideEvent)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def paintEvent(self, paintEvent):
+               self.setButtonRadius(min(self.rect().width(), self.rect().height()) / 2 - 1)
+               if self._poppedUp:
+                       selectionIndex = PieFiling.SELECTION_CENTER
+               else:
+                       selectionIndex = PieFiling.SELECTION_NONE
+
+               screen = QtGui.QStylePainter(self)
+               screen.setRenderHint(QtGui.QPainter.Antialiasing, True)
+               option = QtGui.QStyleOptionButton()
+               option.initFrom(self)
+               option.state = QtGui.QStyle.State_Sunken if self._pressed else QtGui.QStyle.State_Raised
+
+               screen.drawControl(QtGui.QStyle.CE_PushButton, option)
+               self._buttonArtist.paintPainter(selectionIndex, screen)
+
+               QtGui.QWidget.paintEvent(self, paintEvent)
+
+       def __iter__(self):
+               return iter(self._filing)
+
+       def __len__(self):
+               return len(self._filing)
+
+       def _popup_child(self, position):
+               self._poppedUp = True
+               self.aboutToShow.emit()
+
+               self._delayPopupTimer.stop()
+               self._popupLocation = None
+
+               position = position - QtCore.QPoint(self._filing.outerRadius(), self._filing.outerRadius())
+               self._display.move(position)
+               self._display.show()
+
+               self.update()
+
+       def _hide_child(self):
+               self._poppedUp = False
+               self.aboutToHide.emit()
+               self._display.hide()
+               self.update()
+
+       def _select_at(self, index):
+               self._selectionIndex = index
+
+       def _update_selection(self, lastMousePos, ignoreOuter = False):
+               radius = _radius_at(self._cachedCenterPosition, lastMousePos)
+               if radius < self._filing.innerRadius():
+                       self._select_at(PieFiling.SELECTION_CENTER)
+               elif radius <= self._filing.outerRadius() or ignoreOuter:
+                       self._select_at(self.indexAt(lastMousePos))
+               else:
+                       self._select_at(PieFiling.SELECTION_NONE)
+
+       def _activate_at(self, index):
+               if index == PieFiling.SELECTION_NONE:
+                       self.canceled.emit()
+                       return
+               elif index == PieFiling.SELECTION_CENTER:
+                       child = self._filing.center()
+               else:
+                       child = self.itemAt(index)
+
+               if child.action().isEnabled():
+                       child.action().trigger()
+                       self.activated.emit(index)
+               else:
+                       self.canceled.emit()
+
+
+class QPieMenu(QtGui.QWidget):
+
+       activated = qt_compat.Signal(int)
+       highlighted = qt_compat.Signal(int)
+       canceled = qt_compat.Signal()
+       aboutToShow = qt_compat.Signal()
+       aboutToHide = qt_compat.Signal()
+
+       def __init__(self, parent = None):
+               QtGui.QWidget.__init__(self, parent)
+               self._cachedCenterPosition = self.rect().center()
+
+               self._filing = PieFiling()
+               self._artist = PieArtist(self._filing)
+               self._selectionIndex = PieFiling.SELECTION_NONE
+
+               self._mousePosition = ()
+               self.setFocusPolicy(QtCore.Qt.StrongFocus)
+
+       def popup(self, pos):
+               self._update_selection(pos)
+               self.show()
+
+       def insertItem(self, item, index = -1):
+               self._filing.insertItem(item, index)
+               self.update()
+
+       def removeItemAt(self, index):
+               self._filing.removeItemAt(index)
+               self.update()
+
+       def set_center(self, item):
+               self._filing.set_center(item)
+               self.update()
+
+       def clear(self):
+               self._filing.clear()
+               self.update()
+
+       def itemAt(self, index):
+               return self._filing.itemAt(index)
+
+       def indexAt(self, point):
+               return self._filing.indexAt(self._cachedCenterPosition, point)
+
+       def innerRadius(self):
+               return self._filing.innerRadius()
+
+       def setInnerRadius(self, radius):
+               self._filing.setInnerRadius(radius)
+               self.update()
+
+       def outerRadius(self):
+               return self._filing.outerRadius()
+
+       def setOuterRadius(self, radius):
+               self._filing.setOuterRadius(radius)
+               self.update()
+
+       def sizeHint(self):
+               return self._artist.pieSize()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def mousePressEvent(self, mouseEvent):
+               lastSelection = self._selectionIndex
+
+               lastMousePos = mouseEvent.pos()
+               self._update_selection(lastMousePos)
+               self._mousePosition = lastMousePos
+
+               if lastSelection != self._selectionIndex:
+                       self.highlighted.emit(self._selectionIndex)
+                       self.update()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def mouseMoveEvent(self, mouseEvent):
+               lastSelection = self._selectionIndex
+
+               lastMousePos = mouseEvent.pos()
+               self._update_selection(lastMousePos)
+
+               if lastSelection != self._selectionIndex:
+                       self.highlighted.emit(self._selectionIndex)
+                       self.update()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def mouseReleaseEvent(self, mouseEvent):
+               lastSelection = self._selectionIndex
+
+               lastMousePos = mouseEvent.pos()
+               self._update_selection(lastMousePos)
+               self._mousePosition = ()
+
+               self._activate_at(self._selectionIndex)
+               self.update()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def keyPressEvent(self, keyEvent):
+               if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
+                       if self._selectionIndex != len(self._filing) - 1:
+                               nextSelection = self._selectionIndex + 1
+                       else:
+                               nextSelection = 0
+                       self._select_at(nextSelection)
+                       self.update()
+               elif keyEvent.key() in [QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab]:
+                       if 0 < self._selectionIndex:
+                               nextSelection = self._selectionIndex - 1
+                       else:
+                               nextSelection = len(self._filing) - 1
+                       self._select_at(nextSelection)
+                       self.update()
+               elif keyEvent.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space]:
+                       self._activate_at(self._selectionIndex)
+               elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]:
+                       self._activate_at(PieFiling.SELECTION_NONE)
+               else:
+                       QtGui.QWidget.keyPressEvent(self, keyEvent)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def showEvent(self, showEvent):
+               self.aboutToShow.emit()
+               self._cachedCenterPosition = self.rect().center()
+
+               mask = self._artist.show(self.palette())
+               self.setMask(mask)
+
+               lastMousePos = self.mapFromGlobal(QtGui.QCursor.pos())
+               self._update_selection(lastMousePos)
+
+               QtGui.QWidget.showEvent(self, showEvent)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def hideEvent(self, hideEvent):
+               self._artist.hide()
+               self._selectionIndex = PieFiling.SELECTION_NONE
+               QtGui.QWidget.hideEvent(self, hideEvent)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def paintEvent(self, paintEvent):
+               canvas = self._artist.paint(self._selectionIndex)
+
+               screen = QtGui.QPainter(self)
+               screen.drawPixmap(QtCore.QPoint(0, 0), canvas)
+
+               QtGui.QWidget.paintEvent(self, paintEvent)
+
+       def __iter__(self):
+               return iter(self._filing)
+
+       def __len__(self):
+               return len(self._filing)
+
+       def _select_at(self, index):
+               self._selectionIndex = index
+
+       def _update_selection(self, lastMousePos):
+               radius = _radius_at(self._cachedCenterPosition, lastMousePos)
+               if radius < self._filing.innerRadius():
+                       self._selectionIndex = PieFiling.SELECTION_CENTER
+               elif radius <= self._filing.outerRadius():
+                       self._select_at(self.indexAt(lastMousePos))
+               else:
+                       self._selectionIndex = PieFiling.SELECTION_NONE
+
+       def _activate_at(self, index):
+               if index == PieFiling.SELECTION_NONE:
+                       self.canceled.emit()
+                       self.aboutToHide.emit()
+                       self.hide()
+                       return
+               elif index == PieFiling.SELECTION_CENTER:
+                       child = self._filing.center()
+               else:
+                       child = self.itemAt(index)
+
+               if child.isEnabled():
+                       child.action().trigger()
+                       self.activated.emit(index)
+               else:
+                       self.canceled.emit()
+               self.aboutToHide.emit()
+               self.hide()
+
+
+def init_pies():
+       PieFiling.NULL_CENTER.setEnabled(False)
+
+
+def _print(msg):
+       print msg
+
+
+def _on_about_to_hide(app):
+       app.exit()
+
+
+if __name__ == "__main__":
+       app = QtGui.QApplication([])
+       init_pies()
+
+       if False:
+               pie = QPieMenu()
+               pie.show()
+
+       if False:
+               singleAction = QtGui.QAction(None)
+               singleAction.setText("Boo")
+               singleItem = QActionPieItem(singleAction)
+               spie = QPieMenu()
+               spie.insertItem(singleItem)
+               spie.show()
+
+       if False:
+               oneAction = QtGui.QAction(None)
+               oneAction.setText("Chew")
+               oneItem = QActionPieItem(oneAction)
+               twoAction = QtGui.QAction(None)
+               twoAction.setText("Foo")
+               twoItem = QActionPieItem(twoAction)
+               iconTextAction = QtGui.QAction(None)
+               iconTextAction.setText("Icon")
+               iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
+               iconTextItem = QActionPieItem(iconTextAction)
+               mpie = QPieMenu()
+               mpie.insertItem(oneItem)
+               mpie.insertItem(twoItem)
+               mpie.insertItem(oneItem)
+               mpie.insertItem(iconTextItem)
+               mpie.show()
+
+       if True:
+               oneAction = QtGui.QAction(None)
+               oneAction.setText("Chew")
+               oneAction.triggered.connect(lambda: _print("Chew"))
+               oneItem = QActionPieItem(oneAction)
+               twoAction = QtGui.QAction(None)
+               twoAction.setText("Foo")
+               twoAction.triggered.connect(lambda: _print("Foo"))
+               twoItem = QActionPieItem(twoAction)
+               iconAction = QtGui.QAction(None)
+               iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
+               iconAction.triggered.connect(lambda: _print("Icon"))
+               iconItem = QActionPieItem(iconAction)
+               iconTextAction = QtGui.QAction(None)
+               iconTextAction.setText("Icon")
+               iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
+               iconTextAction.triggered.connect(lambda: _print("Icon and text"))
+               iconTextItem = QActionPieItem(iconTextAction)
+               mpie = QPieMenu()
+               mpie.set_center(iconItem)
+               mpie.insertItem(oneItem)
+               mpie.insertItem(twoItem)
+               mpie.insertItem(oneItem)
+               mpie.insertItem(iconTextItem)
+               mpie.show()
+               mpie.aboutToHide.connect(lambda: _on_about_to_hide(app))
+               mpie.canceled.connect(lambda: _print("Canceled"))
+
+       if False:
+               oneAction = QtGui.QAction(None)
+               oneAction.setText("Chew")
+               oneAction.triggered.connect(lambda: _print("Chew"))
+               oneItem = QActionPieItem(oneAction)
+               twoAction = QtGui.QAction(None)
+               twoAction.setText("Foo")
+               twoAction.triggered.connect(lambda: _print("Foo"))
+               twoItem = QActionPieItem(twoAction)
+               iconAction = QtGui.QAction(None)
+               iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
+               iconAction.triggered.connect(lambda: _print("Icon"))
+               iconItem = QActionPieItem(iconAction)
+               iconTextAction = QtGui.QAction(None)
+               iconTextAction.setText("Icon")
+               iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
+               iconTextAction.triggered.connect(lambda: _print("Icon and text"))
+               iconTextItem = QActionPieItem(iconTextAction)
+               pieFiling = PieFiling()
+               pieFiling.set_center(iconItem)
+               pieFiling.insertItem(oneItem)
+               pieFiling.insertItem(twoItem)
+               pieFiling.insertItem(oneItem)
+               pieFiling.insertItem(iconTextItem)
+               mpie = QPieDisplay(pieFiling)
+               mpie.show()
+
+       if False:
+               oneAction = QtGui.QAction(None)
+               oneAction.setText("Chew")
+               oneAction.triggered.connect(lambda: _print("Chew"))
+               oneItem = QActionPieItem(oneAction)
+               twoAction = QtGui.QAction(None)
+               twoAction.setText("Foo")
+               twoAction.triggered.connect(lambda: _print("Foo"))
+               twoItem = QActionPieItem(twoAction)
+               iconAction = QtGui.QAction(None)
+               iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
+               iconAction.triggered.connect(lambda: _print("Icon"))
+               iconItem = QActionPieItem(iconAction)
+               iconTextAction = QtGui.QAction(None)
+               iconTextAction.setText("Icon")
+               iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
+               iconTextAction.triggered.connect(lambda: _print("Icon and text"))
+               iconTextItem = QActionPieItem(iconTextAction)
+               mpie = QPieButton(iconItem)
+               mpie.set_center(iconItem)
+               mpie.insertItem(oneItem)
+               mpie.insertItem(twoItem)
+               mpie.insertItem(oneItem)
+               mpie.insertItem(iconTextItem)
+               mpie.show()
+               mpie.aboutToHide.connect(lambda: _on_about_to_hide(app))
+               mpie.canceled.connect(lambda: _print("Canceled"))
+
+       app.exec_()
diff --git a/gonvert/util/qtpieboard.py b/gonvert/util/qtpieboard.py
new file mode 100755 (executable)
index 0000000..50ae9ae
--- /dev/null
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+
+
+from __future__ import division
+
+import os
+import warnings
+
+import qt_compat
+QtGui = qt_compat.import_module("QtGui")
+
+import qtpie
+
+
+class PieKeyboard(object):
+
+       SLICE_CENTER = -1
+       SLICE_NORTH = 0
+       SLICE_NORTH_WEST = 1
+       SLICE_WEST = 2
+       SLICE_SOUTH_WEST = 3
+       SLICE_SOUTH = 4
+       SLICE_SOUTH_EAST = 5
+       SLICE_EAST = 6
+       SLICE_NORTH_EAST = 7
+
+       MAX_ANGULAR_SLICES = 8
+
+       SLICE_DIRECTIONS = [
+               SLICE_CENTER,
+               SLICE_NORTH,
+               SLICE_NORTH_WEST,
+               SLICE_WEST,
+               SLICE_SOUTH_WEST,
+               SLICE_SOUTH,
+               SLICE_SOUTH_EAST,
+               SLICE_EAST,
+               SLICE_NORTH_EAST,
+       ]
+
+       SLICE_DIRECTION_NAMES = [
+               "CENTER",
+               "NORTH",
+               "NORTH_WEST",
+               "WEST",
+               "SOUTH_WEST",
+               "SOUTH",
+               "SOUTH_EAST",
+               "EAST",
+               "NORTH_EAST",
+       ]
+
+       def __init__(self):
+               self._layout = QtGui.QGridLayout()
+               self._widget = QtGui.QWidget()
+               self._widget.setLayout(self._layout)
+
+               self.__cells = {}
+
+       @property
+       def toplevel(self):
+               return self._widget
+
+       def add_pie(self, row, column, pieButton):
+               assert len(pieButton) == 8
+               self._layout.addWidget(pieButton, row, column)
+               self.__cells[(row, column)] = pieButton
+
+       def get_pie(self, row, column):
+               return self.__cells[(row, column)]
+
+
+class KeyboardModifier(object):
+
+       def __init__(self, name):
+               self.name = name
+               self.lock = False
+               self.once = False
+
+       @property
+       def isActive(self):
+               return self.lock or self.once
+
+       def on_toggle_lock(self, *args, **kwds):
+               self.lock = not self.lock
+
+       def on_toggle_once(self, *args, **kwds):
+               self.once = not self.once
+
+       def reset_once(self):
+               self.once = False
+
+
+def parse_keyboard_data(text):
+       return eval(text)
+
+
+def _enumerate_pie_slices(pieData, iconPaths):
+       for direction, directionName in zip(
+               PieKeyboard.SLICE_DIRECTIONS, PieKeyboard.SLICE_DIRECTION_NAMES
+       ):
+               if directionName in pieData:
+                       sliceData = pieData[directionName]
+
+                       action = QtGui.QAction(None)
+                       try:
+                               action.setText(sliceData["text"])
+                       except KeyError:
+                               pass
+                       try:
+                               relativeIconPath = sliceData["path"]
+                       except KeyError:
+                               pass
+                       else:
+                               for iconPath in iconPaths:
+                                       absIconPath = os.path.join(iconPath, relativeIconPath)
+                                       if os.path.exists(absIconPath):
+                                               action.setIcon(QtGui.QIcon(absIconPath))
+                                               break
+                       pieItem = qtpie.QActionPieItem(action)
+                       actionToken = sliceData["action"]
+               else:
+                       pieItem = qtpie.PieFiling.NULL_CENTER
+                       actionToken = ""
+               yield direction, pieItem, actionToken
+
+
+def load_keyboard(keyboardName, dataTree, keyboard, keyboardHandler, iconPaths):
+       for (row, column), pieData in dataTree.iteritems():
+               pieItems = list(_enumerate_pie_slices(pieData, iconPaths))
+               assert pieItems[0][0] == PieKeyboard.SLICE_CENTER, pieItems[0]
+               _, center, centerAction = pieItems.pop(0)
+
+               pieButton = qtpie.QPieButton(center)
+               pieButton.set_center(center)
+               keyboardHandler.map_slice_action(center, centerAction)
+               for direction, pieItem, action in pieItems:
+                       pieButton.insertItem(pieItem)
+                       keyboardHandler.map_slice_action(pieItem, action)
+               keyboard.add_pie(row, column, pieButton)
+
+
+class KeyboardHandler(object):
+
+       def __init__(self, keyhandler):
+               self.__keyhandler = keyhandler
+               self.__commandHandlers = {}
+               self.__modifiers = {}
+               self.__sliceActions = {}
+
+               self.register_modifier("Shift")
+               self.register_modifier("Super")
+               self.register_modifier("Control")
+               self.register_modifier("Alt")
+
+       def register_command_handler(self, command, handler):
+               # @todo Look into hooking these up directly to the pie actions
+               self.__commandHandlers["[%s]" % command] = handler
+
+       def unregister_command_handler(self, command):
+               # @todo Look into hooking these up directly to the pie actions
+               del self.__commandHandlers["[%s]" % command]
+
+       def register_modifier(self, modifierName):
+               mod = KeyboardModifier(modifierName)
+               self.register_command_handler(modifierName, mod.on_toggle_lock)
+               self.__modifiers["<%s>" % modifierName] = mod
+
+       def unregister_modifier(self, modifierName):
+               self.unregister_command_handler(modifierName)
+               del self.__modifiers["<%s>" % modifierName]
+
+       def map_slice_action(self, slice, action):
+               callback = lambda direction: self(direction, action)
+               slice.action().triggered.connect(callback)
+               self.__sliceActions[slice] = (action, callback)
+
+       def __call__(self, direction, action):
+               activeModifiers = [
+                       mod.name
+                       for mod in self.__modifiers.itervalues()
+                               if mod.isActive
+               ]
+
+               needResetOnce = False
+               if action.startswith("[") and action.endswith("]"):
+                       commandName = action[1:-1]
+                       if action in self.__commandHandlers:
+                               self.__commandHandlers[action](commandName, activeModifiers)
+                               needResetOnce = True
+                       else:
+                               warnings.warn("Unknown command: [%s]" % commandName)
+               elif action.startswith("<") and action.endswith(">"):
+                       modName = action[1:-1]
+                       for mod in self.__modifiers.itervalues():
+                               if mod.name == modName:
+                                       mod.on_toggle_once()
+                                       break
+                       else:
+                               warnings.warn("Unknown modifier: <%s>" % modName)
+               else:
+                       self.__keyhandler(action, activeModifiers)
+                       needResetOnce = True
+
+               if needResetOnce:
+                       for mod in self.__modifiers.itervalues():
+                               mod.reset_once()
diff --git a/gonvert/util/qui_utils.py b/gonvert/util/qui_utils.py
new file mode 100644 (file)
index 0000000..11b3453
--- /dev/null
@@ -0,0 +1,419 @@
+import sys
+import contextlib
+import datetime
+import logging
+
+import qt_compat
+QtCore = qt_compat.QtCore
+QtGui = qt_compat.import_module("QtGui")
+
+import misc
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+@contextlib.contextmanager
+def notify_error(log):
+       try:
+               yield
+       except:
+               log.push_exception()
+
+
+@contextlib.contextmanager
+def notify_busy(log, message):
+       log.push_busy(message)
+       try:
+               yield
+       finally:
+               log.pop(message)
+
+
+class ErrorMessage(object):
+
+       LEVEL_ERROR = 0
+       LEVEL_BUSY = 1
+       LEVEL_INFO = 2
+
+       def __init__(self, message, level):
+               self._message = message
+               self._level = level
+               self._time = datetime.datetime.now()
+
+       @property
+       def level(self):
+               return self._level
+
+       @property
+       def message(self):
+               return self._message
+
+       def __repr__(self):
+               return "%s.%s(%r, %r)" % (__name__, self.__class__.__name__, self._message, self._level)
+
+
+class QErrorLog(QtCore.QObject):
+
+       messagePushed = qt_compat.Signal()
+       messagePopped = qt_compat.Signal()
+
+       def __init__(self):
+               QtCore.QObject.__init__(self)
+               self._messages = []
+
+       def push_busy(self, message):
+               _moduleLogger.info("Entering state: %s" % message)
+               self._push_message(message, ErrorMessage.LEVEL_BUSY)
+
+       def push_message(self, message):
+               self._push_message(message, ErrorMessage.LEVEL_INFO)
+
+       def push_error(self, message):
+               self._push_message(message, ErrorMessage.LEVEL_ERROR)
+
+       def push_exception(self):
+               userMessage = str(sys.exc_info()[1])
+               _moduleLogger.exception(userMessage)
+               self.push_error(userMessage)
+
+       def pop(self, message = None):
+               if message is None:
+                       del self._messages[0]
+               else:
+                       _moduleLogger.info("Exiting state: %s" % message)
+                       messageIndex = [
+                               i
+                               for (i, error) in enumerate(self._messages)
+                               if error.message == message
+                       ]
+                       # Might be removed out of order
+                       if messageIndex:
+                               del self._messages[messageIndex[0]]
+               self.messagePopped.emit()
+
+       def peek_message(self):
+               return self._messages[0]
+
+       def _push_message(self, message, level):
+               self._messages.append(ErrorMessage(message, level))
+               # Sort is defined as stable, so this should be fine
+               self._messages.sort(key=lambda x: x.level)
+               self.messagePushed.emit()
+
+       def __len__(self):
+               return len(self._messages)
+
+
+class ErrorDisplay(object):
+
+       _SENTINEL_ICON = QtGui.QIcon()
+
+       def __init__(self, errorLog):
+               self._errorLog = errorLog
+               self._errorLog.messagePushed.connect(self._on_message_pushed)
+               self._errorLog.messagePopped.connect(self._on_message_popped)
+
+               self._icons = None
+               self._severityLabel = QtGui.QLabel()
+               self._severityLabel.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+               self._message = QtGui.QLabel()
+               self._message.setText("Boo")
+               self._message.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+               self._message.setWordWrap(True)
+
+               self._closeLabel = None
+
+               self._controlLayout = QtGui.QHBoxLayout()
+               self._controlLayout.addWidget(self._severityLabel, 1, QtCore.Qt.AlignCenter)
+               self._controlLayout.addWidget(self._message, 1000)
+
+               self._widget = QtGui.QWidget()
+               self._widget.setLayout(self._controlLayout)
+               self._widget.hide()
+
+       @property
+       def toplevel(self):
+               return self._widget
+
+       def _show_error(self):
+               if self._icons is None:
+                       self._icons = {
+                               ErrorMessage.LEVEL_BUSY:
+                                       get_theme_icon(
+                                               #("process-working", "view-refresh", "general_refresh", "gtk-refresh")
+                                               ("view-refresh", "general_refresh", "gtk-refresh", )
+                                       ).pixmap(32, 32),
+                               ErrorMessage.LEVEL_INFO:
+                                       get_theme_icon(
+                                               ("dialog-information", "general_notes", "gtk-info")
+                                       ).pixmap(32, 32),
+                               ErrorMessage.LEVEL_ERROR:
+                                       get_theme_icon(
+                                               ("dialog-error", "app_install_error", "gtk-dialog-error")
+                                       ).pixmap(32, 32),
+                       }
+               if self._closeLabel is None:
+                       closeIcon = get_theme_icon(("window-close", "general_close", "gtk-close"), self._SENTINEL_ICON)
+                       if closeIcon is not self._SENTINEL_ICON:
+                               self._closeLabel = QtGui.QPushButton(closeIcon, "")
+                       else:
+                               self._closeLabel = QtGui.QPushButton("X")
+                       self._closeLabel.clicked.connect(self._on_close)
+                       self._controlLayout.addWidget(self._closeLabel, 1, QtCore.Qt.AlignCenter)
+               error = self._errorLog.peek_message()
+               self._message.setText(error.message)
+               self._severityLabel.setPixmap(self._icons[error.level])
+               self._widget.show()
+
+       @qt_compat.Slot()
+       @qt_compat.Slot(bool)
+       @misc.log_exception(_moduleLogger)
+       def _on_close(self, checked = False):
+               self._errorLog.pop()
+
+       @qt_compat.Slot()
+       @misc.log_exception(_moduleLogger)
+       def _on_message_pushed(self):
+               self._show_error()
+
+       @qt_compat.Slot()
+       @misc.log_exception(_moduleLogger)
+       def _on_message_popped(self):
+               if len(self._errorLog) == 0:
+                       self._message.setText("")
+                       self._widget.hide()
+               else:
+                       self._show_error()
+
+
+class QHtmlDelegate(QtGui.QStyledItemDelegate):
+
+       UNDEFINED_SIZE = -1
+
+       def __init__(self, *args, **kwd):
+               QtGui.QStyledItemDelegate.__init__(*((self, ) + args), **kwd)
+               self._width = self.UNDEFINED_SIZE
+
+       def paint(self, painter, option, index):
+               newOption = QtGui.QStyleOptionViewItemV4(option)
+               self.initStyleOption(newOption, index)
+               if newOption.widget is not None:
+                       style = newOption.widget.style()
+               else:
+                       style = QtGui.QApplication.style()
+
+               doc = QtGui.QTextDocument()
+               doc.setHtml(newOption.text)
+               doc.setTextWidth(newOption.rect.width())
+
+               newOption.text = ""
+               style.drawControl(QtGui.QStyle.CE_ItemViewItem, newOption, painter)
+
+               ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
+               if newOption.state & QtGui.QStyle.State_Selected:
+                       ctx.palette.setColor(
+                               QtGui.QPalette.Text,
+                               newOption.palette.color(
+                                       QtGui.QPalette.Active,
+                                       QtGui.QPalette.HighlightedText
+                               )
+                       )
+               else:
+                       ctx.palette.setColor(
+                               QtGui.QPalette.Text,
+                               newOption.palette.color(
+                                       QtGui.QPalette.Active,
+                                       QtGui.QPalette.Text
+                               )
+                       )
+
+               textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, newOption)
+               painter.save()
+               painter.translate(textRect.topLeft())
+               painter.setClipRect(textRect.translated(-textRect.topLeft()))
+               doc.documentLayout().draw(painter, ctx)
+               painter.restore()
+
+       def setWidth(self, width, model):
+               if self._width == width:
+                       return
+               self._width = width
+               for c in xrange(model.rowCount()):
+                       cItem = model.item(c, 0)
+                       for r in xrange(model.rowCount()):
+                               rItem = cItem.child(r, 0)
+                               rIndex = model.indexFromItem(rItem)
+                               self.sizeHintChanged.emit(rIndex)
+                               return
+
+       def sizeHint(self, option, index):
+               newOption = QtGui.QStyleOptionViewItemV4(option)
+               self.initStyleOption(newOption, index)
+
+               doc = QtGui.QTextDocument()
+               doc.setHtml(newOption.text)
+               if self._width != self.UNDEFINED_SIZE:
+                       width = self._width
+               else:
+                       width = newOption.rect.width()
+               doc.setTextWidth(width)
+               size = QtCore.QSize(doc.idealWidth(), doc.size().height())
+               return size
+
+
+class QSignalingMainWindow(QtGui.QMainWindow):
+
+       closed = qt_compat.Signal()
+       hidden = qt_compat.Signal()
+       shown = qt_compat.Signal()
+       resized = qt_compat.Signal()
+
+       def __init__(self, *args, **kwd):
+               QtGui.QMainWindow.__init__(*((self, )+args), **kwd)
+
+       def closeEvent(self, event):
+               val = QtGui.QMainWindow.closeEvent(self, event)
+               self.closed.emit()
+               return val
+
+       def hideEvent(self, event):
+               val = QtGui.QMainWindow.hideEvent(self, event)
+               self.hidden.emit()
+               return val
+
+       def showEvent(self, event):
+               val = QtGui.QMainWindow.showEvent(self, event)
+               self.shown.emit()
+               return val
+
+       def resizeEvent(self, event):
+               val = QtGui.QMainWindow.resizeEvent(self, event)
+               self.resized.emit()
+               return val
+
+def set_current_index(selector, itemText, default = 0):
+       for i in xrange(selector.count()):
+               if selector.itemText(i) == itemText:
+                       selector.setCurrentIndex(i)
+                       break
+       else:
+               itemText.setCurrentIndex(default)
+
+
+def _null_set_stackable(window, isStackable):
+       pass
+
+
+def _maemo_set_stackable(window, isStackable):
+       window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable)
+
+
+try:
+       QtCore.Qt.WA_Maemo5StackedWindow
+       set_stackable = _maemo_set_stackable
+except AttributeError:
+       set_stackable = _null_set_stackable
+
+
+def _null_set_autorient(window, doAutoOrient):
+       pass
+
+
+def _maemo_set_autorient(window, doAutoOrient):
+       window.setAttribute(QtCore.Qt.WA_Maemo5AutoOrientation, doAutoOrient)
+
+
+try:
+       QtCore.Qt.WA_Maemo5AutoOrientation
+       set_autorient = _maemo_set_autorient
+except AttributeError:
+       set_autorient = _null_set_autorient
+
+
+def screen_orientation():
+       geom = QtGui.QApplication.desktop().screenGeometry()
+       if geom.width() <= geom.height():
+               return QtCore.Qt.Vertical
+       else:
+               return QtCore.Qt.Horizontal
+
+
+def _null_set_window_orientation(window, orientation):
+       pass
+
+
+def _maemo_set_window_orientation(window, orientation):
+       if orientation == QtCore.Qt.Vertical:
+               window.setAttribute(QtCore.Qt.WA_Maemo5LandscapeOrientation, False)
+               window.setAttribute(QtCore.Qt.WA_Maemo5PortraitOrientation, True)
+       elif orientation == QtCore.Qt.Horizontal:
+               window.setAttribute(QtCore.Qt.WA_Maemo5LandscapeOrientation, True)
+               window.setAttribute(QtCore.Qt.WA_Maemo5PortraitOrientation, False)
+       elif orientation is None:
+               window.setAttribute(QtCore.Qt.WA_Maemo5LandscapeOrientation, False)
+               window.setAttribute(QtCore.Qt.WA_Maemo5PortraitOrientation, False)
+       else:
+               raise RuntimeError("Unknown orientation: %r" % orientation)
+
+
+try:
+       QtCore.Qt.WA_Maemo5LandscapeOrientation
+       QtCore.Qt.WA_Maemo5PortraitOrientation
+       set_window_orientation = _maemo_set_window_orientation
+except AttributeError:
+       set_window_orientation = _null_set_window_orientation
+
+
+def _null_show_progress_indicator(window, isStackable):
+       pass
+
+
+def _maemo_show_progress_indicator(window, isStackable):
+       window.setAttribute(QtCore.Qt.WA_Maemo5ShowProgressIndicator, isStackable)
+
+
+try:
+       QtCore.Qt.WA_Maemo5ShowProgressIndicator
+       show_progress_indicator = _maemo_show_progress_indicator
+except AttributeError:
+       show_progress_indicator = _null_show_progress_indicator
+
+
+def _null_mark_numbers_preferred(widget):
+       pass
+
+
+def _newqt_mark_numbers_preferred(widget):
+       widget.setInputMethodHints(QtCore.Qt.ImhPreferNumbers)
+
+
+try:
+       QtCore.Qt.ImhPreferNumbers
+       mark_numbers_preferred = _newqt_mark_numbers_preferred
+except AttributeError:
+       mark_numbers_preferred = _null_mark_numbers_preferred
+
+
+def _null_get_theme_icon(iconNames, fallback = None):
+       icon = fallback if fallback is not None else QtGui.QIcon()
+       return icon
+
+
+def _newqt_get_theme_icon(iconNames, fallback = None):
+       for iconName in iconNames:
+               if QtGui.QIcon.hasThemeIcon(iconName):
+                       icon = QtGui.QIcon.fromTheme(iconName)
+                       break
+       else:
+               icon = fallback if fallback is not None else QtGui.QIcon()
+       return icon
+
+
+try:
+       QtGui.QIcon.fromTheme
+       get_theme_icon = _newqt_get_theme_icon
+except AttributeError:
+       get_theme_icon = _null_get_theme_icon
+
diff --git a/gonvert/util/qwrappers.py b/gonvert/util/qwrappers.py
new file mode 100644 (file)
index 0000000..09270cd
--- /dev/null
@@ -0,0 +1,328 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+from __future__ import division
+
+import logging
+
+import qt_compat
+QtCore = qt_compat.QtCore
+QtGui = qt_compat.import_module("QtGui")
+
+import qui_utils
+import misc as misc_utils
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class ApplicationWrapper(object):
+
+       DEFAULT_ORIENTATION = "Default"
+       AUTO_ORIENTATION = "Auto"
+       LANDSCAPE_ORIENTATION = "Landscape"
+       PORTRAIT_ORIENTATION = "Portrait"
+
+       def __init__(self, qapp, constants):
+               self._constants = constants
+               self._qapp = qapp
+               self._clipboard = QtGui.QApplication.clipboard()
+
+               self._errorLog = qui_utils.QErrorLog()
+               self._mainWindow = None
+
+               self._fullscreenAction = QtGui.QAction(None)
+               self._fullscreenAction.setText("Fullscreen")
+               self._fullscreenAction.setCheckable(True)
+               self._fullscreenAction.setShortcut(QtGui.QKeySequence("CTRL+Enter"))
+               self._fullscreenAction.toggled.connect(self._on_toggle_fullscreen)
+
+               self._orientation = self.DEFAULT_ORIENTATION
+               self._orientationAction = QtGui.QAction(None)
+               self._orientationAction.setText("Next Orientation")
+               self._orientationAction.setCheckable(True)
+               self._orientationAction.setShortcut(QtGui.QKeySequence("CTRL+o"))
+               self._orientationAction.triggered.connect(self._on_next_orientation)
+
+               self._logAction = QtGui.QAction(None)
+               self._logAction.setText("Log")
+               self._logAction.setShortcut(QtGui.QKeySequence("CTRL+l"))
+               self._logAction.triggered.connect(self._on_log)
+
+               self._quitAction = QtGui.QAction(None)
+               self._quitAction.setText("Quit")
+               self._quitAction.setShortcut(QtGui.QKeySequence("CTRL+q"))
+               self._quitAction.triggered.connect(self._on_quit)
+
+               self._aboutAction = QtGui.QAction(None)
+               self._aboutAction.setText("About")
+               self._aboutAction.triggered.connect(self._on_about)
+
+               self._qapp.lastWindowClosed.connect(self._on_app_quit)
+               self._mainWindow = self._new_main_window()
+               self._mainWindow.window.destroyed.connect(self._on_child_close)
+
+               self.load_settings()
+
+               self._mainWindow.show()
+               self._idleDelay = QtCore.QTimer()
+               self._idleDelay.setSingleShot(True)
+               self._idleDelay.setInterval(0)
+               self._idleDelay.timeout.connect(self._on_delayed_start)
+               self._idleDelay.start()
+
+       def load_settings(self):
+               raise NotImplementedError("Booh")
+
+       def save_settings(self):
+               raise NotImplementedError("Booh")
+
+       def _new_main_window(self):
+               raise NotImplementedError("Booh")
+
+       @property
+       def qapp(self):
+               return self._qapp
+
+       @property
+       def constants(self):
+               return self._constants
+
+       @property
+       def errorLog(self):
+               return self._errorLog
+
+       @property
+       def fullscreenAction(self):
+               return self._fullscreenAction
+
+       @property
+       def orientationAction(self):
+               return self._orientationAction
+
+       @property
+       def orientation(self):
+               return self._orientation
+
+       @property
+       def logAction(self):
+               return self._logAction
+
+       @property
+       def aboutAction(self):
+               return self._aboutAction
+
+       @property
+       def quitAction(self):
+               return self._quitAction
+
+       def set_orientation(self, orientation):
+               self._orientation = orientation
+               self._mainWindow.update_orientation(self._orientation)
+
+       @classmethod
+       def _next_orientation(cls, current):
+               return {
+                       cls.DEFAULT_ORIENTATION: cls.AUTO_ORIENTATION,
+                       cls.AUTO_ORIENTATION: cls.LANDSCAPE_ORIENTATION,
+                       cls.LANDSCAPE_ORIENTATION: cls.PORTRAIT_ORIENTATION,
+                       cls.PORTRAIT_ORIENTATION: cls.DEFAULT_ORIENTATION,
+               }[current]
+
+       def _close_windows(self):
+               if self._mainWindow is not None:
+                       self.save_settings()
+                       self._mainWindow.window.destroyed.disconnect(self._on_child_close)
+                       self._mainWindow.close()
+                       self._mainWindow = None
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_delayed_start(self):
+               self._mainWindow.start()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_app_quit(self, checked = False):
+               if self._mainWindow is not None:
+                       self.save_settings()
+                       self._mainWindow.destroy()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_child_close(self, obj = None):
+               if self._mainWindow is not None:
+                       self.save_settings()
+                       self._mainWindow = None
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_toggle_fullscreen(self, checked = False):
+               with qui_utils.notify_error(self._errorLog):
+                       self._mainWindow.set_fullscreen(checked)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_next_orientation(self, checked = False):
+               with qui_utils.notify_error(self._errorLog):
+                       self.set_orientation(self._next_orientation(self._orientation))
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_about(self, checked = True):
+               raise NotImplementedError("Booh")
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_log(self, checked = False):
+               with qui_utils.notify_error(self._errorLog):
+                       with open(self._constants._user_logpath_, "r") as f:
+                               logLines = f.xreadlines()
+                               log = "".join(logLines)
+                               self._clipboard.setText(log)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_quit(self, checked = False):
+               with qui_utils.notify_error(self._errorLog):
+                       self._close_windows()
+
+
+class WindowWrapper(object):
+
+       def __init__(self, parent, app):
+               self._app = app
+
+               self._errorDisplay = qui_utils.ErrorDisplay(self._app.errorLog)
+
+               self._layout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight)
+               self._layout.setContentsMargins(0, 0, 0, 0)
+
+               self._superLayout = QtGui.QVBoxLayout()
+               self._superLayout.addWidget(self._errorDisplay.toplevel)
+               self._superLayout.setContentsMargins(0, 0, 0, 0)
+               self._superLayout.addLayout(self._layout)
+
+               centralWidget = QtGui.QWidget()
+               centralWidget.setLayout(self._superLayout)
+               centralWidget.setContentsMargins(0, 0, 0, 0)
+
+               self._window = qui_utils.QSignalingMainWindow(parent)
+               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
+               qui_utils.set_stackable(self._window, True)
+               self._window.setCentralWidget(centralWidget)
+
+               self._closeWindowAction = QtGui.QAction(None)
+               self._closeWindowAction.setText("Close")
+               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
+               self._closeWindowAction.triggered.connect(self._on_close_window)
+
+               self._window.addAction(self._closeWindowAction)
+               self._window.addAction(self._app.quitAction)
+               self._window.addAction(self._app.fullscreenAction)
+               self._window.addAction(self._app.orientationAction)
+               self._window.addAction(self._app.logAction)
+
+       @property
+       def window(self):
+               return self._window
+
+       @property
+       def windowOrientation(self):
+               geom = self._window.size()
+               if geom.width() <= geom.height():
+                       return QtCore.Qt.Vertical
+               else:
+                       return QtCore.Qt.Horizontal
+
+       @property
+       def idealWindowOrientation(self):
+               if self._app.orientation ==  self._app.AUTO_ORIENTATION:
+                       windowOrientation = self.windowOrientation
+               elif self._app.orientation ==  self._app.DEFAULT_ORIENTATION:
+                       windowOrientation = qui_utils.screen_orientation()
+               elif self._app.orientation ==  self._app.LANDSCAPE_ORIENTATION:
+                       windowOrientation = QtCore.Qt.Horizontal
+               elif self._app.orientation ==  self._app.PORTRAIT_ORIENTATION:
+                       windowOrientation = QtCore.Qt.Vertical
+               else:
+                       raise RuntimeError("Bad! No %r for you" % self._app.orientation)
+               return windowOrientation
+
+       def walk_children(self):
+               return ()
+
+       def start(self):
+               pass
+
+       def close(self):
+               for child in self.walk_children():
+                       child.window.destroyed.disconnect(self._on_child_close)
+                       child.close()
+               self._window.close()
+
+       def destroy(self):
+               pass
+
+       def show(self):
+               self._window.show()
+               for child in self.walk_children():
+                       child.show()
+               self.set_fullscreen(self._app.fullscreenAction.isChecked())
+
+       def hide(self):
+               for child in self.walk_children():
+                       child.hide()
+               self._window.hide()
+
+       def set_fullscreen(self, isFullscreen):
+               if self._window.isVisible():
+                       if isFullscreen:
+                               self._window.showFullScreen()
+                       else:
+                               self._window.showNormal()
+               for child in self.walk_children():
+                       child.set_fullscreen(isFullscreen)
+
+       def update_orientation(self, orientation):
+               if orientation == self._app.DEFAULT_ORIENTATION:
+                       qui_utils.set_autorient(self.window, False)
+                       qui_utils.set_window_orientation(self.window, None)
+               elif orientation == self._app.AUTO_ORIENTATION:
+                       qui_utils.set_autorient(self.window, True)
+                       qui_utils.set_window_orientation(self.window, None)
+               elif orientation == self._app.LANDSCAPE_ORIENTATION:
+                       qui_utils.set_autorient(self.window, False)
+                       qui_utils.set_window_orientation(self.window, QtCore.Qt.Horizontal)
+               elif orientation == self._app.PORTRAIT_ORIENTATION:
+                       qui_utils.set_autorient(self.window, False)
+                       qui_utils.set_window_orientation(self.window, QtCore.Qt.Vertical)
+               else:
+                       raise RuntimeError("Unknown orientation: %r" % orientation)
+               for child in self.walk_children():
+                       child.update_orientation(orientation)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_child_close(self, obj = None):
+               raise NotImplementedError("Booh")
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_window(self, checked = True):
+               with qui_utils.notify_error(self._errorLog):
+                       self.close()
+
+
+class AutoFreezeWindowFeature(object):
+
+       def __init__(self, app, window):
+               self._app = app
+               self._window = window
+               self._app.qapp.focusChanged.connect(self._on_focus_changed)
+               if self._app.qapp.focusWidget() is not None:
+                       self._window.setUpdatesEnabled(True)
+               else:
+                       self._window.setUpdatesEnabled(False)
+
+       def close(self):
+               self._app.qapp.focusChanged.disconnect(self._on_focus_changed)
+               self._window.setUpdatesEnabled(True)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_focus_changed(self, oldWindow, newWindow):
+               with qui_utils.notify_error(self._app.errorLog):
+                       if oldWindow is None and newWindow is not None:
+                               self._window.setUpdatesEnabled(True)
+                       elif oldWindow is not None and newWindow is None:
+                               self._window.setUpdatesEnabled(False)
diff --git a/gonvert/util/time_utils.py b/gonvert/util/time_utils.py
new file mode 100644 (file)
index 0000000..90ec84d
--- /dev/null
@@ -0,0 +1,94 @@
+from datetime import tzinfo, timedelta, datetime
+
+ZERO = timedelta(0)
+HOUR = timedelta(hours=1)
+
+
+def first_sunday_on_or_after(dt):
+       days_to_go = 6 - dt.weekday()
+       if days_to_go:
+               dt += timedelta(days_to_go)
+       return dt
+
+
+# US DST Rules
+#
+# This is a simplified (i.e., wrong for a few cases) set of rules for US
+# DST start and end times. For a complete and up-to-date set of DST rules
+# and timezone definitions, visit the Olson Database (or try pytz):
+# http://www.twinsun.com/tz/tz-link.htm
+# http://sourceforge.net/projects/pytz/ (might not be up-to-date)
+#
+# In the US, since 2007, DST starts at 2am (standard time) on the second
+# Sunday in March, which is the first Sunday on or after Mar 8.
+DSTSTART_2007 = datetime(1, 3, 8, 2)
+# and ends at 2am (DST time; 1am standard time) on the first Sunday of Nov.
+DSTEND_2007 = datetime(1, 11, 1, 1)
+# From 1987 to 2006, DST used to start at 2am (standard time) on the first
+# Sunday in April and to end at 2am (DST time; 1am standard time) on the last
+# Sunday of October, which is the first Sunday on or after Oct 25.
+DSTSTART_1987_2006 = datetime(1, 4, 1, 2)
+DSTEND_1987_2006 = datetime(1, 10, 25, 1)
+# From 1967 to 1986, DST used to start at 2am (standard time) on the last
+# Sunday in April (the one on or after April 24) and to end at 2am (DST time;
+# 1am standard time) on the last Sunday of October, which is the first Sunday
+# on or after Oct 25.
+DSTSTART_1967_1986 = datetime(1, 4, 24, 2)
+DSTEND_1967_1986 = DSTEND_1987_2006
+
+
+class USTimeZone(tzinfo):
+
+       def __init__(self, hours, reprname, stdname, dstname):
+               self.stdoffset = timedelta(hours=hours)
+               self.reprname = reprname
+               self.stdname = stdname
+               self.dstname = dstname
+
+       def __repr__(self):
+               return self.reprname
+
+       def tzname(self, dt):
+               if self.dst(dt):
+                       return self.dstname
+               else:
+                       return self.stdname
+
+       def utcoffset(self, dt):
+               return self.stdoffset + self.dst(dt)
+
+       def dst(self, dt):
+               if dt is None or dt.tzinfo is None:
+                       # An exception may be sensible here, in one or both cases.
+                       # It depends on how you want to treat them.  The default
+                       # fromutc() implementation (called by the default astimezone()
+                       # implementation) passes a datetime with dt.tzinfo is self.
+                       return ZERO
+               assert dt.tzinfo is self
+
+               # Find start and end times for US DST. For years before 1967, return
+               # ZERO for no DST.
+               if 2006 < dt.year:
+                       dststart, dstend = DSTSTART_2007, DSTEND_2007
+               elif 1986 < dt.year < 2007:
+                       dststart, dstend = DSTSTART_1987_2006, DSTEND_1987_2006
+               elif 1966 < dt.year < 1987:
+                       dststart, dstend = DSTSTART_1967_1986, DSTEND_1967_1986
+               else:
+                       return ZERO
+
+               start = first_sunday_on_or_after(dststart.replace(year=dt.year))
+               end = first_sunday_on_or_after(dstend.replace(year=dt.year))
+
+               # Can't compare naive to aware objects, so strip the timezone from
+               # dt first.
+               if start <= dt.replace(tzinfo=None) < end:
+                       return HOUR
+               else:
+                       return ZERO
+
+
+Eastern  = USTimeZone(-5, "Eastern",  "EST", "EDT")
+Central  = USTimeZone(-6, "Central",  "CST", "CDT")
+Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
+Pacific  = USTimeZone(-8, "Pacific",  "PST", "PDT")
diff --git a/gonvert/util/tp_utils.py b/gonvert/util/tp_utils.py
new file mode 100644 (file)
index 0000000..7c55c42
--- /dev/null
@@ -0,0 +1,220 @@
+#!/usr/bin/env python
+
+import logging
+
+import dbus
+import telepathy
+
+import util.go_utils as gobject_utils
+import misc
+
+
+_moduleLogger = logging.getLogger(__name__)
+DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'
+
+
+class WasMissedCall(object):
+
+       def __init__(self, bus, conn, chan, on_success, on_error):
+               self.__on_success = on_success
+               self.__on_error = on_error
+
+               self._requested = None
+               self._didMembersChange = False
+               self._didClose = False
+               self._didReport = False
+
+               self._onTimeout = gobject_utils.Timeout(self._on_timeout)
+               self._onTimeout.start(seconds=60)
+
+               chan[telepathy.interfaces.CHANNEL_INTERFACE_GROUP].connect_to_signal(
+                       "MembersChanged",
+                       self._on_members_changed,
+               )
+
+               chan[telepathy.interfaces.CHANNEL].connect_to_signal(
+                       "Closed",
+                       self._on_closed,
+               )
+
+               chan[DBUS_PROPERTIES].GetAll(
+                       telepathy.interfaces.CHANNEL_INTERFACE,
+                       reply_handler = self._on_got_all,
+                       error_handler = self._on_error,
+               )
+
+       def cancel(self):
+               self._report_error("by request")
+
+       def _report_missed_if_ready(self):
+               if self._didReport:
+                       pass
+               elif self._requested is not None and (self._didMembersChange or self._didClose):
+                       if self._requested:
+                               self._report_error("wrong direction")
+                       elif self._didClose:
+                               self._report_success()
+                       else:
+                               self._report_error("members added")
+               else:
+                       if self._didClose:
+                               self._report_error("closed too early")
+
+       def _report_success(self):
+               assert not self._didReport, "Double reporting a missed call"
+               self._didReport = True
+               self._onTimeout.cancel()
+               self.__on_success(self)
+
+       def _report_error(self, reason):
+               assert not self._didReport, "Double reporting a missed call"
+               self._didReport = True
+               self._onTimeout.cancel()
+               self.__on_error(self, reason)
+
+       @misc.log_exception(_moduleLogger)
+       def _on_got_all(self, properties):
+               self._requested = properties["Requested"]
+               self._report_missed_if_ready()
+
+       @misc.log_exception(_moduleLogger)
+       def _on_members_changed(self, message, added, removed, lp, rp, actor, reason):
+               if added:
+                       self._didMembersChange = True
+                       self._report_missed_if_ready()
+
+       @misc.log_exception(_moduleLogger)
+       def _on_closed(self):
+               self._didClose = True
+               self._report_missed_if_ready()
+
+       @misc.log_exception(_moduleLogger)
+       def _on_error(self, *args):
+               self._report_error(args)
+
+       @misc.log_exception(_moduleLogger)
+       def _on_timeout(self):
+               self._report_error("timeout")
+               return False
+
+
+class NewChannelSignaller(object):
+
+       def __init__(self, on_new_channel):
+               self._sessionBus = dbus.SessionBus()
+               self._on_user_new_channel = on_new_channel
+
+       def start(self):
+               self._sessionBus.add_signal_receiver(
+                       self._on_new_channel,
+                       "NewChannel",
+                       "org.freedesktop.Telepathy.Connection",
+                       None,
+                       None
+               )
+
+       def stop(self):
+               self._sessionBus.remove_signal_receiver(
+                       self._on_new_channel,
+                       "NewChannel",
+                       "org.freedesktop.Telepathy.Connection",
+                       None,
+                       None
+               )
+
+       @misc.log_exception(_moduleLogger)
+       def _on_new_channel(
+               self, channelObjectPath, channelType, handleType, handle, supressHandler
+       ):
+               connObjectPath = channel_path_to_conn_path(channelObjectPath)
+               serviceName = path_to_service_name(channelObjectPath)
+               try:
+                       self._on_user_new_channel(
+                               self._sessionBus, serviceName, connObjectPath, channelObjectPath, channelType
+                       )
+               except Exception:
+                       _moduleLogger.exception("Blocking exception from being passed up")
+
+
+class EnableSystemContactIntegration(object):
+
+       ACCOUNT_MGR_NAME = "org.freedesktop.Telepathy.AccountManager"
+       ACCOUNT_MGR_PATH = "/org/freedesktop/Telepathy/AccountManager"
+       ACCOUNT_MGR_IFACE_QUERY = "com.nokia.AccountManager.Interface.Query"
+       ACCOUNT_IFACE_COMPAT = "com.nokia.Account.Interface.Compat"
+       ACCOUNT_IFACE_COMPAT_PROFILE = "com.nokia.Account.Interface.Compat.Profile"
+       DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'
+
+       def __init__(self, profileName):
+               self._bus = dbus.SessionBus()
+               self._profileName = profileName
+
+       def start(self):
+               self._accountManager = self._bus.get_object(
+                       self.ACCOUNT_MGR_NAME,
+                       self.ACCOUNT_MGR_PATH,
+               )
+               self._accountManagerQuery = dbus.Interface(
+                       self._accountManager,
+                       dbus_interface=self.ACCOUNT_MGR_IFACE_QUERY,
+               )
+
+               self._accountManagerQuery.FindAccounts(
+                       {
+                               self.ACCOUNT_IFACE_COMPAT_PROFILE: self._profileName,
+                       },
+                       reply_handler = self._on_found_accounts_reply,
+                       error_handler = self._on_error,
+               )
+
+       @misc.log_exception(_moduleLogger)
+       def _on_found_accounts_reply(self, accountObjectPaths):
+               for accountObjectPath in accountObjectPaths:
+                       print accountObjectPath
+                       account = self._bus.get_object(
+                               self.ACCOUNT_MGR_NAME,
+                               accountObjectPath,
+                       )
+                       accountProperties = dbus.Interface(
+                               account,
+                               self.DBUS_PROPERTIES,
+                       )
+                       accountProperties.Set(
+                               self.ACCOUNT_IFACE_COMPAT,
+                               "SecondaryVCardFields",
+                               ["TEL"],
+                               reply_handler = self._on_field_set,
+                               error_handler = self._on_error,
+                       )
+
+       @misc.log_exception(_moduleLogger)
+       def _on_field_set(self):
+               _moduleLogger.info("SecondaryVCardFields Set")
+
+       @misc.log_exception(_moduleLogger)
+       def _on_error(self, error):
+               _moduleLogger.error("%r" % (error, ))
+
+
+def channel_path_to_conn_path(channelObjectPath):
+       """
+       >>> channel_path_to_conn_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
+       '/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME'
+       """
+       return channelObjectPath.rsplit("/", 1)[0]
+
+
+def path_to_service_name(path):
+       """
+       >>> path_to_service_name("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
+       'org.freedesktop.Telepathy.ConnectionManager.theonering.gv.USERNAME'
+       """
+       return ".".join(path[1:].split("/")[0:7])
+
+
+def cm_from_path(path):
+       """
+       >>> cm_from_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
+       'theonering'
+       """
+       return path[1:].split("/")[4]
diff --git a/gonvert/windows.py b/gonvert/windows.py
new file mode 100644 (file)
index 0000000..6aefe7e
--- /dev/null
@@ -0,0 +1,340 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+from __future__ import division
+
+import logging
+
+from PyQt4 import QtGui
+from PyQt4 import QtCore
+
+import constants
+from util import qui_utils
+from util import misc as misc_utils
+import unit_data
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class FavoritesWindow(object):
+
+       def __init__(self, parent, app, source, hidden):
+               self._app = app
+               self._source = list(source)
+               self._hidden = hidden
+
+               self._categories = QtGui.QTreeWidget()
+               self._categories.setHeaderLabels(["Categories"])
+               self._categories.setHeaderHidden(True)
+               self._categories.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
+               if not constants.IS_MAEMO:
+                       self._categories.setAlternatingRowColors(True)
+               self._childWidgets = []
+               for catName in self._source:
+                       twi = QtGui.QTreeWidgetItem(self._categories)
+                       twi.setText(0, catName)
+                       self._childWidgets.append(twi)
+                       if catName in self._hidden:
+                               twi.setCheckState(0, QtCore.Qt.Unchecked)
+                       else:
+                               twi.setCheckState(0, QtCore.Qt.Checked)
+               self._categories.itemChanged.connect(self._on_item_changed)
+
+               self._allButton = QtGui.QPushButton("All")
+               self._allButton.clicked.connect(self._on_select_all)
+               self._invertButton = QtGui.QPushButton("Invert")
+               self._invertButton.clicked.connect(self._on_invert_selection)
+               self._noneButton = QtGui.QPushButton("None")
+               self._noneButton.clicked.connect(self._on_select_none)
+
+               self._buttonLayout = QtGui.QHBoxLayout()
+               self._buttonLayout.addWidget(self._allButton)
+               self._buttonLayout.addWidget(self._invertButton)
+               self._buttonLayout.addWidget(self._noneButton)
+
+               self._layout = QtGui.QVBoxLayout()
+               self._layout.addWidget(self._categories)
+               self._layout.addLayout(self._buttonLayout)
+
+               centralWidget = QtGui.QWidget()
+               centralWidget.setLayout(self._layout)
+
+               self._window = QtGui.QMainWindow(parent)
+               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
+               qui_utils.set_stackable(self._window, True)
+               self._window.setWindowTitle("%s - Favorites" % constants.__pretty_app_name__)
+               self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
+               self._window.setCentralWidget(centralWidget)
+
+               self._closeWindowAction = QtGui.QAction(None)
+               self._closeWindowAction.setText("Close")
+               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
+               self._closeWindowAction.triggered.connect(self._on_close_window)
+
+               if constants.IS_MAEMO:
+                       self._window.addAction(self._closeWindowAction)
+                       self._window.addAction(self._app.quitAction)
+                       self._window.addAction(self._app.fullscreenAction)
+               else:
+                       fileMenu = self._window.menuBar().addMenu("&Units")
+                       fileMenu.addAction(self._closeWindowAction)
+                       fileMenu.addAction(self._app.quitAction)
+
+                       viewMenu = self._window.menuBar().addMenu("&View")
+                       viewMenu.addAction(self._app.fullscreenAction)
+
+               self._window.addAction(self._app.logAction)
+
+               self.set_fullscreen(self._app.fullscreenAction.isChecked())
+               self._window.show()
+
+       @property
+       def window(self):
+               return self._window
+
+       def show(self):
+               self._window.show()
+
+       def hide(self):
+               self._window.hide()
+
+       def close(self):
+               self._window.close()
+
+       def set_fullscreen(self, isFullscreen):
+               if isFullscreen:
+                       self._window.showFullScreen()
+               else:
+                       self._window.showNormal()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_select_all(self, checked = False):
+               for child in self._childWidgets:
+                       child.setCheckState(0, QtCore.Qt.Checked)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_invert_selection(self, checked = False):
+               for child in self._childWidgets:
+                       state = child.checkState(0)
+                       if state == QtCore.Qt.Unchecked:
+                               newState = QtCore.Qt.Checked
+                       elif state == QtCore.Qt.Checked:
+                               newState = QtCore.Qt.Unchecked
+                       else:
+                               raise RuntimeError("Bad check state %r" % state)
+                       child.setCheckState(0, newState)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_select_none(self, checked = False):
+               for child in self._childWidgets:
+                       child.setCheckState(0, QtCore.Qt.Unchecked)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_item_changed(self, item, column):
+               state = item.checkState(column)
+               if state == QtCore.Qt.Unchecked:
+                       name = str(item.text(column))
+                       self._hidden.add(name)
+               elif state == QtCore.Qt.Checked:
+                       name = str(item.text(column))
+                       self._hidden.remove(name)
+               else:
+                       raise RuntimeError("Bad check state %r" % state)
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_window(self, checked = True):
+               self.close()
+
+
+class QuickJump(object):
+
+       MINIMAL_ENTRY = 3
+
+       def __init__(self, parent, app):
+               self._app = app
+
+               self._searchLabel = QtGui.QLabel("Search:")
+               self._searchEntry = QtGui.QLineEdit("")
+               self._searchEntry.textEdited.connect(self._on_search_edited)
+
+               self._entryLayout = QtGui.QHBoxLayout()
+               self._entryLayout.addWidget(self._searchLabel)
+               self._entryLayout.addWidget(self._searchEntry)
+
+               self._resultsBox = QtGui.QTreeWidget()
+               self._resultsBox.setHeaderLabels(["Categories", "Units"])
+               self._resultsBox.setHeaderHidden(True)
+               if not constants.IS_MAEMO:
+                       self._resultsBox.setAlternatingRowColors(True)
+               self._resultsBox.itemClicked.connect(self._on_result_clicked)
+
+               self._layout = QtGui.QVBoxLayout()
+               self._layout.addLayout(self._entryLayout)
+               self._layout.addWidget(self._resultsBox)
+
+               centralWidget = QtGui.QWidget()
+               centralWidget.setLayout(self._layout)
+
+               self._window = QtGui.QMainWindow(parent)
+               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
+               qui_utils.set_stackable(self._window, True)
+               self._window.setWindowTitle("%s - Quick Jump" % constants.__pretty_app_name__)
+               self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
+               self._window.setCentralWidget(centralWidget)
+
+               self._closeWindowAction = QtGui.QAction(None)
+               self._closeWindowAction.setText("Close")
+               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
+               self._closeWindowAction.triggered.connect(self._on_close_window)
+
+               if constants.IS_MAEMO:
+                       self._window.addAction(self._closeWindowAction)
+                       self._window.addAction(self._app.quitAction)
+                       self._window.addAction(self._app.fullscreenAction)
+               else:
+                       fileMenu = self._window.menuBar().addMenu("&Units")
+                       fileMenu.addAction(self._closeWindowAction)
+                       fileMenu.addAction(self._app.quitAction)
+
+                       viewMenu = self._window.menuBar().addMenu("&View")
+                       viewMenu.addAction(self._app.fullscreenAction)
+
+               self._window.addAction(self._app.logAction)
+
+               self.set_fullscreen(self._app.fullscreenAction.isChecked())
+               self._window.show()
+
+       @property
+       def window(self):
+               return self._window
+
+       def show(self):
+               self._window.show()
+
+       def hide(self):
+               self._window.hide()
+
+       def close(self):
+               self._window.close()
+
+       def set_fullscreen(self, isFullscreen):
+               if isFullscreen:
+                       self._window.showFullScreen()
+               else:
+                       self._window.showNormal()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_window(self, checked = True):
+               self.close()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_result_clicked(self, item, columnIndex):
+               categoryName = unicode(item.text(0))
+               unitName = unicode(item.text(1))
+               catWindow = self._app.request_category()
+               unitsWindow = catWindow.select_category(categoryName)
+               unitsWindow.select_unit(unitName)
+               self.close()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_search_edited(self, *args):
+               userInput = self._searchEntry.text()
+               if len(userInput) <  self.MINIMAL_ENTRY:
+                       return
+
+               self._resultsBox.clear()
+               lowerInput = str(userInput).lower()
+               for catIndex, category in enumerate(unit_data.UNIT_CATEGORIES):
+                       units = unit_data.get_units(category)
+                       for unitIndex, unit in enumerate(units):
+                               loweredUnit = unit.lower()
+                               if lowerInput in loweredUnit:
+                                       twi = QtGui.QTreeWidgetItem(self._resultsBox)
+                                       twi.setText(0, category)
+                                       twi.setText(1, unit)
+
+
+class Recent(object):
+
+       def __init__(self, parent, app):
+               self._app = app
+
+               self._resultsBox = QtGui.QTreeWidget()
+               self._resultsBox.setHeaderLabels(["Categories", "Units"])
+               self._resultsBox.setHeaderHidden(True)
+               if not constants.IS_MAEMO:
+                       self._resultsBox.setAlternatingRowColors(True)
+               self._resultsBox.itemClicked.connect(self._on_result_clicked)
+
+               self._layout = QtGui.QVBoxLayout()
+               self._layout.addWidget(self._resultsBox)
+
+               centralWidget = QtGui.QWidget()
+               centralWidget.setLayout(self._layout)
+
+               self._window = QtGui.QMainWindow(parent)
+               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
+               qui_utils.set_stackable(self._window, True)
+               self._window.setWindowTitle("%s - Recent" % constants.__pretty_app_name__)
+               self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
+               self._window.setCentralWidget(centralWidget)
+
+               for cat, unit in self._app.get_recent():
+                       twi = QtGui.QTreeWidgetItem(self._resultsBox)
+                       twi.setText(0, cat)
+                       twi.setText(1, unit)
+
+               self._closeWindowAction = QtGui.QAction(None)
+               self._closeWindowAction.setText("Close")
+               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
+               self._closeWindowAction.triggered.connect(self._on_close_window)
+
+               if constants.IS_MAEMO:
+                       self._window.addAction(self._closeWindowAction)
+                       self._window.addAction(self._app.quitAction)
+                       self._window.addAction(self._app.fullscreenAction)
+               else:
+                       fileMenu = self._window.menuBar().addMenu("&Units")
+                       fileMenu.addAction(self._closeWindowAction)
+                       fileMenu.addAction(self._app.quitAction)
+
+                       viewMenu = self._window.menuBar().addMenu("&View")
+                       viewMenu.addAction(self._app.fullscreenAction)
+
+               self._window.addAction(self._app.logAction)
+
+               self.set_fullscreen(self._app.fullscreenAction.isChecked())
+               self._window.show()
+
+       @property
+       def window(self):
+               return self._window
+
+       def show(self):
+               self._window.show()
+
+       def hide(self):
+               self._window.hide()
+
+       def close(self):
+               self._window.close()
+
+       def set_fullscreen(self, isFullscreen):
+               if isFullscreen:
+                       self._window.showFullScreen()
+               else:
+                       self._window.showNormal()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_close_window(self, checked = True):
+               self.close()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_result_clicked(self, item, columnIndex):
+               categoryName = unicode(item.text(0))
+               unitName = unicode(item.text(1))
+               catWindow = self._app.request_category()
+               unitsWindow = catWindow.select_category(categoryName)
+               unitsWindow.select_unit(unitName)
+               self.close()
diff --git a/setup.py b/setup.py
new file mode 100755 (executable)
index 0000000..c1418f5
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+reload(sys).setdefaultencoding("UTF-8")
+import os
+
+try:
+       from sdist_maemo import sdist_maemo as _sdist_maemo
+       sdist_maemo = _sdist_maemo
+except ImportError:
+       sdist_maemo = None
+       print 'sdist_maemo command not available'
+
+from distutils.core import setup
+
+
+#[[[cog
+#      import cog
+#      from gonvert import constants
+#      cog.outl('APP_NAME="%s"' % constants.__app_name__)
+#      cog.outl('PRETTY_APP_NAME="%s"' % constants.__pretty_app_name__)
+#      cog.outl('VERSION="%s"' % constants.__version__)
+#      cog.outl('BUILD="%s"' % constants.__build__)
+#      cog.outl('DESKTOP_FILE_PATH="%s"' % DESKTOP_FILE_PATH)
+#      cog.outl('INPUT_DESKTOP_FILE="%s"' % INPUT_DESKTOP_FILE)
+#      cog.outl('ICON_CATEGORY="%s"' % ICON_CATEGORY)
+#      cog.outl('ICON_SIZES=[%s]' % ICON_SIZES)
+#]]]
+APP_NAME="gonvert"
+PRETTY_APP_NAME="Gonvert"
+VERSION="1.1.4"
+BUILD="0"
+DESKTOP_FILE_PATH="/usr/share/applications"
+INPUT_DESKTOP_FILE="data/ubuntu/gonvert.desktop"
+ICON_CATEGORY="apps"
+ICON_SIZES=[32,48]
+#[[[end]]] (checksum: fefe5943a0176aa799fab0d963c33832)
+
+CHANGES = """Switching from py2deb.py to sdist_maemo
+""".strip()
+BUGTRACKER_URL = "https://bugs.maemo.org/enter_bug.cgi?product=Gonvert"
+
+
+def is_package(path):
+       return (
+               os.path.isdir(path) and
+               os.path.isfile(os.path.join(path, '__init__.py'))
+       )
+
+
+def find_packages(path, base="", includeRoot=False):
+       """ Find all packages in path """
+       if includeRoot:
+               assert not base, "Base not supported with includeRoot: %r" % base
+               rootPath, module_name = os.path.split(path)
+               yield module_name
+               base = module_name
+       for item in os.listdir(path):
+               dir = os.path.join(path, item)
+               if is_package( dir ):
+                       if base:
+                               module_name = "%(base)s.%(item)s" % vars()
+                       else:
+                               module_name = item
+                       yield module_name
+                       for mname in find_packages(dir, module_name):
+                               yield mname
+
+
+setup(
+       name=APP_NAME,
+       version=VERSION,
+       description="Unit Conversions",
+       long_description="A conversion utility that allows conversion between many units like CGS, Ancient, Imperial with many categories like length, mass, numbers, etc. All units converted values shown at once as you type",
+       author="Ed Page",
+       author_email="eopage@byu.net",
+       maintainer="Ed Page",
+       maintainer_email="eopage@byu.net",
+       url="http://gonvert.garage.maemo.org",
+       license="GNU GPLv2",
+       scripts=[
+               "Gonvert",
+       ],
+       packages=list(find_packages(APP_NAME, includeRoot=True)),
+       package_data={
+       },
+       data_files=[
+               (DESKTOP_FILE_PATH, [INPUT_DESKTOP_FILE]),
+               ("/usr/share/icons/hicolor/scalable/%s" % ICON_CATEGORY, ["data/%s.svg" % APP_NAME]),
+       ] +
+       [
+               (
+                       "/usr/share/icons/hicolor/%sx%s/%s" % (size, size, ICON_CATEGORY),
+                       ["data/icons/%s/%s.png" % (size, APP_NAME)]
+               )
+               for size in ICON_SIZES
+       ],
+       requires=[
+               "PySide",
+               "simplejson",
+       ],
+       cmdclass={
+               'sdist_ubuntu': sdist_maemo,
+               'sdist_diablo': sdist_maemo,
+               'sdist_fremantle': sdist_maemo,
+               'sdist_harmattan': sdist_maemo,
+       },
+       options={
+               "sdist_ubuntu": {
+                       "debian_package": APP_NAME,
+                       "section": "science",
+                       "copyright": "gpl",
+                       "changelog": CHANGES,
+                       "buildversion": str(BUILD),
+                       "depends": "python, python-pyside.qtcore, python-pyside.qtgui, python-simplejson",
+                       "architecture": "any",
+               },
+               "sdist_diablo": {
+                       "debian_package": APP_NAME,
+                       "Maemo_Display_Name": PRETTY_APP_NAME,
+                       #"Maemo_Upgrade_Description": CHANGES,
+                       "Maemo_Bugtracker": BUGTRACKER_URL,
+                       "Maemo_Icon_26": "data/icons/26/%s.png" % APP_NAME,
+                       "section": "user/science",
+                       "copyright": "gpl",
+                       "changelog": CHANGES,
+                       "buildversion": str(BUILD),
+                       "depends": "python2.5, python2.5-qt4-core, python2.5-qt4-gui, python-simplejson",
+                       "architecture": "any",
+               },
+               "sdist_fremantle": {
+                       "debian_package": APP_NAME,
+                       "Maemo_Display_Name": PRETTY_APP_NAME,
+                       #"Maemo_Upgrade_Description": CHANGES,
+                       "Maemo_Bugtracker": BUGTRACKER_URL,
+                       "Maemo_Icon_26": "data/icons/48/%s.png" % APP_NAME,
+                       "section": "user/science",
+                       "copyright": "gpl",
+                       "changelog": CHANGES,
+                       "buildversion": str(BUILD),
+                       "depends": "python, python-pyside.qtcore, python-pyside.qtgui, python-pyside.qtmaemo5, python-simplejson",
+                       "architecture": "any",
+               },
+               "sdist_harmattan": {
+                       "debian_package": APP_NAME,
+                       "Maemo_Display_Name": PRETTY_APP_NAME,
+                       #"Maemo_Upgrade_Description": CHANGES,
+                       "Maemo_Bugtracker": BUGTRACKER_URL,
+                       "Maemo_Icon_26": "data/icons/48/%s.png" % APP_NAME,
+                       "MeeGo_Desktop_Entry_Filename": APP_NAME,
+                       #"MeeGo_Desktop_Entry": "",
+                       "section": "user/science",
+                       "copyright": "gpl",
+                       "changelog": CHANGES,
+                       "buildversion": str(BUILD),
+                       "depends": "python, python-pyside.qtcore, python-pyside.qtgui, python-simplejson",
+                       "architecture": "any",
+               },
+               "bdist_rpm": {
+                       "requires": "REPLACEME",
+                       "icon": "data/icons/48/%s.png" % APP_NAME,
+                       "group": "REPLACEME",
+               },
+       },
+)
diff --git a/src b/src
new file mode 120000 (symlink)
index 0000000..3fd0298
--- /dev/null
+++ b/src
@@ -0,0 +1 @@
+gonvert/
\ No newline at end of file
diff --git a/src/__init__.py b/src/__init__.py
deleted file mode 100644 (file)
index 4265cc3..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#!/usr/bin/env python
diff --git a/src/constants.py b/src/constants.py
deleted file mode 100644 (file)
index 63de85a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-import os
-
-__pretty_app_name__ = "Gonvert"
-__app_name__ = "gonvert"
-__version__ = "1.1.4"
-__build__ = 0
-__app_magic__ = 0xdeadbeef
-_data_path_ = os.path.join(os.path.expanduser("~"), ".gonvert")
-_user_settings_ = "%s/settings.json" % _data_path_
-_user_logpath_ = "%s/gonvert.log" % _data_path_
-
-PROFILE_STARTUP = False
-IS_MAEMO = True
diff --git a/src/converters.py b/src/converters.py
deleted file mode 100644 (file)
index 219df1d..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-"""
-All classes for conversions are defined below:
- each class should have one method for converting "to_base and another for converting "from_base"
-the return value is the converted value to or from base
-"""
-
-# For the sake of eval'ing some code
-import math
-
-
-# used for Computer numbers base definitions.
-ALPHA_NUMERIC = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
-
-
-def makeBase(x, base = len(ALPHA_NUMERIC), table=ALPHA_NUMERIC):
-       """
-       Convert from base 10 to any other base.
-       >> makeBase(1, 10)
-       '1'
-       >> makeBase(11, 10)
-       '11'
-       >> makeBase(11, 16)
-       'b'
-       """
-       div, mod = divmod(x, base)
-       if not div:
-               return table[mod]
-       return makeBase(div, base, table) + table[mod]
-
-
-# roman numerals
-roman_group = {
-       1: ('i', 'v'),
-       10: ('x', 'l'),
-       100: ('c', 'd'),
-       1000: ('m', 'A'),
-       10000: ('B', 'C'),
-}
-
-
-# functions that convert Arabic digits to roman numerals
-roman_value = {
-       0: lambda i, v, x: '',
-       1: lambda i, v, x: i,
-       2: lambda i, v, x: i+i,
-       3: lambda i, v, x: i+i+i,
-       4: lambda i, v, x: i+v,
-       5: lambda i, v, x: v,
-       6: lambda i, v, x: v+i,
-       7: lambda i, v, x: v+i+i,
-       8: lambda i, v, x: v+i+i+i,
-       9: lambda i, v, x: i+x,
-}
-
-
-def toroman(n):
-       """
-       convert a decimal number in [1,4000) to a roman numeral
-       >>> toroman(0)
-       >>> toroman(4001)
-       >>> toroman(1)
-       'i'
-       >>> toroman(4)
-       'iv'
-       >>> toroman(5)
-       'v'
-       >>> toroman(10)
-       'x'
-       >>> toroman(15)
-       'xv'
-       """
-       if n < 0:
-               raise NotImplementedError("Value out of roman comprehension")
-       elif n == 0:
-               pass
-       elif n >= 4000:
-               raise NotImplementedError("Value Out of Range")
-
-       base = 1
-       s = ''
-       while n > 0:
-               i, v = roman_group[base]
-               base = base * 10
-               x, l = roman_group[base]
-               digit = n % 10
-               n = (n-digit)/10
-               s = roman_value[digit](i, v, x) + s
-       return s
-
-
-def fromroman(s, rbase = 1):
-       """
-       convert a roman numeral (in lowercase) to a decimal integer
-       >>> fromroman('')
-       0
-       >>> fromroman('x')
-       5
-       >>> fromroman('xv')
-       15
-       """
-       if len(s) == 0:
-               return 0
-       elif rbase > 1000:
-               return 0
-
-       i, v = roman_group[rbase]
-       x, l = roman_group[rbase*10]
-       conversions = [
-               (v+i+i+i, 8),
-               (i+i+i+i, 5),
-               (v+i+i, 7),
-               (i+i+i, 3),
-               (v+i, 6),
-               (i+x, 9),
-               (i+v, 4),
-               (i+i, 2),
-               (i, 1),
-               (v, 5),
-       ]
-       for conversion in conversions:
-               if s.endswith(conversion[0]):
-                       digit = conversion[1]
-                       s = s[:-len(conversion[0])]
-                       break
-       else:
-               digit = 0
-               s = s
-
-       return digit * rbase + fromroman(s, rbase*10)
-
-
-class simple_multiplier(object):
-
-       def to_base(self, value, multiplier):
-               return value * (multiplier)
-
-       def from_base(self, value, multiplier):
-               if multiplier == 0:
-                       return 0.0
-               else:
-                       return value / (multiplier)
-
-
-class simple_inverter(object):
-
-       def to_base(self, value, multiplier):
-               if value == 0:
-                       return 0.0
-               else:
-                       return (multiplier) / value
-
-       def from_base(self, value, multiplier):
-               if value == 0:
-                       return 0.0
-               else:
-                       return (multiplier) / value
-
-
-class simple_gain_offset(object):
-
-       def to_base(self, value, (gain, offset)):
-               return (value * (gain)) + offset
-
-       def from_base(self, value, (gain, offset)):
-               if gain == 0:
-                       return 0.0
-               else:
-                       return (value - offset) / gain
-
-
-class simple_offset_gain(object):
-
-       def to_base(self, value, (offset, gain)):
-               return (value + offset) * gain
-
-       def from_base(self, value, (offset, gain)):
-               if gain == 0:
-                       return 0.0
-               else:
-                       return (value / gain) - offset
-
-
-class slope_offset(object):
-       ''"convert using points on a graph''"
-
-       def to_base(self, value, ((low_in, high_in), (low_out, high_out))):
-               gain = (high_out-low_out)/(high_in-low_in)
-               offset = low_out - gain*low_in
-               return gain*value+offset
-
-       def from_base(self, value, ((low_out, high_out), (low_in, high_in))):
-               gain = (high_out-low_out)/(high_in-low_in)
-               offset = low_out - gain*low_in
-               return gain*value+offset
-
-
-class double_slope_offset(object):
-       ''"convert using points on a graph, graph split into two slopes''"
-
-       def to_base(self, value, ((low1_in, high1_in), (low1_out, high1_out), (low2_in, high2_in), (low2_out, high2_out))):
-               if low1_in <= value <= high1_in:
-                       gain = (high1_out-low1_out)/(high1_in-low1_in)
-                       offset = low1_out - gain*low1_in
-                       return gain*value+offset
-               if low2_in <= value <= high2_in:
-                       gain = (high2_out-low2_out)/(high2_in-low2_in)
-                       offset = low2_out - gain*low2_in
-                       return gain*value+offset
-               return 0.0
-
-       def from_base(self, value, ((low1_in, high1_in), (low1_out, high1_out), (low2_in, high2_in), (low2_out, high2_out))):
-               if low1_out <= value <= high1_out:
-                       gain = (high1_in-low1_in)/(high1_out-low1_out)
-                       offset = low1_in - gain*low1_out
-                       return gain*value+offset
-               if low2_out <= value <= high2_out:
-                       gain = (high2_in-low2_in)/(high2_out-low2_out)
-                       offset = low2_in - gain*low2_out
-                       return gain*value+offset
-               return 0.0
-
-
-class base_converter(object):
-
-       def to_base(self, value, base):
-               """
-               Convert from any base to base 10 (decimal)
-               """
-               # Protection against fractional values
-               value = value.split(".", 1)[0]
-
-               result = 0L #will contain the long base-10 (decimal) number to be returned
-               position = len(value) #length of the string that is to be converted
-               for x in value:
-                       position = position-1
-                       result = long(result + long(long(ALPHA_NUMERIC.find(x))*(long(base)**long(position))))
-               return result
-
-       def from_base(self, value, base):
-               """
-               Convert from decimal to any base
-               """
-               return makeBase(int(value), base)
-
-
-class roman_numeral(object):
-
-       def to_base(self, value, junk):
-               """
-               Convert from roman numeral to base 10 (decimal)
-               """
-               if value == "0":
-                       return 0L
-               else:
-                       return fromroman(value)
-
-       def from_base(self, value, junk):
-               """
-               Convert from decimal to roman numeral
-               """
-               return toroman(int(value))
-
-
-
-class function(object):
-       ''"defined simple function can be as complicated as you like, however, both to/from base must be defined.''"
-
-       #value is assumed to be a string
-       #convert from a defined function to base
-       def to_base(self, value, (to_base, from_base)):
-               leftOfX, rightOfX = to_base.split("x", 1)
-               y = 0 # "undefined" y was driving me nuts
-               exec "y=" + leftOfX + str(value) + rightOfX
-               return y
-
-       def from_base(self, value, (to_base, from_base)):
-               leftOfX, rightOfX = from_base.split("x", 1)
-               y = 0 # "undefined" y was driving me nuts
-               exec "y=" + leftOfX + str(value) + rightOfX
-               return y
-
-
-#--------- function definitions from classes ------------
-m = simple_multiplier()
-inv = simple_inverter()
-gof = simple_gain_offset()
-ofg = simple_offset_gain()
-slo = slope_offset()
-dso = double_slope_offset()
-b = base_converter()
-r = roman_numeral()
-f = function()
diff --git a/src/gonvert.py b/src/gonvert.py
deleted file mode 100755 (executable)
index 10c789e..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/python
-
-import sys
-
-
-sys.path.append("/opt/gonvert/lib")
-
-
-import gonvert_qt
-
-
-if __name__ == "__main__":
-       gonvert_qt.run_gonvert()
diff --git a/src/gonvert_qt.py b/src/gonvert_qt.py
deleted file mode 100755 (executable)
index 561c737..0000000
+++ /dev/null
@@ -1,1584 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: UTF8 -*-
-
-#@todo Research Fn
-#@todo Research optimizations
-
-from __future__ import with_statement
-
-import os
-import math
-import simplejson
-import logging
-import logging.handlers
-
-import util.qt_compat as qt_compat
-QtCore = qt_compat.QtCore
-QtGui = qt_compat.import_module("QtGui")
-
-import constants
-from util import qui_utils
-from util import misc as misc_utils
-import unit_data
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-def split_number(number):
-       if number == 0.0:
-               # Optimize the startup case
-               return "0.", "0"
-
-       try:
-               fractional, integer = math.modf(number)
-       except TypeError:
-               integerDisplay = number
-               fractionalDisplay = ""
-       else:
-               integerDisplay = str(integer)
-               fractionalDisplay = str(fractional)
-               if "e+" in integerDisplay:
-                       integerDisplay = number
-                       fractionalDisplay = ""
-               elif "e-" in fractionalDisplay:
-                       if 0.0 < integer:
-                               integerDisplay = number
-                               fractionalDisplay = ""
-                       else:
-                               integerDisplay = ""
-                               fractionalDisplay = number
-               else:
-                       integerDisplay = integerDisplay[0:-2] + "."
-                       fractionalDisplay = fractionalDisplay[2:]
-
-       return integerDisplay, fractionalDisplay
-
-
-class Gonvert(object):
-
-       _DATA_PATHS = [
-               os.path.dirname(__file__),
-               os.path.join(os.path.dirname(__file__), "../share"),
-               os.path.join(os.path.dirname(__file__), "../data"),
-               '/usr/share/gonvert',
-               '/opt/gonvert/share',
-       ]
-
-       def __init__(self, app):
-               self._dataPath = ""
-               for dataPath in self._DATA_PATHS:
-                       appIconPath = os.path.join(dataPath, "pixmaps", "gonvert.png")
-                       if os.path.isfile(appIconPath):
-                               self._dataPath = dataPath
-                               break
-               else:
-                       raise RuntimeError("UI Descriptor not found!")
-               self._app = app
-               self._appIconPath = appIconPath
-               self._recent = []
-               self._hiddenCategories = set()
-               self._hiddenUnits = {}
-               self._clipboard = QtGui.QApplication.clipboard()
-
-               self._jumpWindow = None
-               self._recentWindow = None
-               self._mainWindow = None
-               self._catWindow = None
-               self._quickWindow = None
-
-               self._on_jump_close = lambda obj = None: self._on_child_close("_jumpWindow", obj)
-               self._on_recent_close = lambda obj = None: self._on_child_close("_recentWindow", obj)
-               self._on_cat_close = lambda obj = None: self._on_child_close("_catWindow", obj)
-               self._on_quick_close = lambda obj = None: self._on_child_close("_quickWindow", obj)
-
-               self._condensedAction = QtGui.QAction(None)
-               self._condensedAction.setText("Condensed View")
-               self._condensedAction.setCheckable(True)
-               self._condensedAction.triggered.connect(self._on_condensed_start)
-
-               self._jumpAction = QtGui.QAction(None)
-               self._jumpAction.setText("Quick Jump")
-               self._jumpAction.setStatusTip("Search for a unit and jump straight to it")
-               self._jumpAction.setToolTip("Search for a unit and jump straight to it")
-               self._jumpAction.setShortcut(QtGui.QKeySequence("CTRL+j"))
-               self._jumpAction.triggered.connect(self._on_jump_start)
-
-               self._recentAction = QtGui.QAction(None)
-               self._recentAction.setText("Recent Units")
-               self._recentAction.setStatusTip("View the recent units")
-               self._recentAction.setToolTip("View the recent units")
-               self._recentAction.setShortcut(QtGui.QKeySequence("CTRL+r"))
-               self._recentAction.triggered.connect(self._on_recent_start)
-
-               self._fullscreenAction = QtGui.QAction(None)
-               self._fullscreenAction.setText("Fullscreen")
-               self._fullscreenAction.setCheckable(True)
-               self._fullscreenAction.setShortcut(QtGui.QKeySequence("CTRL+Enter"))
-               self._fullscreenAction.toggled.connect(self._on_toggle_fullscreen)
-
-               self._showFavoritesAction = QtGui.QAction(None)
-               self._showFavoritesAction.setCheckable(True)
-               self._showFavoritesAction.setText("Favorites Only")
-
-               self._sortActionGroup = QtGui.QActionGroup(None)
-               self._sortByNameAction = QtGui.QAction(self._sortActionGroup)
-               self._sortByNameAction.setText("Sort By Name")
-               self._sortByNameAction.setStatusTip("Sort the units by name")
-               self._sortByNameAction.setToolTip("Sort the units by name")
-               self._sortByNameAction.setCheckable(True)
-               self._sortByValueAction = QtGui.QAction(self._sortActionGroup)
-               self._sortByValueAction.setText("Sort By Value")
-               self._sortByValueAction.setStatusTip("Sort the units by value")
-               self._sortByValueAction.setToolTip("Sort the units by value")
-               self._sortByValueAction.setCheckable(True)
-               self._sortByUnitAction = QtGui.QAction(self._sortActionGroup)
-               self._sortByUnitAction.setText("Sort By Unit")
-               self._sortByUnitAction.setStatusTip("Sort the units by unit")
-               self._sortByUnitAction.setToolTip("Sort the units by unit")
-               self._sortByUnitAction.setCheckable(True)
-
-               self._sortByNameAction.setChecked(True)
-
-               self._logAction = QtGui.QAction(None)
-               self._logAction.setText("Log")
-               self._logAction.setShortcut(QtGui.QKeySequence("CTRL+l"))
-               self._logAction.triggered.connect(self._on_log)
-
-               self._quitAction = QtGui.QAction(None)
-               self._quitAction.setText("Quit")
-               self._quitAction.setShortcut(QtGui.QKeySequence("CTRL+q"))
-               self._quitAction.triggered.connect(self._on_quit)
-
-               self._app.lastWindowClosed.connect(self._on_app_quit)
-               self.load_settings()
-
-               self.request_category()
-               if self._recent:
-                       self._mainWindow.select_category(self._recent[-1][0])
-
-       def request_category(self):
-               if self._condensedAction.isChecked():
-                       if self._catWindow is not None:
-                               self._catWindow.hide()
-
-                       if self._quickWindow is None:
-                               self._quickWindow = QuickConvert(None, self)
-                               self._quickWindow.window.destroyed.connect(self._on_quick_close)
-                       else:
-                               self._quickWindow.show()
-
-                       self._mainWindow = self._quickWindow
-               else:
-                       if self._quickWindow is not None:
-                               self._quickWindow.hide()
-
-                       if self._catWindow is None:
-                               self._catWindow = CategoryWindow(None, self)
-                               self._catWindow.window.destroyed.connect(self._on_cat_close)
-                       else:
-                               self._catWindow.window.show()
-
-                       self._mainWindow = self._catWindow
-
-               return self._mainWindow
-
-       def search_units(self):
-               import windows
-               jumpWindow = windows.QuickJump(None, self)
-               jumpWindow.window.destroyed.connect(self._on_jump_close)
-               self._jumpWindow = jumpWindow
-               return self._jumpWindow
-
-       def show_recent(self):
-               import windows
-               recentWindow = windows.Recent(None, self)
-               recentWindow.window.destroyed.connect(self._on_recent_close)
-               self._recentWindow = recentWindow
-               return self._recentWindow
-
-       def add_recent(self, categoryName, unitName):
-               catUnit = categoryName, unitName
-               try:
-                       self._recent.remove(catUnit)
-               except ValueError:
-                       pass # ignore if its not already in the recent history
-               assert catUnit not in self._recent
-               self._recent.append(catUnit)
-
-       def get_recent_unit(self, categoryName, fromMostRecent = 0):
-               recentUnitName = ""
-               for catName, unitName in reversed(self._recent):
-                       if catName == categoryName:
-                               recentUnitName = unitName
-                               if fromMostRecent <= 0:
-                                       break
-                               else:
-                                       fromMostRecent -= 1
-               return recentUnitName
-
-       def get_recent(self):
-               return reversed(self._recent)
-
-       @property
-       def hiddenCategories(self):
-               return self._hiddenCategories
-
-       def get_hidden_units(self, categoryName):
-               try:
-                       return self._hiddenUnits[categoryName]
-               except KeyError:
-                       self._hiddenUnits[categoryName] = set()
-                       return self._hiddenUnits[categoryName]
-
-       def load_settings(self):
-               try:
-                       with open(constants._user_settings_, "r") as settingsFile:
-                               settings = simplejson.load(settingsFile)
-               except IOError, e:
-                       _moduleLogger.info("No settings")
-                       settings = {}
-               except ValueError:
-                       _moduleLogger.info("Settings were corrupt")
-                       settings = {}
-
-               self._fullscreenAction.setChecked(settings.get("isFullScreen", False))
-
-               sortBy = settings.get("sortBy", "name")
-               if sortBy not in ["name", "value", "unit"]:
-                       _moduleLogger.info("Setting sortBy is not a valid value: %s" % sortBy)
-                       sortBy = "name"
-               if sortBy == "name":
-                       self._sortByNameAction.setChecked(True)
-                       self._sortByValueAction.setChecked(False)
-                       self._sortByUnitAction.setChecked(False)
-               elif sortBy == "value":
-                       self._sortByNameAction.setChecked(False)
-                       self._sortByValueAction.setChecked(True)
-                       self._sortByUnitAction.setChecked(False)
-               elif sortBy == "unit":
-                       self._sortByNameAction.setChecked(False)
-                       self._sortByValueAction.setChecked(False)
-                       self._sortByUnitAction.setChecked(True)
-               else:
-                       raise RuntimeError("How did this sortBy come about? %s" % sortBy)
-
-               recent = settings.get("recent", self._recent)
-               for category, unit in recent:
-                       self.add_recent(category, unit)
-
-               self._hiddenCategories = set(settings.get("hiddenCategories", set()))
-               self._hiddenUnits = dict(
-                       (catName, set(units))
-                       for (catName, units) in settings.get("hiddenUnits", {}).iteritems()
-               )
-
-               self._showFavoritesAction.setChecked(settings.get("showFavorites", True))
-
-               self._condensedAction.setChecked(settings.get("useQuick", True))
-
-       def save_settings(self):
-               if self._sortByNameAction.isChecked():
-                       sortBy = "name"
-               elif self._sortByValueAction.isChecked():
-                       sortBy = "value"
-               elif self._sortByUnitAction.isChecked():
-                       sortBy = "unit"
-               else:
-                       raise RuntimeError("Unknown sorting value")
-               settings = {
-                       "isFullScreen": self._fullscreenAction.isChecked(),
-                       "recent": self._recent,
-                       "hiddenCategories": list(self._hiddenCategories),
-                       "hiddenUnits": dict(
-                               (catName, list(units))
-                               for (catName, units) in self._hiddenUnits.iteritems()
-                       ),
-                       "showFavorites": self._showFavoritesAction.isChecked(),
-                       "useQuick": self._condensedAction.isChecked(),
-                       "sortBy": sortBy,
-               }
-               with open(constants._user_settings_, "w") as settingsFile:
-                       simplejson.dump(settings, settingsFile)
-
-       @property
-       def appIconPath(self):
-               return self._appIconPath
-
-       @property
-       def jumpAction(self):
-               return self._jumpAction
-
-       @property
-       def recentAction(self):
-               return self._recentAction
-
-       @property
-       def fullscreenAction(self):
-               return self._fullscreenAction
-
-       @property
-       def condensedAction(self):
-               return self._condensedAction
-
-       @property
-       def sortByNameAction(self):
-               return self._sortByNameAction
-
-       @property
-       def sortByValueAction(self):
-               return self._sortByValueAction
-
-       @property
-       def sortByUnitAction(self):
-               return self._sortByUnitAction
-
-       @property
-       def logAction(self):
-               return self._logAction
-
-       @property
-       def quitAction(self):
-               return self._quitAction
-
-       @property
-       def showFavoritesAction(self):
-               return self._showFavoritesAction
-
-       def _walk_children(self):
-               if self._catWindow is not None:
-                       yield self._catWindow
-               if self._quickWindow is not None:
-                       yield self._quickWindow
-               if self._jumpWindow is not None:
-                       yield self._jumpWindow
-               if self._recentWindow is not None:
-                       yield self._recentWindow
-
-       def _close_windows(self):
-               if self._catWindow is not None:
-                       self._catWindow.window.destroyed.disconnect(self._on_cat_close)
-                       self._catWindow.close()
-                       self._catWindow = None
-               if self._quickWindow is not None:
-                       self._quickWindow.window.destroyed.disconnect(self._on_quick_close)
-                       self._quickWindow.close()
-                       self._quickWindow = None
-               if self._jumpWindow is not None:
-                       self._jumpWindow.window.destroyed.disconnect(self._on_jump_close)
-                       self._jumpWindow.close()
-                       self._jumpWindow = None
-               if self._recentWindow is not None:
-                       self._recentWindow.window.destroyed.disconnect(self._on_recent_close)
-                       self._recentWindow.close()
-                       self._recentWindow = None
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_app_quit(self, checked = False):
-               self.save_settings()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_child_close(self, name, obj = None):
-               if not hasattr(self, name):
-                       _moduleLogger.info("Something weird going on when we don't have a %s" % name)
-                       return
-               setattr(self, name, None)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_toggle_fullscreen(self, checked = False):
-               for window in self._walk_children():
-                       window.set_fullscreen(checked)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_condensed_start(self, checked = False):
-               self.request_category()
-               if self._recent:
-                       self._mainWindow.select_category(self._recent[-1][0])
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_jump_start(self, checked = False):
-               self.search_units()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_recent_start(self, checked = False):
-               self.show_recent()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_log(self, checked = False):
-               with open(constants._user_logpath_, "r") as f:
-                       logLines = f.xreadlines()
-                       log = "".join(logLines)
-                       self._clipboard.setText(log)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_quit(self, checked = False):
-               self._close_windows()
-
-
-class QuickConvert(object):
-
-       def __init__(self, parent, app):
-               self._app = app
-               self._categoryName = ""
-               self._inputUnitName = ""
-               self._outputUnitName = ""
-               self._unitNames = []
-               self._favoritesWindow = None
-
-               self._inputUnitValue = QtGui.QLineEdit()
-               qui_utils.mark_numbers_preferred(self._inputUnitValue)
-               self._inputUnitValue.textEdited.connect(self._on_value_edited)
-               self._inputUnitSymbol = QtGui.QLabel()
-
-               self._outputUnitValue = QtGui.QLineEdit()
-               qui_utils.mark_numbers_preferred(self._outputUnitValue)
-               self._outputUnitValue.textEdited.connect(self._on_output_value_edited)
-               self._outputUnitSymbol = QtGui.QLabel()
-
-               self._conversionLayout = QtGui.QHBoxLayout()
-               self._conversionLayout.addWidget(self._inputUnitValue)
-               self._conversionLayout.addWidget(self._inputUnitSymbol)
-               self._conversionLayout.addWidget(self._outputUnitValue)
-               self._conversionLayout.addWidget(self._outputUnitSymbol)
-
-               self._categoryView = QtGui.QTreeWidget()
-               self._categoryView.setHeaderLabels(["Categories"])
-               self._categoryView.setHeaderHidden(False)
-               self._categoryView.setRootIsDecorated(False)
-               if not constants.IS_MAEMO:
-                       self._categoryView.setAlternatingRowColors(True)
-               self._categoryView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
-               self._categoryView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
-               for catName in unit_data.UNIT_CATEGORIES:
-                       twi = QtGui.QTreeWidgetItem(self._categoryView)
-                       twi.setText(0, catName)
-               self._categorySelection = self._categoryView.selectionModel()
-               self._categorySelection.selectionChanged.connect(self._on_category_selection_changed)
-
-               self._inputView = QtGui.QTreeWidget()
-               self._inputView.setHeaderLabels(["From", "Name"])
-               self._inputView.setHeaderHidden(False)
-               self._inputView.header().hideSection(1)
-               self._inputView.setRootIsDecorated(False)
-               if not constants.IS_MAEMO:
-                       self._inputView.setAlternatingRowColors(True)
-               self._inputView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
-               self._inputView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
-               self._inputSelection = self._inputView.selectionModel()
-               self._inputSelection.selectionChanged.connect(self._on_input_selection_changed)
-
-               self._outputView = QtGui.QTreeWidget()
-               self._outputView.setHeaderLabels(["To", "Name"])
-               self._outputView.setHeaderHidden(False)
-               self._outputView.header().hideSection(1)
-               self._outputView.setRootIsDecorated(False)
-               if not constants.IS_MAEMO:
-                       self._outputView.setAlternatingRowColors(True)
-               self._outputView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
-               self._outputView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
-               self._outputWidgets = []
-               self._outputSelection = self._outputView.selectionModel()
-               self._outputSelection.selectionChanged.connect(self._on_output_selection_changed)
-
-               self._selectionLayout = QtGui.QHBoxLayout()
-               self._selectionLayout.addWidget(self._categoryView)
-               self._selectionLayout.addWidget(self._inputView)
-               self._selectionLayout.addWidget(self._outputView)
-
-               self._layout = QtGui.QVBoxLayout()
-               self._layout.addLayout(self._conversionLayout)
-               self._layout.addLayout(self._selectionLayout)
-
-               centralWidget = QtGui.QWidget()
-               centralWidget.setLayout(self._layout)
-
-               self._window = QtGui.QMainWindow(parent)
-               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
-               qui_utils.set_stackable(self._window, True)
-               self._window.setWindowTitle("%s - Quick Convert" % (constants.__pretty_app_name__, ))
-               self._window.setWindowIcon(QtGui.QIcon(app.appIconPath))
-               self._window.setCentralWidget(centralWidget)
-
-               self._chooseCatFavoritesAction = QtGui.QAction(None)
-               self._chooseCatFavoritesAction.setText("Select Categories")
-               self._chooseCatFavoritesAction.triggered.connect(self._on_choose_category_favorites)
-
-               self._chooseUnitFavoritesAction = QtGui.QAction(None)
-               self._chooseUnitFavoritesAction.setText("Select Units")
-               self._chooseUnitFavoritesAction.triggered.connect(self._on_choose_unit_favorites)
-               self._chooseUnitFavoritesAction.setEnabled(False)
-
-               self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
-
-               self._closeWindowAction = QtGui.QAction(None)
-               self._closeWindowAction.setText("Close Window")
-               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
-               self._closeWindowAction.triggered.connect(self._on_close_window)
-
-               if constants.IS_MAEMO:
-                       self._window.addAction(self._closeWindowAction)
-                       self._window.addAction(self._app.quitAction)
-                       self._window.addAction(self._app.fullscreenAction)
-
-                       fileMenu = self._window.menuBar().addMenu("&Units")
-                       fileMenu.addAction(self._chooseCatFavoritesAction)
-                       fileMenu.addAction(self._chooseUnitFavoritesAction)
-
-                       viewMenu = self._window.menuBar().addMenu("&View")
-                       viewMenu.addAction(self._app.showFavoritesAction)
-                       viewMenu.addAction(self._app.condensedAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.jumpAction)
-                       viewMenu.addAction(self._app.recentAction)
-               else:
-                       fileMenu = self._window.menuBar().addMenu("&Units")
-                       fileMenu.addAction(self._chooseCatFavoritesAction)
-                       fileMenu.addAction(self._chooseUnitFavoritesAction)
-                       fileMenu.addAction(self._closeWindowAction)
-                       fileMenu.addAction(self._app.quitAction)
-
-                       viewMenu = self._window.menuBar().addMenu("&View")
-                       viewMenu.addAction(self._app.showFavoritesAction)
-                       viewMenu.addAction(self._app.condensedAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.jumpAction)
-                       viewMenu.addAction(self._app.recentAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.fullscreenAction)
-
-               self._window.addAction(self._app.logAction)
-
-               self._update_favorites()
-               self.set_fullscreen(self._app.fullscreenAction.isChecked())
-               self._window.show()
-
-       @property
-       def window(self):
-               return self._window
-
-       def show(self):
-               self._window.show()
-
-       def hide(self):
-               self._window.hide()
-
-       def close(self):
-               self._window.close()
-
-       def set_fullscreen(self, isFullscreen):
-               if isFullscreen:
-                       self._window.showFullScreen()
-               else:
-                       self._window.showNormal()
-
-       def select_category(self, categoryName):
-               self._select_category(categoryName)
-
-               i = unit_data.UNIT_CATEGORIES.index(categoryName)
-               rootIndex = self._categoryView.rootIndex()
-               currentIndex = self._categoryView.model().index(i, 0, rootIndex)
-               self._categoryView.scrollTo(currentIndex)
-               self._categoryView.setItemSelected(self._categoryView.topLevelItem(i), True)
-
-               return self
-
-       def select_unit(self, name):
-               self.select_input(name)
-               return self
-
-       def select_input(self, name):
-               self._select_input(name)
-
-               i = self._unitNames.index(name)
-               rootIndex = self._inputView.rootIndex()
-               currentIndex = self._inputView.model().index(i, 0, rootIndex)
-               self._inputView.scrollTo(currentIndex)
-               self._inputView.setItemSelected(self._inputView.topLevelItem(i), True)
-
-       def select_output(self, name):
-               self._select_output(name)
-
-               i = self._unitNames.index(name)
-               rootIndex = self._outputView.rootIndex()
-               currentIndex = self._outputView.model().index(i, 0, rootIndex)
-               self._outputView.scrollTo(currentIndex)
-               self._outputView.setItemSelected(self._outputView.topLevelItem(i), True)
-
-       def _select_category(self, categoryName):
-               self._inputUnitName = ""
-               self._outputUnitName = ""
-               self._inputUnitValue.setText("")
-               self._inputUnitSymbol.setText("")
-               self._inputView.clear()
-               self._outputUnitValue.setText("")
-               self._outputUnitSymbol.setText("")
-               self._outputView.clear()
-               self._categoryName = categoryName
-               self._chooseUnitFavoritesAction.setEnabled(True)
-
-               unitData = unit_data.UNIT_DESCRIPTIONS[categoryName]
-               self._unitNames = list(unit_data.get_units(categoryName))
-               self._unitNames.sort()
-               for key in self._unitNames:
-                       conversion, unit, description = unitData[key]
-                       unit = key
-
-                       twi = QtGui.QTreeWidgetItem(self._inputView)
-                       twi.setText(0, unit)
-                       twi.setText(1, key)
-
-                       twi = QtGui.QTreeWidgetItem(self._outputView)
-                       twi.setText(0, unit)
-                       twi.setText(1, key)
-
-               defaultInputUnitName = self._app.get_recent_unit(categoryName)
-               if defaultInputUnitName:
-                       self.select_input(defaultInputUnitName)
-                       defaultOutputUnitName = self._app.get_recent_unit(categoryName, 1)
-                       assert defaultOutputUnitName
-                       self.select_output(defaultOutputUnitName)
-
-       def _select_input(self, name):
-               self._app.add_recent(self._categoryName, name)
-               self._inputUnitName = name
-
-               unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
-               conversion, unit, description = unitData[name]
-
-               self._inputUnitSymbol.setText(unit if unit else name)
-
-               if "" not in [self._categoryName, self._inputUnitName, self._outputUnitName]:
-                       self._update_output()
-
-       def _select_output(self, name):
-               # Add the output to recent but don't make things weird by making it the most recent
-               self._app.add_recent(self._categoryName, name)
-               self._app.add_recent(self._categoryName, self._inputUnitName)
-               self._outputUnitName = name
-
-               unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
-               conversion, unit, description = unitData[name]
-
-               self._outputUnitSymbol.setText(unit if unit else name)
-
-               if "" not in [self._categoryName, self._inputUnitName, self._outputUnitName]:
-                       self._update_output()
-
-       def _sanitize_value(self, userEntry):
-               if self._categoryName == "Computer Numbers":
-                       if userEntry == '':
-                               value = '0'
-                       else:
-                               value = userEntry
-               else:
-                       if userEntry == '':
-                               value = 0.0
-                       else:
-                               value = float(userEntry)
-               return value
-
-       def _update_output(self):
-               assert self._categoryName
-               assert self._inputUnitName
-               assert self._outputUnitName
-
-               userInput = str(self._inputUnitValue.text())
-               value = self._sanitize_value(userInput)
-
-               unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
-               inputConversion, _, _ = unitData[self._inputUnitName]
-               outputConversion, _, _ = unitData[self._outputUnitName]
-
-               func, arg = inputConversion
-               base = func.to_base(value, arg)
-
-               func, arg = outputConversion
-               newValue = func.from_base(base, arg)
-               self._outputUnitValue.setText(str(newValue))
-
-       def _update_input(self):
-               assert self._categoryName
-               assert self._inputUnitName
-               assert self._outputUnitName
-
-               userOutput = str(self._outputUnitValue.text())
-               value = self._sanitize_value(userOutput)
-
-               unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
-               inputConversion, _, _ = unitData[self._inputUnitName]
-               outputConversion, _, _ = unitData[self._outputUnitName]
-
-               func, arg = outputConversion
-               base = func.to_base(value, arg)
-
-               func, arg = inputConversion
-               newValue = func.from_base(base, arg)
-               self._inputUnitValue.setText(str(newValue))
-
-       def _update_favorites(self):
-               if self._app.showFavoritesAction.isChecked():
-                       assert self._categoryView.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
-                       for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
-                               if catName in self._app.hiddenCategories:
-                                       self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), True)
-                               else:
-                                       self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), False)
-
-                       for i, unitName in enumerate(self._unitNames):
-                               if unitName in self._app.get_hidden_units(self._categoryName):
-                                       self._inputView.setRowHidden(i, self._inputView.rootIndex(), True)
-                                       self._outputView.setRowHidden(i, self._outputView.rootIndex(), True)
-                               else:
-                                       self._inputView.setRowHidden(i, self._inputView.rootIndex(), False)
-                                       self._outputView.setRowHidden(i, self._outputView.rootIndex(), False)
-               else:
-                       for i in xrange(self._categoryView.topLevelItemCount()):
-                               self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), False)
-
-                       for i in xrange(len(self._unitNames)):
-                               self._inputView.setRowHidden(i, self._inputView.rootIndex(), False)
-                               self._outputView.setRowHidden(i, self._outputView.rootIndex(), False)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_window(self, checked = True):
-               self.close()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_show_favorites(self, checked = True):
-               if checked:
-                       assert self._categoryView.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
-                       for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
-                               if catName in self._app.hiddenCategories:
-                                       self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), True)
-
-                       for i, unitName in enumerate(self._unitNames):
-                               if unitName in self._app.get_hidden_units(self._categoryName):
-                                       self._inputView.setRowHidden(i, self._inputView.rootIndex(), True)
-                                       self._outputView.setRowHidden(i, self._outputView.rootIndex(), True)
-               else:
-                       for i in xrange(self._categoryView.topLevelItemCount()):
-                               self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), False)
-
-                       for i in xrange(len(self._unitNames)):
-                               self._inputView.setRowHidden(i, self._inputView.rootIndex(), False)
-                               self._outputView.setRowHidden(i, self._outputView.rootIndex(), False)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_choose_category_favorites(self, obj = None):
-               assert self._favoritesWindow is None
-               import windows
-               self._favoritesWindow = windows.FavoritesWindow(
-                       self._window,
-                       self._app,
-                       unit_data.UNIT_CATEGORIES,
-                       self._app.hiddenCategories
-               )
-               self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
-               return self._favoritesWindow
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_choose_unit_favorites(self, obj = None):
-               assert self._favoritesWindow is None
-               import windows
-               self._favoritesWindow = windows.FavoritesWindow(
-                       self._window,
-                       self._app,
-                       unit_data.get_units(self._categoryName),
-                       self._app.get_hidden_units(self._categoryName)
-               )
-               self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
-               return self._favoritesWindow
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_favorites(self, obj = None):
-               self._favoritesWindow = None
-               self._update_favorites()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_value_edited(self, *args):
-               self._update_output()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_output_value_edited(self, *args):
-               self._update_input()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_category_selection_changed(self, selected, deselected):
-               selectedNames = [
-                       str(item.text(0))
-                       for item in self._categoryView.selectedItems()
-               ]
-               assert len(selectedNames) == 1
-               self._select_category(selectedNames[0])
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_input_selection_changed(self, selected, deselected):
-               selectedNames = [
-                       str(item.text(1))
-                       for item in self._inputView.selectedItems()
-               ]
-               if selectedNames:
-                       assert len(selectedNames) == 1
-                       name = selectedNames[0]
-                       self._select_input(name)
-               else:
-                       pass
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_output_selection_changed(self, selected, deselected):
-               selectedNames = [
-                       str(item.text(1))
-                       for item in self._outputView.selectedItems()
-               ]
-               if selectedNames:
-                       assert len(selectedNames) == 1, selectedNames
-                       name = selectedNames[0]
-                       self._select_output(name)
-               else:
-                       pass
-
-
-class CategoryWindow(object):
-
-       def __init__(self, parent, app):
-               self._app = app
-               self._unitWindow = None
-               self._favoritesWindow = None
-
-               self._categories = QtGui.QTreeWidget()
-               self._categories.setHeaderLabels(["Categories"])
-               self._categories.itemClicked.connect(self._on_category_clicked)
-               self._categories.setHeaderHidden(True)
-               self._categories.setRootIsDecorated(False)
-               self._categories.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
-               self._categories.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
-               if not constants.IS_MAEMO:
-                       self._categories.setAlternatingRowColors(True)
-               for catName in unit_data.UNIT_CATEGORIES:
-                       twi = QtGui.QTreeWidgetItem(self._categories)
-                       twi.setText(0, catName)
-
-               self._layout = QtGui.QVBoxLayout()
-               self._layout.addWidget(self._categories)
-
-               centralWidget = QtGui.QWidget()
-               centralWidget.setLayout(self._layout)
-
-               self._window = QtGui.QMainWindow(parent)
-               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
-               qui_utils.set_stackable(self._window, True)
-               self._window.setWindowTitle("%s - Categories" % constants.__pretty_app_name__)
-               self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
-               self._window.setCentralWidget(centralWidget)
-
-               self._chooseFavoritesAction = QtGui.QAction(None)
-               self._chooseFavoritesAction.setText("Select Favorites")
-               self._chooseFavoritesAction.setShortcut(QtGui.QKeySequence("CTRL+f"))
-               self._chooseFavoritesAction.triggered.connect(self._on_choose_favorites)
-
-               self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
-
-               self._closeWindowAction = QtGui.QAction(None)
-               self._closeWindowAction.setText("Close")
-               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
-               self._closeWindowAction.triggered.connect(self._on_close_window)
-
-               if constants.IS_MAEMO:
-                       fileMenu = self._window.menuBar().addMenu("&Units")
-                       fileMenu.addAction(self._chooseFavoritesAction)
-
-                       viewMenu = self._window.menuBar().addMenu("&View")
-                       viewMenu.addAction(self._app.showFavoritesAction)
-                       viewMenu.addAction(self._app.condensedAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.jumpAction)
-                       viewMenu.addAction(self._app.recentAction)
-
-                       self._window.addAction(self._closeWindowAction)
-                       self._window.addAction(self._app.quitAction)
-                       self._window.addAction(self._app.fullscreenAction)
-               else:
-                       fileMenu = self._window.menuBar().addMenu("&Units")
-                       fileMenu.addAction(self._chooseFavoritesAction)
-                       fileMenu.addAction(self._closeWindowAction)
-                       fileMenu.addAction(self._app.quitAction)
-
-                       viewMenu = self._window.menuBar().addMenu("&View")
-                       viewMenu.addAction(self._app.showFavoritesAction)
-                       viewMenu.addAction(self._app.condensedAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.jumpAction)
-                       viewMenu.addAction(self._app.recentAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.fullscreenAction)
-
-               self._window.addAction(self._app.logAction)
-
-               self._update_favorites()
-               self.set_fullscreen(self._app.fullscreenAction.isChecked())
-               self._window.show()
-
-       @property
-       def window(self):
-               return self._window
-
-       def walk_children(self):
-               if self._unitWindow is not None:
-                       yield self._unitWindow
-               if self._favoritesWindow is not None:
-                       yield self._favoritesWindow
-
-       def show(self):
-               self._window.show()
-               for child in self.walk_children():
-                       child.show()
-
-       def hide(self):
-               for child in self.walk_children():
-                       child.hide()
-               self._window.hide()
-
-       def close(self):
-               for child in self.walk_children():
-                       child.window.destroyed.disconnect(self._on_child_close)
-                       child.close()
-               self._window.close()
-
-       def select_category(self, categoryName):
-               self._select_category(categoryName)
-
-               i = unit_data.UNIT_CATEGORIES.index(categoryName)
-               rootIndex = self._categories.rootIndex()
-               currentIndex = self._categories.model().index(i, 0, rootIndex)
-               self._categories.scrollTo(currentIndex)
-               self._categories.setItemSelected(self._categories.topLevelItem(i), True)
-               return self._unitWindow
-
-       def set_fullscreen(self, isFullscreen):
-               if isFullscreen:
-                       self._window.showFullScreen()
-               else:
-                       self._window.showNormal()
-               for child in self.walk_children():
-                       child.set_fullscreen(isFullscreen)
-
-       def _select_category(self, categoryName):
-               for child in self.walk_children():
-                       child.window.destroyed.disconnect(self._on_child_close)
-                       child.close()
-               self._unitWindow = UnitWindow(self._window, categoryName, self._app)
-               self._unitWindow.window.destroyed.connect(self._on_child_close)
-
-       def _update_favorites(self):
-               if self._app.showFavoritesAction.isChecked():
-                       assert self._categories.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
-                       for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
-                               if catName in self._app.hiddenCategories:
-                                       self._categories.setRowHidden(i, self._categories.rootIndex(), True)
-                               else:
-                                       self._categories.setRowHidden(i, self._categories.rootIndex(), False)
-               else:
-                       for i in xrange(self._categories.topLevelItemCount()):
-                               self._categories.setRowHidden(i, self._categories.rootIndex(), False)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_show_favorites(self, checked = True):
-               if checked:
-                       assert self._categories.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
-                       for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
-                               if catName in self._app.hiddenCategories:
-                                       self._categories.setRowHidden(i, self._categories.rootIndex(), True)
-               else:
-                       for i in xrange(self._categories.topLevelItemCount()):
-                               self._categories.setRowHidden(i, self._categories.rootIndex(), False)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_choose_favorites(self, obj = None):
-               assert self._favoritesWindow is None
-               import windows
-               self._favoritesWindow = windows.FavoritesWindow(
-                       self._window,
-                       self._app,
-                       unit_data.UNIT_CATEGORIES,
-                       self._app.hiddenCategories
-               )
-               self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
-               return self._favoritesWindow
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_favorites(self, obj = None):
-               self._favoritesWindow = None
-               self._update_favorites()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_child_close(self, obj = None):
-               self._unitWindow = None
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_window(self, checked = True):
-               self.close()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_category_clicked(self, item, columnIndex):
-               categoryName = unicode(item.text(0))
-               self.select_category(categoryName)
-
-
-class UnitData(object):
-
-       HEADERS = ["Name", "Value", "", "Unit"]
-       ALIGNMENT = [QtCore.Qt.AlignLeft, QtCore.Qt.AlignRight, QtCore.Qt.AlignLeft, QtCore.Qt.AlignLeft]
-       NAME_COLUMN = 0
-       VALUE_COLUMN_0 = 1
-       VALUE_COLUMN_1 = 2
-       UNIT_COLUMN = 3
-
-       __slots__ = [
-               "_name", "_unit", "_description", "_conversion",
-               "_value", "_integerDisplay", "_fractionalDisplay",
-       ]
-
-       def __init__(self, name, unit, description, conversion):
-               self._name = name
-               self._unit = unit
-               self._description = description
-               self._conversion = conversion
-
-               self._value = 0.0
-               self._integerDisplay, self._fractionalDisplay = split_number(self._value)
-
-       @property
-       def name(self):
-               return self._name
-
-       @property
-       def value(self):
-               return self._value
-
-       def update_value(self, newValue):
-               self._value = newValue
-               self._integerDisplay, self._fractionalDisplay = split_number(newValue)
-
-       @property
-       def unit(self):
-               return self._unit
-
-       @property
-       def conversion(self):
-               return self._conversion
-
-       def data(self, column):
-               try:
-                       return [self._name, self._integerDisplay, self._fractionalDisplay, self._unit][column]
-               except IndexError:
-                       return None
-
-
-class UnitModel(QtCore.QAbstractItemModel):
-
-       def __init__(self, categoryName, parent=None):
-               super(UnitModel, self).__init__(parent)
-               self._categoryName = categoryName
-               self._unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
-               if self._categoryName == "Computer Numbers":
-                       self._sanitize_value = self._sanitize_alpha_value
-               else:
-                       self._sanitize_value = self._sanitize_numeric_value
-
-               self._children = []
-               for key in unit_data.get_units(self._categoryName):
-                       conversion, unit, description = self._unitData[key]
-                       self._children.append(UnitData(key, unit, description, conversion))
-               self._sortSettings = None
-
-       @misc_utils.log_exception(_moduleLogger)
-       def columnCount(self, parent):
-               if parent.isValid():
-                       return 0
-               else:
-                       return len(UnitData.HEADERS)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def data(self, index, role):
-               #if not index.isValid():
-               #       return None
-
-               if role == QtCore.Qt.DisplayRole:
-                       item = index.internalPointer()
-                       if isinstance(item, UnitData):
-                               return item.data(index.column())
-                       elif item is UnitData.HEADERS:
-                               return item[index.column()]
-               elif role == QtCore.Qt.TextAlignmentRole:
-                       return UnitData.ALIGNMENT[index.column()]
-               else:
-                       return None
-
-       @misc_utils.log_exception(_moduleLogger)
-       def sort(self, column, order = QtCore.Qt.AscendingOrder):
-               self._sortSettings = column, order
-               isReverse = order == QtCore.Qt.AscendingOrder
-               if column == UnitData.NAME_COLUMN:
-                       key_func = lambda item: item.name
-               elif column in [UnitData.VALUE_COLUMN_0, UnitData.VALUE_COLUMN_1]:
-                       key_func = lambda item: item.value
-               elif column == UnitData.UNIT_COLUMN:
-                       key_func = lambda item: item.unit
-               self._children.sort(key=key_func, reverse = isReverse)
-
-               self._all_changed()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def flags(self, index):
-               if not index.isValid():
-                       return QtCore.Qt.NoItemFlags
-
-               return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
-
-       @misc_utils.log_exception(_moduleLogger)
-       def headerData(self, section, orientation, role):
-               if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
-                       return UnitData.HEADERS[section]
-
-               return None
-
-       @misc_utils.log_exception(_moduleLogger)
-       def index(self, row, column, parent):
-               #if not self.hasIndex(row, column, parent):
-               #       return QtCore.QModelIndex()
-               #elif parent.isValid():
-               #       return QtCore.QModelIndex()
-
-               parentItem = UnitData.HEADERS
-               childItem = self._children[row]
-               if childItem:
-                       return self.createIndex(row, column, childItem)
-               else:
-                       return QtCore.QModelIndex()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def parent(self, index):
-               if not index.isValid():
-                       return QtCore.QModelIndex()
-
-               childItem = index.internalPointer()
-               if isinstance(childItem, UnitData):
-                       return QtCore.QModelIndex()
-               elif childItem is UnitData.HEADERS:
-                       return None
-
-       @misc_utils.log_exception(_moduleLogger)
-       def rowCount(self, parent):
-               if 0 < parent.column():
-                       return 0
-
-               if not parent.isValid():
-                       return len(self._children)
-               else:
-                       return len(self._children)
-
-       def get_unit(self, index):
-               assert 0 <= index
-               return self._children[index]
-
-       def get_unit_names(self):
-               for child in self._children:
-                       yield child.name
-
-       def index_unit(self, unitName):
-               for i, child in enumerate(self._children):
-                       if child.name == unitName:
-                               return i
-               else:
-                       raise RuntimeError("Unit not found")
-
-       def update_values(self, fromIndex, userInput):
-               value = self._sanitize_value(userInput)
-               func, arg = self._children[fromIndex].conversion
-               base = func.to_base(value, arg)
-               for i, child in enumerate(self._children):
-                       func, arg = child.conversion
-                       newValue = func.from_base(base, arg)
-                       child.update_value(newValue)
-
-               if (
-                       self._sortSettings is not None and
-                       self._sortSettings[0]  in [UnitData.VALUE_COLUMN_0, UnitData.VALUE_COLUMN_1]
-               ):
-                       # Sort takes care of marking everything as changed
-                       self.sort(*self._sortSettings)
-                       return True
-               else:
-                       self._values_changed()
-                       return False
-
-       def __len__(self):
-               return len(self._children)
-
-       def _values_changed(self):
-               topLeft = self.createIndex(0, UnitData.VALUE_COLUMN_0, self._children[0])
-               bottomRight = self.createIndex(len(self._children)-1, UnitData.VALUE_COLUMN_1, self._children[-1])
-               self.dataChanged.emit(topLeft, bottomRight)
-
-       def _all_changed(self):
-               topLeft = self.createIndex(0, 0, self._children[0])
-               bottomRight = self.createIndex(len(self._children)-1, len(UnitData.HEADERS)-1, self._children[-1])
-               self.dataChanged.emit(topLeft, bottomRight)
-
-       def _sanitize_alpha_value(self, userEntry):
-               if userEntry:
-                       value = userEntry
-               else:
-                       value = '0'
-               return value
-
-       def _sanitize_numeric_value(self, userEntry):
-               if userEntry:
-                       value = float(userEntry)
-               else:
-                       value = 0.0
-               return value
-
-
-class UnitWindow(object):
-
-       def __init__(self, parent, category, app):
-               self._app = app
-               self._categoryName = category
-               self._selectedIndex = 0
-               self._favoritesWindow = None
-
-               self._selectedUnitName = QtGui.QLabel()
-               self._selectedUnitValue = QtGui.QLineEdit()
-               self._selectedUnitValue.textEdited.connect(self._on_value_edited)
-               qui_utils.mark_numbers_preferred(self._selectedUnitValue)
-               self._selectedUnitSymbol = QtGui.QLabel()
-               self._updateDelayTimer = QtCore.QTimer()
-               self._updateDelayTimer.setInterval(100)
-               self._updateDelayTimer.setSingleShot(True)
-               self._updateDelayTimer.timeout.connect(self._on_value_edited_delayed)
-
-               self._selectedUnitLayout = QtGui.QHBoxLayout()
-               self._selectedUnitLayout.addWidget(self._selectedUnitName)
-               self._selectedUnitLayout.addWidget(self._selectedUnitValue)
-               self._selectedUnitLayout.addWidget(self._selectedUnitSymbol)
-
-               self._unitsModel = UnitModel(self._categoryName)
-               self._unitsView = QtGui.QTreeView()
-               self._unitsView.setModel(self._unitsModel)
-               self._unitsView.setUniformRowHeights(True)
-               self._unitsView.setSortingEnabled(True)
-               self._unitsView.setRootIsDecorated(False)
-               self._unitsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-               self._unitsView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
-               self._unitsView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
-               self._unitsView.setHeaderHidden(True)
-               self._unitsView.clicked.connect(self._on_unit_clicked)
-               if not constants.IS_MAEMO:
-                       self._unitsView.setAlternatingRowColors(True)
-
-               viewHeader = self._unitsView.header()
-               viewHeader.setSortIndicatorShown(True)
-               viewHeader.setClickable(True)
-
-               viewHeader.setResizeMode(UnitData.NAME_COLUMN, QtGui.QHeaderView.ResizeToContents)
-               viewHeader.setResizeMode(UnitData.VALUE_COLUMN_0, QtGui.QHeaderView.ResizeToContents)
-               viewHeader.setResizeMode(UnitData.VALUE_COLUMN_1, QtGui.QHeaderView.ResizeToContents)
-               viewHeader.setResizeMode(UnitData.UNIT_COLUMN, QtGui.QHeaderView.ResizeToContents)
-               viewHeader.setStretchLastSection(False)
-
-               # Trying to make things faster by locking in the initial size of the immutable columns
-               nameSize = min(viewHeader.sectionSize(UnitData.NAME_COLUMN), 300)
-               viewHeader.setResizeMode(UnitData.NAME_COLUMN, QtGui.QHeaderView.Fixed)
-               viewHeader.resizeSection(UnitData.NAME_COLUMN, nameSize)
-               unitSize = min(viewHeader.sectionSize(UnitData.UNIT_COLUMN), 150)
-               viewHeader.setResizeMode(UnitData.UNIT_COLUMN, QtGui.QHeaderView.Fixed)
-               viewHeader.resizeSection(UnitData.UNIT_COLUMN, unitSize)
-
-               self._layout = QtGui.QVBoxLayout()
-               self._layout.addLayout(self._selectedUnitLayout)
-               self._layout.addWidget(self._unitsView)
-
-               centralWidget = QtGui.QWidget()
-               centralWidget.setLayout(self._layout)
-
-               self._window = QtGui.QMainWindow(parent)
-               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
-               qui_utils.set_stackable(self._window, True)
-               self._window.setWindowTitle("%s - %s" % (constants.__pretty_app_name__, category))
-               self._window.setWindowIcon(QtGui.QIcon(app.appIconPath))
-               self._window.setCentralWidget(centralWidget)
-
-               defaultUnitName = self._app.get_recent_unit(self._categoryName)
-               if defaultUnitName:
-                       self.select_unit(defaultUnitName)
-               else:
-                       self._select_unit(0)
-
-               if self._app.sortByNameAction.isChecked():
-                       sortColumn = UnitData.NAME_COLUMN
-               elif self._app.sortByValueAction.isChecked():
-                       sortColumn = UnitData.VALUE_COLUMN_0
-               elif self._app.sortByUnitAction.isChecked():
-                       sortColumn = UnitData.UNIT_COLUMN
-               else:
-                       raise RuntimeError("No sort column selected")
-               if sortColumn != 0:
-                       # By default it sorts by he first column (name)
-                       self._unitsModel.sort(sortColumn)
-
-               self._chooseFavoritesAction = QtGui.QAction(None)
-               self._chooseFavoritesAction.setText("Select Favorites")
-               self._chooseFavoritesAction.setShortcut(QtGui.QKeySequence("CTRL+f"))
-               self._chooseFavoritesAction.triggered.connect(self._on_choose_favorites)
-
-               self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
-
-               self._previousUnitAction = QtGui.QAction(None)
-               self._previousUnitAction.setText("Previous Unit")
-               self._previousUnitAction.setShortcut(QtGui.QKeySequence("Up"))
-               self._previousUnitAction.triggered.connect(self._on_previous_unit)
-
-               self._nextUnitAction = QtGui.QAction(None)
-               self._nextUnitAction.setText("Next Unit")
-               self._nextUnitAction.setShortcut(QtGui.QKeySequence("Down"))
-               self._nextUnitAction.triggered.connect(self._on_next_unit)
-
-               self._closeWindowAction = QtGui.QAction(None)
-               self._closeWindowAction.setText("Close Window")
-               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
-               self._closeWindowAction.triggered.connect(self._on_close_window)
-
-               if constants.IS_MAEMO:
-                       self._window.addAction(self._closeWindowAction)
-                       self._window.addAction(self._app.quitAction)
-                       self._window.addAction(self._app.fullscreenAction)
-
-                       fileMenu = self._window.menuBar().addMenu("&Units")
-                       fileMenu.addAction(self._chooseFavoritesAction)
-
-                       viewMenu = self._window.menuBar().addMenu("&View")
-                       viewMenu.addAction(self._app.showFavoritesAction)
-                       viewMenu.addAction(self._app.condensedAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.sortByNameAction)
-                       viewMenu.addAction(self._app.sortByValueAction)
-                       viewMenu.addAction(self._app.sortByUnitAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.jumpAction)
-                       viewMenu.addAction(self._app.recentAction)
-               else:
-                       fileMenu = self._window.menuBar().addMenu("&Units")
-                       fileMenu.addAction(self._chooseFavoritesAction)
-                       fileMenu.addAction(self._closeWindowAction)
-                       fileMenu.addAction(self._app.quitAction)
-
-                       viewMenu = self._window.menuBar().addMenu("&View")
-                       viewMenu.addAction(self._app.showFavoritesAction)
-                       viewMenu.addAction(self._app.condensedAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.sortByNameAction)
-                       viewMenu.addAction(self._app.sortByValueAction)
-                       viewMenu.addAction(self._app.sortByUnitAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.jumpAction)
-                       viewMenu.addAction(self._app.recentAction)
-                       viewMenu.addSeparator()
-                       viewMenu.addAction(self._app.fullscreenAction)
-
-               self._app.sortByNameAction.triggered.connect(self._on_sort_by_name)
-               self._app.sortByValueAction.triggered.connect(self._on_sort_by_value)
-               self._app.sortByUnitAction.triggered.connect(self._on_sort_by_unit)
-
-               self._window.addAction(self._app.logAction)
-               self._window.addAction(self._nextUnitAction)
-               self._window.addAction(self._previousUnitAction)
-               self._window.addAction(self._chooseFavoritesAction)
-
-               self._update_favorites()
-               self.set_fullscreen(self._app.fullscreenAction.isChecked())
-               self._window.show()
-
-       @property
-       def window(self):
-               return self._window
-
-       def show(self):
-               for child in self.walk_children():
-                       child.hide()
-               self._window.show()
-
-       def hide(self):
-               for child in self.walk_children():
-                       child.hide()
-               self._window.hide()
-
-       def close(self):
-               for child in self.walk_children():
-                       child.window.destroyed.disconnect(self._on_child_close)
-                       child.close()
-               self._window.close()
-
-       def set_fullscreen(self, isFullscreen):
-               if isFullscreen:
-                       self._window.showFullScreen()
-               else:
-                       self._window.showNormal()
-
-       def select_unit(self, unitName):
-               index = self._unitsModel.index_unit(unitName)
-               self._select_unit(index)
-
-               qindex = self._unitsModel.createIndex(index, 0, self._unitsModel.get_unit(index))
-               self._unitsView.scrollTo(qindex)
-
-       def walk_children(self):
-               if self._favoritesWindow is not None:
-                       yield self._favoritesWindow
-
-       def _select_unit(self, index):
-               unit = self._unitsModel.get_unit(index)
-               self._selectedUnitName.setText(unit.name)
-               self._selectedUnitValue.setText(str(unit.value))
-               self._selectedUnitSymbol.setText(unit.unit)
-
-               self._selectedIndex = index
-               self._app.add_recent(self._categoryName, self._unitsModel.get_unit(index).name)
-
-       def _update_favorites(self, force = False):
-               if self._app.showFavoritesAction.isChecked():
-                       unitNames = list(self._unitsModel.get_unit_names())
-                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
-                       for i, unitName in enumerate(unitNames):
-                               if unitName in hiddenUnits:
-                                       self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
-                               else:
-                                       self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
-               else:
-                       if force:
-                               for i in xrange(len(self._unitsModel)):
-                                       self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_show_favorites(self, checked = True):
-               if checked:
-                       unitNames = list(self._unitsModel.get_unit_names())
-                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
-                       for i, unitName in enumerate(unitNames):
-                               if unitName in hiddenUnits:
-                                       self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
-               else:
-                       for i in xrange(len(self._unitsModel)):
-                               self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_choose_favorites(self, obj = None):
-               assert self._favoritesWindow is None
-               import windows
-               self._favoritesWindow = windows.FavoritesWindow(
-                       self._window,
-                       self._app,
-                       unit_data.get_units(self._categoryName),
-                       self._app.get_hidden_units(self._categoryName)
-               )
-               self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
-               return self._favoritesWindow
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_favorites(self, obj = None):
-               self._favoritesWindow = None
-               self._update_favorites(force=True)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_previous_unit(self, checked = True):
-               index = self._selectedIndex - 1
-               unitData = self._unitsModel.get_unit(index)
-               unitName = unitData.name
-
-               if self._app.showFavoritesAction.isChecked():
-                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
-                       while unitName in hiddenUnits:
-                               index -= 1
-                               unitData = self._unitsModel.get_unit(index)
-                               unitName = unitData.name
-
-               self.select_unit(unitName)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_next_unit(self, checked = True):
-               index = self._selectedIndex + 1
-               unitData = self._unitsModel.get_unit(index)
-               unitName = unitData.name
-
-               if self._app.showFavoritesAction.isChecked():
-                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
-                       while unitName in hiddenUnits:
-                               index += 1
-                               unitData = self._unitsModel.get_unit(index)
-                               unitName = unitData.name
-
-               self.select_unit(unitName)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_window(self, checked = True):
-               self.close()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_sort_by_name(self, checked = False):
-               self._unitsModel.sort(UnitData.NAME_COLUMN, QtCore.Qt.DescendingOrder)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_sort_by_value(self, checked = False):
-               self._unitsModel.sort(UnitData.VALUE_COLUMN_0)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_sort_by_unit(self, checked = False):
-               self._unitsModel.sort(UnitData.UNIT_COLUMN, QtCore.Qt.DescendingOrder)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_unit_clicked(self, index):
-               self._select_unit(index.row())
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_value_edited(self, *args):
-               if not self._updateDelayTimer.isActive():
-                       self._updateDelayTimer.start()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_value_edited_delayed(self, *args):
-               userInput = str(self._selectedUnitValue.text())
-               orderChanged = self._unitsModel.update_values(self._selectedIndex, userInput)
-               if orderChanged:
-                       self._update_favorites()
-
-
-def run_gonvert():
-       try:
-               os.makedirs(constants._data_path_)
-       except OSError, e:
-               if e.errno != 17:
-                       raise
-
-       logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s'
-       logging.basicConfig(level=logging.DEBUG, format=logFormat)
-       rotating = logging.handlers.RotatingFileHandler(constants._user_logpath_, maxBytes=512*1024, backupCount=1)
-       rotating.setFormatter(logging.Formatter(logFormat))
-       root = logging.getLogger()
-       root.addHandler(rotating)
-       _moduleLogger.info("%s %s-%s" % (constants.__app_name__, constants.__version__, constants.__build__))
-       _moduleLogger.info("OS: %s" % (os.uname()[0], ))
-       _moduleLogger.info("Kernel: %s (%s) for %s" % os.uname()[2:])
-       _moduleLogger.info("Hostname: %s" % os.uname()[1])
-
-       app = QtGui.QApplication([])
-       handle = Gonvert(app)
-       if constants.PROFILE_STARTUP:
-               return 0
-       else:
-               return app.exec_()
-
-
-if __name__ == "__main__":
-       import sys
-       val = run_gonvert()
-       sys.exit(val)
diff --git a/src/unit_data.py b/src/unit_data.py
deleted file mode 100644 (file)
index 61770b5..0000000
+++ /dev/null
@@ -1,2312 +0,0 @@
-import math
-
-import converters
-
-try:
-       raise ImportError()
-       import gettext
-except ImportError:
-       _ = lambda x: x
-else:
-       _ = gettext.gettext
-
-
-
-#----- main dictionary of unit descriptions below ------------
-# first entry defines base unit
-# remaining entries define unit specifications [(function, argument), units, description]
-#      where function can be m and argument is the multiplying factor to_base
-#      or function can be any other arbitrary function and argument can be a single argument
-UNIT_DESCRIPTIONS = {
-       _(u"Acceleration"): {
-               ".base_unit": _(u"meter per second squared"),
-               _(u"free fall"):
-               [(converters.m, 9.80665), _(u"gn"), _(u"The ideal falling motion of a body that is subject only to the earth's gravitational field.")],
-               _(u"meter per second squared"):
-               [(converters.m, 1.0), u"m/s\xb2", u''],
-               _(u"foot per second squared"):
-               [(converters.m, 30.48/100), u"ft/s\xb2", u''],
-               _(u"centimeter per second squared"):
-               [(converters.m, 1/100.0), u"cm/s\xb2", ''],
-               _(u"gal"):
-               [(converters.m, 1/100.0), _(u"Gal"), _(u"A unit of gravitational acceleration equal to one centimeter per second per second (named after Galileo)")],
-               _(u"millimeter per second squared"):
-               [(converters.m, 1/1000.0), u"mm/s\xb2", '']
-       },
-
-       _(u"Angle"): {
-               ".base_unit": _(u"radian"),
-               _(u"revolution / circle / perigon / turn"):
-               [(converters.m, 2.0*math.pi), "r", _(u"The act of revolving, or turning round on an axis or a center; the motion of a body round a fixed point or line; rotation; as, the revolution of a wheel, of a top, of the earth on its axis, etc.")],
-               _(u"right angle"):
-               [(converters.m, math.pi/2.0), "L", _(u"The angle formed by one line meeting another perpendicularly")],
-               _(u"radian"):
-               [(converters.m, 1.0), _(u"rad"), _(u"An arc of a circle which is equal in length to the radius, or the angle measured by such an arc.")],
-               _(u"degree"):
-               [(converters.m, math.pi/180.0), u"\xb0", _(u"1/360 of a complete revolution.")],
-               _(u"grad | grade | gon"):
-               [(converters.m, math.pi/200), _(u"g"), _(u"One-hundredth of a right angle.")],
-               _(u"milliradian"):
-               [(converters.m, 1/1000.0), _(u"mrad"), _(u"A unit of angular distance equal to one thousandth of a radian.")],
-               _(u"minute"):
-               [(converters.m, math.pi/(180.0*60)), "'", _(u"The sixtieth part of a degree; sixty seconds (Marked thus ('); as, 10deg 20').")],
-               _(u"second"):
-               [(converters.m, math.pi/(180.0*60*60)), '"', _(u"""One sixtieth of a minute.(Marked thus ("); as, 10deg 20' 30"). ''""")],
-               _(u"mil"):
-               [(converters.m, (2*math.pi)/6400), '', _(u"Used in artillery; 1/6400 of a complete revolution.")],
-               _(u"centesimal minute"):
-               [(converters.m, math.pi/20000), '', _(u"One hundredth of a grade, 0.01 grade")],
-               _(u"centesimal second"):
-               [(converters.m, math.pi/2000000), '', _(u"One ten-thousandth of a grade, 0.0001 grade")],
-               _(u"octant"):
-               [(converters.m, math.pi/4.0), '', _(u"The eighth part of a circle (an arc of 45 degrees).")],
-               _(u"quadrant"):
-               [(converters.m, math.pi/2.0), '', _(u"The fourth part of a circle (an arc of 90 degrees).")],
-               _(u"sextant"):
-               [(converters.m, math.pi/3.0), '', _(u"The sixth part of a circle (an arc of 60 degrees).")],
-               _(u"point"):
-               [(converters.m, math.pi/16.0), '', _(u"1/32 of a circle. Points are used on the face of a compass (32 points). Each point is labelled clockwise starting from North as follows: North, North by East, North Northeast, Northeast by North, and Northeast, etc.")],
-               _(u"sign"):
-               [(converters.m, math.pi/6.0), '', _(u"The twelfth part of a circle as in twelve signs of the zodiac (an arc of 30 degrees).")],
-       },
-
-       _(u"Angular Velocity / Frequency"): {
-               ".base_unit": _(u"radian per second"),
-               _(u"kiloradian per second"):
-               [(converters.m, 1000.0), "krad/s", ''],
-               _(u"revolution per second"):
-               [(converters.m, 2*math.pi), "rev/s", ''],
-               _(u"hertz"):
-               [(converters.m, 2*math.pi), "Hz", _(u"Named after the German physicist Heinrich Hertz (1857-1894) who was the first to produce electromagnetic waves artificially. Having a periodic interval of one second.")],
-               _(u"radian per second"):
-               [(converters.m, 1.0), "rad/s", ''],
-               _(u"milliradian per second"):
-               [(converters.m, 1/1000.0), "mrad/s", ''],
-               _(u"revolution per minute"):
-               [(converters.m, (2*math.pi)/60.0), "rpm", ''],
-               _(u"revolution per hour"):
-               [(converters.m, (2*math.pi)/3600.0), "rph", ''],
-               _(u"revolution per day"):
-               [(converters.m, (2*math.pi)/(3600.0*24)), "rpd", ''],
-               _(u"gigahertz"):
-               [(converters.m, 1e9*2*math.pi), "GHz", _(u"One billion hertz.")],
-               _(u"terahertz"):
-               [(converters.m, 1e12*2*math.pi), "THz", ''],
-               _(u"petahertz"):
-               [(converters.m, 1e15*2*math.pi), "PHz", ''],
-               _(u"exahertz"):
-               [(converters.m, 1e18*2*math.pi), "EHz", ''],
-               _(u"megahertz"):
-               [(converters.m, 1e6*2*math.pi), "MHz", _(u"One million hertz.")],
-               _(u"kilohertz"):
-               [(converters.m, 1e3*2*math.pi), "kHz", _(u"One thousand hertz.")],
-       },
-
-       _(u"Area"): {
-               ".base_unit": _(u"square meter"),
-               _(u"meter diameter circle"):
-               [(converters.f, ('math.pi*(x/2.0)**2', '2.0*(x/math.pi)**(0.5)')), "m dia.", _(u"Type the diameter of the circle in meters to find its area displayed in other fields.")],
-               _(u"centimeter diameter circle"):
-               [(converters.f, ('math.pi*(x/200.0)**2', '200.0*(x/math.pi)**(0.5)')), "cm dia.", _(u"Type the diameter of the circle in centimeters to find its area displayed in other fields.")],
-               _(u"inch diameter circle"):
-               [(converters.f, ('math.pi*(((x*(25.4/1000))/2.0) )**2', '1000/25.4 * 2.0*(x/math.pi)**(0.5)')), "in dia.", _(u"Type the diameter of the circle in inches to find its area displayed in other fields.")],
-               _(u"foot diameter circle"):
-               [(converters.f, ('math.pi*(((x*((12*25.4)/1000))/2.0) )**2', '1000/(12*25.4) * 2.0*(x/math.pi)**(0.5)')), "ft dia.", _(u"Type the diameter of the circle in feet to find its area displayed in other fields.")],
-               _(u"are"):
-               [(converters.m, 100.0), '', _(u"The unit of superficial measure, being a square of which each side is ten meters in length; 100 square meters, or about 119.6 square yards.")],
-               _(u"acre"):
-               [(converters.m, 4046.8564224), '', _(u"A piece of land, containing 160 square rods, or 4, 840 square yards, or 43, 560 square feet. This is the English statute acre. That of the United States is the same. The Scotch acre was about 1.26 of the English, and the Irish 1.62 of the English. Note: The acre was limited to its present definite quantity by statutes of Edward I., Edward III., and Henry VIII.")],
-               _(u"acre (Cheshire)"):
-               [(converters.m, 8561.97632), '', ''],
-               _(u"acre (Irish)"):
-               [(converters.m, 6555.26312), '', ''],
-               _(u"acre (Scottish)"):
-               [(converters.m, 5142.20257), '', ''],
-               _(u"arpent (French)"):
-               [(converters.m, 4088/1.196), '', _(u" 4, 088 sq. yards, or nearly five sixths of an English acre.")],
-               _(u"arpent (woodland)"):
-               [(converters.m, 16*25.29285264*10+16*25.29285264*2.5+(16*25.29285264*10)/160), '', _(u"1 acre, 1 rood, 1 perch")],
-               _(u"barn"):
-               [(converters.m, 1.0/1e28), '', _('Used in Nuclear physics to describe the apparent cross-sectional size of atomic sized objects that are bombarded with smaller objects (like electrons). 10^-28 square meters. 100 square femtometers. Originated from the semi-humorous idiom big as a barn and used by physicists to describe the size of the scattering object (Ex: That was as big as 5 barns!).')],
-               _(u"cho"):
-               [(converters.m, 16*25.29285264*10*2.45), '', _(u"Japanese. 2.45 acre")],
-               _(u"circular inch"):
-               [(converters.m, 1000000.0/(1e6*1550*1.273)), '', ''],
-               _(u"circular mil"):
-               [(converters.m, 1.0/(1e6*1550*1.273)), "cmil", ''],
-               _(u"desyatina | dessiatina"):
-               [(converters.m, 16*25.29285264*10*2.6996), '', _(u"Russian. 2.6996 acre. 2400 square sadzhens")],
-               _(u"flag"):
-               [(converters.m, 25/10.7639104167097), '', _(u"square pace (a pace is 5 feet).")],
-               _(u"hide | carucate"):
-               [(converters.m, 40468.71618), '', _(u"An ancient English measure of the amount of land required to support family")],
-               _(u"hectare"):
-               [(converters.m, 10000.0), "ha", _(u"A measure of area, or superficies, containing a hundred ares, or 10, 000 square meters, and equivalent to 2.471 acres.")],
-               _(u"homestead | quarter section"):
-               [(converters.m, 16*25.29285264*10*160), '', _(u"160 acres, 1/4 square mile, or 1/4 section. Use by the governments of North America early settlers in the western states and provinces were allowed to take title to a homestead of 160 acres of land by registering a claim, settling on the land, and cultivating it.")],
-               _(u"perch"):
-               [(converters.m, (16*25.29285264*10)/160), '', _(u"Used to measure land. A square rod; the 160th part of an acre.")],
-               _(u"sabin"):
-               [(converters.m, 1/10.7639104167097), '', _(u"A unit of acoustic absorption equivalent to the absorption by a square foot of a surface that absorbs all incident sound. 1ft\xb2.")],
-               _(u"square"):
-               [(converters.m, 100/10.7639104167097), '', _(u"Used in the construction for measuring roofing material, finished lumber, and other building materials. One square is equals 100 square feet.")],
-               _(u"section"):
-               [(converters.m, 2.59*1E6), '', _(u"Used in land measuring. One square mile. An area of about 640 acres")],
-               _(u"square league (land)"):
-               [(converters.m, 23309892.99), '', ''],
-               _(u"square mile"):
-                       [(converters.m, 2.59*1e6), u"mi\xb2", ''],
-               _(u"square kilometer"):
-                       [(converters.m, 1e6), u"km\xb2", ''],
-               _(u"rood"):
-                       [(converters.m, 16*25.29285264*2.5), '', _(u"The fourth part of an acre, or forty square rods.")],
-               _(u"shaku"):
-                       [(converters.m, 330.6/10000), '', _(u"A Japanese unit of area, the shaku equals 330.6 square centimeters (51.24 square inches). Note: shaku also means length and volume.")],
-               _(u"square chain (surveyor)"):
-                       [(converters.m, 16*25.29285264), u"ch\xb2", _(u"A unit for land measure equal to four rods square, or one tenth of an acre.")],
-               _(u"link"):
-                       [(converters.m, 4*25.29285264), '', _(u"4 rods square")],
-               _(u"square rod"):
-                       [(converters.m, 25.29285264), u"rd\xb2", ''],
-               _(u"square meter"):
-                       [(converters.m, 1.0), u"m\xb2", _(u"Also know as a centare is (1/100th of an are).")],
-               _(u"square yard"):
-                       [(converters.m, 1/1.19599004630108), u"yd\xb2", _(u"A unit of area equal to one yard by one yard square syn: sq yd")],
-               _(u"square foot"):
-                       [(converters.m, 1/10.7639104167097), u"ft\xb2", _(u"An area equal to that of a square the sides of which are twelve inches; 144 square inches.")],
-               _(u"square inch"):
-                       [(converters.m, 1/(10.7639104167097*144)), u"in\xb2", _(u"A unit of area equal to one inch by one inch square syn: sq in")],
-               _(u"square centimeter"):
-                       [(converters.m, 1.0/10000), u"cm\xb2", ''],
-               _(u"square micrometer"):
-                       [(converters.m, 1.0/1e12), u"\xb5m\xb2", ''],
-               _(u"square millimeter"):
-                       [(converters.m, 1.0/1e6), u"mm\xb2", ''],
-               _(u"square mil"):
-                       [(converters.m, 1.0/(1e6*1550)), u"mil\xb2", ''],
-               _(u"township"):
-                       [(converters.m, 1e6*2.59*36), '', _(u"A division of territory six miles square (36miles\xb2), containing 36 sections.")],
-               _(u"roll (wallpaper)"):
-                       [(converters.m, 30/10.7639104167097), '', ''],
-               _(u"square Scottish ell"):
-                       [(converters.m, 0.88323), '', ''],
-               _(u"fall (Scottish)"):
-                       [(converters.m, 31.79618), '', ''],
-               _(u"joch (German) | yoke"):
-                       [(converters.m, 5746.5577), '', _(u"joch (German) is 40 square klafters")],
-               _(u"labor (Texas)"):
-                       [(converters.m, 716862.83837), '', _(u"An area of land that could be cultivated by one farmer")],
-               _(u"barony"):
-                       [(converters.m, 16187486.47094), '', ''],
-               _(u"square pes (Roman)"):
-                       [(converters.m, 0.08741), '', ''],
-               _(u"square alen (Denmark)"):
-                       [(converters.m, .38121), '', ''],
-               _(u"ferfet (Iceland)"):
-                       [(converters.m, 0.09848), '', ''],
-               _(u"square vara (Spanish)"):
-                       [(converters.m, 0.59418), '', ''],
-               _(u"donum (Yugoslavia)"):
-                       [(converters.m, 699.99992), '', ''],
-               _(u"sahme (Egyptian)"):
-                       [(converters.m, 7.29106), '', ''],
-               _(u"tavola (Italian)"):
-                       [(converters.m, 37.62587), '', ''],
-               _(u"cuadra (Paraguay)"):
-                       [(converters.m, 7486.71249), '', ''],
-               _(u"acaena (Greek)"):
-                       [(converters.m, 9.19744), '', ''],
-               _(u"plethron (Greek)"):
-                       [(converters.m, 951.01483), '', ''],
-       },
-
-       _(u"Atomic Physics"): {
-               ".base_unit": _(u"radian per second"),
-               _(u"kilogram"):
-               [(converters.m, 2.997925e8**2*(1.0/1.054e-34)), "kg", ''],
-               _(u"joule"):
-               [(converters.m, 1.0/1.054e-34), '', _(u"Named after the English physicist James Prescott Joule (1818-1889). A unit of work which is equal to 10^7 units of work in the C. G. S. system of units (ergs), and is practically equivalent to the energy expended in one second by an electric current of one ampere in a resistance of one ohm. One joule is approximately equal to 0.738 foot pounds.")],
-               _(u"erg"):
-               [(converters.m, 1.0/1.054e-27), '', _(u"The unit of work or energy in the C. G. S. system, being the amount of work done by a dyne working through a distance of one centimeter; the amount of energy expended in moving a body one centimeter against a force of one dyne. One foot pound is equal to 13, 560, 000 ergs.")],
-               _(u"GeV Giga electronvolt"):
-               [(converters.m, 2.41796e23*2*math.pi), "Gev", ''],
-               _(u"neutron mass unit"):
-               [(converters.m, 1.00137*1836.11*3.75577e4*13.6058*2.41796e14*2*math.pi), '', ''],
-               _(u"proton mass unit"):
-               [(converters.m, 1836.11*3.75577e4*13.6058*2.41796e14*2*math.pi), '', ''],
-               _(u"atomic mass unit"):
-               [(converters.m, 1822.84*3.75577e4*13.6058*2.41796e14*2*math.pi), "amu", ''],
-               _(u"MeV Mega electronvolt"):
-               [(converters.m, 2.41796e20*2*math.pi), "MeV", ''],
-               _(u"electron rest mass"):
-               [(converters.m, 3.75577e4*13.6058*2.41796e14*2*math.pi), '', ''],
-               _(u"Rydberg constant"):
-               [(converters.m, 13.6058*2.41796e14*2*math.pi), '', _(u"Named after the Swedish physicist Johannes Robert Rydberg (1854-1919). A wave number characteristic of the wave spectrum of each element")],
-               _(u"electronvolt"):
-               [(converters.m, 2.41796e14*2*math.pi), "eV", _(u"A unit of energy equal to the work done by an electron accelerated through a potential difference of 1 volt.")],
-               _(u"kayser or cm^-1"):
-               [(converters.m, 2.997925e10*2*math.pi), "K", _('Named after the German physicist Heinrich Gustav Johannes Kayser (1853-1940). Used to measure light and other electromagnetic waves. The "wave number" in kaysers equals the number of wavelengths per centimeter.')],
-               _(u"kelvin"):
-               [(converters.m, 2.997925e8*2*math.pi/1.4387752e-2), "K", _(u"The basic unit of thermodynamic temperature adopted under the System International d'Unites")],
-               "m^-1":
-               [(converters.m, 2.997925e8*2*math.pi), '', ''],
-               _(u"millikayser"):
-               [(converters.m, 2.997925e7*2*math.pi), '', ''],
-               _(u"hertz"):
-               [(converters.m, 2*math.pi), "Hz", ''],
-               _(u"radian per second"):
-               [(converters.m, 1.0), "rad/s", ''],
-       },
-
-       _(u"Computer Data"): {
-               ".base_unit": _(u"bit"),
-               _(u"bit"):
-               [(converters.m, 1.0), '', _(u"One bit of data. Binary representation On/Off.")],
-               _(u"nibble | hexit | quadbit"):
-               [(converters.m, 4.0), '', _(u"One half a byte")],
-               _(u"byte"):
-               [(converters.m, 8.0), '', _(u"Eight bits")],
-               _(u"character"):
-               [(converters.m, 8.0), '', _('Usually described by one byte (256 possible characters can be defined by one byte).')],
-               _(u"kilobit"):
-               [(converters.m, 2.0**10.0), "kilobit", _(u"2^10 bits")],
-               _(u"megabit"):
-               [(converters.m, 2.0**20.0), "megabit", _(u"2^20 bits")],
-               _(u"kilobyte | kibi"):
-               [(converters.m, 1024.0*8), "K | Ki", _(u"2^10, 1024 bytes. 1024 comes from 2^10 which is close enough to 1000. kibi is the IEEE proposal.")],
-               _(u"megabyte | mebi"):
-               [(converters.m, 1024.0**2*8), "M | Mi", _(u"2^20, 1024^2 bytes. 1024 kilobytes. 1024 comes from 2^10 which is close enough to 1000. mebi is the IEEE proposal.")],
-               _(u"gigabyte | gibi"):
-               [(converters.m, 1024.0**3*8), "G | Gi", _(u"2^30, 1024^3. 1024 megabytes. 1024 comes from 2^10 which is close enough to 1000. gibi is the IEEE proposal.")],
-               _(u"terabyte | tebi"):
-               [(converters.m, 1024.0**4*8), "T | Ti", _(u"2^40, 1024^4. 1024 gigabytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
-               _(u"petabyte | pebi"):
-               [(converters.m, 1024.0**5*8), "P | Pi", _(u"2^50, 1024^5. 1024 terabytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
-               _(u"exabyte | exbi"):
-               [(converters.m, 1024.0**6*8), "E | Ei", _(u"2^60, 1024^6, 1024 petabytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
-               _(u"zebi | zettabyte"):
-               [(converters.m, 1024.0**7*8), "Zi", _(u"1024^7. 1024 exbibytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
-               _(u"yobi | yottabyte"):
-               [(converters.m, 1024.0**8*8), "Yi", _(u"1024^8. 1024 yobibytes. 1024 comes from 2^10 which is close enough to 1000. tebi is the IEEE proposal.")],
-       },
-
-       _(u"Computer Data flow rate"): {
-               ".base_unit": _('bits per second'),
-               _(u"baud: 1"):
-               [(converters.m, 1.0), "", _(u'Symbol rate for 1 bit per symbol. Named after the French telegraph engineer Jean-Maurice-\u00C9mile Baudot (1845 - 1903). Data transmission measured in symbols per second.')],
-               _(u"baud: 10"):
-               [(converters.m, 10.0), "", _(u'Symbol rate for 10 bits per symbol. Named after the French telegraph engineer Jean-Maurice-\u00C9mile Baudot (1845 - 1903). Data transmission measured in symbols per second.')],
-               _(u"baud: 8"):
-               [(converters.m, 8.0), "", _(u'Symbol rate for 8 bits per symbol. Named after the French telegraph engineer Jean-Maurice-\u00C9mile Baudot (1845 - 1903). Data transmission measured in symbols per second.')],
-               _(u"baud: 4"):
-               [(converters.m, 4.0), "", _(u'Symbol rate for 4 bits per symbol. Named after the French telegraph engineer Jean-Maurice-\u00C9mile Baudot (1845 - 1903). Data transmission measured in symbols per second.')],
-               _(u"bits per second"):
-               [(converters.m, 1.0), "bps", _(u" ")],
-               _(u"characters per second"):
-               [(converters.m, 10.0), "cps", _('Rate to transmit one character. The character is usually described as one byte with one stop bit and one start bit (10 bits in total).')],
-       },
-
-       _(u"Computer Numbers"): {
-               ".base_unit": _(u"base 10 decimal"),
-               _(u"base  2 binary"):
-               [(converters.b, 2), "base  2", _('Base two numbering system using the digits 0-1')],
-               _(u"base  3 ternary | trinary"):
-               [(converters.b, 3), "base  3", _(u"Base three numbering system using the digits 0-2. Russian Nikolay Brusentsov built a trinary based computer system.")],
-               _(u"base  4 quaternary | quadrary"):
-               [(converters.b, 4), "base  4", _(u"Base four numbering system using the digits 0-3.")],
-               _(u"base  5 quinary"):
-               [(converters.b, 5), "base  5", _(u"Base five numbering system using the digits 0-4.")],
-               _(u"base  6 senary | hexary"):
-               [(converters.b, 6), "base  6", _(u"Base six numbering system using the digits 0-5.")],
-               _(u"base  7 septenary | septary"):
-               [(converters.b, 7), "base  7", _(u"Base seven numbering system using the digits 0-6.")],
-               _(u"base  8 octonary | octal | octonal | octimal"):
-               [(converters.b, 8), "base  8", _(u"Base eight numbering system using the digits 0-7. Commonly used in older computer systems.")],
-               _(u"base  9 nonary"):
-               [(converters.b, 9), "base  9", _(u"Base nine numbering system using the digits 0-8.")],
-               _(u"base 10 decimal"):
-               [(converters.b, 10), "base 10", _(u"Base ten numbering system using the digits 0-9.")],
-               _(u"base 11 undenary"):
-               [(converters.b, 11), "base 11", _(u"Base eleven numbering system using the digits 0-9, a.")],
-               _(u"base 12 duodecimal"):
-               [(converters.b, 12), "base 12", _(u"Base twelve numbering system using the digits 0-9, a-b.")],
-               _(u"base 13 tridecimal"):
-               [(converters.b, 13), "base 13", _('Base Thirteen numbering system using the digits 0-9, a-c.')],
-               _(u"base 14 quattuordecimal"):
-               [(converters.b, 14), "base 14", _(u"Base Fourteen numbering system using the digits 0-9, a-d.")],
-               _(u"base 15 quindecimal"):
-               [(converters.b, 15), "base 15", _(u"Base Fifteen numbering system using the digits 0-9, a-e.")],
-               _(u"base 16 sexadecimal | hexadecimal | hex"):
-               [(converters.b, 16), "base 16", _(u"Base Sixteen numbering system using the digits 0-1, a-f. Commonly used in computer systems.")],
-               _(u"base 17 septendecimal"):
-               [(converters.b, 17), "base 17", _(u"Base Sixteen numbering system using the digits 0-1, a-g.")],
-               _(u"base 18 octodecimal"):
-               [(converters.b, 18), "base 18", _(u"Base Sixteen numbering system using the digits 0-1, a-h.")],
-               _(u"base 19 nonadecimal"):
-               [(converters.b, 19), "base 19", _(u"Base Sixteen numbering system using the digits 0-1, a-i.")],
-               _(u"base 20 vigesimal"):
-               [(converters.b, 20), "base 20", _(u"Base Twenty numbering system using the digits 0-1, a-j.")],
-               _(u"base 30 trigesimal"):
-               [(converters.b, 30), "base 30", _(u"Base Thirty numbering system using the digits 0-1, a-r.")],
-               _(u"base 36 hexatrigesimal"):
-               [(converters.b, 36), "base 36", _(u"Base Thirty-six numbering system using the digits 0-9, a-z.")],
-               _(u"base 40 quadragesimal"):
-               [(converters.b, 40), "base 40", _(u"Base Forty digits numbering system using the digits 0-1, a-f, A-C.")],
-               _(u"base 50 quinquagesimal"):
-               [(converters.b, 50), "base 50", _(u"Base Fifty digits numbering system using the digits 0-1, a-f, A-M.")],
-               _(u"base 60 sexagesimal"):
-               [(converters.b, 60), "base 60", _(u"Base Sixty numbering system using the digits 0-9, a-z, A-V.")],
-               _(u"base 62 duosexagesimal"):
-               [(converters.b, 62), "base 62", _('Base Sixty-two numbering system using the digits 0-9, a-z, A-Z. This is the highest numbering system that can be represented with all decimal numbers and lower and upper case English alphabet characters. Other number systems include septagesimal (base 70), octagesimal (base 80), nonagesimal (base 90), centimal (base 100), bicentimal (base 200), tercentimal (base 300), quattrocentimal (base 400), quincentimal (base 500).')],
-               _(u"roman numerals"):
-               [(converters.r, 0), '', _('A symbol set in the old Roman notation; I, V, X, L, C, D, M. Range 1 to 3999 (higher values cannot be represented with standard ASCII characters).')],
-       },
-       _('Density'): {
-               ".base_unit": _(u"kilogram/cubic meter"),
-               _('kilogram per cubic meter'):
-               [(converters.m, 1.0), u"kg/m\xb3", ''],
-               _('kg per cubic cm'):
-               [(converters.m, 1.0e6), u"kg/cm\xb3", _(u"kilograms per cubic centimeter.")],
-               _(u"pound mass per gallon (UK)"):
-               [(converters.m, 99.7763664739553), "lbm/gal", _(u"Pounds mass per US liquid gallon.")],
-               _(u"pound mass per gallon (US)"):
-               [(converters.m, 119.826427316897), "lbm/gal", _(u"Pounds mass per US liquid gallon.")],
-               _(u"slug per cubic ft"):
-               [(converters.m, 515.3788), u"slug/ft\xb3", ''],
-               _(u"gram per cubic cm "):
-               [(converters.m, 1000.0), u"g/cm\xb3", ''],
-               _(u"gram per cubic meter "):
-               [(converters.m, .001), u"g/m\xb3", ''],
-               _(u"milligram/cubic meter "):
-               [(converters.m, 1.0e-6), u"mg/m\xb3", ''],
-               _(u"kilogram per liter"):
-               [(converters.m, 1000.0), "kg/l", ''],
-               _(u"metric ton per cubic meter"):
-               [(converters.m, 1000.0), u"metric ton/m\xb3", ''],
-               _(u"pound per cubic foot"):
-               [(converters.m, 0.45359237/0.028316846592), u"lbm/ft\xb3", _(u"Pounds mass per cubic foot.")],
-               _(u"pound per cubic inch"):
-               [(converters.m, 0.45359237/0.000016387064), u"lbm/in\xb3", _(u"Pounds mass per cubic inch.")],
-               _(u"aluminum"):
-               [(converters.m, 2643.0), "Al", _(u"Enter 1 here to find the density of aluminum.")],
-               _(u"iron"):
-               [(converters.m, 7658.0), "Fe", _(u"Enter 1 here to find the density of iron.")],
-               _(u"copper"):
-               [(converters.m, 8906.0), "Cu", _(u"Enter 1 here to find the density of copper.")],
-               _(u"lead"):
-               [(converters.m, 11370.0), "Pb", _(u"Enter 1 here to find the density of lead.")],
-               _(u"gold"):
-               [(converters.m, 19300.0), "Au", _(u"Enter 1 here to find the density of gold.")],
-               _(u"silver"):
-               [(converters.m, 10510.0), "Ag", _(u"Enter 1 here to find the density of silver.")],
-               _(u"water at 4degC"):
-               [(converters.m, 1000.0), u"H20 at 4\xb0C", _(u"Enter 1 here to find the density of water at 4\xb0C. Water weighs 1 gram per cm\xb3.")],
-               _(u"ounces per gallon (UK)"):
-               [(converters.m, (6.23602290462221)), _(u"oz/gal"), ''],
-               _(u"ounces per gallon (US)"):
-               [(converters.m, (7.48915170730604)), _(u"oz/gal"), ''],
-               _(u"ton (UK | long) per cubic yard"):
-               [(converters.m, 2240.0 * 0.45359237/0.764554857984), u"ton/yard\xb3", ''],
-               _(u"ton (UK | long) per cubic foot"):
-               [(converters.m, 2240.0 * 0.45359237/0.764554857984*27.0), u"ton/ft\xb3", ''],
-               _(u"ton (US | short) per cubic yard"):
-               [(converters.m, 2000.0 * 0.45359237/0.764554857984), u"ton/yard\xb3", ''],
-               _(u"ton (US | short) per cubic foot"):
-               [(converters.m, 32040.0), u"ton/ft\xb3", ''],
-       },
-
-       _(u"Electrical Current"): {
-               ".base_unit": _(u"ampere"),
-               _(u"ampere"):
-               [(converters.m, 1.0), "A", u"Named after the French physicist Andr\x82 Marie Amp\x82re (1775-1836). The unit of electric current; -- defined by the International Electrical Congress in 1893 and by U. S. Statute as, one tenth of the unit of current of the C. G. S. system of electro-magnetic units, or the practical equivalent of the unvarying current which, when passed through a standard solution of nitrate of silver in water, deposits silver at the rate of 0.001118 grams per second."],
-               _(u"kiloampere"):
-               [(converters.m, 1.0e3), "kA", ''],
-               _(u"milliampere"):
-               [(converters.m, 1.0e-3), "mA", ''],
-               _(u"microampere"):
-               [(converters.m, 1.0e-6), u"\xb5A", ''],
-               _(u"nanoampere"):
-               [(converters.m, 1.0e-9), "nA", ''],
-               _(u"math.picoampere"):
-               [(converters.m, 1.0e-12), "pA", ''],
-               _(u"abampere"):
-               [(converters.m, 10.0), "abA", _(u"The CGS electromagnetic unit of current.")],
-               _(u"coulomb per second"):
-               [(converters.m, 1.0), '', ''],
-               _(u"statampere"):
-               [(converters.m, 1.e-9/3), '', _(u"The CGS electrostatic unit of current.")],
-               },
-
-       _(u"Electrical Charge"): {
-               ".base_unit": _(u"coulomb"),
-               _(u"faraday"):
-               [(converters.m, 96.5e3), '', _(u"Named after Michael Faraday the The English physicist and chemist who discovered electromagnetic induction (1791-1867). The amount of electric charge that liberates one gram equivalent of any ion from an electrolytic solution. ")],
-               _(u"kilocoulomb"):
-               [(converters.m, 1.0e3), "kC", ''],
-               _(u"ampere-hour"):
-               [(converters.m, 3.6e3), u"A\xb7h", _(u"Commonly used to describe the capacity of a battery.")],
-               _(u"abcoulomb"):
-               [(converters.m, 10.0), "abC", _(u"The CGS electromagnetic unit of charge.")],
-               _(u"coulomb (weber)"):
-               [(converters.m, 1.0), "C", _(u"Named after the French physicist and electrican Coulomb. (Physics) The standard unit of quantity in electrical measurements. It is the quantity of electricity conveyed in one second by the current produced by an electro-motive force of one volt acting in a circuit having a resistance of one ohm, or the quantity transferred by one amp`ere in one second. Formerly called weber.")],
-               _(u"microcoulomb"):
-               [(converters.m, 1.0e-6), u"\xb5C", ''],
-               _(u"nanocoulomb"):
-               [(converters.m, 1.0e-9), "nC", ''],
-               _(u"statcoulomb"):
-               [(converters.m, 1.0e-9/3), "sC", _(u"The CGS electrostatic unit of charge.")],
-               _(u"electron charge"):
-               [(converters.m, 1.0/(6.2414503832469e18)), '', ''],
-               },
-_(u"Electrical Voltage"): {".base_unit": _(u"volt"),
-               _(u"abvolt"):
-               [(converters.m, 1.0e-8), "abV", _(u"A unit of potential equal to one-hundred-millionth of a volt.")],
-               _(u"volt"):
-               [(converters.m, 1.0), "V", _(u"""Named after the Italian electrician Alessandro Volta. The unit of electro-motive force; -- defined by the International Electrical Congress in 1893 and by United States Statute as, that electro-motive force which steadily applied to a conductor whose resistance is one ohm will produce a current of one ampere. It is practically equivalent to 1000/1434 the electro-motive force of a standard Clark's cell at a temperature of 15deg C.""")],
-               _(u"gigavolt"):
-               [(converters.m, 1.0e9), "GV", _(u"One billion volts.")],
-               _(u"megavolt"):
-               [(converters.m, 1.0e6), "MV", _(u"One million volts.")],
-               _(u"kilovolt"):
-               [(converters.m, 1.0e3), "kV", _(u"One thousand volts.")],
-               _(u"millivolt"):
-               [(converters.m, 1.0e-3), "mV", _(u"One thousandth of an volt.")],
-               _(u"microvolt"):
-               [(converters.m, 1.0e-6), u"\xb5V", _(u"One millionth of an volt.")],
-               _(u"nanovolt"):
-               [(converters.m, 1.0e-9), "nV", _(u"One billionth of an volt.")],
-               _(u"statvolt"):
-               [(converters.m, 300.0), '', _(u"300 volts.")],
-       },
-
-       _(u"Electrical Resistance & Conductance"): {
-               ".base_unit": _(u"ohm"),
-               _(u"ohm"):
-               [(converters.m, 1.0), "ohm", _(u"Named after the German physicist Georg Simon Ohm (1787-1854). The standard unit in the measure of electrical resistance, being the resistance of a circuit in which a potential difference of one volt produces a current of one ampere. As defined by the International Electrical Congress in 1893, and by United States Statute, it is a resistance substantially equal to 10^9 units of resistance of the C.G.S. system of electro-magnetic units, and is represented by the resistance offered to an unvarying electric current by a column of mercury at the temperature of melting ice 14.4521 grams in mass, of a constant cross-sectional area, and of the length of 106.3 centimeters. As thus defined it is called the international ohm")],
-               _(u"siemens | mho"):
-               [(converters.inv, 1.0), "S", _(u"Named after Ernst Werner von Siemens (1816-1892). A unit describing how well materials conduct equal to the reciprocal of an ohm syn: mho, S")],
-               _(u"abmho"):
-               [(converters.inv, 1.0e-9), "abmho", ''],
-               _(u"millisiemens | millimho"):
-               [(converters.inv, 1.0e3), "mS", ''],
-               _(u"microsiemens | micromho"):
-               [(converters.inv, 1.0e6), u"\xb5S", ''],
-               _(u"statmho"):
-               [(converters.inv, 8.99e11), '', ''],
-               _(u"gigaohm"):
-               [(converters.m, 1.0e9), _(u"G ohm"), _(u"One billion ohms.")],
-               _(u"megaohm"):
-               [(converters.m, 1.0e6), _(u"M ohm"), _(u"One million ohms.")],
-               _(u"kilohm"):
-               [(converters.m, 1.0e3), _(u"k ohm"), _(u"One thousand ohms.")],
-               _(u"milliohm"):
-               [(converters.m, 1.0e-3), _(u"m ohm"), _(u"One thousandth of an ohm.")],
-               _(u"microhm"):
-               [(converters.m, 1.0e-6), u"\xb5 ohm", _(u"One millionth of an ohm.")],
-               _(u"nanohm"):
-               [(converters.m, 1.0e-9), "n ohm", _(u"One billionth of an ohm.")],
-               _(u"abohm"):
-               [(converters.m, 1.0e-9), "ab ohm", ''],
-               _(u"statohm"):
-               [(converters.m, 8.99e5*1e6), '', ''],
-       },
-
-       _(u"Electrical Inductance"): {
-               ".base_unit": _(u"henry"),
-               _(u"henry"):
-               [(converters.m, 1.0), "H", _(u"Named after the American physicist Joseph Henry (1797-1878). The unit of electric induction; the induction in a circuit when the electro-motive force induced in this circuit is one volt, while the inducing current varies at the rate of one ampere a second.")],
-               _(u"stathenry"):
-               [(converters.m, 8.99e11), '', ''],
-               _(u"ohm-second"):
-               [(converters.m, 1.0), u"ohm\xb7sec", ''],
-               _(u"millihenry"):
-               [(converters.m, 1.0e-3), "mH", ''],
-               _(u"microhenry"):
-               [(converters.m, 1.0e-6), u"\xb5H", ''],
-               _(u"nanohenry"):
-               [(converters.m, 1.0e-9), "nH", ''],
-               _(u"abhenry"):
-               [(converters.m, 1.0e-9), "abH", ''],
-               },
-       _(u"Electrical Capacitance"): {
-               ".base_unit": _(u"farad"),
-               _(u"farad"):
-               [(converters.m, 1.0), "F", _(u"Named after the English electrician Michael Faraday. The standard unit of electrical capacity; the capacity of a condenser whose charge, having an electro-motive force of one volt, is equal to the amount of electricity which, with the same electromotive force, passes through one ohm in one second; the capacity, which, charged with one coulomb, gives an electro-motive force of one volt.")],
-               _(u"abfarad"):
-               [(converters.m, 1e9), "abF", _(u"A capacitance unit equal to one billion farads")],
-               _(u"second/ohm"):
-               [(converters.m, 1.0), '', ''],
-               _(u"microfarad"):
-               [(converters.m, 1e-6), u"\xb5F", ''],
-               _(u"statfarad"):
-               [(converters.m, 1.0e-6/8.99e5), '', ''],
-               _(u"nanofarad"):
-               [(converters.m, 1e-9), "nF", ''],
-               _(u"picofarad"):
-               [(converters.m, 1e-12), "pF", ''],
-               },
-       _(u"Electromagnetic Radiation"): {
-               ".base_unit": _(u"hertz"),
-               _(u"hertz"):
-               [(converters.m, 1.0), 'Hz', _(u"""Named after the German physicist Heinrich Hertz (1857-1894) who was the first to produce electromagnetic waves artificially. Having a periodic interval of one second.""")],
-               _(u"meter"):
-               [(converters.inv, 299792458.0), "m", _(u"Equal to 39.37 English inches, the standard of linear measure in the metric system of weights and measures. It was intended to be, and is very nearly, the ten millionth part of the distance from the equator to the north pole, as ascertained by actual measurement of an arc of a meridian.")],
-               _(u"centimeter"):
-               [(converters.inv, 29979245800.0), "cm", ''],
-               _(u"millimeter"):
-               [(converters.inv, 299792458000.0), "mm", ''],
-               _(u"micrometer | micron"):
-               [(converters.inv, 299792458000000.0), u"\xb5m", _(u"A metric unit of length equal to one millionth of a meter. The thousandth part of one millimeter.")],
-               _(u"nanometer"):
-               [(converters.inv, 299792458000000000.0), "nm", _(u"A metric unit of length equal to one billionth of a meter.")],
-               _(u"angstrom"):
-               [(converters.inv, 2997924580000000000.0), u"\xc5", _(u"Equal to one ten billionth of a meter (or 0.0001 micron); used to specify wavelengths of electromagnetic radiation")],
-               _(u"kilohertz"):
-               [(converters.m, 1.0e3), "KHz", ''],
-               _(u"megahertz"):
-               [(converters.m, 1.0e6), "MHz", ''],
-               _(u"gigahertz"):
-               [(converters.m, 1.0e9), "GHz", ''],
-               _(u"terahertz"):
-               [(converters.m, 1.0e12), "THz", ''],
-               _(u"petahertz"):
-               [(converters.m, 1.0e15), "PHz", ''],
-               _(u"exahertz"):
-               [(converters.m, 1.0e18), "EHz", ''],
-               _(u"electron Volt"):
-               [(converters.m, 1/4.13566e-15), "eV", _(u"Energy. e=h\xf6f where h = Planks constant (4.13566 x 10^-15 electron volts/second). f = frequency in Hertz.")],
-       },
-
-       _(u"Energy | Work"): {
-               ".base_unit": _(u"joule | wattsecond | newton-meter"),
-               _(u"kiloton"):
-               [(converters.m, 4200.0e9), '', _(u"A measure of explosive power (of an atomic weapon) equal to that of 1000 tons of TNT")],
-               _(u"gigawatt-hour"):
-               [(converters.m, 3.6e12), "GWh", ''],
-               _(u"megawatt-hour"):
-               [(converters.m, 3.6e9), "MWh", ''],
-               _(u"kilowatt-hour"):
-               [(converters.m, 3.6e6), "kWh", ''],
-               _(u"horsepower-hour"):
-               [(converters.m, 2.686e6), u"hp\xb7h", ''],
-               _(u"gigajoule"):
-               [(converters.m, 1.0e9), "GJ", ''],
-               _(u"megajoule"):
-               [(converters.m, 1.0e6), "MJ", ''],
-               _(u"kg force meters"):
-               [(converters.m, 9.80665), u"kgf\xb7m", _(u"Work done by one kilogram of force acting through a distance of one meter.")],
-               _(u"kilojoule"):
-               [(converters.m, 1.0e3), "kJ", ''],
-               _(u"watt-hour"):
-               [(converters.m, 3.6e3), "Wh", ''],
-               _(u"British thermal unit"):
-               [(converters.m, 1.055e3), "Btu", ''],
-               _(u"joule | wattsecond | newton-meter"):
-               [(converters.m, 1.0), "J", _(u"Named after the English physicist James Prescott Joule(1818-1889). A unit of work which is equal to 10^7 units of work in the C. G. S. system of units (ergs), and is practically equivalent to the energy expended in one second by an electric current of one ampere in a resistance of one ohm. One joule is approximately equal to 0.738 foot pounds.")],
-               _(u"kilocalorie"):
-               [(converters.m, 4.184e3), "kcal", ''],
-               _(u"calorie"):
-               [(converters.m, 4.184), "cal", _(u"The unit of heat according to the French standard; the amount of heat required to raise the temperature of one kilogram (sometimes, one gram) of water one degree centigrade, or from 0deg to 1deg.")],
-               _(u"foot-poundals"):
-               [(converters.m, 0.04214), '', ''],
-               _(u"foot-pound force"):
-               [(converters.m, 1.356), u"ft\xb7lbf", _(u"A unit of work equal to a force of one pound moving through a distance of one foot")],
-               _(u"millijoule"):
-               [(converters.m, 1.0e-3), "mJ", ''],
-               _(u"microjoule"):
-               [(converters.m, 1.0e-6), u"\xb5J", ''],
-               _(u"attojoule"):
-               [(converters.m, 1.0e-18), "aJ", ''],
-               _(u"erg | dyne-centimeter"):
-               [(converters.m, 1.0e-7), '', _(u"The unit of work or energy in the C. G. S. system, being the amount of work done by a dyne working through a distance of one centimeter; the amount of energy expended in moving a body one centimeter against a force of one dyne. One foot pound is equal to 13, 560, 000 ergs.")],
-               _(u"GeV"):
-               [(converters.m, 1.0e-9/6.24), '', _(u"A billion electronvolts")],
-               _(u"MeV"):
-               [(converters.m, 1.0e-12/6.24), '', _(u"a million electronvolts")],
-               _(u"electron volt"):
-               [(converters.m, 1.0e-18/6.24), "eV", _(u"A unit of energy equal to the work done by an electron accelerated through a potential difference of 1 volt")],
-               #1 cubic foot of natural gas ... 1, 008 to 1, 034 Btu
-               _(u"therm of natural gas"):
-               [(converters.m, 1.055e8), "", '1 therm of natural gas = 100, 000 Btu'],
-               _(u"gallon of liquefied petroleum gas"):
-               [(converters.m, 1.055e3*95475), "LPG", '1 gallon of liquefied petroleum gas = 95, 475 Btu'],
-               _(u"gallon of crude oil"):
-                       [(converters.m, 1.055e3*138095), "", '1 gallon of crude oil = 138, 095 Btu'],
-               _(u"barrel of crude oil"):
-                       [(converters.m, 1.055e3*5800000), "", '1 barrel of crude oil = 5, 800, 000 Btu'],
-               _(u"gallon of kerosene or light distillate oil"):
-                       [(converters.m, 1.055e3*135000), "", '1 gallon of kerosene or light distillate oil = 135, 000 Btu '],
-               _(u"gallon middle distillate or diesel fuel oil"):
-                       [(converters.m, 1.055e3*138690), "", '1 gallon middle distillate or diesel fuel oil = 138, 690 Btu '],
-               _(u"gallon residential fuel oil"):
-                       [(converters.m, 1.055e3*149690), "", '1 gallon residential fuel oil = 149, 690 Btu'],
-               _(u"gallon of gasoline"):
-                       [(converters.m, 1.055e3*125000), "", '1 gallon of gasoline = 125, 000 Btu'],
-               _(u"gallon of ethanol"):
-                       [(converters.m, 1.055e3*84400), "", '1 gallon of ethanol = 84, 400 Btu'],
-               _(u"gallon of methanol"):
-                       [(converters.m, 1.055e3*62800), "", '1 gallon of methanol = 62, 800 Btu'],
-               _(u"gallon gasohol (10% ethanol, 90% gasoline)"):
-                       [(converters.m, 1.055e3*120900), "", '1 gallon gasohol (10% ethanol, 90% gasoline) = 120, 900 Btu'],
-#              _(u"pound of coal"):
-#                      [(converters.m, 1.055e3), "", 'pound of coal = 8, 100-13, 000 Btu'],
-#              _(u"ton of coal"):
-#                      [(converters.m, 1.055e3), "", '1 ton of coal = 16, 200, 00-26, 000, 000 Btu'],
-               _(u"ton of coke"):
-                       [(converters.m, 1.055e3*26000000), "", '1 ton of coke = 26, 000, 000 Btu'],
-# 1 ton of wood = 9, 000, 00-17, 000, 000 Btu
-#              _(u""):
-#                      [(converters.m, 1.055e3), "", ''],
-# 1 standard cord of wood = 18, 000, 000-24, 000, 000 Btu
-#              _(u""):
-#                      [(converters.m, 1.055e3), "", ''],
-# 1 face cord of wood = 6, 000, 000-8, 000, 000 Btu
-#              _(u""):
-#                      [(converters.m, 1.055e3), "", ''],
-
-# GJ to therm and MBTUs would be nice too.
-               _(u"therm"):
-                       [(converters.m, 1.055e-3*10000), "", '10^5 BTUs'],
-
-
-               _(u"Mega British thermal unit"):
-                       [(converters.m, 1.055e-3), "MBtu", 'Million British thermal units'],
-
-               _(u"pound of carbon (upper heating value)"):
-                       [(converters.m, 1.055e3*14550), "", '1 pound of carbon is 14, 550 btu (upper heating value).'],
-       },
-
-       _(u"Flow (dry)"): {
-               ".base_unit": "litres per second",
-               _(u"litres per second"):
-               [(converters.m, 1.0), "lps", _(u"A cubic decimeter of material moving past a point every second.")],
-               _(u"litres per minute"):
-               [(converters.m, 1.0/60), "lpm", _(u"A cubic decimeter of material moving past a point every minute.")],
-               _(u"cubic feet per minute"):
-               [(converters.m, 1/(60*0.0353146667215)), "cfm", _(u"Commonly used to describe the flow rate produced by a large fan or blower.")],
-               _(u"cubic feet per second"):
-               [(converters.m, 1/0.0353146667215), "cfs", ''],
-               _(u"cubic inches per minute"):
-               [(converters.m, 1/(60*61.0237440947)), u"in\xb3/m", ''],
-               _(u"cubic inches per second"):
-               [(converters.m, 1/61.0237440947), u"in\xb3/s", ''],
-       },
-
-       _(u"Flow (liquid)"): {
-               ".base_unit": "litres per second",
-               _(u"litres per second"):
-               [(converters.m, 1.0), "lps", _(u"A cubic decimeter of material moving past a point every second")],
-               _(u"litres per minute"):
-               [(converters.m, 1.0/60), "lpm", ''],
-               _(u"US gallons per minute"):
-               [(converters.m, 60*3.785411784), "gpm (US)", ''],
-               _(u"US gallons per second"):
-               [(converters.m, 3.785411784), "gps (US)", ''],
-               _(u"UK gallons per minute"):
-               [(converters.m, 60*4.54609028199), "gpm (UK)", ''],
-               _(u"UK gallons per second"):
-               [(converters.m, 4.54609028199), "gps (UK)", ''],
-       },
-
-       _(u"Force"): {
-               ".base_unit": "newton",
-               _(u"tonne of force"):
-               [(converters.m, 9806.65), '', _(u"Metric ton of force, 1000 kilonewtons.")],
-               _(u"ton of force"):
-               [(converters.m, 2000*4.4482216152605), "tnf", _(u"2000 pounds of force.")],
-               _(u"sthene"):
-               [(converters.m, 1.0e3), '', _(u"Named from the Greek word sthenos, strength. One sthene is the force required to accelerate a mass of one tonne at a rate of 1 m/s2. ")],
-               _(u"atomic weight"):
-               [(converters.m, 1.6283353926E-26), '', _(u"Generally understood as the weight of the hydrogen atom.")],
-               _(u"kip"):
-               [(converters.m, 4.4482216152605e3), '', _(u"Kilopounds of force.")],
-               _(u"kilonewton"):
-               [(converters.m, 1.0e3), "kN", ''],
-               _(u"kilogram force | kilopond"):
-               [(converters.m, 9.80665), "kgf", ''],
-               _(u"pound force"):
-               [(converters.m, 4.4482216152605), "lbf", ''],
-               _(u"newton"):
-               [(converters.m, 1.0), "N", _(u"Named after the English mathematician and physicist Sir Isaac Newton (1642-1727). A unit of force equal to the force that imparts an acceleration of 1 m/sec\xb2 to a mass of 1 kilogram; equal to 100, 000 dynes")],
-               _(u"ounce force"):
-               [(converters.m, 4.4482216152605/16), "ozf", ''],
-               _(u"poundal"):
-               [(converters.m, 0.138254954376), "pdl", _(u"A unit of force based upon the pound, foot, and second, being the force which, acting on a pound avoirdupois for one second, causes it to acquire by the of that time a velocity of one foot per second. It is about equal to the weight of half an ounce, and is 13, 825 dynes.")],
-               _(u"gram force"):
-               [(converters.m, 9.80665/1e3), "gf", ''],
-               _(u"millinewton"):
-               [(converters.m, 1.0e-3), "mN", ''],
-               _(u"dyne"):
-               [(converters.m, 1.0e-5), "dyn", _(u"The unit of force, in the C. G. S. (Centimeter Gram Second) system of physical units; that is, the force which, acting on a gram for a second, generates a velocity of a centimeter per second.")],
-               _(u"micronewton"):
-               [(converters.m, 1.0e-6), u"\xb5N", ''],
-       },
-
-       _(u"Length"): {
-               ".base_unit": "meter",
-               _(u"klafter | faden (German)"):
-               [(converters.m, 1.8965), '', _(u"Similar to the fathom.")],
-               _(u"klafter | faden (Switzerland)"):
-               [(converters.m, 1.8), '', _(u"Similar to the fathom.")],
-               _(u"earth diamater"):
-               [(converters.m, 12742630), '', _(u"Diameter for the Earth.")],
-               _(u"actus (roman actus)"):
-               [(converters.m, 35.47872), '', _(u"Land measurement, 120 Roman feet (pedes monetales). This was equivalent to 35.47872 meters.")],
-               _(u"angstrom"):
-               [(converters.m, 1.0e-10), u"\xc5", _(u"Equal to one ten billionth of a meter (or 0.0001 micron); used to specify wavelengths of electromagnetic radiation")],
-               _(u"arshin | arshine | archin"):
-               [(converters.m, 0.7112), '', _(u"Russian.  28 inches")],
-               _(u"arpentcan"):
-               [(converters.m, 44289.14688), '', _(u"arpentcan = 27.52 mile")],
-               _(u"arpent (Canadian)"):
-               [(converters.m, 58.471308), '', _(u"Canadian unit of land measurement. 191.835 ft")],
-               _(u"arpentlin | French arpent"):
-               [(converters.m, 30*6.395*12*(25.4/1000)), '', _(u"French unit of land measurement. 30 toises")],
-               _(u"assbaa"):
-               [(converters.m, 0.02), '', _(u"Arabian measure.")],
-               _(u"astronomical unit"):
-               [(converters.m, 149597871000.0), "AU", _(u"Used for distances within the solar system; equal to the mean distance between the Earth and the Sun (approximately 93 million miles or 150 million kilometers).")],
-               _(u"barleycorn"):
-               [(converters.m, 8.46666666666667E-03), '', _(u"Formerly, a measure of length, equal to the average length of a grain of barley; the third part of an inch.")],
-               _(u"bohr radius"):
-               [(converters.m, 52.9177/1e12), '', _(u"Named after the Danish physicist Niels Bohr (1885-1962), who explained the structure of atoms in 1913. The bohr radius represents the mean distance between the proton and the electron in an unexcited hydrogen atom. 52.9177 picometers. ")],
-               _(u"bolt"):
-               [(converters.m, 36.576), '', _(u"A compact package or roll of cloth, as of canvas or silk, often containing about forty yards.")],
-               _(u"bottom measure"):
-               [(converters.m, (25.4/1000)/40), '', _(u"One fortieth of an inch.")],
-               _(u"cable length"):
-               [(converters.m, 219.456), '', _(u"A nautical unit of depth. 720 feet.")],
-               _(u"caliber (gun barrel caliber)"):
-               [(converters.m, 0.000254), '', _(u"The diameter of round or cylindrical body, as of a bullet or column.")],
-               _(u"cane"):
-               [(converters.m, 3.84049), '', _(u"Persian")],
-               _(u"chain (surveyors | Gunters)"):
-               [(converters.m, 20.1168), '', _(u"A surveyors instrument which consists of links and is used in measuring land.One commonly in use is Gunter's chain, which consists of one hundred links, each link being seven inches and ninety-two one hundredths in length; making up the total length of rods, or sixty-six, feet; hence, a measure of that length; hence, also, a unit for land measure equal to four rods.")],
-               _(u"chain (engineers)"):
-               [(converters.m, 100*(12*25.4/1000)), '', _(u"100 ft.")],
-               _(u"charac"):
-               [(converters.m, 0.2601), '', _(u"Persian")],
-               _(u"chebel"):
-               [(converters.m, 21.03124), '', _(u"Persian")],
-               _(u"city block"):
-               [(converters.m, 100*(36*25.4/1000)), '', _(u"An informal measurement, about 100 yards")],
-               _(u"cubit (Biblical | Hebrew | English)"):
-               [(converters.m, 18.00*(25.4/1000)), '', _(u"A measure of length, being the distance from the elbow to the extremity of the middle finger. Note: The cubit varies in length in different countries, the English, Hebrew and Biblical cubits are 18 inches.")],
-               _(u"cubit (Indian) | hasta"):
-               [(converters.m, 0.64161), '', ''],
-               _(u"cubit (Roman)"):
-               [(converters.m, 17.47*(25.4/1000)), '', _(u"A measure of length, being the distance from the elbow to the extremity of the middle finger. Note: The cubit varies in length in different countries, the Roman cubit is 17.47 inches.")],
-               _(u"cubit (Greek) | pechya"):
-                       [(converters.m, 18.20*(25.4/1000)), '', _(u"A measure of length, being the distance from the elbow to the extremity of the middle finger. Note: The cubit varies in length in different countries, the Greek cubit is 18.20 inches.")],
-               _(u"cubit (Israeli)"):
-                       [(converters.m, 0.55372), '', _(u"A measure of length, being the distance from the elbow to the extremity of the middle finger. Note: The cubit varies in length in different countries, the Israeli cubit is 21.8 inches.")],
-               _(u"cloth finger"):
-                       [(converters.m, 4.5*(25.4/1000)), '', _(u"Used in sewing")],
-               _(u"cloth quarter"):
-                       [(converters.m, 9*(25.4/1000)), '', _(u"Used in sewing")],
-               _(u"compton wavelength of the electron"):
-                       [(converters.m, 1836.11*1.00138*1.31962/1e15), '', _(u"Named after Arthur Holly Compton (1892-1962)")],
-               _(u"compton wavelength of the proton"):
-                       [(converters.m, 1.00138*1.31962/1e15), '', _(u"Named after Arthur Holly Compton (1892-1962)")],
-               _(u"compton wavelength of the neutron"):
-                       [(converters.m, 1.31962/1e15), '', _(u"Named after Arthur Holly Compton (1892-1962)")],
-               _(u"classical electron radius"):
-                       [(converters.m, 2.13247*1.00138*1.31962/1e15), '', ''],
-               _(u"digit | digitus"):
-                       [(converters.m, 0.018542), '', _(u"A finger's breadth, commonly estimated to be three fourths of an inch.")],
-               _(u"decimeter"):
-                       [(converters.m, 1.0e-1), "dm", """The tenth part of a meter; a measure of length equal to rather more than 3.937 of an inch."""],
-               _(u"diamond (Typographical)"):
-                       [(converters.m, 4.5*0.35146e-3), '', _(u"4 1/2 pt in height.")],
-               _(u"pearl (Typographical)"):
-                       [(converters.m, 5*0.35146e-3), '', _(u"5 pt in height.")],
-               _(u"agate | ruby (Typographical)"):
-                       [(converters.m, 5.5*0.35146e-3), '', _(u"Used in typing. A kind of type, larger than pearl and smaller than nonpareil; in England called ruby. 5 1/2 pt in height.")],
-               _(u"nonpareil (Typographical)"):
-                       [(converters.m, 6*0.35146e-3), '', _(u"6 pt in height.")],
-               _(u"minion (Typographical)"):
-                       [(converters.m, 7*0.35146e-3), '', _(u"7 pt in height.")],
-               _(u"brevier (Typographical)"):
-                       [(converters.m, 8*0.35146e-3), '', _(u"8 pt in height.")],
-               _(u"bourgeois (Typographical)"):
-                       [(converters.m, 9*0.35146e-3), '', _(u"9 pt in height.")],
-               _(u"elite | long primer (Typographical)"):
-                       [(converters.m, 10*0.35146e-3), '', _(u"10 pt in height.")],
-               _(u"small pica (Typographical)"):
-                       [(converters.m, 11*0.35146e-3), '', _(u"11 pt in height.")],
-               _(u"pica (Typographical)"):
-                       [(converters.m, 12*0.35146e-3), '', _(u"A size of type next larger than small pica, and smaller than English.12 pt in height")],
-               _(u"english (Typographical)"):
-                       [(converters.m, 14*0.35146e-3), '', _(u"14 pt in height.")],
-               _(u"columbian (Typographical)"):
-                       [(converters.m, 16*0.35146e-3), '', _(u"16 pt in height.")],
-               _(u"great primer (Typographical)"):
-                       [(converters.m, 18*0.35146e-3), '', _(u"18 pt in height.")],
-               _(u"point (pica) (Typographical)"):
-                       [(converters.m, 0.35146e-3), "pt", _(u"Typographical measurement. This system was developed in England and is used in Great-Britain and the US. 1 pica equals 12 pica points.")],
-               _(u"point (didot) (Typographical)"):
-                       [(converters.m, 0.376065e-3), "pt", _(u"Typographical measurement. The didot system originated in France but was used in most of Europe")],
-               _(u"cicero (Typographical)"):
-                       [(converters.m, 12*0.376065e-3), '', _(u"Typographical measurement. 1 cicero equals 12 didot points.")],
-               _(u"point (PostScript) (Typographical)"):
-                       [(converters.m, (25.4/1000)/72), "pt", _(u"Typographical measurement. Created by Adobe. There are exactly 72 PostScript points in 1 inch.")],
-
-               _(u"ell (English)"):
-                       [(converters.m, 45*(25.4/1000)), '', _(u"A measure for cloth; -- now rarely used. It is of different lengths in different countries; the English ell being 45 inches, the Dutch or Flemish ell 27, the Scotch about 37.")],
-               _(u"ell (Dutch | Flemish)"):
-                       [(converters.m, 27*(25.4/1000)), '', _(u"A measure for cloth; -- now rarely used. It is of different lengths in different countries; the English ell being 45 inches, the Dutch or Flemish ell 27, the Scotch about 37.")],
-               _(u"ell (Scotch)"):
-                       [(converters.m, 37*(25.4/1000)), '', _(u"A measure for cloth; -- now rarely used. It is of different lengths in different countries; the English ell being 45 inches, the Dutch or Flemish ell 27, the Scotch about 37.")],
-               _(u"em"):
-                       [(converters.m, 0.0003514598), '', _(u"Used in typography. A quadrat, the face or top of which is a perfect square; also, the size of such a square in any given size of type, used as the unit of measurement for that type: 500 m's of pica would be a piece of matter whose length and breadth in pica m's multiplied together produce that number.")],
-               _(u"en"):
-                       [(converters.m, 0.0001757299), '', _(u"Used in typography. Half an em, that is, half of the unit of space in measuring printed matter.")],
-               _(u"fathom"):
-                       [(converters.m, 6*(12*25.4/1000)), '', _(u"6 feet. Approximately the space to which a man can extend his arms.")],
-               _(u"fathom (Greek)"):
-                       [(converters.m, 4*18.20*(25.4/1000)), '', _(u"4 Greek cubits.")],
-               _(u"fermi"):
-                       [(converters.m, 1e-15), '', _(u"a metric unit of length equal to one quadrillionth of a meter ")],
-               _(u"finger breadth"):
-                       [(converters.m, 0.875*(25.4/1000)), '', _(u"The breadth of a finger, or the fourth part of the hand; a measure of nearly an inch.")],
-               _(u"finger length"):
-                       [(converters.m, 4.5*(25.4/1000)), '', _(u"The length of finger, a measure in domestic use in the United States, of about four and a half inches or one eighth of a yard.")],
-               _(u"foot"):
-                       [(converters.m, 12*(25.4/1000)), "ft", _(u"Equivalent to twelve inches; one third of a yard. This measure is supposed to be taken from the length of a man's foot.")],
-               _(u"foot (Assyrian)"):
-                       [(converters.m, 2.63042), '', ''],
-               _(u"foot (Arabian)"):
-                       [(converters.m, 0.31919), '', ''],
-               _(u"foot (Roman) | pes"):
-                       [(converters.m, 0.2959608), '', ''],
-               _(u"foot (geodetic | survey)"):
-                       [(converters.m, 1200.0/3937), '', _(u"A former U.S. definition of the foot as exactly 1200/3937 meter or about 30.48006096 centimeters. This was the official U.S. definition of the foot from 1866 to 1959; it makes the meter equal exactly 39.37 inches. In 1959 the survey foot was replaced by the international foot, equal to exactly 30.48 centimeters. However, the survey foot remains the basis for precise geodetic surveying in the U.S.")],
-               _(u"furlong"):
-                       [(converters.m, 40*5.0292), '', 'The eighth part of a mile; forty rods; two hundred and twenty yards. From the Old English fuhrlang, meaning "the length of a furrow".'],
-               _(u"ghalva"):
-                       [(converters.m, 230.42925), '', _(u"Arabian measure")],
-               _(u"gradus (Roman)"):
-                       [(converters.m, 2.43*(12*25.4/1000)), '', ''],
-               _(u"hand"):
-                       [(converters.m, 0.1016), '', _(u"A measure equal to a hand's breadth, -- four inches; a palm. Chiefly used in measuring the height of horses.")],
-               _(u"inch"):
-                       [(converters.m, (25.4/1000)), "in", _(u"The twelfth part of a foot, commonly subdivided into halves, quarters, eights, sixteenths, etc., as among mechanics. It was also formerly divided into twelve parts, called lines, and originally into three parts, called barleycorns, its length supposed to have been determined from three grains of barley placed end to end lengthwise.")],
-               _(u"ken"):
-                       [(converters.m, 2.11836), '', _(u"Japanese fathom. The ken is the length of a traditional tatami mat.")],
-               _(u"league (land | statute)"):
-                       [(converters.m, 3*1609.344), '', _(u" Used as a land measure. 3 statute miles.")],
-               _(u"league (nautical)"):
-                       [(converters.m, 3*1852), '', _(u" Used as a marine measure. 3 nautical miles.")],
-               _(u"li"):
-                       [(converters.m, 644.652), '', _(u"A Chinese measure of distance, being a little more than one third of a mile.")],
-               _(u"light second"):
-                       [(converters.m, 299792458), '', _(u"The distance over which light can travel in one second; -- used as a unit in expressing stellar distances.")],
-               _(u"light year"):
-                       [(converters.m, 9.460528405106E+15), '', _(u"The distance over which light can travel in a year's time; -- used as a unit in expressing stellar distances. It is more than 63, 000 times as great as the distance from the earth to the sun.")],
-               _(u"line"):
-                       [(converters.m, (25.4/1000)/12), '', _(u"A measure of length; one twelfth of an inch.")],
-               _(u"link (Gunters | surveyors)"):
-                       [(converters.m, 0.201168), '', _(u"""Part of a surveyors instrument (chain) which consists of links and is used in measuring land. One commonly in use is Gunter's chain, which consists of one hundred links, each link being 7.92" in length.""")],
-               _(u"link (US | engineers)"):
-                       [(converters.m, 12*(25.4/1000)), '', _(u"Used by surveyors. In the U.S., where 100-foot chains are more common, the link is the same as the foot. ")],
-               _(u"marathon"):
-                       [(converters.m, 42194.988), '', _(u"a footrace of 26 miles 385 yards")],
-
-               _(u"megameter"):
-                       [(converters.m, 1.0e6), '', _(u"In the metric system, one million meters, or one thousand kilometers.")],
-               _(u"kilometer"):
-                       [(converters.m, 1.0e3), "km", _(u"Being a thousand meters. It is equal to 3, 280.8 feet, or 62137 of a mile.")],
-               _(u"meter"):
-                       [(converters.m, 1.0), "m", _(u"Equal to 39.37 English inches, the standard of linear measure in the metric system of weights and measures. It was intended to be, and is very nearly, the ten millionth part of the distance from the equator to the north pole, as ascertained by actual measurement of an arc of a meridian.")],
-               _(u"centimeter"):
-                       [(converters.m, 1.0e-2), "cm", _(u"""The hundredth part of a meter; a measure of length equal to rather more than thirty-nine hundredths (0.3937) of an inch.""")],
-               _(u"millimeter"):
-                       [(converters.m, 1.0e-3), "mm", _(u"A lineal measure in the metric system, containing the thousandth part of a meter; equal to .03937 of an inch.")],
-               _(u"micrometer | micron"):
-                       [(converters.m, 1.0e-6), u"\xb5m", _(u"A metric unit of length equal to one millionth of a meter. The thousandth part of one millimeter.")],
-               _(u"nanometer"):
-                       [(converters.m, 1.0e-9), "nm", _(u"A metric unit of length equal to one billionth of a meter.")],
-               _(u"picometer"):
-                       [(converters.m, 1.0e-12), '', _(u"A metric unit of length equal to one trillionth of a meter.")],
-               _(u"femtometer"):
-                       [(converters.m, 1.0e-15), '', _(u"A metric unit of length equal to one quadrillionth of a meter.")],
-               _(u"mil"):
-                       [(converters.m, (25.4/1e6)), "mil", _(u"Equal to one thousandth of an inch; used to specify thickness (e.g., of sheets or wire)")],
-               _(u"mile (Roman)"):
-                       [(converters.m, 1479.804), '', _(u"5000 Roman feet.")],
-               _(u"mile (statute)"):
-                       [(converters.m, 1609.344), "mi", _(u"Mile is from the Latin word for 1000 (mille). A mile conforming to statute, that is, in England and the United States, a mile of 5, 280 feet, as distinguished from any other mile.")],
-               _(u"mile (nautical | geographical)"):
-                       [(converters.m, 1852.0), "nmi", _(u"Geographical, or Nautical mile, one sixtieth of a degree of a great circle of the earth, or about 6080.27 feet.")],
-               _(u"nail (cloth)"):
-                       [(converters.m, 0.05715), '', _(u"Used for measuring cloth. 1/20 ell. The length of the last two joints (including the fingernail) of the middle finger. The nail is equivalent to 1/16 yard, 1/4 span.")],
-               _(u"naval shot"):
-                       [(converters.m, 15*6*(12*25.4/1000)), '', _(u"Equal to 15 fathoms")],
-               _(u"pace"):
-                       [(converters.m, 2.5*(12*25.4/1000)), '', _(u"The length of a step in walking or marching, reckoned from the heel of one foot to the heel of the other. Note: Ordinarily the pace is estimated at two and one half linear feet.")],
-               _(u"pace (Roman) | passus"):
-                       [(converters.m, 5*0.2959608), '', _(u" The Roman pace (passus) was from the heel of one foot to the heel of the same foot when it next touched the ground, five Roman feet.")],
-               _(u"pace (quick-time marching)"):
-                       [(converters.m, 30*(25.4/1000)), '', _(u"The regulation marching pace in the English and United States armies is thirty inches for quick time.")],
-               _(u"pace (double-time marching)"):
-                       [(converters.m, 36*(25.4/1000)), '', _(u"The regulation marching pace in the English and United States armies is thirty-six inches for double time. ")],
-               _(u"palm (Greek)"):
-                       [(converters.m, 7.71313333333333e-02), '', _(u"A lineal measure equal either to the breadth of the hand or to its length from the wrist to the ends of the fingers; a hand; -- used in measuring a horse's height. In Greece, the palm was reckoned at three inches. At the present day, this measure varies in the most arbitrary manner, being different in each country, and occasionally varying in the same. One third of a Greek span, ")],
-               _(u"palm (Roman lesser)"):
-                       [(converters.m, 2.91*(25.4/1000)), '', _(u"A lineal measure equal either to the breadth of the hand or to its length from the wrist to the ends of the fingers; a hand; -- used in measuring a horse's height. One of two Roman measures of the palm, the lesser palm is 2.91 inches. At the present day, this measure varies in the most arbitrary manner, being different in each country, and occasionally varying in the same.")],
-               _(u"palm (Roman greater)"):
-                       [(converters.m, 8.73*(25.4/1000)), '', _(u"A lineal measure equal either to the breadth of the hand or to its length from the wrist to the ends of the fingers; a hand; -- used in measuring a horse's height. One of two Roman measures of the palm, the greater palm is 8.73 inches. At the present day, this measure varies in the most arbitrary manner, being different in each country, and occasionally varying in the same.")],
-               _(u"parasang"):
-                       [(converters.m, 3.5*1609.344), '', _(u"A Persian measure of length, which, according to Herodotus and Xenophon, was thirty stadia, or somewhat more than three and a half miles. The measure varied in different times and places, and, as now used, is estimated at three and a half English miles.")],
-               _(u"parsec"):
-                       [(converters.m, 3.08567758767931e16), '', _(u"A unit of astronomical length based on the distance from  Earth at which stellar parallax is 1 second of arc; equivalent to 3.262 light years")],
-               _(u"rod | pole | perch"):
-                       [(converters.m, 5.0292), '', _(u"Containing sixteen and a half feet; -- called also perch, and pole.")],
-               _(u"ri"):
-                       [(converters.m, 3926.79936), '', _(u"Japanese league.")],
-               _(u"rope"):
-                       [(converters.m, 20*12*(25.4/1000)), '', _(u"20 feet")],
-               _(u"sadzhens | sagene | sazhen"):
-                       [(converters.m, 2.10312), '', _(u"Russian and East European. Used in previous centuries (until WWI or WWII). The distance between a grown man's spread of arms , from the finger- tips of one to hand to the finger-tips of the other hand. Equal to about 7 feet long (2.13 m).")],
-               _(u"shaku"):
-                       [(converters.m, 0.303022), '', _(u" A Japanese foot. Note: shaku also means area and volume.")],
-               _(u"skein"):
-                       [(converters.m, 120*3*12*(25.4/1000)), '', _(u"120 yards. A skein of cotton yarn is formed by eighty turns of the thread round a fifty-four inch reel.")],
-               _(u"soccer field"):
-                       [(converters.m, 100*3*12*(25.4/1000)), '', _(u"100 yards")],
-               _(u"solar diameter"):
-                       [(converters.m, 1391900000), '', _(u"Diameter of our sun.")],
-               _(u"span (Greek)"):
-                       [(converters.m, 0.231394), '', _(u"To measure by the span of the hand with the fingers extended, or with the fingers encompassing the object; as, to span a space or distance; to span a cylinder. One half of a Greek cubit.")],
-               _(u"span (cloth)"):
-                       [(converters.m, 9*(25.4/1000)), '', _(u"9 inches")],
-               _(u"spindle (cotten yarn)"):
-                       [(converters.m, 15120*3*12*(25.4/1000)), '', _(u"A cotten yarn measure containing 15, 120 yards.")],
-               _(u"spindle (linen yarn)"):
-                       [(converters.m, 14400*3*12*(25.4/1000)), '', _(u"A linen yarn measure containing 14, 400 yards.")],
-               _(u"stadia (Greek) | stadion"):
-                       [(converters.m, 185.1152), '', _(u"A Greek measure of length, being the chief one used for itinerary distances, also adopted by the Romans for nautical and astronomical measurements. It was equal to 600 Greek or 625 Roman feet, or 125 Roman paces, or to 606 feet 9 inches English. This was also called the Olympic stadium, as being the exact length of the foot-race course at Olympia.")],
-               _(u"stadium (Persian)"):
-                       [(converters.m, 214.57962), '', ''],
-               _(u"stadium (Roman)"):
-                       [(converters.m, 184.7088), '', _(u"A Greek measure of length, being the chief one used for itinerary distances, also adopted by the Romans for nautical and astronomical measurements. It was equal to 600 Greek or 625 Roman feet, or 125 Roman paces, or to 606 feet 9 inches English. This was also called the Olympic stadium, as being the exact length of the foot-race course at Olympia.")],
-               _(u"sun (Japanese)"):
-                       [(converters.m, 0.0303022), '', _(u"Japanese measurement.")],
-               _(u"toise (French)"):
-                       [(converters.m, 6.395*12*(25.4/1000)), '', _(u"French fathom.")],
-               _(u"vara (Spanish)"):
-                       [(converters.m, 33.385*(25.4/1000)), '', _(u"A Spanish measure of length equal to about one yard. 33.385 inches. ")],
-               _(u"vara (Mexican)"):
-                       [(converters.m, 0.837946), '', _(u"A Mexican measure of length equal to about one yard. 32.99 inches. ")],
-               _(u"verst | werst"):
-                       [(converters.m, 3500*12*(25.4/1000)), '', _(u"A Russian measure of length containing 3, 500 English feet.")],
-               _(u"yard"):
-                       [(converters.m, 3*12*(25.4/1000)), "yd", _(u"Equaling three feet, or thirty-six inches, being the standard of English and American measure.")],
-       },
-
-       _(u"Luminance"): {
-               ".base_unit": "candela per square meter",
-               _(u"magnitudes per square arcsecond"):
-               [(converters.f, ('108000*(10**(-0.4*x))', 'log((x/108000), 10)/-0.4')), "mags/arcsec2", _(u"Used by astronomers to define the darkness of the night sky. Stars are rated by brightness in magnitudes . A lower magnitude number is a brighter star. The star Vega has a magnitude of zero, and a measurement of 0 magnitudes per square arcsecond would be like having every square arcsecond in the sky will with the brightness of the star Vega.")],
-               _(u"candela per square centimeter"):
-               [(converters.m, 1.0e4), u"cd/cm\xb2", ''],
-               _(u"kilocandela per square meter"):
-               [(converters.m, 1.0e3), u"kcd/m\xb2", ''],
-               _(u"stilb"):
-               [(converters.m, 1.0e4), "sb", 'From a Greek word stilbein meaning "to glitter". Equal to one candela per square centimeter or 104 nits.'],
-               _(u"lambert"):
-               [(converters.m, 3183.09886183791), "L", _(u"Named after the German physicist Johann Heinrich Lambert (1728-1777).Equal to the brightness of a perfectly diffusing surface that emits or reflects one lumen per square centimeter")],
-               _(u"candela per square inch"):
-               [(converters.m, 1550.0031000062), u"cd/in\xb2", ''],
-               _(u"candela per square foot"):
-               [(converters.m, 10.7639104167097), u"cd/ft\xb2", ''],
-               _(u"foot lambert"):
-               [(converters.m, 3.42625909963539), "fL", ''],
-               _(u"millilambert"):
-               [(converters.m, 3.18309886183791), "mL", ''],
-               _(u"candela per square meter"):
-               [(converters.m, 1.0), u"cd/m\xb2", ''],
-               _(u"lumen per steradian square meter"):
-               [(converters.m, 1.0), '', ''],
-               _(u"nit"):
-               [(converters.m, 1.0), '', _(u"Named from the Latin niteo, to shine.")],
-               _(u"apostilb"):
-               [(converters.m, 3.18309886183791/10), "asb", 'Named from the Greek stilbein, to "glitter" or "shine, " with the prefix apo-, "away from." '],
-       },
-
-       _(u"Illumination"): {
-               ".base_unit": "lux",
-               _(u"phot"):
-               [(converters.m, 1.0e4), "ph", _(u"a unit of illumination equal to 1 lumen per square centimeter; 10, 000 phots equal 1 lux")],
-               _(u"lumen per square centimeter"):
-               [(converters.m, 1.0e4), u"lm/cm\xb2", ''],
-               _(u"foot candle"):
-               [(converters.m, 10.7639104167097), "fc", ''],
-               _(u"lumen per square foot"):
-               [(converters.m, 10.7639104167097), u"lm/ft\xb2", ''],
-               _(u"lux"):
-               [(converters.m, 1.0), "lx", _(u"Equal to the illumination produced by luminous flux of one lumen falling perpendicularly on a surface one meter square. Also called meter-candle.")],
-               _(u"metre-candle"):
-               [(converters.m, 1.0), "m-cd", _(u"Equal to the illumination produced by luminous flux of one lumen falling perpendicularly on a surface one meter square. Also called lux.")],
-               _(u"lumen per square meter"):
-               [(converters.m, 1.0), u"lm/m\xb2", ''],
-               _(u"candela steradian per square meter"):
-               [(converters.m, 1.0), '', ''],
-       },
-
-       _(u"Luminous Intensity (point sources)"): {
-               ".base_unit": "candela",
-               _(u"candela"):
-               [(converters.m, 1.0), "cd", _(u"The basic unit of luminous intensity adopted under the System International d'Unites; equal to 1/60 of the luminous intensity per square centimeter of a blackbody radiating at the temperature of 2, 046 degrees Kelvin syn: candle, cd, standard candle.")],
-               _(u"lumen per steradian"):
-               [(converters.m, 1.0), "lm/sr", ''],
-               _(u"hefner candle"):
-               [(converters.m, 0.92), "HC", _(u"Named after F. von Hefner-Altenack (1845-1904)")],
-       },
-
-       _(u"Luminous Flux"): {
-               ".base_unit": "lumen",
-               _(u"lumen"):
-               [(converters.m, 1.0), "lm", _(u"Equal to the luminous flux emitted in a unit solid angle by a point source of one candle intensity")],
-               _(u"candela steradian"):
-               [(converters.m, 1.0), u"cd\xb7sr", ''],
-       },
-
-       _(u"Magnetomotive force"): {
-               ".base_unit": "ampere",
-               _(u"ampere"):
-               [(converters.m, 1.0), "A", ''],
-               _(u"ampere-turn"):
-               [(converters.m, 1.0), "At", _(u"A unit of magnetomotive force equal to the magnetomotive force produced by the passage of 1 ampere through 1 complete turn of a coil.")],
-               _(u"gilbert"):
-               [(converters.m, 0.795775), "Gb", _(u"Named after the English scientist William Gilbert (1544-1603)")],
-               _(u"kiloampere"):
-               [(converters.m, 1e3), "kA", ''],
-               _(u"oersted-centimeter"):
-               [(converters.m, 0.795775), '', _(u"The same value as the gilbert.")],
-       },
-
-       _(u"Magnetic Flux"): {
-               ".base_unit": "weber",
-               _(u"weber"):
-               [(converters.m, 1.0), "Wb", _(u"From the name of Professor Weber, a German electrician. One volt second.")],
-               _(u"milliweber"):
-               [(converters.m, 1.0e-3), "mWb", ''],
-               _(u"microweber"):
-               [(converters.m, 1.0e-6), u"\xb5Wb", ''],
-               _(u"unit pole (electro magnetic unit)"):
-               [(converters.m, 4e-8*math.pi), '', ''],
-               _(u"maxwell"):
-               [(converters.m, 1.0e-8), "Mx", _(u"Named after the Scottish physicist James Clerk Maxwell (1831-1879). A cgs unit of magnetic flux equal to the flux perpendicular to an area of 1 square centimeter in a magnetic field of 1 gauss.")],
-               _(u"line of force"):
-               [(converters.m, 1.0e-8), '', _(u"Same as Maxwell")],
-       },
-
-       _(u"Magnetic Field strength"): {
-               ".base_unit": "ampere per meter",
-               _(u"oersted"):
-               [(converters.m, 1.0e3/(4*math.pi)), "Oe", _(u"Named after the Danish physicist and chemist Hans Christian Oersted (1777-1851). The C.G.S. unit of magnetic reluctance or resistance, equal to the reluctance of a centimeter cube of air (or vacuum) between parallel faces. Also, a reluctance in which unit magnetomotive force sets up unit flux.")],
-               _(u"ampere per meter"):
-               [(converters.m, 1.0), "A/m", ''],
-               _(u"ampere-turn per meter"):
-               [(converters.m, 1.0), "A/m", ''],
-               _(u"kiloampere per meter"):
-               [(converters.m, 1.0e3), "kA/m", ''],
-               _(u"ampere-turn per inch"):
-               [(converters.m, 39.3700787401575), "At/in", ''],
-               _(u"newton per weber"):
-               [(converters.m, 1.0), "N/Wb", _(u"Same as ampere per meter")],
-       },
-
-       _(u"Magnetic Flux Density"): {
-               ".base_unit": "tesla",
-               _(u"tesla"):
-               [(converters.m, 1.0), "T", _(u"Named after the Croatian born inventer Nikola Tesla (1856-1943). A unit of magnetic flux density equal to one weber per square meter.")],
-               _(u"millitesla"):
-               [(converters.m, 1.0e-3), "mT", ''],
-               _(u"microtesla"):
-               [(converters.m, 1.0e-6), u"\xb5T", ''],
-               _(u"nanotesla"):
-               [(converters.m, 1.0e-9), "nT", ''],
-               _(u"weber per square meter"):
-               [(converters.m, 1.0), u"Wb/m\xb2", ''],
-               _(u"kilogauss"):
-               [(converters.m, 1.0e-1), "kG", ''],
-               _(u"gauss"):
-               [(converters.m, 1.0e-4), "G", _(u"Named after German mathematician and astronomer Karl Friedrich Gauss (1777-1855). The C.G.S. unit of density of magnetic field, equal to a field of one line of force per square centimeter, being thus adopted as an international unit at Paris in 1900; sometimes used as a unit of intensity of magnetic field. It was previously suggested as a unit of magnetomotive force.")],
-               _(u"maxwell per square centimeter"):
-               [(converters.m, 1.0e-4), u"Mx/cm\xb2", ''],
-               _(u"maxwell per square inch"):
-               [(converters.m, 1.5500031000062E-05), u"Mx/in\xb2", ''],
-               _(u"line per square inch"):
-               [(converters.m, 1.5500031000062E-05), '', _(u"Same as Maxwell per square inch.")],
-               _(u"gamma"):
-               [(converters.m, 1.0e-9), '', _(u"one nanotesla.")],
-       },
-
-       _(u"Mass"): {
-               ".base_unit": "kilogram",
-               _(u"talanton"):
-               [(converters.m, 149.9985), '', _(u"Greek measure.")],
-               _(u"oka (Egyptian)"):
-               [(converters.m, 1.248), '', ''],
-               _(u"oka (Greek)"):
-               [(converters.m, 1.2799), '', ''],
-               _(u"okia"):
-               [(converters.m, 0.03744027), '', _(u"Egyptian measure.")],
-               _(u"kat"):
-               [(converters.m, 0.009331), '', _(u"Egyptian measure.")],
-               _(u"kerat"):
-               [(converters.m, 0.00019504), '', _(u"Egyptian measure.")],
-               _(u"pala"):
-               [(converters.m, 0.047173), '', _(u"Indian measure.")],
-               _(u"kona"):
-               [(converters.m, 0.00699828), '', _(u"Indian measure.")],
-               _(u"mast"):
-               [(converters.m, .9331), '', _(u"British")],
-               _(u"kilogram"):
-               [(converters.m, 1.0), "kg", _(u"A measure of weight, being a thousand grams, equal to 2.2046 pounds avoirdupois (15, 432.34 grains). It is equal to the weight of a cubic decimeter of distilled water at the temperature of maximum density, or 39deg Fahrenheit.")],
-               _(u"megagram"):
-               [(converters.m, 1.0e3), "Mg", ''],
-               _(u"gram"):
-               [(converters.m, 1.0e-3), "g", _(u"The unit of weight in the metric system. It was intended to be exactly, and is very nearly, equivalent to the weight in a vacuum of one cubic centimeter of pure water at its maximum density. It is equal to 15.432 grains.")],
-               _(u"milligram"):
-               [(converters.m, 1.0e-6), "mg", _(u"A measure of weight, in the metric system, being the thousandth part of a gram, equal to the weight of a cubic millimeter of water, or .01543 of a grain avoirdupois.")],
-               _(u"microgram"):
-               [(converters.m, 1.0e-9), u"\xb5g", _(u"A measure of weight, in the metric system, being the millionth part of a gram.")],
-               _(u"ton (UK | long | gross | deadweight)"):
-               [(converters.m, 2240 * 0.45359237), '', _(u"A British unit of weight equivalent to 2240 pounds")],
-               _(u"ton (US | short)"):
-               [(converters.m, 2000 * 0.45359237), "tn", _(u"A US unit of weight equivalent to 2000 pounds")],
-               _(u"tonne | metric ton"):
-               [(converters.m, 1.0e3), "t", _(u"A metric ton, One Megagram. 1000 kg")],
-               _(u"pound (avoirdupois)"):
-               [(converters.m, 0.45359237), "lb", _(u"The pound in general use in the United States and in England is the pound avoirdupois, which is divided into sixteen ounces, and contains 7, 000 grains. The pound troy is divided into twelve ounces, and contains 5, 760 grains. 144 pounds avoirdupois are equal to 175 pounds troy weight")],
-               _(u"pound (troy)"):
-               [(converters.m, 0.3732417216), '', ''],
-               _(u"hundredweight (short | net | US)"):
-               [(converters.m, 100*0.45359237), "cwt", _(u"A denomination of weight of 100 pounds. In most of the United States, both in practice and by law, it is 100 pounds avoirdupois.")],
-               _(u"hundredweight (long | English)"):
-               [(converters.m, 112*0.45359237), "cwt", _(u"A denomination of weight of 112 pounds")],
-               _(u"slug"):
-               [(converters.m, 14.5939029372064), '', _(u"One slug is the mass accelerated at 1 foot per second per second by a force of 1 pound.")],
-               _(u"ounce (troy)"):
-               [(converters.m, 0.0311034768), "ozt", _(u"A unit of apothecary weight equal to 480 grains.")],
-               _(u"ounce (avoirdupois)"):
-               [(converters.m, 0.45359237/16), "oz", _(u"A weight, the sixteenth part of a pound avoirdupois")],
-               _(u"dram (avoirdupois)"):
-               [(converters.m, (0.45359237/16)/16), '', _(u"A weight; in Avoirdupois weight, one sixteenth part of an ounce.")],
-       _(u"dram (troy | apothecary)"):
-               [(converters.m, (0.0311034768)/8), '', _(u"""A weight; in Apothecaries' weight, one eighth part of an ounce, or sixty grains.""")],
-               _(u"scruple (troy)"):
-                       [(converters.m, 20*(0.45359237/5760)), '', _(u"A weight of twenty grains; the third part of a troy dram.")],
-               _(u"carat"):
-                       [(converters.m, 0.0002), '', _(u"The weight by which precious stones and pearls are weighed.")],
-               _(u"grain"):
-                       [(converters.m, 0.00006479891), "gr", _(u"The unit of the English system of weights; -- so called because considered equal to the average of grains taken from the middle of the ears of wheat. 7, 000 grains constitute the pound avoirdupois and 5, 760 grains constitute the pound troy.")],
-               _(u"amu (atomic mass unit) | dalton"):
-                       [(converters.m, 1.66044E-27), "amu", _(u"Unit of mass for expressing masses of atoms or molecules.")],
-               _(u"catty | caddy | chin"):
-                       [(converters.m, (4.0/3)*0.45359237), '', _(u"An Chinese or East Indian Weight of 1 1/3 pounds.")],
-               _(u"cental"):
-                       [(converters.m, 100*0.45359237), '', _(u"British for 100 pounds. Also called hundredweight in the US.")],
-               _(u"cotton bale (US)"):
-                       [(converters.m, 500*0.45359237), '', _(u"US measurement. 500 pounds")],
-               _(u"cotton bale (Egypt)"):
-                       [(converters.m, 750*0.45359237), '', _(u"Egyptian measurement. 750 pounds")],
-               _(u"crith"):
-                       [(converters.m, 0.0000906), '', _(u"From the Greek word for barleycorn. The weight of a liter of hydrogen at 0.01\xb0 centigrade and with a and pressure of 1 atmosphere.")],
-               _(u"denarius"):
-                       [(converters.m, 60*(0.45359237/5760)), '', _(u"Roman weight measuring 60 troy grains")],
-               _(u"dinar"):
-                       [(converters.m, 4.2e-3), '', _(u"Arabian weight measuring 4.2 gram")],
-               _(u"doppelzentner"):
-                       [(converters.m, 100.0), '', _(u"Metric hundredweight = 100 kg")],
-               _(u"drachma (Greek)"):
-                       [(converters.m, 0.0042923), '', _(u"The weight of an old Greek drachma coin")],
-               _(u"drachma (Dutch)"):
-                       [(converters.m, 3.906e-3), '', _(u"The weight of an old Dutch drachma coin")],
-               _(u"earth mass"):
-                       [(converters.m, 5.983E+24), '', _(u"Mass of the Earth.")],
-               _(u"electron rest mass"):
-                       [(converters.m, 9.109558E-31), '', _(u"The mass of an electron as measured when the it is at rest relative to an observer, an inherent property of the body.")],
-               _(u"funt"):
-                       [(converters.m, 0.408233133), '', _(u"Russian, 0.9 pounds")],
-               _(u"obolos (Ancient Greece)"):
-                       [(converters.m, 0.0042923/6), '', _(u"Ancient Greek weight of an obol coin, 1/6 drachma")],
-               _(u"obolos (Modern Greece)"):
-                       [(converters.m, 1.0e-4), '', _(u"Modern Greek name for decigram.")],
-               _(u"hyl"):
-                       [(converters.m, 0.00980665), '', _(u"From an ancient Greek word for matter. One hyl is the mass that is accelerated at one meter per second per second by one kilogram of force. 0.00980665 kg.")],
-               _(u"pennyweight (troy)"):
-                       [(converters.m, 24*0.00006479891), '', _(u"A troy weight containing twenty-four grains, or the twentieth part of a troy ounce; as, a pennyweight of gold or of arsenic. It was anciently the weight of a silver penny.")],
-               _(u"bekah (Biblical)"):
-                       [(converters.m, 5*24*0.00006479891), '', _(u"1/2 shekel, 5 pennyweight.")],
-               _(u"shekel (Israeli)"):
-                       [(converters.m, 10*24*0.00006479891), '', _(u"The sixtieth part of a mina. Ten pennyweight. An ancient weight and coin used by the Jews and by other nations of the same stock.")],
-               _(u"mina (Greek) | minah (Biblical)"):
-                       [(converters.m, 60*10*24*0.00006479891), '', _(u"The weight of the ancient Greek mina coin. 60 shekels")],
-               _(u"talent (Roman)"):
-                       [(converters.m, 125*0.3265865064), '', _(u"125 Roman libra.")],
-               _(u"talent (silver)"):
-                       [(converters.m, 3000*10*24*0.00006479891), '', _(u"3, 000 shekels or 125 lbs.")],
-               _(u"talent (gold)"):
-                       [(converters.m, 6000*10*24*0.00006479891), '', _(u"2 silver talents, 250 lbs.")],
-               _(u"talent (Hebrew)"):
-                       [(converters.m, 26.332), '', ''],
-               _(u"kin"):
-                       [(converters.m, 0.60010270551), '', _(u"Japanese kin,  1.323 pound.")],
-               _(u"kwan"):
-                       [(converters.m, 3.7512088999), '', _(u"Japanese kwan. 8.27 pound")],
-               _(u"liang | tael"):
-                       [(converters.m, ((4.0/3)*0.45359237)/16), '', _(u"Chinese. 1/16 catty")],
-               _(u"libra | librae | as | pondus"):
-                       [(converters.m, 0.3265865064), '', _(u"Roman originator of the English pound (lb). 12 uncia")],
-               _(u"libra (Mexican)"):
-                       [(converters.m, 0.46039625555), '', ''],
-               _(u"libra (Spanish)"):
-                       [(converters.m, 0.45994266318), '', ''],
-               _(u"livre (French)"):
-                       [(converters.m, 0.49), '', ''],
-               _(u"quarter (long)"):
-                       [(converters.m, (112*0.45359237)/4), '', _(u"The fourth part of a long hundredweight. 28 pounds")],
-               _(u"quarter (short)"):
-                       [(converters.m, (100*0.45359237)/4), '', _(u"The fourth part of a short hundredweight. 25 pounds")],
-               _(u"mite (English)"):
-                       [(converters.m, 0.0000032399455), '', _(u"A small weight; one twentieth of a grain.")],
-               _(u"neutron rest mass"):
-                       [(converters.m, 1.67492E-27), '', _(u"The mass of a neutron as measured when the it is at rest relative to an observer, an inherent property of the body.")],
-               _(u"proton rest mass"):
-                       [(converters.m, 1.672614E-27), '', _(u"The mass of a proton as measured when the it is at rest relative to an observer, an inherent property of the body.")],
-               _(u"pfund (German)"):
-                       [(converters.m, 0.5), '', _(u"German pound. 500 grams. 16 unze.")],
-               _(u"unze (German)"):
-                       [(converters.m, 0.5/16), '', _(u"German ounce. 1/16 pfund.")],
-               _(u"lot (German)"):
-                       [(converters.m, 0.5/32), '', _(u"One half unze.")],
-               _(u"picul | tan | pecul | pecal (Chinese | Summatra))"):
-                       [(converters.m, 133.5*0.45359237), '', _(u"100 catty. 133 1/2 pounds")],
-               _(u"picul (Japan)"):
-                       [(converters.m, (400.0/3)*0.45359237), '', _(u"133 1/3 pounds")],
-               _(u"picul (Borneo)"):
-                       [(converters.m, (1085.0/8)*0.45359237), '', _(u"135 5/8 pounds")],
-               _(u"pood (Russian)"):
-                       [(converters.m, 16.3792204807), '', _(u"A Russian weight, equal to forty Russian pounds or about thirty-six English pounds avoirdupois.")],
-               _(u"quintal"):
-                       [(converters.m, 100.0), '', _(u"A metric measure of weight, being 100, 000 grams, or 100 kilograms")],
-               _(u"quintal (short UK)"):
-                       [(converters.m, 100*0.45359237), '', _(u"100 pounds")],
-               _(u"quintal (long UK)"):
-                       [(converters.m, 112*0.45359237), '', _(u"112 pounds")],
-               _(u"quintal (Spanish)"):
-                       [(converters.m, 45.994266318), '', _(u"Spanish hundredweight")],
-               _(u"scrupulum (Roman)"):
-                       [(converters.m, 0.0011359248923), '', ''],
-               _(u"stone (legal)"):
-                       [(converters.m, 14*0.45359237), '', _(u"14 pounds")],
-               _(u"stone (butchers)"):
-                       [(converters.m, 8*0.45359237), '', _(u"Meat or fish. 8 pounds")],
-               _(u"stone (cheese)"):
-                       [(converters.m, 16*0.45359237), '', _(u"16 pounds.")],
-               _(u"stone (hemp)"):
-                       [(converters.m, 32*0.45359237), '', _(u"32 pounds")],
-               _(u"stone (glass)"):
-                       [(converters.m, 5*0.45359237), '', _(u"5 pounds")],
-               _(u"uncia"):
-                       [(converters.m, 0.3265865064/12), '', _('Ancient Roman. A twelfth part, as of the Roman "as" or "libra"; an ounce. 420 grains')],
-       },
-
-       _(u"Musical notes"): {
-               ".base_unit": "breve",
-               _(u"whole note | semibreve"):
-               [(converters.m, 0.5), '', _(u"A note of half the time or duration of the breve; -- now usually called a whole note.")],
-               _(u"breve"):
-               [(converters.m, 1.0), '', _(u"A note or character of time, equivalent to two semibreves or four minims. When dotted, it is equal to three semibreves.")],
-               _(u"minim"):
-               [(converters.m, 0.25), '', _(u"A time note, a half note, equal to half a semibreve, or two quarter notes or crotchets.")],
-               _(u"crotchet"):
-               [(converters.m, 0.125), '', _(u"A time note, with a stem, having one fourth the value of a semibreve, one half that of a minim, and twice that of a quaver; a quarter note.")],
-               _(u"quaver"):
-               [(converters.m, 0.0625), '', _(u"An eighth note.")],
-       },
-
-       _(u"Power"): {
-               ".base_unit": "watt",
-               _(u"megawatt"):
-               [(converters.m, 1.0e6), "MW", ''],
-               _(u"kilowatt"):
-               [(converters.m, 1.0e3), "kW", ''],
-               _(u"watt"):
-               [(converters.m, 1.0), "W", _(u"Named after the Scottish engineer and inventor James Watt (1736-1819). A unit of power or activity equal to 10^7 C.G.S. units of power, or to work done at the rate of one joule a second.")],
-               _(u"milliwatt"):
-               [(converters.m, 1.0e-3), "mW", ''],
-               _(u"microwatt"):
-               [(converters.m, 1.0e-6), "uW", ''],
-
-               _(u"horsepower (boiler)"):
-               [(converters.m, 9.81e3), '', _(u"A unit of power representing the power exerted by a horse in pulling.")],
-               _(u"horsepower"):
-               [(converters.m, 746.0), "hp", ''],
-               _(u"ton of refrigeration"):
-               [(converters.m, 10.0/3*1055.05585262), "TR", ''],
-               _(u"btu per second"):
-               [(converters.m, 1055.05585262), "Btu/s", ''],
-               _(u"calorie per second"):
-               [(converters.m, 4.1868), "cal/s", ''],
-               _(u"kilcalorie per hour"):
-               [(converters.m, 4186.8/3600), "kcal/h", _(u"Useful for calculating heating facilities and kitchens.")],
-               _(u"frig per hour"):
-               [(converters.m, 4186.8/3600), "frig/h", _(u"The same as kcal/h, but used for air conditioning and refrigerating.")],
-               _(u"foot pound force per second"):
-               [(converters.m, 1.356), "lbf/s", ''],
-               _(u"joule per second"):
-               [(converters.m, 1.0), "J/s", ''],
-               _(u"newton meter per second"):
-               [(converters.m, 1.0), u"N\xb7m/s", ''],
-               _(u"btu per hour"):
-               [(converters.m, 0.293071070172222), "Btu/h", ''],
-               _(u"foot pound force per minute"):
-               [(converters.m, 0.0226), u"ft\xb7lbf/min", ''],
-               _(u"erg per second"):
-               [(converters.m, 1.0e-7), "erg/s", ''],
-               _(u"dyne centimeter per second"):
-               [(converters.m, 1.0e-7), '', ''],
-               _(u"lusec"):
-               [(converters.m, 0.000133322368421), '', _(u"Used to measure the leakage of vacuum pumps. A flow of one liter per second at a pressure of one micrometer of mercury.")],
-       },
-
-       _(u"Pressure and Stress"): {
-               ".base_unit": "pascal",
-               _(u"pascal"):
-               [(converters.m, 1.0), "Pa", _(u"Named after the French philosopher and mathematician Blaise Pascal (1623 - 1662). Equal to one newton per square meter.")],
-               _(u"hectopascal"):
-               [(converters.m, 100), "hPa", ''],
-               _(u"kilopascal"):
-               [(converters.m, 1.0e3), "kPa", ''],
-               _(u"megapascal"):
-               [(converters.m, 1.0e6), "MPa", ''],
-               _(u"atmosphere (absolute, standard)"):
-               [(converters.m, 101325), "atm", _(u"The average pressure of the Earth's atmosphere at sea level.")],
-               _(u"atmosphere (technical)"):
-               [(converters.m, 98066.5), "atm", _(u"A metric unit equal to one kilogram of force per square centimeter.")],
-               _(u"bar"):
-               [(converters.m, 1.0e5), "bar", _(u"From the Greek word baros.")],
-               _(u"pound force per square inch"):
-               [(converters.m, 6894.75729316836), "psi", ''],
-               _(u"ounces per square inch"):
-               [(converters.m, 6894.75729316836/16), u"oz/in\xb2", ''],
-               _(u"feet of water (60F, 15.5C)"):
-               [(converters.m, 12*133.322*1.866), "ftH20", ''],
-               _(u"inches of water (60F, 15.5C)"):
-               [(converters.m, 133.322*1.866), "inH20", ''],
-               _(u"meter of water (60F, 15.5C)"):
-               [(converters.m, 133.322*1.866/.0254), "mH20", ''],
-               _(u"centimeter of water (60F, 15.5C)"):
-               [(converters.m, 133.322*1.866/2.54), "cmH20", ''],
-               _(u"millimeter of water (60F, 15.5C)"):
-               [(converters.m, 133.322*1.866/25.4), "mmH20", ''],
-
-               _(u"feet of water (39.2F, 4C)"):
-               [(converters.m, 2988.9921933), "ftH20", ''],
-               _(u"inches of water (39.2F, 4C)"):
-               [(converters.m, 249.0826828), "inH20", ''],
-               _(u"meter of water (39.2F, 4C)"):
-               [(converters.m, 9806.4048337), "mH20", ''],
-               _(u"centimeter of water (39.2F, 4C)"):
-               [(converters.m, 98.0640483), "cmH20", ''],
-               _(u"millimeter of water (39.2F, 4C)"):
-               [(converters.m, 9.80640483), "mmH20", ''],
-
-               _(u"inches of mercury (60F, 15.5C)"):
-               [(converters.m, 3337.0), "inHg", ''],
-               _(u"millimeter of mercury (0C)"):
-               [(converters.m, 133.322368421), "mmHg", ''],
-               _(u"inches of mercury (0C)"):
-               [(converters.m, 133.322368421*25.4), "inHg", ''],
-               _(u"micrometer of mercury (0C)"):
-               [(converters.m, 0.133322368421), u"\xb5mHg", ''],
-               _(u"centimeter of mercury (0C)"):
-               [(converters.m, 1333.22368421), "cmHg", ''],
-               _(u"foot of mercury (0C)"):
-               [(converters.m, 1333.22368421*25.4), "ftHg", ''],
-               _(u"torricelli"):
-                       [(converters.m, 133.322368421), "torr", _(u"Named after Italian physicist and mathematician Evangelista Torricelli, (1608-1647). A unit of pressure equal to 0.001316 atmosphere.")],
-               _(u"micron"):
-                       [(converters.m, 133.322368421/1000), u"\xb5", _(u"Used in vacuum technology. Equal to 1 millitorr.")],
-               _(u"millibar"):
-                       [(converters.m, 1.0e2), "mbar", ''],
-               _(u"pound force per square foot"):
-                       [(converters.m, 47.8802589803358), u"lbf/ft\xb2", ''],
-               _(u"tons (UK) per square foot"):
-                       [(converters.m, 47.8802589803358*2240), u"tons(UK)/ft\xb2", ''],
-               _(u"tons (US) per square foot"):
-                       [(converters.m, 47.8802589803358*2000), u"tons(US)/ft\xb2", ''],
-               _(u"kilogram force per square meter"):
-                       [(converters.m, 9.80665), u"kgf/m\xb2", ''],
-               _(u"kilogram force per square centimeter"):
-                       [(converters.m, 9.80665e4), u"kgf/cm\xb2", _(u"Used for ground pressure and steel stress.")],
-               _(u"newton per square meter"):
-                       [(converters.m, 1.0), u"N/m\xb2", ''],
-               _(u"newton per square centimeter"):
-                       [(converters.m, 1.0e4), u"N/cm\xb2", ''],
-               _(u"newton per square millimeter"):
-                       [(converters.m, 1.0e6), u"N/mm\xb2", _(u"Used for concrete stress.")],
-               _(u"kiloNewton per square meter"):
-                       [(converters.m, 1.0e3), u"kN/m\xb2", _(u"Used for ground pressure.")],
-               _(u"kiloNewton per square centimeter"):
-                       [(converters.m, 1.0e7), u"kN/cm\xb2", _(u"Used for loads and concrete stress.")],
-               _(u"microbar"):
-                       [(converters.m, 1.0e-1), u"\xb5bar", ''],
-               _(u"dyne per square centimeter"):
-                       [(converters.m, 1.0e-1), u"dyn/cm\xb2", ''],
-               _(u"barie | barye"):
-                       [(converters.m, 0.1), '', ''],
-               _(u"pieze"):
-                       [(converters.m, 1.0e3), '', _(u"From the Greek word piezein (to press). The pieze is a pressure of one sthene per square meter. 1000 newtons per square meter.")],
-       },
-
-       _(u"Prefixes and Suffixes"): {
-               ".base_unit": "one | mono",
-               _(u"centillion (US)"):
-               [(converters.m, 1.0e303), '', _(u"10^303. Note: British word centillion means 10^600 (too big for this program to represent as floating point).")],
-               _(u"novemtrigintillion (US) | vigintillion (UK)"):
-               [(converters.m, 1.0e120), '', _(u"10^120. ")],
-               _(u"octotrigintillion (US)"):
-               [(converters.m, 1.0e117), '', _(u"10^117. ")],
-               _(u"septentrigintillion (US) | novemdecillion (UK)"):
-               [(converters.m, 1.0e114), '', _(u"10^114. ")],
-               _(u"sextrigintillion (US)"):
-               [(converters.m, 1.0e111), '', _(u"10^111. ")],
-               _(u"quintrigintillion (US) | octodecillion (UK)"):
-               [(converters.m, 1.0e108), '', _(u"10^108. ")],
-               _(u"quattuortrigintillion (US)"):
-               [(converters.m, 1.0e105), '', _(u"10^105. ")],
-               _(u"tretrigintillion (US) | septendecillion (UK)"):
-               [(converters.m, 1.0e102), '', _(u"10^102. ")],
-               _(u"googol"):
-               [(converters.m, 1.0e100), '', _(u"10^100 Ten dotrigintillion (US). Note: a googolplex is 10^10^10^2.")],
-               _(u"dotrigintillion (US)"):
-               [(converters.m, 1.0e99), '', _(u"10^99. ")],
-               _(u"untrigintillion (US) | sexdecillion (UK)"):
-               [(converters.m, 1.0e96), '', _(u"10^96. ")],
-               _(u"trigintillion (US)"):
-               [(converters.m, 1.0e93), '', _(u"10^93. ")],
-               _(u"novemvigintillion (US) | quindecillion (UK)"):
-               [(converters.m, 1.0e90), '', _(u"10^90. ")],
-               _(u"octovigintillion (US)"):
-               [(converters.m, 1.0e87), '', _(u"10^87. ")],
-               _(u"septenvigintillion (US) | quattuordecillion (UK)"):
-               [(converters.m, 1.0e84), '', _(u"10^84. ")],
-               _(u"sexvigintillion (US)"):
-               [(converters.m, 1.0e81), '', _(u"10^81. ")],
-               _(u"quinvigintillion (US) | tredecillion (UK)"):
-               [(converters.m, 1.0e78), '', _(u"10^78. ")],
-               _(u"quattuorvigintillion (US)"):
-               [(converters.m, 1.0e75), '', _(u"10^75. ")],
-               _(u"trevigintillion (US) | duodecillion (UK)"):
-               [(converters.m, 1.0e72), '', _(u"10^72. ")],
-               _(u"dovigintillion (US)"):
-               [(converters.m, 1.0e69), '', _(u"10^69. ")],
-               _(u"unvigintillion (US) | undecillion (UK"):
-               [(converters.m, 1.0e66), '', _(u"10^66. ")],
-               _(u"vigintillion (US)"):
-               [(converters.m, 1.0e63), '', _(u"10^63. ")],
-               _(u"novemdecillion (US) | decillion (UK)"):
-               [(converters.m, 1.0e60), '', _(u"10^60. ")],
-               _(u"octodecillion (US)"):
-               [(converters.m, 1.0e57), '', _(u"10^57. ")],
-               _(u"septendecillion (US) | nonillion (UK)"):
-               [(converters.m, 1.0e54), '', _(u"10^54. ")],
-               _(u"sexdecillion (US)"):
-               [(converters.m, 1.0e51), '', _(u"10^51. ")],
-               _(u"quindecillion (US) | octillion (UK)"):
-                       [(converters.m, 1.0e48), '', _(u"10^48. ")],
-               _(u"quattuordecillion (US)"):
-                       [(converters.m, 1.0e45), '', _(u"10^45. ")],
-               _(u"tredecillion (US) | septillion (UK)"):
-                       [(converters.m, 1.0e42), '', _(u"10^42. ")],
-               _(u"duodecillion (US) | chici"):
-                       [(converters.m, 1.0e39), "Ch", _(u"10^39. chici coined by Morgan Burke after Marx brother Chico Marx.")],
-               _(u"undecillion (US) | sextillion (UK) | gummi"):
-                       [(converters.m, 1.0e36), "Gm", _(u"10^36. gummi coined by Morgan Burke after Marx brother Gummo Marx.")],
-               _(u"una | decillion (US) | zeppi"):
-                       [(converters.m, 1.0e33), "Zp", _(u"10^33. zeppi coined by Morgan Burke after Marx brother Zeppo Marx.")],
-               _(u"dea | nonillion (US) | quintillion (UK) | grouchi"):
-                       [(converters.m, 1.0e30), "Gc", _(u"10^30. grouchi coined by Morgan Burke after Marx brother Groucho Marx.")],
-               _(u"nea | octillion (US) | quadrilliard (UK) | harpi"):
-                       [(converters.m, 1.0e27), "Hr", _(u"10^27. harpi coined by Morgan Burke after Marx brother Harpo Marx.")],
-               _(u"yotta | septillion (US) | quadrillion (UK)"):
-                       [(converters.m, 1.0e24), "Y", '10^24. Origin Latin penultimate letter y "iota".'],
-               _(u"zetta | sextillion (US) | trilliard (UK)"):
-                       [(converters.m, 1.0e21), "Z", '10^21. Origin Latin ultimate letter z "zeta".'],
-               _(u"exa | quintillion (US) | trillion (UK)"):
-                       [(converters.m, 1.0e18), "E", '10^18. Origin Greek for outside "exo" / Greek six hexa" as in 1000^6.'],
-               _(u"peta | quadrillion (US) | billiard (UK)"):
-                       [(converters.m, 1.0e15), "P", '10^15. Origin Greek for spread "petalos" / Greek five "penta" as in 1000^5. Note: British use the words "1000 billion".'],
-               _(u"tera | trillion (US) | billion (UK)"):
-                       [(converters.m, 1.0e12), "T", '10^12. Origin Greek for monster "teras" / Greek four "tetra" as in 1000^4. Note: British use the word billion.'],
-               _(u"giga"):
-                       [(converters.m, 1.0e9), "G", '10^9. Origin Greek for giant "gigas".'],
-               _(u"billion (US) | milliard (UK)"):
-                       [(converters.m, 1.0e9), '', '10^9.'],
-               _(u"mega | million"):
-                       [(converters.m, 1.0e6), "M", '10^6. One million times. Origin Greek for large, great "megas".'],
-               _(u"hectokilo"):
-                       [(converters.m, 1.0e5), "hk", _(u"10^5. 100 thousand times")],
-               _(u"myra | myria"):
-                       [(converters.m, 1.0e4), "ma", _(u"Ten thousand times, 10^4")],
-               _(u"kilo | thousand"):
-                       [(converters.m, 1.0e3), "k", 'One thousand times, 10^3.Origin Greek for thousand "chylioi".'],
-               _(u"gross"):
-                       [(converters.m, 144.0), '', _(u"Twelve dozen.")],
-               _(u"hecto | hundred"):
-                       [(converters.m, 1.0e2), '', 'One hundred times, 10^2.Origin Greek for hundred "hekaton".'],
-               _(u"vic"):
-                       [(converters.m, 20.0), '', _(u"Twenty times.")],
-               _(u"duodec"):
-                       [(converters.m, 12.0), '', _(u"Twelve times.")],
-               _(u"dozen (bakers | long)"):
-                       [(converters.m, 13.0), '', _(u"Thirteen items. The cardinal number that is the sum of twelve and one syn:  thirteen, 13, XIII, long dozen.")],
-               _(u"dozen"):
-                       [(converters.m, 12.0), '', _(u"Twelve items. Usually used to measure the quantity of eggs in a carton.")],
-               _(u"undec"):
-                       [(converters.m, 11.0), '', _(u"Eleven times.")],
-               _(u"deca | deka | ten"):
-                       [(converters.m, 1.0e1), '', '10^1. Ten times. Origin Greek for ten "deka".'],
-               _(u"sex | hexad"):
-                       [(converters.m, 6.0), '', _(u"Six times.")],
-               _(u"quin"):
-                       [(converters.m, 5.0), '', _(u"Five times.")],
-               _(u"quadr | quadri | quadruple"):
-                       [(converters.m, 4.0), '', _(u"Four times.")],
-               _(u"thrice | tri | triple"):
-                       [(converters.m, 3.0), '', _(u"Three times.")],
-               _(u"bi | double"):
-                       [(converters.m, 2.0), '', _(u"Two times.")],
-               _(u"sesqui | sesqu"):
-                       [(converters.m, 1.5), '', _(u"One and one half times.")],
-               _(u"one | mono"):
-                       [(converters.m, 1.0), '', _(u"Single unit value.")],
-               _(u"quarter"):
-                       [(converters.m, 0.25), '', _(u"One fourth.")],
-               _(u"demi | semi | half"):
-                       [(converters.m, 0.5), '', _(u"One half.")],
-               _(u"eigth"):
-                       [(converters.m, 0.125), '', _(u"One eigth.")],
-               _(u"deci"):
-                       [(converters.m, 1.0e-1), "d", '10^-1. Origin Latin tenth "decimus".'],
-               _(u"centi"):
-                       [(converters.m, 1.0e-2), "c", '10^-2. Origin Latin hundred, hundredth "centum".'],
-               _(u"percent"):
-                       [(converters.m, 1.0e-2), "%", _(u"10^-2. A proportion multiplied by 100")],
-               _(u"milli"):
-                       [(converters.m, 1.0e-3), "m", '10^-3. A prefix denoting a thousandth part of; as, millimeter, milligram, milliampere.Origin Latin thousand "mille".'],
-               _(u"decimilli"):
-                       [(converters.m, 1.0e-4), "dm", _(u"10^-4")],
-               _(u"centimilli"):
-                       [(converters.m, 1.0e-5), "cm", _(u"10^-5. ")],
-               _(u"micro"):
-                       [(converters.m, 1.0e-6), u"\xb5", '10^-6. A millionth part of; as, microfarad, microohm, micrometer.Origin Latin small "mikros".'],
-               _(u"parts per million | ppm"):
-                       [(converters.m, 1.0e-6), "ppm", _(u"10^-6. Parts per million usually used in measuring chemical concentrations.")],
-               _(u"nano"):
-                       [(converters.m, 1.0e-9), "n", '10^-9. Origin Greek dwarf "nanos".'],
-               _(u"pico"):
-                       [(converters.m, 1.0e-12), "p", '10^-12. Origin Italian tiny "piccolo".'],
-               _(u"femto"):
-                       [(converters.m, 1.0e-15), "f", '10^-15. Origin Old Norse fifteen "femten" as in 10^-15.'],
-               _(u"atto"):
-                       [(converters.m, 1.0e-18), "a", '10^-18. Origin Old Norse eighteen "atten" as in 10^-18.'],
-               _(u"zepto | ento"):
-                       [(converters.m, 1.0e-21), "z", '10^-21. zepto origin Latin ultimate letter z "zeta".'],
-               _(u"yocto | fito"):
-                       [(converters.m, 1.0e-24), "y", '10^-24. yocto origin Latin penultimate letter y "iota".'],
-               _(u"syto | harpo"):
-                       [(converters.m, 1.0e-27), "hr", _(u"10^-27. harpo coined by Morgan Burke after Marx brother Harpo Marx.")],
-               _(u"tredo | groucho"):
-                       [(converters.m, 1.0e-30), "gc", _(u"10^-30. groucho coined by Morgan Burke after Marx brother Groucho Marx.")],
-               _(u"revo | zeppo"):
-                       [(converters.m, 1.0e-33), "zp", _(u"10^-33. zeppo coined by Morgan Burke after Marx brother Zeppo Marx.")],
-               _(u"gummo"):
-                       [(converters.m, 1.0e-36), "gm", _(u"10^-36. Coined by Morgan Burke after Marx brother Gummo Marx.")],
-               _(u"chico"):
-                       [(converters.m, 1.0e-39), "ch", _(u"10^-39. Coined by Morgan Burke after Marx brother Chico Marx.")],
-       },
-
-       #There does not seem to be a "standard" for shoe sizes so some are left out for now until a decent reference can be found.
-       _(u"Shoe Size"): {
-               ".base_unit": "centimeter",
-               _(u"centimeter"):
-               [(converters.m, 1.0), "cm", _(u"The hundredth part of a meter; a measure of length equal to rather more than thirty-nine hundredths (0.3937) of an inch.")],
-               _(u"inch"):
-               [(converters.m, (2.54)), "in", _(u"The twelfth part of a foot, commonly subdivided into halves, quarters, eights, sixteenths, etc., as among mechanics. It was also formerly divided into twelve parts, called lines, and originally into three parts, called barleycorns, its length supposed to have been determined from three grains of barley placed end to end lengthwise.")],
-               #"European"):
-               #       [(converters.slo, ((35, 47), (22.5, 30.5))), '', _(u"Used by Birkenstock")],
-               _(u"Mens (US)"):
-               [(converters.slo, ((6.0, 13.0), ((2.54*(9+1.0/3)), (2.54*(11+2.0/3))))), '', 'Starting at 9 1/3" for size 6 and moving up by 1/6" for each half size to 11 2/3" for size 13. Beware that some manufacturers use different measurement techniques.'],
-               _(u"Womens (US)"):
-               [(converters.gof, (2.54*((2.0+2.0/6)/7.0), 2.54*(8.5-(5.0*((2.0+2.0/6)/7.0))))), '', 'Starting at 8 1/2" for size 5 and moving up by 1/6" for each half size to 10 5/6" for size 12. Beware that some manufacturers use different measurement techniques.'],
-               _(u"Childrens (US)"):
-               [(converters.dso, ((5.5, 13.5), ((2.54*(4.0+5.0/6)), (2.54*7.5)), (1.0, 5.0), (2.54*(7.0+2.0/3), 2.54*9.0))), '', 'Starting at 4 5/6" for size 5 1/2 up to 7 1/3" for size 13 then 7 2/3" for size 1 and going up to 9" for size 5.'],
-               _(u"Mens (UK)"):
-               [(converters.slo, ((5.0, 12.0), ((2.54*(9+1.0/3)), (2.54*(11+2.0/3))))), '', 'Starting at 9 1/3" for size 5 and moving up by 1/6" for each half size to 11 2/3" for size 12. Beware that some manufacturers use different measurement techniques.'],
-               _(u"Womens (UK)"):
-               [(converters.gof, (2.54*((2.0+2.0/6)/7.0), 2.54*(8.5-(3.0*((2.0+2.0/6)/7.0))))), '', 'Starting at 8 1/2" for size 3 and moving up by 1/6" for each half size to 10 5/6" for size 10. Beware that some manufacturers use different measurement techniques.'],
-               #"Asian"):
-               #       [(converters.slo, ((23.5, 31.5), (25.0, 30.5))), '', ''],
-       },
-
-       _(u"Speed | Velocity"): {
-               ".base_unit": "meter per second",
-               _(u"meter per second"):
-               [(converters.m, 1.0), "m/s", ''],
-               _(u"speed of light | warp"):
-               [(converters.m, 299792458.0), "c", _(u"The speed at which light travels in a vacuum; about 300, 000 km per second; a universal constant.")],
-               _(u"miles per second"):
-               [(converters.m, 1609.344), "mi/s", ''],
-               _(u"kilometer per second"):
-               [(converters.m, 1.0e3), "km/s", ''],
-               _(u"millimeter per second"):
-               [(converters.m, 1.0e-3), "mm/s", ''],
-               _(u"knot"):
-               [(converters.m, 0.514444444444444), '', _(u"Nautical measurement for speed as one nautical mile per hour. The number of knots which run off from the reel in half a minute, therefore, shows the number of miles the vessel sails in an hour.")],
-               _(u"miles per hour"):
-               [(converters.m, 0.44704), "mi/h", ''],
-               _(u"foot per second"):
-               [(converters.m, 0.3048), "ft/s", ''],
-               _(u"foot per minute"):
-               [(converters.m, 0.00508), "ft/min", ''],
-               _(u"kilometer per hour"):
-               [(converters.m, 0.277777777777778), "km/h", ''],
-               _(u"mile per day"):
-               [(converters.m, 1.86266666666667E-02), "mi/day", ''],
-               _(u"centimeter per second"):
-               [(converters.m, 1.0e-2), "cm/s", ''],
-               _(u"knot (admiralty)"):
-               [(converters.m, 0.514773333333333), '', ''],
-               _(u"mach (sea level & 32 degF)"):
-               [(converters.m, 331.46), '', ''],
-       },
-
-       _(u"Temperature"): {
-               ".base_unit": _(u"kelvin"),
-               _(u"kelvin"):
-               [(converters.m, 1.0), "K", _(u"Named after the English mathematician and physicist William Thomson Kelvin (1824-1907). The basic unit of thermodynamic temperature adopted under the System International d'Unites.")],
-               _(u"celsius (absolute)"):
-               [(converters.m, 1.0), u"\xb0C absolute", ''],
-               _(u"celsius (formerly centigrade)"):
-               [(converters.ofg, (273.15, 1.0)), u"\xb0C", _(u"Named after the Swedish astronomer and physicist Anders Celsius (1701-1744). The Celsius thermometer or scale. It is the same as the centigrade thermometer or scale. 0\xb0 marks the freezing point of water and 100\xb0 marks the boiling point of water.  ")],
-               _(u"fahrenheit"):
-               [(converters.f, ('((x-32.0)/1.8)+273.15', '((x-273.15)*1.8)+32.0')), u"\xb0F", _(u"Named after the German physicist Gabriel Daniel Fahrenheit (1686-1736). The Fahrenheit thermometer is so graduated that the freezing point of water is at 32\xb0 above the zero of its scale, and the boiling point at 212\xb0 above. It is commonly used in the United States and in England.")],
-
-               _(u"reaumur"):
-               [(converters.gof, (1.25, 273.15)), u"\xb0Re", _(u"Named after the French scientist Ren\u00E9-Antoine Ferchault de R\u00E9aumur (1683-1757). Conformed to the scale adopted by R\u00E9aumur in graduating the thermometer he invented. The R\u00E9aumur thermometer is so graduated that 0\xb0 marks the freezing point and 80\xb0 the boiling point of water.")],
-               _(u"fahrenheit (absolute)"):
-               [(converters.m, 1/1.8), u"\xb0F absolute", ''],
-               _(u"rankine"):
-               [(converters.m, 1/1.8), u"\xb0R", _(u"Named after the British physicist and engineer William Rankine (1820-1872). An absolute temperature scale in Fahrenheit degrees.")],
-       },
-
-       _(u"Temperature Difference"): {
-               ".base_unit": "temp. diff. in kelvin",
-               _(u"temp. diff. in kelvin"):
-               [(converters.m, 1.0), "K", ''],
-               _(u"temp. diff. in degrees Celsius"):
-               [(converters.m, 1.0), u"\xb0C", ''],
-               _(u"temp. diff. in degrees Reaumur"):
-               [(converters.m, 1.25), u"\xb0Re", ''],
-               _(u"temp. diff. in degrees Rankine"):
-               [(converters.m, 5.0/9), u"\xb0R", ''],
-               _(u"temp. diff. in degrees Fahrenheit"):
-               [(converters.m, 5.0/9), u"\xb0F", ''],
-       },
-
-       _(u"Time"): {
-               ".base_unit": "second",
-               _(u"year"):
-               [(converters.m, 365*86400.0), "a", _(u"The time of the apparent revolution of the sun trough the ecliptic; the period occupied by the earth in making its revolution around the sun, called the astronomical year; also, a period more or less nearly agreeing with this, adopted by various nations as a measure of time, and called the civil year; as, the common lunar year of 354 days, still in use among the Mohammedans; the year of 360 days, etc. In common usage, the year consists of 365 days, and every fourth year (called bissextile, or leap year) of 366 days, a day being added to February on that year, on account of the excess above 365 days")],
-               _(u"year (anomalistic)"):
-               [(converters.m, 365*86400.0+22428.0), '', _(u"The time of the earth's revolution from perihelion to perihelion again, which is 365 days, 6 hours, 13 minutes, and 48 seconds.")],
-               _(u"year (common lunar)"):
-               [(converters.m, 354*86400.0), '', _(u"The period of 12 lunar months, or 354 days.")],
-               _(u"year (embolismic | Intercalary lunar)"):
-               [(converters.m, 384*86400.0), '', _(u"The period of 13 lunar months, or 384 days.")],
-               _(u"year (leap | bissextile)"):
-               [(converters.m, 366*86400.0), '', _(u"Bissextile; a year containing 366 days; every fourth year which leaps over a day more than a common year, giving to February twenty-nine days. Note: Every year whose number is divisible by four without a remainder is a leap year, excepting the full centuries, which, to be leap years, must be divisible by 400 without a remainder. If not so divisible they are common years. 1900, therefore, is not a leap year.")],
-               _(u"year (sabbatical)"):
-               [(converters.m, 7*365*86400.0), '', _(u"Every seventh year, in which the Israelites were commanded to suffer their fields and vineyards to rest, or lie without tillage.")],
-               _(u"year (lunar astronomical)"):
-               [(converters.m, 354*86400.0+31716.0), '', _(u"The period of 12 lunar synodical months, or 354 days, 8 hours, 48 minutes, 36 seconds.")],
-               _(u"year (lunisolar)"):
-               [(converters.m, 532*365*86400.0), '', _(u"A period of time, at the end of which, in the Julian calendar, the new and full moons and the eclipses recur on the same days of the week and month and year as in the previous period. It consists of 532 common years, being the least common multiple of the numbers of years in the cycle of the sun and the cycle of the moon.")],
-               _(u"year (sidereal)"):
-               [(converters.m, 365*86400.0+22149.3), '', _(u"The time in which the sun, departing from any fixed star, returns to the same. This is 365 days, 6 hours, 9 minutes, and 9.3 seconds.")],
-               _(u"year (sothic)"):
-               [(converters.m, 365*86400.0+6*3600.0), '', _(u"The Egyptian year of 365 days and 6 hours, as distinguished from the Egyptian vague year, which contained 365 days. The Sothic period consists of 1, 460 Sothic years, being equal to 1, 461 vague years. One of these periods ended in July, a.d. 139.")],
-               _(u"year (tropic)"):
-               [(converters.m, 365*86400.0+20926.0), '', _(u"The solar year; the period occupied by the sun in passing from one tropic or one equinox to the same again, having a mean length of 365 days, 5 hours, 48 minutes, 46.0 seconds, which is 20 minutes, 23.3 seconds shorter than the sidereal year, on account of the precession of the equinoxes.")],
-               _(u"month"):
-               [(converters.m, 365*86400.0/12), '', _(u"One of the twelve portions into which the year is divided; the twelfth part of a year, corresponding nearly to the length of a synodic revolution of the moon, -- whence the name. In popular use, a period of four weeks is often called a month.")],
-               _(u"month (sidereal)"):
-               [(converters.m, 27.322*86400.0), '', _(u"Period between successive conjunctions with a star, 27.322 days")],
-               _(u"month (synodic | lunar month | lunation)"):
-               [(converters.m, 29.53059*86400.0), '', _(u"The period between successive new moons (29.53059 days) syn: lunar month, moon, lunation")],
-               _(u"day"):
-               [(converters.m, 86400.0), "d", _(u"The period of the earth's revolution on its axis. -- ordinarily divided into twenty-four hours. It is measured by the interval between two successive transits of a celestial body over the same meridian, and takes a specific name from that of the body. Thus, if this is the sun, the day (the interval between two successive transits of the sun's center over the same meridian) is called a solar day; if it is a star, a sidereal day; if it is the moon, a lunar day.")],
-               _(u"day (sidereal)"):
-               [(converters.m, 86164.09), '', _(u"The interval between two successive transits of the first point of Aries over the same meridian. The Sidereal day is 23 h. 56 m. 4.09 s. of mean solar time.")],
-               _(u"day (lunar | tidal)"):
-               [(converters.m, 86400.0+50*60.0), '', _(u"24 hours 50 minutes used in tidal predictions. ")],
-               _(u"hour"):
-               [(converters.m, 3600.0), "h", _(u"The twenty-fourth part of a day; sixty minutes.")],
-               _(u"minute"):
-               [(converters.m, 60.0), "min", _(u"The sixtieth part of an hour; sixty seconds.")],
-               _(u"second"):
-               [(converters.m, 1.0), "s", _(u"The sixtieth part of a minute of time.")],
-               _(u"millisecond"):
-               [(converters.m, 1.0e-3), "ms", _(u"One thousandth of a second.")],
-               _(u"microsecond"):
-               [(converters.m, 1.0e-6), u"\xb5s", _(u"One millionth of a second.")],
-               _(u"nanosecond"):
-               [(converters.m, 1.0e-9), "ns", ''],
-               _(u"picosecond"):
-               [(converters.m, 1.0e-12), "ps", ''],
-               _(u"millennium"):
-               [(converters.m, 1000*365*86400.0), '', _(u"A thousand years; especially, the thousand years mentioned in the twentieth chapter in the twentieth chapter of Revelation, during which holiness is to be triumphant throughout the world. Some believe that, during this period, Christ will reign on earth in person with his saints.")],
-               _(u"century"):
-               [(converters.m, 100*365*86400.0), '', _(u"A period of a hundred years; as, this event took place over two centuries ago. Note: Century, in the reckoning of time, although often used in a general way of any series of hundred consecutive years (as, a century of temperance work), usually signifies a division of the Christian era, consisting of a period of one hundred years ending with the hundredth year from which it is named; as, the first century (a. d. 1-100 inclusive); the seventh century (a.d. 601-700); the eighteenth century (a.d. 1701-1800). With words or phrases connecting it with some other system of chronology it is used of similar division of those eras; as, the first century of Rome (A.U.C. 1-100).")],
-               _(u"decade"):
-                       [(converters.m, 10*365*86400.0), '', _(u"A group or division of ten; esp., a period of ten years; a decennium; as, a decade of years or days; a decade of soldiers; the second decade of Livy.")],
-               _(u"week"):
-                       [(converters.m, 7*86400.0), '', _(u"A period of seven days, usually that reckoned from one Sabbath or Sunday to the next. Also seven nights, known as sennight.")],
-               _(u"fortnight"):
-                       [(converters.m, 14*86400.0), '', _(u"Fourteen nights, our ancestors reckoning time by nights and winters.  The space of fourteen days; two weeks.")],
-               _(u"novennial"):
-                       [(converters.m, 9*365*86400.0), '', _(u"Done or recurring every ninth year.")],
-               _(u"octennial"):
-                       [(converters.m, 8*365*86400.0), '', _(u"Happening every eighth year; also, lasting a period of eight years.")],
-               _(u"olympiad"):
-                       [(converters.m, 4*365*86400.0), '', _(u"A period of four years, by which the ancient Greeks reckoned time, being the interval from one celebration of the Olympic games to another, beginning with the victory of Coroebus in the foot race, which took place in the year 776 b.c.; as, the era of the olympiads.")],
-               _(u"pregnancy"):
-                       [(converters.m, 9*365*86400.0/12), '', _(u"The condition of being pregnant; the state of being with young. A period of approximately 9 months for humans")],
-               _(u"quindecennial"):
-                       [(converters.m, 15*365*86400.0), '', _(u"A period of 15 years.")],
-               _(u"quinquennial"):
-                       [(converters.m, 5*365*86400.0), '', _(u"Occurring once in five years, or at the end of every five years; also, lasting five years. A quinquennial event.")],
-               _(u"septennial"):
-                       [(converters.m, 7*365*86400.0), '', _(u"Lasting or continuing seven years; as, septennial parliaments.")],
-               _(u"cesium vibrations"):
-                       [(converters.m, 1.0/9192631770.0), "vibrations", _(u"It takes one second for hot cesium atoms to vibrate 9, 192, 631, 770 times (microwaves). This standard was adopted by the International System in 1967.")],
-       },
-
-       _(u"Viscosity (Dynamic)"): {
-               ".base_unit": "pascal-second",
-               _(u"pascal-second"):
-               [(converters.m, 1.0), u"Pa\xb7s", ''],
-               _(u"reyn"):
-               [(converters.m, 6894.75729316836), '', ''],
-               _(u"poise"):
-               [(converters.m, 0.1), "P", ''],
-               _(u"microreyn"):
-               [(converters.m, 6894.75729316836e-6), '', ''],
-               _(u"millipascal-second"):
-               [(converters.m, 1.0e-3), u"mPa\xb7s", ''],
-               _(u"centipoise"):
-               [(converters.m, 1.0e-3), "cP", ''],
-               _(u"micropascal-second"):
-               [(converters.m, 1.0e-6), u"\xb5Pa\xb7s", ''],
-       },
-
-       _(u"Viscosity (Kinematic)"): {
-               ".base_unit": "square meter per second",
-               _(u"square meter per second"):
-               [(converters.m, 1.0), u"m\xb2/s", ''],
-               _(u"square millimeter per second"):
-               [(converters.m, 1.0e-6), u"mm\xb2/s", ''],
-               _(u"square foot per second"):
-               [(converters.m, 0.09290304), u"ft\xb2/s", ''],
-               _(u"square centimetre per second"):
-               [(converters.m, 1.0e-4), u"cm\xb2/s", ''],
-               _(u"stokes"):
-               [(converters.m, 1.0e-4), "St", ''],
-               _(u"centistokes"):
-               [(converters.m, 1.0e-6), "cSt", ''],
-       },
-
-       _(u"Volume and Liquid Capacity"): {
-               ".base_unit": "millilitre",
-               _(u"can #10 can"):
-               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*109.43), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
-               _(u"can #2 can"):
-               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*20.55), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
-               _(u"can #2.5 can"):
-               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*29.79), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
-               _(u"can #5 can"):
-               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*59.1), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
-               _(u"can #1 can (Picnic)"):
-               [(converters.m, 4*2*2*8*2*3*4.92892159375/128*10.94), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
-               _(u"can #1 can (Tall)"):
-               [(converters.m, 4*2*2*8*2*3*4.92892159375/128* 16.70), '', _(u"Taken from the Can Manufacturers Institute (CMI).  http: //www.cancentral.com/standard.cfm#foodcan")],
-               _(u"can #303 can"):
-               [(converters.m, 4*2*2*8*2*3*4.92892159375/128* 16.88), '', _(u"Taken from the Can Manufacturers Institute (CMI) http: //www.cancentral.com/standard.cfm#foodcan .")],
-               _(u"barrel (wine UK)"):
-               [(converters.m, 31.5*4*2*2.5*8*2*3*4.73551071041125), '', _(u"31.5 UK Gallons")],
-               _(u"barrel (UK)"):
-               [(converters.m, 36*4*2*2.5*8*2*3*4.73551071041125), '', _(u"36 UK Gallons")],
-               _(u"barrel of oil (US)"):
-               [(converters.m, 42*4*2*2*8*2*3*4.92892159375), "", _(u"Barrel of petroleum (oil), 42 US Gallons")],
-               _(u"barrel (US federal)"):
-               [(converters.m, 31*4*2*2*8*2*3*4.92892159375), "", _(u"31 US Gallons")],
-               _(u"barrel (US)"):
-               [(converters.m, 31.5*4*2*2*8*2*3*4.92892159375), "", _(u"31.5 US Gallons")],
-               _(u"caphite"):
-               [(converters.m, 1374.1046), '', _(u"Ancient Arabian")],
-               _(u"cantaro"):
-               [(converters.m, 13521.1108), '', _(u"Spanish")],
-               _(u"oxybaphon"):
-               [(converters.m, 66.245), '', _(u"Greek")],
-               _(u"cotula | hemina | kotyle"):
-               [(converters.m, 308.3505), '', _(u"Greek")],
-               _(u"cyathos"):
-               [(converters.m, 451.5132), '', _(u"Greek")],
-               _(u"cados"):
-               [(converters.m, 38043.3566), '', _(u"Greek")],
-               _(u"metertes | amphura"):
-               [(converters.m, 39001.092), '', _(u"Greek")],
-               _(u"mushti"):
-               [(converters.m, 60.9653), '', _(u"Indian")],
-               _(u"cab"):
-               [(converters.m, 2202.5036), '', _(u"Israeli")],
-               _(u"hekat"):
-               [(converters.m, 4768.6752), '', _(u"Israeli")],
-               _(u"bath | bu"):
-               [(converters.m, 36871.2), '', _(u"Israeli")],
-               _(u"acetabulum"):
-               [(converters.m, 66.0752), '', _('Roman')],
-               _(u"dash (UK)"):
-               [(converters.m, 4.73551071041125/16), '', _(u"one half of a pinch")],
-               _(u"pinch (UK)"):
-                       [(converters.m, 4.73551071041125/8), '', _(u"One eigth of a teaspoon")],
-               _(u"gallon (UK)"):
-                       [(converters.m, 4*2*2.5*8*2*3*4.73551071041125), '', _(u"A measure of capacity, containing four quarts; -- used, for the most part, in liquid measure, but sometimes in dry measure. The English imperial gallon contains 10 pounds avoirdupois of distilled water at 62\xb0F, and barometer at 30 inches, equal to 277.274 cubic inches.")],
-               _(u"quart (UK)"):
-                       [(converters.m, 2*2.5*8*2*3*4.73551071041125), '', _(u"The fourth part of a gallon; the eighth part of a peck; two pints. Note: In imperial measure, a quart is forty English fluid ounces; in wine measure, it is thirty-two American fluid ounces. The United States dry quart contains 67.20 cubic inches, the fluid quart 57.75. The English quart contains 69.32 cubic inches.")],
-               _(u"pint (UK)"):
-                       [(converters.m, 2.5*8*2*3*4.73551071041125), '', ''],
-               _(u"cup (UK)"):
-                       [(converters.m, 8*2*3*4.73551071041125), '', ''],
-               _(u"ounce - fluid ounce (UK)"):
-                       [(converters.m, 2*3*4.73551071041125), '', _(u"Contains 1 ounce mass of distilled water at 62\xb0F, and barometer at 30 inches")],
-               _(u"tablespoon (UK)"):
-                       [(converters.m, 3*4.73551071041125), '', _(u"One sixteenth of a cup. A spoon of the largest size commonly used at the table; -- distinguished from teaspoon, dessert spoon, etc.")],
-               _(u"teaspoon (UK)"):
-                       [(converters.m, 4.73551071041125), '', _(u"One third of a tablespoon. A small spoon used in stirring and sipping tea, coffee, etc., and for other purposes.")],
-               _(u"dash (US)"):
-                       [(converters.m, 4.92892159375/16), '', _(u"one half of a pinch")],
-               _(u"pinch (US)"):
-                       [(converters.m, 4.92892159375/8), '', _(u"One eigth of a teaspoon")],
-               _(u"keg (beer US)"):
-                       [(converters.m, 15.5*768*4.92892159375), "", """US standard size beer keg = 1/2 barrel = 15.5 US gallons; weighs approx. 29.7 pounds empty, 160.5 pounds full."""],
-               _(u"keg (wine US)"):
-                       [(converters.m, 12*768*4.92892159375), "", """12 US gallons."""],
-               _(u"ponykeg (US)"):
-                       [(converters.m, 7.75*768*4.92892159375), "", """1/2 US beerkeg, 7.75 US gallons."""],
-               _(u"barrel (beer US)"):
-                       [(converters.m, 31*768*4.92892159375), "", """Two US beerkegs, 31 US gallons."""],
-               _(u"barrel (wine US)"):
-                       [(converters.m, 31.5*768*4.92892159375), "", """31.5 US gallons."""],
-               _(u"hogshead (US)"):
-                       [(converters.m, 63*768*4.92892159375), "", """Equal to 2 barrels or 63 US gallons."""],
-               _(u"fifth (US)"):
-                       [(converters.m, .2*768*4.92892159375), "", """One fifth of a gallon."""],
-               _(u"jigger"):
-                       [(converters.m, 9*4.92892159375), "", """1.5 fluid ounces (US)"""],
-               _(u"shot"):
-                       [(converters.m, 9*4.92892159375), "", """1.5 fluid ounces (US)"""],
-               _(u"winebottle"):
-                       [(converters.m, 750*1.0), "", """750 milliliters"""],
-               _(u"wineglass (US)"):
-                       [(converters.m, 24*4.92892159375), "", """Equal to 4 fluid ounces (US)."""],
-               _(u"winesplit"):
-                       [(converters.m, 750.0/4), "", """1/4 winebottle"""],
-               _(u"magnum"):
-                       [(converters.m, 1500), "", """1.5 liters"""],
-               _(u"gallon (US)"):
-                       [(converters.m, 4*2*2*8*2*3*4.92892159375), '', _(u"A measure of capacity, containing four quarts; -- used, for the most part, in liquid measure, but sometimes in dry measure. Note: The standard gallon of the Unites States contains 231 cubic inches, or 8.3389 pounds avoirdupois of distilled water at its maximum density, and with the barometer at 30 inches. This is almost exactly equivalent to a cylinder of seven inches in diameter and six inches in height, and is the same as the old English wine gallon. The beer gallon, now little used in the United States, contains 282 cubic inches.")],
-               _(u"quart (US)"):
-                       [(converters.m, 2*2*8*2*3*4.92892159375), '', _(u"The fourth part of a gallon; the eighth part of a peck; two pints. Note: In imperial measure, a quart is forty English fluid ounces; in wine measure, it is thirty-two American fluid ounces. The United States dry quart contains 67.20 cubic inches, the fluid quart 57.75. The English quart contains 69.32 cubic inches.")],
-               _(u"pint (US)"):
-                       [(converters.m, 2*8*2*3*4.92892159375), '', ''],
-               _(u"cup (US)"):
-                       [(converters.m, 8*2*3*4.92892159375), '', ''],
-               _(u"ounce - fluid ounce (US)"):
-                       [(converters.m, 2*3*4.92892159375), '', ''],
-               _(u"beerbottle (US 12 ounce)"):
-                       [(converters.m, 12*2*3*4.92892159375), '', ''],
-               _(u"tablespoon (US)"):
-                       [(converters.m, 3*4.92892159375), '', _(u"One sixteenth of a cup. A spoon of the largest size commonly used at the table; -- distinguished from teaspoon, dessert spoon, etc.")],
-               _(u"teaspoon (US)"):
-                       [(converters.m, 4.92892159375), '', _(u"One third of a tablespoon. A small spoon used in stirring and sipping tea, coffee, etc., and for other purposes.")],
-               _(u"shaku"):
-                       [(converters.m, 18.04), '', _(u"A Japanese unit of volume, the shaku equals about 18.04 milliliters (0.61 U.S. fluid ounce). Note: shaku also means area and length.")],
-               _(u"cubic yard"):
-                       [(converters.m, 27*1728*16.387064), u"yd\xb3", ''],
-               _(u"acre foot"):
-                       [(converters.m, 1728*16.387064*43560), '', ''],
-               _(u"cubic foot"):
-                       [(converters.m, 1728*16.387064), u"ft\xb3", ''],
-               _(u"cubic inch"):
-                       [(converters.m, 16.387064), u"in\xb3", ''],
-               _(u"cubic meter"):
-                       [(converters.m, 1.0e6), u"m\xb3", ''],
-               _(u"cubic decimeter"):
-                       [(converters.m, 1.0e3), u"dm\xb3", ''],
-               _(u"litre"):
-                       [(converters.m, 1.0e3), "l", _(u"A measure of capacity in the metric system, being a cubic decimeter.")],
-               _(u"cubic centimeter"):
-                       [(converters.m, 1.0), u"cm\xb3", ''],
-               _(u"millilitre"):
-                       [(converters.m, 1.0), "ml", ''],
-               _(u"centilitre"):
-                       [(converters.m, 10*1.0), "cl", ''],
-               _(u"decilitre"):
-                       [(converters.m, 10*10*1.0), "dl", ''],
-               _(u"mil"):
-                       [(converters.m, 1.0), '', _(u"Equal to one thousandth of a liter syn: milliliter, millilitre, ml, cubic centimeter, cubic centimeter, cc")],
-               _(u"fluid dram"): 
-                       [(converters.m, 2*3*4.92892159375/8), '', _(u"Used in Pharmaceutical")],
-               _(u"minim"):
-                       [(converters.m, 2*3*4.92892159375/480), '', _(u"Used in Pharmaceutical to represent one drop. 1/60 fluid dram or 1/480 fluid ounce. A U.S. minim is about 0.003760 in\xb3 or 61.610 \xb5l. The British minim is about 0.003612 in\xb3 or 59.194 \xb5l. Origin of the word is from the Latin minimus, or small.")],
-       },
-
-       _(u"Volume and Dry Capacity"): {
-               ".base_unit": "cubic meter",
-               _(u"cubic meter"):
-               [(converters.m, 1.0), u"m\xb3", ''],
-               _(u"cubic decimeter"):
-               [(converters.m, 1.0e-3), u"dm\xb3", ''],
-               _(u"cubic millimeter"):
-               [(converters.m, 1.0e-9), u"mm\xb3", """"""],
-               _(u"cubic centimeter"):
-               [(converters.m, 1.0e-6), u"cm\xb3", """"""],
-               _(u"cord"):
-               [(converters.m, 3.624556363776), '', _(u"A pile of wood 8ft x 4ft x 4ft.")],
-               _(u"cubic yard"):
-               [(converters.m, 0.764554857984), u"yd\xb3", ''],
-               _(u"bushel (US)"):
-               [(converters.m, 4*2*0.00440488377086), "bu", _(u"A dry measure, containing four pecks, eight gallons, or thirty-two quarts. Note: The Winchester bushel, formerly used in England, contained 2150.42 cubic inches, being the volume of a cylinder 181/2 inches in internal diameter and eight inches in depth. The standard bushel measures, prepared by the United States Government and distributed to the States, hold each 77.6274 pounds of distilled water, at 39.8deg Fahr. and 30 inches atmospheric pressure, being the equivalent of the Winchester bushel. The imperial bushel now in use in England is larger than the Winchester bushel, containing 2218.2 cubic inches, or 80 pounds of water at 62deg Fahr.")],
-               _(u"bushel (UK | CAN)"):
-               [(converters.m, 4*2*4*1.1365225e-3), "bu", _(u"A dry measure, containing four pecks, eight gallons, or thirty-two quarts. Note: The Winchester bushel, formerly used in England, contained 2150.42 cubic inches, being the volume of a cylinder 181/2 inches in internal diameter and eight inches in depth. The standard bushel measures, prepared by the United States Government and distributed to the States, hold each 77.6274 pounds of distilled water, at 39.8deg Fahr. and 30 inches atmospheric pressure, being the equivalent of the Winchester bushel. The imperial bushel now in use in England is larger than the Winchester bushel, containing 2218.2 cubic inches, or 80 pounds of water at 62deg Fahr.")],
-               _(u"peck (US)"):
-               [(converters.m, 2*0.00440488377086), '', ''],
-               _(u"peck (UK | CAN)"):
-               [(converters.m, 2*4*1.1365225e-3), '', ''],
-               _(u"gallon (US dry)"):
-               [(converters.m, 0.00440488377086), "gal", ''],
-               _(u"gallon (CAN)"):
-               [(converters.m, 4*1.1365225e-3), "gal", ''],
-               _(u"quart (US dry)"):
-               [(converters.m, 1.101220942715e-3), "qt", ''],
-               _(u"quart (CAN)"):
-               [(converters.m, 1.1365225e-3), "qt", ''],
-               _(u"cubic foot"):
-               [(converters.m, 0.028316846592), u"ft\xb3", ''],
-               _(u"board foot"):
-               [(converters.m, 0.028316846592/12), '', _(u"lumber 1ft\xb2 and 1 in thick")],
-               _(u"litre"):
-               [(converters.m, 1.0e-3), "l", _(u"A measure of capacity in the metric system, being a cubic decimeter.")],
-               _(u"pint (US dry)"):
-               [(converters.m, 5.506104713575E-04), "pt", ''],
-               _(u"cubic inch"):
-               [(converters.m, 0.000016387064), u"in\xb3", ''],
-               _(u"coomb"):
-               [(converters.m, 4*4*2*4*1.1365225e-3), '', _(u"British. 4 bushels")],
-               _(u"peck"):
-               [(converters.m, 37.23670995671), '', _(u"The fourth part of a bushel; a dry measure of eight quarts")],
-               _(u"quart (dry)"):
-               [(converters.m, 4.65458874458874), '', _(u"The fourth part of a gallon; the eighth part of a peck; two pints. Note: In imperial measure, a quart is forty English fluid ounces; in wine measure, it is thirty-two American fluid ounces. The United States dry quart contains 67.20 cubic inches, the fluid quart 57.75. The English quart contains 69.32 cubic inches.")],
-       },
-
-       _(u"Thermal conductance (Area)"): {
-               ".base_unit": "Watts per square meter Kelvin",
-               _(u"watts per square meter Kelvin"):
-               [(converters.m, 1.0), u"W/m\xb2\xb7K", ''],
-               _(u"watts per square meter deg C"):
-               [(converters.m, 1.0), u"W/h.m\xb2\xb7\xb0C", ''],
-               _(u"kilocalories per hour square meter deg C"):
-               [(converters.m, 1.163), u"kcal/h\xb7m\xb7\xb0C", ''],
-               _(u"british thermal units per second square foot deg F"):
-               [(converters.m, 2.0428), u"Btu/sec\xb7ft\xb2\xb7\xb0F", ''],
-               _(u"british thermal units per hour square foot deg F"):
-               [(converters.m, 5.6782), u"Btu/h\xb7ft\xb2\xb7\xb0F", ''],
-       },
-
-       _(u"Thermal conductance (Linear)"): {
-               ".base_unit": "cubic meter",
-               _(u"watts per meter Kelvin"):
-               [(converters.m, 1.0), u"W/m\xb7K", ''],
-               _(u"watts per meter deg C"):
-               [(converters.m, 1.0), u"W/m\xb7\xb0C", ''],
-               _(u"kilocalories per hour meter deg C"):
-               [(converters.m, 1.163), u"kcal/h\xb7m\xb7\xb0C", ''],
-               _(u"british thermal units per second foot deg F"):
-               [(converters.m, 1.703), u"Btu/sec\xb7ft\xb7\xb0F", ''],
-       },
-
-       _(u"Thermal resistance"): {
-               ".base_unit": _(u"square meter kelvin per watt"),
-               _(u"square meter kelvin per watt"):
-               [(converters.m, 1.0), u"m\xb2\xb7K/W", ''],
-               _(u"square meter deg C per watt"):
-               [(converters.m, 1.0), u"m\xb2\xb7\xb0C/W", ''],
-               _(u"clo"):
-               [(converters.m, 1.50e-1), u"clo", _(u"Clo is the unit for effective clothing insulation. It is used to evaluate the expected comfort of users in certain humidity, temperature and workload conditions (and estimate air conditioning or heating loads, for instance.).")],
-               _(u"hour square foot deg F per BTU"):
-               [(converters.m, 1.761e-1), u"h\xb7ft\xb2\xb7\xb0F/Btu", ''],
-               _(u"hour square meter deg C per kilocalorie"):
-               [(converters.m, 8.62e-1), u"h\xb7m\xb2\xb7\xb0C/kcal", ''],
-       },
-
-       _(u"Specific Heat"): {
-               ".base_unit": "joule per kilogram kelvin",
-               _(u"joule per kilogram kelvin"):
-               [(converters.m, 1.0), u"J/kg\xb7K", ''],
-               _(u"joule per kilogram deg C"):
-               [(converters.m, 1.0), u"J/kg\xb7\xb0C", ''],
-               _(u"kilocalories per kilogram deg C"):
-               [(converters.m, 4.18855e3), u"kcal/kg\xb7\xb0C", ''],
-               _(u"btu per pound deg F"):
-               [(converters.m, 4.1868e3), u"BTU/lb\xb7\xb0F", ''],
-       },
-
-       _(u"Fuel consumption"): {
-               ".base_unit": "miles per gallon (US)",
-               _(u"miles per gallon (US)"):
-               [(converters.m, 1.0), u"mpg (US)", ''],
-               _(u"gallons  (US) per 100 miles"):
-               [(converters.inv, 100.0), u'', ''],
-               _(u"miles per gallon (Imperial)"):
-               [(converters.m, 1.0/1.20095), u"mpg (Imperial)", ''],
-               _(u"gallons (Imperial) per 100 miles"):
-               [(converters.inv, 100.0/1.20095), u'', ''],
-               _(u"liters per 100 kilometer"):
-               [(converters.inv, 62.1371192237/0.264172052358), u'', ''],
-               _(u"kilometers per liter"):
-               [(converters.m, .621371192237/0.264172052358), u'', ''],
-       },
-
-       _(u"Torque"): {
-               ".base_unit": "newton meter",
-               _(u"newton meter"):
-               [(converters.m, 1.0), u"N\xb7m", _(u"The SI unit of force that causes rotation.")],
-               _(u"joules"):
-               [(converters.m, 1.0), u"j", ''],
-               _(u"kilo newton meter"):
-               [(converters.m, 1000.0), u"kN\xb7m", ''],
-               _(u"mega newton meter"):
-               [(converters.m, 1000000.0), u"MN\xb7m", ''],
-               _(u"milli newton meter"):
-               [(converters.m, 1.0e-3), u"mN\xb7m", ''],
-               _(u"micro newton meter"):
-               [(converters.m, 1.0e-6), u"\xb5N\xb7m", ''],
-               _(u"dyne centimeter"):
-               [(converters.m, 1.0e-7), u"dyne\xb7cm", ''],
-               _(u"kilogram meter"):
-               [(converters.m, 9.80665), u"kg\xb7m", ''],
-               _(u"centimeter gram"):
-               [(converters.m, 9.80665/100000.0), u"cm\xb7g", ''],
-               _(u"kip"):
-               [(converters.m, 113.0), u"kip", _(u"One thousand inch pounds.")],
-               _(u"foot pounds"):
-               [(converters.m, 1356.0/1000.0), u"lbf\xb7ft", ''],
-               _(u"foot ounce"):
-               [(converters.m, 1356.0/1000.0/16.0), u"oz\xb7ft", ''],
-               _(u"meter kilopond"):
-               [(converters.m, 9.80665), u"mkp", ''],
-               _(u"newton centimeter"):
-               [(converters.m, .01), u"N\xb7cm", ''],
-               _(u"inch ounces"):
-               [(converters.m, 113.0/16.0*.001), u"in\xb7oz", ''],
-               _(u"inch pounds"):
-               [(converters.m, 113.0*.001), u"in\xb7lb", ''],
-               _(u"foot poundal"):
-               [(converters.m, 1.0/23.7303605), u'', ''],
-       },
-
-       _(u"Current Loop"): {".base_unit": "6400 to 32000",
-                       _(u"6400 to 32000"):
-                       [(converters.m, 1.0), u"counts", _(u"Many PLCs must scale the 4 to 20mA signal to an integer, this is commonly a value from 6400 to 32000, ")],
-                       _(u"4 to 20mA"):
-                       [(converters.m, 1600.0), u"mA", _(u"This range of current is commonly used in instrumentation. 0mA is an indication of a broken transmitter loop.")],
-                       _(u"V across 250 ohm"):
-                       [(converters.m, 6400.0), u"V", _(u"A common resistance for current loop instrumentation is 250 ohms. A voltage will be developed across this resistor, that voltage can be used to test the current loop.")],
-                       _(u"percent"):
-                       [(converters.gof, (((32000.0-6400.0)/100.0), 6400.0)), u"%", _(u"This is a percentage of the 4 to 20mA signal.")],
-                       },
-
-       _(u"Currency (UK)"): {
-               ".base_unit": "pound",
-               _(u"pound | quid | soverign"):
-               [(converters.m, 1.0), "", """The base monetary unit in UK."""],
-               _(u"soverign"):
-               [(converters.m, 1.0), "", """One pound."""],
-               _(u"quid"):
-               [(converters.m, 1.0), "", """One pound."""],
-               _(u"fiver"):
-               [(converters.m, 5*1.0), "", """Equal to five pounds."""],
-               _(u"tenner"):
-               [(converters.m, 10*1.0), "", """Equal to Ten pounds."""],
-               _(u"crown"):
-               [(converters.m, 5*1.0/20), "", """Equal to five shillings."""],
-               _(u"shilling"):
-               [(converters.m, 1.0/20), "", """Equal to one twentieth of a pound."""],
-               _(u"bob | shilling"):
-               [(converters.m, 1.0/20), "", """Equal to one twentieth of a pound."""],
-               _(u"penny | pence"):
-               [(converters.m, 1.0/100), "", """Equal to 1/100 of a pound."""],
-               _(u"penny | pence (old)"):
-               [(converters.m, 1.0/240), "", """Equal to 1/240 of a pound.  February 15, 1971 the English coinage system was changed to a decimal system and the old penny ceased to be legal tender August 31, 1971."""],
-               _(u"tuppence(old)"):
-               [(converters.m, 2*1.0/240), "", """Equal to two old pennies. February 15, 1971 the English coinage system was changed to a decimal system."""],
-               _(u"threepence (old)"):
-               [(converters.m, 3*1.0/240), "", """Equal to three old pence.  The threepence was demonitized August 31, 1971."""],
-               _(u"halfpenny | hapenny (old)"):
-               [(converters.m, 1.0/240/2), "", """The old halfpenny was demonetized on August 31, 1969."""],
-               _(u"guinea"):
-               [(converters.m, 21*1.0/20), "", """While the term is still used, the coins are no longer in use.  Webster's Revised Unabridged Dictionary (1913) - A gold coin of England current for twenty-one shillings sterling, ... but not coined since the issue of sovereigns in 1817."""],
-               _(u"farthing"):
-               [(converters.m, 1.0/240/4), "", """"""],
-               _(u"florin (or two bob bit)"):
-               [(converters.m, 2*1.0/20), "", """"""],
-               _(u"half crown (old)"):
-               [(converters.m, (2*1.0/20)+(6*1.0/240)), "", """ The half-crown was demonetized on 1st January 1970."""],
-               _(u"sixpence"):
-               [(converters.m, 6*1.0/240), "", """Equal to six old pence.  February 15, 1971 the English coinage system was changed to a decimal system ."""],
-               _(u"tanner | sixpence"):
-               [(converters.m, 6*1.0/240), "", """Equal to six old pence.  February 15, 1971 the English coinage system was changed to a decimal system ."""],
-               _(u"mark (old British mark)"):
-               [(converters.m, 160*1.0/240), "", """"""],
-               _(u"groat"):
-               [(converters.m, 4*1.0/240), "", """Equal to four old pence"""],
-               _(u"pony"):
-               [(converters.m, 25.0), "", """Equal to twenty five pounds sterling"""],
-       },
-
-       _(u"Radioactivity"): {
-               ".base_unit": "becquerel",
-               _(u"becquerel"):
-               [(converters.m, 1.0), u"Bq", 'The SI derived unit of radioactivity.'],
-               _(u"curie"):
-               [(converters.m, 3.7e10), u"Ci", 'Roughly the activity of 1 gram of the radium isotope 226 Ra.'],
-               _(u"pico curie"):
-               [(converters.m, 3.7e-2), u"pCi", ''],
-       },
-
-       _(u"Radiation dose"): {
-               ".base_unit": "Sievert",
-               _(u"Sievert"):
-               [(converters.m, 1.0), u"Sv", ''],
-               _(u"milli Sievert"):
-               [(converters.m, 1.0e-3), u"mSv", ''],
-               _(u"micro Sievert"):
-               [(converters.m, 1.0e-6), u"\xb5Sv", ''],
-               _(u"rem"):
-               [(converters.m, 0.01), u"rem", ''],
-               _(u"milli rem"):
-               [(converters.m, 1.0e-5), u"mrem", ''],
-               _(u"roentgen"):
-               [(converters.m, 1/1.07185e2), u"R", ''],
-       },
-}
-
-
-UNIT_CATEGORIES = sorted(UNIT_DESCRIPTIONS.iterkeys())
-
-
-def get_units(categoryName):
-       units = sorted(UNIT_DESCRIPTIONS[categoryName].iterkeys())
-
-       # do not display .base_unit description key
-       del units[0]
-
-       return units
-
-
-def get_units_from_category(category):
-       units = sorted(category.iterkeys())
-
-       # do not display .base_unit description key
-       del units[0]
-
-       return units
-
-
-def get_base_unit(categoryName):
-       return UNIT_DESCRIPTIONS[categoryName][".base_unit"]
-
-
-def get_base_unit_from_category(category):
-       return category[".base_unit"]
-
-
-future_dic = {
-       _(u"Wire Gauge"): {
-               ".base_unit": "circular mils",
-               _(u"circular mil"):
-               [(converters.m, 1.0), u"CM", ''],
-               _(u"square mil"):
-               [(converters.m, 1.0), u'', ''],
-               _(u"square milimetres"):
-               [(converters.m, 1.0), u"mm\xb2", ''],
-               _(u"AWG"):
-               [(converters.m, 1.0), "AWG", _(u"American Wire Gauge")],
-               _(u"Diameter mils"):
-               [(converters.m, 1.0), "mil", ''],
-               _(u"Diameter inches"):
-               [(converters.m, 1.0), "in", ''],
-               _(u"Diameter mm"):
-               [(converters.m, 1.0), "mm", ''],
-               _(u"Diameter m"):
-               [(converters.m, 1.0), "m", ''],
-               _(u"Ampacity Cu"):
-               [(converters.m, 1.0), "A", _(u"Copper wire ampacity")],
-               _(u"Ampacity Al"):
-               [(converters.m, 1.0), "A", _(u"Aluminum wire ampacity")],
-               _(u"Resistance of Cu wire at xxdegC"):
-               [(converters.m, 1.0), "ohms/kft", _(u"Copper wire resistance.")],
-               _(u"Resistance of Cu wire at xxdegC"):
-               [(converters.m, 1.0), "ohms/ft", _(u"Copper wire resistance.")],
-               _(u"Resistance of Cu wire at xxdegC"):
-               [(converters.m, 1.0), "ohms/m", _(u"Copper wire resistance.")],
-               _(u"Resistance of Cu wire at xxdegC"):
-               [(converters.m, 1.0), "ohms/km", _(u"Copper wire resistance.")],
-               _(u"Resistance of Al wire at xxdegC"):
-               [(converters.m, 1.0), "ohms/kft", _(u"Copper wire resistance.")],
-               _(u"Resistance of Al wire at xxdegC"):
-               [(converters.m, 1.0), "ohms/ft", _(u"Copper wire resistance.")],
-               _(u"Resistance of Al wire at xxdegC"):
-               [(converters.m, 1.0), "ohms/m", _(u"Copper wire resistance.")],
-               _(u"Resistance of Al wire at xxdegC"):
-               [(converters.m, 1.0), "ohms/km", _(u"Copper wire resistance.")],
-               _(u"Length per Weight Cu Wire"):
-               [(converters.m, 1.0), "ft/lb (Cu)", _(u"Length per weight Copper Wire.")],
-               _(u"Length per Weight Al Wire"):
-               [(converters.m, 1.0), "ft/lb (Al)", _(u"Length per weight Aluminum Wire.")],
-               _(u"Length per resistance Cu Wire"):
-               [(converters.m, 1.0), "ft/ohm (Cu)", _(u"Length per resistance Copper Wire.")],
-               _(u"Length per resistance Al Wire"):
-               [(converters.m, 1.0), "ft/ohm (Al)", _(u"Length per resistance Aluminum Wire.")],
-               _(u"Weight Cu wire"):
-               [(converters.m, 1.0), "kg/km (Cu)", _(u"Copper wire weight.")],
-               _(u"Weight Al wire"):
-               [(converters.m, 1.0), "kg/km (Al)", _(u"Aluminum wire weight.")],
-               _(u"Weight Cu wire"):
-               [(converters.m, 1.0), "lb/kft (Cu)", _(u"Copper wire weight in pounds per 1000 feet.")],
-               _(u"Weight Al wire"):
-               [(converters.m, 1.0), "lb/kft (Al)", _(u"Aluminum wire weight in pounds per 1000 feet.")],
-               _(u"Tensile strength"):
-               [(converters.m, 1.0), "kgf", _(u"Aluminum wire weight.")],
-               _(u"Turns per inch"):
-               [(converters.m, 1.0), "TPI", _(u"Turns per inch of bare wire, useful for winding coils. This value is approximate and will be reduced with insulated wire")],
-       },
-}
diff --git a/src/util/__init__.py b/src/util/__init__.py
deleted file mode 100644 (file)
index 4265cc3..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#!/usr/bin/env python
diff --git a/src/util/algorithms.py b/src/util/algorithms.py
deleted file mode 100644 (file)
index e94fb61..0000000
+++ /dev/null
@@ -1,664 +0,0 @@
-#!/usr/bin/env python
-
-"""
-@note Source http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66448
-"""
-
-import itertools
-import functools
-import datetime
-import types
-import array
-import random
-
-
-def ordered_itr(collection):
-       """
-       >>> [v for v in ordered_itr({"a": 1, "b": 2})]
-       [('a', 1), ('b', 2)]
-       >>> [v for v in ordered_itr([3, 1, 10, -20])]
-       [-20, 1, 3, 10]
-       """
-       if isinstance(collection, types.DictType):
-               keys = list(collection.iterkeys())
-               keys.sort()
-               for key in keys:
-                       yield key, collection[key]
-       else:
-               values = list(collection)
-               values.sort()
-               for value in values:
-                       yield value
-
-
-def itercat(*iterators):
-       """
-       Concatenate several iterators into one.
-
-       >>> [v for v in itercat([1, 2, 3], [4, 1, 3])]
-       [1, 2, 3, 4, 1, 3]
-       """
-       for i in iterators:
-               for x in i:
-                       yield x
-
-
-def product(*args, **kwds):
-       # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
-       # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
-       pools = map(tuple, args) * kwds.get('repeat', 1)
-       result = [[]]
-       for pool in pools:
-               result = [x+[y] for x in result for y in pool]
-       for prod in result:
-               yield tuple(prod)
-
-
-def iterwhile(func, iterator):
-       """
-       Iterate for as long as func(value) returns true.
-       >>> through = lambda b: b
-       >>> [v for v in iterwhile(through, [True, True, False])]
-       [True, True]
-       """
-       iterator = iter(iterator)
-       while 1:
-               next = iterator.next()
-               if not func(next):
-                       raise StopIteration
-               yield next
-
-
-def iterfirst(iterator, count=1):
-       """
-       Iterate through 'count' first values.
-
-       >>> [v for v in iterfirst([1, 2, 3, 4, 5], 3)]
-       [1, 2, 3]
-       """
-       iterator = iter(iterator)
-       for i in xrange(count):
-               yield iterator.next()
-
-
-def iterstep(iterator, n):
-       """
-       Iterate every nth value.
-
-       >>> [v for v in iterstep([1, 2, 3, 4, 5], 1)]
-       [1, 2, 3, 4, 5]
-       >>> [v for v in iterstep([1, 2, 3, 4, 5], 2)]
-       [1, 3, 5]
-       >>> [v for v in iterstep([1, 2, 3, 4, 5], 3)]
-       [1, 4]
-       """
-       iterator = iter(iterator)
-       while True:
-               yield iterator.next()
-               # skip n-1 values
-               for dummy in xrange(n-1):
-                       iterator.next()
-
-
-def itergroup(iterator, count, padValue = None):
-       """
-       Iterate in groups of 'count' values. If there
-       aren't enough values, the last result is padded with
-       None.
-
-       >>> for val in itergroup([1, 2, 3, 4, 5, 6], 3):
-       ...     print tuple(val)
-       (1, 2, 3)
-       (4, 5, 6)
-       >>> for val in itergroup([1, 2, 3, 4, 5, 6], 3):
-       ...     print list(val)
-       [1, 2, 3]
-       [4, 5, 6]
-       >>> for val in itergroup([1, 2, 3, 4, 5, 6, 7], 3):
-       ...     print tuple(val)
-       (1, 2, 3)
-       (4, 5, 6)
-       (7, None, None)
-       >>> for val in itergroup("123456", 3):
-       ...     print tuple(val)
-       ('1', '2', '3')
-       ('4', '5', '6')
-       >>> for val in itergroup("123456", 3):
-       ...     print repr("".join(val))
-       '123'
-       '456'
-       """
-       paddedIterator = itertools.chain(iterator, itertools.repeat(padValue, count-1))
-       nIterators = (paddedIterator, ) * count
-       return itertools.izip(*nIterators)
-
-
-def xzip(*iterators):
-       """Iterative version of builtin 'zip'."""
-       iterators = itertools.imap(iter, iterators)
-       while 1:
-               yield tuple([x.next() for x in iterators])
-
-
-def xmap(func, *iterators):
-       """Iterative version of builtin 'map'."""
-       iterators = itertools.imap(iter, iterators)
-       values_left = [1]
-
-       def values():
-               # Emulate map behaviour, i.e. shorter
-               # sequences are padded with None when
-               # they run out of values.
-               values_left[0] = 0
-               for i in range(len(iterators)):
-                       iterator = iterators[i]
-                       if iterator is None:
-                               yield None
-                       else:
-                               try:
-                                       yield iterator.next()
-                                       values_left[0] = 1
-                               except StopIteration:
-                                       iterators[i] = None
-                                       yield None
-       while 1:
-               args = tuple(values())
-               if not values_left[0]:
-                       raise StopIteration
-               yield func(*args)
-
-
-def xfilter(func, iterator):
-       """Iterative version of builtin 'filter'."""
-       iterator = iter(iterator)
-       while 1:
-               next = iterator.next()
-               if func(next):
-                       yield next
-
-
-def xreduce(func, iterator, default=None):
-       """Iterative version of builtin 'reduce'."""
-       iterator = iter(iterator)
-       try:
-               prev = iterator.next()
-       except StopIteration:
-               return default
-       single = 1
-       for next in iterator:
-               single = 0
-               prev = func(prev, next)
-       if single:
-               return func(prev, default)
-       return prev
-
-
-def daterange(begin, end, delta = datetime.timedelta(1)):
-       """
-       Form a range of dates and iterate over them.
-
-       Arguments:
-       begin -- a date (or datetime) object; the beginning of the range.
-       end   -- a date (or datetime) object; the end of the range.
-       delta -- (optional) a datetime.timedelta object; how much to step each iteration.
-                       Default step is 1 day.
-
-       Usage:
-       """
-       if not isinstance(delta, datetime.timedelta):
-               delta = datetime.timedelta(delta)
-
-       ZERO = datetime.timedelta(0)
-
-       if begin < end:
-               if delta <= ZERO:
-                       raise StopIteration
-               test = end.__gt__
-       else:
-               if delta >= ZERO:
-                       raise StopIteration
-               test = end.__lt__
-
-       while test(begin):
-               yield begin
-               begin += delta
-
-
-class LazyList(object):
-       """
-       A Sequence whose values are computed lazily by an iterator.
-
-       Module for the creation and use of iterator-based lazy lists.
-       this module defines a class LazyList which can be used to represent sequences
-       of values generated lazily. One can also create recursively defined lazy lists
-       that generate their values based on ones previously generated.
-
-       Backport to python 2.5 by Michael Pust
-       """
-
-       __author__ = 'Dan Spitz'
-
-       def __init__(self, iterable):
-               self._exhausted = False
-               self._iterator = iter(iterable)
-               self._data = []
-
-       def __len__(self):
-               """Get the length of a LazyList's computed data."""
-               return len(self._data)
-
-       def __getitem__(self, i):
-               """Get an item from a LazyList.
-               i should be a positive integer or a slice object."""
-               if isinstance(i, int):
-                       #index has not yet been yielded by iterator (or iterator exhausted
-                       #before reaching that index)
-                       if i >= len(self):
-                               self.exhaust(i)
-                       elif i < 0:
-                               raise ValueError('cannot index LazyList with negative number')
-                       return self._data[i]
-
-               #LazyList slices are iterators over a portion of the list.
-               elif isinstance(i, slice):
-                       start, stop, step = i.start, i.stop, i.step
-                       if any(x is not None and x < 0 for x in (start, stop, step)):
-                               raise ValueError('cannot index or step through a LazyList with'
-                                                               'a negative number')
-                       #set start and step to their integer defaults if they are None.
-                       if start is None:
-                               start = 0
-                       if step is None:
-                               step = 1
-
-                       def LazyListIterator():
-                               count = start
-                               predicate = (
-                                       (lambda: True)
-                                       if stop is None
-                                       else (lambda: count < stop)
-                               )
-                               while predicate():
-                                       try:
-                                               yield self[count]
-                                       #slices can go out of actual index range without raising an
-                                       #error
-                                       except IndexError:
-                                               break
-                                       count += step
-                       return LazyListIterator()
-
-               raise TypeError('i must be an integer or slice')
-
-       def __iter__(self):
-               """return an iterator over each value in the sequence,
-               whether it has been computed yet or not."""
-               return self[:]
-
-       def computed(self):
-               """Return an iterator over the values in a LazyList that have
-               already been computed."""
-               return self[:len(self)]
-
-       def exhaust(self, index = None):
-               """Exhaust the iterator generating this LazyList's values.
-               if index is None, this will exhaust the iterator completely.
-               Otherwise, it will iterate over the iterator until either the list
-               has a value for index or the iterator is exhausted.
-               """
-               if self._exhausted:
-                       return
-               if index is None:
-                       ind_range = itertools.count(len(self))
-               else:
-                       ind_range = range(len(self), index + 1)
-
-               for ind in ind_range:
-                       try:
-                               self._data.append(self._iterator.next())
-                       except StopIteration: #iterator is fully exhausted
-                               self._exhausted = True
-                               break
-
-
-class RecursiveLazyList(LazyList):
-
-       def __init__(self, prod, *args, **kwds):
-               super(RecursiveLazyList, self).__init__(prod(self, *args, **kwds))
-
-
-class RecursiveLazyListFactory:
-
-       def __init__(self, producer):
-               self._gen = producer
-
-       def __call__(self, *a, **kw):
-               return RecursiveLazyList(self._gen, *a, **kw)
-
-
-def lazylist(gen):
-       """
-       Decorator for creating a RecursiveLazyList subclass.
-       This should decorate a generator function taking the LazyList object as its
-       first argument which yields the contents of the list in order.
-
-       >>> #fibonnacci sequence in a lazy list.
-       >>> @lazylist
-       ... def fibgen(lst):
-       ...     yield 0
-       ...     yield 1
-       ...     for a, b in itertools.izip(lst, lst[1:]):
-       ...             yield a + b
-       ...
-       >>> #now fibs can be indexed or iterated over as if it were an infinitely long list containing the fibonnaci sequence
-       >>> fibs = fibgen()
-       >>>
-       >>> #prime numbers in a lazy list.
-       >>> @lazylist
-       ... def primegen(lst):
-       ...     yield 2
-       ...     for candidate in itertools.count(3): #start at next number after 2
-       ...             #if candidate is not divisible by any smaller prime numbers,
-       ...             #it is a prime.
-       ...             if all(candidate % p for p in lst.computed()):
-       ...                     yield candidate
-       ...
-       >>> #same for primes- treat it like an infinitely long list containing all prime numbers.
-       >>> primes = primegen()
-       >>> print fibs[0], fibs[1], fibs[2], primes[0], primes[1], primes[2]
-       0 1 1 2 3 5
-       >>> print list(fibs[:10]), list(primes[:10])
-       [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
-       """
-       return RecursiveLazyListFactory(gen)
-
-
-def map_func(f):
-       """
-       >>> import misc
-       >>> misc.validate_decorator(map_func)
-       """
-
-       @functools.wraps(f)
-       def wrapper(*args):
-               result = itertools.imap(f, args)
-               return result
-       return wrapper
-
-
-def reduce_func(function):
-       """
-       >>> import misc
-       >>> misc.validate_decorator(reduce_func(lambda x: x))
-       """
-
-       def decorator(f):
-
-               @functools.wraps(f)
-               def wrapper(*args):
-                       result = reduce(function, f(args))
-                       return result
-               return wrapper
-       return decorator
-
-
-def any_(iterable):
-       """
-       @note Python Version <2.5
-
-       >>> any_([True, True])
-       True
-       >>> any_([True, False])
-       True
-       >>> any_([False, False])
-       False
-       """
-
-       for element in iterable:
-               if element:
-                       return True
-       return False
-
-
-def all_(iterable):
-       """
-       @note Python Version <2.5
-
-       >>> all_([True, True])
-       True
-       >>> all_([True, False])
-       False
-       >>> all_([False, False])
-       False
-       """
-
-       for element in iterable:
-               if not element:
-                       return False
-       return True
-
-
-def for_every(pred, seq):
-       """
-       for_every takes a one argument predicate function and a sequence.
-       @param pred The predicate function should return true or false.
-       @returns true if every element in seq returns true for predicate, else returns false.
-
-       >>> for_every (lambda c: c > 5,(6,7,8,9))
-       True
-
-       @author Source:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52907
-       """
-
-       for i in seq:
-               if not pred(i):
-                       return False
-       return True
-
-
-def there_exists(pred, seq):
-       """
-       there_exists takes a one argument predicate     function and a sequence.
-       @param pred The predicate function should return true or false.
-       @returns true if any element in seq returns true for predicate, else returns false.
-
-       >>> there_exists (lambda c: c > 5,(6,7,8,9))
-       True
-
-       @author Source:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52907
-       """
-
-       for i in seq:
-               if pred(i):
-                       return True
-       return False
-
-
-def func_repeat(quantity, func, *args, **kwd):
-       """
-       Meant to be in connection with "reduce"
-       """
-       for i in xrange(quantity):
-               yield func(*args, **kwd)
-
-
-def function_map(preds, item):
-       """
-       Meant to be in connection with "reduce"
-       """
-       results = (pred(item) for pred in preds)
-
-       return results
-
-
-def functional_if(combiner, preds, item):
-       """
-       Combines the result of a list of predicates applied to item according to combiner
-
-       @see any, every for example combiners
-       """
-       pass_bool = lambda b: b
-
-       bool_results = function_map(preds, item)
-       return combiner(pass_bool, bool_results)
-
-
-def pushback_itr(itr):
-       """
-       >>> list(pushback_itr(xrange(5)))
-       [0, 1, 2, 3, 4]
-       >>>
-       >>> first = True
-       >>> itr = pushback_itr(xrange(5))
-       >>> for i in itr:
-       ...     print i
-       ...     if first and i == 2:
-       ...             first = False
-       ...             print itr.send(i)
-       0
-       1
-       2
-       None
-       2
-       3
-       4
-       >>>
-       >>> first = True
-       >>> itr = pushback_itr(xrange(5))
-       >>> for i in itr:
-       ...     print i
-       ...     if first and i == 2:
-       ...             first = False
-       ...             print itr.send(i)
-       ...             print itr.send(i)
-       0
-       1
-       2
-       None
-       None
-       2
-       2
-       3
-       4
-       >>>
-       >>> itr = pushback_itr(xrange(5))
-       >>> print itr.next()
-       0
-       >>> print itr.next()
-       1
-       >>> print itr.send(10)
-       None
-       >>> print itr.next()
-       10
-       >>> print itr.next()
-       2
-       >>> print itr.send(20)
-       None
-       >>> print itr.send(30)
-       None
-       >>> print itr.send(40)
-       None
-       >>> print itr.next()
-       40
-       >>> print itr.next()
-       30
-       >>> print itr.send(50)
-       None
-       >>> print itr.next()
-       50
-       >>> print itr.next()
-       20
-       >>> print itr.next()
-       3
-       >>> print itr.next()
-       4
-       """
-       for item in itr:
-               maybePushedBack = yield item
-               queue = []
-               while queue or maybePushedBack is not None:
-                       if maybePushedBack is not None:
-                               queue.append(maybePushedBack)
-                               maybePushedBack = yield None
-                       else:
-                               item = queue.pop()
-                               maybePushedBack = yield item
-
-
-def itr_available(queue, initiallyBlock = False):
-       if initiallyBlock:
-               yield queue.get()
-       while not queue.empty():
-               yield queue.get_nowait()
-
-
-class BloomFilter(object):
-       """
-       http://en.wikipedia.org/wiki/Bloom_filter
-       Sources:
-       http://code.activestate.com/recipes/577684-bloom-filter/
-       http://code.activestate.com/recipes/577686-bloom-filter/
-
-       >>> from random import sample
-       >>> from string import ascii_letters
-       >>> states = '''Alabama Alaska Arizona Arkansas California Colorado Connecticut
-       ... Delaware Florida Georgia Hawaii Idaho Illinois Indiana Iowa Kansas
-       ... Kentucky Louisiana Maine Maryland Massachusetts Michigan Minnesota
-       ... Mississippi Missouri Montana Nebraska Nevada NewHampshire NewJersey
-       ... NewMexico NewYork NorthCarolina NorthDakota Ohio Oklahoma Oregon
-       ... Pennsylvania RhodeIsland SouthCarolina SouthDakota Tennessee Texas Utah
-       ... Vermont Virginia Washington WestVirginia Wisconsin Wyoming'''.split()
-       >>> bf = BloomFilter(num_bits=1000, num_probes=14)
-       >>> for state in states:
-       ...     bf.add(state)
-       >>> numStatesFound = sum(state in bf for state in states)
-       >>> numStatesFound, len(states)
-       (50, 50)
-       >>> trials = 100
-       >>> numGarbageFound = sum(''.join(sample(ascii_letters, 5)) in bf for i in range(trials))
-       >>> numGarbageFound, trials
-       (0, 100)
-       """
-
-       def __init__(self, num_bits, num_probes):
-               num_words = (num_bits + 31) // 32
-               self._arr = array.array('B', [0]) * num_words
-               self._num_probes = num_probes
-
-       def add(self, key):
-               for i, mask in self._get_probes(key):
-                       self._arr[i] |= mask
-
-       def union(self, bfilter):
-               if self._match_template(bfilter):
-                       for i, b in enumerate(bfilter._arr):
-                               self._arr[i] |= b
-               else:
-                       # Union b/w two unrelated bloom filter raises this
-                       raise ValueError("Mismatched bloom filters")
-
-       def intersection(self, bfilter):
-               if self._match_template(bfilter):
-                       for i, b in enumerate(bfilter._arr):
-                               self._arr[i] &= b
-               else:
-                       # Intersection b/w two unrelated bloom filter raises this
-                       raise ValueError("Mismatched bloom filters")
-
-       def __contains__(self, key):
-               return all(self._arr[i] & mask for i, mask in self._get_probes(key))
-
-       def _match_template(self, bfilter):
-               return self.num_bits == bfilter.num_bits and self.num_probes == bfilter.num_probes
-
-       def _get_probes(self, key):
-               hasher = random.Random(key).randrange
-               for _ in range(self._num_probes):
-                       array_index = hasher(len(self._arr))
-                       bit_index = hasher(32)
-                       yield array_index, 1 << bit_index
-
-
-if __name__ == "__main__":
-       import doctest
-       print doctest.testmod()
diff --git a/src/util/concurrent.py b/src/util/concurrent.py
deleted file mode 100644 (file)
index f5f6e1d..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import with_statement
-
-import os
-import errno
-import time
-import functools
-import contextlib
-import logging
-
-import misc
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class AsyncTaskQueue(object):
-
-       def __init__(self, taskPool):
-               self._asyncs = []
-               self._taskPool = taskPool
-
-       def add_async(self, func):
-               self.flush()
-               a = AsyncGeneratorTask(self._taskPool, func)
-               self._asyncs.append(a)
-               return a
-
-       def flush(self):
-               self._asyncs = [a for a in self._asyncs if not a.isDone]
-
-
-class AsyncGeneratorTask(object):
-
-       def __init__(self, pool, func):
-               self._pool = pool
-               self._func = func
-               self._run = None
-               self._isDone = False
-
-       @property
-       def isDone(self):
-               return self._isDone
-
-       def start(self, *args, **kwds):
-               assert self._run is None, "Task already started"
-               self._run = self._func(*args, **kwds)
-               trampoline, args, kwds = self._run.send(None) # priming the function
-               self._pool.add_task(
-                       trampoline,
-                       args,
-                       kwds,
-                       self.on_success,
-                       self.on_error,
-               )
-
-       @misc.log_exception(_moduleLogger)
-       def on_success(self, result):
-               _moduleLogger.debug("Processing success for: %r", self._func)
-               try:
-                       trampoline, args, kwds = self._run.send(result)
-               except StopIteration, e:
-                       self._isDone = True
-               else:
-                       self._pool.add_task(
-                               trampoline,
-                               args,
-                               kwds,
-                               self.on_success,
-                               self.on_error,
-                       )
-
-       @misc.log_exception(_moduleLogger)
-       def on_error(self, error):
-               _moduleLogger.debug("Processing error for: %r", self._func)
-               try:
-                       trampoline, args, kwds = self._run.throw(error)
-               except StopIteration, e:
-                       self._isDone = True
-               else:
-                       self._pool.add_task(
-                               trampoline,
-                               args,
-                               kwds,
-                               self.on_success,
-                               self.on_error,
-                       )
-
-       def __repr__(self):
-               return "<async %s at 0x%x>" % (self._func.__name__, id(self))
-
-       def __hash__(self):
-               return hash(self._func)
-
-       def __eq__(self, other):
-               return self._func == other._func
-
-       def __ne__(self, other):
-               return self._func != other._func
-
-
-def synchronized(lock):
-       """
-       Synchronization decorator.
-
-       >>> import misc
-       >>> misc.validate_decorator(synchronized(object()))
-       """
-
-       def wrap(f):
-
-               @functools.wraps(f)
-               def newFunction(*args, **kw):
-                       lock.acquire()
-                       try:
-                               return f(*args, **kw)
-                       finally:
-                               lock.release()
-               return newFunction
-       return wrap
-
-
-@contextlib.contextmanager
-def qlock(queue, gblock = True, gtimeout = None, pblock = True, ptimeout = None):
-       """
-       Locking with a queue, good for when you want to lock an item passed around
-
-       >>> import Queue
-       >>> item = 5
-       >>> lock = Queue.Queue()
-       >>> lock.put(item)
-       >>> with qlock(lock) as i:
-       ...     print i
-       5
-       """
-       item = queue.get(gblock, gtimeout)
-       try:
-               yield item
-       finally:
-               queue.put(item, pblock, ptimeout)
-
-
-@contextlib.contextmanager
-def flock(path, timeout=-1):
-       WAIT_FOREVER = -1
-       DELAY = 0.1
-       timeSpent = 0
-
-       acquired = False
-
-       while timeSpent <= timeout or timeout == WAIT_FOREVER:
-               try:
-                       fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
-                       acquired = True
-                       break
-               except OSError, e:
-                       if e.errno != errno.EEXIST:
-                               raise
-               time.sleep(DELAY)
-               timeSpent += DELAY
-
-       assert acquired, "Failed to grab file-lock %s within timeout %d" % (path, timeout)
-
-       try:
-               yield fd
-       finally:
-               os.unlink(path)
diff --git a/src/util/coroutines.py b/src/util/coroutines.py
deleted file mode 100755 (executable)
index b1e539e..0000000
+++ /dev/null
@@ -1,623 +0,0 @@
-#!/usr/bin/env python\r
-\r
-"""\r
-Uses for generators\r
-* Pull pipelining (iterators)\r
-* Push pipelining (coroutines)\r
-* State machines (coroutines)\r
-* "Cooperative multitasking" (coroutines)\r
-* Algorithm -> Object transform for cohesiveness (for example context managers) (coroutines)\r
-\r
-Design considerations\r
-* When should a stage pass on exceptions or have it thrown within it?\r
-* When should a stage pass on GeneratorExits?\r
-* Is there a way to either turn a push generator into a iterator or to use\r
-       comprehensions syntax for push generators (I doubt it)\r
-* When should the stage try and send data in both directions\r
-* Since pull generators (generators), push generators (coroutines), subroutines, and coroutines are all coroutines, maybe we should rename the push generators to not confuse them, like signals/slots? and then refer to two-way generators as coroutines\r
-** If so, make s* and co* implementation of functions\r
-"""\r
-\r
-import threading\r
-import Queue\r
-import pickle\r
-import functools\r
-import itertools\r
-import xml.sax\r
-import xml.parsers.expat\r
-\r
-\r
-def autostart(func):\r
-       """\r
-       >>> @autostart\r
-       ... def grep_sink(pattern):\r
-       ...     print "Looking for %s" % pattern\r
-       ...     while True:\r
-       ...             line = yield\r
-       ...             if pattern in line:\r
-       ...                     print line,\r
-       >>> g = grep_sink("python")\r
-       Looking for python\r
-       >>> g.send("Yeah but no but yeah but no")\r
-       >>> g.send("A series of tubes")\r
-       >>> g.send("python generators rock!")\r
-       python generators rock!\r
-       >>> g.close()\r
-       """\r
-\r
-       @functools.wraps(func)\r
-       def start(*args, **kwargs):\r
-               cr = func(*args, **kwargs)\r
-               cr.next()\r
-               return cr\r
-\r
-       return start\r
-\r
-\r
-@autostart\r
-def printer_sink(format = "%s"):\r
-       """\r
-       >>> pr = printer_sink("%r")\r
-       >>> pr.send("Hello")\r
-       'Hello'\r
-       >>> pr.send("5")\r
-       '5'\r
-       >>> pr.send(5)\r
-       5\r
-       >>> p = printer_sink()\r
-       >>> p.send("Hello")\r
-       Hello\r
-       >>> p.send("World")\r
-       World\r
-       >>> # p.throw(RuntimeError, "Goodbye")\r
-       >>> # p.send("Meh")\r
-       >>> # p.close()\r
-       """\r
-       while True:\r
-               item = yield\r
-               print format % (item, )\r
-\r
-\r
-@autostart\r
-def null_sink():\r
-       """\r
-       Good for uses like with cochain to pick up any slack\r
-       """\r
-       while True:\r
-               item = yield\r
-\r
-\r
-def itr_source(itr, target):\r
-       """\r
-       >>> itr_source(xrange(2), printer_sink())\r
-       0\r
-       1\r
-       """\r
-       for item in itr:\r
-               target.send(item)\r
-\r
-\r
-@autostart\r
-def cofilter(predicate, target):\r
-       """\r
-       >>> p = printer_sink()\r
-       >>> cf = cofilter(None, p)\r
-       >>> cf.send("")\r
-       >>> cf.send("Hello")\r
-       Hello\r
-       >>> cf.send([])\r
-       >>> cf.send([1, 2])\r
-       [1, 2]\r
-       >>> cf.send(False)\r
-       >>> cf.send(True)\r
-       True\r
-       >>> cf.send(0)\r
-       >>> cf.send(1)\r
-       1\r
-       >>> # cf.throw(RuntimeError, "Goodbye")\r
-       >>> # cf.send(False)\r
-       >>> # cf.send(True)\r
-       >>> # cf.close()\r
-       """\r
-       if predicate is None:\r
-               predicate = bool\r
-\r
-       while True:\r
-               try:\r
-                       item = yield\r
-                       if predicate(item):\r
-                               target.send(item)\r
-               except StandardError, e:\r
-                       target.throw(e.__class__, e.message)\r
-\r
-\r
-@autostart\r
-def comap(function, target):\r
-       """\r
-       >>> p = printer_sink()\r
-       >>> cm = comap(lambda x: x+1, p)\r
-       >>> cm.send(0)\r
-       1\r
-       >>> cm.send(1.0)\r
-       2.0\r
-       >>> cm.send(-2)\r
-       -1\r
-       >>> # cm.throw(RuntimeError, "Goodbye")\r
-       >>> # cm.send(0)\r
-       >>> # cm.send(1.0)\r
-       >>> # cm.close()\r
-       """\r
-       while True:\r
-               try:\r
-                       item = yield\r
-                       mappedItem = function(item)\r
-                       target.send(mappedItem)\r
-               except StandardError, e:\r
-                       target.throw(e.__class__, e.message)\r
-\r
-\r
-def func_sink(function):\r
-       return comap(function, null_sink())\r
-\r
-\r
-def expand_positional(function):\r
-\r
-       @functools.wraps(function)\r
-       def expander(item):\r
-               return function(*item)\r
-\r
-       return expander\r
-\r
-\r
-@autostart\r
-def append_sink(l):\r
-       """\r
-       >>> l = []\r
-       >>> apps = append_sink(l)\r
-       >>> apps.send(1)\r
-       >>> apps.send(2)\r
-       >>> apps.send(3)\r
-       >>> print l\r
-       [1, 2, 3]\r
-       """\r
-       while True:\r
-               item = yield\r
-               l.append(item)\r
-\r
-\r
-@autostart\r
-def last_n_sink(l, n = 1):\r
-       """\r
-       >>> l = []\r
-       >>> lns = last_n_sink(l)\r
-       >>> lns.send(1)\r
-       >>> lns.send(2)\r
-       >>> lns.send(3)\r
-       >>> print l\r
-       [3]\r
-       """\r
-       del l[:]\r
-       while True:\r
-               item = yield\r
-               extraCount = len(l) - n + 1\r
-               if 0 < extraCount:\r
-                       del l[0:extraCount]\r
-               l.append(item)\r
-\r
-\r
-@autostart\r
-def coreduce(target, function, initializer = None):\r
-       """\r
-       >>> reduceResult = []\r
-       >>> lns = last_n_sink(reduceResult)\r
-       >>> cr = coreduce(lns, lambda x, y: x + y, 0)\r
-       >>> cr.send(1)\r
-       >>> cr.send(2)\r
-       >>> cr.send(3)\r
-       >>> print reduceResult\r
-       [6]\r
-       >>> cr = coreduce(lns, lambda x, y: x + y)\r
-       >>> cr.send(1)\r
-       >>> cr.send(2)\r
-       >>> cr.send(3)\r
-       >>> print reduceResult\r
-       [6]\r
-       """\r
-       isFirst = True\r
-       cumulativeRef = initializer\r
-       while True:\r
-               item = yield\r
-               if isFirst and initializer is None:\r
-                       cumulativeRef = item\r
-               else:\r
-                       cumulativeRef = function(cumulativeRef, item)\r
-               target.send(cumulativeRef)\r
-               isFirst = False\r
-\r
-\r
-@autostart\r
-def cotee(targets):\r
-       """\r
-       Takes a sequence of coroutines and sends the received items to all of them\r
-\r
-       >>> ct = cotee((printer_sink("1 %s"), printer_sink("2 %s")))\r
-       >>> ct.send("Hello")\r
-       1 Hello\r
-       2 Hello\r
-       >>> ct.send("World")\r
-       1 World\r
-       2 World\r
-       >>> # ct.throw(RuntimeError, "Goodbye")\r
-       >>> # ct.send("Meh")\r
-       >>> # ct.close()\r
-       """\r
-       while True:\r
-               try:\r
-                       item = yield\r
-                       for target in targets:\r
-                               target.send(item)\r
-               except StandardError, e:\r
-                       for target in targets:\r
-                               target.throw(e.__class__, e.message)\r
-\r
-\r
-class CoTee(object):\r
-       """\r
-       >>> ct = CoTee()\r
-       >>> ct.register_sink(printer_sink("1 %s"))\r
-       >>> ct.register_sink(printer_sink("2 %s"))\r
-       >>> ct.stage.send("Hello")\r
-       1 Hello\r
-       2 Hello\r
-       >>> ct.stage.send("World")\r
-       1 World\r
-       2 World\r
-       >>> ct.register_sink(printer_sink("3 %s"))\r
-       >>> ct.stage.send("Foo")\r
-       1 Foo\r
-       2 Foo\r
-       3 Foo\r
-       >>> # ct.stage.throw(RuntimeError, "Goodbye")\r
-       >>> # ct.stage.send("Meh")\r
-       >>> # ct.stage.close()\r
-       """\r
-\r
-       def __init__(self):\r
-               self.stage = self._stage()\r
-               self._targets = []\r
-\r
-       def register_sink(self, sink):\r
-               self._targets.append(sink)\r
-\r
-       def unregister_sink(self, sink):\r
-               self._targets.remove(sink)\r
-\r
-       def restart(self):\r
-               self.stage = self._stage()\r
-\r
-       @autostart\r
-       def _stage(self):\r
-               while True:\r
-                       try:\r
-                               item = yield\r
-                               for target in self._targets:\r
-                                       target.send(item)\r
-                       except StandardError, e:\r
-                               for target in self._targets:\r
-                                       target.throw(e.__class__, e.message)\r
-\r
-\r
-def _flush_queue(queue):\r
-       while not queue.empty():\r
-               yield queue.get()\r
-\r
-\r
-@autostart\r
-def cocount(target, start = 0):\r
-       """\r
-       >>> cc = cocount(printer_sink("%s"))\r
-       >>> cc.send("a")\r
-       0\r
-       >>> cc.send(None)\r
-       1\r
-       >>> cc.send([])\r
-       2\r
-       >>> cc.send(0)\r
-       3\r
-       """\r
-       for i in itertools.count(start):\r
-               item = yield\r
-               target.send(i)\r
-\r
-\r
-@autostart\r
-def coenumerate(target, start = 0):\r
-       """\r
-       >>> ce = coenumerate(printer_sink("%r"))\r
-       >>> ce.send("a")\r
-       (0, 'a')\r
-       >>> ce.send(None)\r
-       (1, None)\r
-       >>> ce.send([])\r
-       (2, [])\r
-       >>> ce.send(0)\r
-       (3, 0)\r
-       """\r
-       for i in itertools.count(start):\r
-               item = yield\r
-               decoratedItem = i, item\r
-               target.send(decoratedItem)\r
-\r
-\r
-@autostart\r
-def corepeat(target, elem):\r
-       """\r
-       >>> cr = corepeat(printer_sink("%s"), "Hello World")\r
-       >>> cr.send("a")\r
-       Hello World\r
-       >>> cr.send(None)\r
-       Hello World\r
-       >>> cr.send([])\r
-       Hello World\r
-       >>> cr.send(0)\r
-       Hello World\r
-       """\r
-       while True:\r
-               item = yield\r
-               target.send(elem)\r
-\r
-\r
-@autostart\r
-def cointercept(target, elems):\r
-       """\r
-       >>> cr = cointercept(printer_sink("%s"), [1, 2, 3, 4])\r
-       >>> cr.send("a")\r
-       1\r
-       >>> cr.send(None)\r
-       2\r
-       >>> cr.send([])\r
-       3\r
-       >>> cr.send(0)\r
-       4\r
-       >>> cr.send("Bye")\r
-       Traceback (most recent call last):\r
-         File "/usr/lib/python2.5/doctest.py", line 1228, in __run\r
-           compileflags, 1) in test.globs\r
-         File "<doctest __main__.cointercept[5]>", line 1, in <module>\r
-           cr.send("Bye")\r
-       StopIteration\r
-       """\r
-       item = yield\r
-       for elem in elems:\r
-               target.send(elem)\r
-               item = yield\r
-\r
-\r
-@autostart\r
-def codropwhile(target, pred):\r
-       """\r
-       >>> cdw = codropwhile(printer_sink("%s"), lambda x: x)\r
-       >>> cdw.send([0, 1, 2])\r
-       >>> cdw.send(1)\r
-       >>> cdw.send(True)\r
-       >>> cdw.send(False)\r
-       >>> cdw.send([0, 1, 2])\r
-       [0, 1, 2]\r
-       >>> cdw.send(1)\r
-       1\r
-       >>> cdw.send(True)\r
-       True\r
-       """\r
-       while True:\r
-               item = yield\r
-               if not pred(item):\r
-                       break\r
-\r
-       while True:\r
-               item = yield\r
-               target.send(item)\r
-\r
-\r
-@autostart\r
-def cotakewhile(target, pred):\r
-       """\r
-       >>> ctw = cotakewhile(printer_sink("%s"), lambda x: x)\r
-       >>> ctw.send([0, 1, 2])\r
-       [0, 1, 2]\r
-       >>> ctw.send(1)\r
-       1\r
-       >>> ctw.send(True)\r
-       True\r
-       >>> ctw.send(False)\r
-       >>> ctw.send([0, 1, 2])\r
-       >>> ctw.send(1)\r
-       >>> ctw.send(True)\r
-       """\r
-       while True:\r
-               item = yield\r
-               if not pred(item):\r
-                       break\r
-               target.send(item)\r
-\r
-       while True:\r
-               item = yield\r
-\r
-\r
-@autostart\r
-def coslice(target, lower, upper):\r
-       """\r
-       >>> cs = coslice(printer_sink("%r"), 3, 5)\r
-       >>> cs.send("0")\r
-       >>> cs.send("1")\r
-       >>> cs.send("2")\r
-       >>> cs.send("3")\r
-       '3'\r
-       >>> cs.send("4")\r
-       '4'\r
-       >>> cs.send("5")\r
-       >>> cs.send("6")\r
-       """\r
-       for i in xrange(lower):\r
-               item = yield\r
-       for i in xrange(upper - lower):\r
-               item = yield\r
-               target.send(item)\r
-       while True:\r
-               item = yield\r
-\r
-\r
-@autostart\r
-def cochain(targets):\r
-       """\r
-       >>> cr = cointercept(printer_sink("good %s"), [1, 2, 3, 4])\r
-       >>> cc = cochain([cr, printer_sink("end %s")])\r
-       >>> cc.send("a")\r
-       good 1\r
-       >>> cc.send(None)\r
-       good 2\r
-       >>> cc.send([])\r
-       good 3\r
-       >>> cc.send(0)\r
-       good 4\r
-       >>> cc.send("Bye")\r
-       end Bye\r
-       """\r
-       behind = []\r
-       for target in targets:\r
-               try:\r
-                       while behind:\r
-                               item = behind.pop()\r
-                               target.send(item)\r
-                       while True:\r
-                               item = yield\r
-                               target.send(item)\r
-               except StopIteration:\r
-                       behind.append(item)\r
-\r
-\r
-@autostart\r
-def queue_sink(queue):\r
-       """\r
-       >>> q = Queue.Queue()\r
-       >>> qs = queue_sink(q)\r
-       >>> qs.send("Hello")\r
-       >>> qs.send("World")\r
-       >>> qs.throw(RuntimeError, "Goodbye")\r
-       >>> qs.send("Meh")\r
-       >>> qs.close()\r
-       >>> print [i for i in _flush_queue(q)]\r
-       [(None, 'Hello'), (None, 'World'), (<type 'exceptions.RuntimeError'>, 'Goodbye'), (None, 'Meh'), (<type 'exceptions.GeneratorExit'>, None)]\r
-       """\r
-       while True:\r
-               try:\r
-                       item = yield\r
-                       queue.put((None, item))\r
-               except StandardError, e:\r
-                       queue.put((e.__class__, e.message))\r
-               except GeneratorExit:\r
-                       queue.put((GeneratorExit, None))\r
-                       raise\r
-\r
-\r
-def decode_item(item, target):\r
-       if item[0] is None:\r
-               target.send(item[1])\r
-               return False\r
-       elif item[0] is GeneratorExit:\r
-               target.close()\r
-               return True\r
-       else:\r
-               target.throw(item[0], item[1])\r
-               return False\r
-\r
-\r
-def queue_source(queue, target):\r
-       """\r
-       >>> q = Queue.Queue()\r
-       >>> for i in [\r
-       ...     (None, 'Hello'),\r
-       ...     (None, 'World'),\r
-       ...     (GeneratorExit, None),\r
-       ...     ]:\r
-       ...     q.put(i)\r
-       >>> qs = queue_source(q, printer_sink())\r
-       Hello\r
-       World\r
-       """\r
-       isDone = False\r
-       while not isDone:\r
-               item = queue.get()\r
-               isDone = decode_item(item, target)\r
-\r
-\r
-def threaded_stage(target, thread_factory = threading.Thread):\r
-       messages = Queue.Queue()\r
-\r
-       run_source = functools.partial(queue_source, messages, target)\r
-       thread_factory(target=run_source).start()\r
-\r
-       # Sink running in current thread\r
-       return functools.partial(queue_sink, messages)\r
-\r
-\r
-@autostart\r
-def pickle_sink(f):\r
-       while True:\r
-               try:\r
-                       item = yield\r
-                       pickle.dump((None, item), f)\r
-               except StandardError, e:\r
-                       pickle.dump((e.__class__, e.message), f)\r
-               except GeneratorExit:\r
-                       pickle.dump((GeneratorExit, ), f)\r
-                       raise\r
-               except StopIteration:\r
-                       f.close()\r
-                       return\r
-\r
-\r
-def pickle_source(f, target):\r
-       try:\r
-               isDone = False\r
-               while not isDone:\r
-                       item = pickle.load(f)\r
-                       isDone = decode_item(item, target)\r
-       except EOFError:\r
-               target.close()\r
-\r
-\r
-class EventHandler(object, xml.sax.ContentHandler):\r
-\r
-       START = "start"\r
-       TEXT = "text"\r
-       END = "end"\r
-\r
-       def __init__(self, target):\r
-               object.__init__(self)\r
-               xml.sax.ContentHandler.__init__(self)\r
-               self._target = target\r
-\r
-       def startElement(self, name, attrs):\r
-               self._target.send((self.START, (name, attrs._attrs)))\r
-\r
-       def characters(self, text):\r
-               self._target.send((self.TEXT, text))\r
-\r
-       def endElement(self, name):\r
-               self._target.send((self.END, name))\r
-\r
-\r
-def expat_parse(f, target):\r
-       parser = xml.parsers.expat.ParserCreate()\r
-       parser.buffer_size = 65536\r
-       parser.buffer_text = True\r
-       parser.returns_unicode = False\r
-       parser.StartElementHandler = lambda name, attrs: target.send(('start', (name, attrs)))\r
-       parser.EndElementHandler = lambda name: target.send(('end', name))\r
-       parser.CharacterDataHandler = lambda data: target.send(('text', data))\r
-       parser.ParseFile(f)\r
-\r
-\r
-if __name__ == "__main__":\r
-       import doctest\r
-       doctest.testmod()\r
diff --git a/src/util/go_utils.py b/src/util/go_utils.py
deleted file mode 100644 (file)
index 61e731d..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import with_statement
-
-import time
-import functools
-import threading
-import Queue
-import logging
-
-import gobject
-
-import algorithms
-import misc
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-def make_idler(func):
-       """
-       Decorator that makes a generator-function into a function that will continue execution on next call
-       """
-       a = []
-
-       @functools.wraps(func)
-       def decorated_func(*args, **kwds):
-               if not a:
-                       a.append(func(*args, **kwds))
-               try:
-                       a[0].next()
-                       return True
-               except StopIteration:
-                       del a[:]
-                       return False
-
-       return decorated_func
-
-
-def async(func):
-       """
-       Make a function mainloop friendly. the function will be called at the
-       next mainloop idle state.
-
-       >>> import misc
-       >>> misc.validate_decorator(async)
-       """
-
-       @functools.wraps(func)
-       def new_function(*args, **kwargs):
-
-               def async_function():
-                       func(*args, **kwargs)
-                       return False
-
-               gobject.idle_add(async_function)
-
-       return new_function
-
-
-class Async(object):
-
-       def __init__(self, func, once = True):
-               self.__func = func
-               self.__idleId = None
-               self.__once = once
-
-       def start(self):
-               assert self.__idleId is None
-               if self.__once:
-                       self.__idleId = gobject.idle_add(self._on_once)
-               else:
-                       self.__idleId = gobject.idle_add(self.__func)
-
-       def is_running(self):
-               return self.__idleId is not None
-
-       def cancel(self):
-               if self.__idleId is not None:
-                       gobject.source_remove(self.__idleId)
-                       self.__idleId = None
-
-       def __call__(self):
-               return self.start()
-
-       @misc.log_exception(_moduleLogger)
-       def _on_once(self):
-               self.cancel()
-               try:
-                       self.__func()
-               except Exception:
-                       pass
-               return False
-
-
-class Timeout(object):
-
-       def __init__(self, func, once = True):
-               self.__func = func
-               self.__timeoutId = None
-               self.__once = once
-
-       def start(self, **kwds):
-               assert self.__timeoutId is None
-
-               callback = self._on_once if self.__once else self.__func
-
-               assert len(kwds) == 1
-               timeoutInSeconds = kwds["seconds"]
-               assert 0 <= timeoutInSeconds
-
-               if timeoutInSeconds == 0:
-                       self.__timeoutId = gobject.idle_add(callback)
-               else:
-                       self.__timeoutId = timeout_add_seconds(timeoutInSeconds, callback)
-
-       def is_running(self):
-               return self.__timeoutId is not None
-
-       def cancel(self):
-               if self.__timeoutId is not None:
-                       gobject.source_remove(self.__timeoutId)
-                       self.__timeoutId = None
-
-       def __call__(self, **kwds):
-               return self.start(**kwds)
-
-       @misc.log_exception(_moduleLogger)
-       def _on_once(self):
-               self.cancel()
-               try:
-                       self.__func()
-               except Exception:
-                       pass
-               return False
-
-
-_QUEUE_EMPTY = object()
-
-
-class FutureThread(object):
-
-       def __init__(self):
-               self.__workQueue = Queue.Queue()
-               self.__thread = threading.Thread(
-                       name = type(self).__name__,
-                       target = self.__consume_queue,
-               )
-               self.__isRunning = True
-
-       def start(self):
-               self.__thread.start()
-
-       def stop(self):
-               self.__isRunning = False
-               for _ in algorithms.itr_available(self.__workQueue):
-                       pass # eat up queue to cut down dumb work
-               self.__workQueue.put(_QUEUE_EMPTY)
-
-       def clear_tasks(self):
-               for _ in algorithms.itr_available(self.__workQueue):
-                       pass # eat up queue to cut down dumb work
-
-       def add_task(self, func, args, kwds, on_success, on_error):
-               task = func, args, kwds, on_success, on_error
-               self.__workQueue.put(task)
-
-       @misc.log_exception(_moduleLogger)
-       def __trampoline_callback(self, on_success, on_error, isError, result):
-               if not self.__isRunning:
-                       if isError:
-                               _moduleLogger.error("Masking: %s" % (result, ))
-                       isError = True
-                       result = StopIteration("Cancelling all callbacks")
-               callback = on_success if not isError else on_error
-               try:
-                       callback(result)
-               except Exception:
-                       _moduleLogger.exception("Callback errored")
-               return False
-
-       @misc.log_exception(_moduleLogger)
-       def __consume_queue(self):
-               while True:
-                       task = self.__workQueue.get()
-                       if task is _QUEUE_EMPTY:
-                               break
-                       func, args, kwds, on_success, on_error = task
-
-                       try:
-                               result = func(*args, **kwds)
-                               isError = False
-                       except Exception, e:
-                               _moduleLogger.error("Error, passing it back to the main thread")
-                               result = e
-                               isError = True
-                       self.__workQueue.task_done()
-
-                       gobject.idle_add(self.__trampoline_callback, on_success, on_error, isError, result)
-               _moduleLogger.debug("Shutting down worker thread")
-
-
-class AutoSignal(object):
-
-       def __init__(self, toplevel):
-               self.__disconnectPool = []
-               toplevel.connect("destroy", self.__on_destroy)
-
-       def connect_auto(self, widget, *args):
-               id = widget.connect(*args)
-               self.__disconnectPool.append((widget, id))
-
-       @misc.log_exception(_moduleLogger)
-       def __on_destroy(self, widget):
-               _moduleLogger.info("Destroy: %r (%s to clean up)" % (self, len(self.__disconnectPool)))
-               for widget, id in self.__disconnectPool:
-                       widget.disconnect(id)
-               del self.__disconnectPool[:]
-
-
-def throttled(minDelay, queue):
-       """
-       Throttle the calls to a function by queueing all the calls that happen
-       before the minimum delay
-
-       >>> import misc
-       >>> import Queue
-       >>> misc.validate_decorator(throttled(0, Queue.Queue()))
-       """
-
-       def actual_decorator(func):
-
-               lastCallTime = [None]
-
-               def process_queue():
-                       if 0 < len(queue):
-                               func, args, kwargs = queue.pop(0)
-                               lastCallTime[0] = time.time() * 1000
-                               func(*args, **kwargs)
-                       return False
-
-               @functools.wraps(func)
-               def new_function(*args, **kwargs):
-                       now = time.time() * 1000
-                       if (
-                               lastCallTime[0] is None or
-                               (now - lastCallTime >= minDelay)
-                       ):
-                               lastCallTime[0] = now
-                               func(*args, **kwargs)
-                       else:
-                               queue.append((func, args, kwargs))
-                               lastCallDelta = now - lastCallTime[0]
-                               processQueueTimeout = int(minDelay * len(queue) - lastCallDelta)
-                               gobject.timeout_add(processQueueTimeout, process_queue)
-
-               return new_function
-
-       return actual_decorator
-
-
-def _old_timeout_add_seconds(timeout, callback):
-       return gobject.timeout_add(timeout * 1000, callback)
-
-
-def _timeout_add_seconds(timeout, callback):
-       return gobject.timeout_add_seconds(timeout, callback)
-
-
-try:
-       gobject.timeout_add_seconds
-       timeout_add_seconds = _timeout_add_seconds
-except AttributeError:
-       timeout_add_seconds = _old_timeout_add_seconds
diff --git a/src/util/gtk_utils.py b/src/util/gtk_utils.py
deleted file mode 100644 (file)
index 342feae..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import with_statement
-from __future__ import division
-
-import contextlib
-import logging
-
-import gtk
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-@contextlib.contextmanager
-def gtk_lock():
-       gtk.gdk.threads_enter()
-       try:
-               yield
-       finally:
-               gtk.gdk.threads_leave()
-
-
-def find_parent_window(widget):
-       while True:
-               parent = widget.get_parent()
-               if isinstance(parent, gtk.Window):
-                       return parent
-               widget = parent
-
-
-if __name__ == "__main__":
-       pass
-
diff --git a/src/util/hildonize.py b/src/util/hildonize.py
deleted file mode 100644 (file)
index 339eb2a..0000000
+++ /dev/null
@@ -1,766 +0,0 @@
-#!/usr/bin/env python
-
-"""
-Open Issues
-       @bug not all of a message is shown
-       @bug Buttons are too small
-"""
-
-
-import gobject
-import gtk
-import dbus
-
-
-class _NullHildonModule(object):
-       pass
-
-
-try:
-       import hildon as _hildon
-       hildon  = _hildon # Dumb but gets around pyflakiness
-except (ImportError, OSError):
-       hildon = _NullHildonModule
-
-
-IS_HILDON_SUPPORTED = hildon is not _NullHildonModule
-
-
-class _NullHildonProgram(object):
-
-       def add_window(self, window):
-               pass
-
-
-def _hildon_get_app_class():
-       return hildon.Program
-
-
-def _null_get_app_class():
-       return _NullHildonProgram
-
-
-try:
-       hildon.Program
-       get_app_class = _hildon_get_app_class
-except AttributeError:
-       get_app_class = _null_get_app_class
-
-
-def _hildon_set_application_name(name):
-       gtk.set_application_name(name)
-
-
-def _null_set_application_name(name):
-       pass
-
-
-try:
-       gtk.set_application_name
-       set_application_name = _hildon_set_application_name
-except AttributeError:
-       set_application_name = _null_set_application_name
-
-
-def _fremantle_hildonize_window(app, window):
-       oldWindow = window
-       newWindow = hildon.StackableWindow()
-       if oldWindow.get_child() is not None:
-               oldWindow.get_child().reparent(newWindow)
-       app.add_window(newWindow)
-       return newWindow
-
-
-def _hildon_hildonize_window(app, window):
-       oldWindow = window
-       newWindow = hildon.Window()
-       if oldWindow.get_child() is not None:
-               oldWindow.get_child().reparent(newWindow)
-       app.add_window(newWindow)
-       return newWindow
-
-
-def _null_hildonize_window(app, window):
-       return window
-
-
-try:
-       hildon.StackableWindow
-       hildonize_window = _fremantle_hildonize_window
-except AttributeError:
-       try:
-               hildon.Window
-               hildonize_window = _hildon_hildonize_window
-       except AttributeError:
-               hildonize_window = _null_hildonize_window
-
-
-def _fremantle_hildonize_menu(window, gtkMenu):
-       appMenu = hildon.AppMenu()
-       window.set_app_menu(appMenu)
-       gtkMenu.get_parent().remove(gtkMenu)
-       return appMenu
-
-
-def _hildon_hildonize_menu(window, gtkMenu):
-       hildonMenu = gtk.Menu()
-       for child in gtkMenu.get_children():
-               child.reparent(hildonMenu)
-       window.set_menu(hildonMenu)
-       gtkMenu.destroy()
-       return hildonMenu
-
-
-def _null_hildonize_menu(window, gtkMenu):
-       return gtkMenu
-
-
-try:
-       hildon.AppMenu
-       GTK_MENU_USED = False
-       IS_FREMANTLE_SUPPORTED = True
-       hildonize_menu = _fremantle_hildonize_menu
-except AttributeError:
-       GTK_MENU_USED = True
-       IS_FREMANTLE_SUPPORTED = False
-       if IS_HILDON_SUPPORTED:
-               hildonize_menu = _hildon_hildonize_menu
-       else:
-               hildonize_menu = _null_hildonize_menu
-
-
-def _hildon_set_button_auto_selectable(button):
-       button.set_theme_size(hildon.HILDON_SIZE_AUTO_HEIGHT)
-
-
-def _null_set_button_auto_selectable(button):
-       pass
-
-
-try:
-       hildon.HILDON_SIZE_AUTO_HEIGHT
-       gtk.Button.set_theme_size
-       set_button_auto_selectable = _hildon_set_button_auto_selectable
-except AttributeError:
-       set_button_auto_selectable = _null_set_button_auto_selectable
-
-
-def _hildon_set_button_finger_selectable(button):
-       button.set_theme_size(hildon.HILDON_SIZE_FINGER_HEIGHT)
-
-
-def _null_set_button_finger_selectable(button):
-       pass
-
-
-try:
-       hildon.HILDON_SIZE_FINGER_HEIGHT
-       gtk.Button.set_theme_size
-       set_button_finger_selectable = _hildon_set_button_finger_selectable
-except AttributeError:
-       set_button_finger_selectable = _null_set_button_finger_selectable
-
-
-def _hildon_set_button_thumb_selectable(button):
-       button.set_theme_size(hildon.HILDON_SIZE_THUMB_HEIGHT)
-
-
-def _null_set_button_thumb_selectable(button):
-       pass
-
-
-try:
-       hildon.HILDON_SIZE_THUMB_HEIGHT
-       gtk.Button.set_theme_size
-       set_button_thumb_selectable = _hildon_set_button_thumb_selectable
-except AttributeError:
-       set_button_thumb_selectable = _null_set_button_thumb_selectable
-
-
-def _hildon_set_cell_thumb_selectable(renderer):
-       renderer.set_property("scale", 1.5)
-
-
-def _null_set_cell_thumb_selectable(renderer):
-       pass
-
-
-if IS_HILDON_SUPPORTED:
-       set_cell_thumb_selectable = _hildon_set_cell_thumb_selectable
-else:
-       set_cell_thumb_selectable = _null_set_cell_thumb_selectable
-
-
-def _hildon_set_pix_cell_thumb_selectable(renderer):
-       renderer.set_property("stock-size", 48)
-
-
-def _null_set_pix_cell_thumb_selectable(renderer):
-       pass
-
-
-if IS_HILDON_SUPPORTED:
-       set_pix_cell_thumb_selectable = _hildon_set_pix_cell_thumb_selectable
-else:
-       set_pix_cell_thumb_selectable = _null_set_pix_cell_thumb_selectable
-
-
-def _fremantle_show_information_banner(parent, message):
-       hildon.hildon_banner_show_information(parent, "", message)
-
-
-def _hildon_show_information_banner(parent, message):
-       hildon.hildon_banner_show_information(parent, None, message)
-
-
-def _null_show_information_banner(parent, message):
-       pass
-
-
-if IS_FREMANTLE_SUPPORTED:
-       show_information_banner = _fremantle_show_information_banner
-else:
-       try:
-               hildon.hildon_banner_show_information
-               show_information_banner = _hildon_show_information_banner
-       except AttributeError:
-               show_information_banner = _null_show_information_banner
-
-
-def _fremantle_show_busy_banner_start(parent, message):
-       hildon.hildon_gtk_window_set_progress_indicator(parent, True)
-       return parent
-
-
-def _fremantle_show_busy_banner_end(banner):
-       hildon.hildon_gtk_window_set_progress_indicator(banner, False)
-
-
-def _hildon_show_busy_banner_start(parent, message):
-       return hildon.hildon_banner_show_animation(parent, None, message)
-
-
-def _hildon_show_busy_banner_end(banner):
-       banner.destroy()
-
-
-def _null_show_busy_banner_start(parent, message):
-       return None
-
-
-def _null_show_busy_banner_end(banner):
-       assert banner is None
-
-
-try:
-       hildon.hildon_gtk_window_set_progress_indicator
-       show_busy_banner_start = _fremantle_show_busy_banner_start
-       show_busy_banner_end = _fremantle_show_busy_banner_end
-except AttributeError:
-       try:
-               hildon.hildon_banner_show_animation
-               show_busy_banner_start = _hildon_show_busy_banner_start
-               show_busy_banner_end = _hildon_show_busy_banner_end
-       except AttributeError:
-               show_busy_banner_start = _null_show_busy_banner_start
-               show_busy_banner_end = _null_show_busy_banner_end
-
-
-def _hildon_hildonize_text_entry(textEntry):
-       textEntry.set_property('hildon-input-mode', 7)
-
-
-def _null_hildonize_text_entry(textEntry):
-       pass
-
-
-if IS_HILDON_SUPPORTED:
-       hildonize_text_entry = _hildon_hildonize_text_entry
-else:
-       hildonize_text_entry = _null_hildonize_text_entry
-
-
-def _hildon_window_to_portrait(window):
-       # gtk documentation is unclear whether this does a "=" or a "|="
-       flags = hildon.PORTRAIT_MODE_SUPPORT | hildon.PORTRAIT_MODE_REQUEST
-       hildon.hildon_gtk_window_set_portrait_flags(window, flags)
-
-
-def _hildon_window_to_landscape(window):
-       # gtk documentation is unclear whether this does a "=" or a "&= ~"
-       flags = hildon.PORTRAIT_MODE_SUPPORT
-       hildon.hildon_gtk_window_set_portrait_flags(window, flags)
-
-
-def _null_window_to_portrait(window):
-       pass
-
-
-def _null_window_to_landscape(window):
-       pass
-
-
-try:
-       hildon.PORTRAIT_MODE_SUPPORT
-       hildon.PORTRAIT_MODE_REQUEST
-       hildon.hildon_gtk_window_set_portrait_flags
-
-       window_to_portrait = _hildon_window_to_portrait
-       window_to_landscape = _hildon_window_to_landscape
-except AttributeError:
-       window_to_portrait = _null_window_to_portrait
-       window_to_landscape = _null_window_to_landscape
-
-
-def get_device_orientation():
-       bus = dbus.SystemBus()
-       try:
-               rawMceRequest = bus.get_object("com.nokia.mce", "/com/nokia/mce/request")
-               mceRequest = dbus.Interface(rawMceRequest, dbus_interface="com.nokia.mce.request")
-               orientation, standState, faceState, xAxis, yAxis, zAxis = mceRequest.get_device_orientation()
-       except dbus.exception.DBusException:
-               # catching for documentation purposes that when a system doesn't
-               # support this, this is what to expect
-               raise
-
-       if orientation == "":
-               return gtk.ORIENTATION_HORIZONTAL
-       elif orientation == "":
-               return gtk.ORIENTATION_VERTICAL
-       else:
-               raise RuntimeError("Unknown orientation: %s" % orientation)
-
-
-def _hildon_hildonize_password_entry(textEntry):
-       textEntry.set_property('hildon-input-mode', 7 | (1 << 29))
-
-
-def _null_hildonize_password_entry(textEntry):
-       pass
-
-
-if IS_HILDON_SUPPORTED:
-       hildonize_password_entry = _hildon_hildonize_password_entry
-else:
-       hildonize_password_entry = _null_hildonize_password_entry
-
-
-def _hildon_hildonize_combo_entry(comboEntry):
-       comboEntry.set_property('hildon-input-mode', 1 << 4)
-
-
-def _null_hildonize_combo_entry(textEntry):
-       pass
-
-
-if IS_HILDON_SUPPORTED:
-       hildonize_combo_entry = _hildon_hildonize_combo_entry
-else:
-       hildonize_combo_entry = _null_hildonize_combo_entry
-
-
-def _null_create_seekbar():
-       adjustment = gtk.Adjustment(0, 0, 101, 1, 5, 1)
-       seek = gtk.HScale(adjustment)
-       seek.set_draw_value(False)
-       return seek
-
-
-def _fremantle_create_seekbar():
-       seek = hildon.Seekbar()
-       seek.set_range(0.0, 100)
-       seek.set_draw_value(False)
-       seek.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
-       return seek
-
-
-try:
-       hildon.Seekbar
-       create_seekbar = _fremantle_create_seekbar
-except AttributeError:
-       create_seekbar = _null_create_seekbar
-
-
-def _fremantle_hildonize_scrollwindow(scrolledWindow):
-       pannableWindow = hildon.PannableArea()
-
-       child = scrolledWindow.get_child()
-       scrolledWindow.remove(child)
-       pannableWindow.add(child)
-
-       parent = scrolledWindow.get_parent()
-       if parent is not None:
-               parent.remove(scrolledWindow)
-               parent.add(pannableWindow)
-
-       return pannableWindow
-
-
-def _hildon_hildonize_scrollwindow(scrolledWindow):
-       hildon.hildon_helper_set_thumb_scrollbar(scrolledWindow, True)
-       return scrolledWindow
-
-
-def _null_hildonize_scrollwindow(scrolledWindow):
-       return scrolledWindow
-
-
-try:
-       hildon.PannableArea
-       hildonize_scrollwindow = _fremantle_hildonize_scrollwindow
-       hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
-except AttributeError:
-       try:
-               hildon.hildon_helper_set_thumb_scrollbar
-               hildonize_scrollwindow = _hildon_hildonize_scrollwindow
-               hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
-       except AttributeError:
-               hildonize_scrollwindow = _null_hildonize_scrollwindow
-               hildonize_scrollwindow_with_viewport = _null_hildonize_scrollwindow
-
-
-def _hildon_request_number(parent, title, range, default):
-       spinner = hildon.NumberEditor(*range)
-       spinner.set_value(default)
-
-       dialog = gtk.Dialog(
-               title,
-               parent,
-               gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
-               (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
-       )
-       dialog.set_default_response(gtk.RESPONSE_CANCEL)
-       dialog.get_child().add(spinner)
-
-       try:
-               dialog.show_all()
-               response = dialog.run()
-
-               if response == gtk.RESPONSE_OK:
-                       return spinner.get_value()
-               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
-                       raise RuntimeError("User cancelled request")
-               else:
-                       raise RuntimeError("Unrecognized response %r", response)
-       finally:
-               dialog.hide()
-               dialog.destroy()
-
-
-def _null_request_number(parent, title, range, default):
-       adjustment = gtk.Adjustment(default, range[0], range[1], 1, 5, 0)
-       spinner = gtk.SpinButton(adjustment, 0, 0)
-       spinner.set_wrap(False)
-
-       dialog = gtk.Dialog(
-               title,
-               parent,
-               gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
-               (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
-       )
-       dialog.set_default_response(gtk.RESPONSE_CANCEL)
-       dialog.get_child().add(spinner)
-
-       try:
-               dialog.show_all()
-               response = dialog.run()
-
-               if response == gtk.RESPONSE_OK:
-                       return spinner.get_value_as_int()
-               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
-                       raise RuntimeError("User cancelled request")
-               else:
-                       raise RuntimeError("Unrecognized response %r", response)
-       finally:
-               dialog.hide()
-               dialog.destroy()
-
-
-try:
-       hildon.NumberEditor # TODO deprecated in fremantle
-       request_number = _hildon_request_number
-except AttributeError:
-       request_number = _null_request_number
-
-
-def _hildon_touch_selector(parent, title, items, defaultIndex):
-       model = gtk.ListStore(gobject.TYPE_STRING)
-       for item in items:
-               model.append((item, ))
-
-       selector = hildon.TouchSelector()
-       selector.append_text_column(model, True)
-       selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
-       selector.set_active(0, defaultIndex)
-
-       dialog = hildon.PickerDialog(parent)
-       dialog.set_selector(selector)
-
-       try:
-               dialog.show_all()
-               response = dialog.run()
-
-               if response == gtk.RESPONSE_OK:
-                       return selector.get_active(0)
-               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
-                       raise RuntimeError("User cancelled request")
-               else:
-                       raise RuntimeError("Unrecognized response %r", response)
-       finally:
-               dialog.hide()
-               dialog.destroy()
-
-
-def _on_null_touch_selector_activated(treeView, path, column, dialog, pathData):
-       dialog.response(gtk.RESPONSE_OK)
-       pathData[0] = path
-
-
-def _null_touch_selector(parent, title, items, defaultIndex = -1):
-       parentSize = parent.get_size()
-
-       model = gtk.ListStore(gobject.TYPE_STRING)
-       for item in items:
-               model.append((item, ))
-
-       cell = gtk.CellRendererText()
-       set_cell_thumb_selectable(cell)
-       column = gtk.TreeViewColumn(title)
-       column.pack_start(cell, expand=True)
-       column.add_attribute(cell, "text", 0)
-
-       treeView = gtk.TreeView()
-       treeView.set_model(model)
-       treeView.append_column(column)
-       selection = treeView.get_selection()
-       selection.set_mode(gtk.SELECTION_SINGLE)
-       if 0 < defaultIndex:
-               selection.select_path((defaultIndex, ))
-
-       scrolledWin = gtk.ScrolledWindow()
-       scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-       scrolledWin.add(treeView)
-
-       dialog = gtk.Dialog(
-               title,
-               parent,
-               gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
-               (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
-       )
-       dialog.set_default_response(gtk.RESPONSE_CANCEL)
-       dialog.get_child().add(scrolledWin)
-       dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
-
-       scrolledWin = hildonize_scrollwindow(scrolledWin)
-       pathData = [None]
-       treeView.connect("row-activated", _on_null_touch_selector_activated, dialog, pathData)
-
-       try:
-               dialog.show_all()
-               response = dialog.run()
-
-               if response == gtk.RESPONSE_OK:
-                       if pathData[0] is None:
-                               raise RuntimeError("No selection made")
-                       return pathData[0][0]
-               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
-                       raise RuntimeError("User cancelled request")
-               else:
-                       raise RuntimeError("Unrecognized response %r", response)
-       finally:
-               dialog.hide()
-               dialog.destroy()
-
-
-try:
-       hildon.PickerDialog
-       hildon.TouchSelector
-       touch_selector = _hildon_touch_selector
-except AttributeError:
-       touch_selector = _null_touch_selector
-
-
-def _hildon_touch_selector_entry(parent, title, items, defaultItem):
-       # Got a segfault when using append_text_column with TouchSelectorEntry, so using this way
-       try:
-               selector = hildon.TouchSelectorEntry(text=True)
-       except TypeError:
-               selector = hildon.hildon_touch_selector_entry_new_text()
-       defaultIndex = -1
-       for i, item in enumerate(items):
-               selector.append_text(item)
-               if item == defaultItem:
-                       defaultIndex = i
-
-       dialog = hildon.PickerDialog(parent)
-       dialog.set_selector(selector)
-
-       if 0 < defaultIndex:
-               selector.set_active(0, defaultIndex)
-       else:
-               selector.get_entry().set_text(defaultItem)
-
-       try:
-               dialog.show_all()
-               response = dialog.run()
-       finally:
-               dialog.hide()
-
-       if response == gtk.RESPONSE_OK:
-               return selector.get_entry().get_text()
-       elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
-               raise RuntimeError("User cancelled request")
-       else:
-               raise RuntimeError("Unrecognized response %r", response)
-
-
-def _on_null_touch_selector_entry_entry_changed(entry, result, selection, defaultIndex):
-       custom = entry.get_text().strip()
-       if custom:
-               result[0] = custom
-               selection.unselect_all()
-       else:
-               result[0] = None
-               selection.select_path((defaultIndex, ))
-
-
-def _on_null_touch_selector_entry_entry_activated(customEntry, dialog, result):
-       dialog.response(gtk.RESPONSE_OK)
-       result[0] = customEntry.get_text()
-
-
-def _on_null_touch_selector_entry_tree_activated(treeView, path, column, dialog, result):
-       dialog.response(gtk.RESPONSE_OK)
-       model = treeView.get_model()
-       itr = model.get_iter(path)
-       if itr is not None:
-               result[0] = model.get_value(itr, 0)
-
-
-def _null_touch_selector_entry(parent, title, items, defaultItem):
-       parentSize = parent.get_size()
-
-       model = gtk.ListStore(gobject.TYPE_STRING)
-       defaultIndex = -1
-       for i, item in enumerate(items):
-               model.append((item, ))
-               if item == defaultItem:
-                       defaultIndex = i
-
-       cell = gtk.CellRendererText()
-       set_cell_thumb_selectable(cell)
-       column = gtk.TreeViewColumn(title)
-       column.pack_start(cell, expand=True)
-       column.add_attribute(cell, "text", 0)
-
-       treeView = gtk.TreeView()
-       treeView.set_model(model)
-       treeView.append_column(column)
-       selection = treeView.get_selection()
-       selection.set_mode(gtk.SELECTION_SINGLE)
-
-       scrolledWin = gtk.ScrolledWindow()
-       scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-       scrolledWin.add(treeView)
-
-       customEntry = gtk.Entry()
-
-       layout = gtk.VBox()
-       layout.pack_start(customEntry, expand=False)
-       layout.pack_start(scrolledWin)
-
-       dialog = gtk.Dialog(
-               title,
-               parent,
-               gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
-               (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
-       )
-       dialog.set_default_response(gtk.RESPONSE_CANCEL)
-       dialog.get_child().add(layout)
-       dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
-
-       scrolledWin = hildonize_scrollwindow(scrolledWin)
-
-       result = [None]
-       if 0 < defaultIndex:
-               selection.select_path((defaultIndex, ))
-               result[0] = defaultItem
-       else:
-               customEntry.set_text(defaultItem)
-
-       customEntry.connect("activate", _on_null_touch_selector_entry_entry_activated, dialog, result)
-       customEntry.connect("changed", _on_null_touch_selector_entry_entry_changed, result, selection, defaultIndex)
-       treeView.connect("row-activated", _on_null_touch_selector_entry_tree_activated, dialog, result)
-
-       try:
-               dialog.show_all()
-               response = dialog.run()
-
-               if response == gtk.RESPONSE_OK:
-                       _, itr = selection.get_selected()
-                       if itr is not None:
-                               return model.get_value(itr, 0)
-                       else:
-                               enteredText = customEntry.get_text().strip()
-                               if enteredText:
-                                       return enteredText
-                               elif result[0] is not None:
-                                       return result[0]
-                               else:
-                                       raise RuntimeError("No selection made")
-               elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
-                       raise RuntimeError("User cancelled request")
-               else:
-                       raise RuntimeError("Unrecognized response %r", response)
-       finally:
-               dialog.hide()
-               dialog.destroy()
-
-
-try:
-       hildon.PickerDialog
-       hildon.TouchSelectorEntry
-       touch_selector_entry = _hildon_touch_selector_entry
-except AttributeError:
-       touch_selector_entry = _null_touch_selector_entry
-
-
-if __name__ == "__main__":
-       app = get_app_class()()
-
-       label = gtk.Label("Hello World from a Label!")
-
-       win = gtk.Window()
-       win.add(label)
-       win = hildonize_window(app, win)
-       if False and IS_FREMANTLE_SUPPORTED:
-               appMenu = hildon.AppMenu()
-               for i in xrange(5):
-                       b = gtk.Button(str(i))
-                       appMenu.append(b)
-               win.set_app_menu(appMenu)
-               win.show_all()
-               appMenu.show_all()
-               gtk.main()
-       elif False:
-               print touch_selector(win, "Test", ["A", "B", "C", "D"], 2)
-       elif False:
-               print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "C")
-               print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "Blah")
-       elif False:
-               import pprint
-               name, value = "", ""
-               goodLocals = [
-                       (name, value) for (name, value) in locals().iteritems()
-                       if not name.startswith("_")
-               ]
-               pprint.pprint(goodLocals)
-       elif False:
-               import time
-               show_information_banner(win, "Hello World")
-               time.sleep(5)
-       elif False:
-               import time
-               banner = show_busy_banner_start(win, "Hello World")
-               time.sleep(5)
-               show_busy_banner_end(banner)
diff --git a/src/util/io.py b/src/util/io.py
deleted file mode 100644 (file)
index 4198f4b..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-#!/usr/bin/env python
-
-
-from __future__ import with_statement
-
-import os
-import pickle
-import contextlib
-import itertools
-import codecs
-from xml.sax import saxutils
-import csv
-try:
-       import cStringIO as StringIO
-except ImportError:
-       import StringIO
-
-
-@contextlib.contextmanager
-def change_directory(directory):
-       previousDirectory = os.getcwd()
-       os.chdir(directory)
-       currentDirectory = os.getcwd()
-
-       try:
-               yield previousDirectory, currentDirectory
-       finally:
-               os.chdir(previousDirectory)
-
-
-@contextlib.contextmanager
-def pickled(filename):
-       """
-       Here is an example usage:
-       with pickled("foo.db") as p:
-               p("users", list).append(["srid", "passwd", 23])
-       """
-
-       if os.path.isfile(filename):
-               data = pickle.load(open(filename))
-       else:
-               data = {}
-
-       def getter(item, factory):
-               if item in data:
-                       return data[item]
-               else:
-                       data[item] = factory()
-                       return data[item]
-
-       yield getter
-
-       pickle.dump(data, open(filename, "w"))
-
-
-@contextlib.contextmanager
-def redirect(object_, attr, value):
-       """
-       >>> import sys
-       ... with redirect(sys, 'stdout', open('stdout', 'w')):
-       ...     print "hello"
-       ...
-       >>> print "we're back"
-       we're back
-       """
-       orig = getattr(object_, attr)
-       setattr(object_, attr, value)
-       try:
-               yield
-       finally:
-               setattr(object_, attr, orig)
-
-
-def pathsplit(path):
-       """
-       >>> pathsplit("/a/b/c")
-       ['', 'a', 'b', 'c']
-       >>> pathsplit("./plugins/builtins.ini")
-       ['.', 'plugins', 'builtins.ini']
-       """
-       pathParts = path.split(os.path.sep)
-       return pathParts
-
-
-def commonpath(l1, l2, common=None):
-       """
-       >>> commonpath(pathsplit('/a/b/c/d'), pathsplit('/a/b/c1/d1'))
-       (['', 'a', 'b'], ['c', 'd'], ['c1', 'd1'])
-       >>> commonpath(pathsplit("./plugins/"), pathsplit("./plugins/builtins.ini"))
-       (['.', 'plugins'], [''], ['builtins.ini'])
-       >>> commonpath(pathsplit("./plugins/builtins"), pathsplit("./plugins"))
-       (['.', 'plugins'], ['builtins'], [])
-       """
-       if common is None:
-               common = []
-
-       if l1 == l2:
-               return l1, [], []
-
-       for i, (leftDir, rightDir) in enumerate(zip(l1, l2)):
-               if leftDir != rightDir:
-                       return l1[0:i], l1[i:], l2[i:]
-       else:
-               if leftDir == rightDir:
-                       i += 1
-               return l1[0:i], l1[i:], l2[i:]
-
-
-def relpath(p1, p2):
-       """
-       >>> relpath('/', '/')
-       './'
-       >>> relpath('/a/b/c/d', '/')
-       '../../../../'
-       >>> relpath('/a/b/c/d', '/a/b/c1/d1')
-       '../../c1/d1'
-       >>> relpath('/a/b/c/d', '/a/b/c1/d1/')
-       '../../c1/d1'
-       >>> relpath("./plugins/builtins", "./plugins")
-       '../'
-       >>> relpath("./plugins/", "./plugins/builtins.ini")
-       'builtins.ini'
-       """
-       sourcePath = os.path.normpath(p1)
-       destPath = os.path.normpath(p2)
-
-       (common, sourceOnly, destOnly) = commonpath(pathsplit(sourcePath), pathsplit(destPath))
-       if len(sourceOnly) or len(destOnly):
-               relParts = itertools.chain(
-                       (('..' + os.sep) * len(sourceOnly), ),
-                       destOnly,
-               )
-               return os.path.join(*relParts)
-       else:
-               return "."+os.sep
-
-
-class UTF8Recoder(object):
-       """
-       Iterator that reads an encoded stream and reencodes the input to UTF-8
-       """
-       def __init__(self, f, encoding):
-               self.reader = codecs.getreader(encoding)(f)
-
-       def __iter__(self):
-               return self
-
-       def next(self):
-               return self.reader.next().encode("utf-8")
-
-
-class UnicodeReader(object):
-       """
-       A CSV reader which will iterate over lines in the CSV file "f",
-       which is encoded in the given encoding.
-       """
-
-       def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
-               f = UTF8Recoder(f, encoding)
-               self.reader = csv.reader(f, dialect=dialect, **kwds)
-
-       def next(self):
-               row = self.reader.next()
-               return [unicode(s, "utf-8") for s in row]
-
-       def __iter__(self):
-               return self
-
-class UnicodeWriter(object):
-       """
-       A CSV writer which will write rows to CSV file "f",
-       which is encoded in the given encoding.
-       """
-
-       def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
-               # Redirect output to a queue
-               self.queue = StringIO.StringIO()
-               self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
-               self.stream = f
-               self.encoder = codecs.getincrementalencoder(encoding)()
-
-       def writerow(self, row):
-               self.writer.writerow([s.encode("utf-8") for s in row])
-               # Fetch UTF-8 output from the queue ...
-               data = self.queue.getvalue()
-               data = data.decode("utf-8")
-               # ... and reencode it into the target encoding
-               data = self.encoder.encode(data)
-               # write to the target stream
-               self.stream.write(data)
-               # empty queue
-               self.queue.truncate(0)
-
-       def writerows(self, rows):
-               for row in rows:
-                       self.writerow(row)
-
-
-def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
-       # csv.py doesn't do Unicode; encode temporarily as UTF-8:
-       csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
-                                                       dialect=dialect, **kwargs)
-       for row in csv_reader:
-               # decode UTF-8 back to Unicode, cell by cell:
-               yield [unicode(cell, 'utf-8') for cell in row]
-
-
-def utf_8_encoder(unicode_csv_data):
-       for line in unicode_csv_data:
-               yield line.encode('utf-8')
-
-
-_UNESCAPE_ENTITIES = {
- "&quot;": '"',
- "&nbsp;": " ",
- "&#39;": "'",
-}
-
-
-_ESCAPE_ENTITIES = dict((v, k) for (v, k) in zip(_UNESCAPE_ENTITIES.itervalues(), _UNESCAPE_ENTITIES.iterkeys()))
-del _ESCAPE_ENTITIES[" "]
-
-
-def unescape(text):
-       plain = saxutils.unescape(text, _UNESCAPE_ENTITIES)
-       return plain
-
-
-def escape(text):
-       fancy = saxutils.escape(text, _ESCAPE_ENTITIES)
-       return fancy
diff --git a/src/util/linux.py b/src/util/linux.py
deleted file mode 100644 (file)
index 4e77445..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/usr/bin/env python
-
-
-import os
-import logging
-
-try:
-       from xdg import BaseDirectory as _BaseDirectory
-       BaseDirectory = _BaseDirectory
-except ImportError:
-       BaseDirectory = None
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-_libc = None
-
-
-def set_process_name(name):
-       try: # change process name for killall
-               global _libc
-               if _libc is None:
-                       import ctypes
-                       _libc = ctypes.CDLL('libc.so.6')
-               _libc.prctl(15, name, 0, 0, 0)
-       except Exception, e:
-               _moduleLogger.warning('Unable to set processName: %s" % e')
-
-
-def get_new_resource(resourceType, resource, name):
-       if BaseDirectory is not None:
-               if resourceType == "data":
-                       base = BaseDirectory.xdg_data_home
-                       if base == "/usr/share/mime":
-                               # Ugly hack because somehow Maemo 4.1 seems to be set to this
-                               base = os.path.join(os.path.expanduser("~"), ".%s" % resource)
-               elif resourceType == "config":
-                       base = BaseDirectory.xdg_config_home
-               elif resourceType == "cache":
-                       base = BaseDirectory.xdg_cache_home
-               else:
-                       raise RuntimeError("Unknown type: "+resourceType)
-       else:
-               base = os.path.join(os.path.expanduser("~"), ".%s" % resource)
-
-       filePath = os.path.join(base, resource, name)
-       dirPath = os.path.dirname(filePath)
-       if not os.path.exists(dirPath):
-               # Looking before I leap to not mask errors
-               os.makedirs(dirPath)
-
-       return filePath
-
-
-def get_existing_resource(resourceType, resource, name):
-       if BaseDirectory is not None:
-               if resourceType == "data":
-                       base = BaseDirectory.xdg_data_home
-               elif resourceType == "config":
-                       base = BaseDirectory.xdg_config_home
-               elif resourceType == "cache":
-                       base = BaseDirectory.xdg_cache_home
-               else:
-                       raise RuntimeError("Unknown type: "+resourceType)
-       else:
-               base = None
-
-       if base is not None:
-               finalPath = os.path.join(base, name)
-               if os.path.exists(finalPath):
-                       return finalPath
-
-       altBase = os.path.join(os.path.expanduser("~"), ".%s" % resource)
-       finalPath = os.path.join(altBase, name)
-       if os.path.exists(finalPath):
-               return finalPath
-       else:
-               raise RuntimeError("Resource not found: %r" % ((resourceType, resource, name), ))
diff --git a/src/util/misc.py b/src/util/misc.py
deleted file mode 100644 (file)
index 9b8d88c..0000000
+++ /dev/null
@@ -1,900 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import with_statement
-
-import sys
-import re
-import cPickle
-
-import functools
-import contextlib
-import inspect
-
-import optparse
-import traceback
-import warnings
-import string
-
-
-class AnyData(object):
-
-       pass
-
-
-_indentationLevel = [0]
-
-
-def log_call(logger):
-
-       def log_call_decorator(func):
-
-               @functools.wraps(func)
-               def wrapper(*args, **kwds):
-                       logger.debug("%s> %s" % (" " * _indentationLevel[0], func.__name__, ))
-                       _indentationLevel[0] += 1
-                       try:
-                               return func(*args, **kwds)
-                       finally:
-                               _indentationLevel[0] -= 1
-                               logger.debug("%s< %s" % (" " * _indentationLevel[0], func.__name__, ))
-
-               return wrapper
-
-       return log_call_decorator
-
-
-def log_exception(logger):
-
-       def log_exception_decorator(func):
-
-               @functools.wraps(func)
-               def wrapper(*args, **kwds):
-                       try:
-                               return func(*args, **kwds)
-                       except Exception:
-                               logger.exception(func.__name__)
-                               raise
-
-               return wrapper
-
-       return log_exception_decorator
-
-
-def printfmt(template):
-       """
-       This hides having to create the Template object and call substitute/safe_substitute on it. For example:
-
-       >>> num = 10
-       >>> word = "spam"
-       >>> printfmt("I would like to order $num units of $word, please") #doctest: +SKIP
-       I would like to order 10 units of spam, please
-       """
-       frame = inspect.stack()[-1][0]
-       try:
-               print string.Template(template).safe_substitute(frame.f_locals)
-       finally:
-               del frame
-
-
-def is_special(name):
-       return name.startswith("__") and name.endswith("__")
-
-
-def is_private(name):
-       return name.startswith("_") and not is_special(name)
-
-
-def privatize(clsName, attributeName):
-       """
-       At runtime, make an attributeName private
-
-       Example:
-       >>> class Test(object):
-       ...     pass
-       ...
-       >>> try:
-       ...     dir(Test).index("_Test__me")
-       ...     print dir(Test)
-       ... except:
-       ...     print "Not Found"
-       Not Found
-       >>> setattr(Test, privatize(Test.__name__, "me"), "Hello World")
-       >>> try:
-       ...     dir(Test).index("_Test__me")
-       ...     print "Found"
-       ... except:
-       ...     print dir(Test)
-       0
-       Found
-       >>> print getattr(Test, obfuscate(Test.__name__, "__me"))
-       Hello World
-       >>>
-       >>> is_private(privatize(Test.__name__, "me"))
-       True
-       >>> is_special(privatize(Test.__name__, "me"))
-       False
-       """
-       return "".join(["_", clsName, "__", attributeName])
-
-
-def obfuscate(clsName, attributeName):
-       """
-       At runtime, turn a private name into the obfuscated form
-
-       Example:
-       >>> class Test(object):
-       ...     __me = "Hello World"
-       ...
-       >>> try:
-       ...     dir(Test).index("_Test__me")
-       ...     print "Found"
-       ... except:
-       ...     print dir(Test)
-       0
-       Found
-       >>> print getattr(Test, obfuscate(Test.__name__, "__me"))
-       Hello World
-       >>> is_private(obfuscate(Test.__name__, "__me"))
-       True
-       >>> is_special(obfuscate(Test.__name__, "__me"))
-       False
-       """
-       return "".join(["_", clsName, attributeName])
-
-
-class PAOptionParser(optparse.OptionParser, object):
-       """
-       >>> if __name__ == '__main__':
-       ...     #parser = PAOptionParser("My usage str")
-       ...     parser = PAOptionParser()
-       ...     parser.add_posarg("Foo", help="Foo usage")
-       ...     parser.add_posarg("Bar", dest="bar_dest")
-       ...     parser.add_posarg("Language", dest='tr_type', type="choice", choices=("Python", "Other"))
-       ...     parser.add_option('--stocksym', dest='symbol')
-       ...     values, args = parser.parse_args()
-       ...     print values, args
-       ...
-
-       python mycp.py  -h
-       python mycp.py
-       python mycp.py  foo
-       python mycp.py  foo bar
-
-       python mycp.py foo bar lava
-       Usage: pa.py <Foo> <Bar> <Language> [options]
-
-       Positional Arguments:
-       Foo: Foo usage
-       Bar:
-       Language:
-
-       pa.py: error: option --Language: invalid choice: 'lava' (choose from 'Python', 'Other'
-       """
-
-       def __init__(self, *args, **kw):
-               self.posargs = []
-               super(PAOptionParser, self).__init__(*args, **kw)
-
-       def add_posarg(self, *args, **kw):
-               pa_help = kw.get("help", "")
-               kw["help"] = optparse.SUPPRESS_HELP
-               o = self.add_option("--%s" % args[0], *args[1:], **kw)
-               self.posargs.append((args[0], pa_help))
-
-       def get_usage(self, *args, **kwargs):
-               params = (' '.join(["<%s>" % arg[0] for arg in self.posargs]), '\n '.join(["%s: %s" % (arg) for arg in self.posargs]))
-               self.usage = "%%prog %s [options]\n\nPositional Arguments:\n %s" % params
-               return super(PAOptionParser, self).get_usage(*args, **kwargs)
-
-       def parse_args(self, *args, **kwargs):
-               args = sys.argv[1:]
-               args0 = []
-               for p, v in zip(self.posargs, args):
-                       args0.append("--%s" % p[0])
-                       args0.append(v)
-               args = args0 + args
-               options, args = super(PAOptionParser, self).parse_args(args, **kwargs)
-               if len(args) < len(self.posargs):
-                       msg = 'Missing value(s) for "%s"\n' % ", ".join([arg[0] for arg in self.posargs][len(args):])
-                       self.error(msg)
-               return options, args
-
-
-def explicitly(name, stackadd=0):
-       """
-       This is an alias for adding to '__all__'.  Less error-prone than using
-       __all__ itself, since setting __all__ directly is prone to stomping on
-       things implicitly exported via L{alias}.
-
-       @note Taken from PyExport (which could turn out pretty cool):
-       @li @a http://codebrowse.launchpad.net/~glyph/
-       @li @a http://glyf.livejournal.com/74356.html
-       """
-       packageVars = sys._getframe(1+stackadd).f_locals
-       globalAll = packageVars.setdefault('__all__', [])
-       globalAll.append(name)
-
-
-def public(thunk):
-       """
-       This is a decorator, for convenience.  Rather than typing the name of your
-       function twice, you can decorate a function with this.
-
-       To be real, @public would need to work on methods as well, which gets into
-       supporting types...
-
-       @note Taken from PyExport (which could turn out pretty cool):
-       @li @a http://codebrowse.launchpad.net/~glyph/
-       @li @a http://glyf.livejournal.com/74356.html
-       """
-       explicitly(thunk.__name__, 1)
-       return thunk
-
-
-def _append_docstring(obj, message):
-       if obj.__doc__ is None:
-               obj.__doc__ = message
-       else:
-               obj.__doc__ += message
-
-
-def validate_decorator(decorator):
-
-       def simple(x):
-               return x
-
-       f = simple
-       f.__name__ = "name"
-       f.__doc__ = "doc"
-       f.__dict__["member"] = True
-
-       g = decorator(f)
-
-       if f.__name__ != g.__name__:
-               print f.__name__, "!=", g.__name__
-
-       if g.__doc__ is None:
-               print decorator.__name__, "has no doc string"
-       elif not g.__doc__.startswith(f.__doc__):
-               print g.__doc__, "didn't start with", f.__doc__
-
-       if not ("member" in g.__dict__ and g.__dict__["member"]):
-               print "'member' not in ", g.__dict__
-
-
-def deprecated_api(func):
-       """
-       This is a decorator which can be used to mark functions
-       as deprecated. It will result in a warning being emitted
-       when the function is used.
-
-       >>> validate_decorator(deprecated_api)
-       """
-
-       @functools.wraps(func)
-       def newFunc(*args, **kwargs):
-               warnings.warn("Call to deprecated function %s." % func.__name__, category=DeprecationWarning)
-               return func(*args, **kwargs)
-
-       _append_docstring(newFunc, "\n@deprecated")
-       return newFunc
-
-
-def unstable_api(func):
-       """
-       This is a decorator which can be used to mark functions
-       as deprecated. It will result in a warning being emitted
-       when the function is used.
-
-       >>> validate_decorator(unstable_api)
-       """
-
-       @functools.wraps(func)
-       def newFunc(*args, **kwargs):
-               warnings.warn("Call to unstable API function %s." % func.__name__, category=FutureWarning)
-               return func(*args, **kwargs)
-       _append_docstring(newFunc, "\n@unstable")
-       return newFunc
-
-
-def enabled(func):
-       """
-       This decorator doesn't add any behavior
-
-       >>> validate_decorator(enabled)
-       """
-       return func
-
-
-def disabled(func):
-       """
-       This decorator disables the provided function, and does nothing
-
-       >>> validate_decorator(disabled)
-       """
-
-       @functools.wraps(func)
-       def emptyFunc(*args, **kargs):
-               pass
-       _append_docstring(emptyFunc, "\n@note Temporarily Disabled")
-       return emptyFunc
-
-
-def metadata(document=True, **kwds):
-       """
-       >>> validate_decorator(metadata(author="Ed"))
-       """
-
-       def decorate(func):
-               for k, v in kwds.iteritems():
-                       setattr(func, k, v)
-                       if document:
-                               _append_docstring(func, "\n@"+k+" "+v)
-               return func
-       return decorate
-
-
-def prop(func):
-       """Function decorator for defining property attributes
-
-       The decorated function is expected to return a dictionary
-       containing one or more of the following pairs:
-               fget - function for getting attribute value
-               fset - function for setting attribute value
-               fdel - function for deleting attribute
-       This can be conveniently constructed by the locals() builtin
-       function; see:
-       http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183
-       @author http://kbyanc.blogspot.com/2007/06/python-property-attribute-tricks.html
-
-       Example:
-       >>> #Due to transformation from function to property, does not need to be validated
-       >>> #validate_decorator(prop)
-       >>> class MyExampleClass(object):
-       ...     @prop
-       ...     def foo():
-       ...             "The foo property attribute's doc-string"
-       ...             def fget(self):
-       ...                     print "GET"
-       ...                     return self._foo
-       ...             def fset(self, value):
-       ...                     print "SET"
-       ...                     self._foo = value
-       ...             return locals()
-       ...
-       >>> me = MyExampleClass()
-       >>> me.foo = 10
-       SET
-       >>> print me.foo
-       GET
-       10
-       """
-       return property(doc=func.__doc__, **func())
-
-
-def print_handler(e):
-       """
-       @see ExpHandler
-       """
-       print "%s: %s" % (type(e).__name__, e)
-
-
-def print_ignore(e):
-       """
-       @see ExpHandler
-       """
-       print 'Ignoring %s exception: %s' % (type(e).__name__, e)
-
-
-def print_traceback(e):
-       """
-       @see ExpHandler
-       """
-       #print sys.exc_info()
-       traceback.print_exc(file=sys.stdout)
-
-
-def ExpHandler(handler = print_handler, *exceptions):
-       """
-       An exception handling idiom using decorators
-       Examples
-       Specify exceptions in order, first one is handled first
-       last one last.
-
-       >>> validate_decorator(ExpHandler())
-       >>> @ExpHandler(print_ignore, ZeroDivisionError)
-       ... @ExpHandler(None, AttributeError, ValueError)
-       ... def f1():
-       ...     1/0
-       >>> @ExpHandler(print_traceback, ZeroDivisionError)
-       ... def f2():
-       ...     1/0
-       >>> @ExpHandler()
-       ... def f3(*pargs):
-       ...     l = pargs
-       ...     return l[10]
-       >>> @ExpHandler(print_traceback, ZeroDivisionError)
-       ... def f4():
-       ...     return 1
-       >>>
-       >>>
-       >>> f1()
-       Ignoring ZeroDivisionError exception: integer division or modulo by zero
-       >>> f2() # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
-       Traceback (most recent call last):
-       ...
-       ZeroDivisionError: integer division or modulo by zero
-       >>> f3()
-       IndexError: tuple index out of range
-       >>> f4()
-       1
-       """
-
-       def wrapper(f):
-               localExceptions = exceptions
-               if not localExceptions:
-                       localExceptions = [Exception]
-               t = [(ex, handler) for ex in localExceptions]
-               t.reverse()
-
-               def newfunc(t, *args, **kwargs):
-                       ex, handler = t[0]
-                       try:
-                               if len(t) == 1:
-                                       return f(*args, **kwargs)
-                               else:
-                                       #Recurse for embedded try/excepts
-                                       dec_func = functools.partial(newfunc, t[1:])
-                                       dec_func = functools.update_wrapper(dec_func, f)
-                                       return dec_func(*args, **kwargs)
-                       except ex, e:
-                               return handler(e)
-
-               dec_func = functools.partial(newfunc, t)
-               dec_func = functools.update_wrapper(dec_func, f)
-               return dec_func
-       return wrapper
-
-
-def into_debugger(func):
-       """
-       >>> validate_decorator(into_debugger)
-       """
-
-       @functools.wraps(func)
-       def newFunc(*args, **kwargs):
-               try:
-                       return func(*args, **kwargs)
-               except:
-                       import pdb
-                       pdb.post_mortem()
-
-       return newFunc
-
-
-class bindclass(object):
-       """
-       >>> validate_decorator(bindclass)
-       >>> class Foo(BoundObject):
-       ...      @bindclass
-       ...      def foo(this_class, self):
-       ...              return this_class, self
-       ...
-       >>> class Bar(Foo):
-       ...      @bindclass
-       ...      def bar(this_class, self):
-       ...              return this_class, self
-       ...
-       >>> f = Foo()
-       >>> b = Bar()
-       >>>
-       >>> f.foo() # doctest: +ELLIPSIS
-       (<class '...Foo'>, <...Foo object at ...>)
-       >>> b.foo() # doctest: +ELLIPSIS
-       (<class '...Foo'>, <...Bar object at ...>)
-       >>> b.bar() # doctest: +ELLIPSIS
-       (<class '...Bar'>, <...Bar object at ...>)
-       """
-
-       def __init__(self, f):
-               self.f = f
-               self.__name__ = f.__name__
-               self.__doc__ = f.__doc__
-               self.__dict__.update(f.__dict__)
-               self.m = None
-
-       def bind(self, cls, attr):
-
-               def bound_m(*args, **kwargs):
-                       return self.f(cls, *args, **kwargs)
-               bound_m.__name__ = attr
-               self.m = bound_m
-
-       def __get__(self, obj, objtype=None):
-               return self.m.__get__(obj, objtype)
-
-
-class ClassBindingSupport(type):
-       "@see bindclass"
-
-       def __init__(mcs, name, bases, attrs):
-               type.__init__(mcs, name, bases, attrs)
-               for attr, val in attrs.iteritems():
-                       if isinstance(val, bindclass):
-                               val.bind(mcs, attr)
-
-
-class BoundObject(object):
-       "@see bindclass"
-       __metaclass__ = ClassBindingSupport
-
-
-def bindfunction(f):
-       """
-       >>> validate_decorator(bindfunction)
-       >>> @bindfunction
-       ... def factorial(thisfunction, n):
-       ...      # Within this function the name 'thisfunction' refers to the factorial
-       ...      # function(with only one argument), even after 'factorial' is bound
-       ...      # to another object
-       ...      if n > 0:
-       ...              return n * thisfunction(n - 1)
-       ...      else:
-       ...              return 1
-       ...
-       >>> factorial(3)
-       6
-       """
-
-       @functools.wraps(f)
-       def bound_f(*args, **kwargs):
-               return f(bound_f, *args, **kwargs)
-       return bound_f
-
-
-class Memoize(object):
-       """
-       Memoize(fn) - an instance which acts like fn but memoizes its arguments
-       Will only work on functions with non-mutable arguments
-       @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52201
-
-       >>> validate_decorator(Memoize)
-       """
-
-       def __init__(self, fn):
-               self.fn = fn
-               self.__name__ = fn.__name__
-               self.__doc__ = fn.__doc__
-               self.__dict__.update(fn.__dict__)
-               self.memo = {}
-
-       def __call__(self, *args):
-               if args not in self.memo:
-                       self.memo[args] = self.fn(*args)
-               return self.memo[args]
-
-
-class MemoizeMutable(object):
-       """Memoize(fn) - an instance which acts like fn but memoizes its arguments
-       Will work on functions with mutable arguments(slower than Memoize)
-       @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52201
-
-       >>> validate_decorator(MemoizeMutable)
-       """
-
-       def __init__(self, fn):
-               self.fn = fn
-               self.__name__ = fn.__name__
-               self.__doc__ = fn.__doc__
-               self.__dict__.update(fn.__dict__)
-               self.memo = {}
-
-       def __call__(self, *args, **kw):
-               text = cPickle.dumps((args, kw))
-               if text not in self.memo:
-                       self.memo[text] = self.fn(*args, **kw)
-               return self.memo[text]
-
-
-callTraceIndentationLevel = 0
-
-
-def call_trace(f):
-       """
-       Synchronization decorator.
-
-       >>> validate_decorator(call_trace)
-       >>> @call_trace
-       ... def a(a, b, c):
-       ...     pass
-       >>> a(1, 2, c=3)
-       Entering a((1, 2), {'c': 3})
-       Exiting a((1, 2), {'c': 3})
-       """
-
-       @functools.wraps(f)
-       def verboseTrace(*args, **kw):
-               global callTraceIndentationLevel
-
-               print "%sEntering %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
-               callTraceIndentationLevel += 1
-               try:
-                       result = f(*args, **kw)
-               except:
-                       callTraceIndentationLevel -= 1
-                       print "%sException %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
-                       raise
-               callTraceIndentationLevel -= 1
-               print "%sExiting %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
-               return result
-
-       @functools.wraps(f)
-       def smallTrace(*args, **kw):
-               global callTraceIndentationLevel
-
-               print "%sEntering %s" % ("\t"*callTraceIndentationLevel, f.__name__)
-               callTraceIndentationLevel += 1
-               try:
-                       result = f(*args, **kw)
-               except:
-                       callTraceIndentationLevel -= 1
-                       print "%sException %s" % ("\t"*callTraceIndentationLevel, f.__name__)
-                       raise
-               callTraceIndentationLevel -= 1
-               print "%sExiting %s" % ("\t"*callTraceIndentationLevel, f.__name__)
-               return result
-
-       #return smallTrace
-       return verboseTrace
-
-
-@contextlib.contextmanager
-def nested_break():
-       """
-       >>> with nested_break() as mylabel:
-       ...     for i in xrange(3):
-       ...             print "Outer", i
-       ...             for j in xrange(3):
-       ...                     if i == 2: raise mylabel
-       ...                     if j == 2: break
-       ...                     print "Inner", j
-       ...             print "more processing"
-       Outer 0
-       Inner 0
-       Inner 1
-       Outer 1
-       Inner 0
-       Inner 1
-       Outer 2
-       """
-
-       class NestedBreakException(Exception):
-               pass
-
-       try:
-               yield NestedBreakException
-       except NestedBreakException:
-               pass
-
-
-@contextlib.contextmanager
-def lexical_scope(*args):
-       """
-       @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/520586
-       Example:
-       >>> b = 0
-       >>> with lexical_scope(1) as (a):
-       ...     print a
-       ...
-       1
-       >>> with lexical_scope(1,2,3) as (a,b,c):
-       ...     print a,b,c
-       ...
-       1 2 3
-       >>> with lexical_scope():
-       ...     d = 10
-       ...     def foo():
-       ...             pass
-       ...
-       >>> print b
-       2
-       """
-
-       frame = inspect.currentframe().f_back.f_back
-       saved = frame.f_locals.keys()
-       try:
-               if not args:
-                       yield
-               elif len(args) == 1:
-                       yield args[0]
-               else:
-                       yield args
-       finally:
-               f_locals = frame.f_locals
-               for key in (x for x in f_locals.keys() if x not in saved):
-                       del f_locals[key]
-               del frame
-
-
-def normalize_number(prettynumber):
-       """
-       function to take a phone number and strip out all non-numeric
-       characters
-
-       >>> normalize_number("+012-(345)-678-90")
-       '+01234567890'
-       >>> normalize_number("1-(345)-678-9000")
-       '+13456789000'
-       >>> normalize_number("+1-(345)-678-9000")
-       '+13456789000'
-       """
-       uglynumber = re.sub('[^0-9+]', '', prettynumber)
-       if uglynumber.startswith("+"):
-               pass
-       elif uglynumber.startswith("1"):
-               uglynumber = "+"+uglynumber
-       elif 10 <= len(uglynumber):
-               assert uglynumber[0] not in ("+", "1"), "Number format confusing"
-               uglynumber = "+1"+uglynumber
-       else:
-               pass
-
-       return uglynumber
-
-
-_VALIDATE_RE = re.compile("^\+?[0-9]{10,}$")
-
-
-def is_valid_number(number):
-       """
-       @returns If This number be called ( syntax validation only )
-       """
-       return _VALIDATE_RE.match(number) is not None
-
-
-def make_ugly(prettynumber):
-       """
-       function to take a phone number and strip out all non-numeric
-       characters
-
-       >>> make_ugly("+012-(345)-678-90")
-       '+01234567890'
-       """
-       return normalize_number(prettynumber)
-
-
-def _make_pretty_with_areacode(phonenumber):
-       prettynumber = "(%s)" % (phonenumber[0:3], )
-       if 3 < len(phonenumber):
-               prettynumber += " %s" % (phonenumber[3:6], )
-               if 6 < len(phonenumber):
-                       prettynumber += "-%s" % (phonenumber[6:], )
-       return prettynumber
-
-
-def _make_pretty_local(phonenumber):
-       prettynumber = "%s" % (phonenumber[0:3], )
-       if 3 < len(phonenumber):
-               prettynumber += "-%s" % (phonenumber[3:], )
-       return prettynumber
-
-
-def _make_pretty_international(phonenumber):
-       prettynumber = phonenumber
-       if phonenumber.startswith("1"):
-               prettynumber = "1 "
-               prettynumber += _make_pretty_with_areacode(phonenumber[1:])
-       return prettynumber
-
-
-def make_pretty(phonenumber):
-       """
-       Function to take a phone number and return the pretty version
-       pretty numbers:
-               if phonenumber begins with 0:
-                       ...-(...)-...-....
-               if phonenumber begins with 1: ( for gizmo callback numbers )
-                       1 (...)-...-....
-               if phonenumber is 13 digits:
-                       (...)-...-....
-               if phonenumber is 10 digits:
-                       ...-....
-       >>> make_pretty("12")
-       '12'
-       >>> make_pretty("1234567")
-       '123-4567'
-       >>> make_pretty("2345678901")
-       '+1 (234) 567-8901'
-       >>> make_pretty("12345678901")
-       '+1 (234) 567-8901'
-       >>> make_pretty("01234567890")
-       '+012 (345) 678-90'
-       >>> make_pretty("+01234567890")
-       '+012 (345) 678-90'
-       >>> make_pretty("+12")
-       '+1 (2)'
-       >>> make_pretty("+123")
-       '+1 (23)'
-       >>> make_pretty("+1234")
-       '+1 (234)'
-       """
-       if phonenumber is None or phonenumber == "":
-               return ""
-
-       phonenumber = normalize_number(phonenumber)
-
-       if phonenumber == "":
-               return ""
-       elif phonenumber[0] == "+":
-               prettynumber = _make_pretty_international(phonenumber[1:])
-               if not prettynumber.startswith("+"):
-                       prettynumber = "+"+prettynumber
-       elif 8 < len(phonenumber) and phonenumber[0] in ("1", ):
-               prettynumber = _make_pretty_international(phonenumber)
-       elif 7 < len(phonenumber):
-               prettynumber = _make_pretty_with_areacode(phonenumber)
-       elif 3 < len(phonenumber):
-               prettynumber = _make_pretty_local(phonenumber)
-       else:
-               prettynumber = phonenumber
-       return prettynumber.strip()
-
-
-def similar_ugly_numbers(lhs, rhs):
-       return (
-               lhs == rhs or
-               lhs[1:] == rhs and lhs.startswith("1") or
-               lhs[2:] == rhs and lhs.startswith("+1") or
-               lhs == rhs[1:] and rhs.startswith("1") or
-               lhs == rhs[2:] and rhs.startswith("+1")
-       )
-
-
-def abbrev_relative_date(date):
-       """
-       >>> abbrev_relative_date("42 hours ago")
-       '42 h'
-       >>> abbrev_relative_date("2 days ago")
-       '2 d'
-       >>> abbrev_relative_date("4 weeks ago")
-       '4 w'
-       """
-       parts = date.split(" ")
-       return "%s %s" % (parts[0], parts[1][0])
-
-
-def parse_version(versionText):
-       """
-       >>> parse_version("0.5.2")
-       [0, 5, 2]
-       """
-       return [
-               int(number)
-               for number in versionText.split(".")
-       ]
-
-
-def compare_versions(leftParsedVersion, rightParsedVersion):
-       """
-       >>> compare_versions([0, 1, 2], [0, 1, 2])
-       0
-       >>> compare_versions([0, 1, 2], [0, 1, 3])
-       -1
-       >>> compare_versions([0, 1, 2], [0, 2, 2])
-       -1
-       >>> compare_versions([0, 1, 2], [1, 1, 2])
-       -1
-       >>> compare_versions([0, 1, 3], [0, 1, 2])
-       1
-       >>> compare_versions([0, 2, 2], [0, 1, 2])
-       1
-       >>> compare_versions([1, 1, 2], [0, 1, 2])
-       1
-       """
-       for left, right in zip(leftParsedVersion, rightParsedVersion):
-               if left < right:
-                       return -1
-               elif right < left:
-                       return 1
-       else:
-               return 0
diff --git a/src/util/overloading.py b/src/util/overloading.py
deleted file mode 100644 (file)
index 89cb738..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-#!/usr/bin/env python
-import new
-
-# Make the environment more like Python 3.0
-__metaclass__ = type
-from itertools import izip as zip
-import textwrap
-import inspect
-
-
-__all__ = [
-       "AnyType",
-       "overloaded"
-]
-
-
-AnyType = object
-
-
-class overloaded:
-       """
-       Dynamically overloaded functions.
-
-       This is an implementation of (dynamically, or run-time) overloaded
-       functions; also known as generic functions or multi-methods.
-
-       The dispatch algorithm uses the types of all argument for dispatch,
-       similar to (compile-time) overloaded functions or methods in C++ and
-       Java.
-
-       Most of the complexity in the algorithm comes from the need to support
-       subclasses in call signatures.  For example, if an function is
-       registered for a signature (T1, T2), then a call with a signature (S1,
-       S2) is acceptable, assuming that S1 is a subclass of T1, S2 a subclass
-       of T2, and there are no other more specific matches (see below).
-
-       If there are multiple matches and one of those doesn't *dominate* all
-       others, the match is deemed ambiguous and an exception is raised.  A
-       subtlety here: if, after removing the dominated matches, there are
-       still multiple matches left, but they all map to the same function,
-       then the match is not deemed ambiguous and that function is used.
-       Read the method find_func() below for details.
-
-       @note Python 2.5 is required due to the use of predicates any() and all().
-       @note only supports positional arguments
-
-       @author http://www.artima.com/weblogs/viewpost.jsp?thread=155514
-
-       >>> import misc
-       >>> misc.validate_decorator (overloaded)
-       >>>
-       >>>
-       >>>
-       >>>
-       >>> #################
-       >>> #Basics, with reusing names and without
-       >>> @overloaded
-       ... def foo(x):
-       ...     "prints x"
-       ...     print x
-       ...
-       >>> @foo.register(int)
-       ... def foo(x):
-       ...     "prints the hex representation of x"
-       ...     print hex(x)
-       ...
-       >>> from types import DictType
-       >>> @foo.register(DictType)
-       ... def foo_dict(x):
-       ...     "prints the keys of x"
-       ...     print [k for k in x.iterkeys()]
-       ...
-       >>> #combines all of the doc strings to help keep track of the specializations
-       >>> foo.__doc__  # doctest: +ELLIPSIS
-       "prints x\\n\\n...overloading.foo (<type 'int'>):\\n\\tprints the hex representation of x\\n\\n...overloading.foo_dict (<type 'dict'>):\\n\\tprints the keys of x"
-       >>> foo ("text")
-       text
-       >>> foo (10) #calling the specialized foo
-       0xa
-       >>> foo ({3:5, 6:7}) #calling the specialization foo_dict
-       [3, 6]
-       >>> foo_dict ({3:5, 6:7}) #with using a unique name, you still have the option of calling the function directly
-       [3, 6]
-       >>>
-       >>>
-       >>>
-       >>>
-       >>> #################
-       >>> #Multiple arguments, accessing the default, and function finding
-       >>> @overloaded
-       ... def two_arg (x, y):
-       ...     print x,y
-       ...
-       >>> @two_arg.register(int, int)
-       ... def two_arg_int_int (x, y):
-       ...     print hex(x), hex(y)
-       ...
-       >>> @two_arg.register(float, int)
-       ... def two_arg_float_int (x, y):
-       ...     print x, hex(y)
-       ...
-       >>> @two_arg.register(int, float)
-       ... def two_arg_int_float (x, y):
-       ...     print hex(x), y
-       ...
-       >>> two_arg.__doc__ # doctest: +ELLIPSIS
-       "...overloading.two_arg_int_int (<type 'int'>, <type 'int'>):\\n\\n...overloading.two_arg_float_int (<type 'float'>, <type 'int'>):\\n\\n...overloading.two_arg_int_float (<type 'int'>, <type 'float'>):"
-       >>> two_arg(9, 10)
-       0x9 0xa
-       >>> two_arg(9.0, 10)
-       9.0 0xa
-       >>> two_arg(15, 16.0)
-       0xf 16.0
-       >>> two_arg.default_func(9, 10)
-       9 10
-       >>> two_arg.find_func ((int, float)) == two_arg_int_float
-       True
-       >>> (int, float) in two_arg
-       True
-       >>> (str, int) in two_arg
-       False
-       >>>
-       >>>
-       >>>
-       >>> #################
-       >>> #wildcard
-       >>> @two_arg.register(AnyType, str)
-       ... def two_arg_any_str (x, y):
-       ...     print x, y.lower()
-       ...
-       >>> two_arg("Hello", "World")
-       Hello world
-       >>> two_arg(500, "World")
-       500 world
-       """
-
-       def __init__(self, default_func):
-               # Decorator to declare new overloaded function.
-               self.registry = {}
-               self.cache = {}
-               self.default_func = default_func
-               self.__name__ = self.default_func.__name__
-               self.__doc__ = self.default_func.__doc__
-               self.__dict__.update (self.default_func.__dict__)
-
-       def __get__(self, obj, type=None):
-               if obj is None:
-                       return self
-               return new.instancemethod(self, obj)
-
-       def register(self, *types):
-               """
-               Decorator to register an implementation for a specific set of types.
-
-               .register(t1, t2)(f) is equivalent to .register_func((t1, t2), f).
-               """
-
-               def helper(func):
-                       self.register_func(types, func)
-
-                       originalDoc = self.__doc__ if self.__doc__ is not None else ""
-                       typeNames = ", ".join ([str(type) for type in types])
-                       typeNames = "".join ([func.__module__+".", func.__name__, " (", typeNames, "):"])
-                       overloadedDoc = ""
-                       if func.__doc__ is not None:
-                               overloadedDoc = textwrap.fill (func.__doc__, width=60, initial_indent="\t", subsequent_indent="\t")
-                       self.__doc__ = "\n".join ([originalDoc, "", typeNames, overloadedDoc]).strip()
-
-                       new_func = func
-
-                       #Masking the function, so we want to take on its traits
-                       if func.__name__ == self.__name__:
-                               self.__dict__.update (func.__dict__)
-                               new_func = self
-                       return new_func
-
-               return helper
-
-       def register_func(self, types, func):
-               """Helper to register an implementation."""
-               self.registry[tuple(types)] = func
-               self.cache = {} # Clear the cache (later we can optimize this).
-
-       def __call__(self, *args):
-               """Call the overloaded function."""
-               types = tuple(map(type, args))
-               func = self.cache.get(types)
-               if func is None:
-                       self.cache[types] = func = self.find_func(types)
-               return func(*args)
-
-       def __contains__ (self, types):
-               return self.find_func(types) is not self.default_func
-
-       def find_func(self, types):
-               """Find the appropriate overloaded function; don't call it.
-
-               @note This won't work for old-style classes or classes without __mro__
-               """
-               func = self.registry.get(types)
-               if func is not None:
-                       # Easy case -- direct hit in registry.
-                       return func
-
-               # Phillip Eby suggests to use issubclass() instead of __mro__.
-               # There are advantages and disadvantages.
-
-               # I can't help myself -- this is going to be intense functional code.
-               # Find all possible candidate signatures.
-               mros = tuple(inspect.getmro(t) for t in types)
-               n = len(mros)
-               candidates = [sig for sig in self.registry
-                               if len(sig) == n and
-                                       all(t in mro for t, mro in zip(sig, mros))]
-
-               if not candidates:
-                       # No match at all -- use the default function.
-                       return self.default_func
-               elif len(candidates) == 1:
-                       # Unique match -- that's an easy case.
-                       return self.registry[candidates[0]]
-
-               # More than one match -- weed out the subordinate ones.
-
-               def dominates(dom, sub,
-                               orders=tuple(dict((t, i) for i, t in enumerate(mro))
-                                                       for mro in mros)):
-                       # Predicate to decide whether dom strictly dominates sub.
-                       # Strict domination is defined as domination without equality.
-                       # The arguments dom and sub are type tuples of equal length.
-                       # The orders argument is a precomputed auxiliary data structure
-                       # giving dicts of ordering information corresponding to the
-                       # positions in the type tuples.
-                       # A type d dominates a type s iff order[d] <= order[s].
-                       # A type tuple (d1, d2, ...) dominates a type tuple of equal length
-                       # (s1, s2, ...) iff d1 dominates s1, d2 dominates s2, etc.
-                       if dom is sub:
-                               return False
-                       return all(order[d] <= order[s] for d, s, order in zip(dom, sub, orders))
-
-               # I suppose I could inline dominates() but it wouldn't get any clearer.
-               candidates = [cand
-                               for cand in candidates
-                                       if not any(dominates(dom, cand) for dom in candidates)]
-               if len(candidates) == 1:
-                       # There's exactly one candidate left.
-                       return self.registry[candidates[0]]
-
-               # Perhaps these multiple candidates all have the same implementation?
-               funcs = set(self.registry[cand] for cand in candidates)
-               if len(funcs) == 1:
-                       return funcs.pop()
-
-               # No, the situation is irreducibly ambiguous.
-               raise TypeError("ambigous call; types=%r; candidates=%r" %
-                                               (types, candidates))
diff --git a/src/util/qore_utils.py b/src/util/qore_utils.py
deleted file mode 100644 (file)
index 153558d..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-import logging
-
-import qt_compat
-QtCore = qt_compat.QtCore
-
-import misc
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class QThread44(QtCore.QThread):
-       """
-       This is to imitate QThread in Qt 4.4+ for when running on older version
-       See http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong
-       (On Lucid I have Qt 4.7 and this is still an issue)
-       """
-
-       def __init__(self, parent = None):
-               QtCore.QThread.__init__(self, parent)
-
-       def run(self):
-               self.exec_()
-
-
-class _WorkerThread(QtCore.QObject):
-
-       _taskComplete  = qt_compat.Signal(object)
-
-       def __init__(self, futureThread):
-               QtCore.QObject.__init__(self)
-               self._futureThread = futureThread
-               self._futureThread._addTask.connect(self._on_task_added)
-               self._taskComplete.connect(self._futureThread._on_task_complete)
-
-       @qt_compat.Slot(object)
-       def _on_task_added(self, task):
-               self.__on_task_added(task)
-
-       @misc.log_exception(_moduleLogger)
-       def __on_task_added(self, task):
-               if not self._futureThread._isRunning:
-                       _moduleLogger.error("Dropping task")
-
-               func, args, kwds, on_success, on_error = task
-
-               try:
-                       result = func(*args, **kwds)
-                       isError = False
-               except Exception, e:
-                       _moduleLogger.error("Error, passing it back to the main thread")
-                       result = e
-                       isError = True
-
-               taskResult = on_success, on_error, isError, result
-               self._taskComplete.emit(taskResult)
-
-
-class FutureThread(QtCore.QObject):
-
-       _addTask = qt_compat.Signal(object)
-
-       def __init__(self):
-               QtCore.QObject.__init__(self)
-               self._thread = QThread44()
-               self._isRunning = False
-               self._worker = _WorkerThread(self)
-               self._worker.moveToThread(self._thread)
-
-       def start(self):
-               self._thread.start()
-               self._isRunning = True
-
-       def stop(self):
-               self._isRunning = False
-               self._thread.quit()
-
-       def add_task(self, func, args, kwds, on_success, on_error):
-               assert self._isRunning, "Task queue not started"
-               task = func, args, kwds, on_success, on_error
-               self._addTask.emit(task)
-
-       @qt_compat.Slot(object)
-       def _on_task_complete(self, taskResult):
-               self.__on_task_complete(taskResult)
-
-       @misc.log_exception(_moduleLogger)
-       def __on_task_complete(self, taskResult):
-               on_success, on_error, isError, result = taskResult
-               if not self._isRunning:
-                       if isError:
-                               _moduleLogger.error("Masking: %s" % (result, ))
-                       isError = True
-                       result = StopIteration("Cancelling all callbacks")
-               callback = on_success if not isError else on_error
-               try:
-                       callback(result)
-               except Exception:
-                       _moduleLogger.exception("Callback errored")
diff --git a/src/util/qt_compat.py b/src/util/qt_compat.py
deleted file mode 100644 (file)
index 2ab7fa4..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import with_statement
-from __future__ import division
-
-#try:
-#      import PySide.QtCore as _QtCore
-#      QtCore = _QtCore
-#      USES_PYSIDE = True
-#except ImportError:
-if True:
-       import sip
-       sip.setapi('QString', 2)
-       sip.setapi('QVariant', 2)
-       import PyQt4.QtCore as _QtCore
-       QtCore = _QtCore
-       USES_PYSIDE = False
-
-
-def _pyside_import_module(moduleName):
-       pyside = __import__('PySide', globals(), locals(), [moduleName], -1)
-       return getattr(pyside, moduleName)
-
-
-def _pyqt4_import_module(moduleName):
-       pyside = __import__('PyQt4', globals(), locals(), [moduleName], -1)
-       return getattr(pyside, moduleName)
-
-
-if USES_PYSIDE:
-       import_module = _pyside_import_module
-
-       Signal = QtCore.Signal
-       Slot = QtCore.Slot
-       Property = QtCore.Property
-else:
-       import_module = _pyqt4_import_module
-
-       Signal = QtCore.pyqtSignal
-       Slot = QtCore.pyqtSlot
-       Property = QtCore.pyqtProperty
-
-
-if __name__ == "__main__":
-       pass
-
diff --git a/src/util/qtpie.py b/src/util/qtpie.py
deleted file mode 100755 (executable)
index 6b77d5d..0000000
+++ /dev/null
@@ -1,1094 +0,0 @@
-#!/usr/bin/env python
-
-import math
-import logging
-
-import qt_compat
-QtCore = qt_compat.QtCore
-QtGui = qt_compat.import_module("QtGui")
-
-import misc as misc_utils
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-_TWOPI = 2 * math.pi
-
-
-def _radius_at(center, pos):
-       delta = pos - center
-       xDelta = delta.x()
-       yDelta = delta.y()
-
-       radius = math.sqrt(xDelta ** 2 + yDelta ** 2)
-       return radius
-
-
-def _angle_at(center, pos):
-       delta = pos - center
-       xDelta = delta.x()
-       yDelta = delta.y()
-
-       radius = math.sqrt(xDelta ** 2 + yDelta ** 2)
-       angle = math.acos(xDelta / radius)
-       if 0 <= yDelta:
-               angle = _TWOPI - angle
-
-       return angle
-
-
-class QActionPieItem(object):
-
-       def __init__(self, action, weight = 1):
-               self._action = action
-               self._weight = weight
-
-       def action(self):
-               return self._action
-
-       def setWeight(self, weight):
-               self._weight = weight
-
-       def weight(self):
-               return self._weight
-
-       def setEnabled(self, enabled = True):
-               self._action.setEnabled(enabled)
-
-       def isEnabled(self):
-               return self._action.isEnabled()
-
-
-class PieFiling(object):
-
-       INNER_RADIUS_DEFAULT = 64
-       OUTER_RADIUS_DEFAULT = 192
-
-       SELECTION_CENTER = -1
-       SELECTION_NONE = -2
-
-       NULL_CENTER = QActionPieItem(QtGui.QAction(None))
-
-       def __init__(self):
-               self._innerRadius = self.INNER_RADIUS_DEFAULT
-               self._outerRadius = self.OUTER_RADIUS_DEFAULT
-               self._children = []
-               self._center = self.NULL_CENTER
-
-               self._cacheIndexToAngle = {}
-               self._cacheTotalWeight = 0
-
-       def insertItem(self, item, index = -1):
-               self._children.insert(index, item)
-               self._invalidate_cache()
-
-       def removeItemAt(self, index):
-               item = self._children.pop(index)
-               self._invalidate_cache()
-
-       def set_center(self, item):
-               if item is None:
-                       item = self.NULL_CENTER
-               self._center = item
-
-       def center(self):
-               return self._center
-
-       def clear(self):
-               del self._children[:]
-               self._center = self.NULL_CENTER
-               self._invalidate_cache()
-
-       def itemAt(self, index):
-               return self._children[index]
-
-       def indexAt(self, center, point):
-               return self._angle_to_index(_angle_at(center, point))
-
-       def innerRadius(self):
-               return self._innerRadius
-
-       def setInnerRadius(self, radius):
-               self._innerRadius = radius
-
-       def outerRadius(self):
-               return self._outerRadius
-
-       def setOuterRadius(self, radius):
-               self._outerRadius = radius
-
-       def __iter__(self):
-               return iter(self._children)
-
-       def __len__(self):
-               return len(self._children)
-
-       def __getitem__(self, index):
-               return self._children[index]
-
-       def _invalidate_cache(self):
-               self._cacheIndexToAngle.clear()
-               self._cacheTotalWeight = sum(child.weight() for child in self._children)
-               if self._cacheTotalWeight == 0:
-                       self._cacheTotalWeight = 1
-
-       def _index_to_angle(self, index, isShifted):
-               key = index, isShifted
-               if key in self._cacheIndexToAngle:
-                       return self._cacheIndexToAngle[key]
-               index = index % len(self._children)
-
-               baseAngle = _TWOPI / self._cacheTotalWeight
-
-               angle = math.pi / 2
-               if isShifted:
-                       if self._children:
-                               angle -= (self._children[0].weight() * baseAngle) / 2
-                       else:
-                               angle -= baseAngle / 2
-               while angle < 0:
-                       angle += _TWOPI
-
-               for i, child in enumerate(self._children):
-                       if index < i:
-                               break
-                       angle += child.weight() * baseAngle
-               while _TWOPI < angle:
-                       angle -= _TWOPI
-
-               self._cacheIndexToAngle[key] = angle
-               return angle
-
-       def _angle_to_index(self, angle):
-               numChildren = len(self._children)
-               if numChildren == 0:
-                       return self.SELECTION_CENTER
-
-               baseAngle = _TWOPI / self._cacheTotalWeight
-
-               iterAngle = math.pi / 2 - (self.itemAt(0).weight() * baseAngle) / 2
-               while iterAngle < 0:
-                       iterAngle += _TWOPI
-
-               oldIterAngle = iterAngle
-               for index, child in enumerate(self._children):
-                       iterAngle += child.weight() * baseAngle
-                       if oldIterAngle < angle and angle <= iterAngle:
-                               return index - 1 if index != 0 else numChildren - 1
-                       elif oldIterAngle < (angle + _TWOPI) and (angle + _TWOPI <= iterAngle):
-                               return index - 1 if index != 0 else numChildren - 1
-                       oldIterAngle = iterAngle
-
-
-class PieArtist(object):
-
-       ICON_SIZE_DEFAULT = 48
-
-       SHAPE_CIRCLE = "circle"
-       SHAPE_SQUARE = "square"
-       DEFAULT_SHAPE = SHAPE_SQUARE
-
-       BACKGROUND_FILL = "fill"
-       BACKGROUND_NOFILL = "no fill"
-
-       def __init__(self, filing, background = BACKGROUND_FILL):
-               self._filing = filing
-
-               self._cachedOuterRadius = self._filing.outerRadius()
-               self._cachedInnerRadius = self._filing.innerRadius()
-               canvasSize = self._cachedOuterRadius * 2 + 1
-               self._canvas = QtGui.QPixmap(canvasSize, canvasSize)
-               self._mask = None
-               self._backgroundState = background
-               self.palette = None
-
-       def pieSize(self):
-               diameter = self._filing.outerRadius() * 2 + 1
-               return QtCore.QSize(diameter, diameter)
-
-       def centerSize(self):
-               painter = QtGui.QPainter(self._canvas)
-               text = self._filing.center().action().text()
-               fontMetrics = painter.fontMetrics()
-               if text:
-                       textBoundingRect = fontMetrics.boundingRect(text)
-               else:
-                       textBoundingRect = QtCore.QRect()
-               textWidth = textBoundingRect.width()
-               textHeight = textBoundingRect.height()
-
-               return QtCore.QSize(
-                       textWidth + self.ICON_SIZE_DEFAULT,
-                       max(textHeight, self.ICON_SIZE_DEFAULT),
-               )
-
-       def show(self, palette):
-               self.palette = palette
-
-               if (
-                       self._cachedOuterRadius != self._filing.outerRadius() or
-                       self._cachedInnerRadius != self._filing.innerRadius()
-               ):
-                       self._cachedOuterRadius = self._filing.outerRadius()
-                       self._cachedInnerRadius = self._filing.innerRadius()
-                       self._canvas = self._canvas.scaled(self.pieSize())
-
-               if self._mask is None:
-                       self._mask = QtGui.QBitmap(self._canvas.size())
-                       self._mask.fill(QtCore.Qt.color0)
-                       self._generate_mask(self._mask)
-                       self._canvas.setMask(self._mask)
-               return self._mask
-
-       def hide(self):
-               self.palette = None
-
-       def paint(self, selectionIndex):
-               painter = QtGui.QPainter(self._canvas)
-               painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
-
-               self.paintPainter(selectionIndex, painter)
-
-               return self._canvas
-
-       def paintPainter(self, selectionIndex, painter):
-               adjustmentRect = painter.viewport().adjusted(0, 0, -1, -1)
-
-               numChildren = len(self._filing)
-               if numChildren == 0:
-                       self._paint_center_background(painter, adjustmentRect, selectionIndex)
-                       self._paint_center_foreground(painter, adjustmentRect, selectionIndex)
-                       return self._canvas
-               else:
-                       for i in xrange(len(self._filing)):
-                               self._paint_slice_background(painter, adjustmentRect, i, selectionIndex)
-
-               self._paint_center_background(painter, adjustmentRect, selectionIndex)
-               self._paint_center_foreground(painter, adjustmentRect, selectionIndex)
-
-               for i in xrange(len(self._filing)):
-                       self._paint_slice_foreground(painter, adjustmentRect, i, selectionIndex)
-
-       def _generate_mask(self, mask):
-               """
-               Specifies on the mask the shape of the pie menu
-               """
-               painter = QtGui.QPainter(mask)
-               painter.setPen(QtCore.Qt.color1)
-               painter.setBrush(QtCore.Qt.color1)
-               if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
-                       painter.drawRect(mask.rect())
-               elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
-                       painter.drawEllipse(mask.rect().adjusted(0, 0, -1, -1))
-               else:
-                       raise NotImplementedError(self.DEFAULT_SHAPE)
-
-       def _paint_slice_background(self, painter, adjustmentRect, i, selectionIndex):
-               if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
-                       currentWidth = adjustmentRect.width()
-                       newWidth = math.sqrt(2) * currentWidth
-                       dx = (newWidth - currentWidth) / 2
-                       adjustmentRect = adjustmentRect.adjusted(-dx, -dx, dx, dx)
-               elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
-                       pass
-               else:
-                       raise NotImplementedError(self.DEFAULT_SHAPE)
-
-               if self._backgroundState == self.BACKGROUND_NOFILL:
-                       painter.setBrush(QtGui.QBrush(QtCore.Qt.transparent))
-                       painter.setPen(self.palette.highlight().color())
-               else:
-                       if i == selectionIndex and self._filing[i].isEnabled():
-                               painter.setBrush(self.palette.highlight())
-                               painter.setPen(self.palette.highlight().color())
-                       else:
-                               painter.setBrush(self.palette.window())
-                               painter.setPen(self.palette.window().color())
-
-               a = self._filing._index_to_angle(i, True)
-               b = self._filing._index_to_angle(i + 1, True)
-               if b < a:
-                       b += _TWOPI
-               size = b - a
-               if size < 0:
-                       size += _TWOPI
-
-               startAngleInDeg = (a * 360 * 16) / _TWOPI
-               sizeInDeg = (size * 360 * 16) / _TWOPI
-               painter.drawPie(adjustmentRect, int(startAngleInDeg), int(sizeInDeg))
-
-       def _paint_slice_foreground(self, painter, adjustmentRect, i, selectionIndex):
-               child = self._filing[i]
-
-               a = self._filing._index_to_angle(i, True)
-               b = self._filing._index_to_angle(i + 1, True)
-               if b < a:
-                       b += _TWOPI
-               middleAngle = (a + b) / 2
-               averageRadius = (self._cachedInnerRadius + self._cachedOuterRadius) / 2
-
-               sliceX = averageRadius * math.cos(middleAngle)
-               sliceY = - averageRadius * math.sin(middleAngle)
-
-               piePos = adjustmentRect.center()
-               pieX = piePos.x()
-               pieY = piePos.y()
-               self._paint_label(
-                       painter, child.action(), i == selectionIndex, pieX+sliceX, pieY+sliceY
-               )
-
-       def _paint_label(self, painter, action, isSelected, x, y):
-               text = action.text()
-               fontMetrics = painter.fontMetrics()
-               if text:
-                       textBoundingRect = fontMetrics.boundingRect(text)
-               else:
-                       textBoundingRect = QtCore.QRect()
-               textWidth = textBoundingRect.width()
-               textHeight = textBoundingRect.height()
-
-               icon = action.icon().pixmap(
-                       QtCore.QSize(self.ICON_SIZE_DEFAULT, self.ICON_SIZE_DEFAULT),
-                       QtGui.QIcon.Normal,
-                       QtGui.QIcon.On,
-               )
-               iconWidth = icon.width()
-               iconHeight = icon.width()
-               averageWidth = (iconWidth + textWidth)/2
-               if not icon.isNull():
-                       iconRect = QtCore.QRect(
-                               x - averageWidth,
-                               y - iconHeight/2,
-                               iconWidth,
-                               iconHeight,
-                       )
-
-                       painter.drawPixmap(iconRect, icon)
-
-               if text:
-                       if isSelected:
-                               if action.isEnabled():
-                                       pen = self.palette.highlightedText()
-                                       brush = self.palette.highlight()
-                               else:
-                                       pen = self.palette.mid()
-                                       brush = self.palette.window()
-                       else:
-                               if action.isEnabled():
-                                       pen = self.palette.windowText()
-                               else:
-                                       pen = self.palette.mid()
-                               brush = self.palette.window()
-
-                       leftX = x - averageWidth + iconWidth
-                       topY = y + textHeight/2
-                       painter.setPen(pen.color())
-                       painter.setBrush(brush)
-                       painter.drawText(leftX, topY, text)
-
-       def _paint_center_background(self, painter, adjustmentRect, selectionIndex):
-               if self._backgroundState == self.BACKGROUND_NOFILL:
-                       return
-               if len(self._filing) == 0:
-                       if self._backgroundState == self.BACKGROUND_NOFILL:
-                               painter.setBrush(QtGui.QBrush(QtCore.Qt.transparent))
-                       else:
-                               if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
-                                       painter.setBrush(self.palette.highlight())
-                               else:
-                                       painter.setBrush(self.palette.window())
-                       painter.setPen(self.palette.mid().color())
-
-                       painter.drawRect(adjustmentRect)
-               else:
-                       dark = self.palette.mid().color()
-                       light = self.palette.light().color()
-                       if self._backgroundState == self.BACKGROUND_NOFILL:
-                               background = QtGui.QBrush(QtCore.Qt.transparent)
-                       else:
-                               if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
-                                       background = self.palette.highlight().color()
-                               else:
-                                       background = self.palette.window().color()
-
-                       innerRadius = self._cachedInnerRadius
-                       adjustmentCenterPos = adjustmentRect.center()
-                       innerRect = QtCore.QRect(
-                               adjustmentCenterPos.x() - innerRadius,
-                               adjustmentCenterPos.y() - innerRadius,
-                               innerRadius * 2 + 1,
-                               innerRadius * 2 + 1,
-                       )
-
-                       painter.setPen(QtCore.Qt.NoPen)
-                       painter.setBrush(background)
-                       painter.drawPie(innerRect, 0, 360 * 16)
-
-                       if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
-                               pass
-                       elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
-                               painter.setPen(QtGui.QPen(dark, 1))
-                               painter.setBrush(QtCore.Qt.NoBrush)
-                               painter.drawEllipse(adjustmentRect)
-                       else:
-                               raise NotImplementedError(self.DEFAULT_SHAPE)
-
-       def _paint_center_foreground(self, painter, adjustmentRect, selectionIndex):
-               centerPos = adjustmentRect.center()
-               pieX = centerPos.x()
-               pieY = centerPos.y()
-
-               x = pieX
-               y = pieY
-
-               self._paint_label(
-                       painter,
-                       self._filing.center().action(),
-                       selectionIndex == PieFiling.SELECTION_CENTER,
-                       x, y
-               )
-
-
-class QPieDisplay(QtGui.QWidget):
-
-       def __init__(self, filing, parent = None, flags = QtCore.Qt.Window):
-               QtGui.QWidget.__init__(self, parent, flags)
-               self._filing = filing
-               self._artist = PieArtist(self._filing)
-               self._selectionIndex = PieFiling.SELECTION_NONE
-
-       def popup(self, pos):
-               self._update_selection(pos)
-               self.show()
-
-       def sizeHint(self):
-               return self._artist.pieSize()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def showEvent(self, showEvent):
-               mask = self._artist.show(self.palette())
-               self.setMask(mask)
-
-               QtGui.QWidget.showEvent(self, showEvent)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def hideEvent(self, hideEvent):
-               self._artist.hide()
-               self._selectionIndex = PieFiling.SELECTION_NONE
-               QtGui.QWidget.hideEvent(self, hideEvent)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def paintEvent(self, paintEvent):
-               canvas = self._artist.paint(self._selectionIndex)
-               offset = (self.size() - canvas.size()) / 2
-
-               screen = QtGui.QPainter(self)
-               screen.drawPixmap(QtCore.QPoint(offset.width(), offset.height()), canvas)
-
-               QtGui.QWidget.paintEvent(self, paintEvent)
-
-       def selectAt(self, index):
-               oldIndex = self._selectionIndex
-               self._selectionIndex = index
-               if self.isVisible():
-                       self.update()
-
-
-class QPieButton(QtGui.QWidget):
-
-       activated = qt_compat.Signal(int)
-       highlighted = qt_compat.Signal(int)
-       canceled = qt_compat.Signal()
-       aboutToShow = qt_compat.Signal()
-       aboutToHide = qt_compat.Signal()
-
-       BUTTON_RADIUS = 24
-       DELAY = 250
-
-       def __init__(self, buttonSlice, parent = None, buttonSlices = None):
-               # @bug Artifacts on Maemo 5 due to window 3D effects, find way to disable them for just these?
-               # @bug The pie's are being pushed back on screen on Maemo, leading to coordinate issues
-               QtGui.QWidget.__init__(self, parent)
-               self._cachedCenterPosition = self.rect().center()
-
-               self._filing = PieFiling()
-               self._display = QPieDisplay(self._filing, None, QtCore.Qt.SplashScreen)
-               self._selectionIndex = PieFiling.SELECTION_NONE
-
-               self._buttonFiling = PieFiling()
-               self._buttonFiling.set_center(buttonSlice)
-               if buttonSlices is not None:
-                       for slice in buttonSlices:
-                               self._buttonFiling.insertItem(slice)
-               self._buttonFiling.setOuterRadius(self.BUTTON_RADIUS)
-               self._buttonArtist = PieArtist(self._buttonFiling, PieArtist.BACKGROUND_NOFILL)
-               self._poppedUp = False
-               self._pressed = False
-
-               self._delayPopupTimer = QtCore.QTimer()
-               self._delayPopupTimer.setInterval(self.DELAY)
-               self._delayPopupTimer.setSingleShot(True)
-               self._delayPopupTimer.timeout.connect(self._on_delayed_popup)
-               self._popupLocation = None
-
-               self._mousePosition = None
-               self.setFocusPolicy(QtCore.Qt.StrongFocus)
-               self.setSizePolicy(
-                       QtGui.QSizePolicy(
-                               QtGui.QSizePolicy.MinimumExpanding,
-                               QtGui.QSizePolicy.MinimumExpanding,
-                       )
-               )
-
-       def insertItem(self, item, index = -1):
-               self._filing.insertItem(item, index)
-
-       def removeItemAt(self, index):
-               self._filing.removeItemAt(index)
-
-       def set_center(self, item):
-               self._filing.set_center(item)
-
-       def set_button(self, item):
-               self.update()
-
-       def clear(self):
-               self._filing.clear()
-
-       def itemAt(self, index):
-               return self._filing.itemAt(index)
-
-       def indexAt(self, point):
-               return self._filing.indexAt(self._cachedCenterPosition, point)
-
-       def innerRadius(self):
-               return self._filing.innerRadius()
-
-       def setInnerRadius(self, radius):
-               self._filing.setInnerRadius(radius)
-
-       def outerRadius(self):
-               return self._filing.outerRadius()
-
-       def setOuterRadius(self, radius):
-               self._filing.setOuterRadius(radius)
-
-       def buttonRadius(self):
-               return self._buttonFiling.outerRadius()
-
-       def setButtonRadius(self, radius):
-               self._buttonFiling.setOuterRadius(radius)
-               self._buttonFiling.setInnerRadius(radius / 2)
-               self._buttonArtist.show(self.palette())
-
-       def minimumSizeHint(self):
-               return self._buttonArtist.centerSize()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def mousePressEvent(self, mouseEvent):
-               lastSelection = self._selectionIndex
-
-               lastMousePos = mouseEvent.pos()
-               self._mousePosition = lastMousePos
-               self._update_selection(self._cachedCenterPosition)
-
-               self.highlighted.emit(self._selectionIndex)
-
-               self._display.selectAt(self._selectionIndex)
-               self._pressed = True
-               self.update()
-               self._popupLocation = mouseEvent.globalPos()
-               self._delayPopupTimer.start()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_delayed_popup(self):
-               assert self._popupLocation is not None, "Widget location abuse"
-               self._popup_child(self._popupLocation)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def mouseMoveEvent(self, mouseEvent):
-               lastSelection = self._selectionIndex
-
-               lastMousePos = mouseEvent.pos()
-               if self._mousePosition is None:
-                       # Absolute
-                       self._update_selection(lastMousePos)
-               else:
-                       # Relative
-                       self._update_selection(
-                               self._cachedCenterPosition + (lastMousePos - self._mousePosition),
-                               ignoreOuter = True,
-                       )
-
-               if lastSelection != self._selectionIndex:
-                       self.highlighted.emit(self._selectionIndex)
-                       self._display.selectAt(self._selectionIndex)
-
-               if self._selectionIndex != PieFiling.SELECTION_CENTER and self._delayPopupTimer.isActive():
-                       self._on_delayed_popup()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def mouseReleaseEvent(self, mouseEvent):
-               self._delayPopupTimer.stop()
-               self._popupLocation = None
-
-               lastSelection = self._selectionIndex
-
-               lastMousePos = mouseEvent.pos()
-               if self._mousePosition is None:
-                       # Absolute
-                       self._update_selection(lastMousePos)
-               else:
-                       # Relative
-                       self._update_selection(
-                               self._cachedCenterPosition + (lastMousePos - self._mousePosition),
-                               ignoreOuter = True,
-                       )
-               self._mousePosition = None
-
-               self._activate_at(self._selectionIndex)
-               self._pressed = False
-               self.update()
-               self._hide_child()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def keyPressEvent(self, keyEvent):
-               if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
-                       self._popup_child(QtGui.QCursor.pos())
-                       if self._selectionIndex != len(self._filing) - 1:
-                               nextSelection = self._selectionIndex + 1
-                       else:
-                               nextSelection = 0
-                       self._select_at(nextSelection)
-                       self._display.selectAt(self._selectionIndex)
-               elif keyEvent.key() in [QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab]:
-                       self._popup_child(QtGui.QCursor.pos())
-                       if 0 < self._selectionIndex:
-                               nextSelection = self._selectionIndex - 1
-                       else:
-                               nextSelection = len(self._filing) - 1
-                       self._select_at(nextSelection)
-                       self._display.selectAt(self._selectionIndex)
-               elif keyEvent.key() in [QtCore.Qt.Key_Space]:
-                       self._popup_child(QtGui.QCursor.pos())
-                       self._select_at(PieFiling.SELECTION_CENTER)
-                       self._display.selectAt(self._selectionIndex)
-               elif keyEvent.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space]:
-                       self._delayPopupTimer.stop()
-                       self._popupLocation = None
-                       self._activate_at(self._selectionIndex)
-                       self._hide_child()
-               elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]:
-                       self._delayPopupTimer.stop()
-                       self._popupLocation = None
-                       self._activate_at(PieFiling.SELECTION_NONE)
-                       self._hide_child()
-               else:
-                       QtGui.QWidget.keyPressEvent(self, keyEvent)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def resizeEvent(self, resizeEvent):
-               self.setButtonRadius(min(resizeEvent.size().width(), resizeEvent.size().height()) / 2 - 1)
-               QtGui.QWidget.resizeEvent(self, resizeEvent)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def showEvent(self, showEvent):
-               self._buttonArtist.show(self.palette())
-               self._cachedCenterPosition = self.rect().center()
-
-               QtGui.QWidget.showEvent(self, showEvent)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def hideEvent(self, hideEvent):
-               self._display.hide()
-               self._select_at(PieFiling.SELECTION_NONE)
-               QtGui.QWidget.hideEvent(self, hideEvent)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def paintEvent(self, paintEvent):
-               self.setButtonRadius(min(self.rect().width(), self.rect().height()) / 2 - 1)
-               if self._poppedUp:
-                       selectionIndex = PieFiling.SELECTION_CENTER
-               else:
-                       selectionIndex = PieFiling.SELECTION_NONE
-
-               screen = QtGui.QStylePainter(self)
-               screen.setRenderHint(QtGui.QPainter.Antialiasing, True)
-               option = QtGui.QStyleOptionButton()
-               option.initFrom(self)
-               option.state = QtGui.QStyle.State_Sunken if self._pressed else QtGui.QStyle.State_Raised
-
-               screen.drawControl(QtGui.QStyle.CE_PushButton, option)
-               self._buttonArtist.paintPainter(selectionIndex, screen)
-
-               QtGui.QWidget.paintEvent(self, paintEvent)
-
-       def __iter__(self):
-               return iter(self._filing)
-
-       def __len__(self):
-               return len(self._filing)
-
-       def _popup_child(self, position):
-               self._poppedUp = True
-               self.aboutToShow.emit()
-
-               self._delayPopupTimer.stop()
-               self._popupLocation = None
-
-               position = position - QtCore.QPoint(self._filing.outerRadius(), self._filing.outerRadius())
-               self._display.move(position)
-               self._display.show()
-
-               self.update()
-
-       def _hide_child(self):
-               self._poppedUp = False
-               self.aboutToHide.emit()
-               self._display.hide()
-               self.update()
-
-       def _select_at(self, index):
-               self._selectionIndex = index
-
-       def _update_selection(self, lastMousePos, ignoreOuter = False):
-               radius = _radius_at(self._cachedCenterPosition, lastMousePos)
-               if radius < self._filing.innerRadius():
-                       self._select_at(PieFiling.SELECTION_CENTER)
-               elif radius <= self._filing.outerRadius() or ignoreOuter:
-                       self._select_at(self.indexAt(lastMousePos))
-               else:
-                       self._select_at(PieFiling.SELECTION_NONE)
-
-       def _activate_at(self, index):
-               if index == PieFiling.SELECTION_NONE:
-                       self.canceled.emit()
-                       return
-               elif index == PieFiling.SELECTION_CENTER:
-                       child = self._filing.center()
-               else:
-                       child = self.itemAt(index)
-
-               if child.action().isEnabled():
-                       child.action().trigger()
-                       self.activated.emit(index)
-               else:
-                       self.canceled.emit()
-
-
-class QPieMenu(QtGui.QWidget):
-
-       activated = qt_compat.Signal(int)
-       highlighted = qt_compat.Signal(int)
-       canceled = qt_compat.Signal()
-       aboutToShow = qt_compat.Signal()
-       aboutToHide = qt_compat.Signal()
-
-       def __init__(self, parent = None):
-               QtGui.QWidget.__init__(self, parent)
-               self._cachedCenterPosition = self.rect().center()
-
-               self._filing = PieFiling()
-               self._artist = PieArtist(self._filing)
-               self._selectionIndex = PieFiling.SELECTION_NONE
-
-               self._mousePosition = ()
-               self.setFocusPolicy(QtCore.Qt.StrongFocus)
-
-       def popup(self, pos):
-               self._update_selection(pos)
-               self.show()
-
-       def insertItem(self, item, index = -1):
-               self._filing.insertItem(item, index)
-               self.update()
-
-       def removeItemAt(self, index):
-               self._filing.removeItemAt(index)
-               self.update()
-
-       def set_center(self, item):
-               self._filing.set_center(item)
-               self.update()
-
-       def clear(self):
-               self._filing.clear()
-               self.update()
-
-       def itemAt(self, index):
-               return self._filing.itemAt(index)
-
-       def indexAt(self, point):
-               return self._filing.indexAt(self._cachedCenterPosition, point)
-
-       def innerRadius(self):
-               return self._filing.innerRadius()
-
-       def setInnerRadius(self, radius):
-               self._filing.setInnerRadius(radius)
-               self.update()
-
-       def outerRadius(self):
-               return self._filing.outerRadius()
-
-       def setOuterRadius(self, radius):
-               self._filing.setOuterRadius(radius)
-               self.update()
-
-       def sizeHint(self):
-               return self._artist.pieSize()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def mousePressEvent(self, mouseEvent):
-               lastSelection = self._selectionIndex
-
-               lastMousePos = mouseEvent.pos()
-               self._update_selection(lastMousePos)
-               self._mousePosition = lastMousePos
-
-               if lastSelection != self._selectionIndex:
-                       self.highlighted.emit(self._selectionIndex)
-                       self.update()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def mouseMoveEvent(self, mouseEvent):
-               lastSelection = self._selectionIndex
-
-               lastMousePos = mouseEvent.pos()
-               self._update_selection(lastMousePos)
-
-               if lastSelection != self._selectionIndex:
-                       self.highlighted.emit(self._selectionIndex)
-                       self.update()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def mouseReleaseEvent(self, mouseEvent):
-               lastSelection = self._selectionIndex
-
-               lastMousePos = mouseEvent.pos()
-               self._update_selection(lastMousePos)
-               self._mousePosition = ()
-
-               self._activate_at(self._selectionIndex)
-               self.update()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def keyPressEvent(self, keyEvent):
-               if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
-                       if self._selectionIndex != len(self._filing) - 1:
-                               nextSelection = self._selectionIndex + 1
-                       else:
-                               nextSelection = 0
-                       self._select_at(nextSelection)
-                       self.update()
-               elif keyEvent.key() in [QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab]:
-                       if 0 < self._selectionIndex:
-                               nextSelection = self._selectionIndex - 1
-                       else:
-                               nextSelection = len(self._filing) - 1
-                       self._select_at(nextSelection)
-                       self.update()
-               elif keyEvent.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space]:
-                       self._activate_at(self._selectionIndex)
-               elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]:
-                       self._activate_at(PieFiling.SELECTION_NONE)
-               else:
-                       QtGui.QWidget.keyPressEvent(self, keyEvent)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def showEvent(self, showEvent):
-               self.aboutToShow.emit()
-               self._cachedCenterPosition = self.rect().center()
-
-               mask = self._artist.show(self.palette())
-               self.setMask(mask)
-
-               lastMousePos = self.mapFromGlobal(QtGui.QCursor.pos())
-               self._update_selection(lastMousePos)
-
-               QtGui.QWidget.showEvent(self, showEvent)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def hideEvent(self, hideEvent):
-               self._artist.hide()
-               self._selectionIndex = PieFiling.SELECTION_NONE
-               QtGui.QWidget.hideEvent(self, hideEvent)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def paintEvent(self, paintEvent):
-               canvas = self._artist.paint(self._selectionIndex)
-
-               screen = QtGui.QPainter(self)
-               screen.drawPixmap(QtCore.QPoint(0, 0), canvas)
-
-               QtGui.QWidget.paintEvent(self, paintEvent)
-
-       def __iter__(self):
-               return iter(self._filing)
-
-       def __len__(self):
-               return len(self._filing)
-
-       def _select_at(self, index):
-               self._selectionIndex = index
-
-       def _update_selection(self, lastMousePos):
-               radius = _radius_at(self._cachedCenterPosition, lastMousePos)
-               if radius < self._filing.innerRadius():
-                       self._selectionIndex = PieFiling.SELECTION_CENTER
-               elif radius <= self._filing.outerRadius():
-                       self._select_at(self.indexAt(lastMousePos))
-               else:
-                       self._selectionIndex = PieFiling.SELECTION_NONE
-
-       def _activate_at(self, index):
-               if index == PieFiling.SELECTION_NONE:
-                       self.canceled.emit()
-                       self.aboutToHide.emit()
-                       self.hide()
-                       return
-               elif index == PieFiling.SELECTION_CENTER:
-                       child = self._filing.center()
-               else:
-                       child = self.itemAt(index)
-
-               if child.isEnabled():
-                       child.action().trigger()
-                       self.activated.emit(index)
-               else:
-                       self.canceled.emit()
-               self.aboutToHide.emit()
-               self.hide()
-
-
-def init_pies():
-       PieFiling.NULL_CENTER.setEnabled(False)
-
-
-def _print(msg):
-       print msg
-
-
-def _on_about_to_hide(app):
-       app.exit()
-
-
-if __name__ == "__main__":
-       app = QtGui.QApplication([])
-       init_pies()
-
-       if False:
-               pie = QPieMenu()
-               pie.show()
-
-       if False:
-               singleAction = QtGui.QAction(None)
-               singleAction.setText("Boo")
-               singleItem = QActionPieItem(singleAction)
-               spie = QPieMenu()
-               spie.insertItem(singleItem)
-               spie.show()
-
-       if False:
-               oneAction = QtGui.QAction(None)
-               oneAction.setText("Chew")
-               oneItem = QActionPieItem(oneAction)
-               twoAction = QtGui.QAction(None)
-               twoAction.setText("Foo")
-               twoItem = QActionPieItem(twoAction)
-               iconTextAction = QtGui.QAction(None)
-               iconTextAction.setText("Icon")
-               iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
-               iconTextItem = QActionPieItem(iconTextAction)
-               mpie = QPieMenu()
-               mpie.insertItem(oneItem)
-               mpie.insertItem(twoItem)
-               mpie.insertItem(oneItem)
-               mpie.insertItem(iconTextItem)
-               mpie.show()
-
-       if True:
-               oneAction = QtGui.QAction(None)
-               oneAction.setText("Chew")
-               oneAction.triggered.connect(lambda: _print("Chew"))
-               oneItem = QActionPieItem(oneAction)
-               twoAction = QtGui.QAction(None)
-               twoAction.setText("Foo")
-               twoAction.triggered.connect(lambda: _print("Foo"))
-               twoItem = QActionPieItem(twoAction)
-               iconAction = QtGui.QAction(None)
-               iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
-               iconAction.triggered.connect(lambda: _print("Icon"))
-               iconItem = QActionPieItem(iconAction)
-               iconTextAction = QtGui.QAction(None)
-               iconTextAction.setText("Icon")
-               iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
-               iconTextAction.triggered.connect(lambda: _print("Icon and text"))
-               iconTextItem = QActionPieItem(iconTextAction)
-               mpie = QPieMenu()
-               mpie.set_center(iconItem)
-               mpie.insertItem(oneItem)
-               mpie.insertItem(twoItem)
-               mpie.insertItem(oneItem)
-               mpie.insertItem(iconTextItem)
-               mpie.show()
-               mpie.aboutToHide.connect(lambda: _on_about_to_hide(app))
-               mpie.canceled.connect(lambda: _print("Canceled"))
-
-       if False:
-               oneAction = QtGui.QAction(None)
-               oneAction.setText("Chew")
-               oneAction.triggered.connect(lambda: _print("Chew"))
-               oneItem = QActionPieItem(oneAction)
-               twoAction = QtGui.QAction(None)
-               twoAction.setText("Foo")
-               twoAction.triggered.connect(lambda: _print("Foo"))
-               twoItem = QActionPieItem(twoAction)
-               iconAction = QtGui.QAction(None)
-               iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
-               iconAction.triggered.connect(lambda: _print("Icon"))
-               iconItem = QActionPieItem(iconAction)
-               iconTextAction = QtGui.QAction(None)
-               iconTextAction.setText("Icon")
-               iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
-               iconTextAction.triggered.connect(lambda: _print("Icon and text"))
-               iconTextItem = QActionPieItem(iconTextAction)
-               pieFiling = PieFiling()
-               pieFiling.set_center(iconItem)
-               pieFiling.insertItem(oneItem)
-               pieFiling.insertItem(twoItem)
-               pieFiling.insertItem(oneItem)
-               pieFiling.insertItem(iconTextItem)
-               mpie = QPieDisplay(pieFiling)
-               mpie.show()
-
-       if False:
-               oneAction = QtGui.QAction(None)
-               oneAction.setText("Chew")
-               oneAction.triggered.connect(lambda: _print("Chew"))
-               oneItem = QActionPieItem(oneAction)
-               twoAction = QtGui.QAction(None)
-               twoAction.setText("Foo")
-               twoAction.triggered.connect(lambda: _print("Foo"))
-               twoItem = QActionPieItem(twoAction)
-               iconAction = QtGui.QAction(None)
-               iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
-               iconAction.triggered.connect(lambda: _print("Icon"))
-               iconItem = QActionPieItem(iconAction)
-               iconTextAction = QtGui.QAction(None)
-               iconTextAction.setText("Icon")
-               iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
-               iconTextAction.triggered.connect(lambda: _print("Icon and text"))
-               iconTextItem = QActionPieItem(iconTextAction)
-               mpie = QPieButton(iconItem)
-               mpie.set_center(iconItem)
-               mpie.insertItem(oneItem)
-               mpie.insertItem(twoItem)
-               mpie.insertItem(oneItem)
-               mpie.insertItem(iconTextItem)
-               mpie.show()
-               mpie.aboutToHide.connect(lambda: _on_about_to_hide(app))
-               mpie.canceled.connect(lambda: _print("Canceled"))
-
-       app.exec_()
diff --git a/src/util/qtpieboard.py b/src/util/qtpieboard.py
deleted file mode 100755 (executable)
index 50ae9ae..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-#!/usr/bin/env python
-
-
-from __future__ import division
-
-import os
-import warnings
-
-import qt_compat
-QtGui = qt_compat.import_module("QtGui")
-
-import qtpie
-
-
-class PieKeyboard(object):
-
-       SLICE_CENTER = -1
-       SLICE_NORTH = 0
-       SLICE_NORTH_WEST = 1
-       SLICE_WEST = 2
-       SLICE_SOUTH_WEST = 3
-       SLICE_SOUTH = 4
-       SLICE_SOUTH_EAST = 5
-       SLICE_EAST = 6
-       SLICE_NORTH_EAST = 7
-
-       MAX_ANGULAR_SLICES = 8
-
-       SLICE_DIRECTIONS = [
-               SLICE_CENTER,
-               SLICE_NORTH,
-               SLICE_NORTH_WEST,
-               SLICE_WEST,
-               SLICE_SOUTH_WEST,
-               SLICE_SOUTH,
-               SLICE_SOUTH_EAST,
-               SLICE_EAST,
-               SLICE_NORTH_EAST,
-       ]
-
-       SLICE_DIRECTION_NAMES = [
-               "CENTER",
-               "NORTH",
-               "NORTH_WEST",
-               "WEST",
-               "SOUTH_WEST",
-               "SOUTH",
-               "SOUTH_EAST",
-               "EAST",
-               "NORTH_EAST",
-       ]
-
-       def __init__(self):
-               self._layout = QtGui.QGridLayout()
-               self._widget = QtGui.QWidget()
-               self._widget.setLayout(self._layout)
-
-               self.__cells = {}
-
-       @property
-       def toplevel(self):
-               return self._widget
-
-       def add_pie(self, row, column, pieButton):
-               assert len(pieButton) == 8
-               self._layout.addWidget(pieButton, row, column)
-               self.__cells[(row, column)] = pieButton
-
-       def get_pie(self, row, column):
-               return self.__cells[(row, column)]
-
-
-class KeyboardModifier(object):
-
-       def __init__(self, name):
-               self.name = name
-               self.lock = False
-               self.once = False
-
-       @property
-       def isActive(self):
-               return self.lock or self.once
-
-       def on_toggle_lock(self, *args, **kwds):
-               self.lock = not self.lock
-
-       def on_toggle_once(self, *args, **kwds):
-               self.once = not self.once
-
-       def reset_once(self):
-               self.once = False
-
-
-def parse_keyboard_data(text):
-       return eval(text)
-
-
-def _enumerate_pie_slices(pieData, iconPaths):
-       for direction, directionName in zip(
-               PieKeyboard.SLICE_DIRECTIONS, PieKeyboard.SLICE_DIRECTION_NAMES
-       ):
-               if directionName in pieData:
-                       sliceData = pieData[directionName]
-
-                       action = QtGui.QAction(None)
-                       try:
-                               action.setText(sliceData["text"])
-                       except KeyError:
-                               pass
-                       try:
-                               relativeIconPath = sliceData["path"]
-                       except KeyError:
-                               pass
-                       else:
-                               for iconPath in iconPaths:
-                                       absIconPath = os.path.join(iconPath, relativeIconPath)
-                                       if os.path.exists(absIconPath):
-                                               action.setIcon(QtGui.QIcon(absIconPath))
-                                               break
-                       pieItem = qtpie.QActionPieItem(action)
-                       actionToken = sliceData["action"]
-               else:
-                       pieItem = qtpie.PieFiling.NULL_CENTER
-                       actionToken = ""
-               yield direction, pieItem, actionToken
-
-
-def load_keyboard(keyboardName, dataTree, keyboard, keyboardHandler, iconPaths):
-       for (row, column), pieData in dataTree.iteritems():
-               pieItems = list(_enumerate_pie_slices(pieData, iconPaths))
-               assert pieItems[0][0] == PieKeyboard.SLICE_CENTER, pieItems[0]
-               _, center, centerAction = pieItems.pop(0)
-
-               pieButton = qtpie.QPieButton(center)
-               pieButton.set_center(center)
-               keyboardHandler.map_slice_action(center, centerAction)
-               for direction, pieItem, action in pieItems:
-                       pieButton.insertItem(pieItem)
-                       keyboardHandler.map_slice_action(pieItem, action)
-               keyboard.add_pie(row, column, pieButton)
-
-
-class KeyboardHandler(object):
-
-       def __init__(self, keyhandler):
-               self.__keyhandler = keyhandler
-               self.__commandHandlers = {}
-               self.__modifiers = {}
-               self.__sliceActions = {}
-
-               self.register_modifier("Shift")
-               self.register_modifier("Super")
-               self.register_modifier("Control")
-               self.register_modifier("Alt")
-
-       def register_command_handler(self, command, handler):
-               # @todo Look into hooking these up directly to the pie actions
-               self.__commandHandlers["[%s]" % command] = handler
-
-       def unregister_command_handler(self, command):
-               # @todo Look into hooking these up directly to the pie actions
-               del self.__commandHandlers["[%s]" % command]
-
-       def register_modifier(self, modifierName):
-               mod = KeyboardModifier(modifierName)
-               self.register_command_handler(modifierName, mod.on_toggle_lock)
-               self.__modifiers["<%s>" % modifierName] = mod
-
-       def unregister_modifier(self, modifierName):
-               self.unregister_command_handler(modifierName)
-               del self.__modifiers["<%s>" % modifierName]
-
-       def map_slice_action(self, slice, action):
-               callback = lambda direction: self(direction, action)
-               slice.action().triggered.connect(callback)
-               self.__sliceActions[slice] = (action, callback)
-
-       def __call__(self, direction, action):
-               activeModifiers = [
-                       mod.name
-                       for mod in self.__modifiers.itervalues()
-                               if mod.isActive
-               ]
-
-               needResetOnce = False
-               if action.startswith("[") and action.endswith("]"):
-                       commandName = action[1:-1]
-                       if action in self.__commandHandlers:
-                               self.__commandHandlers[action](commandName, activeModifiers)
-                               needResetOnce = True
-                       else:
-                               warnings.warn("Unknown command: [%s]" % commandName)
-               elif action.startswith("<") and action.endswith(">"):
-                       modName = action[1:-1]
-                       for mod in self.__modifiers.itervalues():
-                               if mod.name == modName:
-                                       mod.on_toggle_once()
-                                       break
-                       else:
-                               warnings.warn("Unknown modifier: <%s>" % modName)
-               else:
-                       self.__keyhandler(action, activeModifiers)
-                       needResetOnce = True
-
-               if needResetOnce:
-                       for mod in self.__modifiers.itervalues():
-                               mod.reset_once()
diff --git a/src/util/qui_utils.py b/src/util/qui_utils.py
deleted file mode 100644 (file)
index 11b3453..0000000
+++ /dev/null
@@ -1,419 +0,0 @@
-import sys
-import contextlib
-import datetime
-import logging
-
-import qt_compat
-QtCore = qt_compat.QtCore
-QtGui = qt_compat.import_module("QtGui")
-
-import misc
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-@contextlib.contextmanager
-def notify_error(log):
-       try:
-               yield
-       except:
-               log.push_exception()
-
-
-@contextlib.contextmanager
-def notify_busy(log, message):
-       log.push_busy(message)
-       try:
-               yield
-       finally:
-               log.pop(message)
-
-
-class ErrorMessage(object):
-
-       LEVEL_ERROR = 0
-       LEVEL_BUSY = 1
-       LEVEL_INFO = 2
-
-       def __init__(self, message, level):
-               self._message = message
-               self._level = level
-               self._time = datetime.datetime.now()
-
-       @property
-       def level(self):
-               return self._level
-
-       @property
-       def message(self):
-               return self._message
-
-       def __repr__(self):
-               return "%s.%s(%r, %r)" % (__name__, self.__class__.__name__, self._message, self._level)
-
-
-class QErrorLog(QtCore.QObject):
-
-       messagePushed = qt_compat.Signal()
-       messagePopped = qt_compat.Signal()
-
-       def __init__(self):
-               QtCore.QObject.__init__(self)
-               self._messages = []
-
-       def push_busy(self, message):
-               _moduleLogger.info("Entering state: %s" % message)
-               self._push_message(message, ErrorMessage.LEVEL_BUSY)
-
-       def push_message(self, message):
-               self._push_message(message, ErrorMessage.LEVEL_INFO)
-
-       def push_error(self, message):
-               self._push_message(message, ErrorMessage.LEVEL_ERROR)
-
-       def push_exception(self):
-               userMessage = str(sys.exc_info()[1])
-               _moduleLogger.exception(userMessage)
-               self.push_error(userMessage)
-
-       def pop(self, message = None):
-               if message is None:
-                       del self._messages[0]
-               else:
-                       _moduleLogger.info("Exiting state: %s" % message)
-                       messageIndex = [
-                               i
-                               for (i, error) in enumerate(self._messages)
-                               if error.message == message
-                       ]
-                       # Might be removed out of order
-                       if messageIndex:
-                               del self._messages[messageIndex[0]]
-               self.messagePopped.emit()
-
-       def peek_message(self):
-               return self._messages[0]
-
-       def _push_message(self, message, level):
-               self._messages.append(ErrorMessage(message, level))
-               # Sort is defined as stable, so this should be fine
-               self._messages.sort(key=lambda x: x.level)
-               self.messagePushed.emit()
-
-       def __len__(self):
-               return len(self._messages)
-
-
-class ErrorDisplay(object):
-
-       _SENTINEL_ICON = QtGui.QIcon()
-
-       def __init__(self, errorLog):
-               self._errorLog = errorLog
-               self._errorLog.messagePushed.connect(self._on_message_pushed)
-               self._errorLog.messagePopped.connect(self._on_message_popped)
-
-               self._icons = None
-               self._severityLabel = QtGui.QLabel()
-               self._severityLabel.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-               self._message = QtGui.QLabel()
-               self._message.setText("Boo")
-               self._message.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-               self._message.setWordWrap(True)
-
-               self._closeLabel = None
-
-               self._controlLayout = QtGui.QHBoxLayout()
-               self._controlLayout.addWidget(self._severityLabel, 1, QtCore.Qt.AlignCenter)
-               self._controlLayout.addWidget(self._message, 1000)
-
-               self._widget = QtGui.QWidget()
-               self._widget.setLayout(self._controlLayout)
-               self._widget.hide()
-
-       @property
-       def toplevel(self):
-               return self._widget
-
-       def _show_error(self):
-               if self._icons is None:
-                       self._icons = {
-                               ErrorMessage.LEVEL_BUSY:
-                                       get_theme_icon(
-                                               #("process-working", "view-refresh", "general_refresh", "gtk-refresh")
-                                               ("view-refresh", "general_refresh", "gtk-refresh", )
-                                       ).pixmap(32, 32),
-                               ErrorMessage.LEVEL_INFO:
-                                       get_theme_icon(
-                                               ("dialog-information", "general_notes", "gtk-info")
-                                       ).pixmap(32, 32),
-                               ErrorMessage.LEVEL_ERROR:
-                                       get_theme_icon(
-                                               ("dialog-error", "app_install_error", "gtk-dialog-error")
-                                       ).pixmap(32, 32),
-                       }
-               if self._closeLabel is None:
-                       closeIcon = get_theme_icon(("window-close", "general_close", "gtk-close"), self._SENTINEL_ICON)
-                       if closeIcon is not self._SENTINEL_ICON:
-                               self._closeLabel = QtGui.QPushButton(closeIcon, "")
-                       else:
-                               self._closeLabel = QtGui.QPushButton("X")
-                       self._closeLabel.clicked.connect(self._on_close)
-                       self._controlLayout.addWidget(self._closeLabel, 1, QtCore.Qt.AlignCenter)
-               error = self._errorLog.peek_message()
-               self._message.setText(error.message)
-               self._severityLabel.setPixmap(self._icons[error.level])
-               self._widget.show()
-
-       @qt_compat.Slot()
-       @qt_compat.Slot(bool)
-       @misc.log_exception(_moduleLogger)
-       def _on_close(self, checked = False):
-               self._errorLog.pop()
-
-       @qt_compat.Slot()
-       @misc.log_exception(_moduleLogger)
-       def _on_message_pushed(self):
-               self._show_error()
-
-       @qt_compat.Slot()
-       @misc.log_exception(_moduleLogger)
-       def _on_message_popped(self):
-               if len(self._errorLog) == 0:
-                       self._message.setText("")
-                       self._widget.hide()
-               else:
-                       self._show_error()
-
-
-class QHtmlDelegate(QtGui.QStyledItemDelegate):
-
-       UNDEFINED_SIZE = -1
-
-       def __init__(self, *args, **kwd):
-               QtGui.QStyledItemDelegate.__init__(*((self, ) + args), **kwd)
-               self._width = self.UNDEFINED_SIZE
-
-       def paint(self, painter, option, index):
-               newOption = QtGui.QStyleOptionViewItemV4(option)
-               self.initStyleOption(newOption, index)
-               if newOption.widget is not None:
-                       style = newOption.widget.style()
-               else:
-                       style = QtGui.QApplication.style()
-
-               doc = QtGui.QTextDocument()
-               doc.setHtml(newOption.text)
-               doc.setTextWidth(newOption.rect.width())
-
-               newOption.text = ""
-               style.drawControl(QtGui.QStyle.CE_ItemViewItem, newOption, painter)
-
-               ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
-               if newOption.state & QtGui.QStyle.State_Selected:
-                       ctx.palette.setColor(
-                               QtGui.QPalette.Text,
-                               newOption.palette.color(
-                                       QtGui.QPalette.Active,
-                                       QtGui.QPalette.HighlightedText
-                               )
-                       )
-               else:
-                       ctx.palette.setColor(
-                               QtGui.QPalette.Text,
-                               newOption.palette.color(
-                                       QtGui.QPalette.Active,
-                                       QtGui.QPalette.Text
-                               )
-                       )
-
-               textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, newOption)
-               painter.save()
-               painter.translate(textRect.topLeft())
-               painter.setClipRect(textRect.translated(-textRect.topLeft()))
-               doc.documentLayout().draw(painter, ctx)
-               painter.restore()
-
-       def setWidth(self, width, model):
-               if self._width == width:
-                       return
-               self._width = width
-               for c in xrange(model.rowCount()):
-                       cItem = model.item(c, 0)
-                       for r in xrange(model.rowCount()):
-                               rItem = cItem.child(r, 0)
-                               rIndex = model.indexFromItem(rItem)
-                               self.sizeHintChanged.emit(rIndex)
-                               return
-
-       def sizeHint(self, option, index):
-               newOption = QtGui.QStyleOptionViewItemV4(option)
-               self.initStyleOption(newOption, index)
-
-               doc = QtGui.QTextDocument()
-               doc.setHtml(newOption.text)
-               if self._width != self.UNDEFINED_SIZE:
-                       width = self._width
-               else:
-                       width = newOption.rect.width()
-               doc.setTextWidth(width)
-               size = QtCore.QSize(doc.idealWidth(), doc.size().height())
-               return size
-
-
-class QSignalingMainWindow(QtGui.QMainWindow):
-
-       closed = qt_compat.Signal()
-       hidden = qt_compat.Signal()
-       shown = qt_compat.Signal()
-       resized = qt_compat.Signal()
-
-       def __init__(self, *args, **kwd):
-               QtGui.QMainWindow.__init__(*((self, )+args), **kwd)
-
-       def closeEvent(self, event):
-               val = QtGui.QMainWindow.closeEvent(self, event)
-               self.closed.emit()
-               return val
-
-       def hideEvent(self, event):
-               val = QtGui.QMainWindow.hideEvent(self, event)
-               self.hidden.emit()
-               return val
-
-       def showEvent(self, event):
-               val = QtGui.QMainWindow.showEvent(self, event)
-               self.shown.emit()
-               return val
-
-       def resizeEvent(self, event):
-               val = QtGui.QMainWindow.resizeEvent(self, event)
-               self.resized.emit()
-               return val
-
-def set_current_index(selector, itemText, default = 0):
-       for i in xrange(selector.count()):
-               if selector.itemText(i) == itemText:
-                       selector.setCurrentIndex(i)
-                       break
-       else:
-               itemText.setCurrentIndex(default)
-
-
-def _null_set_stackable(window, isStackable):
-       pass
-
-
-def _maemo_set_stackable(window, isStackable):
-       window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable)
-
-
-try:
-       QtCore.Qt.WA_Maemo5StackedWindow
-       set_stackable = _maemo_set_stackable
-except AttributeError:
-       set_stackable = _null_set_stackable
-
-
-def _null_set_autorient(window, doAutoOrient):
-       pass
-
-
-def _maemo_set_autorient(window, doAutoOrient):
-       window.setAttribute(QtCore.Qt.WA_Maemo5AutoOrientation, doAutoOrient)
-
-
-try:
-       QtCore.Qt.WA_Maemo5AutoOrientation
-       set_autorient = _maemo_set_autorient
-except AttributeError:
-       set_autorient = _null_set_autorient
-
-
-def screen_orientation():
-       geom = QtGui.QApplication.desktop().screenGeometry()
-       if geom.width() <= geom.height():
-               return QtCore.Qt.Vertical
-       else:
-               return QtCore.Qt.Horizontal
-
-
-def _null_set_window_orientation(window, orientation):
-       pass
-
-
-def _maemo_set_window_orientation(window, orientation):
-       if orientation == QtCore.Qt.Vertical:
-               window.setAttribute(QtCore.Qt.WA_Maemo5LandscapeOrientation, False)
-               window.setAttribute(QtCore.Qt.WA_Maemo5PortraitOrientation, True)
-       elif orientation == QtCore.Qt.Horizontal:
-               window.setAttribute(QtCore.Qt.WA_Maemo5LandscapeOrientation, True)
-               window.setAttribute(QtCore.Qt.WA_Maemo5PortraitOrientation, False)
-       elif orientation is None:
-               window.setAttribute(QtCore.Qt.WA_Maemo5LandscapeOrientation, False)
-               window.setAttribute(QtCore.Qt.WA_Maemo5PortraitOrientation, False)
-       else:
-               raise RuntimeError("Unknown orientation: %r" % orientation)
-
-
-try:
-       QtCore.Qt.WA_Maemo5LandscapeOrientation
-       QtCore.Qt.WA_Maemo5PortraitOrientation
-       set_window_orientation = _maemo_set_window_orientation
-except AttributeError:
-       set_window_orientation = _null_set_window_orientation
-
-
-def _null_show_progress_indicator(window, isStackable):
-       pass
-
-
-def _maemo_show_progress_indicator(window, isStackable):
-       window.setAttribute(QtCore.Qt.WA_Maemo5ShowProgressIndicator, isStackable)
-
-
-try:
-       QtCore.Qt.WA_Maemo5ShowProgressIndicator
-       show_progress_indicator = _maemo_show_progress_indicator
-except AttributeError:
-       show_progress_indicator = _null_show_progress_indicator
-
-
-def _null_mark_numbers_preferred(widget):
-       pass
-
-
-def _newqt_mark_numbers_preferred(widget):
-       widget.setInputMethodHints(QtCore.Qt.ImhPreferNumbers)
-
-
-try:
-       QtCore.Qt.ImhPreferNumbers
-       mark_numbers_preferred = _newqt_mark_numbers_preferred
-except AttributeError:
-       mark_numbers_preferred = _null_mark_numbers_preferred
-
-
-def _null_get_theme_icon(iconNames, fallback = None):
-       icon = fallback if fallback is not None else QtGui.QIcon()
-       return icon
-
-
-def _newqt_get_theme_icon(iconNames, fallback = None):
-       for iconName in iconNames:
-               if QtGui.QIcon.hasThemeIcon(iconName):
-                       icon = QtGui.QIcon.fromTheme(iconName)
-                       break
-       else:
-               icon = fallback if fallback is not None else QtGui.QIcon()
-       return icon
-
-
-try:
-       QtGui.QIcon.fromTheme
-       get_theme_icon = _newqt_get_theme_icon
-except AttributeError:
-       get_theme_icon = _null_get_theme_icon
-
diff --git a/src/util/qwrappers.py b/src/util/qwrappers.py
deleted file mode 100644 (file)
index 2c50c8a..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import with_statement
-from __future__ import division
-
-import logging
-
-import qt_compat
-QtCore = qt_compat.QtCore
-QtGui = qt_compat.import_module("QtGui")
-
-from util import qui_utils
-from util import misc as misc_utils
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class ApplicationWrapper(object):
-
-       DEFAULT_ORIENTATION = "Default"
-       AUTO_ORIENTATION = "Auto"
-       LANDSCAPE_ORIENTATION = "Landscape"
-       PORTRAIT_ORIENTATION = "Portrait"
-
-       def __init__(self, qapp, constants):
-               self._constants = constants
-               self._qapp = qapp
-               self._clipboard = QtGui.QApplication.clipboard()
-
-               self._errorLog = qui_utils.QErrorLog()
-               self._mainWindow = None
-
-               self._fullscreenAction = QtGui.QAction(None)
-               self._fullscreenAction.setText("Fullscreen")
-               self._fullscreenAction.setCheckable(True)
-               self._fullscreenAction.setShortcut(QtGui.QKeySequence("CTRL+Enter"))
-               self._fullscreenAction.toggled.connect(self._on_toggle_fullscreen)
-
-               self._orientation = self.DEFAULT_ORIENTATION
-               self._orientationAction = QtGui.QAction(None)
-               self._orientationAction.setText("Next Orientation")
-               self._orientationAction.setCheckable(True)
-               self._orientationAction.setShortcut(QtGui.QKeySequence("CTRL+o"))
-               self._orientationAction.triggered.connect(self._on_next_orientation)
-
-               self._logAction = QtGui.QAction(None)
-               self._logAction.setText("Log")
-               self._logAction.setShortcut(QtGui.QKeySequence("CTRL+l"))
-               self._logAction.triggered.connect(self._on_log)
-
-               self._quitAction = QtGui.QAction(None)
-               self._quitAction.setText("Quit")
-               self._quitAction.setShortcut(QtGui.QKeySequence("CTRL+q"))
-               self._quitAction.triggered.connect(self._on_quit)
-
-               self._aboutAction = QtGui.QAction(None)
-               self._aboutAction.setText("About")
-               self._aboutAction.triggered.connect(self._on_about)
-
-               self._qapp.lastWindowClosed.connect(self._on_app_quit)
-               self._mainWindow = self._new_main_window()
-               self._mainWindow.window.destroyed.connect(self._on_child_close)
-
-               self.load_settings()
-
-               self._mainWindow.show()
-               self._idleDelay = QtCore.QTimer()
-               self._idleDelay.setSingleShot(True)
-               self._idleDelay.setInterval(0)
-               self._idleDelay.timeout.connect(self._on_delayed_start)
-               self._idleDelay.start()
-
-       def load_settings(self):
-               raise NotImplementedError("Booh")
-
-       def save_settings(self):
-               raise NotImplementedError("Booh")
-
-       def _new_main_window(self):
-               raise NotImplementedError("Booh")
-
-       @property
-       def qapp(self):
-               return self._qapp
-
-       @property
-       def constants(self):
-               return self._constants
-
-       @property
-       def errorLog(self):
-               return self._errorLog
-
-       @property
-       def fullscreenAction(self):
-               return self._fullscreenAction
-
-       @property
-       def orientationAction(self):
-               return self._orientationAction
-
-       @property
-       def orientation(self):
-               return self._orientation
-
-       @property
-       def logAction(self):
-               return self._logAction
-
-       @property
-       def aboutAction(self):
-               return self._aboutAction
-
-       @property
-       def quitAction(self):
-               return self._quitAction
-
-       def set_orientation(self, orientation):
-               self._orientation = orientation
-               self._mainWindow.update_orientation(self._orientation)
-
-       @classmethod
-       def _next_orientation(cls, current):
-               return {
-                       cls.DEFAULT_ORIENTATION: cls.AUTO_ORIENTATION,
-                       cls.AUTO_ORIENTATION: cls.LANDSCAPE_ORIENTATION,
-                       cls.LANDSCAPE_ORIENTATION: cls.PORTRAIT_ORIENTATION,
-                       cls.PORTRAIT_ORIENTATION: cls.DEFAULT_ORIENTATION,
-               }[current]
-
-       def _close_windows(self):
-               if self._mainWindow is not None:
-                       self.save_settings()
-                       self._mainWindow.window.destroyed.disconnect(self._on_child_close)
-                       self._mainWindow.close()
-                       self._mainWindow = None
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_delayed_start(self):
-               self._mainWindow.start()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_app_quit(self, checked = False):
-               if self._mainWindow is not None:
-                       self.save_settings()
-                       self._mainWindow.destroy()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_child_close(self, obj = None):
-               if self._mainWindow is not None:
-                       self.save_settings()
-                       self._mainWindow = None
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_toggle_fullscreen(self, checked = False):
-               with qui_utils.notify_error(self._errorLog):
-                       self._mainWindow.set_fullscreen(checked)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_next_orientation(self, checked = False):
-               with qui_utils.notify_error(self._errorLog):
-                       self.set_orientation(self._next_orientation(self._orientation))
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_about(self, checked = True):
-               raise NotImplementedError("Booh")
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_log(self, checked = False):
-               with qui_utils.notify_error(self._errorLog):
-                       with open(self._constants._user_logpath_, "r") as f:
-                               logLines = f.xreadlines()
-                               log = "".join(logLines)
-                               self._clipboard.setText(log)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_quit(self, checked = False):
-               with qui_utils.notify_error(self._errorLog):
-                       self._close_windows()
-
-
-class WindowWrapper(object):
-
-       def __init__(self, parent, app):
-               self._app = app
-
-               self._errorDisplay = qui_utils.ErrorDisplay(self._app.errorLog)
-
-               self._layout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight)
-               self._layout.setContentsMargins(0, 0, 0, 0)
-
-               self._superLayout = QtGui.QVBoxLayout()
-               self._superLayout.addWidget(self._errorDisplay.toplevel)
-               self._superLayout.setContentsMargins(0, 0, 0, 0)
-               self._superLayout.addLayout(self._layout)
-
-               centralWidget = QtGui.QWidget()
-               centralWidget.setLayout(self._superLayout)
-               centralWidget.setContentsMargins(0, 0, 0, 0)
-
-               self._window = qui_utils.QSignalingMainWindow(parent)
-               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
-               qui_utils.set_stackable(self._window, True)
-               self._window.setCentralWidget(centralWidget)
-
-               self._closeWindowAction = QtGui.QAction(None)
-               self._closeWindowAction.setText("Close")
-               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
-               self._closeWindowAction.triggered.connect(self._on_close_window)
-
-               self._window.addAction(self._closeWindowAction)
-               self._window.addAction(self._app.quitAction)
-               self._window.addAction(self._app.fullscreenAction)
-               self._window.addAction(self._app.orientationAction)
-               self._window.addAction(self._app.logAction)
-
-       @property
-       def window(self):
-               return self._window
-
-       @property
-       def windowOrientation(self):
-               geom = self._window.size()
-               if geom.width() <= geom.height():
-                       return QtCore.Qt.Vertical
-               else:
-                       return QtCore.Qt.Horizontal
-
-       @property
-       def idealWindowOrientation(self):
-               if self._app.orientation ==  self._app.AUTO_ORIENTATION:
-                       windowOrientation = self.windowOrientation
-               elif self._app.orientation ==  self._app.DEFAULT_ORIENTATION:
-                       windowOrientation = qui_utils.screen_orientation()
-               elif self._app.orientation ==  self._app.LANDSCAPE_ORIENTATION:
-                       windowOrientation = QtCore.Qt.Horizontal
-               elif self._app.orientation ==  self._app.PORTRAIT_ORIENTATION:
-                       windowOrientation = QtCore.Qt.Vertical
-               else:
-                       raise RuntimeError("Bad! No %r for you" % self._app.orientation)
-               return windowOrientation
-
-       def walk_children(self):
-               return ()
-
-       def start(self):
-               pass
-
-       def close(self):
-               for child in self.walk_children():
-                       child.window.destroyed.disconnect(self._on_child_close)
-                       child.close()
-               self._window.close()
-
-       def destroy(self):
-               pass
-
-       def show(self):
-               self._window.show()
-               for child in self.walk_children():
-                       child.show()
-               self.set_fullscreen(self._app.fullscreenAction.isChecked())
-
-       def hide(self):
-               for child in self.walk_children():
-                       child.hide()
-               self._window.hide()
-
-       def set_fullscreen(self, isFullscreen):
-               if self._window.isVisible():
-                       if isFullscreen:
-                               self._window.showFullScreen()
-                       else:
-                               self._window.showNormal()
-               for child in self.walk_children():
-                       child.set_fullscreen(isFullscreen)
-
-       def update_orientation(self, orientation):
-               if orientation == self._app.DEFAULT_ORIENTATION:
-                       qui_utils.set_autorient(self.window, False)
-                       qui_utils.set_window_orientation(self.window, None)
-               elif orientation == self._app.AUTO_ORIENTATION:
-                       qui_utils.set_autorient(self.window, True)
-                       qui_utils.set_window_orientation(self.window, None)
-               elif orientation == self._app.LANDSCAPE_ORIENTATION:
-                       qui_utils.set_autorient(self.window, False)
-                       qui_utils.set_window_orientation(self.window, QtCore.Qt.Horizontal)
-               elif orientation == self._app.PORTRAIT_ORIENTATION:
-                       qui_utils.set_autorient(self.window, False)
-                       qui_utils.set_window_orientation(self.window, QtCore.Qt.Vertical)
-               else:
-                       raise RuntimeError("Unknown orientation: %r" % orientation)
-               for child in self.walk_children():
-                       child.update_orientation(orientation)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_child_close(self, obj = None):
-               raise NotImplementedError("Booh")
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_window(self, checked = True):
-               with qui_utils.notify_error(self._errorLog):
-                       self.close()
-
-
-class AutoFreezeWindowFeature(object):
-
-       def __init__(self, app, window):
-               self._app = app
-               self._window = window
-               self._app.qapp.focusChanged.connect(self._on_focus_changed)
-               if self._app.qapp.focusWidget() is not None:
-                       self._window.setUpdatesEnabled(True)
-               else:
-                       self._window.setUpdatesEnabled(False)
-
-       def close(self):
-               self._app.qapp.focusChanged.disconnect(self._on_focus_changed)
-               self._window.setUpdatesEnabled(True)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_focus_changed(self, oldWindow, newWindow):
-               with qui_utils.notify_error(self._app.errorLog):
-                       if oldWindow is None and newWindow is not None:
-                               self._window.setUpdatesEnabled(True)
-                       elif oldWindow is not None and newWindow is None:
-                               self._window.setUpdatesEnabled(False)
diff --git a/src/util/time_utils.py b/src/util/time_utils.py
deleted file mode 100644 (file)
index 90ec84d..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-from datetime import tzinfo, timedelta, datetime
-
-ZERO = timedelta(0)
-HOUR = timedelta(hours=1)
-
-
-def first_sunday_on_or_after(dt):
-       days_to_go = 6 - dt.weekday()
-       if days_to_go:
-               dt += timedelta(days_to_go)
-       return dt
-
-
-# US DST Rules
-#
-# This is a simplified (i.e., wrong for a few cases) set of rules for US
-# DST start and end times. For a complete and up-to-date set of DST rules
-# and timezone definitions, visit the Olson Database (or try pytz):
-# http://www.twinsun.com/tz/tz-link.htm
-# http://sourceforge.net/projects/pytz/ (might not be up-to-date)
-#
-# In the US, since 2007, DST starts at 2am (standard time) on the second
-# Sunday in March, which is the first Sunday on or after Mar 8.
-DSTSTART_2007 = datetime(1, 3, 8, 2)
-# and ends at 2am (DST time; 1am standard time) on the first Sunday of Nov.
-DSTEND_2007 = datetime(1, 11, 1, 1)
-# From 1987 to 2006, DST used to start at 2am (standard time) on the first
-# Sunday in April and to end at 2am (DST time; 1am standard time) on the last
-# Sunday of October, which is the first Sunday on or after Oct 25.
-DSTSTART_1987_2006 = datetime(1, 4, 1, 2)
-DSTEND_1987_2006 = datetime(1, 10, 25, 1)
-# From 1967 to 1986, DST used to start at 2am (standard time) on the last
-# Sunday in April (the one on or after April 24) and to end at 2am (DST time;
-# 1am standard time) on the last Sunday of October, which is the first Sunday
-# on or after Oct 25.
-DSTSTART_1967_1986 = datetime(1, 4, 24, 2)
-DSTEND_1967_1986 = DSTEND_1987_2006
-
-
-class USTimeZone(tzinfo):
-
-       def __init__(self, hours, reprname, stdname, dstname):
-               self.stdoffset = timedelta(hours=hours)
-               self.reprname = reprname
-               self.stdname = stdname
-               self.dstname = dstname
-
-       def __repr__(self):
-               return self.reprname
-
-       def tzname(self, dt):
-               if self.dst(dt):
-                       return self.dstname
-               else:
-                       return self.stdname
-
-       def utcoffset(self, dt):
-               return self.stdoffset + self.dst(dt)
-
-       def dst(self, dt):
-               if dt is None or dt.tzinfo is None:
-                       # An exception may be sensible here, in one or both cases.
-                       # It depends on how you want to treat them.  The default
-                       # fromutc() implementation (called by the default astimezone()
-                       # implementation) passes a datetime with dt.tzinfo is self.
-                       return ZERO
-               assert dt.tzinfo is self
-
-               # Find start and end times for US DST. For years before 1967, return
-               # ZERO for no DST.
-               if 2006 < dt.year:
-                       dststart, dstend = DSTSTART_2007, DSTEND_2007
-               elif 1986 < dt.year < 2007:
-                       dststart, dstend = DSTSTART_1987_2006, DSTEND_1987_2006
-               elif 1966 < dt.year < 1987:
-                       dststart, dstend = DSTSTART_1967_1986, DSTEND_1967_1986
-               else:
-                       return ZERO
-
-               start = first_sunday_on_or_after(dststart.replace(year=dt.year))
-               end = first_sunday_on_or_after(dstend.replace(year=dt.year))
-
-               # Can't compare naive to aware objects, so strip the timezone from
-               # dt first.
-               if start <= dt.replace(tzinfo=None) < end:
-                       return HOUR
-               else:
-                       return ZERO
-
-
-Eastern  = USTimeZone(-5, "Eastern",  "EST", "EDT")
-Central  = USTimeZone(-6, "Central",  "CST", "CDT")
-Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
-Pacific  = USTimeZone(-8, "Pacific",  "PST", "PDT")
diff --git a/src/util/tp_utils.py b/src/util/tp_utils.py
deleted file mode 100644 (file)
index 7c55c42..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-#!/usr/bin/env python
-
-import logging
-
-import dbus
-import telepathy
-
-import util.go_utils as gobject_utils
-import misc
-
-
-_moduleLogger = logging.getLogger(__name__)
-DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'
-
-
-class WasMissedCall(object):
-
-       def __init__(self, bus, conn, chan, on_success, on_error):
-               self.__on_success = on_success
-               self.__on_error = on_error
-
-               self._requested = None
-               self._didMembersChange = False
-               self._didClose = False
-               self._didReport = False
-
-               self._onTimeout = gobject_utils.Timeout(self._on_timeout)
-               self._onTimeout.start(seconds=60)
-
-               chan[telepathy.interfaces.CHANNEL_INTERFACE_GROUP].connect_to_signal(
-                       "MembersChanged",
-                       self._on_members_changed,
-               )
-
-               chan[telepathy.interfaces.CHANNEL].connect_to_signal(
-                       "Closed",
-                       self._on_closed,
-               )
-
-               chan[DBUS_PROPERTIES].GetAll(
-                       telepathy.interfaces.CHANNEL_INTERFACE,
-                       reply_handler = self._on_got_all,
-                       error_handler = self._on_error,
-               )
-
-       def cancel(self):
-               self._report_error("by request")
-
-       def _report_missed_if_ready(self):
-               if self._didReport:
-                       pass
-               elif self._requested is not None and (self._didMembersChange or self._didClose):
-                       if self._requested:
-                               self._report_error("wrong direction")
-                       elif self._didClose:
-                               self._report_success()
-                       else:
-                               self._report_error("members added")
-               else:
-                       if self._didClose:
-                               self._report_error("closed too early")
-
-       def _report_success(self):
-               assert not self._didReport, "Double reporting a missed call"
-               self._didReport = True
-               self._onTimeout.cancel()
-               self.__on_success(self)
-
-       def _report_error(self, reason):
-               assert not self._didReport, "Double reporting a missed call"
-               self._didReport = True
-               self._onTimeout.cancel()
-               self.__on_error(self, reason)
-
-       @misc.log_exception(_moduleLogger)
-       def _on_got_all(self, properties):
-               self._requested = properties["Requested"]
-               self._report_missed_if_ready()
-
-       @misc.log_exception(_moduleLogger)
-       def _on_members_changed(self, message, added, removed, lp, rp, actor, reason):
-               if added:
-                       self._didMembersChange = True
-                       self._report_missed_if_ready()
-
-       @misc.log_exception(_moduleLogger)
-       def _on_closed(self):
-               self._didClose = True
-               self._report_missed_if_ready()
-
-       @misc.log_exception(_moduleLogger)
-       def _on_error(self, *args):
-               self._report_error(args)
-
-       @misc.log_exception(_moduleLogger)
-       def _on_timeout(self):
-               self._report_error("timeout")
-               return False
-
-
-class NewChannelSignaller(object):
-
-       def __init__(self, on_new_channel):
-               self._sessionBus = dbus.SessionBus()
-               self._on_user_new_channel = on_new_channel
-
-       def start(self):
-               self._sessionBus.add_signal_receiver(
-                       self._on_new_channel,
-                       "NewChannel",
-                       "org.freedesktop.Telepathy.Connection",
-                       None,
-                       None
-               )
-
-       def stop(self):
-               self._sessionBus.remove_signal_receiver(
-                       self._on_new_channel,
-                       "NewChannel",
-                       "org.freedesktop.Telepathy.Connection",
-                       None,
-                       None
-               )
-
-       @misc.log_exception(_moduleLogger)
-       def _on_new_channel(
-               self, channelObjectPath, channelType, handleType, handle, supressHandler
-       ):
-               connObjectPath = channel_path_to_conn_path(channelObjectPath)
-               serviceName = path_to_service_name(channelObjectPath)
-               try:
-                       self._on_user_new_channel(
-                               self._sessionBus, serviceName, connObjectPath, channelObjectPath, channelType
-                       )
-               except Exception:
-                       _moduleLogger.exception("Blocking exception from being passed up")
-
-
-class EnableSystemContactIntegration(object):
-
-       ACCOUNT_MGR_NAME = "org.freedesktop.Telepathy.AccountManager"
-       ACCOUNT_MGR_PATH = "/org/freedesktop/Telepathy/AccountManager"
-       ACCOUNT_MGR_IFACE_QUERY = "com.nokia.AccountManager.Interface.Query"
-       ACCOUNT_IFACE_COMPAT = "com.nokia.Account.Interface.Compat"
-       ACCOUNT_IFACE_COMPAT_PROFILE = "com.nokia.Account.Interface.Compat.Profile"
-       DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'
-
-       def __init__(self, profileName):
-               self._bus = dbus.SessionBus()
-               self._profileName = profileName
-
-       def start(self):
-               self._accountManager = self._bus.get_object(
-                       self.ACCOUNT_MGR_NAME,
-                       self.ACCOUNT_MGR_PATH,
-               )
-               self._accountManagerQuery = dbus.Interface(
-                       self._accountManager,
-                       dbus_interface=self.ACCOUNT_MGR_IFACE_QUERY,
-               )
-
-               self._accountManagerQuery.FindAccounts(
-                       {
-                               self.ACCOUNT_IFACE_COMPAT_PROFILE: self._profileName,
-                       },
-                       reply_handler = self._on_found_accounts_reply,
-                       error_handler = self._on_error,
-               )
-
-       @misc.log_exception(_moduleLogger)
-       def _on_found_accounts_reply(self, accountObjectPaths):
-               for accountObjectPath in accountObjectPaths:
-                       print accountObjectPath
-                       account = self._bus.get_object(
-                               self.ACCOUNT_MGR_NAME,
-                               accountObjectPath,
-                       )
-                       accountProperties = dbus.Interface(
-                               account,
-                               self.DBUS_PROPERTIES,
-                       )
-                       accountProperties.Set(
-                               self.ACCOUNT_IFACE_COMPAT,
-                               "SecondaryVCardFields",
-                               ["TEL"],
-                               reply_handler = self._on_field_set,
-                               error_handler = self._on_error,
-                       )
-
-       @misc.log_exception(_moduleLogger)
-       def _on_field_set(self):
-               _moduleLogger.info("SecondaryVCardFields Set")
-
-       @misc.log_exception(_moduleLogger)
-       def _on_error(self, error):
-               _moduleLogger.error("%r" % (error, ))
-
-
-def channel_path_to_conn_path(channelObjectPath):
-       """
-       >>> channel_path_to_conn_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
-       '/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME'
-       """
-       return channelObjectPath.rsplit("/", 1)[0]
-
-
-def path_to_service_name(path):
-       """
-       >>> path_to_service_name("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
-       'org.freedesktop.Telepathy.ConnectionManager.theonering.gv.USERNAME'
-       """
-       return ".".join(path[1:].split("/")[0:7])
-
-
-def cm_from_path(path):
-       """
-       >>> cm_from_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
-       'theonering'
-       """
-       return path[1:].split("/")[4]
diff --git a/src/windows.py b/src/windows.py
deleted file mode 100644 (file)
index 6aefe7e..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import with_statement
-from __future__ import division
-
-import logging
-
-from PyQt4 import QtGui
-from PyQt4 import QtCore
-
-import constants
-from util import qui_utils
-from util import misc as misc_utils
-import unit_data
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class FavoritesWindow(object):
-
-       def __init__(self, parent, app, source, hidden):
-               self._app = app
-               self._source = list(source)
-               self._hidden = hidden
-
-               self._categories = QtGui.QTreeWidget()
-               self._categories.setHeaderLabels(["Categories"])
-               self._categories.setHeaderHidden(True)
-               self._categories.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
-               if not constants.IS_MAEMO:
-                       self._categories.setAlternatingRowColors(True)
-               self._childWidgets = []
-               for catName in self._source:
-                       twi = QtGui.QTreeWidgetItem(self._categories)
-                       twi.setText(0, catName)
-                       self._childWidgets.append(twi)
-                       if catName in self._hidden:
-                               twi.setCheckState(0, QtCore.Qt.Unchecked)
-                       else:
-                               twi.setCheckState(0, QtCore.Qt.Checked)
-               self._categories.itemChanged.connect(self._on_item_changed)
-
-               self._allButton = QtGui.QPushButton("All")
-               self._allButton.clicked.connect(self._on_select_all)
-               self._invertButton = QtGui.QPushButton("Invert")
-               self._invertButton.clicked.connect(self._on_invert_selection)
-               self._noneButton = QtGui.QPushButton("None")
-               self._noneButton.clicked.connect(self._on_select_none)
-
-               self._buttonLayout = QtGui.QHBoxLayout()
-               self._buttonLayout.addWidget(self._allButton)
-               self._buttonLayout.addWidget(self._invertButton)
-               self._buttonLayout.addWidget(self._noneButton)
-
-               self._layout = QtGui.QVBoxLayout()
-               self._layout.addWidget(self._categories)
-               self._layout.addLayout(self._buttonLayout)
-
-               centralWidget = QtGui.QWidget()
-               centralWidget.setLayout(self._layout)
-
-               self._window = QtGui.QMainWindow(parent)
-               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
-               qui_utils.set_stackable(self._window, True)
-               self._window.setWindowTitle("%s - Favorites" % constants.__pretty_app_name__)
-               self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
-               self._window.setCentralWidget(centralWidget)
-
-               self._closeWindowAction = QtGui.QAction(None)
-               self._closeWindowAction.setText("Close")
-               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
-               self._closeWindowAction.triggered.connect(self._on_close_window)
-
-               if constants.IS_MAEMO:
-                       self._window.addAction(self._closeWindowAction)
-                       self._window.addAction(self._app.quitAction)
-                       self._window.addAction(self._app.fullscreenAction)
-               else:
-                       fileMenu = self._window.menuBar().addMenu("&Units")
-                       fileMenu.addAction(self._closeWindowAction)
-                       fileMenu.addAction(self._app.quitAction)
-
-                       viewMenu = self._window.menuBar().addMenu("&View")
-                       viewMenu.addAction(self._app.fullscreenAction)
-
-               self._window.addAction(self._app.logAction)
-
-               self.set_fullscreen(self._app.fullscreenAction.isChecked())
-               self._window.show()
-
-       @property
-       def window(self):
-               return self._window
-
-       def show(self):
-               self._window.show()
-
-       def hide(self):
-               self._window.hide()
-
-       def close(self):
-               self._window.close()
-
-       def set_fullscreen(self, isFullscreen):
-               if isFullscreen:
-                       self._window.showFullScreen()
-               else:
-                       self._window.showNormal()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_select_all(self, checked = False):
-               for child in self._childWidgets:
-                       child.setCheckState(0, QtCore.Qt.Checked)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_invert_selection(self, checked = False):
-               for child in self._childWidgets:
-                       state = child.checkState(0)
-                       if state == QtCore.Qt.Unchecked:
-                               newState = QtCore.Qt.Checked
-                       elif state == QtCore.Qt.Checked:
-                               newState = QtCore.Qt.Unchecked
-                       else:
-                               raise RuntimeError("Bad check state %r" % state)
-                       child.setCheckState(0, newState)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_select_none(self, checked = False):
-               for child in self._childWidgets:
-                       child.setCheckState(0, QtCore.Qt.Unchecked)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_item_changed(self, item, column):
-               state = item.checkState(column)
-               if state == QtCore.Qt.Unchecked:
-                       name = str(item.text(column))
-                       self._hidden.add(name)
-               elif state == QtCore.Qt.Checked:
-                       name = str(item.text(column))
-                       self._hidden.remove(name)
-               else:
-                       raise RuntimeError("Bad check state %r" % state)
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_window(self, checked = True):
-               self.close()
-
-
-class QuickJump(object):
-
-       MINIMAL_ENTRY = 3
-
-       def __init__(self, parent, app):
-               self._app = app
-
-               self._searchLabel = QtGui.QLabel("Search:")
-               self._searchEntry = QtGui.QLineEdit("")
-               self._searchEntry.textEdited.connect(self._on_search_edited)
-
-               self._entryLayout = QtGui.QHBoxLayout()
-               self._entryLayout.addWidget(self._searchLabel)
-               self._entryLayout.addWidget(self._searchEntry)
-
-               self._resultsBox = QtGui.QTreeWidget()
-               self._resultsBox.setHeaderLabels(["Categories", "Units"])
-               self._resultsBox.setHeaderHidden(True)
-               if not constants.IS_MAEMO:
-                       self._resultsBox.setAlternatingRowColors(True)
-               self._resultsBox.itemClicked.connect(self._on_result_clicked)
-
-               self._layout = QtGui.QVBoxLayout()
-               self._layout.addLayout(self._entryLayout)
-               self._layout.addWidget(self._resultsBox)
-
-               centralWidget = QtGui.QWidget()
-               centralWidget.setLayout(self._layout)
-
-               self._window = QtGui.QMainWindow(parent)
-               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
-               qui_utils.set_stackable(self._window, True)
-               self._window.setWindowTitle("%s - Quick Jump" % constants.__pretty_app_name__)
-               self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
-               self._window.setCentralWidget(centralWidget)
-
-               self._closeWindowAction = QtGui.QAction(None)
-               self._closeWindowAction.setText("Close")
-               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
-               self._closeWindowAction.triggered.connect(self._on_close_window)
-
-               if constants.IS_MAEMO:
-                       self._window.addAction(self._closeWindowAction)
-                       self._window.addAction(self._app.quitAction)
-                       self._window.addAction(self._app.fullscreenAction)
-               else:
-                       fileMenu = self._window.menuBar().addMenu("&Units")
-                       fileMenu.addAction(self._closeWindowAction)
-                       fileMenu.addAction(self._app.quitAction)
-
-                       viewMenu = self._window.menuBar().addMenu("&View")
-                       viewMenu.addAction(self._app.fullscreenAction)
-
-               self._window.addAction(self._app.logAction)
-
-               self.set_fullscreen(self._app.fullscreenAction.isChecked())
-               self._window.show()
-
-       @property
-       def window(self):
-               return self._window
-
-       def show(self):
-               self._window.show()
-
-       def hide(self):
-               self._window.hide()
-
-       def close(self):
-               self._window.close()
-
-       def set_fullscreen(self, isFullscreen):
-               if isFullscreen:
-                       self._window.showFullScreen()
-               else:
-                       self._window.showNormal()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_window(self, checked = True):
-               self.close()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_result_clicked(self, item, columnIndex):
-               categoryName = unicode(item.text(0))
-               unitName = unicode(item.text(1))
-               catWindow = self._app.request_category()
-               unitsWindow = catWindow.select_category(categoryName)
-               unitsWindow.select_unit(unitName)
-               self.close()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_search_edited(self, *args):
-               userInput = self._searchEntry.text()
-               if len(userInput) <  self.MINIMAL_ENTRY:
-                       return
-
-               self._resultsBox.clear()
-               lowerInput = str(userInput).lower()
-               for catIndex, category in enumerate(unit_data.UNIT_CATEGORIES):
-                       units = unit_data.get_units(category)
-                       for unitIndex, unit in enumerate(units):
-                               loweredUnit = unit.lower()
-                               if lowerInput in loweredUnit:
-                                       twi = QtGui.QTreeWidgetItem(self._resultsBox)
-                                       twi.setText(0, category)
-                                       twi.setText(1, unit)
-
-
-class Recent(object):
-
-       def __init__(self, parent, app):
-               self._app = app
-
-               self._resultsBox = QtGui.QTreeWidget()
-               self._resultsBox.setHeaderLabels(["Categories", "Units"])
-               self._resultsBox.setHeaderHidden(True)
-               if not constants.IS_MAEMO:
-                       self._resultsBox.setAlternatingRowColors(True)
-               self._resultsBox.itemClicked.connect(self._on_result_clicked)
-
-               self._layout = QtGui.QVBoxLayout()
-               self._layout.addWidget(self._resultsBox)
-
-               centralWidget = QtGui.QWidget()
-               centralWidget.setLayout(self._layout)
-
-               self._window = QtGui.QMainWindow(parent)
-               self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
-               qui_utils.set_stackable(self._window, True)
-               self._window.setWindowTitle("%s - Recent" % constants.__pretty_app_name__)
-               self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
-               self._window.setCentralWidget(centralWidget)
-
-               for cat, unit in self._app.get_recent():
-                       twi = QtGui.QTreeWidgetItem(self._resultsBox)
-                       twi.setText(0, cat)
-                       twi.setText(1, unit)
-
-               self._closeWindowAction = QtGui.QAction(None)
-               self._closeWindowAction.setText("Close")
-               self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
-               self._closeWindowAction.triggered.connect(self._on_close_window)
-
-               if constants.IS_MAEMO:
-                       self._window.addAction(self._closeWindowAction)
-                       self._window.addAction(self._app.quitAction)
-                       self._window.addAction(self._app.fullscreenAction)
-               else:
-                       fileMenu = self._window.menuBar().addMenu("&Units")
-                       fileMenu.addAction(self._closeWindowAction)
-                       fileMenu.addAction(self._app.quitAction)
-
-                       viewMenu = self._window.menuBar().addMenu("&View")
-                       viewMenu.addAction(self._app.fullscreenAction)
-
-               self._window.addAction(self._app.logAction)
-
-               self.set_fullscreen(self._app.fullscreenAction.isChecked())
-               self._window.show()
-
-       @property
-       def window(self):
-               return self._window
-
-       def show(self):
-               self._window.show()
-
-       def hide(self):
-               self._window.hide()
-
-       def close(self):
-               self._window.close()
-
-       def set_fullscreen(self, isFullscreen):
-               if isFullscreen:
-                       self._window.showFullScreen()
-               else:
-                       self._window.showNormal()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_close_window(self, checked = True):
-               self.close()
-
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_result_clicked(self, item, columnIndex):
-               categoryName = unicode(item.text(0))
-               unitName = unicode(item.text(1))
-               catWindow = self._app.request_category()
-               unitsWindow = catWindow.select_category(categoryName)
-               unitsWindow.select_unit(unitName)
-               self.close()
diff --git a/support/THANKS b/support/THANKS
new file mode 100644 (file)
index 0000000..df83f2b
--- /dev/null
@@ -0,0 +1,37 @@
+Thanks to the following people:
+
+Icon graphics:
+       Eugenia Loli-Queru
+       Zdravko Nikolov (alternative icon)
+
+Polish translation:
+       Cezary Morga
+
+Makefile corrections:
+       Dag Wieers
+
+Fixes and new units:
+       Dale Hair
+       Bill Cushman
+       James Boulton
+       Rafael Villar Burke
+       Kim Lux
+
+RPM spec file:
+       Andy Rabagliati
+
+Code snippets:
+       Roman numeral converter written by David Leonard
+
+Bug Reports:
+       George Vlahavas
+       Klaus Alexander Seistrup
+       Cezary Morga
+       Jeff Gove
+       Sebastien de Menten
+       Zdravko Nikolov
+
+Bugfix:
+       Riccardo Galli
+       Alexander Skwar
+
diff --git a/support/TODO b/support/TODO
new file mode 100644 (file)
index 0000000..1f21814
--- /dev/null
@@ -0,0 +1,44 @@
+- Add popup dialog box when "write units" to explain that writing
+  goes to standard output. to write to filename.txt gonvert must 
+  be started in console mode as follows:
+    gonvert > filename.txt
+    
+- add AWG (American Wire Gauge) conversions from Diameter/length and Area.
+
+- add volume of a sphere with diameter given in:
+       - inches, feet, metres, cm.
+       
+- add subcategories, especially for common measures:
+       - length,area,mass: all,foriegn,ancient
+       
+- add saveable customisable units for derived unit categories:
+       - accelation (length/time²)
+       - angular velocity (angle/time)
+       - Computer data flow rate (computer data/time)
+       - density (mass/volume)
+       - energy | work ( force Â· time)
+       - flow dry/liquid (volume/time)
+       - velocity (length/time)
+       - fuel efficiency: (length/volume) (NEW*)
+       - etc...
+       - probably use pickle method
+       - probably use pop up window to define numerator and denominator units
+         each with pull down selections.
+       
+- detailed instructions for creating your own conversions
+- Morse code converter
+- add: - angular acceleration
+       - torque
+- add ASCII to computer numbers (convert Hex to ASCII)
+
+- Add bookmarks (certain categories and units preselected, Ex:
+       ^A add bookmark
+       ^B view bookmarks
+       edit bookmarks
+       ^right ^left to go to previous and next bookmarks
+       command line option to start up with bookmark
+
+- Add history (possibly similar to bookmarks)
+       - a popup dialog showing last x pairs
+       - ability to remove a pair from history
+       - display group:unit1[,unit2]
diff --git a/support/builddeb.py b/support/builddeb.py
deleted file mode 100755 (executable)
index 5a2e432..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/usr/bin/python2.5
-
-import os
-import sys
-
-import py2deb
-
-import constants
-
-
-__appname__ = constants.__app_name__
-__description__ = """Unit Conversions
-A conversion utility that allows conversion between many units like CGS, Ancient, Imperial with many categories like length, mass, numbers, etc. All units converted values shown at once as you type
-.
-Homepage: http://gonvert.garage.maemo.org
-"""
-__author__ = "Ed Page"
-__email__ = "eopage@byu.net"
-__version__ = constants.__version__
-__build__ = constants.__build__
-__changelog__ = """
-* Adding back in scrolling when using arrow keys in Units Window
-* Fixing Units Window arrow keys when only showing favorites
-* Removed GTK support
-* Improved startup time
-* Changed the default to condensed mode for better startup time and since people probably will prefer it
-* Saved some wasted horizontal space
-""".strip()
-
-
-__postinstall__ = """#!/bin/sh -e
-
-gtk-update-icon-cache -f /usr/share/icons/hicolor
-rm -f ~/.gonvert/gonvert.log ~/.gonvert/selections.dat ~/.gonvert/window.dat
-"""
-
-__preremove__ = """#!/bin/sh -e
-"""
-
-
-def find_files(prefix, path):
-       for root, dirs, files in os.walk(path):
-               for file in files:
-                       if file.startswith(prefix+"-"):
-                               fileParts = file.split("-")
-                               unused, relPathParts, newName = fileParts[0], fileParts[1:-1], fileParts[-1]
-                               assert unused == prefix
-                               relPath = os.sep.join(relPathParts)
-                               yield relPath, file, newName
-
-
-def unflatten_files(files):
-       d = {}
-       for relPath, oldName, newName in files:
-               if relPath not in d:
-                       d[relPath] = []
-               d[relPath].append((oldName, newName))
-       return d
-
-
-def build_package(distribution):
-       try:
-               os.chdir(os.path.dirname(sys.argv[0]))
-       except:
-               pass
-
-       py2deb.Py2deb.SECTIONS = py2deb.SECTIONS_BY_POLICY[distribution]
-       p = py2deb.Py2deb(__appname__)
-       p.prettyName = constants.__pretty_app_name__
-       p.description = __description__
-       p.bugTracker = "https://bugs.maemo.org/enter_bug.cgi?product=Gonvert"
-       p.author = __author__
-       p.mail = __email__
-       p.license = "lgpl"
-       p.depends = ", ".join([
-               "python2.6 | python2.5",
-               "python-simplejson",
-       ])
-       p.depends += {
-               "debian": ", python-qt4",
-               "diablo": ", python2.5-qt4-core, python2.5-qt4-gui",
-               "fremantle": ", python2.5-qt4-core, python2.5-qt4-gui, python2.5-qt4-maemo5",
-       }[distribution]
-       p.recommends = ", ".join([
-       ])
-       p.section = {
-               "debian": "science",
-               "diablo": "user/science",
-               "fremantle": "user/science",
-       }[distribution]
-       p.arch = "all"
-       p.urgency = "low"
-       p.distribution = "diablo fremantle debian"
-       p.repository = "extras"
-       p.changelog = __changelog__
-       p.postinstall = __postinstall__
-       p.preremove = __preremove__
-       p.icon = {
-               "debian": "data-pixmaps-gonvert.png",
-               "diablo": "data-pixmaps-gonvert.png",
-               "fremantle": "data-pixmaps-gonvert.png", # Fremantle natively uses 48x48
-       }[distribution]
-       p["/opt/gonvert/bin"] = [ "gonvert.py" ]
-       for relPath, files in unflatten_files(find_files("src", ".")).iteritems():
-               fullPath = "/opt/gonvert/lib"
-               if relPath:
-                       fullPath += os.sep+relPath
-               p[fullPath] = list(
-                       "|".join((oldName, newName))
-                       for (oldName, newName) in files
-               )
-       for relPath, files in unflatten_files(find_files("data", ".")).iteritems():
-               fullPath = "/opt/gonvert/share"
-               if relPath:
-                       fullPath += os.sep+relPath
-               p[fullPath] = list(
-                       "|".join((oldName, newName))
-                       for (oldName, newName) in files
-               )
-       p["/usr/share/applications/hildon"] = ["gonvert.desktop"]
-       p["/usr/share/icons/hicolor/26x26/hildon"] = ["data-pixmaps-gonvert.png|gonvert.png"]
-       p["/usr/share/icons/hicolor/64x64/hildon"] = ["data-pixmaps-gonvert.png|gonvert.png"]
-       p["/usr/share/icons/hicolor/scalable/hildon"] = ["data-pixmaps-gonvert.png|gonvert.png"]
-
-       if distribution == "debian":
-               print p
-               print p.generate(
-                       version="%s-%s" % (__version__, __build__),
-                       changelog=__changelog__,
-                       build=True,
-                       tar=False,
-                       changes=False,
-                       dsc=False,
-               )
-               print "Building for %s finished" % distribution
-       else:
-               print p
-               print p.generate(
-                       version="%s-%s" % (__version__, __build__),
-                       changelog=__changelog__,
-                       build=False,
-                       tar=True,
-                       changes=True,
-                       dsc=True,
-               )
-               print "Building for %s finished" % distribution
-
-
-if __name__ == "__main__":
-       if len(sys.argv) > 1:
-               try:
-                       import optparse
-               except ImportError:
-                       optparse = None
-
-               if optparse is not None:
-                       parser = optparse.OptionParser()
-                       (commandOptions, commandArgs) = parser.parse_args()
-       else:
-               commandArgs = None
-               commandArgs = ["diablo"]
-       build_package(commandArgs[0])
diff --git a/support/doc/CHANGELOG b/support/doc/CHANGELOG
deleted file mode 100644 (file)
index 08becff..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-Project: gonvert
-
-Version:
-0.2.24 - Ed Page updated files for Maemo.
-       - Add Radioactivity and Radiation dose categories.
-0.2.23  - Added UK currency category and other UK measurements thanks to Dale Hair
-0.2.22  - Restore previously used window size
-0.2.21  - Category column widened. Maximize on start.
-0.2.20  - correction in micron pressure conversion
-0.2.19  - viscosity cP conversion correction
-0.2.18 - addition of magnitudes per square arcsecond to Luminance category
-0.2.17 - updated baud definitions
-       - fixed homepath location because new debian version changed
-0.2.16 - fixed icon locating for display in about
-       - added alternate icon gonvert-icon_alernative.png (copy over gonvert.png)
-0.2.15 - updated mainloop to main as discovered by Alexander Skwar
-0.2.14 - added Calgary energy and volume suggestions per Kim Lux
-0.2.13 - new more easily understandable icon
-       - nanotesla definition (nT).
-       - added shortlist feature.
-0.2.12 - removed inoperable books feature.
-       - fixed up acre accuracy.
-0.2.11 - miodified descriprion for silver, newton, sadzhens.
-0.2.10 - \x90 changed to \u00C9 for Emile and similar for Reaumur utf-8 text.
-       - Added translation for "All" book text.
-       - The write units text is translatable.
-       - The pl_messages.po file has been updated
-0.2.09 - Added utf-8 coding to all text strings in preparation for complete language translation.
-0.2.08 - Added language translation for menus and labels.
-0.2.07 - Added language translation changes and messages.pot.
-0.2.06 - Fixed category list size to show preselected categorys on startup,
-         scroll window H&Vpolicy set to always.
-0.2.05 - Spelling of Luminance category fixed.
-0.2.04 - Modified unit clicking to force focus on value entry.
-         Modified Makefile to remove /share/share bug for desktop entry.
-0.2.03 - Modified Makefile to allow better integration on other platforms.
-0.2.01 - Added saved selections feature, creates ~/.gonvert/ and file. 
-0.1.11 - fixed packaging for RPM
-0.1.10 - added Current Loop category for PLCs and 4-20mA instrumentation.
-0.1.9  - added kilobit, and more density units.
-0.1.8  - Added Torque units
-0.1.7  - Added many more pressure units
-       - Added thermal categories
-       - Added fuel consumption category
-       - Program extension to .pyw so that Windows startup without console
-0.1.6  - add more frequency units
-       - fixed computer number bases nums was bad near "h" and "v"
-       - fixed error:
-         "GtkTextBuffer.insert_at_cursor() takes exactly 1 argument (2 given)"
-         thanks to Riccardo Galli
-0.1.5  - put packages into /usr instead of /usr/local
-       - add gnome menu item back in
-0.1.4  - remove dependency on gnome-config from Makefile, RPM, binary.
-0.1.3  - touched up computer numbers units for better sorting
-       - limited up resizing of windows to prevent dissapearing areas
-       - fixed find bug that some users might notice (TreeViewColumn/None)
-0.1.2  - Added description box when writing units
-0.1.1  - Added help/about box
-       - fixed bug that sets focus on line 2480
-       - fixed temperature difference units labels
-       - all scroll bars only show when needed
-       - Added RPM distribution
-0.1.0   - Major modifications for GTK2 (RedHat 8.0)
-       - addition of units column in display
-       - sorting for all units columns with sort pointer
-0.0.15 - added Electromagnetic Radiation category
-0.0.14 - fixed window close bug, attempt to fix libglade XML startup bug for
-          some machines
-0.0.13 - changes for python2.2, had to remove gnome dependencies
-0.0.12 - change contact information address
-0.0.11 - addition of ppm to "find" utility
-0.0.10 - addition of petabyte to computer data
-0.0.9  - addition of cesium atom vibrations to Time category
-0.0.8  - more accurate calculation of degrees F
-0.0.7  - added 'Find unit' feature
-       - changed Category list to clist for ease of moveto (focus) after find
-0.0.6  - added description for Amperes
-       - added DENSITY category
-       - added 4 new categories 101 new units
-       - added shoe size converter
-       - add a function to convert custom formulas (like area from diameter)
-         example: area = pi * (D/2)^2
-         base value = pi* (x/2)^2  #metres in diameter metres, cm, inch, foot.
-0.0.5  - Tool for listing all categories and units to STDOUT.
-       - re-organization of project files.
-       - addition of suffixes between duodecillion and centillion.
-       - addition of Makefile to install onto Gnome based systems.
-       - sort Units or Value columns (ascending or descending)
-         by clicking on column.
-0.0.4  - Prefixes and Suffixes addition of;
-         ppm, %, Marx brothers, various descriptions.
-       - addition of microgram to mass category.
-       - replaced base 63 with 62 from computer numbers since
-         only 62 characters can be represented.
-       - fixed error if second line has nothing it wouldn't get
-         updated.
-0.0.3  - fix bug in labelling of base 36 (was base 37)
-         all numbering systems past 23 were at fault due
-         to improper nums string (fixed).
-0.0.2  - Completion of second row data entry so that changes
-         to text are not cyclicly causing changes to all
-         values.
-0.0.1  - Initial release.
diff --git a/support/doc/COPYING b/support/doc/COPYING
deleted file mode 100644 (file)
index d60c31a..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-                   GNU GENERAL PUBLIC LICENSE
-                      Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                           Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-\f
-                   GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-\f
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-\f
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-\f
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                           NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-                    END OF TERMS AND CONDITIONS
-\f
-           How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year  name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/support/doc/FAQ b/support/doc/FAQ
deleted file mode 100644 (file)
index 5111238..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-
- Problem: Cannot run with python2.2
-   Cause: Python Bindings pygtk have not been installed in python 2.2 library
-Solution: Download the latest pygtk from: ftp://ftp.gtk.org/pub/gtk/python/v1.2/
-          After unpacking, in the unpacked directory, type:
-             PYTHON=/usr/bin/python2 ./configure --prefix=/usr
-             make
-             make install
-            
-          Also, if your python2.2 is started by typing python2 then the first
-         line of the gonvert file must be:
-            #!/usr/bin/python2
-          Use "which python2" to find the exact path that starts python2.
-
-
-
- Problem: gonvert reports "ImportError:  No module named gnome.ui"
-   Cause: Python programs that use the gnome interface require pygnome
-Solution: install pygnome which can be obtained from:
-         ftp://ftp.daa.com.au/pub/james/python/
-
-
-Q: Where is the source?
-A: This is a Python program, the program is the source code.
-   Use a text viewer/editor to look directly at gonvert
-
diff --git a/support/doc/INSTALL b/support/doc/INSTALL
deleted file mode 100644 (file)
index 4aa1f93..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-gonvert
-=======
-Any problems, suggestions, features or comments please contact me: 
-       anthony@unihedron.com
-
-
-Requirements
-============
-       RH7.0 has everything you need.
-
-       Otherwise make sure that you have:
-               Python >= 1.5.2         http://python.org
-               PyGTK >= 0.6.6          ftp://ftp.daa.com.au/pub/james/python/
-               pygnome                 ftp://ftp.daa.com.au/pub/james/python/
-               Libglade >= 0.13        http://freshmeat.net/redir/libglade/5651/url_homepage/
-               gnome-libs >= 1.2.4     http://www.helixcode.com/desktop/packages.php3
-               
-       If you want to do GUI development then you will need:
-               glade                   http://www.helixcode.com/desktop/packages.php3
-
-
-Installation Notes
-==================
-gonvert has been tested on RedHat 7.0 (python 1.5.2, libglade 0.14, pygtk 0.6.6)
-
-1) Extract the tar archive.
-
-
-2) cd into the gonvert-x.y.z directory
-
-
-3a)If you just want to test the program as a user then:
-
-       ./gonvert
-
-
-3b) If you want to install the program then:
-
-       su -c "make install"
-       
-
-
-Uninstall the program:
-====================== 
-
-       su -c "make uninstall"
-
-================================================
-Translation:
-To make translations to other languages, follow these steps:
-
-1. Use the following to extract the raw message catalog
-     pygettext gonvert
-     edit messages.txt to CHARSET=utf-8
-
-2, Give the messages.pot file to the translater person
-    See http://www.unicode.org/charts/ for unicode translations
-    To get a defined character, use  \N{Character_name}, for example:
-         \N{SKULL AND CROSSBONES}
-            or 
-         u'\u2620'
-
-3. Get the files back from the translator person:
-     pl_messages.po   the translated message file
-     pl_messages.gmo  the compiled translation, which was compiled with something like:
-                         msgfmt gonvert.po
-
-4. Install the .gmo file in /usr/share/locale/xx/LC_MESSAGES using:
-   # make install_pl
-
-5. To update a translation use the following:
-     msgmerge -U i18n/pl_messages.po messages.pot
-   Merged results go into the source file.
-   Compile the new translation
-     msgfmt i18n/pl_messages.po -oi18n/pl_messages.gmo
-
-7. Test by setting the LANGUAGE variable appropriately, then run gonvert.
-   LANGUAGE=pl
-      or
-   LANGUAGE=en
-================================================
diff --git a/support/doc/README b/support/doc/README
deleted file mode 100644 (file)
index d38ca16..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-Description: 
-===========
-gonvert is a conversion utility that allows conversion between many units 
-like CGS, Ancient, Imperial with many categories like length, mass, numbers, 
-etc. All units converted values shown at once as you type. Easy to add/change 
-your own units. Written in Python,pygtk,libgade. 
-
-
-Goals:
-=====
-The goal when making gonvert was to have the most complete and easy to use 
-unit conversion utility. It is still growing as I find new units.
-
-
-Contact:
-========
- anthony@unihedron.com 
-
-
-INSTALLATION:
-=============
-See INSTALL for installation notes
-
-
-USAGE:
-======
-- Start the program by typing "gonvert".
-- Select the category of units that you want to convert.
-- Select the unit that want to enter a value for.
-- Type the value.
-- The top two rows on the right side are temporary locations that scroll 
-  through each unit you select. You can use this area to convert
-  between one unit and another without scrolling through the entire list
-  units.
-
-
-Other features:
-===============
-- You can sort Units and Value columns by clicking on the column name.
-- You can print (to STDOUT) out all the units by selecting Tools>Write_Units
-- You can find units by typing the name of the desired unit at the bottom
-  input field then pressing the Find butonn or hitting enter.
-  The number of units found that match the desired string is displayed
-  in the label at the bottom right. F6 key also acts as the Find button.
-- previously selected categories and units are saved to ~/.gonvert/ directory
-
-
-
-Warranty:
-=========
-The usual disclaimer, I am not responsible for any damages this program may
-cause. Use at your own risk. See COPYING for license.
-
-
-Making additions and changes:
-=============================
-If you make corrections or additions to the program, please e-mail me so that I
-can incorporate them into the source (with credit to your name of course).
-Please keep in mind that this project is GPL (see COPYING file) so do not use
-copyrighted material in your changes/additions to the unit descriptions that you
-submit to me.
-
-Make sure that all standard calculations using the m() function have floating
-point arguments. In other words use at least one number in the formula with a
-decimal point to force floating point numbers:
- (m,5/2)       #this is bad (the result will be 2, not 2.5)
- (m,5.0/2)     #this forces float (good, the result will be 2.5)
-
-
-Without knowing Python:
-You can alter the code without knowing much about Python just be reviewing the
-method that the units definitions were written. Be careful to include the same
-number of starting and ending brackets, also be careful of the commas.
-
-If you want to use mathematical functions like pi etc you can see what other
-functions there are as follows:
-
-# python
->>>from math import *
->>>dir()
-['__builtins__', '__doc__', '__name__', 'acos', 'asin', 'atan', 'atan2', 'ceil',
-'cos', 'cosh',  'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp',
-'log', 'log10', 'modf', 'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan',  'tanh']
-
-
-A good book for learning Python is the O'Reilly "Learning Python" book by 
-Mark Lutz and David Ascher.
-
-
-
-
-
-
-
-
-Location of installed gonvert software is put into directories suggested by 
-then manpage hier (man hier).
-
- /usr/bin
-       gonvert program (this is the source)
-
- /usr/doc
-       gonvert documentation files.
-
- /usr/lib
-       Files associated with gonvert like the glade file and the pixmaps.
-
- /usr/man
-       gonvert Man page (when one is written)
-
- ~/.gonvert
-       user configuration files associated with gonvert.
-
-
-
diff --git a/support/doc/THANKS b/support/doc/THANKS
deleted file mode 100644 (file)
index df83f2b..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-Thanks to the following people:
-
-Icon graphics:
-       Eugenia Loli-Queru
-       Zdravko Nikolov (alternative icon)
-
-Polish translation:
-       Cezary Morga
-
-Makefile corrections:
-       Dag Wieers
-
-Fixes and new units:
-       Dale Hair
-       Bill Cushman
-       James Boulton
-       Rafael Villar Burke
-       Kim Lux
-
-RPM spec file:
-       Andy Rabagliati
-
-Code snippets:
-       Roman numeral converter written by David Leonard
-
-Bug Reports:
-       George Vlahavas
-       Klaus Alexander Seistrup
-       Cezary Morga
-       Jeff Gove
-       Sebastien de Menten
-       Zdravko Nikolov
-
-Bugfix:
-       Riccardo Galli
-       Alexander Skwar
-
diff --git a/support/doc/TODO b/support/doc/TODO
deleted file mode 100644 (file)
index 1f21814..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-- Add popup dialog box when "write units" to explain that writing
-  goes to standard output. to write to filename.txt gonvert must 
-  be started in console mode as follows:
-    gonvert > filename.txt
-    
-- add AWG (American Wire Gauge) conversions from Diameter/length and Area.
-
-- add volume of a sphere with diameter given in:
-       - inches, feet, metres, cm.
-       
-- add subcategories, especially for common measures:
-       - length,area,mass: all,foriegn,ancient
-       
-- add saveable customisable units for derived unit categories:
-       - accelation (length/time²)
-       - angular velocity (angle/time)
-       - Computer data flow rate (computer data/time)
-       - density (mass/volume)
-       - energy | work ( force Â· time)
-       - flow dry/liquid (volume/time)
-       - velocity (length/time)
-       - fuel efficiency: (length/volume) (NEW*)
-       - etc...
-       - probably use pickle method
-       - probably use pop up window to define numerator and denominator units
-         each with pull down selections.
-       
-- detailed instructions for creating your own conversions
-- Morse code converter
-- add: - angular acceleration
-       - torque
-- add ASCII to computer numbers (convert Hex to ASCII)
-
-- Add bookmarks (certain categories and units preselected, Ex:
-       ^A add bookmark
-       ^B view bookmarks
-       edit bookmarks
-       ^right ^left to go to previous and next bookmarks
-       command line option to start up with bookmark
-
-- Add history (possibly similar to bookmarks)
-       - a popup dialog showing last x pairs
-       - ability to remove a pair from history
-       - display group:unit1[,unit2]
diff --git a/support/fake_py2deb.py b/support/fake_py2deb.py
deleted file mode 100644 (file)
index 5d6149d..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-import pprint
-
-
-class Py2deb(object):
-
-       def __init__(self, appName):
-               self._appName = appName
-               self.description = ""
-               self.author = ""
-               self.mail = ""
-               self.license = ""
-               self.depends = ""
-               self.section = ""
-               self.arch = ""
-               self.ugency = ""
-               self.distribution = ""
-               self.repository = ""
-               self.changelog = ""
-               self.postinstall = ""
-               self.icon = ""
-               self._install = {}
-
-       def generate(self, appVersion, appBuild, changelog, tar, dsc, changes, build, src):
-               return """
-Package: %s
-version: %s-%s
-Changes:
-%s
-
-Build Options:
-       Tar: %s
-       Dsc: %s
-       Changes: %s
-       Build: %s
-       Src: %s
-               """ % (
-                       self._appName, appVersion, appBuild, changelog, tar, dsc, changes, build, src
-               )
-
-       def __str__(self):
-               parts = []
-               parts.append("%s Package Settings:" % (self._appName, ))
-               for settingName in dir(self):
-                       if settingName.startswith("_"):
-                               continue
-                       parts.append("\t%s: %s" % (settingName, getattr(self, settingName)))
-
-               parts.append(pprint.pformat(self._install))
-
-               return "\n".join(parts)
-
-       def __getitem__(self, key):
-               return self._install[key]
-
-       def __setitem__(self, key, item):
-               self._install[key] = item
diff --git a/support/gonvert.desktop b/support/gonvert.desktop
deleted file mode 100644 (file)
index 0d59457..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Desktop Entry]
-Encoding=UTF-8
-Version=1.0
-Type=Application
-Name=Gonvert
-Exec=/usr/bin/run-standalone.sh /opt/gonvert/bin/gonvert.py
-Icon=gonvert
-Categories=Engineering;Science;Education;Utility;Qt;
diff --git a/support/gonvert.spec b/support/gonvert.spec
deleted file mode 100644 (file)
index 5cf1e53..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-Name:           gonvert\r
-Version:        0.1.9\r
-Release:        0.fdr.1\r
-Epoch:          0\r
-Summary:        Units conversion utility.\r
-\r
-Group:          Applications/Engineering\r
-License:        GPL\r
-URL:            http://unihedron.com/projects/gonvert/gonvert.php\r
-Source0:        http://www.unihedron.com/projects/gonvert/downloads/gonvert-0.1.9.tar.gz\r
-Source1:        gonvert.desktop\r
-BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)\r
-\r
-BuildRequires:  desktop-file-utils\r
-Requires:       python >= 0:2.0\r
-Requires:       pygtk2 >= 0:1.99.0\r
-Requires:       libglade2\r
-\r
-BuildArch:      noarch\r
-\r
-%description\r
-gonvert is a conversion utility that allows conversion between many units \r
-like CGS, Ancient, Imperial with many categories like length, mass, numbers, \r
-etc. All units converted values shown at once as you type. Easy to add/change \r
-your own units. Written in Python,pygtk,libgade. \r
-\r
-\r
-\r
-%prep \r
-%setup\r
-\r
-\r
-\r
-%build \r
-make %{?_smp_mflags}\r
-\r
-\r
-\r
-%install \r
-rm -rf ${RPM_BUILD_ROOT}\r
-%makeinstall DESTDIR="${RPM_BUILD_ROOT}"\r
-\r
-chmod 0644 doc/*\r
-rm -rf ${RPM_BUILD_ROOT}%{_prefix}/doc\r
-\r
-rm ${RPM_BUILD_ROOT}%{_datadir}/gnome/apps/Utilities/gonvert.desktop\r
-desktop-file-install --vendor fedora                    \\r
-  --dir ${RPM_BUILD_ROOT}%{_datadir}/applications       \\r
-  --add-category X-Fedora                               \\r
-  %{SOURCE1}\r
-\r
-\r
-\r
-%clean \r
-rm -rf ${RPM_BUILD_ROOT}\r
-\r
-\r
-\r
-%files \r
-%defattr(-,root,root,-) \r
-%doc doc/*\r
-%attr(0755,root,root) %{_bindir}/*\r
-%{_libdir}/*\r
-%attr(0644,root,root) %{_datadir}/applications/fedora-%{name}.desktop\r
-%attr(0644,root,root) %{_datadir}/pixmaps/*\r
-\r
-\r
-\r
-%changelog\r
-* Thu Feb 05 2004 Phillip Compton <pcompton[AT]proteinmedia.com> - 0:0.1.9-0.fdr.1\r
-- Update to 0.1.9.\r
-\r
-* Sun Jan 25 2004 Phillip Compton <pcompton[AT]proteinmedia.com> - 0:0.1.7-0.fdr.1\r
-- Update to 0.1.7.\r
-\r
-* Sun Nov 16 2003 Phillip Compton <pcompton[AT]proteinmedia.com> - 0:0.1.6-0.fdr.3\r
-- BuildReq desktop-file-utils.\r
-\r
-* Tue Aug 05 2003 Phillip Compton <pcompton[AT]proteinmedia.com> - 0:0.1.6-0.fdr.2\r
-- Corrected file permissions.\r
-- Corrects path of Source0.\r
-\r
-* Wed Jul 30 2003 Phillip Compton <pcompton[AT]proteinmedia.com> - 0:0.1.6-0.fdr.1\r
-- Fedorafication.\r
-\r
-* Sat Jun 29 2003 Dag Wieers <dag@wieers.com> - 0.1.6-0\r
-- Updated to release 0.1.6.\r
-\r
-* Wed Feb 26 2003 Dag Wieers <dag@wieers.com> - 0.1.5-0\r
-- Initial package. (using DAR)\r
diff --git a/support/py2deb.py b/support/py2deb.py
deleted file mode 100644 (file)
index 0518480..0000000
+++ /dev/null
@@ -1,994 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-##
-##    Copyright (C) 2009 manatlan manatlan[at]gmail(dot)com
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published
-## by the Free Software Foundation; version 2 only.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-"""
-Known limitations :
-- don't sign package (-us -uc)
-- no distinctions between author and maintainer(packager)
-
-depends on :
-- dpkg-dev (dpkg-buildpackage)
-- alien
-- python
-- fakeroot
-
-changelog
- - ??? ?/??/20?? (By epage)
-    - PEP8
-    - added recommends
-    - fixed bug where it couldn't handle the contents of the pre/post scripts being specified
-    - Added customization based on the targeted policy for sections (Maemo support)
-    - Added maemo specific tarball, dsc, changes file generation support (including icon support)
-    - Added armel architecture
-    - Reduced the size of params being passed around by reducing the calls to locals()
-    - Added respository, distribution, priority
-    - Made setting control file a bit more flexible
- - 0.5 05/09/2009
-    - pre/post install/remove scripts enabled
-    - deb package install py2deb in dist-packages for py2.6
- - 0.4 14/10/2008
-    - use os.environ USERNAME or USER (debian way)
-    - install on py 2.(4,5,6) (*FIX* do better here)
-
-"""
-
-import os
-import hashlib
-import sys
-import shutil
-import time
-import string
-import StringIO
-import stat
-import commands
-import base64
-import tarfile
-from glob import glob
-from datetime import datetime
-import socket # gethostname()
-from subprocess import Popen, PIPE
-
-#~ __version__ = "0.4"
-__version__ = "0.5"
-__author__ = "manatlan"
-__mail__ = "manatlan@gmail.com"
-
-
-PERMS_URW_GRW_OR = stat.S_IRUSR | stat.S_IWUSR | \
-                   stat.S_IRGRP | stat.S_IWGRP | \
-                   stat.S_IROTH
-
-UID_ROOT = 0
-GID_ROOT = 0
-
-
-def run(cmds):
-    p = Popen(cmds, shell=False, stdout=PIPE, stderr=PIPE)
-    time.sleep(0.01)    # to avoid "IOError: [Errno 4] Interrupted system call"
-    out = string.join(p.stdout.readlines()).strip()
-    outerr = string.join(p.stderr.readlines()).strip()
-    return out
-
-
-def deb2rpm(file):
-    txt=run(['alien', '-r', file])
-    return txt.split(" generated")[0]
-
-
-def py2src(TEMP, name):
-    l=glob("%(TEMP)s/%(name)s*.tar.gz" % locals())
-    if len(l) != 1:
-        raise Py2debException("don't find source package tar.gz")
-
-    tar = os.path.basename(l[0])
-    shutil.move(l[0], tar)
-
-    return tar
-
-
-def md5sum(filename):
-    f = open(filename, "r")
-    try:
-        return hashlib.md5(f.read()).hexdigest()
-    finally:
-        f.close()
-
-
-class Py2changes(object):
-
-    def __init__(self, ChangedBy, description, changes, files, category, repository, **kwargs):
-      self.options = kwargs # TODO: Is order important?
-      self.description = description
-      self.changes=changes
-      self.files=files
-      self.category=category
-      self.repository=repository
-      self.ChangedBy=ChangedBy
-
-    def getContent(self):
-        content = ["%s: %s" % (k, v)
-                   for k,v in self.options.iteritems()]
-
-        if self.description:
-            description=self.description.replace("\n","\n ")
-            content.append('Description: ')
-            content.append(' %s' % description)
-        if self.changes:
-            changes=self.changes.replace("\n","\n ")
-            content.append('Changes: ')
-            content.append(' %s' % changes)
-        if self.ChangedBy:
-            content.append("Changed-By: %s" % self.ChangedBy)
-
-        content.append('Files:')
-
-        for onefile in self.files:
-            md5 = md5sum(onefile)
-            size = os.stat(onefile).st_size.__str__()
-            content.append(' ' + md5 + ' ' + size + ' ' + self.category +' '+self.repository+' '+os.path.basename(onefile))
-
-        return "\n".join(content) + "\n\n"
-
-
-def py2changes(params):
-    changescontent = Py2changes(
-        "%(author)s <%(mail)s>" % params,
-        "%(description)s" % params,
-        "%(changelog)s" % params,
-        (
-            "%(TEMP)s/%(name)s_%(version)s.tar.gz" % params,
-            "%(TEMP)s/%(name)s_%(version)s.dsc" % params,
-        ),
-        "%(section)s" % params,
-        "%(repository)s" % params,
-        Format='1.7',
-        Date=time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()),
-        Source="%(name)s" % params,
-        Architecture="%(arch)s" % params,
-        Version="%(version)s" % params,
-        Distribution="%(distribution)s" % params,
-        Urgency="%(urgency)s" % params,
-        Maintainer="%(author)s <%(mail)s>" % params
-    )
-    f = open("%(TEMP)s/%(name)s_%(version)s.changes" % params,"wb")
-    f.write(changescontent.getContent())
-    f.close()
-
-    fileHandle = open('/tmp/py2deb2.tmp', 'w')
-    fileHandle.write('#!/bin/sh\n')
-    fileHandle.write("cd " +os.getcwd()+ "\n")
-    # TODO Renable signing
-    # fileHandle.write("gpg --local-user %(mail)s --clearsign %(TEMP)s/%(name)s_%(version)s.changes\n" % params)
-    fileHandle.write("mv %(TEMP)s/%(name)s_%(version)s.changes.asc %(TEMP)s/%(name)s_%(version)s.changes\n" % params)
-    fileHandle.write('\nexit')
-    fileHandle.close()
-    commands.getoutput("chmod 777 /tmp/py2deb2.tmp")
-    commands.getoutput("/tmp/py2deb2.tmp")
-
-    ret = []
-
-    l=glob("%(TEMP)s/%(name)s*.tar.gz" % params)
-    if len(l)!=1:
-        raise Py2debException("don't find source package tar.gz")
-    tar = os.path.basename(l[0])
-    shutil.move(l[0],tar)
-    ret.append(tar)
-
-    l=glob("%(TEMP)s/%(name)s*.dsc" % params)
-    if len(l)!=1:
-        raise Py2debException("don't find source package dsc")
-    tar = os.path.basename(l[0])
-    shutil.move(l[0],tar)
-    ret.append(tar)
-
-    l = glob("%(TEMP)s/%(name)s*.changes" % params)
-    if len(l)!=1:
-        raise Py2debException("don't find source package changes")
-    tar = os.path.basename(l[0])
-    shutil.move(l[0],tar)
-    ret.append(tar)
-
-    return ret
-
-
-class Py2dsc(object):
-
-    def __init__(self, StandardsVersion, BuildDepends, files, **kwargs):
-      self.options = kwargs # TODO: Is order important?
-      self.StandardsVersion = StandardsVersion
-      self.BuildDepends=BuildDepends
-      self.files=files
-
-    @property
-    def content(self):
-        content = ["%s: %s" % (k, v)
-                   for k,v in self.options.iteritems()]
-
-        if self.BuildDepends:
-            content.append("Build-Depends: %s" % self.BuildDepends)
-        if self.StandardsVersion:
-            content.append("Standards-Version: %s" % self.StandardsVersion)
-
-        content.append('Files:')
-
-        for onefile in self.files:
-            print onefile
-            md5 = md5sum(onefile)
-            size = os.stat(onefile).st_size.__str__()
-            content.append(' '+md5 + ' ' + size +' '+os.path.basename(onefile))
-
-        return "\n".join(content)+"\n\n"
-
-
-def py2dsc(TEMP, name, version, depends, author, mail, arch):
-    dsccontent = Py2dsc(
-        "%(version)s" % locals(),
-        "%(depends)s" % locals(),
-        ("%(TEMP)s/%(name)s_%(version)s.tar.gz" % locals(),),
-        Format='1.0',
-        Source="%(name)s" % locals(),
-        Version="%(version)s" % locals(),
-        Maintainer="%(author)s <%(mail)s>" % locals(),
-        Architecture="%(arch)s" % locals(),
-    )
-
-    filename = "%(TEMP)s/%(name)s_%(version)s.dsc" % locals()
-
-    f = open(filename, "wb")
-    try:
-        f.write(dsccontent.content)
-    finally:
-        f.close()
-
-    fileHandle = open('/tmp/py2deb.tmp', 'w')
-    try:
-        fileHandle.write('#!/bin/sh\n')
-        fileHandle.write("cd " + os.getcwd() + "\n")
-        # TODO Renable signing
-        # fileHandle.write("gpg --local-user %(mail)s --clearsign %(TEMP)s/%(name)s_%(version)s.dsc\n" % locals())
-        fileHandle.write("mv %(TEMP)s/%(name)s_%(version)s.dsc.asc %(filename)s\n" % locals())
-        fileHandle.write('\nexit')
-        fileHandle.close()
-    finally:
-        f.close()
-
-    commands.getoutput("chmod 777 /tmp/py2deb.tmp")
-    commands.getoutput("/tmp/py2deb.tmp")
-
-    return filename
-
-
-class Py2tar(object):
-
-    def __init__(self, dataDirectoryPath):
-        self._dataDirectoryPath = dataDirectoryPath
-
-    def packed(self):
-        return self._getSourcesFiles()
-
-    def _getSourcesFiles(self):
-        directoryPath = self._dataDirectoryPath
-
-        outputFileObj = StringIO.StringIO() # TODO: Do more transparently?
-
-        tarOutput = tarfile.TarFile.open('sources',
-                                 mode = "w:gz",
-                                 fileobj = outputFileObj)
-
-        # Note: We can't use this because we need to fiddle permissions:
-        #       tarOutput.add(directoryPath, arcname = "")
-
-        for root, dirs, files in os.walk(directoryPath):
-            archiveRoot = root[len(directoryPath):]
-
-            tarinfo = tarOutput.gettarinfo(root, archiveRoot)
-            # TODO: Make configurable?
-            tarinfo.uid = UID_ROOT
-            tarinfo.gid = GID_ROOT
-            tarinfo.uname = ""
-            tarinfo.gname = ""
-            tarOutput.addfile(tarinfo)
-
-            for f in  files:
-                tarinfo = tarOutput.gettarinfo(os.path.join(root, f),
-                                               os.path.join(archiveRoot, f))
-                tarinfo.uid = UID_ROOT
-                tarinfo.gid = GID_ROOT
-                tarinfo.uname = ""
-                tarinfo.gname = ""
-                tarOutput.addfile(tarinfo, file(os.path.join(root, f)))
-
-        tarOutput.close()
-
-        data_tar_gz = outputFileObj.getvalue()
-
-        return data_tar_gz
-
-
-def py2tar(DEST, TEMP, name, version):
-    tarcontent = Py2tar("%(DEST)s" % locals())
-    filename = "%(TEMP)s/%(name)s_%(version)s.tar.gz" % locals()
-    f = open(filename, "wb")
-    try:
-        f.write(tarcontent.packed())
-    finally:
-        f.close()
-    return filename
-
-
-class Py2debException(Exception):
-    pass
-
-
-SECTIONS_BY_POLICY = {
-    # http://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections
-    "debian": "admin, base, comm, contrib, devel, doc, editors, electronics, embedded, games, gnome, graphics, hamradio, interpreters, kde, libs, libdevel, mail, math, misc, net, news, non-free, oldlibs, otherosfs, perl, python, science, shells, sound, tex, text, utils, web, x11",
-    # http://maemo.org/forrest-images/pdf/maemo-policy.pdf
-    "chinook": "accessories, communication, games, multimedia, office, other, programming, support, themes, tools",
-    # http://wiki.maemo.org/Task:Package_categories
-    "diablo": "user/desktop, user/development, user/education, user/games, user/graphics, user/multimedia, user/navigation, user/network, user/office, user/science, user/system, user/utilities",
-    # http://wiki.maemo.org/Task:Fremantle_application_categories
-    "mer": "user/desktop, user/development, user/education, user/games, user/graphics, user/multimedia, user/navigation, user/network, user/office, user/science, user/system, user/utilities",
-    # http://wiki.maemo.org/Task:Fremantle_application_categories
-    "fremantle": "user/desktop, user/development, user/education, user/games, user/graphics, user/multimedia, user/navigation, user/network, user/office, user/science, user/system, user/utilities",
-}
-
-
-LICENSE_AGREEMENT = {
-        "gpl": """
-    This package is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This package is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this package; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-
-On Debian systems, the complete text of the GNU General
-Public License can be found in `/usr/share/common-licenses/GPL'.
-""",
-        "lgpl":"""
-    This package 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 of the License, or (at your option) any later version.
-
-    This package is distributed in the hope that 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 package; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-
-On Debian systems, the complete text of the GNU Lesser General
-Public License can be found in `/usr/share/common-licenses/LGPL'.
-""",
-        "bsd": """
-    Redistribution and use in source and binary forms, with or without
-    modification, are permitted under the terms of the BSD License.
-
-    THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-    ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-    SUCH DAMAGE.
-
-On Debian systems, the complete text of the BSD License can be
-found in `/usr/share/common-licenses/BSD'.
-""",
-        "artistic": """
-    This program is free software; you can redistribute it and/or modify it
-    under the terms of the "Artistic License" which comes with Debian.
-
-    THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
-    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
-    OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-On Debian systems, the complete text of the Artistic License
-can be found in `/usr/share/common-licenses/Artistic'.
-"""
-}
-
-
-class Py2deb(object):
-    """
-    heavily based on technic described here :
-    http://wiki.showmedo.com/index.php?title=LinuxJensMakingDeb
-    """
-    ## STATICS
-    clear = False  # clear build folder after py2debianization
-
-    SECTIONS = SECTIONS_BY_POLICY["debian"]
-
-    #http://www.debian.org/doc/debian-policy/footnotes.html#f69
-    ARCHS = "all i386 ia64 alpha amd64 armeb arm hppa m32r m68k mips mipsel powerpc ppc64 s390 s390x sh3 sh3eb sh4 sh4eb sparc darwin-i386 darwin-ia64 darwin-alpha darwin-amd64 darwin-armeb darwin-arm darwin-hppa darwin-m32r darwin-m68k darwin-mips darwin-mipsel darwin-powerpc darwin-ppc64 darwin-s390 darwin-s390x darwin-sh3 darwin-sh3eb darwin-sh4 darwin-sh4eb darwin-sparc freebsd-i386 freebsd-ia64 freebsd-alpha freebsd-amd64 freebsd-armeb freebsd-arm freebsd-hppa freebsd-m32r freebsd-m68k freebsd-mips freebsd-mipsel freebsd-powerpc freebsd-ppc64 freebsd-s390 freebsd-s390x freebsd-sh3 freebsd-sh3eb freebsd-sh4 freebsd-sh4eb freebsd-sparc kfreebsd-i386 kfreebsd-ia64 kfreebsd-alpha kfreebsd-amd64 kfreebsd-armeb kfreebsd-arm kfreebsd-hppa kfreebsd-m32r kfreebsd-m68k kfreebsd-mips kfreebsd-mipsel kfreebsd-powerpc kfreebsd-ppc64 kfreebsd-s390 kfreebsd-s390x kfreebsd-sh3 kfreebsd-sh3eb kfreebsd-sh4 kfreebsd-sh4eb kfreebsd-sparc knetbsd-i386 knetbsd-ia64 knetbsd-alpha knetbsd-amd64 knetbsd-armeb knetbsd-arm knetbsd-hppa knetbsd-m32r knetbsd-m68k knetbsd-mips knetbsd-mipsel knetbsd-powerpc knetbsd-ppc64 knetbsd-s390 knetbsd-s390x knetbsd-sh3 knetbsd-sh3eb knetbsd-sh4 knetbsd-sh4eb knetbsd-sparc netbsd-i386 netbsd-ia64 netbsd-alpha netbsd-amd64 netbsd-armeb netbsd-arm netbsd-hppa netbsd-m32r netbsd-m68k netbsd-mips netbsd-mipsel netbsd-powerpc netbsd-ppc64 netbsd-s390 netbsd-s390x netbsd-sh3 netbsd-sh3eb netbsd-sh4 netbsd-sh4eb netbsd-sparc openbsd-i386 openbsd-ia64 openbsd-alpha openbsd-amd64 openbsd-armeb openbsd-arm openbsd-hppa openbsd-m32r openbsd-m68k openbsd-mips openbsd-mipsel openbsd-powerpc openbsd-ppc64 openbsd-s390 openbsd-s390x openbsd-sh3 openbsd-sh3eb openbsd-sh4 openbsd-sh4eb openbsd-sparc hurd-i386 hurd-ia64 hurd-alpha hurd-amd64 hurd-armeb hurd-arm hurd-hppa hurd-m32r hurd-m68k hurd-mips hurd-mipsel hurd-powerpc hurd-ppc64 hurd-s390 hurd-s390x hurd-sh3 hurd-sh3eb hurd-sh4 hurd-sh4eb hurd-sparc armel".split(" ")
-
-    # license terms taken from dh_make
-    LICENSES = list(LICENSE_AGREEMENT.iterkeys())
-
-    def __setitem__(self, path, files):
-
-        if not type(files)==list:
-            raise Py2debException("value of key path '%s' is not a list"%path)
-        if not files:
-            raise Py2debException("value of key path '%s' should'nt be empty"%path)
-        if not path.startswith("/"):
-            raise Py2debException("key path '%s' malformed (don't start with '/')"%path)
-        if path.endswith("/"):
-            raise Py2debException("key path '%s' malformed (shouldn't ends with '/')"%path)
-
-        nfiles=[]
-        for file in files:
-
-            if ".." in file:
-                raise Py2debException("file '%s' contains '..', please avoid that!"%file)
-
-
-            if "|" in file:
-                if file.count("|")!=1:
-                    raise Py2debException("file '%s' is incorrect (more than one pipe)"%file)
-
-                file, nfile = file.split("|")
-            else:
-                nfile=file  # same localisation
-
-            if os.path.isdir(file):
-                raise Py2debException("file '%s' is a folder, and py2deb refuse folders !"%file)
-
-            if not os.path.isfile(file):
-                raise Py2debException("file '%s' doesn't exist"%file)
-
-            if file.startswith("/"):    # if an absolute file is defined
-                if file==nfile:         # and not renamed (pipe trick)
-                    nfile=os.path.basename(file)   # it's simply copied to 'path'
-
-            nfiles.append((file, nfile))
-
-        nfiles.sort(lambda a, b: cmp(a[1], b[1]))    #sort according new name (nfile)
-
-        self.__files[path]=nfiles
-
-    def __getitem__(self, k):
-        return self.__files[k]
-
-    def __delitem__(self, k):
-        del self.__files[k]
-
-    def __init__(self,
-                    name,
-                    description="no description",
-                    license="gpl",
-                    depends="",
-                    section="utils",
-                    arch="all",
-
-                    url="",
-                    author = None,
-                    mail = None,
-
-                    preinstall = None,
-                    postinstall = None,
-                    preremove = None,
-                    postremove = None
-                ):
-
-        if author is None:
-            author = ("USERNAME" in os.environ) and os.environ["USERNAME"] or None
-            if author is None:
-                author = ("USER" in os.environ) and os.environ["USER"] or "unknown"
-
-        if mail is None:
-            mail = author+"@"+socket.gethostname()
-
-        self.name = name
-        self.prettyName = ""
-        self.description = description
-        self.upgradeDescription = ""
-        self.bugTracker = ""
-        self.license = license
-        self.depends = depends
-        self.recommends = ""
-        self.section = section
-        self.arch = arch
-        self.url = url
-        self.author = author
-        self.mail = mail
-        self.icon = ""
-        self.distribution = ""
-        self.respository = ""
-        self.urgency = "low"
-
-        self.preinstall = preinstall
-        self.postinstall = postinstall
-        self.preremove = preremove
-        self.postremove = postremove
-
-        self.__files={}
-
-    def __repr__(self):
-        name = self.name
-        license = self.license
-        description = self.description
-        depends = self.depends
-        recommends = self.recommends
-        section = self.section
-        arch = self.arch
-        url = self.url
-        author = self.author
-        mail = self.mail
-
-        preinstall = self.preinstall
-        postinstall = self.postinstall
-        preremove = self.preremove
-        postremove = self.postremove
-
-        paths=self.__files.keys()
-        paths.sort()
-        files=[]
-        for path in paths:
-            for file, nfile in self.__files[path]:
-                #~ rfile=os.path.normpath(os.path.join(path, nfile))
-                rfile=os.path.join(path, nfile)
-                if nfile==file:
-                    files.append(rfile)
-                else:
-                    files.append(rfile + " (%s)"%file)
-
-        files.sort()
-        files = "\n".join(files)
-
-
-        lscripts = [    preinstall and "preinst",
-                        postinstall and "postinst",
-                        preremove and "prerm",
-                        postremove and "postrm",
-                    ]
-        scripts = lscripts and ", ".join([i for i in lscripts if i]) or "None"
-        return """
-----------------------------------------------------------------------
-NAME        : %(name)s
-----------------------------------------------------------------------
-LICENSE     : %(license)s
-URL         : %(url)s
-AUTHOR      : %(author)s
-MAIL        : %(mail)s
-----------------------------------------------------------------------
-DEPENDS     : %(depends)s
-RECOMMENDS  : %(recommends)s
-ARCH        : %(arch)s
-SECTION     : %(section)s
-----------------------------------------------------------------------
-DESCRIPTION :
-%(description)s
-----------------------------------------------------------------------
-SCRIPTS : %(scripts)s
-----------------------------------------------------------------------
-FILES :
-%(files)s
-""" % locals()
-
-    def generate(self, version, changelog="", rpm=False, src=False, build=True, tar=False, changes=False, dsc=False):
-        """ generate a deb of version 'version', with or without 'changelog', with or without a rpm
-            (in the current folder)
-            return a list of generated files
-        """
-        if not sum([len(i) for i in self.__files.values()])>0:
-            raise Py2debException("no files are defined")
-
-        if not changelog:
-            changelog="* no changelog"
-
-        name = self.name
-        description = self.description
-        license = self.license
-        depends = self.depends
-        recommends = self.recommends
-        section = self.section
-        arch = self.arch
-        url = self.url
-        distribution = self.distribution
-        repository = self.repository
-        urgency = self.urgency
-        author = self.author
-        mail = self.mail
-        files = self.__files
-        preinstall = self.preinstall
-        postinstall = self.postinstall
-        preremove = self.preremove
-        postremove = self.postremove
-
-        if section not in Py2deb.SECTIONS:
-            raise Py2debException("section '%s' is unknown (%s)" % (section, str(Py2deb.SECTIONS)))
-
-        if arch not in Py2deb.ARCHS:
-            raise Py2debException("arch '%s' is unknown (%s)"% (arch, str(Py2deb.ARCHS)))
-
-        if license not in Py2deb.LICENSES:
-            raise Py2debException("License '%s' is unknown (%s)" % (license, str(Py2deb.LICENSES)))
-
-        # create dates (buildDate, buildDateYear)
-        d=datetime.now()
-        buildDate=d.strftime("%a, %d %b %Y %H:%M:%S +0000")
-        buildDateYear=str(d.year)
-
-        #clean description (add a space before each next lines)
-        description=description.replace("\r", "").strip()
-        description = "\n ".join(description.split("\n"))
-
-        #clean changelog (add 2 spaces before each next lines)
-        changelog=changelog.replace("\r", "").strip()
-        changelog = "\n  ".join(changelog.split("\n"))
-
-        TEMP = ".py2deb_build_folder"
-        DEST = os.path.join(TEMP, name)
-        DEBIAN = os.path.join(DEST, "debian")
-
-        packageContents = locals()
-
-        # let's start the process
-        try:
-            shutil.rmtree(TEMP)
-        except:
-            pass
-
-        os.makedirs(DEBIAN)
-        try:
-            rules=[]
-            dirs=[]
-            for path in files:
-                for ofile, nfile in files[path]:
-                    if os.path.isfile(ofile):
-                        # it's a file
-
-                        if ofile.startswith("/"): # if absolute path
-                            # we need to change dest
-                            dest=os.path.join(DEST, nfile)
-                        else:
-                            dest=os.path.join(DEST, ofile)
-
-                        # copy file to be packaged
-                        destDir = os.path.dirname(dest)
-                        if not os.path.isdir(destDir):
-                            os.makedirs(destDir)
-
-                        shutil.copy2(ofile, dest)
-
-                        ndir = os.path.join(path, os.path.dirname(nfile))
-                        nname = os.path.basename(nfile)
-
-                        # make a line RULES to be sure the destination folder is created
-                        # and one for copying the file
-                        fpath = "/".join(["$(CURDIR)", "debian", name+ndir])
-                        rules.append('mkdir -p "%s"' % fpath)
-                        rules.append('cp -a "%s" "%s"' % (ofile, os.path.join(fpath, nname)))
-
-                        # append a dir
-                        dirs.append(ndir)
-
-                    else:
-                        raise Py2debException("unknown file '' "%ofile) # shouldn't be raised (because controlled before)
-
-            # make rules right
-            rules= "\n\t".join(rules) + "\n"
-            packageContents["rules"] = rules
-
-            # make dirs right
-            dirs= [i[1:] for i in set(dirs)]
-            dirs.sort()
-
-            #==========================================================================
-            # CREATE debian/dirs
-            #==========================================================================
-            open(os.path.join(DEBIAN, "dirs"), "w").write("\n".join(dirs))
-
-            #==========================================================================
-            # CREATE debian/changelog
-            #==========================================================================
-            clog="""%(name)s (%(version)s) stable; urgency=low
-
-  %(changelog)s
-
- -- %(author)s <%(mail)s>  %(buildDate)s
-""" % packageContents
-
-            open(os.path.join(DEBIAN, "changelog"), "w").write(clog)
-
-            #==========================================================================
-            #Create pre/post install/remove
-            #==========================================================================
-            def mkscript(name, dest):
-                if name and name.strip()!="":
-                    if os.path.isfile(name):    # it's a file
-                        content = file(name).read()
-                    else:   # it's a script
-                        content = name
-                    open(os.path.join(DEBIAN, dest), "w").write(content)
-
-            mkscript(preinstall, "preinst")
-            mkscript(postinstall, "postinst")
-            mkscript(preremove, "prerm")
-            mkscript(postremove, "postrm")
-
-
-            #==========================================================================
-            # CREATE debian/compat
-            #==========================================================================
-            open(os.path.join(DEBIAN, "compat"), "w").write("5\n")
-
-            #==========================================================================
-            # CREATE debian/control
-            #==========================================================================
-            generalParagraphFields = [
-                "Source: %(name)s",
-                "Maintainer: %(author)s <%(mail)s>",
-                "Section: %(section)s",
-                "Priority: extra",
-                "Build-Depends: debhelper (>= 5)",
-                "Standards-Version: 3.7.2",
-            ]
-
-            specificParagraphFields = [
-                "Package: %(name)s",
-                "Architecture: %(arch)s",
-                "Depends: %(depends)s",
-                "Recommends: %(recommends)s",
-                "Description: %(description)s",
-            ]
-
-            if self.prettyName:
-                prettyName = "XSBC-Maemo-Display-Name: %s" % self.prettyName.strip()
-                specificParagraphFields.append("\n  ".join(prettyName.split("\n")))
-
-            if self.bugTracker:
-                bugTracker = "XSBC-Bugtracker: %s" % self.bugTracker.strip()
-                specificParagraphFields.append("\n  ".join(bugTracker.split("\n")))
-
-            if self.upgradeDescription:
-                upgradeDescription = "XSBC-Maemo-Upgrade-Description: %s" % self.upgradeDescription.strip()
-                specificParagraphFields.append("\n  ".join(upgradeDescription.split("\n")))
-
-            if self.icon:
-                f = open(self.icon, "rb")
-                try:
-                    rawIcon = f.read()
-                finally:
-                    f.close()
-                uueIcon = base64.b64encode(rawIcon)
-                uueIconLines = []
-                for i, c in enumerate(uueIcon):
-                    if i % 60 == 0:
-                        uueIconLines.append("")
-                    uueIconLines[-1] += c
-                uueIconLines[0:0] = ("XSBC-Maemo-Icon-26:", )
-                specificParagraphFields.append("\n  ".join(uueIconLines))
-
-            generalParagraph = "\n".join(generalParagraphFields)
-            specificParagraph = "\n".join(specificParagraphFields)
-            controlContent = "\n\n".join((generalParagraph, specificParagraph)) % packageContents
-            open(os.path.join(DEBIAN, "control"), "w").write(controlContent)
-
-            #==========================================================================
-            # CREATE debian/copyright
-            #==========================================================================
-            packageContents["txtLicense"] = LICENSE_AGREEMENT[license]
-            packageContents["pv"] =__version__
-            txt="""This package was py2debianized(%(pv)s) by %(author)s <%(mail)s> on
-%(buildDate)s.
-
-It was downloaded from %(url)s
-
-Upstream Author: %(author)s <%(mail)s>
-
-Copyright: %(buildDateYear)s by %(author)s
-
-License:
-
-%(txtLicense)s
-
-The Debian packaging is (C) %(buildDateYear)s, %(author)s <%(mail)s> and
-is licensed under the GPL, see above.
-
-
-# Please also look if there are files or directories which have a
-# different copyright/license attached and list them here.
-""" % packageContents
-            open(os.path.join(DEBIAN, "copyright"), "w").write(txt)
-
-            #==========================================================================
-            # CREATE debian/rules
-            #==========================================================================
-            txt="""#!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-
-
-
-CFLAGS = -Wall -g
-
-ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
-       CFLAGS += -O0
-else
-       CFLAGS += -O2
-endif
-
-configure: configure-stamp
-configure-stamp:
-       dh_testdir
-       # Add here commands to configure the package.
-
-       touch configure-stamp
-
-
-build: build-stamp
-
-build-stamp: configure-stamp
-       dh_testdir
-       touch build-stamp
-
-clean:
-       dh_testdir
-       dh_testroot
-       rm -f build-stamp configure-stamp
-       dh_clean
-
-install: build
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_installdirs
-
-       # ======================================================
-       #$(MAKE) DESTDIR="$(CURDIR)/debian/%(name)s" install
-       mkdir -p "$(CURDIR)/debian/%(name)s"
-
-       %(rules)s
-       # ======================================================
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-# Build architecture-dependent files here.
-binary-arch: build install
-       dh_testdir
-       dh_testroot
-       dh_installchangelogs debian/changelog
-       dh_installdocs
-       dh_installexamples
-#      dh_install
-#      dh_installmenu
-#      dh_installdebconf
-#      dh_installlogrotate
-#      dh_installemacsen
-#      dh_installpam
-#      dh_installmime
-#      dh_python
-#      dh_installinit
-#      dh_installcron
-#      dh_installinfo
-       dh_installman
-       dh_link
-       dh_strip
-       dh_compress
-       dh_fixperms
-#      dh_perl
-#      dh_makeshlibs
-       dh_installdeb
-       dh_shlibdeps
-       dh_gencontrol
-       dh_md5sums
-       dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install configure
-""" % packageContents
-            open(os.path.join(DEBIAN, "rules"), "w").write(txt)
-            os.chmod(os.path.join(DEBIAN, "rules"), 0755)
-
-            ###########################################################################
-            ###########################################################################
-            ###########################################################################
-
-            generatedFiles = []
-
-            if build:
-                #http://www.debian.org/doc/manuals/maint-guide/ch-build.fr.html
-                ret = os.system('cd "%(DEST)s"; dpkg-buildpackage -tc -rfakeroot -us -uc' % packageContents)
-                if ret != 0:
-                    raise Py2debException("buildpackage failed (see output)")
-
-                l=glob("%(TEMP)s/%(name)s*.deb" % packageContents)
-                if len(l) != 1:
-                    raise Py2debException("didn't find builded deb")
-
-                tdeb = l[0]
-                deb = os.path.basename(tdeb)
-                shutil.move(tdeb, deb)
-
-                generatedFiles = [deb, ]
-
-                if rpm:
-                    rpmFilename = deb2rpm(deb)
-                    generatedFiles.append(rpmFilename)
-
-                if src:
-                    tarFilename = py2src(TEMP, name)
-                    generatedFiles.append(tarFilename)
-
-            if tar:
-                tarFilename = py2tar(DEST, TEMP, name, version)
-                generatedFiles.append(tarFilename)
-
-            if dsc:
-                dscFilename = py2dsc(TEMP, name, version, depends, author, mail, arch)
-                generatedFiles.append(dscFilename)
-
-            if changes:
-                changesFilenames = py2changes(packageContents)
-                generatedFiles.extend(changesFilenames)
-
-            return generatedFiles
-
-        #~ except Exception,m:
-            #~ raise Py2debException("build error :"+str(m))
-
-        finally:
-            if Py2deb.clear:
-                shutil.rmtree(TEMP)
-
-
-if __name__ == "__main__":
-    try:
-        os.chdir(os.path.dirname(sys.argv[0]))
-    except:
-        pass
-
-    p=Py2deb("python-py2deb")
-    p.description="Generate simple deb(/rpm/tgz) from python (2.4, 2.5 and 2.6)"
-    p.url = "http://www.manatlan.com/page/py2deb"
-    p.author=__author__
-    p.mail=__mail__
-    p.depends = "dpkg-dev, fakeroot, alien, python"
-    p.section="python"
-    p["/usr/lib/python2.6/dist-packages"] = ["py2deb.py", ]
-    p["/usr/lib/python2.5/site-packages"] = ["py2deb.py", ]
-    p["/usr/lib/python2.4/site-packages"] = ["py2deb.py", ]
-    #~ p.postinstall = "s.py"
-    #~ p.preinstall = "s.py"
-    #~ p.postremove = "s.py"
-    #~ p.preremove = "s.py"
-    print p
-    print p.generate(__version__, changelog = __doc__, src=True)
diff --git a/support/scale.py b/support/scale.py
new file mode 100755 (executable)
index 0000000..f9eb784
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+from __future__ import division
+
+import os
+import logging
+
+from PIL import Image
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+def main(args):
+       import optparse
+       parser = optparse.OptionParser()
+       parser.add_option(
+               "--input", dest="input",
+               help="Input image to scale", metavar="INPUT"
+       )
+       parser.add_option(
+               "--output", dest="output",
+               help="Scaled image", metavar="OUTPUT"
+       )
+       parser.add_option(
+               "--size", dest="size",
+               help="Icon size", metavar="SIZE"
+       )
+       options, positional  = parser.parse_args(args)
+       if positional:
+               parser.error("No positional arguments supported")
+       if None in [options.input, options.output, options.size]:
+               parser.error("Missing argument")
+       if options.size == "guess":
+               parts = reversed(os.path.split(options.output))
+               for part in parts:
+                       try:
+                               options.size = int(part)
+                               _moduleLogger.info("Assuming image size of %r" % options.size)
+                               break
+                       except ValueError:
+                               pass
+
+       icon = Image.open(options.input)
+       icon.thumbnail((options.size, options.size), Image.ANTIALIAS)
+       icon.save(options.output)
+
+
+if __name__ == "__main__":
+       import sys
+       retcode = main(sys.argv[1:])
+       sys.exit(retcode)