├── .gitignore ├── COPYING ├── Makefile.am ├── README.markdown ├── README.win32 ├── autogen.sh ├── configure.ac ├── docs ├── Makefile ├── make.bat └── source │ ├── api.rst │ ├── app.rst │ ├── conf.py │ ├── dialogs.rst │ ├── document.rst │ ├── geany.rst │ ├── index.rst │ ├── install.rst │ ├── intro.rst │ ├── quickstart.rst │ └── starting.rst ├── geany ├── Makefile.am ├── __init__.py ├── console.py ├── logger.py ├── plugin.py └── signalmanager.py ├── geanypy.nsi ├── m4 ├── ax_python_devel.m4 └── ax_python_library.m4 ├── makefile.win32 ├── plugins ├── Makefile.am ├── console.py ├── demo.py └── hello.py ├── src ├── Makefile.am ├── 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-filetypes.h ├── geanypy-glog.c ├── geanypy-highlighting.c ├── geanypy-indentprefs.c ├── geanypy-interfaceprefs.c ├── geanypy-keybindings.c ├── geanypy-keybindings.h ├── 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 ├── geanypy.h └── makefile.win32 └── win32env.mk /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled objects 2 | *.o 3 | *.so 4 | *.a 5 | *.lib 6 | *.dll 7 | *.pyc 8 | *.exe 9 | 10 | # Libtool 11 | *.la 12 | *.lo 13 | 14 | # Autotools 15 | .deps/ 16 | .libs/ 17 | /Makefile 18 | /src/Makefile 19 | /geany/Makefile 20 | /plugins/Makefile 21 | Makefile.in 22 | /configure 23 | /aclocal.m4 24 | /autom4te.cache/ 25 | /config.guess 26 | /config.sub 27 | /config.log 28 | /config.status 29 | /config.h 30 | /depcomp 31 | /install-sh 32 | /ltmain.sh 33 | /m4/l*.m4 34 | /missing 35 | /libtool 36 | /build-aux/ 37 | /src/config.h* 38 | /src/stamp-h1 39 | 40 | # Project-specific 41 | /.backup/ 42 | /docs/build/* 43 | /docs/buildpushdocs.sh 44 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | SUBDIRS = src geany plugins 3 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | GeanyPy 2 | ======= 3 | 4 | Write Geany plugins in Python! 5 | 6 | *Please note:* since GeanyPy is under heavy development, the API it exposes 7 | may change quite drastically. If you start using GeanyPy to write plugins, 8 | drop me a line and I'll do my best to not break your code. 9 | 10 | *Update:* There's now some documentation available, visit 11 | http://codebrainz.github.com/geanypy to read it. 12 | 13 | How it works 14 | ------------ 15 | 16 | In the `src/` directory is a normal Geany plugin (`plugin.c`) which loads the 17 | Python interpreter. It then loads some C Python modules (`dialogs.c`, 18 | `documents.c`, etc.) that interface with Geany's C API. After that, it loads 19 | the `geany` Python module which is just glue/sugar-coating to make the C 20 | module more "Pythonic". 21 | 22 | To write a plugin, inherit from the `geany.Plugin` class and implmenent the 23 | required members (see `geany/plugin.py` documentation comments). Then put the 24 | plugin in a searched plugin path. Currently two locations are search for 25 | plugins. The first is `PREFIX/lib/geany` and the recommended 26 | location is under your personal Geany directory (usually 27 | `~/.config/geany/plugins`). To load or unload plugins, use Geany's regular Plugin 28 | Manager. Python plugins appear there once GeanyPy is activated. 29 | 30 | When the Python Console plugin is enabled, it will add a new tab to the notebook in 31 | the message window area that contains an interactive Python shell with the `geany` 32 | module pre-imported. You can tinker around with API with this console, for 33 | example: 34 | 35 | 36 | ```python 37 | import geany # called automatically in the built-in interactive shell 38 | 39 | doc = geany.document.open("/some/file/here") 40 | 41 | print(doc.file_name) 42 | print(doc.file_type.display_name) 43 | 44 | geany.dialogs.show_msgbox("Hello World") 45 | 46 | geany.main_widgets.window.set_title("Hello Window") 47 | 48 | def on_document_open(doc): 49 | print("Document '%s' was opened" % doc.file_name) 50 | 51 | geany.signals.connect('document-open', on_document_open) 52 | 53 | ``` 54 | 55 | Dependencies 56 | ------------ 57 | 58 | To build GeanyPy you need the following dependencies: 59 | 60 | * Python 2.6+ and development files (I don't think Python 3 will work). 61 | * Geany 0.21+ and development files (from SVN) 62 | * PyGTK 2.0 and development files 63 | 64 | On Debian/Ubuntu, the (non-Geany) dependencies can be installed like this: 65 | 66 | $ apt-get install python python-dev python-gtk2 python-gtk2-dev 67 | 68 | See Geany's documentation/website for information on compiling it from the 69 | Subversion or Git repositories. 70 | 71 | Installation 72 | ------------ 73 | 74 | First you need to know where Geany is installed: 75 | 76 | $ GEANY_PREFIX=`pkg-config --variable=prefix geany` 77 | 78 | The you can install as normal: 79 | 80 | $ ./autogen.sh 81 | $ ./configure --prefix=$GEANY_PREFIX 82 | $ make 83 | # make install 84 | 85 | You should be able to force a specific version of Python to be used, by using 86 | the `PYTHON_VERSION` environment variable, but I've only tested with 2.6.6 so 87 | far. I imagine 2.7 series will also work fine. 88 | 89 | Running on Windows 90 | ------------------ 91 | 92 | See `README.win32` for more information. 93 | -------------------------------------------------------------------------------- /README.win32: -------------------------------------------------------------------------------- 1 | GeanyPy on Windows 2 | ================== 3 | 4 | I'll try and keep a usable Windows build available for Windows users. To 5 | update your GeanyPy when a newer installer is made, run the uninstaller 6 | which is named `geanypy-uninstaller.exe` and can be found in the installation 7 | directory. Once the old version is uninstalled, run the updated installer and 8 | it will re-install the newer build of GeanyPy. 9 | 10 | Keep your plugins in your user directory, not in the system-wide installation 11 | directory and all settings and plugins should remain intact between upgrades. 12 | 13 | Before installing GeanyPy on Windows, make sure to install the dependencies: 14 | 15 | * [Geany 1.22][geany] 16 | * [Python 2.7.3][python] 17 | * [PyGTK+ 2.24.2 "All-in-one"][pygtk] 18 | 19 | Get the Geany installer for Windows *without* GTK+ bundled. A newer version 20 | will be installed with the PyGTK+ "all-in-one" installer. For PyGTK+ get 21 | one of the `pygtk-all-in-one-2.24.x.win32-py2.7.msi` files and it will install 22 | a newer/better GTK+ version than one included with the bundled Geany installer. 23 | That installer also takes care of other dependencies like PyGObject and PyCairo. 24 | For Python, any of the 2.7.x series should do fine. 25 | 26 | **Note:** In order for Geany to detect the GTK+ installation, you may need 27 | to add the GTK+ `bin` directory to your `PATH` environment variable. It's 28 | quite easy and there's [instructions online][setpath]. 29 | 30 | Once you have the dependencies installed, then download GeanPy from: 31 | 32 | https://github.com/downloads/codebrainz/geanypy/GeanyPy-Installer.exe 33 | 34 | Run the installer and when it asks you where to install the files to, select 35 | the directory where Geany was installed, usually: 36 | 37 | C:\Program Files\Geany 38 | 39 | Restart Geany and activate the GeanyPy plugin through the plugin manager. 40 | 41 | [geany]: http://www.geany.org/Download/Releases/ 42 | [python]: http://www.python.org/download/ 43 | [pygtk]: http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.24/ 44 | [setpath]: http://www.computerhope.com/issues/ch000549.htm 45 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | _mkdir() 4 | { 5 | test -d $@ || mkdir $@ 6 | } 7 | 8 | _mkdir build-aux || exit 1 9 | autoreconf -vfi || exit 1 10 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.62]) 2 | 3 | # Initialize build system tools 4 | AC_INIT([geanypy], [0.01], [mbrush@codebrainz.ca]) 5 | AC_CONFIG_AUX_DIR([build-aux]) 6 | AC_CONFIG_MACRO_DIR([m4]) 7 | AC_CONFIG_SRCDIR([src/geanypy-plugin.c]) 8 | AC_CONFIG_HEADERS([src/config.h]) 9 | AM_INIT_AUTOMAKE([1.11.1 -Wall foreign]) 10 | 11 | # Configure build system and tools 12 | AM_SILENT_RULES([yes]) 13 | 14 | # fix for automake >= 1.12 15 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 16 | 17 | # Check for utilities 18 | AC_PROG_CC 19 | AC_PROG_SED 20 | LT_INIT([disable-static]) 21 | 22 | # Check for headers, libraries and packages 23 | AC_CHECK_HEADERS([stdio.h string.h dlfcn.h]) 24 | PKG_CHECK_MODULES([GEANY], [geany >= 0.21]) 25 | PKG_CHECK_MODULES([PYGTK], [pygtk-2.0]) 26 | PKG_CHECK_MODULES([GMODULE], [gmodule-2.0]) 27 | AX_PYTHON_DEVEL([>= '2.6']) 28 | AX_PYTHON_LIBRARY(,[AC_MSG_ERROR([Cannot determine location of the Python DSO])]) 29 | AC_SUBST([PYTHON]) 30 | 31 | # Puts these in the configuration header 32 | AC_DEFINE_UNQUOTED( 33 | [GEANYPY_PYTHON_LIBRARY], 34 | ["$PYTHON_LIBRARY"], 35 | [Location of Python library to dlopen()]) 36 | 37 | # Generate build files 38 | AC_CONFIG_FILES([ 39 | Makefile 40 | src/Makefile 41 | geany/Makefile 42 | plugins/Makefile 43 | ]) 44 | AC_OUTPUT 45 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | htmlhelp: 65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 66 | @echo 67 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 68 | ".hhp project file in $(BUILDDIR)/htmlhelp." 69 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GeanyPy.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GeanyPy.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/GeanyPy" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GeanyPy" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | if NOT "%PAPER%" == "" ( 11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 12 | ) 13 | 14 | if "%1" == "" goto help 15 | 16 | if "%1" == "help" ( 17 | :help 18 | echo.Please use `make ^` where ^ is one of 19 | echo. html to make standalone HTML files 20 | echo. dirhtml to make HTML files named index.html in directories 21 | echo. singlehtml to make a single large HTML file 22 | echo. pickle to make pickle files 23 | echo. json to make JSON files 24 | echo. htmlhelp to make HTML files and a HTML help project 25 | echo. qthelp to make HTML files and a qthelp project 26 | echo. devhelp to make HTML files and a Devhelp project 27 | echo. epub to make an epub 28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 29 | echo. text to make text files 30 | echo. man to make manual pages 31 | echo. changes to make an overview over all changed/added/deprecated items 32 | echo. linkcheck to check all external links for integrity 33 | echo. doctest to run all doctests embedded in the documentation if enabled 34 | goto end 35 | ) 36 | 37 | if "%1" == "clean" ( 38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 39 | del /q /s %BUILDDIR%\* 40 | goto end 41 | ) 42 | 43 | if "%1" == "html" ( 44 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 45 | if errorlevel 1 exit /b 1 46 | echo. 47 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 48 | goto end 49 | ) 50 | 51 | if "%1" == "dirhtml" ( 52 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 53 | if errorlevel 1 exit /b 1 54 | echo. 55 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 56 | goto end 57 | ) 58 | 59 | if "%1" == "singlehtml" ( 60 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 61 | if errorlevel 1 exit /b 1 62 | echo. 63 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 64 | goto end 65 | ) 66 | 67 | if "%1" == "pickle" ( 68 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 69 | if errorlevel 1 exit /b 1 70 | echo. 71 | echo.Build finished; now you can process the pickle files. 72 | goto end 73 | ) 74 | 75 | if "%1" == "json" ( 76 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished; now you can process the JSON files. 80 | goto end 81 | ) 82 | 83 | if "%1" == "htmlhelp" ( 84 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished; now you can run HTML Help Workshop with the ^ 88 | .hhp project file in %BUILDDIR%/htmlhelp. 89 | goto end 90 | ) 91 | 92 | if "%1" == "qthelp" ( 93 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 97 | .qhcp project file in %BUILDDIR%/qthelp, like this: 98 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\GeanyPy.qhcp 99 | echo.To view the help file: 100 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\GeanyPy.ghc 101 | goto end 102 | ) 103 | 104 | if "%1" == "devhelp" ( 105 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 106 | if errorlevel 1 exit /b 1 107 | echo. 108 | echo.Build finished. 109 | goto end 110 | ) 111 | 112 | if "%1" == "epub" ( 113 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 117 | goto end 118 | ) 119 | 120 | if "%1" == "latex" ( 121 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 122 | if errorlevel 1 exit /b 1 123 | echo. 124 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 125 | goto end 126 | ) 127 | 128 | if "%1" == "text" ( 129 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 130 | if errorlevel 1 exit /b 1 131 | echo. 132 | echo.Build finished. The text files are in %BUILDDIR%/text. 133 | goto end 134 | ) 135 | 136 | if "%1" == "man" ( 137 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 141 | goto end 142 | ) 143 | 144 | if "%1" == "changes" ( 145 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.The overview file is in %BUILDDIR%/changes. 149 | goto end 150 | ) 151 | 152 | if "%1" == "linkcheck" ( 153 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Link check complete; look for any errors in the above output ^ 157 | or in %BUILDDIR%/linkcheck/output.txt. 158 | goto end 159 | ) 160 | 161 | if "%1" == "doctest" ( 162 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Testing of doctests in the sources finished, look at the ^ 166 | results in %BUILDDIR%/doctest/output.txt. 167 | goto end 168 | ) 169 | 170 | :end 171 | -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | API Documentation 2 | ***************** 3 | 4 | GeanyPy's API mimics quite closely Geany's C plugin API. The following 5 | documentation is broken down by file/module: 6 | 7 | The :mod:`geany` modules: 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | app.rst 13 | dialogs.rst 14 | document.rst 15 | 16 | The :mod:`geany` package and module 17 | =================================== 18 | 19 | .. module:: geany 20 | 21 | All of GeanyPy's bindings are inside the :mod:`geany` package which also 22 | contains some stuff in it's :mod:`__init__` file, acting like a module itself. 23 | 24 | 25 | .. data:: app 26 | 27 | An instance of :class:`app.App` to store application information. 28 | 29 | .. data:: main_widgets 30 | 31 | An instance of :class:`mainwidgets.MainWidgets` to provide access to 32 | Geany's important GTK+ widgets. 33 | 34 | .. data:: signals 35 | 36 | An instance of :class:`signalmanager.SignalManager` which manages the 37 | connection, disconnection, and emission of Geany's signals. You can 38 | use this as follows:: 39 | 40 | geany.signals.connect('document-open', some_callback_function) 41 | 42 | .. function:: is_realized() 43 | 44 | This function, which is actually in the :mod:`geany.main` module will tell 45 | you if Geany's main window is realized (shown). 46 | 47 | .. function:: locale_init() 48 | 49 | Again, from the :mod:`geany.main` module, this will initialize the `gettext` 50 | translation system. 51 | 52 | .. function:: reload_configuration() 53 | 54 | Also from the :mod:`geany.main` module, this function will cause Geany to 55 | reload most if it's configuration files without restarting. 56 | 57 | Currently the following files are reloaded: 58 | 59 | * all template files 60 | * new file templates 61 | * the New (with template) menus will be updated 62 | * Snippets (snippets.conf) 63 | * filetype extensions (filetype_extensions.conf) 64 | * `settings` and `build_settings` sections of the filetype definition files. 65 | 66 | Plugins may call this function if they changed any of these files (e.g. a 67 | configuration file editor plugin). 68 | -------------------------------------------------------------------------------- /docs/source/app.rst: -------------------------------------------------------------------------------- 1 | The :mod:`app` module 2 | ********************* 3 | 4 | .. module:: app 5 | :synopsis: Application settings 6 | 7 | This modules contains a class to access application settings. 8 | 9 | :class:`App` Objects 10 | ==================== 11 | 12 | .. class:: App 13 | 14 | This class is initialized automatically and by the :mod:`geany` module and 15 | shouldn't be initalized by users. An instance of it is available through 16 | the :data:`geany.app` attribute of the :mod:`geany` module. 17 | 18 | All members of the :class:`App` are read-only properties. 19 | 20 | .. attribute:: App.configdir 21 | 22 | User configuration directory, usually ~/.config/geany. To store configuration 23 | files for your plugin, it's a good idea to use something like this:: 24 | 25 | conf_path = os.path.join(geany.app.configdir, "plugins", "yourplugin", 26 | "yourconfigfile.conf") 27 | 28 | .. attribute:: App.debug_mode 29 | 30 | If True, debug messages should be printed. For example, if you want to make 31 | a :py:func:`print` function that only prints when :attr:`App.debug_mode` 32 | is active, you could do something like this:: 33 | 34 | def debug_print(message): 35 | if geany.app.debug_mode: 36 | print(message) 37 | 38 | .. attribute:: App.project 39 | 40 | If not :py:obj:`None`, the a :class:`project.Project` for currently active project. 41 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # GeanyPy documentation build configuration file, created by 4 | # sphinx-quickstart on Sun Aug 7 12:42:52 2011. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.insert(0, os.path.abspath('.')) 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | 23 | # If your documentation needs a minimal Sphinx version, state it here. 24 | #needs_sphinx = '1.0' 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be extensions 27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 28 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] 29 | 30 | # Add any paths that contain templates here, relative to this directory. 31 | templates_path = ['_templates'] 32 | 33 | # The suffix of source filenames. 34 | source_suffix = '.rst' 35 | 36 | # The encoding of source files. 37 | #source_encoding = 'utf-8-sig' 38 | 39 | # The master toctree document. 40 | master_doc = 'index' 41 | 42 | # General information about the project. 43 | project = u'GeanyPy' 44 | copyright = u'2011, Matthew Brush (mbrush [at] codebrainz [dot] ca)' 45 | 46 | # The version info for the project you're documenting, acts as replacement for 47 | # |version| and |release|, also used in various other places throughout the 48 | # built documents. 49 | # 50 | # The short X.Y version. 51 | version = '1.0' 52 | # The full version, including alpha/beta/rc tags. 53 | release = '1.0' 54 | 55 | # The language for content autogenerated by Sphinx. Refer to documentation 56 | # for a list of supported languages. 57 | #language = None 58 | 59 | # There are two options for replacing |today|: either, you set today to some 60 | # non-false value, then it is used: 61 | #today = '' 62 | # Else, today_fmt is used as the format for a strftime call. 63 | #today_fmt = '%B %d, %Y' 64 | 65 | # List of patterns, relative to source directory, that match files and 66 | # directories to ignore when looking for source files. 67 | exclude_patterns = [] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'sphinx' 85 | 86 | # A list of ignored prefixes for module index sorting. 87 | #modindex_common_prefix = [] 88 | 89 | 90 | # -- Options for HTML output --------------------------------------------------- 91 | 92 | # The theme to use for HTML and HTML Help pages. See the documentation for 93 | # a list of builtin themes. 94 | html_theme = 'default' 95 | 96 | # Theme options are theme-specific and customize the look and feel of a theme 97 | # further. For a list of options available for each theme, see the 98 | # documentation. 99 | #html_theme_options = {} 100 | 101 | # Add any paths that contain custom themes here, relative to this directory. 102 | #html_theme_path = [] 103 | 104 | # The name for this set of Sphinx documents. If None, it defaults to 105 | # " v documentation". 106 | #html_title = None 107 | 108 | # A shorter title for the navigation bar. Default is the same as html_title. 109 | #html_short_title = None 110 | 111 | # The name of an image file (relative to this directory) to place at the top 112 | # of the sidebar. 113 | #html_logo = None 114 | 115 | # The name of an image file (within the static path) to use as favicon of the 116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 117 | # pixels large. 118 | #html_favicon = None 119 | 120 | # Add any paths that contain custom static files (such as style sheets) here, 121 | # relative to this directory. They are copied after the builtin static files, 122 | # so a file named "default.css" will overwrite the builtin "default.css". 123 | html_static_path = ['_static'] 124 | 125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 126 | # using the given strftime format. 127 | #html_last_updated_fmt = '%b %d, %Y' 128 | 129 | # If true, SmartyPants will be used to convert quotes and dashes to 130 | # typographically correct entities. 131 | #html_use_smartypants = True 132 | 133 | # Custom sidebar templates, maps document names to template names. 134 | #html_sidebars = {} 135 | 136 | # Additional templates that should be rendered to pages, maps page names to 137 | # template names. 138 | #html_additional_pages = {} 139 | 140 | # If false, no module index is generated. 141 | #html_domain_indices = True 142 | 143 | # If false, no index is generated. 144 | #html_use_index = True 145 | 146 | # If true, the index is split into individual pages for each letter. 147 | #html_split_index = False 148 | 149 | # If true, links to the reST sources are added to the pages. 150 | #html_show_sourcelink = True 151 | 152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 153 | #html_show_sphinx = True 154 | 155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 156 | #html_show_copyright = True 157 | 158 | # If true, an OpenSearch description file will be output, and all pages will 159 | # contain a tag referring to it. The value of this option must be the 160 | # base URL from which the finished HTML is served. 161 | #html_use_opensearch = '' 162 | 163 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 164 | #html_file_suffix = None 165 | 166 | # Output file base name for HTML help builder. 167 | htmlhelp_basename = 'GeanyPydoc' 168 | 169 | 170 | # -- Options for LaTeX output -------------------------------------------------- 171 | 172 | # The paper size ('letter' or 'a4'). 173 | #latex_paper_size = 'letter' 174 | 175 | # The font size ('10pt', '11pt' or '12pt'). 176 | #latex_font_size = '10pt' 177 | 178 | # Grouping the document tree into LaTeX files. List of tuples 179 | # (source start file, target name, title, author, documentclass [howto/manual]). 180 | latex_documents = [ 181 | ('index', 'GeanyPy.tex', u'GeanyPy Documentation', 182 | u'Matthew Brush \\textless{}mbrush@codebrainz.ca\\textgreater{}', 'manual'), 183 | ] 184 | 185 | # The name of an image file (relative to this directory) to place at the top of 186 | # the title page. 187 | #latex_logo = None 188 | 189 | # For "manual" documents, if this is true, then toplevel headings are parts, 190 | # not chapters. 191 | #latex_use_parts = False 192 | 193 | # If true, show page references after internal links. 194 | #latex_show_pagerefs = False 195 | 196 | # If true, show URL addresses after external links. 197 | #latex_show_urls = False 198 | 199 | # Additional stuff for the LaTeX preamble. 200 | #latex_preamble = '' 201 | 202 | # Documents to append as an appendix to all manuals. 203 | #latex_appendices = [] 204 | 205 | # If false, no module index is generated. 206 | #latex_domain_indices = True 207 | 208 | 209 | # -- Options for manual page output -------------------------------------------- 210 | 211 | # One entry per manual page. List of tuples 212 | # (source start file, name, description, authors, manual section). 213 | man_pages = [ 214 | ('index', 'geanypy', u'GeanyPy Documentation', 215 | [u'Matthew Brush '], 1) 216 | ] 217 | -------------------------------------------------------------------------------- /docs/source/dialogs.rst: -------------------------------------------------------------------------------- 1 | The :mod:`dialogs` module 2 | ************************* 3 | 4 | .. module:: dialogs 5 | :synopsis: Showing dialogs to the user 6 | 7 | This module contains some help functions to show file-related dialogs, 8 | miscellaneous dialogs, etc. You can of course just use the :mod:`gtk` module 9 | to create your own dialogs as well. 10 | 11 | .. function:: show_input([title=None[, parent=None[, label_text=None[, default_text=None]]]]) 12 | 13 | Shows a :class:`gtk.Dialog` to ask the user for text input. 14 | 15 | :param title: The window title for the dialog. 16 | :param parent: The parent :class:`gtk.Window` for the dialog, for example :data:`geany.main_widgets.window`. 17 | :param label_text: Text to put in the label just about the input entry box. 18 | :param default_text: Default text to put in the input entry box. 19 | 20 | :return: A string containing the text the user entered. 21 | 22 | .. function:: show_input_numeric([title=None[, label_text=None[, value=0.0[, minimum=0.0[, maximum=100.0[, step=1.0]]]]]]) 23 | 24 | Shows a :class:`gtk.Dialog` to ask the user to enter a numeric value 25 | using a :class:`gtk.SpinButton`. 26 | 27 | :param title: The window title for the dialog. 28 | :param label_text: Text to put in the label just about the numeric entry box. 29 | :param value: The initial value in the numeric entry box. 30 | :param minimum: The minimum allowed value that can be entered. 31 | :param maximum: The maximum allowed value that can be entered. 32 | :param step: Amount to increment the numeric entry when it's value is moved up or down (ex, using arrows). 33 | 34 | :return: The value entered if the dialog was closed with ok, or :data:`None` if it was cancelled. 35 | 36 | .. function:: show_msgbox(text[, msgtype=gtk.MESSAGE_INFO]) 37 | 38 | Shows a :class:`gtk.Dialog` to show the user a message. 39 | 40 | :param text: The text to show in the message box. 41 | :param msgtype: The message type which is one of the `Gtk Message Type Constants `_. 42 | 43 | .. function:: show_question(text) 44 | 45 | Shows a :class:`gtk.Dialog` to ask the user a Yes/No question. 46 | 47 | :param text: The text to show in the question dialog. 48 | 49 | :return: :data:`True` if the Yes button was pressed, :data:`False` if the No button was pressed. 50 | 51 | .. function:: show_save_as() 52 | 53 | Shows Geany's `Save As` dialog. 54 | 55 | :return: :data:`True` if the file was saved, :data:`False` otherwise. 56 | -------------------------------------------------------------------------------- /docs/source/document.rst: -------------------------------------------------------------------------------- 1 | The :mod:`document` module 2 | ************************** 3 | 4 | .. module:: document 5 | :synopsis: Document-related functions and classes 6 | 7 | This module provides functions for working with documents. Most of the module-level 8 | functions are used for creating instances of the :class:`Document` object. 9 | 10 | 11 | .. function:: find_by_filename(filename) 12 | 13 | Finds a document with the given filename from the open documents. 14 | 15 | :param filename: Filename of :class:`Document` to find. 16 | 17 | :return: A :class:`Document` instance for the found document or :data:`None`. 18 | 19 | .. function:: get_current() 20 | 21 | Gets the currently active document. 22 | 23 | :return: A :class:`Document` instance for the currently active document or :data:`None` if no documents are open. 24 | 25 | .. function:: get_from_page(page_num) 26 | 27 | Gets a document based on it's :class:`gtk.Notebook` page number. 28 | 29 | :param page_num: The tab number of the document in the documents notebook. 30 | 31 | :return: A :class:`Document` instance for the corresponding document or :data:`None` if no document matched. 32 | 33 | .. function:: index(index) 34 | 35 | Gets a document based on its index in Geany's documents array. 36 | 37 | :param index: The index of the document in Geany's documents array. 38 | 39 | :return: A :class:`Document` instance for the corresponding document or :data:`None` if not document matched, or the document that matched isn't valid. 40 | 41 | .. function:: new_file([filename=None[, filetype=None[, text=None]]]) 42 | 43 | Creates a document file. 44 | 45 | :param filename: The documents filename, or :data:`None` for `untitled`. 46 | :param filetype: The documents filetype or :data:`None` to auto-detect it from `filename` (if it's not :data:`None`) 47 | :param text: Initial text to put in the new document or :data:`None` to leave it blank 48 | 49 | :return: A :class:`Document` instance for the new document. 50 | 51 | .. function:: open_file(filename[, read_only=False[, filetype=None[, forced_enc=None]]]) 52 | 53 | Open an existing document file. 54 | 55 | :param filename: Filename of the document to open. 56 | :param read_only: Whether to open the document in read-only mode. 57 | :param filetype: Filetype to open the document as or :data:`None` to detect it automatically. 58 | :param forced_enc: The file encoding to use or :data:`None` to auto-detect it. 59 | 60 | :return: A :class:`Document` instance for the opened document or :data:`None` if it couldn't be opened. 61 | 62 | .. function:: open_files(filenames, read_only=False, filetype="", forced_enc="") 63 | 64 | Open multiple files. This actually calls :func:`open_file` once for each filename in `filenames`. 65 | 66 | :param filenames: List of filenames to open. 67 | :param read_only: Whether to open the document in read-only mode. 68 | :param filetype: Filetype to open the document as or :data:`None` to detect it automatically. 69 | :param forced_enc: The file encoding to use or :data:`None` to auto-detect it. 70 | 71 | .. function:: remove_page(page_num) 72 | 73 | Remove a document from the documents array based on it's page number in the documents notebook. 74 | 75 | :param page_num: The tab number of the document in the documents notebook. 76 | 77 | :return: :data:`True` if the document was actually removed or :data:`False` otherwise. 78 | 79 | .. function:: get_documents_list() 80 | 81 | Get a list of open documents. 82 | 83 | :return: A list of :class:`Document` instances, one for each open document. 84 | 85 | 86 | :class:`Document` Objects 87 | ========================= 88 | 89 | .. class:: Document 90 | 91 | The main class holding information about a specific document. Unless 92 | otherwise noted, the attributes are read-only properties. 93 | 94 | .. attribute:: Document.basename_for_display 95 | 96 | The last part of the filename for this document, possibly truncated to a maximum length in case the filename is very long. 97 | 98 | .. attribute:: Document.notebook_page 99 | 100 | The page number in the :class:`gtk.Notebook` containing documents. 101 | 102 | .. attribute:: Document.status_color 103 | 104 | 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. 105 | 106 | .. attribute:: Document.encoding 107 | 108 | The encoding of this document. Must be a valid string representation of an encoding. This property is read-write. 109 | 110 | .. attribute:: Document.file_type 111 | 112 | The file type of this document as a :class:`Filetype` instance. This property is read-write. 113 | 114 | .. attribute:: Document.text_changed 115 | 116 | Whether this document's text has been changed since it was last saved. 117 | 118 | .. attribute:: Document.file_name 119 | 120 | The file name of this document. 121 | 122 | .. attribute:: Document.has_bom 123 | 124 | Indicates whether the document's file has a byte-order-mark. 125 | 126 | .. attribute:: Document.has_tags 127 | 128 | Indicates whether this document supports source code symbols (tags) to show in the sidebar. 129 | 130 | .. attribute:: Document.index 131 | 132 | Index of the document in Geany's documents array. 133 | 134 | .. attribute:: Document.is_valid 135 | 136 | Indicates whether this document is active and all properties are set correctly. 137 | 138 | .. attribute:: Document.read_only 139 | 140 | Whether the document is in read-only mode. 141 | 142 | .. attribute:: Document.real_path 143 | 144 | The link-dereferenced, locale-encoded file name for this document. 145 | 146 | .. attribute:: Document.editor 147 | 148 | The :class:`Editor` instance associated with this document. 149 | 150 | .. method:: Document.close() 151 | 152 | Close this document. 153 | 154 | :return: :data:`True` if the document was closed, :data:`False` otherwise. 155 | 156 | .. method:: Document.reload([forced_enc=None]) 157 | 158 | Reloads this document. 159 | 160 | :param forced_enc: The encoding to use when reloading this document or :data:`None` to auto-detect it. 161 | 162 | :return: :data:`True` if the document was actually reloaded or :data:`False` otherwise. 163 | 164 | .. method:: Document.rename(new_filename) 165 | 166 | Rename this document to a new file name. Only the file on disk is actually 167 | renamed, you still have to call :meth:`save_as` to change the document object. 168 | It also stops monitoring for file changes to prevent receiving too many file 169 | change events while renaming. File monitoring is setup again in :meth:`save_as`. 170 | 171 | :param new_filename: The new filename to rename to. 172 | 173 | .. method:: Document.save([force=False]) 174 | 175 | Saves this documents file on disk. 176 | 177 | Saving may include replacing tabs by spaces, stripping trailing spaces and adding 178 | a final new line at the end of the file, depending on user preferences. Then, 179 | the `document-before-save` signal is emitted, allowing plugins to modify the 180 | document before it's saved, and the data is actually written to disk. The 181 | file type is set again or auto-detected if it wasn't set yet. Afterwards, 182 | the `document-save` signal is emitted for plugins. If the file is not modified, 183 | this method does nothing unless `force` is set to :data:`True`. 184 | 185 | **Note:** You should ensure that :attr:`file_name` is not :data:`None` before 186 | calling this; otherwise call :func:`dialogs.show_save_as`. 187 | 188 | :param force: Whether to save the document even if it's not modified. 189 | 190 | :return: :data:`True` if the file was saved or :data:`False` if the file could not or should not be saved. 191 | 192 | .. method:: Document.save_as(new_filename) 193 | 194 | Saves the document with a new filename, detecting the filetype. 195 | 196 | :param new_filename: The new filename. 197 | 198 | :return: :data:`True` if the file was saved or :data:`False` if it could not be saved. 199 | 200 | -------------------------------------------------------------------------------- /docs/source/geany.rst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebrainz/geanypy/ae185fe9ebaa1a553b74bb6c5e01b57ff863dabb/docs/source/geany.rst -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. GeanyPy documentation master file, created by 2 | sphinx-quickstart on Sun Aug 7 12:42:52 2011. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to GeanyPy's documentation! 7 | =================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | intro.rst 15 | install.rst 16 | starting.rst 17 | quickstart.rst 18 | api.rst 19 | 20 | Indices and tables 21 | ================== 22 | 23 | * :ref:`genindex` 24 | * :ref:`modindex` 25 | * :ref:`search` 26 | 27 | -------------------------------------------------------------------------------- /docs/source/install.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ************ 3 | 4 | Currently there are no binary packages available for installing GeanyPy so it 5 | must be installed from source. The following instructions will describe how 6 | to do this. 7 | 8 | Getting the Source 9 | ================== 10 | 11 | The best way currently to get GeanyPy is to check it out from `it's repository 12 | on GitHub.com `_. You can clone 13 | GeanyPy's `master` branch by issuing the following command in a directory 14 | where you want to store its source code:: 15 | 16 | $ git clone git://github.com/codebrainz/geanypy.git 17 | $ cd geanypy 18 | 19 | Alternatively, you can download the `master` branch 20 | `compressed into a tarball 21 | `_ 22 | or `zip file `_. Then 23 | extract it where you want to store GeanyPy's source, for example:: 24 | 25 | $ cd ~/src 26 | $ wget -O geanypy.tar.gz https://github.com/codebrainz/geanypy/tarball/master 27 | $ tar xf geanypy.tar.gz 28 | $ cd codebrainz-geanypy-* 29 | 30 | The first method using `Git `_ is the best, since it 31 | allows you to update your copy of GeanyPy quite easily and also makes it 32 | easier to contribute back to the GeanyPy project if you want to. 33 | 34 | Dependencies and where to get them 35 | ================================== 36 | 37 | Of course depending on what operating system and distribution you're using, 38 | getting setup for this process may vary wildly. At present, the following 39 | dependencies are required to compile GeanyPy: 40 | 41 | GCC, Autotools, and all the usual build tools 42 | --------------------------------------------- 43 | 44 | For example on Debian (Ubuntu, Mint, etc.) do:: 45 | 46 | $ apt-get install build-essential 47 | 48 | Or on Fedora, something like this should do:: 49 | 50 | $ yum groupinstall "Development Tools" "Legacy Software Development" 51 | 52 | The latest development version of Geany (0.21+) 53 | ----------------------------------------------- 54 | 55 | Since GeanyPy is wrapping the current development version of Geany, to use it 56 | you are required to use that version of Geany. Until the next Geany release, 57 | you must either checkout the source code from Geany's 58 | `Subversion repository `_ or 59 | `Git mirror `_ or you can get one of the 60 | `Nightly builds `_ if you'd rather not compile 61 | it yourself. 62 | 63 | For more information on installing Geany, please refer to 64 | `Geany's excellent manual 65 | `_ 66 | 67 | Grabbing the dependencies for Geany on a Debian-based disto could be similar to 68 | this:: 69 | 70 | $ apt-get install libgtk2.0-0 libgtk2.0-0-dev 71 | 72 | Or you might even have better luck with:: 73 | 74 | $ apt-get build-dep geany 75 | 76 | A quick session for installing Geany on a Debian-based distro might look 77 | something like this:: 78 | 79 | $ cd ~/src 80 | $ git clone http://git.geany.org/git/geany 81 | $ cd geany 82 | $ ./autogen.sh 83 | $ ./configure 84 | $ make 85 | $ make install # possibly as root 86 | 87 | By default, Geany will install into `/usr/local` so if you want to install it 88 | somewhere else, for example `/opt/geany`, then you would run the `configure` 89 | command above with the `prefix` argument, like:: 90 | 91 | $ ./configure --prefix=/opt/geany 92 | 93 | It's important when installing GeanyPy later that you configure it with the 94 | same prefix where Geany is installed, otherwise Geany won't find the GeanyPy 95 | plugin. 96 | 97 | Python 2.X and development files 98 | -------------------------------- 99 | 100 | As GeanPy makes use of Python's C API to gain access to Geany's C plugin API, 101 | both Python and the development files are required to compile GeanyPy. In 102 | theory, any Python version >= 2.6 and < 3.0 should be compatible with GeanyPy. 103 | You can download Python `from its website `_ or 104 | you can install the required packages using your distro's package manager, for 105 | example with Debian-based distros, run this:: 106 | 107 | $ apt-get install python python-dev 108 | 109 | **Note:** Python 3.0+ is not supported yet, although at some point in the 110 | future, there are plans support it. 111 | 112 | PyGTK and development files 113 | --------------------------- 114 | 115 | Since Geany uses GTK+ as it's UI toolkit, GeanyPy uses PyGTK to interact with 116 | Geany's UI. You can either `download PyGTK from it's website 117 | `_ or you can install it with your 118 | system's pacakge manager, for example in Debian distros:: 119 | 120 | $ apt-get install python-gtk2 python-gtk2-dev 121 | 122 | **Note:** Although PyGTK is all but deprecated (or is completely deprecated?) 123 | in favour of the newer and shinier PyGobject/GObject-introspection, it is 124 | still used in new code in GeanyPy due to lack of documentation and pacakge 125 | support for the newer stuff. 126 | 127 | One fell swoop 128 | -------------- 129 | 130 | If you're running a Debian-based distro, you should be able to install all 131 | the required dependencies, not including Geany itself, with the following 132 | command (as root):: 133 | 134 | $ apt-get install build-essential libgtk2.0-0 libgtk2.0-dev \ 135 | python python-dev python-gtk2 python-gtk2-dev 136 | 137 | And finally ... installing GeanyPy 138 | ================================== 139 | 140 | Once all the dependencies are satisfied, installing GeanyPy should be pretty 141 | straight-forward, continuing on from `Getting the Source`_ above:: 142 | 143 | $ ./autogen.sh 144 | $ ./configure --prefix=/the/same/prefix/used/for/geany 145 | $ make 146 | $ make install # possibly as root 147 | -------------------------------------------------------------------------------- /docs/source/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ************ 3 | 4 | GeanyPy allows people to write their Geany plugins in Python making 5 | authoring a plugin much more accessible to non C programmers. What follows 6 | is a description of installing and using the GeanyPy plugin, paving the way 7 | for the rest of the documentation to covert the details of programming with 8 | the GeanyPy bindings of the Geany API. 9 | -------------------------------------------------------------------------------- /docs/source/quickstart.rst: -------------------------------------------------------------------------------- 1 | Writing a Plugin - Quick Start Guide 2 | ************************************ 3 | 4 | This is just a quick tutorial to describe writing a GeanyPy compatible plugin 5 | in Python. Writing a plugin should be pretty straightforward for any Python 6 | programmers, especially those familiar with writing `regular C plugins for 7 | Geany `_. 8 | 9 | To illustrate the similarities to the C API, the example at the end of this 10 | section will create the same plugin as in Geany's Plugin Howto, except 11 | obviously written in Python. 12 | 13 | The Plugin Interface 14 | ==================== 15 | 16 | The first thing any plugin will want to do is to import the `geany` module:: 17 | 18 | import geany 19 | 20 | **Note:** Due to the way the Geany plugin framework works, importing the 21 | `geany` module will certain fail if you just try running it standalone, outside 22 | of Geany/GeanyPy. 23 | 24 | After that, you create a regular Python class which inherits from the 25 | `geany.Plugin` class:: 26 | 27 | import geany 28 | 29 | class HelloWorld(geany.Plugin): 30 | pass 31 | 32 | This will allow GeanyPy's Python Plugin Manager to locate the plugin as a 33 | GeanyPy plugin. If it doesn't inherit from `geany.Plugin` it will not be 34 | detected. 35 | 36 | There are a few more parts of the interface that must be implemented in order 37 | for the plugin to be detected by GeanyPy:: 38 | 39 | import geany 40 | 41 | class HelloWorld(geany.Plugin): 42 | 43 | __plugin_name__ = "HelloWorld" # required 44 | __plugin_version__ = "version of your plugin" 45 | __plugin_description__ = "description of your plugin" 46 | __plugin_author__ = "Your Name " 47 | 48 | These allow the Python Plugin Manager to glean information about your plugin 49 | which will be shown in the managers plugin list. All but the `__plugin_name__` 50 | attributes are optional, though recommended. 51 | 52 | The next thing that's needed is an entry-point to the plugin. Since Python 53 | classes have a initialization method already, this seems like a logical 54 | entry-point:: 55 | 56 | import geany 57 | 58 | class HelloWorld(geany.Plugin): 59 | 60 | __plugin_name__ = "HelloWorld" # required 61 | __plugin_version__ = "version of your plugin" 62 | __plugin_description__ = "description of your plugin" 63 | __plugin_author__ = "Your Name " 64 | 65 | def __init__(self): 66 | do_stuff_when_loaded() 67 | 68 | If you have some de-initialization code that needs to be run, you can add 69 | a `cleanup` method to the class that is guaranteed to be called when your 70 | plugin is unloaded, however it's optional:: 71 | 72 | import geany 73 | 74 | class HelloWorld(geany.Plugin): 75 | 76 | __plugin_name__ = "HelloWorld" # required 77 | __plugin_version__ = "version of your plugin" 78 | __plugin_description__ = "description of your plugin" 79 | __plugin_author__ = "Your Name " 80 | 81 | def __init__(self): 82 | do_stuff_when_loaded() 83 | 84 | def cleanup(self): 85 | do_stuff_when_unloaded() 86 | 87 | And there you have it! That's the minimum requirements for writing a plugin 88 | that will be detected by GeanyPy. Ok, it doesn't do anything yet, but it 89 | will be shown in the Python Plugin Manager and can be loaded and unloaded. 90 | 91 | Real-world Example 92 | ================== 93 | 94 | To put it into context, here's a plugin that mimics the plugin in 95 | `Geany's Plugin Howto `_:: 96 | 97 | import gtk 98 | import geany 99 | 100 | class HelloWorld(geany.Plugin): 101 | 102 | __plugin_name__ = "HelloWorld" 103 | __plugin_version__ = "1.0" 104 | __plugin_description__ = "Just another tool to say hello world" 105 | __plugin_author__ = "John Doe " 106 | 107 | def __init__(self): 108 | self.menu_item = gtk.MenuItem("Hello World") 109 | self.menu_item.show() 110 | geany.main_widgets.tools_menu.append(self.menu_item) 111 | self.menu_item.connect("activate", self.on_hello_item_clicked) 112 | 113 | def cleanup(self): 114 | self.menu_item.destroy() 115 | 116 | def on_hello_item_clicked(widget, data): 117 | geany.dialogs.show_msgbox("Hello World") 118 | 119 | Hopefully this makes it clear how to write a Python plugin using GeanyPy. This 120 | sample plugin is provided with GeanyPy if you want to try it out. 121 | 122 | Logging 123 | ======= 124 | 125 | GeanyPy provides a logging adapter to log from Python to GLib's logging system 126 | which enables plugins to log messages to Geany's Help->Debug Messages window. 127 | To use it, simply call the base class' constructor in your __init__() method 128 | and use the new `self.logger` attribute. 129 | The logger attribute of `geany.Plugin` emulates a Python `logging.Logger` object 130 | and provides its logging functions: 131 | 132 | * **log** (*lvl, msg, \*args, \*\*kwargs*) 133 | * **debug** (*msg, \*args, \*\*kwargs*) 134 | * **info** (*msg, \*args, \*\*kwargs*) 135 | * **message** (*msg, \*args, \*\*kwargs*) 136 | * **warning** (*msg, \*args, \*\*kwargs*) 137 | * **error** (*msg, \*args, \*\*kwargs*) 138 | * **exception** (*msg, \*args, \*\*kwargs*) 139 | * **critical** (*msg, \*args, \*\*kwargs*) 140 | 141 | The function `message` maps to the GLib log level `G_LOG_LEVEL_MESSAGE`which does not 142 | exist in Python but still can be used. 143 | The keyword argument `exc_info` is supported for all logger methods with 144 | the same semantics as in Python (see 145 | `Python logging documentation `_ for details). 146 | However, the keyword argument `extra` is not supported and will be ignored. 147 | Since you cannot define your own formatter for the GLib logging system, passing 148 | the `extra` keyword argument does not make any sense. 149 | 150 | Here is an example:: 151 | 152 | import geany 153 | 154 | class HelloWorldLogger(geany.Plugin): 155 | 156 | __plugin_name__ = "HelloWorldLogger" 157 | __plugin_version__ = "1.0" 158 | __plugin_description__ = "Just another tool to log hello world" 159 | __plugin_author__ = "John Doe " 160 | 161 | def __init__(self): 162 | geany.Plugin.__init__(self) 163 | self.logger.info(u'Hello World') 164 | 165 | def cleanup(self): 166 | self.logger.debug(u'Bye Bye from HelloWorldLogger') 167 | -------------------------------------------------------------------------------- /docs/source/starting.rst: -------------------------------------------------------------------------------- 1 | Getting Started 2 | *************** 3 | 4 | Before diving into the details and API docs for programming plugins with 5 | GeanyPy, it's important to note how it works and some features it provides. 6 | 7 | What the heck is GeanyPy, really? 8 | ================================= 9 | 10 | GeanyPy is a proxy plugin. Geany initially sees GeanyPy as any other 11 | `plugin `_, but 12 | GeanyPy registers some additional stuff that enables Geany to load python plugins 13 | through GeanyPy. So to activate, use Geany's 14 | `Plugin Manager `_ 15 | under the Tools menu as you would for any other plugin. 16 | 17 | Once the GeanyPy plugin has been activated, Geany should rescan the plugin 18 | directories and pick those up that are supported through GeanyPy. It'll integrate 19 | the python plugins into the Plugin Manager in an additional hierarchy level below 20 | GeanyPy. 21 | 22 | * [ ] Geany plugin 1 23 | * [x] GeanyPy 24 | * [ ] Python plugin 1 25 | * [x] Python plugin 2 26 | * [ ] Python plugin 3 27 | * [ ] Geany plugin 3 28 | 29 | Remember that Geany looks in three places for plugins: 30 | 31 | 1. For system-wide plugins, it will search in (usually) /usr/share/geany or /usr/local/share/geany. 32 | 2. In Geany's config directory under your home directory, typically ~/.config/geany/plugins. 33 | 3. A user-configurable plugin directory (useful during plugin development). 34 | 35 | Python Console 36 | ============== 37 | 38 | Another pretty cool feature of GeanyPy is the Python Console, which similar 39 | to the regular Python interactive interpreter console, but it's found in the 40 | Message Window area (bottom) in Geany. The `geany` Python module used to 41 | interact with Geany will be pre-imported for you, so you can mess around with 42 | Geany using the console, without ever having to even write a plugin. 43 | 44 | **Credits:** The Python Console was taken, almost in its entirety, from the 45 | `medit text editor `_. Props to the 46 | author(s) for such a nice `piece of source code 47 | `_ 48 | 49 | Future Plans 50 | ============ 51 | 52 | Some time in the near future, there should be support for sending text from 53 | the active document into the Python Console. It will also be possible to 54 | have the Python Console either in a separate window, in the sidebar notebook 55 | or in the message window notebook. 56 | 57 | Also, either integration with Geany's keybindings UI under the preferences 58 | dialog or a separate but similar UI just for Python plugins will be added. 59 | Currently using keybindings requires a certain amount of hackery, due to 60 | Geany expecting all plugins to be shared libraries written in C. 61 | -------------------------------------------------------------------------------- /geany/Makefile.am: -------------------------------------------------------------------------------- 1 | geanypy_sources = __init__.py \ 2 | console.py \ 3 | logger.py \ 4 | plugin.py \ 5 | signalmanager.py 6 | geanypy_objects = $(geanypy_sources:.py=.pyc) 7 | geanypydir = $(libdir)/geany/geanypy/geany 8 | geanypy_DATA = $(geanypy_sources) $(geanypy_objects) 9 | 10 | EXTRA_DIST = $(geanypy_sources) 11 | CLEANFILES = $(geanypy_objects) 12 | 13 | .SUFFIXES = .pyc .py 14 | 15 | .py.pyc: 16 | $(AM_V_GEN) \ 17 | $(PYTHON) -c "from py_compile import compile; compile('$<', '$@')" 18 | -------------------------------------------------------------------------------- /geany/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Package file that exposes some of Geany's guts as its own attibutes. Any 3 | objects where it only makes sense to have one instance of are initialed here 4 | and set as attributes. 5 | 6 | You can sort of think of this file as the GeanyData struct from the C API, 7 | though no real attempt is made to mimic that structure here. 8 | """ 9 | 10 | import app 11 | import console 12 | import dialogs 13 | import document 14 | import editor 15 | import encoding 16 | import filetypes 17 | import highlighting 18 | import glog 19 | import main 20 | import msgwindow 21 | import navqueue 22 | import prefs 23 | import project 24 | import scintilla 25 | import search 26 | import templates 27 | import ui_utils 28 | import keybindings 29 | 30 | from app import App 31 | from prefs import Prefs, ToolPrefs 32 | from main import is_realized, locale_init, reload_configuration 33 | from signalmanager import SignalManager 34 | from ui_utils import MainWidgets, InterfacePrefs 35 | from search import SearchPrefs 36 | from templates import TemplatePrefs 37 | 38 | 39 | __all__ = [ "Plugin", 40 | "is_realized", 41 | "locale_init", 42 | "reload_configuration", 43 | "main_widgets", 44 | "interface_prefs", 45 | "app", 46 | "glog", 47 | "keybindings", 48 | "general_prefs", 49 | "search_prefs", 50 | "template_prefs", 51 | "tool_prefs", 52 | "signals" ] 53 | 54 | # Geany's application data fields 55 | app = App() 56 | 57 | # Import GTK+ widgets that are part of Geany's UI 58 | main_widgets = MainWidgets() 59 | 60 | # Preferences 61 | general_prefs = Prefs() # GeanyData->prefs but name clashes with module 62 | interface_prefs = InterfacePrefs() 63 | search_prefs = SearchPrefs() 64 | template_prefs = TemplatePrefs() 65 | tool_prefs = ToolPrefs() 66 | 67 | # GObject to connect signal handlers on and which emits signals. 68 | signals = SignalManager() 69 | 70 | import plugin 71 | from plugin import Plugin 72 | -------------------------------------------------------------------------------- /geany/logger.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from geany import glog 4 | from traceback import format_exception 5 | import logging 6 | import sys 7 | 8 | 9 | GLIB_LOG_LEVEL_MAP = { 10 | logging.DEBUG: glog.LOG_LEVEL_DEBUG, 11 | logging.INFO: glog.LOG_LEVEL_INFO, 12 | logging.WARNING: glog.LOG_LEVEL_WARNING, 13 | # error and critical levels are swapped on purpose because 14 | # GLib interprets CRITICAL less fatal than Python and additionally 15 | # GLib abort()s the program on G_LOG_LEVEL_ERROR which is uncommon 16 | # in Python 17 | logging.ERROR: glog.LOG_LEVEL_CRITICAL, 18 | logging.CRITICAL: glog.LOG_LEVEL_ERROR, 19 | } 20 | 21 | 22 | class PluginLogger(object): 23 | """ 24 | Convenience wrapper softly emulating Python's logging interface. 25 | Any log messages are passed to the GLib log system using g_log() 26 | and the LOG_DOMAIN is set automatically to the plugin's name for convenience. 27 | """ 28 | 29 | def __init__(self, plugin_name): 30 | self._log_domain = u'geanypy-%s' % plugin_name 31 | 32 | def debug(self, msg_format, *args, **kwargs): 33 | self.log(logging.DEBUG, msg_format, *args, **kwargs) 34 | 35 | def info(self, msg_format, *args, **kwargs): 36 | self.log(logging.INFO, msg_format, *args, **kwargs) 37 | 38 | def message(self, msg_format, *args, **kwargs): 39 | self.log(glog.LOG_LEVEL_MESSAGE, msg_format, *args, **kwargs) 40 | 41 | def warning(self, msg_format, *args, **kwargs): 42 | self.log(logging.WARNING, msg_format, *args, **kwargs) 43 | 44 | def error(self, msg_format, *args, **kwargs): 45 | self.log(logging.ERROR, msg_format, *args, **kwargs) 46 | 47 | def exception(self, msg_format, *args, **kwargs): 48 | kwargs['exc_info'] = True 49 | self.error(msg_format, *args, **kwargs) 50 | 51 | def critical(self, msg_format, *args, **kwargs): 52 | self.log(logging.CRITICAL, msg_format, *args, **kwargs) 53 | 54 | warn = warning 55 | fatal = critical 56 | 57 | def log(self, level, msg_format, *args, **kwargs): 58 | # map log level from Python to GLib 59 | glib_log_level = GLIB_LOG_LEVEL_MAP.get(level, glog.LOG_LEVEL_MESSAGE) 60 | # format message 61 | log_message = msg_format % args 62 | # log exception information if requested 63 | exc_info = kwargs.get('exc_info', False) 64 | if exc_info: 65 | traceback_text = self._format_exception(exc_info) 66 | log_message = '%s\n%s' % (log_message, traceback_text) 67 | 68 | glog.glog(self._log_domain, glib_log_level, log_message) 69 | 70 | def _format_exception(self, exc_info): 71 | if not isinstance(exc_info, tuple): 72 | exc_info = sys.exc_info() 73 | exc_text_lines = format_exception(*exc_info) 74 | return ''.join(exc_text_lines) 75 | -------------------------------------------------------------------------------- /geany/plugin.py: -------------------------------------------------------------------------------- 1 | """ 2 | All plugins need to import this module and inherit from the Plugin class. A 3 | basic plugin could look something like this: 4 | 5 | import geany 6 | from geany.plugin import Plugin 7 | 8 | class OpenNewDocumentPlugin(Plugin): 9 | 10 | __plugin_name__ = "Open new document plugin" 11 | 12 | _doc = None 13 | 14 | def __init__(self): 15 | Plugin.__init__(self) 16 | print "About to open a new document" 17 | self.open_document('/path/to/some/file') 18 | 19 | def open_document(self, filename): 20 | self._doc = geany.document.open(filename) 21 | 22 | def close_document(self): 23 | self._doc.close() 24 | self._doc = None 25 | 26 | def cleanup(self): 27 | if self._doc is not None: 28 | self._doc.close() 29 | 30 | The guts of the API are exposed to plugins through the `geany` package and 31 | its modules. 32 | 33 | Plugins should be placed in either the system plugin directory (something like 34 | /usr/local/lib/geany) or in the user plugin directory (something like 35 | ~/.config/geany/plugins). Only files with a `.py` extension will be loaded. 36 | """ 37 | 38 | 39 | from geany.logger import PluginLogger 40 | import keybindings 41 | 42 | class Plugin(object): 43 | """ 44 | Base class for all plugins. All plugins must inherit from this in order 45 | to be properly detected. 46 | """ 47 | 48 | # Child classes should implement these (at least __plugin__name__) 49 | #__plugin_name__ = None 50 | #__plugin_description__ = None 51 | #__plugin_version__ = None 52 | #__plugin_author__ = None 53 | 54 | _events = { 55 | "document-open": [], 56 | # TODO: add more events here 57 | } 58 | 59 | 60 | def __init__(self): 61 | """ 62 | When the plugin is loaded its __init__() function will be called 63 | so that's a good place to put plugin initialization code. 64 | """ 65 | self.logger = PluginLogger(self.name) 66 | 67 | 68 | def cleanup(self): 69 | """ 70 | When the plugin is unloaded the cleanup() function will be called, so 71 | it's a good place to put and clean-up/tear-down code. 72 | """ 73 | pass 74 | 75 | 76 | @property 77 | def __plugin_name__(self): 78 | """ 79 | Plugins must implement a __plugin_name__ attribute that returns the 80 | string name of the plugin. 81 | """ 82 | raise NotImplementedError( 83 | "Plugins must implement the __plugin_name__ attribute.") 84 | 85 | 86 | @property 87 | def name(self): 88 | """ 89 | Get plugin's name. 90 | """ 91 | return self.__plugin_name__ 92 | 93 | 94 | @property 95 | def description(self): 96 | """ 97 | Get plugin's description. 98 | """ 99 | if hasattr(self, '__plugin_description__'): 100 | return self.__plugin_description__ 101 | else: 102 | return "" 103 | 104 | 105 | @property 106 | def version(self): 107 | """ 108 | Get plugin's version. 109 | """ 110 | if hasattr(self, '__plugin_version__'): 111 | return self.__plugin_version__ 112 | else: 113 | return "" 114 | 115 | 116 | @property 117 | def author(self): 118 | """ 119 | Get plugin's author. 120 | """ 121 | if hasattr(self, '__plugin_author__'): 122 | return self.__plugin_author__ 123 | else: 124 | return "" 125 | 126 | def set_key_group(self, section_name, count, callback = None): 127 | """ 128 | Sets up a GeanyKeyGroup for this plugin. You can use that group to add keybindings 129 | with group.add_key_item(). 130 | """ 131 | return keybindings.set_key_group(self, section_name, count, callback) 132 | -------------------------------------------------------------------------------- /geany/signalmanager.py: -------------------------------------------------------------------------------- 1 | """ 2 | A simple analog of `GeanyObject` in the C API, that is, an object to emit 3 | all signals on. The signals are emitted from the C code in signalmanager.c, 4 | where the Geany types get wrapped in PyObject types. 5 | """ 6 | 7 | import gobject 8 | 9 | 10 | class SignalManager(gobject.GObject): 11 | """ 12 | Manages callback functions for events emitted by Geany's internal GObject. 13 | """ 14 | __gsignals__ = { 15 | 'build-start': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 16 | ()), 17 | 'document-activate': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 18 | (gobject.TYPE_PYOBJECT,)), 19 | 'document-before-save': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 20 | (gobject.TYPE_PYOBJECT,)), 21 | 'document-close': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 22 | (gobject.TYPE_PYOBJECT,)), 23 | 'document-filetype-set': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 24 | (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), 25 | 'document-new': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 26 | (gobject.TYPE_PYOBJECT,)), 27 | 'document-open': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 28 | (gobject.TYPE_PYOBJECT,)), 29 | 'document-reload': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 30 | (gobject.TYPE_PYOBJECT,)), 31 | 'document-save': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 32 | (gobject.TYPE_PYOBJECT,)), 33 | 'editor-notify': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, 34 | (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), 35 | 'geany-startup-complete': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 36 | ()), 37 | 'project-close': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 38 | ()), 39 | 'project-dialog-confirmed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 40 | (gobject.TYPE_PYOBJECT,)), 41 | 'project-dialog-open': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 42 | (gobject.TYPE_PYOBJECT,)), 43 | 'project-dialog-close': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 44 | (gobject.TYPE_PYOBJECT,)), 45 | 'project-open': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 46 | (gobject.TYPE_PYOBJECT,)), 47 | 'project-save': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 48 | (gobject.TYPE_PYOBJECT,)), 49 | 'update-editor-menu': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 50 | (gobject.TYPE_STRING, gobject.TYPE_INT, 51 | gobject.TYPE_PYOBJECT)), 52 | } # __gsignals__ 53 | 54 | def __init__(self): 55 | self.__gobject_init__() 56 | 57 | gobject.type_register(SignalManager) 58 | 59 | -------------------------------------------------------------------------------- /geanypy.nsi: -------------------------------------------------------------------------------- 1 | !include "MUI2.nsh" 2 | 3 | Name "GeanyPy Installer" 4 | OutFile "GeanyPy-Installer.exe" 5 | InstallDir $PROGRAMFILES\Geany 6 | DirText "This will install GeanyPy into your existing Geany directory. Make sure to choose the directory where Geany is installed below before proceeding." 7 | RequestExecutionLevel user 8 | 9 | !define _VERSION "0.1.0.0" 10 | VIProductVersion "${_VERSION}" 11 | VIAddVersionKey "ProductName" "GeanyPy Installer" 12 | VIAddVersionKey "CompanyName" "Matthew Brush" 13 | VIAddVersionKey "FileVersion" "${_VERSION}" 14 | VIAddVersionKey "InternalName" "GeanyPy-Installer.exe" 15 | VIAddVersionKey "FileDescription" "Installs GeanyPy into a Geany installation" 16 | VIAddVersionKey "LegalCopyright" "Copyright (c) 2012 Matthew Brush" 17 | 18 | !define MUI_ABORTWARNING 19 | !insertmacro MUI_PAGE_LICENSE "COPYING" 20 | !insertmacro MUI_PAGE_DIRECTORY 21 | !insertmacro MUI_PAGE_INSTFILES 22 | 23 | !insertmacro MUI_UNPAGE_CONFIRM 24 | !insertmacro MUI_UNPAGE_INSTFILES 25 | 26 | !insertmacro MUI_LANGUAGE "English" 27 | 28 | 29 | Section "" 30 | 31 | SetOutPath $INSTDIR\doc\geanypy 32 | File /oname=README.txt README.markdown 33 | File /oname=COPYING.txt COPYING 34 | 35 | SetOutPath $INSTDIR\lib 36 | File src\geanypy.dll 37 | 38 | SetOutPath $INSTDIR\lib\geanypy\geany 39 | File geany\*.py 40 | 41 | SetOutPath $INSTDIR\lib\geanypy\plugins 42 | File plugins\*.py 43 | 44 | WriteUninstaller "$INSTDIR\geanypy-uninstall.exe" 45 | 46 | SectionEnd 47 | 48 | Section "Uninstall" 49 | 50 | SetOutPath $INSTDIR 51 | 52 | Delete "$INSTDIR\geanypy-uninstall.exe" 53 | Delete "$INSTDIR\lib\geanypy.dll" 54 | Delete "$INSTDIR\lib\geanypy\geany\__init__.py" 55 | Delete "$INSTDIR\lib\geanypy\geany\__init__.pyc" 56 | Delete "$INSTDIR\lib\geanypy\geany\console.py" 57 | Delete "$INSTDIR\lib\geanypy\geany\loader.py" 58 | Delete "$INSTDIR\lib\geanypy\geany\loader.pyc" 59 | Delete "$INSTDIR\lib\geanypy\geany\logger.py" 60 | Delete "$INSTDIR\lib\geanypy\geany\logger.pyc" 61 | Delete "$INSTDIR\lib\geanypy\geany\manager.py" 62 | Delete "$INSTDIR\lib\geanypy\geany\manager.pyc" 63 | Delete "$INSTDIR\lib\geanypy\geany\plugin.py" 64 | Delete "$INSTDIR\lib\geanypy\geany\plugin.pyc" 65 | Delete "$INSTDIR\lib\geanypy\geany\signalmanager.pyc" 66 | Delete "$INSTDIR\lib\geanypy\geany\console.pyc" 67 | Delete "$INSTDIR\lib\geanypy\geany\demo.pyc" 68 | Delete "$INSTDIR\lib\geanypy\geany\hello.pyc" 69 | Delete "$INSTDIR\lib\geanypy\geany\signalmanager.py" 70 | Delete "$INSTDIR\lib\geanypy\plugins\console.py" 71 | Delete "$INSTDIR\lib\geanypy\plugins\demo.py" 72 | Delete "$INSTDIR\lib\geanypy\plugins\hello.py" 73 | Delete "$INSTDIR\doc\geanypy\README.txt" 74 | Delete "$INSTDIR\doc\geanypy\COPYING.txt" 75 | 76 | RMDir "$INSTDIR\lib\geanypy\geany" 77 | RMDir "$INSTDIR\lib\geanypy\plugins" 78 | RMDir "$INSTDIR\lib\geanypy" 79 | RMDir "$INSTDIR\doc\geanypy" 80 | 81 | 82 | SectionEnd 83 | -------------------------------------------------------------------------------- /m4/ax_python_devel.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_python_devel.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_PYTHON_DEVEL([version]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Note: Defines as a precious variable "PYTHON_VERSION". Don't override it 12 | # in your configure.ac. 13 | # 14 | # This macro checks for Python and tries to get the include path to 15 | # 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LIBS) output 16 | # variables. It also exports $(PYTHON_EXTRA_LIBS) and 17 | # $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. 18 | # 19 | # You can search for some particular version of Python by passing a 20 | # parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please 21 | # note that you *have* to pass also an operator along with the version to 22 | # match, and pay special attention to the single quotes surrounding the 23 | # version number. Don't use "PYTHON_VERSION" for this: that environment 24 | # variable is declared as precious and thus reserved for the end-user. 25 | # 26 | # This macro should work for all versions of Python >= 2.1.0. As an end 27 | # user, you can disable the check for the python version by setting the 28 | # PYTHON_NOVERSIONCHECK environment variable to something else than the 29 | # empty string. 30 | # 31 | # If you need to use this macro for an older Python version, please 32 | # contact the authors. We're always open for feedback. 33 | # 34 | # LICENSE 35 | # 36 | # Copyright (c) 2009 Sebastian Huber 37 | # Copyright (c) 2009 Alan W. Irwin 38 | # Copyright (c) 2009 Rafael Laboissiere 39 | # Copyright (c) 2009 Andrew Collier 40 | # Copyright (c) 2009 Matteo Settenvini 41 | # Copyright (c) 2009 Horst Knorr 42 | # Copyright (c) 2013 Daniel Mullner 43 | # 44 | # This program is free software: you can redistribute it and/or modify it 45 | # under the terms of the GNU General Public License as published by the 46 | # Free Software Foundation, either version 3 of the License, or (at your 47 | # option) any later version. 48 | # 49 | # This program is distributed in the hope that it will be useful, but 50 | # WITHOUT ANY WARRANTY; without even the implied warranty of 51 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 52 | # Public License for more details. 53 | # 54 | # You should have received a copy of the GNU General Public License along 55 | # with this program. If not, see . 56 | # 57 | # As a special exception, the respective Autoconf Macro's copyright owner 58 | # gives unlimited permission to copy, distribute and modify the configure 59 | # scripts that are the output of Autoconf when processing the Macro. You 60 | # need not follow the terms of the GNU General Public License when using 61 | # or distributing such scripts, even though portions of the text of the 62 | # Macro appear in them. The GNU General Public License (GPL) does govern 63 | # all other use of the material that constitutes the Autoconf Macro. 64 | # 65 | # This special exception to the GPL applies to versions of the Autoconf 66 | # Macro released by the Autoconf Archive. When you make and distribute a 67 | # modified version of the Autoconf Macro, you may extend this special 68 | # exception to the GPL to apply to your modified version as well. 69 | 70 | #serial 18 71 | 72 | AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) 73 | AC_DEFUN([AX_PYTHON_DEVEL],[ 74 | # 75 | # Allow the use of a (user set) custom python version 76 | # 77 | AC_ARG_VAR([PYTHON_VERSION],[The installed Python 78 | version to use, for example '2.3'. This string 79 | will be appended to the Python interpreter 80 | canonical name.]) 81 | 82 | AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) 83 | if test -z "$PYTHON"; then 84 | AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) 85 | PYTHON_VERSION="" 86 | fi 87 | 88 | # 89 | # Check for a version of Python >= 2.1.0 90 | # 91 | AC_MSG_CHECKING([for a version of Python >= '2.1.0']) 92 | ac_supports_python_ver=`$PYTHON -c "import sys; \ 93 | ver = sys.version.split ()[[0]]; \ 94 | print (ver >= '2.1.0')"` 95 | if test "$ac_supports_python_ver" != "True"; then 96 | if test -z "$PYTHON_NOVERSIONCHECK"; then 97 | AC_MSG_RESULT([no]) 98 | AC_MSG_FAILURE([ 99 | This version of the AC@&t@_PYTHON_DEVEL macro 100 | doesn't work properly with versions of Python before 101 | 2.1.0. You may need to re-run configure, setting the 102 | variables PYTHON_CPPFLAGS, PYTHON_LIBS, PYTHON_SITE_PKG, 103 | PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. 104 | Moreover, to disable this check, set PYTHON_NOVERSIONCHECK 105 | to something else than an empty string. 106 | ]) 107 | else 108 | AC_MSG_RESULT([skip at user request]) 109 | fi 110 | else 111 | AC_MSG_RESULT([yes]) 112 | fi 113 | 114 | # 115 | # if the macro parameter ``version'' is set, honour it 116 | # 117 | if test -n "$1"; then 118 | AC_MSG_CHECKING([for a version of Python $1]) 119 | ac_supports_python_ver=`$PYTHON -c "import sys; \ 120 | ver = sys.version.split ()[[0]]; \ 121 | print (ver $1)"` 122 | if test "$ac_supports_python_ver" = "True"; then 123 | AC_MSG_RESULT([yes]) 124 | else 125 | AC_MSG_RESULT([no]) 126 | AC_MSG_ERROR([this package requires Python $1. 127 | If you have it installed, but it isn't the default Python 128 | interpreter in your system path, please pass the PYTHON_VERSION 129 | variable to configure. See ``configure --help'' for reference. 130 | ]) 131 | PYTHON_VERSION="" 132 | fi 133 | fi 134 | 135 | # 136 | # Check if you have distutils, else fail 137 | # 138 | AC_MSG_CHECKING([for the distutils Python package]) 139 | ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` 140 | if test -z "$ac_distutils_result"; then 141 | AC_MSG_RESULT([yes]) 142 | else 143 | AC_MSG_RESULT([no]) 144 | AC_MSG_ERROR([cannot import Python module "distutils". 145 | Please check your Python installation. The error was: 146 | $ac_distutils_result]) 147 | PYTHON_VERSION="" 148 | fi 149 | 150 | # 151 | # Check for Python include path 152 | # 153 | AC_MSG_CHECKING([for Python include path]) 154 | if test -z "$PYTHON_CPPFLAGS"; then 155 | python_path=`$PYTHON -c "import distutils.sysconfig; \ 156 | print (distutils.sysconfig.get_python_inc ());"` 157 | plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ 158 | print (distutils.sysconfig.get_python_inc (plat_specific=1));"` 159 | if test -n "${python_path}"; then 160 | if test "${plat_python_path}" != "${python_path}"; then 161 | python_path="-I$python_path -I$plat_python_path" 162 | else 163 | python_path="-I$python_path" 164 | fi 165 | fi 166 | PYTHON_CPPFLAGS=$python_path 167 | fi 168 | AC_MSG_RESULT([$PYTHON_CPPFLAGS]) 169 | AC_SUBST([PYTHON_CPPFLAGS]) 170 | 171 | # 172 | # Check for Python library path 173 | # 174 | AC_MSG_CHECKING([for Python library path]) 175 | if test -z "$PYTHON_LIBS"; then 176 | # (makes two attempts to ensure we've got a version number 177 | # from the interpreter) 178 | ac_python_version=`cat<]], 299 | [[Py_Initialize();]]) 300 | ],[pythonexists=yes],[pythonexists=no]) 301 | AC_LANG_POP([C]) 302 | # turn back to default flags 303 | CPPFLAGS="$ac_save_CPPFLAGS" 304 | LIBS="$ac_save_LIBS" 305 | LDFLAGS="$ac_save_LDFLAGS" 306 | 307 | AC_MSG_RESULT([$pythonexists]) 308 | 309 | if test ! "x$pythonexists" = "xyes"; then 310 | AC_MSG_FAILURE([ 311 | Could not link test program to Python. Maybe the main Python library has been 312 | installed in some non-standard library path. If so, pass it to configure, 313 | via the LIBS environment variable. 314 | Example: ./configure LIBS="-L/usr/non-standard-path/python/lib" 315 | ============================================================================ 316 | ERROR! 317 | You probably have to install the development version of the Python package 318 | for your distribution. The exact name of this package varies among them. 319 | ============================================================================ 320 | ]) 321 | PYTHON_VERSION="" 322 | fi 323 | 324 | # 325 | # all done! 326 | # 327 | ]) 328 | -------------------------------------------------------------------------------- /m4/ax_python_library.m4: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Colomban Wendling 2 | # 3 | # AX_PYTHON_LIBRARY([action-if-found], [action-if-not-found]) 4 | # 5 | # Tries to locate Python DSO path. It requires $PYTHON to be set 6 | 7 | AC_DEFUN([AX_PYTHON_LIBRARY],[ 8 | AC_REQUIRE([AX_PYTHON_DEVEL]) 9 | 10 | AC_MSG_CHECKING([for Python DSO location]) 11 | 12 | ax_python_library=`cat << EOD | $PYTHON - 2>/dev/null 13 | from distutils.sysconfig import get_config_vars 14 | from os.path import join as path_join 15 | 16 | cvars = get_config_vars() 17 | # support multiarch-enabled distributions like Ubuntu 18 | if not 'MULTIARCH' in cvars.keys(): 19 | cvars[['MULTIARCH']] = '' 20 | # try to find the real installed lib, not the generic link 21 | if not 'INSTSONAME' in cvars.keys(): 22 | cvars[['INSTSONAME']] = cvars[['LDLIBRARY']] 23 | print(path_join(cvars[['LIBDIR']], cvars[['MULTIARCH']], cvars[['INSTSONAME']])) 24 | EOD` 25 | 26 | AC_SUBST([PYTHON_LIBRARY], [$ax_python_library]) 27 | AC_MSG_RESULT([$PYTHON_LIBRARY]) 28 | AS_IF([test -z "$ax_python_library"], [$2], [$1]) 29 | ]) 30 | -------------------------------------------------------------------------------- /makefile.win32: -------------------------------------------------------------------------------- 1 | # You'll likely need to customize win32env.mk 2 | include win32env.mk 3 | 4 | all: 5 | $(MAKE) -C src -f makefile.win32 6 | 7 | clean: 8 | $(MAKE) -C src -f makefile.win32 clean 9 | 10 | install: 11 | -mkdir "$(GEANY_PREFIX)/lib/geanypy/geany" 12 | -copy /Y src\geanypy.dll "$(GEANY_PREFIX)/lib/" 13 | -copy /Y geany\*.py "$(GEANY_PREFIX)/lib/geanypy/geany/" 14 | -copy /Y plugins\*.py "$(GEANY_PREFIX)/lib/" 15 | 16 | uninstall: 17 | -del "$(GEANY_PREFIX)/lib/geanypy.dll" 18 | -rmdir /S /Q "$(GEANY_PREFIX)/lib/geanypy/" 19 | 20 | dist: all 21 | $(MAKE_NSIS) geanypy.nsi 22 | -------------------------------------------------------------------------------- /plugins/Makefile.am: -------------------------------------------------------------------------------- 1 | geanypy_plugins = demo.py hello.py console.py 2 | geanypydir = $(libdir)/geany 3 | geanypy_DATA = $(geanypy_plugins) 4 | EXTRA_DIST = $(geanypy_plugins) 5 | -------------------------------------------------------------------------------- /plugins/console.py: -------------------------------------------------------------------------------- 1 | import os 2 | from ConfigParser import SafeConfigParser 3 | import geany 4 | import geany.console 5 | import gobject 6 | import gtk 7 | import gtk.gdk as gdk 8 | import pango 9 | 10 | 11 | WIDGET_STATES = [ gtk.STATE_NORMAL, gtk.STATE_ACTIVE, gtk.STATE_PRELIGHT, 12 | gtk.STATE_SELECTED, gtk.STATE_INSENSITIVE ] 13 | 14 | 15 | class ConsolePlugin(geany.Plugin): 16 | 17 | __plugin_name__ = "Python Console" 18 | __plugin_description__ = ("Installs a Python console that allows you " + 19 | "to use the Geany API.") 20 | __plugin_version__ = "0.01" 21 | __plugin_author__ = "Matthew Brush " 22 | 23 | _font = "Monospace 9" 24 | _bg = "#FFFFFF" 25 | _fg = "#000000" 26 | _banner = ("Geany Python Console\n You can access the Geany Python " + 27 | "API by importing the `geany' module.\n") 28 | _use_rl_completer = True 29 | 30 | _builder = None 31 | 32 | def __init__(self): 33 | geany.Plugin.__init__(self) 34 | self.load_config() 35 | self.install_console() 36 | 37 | 38 | def cleanup(self): 39 | #self.save_config() 40 | self.on_save_config_timeout() # do it now 41 | self.uninstall_console() 42 | 43 | 44 | def load_config(self): 45 | self.cfg_path = os.path.join(geany.app.configdir, "plugins", "pyconsole.conf") 46 | self.cfg = SafeConfigParser() 47 | self.cfg.read(self.cfg_path) 48 | 49 | 50 | def save_config(self): 51 | gobject.idle_add(self.on_save_config_timeout) 52 | 53 | 54 | def on_save_config_timeout(self, data=None): 55 | self.cfg.write(open(self.cfg_path, 'w')) 56 | return False 57 | 58 | 59 | def install_console(self): 60 | 61 | # load general settings 62 | self.banner = self.banner 63 | self.use_rl_completer = self.use_rl_completer 64 | 65 | self.swin = gtk.ScrolledWindow() 66 | self.swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) 67 | self.console = geany.console.Console(banner = self.banner, 68 | use_rlcompleter = self.use_rl_completer) 69 | self.console.connect("populate-popup", self.on_console_populate_popup) 70 | 71 | # apply appearance settings 72 | self.font = self.font 73 | self.bg = self.bg 74 | self.fg = self.fg 75 | 76 | self.swin.add(self.console) 77 | geany.main_widgets.message_window_notebook.append_page(self.swin, 78 | gtk.Label("Python")) 79 | 80 | self.swin.show_all() 81 | self.save_config() 82 | 83 | def uninstall_console(self): 84 | self.swin.destroy() 85 | 86 | 87 | def _get_font(self): 88 | if self.cfg.has_section('appearances'): 89 | if self.cfg.has_option('appearances', 'font'): 90 | return self.cfg.get('appearances', 'font') 91 | return self._font 92 | def _set_font(self, font): 93 | self._font = font 94 | font_desc = pango.FontDescription(font) 95 | self.console.modify_font(font_desc) 96 | if not self.cfg.has_section('appearances'): 97 | self.cfg.add_section('appearances') 98 | self.cfg.set('appearances', 'font', self._font) 99 | self.save_config() 100 | font = property(_get_font, _set_font) 101 | 102 | 103 | def _get_bg(self): 104 | if self.cfg.has_section('appearances'): 105 | if self.cfg.has_option('appearances', 'bg_color'): 106 | return self.cfg.get('appearances', 'bg_color') 107 | return self._bg 108 | def _set_bg(self, bg): 109 | self._bg = bg 110 | color = gdk.color_parse(self._bg) 111 | for state in WIDGET_STATES: 112 | self.console.modify_bg(state, color) 113 | self.console.modify_base(state, color) 114 | if not self.cfg.has_section('appearances'): 115 | self.cfg.add_section('appearances') 116 | self.cfg.set('appearances', 'bg_color', self._bg) 117 | self.save_config() 118 | bg = property(_get_bg, _set_bg) 119 | 120 | 121 | def _get_fg(self): 122 | if self.cfg.has_section('appearances'): 123 | if self.cfg.has_option('appearances', 'fg_color'): 124 | return self.cfg.get('appearances', 'fg_color') 125 | return self._fg 126 | def _set_fg(self, fg): 127 | self._fg = fg 128 | color = gdk.color_parse(self._fg) 129 | for state in WIDGET_STATES: 130 | self.console.modify_fg(state, color) 131 | self.console.modify_text(state, color) 132 | if not self.cfg.has_section('appearances'): 133 | self.cfg.add_section('appearances') 134 | self.cfg.set('appearances', 'fg_color', self._fg) 135 | self.save_config() 136 | fg = property(_get_fg, _set_fg) 137 | 138 | 139 | def _get_banner(self): 140 | if self.cfg.has_section('general'): 141 | if self.cfg.has_option('general', 'banner'): 142 | return self.cfg.get('general', 'banner') 143 | return self._banner 144 | def _set_banner(self, banner): 145 | self._banner = banner 146 | if not self.cfg.has_section('general'): 147 | self.cfg.add_section('general') 148 | self.cfg.set('general', 'banner', self._banner) 149 | self.save_config() 150 | banner = property(_get_banner, _set_banner) 151 | 152 | 153 | def _get_use_rl_completer(self): 154 | if self.cfg.has_section('general'): 155 | if self.cfg.has_option('general', 'use_rl_completer'): 156 | return self.cfg.getboolean('general', 'use_rl_completer') 157 | return self._use_rl_completer 158 | def _set_use_rl_completer(self, use_rl_completer): 159 | self._use_rl_completer = use_rl_completer 160 | if not self.cfg.has_section('general'): 161 | self.cfg.add_section('general') 162 | self.cfg.set('general', 'use_rl_completer', 163 | str(self._use_rl_completer).lower()) 164 | self.save_config() 165 | use_rl_completer = property(_get_use_rl_completer, _set_use_rl_completer) 166 | 167 | 168 | def on_console_populate_popup(self, textview, menu, data=None): 169 | item = gtk.SeparatorMenuItem() 170 | item.show() 171 | menu.append(item) 172 | item = gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES) 173 | item.show() 174 | menu.append(item) 175 | item.connect("activate", lambda w,d=None: self.show_configure()) 176 | 177 | 178 | def on_banner_changed(self, text_buf, data=None): 179 | self.banner = text_buf.get_text(text_buf.get_start_iter(), text_buf.get_end_iter()) 180 | 181 | def on_use_rl_completer_toggled(self, chk_btn, data=None): 182 | self.use_rl_completer = chk_btn.get_active() 183 | 184 | def on_font_changed(self, font_btn, data=None): 185 | self.font = font_btn.get_font_name() 186 | 187 | def on_fg_color_changed(self, clr_btn, data=None): 188 | self.fg = clr_btn.get_color().to_string() 189 | 190 | def on_bg_color_changed(self, clr_btn, data=None): 191 | self.bg = clr_btn.get_color().to_string() 192 | 193 | 194 | def configure(self, dialog): 195 | 196 | vbox = gtk.VBox(spacing=6) 197 | vbox.set_border_width(6) 198 | 199 | lbl = gtk.Label() 200 | lbl.set_use_markup(True) 201 | lbl.set_markup("General") 202 | 203 | fra_general = gtk.Frame("") 204 | fra_general.set_shadow_type(gtk.SHADOW_NONE) 205 | fra_general.set_label_widget(lbl) 206 | 207 | al_general = gtk.Alignment(0.0, 0.0, 1.0, 1.0) 208 | al_general.set_padding(0, 0, 12, 0) 209 | fra_general.add(al_general) 210 | 211 | tbl = gtk.Table(3, 2, False) 212 | tbl.set_row_spacings(6) 213 | tbl.set_col_spacings(6) 214 | tbl.set_border_width(6) 215 | 216 | lbl = gtk.Label("Banner:") 217 | lbl.set_alignment(0.0, 0.0) 218 | 219 | tvw = gtk.TextView() 220 | tvw.get_buffer().set_text(self.banner) 221 | tvw.get_buffer().connect("changed", self.on_banner_changed) 222 | 223 | swin = gtk.ScrolledWindow() 224 | swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 225 | swin.set_shadow_type(gtk.SHADOW_ETCHED_IN) 226 | swin.add(tvw) 227 | 228 | tbl.attach(lbl, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0) 229 | tbl.attach(swin, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0) 230 | 231 | lbl = gtk.Label("") 232 | lbl.set_alignment(0.0, 0.5) 233 | 234 | check = gtk.CheckButton("Use Readline") 235 | if self.use_rl_completer: 236 | check.set_active(True) 237 | check.connect("toggled", self.on_use_rl_completer_toggled) 238 | 239 | tbl.attach(lbl, 0, 1, 1, 2, gtk.FILL, gtk.FILL, 0, 0) 240 | tbl.attach(check, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 0, 0) 241 | 242 | lbl = gtk.Label("") 243 | lbl.set_alignment(0.0, 0.5) 244 | lbl.set_use_markup(True) 245 | lbl.set_markup('' + 246 | 'Note: General settings will be applied when console is reloaded.' + 247 | '') 248 | tbl.attach(lbl, 0, 2, 2, 3, gtk.FILL, gtk.FILL, 0, 0) 249 | 250 | al_general.add(tbl) 251 | 252 | lbl = gtk.Label() 253 | lbl.set_use_markup(True) 254 | lbl.set_markup("Appearances") 255 | 256 | fra_appearances = gtk.Frame("") 257 | fra_appearances.set_shadow_type(gtk.SHADOW_NONE) 258 | fra_appearances.set_label_widget(lbl) 259 | 260 | al_appearances = gtk.Alignment(0.0, 0.0, 1.0, 1.0) 261 | al_appearances.set_padding(0, 0, 12, 0) 262 | fra_appearances.add(al_appearances) 263 | 264 | tbl = gtk.Table(3, 2, False) 265 | tbl.set_row_spacings(6) 266 | tbl.set_col_spacings(6) 267 | tbl.set_border_width(6) 268 | 269 | lbl = gtk.Label("Font:") 270 | lbl.set_alignment(0.0, 0.5) 271 | 272 | btn = gtk.FontButton(self.font) 273 | btn.connect("font-set", self.on_font_changed) 274 | 275 | tbl.attach(lbl, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0) 276 | tbl.attach(btn, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) 277 | 278 | lbl = gtk.Label("FG Color:") 279 | lbl.set_alignment(0.0, 0.5) 280 | 281 | btn = gtk.ColorButton(gdk.color_parse(self.fg)) 282 | btn.connect("color-set", self.on_fg_color_changed) 283 | 284 | tbl.attach(lbl, 0, 1, 1, 2, gtk.FILL, gtk.FILL, 0, 0) 285 | tbl.attach(btn, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) 286 | 287 | lbl = gtk.Label("BG Color:") 288 | lbl.set_alignment(0.0, 0.5) 289 | 290 | btn = gtk.ColorButton(gdk.color_parse(self.bg)) 291 | btn.connect("color-set", self.on_bg_color_changed) 292 | 293 | tbl.attach(lbl, 0, 1, 2, 3, gtk.FILL, gtk.FILL, 0, 0) 294 | tbl.attach(btn, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL, 0, 0) 295 | 296 | al_appearances.add(tbl) 297 | 298 | vbox.pack_start(fra_general, True, True, 0) 299 | vbox.pack_start(fra_appearances, False, True, 0) 300 | 301 | return vbox 302 | -------------------------------------------------------------------------------- /plugins/demo.py: -------------------------------------------------------------------------------- 1 | import geany 2 | 3 | 4 | class DemoPlugin(geany.Plugin): 5 | 6 | __plugin_name__ = "Demo Plugin" 7 | __plugin_version__ = "0.01" 8 | __plugin_description__ = "A Sample plugin" 9 | __plugin_author__ = "Matthew Brush " 10 | 11 | def __init__(self): 12 | geany.Plugin.__init__(self) 13 | print("Demo plugin initializing") 14 | doc = geany.document.new_file() 15 | doc.editor.scintilla.set_text("Hello from the Demo plugin") 16 | 17 | def cleanup(self): 18 | print("Demo plugin cleaning up") 19 | -------------------------------------------------------------------------------- /plugins/hello.py: -------------------------------------------------------------------------------- 1 | import gtk 2 | import geany 3 | 4 | class HelloWorld(geany.Plugin): 5 | 6 | __plugin_name__ = "HelloWorld" 7 | __plugin_version__ = "1.0" 8 | __plugin_description__ = "Just another tool to say hello world" 9 | __plugin_author__ = "John Doe " 10 | 11 | def __init__(self): 12 | self.menu_item = gtk.MenuItem("Hello World") 13 | self.menu_item.show() 14 | geany.main_widgets.tools_menu.append(self.menu_item) 15 | self.menu_item.connect("activate", self.on_hello_item_clicked) 16 | 17 | def cleanup(self): 18 | self.menu_item.destroy() 19 | 20 | def on_hello_item_clicked(widget, data): 21 | geany.dialogs.show_msgbox("Hello World") 22 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | geanyplugin_LTLIBRARIES = geanypy.la 2 | geanyplugindir = $(libdir)/geany 3 | 4 | geanypy_la_LDFLAGS = -module -avoid-version -Wl,--export-dynamic 5 | geanypy_la_CPPFLAGS = @PYTHON_CPPFLAGS@ \ 6 | -DGEANYPY_PYTHON_DIR="\"$(libdir)/geany/geanypy\"" \ 7 | -DGEANYPY_PLUGIN_DIR="\"$(libdir)/geany\"" \ 8 | -DG_LOG_DOMAIN=\"GeanyPy\" 9 | geanypy_la_CFLAGS = @PYGTK_CFLAGS@ @GEANY_CFLAGS@ @GMODULE_CFLAGS@ 10 | geanypy_la_LIBADD = @GEANY_LIBS@ @PYGTK_LIBS@ \ 11 | $(PYTHON_LDFLAGS) $(PYTHON_LIBS) \ 12 | @PYTHON_EXTRA_LIBS@ @PYTHON_EXTRA_LDFLAGS@ \ 13 | @GMODULE_LIBS@ 14 | geanypy_la_SOURCES = geanypy-app.c \ 15 | geanypy-dialogs.c \ 16 | geanypy-document.c geanypy-document.h \ 17 | geanypy-editor.c geanypy-editor.h \ 18 | geanypy-encoding.c geanypy-encoding.h \ 19 | geanypy-filetypes.c geanypy-filetypes.h \ 20 | geanypy-glog.c \ 21 | geanypy.h \ 22 | geanypy-highlighting.c \ 23 | geanypy-indentprefs.c \ 24 | geanypy-interfaceprefs.c \ 25 | geanypy-keybindings.c geanypy-keybindings.h \ 26 | geanypy-main.c \ 27 | geanypy-mainwidgets.c \ 28 | geanypy-msgwindow.c \ 29 | geanypy-navqueue.c \ 30 | geanypy-plugin.c geanypy-plugin.h \ 31 | geanypy-prefs.c \ 32 | geanypy-project.c geanypy-project.h \ 33 | geanypy-scinotification.c \ 34 | geanypy-scinotifyheader.c \ 35 | geanypy-scintilla.c geanypy-scintilla.h \ 36 | geanypy-search.c \ 37 | geanypy-signalmanager.c geanypy-signalmanager.h \ 38 | geanypy-templates.c \ 39 | geanypy-uiutils.c geanypy-uiutils.h 40 | -------------------------------------------------------------------------------- /src/geanypy-app.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | typedef struct 8 | { 9 | PyObject_HEAD 10 | GeanyApp *app; 11 | } App; 12 | 13 | 14 | static void 15 | App_dealloc(App *self) 16 | { 17 | g_return_if_fail(self != NULL); 18 | self->ob_type->tp_free((PyObject *) self); 19 | } 20 | 21 | 22 | static int 23 | App_init(App *self) 24 | { 25 | g_return_val_if_fail(self != NULL, -1); 26 | g_return_val_if_fail(geany_data, -1); 27 | self->app = geany_data->app; 28 | return 0; 29 | } 30 | 31 | 32 | static PyObject * 33 | App_get_property(App *self, const gchar *prop_name) 34 | { 35 | g_return_val_if_fail(self != NULL, NULL); 36 | g_return_val_if_fail(prop_name != NULL, NULL); 37 | 38 | if (!self->app) 39 | { 40 | PyErr_SetString(PyExc_RuntimeError, 41 | "App instance not initialized properly"); 42 | return NULL; 43 | } 44 | 45 | if (g_str_equal(prop_name, "configdir") && self->app->configdir) 46 | return PyString_FromString(self->app->configdir); 47 | #if ENABLE_PRIVATE 48 | else if (g_str_equal(prop_name, "datadir") && self->app->datadir) 49 | return PyString_FromString(self->app->datadir); 50 | else if (g_str_equal(prop_name, "docdir") && self->app->docdir) 51 | return PyString_FromString(self->app->docdir); 52 | #endif 53 | else if (g_str_equal(prop_name, "debug_mode") && self->app->debug_mode) 54 | { 55 | if (self->app->debug_mode) 56 | Py_RETURN_TRUE; 57 | else 58 | Py_RETURN_FALSE; 59 | } 60 | else if (g_str_equal(prop_name, "project") && self->app->project) 61 | return (PyObject *) GEANYPY_NEW(Project); 62 | 63 | Py_RETURN_NONE; 64 | } 65 | GEANYPY_PROPS_READONLY(App); 66 | 67 | 68 | static PyGetSetDef App_getseters[] = { 69 | GEANYPY_GETSETDEF(App, "configdir", 70 | "User configuration directory, usually ~/.config/geany. "), 71 | #ifdef ENABLE_PRIVATE 72 | GEANYPY_GETSETDEF(App, "datadir", "Geany's data directory."), 73 | GEANYPY_GETSETDEF(App, "docdir", "Geany's documentation directory."), 74 | #endif 75 | GEANYPY_GETSETDEF(App, "debug_mode", 76 | "True if debug messages should be printed."), 77 | GEANYPY_GETSETDEF(App, "project", 78 | "Currently active project or None if none is open."), 79 | { NULL } 80 | }; 81 | 82 | 83 | static PyTypeObject AppType = { 84 | PyObject_HEAD_INIT(NULL) 85 | 0, /* ob_size */ 86 | "geany.app.App", /* tp_name */ 87 | sizeof(App), /* tp_basicsize */ 88 | 0, /* tp_itemsize */ 89 | (destructor) App_dealloc, /* tp_dealloc */ 90 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 91 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 92 | "Wrapper around a GeanyApp structure.", /* tp_doc */ 93 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 94 | App_getseters, /* tp_getset */ 95 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 96 | (initproc) App_init, /* tp_init */ 97 | 0, 0, /* tp_alloc - tp_new */ 98 | }; 99 | 100 | 101 | static PyMethodDef AppModule_methods[] = { { NULL } }; 102 | 103 | 104 | PyMODINIT_FUNC initapp(void) 105 | { 106 | PyObject *m; 107 | 108 | AppType.tp_new = PyType_GenericNew; 109 | if (PyType_Ready(&AppType) < 0) 110 | return; 111 | 112 | m = Py_InitModule3("app", AppModule_methods, "Application information"); 113 | 114 | Py_INCREF(&AppType); 115 | PyModule_AddObject(m, "App", (PyObject *) &AppType); 116 | } 117 | -------------------------------------------------------------------------------- /src/geanypy-dialogs.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static PyObject * 9 | Dialogs_show_input(PyObject *self, PyObject *args, PyObject *kwargs) 10 | { 11 | const gchar *title=NULL, *label_text=NULL, *default_text=NULL, *result=NULL; 12 | PyObject *py_win_obj = NULL; 13 | PyGObject *py_win_gobj; 14 | GtkWindow *win; 15 | static gchar *kwlist[] = { "title", "parent", "label_text", "default_text", NULL }; 16 | 17 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "|zOzz", kwlist, 18 | &title, &py_win_obj, &label_text, &default_text)) 19 | { 20 | if (title == NULL) 21 | title = ""; 22 | 23 | if (py_win_obj != NULL && py_win_obj != Py_None) 24 | { 25 | py_win_gobj = (PyGObject *) py_win_obj; 26 | win = GTK_WINDOW((GObject *) py_win_gobj->obj); 27 | } 28 | else 29 | win = GTK_WINDOW(geany->main_widgets->window); 30 | 31 | result = dialogs_show_input(title, win, label_text, default_text); 32 | if (result != NULL) 33 | return PyString_FromString(result); 34 | } 35 | 36 | Py_RETURN_NONE; 37 | } 38 | 39 | 40 | static PyObject * 41 | Dialogs_show_input_numeric(PyObject *self, PyObject *args, PyObject *kwargs) 42 | { 43 | const gchar *title=NULL, *label_text = NULL; 44 | gdouble value = 0.0, min = 0.0, max = 0.0, step = 0.0; 45 | static gchar *kwlist[] = { "title", "label_text", "value", "minimum", 46 | "maximum", "step", NULL }; 47 | 48 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "|zzdddd", kwlist, 49 | &title, &label_text, &value, &min, &max, &step)) 50 | { 51 | if (title == NULL) 52 | title = ""; 53 | 54 | if (label_text == NULL) 55 | label_text = ""; 56 | 57 | if (dialogs_show_input_numeric(title, label_text, &value, min, max, step)) 58 | return PyFloat_FromDouble(value); 59 | } 60 | 61 | Py_RETURN_NONE; 62 | } 63 | 64 | 65 | static PyObject * 66 | Dialogs_show_msgbox(PyObject *self, PyObject *args, PyObject *kwargs) 67 | { 68 | gchar *text = NULL; 69 | gint msgtype = (gint) GTK_MESSAGE_INFO; 70 | static gchar *kwlist[] = { "text", "msgtype", NULL }; 71 | 72 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist, &text, &msgtype)) 73 | { 74 | if (text != NULL) 75 | { 76 | dialogs_show_msgbox((GtkMessageType) msgtype, "%s", text); 77 | Py_RETURN_TRUE; 78 | } 79 | } 80 | Py_RETURN_NONE; 81 | } 82 | 83 | 84 | static PyObject * 85 | Dialogs_show_question(PyObject *self, PyObject *args, PyObject *kwargs) 86 | { 87 | gchar *text = NULL; 88 | static gchar *kwlist[] = { "text", NULL }; 89 | 90 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &text)) 91 | { 92 | if (text != NULL) 93 | { 94 | if (dialogs_show_question("%s", text)) 95 | Py_RETURN_TRUE; 96 | else 97 | Py_RETURN_FALSE; 98 | } 99 | } 100 | Py_RETURN_NONE; 101 | } 102 | 103 | 104 | static PyObject * 105 | Dialogs_show_save_as(PyObject *self) 106 | { 107 | if (dialogs_show_save_as()) 108 | Py_RETURN_TRUE; 109 | else 110 | Py_RETURN_FALSE; 111 | } 112 | 113 | 114 | static 115 | PyMethodDef DialogsModule_methods[] = { 116 | { "show_input", (PyCFunction) Dialogs_show_input, METH_KEYWORDS, 117 | "Asks the user for input." }, 118 | { "show_input_numeric", (PyCFunction) Dialogs_show_input_numeric, METH_KEYWORDS, 119 | "Shows an input box to enter a numerical value." }, 120 | { "show_msgbox", (PyCFunction) Dialogs_show_msgbox, METH_KEYWORDS, 121 | "Shows a message box of the specified type. See " 122 | "gtk.MESSAGE_TYPE_* constants."}, 123 | { "show_question", (PyCFunction) Dialogs_show_question, METH_KEYWORDS, 124 | "Shows a question message box with Yes/No buttons." }, 125 | { "show_save_as", (PyCFunction) Dialogs_show_save_as, METH_NOARGS, 126 | "Shows the Save As dialog for the current notebook." }, 127 | { NULL } 128 | }; 129 | 130 | 131 | PyMODINIT_FUNC initdialogs(void) 132 | { 133 | Py_InitModule3("dialogs", DialogsModule_methods, 134 | "Wrappers around Geany's dialog functions."); 135 | } 136 | -------------------------------------------------------------------------------- /src/geanypy-document.h: -------------------------------------------------------------------------------- 1 | #ifndef GEANYPY_DOCUMENT_H__ 2 | #define GEANYPY_DOCUMENT_H__ 3 | 4 | typedef struct 5 | { 6 | PyObject_HEAD 7 | GeanyDocument *doc; 8 | } Document; 9 | 10 | Document *Document_create_new_from_geany_document(GeanyDocument *doc); 11 | 12 | #endif /* GEANYPY_DOCUMENT_H__ */ 13 | -------------------------------------------------------------------------------- /src/geanypy-editor.h: -------------------------------------------------------------------------------- 1 | #ifndef GEANYPY_EDITOR_H__ 2 | #define GEANYPY_EDITOR_H__ 3 | 4 | extern PyTypeObject IndentPrefsType; 5 | 6 | typedef struct 7 | { 8 | PyObject_HEAD 9 | GeanyEditor *editor; 10 | } Editor; 11 | 12 | typedef struct 13 | { 14 | PyObject_HEAD 15 | GeanyIndentPrefs *indent_prefs; 16 | } IndentPrefs; 17 | 18 | Editor *Editor_create_new_from_geany_editor(GeanyEditor *editor); 19 | IndentPrefs *IndentPrefs_create_new_from_geany_indent_prefs(GeanyIndentPrefs *indent_prefs); 20 | 21 | #endif /* GEANYPY_EDITOR_H__ */ 22 | -------------------------------------------------------------------------------- /src/geanypy-encoding.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static PyObject * 9 | Encodings_convert_to_utf8(PyObject *module, PyObject *args, PyObject *kwargs) 10 | { 11 | gchar *buffer = NULL, *used_encoding = NULL, *new_buffer = NULL; 12 | gssize size = -1; /* bug alert: this is gsize in Geany for some reason */ 13 | PyObject *result; 14 | static gchar *kwlist[] = { "buffer", "size", NULL }; 15 | 16 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|l", kwlist, &buffer, &size)) 17 | { 18 | new_buffer = encodings_convert_to_utf8(buffer, size, &used_encoding); 19 | if (new_buffer != NULL) 20 | { 21 | result = Py_BuildValue("ss", new_buffer, used_encoding); 22 | g_free(new_buffer); 23 | g_free(used_encoding); 24 | return result; 25 | } 26 | } 27 | 28 | Py_RETURN_NONE; 29 | } 30 | 31 | 32 | static PyObject * 33 | Encodings_convert_to_utf8_from_charset(PyObject *module, PyObject *args, PyObject *kwargs) 34 | { 35 | gchar *buffer = NULL, *charset = NULL, *new_buffer = NULL; 36 | gssize size = -1; 37 | gint fast = 0; 38 | PyObject *result; 39 | static gchar *kwlist[] = { "buffer", "size", "charset", "fast", NULL }; 40 | 41 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "ss|li", kwlist, &buffer, 42 | &charset, &size, &fast)) 43 | { 44 | new_buffer = encodings_convert_to_utf8_from_charset(buffer, size, charset, fast); 45 | if (new_buffer != NULL) 46 | { 47 | result = Py_BuildValue("s", new_buffer); 48 | g_free(new_buffer); 49 | return result; 50 | } 51 | } 52 | 53 | Py_RETURN_NONE; 54 | } 55 | 56 | 57 | static PyObject * 58 | Encodings_get_charset_from_index(PyObject *module, PyObject *args, PyObject *kwargs) 59 | { 60 | gint idx = 0; 61 | const gchar *charset = NULL; 62 | static gchar *kwlist[] = { "index", NULL }; 63 | 64 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &idx)) 65 | { 66 | charset = encodings_get_charset_from_index(idx); 67 | if (charset != NULL) 68 | return Py_BuildValue("s", charset); 69 | } 70 | 71 | Py_RETURN_NONE; 72 | } 73 | 74 | 75 | static const gchar *encoding_names[] = { 76 | "ISO_8859_1", 77 | "ISO_8859_2", 78 | "ISO_8859_3", 79 | "ISO_8859_4", 80 | "ISO_8859_5", 81 | "ISO_8859_6", 82 | "ISO_8859_7", 83 | "ISO_8859_8", 84 | "ISO_8859_8_I", 85 | "ISO_8859_9", 86 | "ISO_8859_10", 87 | "ISO_8859_13", 88 | "ISO_8859_14", 89 | "ISO_8859_15", 90 | "ISO_8859_16", 91 | 92 | "UTF_7", 93 | "UTF_8", 94 | "UTF_16LE", 95 | "UTF_16BE", 96 | "UCS_2LE", 97 | "UCS_2BE", 98 | "UTF_32LE", 99 | "UTF_32BE", 100 | 101 | "ARMSCII_8", 102 | "BIG5", 103 | "BIG5_HKSCS", 104 | "CP_866", 105 | 106 | "EUC_JP", 107 | "EUC_KR", 108 | "EUC_TW", 109 | 110 | "GB18030", 111 | "GB2312", 112 | "GBK", 113 | "GEOSTD8", 114 | "HZ", 115 | 116 | "IBM_850", 117 | "IBM_852", 118 | "IBM_855", 119 | "IBM_857", 120 | "IBM_862", 121 | "IBM_864", 122 | 123 | "ISO_2022_JP", 124 | "ISO_2022_KR", 125 | "ISO_IR_111", 126 | "JOHAB", 127 | "KOI8_R", 128 | "KOI8_U", 129 | 130 | "SHIFT_JIS", 131 | "TCVN", 132 | "TIS_620", 133 | "UHC", 134 | "VISCII", 135 | 136 | "WINDOWS_1250", 137 | "WINDOWS_1251", 138 | "WINDOWS_1252", 139 | "WINDOWS_1253", 140 | "WINDOWS_1254", 141 | "WINDOWS_1255", 142 | "WINDOWS_1256", 143 | "WINDOWS_1257", 144 | "WINDOWS_1258", 145 | 146 | "NONE", 147 | "CP_932", 148 | 149 | "MAX" 150 | }; 151 | 152 | 153 | static PyObject * 154 | Encodings_get_list(PyObject *module) 155 | { 156 | int i; 157 | PyObject *list; 158 | list = PyList_New(0); 159 | for (i = 0; i < GEANY_ENCODINGS_MAX; i++) 160 | PyList_Append(list, PyString_FromString(encoding_names[i])); 161 | return list; 162 | } 163 | 164 | 165 | static 166 | PyMethodDef EncodingsModule_methods[] = { 167 | { 168 | "convert_to_utf8", 169 | (PyCFunction)Encodings_convert_to_utf8, METH_KEYWORDS, 170 | "Tries to convert the supplied buffer to UTF-8 encoding. Returns " 171 | "the converted buffer and the encoding that was used." 172 | }, 173 | { 174 | "convert_to_utf8_from_charset", 175 | (PyCFunction)Encodings_convert_to_utf8_from_charset, METH_KEYWORDS, 176 | "Tries to convert the supplied buffer to UTF-8 from the supplied " 177 | "charset. If the fast parameter is not False (default), additional " 178 | "checks to validate the converted string are performed." 179 | }, 180 | { 181 | "get_charset_from_index", 182 | (PyCFunction)Encodings_get_charset_from_index, METH_KEYWORDS, 183 | "Gets the character set name of the specified index." 184 | }, 185 | { 186 | "get_list", 187 | (PyCFunction) Encodings_get_list, METH_NOARGS, 188 | "Gets a list of all supported encodings." 189 | }, 190 | { NULL } 191 | }; 192 | 193 | 194 | PyMODINIT_FUNC 195 | initencoding(void) 196 | { 197 | int i; 198 | PyObject *m; 199 | 200 | m = Py_InitModule3("encoding", EncodingsModule_methods, 201 | "Encoding conversion functions."); 202 | 203 | for (i = 0; i < GEANY_ENCODINGS_MAX; i++) 204 | PyModule_AddIntConstant(m, encoding_names[i], (glong) i); 205 | } 206 | -------------------------------------------------------------------------------- /src/geanypy-encoding.h: -------------------------------------------------------------------------------- 1 | #ifndef GEANYPY_ENCODING_H__ 2 | #define GEANYPY_ENCODING_H__ 3 | 4 | #endif /* GEANYPY_ENCODING_H__ */ 5 | -------------------------------------------------------------------------------- /src/geanypy-filetypes.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static void 9 | Filetype_dealloc(Filetype *self) 10 | { 11 | self->ob_type->tp_free((PyObject *) self); 12 | } 13 | 14 | 15 | static int 16 | Filetype_init(Filetype *self, PyObject *args, PyObject *kwds) 17 | { 18 | self->ft = NULL; 19 | return 0; 20 | } 21 | 22 | 23 | static PyObject * 24 | Filetype_get_property(Filetype *self, const gchar *prop_name) 25 | { 26 | g_return_val_if_fail(self != NULL, NULL); 27 | g_return_val_if_fail(prop_name != NULL, NULL); 28 | 29 | if (!self->ft) 30 | { 31 | PyErr_SetString(PyExc_RuntimeError, 32 | "Filetype instance not initialized properly"); 33 | return NULL; 34 | } 35 | 36 | if (g_str_equal(prop_name, "display_name")) 37 | GEANYPY_RETURN_STRING(filetypes_get_display_name(self->ft)) 38 | else if (g_str_equal(prop_name, "extension")) 39 | GEANYPY_RETURN_STRING(self->ft->extension) 40 | else if (g_str_equal(prop_name, "id")) 41 | return PyInt_FromLong((glong) self->ft->id); 42 | else if (g_str_equal(prop_name, "lang")) 43 | return PyInt_FromLong((glong) self->ft->lang); 44 | else if (g_str_equal(prop_name, "name")) 45 | GEANYPY_RETURN_STRING(self->ft->name) 46 | else if (g_str_equal(prop_name, "pattern")) 47 | { 48 | gint i, len; 49 | PyObject *list = PyList_New(0); 50 | if (self->ft->pattern) 51 | { 52 | len = g_strv_length(self->ft->pattern); 53 | for (i = 0; i < len; i++) 54 | PyList_Append(list, PyString_FromString(self->ft->pattern[i])); 55 | } 56 | return list; 57 | 58 | } 59 | else if (g_str_equal(prop_name, "title")) 60 | GEANYPY_RETURN_STRING(self->ft->title) 61 | #ifdef ENABLE_PRIVATE 62 | else if (g_str_equal(prop_name, "context_action_cmd")) 63 | GEANYPY_RETURN_STRING(self->ft->context_action_cmd) 64 | else if (g_str_equal(prop_name, "comment_open")) 65 | GEANYPY_RETURN_STRING(self->ft->comment_open) 66 | else if (g_str_equal(prop_name, "comment_use_indent")) 67 | { 68 | if (self->ft->comment_use_indent) 69 | Py_RETURN_TRUE; 70 | else 71 | Py_RETURN_FALSE; 72 | } 73 | else if (g_str_equal(prop_name, "group")) 74 | return PyInt_FromLong((glong) self->ft->group); 75 | else if (g_str_equal(prop_name, "error_regex_string")) 76 | return PyString_FromString(self->ft->error_regex_string); 77 | else if (g_str_equal(prop_name, "lexer_filetype")) 78 | { 79 | if (self->ft->lexer_filetype) 80 | return Filetype_create_new_from_geany_filetype(self->ft->lexer_filetype); 81 | } 82 | else if (g_str_equal(prop_name, "mime_type")) 83 | GEANYPY_RETURN_STRING(self->ft->lexer_filetype) 84 | else if (g_str_equal(prop_name, "icon")) 85 | { 86 | if (self->ft->icon) 87 | return (PyObject *) pygobject_new(G_OBJECT(self->ft->icon)); 88 | } 89 | else if (g_str_equal(prop_name, "comment_single")) 90 | GEANYPY_RETURN_STRING(self->ft->comment_single) 91 | else if (g_str_equal(prop_name, "indent_type")) 92 | return PyInt_FromLong((glong) self->ft->indent_type); 93 | else if (g_str_equal(prop_name, "indent_width")) 94 | return PyInt_FromLong((glong) self->ft->indent_width); 95 | #endif 96 | 97 | Py_RETURN_NONE; 98 | } 99 | GEANYPY_PROPS_READONLY(Filetype); 100 | 101 | 102 | static PyGetSetDef Filetype_getseters[] = { 103 | GEANYPY_GETSETDEF(Filetype, "display_name", 104 | "Gets the filetype's name for display."), 105 | GEANYPY_GETSETDEF(Filetype, "extension", 106 | "Default file extension for new files or None."), 107 | GEANYPY_GETSETDEF(Filetype, "id", 108 | "Index of the the filetype."), 109 | GEANYPY_GETSETDEF(Filetype, "lang", 110 | "TagManager language type, -1 for all, -2 for none."), 111 | GEANYPY_GETSETDEF(Filetype, "name", 112 | "Untranslated short name, such as 'C' or 'None'."), 113 | GEANYPY_GETSETDEF(Filetype, "pattern", 114 | "List of filename-matching wildcard strings."), 115 | GEANYPY_GETSETDEF(Filetype, "title", 116 | "Shown in the open dialog, such as 'C source file'."), 117 | #ifdef ENABLE_PRIVATE 118 | GEANYPY_GETSETDEF(Filetype, "context_action_cmd", ""), 119 | GEANYPY_GETSETDEF(Filetype, "comment_open", ""), 120 | GEANYPY_GETSETDEF(Filetype, "comment_use_indent", ""), 121 | GEANYPY_GETSETDEF(Filetype, "group", ""), 122 | GEANYPY_GETSETDEF(Filetype, "error_regex_string", ""), 123 | GEANYPY_GETSETDEF(Filetype, "lexer_filetype", ""), 124 | GEANYPY_GETSETDEF(Filetype, "mime_type", ""), 125 | GEANYPY_GETSETDEF(Filetype, "icon", ""), 126 | GEANYPY_GETSETDEF(Filetype, "comment_single", ""), 127 | GEANYPY_GETSETDEF(Filetype, "indent_type", ""), 128 | GEANYPY_GETSETDEF(Filetype, "indent_width", ""), 129 | #endif 130 | { NULL } 131 | }; 132 | 133 | 134 | static PyTypeObject FiletypeType = { 135 | PyObject_HEAD_INIT(NULL) 136 | 0, /* ob_size */ 137 | "geany.filetypes.Filetype", /* tp_name */ 138 | sizeof(Filetype), /* tp_basicsize */ 139 | 0, /* tp_itemsize */ 140 | (destructor) Filetype_dealloc, /* tp_dealloc */ 141 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 142 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 143 | "Wrapper around a GeanyFiletype structure.",/* tp_doc */ 144 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 145 | Filetype_getseters, /* tp_getset */ 146 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 147 | (initproc) Filetype_init, /* tp_init */ 148 | 0, 0, /* tp_alloc - tp_new */ 149 | 150 | }; 151 | 152 | 153 | static PyObject * 154 | Filetype_detect_from_file(PyObject *self, PyObject *args, PyObject *kwargs) 155 | { 156 | GeanyFiletype *ft; 157 | gchar *filename = NULL; 158 | static gchar *kwlist[] = { "filename", NULL }; 159 | 160 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename)) 161 | { 162 | if (filename) 163 | { 164 | if ((ft = filetypes_detect_from_file(filename))) 165 | return (PyObject *) Filetype_create_new_from_geany_filetype(ft); 166 | } 167 | } 168 | 169 | Py_RETURN_NONE; 170 | } 171 | 172 | 173 | static PyObject * 174 | Filetype_get_sorted_by_name(PyObject *self) 175 | { 176 | const GSList *glist, *iter; 177 | PyObject *list; 178 | 179 | glist = filetypes_get_sorted_by_name(); 180 | list = PyList_New(0); 181 | 182 | for (iter = glist; iter != NULL; iter = g_slist_next(iter)) 183 | { 184 | if (!iter->data) 185 | continue; 186 | PyList_Append(list, (PyObject *) 187 | Filetype_create_new_from_geany_filetype((GeanyFiletype *) iter->data)); 188 | } 189 | 190 | return list; 191 | } 192 | 193 | 194 | static PyObject * 195 | Filetype_index(PyObject *self, PyObject *args, PyObject *kwargs) 196 | { 197 | GeanyFiletype *ft; 198 | gint idx = -1; 199 | static gchar *kwlist[] = { "index", NULL }; 200 | 201 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &idx)) 202 | { 203 | if ((ft = filetypes_index(idx))) 204 | return (PyObject *) Filetype_create_new_from_geany_filetype(ft); 205 | } 206 | 207 | Py_RETURN_NONE; 208 | } 209 | 210 | 211 | static PyObject * 212 | Filetype_lookup_by_name(PyObject *self, PyObject *args, PyObject *kwargs) 213 | { 214 | GeanyFiletype *ft; 215 | gchar *filetype = NULL; 216 | static gchar *kwlist[] = { "name", NULL }; 217 | 218 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filetype)) 219 | { 220 | if (filetype && (ft = filetypes_lookup_by_name(filetype))) 221 | return (PyObject *) Filetype_create_new_from_geany_filetype(ft); 222 | } 223 | 224 | Py_RETURN_NONE; 225 | } 226 | 227 | 228 | static PyObject * 229 | Filetype_get_sorted_by_title(PyObject *self, PyObject *args) 230 | { 231 | const GSList *iter; 232 | PyObject *list; 233 | 234 | list = PyList_New(0); 235 | 236 | for (iter = geany_data->filetypes_by_title; iter != NULL; iter = g_slist_next(iter)) 237 | { 238 | if (!iter->data) 239 | continue; 240 | PyList_Append(list, (PyObject *) 241 | Filetype_create_new_from_geany_filetype((GeanyFiletype *) iter->data)); 242 | } 243 | 244 | return list; 245 | } 246 | 247 | 248 | static 249 | PyMethodDef FiletypeModule_methods[] = { 250 | { "detect_from_file", (PyCFunction) Filetype_detect_from_file, METH_KEYWORDS }, 251 | { "index", (PyCFunction) Filetype_index, METH_KEYWORDS }, 252 | { "get_sorted_by_name", (PyCFunction) Filetype_get_sorted_by_name, METH_NOARGS }, 253 | { "lookup_by_name", (PyCFunction) Filetype_lookup_by_name, METH_KEYWORDS }, 254 | { "get_sorted_by_title", (PyCFunction) Filetype_get_sorted_by_title, METH_NOARGS }, 255 | { NULL } 256 | }; 257 | 258 | 259 | PyMODINIT_FUNC 260 | initfiletypes(void) 261 | { 262 | PyObject *m; 263 | 264 | FiletypeType.tp_new = PyType_GenericNew; 265 | if (PyType_Ready(&FiletypeType) < 0) 266 | return; 267 | 268 | m = Py_InitModule3("filetypes", FiletypeModule_methods, 269 | "Filetype information and management."); 270 | 271 | Py_INCREF(&FiletypeType); 272 | PyModule_AddObject(m, "Filetype", (PyObject *)&FiletypeType); 273 | } 274 | 275 | 276 | Filetype *Filetype_create_new_from_geany_filetype(GeanyFiletype *ft) 277 | { 278 | Filetype *self; 279 | self = (Filetype *) PyObject_CallObject((PyObject *) &FiletypeType, NULL); 280 | self->ft = ft; 281 | return self; 282 | } 283 | -------------------------------------------------------------------------------- /src/geanypy-filetypes.h: -------------------------------------------------------------------------------- 1 | #ifndef GEANYPY_FILETYPES_H__ 2 | #define GEANYPY_FILETYPES_H__ 3 | 4 | typedef struct 5 | { 6 | PyObject_HEAD 7 | GeanyFiletype *ft; 8 | } Filetype; 9 | 10 | Filetype *Filetype_create_new_from_geany_filetype(GeanyFiletype *ft); 11 | 12 | #endif /* GEANYPY_FILETYPES_H__ */ 13 | -------------------------------------------------------------------------------- /src/geanypy-glog.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static PyObject * 9 | Glog_glog(PyObject *module, PyObject *args, PyObject *kwargs) 10 | { 11 | static gchar *kwlist[] = { "log_domain", "log_level", "message", NULL }; 12 | gchar *log_domain, *message; 13 | GLogLevelFlags log_level; 14 | 15 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "sis", kwlist, &log_domain, &log_level, &message)) 16 | { 17 | g_log(log_domain, log_level, "%s", message); 18 | } 19 | Py_RETURN_NONE; 20 | } 21 | 22 | 23 | static 24 | PyMethodDef GlogModule_methods[] = { 25 | { "glog", (PyCFunction) Glog_glog, METH_KEYWORDS, "Wrapper around g_log()." }, 26 | { NULL } 27 | }; 28 | 29 | 30 | PyMODINIT_FUNC initglog(void) 31 | { 32 | PyObject *m; 33 | 34 | m = Py_InitModule3("glog", GlogModule_methods, "GLib Log utility functions."); 35 | 36 | /* TODO: These constants are for the geany.logger.GLIB_LOG_LEVEL_MAP mapping. 37 | * It would be better to build this mapping on the C layer but how to 38 | * access the Python logging.* level constants here? */ 39 | PyModule_AddIntConstant(m, "LOG_LEVEL_DEBUG", G_LOG_LEVEL_DEBUG); 40 | PyModule_AddIntConstant(m, "LOG_LEVEL_INFO", G_LOG_LEVEL_INFO); 41 | PyModule_AddIntConstant(m, "LOG_LEVEL_MESSAGE", G_LOG_LEVEL_MESSAGE); 42 | PyModule_AddIntConstant(m, "LOG_LEVEL_WARNING", G_LOG_LEVEL_WARNING); 43 | PyModule_AddIntConstant(m, "LOG_LEVEL_ERROR", G_LOG_LEVEL_ERROR); 44 | PyModule_AddIntConstant(m, "LOG_LEVEL_CRITICAL", G_LOG_LEVEL_CRITICAL); 45 | } 46 | -------------------------------------------------------------------------------- /src/geanypy-highlighting.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | typedef struct 9 | { 10 | PyObject_HEAD 11 | const GeanyLexerStyle *lexer_style; 12 | } LexerStyle; 13 | 14 | 15 | static void 16 | LexerStyle_dealloc(LexerStyle *self) 17 | { 18 | self->ob_type->tp_free((PyObject *) self); 19 | } 20 | 21 | 22 | static int 23 | LexerStyle_init(LexerStyle *self, PyObject *args, PyObject *kwds) 24 | { 25 | self->lexer_style = NULL; 26 | return 0; 27 | } 28 | 29 | 30 | static PyObject * 31 | LexerStyle_get_property(LexerStyle *self, const gchar *prop_name) 32 | { 33 | g_return_val_if_fail(self != NULL, NULL); 34 | g_return_val_if_fail(prop_name != NULL, NULL); 35 | 36 | if (!self->lexer_style) 37 | { 38 | PyErr_SetString(PyExc_RuntimeError, 39 | "LexerStyle instance not initialized properly"); 40 | return NULL; 41 | } 42 | 43 | if (g_str_equal(prop_name, "background")) 44 | { 45 | guint16 red, green, blue; 46 | red = self->lexer_style->background & 255; 47 | green = (self->lexer_style->background >> 8) & 255; 48 | blue = (self->lexer_style->background >> 16) & 255; 49 | return Py_BuildValue("iii", red, green, blue); 50 | } 51 | else if (g_str_equal(prop_name, "foreground")) 52 | { 53 | guint16 red, green, blue; 54 | red = self->lexer_style->foreground & 255; 55 | green = (self->lexer_style->foreground >> 8) & 255; 56 | blue = (self->lexer_style->foreground >> 16) & 255; 57 | return Py_BuildValue("iii", red, green, blue); 58 | } 59 | else if (g_str_equal(prop_name, "bold")) 60 | { 61 | if (self->lexer_style->bold) 62 | Py_RETURN_TRUE; 63 | else 64 | Py_RETURN_FALSE; 65 | } 66 | else if (g_str_equal(prop_name, "italic")) 67 | { 68 | if (self->lexer_style->italic) 69 | Py_RETURN_TRUE; 70 | else 71 | Py_RETURN_FALSE; 72 | } 73 | 74 | Py_RETURN_NONE; 75 | } 76 | GEANYPY_PROPS_READONLY(LexerStyle); 77 | 78 | 79 | static PyGetSetDef LexerStyle_getseters[] = { 80 | GEANYPY_GETSETDEF(LexerStyle, "background", 81 | "Background color of text, as an (R,G,B) tuple."), 82 | GEANYPY_GETSETDEF(LexerStyle, "foreground", 83 | "Foreground color of text, as an (R,G,B) tuple."), 84 | GEANYPY_GETSETDEF(LexerStyle, "bold", 85 | "Whether the text is bold or not."), 86 | GEANYPY_GETSETDEF(LexerStyle, "italic", 87 | "Whether the text is italic or not."), 88 | { NULL } 89 | }; 90 | 91 | 92 | static PyTypeObject LexerStyleType = { 93 | PyObject_HEAD_INIT(NULL) 94 | 0, /*ob_size*/ 95 | "geany.highlighting.LexerStyle", /*tp_name*/ 96 | sizeof(Editor), /*tp_basicsize*/ 97 | 0, /*tp_itemsize*/ 98 | (destructor) LexerStyle_dealloc, /*tp_dealloc*/ 99 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 100 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 101 | "Wrapper around a GeanyLexerStyle structure.", /* tp_doc */ 102 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 103 | LexerStyle_getseters, /* tp_getset */ 104 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 105 | (initproc) LexerStyle_init, /* tp_init */ 106 | 0, 0, /* tp_alloc - tp_new */ 107 | }; 108 | 109 | 110 | static PyObject * 111 | Highlighting_get_style(PyObject *module, PyObject *args, PyObject *kwargs) 112 | { 113 | gint ft_id, style_id; 114 | LexerStyle *lexer_style; 115 | const GeanyLexerStyle *ls; 116 | static gchar *kwlist[] = { "filetype_id", "style_id", NULL }; 117 | 118 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &ft_id, &style_id)) 119 | { 120 | ls = highlighting_get_style(ft_id, style_id); 121 | if (ls != NULL) 122 | { 123 | lexer_style = (LexerStyle *) PyObject_CallObject((PyObject *) &LexerStyleType, NULL); 124 | lexer_style->lexer_style = ls; 125 | return (PyObject *) lexer_style; 126 | } 127 | } 128 | 129 | Py_RETURN_NONE; 130 | } 131 | 132 | 133 | static PyObject * 134 | Highlighting_is_code_style(PyObject *module, PyObject *args, PyObject *kwargs) 135 | { 136 | gint lexer, style; 137 | static gchar *kwlist[] = { "lexer", "style", NULL }; 138 | 139 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &lexer, &style)) 140 | { 141 | if (highlighting_is_code_style(lexer, style)) 142 | Py_RETURN_TRUE; 143 | else 144 | Py_RETURN_FALSE; 145 | } 146 | 147 | Py_RETURN_NONE; 148 | } 149 | 150 | 151 | static PyObject * 152 | Highlighting_is_comment_style(PyObject *module, PyObject *args, PyObject *kwargs) 153 | { 154 | gint lexer, style; 155 | static gchar *kwlist[] = { "lexer", "style", NULL }; 156 | 157 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &lexer, &style)) 158 | { 159 | if (highlighting_is_comment_style(lexer, style)) 160 | Py_RETURN_TRUE; 161 | else 162 | Py_RETURN_FALSE; 163 | } 164 | 165 | Py_RETURN_NONE; 166 | } 167 | 168 | 169 | static PyObject * 170 | Highlighting_is_string_style(PyObject *module, PyObject *args, PyObject *kwargs) 171 | { 172 | gint lexer, style; 173 | static gchar *kwlist[] = { "lexer", "style", NULL }; 174 | 175 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &lexer, &style)) 176 | { 177 | if (highlighting_is_string_style(lexer, style)) 178 | Py_RETURN_TRUE; 179 | else 180 | Py_RETURN_FALSE; 181 | } 182 | 183 | Py_RETURN_NONE; 184 | } 185 | 186 | 187 | static PyObject * 188 | Highlighting_set_styles(PyObject *module, PyObject *args, PyObject *kwargs) 189 | { 190 | PyObject *py_sci, *py_ft; 191 | Scintilla *sci; 192 | Filetype *ft; 193 | static gchar *kwlist[] = { "sci", "filetype", NULL }; 194 | 195 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwlist, &py_sci, &py_ft)) 196 | { 197 | if (py_sci != Py_None && py_ft != Py_None) 198 | { 199 | sci = (Scintilla *) py_sci; 200 | ft = (Filetype *) py_ft; 201 | highlighting_set_styles(sci->sci, ft->ft); 202 | } 203 | } 204 | 205 | Py_RETURN_NONE; 206 | } 207 | 208 | 209 | static 210 | PyMethodDef EditorModule_methods[] = { 211 | { "get_style", (PyCFunction) Highlighting_get_style, METH_KEYWORDS }, 212 | { "is_code_style", (PyCFunction) Highlighting_is_code_style, METH_KEYWORDS }, 213 | { "is_comment_style", (PyCFunction) Highlighting_is_comment_style, METH_KEYWORDS }, 214 | { "is_string_style", (PyCFunction) Highlighting_is_string_style, METH_KEYWORDS }, 215 | { "set_styles", (PyCFunction) Highlighting_set_styles, METH_KEYWORDS }, 216 | { NULL } 217 | }; 218 | 219 | 220 | PyMODINIT_FUNC 221 | inithighlighting(void) 222 | { 223 | PyObject *m; 224 | 225 | LexerStyleType.tp_new = PyType_GenericNew; 226 | if (PyType_Ready(&LexerStyleType) < 0) 227 | return; 228 | 229 | m = Py_InitModule3("highlighting", EditorModule_methods, 230 | "Highlighting information and management."); 231 | 232 | Py_INCREF(&LexerStyleType); 233 | PyModule_AddObject(m, "LexerStyle", (PyObject *)&LexerStyleType); 234 | } 235 | -------------------------------------------------------------------------------- /src/geanypy-indentprefs.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static void 9 | IndentPrefs_dealloc(IndentPrefs *self) 10 | { 11 | self->ob_type->tp_free((PyObject *) self); 12 | } 13 | 14 | 15 | static int 16 | IndentPrefs_init(IndentPrefs *self, PyObject *args, PyObject *kwds) 17 | { 18 | self->indent_prefs = NULL; 19 | return 0; 20 | } 21 | 22 | 23 | static PyObject * 24 | IndentPrefs_get_property(IndentPrefs *self, const gchar *prop_name) 25 | { 26 | g_return_val_if_fail(self != NULL, NULL); 27 | g_return_val_if_fail(prop_name != NULL, NULL); 28 | 29 | if (!self->indent_prefs) 30 | { 31 | PyErr_SetString(PyExc_RuntimeError, 32 | "IndentPrefs instance not initialized properly"); 33 | return NULL; 34 | } 35 | 36 | if (g_str_equal(prop_name, "width")) 37 | return PyInt_FromLong((glong) self->indent_prefs->width); 38 | else if (g_str_equal(prop_name, "type")) 39 | return PyInt_FromLong((glong) self->indent_prefs->type); 40 | else if (g_str_equal(prop_name, "hard_tab_width")) 41 | return PyInt_FromLong((glong) self->indent_prefs->hard_tab_width); 42 | 43 | Py_RETURN_NONE; 44 | } 45 | GEANYPY_PROPS_READONLY(IndentPrefs); 46 | 47 | 48 | static PyGetSetDef IndentPrefs_getseters[] = { 49 | GEANYPY_GETSETDEF(IndentPrefs, "width", "Indent width in characters."), 50 | GEANYPY_GETSETDEF(IndentPrefs, "type", 51 | "Whether to use tabs, spaces, or both to indent."), 52 | GEANYPY_GETSETDEF(IndentPrefs, "hard_tab_width", 53 | "Width of a tab, but only when using INDENT_TYPE_BOTH."), 54 | { NULL } 55 | }; 56 | 57 | 58 | PyTypeObject IndentPrefsType = { 59 | PyObject_HEAD_INIT(NULL) 60 | 0, /* ob_size */ 61 | "geany.editor.IndentPrefs", /* tp_name */ 62 | sizeof(IndentPrefs), /* tp_basicsize */ 63 | 0, /* tp_itemsize */ 64 | (destructor) IndentPrefs_dealloc, /* tp_dealloc */ 65 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 66 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 67 | "Wrapper around a GeanyIndentPrefs structure.", /* tp_doc */ 68 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 69 | IndentPrefs_getseters, /* tp_getset */ 70 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 71 | (initproc) IndentPrefs_init, /* tp_init */ 72 | 0, 0, /* tp_alloc - tp_new */ 73 | }; 74 | 75 | 76 | IndentPrefs *IndentPrefs_create_new_from_geany_indent_prefs(GeanyIndentPrefs *indent_prefs) 77 | { 78 | IndentPrefs *self; 79 | self = (IndentPrefs *) PyObject_CallObject((PyObject *) &IndentPrefsType, NULL); 80 | self->indent_prefs = indent_prefs; 81 | return self; 82 | } 83 | -------------------------------------------------------------------------------- /src/geanypy-interfaceprefs.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | #define RET_BOOL(memb) \ 9 | { \ 10 | if (memb) \ 11 | Py_RETURN_TRUE; \ 12 | else \ 13 | Py_RETURN_FALSE; \ 14 | } 15 | 16 | 17 | static void 18 | InterfacePrefs_dealloc(InterfacePrefs *self) 19 | { 20 | g_return_if_fail(self != NULL); 21 | self->ob_type->tp_free((PyObject *) self); 22 | } 23 | 24 | 25 | static int 26 | InterfacePrefs_init(InterfacePrefs *self) 27 | { 28 | g_return_val_if_fail(self != NULL, -1); 29 | self->iface_prefs = geany_data->interface_prefs; 30 | return 0; 31 | } 32 | 33 | 34 | static PyObject * 35 | InterfacePrefs_get_property(InterfacePrefs *self, const gchar *prop_name) 36 | { 37 | g_return_val_if_fail(self != NULL, NULL); 38 | g_return_val_if_fail(prop_name != NULL, NULL); 39 | 40 | if (!self->iface_prefs) 41 | { 42 | PyErr_SetString(PyExc_RuntimeError, 43 | "InterfacePrefs instance not initialized properly"); 44 | return NULL; 45 | } 46 | 47 | if (g_str_equal(prop_name, "compiler_tab_autoscroll")) 48 | RET_BOOL(self->iface_prefs->compiler_tab_autoscroll) 49 | else if (g_str_equal(prop_name, "editor_font")) 50 | return PyString_FromString(self->iface_prefs->editor_font); 51 | else if (g_str_equal(prop_name, "highlighting_invert_all")) 52 | RET_BOOL(self->iface_prefs->highlighting_invert_all) 53 | else if (g_str_equal(prop_name, "msgwin_compiler_visible")) 54 | RET_BOOL(self->iface_prefs->msgwin_compiler_visible) 55 | else if (g_str_equal(prop_name, "msgwin_font")) 56 | return PyString_FromString(self->iface_prefs->msgwin_font); 57 | else if (g_str_equal(prop_name, "msgwin_messages_visible")) 58 | RET_BOOL(self->iface_prefs->msgwin_messages_visible) 59 | else if (g_str_equal(prop_name, "msgwin_scribble_visible")) 60 | RET_BOOL(self->iface_prefs->msgwin_scribble_visible) 61 | else if (g_str_equal(prop_name, "msgwin_status_visible")) 62 | RET_BOOL(self->iface_prefs->msgwin_status_visible) 63 | else if (g_str_equal(prop_name, "notebook_double_click_hides_widgets")) 64 | RET_BOOL(self->iface_prefs->notebook_double_click_hides_widgets) 65 | else if (g_str_equal(prop_name, "show_notebook_tabs")) 66 | RET_BOOL(self->iface_prefs->show_notebook_tabs) 67 | else if (g_str_equal(prop_name, "show_symbol_list_expanders")) 68 | RET_BOOL(self->iface_prefs->show_symbol_list_expanders) 69 | else if (g_str_equal(prop_name, "sidebar_openfiles_visible")) 70 | RET_BOOL(self->iface_prefs->sidebar_openfiles_visible) 71 | else if (g_str_equal(prop_name, "sidebar_pos")) 72 | return PyInt_FromLong((glong) self->iface_prefs->sidebar_pos); 73 | else if (g_str_equal(prop_name, "sidebar_symbol_visible")) 74 | RET_BOOL(self->iface_prefs->sidebar_symbol_visible) 75 | else if (g_str_equal(prop_name, "statusbar_visible")) 76 | RET_BOOL(self->iface_prefs->statusbar_visible) 77 | else if (g_str_equal(prop_name, "tab_pos_editor")) 78 | return PyInt_FromLong((glong) self->iface_prefs->tab_pos_editor); 79 | else if (g_str_equal(prop_name, "tab_pos_msgwin")) 80 | return PyInt_FromLong((glong) self->iface_prefs->tab_pos_msgwin); 81 | else if (g_str_equal(prop_name, "tab_pos_sidebar")) 82 | return PyInt_FromLong((glong) self->iface_prefs->tab_pos_sidebar); 83 | else if (g_str_equal(prop_name, "tagbar_font")) 84 | return PyString_FromString(self->iface_prefs->tagbar_font); 85 | else if (g_str_equal(prop_name, "use_native_windows_dialogs")) 86 | RET_BOOL(self->iface_prefs->use_native_windows_dialogs) 87 | 88 | Py_RETURN_NONE; 89 | } 90 | GEANYPY_PROPS_READONLY(InterfacePrefs); 91 | 92 | 93 | static PyGetSetDef InterfacePrefs_getseters[] = { 94 | GEANYPY_GETSETDEF(InterfacePrefs, "compiler_tab_autoscroll", 95 | "Wether compiler messages window is automatically scrolled to " 96 | "show new messages."), 97 | GEANYPY_GETSETDEF(InterfacePrefs, "editor_font", 98 | "Font used in the editor window."), 99 | GEANYPY_GETSETDEF(InterfacePrefs, "highlighting_invert_all", 100 | "Whether highlighting colors are inverted."), 101 | GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_compiler_visible", 102 | "Wether message window's compiler tab is visible."), 103 | GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_font", 104 | "Font used in the message window."), 105 | GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_messages_visible", 106 | "Whether message window's messages tab is visible."), 107 | GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_scribble_visible", 108 | "Whether message window's scribble tab is visible."), 109 | GEANYPY_GETSETDEF(InterfacePrefs, "msgwin_status_visible", 110 | "Whether message window's status tab is visible."), 111 | GEANYPY_GETSETDEF(InterfacePrefs, "notebook_double_click_hides_widgets", 112 | "Whether a double-click on the notebook tabs hides all other windows."), 113 | GEANYPY_GETSETDEF(InterfacePrefs, "show_notebook_tabs", 114 | "Whether editor tabs are visible."), 115 | GEANYPY_GETSETDEF(InterfacePrefs, "show_symbol_list_expanders", 116 | "Whether to show expanders in the symbol list."), 117 | GEANYPY_GETSETDEF(InterfacePrefs, "sidebar_openfiles_visible", 118 | "Whether the open files list is visible."), 119 | GEANYPY_GETSETDEF(InterfacePrefs, "sidebar_pos", 120 | "Position of the sidebar (left or right)."), 121 | GEANYPY_GETSETDEF(InterfacePrefs, "sidebar_symbol_visible", 122 | "Whether the status bar is visible."), 123 | GEANYPY_GETSETDEF(InterfacePrefs, "statusbar_visible", 124 | "Whether the statusbar is visible."), 125 | GEANYPY_GETSETDEF(InterfacePrefs, "tab_pos_editor", 126 | "Position of the editor's tabs."), 127 | GEANYPY_GETSETDEF(InterfacePrefs, "tab_pos_msgwin", 128 | "Position of the message window tabs."), 129 | GEANYPY_GETSETDEF(InterfacePrefs, "tab_pos_sidebar", 130 | "Position of the sidebar's tabs."), 131 | GEANYPY_GETSETDEF(InterfacePrefs, "tagbar_font", 132 | "Symbol sidebar font."), 133 | GEANYPY_GETSETDEF(InterfacePrefs, "use_native_windows_dialogs", 134 | "Whether to use native Windows dialogs (only on Win32)."), 135 | { NULL } 136 | }; 137 | 138 | 139 | PyTypeObject InterfacePrefsType = { 140 | PyObject_HEAD_INIT(NULL) 141 | 0, /* ob_size */ 142 | "geany.ui_utils.InterfacePrefs", /* tp_name */ 143 | sizeof(InterfacePrefs), /* tp_basicsize */ 144 | 0, /* tp_itemsize */ 145 | (destructor) InterfacePrefs_dealloc, /* tp_dealloc */ 146 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 147 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 148 | "Wrapper around a GeanyInterfacePrefs structure.", /* tp_doc */ 149 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 150 | InterfacePrefs_getseters, /* tp_getset */ 151 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 152 | (initproc) InterfacePrefs_init, /* tp_init */ 153 | 0, 0, /* tp_alloc - tp_new */ 154 | }; 155 | -------------------------------------------------------------------------------- /src/geanypy-keybindings.c: -------------------------------------------------------------------------------- 1 | /* 2 | * plugin.c 3 | * 4 | * Copyright 2015 Thomas Martitz 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | */ 21 | 22 | 23 | #include "geanypy.h" 24 | #include "geanypy-keybindings.h" 25 | 26 | #include 27 | 28 | static gboolean call_key(gpointer *unused, guint key_id, gpointer data) 29 | { 30 | PyObject *callback = data; 31 | PyObject *args; 32 | 33 | args = Py_BuildValue("(i)", key_id); 34 | PyObject_CallObject(callback, args); 35 | Py_DECREF(args); 36 | } 37 | 38 | 39 | /* plugin.py provides an OOP-style wrapper around this so call it like: 40 | * class Foo(geany.Plugin): 41 | * def __init__(self): 42 | * self.set_key_group(...) 43 | */ 44 | static PyObject * 45 | Keybindings_set_key_group(PyObject *self, PyObject *args, PyObject *kwargs) 46 | { 47 | static gchar *kwlist[] = { "plugin", "section_name", "count", "callback", NULL }; 48 | int count = 0; 49 | const gchar *section_name = NULL; 50 | GeanyKeyGroup *group = NULL; 51 | PyObject *py_callback = NULL; 52 | PyObject *py_ret = Py_None; 53 | PyObject *py_plugin; 54 | gboolean has_cb = FALSE; 55 | 56 | Py_INCREF(Py_None); 57 | 58 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "Osi|O", kwlist, 59 | &py_plugin, §ion_name, &count, &py_callback)) 60 | { 61 | GeanyPlugin *plugin = plugin_get(py_plugin); 62 | g_return_val_if_fail(plugin != NULL, Py_None); 63 | 64 | has_cb = PyCallable_Check(py_callback); 65 | if (has_cb) 66 | { 67 | Py_INCREF(py_callback); 68 | group = plugin_set_key_group_full(plugin, section_name, count, 69 | (GeanyKeyGroupFunc) call_key, py_callback, 70 | (GDestroyNotify) Py_DecRef); 71 | } 72 | else 73 | group = plugin_set_key_group(plugin, section_name, count, NULL); 74 | } 75 | 76 | if (group) 77 | { 78 | Py_DECREF(py_ret); 79 | py_ret = KeyGroup_new_with_geany_key_group(group, has_cb); 80 | } 81 | 82 | return py_ret; 83 | } 84 | 85 | 86 | static PyObject * 87 | KeyGroup_add_key_item(KeyGroup *self, PyObject *args, PyObject *kwargs) 88 | { 89 | static gchar *kwlist[] = { "name", "label", "callback", "key_id", "key", "mod" , "menu_item", NULL }; 90 | int id = -1; 91 | int key = 0, mod = 0; 92 | const gchar *name = NULL, *label = NULL; 93 | PyObject *py_menu_item = NULL; 94 | PyObject *py_callback = NULL; 95 | GeanyKeyBinding *item = NULL; 96 | 97 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "ss|OiiiO", kwlist, 98 | &name, &label, &py_callback, &id, &key, &mod, &py_menu_item)) 99 | { 100 | if (id == -1) 101 | id = self->item_index; 102 | 103 | GtkWidget *menu_item = (py_menu_item == NULL || py_menu_item == Py_None) 104 | ? NULL : GTK_WIDGET(pygobject_get(py_menu_item)); 105 | if (PyCallable_Check(py_callback)) 106 | { 107 | Py_INCREF(py_callback); 108 | item = keybindings_set_item_full(self->kb_group, id, (guint) key, 109 | (GdkModifierType) mod, name, label, menu_item, 110 | (GeanyKeyBindingFunc) call_key, py_callback, 111 | (GDestroyNotify) Py_DecRef); 112 | } 113 | else 114 | { 115 | if (!self->has_cb) 116 | g_warning("Either KeyGroup or the Keybinding must have a callback\n"); 117 | else 118 | item = keybindings_set_item(self->kb_group, id, NULL, (guint) key, 119 | (GdkModifierType) mod, name, label, menu_item); 120 | } 121 | Py_XDECREF(py_menu_item); 122 | 123 | self->item_index = id + 1; 124 | } 125 | 126 | if (item) 127 | { 128 | /* Return a tuple containing the key group and the opaque GeanyKeyBinding pointer. 129 | * This is in preparation of allowing chained calls like 130 | * set_kb_group(X, 3).add_key_item().add_key_item().add_key_item() 131 | * without losing access to the keybinding pointer (might become necessary for newer 132 | * Geany APIs). 133 | * Note that the plain tuple doesn't support the above yet, we've got to subclass it, 134 | * but we are prepared without breaking sub-plugins */ 135 | PyObject *ret = PyTuple_Pack(2, self, PyCapsule_New(item, "GeanyKeyBinding", NULL)); 136 | return ret; 137 | } 138 | Py_RETURN_NONE; 139 | } 140 | 141 | 142 | static PyMethodDef 143 | KeyGroup_methods[] = { 144 | { "add_key_item", (PyCFunction)KeyGroup_add_key_item, METH_KEYWORDS, 145 | "Adds an action to the plugin's key group" }, 146 | { NULL } 147 | }; 148 | 149 | static PyMethodDef 150 | Keybindings_methods[] = { 151 | { "set_key_group", (PyCFunction)Keybindings_set_key_group, METH_KEYWORDS, 152 | "Sets up a GeanyKeybindingGroup for this plugin." }, 153 | { NULL } 154 | }; 155 | 156 | 157 | static PyGetSetDef 158 | KeyGroup_getseters[] = { 159 | { NULL }, 160 | }; 161 | 162 | 163 | static void 164 | KeyGroup_dealloc(KeyGroup *self) 165 | { 166 | g_return_if_fail(self != NULL); 167 | self->ob_type->tp_free((PyObject *) self); 168 | } 169 | 170 | 171 | static PyTypeObject KeyGroupType = { 172 | PyObject_HEAD_INIT(NULL) 173 | 0, /* ob_size */ 174 | "geany.keybindings.KeyGroup", /* tp_name */ 175 | sizeof(KeyGroup), /* tp_basicsize */ 176 | 0, /* tp_itemsize */ 177 | (destructor) KeyGroup_dealloc, /* tp_dealloc */ 178 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 179 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 180 | "Wrapper around a GeanyKeyGroup structure." ,/* tp_doc */ 181 | 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_iternext */ 182 | KeyGroup_methods, /* tp_methods */ 183 | 0, /* tp_members */ 184 | KeyGroup_getseters, /* tp_getset */ 185 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 186 | 0, 0, (newfunc) PyType_GenericNew, /* tp_init - tp_alloc, tp_new */ 187 | }; 188 | 189 | 190 | PyMODINIT_FUNC initkeybindings(void) 191 | { 192 | PyObject *m; 193 | 194 | if (PyType_Ready(&KeyGroupType) < 0) 195 | return; 196 | 197 | m = Py_InitModule3("keybindings", Keybindings_methods, "Keybindings support."); 198 | 199 | Py_INCREF(&KeyGroupType); 200 | PyModule_AddObject(m, "KeyGroup", (PyObject *)&KeyGroupType); 201 | } 202 | 203 | PyObject *KeyGroup_new_with_geany_key_group(GeanyKeyGroup *group, gboolean has_cb) 204 | { 205 | KeyGroup *ret = PyObject_New(KeyGroup, &KeyGroupType); 206 | 207 | ret->kb_group = group; 208 | ret->has_cb = has_cb; 209 | ret->item_index = 0; 210 | 211 | return (PyObject *) ret; 212 | } 213 | 214 | -------------------------------------------------------------------------------- /src/geanypy-keybindings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * geanypy-keybindings.h 3 | * 4 | * Copyright 2015 Thomas Martitz 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | */ 21 | 22 | 23 | #ifndef GEANYPY_KEYBINDINGS_H 24 | #define GEANYPY_KEYBINDINGS_H 25 | 26 | #include 27 | 28 | typedef struct 29 | { 30 | PyObject_HEAD 31 | GeanyKeyGroup *kb_group; 32 | gboolean has_cb; 33 | gint item_index; 34 | } KeyGroup; 35 | 36 | extern PyObject * 37 | KeyGroup_new_with_geany_key_group(GeanyKeyGroup *group, gboolean has_cb); 38 | 39 | #endif /* GEANYPY_KEYBINDINGS_H */ 40 | -------------------------------------------------------------------------------- /src/geanypy-main.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static PyObject * 9 | Main_is_realized(PyObject *module) 10 | { 11 | if (main_is_realized()) 12 | Py_RETURN_TRUE; 13 | else 14 | Py_RETURN_FALSE; 15 | } 16 | 17 | 18 | static PyObject * 19 | Main_locale_init(PyObject *module, PyObject *args, PyObject *kwargs) 20 | { 21 | gchar *locale_dir = NULL, *package = NULL; 22 | static gchar *kwlist[] = { "locale_dir", "gettext_package", NULL }; 23 | 24 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &locale_dir, &package)) 25 | { 26 | if (locale_dir && package) 27 | main_locale_init(locale_dir, package); 28 | } 29 | 30 | Py_RETURN_NONE; 31 | } 32 | 33 | 34 | static PyObject * 35 | Main_reload_configuration(PyObject *module) 36 | { 37 | main_reload_configuration(); 38 | Py_RETURN_NONE; 39 | } 40 | 41 | 42 | static 43 | PyMethodDef MainModule_methods[] = { 44 | { "is_realized", (PyCFunction) Main_is_realized, METH_NOARGS, 45 | "Checks whether the main gtk.Window has been realized." }, 46 | { "locale_init", (PyCFunction) Main_locale_init, METH_KEYWORDS, 47 | "Initializes the gettext translation system." }, 48 | { "reload_configuration", (PyCFunction) Main_reload_configuration, METH_NOARGS, 49 | "Reloads most of Geany's configuration files without restarting." }, 50 | { NULL } 51 | }; 52 | 53 | 54 | PyMODINIT_FUNC initmain(void) 55 | { 56 | Py_InitModule3("main", MainModule_methods, "Main program related functions."); 57 | } 58 | -------------------------------------------------------------------------------- /src/geanypy-mainwidgets.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static void 9 | MainWidgets_dealloc(MainWidgets *self) 10 | { 11 | self->ob_type->tp_free((PyObject *) self); 12 | } 13 | 14 | 15 | static int 16 | MainWidgets_init(MainWidgets *self) 17 | { 18 | self->main_widgets = geany_data->main_widgets; 19 | return 0; 20 | } 21 | 22 | 23 | static PyObject * 24 | MainWidgets_get_property(MainWidgets *self, const gchar *prop_name) 25 | { 26 | g_return_val_if_fail(self != NULL, NULL); 27 | g_return_val_if_fail(prop_name != NULL, NULL); 28 | 29 | if (!self->main_widgets) 30 | { 31 | PyErr_SetString(PyExc_RuntimeError, 32 | "MainWidgets instance not initialized properly"); 33 | return NULL; 34 | } 35 | 36 | if (g_str_equal(prop_name, "editor_menu")) 37 | return pygobject_new(G_OBJECT(self->main_widgets->editor_menu)); 38 | else if (g_str_equal(prop_name, "message_window_notebook")) 39 | return pygobject_new(G_OBJECT(self->main_widgets->message_window_notebook)); 40 | else if (g_str_equal(prop_name, "notebook")) 41 | return pygobject_new(G_OBJECT(self->main_widgets->notebook)); 42 | else if (g_str_equal(prop_name, "progressbar")) 43 | return pygobject_new(G_OBJECT(self->main_widgets->progressbar)); 44 | else if (g_str_equal(prop_name, "project_menu")) 45 | return pygobject_new(G_OBJECT(self->main_widgets->project_menu)); 46 | else if (g_str_equal(prop_name, "sidebar_notebook")) 47 | return pygobject_new(G_OBJECT(self->main_widgets->sidebar_notebook)); 48 | else if (g_str_equal(prop_name, "toolbar")) 49 | return pygobject_new(G_OBJECT(self->main_widgets->toolbar)); 50 | else if (g_str_equal(prop_name, "tools_menu")) 51 | return pygobject_new(G_OBJECT(self->main_widgets->tools_menu)); 52 | else if (g_str_equal(prop_name, "window")) 53 | return pygobject_new(G_OBJECT(self->main_widgets->window)); 54 | 55 | Py_RETURN_NONE; 56 | } 57 | GEANYPY_PROPS_READONLY(MainWidgets); 58 | 59 | 60 | static PyGetSetDef MainWidgets_getseters[] = { 61 | GEANYPY_GETSETDEF(MainWidgets, "editor_menu", 62 | "Popup context menu on the editor widget."), 63 | GEANYPY_GETSETDEF(MainWidgets, "message_window_notebook", 64 | "Notebook in the bottom message window."), 65 | GEANYPY_GETSETDEF(MainWidgets, "notebook", 66 | "The central documents notebook."), 67 | GEANYPY_GETSETDEF(MainWidgets, "progressbar", 68 | "Progress bar in the bottom status bar."), 69 | GEANYPY_GETSETDEF(MainWidgets, "project_menu", 70 | "The Project menu in the main menu bar."), 71 | GEANYPY_GETSETDEF(MainWidgets, "sidebar_notebook", 72 | "The notebook in the sidebar."), 73 | GEANYPY_GETSETDEF(MainWidgets, "toolbar", 74 | "Main toolbar."), 75 | GEANYPY_GETSETDEF(MainWidgets, "tools_menu", 76 | "The Tools menu in the main menu bar (recommended for plugins)."), 77 | GEANYPY_GETSETDEF(MainWidgets, "window", 78 | "Main window."), 79 | { NULL }, 80 | }; 81 | 82 | 83 | PyTypeObject MainWidgetsType = { 84 | PyObject_HEAD_INIT(NULL) 85 | 0, /* ob_size */ 86 | "geany.ui_utils.MainWidgets", /* tp_name */ 87 | sizeof(MainWidgets), /* tp_basicsize */ 88 | 0, /* tp_itemsize */ 89 | (destructor) MainWidgets_dealloc, /* tp_dealloc */ 90 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 91 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 92 | "Wrapper around the GeanyMainWidgets structure.", /* tp_doc */ 93 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 94 | MainWidgets_getseters, /* tp_getset */ 95 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 96 | (initproc) MainWidgets_init, /* tp_init */ 97 | 0, 0, /* tp_alloc - tp_new */ 98 | 99 | }; 100 | -------------------------------------------------------------------------------- /src/geanypy-msgwindow.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static PyObject * 9 | Msgwin_clear_tab(PyObject *module, PyObject *args, PyObject *kwargs) 10 | { 11 | gint tab_num = 0; 12 | static gchar *kwlist[] = { "tabnum", NULL }; 13 | 14 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &tab_num)) 15 | msgwin_clear_tab(tab_num); 16 | 17 | Py_RETURN_NONE; 18 | } 19 | 20 | 21 | static PyObject * 22 | Msgwin_compiler_add(PyObject *module, PyObject *args, PyObject *kwargs) 23 | { 24 | gint msg_color = COLOR_BLACK; 25 | gchar *msg = NULL; 26 | static gchar *kwlist[] = { "msg", "msg_color", NULL }; 27 | 28 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist, &msg, &msg_color)) 29 | msgwin_compiler_add(msg_color, "%s", msg); 30 | 31 | Py_RETURN_NONE; 32 | } 33 | 34 | 35 | static PyObject * 36 | Msgwin_msg_add(PyObject *module, PyObject *args, PyObject *kwargs) 37 | { 38 | gint msg_color = COLOR_BLACK, line = -1; 39 | PyObject *obj = NULL; 40 | Document *py_doc = NULL; 41 | GeanyDocument *doc = NULL; 42 | gchar *msg = NULL; 43 | static gchar *kwlist[] = { "msg", "msg_color", "line", "doc", NULL }; 44 | 45 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "s|iiO", kwlist, &msg, 46 | &msg_color, &line, &obj)) 47 | { 48 | if (obj == NULL || obj == Py_None) 49 | doc = NULL; 50 | else 51 | { 52 | py_doc = (Document *) obj; 53 | doc = py_doc->doc; 54 | } 55 | msgwin_msg_add(msg_color, line, doc, "%s", msg); 56 | } 57 | 58 | Py_RETURN_NONE; 59 | } 60 | 61 | 62 | static PyObject * 63 | Msgwin_set_messages_dir(PyObject *module, PyObject *args, PyObject *kwargs) 64 | { 65 | gchar *msgdir = NULL; 66 | static gchar *kwlist[] = { "messages_dir", NULL }; 67 | 68 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &msgdir)) 69 | { 70 | if (msgdir != NULL) 71 | msgwin_set_messages_dir(msgdir); 72 | } 73 | 74 | Py_RETURN_NONE; 75 | } 76 | 77 | 78 | static PyObject * 79 | Msgwin_status_add(PyObject *module, PyObject *args, PyObject *kwargs) 80 | { 81 | gchar *msg = NULL; 82 | static gchar *kwlist[] = { "msg", NULL }; 83 | 84 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &msg)) 85 | { 86 | if (msg != NULL) 87 | msgwin_status_add("%s", msg); 88 | } 89 | 90 | Py_RETURN_NONE; 91 | } 92 | 93 | 94 | static PyObject * 95 | Msgwin_switch_tab(PyObject *module, PyObject *args, PyObject *kwargs) 96 | { 97 | gint tabnum = 0, show = 0; 98 | static gchar *kwlist[] = { "tabnum", "show", NULL }; 99 | 100 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &tabnum, &show)) 101 | msgwin_switch_tab(tabnum, show); 102 | 103 | Py_RETURN_NONE; 104 | } 105 | 106 | 107 | static 108 | PyMethodDef MsgwinModule_methods[] = { 109 | { "clear_tab", (PyCFunction) Msgwin_clear_tab, METH_KEYWORDS }, 110 | { "compiler_add", (PyCFunction) Msgwin_compiler_add, METH_KEYWORDS }, 111 | { "msg_add", (PyCFunction) Msgwin_msg_add, METH_KEYWORDS }, 112 | { "set_messages_dir", (PyCFunction) Msgwin_set_messages_dir, METH_KEYWORDS }, 113 | { "status_add", (PyCFunction) Msgwin_status_add, METH_KEYWORDS }, 114 | { "switch_tab", (PyCFunction) Msgwin_switch_tab, METH_KEYWORDS }, 115 | { NULL } 116 | }; 117 | 118 | 119 | PyMODINIT_FUNC 120 | initmsgwin(void) 121 | { 122 | PyObject *m; 123 | 124 | m = Py_InitModule3("msgwindow", MsgwinModule_methods, 125 | "Message windows information and management."); 126 | 127 | PyModule_AddIntConstant(m, "COLOR_RED", COLOR_RED); 128 | PyModule_AddIntConstant(m, "COLOR_DARK_RED", COLOR_DARK_RED); 129 | PyModule_AddIntConstant(m, "COLOR_BLACK", COLOR_BLACK); 130 | PyModule_AddIntConstant(m, "COLOR_BLUE", COLOR_BLUE); 131 | 132 | PyModule_AddIntConstant(m, "TAB_STATUS", MSG_STATUS); 133 | PyModule_AddIntConstant(m, "TAB_COMPILER", MSG_COMPILER); 134 | PyModule_AddIntConstant(m, "TAB_MESSAGE", MSG_MESSAGE); 135 | PyModule_AddIntConstant(m, "TAB_SCRIBBLE", MSG_SCRATCH); 136 | PyModule_AddIntConstant(m, "TAB_TERMINAL", MSG_VTE); 137 | } 138 | -------------------------------------------------------------------------------- /src/geanypy-navqueue.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static PyObject * 9 | Navqueue_goto_line(PyObject *module, PyObject *args, PyObject *kwargs) 10 | { 11 | gint line = 1; 12 | PyObject *py_old = NULL, *py_new = NULL; 13 | Document *py_doc_old, *py_doc_new; 14 | GeanyDocument *old_doc, *new_doc; 15 | static gchar *kwlist[] = { "old_doc", "new_doc", "line", NULL }; 16 | 17 | if (PyArg_ParseTupleAndKeywords(args, kwargs, "OOi", kwlist, &py_old, 18 | &py_new, &line)) 19 | { 20 | if (!py_old || py_old == Py_None) 21 | old_doc = NULL; 22 | else 23 | { 24 | py_doc_old = (Document *) py_old; 25 | old_doc = py_doc_old->doc; 26 | } 27 | 28 | if (!py_new || py_new == Py_None) 29 | Py_RETURN_NONE; 30 | else 31 | { 32 | py_doc_new = (Document *) py_new; 33 | new_doc = py_doc_new->doc; 34 | } 35 | 36 | if ( (old_doc != NULL && !DOC_VALID(old_doc)) || !DOC_VALID(new_doc) ) 37 | Py_RETURN_NONE; 38 | if (navqueue_goto_line(old_doc, new_doc, line)) 39 | Py_RETURN_TRUE; 40 | else 41 | Py_RETURN_FALSE; 42 | } 43 | 44 | Py_RETURN_NONE; 45 | } 46 | 47 | 48 | #ifdef ENABLE_PRIVATE 49 | static PyObject * 50 | Navqueue_go_back(PyObject *module) 51 | { 52 | navqueue_go_back(); 53 | Py_RETURN_NONE; 54 | } 55 | 56 | 57 | static PyObject * 58 | Navqueue_go_forward(PyObject *module) 59 | { 60 | navqueue_go_forward(); 61 | Py_RETURN_NONE; 62 | } 63 | #endif 64 | 65 | 66 | static 67 | PyMethodDef NavqueueModule_methods[] = { 68 | { "goto_line", (PyCFunction) Navqueue_goto_line, METH_KEYWORDS, 69 | "Adds old file position and new file position to the navqueue, " 70 | "then goes to the new position." }, 71 | #ifdef ENABLE_PRIVATE 72 | { "go_back", (PyCFunction) Navqueue_go_back, METH_NOARGS, 73 | "Navigate backwards." }, 74 | { "go_forward", (PyCFunction) Navqueue_go_forward, METH_NOARGS, 75 | "Navigate forward." }, 76 | #endif 77 | { NULL } 78 | }; 79 | 80 | 81 | PyMODINIT_FUNC initnavqueue(void) 82 | { 83 | Py_InitModule3("navqueue", NavqueueModule_methods, "Simple code navigation."); 84 | } 85 | -------------------------------------------------------------------------------- /src/geanypy-plugin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * plugin.c 3 | * 4 | * Copyright 2011 Matthew Brush 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19 | * MA 02110-1301, USA. 20 | */ 21 | 22 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 23 | # include "config.h" 24 | #endif 25 | 26 | #define INCLUDE_PYGOBJECT_ONCE_FULL 27 | 28 | #include "geanypy.h" 29 | #include "geanypy-keybindings.h" 30 | 31 | #include 32 | #include 33 | 34 | GeanyData *geany_data; 35 | 36 | /* Forward declarations to prevent compiler warnings. */ 37 | PyMODINIT_FUNC initapp(void); 38 | PyMODINIT_FUNC initdialogs(void); 39 | PyMODINIT_FUNC initdocument(void); 40 | PyMODINIT_FUNC initeditor(void); 41 | PyMODINIT_FUNC initencoding(void); 42 | PyMODINIT_FUNC initfiletypes(void); 43 | PyMODINIT_FUNC initglog(void); 44 | PyMODINIT_FUNC inithighlighting(void); 45 | PyMODINIT_FUNC initmain(void); 46 | PyMODINIT_FUNC initmsgwin(void); 47 | PyMODINIT_FUNC initnavqueue(void); 48 | PyMODINIT_FUNC initprefs(void); 49 | PyMODINIT_FUNC initproject(void); 50 | PyMODINIT_FUNC initscintilla(void); 51 | PyMODINIT_FUNC initsearch(void); 52 | PyMODINIT_FUNC inittemplates(void); 53 | PyMODINIT_FUNC initui_utils(void); 54 | PyMODINIT_FUNC initkeybindings(void); 55 | 56 | 57 | static void 58 | GeanyPy_start_interpreter(void) 59 | { 60 | gchar *init_code; 61 | gchar *py_dir = NULL; 62 | 63 | 64 | #ifndef GEANYPY_WINDOWS 65 | { /* Prevents a crash in the dynload thingy 66 | TODO: is this or the old dlopen version even needed? */ 67 | GModule *mod = g_module_open(GEANYPY_PYTHON_LIBRARY, G_MODULE_BIND_LAZY); 68 | if (!mod) { 69 | g_warning(_("Unable to pre-load Python library: %s."), g_module_error()); 70 | return; 71 | } 72 | g_module_close(mod); 73 | } 74 | #endif 75 | 76 | Py_Initialize(); 77 | 78 | /* Import the C modules */ 79 | initapp(); 80 | initdialogs(); 81 | initdocument(); 82 | initeditor(); 83 | initencoding(); 84 | initfiletypes(); 85 | initglog(); 86 | inithighlighting(); 87 | initmain(); 88 | initmsgwin(); 89 | initnavqueue(); 90 | initprefs(); 91 | initproject(); 92 | initscintilla(); 93 | initsearch(); 94 | inittemplates(); 95 | initui_utils(); 96 | initkeybindings(); 97 | 98 | #ifdef GEANYPY_WINDOWS 99 | { /* On windows, get path at runtime since we don't really know where 100 | * Geany is installed ahead of time. */ 101 | gchar *geany_base_dir; 102 | geany_base_dir = g_win32_get_package_installation_directory_of_module(NULL); 103 | if (geany_base_dir) 104 | { 105 | py_dir = g_build_filename(geany_base_dir, "lib", "geanypy", NULL); 106 | g_free(geany_base_dir); 107 | } 108 | if (!g_file_test(py_dir, G_FILE_TEST_EXISTS)) 109 | { 110 | g_critical("The path to the `geany' module was not found: %s", py_dir); 111 | g_free(py_dir); 112 | py_dir = g_strdup(""); /* will put current dir on path? */ 113 | } 114 | } 115 | #else 116 | py_dir = g_strdup(GEANYPY_PYTHON_DIR); 117 | #endif 118 | 119 | /* Adjust Python path to find wrapper package (geany) */ 120 | init_code = g_strdup_printf( 121 | "import os, sys\n" 122 | "path = '%s'.replace('~', os.path.expanduser('~'))\n" 123 | "sys.path.append(path)\n" 124 | "path = '%s/plugins'.replace('~', os.path.expanduser('~'))\n" 125 | "sys.path.append(path)\n" 126 | "path = '%s'.replace('~', os.path.expanduser('~'))\n" 127 | "sys.path.append(path)\n" 128 | "import geany\n", py_dir, geany_data->app->configdir, GEANYPY_PLUGIN_DIR); 129 | g_free(py_dir); 130 | 131 | PyRun_SimpleString(init_code); 132 | g_free(init_code); 133 | 134 | } 135 | 136 | static void 137 | GeanyPy_stop_interpreter(void) 138 | { 139 | if (Py_IsInitialized()) 140 | Py_Finalize(); 141 | } 142 | 143 | typedef struct 144 | { 145 | PyObject *base; 146 | SignalManager *signal_manager; 147 | } 148 | GeanyPyData; 149 | 150 | typedef struct 151 | { 152 | PyObject *class; 153 | PyObject *module; 154 | PyObject *instance; 155 | } 156 | GeanyPyPluginData; 157 | 158 | static gboolean has_error(void) 159 | { 160 | if (PyErr_Occurred()) 161 | { 162 | PyErr_Print(); 163 | return TRUE; 164 | } 165 | return FALSE; 166 | } 167 | 168 | static gboolean geanypy_proxy_init(GeanyPlugin *plugin, gpointer pdata) 169 | { 170 | GeanyPyPluginData *data = (GeanyPyPluginData *) pdata; 171 | 172 | data->instance = PyObject_CallObject(data->class, NULL); 173 | if (has_error()) 174 | return FALSE; 175 | 176 | return TRUE; 177 | } 178 | 179 | 180 | static void geanypy_proxy_cleanup(GeanyPlugin *plugin, gpointer pdata) 181 | { 182 | GeanyPyPluginData *data = (GeanyPyPluginData *) pdata; 183 | 184 | PyObject_CallMethod(data->instance, "cleanup", NULL); 185 | if (has_error()) 186 | return; 187 | } 188 | 189 | 190 | static GtkWidget *geanypy_proxy_configure(GeanyPlugin *plugin, GtkDialog *parent, gpointer pdata) 191 | { 192 | GeanyPyPluginData *data = (GeanyPyPluginData *) pdata; 193 | PyObject *o, *oparent; 194 | GObject *widget; 195 | 196 | oparent = pygobject_new(G_OBJECT(parent)); 197 | o = PyObject_CallMethod(data->instance, "configure", "O", oparent, NULL); 198 | Py_DECREF(oparent); 199 | 200 | if (!has_error() && o != Py_None) 201 | { 202 | /* Geany wants only the underlying GtkWidget, we must only ref that 203 | * and free the pygobject wrapper */ 204 | widget = g_object_ref(pygobject_get(o)); 205 | Py_DECREF(o); 206 | return GTK_WIDGET(widget); 207 | } 208 | 209 | Py_DECREF(o); /* Must unref even if it's Py_None */ 210 | return NULL; 211 | } 212 | 213 | 214 | static void do_show_configure(GtkWidget *button, gpointer pdata) 215 | { 216 | GeanyPyPluginData *data = (GeanyPyPluginData *) pdata; 217 | PyObject_CallMethod(data->instance, "show_configure", NULL); 218 | } 219 | 220 | 221 | static GtkWidget *geanypy_proxy_configure_legacy(GeanyPlugin *plugin, GtkDialog *parent, gpointer pdata) 222 | { 223 | GeanyPyPluginData *data = (GeanyPyPluginData *) pdata; 224 | PyObject *o, *oparent; 225 | GtkWidget *box, *label, *button, *align; 226 | gchar *text; 227 | 228 | /* This creates a simple page that has only one button to show the plugin's legacy configure 229 | * dialog. It is for older plugins that implement show_configure(). It's not pretty but 230 | * it provides basic backwards compatibility. */ 231 | box = gtk_vbox_new(FALSE, 2); 232 | 233 | text = g_strdup_printf("The plugin \"%s\" is older and hasn't been updated\nto provide a configuration UI. However, it provides a dialog to\nallow you to change the plugin's preferences.", plugin->info->name); 234 | label = gtk_label_new(text); 235 | 236 | align = gtk_alignment_new(0, 0, 1, 1); 237 | gtk_container_add(GTK_CONTAINER(align), label); 238 | gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 6, 2, 2); 239 | gtk_box_pack_start(GTK_BOX(box), align, FALSE, FALSE, 0); 240 | 241 | button = gtk_button_new_with_label("Open dialog"); 242 | align = gtk_alignment_new(0.5, 0, 0.3f, 1); 243 | gtk_container_add(GTK_CONTAINER(align), button); 244 | g_signal_connect(button, "clicked", (GCallback) do_show_configure, pdata); 245 | gtk_box_pack_start(GTK_BOX(box), align, FALSE, TRUE, 0); 246 | 247 | gtk_widget_show_all(box); 248 | g_free(text); 249 | return box; 250 | } 251 | 252 | static void geanypy_proxy_help(GeanyPlugin *plugin, gpointer pdata) 253 | { 254 | GeanyPyPluginData *data = (GeanyPyPluginData *) pdata; 255 | 256 | PyObject_CallMethod(data->instance, "help", NULL); 257 | if (has_error()) 258 | return; 259 | } 260 | 261 | static gint 262 | geanypy_probe(GeanyPlugin *proxy, const gchar *filename, gpointer pdata) 263 | { 264 | gchar *file_plugin = g_strdup_printf("%.*s.plugin", 265 | (int)(strrchr(filename, '.') - filename), filename); 266 | gint ret = PROXY_IGNORED; 267 | 268 | /* avoid clash with libpeas py plugins, those come with a corresponding .plugin file */ 269 | if (!g_file_test(file_plugin, G_FILE_TEST_EXISTS)) 270 | ret = PROXY_MATCHED; 271 | 272 | g_free(file_plugin); 273 | return ret; 274 | } 275 | 276 | 277 | static const gchar *string_from_attr(PyObject *o, const gchar *attr) 278 | { 279 | PyObject *string = PyObject_GetAttrString(o, attr); 280 | const gchar *ret = PyString_AsString(string); 281 | Py_DECREF(string); 282 | 283 | return ret; 284 | } 285 | 286 | 287 | static gpointer 288 | geanypy_load(GeanyPlugin *proxy, GeanyPlugin *subplugin, const gchar *filename, gpointer pdata) 289 | { 290 | GeanyPyData *data = pdata; 291 | PyObject *fromlist, *module, *dict, *key, *val, *found = NULL; 292 | Py_ssize_t pos = 0; 293 | gchar *modulename, *dot; 294 | gpointer ret = NULL; 295 | 296 | modulename = g_path_get_basename(filename); 297 | /* We are guaranteed that filename has a .py extension 298 | * because we did geany_plugin_register_proxy() for it */ 299 | dot = strrchr(modulename, '.'); 300 | *dot = '\0'; 301 | /* we need a fromlist to be able to import modules with a '.' in the 302 | * name. -- libpeas */ 303 | fromlist = PyTuple_New (0); 304 | 305 | module = PyImport_ImportModuleEx(modulename, NULL, NULL, fromlist); 306 | if (has_error() || !module) 307 | goto err; 308 | 309 | dict = PyModule_GetDict(module); 310 | 311 | while (PyDict_Next (dict, &pos, &key, &val) && found == NULL) 312 | { 313 | if (PyType_Check(val) && PyObject_IsSubclass(val, data->base)) 314 | found = val; 315 | } 316 | 317 | if (found) 318 | { 319 | GeanyPyPluginData *pdata = g_slice_new(GeanyPyPluginData); 320 | PluginInfo *info = subplugin->info; 321 | GeanyPluginFuncs *funcs = subplugin->funcs; 322 | PyObject *caps = PyCapsule_New(subplugin, "GeanyPlugin", NULL); 323 | Py_INCREF(found); 324 | pdata->module = module; 325 | pdata->class = found; 326 | PyObject_SetAttrString(pdata->class, "__geany_plugin__", caps); 327 | pdata->instance = NULL; 328 | info->name = string_from_attr(pdata->class, "__plugin_name__"); 329 | info->description = string_from_attr(pdata->class, "__plugin_description__"); 330 | info->version = string_from_attr(pdata->class, "__plugin_version__"); 331 | info->author = string_from_attr(pdata->class, "__plugin_author__"); 332 | funcs->init = geanypy_proxy_init; 333 | funcs->cleanup = geanypy_proxy_cleanup; 334 | if (PyObject_HasAttrString(found, "configure")) 335 | funcs->configure = geanypy_proxy_configure; 336 | else if (PyObject_HasAttrString(found, "show_configure")) 337 | funcs->configure = geanypy_proxy_configure_legacy; 338 | if (PyObject_HasAttrString(found, "help")) 339 | funcs->help = geanypy_proxy_help; 340 | if (GEANY_PLUGIN_REGISTER_FULL(subplugin, 224, pdata, NULL)) 341 | ret = pdata; 342 | } 343 | 344 | err: 345 | g_free(modulename); 346 | Py_DECREF(fromlist); 347 | return ret; 348 | } 349 | 350 | 351 | static void 352 | geanypy_unload(GeanyPlugin *plugin, GeanyPlugin *subplugin, gpointer load_data, gpointer pdata_) 353 | { 354 | GeanyPyPluginData *pdata = load_data; 355 | 356 | Py_XDECREF(pdata->instance); 357 | Py_DECREF(pdata->class); 358 | Py_DECREF(pdata->module); 359 | while (PyGC_Collect()); 360 | g_slice_free(GeanyPyPluginData, pdata); 361 | } 362 | 363 | 364 | static gboolean geanypy_init(GeanyPlugin *plugin_, gpointer pdata) 365 | { 366 | const gchar *exts[] = { "py", NULL }; 367 | GeanyPyData *state = pdata; 368 | PyObject *module; 369 | 370 | plugin_->proxy_funcs->probe = geanypy_probe; 371 | plugin_->proxy_funcs->load = geanypy_load; 372 | plugin_->proxy_funcs->unload = geanypy_unload; 373 | 374 | geany_data = plugin_->geany_data; 375 | 376 | GeanyPy_start_interpreter(); 377 | state->signal_manager = signal_manager_new(plugin_); 378 | 379 | module = PyImport_ImportModule("geany.plugin"); 380 | if (has_error() || !module) 381 | goto err; 382 | 383 | state->base = PyObject_GetAttrString(module, "Plugin"); 384 | Py_DECREF(module); 385 | if (has_error() || !state->base) 386 | goto err; 387 | 388 | if (!geany_plugin_register_proxy(plugin_, exts)) { 389 | Py_DECREF(state->base); 390 | goto err; 391 | } 392 | 393 | return TRUE; 394 | 395 | err: 396 | signal_manager_free(state->signal_manager); 397 | GeanyPy_stop_interpreter(); 398 | return FALSE; 399 | } 400 | 401 | 402 | static void geanypy_cleanup(GeanyPlugin *plugin, gpointer pdata) 403 | { 404 | GeanyPyData *state = pdata; 405 | signal_manager_free(state->signal_manager); 406 | Py_DECREF(state->base); 407 | GeanyPy_stop_interpreter(); 408 | } 409 | 410 | G_MODULE_EXPORT void 411 | geany_load_module(GeanyPlugin *plugin) 412 | { 413 | GeanyPyData *state = g_new0(GeanyPyData, 1); 414 | 415 | plugin->info->name = _("GeanyPy"); 416 | plugin->info->description = _("Python plugins support"); 417 | plugin->info->version = "1.0"; 418 | plugin->info->author = "Matthew Brush "; 419 | plugin->funcs->init = geanypy_init; 420 | plugin->funcs->cleanup = geanypy_cleanup; 421 | 422 | GEANY_PLUGIN_REGISTER_FULL(plugin, 226, state, g_free); 423 | } 424 | -------------------------------------------------------------------------------- /src/geanypy-plugin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * plugin.h 3 | * 4 | * Copyright 2011 Matthew Brush 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 | * 02110-1301 USA. 20 | * 21 | */ 22 | 23 | #ifndef PLUGIN_H 24 | #define PLUGIN_H 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | extern GeanyData *geany_data; 30 | 31 | 32 | #ifndef PyMODINIT_FUNC 33 | #define PyMODINIT_FUNC void 34 | #endif 35 | 36 | static inline GeanyPlugin *plugin_get(PyObject *self) 37 | { 38 | PyObject *caps = PyObject_GetAttrString(self, "__geany_plugin__"); 39 | return PyCapsule_GetPointer(caps, "GeanyPlugin"); 40 | } 41 | 42 | 43 | 44 | #ifdef __cplusplus 45 | } /* extern "C" */ 46 | #endif 47 | #endif /* PLUGIN_H */ 48 | -------------------------------------------------------------------------------- /src/geanypy-prefs.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | typedef struct 9 | { 10 | PyObject_HEAD 11 | GeanyPrefs *prefs; 12 | } Prefs; 13 | 14 | 15 | static void 16 | Prefs_dealloc(Prefs *self) 17 | { 18 | g_return_if_fail(self != NULL); 19 | self->ob_type->tp_free((PyObject *) self); 20 | } 21 | 22 | 23 | static int 24 | Prefs_init(Prefs *self) 25 | { 26 | g_return_val_if_fail(self != NULL, -1); 27 | self->prefs = geany_data->prefs; 28 | return 0; 29 | } 30 | 31 | 32 | static PyObject * 33 | Prefs_get_property(Prefs *self, const gchar *prop_name) 34 | { 35 | g_return_val_if_fail(self != NULL, NULL); 36 | g_return_val_if_fail(prop_name != NULL, NULL); 37 | 38 | if (!self->prefs) 39 | { 40 | PyErr_SetString(PyExc_RuntimeError, 41 | "Prefs instance not initialized properly"); 42 | return NULL; 43 | } 44 | 45 | if (g_str_equal(prop_name, "default_open_path") && self->prefs->default_open_path) 46 | return PyString_FromString(self->prefs->default_open_path); 47 | 48 | Py_RETURN_NONE; 49 | } 50 | GEANYPY_PROPS_READONLY(Prefs); 51 | 52 | 53 | static PyGetSetDef Prefs_getseters[] = { 54 | GEANYPY_GETSETDEF(Prefs, "default_open_path", 55 | "Default path to look for files when no other path is appropriate."), 56 | { NULL } 57 | }; 58 | 59 | 60 | static PyTypeObject PrefsType = { 61 | PyObject_HEAD_INIT(NULL) 62 | 0, /* ob_size */ 63 | "geany.prefs.Prefs", /* tp_name */ 64 | sizeof(Prefs), /* tp_basicsize */ 65 | 0, /* tp_itemsize */ 66 | (destructor) Prefs_dealloc, /* tp_dealloc */ 67 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 68 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 69 | "Wrapper around a GeanyPrefs structure.", /* tp_doc */ 70 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 71 | Prefs_getseters, /* tp_getset */ 72 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 73 | (initproc) Prefs_init, /* tp_init */ 74 | 0, 0, /* tp_alloc - tp_new */ 75 | }; 76 | 77 | 78 | typedef struct 79 | { 80 | PyObject_HEAD 81 | GeanyToolPrefs *tool_prefs; 82 | } ToolPrefs; 83 | 84 | 85 | static void 86 | ToolPrefs_dealloc(ToolPrefs *self) 87 | { 88 | g_return_if_fail(self != NULL); 89 | self->ob_type->tp_free((PyObject *) self); 90 | } 91 | 92 | 93 | static int 94 | ToolPrefs_init(ToolPrefs *self) 95 | { 96 | g_return_val_if_fail(self != NULL, -1); 97 | self->tool_prefs = geany_data->tool_prefs; 98 | return 0; 99 | } 100 | 101 | 102 | static PyObject * 103 | ToolPrefs_get_property(ToolPrefs *self, const gchar *prop_name) 104 | { 105 | g_return_val_if_fail(self != NULL, NULL); 106 | g_return_val_if_fail(prop_name != NULL, NULL); 107 | 108 | if (!self->tool_prefs) 109 | { 110 | PyErr_SetString(PyExc_RuntimeError, 111 | "ToolPrefs instance not initialized properly"); 112 | return NULL; 113 | } 114 | 115 | if (g_str_equal(prop_name, "browser_cmd") && self->tool_prefs->browser_cmd) 116 | return PyString_FromString(self->tool_prefs->browser_cmd); 117 | else if (g_str_equal(prop_name, "context_action_cmd") && self->tool_prefs->context_action_cmd) 118 | return PyString_FromString(self->tool_prefs->context_action_cmd); 119 | else if (g_str_equal(prop_name, "grep_cmd") && self->tool_prefs->grep_cmd) 120 | return PyString_FromString(self->tool_prefs->grep_cmd); 121 | else if (g_str_equal(prop_name, "term_cmd") && self->tool_prefs->term_cmd) 122 | return PyString_FromString(self->tool_prefs->term_cmd); 123 | 124 | Py_RETURN_NONE; 125 | } 126 | GEANYPY_PROPS_READONLY(ToolPrefs); 127 | 128 | 129 | static PyGetSetDef ToolPrefs_getseters[] = { 130 | GEANYPY_GETSETDEF(ToolPrefs, "browser_cmd", ""), 131 | GEANYPY_GETSETDEF(ToolPrefs, "context_action_cmd", ""), 132 | GEANYPY_GETSETDEF(ToolPrefs, "grep_cmd", ""), 133 | GEANYPY_GETSETDEF(ToolPrefs, "term_cmd", ""), 134 | { NULL } 135 | }; 136 | 137 | 138 | static PyTypeObject ToolPrefsType = { 139 | PyObject_HEAD_INIT(NULL) 140 | 0, /* ob_size */ 141 | "geany.prefs.ToolPrefs", /* tp_name */ 142 | sizeof(ToolPrefs), /* tp_basicsize */ 143 | 0, /* tp_itemsize */ 144 | (destructor) ToolPrefs_dealloc, /* tp_dealloc */ 145 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 146 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 147 | "Wrapper around a GeanyToolPrefs structure.", /* tp_doc */ 148 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 149 | ToolPrefs_getseters, /* tp_getset */ 150 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 151 | (initproc) ToolPrefs_init, /* tp_init */ 152 | 0, 0, /* tp_alloc - tp_new */ 153 | }; 154 | 155 | 156 | #ifdef ENABLE_PRIVATE 157 | static PyObject * 158 | Prefs_show_dialog(PyObject *module) 159 | { 160 | prefs_show_dialog(); 161 | Py_RETURN_NONE; 162 | } 163 | #endif 164 | 165 | 166 | static PyMethodDef PrefsModule_methods[] = { 167 | #ifdef ENABLE_PRIVATE 168 | { "show_dialog", (PyCFunction) Prefs_show_dialog, METH_NOARGS, 169 | "Show the preferences dialog." }, 170 | #endif 171 | { NULL } 172 | }; 173 | 174 | 175 | PyMODINIT_FUNC initprefs(void) 176 | { 177 | PyObject *m; 178 | 179 | PrefsType.tp_new = PyType_GenericNew; 180 | if (PyType_Ready(&PrefsType) < 0) 181 | return; 182 | 183 | ToolPrefsType.tp_new = PyType_GenericNew; 184 | if (PyType_Ready(&ToolPrefsType) < 0) 185 | return; 186 | 187 | m = Py_InitModule3("prefs", PrefsModule_methods, 188 | "General preferences dialog settings"); 189 | 190 | Py_INCREF(&PrefsType); 191 | PyModule_AddObject(m, "Prefs", (PyObject *) &PrefsType); 192 | 193 | Py_INCREF(&ToolPrefsType); 194 | PyModule_AddObject(m, "ToolPrefs", (PyObject *) &ToolPrefsType); 195 | } 196 | -------------------------------------------------------------------------------- /src/geanypy-project.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static void 9 | Project_dealloc(Project *self) 10 | { 11 | g_return_if_fail(self != NULL); 12 | self->ob_type->tp_free((PyObject *) self); 13 | } 14 | 15 | 16 | static int 17 | Project_init(Project *self, PyObject *args, PyObject *kwds) 18 | { 19 | g_return_val_if_fail(self != NULL, -1); 20 | self->project = geany_data->app->project; 21 | return 0; 22 | } 23 | 24 | 25 | static PyObject * 26 | Project_get_property(Project *self, const gchar *prop_name) 27 | { 28 | g_return_val_if_fail(self != NULL, NULL); 29 | g_return_val_if_fail(prop_name != NULL, NULL); 30 | 31 | if (!self->project) 32 | Py_RETURN_NONE; 33 | 34 | if (g_str_equal(prop_name, "base_path") && self->project->base_path) 35 | return PyString_FromString(self->project->base_path); 36 | else if (g_str_equal(prop_name, "description") && self->project->description) 37 | return PyString_FromString(self->project->description); 38 | else if (g_str_equal(prop_name, "file_name") && self->project->file_name) 39 | return PyString_FromString(self->project->file_name); 40 | else if (g_str_equal(prop_name, "file_patterns") && self->project->file_patterns) 41 | { 42 | guint i, len; 43 | PyObject *set; 44 | len = g_strv_length(self->project->file_patterns); 45 | set = PyFrozenSet_New(NULL); 46 | for (i = 0; i < len; i++) 47 | PySet_Add(set, PyString_FromString(self->project->file_patterns[i])); 48 | return set; 49 | } 50 | else if (g_str_equal(prop_name, "name") && self->project->name) 51 | return PyString_FromString(self->project->name); 52 | else if (g_str_equal(prop_name, "type") && self->project->type) 53 | return Py_BuildValue("i", self->project->type); 54 | 55 | Py_RETURN_NONE; 56 | } 57 | GEANYPY_PROPS_READONLY(Project); 58 | 59 | 60 | static PyGetSetDef Project_getseters[] = { 61 | GEANYPY_GETSETDEF(Project, "base_path", 62 | "Base path of the project directory (maybe relative)."), 63 | GEANYPY_GETSETDEF(Project, "description", 64 | "Short description of the project."), 65 | GEANYPY_GETSETDEF(Project, "file_name", 66 | "Where the project file is stored."), 67 | GEANYPY_GETSETDEF(Project, "file_patterns", 68 | "Sequence of filename extension patterns."), 69 | GEANYPY_GETSETDEF(Project, "name", 70 | "The name of the project."), 71 | GEANYPY_GETSETDEF(Project, "type", 72 | "Identifier whether it is a pure Geany project or modified/" 73 | "extended by a plugin."), 74 | { NULL } 75 | }; 76 | 77 | 78 | PyTypeObject ProjectType = { 79 | PyObject_HEAD_INIT(NULL) 80 | 0, /* ob_size */ 81 | "geany.project.Project", /* tp_name */ 82 | sizeof(Project), /* tp_basicsize */ 83 | 0, /* tp_itemsize */ 84 | (destructor) Project_dealloc, /* tp_dealloc */ 85 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 86 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 87 | "Wrapper around a GeanyProject structure.", /* tp_doc */ 88 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 89 | Project_getseters, /* tp_getset */ 90 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 91 | (initproc) Project_init, /* tp_init */ 92 | 0, 0, /* tp_alloc - tp_new */ 93 | }; 94 | 95 | 96 | static PyMethodDef ProjectModule_methods[] = { { NULL } }; 97 | 98 | 99 | PyMODINIT_FUNC initproject(void) 100 | { 101 | PyObject *m; 102 | 103 | ProjectType.tp_new = PyType_GenericNew; 104 | if (PyType_Ready(&ProjectType) < 0) 105 | return; 106 | 107 | m = Py_InitModule3("project", ProjectModule_methods, "Project information"); 108 | 109 | Py_INCREF(&ProjectType); 110 | PyModule_AddObject(m, "Project", (PyObject *)&ProjectType); 111 | } 112 | -------------------------------------------------------------------------------- /src/geanypy-project.h: -------------------------------------------------------------------------------- 1 | #ifndef GEANYPY_PROJECT_H__ 2 | #define GEANYPY_PROJECT_H__ 3 | 4 | extern PyTypeObject ProjectType; 5 | 6 | typedef struct 7 | { 8 | PyObject_HEAD 9 | GeanyProject *project; 10 | } Project; 11 | 12 | PyMODINIT_FUNC initproject(void); 13 | 14 | #endif /* GEANYPY_PROJECT_H__ */ 15 | -------------------------------------------------------------------------------- /src/geanypy-scinotification.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static void 9 | Notification_dealloc(Notification *self) 10 | { 11 | Py_XDECREF(self->hdr); 12 | self->ob_type->tp_free((PyObject *) self); 13 | } 14 | 15 | 16 | static int 17 | Notification_init(Notification *self, PyObject *args, PyObject *kwds) 18 | { 19 | self->notif = NULL; 20 | self->hdr = NULL; 21 | return 0; 22 | } 23 | 24 | 25 | static PyObject * 26 | Notification_get_property(Notification *self, const gchar *prop_name) 27 | { 28 | g_return_val_if_fail(self != NULL, NULL); 29 | g_return_val_if_fail(prop_name != NULL, NULL); 30 | 31 | if (!self->notif) 32 | { 33 | PyErr_SetString(PyExc_RuntimeError, 34 | "Notification instance not initialized properly"); 35 | return NULL; 36 | } 37 | 38 | if (g_str_equal(prop_name, "nmhdr")) 39 | { 40 | Py_INCREF(self->hdr); 41 | return (PyObject *) self->hdr; 42 | } 43 | else if (g_str_equal(prop_name, "position")) 44 | return PyInt_FromLong((glong) self->notif->position); 45 | else if (g_str_equal(prop_name, "ch")) 46 | return Py_BuildValue("c", self->notif->ch); 47 | else if (g_str_equal(prop_name, "modifiers")) 48 | return PyInt_FromLong((glong) self->notif->modifiers); 49 | else if (g_str_equal(prop_name, "modification_type")) 50 | return PyInt_FromLong((glong) self->notif->modificationType); 51 | else if (g_str_equal(prop_name, "text")) 52 | return PyString_FromString(self->notif->text); 53 | else if (g_str_equal(prop_name, "length")) 54 | return PyInt_FromLong((glong) self->notif->length); 55 | else if (g_str_equal(prop_name, "lines_added")) 56 | return PyInt_FromLong((glong) self->notif->linesAdded); 57 | else if (g_str_equal(prop_name, "message")) 58 | return PyInt_FromLong((glong) self->notif->message); 59 | else if (g_str_equal(prop_name, "w_param")) 60 | return PyLong_FromLong(self->notif->wParam); 61 | else if (g_str_equal(prop_name, "l_param")) 62 | return PyLong_FromLong(self->notif->lParam); 63 | else if (g_str_equal(prop_name, "line")) 64 | return PyInt_FromLong((glong) self->notif->line); 65 | else if (g_str_equal(prop_name, "fold_level_now")) 66 | return PyInt_FromLong((glong) self->notif->foldLevelNow); 67 | else if (g_str_equal(prop_name, "fold_level_prev")) 68 | return PyInt_FromLong((glong) self->notif->foldLevelPrev); 69 | else if (g_str_equal(prop_name, "margin")) 70 | return PyInt_FromLong((glong) self->notif->margin); 71 | else if (g_str_equal(prop_name, "list_type")) 72 | return PyInt_FromLong((glong) self->notif->listType); 73 | else if (g_str_equal(prop_name, "x")) 74 | return PyInt_FromLong((glong) self->notif->x); 75 | else if (g_str_equal(prop_name, "y")) 76 | return PyInt_FromLong((glong) self->notif->y); 77 | else if (g_str_equal(prop_name, "token")) 78 | return PyInt_FromLong((glong) self->notif->token); 79 | else if (g_str_equal(prop_name, "annotation_lines_added")) 80 | return PyInt_FromLong((glong) self->notif->annotationLinesAdded); 81 | else if (g_str_equal(prop_name, "updated")) 82 | return PyInt_FromLong((glong) self->notif->updated); 83 | 84 | Py_RETURN_NONE; 85 | } 86 | GEANYPY_PROPS_READONLY(Notification); 87 | 88 | 89 | static PyGetSetDef Notification_getseters[] = { 90 | GEANYPY_GETSETDEF(Notification, "nmhdr", ""), 91 | GEANYPY_GETSETDEF(Notification, "position", ""), 92 | GEANYPY_GETSETDEF(Notification, "ch", ""), 93 | GEANYPY_GETSETDEF(Notification, "modifiers", ""), 94 | GEANYPY_GETSETDEF(Notification, "modification_type", ""), 95 | GEANYPY_GETSETDEF(Notification, "text", ""), 96 | GEANYPY_GETSETDEF(Notification, "length", ""), 97 | GEANYPY_GETSETDEF(Notification, "lines_added", ""), 98 | GEANYPY_GETSETDEF(Notification, "message", ""), 99 | GEANYPY_GETSETDEF(Notification, "w_param", ""), 100 | GEANYPY_GETSETDEF(Notification, "l_param", ""), 101 | GEANYPY_GETSETDEF(Notification, "line", ""), 102 | GEANYPY_GETSETDEF(Notification, "fold_level_now", ""), 103 | GEANYPY_GETSETDEF(Notification, "fold_level_prev", ""), 104 | GEANYPY_GETSETDEF(Notification, "margin", ""), 105 | GEANYPY_GETSETDEF(Notification, "list_type", ""), 106 | GEANYPY_GETSETDEF(Notification, "x", ""), 107 | GEANYPY_GETSETDEF(Notification, "y", ""), 108 | GEANYPY_GETSETDEF(Notification, "token", ""), 109 | GEANYPY_GETSETDEF(Notification, "annotation_lines_added", ""), 110 | GEANYPY_GETSETDEF(Notification, "updated", ""), 111 | { NULL } 112 | }; 113 | 114 | 115 | PyTypeObject NotificationType = { 116 | PyObject_HEAD_INIT(NULL) 117 | 0, /* ob_size */ 118 | "geany.scintilla.Notification", /* tp_name */ 119 | sizeof(Notification), /* tp_basicsize */ 120 | 0, /* tp_itemsize */ 121 | (destructor) Notification_dealloc, /* tp_dealloc */ 122 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 123 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 124 | "Wrapper around a SCNotification structure.", /* tp_doc */ 125 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 126 | Notification_getseters, /* tp_getset */ 127 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 128 | (initproc) Notification_init, /* tp_init */ 129 | 0, 0, /* tp_alloc - tp_new */ 130 | 131 | }; 132 | 133 | 134 | Notification *Notification_create_new_from_scintilla_notification(SCNotification *notif) 135 | { 136 | Notification *self; 137 | self = (Notification *) PyObject_CallObject((PyObject *) &NotificationType, NULL); 138 | self->notif = notif; 139 | self->hdr = NotifyHeader_create_new_from_scintilla_notification(self->notif); 140 | return self; 141 | } 142 | -------------------------------------------------------------------------------- /src/geanypy-scinotifyheader.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | static void 9 | NotifyHeader_dealloc(NotifyHeader *self) 10 | { 11 | self->ob_type->tp_free((PyObject *) self); 12 | } 13 | 14 | 15 | static int 16 | NotifyHeader_init(NotifyHeader *self, PyObject *args, PyObject *kwds) 17 | { 18 | self->notif = NULL; 19 | return 0; 20 | } 21 | 22 | 23 | static PyObject * 24 | NotifyHeader_get_property(NotifyHeader *self, const gchar *prop_name) 25 | { 26 | g_return_val_if_fail(self != NULL, NULL); 27 | g_return_val_if_fail(prop_name != NULL, NULL); 28 | 29 | if (!self->notif) 30 | { 31 | PyErr_SetString(PyExc_RuntimeError, 32 | "NotifyHeader instance not initialized properly"); 33 | return NULL; 34 | } 35 | 36 | if (g_str_equal(prop_name, "hwnd_from")) 37 | return PyLong_FromVoidPtr(self->notif->nmhdr.hwndFrom); 38 | else if (g_str_equal(prop_name, "id_from")) 39 | return PyLong_FromLong(self->notif->nmhdr.idFrom); 40 | else if (g_str_equal(prop_name, "code")) 41 | return PyInt_FromLong((glong) self->notif->nmhdr.code); 42 | 43 | Py_RETURN_NONE; 44 | } 45 | GEANYPY_PROPS_READONLY(NotifyHeader); 46 | 47 | 48 | static PyGetSetDef NotifyHeader_getseters[] = { 49 | GEANYPY_GETSETDEF(NotifyHeader, "hwnd_from", ""), 50 | GEANYPY_GETSETDEF(NotifyHeader, "id_from", ""), 51 | GEANYPY_GETSETDEF(NotifyHeader, "code", ""), 52 | { NULL } 53 | }; 54 | 55 | 56 | PyTypeObject NotifyHeaderType = { 57 | PyObject_HEAD_INIT(NULL) 58 | 0, /* ob_size */ 59 | "geany.scintilla.NotifyHeader", /* tp_name */ 60 | sizeof(NotifyHeader), /* tp_basicsize */ 61 | 0, /* tp_itemsize */ 62 | (destructor) NotifyHeader_dealloc, /* tp_dealloc */ 63 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 64 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 65 | "Wrapper around a NotifyHeader structure.", /* tp_doc */ 66 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 67 | NotifyHeader_getseters, /* tp_getset */ 68 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 69 | (initproc) NotifyHeader_init, /* tp_init */ 70 | 0, 0, /* tp_alloc - tp_new */ 71 | 72 | }; 73 | 74 | 75 | NotifyHeader *NotifyHeader_create_new_from_scintilla_notification(SCNotification *notif) 76 | { 77 | NotifyHeader *self; 78 | self = (NotifyHeader *) PyObject_CallObject((PyObject *) &NotifyHeaderType, NULL); 79 | self->notif = notif; 80 | return self; 81 | } 82 | -------------------------------------------------------------------------------- /src/geanypy-scintilla.h: -------------------------------------------------------------------------------- 1 | #ifndef GEANYPY_SCINTILLA_H__ 2 | #define GEANYPY_SCINTILLA_H__ 3 | 4 | extern PyTypeObject NotificationType; 5 | extern PyTypeObject NotifyHeaderType; 6 | 7 | typedef struct 8 | { 9 | PyObject_HEAD 10 | ScintillaObject *sci; 11 | } Scintilla; 12 | 13 | typedef struct 14 | { 15 | PyObject_HEAD 16 | SCNotification *notif; 17 | } NotifyHeader; 18 | 19 | typedef struct 20 | { 21 | PyObject_HEAD 22 | SCNotification *notif; 23 | NotifyHeader *hdr; 24 | } Notification; 25 | 26 | 27 | PyMODINIT_FUNC init_geany_scintilla(void); 28 | Scintilla *Scintilla_create_new_from_scintilla(ScintillaObject *sci); 29 | Notification *Notification_create_new_from_scintilla_notification(SCNotification *notif); 30 | NotifyHeader *NotifyHeader_create_new_from_scintilla_notification(SCNotification *notif); 31 | 32 | #endif /* GEANYPY_SCINTILLA_H__ */ 33 | -------------------------------------------------------------------------------- /src/geanypy-search.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | 8 | typedef struct 9 | { 10 | PyObject_HEAD 11 | GeanySearchPrefs *search_prefs; 12 | } SearchPrefs; 13 | 14 | 15 | static void 16 | SearchPrefs_dealloc(SearchPrefs *self) 17 | { 18 | g_return_if_fail(self != NULL); 19 | self->ob_type->tp_free((PyObject *) self); 20 | } 21 | 22 | 23 | static int 24 | SearchPrefs_init(SearchPrefs *self) 25 | { 26 | g_return_val_if_fail(self != NULL, -1); 27 | self->search_prefs = geany_data->search_prefs; 28 | return 0; 29 | } 30 | 31 | 32 | static PyObject * 33 | SearchPrefs_get_property(SearchPrefs *self, const gchar *prop_name) 34 | { 35 | g_return_val_if_fail(self != NULL, NULL); 36 | g_return_val_if_fail(prop_name != NULL, NULL); 37 | 38 | if (!self->search_prefs) 39 | { 40 | PyErr_SetString(PyExc_RuntimeError, 41 | "SearchPrefs instance not initialized properly"); 42 | return NULL; 43 | } 44 | 45 | if (g_str_equal(prop_name, "use_current_word")) 46 | { 47 | if (self->search_prefs->use_current_word) 48 | Py_RETURN_TRUE; 49 | else 50 | Py_RETURN_FALSE; 51 | } 52 | 53 | Py_RETURN_NONE; 54 | } 55 | GEANYPY_PROPS_READONLY(SearchPrefs); 56 | 57 | 58 | static PyGetSetDef SearchPrefs_getseters[] = { 59 | GEANYPY_GETSETDEF(SearchPrefs, "use_current_word", 60 | "Use current word for default search text."), 61 | { NULL } 62 | }; 63 | 64 | 65 | static PyTypeObject SearchPrefsType = { 66 | PyObject_HEAD_INIT(NULL) 67 | 0, /* ob_size */ 68 | "geany.search.SearchPrefs", /* tp_name */ 69 | sizeof(SearchPrefs), /* tp_basicsize */ 70 | 0, /* tp_itemsize */ 71 | (destructor) SearchPrefs_dealloc, /* tp_dealloc */ 72 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 73 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 74 | "Wrapper around a GeanySearchPrefs structure.", /* tp_doc */ 75 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 76 | SearchPrefs_getseters, /* tp_getset */ 77 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 78 | (initproc) SearchPrefs_init, /* tp_init */ 79 | 0, 0, /* tp_alloc - tp_new */ 80 | }; 81 | 82 | 83 | static PyObject * 84 | Search_show_find_in_files_dialog(PyObject *self, PyObject *args, PyObject *kwargs) 85 | { 86 | gchar *dir = NULL; 87 | static gchar *kwlist[] = { "init_dir", NULL }; 88 | 89 | PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &dir); 90 | search_show_find_in_files_dialog(dir); 91 | 92 | Py_RETURN_NONE; 93 | } 94 | 95 | 96 | static PyMethodDef SearchPrefsModule_methods[] = { 97 | { "show_find_in_files_dialog", 98 | (PyCFunction) Search_show_find_in_files_dialog, 99 | METH_KEYWORDS, 100 | "Shows the Find in Files dialog, taking an optional directory " 101 | "to search in for the dialog or if not specified then uses " 102 | "the current document's directory." }, 103 | { NULL } 104 | }; 105 | 106 | 107 | PyMODINIT_FUNC initsearch(void) 108 | { 109 | PyObject *m; 110 | 111 | SearchPrefsType.tp_new = PyType_GenericNew; 112 | if (PyType_Ready(&SearchPrefsType) < 0) 113 | return; 114 | 115 | m = Py_InitModule3("search", SearchPrefsModule_methods, 116 | "Search preferences and information."); 117 | 118 | Py_INCREF(&SearchPrefsType); 119 | PyModule_AddObject(m, "SearchPrefs", (PyObject *) &SearchPrefsType); 120 | } 121 | -------------------------------------------------------------------------------- /src/geanypy-signalmanager.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | struct _SignalManager 8 | { 9 | GeanyPlugin *geany_plugin; 10 | PyObject *py_obj; 11 | GObject *obj; 12 | }; 13 | 14 | 15 | static void signal_manager_connect_signals(SignalManager *man); 16 | 17 | static void on_build_start(GObject *geany_object, SignalManager *man); 18 | static void on_document_activate(GObject *geany_object, GeanyDocument *doc, SignalManager *man); 19 | static void on_document_before_save(GObject *geany_object, GeanyDocument *doc, SignalManager *man); 20 | static void on_document_close(GObject *geany_object, GeanyDocument *doc, SignalManager *man); 21 | static void on_document_filetype_set(GObject *geany_object, GeanyDocument *doc, GeanyFiletype *filetype_old, SignalManager *man); 22 | static void on_document_new(GObject *geany_object, GeanyDocument *doc, SignalManager *man); 23 | static void on_document_open(GObject *geany_object, GeanyDocument *doc, SignalManager *man); 24 | static void on_document_reload(GObject *geany_object, GeanyDocument *doc, SignalManager *man); 25 | static void on_document_save(GObject *geany_object, GeanyDocument *doc, SignalManager *man); 26 | static gboolean on_editor_notify(GObject *geany_object, GeanyEditor *editor, SCNotification *nt, SignalManager *man); 27 | static void on_geany_startup_complete(GObject *geany_object, SignalManager *man); 28 | static void on_project_close(GObject *geany_object, SignalManager *man); 29 | static void on_project_dialog_confirmed(GObject *geany_object, GtkWidget *notebook, SignalManager *man); 30 | static void on_project_dialog_open(GObject *geany_object, GtkWidget *notebook, SignalManager *man); 31 | static void on_project_dialog_close(GObject *geany_object, GtkWidget *notebook, SignalManager *man); 32 | static void on_project_open(GObject *geany_object, GKeyFile *config, SignalManager *man); 33 | static void on_project_save(GObject *geany_object, GKeyFile *config, SignalManager *man); 34 | static void on_update_editor_menu(GObject *geany_object, const gchar *word, gint pos, GeanyDocument *doc, SignalManager *man); 35 | 36 | 37 | SignalManager *signal_manager_new(GeanyPlugin *geany_plugin) 38 | { 39 | SignalManager *man; 40 | PyObject *module; 41 | 42 | man = g_new0(SignalManager, 1); 43 | 44 | man->geany_plugin = geany_plugin; 45 | man->py_obj = NULL; 46 | man->obj = NULL; 47 | 48 | module = PyImport_ImportModule("geany"); 49 | if (!module) 50 | { 51 | if (PyErr_Occurred()) 52 | PyErr_Print(); 53 | g_warning("Unable to import 'geany' module"); 54 | g_free(man); 55 | return NULL; 56 | } 57 | 58 | man->py_obj = PyObject_GetAttrString(module, "signals"); 59 | Py_DECREF(module); 60 | if (!man->py_obj) 61 | { 62 | if (PyErr_Occurred()) 63 | PyErr_Print(); 64 | g_warning("Unable to get 'SignalManager' instance from 'geany' module."); 65 | g_free(man); 66 | return NULL; 67 | } 68 | man->obj = pygobject_get(man->py_obj); 69 | 70 | signal_manager_connect_signals(man); 71 | 72 | return man; 73 | } 74 | 75 | 76 | void signal_manager_free(SignalManager *man) 77 | { 78 | g_return_if_fail(man != NULL); 79 | Py_XDECREF(man->py_obj); 80 | g_free(man); 81 | } 82 | 83 | GObject *signal_manager_get_gobject(SignalManager *signal_manager) 84 | { 85 | return G_OBJECT(signal_manager->obj); 86 | } 87 | 88 | 89 | static void signal_manager_connect_signals(SignalManager *man) 90 | { 91 | GeanyPlugin *geany_plugin = man->geany_plugin; 92 | plugin_signal_connect(geany_plugin, NULL, "build-start", TRUE, G_CALLBACK(on_build_start), man); 93 | plugin_signal_connect(geany_plugin, NULL, "document-activate", TRUE, G_CALLBACK(on_document_activate), man); 94 | plugin_signal_connect(geany_plugin, NULL, "document-before-save", TRUE, G_CALLBACK(on_document_before_save), man); 95 | plugin_signal_connect(geany_plugin, NULL, "document-close", TRUE, G_CALLBACK(on_document_close), man); 96 | plugin_signal_connect(geany_plugin, NULL, "document-filetype-set", TRUE, G_CALLBACK(on_document_filetype_set), man); 97 | plugin_signal_connect(geany_plugin, NULL, "document-new", TRUE, G_CALLBACK(on_document_new), man); 98 | plugin_signal_connect(geany_plugin, NULL, "document-open", TRUE, G_CALLBACK(on_document_open), man); 99 | plugin_signal_connect(geany_plugin, NULL, "document-reload", TRUE, G_CALLBACK(on_document_reload), man); 100 | plugin_signal_connect(geany_plugin, NULL, "document-save", TRUE, G_CALLBACK(on_document_save), man); 101 | plugin_signal_connect(geany_plugin, NULL, "editor-notify", TRUE, G_CALLBACK(on_editor_notify), man); 102 | plugin_signal_connect(geany_plugin, NULL, "geany-startup-complete", TRUE, G_CALLBACK(on_geany_startup_complete), man); 103 | plugin_signal_connect(geany_plugin, NULL, "project-close", TRUE, G_CALLBACK(on_project_close), man); 104 | plugin_signal_connect(geany_plugin, NULL, "project-dialog-confirmed", TRUE, G_CALLBACK(on_project_dialog_confirmed), man); 105 | plugin_signal_connect(geany_plugin, NULL, "project-dialog-open", TRUE, G_CALLBACK(on_project_dialog_open), man); 106 | plugin_signal_connect(geany_plugin, NULL, "project-dialog-close", TRUE, G_CALLBACK(on_project_dialog_close), man); 107 | plugin_signal_connect(geany_plugin, NULL, "project-open", TRUE, G_CALLBACK(on_project_open), man); 108 | plugin_signal_connect(geany_plugin, NULL, "project-save", TRUE, G_CALLBACK(on_project_save), man); 109 | plugin_signal_connect(geany_plugin, NULL, "update-editor-menu", TRUE, G_CALLBACK(on_update_editor_menu), man); 110 | } 111 | 112 | static void on_build_start(GObject *geany_object, SignalManager *man) 113 | { 114 | g_signal_emit_by_name(man->obj, "build-start"); 115 | } 116 | 117 | 118 | static void on_document_event(GObject *geany_object, GeanyDocument *doc, SignalManager *man, const gchar *signal_name) 119 | { 120 | PyObject *py_doc = (PyObject *) Document_create_new_from_geany_document(doc); 121 | g_signal_emit_by_name(man->obj, signal_name, py_doc); 122 | Py_XDECREF(py_doc); 123 | } 124 | 125 | 126 | static void on_document_activate(GObject *geany_object, GeanyDocument *doc, SignalManager *man) 127 | { 128 | on_document_event(geany_object, doc, man, "document-activate"); 129 | } 130 | 131 | 132 | static void on_document_before_save(GObject *geany_object, GeanyDocument *doc, SignalManager *man) 133 | { 134 | on_document_event(geany_object, doc, man, "document-before-save"); 135 | } 136 | 137 | 138 | static void on_document_close(GObject *geany_object, GeanyDocument *doc, SignalManager *man) 139 | { 140 | on_document_event(geany_object, doc, man, "document-close"); 141 | } 142 | 143 | 144 | static void on_document_filetype_set(GObject *geany_object, GeanyDocument *doc, GeanyFiletype *filetype_old, SignalManager *man) 145 | { 146 | PyObject *py_doc, *py_ft; 147 | py_doc = (PyObject *) Document_create_new_from_geany_document(doc); 148 | py_ft = (PyObject *) Filetype_create_new_from_geany_filetype(filetype_old); 149 | g_signal_emit_by_name(man->obj, "document-filetype-set", py_doc, py_ft); 150 | Py_XDECREF(py_doc); 151 | Py_XDECREF(py_ft); 152 | } 153 | 154 | 155 | static void on_document_new(GObject *geany_object, GeanyDocument *doc, SignalManager *man) 156 | { 157 | on_document_event(geany_object, doc, man, "document-new"); 158 | } 159 | 160 | 161 | static void on_document_open(GObject *geany_object, GeanyDocument *doc, SignalManager *man) 162 | { 163 | on_document_event(geany_object, doc, man, "document-open"); 164 | } 165 | 166 | 167 | static void on_document_reload(GObject *geany_object, GeanyDocument *doc, SignalManager *man) 168 | { 169 | on_document_event(geany_object, doc, man, "document-reload"); 170 | } 171 | 172 | 173 | static void on_document_save(GObject *geany_object, GeanyDocument *doc, SignalManager *man) 174 | { 175 | on_document_event(geany_object, doc, man, "document-save"); 176 | } 177 | 178 | 179 | static gboolean on_editor_notify(GObject *geany_object, GeanyEditor *editor, SCNotification *nt, SignalManager *man) 180 | { 181 | gboolean res = FALSE; 182 | PyObject *py_ed, *py_notif; 183 | py_ed = (PyObject *) Editor_create_new_from_geany_editor(editor); 184 | py_notif = (PyObject *) Notification_create_new_from_scintilla_notification(nt); 185 | g_signal_emit_by_name(man->obj, "editor-notify", py_ed, py_notif, &res); 186 | Py_XDECREF(py_ed); 187 | Py_XDECREF(py_notif); 188 | return res; 189 | } 190 | 191 | 192 | static void on_geany_startup_complete(GObject *geany_object, SignalManager *man) 193 | { 194 | g_signal_emit_by_name(man->obj, "geany-startup-complete"); 195 | } 196 | 197 | 198 | static void on_project_close(GObject *geany_object, SignalManager *man) 199 | { 200 | g_signal_emit_by_name(man->obj, "project-close"); 201 | } 202 | 203 | 204 | static void on_project_dialog_confirmed(GObject *geany_object, GtkWidget *notebook, SignalManager *man) 205 | { 206 | PyObject *gob = (PyObject *) pygobject_new(G_OBJECT(notebook)); 207 | g_signal_emit_by_name(man->obj, "project-dialog-confirmed", gob); 208 | Py_XDECREF(gob); 209 | } 210 | 211 | 212 | static void on_project_dialog_open(GObject *geany_object, GtkWidget *notebook, SignalManager *man) 213 | { 214 | PyObject *gob = (PyObject *) pygobject_new(G_OBJECT(notebook)); 215 | g_signal_emit_by_name(man->obj, "project-dialog-open", gob); 216 | Py_XDECREF(gob); 217 | } 218 | 219 | static void on_project_dialog_close(GObject *geany_object, GtkWidget *notebook, SignalManager *man) 220 | { 221 | PyObject *gob = (PyObject *) pygobject_new(G_OBJECT(notebook)); 222 | g_signal_emit_by_name(man->obj, "project-dialog-close", gob); 223 | Py_XDECREF(gob); 224 | } 225 | 226 | 227 | static void on_project_open(GObject *geany_object, GKeyFile *config, SignalManager *man) 228 | { 229 | PyObject *py_proj = (PyObject *) GEANYPY_NEW(Project); 230 | g_signal_emit_by_name(man->obj, "project-open", py_proj); 231 | Py_XDECREF(py_proj); 232 | } 233 | 234 | 235 | static void on_project_save(GObject *geany_object, GKeyFile *config, SignalManager *man) 236 | { 237 | PyObject *py_proj = (PyObject *) GEANYPY_NEW(Project); 238 | g_signal_emit_by_name(man->obj, "project-save", py_proj); 239 | Py_XDECREF(py_proj); 240 | } 241 | 242 | 243 | static void on_update_editor_menu(GObject *geany_object, const gchar *word, gint pos, GeanyDocument *doc, SignalManager *man) 244 | { 245 | PyObject *py_doc = (PyObject *) Document_create_new_from_geany_document(doc); 246 | g_signal_emit_by_name(man->obj, "update-editor-menu", word, pos, py_doc); 247 | Py_XDECREF(py_doc); 248 | } 249 | -------------------------------------------------------------------------------- /src/geanypy-signalmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNALMANAGER_H 2 | #define SIGNALMANAGER_H 3 | 4 | typedef struct _SignalManager SignalManager; 5 | 6 | SignalManager *signal_manager_new(GeanyPlugin *geany_plugin); 7 | void signal_manager_free(SignalManager *signal_manager); 8 | GObject *signal_manager_get_gobject(SignalManager *signal_manager); 9 | 10 | #endif /* SIGNALMANAGER_H */ 11 | -------------------------------------------------------------------------------- /src/geanypy-templates.c: -------------------------------------------------------------------------------- 1 | #if defined(HAVE_CONFIG_H) && !defined(GEANYPY_WINDOWS) 2 | # include "config.h" 3 | #endif 4 | 5 | #include "geanypy.h" 6 | 7 | /* TODO: see if the TemplatePrefs members are safe to modify. */ 8 | 9 | typedef struct 10 | { 11 | PyObject_HEAD 12 | GeanyTemplatePrefs *template_prefs; 13 | } TemplatePrefs; 14 | 15 | 16 | static void 17 | TemplatePrefs_dealloc(TemplatePrefs *self) 18 | { 19 | g_return_if_fail(self != NULL); 20 | self->ob_type->tp_free((PyObject *) self); 21 | } 22 | 23 | 24 | static int 25 | TemplatePrefs_init(TemplatePrefs *self) 26 | { 27 | g_return_val_if_fail(self != NULL, -1); 28 | self->template_prefs = geany_data->template_prefs; 29 | return 0; 30 | } 31 | 32 | 33 | static PyObject * 34 | TemplatePrefs_get_property(TemplatePrefs *self, const gchar *prop_name) 35 | { 36 | g_return_val_if_fail(self != NULL, NULL); 37 | g_return_val_if_fail(prop_name != NULL, NULL); 38 | 39 | if (!self->template_prefs) 40 | { 41 | PyErr_SetString(PyExc_RuntimeError, 42 | "TemplatePrefs instance not initialized properly"); 43 | return NULL; 44 | } 45 | 46 | if (g_str_equal(prop_name, "company")) 47 | return PyString_FromString(self->template_prefs->company); 48 | else if (g_str_equal(prop_name, "developer")) 49 | return PyString_FromString(self->template_prefs->developer); 50 | else if (g_str_equal(prop_name, "initials")) 51 | return PyString_FromString(self->template_prefs->initials); 52 | else if (g_str_equal(prop_name, "mail")) 53 | return PyString_FromString(self->template_prefs->mail); 54 | else if (g_str_equal(prop_name, "version")) 55 | return PyString_FromString(self->template_prefs->version); 56 | 57 | Py_RETURN_NONE; 58 | } 59 | GEANYPY_PROPS_READONLY(TemplatePrefs); 60 | 61 | 62 | static PyGetSetDef TemplatePrefs_getseters[] = { 63 | GEANYPY_GETSETDEF(TemplatePrefs, "company", ""), 64 | GEANYPY_GETSETDEF(TemplatePrefs, "developer", ""), 65 | GEANYPY_GETSETDEF(TemplatePrefs, "initials", ""), 66 | GEANYPY_GETSETDEF(TemplatePrefs, "mail", "Email address"), 67 | GEANYPY_GETSETDEF(TemplatePrefs, "version", "Initial version"), 68 | { NULL } 69 | }; 70 | 71 | 72 | static PyTypeObject TemplatePrefsType = { 73 | PyObject_HEAD_INIT(NULL) 74 | 0, /* ob_size */ 75 | "geany.templates.TemplatePrefs", /* tp_name */ 76 | sizeof(TemplatePrefs), /* tp_basicsize */ 77 | 0, /* tp_itemsize */ 78 | (destructor) TemplatePrefs_dealloc, /* tp_dealloc */ 79 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */ 80 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 81 | "Wrapper around a GeanyTemplatePrefs structure.", /* tp_doc */ 82 | 0, 0, 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_members */ 83 | TemplatePrefs_getseters, /* tp_getset */ 84 | 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */ 85 | (initproc) TemplatePrefs_init, /* tp_init */ 86 | 0, 0, /* tp_alloc - tp_new */ 87 | }; 88 | 89 | 90 | static PyMethodDef TemplatePrefsModule_methods[] = { { NULL } }; 91 | 92 | 93 | PyMODINIT_FUNC inittemplates(void) 94 | { 95 | PyObject *m; 96 | 97 | TemplatePrefsType.tp_new = PyType_GenericNew; 98 | if (PyType_Ready(&TemplatePrefsType) < 0) 99 | return; 100 | 101 | m = Py_InitModule3("templates", TemplatePrefsModule_methods, 102 | "Template information and management."); 103 | 104 | Py_INCREF(&TemplatePrefsType); 105 | PyModule_AddObject(m, "TemplatePrefs", (PyObject *) &TemplatePrefsType); 106 | } 107 | -------------------------------------------------------------------------------- /src/geanypy-uiutils.h: -------------------------------------------------------------------------------- 1 | #ifndef GEANYPY_UI_UTILS_H__ 2 | #define GEANYPY_UI_UTILS_H__ 3 | 4 | extern PyTypeObject InterfacePrefsType; 5 | extern PyTypeObject MainWidgetsType; 6 | 7 | typedef struct 8 | { 9 | PyObject_HEAD 10 | GeanyInterfacePrefs *iface_prefs; 11 | } InterfacePrefs; 12 | 13 | typedef struct 14 | { 15 | PyObject_HEAD 16 | GeanyMainWidgets *main_widgets; 17 | } MainWidgets; 18 | 19 | #endif /* GEANYPY_UI_UTILS_H__ */ 20 | -------------------------------------------------------------------------------- /src/geanypy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * geanypy.h 3 | * 4 | * Copyright 2011 Matthew Brush 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 | * 02110-1301 USA. 20 | * 21 | */ 22 | 23 | #ifndef GEANYPY_H__ 24 | #define GEANYPY_H__ 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | /* For plain make file build using Mingw on Windows */ 31 | #if defined(__MINGW32__) && defined(GEANYPY_WINDOWS_BUILD) 32 | # define GEANYPY_WINDOWS 1 33 | #endif 34 | 35 | #include 36 | #ifndef PyMODINIT_FUNC 37 | # define PyMODINIT_FUNC void 38 | #endif 39 | #include 40 | 41 | 42 | /* Defines a setter that throws an attribute exception when called. */ 43 | #define GEANYPY_PROPS_READONLY(cls) \ 44 | static int \ 45 | cls ## _set_property(cls *self, PyObject *value, const gchar *prop_name) { \ 46 | PyErr_SetString(PyExc_AttributeError, "can't set attribute"); \ 47 | return -1; } 48 | 49 | /* Defines a getter/setter for use in the tp_getset table. */ 50 | #define GEANYPY_GETSETDEF(cls, prop_name, doc) \ 51 | { prop_name, \ 52 | (getter) cls ## _get_property, \ 53 | (setter) cls ## _set_property, \ 54 | doc, \ 55 | prop_name } 56 | 57 | 58 | /* Initializes a new `cls` type object. */ 59 | #define GEANYPY_NEW(cls) \ 60 | (cls *) PyObject_CallObject((PyObject *) &(cls ## Type), NULL); 61 | 62 | 63 | /* Returns a new py string or py none if string is NULL. */ 64 | #define GEANYPY_RETURN_STRING(memb) \ 65 | { \ 66 | if (memb != NULL) \ 67 | return PyString_FromString(memb); \ 68 | else \ 69 | Py_RETURN_NONE; \ 70 | } 71 | 72 | 73 | #include 74 | #include 75 | #include 76 | 77 | #include 78 | 79 | /* necessary for compilation with -fno-common, 80 | * see https://bugzilla.gnome.org/show_bug.cgi?id=610657 for details, 81 | * INCLUDE_PYGOBJECT_ONCE_FULL is set only once in geanypy-plugin.c */ 82 | #ifndef INCLUDE_PYGOBJECT_ONCE_FULL 83 | # define NO_IMPORT_PYGOBJECT 84 | # define NO_IMPORT_PYGTK 85 | #endif 86 | 87 | #include 88 | 89 | #ifndef GEANYPY_WINDOWS 90 | /* Used with the results of `pkg-config --cflags pygtk-2.0` */ 91 | # include 92 | #else 93 | /* On windows the path of pygtk.h is directly an include dir */ 94 | # include 95 | #endif 96 | 97 | #ifndef GTK 98 | # define GTK 99 | #endif 100 | #include 101 | #include 102 | 103 | /* Expansion of this Scintilla macro causes name clashes. */ 104 | #undef NotifyHeader 105 | 106 | #include 107 | 108 | #ifndef G_LOG_DOMAIN 109 | # define G_LOG_DOMAIN "GeanyPy" 110 | #endif 111 | 112 | #include "geanypy-document.h" 113 | #include "geanypy-editor.h" 114 | #include "geanypy-encoding.h" 115 | #include "geanypy-filetypes.h" 116 | #include "geanypy-plugin.h" 117 | #include "geanypy-project.h" 118 | #include "geanypy-scintilla.h" 119 | #include "geanypy-signalmanager.h" 120 | #include "geanypy-uiutils.h" 121 | 122 | 123 | #ifdef __cplusplus 124 | } /* extern "C" */ 125 | #endif 126 | 127 | #endif /* GEANYPY_H__ */ 128 | -------------------------------------------------------------------------------- /src/makefile.win32: -------------------------------------------------------------------------------- 1 | include ../win32env.mk 2 | 3 | SOURCES = \ 4 | geanypy-app.c \ 5 | geanypy-dialogs.c \ 6 | geanypy-document.c \ 7 | geanypy-editor.c \ 8 | geanypy-encoding.c \ 9 | geanypy-filetypes.c \ 10 | geanypy-highlighting.c \ 11 | geanypy-indentprefs.c \ 12 | geanypy-interfaceprefs.c \ 13 | geanypy-glog.c \ 14 | geanypy-main.c \ 15 | geanypy-mainwidgets.c \ 16 | geanypy-msgwindow.c \ 17 | geanypy-navqueue.c \ 18 | geanypy-plugin.c \ 19 | geanypy-prefs.c \ 20 | geanypy-project.c \ 21 | geanypy-scinotification.c \ 22 | geanypy-scinotifyheader.c \ 23 | geanypy-scintilla.c \ 24 | geanypy-search.c \ 25 | geanypy-signalmanager.c \ 26 | geanypy-templates.c \ 27 | geanypy-uiutils.c 28 | 29 | OBJECTS = $(SOURCES:.c=.o) 30 | 31 | 32 | geanypy.dll: $(OBJECTS) 33 | $(CC) -shared -o $@ $^ $(_LDFLAGS) 34 | 35 | %.o: %.c 36 | $(CC) $(_CFLAGS) -c -o $@ $< 37 | 38 | clean: 39 | $(RM_F) geanypy.dll *.o 40 | -------------------------------------------------------------------------------- /win32env.mk: -------------------------------------------------------------------------------- 1 | # Adjust the following paths to where you have stuff installed 2 | # ============================================================ 3 | # Note: don't quote the paths here, it's done below where needed 4 | 5 | # Where Mingw was installed 6 | MINGW_PREFIX = C:/Mingw 7 | 8 | # Where GTK+ bundle was installed 9 | GTK_PREFIX = C:/Gtk 10 | 11 | # Python 2 digit version (ie. 25, 26, 27) 12 | PYTHON_VERSION=27 13 | 14 | # Where Python 2.x was installed 15 | PYTHON_PREFIX = C:/Python$(PYTHON_VERSION) 16 | 17 | # Where Geany was installed 18 | GEANY_PREFIX = C:/Program Files/Geany 19 | 20 | # Where Geany's source code is 21 | # Note: official installer doesn't isntall headers, so need from source 22 | GEANY_SRCDIR = C:/Documents and Settings/User/My Documents/Projects/Geany/geany 23 | 24 | # Where PyGObject and PyGTK source code trees are 25 | # Note: official installer doesn't install headers, so need from source 26 | PYGOBJECT_SRCDIR = C:/Gtk/Python/pygobject-2.28.6 27 | PYGTK_SRCDIR = C:/Gtk/Python/pygtk-2.24.0 28 | 29 | # For making an .exe installer, path to NSIS `makensis.exe` program 30 | MAKE_NSIS = C:/Program Files/NSIS/makensis.exe 31 | 32 | # ====================================================================== 33 | # Shouldn't need to change anything below here 34 | # ============================================ 35 | 36 | GEANYPY_VERSION = 0.1 37 | 38 | CC = $(MINGW_PREFIX)/bin/gcc.exe 39 | MAKE = $(MINGW_PREFIX)/bin/mingw32-make.exe 40 | RM_F = -del /f /q 41 | 42 | GEANYPY_CFLAGS = \ 43 | -DGEANYPY_WINDOWS_BUILD=1 \ 44 | -DG_LOG_DOMAIN=\"GeanyPy\" 45 | 46 | GEANY_CFLAGS = \ 47 | -I"$(GEANY_SRCDIR)/plugins" \ 48 | -I"$(GEANY_SRCDIR)/src" \ 49 | -I"$(GEANY_SRCDIR)/tagmanager/src" \ 50 | -I"$(GEANY_SRCDIR)/scintilla/include" \ 51 | -DGTK 52 | 53 | GTK_CFLAGS = \ 54 | -I"$(GTK_PREFIX)/include" \ 55 | -I"$(GTK_PREFIX)/include/gtk-2.0" \ 56 | -I"$(GTK_PREFIX)/include/glib-2.0" \ 57 | -I"$(GTK_PREFIX)/include/cairo" \ 58 | -I"$(GTK_PREFIX)/include/pango-1.0" \ 59 | -I"$(GTK_PREFIX)/include/gdk-pixbuf-2.0" \ 60 | -I"$(GTK_PREFIX)/include/atk-1.0" \ 61 | -I"$(GTK_PREFIX)/lib/glib-2.0/include" \ 62 | -I"$(GTK_PREFIX)/lib/gtk-2.0/include" 63 | 64 | GTK_LIBS = \ 65 | -L"$(GTK_PREFIX)/lib" \ 66 | -lgtk-win32-2.0 \ 67 | -lgdk-win32-2.0 \ 68 | -latk-1.0 -lgio-2.0 \ 69 | -lpangoft2-1.0 \ 70 | -lpangocairo-1.0 \ 71 | -lgdk_pixbuf-2.0 \ 72 | -lcairo \ 73 | -lpango-1.0 \ 74 | -lfreetype \ 75 | -lfontconfig \ 76 | -lgobject-2.0 \ 77 | -lglib-2.0 \ 78 | -lgmodule-2.0 79 | 80 | PYTHON_CFLAGS = \ 81 | -I"$(PYTHON_PREFIX)/include" 82 | 83 | PYTHON_LIBS = \ 84 | -L"$(PYTHON_PREFIX)/libs" \ 85 | -lpython$(PYTHON_VERSION) 86 | 87 | PYGOBJECT_CFLAGS = \ 88 | -I"$(PYGOBJECT_SRCDIR)/gio" \ 89 | -I"$(PYGOBJECT_SRCDIR)/glib" \ 90 | -I"$(PYGOBJECT_SRCDIR)/gobject" 91 | 92 | PYGTK_CFLAGS = \ 93 | -I"$(PYGTK_SRCDIR)/gtk" 94 | 95 | _CFLAGS = \ 96 | $(CFLAGS) -Wall -Werror -g \ 97 | $(PYTHON_CFLAGS) \ 98 | $(GTK_CFLAGS) \ 99 | $(PYGOBJECT_CFLAGS) \ 100 | $(PYGTK_CFLAGS) \ 101 | $(GEANY_CFLAGS) \ 102 | $(GEANYPY_CFLAGS) 103 | 104 | _LDFLAGS = \ 105 | $(LDFLAGS) \ 106 | $(PYTHON_LIBS) \ 107 | $(GTK_LIBS) 108 | --------------------------------------------------------------------------------