Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #107 from elextr/geanypy

Adding Geanypy to geany-plugins
  • Loading branch information...
commit 6af2bf6d078a9d828e68dbc5c27eca9009b1e708 2 parents 7061c74 + 81a775b
@frlan frlan authored
Showing with 9,296 additions and 1 deletion.
  1. +1 −0  .gitignore
  2. +6 −0 MAINTAINERS
  3. +5 −1 Makefile.am
  4. +20 −0 build/geanypy.m4
  5. +1 −0  configure.ac
  6. +2 −0  geanypy/AUTHORS
  7. +339 −0 geanypy/COPYING
  8. +1 −0  geanypy/ChangeLog
  9. +4 −0 geanypy/Makefile.am
  10. +1 −0  geanypy/NEWS
  11. +73 −0 geanypy/README
  12. +170 −0 geanypy/doc/make.bat
  13. +67 −0 geanypy/doc/source/api.rst
  14. +40 −0 geanypy/doc/source/app.rst
  15. +216 −0 geanypy/doc/source/conf.py
  16. +55 −0 geanypy/doc/source/dialogs.rst
  17. +199 −0 geanypy/doc/source/document.rst
  18. 0  geanypy/doc/source/geany.rst
  19. +26 −0 geanypy/doc/source/index.rst
  20. +146 −0 geanypy/doc/source/install.rst
  21. +8 −0 geanypy/doc/source/intro.rst
  22. +121 −0 geanypy/doc/source/quickstart.rst
  23. +64 −0 geanypy/doc/source/starting.rst
  24. +18 −0 geanypy/geany/Makefile.am
  25. +69 −0 geanypy/geany/__init__.py
  26. +665 −0 geanypy/geany/console.py
  27. +172 −0 geanypy/geany/loader.py
  28. +179 −0 geanypy/geany/manager.py
  29. +123 −0 geanypy/geany/plugin.py
  30. +58 −0 geanypy/geany/signalmanager.py
  31. +325 −0 geanypy/m4/ax_python_devel.m4
  32. +26 −0 geanypy/m4/ax_python_library.m4
  33. +4 −0 geanypy/plugins/Makefile.am
  34. +313 −0 geanypy/plugins/console.py
  35. +18 −0 geanypy/plugins/demo.py
  36. +21 −0 geanypy/plugins/hello.py
  37. +39 −0 geanypy/src/Makefile.am
  38. +113 −0 geanypy/src/geanypy-app.c
  39. +131 −0 geanypy/src/geanypy-dialogs.c
  40. +562 −0 geanypy/src/geanypy-document.c
  41. +12 −0 geanypy/src/geanypy-document.h
  42. +466 −0 geanypy/src/geanypy-editor.c
  43. +21 −0 geanypy/src/geanypy-editor.h
  44. +310 −0 geanypy/src/geanypy-encoding.c
  45. +14 −0 geanypy/src/geanypy-encoding.h
  46. +278 −0 geanypy/src/geanypy-filetypes.c
  47. +12 −0 geanypy/src/geanypy-filetypes.h
  48. +230 −0 geanypy/src/geanypy-highlighting.c
  49. +78 −0 geanypy/src/geanypy-indentprefs.c
  50. +150 −0 geanypy/src/geanypy-interfaceprefs.c
  51. +53 −0 geanypy/src/geanypy-main.c
  52. +95 −0 geanypy/src/geanypy-mainwidgets.c
  53. +133 −0 geanypy/src/geanypy-msgwindow.c
  54. +80 −0 geanypy/src/geanypy-navqueue.c
  55. +280 −0 geanypy/src/geanypy-plugin.c
  56. +43 −0 geanypy/src/geanypy-plugin.h
  57. +191 −0 geanypy/src/geanypy-prefs.c
  58. +107 −0 geanypy/src/geanypy-project.c
  59. +14 −0 geanypy/src/geanypy-project.h
  60. +137 −0 geanypy/src/geanypy-scinotification.c
  61. +77 −0 geanypy/src/geanypy-scinotifyheader.c
  62. +1,006 −0 geanypy/src/geanypy-scintilla.c
  63. +32 −0 geanypy/src/geanypy-scintilla.h
  64. +95 −0 geanypy/src/geanypy-search.c
  65. +241 −0 geanypy/src/geanypy-signalmanager.c
  66. +10 −0 geanypy/src/geanypy-signalmanager.h
  67. +102 −0 geanypy/src/geanypy-templates.c
  68. +449 −0 geanypy/src/geanypy-uiutils.c
  69. +19 −0 geanypy/src/geanypy-uiutils.h
  70. +119 −0 geanypy/src/geanypy.h
  71. +38 −0 geanypy/src/makefile.win32
  72. +3 −0  po/POTFILES.in
