From 910e7cb6891e11b1110a3ca44b5321a8b0e1c5c1 Mon Sep 17 00:00:00 2001 From: James Henstridge Date: Sun, 5 Sep 1999 15:46:57 +0000 Subject: [PATCH] added pygimp to tree, as organised with Marc Lehmann. I have not hooked it 1999-09-05 James Henstridge * plug-ins/pygimp/*: added pygimp to tree, as organised with Marc Lehmann. I have not hooked it into the main makefile yet. That should not be difficult though. --- ChangeLog | 6 + plug-ins/pygimp/.cvsignore | 17 + plug-ins/pygimp/AUTHORS | 0 plug-ins/pygimp/ChangeLog | 107 + plug-ins/pygimp/Makefile.am.14 | 17 + plug-ins/pygimp/Makefile.in | 457 ++ plug-ins/pygimp/NEWS | 65 + plug-ins/pygimp/README | 29 + plug-ins/pygimp/acinclude.m4 | 135 + plug-ins/pygimp/configure.in | 26 + plug-ins/pygimp/doc/.cvsignore | 3 + plug-ins/pygimp/doc/Makefile.am | 31 + plug-ins/pygimp/doc/end-note.html | 123 + .../pygimp/doc/gimp-module-procedures.html | 1503 ++++++ plug-ins/pygimp/doc/gimp-objects.html | 3004 +++++++++++ plug-ins/pygimp/doc/procedural-database.html | 361 ++ plug-ins/pygimp/doc/pygimp.html | 232 + plug-ins/pygimp/doc/pygimp.sgml | 2101 ++++++++ plug-ins/pygimp/doc/structure-of-plugin.html | 573 ++ plug-ins/pygimp/doc/support-modules.html | 259 + plug-ins/pygimp/gimpenums.py | 185 + plug-ins/pygimp/gimpfu.py | 495 ++ plug-ins/pygimp/gimpmodule.c | 4607 +++++++++++++++++ plug-ins/pygimp/gimpplugin.py | 56 + plug-ins/pygimp/gimpshelf.py | 83 + plug-ins/pygimp/gimpui.py | 364 ++ plug-ins/pygimp/plug-ins/.cvsignore | 5 + plug-ins/pygimp/plug-ins/Makefile.am | 7 + plug-ins/pygimp/plug-ins/clothify.py | 74 + plug-ins/pygimp/plug-ins/foggify.py | 54 + plug-ins/pygimp/plug-ins/gimpcons.py | 81 + plug-ins/pygimp/plug-ins/gtkcons.py | 330 ++ plug-ins/pygimp/plug-ins/pdbbrowse.py | 307 ++ plug-ins/pygimp/plug-ins/shadow_bevel.py | 68 + plug-ins/pygimp/plug-ins/sphere.py | 103 + plug-ins/pygimp/plug-ins/whirlpinch.py | 217 + plug-ins/pygimp/py-compile | 68 + plug-ins/pygimp/pygimp.spec | 50 + 38 files changed, 16203 insertions(+) create mode 100644 plug-ins/pygimp/.cvsignore create mode 100644 plug-ins/pygimp/AUTHORS create mode 100644 plug-ins/pygimp/ChangeLog create mode 100644 plug-ins/pygimp/Makefile.am.14 create mode 100644 plug-ins/pygimp/Makefile.in create mode 100644 plug-ins/pygimp/NEWS create mode 100644 plug-ins/pygimp/README create mode 100644 plug-ins/pygimp/acinclude.m4 create mode 100644 plug-ins/pygimp/configure.in create mode 100644 plug-ins/pygimp/doc/.cvsignore create mode 100644 plug-ins/pygimp/doc/Makefile.am create mode 100644 plug-ins/pygimp/doc/end-note.html create mode 100644 plug-ins/pygimp/doc/gimp-module-procedures.html create mode 100644 plug-ins/pygimp/doc/gimp-objects.html create mode 100644 plug-ins/pygimp/doc/procedural-database.html create mode 100644 plug-ins/pygimp/doc/pygimp.html create mode 100644 plug-ins/pygimp/doc/pygimp.sgml create mode 100644 plug-ins/pygimp/doc/structure-of-plugin.html create mode 100644 plug-ins/pygimp/doc/support-modules.html create mode 100644 plug-ins/pygimp/gimpenums.py create mode 100644 plug-ins/pygimp/gimpfu.py create mode 100644 plug-ins/pygimp/gimpmodule.c create mode 100644 plug-ins/pygimp/gimpplugin.py create mode 100644 plug-ins/pygimp/gimpshelf.py create mode 100644 plug-ins/pygimp/gimpui.py create mode 100644 plug-ins/pygimp/plug-ins/.cvsignore create mode 100644 plug-ins/pygimp/plug-ins/Makefile.am create mode 100755 plug-ins/pygimp/plug-ins/clothify.py create mode 100755 plug-ins/pygimp/plug-ins/foggify.py create mode 100755 plug-ins/pygimp/plug-ins/gimpcons.py create mode 100755 plug-ins/pygimp/plug-ins/gtkcons.py create mode 100755 plug-ins/pygimp/plug-ins/pdbbrowse.py create mode 100755 plug-ins/pygimp/plug-ins/shadow_bevel.py create mode 100755 plug-ins/pygimp/plug-ins/sphere.py create mode 100755 plug-ins/pygimp/plug-ins/whirlpinch.py create mode 100755 plug-ins/pygimp/py-compile create mode 100644 plug-ins/pygimp/pygimp.spec diff --git a/ChangeLog b/ChangeLog index 8f89be0587..1736d1a474 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +1999-09-05 James Henstridge + + * plug-ins/pygimp/*: added pygimp to tree, as organised with Marc + Lehmann. I have not hooked it into the main makefile yet. That + should not be difficult though. + Sun Sep 5 14:48:30 MEST 1999 Sven Neumann * app/gimpcontextpreview.c: small cosmetic change diff --git a/plug-ins/pygimp/.cvsignore b/plug-ins/pygimp/.cvsignore new file mode 100644 index 0000000000..4c05a35bbe --- /dev/null +++ b/plug-ins/pygimp/.cvsignore @@ -0,0 +1,17 @@ +Makefile +#Makefile.in +*.pyc +*.pyo +*.so +configure +config.log +config.h +config.cache +stamp-h +config.h.in +stamp-h.in +aclocal.m4 +INSTALL +mkinstalldirs +install-sh + diff --git a/plug-ins/pygimp/AUTHORS b/plug-ins/pygimp/AUTHORS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plug-ins/pygimp/ChangeLog b/plug-ins/pygimp/ChangeLog new file mode 100644 index 0000000000..a471b2b3fa --- /dev/null +++ b/plug-ins/pygimp/ChangeLog @@ -0,0 +1,107 @@ +1999-07-08 James Henstridge + + * plug-ins/shadow_bevel.py: added {disable,enable}_undo() calls. Also + made it possible to turn off the creation of a shadow. + + * plug-ins/foggify.py: added disable_undo() and enable_undo() calls + round the plugin code. + + * plug-ins/Makefile.am: forgot to add foggify and shadow_bevel to + the EXTRA_DIST list. + + * gimpui.py: fixed some small bugs that caused an exception when + loading on some versions of python. Weird thing is, it did not + cause problems on my computer (using 1.5.1) + +1999-07-05 James Henstridge + + * plug-ins/foggify.py, plug-ins/shadow_bevel.py: two new example + scripts. + +1999-07-04 James Henstridge + + * gimpmodule.c: added support for parasites. This new code is + conditionally compiled if GIMP_HAVE_PARASITES is defined. + Added other gimp 1.1 features to gimpmodule. + +1999-07-03 James Henstridge + + * gimpmodule.c (initgimp): added {major,minor,micro}_version constants + to gimpmodule. + (*) removed all the empty __doc__ strings -- they were just wasting + space, and I have other docs in there now. + + * plug-ins/clothify.py (python_clothify): same here. + + * plug-ins/sphere.py (python_sphere): some small hacks to get the + plugin to work with both gimp 1.0 and 1.1 -- using the pattern + "if pdb.gimp_whatever.nparams == n" to check if the img parameter + should be removed. + + * plug-ins/pdbbrowse.py (BrowseWin.extension_pdb_browse): fixed + small bug. + +1999-07-02 James Henstridge + + * plug-ins/whirlpinch.py (python_whirl_pinch): adjusted plug in to + work with drawable changes. + + * gimpmodule.c: removed the drawable type -- now only layer or channel + types are used. Also moved the drawable type's methods to both the + layer and channel object types. Also added some of the attributes of + the drawable object to layer and channel objects (some aren't needed + anymore, since layers are clearly defined as layers, and channels + as channels). + Make tile and pixel region objects keep a reference to their + associated drawable. + Added attributes has_alpha, is_colour, is_grey and is_indexed to + layer and channel objects. + +1999-06-22 James Henstridge + + * gimpplugin.py: renamed from plugin.py. This is mainly to reduce + namespace polution. + + * getvals.py: removed file. It has been replaced by the much more + functional gimpfu module. + + * plug-ins/gimpcons.py: added a browse button that will display the + pdbbrowse window, which can then be used to choose a pdb function. + The selected pdb function gets its prototype inserted on the + command line. + + * plug-ins/pdbbrowse.py: converted to use GTK+ widgets and gimpfu. + + * plug-ins/gimpcons.py: converted to use gimpfu and gtkcons.py. + + * plug-ings/gtkcons.py: a GTK replacement for tkcons.py. + + * plug-ins/whirlpinch.py, plug-ins/sphere.py: converted these two + to use gimpfu. + + * gimpshelf.py (shelf): some changes so that the gimp internal types + will pickle correctly. + + * gimpmodule.c (initgimp): export the type objects for gimpmodule's + internal types. This is mainly to help get pickling to work + correctly. + (_id2*): new functions to help convert id's to the builtin types. + + * plug-ins/clothify.py: converted to use gimpfu module. + +1999-06-21 James Henstridge + + * gimpfu.py: a simplified interface to writing GIMP plugins. It + handles all the user interaction stuff and saving the last used + values. It uses pygtk, so should match the interface of the rest + of gimp. + + * gimpui.py: new file that implements a number of useful widgets + for use with the interfaces of plugins. It requires pygtk. + + * gimpmodule.c (tuple_to_GParam): accept None for layer, channel + or drawable arguments to PDB functions. This value gets treated + like an ID of -1. + (img_cmp, lay_cmp, chn_cmp, drw_cmp): added compare functions so + that the == operator works as expected with those object types. + diff --git a/plug-ins/pygimp/Makefile.am.14 b/plug-ins/pygimp/Makefile.am.14 new file mode 100644 index 0000000000..20d455606a --- /dev/null +++ b/plug-ins/pygimp/Makefile.am.14 @@ -0,0 +1,17 @@ +SUBDIRS = doc plug-ins + +INCLUDES = $(PYTHON_INCLUDES) $(PYTHON_CFLAGS) $(GIMP_CFLAGS_NOUI) + +pyexec_PROGRAMS = gimpmodule$(SO) + +gimpmodule__SO__SOURCES = gimpmodule.c +gimpmodule__SO__LDADD = $(GIMP_LIBS_NOUI) +gimpmodule__SO__LINK = $(PYTHON_LINK) + +python_PYTHON = gimpplugin.py gimpenums.py gimpshelf.py \ + gimpui.py gimpfu.py + +EXTRA_DIST = pygimp.spec + +snap: + $(MAKE) dist distdir=$(PACKAGE)-SNAP-`date +"%Y%m%d"` diff --git a/plug-ins/pygimp/Makefile.in b/plug-ins/pygimp/Makefile.in new file mode 100644 index 0000000000..a3eaaa77c4 --- /dev/null +++ b/plug-ins/pygimp/Makefile.in @@ -0,0 +1,457 @@ +# This has been modified slightly. The Makefile.am file has been moved to +# Makefile.am.14 because it uses some non standard extensions. This should +# be resolved when automake-1.5 comes out. + +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = . + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +GIMPTOOL = @GIMPTOOL@ +GIMP_CFLAGS_NOUI = @GIMP_CFLAGS_NOUI@ +GIMP_LIBS_NOUI = @GIMP_LIBS_NOUI@ +MAKEINFO = @MAKEINFO@ +OPT = @OPT@ +PACKAGE = @PACKAGE@ +PYTHON = @PYTHON@ +PYTHON_CFLAGS = @PYTHON_CFLAGS@ +PYTHON_INCLUDES = @PYTHON_INCLUDES@ +PYTHON_LINK = @PYTHON_LINK@ +SO = @SO@ +VERSION = @VERSION@ +pluginexecdir = @pluginexecdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ + +SUBDIRS = doc plug-ins + +INCLUDES = $(PYTHON_INCLUDES) $(PYTHON_CFLAGS) $(GIMP_CFLAGS_NOUI) + +pyexec_PROGRAMS = gimpmodule$(SO) + +gimpmodule__SO__SOURCES = gimpmodule.c +gimpmodule__SO__LDADD = $(GIMP_LIBS_NOUI) +gimpmodule__SO__LINK = $(PYTHON_LINK) + +python_PYTHON = gimpplugin.py gimpenums.py gimpshelf.py gimpui.py gimpfu.py + + +EXTRA_DIST = pygimp.spec +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +PROGRAMS = $(pyexec_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +gimpmodule__SO__OBJECTS = gimpmodule.o +gimpmodule__SO__DEPENDENCIES = +gimpmodule__SO__LDFLAGS = +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +PYTHONFILES = gimpplugin.py gimpenums.py gimpshelf.py gimpui.py \ +gimpfu.py +py_compile = $(top_srcdir)/py-compile +DIST_COMMON = README $(PYTHONFILES) AUTHORS COPYING ChangeLog INSTALL \ +Makefile.am.14 Makefile.in NEWS aclocal.m4 configure configure.in \ +install-sh missing mkinstalldirs py-compile + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(gimpmodule__SO__SOURCES) +OBJECTS = $(gimpmodule__SO__OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status + +$(ACLOCAL_M4): configure.in + cd $(srcdir) && $(ACLOCAL) + +config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck +$(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES) + cd $(srcdir) && $(AUTOCONF) + +mostlyclean-pyexecPROGRAMS: + +clean-pyexecPROGRAMS: + -test -z "$(pyexec_PROGRAMS)" || rm -f $(pyexec_PROGRAMS) + +distclean-pyexecPROGRAMS: + +maintainer-clean-pyexecPROGRAMS: + +install-pyexecPROGRAMS: $(pyexec_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(pyexecdir) + @list='$(pyexec_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pyexecdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pyexecdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-pyexecPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(pyexec_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(pyexecdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +gimpmodule$(SO): $(gimpmodule__SO__OBJECTS) $(gimpmodule__SO__DEPENDENCIES) + @rm -f gimpmodule$(SO) + $(gimpmodule__SO__LINK) $(gimpmodule__SO__LDFLAGS) $(gimpmodule__SO__OBJECTS) $(gimpmodule__SO__LDADD) $(LIBS) + +mostlyclean-pythonPYTHON: + +clean-pythonPYTHON: + +distclean-pythonPYTHON: + +maintainer-clean-pythonPYTHON: + +install-pythonPYTHON: $(python_PYTHON) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(pythondir) + @list='$(python_PYTHON)'; for p in $$list; do\ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_PROGRAM) $(srcdir)/$$p $(DESTDIR)$(pythondir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pythondir)/$$p; \ + else :; fi; \ + done + @PYTHON=$(PYTHON) $(py_compile) --basedir $(DESTDIR)$(pythondir) $(python_PYTHON) + +uninstall-pythonPYTHON: + @$(NORMAL_UNINSTALL) + list='$(python_PYTHON)'; for p in $$list; do \ + rm -f $(DESTDIR)$(pythondir)/$$p; \ + rm -f $(DESTDIR)$(pythondir)/$${p}c; \ + rm -f $(DESTDIR)$(pythondir)/$${p}o; \ + done + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. + +@SET_MAKE@ + +all-recursive install-data-recursive install-exec-recursive \ +installdirs-recursive install-recursive uninstall-recursive \ +check-recursive installcheck-recursive info-recursive dvi-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ + rev="$$subdir $$rev"; \ + test "$$subdir" = "." && dot_seen=yes; \ + done; \ + test "$$dot_seen" = "no" && rev=". $$rev"; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + -rm -rf $(distdir) + GZIP=$(GZIP_ENV) $(TAR) zxf $(distdir).tar.gz + mkdir $(distdir)/=build + mkdir $(distdir)/=inst + dc_install_base=`cd $(distdir)/=inst && pwd`; \ + cd $(distdir)/=build \ + && ../configure --srcdir=.. --prefix=$$dc_install_base \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) dist + -rm -rf $(distdir) + @banner="$(distdir).tar.gz is ready for distribution"; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + echo "$$dashes" +dist: distdir + -chmod -R a+r $(distdir) + GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) + -rm -rf $(distdir) +dist-all: distdir + -chmod -R a+r $(distdir) + GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) + -rm -rf $(distdir) +distdir: $(DISTFILES) + -rm -rf $(distdir) + mkdir $(distdir) + -chmod 777 $(distdir) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + chmod 777 $(distdir)/$$subdir; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(distdir) distdir=../$(distdir)/$$subdir distdir) \ + || exit 1; \ + fi; \ + done +gimpmodule.o: gimpmodule.c + +info-am: +info: info-recursive +dvi-am: +dvi: dvi-recursive +check-am: all-am +check: check-recursive +installcheck-am: +installcheck: installcheck-recursive +install-exec-am: install-pyexecPROGRAMS +install-exec: install-exec-recursive + +install-data-am: install-pythonPYTHON +install-data: install-data-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-recursive +uninstall-am: uninstall-pyexecPROGRAMS uninstall-pythonPYTHON +uninstall: uninstall-recursive +all-am: Makefile $(PROGRAMS) +all-redirect: all-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: installdirs-recursive +installdirs-am: + $(mkinstalldirs) $(DESTDIR)$(pyexecdir) $(DESTDIR)$(pythondir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-pyexecPROGRAMS mostlyclean-compile \ + mostlyclean-pythonPYTHON mostlyclean-tags \ + mostlyclean-generic + +mostlyclean: mostlyclean-recursive + +clean-am: clean-pyexecPROGRAMS clean-compile clean-pythonPYTHON \ + clean-tags clean-generic mostlyclean-am + +clean: clean-recursive + +distclean-am: distclean-pyexecPROGRAMS distclean-compile \ + distclean-pythonPYTHON distclean-tags distclean-generic \ + clean-am + +distclean: distclean-recursive + -rm -f config.status + +maintainer-clean-am: maintainer-clean-pyexecPROGRAMS \ + maintainer-clean-compile maintainer-clean-pythonPYTHON \ + maintainer-clean-tags maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-recursive + -rm -f config.status + +.PHONY: mostlyclean-pyexecPROGRAMS distclean-pyexecPROGRAMS \ +clean-pyexecPROGRAMS maintainer-clean-pyexecPROGRAMS \ +uninstall-pyexecPROGRAMS install-pyexecPROGRAMS mostlyclean-compile \ +distclean-compile clean-compile maintainer-clean-compile \ +mostlyclean-pythonPYTHON distclean-pythonPYTHON clean-pythonPYTHON \ +maintainer-clean-pythonPYTHON uninstall-pythonPYTHON \ +install-pythonPYTHON install-data-recursive uninstall-data-recursive \ +install-exec-recursive uninstall-exec-recursive installdirs-recursive \ +uninstalldirs-recursive all-recursive check-recursive \ +installcheck-recursive info-recursive dvi-recursive \ +mostlyclean-recursive distclean-recursive clean-recursive \ +maintainer-clean-recursive tags tags-recursive mostlyclean-tags \ +distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ +dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ +install-exec install-data-am install-data install-am install \ +uninstall-am uninstall all-redirect all-am all installdirs-am \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +snap: + $(MAKE) dist distdir=$(PACKAGE)-SNAP-`date +"%Y%m%d"` + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/plug-ins/pygimp/NEWS b/plug-ins/pygimp/NEWS new file mode 100644 index 0000000000..e59437860c --- /dev/null +++ b/plug-ins/pygimp/NEWS @@ -0,0 +1,65 @@ +pygimp-0.5: 8-July-1999 + - Fixed some bugs that I missed in gimpui. It should actually work + on other people's systems now (I don't know why it worked on mine). + - Included the foggify.py and shadow_bevel.py plugins in the package. + - Added a timeout function to flush the displays in the gimpconsole + plugin. This way you can see the results of what you type easily. + +pygimp-0.4: 5-July-1999 + - Removed the drawable type -- now layer or channel objects are used + in its place. All drawable methods have been transfered to both + the channel and layer objects. The layer and channel objects have + the following attributes in common: + ID, bpp, has_alpha, height, image, is_color, is_colour, is_gray, + is_grey, is_indexed, mask_bounds, name, offsets, opacity + This means that the gimp.drawable(layer|channel) command is gone, + and so is the gimp.layer(drawable) and gimp.channel(drawable) + syntaxes (the gimp.layer and gimp.channel commands still exist). + I made this change because the previous setup was confusing, and + more complicated than it needed to be. + - Removed all tkinter code, and replaced it with pygtk code. There + is now also a convenience module gimpfu, which is a simpler + interface to + +pygimp-0.3: 8-February-1999 + - Some small build cleanups. Now you should be able to do the normal + ./configure ; make ; make install. It should compile fine with + GIMP 1.0 (I haven't tested 1.1 series though). + - gimpmodule.c now prints exception messages, so now exceptions caused + by a plugin will print a stack trace to stdout, and cause gimp to + realise the plugin failed. + - The disable_undo and enable_undo methods of image now use the + stack based enable/disable functions. + - Added function names to PyArg_ParseTuple calls, so exceptions + are a little easier to understand. + - added a spec file for building RPMS for this package. + - added gimp.extension_ack, gimp.extension_process, + gimp.install_temp_proc and gimp.uninstall_temp_proc which will be + useful to implement an interface similar to script-fu's (ie. one + copy of python running in the background, responding to requests + from the gimp process every now and again). + - changed over to using glib's safe memory allocation routines g_new, + g_strdup and g_free. + - Converted documentation to docbook -- linuxdoc is just about dead. + +pygimp-0.2.0: 19-Nov-1997 + - implemented pixel regions. You can access them as a mapping. (ie. + pr[x,y] is get_pixel, pr[x1:x2,y] is get_row, pr[x, y1:y2] is get_col + and pr[x1:x2, y1:y2] is get_rect) + - to demonstrate the use of this low level object, I have included a + translation of the c plugin Whirl and Pinch. + - fixed a problem where I didn't check for a NULL when parsing + arguments for a plugin. This caused a crash when you ran a plugin + that took a string as an argument was run in interactive mode. + +pygimp-0.1.0: 18-Nov-1997 + - first public release. + - implements images, layers, channels, drawables, tiles, and just about + everything except pixel regions. + - I have included a number of example plugins: + - gimpcons is an interactive python console + - pdbbrowse is a browser for the PDB + - clothify is a translation of the Script-Fu script + - sphere is a translation of the Script-Fu script + + diff --git a/plug-ins/pygimp/README b/plug-ins/pygimp/README new file mode 100644 index 0000000000..124466df7e --- /dev/null +++ b/plug-ins/pygimp/README @@ -0,0 +1,29 @@ + Gimp-Python - allows the writing of plugins for Gimp in Python. + Copyright (C) 1997-1999 James Henstridge + +This is the Gimp-Python package. It is free software and is covered +by the GNU General Public Licence (A copy of which is in the file +COPYING in the distribution) with the exception that you may link +gimpmodule with the python program. + +For more information, please see the documentation in the directory +doc. The documentation is in SGML, and translations to text, html and +texinfo are included in the distribution. + +To build the program, you should be able to just type: + ./configure --prefix= + make + make install + +Pregenerated HTML docs are included with the package in the doc +directory. If you want ps or pdf documentation, get Mark Galassi's +docbook SGML packages. + +Here is a list of what you will find in the subdirectories of this +distribution: + . - The actual source code. + doc/ - The documentation. + plug-ins/ - The sample plugins. Contents are copied to GIMP plug-ins + directory. + + diff --git a/plug-ins/pygimp/acinclude.m4 b/plug-ins/pygimp/acinclude.m4 new file mode 100644 index 0000000000..fc90dbcd6f --- /dev/null +++ b/plug-ins/pygimp/acinclude.m4 @@ -0,0 +1,135 @@ +## Find the install dirs for the python installation. +## By James Henstridge + +# serial 1 + +AC_DEFUN(AM_PATH_PYTHON, + [AC_CHECK_PROGS(PYTHON, python python1.5 python1.4 python1.3,no) + if test "$PYTHON" != no; then + AC_MSG_CHECKING([where .py files should go]) +changequote(, )dnl + pythondir=`$PYTHON -c ' +import sys +if sys.version[0] > "1" or sys.version[2] > "4": + print "%s/lib/python%s/site-packages" % (sys.prefix, sys.version[:3]) +else: + print "%s/lib/python%s" % (sys.prefix, sys.version[:3])'` +changequote([, ])dnl + AC_MSG_RESULT($pythondir) + AC_MSG_CHECKING([where python extensions should go]) +changequote(, )dnl + pyexecdir=`$PYTHON -c ' +import sys +if sys.version[0] > "1" or sys.version[2] > "4": + print "%s/lib/python%s/site-packages" % (sys.exec_prefix, sys.version[:3]) +else: + print "%s/lib/python%s/sharedmodules" % (sys.exec_prefix, sys.version[:3])'` +changequote([, ])dnl + AC_MSG_RESULT($pyexecdir) + else + # these defaults are version independent ... + AC_MSG_CHECKING([where .py files should go]) + pythondir='$(prefix)/lib/site-python' + AC_MSG_RESULT($pythondir) + AC_MSG_CHECKING([where python extensions should go]) + pyexecdir='$(exec_prefix)/lib/site-python' + AC_MSG_RESULT($pyexecdir) + fi + AC_SUBST(pythondir) + AC_SUBST(pyexecdir)]) + + +## this one is commonly used with AM_PATH_PYTHONDIR ... +dnl AM_CHECK_PYMOD(MODNAME [,SYMBOL [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]]]) +dnl Check if a module containing a given symbol is visible to python. +AC_DEFUN(AM_CHECK_PYMOD, +[AC_REQUIRE([AM_PATH_PYTHON]) +py_mod_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` +AC_MSG_CHECKING(for ifelse([$2],[],,[$2 in ])python module $1) +AC_CACHE_VAL(py_cv_mod_$py_mod_var, [ +ifelse([$2],[], [prog=" +import sys +try: + import $1 +except ImportError: + sys.exit(1) +except: + sys.exit(0) +sys.exit(0)"], [prog=" +import $1 +$1.$2"]) +if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC + then + eval "py_cv_mod_$py_mod_var=yes" + else + eval "py_cv_mod_$py_mod_var=no" + fi +]) +py_val=`eval "echo \`echo '$py_cv_mod_'$py_mod_var\`"` +if test "x$py_val" != xno; then + AC_MSG_RESULT(yes) + ifelse([$3], [],, [$3 +])dnl +else + AC_MSG_RESULT(no) + ifelse([$4], [],, [$4 +])dnl +fi +]) + + + +# serial 1 + +dnl finds information needed for compilation of shared library style python +dnl extensions. AM_PATH_PYTHON should be called before hand. +AC_DEFUN(AM_INIT_PYEXEC_MOD, + [AC_REQUIRE([AM_PATH_PYTHON]) + AC_MSG_CHECKING([for python headers]) + AC_CACHE_VAL(am_cv_python_includes, + [changequote(,)dnl + am_cv_python_includes="`$PYTHON -c ' +import sys +includepy = \"%s/include/python%s\" % (sys.prefix, sys.version[:3]) +if sys.version[0] > \"1\" or sys.version[2] > \"4\": + libpl = \"%s/include/python%s\" % (sys.exec_prefix, sys.version[:3]) +else: + libpl = \"%s/lib/python%s/config\" % (sys.exec_prefix, sys.version[:3]) +print \"-I%s -I%s\" % (includepy, libpl)'`" + changequote([, ])]) + PYTHON_INCLUDES="$am_cv_python_includes" + AC_MSG_RESULT(found) + AC_SUBST(PYTHON_INCLUDES) + + AC_MSG_CHECKING([definitions from Python makefile]) + AC_CACHE_VAL(am_cv_python_makefile, + [changequote(,)dnl + py_makefile="`$PYTHON -c ' +import sys +print \"%s/lib/python%s/config/Makefile\"%(sys.exec_prefix, sys.version[:3])'`" + if test ! -f "$py_makefile"; then + AC_MSG_ERROR([*** Couldn't find the python config makefile. Maybe you are +*** missing the development portion of the python installation]) + fi + eval `sed -n \ +-e "s/^CC=[ ]*\(.*\)/am_cv_python_CC='\1'/p" \ +-e "s/^OPT=[ ]*\(.*\)/am_cv_python_OPT='\1'/p" \ +-e "s/^CCSHARED=[ ]*\(.*\)/am_cv_python_CCSHARED='\1'/p" \ +-e "s/^LDSHARED=[ ]*\(.*\)/am_cv_python_LDSHARED='\1'/p" \ +-e "s/^SO=[ ]*\(.*\)/am_cv_python_SO='\1'/p" \ + $py_makefile` + am_cv_python_makefile=found + changequote([, ])]) + AC_MSG_RESULT(done) + CC="$am_cv_python_CC" + OPT="$am_cv_python_OPT" + SO="$am_cv_python_SO" + PYTHON_CFLAGS="$am_cv_python_CCSHARED \$(OPT)" + PYTHON_LINK="$am_cv_python_LDSHARED -o \[$]@" + + AC_SUBST(CC)dnl + AC_SUBST(OPT)dnl + AC_SUBST(SO)dnl + AC_SUBST(PYTHON_CFLAGS)dnl + AC_SUBST(PYTHON_LINK)]) + diff --git a/plug-ins/pygimp/configure.in b/plug-ins/pygimp/configure.in new file mode 100644 index 0000000000..e5715c9240 --- /dev/null +++ b/plug-ins/pygimp/configure.in @@ -0,0 +1,26 @@ +AC_INIT(gimpmodule.c) + +AM_INIT_AUTOMAKE(pygimp, 0.5) + +AM_PATH_PYTHON +AM_INIT_PYEXEC_MOD + +AM_CHECK_PYMOD(gtk,,,AC_MSG_ERROR([You need the pygtk package to use pygimp])) + +AC_PATH_PROG(GIMPTOOL, gimptool, no) + +if test "x$GIMPTOOL" = xno; then + AC_MSG_ERROR([could not find gimptool script]) +fi + +GIMP_CFLAGS_NOUI=`$GIMPTOOL --cflags-noui` +GIMP_LIBS_NOUI=`$GIMPTOOL --libs-noui` + +gimp_ver=`$GIMPTOOL --version | cut -d. -f 1-2 ` +pluginexecdir=\${libdir}/gimp/$gimp_ver/plug-ins + +AC_SUBST(GIMP_CFLAGS_NOUI) +AC_SUBST(GIMP_LIBS_NOUI) +AC_SUBST(pluginexecdir) + +AC_OUTPUT(Makefile doc/Makefile plug-ins/Makefile) diff --git a/plug-ins/pygimp/doc/.cvsignore b/plug-ins/pygimp/doc/.cvsignore new file mode 100644 index 0000000000..22a4e7292f --- /dev/null +++ b/plug-ins/pygimp/doc/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in + diff --git a/plug-ins/pygimp/doc/Makefile.am b/plug-ins/pygimp/doc/Makefile.am new file mode 100644 index 0000000000..96a30af2cd --- /dev/null +++ b/plug-ins/pygimp/doc/Makefile.am @@ -0,0 +1,31 @@ + +noinst_DATA = pygimp.html + +EXTRA_DIST = pygimp.sgml \ + pygimp.html \ + structure-of-plugin.html \ + procedural-database.html \ + gimp-module-procedures.html \ + gimp-objects.html \ + support-modules.html \ + end-note.html + +printed: pygimp.ps pygimp.pdf + +clean: + rm -f *.html pygimp.ps pygimp.pdf + + +pygimp.ps: pygimp.sgml + db2ps $(srcdir)/pygimp.sgml + +pygimp.pdf: pygimp.sgml + db2pdf $(srcdir)/pygimp.sgml + +pygimp.html: pygimp.sgml + cd $(srcdir) && db2html pygimp.sgml + cd $(srcdir) && mv pygimp/*.html . + rm -rf $(srcdir)/pygimp + +.PHONY: printed clean + diff --git a/plug-ins/pygimp/doc/end-note.html b/plug-ins/pygimp/doc/end-note.html new file mode 100644 index 0000000000..c3e446d30a --- /dev/null +++ b/plug-ins/pygimp/doc/end-note.html @@ -0,0 +1,123 @@ + +End Note
Gimp Python Documentation
Prev 

End Note

This package is not yet complete, but it has enough in it to + be useful for writing plugins for Gimp. If you write any plugins + that might be useful as examples, please mail me at james@daa.com.au.


PrevHome 
Support Modules  
\ No newline at end of file diff --git a/plug-ins/pygimp/doc/gimp-module-procedures.html b/plug-ins/pygimp/doc/gimp-module-procedures.html new file mode 100644 index 0000000000..870c9f8b75 --- /dev/null +++ b/plug-ins/pygimp/doc/gimp-module-procedures.html @@ -0,0 +1,1503 @@ + +Gimp Module Procedures
Gimp Python Documentation
PrevNext

Gimp Module Procedures

The gimp module contains a number of + procedures and functions, as well as the definitions of many gimp + types such as images, and the procedural database. This section + explains the base level procedures.

Constructors and Object Deletion

There are a number of functions in the + gimp module that are used to create the objects + used to make up an image in Gimp. Here is a set of descriptions + of these constructors:

gimp.image(width, + height, + type)

This procedure creates an image with the given + dimensions and type (type is one of + RGB, GRAY or + INDEXED).

gimp.layer(img, + name, width, + height, type, + opacity, + mode)

Create a new layer called + name, with the given dimensions and + type (one of the + *_IMAGE constants), + opacity (float between 0 and 100) and + a mode (one of the + *_MODE constants). The layer can + then be added to the image with the + img.add_layer method.

gimp.channel(img, + name, width, + height, + opacity, + colour)

Create a new channel object with the given + dimensions, opacity and + colour (one of the + *_CHANNEL constants). This channel can + then be added to an image.

gimp.display(img)

Create a new display window for the given image. + The window will not be displayed until a call to + gimp.displays_flush is made.

gimp.parasite(name, flags, data)

Create a new parasite. The parasite can then be + attached to gimp, an image or a drawable. This is only + available in gimp >= 1.1

When any of these objects get removed from memory (such as + when their name goes out of range), the gimp thing it represents + does not get deleted with it (otherwise when your plugin + finished running, it would delete all its work). In order to + delete the thing the Python object represents, you should use + the gimp.delete procedure. It deletes the + gimp thing associated with the Python object given as a + parameter. If the object is not an image, layer, channel, + drawable or display gimp.delete does + nothing.

Configuration Information

There are a number of functions that can be used to gather + information about the environment the plugin is running in:

gimp.color_cube() or + gimp.colour_cube()

Returns the current colour cube.

gimp.gamma()

Returns the current gamma correction.

gimp.install_cmap()

Returns non-zero if a colour map has been + installed.

gimp.use_xshm()

Returns non-zero if Gimp is using X shared + memory.

gimp.gtkrc()

Returns the file name of the GTK configuration + file.

Palette Operations

These functions alter the currently selected foreground + and background.

gimp.get_background()

Returns a 3-tuple containing the current background + colour in RGB form.

gimp.get_foreground()

Returns a 3-tuple containing the current foreground + colour in RGB form.

gimp.set_background(r, + g, b)

Sets the current background colour. The three + arguments can be replaced by a single 3-tuple like that + returned by gimp.get_background.

gimp.set_foreground(r, + g, b)

Sets the current foreground colour. Like + gimp.set_background, the arguments may + be replaced by a 3-tuple.

Gradient Operations

These functions perform operations on gradients:

gimp.gradients_get_active()

Returns the name of the active gradient.

gimp.gradients_set_active(name)

Sets the active gradient.

gimp.gradients_get_list()

Returns a list of the names of the available + gradients.

gimp.gradients_sample_uniform(num)

Returns a list of num + samples, where samples consist of 4-tuples of floats + representing the red, green, blue and alpha values for the + sample.

gimp.gradients_sample_custom(pos)

Similar to + gimp.gradients_sample_uniform, except + the samples are taken at the positions given in the list + of floats pos instead of uniformly + through the gradient.

PDB Registration Functions

These functions either install procedures into the PDB or + alert gimp to their special use (eg as file handlers).

For simple plugins, you will usually only need to use + register from gimpfu.

gimp.install_procedure(name, + blurb, help, + author, + copyright, + date, + menu_path, + image_types, + type, params, + ret_vals)

This procedure is used to install a procedure into + the PDB. The first eight parameters are strings, + type is a one of the + PROC_* constants, and the last two + parameters are sequences describing the parameters and + return values. Their format is the same as the param and + ret_vals methods or PDB procedures.

gimp.install_temp_proc(name, + blurb, help, + author, + copyright, + date, + menu_path, + image_types, type, + params, ret_vals)

This procedure is used to install a procedure into + the PDB temporarily. That is, it must be added again + every time gimp is run. This procedure will be called the + same way as all other procedures for a plugin.

gimp.uninstall_temp_proc(name)

This removes a temporary procedure from the + PDB.

gimp.register_magic_load_handler(name, + extensions, + prefixes, + magics)

This procedure tells Gimp that the PDB procedure + name can load files with + extensions and + prefixes (eg http:) with magic + information magics.

gimp.register_load_handler(name, + extensions, + prefixes)

This procedure tells Gimp that the PDB procedure + name can load files with + extensions and + prefixes (eg http:).

gimp.register_save_handler(name, + extensions, + prefixes)

This procedure tells Gimp that the PDB procedure + name can save files with + extensions and + prefixes (eg http:).

Other Functions

These are the other functions in the + gimp module.

gimp.main(init_func, + quit_func, + query_func, + run_func)

This function is the one that controls the execution + of a Gimp-Python plugin. It is better to not use this + directly but rather subclass the plugin class, defined in + the the section called The gimpplugin Module.

gimp.pdb

The procedural database object.

gimp.progress_init([label])

(Re)Initialise the progress meter with + label (or the plugin name) as a + label in the window.

gimp.progress_update(percnt)

Set the progress meter to + percnt done.

gimp.query_images()

Returns a list of all the image objects.

gimp.quit()

Stops execution imediately and exits.

gimp.displays_flush()

Update all the display windows.

gimp.tile_width()

The maximum width of a tile.

gimp.tile_height()

The maximum height of a tile.

gimp.tile_cache_size(kb)

Set the size of the tile cache in kilobytes.

gimp.tile_cache_ntiles(n)

Set the size of the tile cache in tiles.

gimp.get_data(key)

Get the information associated with + key. The data will be a string. + This function should probably be used through the the section called The gimpshelf Module.

gimp.set_data(key, + data)

Set the information in the string + data with + key. The data will persist for the + whole gimp session. Rather than directly accessing this + function, it is better to go through the the section called The gimpshelf Module.

gimp.extension_ack()

Tells gimp that the plugin has finished its work, + while keeping the plugin connection open. This is used by + an extension plugin to tell gimp it can continue, while + leaving the plugin connection open. This is what the + script-fu plugin does so that only one scheme interpretter + is needed.

gimp.extension_process(timeout)

Makes the plugin check for messages from gimp. + generally this is not needed, as messages are checked + during most calls in the gimp module.

Parasites

In gimp >= 1.1, it is possible to attach arbitrary data to + an image through the use of parasites. Parasites are simply + wrappers for the data, containing its name and some flags. + Parasites have the following parameters:

data

The data for the parasite -- a string

flags

The flags for the parasite

is_persistent

True if this parasite is persistent

is_undoable

True if this parasite is undoable

name

The name of the parasite

Parasites also have the methods copy, + is_type and + has_flag.

There is a set of four functions that are used to + manipulate parasites. They exist as functions in the + gimp module, and methods for image and + drawable objects. They are:

find_parasite(name)

find a parasite by its name.

attach_parasite(parasite)

Attach a parasite to this object.

attach_new_parasite(name, flags, data)

Create a new parasite and attach it.

detach_parasite(name)

Detach the named parasite


PrevHomeNext
The Procedural Database Gimp Objects
\ No newline at end of file diff --git a/plug-ins/pygimp/doc/gimp-objects.html b/plug-ins/pygimp/doc/gimp-objects.html new file mode 100644 index 0000000000..e249fb14a4 --- /dev/null +++ b/plug-ins/pygimp/doc/gimp-objects.html @@ -0,0 +1,3004 @@ + +Gimp Objects
Gimp Python Documentation
PrevNext

Gimp Objects

Gimp-Python implements a number of special object types that + represent the different types of parameters you can pass to a PDB + procedure. Rather than just making these place holders, I have + added a number of members and methods to them that allow a lot of + configurability without directly calling PDB procedures.

There are also a couple of extra objects that allow low + level manipulation of images. These are tile objects (working) + and pixel regions (not quite finished).

Image Object

This is the object that represents an open image. In this + section, image represents a generic + image object.

Image Members

image.active_channel

This is the active channel of the image. You can + also assign to this member, or + None if there is no active + channel.

image.active_layer

This is the active layer of the image. You can + also assign to this member, or + None if there is no active + layer.

image.base_type

This is the type of the image (eg RGB, INDEXED).

image.channels

This is a list of the channels of the image. + Altering this list has no effect, and you can not assign + to this member.

image.cmap

This is the colour map for the image.

image.filename

This is the filename for the image. A file load + or save handler might assign to this.

image.height

This is the height of the image. You can't assign + to this member.

image.floating_selection

The floating selection layer, or + None if there is no floating + selection.

image.layers

This is a list of the layers of the image.

image.selection

The selection mask for the image.

image.width

This is the width of the image. You can't assign + to this member.

Image Methods

image.add_channel(channel, + position)

Adds channel to + image in position + position.

image.add_layer(layer, + position)

Adds layer to + image in position + position.

image.add_layer_mask(layer, + mask)

Adds the mask mask to + layer.

image.clean_all()

Unsets the dirty flag on the image.

image.disable_undo()

Disables undo for + image.

image.enable_undo()

Enables undo for image. + You might use these commands round a plugin, so that the + plugin's actions can be undone in a single step.

image.flatten()

Returns the resulting layer after merging all the + visible layers, discarding non visible ones and + stripping the alpha channel.

image.get_component_active(component)

Returns true if component + (one of the *_CHANNEL constants) is + active.

image.get_component_visible(component)

Returns true if component + is visible.

image.set_component_active(component, + active)

Sets the activeness of + component.

image.set_component_visible(component, + active)

Sets the visibility of + component.

image.lower_channel(channel)

Lowers channel.

image.lower_layer(layer)

Lowers layer.

image.merge_visible_layers(type)

Merges the visible layers of + image using the given merge + type.

image.pick_correlate_layer(x, + y)

Returns the layer that is visible at the point + (x,y), or + None if no layer matches.

image.raise_channel(channel)

Raises channel.

image.raise_layer(layer)

Raises layer.

image.remove_channel(channel)

Removes channel from + image.

image.remove_layer(layer)

Removes layer from + image.

image.remove_layer_mask(layer, + mode)

Removes the mask from + layer, with the given + mode (either APPLY or + DISCARD).

image.resize(width, + height, x, + y)

Resizes the image to size (width, + height) and places the old contents at + position (x,y).

Channel Objects

These objects represent a Gimp Image's colour channels. + In this section, channel will refer + to a generic channel object.

Channel Members

channel.colour + or + channel.color

The colour of the channel.

channel.height

The height of the channel.

channel.width

The width of the channel.

channel.image

The image the channel belongs to, or + None if it isn't attached + yet.

channel.layer

The channel's layer (??) or + None if one doesn't exist.

channel.layer_mask

Non zero if the channel is a layer mask.

channel.name

The name of the channel.

channel.opacity

The opacity of the channel.

channel.show_masked

The show_masked value of the channel.

channel.visible

Non-zero if the channel is visible.

Channel Methods

channel.copy()

returns a copy of the channel.

Layer Objects

Layer objects represent the layers of a Gimp image. In + this section I will refer to a generic layer called + layer.

Layer Members

layer.apply_mask

The apply mask setting. (non zero if the layer + mask is being composited with the layer's alpha + channel).

layer.bpp

The number of bytes per pixel.

layer.edit_mask

The edit mask setting. (non zero if the mask is + active, rather than the layer).

layer.height

The height of the layer.

layer.image

The image the layer is part of, or + None if the layer isn't + attached.

layer.is_floating_selection

Non zero if this layer is the image's floating + selection.

layer.mask

The layer's mask, or None + if it doesn't have one.

layer.mode

The mode of the layer.

layer.name

The name of the layer.

layer.opacity

The opacity of the layer.

layer.preserve_transparency

The layer's preserve transparency setting.

Layer Methods

layer.add_alpha()

Adds an alpha component to the layer.

layer.copy([alpha])

Creates a copy of the layer, optionally with an + alpha layer.

layer.create_mask(type)

Creates a layer mask of type + type.

layer.resize(w, + h, x, + y)

Resizes the layer to (w, + h), positioning the original contents at + (x,y).

layer.scale(h, + w, + origin)

Scales the layer to (w, h), + using the specified origin (local + or image).

layer.set_offsets(x, + y)

Sets the offset of the layer, relative to the + image's origin

layer.translate(x, + y)

Moves the layer to (x, y) + relative to its current position.

Drawable Objects

Both layers and channels are drawables. Hence there are a + number of operations that can be performed on both objects. + They also have some common attributes and methods. In the + description of these attributes, I will refer to a generic + drawable called drawable.

Drawable Members

drawable.bpp

The number of bytes per pixel.

drawable.is_colour + or + drawable.is_color

Non zero if the drawable is colour.

drawable.is_grey + or + drawable.is_gray

Non zero if the drawable is greyscale.

drawable.has_alpha

Non zero if the drawable has an alpha channel.

drawable.height

The height of the drawable.

drawable.image

The image the drawable belongs to.

drawable.is_indexed

Non zero if the drawable uses an indexed colour + scheme.

drawable.mask_bounds

The bounds of the drawable's selection.

drawable.name

The name of the drawable.

drawable.offsets

The offset of the top left hand corner of the + drawable.

drawable.type

The type of the drawable.

drawable.visible

Non zero if the drawable is visible.

drawable.width

The width of the drawable.

Drawable Methods

drawable.fill(fill_type)

Fills the drawable with given + fill_type (one of the + *_FILL constants).

drawable.flush()

Flush the changes to the drawable.

drawable.get_pixel_rgn(x, + y, w, + h, [dirty, + [shadow])

Creates a pixel region for the drawable. It will + cover the region with origin + (x,y) and dimensions w + x h. The dirty + argument sets whether any changes to the pixel region + will be reflected in the drawable (default is TRUE). + The shadow argument sets whether + the pixel region acts on the shadow tiles or not + (default is FALSE). If you draw on the shadow tiles, + you must call + drawable.merge_shadow() + for changes to take effect.

drawable.get_tile(shadow, + row, + col)

Get a tile at (row, + col). Either on or off the + shadow buffer.

drawable.get_tile2(shadow, + x, y)

Get the tile that contains the pixel + (x, y).

drawable.merge_shadow()

Merge the shadow buffer back into the + drawable.

drawable.update(x, + y, w, + h)

Update the given portion of the drawable.

Tile Objects

Tile objects represent the way Gimp stores information. A + tile is basically just a 64x64 pixel region of the drawable. + The reason Gimp breaks the image into small pieces like this is + so that the whole image doesn't have to be loaded into memory in + order to alter one part of it. This becomes important with + larger images.

In Gimp-Python, you would use Tiles if you wanted to + perform some low level operation on the image, instead of using + procedures in the PDB. This type of object gives a Gimp-Python + plugin the power of a C plugin, rather than just the power of a + Script-Fu script. Tile objects are created with either the + drawable.get_tile() + or + drawable.get_tile2() + functions. In this section, I will refer to a generic tile + object named tile.

Tile Members

All tile members are read only.

tile.bpp

The number of bytes per pixel.

tile.dirty

If there have been changes to the tile since it + was last flushed.

tile.drawable

The drawable that the tile is from.

tile.eheight

The actual height of the tile.

tile.ewidth

The actual width of the tile.

tile.ref_count

The reference count of the tile. (this is + independent of the Python object reference + count).

tile.shadow

Non zero if the tile is part of the shadow + buffer.

Tile Methods

tile.flush()

Flush any changes in the tile. Note that the tile + is automatically flushed when the Python object is + deleted from memory.

Tile Mapping Behaviour

Tile objects also act as a mapping, or sequence. You + can access the pixels in the tile in one of two ways. You can + either access them with a single number, which refers to its + position in the tile + (eg. tile[64] + refers to the first pixel in the second row of a 64x64 pixel + tile). The other way is with a tuple, representing the + coordinates on the tile + (eg. tile[0, 1] + refers to the first pixel on the second row of the + tile).

The type of these subscripts is a string of length + tile.bpp. + When you assign to a subscript, the dirty flag is + automatically set on the tile, so you don't have to explicitly + set the flag, or flush the tile.

Pixel Regions

Pixel region objects give an interface for low level + operations to act on large regions of an image, instead of on + small 64x64 pixel tiles. In this section I will refer to a + generic pixel region called pr. For + an example of a pixel region's use, please see the example + plugin whirlpinch.py.

Pixel Region Members

pr.drawable

The drawable this pixel region is for.

pr.bpp

The number of bytes per pixel for the drawable.

pr.rowstride

The rowstride for the pixel region.

pr.x

The x coordinate of the top left hand corner.

pr.y

The y coordinate of the top left hand corner.

pr.w

The width of the pixel region.

pr.h

The height of the pixel region.

pr.dirty

Non zero if changes to the pixel region will be + reflected in the drawable.

pr.shadow

Non zero if the pixel region acts on the shadow + tiles of the drawable.

Pixel Region Methods

pr.resize(x, + y, w, + h)

resize the pixel region so that it operates on the + the region with corner (x, y) + with dimensions w x h.

Pixel Region Mapping Behaviour

The pixel region acts as a mapping. The index is a + 2-tuple with components that are either integers or slices. + The subscripts may be read and assigned to. The type of the + subscripts is a string containing the binary data of the + requested region. Here is a description of the posible + operations:

pr[x, + y]

Get/Set the pixel at + (x,y)

pr[x1:x2, + y]

Get/Set the row starting at (x1, + y), width x2 - + x1.

pr[x, + y1:y2]

Get/Set the column starting at (x, + y1), height y2 - + y1.

pr[x1:x2, + y1:y1]

Get/Set the rectangle starting at (x1, + y1), width x2 - x1 + and height y2 - y1.


PrevHomeNext
Gimp Module Procedures Support Modules
\ No newline at end of file diff --git a/plug-ins/pygimp/doc/procedural-database.html b/plug-ins/pygimp/doc/procedural-database.html new file mode 100644 index 0000000000..64d039d37f --- /dev/null +++ b/plug-ins/pygimp/doc/procedural-database.html @@ -0,0 +1,361 @@ + +The Procedural Database
Gimp Python Documentation
PrevNext

The Procedural Database

The procedural database is a registry of things gimp and its + plugins can do. When you install a procedure for your plugin, you + are extending the procedural database.

The procedural database is self documenting, in that when + you install a procedure in it, you also add documentation for it, + its parameters and return values.

The Gimp-Python Model

In Gimp-Python, the procedural database is represented by + the object gimp.pdb. In most of my + plugins, I make an assignment from gimp.pdb + to pdb for convenience.

You can query the procedural database with + pdb's method query. Its + specification is:

pdb.query(name, [blurb, [help, [author, [copyright, [date, [type]]]]]])

Each parameter is a regular expression that is checked + against the corresponding field in the procedural database. The + method returns a list of the names of matching procedures. If + query is called without any arguments, it will + return every procedure in the database.

Procedural Database Procedures

Procedures can be accessed as procedures, or by treating + pdb as a mapping objest. As an example, + the probedure gimp_edit_fill can be + accessed as either pdb.gimp_edit_fill or + pdb['gimp_edit_fill']. The second form is + mainly for procedures whose names are not valid Python names (eg + in script-fu-..., the dashes are interpreted as minuses).

These procedure objects have a number of attribute:

proc_name

The name of the procedure.

proc_blurb

A short peice of information about the procedure.

proc_help

More detailed information about the procedure.

proc_author

The author of the procedure.

proc_copyright

The copyright holder for the procedure (usually the + same as the author).

proc_date

The date when the procedure was written.

proc_type

The type of procedure. This will be one of + PROC_PLUG_IN, PROC_EXTENSION or PROC_TEMPORARY.

nparams

The number of parameters the procedure takes.

nreturn_vals

The number of return values the procedure gives.

params

A description of parameters of the procedure. It + takes the form of a tuple of 3-tuples, where each 3-tuple + describes a parameter. The items in the 3-tuple are a + parameter type (one of the PARAM_* constants), a + name for the parameter, and a description of the + parameter.

return_vals

A description of the return values. It takes the + same form as the params + attribute.

A procedure object may also be called. At this point, + Gimp-Python doesn't support keyword arguments for PDB + procedures. Arguments are passed to the procedure in the normal + method. The return depends on the number of return values:

  • If there are zero return values, + None is returned.

  • If there is only a single return value, it is + returned.

  • If there are more return values, then they are + returned as a tuple.

More Information

For more information on invoking PDB procedures, please + see the example plugins. For information on individual + procedures, please see the PDB Browser plugin (in the Xtns + menu). It alows you to peruse to the database + interactively.


PrevHomeNext
The Structure Of A Plugin Gimp Module Procedures
\ No newline at end of file diff --git a/plug-ins/pygimp/doc/pygimp.html b/plug-ins/pygimp/doc/pygimp.html new file mode 100644 index 0000000000..994f6e0aa3 --- /dev/null +++ b/plug-ins/pygimp/doc/pygimp.html @@ -0,0 +1,232 @@ + +Gimp Python Documentation

Gimp Python Documentation

James Henstridge

          <james@daa.com.au>
+        

Published v0.4, 5 July 1999

This document outlines the interfaces to Gimp-Python, + which is a set of Python modules that act as a wrapper to + libgimp allowing the writing of + plug-ins for Gimp. In this way, Gimp-Python is similar to + Script-Fu, except that you can use the full set of Python + extension modules from the plug-in.


Introduction

What is it?

Gimp-Python is a scripting extension for Gimp, similar to + Script-Fu. The main difference is in what is called first. In + Script-Fu, the script-fu plugin executes the script, while in + Gimp-Python the script is in control.

In fact, you will find that the Gimp-Python scripts start + with the line #!/usr/bin/python. The + gimp extension is loaded with the familiar + import command.

Another point of difference between Gimp-Python and + Script-Fu is that Gimp-Python stores images, layers, channels + and other types as objects rather than just storing their ID. + This allows better type checking that is missing from Script-Fu, + and allows those types to act as objects, complete with + attributes and methods.

Also, Gimp-Python is not limited to just calling + procedures from the PDB. It also implements the rest of + libgimp, including tiles and pixel regions, + and access to other lower level functions.

Installation

Gimp-python consists of a Python module written in C and + some native python support modules. You can build pygimp with + the commands:

./configure
+make
+make install

This will build and install gimpmodule and its supporting + modules, and install the sample plugins in gimp's plugin + directory.


  Next
  The Structure Of A Plugin
\ No newline at end of file diff --git a/plug-ins/pygimp/doc/pygimp.sgml b/plug-ins/pygimp/doc/pygimp.sgml new file mode 100644 index 0000000000..3ba4c3a620 --- /dev/null +++ b/plug-ins/pygimp/doc/pygimp.sgml @@ -0,0 +1,2101 @@ + + +
+ + + + Gimp Python Documentation + + James + Henstridge + +
+ james@daa.com.au +
+
+
+ + v0.4, 5 July 1999 + + + + This document outlines the interfaces to Gimp-Python, + which is a set of Python modules that act as a wrapper to + libgimp allowing the writing of + plug-ins for Gimp. In this way, Gimp-Python is similar to + Script-Fu, except that you can use the full set of Python + extension modules from the plug-in. + + + +
+ + + Introduction + + + What is it? + + Gimp-Python is a scripting extension for Gimp, similar to + Script-Fu. The main difference is in what is called first. In + Script-Fu, the script-fu plugin executes the script, while in + Gimp-Python the script is in control. + + In fact, you will find that the Gimp-Python scripts start + with the line #!/usr/bin/python. The + gimp extension is loaded with the familiar + import command. + + Another point of difference between Gimp-Python and + Script-Fu is that Gimp-Python stores images, layers, channels + and other types as objects rather than just storing their ID. + This allows better type checking that is missing from Script-Fu, + and allows those types to act as objects, complete with + attributes and methods. + + Also, Gimp-Python is not limited to just calling + procedures from the PDB. It also implements the rest of + libgimp, including tiles and pixel regions, + and access to other lower level functions. + + + + + Installation + + Gimp-python consists of a Python module written in C and + some native python support modules. You can build pygimp with + the commands: + +./configure +make +make install + + + This will build and install gimpmodule and its supporting + modules, and install the sample plugins in gimp's plugin + directory. + + + + + + + The Structure Of A Plugin + + The majority of code in this package resides in + gimpmodule.c, but this provides a poor + interface for implementing some portions of a plugin. For this + reason, there is a python module called + plugin.py that sets out a structure for + plugins and implements some things that were either too dificult + or impossible to do in C. + + The main purpose of plugin.py was to + implement an object oriented structure for plug-ins. As well as + this, it handles tracebacks, which are otherwise ignored by + libgimp, and gives a method to call + other Gimp-Python plug-ins without going through the procedural + database. + + + An Example Plugin + + As in a lot of manuals, the first thing you examine is an + example, so here is an example. I have included it before + explaining what it does to allow more advanced programmers to + see the structure up front. It is a translation of the clothify + Script-Fu extension: + + +A sample python plugin + +#!/usr/bin/python +import math +from gimpfu import * + +have_gimp11 = gimp.major_version > 1 or \ + gimp.major_version == 1 and gimp.minor_version >= 1 + +def python_clothify(timg, tdrawable, bx=9, by=9, + azimuth=135, elevation=45, depth=3): + bx = 9 ; by = 9 ; azimuth = 135 ; elevation = 45 ; depth = 3 + width = tdrawable.width + height = tdrawable.height + img = gimp.image(width, height, RGB) + layer_one = gimp.layer(img, "X Dots", width, height, RGB_IMAGE, + 100, NORMAL_MODE) + img.disable_undo() + if have_gimp11: + pdb.gimp_edit_fill(layer_one) + else: + pdb.gimp_edit_fill(img, layer_one) + img.add_layer(layer_one, 0) + pdb.plug_in_noisify(img, layer_one, 0, 0.7, 0.7, 0.7, 0.7) + layer_two = layer_one.copy() + layer_two.mode = MULTIPLY_MODE + layer_two.name = "Y Dots" + img.add_layer(layer_two, 0) + pdb.plug_in_gauss_rle(img, layer_one, bx, 1, 0) + pdb.plug_in_gauss_rle(img, layer_two, by, 0, 1) + img.flatten() + bump_layer = img.active_layer + pdb.plug_in_c_astretch(img, bump_layer) + pdb.plug_in_noisify(img, bump_layer, 0, 0.2, 0.2, 0.2, 0.2) + pdb.plug_in_bump_map(img, tdrawable, bump_layer, azimuth, + elevation, depth, 0, 0, 0, 0, TRUE, FALSE, 0) + gimp.delete(img) + +register( + "python_fu_clothify", + "Make the specified layer look like it is printed on cloth", + "Make the specified layer look like it is printed on cloth", + "James Henstridge", + "James Henstridge", + "1997-1999", + "<Image>/Python-Fu/Alchemy/Clothify", + "RGB*, GRAY*", + [ + (PF_INT, "x_blur", "X Blur", 9), + (PF_INT, "y_blur", "Y Blur", 9), + (PF_INT, "azimuth", "Azimuth", 135), + (PF_INT, "elevation", "elevation", 45), + (PF_INT, "depth", "Depth", 3) + ], + [], + python_clothify) + +main() + + + + + + + Import Modules + + In this plugin, a number of modules are imported. The + important ones are: + + + + gimpfu: this module provides a + simple interface for writing plugins, similar to what + script-fu provides. It provides the GUI for entering in + parameters in interactive mode and performs some sanity + checks when registering the plugin. + + By using "from gimpfu import *", this module also + provides an easy way to get all the commonly used symbols + into the plugin's namespace. + + + gimp: the main part of the gimp + extension. This is imported with gimpfu. + + + gimpenums: a number of useful + constants. This is also automatically imported with + gimpfu. + + + + The pdb variable is a variable for accessing the + procedural database. It is imported into the plugin's namespace + with gimpfu for convenience. + + + + + Plugin Framework + + With pygimp-0.4, the gimpfu module was introduced. It + simplifies writing plugins a lot. It handles the run mode + (interactive, non interactive or run with last values), + providing a GUI for interactive mode and saving the last used + settings. + + Using the gimpfu plugin, all you need to do is write the + function that should be run, make a call to + register, and finally a call to + main to get the plugin started. + + If the plugin is to be run on an image, the first + parameter to the plugin function should be the image, and the + second should be the current drawable (do not worry about the + run_mode parameter). Plugins that do not act on an existing + image (and hence go in the toolbox's menus) do not need these + parameters. Any other parameters are specific to the + plugin. + + After defining the plugin function, you need to call + register to register the plugin with gimp + (When the plugin is run to query it, this information is passed + to gimp. When it is run interactively, this information is used + to construct the GUI). The parameters to + register are: + + name + blurb + help + author + copyright + date + menupath + imagetypes + params + results + function + + + Most of these parameters are quite self explanatory. The + menupath option should start with <Image%gt;/ for image + plugins and <Toolbox>/ for toolbox plugins. The remainder + of the menupath is a slash separated path to its menu item. + + The params parameter holds a list parameters for the + function. It is a list of tuples. Note that you do not have to + specify the run_type, image or drawable parameters, as gimpfu + will add these automatically for you. The tuple format is + (type, name, description, default [, extra]). The allowed type + codes are: + + PF_INT8 + PF_INT16 + PF_INT32 + PF_INT + PF_FLOAT + PF_STRING + PF_VALUE + PF_INT8ARRAY + PF_INT16ARRAY + PF_INT32ARRAY + PF_INTARRAY + PF_FLOATARRAY + PF_STRINGARRAY + PF_COLOR + PF_COLOUR + PF_REGION + PF_IMAGE + PF_LAYER + PF_CHANNEL + PF_DRAWABLE + PF_TOGGLE + PF_BOOL + PF_SLIDER + PF_SPINNER + PF_ADJUSTMENT + PF_FONT + PF_FILE + PF_BRUSH + PF_PATTERN + PF_GRADIENT + + + These values map onto the standard PARAM_* constants. The + reason to use the extra constants is that they give gimpfu more + information, so it can produce a better interface (for instance, + the PF_FONT type is equivalent to PARAM_STRING, but in the GUI + you get a small button that will bring up a font selection + dialog). + + The PF_SLIDER, PF_SPINNER and PF_ADJUSTMENT types require + the extra parameter. It is of the form (min, max, step), and + gives the limits for the spin button or slider. + + The results parameter is a list of 3-tuples of the form + (type, name, description). It defines the return values for the + function. If there is only a single return value, the plugin + function should return just that value. If there is more than + one, the plugin function should return a tuple of results. + + The final parameter to register is + the plugin function itself. + + After registering one or more plugin functions, you must + call the main function. This will cause + the plugin to start running. A GUI will be displayed when + needed, and your plugin function will be called at the + appropriate times. + + + + + + + The Procedural Database + + The procedural database is a registry of things gimp and its + plugins can do. When you install a procedure for your plugin, you + are extending the procedural database. + + The procedural database is self documenting, in that when + you install a procedure in it, you also add documentation for it, + its parameters and return values. + + + The Gimp-Python Model + + In Gimp-Python, the procedural database is represented by + the object gimp.pdb. In most of my + plugins, I make an assignment from gimp.pdb + to pdb for convenience. + + You can query the procedural database with + pdb's method query. Its + specification is: + + +pdb.query(name, [blurb, [help, [author, [copyright, [date, [type]]]]]]) + + + Each parameter is a regular expression that is checked + against the corresponding field in the procedural database. The + method returns a list of the names of matching procedures. If + query is called without any arguments, it will + return every procedure in the database. + + + + + Procedural Database Procedures + + Procedures can be accessed as procedures, or by treating + pdb as a mapping objest. As an example, + the probedure gimp_edit_fill can be + accessed as either pdb.gimp_edit_fill or + pdb['gimp_edit_fill']. The second form is + mainly for procedures whose names are not valid Python names (eg + in script-fu-..., the dashes are interpreted as minuses). + + These procedure objects have a number of attribute: + + + + proc_name + + The name of the procedure. + + + + proc_blurb + + A short peice of information about the procedure. + + + + proc_help + + More detailed information about the procedure. + + + + proc_author + + The author of the procedure. + + + + proc_copyright + + The copyright holder for the procedure (usually the + same as the author). + + + + proc_date + + The date when the procedure was written. + + + + proc_type + + The type of procedure. This will be one of + PROC_PLUG_IN, PROC_EXTENSION or PROC_TEMPORARY. + + + + nparams + + The number of parameters the procedure takes. + + + + nreturn_vals + + The number of return values the procedure gives. + + + + params + + A description of parameters of the procedure. It + takes the form of a tuple of 3-tuples, where each 3-tuple + describes a parameter. The items in the 3-tuple are a + parameter type (one of the PARAM_* constants), a + name for the parameter, and a description of the + parameter. + + + + return_vals + + A description of the return values. It takes the + same form as the params + attribute. + + + + + A procedure object may also be called. At this point, + Gimp-Python doesn't support keyword arguments for PDB + procedures. Arguments are passed to the procedure in the normal + method. The return depends on the number of return values: + + + + If there are zero return values, + None is returned. + + + If there is only a single return value, it is + returned. + + + If there are more return values, then they are + returned as a tuple. + + + + + + + More Information + + For more information on invoking PDB procedures, please + see the example plugins. For information on individual + procedures, please see the PDB Browser plugin (in the Xtns + menu). It alows you to peruse to the database + interactively. + + + + + + + Gimp Module Procedures + + The gimp module contains a number of + procedures and functions, as well as the definitions of many gimp + types such as images, and the procedural database. This section + explains the base level procedures. + + + Constructors and Object Deletion + + There are a number of functions in the + gimp module that are used to create the objects + used to make up an image in Gimp. Here is a set of descriptions + of these constructors: + + + + gimp.image(width, + height, + type) + + This procedure creates an image with the given + dimensions and type (type is one of + RGB, GRAY or + INDEXED). + + + + gimp.layer(img, + name, width, + height, type, + opacity, + mode) + + Create a new layer called + name, with the given dimensions and + type (one of the + *_IMAGE constants), + opacity (float between 0 and 100) and + a mode (one of the + *_MODE constants). The layer can + then be added to the image with the + img.add_layer method. + + + + gimp.channel(img, + name, width, + height, + opacity, + colour) + + Create a new channel object with the given + dimensions, opacity and + colour (one of the + *_CHANNEL constants). This channel can + then be added to an image. + + + + gimp.display(img) + + Create a new display window for the given image. + The window will not be displayed until a call to + gimp.displays_flush is made. + + + + gimp.parasite(name, flags, data) + + Create a new parasite. The parasite can then be + attached to gimp, an image or a drawable. This is only + available in gimp >= 1.1 + + + + + When any of these objects get removed from memory (such as + when their name goes out of range), the gimp thing it represents + does not get deleted with it (otherwise when your plugin + finished running, it would delete all its work). In order to + delete the thing the Python object represents, you should use + the gimp.delete procedure. It deletes the + gimp thing associated with the Python object given as a + parameter. If the object is not an image, layer, channel, + drawable or display gimp.delete does + nothing. + + + + + Configuration Information + + There are a number of functions that can be used to gather + information about the environment the plugin is running in: + + + + gimp.color_cube() or + gimp.colour_cube() + + Returns the current colour cube. + + + + gimp.gamma() + + Returns the current gamma correction. + + + + gimp.install_cmap() + + Returns non-zero if a colour map has been + installed. + + + + gimp.use_xshm() + + Returns non-zero if Gimp is using X shared + memory. + + + + gimp.gtkrc() + + Returns the file name of the GTK configuration + file. + + + + + + + + Palette Operations + + These functions alter the currently selected foreground + and background. + + + + gimp.get_background() + + Returns a 3-tuple containing the current background + colour in RGB form. + + + + gimp.get_foreground() + + Returns a 3-tuple containing the current foreground + colour in RGB form. + + + + gimp.set_background(r, + g, b) + + Sets the current background colour. The three + arguments can be replaced by a single 3-tuple like that + returned by gimp.get_background. + + + + gimp.set_foreground(r, + g, b) + + Sets the current foreground colour. Like + gimp.set_background, the arguments may + be replaced by a 3-tuple. + + + + + + + + Gradient Operations + + These functions perform operations on gradients: + + + + gimp.gradients_get_active() + + Returns the name of the active gradient. + + + + gimp.gradients_set_active(name) + + Sets the active gradient. + + + + gimp.gradients_get_list() + + Returns a list of the names of the available + gradients. + + + + gimp.gradients_sample_uniform(num) + + Returns a list of num + samples, where samples consist of 4-tuples of floats + representing the red, green, blue and alpha values for the + sample. + + + + gimp.gradients_sample_custom(pos) + + Similar to + gimp.gradients_sample_uniform, except + the samples are taken at the positions given in the list + of floats pos instead of uniformly + through the gradient. + + + + + + + + PDB Registration Functions + + These functions either install procedures into the PDB or + alert gimp to their special use (eg as file handlers). + + For simple plugins, you will usually only need to use + register from gimpfu. + + + + gimp.install_procedure(name, + blurb, help, + author, + copyright, + date, + menu_path, + image_types, + type, params, + ret_vals) + + This procedure is used to install a procedure into + the PDB. The first eight parameters are strings, + type is a one of the + PROC_* constants, and the last two + parameters are sequences describing the parameters and + return values. Their format is the same as the param and + ret_vals methods or PDB procedures. + + + + gimp.install_temp_proc(name, + blurb, help, + author, + copyright, + date, + menu_path, + image_types, type, + params, ret_vals) + + This procedure is used to install a procedure into + the PDB temporarily. That is, it must be added again + every time gimp is run. This procedure will be called the + same way as all other procedures for a plugin. + + + + gimp.uninstall_temp_proc(name) + + This removes a temporary procedure from the + PDB. + + + + gimp.register_magic_load_handler(name, + extensions, + prefixes, + magics) + + This procedure tells Gimp that the PDB procedure + name can load files with + extensions and + prefixes (eg http:) with magic + information magics. + + + + gimp.register_load_handler(name, + extensions, + prefixes) + + This procedure tells Gimp that the PDB procedure + name can load files with + extensions and + prefixes (eg http:). + + + + gimp.register_save_handler(name, + extensions, + prefixes) + + This procedure tells Gimp that the PDB procedure + name can save files with + extensions and + prefixes (eg http:). + + + + + + + + Other Functions + + These are the other functions in the + gimp module. + + + + gimp.main(init_func, + quit_func, + query_func, + run_func) + + This function is the one that controls the execution + of a Gimp-Python plugin. It is better to not use this + directly but rather subclass the plugin class, defined in + the . + + + + gimp.pdb + + The procedural database object. + + + + gimp.progress_init([label]) + + (Re)Initialise the progress meter with + label (or the plugin name) as a + label in the window. + + + + gimp.progress_update(percnt) + + Set the progress meter to + percnt done. + + + + gimp.query_images() + + Returns a list of all the image objects. + + + + gimp.quit() + + Stops execution imediately and exits. + + + + gimp.displays_flush() + + Update all the display windows. + + + + gimp.tile_width() + + The maximum width of a tile. + + + + gimp.tile_height() + + The maximum height of a tile. + + + + gimp.tile_cache_size(kb) + + Set the size of the tile cache in kilobytes. + + + + gimp.tile_cache_ntiles(n) + + Set the size of the tile cache in tiles. + + + + gimp.get_data(key) + + Get the information associated with + key. The data will be a string. + This function should probably be used through the . + + + + gimp.set_data(key, + data) + + Set the information in the string + data with + key. The data will persist for the + whole gimp session. Rather than directly accessing this + function, it is better to go through the . + + + + gimp.extension_ack() + + Tells gimp that the plugin has finished its work, + while keeping the plugin connection open. This is used by + an extension plugin to tell gimp it can continue, while + leaving the plugin connection open. This is what the + script-fu plugin does so that only one scheme interpretter + is needed. + + + + gimp.extension_process(timeout) + + Makes the plugin check for messages from gimp. + generally this is not needed, as messages are checked + during most calls in the gimp module. + + + + + + + + Parasites + + In gimp >= 1.1, it is possible to attach arbitrary data to + an image through the use of parasites. Parasites are simply + wrappers for the data, containing its name and some flags. + Parasites have the following parameters: + + + data + + The data for the parasite -- a string + + + + flags + + The flags for the parasite + + + + is_persistent + + True if this parasite is persistent + + + + is_undoable + + True if this parasite is undoable + + + + name + + The name of the parasite + + + + + Parasites also have the methods copy, + is_type and + has_flag. + + There is a set of four functions that are used to + manipulate parasites. They exist as functions in the + gimp module, and methods for image and + drawable objects. They are: + + + find_parasite(name) + + find a parasite by its name. + + + + attach_parasite(parasite) + + Attach a parasite to this object. + + + + attach_new_parasite(name, flags, data) + + Create a new parasite and attach it. + + + + detach_parasite(name) + + Detach the named parasite + + + + + + + + + + Gimp Objects + + Gimp-Python implements a number of special object types that + represent the different types of parameters you can pass to a PDB + procedure. Rather than just making these place holders, I have + added a number of members and methods to them that allow a lot of + configurability without directly calling PDB procedures. + + There are also a couple of extra objects that allow low + level manipulation of images. These are tile objects (working) + and pixel regions (not quite finished). + + + Image Object + + This is the object that represents an open image. In this + section, image represents a generic + image object. + + + Image Members + + + + + + image.active_channel + + This is the active channel of the image. You can + also assign to this member, or + None if there is no active + channel. + + + + image.active_layer + + This is the active layer of the image. You can + also assign to this member, or + None if there is no active + layer. + + + + image.base_type + + This is the type of the image (eg RGB, INDEXED). + + + + image.channels + + This is a list of the channels of the image. + Altering this list has no effect, and you can not assign + to this member. + + + + image.cmap + + This is the colour map for the image. + + + + image.filename + + This is the filename for the image. A file load + or save handler might assign to this. + + + + image.height + + This is the height of the image. You can't assign + to this member. + + + + image.floating_selection + + The floating selection layer, or + None if there is no floating + selection. + + + + image.layers + + This is a list of the layers of the image. + + + + image.selection + + The selection mask for the image. + + + + image.width + + This is the width of the image. You can't assign + to this member. + + + + + + + + Image Methods + + + + + + image.add_channel(channel, + position) + + Adds channel to + image in position + position. + + + + image.add_layer(layer, + position) + + Adds layer to + image in position + position. + + + + image.add_layer_mask(layer, + mask) + + Adds the mask mask to + layer. + + + + image.clean_all() + + Unsets the dirty flag on the image. + + + + image.disable_undo() + + Disables undo for + image. + + + + image.enable_undo() + + Enables undo for image. + You might use these commands round a plugin, so that the + plugin's actions can be undone in a single step. + + + + image.flatten() + + Returns the resulting layer after merging all the + visible layers, discarding non visible ones and + stripping the alpha channel. + + + + image.get_component_active(component) + + Returns true if component + (one of the *_CHANNEL constants) is + active. + + + + image.get_component_visible(component) + + Returns true if component + is visible. + + + + image.set_component_active(component, + active) + + Sets the activeness of + component. + + + + image.set_component_visible(component, + active) + + Sets the visibility of + component. + + + + image.lower_channel(channel) + + Lowers channel. + + + + image.lower_layer(layer) + + Lowers layer. + + + + image.merge_visible_layers(type) + + Merges the visible layers of + image using the given merge + type. + + + + image.pick_correlate_layer(x, + y) + + Returns the layer that is visible at the point + (x,y), or + None if no layer matches. + + + +image.raise_channel(channel) + + Raises channel. + + + + image.raise_layer(layer) + + Raises layer. + + + + image.remove_channel(channel) + + Removes channel from + image. + + + + image.remove_layer(layer) + + Removes layer from + image. + + + + image.remove_layer_mask(layer, + mode) + + Removes the mask from + layer, with the given + mode (either APPLY or + DISCARD). + + + + image.resize(width, + height, x, + y) + + Resizes the image to size (width, + height) and places the old contents at + position (x,y). + + + + + + + + + + Channel Objects + + These objects represent a Gimp Image's colour channels. + In this section, channel will refer + to a generic channel object. + + + Channel Members + + + + + + channel.colour + or + channel.color + + The colour of the channel. + + + + channel.height + + The height of the channel. + + + + channel.width + + The width of the channel. + + + + channel.image + + The image the channel belongs to, or + None if it isn't attached + yet. + + + + channel.layer + + The channel's layer (??) or + None if one doesn't exist. + + + + channel.layer_mask + + Non zero if the channel is a layer mask. + + + + channel.name + + The name of the channel. + + + + channel.opacity + + The opacity of the channel. + + + + channel.show_masked + + The show_masked value of the channel. + + + + channel.visible + + Non-zero if the channel is visible. + + + + + + + + Channel Methods + + + + + + channel.copy() + + returns a copy of the channel. + + + + + + + + + + Layer Objects + + Layer objects represent the layers of a Gimp image. In + this section I will refer to a generic layer called + layer. + + + Layer Members + + + + + + layer.apply_mask + + The apply mask setting. (non zero if the layer + mask is being composited with the layer's alpha + channel). + + + + layer.bpp + + The number of bytes per pixel. + + + + layer.edit_mask + + The edit mask setting. (non zero if the mask is + active, rather than the layer). + + + + layer.height + + The height of the layer. + + + + layer.image + + The image the layer is part of, or + None if the layer isn't + attached. + + + + layer.is_floating_selection + + Non zero if this layer is the image's floating + selection. + + + + layer.mask + + The layer's mask, or None + if it doesn't have one. + + + + layer.mode + + The mode of the layer. + + + + layer.name + + The name of the layer. + + + + layer.opacity + + The opacity of the layer. + + + + layer.preserve_transparency + + The layer's preserve transparency setting. + + + + + + + + Layer Methods + + + + + + layer.add_alpha() + + Adds an alpha component to the layer. + + + + layer.copy([alpha]) + + Creates a copy of the layer, optionally with an + alpha layer. + + + + layer.create_mask(type) + + Creates a layer mask of type + type. + + + + layer.resize(w, + h, x, + y) + + Resizes the layer to (w, + h), positioning the original contents at + (x,y). + + + + layer.scale(h, + w, + origin) + + Scales the layer to (w, h), + using the specified origin (local + or image). + + + + layer.set_offsets(x, + y) + + Sets the offset of the layer, relative to the + image's origin + + + + layer.translate(x, + y) + + Moves the layer to (x, y) + relative to its current position. + + + + + + + + + + Drawable Objects + + Both layers and channels are drawables. Hence there are a + number of operations that can be performed on both objects. + They also have some common attributes and methods. In the + description of these attributes, I will refer to a generic + drawable called drawable. + + + Drawable Members + + + + + + drawable.bpp + + The number of bytes per pixel. + + + + drawable.is_colour + or + drawable.is_color + + Non zero if the drawable is colour. + + + + drawable.is_grey + or + drawable.is_gray + + Non zero if the drawable is greyscale. + + + + drawable.has_alpha + + Non zero if the drawable has an alpha channel. + + + + drawable.height + + The height of the drawable. + + + + drawable.image + + The image the drawable belongs to. + + + + drawable.is_indexed + + Non zero if the drawable uses an indexed colour + scheme. + + + + drawable.mask_bounds + + The bounds of the drawable's selection. + + + + drawable.name + + The name of the drawable. + + + + drawable.offsets + + The offset of the top left hand corner of the + drawable. + + + + drawable.type + + The type of the drawable. + + + + drawable.visible + + Non zero if the drawable is visible. + + + + drawable.width + + The width of the drawable. + + + + + + + + Drawable Methods + + + + + + drawable.fill(fill_type) + + Fills the drawable with given + fill_type (one of the + *_FILL constants). + + + + drawable.flush() + + Flush the changes to the drawable. + + + + drawable.get_pixel_rgn(x, + y, w, + h, [dirty, + [shadow]) + + Creates a pixel region for the drawable. It will + cover the region with origin + (x,y) and dimensions w + x h. The dirty + argument sets whether any changes to the pixel region + will be reflected in the drawable (default is TRUE). + The shadow argument sets whether + the pixel region acts on the shadow tiles or not + (default is FALSE). If you draw on the shadow tiles, + you must call + drawable.merge_shadow() + for changes to take effect. + + + + drawable.get_tile(shadow, + row, + col) + + Get a tile at (row, + col). Either on or off the + shadow buffer. + + + + drawable.get_tile2(shadow, + x, y) + + Get the tile that contains the pixel + (x, y). + + + + drawable.merge_shadow() + + Merge the shadow buffer back into the + drawable. + + + + drawable.update(x, + y, w, + h) + + Update the given portion of the drawable. + + + + + + + + + + Tile Objects + + Tile objects represent the way Gimp stores information. A + tile is basically just a 64x64 pixel region of the drawable. + The reason Gimp breaks the image into small pieces like this is + so that the whole image doesn't have to be loaded into memory in + order to alter one part of it. This becomes important with + larger images. + + In Gimp-Python, you would use Tiles if you wanted to + perform some low level operation on the image, instead of using + procedures in the PDB. This type of object gives a Gimp-Python + plugin the power of a C plugin, rather than just the power of a + Script-Fu script. Tile objects are created with either the + drawable.get_tile() + or + drawable.get_tile2() + functions. In this section, I will refer to a generic tile + object named tile. + + + Tile Members + + All tile members are read only. + + + + tile.bpp + + The number of bytes per pixel. + + + + tile.dirty + + If there have been changes to the tile since it + was last flushed. + + + + tile.drawable + + The drawable that the tile is from. + + + + tile.eheight + + The actual height of the tile. + + + + tile.ewidth + + The actual width of the tile. + + + + tile.ref_count + + The reference count of the tile. (this is + independent of the Python object reference + count). + + + + tile.shadow + + Non zero if the tile is part of the shadow + buffer. + + + + + + + + Tile Methods + + + + + + tile.flush() + + Flush any changes in the tile. Note that the tile + is automatically flushed when the Python object is + deleted from memory. + + + + + + + + Tile Mapping Behaviour + + Tile objects also act as a mapping, or sequence. You + can access the pixels in the tile in one of two ways. You can + either access them with a single number, which refers to its + position in the tile + (eg. tile[64] + refers to the first pixel in the second row of a 64x64 pixel + tile). The other way is with a tuple, representing the + coordinates on the tile + (eg. tile[0, 1] + refers to the first pixel on the second row of the + tile). + + The type of these subscripts is a string of length + tile.bpp. + When you assign to a subscript, the dirty flag is + automatically set on the tile, so you don't have to explicitly + set the flag, or flush the tile. + + + + + + + Pixel Regions + + Pixel region objects give an interface for low level + operations to act on large regions of an image, instead of on + small 64x64 pixel tiles. In this section I will refer to a + generic pixel region called pr. For + an example of a pixel region's use, please see the example + plugin whirlpinch.py. + + + Pixel Region Members + + + + + + pr.drawable + + The drawable this pixel region is for. + + + + pr.bpp + + The number of bytes per pixel for the drawable. + + + + pr.rowstride + + The rowstride for the pixel region. + + + + pr.x + + The x coordinate of the top left hand corner. + + + + pr.y + + The y coordinate of the top left hand corner. + + + + pr.w + + The width of the pixel region. + + + + pr.h + + The height of the pixel region. + + + + pr.dirty + + Non zero if changes to the pixel region will be + reflected in the drawable. + + + + pr.shadow + + Non zero if the pixel region acts on the shadow + tiles of the drawable. + + + + + + + + Pixel Region Methods + + + + + + pr.resize(x, + y, w, + h) + + resize the pixel region so that it operates on the + the region with corner (x, y) + with dimensions w x h. + + + + + + + + Pixel Region Mapping Behaviour + + The pixel region acts as a mapping. The index is a + 2-tuple with components that are either integers or slices. + The subscripts may be read and assigned to. The type of the + subscripts is a string containing the binary data of the + requested region. Here is a description of the posible + operations: + + + + pr[x, + y] + + Get/Set the pixel at + (x,y) + + + + pr[x1:x2, + y] + + Get/Set the row starting at (x1, + y), width x2 - + x1. + + + + pr[x, + y1:y2] + + Get/Set the column starting at (x, + y1), height y2 - + y1. + + + + pr[x1:x2, + y1:y1] + + Get/Set the rectangle starting at (x1, + y1), width x2 - x1 + and height y2 - y1. + + + + + + + + + + + + Support Modules + + This section describes the modules that help make using the + gimp module easier. These range from a set + of constants to storing persistent data. + + + The gimpenums Module + + This module contains all the constants found in the header + libgimp/gimpenums.h, as well as some extra + constants that are available in Script-Fu. + + + + + The gimpfu Module + + This module was fully described in an earlier section. It + provides an easy interface for writing plugins, where you do not + need to worry about run_modes, GUI's and saving previous values. + It is the recommended module for writing plugins. + + + + + The gimpplugin Module + + This module provides the framework for writing Gimp + plugins in Python. It gives more flexibility for writing + plugins than the gimpfu module, but does not offer as many + features (such as automatic GUI building). + + To use this framework you subclass + gimpplugin.plugin like so: + + +import gimpplugin +class myplugin(gimpplugin.plugin): + def init(self): + # initialisation routines + # called when gimp starts. + def quit(self): + # clean up routines + # called when gimp exits (normally). + def query(self): + # called to find what functionality the plugin provides. + gimp.install_procedure("procname", ...) + # note that this method name matches the first arg of + # gimp.install_procedure + def procname(self, arg1, ...): + # do what ever this plugin should do + + + + + + The gimpshelf Module + + This module gives a nicer interface to the persistent + storage interface for Gimp plugins. Due to the complicated + nature of Python objects (there is often a lot of connections + between them), it can be dificult to work out what to store in + gimp's persistent storage. The python interface only allows + storage of strings, so this module wraps pickle and unpickle to + allow persistentstorage of any python object. + + Here is some examples of using this module: + + +>>> from gimpshelf import shelf +>>> shelf['james'] = ['forty-two', (42, 42L, 42.0)] +>>> shelf.has_key('james') +1 +>>> shelf['james'] +['forty-two', (42, 42L, 42.0)] + + + Anything you store with + gimpshelf.shelf will exist until Gimp + exits. This makes this interface perfect for when a plugin is + executed with the run mode + RUN_WITH_LAST_VALS. + + + + + + + End Note + + This package is not yet complete, but it has enough in it to + be useful for writing plugins for Gimp. If you write any plugins + that might be useful as examples, please mail me at james@daa.com.au. + + + +
diff --git a/plug-ins/pygimp/doc/structure-of-plugin.html b/plug-ins/pygimp/doc/structure-of-plugin.html new file mode 100644 index 0000000000..2931ce9b3d --- /dev/null +++ b/plug-ins/pygimp/doc/structure-of-plugin.html @@ -0,0 +1,573 @@ + +The Structure Of A Plugin
Gimp Python Documentation
PrevNext

The Structure Of A Plugin

The majority of code in this package resides in + gimpmodule.c, but this provides a poor + interface for implementing some portions of a plugin. For this + reason, there is a python module called + plugin.py that sets out a structure for + plugins and implements some things that were either too dificult + or impossible to do in C.

The main purpose of plugin.py was to + implement an object oriented structure for plug-ins. As well as + this, it handles tracebacks, which are otherwise ignored by + libgimp, and gives a method to call + other Gimp-Python plug-ins without going through the procedural + database.

An Example Plugin

As in a lot of manuals, the first thing you examine is an + example, so here is an example. I have included it before + explaining what it does to allow more advanced programmers to + see the structure up front. It is a translation of the clothify + Script-Fu extension:

Example 1. A sample python plugin

#!/usr/bin/python
+import math
+from gimpfu import *
+
+have_gimp11 = gimp.major_version > 1 or \
+	      gimp.major_version == 1 and gimp.minor_version >= 1
+
+def python_clothify(timg, tdrawable, bx=9, by=9,
+		    azimuth=135, elevation=45, depth=3):
+	bx = 9 ; by = 9 ; azimuth = 135 ; elevation = 45 ; depth = 3
+	width = tdrawable.width
+	height = tdrawable.height
+	img = gimp.image(width, height, RGB)
+	layer_one = gimp.layer(img, "X Dots", width, height, RGB_IMAGE,
+			       100, NORMAL_MODE)
+	img.disable_undo()
+	if have_gimp11:
+		pdb.gimp_edit_fill(layer_one)
+	else:
+		pdb.gimp_edit_fill(img, layer_one)
+	img.add_layer(layer_one, 0)
+	pdb.plug_in_noisify(img, layer_one, 0, 0.7, 0.7, 0.7, 0.7)
+	layer_two = layer_one.copy()
+	layer_two.mode = MULTIPLY_MODE
+	layer_two.name = "Y Dots"
+	img.add_layer(layer_two, 0)
+	pdb.plug_in_gauss_rle(img, layer_one, bx, 1, 0)
+	pdb.plug_in_gauss_rle(img, layer_two, by, 0, 1)
+	img.flatten()
+	bump_layer = img.active_layer
+	pdb.plug_in_c_astretch(img, bump_layer)
+	pdb.plug_in_noisify(img, bump_layer, 0, 0.2, 0.2, 0.2, 0.2)
+	pdb.plug_in_bump_map(img, tdrawable, bump_layer, azimuth,
+			     elevation, depth, 0, 0, 0, 0, TRUE, FALSE, 0)
+	gimp.delete(img)
+
+register(
+	"python_fu_clothify",
+	"Make the specified layer look like it is printed on cloth",
+	"Make the specified layer look like it is printed on cloth",
+	"James Henstridge",
+	"James Henstridge",
+	"1997-1999",
+	"<Image>/Python-Fu/Alchemy/Clothify",
+	"RGB*, GRAY*",
+	[
+		(PF_INT, "x_blur", "X Blur", 9),
+		(PF_INT, "y_blur", "Y Blur", 9),
+		(PF_INT, "azimuth", "Azimuth", 135),
+		(PF_INT, "elevation", "elevation", 45),
+		(PF_INT, "depth", "Depth", 3)
+	],
+	[],
+	python_clothify)
+
+main()

Import Modules

In this plugin, a number of modules are imported. The + important ones are:

  • gimpfu: this module provides a + simple interface for writing plugins, similar to what + script-fu provides. It provides the GUI for entering in + parameters in interactive mode and performs some sanity + checks when registering the plugin.

    By using "from gimpfu import *", this module also + provides an easy way to get all the commonly used symbols + into the plugin's namespace.

  • gimp: the main part of the gimp + extension. This is imported with gimpfu.

  • gimpenums: a number of useful + constants. This is also automatically imported with + gimpfu.

The pdb variable is a variable for accessing the + procedural database. It is imported into the plugin's namespace + with gimpfu for convenience.

Plugin Framework

With pygimp-0.4, the gimpfu module was introduced. It + simplifies writing plugins a lot. It handles the run mode + (interactive, non interactive or run with last values), + providing a GUI for interactive mode and saving the last used + settings.

Using the gimpfu plugin, all you need to do is write the + function that should be run, make a call to + register, and finally a call to + main to get the plugin started.

If the plugin is to be run on an image, the first + parameter to the plugin function should be the image, and the + second should be the current drawable (do not worry about the + run_mode parameter). Plugins that do not act on an existing + image (and hence go in the toolbox's menus) do not need these + parameters. Any other parameters are specific to the + plugin.

After defining the plugin function, you need to call + register to register the plugin with gimp + (When the plugin is run to query it, this information is passed + to gimp. When it is run interactively, this information is used + to construct the GUI). The parameters to + register are:

name
blurb
help
author
copyright
date
menupath
imagetypes
params
results
function

Most of these parameters are quite self explanatory. The + menupath option should start with <Image%gt;/ for image + plugins and <Toolbox>/ for toolbox plugins. The remainder + of the menupath is a slash separated path to its menu item.

The params parameter holds a list parameters for the + function. It is a list of tuples. Note that you do not have to + specify the run_type, image or drawable parameters, as gimpfu + will add these automatically for you. The tuple format is + (type, name, description, default [, extra]). The allowed type + codes are:

PF_INT8
PF_INT16
PF_INT32
PF_INT
PF_FLOAT
PF_STRING
PF_VALUE
PF_INT8ARRAY
PF_INT16ARRAY
PF_INT32ARRAY
PF_INTARRAY
PF_FLOATARRAY
PF_STRINGARRAY
PF_COLOR
PF_COLOUR
PF_REGION
PF_IMAGE
PF_LAYER
PF_CHANNEL
PF_DRAWABLE
PF_TOGGLE
PF_BOOL
PF_SLIDER
PF_SPINNER
PF_ADJUSTMENT
PF_FONT
PF_FILE
PF_BRUSH
PF_PATTERN
PF_GRADIENT

These values map onto the standard PARAM_* constants. The + reason to use the extra constants is that they give gimpfu more + information, so it can produce a better interface (for instance, + the PF_FONT type is equivalent to PARAM_STRING, but in the GUI + you get a small button that will bring up a font selection + dialog).

The PF_SLIDER, PF_SPINNER and PF_ADJUSTMENT types require + the extra parameter. It is of the form (min, max, step), and + gives the limits for the spin button or slider.

The results parameter is a list of 3-tuples of the form + (type, name, description). It defines the return values for the + function. If there is only a single return value, the plugin + function should return just that value. If there is more than + one, the plugin function should return a tuple of results.

The final parameter to register is + the plugin function itself.

After registering one or more plugin functions, you must + call the main function. This will cause + the plugin to start running. A GUI will be displayed when + needed, and your plugin function will be called at the + appropriate times.


PrevHomeNext
Gimp Python Documentation The Procedural Database
\ No newline at end of file diff --git a/plug-ins/pygimp/doc/support-modules.html b/plug-ins/pygimp/doc/support-modules.html new file mode 100644 index 0000000000..10a70dc5bb --- /dev/null +++ b/plug-ins/pygimp/doc/support-modules.html @@ -0,0 +1,259 @@ + +Support Modules
Gimp Python Documentation
PrevNext

Support Modules

This section describes the modules that help make using the + gimp module easier. These range from a set + of constants to storing persistent data.

The gimpenums Module

This module contains all the constants found in the header + libgimp/gimpenums.h, as well as some extra + constants that are available in Script-Fu.

The gimpfu Module

This module was fully described in an earlier section. It + provides an easy interface for writing plugins, where you do not + need to worry about run_modes, GUI's and saving previous values. + It is the recommended module for writing plugins.

The gimpplugin Module

This module provides the framework for writing Gimp + plugins in Python. It gives more flexibility for writing + plugins than the gimpfu module, but does not offer as many + features (such as automatic GUI building).

To use this framework you subclass + gimpplugin.plugin like so:

import gimpplugin
+class myplugin(gimpplugin.plugin):
+	def init(self):
+		# initialisation routines
+		# called when gimp starts.
+	def quit(self):
+		# clean up routines
+		# called when gimp exits (normally).
+	def query(self):
+		# called to find what functionality the plugin provides.
+		gimp.install_procedure("procname", ...)
+	# note that this method name matches the first arg of
+	# gimp.install_procedure
+	def procname(self, arg1, ...):
+		# do what ever this plugin should do

The gimpshelf Module

This module gives a nicer interface to the persistent + storage interface for Gimp plugins. Due to the complicated + nature of Python objects (there is often a lot of connections + between them), it can be dificult to work out what to store in + gimp's persistent storage. The python interface only allows + storage of strings, so this module wraps pickle and unpickle to + allow persistentstorage of any python object.

Here is some examples of using this module:

>>> from gimpshelf import shelf
+>>> shelf['james'] = ['forty-two', (42, 42L, 42.0)]
+>>> shelf.has_key('james')
+1
+>>> shelf['james']
+['forty-two', (42, 42L, 42.0)]

Anything you store with + gimpshelf.shelf will exist until Gimp + exits. This makes this interface perfect for when a plugin is + executed with the run mode + RUN_WITH_LAST_VALS.


PrevHomeNext
Gimp Objects End Note
\ No newline at end of file diff --git a/plug-ins/pygimp/gimpenums.py b/plug-ins/pygimp/gimpenums.py new file mode 100644 index 0000000000..d91e6e8659 --- /dev/null +++ b/plug-ins/pygimp/gimpenums.py @@ -0,0 +1,185 @@ +# Gimp-Python - allows the writing of Gimp plugins in Python. +# Copyright (C) 1997 James Henstridge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# gimpenums.py -- constants for use with the gimp module +# +# this file contains constants that are useful for use in +# gimp plugins. Just add 'from gimpenums import *' to the top +# of the script + +RGB = 0 +GRAY = 1 +INDEXED = 2 + +RGB_IMAGE = 0 +RGBA_IMAGE = 1 +GRAY_IMAGE = 2 +GRAYA_IMAGE = 3 +INDEXED_IMAGE = 4 +INDEXEDA_IMAGE = 5 + +NORMAL_MODE = 0 +DISSOLVE_MODE = 1 +MULTIPLY_MODE = 3 +SCREEN_MODE = 4 +OVERLAY_MODE = 5 +DIFFERENCE_MODE = 6 +ADDITION_MODE = 7 +SUBTACT_MODE = 8 +DARKEN_ONLY_MODE = 9 +LIGHTEN_ONLY_MODE = 10 +HUE_MODE = 11 +SATURATION_MODE = 12 +COLOR_MODE = 13 +VALUE_MODE = 14 + +BG_IMAGE_FILL = 0 +WHITE_IMAGE_FILL = 1 +TRANS_IMAGE_FILL = 2 + +PARAM_INT32 = 0 +PARAM_INT16 = 1 +PARAM_INT8 = 2 +PARAM_FLOAT = 3 +PARAM_STRING = 4 +PARAM_INT32ARRAY = 5 +PARAM_INT16ARRAY = 6 +PARAM_INT8ARRAY = 7 +PARAM_FLOATARRAY = 8 +PARAM_STRINGARRAY = 9 +PARAM_COLOR = 10 +PARAM_REGION = 11 +PARAM_DISPLAY = 12 +PARAM_IMAGE = 13 +PARAM_LAYER = 14 +PARAM_CHANNEL = 15 +PARAM_DRAWABLE = 16 +PARAM_SELECTION = 17 +PARAM_BOUNDARY = 18 +PARAM_PATH = 19 +PARAM_STATUS = 20 +PARAM_END = 21 + +PROC_PLUG_IN = 1 +PROC_EXTENSION = 2 +PROC_TEMPORARY = 3 + +RUN_INTERACTIVE = 0 +RUN_NONINTERACTIVE = 1 +RUN_WITH_LAST_VALS = 2 + +STATUS_EXECUTION_ERROR = 0 +STATUS_CALLING_ERROR = 1 +STATUS_PASS_THROUGH = 2 +STATUS_SUCCESS = 3 + +# extra constants +NORMAL = 0 +DISSOLVE = 1 +BEHIND = 2 +MULTIPLY = 3 +SCREEN = 4 +OVERLAY = 5 +DIFFERENCE = 6 +ADDITION = 7 +SUBTRACTION = 8 +DARKEN_ONLY = 9 +LIGHTEN_ONLY = 10 +HUE = 11 +SATURATION = 12 +COLOR = 13 +COLOUR = 13 +VALUE = 14 + +FG_BG_RGB = 0 +FG_BG_HSV = 1 +FG_TRANS = 2 +CUSTOM = 3 + +LINEAR = 0 +BILINEAR = 1 +RADIAL = 2 +SQUARE = 3 +CONICAL_SYMMETRIC = 4 +CONNUCAL_ASYMMETRIC = 5 +SHAPEBURST_ANGULAR = 6 +SHAPEBURST_SPHERICAL = 7 +SHAPEBURST_DIMPLED = 8 + +REPEAT_NONE = 0 +REPEAT_SAWTOOTH = 1 +REPEAT_TRIANGULAR = 2 + +FG_BUCKET_FILL = 0 +BG_BUCKET_FILL = 1 +PATTERN_BUCKET_FILL = 2 + +BG_IMAGE_FILL = 0 +WHITE_IMAGE_FILL = 1 +TRANS_IMAGE_FILL = 2 +NO_IMAGE_FILL = 3 + +RED_CHANNEL = 0 +GREEN_CHANNEL = 1 +BLUE_CHANNEL = 2 +GRAY_CHANNEL = 3 +GREY_CHANNEL = 3 +INDEXED_CHANNEL = 4 + +WHITE_MASK = 0 +BLACK_MASK = 1 +ALPHA_MASK = 2 + +APPLY = 0 +DISCARD = 1 + +EXPAND_AS_NECESSARY = 0 +CLIP_TO_IMAGE = 1 +CLIP_TO_BOTTOM_LAYER = 2 + +ADD = 0 +SUB = 1 +REPLACE = 2 +INTERSECT = 3 + +PIXELS = 0 +POINTS = 1 + +IMAGE_CLONE = 0 +PATTERN_CLONE = 1 + +BLUR = 0 +SHARPEN = 1 + +TRUE = 1 +FALSE = 0 + +import gimp +if gimp.major_version > 1 or gimp.major_version==1 and gimp.minor_version>=1: + PARASITE_PERSISTENT = 1 + PARASITE_UNDOABLE = 2 + PARASITE_ATTACH_PARENT = 0x80 << 8 + PARASITE_PARENT_PERSISTENT = PARASITE_PERSISTENT << 8 + PARASITE_PARENT_UNDOABLE = PARASITE_UNDOABLE << 8 + PARASITE_ATTACH_GRANDPARENT = 0x80 << 16 + PARASITE_GRANDPARENT_PERSISTENT = PARASITE_PERSISTENT << 16 + PARASITE_GRANDPARENT_UNDOABLE = PARASITE_UNDOABLE << 16 + + ORIENTATION_UNKNOWN = 0 + ORIENTATION_HORIZONTAL = 1 + ORIENTATION_VERTICAL = 2 +del gimp diff --git a/plug-ins/pygimp/gimpfu.py b/plug-ins/pygimp/gimpfu.py new file mode 100644 index 0000000000..915738d4e8 --- /dev/null +++ b/plug-ins/pygimp/gimpfu.py @@ -0,0 +1,495 @@ +'''Simple interface to writing GIMP plugins in python. + +Instead of worrying about all the user interaction, saving last used values +and everything, the gimpfu module can take care of it for you. It provides +a simple register() function that will register your plugin if needed, and +cause your plugin function to be called when needed. + +Gimpfu will also handle showing a user interface for editing plugin parameters +if the plugin is called interactively, and will also save the last used +parameters, so the RUN_WITH_LAST_VALUES run_type will work correctly. It +will also make sure that the displays are flushed on completion if the plugin +was run interactively. + +When registering the plugin, you do not need to worry about specifying +the run_type parameter. And if the plugin is an image plugin (the menu +path starts with /), the image and drawable parameters are also +automatically added. + +A typical gimpfu plugin would look like this: + from gimpfu import * + + def plugin_func(image, drawable, args): + #do what plugins do best + register( + "plugin_func", + "blurb", + "help message", + "author", + "copyright", + "year", + "/Somewhere/My plugin", + "*", + [(PF_STRING, "arg", "The argument", "default-value")], + [], + plugin_func) + main() + +The call to "from gimpfu import *" will import all the gimp constants into +the plugin namespace, and also import the symbols gimp, pdb, register and +main. This should be just about all any plugin needs. You can use any +of the PF_* constants below as parameter types, and an appropriate user +interface element will be displayed when the plugin is run in interactive +mode. Note that the the PF_SPINNER and PF_SLIDER types expect a fifth +element in their description tuple -- a 3-tuple of the form (lower,upper,step), +which defines the limits for the slider or spinner.''' + +import string; _string = string; del string +import gimp +from gimpenums import * +pdb = gimp.pdb + +error = "gimpfu.error" + +PF_INT8 = PARAM_INT8 +PF_INT16 = PARAM_INT16 +PF_INT32 = PARAM_INT32 +PF_INT = PF_INT32 +PF_FLOAT = PARAM_FLOAT +PF_STRING = PARAM_STRING +PF_VALUE = PF_STRING +PF_INT8ARRAY = PARAM_INT8ARRAY +PF_INT16ARRAY = PARAM_INT16ARRAY +PF_INT32ARRAY = PARAM_INT32ARRAY +PF_INTARRAY = PF_INT32ARRAY +PF_FLOATARRAY = PARAM_FLOATARRAY +PF_STRINGARRAY = PARAM_STRINGARRAY +PF_COLOR = PARAM_COLOR +PF_COLOUR = PF_COLOR +PF_REGION = PARAM_REGION +#PF_DISPLAY = PARAM_DISPLAY +PF_IMAGE = PARAM_IMAGE +PF_LAYER = PARAM_LAYER +PF_CHANNEL = PARAM_CHANNEL +PF_DRAWABLE = PARAM_DRAWABLE +#PF_SELECTION = PARAM_SELECTION +#PF_BOUNDARY = PARAM_BOUNDARY +#PF_PATH = PARAM_PATH +#PF_STATUS = PARAM_STATUS + +PF_TOGGLE = 1000 +PF_BOOL = PF_TOGGLE +PF_SLIDER = 1001 +PF_SPINNER = 1002 +PF_ADJUSTMENT = PF_SPINNER + +PF_FONT = 1003 +PF_FILE = 1004 +PF_BRUSH = 1005 +PF_PATTERN = 1006 +PF_GRADIENT = 1007 + +_type_mapping = { + PF_INT8 : PARAM_INT8, + PF_INT16 : PARAM_INT16, + PF_INT32 : PARAM_INT32, + PF_FLOAT : PARAM_FLOAT, + PF_STRING : PARAM_STRING, + PF_INT8ARRAY : PARAM_INT8ARRAY, + PF_INT16ARRAY : PARAM_INT16ARRAY, + PF_INT32ARRAY : PARAM_INT32ARRAY, + PF_FLOATARRAY : PARAM_FLOATARRAY, + PF_STRINGARRAY : PARAM_STRINGARRAY, + PF_COLOUR : PARAM_COLOR, + PF_REGION : PARAM_REGION, + PF_IMAGE : PARAM_IMAGE, + PF_LAYER : PARAM_LAYER, + PF_CHANNEL : PARAM_CHANNEL, + PF_DRAWABLE : PARAM_DRAWABLE, + + PF_TOGGLE : PARAM_INT32, + PF_SLIDER : PARAM_FLOAT, + PF_SPINNER : PARAM_INT32, + + PF_FONT : PARAM_STRING, + PF_FILE : PARAM_STRING, + PF_BRUSH : PARAM_STRING, + PF_PATTERN : PARAM_STRING, + PF_GRADIENT : PARAM_STRING +} + +_registered_plugins_ = {} + +def register(func_name, blurb, help, author, copyright, date, menupath, + imagetypes, params, results, function): + '''This is called to register a new plugin.''' + # First perform some sanity checks on the data + def letterCheck(str): + allowed = _string.letters + _string.digits + '_' + for ch in str: + if not ch in allowed: + return 0 + else: + return 1 + if not letterCheck(func_name): + raise error, "function name contains ileagal characters" + for ent in params: + if len(ent) < 4: + raise error, "sequence not long enough for "+ent[0] + if type(ent[0]) != type(42): + raise error, "parameter types must be integers" + if not letterCheck(ent[1]): + raise error,"parameter name contains ilegal characters" + for ent in results: + if len(ent) < 3: + raise error, "sequence not long enough for "+ent[0] + if type(ent[0]) != type(42): + raise error, "result types must be integers" + if not letterCheck(ent[1]): + raise error,"result name contains ilegal characters" + if menupath[:8] == '/': + plugin_type = PROC_PLUG_IN + elif menupath[:10] == '/': + plugin_type = PROC_EXTENSION + else: + raise error, "menu path must start with or " + + if not func_name[:7] == 'python_' and \ + not func_name[:10] == 'extension_' and \ + not func_name[:8] == 'plug_in_': + func_name = 'python_fu_' + func_name + + _registered_plugins_[func_name] = (blurb, help, author, copyright, + date, menupath, imagetypes, + plugin_type, params, results, + function) + +def _query(): + for plugin in _registered_plugins_.keys(): + (blurb, help, author, copyright, date, + menupath, imagetypes, plugin_type, + params, results, function) = _registered_plugins_[plugin] + + fn = lambda x: (_type_mapping[x[0]], x[1], x[2]) + params = map(fn, params) + # add the run mode argument ... + params.insert(0, (PARAM_INT32, "run_mode", + "Interactive, Non-Interactive")) + if plugin_type == PROC_PLUG_IN: + params.insert(1, (PARAM_IMAGE, "image", + "The image to work on")) + params.insert(2, (PARAM_DRAWABLE, "drawable", + "The drawable to work on")) + results = map(fn, results) + gimp.install_procedure(plugin, blurb, help, author, copyright, + date, menupath, imagetypes, plugin_type, + params, results) + +def _get_defaults(func_name): + import gimpshelf + (blurb, help, author, copyright, date, + menupath, imagetypes, plugin_type, + params, results, function) = _registered_plugins_[func_name] + + key = "python-fu-save--" + func_name + if gimpshelf.shelf.has_key(key): + return gimpshelf.shelf[key] + else: + # return the default values + return map(lambda x: x[3], params) + +def _set_defaults(func_name, defaults): + import gimpshelf + + key = "python-fu-save--" + func_name + gimpshelf.shelf[key] = defaults + +def _interact(func_name): + (blurb, help, author, copyright, date, + menupath, imagetypes, plugin_type, + params, results, function) = _registered_plugins_[func_name] + + # short circuit for no parameters ... + if len(params) == 0: return [] + + import gtk + import gimpui + + gtk.rc_parse(gimp.gtkrc()) + + defaults = _get_defaults(func_name) + # define a mapping of param types to edit objects ... + class StringEntry(gtk.GtkEntry): + def __init__(self, default=''): + import gtk + gtk.GtkEntry.__init__(self) + self.set_text(str(default)) + def get_value(self): + return self.get_text() + class IntEntry(StringEntry): + def get_value(self): + import string + return string.atoi(self.get_text()) + class FloatEntry(StringEntry): + def get_value(self): + import string + return string.atof(self.get_text()) + class ArrayEntry(StringEntry): + def get_value(self): + return eval(self.get_text()) + class SliderEntry(gtk.GtkHScale): + # bounds is (upper, lower, step) + def __init__(self, default=0, bounds=(0, 100, 5)): + import gtk + self.adj = gtk.GtkAdjustment(default, bounds[0], + bounds[1], bounds[2], + bounds[2], bounds[2]) + gtk.GtkHScale.__init__(self, self.adj) + def get_value(self): + return self.adj.value + class SpinnerEntry(gtk.GtkSpinButton): + # bounds is (upper, lower, step) + def __init__(self, default=0, bounds=(0, 100, 5)): + import gtk + self.adj = gtk.GtkAdjustment(default, bounds[0], + bounds[1], bounds[2], + bounds[2], bounds[2]) + gtk.GtkSpinButton.__init__(self, self.adj, 1, 0) + def get_value(self): + return int(self.adj.value) + class ToggleEntry(gtk.GtkToggleButton): + def __init__(self, default=0): + import gtk + gtk.GtkToggleButton.__init__(self) + self.label = gtk.GtkLabel("No") + self.add(self.label) + self.label.show() + self.connect("toggled", self.changed) + self.set_active(default) + def changed(self, tog): + if tog.active: + self.label.set_text("Yes") + else: + self.label.set_text("No") + def get_value(self): + return self.get_active() + + _edit_mapping = { + PF_INT8 : IntEntry, + PF_INT16 : IntEntry, + PF_INT32 : IntEntry, + PF_FLOAT : FloatEntry, + PF_STRING : StringEntry, + PF_INT8ARRAY : ArrayEntry, + PF_INT16ARRAY : ArrayEntry, + PF_INT32ARRAY : ArrayEntry, + PF_FLOATARRAY : ArrayEntry, + PF_STRINGARRAY : ArrayEntry, + PF_COLOUR : gimpui.ColourSelector, + PF_REGION : IntEntry, # should handle differently ... + PF_IMAGE : gimpui.ImageSelector, + PF_LAYER : gimpui.LayerSelector, + PF_CHANNEL : gimpui.ChannelSelector, + PF_DRAWABLE : gimpui.DrawableSelector, + + PF_TOGGLE : ToggleEntry, + PF_SLIDER : SliderEntry, + PF_SPINNER : SpinnerEntry, + + PF_FONT : gimpui.FontSelector, + PF_FILE : gimpui.FileSelector, + PF_BRUSH : gimpui.BrushSelector, + PF_PATTERN : gimpui.PatternSelector, + PF_GRADIENT : gimpui.GradientSelector, + } + + tooltips = gtk.GtkTooltips() + + dialog = gtk.GtkDialog() + dialog.set_title(func_name) + table = gtk.GtkTable(len(params), 3, gtk.FALSE) + table.set_border_width(5) + table.set_row_spacings(2) + table.set_col_spacings(10) + dialog.vbox.pack_start(table) + table.show() + + vbox = gtk.GtkVBox(gtk.FALSE, 15) + table.attach(vbox, 0,1, 0,len(params), xoptions=gtk.FILL) + vbox.show() + pix = _get_logo(vbox.get_colormap()) + vbox.pack_start(pix, expand=gtk.FALSE) + pix.show() + + label = gtk.GtkLabel(blurb) + label.set_line_wrap(TRUE) + label.set_justify(gtk.JUSTIFY_LEFT) + label.set_usize(100, -1) + vbox.pack_start(label, expand=gtk.FALSE) + label.show() + + edit_wids = [] + for i in range(len(params)): + type = params[i][0] + name = params[i][1] + desc = params[i][2] + def_val = defaults[i] + + label = gtk.GtkLabel(name) + label.set_alignment(1.0, 0.5) + table.attach(label, 1,2, i,i+1, xoptions=gtk.FILL) + label.show() + + if type in (PF_SPINNER, PF_SLIDER): + wid = _edit_mapping[type](def_val, params[i][4]) + else: + wid = _edit_mapping[type](def_val) + table.attach(wid, 2,3, i,i+1) + tooltips.set_tip(wid, desc, None) + wid.show() + edit_wids.append(wid) + + def delete_event(win, event=None): + import gtk + win.hide() + gtk.mainquit() + return TRUE + + # this is a hack ... + finished = [ 0 ] + def ok_clicked(button, win=dialog, finished=finished): + import gtk + win.hide() + finished[0] = 1 + gtk.mainquit() + b = gtk.GtkButton("OK") + b.set_flags(gtk.CAN_DEFAULT) + dialog.action_area.pack_start(b) + b.grab_default() + b.connect("clicked", ok_clicked) + b.show() + + b = gtk.GtkButton("Cancel") + b.set_flags(gtk.CAN_DEFAULT) + dialog.action_area.pack_start(b) + b.connect("clicked", delete_event) + b.show() + + dialog.show() + tooltips.enable() + # run the main loop + gtk.mainloop() + ret = None + if finished[0]: + # OK was clicked + ret = map(lambda wid: wid.get_value(), edit_wids) + _set_defaults(func_name, ret) + dialog.destroy() + return ret + +def _run(func_name, params): + run_mode = params[0] + plugin_type = _registered_plugins_[func_name][7] + func = _registered_plugins_[func_name][10] + + if plugin_type == PROC_PLUG_IN: + start_params = params[1:3] + extra_params = params[3:] + else: + start_params = () + extra_params = params[1:] + + if run_mode == RUN_INTERACTIVE: + extra_params = _interact(func_name) + if extra_params == None: + return + elif run_mode == RUN_WITH_LAST_VALS: + extra_params = _get_defaults(func_name) + + params = start_params + tuple(extra_params) + res = apply(func, params) + if run_mode != RUN_NONINTERACTIVE: gimp.displays_flush() + +def main(): + '''This should be called after registering the plugin.''' + gimp.main(None, None, _query, _run) + +_python_image = [ +"64 64 7 1", +" c #000000", +". c #00FF00", +"X c None", +"o c #FF0000", +"O c #FFFF00", +"+ c #808000", +"@ c #0000FF", +"XXXXXXXXXXXXXX XX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXX XX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXX ++++ ++++++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXX ++++ ++++++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX ++++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX ++++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX OOOO OOOOOO ++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX OOOO OOOOOO ++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX OO@@ OO@@@@ ++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX OO@@ OO@@@@ ++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX OO@@ OO@@@@ ++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX OO@@ OO@@@@ ++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX OOOO OOOOOO ++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXX OOOO OOOOOO ++ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXX ++ ++++++ XXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXX ++ ++++++ XXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXX ++++++++++++++++++++++ ++++++ XXXXXXXXXXXXXXXXXXXXXX", +"XXXX ++++++++++++++++++++++ ++++++ XXXXXXXXXXXXXXXXXXXXXX", +"XX ++++++++++++++++++++++++++++ ++++++++ XXXXXXXXXXXXXXXXXXXX", +"XX ++++++++++++++++++++++++++++ ++++++++ XXXXXXXXXXXXXXXXXXXX", +" ++++++++++++++++++++++++++++++++ ++++++++ XXXXXXXXXXXXXXXXXX", +" ++++++++++++++++++++++.+.+..++++ ++++++++ XXXXXXXXXXXXXXXXXX", +" ++ ++++ ++++++++++.+........++ ++++++++ XXXXXXXXXXXXXXXXXX", +" ++ ++++ ++++++++.+...........+ ++++++++ XXXXXXXXXXXXXXXXXX", +" ++++++++++++++++++.+.. ...... ++++++++++ XXXXXXXXXXXXXXXX", +" ++++++++++++++++.+... ...... ++++++++++ XXXXXXXXXXXXXXXX", +" .+.+.+.+.+.+.+.+.... ..++.. ..++++++++++ XXXXXXXXXXXXXXXX", +" .+.+.+.+.+.+.+..... ..++.. ...+++++++++ XXXXXXXXXXXXXXXX", +" ................ ++.... ......++++++++ XXXXXXXXXXXXXXXX", +" ............... +..... +......+++++++ XXXXXXXXXXXXXXXX", +"XX +.+.+.+.+..+ ++++.. ++++...+++++++ XXXXXXXXXXXXXXXX", +"XX ++++++++++++ +...... ++++...+++++++ XXXXXXXXXXXXXXXX", +"XXXX ooooo ++.... XX +......+++++++ XXXXXXXXXXXXXXXX", +"XXXX ooooo ++..... XX .......+++++++ XXXXXXXXXXXXXXXX", +"XXXX ooooo ++...... XXXX ++....++++++ XXXXXXXXXXXXXXXXXX", +"XXXX ooooo ++....... XXXX ++...+++++++ XXXXXXXXXXXXXXXXXX", +"XXXX ooooo.++...... XXXX +......+++++++ XXXXXXXXXXXXXXXXXX", +"XXXX ooooo..+....... XXXX ......++++++++ XXXXXXXXXXXXXXXXXX", +"XXXXooooo....... XXXX +++++....+++++++ XXXXXXXXXXXXXXXXXXXX", +"XXXooooo........ XXXX +++++..+++++++++ XXXXXXXXXXXXXXXXXXXX", +"XXoooo XXXXXX +.......++++++++ XXXXXXXXXXXXXXXXXXXXXX", +"Xooooo XXXXXX .......+++++++++ XXXXXXXXXXXXXXXXXXXXXX", +"oooXooXXXXXXXXXXXX +++++....+++++++++ XXXXXXXXXXXXXXXXXXXXXX ", +"ooXXooXXXXXXXXXXXX +++++...++++++++++ XXXXXXXXXXXXXXXXXXXXXX ", +"XXXXooXXXXXXXX ++.......+++++++++ XXXXXXXXXXXXXXXXXXXXXX ", +"XXXXooXXXXXXXX +.......++++++++++ XXXXXXXXXXXXXXXXXXXXXX ", +"XXXXXXXXXXXX ++++++.....+++++++++ XXXXXX XXXX ++ ", +"XXXXXXXXXXXX ++++++....++++++++++ XXXXXX XXXX ++ ", +"XXXXXXXXXXXX +........+++++++++ ++++++++ XX ++++++ ++++ ", +"XXXXXXXXXXXX ........++++++++++ ++++++++ XX ++++++ ++++ ", +"XXXXXXXXXX ..........++++++++ ++++++++++++ ++++++++++++++++ ", +"XXXXXXXXXX .........+++++++++ ++++++++++++ ++++++++++++++++ ", +"XXXXXXXXXX ++++....++++++++++++++++++++++++++++++++++++++++ XX", +"XXXXXXXXXX +++++...++++++++++++++++++++++++++++++++++++++++ XX", +"XXXXXXXXXX ++++....++++++++++++++++++++++++++++++ ..... XXXX", +"XXXXXXXXXX ........++++++++++++++++++++++++++++++ .... XXXX", +"XXXXXXXXXX .........+++++++++++..++++++++++++++ XXXX XXXXXX", +"XXXXXXXXXX .............+++++++..++++++++++++++ XXXX XXXXXX", +"XXXXXXXXXXXX +++++............ ............ XXXXXXXXXXXXXXXX", +"XXXXXXXXXXXX ++++++.......... .......... XXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXX +.....++.. XXXX XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXX .....++++. XXXX XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +] + +def _get_logo(colormap): + import gtk + pix, mask = gtk.create_pixmap_from_xpm_d(colormap, None, _python_image) + return gtk.GtkPixmap(pix, mask) diff --git a/plug-ins/pygimp/gimpmodule.c b/plug-ins/pygimp/gimpmodule.c new file mode 100644 index 0000000000..f2a9ab6347 --- /dev/null +++ b/plug-ins/pygimp/gimpmodule.c @@ -0,0 +1,4607 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- */ +/* + Gimp-Python - allows the writing of Gimp plugins in Python. + Copyright (C) 1997-1999 James Henstridge + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Python.h" +#include "sysmodule.h" +#include "structmember.h" +#include +static PyObject *ErrorObject; + +#ifndef GIMP_CHECK_VERSION +# define GIMP_CHECK_VERSION(major, minor, micro) \ + (GIMP_MAJOR_VERSION > (major) || \ + (GIMP_MAJOR_VERSION == (major) && GIMP_MINOR_VERSION > (minor)) || \ + (GIMP_MAJOR_VERSION == (major) && GIMP_MINOR_VERSION == (minor) && \ + GIMP_MICRO_VERSION >= (micro))) +#endif + +#ifndef PG_DEBUG +# define PG_DEBUG 0 +#endif + +/* ----------------------------------------------------- */ + +/* Declarations for objects of type pdb */ + +typedef struct { + PyObject_HEAD + /* XXXX Add your own stuff here */ +} pdbobject; + +staticforward PyTypeObject Pdbtype; +#define pdb_check(v) ((v)->ob_type == &Pdbtype) + + +/* ---------------------------------------------------------------- */ + +/* Declarations for objects of type pdbFunc */ + +typedef struct { + PyObject_HEAD + char *name; + PyObject *proc_name, *proc_blurb, *proc_help, *proc_author, + *proc_copyright, *proc_date, *proc_type, *py_params, + *py_return_vals; + int nparams, nreturn_vals; + GParamDef *params, *return_vals; +} pfobject; + +staticforward PyTypeObject Pftype; +#define pf_check(v) ((v)->ob_type == &Pftype) +static pfobject *newpfobject(char *); + +/* ---------------------------------------------------------------- */ + +/* Declarations for objects of type Image */ + +typedef struct { + PyObject_HEAD + gint32 ID; +} imgobject; + +staticforward PyTypeObject Imgtype; +#define img_check(v) ((v)->ob_type == &Imgtype) +static imgobject *newimgobject(gint32); + +/* ---------------------------------------------------------------- */ + +/* Declarations for objects of type Display */ + +typedef struct { + PyObject_HEAD + gint32 ID; +} dispobject; + +staticforward PyTypeObject Disptype; +#define disp_check(v) ((v)->ob_type == &Disptype) +static dispobject *newdispobject(gint32); + +/* ---------------------------------------------------------------- */ + +/* Declarations for objects of type Layer and channel */ + +typedef struct { + PyObject_HEAD + gint32 ID; + GDrawable *drawable; +} drwobject, layobject, chnobject; + +staticforward PyTypeObject Laytype; +#define lay_check(v) ((v)->ob_type == &Laytype) +static layobject *newlayobject(gint32); + +staticforward PyTypeObject Chntype; +#define chn_check(v) ((v)->ob_type == &Chntype) +static chnobject *newchnobject(gint32); + +/* The drawable type isn't really a type -- it can be a channel or layer */ +#define drw_check(v) ((v)->ob_type == &Laytype || (v)->ob_type == &Chntype) +static drwobject *newdrwobject(GDrawable *, gint32); + +/* ---------------------------------------------------------------- */ + +/* Declarations for objects of type Tile */ + +typedef struct { + PyObject_HEAD + GTile *tile; + drwobject *drawable; /* we keep a reference to the drawable */ +} tileobject; + +staticforward PyTypeObject Tiletype; +#define tile_check(v) ((v)->ob_type == &Tiletype) +static tileobject *newtileobject(GTile *, drwobject *drw); + +/* ---------------------------------------------------------------- */ + +/* Declarations for objects of type PixelRegion */ + +typedef struct { + PyObject_HEAD + GPixelRgn pr; + drwobject *drawable; /* keep the drawable around */ +} probject; + +staticforward PyTypeObject Prtype; +#define pr_check(v) ((v)->ob_type == &Prtype) +static probject *newprobject(drwobject *drw, int, int, int, int, int, int); + +/* ---------------------------------------------------------------- */ + +#ifdef GIMP_HAVE_PARASITES +/* Declarations for objects of type Parasite */ + +typedef struct { + PyObject_HEAD + Parasite *para; +} paraobject; + +staticforward PyTypeObject Paratype; +#define para_check(v) ((v)->ob_type == &Paratype) +static paraobject *newparaobject(Parasite *para); + +#endif +/* ---------------------------------------------------------------- */ + +/* routines to convert between Python tuples and gimp GParam's */ + +#if PG_DEBUG > 0 + +static void print_GParam(int nparams, GParam *params) { + int i; + + for (i = 0; i < nparams; i++) { + switch(params[i].type) { + case PARAM_INT32: + fprintf(stderr, "%i. int %i\n", i, + params[i].data.d_int32); + break; + case PARAM_INT16: + fprintf(stderr, "%i. int %i\n", i, + params[i].data.d_int16); + break; + case PARAM_INT8: + fprintf(stderr, "%i. int %i\n", i, + params[i].data.d_int8); + break; + case PARAM_FLOAT: + fprintf(stderr, "%i. float %f\n", i, + params[i].data.d_float); + break; + case PARAM_STRING: + fprintf(stderr, "%i. string %s\n", i, + params[i].data.d_string); + break; + case PARAM_INT32ARRAY: + case PARAM_INT16ARRAY: + case PARAM_INT8ARRAY: + case PARAM_FLOATARRAY: + case PARAM_STRINGARRAY: + fprintf(stderr, "%i. array of type %i %s\n", i, + params[i].type, params[i].data.d_int32array + == NULL ? "(null)":""); + break; + case PARAM_STATUS: + fprintf(stderr, "%i. status %i\n", i, + params[i].data.d_status); + break; + default: + fprintf(stderr, "%i. other %i\n", i, + params[i].data.d_int32); + break; + } + } +} + +#endif + +static PyObject * +GParam_to_tuple(int nparams, GParam *params) { + PyObject *args, *tmp; + int i, j, n; + + args = PyTuple_New(nparams); + for (i = 0; i < nparams && params[i].type != PARAM_END; i++) { + switch(params[i].type) { + case PARAM_INT32: + PyTuple_SetItem(args, i, PyInt_FromLong( + (long) params[i].data.d_int32)); + break; + case PARAM_INT16: + PyTuple_SetItem(args, i, PyInt_FromLong( + (long) params[i].data.d_int16)); + break; + case PARAM_INT8: + PyTuple_SetItem(args, i, PyInt_FromLong( + (long) params[i].data.d_int8)); + break; + case PARAM_FLOAT: + PyTuple_SetItem(args, i, PyFloat_FromDouble( + (double) params[i].data.d_float)); + break; + case PARAM_STRING: + if (params[i].data.d_string == NULL) { + Py_INCREF(Py_None); + PyTuple_SetItem(args, i, Py_None); + } else + PyTuple_SetItem(args, i, + PyString_FromString( + params[i].data.d_string)); + break; + + /* For these to work, the previous argument must have + * been an integer + */ + case PARAM_INT32ARRAY: + if (params[i].data.d_int32array == NULL) { + PyTuple_SetItem(args,i,PyTuple_New(0)); + break; + } + if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) { + Py_DECREF(args); + return NULL; + } + if (!PyInt_Check(tmp)) { + PyErr_SetString(PyExc_TypeError, + "count type must be integer"); + Py_DECREF(args); + return NULL; + } + n = PyInt_AsLong(tmp); + tmp = PyTuple_New(n); + for (j = 0; j < n; j++) + PyTuple_SetItem(tmp, j, PyInt_FromLong( + (long)params[i].data.d_int32array[j])); + PyTuple_SetItem(args, i, tmp); + break; + case PARAM_INT16ARRAY: + if (params[i].data.d_int16array == NULL) { + PyTuple_SetItem(args,i,PyTuple_New(0)); + break; + } + if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) { + Py_DECREF(args); + return NULL; + } + if (!PyInt_Check(tmp)) { + PyErr_SetString(PyExc_TypeError, + "count type must be integer"); + Py_DECREF(args); + return NULL; + } + n = PyInt_AsLong(tmp); + tmp = PyTuple_New(n); + for (j = 0; j < n; j++) + PyTuple_SetItem(tmp, j, PyInt_FromLong( + (long)params[i].data.d_int16array[j])); + PyTuple_SetItem(args, i, tmp); + break; + case PARAM_INT8ARRAY: + if (params[i].data.d_int8array == NULL) { + PyTuple_SetItem(args,i,PyTuple_New(0)); + break; + } + if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) { + Py_DECREF(args); + return NULL; + } + if (!PyInt_Check(tmp)) { + PyErr_SetString(PyExc_TypeError, + "count type must be integer"); + Py_DECREF(args); + return NULL; + } + n = PyInt_AsLong(tmp); + tmp = PyTuple_New(n); + for (j = 0; j < n; j++) + PyTuple_SetItem(tmp, j, PyInt_FromLong( + (long)params[i].data.d_int8array[j])); + PyTuple_SetItem(args, i, tmp); + break; + case PARAM_FLOATARRAY: + if (params[i].data.d_floatarray == NULL) { + PyTuple_SetItem(args,i,PyTuple_New(0)); + break; + } + if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) { + Py_DECREF(args); + return NULL; + } + if (!PyInt_Check(tmp)) { + PyErr_SetString(PyExc_TypeError, + "count type must be integer"); + Py_DECREF(args); + return NULL; + } + n = PyInt_AsLong(tmp); + tmp = PyTuple_New(n); + for (j = 0; j < n; j++) + PyTuple_SetItem(tmp,j, + PyFloat_FromDouble((double) + params[i].data.d_floatarray[j])); + PyTuple_SetItem(args, i, tmp); + break; + case PARAM_STRINGARRAY: + if (params[i].data.d_stringarray == NULL) { + PyTuple_SetItem(args,i,PyTuple_New(0)); + break; + } + if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) { + Py_DECREF(args); + return NULL; + } + if (!PyInt_Check(tmp)) { + PyErr_SetString(PyExc_TypeError, + "count type must be integer"); + Py_DECREF(args); + return NULL; + } + n = PyInt_AsLong(tmp); + tmp = PyTuple_New(n); + for (j = 0; j < n; j++) + PyTuple_SetItem(tmp,j, + PyString_FromString( + params[i].data.d_stringarray[j])); + PyTuple_SetItem(args, i, tmp); + break; + case PARAM_COLOR: + PyTuple_SetItem(args, i, Py_BuildValue("(iii)", + (long) params[i].data.d_color.red, + (long) params[i].data.d_color.green, + (long) params[i].data.d_color.blue)); + break; + case PARAM_REGION: + PyTuple_SetItem(args, i, Py_BuildValue("(iiii)", + (long) params[i].data.d_region.x, + (long) params[i].data.d_region.y, + (long) params[i].data.d_region.width, + (long) params[i].data.d_region.height)); + break; + case PARAM_DISPLAY: + PyTuple_SetItem(args, i, (PyObject *) + newdispobject( + params[i].data.d_display)); + break; + case PARAM_IMAGE: + PyTuple_SetItem(args, i, (PyObject *) + newimgobject(params[i].data.d_image)); + break; + case PARAM_LAYER: + PyTuple_SetItem(args, i, (PyObject *) + newlayobject(params[i].data.d_layer)); + break; + case PARAM_CHANNEL: + PyTuple_SetItem(args, i, (PyObject *) + newchnobject(params[i].data.d_channel)); + break; + case PARAM_DRAWABLE: + PyTuple_SetItem(args, i, (PyObject *) + newdrwobject(NULL, + params[i].data.d_drawable)); + break; + case PARAM_SELECTION: + PyTuple_SetItem(args, i, (PyObject *) + newlayobject( + params[i].data.d_selection)); + break; + case PARAM_BOUNDARY: + PyTuple_SetItem(args, i, PyInt_FromLong( + params[i].data.d_boundary)); + break; + case PARAM_PATH: + PyTuple_SetItem(args, i, PyInt_FromLong( + params[i].data.d_path)); + break; +#ifdef GIMP_HAVE_PARASITES + case PARAM_PARASITE: + PyTuple_SetItem(args, i, + (PyObject *)newparaobject(parasite_copy( + &(params[i].data.d_parasite)))); + break; +#endif + case PARAM_STATUS: + PyTuple_SetItem(args, i, PyInt_FromLong( + params[i].data.d_status)); + break; + case PARAM_END: + break; + } + } + return args; +} + +static GParam * +tuple_to_GParam(PyObject *args, GParamDef *ptype, int nparams) { + PyObject *tuple, *item, *r, *g, *b, *x, *y, *w, *h; + GParam *ret; + int i, j, len; + gint32 *i32a; gint16 *i16a; gint8 *i8a; gdouble *fa; gchar **sa; + + if (nparams == 0) + tuple = PyTuple_New(0); + else if (!PyTuple_Check(args) && nparams == 1) + tuple = Py_BuildValue("(O)", args); + else { + Py_INCREF(args); + tuple = args; + } + if (!PyTuple_Check(tuple)) { + PyErr_SetString(PyExc_TypeError, "wrong type of parameter"); + return NULL; + } + + if (PyTuple_Size(tuple) != nparams) { + PyErr_SetString(PyExc_TypeError, "wrong number of parameters"); + return NULL; + } + + ret = g_new(GParam, nparams+1); + for (i = 0; i <= nparams; i++) + ret[i].type = PARAM_STATUS; +#define check(expr) if (expr) { \ + PyErr_SetString(PyExc_TypeError, "wrong parameter type"); \ + Py_DECREF(tuple); \ + gimp_destroy_params(ret, nparams); \ + return NULL; \ + } +#define arraycheck(expr, ar) if (expr) { \ + PyErr_SetString(PyExc_TypeError, "subscript of wrong type"); \ + Py_DECREF(tuple); \ + gimp_destroy_params(ret, nparams); \ + g_free(ar); \ + return NULL; \ + } + for (i = 1; i <= nparams; i++) { + item = PyTuple_GetItem(tuple, i-1); + switch (ptype[i-1].type) { + case PARAM_INT32: + check((x = PyNumber_Int(item)) == NULL) + ret[i].data.d_int32 = PyInt_AsLong(x); + Py_DECREF(x); + break; + case PARAM_INT16: + check((x = PyNumber_Int(item)) == NULL) + ret[i].data.d_int16 = PyInt_AsLong(x); + Py_DECREF(x); + break; + case PARAM_INT8: + check((x = PyNumber_Int(item)) == NULL) + ret[i].data.d_int8 = PyInt_AsLong(x); + Py_DECREF(x); + break; + case PARAM_FLOAT: + check((x = PyNumber_Float(item)) == NULL) + ret[i].data.d_float = PyFloat_AsDouble(x); + Py_DECREF(x); + break; + case PARAM_STRING: + check((x = PyObject_Str(item)) == NULL) + ret[i].data.d_string = g_strdup( + PyString_AsString(x)); + Py_DECREF(x); + break; + case PARAM_INT32ARRAY: + check(!PySequence_Check(item)) + len = PySequence_Length(item); + i32a = g_new(gint32, len); + for (j = 0; j < len; j++) { + x = PySequence_GetItem(item, j); + arraycheck((y=PyNumber_Int(x))==NULL, + i32a); + i32a[j] = PyInt_AsLong(y); + Py_DECREF(y); + } + ret[i].data.d_int32array = i32a; + break; + case PARAM_INT16ARRAY: + check(!PySequence_Check(item)) + len = PySequence_Length(item); + i16a = g_new(gint16, len); + for (j = 0; j < len; j++) { + x = PySequence_GetItem(item, j); + arraycheck((y=PyNumber_Int(x))==NULL, + i16a); + i16a[j] = PyInt_AsLong(y); + Py_DECREF(y); + } + ret[i].data.d_int16array = i16a; + break; + case PARAM_INT8ARRAY: + check(!PySequence_Check(item)) + len = PySequence_Length(item); + i8a = g_new(gint8, len); + for (j = 0; j < len; j++) { + x = PySequence_GetItem(item, j); + arraycheck((y=PyNumber_Int(x))==NULL, + i8a); + i8a[j] = PyInt_AsLong(y); + Py_DECREF(y); + } + ret[i].data.d_int8array = i8a; + break; + case PARAM_FLOATARRAY: + check(!PySequence_Check(item)) + len = PySequence_Length(item); + fa = g_new(gdouble, len); + for (j = 0; j < len; j++) { + x = PySequence_GetItem(item, j); + arraycheck((y=PyNumber_Float(x))==NULL, + fa); + fa[j] = PyFloat_AsDouble(y); + Py_DECREF(y); + } + ret[i].data.d_floatarray = fa; + break; + case PARAM_STRINGARRAY: + check(!PySequence_Check(item)) + len = PySequence_Length(item); + sa = g_new(gchar *, len); + for (j = 0; j < len; j++) { + x = PySequence_GetItem(item, j); + arraycheck((y=PyObject_Str(x))==NULL, + sa); + sa[j] = g_strdup(PyString_AsString(y)); + Py_DECREF(y); + } + ret[i].data.d_stringarray = sa; + break; + case PARAM_COLOR: + check(!PySequence_Check(item) || + PySequence_Length(item) < 3) + r = PySequence_GetItem(item, 0); + g = PySequence_GetItem(item, 1); + b = PySequence_GetItem(item, 2); + check(!PyInt_Check(r) || !PyInt_Check(g) || + !PyInt_Check(b)) + ret[i].data.d_color.red = PyInt_AsLong(r); + ret[i].data.d_color.green = PyInt_AsLong(g); + ret[i].data.d_color.blue = PyInt_AsLong(b); + break; + case PARAM_REGION: + check(!PySequence_Check(item) || + PySequence_Length(item) < 4) + x = PySequence_GetItem(item, 0); + y = PySequence_GetItem(item, 1); + w = PySequence_GetItem(item, 2); + h = PySequence_GetItem(item, 3); + check(!PyInt_Check(x) || !PyInt_Check(y) || + !PyInt_Check(w) || !PyInt_Check(h)) + ret[i].data.d_region.x = PyInt_AsLong(x); + ret[i].data.d_region.y = PyInt_AsLong(y); + ret[i].data.d_region.width = PyInt_AsLong(w); + ret[i].data.d_region.height = PyInt_AsLong(h); + break; + case PARAM_DISPLAY: + check(!disp_check(item)) + ret[i].data.d_display=((dispobject*)item)->ID; + break; + case PARAM_IMAGE: + if (item == Py_None) { + ret[i].data.d_image = -1; + break; + } + check(!img_check(item)) + ret[i].data.d_image=((imgobject*)item)->ID; + break; + case PARAM_LAYER: + if (item == Py_None) { + ret[i].data.d_layer = -1; + break; + } + check(!lay_check(item)) + ret[i].data.d_layer=((layobject*)item)->ID; + break; + case PARAM_CHANNEL: + if (item == Py_None) { + ret[i].data.d_channel = -1; + break; + } + check(!chn_check(item)) + ret[i].data.d_channel=((chnobject*)item)->ID; + break; + case PARAM_DRAWABLE: + if (item == Py_None) { + ret[i].data.d_channel = -1; + break; + } + check(!drw_check(item) && !lay_check(item) && !chn_check(item)) + ret[i].data.d_channel=((drwobject*)item)->ID; + break; + case PARAM_SELECTION: + check(!lay_check(item)) + ret[i].data.d_selection=((layobject*)item)->ID; + break; + case PARAM_BOUNDARY: + check(!PyInt_Check(item)) + ret[i].data.d_boundary = PyInt_AsLong(item); + break; + case PARAM_PATH: + check(!PyInt_Check(item)) + ret[i].data.d_path = PyInt_AsLong(item); + break; +#ifdef GIMP_HAVE_PARASITES + case PARAM_PARASITE: + /* can't do anything, since size of Parasite is not known */ + break; +#endif + case PARAM_STATUS: + check(!PyInt_Check(item)) + ret[i].data.d_status = PyInt_AsLong(item); + break; + case PARAM_END: + break; + } +#undef check +#undef arraycheck + ret[i].type = ptype[i-1].type; + } + + Py_DECREF(tuple); + return ret; +} + +/* ---------------------------------------------------------------- */ +/* generic number conversions. Most gimp objects contain an ID field + * at the start. This code fragment adds conversion of that ID to + * an integer, hex, oct or float + */ + +static PyObject * +gobj_int(self) + imgobject *self; /* or layobject, drwobject, chnobject ... */ +{ + return PyInt_FromLong((long)self->ID); +} + +static PyObject * +gobj_long(self) + imgobject *self; +{ + return PyLong_FromLong((long)self->ID); +} + +static PyObject * +gobj_float(self) + imgobject *self; +{ + return PyFloat_FromDouble((double)self->ID); +} + +static PyObject * +gobj_oct(self) + imgobject *self; +{ + char buf[20]; + + if (self->ID == 0) + strcpy(buf, "0"); + else + sprintf(buf, "0%lo", (unsigned long)self->ID); + return PyString_FromString(buf); +} + +static PyObject * +gobj_hex(self) + imgobject *self; +{ + char buf[20]; + + sprintf(buf, "0x%lx", (unsigned long)self->ID); + return PyString_FromString(buf); +} + +static PyNumberMethods gobj_as_number = { + (binaryfunc)0, /*nb_add*/ + (binaryfunc)0, /*nb_subtract*/ + (binaryfunc)0, /*nb_multiply*/ + (binaryfunc)0, /*nb_divide*/ + (binaryfunc)0, /*nb_remainder*/ + (binaryfunc)0, /*nb_divmod*/ + (ternaryfunc)0, /*nb_power*/ + (unaryfunc)0, /*nb_negative*/ + (unaryfunc)0, /*nb_positive*/ + (unaryfunc)0, /*nb_absolute*/ + (inquiry)0, /*nb_nonzero*/ + (unaryfunc)0, /*nb_invert*/ + (binaryfunc)0, /*nb_lshift*/ + (binaryfunc)0, /*nb_rshift*/ + (binaryfunc)0, /*nb_and*/ + (binaryfunc)0, /*nb_xor*/ + (binaryfunc)0, /*nb_or*/ + 0, /*nb_coerce*/ + (unaryfunc)gobj_int, /*nb_int*/ + (unaryfunc)gobj_long, /*nb_long*/ + (unaryfunc)gobj_float, /*nb_float*/ + (unaryfunc)gobj_oct, /*nb_oct*/ + (unaryfunc)gobj_hex, /*nb_hex*/ +}; + +/* ---------------------------------------------------------------- */ + +static PyObject * +pdb_query(self, args) + pdbobject *self; + PyObject *args; +{ + char *n=".*", *b=".*", *h=".*", *a=".*", *c=".*", *d=".*", *t=".*"; + int num, i; + char **names; + PyObject *ret; + if (!PyArg_ParseTuple(args, "|zzzzzzz:pdb.query", &n, &b, &h, &a, + &c, &d, &t)) + return NULL; + gimp_query_database(n, b, h, a, c, d, t, &num, &names); + ret = PyList_New(num); + for (i = 0; i < num; i++) + PyList_SetItem(ret, i, PyString_FromString(names[i])); + g_free(names); + return ret; +} + +static struct PyMethodDef pdb_methods[] = { + {"query", (PyCFunction)pdb_query, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +static pdbobject * +newpdbobject() +{ + pdbobject *self; + + self = PyObject_NEW(pdbobject, &Pdbtype); + if (self == NULL) + return NULL; + /* XXXX Add your own initializers here */ + return self; +} + + +static void +pdb_dealloc(self) + pdbobject *self; +{ + /* XXXX Add your own cleanup code here */ + PyMem_DEL(self); +} + +static PyObject * +pdb_repr(self) + pdbobject *self; +{ + return PyString_FromString(""); +} + +/* Code to access pdb objects as mappings */ + +static int +pdb_length(self) + pdbobject *self; +{ + PyErr_SetString(ErrorObject, "Can not size the procedural database."); + return -1; +} + +static PyObject * +pdb_subscript(self, key) + pdbobject *self; + PyObject *key; +{ + PyObject *r; + + if (!PyString_Check(key)) { + PyErr_SetString(PyExc_TypeError, "Subscript must be a string."); + return NULL; + } + r = (PyObject *)newpfobject(PyString_AsString(key)); + if (r == NULL) { + PyErr_Clear(); + PyErr_SetObject(PyExc_KeyError, key); + } + return r; +} + +static int +pdb_ass_sub(self, v, w) + pdbobject *self; + PyObject *v, *w; +{ + PyErr_SetString(ErrorObject, + "Use install_procedure to add to the PDB."); + return -1; +} + +static PyMappingMethods pdb_as_mapping = { + (inquiry)pdb_length, /*mp_length*/ + (binaryfunc)pdb_subscript, /*mp_subscript*/ + (objobjargproc)pdb_ass_sub, /*mp_ass_subscript*/ +}; + +/* -------------------------------------------------------- */ + +static PyObject * +pdb_getattr(self, name) + pdbobject *self; + char *name; +{ + PyObject *r; + + r = Py_FindMethod(pdb_methods, (PyObject *)self, name); + if (r == NULL) { + PyErr_Clear(); + return (PyObject *)newpfobject(name); + } + return r; +} + +static PyTypeObject Pdbtype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "pdb", /*tp_name*/ + sizeof(pdbobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)pdb_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)pdb_getattr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)pdb_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &pdb_as_mapping, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + NULL /* Documentation string */ +}; + +/* End of code for pdb objects */ +/* -------------------------------------------------------- */ + + +static pfobject * +newpfobject(name) + char *name; +{ + pfobject *self; + char *b,*h,*a,*c,*d; + int pt, np, nr, i; + GParamDef *p, *r; + + if (!gimp_query_procedure(name, &b, &h, &a, &c, &d, &pt, + &np, &nr, &p, &r)) { + PyErr_SetString(ErrorObject, "procedure not found."); + return NULL; + } + + self = PyObject_NEW(pfobject, &Pftype); + if (self == NULL) + return NULL; + + self->name = g_strdup(name); + self->proc_name = PyString_FromString(name); + self->proc_blurb = PyString_FromString(b); + self->proc_help = PyString_FromString(h); + self->proc_author = PyString_FromString(a); + self->proc_copyright = PyString_FromString(c); + self->proc_date = PyString_FromString(d); + self->proc_type = PyInt_FromLong(pt); + self->nparams = np; + self->nreturn_vals = nr; + self->params = p; + self->return_vals = r; + + self->py_params = PyTuple_New(np); + for (i = 0; i < np; i++) + PyTuple_SetItem(self->py_params, i, + Py_BuildValue("(iss)", p[i].type, p[i].name, + p[i].description)); + self->py_return_vals = PyTuple_New(nr); + for (i = 0; i < nr; i++) + PyTuple_SetItem(self->py_return_vals, i, + Py_BuildValue("(iss)", r[i].type, r[i].name, + r[i].description)); + + g_free(b); g_free(h); g_free(a); g_free(c); g_free(d); + + return self; +} + +#ifndef GIMP_HAVE_DESTROY_PARAMDEFS +static void +gimp_destroy_paramdefs (GParamDef *paramdefs, + int nparams) +{ + while (nparams--) { + g_free (paramdefs[nparams].name); + g_free (paramdefs[nparams].description); + } + + g_free (paramdefs); +} +#endif +static void +pf_dealloc(self) + pfobject *self; +{ + g_free(self->name); + Py_DECREF(self->proc_name); + Py_DECREF(self->proc_blurb); + Py_DECREF(self->proc_help); + Py_DECREF(self->proc_author); + Py_DECREF(self->proc_copyright); + Py_DECREF(self->proc_date); + Py_DECREF(self->proc_type); + Py_DECREF(self->py_params); + Py_DECREF(self->py_return_vals); + gimp_destroy_paramdefs(self->params, self->nparams); + gimp_destroy_paramdefs(self->return_vals, self->nreturn_vals); + PyMem_DEL(self); +} +#define OFF(x) offsetof(pfobject, x) + +static struct memberlist pf_memberlist[] = { + {"proc_name", T_OBJECT, OFF(proc_name), RO}, + {"proc_blurb", T_OBJECT, OFF(proc_blurb), RO}, + {"proc_help", T_OBJECT, OFF(proc_help), RO}, + {"proc_author", T_OBJECT, OFF(proc_author), RO}, + {"proc_copyright", T_OBJECT, OFF(proc_copyright), RO}, + {"proc_date", T_OBJECT, OFF(proc_date), RO}, + {"proc_type", T_OBJECT, OFF(proc_type), RO}, + {"nparams", T_INT, OFF(nparams), RO}, + {"nreturn_vals", T_INT, OFF(nreturn_vals), RO}, + {"params", T_OBJECT, OFF(py_params), RO}, + {"return_vals", T_OBJECT, OFF(py_return_vals), RO}, + {NULL} /* Sentinel */ +}; + +#undef OFF + +static PyObject * +pf_getattr(self, name) + pfobject *self; + char *name; +{ + return PyMember_Get((char *)self, pf_memberlist, name); +} + +static PyObject * +pf_repr(self) + pfobject *self; +{ + PyObject *s; + + s = PyString_FromString("proc_name); + PyString_ConcatAndDel(&s, PyString_FromString(">")); + return s; +} + +static PyObject * +pf_call(self, args, kwargs) + pfobject *self; + PyObject *args; + PyObject *kwargs; +{ + GParam *params, *ret; + int nret; + PyObject *t = NULL, *r; + +#if PG_DEBUG > 0 + fprintf(stderr, "--- %s --- ", PyString_AsString(self->proc_name)); +#endif + if (self->nparams > 0 && !strcmp(self->params[0].name, "run_mode")) { + params = tuple_to_GParam(args, self->params+1, self->nparams-1); + if (params == NULL) + return NULL; + params[0].type = self->params[0].type; + params[0].data.d_int32 = RUN_NONINTERACTIVE; +#if PG_DEBUG > 1 + print_GParam(self->nparams, params); +#endif + ret = gimp_run_procedure2(self->name, &nret, self->nparams, + params); + } else { + params = tuple_to_GParam(args, self->params, self->nparams); + if (params == NULL) + return NULL; +#if PG_DEBUG > 1 + print_GParam(self->nparams, params+1); +#endif + ret = gimp_run_procedure2(self->name, &nret, self->nparams, + params+1); + } + gimp_destroy_params(params, self->nparams); + if (!ret) { + PyErr_SetString(ErrorObject, "no status returned"); +#if PG_DEBUG >= 1 + fprintf(stderr, "ret == NULL\n"); +#endif + return NULL; + } + switch(ret[0].data.d_status) { + case STATUS_EXECUTION_ERROR: +#if PG_DEBUG > 0 + fprintf(stderr, "execution error\n"); +#endif + gimp_destroy_params(ret, nret); + PyErr_SetString(PyExc_RuntimeError, "execution error"); + return NULL; + break; + case STATUS_CALLING_ERROR: +#if PG_DEBUG > 0 + fprintf(stderr, "calling error\n"); +#endif + gimp_destroy_params(ret, nret); + PyErr_SetString(PyExc_TypeError, "invalid arguments"); + return NULL; + break; + case STATUS_SUCCESS: +#if PG_DEBUG > 0 + fprintf(stderr, "success\n"); +#endif + t = GParam_to_tuple(nret-1, ret+1); + gimp_destroy_params(ret, nret); + if (t == NULL) { + PyErr_SetString(ErrorObject, + "couldn't make return value"); + return NULL; + } + break; + default: +#if PG_DEBUG > 0 + fprintf(stderr, "unknown - %i (type %i)\n", + ret[0].data.d_status, ret[0].type); +#endif + PyErr_SetString(ErrorObject, "unknown return code."); + return NULL; + break; + } + if (PyTuple_Size(t) == 1) { + r = PyTuple_GetItem(t, 0); + Py_INCREF(r); + Py_DECREF(t); + return r; + } + if (PyTuple_Size(t) == 0) { + r = Py_None; + Py_INCREF(r); + Py_DECREF(t); + return r; + } + return t; +} + + +static PyTypeObject Pftype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "pdbFunc", /*tp_name*/ + sizeof(pfobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)pf_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)pf_getattr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)pf_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)pf_call, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + NULL /* Documentation string */ +}; + +/* End of code for pdbFunc objects */ +/* -------------------------------------------------------- */ + + +static PyObject * +img_add_channel(self, args) + imgobject *self; + PyObject *args; +{ + chnobject *chn; + int pos; + + if (!PyArg_ParseTuple(args, "O!i:add_channel", &Chntype, &chn, &pos)) + return NULL; + gimp_image_add_channel(self->ID, chn->ID, pos); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_add_layer(self, args) + imgobject *self; + PyObject *args; +{ + layobject *lay; + int pos; + + if (!PyArg_ParseTuple(args, "O!i:add_layer", &Laytype, &lay, &pos)) + return NULL; + gimp_image_add_layer(self->ID, lay->ID, pos); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_add_layer_mask(self, args) + imgobject *self; + PyObject *args; +{ + layobject *lay; + chnobject *mask; + if (!PyArg_ParseTuple(args, "O!O!:add_layer_mask", &Laytype, &lay, + &Chntype, &mask)) + return NULL; + gimp_image_add_layer_mask(self->ID, lay->ID, mask->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_clean_all(self, args) + imgobject *self; + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":clean_all")) + return NULL; + gimp_image_clean_all(self->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_disable_undo(self, args) + imgobject *self; + PyObject *args; +{ + /*GParam *return_vals; + int nreturn_vals;*/ + + if (!PyArg_ParseTuple(args, ":disable_undo")) + return NULL; + /*return_vals = gimp_run_procedure("gimp_undo_push_group_start", + &nreturn_vals, PARAM_IMAGE, self->ID, + PARAM_END); + gimp_destroy_params(return_vals, nreturn_vals);*/ + gimp_image_disable_undo(self->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_enable_undo(self, args) + imgobject *self; + PyObject *args; +{ + /*GParam *return_vals; + int nreturn_vals;*/ + + if (!PyArg_ParseTuple(args, ":enable_undo")) + return NULL; + /*return_vals = gimp_run_procedure("gimp_undo_push_group_start", + &nreturn_vals, PARAM_IMAGE, self->ID, + PARAM_END); + gimp_destroy_params(return_vals, nreturn_vals);*/ + gimp_image_enable_undo(self->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_flatten(self, args) + imgobject *self; + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":flatten")) + return NULL; + gimp_image_flatten(self->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_lower_channel(self, args) + imgobject *self; + PyObject *args; +{ + chnobject *chn; + if (!PyArg_ParseTuple(args, "O!:lower_channel", &Chntype, &chn)) + return NULL; + gimp_image_lower_channel(self->ID, chn->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_lower_layer(self, args) + imgobject *self; + PyObject *args; +{ + layobject *lay; + if (!PyArg_ParseTuple(args, "O!:lower_layer", &Laytype, &lay)) + return NULL; + gimp_image_lower_layer(self->ID, lay->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_merge_visible_layers(self, args) + imgobject *self; + PyObject *args; +{ + gint32 id; + int merge; + if (!PyArg_ParseTuple(args, "i:merge_visible_layers", &merge)) + return NULL; + id = gimp_image_merge_visible_layers(self->ID, merge); + if (id == -1) { + PyErr_SetString(ErrorObject, "Can't merge layers."); + return NULL; + } + return (PyObject *)newlayobject(id); +} + + +static PyObject * +img_pick_correlate_layer(self, args) + imgobject *self; + PyObject *args; +{ + int x,y; + gint32 id; + + if (!PyArg_ParseTuple(args, "ii:pick_correlate_layer", &x, &y)) + return NULL; + id = gimp_image_pick_correlate_layer(self->ID, x, y); + if (id == -1) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *)newlayobject(id); +} + + +static PyObject * +img_raise_channel(self, args) + imgobject *self; + PyObject *args; +{ + chnobject *chn; + + if (!PyArg_ParseTuple(args, "O!:raise_channel", &Chntype, &chn)) + return NULL; + gimp_image_raise_channel(self->ID, chn->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_raise_layer(self, args) + imgobject *self; + PyObject *args; +{ + layobject *lay; + if (!PyArg_ParseTuple(args, "O!:raise_layer", &Laytype, &lay)) + return NULL; + gimp_image_raise_layer(self->ID, lay->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_remove_channel(self, args) + imgobject *self; + PyObject *args; +{ + chnobject *chn; + if (!PyArg_ParseTuple(args, "O!:remove_channel", &Chntype, &chn)) + return NULL; + gimp_image_remove_channel(self->ID, chn->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_remove_layer(self, args) + imgobject *self; + PyObject *args; +{ + layobject *lay; + if (!PyArg_ParseTuple(args, "O!:remove_layer", &Laytype, &lay)) + return NULL; + gimp_image_remove_layer(self->ID, lay->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_remove_layer_mask(self, args) + imgobject *self; + PyObject *args; +{ + layobject *lay; + int mode; + if (!PyArg_ParseTuple(args, "O!i:remove_layer_mask", &Laytype, &lay, + &mode)) + return NULL; + gimp_image_remove_layer_mask(self->ID, lay->ID, mode); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_resize(self, args) + imgobject *self; + PyObject *args; +{ + unsigned int new_w, new_h; + int offs_x, offs_y; + if (!PyArg_ParseTuple(args, "iiii:resize", &new_w, &new_h, + &offs_x, &offs_y)) + return NULL; + gimp_image_resize(self->ID, new_w, new_h, offs_x, offs_y); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_get_component_active(self, args) + imgobject *self; + PyObject *args; +{ + int comp; + if (!PyArg_ParseTuple(args, "i:get_component_active", &comp)) + return NULL; + return PyInt_FromLong((long) + gimp_image_get_component_active(self->ID, comp)); +} + + +static PyObject * +img_get_component_visible(self, args) + imgobject *self; + PyObject *args; +{ + int comp; + + if (!PyArg_ParseTuple(args, "i:get_component_visible", &comp)) + return NULL; + + return PyInt_FromLong((long) + gimp_image_get_component_visible(self->ID, comp)); +} + + +static PyObject * +img_set_component_active(self, args) + imgobject *self; + PyObject *args; +{ + int comp, a; + if (!PyArg_ParseTuple(args, "ii:set_component_active", &comp, &a)) + return NULL; + gimp_image_set_component_active(self->ID, comp, a); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +img_set_component_visible(self, args) + imgobject *self; + PyObject *args; +{ + int comp, v; + if (!PyArg_ParseTuple(args, "ii:set_component_visible", &comp, &v)) + return NULL; + gimp_image_set_component_visible(self->ID, comp, v); + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef GIMP_HAVE_PARASITES +static PyObject * +img_find_parasite(self, args) + imgobject *self; + PyObject *args; +{ + char *name; + if (!PyArg_ParseTuple(args, "s:find_parasite", &name)) + return NULL; + return (PyObject *)newparaobject(gimp_image_find_parasite(self->ID, name)); +} + +static PyObject * +img_attach_parasite(self, args) + imgobject *self; + PyObject *args; +{ + paraobject *parasite; + if (!PyArg_ParseTuple(args, "O!:attach_parasite", &Paratype, ¶site)) + return NULL; + gimp_image_attach_parasite(self->ID, parasite->para); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +img_attach_new_parasite(self, args) + imgobject *self; + PyObject *args; +{ + char *name, *data; + int flags, size; + if (!PyArg_ParseTuple(args, "sis#:attach_new_parasite", &name, &flags, + &data, &size)) + return NULL; + gimp_image_attach_new_parasite(self->ID, name, flags, size, data); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +img_detach_parasite(self, args) + imgobject *self; + PyObject *args; +{ + char *name; + if (!PyArg_ParseTuple(args, "s:detach_parasite", &name)) + return NULL; + gimp_image_detach_parasite(self->ID, name); + Py_INCREF(Py_None); + return Py_None; +} + +#endif +#ifdef GIMP_HAVE_RESOLUTION_INFO +/* gimp_image_set_resolution + * gimp_image_get_resolution + * gimp_set_unit + * gimp_get_unit + */ +#endif +#if GIMP_CHECK_VERSION(1,1,0) +static PyObject * +img_get_layer_by_tattoo(self, args) + imgobject *self; + PyObject *args; +{ + int tattoo; + if (!PyArg_ParseTuple(args, "i:get_layer_by_tattoo", &tattoo)) + return NULL; + return (PyObject *)newlayobject(gimp_image_get_layer_by_tattoo(self->ID, + tattoo)); +} + +static PyObject * +img_get_channel_by_tattoo(self, args) + imgobject *self; + PyObject *args; +{ + int tattoo; + if (!PyArg_ParseTuple(args, "i:get_channel_by_tattoo", &tattoo)) + return NULL; + return (PyObject *)newchnobject(gimp_image_get_channel_by_tattoo(self->ID, + tattoo)); +} + +static PyObject * +img_add_hguide(self, args) + imgobject *self; + PyObject *args; +{ + int ypos; + if (!PyArg_ParseTuple(args, "i:add_hguide", &ypos)) + return NULL; + gimp_image_add_hguide(self->ID, ypos); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +img_add_vguide(self, args) + imgobject *self; + PyObject *args; +{ + int xpos; + if (!PyArg_ParseTuple(args, "i:add_vguide", &xpos)) + return NULL; + gimp_image_add_vguide(self->ID, xpos); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +img_delete_guide(self, args) + imgobject *self; + PyObject *args; +{ + int guide; + if (!PyArg_ParseTuple(args, "i:delete_guide", &guide)) + return NULL; + gimp_image_delete_guide(self->ID, guide); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +img_find_next_guide(self, args) + imgobject *self; + PyObject *args; +{ + int guide; + if (!PyArg_ParseTuple(args, "i:find_next_guide", &guide)) + return NULL; + return PyInt_FromLong(gimp_image_find_next_guide(self->ID, guide)); +} + +static PyObject * +img_get_guide_orientation(self, args) + imgobject *self; + PyObject *args; +{ + int guide; + if (!PyArg_ParseTuple(args, "i:get_guide_orientation", &guide)) + return NULL; + return PyInt_FromLong(gimp_image_get_guide_orientation(self->ID, guide)); +} + +static PyObject * +img_get_guide_position(self, args) + imgobject *self; + PyObject *args; +{ + int guide; + if (!PyArg_ParseTuple(args, "i:get_guide_position", &guide)) + return NULL; + return PyInt_FromLong(gimp_image_get_guide_position(self->ID, guide)); +} +#endif + +static struct PyMethodDef img_methods[] = { + {"add_channel", (PyCFunction)img_add_channel, METH_VARARGS}, + {"add_layer", (PyCFunction)img_add_layer, METH_VARARGS}, + {"add_layer_mask", (PyCFunction)img_add_layer_mask, METH_VARARGS}, + {"clean_all", (PyCFunction)img_clean_all, METH_VARARGS}, + {"disable_undo", (PyCFunction)img_disable_undo, METH_VARARGS}, + {"enable_undo", (PyCFunction)img_enable_undo, METH_VARARGS}, + {"flatten", (PyCFunction)img_flatten, METH_VARARGS}, + {"lower_channel", (PyCFunction)img_lower_channel, METH_VARARGS}, + {"lower_layer", (PyCFunction)img_lower_layer, METH_VARARGS}, + {"merge_visible_layers", (PyCFunction)img_merge_visible_layers, METH_VARARGS}, + {"pick_correlate_layer", (PyCFunction)img_pick_correlate_layer, METH_VARARGS}, + {"raise_channel", (PyCFunction)img_raise_channel, METH_VARARGS}, + {"raise_layer", (PyCFunction)img_raise_layer, METH_VARARGS}, + {"remove_channel", (PyCFunction)img_remove_channel, METH_VARARGS}, + {"remove_layer", (PyCFunction)img_remove_layer, METH_VARARGS}, + {"remove_layer_mask", (PyCFunction)img_remove_layer_mask, METH_VARARGS}, + {"resize", (PyCFunction)img_resize, METH_VARARGS}, + {"get_component_active", (PyCFunction)img_get_component_active, METH_VARARGS}, + {"get_component_visible", (PyCFunction)img_get_component_visible, METH_VARARGS}, + {"set_component_active", (PyCFunction)img_set_component_active, METH_VARARGS}, + {"set_component_visible", (PyCFunction)img_set_component_visible, METH_VARARGS}, +#ifdef GIMP_HAVE_PARASITES + {"find_parasite", (PyCFunction)img_find_parasite, METH_VARARGS}, + {"attach_parasite", (PyCFunction)img_attach_parasite, METH_VARARGS}, + {"attach_new_parasite", (PyCFunction)img_attach_new_parasite,METH_VARARGS}, + {"detach_parasite", (PyCFunction)img_detach_parasite, METH_VARARGS}, +#endif +#if GIMP_CHECK_VERSION(1,1,0) + {"get_layer_by_tattoo",(PyCFunction)img_get_layer_by_tattoo,METH_VARARGS}, + {"get_channel_by_tattoo",(PyCFunction)img_get_channel_by_tattoo,METH_VARARGS}, + {"add_hguide", (PyCFunction)img_add_hguide, METH_VARARGS}, + {"add_vguide", (PyCFunction)img_add_vguide, METH_VARARGS}, + {"delete_guide", (PyCFunction)img_delete_guide, METH_VARARGS}, + {"find_next_guide", (PyCFunction)img_find_next_guide, METH_VARARGS}, + {"get_guide_orientation",(PyCFunction)img_get_guide_orientation,METH_VARARGS}, + {"get_guide_position", (PyCFunction)img_get_guide_position, METH_VARARGS}, +#endif + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +static imgobject * +newimgobject(gint32 ID) +{ + imgobject *self; + + if (ID == -1) { + Py_INCREF(Py_None); + return (imgobject *)Py_None; + } + self = PyObject_NEW(imgobject, &Imgtype); + if (self == NULL) + return NULL; + self->ID = ID; + return self; +} + + +static void +img_dealloc(self) + imgobject *self; +{ + /* XXXX Add your own cleanup code here */ + PyMem_DEL(self); +} + +static PyObject * +img_getattr(self, name) + imgobject *self; + char *name; +{ + gint32 *a, id; + guchar *c; + int n, i; + PyObject *ret; + if (!strcmp(name, "__members__")) + return Py_BuildValue( +#ifdef GIMP_HAVE_RESOLUTION_INFO + "[ssssssssssss]", +#else + "[ssssssssss]", +#endif + "ID", "active_channel", + "active_layer", "base_type", "channels", "cmap", + "filename", "floating_selection", + "height", "layers", +#ifdef GIMP_HAVE_RESOLUTION_INFO + "resolution", +#endif + "selection", +#ifdef GIMP_HAVE_RESOLUTION_INFO + "unit", +#endif + "width"); + if (!strcmp(name, "ID")) + return PyInt_FromLong(self->ID); + if (!strcmp(name, "active_channel")) { + id = gimp_image_get_active_channel(self->ID); + if (id == -1) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *)newchnobject(id); + } + if (!strcmp(name, "active_layer")) { + id = gimp_image_get_active_layer(self->ID); + if (id == -1) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *)newlayobject(id); + } + if (!strcmp(name, "base_type")) + return PyInt_FromLong(gimp_image_base_type(self->ID)); + if (!strcmp(name, "channels")) { + a = gimp_image_get_channels(self->ID, &n); + ret = PyList_New(n); + for (i = 0; i < n; i++) + PyList_SetItem(ret, i, (PyObject *)newchnobject(a[i])); + return ret; + } + if (!strcmp(name, "cmap")) { + c = gimp_image_get_cmap(self->ID, &n); + return PyString_FromStringAndSize(c, n * 3); + } + if (!strcmp(name, "filename")) + return PyString_FromString(gimp_image_get_filename(self->ID)); + if (!strcmp(name, "floating_selection")) { + id = gimp_image_floating_selection(self->ID); + if (id == -1) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *)newlayobject(id); + } + if (!strcmp(name, "layers")) { + a = gimp_image_get_layers(self->ID, &n); + ret = PyList_New(n); + for (i = 0; i < n; i++) + PyList_SetItem(ret, i, (PyObject *)newlayobject(a[i])); + return ret; + } + if (!strcmp(name, "selection")) + return (PyObject *)newchnobject( + gimp_image_get_selection(self->ID)); + if (!strcmp(name, "height")) + return PyInt_FromLong(gimp_image_height(self->ID)); + if (!strcmp(name, "width")) + return PyInt_FromLong(gimp_image_width(self->ID)); + +#ifdef GIMP_HAVE_RESOLUTION_INFO + if (!strcmp(name, "resolution")) { + double xres, yres; + gimp_image_get_resolution(self->ID, &xres, &yres); + return Py_BuildValue("(dd)", xres, yres); + } + if (!strcmp(name, "unit")) + return PyInt_FromLong(gimp_image_get_unit(self->ID)); +#endif + + return Py_FindMethod(img_methods, (PyObject *)self, name); +} + +static int +img_setattr(self, name, v) + imgobject *self; + char *name; + PyObject *v; +{ + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, "can not delete attributes."); + return -1; + } + if (!strcmp(name, "active_channel")) { + if (!chn_check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_image_set_active_channel(self->ID, ((chnobject *)v)->ID); + return 0; + } + if (!strcmp(name, "active_layer")) { + if (!lay_check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_image_set_active_layer(self->ID, ((layobject *)v)->ID); + return 0; + } + if (!strcmp(name, "cmap")) { + if (!PyString_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_image_set_cmap(self->ID, PyString_AsString(v), + PyString_Size(v) / 3); + return 0; + } + if (!strcmp(name, "filename")) { + if (!PyString_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_image_set_filename(self->ID, PyString_AsString(v)); + return 0; + } +#ifdef GIMP_HAVE_RESOLUTION_INFO + if (!strcmp(name, "resolution")) { + PyObject *xo, *yo; + if (!PySequence_Check(v) || + !PyFloat_Check(xo = PySequence_GetItem(v, 0)) || + !PyFloat_Check(yo = PySequence_GetItem(v, 1))) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_image_set_resolution(self->ID, PyFloat_AsDouble(xo), + PyFloat_AsDouble(yo)); + } + if (!strcmp(name, "unit")) { + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_image_set_unit(self->ID, PyInt_AsLong(v)); + } +#endif + if (!strcmp(name, "channels") || !strcmp(name, "layers") || + !strcmp(name, "selection") || !strcmp(name, "height") || + !strcmp(name, "base_type") || !strcmp(name, "width") || + !strcmp(name, "floating_selection") || !strcmp(name, "ID")) { + PyErr_SetString(PyExc_TypeError, "read-only attribute."); + return -1; + } + + return -1; +} + +static PyObject * +img_repr(self) + imgobject *self; +{ + PyObject *s; + + s = PyString_FromString("ID))); + PyString_ConcatAndDel(&s, PyString_FromString(">")); + return s; +} + +static int +img_cmp(self, other) + imgobject *self, *other; +{ + return self->ID - other->ID; +} + +static PyTypeObject Imgtype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "Image", /*tp_name*/ + sizeof(imgobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)img_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)img_getattr, /*tp_getattr*/ + (setattrfunc)img_setattr, /*tp_setattr*/ + (cmpfunc)img_cmp, /*tp_compare*/ + (reprfunc)img_repr, /*tp_repr*/ + &gobj_as_number, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + NULL /* Documentation string */ +}; + +/* End of code for Image objects */ +/* -------------------------------------------------------- */ + + +static struct PyMethodDef disp_methods[] = { + + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +static dispobject * +newdispobject(gint32 ID) +{ + dispobject *self; + + self = PyObject_NEW(dispobject, &Disptype); + if (self == NULL) + return NULL; + self->ID = ID; + return self; +} + + +static void +disp_dealloc(self) + dispobject *self; +{ + PyMem_DEL(self); +} + +static PyObject * +disp_getattr(self, name) + dispobject *self; + char *name; +{ + return Py_FindMethod(disp_methods, (PyObject *)self, name); +} + +static PyObject * +disp_repr(self) + dispobject *self; +{ + PyObject *s; + + s = PyString_FromString(""); + return s; +} + +static PyTypeObject Disptype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "Display", /*tp_name*/ + sizeof(dispobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)disp_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)disp_getattr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)disp_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + NULL /* Documentation string */ +}; + +/* End of code for Display objects */ +/* -------------------------------------------------------- */ + + +static ensure_drawable(self) + drwobject *self; +{ + if (!self->drawable) + self->drawable = gimp_drawable_get(self->ID); +} + +static PyObject * +drw_flush(self, args) + drwobject *self; + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":flush")) + return NULL; + ensure_drawable(self); + gimp_drawable_flush(self->drawable); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +drw_update(self, args) + drwobject *self; + PyObject *args; +{ + int x, y; + unsigned int w, h; + if (!PyArg_ParseTuple(args, "iiii:update", &x, &y, &w, &h)) + return NULL; + gimp_drawable_update(self->ID, x, y, w, h); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +drw_merge_shadow(self, args) + drwobject *self; + PyObject *args; +{ + int u; + if (!PyArg_ParseTuple(args, "i:merge_shadow", &u)) + return NULL; + gimp_drawable_merge_shadow(self->ID, u); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +drw_fill(self, args) + drwobject *self; + PyObject *args; +{ + int f; + if (!PyArg_ParseTuple(args, "i:fill", &f)) + return NULL; + gimp_drawable_fill(self->ID, f); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +drw_get_tile(self, args) + drwobject *self; + PyObject *args; +{ + GTile *t; + int shadow, r, c; + if (!PyArg_ParseTuple(args, "iii:get_tile", &shadow, &r, &c)) + return NULL; + ensure_drawable(self); + t = gimp_drawable_get_tile(self->drawable, shadow, r, c); + return (PyObject *)newtileobject(t, self); +} + +static PyObject * +drw_get_tile2(self, args) + drwobject *self; + PyObject *args; +{ + GTile *t; + int shadow, x, y; + if (!PyArg_ParseTuple(args, "iii:get_tile2", &shadow, &x ,&y)) + return NULL; + ensure_drawable(self); + t = gimp_drawable_get_tile2(self->drawable, shadow, x, y); + return (PyObject *)newtileobject(t, self); +} + +static PyObject * +drw_get_pixel_rgn(self, args) + drwobject *self; + PyObject *args; +{ + int x, y, w, h, dirty = 1, shadow = 0; + if (!PyArg_ParseTuple(args, "iiii|ii:get_pixel_rgn", &x,&y, + &w,&h, &dirty,&shadow)) + return NULL; + ensure_drawable(self); + return (PyObject *)newprobject(self, x, y, w, h, + dirty, shadow); +} + +#ifdef GIMP_HAVE_PARASITES +static PyObject * +drw_find_parasite(self, args) + drwobject *self; + PyObject *args; +{ + char *name; + if (!PyArg_ParseTuple(args, "s:find_parasite", &name)) + return NULL; + return (PyObject *)newparaobject(gimp_drawable_find_parasite(self->ID, + name)); +} + +static PyObject * +drw_attach_parasite(self, args) + drwobject *self; + PyObject *args; +{ + paraobject *parasite; + if (!PyArg_ParseTuple(args, "O!:attach_parasite", &Paratype, ¶site)) + return NULL; + gimp_drawable_attach_parasite(self->ID, parasite->para); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +drw_attach_new_parasite(self, args) + drwobject *self; + PyObject *args; +{ + char *name, *data; + int flags, size; + if (!PyArg_ParseTuple(args, "sis#:attach_new_parasite", &name, &flags, + &data, &size)) + return NULL; + gimp_drawable_attach_new_parasite(self->ID, name, flags, size, data); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +drw_detach_parasite(self, args) + drwobject *self; + PyObject *args; +{ + char *name; + if (!PyArg_ParseTuple(args, "s:detach_parasite", &name)) + return NULL; + gimp_drawable_detach_parasite(self->ID, name); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +/* for inclusion with the methods of layer and channel objects */ +#ifdef GIMP_HAVE_PARASITES +#define drw_methods() \ + {"flush", (PyCFunction)drw_flush, METH_VARARGS}, \ + {"update", (PyCFunction)drw_update, METH_VARARGS}, \ + {"merge_shadow", (PyCFunction)drw_merge_shadow, METH_VARARGS}, \ + {"fill", (PyCFunction)drw_fill, METH_VARARGS}, \ + {"get_tile", (PyCFunction)drw_get_tile, METH_VARARGS}, \ + {"get_tile2", (PyCFunction)drw_get_tile2, METH_VARARGS}, \ + {"get_pixel_rgn", (PyCFunction)drw_get_pixel_rgn, METH_VARARGS}, \ + {"find_parasite", (PyCFunction)img_find_parasite, METH_VARARGS}, \ + {"attach_parasite", (PyCFunction)img_attach_parasite, METH_VARARGS},\ + {"attach_new_parasite",(PyCFunction)img_attach_new_parasite,METH_VARARGS},\ + {"detach_parasite", (PyCFunction)img_detach_parasite, METH_VARARGS} +#else +#define drw_methods() \ + {"flush", (PyCFunction)drw_flush, METH_VARARGS}, \ + {"update", (PyCFunction)drw_update, METH_VARARGS}, \ + {"merge_shadow", (PyCFunction)drw_merge_shadow, METH_VARARGS}, \ + {"fill", (PyCFunction)drw_fill, METH_VARARGS}, \ + {"get_tile", (PyCFunction)drw_get_tile, METH_VARARGS}, \ + {"get_tile2", (PyCFunction)drw_get_tile2, METH_VARARGS}, \ + {"get_pixel_rgn", (PyCFunction)drw_get_pixel_rgn, METH_VARARGS} +#endif +/* ---------- */ + + +static drwobject * +newdrwobject(GDrawable *d, gint32 ID) +{ + drwobject *self; + + if (d == NULL && ID == -1) { + Py_INCREF(Py_None); + return (drwobject *)Py_None; + } + if (d != NULL) + ID = d->id; + /* create the appropriate object type */ + if (gimp_drawable_layer(ID)) + self = newlayobject(ID); + else + self = newchnobject(ID); + + if (self == NULL) + return NULL; + + self->drawable = d; + + return self; +} + +/* End of code for Drawable objects */ +/* -------------------------------------------------------- */ + + +static PyObject * +lay_copy(self, args) + layobject *self; + PyObject *args; +{ + int add_alpha = 0, nreturn_vals; + GParam *return_vals; + gint32 id; + + /* start of long convoluted (working) layer_copy */ + if (!PyArg_ParseTuple(args, "|i:copy", &add_alpha)) + return NULL; + + return_vals = gimp_run_procedure("gimp_layer_copy", + &nreturn_vals, + PARAM_LAYER, self->ID, + PARAM_INT32, add_alpha, + PARAM_END); + if (return_vals[0].data.d_status != STATUS_SUCCESS) { + PyErr_SetString(ErrorObject, "can't create new layer"); + return NULL; + } + id = return_vals[1].data.d_layer; + gimp_destroy_params(return_vals, nreturn_vals); + return (PyObject *)newlayobject(id); + + /* This simple version of the code doesn't seem to work */ + /* return (PyObject *)newlayobject(gimp_layer_copy(self->ID));*/ +} + + +static PyObject * +lay_add_alpha(self, args) + layobject *self; + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":add_alpha")) + return NULL; + gimp_layer_add_alpha(self->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +lay_create_mask(self, args) + layobject *self; + PyObject *args; +{ + int type; + + if (!PyArg_ParseTuple(args, "i:create_mask", &type)) + return NULL; + return (PyObject *)newchnobject(gimp_layer_create_mask(self->ID,type)); +} + + +static PyObject * +lay_resize(self, args) + layobject *self; + PyObject *args; +{ + unsigned int new_h, new_w; + int offs_x, offs_y; + if (!PyArg_ParseTuple(args, "iiii:resize", &new_w, &new_h, + &offs_x, &offs_y)) + return NULL; + gimp_layer_resize(self->ID, new_w, new_h, offs_x, offs_y); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +lay_scale(self, args) + layobject *self; + PyObject *args; +{ + unsigned int new_w, new_h; + int local_origin; + if (!PyArg_ParseTuple(args, "iii:scale", &new_w, &new_h, + &local_origin)) + return NULL; + gimp_layer_scale(self->ID, new_w, new_h, local_origin); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +lay_translate(self, args) + layobject *self; + PyObject *args; +{ + int offs_x, offs_y; + if (!PyArg_ParseTuple(args, "ii:translate", &offs_x, &offs_y)) + return NULL; + gimp_layer_translate(self->ID, offs_x, offs_y); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +lay_set_offsets(self, args) + layobject *self; + PyObject *args; +{ + int offs_x, offs_y; + if (!PyArg_ParseTuple(args, "ii:set_offsets", &offs_x, &offs_y)) + return NULL; + gimp_layer_set_offsets(self->ID, offs_x, offs_y); + Py_INCREF(Py_None); + return Py_None; +} + +#if GIMP_CHECK_VERSION(1,1,0) +static PyObject * +lay_get_tattoo(self, args) + layobject *self; + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":get_tattoo")) + return NULL; + return PyInt_FromLong(gimp_layer_get_tattoo(self->ID)); +} +#endif + +static struct PyMethodDef lay_methods[] = { + {"copy", (PyCFunction)lay_copy, METH_VARARGS}, + {"add_alpha", (PyCFunction)lay_add_alpha, METH_VARARGS}, + {"create_mask", (PyCFunction)lay_create_mask, METH_VARARGS}, + {"resize", (PyCFunction)lay_resize, METH_VARARGS}, + {"scale", (PyCFunction)lay_scale, METH_VARARGS}, + {"translate", (PyCFunction)lay_translate, METH_VARARGS}, + {"set_offsets", (PyCFunction)lay_set_offsets, METH_VARARGS}, +#if GIMP_CHECK_VERSION(1,1,0) + {"get_tattoo", (PyCFunction)lay_get_tattoo, METH_VARARGS}, +#endif + + drw_methods(), + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +static layobject * +newlayobject(gint32 ID) +{ + layobject *self; + + if (ID == -1) { + Py_INCREF(Py_None); + return (layobject *)Py_None; + } + self = PyObject_NEW(layobject, &Laytype); + if (self == NULL) + return NULL; + self->ID = ID; + self->drawable = NULL; + return self; +} + + +static void +lay_dealloc(self) + layobject *self; +{ + if (self->drawable) + gimp_drawable_detach(self->drawable); + PyMem_DEL(self); +} + +static PyObject * +lay_getattr(self, name) + layobject *self; + char *name; +{ + gint32 id; + + if (!strcmp(name, "__members__")) + return Py_BuildValue("[sssssssssssssssssss]", "ID", "apply_mask", + "bpp", "edit_mask", "has_alpha", "height", + "image", "is_color", "is_colour", + "is_floating_selection", "is_gray", "is_grey", + "is_indexed", "mask", "mask_bounds", + "mode", "name", "offsets", "opacity", + "preserve_transparency", "show_mask", "type", + "visible", "width"); + if (!strcmp(name, "ID")) + return PyInt_FromLong(self->ID); + if (!strcmp(name, "bpp")) + return PyInt_FromLong((long) gimp_layer_bpp(self->ID)); + if (!strcmp(name, "has_alpha")) + return PyInt_FromLong(gimp_drawable_has_alpha(self->ID)); + if (!strcmp(name, "height")) + return PyInt_FromLong((long) gimp_layer_height(self->ID)); + if (!strcmp(name, "image")) { + id = gimp_layer_get_image_id(self->ID); + if (id == -1) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *)newimgobject(id); + } + if (!strcmp(name, "is_color") || !strcmp(name, "is_colour")) + return PyInt_FromLong(gimp_drawable_color(self->ID)); + if (!strcmp(name, "is_floating_selection")) + return PyInt_FromLong( + gimp_layer_is_floating_selection(self->ID)); + if (!strcmp(name, "is_gray") || !strcmp(name, "is_grey")) + return PyInt_FromLong(gimp_drawable_gray(self->ID)); + if (!strcmp(name, "is_indexed")) + return PyInt_FromLong(gimp_drawable_indexed(self->ID)); + if (!strcmp(name, "mask")) { + id = gimp_layer_get_mask_id(self->ID); + if (id == -1) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *)newchnobject(id); + } + if (!strcmp(name, "mask_bounds")) { + gint x1, y1, x2, y2; + gimp_drawable_mask_bounds(self->ID, &x1, &y1, &x2, &y2); + return Py_BuildValue("(iiii)", x1, y1, x2, y2); + } + if (!strcmp(name, "apply_mask")) + return PyInt_FromLong((long) gimp_layer_get_apply_mask( + self->ID)); + if (!strcmp(name, "edit_mask")) + return PyInt_FromLong((long) gimp_layer_get_edit_mask( + self->ID)); + if (!strcmp(name, "mode")) + return PyInt_FromLong((long) gimp_layer_get_mode(self->ID)); + if (!strcmp(name, "name")) + return PyString_FromString(gimp_layer_get_name(self->ID)); + if (!strcmp(name, "offsets")) { + gint x, y; + gimp_drawable_offsets(self->ID, &x, &y); + return Py_BuildValue("(ii)", x, y); + } + if (!strcmp(name, "opacity")) + return PyFloat_FromDouble((double) gimp_layer_get_opacity( + self->ID)); + if (!strcmp(name, "preserve_transparency")) + return PyInt_FromLong((long) + gimp_layer_get_preserve_transparency(self->ID)); + if (!strcmp(name, "show_mask")) + return PyInt_FromLong((long) gimp_layer_get_show_mask( + self->ID)); + if (!strcmp(name, "type")) + return PyInt_FromLong((long) gimp_layer_type(self->ID)); + if (!strcmp(name, "visible")) + return PyInt_FromLong((long) gimp_layer_get_visible(self->ID)); + if (!strcmp(name, "width")) + return PyInt_FromLong((long) gimp_layer_width(self->ID)); + return Py_FindMethod(lay_methods, (PyObject *)self, name); +} + +static int +lay_setattr(self, name, v) + layobject *self; + char *name; + PyObject *v; +{ + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, "can not delete attributes."); + return -1; + } + /* Set attribute 'name' to value 'v'. v==NULL means delete */ + if (!strcmp(name, "apply_mask")) { + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_layer_set_apply_mask(self->ID, PyInt_AsLong(v)); + return 0; + } + if (!strcmp(name, "edit_mask")) { + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_layer_set_edit_mask(self->ID, PyInt_AsLong(v)); + return 0; + } + if (!strcmp(name, "mode")) { + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_layer_set_mode(self->ID, (GLayerMode)PyInt_AsLong(v)); + return 0; + } + if (!strcmp(name, "name")) { + if (!PyString_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_layer_set_name(self->ID, PyString_AsString(v)); + return 0; + } + if (!strcmp(name, "opacity")) { + if (!PyFloat_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_layer_set_opacity(self->ID, PyFloat_AsDouble(v)); + return 0; + } + if (!strcmp(name, "preserve_transparency")) { + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_layer_set_preserve_transparency(self->ID, + PyInt_AsLong(v)); + return 0; + } + if (!strcmp(name, "show_mask")) { + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_layer_set_show_mask(self->ID, PyInt_AsLong(v)); + return 0; + } + if (!strcmp(name, "visible")) { + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_layer_set_visible(self->ID, PyInt_AsLong(v)); + return 0; + } + if (!strcmp(name, "ID") || + !strcmp(name, "bpp") || !strcmp(name, "height") || + !strcmp(name, "image") || !strcmp(name, "mask") || + !strcmp(name, "type") || !strcmp(name, "width") || + !strcmp(name, "is_floating_selection") || + !strcmp(name, "offsets") || !strcmp(name, "mask_bounds") || + !strcmp(name, "has_alpha") || !strcmp(name, "is_color") || + !strcmp(name, "is_colour") || !strcmp(name, "is_gray") || + !strcmp(name, "is_grey") || !strcmp(name, "is_indexed") || + !strcmp(name, "__members__")) { + PyErr_SetString(PyExc_TypeError, "read-only attribute."); + return -1; + } + return -1; +} + +static PyObject * +lay_repr(self) + layobject *self; +{ + PyObject *s; + + s = PyString_FromString("ID))); + PyString_ConcatAndDel(&s, PyString_FromString(">")); + return s; +} + +static int +lay_cmp(self, other) + layobject *self, *other; +{ + return self->ID - other->ID; +} + +static PyTypeObject Laytype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "Layer", /*tp_name*/ + sizeof(layobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)lay_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)lay_getattr, /*tp_getattr*/ + (setattrfunc)lay_setattr, /*tp_setattr*/ + (cmpfunc)lay_cmp, /*tp_compare*/ + (reprfunc)lay_repr, /*tp_repr*/ + &gobj_as_number, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + NULL /* Documentation string */ +}; + +/* End of code for Layer objects */ +/* -------------------------------------------------------- */ + + +static PyObject * +chn_copy(self, args) + chnobject *self; + PyObject *args; +{ + gint32 id; + + if (!PyArg_ParseTuple(args, ":copy")) + return NULL; + id = gimp_channel_copy(self->ID); + if (id == -1) { + PyErr_SetString(ErrorObject, "can't copy channel"); + return NULL; + } + return (PyObject *)newchnobject(id); +} + +#if GIMP_CHECK_VERSION(1,1,0) +static PyObject * +chn_get_tattoo(self, args) + chnobject *self; + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":get_tattoo")) + return NULL; + return PyInt_FromLong(gimp_channel_get_tattoo(self->ID)); +} +#endif + +static struct PyMethodDef chn_methods[] = { + {"copy", (PyCFunction)chn_copy, METH_VARARGS}, +#if GIMP_CHECK_VERSION(1,1,0) + {"get_tattoo", (PyCFunction)chn_get_tattoo, METH_VARARGS}, +#endif + drw_methods(), + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +static chnobject * +newchnobject(gint32 ID) +{ + chnobject *self; + + if (ID == -1) { + Py_INCREF(Py_None); + return (chnobject *)Py_None; + } + self = PyObject_NEW(chnobject, &Chntype); + if (self == NULL) + return NULL; + self->ID = ID; + self->drawable = NULL; + return self; +} + +static void +chn_dealloc(self) + chnobject *self; +{ + if (self->drawable) + gimp_drawable_detach(self->drawable); + PyMem_DEL(self); +} + +static PyObject * +chn_getattr(self, name) + chnobject *self; + char *name; +{ + unsigned char r, g, b; + gint32 id; + + if (!strcmp(name, "__members__")) + return Py_BuildValue("[ssssssssssssssssssssss]", "ID", "bpp", "color", + "colour", "has_alpha", "height", "image", + "is_color", "is_colour", "is_gray", "is_grey", + "is_indexed", "layer", + "layer_mask", "mask_bounds", "name", "offsets", + "opacity", "show_masked", "type", "visible", + "width"); + if (!strcmp(name, "ID")) + return PyInt_FromLong(self->ID); + if (!strcmp(name, "bpp")) + return PyInt_FromLong(gimp_drawable_bpp(self->ID)); + if (!strcmp(name, "color") || !strcmp(name, "colour")) { + gimp_channel_get_color(self->ID, &r, &g, &b); + return Py_BuildValue("(iii)", (long)r, (long)g, (long)b); + } + if (!strcmp(name, "has_alpha")) + return PyInt_FromLong(gimp_drawable_has_alpha(self->ID)); + if (!strcmp(name, "height")) + return PyInt_FromLong((long)gimp_channel_height(self->ID)); + if (!strcmp(name, "image")) { + id = gimp_channel_get_image_id(self->ID); + if (id == -1) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *)newimgobject(id); + } + if (!strcmp(name, "is_color") || !strcmp(name, "is_colour")) + return PyInt_FromLong(gimp_drawable_color(self->ID)); + if (!strcmp(name, "is_gray") || !strcmp(name, "is_grey")) + return PyInt_FromLong(gimp_drawable_gray(self->ID)); + if (!strcmp(name, "is_indexed")) + return PyInt_FromLong(gimp_drawable_indexed(self->ID)); + if (!strcmp(name, "layer")) { + id = gimp_channel_get_layer_id(self->ID); + if (id == -1) { + Py_INCREF(Py_None); + return Py_None; + } + return (PyObject *)newlayobject(id); + } + if (!strcmp(name, "layer_mask")) + return PyInt_FromLong(gimp_drawable_layer_mask(self->ID)); + if (!strcmp(name, "mask_bounds")) { + gint x1, y1, x2, y2; + gimp_drawable_mask_bounds(self->ID, &x1, &y1, &x2, &y2); + return Py_BuildValue("(iiii)", x1, y1, x2, y2); + } + if (!strcmp(name, "name")) + return PyString_FromString(gimp_channel_get_name(self->ID)); + if (!strcmp(name, "offsets")) { + gint x, y; + gimp_drawable_offsets(self->ID, &x, &y); + return Py_BuildValue("(ii)", x, y); + } + if (!strcmp(name, "opacity")) + return PyFloat_FromDouble(gimp_channel_get_opacity(self->ID)); + if (!strcmp(name, "show_masked")) + return PyInt_FromLong(gimp_channel_get_show_masked(self->ID)); + if (!strcmp(name, "type")) + return PyInt_FromLong(gimp_drawable_type(self->ID)); + if (!strcmp(name, "visible")) + return PyInt_FromLong(gimp_channel_get_visible(self->ID)); + if (!strcmp(name, "width")) + return PyInt_FromLong(gimp_channel_width(self->ID)); + return Py_FindMethod(chn_methods, (PyObject *)self, name); +} + +static int +chn_setattr(self, name, v) + chnobject *self; + char *name; + PyObject *v; +{ + PyObject *r, *g, *b; + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, "can not delete attributes."); + return -1; + } + if (!strcmp(name, "color") || !strcmp(name, "colour")) { + if (!PySequence_Check(v) || PySequence_Length(v) < 3) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + r = PySequence_GetItem(v, 0); + g = PySequence_GetItem(v, 1); + b = PySequence_GetItem(v, 2); + if (!PyInt_Check(r) || !PyInt_Check(g) || !PyInt_Check(b)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + Py_DECREF(r); Py_DECREF(g); Py_DECREF(b); + return -1; + } + gimp_channel_set_color(self->ID, PyInt_AsLong(r), + PyInt_AsLong(g), PyInt_AsLong(b)); + Py_DECREF(r); Py_DECREF(g); Py_DECREF(b); + return 0; + } + if (!strcmp(name, "name")) { + if (!PyString_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_channel_set_name(self->ID, PyString_AsString(v)); + return 0; + } + if (!strcmp(name, "opacity")) { + if (!PyFloat_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_channel_set_opacity(self->ID, PyFloat_AsDouble(v)); + return 0; + } + /* if (!strcmp(name, "show_masked")) { + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_channel_set_show_masked(self->ID, PyInt_AsLong(v)); + return 0; + } + */ if (!strcmp(name, "visible")) { + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, "type mis-match."); + return -1; + } + gimp_channel_set_visible(self->ID, PyInt_AsLong(v)); + return 0; + } + if (!strcmp(name, "height") || !strcmp(name, "image") || + !strcmp(name, "layer") || !strcmp(name, "width") || + !strcmp(name, "ID") || !strcmp(name, "bpp") || + !strcmp(name, "layer_mask") || !strcmp(name, "mask_bounds") || + !strcmp(name, "offsets") || !strcmp(name, "type") || + !strcmp(name, "has_alpha") || !strcmp(name, "is_color") || + !strcmp(name, "is_colour") || !strcmp(name, "is_gray") || + !strcmp(name, "is_grey") || !strcmp(name, "is_indexed") || + !strcmp(name, "__members__")) { + PyErr_SetString(PyExc_TypeError, "read-only attribute."); + return -1; + } + return -1; +} + +static PyObject * +chn_repr(self) + chnobject *self; +{ + PyObject *s; + + s = PyString_FromString("ID))); + PyString_ConcatAndDel(&s, PyString_FromString(">")); + return s; +} + +static int +chn_cmp(self, other) + chnobject *self, *other; +{ + return self->ID - other->ID; +} + +static PyTypeObject Chntype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "Channel", /*tp_name*/ + sizeof(chnobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)chn_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)chn_getattr, /*tp_getattr*/ + (setattrfunc)chn_setattr, /*tp_setattr*/ + (cmpfunc)chn_cmp, /*tp_compare*/ + (reprfunc)chn_repr, /*tp_repr*/ + &gobj_as_number, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + NULL /* Documentation string */ +}; + +/* End of code for Channel objects */ +/* -------------------------------------------------------- */ + + +static PyObject * +tile_flush(self, args) + tileobject *self; + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":flush")) + return NULL; + gimp_tile_flush(self->tile); + Py_INCREF(Py_None); + return Py_None; +} + + +static struct PyMethodDef tile_methods[] = { + {"flush", (PyCFunction)tile_flush, METH_VARARGS}, + + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +static tileobject * +newtileobject(t, drw) + GTile *t; + drwobject *drw; +{ + tileobject *self; + + self = PyObject_NEW(tileobject, &Tiletype); + if (self == NULL) + return NULL; + gimp_tile_ref(t); + self->tile = t; + + Py_INCREF(drw); + self->drawable = drw; + + return self; +} + + +static void +tile_dealloc(self) + tileobject *self; +{ + gimp_tile_unref(self->tile, FALSE); + Py_DECREF(self->drawable); + PyMem_DEL(self); +} + +#define OFF(x) offsetof(GTile, x) + +static struct memberlist tile_memberlist[] = { + {"ewidth", T_UINT, OFF(ewidth), RO}, + {"eheight", T_UINT, OFF(eheight), RO}, + {"bpp", T_UINT, OFF(bpp), RO}, + {"ref_count", T_USHORT, OFF(ref_count), RO}, + {"dirty", T_INT, 0, RO}, + {"shadow", T_INT, 0, RO}, + {"drawable", T_INT, OFF(drawable), RO}, + {NULL} /* Sentinel */ +}; + +#undef OFF + +static PyObject * +tile_getattr(self, name) + tileobject *self; + char *name; +{ + PyObject *rv; + + if (!strcmp(name, "dirty")) + return PyInt_FromLong(self->tile->dirty); + if (!strcmp(name, "shadow")) + return PyInt_FromLong(self->tile->shadow); + if (!strcmp(name, "drawable")) + return (PyObject *)newdrwobject(self->tile->drawable, 0); + rv = PyMember_Get((char *)self->tile, tile_memberlist, name); + if (rv) + return rv; + PyErr_Clear(); + return Py_FindMethod(tile_methods, (PyObject *)self, name); +} + +static PyObject * +tile_repr(self) + tileobject *self; +{ + PyObject *s; + if (self->tile->shadow) + s = PyString_FromString("tile->drawable->id))); + PyString_ConcatAndDel(&s, PyString_FromString(">")); + return s; +} + +static int +tile_length(self) + tileobject *self; +{ + return self->tile->ewidth * self->tile->eheight; +} + +static PyObject * +tile_subscript(self, sub) + tileobject *self; + PyObject *sub; +{ + GTile *tile = self->tile; + int bpp = tile->bpp; + long x, y; + + if (PyInt_Check(sub)) { + x = PyInt_AsLong(sub); + if (x < 0 || x >= tile->ewidth * tile->eheight) { + PyErr_SetString(PyExc_IndexError,"index out of range"); + return NULL; + } + return PyString_FromStringAndSize(tile->data + bpp * x, bpp); + } + if (PyTuple_Check(sub)) { + if (!PyArg_ParseTuple(sub, "ll", &x, &y)) + return NULL; + if (x < 0 || y < 0 || x >= tile->ewidth || y>=tile->eheight) { + PyErr_SetString(PyExc_IndexError,"index out of range"); + return NULL; + } + return PyString_FromStringAndSize(tile->data + bpp * (x + + y * tile->ewidth), bpp); + } + PyErr_SetString(PyExc_TypeError, "tile subscript not int or 2-tuple"); + return NULL; +} + +static int +tile_ass_sub(self, v, w) + tileobject *self; + PyObject *v, *w; +{ + GTile *tile = self->tile; + int bpp = tile->bpp, i; + long x, y; + char *pix, *data; + + if (w == NULL) { + PyErr_SetString(PyExc_TypeError, + "can not delete pixels in tile"); + return -1; + } + if (!PyString_Check(w) && PyString_Size(w) == bpp) { + PyErr_SetString(PyExc_TypeError, "invalid subscript"); + return -1; + } + pix = PyString_AsString(w); + if (PyInt_Check(v)) { + x = PyInt_AsLong(v); + if (x < 0 || x >= tile->ewidth * tile->eheight) { + PyErr_SetString(PyExc_IndexError,"index out of range"); + return -1; + } + data = tile->data + x * bpp; + for (i = 0; i < bpp; i++) + data[i] = pix[i]; + tile->dirty = TRUE; + return 0; + } + if (PyTuple_Check(v)) { + if (!PyArg_ParseTuple(v, "ll", &x, &y)) + return -1; + if (x < 0 || y < 0 || x >= tile->ewidth || y>=tile->eheight) { + PyErr_SetString(PyExc_IndexError,"index out of range"); + return -1; + } + data = tile->data + bpp * (x + y * tile->ewidth); + for (i = 0; i < bpp; i++) + data[i] = pix[i]; + tile->dirty = TRUE; + return 0; + } + PyErr_SetString(PyExc_TypeError, "tile subscript not int or 2-tuple"); + return -1; +} + +static PyMappingMethods tile_as_mapping = { + (inquiry)tile_length, /*length*/ + (binaryfunc)tile_subscript, /*subscript*/ + (objobjargproc)tile_ass_sub, /*ass_sub*/ +}; + +static PyTypeObject Tiletype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "Tile", /*tp_name*/ + sizeof(tileobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)tile_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)tile_getattr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)tile_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &tile_as_mapping, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + NULL /* Documentation string */ +}; + +/* End of code for Tile objects */ +/* -------------------------------------------------------- */ + + +static PyObject * +pr_resize(self, args) + probject *self; + PyObject *args; +{ + int x, y, w, h; + if (!PyArg_ParseTuple(args, "iiii:resize", &x, &y, &w, &h)) + return NULL; + gimp_pixel_rgn_resize(&(self->pr), x, y, w, h); + Py_INCREF(Py_None); + return Py_None; +} + + + +static struct PyMethodDef pr_methods[] = { + {"resize", (PyCFunction)pr_resize, METH_VARARGS}, + + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +static probject * +newprobject(drawable, x, y, width, height, dirty, shadow) + drwobject *drawable; + int x, y, width, height, dirty, shadow; +{ + probject *self; + + self = PyObject_NEW(probject, &Prtype); + if (self == NULL) + return NULL; + gimp_pixel_rgn_init(&(self->pr), drawable->drawable, x, y, width, height, + dirty, shadow); + self->drawable = drawable; + Py_INCREF(drawable); + return self; +} + + +static void +pr_dealloc(self) + probject *self; +{ + Py_DECREF(self->drawable); + PyMem_DEL(self); +} + +/* Code to access pr objects as mappings */ + +static int +pr_length(self) + probject *self; +{ + PyErr_SetString(ErrorObject, "Can't get size of pixel region."); + return -1; +} + +static PyObject * +pr_subscript(self, key) + probject *self; + PyObject *key; +{ + GPixelRgn *pr = &(self->pr); + int bpp = pr->bpp; + PyObject *x, *y; + int x1, y1, x2, y2, xs, ys; + + if (!PyTuple_Check(key) || PyTuple_Size(key) != 2) { + PyErr_SetString(PyExc_TypeError,"subscript must be a 2-tuple."); + return NULL; + } + if (!PyArg_ParseTuple(key, "OO", &x, &y)) + return NULL; + if (PyInt_Check(x)) { + x1 = PyInt_AsLong(x); + if (pr->x > x1 || x1 >= pr->x + pr->w) { + PyErr_SetString(PyExc_IndexError, + "x subscript out of range"); + return NULL; + } + if (PyInt_Check(y)) { + char buf[bpp]; + + y1 = PyInt_AsLong(y); + if (pr->y > y1 || y1 >= pr->y + pr->h) { + PyErr_SetString(PyExc_IndexError, + "y subscript out of range"); + return NULL; + } + gimp_pixel_rgn_get_pixel(pr, buf, x1, y1); + return PyString_FromStringAndSize(buf, bpp); + } else if (PySlice_Check(y)) + if (PySlice_GetIndices((PySliceObject *)y, + pr->y + pr->h, &y1, &y2, &ys) || + (y1 != 0 && pr->y > y1) || + pr->y > y2 || ys != 1) { + PyErr_SetString(PyExc_IndexError, + "invalid y slice"); + return NULL; + } else { + char buf[bpp * (y2 - y1)]; + + if (y1 == 0) y1 = pr->y; + gimp_pixel_rgn_get_col(pr, buf, x1, y1, y2-y1); + return PyString_FromStringAndSize(buf, + bpp * (y2 - y1)); + } + else { + PyErr_SetString(PyExc_TypeError,"invalid y subscript"); + return NULL; + } + } else if (PySlice_Check(x)) { + if (PySlice_GetIndices((PySliceObject *)x, pr->x + pr->w, + &x1, &x2, &xs) || (x1 != 0 && pr->x > x1) || + pr->x > x2 || xs != 1) { + PyErr_SetString(PyExc_IndexError, "invalid x slice"); + return NULL; + } + if (x1 == 0) x1 = pr->x; + if (PyInt_Check(y)) { + char buf[bpp * (x2 - x1)]; + + y1 = PyInt_AsLong(y); + if (pr->y > y1 || y1 >= pr->y + pr->h) { + PyErr_SetString(PyExc_IndexError, + "y subscript out of range"); + return NULL; + } + gimp_pixel_rgn_get_row(pr, buf, x1, y1, x2 - x1); + return PyString_FromStringAndSize(buf, bpp * (x2-x1)); + } else if (PySlice_Check(y)) + if (PySlice_GetIndices((PySliceObject *)y, + pr->y + pr->h, &y1, &y2, &ys) || + (y1 != 0 && pr->y) > y1 || + pr->y > y2 || ys != 1) { + PyErr_SetString(PyExc_IndexError, + "invalid y slice"); + return NULL; + } else { + char buf[bpp * (x2 - x1) * (y2 - y1)]; + + if (y1 == 0) y1 = pr->y; + gimp_pixel_rgn_get_rect(pr, buf, x1, y1, + x2 - x1, y2 - y1); + return PyString_FromStringAndSize(buf, + bpp * (x2 - x1) * (y2 - y1)); + } + else { + PyErr_SetString(PyExc_TypeError,"invalid y subscript"); + return NULL; + } + } else { + PyErr_SetString(PyExc_TypeError, "invalid x subscript"); + return NULL; + } +} + +static int +pr_ass_sub(self, v, w) + probject *self; + PyObject *v, *w; +{ + GPixelRgn *pr = &(self->pr); + int bpp = pr->bpp; + PyObject *x, *y; + char *buf; + int len, x1, x2, xs, y1, y2, ys; + + if (w == NULL) { + PyErr_SetString(PyExc_TypeError, "can't delete subscripts."); + return -1; + } + if (!PyString_Check(w)) { + PyErr_SetString(PyExc_TypeError, + "must assign string to subscript"); + return -1; + } + if (!PyTuple_Check(v) || PyTuple_Size(v) != 2) { + PyErr_SetString(PyExc_TypeError,"subscript must be a 2-tuple"); + return -1; + } + if (!PyArg_ParseTuple(v, "OO", &x, &y)) + return -1; + buf = PyString_AsString(w); + len = PyString_Size(w); + if (PyInt_Check(x)) { + x1 = PyInt_AsLong(x); + if (pr->x > x1 || x1 >= pr->x + pr->w) { + PyErr_SetString(PyExc_IndexError, + "x subscript out of range"); + return -1; + } + if (PyInt_Check(y)) { + y1 = PyInt_AsLong(y); + if (pr->y > y1 || y1 >= pr->y + pr->h) { + PyErr_SetString(PyExc_IndexError, + "y subscript out of range"); + return -1; + } + if (len != bpp) { + PyErr_SetString(PyExc_TypeError, + "string is wrong length"); + return -1; + } + gimp_pixel_rgn_set_pixel(pr, buf, x1, y1); + return 0; + } else if (PySlice_Check(y)) { + if (PySlice_GetIndices((PySliceObject *)y, + pr->y + pr->h, &y1, &y2, &ys) || + (y1 != 0 && pr->y > y1) || + pr->y > y2 || ys != 1) { + PyErr_SetString(PyExc_IndexError, + "invalid y slice"); + return -1; + } + if (y1 == 0) y1 = pr->y; + if (len != bpp * (y2 - y1)) { + PyErr_SetString(PyExc_TypeError, + "string is wrong length"); + return -1; + } + gimp_pixel_rgn_set_col(pr, buf, x1, y1, y2 - y1); + return 0; + } else { + PyErr_SetString(PyExc_IndexError,"invalid y subscript"); + return -1; + } + } else if (PySlice_Check(x)) { + if (PySlice_GetIndices((PySliceObject *)x, pr->x + pr->w, + &x1, &x2, &xs) || (x1 != 0 && pr->x > x1) || + pr->x > x2 || xs != 1) { + PyErr_SetString(PyExc_IndexError, "invalid x slice"); + return -1; + } + if (x1 == 0) x1 = pr->x; + if (PyInt_Check(y)) { + y1 = PyInt_AsLong(y); + if (pr->y > y1 || y1 >= pr->y + pr->h) { + PyErr_SetString(PyExc_IndexError, + "y subscript out of range"); + return -1; + } + if (len != bpp * (x2 - x1)) { + PyErr_SetString(PyExc_TypeError, + "string is wrong length"); + return -1; + } + gimp_pixel_rgn_set_row(pr, buf, x1, y1, x2 - x1); + return 0; + } else if (PySlice_Check(y)) { + if (PySlice_GetIndices((PySliceObject *)y, + pr->y + pr->h, &y1, &y2, &ys) || + (y1 != 0 && pr->y > y1) || + pr->y > y2 || ys != 1) { + PyErr_SetString(PyExc_IndexError, + "invalid y slice"); + return -1; + } + if (y1 == 0) y1 = pr->y; + if (len != bpp * (x2 - x1) * (y2 - y1)) { + PyErr_SetString(PyExc_TypeError, + "string is wrong length"); + return -1; + } + gimp_pixel_rgn_set_rect(pr, buf, x1, y1, x2-x1, y2-y1); + return 0; + } else { + PyErr_SetString(PyExc_TypeError,"invalid y subscript"); + return -1; + } + } else { + PyErr_SetString(PyExc_TypeError, "invalid x subscript"); + return -1; + } + return -1; +} + +static PyMappingMethods pr_as_mapping = { + (inquiry)pr_length, /*mp_length*/ + (binaryfunc)pr_subscript, /*mp_subscript*/ + (objobjargproc)pr_ass_sub, /*mp_ass_subscript*/ +}; + +/* -------------------------------------------------------- */ + +#define OFF(x) offsetof(GPixelRgn, x) + +static struct memberlist pr_memberlist[] = { + {"drawable", T_INT, OFF(drawable), RO}, + {"bpp", T_UINT, OFF(bpp), RO}, + {"rowstride", T_UINT, OFF(rowstride), RO}, + {"x", T_UINT, OFF(x), RO}, + {"y", T_UINT, OFF(y), RO}, + {"w", T_UINT, OFF(w), RO}, + {"h", T_UINT, OFF(h), RO}, + {"dirty", T_UINT, 0 /*bitfield*/, RO}, + {"shadow", T_UINT, 0 /*bitfield*/, RO} +}; + +#undef OFF + +static PyObject * +pr_getattr(self, name) + probject *self; + char *name; +{ + PyObject *rv; + + if (!strcmp(name, "drawable")) + return (PyObject *)newdrwobject(self->pr.drawable, 0); + if (!strcmp(name, "dirty")) + return PyInt_FromLong(self->pr.dirty); + if (!strcmp(name, "shadow")) + return PyInt_FromLong(self->pr.shadow); + rv = PyMember_Get((char *)&(self->pr), pr_memberlist, name); + if (rv) + return rv; + PyErr_Clear(); + return Py_FindMethod(pr_methods, (PyObject *)self, name); +} + +static PyObject * +pr_repr(self) + probject *self; +{ + PyObject *s; + s = PyString_FromString("pr.drawable->id))); + PyString_ConcatAndDel(&s, PyString_FromString(">")); + return s; +} + +static PyTypeObject Prtype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "PixelRegion", /*tp_name*/ + sizeof(probject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)pr_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)pr_getattr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)pr_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &pr_as_mapping, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + NULL /* Documentation string */ +}; + +/* End of code for PixelRegion objects */ +/* -------------------------------------------------------- */ + +#ifdef GIMP_HAVE_PARASITES +static PyObject * +para_copy(self, args) + paraobject *self; + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":copy")) + return NULL; + return (PyObject *)newparaobject(parasite_copy(self->para)); +} + +static PyObject * +para_is_type(self, args) + paraobject *self; + PyObject *args; +{ + char *name; + if (!PyArg_ParseTuple(args, "s:is_type", &name)) + return NULL; + return PyInt_FromLong(parasite_is_type(self->para, name)); +} + +static PyObject * +para_has_flag(self, args) + paraobject *self; + PyObject *args; +{ + int flag; + if (!PyArg_ParseTuple(args, "i:has_flag", &flag)) + return NULL; + return PyInt_FromLong(parasite_has_flag(self->para, flag)); +} + + + +static struct PyMethodDef para_methods[] = { + {"copy", (PyCFunction)para_copy, METH_VARARGS}, + {"is_type", (PyCFunction)para_is_type, METH_VARARGS}, + {"has_flag",(PyCFunction)para_has_flag, METH_VARARGS}, + + {NULL, NULL} /* sentinel */ +}; + +static paraobject * +newparaobject(para) + Parasite *para; +{ + paraobject *self; + + if (!para) { + Py_INCREF(Py_None); + return (paraobject *)Py_None; + } + self = PyObject_NEW(paraobject, &Paratype); + if (self == NULL) + return NULL; + self->para = para; + return self; +} + + +static void +para_dealloc(self) + paraobject *self; +{ + parasite_free(self->para); + PyMem_DEL(self); +} + +static PyObject * +para_getattr(self, name) + paraobject *self; + char *name; +{ + PyObject *rv; + + if (!strcmp(name, "__members__")) { +#if GIMP_CHECK_VERSION(1,1,5) + return Py_BuildValue("[sssss]", "data", "flags", "is_persistent", + "is_undoable", "name"); +#else + return Py_BuildValue("[ssss]", "data", "flags", "is_persistent", + "name"); +#endif + } + if (!strcmp(name, "is_persistent")) + return PyInt_FromLong(parasite_is_persistent(self->para)); +#if GIMP_CHECK_VERSION(1,1,5) + if (!strcmp(name, "is_undoable")) + return PyInt_FromLong(parasite_is_undoable(self->para)); +#endif + if (!strcmp(name, "flags")) + return PyInt_FromLong(parasite_flags(self->para)); + if (!strcmp(name, "name")) + return PyString_FromString(parasite_name(self->para)); + if (!strcmp(name, "data")) + return PyString_FromStringAndSize(parasite_data(self->para), + parasite_data_size(self->para)); + return Py_FindMethod(para_methods, (PyObject *)self, name); +} + +static PyObject * +para_repr(self) + paraobject *self; +{ + PyObject *s; + s = PyString_FromString("para))); + PyString_ConcatAndDel(&s, PyString_FromString(">")); + return s; +} + +static PyObject * +para_str(self) + paraobject *self; +{ + return PyString_FromStringAndSize(parasite_data(self->para), + parasite_data_size(self->para)); +} + +static PyTypeObject Paratype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "Parasite", /*tp_name*/ + sizeof(paraobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)para_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)para_getattr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)para_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)para_str, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + NULL /* Documentation string */ +}; + +/* End of code for Parasite objects */ +/* -------------------------------------------------------- */ +#endif + +GPlugInInfo PLUG_IN_INFO = { + NULL, /* init_proc */ + NULL, /* quit_proc */ + NULL, /* query_proc */ + NULL /* run_proc */ +}; + +static PyObject *callbacks[] = { + NULL, NULL, NULL, NULL +}; + +static void pygimp_init_proc() { + PyObject *r; + r = PyObject_CallFunction(callbacks[0], "()"); + if (!r) { + PyErr_Print(); + PyErr_Clear(); + return; + } + Py_DECREF(r); +} + +static void pygimp_quit_proc() { + PyObject *r; + r = PyObject_CallFunction(callbacks[1], "()"); + if (!r) { + PyErr_Print(); + PyErr_Clear(); + return; + } + Py_DECREF(r); +} + +static void pygimp_query_proc() { + PyObject *r; + r = PyObject_CallFunction(callbacks[2], "()"); + if (!r) { + PyErr_Print(); + PyErr_Clear(); + return; + } + Py_DECREF(r); +} + +static void pygimp_run_proc(char *name, int nparams, GParam *params, + int *nreturn_vals, GParam **return_vals) { + PyObject *args, *ret; + GParamDef *pd, *rv; + char *b, *h, *a, *c, *d; + int t, np, nrv; + + gimp_query_procedure(name, &b, &h, &a, &c, &d, &t, &np, &nrv, + &pd, &rv); + g_free(b); g_free(h); g_free(a); g_free(c); g_free(d); g_free(pd); + +#if PG_DEBUG > 0 + fprintf(stderr, "Params for %s:", name); + print_GParam(nparams, params); +#endif + args = GParam_to_tuple(nparams, params); + if (args == NULL) { + PyErr_Clear(); + *nreturn_vals = 1; + *return_vals = g_new(GParam, 1); + (*return_vals)[0].type = PARAM_STATUS; + (*return_vals)[0].data.d_status = STATUS_CALLING_ERROR; + return; + } + ret = PyObject_CallFunction(callbacks[3], "(sO)", name, args); + Py_DECREF(args); + if (ret == NULL) { + PyErr_Print(); + PyErr_Clear(); + *nreturn_vals = 1; + *return_vals = g_new(GParam, 1); + (*return_vals)[0].type = PARAM_STATUS; + (*return_vals)[0].data.d_status = STATUS_EXECUTION_ERROR; + return; + } + *return_vals = tuple_to_GParam(ret, rv, nrv); + g_free(rv); + if (*return_vals == NULL) { + PyErr_Clear(); + *nreturn_vals = 1; + *return_vals = g_new(GParam, 1); + (*return_vals)[0].type = PARAM_STATUS; + (*return_vals)[0].data.d_status = STATUS_EXECUTION_ERROR; + return; + } + Py_DECREF(ret); + *nreturn_vals = nrv + 1; + (*return_vals)[0].type = PARAM_STATUS; + (*return_vals)[0].data.d_status = STATUS_SUCCESS; +} + +static PyObject * +gimp_Main(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + PyObject *av; + int argc, i; + char **argv; + PyObject *ip, *qp, *query, *rp; + + if (!PyArg_ParseTuple(args, "OOOO:main", &ip, &qp, &query, &rp)) + return NULL; + +#define Arg_Check(v) (PyCallable_Check(v) || (v) == Py_None) + + if (!Arg_Check(ip) || !Arg_Check(qp) || !Arg_Check(query) || + !Arg_Check(rp)) { + PyErr_SetString(ErrorObject, "arguments must be callable."); + return NULL; + } + + if (ip != Py_None) { + callbacks[0] = ip; + PLUG_IN_INFO.init_proc = pygimp_init_proc; + } + if (qp != Py_None) { + callbacks[1] = qp; + PLUG_IN_INFO.quit_proc = pygimp_quit_proc; + } + if (query != Py_None) { + callbacks[2] = query; + PLUG_IN_INFO.query_proc = pygimp_query_proc; + } + if (rp != Py_None) { + callbacks[3] = rp; + PLUG_IN_INFO.run_proc = pygimp_run_proc; + } + + av = PySys_GetObject("argv"); + + argc = PyList_Size(av); + argv = g_new(char *, argc); + + for (i = 0; i < argc; i++) + argv[i] = g_strdup(PyString_AsString(PyList_GetItem(av, i))); + + gimp_main(argc, argv); + + if (argv != NULL) { + for (i = 0; i < argc; i++) + if (argv[i] != NULL) + g_free(argv[i]); + g_free(argv); + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Quit(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + + if (!PyArg_ParseTuple(args, ":quit")) + return NULL; + gimp_quit(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Set_data(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *id, *data; + int bytes, nreturn_vals; + GParam *return_vals; + + if (!PyArg_ParseTuple(args, "ss#:set_data", &id, &data, &bytes)) + return NULL; + return_vals = gimp_run_procedure("gimp_procedural_db_set_data", + &nreturn_vals, PARAM_STRING, id, PARAM_INT32, bytes, + PARAM_INT8ARRAY, data, PARAM_END); + if (return_vals[0].data.d_status != STATUS_SUCCESS) { + PyErr_SetString(ErrorObject, "error occurred while storing"); + return NULL; + } + gimp_destroy_params(return_vals, nreturn_vals); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Get_data(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *id; + int nreturn_vals; + GParam *return_vals; + PyObject *s; + + if (!PyArg_ParseTuple(args, "s:get_data", &id)) + return NULL; + + return_vals = gimp_run_procedure("gimp_procedural_db_get_data", + &nreturn_vals, PARAM_STRING, id, PARAM_END); + + if (return_vals[0].data.d_status != STATUS_SUCCESS) { + PyErr_SetString(ErrorObject, "no data for id"); + return NULL; + } + s = PyString_FromStringAndSize((char *)return_vals[2].data.d_int8array, + return_vals[1].data.d_int32); + gimp_destroy_params(return_vals, nreturn_vals); + return s; +} + +static PyObject * +gimp_Progress_init(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *msg = NULL; + if (!PyArg_ParseTuple(args, "|s:progress_init", &msg)) + return NULL; + gimp_progress_init(msg); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Progress_update(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + double p; + if (!PyArg_ParseTuple(args, "d:progress_update", &p)) + return NULL; + gimp_progress_update(p); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Query_images(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + gint32 *imgs; + int nimgs, i; + PyObject *ret; + if (!PyArg_ParseTuple(args, ":query_images")) + return NULL; + imgs = gimp_query_images(&nimgs); + ret = PyList_New(nimgs); + for (i = 0; i < nimgs; i++) + PyList_SetItem(ret, i, (PyObject *)newimgobject(imgs[i])); + return ret; +} + +static PyObject * +gimp_Install_procedure(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *name, *blurb, *help, *author, *copyright, *date, *menu_path, + *image_types, *n, *d; + GParamDef *params, *return_vals; + int type, nparams, nreturn_vals, i; + PyObject *pars, *rets; + + if (!PyArg_ParseTuple(args, "sssssszziOO:install_procedure", + &name, &blurb, &help, + &author, ©right, &date, &menu_path, &image_types, + &type, &pars, &rets)) + return NULL; + if (!PySequence_Check(pars) || !PySequence_Check(rets)) { + PyErr_SetString(PyExc_TypeError, + "last two args must be sequences"); + return NULL; + } + nparams = PySequence_Length(pars); + nreturn_vals = PySequence_Length(rets); + params = g_new(GParamDef, nparams); + for (i = 0; i < nparams; i++) { + if (!PyArg_ParseTuple(PySequence_GetItem(pars, i), "iss", + &(params[i].type), &n, &d)) { + g_free(params); + return NULL; + } + params[i].name = g_strdup(n); + params[i].description = g_strdup(d); + } + return_vals = g_new(GParamDef, nreturn_vals); + for (i = 0; i < nreturn_vals; i++) { + if (!PyArg_ParseTuple(PySequence_GetItem(rets, i), "iss", + &(return_vals[i].type), &n, &d)) { + g_free(params); g_free(return_vals); + return NULL; + } + return_vals[i].name = g_strdup(n); + return_vals[i].description = g_strdup(d); + } + gimp_install_procedure(name, blurb, help, author, copyright, date, + menu_path, image_types, type, nparams, nreturn_vals, params, + return_vals); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Install_temp_proc(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *name, *blurb, *help, *author, *copyright, *date, *menu_path, + *image_types, *n, *d; + GParamDef *params, *return_vals; + int type, nparams, nreturn_vals, i; + PyObject *pars, *rets; + + if (!PyArg_ParseTuple(args, "sssssszziOO:install_temp_proc", + &name, &blurb, &help, + &author, ©right, &date, &menu_path, &image_types, + &type, &pars, &rets)) + return NULL; + if (!PySequence_Check(pars) || !PySequence_Check(rets)) { + PyErr_SetString(PyExc_TypeError, + "last two args must be sequences"); + return NULL; + } + nparams = PySequence_Length(pars); + nreturn_vals = PySequence_Length(rets); + params = g_new(GParamDef, nparams); + for (i = 0; i < nparams; i++) { + if (!PyArg_ParseTuple(PySequence_GetItem(pars, i), "iss", + &(params[i].type), &n, &d)) { + g_free(params); + return NULL; + } + params[i].name = g_strdup(n); + params[i].description = g_strdup(d); + } + return_vals = g_new(GParamDef, nreturn_vals); + for (i = 0; i < nreturn_vals; i++) { + if (!PyArg_ParseTuple(PySequence_GetItem(rets, i), "iss", + &(return_vals[i].type), &n, &d)) { + g_free(params); g_free(return_vals); + return NULL; + } + return_vals[i].name = g_strdup(n); + return_vals[i].description = g_strdup(d); + } + gimp_install_temp_proc(name, blurb, help, author, copyright, date, + menu_path, image_types, type, nparams, nreturn_vals, params, + return_vals, pygimp_run_proc); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Uninstall_temp_proc(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *name; + + if (!PyArg_ParseTuple(args, "s:uninstall_temp_proc", &name)) + return NULL; + gimp_uninstall_temp_proc(name); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Register_magic_load_handler(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *name, *extensions, *prefixes, *magics; + if (!PyArg_ParseTuple(args, "ssss:register_magic_load_handler", + &name, &extensions, &prefixes, &magics)) + return NULL; + gimp_register_magic_load_handler(name, extensions, prefixes, magics); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Register_load_handler(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *name, *extensions, *prefixes; + if (!PyArg_ParseTuple(args, "sss:register_load_handler", + &name, &extensions, &prefixes)) + return NULL; + gimp_register_load_handler(name, extensions, prefixes); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Register_save_handler(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *name, *extensions, *prefixes; + if (!PyArg_ParseTuple(args, "sss:register_save_handler", + &name, &extensions, &prefixes)) + return NULL; + gimp_register_save_handler(name, extensions, prefixes); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Gamma(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":gamma")) + return NULL; + return PyFloat_FromDouble(gimp_gamma()); +} + +static PyObject * +gimp_Install_cmap(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":install_cmap")) + return NULL; + return PyInt_FromLong(gimp_install_cmap()); +} + +static PyObject * +gimp_Use_xshm(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":use_xshm")) + return NULL; + return PyInt_FromLong(gimp_use_xshm()); +} + +static PyObject * +gimp_Color_cube(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":color_cube")) + return NULL; + return PyString_FromString(gimp_color_cube()); +} + +static PyObject * +gimp_Gtkrc(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":gtkrc")) + return NULL; + return PyString_FromString(gimp_gtkrc()); +} + +static PyObject * +gimp_Get_background(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + guchar r, g, b; + if (!PyArg_ParseTuple(args, ":get_background")) + return NULL; + gimp_palette_get_background(&r, &g, &b); + return Py_BuildValue("(iii)", (int)r, (int)g, (int)b); +} + +static PyObject * +gimp_Get_foreground(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + guchar r, g, b; + if (!PyArg_ParseTuple(args, ":get_foreground")) + return NULL; + gimp_palette_get_foreground(&r, &g, &b); + return Py_BuildValue("(iii)", (int)r, (int)g, (int)b); +} + +static PyObject * +gimp_Set_background(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + int r, g, b; + if (!PyArg_ParseTuple(args, "(iii):set_background", &r, &g, &b)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "iii:set_background", &r, &g, &b)) + return NULL; + } + gimp_palette_set_background(r,g,b); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Set_foreground(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + int r, g, b; + if (!PyArg_ParseTuple(args, "(iii):set_foreground", &r, &g, &b)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "iii:set_foreground", &r, &g, &b)) + return NULL; + } + gimp_palette_set_foreground(r,g,b); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Gradients_get_list(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char **list; + int num, i; + PyObject *ret; + if (!PyArg_ParseTuple(args, ":gradients_get_list")) + return NULL; + list = gimp_gradients_get_list(&num); + ret = PyList_New(num); + for (i = 0; i < num; i++) + PyList_SetItem(ret, i, PyString_FromString(list[i])); + g_free(list); + return ret; +} + +static PyObject * +gimp_Gradients_get_active(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + if (!PyArg_ParseTuple(args, ":gradients_get_active")) + return NULL; + return PyString_FromString(gimp_gradients_get_active()); +} + +static PyObject * +gimp_Gradients_set_active(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + char *actv; + if (!PyArg_ParseTuple(args, "s:gradients_set_active", &actv)) + return NULL; + gimp_gradients_set_active(actv); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Gradients_sample_uniform(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + int num, i, j; + double *samp; + PyObject *ret; + if (!PyArg_ParseTuple(args, "i:gradients_sample_uniform", &num)) + return NULL; + samp = gimp_gradients_sample_uniform(num); + ret = PyList_New(num); + for (i = 0, j = 0; i < num; i++, j += 4) + PyList_SetItem(ret, i, Py_BuildValue("(dddd)", samp[j], + samp[j+1], samp[j+2], samp[j+3])); + g_free(samp); + return ret; +} + +static PyObject * +gimp_Gradients_sample_custom(self, args) + PyObject *self; /* Not used */ + PyObject *args; +{ + int num, i, j; + double *pos, *samp; + PyObject *ret, *item; + if (!PyArg_ParseTuple(args, "O:gradients_sample_custom", &ret)) + return NULL; + if (!PySequence_Check(ret)) { + PyErr_SetString(PyExc_TypeError, + "second arg must be a sequence"); + return NULL; + } + num = PySequence_Length(ret); + pos = g_new(gdouble, num); + for (i = 0; i < num; i++) { + item = PySequence_GetItem(ret, i); + if (!PyFloat_Check(item)) { + PyErr_SetString(PyExc_TypeError, + "second arg must be a sequence of floats"); + g_free(pos); + return NULL; + } + pos[i] = PyFloat_AsDouble(item); + } + samp = gimp_gradients_sample_custom(num, pos); + g_free(pos); + ret = PyList_New(num); + for (i = 0, j = 0; i < num; i++, j += 4) + PyList_SetItem(ret, i, Py_BuildValue("(dddd)", samp[j], + samp[j+1], samp[j+2], samp[j+3])); + g_free(samp); + return ret; +} + + +static PyObject * +gimp_image(self, args) + PyObject *self, *args; +{ + unsigned int width, height; + GImageType type; + + if (!PyArg_ParseTuple(args, "iii:image", &width, &height, &type)) + return NULL; + return (PyObject *)newimgobject(gimp_image_new(width, height, type)); +} + + +static PyObject * +gimp_layer(self, args) + PyObject *self, *args; +{ + drwobject *drw; + imgobject *img; + char *name; + unsigned int width, height; + GDrawableType type; + double opacity; + GLayerMode mode; + + + if (!PyArg_ParseTuple(args, "O!siiidi:layer", &Imgtype, &img, &name, + &width, &height, &type, &opacity, &mode)) + return NULL; + return (PyObject *)newlayobject(gimp_layer_new(img->ID, name, width, + height, type, opacity, mode)); +} + + +static PyObject * +gimp_channel(self, args) + PyObject *self, *args; +{ + drwobject *drw; + imgobject *img; + char *name; + unsigned int width, height, r, g, b; + double opacity; + unsigned char colour[3]; + + if (!PyArg_ParseTuple(args, "O!siid(iii):channel", &Imgtype, &img, + &name, &width, &height, &opacity, &r, &g, &b)) + return NULL; + colour[0] = r & 0xff; + colour[1] = g & 0xff; + colour[2] = b & 0xff; + return (PyObject *)newchnobject(gimp_channel_new(img->ID, name, + width, height, opacity, colour)); +} + + +static PyObject * +gimp_display(self, args) + PyObject *self, *args; +{ + imgobject *img; + + if (!PyArg_ParseTuple(args, "O!:display", &Imgtype, &img)) + return NULL; + return (PyObject *)newdispobject(gimp_display_new(img->ID)); +} + + +static PyObject * +gimp_delete(self, args) + PyObject *self, *args; +{ + imgobject *img; + + if (!PyArg_ParseTuple(args, "O:delete", &img)) + return NULL; + if (img_check(img)) gimp_image_delete(img->ID); + else if (lay_check(img)) gimp_layer_delete(img->ID); + else if (chn_check(img)) gimp_channel_delete(img->ID); + else if (disp_check(img)) gimp_display_delete(img->ID); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +gimp_Displays_flush(self, args) + PyObject *self, *args; +{ + if (!PyArg_ParseTuple(args, ":flush")) + return NULL; + gimp_displays_flush(); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +gimp_Tile_cache_size(self, args) + PyObject *self, *args; +{ + unsigned long k; + if (!PyArg_ParseTuple(args, "l:tile_cache_size", &k)) + return NULL; + gimp_tile_cache_size(k); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +gimp_Tile_cache_ntiles(self, args) + PyObject *self, *args; +{ + unsigned long n; + if (!PyArg_ParseTuple(args, "l:tile_cache_ntiles", &n)) + return NULL; + gimp_tile_cache_ntiles(n); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +gimp_Tile_width(self, args) + PyObject *self, *args; +{ + if (PyArg_ParseTuple(args, ":tile_width")) + return NULL; + return PyInt_FromLong(gimp_tile_width()); +} + + +static PyObject * +gimp_Tile_height(self, args) + PyObject *self, *args; +{ + if (PyArg_ParseTuple(args, ":tile_height")) + return NULL; + return PyInt_FromLong(gimp_tile_height()); +} + +void gimp_extension_ack (void); +void gimp_extension_process (guint timeout); + +static PyObject * +gimp_Extension_ack(self, args) + PyObject *self, *args; +{ + if (!PyArg_ParseTuple(args, ":extension_ack")) + return NULL; + gimp_extension_ack(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Extension_process(self, args) + PyObject *self, *args; +{ + int timeout; + + if (!PyArg_ParseTuple(args, "i:extension_process", &timeout)) + return NULL; + gimp_extension_process(timeout); + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef GIMP_HAVE_PARASITES +static PyObject * +new_parasite(self, args) + PyObject *self, *args; +{ + char *name, *data; + int flags, size; + if (!PyArg_ParseTuple(args, "sis#:parasite", &name, &flags, + &data, &size)) + return NULL; + return (PyObject *)newparaobject(parasite_new(name, flags, size, data)); +} + +static PyObject * +gimp_Find_parasite(self, args) + PyObject *self, *args; +{ + char *name; + if (!PyArg_ParseTuple(args, "s:find_parasite", &name)) + return NULL; + return (PyObject *)newparaobject(gimp_find_parasite(name)); +} + +static PyObject * +gimp_Attach_parasite(self, args) + PyObject *self, *args; +{ + paraobject *parasite; + if (!PyArg_ParseTuple(args, "O!:attach_parasite", &Paratype, ¶site)) + return NULL; + gimp_attach_parasite(parasite->para); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Attach_new_parasite(self, args) + PyObject *self, *args; +{ + char *name, *data; + int flags, size; + if (!PyArg_ParseTuple(args, "sis#:attach_new_parasite", &name, &flags, + &data, &size)) + return NULL; + gimp_attach_new_parasite(name, flags, size, data); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gimp_Detach_parasite(self, args) + PyObject *self, *args; +{ + char *name; + if (!PyArg_ParseTuple(args, "s:detach_parasite", &name)) + return NULL; + gimp_detach_parasite(name); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef GIMP_HAVE_DEFAULT_DISPLAY +static PyObject * +gimp_Default_display(self, args) + PyObject *self, *args; +{ + if (!PyArg_ParseTuple(args, ":default_display")) + return NULL; + return (PyObject *)newdispobject(gimp_default_display()); +} +#endif + +static PyObject * +id2image(self, args) + PyObject *self, *args; +{ + int id; + if (!PyArg_ParseTuple(args, "i:_id2image", &id)) + return NULL; + if (id >= 0) + return (PyObject *)newimgobject(id); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +id2drawable(self, args) + PyObject *self, *args; +{ + int id; + if (!PyArg_ParseTuple(args, "i:_id2drawable", &id)) + return NULL; + if (id >= 0) + return (PyObject *)newdrwobject(NULL, id); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +id2display(self, args) + PyObject *self, *args; +{ + int id; + if (!PyArg_ParseTuple(args, "i:_id2display", &id)) + return NULL; + if (id >= 0) + return (PyObject *)newdispobject(id); + Py_INCREF(Py_None); + return Py_None; +} + +/* List of methods defined in the module */ + +static struct PyMethodDef gimp_methods[] = { + {"main", (PyCFunction)gimp_Main, METH_VARARGS}, + {"quit", (PyCFunction)gimp_Quit, METH_VARARGS}, + {"set_data", (PyCFunction)gimp_Set_data, METH_VARARGS}, + {"get_data", (PyCFunction)gimp_Get_data, METH_VARARGS}, + {"progress_init", (PyCFunction)gimp_Progress_init, METH_VARARGS}, + {"progress_update", (PyCFunction)gimp_Progress_update, METH_VARARGS}, + {"query_images", (PyCFunction)gimp_Query_images, METH_VARARGS}, + {"install_procedure", (PyCFunction)gimp_Install_procedure, METH_VARARGS}, + {"install_temp_proc", (PyCFunction)gimp_Install_temp_proc, METH_VARARGS}, + {"uninstall_temp_proc", (PyCFunction)gimp_Uninstall_temp_proc, METH_VARARGS}, + {"register_magic_load_handler", (PyCFunction)gimp_Register_magic_load_handler, METH_VARARGS}, + {"register_load_handler", (PyCFunction)gimp_Register_load_handler, METH_VARARGS}, + {"register_save_handler", (PyCFunction)gimp_Register_save_handler, METH_VARARGS}, + {"gamma", (PyCFunction)gimp_Gamma, METH_VARARGS}, + {"install_cmap", (PyCFunction)gimp_Install_cmap, METH_VARARGS}, + {"use_xshm", (PyCFunction)gimp_Use_xshm, METH_VARARGS}, + {"color_cube", (PyCFunction)gimp_Color_cube, METH_VARARGS}, + {"colour_cube", (PyCFunction)gimp_Color_cube, METH_VARARGS}, + {"gtkrc", (PyCFunction)gimp_Gtkrc, METH_VARARGS}, + {"get_background", (PyCFunction)gimp_Get_background, METH_VARARGS}, + {"get_foreground", (PyCFunction)gimp_Get_foreground, METH_VARARGS}, + {"set_background", (PyCFunction)gimp_Set_background, METH_VARARGS}, + {"set_foreground", (PyCFunction)gimp_Set_foreground, METH_VARARGS}, + {"gradients_get_list", (PyCFunction)gimp_Gradients_get_list, METH_VARARGS}, + {"gradients_get_active", (PyCFunction)gimp_Gradients_get_active, METH_VARARGS}, + {"gradients_set_active", (PyCFunction)gimp_Gradients_set_active, METH_VARARGS}, + {"gradients_sample_uniform", (PyCFunction)gimp_Gradients_sample_uniform, METH_VARARGS}, + {"gradients_sample_custom", (PyCFunction)gimp_Gradients_sample_custom, METH_VARARGS}, + {"image", (PyCFunction)gimp_image, METH_VARARGS}, + {"layer", (PyCFunction)gimp_layer, METH_VARARGS}, + {"channel", (PyCFunction)gimp_channel, METH_VARARGS}, + {"display", (PyCFunction)gimp_display, METH_VARARGS}, + {"delete", (PyCFunction)gimp_delete, METH_VARARGS}, + {"displays_flush", (PyCFunction)gimp_Displays_flush, METH_VARARGS}, + {"tile_cache_size", (PyCFunction)gimp_Tile_cache_size, METH_VARARGS}, + {"tile_cache_ntiles", (PyCFunction)gimp_Tile_cache_ntiles, METH_VARARGS}, + {"tile_width", (PyCFunction)gimp_Tile_width, METH_VARARGS}, + {"tile_height", (PyCFunction)gimp_Tile_height, METH_VARARGS}, + {"extension_ack", (PyCFunction)gimp_Extension_ack, METH_VARARGS}, + {"extension_process", (PyCFunction)gimp_Extension_process, METH_VARARGS}, +#ifdef GIMP_HAVE_PARASITES + {"parasite", (PyCFunction)new_parasite, METH_VARARGS}, + {"find_parasite", (PyCFunction)gimp_Find_parasite, METH_VARARGS}, + {"attach_parasite", (PyCFunction)gimp_Attach_parasite, METH_VARARGS}, + {"attach_new_parasite",(PyCFunction)gimp_Attach_new_parasite,METH_VARARGS}, + {"detach_parasite", (PyCFunction)gimp_Detach_parasite, METH_VARARGS}, +#endif +#ifdef GIMP_HAVE_DEFAULT_DISPLAY + {"default_display", (PyCFunction)gimp_Default_display, METH_VARARGS}, +#endif + {"_id2image", (PyCFunction)id2image, METH_VARARGS}, + {"_id2drawable", (PyCFunction)id2drawable, METH_VARARGS}, + {"_id2display", (PyCFunction)id2display, METH_VARARGS}, + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ +}; + + +/* Initialization function for the module (*must* be called initgimp) */ + +static char gimp_module_documentation[] = +"" +; + +void +initgimp() +{ + PyObject *m, *d; + PyObject *i; + + /* Create the module and add the functions */ + m = Py_InitModule4("gimp", gimp_methods, + gimp_module_documentation, + (PyObject*)NULL,PYTHON_API_VERSION); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + ErrorObject = PyString_FromString("gimp.error"); + PyDict_SetItemString(d, "error", ErrorObject); + + PyDict_SetItemString(d, "pdb", (PyObject *)newpdbobject()); + + /* export the types used in gimpmodule */ + PyDict_SetItemString(d, "ImageType", (PyObject *)&Imgtype); + PyDict_SetItemString(d, "LayerType", (PyObject *)&Laytype); + PyDict_SetItemString(d, "ChannelType", (PyObject *)&Chntype); + PyDict_SetItemString(d, "DisplayType", (PyObject *)&Disptype); + PyDict_SetItemString(d, "TileType", (PyObject *)&Tiletype); + PyDict_SetItemString(d, "PixelRegionType", (PyObject *)&Prtype); +#ifdef GIMP_HAVE_PARASITES + PyDict_SetItemString(d, "ParasiteType", (PyObject *)&Paratype); +#endif + + PyDict_SetItemString(d, "major_version", + i=PyInt_FromLong(gimp_major_version)); + Py_DECREF(i); + PyDict_SetItemString(d, "minor_version", + i=PyInt_FromLong(gimp_minor_version)); + Py_DECREF(i); + PyDict_SetItemString(d, "micro_version", + i=PyInt_FromLong(gimp_micro_version)); + Py_DECREF(i); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module gimp"); +} + diff --git a/plug-ins/pygimp/gimpplugin.py b/plug-ins/pygimp/gimpplugin.py new file mode 100644 index 0000000000..b8bef2795e --- /dev/null +++ b/plug-ins/pygimp/gimpplugin.py @@ -0,0 +1,56 @@ +# Gimp-Python - allows the writing of Gimp plugins in Python. +# Copyright (C) 1997 James Henstridge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# plugin.py -- helper for writing gimp plugins +# Copyright (C) 1997, James Henstridge. +# +# This is a small wrapper that makes plugins look like an object class that +# you can derive to create your plugin. With this wrapper, you are pretty +# much responsible for doing everything (checking run_mode, gui, etc). If +# you want to write a quick plugin, you probably want the gimpfu module. +# +# A plugin using this module would look something like this: +# +# import gimp, gimpplugin +# pdb = gimp.pdb +# class myplugin(gimpplugin.plugin): +# def query(self): +# gimp.install_procedure("plug_in_mine", ...) +# def plug_in_mine(self, par1, par2, par3,...): +# do_something() +# +# if __name__ == '__main__': myplugin().start() + +import gimp + +class plugin: + def start(self): + gimp.main(self.init, self.quit, self.query, self._run) + def init(self): + pass + def quit(self): + pass + def query(self): + pass + def _run(self, name, params): + if hasattr(self, name): + apply(getattr(self, name), params) + else: + raise AttributeError, name + +if __name__ == '__main__': plugin().start() + diff --git a/plug-ins/pygimp/gimpshelf.py b/plug-ins/pygimp/gimpshelf.py new file mode 100644 index 0000000000..425bf285d3 --- /dev/null +++ b/plug-ins/pygimp/gimpshelf.py @@ -0,0 +1,83 @@ +# Gimp-Python - allows the writing of Gimp plugins in Python. +# Copyright (C) 1997 James Henstridge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# gimshelf.py -- a simple module to help gimp modules written in Python +# store persistent data. +# +# Copyright (C) 1997, James Henstridge +# +# The gimp module provides a basic method for storing information that persists +# for a whole gimp session, but only allows for the storage of strings. This +# is because other Python types usually have pointers to other Python objects, +# making it dificult to work out what to save. This module gives an interface +# to the gimp module's primitive interface, which resembles the shelve module. + +try: + # use cPickle instead of pickle if it is available. + import cPickle + pickle = cPickle + del cPickle +except ImportError: + import pickle +import StringIO +import gimp + +try: + # this will fail with python 1.4. All we lose is that the values + # for a plugin which takes extra image/drawables/etc will not be + # saved between invocations. + import copy_reg + def _image_id(obj): + return gimp._id2image, (obj.ID,) + def _drawable_id(obj): + return gimp._id2drawable, (obj.ID,) + def _display_id(obj): + return gimp._id2display, int(obj) + copy_reg.pickle(gimp.ImageType, _image_id, gimp._id2image) + copy_reg.pickle(gimp.LayerType, _drawable_id, gimp._id2drawable) + copy_reg.pickle(gimp.ChannelType, _drawable_id, gimp._id2drawable) + copy_reg.pickle(gimp.DisplayType, _display_id, gimp._id2display) + del copy_reg, _image_id, _drawable_id, _display_id +except ImportError: + pass + +class Gimpshelf: + def has_key(self, key): + try: + s = gimp.get_data(key) + return 1 + except gimp.error: + return 0 + + def __getitem__(self, key): + try: + s = gimp.get_data(key) + except gimp.error: + raise KeyError, key + f = StringIO.StringIO(s) + return pickle.Unpickler(f).load() + def __setitem__(self, key, value): + f = StringIO.StringIO() + p = pickle.Pickler(f) + p.dump(value) + gimp.set_data(key, f.getvalue()) + def __delitem__(self, key): + gimp.set_data(key, '') + +shelf = Gimpshelf() +del Gimpshelf + diff --git a/plug-ins/pygimp/gimpui.py b/plug-ins/pygimp/gimpui.py new file mode 100644 index 0000000000..b29e17ceb7 --- /dev/null +++ b/plug-ins/pygimp/gimpui.py @@ -0,0 +1,364 @@ +'''This module implements the UI items found in the libgimpui library. +It requires pygtk to work. These functions take use to callbacks -- one +is a constraint function, and the other is the callback object. The +constraint function takes an image object as its first argument, and +a drawable object as its second if appropriate. The callback functions +get the selected object as their first argument, and the user data as +the second. + +It also implements a number of selector widgets, which can be used to select +various gimp data types. Each of these selectors takes default as an argument +to the constructor, and has a get_value() method for retrieving the result. +''' + +import gtk, gimp + +def _callbackWrapper(menu_item, callback, data): + callback(menu_item.get_data("Gimp-ID"), data) + +def _createMenu(items, callback, data): + menu = gtk.GtkMenu() + if not items: + items = [("(none)", None)] + for label, id in items: + menu_item = gtk.GtkMenuItem(label) + menu_item.set_data("Gimp-ID", id) + menu.add(menu_item) + if callback: + menu_item.connect("activate", _callbackWrapper, + callback, data) + menu_item.show() + return menu + + +def ImageMenu(constraint=None, callback=None, data=None): + items = [] + for img in gimp.query_images(): + if constraint and not constraint(img): + continue + items.append((img.filename, img)) + items.sort() + return _createMenu(items, callback, data) + +def LayerMenu(constraint=None, callback=None, data=None): + items = [] + for img in gimp.query_images(): + filename = img.filename + for layer in img.layers: + if constraint and not constraint(img, layer): + continue + name = filename + "/" + layer.name + items.append((name, layer)) + items.sort() + return _createMenu(items, callback, data) + +def ChannelMenu(constraint=None, callback=None, data=None): + items = [] + for img in gimp.query_images(): + filename = img.filename + for channel in img.channels: + if constraint and not constraint(img, channel): + continue + name = filename + "/" + channel.name + items.append((name, channel)) + items.sort() + return _createMenu(items, callback, data) + +def DrawableMenu(constraint=None, callback=None, data=None): + items = [] + for img in gimp.query_images(): + filename = img.filename + for drawable in img.layers + img.channels: + if constraint and not constraint(img, drawable): + continue + name = filename + "/" + drawable.name + items.append((name, drawable)) + items.sort() + return _createMenu(items, callback, data) + +class ImageSelector(gtk.GtkOptionMenu): + def __init__(self, default=None): + gtk.GtkOptionMenu.__init__(self) + self.menu = ImageMenu(None, self.clicked) + self.set_menu(self.menu) + self.selected = default + children = self.menu.children() + for child in range(len(children)): + if children[child].get_data("Gimp-ID") == default: + self.set_history(child) + break + def clicked(self, img, data=None): + self.selected = img + def get_value(self): + return self.selected + +class LayerSelector(gtk.GtkOptionMenu): + def __init__(self, default=None): + gtk.GtkOptionMenu.__init__(self) + self.menu = LayerMenu(None, self.clicked) + self.set_menu(self.menu) + self.selected = default + children = self.menu.children() + for child in range(len(children)): + if children[child].get_data("Gimp-ID") == default: + self.set_history(child) + break + def clicked(self, layer, data=None): + self.selected = layer + def get_value(self): + return self.selected + +class ChannelSelector(gtk.GtkOptionMenu): + def __init__(self, default=None): + gtk.GtkOptionMenu.__init__(self) + self.menu = ChannelMenu(None, self.clicked) + self.set_menu(self.menu) + self.selected = default + children = self.menu.children() + for child in range(len(children)): + if children[child].get_data("Gimp-ID") == default: + self.set_history(child) + break + def clicked(self, channel, data=None): + self.selected = channel + def get_value(self): + return self.selected + +class DrawableSelector(gtk.GtkOptionMenu): + def __init__(self, default=None): + gtk.GtkOptionMenu.__init__(self) + self.menu = DrawableMenu(None, self.clicked) + self.set_menu(self.menu) + self.selected = default + children = self.menu.children() + for child in range(len(children)): + if children[child].get_data("Gimp-ID") == default: + self.set_history(child) + break + def clicked(self, drawable, data=None): + self.selected = drawable + def get_value(self): + return self.selected + +class ColourSelector(gtk.GtkButton): + def __init__(self, default=(255, 0, 0)): + gtk.GtkButton.__init__(self) + self.set_usize(100, 20) + + self.colour = default + self.update_colour() + + self.dialog = None + self.connect("clicked", self.show_dialog) + def update_colour(self): + r, g, b = self.colour + colour = self.get_colormap().alloc(r*256, g*256, b*256) + style = self.get_style().copy() + style.bg[gtk.STATE_NORMAL] = colour + style.bg[gtk.STATE_PRELIGHT] = colour + self.set_style(style) + self.queue_draw() + + def show_dialog(self, button): + if self.dialog: + self.dialog.show() + return + self.dialog = gtk.GtkColorSelectionDialog("Colour") + self.dialog.colorsel.set_color(tuple(map(lambda x: x/255.0, + self.colour))) + def delete_event(win, event): + win.hide() + return gtk.TRUE + self.dialog.connect("delete_event", delete_event) + self.dialog.ok_button.connect("clicked", self.selection_ok) + self.dialog.cancel_button.connect("clicked", self.dialog.hide) + self.dialog.show() + + def selection_ok(self, button): + colour = self.dialog.colorsel.get_color() + self.colour = tuple(map(lambda x: int(x*255.99), colour)) + self.update_colour() + self.dialog.hide() + + def get_value(self): + return self.colour + +class _Selector(gtk.GtkHBox): + def __init__(self): + gtk.GtkHBox.__init__(self, gtk.FALSE, 5) + self.entry = gtk.GtkEntry() + self.pack_start(self.entry) + self.entry.show() + self.button = gtk.GtkButton("...") + self.button.connect("clicked", self.show_dialog) + self.pack_start(self.button, expand=gtk.FALSE) + self.button.show() + + self.dialog = gtk.GtkDialog() + self.dialog.set_title(self.get_title()) + def delete_event(win, event): + win.hide() + return gtk.TRUE + self.dialog.connect("delete_event", delete_event) + + box = gtk.GtkVBox() + box.set_border_width(5) + self.dialog.vbox.pack_start(box) + box.show() + + swin = gtk.GtkScrolledWindow() + swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + box.pack_start(swin) + swin.show() + + items = map(None, self.get_list()) + list = gtk.GtkList() + list.set_selection_mode(gtk.SELECTION_BROWSE) + self.selected = self.get_default() + self.entry.set_text(self.selected) + items.sort() + for s in items: + item = gtk.GtkListItem(s) + list.add(item) + if s == self.selected: + list.select_child(item) + item.show() + swin.add_with_viewport(list) + list.show() + + b = gtk.GtkButton("OK") + self.dialog.action_area.pack_start(b) + b.set_flags(gtk.CAN_DEFAULT) + b.grab_default() + b.show() + b.connect("clicked", self.selection_ok, list) + + b = gtk.GtkButton("Cancel") + self.dialog.action_area.pack_start(b) + b.set_flags(gtk.CAN_DEFAULT) + b.show() + b.connect("clicked", self.dialog.hide) + + self.dialog.set_usize(300, 225) + + def show_dialog(self, button): + self.dialog.show() + + def selection_ok(self, button, list): + self.dialog.hide() + + sel = list.get_selection() + if not sel: return + self.selected = sel[0].children()[0].get() + self.entry.set_text(self.selected) + + def get_value(self): + return self.selected + +class PatternSelector(_Selector): + def __init__(self, default=""): + self.default = default + _Selector.__init__(self) + def get_default(self): + return self.default + def get_title(self): + return "Patterns" + def get_list(self): + num, patterns = gimp.pdb.gimp_patterns_list() + return patterns + +class BrushSelector(_Selector): + def __init__(self, default=""): + self.default = default + _Selector.__init__(self) + def get_default(self): + return self.default + def get_title(self): + return "Brushes" + def get_list(self): + num, brushes = gimp.pdb.gimp_brushes_list() + return brushes + +class GradientSelector(_Selector): + def __init__(self, default=""): + self.default = default + _Selector.__init__(self) + def get_default(self): + return self.default + def get_title(self): + return "Gradients" + def get_list(self): + num, gradients = gimp.pdb.gimp_gradients_get_list() + return gradients + +class FontSelector(gtk.GtkHBox): + def __init__(self, default="fixed"): + gtk.GtkHBox.__init__(self, gtk.FALSE, 5) + self.entry = gtk.GtkEntry() + self.pack_start(self.entry) + self.entry.show() + self.button = gtk.GtkButton("...") + self.button.connect("clicked", self.show_dialog) + self.pack_start(self.button, expand=gtk.FALSE) + self.button.show() + + self.dialog = gtk.GtkFontSelectionDialog("Fonts") + self.dialog.set_default_size(400, 300) + def delete_event(win, event): + win.hide() + return gtk.TRUE + self.dialog.connect("delete_event", delete_event) + + self.dialog.ok_button.connect("clicked", self.selection_ok) + self.dialog.cancel_button.connect("clicked", self.dialog.hide) + + self.dialog.set_font_name(default) + self.selected = default + self.entry.set_text(self.selected) + + def show_dialog(self, button): + self.dialog.show() + + def selection_ok(self, button): + self.dialog.hide() + self.selected = self.dialog.get_font_name() + self.entry.set_text(self.selected) + + def get_value(self): + return self.selected + +class FileSelector(gtk.GtkHBox): + def __init__(self, default=""): + gtk.GtkHBox.__init__(self, gtk.FALSE, 5) + self.entry = gtk.GtkEntry() + self.pack_start(self.entry) + self.entry.show() + self.button = gtk.GtkButton("...") + self.button.connect("clicked", self.show_dialog) + self.pack_start(self.button, expand=gtk.FALSE) + self.button.show() + + self.dialog = gtk.GtkFileSelection("Fonts") + self.dialog.set_default_size(400, 300) + def delete_event(win, event): + win.hide() + return gtk.TRUE + self.dialog.connect("delete_event", delete_event) + + self.dialog.ok_button.connect("clicked", self.selection_ok) + self.dialog.cancel_button.connect("clicked", self.dialog.hide) + + self.dialog.set_filename(default) + self.selected = self.dialog.get_filename() + self.entry.set_text(self.selected) + + def show_dialog(self, button): + self.dialog.show() + + def selection_ok(self, button): + self.dialog.hide() + self.selected = self.dialog.get_filename() + self.entry.set_text(self.selected) + + def get_value(self): + return self.selected diff --git a/plug-ins/pygimp/plug-ins/.cvsignore b/plug-ins/pygimp/plug-ins/.cvsignore new file mode 100644 index 0000000000..0552732a18 --- /dev/null +++ b/plug-ins/pygimp/plug-ins/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +*.pyc +*.pyo + diff --git a/plug-ins/pygimp/plug-ins/Makefile.am b/plug-ins/pygimp/plug-ins/Makefile.am new file mode 100644 index 0000000000..18cc8e054f --- /dev/null +++ b/plug-ins/pygimp/plug-ins/Makefile.am @@ -0,0 +1,7 @@ +pluginexec_SCRIPTS = clothify.py gimpcons.py pdbbrowse.py sphere.py \ + whirlpinch.py foggify.py shadow_bevel.py + +pluginexec_DATA = gtkcons.py + +EXTRA_DIST = clothify.py gimpcons.py pdbbrowse.py sphere.py gtkcons.py \ + whirlpinch.py foggify.py shadow_bevel.py diff --git a/plug-ins/pygimp/plug-ins/clothify.py b/plug-ins/pygimp/plug-ins/clothify.py new file mode 100755 index 0000000000..85166095a5 --- /dev/null +++ b/plug-ins/pygimp/plug-ins/clothify.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +# Gimp-Python - allows the writing of Gimp plugins in Python. +# Copyright (C) 1997 James Henstridge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import math +from gimpfu import * + +have_gimp11 = gimp.major_version > 1 or \ + gimp.major_version == 1 and gimp.minor_version >= 1 + +def python_clothify(timg, tdrawable, bx=9, by=9, + azimuth=135, elevation=45, depth=3): + bx = 9 ; by = 9 ; azimuth = 135 ; elevation = 45 ; depth = 3 + width = tdrawable.width + height = tdrawable.height + img = gimp.image(width, height, RGB) + layer_one = gimp.layer(img, "X Dots", width, height, RGB_IMAGE, + 100, NORMAL_MODE) + img.disable_undo() + if have_gimp11: + pdb.gimp_edit_fill(layer_one) + else: + pdb.gimp_edit_fill(img, layer_one) + img.add_layer(layer_one, 0) + pdb.plug_in_noisify(img, layer_one, 0, 0.7, 0.7, 0.7, 0.7) + layer_two = layer_one.copy() + layer_two.mode = MULTIPLY_MODE + layer_two.name = "Y Dots" + img.add_layer(layer_two, 0) + pdb.plug_in_gauss_rle(img, layer_one, bx, 1, 0) + pdb.plug_in_gauss_rle(img, layer_two, by, 0, 1) + img.flatten() + bump_layer = img.active_layer + pdb.plug_in_c_astretch(img, bump_layer) + pdb.plug_in_noisify(img, bump_layer, 0, 0.2, 0.2, 0.2, 0.2) + pdb.plug_in_bump_map(img, tdrawable, bump_layer, azimuth, + elevation, depth, 0, 0, 0, 0, TRUE, FALSE, 0) + gimp.delete(img) + +register( + "python_fu_clothify", + "Make the specified layer look like it is printed on cloth", + "Make the specified layer look like it is printed on cloth", + "James Henstridge", + "James Henstridge", + "1997-1999", + "/Python-Fu/Alchemy/Clothify", + "RGB*, GRAY*", + [ + (PF_INT, "x_blur", "X Blur", 9), + (PF_INT, "y_blur", "Y Blur", 9), + (PF_INT, "azimuth", "Azimuth", 135), + (PF_INT, "elevation", "elevation", 45), + (PF_INT, "depth", "Depth", 3) + ], + [], + python_clothify) + +main() diff --git a/plug-ins/pygimp/plug-ins/foggify.py b/plug-ins/pygimp/plug-ins/foggify.py new file mode 100755 index 0000000000..ca18da3ed6 --- /dev/null +++ b/plug-ins/pygimp/plug-ins/foggify.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +from gimpfu import * +import time + +have_gimp11 = gimp.major_version > 1 or \ + gimp.major_version == 1 and gimp.minor_version >= 1 + +def python_foggify(img, layer, name, colour, turbulence, opacity): + img.disable_undo() + + fog = gimp.layer(img, name, layer.width, layer.height, RGBA_IMAGE, + opacity, NORMAL_MODE) + oldbg = gimp.get_background() + gimp.set_background(colour) + if have_gimp11: + pdb.gimp_edit_fill(fog) + else: + pdb.gimp_edit_fill(img, fog) + gimp.set_background(oldbg) + + img.add_layer(fog, 0) + + # create a layer mask for the new layer + mask = fog.create_mask(0) + img.add_layer_mask(fog, mask) + + # add some clouds to the layer + pdb.plug_in_plasma(img, mask, int(time.time()), turbulence) + + # apply the clouds to the layer + img.remove_layer_mask(fog, APPLY) + + img.enable_undo() + +register( + "python_fu_foggify", + "Add a layer of fog to the image", + "Add a layer of fog to the image", + "James Henstridge", + "James Henstridge", + "1999", + "/Python-Fu/Effects/Add fog", + "RGB*, GRAY*", + [ + (PF_STRING, "name", "The new layer name", "Clouds"), + (PF_COLOUR, "colour", "The colour of the fog", (240,180,70)), + (PF_SLIDER, "turbulence", "The turbulence", 1.0, (0, 10, 0.1)), + (PF_SLIDER, "opacity", "The opacity", 100, (0, 100, 1)), + ], + [], + python_foggify) + +main() diff --git a/plug-ins/pygimp/plug-ins/gimpcons.py b/plug-ins/pygimp/plug-ins/gimpcons.py new file mode 100755 index 0000000000..aa244b7835 --- /dev/null +++ b/plug-ins/pygimp/plug-ins/gimpcons.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +# Gimp-Python - allows the writing of Gimp plugins in Python. +# Copyright (C) 1997 James Henstridge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +from gimpfu import * +import gtkcons + +def extension_python_fu_console(): + import gtk, gimpenums, gimpshelf + gtk.rc_parse(gimp.gtkrc()) + namespace = {'__builtins__': __builtins__, + '__name__': '__main__', '__doc__': None, + 'gimp': gimp, 'pdb': gimp.pdb, + 'shelf': gimpshelf.shelf} + for s in gimpenums.__dict__.keys(): + if s[0] != '_': + namespace[s] = getattr(gimpenums, s) + + win = gtk.GtkWindow() + win.connect("destroy", gtk.mainquit) + win.set_title("Gimp-Python Console") + cons = gtkcons.Console(namespace=namespace, + copyright='Gimp Python Extensions - Copyright (C), 1997-1999' + + ' James Henstridge\n', quit_cb=gtk.mainquit) + + def browse(button, cons): + import gtk, pdbbrowse + def ok_clicked(button, browse, cons=cons): + cons.line.set_text(browse.cmd) + browse.destroy() + win = pdbbrowse.BrowseWin(ok_button=ok_clicked) + win.connect("destroy", gtk.mainquit) + win.set_modal(TRUE) + win.show() + gtk.mainloop() + button = gtk.GtkButton("Browse") + button.connect("clicked", browse, cons) + cons.inputbox.pack_end(button, expand=FALSE) + button.show() + win.add(cons) + cons.show() + win.set_default_size(475, 300) + win.show() + cons.init() + # flush the displays every half second + def timeout(): + gimp.displays_flush() + return TRUE + gtk.timeout_add(500, timeout) + gtk.mainloop() + +register( + "python_fu_console", + "Python interactive interpreter with gimp extensions", + "Type in commands and see results", + "James Henstridge", + "James Henstridge", + "1997-1999", + "/Xtns/Python-Fu/Console", + "*", + [], + [], + extension_python_fu_console) + +main() + diff --git a/plug-ins/pygimp/plug-ins/gtkcons.py b/plug-ins/pygimp/plug-ins/gtkcons.py new file mode 100755 index 0000000000..b00a5a5621 --- /dev/null +++ b/plug-ins/pygimp/plug-ins/gtkcons.py @@ -0,0 +1,330 @@ +#!/usr/bin/env python + +# Interactive Python-GTK Console +# Copyright (C), 1998 James Henstridge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# This module implements an interactive python session in a GTK window. To +# start the session, use the gtk_console command. Its specification is: +# gtk_console(namespace, title, copyright) +# where namespace is a dictionary representing the namespace of the session, +# title is the title on the window and +# copyright is any additional copyright info to print. +# +# As well as the starting attributes in namespace, the session will also +# have access to the list __history__, which is the command history. + +import sys, string, traceback +#sys.path.append('/home/james/.gimp/plug-ins') +from gtk import * + +stdout = sys.stdout + +if not hasattr(sys, 'ps1'): sys.ps1 = '>>> ' +if not hasattr(sys, 'ps2'): sys.ps2 = '... ' + +# some functions to help recognise breaks between commands +def remQuotStr(s): + '''Returns s with any quoted strings removed (leaving quote marks)''' + r = '' + inq = 0 + qt = '' + prev = '_' + while len(s): + s0, s = s[0], s[1:] + if inq and (s0 != qt or prev == '\\'): + prev = s0 + continue + prev = s0 + if s0 in '\'"': + if inq: + inq = 0 + else: + inq = 1 + qt = s0 + r = r + s0 + return r + +def bracketsBalanced(s): + '''Returns true iff the brackets in s are balanced''' + s = filter(lambda x: x in '()[]{}', s) + stack = [] + brackets = {'(':')', '[':']', '{':'}'} + while len(s) != 0: + if s[0] in ")]}": + if len(stack) != 0 and brackets[stack[-1]] == s[0]: + del stack[-1] + else: + return 0 + else: + stack.append(s[0]) + s = s[1:] + return len(stack) == 0 + +class gtkoutfile: + '''A fake output file object. It sends output to a TK test widget, + and if asked for a file number, returns one set on instance creation''' + def __init__(self, w, fn, font): + self.__fn = fn + self.__w = w + self.__font = font + def close(self): pass + flush = close + def fileno(self): return self.__fn + def isatty(self): return 0 + def read(self, a): return '' + def readline(self): return '' + def readlines(self): return [] + def write(self, s): + #stdout.write(str(self.__w.get_point()) + '\n') + self.__w.freeze() + self.__w.insert(self.__font, self.__w.fg, + self.__w.bg, s) + self.__w.thaw() + self.__w.queue_draw() + def writelines(self, l): + self.__w.freeze() + for s in l: self.__w.insert(self.__font, + self.__w.fg, self.__w.bg, s) + self.__w.thaw() + self.__w.queue_draw() + def seek(self, a): raise IOError, (29, 'Illegal seek') + def tell(self): raise IOError, (29, 'Illegal seek') + truncate = tell + +class Console(GtkVBox): + def __init__(self, namespace={}, copyright='', quit_cb=None): + GtkVBox.__init__(self, spacing=2) + self.set_border_width(2) + self.copyright = copyright + #self.set_usize(475, 300) + + self.quit_cb = quit_cb + + #load the fonts we will use + self.normal = load_font( + "-*-helvetica-medium-r-normal-*-*-100-*-*-*-*-*-*") + self.title = load_font( + "-*-helvetica-bold-r-normal-*-*-100-*-*-*-*-*-*") + self.error = load_font( + "-*-helvetica-medium-o-normal-*-12-100-*-*-*-*-*-*") + self.command = load_font( + "-*-helvetica-bold-r-normal-*-*-100-*-*-*-*-*-*") + + self.inp = GtkHBox() + self.pack_start(self.inp) + self.inp.show() + + self.text = GtkText() + self.text.set_editable(FALSE) + self.text.set_word_wrap(TRUE) + self.text.set_usize(500, 400) + self.inp.pack_start(self.text, padding=1) + self.text.show() + + self.vscroll = GtkVScrollbar(self.text.get_vadjustment()) + self.vscroll.set_update_policy(POLICY_AUTOMATIC) + self.inp.pack_end(self.vscroll, expand=FALSE) + self.vscroll.show() + + self.inputbox = GtkHBox(spacing=2) + self.pack_end(self.inputbox, expand=FALSE) + self.inputbox.show() + + self.prompt = GtkLabel(sys.ps1) + self.prompt.set_padding(xp=2, yp=0) + self.prompt.set_usize(26, -1) + self.inputbox.pack_start(self.prompt, fill=FALSE, expand=FALSE) + self.prompt.show() + + self.closer = GtkButton("Close") + self.closer.connect("clicked", self.quit) + self.inputbox.pack_end(self.closer, fill=FALSE, expand=FALSE) + self.closer.show() + + self.line = GtkEntry() + self.line.set_usize(400,-1) + self.line.connect("key_press_event", self.key_function) + self.inputbox.pack_start(self.line, padding=2) + self.line.show() + + # now let the text box be resized + self.text.set_usize(0, 0) + self.line.set_usize(0, -1) + + self.namespace = namespace + + self.cmd = '' + self.cmd2 = '' + + # set up hooks for standard output. + self.stdout = gtkoutfile(self.text, sys.stdout.fileno(), + self.normal) + self.stderr = gtkoutfile(self.text, sys.stderr.fileno(), + self.error) + + # set up command history + self.history = [''] + self.histpos = 0 + self.namespace['__history__'] = self.history + + def init(self): + self.text.realize() + self.text.style = self.text.get_style() + self.text.fg = self.text.style.fg[STATE_NORMAL] + self.text.bg = self.text.style.white + + self.text.insert(self.title, self.text.fg, + self.text.bg, 'Python %s\n%s\n\n' % + (sys.version, sys.copyright) + + 'Interactive Python-GTK Console - ' + + 'Copyright (C) 1998 James Henstridge\n\n' + + self.copyright + '\n') + self.line.grab_focus() + + def quit(self, *args): + self.hide() + self.destroy() + if self.quit_cb: self.quit_cb() + + def key_function(self, entry, event): + if event.keyval == GDK.Return: + self.line.emit_stop_by_name("key_press_event") + self.eval() + if event.keyval == GDK.Tab: + self.line.emit_stop_by_name("key_press_event") + self.line.append_text('\t') + idle_add(self.focus_text) + elif event.keyval in (GDK.KP_Up, GDK.Up): + self.line.emit_stop_by_name("key_press_event") + self.historyUp() + idle_add(self.focus_text) + elif event.keyval in (GDK.KP_Down, GDK.Down): + self.line.emit_stop_by_name("key_press_event") + self.historyDown() + idle_add(self.focus_text) + elif event.keyval in (GDK.D, GDK.d) and \ + event.state & GDK.CONTROL_MASK: + self.line.emit_stop_by_name("key_press_event") + self.ctrld() + + def focus_text(self): + self.line.grab_focus() + return FALSE # don't requeue this handler + + def ctrld(self): + #self.quit() + pass + + def historyUp(self): + if self.histpos > 0: + l = self.line.get_text() + if len(l) > 0 and l[0] == '\n': l = l[1:] + if len(l) > 0 and l[-1] == '\n': l = l[:-1] + self.history[self.histpos] = l + self.histpos = self.histpos - 1 + self.line.set_text(self.history[self.histpos]) + + def historyDown(self): + if self.histpos < len(self.history) - 1: + l = self.line.get_text() + if len(l) > 0 and l[0] == '\n': l = l[1:] + if len(l) > 0 and l[-1] == '\n': l = l[:-1] + self.history[self.histpos] = l + self.histpos = self.histpos + 1 + self.line.set_text(self.history[self.histpos]) + + def eval(self): + l = self.line.get_text() + '\n' + if len(l) > 1 and l[0] == '\n': l = l[1:] + self.histpos = len(self.history) - 1 + if len(l) > 0 and l[-1] == '\n': + self.history[self.histpos] = l[:-1] + else: + self.history[self.histpos] = l + self.line.set_text('') + self.text.freeze() + self.text.insert(self.command, self.text.fg, self.text.bg, + self.prompt.get() + l) + self.text.thaw() + if l == '\n': + self.run(self.cmd) + self.cmd = '' + self.cmd2 = '' + return + self.histpos = self.histpos + 1 + self.history.append('') + self.cmd = self.cmd + l + self.cmd2 = self.cmd2 + remQuotStr(l) + l = string.rstrip(l) + if not bracketsBalanced(self.cmd2) or l[-1] == ':' or \ + l[-1] == '\\' or l[0] in ' \11': + self.prompt.set_text(sys.ps2) + self.prompt.queue_draw() + return + self.run(self.cmd) + self.cmd = '' + self.cmd2 = '' + + def run(self, cmd): + sys.stdout, self.stdout = self.stdout, sys.stdout + sys.stderr, self.stderr = self.stderr, sys.stderr + try: + try: + r = eval(cmd, self.namespace, self.namespace) + if r is not None: + print `r` + except SyntaxError: + exec cmd in self.namespace + except: + if hasattr(sys, 'last_type') and \ + sys.last_type == SystemExit: + self.quit() + else: + traceback.print_exc() + self.prompt.set_text(sys.ps1) + self.prompt.queue_draw() + adj = self.text.get_vadjustment() + adj.set_value(adj.upper - adj.page_size) + sys.stdout, self.stdout = self.stdout, sys.stdout + sys.stderr, self.stderr = self.stderr, sys.stderr + +def gtk_console(ns, title='Python', copyright='', menu=None): + win = GtkWindow() + win.set_usize(475, 300) + win.connect("destroy", mainquit) + win.connect("delete_event", mainquit) + win.set_title(title) + cons = Console(namespace=ns, copyright=copyright, quit_cb=mainquit) + if menu: + box = GtkVBox() + win.add(box) + box.show() + box.pack_start(menu, expand=FALSE) + menu.show() + box.pack_start(cons) + else: + win.add(cons) + cons.show() + win.show() + win.set_usize(0,0) + cons.init() + mainloop() + +if __name__ == '__main__': + gtk_console({'__builtins__': __builtins__, '__name__': '__main__', + '__doc__': None}) + diff --git a/plug-ins/pygimp/plug-ins/pdbbrowse.py b/plug-ins/pygimp/plug-ins/pdbbrowse.py new file mode 100755 index 0000000000..ca921c2245 --- /dev/null +++ b/plug-ins/pygimp/plug-ins/pdbbrowse.py @@ -0,0 +1,307 @@ +#!/usr/bin/python + +# Gimp-Python - allows the writing of Gimp plugins in Python. +# Copyright (C) 1997 James Henstridge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +from gimpfu import * +import gimpenums +import gtk +import string + +pars = filter(lambda x: x[:6] == 'PARAM_', dir(gimpenums)) +partypes = [''] * len(pars) +for i in pars: + partypes[gimpenums.__dict__[i]] = i[6:] +del pars, i + +class BrowseWin(gtk.GtkWindow): + def __init__(self, ok_button=None): + gtk.GtkWindow.__init__(self) + self.set_title("PDB Browser") + + vbox = gtk.GtkVBox(FALSE, 5) + vbox.set_border_width(2) + self.add(vbox) + vbox.show() + + paned = gtk.GtkHPaned() + vbox.pack_start(paned) + paned.show() + + listsw = gtk.GtkScrolledWindow() + listsw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + paned.add1(listsw) + listsw.show() + + self.list = gtk.GtkCList(1) + self.list.set_column_auto_resize(0, TRUE) + self.list.set_selection_mode(gtk.SELECTION_BROWSE) + listsw.add(self.list) + self.list.show() + self.update_list() + self.list.connect("select_row", self.display) + + self.infosw = gtk.GtkScrolledWindow() + self.infosw.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + paned.add2(self.infosw) + self.infosw.show() + + self.info = None + + paned.set_position(150) + self.cmd = None + self.display(self.list, 0, -1, None) + + hbox = gtk.GtkHBox(FALSE, 5) + vbox.pack_start(hbox, expand=FALSE) + hbox.show() + + entry = gtk.GtkEntry() + hbox.pack_start(entry, expand=FALSE) + entry.show() + + button = gtk.GtkButton("Search by Name") + button.connect("clicked", self.search_name, entry) + hbox.pack_start(button, expand=FALSE) + button.show() + + button = gtk.GtkButton("Search by Blurb") + button.connect("clicked", self.search_blurb, entry) + hbox.pack_start(button, expand=FALSE) + button.show() + + button = gtk.GtkButton("Close") + button.connect("clicked", self.destroy) + hbox.pack_end(button, expand=FALSE) + button.show() + + if ok_button: + button = gtk.GtkButton("OK") + button.connect("clicked", ok_button, self) + hbox.pack_end(button, expand=FALSE) + button.show() + + self.set_default_size(500, 300) + + def search_name(self, button, entry): + self.update_list(name=entry.get_text()) + def search_blurb(self, button, entry): + self.update_list(blurb=entry.get_text()) + + def update_list(self, name='.*', blurb='.*'): + self.pdblist = pdb.query(name,blurb,'.*','.*','.*','.*','.*') + self.pdblist.sort() + self.list.clear() + for item in self.pdblist: + self.list.append([item]) + + def display(self, clist, row, column, event): + proc = pdb[clist.get_text(row, 0)] + self.info = gtk.GtkTable(1, 5, FALSE); + row = 0 + label = gtk.GtkLabel("Name:") + label.set_alignment(1.0, 0.5) + self.info.attach(label, 0,1, row,row+1, + xoptions=gtk.FILL, yoptions=gtk.FILL) + label.show() + + label = gtk.GtkEntry() + label.set_text(proc.proc_name) + label.set_editable(FALSE) + self.info.attach(label, 1,4, row,row+1, + xoptions=gtk.FILL, yoptions=gtk.FILL) + label.show() + row = row + 1 + + label = gtk.GtkLabel("Blurb:") + label.set_alignment(1.0, 0.5) + self.info.attach(label, 0,1, row,row+1, + xoptions=gtk.FILL, yoptions=gtk.FILL) + label.show() + + label = gtk.GtkLabel(proc.proc_blurb) + label.set_alignment(0.0, 0.5) + self.info.attach(label, 1,4, row,row+1, + yoptions=gtk.FILL) + label.show() + row = row + 1 + + label = gtk.GtkLabel("Copyright:") + label.set_alignment(1.0, 0.5) + self.info.attach(label, 0,1, row,row+1, + xoptions=gtk.FILL, yoptions=gtk.FILL) + label.show() + + label = gtk.GtkLabel(proc.proc_date+", "+proc.proc_copyright) + label.set_alignment(0.0, 0.5) + self.info.attach(label, 1,4, row,row+1, + yoptions=gtk.FILL) + label.show() + row = row + 1 + + label = gtk.GtkLabel("Author:") + label.set_alignment(1.0, 0.5) + self.info.attach(label, 0,1, row,row+1, + xoptions=gtk.FILL, yoptions=gtk.FILL) + label.show() + + label = gtk.GtkLabel(proc.proc_author) + label.set_alignment(0.0, 0.5) + self.info.attach(label, 1,4, row,row+1, + yoptions=gtk.FILL) + label.show() + row = row + 1 + + hsep = gtk.GtkHSeparator() + self.info.attach(hsep, 0,4, row,row+1, + yoptions=gtk.FILL) + hsep.show() + row = row + 1 + + if len(proc.params) > 0: + label = gtk.GtkLabel("In:") + label.set_alignment(1.0, 0.5) + self.info.attach(label, 0,1, row,row+len(proc.params), + xoptions=gtk.FILL, yoptions=gtk.FILL) + label.show() + for tp, name, desc in proc.params: + label = gtk.GtkLabel(name) + label.set_alignment(0.0, 0.5) + self.info.attach(label, 1,2, row,row+1, + xoptions=gtk.FILL, + yoptions=gtk.FILL) + label.show() + + label = gtk.GtkLabel(partypes[tp]) + label.set_alignment(0.0, 0.5) + self.info.attach(label, 2,3, row,row+1, + xoptions=gtk.FILL, + yoptions=gtk.FILL) + label.show() + + label = gtk.GtkLabel(desc) + label.set_alignment(0.0, 0.5) + self.info.attach(label, 3,4, row,row+1, + yoptions=gtk.FILL) + label.show() + row = row + 1 + hsep = gtk.GtkHSeparator() + self.info.attach(hsep, 0,4, row,row+1, + yoptions=gtk.FILL) + hsep.show() + row = row + 1 + + if len(proc.return_vals) > 0: + label = gtk.GtkLabel("Out:") + label.set_alignment(1.0, 0.5) + self.info.attach(label, 0,1, + row,row+len(proc.return_vals), + xoptions=gtk.FILL, yoptions=gtk.FILL) + label.show() + for tp, name, desc in proc.return_vals: + label = gtk.GtkLabel(name) + label.set_alignment(0.0, 0.5) + self.info.attach(label, 1,2, row,row+1, + xoptions=gtk.FILL, + yoptions=gtk.FILL) + label.show() + + label = gtk.GtkLabel(partypes[tp]) + label.set_alignment(0.0, 0.5) + self.info.attach(label, 2,3, row,row+1, + xoptions=gtk.FILL, + yoptions=gtk.FILL) + label.show() + + label = gtk.GtkLabel(desc) + label.set_alignment(0.0, 0.5) + self.info.attach(label, 3,4, row,row+1, + yoptions=gtk.FILL) + label.show() + row = row + 1 + hsep = gtk.GtkHSeparator() + self.info.attach(hsep, 0,4, row,row+1, + yoptions=gtk.FILL) + hsep.show() + row = row + 1 + + label = gtk.GtkLabel("Help:") + label.set_alignment(1.0, 0.5) + self.info.attach(label, 0,1, row,row+1, + xoptions=gtk.FILL, yoptions=gtk.FILL) + label.show() + + label = gtk.GtkLabel(proc.proc_help) + label.set_alignment(0.0, 0.5) + label.set_justify(gtk.JUSTIFY_LEFT) + label.set_line_wrap(TRUE) + label.set_usize(300, -1) + self.info.attach(label, 1,4, row,row+1, + yoptions=gtk.FILL) + label.show() + row = row + 1 + + self.info.set_col_spacings(5) + self.info.set_row_spacings(3) + self.info.set_border_width(3) + + children = self.infosw.children() + if children: + self.infosw.remove(children[0]) + + self.infosw.add_with_viewport(self.info) + self.info.show() + + # now setup the self.cmd + self.cmd = '' + if len(proc.return_vals) > 0: + self.cmd = string.join( + map(lambda x: x[1], proc.return_vals), ', ') +\ + ' = ' + if '-' in proc.proc_name: + self.cmd = self.cmd + "pdb['" + proc.proc_name + "']" + else: + self.cmd = self.cmd + "pdb." + proc.proc_name + if len(proc.params) > 0 and proc.params[0][1] == 'run_mode': + params = proc.params[1:] + else: + params = proc.params + self.cmd = self.cmd + "(" + string.join( + map(lambda x: x[1], params), ', ') + ")" + +if __name__ == '__main__': + def extension_pdb_browse(): + gtk.rc_parse(gimp.gtkrc()) + win = BrowseWin() + win.connect("destroy", gtk.mainquit) + win.show() + gtk.mainloop() + register( + "python_fu_pdb_browse", + "Browse the Procedural Database", + "Pick a PDB proc, and read the information", + "James Henstridge", + "James Henstridge", + "1997-1999", + "/Xtns/Python-Fu/PDB Browser", + "*", + [], + [], + extension_pdb_browse) + main() + diff --git a/plug-ins/pygimp/plug-ins/shadow_bevel.py b/plug-ins/pygimp/plug-ins/shadow_bevel.py new file mode 100755 index 0000000000..2c65713555 --- /dev/null +++ b/plug-ins/pygimp/plug-ins/shadow_bevel.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +from gimpfu import * + +have_gimp11 = gimp.major_version > 1 or gimp.major_version == 1 and \ + gimp.minor_version >= 1 + +def shadow_bevel(img, drawable, blur, bevel, do_shadow, drop_x, drop_y): + # disable undo for the image + img.disable_undo() + + # copy the layer + shadow = drawable.copy(TRUE) + img.add_layer(shadow, img.layers.index(drawable)+1) + shadow.name = drawable.name + " shadow" + shadow.preserve_transparency = FALSE + + # threshold the shadow layer to all white + if have_gimp11: + pdb.gimp_threshold(shadow, 0, 255) + else: + pdb.gimp_threshold(img, shadow, 0, 255) + + # blur the shadow layer + pdb.plug_in_gauss_iir(img, shadow, blur, TRUE, TRUE) + + # do the bevel thing ... + if bevel: + pdb.plug_in_bump_map(img, drawable, shadow, 135, 45, 3, + 0, 0, 0, 0, TRUE, FALSE, 0) + + # make the shadow layer black now ... + if have_gimp11: + pdb.gimp_invert(shadow) + else: + pdb.gimp_invert(img, shadow) + + # translate the drop shadow + shadow.translate(drop_x, drop_y) + + if not do_shadow: + # delete shadow ... + gimp.delete(shadow) + + # enable undo again + img.enable_undo() + +register( + "shadow_bevel", + "Add a drop shadow to a layer, and optionally bevel it.", + "Add a drop shadow to a layer, and optionally bevel it.", + "James Henstridge", + "James Henstridge", + "1999", + "/Python-Fu/Effects/Drop Shadow and Bevel", + "RGBA, GRAYA", + [ + (PF_SLIDER, "blur", "Shadow Blur", 6, (1, 30, 1)), + (PF_BOOL, "bevel", "Bevel the image", TRUE), + (PF_BOOL, "shadow", "Make a drop shadow", TRUE), + (PF_INT, "drop_x", "Drop shadow X displacement", 3), + (PF_INT, "drop_y", "Drop shadow Y displacement", 6) + ], + [], + shadow_bevel) + +main() + diff --git a/plug-ins/pygimp/plug-ins/sphere.py b/plug-ins/pygimp/plug-ins/sphere.py new file mode 100755 index 0000000000..eb2a23ecca --- /dev/null +++ b/plug-ins/pygimp/plug-ins/sphere.py @@ -0,0 +1,103 @@ +#!/usr/bin/python + +# Gimp-Python - allows the writing of Gimp plugins in Python. +# Copyright (C) 1997 James Henstridge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import math +from gimpfu import * + +have_gimp11 = gimp.major_version > 1 or \ + gimp.major_version == 1 and gimp.minor_version >= 1 + +def python_sphere(radius, light, shadow, bg_colour, sphere_colour): + width = radius * 3.75 + height = radius * 2.5; + img = gimp.image(width, height, RGB) + drawable = gimp.layer(img, "Sphere Layer", width, height, + RGB_IMAGE, 100, NORMAL_MODE) + radians = light * math.pi / 180 + cx = width / 2 + cy = height / 2 + light_x = cx + radius * 0.6 * math.cos(radians) + light_y = cy - radius * 0.6 * math.sin(radians) + light_end_x = cx + radius * math.cos(math.pi + radians) + light_end_y = cy - radius * math.sin(math.pi + radians) + offset = radius * 0.1 + old_fg = gimp.get_foreground() + old_bg = gimp.get_background() + img.disable_undo() + img.add_layer(drawable, 0) + gimp.set_foreground(sphere_colour) + gimp.set_background(bg_colour) + if have_gimp11: + pdb.gimp_edit_fill(drawable) + else: + pdb.gimp_edit_fill(img, drawable) + gimp.set_background(20, 20, 20) + if (light >= 45 and light <= 75 or light <= 135 and + light >= 105) and shadow: + shadow_w = radius * 2.5 * math.cos(math.pi + radians) + shadow_h = radius * 0.5 + shadow_x = cx + shadow_y = cy + radius * 0.65 + if shadow_w < 0: + shadow_x = cx + shadow_w + shadow_w = -shadow_w + pdb.gimp_ellipse_select(img, shadow_x, shadow_y, + shadow_w, shadow_h, REPLACE, 1, 1, 7.5) + if have_gimp11: + pdb.gimp_bucket_fill(drawable, BG_BUCKET_FILL, + MULTIPLY_MODE, 100, 0, 0, 0, 0) + else: + pdb.gimp_bucket_fill(img, drawable, BG_BUCKET_FILL, + MULTIPLY_MODE, 100, 0, 0, 0, 0) + pdb.gimp_ellipse_select(img, cx - radius, cy - radius, + 2 * radius, 2 * radius, REPLACE, 1, 0, 0) + if have_gimp11: + pdb.gimp_blend(drawable, FG_BG_RGB, NORMAL_MODE, RADIAL, + 100, offset, REPEAT_NONE, 0, 0, 0, light_x, + light_y, light_end_x, light_end_y) + else: + pdb.gimp_blend(img, drawable, FG_BG_RGB, NORMAL_MODE, RADIAL, + 100, offset, REPEAT_NONE, 0, 0, 0, light_x, + light_y, light_end_x, light_end_y) + pdb.gimp_selection_none(img) + gimp.set_background(old_bg) + gimp.set_foreground(old_fg) + img.enable_undo() + disp = gimp.display(img) + +register( + "python_fu_sphere", + "Simple spheres with drop shadows", + "Simple spheres with drop shadows (based on script-fu version)", + "James Henstridge", + "James Henstridge", + "1997-1999", + "/Xtns/Python-Fu/Misc/Sphere", + "RGB*, GRAY*, INDEXED*", + [ + (PF_INT, "radius", "Radius for sphere", 100), + (PF_SLIDER, "light", "light angle", 45, (0,360,1)), + (PF_TOGGLE, "shadow", "shadow?", 1), + (PF_COLOR, "bg_colour", "background", (255,255,255)), + (PF_COLOR, "sphere_colour", "sphere", (255,0,0)) + ], + [], + python_sphere) + +main() diff --git a/plug-ins/pygimp/plug-ins/whirlpinch.py b/plug-ins/pygimp/plug-ins/whirlpinch.py new file mode 100755 index 0000000000..a3ecf93918 --- /dev/null +++ b/plug-ins/pygimp/plug-ins/whirlpinch.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python + +# Gimp-Python - allows the writing of Gimp plugins in Python. +# Copyright (C) 1997 James Henstridge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Algorithms stolen from the whirl and pinch plugin distributed with Gimp, +# by Federico Mena Quintero and Scott Goehring +# +# This version does the same thing, except there is no preview, and it is +# written in python and is slower. + +import math, struct +from gimpfu import * + +class pixel_fetcher: + def __init__(self, drawable): + self.col = -1 + self.row = -1 + self.img_width = drawable.width + self.img_height = drawable.height + self.img_bpp = drawable.bpp + self.img_has_alpha = drawable.has_alpha + self.tile_width = 64 + self.tile_height = 64 + self.bg_colour = '\0\0\0\0' + self.bounds = drawable.mask_bounds + self.drawable = drawable + self.tile = None + def set_bg_colour(self, r, g, b, a): + self.bg_colour = struct.pack('bbb', r,g,b) + if self.img_has_alpha: + self.bg_colour = self.bg_colour + chr(a) + def get_pixel(self, x, y): + sel_x1, sel_y1, sel_x2, sel_y2 = self.bounds + if x < sel_x1 or x >= sel_x2 or y < sel_y1 or y >= sel_y2: + return self.bg_colour + col = x / self.tile_width + coloff = x % self.tile_width + row = y / self.tile_height + rowoff = y % self.tile_height + + if col != self.col or row != self.row or self.tile == None: + self.tile = self.drawable.get_tile(FALSE, row, col) + self.col = col + self.row = row + return self.tile[coloff, rowoff] + +class Dummy: + pass + +def python_whirl_pinch(image, drawable, whirl, pinch, radius): + self = Dummy() + self.width = drawable.width + self.height = drawable.height + self.bpp = drawable.bpp + self.has_alpha = drawable.has_alpha + self.bounds = drawable.mask_bounds + self.sel_x1, self.sel_y1, self.sel_x2, self.sel_y2 = \ + drawable.mask_bounds + self.sel_w = self.sel_x2 - self.sel_x1 + self.sel_h = self.sel_y2 - self.sel_y1 + self.cen_x = (self.sel_x1 + self.sel_x2 - 1) / 2.0 + self.cen_y = (self.sel_y1 + self.sel_y2 - 1) / 2.0 + xhsiz = (self.sel_w - 1) / 2.0 + yhsiz = (self.sel_h - 1) / 2.0 + + if xhsiz < yhsiz: + self.scale_x = yhsiz / xhsiz + self.scale_y = 1.0 + elif xhsiz > yhsiz: + self.scale_x = 1.0 + self.scale_y = xhsiz / yhsiz + else: + self.scale_x = 1.0 + self.scale_y = 1.0 + + self.radius = max(xhsiz, yhsiz); + + if not drawable.is_colour and not drawable.is_grey: + return + + gimp.tile_cache_ntiles(2 * (self.width + 63) / 64) + + whirl = whirl * math.pi / 180 + dest_rgn = drawable.get_pixel_rgn(self.sel_x1, self.sel_y1, + self.sel_w, self.sel_h, TRUE, TRUE) + pft = pixel_fetcher(drawable) + pfb = pixel_fetcher(drawable) + bg_colour = gimp.get_background() + pft.set_bg_colour(bg_colour[0], bg_colour[1], bg_colour[2], 0) + pfb.set_bg_colour(bg_colour[0], bg_colour[1], bg_colour[2], 0) + progress = 0 + max_progress = self.sel_w * self.sel_h + gimp.progress_init("Whirling and pinching...") + self.radius2 = self.radius * self.radius * radius + pixel = ['', '', '', ''] + values = [0,0,0,0] + for row in range(self.sel_y1, (self.sel_y1+self.sel_y2)/2+1): + top_p = '' + bot_p = '' + for col in range(self.sel_x1, self.sel_x2): + q, cx, cy = calc_undistorted_coords(self, col, + row, whirl, pinch, radius) + if q: + if cx >= 0: ix = int(cx) + else: ix = -(int(-cx) + 1) + if cy >= 0: iy = int(cy) + else: iy = -(int(-cx) + 1) + pixel[0] = pft.get_pixel(ix, iy) + pixel[1] = pft.get_pixel(ix+1, iy) + pixel[2] = pft.get_pixel(ix, iy+1) + pixel[3] = pft.get_pixel(ix+1, iy+1) + for i in range(self.bpp): + values[0] = ord(pixel[0][i]) + values[1] = ord(pixel[1][i]) + values[2] = ord(pixel[2][i]) + values[3] = ord(pixel[3][i]) + top_p = top_p + bilinear(cx,cy, values) + cx = self.cen_x + (self.cen_x - cx) + cy = self.cen_y + (self.cen_y - cy) + if cx >= 0: ix = int(cx) + else: ix = -(int(-cx) + 1) + if cy >= 0: iy = int(cy) + else: iy = -(int(-cy) + 1) + pixel[0] = pfb.get_pixel(ix, iy) + pixel[1] = pfb.get_pixel(ix+1, iy) + pixel[2] = pfb.get_pixel(ix, iy+1) + pixel[3] = pfb.get_pixel(ix+1, iy+1) + tmp = '' + for i in range(self.bpp): + values[0] = ord(pixel[0][i]) + values[1] = ord(pixel[1][i]) + values[2] = ord(pixel[2][i]) + values[3] = ord(pixel[3][i]) + tmp = tmp + bilinear(cx,cy, values) + bot_p = tmp + bot_p + else: + top_p = top_p + pft.get_pixel(col, row) + bot_p = pfb.get_pixel((self.sel_x2 - 1) - + (col - self.sel_x1), (self.sel_y2-1) - + (row - self.sel_y1)) + bot_p + dest_rgn[self.sel_x1:self.sel_x2, row] = top_p + dest_rgn[self.sel_x1:self.sel_x2, (self.sel_y2 - 1) + - (row - self.sel_y1)] = bot_p + progress = progress + self.sel_w * 2 + gimp.progress_update(float(progress) / max_progress) + drawable.flush() + drawable.merge_shadow(TRUE) + drawable.update(self.sel_x1,self.sel_y1,self.sel_w,self.sel_h) + +def calc_undistorted_coords(self, wx, wy, whirl, pinch, radius): + dx = (wx - self.cen_x) * self.scale_x + dy = (wy - self.cen_y) * self.scale_y + d = dx * dx + dy * dy + inside = d < self.radius2 + + if inside: + dist = math.sqrt(d / radius) / self.radius + if (d == 0.0): + factor = 1.0 + else: + factor = math.pow(math.sin(math.pi / 2 * dist), + -pinch) + dx = dx * factor + dy = dy * factor + factor = 1 - dist + ang = whirl * factor * factor + sina = math.sin(ang) + cosa = math.cos(ang) + x = (cosa * dx - sina * dy) / self.scale_x + self.cen_x + y = (sina * dx + cosa * dy) / self.scale_y + self.cen_y + else: + x = wx + y = wy + return inside, float(x), float(y) + +def bilinear(x, y, values): + x = x % 1.0 + y = y % 1.0 + m0 = values[0] + x * (values[1] - values[0]) + m1 = values[2] + x * (values[3] - values[2]) + return chr(m0 + y * (m1 - m0)) + + +register( + "python_fu_whirl_pinch", + "Distorts an image by whirling and pinching", + "Distorts an image by whirling and pinching", + "James Henstridge (translated from C plugin)", + "James Henstridge", + "1997-1999", + "/Python-Fu/Distorts/Whirl and Pinch", + "RGB*, GRAY*", + [ + (PF_SLIDER, "whirl", "Whirl angle", 90, (-360, 360, 1)), + (PF_FLOAT, "pinch", "Pinch amount", 0), + (PF_FLOAT, "radius", "radius", 1) + ], + [], + python_whirl_pinch) + +main() + diff --git a/plug-ins/pygimp/py-compile b/plug-ins/pygimp/py-compile new file mode 100755 index 0000000000..b60649de66 --- /dev/null +++ b/plug-ins/pygimp/py-compile @@ -0,0 +1,68 @@ +#!/bin/sh +# called as "py-compile [--basedir DIR] PY_FILES ... + +if [ -z "$PYTHON" ]; then + PYTHON=python +fi + +basedir= + +case "$1" in + --basedir) + basedir=$2 + shift 2 + ;; + --help) + echo "Usage: py-compile [--basedir DIR] PY_FILES ..." + echo "Byte compile some python scripts. This should be performed" + echo "after they have been moved to the final installation location" + exit 0 + ;; + --version) + echo "py-compile version 0.0" + exit 0 + ;; +esac + +if [ $# = 0 ]; then + echo "No files given to $0" 1>&2 + exit 1 +fi + +# if basedir was given, then it should be prepended to filenames before +# byte compilation. +if [ -z "$basedir" ]; then + trans="path = file" +else + trans="path = os.path.join('$basedir', file)" +fi + +$PYTHON -c " +import sys, os, string, py_compile + +files = '''$*''' +print 'Byte-compiling python modules...' +for file in string.split(files): + $trans + if not os.path.exists(path) or not (len(path) >= 3 and path[-3:] == '.py'): + continue + print file, + sys.stdout.flush() + py_compile.compile(path) +print" || exit $? + +# this will fail for python < 1.5, but that doesn't matter ... +$PYTHON -O -c " +import sys, os, string, py_compile + +files = '''$*''' +print 'Byte-compiling python modules (optimised versions) ...' +for file in string.split(files): + $trans + if not os.path.exists(path) or not (len(path) >= 3 and path[-3:] == '.py'): + continue + print file, + sys.stdout.flush() + py_compile.compile(path) +print" 2>/dev/null || : + diff --git a/plug-ins/pygimp/pygimp.spec b/plug-ins/pygimp/pygimp.spec new file mode 100644 index 0000000000..098ef3a4ce --- /dev/null +++ b/plug-ins/pygimp/pygimp.spec @@ -0,0 +1,50 @@ +%define ver 0.3 +%define prefix /usr + +%define py_ver 1.5 +%define gimp_ver 1.0 + +Name: pygimp +Version: %ver +Release: 1 +Summary: A python extension allowing you to write Gimp plugins in Python +Copyright: GPL +Group: X11/Applications/Graphics +Packager: James Henstridge +Requires: gimp python + +Source: ftp://ftp.daa.com.au/pub/james/pygimp/pygimp-%{ver}.tar.gz +BuildRoot: /tmp/pygimp-root + +%description +pygimp allows you to write Gimp plugins with the python language. Unlike +script-fu scripts which only have access to functions in the PDB (procedural +database), pygimp plugins have access to all functionality that C plugins +have, including direct pixel manipulation that is required for many plugins. + +%prep +%setup +CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{prefix} + +%build +make + +%install +rm -rf $RPM_BUILD_ROOT +make DESTDIR=$RPM_BUILD_ROOT install + +%files +%{prefix}/lib/python%{py_ver}/site-packages/gimpmodule.so +%{prefix}/lib/python%{py_ver}/site-packages/gimpenums.py* +%{prefix}/lib/python%{py_ver}/site-packages/plugin.py* +%{prefix}/lib/python%{py_ver}/site-packages/gimpshelf.py* +%{prefix}/lib/python%{py_ver}/site-packages/getvals.py* + +%{prefix}/lib/gimp/%{gimp_ver}/plug-ins/clothify.py +%{prefix}/lib/gimp/%{gimp_ver}/plug-ins/tkcons.py +%{prefix}/lib/gimp/%{gimp_ver}/plug-ins/gimpcons.py +%{prefix}/lib/gimp/%{gimp_ver}/plug-ins/pdbbrowse.py +%{prefix}/lib/gimp/%{gimp_ver}/plug-ins/sphere.py +%{prefix}/lib/gimp/%{gimp_ver}/plug-ins/whirlpinch.py + +%doc README NEWS COPYING doc/*.html \ No newline at end of file