Initial import of all of the helpful utils
authorEd Page <eopage@byu.net>
Thu, 22 Apr 2010 22:42:17 +0000 (17:42 -0500)
committerEd Page <eopage@byu.net>
Thu, 22 Apr 2010 22:42:17 +0000 (17:42 -0500)
17 files changed:
.gitignore [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
src/__init__.py [new file with mode: 0644]
src/constants.py [new file with mode: 0644]
src/hildonize.py [new file with mode: 0644]
support/builddeb.py [new file with mode: 0755]
support/fake_py2deb.py [new file with mode: 0644]
support/py2deb.py [new file with mode: 0644]
support/pylint.rc [new file with mode: 0644]
support/test_syntax.py [new file with mode: 0755]
support/todo.py [new file with mode: 0755]
tests/test_utils.py [new file with mode: 0644]
www/download.html [new file with mode: 0644]
www/index.html [new file with mode: 0644]
www/screenshots.html [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..0d20b64
--- /dev/null
@@ -0,0 +1 @@
+*.pyc
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..5ab7695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\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.
+
+  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.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\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
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\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
+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
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\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.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library'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 library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..af801b1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,106 @@
+PROJECT_NAME=
+SOURCE_PATH=src
+SOURCE=$(shell find $(SOURCE_PATH) -iname "*.py")
+PROGRAM=$(SOURCE_PATH)/$(PROJECT_NAME).py
+DATA_TYPES=*.ini *.map *.glade *.png
+DATA=$(foreach type, $(DATA_TYPES), $(shell find $(SOURCE_PATH) -iname "$(type)"))
+OBJ=$(SOURCE:.py=.pyc)
+BUILD_PATH=./build
+TAG_FILE=~/.ctags/$(PROJECT_NAME).tags
+TODO_FILE=./TODO
+
+DEBUGGER=winpdb
+UNIT_TEST=nosetests --with-doctest -w .
+SYNTAX_TEST=support/test_syntax.py
+STYLE_TEST=../../Python/tools/pep8.py --ignore=W191,E501
+LINT_RC=./support/pylint.rc
+LINT=pylint --rcfile=$(LINT_RC)
+PROFILE_GEN=python -m cProfile -o .profile
+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).py
+
+profile: $(OBJ)
+       $(PROFILE_GEN) $(PROGRAM)
+       $(PROFILE_VIEW)
+
+debug: $(OBJ)
+       $(DEBUGGER) $(PROGRAM)
+
+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)) ; )
+       #$(foreach file, $(OBJ), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; )
+       cp support/$(PROJECT_NAME).desktop $(BUILD_PATH)/generic
+       cp support/icons/hicolor/26x26/hildon/$(PROJECT_NAME).png $(BUILD_PATH)/generic/26x26-$(PROJECT_NAME).png
+       cp support/icons/hicolor/64x64/hildon/$(PROJECT_NAME).png $(BUILD_PATH)/generic/64x64-$(PROJECT_NAME).png
+       cp support/icons/hicolor/scalable/hildon/$(PROJECT_NAME).png $(BUILD_PATH)/generic/scale-$(PROJECT_NAME).png
+       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
+
+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
+
+lint: $(OBJ)
+       $(foreach file, $(SOURCE), $(LINT) $(file) ; )
+
+tags: $(TAG_FILE) 
+
+todo: $(TODO_FILE)
+
+clean:
+       rm -Rf $(OBJ)
+       rm -Rf $(BUILD_PATH)
+       rm -Rf $(TODO_FILE)
+
+distclean:
+       rm -Rf $(OBJ)
+       rm -Rf $(BUILD_PATH)
+       rm -Rf $(TAG_FILE)
+       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
+
+$(TAG_FILE): $(OBJ)
+       mkdir -p $(dir $(TAG_FILE))
+       $(CTAGS) -o $(TAG_FILE) $(SOURCE)
+
+$(TODO_FILE): $(SOURCE)
+       @- $(TODO_FINDER) $(SOURCE) > $(TODO_FILE)
+
+%.pyc: %.py
+       $(SYNTAX_TEST) $<
+
+#Makefile Debugging
+#Target to print any variable, can be added to the dependencies of any other target
+#Userfule flags for make, -d, -p, -n
+print-%: ; @$(error $* is $($*) ($(value $*)) (from $(origin $*)))
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..ac2fe23
--- /dev/null
+++ b/README
@@ -0,0 +1,4 @@
+Building a package
+===================
+Run
+       make package
diff --git a/src/__init__.py b/src/__init__.py
new file mode 100644 (file)
index 0000000..4265cc3
--- /dev/null
@@ -0,0 +1 @@
+#!/usr/bin/env python
diff --git a/src/constants.py b/src/constants.py
new file mode 100644 (file)
index 0000000..bcaa5a9
--- /dev/null
@@ -0,0 +1,10 @@
+import os
+
+__pretty_app_name__ = ""
+__app_name__ = ""
+__version__ = "0.1.0"
+__build__ = 0
+__app_magic__ = 0xdeadbeef
+_data_path_ = os.path.join(os.path.expanduser("~"), ".%s" % __app_name__)
+_user_settings_ = "%s/settings.ini" % _data_path_
+_user_logpath_ = "%s/%s.log" % (_data_path_, __app_name__)
diff --git a/src/hildonize.py b/src/hildonize.py
new file mode 100644 (file)
index 0000000..77d585a
--- /dev/null
@@ -0,0 +1,741 @@
+#!/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_title(window, title):
+       pass
+
+
+def _null_set_application_title(window, title):
+       window.set_title(title)
+
+
+if IS_HILDON_SUPPORTED:
+       set_application_title = _hildon_set_application_title
+else:
+       set_application_title = _null_set_application_title
+
+
+def _fremantle_hildonize_window(app, window):
+       oldWindow = window
+       newWindow = hildon.StackableWindow()
+       oldWindow.get_child().reparent(newWindow)
+       app.add_window(newWindow)
+       return newWindow
+
+
+def _hildon_hildonize_window(app, window):
+       oldWindow = window
+       newWindow = hildon.Window()
+       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 _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/support/builddeb.py b/support/builddeb.py
new file mode 100755 (executable)
index 0000000..203beaf
--- /dev/null
@@ -0,0 +1,149 @@
+#!/usr/bin/python2.5
+
+import os
+import sys
+
+try:
+       import py2deb
+except ImportError:
+       import fake_py2deb as py2deb
+
+import constants
+
+
+__appname__ = constants.__app_name__
+__description__ = """
+.
+Homepage:
+"""
+__author__ = "Ed Page"
+__email__ = "eopage@byu.net"
+__version__ = constants.__version__
+__build__ = constants.__build__
+__changelog__ = """
+"""
+
+
+__postinstall__ = """#!/bin/sh -e
+
+gtk-update-icon-cache -f /usr/share/icons/hicolor
+"""
+
+
+def find_files(path):
+       for root, dirs, files in os.walk(path):
+               for file in files:
+                       if file.startswith("src-"):
+                               fileParts = file.split("-")
+                               unused, relPathParts, newName = fileParts[0], fileParts[1:-1], fileParts[-1]
+                               assert unused == "src"
+                               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 = ""
+       p.upgradeDescription = __changelog__.split("\n\n", 1)[0]
+       p.author = __author__
+       p.mail = __email__
+       p.license = "lgpl"
+       p.depends = ", ".join([
+               "python2.6 | python2.5",
+               "python-gtk2 | python2.5-gtk2",
+               "python-xml | python2.5-xml",
+               "python-dbus | python2.5-dbus",
+       ])
+       maemoSpecificDepends = ", python-osso | python2.5-osso, python-hildon | python2.5-hildon"
+       p.depends += {
+               "debian": ", python-glade2",
+               "diablo": maemoSpecificDepends + ", python2.5-conic",
+               "fremantle": maemoSpecificDepends + ", python-glade2, python-alarm",
+       }[distribution]
+       p.recommends = ", ".join([
+       ])
+       p.section = {
+               "debian": "",
+               "diablo": "",
+               "fremantle": "",
+       }[distribution]
+       p.arch = "all"
+       p.urgency = "low"
+       p.distribution = "diablo fremantle debian"
+       p.repository = "extras"
+       p.changelog = __changelog__
+       p.postinstall = __postinstall__
+       p.icon = {
+               "debian": "",
+               "diablo": "",
+               "fremantle": "", # Fremantle natively uses 48x48
+       }[distribution]
+       p["/usr/bin"] = [ "" ]
+       for relPath, files in unflatten_files(find_files(".")).iteritems():
+               fullPath = ""
+               if relPath:
+                       fullPath += os.sep+relPath
+               p[fullPath] = list(
+                       "|".join((oldName, newName))
+                       for (oldName, newName) in files
+               )
+       p["/usr/share/applications/hildon"] = [""]
+       p["/usr/share/icons/hicolor/26x26/hildon"] = [""]
+       p["/usr/share/icons/hicolor/64x64/hildon"] = [""]
+       p["/usr/share/icons/hicolor/scalable/hildon"] = [""]
+
+       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/fake_py2deb.py b/support/fake_py2deb.py
new file mode 100644 (file)
index 0000000..5d6149d
--- /dev/null
@@ -0,0 +1,56 @@
+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/py2deb.py b/support/py2deb.py
new file mode 100644 (file)
index 0000000..4a47513
--- /dev/null
@@ -0,0 +1,991 @@
+#!/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 __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/pylint.rc b/support/pylint.rc
new file mode 100644 (file)
index 0000000..2a371a1
--- /dev/null
@@ -0,0 +1,305 @@
+# lint Python modules using external checkers.
+#
+# This is the main checker controling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories.
+#enable-msg-cat=
+
+# Disable all messages in the listed categories.
+#disable-msg-cat=
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+disable-msg=W0403,W0612,W0613,C0103,C0111,C0301,R0903,W0142,W0603,R0904,R0921,R0201
+
+[REPORTS]
+
+# set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=colorized
+
+# Include message's id in output
+include-ids=yes
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note).You have access to the variables errors warning, statement which
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+#
+[VARIABLES]
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# try to find bugs in the code using type inference
+# 
+[TYPECHECK]
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# When zope mode is activated, consider the acquired-members option to ignore
+# access to some undefined attributes.
+zope=no
+
+# List of members which are usually get through zope's acquisition mecanism and
+# so shouldn't trigger E0201 when accessed (need zope=yes to be considered).
+acquired-members=REQUEST,acl_users,aq_parent
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=15
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=1
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Maximum number of characters on a single line.
+# @note Limiting this to the most extreme cases
+max-line-length=100
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string='\t'
diff --git a/support/test_syntax.py b/support/test_syntax.py
new file mode 100755 (executable)
index 0000000..65a373c
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+import commands
+
+
+verbose = False
+
+
+def syntax_test(file):
+       commandTemplate = """
+       python -t -t -W all -c "import py_compile; py_compile.compile ('%(filename)s', doraise=False)" """
+       compileCommand = commandTemplate % {"filename": file}
+       (status, text) = commands.getstatusoutput (compileCommand)
+       text = text.rstrip()
+       passed = len(text) == 0
+
+       if passed:
+               output = ("Syntax is correct for "+file) if verbose else ""
+       else:
+               output = ("Syntax is invalid for %s\n" % file) if verbose else ""
+               output += text
+       return (passed, output)
+
+
+if __name__ == "__main__":
+       import sys
+       import os
+       import optparse
+
+       opar = optparse.OptionParser()
+       opar.add_option("-v", "--verbose", dest="verbose", help="Toggle verbosity", action="store_true", default=False)
+       options, args = opar.parse_args(sys.argv[1:])
+       verbose = options.verbose
+
+       completeOutput = []
+       allPassed = True
+       for filename in args:
+               passed, output = syntax_test(filename)
+               if not passed:
+                       allPassed = False
+               if output.strip():
+                       completeOutput.append(output)
+       print "\n".join(completeOutput)
+
+       sys.exit(0 if allPassed else 1);
diff --git a/support/todo.py b/support/todo.py
new file mode 100755 (executable)
index 0000000..90cbd04
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+import itertools
+
+
+verbose = False
+
+
+def tag_parser(file, tag):
+       """
+       >>> nothing = []
+       >>> for todo in tag_parser(nothing, "@todo"):
+       ...     print todo
+       ...
+       >>> one = ["@todo Help!"]
+       >>> for todo in tag_parser(one, "@todo"):
+       ...     print todo
+       ...
+       1: @todo Help!
+       >>> mixed = ["one", "@todo two", "three"]
+       >>> for todo in tag_parser(mixed, "@todo"):
+       ...     print todo
+       ...
+       2: @todo two
+       >>> embedded = ["one @todo two", "three"]
+       >>> for todo in tag_parser(embedded, "@todo"):
+       ...     print todo
+       ...
+       1: @todo two
+       >>> continuation = ["one", "@todo two", " three"]
+       >>> for todo in tag_parser(continuation, "@todo"):
+       ...     print todo
+       ...
+       2: @todo two three
+       >>> series = ["one", "@todo two", "@todo three"]
+       >>> for todo in tag_parser(series, "@todo"):
+       ...     print todo
+       ...
+       2: @todo two
+       3: @todo three
+       """
+       currentTodo = []
+       prefix = None
+       for lineNumber, line in enumerate(file):
+               column = line.find(tag)
+               if column != -1:
+                       if currentTodo:
+                               yield "\n".join (currentTodo)
+                       prefix = line[0:column]
+                       currentTodo = ["%d: %s" % (lineNumber+1, line[column:].strip())]
+               elif prefix is not None and len(prefix)+1 < len(line) and line.startswith(prefix) and line[len(prefix)].isspace():
+                       currentTodo.append (line[len(prefix):].rstrip())
+               elif currentTodo:
+                       yield "\n".join (currentTodo)
+                       currentTodo = []
+                       prefix = None
+       if currentTodo:
+               yield "\n".join (currentTodo)
+
+
+def tag_finder(filename, tag):
+       todoList = []
+
+       with open(filename) as file:
+               body = "\n".join (tag_parser(file, tag))
+       passed = not body
+       if passed:
+               output = "No %s's for %s" % (tag, filename) if verbose else ""
+       else:
+               header = "%s's for %s:\n" % (tag, filename) if verbose else ""
+               output = header + body
+               output += "\n" if verbose else ""
+
+       return (passed, output)
+
+
+if __name__ == "__main__":
+       import sys
+       import os
+       import optparse
+
+       opar = optparse.OptionParser()
+       opar.add_option("-v", "--verbose", dest="verbose", help="Toggle verbosity", action="store_true", default=False)
+       options, args = opar.parse_args(sys.argv[1:])
+       verbose = options.verbose
+
+       bugsAsError = True
+       todosAsError = False
+
+       completeOutput = []
+       allPassed = True
+       for filename in args:
+               bugPassed, bugOutput = tag_finder(filename, "@bug")
+               todoPassed, todoOutput = tag_finder(filename, "@todo")
+               output = "\n".join ([bugOutput, todoOutput])
+               if (not bugPassed and bugsAsError) or (not todoPassed and todosAsError):
+                       allPassed = False
+               output = output.strip()
+               if output:
+                       completeOutput.append(filename+":\n"+output+"\n\n")
+       print "\n".join(completeOutput)
+       
+       sys.exit(0 if allPassed else 1);
diff --git a/tests/test_utils.py b/tests/test_utils.py
new file mode 100644 (file)
index 0000000..a2da797
--- /dev/null
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+
+
+from __future__ import with_statement
+
+import inspect
+import contextlib
+import functools
+
+
+def TODO(func):
+       """
+       unittest test method decorator that ignores
+       exceptions raised by test
+
+       Used to annotate test methods for code that may
+       not be written yet.  Ignores failures in the
+       annotated test method; fails if the text
+       unexpectedly succeeds.
+       !author http://kbyanc.blogspot.com/2007/06/pythons-unittest-module-aint-that-bad.html
+
+       Example:
+       >>> import unittest
+       >>> class ExampleTestCase(unittest.TestCase):
+       ...     @TODO
+       ...     def testToDo(self):
+       ...             MyModule.DoesNotExistYet('boo')
+       ...
+       """
+
+       @functools.wraps(func)
+       def wrapper(*args, **kw):
+               try:
+                       func(*args, **kw)
+                       succeeded = True
+               except:
+                       succeeded = False
+               assert succeeded is False, \
+                       "%s marked TODO but passed" % func.__name__
+       return wrapper
+
+
+def PlatformSpecific(platformList):
+       """
+       unittest test method decorator that only
+       runs test method if os.name is in the
+       given list of platforms
+       !author http://kbyanc.blogspot.com/2007/06/pythons-unittest-module-aint-that-bad.html
+       Example:
+       >>> import unittest
+       >>> class ExampleTestCase(unittest.TestCase):
+       ...     @PlatformSpecific(('mac', ))
+       ...     def testMacOnly(self):
+       ...             MyModule.SomeMacSpecificFunction()
+       ...
+       """
+
+       def decorator(func):
+               import os
+
+               @functools.wraps(func)
+               def wrapper(*args, **kw):
+                       if os.name in platformList:
+                               return func(*args, **kw)
+               return wrapper
+       return decorator
+
+
+def CheckReferences(func):
+       """
+       !author http://kbyanc.blogspot.com/2007/06/pythons-unittest-module-aint-that-bad.html
+       """
+
+       @functools.wraps(func)
+       def wrapper(*args, **kw):
+               refCounts = []
+               for i in range(5):
+                       func(*args, **kw)
+                       refCounts.append(XXXGetRefCount())
+               assert min(refCounts) != max(refCounts), "Reference counts changed - %r" % refCounts
+
+       return wrapper
+
+
+@contextlib.contextmanager
+def expected(exception):
+       """
+       >>> with expected2(ZeroDivisionError):
+       ...     1 / 0
+       >>> with expected2(AssertionError("expected ZeroDivisionError to have been thrown")):
+       ...     with expected(ZeroDivisionError):
+       ...             1 / 2
+       Traceback (most recent call last):
+               File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+                       compileflags, 1) in test.globs
+               File "<doctest libraries.recipes.context.expected[1]>", line 3, in <module>
+                       1 / 2
+               File "/media/data/Personal/Development/bzr/Recollection-trunk/src/libraries/recipes/context.py", line 139, in __exit__
+                       assert t is not None, ("expected {0:%s} to have been thrown" % (self._t.__name__))
+       AssertionError: expected {0:ZeroDivisionError} to have been thrown
+       >>> with expected2(Exception("foo")):
+       ...     raise Exception("foo")
+       >>> with expected2(Exception("bar")):
+       ...     with expected(Exception("foo")): # this won't catch it
+       ...             raise Exception("bar")
+       ...     assert False, "should not see me"
+       >>> with expected2(Exception("can specify")):
+       ...     raise Exception("can specify prefixes")
+       >>> with expected2(Exception("Base class fun")):
+       True
+       >>> True
+       False
+       """
+       if isinstance(exception, Exception):
+               excType, excValue = type(exception), str(exception)
+       elif isinstance(exception, type):
+               excType, excValue = exception, ""
+
+       try:
+               yield
+       except Exception, e:
+               if not (excType in inspect.getmro(type(e)) and str(e).startswith(excValue)):
+                       raise
+       else:
+               raise AssertionError("expected {0:%s} to have been thrown" % excType.__name__)
+
+
+if __name__ == "__main__":
+       import doctest
+       doctest.testmod()
diff --git a/www/download.html b/www/download.html
new file mode 100644 (file)
index 0000000..a2b137b
--- /dev/null
@@ -0,0 +1,36 @@
+<html>
+       <head>
+               <title></title>
+       </head>
+       <body>
+               <h1>
+               </h1>
+               <h2></h2>
+               <h3><a href="index.html">[About]</a> <a href="screenshots.html">[Screenshots]</a> <a href="download.html">[Download]</a>
+               <h3>Download</h3>
+
+               <h4>Packages</h4>
+               <p>Maemo 5 and Later: Go to the application installer, enable Extras, and download</p>
+
+               <p>Maemo 4.1 and Earlier: Add the <a href="http://wiki.maemo.org/Extras">Extras Repository</a> and check the App Manager</p>
+
+               <p>Linux: <a href="">.deb files</a></p>
+
+               <h3>Development</h3>
+               <h4>Source</h4>
+               <p>For the most up to date version check out <a href="">git</a>.
+               </p>
+               <p>Requires</p>
+               <ul>
+                       <li>PyGTK / Glade</li>
+                       <li>Python bindings for hildon, osso, dbus, and conic</li>
+               </ul>
+
+               <h4>Bugs</h4>
+
+               <p>Discuss your issue on <a href="">Maemo.Org Talk</a></p>
+
+               <p>File a <a href="">bug report</a></p>
+               <p>View  <a href="">existing bug reports</a></p>
+       </body>
+</html>
diff --git a/www/index.html b/www/index.html
new file mode 100644 (file)
index 0000000..b794594
--- /dev/null
@@ -0,0 +1,30 @@
+<html>
+       <head>
+               <title></title>
+       </head>
+       <body>
+               <h1>
+               </h1>
+               <h2></h2>
+
+               <h3><a href="">[About]</a> <a href="screenshots.html">[Screenshots]</a> <a href="download.html">[Download]</a>
+
+               <h3>About</h3>
+               <p>
+               </p>
+
+
+               <p>This has been tested on Maemo 5, Maemo 4.1, and Ubuntu 9.04.  Go check out some <a href="">reviews</a>  or join the <a href="">conversation</a></p>
+
+               <p>This is Free Software and available under the <a href="http://www.gnu.org/licenses/lgpl-2.1.html">LGPLv2.1</a>.
+
+               <h4>Features</h4>
+
+               <p>(See <a href="">Maemo.Org Talk</a>)</p>
+
+               <ul>
+                       <li></li>
+               </ul>
+
+       </body>
+</html>
diff --git a/www/screenshots.html b/www/screenshots.html
new file mode 100644 (file)
index 0000000..a7bdf98
--- /dev/null
@@ -0,0 +1,14 @@
+<html>
+       <head>
+               <title></title>
+       </head>
+       <body>
+               <h1>
+               </h1>
+               <h2></h2>
+               <h3><a href="index.html">[About]</a> <a href="screenshots.html">[Screenshots]</a> <a href="download.html">[Download]</a>
+
+               <h3>Screen shots</h3>
+
+       </body>
+</html>