View
1  .gitignore
@@ -7,6 +7,7 @@
*.so
*.dll
*.exe
+*.pyc
deps.mak
.deps/
.libs/
View
6 MAINTAINERS
@@ -114,6 +114,12 @@ M: Yura Siamashka <yurand2@gmail.com>
W: http://plugins.geany.org/geanyprj.html
S: Odd Fixes
+geanypy
+P: Lex Trotman <elextr@gmail.com>
+M: Lex Trotman <elextr@gmail.com>
+W: http://plugins.geany.org/geanypy.html
+S: Maintained
+
geanysendmail
P: Frank Lanitz <frank@frank.uvena.de>
M: Frank Lanitz <frank@frank.uvena.de>
View
6 Makefile.am
@@ -1,4 +1,4 @@
-ACLOCAL_AMFLAGS = -I build/cache -I build -I build/bundled --install
+ACLOCAL_AMFLAGS = -I build/cache -I build -I build/bundled -I geanypy/m4 --install
SUBDIRS = po
@@ -70,6 +70,10 @@ if ENABLE_GEANYPRJ
SUBDIRS += geanyprj
endif
+if ENABLE_GEANYPY
+SUBDIRS += geanypy
+endif
+
if ENABLE_GEANYSENDMAIL
SUBDIRS += geanysendmail
endif
View
20 build/geanypy.m4
@@ -0,0 +1,20 @@
+AC_DEFUN([GP_CHECK_GEANYPY],
+[
+ GP_ARG_DISABLE([Geanypy], [auto])
+ GP_CHECK_PLUGIN_GTK2_ONLY([Geanypy])
+ GP_COMMIT_PLUGIN_STATUS([Geanypy])
+ PKG_CHECK_MODULES([PYGTK], [pygtk-2.0])
+ AX_PYTHON_DEVEL([>= '2.6'])
+ AX_PYTHON_LIBRARY(,[AC_MSG_ERROR([Cannot find Python library])])
+ AC_SUBST([PYTHON])
+ AC_DEFINE_UNQUOTED(
+ [GEANYPY_PYTHON_LIBRARY],
+ ["$PYTHON_LIBRARY"],
+ [Location of Python library to dlopen()])
+ AC_CONFIG_FILES([
+ geanypy/Makefile
+ geanypy/src/Makefile
+ geanypy/geany/Makefile
+ geanypy/plugins/Makefile
+ ])
+])
View
1  configure.ac
@@ -41,6 +41,7 @@ GP_CHECK_GEANYMACRO
GP_CHECK_GEANYMINISCRIPT
GP_CHECK_GEANYNUMBEREDBOOKMARKS
GP_CHECK_GEANYPRJ
+GP_CHECK_GEANYPY
GP_CHECK_GEANYSENDMAIL
GP_CHECK_GEANYVC
GP_CHECK_GEANYPG
View
2  geanypy/AUTHORS
@@ -0,0 +1,2 @@
+Matthew Brush <mbrush@codebrainz.ca>
+Lex Trotman <elextr@gmail.com> (Geany-Plugins port)
View
339 geanypy/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
View
1  geanypy/ChangeLog
@@ -0,0 +1 @@
+Unused
View
4 geanypy/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/build/vars.auxfiles.mk
+#ACLOCAL_AMFLAGS += -I geanypy/m4
+SUBDIRS = src geany plugins
+plugin = geanypy
View
1  geanypy/NEWS
@@ -0,0 +1 @@
+UnNEWSed
View
73 geanypy/README
@@ -0,0 +1,73 @@
+GeanyPy
+=======
+
+Write Geany plugins in Python!
+
+Provides most of the standard Geany C API for Python.
+
+**Please note:** GeanyPy here in geany-plugins is based on the upstream
+at https://github.com/codebrainz/geanypy which is still under
+development, however it is useful as is. Parts of the existing API
+which mirror the Geany C API will probably not change unless the Geany API
+changes, however new API may be added. Also documentation is needed,
+contributions are welcome.
+
+How it works
+------------
+
+In the ``src/`` directory is a normal Geany plugin (``plugin.c``) which loads the
+Python interpreter. It then loads some C Python modules (``dialogs.c``,
+``documents.c``, etc.) that interface with Geany's C API. After that, it loads
+the ``geany`` Python module which is just glue/sugar-coating to make the C
+module more "Pythonic".
+
+To write a plugin, inherit from the ``geany.Plugin`` class and implmenent the
+required members (see ``geany/plugin.py`` documentation comments). Then put the
+plugin in a searched plugin path. Currently two locations are search for
+plugins. The first is ``PREFIX/share/geany/geanypy/plugins`` and the recommended
+location is under your personal Geany directory (usually
+``~/.config/geany/plugins/geanypy/plugins``). To load or unload plugins, click
+the Python Plugin Manager item under the Tools menu which will appear when you
+activate GeanyPy through Geany's regular plugin manager.
+
+When the GeanyPy plugin is loaded, it has an option to add a new tab to
+the notebook in the message window area that contains an interactive
+Python shell with the ``geany`` module pre-imported. You can tinker
+around with API with this console, for example::
+
+
+ import geany
+
+ doc = geany.document.open_file("/some/file/here")
+
+ print(doc.file_name)
+ print(doc.file_type.display_name)
+
+ geany.dialogs.show_msgbox("Hello World")
+
+ geany.main_widgets.window.set_title("Hello Window")
+
+ def on_document_open(doc):
+ print("Document '%s' was opened" % doc.file_name)
+
+ geany.signals.connect('document-open', on_document_open)
+
+
+Dependencies
+------------
+
+To build GeanyPy you need the following dependencies:
+
+* Python 2.6 or 2.7 and development files.
+* Geany 1.24+ and development files
+* PyGTK 2.0 and development files
+
+Running on Windows
+------------------
+
+Not currently supported for the geany-plugins version, see upstream
+https://github.com/codebrainz/geanypy.
+
+Upstream developed by Matthew Brush.
+
+Geany-plugins version maintained by elextr@lists.geany.org
View
170 geanypy/doc/make.bat
@@ -0,0 +1,170 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\GeanyPy.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\GeanyPy.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+:end
View
67 geanypy/doc/source/api.rst
@@ -0,0 +1,67 @@
+API Documentation
+*****************
+
+GeanyPy's API mimics quite closely Geany's C plugin API. The following
+documentation is broken down by file/module:
+
+The :mod:`geany` modules:
+
+.. toctree::
+ :maxdepth: 2
+
+ app.rst
+ dialogs.rst
+ document.rst
+
+The :mod:`geany` package and module
+===================================
+
+.. module:: geany
+
+All of GeanyPy's bindings are inside the :mod:`geany` package which also
+contains some stuff in it's :mod:`__init__` file, acting like a module itself.
+
+
+.. data:: app
+
+ An instance of :class:`app.App` to store application information.
+
+.. data:: main_widgets
+
+ An instance of :class:`mainwidgets.MainWidgets` to provide access to
+ Geany's important GTK+ widgets.
+
+.. data:: signals
+
+ An instance of :class:`signalmanager.SignalManager` which manages the
+ connection, disconnection, and emission of Geany's signals. You can
+ use this as follows::
+
+ geany.signals.connect('document-open', some_callback_function)
+
+.. function:: is_realized()
+
+ This function, which is actually in the :mod:`geany.main` module will tell
+ you if Geany's main window is realized (shown).
+
+.. function:: locale_init()
+
+ Again, from the :mod:`geany.main` module, this will initialize the `gettext`
+ translation system.
+
+.. function:: reload_configuration()
+
+ Also from the :mod:`geany.main` module, this function will cause Geany to
+ reload most if it's configuration files without restarting.
+
+ Currently the following files are reloaded:
+
+ * all template files
+ * new file templates
+ * the New (with template) menus will be updated
+ * Snippets (snippets.conf)
+ * filetype extensions (filetype_extensions.conf)
+ * `settings` and `build_settings` sections of the filetype definition files.
+
+ Plugins may call this function if they changed any of these files (e.g. a
+ configuration file editor plugin).
View
40 geanypy/doc/source/app.rst
@@ -0,0 +1,40 @@
+The :mod:`app` module
+*********************
+
+.. module:: app
+ :synopsis: Application settings
+
+This modules contains a class to access application settings.
+
+:class:`App` Objects
+====================
+
+.. class:: App
+
+This class is initialized automatically and by the :mod:`geany` module and
+shouldn't be initalized by users. An instance of it is available through
+the :data:`geany.app` attribute of the :mod:`geany` module.
+
+All members of the :class:`App` are read-only properties.
+
+ .. attribute:: App.configdir
+
+ User configuration directory, usually ~/.config/geany. To store configuration
+ files for your plugin, it's a good idea to use something like this::
+
+ conf_path = os.path.join(geany.app.configdir, "plugins", "yourplugin",
+ "yourconfigfile.conf")
+
+ .. attribute:: App.debug_mode
+
+ If True, debug messages should be printed. For example, if you want to make
+ a :py:func:`print` function that only prints when :attr:`App.debug_mode`
+ is active, you could do something like this::
+
+ def debug_print(message):
+ if geany.app.debug_mode:
+ print(message)
+
+ .. attribute:: App.project
+
+ If not :py:obj:`None`, the a :class:`project.Project` for currently active project.
View
216 geanypy/doc/source/conf.py
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+#
+# GeanyPy documentation build configuration file, created by
+# sphinx-quickstart on Sun Aug 7 12:42:52 2011.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'GeanyPy'
+copyright = u'2011, Matthew Brush (mbrush [at] codebrainz [dot] ca)'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.0'
+# The full version, including alpha/beta/rc tags.
+release = '1.0'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'GeanyPydoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'GeanyPy.tex', u'GeanyPy Documentation',
+ u'Matthew Brush \\textless{}mbrush@codebrainz.ca\\textgreater{}', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'geanypy', u'GeanyPy Documentation',
+ [u'Matthew Brush <mbrush@codebrainz.ca>'], 1)
+]
View
55 geanypy/doc/source/dialogs.rst
@@ -0,0 +1,55 @@
+The :mod:`dialogs` module
+*************************
+
+.. module:: dialogs
+ :synopsis: Showing dialogs to the user
+
+This module contains some help functions to show file-related dialogs,
+miscellaneous dialogs, etc. You can of course just use the :mod:`gtk` module
+to create your own dialogs as well.
+
+.. function:: show_input([title=None[, parent=None[, label_text=None[, default_text=None]]]])
+
+ Shows a :class:`gtk.Dialog` to ask the user for text input.
+
+ :param title: The window title for the dialog.
+ :param parent: The parent :class:`gtk.Window` for the dialog, for example :data:`geany.main_widgets.window`.
+ :param label_text: Text to put in the label just about the input entry box.
+ :param default_text: Default text to put in the input entry box.
+
+ :return: A string containing the text the user entered.
+
+.. function:: show_input_numeric([title=None[, label_text=None[, value=0.0[, minimum=0.0[, maximum=100.0[, step=1.0]]]]]])
+
+ Shows a :class:`gtk.Dialog` to ask the user to enter a numeric value
+ using a :class:`gtk.SpinButton`.
+
+ :param title: The window title for the dialog.
+ :param label_text: Text to put in the label just about the numeric entry box.
+ :param value: The initial value in the numeric entry box.
+ :param minimum: The minimum allowed value that can be entered.
+ :param maximum: The maximum allowed value that can be entered.
+ :param step: Amount to increment the numeric entry when it's value is moved up or down (ex, using arrows).
+
+ :return: The value entered if the dialog was closed with ok, or :data:`None` if it was cancelled.
+
+.. function:: show_msgbox(text[, msgtype=gtk.MESSAGE_INFO])
+
+ Shows a :class:`gtk.Dialog` to show the user a message.
+
+ :param text: The text to show in the message box.
+ :param msgtype: The message type which is one of the `Gtk Message Type Constants <http://www.pygtk.org/docs/pygtk/gtk-constants.html#gtk-message-type-constants>`_.
+
+.. function:: show_question(text)
+
+ Shows a :class:`gtk.Dialog` to ask the user a Yes/No question.
+
+ :param text: The text to show in the question dialog.
+
+ :return: :data:`True` if the Yes button was pressed, :data:`False` if the No button was pressed.
+
+.. function:: show_save_as()
+
+ Shows Geany's `Save As` dialog.
+
+ :return: :data:`True` if the file was saved, :data:`False` otherwise.
View
199 geanypy/doc/source/document.rst
@@ -0,0 +1,199 @@
+The :mod:`document` module
+**************************
+
+.. module:: document
+ :synopsis: Document-related functions and classes
+
+This module provides functions for working with documents. Most of the module-level
+functions are used for creating instances of the :class:`Document` object.
+
+
+.. function:: find_by_filename(filename)
+
+ Finds a document with the given filename from the open documents.
+
+ :param filename: Filename of :class:`Document` to find.
+
+ :return: A :class:`Document` instance for the found document or :data:`None`.
+
+.. function:: get_current()
+
+ Gets the currently active document.
+
+ :return: A :class:`Document` instance for the currently active document or :data:`None` if no documents are open.
+
+.. function:: get_from_page(page_num)
+
+ Gets a document based on it's :class:`gtk.Notebook` page number.
+
+ :param page_num: The tab number of the document in the documents notebook.
+
+ :return: A :class:`Document` instance for the corresponding document or :data:`None` if no document matched.
+
+.. function:: get_from_index(index)
+
+ Gets a document based on its index in Geany's documents array.
+
+ :param index: The index of the document in Geany's documents array.
+
+ :return: A :class:`Document` instance for the corresponding document or :data:`None` if not document matched, or the document that matched isn't valid.
+
+.. function:: new([filename=None[, filetype=None[, text=None]]])
+
+ Creates a document file.
+
+ :param filename: The documents filename, or :data:`None` for `untitled`.
+ :param filetype: The documents filetype or :data:`None` to auto-detect it from `filename` (if it's not :data:`None`)
+ :param text: Initial text to put in the new document or :data:`None` to leave it blank
+
+ :return: A :class:`Document` instance for the new document.
+
+.. function:: open(filename[, read_only=False[, filetype=None[, forced_enc=None]]])
+
+ Open an existing document file.
+
+ :param filename: Filename of the document to open.
+ :param read_only: Whether to open the document in read-only mode.
+ :param filetype: Filetype to open the document as or :data:`None` to detect it automatically.
+ :param forced_enc: The file encoding to use or :data:`None` to auto-detect it.
+
+ :return: A :class:`Document` instance for the opened document or :data:`None` if it couldn't be opened.
+
+.. function:: open_files(filenames, read_only=False, filetype="", forced_enc="")
+
+ Open multiple files. This actually calls :func:`open` once for each filename in `filenames`.
+
+ :param filenames: List of filenames to open.
+ :param read_only: Whether to open the document in read-only mode.
+ :param filetype: Filetype to open the document as or :data:`None` to detect it automatically.
+ :param forced_enc: The file encoding to use or :data:`None` to auto-detect it.
+
+.. function:: remove_page(page_num)
+
+ Remove a document from the documents array based on it's page number in the documents notebook.
+
+ :param page_num: The tab number of the document in the documents notebook.
+
+ :return: :data:`True` if the document was actually removed or :data:`False` otherwise.
+
+.. function:: get_documents_list()
+
+ Get a list of open documents.
+
+ :return: A list of :class:`Document` instances, one for each open document.
+
+
+:class:`Document` Objects
+=========================
+
+.. class:: Document
+
+ The main class holding information about a specific document. Unless
+ otherwise noted, the attributes are read-only properties.
+
+ .. attribute:: Document.basename_for_display
+
+ The last part of the filename for this document, possibly truncated to a maximum length in case the filename is very long.
+
+ .. attribute:: Document.notebook_page
+
+ The page number in the :class:`gtk.Notebook` containing documents.
+
+ .. attribute:: Document.status_color
+
+ Gets the status color of the document, or :data:`None` if the default widget coloring should be used. The color is red if the document has changes, green if it's read-only or :data:`None` if the document is unmodified but writable. The value is a tuple of the RGB values for red, green, and blue respectively.
+
+ .. attribute:: Document.encoding
+
+ The encoding of this document. Must be a valid string representation of an encoding. This property is read-write.
+
+ .. attribute:: Document.file_type
+
+ The file type of this document as a :class:`Filetype` instance. This property is read-write.
+
+ .. attribute:: Document.text_changed
+
+ Whether this document's text has been changed since it was last saved.
+
+ .. attribute:: Document.file_name
+
+ The file name of this document.
+
+ .. attribute:: Document.has_bom
+
+ Indicates whether the document's file has a byte-order-mark.
+
+ .. attribute:: Document.has_tags
+
+ Indicates whether this document supports source code symbols (tags) to show in the sidebar.
+
+ .. attribute:: Document.index
+
+ Index of the document in Geany's documents array.
+
+ .. attribute:: Document.is_valid
+
+ Indicates whether this document is active and all properties are set correctly.
+
+ .. attribute:: Document.read_only
+
+ Whether the document is in read-only mode.
+
+ .. attribute:: Document.real_path
+
+ The link-dereferenced, locale-encoded file name for this document.
+
+ .. attribute:: Document.editor
+
+ The :class:`Editor` instance associated with this document.
+
+ .. method:: Document.close()
+
+ Close this document.
+
+ :return: :data:`True` if the document was closed, :data:`False` otherwise.
+
+ .. method:: Document.reload([forced_enc=None])
+
+ Reloads this document.
+
+ :param forced_enc: The encoding to use when reloading this document or :data:`None` to auto-detect it.
+
+ :return: :data:`True` if the document was actually reloaded or :data:`False` otherwise.
+
+ .. method:: Document.rename(new_filename)
+
+ Rename this document to a new file name. Only the file on disk is actually
+ renamed, you still have to call :meth:`save_as` to change the document object.
+ It also stops monitoring for file changes to prevent receiving too many file
+ change events while renaming. File monitoring is setup again in :meth:`save_as`.
+
+ :param new_filename: The new filename to rename to.
+
+ .. method:: Document.save([force=False])
+
+ Saves this documents file on disk.
+
+ Saving may include replacing tabs by spaces, stripping trailing spaces and adding
+ a final new line at the end of the file, depending on user preferences. Then,
+ the `document-before-save` signal is emitted, allowing plugins to modify the
+ document before it's saved, and the data is actually written to disk. The
+ file type is set again or auto-detected if it wasn't set yet. Afterwards,
+ the `document-save` signal is emitted for plugins. If the file is not modified,
+ this method does nothing unless `force` is set to :data:`True`.
+
+ **Note:** You should ensure that :attr:`file_name` is not :data:`None` before
+ calling this; otherwise call :func:`dialogs.show_save_as`.
+
+ :param force: Whether to save the document even if it's not modified.
+
+ :return: :data:`True` if the file was saved or :data:`False` if the file could not or should not be saved.
+
+ .. method:: Document.save_as(new_filename)
+
+ Saves the document with a new filename, detecting the filetype.
+
+ :param new_filename: The new filename.
+
+ :return: :data:`True` if the file was saved or :data:`False` if it could not be saved.
+
View
0  geanypy/doc/source/geany.rst
No changes.
View
26 geanypy/doc/source/index.rst
@@ -0,0 +1,26 @@
+.. GeanyPy documentation master file, created by
+ sphinx-quickstart on Sun Aug 7 12:42:52 2011.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to GeanyPy's documentation!
+===================================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ intro.rst
+ install.rst
+ starting.rst
+ quickstart.rst
+ api.rst
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
View
146 geanypy/doc/source/install.rst
@@ -0,0 +1,146 @@
+Installation
+************
+
+Currently there are no binary packages available for installing GeanyPy so it
+must be installed from source. The following instructions will describe how
+to do this.
+
+Getting the Source
+==================
+
+The best way currently to get GeanyPy is to check it out from `it's repository
+on GitHub.com <https://github.com/codebrainz/geanypy>`_. You can clone
+GeanyPy's `master` branch by issuing the following command in a directory
+where you want to store its source code::
+
+ $ git clone git://github.com/codebrainz/geanypy.git
+ $ cd geanypy
+
+Alternatively, you can download the `master` branch
+`compressed into a tarball
+<https://github.com/codebrainz/geanypy/tarball/master>`_
+or `zip file <https://github.com/codebrainz/geanypy/zipball/master>`_. Then
+extract it where you want to store GeanyPy's source, for example::
+
+ $ cd ~/src
+ $ wget -O geanypy.tar.gz https://github.com/codebrainz/geanypy/tarball/master
+ $ tar xf geanypy.tar.gz
+ $ cd codebrainz-geanypy-*
+
+The first method using `Git <http://git-scm.com/>`_ is the best, since it
+allows you to update your copy of GeanyPy quite easily and also makes it
+easier to contribute back to the GeanyPy project if you want to.
+
+Dependencies and where to get them
+==================================
+
+Of course depending on what operating system and distribution you're using,
+getting setup for this process may vary wildly. At present, the following
+dependencies are required to compile GeanyPy:
+
+GCC, Autotools, and all the usual build tools
+---------------------------------------------
+
+For example on Debian (Ubuntu, Mint, etc.) do::
+
+ $ apt-get install build-essential
+
+Or on Fedora, something like this should do::
+
+ $ yum groupinstall "Development Tools" "Legacy Software Development"
+
+The latest development version of Geany (0.21+)
+-----------------------------------------------
+
+Since GeanyPy is wrapping the current development version of Geany, to use it
+you are required to use that version of Geany. Until the next Geany release,
+you must either checkout the source code from Geany's
+`Subversion repository <http://www.geany.org/Download/SVN>`_ or
+`Git mirror <http://git.geany.org>`_ or you can get one of the
+`Nightly builds <http://nightly.geany.org>`_ if you'd rather not compile
+it yourself.
+
+For more information on installing Geany, please refer to
+`Geany's excellent manual
+<http://www.geany.org/manual/current/index.html#installation>`_
+
+Grabbing the dependencies for Geany on a Debian-based disto could be similar to
+this::
+
+ $ apt-get install libgtk2.0-0 libgtk2.0-0-dev
+
+Or you might even have better luck with::
+
+ $ apt-get build-dep geany
+
+A quick session for installing Geany on a Debian-based distro might look
+something like this::
+
+ $ cd ~/src
+ $ git clone http://git.geany.org/git/geany
+ $ cd geany
+ $ ./autogen.sh
+ $ ./configure
+ $ make
+ $ make install # possibly as root
+
+By default, Geany will install into `/usr/local` so if you want to install it
+somewhere else, for example `/opt/geany`, then you would run the `configure`
+command above with the `prefix` argument, like::
+
+ $ ./configure --prefix=/opt/geany
+
+It's important when installing GeanyPy later that you configure it with the
+same prefix where Geany is installed, otherwise Geany won't find the GeanyPy
+plugin.
+
+Python 2.X and development files
+--------------------------------
+
+As GeanPy makes use of Python's C API to gain access to Geany's C plugin API,
+both Python and the development files are required to compile GeanyPy. In
+theory, any Python version >= 2.6 and < 3.0 should be compatible with GeanyPy.
+You can download Python `from its website <http://www.python.org/download>`_ or
+you can install the required packages using your distro's package manager, for
+example with Debian-based distros, run this::
+
+ $ apt-get install python python-dev
+
+**Note:** Python 3.0+ is not supported yet, although at some point in the
+future, there are plans support it.
+
+PyGTK and development files
+---------------------------
+
+Since Geany uses GTK+ as it's UI toolkit, GeanyPy uses PyGTK to interact with
+Geany's UI. You can either `download PyGTK from it's website
+<http://www.pygtk.org/downloads.html>`_ or you can install it with your
+system's pacakge manager, for example in Debian distros::
+
+ $ apt-get install python-gtk2 python-gtk2-dev
+
+**Note:** Although PyGTK is all but deprecated (or is completely deprecated?)
+in favour of the newer and shinier PyGobject/GObject-introspection, it is
+still used in new code in GeanyPy due to lack of documentation and pacakge
+support for the newer stuff.
+
+One fell swoop
+--------------
+
+If you're running a Debian-based distro, you should be able to install all
+the required dependencies, not including Geany itself, with the following
+command (as root)::
+
+ $ apt-get install build-essential libgtk2.0-0 libgtk2.0-dev \
+ python python-dev python-gtk2 python-gtk2-dev
+
+And finally ... installing GeanyPy
+==================================
+
+Once all the dependencies are satisfied, installing GeanyPy should be pretty
+straight-forward, continuing on from `Getting the Source`_ above::
+
+ $ ./autogen.sh
+ $ ./configure --prefix=/the/same/prefix/used/for/geany
+ $ make
+ $ make install # possibly as root
View
8 geanypy/doc/source/intro.rst
@@ -0,0 +1,8 @@
+Introduction
+************
+
+GeanyPy allows people to write their Geany plugins in Python making
+authoring a plugin much more accessible to non C programmers. What follows
+is a description of installing and using the GeanyPy plugin, paving the way
+for the rest of the documentation to covert the details of programming with
+the GeanyPy bindings of the Geany API.
View
121 geanypy/doc/source/quickstart.rst
@@ -0,0 +1,121 @@
+Writing a Plugin - Quick Start Guide
+************************************
+
+This is just a quick tutorial to describe writing a GeanyPy compatible plugin
+in Python. Writing a plugin should be pretty straightforward for any Python
+programmers, especially those familiar with writing `regular C plugins for
+Geany <http://www.geany.org/manual/reference/howto.html>`_.
+
+To illustrate the similarities to the C API, the example at the end of this
+section will create the same plugin as in Geany's Plugin Howto, except
+obviously written in Python.
+
+The Plugin Interface
+====================
+
+The first thing any plugin will want to do is to import the `geany` module::
+
+ import geany
+
+**Note:** Due to the way the Geany plugin framework works, importing the
+`geany` module will certain fail if you just try running it standalone, outside
+of Geany/GeanyPy.
+
+After that, you create a regular Python class which inherits from the
+`geany.Plugin` class::
+
+ import geany
+
+ class HelloWorld(geany.Plugin):
+ pass
+
+This will allow GeanyPy's Python Plugin Manager to locate the plugin as a
+GeanyPy plugin. If it doesn't inherit from `geany.Plugin` it will not be
+detected.
+
+There are a few more parts of the interface that must be implemented in order
+for the plugin to be detected by GeanyPy::
+
+ import geany
+
+ class HelloWorld(geany.Plugin):
+
+ __plugin_name__ = "HelloWorld" # required
+ __plugin_version__ = "version of your plugin"
+ __plugin_description__ = "description of your plugin"
+ __plugin_author__ = "Your Name <your email address>"
+
+These allow the Python Plugin Manager to glean information about your plugin
+which will be shown in the managers plugin list. All but the `__plugin_name__`
+attributes are optional, though recommended.
+
+The next thing that's needed is an entry-point to the plugin. Since Python
+classes have a initialization method already, this seems like a logical
+entry-point::
+
+ import geany
+
+ class HelloWorld(geany.Plugin):
+
+ __plugin_name__ = "HelloWorld" # required
+ __plugin_version__ = "version of your plugin"
+ __plugin_description__ = "description of your plugin"
+ __plugin_author__ = "Your Name <your email address>"
+
+ def __init__(self):
+ do_stuff_when_loaded()
+
+If you have some de-initialization code that needs to be run, you can add
+a `cleanup` method to the class that is guaranteed to be called when your
+plugin is unloaded, however it's optional::
+
+ import geany
+
+ class HelloWorld(geany.Plugin):
+
+ __plugin_name__ = "HelloWorld" # required
+ __plugin_version__ = "version of your plugin"
+ __plugin_description__ = "description of your plugin"
+ __plugin_author__ = "Your Name <your email address>"
+
+ def __init__(self):
+ do_stuff_when_loaded()
+
+ def cleanup(self):
+ do_stuff_when_unloaded()
+
+And there you have it! That's the minimum requirements for writing a plugin
+that will be detected by GeanyPy. Ok, it doesn't do anything yet, but it
+will be shown in the Python Plugin Manager and can be loaded and unloaded.
+
+Real-world Example
+==================
+
+To put it into context, here's a plugin that mimics the plugin in
+`Geany's Plugin Howto <http://www.geany.org/manual/reference/howto.html>`_::
+
+ import gtk
+ import geany
+
+ class HelloWorld(geany.Plugin):
+
+ __plugin_name__ = "HelloWorld"
+ __plugin_version__ = "1.0"
+ __plugin_description__ = "Just another tool to say hello world"
+ __plugin_author__ = "John Doe <john.doe@example.org>"
+
+ def __init__(self):
+ self.menu_item = gtk.MenuItem("Hello World")
+ self.menu_item.show()
+ geany.main_widgets.tools_menu.append(self.menu_item)
+ self.menu_item.connect("activate", self.on_hello_item_clicked)
+
+ def cleanup(self):
+ self.menu_item.destroy()
+
+ def on_hello_item_clicked(widget, data):
+ geany.dialogs.show_msgbox("Hello World")
+
+Hopefully this makes it clear how to write a Python plugin using GeanyPy. This
+sample plugin is provided with GeanyPy if you want to try it out.
+
View
64 geanypy/doc/source/starting.rst
@@ -0,0 +1,64 @@
+Getting Started
+***************
+
+Before diving into the details and API docs for programming plugins with
+GeanyPy, it's important to note how it works and some features it provides.
+
+What the heck is GeanyPy, really?
+=================================
+
+GeanyPy is "just another Geany plugin", really. Geany sees GeanyPy as any
+other `plugin <http://www.geany.org/manual/current/index.html#plugins>`_, so
+to activate GeanyPy, use Geany's
+`Plugin Manager <http://www.geany.org/manual/current/index.html#plugin-manager>`_
+under the Tools menu as you would for any other plugin.
+
+Once the GeanyPy plugin has been activated, a few elements are added to Geany's
+user interface as described below.
+
+Python Plugin Manager
+=====================
+
+Under the Tools menu, you will find the Python Plugin Manager, which is meant
+to be similar to Geany's own Plugin Manager. This is where you will activate
+any plugins written in Python.
+
+The Python Plugin Manager looks in exactly two places for plugins:
+
+1. For system-wide plugins, it will search in PREFIX/share/geany/geanypy/plugins.
+2. In Geany's config directory under your home directory, typically ~/.config/geany/plugins/geanypy/plugins.
+
+Where `PREFIX` is the prefix used at configure time with Geany/GeanyPy (see
+the previous section, Installation). Both of these paths may vary depending on
+your platform, but for most \*nix systems, the above paths should hold true.
+
+Any plugins which follow the proper interface found in either of those two
+directories will be listed in the Python Plugin Manager and you will be able
+to activate and deactivate them there.
+
+Python Console
+==============
+
+Another pretty cool feature of GeanyPy is the Python Console, which similar
+to the regular Python interactive interpreter console, but it's found in the
+Message Window area (bottom) in Geany. The `geany` Python module used to
+interact with Geany will be pre-imported for you, so you can mess around with
+Geany using the console, without ever having to even write a plugin.
+
+**Credits:** The Python Console was taken, almost in its entirety, from the
+`medit text editor <http://mooedit.sourceforge.net>`_. Props to the
+author(s) for such a nice `piece of source code
+<https://bitbucket.org/medit/medit/src/83c24f751493/moo/moopython/plugins/lib/pyconsole.py>`_
+
+Future Plans
+============
+
+Some time in the near future, there should be support for sending text from
+the active document into the Python Console. It will also be possible to
+have the Python Console either in a separate window, in the sidebar notebook
+or in the message window notebook.
+
+Also, either integration with Geany's keybindings UI under the preferences
+dialog or a separate but similar UI just for Python plugins will be added.
+Currently using keybindings requires a certain amount of hackery, due to
+Geany expecting all plugins to be shared libraries written in C.
View
18 geanypy/geany/Makefile.am
@@ -0,0 +1,18 @@
+geanypy_sources = __init__.py \
+ console.py \
+ manager.py \
+ loader.py \
+ plugin.py \
+ signalmanager.py
+geanypy_objects = $(geanypy_sources:.py=.pyc)
+geanypydir = $(libdir)/geany/geanypy/geany
+geanypy_DATA = $(geanypy_sources) $(geanypy_objects)
+
+EXTRA_DIST = $(geanypy_sources)
+CLEANFILES = $(geanypy_objects)
+
+.SUFFIXES = .pyc .py
+
+.py.pyc:
+ $(AM_V_GEN) \
+ $(PYTHON) -c "from py_compile import compile; compile('$<', '$@')"
View
69 geanypy/geany/__init__.py
@@ -0,0 +1,69 @@
+"""
+Package file that exposes some of Geany's guts as its own attibutes. Any
+objects where it only makes sense to have one instance of are initialed here
+and set as attributes.
+
+You can sort of think of this file as the GeanyData struct from the C API,
+though no real attempt is made to mimic that structure here.
+"""
+
+import app
+import console
+import dialogs
+import document
+import editor
+import encoding
+import filetypes
+import highlighting
+import loader
+import main
+import manager
+import msgwindow
+import navqueue
+import prefs
+import project
+import scintilla
+import search
+import templates
+import ui_utils
+
+from app import App
+from prefs import Prefs, ToolPrefs
+from main import is_realized, locale_init, reload_configuration
+from signalmanager import SignalManager
+from ui_utils import MainWidgets, InterfacePrefs
+from search import SearchPrefs
+from templates import TemplatePrefs
+
+
+__all__ = [ "Plugin",
+ "is_realized",
+ "locale_init",
+ "reload_configuration",
+ "main_widgets",
+ "interface_prefs",
+ "app",
+ "general_prefs",
+ "search_prefs",
+ "template_prefs",
+ "tool_prefs",
+ "signals" ]
+
+# Geany's application data fields
+app = App()
+
+# Import GTK+ widgets that are part of Geany's UI
+main_widgets = MainWidgets()
+
+# Preferences
+general_prefs = Prefs() # GeanyData->prefs but name clashes with module
+interface_prefs = InterfacePrefs()
+search_prefs = SearchPrefs()
+template_prefs = TemplatePrefs()
+tool_prefs = ToolPrefs()
+
+# GObject to connect signal handlers on and which emits signals.
+signals = SignalManager()
+
+import plugin
+from plugin import Plugin
View
665 geanypy/geany/console.py
@@ -0,0 +1,665 @@
+#!/usr/bin/env python
+#
+# pyconsole.py
+#
+# Copyright (C) 2004-2010 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
+# Thanks to Geoffrey French for ideas.
+#
+# This file is part of medit. medit 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.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with medit. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# This module 'runs' python interpreter in a TextView widget.
+# The main class is Console, usage is:
+# Console(locals=None, banner=None, completer=None, use_rlcompleter=True, start_script='') -
+# it creates the widget and 'starts' interactive session; see the end of
+# this file. If start_script is not empty, it pastes it as it was entered from keyboard.
+#
+# Console has "command" signal which is emitted when code is about to
+# be executed. You may connect to it using console.connect or console.connect_after
+# to get your callback ran before or after the code is executed.
+#
+# To modify output appearance, set attributes of console.stdout_tag and
+# console.stderr_tag.
+#
+# Console may subclass a type other than gtk.TextView, to allow syntax highlighting and stuff,
+# e.g.:
+# console_type = pyconsole.ConsoleType(moo.TextView)
+# console = console_type(use_rlcompleter=False, start_script="import moo\nimport gtk\n")
+#
+# This widget is not a replacement for real terminal with python running
+# inside: GtkTextView is not a terminal.
+# The use case is: you have a python program, you create this widget,
+# and inspect your program interiors.
+
+import gtk
+import gtk.gdk as gdk
+import gobject
+import pango
+import gtk.keysyms as _keys
+import code
+import sys
+import keyword
+import re
+
+# commonprefix() from posixpath
+def _commonprefix(m):
+ "Given a list of pathnames, returns the longest common leading component"
+ if not m: return ''
+ prefix = m[0]
+ for item in m:
+ for i in range(len(prefix)):
+ if prefix[:i+1] != item[:i+1]:
+ prefix = prefix[:i]
+ if i == 0:
+ return ''
+ break
+ return prefix
+
+class _ReadLine(object):
+
+ class Output(object):
+ def __init__(self, console, tag_name):
+ object.__init__(self)
+ self.buffer = console.get_buffer()
+ self.tag_name = tag_name
+ def write(self, text):
+ pos = self.buffer.get_iter_at_mark(self.buffer.get_insert())
+ self.buffer.insert_with_tags_by_name(pos, text, self.tag_name)
+
+ class History(object):
+ def __init__(self):
+ object.__init__(self)
+ self.items = ['']
+ self.ptr = 0
+ self.edited = {}
+
+ def commit(self, text):
+ if text and self.items[-1] != text:
+ self.items.append(text)
+ self.ptr = 0
+ self.edited = {}
+
+ def get(self, dir, text):
+ if len(self.items) == 1:
+ return None
+
+ if text != self.items[self.ptr]:
+ self.edited[self.ptr] = text
+ elif self.edited.has_key(self.ptr):
+ del self.edited[self.ptr]
+
+ self.ptr = self.ptr + dir
+ if self.ptr >= len(self.items):
+ self.ptr = 0
+ elif self.ptr < 0:
+ self.ptr = len(self.items) - 1
+
+ try:
+ return self.edited[self.ptr]
+ except KeyError:
+ return self.items[self.ptr]
+
+ def __init__(self):
+ object.__init__(self)
+
+ self.set_wrap_mode(gtk.WRAP_CHAR)
+ self.modify_font(pango.FontDescription("Monospace"))
+
+ self.buffer = self.get_buffer()
+ self.buffer.connect("insert-text", self.on_buf_insert)
+ self.buffer.connect("delete-range", self.on_buf_delete)
+ self.buffer.connect("mark-set", self.on_buf_mark_set)
+ self.do_insert = False
+ self.do_delete = False
+
+ self.stdout_tag = self.buffer.create_tag("stdout", foreground="#006000")
+ self.stderr_tag = self.buffer.create_tag("stderr", foreground="#B00000")
+ self._stdout = _ReadLine.Output(self, "stdout")
+ self._stderr = _ReadLine.Output(self, "stderr")
+
+ self.cursor = self.buffer.create_mark("cursor",
+ self.buffer.get_start_iter(),
+ False)
+ insert = self.buffer.get_insert()
+ self.cursor.set_visible(True)
+ insert.set_visible(False)
+
+ self.ps = ''
+ self.in_raw_input = False
+ self.run_on_raw_input = None
+ self.tab_pressed = 0
+ self.history = _ReadLine.History()
+ self.nonword_re = re.compile("[^\w\._]")
+
+ def freeze_undo(self):
+ try: self.begin_not_undoable_action()
+ except: pass
+
+ def thaw_undo(self):
+ try: self.end_not_undoable_action()
+ except: pass
+
+ def raw_input(self, ps=None):
+ if ps:
+ self.ps = ps
+ else:
+ self.ps = ''
+
+ iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
+
+ if ps:
+ self.freeze_undo()
+ self.buffer.insert(iter, self.ps)
+ self.thaw_undo()
+
+ self.__move_cursor_to(iter)
+ self.scroll_to_mark(self.cursor, 0.2)
+
+ self.in_raw_input = True
+
+ if self.run_on_raw_input:
+ run_now = self.run_on_raw_input
+ self.run_on_raw_input = None
+ self.buffer.insert_at_cursor(run_now + '\n')
+
+ def on_buf_mark_set(self, buffer, iter, mark):
+ if mark is not buffer.get_insert():
+ return
+ start = self.__get_start()
+ end = self.__get_end()
+ if iter.compare(self.__get_start()) >= 0 and \
+ iter.compare(self.__get_end()) <= 0:
+ buffer.move_mark_by_name("cursor", iter)
+ self.scroll_to_mark(self.cursor, 0.2)
+
+ def __insert(self, iter, text):
+ self.do_insert = True
+ self.buffer.insert(iter, text)
+ self.do_insert = False
+
+ def on_buf_insert(self, buf, iter, text, len):
+ if not self.in_raw_input or self.do_insert or not len:
+ return
+ buf.stop_emission("insert-text")
+ lines = text.splitlines()
+ need_eol = False
+ for l in lines:
+ if need_eol:
+ self._commit()
+ iter = self.__get_cursor()
+ else:
+ cursor = self.__get_cursor()
+ if iter.compare(self.__get_start()) < 0:
+ iter = cursor
+ elif iter.compare(self.__get_end()) > 0:
+ iter = cursor
+ else:
+ self.__move_cursor_to(iter)
+ need_eol = True
+ self.__insert(iter, l)
+ self.__move_cursor(0)
+
+ def __delete(self, start, end):
+ self.do_delete = True
+ self.buffer.delete(start, end)
+ self.do_delete = False
+
+ def on_buf_delete(self, buf, start, end):
+ if not self.in_raw_input or self.do_delete:
+ return
+
+ buf.stop_emission("delete-range")
+
+ start.order(end)
+ line_start = self.__get_start()
+ line_end = self.__get_end()
+
+ if start.compare(line_end) > 0:
+ return
+ if end.compare(line_start) < 0:
+ return
+
+ self.__move_cursor(0)
+
+ if start.compare(line_start) < 0:
+ start = line_start
+ if end.compare(line_end) > 0:
+ end = line_end
+ self.__delete(start, end)
+
+ def do_key_press_event(self, event, parent_type):
+ if not self.in_raw_input:
+ return parent_type.do_key_press_event(self, event)
+
+ tab_pressed = self.tab_pressed
+ self.tab_pressed = 0
+ handled = True
+
+ state = event.state & (gdk.SHIFT_MASK |
+ gdk.CONTROL_MASK |
+ gdk.MOD1_MASK)
+ keyval = event.keyval
+
+ if not state:
+ if keyval == _keys.Return:
+ self._commit()
+ elif keyval == _keys.Up:
+ self.__history(-1)
+ elif keyval == _keys.Down:
+ self.__history(1)
+ elif keyval == _keys.Left:
+ self.__move_cursor(-1)
+ elif keyval == _keys.Right:
+ self.__move_cursor(1)
+ elif keyval == _keys.Home:
+ self.__move_cursor(-10000)
+ elif keyval == _keys.End:
+ self.__move_cursor(10000)
+ elif keyval == _keys.Tab:
+ cursor = self.__get_cursor()
+ if cursor.starts_line():
+ handled = False
+ else:
+ cursor.backward_char()
+ if cursor.get_char().isspace():
+ handled = False
+ else:
+ self.tab_pressed = tab_pressed + 1
+ self.__complete()
+ else:
+ handled = False
+ elif state == gdk.CONTROL_MASK:
+ if keyval == _keys.u:
+ start = self.__get_start()
+ end = self.__get_cursor()
+ self.__delete(start, end)
+ else:
+ handled = False
+ else:
+ handled = False
+
+ if not handled:
+ return parent_type.do_key_press_event(self, event)
+ else:
+ return True
+
+ def __history(self, dir):
+ text = self._get_line()
+ new_text = self.history.get(dir, text)
+ if not new_text is None:
+ self.__replace_line(new_text)
+ self.__move_cursor(0)
+ self.scroll_to_mark(self.cursor, 0.2)
+
+ def __get_cursor(self):
+ return self.buffer.get_iter_at_mark(self.cursor)
+ def __get_start(self):
+ iter = self.__get_cursor()
+ iter.set_line_offset(len(self.ps))
+ return iter
+ def __get_end(self):
+ iter = self.__get_cursor()
+ if not iter.ends_line():
+ iter.forward_to_line_end()
+ return iter
+
+ def __get_text(self, start, end):
+ return self.buffer.get_text(start, end, False)
+
+ def __move_cursor_to(self, iter):
+ self.buffer.place_cursor(iter)
+ self.buffer.move_mark_by_name("cursor", iter)
+
+ def __move_cursor(self, howmany):
+ iter = self.__get_cursor()
+ end = self.__get_cursor()
+ if not end.ends_line():
+ end.forward_to_line_end()
+ line_len = end.get_line_offset()
+ move_to = iter.get_line_offset() + howmany
+ move_to = min(max(move_to, len(self.ps)), line_len)
+ iter.set_line_offset(move_to)
+ self.__move_cursor_to(iter)
+
+ def __delete_at_cursor(self, howmany):
+ iter = self.__get_cursor()
+ end = self.__get_cursor()
+ if not end.ends_line():
+ end.forward_to_line_end()
+ line_len = end.get_line_offset()
+ erase_to = iter.get_line_offset() + howmany
+ if erase_to > line_len:
+ erase_to = line_len
+ elif erase_to < len(self.ps):
+ erase_to = len(self.ps)
+ end.set_line_offset(erase_to)
+ self.__delete(iter, end)
+
+ def __get_width(self):
+ if not (self.flags() & gtk.REALIZED):
+ return 80
+ layout = pango.Layout(self.get_pango_context())
+ letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ layout.set_text(letters)
+ pix_width = layout.get_pixel_size()[0]
+ return self.allocation.width * len(letters) / pix_width
+
+ def __print_completions(self, completions):
+ line_start = self.__get_text(self.__get_start(), self.__get_cursor())
+ line_end = self.__get_text(self.__get_cursor(), self.__get_end())
+ iter = self.buffer.get_end_iter()
+ self.__move_cursor_to(iter)
+ self.__insert(iter, "\n")
+
+ width = max(self.__get_width(), 4)
+ max_width = max([len(s) for s in completions])
+ n_columns = max(int(width / (max_width + 1)), 1)
+ col_width = int(width / n_columns)
+ total = len(completions)
+ col_length = total / n_columns
+ if total % n_columns:
+ col_length = col_length + 1
+ col_length = max(col_length, 1)
+
+ if col_length == 1:
+ n_columns = total
+ col_width = width / total
+
+ for i in range(col_length):
+ for j in range(n_columns):
+ ind = i + j*col_length
+ if ind < total:
+ if j == n_columns - 1:
+ n_spaces = 0
+ else:
+ n_spaces = col_width - len(completions[ind])
+ self.__insert(iter, completions[ind] + " " * n_spaces)
+ self.__insert(iter, "\n")
+
+ self.__insert(iter, "%s%s%s" % (self.ps, line_start, line_end))
+ iter.set_line_offset(len(self.ps) + len(line_start))
+ self.__move_cursor_to(iter)
+ self.scroll_to_mark(self.cursor, 0.2)
+
+ def __complete(self):
+ text = self.__get_text(self.__get_start(), self.__get_cursor())
+ start = ''
+ word = text
+ nonwords = self.nonword_re.findall(text)
+ if nonwords:
+ last = text.rfind(nonwords[-1]) + len(nonwords[-1])
+ start = text[:last]
+ word = text[last:]
+
+ completions = self.complete(word)
+
+ if completions:
+ prefix = _commonprefix(completions)
+ if prefix != word:
+ start_iter = self.__get_start()
+ start_iter.forward_chars(len(start))
+ end_iter = start_iter.copy()
+ end_iter.forward_chars(len(word))
+ self.__delete(start_iter, end_iter)
+ self.__insert(end_iter, prefix)
+ elif self.tab_pressed > 1:
+ self.freeze_undo()
+ self.__print_completions(completions)
+ self.thaw_undo()
+ self.tab_pressed = 0
+
+ def complete(self, text):
+ return None
+
+ def _get_line(self):
+ start = self.__get_start()
+ end = self.__get_end()
+ return self.buffer.get_text(start, end, False)
+
+ def __replace_line(self, new_text):
+ start = self.__get_start()
+ end = self.__get_end()
+ self.__delete(start, end)
+ self.__insert(end, new_text)
+
+ def _commit(self):
+ end = self.__get_cursor()
+ if not end.ends_line():
+ end.forward_to_line_end()
+ text = self._get_line()
+ self.__move_cursor_to(end)
+ self.freeze_undo()
+ self.__insert(end, "\n")
+ self.in_raw_input = False
+ self.history.commit(text)
+ self.do_raw_input(text)
+ self.thaw_undo()
+
+ def do_raw_input(self, text):
+ pass
+
+
+class _Console(_ReadLine, code.InteractiveInterpreter):
+ def __init__(self, locals=None, banner=None,
+ completer=None, use_rlcompleter=True,
+ start_script=None):
+ _ReadLine.__init__(self)
+
+
+ code.InteractiveInterpreter.__init__(self, locals)
+ self.locals["__console__"] = self
+
+ self.start_script = start_script
+ self.completer = completer
+ self.banner = banner
+
+ if not self.completer and use_rlcompleter:
+ try:
+ import rlcompleter
+ self.completer = rlcompleter.Completer()
+ except ImportError:
+ pass
+
+ self.ps1 = ">>> "
+ self.ps2 = "... "
+ self.__start()
+ self.run_on_raw_input = start_script
+ self.raw_input(self.ps1)
+
+ def __start(self):
+ self.cmd_buffer = ""
+
+ self.freeze_undo()
+ self.thaw_undo()
+ self.buffer.set_text("")
+
+ if self.banner:
+ iter = self.buffer.get_start_iter()
+ self.buffer.insert_with_tags_by_name(iter, self.banner, "stdout")
+ if not iter.starts_line():
+ self.buffer.insert(iter, "\n")
+
+ def clear(self, start_script=None):
+ if start_script is None:
+ start_script = self.start_script
+ else:
+ self.start_script = start_script
+
+ self.__start()
+ self.run_on_raw_input = start_script
+
+ def do_raw_input(self, text):
+ if self.cmd_buffer:
+ cmd = self.cmd_buffer + "\n" + text
+ else:
+ cmd = text
+
+ saved_stdout, saved_stderr = sys.stdout, sys.stderr
+ sys.stdout, sys.stderr = self._stdout, self._stderr
+
+ if self.runsource(cmd):
+ self.cmd_buffer = cmd
+ ps = self.ps2
+ else:
+ self.cmd_buffer = ''
+ ps = self.ps1
+
+ sys.stdout, sys.stderr = saved_stdout, saved_stderr
+ self.raw_input(ps)
+
+ def do_command(self, code):
+ try:
+ eval(code, self.locals)
+ except SystemExit:
+ raise
+ except:
+ self.showtraceback()
+
+ def runcode(self, code):
+ if gtk.pygtk_version[1] < 8:
+ self.do_command(code)
+ else:
+ self.emit("command", code)
+
+ def exec_command(self, command):
+ if self._get_line():
+ self._commit()
+ self.buffer.insert_at_cursor(command)
+ self._commit()
+
+ def complete_attr(self, start, end):
+ try:
+ obj = eval(start, self.locals)
+ strings = dir(obj)
+
+ if end:
+ completions = {}
+ for s in strings:
+ if s.startswith(end):
+ completions[s] = None
+ completions = completions.keys()
+ else:
+ completions = strings
+
+ completions.sort()
+ return [start + "." + s for s in completions]
+ except:
+ return None
+
+ def complete(self, text):
+ if self.completer:
+ completions = []
+ i = 0
+ try:
+ while 1:
+ s = self.completer.complete(text, i)
+ if s:
+ completions.append(s)
+ i = i + 1
+ else:
+ completions.sort()
+ return completions
+ except NameError:
+ return None
+
+ dot = text.rfind(".")
+ if dot >= 0:
+ return self.complete_attr(text[:dot], text[dot+1:])
+
+ completions = {}
+ strings = keyword.kwlist
+
+ if self.locals:
+ strings.extend(self.locals.keys())
+
+ try: strings.extend(eval("globals()", self.locals).keys())
+ except: pass
+
+ try:
+ exec "import __builtin__" in self.locals
+ strings.extend(eval("dir(__builtin__)", self.locals))
+ except:
+ pass
+
+ for s in strings:
+ if s.startswith(text):
+ completions[s] = None
+ completions = completions.keys()
+ completions.sort()
+ return completions
+
+
+def ReadLineType(t=gtk.TextView):
+ class readline(t, _ReadLine):
+ def __init__(self, *args, **kwargs):
+ t.__init__(self)
+ _ReadLine.__init__(self, *args, **kwargs)
+ def do_key_press_event(self, event):
+ return _ReadLine.do_key_press_event(self, event, t)
+ gobject.type_register(readline)
+ return readline
+
+def ConsoleType(t=gtk.TextView):
+ class console_type(t, _Console):
+ __gsignals__ = {
+ 'command' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (object,)),
+ 'key-press-event' : 'override'
+ }
+
+ def __init__(self, *args, **kwargs):
+ if gtk.pygtk_version[1] < 8:
+ gobject.GObject.__init__(self)
+ else:
+ t.__init__(self)
+ _Console.__init__(self, *args, **kwargs)
+
+ def do_command(self, code):
+ return _Console.do_command(self, code)
+
+ def do_key_press_event(self, event):
+ return _Console.do_key_press_event(self, event, t)
+
+ if gtk.pygtk_version[1] < 8:
+ gobject.type_register(console_type)
+
+ return console_type
+