Branch: refs/heads/master Author: Frank Lanitz frank@frank.uvena.de Committer: Frank Lanitz frank@frank.uvena.de Date: Sat, 15 Jun 2013 11:59:32 UTC Commit: 6af2bf6d078a9d828e68dbc5c27eca9009b1e708 https://github.com/geany/geany-plugins/commit/6af2bf6d078a9d828e68dbc5c27eca...
Log Message: ----------- Merge pull request #107 from elextr/geanypy
Adding Geanypy to geany-plugins
Modified Paths: -------------- .gitignore MAINTAINERS Makefile.am build/geanypy.m4 configure.ac geanypy/AUTHORS geanypy/COPYING geanypy/ChangeLog geanypy/Makefile.am geanypy/NEWS geanypy/README geanypy/doc/make.bat geanypy/doc/source/api.rst geanypy/doc/source/app.rst geanypy/doc/source/conf.py geanypy/doc/source/dialogs.rst geanypy/doc/source/document.rst geanypy/doc/source/geany.rst geanypy/doc/source/index.rst geanypy/doc/source/install.rst geanypy/doc/source/intro.rst geanypy/doc/source/quickstart.rst geanypy/doc/source/starting.rst geanypy/geany/Makefile.am geanypy/geany/__init__.py geanypy/geany/console.py geanypy/geany/loader.py geanypy/geany/manager.py geanypy/geany/plugin.py geanypy/geany/signalmanager.py geanypy/m4/ax_python_devel.m4 geanypy/m4/ax_python_library.m4 geanypy/plugins/Makefile.am geanypy/plugins/console.py geanypy/plugins/demo.py geanypy/plugins/hello.py geanypy/src/Makefile.am geanypy/src/geanypy-app.c geanypy/src/geanypy-dialogs.c geanypy/src/geanypy-document.c geanypy/src/geanypy-document.h geanypy/src/geanypy-editor.c geanypy/src/geanypy-editor.h geanypy/src/geanypy-encoding.c geanypy/src/geanypy-encoding.h geanypy/src/geanypy-filetypes.c geanypy/src/geanypy-filetypes.h geanypy/src/geanypy-highlighting.c geanypy/src/geanypy-indentprefs.c geanypy/src/geanypy-interfaceprefs.c geanypy/src/geanypy-main.c geanypy/src/geanypy-mainwidgets.c geanypy/src/geanypy-msgwindow.c geanypy/src/geanypy-navqueue.c geanypy/src/geanypy-plugin.c geanypy/src/geanypy-plugin.h geanypy/src/geanypy-prefs.c geanypy/src/geanypy-project.c geanypy/src/geanypy-project.h geanypy/src/geanypy-scinotification.c geanypy/src/geanypy-scinotifyheader.c geanypy/src/geanypy-scintilla.c geanypy/src/geanypy-scintilla.h geanypy/src/geanypy-search.c geanypy/src/geanypy-signalmanager.c geanypy/src/geanypy-signalmanager.h geanypy/src/geanypy-templates.c geanypy/src/geanypy-uiutils.c geanypy/src/geanypy-uiutils.h geanypy/src/geanypy.h geanypy/src/makefile.win32 po/POTFILES.in
Modified: .gitignore 1 files changed, 1 insertions(+), 0 deletions(-) =================================================================== @@ -7,6 +7,7 @@ *.so *.dll *.exe +*.pyc deps.mak .deps/ .libs/
Modified: MAINTAINERS 6 files changed, 6 insertions(+), 0 deletions(-) =================================================================== @@ -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
Modified: Makefile.am 6 files changed, 5 insertions(+), 1 deletions(-) =================================================================== @@ -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
Modified: build/geanypy.m4 20 files changed, 20 insertions(+), 0 deletions(-) =================================================================== @@ -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 + ]) +])
Modified: configure.ac 1 files changed, 1 insertions(+), 0 deletions(-) =================================================================== @@ -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
Modified: geanypy/AUTHORS 2 files changed, 2 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,2 @@ +Matthew Brush mbrush@codebrainz.ca +Lex Trotman elextr@gmail.com (Geany-Plugins port)
Modified: geanypy/COPYING 339 files changed, 339 insertions(+), 0 deletions(-) =================================================================== @@ -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.
Modified: geanypy/ChangeLog 1 files changed, 1 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1 @@ +Unused
Modified: geanypy/Makefile.am 4 files changed, 4 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,4 @@ +include $(top_srcdir)/build/vars.auxfiles.mk +#ACLOCAL_AMFLAGS += -I geanypy/m4 +SUBDIRS = src geany plugins +plugin = geanypy
Modified: geanypy/NEWS 1 files changed, 1 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1 @@ +UnNEWSed
Modified: geanypy/README 73 files changed, 73 insertions(+), 0 deletions(-) =================================================================== @@ -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
Modified: geanypy/doc/make.bat 170 files changed, 170 insertions(+), 0 deletions(-) =================================================================== @@ -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
Modified: geanypy/doc/source/api.rst 67 files changed, 67 insertions(+), 0 deletions(-) =================================================================== @@ -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).
Modified: geanypy/doc/source/app.rst 40 files changed, 40 insertions(+), 0 deletions(-) =================================================================== @@ -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.
Modified: geanypy/doc/source/conf.py 216 files changed, 216 insertions(+), 0 deletions(-) =================================================================== @@ -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) +]
Modified: geanypy/doc/source/dialogs.rst 55 files changed, 55 insertions(+), 0 deletions(-) =================================================================== @@ -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.
Modified: geanypy/doc/source/document.rst 199 files changed, 199 insertions(+), 0 deletions(-) =================================================================== @@ -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. +
Modified: geanypy/doc/source/geany.rst 0 files changed, 0 insertions(+), 0 deletions(-) =================================================================== No diff available, check online
Modified: geanypy/doc/source/index.rst 26 files changed, 26 insertions(+), 0 deletions(-) =================================================================== @@ -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` +
Modified: geanypy/doc/source/install.rst 146 files changed, 146 insertions(+), 0 deletions(-) =================================================================== @@ -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
Modified: geanypy/doc/source/intro.rst 8 files changed, 8 insertions(+), 0 deletions(-) =================================================================== @@ -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.
Modified: geanypy/doc/source/quickstart.rst 121 files changed, 121 insertions(+), 0 deletions(-) =================================================================== @@ -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. +
Modified: geanypy/doc/source/starting.rst 64 files changed, 64 insertions(+), 0 deletions(-) =================================================================== @@ -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.
Modified: geanypy/geany/Makefile.am 18 files changed, 18 insertions(+), 0 deletions(-) =================================================================== @@ -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('$<', '$@')"
Modified: geanypy/geany/__init__.py 69 files changed, 69 insertions(+), 0 deletions(-) =================================================================== @@ -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
Modified: geanypy/geany/console.py 665 files changed, 665 insertions(+), 0 deletions(-) =================================================================== @@ -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 + +ReadLine = ReadLineType() +Console = ConsoleType() + +def _create_widget(start_script): + + console = Console(banner="Geany Python Console", + use_rlcompleter=False, + start_script=start_script) + return console + +def _make_window(start_script="import geany\n"): + window = gtk.Window() + window.set_title("Python Console") + swin = gtk.ScrolledWindow() + swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) + window.add(swin) + console = _create_widget(start_script) + swin.add(console) + window.set_default_size(500, 400) + window.show_all() + + if not gtk.main_level(): + window.connect("destroy", gtk.main_quit) + gtk.main() + + return console + +if __name__ == '__main__': + import sys + import os + sys.path.insert(0, os.getcwd()) + _make_window(sys.argv[1:] and '\n'.join(sys.argv[1:]) + '\n' or None)
Modified: geanypy/geany/loader.py 172 files changed, 172 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,172 @@ +import os +import imp +from collections import namedtuple +import geany + +PluginInfo = namedtuple('PluginInfo', 'filename, name, version, description, author, cls') + + +class PluginLoader(object): + + plugins = {} + + def __init__(self, plugin_dirs): + + self.plugin_dirs = plugin_dirs + + self.available_plugins = [] + for plugin in self.iter_plugin_info(): + self.available_plugins.append(plugin) + + self.restore_loaded_plugins() + + + def update_loaded_plugins_file(self): + for path in self.plugin_dirs: + if os.path.isdir(path): + try: + state_file = os.path.join(path, '.loaded_plugins') + with open(state_file, 'w') as f: + for plugfn in self.plugins: + f.write("%s\n" % plugfn) + except IOError as err: + if err.errno == 13: #perms + pass + else: + raise + + + def restore_loaded_plugins(self): + loaded_plugins = [] + for path in reversed(self.plugin_dirs): + state_file = os.path.join(path, ".loaded_plugins") + if os.path.exists(state_file): + for line in open(state_file): + line = line.strip() + if line not in loaded_plugins: + loaded_plugins.append(line) + for filename in loaded_plugins: + self.load_plugin(filename) + + + def load_all_plugins(self): + + for plugin_info in self.iter_plugin_info(): + if plugin_filename.endswith('test.py'): # hack for testing + continue + plug = self.load_plugin(plugin_info.filename) + if plug: + print("Loaded plugin: %s" % plugin_info.filename) + print(" Name: %s v%s" % (plug.name, plug.version)) + print(" Desc: %s" % plug.description) + print(" Author: %s" % plug.author) + + + def unload_all_plugins(self): + + for plugin in self.plugins: + self.unload_plugin(plugin) + + + def reload_all_plugins(self): + + self.unload_all_plugins() + self.load_all_plugins() + + + def iter_plugin_info(self): + + for d in self.plugin_dirs: + if os.path.isdir(d): + for current_file in os.listdir(d): + #check inside folders inside the plugins dir so we can load .py files here as plugins + current_path=os.path.abspath(os.path.join(d, current_file)) + if os.path.isdir(current_path): + for plugin_folder_file in os.listdir(current_path): + if plugin_folder_file.endswith('.py'): + #loop around results if its fails to load will never reach yield + for p in self.load_plugin_info(current_path,plugin_folder_file): + yield p + + #not a sub directory so if it ends with .py lets just attempt to load it as a plugin + if current_file.endswith('.py'): + #loop around results if its fails to load will never reach yield + for p in self.load_plugin_info(d,current_file): + yield p + + def load_plugin_info(self,d,f): + filename = os.path.abspath(os.path.join(d, f)) + if filename.endswith("test.py"): + pass + text = open(filename).read() + module_name = os.path.basename(filename)[:-3] + try: + module = imp.load_source(module_name, filename) + except ImportError as exc: + print "Error: failed to import settings module ({})".format(exc) + module=None + if module: + for k, v in module.__dict__.iteritems(): + if k == geany.Plugin.__name__: + continue + try: + if issubclass(v, geany.Plugin): + inf = PluginInfo( + filename, + getattr(v, '__plugin_name__'), + getattr(v, '__plugin_version__', ''), + getattr(v, '__plugin_description__', ''), + getattr(v, '__plugin_author__', ''), + v) + yield inf + + except TypeError: + continue + + + def load_plugin(self, filename): + + for avail in self.available_plugins: + if avail.filename == filename: + inst = avail.cls() + self.plugins[filename] = inst + self.update_loaded_plugins_file() + geany.ui_utils.set_statusbar('GeanyPy: plugin activated: %s' % + inst.name, True) + return inst + + + def unload_plugin(self, filename): + + try: + plugin = self.plugins[filename] + name = plugin.name + plugin.cleanup() + del self.plugins[filename] + self.update_loaded_plugins_file() + geany.ui_utils.set_statusbar('GeanyPy: plugin deactivated: %s' % + name, True) + except KeyError: + print("Unable to unload plugin '%s': it's not loaded" % filename) + + + def reload_plugin(self, filename): + + if filename in self.plugins: + self.unload_plugin(filename) + self.load_plugin(filename) + + + def plugin_has_help(self, filename): + + for plugin_info in self.iter_plugin_info(): + if plugin_info.filename == filename: + return hasattr(plugin_info.cls, 'show_help') + + + def plugin_has_configure(self, filename): + + try: + return hasattr(self.plugins[filename], 'show_configure') + except KeyError: + return None
Modified: geanypy/geany/manager.py 179 files changed, 179 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,179 @@ +import gtk +import gobject +import glib +from htmlentitydefs import name2codepoint +from loader import PluginLoader + + +class PluginManager(gtk.Dialog): + + def __init__(self, plugin_dirs=[]): + gtk.Dialog.__init__(self, title="Plugin Manager") + self.loader = PluginLoader(plugin_dirs) + + self.set_default_size(400, 450) + self.set_has_separator(True) + icon = self.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU) + self.set_icon(icon) + + self.connect("response", lambda w,d: self.hide()) + + vbox = gtk.VBox(False, 12) + vbox.set_border_width(12) + + lbl = gtk.Label("Choose plugins to load or unload:") + lbl.set_alignment(0.0, 0.5) + vbox.pack_start(lbl, False, False, 0) + + sw = gtk.ScrolledWindow() + sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) + vbox.pack_start(sw, True, True, 0) + + self.treeview = gtk.TreeView() + sw.add(self.treeview) + + vbox.show_all() + + self.get_content_area().add(vbox) + + action_area = self.get_action_area() + action_area.set_spacing(0) + action_area.set_homogeneous(False) + + btn = gtk.Button(stock=gtk.STOCK_CLOSE) + btn.set_border_width(6) + btn.connect("clicked", lambda x: self.response(gtk.RESPONSE_CLOSE)) + action_area.pack_start(btn, False, True, 0) + btn.show() + + self.btn_help = gtk.Button(stock=gtk.STOCK_HELP) + self.btn_help.set_border_width(6) + self.btn_help.set_no_show_all(True) + action_area.pack_start(self.btn_help, False, True, 0) + action_area.set_child_secondary(self.btn_help, True) + + self.btn_prefs = gtk.Button(stock=gtk.STOCK_PREFERENCES) + self.btn_prefs.set_border_width(6) + self.btn_prefs.set_no_show_all(True) + action_area.pack_start(self.btn_prefs, False, True, 0) + action_area.set_child_secondary(self.btn_prefs, True) + + action_area.show() + + self.load_plugins_list() + + + def on_help_button_clicked(self, button, treeview, model): + path = treeview.get_cursor()[0] + iter = model.get_iter(path) + filename = model.get_value(iter, 2) + for plugin in self.loader.available_plugins: + if plugin.filename == filename: + plugin.cls.show_help() + break + else: + print("Plugin does not support help function") + + + def on_preferences_button_clicked(self, button, treeview, model): + path = treeview.get_cursor()[0] + iter = model.get_iter(path) + filename = model.get_value(iter, 2) + try: + self.loader.plugins[filename].show_configure() + except KeyError: + print("Plugin is not loaded, can't run configure function") + + + def activate_plugin(self, filename): + self.loader.load_plugin(filename) + + + def deactivate_plugin(self, filename): + self.loader.unload_plugin(filename) + + + def load_plugins_list(self): + liststore = gtk.ListStore(gobject.TYPE_BOOLEAN, str, str) + + self.btn_help.connect("clicked", + self.on_help_button_clicked, self.treeview, liststore) + + self.btn_prefs.connect("clicked", + self.on_preferences_button_clicked, self.treeview, liststore) + + self.treeview.set_model(liststore) + self.treeview.set_headers_visible(False) + self.treeview.set_grid_lines(True) + + check_renderer = gtk.CellRendererToggle() + check_renderer.set_radio(False) + check_renderer.connect('toggled', self.on_plugin_load_toggled, liststore) + text_renderer = gtk.CellRendererText() + + check_column = gtk.TreeViewColumn(None, check_renderer, active=0) + text_column = gtk.TreeViewColumn(None, text_renderer, markup=1) + + self.treeview.append_column(check_column) + self.treeview.append_column(text_column) + + self.treeview.connect('row-activated', + self.on_row_activated, check_renderer, liststore) + self.treeview.connect('cursor-changed', + self.on_selected_plugin_changed, liststore) + + self.load_sorted_plugins_info(liststore) + + + def load_sorted_plugins_info(self, list_store): + + plugin_info_list = list(self.loader.iter_plugin_info()) + #plugin_info_list.sort(key=lambda pi: pi[1]) + + for plugin_info in plugin_info_list: + + lbl = str('<big><b>%s</b></big> <small>%s</small>\n%s\n' + + '<small><b>Author:</b> %s\n' + + '<b>Filename:</b> %s</small>') % ( + glib.markup_escape_text(plugin_info.name), + glib.markup_escape_text(plugin_info.version), + glib.markup_escape_text(plugin_info.description), + glib.markup_escape_text(plugin_info.author), + glib.markup_escape_@@ Diff output truncated at 100000 characters. @@
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
plugins-commits@lists.geany.org