From 4002bbedb2448f3dee3e045b8ec68d0ee9138081 Mon Sep 17 00:00:00 2001 From: elextr Date: Wed, 12 Jun 2013 21:38:23 +1000 Subject: [PATCH 1/3] Added Geanypy Python Plugin Plugin --- MAINTAINERS | 6 + Makefile.am | 6 +- build/geanypy.m4 | 20 + configure.ac | 1 + geanypy/AUTHORS | 2 + geanypy/COPYING | 339 +++++++++ geanypy/ChangeLog | 1 + geanypy/Makefile.am | 4 + geanypy/NEWS | 1 + geanypy/README | 73 ++ geanypy/doc/make.bat | 170 +++++ geanypy/doc/source/api.rst | 67 ++ geanypy/doc/source/app.rst | 40 + geanypy/doc/source/conf.py | 216 ++++++ geanypy/doc/source/dialogs.rst | 55 ++ geanypy/doc/source/document.rst | 199 +++++ geanypy/doc/source/geany.rst | 0 geanypy/doc/source/index.rst | 26 + geanypy/doc/source/install.rst | 146 ++++ geanypy/doc/source/intro.rst | 8 + geanypy/doc/source/quickstart.rst | 121 +++ geanypy/doc/source/starting.rst | 64 ++ geanypy/geany/Makefile.am | 18 + geanypy/geany/__init__.py | 69 ++ geanypy/geany/__init__.pyc | Bin 0 -> 1779 bytes geanypy/geany/console.py | 665 ++++++++++++++++ geanypy/geany/console.pyc | Bin 0 -> 19185 bytes geanypy/geany/loader.py | 172 +++++ geanypy/geany/loader.pyc | Bin 0 -> 5382 bytes geanypy/geany/manager.py | 179 +++++ geanypy/geany/manager.pyc | Bin 0 -> 6174 bytes geanypy/geany/plugin.py | 123 +++ geanypy/geany/plugin.pyc | Bin 0 -> 3083 bytes geanypy/geany/signalmanager.py | 58 ++ geanypy/geany/signalmanager.pyc | Bin 0 -> 1930 bytes geanypy/m4/ax_python_devel.m4 | 325 ++++++++ geanypy/m4/ax_python_library.m4 | 26 + geanypy/plugins/Makefile.am | 4 + geanypy/plugins/console.py | 313 ++++++++ geanypy/plugins/demo.py | 18 + geanypy/plugins/hello.py | 21 + geanypy/src/Makefile.am | 38 + geanypy/src/geanypy-app.c | 113 +++ geanypy/src/geanypy-dialogs.c | 131 ++++ geanypy/src/geanypy-document.c | 562 ++++++++++++++ geanypy/src/geanypy-document.h | 12 + geanypy/src/geanypy-editor.c | 466 ++++++++++++ geanypy/src/geanypy-editor.h | 21 + geanypy/src/geanypy-encoding.c | 310 ++++++++ geanypy/src/geanypy-encoding.h | 14 + geanypy/src/geanypy-filetypes.c | 278 +++++++ geanypy/src/geanypy-filetypes.h | 12 + geanypy/src/geanypy-highlighting.c | 230 ++++++ geanypy/src/geanypy-indentprefs.c | 78 ++ geanypy/src/geanypy-interfaceprefs.c | 150 ++++ geanypy/src/geanypy-main.c | 53 ++ geanypy/src/geanypy-mainwidgets.c | 95 +++ geanypy/src/geanypy-msgwindow.c | 133 ++++ geanypy/src/geanypy-navqueue.c | 80 ++ geanypy/src/geanypy-plugin.c | 280 +++++++ geanypy/src/geanypy-plugin.h | 43 ++ geanypy/src/geanypy-prefs.c | 191 +++++ geanypy/src/geanypy-project.c | 107 +++ geanypy/src/geanypy-project.h | 14 + geanypy/src/geanypy-scinotification.c | 137 ++++ geanypy/src/geanypy-scinotifyheader.c | 77 ++ geanypy/src/geanypy-scintilla.c | 1006 +++++++++++++++++++++++++ geanypy/src/geanypy-scintilla.h | 32 + geanypy/src/geanypy-search.c | 95 +++ geanypy/src/geanypy-signalmanager.c | 241 ++++++ geanypy/src/geanypy-signalmanager.h | 10 + geanypy/src/geanypy-templates.c | 102 +++ geanypy/src/geanypy-uiutils.c | 449 +++++++++++ geanypy/src/geanypy-uiutils.h | 19 + geanypy/src/geanypy.h | 119 +++ geanypy/src/makefile.win32 | 38 + po/POTFILES.in | 3 + 77 files changed, 9294 insertions(+), 1 deletion(-) create mode 100644 build/geanypy.m4 create mode 100644 geanypy/AUTHORS create mode 100644 geanypy/COPYING create mode 100644 geanypy/ChangeLog create mode 100644 geanypy/Makefile.am create mode 100644 geanypy/NEWS create mode 100644 geanypy/README create mode 100644 geanypy/doc/make.bat create mode 100644 geanypy/doc/source/api.rst create mode 100644 geanypy/doc/source/app.rst create mode 100644 geanypy/doc/source/conf.py create mode 100644 geanypy/doc/source/dialogs.rst create mode 100644 geanypy/doc/source/document.rst create mode 100644 geanypy/doc/source/geany.rst create mode 100644 geanypy/doc/source/index.rst create mode 100644 geanypy/doc/source/install.rst create mode 100644 geanypy/doc/source/intro.rst create mode 100644 geanypy/doc/source/quickstart.rst create mode 100644 geanypy/doc/source/starting.rst create mode 100644 geanypy/geany/Makefile.am create mode 100644 geanypy/geany/__init__.py create mode 100644 geanypy/geany/__init__.pyc create mode 100644 geanypy/geany/console.py create mode 100644 geanypy/geany/console.pyc create mode 100644 geanypy/geany/loader.py create mode 100644 geanypy/geany/loader.pyc create mode 100644 geanypy/geany/manager.py create mode 100644 geanypy/geany/manager.pyc create mode 100644 geanypy/geany/plugin.py create mode 100644 geanypy/geany/plugin.pyc create mode 100644 geanypy/geany/signalmanager.py create mode 100644 geanypy/geany/signalmanager.pyc create mode 100644 geanypy/m4/ax_python_devel.m4 create mode 100644 geanypy/m4/ax_python_library.m4 create mode 100644 geanypy/plugins/Makefile.am create mode 100644 geanypy/plugins/console.py create mode 100644 geanypy/plugins/demo.py create mode 100644 geanypy/plugins/hello.py create mode 100644 geanypy/src/Makefile.am create mode 100644 geanypy/src/geanypy-app.c create mode 100644 geanypy/src/geanypy-dialogs.c create mode 100644 geanypy/src/geanypy-document.c create mode 100644 geanypy/src/geanypy-document.h create mode 100644 geanypy/src/geanypy-editor.c create mode 100644 geanypy/src/geanypy-editor.h create mode 100644 geanypy/src/geanypy-encoding.c create mode 100644 geanypy/src/geanypy-encoding.h create mode 100644 geanypy/src/geanypy-filetypes.c create mode 100644 geanypy/src/geanypy-filetypes.h create mode 100644 geanypy/src/geanypy-highlighting.c create mode 100644 geanypy/src/geanypy-indentprefs.c create mode 100644 geanypy/src/geanypy-interfaceprefs.c create mode 100644 geanypy/src/geanypy-main.c create mode 100644 geanypy/src/geanypy-mainwidgets.c create mode 100644 geanypy/src/geanypy-msgwindow.c create mode 100644 geanypy/src/geanypy-navqueue.c create mode 100644 geanypy/src/geanypy-plugin.c create mode 100644 geanypy/src/geanypy-plugin.h create mode 100644 geanypy/src/geanypy-prefs.c create mode 100644 geanypy/src/geanypy-project.c create mode 100644 geanypy/src/geanypy-project.h create mode 100644 geanypy/src/geanypy-scinotification.c create mode 100644 geanypy/src/geanypy-scinotifyheader.c create mode 100644 geanypy/src/geanypy-scintilla.c create mode 100644 geanypy/src/geanypy-scintilla.h create mode 100644 geanypy/src/geanypy-search.c create mode 100644 geanypy/src/geanypy-signalmanager.c create mode 100644 geanypy/src/geanypy-signalmanager.h create mode 100644 geanypy/src/geanypy-templates.c create mode 100644 geanypy/src/geanypy-uiutils.c create mode 100644 geanypy/src/geanypy-uiutils.h create mode 100644 geanypy/src/geanypy.h create mode 100644 geanypy/src/makefile.win32 diff --git a/MAINTAINERS b/MAINTAINERS index d71a854d3..0ddb2cef4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -114,6 +114,12 @@ M: Yura Siamashka W: http://plugins.geany.org/geanyprj.html S: Odd Fixes +geanypy +P: Lex Trotman +M: Lex Trotman +W: http://plugins.geany.org/geanypy.html +S: Maintained + geanysendmail P: Frank Lanitz M: Frank Lanitz diff --git a/Makefile.am b/Makefile.am index f81fe5d47..7e4e746ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -ACLOCAL_AMFLAGS = -I build/cache -I build -I build/bundled --install +ACLOCAL_AMFLAGS = -I build/cache -I build -I build/bundled -I geanypy/m4 --install SUBDIRS = po @@ -70,6 +70,10 @@ if ENABLE_GEANYPRJ SUBDIRS += geanyprj endif +if ENABLE_GEANYPY +SUBDIRS += geanypy +endif + if ENABLE_GEANYSENDMAIL SUBDIRS += geanysendmail endif diff --git a/build/geanypy.m4 b/build/geanypy.m4 new file mode 100644 index 000000000..6bf5f6f95 --- /dev/null +++ b/build/geanypy.m4 @@ -0,0 +1,20 @@ +AC_DEFUN([GP_CHECK_GEANYPY], +[ + GP_ARG_DISABLE([Geanypy], [auto]) + GP_CHECK_PLUGIN_GTK2_ONLY([Geanypy]) + GP_COMMIT_PLUGIN_STATUS([Geanypy]) + PKG_CHECK_MODULES([PYGTK], [pygtk-2.0]) + AX_PYTHON_DEVEL([>= '2.6']) + AX_PYTHON_LIBRARY(,[AC_MSG_ERROR([Cannot find Python library])]) + AC_SUBST([PYTHON]) + AC_DEFINE_UNQUOTED( + [GEANYPY_PYTHON_LIBRARY], + ["$PYTHON_LIBRARY"], + [Location of Python library to dlopen()]) + AC_CONFIG_FILES([ + geanypy/Makefile + geanypy/src/Makefile + geanypy/geany/Makefile + geanypy/plugins/Makefile + ]) +]) diff --git a/configure.ac b/configure.ac index 9585b1746..1ea80ba67 100644 --- a/configure.ac +++ b/configure.ac @@ -41,6 +41,7 @@ GP_CHECK_GEANYMACRO GP_CHECK_GEANYMINISCRIPT GP_CHECK_GEANYNUMBEREDBOOKMARKS GP_CHECK_GEANYPRJ +GP_CHECK_GEANYPY GP_CHECK_GEANYSENDMAIL GP_CHECK_GEANYVC GP_CHECK_GEANYPG diff --git a/geanypy/AUTHORS b/geanypy/AUTHORS new file mode 100644 index 000000000..c0aabb980 --- /dev/null +++ b/geanypy/AUTHORS @@ -0,0 +1,2 @@ +Matthew Brush +Lex Trotman (Geany-Plugins port) diff --git a/geanypy/COPYING b/geanypy/COPYING new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/geanypy/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + , 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. diff --git a/geanypy/ChangeLog b/geanypy/ChangeLog new file mode 100644 index 000000000..ba6c16dac --- /dev/null +++ b/geanypy/ChangeLog @@ -0,0 +1 @@ +Unused diff --git a/geanypy/Makefile.am b/geanypy/Makefile.am new file mode 100644 index 000000000..a59aa4acb --- /dev/null +++ b/geanypy/Makefile.am @@ -0,0 +1,4 @@ +include $(top_srcdir)/build/vars.auxfiles.mk +#ACLOCAL_AMFLAGS += -I geanypy/m4 +SUBDIRS = src geany plugins +plugin = geanypy diff --git a/geanypy/NEWS b/geanypy/NEWS new file mode 100644 index 000000000..16ed512fb --- /dev/null +++ b/geanypy/NEWS @@ -0,0 +1 @@ +UnNEWSed diff --git a/geanypy/README b/geanypy/README new file mode 100644 index 000000000..6ee961629 --- /dev/null +++ b/geanypy/README @@ -0,0 +1,73 @@ +GeanyPy +======= + +Write Geany plugins in Python! + +Provides most of the standard Geany C API for Python. + +**Please note:** GeanyPy here in geany-plugins is based on the upstream +at https://github.com/codebrainz/geanypy which is still under +development, however it is useful as is. Parts of the existing API +which mirror the Geany C API will probably not change unless the Geany API +changes, however new API may be added. Also documentation is needed, +contributions are welcome. + +How it works +------------ + +In the ``src/`` directory is a normal Geany plugin (``plugin.c``) which loads the +Python interpreter. It then loads some C Python modules (``dialogs.c``, +``documents.c``, etc.) that interface with Geany's C API. After that, it loads +the ``geany`` Python module which is just glue/sugar-coating to make the C +module more "Pythonic". + +To write a plugin, inherit from the ``geany.Plugin`` class and implmenent the +required members (see ``geany/plugin.py`` documentation comments). Then put the +plugin in a searched plugin path. Currently two locations are search for +plugins. The first is ``PREFIX/share/geany/geanypy/plugins`` and the recommended +location is under your personal Geany directory (usually +``~/.config/geany/plugins/geanypy/plugins``). To load or unload plugins, click +the Python Plugin Manager item under the Tools menu which will appear when you +activate GeanyPy through Geany's regular plugin manager. + +When the GeanyPy plugin is loaded, it has an option to add a new tab to +the notebook in the message window area that contains an interactive +Python shell with the ``geany`` module pre-imported. You can tinker +around with API with this console, for example:: + + + import geany + + doc = geany.document.open_file("/some/file/here") + + print(doc.file_name) + print(doc.file_type.display_name) + + geany.dialogs.show_msgbox("Hello World") + + geany.main_widgets.window.set_title("Hello Window") + + def on_document_open(doc): + print("Document '%s' was opened" % doc.file_name) + + geany.signals.connect('document-open', on_document_open) + + +Dependencies +------------ + +To build GeanyPy you need the following dependencies: + +* Python 2.6 or 2.7 and development files. +* Geany 1.24+ and development files +* PyGTK 2.0 and development files + +Running on Windows +------------------ + +Not currently supported for the geany-plugins version, see upstream +https://github.com/codebrainz/geanypy. + +Upstream developed by Matthew Brush. + +Geany-plugins version maintained by elextr@lists.geany.org diff --git a/geanypy/doc/make.bat b/geanypy/doc/make.bat new file mode 100644 index 000000000..d363ad0ed --- /dev/null +++ b/geanypy/doc/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ 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 diff --git a/geanypy/doc/source/api.rst b/geanypy/doc/source/api.rst new file mode 100644 index 000000000..91e112a44 --- /dev/null +++ b/geanypy/doc/source/api.rst @@ -0,0 +1,67 @@ +API Documentation +***************** + +GeanyPy's API mimics quite closely Geany's C plugin API. The following +documentation is broken down by file/module: + +The :mod:`geany` modules: + +.. toctree:: + :maxdepth: 2 + + app.rst + dialogs.rst + document.rst + +The :mod:`geany` package and module +=================================== + +.. module:: geany + +All of GeanyPy's bindings are inside the :mod:`geany` package which also +contains some stuff in it's :mod:`__init__` file, acting like a module itself. + + +.. data:: app + + An instance of :class:`app.App` to store application information. + +.. data:: main_widgets + + An instance of :class:`mainwidgets.MainWidgets` to provide access to + Geany's important GTK+ widgets. + +.. data:: signals + + An instance of :class:`signalmanager.SignalManager` which manages the + connection, disconnection, and emission of Geany's signals. You can + use this as follows:: + + geany.signals.connect('document-open', some_callback_function) + +.. function:: is_realized() + + This function, which is actually in the :mod:`geany.main` module will tell + you if Geany's main window is realized (shown). + +.. function:: locale_init() + + Again, from the :mod:`geany.main` module, this will initialize the `gettext` + translation system. + +.. function:: reload_configuration() + + Also from the :mod:`geany.main` module, this function will cause Geany to + reload most if it's configuration files without restarting. + + Currently the following files are reloaded: + + * all template files + * new file templates + * the New (with template) menus will be updated + * Snippets (snippets.conf) + * filetype extensions (filetype_extensions.conf) + * `settings` and `build_settings` sections of the filetype definition files. + + Plugins may call this function if they changed any of these files (e.g. a + configuration file editor plugin). diff --git a/geanypy/doc/source/app.rst b/geanypy/doc/source/app.rst new file mode 100644 index 000000000..94d85da39 --- /dev/null +++ b/geanypy/doc/source/app.rst @@ -0,0 +1,40 @@ +The :mod:`app` module +********************* + +.. module:: app + :synopsis: Application settings + +This modules contains a class to access application settings. + +:class:`App` Objects +==================== + +.. class:: App + +This class is initialized automatically and by the :mod:`geany` module and +shouldn't be initalized by users. An instance of it is available through +the :data:`geany.app` attribute of the :mod:`geany` module. + +All members of the :class:`App` are read-only properties. + + .. attribute:: App.configdir + + User configuration directory, usually ~/.config/geany. To store configuration + files for your plugin, it's a good idea to use something like this:: + + conf_path = os.path.join(geany.app.configdir, "plugins", "yourplugin", + "yourconfigfile.conf") + + .. attribute:: App.debug_mode + + If True, debug messages should be printed. For example, if you want to make + a :py:func:`print` function that only prints when :attr:`App.debug_mode` + is active, you could do something like this:: + + def debug_print(message): + if geany.app.debug_mode: + print(message) + + .. attribute:: App.project + + If not :py:obj:`None`, the a :class:`project.Project` for currently active project. diff --git a/geanypy/doc/source/conf.py b/geanypy/doc/source/conf.py new file mode 100644 index 000000000..f6b808ad0 --- /dev/null +++ b/geanypy/doc/source/conf.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +# +# GeanyPy documentation build configuration file, created by +# sphinx-quickstart on Sun Aug 7 12:42:52 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'GeanyPy' +copyright = u'2011, Matthew Brush (mbrush [at] codebrainz [dot] ca)' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v 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 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 '], 1) +] diff --git a/geanypy/doc/source/dialogs.rst b/geanypy/doc/source/dialogs.rst new file mode 100644 index 000000000..852614d59 --- /dev/null +++ b/geanypy/doc/source/dialogs.rst @@ -0,0 +1,55 @@ +The :mod:`dialogs` module +************************* + +.. module:: dialogs + :synopsis: Showing dialogs to the user + +This module contains some help functions to show file-related dialogs, +miscellaneous dialogs, etc. You can of course just use the :mod:`gtk` module +to create your own dialogs as well. + +.. function:: show_input([title=None[, parent=None[, label_text=None[, default_text=None]]]]) + + Shows a :class:`gtk.Dialog` to ask the user for text input. + + :param title: The window title for the dialog. + :param parent: The parent :class:`gtk.Window` for the dialog, for example :data:`geany.main_widgets.window`. + :param label_text: Text to put in the label just about the input entry box. + :param default_text: Default text to put in the input entry box. + + :return: A string containing the text the user entered. + +.. function:: show_input_numeric([title=None[, label_text=None[, value=0.0[, minimum=0.0[, maximum=100.0[, step=1.0]]]]]]) + + Shows a :class:`gtk.Dialog` to ask the user to enter a numeric value + using a :class:`gtk.SpinButton`. + + :param title: The window title for the dialog. + :param label_text: Text to put in the label just about the numeric entry box. + :param value: The initial value in the numeric entry box. + :param minimum: The minimum allowed value that can be entered. + :param maximum: The maximum allowed value that can be entered. + :param step: Amount to increment the numeric entry when it's value is moved up or down (ex, using arrows). + + :return: The value entered if the dialog was closed with ok, or :data:`None` if it was cancelled. + +.. function:: show_msgbox(text[, msgtype=gtk.MESSAGE_INFO]) + + Shows a :class:`gtk.Dialog` to show the user a message. + + :param text: The text to show in the message box. + :param msgtype: The message type which is one of the `Gtk Message Type Constants `_. + +.. 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. diff --git a/geanypy/doc/source/document.rst b/geanypy/doc/source/document.rst new file mode 100644 index 000000000..8de6324c8 --- /dev/null +++ b/geanypy/doc/source/document.rst @@ -0,0 +1,199 @@ +The :mod:`document` module +************************** + +.. module:: document + :synopsis: Document-related functions and classes + +This module provides functions for working with documents. Most of the module-level +functions are used for creating instances of the :class:`Document` object. + + +.. function:: find_by_filename(filename) + + Finds a document with the given filename from the open documents. + + :param filename: Filename of :class:`Document` to find. + + :return: A :class:`Document` instance for the found document or :data:`None`. + +.. function:: get_current() + + Gets the currently active document. + + :return: A :class:`Document` instance for the currently active document or :data:`None` if no documents are open. + +.. function:: get_from_page(page_num) + + Gets a document based on it's :class:`gtk.Notebook` page number. + + :param page_num: The tab number of the document in the documents notebook. + + :return: A :class:`Document` instance for the corresponding document or :data:`None` if no document matched. + +.. function:: get_from_index(index) + + Gets a document based on its index in Geany's documents array. + + :param index: The index of the document in Geany's documents array. + + :return: A :class:`Document` instance for the corresponding document or :data:`None` if not document matched, or the document that matched isn't valid. + +.. function:: new([filename=None[, filetype=None[, text=None]]]) + + Creates a document file. + + :param filename: The documents filename, or :data:`None` for `untitled`. + :param filetype: The documents filetype or :data:`None` to auto-detect it from `filename` (if it's not :data:`None`) + :param text: Initial text to put in the new document or :data:`None` to leave it blank + + :return: A :class:`Document` instance for the new document. + +.. function:: open(filename[, read_only=False[, filetype=None[, forced_enc=None]]]) + + Open an existing document file. + + :param filename: Filename of the document to open. + :param read_only: Whether to open the document in read-only mode. + :param filetype: Filetype to open the document as or :data:`None` to detect it automatically. + :param forced_enc: The file encoding to use or :data:`None` to auto-detect it. + + :return: A :class:`Document` instance for the opened document or :data:`None` if it couldn't be opened. + +.. function:: open_files(filenames, read_only=False, filetype="", forced_enc="") + + Open multiple files. This actually calls :func:`open` once for each filename in `filenames`. + + :param filenames: List of filenames to open. + :param read_only: Whether to open the document in read-only mode. + :param filetype: Filetype to open the document as or :data:`None` to detect it automatically. + :param forced_enc: The file encoding to use or :data:`None` to auto-detect it. + +.. function:: remove_page(page_num) + + Remove a document from the documents array based on it's page number in the documents notebook. + + :param page_num: The tab number of the document in the documents notebook. + + :return: :data:`True` if the document was actually removed or :data:`False` otherwise. + +.. function:: get_documents_list() + + Get a list of open documents. + + :return: A list of :class:`Document` instances, one for each open document. + + +:class:`Document` Objects +========================= + +.. class:: Document + + The main class holding information about a specific document. Unless + otherwise noted, the attributes are read-only properties. + + .. attribute:: Document.basename_for_display + + The last part of the filename for this document, possibly truncated to a maximum length in case the filename is very long. + + .. attribute:: Document.notebook_page + + The page number in the :class:`gtk.Notebook` containing documents. + + .. attribute:: Document.status_color + + Gets the status color of the document, or :data:`None` if the default widget coloring should be used. The color is red if the document has changes, green if it's read-only or :data:`None` if the document is unmodified but writable. The value is a tuple of the RGB values for red, green, and blue respectively. + + .. attribute:: Document.encoding + + The encoding of this document. Must be a valid string representation of an encoding. This property is read-write. + + .. attribute:: Document.file_type + + The file type of this document as a :class:`Filetype` instance. This property is read-write. + + .. attribute:: Document.text_changed + + Whether this document's text has been changed since it was last saved. + + .. attribute:: Document.file_name + + The file name of this document. + + .. attribute:: Document.has_bom + + Indicates whether the document's file has a byte-order-mark. + + .. attribute:: Document.has_tags + + Indicates whether this document supports source code symbols (tags) to show in the sidebar. + + .. attribute:: Document.index + + Index of the document in Geany's documents array. + + .. attribute:: Document.is_valid + + Indicates whether this document is active and all properties are set correctly. + + .. attribute:: Document.read_only + + Whether the document is in read-only mode. + + .. attribute:: Document.real_path + + The link-dereferenced, locale-encoded file name for this document. + + .. attribute:: Document.editor + + The :class:`Editor` instance associated with this document. + + .. method:: Document.close() + + Close this document. + + :return: :data:`True` if the document was closed, :data:`False` otherwise. + + .. method:: Document.reload([forced_enc=None]) + + Reloads this document. + + :param forced_enc: The encoding to use when reloading this document or :data:`None` to auto-detect it. + + :return: :data:`True` if the document was actually reloaded or :data:`False` otherwise. + + .. method:: Document.rename(new_filename) + + Rename this document to a new file name. Only the file on disk is actually + renamed, you still have to call :meth:`save_as` to change the document object. + It also stops monitoring for file changes to prevent receiving too many file + change events while renaming. File monitoring is setup again in :meth:`save_as`. + + :param new_filename: The new filename to rename to. + + .. method:: Document.save([force=False]) + + Saves this documents file on disk. + + Saving may include replacing tabs by spaces, stripping trailing spaces and adding + a final new line at the end of the file, depending on user preferences. Then, + the `document-before-save` signal is emitted, allowing plugins to modify the + document before it's saved, and the data is actually written to disk. The + file type is set again or auto-detected if it wasn't set yet. Afterwards, + the `document-save` signal is emitted for plugins. If the file is not modified, + this method does nothing unless `force` is set to :data:`True`. + + **Note:** You should ensure that :attr:`file_name` is not :data:`None` before + calling this; otherwise call :func:`dialogs.show_save_as`. + + :param force: Whether to save the document even if it's not modified. + + :return: :data:`True` if the file was saved or :data:`False` if the file could not or should not be saved. + + .. method:: Document.save_as(new_filename) + + Saves the document with a new filename, detecting the filetype. + + :param new_filename: The new filename. + + :return: :data:`True` if the file was saved or :data:`False` if it could not be saved. + diff --git a/geanypy/doc/source/geany.rst b/geanypy/doc/source/geany.rst new file mode 100644 index 000000000..e69de29bb diff --git a/geanypy/doc/source/index.rst b/geanypy/doc/source/index.rst new file mode 100644 index 000000000..98a84e96f --- /dev/null +++ b/geanypy/doc/source/index.rst @@ -0,0 +1,26 @@ +.. GeanyPy documentation master file, created by + sphinx-quickstart on Sun Aug 7 12:42:52 2011. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to GeanyPy's documentation! +=================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + intro.rst + install.rst + starting.rst + quickstart.rst + api.rst + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/geanypy/doc/source/install.rst b/geanypy/doc/source/install.rst new file mode 100644 index 000000000..1b0e06186 --- /dev/null +++ b/geanypy/doc/source/install.rst @@ -0,0 +1,146 @@ +Installation +************ + +Currently there are no binary packages available for installing GeanyPy so it +must be installed from source. The following instructions will describe how +to do this. + +Getting the Source +================== + +The best way currently to get GeanyPy is to check it out from `it's repository +on GitHub.com `_. 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 +`_ +or `zip file `_. 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 `_ 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 `_ or +`Git mirror `_ or you can get one of the +`Nightly builds `_ if you'd rather not compile +it yourself. + +For more information on installing Geany, please refer to +`Geany's excellent manual +`_ + +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 `_ 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 +`_ 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 diff --git a/geanypy/doc/source/intro.rst b/geanypy/doc/source/intro.rst new file mode 100644 index 000000000..7a691b9b5 --- /dev/null +++ b/geanypy/doc/source/intro.rst @@ -0,0 +1,8 @@ +Introduction +************ + +GeanyPy allows people to write their Geany plugins in Python making +authoring a plugin much more accessible to non C programmers. What follows +is a description of installing and using the GeanyPy plugin, paving the way +for the rest of the documentation to covert the details of programming with +the GeanyPy bindings of the Geany API. diff --git a/geanypy/doc/source/quickstart.rst b/geanypy/doc/source/quickstart.rst new file mode 100644 index 000000000..ab128ea1f --- /dev/null +++ b/geanypy/doc/source/quickstart.rst @@ -0,0 +1,121 @@ +Writing a Plugin - Quick Start Guide +************************************ + +This is just a quick tutorial to describe writing a GeanyPy compatible plugin +in Python. Writing a plugin should be pretty straightforward for any Python +programmers, especially those familiar with writing `regular C plugins for +Geany `_. + +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 " + +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 " + + 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 " + + 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 `_:: + + 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 " + + 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. + diff --git a/geanypy/doc/source/starting.rst b/geanypy/doc/source/starting.rst new file mode 100644 index 000000000..917dae262 --- /dev/null +++ b/geanypy/doc/source/starting.rst @@ -0,0 +1,64 @@ +Getting Started +*************** + +Before diving into the details and API docs for programming plugins with +GeanyPy, it's important to note how it works and some features it provides. + +What the heck is GeanyPy, really? +================================= + +GeanyPy is "just another Geany plugin", really. Geany sees GeanyPy as any +other `plugin `_, so +to activate GeanyPy, use Geany's +`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 `_. Props to the +author(s) for such a nice `piece of source code +`_ + +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. diff --git a/geanypy/geany/Makefile.am b/geanypy/geany/Makefile.am new file mode 100644 index 000000000..880c5f662 --- /dev/null +++ b/geanypy/geany/Makefile.am @@ -0,0 +1,18 @@ +geanypy_sources = __init__.py \ + console.py \ + manager.py \ + loader.py \ + plugin.py \ + signalmanager.py +geanypy_objects = $(geanypy_sources:.py=.pyc) +geanypydir = $(libdir)/geany/geanypy/geany +geanypy_DATA = $(geanypy_sources) $(geanypy_objects) + +EXTRA_DIST = $(geanypy_sources) +CLEANFILES = $(geanypy_objects) + +.SUFFIXES = .pyc .py + +.py.pyc: + $(AM_V_GEN) \ + $(PYTHON) -c "from py_compile import compile; compile('$<', '$@')" diff --git a/geanypy/geany/__init__.py b/geanypy/geany/__init__.py new file mode 100644 index 000000000..493dfa4d8 --- /dev/null +++ b/geanypy/geany/__init__.py @@ -0,0 +1,69 @@ +""" +Package file that exposes some of Geany's guts as its own attibutes. Any +objects where it only makes sense to have one instance of are initialed here +and set as attributes. + +You can sort of think of this file as the GeanyData struct from the C API, +though no real attempt is made to mimic that structure here. +""" + +import app +import console +import dialogs +import document +import editor +import encoding +import filetypes +import highlighting +import loader +import main +import manager +import msgwindow +import navqueue +import prefs +import project +import scintilla +import search +import templates +import ui_utils + +from app import App +from prefs import Prefs, ToolPrefs +from main import is_realized, locale_init, reload_configuration +from signalmanager import SignalManager +from ui_utils import MainWidgets, InterfacePrefs +from search import SearchPrefs +from templates import TemplatePrefs + + +__all__ = [ "Plugin", + "is_realized", + "locale_init", + "reload_configuration", + "main_widgets", + "interface_prefs", + "app", + "general_prefs", + "search_prefs", + "template_prefs", + "tool_prefs", + "signals" ] + +# Geany's application data fields +app = App() + +# Import GTK+ widgets that are part of Geany's UI +main_widgets = MainWidgets() + +# Preferences +general_prefs = Prefs() # GeanyData->prefs but name clashes with module +interface_prefs = InterfacePrefs() +search_prefs = SearchPrefs() +template_prefs = TemplatePrefs() +tool_prefs = ToolPrefs() + +# GObject to connect signal handlers on and which emits signals. +signals = SignalManager() + +import plugin +from plugin import Plugin diff --git a/geanypy/geany/__init__.pyc b/geanypy/geany/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66987f9a38c6ba1af7416c4d1c168ccd7188cefd GIT binary patch literal 1779 zcma)6+m7Tk5Uq62tuwctThA_YGqA9|2+cDO2r&W)?ZcuWvl7BD!JD|#-ti^G&JM#X zKgE~uBYXf<*&T!s4|LSk$5nP!ol{Pa|JcoczxeID3Ra&T{QVNYer*I$18}q%)FaRt za0CgDfR6?|20k9}8t}CNuLEBn@CNXW0dE4|9Pk$KtpQJfPX?R=&j-8>e0#tJw;C1qSjMe#P-*AkQOsALK~{AAr1w;6sp? z5j+EV6~RX!XAyi1@+^Yqg9>xt^N44l=U`p{e}UbM;BJ0Ox8bu4AiovWy=Wv`C@oo7 ziooR0t@F||?~G(_!M>5gK0f!X>4Rs&Glf6y!7>qodeaB#uNk|skGXsER#q5$SjtY~ zF6OL$WJcUmE@?fccWf!%VN#26>w~aWtRQH=RY3_YYevU&VQV}Ts2A0HH zrm&4h`I73XUt}HE+EqxqB>v$Tin$T2a7f22gA=WTy4)M70&4aSFdx)8>c=uB;dl~OTQid9A zJ4Y``Jm4$LqBOFn^huc*7&J+}_p0o1n=fz^6g0_#F516`LwYBvR!tR@wV +# 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 . +# + +# 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) diff --git a/geanypy/geany/console.pyc b/geanypy/geany/console.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e70fd15d9eae717b91a2b712ea7794f9a5ac1751 GIT binary patch literal 19185 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 Cui=LP literal 0 HcmV?d00001 diff --git a/geanypy/geany/loader.py b/geanypy/geany/loader.py new file mode 100644 index 000000000..14b9b73d6 --- /dev/null +++ b/geanypy/geany/loader.py @@ -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 diff --git a/geanypy/geany/loader.pyc b/geanypy/geany/loader.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20bce51881452618dd03dc7b0a160da903f8d58e GIT binary patch literal 5382 zcma)AOK&4t6+TsVoY;9zr`vSTt7`^Y@jz&p$0}-IhR&!NF?iCIq1_XREXP$z%5hvy z-Ro{god`r`#{#yXS->DM3nU~KNGuRP0tuG<16FLNK)qc}53+FK2x*tD~xZ?(}H*!?tH4cc8}=dj9c@^0FS;%t0R zp7?D%1`D~l3t*5)Bc+ebkJ_}s-~?x8sGTBDRPC-u4>xp-CQyQED97F-yR3+DIQ`phYqUft`n^BS(`wZHxN3EpW+Udqk zH)8A(dJ`LGc}Ft=4z|Rp{eIkwY!%fkHa5-TW|!)XMoVkFfGwghad$V=78kmmYp979 zri_{{&2IOP?F?E?9Fy2)llm|;3GvH**<1Fm`fY;7ZMf<#9`kz?!%y_`)Wk<}>WPIK zeAR?UO=J~0elY4RQkei2wh5itDQXMwqWQB`IW5TWW!NRECZKM}#Shes8`m+LlF|8=tn z!EayG8RQmuY^bYSrjqdfHQwK8kCu@dRcXIv_aQp4F#u1yI%(3=N(x{s9czRk=RMAV zqV~S?-rHH0X1Y4gvR+!FIz#F`nA!yk)P^Y19C>)WX!s6fNW ziKBzHAl7vP+=3cFRfgXZT*5Wiw{I)5MIw8Ky|Gx$;$T{`4cbR%hCPb*h8%53g*=HSkE7E=w%Rww4N0_<|+#DN?uiATK4Af zdCr^n+B8%e!WgCx(HNfQZU~(!IwMacE^xz|9L-D5K?r|>npMZ7E_e$_5f$yM-un(6 z4j`&~b`U*$VW*-D&{%gvGpu7av+i(0B1-oS#FKq-dI_O5bM;9O!YxMd906$EvJ z5;lWJ=%T9y!8!to&LA*B@Ro)WcGSo@p^_(&a(k&!GZd+2L5b#ZV)w%rI5j|_n%8lP zlt(K?aUx(T=q_d{ZK^j;GPKezbT`Dt0%NI^a z{T6x+?<@Y2lamMXQ8`H>^gc}{Jk}uI#Tci@e+BcxS5Ro+Q>}ikAX)rb_1(kbT#$+r zg!aX!&5=)04F5e=*gu~he1?D3;G+}+Ev|3>MTyG=si{$50y{y^8i_Cok?1^%ITHJ` z4C|wfv}b*L?VQJQAA2$iU|=wfSqNX2IEtBYxdiD3P(9pDKbXUBp{}M}Y z(ZAs>7g)A=FXl}o3dMgLLLnx>{E%)SFIth?jhaw1OJJa}+ubs~O z!Gr-K%yCsfD=x@z0aBI{a9csvQPHg9m&iJ%k=x;fSvf&4^90O@9ZS;l{a%IiqVRc@C;!3gf>}kk<}qu*E^^j53TzrAhkYbAfr+h6dV3}~Ort?J4r)(MU%@4rSLSv> zK5jN~!HK?{n$eA3(ZqHn*F7Ple|6+11X zFEW}>LGts+Zqh;4jK>O57i+HbV_f*t00m+ zuknoi1$^ev&rjK#^O2;GnJ68fyLik#ieYO^*AMiq2^6UDjTt*^6HRb1TnGk>FPvK) z`VMREO$w@o-#XsJ6y%Ck=nCH8p|v^A@*OWS5x7SfHjdZw^do4sZSn~Daim!VVF)Rr z%NGi$;k8x6YIuX)6kU-UB+Y?M@Jh>Q3oCCjO&^$@Ru+DdbGXU-*&wdzZZl}PEt#_D ztDVkBdd6O=bc*{UJceXU9$bV{=b`F3D159?Zb+$2AI0%|xk3@XZn#_y3Z?o+s?-RL zVB^U*oIcNhCuBi(2StJyp|>hbk1A~Ro*lna9SAtVC51@xNN5o=yaEobo0}*0>wYz|C&_Bl_ywCBF z#U=`c=DYE+bCZjz59yP-)ukH$GG_b~kIS|F0<>L*zWJd!=VnY};-gxp12R{DMT#7N z<8NwRtr!u2)XFi%k?#yt*Yh1Se?bXn=Ej1-lU!Oc9Xq9mOCnbS#w!**kJ8V6#p+KS1~t_X}rRC z<4@3U2&urJuEU6GOQ?r$qtN&JW-}-1rsEmqASD}WR8!_qlV`?RO^!m+v3spi%X!G= z4_NS5!gC#K?c#4bA+JwIFwdhX`6a(RTV5>B<5R-tYWc%s %s\n%s\n' + + 'Author: %s\n' + + 'Filename: %s') % ( + 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_text(plugin_info.filename)) + + loaded = plugin_info.filename in self.loader.plugins + + list_store.append([loaded, lbl, plugin_info.filename]) + + + def on_selected_plugin_changed(self, treeview, model): + + path = treeview.get_cursor()[0] + iter = model.get_iter(path) + filename = model.get_value(iter, 2) + active = model.get_value(iter, 0) + + if self.loader.plugin_has_configure(filename): + self.btn_prefs.set_visible(True) + else: + self.btn_prefs.set_visible(False) + + if self.loader.plugin_has_help(filename): + self.btn_help.set_visible(True) + else: + self.btn_help.set_visible(False) + + + def on_plugin_load_toggled(self, cell, path, model): + active = not cell.get_active() + iter = model.get_iter(path) + model.set_value(iter, 0, active) + if active: + self.activate_plugin(model.get_value(iter, 2)) + else: + self.deactivate_plugin(model.get_value(iter, 2)) + + + def on_row_activated(self, tvw, path, view_col, cell, model): + self.on_plugin_load_toggled(cell, path, model) diff --git a/geanypy/geany/manager.pyc b/geanypy/geany/manager.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4971509b31d6eddb2812e0ecdce9725d70c05e13 GIT binary patch literal 6174 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 literal 0 HcmV?d00001 diff --git a/geanypy/m4/ax_python_devel.m4 b/geanypy/m4/ax_python_devel.m4 new file mode 100644 index 000000000..a62b860de --- /dev/null +++ b/geanypy/m4/ax_python_devel.m4 @@ -0,0 +1,325 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_python_devel.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PYTHON_DEVEL([version]) +# +# DESCRIPTION +# +# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it +# in your configure.ac. +# +# This macro checks for Python and tries to get the include path to +# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) +# output variables. It also exports $(PYTHON_EXTRA_LIBS) and +# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. +# +# You can search for some particular version of Python by passing a +# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please +# note that you *have* to pass also an operator along with the version to +# match, and pay special attention to the single quotes surrounding the +# version number. Don't use "PYTHON_VERSION" for this: that environment +# variable is declared as precious and thus reserved for the end-user. +# +# This macro should work for all versions of Python >= 2.1.0. As an end +# user, you can disable the check for the python version by setting the +# PYTHON_NOVERSIONCHECK environment variable to something else than the +# empty string. +# +# If you need to use this macro for an older Python version, please +# contact the authors. We're always open for feedback. +# +# LICENSE +# +# Copyright (c) 2009 Sebastian Huber +# Copyright (c) 2009 Alan W. Irwin +# Copyright (c) 2009 Rafael Laboissiere +# Copyright (c) 2009 Andrew Collier +# Copyright (c) 2009 Matteo Settenvini +# Copyright (c) 2009 Horst Knorr +# +# 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 3 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, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 8 + +AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) +AC_DEFUN([AX_PYTHON_DEVEL],[ + # + # Allow the use of a (user set) custom python version + # + AC_ARG_VAR([PYTHON_VERSION],[The installed Python + version to use, for example '2.3'. This string + will be appended to the Python interpreter + canonical name.]) + + AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) + if test -z "$PYTHON"; then + AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) + PYTHON_VERSION="" + fi + + # + # Check for a version of Python >= 2.1.0 + # + AC_MSG_CHECKING([for a version of Python >= '2.1.0']) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver >= '2.1.0')"` + if test "$ac_supports_python_ver" != "True"; then + if test -z "$PYTHON_NOVERSIONCHECK"; then + AC_MSG_RESULT([no]) + AC_MSG_FAILURE([ +This version of the AC@&t@_PYTHON_DEVEL macro +doesn't work properly with versions of Python before +2.1.0. You may need to re-run configure, setting the +variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, +PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. +Moreover, to disable this check, set PYTHON_NOVERSIONCHECK +to something else than an empty string. +]) + else + AC_MSG_RESULT([skip at user request]) + fi + else + AC_MSG_RESULT([yes]) + fi + + # + # if the macro parameter ``version'' is set, honour it + # + if test -n "$1"; then + AC_MSG_CHECKING([for a version of Python $1]) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver $1)"` + if test "$ac_supports_python_ver" = "True"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_ERROR([this package requires Python $1. +If you have it installed, but it isn't the default Python +interpreter in your system path, please pass the PYTHON_VERSION +variable to configure. See ``configure --help'' for reference. +]) + PYTHON_VERSION="" + fi + fi + + # + # Check if you have distutils, else fail + # + AC_MSG_CHECKING([for the distutils Python package]) + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` + if test -z "$ac_distutils_result"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot import Python module "distutils". +Please check your Python installation. The error was: +$ac_distutils_result]) + PYTHON_VERSION="" + fi + + # + # Check for Python include path + # + AC_MSG_CHECKING([for Python include path]) + if test -z "$PYTHON_CPPFLAGS"; then + python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc ());"` + if test -n "${python_path}"; then + python_path="-I$python_path" + fi + PYTHON_CPPFLAGS=$python_path + fi + AC_MSG_RESULT([$PYTHON_CPPFLAGS]) + AC_SUBST([PYTHON_CPPFLAGS]) + + # + # Check for Python library path + # + AC_MSG_CHECKING([for Python library path]) + if test -z "$PYTHON_LDFLAGS"; then + # (makes two attempts to ensure we've got a version number + # from the interpreter) + ac_python_version=`cat<]], + [[Py_Initialize();]]) + ],[pythonexists=yes],[pythonexists=no]) + AC_LANG_POP([C]) + # turn back to default flags + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + + AC_MSG_RESULT([$pythonexists]) + + if test ! "x$pythonexists" = "xyes"; then + AC_MSG_FAILURE([ + Could not link test program to Python. Maybe the main Python library has been + installed in some non-standard library path. If so, pass it to configure, + via the LDFLAGS environment variable. + Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib" + ============================================================================ + ERROR! + You probably have to install the development version of the Python package + for your distribution. The exact name of this package varies among them. + ============================================================================ + ]) + PYTHON_VERSION="" + fi + + # + # all done! + # +]) diff --git a/geanypy/m4/ax_python_library.m4 b/geanypy/m4/ax_python_library.m4 new file mode 100644 index 000000000..50994078a --- /dev/null +++ b/geanypy/m4/ax_python_library.m4 @@ -0,0 +1,26 @@ +# Copyright (c) 2011 Colomban Wendling +# +# AX_PYTHON_LIBRARY([action-if-found], [action-if-not-found]) +# +# Tries to locate Python DSO path. It requires $PYTHON to be set + +AC_DEFUN([AX_PYTHON_LIBRARY],[ + AC_REQUIRE([AX_PYTHON_DEVEL]) + + AC_MSG_CHECKING([for Python DSO location]) + + ax_python_library=`cat << EOD | $PYTHON - 2>/dev/null +from distutils.sysconfig import get_config_vars +from os.path import join as path_join + +cvars = get_config_vars() +# support multiarch-enabled distributions like Ubuntu +if not 'MULTIARCH' in cvars.keys(): + cvars[['MULTIARCH']] = '' +print(path_join(cvars[['LIBDIR']], cvars[['MULTIARCH']], cvars[['LDLIBRARY']])) +EOD` + + AC_SUBST([PYTHON_LIBRARY], [$ax_python_library]) + AC_MSG_RESULT([$PYTHON_LIBRARY]) + AS_IF([test -z "$ax_python_library"], [$2], [$1]) +]) diff --git a/geanypy/plugins/Makefile.am b/geanypy/plugins/Makefile.am new file mode 100644 index 000000000..6aa07ff22 --- /dev/null +++ b/geanypy/plugins/Makefile.am @@ -0,0 +1,4 @@ +geanypy_plugins = demo.py hello.py console.py +geanypydir = $(datadir)/geany/geanypy/plugins +geanypy_DATA = $(geanypy_plugins) +EXTRA_DIST = $(geanypy_plugins) diff --git a/geanypy/plugins/console.py b/geanypy/plugins/console.py new file mode 100644 index 000000000..ee26ae35b --- /dev/null +++ b/geanypy/plugins/console.py @@ -0,0 +1,313 @@ +import os +from ConfigParser import SafeConfigParser +import geany +import geany.console +import gobject +import gtk +import gtk.gdk as gdk +import pango + + +WIDGET_STATES = [ gtk.STATE_NORMAL, gtk.STATE_ACTIVE, gtk.STATE_PRELIGHT, + gtk.STATE_SELECTED, gtk.STATE_INSENSITIVE ] + + +class ConsolePlugin(geany.Plugin): + + __plugin_name__ = "Python Console" + __plugin_description__ = ("Installs a Python console that allows you " + + "to use the Geany API.") + __plugin_version__ = "0.01" + __plugin_author__ = "Matthew Brush " + + _font = "Monospace 9" + _bg = "#FFFFFF" + _fg = "#000000" + _banner = ("Geany Python Console\n You can access the Geany Python " + + "API by importing the `geany' module.\n") + _use_rl_completer = True + + _builder = None + + def __init__(self): + geany.Plugin.__init__(self) + self.load_config() + self.install_console() + + + def cleanup(self): + #self.save_config() + self.on_save_config_timeout() # do it now + self.uninstall_console() + + + def load_config(self): + self.cfg_path = os.path.join(geany.app.configdir, "plugins", "pyconsole.conf") + self.cfg = SafeConfigParser() + self.cfg.read(self.cfg_path) + + + def save_config(self): + gobject.idle_add(self.on_save_config_timeout) + + + def on_save_config_timeout(self, data=None): + self.cfg.write(open(self.cfg_path, 'w')) + return False + + + def install_console(self): + + # load general settings + self.banner = self.banner + self.use_rl_completer = self.use_rl_completer + + self.swin = gtk.ScrolledWindow() + self.swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) + self.console = geany.console.Console(banner = self.banner, + use_rlcompleter = self.use_rl_completer) + self.console.connect("populate-popup", self.on_console_populate_popup) + + # apply appearance settings + self.font = self.font + self.bg = self.bg + self.fg = self.fg + + self.swin.add(self.console) + geany.main_widgets.message_window_notebook.append_page(self.swin, + gtk.Label("Python")) + + self.swin.show_all() + self.save_config() + + def uninstall_console(self): + self.swin.destroy() + + + def _get_font(self): + if self.cfg.has_section('appearances'): + if self.cfg.has_option('appearances', 'font'): + return self.cfg.get('appearances', 'font') + return self._font + def _set_font(self, font): + self._font = font + font_desc = pango.FontDescription(font) + self.console.modify_font(font_desc) + if not self.cfg.has_section('appearances'): + self.cfg.add_section('appearances') + self.cfg.set('appearances', 'font', self._font) + self.save_config() + font = property(_get_font, _set_font) + + + def _get_bg(self): + if self.cfg.has_section('appearances'): + if self.cfg.has_option('appearances', 'bg_color'): + return self.cfg.get('appearances', 'bg_color') + return self._bg + def _set_bg(self, bg): + self._bg = bg + color = gdk.color_parse(self._bg) + for state in WIDGET_STATES: + self.console.modify_bg(state, color) + self.console.modify_base(state, color) + if not self.cfg.has_section('appearances'): + self.cfg.add_section('appearances') + self.cfg.set('appearances', 'bg_color', self._bg) + self.save_config() + bg = property(_get_bg, _set_bg) + + + def _get_fg(self): + if self.cfg.has_section('appearances'): + if self.cfg.has_option('appearances', 'fg_color'): + return self.cfg.get('appearances', 'fg_color') + return self._fg + def _set_fg(self, fg): + self._fg = fg + color = gdk.color_parse(self._fg) + for state in WIDGET_STATES: + self.console.modify_fg(state, color) + self.console.modify_text(state, color) + if not self.cfg.has_section('appearances'): + self.cfg.add_section('appearances') + self.cfg.set('appearances', 'fg_color', self._fg) + self.save_config() + fg = property(_get_fg, _set_fg) + + + def _get_banner(self): + if self.cfg.has_section('general'): + if self.cfg.has_option('general', 'banner'): + return self.cfg.get('general', 'banner') + return self._banner + def _set_banner(self, banner): + self._banner = banner + if not self.cfg.has_section('general'): + self.cfg.add_section('general') + self.cfg.set('general', 'banner', self._banner) + self.save_config() + banner = property(_get_banner, _set_banner) + + + def _get_use_rl_completer(self): + if self.cfg.has_section('general'): + if self.cfg.has_option('general', 'use_rl_completer'): + return self.cfg.getboolean('general', 'use_rl_completer') + return self._use_rl_completer + def _set_use_rl_completer(self, use_rl_completer): + self._use_rl_completer = use_rl_completer + if not self.cfg.has_section('general'): + self.cfg.add_section('general') + self.cfg.set('general', 'use_rl_completer', + str(self._use_rl_completer).lower()) + self.save_config() + use_rl_completer = property(_get_use_rl_completer, _set_use_rl_completer) + + + def on_console_populate_popup(self, textview, menu, data=None): + item = gtk.SeparatorMenuItem() + item.show() + menu.append(item) + item = gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES) + item.show() + menu.append(item) + item.connect("activate", lambda w,d=None: self.show_configure()) + + + def on_banner_changed(self, text_buf, data=None): + self.banner = text_buf.get_text(text_buf.get_start_iter(), text_buf.get_end_iter()) + + def on_use_rl_completer_toggled(self, chk_btn, data=None): + self.use_rl_completer = chk_btn.get_active() + + def on_font_changed(self, font_btn, data=None): + self.font = font_btn.get_font_name() + + def on_fg_color_changed(self, clr_btn, data=None): + self.fg = clr_btn.get_color().to_string() + + def on_bg_color_changed(self, clr_btn, data=None): + self.bg = clr_btn.get_color().to_string() + + + def show_configure(self): + dialog = gtk.Dialog("Configure Python Console", + geany.main_widgets.window, + gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + (gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT)) + + dialog.set_has_separator(True) + + content_area = dialog.get_content_area() + content_area.set_border_width(6) + + vbox = gtk.VBox(spacing=6) + vbox.set_border_width(6) + + lbl = gtk.Label() + lbl.set_use_markup(True) + lbl.set_markup("General") + + fra_general = gtk.Frame("") + fra_general.set_shadow_type(gtk.SHADOW_NONE) + fra_general.set_label_widget(lbl) + + al_general = gtk.Alignment(0.0, 0.0, 1.0, 1.0) + al_general.set_padding(0, 0, 12, 0) + fra_general.add(al_general) + + tbl = gtk.Table(3, 2, False) + tbl.set_row_spacings(6) + tbl.set_col_spacings(6) + tbl.set_border_width(6) + + lbl = gtk.Label("Banner:") + lbl.set_alignment(0.0, 0.0) + + tvw = gtk.TextView() + tvw.get_buffer().set_text(self.banner) + tvw.get_buffer().connect("changed", self.on_banner_changed) + + swin = gtk.ScrolledWindow() + swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + swin.set_shadow_type(gtk.SHADOW_ETCHED_IN) + swin.add(tvw) + + tbl.attach(lbl, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(swin, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0) + + lbl = gtk.Label("") + lbl.set_alignment(0.0, 0.5) + + check = gtk.CheckButton("Use Readline") + if self.use_rl_completer: + check.set_active(True) + check.connect("toggled", self.on_use_rl_completer_toggled) + + tbl.attach(lbl, 0, 1, 1, 2, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(check, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 0, 0) + + lbl = gtk.Label("") + lbl.set_alignment(0.0, 0.5) + lbl.set_use_markup(True) + lbl.set_markup('' + + 'Note: General settings will be applied when console is reloaded.' + + '') + tbl.attach(lbl, 0, 2, 2, 3, gtk.FILL, gtk.FILL, 0, 0) + + al_general.add(tbl) + + lbl = gtk.Label() + lbl.set_use_markup(True) + lbl.set_markup("Appearances") + + fra_appearances = gtk.Frame("") + fra_appearances.set_shadow_type(gtk.SHADOW_NONE) + fra_appearances.set_label_widget(lbl) + + al_appearances = gtk.Alignment(0.0, 0.0, 1.0, 1.0) + al_appearances.set_padding(0, 0, 12, 0) + fra_appearances.add(al_appearances) + + tbl = gtk.Table(3, 2, False) + tbl.set_row_spacings(6) + tbl.set_col_spacings(6) + tbl.set_border_width(6) + + lbl = gtk.Label("Font:") + lbl.set_alignment(0.0, 0.5) + + btn = gtk.FontButton(self.font) + btn.connect("font-set", self.on_font_changed) + + tbl.attach(lbl, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(btn, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) + + lbl = gtk.Label("FG Color:") + lbl.set_alignment(0.0, 0.5) + + btn = gtk.ColorButton(gdk.color_parse(self.fg)) + btn.connect("color-set", self.on_fg_color_changed) + + tbl.attach(lbl, 0, 1, 1, 2, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(btn, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) + + lbl = gtk.Label("BG Color:") + lbl.set_alignment(0.0, 0.5) + + btn = gtk.ColorButton(gdk.color_parse(self.bg)) + btn.connect("color-set", self.on_bg_color_changed) + + tbl.attach(lbl, 0, 1, 2, 3, gtk.FILL, gtk.FILL, 0, 0) + tbl.attach(btn, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) + + al_appearances.add(tbl) + + vbox.pack_start(fra_general, True, True, 0) + vbox.pack_start(fra_appearances, False, True, 0) + content_area.pack_start(vbox, True, True, 0) + content_area.show_all() + + dialog.run() + dialog.destroy() diff --git a/geanypy/plugins/demo.py b/geanypy/plugins/demo.py new file mode 100644 index 000000000..77ef0d018 --- /dev/null +++ b/geanypy/plugins/demo.py @@ -0,0 +1,18 @@ +import geany + + +class DemoPlugin(geany.Plugin): + + __plugin_name__ = "Demo Plugin" + __plugin_version__ = "0.01" + __plugin_description__ = "A Sample plugin" + __plugin_author__ = "Matthew Brush " + + def __init__(self): + geany.Plugin.__init__(self) + print("Demo plugin initializing") + doc = geany.document.new_file() + doc.editor.scintilla.set_text("Hello from the Demo plugin") + + def cleanup(self): + print("Demo plugin cleaning up") diff --git a/geanypy/plugins/hello.py b/geanypy/plugins/hello.py new file mode 100644 index 000000000..dfa7d273f --- /dev/null +++ b/geanypy/plugins/hello.py @@ -0,0 +1,21 @@ +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 " + + 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") diff --git a/geanypy/src/Makefile.am b/geanypy/src/Makefile.am new file mode 100644 index 000000000..f2b479772 --- /dev/null +++ b/geanypy/src/Makefile.am @@ -0,0 +1,38 @@ +include $(top_srcdir)/build/vars.build.mk + +geanyplugin_LTLIBRARIES = geanypy.la +geanyplugindir = $(libdir)/geany + +geanypy_la_LDFLAGS = -module -avoid-version -Wl,--export-dynamic +geanypy_la_CPPFLAGS = @GEANY_CFLAGS@ @PYGTK_CFLAGS@ @PYTHON_CPPFLAGS@ \ + -DGEANYPY_PYTHON_DIR="\"$(libdir)/geany/geanypy\"" \ + -DGEANYPY_PLUGIN_DIR="\"$(datadir)/geany/geanypy/plugins\"" \ + -UHAVE_CONFIG_H +geanypy_la_LIBADD = @GEANY_LIBS@ @PYGTK_LIBS@ @PYTHON_LDFLAGS@ \ + @PYTHON_EXTRA_LIBS@ @PYTHON_EXTRA_LDFLAGS@ +geanypy_la_SOURCES = geanypy-app.c \ + geanypy-dialogs.c \ + geanypy-document.c geanypy-document.h \ + geanypy-editor.c geanypy-editor.h \ + geanypy-encoding.c geanypy-encoding.h \ + geanypy-filetypes.c \ + geanypy-geanypy.h \ + geanypy-highlighting.c \ + geanypy-indentprefs.c \ + geanypy-interfaceprefs.c \ + geanypy-main.c \ + geanypy-mainwidgets.c \ + geanypy-msgwindow.c \ + geanypy-navqueue.c \ + geanypy-plugin.c geanypy-plugin.h \ + geanypy-prefs.c \ + geanypy-project.c geanypy-project.h \ + geanypy-scinotification.c \ + geanypy-scinotifyheader.c \ + geanypy-scintilla.c geanypy-scintilla.h \ + geanypy-search.c \ + geanypy-signalmanager.c geanypy-signalmanager.h \ + geanypy-templates.c \ + geanypy-uiutils.c geanypy-uiutils.h + +include $(top_srcdir)/build/cppcheck.mk diff --git a/geanypy/src/geanypy-app.c b/geanypy/src/geanypy-app.c new file mode 100644 index 000000000..f9e1e0d91 --- /dev/null +++ b/geanypy/src/geanypy-app.c @@ -0,0 +1,113 @@ +#include "geanypy.h" + + +typedef struct +{ + PyObject_HEAD + GeanyApp *app; +} App; + + +static void +App_dealloc(App *self) +{ + g_return_if_fail(self != NULL); + self->ob_type->tp_free((PyObject *) self); +} + + +static int +App_init(App *self) +{ + g_return_val_if_fail(self != NULL, -1); + g_return_val_if_fail(geany_data, -1); + self->app = geany_data->app; + return 0; +} + + +static PyObject * +App_get_property(App *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->app) + { + PyErr_SetString(PyExc_RuntimeError, + "App instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "configdir") && self->app->configdir) + return PyString_FromString(self->app->configdir); +#if ENABLE_PRIVATE + else if (g_str_equal(prop_name, "datadir") && self->app->datadir) + return PyString_FromString(self->app->datadir); + else if (g_str_equal(prop_name, "docdir") && self->app->docdir) + return PyString_FromString(self->app->docdir); +#endif + else if (g_str_equal(prop_name, "debug_mode") && self->app->debug_mode) + { + if (self->app->debug_mode) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "project") && self->app->project) + return (PyObject *) GEANYPY_NEW(Project); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(App); + + +static PyGetSetDef App_getseters[] = { + GEANYPY_GETSETDEF(App, "configdir", + "User configuration directory, usually ~/.config/geany. "), +#ifdef ENABLE_PRIVATE + GEANYPY_GETSETDEF(App, "datadir", "Geany's data directory."), + GEANYPY_GETSETDEF(App, "docdir", "Geany's documentation directory."), +#endif + GEANYPY_GETSETDEF(App, "debug_mode", + "True if debug messages should be printed."), + GEANYPY_GETSETDEF(App, "project", + "Currently active project or None if none is open."), + { NULL } +}; + + +static PyTypeObject AppType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.app.App", /* tp_name */ + sizeof(App), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) App_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyApp structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + App_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) App_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + + +static PyMethodDef AppModule_methods[] = { { NULL } }; + + +PyMODINIT_FUNC initapp(void) +{ + PyObject *m; + + AppType.tp_new = PyType_GenericNew; + if (PyType_Ready(&AppType) < 0) + return; + + m = Py_InitModule3("app", AppModule_methods, "Application information"); + + Py_INCREF(&AppType); + PyModule_AddObject(m, "App", (PyObject *) &AppType); +} diff --git a/geanypy/src/geanypy-dialogs.c b/geanypy/src/geanypy-dialogs.c new file mode 100644 index 000000000..7698927a8 --- /dev/null +++ b/geanypy/src/geanypy-dialogs.c @@ -0,0 +1,131 @@ +#include "geanypy.h" + + +static PyObject * +Dialogs_show_input(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const gchar *title=NULL, *label_text=NULL, *default_text=NULL, *result=NULL; + PyObject *py_win_obj = NULL; + PyGObject *py_win_gobj; + GtkWindow *win; + static gchar *kwlist[] = { "title", "parent", "label_text", "default_text", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|zOzz", kwlist, + &title, &py_win_obj, &label_text, &default_text)) + { + if (title == NULL) + title = ""; + + if (py_win_obj != NULL && py_win_obj != Py_None) + { + py_win_gobj = (PyGObject *) py_win_obj; + win = GTK_WINDOW((GObject *) py_win_gobj->obj); + } + else + win = GTK_WINDOW(geany->main_widgets->window); + + result = dialogs_show_input(title, win, label_text, default_text); + if (result != NULL) + return PyString_FromString(result); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Dialogs_show_input_numeric(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const gchar *title=NULL, *label_text = NULL; + gdouble value = 0.0, min = 0.0, max = 0.0, step = 0.0; + static gchar *kwlist[] = { "title", "label_text", "value", "minimum", + "maximum", "step", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|zzdddd", kwlist, + &title, &label_text, &value, &min, &max, &step)) + { + if (title == NULL) + title = ""; + + if (label_text == NULL) + label_text = ""; + + if (dialogs_show_input_numeric(title, label_text, &value, min, max, step)) + return PyFloat_FromDouble(value); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Dialogs_show_msgbox(PyObject *self, PyObject *args, PyObject *kwargs) +{ + gchar *text = NULL; + gint msgtype = (gint) GTK_MESSAGE_INFO; + static gchar *kwlist[] = { "text", "msgtype", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist, &text, &msgtype)) + { + if (text != NULL) + { + dialogs_show_msgbox((GtkMessageType) msgtype, "%s", text); + Py_RETURN_TRUE; + } + } + Py_RETURN_NONE; +} + + +static PyObject * +Dialogs_show_question(PyObject *self, PyObject *args, PyObject *kwargs) +{ + gchar *text = NULL; + static gchar *kwlist[] = { "text", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &text)) + { + if (text != NULL) + { + if (dialogs_show_question("%s", text)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + } + Py_RETURN_NONE; +} + + +static PyObject * +Dialogs_show_save_as(PyObject *self) +{ + if (dialogs_show_save_as()) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + + +static +PyMethodDef DialogsModule_methods[] = { + { "show_input", (PyCFunction) Dialogs_show_input, METH_KEYWORDS, + "Asks the user for input." }, + { "show_input_numeric", (PyCFunction) Dialogs_show_input_numeric, METH_KEYWORDS, + "Shows an input box to enter a numerical value." }, + { "show_msgbox", (PyCFunction) Dialogs_show_msgbox, METH_KEYWORDS, + "Shows a message box of the specified type. See " + "gtk.MESSAGE_TYPE_* constants."}, + { "show_question", (PyCFunction) Dialogs_show_question, METH_KEYWORDS, + "Shows a question message box with Yes/No buttons." }, + { "show_save_as", (PyCFunction) Dialogs_show_save_as, METH_NOARGS, + "Shows the Save As dialog for the current notebook." }, + { NULL } +}; + + +PyMODINIT_FUNC initdialogs(void) +{ + Py_InitModule3("dialogs", DialogsModule_methods, + "Wrappers around Geany's dialog functions."); +} diff --git a/geanypy/src/geanypy-document.c b/geanypy/src/geanypy-document.c new file mode 100644 index 000000000..4799627db --- /dev/null +++ b/geanypy/src/geanypy-document.c @@ -0,0 +1,562 @@ +#include "geanypy.h" + + +static void +Document_dealloc(Document *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + + +static int +Document_init(Document *self) +{ + self->doc = NULL; + return 0; +} + + +static PyObject * +Document_get_property(Document *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->doc) + { + PyErr_SetString(PyExc_RuntimeError, + "Document instance not initialized properly"); + return NULL; + } + + if (!DOC_VALID(self->doc)) + { + PyErr_SetString(PyExc_RuntimeError, "Document is invalid"); + return NULL; + } + + if (g_str_equal(prop_name, "basename_for_display")) + { + PyObject *py_str = NULL; + gchar *res_string; + res_string = document_get_basename_for_display(self->doc, -1); + if (!res_string) + { + PyErr_SetString(PyExc_RuntimeError, + "Geany API failed to return a string"); + Py_RETURN_NONE; + } + py_str = PyString_FromString(res_string); + g_free(res_string); + return py_str; + } + else if (g_str_equal(prop_name, "notebook_page")) + return Py_BuildValue("i", document_get_notebook_page(self->doc)); + else if (g_str_equal(prop_name, "status_color")) + { + const GdkColor *color = document_get_status_color(self->doc); + if (!color) + Py_RETURN_NONE; + return Py_BuildValue("iii", color->red, color->green, color->blue); + } + else if (g_str_equal(prop_name, "editor") && self->doc->editor) + { + Editor *editor; + if (self->doc->editor != NULL) + { + editor = Editor_create_new_from_geany_editor(self->doc->editor); + return (PyObject *) editor; + } + Py_RETURN_NONE; + } + else if (g_str_equal(prop_name, "encoding") && self->doc->encoding) + return PyString_FromString(self->doc->encoding); + else if (g_str_equal(prop_name, "file_name") && self->doc->file_name) + return PyString_FromString(self->doc->file_name); + else if (g_str_equal(prop_name, "file_type") && self->doc->file_type) + return (PyObject *) Filetype_create_new_from_geany_filetype(self->doc->file_type); + else if (g_str_equal(prop_name, "has_bom")) + { + if (self->doc->has_bom) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "has_tags")) + { + if (self->doc->has_tags) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "index")) + return Py_BuildValue("i", self->doc->index); + else if (g_str_equal(prop_name, "is_valid")) + { + if (self->doc->is_valid) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "readonly")) + { + if (self->doc->readonly) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "real_path")) + { + if (self->doc->real_path) + return PyString_FromString(self->doc->real_path); + Py_RETURN_NONE; + } + else if (g_str_equal(prop_name, "text_changed")) + { + if (self->doc->changed) + Py_RETURN_NONE; + else + Py_RETURN_NONE; + } + + Py_RETURN_NONE; +} + + +static int +Document_set_property(Document *self, PyObject *value, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, -1); + g_return_val_if_fail(value != NULL, -1); + g_return_val_if_fail(prop_name != NULL, -1); + + if (!self->doc) + { + PyErr_SetString(PyExc_RuntimeError, + "Document instance not initialized properly"); + return -1; + } + + if (g_str_equal(prop_name, "encoding")) + { + gchar *encoding = PyString_AsString(value); + if (encoding) + { + document_set_encoding(self->doc, encoding); + return 0; + } + } + else if (g_str_equal(prop_name, "filetype")) + { + Filetype *filetype = (Filetype *) value; + if (filetype->ft) + { + document_set_filetype(self->doc, filetype->ft); + return 0; + } + } + else if (g_str_equal(prop_name, "text_changed")) + { + long v = PyInt_AsLong(value); + if (v == -1 && PyErr_Occurred()) + { + PyErr_Print(); + return -1; + } + document_set_text_changed(self->doc, (gboolean) v); + return 0; + } + + PyErr_SetString(PyExc_AttributeError, "can't set property"); + return -1; +} + + +static PyObject* +Document_close(Document *self) +{ + if (document_close(self->doc)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + + +static PyObject* +Document_reload_file(Document *self, PyObject *args, PyObject *kwargs) +{ + gchar *forced_enc = NULL; + static gchar *kwlist[] = { "forced_enc", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|z", kwlist, &forced_enc)) + { + if (document_reload_file(self->doc, forced_enc)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} + + +static PyObject* +Document_rename_file(Document *self, PyObject *args, PyObject *kwargs) +{ + gchar *new_fn = NULL; + static gchar *kwlist[] = { "new_filename", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &new_fn)) + { + if (new_fn != NULL) + document_rename_file(self->doc, new_fn); + } + + if (DOC_VALID(self->doc) && self->doc->file_name == new_fn) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + + Py_RETURN_NONE; +} + + +static PyObject* +Document_save_file(Document *self, PyObject *args, PyObject *kwargs) +{ + gboolean result; + gint force = 0; + static gchar *kwlist[] = { "force", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &force)) + { + result = document_save_file(self->doc, (gboolean) force); + if (result) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + Py_RETURN_NONE; +} + + +static PyObject* +Document_save_file_as(Document *self, PyObject *args, PyObject *kwargs) +{ + gboolean result; + gchar *filename = NULL; + static gchar *kwlist[] = { "new_filename", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename)) + { + if (filename != NULL) + { + result = document_save_file_as(self->doc, filename); + if (result) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + } + Py_RETURN_NONE; +} + + +static PyMethodDef Document_methods[] = { + { "close", (PyCFunction)Document_close, METH_NOARGS, + "Closes the document." }, + { "reload_file", (PyCFunction)Document_reload_file, METH_KEYWORDS, + "Reloads the document with the specified file encoding or None " + "to auto-detect the file encoding." }, + { "rename_file", (PyCFunction)Document_rename_file, METH_KEYWORDS, + "Renames the document's file." }, + { "save_file", (PyCFunction)Document_save_file, METH_KEYWORDS, + "Saves the document's file." }, + { "save_file_as", (PyCFunction)Document_save_file_as, METH_KEYWORDS, + "Saves the document with a new filename, detecting the filetype." }, + { NULL } +}; + + +static PyGetSetDef Document_getseters[] = { + GEANYPY_GETSETDEF(Document, "editor", + "The editor associated with the document."), + GEANYPY_GETSETDEF(Document, "encoding", + "The encoding of the document."), + GEANYPY_GETSETDEF(Document, "file_name", + "The document's filename."), + GEANYPY_GETSETDEF(Document, "file_type", + "The filetype for this document."), + GEANYPY_GETSETDEF(Document, "has_bom", + "Internally used flag to indicate whether the file of this document " + "has a byte-order-mark."), + GEANYPY_GETSETDEF(Document, "has_tags", + "Whether this document supports source code symbols (tags) to " + "show in the sidebar."), + GEANYPY_GETSETDEF(Document, "index", + "Index of the document."), + GEANYPY_GETSETDEF(Document, "is_valid", + "General flag to represent this document is active and all properties " + "are set correctly."), + GEANYPY_GETSETDEF(Document, "readonly", + "Whether this document is read-only."), + GEANYPY_GETSETDEF(Document, "real_path", + "The link-dereferenced filename of the document."), + GEANYPY_GETSETDEF(Document, "basename_for_display", + "Returns the basename of the document's file."), + GEANYPY_GETSETDEF(Document, "notebook_page", + "Gets the notebook page index for the document."), + GEANYPY_GETSETDEF(Document, "status_color", + "Gets the status color of the document or None for default."), + GEANYPY_GETSETDEF(Document, "text_changed", + "Whether the document has been changed since it was last saved."), + { NULL }, +}; + + + +static PyTypeObject DocumentType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.document.Document", /* tp_name */ + sizeof(Document), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) Document_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyDocument structure.",/* tp_doc */ + 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_iternext */ + Document_methods, /* tp_methods */ + 0, /* tp_members */ + Document_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) Document_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ + +}; + + +static PyObject* +Document_find_by_filename(PyObject *self, PyObject *args, PyObject *kwargs) +{ + gchar *fn; + GeanyDocument *doc; + static gchar *kwlist[] = { "filename", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &fn)) + { + doc = document_find_by_filename(fn); + if (DOC_VALID(doc)) + return (PyObject *) Document_create_new_from_geany_document(doc); + } + Py_RETURN_NONE; +} + + +static PyObject* +Document_find_by_real_path(PyObject *self, PyObject *args, PyObject *kwargs) +{ + gchar *fn; + GeanyDocument *doc; + static gchar *kwlist[] = { "real_path", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &fn)) + { + doc = document_find_by_real_path(fn); + if (DOC_VALID(doc)) + return (PyObject *) Document_create_new_from_geany_document(doc); + } + Py_RETURN_NONE; +} + + +static PyObject* +Document_get_current(PyObject *self) +{ + GeanyDocument *doc; + + doc = document_get_current(); + if (DOC_VALID(doc)) + return (PyObject *) Document_create_new_from_geany_document(doc); + Py_RETURN_NONE; +} + + +static PyObject* +Document_get_from_page(PyObject *self, PyObject *args, PyObject *kwargs) +{ + gint page_num; + GeanyDocument *doc; + static gchar *kwlist[] = { "page_num", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &page_num)) + { + doc = document_get_from_page(page_num); + if (DOC_VALID(doc)) + return (PyObject *) Document_create_new_from_geany_document(doc); + } + Py_RETURN_NONE; +} + + +static PyObject* +Document_get_from_index(PyObject *self, PyObject *args, PyObject *kwargs) +{ + gint idx; + GeanyDocument *doc; + static gchar *kwlist[] = { "index", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &idx)) + { + doc = document_index(idx); + if (DOC_VALID(doc)) + return (PyObject *) Document_create_new_from_geany_document(doc); + } + Py_RETURN_NONE; +} + + +static PyObject* +Document_new_file(PyObject *self, PyObject *args, PyObject *kwargs) +{ + gchar *filename = NULL, *initial_text = NULL; + Filetype *filetype = NULL; + PyObject *py_ft = NULL; + GeanyDocument *doc; + GeanyFiletype *ft = NULL; + static gchar *kwlist[] = { "filename", "filetype", "initial_text", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|zOz", kwlist, + &filename, &py_ft, &initial_text)) + { + if (py_ft != NULL && py_ft != Py_None) + { + filetype = (Filetype *) py_ft; + if (filetype->ft != NULL) + ft = filetype->ft; + } + doc = document_new_file(filename, ft, initial_text); + if (DOC_VALID(doc)) + return (PyObject *) Document_create_new_from_geany_document(doc); + } + Py_RETURN_NONE; +} + + +static PyObject* +Document_open_file(PyObject *self, PyObject *args, PyObject *kwargs) +{ + gint read_only = 0; + gchar *filename = NULL, *forced_encoding = NULL; + GeanyDocument *doc; + GeanyFiletype *ft = NULL; + Filetype *filetype = NULL; + PyObject *py_ft = NULL; + static gchar *kwlist[] = { "filename", "read_only", "filetype", + "forced_enc", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|iOz", kwlist, &filename, + &read_only, &py_ft, &forced_encoding)) + { + if (py_ft != NULL && py_ft != Py_None) + { + filetype = (Filetype *) py_ft; + if (filetype->ft != NULL) + ft = filetype->ft; + } + doc = document_open_file(filename, read_only, ft, forced_encoding); + if (DOC_VALID(doc)) + return (PyObject *) Document_create_new_from_geany_document(doc); + } + Py_RETURN_NONE; +} + + +static PyObject* +Document_remove_page(PyObject *self, PyObject *args, PyObject *kwargs) +{ + guint page_num; + static gchar *kwlist[] = { "page_num", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &page_num)) + { + if (document_remove_page(page_num)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + Py_RETURN_NONE; +} + + +static PyObject * +Document_get_documents_list(PyObject *module) +{ + guint i; + GeanyDocument *doc; + PyObject *list; + + list = PyList_New(0); + + for (i = 0; i < geany_data->documents_array->len; i++) + { + doc = g_ptr_array_index(geany_data->documents_array, i); + if (DOC_VALID(doc)) + { + PyList_Append(list, + (PyObject *) Document_create_new_from_geany_document(doc)); + } + } + + return list; +} + + +static +PyMethodDef DocumentModule_methods[] = { + { "find_by_filename", (PyCFunction) Document_find_by_filename, METH_KEYWORDS, + "Finds a document with the given filename." }, + { "find_by_real_path", (PyCFunction) Document_find_by_real_path, METH_KEYWORDS, + "Finds a document whose real path matches the given filename." }, + { "get_current", (PyCFunction) Document_get_current, METH_NOARGS, + "Finds the current document."}, + { "get_from_page", (PyCFunction) Document_get_from_page, METH_KEYWORDS, + "Finds the document for the given notebook page." }, + { "index", (PyCFunction) Document_get_from_index, METH_KEYWORDS, + "Finds the document with the given index." }, + { "new_file", (PyCFunction) Document_new_file, METH_KEYWORDS, + "Creates a new document." }, + { "open_file", (PyCFunction) Document_open_file, METH_KEYWORDS, + "Opens a new document specified by the given filename." }, + { "remove_page", (PyCFunction) Document_remove_page, METH_KEYWORDS, + "Removes the given notebook tab page and clears all related " + "information in the documents list." }, + { "get_documents_list", (PyCFunction) Document_get_documents_list, METH_NOARGS, + "Returns a sequence of valid documents." }, + { NULL } +}; + + +PyMODINIT_FUNC initdocument(void) +{ + PyObject *m; + + DocumentType.tp_new = PyType_GenericNew; + if (PyType_Ready(&DocumentType) < 0) + return; + + m = Py_InitModule3("document", DocumentModule_methods, + "Document information and management."); + + Py_INCREF(&DocumentType); + PyModule_AddObject(m, "Document", (PyObject *)&DocumentType); +} + + +Document *Document_create_new_from_geany_document(GeanyDocument *doc) +{ + Document *self; + self = (Document *) PyObject_CallObject((PyObject *) &DocumentType, NULL); + self->doc = doc; + return self; +} diff --git a/geanypy/src/geanypy-document.h b/geanypy/src/geanypy-document.h new file mode 100644 index 000000000..90d0991f6 --- /dev/null +++ b/geanypy/src/geanypy-document.h @@ -0,0 +1,12 @@ +#ifndef GEANYPY_DOCUMENT_H__ +#define GEANYPY_DOCUMENT_H__ + +typedef struct +{ + PyObject_HEAD + GeanyDocument *doc; +} Document; + +Document *Document_create_new_from_geany_document(GeanyDocument *doc); + +#endif /* GEANYPY_DOCUMENT_H__ */ diff --git a/geanypy/src/geanypy-editor.c b/geanypy/src/geanypy-editor.c new file mode 100644 index 000000000..1d8836a8d --- /dev/null +++ b/geanypy/src/geanypy-editor.c @@ -0,0 +1,466 @@ +#include "geanypy.h" + + +static void +Editor_dealloc(Editor *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + + +static int +Editor_init(Editor *self) +{ + self->editor = NULL; + return 0; +} + + +static PyObject * +Editor_get_property(Editor *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->editor) + { + PyErr_SetString(PyExc_RuntimeError, + "Editor instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "eol_char")) + return PyString_FromString(editor_get_eol_char(self->editor)); + else if (g_str_equal(prop_name, "eol_char_name")) + return PyString_FromString(editor_get_eol_char_name(self->editor)); + else if (g_str_equal(prop_name, "indent_prefs")) + { + const GeanyIndentPrefs *indent_prefs; + IndentPrefs *py_prefs; + indent_prefs = editor_get_indent_prefs(self->editor); + if (indent_prefs) + { + py_prefs = IndentPrefs_create_new_from_geany_indent_prefs( + (GeanyIndentPrefs *) indent_prefs); + return (PyObject *) py_prefs; + } + Py_RETURN_NONE; + } + else if (g_str_equal(prop_name, "auto_indent")) + { + if (self->editor->auto_indent) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "document")) + { + PyObject *py_doc; + py_doc = (PyObject *) Document_create_new_from_geany_document( + self->editor->document); + if (py_doc && py_doc != Py_None) + Py_RETURN_NONE; + return py_doc; + } + else if (g_str_equal(prop_name, "line_breaking")) + { + if (self->editor->line_breaking) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "line_wrapping")) + { + if (self->editor->line_wrapping) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "scintilla")) + { + Scintilla *sci; + sci = Scintilla_create_new_from_scintilla(self->editor->sci); + if (!sci) + Py_RETURN_NONE; + return (PyObject *) sci; + } + else if (g_str_equal(prop_name, "scroll_percent")) + return PyFloat_FromDouble((gdouble) self->editor->scroll_percent); + + PyErr_SetString(PyExc_AttributeError, "can't get property"); + Py_RETURN_NONE; +} + + +static int +Editor_set_property(Editor *self, PyObject *value, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, -1); + g_return_val_if_fail(value != NULL, -1); + g_return_val_if_fail(prop_name != NULL, -1); + + if (!self->editor) + { + PyErr_SetString(PyExc_RuntimeError, + "Editor instance not initialized properly"); + return -1; + } + + if (g_str_equal(prop_name, "indent_type")) + { + long indent_type = PyInt_AsLong(value); + if (indent_type == -1 && PyErr_Occurred()) + { + PyErr_Print(); + return -1; + } + editor_set_indent_type(self->editor, (GeanyIndentType) indent_type); + return 0; + } + + PyErr_SetString(PyExc_AttributeError, "can't set property"); + return -1; +} + + +static PyObject * +Editor_create_widget(Editor *self) +{ + ScintillaObject *sci; + PyObject *pysci; + + if (self->editor == NULL) + Py_RETURN_NONE; + + sci = editor_create_widget(self->editor); + if (!sci) + Py_RETURN_NONE; + + pysci = pygobject_new(G_OBJECT(sci)); + if (!pysci) + { + gtk_widget_destroy(GTK_WIDGET(sci)); + Py_RETURN_NONE; + } + + return pysci; +} + + +static PyObject * +Editor_find_snippet(Editor *self, PyObject *args, PyObject *kwargs) +{ + gchar *name; + const gchar *snippet; + static gchar *kwlist[] = { "snippet_name", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) + { + if (name != NULL) + { + snippet = editor_find_snippet(self->editor, name); + if (snippet != NULL) + return Py_BuildValue("s", snippet); + } + } + Py_RETURN_NONE; +} + + +static PyObject * +Editor_get_word_at_position(Editor *self, PyObject *args, PyObject *kwargs) +{ + gint pos = -1; + gchar *wordchars = NULL, *word = NULL; + PyObject *py_word; + static gchar *kwlist[] = { "pos", "wordchars", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|iz", kwlist, &pos, &wordchars)) + { + word = editor_get_word_at_pos(self->editor, pos, wordchars); + if (word != NULL) + { + py_word = Py_BuildValue("s", word); + g_free(word); + return py_word; + } + } + + Py_RETURN_NONE; +} + + +static PyObject * +Editor_goto_pos(Editor *self, PyObject *args, PyObject *kwargs) +{ + gint pos, mark = FALSE, result; + static gchar *kwlist[] = { "pos", "mark", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &pos, &mark)) + { + result = editor_goto_pos(self->editor, pos, mark); + if (result) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + Py_RETURN_NONE; +} + + +static PyObject * +Editor_indicator_clear(Editor *self, PyObject *args, PyObject *kwargs) +{ + gint indic; + static gchar *kwlist[] = { "indic", NULL }; + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &indic)) + editor_indicator_clear(self->editor, indic); + Py_RETURN_NONE; +} + + +static PyObject * +Editor_indicator_set_on_line(Editor *self, PyObject *args, PyObject *kwargs) +{ + gint indic, line_num; + static gchar *kwlist[] = { "indic", "line", NULL }; + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &indic, &line_num)) + editor_indicator_set_on_line(self->editor, indic, line_num); + Py_RETURN_NONE; +} + + +static PyObject * +Editor_indicator_set_on_range(Editor *self, PyObject *args, PyObject *kwargs) +{ + gint indic, start, end; + static gchar *kwlist[] = { "indic", "start", "end", NULL }; + if (PyArg_ParseTupleAndKeywords(args, kwargs, "iii", kwlist, &indic, &start, &end)) + editor_indicator_set_on_range(self->editor, indic, start, end); + Py_RETURN_NONE; +} + + +static PyObject * +Editor_insert_snippet(Editor *self, PyObject *args, PyObject *kwargs) +{ + gint pos = 0; + gchar *snippet = NULL; + static gchar *kwlist[] = { "pos", "snippet", NULL }; + if (PyArg_ParseTupleAndKeywords(args, kwargs, "is", kwlist, &pos, &snippet)) + editor_insert_snippet(self->editor, pos, snippet); + Py_RETURN_NONE; +} + + +static PyObject * +Editor_insert_text_block(Editor *self, PyObject *args, PyObject *kwargs) +{ + gchar *text = NULL; + gint insert_pos, cursor_index = -1, newline_indent_size = -1; + gint replace_newlines = 0; + static gchar *kwlist[] = { "text", "insert_pos", "cursor_index", + "newline_indent_size", "replace_newlines", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "si|iii", kwlist, &text, + &insert_pos, &cursor_index, &newline_indent_size, &replace_newlines)) + { + editor_insert_text_block(self->editor, text, insert_pos, cursor_index, + newline_indent_size, (gboolean) replace_newlines); + } + + Py_RETURN_NONE; +} + + +static PyMethodDef Editor_methods[] = { + { "create_widget", (PyCFunction) Editor_create_widget, METH_NOARGS, + "Creates a new Scintilla widget based on the settings for " + "editor." }, + { "find_snippet", (PyCFunction) Editor_find_snippet, METH_KEYWORDS, + "Get snippet by name." }, + { "get_word_at_position", (PyCFunction) Editor_get_word_at_position, METH_KEYWORDS, + "Finds the word at the position specified." }, + { "goto_pos", (PyCFunction) Editor_goto_pos, METH_KEYWORDS, + "Moves to position, switching to the current document if " + "necessary, setting a marker if mark is set." }, + { "indicator_clear", (PyCFunction) Editor_indicator_clear, METH_KEYWORDS, + "Deletes all currently set indicators matching the specified " + "indicator in the editor." }, + { "indicator_set_on_line", (PyCFunction) Editor_indicator_set_on_line, METH_KEYWORDS, + "Sets an indicator on the specified line." }, + { "indicator_set_on_range", (PyCFunction) Editor_indicator_set_on_range, METH_KEYWORDS, + "Sets an indicator on the range specified." }, + { "insert_snippet", (PyCFunction) Editor_insert_snippet, METH_KEYWORDS, + "Replces all special sequences in snippet and inserts it at " + "the specified position." }, + { "insert_text_block", (PyCFunction) Editor_insert_text_block, METH_KEYWORDS, + "Inserts text, replacing tab chars and newline chars accordingly " + "for the document." }, + { NULL } +}; + + +static PyGetSetDef Editor_getseters[] = { + GEANYPY_GETSETDEF(Editor, "eol_char", + "The endo of line character(s)."), + GEANYPY_GETSETDEF(Editor, "eol_char_name", + "The localized name (for displaying) of the used end of line " + "characters in the editor."), + GEANYPY_GETSETDEF(Editor, "indent_prefs", + "Editor's indentation preferences."), + GEANYPY_GETSETDEF(Editor, "indent_type", + "Sets the indent type for the editor."), + GEANYPY_GETSETDEF(Editor, "auto_indent", + "True if auto-indentation is enabled."), + GEANYPY_GETSETDEF(Editor, "document", + "The document associated with the editor."), + GEANYPY_GETSETDEF(Editor, "line_breaking", + "Whether to split long lines as you type."), + GEANYPY_GETSETDEF(Editor, "line_wrapping", + "True if line wrapping is enabled."), + GEANYPY_GETSETDEF(Editor, "scintilla", + "The Scintilla widget used by the editor."), + GEANYPY_GETSETDEF(Editor, "scroll_percent", + "Percentage to scroll view by on paint, if positive."), + { NULL }, +}; + + +static PyTypeObject EditorType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.editor.Editor", /* tp_name */ + sizeof(Editor), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Editor_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyEditor structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_iternext */ + Editor_methods, /* tp_methods */ + 0, /* tp_members */ + Editor_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc)Editor_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ + +}; + + +static PyObject * +Editor__find_snippet(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gchar *name; + const gchar *snippet; + static gchar *kwlist[] = { "snippet_name", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) + { + if (name != NULL) + { + snippet = editor_find_snippet(NULL, name); + if (snippet != NULL) + return Py_BuildValue("s", snippet); + } + } + Py_RETURN_NONE; +} + + +static PyObject * +Editor_get_default_eol_char(PyObject *module) +{ + const gchar *eol_char = editor_get_eol_char(NULL); + if (eol_char != NULL) + return Py_BuildValue("s", eol_char); + Py_RETURN_NONE; +} + + +static PyObject * +Editor_get_default_eol_char_name(PyObject *module) +{ + const gchar *eol_char_name = editor_get_eol_char_name(NULL); + if (eol_char_name != NULL) + return Py_BuildValue("s", eol_char_name); + Py_RETURN_NONE; +} + + +static PyObject * +Editor_get_default_indent_prefs(PyObject *module) +{ + const GeanyIndentPrefs *indent_prefs; + IndentPrefs *py_prefs; + indent_prefs = editor_get_indent_prefs(NULL); + if (indent_prefs != NULL) + { + py_prefs = IndentPrefs_create_new_from_geany_indent_prefs((GeanyIndentPrefs *)indent_prefs); + return (PyObject *) py_prefs; + } + Py_RETURN_NONE; +} + + +static +PyMethodDef EditorModule_methods[] = { + { "find_snippet", (PyCFunction) Editor__find_snippet, METH_VARARGS, + "Gets a snippet by name from the default set." }, + { "get_default_eol_char", (PyCFunction) Editor_get_default_eol_char, METH_NOARGS, + "The default eol char." }, + { "get_default_eol_char_name", (PyCFunction) Editor_get_default_eol_char_name, METH_NOARGS, + "Retrieves the localized name (for displaying) of the default end " + "of line characters." }, + { "get_default_indent_prefs", (PyCFunction) Editor_get_default_indent_prefs, METH_NOARGS, + "Gets the default indentation preferences." }, + { NULL } +}; + + +PyMODINIT_FUNC initeditor(void) +{ + PyObject *m; + + EditorType.tp_new = PyType_GenericNew; + if (PyType_Ready(&EditorType) < 0) + return; + + IndentPrefsType.tp_new = PyType_GenericNew; + if (PyType_Ready(&IndentPrefsType) < 0) + return; + + m = Py_InitModule3("editor", EditorModule_methods, + "Editor information and management."); + + Py_INCREF(&EditorType); + PyModule_AddObject(m, "Editor", (PyObject *)&EditorType); + + Py_INCREF(&IndentPrefsType); + PyModule_AddObject(m, "IndentPrefs", (PyObject *)&IndentPrefsType); + + PyModule_AddIntConstant(m, "INDENT_TYPE_SPACES", + (glong) GEANY_INDENT_TYPE_SPACES); + PyModule_AddIntConstant(m, "INDENT_TYPE_TABS", + (glong) GEANY_INDENT_TYPE_TABS); + PyModule_AddIntConstant(m, "INDENT_TYPE_BOTH", + (glong) GEANY_INDENT_TYPE_BOTH); + PyModule_AddIntConstant(m, "INDICATOR_ERROR", + (glong) GEANY_INDICATOR_ERROR); + PyModule_AddIntConstant(m, "INDICATOR_SEARCH", + (glong) GEANY_INDICATOR_SEARCH); + PyModule_AddStringConstant(m, "WORDCHARS", GEANY_WORDCHARS); + + PyModule_AddIntConstant(m, "INDENT_TYPE_SPACES", (glong) GEANY_INDENT_TYPE_SPACES); + PyModule_AddIntConstant(m, "INDENT_TYPE_TABS", (glong) GEANY_INDENT_TYPE_TABS); + PyModule_AddIntConstant(m, "INDENT_TYPE_BOTH", (glong) GEANY_INDENT_TYPE_BOTH); +} + + +Editor *Editor_create_new_from_geany_editor(GeanyEditor *editor) +{ + Editor *self; + self = (Editor *) PyObject_CallObject((PyObject *) &EditorType, NULL); + self->editor = editor; + return self; +} diff --git a/geanypy/src/geanypy-editor.h b/geanypy/src/geanypy-editor.h new file mode 100644 index 000000000..d148ee8bb --- /dev/null +++ b/geanypy/src/geanypy-editor.h @@ -0,0 +1,21 @@ +#ifndef GEANYPY_EDITOR_H__ +#define GEANYPY_EDITOR_H__ + +PyTypeObject IndentPrefsType; + +typedef struct +{ + PyObject_HEAD + GeanyEditor *editor; +} Editor; + +typedef struct +{ + PyObject_HEAD + GeanyIndentPrefs *indent_prefs; +} IndentPrefs; + +Editor *Editor_create_new_from_geany_editor(GeanyEditor *editor); +IndentPrefs *IndentPrefs_create_new_from_geany_indent_prefs(GeanyIndentPrefs *indent_prefs); + +#endif /* GEANYPY_EDITOR_H__ */ diff --git a/geanypy/src/geanypy-encoding.c b/geanypy/src/geanypy-encoding.c new file mode 100644 index 000000000..8ffa5fa25 --- /dev/null +++ b/geanypy/src/geanypy-encoding.c @@ -0,0 +1,310 @@ +#include "geanypy.h" + + +static void +Encoding_dealloc(Encoding *self) +{ + g_return_if_fail(self != NULL); + self->ob_type->tp_free((PyObject *) self); +} + + +static int +Encoding_init(Encoding *self) +{ + g_return_val_if_fail(self != NULL, -1); + self->encoding = NULL; + return 0; +} + + +static PyObject * +Encoding_get_property(Encoding *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->encoding) + { + PyErr_SetString(PyExc_RuntimeError, + "Encoding instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "charset") && self->encoding->charset) + return PyString_FromString(self->encoding->charset); + else if (g_str_equal(prop_name, "group")) + return PyInt_FromLong((glong) self->encoding->group); + else if (g_str_equal(prop_name, "idx")) + return PyInt_FromLong((glong) self->encoding->idx); + else if (g_str_equal(prop_name, "name") && self->encoding->name) + return PyString_FromString(self->encoding->name); + else if (g_str_equal(prop_name, "order")) + return PyInt_FromLong((glong) self->encoding->order); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(Encoding); + + +static PyGetSetDef Encoding_getseters[] = { + GEANYPY_GETSETDEF(Encoding, "charset", + "String representation of the encoding, ex. 'ISO-8859-3'."), + GEANYPY_GETSETDEF(Encoding, "group", + "Internally used member for grouping (see encoding.GROUP_* constants)."), + GEANYPY_GETSETDEF(Encoding, "idx", + "The index of the encoding, (see encoding.* constants, not encoding.GROUP_*)."), + GEANYPY_GETSETDEF(Encoding, "name", + "Translatable and descriptive name of the encoding, ex 'South European'."), + GEANYPY_GETSETDEF(Encoding, "order", + "Internally used member for grouping."), + { NULL } +}; + + +PyTypeObject EncodingType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.encoding.Encoding", /* tp_name */ + sizeof(Encoding), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) Encoding_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyEncoding structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + Encoding_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) Encoding_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + + +Encoding *Encoding_create_new_from_geany_encoding(GeanyEncoding *enc) +{ + Encoding *self; + self = (Encoding *) PyObject_CallObject((PyObject *) &EncodingType, NULL); + self->encoding = enc; + return self; +} + + +static PyObject * +Encodings_convert_to_utf8(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gchar *buffer = NULL, *used_encoding = NULL, *new_buffer = NULL; + gssize size = -1; /* bug alert: this is gsize in Geany for some reason */ + PyObject *result; + static gchar *kwlist[] = { "buffer", "size", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|l", kwlist, &buffer, &size)) + { + new_buffer = encodings_convert_to_utf8(buffer, size, &used_encoding); + if (new_buffer != NULL) + { + result = Py_BuildValue("ss", new_buffer, used_encoding); + g_free(new_buffer); + g_free(used_encoding); + return result; + } + } + + Py_RETURN_NONE; +} + + +static PyObject * +Encodings_convert_to_utf8_from_charset(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gchar *buffer = NULL, *charset = NULL, *new_buffer = NULL; + gssize size = -1; + gint fast = 0; + PyObject *result; + static gchar *kwlist[] = { "buffer", "size", "charset", "fast", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ss|li", kwlist, &buffer, + &charset, &size, &fast)) + { + new_buffer = encodings_convert_to_utf8_from_charset(buffer, size, charset, fast); + if (new_buffer != NULL) + { + result = Py_BuildValue("s", new_buffer); + g_free(new_buffer); + return result; + } + } + + Py_RETURN_NONE; +} + + +static PyObject * +Encodings_get_charset_from_index(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint idx = 0; + const gchar *charset = NULL; + static gchar *kwlist[] = { "index", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &idx)) + { + charset = encodings_get_charset_from_index(idx); + if (charset != NULL) + return Py_BuildValue("s", charset); + } + + Py_RETURN_NONE; +} + + +static const gchar *encoding_groups[] = { + "GROUP_NONE", + "GROUP_WEST_EUROPEAN", + "GROUP_EAST_EUROPEAN", + "GROUP_EAST_ASIAN", + "GROUP_ASIAN", + "GROUP_MIDDLE_EASTERN", + "GROUP_UNICODE" +}; + + +static const gchar *encoding_names[] = { + "ISO_8859_1", + "ISO_8859_2", + "ISO_8859_3", + "ISO_8859_4", + "ISO_8859_5", + "ISO_8859_6", + "ISO_8859_7", + "ISO_8859_8", + "ISO_8859_8_I", + "ISO_8859_9", + "ISO_8859_10", + "ISO_8859_13", + "ISO_8859_14", + "ISO_8859_15", + "ISO_8859_16", + + "UTF_7", + "UTF_8", + "UTF_16LE", + "UTF_16BE", + "UCS_2LE", + "UCS_2BE", + "UTF_32LE", + "UTF_32BE", + + "ARMSCII_8", + "BIG5", + "BIG5_HKSCS", + "CP_866", + + "EUC_JP", + "EUC_KR", + "EUC_TW", + + "GB18030", + "GB2312", + "GBK", + "GEOSTD8", + "HZ", + + "IBM_850", + "IBM_852", + "IBM_855", + "IBM_857", + "IBM_862", + "IBM_864", + + "ISO_2022_JP", + "ISO_2022_KR", + "ISO_IR_111", + "JOHAB", + "KOI8_R", + "KOI8_U", + + "SHIFT_JIS", + "TCVN", + "TIS_620", + "UHC", + "VISCII", + + "WINDOWS_1250", + "WINDOWS_1251", + "WINDOWS_1252", + "WINDOWS_1253", + "WINDOWS_1254", + "WINDOWS_1255", + "WINDOWS_1256", + "WINDOWS_1257", + "WINDOWS_1258", + + "NONE", + "CP_932", + + "MAX" +}; + + +static PyObject * +Encodings_get_list(PyObject *module) +{ + int i; + PyObject *list; + list = PyList_New(0); + for (i = 0; i < GEANY_ENCODINGS_MAX; i++) + PyList_Append(list, PyString_FromString(encoding_names[i])); + return list; +} + + +static +PyMethodDef EncodingsModule_methods[] = { + { + "convert_to_utf8", + (PyCFunction)Encodings_convert_to_utf8, METH_KEYWORDS, + "Tries to convert the supplied buffer to UTF-8 encoding. Returns " + "the converted buffer and the encoding that was used." + }, + { + "convert_to_utf8_from_charset", + (PyCFunction)Encodings_convert_to_utf8_from_charset, METH_KEYWORDS, + "Tries to convert the supplied buffer to UTF-8 from the supplied " + "charset. If the fast parameter is not False (default), additional " + "checks to validate the converted string are performed." + }, + { + "get_charset_from_index", + (PyCFunction)Encodings_get_charset_from_index, METH_KEYWORDS, + "Gets the character set name of the specified index." + }, + { + "get_list", + (PyCFunction) Encodings_get_list, METH_NOARGS, + "Gets a list of all supported encodings." + }, + { NULL } +}; + + +PyMODINIT_FUNC +initencoding(void) +{ + int i; + PyObject *m; + + EncodingType.tp_new = PyType_GenericNew; + if (PyType_Ready(&EncodingType) < 0) + return; + + m = Py_InitModule3("encoding", EncodingsModule_methods, + "Encoding conversion functions."); + + Py_INCREF(&EncodingType); + PyModule_AddObject(m, "Encoding", (PyObject *) &EncodingType); + + for (i = 0; i < GEANY_ENCODINGS_MAX; i++) + PyModule_AddIntConstant(m, encoding_names[i], (glong) i); + + for (i = 0; i < GEANY_ENCODING_GROUPS_MAX; i++) + PyModule_AddIntConstant(m, encoding_groups[i], (glong) i); +} diff --git a/geanypy/src/geanypy-encoding.h b/geanypy/src/geanypy-encoding.h new file mode 100644 index 000000000..1c586c16a --- /dev/null +++ b/geanypy/src/geanypy-encoding.h @@ -0,0 +1,14 @@ +#ifndef GEANYPY_ENCODING_H__ +#define GEANYPY_ENCODING_H__ + +PyTypeObject EncodingType; + +typedef struct +{ + PyObject_HEAD + GeanyEncoding *encoding; +} Encoding; + +Encoding *Encoding_create_new_from_geany_encoding(GeanyEncoding *enc); + +#endif /* GEANYPY_ENCODING_H__ */ diff --git a/geanypy/src/geanypy-filetypes.c b/geanypy/src/geanypy-filetypes.c new file mode 100644 index 000000000..79c28a551 --- /dev/null +++ b/geanypy/src/geanypy-filetypes.c @@ -0,0 +1,278 @@ +#include "geanypy.h" + + +static void +Filetype_dealloc(Filetype *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + + +static int +Filetype_init(Filetype *self, PyObject *args, PyObject *kwds) +{ + self->ft = NULL; + return 0; +} + + +static PyObject * +Filetype_get_property(Filetype *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->ft) + { + PyErr_SetString(PyExc_RuntimeError, + "Filetype instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "display_name")) + GEANYPY_RETURN_STRING(filetypes_get_display_name(self->ft)) + else if (g_str_equal(prop_name, "extension")) + GEANYPY_RETURN_STRING(self->ft->extension) + else if (g_str_equal(prop_name, "id")) + return PyInt_FromLong((glong) self->ft->id); + else if (g_str_equal(prop_name, "lang")) + return PyInt_FromLong((glong) self->ft->lang); + else if (g_str_equal(prop_name, "name")) + GEANYPY_RETURN_STRING(self->ft->name) + else if (g_str_equal(prop_name, "pattern")) + { + gint i, len; + PyObject *list = PyList_New(0); + if (self->ft->pattern) + { + len = g_strv_length(self->ft->pattern); + for (i = 0; i < len; i++) + PyList_Append(list, PyString_FromString(self->ft->pattern[i])); + } + return list; + + } + else if (g_str_equal(prop_name, "title")) + GEANYPY_RETURN_STRING(self->ft->title) +#ifdef ENABLE_PRIVATE + else if (g_str_equal(prop_name, "context_action_cmd")) + GEANYPY_RETURN_STRING(self->ft->context_action_cmd) + else if (g_str_equal(prop_name, "comment_open")) + GEANYPY_RETURN_STRING(self->ft->comment_open) + else if (g_str_equal(prop_name, "comment_use_indent")) + { + if (self->ft->comment_use_indent) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "group")) + return PyInt_FromLong((glong) self->ft->group); + else if (g_str_equal(prop_name, "error_regex_string")) + return PyString_FromString(self->ft->error_regex_string); + else if (g_str_equal(prop_name, "lexer_filetype")) + { + if (self->ft->lexer_filetype) + return Filetype_create_new_from_geany_filetype(self->ft->lexer_filetype); + } + else if (g_str_equal(prop_name, "mime_type")) + GEANYPY_RETURN_STRING(self->ft->lexer_filetype) + else if (g_str_equal(prop_name, "icon")) + { + if (self->ft->icon) + return (PyObject *) pygobject_new(G_OBJECT(self->ft->icon)); + } + else if (g_str_equal(prop_name, "comment_single")) + GEANYPY_RETURN_STRING(self->ft->comment_single) + else if (g_str_equal(prop_name, "indent_type")) + return PyInt_FromLong((glong) self->ft->indent_type); + else if (g_str_equal(prop_name, "indent_width")) + return PyInt_FromLong((glong) self->ft->indent_width); +#endif + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(Filetype); + + +static PyGetSetDef Filetype_getseters[] = { + GEANYPY_GETSETDEF(Filetype, "display_name", + "Gets the filetype's name for display."), + GEANYPY_GETSETDEF(Filetype, "extension", + "Default file extension for new files or None."), + GEANYPY_GETSETDEF(Filetype, "id", + "Index of the the filetype."), + GEANYPY_GETSETDEF(Filetype, "lang", + "TagManager language type, -1 for all, -2 for none."), + GEANYPY_GETSETDEF(Filetype, "name", + "Untranslated short name, such as 'C' or 'None'."), + GEANYPY_GETSETDEF(Filetype, "pattern", + "List of filename-matching wildcard strings."), + GEANYPY_GETSETDEF(Filetype, "title", + "Shown in the open dialog, such as 'C source file'."), +#ifdef ENABLE_PRIVATE + GEANYPY_GETSETDEF(Filetype, "context_action_cmd", ""), + GEANYPY_GETSETDEF(Filetype, "comment_open", ""), + GEANYPY_GETSETDEF(Filetype, "comment_use_indent", ""), + GEANYPY_GETSETDEF(Filetype, "group", ""), + GEANYPY_GETSETDEF(Filetype, "error_regex_string", ""), + GEANYPY_GETSETDEF(Filetype, "lexer_filetype", ""), + GEANYPY_GETSETDEF(Filetype, "mime_type", ""), + GEANYPY_GETSETDEF(Filetype, "icon", ""), + GEANYPY_GETSETDEF(Filetype, "comment_single", ""), + GEANYPY_GETSETDEF(Filetype, "indent_type", ""), + GEANYPY_GETSETDEF(Filetype, "indent_width", ""), +#endif + { NULL } +}; + + +static PyTypeObject FiletypeType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.filetypes.Filetype", /* tp_name */ + sizeof(Filetype), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) Filetype_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyFiletype structure.",/* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + Filetype_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) Filetype_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ + +}; + + +static PyObject * +Filetype_detect_from_file(PyObject *self, PyObject *args, PyObject *kwargs) +{ + GeanyFiletype *ft; + gchar *filename = NULL; + static gchar *kwlist[] = { "filename", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename)) + { + if (filename) + { + if ((ft = filetypes_detect_from_file(filename))) + return (PyObject *) Filetype_create_new_from_geany_filetype(ft); + } + } + + Py_RETURN_NONE; +} + + +static PyObject * +Filetype_get_sorted_by_name(PyObject *self) +{ + const GSList *glist, *iter; + PyObject *list; + + glist = filetypes_get_sorted_by_name(); + list = PyList_New(0); + + for (iter = glist; iter != NULL; iter = g_slist_next(iter)) + { + if (!iter->data) + continue; + PyList_Append(list, (PyObject *) + Filetype_create_new_from_geany_filetype((GeanyFiletype *) iter->data)); + } + + return list; +} + + +static PyObject * +Filetype_index(PyObject *self, PyObject *args, PyObject *kwargs) +{ + GeanyFiletype *ft; + gint idx = -1; + static gchar *kwlist[] = { "index", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &idx)) + { + if ((ft = filetypes_index(idx))) + return (PyObject *) Filetype_create_new_from_geany_filetype(ft); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Filetype_lookup_by_name(PyObject *self, PyObject *args, PyObject *kwargs) +{ + GeanyFiletype *ft; + gchar *filetype = NULL; + static gchar *kwlist[] = { "name", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filetype)) + { + if (filetype && (ft = filetypes_lookup_by_name(filetype))) + return (PyObject *) Filetype_create_new_from_geany_filetype(ft); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Filetype_get_sorted_by_title(PyObject *self, PyObject *args) +{ + const GSList *iter; + PyObject *list; + + list = PyList_New(0); + + for (iter = geany_data->filetypes_by_title; iter != NULL; iter = g_slist_next(iter)) + { + if (!iter->data) + continue; + PyList_Append(list, (PyObject *) + Filetype_create_new_from_geany_filetype((GeanyFiletype *) iter->data)); + } + + return list; +} + + +static +PyMethodDef FiletypeModule_methods[] = { + { "detect_from_file", (PyCFunction) Filetype_detect_from_file, METH_KEYWORDS }, + { "index", (PyCFunction) Filetype_index, METH_KEYWORDS }, + { "get_sorted_by_name", (PyCFunction) Filetype_get_sorted_by_name, METH_NOARGS }, + { "lookup_by_name", (PyCFunction) Filetype_lookup_by_name, METH_KEYWORDS }, + { "get_sorted_by_title", (PyCFunction) Filetype_get_sorted_by_title, METH_NOARGS }, + { NULL } +}; + + +PyMODINIT_FUNC +initfiletypes(void) +{ + PyObject *m; + + FiletypeType.tp_new = PyType_GenericNew; + if (PyType_Ready(&FiletypeType) < 0) + return; + + m = Py_InitModule3("filetypes", FiletypeModule_methods, + "Filetype information and management."); + + Py_INCREF(&FiletypeType); + PyModule_AddObject(m, "Filetype", (PyObject *)&FiletypeType); +} + + +Filetype *Filetype_create_new_from_geany_filetype(GeanyFiletype *ft) +{ + Filetype *self; + self = (Filetype *) PyObject_CallObject((PyObject *) &FiletypeType, NULL); + self->ft = ft; + return self; +} diff --git a/geanypy/src/geanypy-filetypes.h b/geanypy/src/geanypy-filetypes.h new file mode 100644 index 000000000..3027bfa03 --- /dev/null +++ b/geanypy/src/geanypy-filetypes.h @@ -0,0 +1,12 @@ +#ifndef GEANYPY_FILETYPES_H__ +#define GEANYPY_FILETYPES_H__ + +typedef struct +{ + PyObject_HEAD + GeanyFiletype *ft; +} Filetype; + +Filetype *Filetype_create_new_from_geany_filetype(GeanyFiletype *ft); + +#endif /* GEANYPY_FILETYPES_H__ */ diff --git a/geanypy/src/geanypy-highlighting.c b/geanypy/src/geanypy-highlighting.c new file mode 100644 index 000000000..330201848 --- /dev/null +++ b/geanypy/src/geanypy-highlighting.c @@ -0,0 +1,230 @@ +#include "geanypy.h" + + +typedef struct +{ + PyObject_HEAD + const GeanyLexerStyle *lexer_style; +} LexerStyle; + + +static void +LexerStyle_dealloc(LexerStyle *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + + +static int +LexerStyle_init(LexerStyle *self, PyObject *args, PyObject *kwds) +{ + self->lexer_style = NULL; + return 0; +} + + +static PyObject * +LexerStyle_get_property(LexerStyle *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->lexer_style) + { + PyErr_SetString(PyExc_RuntimeError, + "LexerStyle instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "background")) + { + guint16 red, green, blue; + red = self->lexer_style->background & 255; + green = (self->lexer_style->background >> 8) & 255; + blue = (self->lexer_style->background >> 16) & 255; + return Py_BuildValue("iii", red, green, blue); + } + else if (g_str_equal(prop_name, "foreground")) + { + guint16 red, green, blue; + red = self->lexer_style->foreground & 255; + green = (self->lexer_style->foreground >> 8) & 255; + blue = (self->lexer_style->foreground >> 16) & 255; + return Py_BuildValue("iii", red, green, blue); + } + else if (g_str_equal(prop_name, "bold")) + { + if (self->lexer_style->bold) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + else if (g_str_equal(prop_name, "italic")) + { + if (self->lexer_style->italic) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(LexerStyle); + + +static PyGetSetDef LexerStyle_getseters[] = { + GEANYPY_GETSETDEF(LexerStyle, "background", + "Background color of text, as an (R,G,B) tuple."), + GEANYPY_GETSETDEF(LexerStyle, "foreground", + "Foreground color of text, as an (R,G,B) tuple."), + GEANYPY_GETSETDEF(LexerStyle, "bold", + "Whether the text is bold or not."), + GEANYPY_GETSETDEF(LexerStyle, "italic", + "Whether the text is italic or not."), + { NULL } +}; + + +static PyTypeObject LexerStyleType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "geany.highlighting.LexerStyle", /*tp_name*/ + sizeof(Editor), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor) LexerStyle_dealloc, /*tp_dealloc*/ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "Wrapper around a GeanyLexerStyle structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + LexerStyle_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) LexerStyle_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + + +static PyObject * +Highlighting_get_style(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint ft_id, style_id; + LexerStyle *lexer_style; + const GeanyLexerStyle *ls; + static gchar *kwlist[] = { "filetype_id", "style_id", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &ft_id, &style_id)) + { + ls = highlighting_get_style(ft_id, style_id); + if (ls != NULL) + { + lexer_style = (LexerStyle *) PyObject_CallObject((PyObject *) &LexerStyleType, NULL); + lexer_style->lexer_style = ls; + return (PyObject *) lexer_style; + } + } + + Py_RETURN_NONE; +} + + +static PyObject * +Highlighting_is_code_style(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint lexer, style; + static gchar *kwlist[] = { "lexer", "style", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &lexer, &style)) + { + if (highlighting_is_code_style(lexer, style)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} + + +static PyObject * +Highlighting_is_comment_style(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint lexer, style; + static gchar *kwlist[] = { "lexer", "style", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &lexer, &style)) + { + if (highlighting_is_comment_style(lexer, style)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} + + +static PyObject * +Highlighting_is_string_style(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint lexer, style; + static gchar *kwlist[] = { "lexer", "style", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &lexer, &style)) + { + if (highlighting_is_string_style(lexer, style)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} + + +static PyObject * +Highlighting_set_styles(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *py_sci, *py_ft; + Scintilla *sci; + Filetype *ft; + static gchar *kwlist[] = { "sci", "filetype", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwlist, &py_sci, &py_ft)) + { + if (py_sci != Py_None && py_ft != Py_None) + { + sci = (Scintilla *) py_sci; + ft = (Filetype *) py_ft; + highlighting_set_styles(sci->sci, ft->ft); + } + } + + Py_RETURN_NONE; +} + + +static +PyMethodDef EditorModule_methods[] = { + { "get_style", (PyCFunction) Highlighting_get_style, METH_KEYWORDS }, + { "is_code_style", (PyCFunction) Highlighting_is_code_style, METH_KEYWORDS }, + { "is_comment_style", (PyCFunction) Highlighting_is_comment_style, METH_KEYWORDS }, + { "is_string_style", (PyCFunction) Highlighting_is_string_style, METH_KEYWORDS }, + { "set_styles", (PyCFunction) Highlighting_set_styles, METH_KEYWORDS }, + { NULL } +}; + + +PyMODINIT_FUNC +inithighlighting(void) +{ + PyObject *m; + + LexerStyleType.tp_new = PyType_GenericNew; + if (PyType_Ready(&LexerStyleType) < 0) + return; + + m = Py_InitModule3("highlighting", EditorModule_methods, + "Highlighting information and management."); + + Py_INCREF(&LexerStyleType); + PyModule_AddObject(m, "LexerStyle", (PyObject *)&LexerStyleType); +} diff --git a/geanypy/src/geanypy-indentprefs.c b/geanypy/src/geanypy-indentprefs.c new file mode 100644 index 000000000..776ffd1bf --- /dev/null +++ b/geanypy/src/geanypy-indentprefs.c @@ -0,0 +1,78 @@ +#include "geanypy.h" + + +static void +IndentPrefs_dealloc(IndentPrefs *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + + +static int +IndentPrefs_init(IndentPrefs *self, PyObject *args, PyObject *kwds) +{ + self->indent_prefs = NULL; + return 0; +} + + +static PyObject * +IndentPrefs_get_property(IndentPrefs *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->indent_prefs) + { + PyErr_SetString(PyExc_RuntimeError, + "IndentPrefs instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "width")) + return PyInt_FromLong((glong) self->indent_prefs->width); + else if (g_str_equal(prop_name, "type")) + return PyInt_FromLong((glong) self->indent_prefs->type); + else if (g_str_equal(prop_name, "hard_tab_width")) + return PyInt_FromLong((glong) self->indent_prefs->hard_tab_width); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(IndentPrefs); + + +static PyGetSetDef IndentPrefs_getseters[] = { + GEANYPY_GETSETDEF(IndentPrefs, "width", "Indent width in characters."), + GEANYPY_GETSETDEF(IndentPrefs, "type", + "Whether to use tabs, spaces, or both to indent."), + GEANYPY_GETSETDEF(IndentPrefs, "hard_tab_width", + "Width of a tab, but only when using INDENT_TYPE_BOTH."), + { NULL } +}; + + +PyTypeObject IndentPrefsType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.editor.IndentPrefs", /* tp_name */ + sizeof(IndentPrefs), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) IndentPrefs_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyIndentPrefs structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + IndentPrefs_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) IndentPrefs_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + + +IndentPrefs *IndentPrefs_create_new_from_geany_indent_prefs(GeanyIndentPrefs *indent_prefs) +{ + IndentPrefs *self; + self = (IndentPrefs *) PyObject_CallObject((PyObject *) &IndentPrefsType, NULL); + self->indent_prefs = indent_prefs; + return self; +} diff --git a/geanypy/src/geanypy-interfaceprefs.c b/geanypy/src/geanypy-interfaceprefs.c new file mode 100644 index 000000000..3cd0f1767 --- /dev/null +++ b/geanypy/src/geanypy-interfaceprefs.c @@ -0,0 +1,150 @@ +#include "geanypy.h" + + +#define RET_BOOL(memb) \ + { \ + if (memb) \ + Py_RETURN_TRUE; \ + else \ + Py_RETURN_FALSE; \ + } + + +static void +InterfacePrefs_dealloc(InterfacePrefs *self) +{ + g_return_if_fail(self != NULL); + self->ob_type->tp_free((PyObject *) self); +} + + +static int +InterfacePrefs_init(InterfacePrefs *self) +{ + g_return_val_if_fail(self != NULL, -1); + self->iface_prefs = geany_data->interface_prefs; + return 0; +} + + +static PyObject * +InterfacePrefs_get_property(InterfacePrefs *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->iface_prefs) + { + PyErr_SetString(PyExc_RuntimeError, + "InterfacePrefs instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "compiler_tab_autoscroll")) + RET_BOOL(self->iface_prefs->compiler_tab_autoscroll) + else if (g_str_equal(prop_name, "editor_font")) + return PyString_FromString(self->iface_prefs->editor_font); + else if (g_str_equal(prop_name, "highlighting_invert_all")) + RET_BOOL(self->iface_prefs->highlighting_invert_all) + else if (g_str_equal(prop_name, "msgwin_compiler_visible")) + RET_BOOL(self->iface_prefs->msgwin_compiler_visible) + else if (g_str_equal(prop_name, "msgwin_font")) + return PyString_FromString(self->iface_prefs->msgwin_font); + else if (g_str_equal(prop_name, "msgwin_messages_visible")) + RET_BOOL(self->iface_prefs->msgwin_messages_visible) + else if (g_str_equal(prop_name, "msgwin_scribble_visible")) + RET_BOOL(self->iface_prefs->msgwin_scribble_visible) + else if (g_str_equal(prop_name, "msgwin_status_visible")) + RET_BOOL(self->iface_prefs->msgwin_status_visible) + else if (g_str_equal(prop_name, "notebook_double_click_hides_widgets")) + RET_BOOL(self->iface_prefs->notebook_double_click_hides_widgets) + else if (g_str_equal(prop_name, "show_notebook_tabs")) + RET_BOOL(self->iface_prefs->show_notebook_tabs) + else if (g_str_equal(prop_name, "show_symbol_list_expanders")) + RET_BOOL(self->iface_prefs->show_symbol_list_expanders) + else if (g_str_equal(prop_name, "sidebar_openfiles_visible")) + RET_BOOL(self->iface_prefs->sidebar_openfiles_visible) + else if (g_str_equal(prop_name, "sidebar_pos")) + return PyInt_FromLong((glong) self->iface_prefs->sidebar_pos); + else if (g_str_equal(prop_name, "sidebar_symbol_visible")) + RET_BOOL(self->iface_prefs->sidebar_symbol_visible) + else if (g_str_equal(prop_name, "statusbar_visible")) + RET_BOOL(self->iface_prefs->statusbar_visible) + else if (g_str_equal(prop_name, "tab_pos_editor")) + return PyInt_FromLong((glong) self->iface_prefs->tab_pos_editor); + else if (g_str_equal(prop_name, "tab_pos_msgwin")) + return PyInt_FromLong((glong) self->iface_prefs->tab_pos_msgwin); + else if (g_str_equal(prop_name, "tab_pos_sidebar")) + return PyInt_FromLong((glong) self->iface_prefs->tab_pos_sidebar); + else if (g_str_equal(prop_name, "tagbar_font")) + return PyString_FromString(self->iface_prefs->tagbar_font); + else if (g_str_equal(prop_name, "use_native_windows_dialogs")) + RET_BOOL(self->iface_prefs->use_native_windows_dialogs) + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(InterfacePrefs); + + +static PyGetSetDef InterfacePrefs_getseters[] = { + GEANYPY_GETSETDEF(InterfacePrefs, "compiler_tab_autoscroll", + "Wether compiler messages window is automatically scrolled to " + "show new messages."), + GEANYPY_GETSETDEF(InterfacePrefs, "editor_font", + "Font used in the editor window."), + GEANYPY_GETSETDEF(InterfacePrefs, "highlighting_invert_all", + "Whether highlighting colors are inverted."), + GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_compiler_visible", + "Wether message window's compiler tab is visible."), + GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_font", + "Font used in the message window."), + GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_messages_visible", + "Whether message window's messages tab is visible."), + GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_scribble_visible", + "Whether message window's scribble tab is visible."), + GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_status_visible", + "Whether message window's status tab is visible."), + GEANYPY_GETSETDEF(InterfacePrefs, "notebook_double_click_hides_widgets", + "Whether a double-click on the notebook tabs hides all other windows."), + GEANYPY_GETSETDEF(InterfacePrefs, "show_notebook_tabs", + "Whether editor tabs are visible."), + GEANYPY_GETSETDEF(InterfacePrefs, "show_symbol_list_expanders", + "Whether to show expanders in the symbol list."), + GEANYPY_GETSETDEF(InterfacePrefs, "sidebar_openfiles_visible", + "Whether the open files list is visible."), + GEANYPY_GETSETDEF(InterfacePrefs, "sidebar_pos", + "Position of the sidebar (left or right)."), + GEANYPY_GETSETDEF(InterfacePrefs, "sidebar_symbol_visible", + "Whether the status bar is visible."), + GEANYPY_GETSETDEF(InterfacePrefs, "statusbar_visible", + "Whether the statusbar is visible."), + GEANYPY_GETSETDEF(InterfacePrefs, "tab_pos_editor", + "Position of the editor's tabs."), + GEANYPY_GETSETDEF(InterfacePrefs, "tab_pos_msgwin", + "Position of the message window tabs."), + GEANYPY_GETSETDEF(InterfacePrefs, "tab_pos_sidebar", + "Position of the sidebar's tabs."), + GEANYPY_GETSETDEF(InterfacePrefs, "tagbar_font", + "Symbol sidebar font."), + GEANYPY_GETSETDEF(InterfacePrefs, "use_native_windows_dialogs", + "Whether to use native Windows dialogs (only on Win32)."), + { NULL } +}; + + +PyTypeObject InterfacePrefsType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.ui_utils.InterfacePrefs", /* tp_name */ + sizeof(InterfacePrefs), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) InterfacePrefs_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyInterfacePrefs structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + InterfacePrefs_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) InterfacePrefs_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; diff --git a/geanypy/src/geanypy-main.c b/geanypy/src/geanypy-main.c new file mode 100644 index 000000000..961007b09 --- /dev/null +++ b/geanypy/src/geanypy-main.c @@ -0,0 +1,53 @@ +#include "geanypy.h" + + +static PyObject * +Main_is_realized(PyObject *module) +{ + if (main_is_realized()) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + + +static PyObject * +Main_locale_init(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gchar *locale_dir = NULL, *package = NULL; + static gchar *kwlist[] = { "locale_dir", "gettext_package", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &locale_dir, &package)) + { + if (locale_dir && package) + main_locale_init(locale_dir, package); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Main_reload_configuration(PyObject *module) +{ + main_reload_configuration(); + Py_RETURN_NONE; +} + + +static +PyMethodDef MainModule_methods[] = { + { "is_realized", (PyCFunction) Main_is_realized, METH_NOARGS, + "Checks whether the main gtk.Window has been realized." }, + { "locale_init", (PyCFunction) Main_locale_init, METH_KEYWORDS, + "Initializes the gettext translation system." }, + { "reload_configuration", (PyCFunction) Main_reload_configuration, METH_NOARGS, + "Reloads most of Geany's configuration files without restarting." }, + { NULL } +}; + + +PyMODINIT_FUNC initmain(void) +{ + Py_InitModule3("main", MainModule_methods, "Main program related functions."); +} diff --git a/geanypy/src/geanypy-mainwidgets.c b/geanypy/src/geanypy-mainwidgets.c new file mode 100644 index 000000000..4af873421 --- /dev/null +++ b/geanypy/src/geanypy-mainwidgets.c @@ -0,0 +1,95 @@ +#include "geanypy.h" + + +static void +MainWidgets_dealloc(MainWidgets *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + + +static int +MainWidgets_init(MainWidgets *self) +{ + self->main_widgets = geany_data->main_widgets; + return 0; +} + + +static PyObject * +MainWidgets_get_property(MainWidgets *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->main_widgets) + { + PyErr_SetString(PyExc_RuntimeError, + "MainWidgets instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "editor_menu")) + return pygobject_new(G_OBJECT(self->main_widgets->editor_menu)); + else if (g_str_equal(prop_name, "message_window_notebook")) + return pygobject_new(G_OBJECT(self->main_widgets->message_window_notebook)); + else if (g_str_equal(prop_name, "notebook")) + return pygobject_new(G_OBJECT(self->main_widgets->notebook)); + else if (g_str_equal(prop_name, "progressbar")) + return pygobject_new(G_OBJECT(self->main_widgets->progressbar)); + else if (g_str_equal(prop_name, "project_menu")) + return pygobject_new(G_OBJECT(self->main_widgets->project_menu)); + else if (g_str_equal(prop_name, "sidebar_notebook")) + return pygobject_new(G_OBJECT(self->main_widgets->sidebar_notebook)); + else if (g_str_equal(prop_name, "toolbar")) + return pygobject_new(G_OBJECT(self->main_widgets->toolbar)); + else if (g_str_equal(prop_name, "tools_menu")) + return pygobject_new(G_OBJECT(self->main_widgets->tools_menu)); + else if (g_str_equal(prop_name, "window")) + return pygobject_new(G_OBJECT(self->main_widgets->window)); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(MainWidgets); + + +static PyGetSetDef MainWidgets_getseters[] = { + GEANYPY_GETSETDEF(MainWidgets, "editor_menu", + "Popup context menu on the editor widget."), + GEANYPY_GETSETDEF(MainWidgets, "message_window_notebook", + "Notebook in the bottom message window."), + GEANYPY_GETSETDEF(MainWidgets, "notebook", + "The central documents notebook."), + GEANYPY_GETSETDEF(MainWidgets, "progressbar", + "Progress bar in the bottom status bar."), + GEANYPY_GETSETDEF(MainWidgets, "project_menu", + "The Project menu in the main menu bar."), + GEANYPY_GETSETDEF(MainWidgets, "sidebar_notebook", + "The notebook in the sidebar."), + GEANYPY_GETSETDEF(MainWidgets, "toolbar", + "Main toolbar."), + GEANYPY_GETSETDEF(MainWidgets, "tools_menu", + "The Tools menu in the main menu bar (recommended for plugins)."), + GEANYPY_GETSETDEF(MainWidgets, "window", + "Main window."), + { NULL }, +}; + + +PyTypeObject MainWidgetsType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.ui_utils.MainWidgets", /* tp_name */ + sizeof(MainWidgets), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) MainWidgets_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around the GeanyMainWidgets structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + MainWidgets_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) MainWidgets_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ + +}; diff --git a/geanypy/src/geanypy-msgwindow.c b/geanypy/src/geanypy-msgwindow.c new file mode 100644 index 000000000..558f100f9 --- /dev/null +++ b/geanypy/src/geanypy-msgwindow.c @@ -0,0 +1,133 @@ +#include "geanypy.h" + + +static PyObject * +Msgwin_clear_tab(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint tab_num = 0; + static gchar *kwlist[] = { "tabnum", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &tab_num)) + msgwin_clear_tab(tab_num); + + Py_RETURN_NONE; +} + + +static PyObject * +Msgwin_compiler_add(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint msg_color = COLOR_BLACK; + gchar *msg = NULL; + static gchar *kwlist[] = { "msg", "msg_color", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist, &msg, &msg_color)) + msgwin_compiler_add(msg_color, "%s", msg); + + Py_RETURN_NONE; +} + + +static PyObject * +Msgwin_msg_add(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint msg_color = COLOR_BLACK, line = -1; + PyObject *obj = NULL; + Document *py_doc = NULL; + GeanyDocument *doc = NULL; + gchar *msg = NULL; + static gchar *kwlist[] = { "msg", "msg_color", "line", "doc", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|iiO", kwlist, &msg, + &msg_color, &line, &obj)) + { + if (obj == NULL || obj == Py_None) + doc = NULL; + else + { + py_doc = (Document *) obj; + doc = py_doc->doc; + } + msgwin_msg_add(msg_color, line, doc, "%s", msg); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Msgwin_set_messages_dir(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gchar *msgdir = NULL; + static gchar *kwlist[] = { "messages_dir", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &msgdir)) + { + if (msgdir != NULL) + msgwin_set_messages_dir(msgdir); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Msgwin_status_add(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gchar *msg = NULL; + static gchar *kwlist[] = { "msg", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &msg)) + { + if (msg != NULL) + msgwin_status_add("%s", msg); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Msgwin_switch_tab(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint tabnum = 0, show = 0; + static gchar *kwlist[] = { "tabnum", "show", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &tabnum, &show)) + msgwin_switch_tab(tabnum, show); + + Py_RETURN_NONE; +} + + +static +PyMethodDef MsgwinModule_methods[] = { + { "clear_tab", (PyCFunction) Msgwin_clear_tab, METH_KEYWORDS }, + { "compiler_add", (PyCFunction) Msgwin_compiler_add, METH_KEYWORDS }, + { "msg_add", (PyCFunction) Msgwin_msg_add, METH_KEYWORDS }, + { "set_messages_dir", (PyCFunction) Msgwin_set_messages_dir, METH_KEYWORDS }, + { "status_add", (PyCFunction) Msgwin_status_add, METH_KEYWORDS }, + { "switch_tab", (PyCFunction) Msgwin_switch_tab, METH_KEYWORDS }, + { NULL } +}; + + +PyMODINIT_FUNC +initmsgwin(void) +{ + PyObject *m; + + m = Py_InitModule3("msgwindow", MsgwinModule_methods, + "Message windows information and management."); + + PyModule_AddIntConstant(m, "COLOR_RED", COLOR_RED); + PyModule_AddIntConstant(m, "COLOR_DARK_RED", COLOR_DARK_RED); + PyModule_AddIntConstant(m, "COLOR_BLACK", COLOR_BLACK); + PyModule_AddIntConstant(m, "COLOR_BLUE", COLOR_BLUE); + + PyModule_AddIntConstant(m, "TAB_STATUS", MSG_STATUS); + PyModule_AddIntConstant(m, "TAB_COMPILER", MSG_COMPILER); + PyModule_AddIntConstant(m, "TAB_MESSAGE", MSG_MESSAGE); + PyModule_AddIntConstant(m, "TAB_SCRIBBLE", MSG_SCRATCH); + PyModule_AddIntConstant(m, "TAB_TERMINAL", MSG_VTE); +} diff --git a/geanypy/src/geanypy-navqueue.c b/geanypy/src/geanypy-navqueue.c new file mode 100644 index 000000000..1b791b367 --- /dev/null +++ b/geanypy/src/geanypy-navqueue.c @@ -0,0 +1,80 @@ +#include "geanypy.h" + + +static PyObject * +Navqueue_goto_line(PyObject *module, PyObject *args, PyObject *kwargs) +{ + gint line = 1; + PyObject *py_old = NULL, *py_new = NULL; + Document *py_doc_old, *py_doc_new; + GeanyDocument *old_doc, *new_doc; + static gchar *kwlist[] = { "old_doc", "new_doc", "line", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "OOi", kwlist, &py_old, + &py_new, &line)) + { + if (!py_old || py_old == Py_None) + old_doc = NULL; + else + { + py_doc_old = (Document *) py_old; + old_doc = py_doc_old->doc; + } + + if (!py_new || py_new == Py_None) + Py_RETURN_NONE; + else + { + py_doc_new = (Document *) py_new; + new_doc = py_doc_new->doc; + } + + if ( (old_doc != NULL && !DOC_VALID(old_doc)) || !DOC_VALID(new_doc) ) + Py_RETURN_NONE; + if (navqueue_goto_line(old_doc, new_doc, line)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} + + +#ifdef ENABLE_PRIVATE +static PyObject * +Navqueue_go_back(PyObject *module) +{ + navqueue_go_back(); + Py_RETURN_NONE; +} + + +static PyObject * +Navqueue_go_forward(PyObject *module) +{ + navqueue_go_forward(); + Py_RETURN_NONE; +} +#endif + + +static +PyMethodDef NavqueueModule_methods[] = { + { "goto_line", (PyCFunction) Navqueue_goto_line, METH_KEYWORDS, + "Adds old file position and new file position to the navqueue, " + "then goes to the new position." }, +#ifdef ENABLE_PRIVATE + { "go_back", (PyCFunction) Navqueue_go_back, METH_NOARGS, + "Navigate backwards." }, + { "go_forward", (PyCFunction) Navqueue_go_forward, METH_NOARGS, + "Navigate forward." }, +#endif + { NULL } +}; + + +PyMODINIT_FUNC initnavqueue(void) +{ + Py_InitModule3("navqueue", NavqueueModule_methods, "Simple code navigation."); +} diff --git a/geanypy/src/geanypy-plugin.c b/geanypy/src/geanypy-plugin.c new file mode 100644 index 000000000..7b850712a --- /dev/null +++ b/geanypy/src/geanypy-plugin.c @@ -0,0 +1,280 @@ +/* + * plugin.c + * + * Copyright 2011 Matthew Brush + * + * 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. + */ + +#include "geanypy.h" + +G_MODULE_EXPORT GeanyPlugin *geany_plugin; +G_MODULE_EXPORT GeanyData *geany_data; +G_MODULE_EXPORT GeanyFunctions *geany_functions; + + +G_MODULE_EXPORT PLUGIN_VERSION_CHECK(211) + +G_MODULE_EXPORT PLUGIN_SET_INFO( + _("GeanyPy"), + _("Python plugins support"), + "1.0", + "Matthew Brush ") + + +static GtkWidget *loader_item = NULL; +static PyObject *manager = NULL; +static gchar *plugin_dir = NULL; +static SignalManager *signal_manager = NULL; + + +/* Forward declarations to prevent compiler warnings. */ +PyMODINIT_FUNC initapp(void); +PyMODINIT_FUNC initdialogs(void); +PyMODINIT_FUNC initdocument(void); +PyMODINIT_FUNC initeditor(void); +PyMODINIT_FUNC initencoding(void); +PyMODINIT_FUNC initfiletypes(void); +PyMODINIT_FUNC inithighlighting(void); +PyMODINIT_FUNC initmain(void); +PyMODINIT_FUNC initmsgwin(void); +PyMODINIT_FUNC initnavqueue(void); +PyMODINIT_FUNC initprefs(void); +PyMODINIT_FUNC initproject(void); +PyMODINIT_FUNC initscintilla(void); +PyMODINIT_FUNC initsearch(void); +PyMODINIT_FUNC inittemplates(void); +PyMODINIT_FUNC initui_utils(void); + + +static void +GeanyPy_start_interpreter(void) +{ + gchar *init_code; + gchar *py_dir = NULL; + + +#ifndef GEANYPY_WINDOWS + { /* Prevents a crash in the dynload thingy + TODO: is this or the old dlopen version even needed? */ + GModule *mod = g_module_open(GEANYPY_PYTHON_LIBRARY, G_MODULE_BIND_LAZY); + if (!mod) { + g_warning(_("Unable to pre-load Python library: %s."), g_module_error()); + return; + } + g_module_close(mod); + } +#endif + + Py_Initialize(); + + /* Import the C modules */ + initapp(); + initdialogs(); + initdocument(); + initeditor(); + initencoding(); + initfiletypes(); + inithighlighting(); + initmain(); + initmsgwin(); + initnavqueue(); + initprefs(); + initproject(); + initscintilla(); + initsearch(); + inittemplates(); + initui_utils(); + +#ifdef GEANYPY_WINDOWS + { /* On windows, get path at runtime since we don't really know where + * Geany is installed ahead of time. */ + gchar *geany_base_dir; + geany_base_dir = g_win32_get_package_installation_directory_of_module(NULL); + if (geany_base_dir) + { + py_dir = g_build_filename(geany_base_dir, "lib", "geanypy", NULL); + g_free(geany_base_dir); + } + if (!g_file_test(py_dir, G_FILE_TEST_EXISTS)) + { + g_critical("The path to the `geany' module was not found: %s", py_dir); + g_free(py_dir); + py_dir = g_strdup(""); /* will put current dir on path? */ + } + } +#else + py_dir = g_strdup(GEANYPY_PYTHON_DIR); +#endif + + /* Adjust Python path to find wrapper package (geany) */ + init_code = g_strdup_printf( + "import os, sys\n" + "path = '%s'.replace('~', os.path.expanduser('~'))\n" + "sys.path.append(path)\n" + "import geany\n", py_dir); + g_free(py_dir); + + PyRun_SimpleString(init_code); + g_free(init_code); + +} + +static void +GeanyPy_stop_interpreter(void) +{ + if (Py_IsInitialized()) + Py_Finalize(); +} + + +static void +GeanyPy_init_manager(const gchar *dir) +{ + PyObject *module, *man, *args; + gchar *sys_plugin_dir = NULL; + + g_return_if_fail(dir != NULL); + + module = PyImport_ImportModule("geany.manager"); + if (module == NULL) + { + g_warning(_("Failed to import manager module")); + return; + } + + man = PyObject_GetAttrString(module, "PluginManager"); + Py_DECREF(module); + + if (man == NULL) + { + g_warning(_("Failed to retrieve PluginManager from manager module")); + return; + } + +#ifdef GEANYPY_WINDOWS + { /* Detect the system plugin's dir at runtime on Windows since we + * don't really know where Geany is installed. */ + gchar *geany_base_dir; + geany_base_dir = g_win32_get_package_installation_directory_of_module(NULL); + if (geany_base_dir) + { + sys_plugin_dir = g_build_filename(geany_base_dir, "lib", "geanypy", "plugins", NULL); + g_free(geany_base_dir); + } + if (!g_file_test(sys_plugin_dir, G_FILE_TEST_EXISTS)) + { + g_warning(_("System plugin directory not found.")); + g_free(sys_plugin_dir); + sys_plugin_dir = NULL; + } + } +#else + sys_plugin_dir = g_strdup(GEANYPY_PLUGIN_DIR); +#endif + + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "User plugins: %s", dir); + + if (sys_plugin_dir) + { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "System plugins: %s", sys_plugin_dir); + args = Py_BuildValue("([s, s])", sys_plugin_dir, dir); + g_free(sys_plugin_dir); + } + else + args = Py_BuildValue("([s])", dir); + + manager = PyObject_CallObject(man, args); + if (PyErr_Occurred()) + PyErr_Print(); + Py_DECREF(man); + Py_DECREF(args); + + if (manager == NULL) + { + g_warning(_("Unable to instantiate new PluginManager")); + return; + } +} + + +static void +GeanyPy_show_manager(void) +{ + PyObject *show_method; + + g_return_if_fail(manager != NULL); + + show_method = PyObject_GetAttrString(manager, "show_all"); + if (show_method == NULL) + { + g_warning(_("Unable to get show_all() method on plugin manager")); + return; + } + PyObject_CallObject(show_method, NULL); + Py_DECREF(show_method); +} + + +static void +on_python_plugin_loader_activate(GtkMenuItem *item, gpointer user_data) +{ + GeanyPy_show_manager(); +} + + +G_MODULE_EXPORT void +plugin_init(GeanyData *data) +{ + GeanyPy_start_interpreter(); + signal_manager = signal_manager_new(geany_plugin); + + plugin_dir = g_build_filename(geany->app->configdir, + "plugins", "geanypy", "plugins", NULL); + + if (!g_file_test(plugin_dir, G_FILE_TEST_IS_DIR)) + { + if (g_mkdir_with_parents(plugin_dir, 0755) == -1) + { + g_warning(_("Unable to create Python plugins directory: %s: %s"), + plugin_dir, + strerror(errno)); + g_free(plugin_dir); + plugin_dir = NULL; + } + } + + if (plugin_dir != NULL) + GeanyPy_init_manager(plugin_dir); + + loader_item = gtk_menu_item_new_with_label(_("Python Plugin Manager")); + gtk_widget_set_sensitive(loader_item, plugin_dir != NULL); + gtk_menu_append(GTK_MENU(geany->main_widgets->tools_menu), loader_item); + gtk_widget_show(loader_item); + g_signal_connect(loader_item, "activate", + G_CALLBACK(on_python_plugin_loader_activate), NULL); +} + + +G_MODULE_EXPORT void plugin_cleanup(void) +{ + signal_manager_free(signal_manager); + Py_XDECREF(manager); + GeanyPy_stop_interpreter(); + gtk_widget_destroy(loader_item); + g_free(plugin_dir); +} diff --git a/geanypy/src/geanypy-plugin.h b/geanypy/src/geanypy-plugin.h new file mode 100644 index 000000000..5457404d8 --- /dev/null +++ b/geanypy/src/geanypy-plugin.h @@ -0,0 +1,43 @@ +/* + * plugin.h + * + * Copyright 2011 Matthew Brush + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + * + */ + +#ifndef PLUGIN_H +#define PLUGIN_H +#ifdef __cplusplus +extern "C" { +#endif + + +extern GeanyPlugin *geany_plugin; +extern GeanyData *geany_data; +extern GeanyFunctions *geany_functions; + + +#ifndef PyMODINIT_FUNC +#define PyMODINIT_FUNC void +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* PLUGIN_H */ diff --git a/geanypy/src/geanypy-prefs.c b/geanypy/src/geanypy-prefs.c new file mode 100644 index 000000000..011e8e32a --- /dev/null +++ b/geanypy/src/geanypy-prefs.c @@ -0,0 +1,191 @@ +#include "geanypy.h" + + +typedef struct +{ + PyObject_HEAD + GeanyPrefs *prefs; +} Prefs; + + +static void +Prefs_dealloc(Prefs *self) +{ + g_return_if_fail(self != NULL); + self->ob_type->tp_free((PyObject *) self); +} + + +static int +Prefs_init(Prefs *self) +{ + g_return_val_if_fail(self != NULL, -1); + self->prefs = geany_data->prefs; + return 0; +} + + +static PyObject * +Prefs_get_property(Prefs *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->prefs) + { + PyErr_SetString(PyExc_RuntimeError, + "Prefs instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "default_open_path") && self->prefs->default_open_path) + return PyString_FromString(self->prefs->default_open_path); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(Prefs); + + +static PyGetSetDef Prefs_getseters[] = { + GEANYPY_GETSETDEF(Prefs, "default_open_path", + "Default path to look for files when no other path is appropriate."), + { NULL } +}; + + +static PyTypeObject PrefsType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.prefs.Prefs", /* tp_name */ + sizeof(Prefs), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) Prefs_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyPrefs structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + Prefs_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) Prefs_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + + +typedef struct +{ + PyObject_HEAD + GeanyToolPrefs *tool_prefs; +} ToolPrefs; + + +static void +ToolPrefs_dealloc(ToolPrefs *self) +{ + g_return_if_fail(self != NULL); + self->ob_type->tp_free((PyObject *) self); +} + + +static int +ToolPrefs_init(ToolPrefs *self) +{ + g_return_val_if_fail(self != NULL, -1); + self->tool_prefs = geany_data->tool_prefs; + return 0; +} + + +static PyObject * +ToolPrefs_get_property(ToolPrefs *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->tool_prefs) + { + PyErr_SetString(PyExc_RuntimeError, + "ToolPrefs instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "browser_cmd") && self->tool_prefs->browser_cmd) + return PyString_FromString(self->tool_prefs->browser_cmd); + else if (g_str_equal(prop_name, "context_action_cmd") && self->tool_prefs->context_action_cmd) + return PyString_FromString(self->tool_prefs->context_action_cmd); + else if (g_str_equal(prop_name, "grep_cmd") && self->tool_prefs->grep_cmd) + return PyString_FromString(self->tool_prefs->grep_cmd); + else if (g_str_equal(prop_name, "term_cmd") && self->tool_prefs->term_cmd) + return PyString_FromString(self->tool_prefs->term_cmd); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(ToolPrefs); + + +static PyGetSetDef ToolPrefs_getseters[] = { + GEANYPY_GETSETDEF(ToolPrefs, "browser_cmd", ""), + GEANYPY_GETSETDEF(ToolPrefs, "context_action_cmd", ""), + GEANYPY_GETSETDEF(ToolPrefs, "grep_cmd", ""), + GEANYPY_GETSETDEF(ToolPrefs, "term_cmd", ""), + { NULL } +}; + + +static PyTypeObject ToolPrefsType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.prefs.ToolPrefs", /* tp_name */ + sizeof(ToolPrefs), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) ToolPrefs_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyToolPrefs structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + ToolPrefs_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) ToolPrefs_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + + +#ifdef ENABLE_PRIVATE +static PyObject * +Prefs_show_dialog(PyObject *module) +{ + prefs_show_dialog(); + Py_RETURN_NONE; +} +#endif + + +static PyMethodDef PrefsModule_methods[] = { +#ifdef ENABLE_PRIVATE + { "show_dialog", (PyCFunction) Prefs_show_dialog, METH_NOARGS, + "Show the preferences dialog." }, +#endif + { NULL } +}; + + +PyMODINIT_FUNC initprefs(void) +{ + PyObject *m; + + PrefsType.tp_new = PyType_GenericNew; + if (PyType_Ready(&PrefsType) < 0) + return; + + ToolPrefsType.tp_new = PyType_GenericNew; + if (PyType_Ready(&ToolPrefsType) < 0) + return; + + m = Py_InitModule3("prefs", PrefsModule_methods, + "General preferences dialog settings"); + + Py_INCREF(&PrefsType); + PyModule_AddObject(m, "Prefs", (PyObject *) &PrefsType); + + Py_INCREF(&ToolPrefsType); + PyModule_AddObject(m, "ToolPrefs", (PyObject *) &ToolPrefsType); +} diff --git a/geanypy/src/geanypy-project.c b/geanypy/src/geanypy-project.c new file mode 100644 index 000000000..bfeb1cabd --- /dev/null +++ b/geanypy/src/geanypy-project.c @@ -0,0 +1,107 @@ +#include "geanypy.h" + + +static void +Project_dealloc(Project *self) +{ + g_return_if_fail(self != NULL); + self->ob_type->tp_free((PyObject *) self); +} + + +static int +Project_init(Project *self, PyObject *args, PyObject *kwds) +{ + g_return_val_if_fail(self != NULL, -1); + self->project = geany_data->app->project; + return 0; +} + + +static PyObject * +Project_get_property(Project *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->project) + Py_RETURN_NONE; + + if (g_str_equal(prop_name, "base_path") && self->project->base_path) + return PyString_FromString(self->project->base_path); + else if (g_str_equal(prop_name, "description") && self->project->description) + return PyString_FromString(self->project->description); + else if (g_str_equal(prop_name, "file_name") && self->project->file_name) + return PyString_FromString(self->project->file_name); + else if (g_str_equal(prop_name, "file_patterns") && self->project->file_patterns) + { + guint i, len; + PyObject *set; + len = g_strv_length(self->project->file_patterns); + set = PyFrozenSet_New(NULL); + for (i = 0; i < len; i++) + PySet_Add(set, PyString_FromString(self->project->file_patterns[i])); + return set; + } + else if (g_str_equal(prop_name, "name") && self->project->name) + return PyString_FromString(self->project->name); + else if (g_str_equal(prop_name, "type") && self->project->type) + return Py_BuildValue("i", self->project->type); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(Project); + + +static PyGetSetDef Project_getseters[] = { + GEANYPY_GETSETDEF(Project, "base_path", + "Base path of the project directory (maybe relative)."), + GEANYPY_GETSETDEF(Project, "description", + "Short description of the project."), + GEANYPY_GETSETDEF(Project, "file_name", + "Where the project file is stored."), + GEANYPY_GETSETDEF(Project, "file_patterns", + "Sequence of filename extension patterns."), + GEANYPY_GETSETDEF(Project, "name", + "The name of the project."), + GEANYPY_GETSETDEF(Project, "type", + "Identifier whether it is a pure Geany project or modified/" + "extended by a plugin."), + { NULL } +}; + + +PyTypeObject ProjectType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.project.Project", /* tp_name */ + sizeof(Project), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) Project_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyProject structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + Project_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) Project_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + + +static PyMethodDef ProjectModule_methods[] = { { NULL } }; + + +PyMODINIT_FUNC initproject(void) +{ + PyObject *m; + + ProjectType.tp_new = PyType_GenericNew; + if (PyType_Ready(&ProjectType) < 0) + return; + + m = Py_InitModule3("project", ProjectModule_methods, "Project information"); + + Py_INCREF(&ProjectType); + PyModule_AddObject(m, "Project", (PyObject *)&ProjectType); +} diff --git a/geanypy/src/geanypy-project.h b/geanypy/src/geanypy-project.h new file mode 100644 index 000000000..4e49a90dc --- /dev/null +++ b/geanypy/src/geanypy-project.h @@ -0,0 +1,14 @@ +#ifndef GEANYPY_PROJECT_H__ +#define GEANYPY_PROJECT_H__ + +PyTypeObject ProjectType; + +typedef struct +{ + PyObject_HEAD + GeanyProject *project; +} Project; + +PyMODINIT_FUNC initproject(void); + +#endif /* GEANYPY_PROJECT_H__ */ diff --git a/geanypy/src/geanypy-scinotification.c b/geanypy/src/geanypy-scinotification.c new file mode 100644 index 000000000..86081d038 --- /dev/null +++ b/geanypy/src/geanypy-scinotification.c @@ -0,0 +1,137 @@ +#include "geanypy.h" + + +static void +Notification_dealloc(Notification *self) +{ + Py_XDECREF(self->hdr); + self->ob_type->tp_free((PyObject *) self); +} + + +static int +Notification_init(Notification *self, PyObject *args, PyObject *kwds) +{ + self->notif = NULL; + self->hdr = NULL; + return 0; +} + + +static PyObject * +Notification_get_property(Notification *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->notif) + { + PyErr_SetString(PyExc_RuntimeError, + "Notification instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "nmhdr")) + { + Py_INCREF(self->hdr); + return (PyObject *) self->hdr; + } + else if (g_str_equal(prop_name, "position")) + return PyInt_FromLong((glong) self->notif->position); + else if (g_str_equal(prop_name, "ch")) + return Py_BuildValue("c", self->notif->ch); + else if (g_str_equal(prop_name, "modifiers")) + return PyInt_FromLong((glong) self->notif->modifiers); + else if (g_str_equal(prop_name, "modification_type")) + return PyInt_FromLong((glong) self->notif->modificationType); + else if (g_str_equal(prop_name, "text")) + return PyString_FromString(self->notif->text); + else if (g_str_equal(prop_name, "length")) + return PyInt_FromLong((glong) self->notif->length); + else if (g_str_equal(prop_name, "lines_added")) + return PyInt_FromLong((glong) self->notif->linesAdded); + else if (g_str_equal(prop_name, "message")) + return PyInt_FromLong((glong) self->notif->message); + else if (g_str_equal(prop_name, "w_param")) + return PyLong_FromLong(self->notif->wParam); + else if (g_str_equal(prop_name, "l_param")) + return PyLong_FromLong(self->notif->lParam); + else if (g_str_equal(prop_name, "line")) + return PyInt_FromLong((glong) self->notif->line); + else if (g_str_equal(prop_name, "fold_level_now")) + return PyInt_FromLong((glong) self->notif->foldLevelNow); + else if (g_str_equal(prop_name, "fold_level_prev")) + return PyInt_FromLong((glong) self->notif->foldLevelPrev); + else if (g_str_equal(prop_name, "margin")) + return PyInt_FromLong((glong) self->notif->margin); + else if (g_str_equal(prop_name, "list_type")) + return PyInt_FromLong((glong) self->notif->listType); + else if (g_str_equal(prop_name, "x")) + return PyInt_FromLong((glong) self->notif->x); + else if (g_str_equal(prop_name, "y")) + return PyInt_FromLong((glong) self->notif->y); + else if (g_str_equal(prop_name, "token")) + return PyInt_FromLong((glong) self->notif->token); + else if (g_str_equal(prop_name, "annotation_lines_added")) + return PyInt_FromLong((glong) self->notif->annotationLinesAdded); + else if (g_str_equal(prop_name, "updated")) + return PyInt_FromLong((glong) self->notif->updated); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(Notification); + + +static PyGetSetDef Notification_getseters[] = { + GEANYPY_GETSETDEF(Notification, "nmhdr", ""), + GEANYPY_GETSETDEF(Notification, "position", ""), + GEANYPY_GETSETDEF(Notification, "ch", ""), + GEANYPY_GETSETDEF(Notification, "modifiers", ""), + GEANYPY_GETSETDEF(Notification, "modification_type", ""), + GEANYPY_GETSETDEF(Notification, "text", ""), + GEANYPY_GETSETDEF(Notification, "length", ""), + GEANYPY_GETSETDEF(Notification, "lines_added", ""), + GEANYPY_GETSETDEF(Notification, "message", ""), + GEANYPY_GETSETDEF(Notification, "w_param", ""), + GEANYPY_GETSETDEF(Notification, "l_param", ""), + GEANYPY_GETSETDEF(Notification, "line", ""), + GEANYPY_GETSETDEF(Notification, "fold_level_now", ""), + GEANYPY_GETSETDEF(Notification, "fold_level_prev", ""), + GEANYPY_GETSETDEF(Notification, "margin", ""), + GEANYPY_GETSETDEF(Notification, "list_type", ""), + GEANYPY_GETSETDEF(Notification, "x", ""), + GEANYPY_GETSETDEF(Notification, "y", ""), + GEANYPY_GETSETDEF(Notification, "token", ""), + GEANYPY_GETSETDEF(Notification, "annotation_lines_added", ""), + GEANYPY_GETSETDEF(Notification, "updated", ""), + { NULL } +}; + + +PyTypeObject NotificationType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.scintilla.Notification", /* tp_name */ + sizeof(Notification), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) Notification_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a SCNotification structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + Notification_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) Notification_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ + +}; + + +Notification *Notification_create_new_from_scintilla_notification(SCNotification *notif) +{ + Notification *self; + self = (Notification *) PyObject_CallObject((PyObject *) &NotificationType, NULL); + self->notif = notif; + self->hdr = NotifyHeader_create_new_from_scintilla_notification(self->notif); + return self; +} diff --git a/geanypy/src/geanypy-scinotifyheader.c b/geanypy/src/geanypy-scinotifyheader.c new file mode 100644 index 000000000..65dc92e09 --- /dev/null +++ b/geanypy/src/geanypy-scinotifyheader.c @@ -0,0 +1,77 @@ +#include "geanypy.h" + + +static void +NotifyHeader_dealloc(NotifyHeader *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + + +static int +NotifyHeader_init(NotifyHeader *self, PyObject *args, PyObject *kwds) +{ + self->notif = NULL; + return 0; +} + + +static PyObject * +NotifyHeader_get_property(NotifyHeader *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->notif) + { + PyErr_SetString(PyExc_RuntimeError, + "NotifyHeader instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "hwnd_from")) + return PyLong_FromVoidPtr(self->notif->nmhdr.hwndFrom); + else if (g_str_equal(prop_name, "id_from")) + return PyLong_FromLong(self->notif->nmhdr.idFrom); + else if (g_str_equal(prop_name, "code")) + return PyInt_FromLong((glong) self->notif->nmhdr.code); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(NotifyHeader); + + +static PyGetSetDef NotifyHeader_getseters[] = { + GEANYPY_GETSETDEF(NotifyHeader, "hwnd_from", ""), + GEANYPY_GETSETDEF(NotifyHeader, "id_from", ""), + GEANYPY_GETSETDEF(NotifyHeader, "code", ""), + { NULL } +}; + + +PyTypeObject NotifyHeaderType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.scintilla.NotifyHeader", /* tp_name */ + sizeof(NotifyHeader), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) NotifyHeader_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a NotifyHeader structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + NotifyHeader_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) NotifyHeader_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ + +}; + + +NotifyHeader *NotifyHeader_create_new_from_scintilla_notification(SCNotification *notif) +{ + NotifyHeader *self; + self = (NotifyHeader *) PyObject_CallObject((PyObject *) &NotifyHeaderType, NULL); + self->notif = notif; + return self; +} diff --git a/geanypy/src/geanypy-scintilla.c b/geanypy/src/geanypy-scintilla.c new file mode 100644 index 000000000..6c5d9a85e --- /dev/null +++ b/geanypy/src/geanypy-scintilla.c @@ -0,0 +1,1006 @@ +#include "geanypy.h" + + +/* Bail-out when ScintillaObject being wrapped is NULL. */ +#define SCI_RET_IF_FAIL(obj) { \ + if (!self->sci) { \ + PyErr_SetString(PyExc_RuntimeError, \ + "Scintilla instance not initialized properly."); \ + Py_RETURN_NONE; } } + + +static void +Scintilla_dealloc(Scintilla *self) +{ + self->ob_type->tp_free((PyObject *) self); +} + + +static int +Scintilla_init(Scintilla *self) +{ + self->sci = NULL; + return 0; +} + + +static PyObject * +Scintilla_get_property(Scintilla *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->sci) + { + PyErr_SetString(PyExc_RuntimeError, + "Scintilla instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "widget")) + return pygobject_new(G_OBJECT(self->sci)); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(Scintilla); + + +static PyObject * +Scintilla_delete_marker_at_line(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line_num, marker; + static gchar *kwlist[] = { "line_number", "marker", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &line_num, &marker)) + sci_delete_marker_at_line(self->sci, line_num, marker); + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_end_undo_action(Scintilla *self) +{ + SCI_RET_IF_FAIL(self); + sci_end_undo_action(self->sci); + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_ensure_line_is_visible(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line = -1; + static gchar *kwlist[] = { "line", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &line)) + { + if (line == -1) + line = sci_get_current_line(self->sci); + sci_ensure_line_is_visible(self->sci, line); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_find_matching_brace(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos, match_pos; + static gchar *kwlist[] = { "pos", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pos)) + { + match_pos = sci_find_matching_brace(self->sci, pos); + return Py_BuildValue("i", match_pos); + } + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_find_text(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos = -1, flags = 0; + glong start_chr = 0, end_chr = 0; + gchar *search_text; + struct Sci_TextToFind ttf = { { 0 } }; + static gchar *kwlist[] = { "text", "flags", "start_char", "end_char", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|ill", kwlist, + &search_text, &flags, &start_chr, &end_chr)) + { + ttf.chrg.cpMin = start_chr; + ttf.chrg.cpMax = end_chr; + ttf.lpstrText = search_text; + pos = sci_find_text(self->sci, flags, &ttf); + if (pos > -1) + return Py_BuildValue("ll", ttf.chrgText.cpMin, ttf.chrgText.cpMax); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_char_at(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos; + gchar chr; + static gchar *kwlist[] = { "pos", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pos)) + { + chr = sci_get_char_at(self->sci, pos); + return PyString_FromFormat("%c", chr); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_col_from_position(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos, col; + static gchar *kwlist[] = { "pos", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pos)) + { + col = sci_get_col_from_position(self->sci, pos); + return Py_BuildValue("i", col); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_contents(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint len = -1; + gchar *text; + PyObject *py_text; + static gchar *kwlist[] = { "len", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &len)) + { + if (len == -1) + len = sci_get_length(self->sci) + 1; + text = sci_get_contents(self->sci, len); + if (text == NULL) + Py_RETURN_NONE; + py_text = PyString_FromString(text); + g_free(text); + return py_text; + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_contents_range(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint start = -1, end = -1; + gchar *text; + PyObject *py_text; + static gchar *kwlist[] = { "start", "end", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, &start, &end)) + { + if (start == -1) + start = 0; + if (end == -1) + end = sci_get_length(self->sci) + 1; + text = sci_get_contents_range(self->sci, start, end); + if (text == NULL) + Py_RETURN_NONE; + py_text = PyString_FromString(text); + g_free(text); + return py_text; + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_current_line(Scintilla *self) +{ + gint line; + SCI_RET_IF_FAIL(self); + line = sci_get_current_line(self->sci); + return Py_BuildValue("i", line); +} + +static PyObject * +Scintilla_get_current_position(Scintilla *self) +{ + gint pos; + SCI_RET_IF_FAIL(self); + pos = sci_get_current_position(self->sci); + return Py_BuildValue("i", pos); +} + + +static PyObject * +Scintilla_get_length(Scintilla *self) +{ + gint len; + SCI_RET_IF_FAIL(self); + len = sci_get_length(self->sci); + return Py_BuildValue("i", len); +} + + +static PyObject * +Scintilla_get_line(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line_num = -1; + gchar *text; + PyObject *py_text; + static gchar *kwlist[] = { "line_num", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &line_num)) + { + if (line_num == -1) + line_num = sci_get_current_line(self->sci); + text = sci_get_line(self->sci, line_num); + if (text == NULL) + Py_RETURN_NONE; + py_text = PyString_FromString(text); + g_free(text); + return py_text; + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_line_count(Scintilla *self) +{ + gint line_count; + SCI_RET_IF_FAIL(self); + line_count = sci_get_line_count(self->sci); + return Py_BuildValue("i", line_count); +} + + +static PyObject * +Scintilla_get_line_end_position(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line = -1, line_end_pos; + static gchar *kwlist[] = { "line", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &line)) + { + if (line == -1) + line = sci_get_current_line(self->sci); + line_end_pos = sci_get_line_end_position(self->sci, line); + return Py_BuildValue("i", line_end_pos); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_line_from_position(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line, pos; + static gchar *kwlist[] = { "pos", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &pos)) + { + if (pos == -1) + pos = sci_get_current_position(self->sci); + line = sci_get_line_from_position(self->sci, pos); + return Py_BuildValue("i", line); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_line_indentation(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line = -1, width; + static gchar *kwlist[] = { "line", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &line)) + { + if (line == -1) + line = sci_get_current_line(self->sci); + width = sci_get_line_indentation(self->sci, line); + return Py_BuildValue("i", width); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_line_is_visible(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line = -1; + gboolean visible; + static gchar *kwlist[] = { "line", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &line)) + { + if (line == -1) + line = sci_get_current_line(self->sci); + visible = sci_get_line_is_visible(self->sci, line); + if (visible) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_line_length(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line = -1, length; + static gchar *kwlist[] = { "line", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &line)) + { + if (line == -1) + line = sci_get_current_line(self->sci); + length = sci_get_line_length(self->sci, line); + return Py_BuildValue("i", length); + } + + Py_RETURN_NONE; +} + + + +static PyObject * +Scintilla_get_position_from_line(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line = -1, pos; + static gchar *kwlist[] = { "line", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &line)) + { + if (line == -1) + line = sci_get_current_line(self->sci); + pos = sci_get_position_from_line(self->sci, line); + return Py_BuildValue("i", pos); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_selected_text_length(Scintilla *self) +{ + gint len; + SCI_RET_IF_FAIL(self); + len = sci_get_selected_text_length(self->sci); + return Py_BuildValue("i", len); +} + + +static PyObject * +Scintilla_get_selection_contents(Scintilla *self) +{ + gchar *text; + PyObject *py_text; + SCI_RET_IF_FAIL(self); + text = sci_get_selection_contents(self->sci); + if (text == NULL) + Py_RETURN_NONE; + py_text = PyString_FromString(text); + g_free(text); + return py_text; +} + + +static PyObject * +Scintilla_get_selection_end(Scintilla *self) +{ + gint pos; + SCI_RET_IF_FAIL(self); + pos = sci_get_selection_end(self->sci); + return Py_BuildValue("i", pos); +} + + +static PyObject * +Scintilla_get_selection_mode(Scintilla *self) +{ + gint mode; + SCI_RET_IF_FAIL(self); + mode = sci_get_selection_mode(self->sci); + return Py_BuildValue("i", mode); +} + + +static PyObject * +Scintilla_get_selection_start(Scintilla *self) +{ + gint pos; + SCI_RET_IF_FAIL(self); + pos = sci_get_selection_start(self->sci); + return Py_BuildValue("i", pos); +} + + +static PyObject * +Scintilla_get_style_at(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos = -1, style; + static gchar *kwlist[] = { "pos", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &pos)) + { + if (pos == -1) + pos = sci_get_current_position(self->sci); + style = sci_get_style_at(self->sci, pos); + return Py_BuildValue("i", style); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_get_tab_width(Scintilla *self) +{ + gint width; + SCI_RET_IF_FAIL(self); + width = sci_get_tab_width(self->sci); + return Py_BuildValue("i", width); +} + + +static PyObject * +Scintilla_goto_line(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line, unfold; + static gchar *kwlist[] = { "line", "unfold", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &line, &unfold)) + sci_goto_line(self->sci, line, (gboolean) unfold); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_has_selection(Scintilla *self) +{ + SCI_RET_IF_FAIL(self); + if (sci_has_selection(self->sci)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + + +static PyObject * +Scintilla_indicator_clear(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos, len; + static gchar *kwlist[] = { "pos", "len", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &pos, &len)) + sci_indicator_clear(self->sci, pos, len); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_indicator_set(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint indic; + static gchar *kwlist[] = { "indic", NULL }; + + SCI_RET_IF_FAIL(self); + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &indic)) + sci_indicator_set(self->sci, indic); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_insert_text(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos = -1; + gchar *text; + static gchar *kwlist[] = { "text", "pos", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist, &text, &pos)) + { + if (pos == -1) + pos = sci_get_current_position(self->sci); + if (text != NULL) + sci_insert_text(self->sci, pos, text); + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_is_marker_set_at_line(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gboolean result; + gint line, marker; + static gchar *kwlist[] = { "line", "marker", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &line, &marker)) + { + result = sci_is_marker_set_at_line(self->sci, line, marker); + if (result) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_replace_sel(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gchar *text; + static gchar *kwlist[] = { "text", NULL }; + SCI_RET_IF_FAIL(self); + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &text)) + sci_replace_sel(self->sci, text); + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_scroll_caret(Scintilla *self) +{ + SCI_RET_IF_FAIL(self); + sci_scroll_caret(self->sci); + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_send_command(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint cmd; + static gchar *kwlist[] = { "cmd", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &cmd)) + sci_send_command(self->sci, cmd); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_set_current_position(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos, stc = FALSE; + static gchar *kwlist[] = { "pos", "scroll_to_caret", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &pos, &stc)) + sci_set_current_position(self->sci, pos, stc); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_set_font(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint style, size; + gchar *font; + static gchar *kwlist[] = { "style", "font", "size", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "isi", kwlist, &style, &font, &size)) + sci_set_font(self->sci, style, font, size); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_set_line_indentation(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line, indent; + static gchar *kwlist[] = { "line", "indent", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &line, &indent)) + sci_set_line_indentation(self->sci, line, indent); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_set_marker_at_line(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint line, marker; + static gchar *kwlist[] = { "line", "marker", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &line, &marker)) + sci_set_marker_at_line(self->sci, line, marker); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_set_selection_end(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos; + static gchar *kwlist[] = { "pos", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pos)) + sci_set_selection_end(self->sci, pos); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_set_selection_mode(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint mode; + static gchar *kwlist[] = { "mode", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &mode)) + sci_set_selection_mode(self->sci, mode); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_set_selection_start(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint pos; + static gchar *kwlist[] = { "pos", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pos)) + sci_set_selection_start(self->sci, pos); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_set_text(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gchar *text; + static gchar *kwlist[] = { "text", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &text)) + sci_set_text(self->sci, text); + + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_start_undo_action(Scintilla *self) +{ + SCI_RET_IF_FAIL(self); + sci_start_undo_action(self->sci); + Py_RETURN_NONE; +} + + +static PyObject * +Scintilla_send_message(Scintilla *self, PyObject *args, PyObject *kwargs) +{ + gint msg; + glong uptr = 0, sptr = 0, ret; + static gchar *kwlist[] = { "msg", "lparam", "wparam", NULL }; + + SCI_RET_IF_FAIL(self); + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "i|ll", kwlist, &msg, &uptr, &sptr)) + { + ret = scintilla_send_message(self->sci, msg, uptr, sptr); + return Py_BuildValue("l", ret); + } + + Py_RETURN_NONE; +} + + +static PyMethodDef Scintilla_methods[] = { + { "delete_marker_at_line", (PyCFunction) Scintilla_delete_marker_at_line, METH_KEYWORDS, + "Deletes a line marker." }, + { "end_undo_action", (PyCFunction) Scintilla_end_undo_action, METH_NOARGS, + "Ends grouping a set of edits together as one Undo action." }, + { "ensure_line_is_visible", (PyCFunction) Scintilla_ensure_line_is_visible, METH_KEYWORDS, + "Makes line visible (folding may have hidden it)." }, + { "find_matching_brace", (PyCFunction) Scintilla_find_matching_brace, METH_KEYWORDS, + "Finds a matching brace at pos." }, + { "find_text", (PyCFunction) Scintilla_find_text, METH_KEYWORDS, + "Finds text in the document." }, + { "get_char_at", (PyCFunction) Scintilla_get_char_at, METH_KEYWORDS, + "Gets the character at a position." }, + { "get_col_from_position", (PyCFunction) Scintilla_get_col_from_position, METH_KEYWORDS, + "Gets the column number relative to the start of the line that " + "pos is on." }, + { "get_contents", (PyCFunction) Scintilla_get_contents, METH_KEYWORDS, + "Gets all text inside a given text length." }, + { "get_contents_range", (PyCFunction) Scintilla_get_contents_range, METH_KEYWORDS, + "Gets text between start and end." }, + { "get_current_line", (PyCFunction) Scintilla_get_current_line, METH_NOARGS, + "Gets current line number." }, + { "get_current_position", (PyCFunction) Scintilla_get_current_position, METH_NOARGS, + "Gets the cursor position." }, + { "get_length", (PyCFunction) Scintilla_get_length, METH_NOARGS, + "Gets the length of all text." }, + { "get_line", (PyCFunction) Scintilla_get_line, METH_KEYWORDS, + "Gets line contents." }, + { "get_line_count", (PyCFunction) Scintilla_get_line_count, METH_NOARGS, + "Gets the total number of lines." }, + { "get_line_end_position", (PyCFunction) Scintilla_get_line_end_position, METH_KEYWORDS, + "Gets the position at the end of a line." }, + { "get_line_from_position", (PyCFunction) Scintilla_get_line_from_position, METH_KEYWORDS, + "Gets the line number from pos." }, + { "get_line_indentation", (PyCFunction) Scintilla_get_line_indentation, METH_KEYWORDS, + "Gets the indentation width of a line." }, + { "get_line_is_visible", (PyCFunction) Scintilla_get_line_is_visible, METH_KEYWORDS, + "Checks if a line is visible (folding may have hidden it)." }, + { "get_line_length", (PyCFunction) Scintilla_get_line_length, METH_KEYWORDS, + "Gets line length." }, + { "get_position_from_line", (PyCFunction) Scintilla_get_position_from_line, METH_KEYWORDS, + "Gets the position for the start of line." }, + { "get_selected_text_length", (PyCFunction) Scintilla_get_selected_text_length, METH_NOARGS, + "Gets selected text length."}, + { "get_selection_contents", (PyCFunction) Scintilla_get_selection_contents, METH_NOARGS, + "Gets selected text." }, + { "get_selection_end", (PyCFunction) Scintilla_get_selection_end, METH_NOARGS, + "Gets the selection end position." }, + { "get_selection_mode", (PyCFunction) Scintilla_get_selection_mode, METH_NOARGS, + "Gets the selection mode." }, + { "get_selection_start", (PyCFunction) Scintilla_get_selection_start, METH_NOARGS, + "Gets the selection start position." }, + { "get_style_at", (PyCFunction) Scintilla_get_style_at, METH_KEYWORDS, + "Gets the style ID at pos." }, + { "get_tab_width", (PyCFunction) Scintilla_get_tab_width, METH_NOARGS, + "Gets display tab width (this is not indent width, see IndentPrefs)." }, + { "goto_line", (PyCFunction) Scintilla_goto_line, METH_KEYWORDS, + "Jumps to the specified line in the document." }, + { "has_selection", (PyCFunction) Scintilla_has_selection, METH_NOARGS, + "Checks if there's a selection." }, + { "indicator_clear", (PyCFunction) Scintilla_indicator_clear, METH_KEYWORDS, + "Clears the currently set indicator from a range of text." }, + { "indicator_set", (PyCFunction) Scintilla_indicator_set, METH_KEYWORDS, + "Sets the current indicator." }, + { "insert_text", (PyCFunction) Scintilla_insert_text, METH_KEYWORDS, + "Inserts text at pos." }, + { "is_marker_set_at_line", (PyCFunction) Scintilla_is_marker_set_at_line, METH_KEYWORDS, + "Checks if a line has a marker set." }, + { "replace_sel", (PyCFunction) Scintilla_replace_sel, METH_KEYWORDS, + "Replaces selection." }, + { "scroll_caret", (PyCFunction) Scintilla_scroll_caret, METH_NOARGS, + "Scrolls the cursor in view." }, + { "send_command", (PyCFunction) Scintilla_send_command, METH_KEYWORDS, + "Sends Scintilla commands without any parameters (see send_message function)." }, + { "set_current_position", (PyCFunction) Scintilla_set_current_position, METH_KEYWORDS, + "Sets the cursor position." }, + { "set_font", (PyCFunction) Scintilla_set_font, METH_KEYWORDS, + "Sets the font and size for a particular style." }, + { "set_line_indentation", (PyCFunction) Scintilla_set_line_indentation, METH_KEYWORDS, + "Sets the indentation of a line." }, + { "set_marker_at_line", (PyCFunction) Scintilla_set_marker_at_line, METH_KEYWORDS, + "Sets a line marker." }, + { "set_selection_end", (PyCFunction) Scintilla_set_selection_end, METH_KEYWORDS, + "Sets the selection end position." }, + { "set_selection_mode", (PyCFunction) Scintilla_set_selection_mode, METH_KEYWORDS, + "Sets selection mode." }, + { "set_selection_start", (PyCFunction) Scintilla_set_selection_start, METH_KEYWORDS, + "Sets the selection start position." }, + { "set_text", (PyCFunction) Scintilla_set_text, METH_KEYWORDS, + "Sets all text." }, + { "start_undo_action", (PyCFunction) Scintilla_start_undo_action, METH_NOARGS, + "Begins grouping a set of edits together as one Undo action." }, + { "send_message", (PyCFunction) Scintilla_send_message, METH_KEYWORDS, + "Send a message to the Scintilla widget." }, + { NULL } +}; + + +static PyGetSetDef Scintilla_getseters[] = { + GEANYPY_GETSETDEF(Scintilla, "widget", + "Gets the ScintillaObject as a GTK+ widget."), + { NULL } +}; + + +static PyTypeObject ScintillaType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.scintilla.Scintilla", /* tp_name */ + sizeof(Scintilla), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) Scintilla_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a ScintillaObject structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_iternext */ + Scintilla_methods, /* tp_methods */ + 0, /* tp_members */ + Scintilla_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) Scintilla_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + +static PyMethodDef ScintillaModule_methods[] = { { NULL } }; + + +PyMODINIT_FUNC initscintilla(void) +{ + PyObject *m; + + ScintillaType.tp_new = PyType_GenericNew; + if (PyType_Ready(&ScintillaType) < 0) + return; + + NotificationType.tp_new = PyType_GenericNew; + if (PyType_Ready(&NotificationType) < 0) + return; + + NotifyHeaderType.tp_new = PyType_GenericNew; + if (PyType_Ready(&NotifyHeaderType) < 0) + return; + + m = Py_InitModule("scintilla", ScintillaModule_methods); + + Py_INCREF(&ScintillaType); + PyModule_AddObject(m, "Scintilla", (PyObject *)&ScintillaType); + + Py_INCREF(&NotificationType); + PyModule_AddObject(m, "Notification", (PyObject *)&NotificationType); + + Py_INCREF(&NotifyHeaderType); + PyModule_AddObject(m, "NotifyHeader", (PyObject *)&NotifyHeaderType); + + + PyModule_AddIntConstant(m, "FLAG_WHOLE_WORD", SCFIND_WHOLEWORD); + PyModule_AddIntConstant(m, "FLAG_MATCH_CASE", SCFIND_MATCHCASE); + PyModule_AddIntConstant(m, "FLAG_WORD_START", SCFIND_WORDSTART); + PyModule_AddIntConstant(m, "FLAG_REGEXP", SCFIND_REGEXP); + PyModule_AddIntConstant(m, "FLAG_POSIX", SCFIND_POSIX); + + PyModule_AddIntConstant(m, "UPDATE_CONTENT", SC_UPDATE_CONTENT); + PyModule_AddIntConstant(m, "UPDATE_SELECTION", SC_UPDATE_SELECTION); + PyModule_AddIntConstant(m, "UPDATE_V_SCROLL", SC_UPDATE_V_SCROLL); + PyModule_AddIntConstant(m, "UPDATE_H_SCROLL", SC_UPDATE_H_SCROLL); + + PyModule_AddIntConstant(m, "MOD_INSERT_TEXT", SC_MOD_INSERTTEXT); + PyModule_AddIntConstant(m, "MOD_DELETE_TEXT", SC_MOD_DELETETEXT); + PyModule_AddIntConstant(m, "MOD_CHANGE_STYLE", SC_MOD_CHANGESTYLE); + PyModule_AddIntConstant(m, "MOD_CHANGE_FOLD", SC_MOD_CHANGEFOLD); + PyModule_AddIntConstant(m, "PERFORMED_USER", SC_PERFORMED_USER); + PyModule_AddIntConstant(m, "PERFORMED_UNDO", SC_PERFORMED_UNDO); + PyModule_AddIntConstant(m, "PERFORMED_REDO", SC_PERFORMED_REDO); + PyModule_AddIntConstant(m, "MULTI_STEP_UNDO_REDO", SC_MULTISTEPUNDOREDO); + PyModule_AddIntConstant(m, "LAST_STEP_IN_UNDO_REDO", SC_LASTSTEPINUNDOREDO); + PyModule_AddIntConstant(m, "MOD_CHANGE_MARKER", SC_MOD_CHANGEMARKER); + PyModule_AddIntConstant(m, "MOD_BEFORE_INSERT", SC_MOD_BEFOREINSERT); + PyModule_AddIntConstant(m, "MOD_BEFORE_DELETE", SC_MOD_BEFOREDELETE); + PyModule_AddIntConstant(m, "MOD_CHANGE_INDICATOR", SC_MOD_CHANGEINDICATOR); + PyModule_AddIntConstant(m, "MOD_CHANGE_LINE_STATE", SC_MOD_CHANGELINESTATE); + PyModule_AddIntConstant(m, "MOD_LEXER_STATE", SC_MOD_LEXERSTATE); + PyModule_AddIntConstant(m, "MOD_CHANGE_MARGIN", SC_MOD_CHANGEMARGIN); + PyModule_AddIntConstant(m, "MOD_CHANGE_ANNOTATION", SC_MOD_CHANGEANNOTATION); + PyModule_AddIntConstant(m, "MULTILINE_UNDO_REDO", SC_MULTILINEUNDOREDO); + PyModule_AddIntConstant(m, "START_ACTION", SC_STARTACTION); + PyModule_AddIntConstant(m, "MOD_CONTAINER", SC_MOD_CONTAINER); + PyModule_AddIntConstant(m, "MOD_EVENT_MASK_ALL", SC_MODEVENTMASKALL); + + PyModule_AddIntConstant(m, "STYLE_NEEDED", SCN_STYLENEEDED); + PyModule_AddIntConstant(m, "CHAR_ADDED", SCN_CHARADDED); + PyModule_AddIntConstant(m, "SAVE_POINT_REACHED", SCN_SAVEPOINTREACHED); + PyModule_AddIntConstant(m, "SAVE_POINT_LEFT", SCN_SAVEPOINTLEFT); + PyModule_AddIntConstant(m, "MODIFY_ATTEMPT_RO", SCN_MODIFYATTEMPTRO); + PyModule_AddIntConstant(m, "KEY", SCN_KEY); + PyModule_AddIntConstant(m, "DOUBLE_CLICK", SCN_DOUBLECLICK); + PyModule_AddIntConstant(m, "UPDATE_UI", SCN_UPDATEUI); + PyModule_AddIntConstant(m, "MODIFIED", SCN_MODIFIED); + PyModule_AddIntConstant(m, "MACRO_RECORD", SCN_MACRORECORD); + PyModule_AddIntConstant(m, "MARGIN_CLICK", SCN_MARGINCLICK); + PyModule_AddIntConstant(m, "NEED_SHOWN", SCN_NEEDSHOWN); + PyModule_AddIntConstant(m, "PAINTED", SCN_PAINTED); + PyModule_AddIntConstant(m, "USER_LIST_SELECTION", SCN_USERLISTSELECTION); + PyModule_AddIntConstant(m, "URI_DROPPED", SCN_URIDROPPED); + PyModule_AddIntConstant(m, "DWELL_START", SCN_DWELLSTART); + PyModule_AddIntConstant(m, "DWELL_END", SCN_DWELLEND); + PyModule_AddIntConstant(m, "ZOOM", SCN_ZOOM); + PyModule_AddIntConstant(m, "HOT_SPOT_CLICK", SCN_HOTSPOTCLICK); + PyModule_AddIntConstant(m, "HOT_SPOT_DOUBLE_CLICK", SCN_HOTSPOTDOUBLECLICK); + PyModule_AddIntConstant(m, "CALL_TIP_CLICK", SCN_CALLTIPCLICK); + PyModule_AddIntConstant(m, "AUTO_C_SELECTION", SCN_AUTOCSELECTION); + PyModule_AddIntConstant(m, "INDICATOR_CLICK", SCN_INDICATORCLICK); + PyModule_AddIntConstant(m, "INDICATOR_RELEASE", SCN_INDICATORRELEASE); + PyModule_AddIntConstant(m, "AUTOC_CANCELLED", SCN_AUTOCCANCELLED); + PyModule_AddIntConstant(m, "AUTOC_CHAR_DELETED", SCN_AUTOCCHARDELETED); + PyModule_AddIntConstant(m, "HOT_SPOT_RELEASE_CLICK", SCN_HOTSPOTRELEASECLICK); +} + + +Scintilla *Scintilla_create_new_from_scintilla(ScintillaObject *sci) +{ + Scintilla *self; + self = (Scintilla *) PyObject_CallObject((PyObject *) &ScintillaType, NULL); + self->sci = sci; + return self; +} diff --git a/geanypy/src/geanypy-scintilla.h b/geanypy/src/geanypy-scintilla.h new file mode 100644 index 000000000..a4dc14a51 --- /dev/null +++ b/geanypy/src/geanypy-scintilla.h @@ -0,0 +1,32 @@ +#ifndef GEANYPY_SCINTILLA_H__ +#define GEANYPY_SCINTILLA_H__ + +PyTypeObject NotificationType; +PyTypeObject NotifyHeaderType; + +typedef struct +{ + PyObject_HEAD + ScintillaObject *sci; +} Scintilla; + +typedef struct +{ + PyObject_HEAD + SCNotification *notif; +} NotifyHeader; + +typedef struct +{ + PyObject_HEAD + SCNotification *notif; + NotifyHeader *hdr; +} Notification; + + +PyMODINIT_FUNC init_geany_scintilla(void); +Scintilla *Scintilla_create_new_from_scintilla(ScintillaObject *sci); +Notification *Notification_create_new_from_scintilla_notification(SCNotification *notif); +NotifyHeader *NotifyHeader_create_new_from_scintilla_notification(SCNotification *notif); + +#endif /* GEANYPY_SCINTILLA_H__ */ diff --git a/geanypy/src/geanypy-search.c b/geanypy/src/geanypy-search.c new file mode 100644 index 000000000..fe6328ba4 --- /dev/null +++ b/geanypy/src/geanypy-search.c @@ -0,0 +1,95 @@ +#include "geanypy.h" + + +typedef struct +{ + PyObject_HEAD + GeanySearchPrefs *search_prefs; +} SearchPrefs; + + +static void +SearchPrefs_dealloc(SearchPrefs *self) +{ + g_return_if_fail(self != NULL); + self->ob_type->tp_free((PyObject *) self); +} + + +static int +SearchPrefs_init(SearchPrefs *self) +{ + g_return_val_if_fail(self != NULL, -1); + self->search_prefs = geany_data->search_prefs; + return 0; +} + + +static PyObject * +SearchPrefs_get_property(SearchPrefs *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->search_prefs) + { + PyErr_SetString(PyExc_RuntimeError, + "SearchPrefs instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "use_current_word")) + { + if (self->search_prefs->use_current_word) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(SearchPrefs); + + +static PyGetSetDef SearchPrefs_getseters[] = { + GEANYPY_GETSETDEF(SearchPrefs, "use_current_word", + "Use current word for default search text."), + { NULL } +}; + + +static PyTypeObject SearchPrefsType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.search.SearchPrefs", /* tp_name */ + sizeof(SearchPrefs), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) SearchPrefs_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanySearchPrefs structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + SearchPrefs_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) SearchPrefs_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + + +static PyMethodDef SearchPrefsModule_methods[] = { { NULL } }; + + +PyMODINIT_FUNC initsearch(void) +{ + PyObject *m; + + SearchPrefsType.tp_new = PyType_GenericNew; + if (PyType_Ready(&SearchPrefsType) < 0) + return; + + m = Py_InitModule3("search", SearchPrefsModule_methods, + "Search preferences and information."); + + Py_INCREF(&SearchPrefsType); + PyModule_AddObject(m, "SearchPrefs", (PyObject *) &SearchPrefsType); +} diff --git a/geanypy/src/geanypy-signalmanager.c b/geanypy/src/geanypy-signalmanager.c new file mode 100644 index 000000000..a488019fc --- /dev/null +++ b/geanypy/src/geanypy-signalmanager.c @@ -0,0 +1,241 @@ +#include "geanypy.h" + +struct _SignalManager +{ + GeanyPlugin *geany_plugin; + PyObject *py_obj; + GObject *obj; +}; + + +static void signal_manager_connect_signals(SignalManager *man); + +static void on_build_start(GObject *geany_object, SignalManager *man); +static void on_document_activate(GObject *geany_object, GeanyDocument *doc, SignalManager *man); +static void on_document_before_save(GObject *geany_object, GeanyDocument *doc, SignalManager *man); +static void on_document_close(GObject *geany_object, GeanyDocument *doc, SignalManager *man); +static void on_document_filetype_set(GObject *geany_object, GeanyDocument *doc, GeanyFiletype *filetype_old, SignalManager *man); +static void on_document_new(GObject *geany_object, GeanyDocument *doc, SignalManager *man); +static void on_document_open(GObject *geany_object, GeanyDocument *doc, SignalManager *man); +static void on_document_reload(GObject *geany_object, GeanyDocument *doc, SignalManager *man); +static void on_document_save(GObject *geany_object, GeanyDocument *doc, SignalManager *man); +static gboolean on_editor_notify(GObject *geany_object, GeanyEditor *editor, SCNotification *nt, SignalManager *man); +static void on_geany_startup_complete(GObject *geany_object, SignalManager *man); +static void on_project_close(GObject *geany_object, SignalManager *man); +static void on_project_dialog_confirmed(GObject *geany_object, GtkWidget *notebook, SignalManager *man); +static void on_project_dialog_open(GObject *geany_object, GtkWidget *notebook, SignalManager *man); +static void on_project_dialog_close(GObject *geany_object, GtkWidget *notebook, SignalManager *man); +static void on_project_open(GObject *geany_object, GKeyFile *config, SignalManager *man); +static void on_project_save(GObject *geany_object, GKeyFile *config, SignalManager *man); +static void on_update_editor_menu(GObject *geany_object, const gchar *word, gint pos, GeanyDocument *doc, SignalManager *man); + + +SignalManager *signal_manager_new(GeanyPlugin *geany_plugin) +{ + SignalManager *man; + PyObject *module; + + man = g_new0(SignalManager, 1); + + man->geany_plugin = geany_plugin; + man->py_obj = NULL; + man->obj = NULL; + + module = PyImport_ImportModule("geany"); + if (!module) + { + if (PyErr_Occurred()) + PyErr_Print(); + g_warning("Unable to import 'geany' module"); + return NULL; + } + + man->py_obj = PyObject_GetAttrString(module, "signals"); + Py_DECREF(module); + if (!man->py_obj) + { + if (PyErr_Occurred()) + PyErr_Print(); + g_warning("Unable to get 'SignalManager' instance from 'geany' module."); + return NULL; + } + man->obj = pygobject_get(man->py_obj); + + signal_manager_connect_signals(man); + + return man; +} + + +void signal_manager_free(SignalManager *man) +{ + g_return_if_fail(man != NULL); + Py_XDECREF(man->py_obj); + g_free(man); +} + +GObject *signal_manager_get_gobject(SignalManager *signal_manager) +{ + return G_OBJECT(signal_manager->obj); +} + + +static void signal_manager_connect_signals(SignalManager *man) +{ + plugin_signal_connect(geany_plugin, NULL, "build-start", TRUE, G_CALLBACK(on_build_start), man); + plugin_signal_connect(geany_plugin, NULL, "document-activate", TRUE, G_CALLBACK(on_document_activate), man); + plugin_signal_connect(geany_plugin, NULL, "document-before-save", TRUE, G_CALLBACK(on_document_before_save), man); + plugin_signal_connect(geany_plugin, NULL, "document-close", TRUE, G_CALLBACK(on_document_close), man); + plugin_signal_connect(geany_plugin, NULL, "document-filetype-set", TRUE, G_CALLBACK(on_document_filetype_set), man); + plugin_signal_connect(geany_plugin, NULL, "document-new", TRUE, G_CALLBACK(on_document_new), man); + plugin_signal_connect(geany_plugin, NULL, "document-open", TRUE, G_CALLBACK(on_document_open), man); + plugin_signal_connect(geany_plugin, NULL, "document-reload", TRUE, G_CALLBACK(on_document_reload), man); + plugin_signal_connect(geany_plugin, NULL, "document-save", TRUE, G_CALLBACK(on_document_save), man); + plugin_signal_connect(geany_plugin, NULL, "editor-notify", TRUE, G_CALLBACK(on_editor_notify), man); + plugin_signal_connect(geany_plugin, NULL, "geany-startup-complete", TRUE, G_CALLBACK(on_geany_startup_complete), man); + plugin_signal_connect(geany_plugin, NULL, "project-close", TRUE, G_CALLBACK(on_project_close), man); + plugin_signal_connect(geany_plugin, NULL, "project-dialog-confirmed", TRUE, G_CALLBACK(on_project_dialog_confirmed), man); + plugin_signal_connect(geany_plugin, NULL, "project-dialog-open", TRUE, G_CALLBACK(on_project_dialog_open), man); + plugin_signal_connect(geany_plugin, NULL, "project-dialog-close", TRUE, G_CALLBACK(on_project_dialog_close), man); + plugin_signal_connect(geany_plugin, NULL, "project-open", TRUE, G_CALLBACK(on_project_open), man); + plugin_signal_connect(geany_plugin, NULL, "project-save", TRUE, G_CALLBACK(on_project_save), man); + plugin_signal_connect(geany_plugin, NULL, "update-editor-menu", TRUE, G_CALLBACK(on_update_editor_menu), man); +} + +static void on_build_start(GObject *geany_object, SignalManager *man) +{ + g_signal_emit_by_name(man->obj, "build-start"); +} + + +static void on_document_event(GObject *geany_object, GeanyDocument *doc, SignalManager *man, const gchar *signal_name) +{ + PyObject *py_doc = (PyObject *) Document_create_new_from_geany_document(doc); + g_signal_emit_by_name(man->obj, signal_name, py_doc); + Py_XDECREF(py_doc); +} + + +static void on_document_activate(GObject *geany_object, GeanyDocument *doc, SignalManager *man) +{ + on_document_event(geany_object, doc, man, "document-activate"); +} + + +static void on_document_before_save(GObject *geany_object, GeanyDocument *doc, SignalManager *man) +{ + on_document_event(geany_object, doc, man, "document-before-save"); +} + + +static void on_document_close(GObject *geany_object, GeanyDocument *doc, SignalManager *man) +{ + on_document_event(geany_object, doc, man, "document-close"); +} + + +static void on_document_filetype_set(GObject *geany_object, GeanyDocument *doc, GeanyFiletype *filetype_old, SignalManager *man) +{ + PyObject *py_doc, *py_ft; + py_doc = (PyObject *) Document_create_new_from_geany_document(doc); + py_ft = (PyObject *) Filetype_create_new_from_geany_filetype(filetype_old); + g_signal_emit_by_name(man->obj, "document-filetype-set", py_doc, py_ft); + Py_XDECREF(py_doc); + Py_XDECREF(py_ft); +} + + +static void on_document_new(GObject *geany_object, GeanyDocument *doc, SignalManager *man) +{ + on_document_event(geany_object, doc, man, "document-new"); +} + + +static void on_document_open(GObject *geany_object, GeanyDocument *doc, SignalManager *man) +{ + on_document_event(geany_object, doc, man, "document-open"); +} + + +static void on_document_reload(GObject *geany_object, GeanyDocument *doc, SignalManager *man) +{ + on_document_event(geany_object, doc, man, "document-reload"); +} + + +static void on_document_save(GObject *geany_object, GeanyDocument *doc, SignalManager *man) +{ + on_document_event(geany_object, doc, man, "document-save"); +} + + +static gboolean on_editor_notify(GObject *geany_object, GeanyEditor *editor, SCNotification *nt, SignalManager *man) +{ + gboolean res = FALSE; + PyObject *py_ed, *py_notif; + py_ed = (PyObject *) Editor_create_new_from_geany_editor(editor); + py_notif = (PyObject *) Notification_create_new_from_scintilla_notification(nt); + g_signal_emit_by_name(man->obj, "editor-notify", py_ed, py_notif, &res); + Py_XDECREF(py_ed); + Py_XDECREF(py_notif); + return res; +} + + +static void on_geany_startup_complete(GObject *geany_object, SignalManager *man) +{ + g_signal_emit_by_name(man->obj, "geany-startup-complete"); +} + + +static void on_project_close(GObject *geany_object, SignalManager *man) +{ + g_signal_emit_by_name(man->obj, "project-close"); +} + + +static void on_project_dialog_confirmed(GObject *geany_object, GtkWidget *notebook, SignalManager *man) +{ + PyObject *gob = (PyObject *) pygobject_new(G_OBJECT(notebook)); + g_signal_emit_by_name(man->obj, "project-dialog-confirmed", gob); + Py_XDECREF(gob); +} + + +static void on_project_dialog_open(GObject *geany_object, GtkWidget *notebook, SignalManager *man) +{ + PyObject *gob = (PyObject *) pygobject_new(G_OBJECT(notebook)); + g_signal_emit_by_name(man->obj, "project-dialog-open", gob); + Py_XDECREF(gob); +} + +static void on_project_dialog_close(GObject *geany_object, GtkWidget *notebook, SignalManager *man) +{ + PyObject *gob = (PyObject *) pygobject_new(G_OBJECT(notebook)); + g_signal_emit_by_name(man->obj, "project-dialog-close", gob); + Py_XDECREF(gob); +} + + +static void on_project_open(GObject *geany_object, GKeyFile *config, SignalManager *man) +{ + PyObject *py_proj = (PyObject *) GEANYPY_NEW(Project); + g_signal_emit_by_name(man->obj, "project-open", py_proj); + Py_XDECREF(py_proj); +} + + +static void on_project_save(GObject *geany_object, GKeyFile *config, SignalManager *man) +{ + PyObject *py_proj = (PyObject *) GEANYPY_NEW(Project); + g_signal_emit_by_name(man->obj, "project-save", py_proj); + Py_XDECREF(py_proj); +} + + +static void on_update_editor_menu(GObject *geany_object, const gchar *word, gint pos, GeanyDocument *doc, SignalManager *man) +{ + PyObject *py_doc = (PyObject *) Document_create_new_from_geany_document(doc); + g_signal_emit_by_name(man->obj, "update-editor-menu", word, pos, py_doc); + Py_XDECREF(py_doc); +} diff --git a/geanypy/src/geanypy-signalmanager.h b/geanypy/src/geanypy-signalmanager.h new file mode 100644 index 000000000..886ff25c9 --- /dev/null +++ b/geanypy/src/geanypy-signalmanager.h @@ -0,0 +1,10 @@ +#ifndef SIGNALMANAGER_H +#define SIGNALMANAGER_H + +typedef struct _SignalManager SignalManager; + +SignalManager *signal_manager_new(GeanyPlugin *geany_plugin); +void signal_manager_free(SignalManager *signal_manager); +GObject *signal_manager_get_gobject(SignalManager *signal_manager); + +#endif /* SIGNALMANAGER_H */ diff --git a/geanypy/src/geanypy-templates.c b/geanypy/src/geanypy-templates.c new file mode 100644 index 000000000..be44515be --- /dev/null +++ b/geanypy/src/geanypy-templates.c @@ -0,0 +1,102 @@ +#include "geanypy.h" + +/* TODO: see if the TemplatePrefs members are safe to modify. */ + +typedef struct +{ + PyObject_HEAD + GeanyTemplatePrefs *template_prefs; +} TemplatePrefs; + + +static void +TemplatePrefs_dealloc(TemplatePrefs *self) +{ + g_return_if_fail(self != NULL); + self->ob_type->tp_free((PyObject *) self); +} + + +static int +TemplatePrefs_init(TemplatePrefs *self) +{ + g_return_val_if_fail(self != NULL, -1); + self->template_prefs = geany_data->template_prefs; + return 0; +} + + +static PyObject * +TemplatePrefs_get_property(TemplatePrefs *self, const gchar *prop_name) +{ + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(prop_name != NULL, NULL); + + if (!self->template_prefs) + { + PyErr_SetString(PyExc_RuntimeError, + "TemplatePrefs instance not initialized properly"); + return NULL; + } + + if (g_str_equal(prop_name, "company")) + return PyString_FromString(self->template_prefs->company); + else if (g_str_equal(prop_name, "developer")) + return PyString_FromString(self->template_prefs->developer); + else if (g_str_equal(prop_name, "initials")) + return PyString_FromString(self->template_prefs->initials); + else if (g_str_equal(prop_name, "mail")) + return PyString_FromString(self->template_prefs->mail); + else if (g_str_equal(prop_name, "version")) + return PyString_FromString(self->template_prefs->version); + + Py_RETURN_NONE; +} +GEANYPY_PROPS_READONLY(TemplatePrefs); + + +static PyGetSetDef TemplatePrefs_getseters[] = { + GEANYPY_GETSETDEF(TemplatePrefs, "company", ""), + GEANYPY_GETSETDEF(TemplatePrefs, "developer", ""), + GEANYPY_GETSETDEF(TemplatePrefs, "initials", ""), + GEANYPY_GETSETDEF(TemplatePrefs, "mail", "Email address"), + GEANYPY_GETSETDEF(TemplatePrefs, "version", "Initial version"), + { NULL } +}; + + +static PyTypeObject TemplatePrefsType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "geany.templates.TemplatePrefs", /* tp_name */ + sizeof(TemplatePrefs), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) TemplatePrefs_dealloc, /* tp_dealloc */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Wrapper around a GeanyTemplatePrefs structure.", /* tp_doc */ + 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ + TemplatePrefs_getseters, /* tp_getset */ + 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ + (initproc) TemplatePrefs_init, /* tp_init */ + 0, 0, /* tp_alloc - tp_new */ +}; + + +static PyMethodDef TemplatePrefsModule_methods[] = { { NULL } }; + + +PyMODINIT_FUNC inittemplates(void) +{ + PyObject *m; + + TemplatePrefsType.tp_new = PyType_GenericNew; + if (PyType_Ready(&TemplatePrefsType) < 0) + return; + + m = Py_InitModule3("templates", TemplatePrefsModule_methods, + "Template information and management."); + + Py_INCREF(&TemplatePrefsType); + PyModule_AddObject(m, "TemplatePrefs", (PyObject *) &TemplatePrefsType); +} diff --git a/geanypy/src/geanypy-uiutils.c b/geanypy/src/geanypy-uiutils.c new file mode 100644 index 000000000..58f63cb1a --- /dev/null +++ b/geanypy/src/geanypy-uiutils.c @@ -0,0 +1,449 @@ +#include "geanypy.h" + + +#define GOB_CHECK(pyobj, arg) \ + { \ + if (!pyobj || pyobj == Py_None || !pygobject_check(pyobj, PyGObject_Type)) \ + { \ + PyErr_SetString(PyExc_ValueError, \ + "argument " #arg " must inherit from a gobject.GObject type"); \ + return NULL; \ + } \ + } + +#define GOB_TYPE_CHECK(gob, gob_type, arg) \ + { \ + if (!gob || !G_IS_OBJECT(gob) || \ + !g_type_is_a(G_TYPE_FROM_INSTANCE(gob), gob_type)) \ + { \ + PyErr_SetString(PyExc_ValueError, \ + "argument " #arg " must inherit from a " #gob_type " type"); \ + return NULL; \ + } \ + } + + +static PyTypeObject *PyGObject_Type = NULL; + + +static PyObject * +UiUtils_hookup_widget(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *py_owner = NULL, *py_widget = NULL; + const gchar *widget_name = NULL; + GObject *owner = NULL, *widget = NULL; + static gchar *kwlist[] = { "owner", "widget", "widget_name", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "OOs", kwlist, + &py_owner, &py_widget, &widget_name)) + { + GOB_CHECK(py_owner, 1); + GOB_CHECK(py_widget, 2); + owner = pygobject_get(py_owner); + widget = pygobject_get(py_widget); + ui_hookup_widget(owner, widget, widget_name); + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_lookup_widget(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *py_widget = NULL; + const gchar *widget_name = NULL; + GObject *widget = NULL; + GtkWidget *found_widget = NULL; + static gchar *kwlist[] = { "widget", "widget_name", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "Os", kwlist, + &py_widget, &widget_name)) + { + GOB_CHECK(py_widget, 1); + widget = pygobject_get(py_widget); + GOB_TYPE_CHECK(widget, GTK_TYPE_WIDGET, 1); + found_widget = ui_lookup_widget(GTK_WIDGET(widget), widget_name); + if (GTK_IS_WIDGET(found_widget)) + return pygobject_new(G_OBJECT(found_widget)); + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_add_document_sensitive(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *py_widget = NULL; + GObject *widget = NULL; + static gchar *kwlist[] = { "widget", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &py_widget)) + { + GOB_CHECK(py_widget, 1); + widget = pygobject_get(py_widget); + GOB_TYPE_CHECK(widget, GTK_TYPE_WIDGET, 1); + ui_add_document_sensitive(GTK_WIDGET(widget)); + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_button_new_with_image(PyObject *module, PyObject *args, PyObject *kwargs) +{ + const gchar *stock_id = NULL, *text = NULL; + GtkWidget *button = NULL; + static gchar *kwlist[] = { "stock_id", "text", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &stock_id, &text)) + { + button = ui_button_new_with_image(stock_id, text); + if (GTK_IS_WIDGET(button)) + return pygobject_new(G_OBJECT(button)); + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_combo_box_add_to_history(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *py_cbo = NULL; + const gchar *text = NULL; + gint hist_len = 0; + GObject *widget = NULL; + static gchar *kwlist[] = { "combo_entry", "text", "history_len", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "Osi", kwlist, + &py_cbo, &text, &hist_len)) + { + GOB_CHECK(py_cbo, 1); + widget = pygobject_get(py_cbo); + GOB_TYPE_CHECK(widget, GTK_TYPE_COMBO_BOX_ENTRY, 1); + ui_combo_box_add_to_history(GTK_COMBO_BOX_ENTRY(widget), text, hist_len); + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_dialog_vbox_new(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *py_dlg; + GObject *dlg; + GtkWidget *widget; + static gchar *kwlist[] = { "dialog", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &py_dlg)) + { + GOB_CHECK(py_dlg, 1); + dlg = pygobject_get(py_dlg); + GOB_TYPE_CHECK(dlg, GTK_TYPE_DIALOG, 1); + widget = ui_dialog_vbox_new(GTK_DIALOG(dlg)); + if (GTK_IS_WIDGET(widget)) + return pygobject_new(G_OBJECT(widget)); + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_entry_add_clear_icon(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *py_ent = NULL; + GObject *ent; + static gchar *kwlist[] = { "entry", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &py_ent)) + { + GOB_CHECK(py_ent, 1); + ent = pygobject_get(py_ent); + GOB_TYPE_CHECK(ent, GTK_TYPE_ENTRY, 1); + ui_entry_add_clear_icon(GTK_ENTRY(ent)); + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_frame_new_with_alignment(PyObject *module, PyObject *args, PyObject *kwargs) +{ + const gchar *text = NULL; + static gchar *kwlist[] = { "label_text", NULL }; + GtkWidget *alignment = NULL, *frame = NULL; + PyObject *py_al = NULL, *py_fr = NULL, *ret; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &text)) + { + frame = ui_frame_new_with_alignment(text, &alignment); + py_al = (PyObject *) pygobject_new(G_OBJECT(frame)); + py_fr = (PyObject *) pygobject_new(G_OBJECT(alignment)); + ret = Py_BuildValue("OO", py_al, py_fr); + Py_DECREF(py_al); + Py_DECREF(py_fr); + return ret; + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_get_gtk_settings_integer(PyObject *module, PyObject *args, PyObject *kwargs) +{ + const gchar *prop_name = NULL; + gint default_value = 0; + static gchar *kwlist[] = { "property_name", "default_value", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "si", kwlist, &prop_name, + &default_value)) + { + return PyInt_FromLong( + (glong) ui_get_gtk_settings_integer(prop_name, default_value)); + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_image_menu_item_new(PyObject *module, PyObject *args, PyObject *kwargs) +{ + const gchar *stock_id = NULL, *label = NULL; + GtkWidget *ret = NULL; + static gchar *kwlist[] = { "stock_id", "label", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, + &stock_id, &label)) + { + ret = ui_image_menu_item_new(stock_id, label); + if (GTK_IS_WIDGET(ret)) + return pygobject_new(G_OBJECT(ret)); + } + + Py_RETURN_NONE; +} + + +static PyObject * +UiUtils_is_keyval_enter_or_return(PyObject *module, PyObject *args, PyObject *kwargs) +{ + guint kv; + static gchar *kwlist[] = { "keyval", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "I", kwlist, &kv)) + { + if (ui_is_keyval_enter_or_return(kv)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + Py_RETURN_NONE; +} + + +/* FIXME: ui_menu_add_document_items() and ui_menu_add_document_items_sorted() + * skipped because I don't know how to pass GCallbacks between Python and C, + * if it's even possible. */ + + + static PyObject * + UiUtils_path_box_new(PyObject *module, PyObject *args, PyObject *kwargs) + { + gint act; + PyObject *py_ent = NULL; + GObject *ent = NULL; + GtkWidget *pbox = NULL; + const gchar *title = NULL; + static gchar *kwlist[] = { "title", "action", "entry", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "ziO", kwlist, + &title, &act, &py_ent)) + { + GOB_CHECK(py_ent, 3); + ent = pygobject_get(py_ent); + GOB_TYPE_CHECK(ent, GTK_TYPE_ENTRY, 3); + pbox = ui_path_box_new(title, (GtkFileChooserAction) act, GTK_ENTRY(ent)); + if (GTK_IS_WIDGET(pbox)) + return pygobject_new(G_OBJECT(pbox)); + } + + + Py_RETURN_NONE; + } + + + static PyObject * + UiUtils_progress_bar_start(PyObject *module, PyObject *args, PyObject *kwargs) + { + const gchar *text = NULL; + static gchar *kwlist[] = { "text", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "z", kwlist, &text)) + ui_progress_bar_start(text); + + Py_RETURN_NONE; + } + + + static PyObject * + UiUtils_progress_bar_stop(PyObject *module) + { + ui_progress_bar_stop(); + Py_RETURN_NONE; + } + + + static PyObject * + UiUtils_set_statusbar(PyObject *module, PyObject *args, PyObject *kwargs) + { + gint log = 0; + const gchar *text = NULL; + static gchar *kwlist[] = { "text", "log", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist, &text, &log)) + ui_set_statusbar((gboolean) log, "%s", text); + + Py_RETURN_NONE; + } + + + /* FIXME: ui_table_add_row() skipped since it's probably not useful and + * not well documented. */ + + +static PyObject * +UiUtils_widget_modify_font_from_string(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *py_widget = NULL; + GObject *widget = NULL; + const gchar *font_str = NULL; + static gchar *kwlist[] = { "widget", "font_str", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "Os", kwlist, &py_widget, &font_str)) + { + GOB_CHECK(py_widget, 1); + widget = pygobject_get(py_widget); + GOB_TYPE_CHECK(widget, GTK_TYPE_WIDGET, 1); + ui_widget_modify_font_from_string(GTK_WIDGET(widget), font_str); + } + + Py_RETURN_NONE; +} + + +/* Deprecated in Geany 0.21 in favour of gtk_widget_set_tooltip_text() */ +#if 0 +static PyObject * +UiUtils_widget_set_tooltip_text(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *py_widget = NULL; + GObject *widget = NULL; + const gchar *text = NULL; + static gchar *kwlist[] = { "widget", "text", NULL }; + + if (PyArg_ParseTupleAndKeywords(args, kwargs, "Os", kwlist, &py_widget, &text)) + { + GOB_CHECK(py_widget, 1); + widget = pygobject_get(py_widget); + GOB_TYPE_CHECK(widget, GTK_TYPE_WIDGET, 1); + ui_widget_set_tooltip_text(GTK_WIDGET(widget), text); + } + + Py_RETURN_NONE; +} +#endif + + +static PyMethodDef UiUtilsModule_methods[] = { + { "hookup_widget", (PyCFunction) UiUtils_hookup_widget, METH_KEYWORDS, + "Sets a name to lookup widget from owner." }, + { "lookup_widget", (PyCFunction) UiUtils_lookup_widget, METH_KEYWORDS, + "Returns a widget from a name in a component, usually created " + "by Glade. Call it with the toplevel widget in the component " + "(ie. a window or dialog), or alternatively any widget in the " + "component, and the name of the widget you want returned."}, + { "add_document_sensitive", (PyCFunction) UiUtils_add_document_sensitive, METH_KEYWORDS, + "Adds a widget to the list of widgets that should be set " + "sensitive or insensitive depending if any documents are open." }, + { "button_new_with_image", (PyCFunction) UiUtils_button_new_with_image, METH_KEYWORDS, + "Creates a gtk.Button with custom text and a stock image similar " + "to gtk.Button() initializer." }, + { "combo_box_add_to_history", (PyCFunction) UiUtils_combo_box_add_to_history, METH_KEYWORDS, + "Prepends text to the dropdown list, removing a duplicate element " + "in the list if found. Also ensures there are less than the " + "specified number of elements in the history." }, + { "dialog_vbox_new", (PyCFunction) UiUtils_dialog_vbox_new, METH_KEYWORDS, + "Makes a fixed border for dialogs without increasing the button " + "box border size." }, + { "entry_add_clear_icon", (PyCFunction) UiUtils_entry_add_clear_icon, METH_KEYWORDS, + "Adds a small clear icon to the right end of the passed in entry. " + "A callback to clear the contents of the gtk.Entry is automatically " + "added." }, + { "frame_new_with_alignement", (PyCFunction) UiUtils_frame_new_with_alignment, METH_KEYWORDS, + "Creates a GNOME HIG-style frame with no border and indented " + "child alignment. Returns a tuple with the gtk.Frame as the first " + "element and the gtk.Alignment as the second element." }, + { "get_gtk_settings_integer", (PyCFunction) UiUtils_get_gtk_settings_integer, METH_KEYWORDS, + "Reads an integer from the GTK default settings registry." }, + { "image_menu_item_new", (PyCFunction) UiUtils_image_menu_item_new, METH_KEYWORDS, + "Creates a gtk.ImageMenuItem with a stock image and a custom label." }, + { "is_keyval_enter_or_return", (PyCFunction) UiUtils_is_keyval_enter_or_return, METH_KEYWORDS, + "Checks whether the passed in keyval is the Enter or Returns key. " }, + { "path_box_new", (PyCFunction) UiUtils_path_box_new, METH_KEYWORDS, + "Creates a gtk.HBox with entry packed into it and an open button " + "which runs a file chooser, replacing entry text (if successful) " + "with the path returned from the gtk.FileChooser." }, + { "progress_bar_start", (PyCFunction) UiUtils_progress_bar_start, METH_KEYWORDS, + "Starts a constantly pulsing progressbar in the right corner of " + "the statusbar (if the status bar is visible)." }, + { "progress_bar_stop", (PyCFunction) UiUtils_progress_bar_stop, METH_NOARGS, + "Stops a running progress bar and hides the widget again." }, + { "set_statusbar", (PyCFunction) UiUtils_set_statusbar, METH_KEYWORDS, + "Displays text on the statusbar." }, + { "widget_modify_font_from_string", (PyCFunction) UiUtils_widget_modify_font_from_string, METH_KEYWORDS, + "Modifies the font of a widget using modify_font() automatically " + "parsing the Pango font description string in font_str." }, + { NULL }, +}; + + +PyMODINIT_FUNC initui_utils(void) +{ + PyObject *m; + + init_pygobject(); + init_pygtk(); + m = PyImport_ImportModule("gobject"); + + if (m) + { + PyGObject_Type = (PyTypeObject *) PyObject_GetAttrString(m, "GObject"); + Py_XDECREF(m); + } + + InterfacePrefsType.tp_new = PyType_GenericNew; + if (PyType_Ready(&InterfacePrefsType) < 0) + return; + + MainWidgetsType.tp_new = PyType_GenericNew; + if (PyType_Ready(&MainWidgetsType) < 0) + return; + + m = Py_InitModule3("ui_utils", UiUtilsModule_methods, + "User interface information and utilities."); + + Py_INCREF(&InterfacePrefsType); + PyModule_AddObject(m, "InterfacePrefs", (PyObject *) &InterfacePrefsType); + + Py_INCREF(&MainWidgetsType); + PyModule_AddObject(m, "MainWidgets", (PyObject *) &MainWidgetsType); +} diff --git a/geanypy/src/geanypy-uiutils.h b/geanypy/src/geanypy-uiutils.h new file mode 100644 index 000000000..cb7915677 --- /dev/null +++ b/geanypy/src/geanypy-uiutils.h @@ -0,0 +1,19 @@ +#ifndef GEANYPY_UI_UTILS_H__ +#define GEANYPY_UI_UTILS_H__ + +PyTypeObject InterfacePrefsType; +PyTypeObject MainWidgetsType; + +typedef struct +{ + PyObject_HEAD + GeanyInterfacePrefs *iface_prefs; +} InterfacePrefs; + +typedef struct +{ + PyObject_HEAD + GeanyMainWidgets *main_widgets; +} MainWidgets; + +#endif /* GEANYPY_UI_UTILS_H__ */ diff --git a/geanypy/src/geanypy.h b/geanypy/src/geanypy.h new file mode 100644 index 000000000..5d37a91ed --- /dev/null +++ b/geanypy/src/geanypy.h @@ -0,0 +1,119 @@ +/* + * geanypy.h + * + * Copyright 2011 Matthew Brush + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + * + */ + +#ifndef GEANYPY_H__ +#define GEANYPY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* For plain make file build using Mingw on Windows */ +#if defined(__MINGW32__) && defined(GEANYPY_WINDOWS_BUILD) +# define GEANYPY_WINDOWS 1 +#endif + +#include +#ifndef PyMODINIT_FUNC +# define PyMODINIT_FUNC void +#endif +#include + + +/* Defines a setter that throws an attribute exception when called. */ +#define GEANYPY_PROPS_READONLY(cls) \ + static int \ + cls ## _set_property(cls *self, PyObject *value, const gchar *prop_name) { \ + PyErr_SetString(PyExc_AttributeError, "can't set attribute"); \ + return -1; } + +/* Defines a getter/setter for use in the tp_getset table. */ +#define GEANYPY_GETSETDEF(cls, prop_name, doc) \ + { prop_name, \ + (getter) cls ## _get_property, \ + (setter) cls ## _set_property, \ + doc, \ + prop_name } + + +/* Initializes a new `cls` type object. */ +#define GEANYPY_NEW(cls) \ + (cls *) PyObject_CallObject((PyObject *) &(cls ## Type), NULL); + + +/* Returns a new py string or py none if string is NULL. */ +#define GEANYPY_RETURN_STRING(memb) \ + { \ + if (memb != NULL) \ + return PyString_FromString(memb); \ + else \ + Py_RETURN_NONE; \ + } + + +#include +#include +#include + +#include +#include + +#ifndef GEANYPY_WINDOWS +/* Used with the results of `pkg-config --cflags pygtk-2.0` */ +# include +#else +/* On windows the path of pygtk.h is directly an include dir */ +# include +#endif + +#ifndef GTK +# define GTK +#endif +#include +#include + +#include + +#ifndef G_LOG_DOMAIN +# define G_LOG_DOMAIN "GeanyPy" +#endif + +#ifndef GEANYPY_WINDOWS +# include "config.h" +#endif + +#include "geanypy-document.h" +#include "geanypy-editor.h" +#include "geanypy-encoding.h" +#include "geanypy-filetypes.h" +#include "geanypy-plugin.h" +#include "geanypy-project.h" +#include "geanypy-scintilla.h" +#include "geanypy-signalmanager.h" +#include "geanypy-uiutils.h" + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* GEANYPY_H__ */ diff --git a/geanypy/src/makefile.win32 b/geanypy/src/makefile.win32 new file mode 100644 index 000000000..208aa1ef3 --- /dev/null +++ b/geanypy/src/makefile.win32 @@ -0,0 +1,38 @@ +include ../win32env.mk + +SOURCES = \ + geanypy-app.c \ + geanypy-dialogs.c \ + geanypy-document.c \ + geanypy-editor.c \ + geanypy-encoding.c \ + geanypy-filetypes.c \ + geanypy-highlighting.c \ + geanypy-indentprefs.c \ + geanypy-interfaceprefs.c \ + geanypy-main.c \ + geanypy-mainwidgets.c \ + geanypy-msgwindow.c \ + geanypy-navqueue.c \ + geanypy-plugin.c \ + geanypy-prefs.c \ + geanypy-project.c \ + geanypy-scinotification.c \ + geanypy-scinotifyheader.c \ + geanypy-scintilla.c \ + geanypy-search.c \ + geanypy-signalmanager.c \ + geanypy-templates.c \ + geanypy-uiutils.c + +OBJECTS = $(SOURCES:.c=.o) + + +geanypy.dll: $(OBJECTS) + $(CC) -shared -o $@ $^ $(_LDFLAGS) + +%.o: %.c + $(CC) $(_CFLAGS) -c -o $@ $< + +clean: + $(RM_F) geanypy.dll *.o diff --git a/po/POTFILES.in b/po/POTFILES.in index 381632bc9..cec69b8ca 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -184,6 +184,9 @@ geanyprj/src/sidebar.c geanyprj/src/utils.c geanyprj/src/xproject.c +# Geanypy +geanypy/src/geanypy-plugin.c + # geanysendmail geanysendmail/src/geanysendmail.c From 821565ebdb87c81f9ff9dc6587672fc35ae82d55 Mon Sep 17 00:00:00 2001 From: elextr Date: Sat, 15 Jun 2013 20:10:25 +1000 Subject: [PATCH 2/3] Fix Warnings Alias warnings by setting -fno-strict-aliasing flag, this disables the optimisation so is safe (and matches Python). String const warnings by -Wno-write-string flag, gcc notes that the warning may be useful, but can be annoying when const is not properly used on the pointers (defined by Python so not fixable). geanypy-uiutils.c combo box warning by updating to GtkComboBoxText to match changes to Geany API. --- geanypy/src/Makefile.am | 1 + geanypy/src/geanypy-uiutils.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/geanypy/src/Makefile.am b/geanypy/src/Makefile.am index f2b479772..a58676125 100644 --- a/geanypy/src/Makefile.am +++ b/geanypy/src/Makefile.am @@ -8,6 +8,7 @@ geanypy_la_CPPFLAGS = @GEANY_CFLAGS@ @PYGTK_CFLAGS@ @PYTHON_CPPFLAGS@ \ -DGEANYPY_PYTHON_DIR="\"$(libdir)/geany/geanypy\"" \ -DGEANYPY_PLUGIN_DIR="\"$(datadir)/geany/geanypy/plugins\"" \ -UHAVE_CONFIG_H +geanypy_la_CFLAGS = -fno-strict-aliasing -Wno-write-strings geanypy_la_LIBADD = @GEANY_LIBS@ @PYGTK_LIBS@ @PYTHON_LDFLAGS@ \ @PYTHON_EXTRA_LIBS@ @PYTHON_EXTRA_LDFLAGS@ geanypy_la_SOURCES = geanypy-app.c \ diff --git a/geanypy/src/geanypy-uiutils.c b/geanypy/src/geanypy-uiutils.c index 58f63cb1a..1c25d075a 100644 --- a/geanypy/src/geanypy-uiutils.c +++ b/geanypy/src/geanypy-uiutils.c @@ -123,8 +123,8 @@ UiUtils_combo_box_add_to_history(PyObject *module, PyObject *args, PyObject *kwa { GOB_CHECK(py_cbo, 1); widget = pygobject_get(py_cbo); - GOB_TYPE_CHECK(widget, GTK_TYPE_COMBO_BOX_ENTRY, 1); - ui_combo_box_add_to_history(GTK_COMBO_BOX_ENTRY(widget), text, hist_len); + GOB_TYPE_CHECK(widget, GTK_TYPE_COMBO_BOX_TEXT, 1); + ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(widget), text, hist_len); } Py_RETURN_NONE; From 81a775be774e0b5793cb1c7c26b52f98f1b95cb8 Mon Sep 17 00:00:00 2001 From: elextr Date: Sat, 15 Jun 2013 20:44:20 +1000 Subject: [PATCH 3/3] Remove pyc files from Git and stop them returning --- .gitignore | 1 + geanypy/geany/__init__.pyc | Bin 1779 -> 0 bytes geanypy/geany/console.pyc | Bin 19185 -> 0 bytes geanypy/geany/loader.pyc | Bin 5382 -> 0 bytes geanypy/geany/manager.pyc | Bin 6174 -> 0 bytes geanypy/geany/plugin.pyc | Bin 3083 -> 0 bytes geanypy/geany/signalmanager.pyc | Bin 1930 -> 0 bytes 7 files changed, 1 insertion(+) delete mode 100644 geanypy/geany/__init__.pyc delete mode 100644 geanypy/geany/console.pyc delete mode 100644 geanypy/geany/loader.pyc delete mode 100644 geanypy/geany/manager.pyc delete mode 100644 geanypy/geany/plugin.pyc delete mode 100644 geanypy/geany/signalmanager.pyc diff --git a/.gitignore b/.gitignore index 54561773a..015bec5b2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.so *.dll *.exe +*.pyc deps.mak .deps/ .libs/ diff --git a/geanypy/geany/__init__.pyc b/geanypy/geany/__init__.pyc deleted file mode 100644 index 66987f9a38c6ba1af7416c4d1c168ccd7188cefd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1779 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 Cui=LP diff --git a/geanypy/geany/loader.pyc b/geanypy/geany/loader.pyc deleted file mode 100644 index 20bce51881452618dd03dc7b0a160da903f8d58e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5382 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