Imported Upstream version 3.27.0 upstream/3.27.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 30 Oct 2018 01:29:43 +0000 (10:29 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 30 Oct 2018 01:29:43 +0000 (10:29 +0900)
94 files changed:
AUTHORS [deleted file]
ChangeLog
MANIFEST.in [new file with mode: 0644]
Makefile.am
Makefile.in
NEWS
PKG-INFO
PKG-INFO.in
README [deleted file]
README.rst [new file with mode: 0644]
configure
configure.ac
examples/Makefile.am
examples/Makefile.in
examples/demo/demo.py [new file with mode: 0755]
examples/demo/demos/Css/__init__.py [new file with mode: 0644]
examples/demo/demos/Css/css_accordion.py [new file with mode: 0644]
examples/demo/demos/Css/css_basics.py [new file with mode: 0644]
examples/demo/demos/Css/css_multiplebgs.py [new file with mode: 0644]
examples/demo/demos/Entry/__init__.py [new file with mode: 0644]
examples/demo/demos/Entry/entry_buffer.py [new file with mode: 0644]
examples/demo/demos/Entry/entry_completion.py [new file with mode: 0644]
examples/demo/demos/Entry/search_entry.py [new file with mode: 0644]
examples/demo/demos/IconView/__init__.py [new file with mode: 0644]
examples/demo/demos/IconView/iconviewbasics.py [new file with mode: 0644]
examples/demo/demos/IconView/iconviewedit.py [new file with mode: 0644]
examples/demo/demos/TreeView/__init__.py [new file with mode: 0644]
examples/demo/demos/TreeView/liststore.py [new file with mode: 0644]
examples/demo/demos/TreeView/treemodel_filelist.py [new file with mode: 0644]
examples/demo/demos/TreeView/treemodel_filetree.py [new file with mode: 0644]
examples/demo/demos/TreeView/treemodel_large.py [new file with mode: 0644]
examples/demo/demos/__init__.py [new file with mode: 0644]
examples/demo/demos/appwindow.py [new file with mode: 0644]
examples/demo/demos/assistant.py [new file with mode: 0644]
examples/demo/demos/builder.py [new file with mode: 0644]
examples/demo/demos/button_box.py [new file with mode: 0644]
examples/demo/demos/clipboard.py [new file with mode: 0644]
examples/demo/demos/colorselector.py [new file with mode: 0644]
examples/demo/demos/combobox.py [new file with mode: 0644]
examples/demo/demos/data/alphatest.png [new file with mode: 0644]
examples/demo/demos/data/apple-red.png [new file with mode: 0644]
examples/demo/demos/data/background.jpg [new file with mode: 0644]
examples/demo/demos/data/brick.png [new file with mode: 0644]
examples/demo/demos/data/brick2.png [new file with mode: 0644]
examples/demo/demos/data/css_accordion.css [new file with mode: 0644]
examples/demo/demos/data/css_basics.css [new file with mode: 0644]
examples/demo/demos/data/css_multiplebgs.css [new file with mode: 0644]
examples/demo/demos/data/cssview.css [new file with mode: 0644]
examples/demo/demos/data/demo.gresource [new file with mode: 0644]
examples/demo/demos/data/demo.gresource.xml [new file with mode: 0644]
examples/demo/demos/data/demo.ui [new file with mode: 0644]
examples/demo/demos/data/floppybuddy.gif [new file with mode: 0644]
examples/demo/demos/data/gnome-applets.png [new file with mode: 0644]
examples/demo/demos/data/gnome-calendar.png [new file with mode: 0644]
examples/demo/demos/data/gnome-foot.png [new file with mode: 0644]
examples/demo/demos/data/gnome-fs-directory.png [new file with mode: 0644]
examples/demo/demos/data/gnome-fs-regular.png [new file with mode: 0644]
examples/demo/demos/data/gnome-gimp.png [new file with mode: 0644]
examples/demo/demos/data/gnome-gmush.png [new file with mode: 0644]
examples/demo/demos/data/gnome-gsame.png [new file with mode: 0644]
examples/demo/demos/data/gnu-keys.png [new file with mode: 0644]
examples/demo/demos/data/gtk-logo-rgb.gif [new file with mode: 0644]
examples/demo/demos/data/reset.css [new file with mode: 0644]
examples/demo/demos/dialogs.py [new file with mode: 0644]
examples/demo/demos/drawingarea.py [new file with mode: 0644]
examples/demo/demos/expander.py [new file with mode: 0644]
examples/demo/demos/flowbox.py [new file with mode: 0755]
examples/demo/demos/images.py [new file with mode: 0644]
examples/demo/demos/infobars.py [new file with mode: 0644]
examples/demo/demos/links.py [new file with mode: 0644]
examples/demo/demos/menus.py [new file with mode: 0644]
examples/demo/demos/pickers.py [new file with mode: 0644]
examples/demo/demos/pixbuf.py [new file with mode: 0644]
examples/demo/demos/printing.py [new file with mode: 0644]
examples/demo/demos/rotatedtext.py [new file with mode: 0644]
examples/demo/demos/test.py [new file with mode: 0644]
gi/_ossighelper.py [new file with mode: 0644]
gi/overrides/GLib.py
gi/overrides/Gio.py
gi/overrides/Gtk.py
m4/ax_code_coverage.m4
m4/ax_compiler_flags_cflags.m4
m4/glib-2.0.m4
pygi-convert.sh [deleted file]
pygobject-3.0-uninstalled.pc.in [deleted file]
pygobject.doap
setup.py [changed mode: 0755->0644]
tests/Makefile.am
tests/Makefile.in
tests/test_everything.py
tests/test_gi.py
tests/test_ossig.py [new file with mode: 0644]
tests/test_overrides_gtk.py
tools/pygi-convert.sh [new file with mode: 0755]

diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644 (file)
index 76b1091..0000000
--- a/AUTHORS
+++ /dev/null
@@ -1,11 +0,0 @@
-Original Authors:
-James Henstridge <james@daa.com.au>
-Johan Dahlin <johan@gnome.org>
-
-Current Maintainers:
-Ignacio Casal Quinteiro <icq@gnome.org>
-Martin Pitt <martinpitt@gnome.org>
-Paolo Borelli <pborelli@gnome.org>
-Sebastian Pölsterl <sebp@k-d-w.org>
-Simon Feltman <s.feltman@gmail.com>
-Tomeu Vizoso <tomeu.vizoso@collabora.co.uk>
index 23d0994756c4cdd6b29721bd3dbf2952b775b5cd..c302ac317455abb578bf5e253cc9ee6fcbf3f864 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,500 @@
-commit 17b4ba8707a8c6c24cd52e59a1f107dccfa9fd55
+commit eb2ff4362878d0e348c67c606a32e8d332e2454d
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Dec 8 16:56:10 2017 +0100
+
+    demo: pep8 fixes
+
+    The demo app now gets analyzed by pep8 since it was moved to the
+    examples.
+
+ examples/demo/demos/Css/css_accordion.py           |    1 +
+ examples/demo/demos/Entry/entry_buffer.py          |    1 +
+ examples/demo/demos/Entry/entry_completion.py      |    1 +
+ examples/demo/demos/Entry/search_entry.py          |    1 +
+ examples/demo/demos/IconView/iconviewbasics.py     |    1 +
+ examples/demo/demos/IconView/iconviewedit.py       |    1 +
+ examples/demo/demos/TreeView/liststore.py          |    2 +
+ examples/demo/demos/TreeView/treemodel_filelist.py |    1 +
+ examples/demo/demos/TreeView/treemodel_filetree.py |    1 +
+ examples/demo/demos/appwindow.py                   |    1 +
+ examples/demo/demos/assistant.py                   |    1 +
+ examples/demo/demos/builder.py                     |    1 +
+ examples/demo/demos/button_box.py                  |    1 +
+ examples/demo/demos/clipboard.py                   |    1 +
+ examples/demo/demos/colorselector.py               |    1 +
+ examples/demo/demos/combobox.py                    |    1 +
+ examples/demo/demos/dialogs.py                     |    1 +
+ examples/demo/demos/drawingarea.py                 |   11 +-
+ examples/demo/demos/expander.py                    |    1 +
+ examples/demo/demos/flowbox.py                     | 1332
+ ++++++++++----------
+ examples/demo/demos/images.py                      |    1 +
+ examples/demo/demos/infobars.py                    |    1 +
+ examples/demo/demos/links.py                       |    1 +
+ examples/demo/demos/menus.py                       |    1 +
+ examples/demo/demos/pickers.py                     |    1 +
+ examples/demo/demos/pixbuf.py                      |    1 +
+ examples/demo/demos/printing.py                    |    1 +
+ examples/demo/demos/rotatedtext.py                 |    1 +
+ 28 files changed, 700 insertions(+), 670 deletions(-)
+
+commit af9ddf322f7cf377996b8fa29906bd4b254dc001
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Dec 8 16:40:33 2017 +0100
+
+    Fix ctypes.PyDLL construction under Windows
+
+    We require the ctypes.pythonapi interface but can't use the global one
+    since we have to change it which could potentially break other users
+    of that interface.
+
+    Turns out simply passing None to PyDLL() only works on Unix to
+    load the
+    CPython library. Instead copy the logic from the ctypes module in the
+    stdlib.
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=622084
+
+ gi/_ossighelper.py | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+commit d19aca693950c0edb02b226db8bcf81a5304870e
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Dec 8 14:40:58 2017 +0100
+
+    configure.ac: Error out in case autoconf-archive isn't installed
+
+    Check whether the AX_IS_RELEASE macro is defined and if not print
+    a proper error message.
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=784428
+
+ configure.ac | 3 +++
+ 1 file changed, 3 insertions(+)
+
+commit 26001a6ae42f8e10ed4c44bc9414e219b946cee0
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Wed Dec 6 18:37:45 2017 +0100
+
+    Move pygi-convert.sh into tools
+
+ MANIFEST.in                              | 2 +-
+ Makefile.am                              | 2 +-
+ pygi-convert.sh => tools/pygi-convert.sh | 0
+ 3 files changed, 2 insertions(+), 2 deletions(-)
+
+commit dd1507be6c198319304342b73c8ec1ec2a102145
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Wed Dec 6 18:26:13 2017 +0100
+
+    README: Convert to reST
+
+ MANIFEST.in          |  2 +-
+ Makefile.am          |  3 ++-
+ README => README.rst | 56
+ ++++++++++++++++++++++++++++++++++------------------
+ 3 files changed, 40 insertions(+), 21 deletions(-)
+
+commit e4a52b9f99667df98806d0c4ee78c2909ead27e1
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Wed Dec 6 17:53:51 2017 +0100
+
+    demo: Move demo into examples and dist it
+
+    We might want to install it in the future, but until then at least put
+    it into the tarball.
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=735918
+
+ MANIFEST.in                                                 |   3 +--
+ examples/Makefile.am                                        |   7 ++++++-
+ {demo => examples/demo}/demo.py                             |   0
+ {demo => examples/demo}/demos/Css/__init__.py               |   0
+ {demo => examples/demo}/demos/Css/css_accordion.py          |   0
+ {demo => examples/demo}/demos/Css/css_basics.py             |   0
+ {demo => examples/demo}/demos/Css/css_multiplebgs.py        |   0
+ {demo => examples/demo}/demos/Entry/__init__.py             |   0
+ {demo => examples/demo}/demos/Entry/entry_buffer.py         |   0
+ {demo => examples/demo}/demos/Entry/entry_completion.py     |   0
+ {demo => examples/demo}/demos/Entry/search_entry.py         |   0
+ {demo => examples/demo}/demos/IconView/__init__.py          |   0
+ {demo => examples/demo}/demos/IconView/iconviewbasics.py    |   0
+ {demo => examples/demo}/demos/IconView/iconviewedit.py      |   0
+ {demo => examples/demo}/demos/TreeView/__init__.py          |   0
+ {demo => examples/demo}/demos/TreeView/liststore.py         |   0
+ .../demo}/demos/TreeView/treemodel_filelist.py              |   0
+ .../demo}/demos/TreeView/treemodel_filetree.py              |   0
+ {demo => examples/demo}/demos/TreeView/treemodel_large.py   |   0
+ {demo => examples/demo}/demos/__init__.py                   |   0
+ {demo => examples/demo}/demos/appwindow.py                  |   0
+ {demo => examples/demo}/demos/assistant.py                  |   0
+ {demo => examples/demo}/demos/builder.py                    |   0
+ {demo => examples/demo}/demos/button_box.py                 |   0
+ {demo => examples/demo}/demos/clipboard.py                  |   0
+ {demo => examples/demo}/demos/colorselector.py              |   0
+ {demo => examples/demo}/demos/combobox.py                   |   0
+ {demo => examples/demo}/demos/data/alphatest.png            | Bin
+ {demo => examples/demo}/demos/data/apple-red.png            | Bin
+ {demo => examples/demo}/demos/data/background.jpg           | Bin
+ {demo => examples/demo}/demos/data/brick.png                | Bin
+ {demo => examples/demo}/demos/data/brick2.png               | Bin
+ {demo => examples/demo}/demos/data/css_accordion.css        |   0
+ {demo => examples/demo}/demos/data/css_basics.css           |   0
+ {demo => examples/demo}/demos/data/css_multiplebgs.css      |   0
+ {demo => examples/demo}/demos/data/cssview.css              |   0
+ {demo => examples/demo}/demos/data/demo.gresource           | Bin
+ {demo => examples/demo}/demos/data/demo.gresource.xml       |   0
+ {demo => examples/demo}/demos/data/demo.ui                  |   0
+ {demo => examples/demo}/demos/data/floppybuddy.gif          | Bin
+ {demo => examples/demo}/demos/data/gnome-applets.png        | Bin
+ {demo => examples/demo}/demos/data/gnome-calendar.png       | Bin
+ {demo => examples/demo}/demos/data/gnome-foot.png           | Bin
+ {demo => examples/demo}/demos/data/gnome-fs-directory.png   | Bin
+ {demo => examples/demo}/demos/data/gnome-fs-regular.png     | Bin
+ {demo => examples/demo}/demos/data/gnome-gimp.png           | Bin
+ {demo => examples/demo}/demos/data/gnome-gmush.png          | Bin
+ {demo => examples/demo}/demos/data/gnome-gsame.png          | Bin
+ {demo => examples/demo}/demos/data/gnu-keys.png             | Bin
+ {demo => examples/demo}/demos/data/gtk-logo-rgb.gif         | Bin
+ {demo => examples/demo}/demos/data/reset.css                |   0
+ {demo => examples/demo}/demos/dialogs.py                    |   0
+ {demo => examples/demo}/demos/drawingarea.py                |   0
+ {demo => examples/demo}/demos/expander.py                   |   0
+ {demo => examples/demo}/demos/flowbox.py                    |   0
+ {demo => examples/demo}/demos/images.py                     |   0
+ {demo => examples/demo}/demos/infobars.py                   |   0
+ {demo => examples/demo}/demos/links.py                      |   0
+ {demo => examples/demo}/demos/menus.py                      |   0
+ {demo => examples/demo}/demos/pickers.py                    |   0
+ {demo => examples/demo}/demos/pixbuf.py                     |   0
+ {demo => examples/demo}/demos/printing.py                   |   0
+ {demo => examples/demo}/demos/rotatedtext.py                |   0
+ {demo => examples/demo}/demos/test.py                       |   0
+ 64 files changed, 7 insertions(+), 3 deletions(-)
+
+commit f8ff3b5e0e769e6db1509426af28728129780529
+Author: Gian Mario Tagliaretti <gianmt@gnome.org>
+Date:   Thu Sep 4 08:51:10 2014 +0100
+
+    demo: Add new Gtk.FlowBox example
+
+    Added a new example for the Gtk.FlowBox class added in GTK+ 3.12.
+    This example additionally makes use of Gtk.HeaderBar.
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=735918
+
+ demo/demos/flowbox.py | 750
+ ++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 750 insertions(+)
+
+commit 3f4a176c77a8026949c04d85d0a822554c4c661c
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date:   Tue Sep 2 10:53:29 2014 -0700
+
+    demo: Use HeaderBar for main app window
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=735918
+
+ demo/demo.py | 23 +++++++++++------------
+ 1 file changed, 11 insertions(+), 12 deletions(-)
+
+commit 995f1e72eed0970d56d871ed8452c61f92a40696
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date:   Tue Sep 2 11:10:31 2014 -0700
+
+    demo: PyFlakes and PEP8 fixes
+
+    Since PyFlakes and PEP8 are not currently run on the demo due to it
+    not being packaged, manually check and fix this for now.
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=735918
+
+ demo/demo.py                         | 2 +-
+ demo/demos/Entry/entry_buffer.py     | 2 +-
+ demo/demos/Entry/entry_completion.py | 2 +-
+ demo/demos/clipboard.py              | 6 +-----
+ demo/demos/combobox.py               | 2 +-
+ 5 files changed, 5 insertions(+), 9 deletions(-)
+
+commit 5d2ce69ed51aec62872ebb2a129c2b8a6b4256af
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date:   Tue Sep 2 11:01:55 2014 -0700
+
+    demo: Rename gtk-demo.py to demo.py
+
+    Remove GTK+ specificity which also allows the demo app to be used as
+    one of the demos itself.
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=735918
+
+ demo/{gtk-demo.py => demo.py} | 0
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+
+commit 7e48d91a5e1f180cbe4adea41bd41939c26c77b0
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date:   Mon Sep 1 22:04:29 2014 -0700
+
+    demo: Rename demos/gtk-demo to simply demo
+
+    Move the entire "pygobject/demos/gtk-demo" folder to "pygobject/demo".
+    Since we only have a single demo app which should be used for all
+    platform demos we can remove the diectory obfuscation.
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=735918
+
+ {demos/gtk-demo => demo}/demos/Css/__init__.py              |   0
+ {demos/gtk-demo => demo}/demos/Css/css_accordion.py         |   0
+ {demos/gtk-demo => demo}/demos/Css/css_basics.py            |   0
+ {demos/gtk-demo => demo}/demos/Css/css_multiplebgs.py       |   0
+ {demos/gtk-demo => demo}/demos/Entry/__init__.py            |   0
+ {demos/gtk-demo => demo}/demos/Entry/entry_buffer.py        |   0
+ {demos/gtk-demo => demo}/demos/Entry/entry_completion.py    |   0
+ {demos/gtk-demo => demo}/demos/Entry/search_entry.py        |   0
+ {demos/gtk-demo => demo}/demos/IconView/__init__.py         |   0
+ {demos/gtk-demo => demo}/demos/IconView/iconviewbasics.py   |   0
+ {demos/gtk-demo => demo}/demos/IconView/iconviewedit.py     |   0
+ {demos/gtk-demo => demo}/demos/TreeView/__init__.py         |   0
+ {demos/gtk-demo => demo}/demos/TreeView/liststore.py        |   0
+ .../gtk-demo => demo}/demos/TreeView/treemodel_filelist.py  |   0
+ .../gtk-demo => demo}/demos/TreeView/treemodel_filetree.py  |   0
+ {demos/gtk-demo => demo}/demos/TreeView/treemodel_large.py  |   0
+ {demos/gtk-demo => demo}/demos/__init__.py                  |   0
+ {demos/gtk-demo => demo}/demos/appwindow.py                 |   0
+ {demos/gtk-demo => demo}/demos/assistant.py                 |   0
+ {demos/gtk-demo => demo}/demos/builder.py                   |   0
+ {demos/gtk-demo => demo}/demos/button_box.py                |   0
+ {demos/gtk-demo => demo}/demos/clipboard.py                 |   0
+ {demos/gtk-demo => demo}/demos/colorselector.py             |   0
+ {demos/gtk-demo => demo}/demos/combobox.py                  |   0
+ {demos/gtk-demo => demo}/demos/data/alphatest.png           | Bin
+ {demos/gtk-demo => demo}/demos/data/apple-red.png           | Bin
+ {demos/gtk-demo => demo}/demos/data/background.jpg          | Bin
+ {demos/gtk-demo => demo}/demos/data/brick.png               | Bin
+ {demos/gtk-demo => demo}/demos/data/brick2.png              | Bin
+ {demos/gtk-demo => demo}/demos/data/css_accordion.css       |   0
+ {demos/gtk-demo => demo}/demos/data/css_basics.css          |   0
+ {demos/gtk-demo => demo}/demos/data/css_multiplebgs.css     |   0
+ {demos/gtk-demo => demo}/demos/data/cssview.css             |   0
+ {demos/gtk-demo => demo}/demos/data/demo.gresource          | Bin
+ {demos/gtk-demo => demo}/demos/data/demo.gresource.xml      |   0
+ {demos/gtk-demo => demo}/demos/data/demo.ui                 |   0
+ {demos/gtk-demo => demo}/demos/data/floppybuddy.gif         | Bin
+ {demos/gtk-demo => demo}/demos/data/gnome-applets.png       | Bin
+ {demos/gtk-demo => demo}/demos/data/gnome-calendar.png      | Bin
+ {demos/gtk-demo => demo}/demos/data/gnome-foot.png          | Bin
+ {demos/gtk-demo => demo}/demos/data/gnome-fs-directory.png  | Bin
+ {demos/gtk-demo => demo}/demos/data/gnome-fs-regular.png    | Bin
+ {demos/gtk-demo => demo}/demos/data/gnome-gimp.png          | Bin
+ {demos/gtk-demo => demo}/demos/data/gnome-gmush.png         | Bin
+ {demos/gtk-demo => demo}/demos/data/gnome-gsame.png         | Bin
+ {demos/gtk-demo => demo}/demos/data/gnu-keys.png            | Bin
+ {demos/gtk-demo => demo}/demos/data/gtk-logo-rgb.gif        | Bin
+ {demos/gtk-demo => demo}/demos/data/reset.css               |   0
+ {demos/gtk-demo => demo}/demos/dialogs.py                   |   0
+ {demos/gtk-demo => demo}/demos/drawingarea.py               |   0
+ {demos/gtk-demo => demo}/demos/expander.py                  |   0
+ {demos/gtk-demo => demo}/demos/images.py                    |   0
+ {demos/gtk-demo => demo}/demos/infobars.py                  |   0
+ {demos/gtk-demo => demo}/demos/links.py                     |   0
+ {demos/gtk-demo => demo}/demos/menus.py                     |   0
+ {demos/gtk-demo => demo}/demos/pickers.py                   |   0
+ {demos/gtk-demo => demo}/demos/pixbuf.py                    |   0
+ {demos/gtk-demo => demo}/demos/printing.py                  |   0
+ {demos/gtk-demo => demo}/demos/rotatedtext.py               |   0
+ {demos/gtk-demo => demo}/demos/test.py                      |   0
+ {demos/gtk-demo => demo}/gtk-demo.py                        |   0
+ 61 files changed, 0 insertions(+), 0 deletions(-)
+
+commit 1ea36ccfc755693b9ce4cf28be549704bf5370d3
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Wed Dec 6 16:54:54 2017 +0100
+
+    Remove AUTHORS file
+
+    No need to duplicate that information over and over.
+    It's in the README and .doap file still
+
+ AUTHORS     | 11 -----------
+ MANIFEST.in |  1 -
+ README      |  4 ++--
+ 3 files changed, 2 insertions(+), 14 deletions(-)
+
+commit 9462873823a5459276400c0d6cf11e084b681751
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Wed Dec 6 15:55:54 2017 +0100
+
+    Remove pre-commit.hook
+
+    It was obviously not used in some time as the git command doesn't
+    exist anymore.
+    If we ever get per pull/merge request CI we can look into bringing
+    something
+    like this back.
+
+ MANIFEST.in     |  1 -
+ pre-commit.hook | 39 ---------------------------------------
+ 2 files changed, 40 deletions(-)
+
+commit 3e455944f5835c750911d3178a0607201f23f1a8
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Mon Dec 4 15:33:00 2017 +0100
+
+    setup.py: Port to distutils/setuptools
+
+    Instead of wrapping autotools add a proper setuptools based build
+    system.
+    Compared to the autotools one this does not install .pc files
+    or headers
+    and does not allow running tests.
+
+    It uses pkg-config for discovering dependencies and explictely
+    searches
+    for .pc files in the Python prefix so that pycairo installations in a
+    virtualenv are discovered. When using MSVC, pkg-config is skipped and
+    it is assumend that INCLUDE and LIB is properly set up.
+
+    Version information and requirements are parsed from configure.ac,
+    package
+    metadata is parsed from PKG-INFO.in.
+
+    Also adds a "setup.py distcheck" command which makes sure all
+    tracked files
+    end up in the tarball and that the tarball builds (no tests are
+    run atm).
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=789211
+
+ .gitignore   |   1 +
+ MANIFEST.in  |  20 +++
+ Makefile.am  |   3 +-
+ PKG-INFO.in  |   8 +-
+ configure.ac |   3 +-
+ setup.py     | 409
+ +++++++++++++++++++++++++++++++++++++++++++++++------------
+ 6 files changed, 359 insertions(+), 85 deletions(-)
+
+commit 58f677bfaa0f117465a9e2146c5d83768b5a76ac
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Nov 24 13:11:26 2017 +0100
+
+    Install a default SIGINT handler for functions which start an
+    event loop
+
+    Currently ctrl+c on a program blocked on Gtk.main() will raise
+    an exception
+    but not return control. While it's easy to set up the proper signal
+    handling and
+    stop the event loop or execute some other application shutdown code
+    it's nice to have a good default behaviour for small
+    prototypes/examples
+    or when testing some code in an interactive console.
+
+    This adds a context manager which registers a SIGINT handler only
+    in case
+    the default Python signal handler is active and restores the
+    original handle
+    afterwards. Since signal handlers registered through
+    g_unix_signal_add()
+    are not detected by Python's signal module we use PyOS_getsig()
+    through ctypes
+    to detect if the signal handler is changed from outside.
+
+    In case of nested event loops, all of them will be aborted.
+    In case an event loop is started in a thread, nothing will happen.
+
+    The context manager is used in the overrides for Gtk.main(),
+    Gtk.Dialog.run(),
+    Gio.Application.run() and GLib.MainLoop.run()
+
+    This also fixes GLib.MainLoop.run() replacing a non-default signal
+    handler
+    and not restoring the default one:
+        https://bugzilla.gnome.org/show_bug.cgi?id=698623
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=622084
+
+ gi/_ossighelper.py   | 115
+ +++++++++++++++++++++++++++++++++++++++++++++++++++
+ gi/overrides/GLib.py |  31 +++-----------
+ gi/overrides/Gio.py  |   7 ++--
+ gi/overrides/Gtk.py  |  12 +++---
+ tests/test_ossig.py  |  73 +++++++++++++++++++++++++++++++-
+ 5 files changed, 203 insertions(+), 35 deletions(-)
+
+commit a321f6e9d8f5b8e779892eab4ce759b60ff98e39
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Nov 17 20:05:24 2017 +0100
+
+    Make Python OS signal handlers run when an event loop is idling
+
+    When Python receives a signal such as SIGINT it sets a flag and
+    will execute
+    the registered signal handler on the next call to
+    PyErr_CheckSignals().
+    In case the main thread is blocked by an idling event loop (say
+    Gtk.main()
+    or Gtk.Dialog.run()) the check never happens and the signal handler
+    will not get executed.
+
+    To work around the issue use signal.set_wakeup_fd() to wake up
+    the active
+    event loop when a signal is received, which will invoke a Python
+    callback
+    which will lead to the signal handler being executed.
+
+    This patch enables it in overrides for Gtk.main(), Gtk.Dialog.run(),
+    Gio.Application.run() and GLib.MainLoop.run().
+
+    Works on Unix, and on Windows with Python 3.5+.
+
+    With this fix in place it is possible to have a cross platform way to
+    react to SIGINT (GLib.unix_signal_add() worked, but not on Windows),
+    for example:
+
+        signal.signal(signal.SIGINT, lambda *args: Gtk.main_quit())
+        Gtk.main()
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=622084
+
+ Makefile.am          |   3 +-
+ gi/_ossighelper.py   | 137
+ +++++++++++++++++++++++++++++++++++++++++++++++++++
+ gi/overrides/GLib.py |   4 +-
+ gi/overrides/Gio.py  |  12 +++++
+ gi/overrides/Gtk.py  |  14 ++++++
+ tests/Makefile.am    |   1 +
+ tests/test_ossig.py  | 102 ++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 271 insertions(+), 2 deletions(-)
+
+commit 46a9dade170127006df98d44b1a9fb2035ada86b
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Nov 24 17:40:11 2017 +0100
+
+    Drop Python 3.3 support
+
+    It's EOL and not used much (https://hynek.me/articles/python3-2016/)
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=790787
+
+ configure.ac | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit bd165405754ba44dea12fd3f31b841b5c8ba0f1a
+Author: Sander Sweers <infirit@gmail.com>
+Date:   Sun Aug 20 22:44:15 2017 +0200
+
+    Drop set_value usage in Gtk.List/TreeStore.set override
+
+    this causes multiple updates to the store each emitting a signal.
+
+    https://bugzilla.gnome.org/show_bug.cgi?id=790346
+
+ gi/overrides/Gtk.py         | 48
+ ++++++++++++++++++++++++---------------------
+ tests/test_overrides_gtk.py | 12 ++++++++++++
+ 2 files changed, 38 insertions(+), 22 deletions(-)
+
+commit 5f63b8c626eb7f27de346dac2b66f07794e61b07
 Author: Christoph Reiter <creiter@src.gnome.org>
 Date:   Thu Oct 26 17:24:02 2017 +0200
 
@@ -31,7 +527,7 @@ Date:   Thu Oct 26 17:24:02 2017 +0200
  tests/test_signal.py  | 29 ++++++++++++++++++++++++++++-
  2 files changed, 32 insertions(+), 3 deletions(-)
 
-commit b4bf1b9d936e021b1645c069c2e0a3062cfab62b
+commit f13b0d6e0d97e21aa2a4553a036362dec9d0c4cb
 Author: Daniel Colascione <dancol@dancol.org>
 Date:   Tue Oct 24 14:42:43 2017 +0200
 
@@ -62,7 +558,7 @@ Date:   Tue Oct 24 14:42:43 2017 +0200
  gi/gimodule.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
-commit 1136f385d6080297bd57715b749c67f5e7208ba2
+commit 280e80c54e6f2c91638195b92736f5c2b34bbbbd
 Author: Christoph Reiter <creiter@src.gnome.org>
 Date:   Thu Oct 26 09:35:09 2017 +0200
 
@@ -77,7 +573,7 @@ Date:   Thu Oct 26 09:35:09 2017 +0200
  tests/test_gi.py | 1 -
  1 file changed, 1 deletion(-)
 
-commit a37687d3d8bdc42aea63e551401e6686c926c556
+commit 5f41add624990255dfebf2d726af946599f7bcf6
 Author: Christoph Reiter <creiter@src.gnome.org>
 Date:   Mon Oct 23 12:41:45 2017 +0200
 
@@ -94,7 +590,7 @@ Date:   Mon Oct 23 12:41:45 2017 +0200
  tests/test_gi.py                |  5 +++++
  3 files changed, 45 insertions(+)
 
-commit 44a852191a67bc7ef76202412a0102de46eb26f0
+commit 0695d234b0e3e9b0f15a7548816c5c4c96a41ab5
 Author: Philippe Renon <philippe_renon@yahoo.fr>
 Date:   Thu Aug 31 16:39:08 2017 +0200
 
@@ -109,7 +605,7 @@ Date:   Thu Aug 31 16:39:08 2017 +0200
  gi/pygi-enum-marshal.c | 8 ++++----
  1 file changed, 4 insertions(+), 4 deletions(-)
 
-commit fa4330df4e26bb9f77a5cf081d3cc40c342709b9
+commit 1bf7d2cddcd24f619a268d0af85d7919d72bacba
 Author: Christoph Reiter <creiter@src.gnome.org>
 Date:   Sun Oct 22 17:59:17 2017 +0200
 
@@ -126,7 +622,16 @@ Date:   Sun Oct 22 17:59:17 2017 +0200
  tests/test_gi.py                |  6 ++++++
  4 files changed, 32 insertions(+)
 
-commit 5f0f3b330cfa1eb11db4f376d141445847cb9d16
+commit e502d0097f28e6c65d3d5120230fb428aabbc083
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Oct 20 09:48:07 2017 +0200
+
+    pygobject.doap: Add myself as maintainer
+
+ pygobject.doap | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+commit 37db51220668238e4870b7220b496803d942691b
 Author: James Clarke <jrtc27@jrtc27.com>
 Date:   Fri Oct 13 18:04:45 2017 +0100
 
@@ -146,7 +651,7 @@ Date:   Fri Oct 13 18:04:45 2017 +0100
  gi/pygi-closure.c | 40 +++++++++++++++++++++-------------------
  1 file changed, 21 insertions(+), 19 deletions(-)
 
-commit d831decad9e8fdb449518997dee1a5eaa21e0313
+commit 3c791a5d4b17d647a531de04469d04f77fce0548
 Author: Christoph Reiter <creiter@src.gnome.org>
 Date:   Fri Oct 13 19:24:01 2017 +0200
 
@@ -161,11 +666,58 @@ Date:   Fri Oct 13 19:24:01 2017 +0200
  Makefile.am | 7 +++++++
  1 file changed, 7 insertions(+)
 
-commit 5b61ac3f2a66d93110642f43bec4f2a4e656681a
+commit c4995493c233225b1fa5a3e5406e78d02e3b86e6
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Oct 13 17:22:02 2017 +0200
+
+    Drop pygobject-3.0-uninstalled.pc file
+
+    Like glib/gtk+ did in 2011:
+    https://git.gnome.org/browse/glib/commit/?id=306aa62ea5fa4d3a57bca419afcc159f9509b476
+
+ configure.ac                    |  1 -
+ pygobject-3.0-uninstalled.pc.in | 12 ------------
+ 2 files changed, 13 deletions(-)
+
+commit 3363049be9d8cd277b33127427694588598aa6dd
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Oct 13 16:08:01 2017 +0200
+
+    tests: Windows fix
+
+    some fallout from fd5f2a09ce48329d2df191eca9a0cea926ddfb5b
+
+ tests/test_everything.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit fd5f2a09ce48329d2df191eca9a0cea926ddfb5b
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Oct 13 15:29:40 2017 +0200
+
+    tests: some more C locale fixes
+
+    see f40df0a09803ec9c729d842f1f83c6d56aebac22
+
+ tests/test_everything.py | 8 +++++++-
+ tests/test_gi.py         | 6 ++++--
+ 2 files changed, 11 insertions(+), 3 deletions(-)
+
+commit f40df0a09803ec9c729d842f1f83c6d56aebac22
+Author: Christoph Reiter <creiter@src.gnome.org>
+Date:   Fri Oct 13 14:32:44 2017 +0200
+
+    tests: Make the test suite pass with the C locale
+
+    Some filename tests assumed a Unicode locale, skip those.
+
+ tests/test_gi.py | 29 +++++++++++++++++++++++++----
+ 1 file changed, 25 insertions(+), 4 deletions(-)
+
+commit 857fb023886476988d99d35c92894cdbc1bbeab2
 Author: Christoph Reiter <creiter@src.gnome.org>
-Date:   Thu Oct 12 18:58:04 2017 +0200
+Date:   Tue Sep 12 08:35:12 2017 +0200
 
-    configure.ac: version bump to 3.26.1
+    configure.ac: post-release version bump to 3.27.0
 
  configure.ac | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644 (file)
index 0000000..b9f067e
--- /dev/null
@@ -0,0 +1,17 @@
+include *.am
+include autogen.sh
+include configure.ac
+include COPYING
+include HACKING
+include *.in
+include INSTALL
+include m4/introspection.m4
+include m4/python.m4
+include NEWS
+include tools/pygi-convert.sh
+include pygobject.doap
+include README.rst
+recursive-include examples *.py *.am *.png *.css *.ui *.gif *.gresource *.jpg *.xml
+recursive-include gi *.am *.h
+recursive-include pygtkcompat *.am
+recursive-include tests *.py *.c *.h *.xml *.supp *nouppera *.am
index c3ba1a80d1c08da8e306447cef018bfeb0f6a87d..643aefc714e9ee3fbdd60016c6f55c64720db4b8 100644 (file)
@@ -17,10 +17,12 @@ EXTRA_DIST = \
        pygobject-$(PLATFORM_VERSION).pc.in \
        PKG-INFO \
        PKG-INFO.in \
-       pygi-convert.sh \
+       tools/pygi-convert.sh \
        m4/python.m4 \
        m4/introspection.m4 \
-       setup.py
+       setup.py \
+       MANIFEST.in \
+       README.rst
 
 MAINTAINERCLEANFILES = \
        $(srcdir)/INSTALL \
@@ -63,7 +65,8 @@ nobase_pyexec_PYTHON = \
        gi/_propertyhelper.py \
        gi/_signalhelper.py \
        gi/_option.py \
-       gi/_error.py
+       gi/_error.py \
+       gi/_ossighelper.py
 
 # if we build in a separate tree, we need to symlink the *.py files from the
 # source tree; Python does not accept the extensions and modules in different
index 08ce9e1661c087bb1543728c1e25074595989f77..9190734ba79aa1728f6eb56d1c39975df56068ec 100644 (file)
@@ -117,8 +117,7 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
  configure.lineno config.status.lineno
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = config.h
-CONFIG_CLEAN_FILES = pygobject-3.0.pc pygobject-3.0-uninstalled.pc \
-       PKG-INFO
+CONFIG_CLEAN_FILES = pygobject-3.0.pc PKG-INFO
 CONFIG_CLEAN_VPATH_FILES =
 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
 am__vpath_adj = case $$p in \
@@ -213,11 +212,9 @@ CTAGS = ctags
 CSCOPE = cscope
 DIST_SUBDIRS = $(SUBDIRS)
 am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/PKG-INFO.in \
-       $(srcdir)/config.h.in \
-       $(srcdir)/pygobject-3.0-uninstalled.pc.in \
-       $(srcdir)/pygobject-3.0.pc.in AUTHORS COPYING ChangeLog \
-       INSTALL NEWS README compile config.guess config.sub install-sh \
-       ltmain.sh missing py-compile
+       $(srcdir)/config.h.in $(srcdir)/pygobject-3.0.pc.in COPYING \
+       ChangeLog INSTALL NEWS compile config.guess config.sub \
+       install-sh ltmain.sh missing py-compile
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)
@@ -454,10 +451,12 @@ EXTRA_DIST = \
        pygobject-$(PLATFORM_VERSION).pc.in \
        PKG-INFO \
        PKG-INFO.in \
-       pygi-convert.sh \
+       tools/pygi-convert.sh \
        m4/python.m4 \
        m4/introspection.m4 \
-       setup.py
+       setup.py \
+       MANIFEST.in \
+       README.rst
 
 MAINTAINERCLEANFILES = \
        $(srcdir)/INSTALL \
@@ -500,7 +499,8 @@ nobase_pyexec_PYTHON = \
        gi/_propertyhelper.py \
        gi/_signalhelper.py \
        gi/_option.py \
-       gi/_error.py
+       gi/_error.py \
+       gi/_ossighelper.py
 
 
 # pkg-config files
@@ -564,8 +564,6 @@ distclean-hdr:
        -rm -f config.h stamp-h1
 pygobject-3.0.pc: $(top_builddir)/config.status $(srcdir)/pygobject-3.0.pc.in
        cd $(top_builddir) && $(SHELL) ./config.status $@
-pygobject-3.0-uninstalled.pc: $(top_builddir)/config.status $(srcdir)/pygobject-3.0-uninstalled.pc.in
-       cd $(top_builddir) && $(SHELL) ./config.status $@
 PKG-INFO: $(top_builddir)/config.status $(srcdir)/PKG-INFO.in
        cd $(top_builddir) && $(SHELL) ./config.status $@
 
diff --git a/NEWS b/NEWS
index 9070fd91e8e86b888f7e0507a9672c915bd6ee79..b663a0a0300da80ba6f20f0408f68b41a5d16bf3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,13 +1,36 @@
-3.26.1  27-Oct-2017
+3.27.0  08-Dec-2017
+        - demo: pep8 fixes (Christoph Reiter)
+        - Fix ctypes.PyDLL construction under Windows (Christoph Reiter) (#622084)
+        - configure.ac: Error out in case autoconf-archive isn't installed (Christoph Reiter) (#784428)
+        - Move pygi-convert.sh into tools (Christoph Reiter)
+        - README: Convert to reST (Christoph Reiter)
+        - demo: Move demo into examples and dist it (Christoph Reiter) (#735918)
+        - demo: Add new Gtk.FlowBox example (Gian Mario Tagliaretti) (#735918)
+        - demo: Use HeaderBar for main app window (Simon Feltman) (#735918)
+        - demo: PyFlakes and PEP8 fixes (Simon Feltman) (#735918)
+        - demo: Rename gtk-demo.py to demo.py (Simon Feltman) (#735918)
+        - demo: Rename demos/gtk-demo to simply demo (Simon Feltman) (#735918)
+        - Remove AUTHORS file (Christoph Reiter)
+        - Remove pre-commit.hook (Christoph Reiter)
+        - setup.py: Port to distutils/setuptools (Christoph Reiter) (#789211)
+        - Install a default SIGINT handler for functions which start an event loop (Christoph Reiter) (#622084)
+        - Make Python OS signal handlers run when an event loop is idling (Christoph Reiter) (#622084)
+        - Drop Python 3.3 support (Christoph Reiter) (#790787)
+        - Drop set_value usage in Gtk.List/TreeStore.set override (Sander Sweers) (#790346)
         - pygobject-object: Fix Python GC collecting a ref cycle too early (Christoph Reiter) (#731501)
         - Fix potential uninitialized memory access during GC (Daniel Colascione) (#786872)
         - test: revert parts of the previous test as it's broken on 32 bit builds (Christoph Reiter) (#786948)
         - flags: Add testcase for bug 786948 (Christoph Reiter) (#786948)
         - fix potential overflow when marshalling flags from py interface (Philippe Renon) (#786948)
         - to_py_array: Properly handle enum array items (Christoph Reiter) (#788890)
+        - pygobject.doap: Add myself as maintainer (Christoph Reiter)
         - closure: Fix unaligned and out-of-bounds access (James Clarke) (#788894)
         - build: Fix not installing .egg-info file (Christoph Reiter) (#777719)
-        - configure.ac: version bump to 3.26.1 (Christoph Reiter)
+        - Drop pygobject-3.0-uninstalled.pc file (Christoph Reiter)
+        - tests: Windows fix (Christoph Reiter)
+        - tests: some more C locale fixes (Christoph Reiter)
+        - tests: Make the test suite pass with the C locale (Christoph Reiter)
+        - configure.ac: post-release version bump to 3.27.0 (Christoph Reiter)
 
 3.26.0  12-Sep-2017
         - configure.ac: pre-release version bump to 3.26.0 (Christoph Reiter)
index ff24db0b8114575a2556834d845e3fc1e32b1425..279cc409ca780052bd1d2513a855e28da908dcdb 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,15 +1,15 @@
 Metadata-Version: 1.0
-Name: PyGObject
-Version: 3.26.1
-Summary: Python bindings for GObject
-Home-page: http://www.pygtk.org/
+Name: pygobject
+Version: 3.27.0
+Summary: Python bindings for GObject Introspection
+Home-page: https://pygobject.readthedocs.io
 Author: James Henstridge
 Author-email: james@daa.com.au
 Maintainer: Simon Feltman
 Maintainer-email: sfeltman@src.gnome.org
 License: GNU LGPL
-Download-url: ftp://ftp.gnome.org/pub/GNOME/sources/pygobject/3.26/pygobject-3.26.1.tar.gz
-Description: Python bindings for GLib and GObject
+Download-url: ftp://ftp.gnome.org/pub/GNOME/sources/pygobject/3.27/pygobject-3.27.0.tar.gz
+Description: Python bindings for GObject Introspection
 Platform: POSIX, Windows
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Linux
index 651dabebe14a1b8fa9be1738e31e50cc70b8f16a..bebaf0804a8bdebf1d9d79bfe701f7c491fc9e49 100644 (file)
@@ -1,15 +1,15 @@
 Metadata-Version: 1.0
-Name: PyGObject
+Name: pygobject
 Version: @VERSION@
-Summary: Python bindings for GObject
-Home-page: http://www.pygtk.org/
+Summary: Python bindings for GObject Introspection
+Home-page: https://pygobject.readthedocs.io
 Author: James Henstridge
 Author-email: james@daa.com.au
 Maintainer: Simon Feltman
 Maintainer-email: sfeltman@src.gnome.org
 License: GNU LGPL
 Download-url: ftp://ftp.gnome.org/pub/GNOME/sources/pygobject/@PYGOBJECT_MAJOR_VERSION@.@PYGOBJECT_MINOR_VERSION@/pygobject-@VERSION@.tar.gz
-Description: Python bindings for GLib and GObject
+Description: Python bindings for GObject Introspection
 Platform: POSIX, Windows
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Linux
diff --git a/README b/README
deleted file mode 100644 (file)
index eb7ec8a..0000000
--- a/README
+++ /dev/null
@@ -1,108 +0,0 @@
-PyGObject
-=====
-Original authors:   James Henstridge <james@daa.com.au>
-                    Johan Dahlin <johan@gnome.org>
-
-Current maintainers:  Tomeu Vizoso <tomeu.vizoso@collabora.co.uk>
-                      Martin Pitt <martinpitt@gnome.org>
-                      Paolo Borelli <pborelli@gnome.org>
-                      Ignacio Casal Quinteiro <icq@gnome.org>
-                      Sebastian Pölsterl <sebp@k-d-w.org>
-                     Simon Feltman <sfeltman@gnome.org>
-
-
-This archive contains bindings for the GLib, and GObject,
-to be used in Python. It is a fairly complete set of bindings,
-it's already rather useful, and is usable to write moderately
-complex programs.  (see the examples directory for some examples
-of the simpler programs you could write).
-
-If you have any enhancements or bug reports, please file them in
-bugzilla at:
-  http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject
-
-If you have a patch, file the bug first and then use the "create new
-attachment" link on the bug's info page.  My preferred format for
-patches is unified diff format (ie. diff -u).  Please don't send me
-diffs which don't have any context, as these make it very difficult to
-see what the patch does.
-
-New Versions
-============
-
-New versions of this package can be found at:
-  http://ftp.gnome.org/pub/GNOME/sources/pygobject/
-
-
-Mailing list
-============
-
-pygobject development is discussed on the GNOME python-hackers mailing list.
-You can subscribe to it through the web interface:
-
-  https://mail.gnome.org/mailman/listinfo/python-hackers-list/
-
-Requirements
-============
-  * C compiler (GCC and MSVC supported)
-  * Python 2.7 or higher
-  * Glib/Gio 2.38.0 or higher
-  * gobject-introspection 1.46.0 or higher
-  * libffi (optional)
-
-Copyright Information
-=====================
-
-This software is covered by the GNU Lesser General Public Licence
-(version 2.1, or if you choose, a later version).  Basically just don't
-say you wrote bits you didn't.
-
-Compilation
-===========
-
-PyGObject uses the standard autotools for the build infrastructure.  To
-build, it should be as simple as running:
-
-    $ ./configure --prefix=<prefix where python is installed>
-    $ make
-    $ make install
-
-By default, configure searches for a few well-known Python interpreter
-names, such as "python3", "python2", "python2.7", or "python".  If your
-Python interpreter isn't in the path, or is not called "python", you can
-configure pygobject to build against that with --with-python=<path> or
-setting the PYTHON environment variable:
-
-   $ ./configure --with-python=python3
-   $ PYTHON=python3.2 ./configure
-   $ ./configure --with-python=~/my-patched-python/python
-
-If configure can't find GTK+, you may need to set the PKG_CONFIG_PATH
-environment variable to help it find the libraries.
-
-The "make install" target will generate normal and optimised bytecode
-for all the .py files.
-
-Note. If you're installing to another prefix than the one where python
-is installed you'll need to set the PYTHONPATH variable to the
-$prefix/lib/pythonX.Y/site-packages directory created by
-the PyGObject installation.
-
-Tests
-=====
-
-After having compiled and installed pygobject, you may want to test them.
-There are a number of example programs available in the examples/
-subdirectory.
-
-
-Getting Help
-============
-
-If you have questions about programming with PyGObject, you might want to
-check the documentation on
-
-  https://live.gnome.org/PyGObject/
-
-If that does not help, send a message to the mailing list (information on
-subscribing is above), or join #python on irc.gnome.org.
diff --git a/README.rst b/README.rst
new file mode 100644 (file)
index 0000000..7eb7c8b
--- /dev/null
@@ -0,0 +1,126 @@
+=========
+PyGObject
+=========
+
+This archive contains bindings for the GLib, and GObject,
+to be used in Python. It is a fairly complete set of bindings,
+it's already rather useful, and is usable to write moderately
+complex programs.  (see the examples directory for some examples
+of the simpler programs you could write).
+
+If you have any enhancements or bug reports, please file them in
+bugzilla at:
+
+    http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject
+
+If you have a patch, file the bug first and then use the "create new
+attachment" link on the bug's info page.  My preferred format for
+patches is unified diff format (ie. diff -u).  Please don't send me
+diffs which don't have any context, as these make it very difficult to
+see what the patch does.
+
+
+New Versions
+============
+
+New versions of this package can be found at:
+
+    http://ftp.gnome.org/pub/GNOME/sources/pygobject/
+
+
+Mailing list
+============
+
+pygobject development is discussed on the GNOME python-hackers mailing list.
+You can subscribe to it through the web interface:
+
+  https://mail.gnome.org/mailman/listinfo/python-hackers-list/
+
+
+Requirements
+============
+
+  * C compiler (GCC and MSVC supported)
+  * Python 2.7 or higher
+  * Glib/Gio 2.38.0 or higher
+  * gobject-introspection 1.46.0 or higher
+  * libffi (optional)
+
+
+Copyright Information
+=====================
+
+This software is covered by the GNU Lesser General Public Licence
+(version 2.1, or if you choose, a later version).  Basically just don't
+say you wrote bits you didn't.
+
+
+Compilation
+===========
+
+PyGObject uses the standard autotools for the build infrastructure.  To
+build, it should be as simple as running::
+
+    $ ./configure --prefix=<prefix where python is installed>
+    $ make
+    $ make install
+
+By default, configure searches for a few well-known Python interpreter
+names, such as "python3", "python2", "python2.7", or "python".  If your
+Python interpreter isn't in the path, or is not called "python", you can
+configure pygobject to build against that with --with-python=<path> or
+setting the PYTHON environment variable::
+
+    $ ./configure --with-python=python3
+    $ PYTHON=python3.2 ./configure
+    $ ./configure --with-python=~/my-patched-python/python
+
+If configure can't find GTK+, you may need to set the PKG_CONFIG_PATH
+environment variable to help it find the libraries.
+
+The "make install" target will generate normal and optimised bytecode
+for all the .py files.
+
+Note. If you're installing to another prefix than the one where python
+is installed you'll need to set the PYTHONPATH variable to the
+$prefix/lib/pythonX.Y/site-packages directory created by
+the PyGObject installation.
+
+
+Tests
+=====
+
+After having compiled and installed pygobject, you may want to test them.
+There are a number of example programs available in the examples/
+subdirectory.
+
+
+Getting Help
+============
+
+If you have questions about programming with PyGObject, you might want to
+check the documentation on
+
+    https://live.gnome.org/PyGObject/
+
+If that does not help, send a message to the mailing list (information on
+subscribing is above), or join #python on irc.gnome.org.
+
+
+Authors
+=======
+
+Original authors:
+    * James Henstridge <james@daa.com.au>
+    * Johan Dahlin <johan@gnome.org>
+
+Current maintainers:
+    * Tomeu Vizoso <tomeu.vizoso@collabora.co.uk>
+    * Martin Pitt <martinpitt@gnome.org>
+    * Paolo Borelli <pborelli@gnome.org>
+    * Ignacio Casal Quinteiro <icq@gnome.org>
+    * Sebastian Pölsterl <sebp@k-d-w.org>
+    * Simon Feltman <sfeltman@gnome.org>
+    * Christoph Reiter <reiter.christoph@gmail.com>
+
+See the NEWS file and the git history for a list of all contributors.
index 752e07a0ce138241ab71a38a84f9f47beb30ac29..50ea09e01f24f3f2075ff251fb6c92be213c7a6a 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for pygobject 3.26.1.
+# Generated by GNU Autoconf 2.69 for pygobject 3.27.0.
 #
 # Report bugs to <http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject>.
 #
@@ -591,8 +591,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='pygobject'
 PACKAGE_TARNAME='pygobject'
-PACKAGE_VERSION='3.26.1'
-PACKAGE_STRING='pygobject 3.26.1'
+PACKAGE_VERSION='3.27.0'
+PACKAGE_STRING='pygobject 3.27.0'
 PACKAGE_BUGREPORT='http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject'
 PACKAGE_URL='https://wiki.gnome.org/Projects/PyGObject/'
 
@@ -1419,7 +1419,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures pygobject 3.26.1 to adapt to many kinds of systems.
+\`configure' configures pygobject 3.27.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1490,7 +1490,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of pygobject 3.26.1:";;
+     short | recursive ) echo "Configuration of pygobject 3.27.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1635,7 +1635,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-pygobject configure 3.26.1
+pygobject configure 3.27.0
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1913,7 +1913,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by pygobject $as_me 3.26.1, which was
+It was created by pygobject $as_me 3.27.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2265,6 +2265,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
+
+
+
         # $is_release = ($minor_version is even)
         minor_version=`echo "$PACKAGE_VERSION" | sed 's/[^.][^.]*.\([^.][^.]*\).*/\1/'`
         if test "$(( $minor_version % 2 ))" -ne 0; then :
@@ -2283,14 +2286,14 @@ $as_echo "#define PYGOBJECT_MAJOR_VERSION 3" >>confdefs.h
 PYGOBJECT_MAJOR_VERSION=3
 
 
-$as_echo "#define PYGOBJECT_MINOR_VERSION 26" >>confdefs.h
+$as_echo "#define PYGOBJECT_MINOR_VERSION 27" >>confdefs.h
 
-PYGOBJECT_MINOR_VERSION=26
+PYGOBJECT_MINOR_VERSION=27
 
 
-$as_echo "#define PYGOBJECT_MICRO_VERSION 1" >>confdefs.h
+$as_echo "#define PYGOBJECT_MICRO_VERSION 0" >>confdefs.h
 
-PYGOBJECT_MICRO_VERSION=1
+PYGOBJECT_MICRO_VERSION=0
 
 
 ac_config_headers="$ac_config_headers config.h"
@@ -2810,7 +2813,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='pygobject'
- VERSION='3.26.1'
+ VERSION='3.27.0'
 
 
 cat >>confdefs.h <<_ACEOF
 
 # if building for python 3 make sure we have the minimum version supported
 if test $build_py3k = true ; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $PYTHON >= 3.3" >&5
-$as_echo_n "checking for $PYTHON >= 3.3... " >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $PYTHON >= 3.4" >&5
+$as_echo_n "checking for $PYTHON >= 3.4... " >&6; }
   prog="import sys
 # split strings by '.' and convert to numeric.  Append some zeros
 # because we need at least 4 digits for the hex conversion.
 # map returns an iterator in Python 3.0 and a list in 2.x
-minver = list(map(int, '3.3'.split('.'))) + [0, 0, 0]
+minver = list(map(int, '3.4'.split('.'))) + [0, 0, 0]
 minverhex = 0
 # xrange is not present in Python 3.0 and range returns an iterator
 for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i]
@@ -13892,7 +13895,7 @@ else
 #include <stdlib.h>
 
 int
-main (void)
+main ()
 {
   unsigned int major, minor, micro;
 
@@ -15008,7 +15011,7 @@ done
 
 
 
-for flag in              -Wall             -Wextra             -Wundef             -Wnested-externs             -Wwrite-strings             -Wpointer-arith             -Wmissing-declarations             -Wmissing-prototypes             -Wstrict-prototypes             -Wredundant-decls             -Wno-unused-parameter             -Wno-missing-field-initializers             -Wdeclaration-after-statement             -Wformat=2             -Wold-style-definition             -Wcast-align             -Wformat-nonliteral             -Wformat-security             -Wsign-compare             -Wstrict-aliasing             -Wshadow             -Winline             -Wpacked             -Wmissing-format-attribute             -Wmissing-noreturn             -Winit-self             -Wredundant-decls             -Wmissing-include-dirs             -Wunused-but-set-variable             -Warray-bounds             -Wimplicit-function-declaration             -Wreturn-type             -Wswitch-enum             -Wswitch-default             -Wduplicated-cond             -Wduplicated-branches             -Wlogical-op             -Wrestrict             -Wnull-dereference             -Wjump-misses-init             -Wdouble-promotion                                                                ; do
+for flag in              -Wall             -Wextra             -Wundef             -Wnested-externs             -Wwrite-strings             -Wpointer-arith             -Wmissing-declarations             -Wmissing-prototypes             -Wstrict-prototypes             -Wredundant-decls             -Wno-unused-parameter             -Wno-missing-field-initializers             -Wdeclaration-after-statement             -Wformat=2             -Wold-style-definition             -Wcast-align             -Wformat-nonliteral             -Wformat-security             -Wsign-compare             -Wstrict-aliasing             -Wshadow             -Winline             -Wpacked             -Wmissing-format-attribute             -Wmissing-noreturn             -Winit-self             -Wredundant-decls             -Wmissing-include-dirs             -Wunused-but-set-variable             -Warray-bounds             -Wimplicit-function-declaration             -Wreturn-type             -Wswitch-enum             -Wswitch-default                                                                ; do
   as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ax_compiler_flags_test_$flag" | $as_tr_sh`
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts $flag" >&5
 $as_echo_n "checking whether C compiler accepts $flag... " >&6; }
@@ -16330,12 +16333,9 @@ CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
 CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\
 $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
 --rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
-CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
+CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULTS)
 CODE_COVERAGE_IGNORE_PATTERN ?=
 
-GITIGNOREFILES ?=
-GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
-
 code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V))
 code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY))
 code_coverage_v_lcov_cap_0 = @echo "  LCOV   --capture"\
@@ -16365,6 +16365,9 @@ code-coverage-capture-hook:
 
 '"$CODE_COVERAGE_RULES_CLEAN"'
 
+GITIGNOREFILES ?=
+GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
+
 A''M_DISTCHECK_CONFIGURE_FLAGS ?=
 A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
 
@@ -16375,7 +16378,7 @@ A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
 
 
 
-ac_config_files="$ac_config_files Makefile pygobject-3.0.pc pygobject-3.0-uninstalled.pc gi/Makefile gi/repository/Makefile gi/overrides/Makefile examples/Makefile tests/Makefile pygtkcompat/Makefile PKG-INFO"
+ac_config_files="$ac_config_files Makefile pygobject-3.0.pc gi/Makefile gi/repository/Makefile gi/overrides/Makefile examples/Makefile tests/Makefile pygtkcompat/Makefile PKG-INFO"
 
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
@@ -16931,7 +16934,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by pygobject $as_me 3.26.1, which was
+This file was extended by pygobject $as_me 3.27.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -16998,7 +17001,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-pygobject config.status 3.26.1
+pygobject config.status 3.27.0
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -17417,7 +17420,6 @@ do
     "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
     "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
     "pygobject-3.0.pc") CONFIG_FILES="$CONFIG_FILES pygobject-3.0.pc" ;;
-    "pygobject-3.0-uninstalled.pc") CONFIG_FILES="$CONFIG_FILES pygobject-3.0-uninstalled.pc" ;;
     "gi/Makefile") CONFIG_FILES="$CONFIG_FILES gi/Makefile" ;;
     "gi/repository/Makefile") CONFIG_FILES="$CONFIG_FILES gi/repository/Makefile" ;;
     "gi/overrides/Makefile") CONFIG_FILES="$CONFIG_FILES gi/overrides/Makefile" ;;
index 3ad8da115e6c4cb78ee5079f2f6ec19919e8427a..cdecad297258f25fdb34bac52d35cb549087a796 100644 (file)
@@ -13,12 +13,12 @@ AC_PREREQ([2.68])
 #   $ ./configure --with-python=~/my-patched-python/python
 
 m4_define(python_min_ver, 2.7)
-m4_define(python3_min_ver, 3.3)
+m4_define(python3_min_ver, 3.4)
 
 dnl the pygobject version number
 m4_define(pygobject_major_version, 3)
-m4_define(pygobject_minor_version, 26)
-m4_define(pygobject_micro_version, 1)
+m4_define(pygobject_minor_version, 27)
+m4_define(pygobject_micro_version, 0)
 m4_define(pygobject_version, pygobject_major_version.pygobject_minor_version.pygobject_micro_version)
 
 dnl versions of packages we require ...
@@ -26,10 +26,14 @@ m4_define(introspection_required_version, 1.46.0)
 m4_define(pycairo_required_version, 1.11.1)
 m4_define(glib_required_version, 2.38.0)
 m4_define(gio_required_version, 2.38.0)
+m4_define(libffi_required_version, 3.0)
 
 AC_INIT([pygobject],[pygobject_version],
        [http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject],
        [pygobject],[https://wiki.gnome.org/Projects/PyGObject/])
+
+m4_ifndef([AX_IS_RELEASE], [AC_MSG_ERROR(['autoconf-archive' missing])])
+
 AX_IS_RELEASE([minor-version])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_SRCDIR([gi/gimodule.c])
@@ -131,7 +135,7 @@ PYTHON_VALGRIND_SUPP=`$PYTHON -c "import sys; sys.stdout.write('python' + sys.ve
 AC_SUBST([PYTHON_VALGRIND_SUPP])
 
 dnl libffi
-PKG_CHECK_MODULES(FFI, libffi >= 3.0)
+PKG_CHECK_MODULES(FFI, libffi >= libffi_required_version)
 LIBFFI_PC=libffi
 
 AC_SUBST(FFI_CFLAGS)
@@ -194,7 +198,6 @@ AX_CODE_COVERAGE()
 AC_CONFIG_FILES(
   Makefile
   pygobject-3.0.pc
-  pygobject-3.0-uninstalled.pc
   gi/Makefile
   gi/repository/Makefile
   gi/overrides/Makefile
index af9f3d74b9b242c35b51c19dfc7c3a567ebffd2a..c023cc01d3ddd9ffcc74d7f49e4570d3fea5af8d 100644 (file)
@@ -1 +1,6 @@
-EXTRA_DIST = properties.py signal.py option.py cairo-demo.py
+EXTRA_DIST = \
+       properties.py \
+       signal.py \
+       option.py \
+       cairo-demo.py \
+       demo
index 7b2261376995942b3f7958a404765048644bbc7a..54843beecb01f2f6c5ba447a65a822f34379dee2 100644 (file)
@@ -311,7 +311,13 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-EXTRA_DIST = properties.py signal.py option.py cairo-demo.py
+EXTRA_DIST = \
+       properties.py \
+       signal.py \
+       option.py \
+       cairo-demo.py \
+       demo
+
 all: all-am
 
 .SUFFIXES:
diff --git a/examples/demo/demo.py b/examples/demo/demo.py
new file mode 100755 (executable)
index 0000000..d67935d
--- /dev/null
@@ -0,0 +1,356 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+
+import codecs
+import os
+import sys
+import textwrap
+
+from gi.repository import GLib, GObject, Pango, GdkPixbuf, Gtk, Gio
+
+try:
+    from gi.repository import GtkSource
+    GtkSource  # PyFlakes
+except ImportError:
+    GtkSource = None
+
+
+DEMOROOTDIR = os.path.abspath(os.path.dirname(__file__))
+DEMOCODEDIR = os.path.join(DEMOROOTDIR, 'demos')
+sys.path.insert(0, DEMOROOTDIR)
+
+
+class Demo(GObject.GObject):
+    __gtype_name__ = 'GtkDemo'
+
+    def __init__(self, title, module, filename):
+        super(Demo, self).__init__()
+
+        self.title = title
+        self.module = module
+        self.filename = filename
+
+    @classmethod
+    def new_from_file(cls, path):
+        relpath = os.path.relpath(path, DEMOROOTDIR)
+        packagename = os.path.dirname(relpath).replace(os.sep, '.')
+        modulename = os.path.basename(relpath)[0:-3]
+
+        package = __import__(packagename, globals(), locals(), [modulename], 0)
+        module = getattr(package, modulename)
+
+        try:
+            return cls(module.title, module, path)
+        except AttributeError as e:
+            raise AttributeError('(%s): %s' % (path, e.message))
+
+
+class DemoTreeStore(Gtk.TreeStore):
+    __gtype_name__ = 'GtkDemoTreeStore'
+
+    def __init__(self, *args):
+        super(DemoTreeStore, self).__init__(str, Demo, Pango.Style)
+
+        self._parent_nodes = {}
+
+        for filename in self._list_dir(DEMOCODEDIR):
+            fullpath = os.path.join(DEMOCODEDIR, filename)
+            initfile = os.path.join(os.path.dirname(fullpath), '__init__.py')
+
+            if fullpath != initfile and os.path.isfile(initfile) and fullpath.endswith('.py'):
+                parentname = os.path.dirname(os.path.relpath(fullpath, DEMOCODEDIR))
+
+                if parentname:
+                    parent = self._get_parent_node(parentname)
+                else:
+                    parent = None
+
+                demo = Demo.new_from_file(fullpath)
+                self.append(parent, (demo.title, demo, Pango.Style.NORMAL))
+
+    def _list_dir(self, path):
+        demo_file_list = []
+
+        for filename in os.listdir(path):
+            fullpath = os.path.join(path, filename)
+
+            if os.path.isdir(fullpath):
+                demo_file_list.extend(self._list_dir(fullpath))
+            elif os.path.isfile(fullpath):
+                demo_file_list.append(fullpath)
+
+        return sorted(demo_file_list, key=str.lower)
+
+    def _get_parent_node(self, name):
+        if name not in self._parent_nodes.keys():
+            node = self.append(None, (name, None, Pango.Style.NORMAL))
+            self._parent_nodes[name] = node
+
+        return self._parent_nodes[name]
+
+
+class GtkDemoApp(Gtk.Application):
+    __gtype_name__ = 'GtkDemoWindow'
+
+    def __init__(self):
+        super(GtkDemoApp, self).__init__(application_id='org.gnome.pygobject.gtkdemo')
+
+        # Use a GResource to hold the CSS files. Resource bundles are created by
+        # the glib-compile-resources program shipped with Glib which takes an xml
+        # file that describes the bundle, and a set of files that the xml
+        # references. These are combined into a binary resource bundle.
+        base_path = os.path.abspath(os.path.dirname(__file__))
+        resource_path = os.path.join(base_path, 'demos/data/demo.gresource')
+        resource = Gio.Resource.load(resource_path)
+
+        # FIXME: method register() should be without the underscore
+        # FIXME: see https://bugzilla.gnome.org/show_bug.cgi?id=684319
+        # Once the resource has been globally registered it can be used
+        # throughout the application.
+        resource._register()
+
+    def on_activate(self, app):
+        self.window = Gtk.ApplicationWindow.new(self)
+        self.window.set_title('PyGObject GTK+ Code Demos')
+        self.window.set_default_size(600, 400)
+        self.setup_default_icon()
+
+        self.header_bar = Gtk.HeaderBar(show_close_button=True,
+                                        subtitle='Foobar')
+        self.window.set_titlebar(self.header_bar)
+
+        stack = Gtk.Stack(transition_type=Gtk.StackTransitionType.SLIDE_LEFT_RIGHT,
+                          homogeneous=True)
+        switcher = Gtk.StackSwitcher(stack=stack, halign=Gtk.Align.CENTER)
+
+        self.header_bar.set_custom_title(switcher)
+
+        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
+                       homogeneous=False,
+                       spacing=0)
+        self.window.add(hbox)
+
+        tree = self.create_tree()
+        hbox.pack_start(child=tree, expand=False, fill=False, padding=0)
+        hbox.pack_start(child=stack, expand=True, fill=True, padding=0)
+
+        text_widget, info_buffer = self.create_text_view()
+        stack.add_titled(text_widget, name='info', title='Info')
+
+        self.info_buffer = info_buffer
+        self.info_buffer.create_tag('title', font='Sans 18')
+
+        text_widget, self.source_buffer = self.create_source_view()
+        stack.add_titled(text_widget, name='source', title='Source')
+
+        self.window.show_all()
+
+        self.selection_cb(self.tree_view.get_selection(),
+                          self.tree_view.get_model())
+
+    def find_file(self, base=''):
+        dir = os.path.join(DEMOCODEDIR, 'data')
+        logo_file = os.path.join(dir, 'gtk-logo-rgb.gif')
+        base_file = os.path.join(dir, base)
+
+        if (GLib.file_test(logo_file, GLib.FileTest.EXISTS) and
+                GLib.file_test(base_file, GLib.FileTest.EXISTS)):
+            return base_file
+        else:
+            filename = os.path.join(DEMOCODEDIR, base)
+
+            if GLib.file_test(filename, GLib.FileTest.EXISTS):
+                return filename
+
+            # can't find the file
+            raise IOError('Cannot find demo data file "%s"' % base)
+
+    def setup_default_icon(self):
+        filename = self.find_file('gtk-logo-rgb.gif')
+        pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+        transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
+        list = []
+        list.append(transparent)
+        Gtk.Window.set_default_icon_list(list)
+
+    def selection_cb(self, selection, model):
+        sel = selection.get_selected()
+        if sel == ():
+            return
+
+        treeiter = sel[1]
+        title = model.get_value(treeiter, 0)
+        demo = model.get_value(treeiter, 1)
+
+        if demo is None:
+            return
+
+        # Split into paragraphs based on double newlines and use
+        # textwrap to strip out all other formatting whitespace
+        description = ''
+        for paragraph in demo.module.description.split('\n\n'):
+            description += '\n'.join(textwrap.wrap(paragraph, 99999))
+            description += '\n\n'  # Add paragraphs back in
+
+        f = codecs.open(demo.filename, 'rU', 'utf-8')
+        code = f.read()
+        f.close()
+
+        # output and style the title
+        (start, end) = self.info_buffer.get_bounds()
+        self.info_buffer.delete(start, end)
+        (start, end) = self.source_buffer.get_bounds()
+        self.source_buffer.delete(start, end)
+
+        start = self.info_buffer.get_iter_at_offset(0)
+        end = start.copy()
+        self.info_buffer.insert(end, title)
+        start = end.copy()
+        start.backward_chars(len(title))
+        self.info_buffer.apply_tag_by_name('title', start, end)
+        self.info_buffer.insert(end, '\n')
+
+        # output the description
+        self.info_buffer.insert(end, description)
+
+        # output the code
+        start = self.source_buffer.get_iter_at_offset(0)
+        end = start.copy()
+        self.source_buffer.insert(end, code)
+
+    def row_activated_cb(self, view, path, col, store):
+        iter = store.get_iter(path)
+        demo = store.get_value(iter, 1)
+
+        if demo is not None:
+            store.set_value(iter, 2, Pango.Style.ITALIC)
+            try:
+                demo.module.main(self)
+            finally:
+                store.set_value(iter, 2, Pango.Style.NORMAL)
+
+    def create_tree(self):
+        tree_store = DemoTreeStore()
+        tree_view = Gtk.TreeView()
+        self.tree_view = tree_view
+        tree_view.set_model(tree_store)
+        selection = tree_view.get_selection()
+        selection.set_mode(Gtk.SelectionMode.BROWSE)
+        tree_view.set_size_request(200, -1)
+
+        cell = Gtk.CellRendererText()
+        column = Gtk.TreeViewColumn(title='Widget (double click for demo)',
+                                    cell_renderer=cell,
+                                    text=0,
+                                    style=2)
+
+        first_iter = tree_store.get_iter_first()
+        if first_iter is not None:
+            selection.select_iter(first_iter)
+
+        selection.connect('changed', self.selection_cb, tree_store)
+        tree_view.connect('row_activated', self.row_activated_cb, tree_store)
+
+        tree_view.append_column(column)
+
+        tree_view.expand_all()
+        tree_view.set_headers_visible(False)
+        scrolled_window = Gtk.ScrolledWindow(hadjustment=None,
+                                             vadjustment=None)
+        scrolled_window.set_policy(Gtk.PolicyType.NEVER,
+                                   Gtk.PolicyType.AUTOMATIC)
+
+        scrolled_window.add(tree_view)
+
+        label = Gtk.Label(label='Widget (double click for demo)')
+
+        box = Gtk.Notebook()
+        box.append_page(scrolled_window, label)
+
+        tree_view.grab_focus()
+
+        return box
+
+    def create_scrolled_window(self):
+        scrolled_window = Gtk.ScrolledWindow(hadjustment=None,
+                                             vadjustment=None)
+        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
+                                   Gtk.PolicyType.AUTOMATIC)
+        scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
+        return scrolled_window
+
+    def create_text_view(self):
+        text_view = Gtk.TextView()
+        buffer = Gtk.TextBuffer()
+
+        text_view.set_buffer(buffer)
+        text_view.set_editable(False)
+        text_view.set_cursor_visible(False)
+
+        scrolled_window = self.create_scrolled_window()
+        scrolled_window.add(text_view)
+
+        text_view.set_wrap_mode(Gtk.WrapMode.WORD)
+        text_view.set_pixels_above_lines(2)
+        text_view.set_pixels_below_lines(2)
+
+        return scrolled_window, buffer
+
+    def create_source_view(self):
+        font_desc = Pango.FontDescription('monospace 11')
+
+        if GtkSource:
+            lang_mgr = GtkSource.LanguageManager()
+            lang = lang_mgr.get_language('python')
+
+            buffer = GtkSource.Buffer()
+            buffer.set_language(lang)
+            buffer.set_highlight_syntax(True)
+
+            view = GtkSource.View()
+            view.set_buffer(buffer)
+            view.set_show_line_numbers(True)
+
+            scrolled_window = self.create_scrolled_window()
+            scrolled_window.add(view)
+
+        else:
+            scrolled_window, buffer = self.create_text_view()
+            view = scrolled_window.get_child()
+
+        view.modify_font(font_desc)
+        view.set_wrap_mode(Gtk.WrapMode.NONE)
+        return scrolled_window, buffer
+
+    def run(self, argv):
+        self.connect('activate', self.on_activate)
+        return super(GtkDemoApp, self).run(argv)
+
+
+def main(argv):
+    """Entry point for demo manager"""
+    app = GtkDemoApp()
+    return app.run(argv)
+
+
+if __name__ == '__main__':
+    SystemExit(main(sys.argv))
diff --git a/examples/demo/demos/Css/__init__.py b/examples/demo/demos/Css/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/demo/demos/Css/css_accordion.py b/examples/demo/demos/Css/css_accordion.py
new file mode 100644 (file)
index 0000000..2b7cddc
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2013 Gian Mario Tagliaretti <gianmt@gnome.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "CSS Accordion"
+description = """
+A simple accordion demo written using CSS transitions and multiple backgrounds.
+"""
+
+
+from gi.repository import Gtk, Gio
+
+
+class CSSAccordionApp:
+    def __init__(self):
+        window = Gtk.Window()
+        window.set_title('CSS Accordion')
+        window.set_default_size(600, 300)
+        window.set_border_width(10)
+        window.connect('destroy', Gtk.main_quit)
+
+        hbox = Gtk.Box(homogeneous=False, spacing=2,
+                       orientation=Gtk.Orientation.HORIZONTAL)
+        hbox.set_halign(Gtk.Align.CENTER)
+        hbox.set_valign(Gtk.Align.CENTER)
+        window.add(hbox)
+
+        for label in ('This', 'Is', 'A', 'CSS', 'Accordion', ':-)'):
+            hbox.add(Gtk.Button(label=label))
+
+        bytes = Gio.resources_lookup_data("/css_accordion/css_accordion.css", 0)
+
+        provider = Gtk.CssProvider()
+        provider.load_from_data(bytes.get_data())
+
+        self.apply_css(window, provider)
+
+        window.show_all()
+
+    def apply_css(self, widget, provider):
+        Gtk.StyleContext.add_provider(widget.get_style_context(),
+                                      provider,
+                                      Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+
+        if isinstance(widget, Gtk.Container):
+            widget.forall(self.apply_css, provider)
+
+
+def main(demoapp=None):
+    CSSAccordionApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    import os
+    base_path = os.path.abspath(os.path.dirname(__file__))
+    resource_path = os.path.join(base_path, '../data/demo.gresource')
+    resource = Gio.Resource.load(resource_path)
+
+    # FIXME: method register() should be without the underscore
+    # FIXME: see https://bugzilla.gnome.org/show_bug.cgi?id=684319
+    resource._register()
+    main()
diff --git a/examples/demo/demos/Css/css_basics.py b/examples/demo/demos/Css/css_basics.py
new file mode 100644 (file)
index 0000000..18c3d12
--- /dev/null
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2013 Gian Mario Tagliaretti <gianmt@gnome.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "CSS Basics"
+description = """
+Gtk themes are written using CSS. Every widget is build of multiple items
+that you can style very similarly to a regular website.
+"""
+
+import os
+from gi.repository import Gtk, Gdk, Pango, Gio, GLib
+
+
+class CSSBasicsApp:
+    def __init__(self, demoapp):
+        self.demoapp = demoapp
+        #: Store the last successful parsing of the css so we can revert
+        #: this in case of an error.
+        self.last_good_text = ''
+        #: Set when we receive a parsing-error callback. This is needed
+        #: to handle logic after a parsing-error callback which does not raise
+        #: an exception with provider.load_from_data()
+        self.last_error_code = 0
+
+        self.window = Gtk.Window()
+        self.window.set_title('CSS Basics')
+        self.window.set_default_size(400, 300)
+        self.window.set_border_width(10)
+        self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+        self.infobar = Gtk.InfoBar()
+        self.infolabel = Gtk.Label()
+        self.infobar.get_content_area().pack_start(self.infolabel, False, False, 0)
+        self.infobar.set_message_type(Gtk.MessageType.WARNING)
+
+        scrolled = Gtk.ScrolledWindow()
+        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+        box.pack_start(scrolled, expand=True, fill=True, padding=0)
+        box.pack_start(self.infobar, expand=False, fill=True, padding=0)
+        self.window.add(box)
+
+        provider = Gtk.CssProvider()
+
+        buffer = Gtk.TextBuffer()
+        buffer.create_tag(tag_name="warning", underline=Pango.Underline.SINGLE)
+        buffer.create_tag(tag_name="error", underline=Pango.Underline.ERROR)
+        buffer.connect("changed", self.css_text_changed, provider)
+
+        provider.connect("parsing-error", self.show_parsing_error, buffer)
+
+        textview = Gtk.TextView()
+        textview.set_buffer(buffer)
+        scrolled.add(textview)
+
+        bytes = Gio.resources_lookup_data("/css_basics/css_basics.css", 0)
+        buffer.set_text(bytes.get_data().decode('utf-8'))
+
+        self.apply_css(self.window, provider)
+        self.window.show_all()
+        self.infobar.hide()
+
+    def apply_css(self, widget, provider):
+        Gtk.StyleContext.add_provider(widget.get_style_context(),
+                                      provider,
+                                      Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+
+        if isinstance(widget, Gtk.Container):
+            widget.forall(self.apply_css, provider)
+
+    def show_parsing_error(self, provider, section, error, buffer):
+        start = buffer.get_iter_at_line_index(section.get_start_line(),
+                                              section.get_start_position())
+
+        end = buffer.get_iter_at_line_index(section.get_end_line(),
+                                            section.get_end_position())
+
+        if error.code == Gtk.CssProviderError.DEPRECATED:
+            tag_name = "warning"
+        else:
+            tag_name = "error"
+        self.last_error_code = error.code
+
+        self.infolabel.set_text(error.message)
+        self.infobar.show_all()
+
+        buffer.apply_tag_by_name(tag_name, start, end)
+
+    def css_text_changed(self, buffer, provider):
+        start = buffer.get_start_iter()
+        end = buffer.get_end_iter()
+        buffer.remove_all_tags(start, end)
+
+        text = buffer.get_text(start, end, False).encode('utf-8')
+
+        # Ignore CSS errors as they are shown by highlighting
+        try:
+            provider.load_from_data(text)
+        except GLib.GError as e:
+            if e.domain != 'gtk-css-provider-error-quark':
+                raise e
+
+        # If the parsing-error callback is ever run (even in the case of warnings)
+        # load the last good css text that ran without any warnings. Otherwise
+        # we may have a discrepancy in "last_good_text" vs the current buffer
+        # causing section.get_start_position() to give back an invalid position
+        # for the editor buffer.
+        if self.last_error_code:
+            provider.load_from_data(self.last_good_text)
+            self.last_error_code = 0
+        else:
+            self.last_good_text = text
+            self.infobar.hide()
+
+        Gtk.StyleContext.reset_widgets(Gdk.Screen.get_default())
+
+
+def main(demoapp=None):
+    CSSBasicsApp(demoapp)
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    base_path = os.path.abspath(os.path.dirname(__file__))
+    resource_path = os.path.join(base_path, '../data/demo.gresource')
+    resource = Gio.Resource.load(resource_path)
+
+    # FIXME: method register() should be without the underscore
+    # FIXME: see https://bugzilla.gnome.org/show_bug.cgi?id=684319
+    resource._register()
+    main()
diff --git a/examples/demo/demos/Css/css_multiplebgs.py b/examples/demo/demos/Css/css_multiplebgs.py
new file mode 100644 (file)
index 0000000..9e1b011
--- /dev/null
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2013 Gian Mario Tagliaretti <gianmt@gnome.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "CSS Theming/Multiple Backgrounds"
+description = """
+Gtk themes are written using CSS. Every widget is build of multiple items
+that you can style very similarly to a regular website.
+"""
+
+from gi.repository import Gtk, Gdk, Pango, Gio, GLib
+
+
+class CSSMultiplebgsApp:
+    def __init__(self, demoapp):
+        self.demoapp = demoapp
+        #: Store the last successful parsing of the css so we can revert
+        #: this in case of an error.
+        self.last_good_text = ''
+        #: Set when we receive a parsing-error callback. This is needed
+        #: to handle logic after a parsing-error callback which does not raise
+        #: an exception with provider.load_from_data()
+        self.last_error_code = 0
+
+        self.window = Gtk.Window()
+        self.window.set_title('CSS Multiplebgs')
+        self.window.set_default_size(400, 300)
+        self.window.set_border_width(10)
+        self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+        overlay = Gtk.Overlay()
+        overlay.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK |
+                           Gdk.EventMask.LEAVE_NOTIFY_MASK |
+                           Gdk.EventMask.POINTER_MOTION_MASK)
+
+        self.infobar = Gtk.InfoBar()
+        self.infolabel = Gtk.Label()
+        self.infobar.get_content_area().pack_start(self.infolabel, False, False, 0)
+        self.infobar.set_message_type(Gtk.MessageType.WARNING)
+
+        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+        box.pack_start(overlay, expand=True, fill=True, padding=0)
+        box.pack_start(self.infobar, expand=False, fill=True, padding=0)
+        self.window.add(box)
+
+        canvas = Gtk.DrawingArea()
+        canvas.set_name("canvas")
+        canvas.connect("draw", self.drawing_area_draw)
+        overlay.add(canvas)
+
+        button = Gtk.Button()
+        button.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK |
+                          Gdk.EventMask.LEAVE_NOTIFY_MASK |
+                          Gdk.EventMask.POINTER_MOTION_MASK)
+        button.set_name("bricks-button")
+        button.set_halign(Gtk.Align.CENTER)
+        button.set_valign(Gtk.Align.CENTER)
+        button.set_size_request(250, 84)
+        overlay.add_overlay(button)
+
+        paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL)
+        overlay.add_overlay(paned)
+
+        # We need a filler so we get a handle
+        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+        paned.add(box)
+
+        buffer = Gtk.TextBuffer()
+        buffer.create_tag(tag_name="warning", underline=Pango.Underline.SINGLE)
+        buffer.create_tag(tag_name="error", underline=Pango.Underline.ERROR)
+
+        provider = Gtk.CssProvider()
+
+        buffer.connect("changed", self.css_text_changed, provider)
+        provider.connect("parsing-error", self.show_parsing_error, buffer)
+
+        textview = Gtk.TextView()
+        textview.set_buffer(buffer)
+
+        scrolled = Gtk.ScrolledWindow()
+        scrolled.add(textview)
+        paned.add(scrolled)
+
+        bytes = Gio.resources_lookup_data("/css_multiplebgs/css_multiplebgs.css", 0)
+        buffer.set_text(bytes.get_data().decode('utf-8'))
+
+        self.apply_css(self.window, provider)
+        self.window.show_all()
+        self.infobar.hide()
+
+    def drawing_area_draw(self, widget, cairo_t):
+        context = widget.get_style_context()
+        Gtk.render_background(context, cairo_t, 0, 0,
+                              widget.get_allocated_width(),
+                              widget.get_allocated_height())
+
+        Gtk.render_frame(context, cairo_t, 0, 0,
+                         widget.get_allocated_width(),
+                         widget.get_allocated_height())
+
+    def apply_css(self, widget, provider):
+        Gtk.StyleContext.add_provider(widget.get_style_context(),
+                                      provider,
+                                      Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+
+        if isinstance(widget, Gtk.Container):
+            widget.forall(self.apply_css, provider)
+
+    def show_parsing_error(self, provider, section, error, buffer):
+        start = buffer.get_iter_at_line_index(section.get_start_line(),
+                                              section.get_start_position())
+
+        end = buffer.get_iter_at_line_index(section.get_end_line(),
+                                            section.get_end_position())
+
+        if error.code == Gtk.CssProviderError.DEPRECATED:
+            tag_name = "warning"
+        else:
+            tag_name = "error"
+        self.last_error_code = error.code
+
+        self.infolabel.set_text(error.message)
+        self.infobar.show_all()
+
+        buffer.apply_tag_by_name(tag_name, start, end)
+
+    def css_text_changed(self, buffer, provider):
+        start = buffer.get_start_iter()
+        end = buffer.get_end_iter()
+        buffer.remove_all_tags(start, end)
+
+        text = buffer.get_text(start, end, False).encode('utf-8')
+
+        # Ignore CSS errors as they are shown by highlighting
+        try:
+            provider.load_from_data(text)
+        except GLib.GError as e:
+            if e.domain != 'gtk-css-provider-error-quark':
+                raise e
+
+        # If the parsing-error callback is ever run (even in the case of warnings)
+        # load the last good css text that ran without any warnings. Otherwise
+        # we may have a discrepancy in "last_good_text" vs the current buffer
+        # causing section.get_start_position() to give back an invalid position
+        # for the editor buffer.
+        if self.last_error_code:
+            provider.load_from_data(self.last_good_text)
+            self.last_error_code = 0
+        else:
+            self.last_good_text = text
+            self.infobar.hide()
+
+        Gtk.StyleContext.reset_widgets(Gdk.Screen.get_default())
+
+
+def main(demoapp=None):
+    CSSMultiplebgsApp(demoapp)
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    import os
+    base_path = os.path.abspath(os.path.dirname(__file__))
+    resource_path = os.path.join(base_path, '../data/demo.gresource')
+    resource = Gio.Resource.load(resource_path)
+
+    # FIXME: method register() should be without the underscore
+    # FIXME: see https://bugzilla.gnome.org/show_bug.cgi?id=684319
+    resource._register()
+    main()
diff --git a/examples/demo/demos/Entry/__init__.py b/examples/demo/demos/Entry/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/demo/demos/Entry/entry_buffer.py b/examples/demo/demos/Entry/entry_buffer.py
new file mode 100644 (file)
index 0000000..f0c04a4
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Entry Buffer"
+description = """
+Gtk.EntryBuffer provides the text content in a Gtk.Entry.
+"""
+
+
+from gi.repository import Gtk
+
+
+class EntryBufferApp:
+    def __init__(self):
+        self.window = Gtk.Dialog(title='Gtk.EntryBuffer')
+        self.window.add_buttons(Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE)
+        self.window.connect('response', self.destroy)
+        self.window.connect('destroy', lambda x: Gtk.main_quit())
+        self.window.set_resizable(False)
+
+        vbox = Gtk.VBox(homogeneous=False, spacing=0)
+        self.window.get_content_area().pack_start(vbox, True, True, 0)
+        vbox.set_border_width(5)
+
+        label = Gtk.Label()
+        label.set_markup('Entries share a buffer. Typing in one is reflected in the other.')
+        vbox.pack_start(label, False, False, 0)
+
+        # create a buffer
+        buffer = Gtk.EntryBuffer()
+
+        # create our first entry
+        entry = Gtk.Entry(buffer=buffer)
+        vbox.pack_start(entry, False, False, 0)
+
+        # create the second entry
+        entry = Gtk.Entry(buffer=buffer)
+        entry.set_visibility(False)
+        vbox.pack_start(entry, False, False, 0)
+
+        self.window.show_all()
+
+    def destroy(self, *args):
+        self.window.destroy()
+        Gtk.main_quit()
+
+
+def main(demoapp=None):
+    EntryBufferApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/Entry/entry_completion.py b/examples/demo/demos/Entry/entry_completion.py
new file mode 100644 (file)
index 0000000..107c45a
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Entry Completion"
+description = """
+Gtk.EntryCompletion provides a mechanism for adding support for
+completion in Gtk.Entry.
+"""
+
+
+from gi.repository import Gtk
+
+
+class EntryBufferApp:
+    def __init__(self):
+        self.window = Gtk.Dialog(title='Gtk.EntryCompletion')
+        self.window.add_buttons(Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE)
+        self.window.connect('response', self.destroy)
+        self.window.connect('destroy', lambda x: Gtk.main_quit())
+        self.window.set_resizable(False)
+
+        vbox = Gtk.VBox(homogeneous=False, spacing=0)
+        self.window.get_content_area().pack_start(vbox, True, True, 0)
+        vbox.set_border_width(5)
+
+        label = Gtk.Label()
+        label.set_markup('Completion demo, try writing <b>total</b> or <b>gnome</b> for example.')
+        vbox.pack_start(label, False, False, 0)
+
+        # create our entry
+        entry = Gtk.Entry()
+        vbox.pack_start(entry, False, False, 0)
+
+        # create the completion object
+        completion = Gtk.EntryCompletion()
+
+        # assign the completion to the entry
+        entry.set_completion(completion)
+
+        # create tree model and use it as the completion model
+        completion_model = self.create_completion_model()
+        completion.set_model(completion_model)
+
+        completion.set_text_column(0)
+
+        self.window.show_all()
+
+    def create_completion_model(self):
+        store = Gtk.ListStore(str)
+
+        store.append(['GNOME'])
+        store.append(['total'])
+        store.append(['totally'])
+
+        return store
+
+    def destroy(self, *args):
+        self.window.destroy()
+        Gtk.main_quit()
+
+
+def main(demoapp=None):
+    EntryBufferApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/Entry/search_entry.py b/examples/demo/demos/Entry/search_entry.py
new file mode 100644 (file)
index 0000000..793b81a
--- /dev/null
@@ -0,0 +1,254 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Search Entry"
+description = """GtkEntry allows to display icons and progress information.
+This demo shows how to use these features in a search entry.
+"""
+
+from gi.repository import Gtk, GObject
+
+(PIXBUF_COL,
+ TEXT_COL) = range(2)
+
+
+class SearchboxApp:
+    def __init__(self, demoapp):
+        self.demoapp = demoapp
+
+        self.window = Gtk.Dialog(title='Search Entry')
+        self.window.add_buttons(Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE)
+
+        self.window.connect('response', lambda x, y: self.window.destroy())
+        self.window.connect('destroy', Gtk.main_quit)
+
+        content_area = self.window.get_content_area()
+
+        vbox = Gtk.VBox(spacing=5)
+        content_area.pack_start(vbox, True, True, 0)
+        vbox.set_border_width(5)
+
+        label = Gtk.Label()
+        label.set_markup('Search entry demo')
+        vbox.pack_start(label, False, False, 0)
+
+        hbox = Gtk.HBox(homogeneous=False, spacing=10)
+        hbox.set_border_width(0)
+        vbox.pack_start(hbox, True, True, 0)
+
+        # Create our entry
+        entry = Gtk.Entry()
+        hbox.pack_start(entry, False, False, 0)
+
+        # Create the find and cancel buttons
+        notebook = Gtk.Notebook()
+        self.notebook = notebook
+        notebook.set_show_tabs(False)
+        notebook.set_show_border(False)
+        hbox.pack_start(notebook, False, False, 0)
+
+        find_button = Gtk.Button(label='Find')
+        find_button.connect('clicked', self.start_search, entry)
+        notebook.append_page(find_button, None)
+        find_button.show()
+
+        cancel_button = Gtk.Button(label='Cancel')
+        cancel_button.connect('clicked', self.stop_search, entry)
+        notebook.append_page(cancel_button, None)
+        cancel_button.show()
+
+        # Set up the search icon
+        self.search_by_name(None, entry)
+
+        # Set up the clear icon
+        entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY,
+                                  Gtk.STOCK_CLEAR)
+        self.text_changed_cb(entry, None, find_button)
+
+        entry.connect('notify::text', self.text_changed_cb, find_button)
+
+        entry.connect('activate', self.activate_cb)
+
+        # Create the menu
+        menu = self.create_search_menu(entry)
+        entry.connect('icon-press', self.icon_press_cb, menu)
+
+        # FIXME: this should take None for the detach callback
+        #        but our callback implementation does not allow
+        #        it yet, so we pass in a noop callback
+        menu.attach_to_widget(entry, self.detach)
+
+        # add accessible alternatives for icon functionality
+        entry.connect('populate-popup', self.entry_populate_popup)
+
+        self.window.show_all()
+
+    def detach(self, *args):
+        pass
+
+    def show_find_button(self):
+        self.notebook.set_current_page(0)
+
+    def show_cancel_button(self):
+        self.notebook.set_current_page(1)
+
+    def search_progress(self, entry):
+        entry.progress_pulse()
+        return True
+
+    def search_progress_done(self, entry):
+        entry.set_progress_fraction(0.0)
+
+    def finish_search(self, button, entry):
+        self.show_find_button()
+        GObject.source_remove(self.search_progress_id)
+        self.search_progress_done(entry)
+        self.search_progress_id = 0
+
+        return False
+
+    def start_search_feedback(self, entry):
+        self.search_progress_id = GObject.timeout_add(100,
+                                                      self.search_progress,
+                                                      entry)
+
+        return False
+
+    def start_search(self, button, entry):
+        self.show_cancel_button()
+        self.search_progress_id = GObject.timeout_add_seconds(1,
+                                                              self.start_search_feedback,
+                                                              entry)
+        self.finish_search_id = GObject.timeout_add_seconds(15,
+                                                            self.finish_search,
+                                                            button)
+
+    def stop_search(self, button, entry):
+        GObject.source_remove(self.finish_search_id)
+        self.finish_search(button, entry)
+
+    def clear_entry_swapped(self, widget, entry):
+        self.clear_entry(entry)
+
+    def clear_entry(self, entry):
+        entry.set_text('')
+
+    def search_by_name(self, item, entry):
+        entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY,
+                                  Gtk.STOCK_FIND)
+        entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
+                                    'Search by name\n' +
+                                    'Click here to change the search type')
+
+    def search_by_description(self, item, entry):
+        entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY,
+                                  Gtk.STOCK_EDIT)
+        entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
+                                    'Search by description\n' +
+                                    'Click here to change the search type')
+
+    def search_by_file(self, item, entry):
+        entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY,
+                                  Gtk.STOCK_OPEN)
+        entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
+                                    'Search by file name\n' +
+                                    'Click here to change the search type')
+
+    def create_search_menu(self, entry):
+        menu = Gtk.Menu()
+
+        item = Gtk.ImageMenuItem.new_with_mnemonic('Search by _name')
+        image = Gtk.Image.new_from_stock(Gtk.STOCK_FIND, Gtk.IconSize.MENU)
+        item.set_image(image)
+        item.set_always_show_image(True)
+        item.connect('activate', self.search_by_name, entry)
+        menu.append(item)
+
+        item = Gtk.ImageMenuItem.new_with_mnemonic('Search by _description')
+        image = Gtk.Image.new_from_stock(Gtk.STOCK_EDIT, Gtk.IconSize.MENU)
+        item.set_image(image)
+        item.set_always_show_image(True)
+        item.connect('activate', self.search_by_description, entry)
+        menu.append(item)
+
+        item = Gtk.ImageMenuItem.new_with_mnemonic('Search by _file name')
+        image = Gtk.Image.new_from_stock(Gtk.STOCK_OPEN, Gtk.IconSize.MENU)
+        item.set_image(image)
+        item.set_always_show_image(True)
+        item.connect('activate', self.search_by_name, entry)
+        menu.append(item)
+
+        menu.show_all()
+
+        return menu
+
+    def icon_press_cb(self, entry, position, event, menu):
+        if position == Gtk.EntryIconPosition.PRIMARY:
+            menu.popup(None, None, None, None,
+                       event.button, event.time)
+        else:
+            self.clear_entry(entry)
+
+    def text_changed_cb(self, entry, pspec, button):
+        has_text = entry.get_text_length() > 0
+        entry.set_icon_sensitive(Gtk.EntryIconPosition.SECONDARY, has_text)
+        button.set_sensitive(has_text)
+
+    def activate_cb(self, entry, button):
+        if self.search_progress_id != 0:
+            return
+        self.start_search(button, entry)
+
+    def search_entry_destroyed(self, widget):
+        if self.finish_search_id != 0:
+            GObject.source_remove(self.finish_search_id)
+        if self.search_progress_id != 0:
+            GObject.source_remove(self.search_progress_id)
+
+        self.window = None
+
+    def entry_populate_popup(self, entry, menu):
+        has_text = entry.get_text_length() > 0
+
+        item = Gtk.SeparatorMenuItem()
+        item.show()
+        menu.append(item)
+
+        item = Gtk.MenuItem.new_with_mnemonic("C_lear")
+        item.show()
+        item.connect('activate', self.clear_entry_swapped, entry)
+        menu.append(item)
+        item.set_sensitive(has_text)
+
+        search_menu = self.create_search_menu(entry)
+        item = Gtk.MenuItem.new_with_label('Search by')
+        item.show()
+        item.set_submenu(search_menu)
+        menu.append(item)
+
+
+def main(demoapp=None):
+    SearchboxApp(demoapp)
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/IconView/__init__.py b/examples/demo/demos/IconView/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/demo/demos/IconView/iconviewbasics.py b/examples/demo/demos/IconView/iconviewbasics.py
new file mode 100644 (file)
index 0000000..8cb71a8
--- /dev/null
@@ -0,0 +1,221 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Icon View Basics"
+description = """The GtkIconView widget is used to display and manipulate
+icons. It uses a GtkTreeModel for data storage, so the list store example might
+be helpful. We also use the Gio.File API to get the icons for each file type.
+"""
+
+
+import os
+
+from gi.repository import GLib, Gio, GdkPixbuf, Gtk
+
+
+class IconViewApp:
+    (COL_PATH,
+     COL_DISPLAY_NAME,
+     COL_PIXBUF,
+     COL_IS_DIRECTORY,
+     NUM_COLS) = range(5)
+
+    def __init__(self, demoapp):
+        self.pixbuf_lookup = {}
+
+        self.demoapp = demoapp
+
+        self.window = Gtk.Window()
+        self.window.set_title('Gtk.IconView demo')
+        self.window.set_default_size(650, 400)
+        self.window.connect('destroy', Gtk.main_quit)
+
+        vbox = Gtk.VBox()
+        self.window.add(vbox)
+
+        tool_bar = Gtk.Toolbar()
+        vbox.pack_start(tool_bar, False, False, 0)
+
+        up_button = Gtk.ToolButton(stock_id=Gtk.STOCK_GO_UP)
+        up_button.set_is_important(True)
+        up_button.set_sensitive(False)
+        tool_bar.insert(up_button, -1)
+
+        home_button = Gtk.ToolButton(stock_id=Gtk.STOCK_HOME)
+        home_button.set_is_important(True)
+        tool_bar.insert(home_button, -1)
+
+        sw = Gtk.ScrolledWindow()
+        sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
+        sw.set_policy(Gtk.PolicyType.AUTOMATIC,
+                      Gtk.PolicyType.AUTOMATIC)
+
+        vbox.pack_start(sw, True, True, 0)
+
+        # create the store and fill it with content
+        self.parent_dir = '/'
+        store = self.create_store()
+        self.fill_store(store)
+
+        icon_view = Gtk.IconView(model=store)
+        icon_view.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
+        sw.add(icon_view)
+
+        # connect to the 'clicked' signal of the "Up" tool button
+        up_button.connect('clicked', self.up_clicked, store)
+
+        # connect to the 'clicked' signal of the "home" tool button
+        home_button.connect('clicked', self.home_clicked, store)
+
+        self.up_button = up_button
+        self.home_button = home_button
+
+        # we now set which model columns that correspond to the text
+        # and pixbuf of each item
+        icon_view.set_text_column(self.COL_DISPLAY_NAME)
+        icon_view.set_pixbuf_column(self.COL_PIXBUF)
+
+        # connect to the "item-activated" signal
+        icon_view.connect('item-activated', self.item_activated, store)
+        icon_view.grab_focus()
+
+        self.window.show_all()
+
+    def sort_func(self, store, a_iter, b_iter, user_data):
+        (a_name, a_is_dir) = store.get(a_iter,
+                                       self.COL_DISPLAY_NAME,
+                                       self.COL_IS_DIRECTORY)
+
+        (b_name, b_is_dir) = store.get(b_iter,
+                                       self.COL_DISPLAY_NAME,
+                                       self.COL_IS_DIRECTORY)
+
+        if a_name is None:
+            a_name = ''
+
+        if b_name is None:
+            b_name = ''
+
+        if (not a_is_dir) and b_is_dir:
+            return 1
+        elif a_is_dir and (not b_is_dir):
+            return -1
+        elif a_name > b_name:
+            return 1
+        elif a_name < b_name:
+            return -1
+        else:
+            return 0
+
+    def up_clicked(self, item, store):
+        self.parent_dir = os.path.split(self.parent_dir)[0]
+        self.fill_store(store)
+        # de-sensitize the up button if we are at the root
+        self.up_button.set_sensitive(self.parent_dir != '/')
+
+    def home_clicked(self, item, store):
+        self.parent_dir = GLib.get_home_dir()
+        self.fill_store(store)
+
+        # Sensitize the up button
+        self.up_button.set_sensitive(True)
+
+    def item_activated(self, icon_view, tree_path, store):
+        iter_ = store.get_iter(tree_path)
+        (path, is_dir) = store.get(iter_, self.COL_PATH, self.COL_IS_DIRECTORY)
+        if not is_dir:
+            return
+
+        self.parent_dir = path
+        self.fill_store(store)
+
+        self.up_button.set_sensitive(True)
+
+    def create_store(self):
+        store = Gtk.ListStore(str, str, GdkPixbuf.Pixbuf, bool)
+
+        # set sort column and function
+        store.set_default_sort_func(self.sort_func)
+        store.set_sort_column_id(-1, Gtk.SortType.ASCENDING)
+
+        return store
+
+    def file_to_icon_pixbuf(self, path):
+        pixbuf = None
+
+        # get the theme icon
+        f = Gio.file_new_for_path(path)
+        info = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON,
+                            Gio.FileQueryInfoFlags.NONE,
+                            None)
+        gicon = info.get_icon()
+
+        # check to see if it is an image format we support
+        for format in GdkPixbuf.Pixbuf.get_formats():
+            for mime_type in format.get_mime_types():
+                content_type = Gio.content_type_from_mime_type(mime_type)
+                if content_type is not None:
+                    break
+
+            format_gicon = Gio.content_type_get_icon(content_type)
+            if format_gicon.equal(gicon):
+                gicon = f.icon_new()
+                break
+
+        if gicon in self.pixbuf_lookup:
+            return self.pixbuf_lookup[gicon]
+
+        if isinstance(gicon, Gio.ThemedIcon):
+            names = gicon.get_names()
+            icon_theme = Gtk.IconTheme.get_default()
+            for name in names:
+                try:
+                    pixbuf = icon_theme.load_icon(name, 64, 0)
+                    break
+                except GLib.GError:
+                    pass
+
+            self.pixbuf_lookup[gicon] = pixbuf
+
+        elif isinstance(gicon, Gio.FileIcon):
+            icon_file = gicon.get_file()
+            path = icon_file.get_path()
+            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path, 72, 72)
+            self.pixbuf_lookup[gicon] = pixbuf
+
+        return pixbuf
+
+    def fill_store(self, store):
+        store.clear()
+        for name in os.listdir(self.parent_dir):
+            path = os.path.join(self.parent_dir, name)
+            is_dir = os.path.isdir(path)
+            pixbuf = self.file_to_icon_pixbuf(path)
+            store.append((path, name, pixbuf, is_dir))
+
+
+def main(demoapp=None):
+    IconViewApp(demoapp)
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/IconView/iconviewedit.py b/examples/demo/demos/IconView/iconviewedit.py
new file mode 100644 (file)
index 0000000..85dfa93
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Editing and Drag-and-Drop"
+description = """The GtkIconView widget supports Editing and Drag-and-Drop.
+This example also demonstrates using the generic GtkCellLayout interface to set
+up cell renderers in an icon view.
+"""
+
+from gi.repository import Gtk, Gdk, GdkPixbuf
+
+
+class IconviewEditApp:
+    COL_TEXT = 0
+    NUM_COLS = 1
+
+    def __init__(self):
+        self.window = Gtk.Window()
+        self.window.set_title('Editing and Drag-and-Drop')
+        self.window.set_border_width(8)
+        self.window.connect('destroy', Gtk.main_quit)
+
+        store = Gtk.ListStore(str)
+        colors = ['Red', 'Green', 'Blue', 'Yellow']
+        store.clear()
+        for c in colors:
+            store.append([c])
+
+        icon_view = Gtk.IconView(model=store)
+        icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE)
+        icon_view.set_item_orientation(Gtk.Orientation.HORIZONTAL)
+        icon_view.set_columns(2)
+        icon_view.set_reorderable(True)
+
+        renderer = Gtk.CellRendererPixbuf()
+        icon_view.pack_start(renderer, True)
+        icon_view.set_cell_data_func(renderer,
+                                     self.set_cell_color,
+                                     None)
+
+        renderer = Gtk.CellRendererText()
+        icon_view.pack_start(renderer, True)
+        renderer.props.editable = True
+        renderer.connect('edited', self.edited, icon_view)
+        icon_view.add_attribute(renderer, 'text', self.COL_TEXT)
+
+        self.window.add(icon_view)
+
+        self.window.show_all()
+
+    def set_cell_color(self, cell_layout, cell, tree_model, iter_, icon_view):
+
+        # FIXME return single element instead of tuple
+        text = tree_model.get(iter_, self.COL_TEXT)[0]
+        color = Gdk.color_parse(text)
+        pixel = 0
+        if color is not None:
+            pixel = ((color.red >> 8) << 24 |
+                     (color.green >> 8) << 16 |
+                     (color.blue >> 8) << 8)
+
+        pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, 24, 24)
+        pixbuf.fill(pixel)
+
+        cell.props.pixbuf = pixbuf
+
+    def edited(self, cell, path_string, text, icon_view):
+        model = icon_view.get_model()
+        path = Gtk.TreePath(path_string)
+
+        iter_ = model.get_iter(path)
+        model.set_row(iter_, [text])
+
+
+def main(demoapp=None):
+    IconviewEditApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/TreeView/__init__.py b/examples/demo/demos/TreeView/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/demo/demos/TreeView/liststore.py b/examples/demo/demos/TreeView/liststore.py
new file mode 100644 (file)
index 0000000..4b3daa1
--- /dev/null
@@ -0,0 +1,212 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "List Store"
+description = """
+The GtkListStore is used to store data in list form, to be used later on by a
+GtkTreeView to display it. This demo builds a simple GtkListStore and displays
+it. See the Stock Browser demo for a more advanced example.
+"""
+
+
+from gi.repository import Gtk, GObject, GLib
+
+
+class Bug:
+    def __init__(self, is_fixed, number, severity, description):
+        self.is_fixed = is_fixed
+        self.number = number
+        self.severity = severity
+        self.description = description
+
+
+# initial data we use to fill in the store
+data = [Bug(False, 60482, "Normal", "scrollable notebooks and hidden tabs"),
+        Bug(False, 60620, "Critical", "gdk_window_clear_area (gdkwindow-win32.c) is not thread-safe"),
+        Bug(False, 50214, "Major", "Xft support does not clean up correctly"),
+        Bug(True, 52877, "Major", "GtkFileSelection needs a refresh method. "),
+        Bug(False, 56070, "Normal", "Can't click button after setting in sensitive"),
+        Bug(True, 56355, "Normal", "GtkLabel - Not all changes propagate correctly"),
+        Bug(False, 50055, "Normal", "Rework width/height computations for TreeView"),
+        Bug(False, 58278, "Normal", "gtk_dialog_set_response_sensitive () doesn't work"),
+        Bug(False, 55767, "Normal", "Getters for all setters"),
+        Bug(False, 56925, "Normal", "Gtkcalender size"),
+        Bug(False, 56221, "Normal", "Selectable label needs right-click copy menu"),
+        Bug(True, 50939, "Normal", "Add shift clicking to GtkTextView"),
+        Bug(False, 6112, "Enhancement", "netscape-like collapsable toolbars"),
+        Bug(False, 1, "Normal", "First bug :=)")]
+
+
+class ListStoreApp:
+    (COLUMN_FIXED,
+     COLUMN_NUMBER,
+     COLUMN_SEVERITY,
+     COLUMN_DESCRIPTION,
+     COLUMN_PULSE,
+     COLUMN_ICON,
+     COLUMN_ACTIVE,
+     COLUMN_SENSITIVE,
+     NUM_COLUMNS) = range(9)
+
+    def __init__(self):
+        self.window = Gtk.Window()
+        self.window.set_title('Gtk.ListStore Demo')
+        self.window.connect('destroy', Gtk.main_quit)
+
+        vbox = Gtk.VBox(spacing=8)
+        self.window.add(vbox)
+
+        label = Gtk.Label(label='This is the bug list (note: not based on real data, it would be nice to have a nice ODBC interface to bugzilla or so, though).')
+        vbox.pack_start(label, False, False, 0)
+
+        sw = Gtk.ScrolledWindow()
+        sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
+        sw.set_policy(Gtk.PolicyType.NEVER,
+                      Gtk.PolicyType.AUTOMATIC)
+        vbox.pack_start(sw, True, True, 0)
+
+        self.create_model()
+        treeview = Gtk.TreeView(model=self.model)
+        treeview.set_rules_hint(True)
+        treeview.set_search_column(self.COLUMN_DESCRIPTION)
+        sw.add(treeview)
+
+        self.add_columns(treeview)
+
+        self.window.set_default_size(280, 250)
+        self.window.show_all()
+
+        self.window.connect('delete-event', self.window_closed)
+        self.timeout = GLib.timeout_add(80, self.spinner_timeout)
+
+    def window_closed(self, window, event):
+        if self.timeout != 0:
+            GLib.source_remove(self.timeout)
+
+    def spinner_timeout(self):
+        if self.model is None:
+            return False
+
+        iter_ = self.model.get_iter_first()
+        pulse = self.model.get(iter_, self.COLUMN_PULSE)[0]
+        if pulse == 999999999:
+            pulse = 0
+        else:
+            pulse += 1
+
+        self.model.set_value(iter_, self.COLUMN_PULSE, pulse)
+        self.model.set_value(iter_, self.COLUMN_ACTIVE, True)
+
+        return True
+
+    def create_model(self):
+        self.model = Gtk.ListStore(bool,
+                                   GObject.TYPE_INT,
+                                   str,
+                                   str,
+                                   GObject.TYPE_INT,
+                                   str,
+                                   bool,
+                                   bool)
+
+        col = 0
+        for bug in data:
+            if col == 1 or col == 3:
+                icon_name = 'battery-critical-charging-symbolic'
+            else:
+                icon_name = ''
+            if col == 3:
+                is_sensitive = False
+            else:
+                is_sensitive = True
+
+            self.model.append([bug.is_fixed,
+                               bug.number,
+                               bug.severity,
+                               bug.description,
+                               0,
+                               icon_name,
+                               False,
+                               is_sensitive])
+            col += 1
+
+    def add_columns(self, treeview):
+        model = treeview.get_model()
+
+        # column for is_fixed toggle
+        renderer = Gtk.CellRendererToggle()
+        renderer.connect('toggled', self.is_fixed_toggled, model)
+
+        column = Gtk.TreeViewColumn("Fixed?", renderer,
+                                    active=self.COLUMN_FIXED)
+        column.set_fixed_width(50)
+        column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
+        treeview.append_column(column)
+
+        # column for severities
+        renderer = Gtk.CellRendererText()
+        column = Gtk.TreeViewColumn("Severity", renderer,
+                                    text=self.COLUMN_SEVERITY)
+        column.set_sort_column_id(self.COLUMN_SEVERITY)
+        treeview.append_column(column)
+
+        # column for description
+        renderer = Gtk.CellRendererText()
+        column = Gtk.TreeViewColumn("Description", renderer,
+                                    text=self.COLUMN_DESCRIPTION)
+        column.set_sort_column_id(self.COLUMN_DESCRIPTION)
+        treeview.append_column(column)
+
+        # column for spinner
+        renderer = Gtk.CellRendererSpinner()
+        column = Gtk.TreeViewColumn("Spinning", renderer,
+                                    pulse=self.COLUMN_PULSE,
+                                    active=self.COLUMN_ACTIVE)
+        column.set_sort_column_id(self.COLUMN_PULSE)
+        treeview.append_column(column)
+
+        # column for symbolic icon
+        renderer = Gtk.CellRendererPixbuf()
+        renderer.props.follow_state = True
+        column = Gtk.TreeViewColumn("Symbolic icon", renderer,
+                                    icon_name=self.COLUMN_ICON,
+                                    sensitive=self.COLUMN_SENSITIVE)
+        column.set_sort_column_id(self.COLUMN_ICON)
+        treeview.append_column(column)
+
+    def is_fixed_toggled(self, cell, path_str, model):
+        # get toggled iter
+        iter_ = model.get_iter(path_str)
+        is_fixed = model.get_value(iter_, self.COLUMN_FIXED)
+
+        # do something with value
+        is_fixed ^= 1
+
+        model.set_value(iter_, self.COLUMN_FIXED, is_fixed)
+
+
+def main(demoapp=None):
+    ListStoreApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/TreeView/treemodel_filelist.py b/examples/demo/demos/TreeView/treemodel_filelist.py
new file mode 100644 (file)
index 0000000..f011a47
--- /dev/null
@@ -0,0 +1,235 @@
+#!/usr/bin/env python
+
+title = "File List (GenericTreeModel)"
+description = """
+This is a file list demo which makes use of the GenericTreeModel python
+implementation of the Gtk.TreeModel interface. This demo shows what methods
+need to be overridden to provide a valid TreeModel to a TreeView.
+"""
+
+import os
+import stat
+import time
+
+import pygtkcompat
+pygtkcompat.enable()
+pygtkcompat.enable_gtk('3.0')
+
+import gtk
+
+
+folderxpm = [
+    "17 16 7 1",
+    "  c #000000",
+    ". c #808000",
+    "X c yellow",
+    "o c #808080",
+    "O c #c0c0c0",
+    "+ c white",
+    "@ c None",
+    "@@@@@@@@@@@@@@@@@",
+    "@@@@@@@@@@@@@@@@@",
+    "@@+XXXX.@@@@@@@@@",
+    "@+OOOOOO.@@@@@@@@",
+    "@+OXOXOXOXOXOXO. ",
+    "@+XOXOXOXOXOXOX. ",
+    "@+OXOXOXOXOXOXO. ",
+    "@+XOXOXOXOXOXOX. ",
+    "@+OXOXOXOXOXOXO. ",
+    "@+XOXOXOXOXOXOX. ",
+    "@+OXOXOXOXOXOXO. ",
+    "@+XOXOXOXOXOXOX. ",
+    "@+OOOOOOOOOOOOO. ",
+    "@                ",
+    "@@@@@@@@@@@@@@@@@",
+    "@@@@@@@@@@@@@@@@@"
+    ]
+folderpb = gtk.gdk.pixbuf_new_from_xpm_data(folderxpm)
+
+filexpm = [
+    "12 12 3 1",
+    "  c #000000",
+    ". c #ffff04",
+    "X c #b2c0dc",
+    "X        XXX",
+    "X ...... XXX",
+    "X ......   X",
+    "X .    ... X",
+    "X ........ X",
+    "X .   .... X",
+    "X ........ X",
+    "X .     .. X",
+    "X ........ X",
+    "X .     .. X",
+    "X ........ X",
+    "X          X"
+    ]
+filepb = gtk.gdk.pixbuf_new_from_xpm_data(filexpm)
+
+
+class FileListModel(gtk.GenericTreeModel):
+    __gtype_name__ = 'DemoFileListModel'
+
+    column_types = (gtk.gdk.Pixbuf, str, int, str, str)
+    column_names = ['Name', 'Size', 'Mode', 'Last Changed']
+
+    def __init__(self, dname=None):
+        gtk.GenericTreeModel.__init__(self)
+        self._sort_column_id = 0
+        self._sort_order = gtk.SORT_ASCENDING
+
+        if not dname:
+            self.dirname = os.path.expanduser('~')
+        else:
+            self.dirname = os.path.abspath(dname)
+        self.files = ['..'] + [f for f in os.listdir(self.dirname)]
+        return
+
+    def get_pathname(self, path):
+        filename = self.files[path[0]]
+        return os.path.join(self.dirname, filename)
+
+    def is_folder(self, path):
+        filename = self.files[path[0]]
+        pathname = os.path.join(self.dirname, filename)
+        filestat = os.stat(pathname)
+        if stat.S_ISDIR(filestat.st_mode):
+            return True
+        return False
+
+    def get_column_names(self):
+        return self.column_names[:]
+
+    #
+    # GenericTreeModel Implementation
+    #
+    def on_get_flags(self):
+        return 0  # gtk.TREE_MODEL_ITERS_PERSIST
+
+    def on_get_n_columns(self):
+        return len(self.column_types)
+
+    def on_get_column_type(self, n):
+        return self.column_types[n]
+
+    def on_get_iter(self, path):
+        return self.files[path[0]]
+
+    def on_get_path(self, rowref):
+        return self.files.index(rowref)
+
+    def on_get_value(self, rowref, column):
+        fname = os.path.join(self.dirname, rowref)
+        try:
+            filestat = os.stat(fname)
+        except OSError:
+            return None
+        mode = filestat.st_mode
+        if column is 0:
+            if stat.S_ISDIR(mode):
+                return folderpb
+            else:
+                return filepb
+        elif column is 1:
+            return rowref
+        elif column is 2:
+            return filestat.st_size
+        elif column is 3:
+            return oct(stat.S_IMODE(mode))
+        return time.ctime(filestat.st_mtime)
+
+    def on_iter_next(self, rowref):
+        try:
+            i = self.files.index(rowref) + 1
+            return self.files[i]
+        except IndexError:
+            return None
+
+    def on_iter_children(self, rowref):
+        if rowref:
+            return None
+        return self.files[0]
+
+    def on_iter_has_child(self, rowref):
+        return False
+
+    def on_iter_n_children(self, rowref):
+        if rowref:
+            return 0
+        return len(self.files)
+
+    def on_iter_nth_child(self, rowref, n):
+        if rowref:
+            return None
+        try:
+            return self.files[n]
+        except IndexError:
+            return None
+
+    def on_iter_parent(child):
+        return None
+
+
+class GenericTreeModelExample:
+    def delete_event(self, widget, event, data=None):
+        gtk.main_quit()
+        return False
+
+    def __init__(self):
+        # Create a new window
+        self.window = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
+
+        self.window.set_size_request(300, 200)
+
+        self.window.connect("delete_event", self.delete_event)
+
+        self.listmodel = FileListModel()
+
+        # create the TreeView
+        self.treeview = gtk.TreeView()
+
+        self.tvcolumns = []
+
+        # create the TreeViewColumns to display the data
+        for n, name in enumerate(self.listmodel.get_column_names()):
+            if n == 0:
+                cellpb = gtk.CellRendererPixbuf()
+                col = gtk.TreeViewColumn(name, cellpb, pixbuf=0)
+                cell = gtk.CellRendererText()
+                col.pack_start(cell, False)
+                col.add_attribute(cell, 'text', 1)
+            else:
+                cell = gtk.CellRendererText()
+                col = gtk.TreeViewColumn(name, cell, text=n + 1)
+            if n == 1:
+                cell.set_property('xalign', 1.0)
+
+            self.treeview.append_column(col)
+
+        self.treeview.connect('row-activated', self.open_file)
+
+        self.scrolledwindow = gtk.ScrolledWindow()
+        self.scrolledwindow.add(self.treeview)
+        self.window.add(self.scrolledwindow)
+        self.treeview.set_model(self.listmodel)
+        self.window.set_title(self.listmodel.dirname)
+        self.window.show_all()
+
+    def open_file(self, treeview, path, column):
+        model = treeview.get_model()
+        if model.is_folder(path):
+            pathname = model.get_pathname(path)
+            new_model = FileListModel(pathname)
+            self.window.set_title(new_model.dirname)
+            treeview.set_model(new_model)
+        return
+
+
+def main(demoapp=None):
+    demo = GenericTreeModelExample()
+    demo
+    gtk.main()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/examples/demo/demos/TreeView/treemodel_filetree.py b/examples/demo/demos/TreeView/treemodel_filetree.py
new file mode 100644 (file)
index 0000000..3b43190
--- /dev/null
@@ -0,0 +1,280 @@
+#!/usr/bin/env python
+
+title = "File Tree (GenericTreeModel)"
+description = """
+This is a file list demo which makes use of the GenericTreeModel python
+implementation of the Gtk.TreeModel interface. This demo shows what methods
+need to be overridden to provide a valid TreeModel to a TreeView.
+"""
+
+import os
+import stat
+import time
+from collections import OrderedDict
+
+import pygtkcompat
+pygtkcompat.enable_gtk('3.0')
+
+import gtk
+
+
+folderxpm = [
+    "17 16 7 1",
+    "  c #000000",
+    ". c #808000",
+    "X c yellow",
+    "o c #808080",
+    "O c #c0c0c0",
+    "+ c white",
+    "@ c None",
+    "@@@@@@@@@@@@@@@@@",
+    "@@@@@@@@@@@@@@@@@",
+    "@@+XXXX.@@@@@@@@@",
+    "@+OOOOOO.@@@@@@@@",
+    "@+OXOXOXOXOXOXO. ",
+    "@+XOXOXOXOXOXOX. ",
+    "@+OXOXOXOXOXOXO. ",
+    "@+XOXOXOXOXOXOX. ",
+    "@+OXOXOXOXOXOXO. ",
+    "@+XOXOXOXOXOXOX. ",
+    "@+OXOXOXOXOXOXO. ",
+    "@+XOXOXOXOXOXOX. ",
+    "@+OOOOOOOOOOOOO. ",
+    "@                ",
+    "@@@@@@@@@@@@@@@@@",
+    "@@@@@@@@@@@@@@@@@"
+    ]
+folderpb = gtk.gdk.pixbuf_new_from_xpm_data(folderxpm)
+
+filexpm = [
+    "12 12 3 1",
+    "  c #000000",
+    ". c #ffff04",
+    "X c #b2c0dc",
+    "X        XXX",
+    "X ...... XXX",
+    "X ......   X",
+    "X .    ... X",
+    "X ........ X",
+    "X .   .... X",
+    "X ........ X",
+    "X .     .. X",
+    "X ........ X",
+    "X .     .. X",
+    "X ........ X",
+    "X          X"
+    ]
+filepb = gtk.gdk.pixbuf_new_from_xpm_data(filexpm)
+
+
+class FileTreeModel(gtk.GenericTreeModel):
+    __gtype_name__ = 'DemoFileTreeModel'
+
+    column_types = (gtk.gdk.Pixbuf, str, int, str, str)
+    column_names = ['Name', 'Size', 'Mode', 'Last Changed']
+
+    def __init__(self, dname=None):
+        gtk.GenericTreeModel.__init__(self)
+        if not dname:
+            self.dirname = os.path.expanduser('~')
+        else:
+            self.dirname = os.path.abspath(dname)
+        self.files = self.build_file_dict(self.dirname)
+        return
+
+    def build_file_dict(self, dirname):
+        """
+        :Returns:
+            A dictionary containing the files in the given dirname keyed by filename.
+            If the child filename is a sub-directory, the dict value is a dict.
+            Otherwise it will be None.
+        """
+        d = OrderedDict()
+        for fname in os.listdir(dirname):
+            try:
+                filestat = os.stat(os.path.join(dirname, fname))
+            except OSError:
+                d[fname] = None
+            else:
+                d[fname] = OrderedDict() if stat.S_ISDIR(filestat.st_mode) else None
+
+        return d
+
+    def get_node_from_treepath(self, path):
+        """
+        :Returns:
+            The node stored at the given tree path in local storage.
+        """
+        # TreePaths are a series of integer indices so just iterate through them
+        # and index values by each integer since we are using an OrderedDict
+        if path is None:
+            path = []
+        node = self.files
+        for index in path:
+            node = list(node.values())[index]
+        return node
+
+    def get_node_from_filepath(self, filepath):
+        """
+        :Returns:
+            The node stored at the given file path in local storage.
+        """
+        if not filepath:
+            return self.files
+        node = self.files
+        for key in filepath.split(os.path.sep):
+            node = node[key]
+        return node
+
+    def get_column_names(self):
+        return self.column_names[:]
+
+    #
+    # GenericTreeModel Implementation
+    #
+
+    def on_get_flags(self):
+        return 0
+
+    def on_get_n_columns(self):
+        return len(self.column_types)
+
+    def on_get_column_type(self, n):
+        return self.column_types[n]
+
+    def on_get_path(self, relpath):
+        path = []
+        node = self.files
+        for key in relpath.split(os.path.sep):
+            path.append(list(node.keys()).index(key))
+            node = node[key]
+        return path
+
+    def on_get_value(self, relpath, column):
+        fname = os.path.join(self.dirname, relpath)
+        try:
+            filestat = os.stat(fname)
+        except OSError:
+            return None
+        mode = filestat.st_mode
+        if column is 0:
+            if stat.S_ISDIR(mode):
+                return folderpb
+            else:
+                return filepb
+        elif column is 1:
+            return os.path.basename(relpath)
+        elif column is 2:
+            return filestat.st_size
+        elif column is 3:
+            return oct(stat.S_IMODE(mode))
+        return time.ctime(filestat.st_mtime)
+
+    def on_get_iter(self, path):
+        filepath = ''
+        value = self.files
+        for index in path:
+            filepath = os.path.join(filepath, list(value.keys())[index])
+            value = list(value.values())[index]
+        return filepath
+
+    def on_iter_next(self, filepath):
+        parent_path, child_path = os.path.split(filepath)
+        parent = self.get_node_from_filepath(parent_path)
+
+        # Index of filepath within its parents child list
+        sibling_names = list(parent.keys())
+        index = sibling_names.index(child_path)
+        try:
+            return os.path.join(parent_path, sibling_names[index + 1])
+        except IndexError:
+            return None
+
+    def on_iter_children(self, filepath):
+        if filepath:
+            children = list(self.get_node_from_filepath(filepath).keys())
+            if children:
+                return os.path.join(filepath, children[0])
+        elif self.files:
+            return list(self.files.keys())[0]
+
+        return None
+
+    def on_iter_has_child(self, filepath):
+        return bool(self.get_node_from_filepath(filepath))
+
+    def on_iter_n_children(self, filepath):
+        return len(self.get_node_from_filepath(filepath))
+
+    def on_iter_nth_child(self, filepath, n):
+        try:
+            child = list(self.get_node_from_filepath(filepath).keys())[n]
+            if filepath:
+                return os.path.join(filepath, child)
+            else:
+                return child
+        except IndexError:
+            return None
+
+    def on_iter_parent(self, filepath):
+        return os.path.dirname(filepath)
+
+    def on_ref_node(self, filepath):
+        value = self.get_node_from_filepath(filepath)
+        if value is not None:
+            value.update(self.build_file_dict(os.path.join(self.dirname, filepath)))
+
+    def on_unref_node(self, filepath):
+        pass
+
+
+class GenericTreeModelExample:
+    def delete_event(self, widget, event, data=None):
+        gtk.main_quit()
+        return False
+
+    def __init__(self):
+        # Create a new window
+        self.window = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
+        self.window.set_size_request(300, 200)
+        self.window.connect("delete_event", self.delete_event)
+
+        self.listmodel = FileTreeModel()
+
+        # create the TreeView
+        self.treeview = gtk.TreeView()
+
+        # create the TreeViewColumns to display the data
+        column_names = self.listmodel.get_column_names()
+        self.tvcolumn = [None] * len(column_names)
+        cellpb = gtk.CellRendererPixbuf()
+        self.tvcolumn[0] = gtk.TreeViewColumn(column_names[0],
+                                              cellpb, pixbuf=0)
+        cell = gtk.CellRendererText()
+        self.tvcolumn[0].pack_start(cell, False)
+        self.tvcolumn[0].add_attribute(cell, 'text', 1)
+        self.treeview.append_column(self.tvcolumn[0])
+        for n in range(1, len(column_names)):
+            cell = gtk.CellRendererText()
+            if n == 1:
+                cell.set_property('xalign', 1.0)
+            self.tvcolumn[n] = gtk.TreeViewColumn(column_names[n],
+                                                  cell, text=n + 1)
+            self.treeview.append_column(self.tvcolumn[n])
+
+        self.scrolledwindow = gtk.ScrolledWindow()
+        self.scrolledwindow.add(self.treeview)
+        self.window.add(self.scrolledwindow)
+        self.treeview.set_model(self.listmodel)
+        self.window.set_title(self.listmodel.dirname)
+        self.window.show_all()
+
+
+def main(demoapp=None):
+    demo = GenericTreeModelExample()
+    demo
+    gtk.main()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/examples/demo/demos/TreeView/treemodel_large.py b/examples/demo/demos/TreeView/treemodel_large.py
new file mode 100644 (file)
index 0000000..b129521
--- /dev/null
@@ -0,0 +1,143 @@
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# pygobject - Python bindings for the GObject library
+# Copyright (C) 2014 Simon Feltman
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+title = "Tree Model with Large Data"
+description = """
+Implementation of the Gtk.TreeModel interface to create a custom model.
+The demo uses a fake data store (it is not backed by a Python list) and is for
+the purpose of showing how to override the TreeModel interfaces virtual methods.
+"""
+
+from gi.repository import GObject
+from gi.repository import GLib
+from gi.repository import Gtk
+
+
+class Model(GObject.Object, Gtk.TreeModel):
+    columns_types = (str, str)
+    item_count = 100000
+    item_data = 'abcdefghijklmnopqrstuvwxyz'
+
+    def __init__(self):
+        super(Model, self).__init__()
+
+    def do_get_flags(self):
+        return Gtk.TreeModelFlags.LIST_ONLY
+
+    def do_get_n_columns(self):
+        return len(self.columns_types)
+
+    def do_get_column_type(self, n):
+        return self.columns_types[n]
+
+    def do_get_iter(self, path):
+        # Return False and an empty iter when out of range
+        index = path.get_indices()[0]
+        if index < 0 or index >= self.item_count:
+            return False, None
+
+        it = Gtk.TreeIter()
+        it.user_data = index
+        return True, it
+
+    def do_get_path(self, it):
+        return Gtk.TreePath([it.user_data])
+
+    def do_get_value(self, it, column):
+        if column == 0:
+            return str(it.user_data)
+        elif column == 1:
+            return self.item_data
+
+    def do_iter_next(self, it):
+        # Return False if there is not a next item
+        next = it.user_data + 1
+        if next >= self.item_count:
+            return False
+
+        # Set the iters data and return True
+        it.user_data = next
+        return True
+
+    def do_iter_previous(self, it):
+        prev = it.user_data - 1
+        if prev < 0:
+            return False
+
+        it.user_data = prev
+        return True
+
+    def do_iter_children(self, parent):
+        # If parent is None return the first item
+        if parent is None:
+            it = Gtk.TreeIter()
+            it.user_data = 0
+            return True, it
+        return False, None
+
+    def do_iter_has_child(self, it):
+        return it is None
+
+    def do_iter_n_children(self, it):
+        # If iter is None, return the number of top level nodes
+        if it is None:
+            return self.item_count
+        return 0
+
+    def do_iter_nth_child(self, parent, n):
+        if parent is not None or n >= self.item_count:
+            return False, None
+        elif parent is None:
+            # If parent is None, return the nth iter
+            it = Gtk.TreeIter()
+            it.user_data = n
+            return True, it
+
+    def do_iter_parent(self, child):
+        return False, None
+
+
+def main(demoapp=None):
+    model = Model()
+    # Use fixed-height-mode to get better model load and display performance.
+    view = Gtk.TreeView(fixed_height_mode=True, headers_visible=False)
+    column = Gtk.TreeViewColumn()
+    column.props.sizing = Gtk.TreeViewColumnSizing.FIXED
+
+    renderer1 = Gtk.CellRendererText()
+    renderer2 = Gtk.CellRendererText()
+    column.pack_start(renderer1, expand=True)
+    column.pack_start(renderer2, expand=True)
+    column.add_attribute(renderer1, 'text', 0)
+    column.add_attribute(renderer2, 'text', 1)
+    view.append_column(column)
+
+    scrolled = Gtk.ScrolledWindow()
+    scrolled.add(view)
+
+    window = Gtk.Window(title=title)
+    window.set_size_request(480, 640)
+    window.add(scrolled)
+    window.show_all()
+    GLib.timeout_add(10, lambda *args: view.set_model(model))
+    return window
+
+
+if __name__ == "__main__":
+    window = main()
+    window.connect('destroy', Gtk.main_quit)
+    Gtk.main()
diff --git a/examples/demo/demos/__init__.py b/examples/demo/demos/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/demo/demos/appwindow.py b/examples/demo/demos/appwindow.py
new file mode 100644 (file)
index 0000000..893ecc0
--- /dev/null
@@ -0,0 +1,408 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Application main window"
+description = """
+Demonstrates a typical application window with menubar, toolbar, statusbar.
+"""
+
+import os
+
+from gi.repository import GdkPixbuf, Gtk
+
+
+infobar = None
+window = None
+messagelabel = None
+_demoapp = None
+
+
+def widget_destroy(widget, button):
+    widget.destroy()
+
+
+def activate_action(action, user_data=None):
+    global window
+
+    name = action.get_name()
+    _type = type(action)
+    if name == 'DarkTheme':
+        value = action.get_active()
+        settings = Gtk.Settings.get_default()
+        settings.set_property('gtk-application-prefer-dark-theme', value)
+        return
+
+    dialog = Gtk.MessageDialog(message_type=Gtk.MessageType.INFO,
+                               buttons=Gtk.ButtonsType.CLOSE,
+                               text='You activated action: "%s" of type %s' % (name, _type))
+
+    # FIXME: this should be done in the constructor
+    dialog.set_transient_for(window)
+    dialog.connect('response', widget_destroy)
+    dialog.show()
+
+
+def activate_radio_action(action, current, user_data=None):
+    global infobar
+    global messagelabel
+
+    name = current.get_name()
+    _type = type(current)
+    active = current.get_active()
+    value = current.get_current_value()
+    if active:
+        text = 'You activated radio action: "%s" of type %s.\n Current value: %d' % (name, _type, value)
+        messagelabel.set_text(text)
+        infobar.set_message_type(Gtk.MessageType(value))
+        infobar.show()
+
+
+def update_statusbar(buffer, statusbar):
+    statusbar.pop(0)
+    count = buffer.get_char_count()
+
+    iter = buffer.get_iter_at_mark(buffer.get_insert())
+    row = iter.get_line()
+    col = iter.get_line_offset()
+    msg = 'Cursor at row %d column %d - %d chars in document' % (row, col, count)
+
+    statusbar.push(0, msg)
+
+
+def mark_set_callback(buffer, new_location, mark, data):
+    update_statusbar(buffer, data)
+
+
+def about_cb(widget, user_data=None):
+    global window
+
+    authors = ['John (J5) Palmieri',
+               'Tomeu Vizoso',
+               'and many more...']
+
+    documentors = ['David Malcolm',
+                   'Zack Goldberg',
+                   'and many more...']
+
+    license = """
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the Gnome Library; see the file COPYING.LIB.  If not,
+write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+"""
+    dirname = os.path.abspath(os.path.dirname(__file__))
+    filename = os.path.join(dirname, 'data', 'gtk-logo-rgb.gif')
+    pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+    transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
+
+    about = Gtk.AboutDialog(parent=window,
+                            program_name='GTK+ Code Demos',
+                            version='0.1',
+                            copyright='(C) 2010 The PyGI Team',
+                            license=license,
+                            website='http://live.gnome.org/PyGI',
+                            comments='Program to demonstrate PyGI functions.',
+                            authors=authors,
+                            documenters=documentors,
+                            logo=transparent,
+                            title='About GTK+ Code Demos')
+
+    about.connect('response', widget_destroy)
+    about.show()
+
+
+action_entries = (
+    ("FileMenu", None, "_File"),                # name, stock id, label
+    ("OpenMenu", None, "_Open"),                # name, stock id, label
+    ("PreferencesMenu", None, "_Preferences"),  # name, stock id, label
+    ("ColorMenu", None, "_Color"),              # name, stock id, label
+    ("ShapeMenu", None, "_Shape"),              # name, stock id, label
+    ("HelpMenu", None, "_Help"),                # name, stock id, label
+    ("New", Gtk.STOCK_NEW,                      # name, stock id
+     "_New", "<control>N",                      # label, accelerator
+     "Create a new file",                       # tooltip
+     activate_action),
+    ("File1", None,                             # name, stock id
+     "File1", None,                             # label, accelerator
+     "Open first file",                         # tooltip
+     activate_action),
+    ("Save", Gtk.STOCK_SAVE,                    # name, stock id
+     "_Save", "<control>S",                     # label, accelerator
+     "Save current file",                       # tooltip
+     activate_action),
+    ("SaveAs", Gtk.STOCK_SAVE,                  # name, stock id
+     "Save _As...", None,                       # label, accelerator
+     "Save to a file",                          # tooltip
+     activate_action),
+    ("Quit", Gtk.STOCK_QUIT,                    # name, stock id
+     "_Quit", "<control>Q",                     # label, accelerator
+     "Quit",                                    # tooltip
+     activate_action),
+    ("About", None,                             # name, stock id
+     "_About", "<control>A",                    # label, accelerator
+     "About",                                   # tooltip
+     about_cb),
+    ("Logo", "demo-gtk-logo",                   # name, stock id
+     None, None,                                # label, accelerator
+     "GTK+",                                    # tooltip
+     activate_action),
+)
+
+toggle_action_entries = (
+    ("Bold", Gtk.STOCK_BOLD,                    # name, stock id
+     "_Bold", "<control>B",                     # label, accelerator
+     "Bold",                                    # tooltip
+     activate_action,
+     True),                                     # is_active
+    ("DarkTheme", None,                         # name, stock id
+     "_Prefer Dark Theme", None,                # label, accelerator
+     "Prefer Dark Theme",                       # tooltip
+     activate_action,
+     False),                                    # is_active
+)
+
+(COLOR_RED,
+ COLOR_GREEN,
+ COLOR_BLUE) = range(3)
+
+color_action_entries = (
+    ("Red", None,                               # name, stock id
+     "_Red", "<control>R",                      # label, accelerator
+     "Blood", COLOR_RED),                       # tooltip, value
+    ("Green", None,                             # name, stock id
+     "_Green", "<control>G",                    # label, accelerator
+     "Grass", COLOR_GREEN),                     # tooltip, value
+    ("Blue", None,                              # name, stock id
+     "_Blue", "<control>B",                     # label, accelerator
+     "Sky", COLOR_BLUE),                        # tooltip, value
+)
+
+(SHAPE_SQUARE,
+ SHAPE_RECTANGLE,
+ SHAPE_OVAL) = range(3)
+
+shape_action_entries = (
+    ("Square", None,                            # name, stock id
+     "_Square", "<control>S",                   # label, accelerator
+     "Square", SHAPE_SQUARE),                   # tooltip, value
+    ("Rectangle", None,                         # name, stock id
+     "_Rectangle", "<control>R",                # label, accelerator
+     "Rectangle", SHAPE_RECTANGLE),             # tooltip, value
+    ("Oval", None,                              # name, stock id
+     "_Oval", "<control>O",                     # label, accelerator
+     "Egg", SHAPE_OVAL),                        # tooltip, value
+)
+
+ui_info = """
+<ui>
+  <menubar name='MenuBar'>
+    <menu action='FileMenu'>
+      <menuitem action='New'/>
+      <menuitem action='Open'/>
+      <menuitem action='Save'/>
+      <menuitem action='SaveAs'/>
+      <separator/>
+      <menuitem action='Quit'/>
+    </menu>
+    <menu action='PreferencesMenu'>
+      <menuitem action='DarkTheme'/>
+      <menu action='ColorMenu'>
+    <menuitem action='Red'/>
+    <menuitem action='Green'/>
+    <menuitem action='Blue'/>
+      </menu>
+      <menu action='ShapeMenu'>
+        <menuitem action='Square'/>
+        <menuitem action='Rectangle'/>
+        <menuitem action='Oval'/>
+      </menu>
+      <menuitem action='Bold'/>
+    </menu>
+    <menu action='HelpMenu'>
+      <menuitem action='About'/>
+    </menu>
+  </menubar>
+  <toolbar name='ToolBar'>
+    <toolitem action='Open'>
+      <menu action='OpenMenu'>
+        <menuitem action='File1'/>
+      </menu>
+    </toolitem>
+    <toolitem action='Quit'/>
+    <separator action='Sep1'/>
+    <toolitem action='Logo'/>
+  </toolbar>
+</ui>
+"""
+
+
+def _quit(*args):
+    Gtk.main_quit()
+
+
+def register_stock_icons():
+    """
+    This function registers our custom toolbar icons, so they can be themed.
+    It's totally optional to do this, you could just manually insert icons
+    and have them not be themeable, especially if you never expect people
+    to theme your app.
+    """
+    '''
+    item = Gtk.StockItem()
+    item.stock_id = 'demo-gtk-logo'
+    item.label = '_GTK!'
+    item.modifier = 0
+    item.keyval = 0
+    item.translation_domain = None
+
+    Gtk.stock_add(item, 1)
+    '''
+    global _demoapp
+
+    factory = Gtk.IconFactory()
+    factory.add_default()
+
+    if _demoapp is None:
+        filename = os.path.join('data', 'gtk-logo-rgb.gif')
+    else:
+        filename = _demoapp.find_file('gtk-logo-rgb.gif')
+
+    pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+    transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
+    icon_set = Gtk.IconSet.new_from_pixbuf(transparent)
+
+    factory.add('demo-gtk-logo', icon_set)
+
+
+class ToolMenuAction(Gtk.Action):
+    __gtype_name__ = "GtkToolMenuAction"
+
+    def do_create_tool_item(self):
+        return Gtk.MenuToolButton()
+
+
+def main(demoapp=None):
+    global infobar
+    global window
+    global messagelabel
+    global _demoapp
+
+    _demoapp = demoapp
+
+    register_stock_icons()
+
+    window = Gtk.Window()
+    window.set_title('Application Window')
+    window.set_icon_name('gtk-open')
+    window.connect_after('destroy', _quit)
+    table = Gtk.Table(n_rows=1,
+                      n_columns=5,
+                      homogeneous=False)
+    window.add(table)
+
+    action_group = Gtk.ActionGroup(name='AppWindowActions')
+    open_action = ToolMenuAction(name='Open',
+                                 stock_id=Gtk.STOCK_OPEN,
+                                 label='_Open',
+                                 tooltip='Open a file')
+
+    action_group.add_action(open_action)
+    action_group.add_actions(action_entries)
+    action_group.add_toggle_actions(toggle_action_entries)
+    action_group.add_radio_actions(color_action_entries,
+                                   COLOR_RED,
+                                   activate_radio_action)
+    action_group.add_radio_actions(shape_action_entries,
+                                   SHAPE_SQUARE,
+                                   activate_radio_action)
+
+    merge = Gtk.UIManager()
+    merge.insert_action_group(action_group, 0)
+    window.add_accel_group(merge.get_accel_group())
+
+    merge.add_ui_from_string(ui_info)
+
+    bar = merge.get_widget('/MenuBar')
+    bar.show()
+    table.attach(bar, 0, 1, 0, 1,
+                 Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+                 0, 0, 0)
+
+    bar = merge.get_widget('/ToolBar')
+    bar.show()
+    table.attach(bar, 0, 1, 1, 2,
+                 Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+                 0, 0, 0)
+
+    infobar = Gtk.InfoBar()
+    infobar.set_no_show_all(True)
+    messagelabel = Gtk.Label()
+    messagelabel.show()
+    infobar.get_content_area().pack_start(messagelabel, True, True, 0)
+    infobar.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
+    infobar.connect('response', lambda a, b: Gtk.Widget.hide(a))
+
+    table.attach(infobar, 0, 1, 2, 3,
+                 Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+                 0, 0, 0)
+
+    sw = Gtk.ScrolledWindow(hadjustment=None,
+                            vadjustment=None)
+    sw.set_shadow_type(Gtk.ShadowType.IN)
+    table.attach(sw, 0, 1, 3, 4,
+                 Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+                 Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+                 0, 0)
+
+    contents = Gtk.TextView()
+    contents.grab_focus()
+    sw.add(contents)
+
+    # Create statusbar
+    statusbar = Gtk.Statusbar()
+    table.attach(statusbar, 0, 1, 4, 5,
+                 Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+                 0, 0, 0)
+
+    # show text widget info in the statusbar
+    buffer = contents.get_buffer()
+    buffer.connect('changed', update_statusbar, statusbar)
+    buffer.connect('mark_set', mark_set_callback, statusbar)
+
+    update_statusbar(buffer, statusbar)
+
+    window.set_default_size(200, 200)
+    window.show_all()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/assistant.py b/examples/demo/demos/assistant.py
new file mode 100644 (file)
index 0000000..9e729e9
--- /dev/null
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Assistant"
+description = """
+Demonstrates a sample multistep assistant. Assistants are used to divide
+an operation into several simpler sequential steps, and to guide the user
+through these steps.
+"""
+
+
+from gi.repository import Gtk
+
+
+class AssistantApp:
+    def __init__(self):
+        self.assistant = Gtk.Assistant()
+        self.assistant.set_default_size(-1, 300)
+
+        self.create_page1()
+        self.create_page2()
+        self.create_page3()
+
+        self.assistant.connect('cancel', self.on_close_cancel)
+        self.assistant.connect('close', self.on_close_cancel)
+        self.assistant.connect('apply', self.on_apply)
+        self.assistant.connect('prepare', self.on_prepare)
+
+        self.assistant.show()
+
+    def on_close_cancel(self, assistant):
+        assistant.destroy()
+        Gtk.main_quit()
+
+    def on_apply(self, assistant):
+        # apply changes here; this is a fictional example so just do
+        # nothing here
+        pass
+
+    def on_prepare(self, assistant, page):
+        current_page = assistant.get_current_page()
+        n_pages = assistant.get_n_pages()
+        title = 'Sample assistant (%d of %d)' % (current_page + 1, n_pages)
+        assistant.set_title(title)
+
+    def on_entry_changed(self, widget):
+        page_number = self.assistant.get_current_page()
+        current_page = self.assistant.get_nth_page(page_number)
+        text = widget.get_text()
+
+        if text:
+            self.assistant.set_page_complete(current_page, True)
+        else:
+            self.assistant.set_page_complete(current_page, False)
+
+    def create_page1(self):
+        box = Gtk.HBox(homogeneous=False,
+                       spacing=12)
+        box.set_border_width(12)
+        label = Gtk.Label(label='You must fill out this entry to continue:')
+        box.pack_start(label, False, False, 0)
+
+        entry = Gtk.Entry()
+        box.pack_start(entry, True, True, 0)
+        entry.connect('changed', self.on_entry_changed)
+
+        box.show_all()
+        self.assistant.append_page(box)
+        self.assistant.set_page_title(box, 'Page 1')
+        self.assistant.set_page_type(box, Gtk.AssistantPageType.INTRO)
+
+        pixbuf = self.assistant.render_icon(Gtk.STOCK_DIALOG_INFO,
+                                            Gtk.IconSize.DIALOG,
+                                            None)
+
+        self.assistant.set_page_header_image(box, pixbuf)
+
+    def create_page2(self):
+        box = Gtk.VBox(homogeneous=False,
+                       spacing=12)
+        box.set_border_width(12)
+
+        checkbutton = Gtk.CheckButton(label='This is optional data, you may continue even if you do not check this')
+        box.pack_start(checkbutton, False, False, 0)
+
+        box.show_all()
+
+        self.assistant.append_page(box)
+        self.assistant.set_page_complete(box, True)
+        self.assistant.set_page_title(box, 'Page 2')
+
+        pixbuf = self.assistant.render_icon(Gtk.STOCK_DIALOG_INFO,
+                                            Gtk.IconSize.DIALOG,
+                                            None)
+        self.assistant.set_page_header_image(box, pixbuf)
+
+    def create_page3(self):
+        label = Gtk.Label(label='This is a confirmation page, press "Apply" to apply changes')
+        label.show()
+        self.assistant.append_page(label)
+        self.assistant.set_page_complete(label, True)
+        self.assistant.set_page_title(label, 'Confirmation')
+        self.assistant.set_page_type(label, Gtk.AssistantPageType.CONFIRM)
+
+        pixbuf = self.assistant.render_icon(Gtk.STOCK_DIALOG_INFO,
+                                            Gtk.IconSize.DIALOG,
+                                            None)
+        self.assistant.set_page_header_image(label, pixbuf)
+
+
+def main(demoapp=None):
+    AssistantApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/builder.py b/examples/demo/demos/builder.py
new file mode 100644 (file)
index 0000000..47e09a4
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Builder"
+description = """
+Demonstrates an interface loaded from a XML description.
+"""
+
+
+import os
+
+from gi.repository import Gtk
+
+
+class BuilderApp:
+    def __init__(self, demoapp):
+        self.demoapp = demoapp
+
+        self.builder = Gtk.Builder()
+        if demoapp is None:
+            filename = os.path.join('data', 'demo.ui')
+        else:
+            filename = demoapp.find_file('demo.ui')
+
+        self.builder.add_from_file(filename)
+        self.builder.connect_signals(self)
+
+        window = self.builder.get_object('window1')
+        window.connect('destroy', lambda x: Gtk.main_quit())
+        window.show_all()
+
+    def about_activate(self, action):
+        about_dlg = self.builder.get_object('aboutdialog1')
+        about_dlg.run()
+        about_dlg.hide()
+
+    def quit_activate(self, action):
+        Gtk.main_quit()
+
+
+def main(demoapp=None):
+    BuilderApp(demoapp)
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/button_box.py b/examples/demo/demos/button_box.py
new file mode 100644 (file)
index 0000000..be94984
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Button Boxes"
+description = """
+The Button Box widgets are used to arrange buttons with padding.
+"""
+
+
+from gi.repository import Gtk
+
+
+class ButtonBoxApp:
+    def __init__(self):
+        window = Gtk.Window()
+        window.set_title('Button Boxes')
+        window.connect('destroy', lambda x: Gtk.main_quit())
+        window.set_border_width(10)
+
+        main_vbox = Gtk.VBox(homogeneous=False, spacing=0)
+        window.add(main_vbox)
+
+        frame_horz = Gtk.Frame(label='Horizontal Button Boxes')
+        main_vbox.pack_start(frame_horz, True, True, 10)
+
+        vbox = Gtk.VBox(homogeneous=False, spacing=0)
+        vbox.set_border_width(10)
+        frame_horz.add(vbox)
+
+        vbox.pack_start(
+            self.create_bbox(True, 'Spread', 40, Gtk.ButtonBoxStyle.SPREAD),
+            True, True, 0)
+
+        vbox.pack_start(
+            self.create_bbox(True, 'Edge', 40, Gtk.ButtonBoxStyle.EDGE),
+            True, True, 5)
+
+        vbox.pack_start(
+            self.create_bbox(True, 'Start', 40, Gtk.ButtonBoxStyle.START),
+            True, True, 5)
+
+        vbox.pack_start(
+            self.create_bbox(True, 'End', 40, Gtk.ButtonBoxStyle.END),
+            True, True, 5)
+
+        frame_vert = Gtk.Frame(label='Vertical Button Boxes')
+        main_vbox.pack_start(frame_vert, True, True, 10)
+
+        hbox = Gtk.HBox(homogeneous=False, spacing=0)
+        hbox.set_border_width(10)
+        frame_vert.add(hbox)
+
+        hbox.pack_start(
+            self.create_bbox(False, 'Spread', 30, Gtk.ButtonBoxStyle.SPREAD),
+            True, True, 0)
+
+        hbox.pack_start(
+            self.create_bbox(False, 'Edge', 30, Gtk.ButtonBoxStyle.EDGE),
+            True, True, 5)
+
+        hbox.pack_start(
+            self.create_bbox(False, 'Start', 30, Gtk.ButtonBoxStyle.START),
+            True, True, 5)
+
+        hbox.pack_start(
+            self.create_bbox(False, 'End', 30, Gtk.ButtonBoxStyle.END),
+            True, True, 5)
+
+        window.show_all()
+
+    def create_bbox(self, is_horizontal, title, spacing, layout):
+        frame = Gtk.Frame(label=title)
+
+        if is_horizontal:
+            bbox = Gtk.HButtonBox()
+        else:
+            bbox = Gtk.VButtonBox()
+
+        bbox.set_border_width(5)
+        frame.add(bbox)
+
+        bbox.set_layout(layout)
+        bbox.set_spacing(spacing)
+
+        # FIXME: GtkButton consturctor should take a stock_id
+        button = Gtk.Button.new_from_stock(Gtk.STOCK_OK)
+        bbox.add(button)
+
+        button = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
+        bbox.add(button)
+
+        button = Gtk.Button.new_from_stock(Gtk.STOCK_HELP)
+        bbox.add(button)
+
+        return frame
+
+
+def main(demoapp=None):
+    ButtonBoxApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/clipboard.py b/examples/demo/demos/clipboard.py
new file mode 100644 (file)
index 0000000..5a88828
--- /dev/null
@@ -0,0 +1,228 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Clipboard"
+description = """
+GtkClipboard is used for clipboard handling. This demo shows how to
+copy and paste text to and from the clipboard.
+
+It also shows how to transfer images via the clipboard or via
+drag-and-drop, and how to make clipboard contents persist after
+the application exits. Clipboard persistence requires a clipboard
+manager to run.
+"""
+
+
+from gi.repository import Gtk, Gdk
+
+
+class ClipboardApp:
+    def __init__(self):
+        self.window = Gtk.Window()
+        self.window.set_title('Clipboard demo')
+        self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+        vbox = Gtk.VBox(homogeneous=False, spacing=0)
+        vbox.set_border_width(8)
+        self.window.add(vbox)
+
+        label = Gtk.Label(label='"Copy" will copy the text\nin the entry to the clipboard')
+        vbox.pack_start(label, False, False, 0)
+
+        hbox = Gtk.HBox(homogeneous=False, spacing=4)
+        hbox.set_border_width(8)
+        vbox.pack_start(hbox, False, False, 0)
+
+        # create first entry
+        entry = Gtk.Entry()
+        hbox.pack_start(entry, True, True, 0)
+
+        # create button
+        button = Gtk.Button.new_from_stock(Gtk.STOCK_COPY)
+        hbox.pack_start(button, False, False, 0)
+        button.connect('clicked', self.copy_button_clicked, entry)
+
+        label = Gtk.Label(label='"Paste" will paste the text from the clipboard to the entry')
+        vbox.pack_start(label, False, False, 0)
+
+        hbox = Gtk.HBox(homogeneous=False, spacing=4)
+        hbox.set_border_width(8)
+        vbox.pack_start(hbox, False, False, 0)
+
+        # create secondary entry
+        entry = Gtk.Entry()
+        hbox.pack_start(entry, True, True, 0)
+        # create button
+        button = Gtk.Button.new_from_stock(Gtk.STOCK_PASTE)
+        hbox.pack_start(button, False, False, 0)
+        button.connect('clicked', self.paste_button_clicked, entry)
+
+        label = Gtk.Label(label='Images can be transferred via the clipboard, too')
+        vbox.pack_start(label, False, False, 0)
+
+        hbox = Gtk.HBox(homogeneous=False, spacing=4)
+        hbox.set_border_width(8)
+        vbox.pack_start(hbox, False, False, 0)
+
+        # create the first image
+        image = Gtk.Image(stock=Gtk.STOCK_DIALOG_WARNING,
+                          icon_size=Gtk.IconSize.BUTTON)
+
+        ebox = Gtk.EventBox()
+        ebox.add(image)
+        hbox.add(ebox)
+
+        # make ebox a drag source
+        ebox.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
+                             None, Gdk.DragAction.COPY)
+        ebox.drag_source_add_image_targets()
+        ebox.connect('drag-begin', self.drag_begin, image)
+        ebox.connect('drag-data-get', self.drag_data_get, image)
+
+        # accept drops on ebox
+        ebox.drag_dest_set(Gtk.DestDefaults.ALL,
+                           None, Gdk.DragAction.COPY)
+        ebox.drag_dest_add_image_targets()
+        ebox.connect('drag-data-received', self.drag_data_received, image)
+
+        # context menu on ebox
+        ebox.connect('button-press-event', self.button_press, image)
+
+        # create the second image
+        image = Gtk.Image(stock=Gtk.STOCK_STOP,
+                          icon_size=Gtk.IconSize.BUTTON)
+
+        ebox = Gtk.EventBox()
+        ebox.add(image)
+        hbox.add(ebox)
+
+        # make ebox a drag source
+        ebox.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
+                             None, Gdk.DragAction.COPY)
+        ebox.drag_source_add_image_targets()
+        ebox.connect('drag-begin', self.drag_begin, image)
+        ebox.connect('drag-data-get', self.drag_data_get, image)
+
+        # accept drops on ebox
+        ebox.drag_dest_set(Gtk.DestDefaults.ALL,
+                           None, Gdk.DragAction.COPY)
+        ebox.drag_dest_add_image_targets()
+        ebox.connect('drag-data-received', self.drag_data_received, image)
+
+        # context menu on ebox
+        ebox.connect('button-press-event', self.button_press, image)
+
+        # tell the clipboard manager to make data persistent
+        # FIXME: Allow sending strings a Atoms and convert in PyGI
+        atom = Gdk.atom_intern('CLIPBOARD', True)
+        clipboard = Gtk.Clipboard.get(atom)
+        clipboard.set_can_store(None)
+
+        self.window.show_all()
+
+    def copy_button_clicked(self, button, entry):
+        # get the default clipboard
+        atom = Gdk.atom_intern('CLIPBOARD', True)
+        clipboard = entry.get_clipboard(atom)
+
+        # set the clipboard's text
+        # FIXME: don't require passing length argument
+        clipboard.set_text(entry.get_text(), -1)
+
+    def paste_received(self, clipboard, text, entry):
+        if text is not None:
+            entry.set_text(text)
+
+    def paste_button_clicked(self, button, entry):
+        # get the default clipboard
+        atom = Gdk.atom_intern('CLIPBOARD', True)
+        clipboard = entry.get_clipboard(atom)
+
+        # set the clipboard's text
+        clipboard.request_text(self.paste_received, entry)
+
+    def get_image_pixbuf(self, image):
+        # FIXME: We should hide storage types in an override
+        storage_type = image.get_storage_type()
+        if storage_type == Gtk.ImageType.PIXBUF:
+            return image.get_pixbuf()
+        elif storage_type == Gtk.ImageType.STOCK:
+            (stock_id, size) = image.get_stock()
+            return image.render_icon(stock_id, size, None)
+
+        return None
+
+    def drag_begin(self, widget, context, data):
+        pixbuf = self.get_image_pixbuf(data)
+        Gtk.drag_set_icon_pixbuf(context, pixbuf, -2, -2)
+
+    def drag_data_get(self, widget, context, selection_data, info, time, data):
+        pixbuf = self.get_image_pixbuf(data)
+        selection_data.set_pixbuf(pixbuf)
+
+    def drag_data_received(self, widget, context, x, y, selection_data, info, time, data):
+        if selection_data.get_length() > 0:
+            pixbuf = selection_data.get_pixbuf()
+            data.set_from_pixbuf(pixbuf)
+
+    def copy_image(self, item, data):
+        # get the default clipboard
+        atom = Gdk.atom_intern('CLIPBOARD', True)
+        clipboard = Gtk.Clipboard.get(atom)
+        pixbuf = self.get_image_pixbuf(data)
+
+        clipboard.set_image(pixbuf)
+
+    def paste_image(self, item, data):
+        # get the default clipboard
+        atom = Gdk.atom_intern('CLIPBOARD', True)
+        clipboard = Gtk.Clipboard.get(atom)
+        pixbuf = clipboard.wait_for_image()
+
+        if pixbuf is not None:
+            data.set_from_pixbuf(pixbuf)
+
+    def button_press(self, widget, event, data):
+        if event.button != 3:
+            return False
+
+        self.menu = Gtk.Menu()
+
+        item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_COPY, None)
+        item.connect('activate', self.copy_image, data)
+        item.show()
+        self.menu.append(item)
+
+        item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_PASTE, None)
+        item.connect('activate', self.paste_image, data)
+        item.show()
+        self.menu.append(item)
+
+        self.menu.popup(None, None, None, None, event.button, event.time)
+
+
+def main(demoapp=None):
+    ClipboardApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/colorselector.py b/examples/demo/demos/colorselector.py
new file mode 100644 (file)
index 0000000..d05ca52
--- /dev/null
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Color Selector"
+description = """
+ GtkColorSelection lets the user choose a color. GtkColorSelectionDialog is
+ a prebuilt dialog containing a GtkColorSelection.
+ """
+
+
+from gi.repository import Gtk, Gdk
+
+
+class ColorSelectorApp:
+    def __init__(self):
+        # FIXME: we should allow Gdk.Color to be allocated without parameters
+        #        Also color doesn't seem to work
+        self.color = Gdk.RGBA()
+        self.color.red = 0
+        self.color.blue = 1
+        self.color.green = 0
+        self.color.alpha = 1
+
+        self.window = Gtk.Window()
+        self.window.set_title('Color Selection')
+        self.window.set_border_width(8)
+        self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+        vbox = Gtk.VBox(homogeneous=False,
+                        spacing=8)
+        vbox.set_border_width(8)
+        self.window.add(vbox)
+
+        # create color swatch area
+        frame = Gtk.Frame()
+        frame.set_shadow_type(Gtk.ShadowType.IN)
+        vbox.pack_start(frame, True, True, 0)
+
+        self.da = Gtk.DrawingArea()
+        self.da.connect('draw', self.draw_cb)
+
+        # set a minimum size
+        self.da.set_size_request(200, 200)
+        # set the color
+        self.da.override_background_color(0, self.color)
+        frame.add(self.da)
+
+        alignment = Gtk.Alignment(xalign=1.0,
+                                  yalign=0.5,
+                                  xscale=0.0,
+                                  yscale=0.0)
+
+        button = Gtk.Button(label='_Change the above color',
+                            use_underline=True)
+        alignment.add(button)
+        vbox.pack_start(alignment, False, False, 0)
+
+        button.connect('clicked', self.change_color_cb)
+
+        self.window.show_all()
+
+    def draw_cb(self, widget, cairo_ctx):
+        style = widget.get_style_context()
+        bg_color = style.get_background_color(0)
+        Gdk.cairo_set_source_rgba(cairo_ctx, bg_color)
+        cairo_ctx.paint()
+
+        return True
+
+    def change_color_cb(self, button):
+        dialog = Gtk.ColorSelectionDialog(title='Changing color')
+        dialog.set_transient_for(self.window)
+
+        colorsel = dialog.get_color_selection()
+        colorsel.set_previous_rgba(self.color)
+        colorsel.set_current_rgba(self.color)
+        colorsel.set_has_palette(True)
+
+        response = dialog.run()
+
+        if response == Gtk.ResponseType.OK:
+            self.color = colorsel.get_current_rgba()
+            self.da.override_background_color(0, self.color)
+
+        dialog.destroy()
+
+
+def main(demoapp=None):
+    ColorSelectorApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/combobox.py b/examples/demo/demos/combobox.py
new file mode 100644 (file)
index 0000000..36034ba
--- /dev/null
@@ -0,0 +1,319 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Combo boxes"
+description = """
+The ComboBox widget allows to select one option out of a list.
+The ComboBoxEntry additionally allows the user to enter a value
+that is not in the list of options.
+
+How the options are displayed is controlled by cell renderers.
+ """
+
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, GObject
+
+
+(PIXBUF_COL,
+ TEXT_COL) = range(2)
+
+
+class MaskEntry(Gtk.Entry):
+    __gtype_name__ = 'MaskEntry'
+
+    def __init__(self, mask=None):
+        self.mask = mask
+        super(MaskEntry, self).__init__()
+
+        self.connect('changed', self.changed_cb)
+
+        self.error_color = Gdk.RGBA()
+        self.error_color.red = 1.0
+        self.error_color.green = 0.9
+        self.error_color_blue = 0.9
+        self.error_color.alpha = 1.0
+
+        # workaround since override_color doesn't accept None yet
+        style_ctx = self.get_style_context()
+        self.normal_color = style_ctx.get_color(0)
+
+    def set_background(self):
+        if self.mask:
+            if not GLib.regex_match_simple(self.mask,
+                                           self.get_text(), 0, 0):
+                self.override_color(0, self.error_color)
+                return
+
+        self.override_color(0, self.normal_color)
+
+    def changed_cb(self, entry):
+        self.set_background()
+
+
+class ComboboxApp:
+    def __init__(self, demoapp):
+        self.demoapp = demoapp
+
+        self.window = Gtk.Window()
+        self.window.set_title('Combo boxes')
+        self.window.set_border_width(10)
+        self.window.connect('destroy', lambda w: Gtk.main_quit())
+
+        vbox = Gtk.VBox(homogeneous=False, spacing=2)
+        self.window.add(vbox)
+
+        frame = Gtk.Frame(label='Some stock icons')
+        vbox.pack_start(frame, False, False, 0)
+
+        box = Gtk.VBox(homogeneous=False, spacing=0)
+        box.set_border_width(5)
+        frame.add(box)
+
+        model = self.create_stock_icon_store()
+        combo = Gtk.ComboBox(model=model)
+        box.add(combo)
+
+        renderer = Gtk.CellRendererPixbuf()
+        combo.pack_start(renderer, False)
+
+        # FIXME: override set_attributes
+        combo.add_attribute(renderer, 'pixbuf', PIXBUF_COL)
+        combo.set_cell_data_func(renderer, self.set_sensitive, None)
+
+        renderer = Gtk.CellRendererText()
+        combo.pack_start(renderer, True)
+        combo.add_attribute(renderer, 'text', TEXT_COL)
+        combo.set_cell_data_func(renderer, self.set_sensitive, None)
+
+        combo.set_row_separator_func(self.is_separator, None)
+        combo.set_active(0)
+
+        # a combobox demonstrating trees
+        frame = Gtk.Frame(label='Where are we ?')
+        vbox.pack_start(frame, False, False, 0)
+
+        box = Gtk.VBox(homogeneous=False, spacing=0)
+        box.set_border_width(5)
+        frame.add(box)
+
+        model = self.create_capital_store()
+        combo = Gtk.ComboBox(model=model)
+        box.add(combo)
+
+        renderer = Gtk.CellRendererText()
+        combo.pack_start(renderer, True)
+        combo.add_attribute(renderer, 'text', 0)
+        combo.set_cell_data_func(renderer, self.is_capital_sensistive, None)
+
+        # FIXME: make new_from_indices work
+        #        make constructor take list or string of indices
+        path = Gtk.TreePath.new_from_string('0:8')
+        treeiter = model.get_iter(path)
+        combo.set_active_iter(treeiter)
+
+        # A GtkComboBoxEntry with validation.
+
+        frame = Gtk.Frame(label='Editable')
+        vbox.pack_start(frame, False, False, 0)
+
+        box = Gtk.VBox(homogeneous=False, spacing=0)
+        box.set_border_width(5)
+        frame.add(box)
+
+        combo = Gtk.ComboBoxText.new_with_entry()
+        self.fill_combo_entry(combo)
+        box.add(combo)
+
+        entry = MaskEntry(mask='^([0-9]*|One|Two|2\302\275|Three)$')
+
+        Gtk.Container.remove(combo, combo.get_child())
+        combo.add(entry)
+
+        # A combobox with string IDs
+
+        frame = Gtk.Frame(label='String IDs')
+        vbox.pack_start(frame, False, False, 0)
+
+        box = Gtk.VBox(homogeneous=False, spacing=0)
+        box.set_border_width(5)
+        frame.add(box)
+
+        # FIXME: model is not setup when constructing Gtk.ComboBoxText()
+        #        so we call new() - Gtk should fix this to setup the model
+        #        in __init__, not in the constructor
+        combo = Gtk.ComboBoxText.new()
+        combo.append('never', 'Not visible')
+        combo.append('when-active', 'Visible when active')
+        combo.append('always', 'Always visible')
+        box.add(combo)
+
+        entry = Gtk.Entry()
+
+        combo.bind_property('active-id',
+                            entry, 'text',
+                            GObject.BindingFlags.BIDIRECTIONAL)
+
+        box.add(entry)
+        self.window.show_all()
+
+    def strip_underscore(self, s):
+        return s.replace('_', '')
+
+    def create_stock_icon_store(self):
+        stock_id = (Gtk.STOCK_DIALOG_WARNING,
+                    Gtk.STOCK_STOP,
+                    Gtk.STOCK_NEW,
+                    Gtk.STOCK_CLEAR,
+                    None,
+                    Gtk.STOCK_OPEN)
+
+        cellview = Gtk.CellView()
+        store = Gtk.ListStore(GdkPixbuf.Pixbuf, str)
+
+        for id in stock_id:
+            if id is not None:
+                pixbuf = cellview.render_icon(id, Gtk.IconSize.BUTTON, None)
+                item = Gtk.stock_lookup(id)
+                label = self.strip_underscore(item.label)
+                store.append((pixbuf, label))
+            else:
+                store.append((None, 'separator'))
+
+        return store
+
+    def set_sensitive(self, cell_layout, cell, tree_model, treeiter, data):
+        """
+        A GtkCellLayoutDataFunc that demonstrates how one can control
+        sensitivity of rows. This particular function does nothing
+        useful and just makes the second row insensitive.
+        """
+
+        path = tree_model.get_path(treeiter)
+        indices = path.get_indices()
+
+        sensitive = not(indices[0] == 1)
+
+        cell.set_property('sensitive', sensitive)
+
+    def is_separator(self, model, treeiter, data):
+        """
+        A GtkTreeViewRowSeparatorFunc that demonstrates how rows can be
+        rendered as separators. This particular function does nothing
+        useful and just turns the fourth row into a separator.
+        """
+
+        path = model.get_path(treeiter)
+
+        indices = path.get_indices()
+        result = (indices[0] == 4)
+
+        return result
+
+    def create_capital_store(self):
+        capitals = (
+            {'group': 'A - B', 'capital': None},
+            {'group': None, 'capital': 'Albany'},
+            {'group': None, 'capital': 'Annapolis'},
+            {'group': None, 'capital': 'Atlanta'},
+            {'group': None, 'capital': 'Augusta'},
+            {'group': None, 'capital': 'Austin'},
+            {'group': None, 'capital': 'Baton Rouge'},
+            {'group': None, 'capital': 'Bismarck'},
+            {'group': None, 'capital': 'Boise'},
+            {'group': None, 'capital': 'Boston'},
+            {'group': 'C - D', 'capital': None},
+            {'group': None, 'capital': 'Carson City'},
+            {'group': None, 'capital': 'Charleston'},
+            {'group': None, 'capital': 'Cheyeene'},
+            {'group': None, 'capital': 'Columbia'},
+            {'group': None, 'capital': 'Columbus'},
+            {'group': None, 'capital': 'Concord'},
+            {'group': None, 'capital': 'Denver'},
+            {'group': None, 'capital': 'Des Moines'},
+            {'group': None, 'capital': 'Dover'},
+            {'group': 'E - J', 'capital': None},
+            {'group': None, 'capital': 'Frankfort'},
+            {'group': None, 'capital': 'Harrisburg'},
+            {'group': None, 'capital': 'Hartford'},
+            {'group': None, 'capital': 'Helena'},
+            {'group': None, 'capital': 'Honolulu'},
+            {'group': None, 'capital': 'Indianapolis'},
+            {'group': None, 'capital': 'Jackson'},
+            {'group': None, 'capital': 'Jefferson City'},
+            {'group': None, 'capital': 'Juneau'},
+            {'group': 'K - O', 'capital': None},
+            {'group': None, 'capital': 'Lansing'},
+            {'group': None, 'capital': 'Lincon'},
+            {'group': None, 'capital': 'Little Rock'},
+            {'group': None, 'capital': 'Madison'},
+            {'group': None, 'capital': 'Montgomery'},
+            {'group': None, 'capital': 'Montpelier'},
+            {'group': None, 'capital': 'Nashville'},
+            {'group': None, 'capital': 'Oklahoma City'},
+            {'group': None, 'capital': 'Olympia'},
+            {'group': 'P - S', 'capital': None},
+            {'group': None, 'capital': 'Phoenix'},
+            {'group': None, 'capital': 'Pierre'},
+            {'group': None, 'capital': 'Providence'},
+            {'group': None, 'capital': 'Raleigh'},
+            {'group': None, 'capital': 'Richmond'},
+            {'group': None, 'capital': 'Sacramento'},
+            {'group': None, 'capital': 'Salem'},
+            {'group': None, 'capital': 'Salt Lake City'},
+            {'group': None, 'capital': 'Santa Fe'},
+            {'group': None, 'capital': 'Springfield'},
+            {'group': None, 'capital': 'St. Paul'},
+            {'group': 'T - Z', 'capital': None},
+            {'group': None, 'capital': 'Tallahassee'},
+            {'group': None, 'capital': 'Topeka'},
+            {'group': None, 'capital': 'Trenton'}
+        )
+
+        parent = None
+
+        store = Gtk.TreeStore(str)
+
+        for item in capitals:
+            if item['group']:
+                parent = store.append(None, (item['group'],))
+            elif item['capital']:
+                store.append(parent, (item['capital'],))
+
+        return store
+
+    def is_capital_sensistive(self, cell_layout, cell, tree_model, treeiter, data):
+        sensitive = not tree_model.iter_has_child(treeiter)
+        cell.set_property('sensitive', sensitive)
+
+    def fill_combo_entry(self, entry):
+        entry.append_text('One')
+        entry.append_text('Two')
+        entry.append_text('2\302\275')
+        entry.append_text('Three')
+
+
+def main(demoapp=None):
+    ComboboxApp(demoapp)
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/data/alphatest.png b/examples/demo/demos/data/alphatest.png
new file mode 100644 (file)
index 0000000..eb5885f
Binary files /dev/null and b/examples/demo/demos/data/alphatest.png differ
diff --git a/examples/demo/demos/data/apple-red.png b/examples/demo/demos/data/apple-red.png
new file mode 100644 (file)
index 0000000..b0a24e9
Binary files /dev/null and b/examples/demo/demos/data/apple-red.png differ
diff --git a/examples/demo/demos/data/background.jpg b/examples/demo/demos/data/background.jpg
new file mode 100644 (file)
index 0000000..86c006a
Binary files /dev/null and b/examples/demo/demos/data/background.jpg differ
diff --git a/examples/demo/demos/data/brick.png b/examples/demo/demos/data/brick.png
new file mode 100644 (file)
index 0000000..d413cd2
Binary files /dev/null and b/examples/demo/demos/data/brick.png differ
diff --git a/examples/demo/demos/data/brick2.png b/examples/demo/demos/data/brick2.png
new file mode 100644 (file)
index 0000000..cfcd079
Binary files /dev/null and b/examples/demo/demos/data/brick2.png differ
diff --git a/examples/demo/demos/data/css_accordion.css b/examples/demo/demos/data/css_accordion.css
new file mode 100644 (file)
index 0000000..a243427
--- /dev/null
@@ -0,0 +1,52 @@
+@import url("resource://css_accordion/reset.css");
+
+* {
+    transition-property: color, background-color, border-color, background-image, padding, border-width;
+    transition-duration: 1s;
+
+    font: Sans 20px;
+}
+
+GtkWindow {
+    background: linear-gradient(153deg, #151515, #151515 5px, transparent 5px) 0 0,
+                linear-gradient(333deg, #151515, #151515 5px, transparent 5px) 10px 5px,
+                linear-gradient(153deg, #222, #222 5px, transparent 5px) 0 5px,
+                linear-gradient(333deg, #222, #222 5px, transparent 5px) 10px 10px,
+                linear-gradient(90deg, #1b1b1b, #1b1b1b 10px, transparent 10px),
+                linear-gradient(#1d1d1d, #1d1d1d 25%, #1a1a1a 25%, #1a1a1a 50%, transparent 50%, transparent 75%, #242424 75%, #242424);
+    background-color: #131313;
+    background-size: 20px 20px;
+}
+
+.button {
+    color: black;
+    background-color: #bbb;
+    border-style: solid;
+    border-width: 2px 0 2px 2px;
+    border-color: #333;
+
+    padding: 12px 4px;
+}
+
+.button:first-child {
+    border-radius: 5px 0 0 5px;
+}
+
+.button:last-child {
+    border-radius: 0 5px 5px 0;
+    border-width: 2px;
+}
+
+.button:hover {
+    padding: 12px 48px;
+    background-color: #4870bc;
+}
+
+.button *:hover {
+    color: white;
+}
+
+.button:hover:active,
+.button:active {
+    background-color: #993401;
+}
diff --git a/examples/demo/demos/data/css_basics.css b/examples/demo/demos/data/css_basics.css
new file mode 100644 (file)
index 0000000..62dba7a
--- /dev/null
@@ -0,0 +1,22 @@
+/* You can edit the text in this window to change the
+ * appearance of this Window.
+ * Be careful, if you screw it up, nothing might be visible
+ * anymore. :)
+ */
+
+/* This CSS resets all properties to their defaults values
+ *    and overrides all user settings and the theme in use */
+@import url("resource://css_basics/reset.css");
+
+/* Set a very futuristic style by default */
+* {
+  color: green;
+  font-family: Monospace;
+  border: 1px solid;
+}
+
+/* Make sure selections are visible */
+:selected {
+  background-color: darkGreen;
+  color: black;
+}
diff --git a/examples/demo/demos/data/css_multiplebgs.css b/examples/demo/demos/data/css_multiplebgs.css
new file mode 100644 (file)
index 0000000..eb9d4d6
--- /dev/null
@@ -0,0 +1,136 @@
+/* You can edit the text in this window to change the
+ * appearance of this Window.
+ * Be careful, if you screw it up, nothing might be visible
+ * anymore. :)
+ */
+
+/* This CSS resets all properties to their defaults values
+ *    and overrides all user settings and the theme in use */
+@import url("resource://css_multiplebgs/reset.css");
+@import url("resource://css_multiplebgs/cssview.css");
+
+#canvas {
+    transition-property: background-color, background-image;
+    transition-duration: 0.5s;
+
+    background-color: #4870bc;
+}
+
+/* The gradients below are adapted versions of Lea Verou's CSS3 patterns,
+ * licensed under the MIT license:
+ * Copyright (c) 2011 Lea Verou, http://lea.verou.me/
+ *
+ * See https://github.com/LeaVerou/CSS3-Patterns-Gallery
+ */
+
+/**********
+ * Bricks *
+ **********/
+/*
+@define-color brick_hi #d42;
+@define-color brick_lo #b42;
+@define-color brick_hi_backdrop #888;
+@define-color brick_lo_backdrop #999;
+
+#canvas {
+    background-color: #999;
+    background-image: linear-gradient(205deg, @brick_lo, @brick_lo 23px, transparent 23px),
+                      linear-gradient(25deg, @brick_hi, @brick_hi 23px, transparent 23px),
+                      linear-gradient(205deg, @brick_lo, @brick_lo 23px, transparent 23px),
+                      linear-gradient(25deg, @brick_hi, @brick_hi 23px, transparent 23px);
+    background-size: 58px 58px;
+    background-position: 0px 6px, 4px 31px, 29px 35px, 34px 2px;
+}
+
+#canvas:backdrop {
+    background-color: #444;
+    background-image: linear-gradient(205deg, @brick_lo_backdrop, @brick_lo_backdrop 23px, transparent 23px),
+                      linear-gradient(25deg, @brick_hi_backdrop, @brick_hi_backdrop 23px, transparent 23px),
+                     linear-gradient(205deg, @brick_lo_backdrop, @brick_lo_backdrop 23px, transparent 23px),
+                     linear-gradient(25deg, @brick_hi_backdrop, @brick_hi_backdrop 23px, transparent 23px);
+    background-size: 58px 58px;
+    background-position: 0px 6px, 4px 31px, 29px 35px, 34px 2px;
+}
+*/
+
+/*
+#bricks-button {
+    background-color: #eef;
+    background-image: -gtk-scaled(url('resource://css_multiplebgs/brick.png'),url('resource://css_multiplebgs/brick2.png'));
+    background-repeat: no-repeat;
+    background-position: center;
+}
+
+*/
+/**********
+ * Tartan *
+ **********/
+/*
+@define-color tartan_bg #662e2c;
+@define-color tartan_bg_backdrop #333;
+
+#canvas {
+    background-color: @tartan_bg;
+    background-image: repeating-linear-gradient(transparent, transparent 50px, rgba(0,0,0,.4) 50px,
+                                                rgba(0,0,0,.4) 53px, transparent 53px, transparent 63px,
+                                                rgba(0,0,0,.4) 63px, rgba(0,0,0,.4) 66px, transparent 66px,
+                                                transparent 116px, rgba(0,0,0,.5) 116px, rgba(0,0,0,.5) 166px,
+                                                rgba(255,255,255,.2) 166px, rgba(255,255,255,.2) 169px, rgba(0,0,0,.5) 169px,
+                                                rgba(0,0,0,.5) 179px, rgba(255,255,255,.2) 179px, rgba(255,255,255,.2) 182px,
+                                                rgba(0,0,0,.5) 182px, rgba(0,0,0,.5) 232px, transparent 232px),
+                      repeating-linear-gradient(90deg, transparent, transparent 50px, rgba(0,0,0,.4) 50px, rgba(0,0,0,.4) 53px,
+                                                transparent 53px, transparent 63px, rgba(0,0,0,.4) 63px, rgba(0,0,0,.4) 66px,
+                                                transparent 66px, transparent 116px, rgba(0,0,0,.5) 116px, rgba(0,0,0,.5) 166px,
+                                                rgba(255,255,255,.2) 166px, rgba(255,255,255,.2) 169px, rgba(0,0,0,.5) 169px,
+                                                rgba(0,0,0,.5) 179px, rgba(255,255,255,.2) 179px, rgba(255,255,255,.2) 182px,
+                                                rgba(0,0,0,.5) 182px, rgba(0,0,0,.5) 232px, transparent 232px),
+                      repeating-linear-gradient(-55deg, transparent, transparent 1px, rgba(0,0,0,.2) 1px, rgba(0,0,0,.2) 4px,
+                                                transparent 4px, transparent 19px, rgba(0,0,0,.2) 19px,
+                                                rgba(0,0,0,.2) 24px, transparent 24px, transparent 51px, rgba(0,0,0,.2) 51px,
+                                                rgba(0,0,0,.2) 54px, transparent 54px, transparent 74px);
+}
+
+#canvas:backdrop {
+    background-color: @tartan_bg_backdrop;
+}
+*/
+
+/***********
+ * Stripes *
+ ***********/
+
+/*
+@define-color base_bg #4870bc;
+@define-color backdrop_bg #555;
+
+#canvas {
+  background-color: @base_bg;
+  background-image: linear-gradient(to left, transparent, rgba(255,255,255,.07) 50%, transparent 50%),
+                    linear-gradient(to left, transparent, rgba(255,255,255,.13) 50%, transparent 50%),
+                    linear-gradient(to left, transparent, transparent 50%, rgba(255,255,255,.17) 50%),
+                    linear-gradient(to left, transparent, transparent 50%, rgba(255,255,255,.19) 50%);
+  background-size: 29px, 59px, 73px, 109px;
+}
+
+#canvas:backdrop {
+  background-color: @backdrop_bg;
+}
+*/
+
+/***************
+ * Lined Paper *
+ ***************/
+/*
+#canvas {
+    background-color: #fff;
+    background-image: linear-gradient(90deg, transparent 79px, alpha(#f98195, 0.40) 79px, #f98195 80px, alpha(#f98195, 0.40) 81px, transparent 81px),
+                      linear-gradient(alpha(#77c5cf, 0.60), alpha(#77c5cf, 0.60) 1px, transparent 1px);
+    background-size: 100% 36px;
+}
+
+#canvas:backdrop {
+    background-color: #f1f2f4;
+    background-image: linear-gradient(90deg, transparent 79px, alpha(#999, 0.40) 79px, #999 80px, alpha(#999, 0.40) 81px, transparent 81px),
+                      linear-gradient(alpha(#bbb, 0.60), alpha(#bbb, 0.60) 1px, transparent 1px);
+}
+*/
diff --git a/examples/demo/demos/data/cssview.css b/examples/demo/demos/data/cssview.css
new file mode 100644 (file)
index 0000000..5060c39
--- /dev/null
@@ -0,0 +1,41 @@
+/* Make the text editor has a nice style */
+.view {
+  color: #2e3436;
+  font: Monospace;
+  background-color: alpha(white, 0.30);
+}
+
+.view:selected {
+  color: white;
+  background-color: #4a90d9;
+}
+
+.scrollbar.trough,
+.scrollbars-junction {
+  background-color: alpha(white, 0.80);
+}
+
+.scrollbar.slider {
+  border-width: 3px;
+  border-style: solid;
+  border-radius: 10px;
+  border-color: transparent;
+  background-clip: padding-box;
+  background-color: #999;
+}
+
+.scrollbar.slider:prelight {
+  background-color: #555;
+}
+
+.pane-separator {
+  background-color: alpha(white, 0.80);
+  background-image: linear-gradient(transparent, transparent 1px, #999 1px, #999 4px, transparent 4px);
+  background-size: 40px auto;
+  background-repeat: no-repeat;
+  background-position: center;
+}
+
+.pane-separator:prelight {
+  background-image: linear-gradient(transparent, transparent 1px, #555 1px, #555 4px, transparent 4px);
+}
diff --git a/examples/demo/demos/data/demo.gresource b/examples/demo/demos/data/demo.gresource
new file mode 100644 (file)
index 0000000..e19d822
Binary files /dev/null and b/examples/demo/demos/data/demo.gresource differ
diff --git a/examples/demo/demos/data/demo.gresource.xml b/examples/demo/demos/data/demo.gresource.xml
new file mode 100644 (file)
index 0000000..866769f
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/css_accordion">
+    <file>css_accordion.css</file>
+    <file>reset.css</file>
+  </gresource>
+  <gresource prefix="/css_basics">
+    <file>css_basics.css</file>
+    <file>reset.css</file>
+  </gresource>
+  <gresource prefix="/css_multiplebgs">
+    <file>css_multiplebgs.css</file>
+    <file>brick.png</file>
+    <file>brick2.png</file>
+    <file>cssview.css</file>
+    <file>reset.css</file>
+  </gresource>
+</gresources>
diff --git a/examples/demo/demos/data/demo.ui b/examples/demo/demos/data/demo.ui
new file mode 100644 (file)
index 0000000..57dd232
--- /dev/null
@@ -0,0 +1,258 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<interface domain="gtk20">
+    <object class="GtkListStore" id="liststore1">
+      <columns>
+        <column type="gchararray"/>
+        <column type="gchararray"/>
+        <column type="gint"/>
+        <column type="gchararray"/>
+      </columns>
+      <data>
+        <row>
+          <col id="0" translatable="yes">John</col>
+          <col id="1" translatable="yes">Doe</col>
+          <col id="2">25</col>
+          <col id="3" translatable="yes">This is the John Doe row</col>
+        </row>
+        <row>
+          <col id="0" translatable="yes">Mary</col>
+          <col id="1" translatable="yes">Unknown</col>
+          <col id="2">50</col>
+          <col id="3" translatable="yes">This is the Mary Unknown row</col>
+        </row>
+      </data>
+    </object>
+    <object class="GtkUIManager" id="uimanager">
+        <child>
+            <object class="GtkActionGroup" id="DefaultActions">
+                <child>
+                    <object class="GtkAction" id="Copy">
+                        <property name="name">Copy</property>
+                        <property name="tooltip" translatable="yes">Copy selected object into the clipboard</property>
+                        <property name="stock_id">gtk-copy</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Cut">
+                        <property name="name">Cut</property>
+                        <property name="tooltip" translatable="yes">Cut selected object into the clipboard</property>
+                        <property name="stock_id">gtk-cut</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="EditMenu">
+                        <property name="name">EditMenu</property>
+                        <property name="label" translatable="yes">_Edit</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="FileMenu">
+                        <property name="name">FileMenu</property>
+                        <property name="label" translatable="yes">_File</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="New">
+                        <property name="name">New</property>
+                        <property name="tooltip" translatable="yes">Create a new file</property>
+                        <property name="stock_id">gtk-new</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Open">
+                        <property name="name">Open</property>
+                        <property name="tooltip" translatable="yes">Open a file</property>
+                        <property name="stock_id">gtk-open</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Paste">
+                        <property name="name">Paste</property>
+                        <property name="tooltip" translatable="yes">Paste object from the Clipboard</property>
+                        <property name="stock_id">gtk-paste</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Quit">
+                        <property name="name">Quit</property>
+                        <property name="tooltip" translatable="yes">Quit the program</property>
+                        <property name="stock_id">gtk-quit</property>
+                        <signal handler="quit_activate" name="activate"/>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="Save">
+                        <property name="name">Save</property>
+                        <property name="is_important">True</property>
+                        <property name="tooltip" translatable="yes">Save a file</property>
+                        <property name="stock_id">gtk-save</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="SaveAs">
+                        <property name="name">SaveAs</property>
+                        <property name="tooltip" translatable="yes">Save with a different name</property>
+                        <property name="stock_id">gtk-save-as</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="HelpMenu">
+                        <property name="name">HelpMenu</property>
+                        <property name="label" translatable="yes">_Help</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkAction" id="About">
+                        <property name="name">About</property>
+                        <property name="stock_id">gtk-about</property>
+                        <signal handler="about_activate" name="activate"/>
+                    </object>
+                    <accelerator key="F1"/>
+                </child>
+            </object>
+        </child>
+        <ui>
+          <menubar name="menubar1">
+            <menu action="FileMenu" name="FileMenu">
+              <menuitem action="New" name="New"/>
+              <menuitem action="Open" name="Open"/>
+              <menuitem action="Save" name="Save"/>
+              <menuitem action="SaveAs" name="SaveAs"/>
+              <separator/>
+              <menuitem action="Quit" name="Quit"/>
+            </menu>
+            <menu action="EditMenu">
+              <menuitem action="Copy" name="Copy"/>
+              <menuitem action="Cut" name="Cut"/>
+              <menuitem action="Paste" name="Paste"/>
+            </menu>
+            <menu action="HelpMenu" name="HelpMenu">
+              <menuitem action="About" name="About"/>
+            </menu>
+          </menubar>
+          <toolbar name="toolbar1">
+            <toolitem action="New" name="New"/>
+            <toolitem action="Open" name="Open"/>
+            <toolitem action="Save" name="Save"/>
+            <separator/>
+            <toolitem action="Copy" name="Copy"/>
+            <toolitem action="Cut" name="Cut"/>
+            <toolitem action="Paste" name="Paste"/>
+          </toolbar>
+        </ui>
+    </object>
+    <object class="GtkAboutDialog" id="aboutdialog1">
+        <property name="program-name" translatable="yes">GtkBuilder demo</property>
+         <accessibility>
+            <relation target="window1" type="subwindow-of"/>
+        </accessibility>
+    </object>
+    <object class="GtkWindow" id="window1">
+        <property name="default_height">250</property>
+        <property name="default_width">440</property>
+        <property name="title">GtkBuilder demo</property>
+        <child>
+            <object class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <child>
+                    <object constructor="uimanager" class="GtkMenuBar" id="menubar1">
+                        <property name="visible">True</property>
+                       <child internal-child="accessible">
+                           <object class="AtkObject" id="a11y-menubar">
+                               <property name="AtkObject::accessible-name">The menubar</property>
+                           </object>
+                       </child>
+                   </object>
+                    <packing>
+                        <property name="expand">False</property>
+                    </packing>
+                </child>
+                <child>
+                    <object constructor="uimanager" class="GtkToolbar" id="toolbar1">
+                        <property name="visible">True</property>
+                       <child internal-child="accessible">
+                           <object class="AtkObject" id="a11y-toolbar">
+                               <property name="AtkObject::accessible-name">The toolbar</property>
+                           </object>
+                       </child>
+                    </object>
+                    <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                    </packing>
+                </child>
+                <child>
+                    <object class="GtkScrolledWindow" id="scrolledwindow1">
+                      <property name="hscrollbar_policy">automatic</property>
+                      <property name="shadow_type">in</property>
+                      <property name="visible">True</property>
+                      <property name="vscrollbar_policy">automatic</property>
+                      <child>
+                        <object class="GtkTreeView" id="treeview1">
+                          <property name="visible">True</property>
+                          <property name="model">liststore1</property>
+                          <property name="tooltip-column">3</property>
+                         <child internal-child="accessible">
+                             <object class="AtkObject" id="a11y-treeview">
+                                 <property name="AtkObject::accessible-name">Name list</property>
+                                  <property name="AtkObject::accessible-description">
+                                    A list of person with name, surname and age columns
+                                  </property>
+                             </object>
+                         </child>
+                          <child>
+                            <object class="GtkTreeViewColumn" id="column1">
+                              <property name="title">Name</property>
+                              <child>
+                                <object class="GtkCellRendererText" id="renderer1"/>
+                                <attributes>
+                                  <attribute name="text">0</attribute>
+                                </attributes>
+                              </child>
+                            </object>
+                          </child>
+                          <child>
+                            <object class="GtkTreeViewColumn" id="column2">
+                              <property name="title">Surname</property>
+                              <child>
+                                <object class="GtkCellRendererText" id="renderer2"/>
+                                <attributes>
+                                  <attribute name="text">1</attribute>
+                                </attributes>
+                              </child>
+                            </object>
+                          </child>
+                          <child>
+                            <object class="GtkTreeViewColumn" id="column3">
+                              <property name="title">Age</property>
+                              <child>
+                                <object class="GtkCellRendererText" id="renderer3"/>
+                                <attributes>
+                                  <attribute name="text">2</attribute>
+                                </attributes>
+                              </child>
+                            </object>
+                          </child>
+                        </object>
+                      </child>
+                   <accessibility>
+                       <action action_name="move-cursor" description="Move the cursor to select another person."/>
+                   </accessibility>
+                    </object>
+                    <packing>
+                        <property name="position">2</property>
+                    </packing>
+                </child>
+                <child>
+                    <object class="GtkStatusbar" id="statusbar1">
+                        <property name="visible">True</property>
+                    </object>
+                    <packing>
+                        <property name="expand">False</property>
+                        <property name="position">3</property>
+                    </packing>
+                </child>
+            </object>
+        </child>
+    </object>
+</interface>
diff --git a/examples/demo/demos/data/floppybuddy.gif b/examples/demo/demos/data/floppybuddy.gif
new file mode 100644 (file)
index 0000000..ac986c8
Binary files /dev/null and b/examples/demo/demos/data/floppybuddy.gif differ
diff --git a/examples/demo/demos/data/gnome-applets.png b/examples/demo/demos/data/gnome-applets.png
new file mode 100644 (file)
index 0000000..8d3549e
Binary files /dev/null and b/examples/demo/demos/data/gnome-applets.png differ
diff --git a/examples/demo/demos/data/gnome-calendar.png b/examples/demo/demos/data/gnome-calendar.png
new file mode 100644 (file)
index 0000000..889f329
Binary files /dev/null and b/examples/demo/demos/data/gnome-calendar.png differ
diff --git a/examples/demo/demos/data/gnome-foot.png b/examples/demo/demos/data/gnome-foot.png
new file mode 100644 (file)
index 0000000..0476658
Binary files /dev/null and b/examples/demo/demos/data/gnome-foot.png differ
diff --git a/examples/demo/demos/data/gnome-fs-directory.png b/examples/demo/demos/data/gnome-fs-directory.png
new file mode 100644 (file)
index 0000000..05921a6
Binary files /dev/null and b/examples/demo/demos/data/gnome-fs-directory.png differ
diff --git a/examples/demo/demos/data/gnome-fs-regular.png b/examples/demo/demos/data/gnome-fs-regular.png
new file mode 100644 (file)
index 0000000..0f5019c
Binary files /dev/null and b/examples/demo/demos/data/gnome-fs-regular.png differ
diff --git a/examples/demo/demos/data/gnome-gimp.png b/examples/demo/demos/data/gnome-gimp.png
new file mode 100644 (file)
index 0000000..f6bbc6d
Binary files /dev/null and b/examples/demo/demos/data/gnome-gimp.png differ
diff --git a/examples/demo/demos/data/gnome-gmush.png b/examples/demo/demos/data/gnome-gmush.png
new file mode 100644 (file)
index 0000000..0a4b0d0
Binary files /dev/null and b/examples/demo/demos/data/gnome-gmush.png differ
diff --git a/examples/demo/demos/data/gnome-gsame.png b/examples/demo/demos/data/gnome-gsame.png
new file mode 100644 (file)
index 0000000..01c0611
Binary files /dev/null and b/examples/demo/demos/data/gnome-gsame.png differ
diff --git a/examples/demo/demos/data/gnu-keys.png b/examples/demo/demos/data/gnu-keys.png
new file mode 100644 (file)
index 0000000..58a3377
Binary files /dev/null and b/examples/demo/demos/data/gnu-keys.png differ
diff --git a/examples/demo/demos/data/gtk-logo-rgb.gif b/examples/demo/demos/data/gtk-logo-rgb.gif
new file mode 100644 (file)
index 0000000..63c622b
Binary files /dev/null and b/examples/demo/demos/data/gtk-logo-rgb.gif differ
diff --git a/examples/demo/demos/data/reset.css b/examples/demo/demos/data/reset.css
new file mode 100644 (file)
index 0000000..1c27a8e
--- /dev/null
@@ -0,0 +1,68 @@
+/* @import this colorsheet to get the default values for every property.
+ * This is useful when writing special CSS tests that should not be
+ * inluenced by themes - not even the default ones.
+ * Keep in mind that the output will be very ugly and not look like
+ * anything GTK.
+ * Also, when adding new style properties, please add them here.
+ */
+
+* {
+  color: inherit;
+  font-size: inherit;
+  background-color: initial;
+  font-family: inherit;
+  font-style: inherit;
+  font-variant: inherit;
+  font-weight: inherit;
+  text-shadow: inherit;
+  icon-shadow: inherit;
+  box-shadow: initial;
+  margin-top: initial;
+  margin-left: initial;
+  margin-bottom: initial;
+  margin-right: initial;
+  padding-top: initial;
+  padding-left: initial;
+  padding-bottom: initial;
+  padding-right: initial;
+  border-top-style: initial;
+  border-top-width: initial;
+  border-left-style: initial;
+  border-left-width: initial;
+  border-bottom-style: initial;
+  border-bottom-width: initial;
+  border-right-style: initial;
+  border-right-width: initial;
+  border-top-left-radius: initial;
+  border-top-right-radius: initial;
+  border-bottom-right-radius: initial;
+  border-bottom-left-radius: initial;
+  outline-style: initial;
+  outline-width: initial;
+  outline-offset: initial;
+  background-clip: initial;
+  background-origin: initial;
+  background-size: initial;
+  background-position: initial;
+  border-top-color: initial;
+  border-right-color: initial;
+  border-bottom-color: initial;
+  border-left-color: initial;
+  outline-color:  initial;
+  background-repeat: initial;
+  background-image: initial;
+  border-image-source: initial;
+  border-image-repeat: initial;
+  border-image-slice: initial;
+  border-image-width: initial;
+  transition-property: initial;
+  transition-duration: initial;
+  transition-timing-function: initial;
+  transition-delay: initial;
+  engine: initial;
+  gtk-key-bindings: initial;
+
+  -GtkWidget-focus-line-width: 0;
+  -GtkWidget-focus-padding: 0;
+  -GtkNotebook-initial-gap: 0;
+}
diff --git a/examples/demo/demos/dialogs.py b/examples/demo/demos/dialogs.py
new file mode 100644 (file)
index 0000000..47d6822
--- /dev/null
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Dialog and Message Boxes"
+description = """
+Dialog widgets are used to pop up a transient window for user feedback.
+"""
+
+from gi.repository import Gtk
+
+
+class DialogsApp:
+    def __init__(self):
+        self.dialog_counter = 1
+
+        self.window = Gtk.Window(title="Dialogs")
+        self.window.set_border_width(8)
+        self.window.connect('destroy', Gtk.main_quit)
+
+        frame = Gtk.Frame(label="Dialogs")
+        self.window.add(frame)
+
+        vbox = Gtk.VBox(spacing=8)
+        vbox.set_border_width(8)
+        frame.add(vbox)
+
+        # Standard message dialog
+        hbox = Gtk.HBox(spacing=8)
+        vbox.pack_start(hbox, False, False, 0)
+        button = Gtk.Button.new_with_mnemonic("_Message Dialog")
+
+        button.connect('clicked',
+                       self._message_dialog_clicked)
+        hbox.pack_start(button, False, False, 0)
+
+        vbox.pack_start(Gtk.HSeparator(),
+                        False, False, 0)
+
+        # Interactive dialog
+        hbox = Gtk.HBox(spacing=8)
+        vbox.pack_start(hbox, False, False, 0)
+        vbox2 = Gtk.VBox(spacing=0)
+        button = Gtk.Button.new_with_mnemonic("_Interactive Dialog")
+
+        button.connect('clicked',
+                       self._interactive_dialog_clicked)
+        hbox.pack_start(vbox2, False, False, 0)
+        vbox2.pack_start(button, False, False, 0)
+
+        table = Gtk.Table(n_rows=2, n_columns=2, homogeneous=False)
+        table.set_row_spacings(4)
+        table.set_col_spacings(4)
+        hbox.pack_start(table, False, False, 0)
+
+        label = Gtk.Label.new_with_mnemonic("_Entry 1")
+        table.attach_defaults(label, 0, 1, 0, 1)
+
+        self.entry1 = Gtk.Entry()
+        table.attach_defaults(self.entry1, 1, 2, 0, 1)
+        label.set_mnemonic_widget(self.entry1)
+
+        label = Gtk.Label.new_with_mnemonic("E_ntry 2")
+
+        table.attach_defaults(label, 0, 1, 1, 2)
+
+        self.entry2 = Gtk.Entry()
+        table.attach_defaults(self.entry2, 1, 2, 1, 2)
+        label.set_mnemonic_widget(self.entry2)
+
+        self.window.show_all()
+
+    def _interactive_dialog_clicked(self, button):
+        dialog = Gtk.Dialog(title='Interactive Dialog',
+                            transient_for=self.window,
+                            modal=True,
+                            destroy_with_parent=True)
+        dialog.add_buttons(Gtk.STOCK_OK, Gtk.ResponseType.OK,
+                           "_Non-stock Button", Gtk.ResponseType.CANCEL)
+
+        content_area = dialog.get_content_area()
+        hbox = Gtk.HBox(spacing=8)
+        hbox.set_border_width(8)
+        content_area.pack_start(hbox, False, False, 0)
+
+        stock = Gtk.Image(stock=Gtk.STOCK_DIALOG_QUESTION,
+                          icon_size=Gtk.IconSize.DIALOG)
+
+        hbox.pack_start(stock, False, False, 0)
+
+        table = Gtk.Table(n_rows=2, n_columns=2, homogeneous=False)
+        table.set_row_spacings(4)
+        table.set_col_spacings(4)
+        hbox.pack_start(table, True, True, 0)
+        label = Gtk.Label.new_with_mnemonic("_Entry 1")
+        table.attach_defaults(label, 0, 1, 0, 1)
+        local_entry1 = Gtk.Entry()
+        local_entry1.set_text(self.entry1.get_text())
+        table.attach_defaults(local_entry1, 1, 2, 0, 1)
+        label.set_mnemonic_widget(local_entry1)
+
+        label = Gtk.Label.new_with_mnemonic("E_ntry 2")
+        table.attach_defaults(label, 0, 1, 1, 2)
+
+        local_entry2 = Gtk.Entry()
+        local_entry2.set_text(self.entry2.get_text())
+        table.attach_defaults(local_entry2, 1, 2, 1, 2)
+        label.set_mnemonic_widget(local_entry2)
+
+        hbox.show_all()
+
+        response = dialog.run()
+        if response == Gtk.ResponseType.OK:
+            self.entry1.set_text(local_entry1.get_text())
+            self.entry2.set_text(local_entry2.get_text())
+
+        dialog.destroy()
+
+    def _message_dialog_clicked(self, button):
+        dialog = Gtk.MessageDialog(transient_for=self.window,
+                                   modal=True,
+                                   destroy_with_parent=True,
+                                   message_type=Gtk.MessageType.INFO,
+                                   buttons=Gtk.ButtonsType.OK,
+                                   text="This message box has been popped up the following\nnumber of times:")
+        dialog.format_secondary_text('%d' % self.dialog_counter)
+        dialog.run()
+
+        self.dialog_counter += 1
+        dialog.destroy()
+
+
+def main(demoapp=None):
+    DialogsApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/drawingarea.py b/examples/demo/demos/drawingarea.py
new file mode 100644 (file)
index 0000000..b04c41d
--- /dev/null
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Drawing Area"
+description = """
+GtkDrawingArea is a blank area where you can draw custom displays
+of various kinds.
+
+This demo has two drawing areas. The checkerboard area shows
+how you can just draw something; all you have to do is write
+a signal handler for expose_event, as shown here.
+
+The "scribble" area is a bit more advanced, and shows how to handle
+events such as button presses and mouse motion. Click the mouse
+and drag in the scribble area to draw squiggles. Resize the window
+to clear the area.
+"""
+
+
+import cairo
+
+from gi.repository import Gtk, Gdk
+
+
+class DrawingAreaApp:
+    def __init__(self):
+        self.sureface = None
+
+        window = Gtk.Window()
+        window.set_title(title)
+        window.connect('destroy', lambda x: Gtk.main_quit())
+        window.set_border_width(8)
+
+        vbox = Gtk.VBox(homogeneous=False, spacing=8)
+        window.add(vbox)
+
+        # create checkerboard area
+        label = Gtk.Label()
+        label.set_markup('<u>Checkerboard pattern</u>')
+        vbox.pack_start(label, False, False, 0)
+
+        frame = Gtk.Frame()
+        frame.set_shadow_type(Gtk.ShadowType.IN)
+        vbox.pack_start(frame, True, True, 0)
+
+        da = Gtk.DrawingArea()
+        da.set_size_request(100, 100)
+        frame.add(da)
+        da.connect('draw', self.checkerboard_draw_event)
+
+        # create scribble area
+        label = Gtk.Label()
+        label.set_markup('<u>Scribble area</u>')
+        vbox.pack_start(label, False, False, 0)
+
+        frame = Gtk.Frame()
+        frame.set_shadow_type(Gtk.ShadowType.IN)
+        vbox.pack_start(frame, True, True, 0)
+
+        da = Gtk.DrawingArea()
+        da.set_size_request(100, 100)
+        frame.add(da)
+        da.connect('draw', self.scribble_draw_event)
+        da.connect('configure-event', self.scribble_configure_event)
+
+        # event signals
+        da.connect('motion-notify-event', self.scribble_motion_notify_event)
+        da.connect('button-press-event', self.scribble_button_press_event)
+
+        # Ask to receive events the drawing area doesn't normally
+        # subscribe to
+        da.set_events(da.get_events() |
+                      Gdk.EventMask.LEAVE_NOTIFY_MASK |
+                      Gdk.EventMask.BUTTON_PRESS_MASK |
+                      Gdk.EventMask.POINTER_MOTION_MASK |
+                      Gdk.EventMask.POINTER_MOTION_HINT_MASK)
+
+        window.show_all()
+
+    def checkerboard_draw_event(self, da, cairo_ctx):
+
+        # At the start of a draw handler, a clip region has been set on
+        # the Cairo context, and the contents have been cleared to the
+        # widget's background color. The docs for
+        # gdk_window_begin_paint_region() give more details on how this
+        # works.
+        check_size = 10
+        spacing = 2
+
+        xcount = 0
+        i = spacing
+        width = da.get_allocated_width()
+        height = da.get_allocated_height()
+
+        while i < width:
+            j = spacing
+            ycount = xcount % 2  # start with even/odd depending on row
+            while j < height:
+                if ycount % 2:
+                    cairo_ctx.set_source_rgb(0.45777, 0, 0.45777)
+                else:
+                    cairo_ctx.set_source_rgb(1, 1, 1)
+                # If we're outside the clip this will do nothing.
+                cairo_ctx.rectangle(i, j,
+                                    check_size,
+                                    check_size)
+                cairo_ctx.fill()
+
+                j += check_size + spacing
+                ycount += 1
+
+            i += check_size + spacing
+            xcount += 1
+
+        return True
+
+    def scribble_draw_event(self, da, cairo_ctx):
+
+        cairo_ctx.set_source_surface(self.surface, 0, 0)
+        cairo_ctx.paint()
+
+        return False
+
+    def draw_brush(self, widget, x, y):
+        update_rect = Gdk.Rectangle()
+        update_rect.x = x - 3
+        update_rect.y = y - 3
+        update_rect.width = 6
+        update_rect.height = 6
+
+        # paint to the surface where we store our state
+        cairo_ctx = cairo.Context(self.surface)
+
+        Gdk.cairo_rectangle(cairo_ctx, update_rect)
+        cairo_ctx.fill()
+
+        widget.get_window().invalidate_rect(update_rect, False)
+
+    def scribble_configure_event(self, da, event):
+
+        allocation = da.get_allocation()
+        self.surface = da.get_window().create_similar_surface(cairo.CONTENT_COLOR,
+                                                              allocation.width,
+                                                              allocation.height)
+
+        cairo_ctx = cairo.Context(self.surface)
+        cairo_ctx.set_source_rgb(1, 1, 1)
+        cairo_ctx.paint()
+
+        return True
+
+    def scribble_motion_notify_event(self, da, event):
+        if self.surface is None:  # paranoia check, in case we haven't gotten a configure event
+            return False
+
+        # This call is very important; it requests the next motion event.
+        # If you don't call gdk_window_get_pointer() you'll only get
+        # a single motion event. The reason is that we specified
+        # GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events().
+        # If we hadn't specified that, we could just use event->x, event->y
+        # as the pointer location. But we'd also get deluged in events.
+        # By requesting the next event as we handle the current one,
+        # we avoid getting a huge number of events faster than we
+        # can cope.
+
+        (window, x, y, state) = event.window.get_pointer()
+
+        if state & Gdk.ModifierType.BUTTON1_MASK:
+            self.draw_brush(da, x, y)
+
+        return True
+
+    def scribble_button_press_event(self, da, event):
+        if self.surface is None:  # paranoia check, in case we haven't gotten a configure event
+            return False
+
+        if event.button == 1:
+            self.draw_brush(da, event.x, event.y)
+
+        return True
+
+
+def main(demoapp=None):
+    DrawingAreaApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/expander.py b/examples/demo/demos/expander.py
new file mode 100644 (file)
index 0000000..0ec149e
--- /dev/null
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Expander"
+description = """
+GtkExpander allows to provide additional content that is initially hidden.
+This is also known as "disclosure triangle".
+"""
+
+from gi.repository import Gtk
+
+
+class ExpanderApp:
+    def __init__(self):
+        self.window = Gtk.Dialog(title="GtkExpander")
+        self.window.add_buttons(Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE)
+        self.window.set_resizable(False)
+        self.window.connect('response', lambda window, x: window.destroy())
+        self.window.connect('destroy', Gtk.main_quit)
+
+        content_area = self.window.get_content_area()
+        vbox = Gtk.VBox(spacing=5)
+        content_area.pack_start(vbox, True, True, 0)
+        vbox.set_border_width(5)
+
+        label = Gtk.Label(label='Expander demo. Click on the triangle for details.')
+        vbox.pack_start(label, True, True, 0)
+
+        expander = Gtk.Expander(label='Details')
+        vbox.pack_start(expander, False, False, 0)
+
+        label = Gtk.Label(label='Details can be shown or hidden')
+        expander.add(label)
+
+        self.window.show_all()
+
+
+def main(demoapp=None):
+    ExpanderApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/flowbox.py b/examples/demo/demos/flowbox.py
new file mode 100755 (executable)
index 0000000..0485b7c
--- /dev/null
@@ -0,0 +1,752 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2014 Gian Mario Tagliaretti <gianmt@gnome.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "FlowBox"
+description = """
+A FlowBox allows flexible and responsive grids which reflow as needed and
+support sorting and filtering. The children of a GtkFlowBox are regular widgets.
+"""
+
+from gi.repository import Gtk, Gdk
+
+
+class FlowBoxApp:
+    def __init__(self):
+        window = Gtk.Window()
+        window.connect('destroy', lambda x: Gtk.main_quit())
+        window.set_border_width(10)
+        window.set_default_size(600, 400)
+
+        header = Gtk.HeaderBar(title="Flow Box")
+        header.set_subtitle("Sample FlowBox app")
+        header.props.show_close_button = True
+
+        window.set_titlebar(header)
+
+        scrolled = Gtk.ScrolledWindow()
+        scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
+
+        flowbox = Gtk.FlowBox()
+        flowbox.set_valign(Gtk.Align.START)
+        flowbox.set_max_children_per_line(30)
+        flowbox.set_selection_mode(Gtk.SelectionMode.NONE)
+
+        self.create_flowbox(flowbox)
+
+        scrolled.add(flowbox)
+
+        window.add(scrolled)
+        window.show_all()
+
+    def color_swatch_new(self, str_color):
+        rgba = Gdk.RGBA()
+        rgba.parse(str_color)
+
+        text_rgba = Gdk.RGBA()  # default is white
+        if max(rgba.red, rgba.green, rgba.blue) > 0.6:
+            text_rgba.parse('black')
+
+        label = Gtk.Label(label=str_color)
+        label.override_background_color(0, rgba)
+        label.override_color(0, text_rgba)
+        return label
+
+    def create_flowbox(self, flowbox):
+        colors = [
+            'AliceBlue',
+            'AntiqueWhite',
+            'AntiqueWhite1',
+            'AntiqueWhite2',
+            'AntiqueWhite3',
+            'AntiqueWhite4',
+            'aqua',
+            'aquamarine',
+            'aquamarine1',
+            'aquamarine2',
+            'aquamarine3',
+            'aquamarine4',
+            'azure',
+            'azure1',
+            'azure2',
+            'azure3',
+            'azure4',
+            'beige',
+            'bisque',
+            'bisque1',
+            'bisque2',
+            'bisque3',
+            'bisque4',
+            'black',
+            'BlanchedAlmond',
+            'blue',
+            'blue1',
+            'blue2',
+            'blue3',
+            'blue4',
+            'BlueViolet',
+            'brown',
+            'brown1',
+            'brown2',
+            'brown3',
+            'brown4',
+            'burlywood',
+            'burlywood1',
+            'burlywood2',
+            'burlywood3',
+            'burlywood4',
+            'CadetBlue',
+            'CadetBlue1',
+            'CadetBlue2',
+            'CadetBlue3',
+            'CadetBlue4',
+            'chartreuse',
+            'chartreuse1',
+            'chartreuse2',
+            'chartreuse3',
+            'chartreuse4',
+            'chocolate',
+            'chocolate1',
+            'chocolate2',
+            'chocolate3',
+            'chocolate4',
+            'coral',
+            'coral1',
+            'coral2',
+            'coral3',
+            'coral4',
+            'CornflowerBlue',
+            'cornsilk',
+            'cornsilk1',
+            'cornsilk2',
+            'cornsilk3',
+            'cornsilk4',
+            'crimson',
+            'cyan',
+            'cyan1',
+            'cyan2',
+            'cyan3',
+            'cyan4',
+            'DarkBlue',
+            'DarkCyan',
+            'DarkGoldenrod',
+            'DarkGoldenrod1',
+            'DarkGoldenrod2',
+            'DarkGoldenrod3',
+            'DarkGoldenrod4',
+            'DarkGray',
+            'DarkGreen',
+            'DarkGrey',
+            'DarkKhaki',
+            'DarkMagenta',
+            'DarkOliveGreen',
+            'DarkOliveGreen1',
+            'DarkOliveGreen2',
+            'DarkOliveGreen3',
+            'DarkOliveGreen4',
+            'DarkOrange',
+            'DarkOrange1',
+            'DarkOrange2',
+            'DarkOrange3',
+            'DarkOrange4',
+            'DarkOrchid',
+            'DarkOrchid1',
+            'DarkOrchid2',
+            'DarkOrchid3',
+            'DarkOrchid4',
+            'DarkRed',
+            'DarkSalmon',
+            'DarkSeaGreen',
+            'DarkSeaGreen1',
+            'DarkSeaGreen2',
+            'DarkSeaGreen3',
+            'DarkSeaGreen4',
+            'DarkSlateBlue',
+            'DarkSlateGray',
+            'DarkSlateGray1',
+            'DarkSlateGray2',
+            'DarkSlateGray3',
+            'DarkSlateGray4',
+            'DarkSlateGrey',
+            'DarkTurquoise',
+            'DarkViolet',
+            'DeepPink',
+            'DeepPink1',
+            'DeepPink2',
+            'DeepPink3',
+            'DeepPink4',
+            'DeepSkyBlue',
+            'DeepSkyBlue1',
+            'DeepSkyBlue2',
+            'DeepSkyBlue3',
+            'DeepSkyBlue4',
+            'DimGray',
+            'DimGrey',
+            'DodgerBlue',
+            'DodgerBlue1',
+            'DodgerBlue2',
+            'DodgerBlue3',
+            'DodgerBlue4',
+            'firebrick',
+            'firebrick1',
+            'firebrick2',
+            'firebrick3',
+            'firebrick4',
+            'FloralWhite',
+            'ForestGreen',
+            'fuchsia',
+            'gainsboro',
+            'GhostWhite',
+            'gold',
+            'gold1',
+            'gold2',
+            'gold3',
+            'gold4',
+            'goldenrod',
+            'goldenrod1',
+            'goldenrod2',
+            'goldenrod3',
+            'goldenrod4',
+            'gray',
+            'gray0',
+            'gray1',
+            'gray2',
+            'gray3',
+            'gray4',
+            'gray5',
+            'gray6',
+            'gray7',
+            'gray8',
+            'gray9',
+            'gray10',
+            'gray11',
+            'gray12',
+            'gray13',
+            'gray14',
+            'gray15',
+            'gray16',
+            'gray17',
+            'gray18',
+            'gray19',
+            'gray20',
+            'gray21',
+            'gray22',
+            'gray23',
+            'gray24',
+            'gray25',
+            'gray26',
+            'gray27',
+            'gray28',
+            'gray29',
+            'gray30',
+            'gray31',
+            'gray32',
+            'gray33',
+            'gray34',
+            'gray35',
+            'gray36',
+            'gray37',
+            'gray38',
+            'gray39',
+            'gray40',
+            'gray41',
+            'gray42',
+            'gray43',
+            'gray44',
+            'gray45',
+            'gray46',
+            'gray47',
+            'gray48',
+            'gray49',
+            'gray50',
+            'gray51',
+            'gray52',
+            'gray53',
+            'gray54',
+            'gray55',
+            'gray56',
+            'gray57',
+            'gray58',
+            'gray59',
+            'gray60',
+            'gray61',
+            'gray62',
+            'gray63',
+            'gray64',
+            'gray65',
+            'gray66',
+            'gray67',
+            'gray68',
+            'gray69',
+            'gray70',
+            'gray71',
+            'gray72',
+            'gray73',
+            'gray74',
+            'gray75',
+            'gray76',
+            'gray77',
+            'gray78',
+            'gray79',
+            'gray80',
+            'gray81',
+            'gray82',
+            'gray83',
+            'gray84',
+            'gray85',
+            'gray86',
+            'gray87',
+            'gray88',
+            'gray89',
+            'gray90',
+            'gray91',
+            'gray92',
+            'gray93',
+            'gray94',
+            'gray95',
+            'gray96',
+            'gray97',
+            'gray98',
+            'gray99',
+            'gray100',
+            'green',
+            'green1',
+            'green2',
+            'green3',
+            'green4',
+            'GreenYellow',
+            'grey',
+            'grey0',
+            'grey1',
+            'grey2',
+            'grey3',
+            'grey4',
+            'grey5',
+            'grey6',
+            'grey7',
+            'grey8',
+            'grey9',
+            'grey10',
+            'grey11',
+            'grey12',
+            'grey13',
+            'grey14',
+            'grey15',
+            'grey16',
+            'grey17',
+            'grey18',
+            'grey19',
+            'grey20',
+            'grey21',
+            'grey22',
+            'grey23',
+            'grey24',
+            'grey25',
+            'grey26',
+            'grey27',
+            'grey28',
+            'grey29',
+            'grey30',
+            'grey31',
+            'grey32',
+            'grey33',
+            'grey34',
+            'grey35',
+            'grey36',
+            'grey37',
+            'grey38',
+            'grey39',
+            'grey40',
+            'grey41',
+            'grey42',
+            'grey43',
+            'grey44',
+            'grey45',
+            'grey46',
+            'grey47',
+            'grey48',
+            'grey49',
+            'grey50',
+            'grey51',
+            'grey52',
+            'grey53',
+            'grey54',
+            'grey55',
+            'grey56',
+            'grey57',
+            'grey58',
+            'grey59',
+            'grey60',
+            'grey61',
+            'grey62',
+            'grey63',
+            'grey64',
+            'grey65',
+            'grey66',
+            'grey67',
+            'grey68',
+            'grey69',
+            'grey70',
+            'grey71',
+            'grey72',
+            'grey73',
+            'grey74',
+            'grey75',
+            'grey76',
+            'grey77',
+            'grey78',
+            'grey79',
+            'grey80',
+            'grey81',
+            'grey82',
+            'grey83',
+            'grey84',
+            'grey85',
+            'grey86',
+            'grey87',
+            'grey88',
+            'grey89',
+            'grey90',
+            'grey91',
+            'grey92',
+            'grey93',
+            'grey94',
+            'grey95',
+            'grey96',
+            'grey97',
+            'grey98',
+            'grey99',
+            'grey100',
+            'honeydew',
+            'honeydew1',
+            'honeydew2',
+            'honeydew3',
+            'honeydew4',
+            'HotPink',
+            'HotPink1',
+            'HotPink2',
+            'HotPink3',
+            'HotPink4',
+            'IndianRed',
+            'IndianRed1',
+            'IndianRed2',
+            'IndianRed3',
+            'IndianRed4',
+            'indigo',
+            'ivory',
+            'ivory1',
+            'ivory2',
+            'ivory3',
+            'ivory4',
+            'khaki',
+            'khaki1',
+            'khaki2',
+            'khaki3',
+            'khaki4',
+            'lavender',
+            'LavenderBlush',
+            'LavenderBlush1',
+            'LavenderBlush2',
+            'LavenderBlush3',
+            'LavenderBlush4',
+            'LawnGreen',
+            'LemonChiffon',
+            'LemonChiffon1',
+            'LemonChiffon2',
+            'LemonChiffon3',
+            'LemonChiffon4',
+            'LightBlue',
+            'LightBlue1',
+            'LightBlue2',
+            'LightBlue3',
+            'LightBlue4',
+            'LightCoral',
+            'LightCyan',
+            'LightCyan1',
+            'LightCyan2',
+            'LightCyan3',
+            'LightCyan4',
+            'LightGoldenrod',
+            'LightGoldenrod1',
+            'LightGoldenrod2',
+            'LightGoldenrod3',
+            'LightGoldenrod4',
+            'LightGoldenrodYellow',
+            'LightGray',
+            'LightGreen',
+            'LightGrey',
+            'LightPink',
+            'LightPink1',
+            'LightPink2',
+            'LightPink3',
+            'LightPink4',
+            'LightSalmon',
+            'LightSalmon1',
+            'LightSalmon2',
+            'LightSalmon3',
+            'LightSalmon4',
+            'LightSeaGreen',
+            'LightSkyBlue',
+            'LightSkyBlue1',
+            'LightSkyBlue2',
+            'LightSkyBlue3',
+            'LightSkyBlue4',
+            'LightSlateBlue',
+            'LightSlateGray',
+            'LightSlateGrey',
+            'LightSteelBlue',
+            'LightSteelBlue1',
+            'LightSteelBlue2',
+            'LightSteelBlue3',
+            'LightSteelBlue4',
+            'LightYellow',
+            'LightYellow1',
+            'LightYellow2',
+            'LightYellow3',
+            'LightYellow4',
+            'lime',
+            'LimeGreen',
+            'linen',
+            'magenta',
+            'magenta1',
+            'magenta2',
+            'magenta3',
+            'magenta4',
+            'maroon',
+            'maroon1',
+            'maroon2',
+            'maroon3',
+            'maroon4',
+            'MediumAquamarine',
+            'MediumBlue',
+            'MediumOrchid',
+            'MediumOrchid1',
+            'MediumOrchid2',
+            'MediumOrchid3',
+            'MediumOrchid4',
+            'MediumPurple',
+            'MediumPurple1',
+            'MediumPurple2',
+            'MediumPurple3',
+            'MediumPurple4',
+            'MediumSeaGreen',
+            'MediumSlateBlue',
+            'MediumSpringGreen',
+            'MediumTurquoise',
+            'MediumVioletRed',
+            'MidnightBlue',
+            'MintCream',
+            'MistyRose',
+            'MistyRose1',
+            'MistyRose2',
+            'MistyRose3',
+            'MistyRose4',
+            'moccasin',
+            'NavajoWhite',
+            'NavajoWhite1',
+            'NavajoWhite2',
+            'NavajoWhite3',
+            'NavajoWhite4',
+            'navy',
+            'NavyBlue',
+            'OldLace',
+            'olive',
+            'OliveDrab',
+            'OliveDrab1',
+            'OliveDrab2',
+            'OliveDrab3',
+            'OliveDrab4',
+            'orange',
+            'orange1',
+            'orange2',
+            'orange3',
+            'orange4',
+            'OrangeRed',
+            'OrangeRed1',
+            'OrangeRed2',
+            'OrangeRed3',
+            'OrangeRed4',
+            'orchid',
+            'orchid1',
+            'orchid2',
+            'orchid3',
+            'orchid4',
+            'PaleGoldenrod',
+            'PaleGreen',
+            'PaleGreen1',
+            'PaleGreen2',
+            'PaleGreen3',
+            'PaleGreen4',
+            'PaleTurquoise',
+            'PaleTurquoise1',
+            'PaleTurquoise2',
+            'PaleTurquoise3',
+            'PaleTurquoise4',
+            'PaleVioletRed',
+            'PaleVioletRed1',
+            'PaleVioletRed2',
+            'PaleVioletRed3',
+            'PaleVioletRed4',
+            'PapayaWhip',
+            'PeachPuff',
+            'PeachPuff1',
+            'PeachPuff2',
+            'PeachPuff3',
+            'PeachPuff4',
+            'peru',
+            'pink',
+            'pink1',
+            'pink2',
+            'pink3',
+            'pink4',
+            'plum',
+            'plum1',
+            'plum2',
+            'plum3',
+            'plum4',
+            'PowderBlue',
+            'purple',
+            'purple1',
+            'purple2',
+            'purple3',
+            'purple4',
+            'red',
+            'red1',
+            'red2',
+            'red3',
+            'red4',
+            'RosyBrown',
+            'RosyBrown1',
+            'RosyBrown2',
+            'RosyBrown3',
+            'RosyBrown4',
+            'RoyalBlue',
+            'RoyalBlue1',
+            'RoyalBlue2',
+            'RoyalBlue3',
+            'RoyalBlue4',
+            'SaddleBrown',
+            'salmon',
+            'salmon1',
+            'salmon2',
+            'salmon3',
+            'salmon4',
+            'SandyBrown',
+            'SeaGreen',
+            'SeaGreen1',
+            'SeaGreen2',
+            'SeaGreen3',
+            'SeaGreen4',
+            'seashell',
+            'seashell1',
+            'seashell2',
+            'seashell3',
+            'seashell4',
+            'sienna',
+            'sienna1',
+            'sienna2',
+            'sienna3',
+            'sienna4',
+            'silver',
+            'SkyBlue',
+            'SkyBlue1',
+            'SkyBlue2',
+            'SkyBlue3',
+            'SkyBlue4',
+            'SlateBlue',
+            'SlateBlue1',
+            'SlateBlue2',
+            'SlateBlue3',
+            'SlateBlue4',
+            'SlateGray',
+            'SlateGray1',
+            'SlateGray2',
+            'SlateGray3',
+            'SlateGray4',
+            'SlateGrey',
+            'snow',
+            'snow1',
+            'snow2',
+            'snow3',
+            'snow4',
+            'SpringGreen',
+            'SpringGreen1',
+            'SpringGreen2',
+            'SpringGreen3',
+            'SpringGreen4',
+            'SteelBlue',
+            'SteelBlue1',
+            'SteelBlue2',
+            'SteelBlue3',
+            'SteelBlue4',
+            'tan',
+            'tan1',
+            'tan2',
+            'tan3',
+            'tan4',
+            'teal',
+            'thistle',
+            'thistle1',
+            'thistle2',
+            'thistle3',
+            'thistle4',
+            'tomato',
+            'tomato1',
+            'tomato2',
+            'tomato3',
+            'tomato4',
+            'turquoise',
+            'turquoise1',
+            'turquoise2',
+            'turquoise3',
+            'turquoise4',
+            'violet',
+            'VioletRed',
+            'VioletRed1',
+            'VioletRed2',
+            'VioletRed3',
+            'VioletRed4',
+            'wheat',
+            'wheat1',
+            'wheat2',
+            'wheat3',
+            'wheat4',
+            'white',
+            'WhiteSmoke',
+            'yellow',
+            'yellow1',
+            'yellow2',
+            'yellow3',
+            'yellow4',
+            'YellowGreen',
+        ]
+
+        for color in colors:
+            button = self.color_swatch_new(color)
+            flowbox.add(button)
+
+
+def main(demoapp=None):
+    FlowBoxApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/images.py b/examples/demo/demos/images.py
new file mode 100644 (file)
index 0000000..80c99af
--- /dev/null
@@ -0,0 +1,305 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Images"
+description = """GtkImage is used to display an image; the image can be in a
+number of formats. Typically, you load an image into a GdkPixbuf, then display
+the pixbuf. This demo code shows some of the more obscure cases, in the simple
+case a call to gtk_image_new_from_file() is all you need.
+"""
+
+import os
+from os import path
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, Gio, GObject
+
+
+class ImagesApp:
+    def __init__(self):
+        self.pixbuf_loader = None
+        self.image_stream = None
+
+        self.window = Gtk.Window(title="Images")
+        self.window.connect('destroy', self.cleanup_cb)
+        self.window.set_border_width(8)
+
+        vbox = Gtk.VBox(spacing=8)
+        vbox.set_border_width(8)
+        self.window.add(vbox)
+
+        label = Gtk.Label()
+        label.set_markup('<u>Image loaded from file</u>')
+        vbox.pack_start(label, False, False, 0)
+
+        frame = Gtk.Frame()
+        frame.set_shadow_type(Gtk.ShadowType.IN)
+
+        # The alignment keeps the frame from growing when users resize
+        # the window
+        align = Gtk.Alignment(xalign=0.5,
+                              yalign=0.5,
+                              xscale=0,
+                              yscale=0)
+        align.add(frame)
+        vbox.pack_start(align, False, False, 0)
+
+        self.base_path = os.path.abspath(os.path.dirname(__file__))
+        self.base_path = os.path.join(self.base_path, 'data')
+        filename = os.path.join(self.base_path, 'gtk-logo-rgb.gif')
+        pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
+        transparent = pixbuf.add_alpha(True, 0xff, 0xff, 0xff)
+        image = Gtk.Image.new_from_pixbuf(transparent)
+        frame.add(image)
+
+        # Animation
+
+        label = Gtk.Label()
+        label.set_markup('<u>Animation loaded from file</u>')
+        vbox.pack_start(label, False, False, 0)
+
+        frame = Gtk.Frame()
+        frame.set_shadow_type(Gtk.ShadowType.IN)
+
+        # The alignment keeps the frame from growing when users resize
+        # the window
+        align = Gtk.Alignment(xalign=0.5,
+                              yalign=0.5,
+                              xscale=0,
+                              yscale=0)
+        align.add(frame)
+        vbox.pack_start(align, False, False, 0)
+
+        img_path = path.join(self.base_path, 'floppybuddy.gif')
+        image = Gtk.Image.new_from_file(img_path)
+        frame.add(image)
+
+        # Symbolic icon
+
+        label = Gtk.Label()
+        label.set_markup('<u>Symbolic themed icon</u>')
+        vbox.pack_start(label, False, False, 0)
+
+        frame = Gtk.Frame()
+        frame.set_shadow_type(Gtk.ShadowType.IN)
+
+        # The alignment keeps the frame from growing when users resize
+        # the window
+        align = Gtk.Alignment(xalign=0.5,
+                              yalign=0.5,
+                              xscale=0,
+                              yscale=0)
+        align.add(frame)
+        vbox.pack_start(align, False, False, 0)
+
+        gicon = Gio.ThemedIcon.new_with_default_fallbacks('battery-caution-charging-symbolic')
+        image = Gtk.Image.new_from_gicon(gicon, Gtk.IconSize.DIALOG)
+        frame.add(image)
+
+        # progressive
+
+        label = Gtk.Label()
+        label.set_markup('<u>Progressive image loading</u>')
+        vbox.pack_start(label, False, False, 0)
+
+        frame = Gtk.Frame()
+        frame.set_shadow_type(Gtk.ShadowType.IN)
+
+        # The alignment keeps the frame from growing when users resize
+        # the window
+        align = Gtk.Alignment(xalign=0.5,
+                              yalign=0.5,
+                              xscale=0,
+                              yscale=0)
+        align.add(frame)
+        vbox.pack_start(align, False, False, 0)
+
+        image = Gtk.Image.new_from_pixbuf(None)
+        frame.add(image)
+
+        self.start_progressive_loading(image)
+
+        # Sensistivity control
+        button = Gtk.ToggleButton.new_with_mnemonic('_Insensitive')
+        button.connect('toggled', self.toggle_sensitivity_cb, vbox)
+        vbox.pack_start(button, False, False, 0)
+
+        self.window.show_all()
+
+    def toggle_sensitivity_cb(self, button, container):
+        widget_list = container.get_children()
+        for w in widget_list:
+            if w != button:
+                w.set_sensitive(not button.get_active())
+
+        return True
+
+    def progressive_timeout(self, image):
+        # This shows off fully-paranoid error handling, so looks scary.
+        # You could factor out the error handling code into a nice separate
+        # function to make things nicer.
+
+        if self.image_stream:
+            try:
+                buf = self.image_stream.read(256)
+            except IOError as e:
+                dialog = Gtk.MessageDialog(self.window,
+                                           Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                                           Gtk.MessageType.ERROR,
+                                           Gtk.ButtonsType.CLOSE,
+                                           "Failure reading image file 'alphatest.png': %s" % (str(e), ))
+
+                self.image_stream.close()
+                self.image_stream = None
+                self.load_timeout = 0
+
+                dialog.show()
+                dialog.connect('response', lambda x, y: dialog.destroy())
+
+                return False  # uninstall the timeout
+
+            try:
+                self.pixbuf_loader.write(buf)
+
+            except GObject.GError as e:
+                dialog = Gtk.MessageDialog(self.window,
+                                           Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                                           Gtk.MessageType.ERROR,
+                                           Gtk.ButtonsType.CLOSE,
+                                           e.message)
+
+                self.image_stream.close()
+                self.image_stream = None
+                self.load_timeout = 0
+
+                dialog.show()
+                dialog.connect('response', lambda x, y: dialog.destroy())
+
+                return False  # uninstall the timeout
+
+            if len(buf) < 256:  # eof
+                self.image_stream.close()
+                self.image_stream = None
+
+                # Errors can happen on close, e.g. if the image
+                # file was truncated we'll know on close that
+                # it was incomplete.
+                try:
+                    self.pixbuf_loader.close()
+                except GObject.GError as e:
+                    dialog = Gtk.MessageDialog(self.window,
+                                               Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                                               Gtk.MessageType.ERROR,
+                                               Gtk.ButtonsType.CLOSE,
+                                               'Failed to load image: %s' % e.message)
+
+                    self.load_timeout = 0
+
+                    dialog.show()
+                    dialog.connect('response', lambda x, y: dialog.destroy())
+
+                    return False  # uninstall the timeout
+        else:
+            img_path = path.join(self.base_path, 'alphatest.png')
+            try:
+                self.image_stream = open(img_path, 'rb')
+            except IOError as e:
+                dialog = Gtk.MessageDialog(self.window,
+                                           Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                                           Gtk.MessageType.ERROR,
+                                           Gtk.ButtonsType.CLOSE,
+                                           str(e))
+                self.load_timeout = 0
+                dialog.show()
+                dialog.connect('response', lambda x, y: dialog.destroy())
+
+                return False  # uninstall the timeout
+
+            if self.pixbuf_loader:
+                try:
+                    self.pixbuf_loader.close()
+                except GObject.GError:
+                    pass
+                self.pixbuf_loader = None
+
+            self.pixbuf_loader = GdkPixbuf.PixbufLoader()
+
+            self.pixbuf_loader.connect('area-prepared',
+                                       self.progressive_prepared_callback,
+                                       image)
+
+            self.pixbuf_loader.connect('area-updated',
+                                       self.progressive_updated_callback,
+                                       image)
+        # leave timeout installed
+        return True
+
+    def progressive_prepared_callback(self, loader, image):
+        pixbuf = loader.get_pixbuf()
+        # Avoid displaying random memory contents, since the pixbuf
+        # isn't filled in yet.
+        pixbuf.fill(0xaaaaaaff)
+        image.set_from_pixbuf(pixbuf)
+
+    def progressive_updated_callback(self, loader, x, y, width, height, image):
+        # We know the pixbuf inside the GtkImage has changed, but the image
+        # itself doesn't know this; so queue a redraw.  If we wanted to be
+        # really efficient, we could use a drawing area or something
+        # instead of a GtkImage, so we could control the exact position of
+        # the pixbuf on the display, then we could queue a draw for only
+        # the updated area of the image.
+        image.queue_draw()
+
+    def start_progressive_loading(self, image):
+        # This is obviously totally contrived (we slow down loading
+        # on purpose to show how incremental loading works).
+        # The real purpose of incremental loading is the case where
+        # you are reading data from a slow source such as the network.
+        # The timeout simply simulates a slow data source by inserting
+        # pauses in the reading process.
+
+        self.load_timeout = Gdk.threads_add_timeout(150,
+                                                    GLib.PRIORITY_DEFAULT_IDLE,
+                                                    self.progressive_timeout,
+                                                    image)
+
+    def cleanup_cb(self, widget):
+        if self.load_timeout:
+            GLib.source_remove(self.load_timeout)
+
+        if self.pixbuf_loader:
+            try:
+                self.pixbuf_loader.close()
+            except GObject.GError:
+                pass
+
+        if self.image_stream:
+            self.image_stream.close()
+
+        Gtk.main_quit()
+
+
+def main(demoapp=None):
+    ImagesApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/infobars.py b/examples/demo/demos/infobars.py
new file mode 100644 (file)
index 0000000..b8a1dc7
--- /dev/null
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Info Bars"
+description = """
+Info bar widgets are used to report important messages to the user.
+"""
+
+from gi.repository import Gtk
+
+
+class InfobarApp:
+    def __init__(self):
+        self.window = Gtk.Window()
+        self.window.set_title('Info Bars')
+        self.window.set_border_width(8)
+        self.window.connect('destroy', Gtk.main_quit)
+
+        vbox = Gtk.VBox(spacing=0)
+        self.window.add(vbox)
+
+        bar = Gtk.InfoBar()
+        vbox.pack_start(bar, False, False, 0)
+        bar.set_message_type(Gtk.MessageType.INFO)
+        label = Gtk.Label(label='This is an info bar with message type Gtk.MessageType.INFO')
+        bar.get_content_area().pack_start(label, False, False, 0)
+
+        bar = Gtk.InfoBar()
+        vbox.pack_start(bar, False, False, 0)
+        bar.set_message_type(Gtk.MessageType.WARNING)
+        label = Gtk.Label(label='This is an info bar with message type Gtk.MessageType.WARNING')
+        bar.get_content_area().pack_start(label, False, False, 0)
+
+        bar = Gtk.InfoBar()
+        bar.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
+        bar.connect('response', self.on_bar_response)
+        vbox.pack_start(bar, False, False, 0)
+        bar.set_message_type(Gtk.MessageType.QUESTION)
+        label = Gtk.Label(label='This is an info bar with message type Gtk.MessageType.QUESTION')
+        bar.get_content_area().pack_start(label, False, False, 0)
+
+        bar = Gtk.InfoBar()
+        vbox.pack_start(bar, False, False, 0)
+        bar.set_message_type(Gtk.MessageType.ERROR)
+        label = Gtk.Label(label='This is an info bar with message type Gtk.MessageType.ERROR')
+        bar.get_content_area().pack_start(label, False, False, 0)
+
+        bar = Gtk.InfoBar()
+        vbox.pack_start(bar, False, False, 0)
+        bar.set_message_type(Gtk.MessageType.OTHER)
+        label = Gtk.Label(label='This is an info bar with message type Gtk.MessageType.OTHER')
+        bar.get_content_area().pack_start(label, False, False, 0)
+
+        frame = Gtk.Frame(label="Info bars")
+        vbox.pack_start(frame, False, False, 8)
+
+        vbox2 = Gtk.VBox(spacing=8)
+        vbox2.set_border_width(8)
+        frame.add(vbox2)
+
+        # Standard message dialog
+        label = Gtk.Label(label='An example of different info bars')
+        vbox2.pack_start(label, False, False, 0)
+
+        self.window.show_all()
+
+    def on_bar_response(self, info_bar, response_id):
+        dialog = Gtk.MessageDialog(transient_for=self.window,
+                                   modal=True,
+                                   destroy_with_parent=True,
+                                   message_type=Gtk.MessageType.INFO,
+                                   buttons=Gtk.ButtonsType.OK,
+                                   text='You clicked on an info bar')
+        dialog.format_secondary_text('Your response has id %d' % response_id)
+        dialog.run()
+        dialog.destroy()
+
+
+def main(demoapp=None):
+    InfobarApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/links.py b/examples/demo/demos/links.py
new file mode 100644 (file)
index 0000000..c6d331d
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Links"
+description = """
+GtkLabel can show hyperlinks. The default action is to call gtk_show_uri() on
+their URI, but it is possible to override this with a custom handler.
+"""
+
+from gi.repository import Gtk
+
+
+class LinksApp:
+    def __init__(self):
+        self.window = Gtk.Window()
+        self.window.set_title('Links')
+        self.window.set_border_width(12)
+        self.window.connect('destroy', Gtk.main_quit)
+
+        label = Gtk.Label(label="""Some <a href="http://en.wikipedia.org/wiki/Text"
+title="plain text">text</a> may be marked up
+as hyperlinks, which can be clicked
+or activated via <a href="keynav">keynav</a>""")
+
+        label.set_use_markup(True)
+        label.connect("activate-link", self.activate_link)
+        self.window.add(label)
+        label.show()
+
+        self.window.show()
+
+    def activate_link(self, label, uri):
+        if uri == 'keynav':
+            parent = label.get_toplevel()
+            markup = """The term <i>keynav</i> is a shorthand for
+keyboard navigation and refers to the process of using
+a program (exclusively) via keyboard input."""
+            dialog = Gtk.MessageDialog(transient_for=parent,
+                                       destroy_with_parent=True,
+                                       message_type=Gtk.MessageType.INFO,
+                                       buttons=Gtk.ButtonsType.OK,
+                                       text=markup,
+                                       use_markup=True)
+            dialog.present()
+            dialog.connect('response', self.response_cb)
+
+            return True
+
+    def response_cb(self, dialog, response_id):
+        dialog.destroy()
+
+
+def main(demoapp=None):
+    LinksApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/menus.py b/examples/demo/demos/menus.py
new file mode 100644 (file)
index 0000000..7cf2e57
--- /dev/null
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Menus"
+description = """There are several widgets involved in displaying menus. The
+GtkMenuBar widget is a menu bar, which normally appears horizontally at the top
+of an application, but can also be layed out vertically. The GtkMenu widget is
+the actual menu that pops up. Both GtkMenuBar and GtkMenu are subclasses of
+GtkMenuShell; a GtkMenuShell contains menu items (GtkMenuItem). Each menu item
+contains text and/or images and can be selected by the user. There are several
+kinds of menu item, including plain GtkMenuItem, GtkCheckMenuItem which can be
+checked/unchecked, GtkRadioMenuItem which is a check menu item that's in a
+mutually exclusive group, GtkSeparatorMenuItem which is a separator bar,
+GtkTearoffMenuItem which allows a GtkMenu to be torn off, and GtkImageMenuItem
+which can place a GtkImage or other widget next to the menu text. A GtkMenuItem
+can have a submenu, which is simply a GtkMenu to pop up when the menu item is
+selected. Typically, all menu items in a menu bar have submenus. GtkUIManager
+provides a higher-level interface for creating menu bars and menus; while you
+can construct menus manually, most people don't do that. There's a separate demo
+for GtkUIManager.
+"""
+
+from gi.repository import Gtk
+
+
+class MenusApp:
+    def __init__(self):
+        self.window = Gtk.Window()
+        self.window.set_title('Menus')
+        self.window.connect('destroy', Gtk.main_quit)
+
+        accel_group = Gtk.AccelGroup()
+        self.window.add_accel_group(accel_group)
+        self.window.set_border_width(0)
+
+        box = Gtk.HBox()
+        self.window.add(box)
+
+        box1 = Gtk.VBox()
+        box.add(box1)
+
+        menubar = Gtk.MenuBar()
+        box1.pack_start(menubar, False, True, 0)
+
+        menuitem = Gtk.MenuItem(label='test\nline2')
+        menuitem.set_submenu(self.create_menu(3, True))
+        menubar.append(menuitem)
+
+        menuitem = Gtk.MenuItem(label='foo')
+        menuitem.set_submenu(self.create_menu(4, True))
+        menuitem.set_right_justified(True)
+        menubar.append(menuitem)
+
+        box2 = Gtk.VBox(spacing=10)
+        box2.set_border_width(10)
+        box1.pack_start(box2, False, True, 0)
+
+        button = Gtk.Button(label='Flip')
+        button.connect('clicked', self.change_orientation, menubar)
+        box2.pack_start(button, True, True, 0)
+
+        button = Gtk.Button(label='Close')
+        button.connect('clicked', lambda x: self.window.destroy())
+        box2.pack_start(button, True, True, 0)
+        button.set_can_default(True)
+
+        self.window.show_all()
+
+    def create_menu(self, depth, tearoff):
+        if depth < 1:
+            return None
+
+        menu = Gtk.Menu()
+
+        if tearoff:
+            menuitem = Gtk.TearoffMenuItem()
+            menu.append(menuitem)
+
+        i = 0
+        j = 1
+        while i < 5:
+            label = 'item %2d - %d' % (depth, j)
+            # we should be adding this to a group but the group API
+            # isn't bindable - we need something more like the
+            # Gtk.RadioAction API
+            menuitem = Gtk.RadioMenuItem(label=label)
+            menu.append(menuitem)
+
+            if i == 3:
+                menuitem.set_sensitive(False)
+
+            menuitem.set_submenu(self.create_menu(depth - 1, True))
+
+            i += 1
+            j += 1
+
+        return menu
+
+    def change_orientation(self, button, menubar):
+        parent = menubar.get_parent()
+        orientation = parent.get_orientation()
+        parent.set_orientation(1 - orientation)
+
+        if orientation == Gtk.Orientation.VERTICAL:
+            menubar.props.pack_direction = Gtk.PackDirection.TTB
+        else:
+            menubar.props.pack_direction = Gtk.PackDirection.LTR
+
+
+def main(demoapp=None):
+    MenusApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/pickers.py b/examples/demo/demos/pickers.py
new file mode 100644 (file)
index 0000000..d8a9c75
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Pickers"
+description = """These widgets are mainly intended for use in preference
+dialogs. They allow to select colors, fonts, files and directories.
+"""
+
+from gi.repository import Gtk
+
+
+class PickersApp:
+    def __init__(self):
+        self.window = Gtk.Window(title='Pickers')
+        self.window.connect('destroy', Gtk.main_quit)
+        self.window.set_border_width(10)
+
+        table = Gtk.Table(n_rows=4, n_columns=2, homogeneous=False)
+        table.set_col_spacing(0, 10)
+        table.set_row_spacings(3)
+        self.window.add(table)
+        table.set_border_width(10)
+
+        label = Gtk.Label(label='Color:')
+        label.set_alignment(0.0, 0.5)
+        picker = Gtk.ColorButton()
+        table.attach_defaults(label, 0, 1, 0, 1)
+        table.attach_defaults(picker, 1, 2, 0, 1)
+
+        label = Gtk.Label(label='Font:')
+        label.set_alignment(0.0, 0.5)
+        picker = Gtk.FontButton()
+        table.attach_defaults(label, 0, 1, 1, 2)
+        table.attach_defaults(picker, 1, 2, 1, 2)
+
+        label = Gtk.Label(label='File:')
+        label.set_alignment(0.0, 0.5)
+        picker = Gtk.FileChooserButton.new('Pick a File',
+                                           Gtk.FileChooserAction.OPEN)
+        table.attach_defaults(label, 0, 1, 2, 3)
+        table.attach_defaults(picker, 1, 2, 2, 3)
+
+        label = Gtk.Label(label='Folder:')
+        label.set_alignment(0.0, 0.5)
+        picker = Gtk.FileChooserButton.new('Pick a Folder',
+                                           Gtk.FileChooserAction.SELECT_FOLDER)
+        table.attach_defaults(label, 0, 1, 3, 4)
+        table.attach_defaults(picker, 1, 2, 3, 4)
+
+        self.window.show_all()
+
+
+def main(demoapp=None):
+    PickersApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/pixbuf.py b/examples/demo/demos/pixbuf.py
new file mode 100644 (file)
index 0000000..c213584
--- /dev/null
@@ -0,0 +1,184 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Pixbufs"
+description = """A GdkPixbuf represents an image, normally in RGB or RGBA
+format. Pixbufs are normally used to load files from disk and perform image
+scaling. It also shows off how to use GtkDrawingArea to do a simple animation.
+Look at the Image demo for additional pixbuf usage examples.
+"""
+
+from gi.repository import Gtk, Gdk, GdkPixbuf, GLib
+import os
+import math
+
+
+class PixbufApp:
+    FRAME_DELAY = 50
+    BACKGROUND_NAME = "background.jpg"
+    CYCLE_LEN = 60
+
+    def __init__(self):
+        self.background_width = 0
+        self.background_height = 0
+        self.background_pixbuf = None
+        self.frame = None
+        self.frame_num = 0
+        self.pixbufs = []
+        self.image_names = [
+            "apple-red.png",
+            "gnome-applets.png",
+            "gnome-calendar.png",
+            "gnome-foot.png",
+            "gnome-gmush.png",
+            "gnome-gimp.png",
+            "gnome-gsame.png",
+            "gnu-keys.png"
+        ]
+
+        self.window = Gtk.Window(title="Pixbufs")
+        self.window.set_resizable(False)
+        self.window.connect('destroy', self.cleanup_cb)
+
+        try:
+            self.load_pixbufs()
+        except GLib.Error as e:
+            dialog = Gtk.MessageDialog(None,
+                                       0,
+                                       Gtk.MessageType.ERROR,
+                                       Gtk.ButtonsType.CLOSE,
+                                       e.message)
+
+            dialog.run()
+            dialog.destroy()
+            Gtk.main_quit()
+
+        self.window.set_size_request(self.background_width,
+                                     self.background_height)
+        self.frame = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB,
+                                          False,
+                                          8,
+                                          self.background_width,
+                                          self.background_height)
+        self.da = Gtk.DrawingArea()
+        self.da.connect('draw', self.draw_cb)
+        self.window.add(self.da)
+        self.timeout_id = GLib.timeout_add(self.FRAME_DELAY, self.timeout_cb)
+        self.window.show_all()
+
+    def load_pixbufs(self):
+        base_path = os.path.abspath(os.path.dirname(__file__))
+        base_path = os.path.join(base_path, 'data')
+        img_path = os.path.join(base_path, self.BACKGROUND_NAME)
+        self.background_pixbuf = GdkPixbuf.Pixbuf.new_from_file(img_path)
+        self.background_height = self.background_pixbuf.get_height()
+        self.background_width = self.background_pixbuf.get_width()
+
+        for img_name in self.image_names:
+            img_path = os.path.join(base_path, img_name)
+            self.pixbufs.append(GdkPixbuf.Pixbuf.new_from_file(img_path))
+
+    def draw_cb(self, da, cairo_ctx):
+        Gdk.cairo_set_source_pixbuf(cairo_ctx, self.frame, 0, 0)
+        cairo_ctx.paint()
+
+        return True
+
+    def timeout_cb(self):
+        self.background_pixbuf.copy_area(0, 0,
+                                         self.background_width,
+                                         self.background_height,
+                                         self.frame,
+                                         0, 0)
+
+        f = float(self.frame_num % self.CYCLE_LEN) / self.CYCLE_LEN
+
+        xmid = self.background_width / 2.0
+        ymid = self.background_height / 2.0
+        radius = min(xmid, ymid) / 2.0
+
+        r1 = Gdk.Rectangle()
+        r2 = Gdk.Rectangle()
+
+        i = 0
+        for pb in self.pixbufs:
+            i += 1
+            ang = 2.0 * math.pi * i / len(self.pixbufs) - f * 2.0 * math.pi
+
+            iw = pb.get_width()
+            ih = pb.get_height()
+
+            r = radius + (radius / 3.0) * math.sin(f * 2.0 * math.pi)
+
+            xpos = math.floor(xmid + r * math.cos(ang) - iw / 2.0 + 0.5)
+            ypos = math.floor(ymid + r * math.sin(ang) - ih / 2.0 + 0.5)
+
+            if i & 1:
+                k = math.sin(f * 2.0 * math.pi)
+            else:
+                k = math.cos(f * 2.0 * math.pi)
+
+            k = 2.0 * k * k
+            k = max(0.25, k)
+
+            r1.x = xpos
+            r1.y = ypos
+            r1.width = iw * k
+            r1.height = ih * k
+
+            r2.x = 0
+            r2.y = 0
+            r2.width = self.background_width
+            r2.height = self.background_height
+
+            success, dest = Gdk.rectangle_intersect(r1, r2)
+            if success:
+                alpha = 0
+                if i & 1:
+                    alpha = max(127, math.fabs(255 * math.sin(f * 2.0 * math.pi)))
+                else:
+                    alpha = max(127, math.fabs(255 * math.cos(f * 2.0 * math.pi)))
+
+                pb.composite(self.frame,
+                             dest.x, dest.y,
+                             dest.width, dest.height,
+                             xpos, ypos,
+                             k, k,
+                             GdkPixbuf.InterpType.NEAREST,
+                             alpha)
+
+        self.da.queue_draw()
+
+        self.frame_num += 1
+        return True
+
+    def cleanup_cb(self, widget):
+        GLib.source_remove(self.timeout_id)
+        Gtk.main_quit()
+
+
+def main(demoapp=None):
+    PixbufApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/printing.py b/examples/demo/demos/printing.py
new file mode 100644 (file)
index 0000000..6e68037
--- /dev/null
@@ -0,0 +1,180 @@
+#!/usr/bin/env python
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Printing"
+description = """
+GtkPrintOperation offers a simple API to support printing in a cross-platform way.
+"""
+
+from gi.repository import Gtk, GLib, Pango, PangoCairo
+import math
+import os
+
+
+class PrintingApp:
+    HEADER_HEIGHT = 10 * 72 / 25.4
+    HEADER_GAP = 3 * 72 / 25.4
+
+    def __init__(self):
+        self.operation = Gtk.PrintOperation()
+        print_data = {'filename': os.path.abspath(__file__),
+                      'font_size': 12.0,
+                      'lines_per_page': 0,
+                      'lines': None,
+                      'num_lines': 0,
+                      'num_pages': 0
+                     }
+
+        self.operation.connect('begin-print', self.begin_print, print_data)
+        self.operation.connect('draw-page', self.draw_page, print_data)
+        self.operation.connect('end-print', self.end_print, print_data)
+
+        self.operation.set_use_full_page(False)
+        self.operation.set_unit(Gtk.Unit.POINTS)
+        self.operation.set_embed_page_setup(True)
+
+        settings = Gtk.PrintSettings()
+
+        dir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS)
+        if dir is None:
+            dir = GLib.get_home_dir()
+        if settings.get(Gtk.PRINT_SETTINGS_OUTPUT_FILE_FORMAT) == 'ps':
+            ext = '.ps'
+        elif settings.get(Gtk.PRINT_SETTINGS_OUTPUT_FILE_FORMAT) == 'svg':
+            ext = '.svg'
+        else:
+            ext = '.pdf'
+
+        uri = "file://%s/gtk-demo%s" % (dir, ext)
+        settings.set(Gtk.PRINT_SETTINGS_OUTPUT_URI, uri)
+        self.operation.set_print_settings(settings)
+
+    def run(self, parent=None):
+        result = self.operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, parent)
+
+        if result == Gtk.PrintOperationResult.ERROR:
+            message = self.operation.get_error()
+
+            dialog = Gtk.MessageDialog(parent,
+                                       0,
+                                       Gtk.MessageType.ERROR,
+                                       Gtk.ButtonsType.CLOSE,
+                                       message)
+
+            dialog.run()
+            dialog.destroy()
+
+        Gtk.main_quit()
+
+    def begin_print(self, operation, print_ctx, print_data):
+        height = print_ctx.get_height() - self.HEADER_HEIGHT - self.HEADER_GAP
+        print_data['lines_per_page'] = \
+            math.floor(height / print_data['font_size'])
+
+        file_path = print_data['filename']
+        if not os.path.isfile(file_path):
+            file_path = os.path.join('demos', file_path)
+            if not os.path.isfile:
+                raise Exception("file not found: " % (file_path, ))
+
+        # in reality you should most likely not read the entire
+        # file into a buffer
+        source_file = open(file_path, 'r')
+        s = source_file.read()
+        print_data['lines'] = s.split('\n')
+
+        print_data['num_lines'] = len(print_data['lines'])
+        print_data['num_pages'] = \
+            (print_data['num_lines'] - 1) / print_data['lines_per_page'] + 1
+
+        operation.set_n_pages(print_data['num_pages'])
+
+    def draw_page(self, operation, print_ctx, page_num, print_data):
+        cr = print_ctx.get_cairo_context()
+        width = print_ctx.get_width()
+
+        cr.rectangle(0, 0, width, self.HEADER_HEIGHT)
+        cr.set_source_rgb(0.8, 0.8, 0.8)
+        cr.fill_preserve()
+
+        cr.set_source_rgb(0, 0, 0)
+        cr.set_line_width(1)
+        cr.stroke()
+
+        layout = print_ctx.create_pango_layout()
+        desc = Pango.FontDescription('sans 14')
+        layout.set_font_description(desc)
+
+        layout.set_text(print_data['filename'], -1)
+        (text_width, text_height) = layout.get_pixel_size()
+
+        if text_width > width:
+            layout.set_width(width)
+            layout.set_ellipsize(Pango.EllipsizeMode.START)
+            (text_width, text_height) = layout.get_pixel_size()
+
+        cr.move_to((width - text_width) / 2,
+                   (self.HEADER_HEIGHT - text_height) / 2)
+        PangoCairo.show_layout(cr, layout)
+
+        page_str = "%d/%d" % (page_num + 1, print_data['num_pages'])
+        layout.set_text(page_str, -1)
+
+        layout.set_width(-1)
+        (text_width, text_height) = layout.get_pixel_size()
+        cr.move_to(width - text_width - 4,
+                   (self.HEADER_HEIGHT - text_height) / 2)
+        PangoCairo.show_layout(cr, layout)
+
+        layout = print_ctx.create_pango_layout()
+
+        desc = Pango.FontDescription('monospace')
+        desc.set_size(print_data['font_size'] * Pango.SCALE)
+        layout.set_font_description(desc)
+
+        cr.move_to(0, self.HEADER_HEIGHT + self.HEADER_GAP)
+        lines_pp = int(print_data['lines_per_page'])
+        num_lines = print_data['num_lines']
+        data_lines = print_data['lines']
+        font_size = print_data['font_size']
+        line = page_num * lines_pp
+
+        for i in range(lines_pp):
+            if line >= num_lines:
+                break
+
+            layout.set_text(data_lines[line], -1)
+            PangoCairo.show_layout(cr, layout)
+            cr.rel_move_to(0, font_size)
+            line += 1
+
+    def end_print(self, operation, print_ctx, print_data):
+        pass
+
+
+def main(demoapp=None):
+    app = PrintingApp()
+    GLib.idle_add(app.run, demoapp.window)
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/rotatedtext.py b/examples/demo/demos/rotatedtext.py
new file mode 100644 (file)
index 0000000..d47b1cf
--- /dev/null
@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+# coding=utf-8
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2010 Red Hat, Inc., John (J5) Palmieri <johnp@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+title = "Rotated Text"
+description = """This demo shows how to use PangoCairo to draw rotated and
+transformed text.  The right pane shows a rotated GtkLabel widget. In both
+cases, a custom PangoCairo shape renderer is installed to draw a red heard using
+cairo drawing operations instead of the Unicode heart character.
+"""
+
+from gi.repository import Gtk, Pango, PangoCairo, Gdk
+import cairo
+import sys
+import math
+
+# Python 2 and 3 handle UTF8 differently
+if sys.version_info < (3, 0):
+    BYTES_TEXT = "I \xe2\x99\xa5 GTK+"
+    UTF8_TEXT = unicode(BYTES_TEXT, 'UTF-8')
+    BYTES_HEART = "\xe2\x99\xa5"
+    HEART = unicode(BYTES_HEART, 'UTF-8')
+else:
+    UTF8_TEXT = "I â™¥ GTK+"
+    BYTES_TEXT = bytes(UTF8_TEXT, 'utf-8')
+    HEART = "♥"
+    BYTES_HEART = bytes(HEART, 'utf-8')
+
+
+class RotatedTextApp:
+    RADIUS = 150
+    N_WORDS = 5
+    FONT = "Serif 18"
+
+    def __init__(self):
+
+        white = Gdk.RGBA()
+        white.red = 1.0
+        white.green = 1.0
+        white.blue = 1.0
+        white.alpha = 1.0
+
+        self.window = Gtk.Window(title="Rotated Text")
+        self.window.set_default_size(4 * self.RADIUS, 2 * self.RADIUS)
+        self.window.connect('destroy', Gtk.main_quit)
+
+        box = Gtk.HBox()
+        box.set_homogeneous(True)
+        self.window.add(box)
+
+        # add a drawing area
+        da = Gtk.DrawingArea()
+        box.add(da)
+
+        # override the background color from the theme
+        da.override_background_color(0, white)
+
+        da.connect('draw', self.rotated_text_draw)
+
+        label = Gtk.Label(label=UTF8_TEXT)
+        box.add(label)
+        label.set_angle(45)
+
+        # Setup some fancy stuff on the label
+        layout = label.get_layout()
+
+        PangoCairo.context_set_shape_renderer(layout.get_context(),
+                                              self.fancy_shape_renderer,
+                                              None)
+        attrs = self.create_fancy_attr_list_for_layout(layout)
+        label.set_attributes(attrs)
+
+        self.window.show_all()
+
+    def fancy_shape_renderer(self, cairo_ctx, attr, do_path):
+        x, y = cairo_ctx.get_current_point()
+        cairo_ctx.translate(x, y)
+
+        cairo_ctx.scale(float(attr.inc_rect.width) / Pango.SCALE,
+                        float(attr.inc_rect.height) / Pango.SCALE)
+
+        if int(attr.data) == 0x2665:  # U+2665 BLACK HEART SUIT
+            cairo_ctx.move_to(0.5, 0.0)
+            cairo_ctx.line_to(0.9, -0.4)
+            cairo_ctx.curve_to(1.1, -0.8, 0.5, -0.9, 0.5, -0.5)
+            cairo_ctx.curve_to(0.5, -0.9, -0.1, -0.8, 0.1, -0.4)
+            cairo_ctx.close_path()
+
+        if not do_path:
+            cairo_ctx.set_source_rgb(1.0, 0.0, 0.0)
+            cairo_ctx.fill()
+
+    def create_fancy_attr_list_for_layout(self, layout):
+        pango_ctx = layout.get_context()
+        metrics = pango_ctx.get_metrics(layout.get_font_description(),
+                                        None)
+        ascent = metrics.get_ascent()
+
+        logical_rect = Pango.Rectangle()
+        logical_rect.x = 0
+        logical_rect.width = ascent
+        logical_rect.y = -ascent
+        logical_rect.height = ascent
+
+        # Set fancy shape attributes for all hearts
+        attrs = Pango.AttrList()
+
+        # FIXME: attr_shape_new_with_data isn't introspectable
+        '''
+        ink_rect = logical_rect
+        p = BYTES_TEXT.find(BYTES_HEART)
+        while (p != -1):
+            attr = Pango.attr_shape_new_with_data(ink_rect,
+                                                  logical_rect,
+                                                  ord(HEART),
+                                                  None)
+            attr.start_index = p
+            attr.end_index = p + len(BYTES_HEART)
+            p = UTF8_TEXT.find(HEART, attr.end_index)
+
+        attrs.insert(attr)
+        '''
+        return attrs
+
+    def rotated_text_draw(self, da, cairo_ctx):
+        # Create a cairo context and set up a transformation matrix so that the user
+        # space coordinates for the centered square where we draw are [-RADIUS, RADIUS],
+        # [-RADIUS, RADIUS].
+        # We first center, then change the scale.
+        width = da.get_allocated_width()
+        height = da.get_allocated_height()
+        device_radius = min(width, height) / 2.0
+        cairo_ctx.translate(
+            device_radius + (width - 2 * device_radius) / 2,
+            device_radius + (height - 2 * device_radius) / 2)
+        cairo_ctx.scale(device_radius / self.RADIUS,
+                        device_radius / self.RADIUS)
+
+        # Create a subtle gradient source and use it.
+        pattern = cairo.LinearGradient(-self.RADIUS, -self.RADIUS, self.RADIUS, self.RADIUS)
+        pattern.add_color_stop_rgb(0.0, 0.5, 0.0, 0.0)
+        pattern.add_color_stop_rgb(1.0, 0.0, 0.0, 0.5)
+        cairo_ctx.set_source(pattern)
+
+        # Create a PangoContext and set up our shape renderer
+        context = da.create_pango_context()
+        PangoCairo.context_set_shape_renderer(context,
+                                              self.fancy_shape_renderer,
+                                              None)
+
+        # Create a PangoLayout, set the text, font, and attributes */
+        layout = Pango.Layout(context=context)
+        layout.set_text(UTF8_TEXT, -1)
+        desc = Pango.FontDescription(self.FONT)
+        layout.set_font_description(desc)
+
+        attrs = self.create_fancy_attr_list_for_layout(layout)
+        layout.set_attributes(attrs)
+
+        # Draw the layout N_WORDS times in a circle */
+        for i in range(self.N_WORDS):
+            # Inform Pango to re-layout the text with the new transformation matrix
+            PangoCairo.update_layout(cairo_ctx, layout)
+
+            width, height = layout.get_pixel_size()
+            cairo_ctx.move_to(-width / 2, -self.RADIUS * 0.9)
+            PangoCairo.show_layout(cairo_ctx, layout)
+
+            # Rotate for the next turn
+            cairo_ctx.rotate(math.pi * 2 / self.N_WORDS)
+
+        return False
+
+
+def main(demoapp=None):
+    RotatedTextApp()
+    Gtk.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/examples/demo/demos/test.py b/examples/demo/demos/test.py
new file mode 100644 (file)
index 0000000..4bd7684
--- /dev/null
@@ -0,0 +1,16 @@
+title = "Test Demo"
+description = "Dude this is a test"
+
+
+from gi.repository import Gtk
+
+
+def _quit(*args):
+    Gtk.main_quit()
+
+
+def main(demoapp=None):
+    window = Gtk.Window()
+    window.connect_after('destroy', _quit)
+    window.show_all()
+    Gtk.main()
diff --git a/gi/_ossighelper.py b/gi/_ossighelper.py
new file mode 100644 (file)
index 0000000..2c72134
--- /dev/null
@@ -0,0 +1,263 @@
+# -*- coding: utf-8 -*-
+# Copyright 2017 Christoph Reiter
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import print_function
+
+import os
+import sys
+import socket
+import signal
+import ctypes
+import threading
+from contextlib import closing, contextmanager
+
+
+def ensure_socket_not_inheritable(sock):
+    """Ensures that the socket is not inherited by child processes
+
+    Raises:
+        EnvironmentError
+        NotImplementedError: With Python <3.4 on Windows
+    """
+
+    if hasattr(sock, "set_inheritable"):
+        sock.set_inheritable(False)
+    else:
+        try:
+            import fcntl
+        except ImportError:
+            raise NotImplementedError(
+                "Not implemented for older Python on Windows")
+        else:
+            fd = sock.fileno()
+            flags = fcntl.fcntl(fd, fcntl.F_GETFD)
+            fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
+
+
+_wakeup_fd_is_active = False
+"""Since we can't check if set_wakeup_fd() is already used for nested event
+loops without introducing a race condition we keep track of it globally.
+"""
+
+
+@contextmanager
+def wakeup_on_signal():
+    """A decorator for functions which create a glib event loop to keep
+    Python signal handlers working while the event loop is idling.
+
+    In case an OS signal is received will wake the default event loop up
+    shortly so that any registered Python signal handlers registered through
+    signal.signal() can run.
+
+    Works on Windows but needs Python 3.5+.
+
+    In case the wrapped function is not called from the main thread it will be
+    called as is and it will not wake up the default loop for signals.
+    """
+
+    global _wakeup_fd_is_active
+
+    if _wakeup_fd_is_active:
+        yield
+        return
+
+    from gi.repository import GLib
+
+    # On Windows only Python 3.5+ supports passing sockets to set_wakeup_fd
+    set_wakeup_fd_supports_socket = (
+        os.name != "nt" or sys.version_info[:2] >= (3, 5))
+    # On Windows only Python 3 has an implementation of socketpair()
+    has_socketpair = hasattr(socket, "socketpair")
+
+    if not has_socketpair or not set_wakeup_fd_supports_socket:
+        yield
+        return
+
+    read_socket, write_socket = socket.socketpair()
+    with closing(read_socket), closing(write_socket):
+
+        for sock in [read_socket, write_socket]:
+            sock.setblocking(False)
+            ensure_socket_not_inheritable(sock)
+
+        try:
+            orig_fd = signal.set_wakeup_fd(write_socket.fileno())
+        except ValueError:
+            # Raised in case this is not the main thread -> give up.
+            yield
+            return
+        else:
+            _wakeup_fd_is_active = True
+
+        def signal_notify(source, condition):
+            if condition & GLib.IO_IN:
+                try:
+                    return bool(read_socket.recv(1))
+                except EnvironmentError as e:
+                    print(e)
+                    return False
+                return True
+            else:
+                return False
+
+        try:
+            if os.name == "nt":
+                channel = GLib.IOChannel.win32_new_socket(
+                    read_socket.fileno())
+            else:
+                channel = GLib.IOChannel.unix_new(read_socket.fileno())
+
+            source_id = GLib.io_add_watch(
+                channel,
+                GLib.PRIORITY_DEFAULT,
+                (GLib.IOCondition.IN | GLib.IOCondition.HUP |
+                 GLib.IOCondition.NVAL | GLib.IOCondition.ERR),
+                signal_notify)
+            try:
+                yield
+            finally:
+                GLib.source_remove(source_id)
+        finally:
+            write_fd = signal.set_wakeup_fd(orig_fd)
+            if write_fd != write_socket.fileno():
+                # Someone has called set_wakeup_fd while func() was active,
+                # so let's re-revert again.
+                signal.set_wakeup_fd(write_fd)
+            _wakeup_fd_is_active = False
+
+
+def create_pythonapi():
+    # We need our own instance of ctypes.pythonapi so we don't modify the
+    # global shared one. Adapted from the ctypes source.
+    if os.name == "nt":
+        return ctypes.PyDLL("python dll", None, sys.dllhandle)
+    elif sys.platform == "cygwin":
+        return ctypes.PyDLL("libpython%d.%d.dll" % sys.version_info[:2])
+    else:
+        return ctypes.PyDLL(None)
+
+
+pydll = create_pythonapi()
+PyOS_getsig = pydll.PyOS_getsig
+PyOS_getsig.restype = ctypes.c_void_p
+PyOS_getsig.argtypes = [ctypes.c_int]
+
+# We save the signal pointer so we can detect if glib has changed the
+# signal handler behind Python's back (GLib.unix_signal_add)
+if signal.getsignal(signal.SIGINT) is signal.default_int_handler:
+    startup_sigint_ptr = PyOS_getsig(signal.SIGINT)
+else:
+    # Something has set the handler before import, we can't get a ptr
+    # for the default handler so make sure the pointer will never match.
+    startup_sigint_ptr = -1
+
+
+def sigint_handler_is_default():
+    """Returns if on SIGINT the default Python handler would be called"""
+
+    return (signal.getsignal(signal.SIGINT) is signal.default_int_handler and
+            PyOS_getsig(signal.SIGINT) == startup_sigint_ptr)
+
+
+@contextmanager
+def sigint_handler_set_and_restore_default(handler):
+    """Context manager for saving/restoring the SIGINT handler default state.
+
+    Will only restore the default handler again if the handler is not changed
+    while the context is active.
+    """
+
+    assert sigint_handler_is_default()
+
+    signal.signal(signal.SIGINT, handler)
+    sig_ptr = PyOS_getsig(signal.SIGINT)
+    try:
+        yield
+    finally:
+        if signal.getsignal(signal.SIGINT) is handler and \
+                PyOS_getsig(signal.SIGINT) == sig_ptr:
+            signal.signal(signal.SIGINT, signal.default_int_handler)
+
+
+def is_main_thread():
+    """Returns True in case the function is called from the main thread"""
+
+    return threading.current_thread().name == "MainThread"
+
+
+_callback_stack = []
+_sigint_called = False
+
+
+@contextmanager
+def register_sigint_fallback(callback):
+    """Installs a SIGINT signal handler in case the default Python one is
+    active which calls 'callback' in case the signal occurs.
+
+    Only does something if called from the main thread.
+
+    In case of nested context managers the signal handler will be only
+    installed once and the callbacks will be called in the reverse order
+    of their registration.
+
+    The old signal handler will be restored in case no signal handler is
+    registered while the context is active.
+    """
+
+    # To handle multiple levels of event loops we need to call the last
+    # callback first, wait until the inner most event loop returns control
+    # and only then call the next callback, and so on... until we
+    # reach the outer most which manages the signal handler and raises
+    # in the end
+
+    global _callback_stack, _sigint_called
+
+    if not is_main_thread():
+        yield
+        return
+
+    if not sigint_handler_is_default():
+        if _callback_stack:
+            # This is an inner event loop, append our callback
+            # to the stack so the parent context can call it.
+            _callback_stack.append(callback)
+            try:
+                yield
+            finally:
+                if _sigint_called:
+                    _callback_stack.pop()()
+        else:
+            # There is a signal handler set by the user, just do nothing
+            yield
+        return
+
+    _sigint_called = False
+
+    def sigint_handler(sig_num, frame):
+        global _callback_stack, _sigint_called
+
+        if _sigint_called:
+            return
+        _sigint_called = True
+        _callback_stack.pop()()
+
+    _callback_stack.append(callback)
+    with sigint_handler_set_and_restore_default(sigint_handler):
+        try:
+            yield
+        finally:
+            if _sigint_called:
+                signal.default_int_handler()
index 372d6d4165c8c0299e5dfa9a589a459048db3617..52b6af3bf66ecb69053b678019488b246474ce82 100644 (file)
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 # USA
 
-import signal
 import warnings
 import sys
 import socket
 
+from .._ossighelper import wakeup_on_signal, register_sigint_fallback
 from ..module import get_introspection_module
 from .._gi import (variant_type_from_string, source_new,
                    source_set_callback, io_channel_read)
@@ -560,32 +560,13 @@ class MainLoop(GLib.MainLoop):
     def __new__(cls, context=None):
         return GLib.MainLoop.new(context, False)
 
-    # Retain classic pygobject behaviour of quitting main loops on SIGINT
     def __init__(self, context=None):
-        def _handler(loop):
-            loop.quit()
-            loop._quit_by_sigint = True
-            # We handle signal deletion in __del__, return True so GLib
-            # doesn't do the deletion for us.
-            return True
-
-        if sys.platform != 'win32':
-            # compatibility shim, keep around until we depend on glib 2.36
-            if hasattr(GLib, 'unix_signal_add'):
-                fn = GLib.unix_signal_add
-            else:
-                fn = GLib.unix_signal_add_full
-            self._signal_source = fn(GLib.PRIORITY_DEFAULT, signal.SIGINT, _handler, self)
-
-    def __del__(self):
-        if hasattr(self, '_signal_source'):
-            GLib.source_remove(self._signal_source)
+        pass
 
     def run(self):
-        super(MainLoop, self).run()
-        if hasattr(self, '_quit_by_sigint'):
-            # caught by _main_loop_sigint_handler()
-            raise KeyboardInterrupt
+        with register_sigint_fallback(self.quit):
+            with wakeup_on_signal():
+                super(MainLoop, self).run()
 
 
 MainLoop = override(MainLoop)
index cdb3ccb64be29e7f0b98c1e9988bb20653e3c92c..5ab23fcd6ee0eb0ddaf39a0efd0417c3a88d9682 100644 (file)
@@ -20,6 +20,7 @@
 
 import warnings
 
+from .._ossighelper import wakeup_on_signal, register_sigint_fallback
 from ..overrides import override, deprecated_init
 from ..module import get_introspection_module
 from gi import PyGIWarning
@@ -33,6 +34,18 @@ Gio = get_introspection_module('Gio')
 __all__ = []
 
 
+class Application(Gio.Application):
+
+    def run(self, *args, **kwargs):
+        with register_sigint_fallback(self.quit):
+            with wakeup_on_signal():
+                return Gio.Application.run(self, *args, **kwargs)
+
+
+Application = override(Application)
+__all__.append('Application')
+
+
 class VolumeMonitor(Gio.VolumeMonitor):
 
     def __init__(self, *args, **kwargs):
index 08d26127e60d88866c214f35a4788aab6237cab4..c495fd1d88ab5f755aaf37c0f5c44a7b0d45bbb8 100644 (file)
@@ -24,6 +24,7 @@ import sys
 import warnings
 
 from gi.repository import GObject
+from .._ossighelper import wakeup_on_signal, register_sigint_fallback
 from ..overrides import override, strip_boolean_result, deprecated_init
 from ..module import get_introspection_module
 from gi import PyGIDeprecationWarning
@@ -543,6 +544,11 @@ class Dialog(Gtk.Dialog, Container):
         if add_buttons:
             self.add_buttons(*add_buttons)
 
+    def run(self, *args, **kwargs):
+        with register_sigint_fallback(self.destroy):
+            with wakeup_on_signal():
+                return Gtk.Dialog.run(self, *args, **kwargs)
+
     action_area = property(lambda dialog: dialog.get_action_area())
     vbox = property(lambda dialog: dialog.get_content_area())
 
@@ -1007,28 +1013,30 @@ class ListStore(Gtk.ListStore, TreeModel, TreeSortable):
         Gtk.ListStore.set_value(self, treeiter, column, value)
 
     def set(self, treeiter, *args):
-
-        def _set_lists(columns, values):
-            if len(columns) != len(values):
+        def _set_lists(cols, vals):
+            if len(cols) != len(vals):
                 raise TypeError('The number of columns do not match the number of values')
-            for col_num, val in zip(columns, values):
+
+            columns = []
+            values = []
+            for col_num, value in zip(cols, vals):
                 if not isinstance(col_num, int):
                     raise TypeError('TypeError: Expected integer argument for column.')
-                self.set_value(treeiter, col_num, val)
+
+                columns.append(col_num)
+                values.append(self._convert_value(col_num, value))
+
+            Gtk.ListStore.set(self, treeiter, columns, values)
 
         if args:
             if isinstance(args[0], int):
-                columns = args[::2]
-                values = args[1::2]
-                _set_lists(columns, values)
+                _set_lists(args[::2], args[1::2])
             elif isinstance(args[0], (tuple, list)):
                 if len(args) != 2:
                     raise TypeError('Too many arguments')
                 _set_lists(args[0], args[1])
             elif isinstance(args[0], dict):
-                columns = args[0].keys()
-                values = args[0].values()
-                _set_lists(columns, values)
+                _set_lists(list(args[0]), args[0].values())
             else:
                 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}.  No -1 termination is needed.')
 
@@ -1269,28 +1277,30 @@ class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
         Gtk.TreeStore.set_value(self, treeiter, column, value)
 
     def set(self, treeiter, *args):
-
-        def _set_lists(columns, values):
-            if len(columns) != len(values):
+        def _set_lists(cols, vals):
+            if len(cols) != len(vals):
                 raise TypeError('The number of columns do not match the number of values')
-            for col_num, val in zip(columns, values):
+
+            columns = []
+            values = []
+            for col_num, value in zip(cols, vals):
                 if not isinstance(col_num, int):
                     raise TypeError('TypeError: Expected integer argument for column.')
-                self.set_value(treeiter, col_num, val)
+
+                columns.append(col_num)
+                values.append(self._convert_value(col_num, value))
+
+            Gtk.TreeStore.set(self, treeiter, columns, values)
 
         if args:
             if isinstance(args[0], int):
-                columns = args[::2]
-                values = args[1::2]
-                _set_lists(columns, values)
+                _set_lists(args[::2], args[1::2])
             elif isinstance(args[0], (tuple, list)):
                 if len(args) != 2:
                     raise TypeError('Too many arguments')
                 _set_lists(args[0], args[1])
             elif isinstance(args[0], dict):
-                columns = args[0].keys()
-                values = args[0].values()
-                _set_lists(columns, values)
+                _set_lists(args[0].keys(), args[0].values())
             else:
                 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}.  No -1 termination is needed.')
 
@@ -1590,6 +1600,16 @@ def main_quit(*args):
     _Gtk_main_quit()
 
 
+_Gtk_main = Gtk.main
+
+
+@override(Gtk.main)
+def main(*args, **kwargs):
+    with register_sigint_fallback(Gtk.main_quit):
+        with wakeup_on_signal():
+            return _Gtk_main(*args, **kwargs)
+
+
 if Gtk._version in ("2.0", "3.0"):
     stock_lookup = strip_boolean_result(Gtk.stock_lookup)
     __all__.append('stock_lookup')
index 6484f03324352f6e3fb8d3cb6217c15aae228736..0934a44c5e313eed8476661de5e0446b47e84700 100644 (file)
@@ -21,7 +21,7 @@
 #   Test also for gcov program and create GCOV variable that could be
 #   substituted.
 #
-#   Note that all optimization flags in CFLAGS must be disabled when code
+#   Note that all optimisation flags in CFLAGS must be disabled when code
 #   coverage is enabled.
 #
 #   Usage example:
@@ -75,7 +75,7 @@
 #   You should have received a copy of the GNU Lesser General Public License
 #   along with this program. If not, see <https://www.gnu.org/licenses/>.
 
-#serial 25
+#serial 21
 
 AC_DEFUN([AX_CODE_COVERAGE],[
        dnl Check for --enable-code-coverage
@@ -218,12 +218,9 @@ CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
 CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\
 $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
 --rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
-CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
+CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULTS)
 CODE_COVERAGE_IGNORE_PATTERN ?=
 
-GITIGNOREFILES ?=
-GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
-
 code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V))
 code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY))
 code_coverage_v_lcov_cap_0 = @echo "  LCOV   --capture"\
@@ -253,6 +250,9 @@ code-coverage-capture-hook:
 
 '"$CODE_COVERAGE_RULES_CLEAN"'
 
+GITIGNOREFILES ?=
+GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
+
 A''M_DISTCHECK_CONFIGURE_FLAGS ?=
 A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
 
index 9767e6a00650c7bed47055dcc6bfe5ce7871d783..aeb16e329f538be2e33077ab5a6f07aba09d3a6d 100644 (file)
 # LICENSE
 #
 #   Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
-#   Copyright (c) 2017 Reini Urban <rurban@cpan.org>
 #
 #   Copying and distribution of this file, with or without modification, are
 #   permitted in any medium without royalty provided the copyright notice
 #   and this notice are preserved.  This file is offered as-is, without any
 #   warranty.
 
-#serial 15
+#serial 14
 
 AC_DEFUN([AX_COMPILER_FLAGS_CFLAGS],[
     AC_REQUIRE([AC_PROG_SED])
@@ -101,13 +100,6 @@ AC_DEFUN([AX_COMPILER_FLAGS_CFLAGS],[
             -Wreturn-type dnl
             -Wswitch-enum dnl
             -Wswitch-default dnl
-            -Wduplicated-cond dnl
-            -Wduplicated-branches dnl
-            -Wlogical-op dnl
-            -Wrestrict dnl
-            -Wnull-dereference dnl
-            -Wjump-misses-init dnl
-            -Wdouble-promotion dnl
             $4 dnl
             $5 dnl
             $6 dnl
index 4b1901947132a8fa66ddd59fce9d2ccfb7fd842f..d8f03d42090489cba594e2b82f027d7ca423aef5 100644 (file)
@@ -1,9 +1,6 @@
 # Configure paths for GLIB
 # Owen Taylor     1997-2001
 
-# Increment this whenever this file is changed.
-#serial 1
-
 dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
 dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject,
 dnl gthread, or gio is specified in MODULES, pass to pkg-config
@@ -93,7 +90,7 @@ dnl
 #include <stdlib.h>
 
 int 
-main (void)
+main ()
 {
   unsigned int major, minor, micro;
 
diff --git a/pygi-convert.sh b/pygi-convert.sh
deleted file mode 100755 (executable)
index 43af662..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-#!/bin/sh
-
-if [ -n "$1" ]; then
-    FILES_TO_CONVERT="$@"
-else
-    FILES_TO_CONVERT="$(find . -name '*.py')"
-fi
-
-for f in $FILES_TO_CONVERT; do
-    perl -i -0 \
-    -pe "s/import gconf\n/from gi.repository import GConf\n/g;" \
-    -pe "s/gconf\./GConf\./g;" \
-    -pe "s/GConf\.client_get_default/GConf.Client.get_default/g;" \
-    -pe "s/GConf\.CLIENT_/GConf.ClientPreloadType./g;" \
-    -pe "s/GConf\.VALUE_/GConf.ValueType./g;" \
-    -pe "s/gconf_client.notify_add\('\/desktop\/sugar\/collaboration\/publish_gadget',/return;gconf_client.notify_add\('\/desktop\/sugar\/collaboration\/publish_gadget',/g;" \
-\
-    -pe "s/import pygtk/import gi/g;" \
-    -pe "s/pygtk.require\('2.0'\)/gi.require_version\('Gtk', '3.0'\)/g;" \
-    -pe "s/pygtk.require\(\"2.0\"\)/gi.require_version\(\"Gtk\", \"3.0\"\)/g;" \
-    -pe "s/import gtk\n/from gi.repository import Gtk\n/g;" \
-    -pe "s/(?<!\.)gtk\./Gtk\./g;" \
-    -pe "s/Gtk.ACCEL_/Gtk.AccelFlags./g;" \
-    -pe "s/Gtk.ARROW_/Gtk.ArrowType./g;" \
-    -pe "s/Gtk.ASSISTANT_PAGE_/Gtk.AssistantPageType./g;" \
-    -pe "s/Gtk.BUTTONBOX_/Gtk.ButtonBoxStyle./g;" \
-    -pe "s/Gtk.BUTTONS_/Gtk.ButtonsType./g;" \
-    -pe "s/Gtk.CELL_RENDERER_MODE_/Gtk.CellRendererMode./g;" \
-    -pe "s/Gtk.CELL_RENDERER_FOCUSED/Gtk.CellRendererState.FOCUSED/g;" \
-    -pe "s/Gtk.CELL_RENDERER_INSENSITIVE/Gtk.CellRendererState.INSENSITIVE/g;" \
-    -pe "s/Gtk.CELL_RENDERER_PRELIT/Gtk.CellRendererState.PRELIT/g;" \
-    -pe "s/Gtk.CELL_RENDERER_SORTED/Gtk.CellRendererState.SORTED/g;" \
-    -pe "s/Gtk.CELL_RENDERER_SELECTED/Gtk.CellRendererState.SELECTED/g;" \
-    -pe "s/Gtk.CORNER_/Gtk.CornerType./g;" \
-    -pe "s/Gtk.DIALOG_/Gtk.DialogFlags./g;" \
-    -pe "s/Gtk.ENTRY_ICON_/Gtk.EntryIconPosition./g;" \
-    -pe "s/Gtk.EXPAND/Gtk.AttachOptions.EXPAND/g;" \
-    -pe "s/Gtk.FALSE/False/g;" \
-    -pe "s/Gtk.FILE_CHOOSER_ACTION_/Gtk.FileChooserAction./g;" \
-    -pe "s/Gtk.FILL/Gtk.AttachOptions.FILL/g;" \
-    -pe "s/Gtk.ICON_LOOKUP_/Gtk.IconLookupFlags./g;" \
-    -pe "s/Gtk.ICON_SIZE_/Gtk.IconSize./g;" \
-    -pe "s/Gtk.IMAGE_/Gtk.ImageType./g;" \
-    -pe "s/Gtk.JUSTIFY_/Gtk.Justification./g;" \
-    -pe "s/Gtk.MESSAGE_/Gtk.MessageType./g;" \
-    -pe "s/Gtk.MOVEMENT_/Gtk.MovementStep./g;" \
-    -pe "s/Gtk.ORIENTATION_/Gtk.Orientation./g;" \
-    -pe "s/Gtk.POLICY_/Gtk.PolicyType./g;" \
-    -pe "s/Gtk.POS_/Gtk.PositionType./g;" \
-    -pe "s/Gtk.RECENT_FILTER_/Gtk.RecentFilterFlags./g;" \
-    -pe "s/Gtk.RECENT_SORT_/Gtk.RecentSortType./g;" \
-    -pe "s/Gtk.RELIEF_/Gtk.ReliefStyle./g;" \
-    -pe "s/Gtk.RESPONSE_/Gtk.ResponseType./g;" \
-    -pe "s/Gtk.SELECTION_/Gtk.SelectionMode./g;" \
-    -pe "s/Gtk.SHADOW_/Gtk.ShadowType./g;" \
-    -pe "s/Gtk.SHADOW_NONE/Gtk.ShadowType.NONE/g;" \
-    -pe "s/Gtk.SHRINK/Gtk.AttachOptions.SHRINK/g;" \
-    -pe "s/Gtk.SIZE_GROUP_/Gtk.SizeGroupMode./g;" \
-    -pe "s/Gtk.SORT_/Gtk.SortType./g;" \
-    -pe "s/Gtk.STATE_/Gtk.StateType./g;" \
-    -pe "s/Gtk.TARGET_/Gtk.TargetFlags./g;" \
-    -pe "s/Gtk.TEXT_DIR_/Gtk.TextDirection./g;" \
-    -pe "s/Gtk.TEXT_SEARCH_/Gtk.TextSearchFlags./g;" \
-    -pe "s/Gtk.TEXT_WINDOW_/Gtk.TextWindowType./g;" \
-    -pe "s/Gtk.TOOLBAR_/Gtk.ToolbarStyle./g;" \
-    -pe "s/Gtk.TREE_MODEL_/Gtk.TreeModelFlags./g;" \
-    -pe "s/Gtk.TREE_VIEW_COLUMN_/Gtk.TreeViewColumnSizing./g;" \
-    -pe "s/Gtk.TREE_VIEW_DROP_/Gtk.TreeViewDropPosition./g;" \
-    -pe "s/Gtk.TRUE/True/g;" \
-    -pe "s/Gtk.WINDOW_/Gtk.WindowType./g;" \
-    -pe "s/Gtk.DEST_DEFAULT_/Gtk.DestDefaults./g;" \
-    -pe "s/Gtk.WIN_POS_/Gtk.WindowPosition./g;" \
-    -pe "s/Gtk.WRAP_/Gtk.WrapMode./g;" \
-    -pe "s/Gtk.UI_MANAGER_/Gtk.UIManagerItemType./g;" \
-    -pe "s/Gtk.accel_map_/Gtk.AccelMap./g;" \
-    -pe "s/Gtk.settings_get_/Gtk.Settings.get_/g;" \
-    -pe "s/Gtk.icon_theme_get_default/Gtk.IconTheme.get_default/g;" \
-    -pe "s/Gtk.recent_manager_get_default/Gtk.RecentManager.get_default/g;" \
-    -pe "s/Gtk.image_new_from_stock/Gtk.Image.new_from_stock/g;" \
-    -pe "s/Gtk.image_new_from_icon_name/Gtk.Image.new_from_icon_name/g;" \
-    -pe "s/Gtk.window_set_default_icon_name/Gtk.Window.set_default_icon_name/g; " \
-    -pe "s/Gtk.combo_box_new_text/Gtk.ComboBoxText/g;" \
-    -pe "s/Gtk.keysyms\./Gdk.KEY_/g;" \
-    -pe "s/set_flags\(Gtk.CAN_DEFAULT\)/set_can_default\(True\)/g;" \
-    -pe "s/.flags\(\) & Gtk.MAPPED/.get_mapped\(\)/g;" \
-    -pe "s/.flags\(\) & Gtk.REALIZED/.get_realized\(\)/g;" \
-    -pe "s/\.window\.set_type_hint/.set_type_hint/g;" \
-    -pe "s/\.window\.set_skip_taskbar_hint/.set_skip_taskbar_hint/g;" \
-    -pe "s/\.window\.set_transient_for/.set_transient_for/g;" \
-    -pe "s/Gtk.Alignment\(/Gtk.Alignment.new\(/g;" \
-    -pe "#s/Gtk.Window.__init__\(self\)/Gtk.Window.__init__\(Gtk.WindowType.TOPLEVEL\)/g;" \
-    -pe "s/\.child([^_A-Za-z])/.get_child\(\)\1/g;" \
-\
-    -pe "s/column.pack_start\(([^,\)]*)\)/column.pack_start\(\1, True\)/g;" \
-    -pe "s/pack_start\(([^,\)]*)\)/pack_start\(\1, True, True, 0\)/g;" \
-    -pe "s/pack_start\(([^,]*), fill=([^,\)]*)\)/pack_start\(\1, True, \2, 0\)/g;" \
-    -pe "s/pack_start\(([^,]*), expand=([^,\)]*)\)/pack_start\(\1, \2, True, 0\)/g;" \
-    -pe "s/pack_start\(([^,]*),(\s*)padding=([A-Za-z0-9._]*)\)/pack_start\(\1,\2True, True,\2\3\)/g;" \
-    -pe "s/column.pack_end\(([^,\)]*)\)/column.pack_end\(\1, True\)/g;" \
-    -pe "s/pack_end\(([^,\)]*)\)/pack_end\(\1, True, True, 0\)/g;" \
-    -pe "s/pack_end\(([^,]*), fill=([^,\)]*)\)/pack_end\(\1, True, \2, 0\)/g;" \
-    -pe "s/pack_end\(([^,]*), expand=([^,\)]*)\)/pack_end\(\1, \2, True, 0\)/g;" \
-    -pe "s/pack_end\(([^,]*),(\s*)padding=([A-Za-z0-9._]*)\)/pack_end\(\1,\2True, True,\2\3\)/g;" \
-    -pe "#s/Gtk.HBox\(\)/Gtk.HBox\(False, 0\)/g;" \
-    -pe "#s/Gtk.VBox\(\)/Gtk.VBox\(False, 0\)/g;" \
-    -pe "s/Gtk.Label\s*\(([^,\)]+)\)/Gtk.Label\(label=\1\)/g;" \
-    -pe "s/Gtk.AccelLabel\s*\(([^,\)]+)\)/Gtk.AccelLabel\(label=\1\)/g;" \
-    -pe "s/Gtk.((?:Accel)?Label)\(label=label=/Gtk.\1\(label=/g;" \
-    -pe "s/len\(self._content.get_children\(\)\) > 0/self._content.get_children\(\)/g;" \
-    -pe "s/len\(self.menu.get_children\(\)\) > 0/self.menu.get_children\(\)/g;" \
-    -pe "s/import gobject\n/from gi.repository import GObject\n/g;" \
-    -pe "s/Gtk\..*\.__init__/gobject.GObject.__init__/g;" \
-\
-    -pe "s/rsvg.Handle\s*\(data=([^,\)]+)\)/Rsvg.Handle.new_from_data(\1)/g;" \
-\
-    -pe "s/from gtk import gdk\n/from gi.repository import Gdk\n/g;" \
-    -pe "s/import gtk.gdk as gdk\n/from gi.repository import Gdk\n/g;" \
-    -pe "s/Gtk.gdk.x11_/GdkX11.x11_/g;" \
-    -pe "s/Gtk.gdk\./Gdk\./g;" \
-    -pe "s/(?<!\.)gdk\./Gdk\./g;" \
-    -pe "s/Gdk.screen_width/Gdk.Screen.width/g;" \
-    -pe "s/Gdk.screen_height/Gdk.Screen.height/g;" \
-    -pe "s/Gdk.screen_get_default/Gdk.Screen.get_default/g;" \
-    -pe "s/Gdk.display_get_default/Gdk.Display.get_default/g;" \
-    -pe "s/screen_, x_, y_, modmask = display.get_pointer\(\)/x_, y_, modmask = display.get_pointer\(None\)/g;" \
-    -pe "s/Gdk.WINDOW_TYPE_HINT_/Gdk.WindowTypeHint./g;" \
-    -pe "s/Gdk.SHIFT_MASK/Gdk.ModifierType.SHIFT_MASK/g;" \
-    -pe "s/Gdk.LOCK_MASK/Gdk.ModifierType.LOCK_MASK/g;" \
-    -pe "s/Gdk.CONTROL_MASK/Gdk.ModifierType.CONTROL_MASK/g;" \
-    -pe "s/Gdk.MOD1_MASK/Gdk.ModifierType.MOD1_MASK/g;" \
-    -pe "s/Gdk.MOD2_MASK/Gdk.ModifierType.MOD2_MASK/g;" \
-    -pe "s/Gdk.MOD3_MASK/Gdk.ModifierType.MOD3_MASK/g;" \
-    -pe "s/Gdk.MOD4_MASK/Gdk.ModifierType.MOD4_MASK/g;" \
-    -pe "s/Gdk.MOD5_MASK/Gdk.ModifierType.MOD5_MASK/g;" \
-    -pe "s/Gdk.BUTTON1_MASK/Gdk.ModifierType.BUTTON1_MASK/g;" \
-    -pe "s/Gdk.BUTTON2_MASK/Gdk.ModifierType.BUTTON2_MASK/g;" \
-    -pe "s/Gdk.BUTTON3_MASK/Gdk.ModifierType.BUTTON3_MASK/g;" \
-    -pe "s/Gdk.BUTTON4_MASK/Gdk.ModifierType.BUTTON4_MASK/g;" \
-    -pe "s/Gdk.BUTTON5_MASK/Gdk.ModifierType.BUTTON5_MASK/g;" \
-    -pe "s/Gdk.RELEASE_MASK/Gdk.ModifierType.RELEASE_MASK/g;" \
-    -pe "s/Gdk.MODIFIER_MASK/Gdk.ModifierType.MODIFIER_MASK/g;" \
-    -pe "s/Gdk.([A-Z_0-9]*)_MASK/Gdk.EventMask.\1_MASK/g;" \
-    -pe "s/Gdk.VISIBILITY_FULLY_OBSCURED/Gdk.VisibilityState.FULLY_OBSCURED/g;" \
-    -pe "s/Gdk.NOTIFY_ANCESTOR/Gdk.NotifyType.ANCESTOR/g;" \
-    -pe "s/Gdk.NOTIFY_INFERIOR/Gdk.NotifyType.INFERIOR/g;" \
-    -pe "s/Gdk.NOTIFY_NONLINEAR_VIRTUAL/Gdk.NotifyType.NONLINEAR_VIRTUAL/g;" \
-    -pe "s/Gdk.NOTIFY_NONLINEAR/Gdk.NotifyType.NONLINEAR/g;" \
-    -pe "s/Gdk.NOTIFY_UNKNOWN/Gdk.NotifyType.UNKNOWN/g;" \
-    -pe "s/Gdk.NOTIFY_VIRTUAL/Gdk.NotifyType.VIRTUAL/g;" \
-    -pe "s/Gdk.PROP_MODE_APPEND/Gdk.PropMode.APPEND/g;" \
-    -pe "s/Gdk.PROP_MODE_PREPEND/Gdk.PropMode.PREPEND/g;" \
-    -pe "s/Gdk.PROP_MODE_REPLACE/Gdk.PropMode.REPLACE/g;" \
-    -pe "s/Gdk.BUTTON_PRESS/Gdk.EventType.BUTTON_PRESS/g;" \
-    -pe "s/Gdk.ACTION_/Gdk.DragAction./g;" \
-    -pe "s/Gdk.GRAB_/Gdk.GrabStatus./g;" \
-    -pe "s/Gdk.SCROLL_(DOWN|LEFT|RIGHT|UP)/Gdk.ScrollDirection.\1/g;" \
-    -pe "s/Gdk.([A-Z]+_(PTR|CURSOR))/Gdk.CursorType.\1/g;" \
-    -pe "s/Gdk.(CROSSHAIR)/Gdk.CursorType.\1/g;" \
-    -pe "s/Gdk.(WATCH)/Gdk.CursorType.\1/g;" \
-    -pe "s/Gdk.(ARROW)/Gdk.CursorType.\1/g;" \
-    -pe "s/Gdk.(CLOCK)/Gdk.CursorType.\1/g;" \
-    -pe "s/Gdk.WINDOW_STATE_(ABOVE|BELOW|FOCUSED|FULLSCREEN|ICONIFIED|MAXIMIZED|STICKY|WITHDRAWN)/Gdk.WindowState.\1/g;" \
-    -pe "s/Gdk.Cursor\s*\(/Gdk.Cursor.new\(/g;" \
-    -pe "s/#Gdk.Rectangle\(([^,\)]*), ([^,\)]*), ([^,\)]*), ([^,\)]*)\)/\1, \2, \3, \4/g;" \
-    -pe "s/Gdk.Rectangle//g;" \
-    -pe "s/intersection = child_rect.intersect/intersects_, intersection = child_rect.intersect/g;" \
-    -pe "s/event.state/event.get_state\(\)/g;" \
-\
-    -pe "s/Gdk.pixbuf_/GdkPixbuf.Pixbuf./g;" \
-    -pe "s/Gdk.Pixbuf/GdkPixbuf.Pixbuf/g;" \
-    -pe "s/Gdk.INTERP_/GdkPixbuf.InterpType./g;" \
-    -pe "s/Gdk.COLORSPACE_RGB/GdkPixbuf.Colorspace.RGB/g;" \
-\
-    -pe "s/import pango\n/from gi.repository import Pango\n/g;" \
-    -pe "s/pango\./Pango\./g;" \
-    -pe "s/Pango.ALIGN_/Pango.Alignment./g;" \
-    -pe "s/Pango.ELLIPSIZE_/Pango.EllipsizeMode./g;" \
-    -pe "s/Pango.STYLE_/Pango.Style./g;" \
-    -pe "s/Pango.UNDERLINE_/Pango.Underline./g;" \
-    -pe "s/Pango.WEIGHT_/Pango.Weight./g;" \
-    -pe "s/Pango.WRAP_/Pango.WrapMode./g;" \
-    -pe "s/Pango.TAB_/Pango.TabAlign./g;" \
-\
-    -pe "s/import atk\n/from gi.repository import Atk\n/g;" \
-    -pe "s/atk\./Atk\./g;" \
-    -pe "s/Atk.HYPERLINK_/Atk.HyperlinkStateFlags./g;" \
-    -pe "s/Atk.KEY_EVENT_/Atk.KeyEventType./g;" \
-    -pe "s/Atk.LAYER_/Atk.Layer./g;" \
-    -pe "s/Atk.RELATION_/Atk.RelationType./g;" \
-    -pe "s/Atk.ROLE_/Atk.Role./g;" \
-    -pe "s/Atk.STATE_/Atk.StateType./g;" \
-    -pe "s/Atk.TEXT_ATTR_/Atk.TextAttribute./g;" \
-    -pe "s/Atk.TEXT_BOUNDARY_/Atk.TextBoundary./g;" \
-    -pe "s/Atk.TEXT_CLIP_/Atk.TextClipType./g;" \
-\
-    -pe "s/import gio\n/from gi.repository import Gio\n/g;" \
-    -pe "s/gio\./Gio\./g;" \
-    -pe "s/Gio\.File\(uri=/Gio\.File\.new_for_uri\(/g;" \
-    -pe "s/Gio\.File\(path=/Gio\.File\.new_for_path\(/g;" \
-    -pe "s/Gio.FILE_COPY_/Gio.FileCopyFlags./g;" \
-    -pe "s/Gio.FILE_CREATE_/Gio.FileCreateFlags./g;" \
-    -pe "s/Gio.FILE_MONITOR_EVENT_/Gio.FileMonitorEvent./g;" \
-    -pe "s/Gio.FILE_MONITOR_/Gio.FileMonitorFlags./g;" \
-    -pe "s/Gio.FILE_TYPE_/Gio.FileType./g;" \
-    -pe "s/Gio.FILE_QUERY_INFO_/Gio.FileQueryInfoFlags./g;" \
-    -pe "s/Gio.MOUNT_MOUNT_/Gio.MountMountFlags./g;" \
-    -pe "s/Gio.MOUNT_OPERATION_/Gio.MountOperationResult./g;" \
-    -pe "s/Gio.MOUNT_UNMOUNT_/Gio.MountUnmountFlags./g;" \
-    -pe "s/Gio.OUTPUT_STREAM_SPLICE_/Gio.OutputStreamSpliceFlags./g;" \
-    -pe "s/Gio.vfs_/Gio.Vfs./g;" \
-\
-    -pe "#s/import glib\n/from gi.repository import GLib\n/g;" \
-    -pe "#s/(?<!\.)glib\./GLib\./g;" \
-    -pe "#s/GLib.IO_(ERR|HUP|IN|NVAL|OUT|PRI)/GLib.IOCondition./g;" \
-    -pe "#s/GLib.IO_FLAG_/GLib.IOFlags./g;" \
-    -pe "#s/GLib.IO_STATUS_/GLib.IOStatus./g;" \
-    -pe "#s/GLib.OPTION_ERROR_/GLib.OptionError./g;" \
-    -pe "#s/GLib.OPTION_FLAG_/GLib.OptionFlags./g;" \
-    -pe "#s/GLib.SPAWN_/GLib.SpawnFlags./g;" \
-    -pe "#s/GLib.USER_DIRECTORY_/GLib.UserDirectory.DIRECTORY_/g;" \
-\
-    -pe "s/(?<!\.)gobject\./GObject\./g;" \
-    -pe "s/GObject.SIGNAL_/GObject.SignalFlags./g;" \
-    -pe "s/GObject.TYPE_NONE/None/g;" \
-\
-    -pe "s/import hippo\n/from gi.repository import Hippo\n/g;" \
-    -pe "s/hippo\./Hippo\./g;" \
-    -pe "s/Hippo\..*\.__init__/gobject.GObject.__init__/g;" \
-    -pe "s/Hippo.PACK_/Hippo.PackFlags./g;" \
-    -pe "s/Hippo.ORIENTATION_/Hippo.Orientation./g;" \
-    -pe "#s/insert_sorted\(([^,\)]*), ([^,\)]*), ([^,\)]*)\)/insert_sorted\(\1, \2, \3, None\)/g;" \
-    -pe "s/self\._box\.insert_sorted/#self\._box\.insert_sorted/g;" \
-    -pe "s/self._box.append\(([^,\)]*)\)/self._box.append\(\1, 0\)/g;" \
-    -pe "s/self.append\(self._buddy_icon\)/self.append\(self._buddy_icon, 0\)/g;" \
-    -pe "s/self._box.sort\(([^,\)]*)\)/self._box.sort\(\1, None\)/g;" \
-\
-    -pe "s/import wnck\n/from gi.repository import Wnck\n/g;" \
-    -pe "s/wnck\./Wnck\./g;" \
-    -pe "s/Wnck.screen_get_default/Wnck.Screen.get_default/g;" \
-    -pe "s/Wnck.WINDOW_/Wnck.WindowType./g;" \
-\
-    -pe "s/from sugar import _sugarext\n/from gi.repository import SugarExt\n/g;" \
-    -pe "s/_sugarext\.ICON_ENTRY_/SugarExt.SexyIconEntryPosition./g;" \
-    -pe "s/_sugarext\.IconEntry/SugarExt.SexyIconEntry/g;" \
-    -pe "s/_sugarext\.SMClientXSMP/SugarExt.GsmClientXSMP/g;" \
-    -pe "s/_sugarext\.VolumeAlsa/SugarExt.AcmeVolumeAlsa/g;" \
-    -pe "s/_sugarext\./SugarExt\./g;" \
-\
-    -pe "s/import gtksourceview2\n/from gi.repository import GtkSource\n/g;" \
-    -pe "s/import gtksourceview2 as gsv\n/from gi.repository import GtkSource\n/g;" \
-    -pe "s/gtksourceview2\./GtkSource\./g;" \
-    -pe "s/gsv\./GtkSource\./g;" \
-    -pe "s/GtkSource.DRAW_SPACES_/GtkSource.DrawSpacesFlags./g;" \
-    -pe "s/GtkSource.SMART_HOME_END_/GtkSource.SmartHomeEndType./g;" \
-    -pe "s/GtkSource.style_scheme_manager_get_default/GtkSource.StyleSchemeManager.get_default/g;" \
-    -pe "s/GtkSource.language_manager_get_default/GtkSource.LanguageManager.get_default/g;" \
-\
-    -pe "#s/import cairo\n/from gi.repository import cairo\n/g;" \
-\
-    -pe "s/SugarExt.xsmp_init\(\)/'mec'/g;" \
-    -pe "s/SugarExt.xsmp_run\(\)/#SugarExt.xsmp_run\(\)/g;" \
-    -pe "s/SugarExt.session_create_global\(\)/None #SugarExt.session_create_global\(\)/g;" \
-    -pe "s/self.session.start\(\)/return #self.session.start\(\)/g;" \
-\
-    -pe "s/self._box.sort\(self._layout.compare_activities, None\)/pass #self._box.sort(self._layout.compare_activities, None)/g;" \
-    -pe "s/attach_points = info.get_attach_points/has_attach_points_, attach_points = info.get_attach_points/g;" \
-    -pe "s/attach_points\[0\]\[0\]/attach_points\[0\].x/g;" \
-    -pe "s/attach_points\[0\]\[1\]/attach_points\[0\].y/g;" \
-    -pe "s/has_attach_points_/return 0,0;has_attach_points_/g;" \
-    -pe "s/gobject.GObject.__init__\(self, self._model_filter\)/gobject.GObject.__init__\(self, model=self._model_filter\)/g;" \
-    -pe "s/self._model_filter.set_visible_func/return;self._model_filter.set_visible_func/g;" \
-    -pe "s/buddies_column.set_cell_data_func/return;buddies_column.set_cell_data_func/g;" \
-    -pe "s/Hippo\.cairo_surface_from_gdk_pixbuf/SugarExt\.cairo_surface_from_pixbuf/g;" \
-\
-    -pe "s/import pynotify\n/from gi.repository import Notify\n/g;" \
-    -pe "s/pynotify\./Notify\./g;" \
-\
-    -pe "s/import webkit\n/from gi.repository import WebKit\n/g;" \
-    -pe "s/import clutter\n/from gi.repository import Clutter\n/g;" \
-    -pe "s/from clutter import cogl\n/from gi.repository import Cogl\n/g;" \
-    -pe "s/(?<!\.)clutter\./Clutter\./g;" \
-    -pe "s/(?<!\.)cogl\./Cogl\./g;" \
-    -pe "s/Clutter.ACTOR_/Clutter.ActorFlags./g;" \
-    -pe "s/Clutter.ALLOCATION_/Clutter.AllocationFlags./g;" \
-    -pe "s/Clutter.BIND_/Clutter.BindCoordinate./g;" \
-    -pe "s/Clutter.BIN_ALIGNMENT_/Clutter.BinAlignment./g;" \
-    -pe "s/Clutter.BOX_ALIGNMENT_/Clutter.BoxAlignment./g;" \
-    -pe "s/Clutter.DRAG_/Clutter.DragAxis./g;" \
-    -pe "s/Clutter.EASE_/Clutter.AnimationMode./g;" \
-    -pe "s/Clutter.FEATURE_/Clutter.FeatureFlags./g;" \
-    -pe "s/Clutter.FLOW_/Clutter.FLOW_ORIENTATION./g;" \
-    -pe "s/Clutter.FONT_/Clutter.FontFlags./g;" \
-    -pe "s/Clutter.GRAVITY_/Clutter.Gravity./g;" \
-    -pe "s/Clutter.INTERPOLATION/Clutter.Interpolation./g;" \
-    -pe "s/Clutter.LINEAR/Clutter.AnimationMode.LINEAR/g;" \
-    -pe "s/Clutter.PATH_/Clutter.PathNodeType./g;" \
-    -pe "s/Clutter.PICK_/Clutter.PickMode./g;" \
-    -pe "s/Clutter.REQUEST_/Clutter.RequestMode./g;" \
-    -pe "s/Clutter.ROTATE_/Clutter.RotateDirection./g;" \
-    -pe "s/Clutter.SCRIPT_/Clutter.ScriptError./g;" \
-    -pe "s/Clutter.STAGE_STATE_/Clutter.StageState./g;" \
-    -pe "s/Clutter.TABLE_ALIGNMENT_/Clutter.TableAlignment./g;" \
-    -pe "s/Clutter.TEXTURE_ERROR_/Clutter.TextureError./g;" \
-    -pe "s/Clutter.TEXTURE_/Clutter.TextureFlags./g;" \
-    -pe "s/Clutter.TEXT_/Clutter.TextDirection./g;" \
-    -pe "s/Clutter.TIMELINE_/Clutter.TimelineDirection./g;" \
-    -pe "s/Clutter.UNIT_/Clutter.UnitType./g;" \
-    -pe "s/Clutter.X_AXIS/Clutter.RotateAxis.X_AXIS/g;" \
-    -pe "s/Clutter.Y_AXIS/Clutter.RotateAxis.Y_AXIS/g;" \
-    -pe "s/Clutter.Z_AXIS/Clutter.RotateAxis.Z_AXIS/g;" \
-    -pe "s/Clutter.ENTER/Clutter.EventType.ENTER/g;" \
-    -pe "s/Clutter.LEAVE/Clutter.EventType.LEAVE/g;" \
-    -pe "s/Clutter.BUTTON_PRESS/Clutter.EventType.BUTTON_PRESS/g;" \
-    -pe "s/Clutter.BUTTON_RELEASE/Clutter.EventType.BUTTON_RELEASE/g;" \
-    -pe "s/Clutter.KEY_PRESS/Clutter.EventType.KEY_PRESS/g;" \
-    -pe "s/Clutter.KEY_RELEASE/Clutter.EventType.KEY_RELEASE/g;" \
-    -pe "s/Clutter.SCROLL/Clutter.EventType.SCROLL/g;" \
-    -pe "s/Clutter.DELETE/Clutter.EventType.DELETE/g;" \
-    -pe "s/Clutter.CLIENT_MESSAGE/Clutter.EventType.CLIENT_MESSAGE/g;" \
-    -pe "s/Clutter.DESTROY_NOTIFY/Clutter.EventType.DESTROY_NOTIFY/g;" \
-    -pe "s/Clutter.STAGE_STATE/Clutter.EventType.STAGE_STATE/g;" \
-    -pe "s/Clutter.MOTION/Clutter.EventType.MOTION/g;" \
-    -pe "s/Clutter.BUTTON1_MASK/Clutter.ModifierType.BUTTON1_MASK/g;" \
-    -pe "s/Clutter.BUTTON2_MASK/Clutter.ModifierType.BUTTON2_MASK/g;" \
-    -pe "s/Clutter.BUTTON3_MASK/Clutter.ModifierType.BUTTON3_MASK/g;" \
-    -pe "s/Clutter.BUTTON4_MASK/Clutter.ModifierType.BUTTON4_MASK/g;" \
-    -pe "s/Clutter.BUTTON5_MASK/Clutter.ModifierType.BUTTON5_MASK/g;" \
-    -pe "s/Clutter.CONTROL_MASK/Clutter.ModifierType.CONTROL_MASK/g;" \
-    -pe "s/Clutter.HYPER_MASK/Clutter.ModifierType.HYPER_MASK/g;" \
-    -pe "s/Clutter.LOCK_MASK/Clutter.ModifierType.LOCK_MASK/g;" \
-    -pe "s/Clutter.META_MASK/Clutter.ModifierType.META_MASK/g;" \
-    -pe "s/Clutter.MOD1_MASK/Clutter.ModifierType.MOD1_MASK/g;" \
-    -pe "s/Clutter.MOD2_MASK/Clutter.ModifierType.MOD2_MASK/g;" \
-    -pe "s/Clutter.MOD3_MASK/Clutter.ModifierType.MOD3_MASK/g;" \
-    -pe "s/Clutter.MOD4_MASK/Clutter.ModifierType.MOD4_MASK/g;" \
-    -pe "s/Clutter.MOD5_MASK/Clutter.ModifierType.MOD5_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_MASK/Clutter.ModifierType.MODIFIER_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_13_MASK/Clutter.ModifierType.MODIFIER_RESERVED_13_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_14_MASK/Clutter.ModifierType.MODIFIER_RESERVED_14_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_15_MASK/Clutter.ModifierType.MODIFIER_RESERVED_15_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_16_MASK/Clutter.ModifierType.MODIFIER_RESERVED_16_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_17_MASK/Clutter.ModifierType.MODIFIER_RESERVED_17_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_18_MASK/Clutter.ModifierType.MODIFIER_RESERVED_18_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_19_MASK/Clutter.ModifierType.MODIFIER_RESERVED_19_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_20_MASK/Clutter.ModifierType.MODIFIER_RESERVED_20_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_21_MASK/Clutter.ModifierType.MODIFIER_RESERVED_21_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_22_MASK/Clutter.ModifierType.MODIFIER_RESERVED_22_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_23_MASK/Clutter.ModifierType.MODIFIER_RESERVED_23_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_24_MASK/Clutter.ModifierType.MODIFIER_RESERVED_24_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_25_MASK/Clutter.ModifierType.MODIFIER_RESERVED_25_MASK/g;" \
-    -pe "s/Clutter.MODIFIER_RESERVED_29_MASK/Clutter.ModifierType.MODIFIER_RESERVED_29_MASK/g;" \
-    -pe "s/Clutter.RELEASE_MASK/Clutter.ModifierType.RELEASE_MASK/g;" \
-    -pe "s/Clutter.SHIFT_MASK/Clutter.ModifierType.SHIFT_MASK/g;" \
-    -pe "s/Clutter.SUPER_MASK/Clutter.ModifierType.SUPER_MASK/g;" \
-\
-    -pe "s/import gst\n/from gi.repository import Gst\n/g;" \
-    -pe "s/(?<!\.)gst\./Gst\./g;" \
-    -pe "s/Gst.element_factory_find/Gst.ElementFactory.find/g;" \
-    -pe "s/Gst.element_factory_make/Gst.ElementFactory.make/g;" \
-    -pe "s/Gst.caps_from_string/Gst.Caps.from_string/g;" \
-    -pe "s/Gst.STATE_CHANGE_/Gst.StateChangeReturn./g;" \
-    -pe "s/Gst.STATE_/Gst.State./g;" \
-    -pe "s/Gst.MESSAGE_/Gst.MessageType./g;" \
-    -pe "s/Gst.FORMAT_/Gst.Format./g;" \
-    -pe "s/Gst.SEEK_FLAG_/Gst.SeekFlags./g;" \
-    -pe "s/Gst.SEEK_TYPE_/Gst.SeekType./g;" \
-    -pe "s/Gst.LEVEL_/Gst.DebugLevel./g;" \
-    -pe "s/Gst.URI_/Gst.URIType./g;" \
-    -pe "s/Gst.element_make_from_uri/Gst.Element.make_from_uri/g;" \
-    -pe "s/Gst.event_new_seek/Gst.Event.new_seek/g;" \
-    -pe "s/Gst.GhostPad\(/Gst.GhostPad.new\(/g;" \
-    $f
-done
-
-
diff --git a/pygobject-3.0-uninstalled.pc.in b/pygobject-3.0-uninstalled.pc.in
deleted file mode 100644 (file)
index 4cec178..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# you can use the --variable=pygobjectincludedir argument to
-# pkg-config to get this value. You might want to use this to
-# install additional headers.
-pygobjectincludedir=${pcfiledir}/gi/_gobject
-overridesdir=${pcfiledir}/gi/overrides
-
-Name: PyGObject
-Description: Python bindings for GObject
-Requires: gobject-2.0
-Requires.private: @LIBFFI_PC@
-Version: @VERSION@
-Cflags: -I${pcfiledir}/gi/_gobject
index d366a2ae66db1d6838f51c01ec0848607b697ccc..63f184ff3c50ab6e3b97e50838909cd97abba942 100644 (file)
@@ -64,4 +64,11 @@ PyGObject now dynamically accesses any GObject libraries that uses GObject Intro
       <gnome:userid>sfeltman</gnome:userid>
     </foaf:Person>
   </maintainer>
+  <maintainer>
+    <foaf:Person>
+      <foaf:name>Christoph Reiter</foaf:name>
+      <foaf:mbox rdf:resource="mailto:creiter@src.gnome.org" />
+      <gnome:userid>creiter</gnome:userid>
+    </foaf:Person>
+  </maintainer>
 </Project>
old mode 100755 (executable)
new mode 100644 (file)
index 0da9ed9..af3aa5c
--- a/setup.py
+++ b/setup.py
 #!/usr/bin/env python
+# Copyright 2017 Christoph Reiter <reiter.christoph@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
 
+"""
+ATTENTION DISTRO PACKAGERS: This is not a valid replacement for autotools.
+It does not install headers, pkgconfig files and does not support running
+tests. Its main use case atm is installation in virtualenvs and via pip.
+"""
 
+import io
 import os
 import re
-import subprocess
 import sys
+import errno
+import subprocess
+import tarfile
+from email import parser
+
+from setuptools import setup, find_packages
+from distutils.core import Extension, Distribution
+from distutils.ccompiler import new_compiler
+from distutils import dir_util
+
+
+def get_command_class(name):
+    # Returns the right class for either distutils or setuptools
+    return Distribution({}).get_command_class(name)
+
+
+def get_pycairo_pkg_config_name():
+    return "py3cairo" if sys.version_info[0] == 3 else "pycairo"
+
 
-from distutils.command.build import build as orig_build
-from setuptools.command.build_ext import build_ext as orig_build_ext
-from setuptools.command.build_py import build_py as orig_build_py
-from setuptools import setup, Extension
+def get_version_requirement(conf_dir, pkg_config_name):
+    """Given a pkg-config module name gets the minimum version required"""
 
+    if pkg_config_name in ["cairo", "cairo-gobject"]:
+        return "0"
 
-with open("configure.ac", "r") as h:
-    version = ".".join(re.findall("pygobject_[^\s]+_version,\s*(\d+)\)", h.read()))
+    mapping = {
+        "gobject-introspection-1.0": "introspection",
+        "glib-2.0": "glib",
+        "gio-2.0": "gio",
+        get_pycairo_pkg_config_name(): "pycairo",
+        "libffi": "libffi",
+    }
+    assert pkg_config_name in mapping
 
+    configure_ac = os.path.join(conf_dir, "configure.ac")
+    with io.open(configure_ac, "r", encoding="utf-8") as h:
+        text = h.read()
+        conf_name = mapping[pkg_config_name]
+        res = re.findall(
+            r"%s_required_version,\s*([\d\.]+)\)" % conf_name, text)
+        assert len(res) == 1
+        return res[0]
 
-def makedirs(dirpath):
-    """Safely make directories
 
-    By default, os.makedirs fails if the directory already exists.
+def parse_versions(conf_dir):
+    configure_ac = os.path.join(conf_dir, "configure.ac")
+    with io.open(configure_ac, "r", encoding="utf-8") as h:
+        version = re.findall(r"pygobject_[^\s]+_version,\s*(\d+)\)", h.read())
+        assert len(version) == 3
 
-    Python 3.2 introduced the `exist_ok` argument, but we can't use it because
-    we want to keep supporting Python 2 for some time.
+    versions = {
+        "PYGOBJECT_MAJOR_VERSION": version[0],
+        "PYGOBJECT_MINOR_VERSION": version[1],
+        "PYGOBJECT_MICRO_VERSION": version[2],
+        "VERSION": ".".join(version),
+    }
+    return versions
+
+
+def parse_pkg_info(conf_dir):
+    """Returns an email.message.Message instance containing the content
+    of the PKG-INFO file. The version info is parsed from configure.ac
     """
-    import errno
 
-    try:
-        os.makedirs(dirpath)
+    versions = parse_versions(conf_dir)
+
+    pkg_info = os.path.join(conf_dir, "PKG-INFO.in")
+    with io.open(pkg_info, "r", encoding="utf-8") as h:
+        text = h.read()
+        for key, value in versions.items():
+            text = text.replace("@%s@" % key, value)
+
+    p = parser.Parser()
+    message = p.parse(io.StringIO(text))
+    return message
+
+
+def _run_pkg_config(args):
+    command = ["pkg-config"] + args
+
+    # Add $prefix/share/pkgconfig to PKG_CONFIG_PATH so we use the right
+    # pycairo in case we are in a virtualenv
+    env = dict(os.environ)
+    paths = env.get("PKG_CONFIG_PATH", "").split(os.pathsep)
+    paths = [p for p in paths if p]
+    paths.insert(0, os.path.join(sys.prefix, "share", "pkgconfig"))
+    env["PKG_CONFIG_PATH"] = os.pathsep.join(paths)
 
+    try:
+        return subprocess.check_output(command, env=env)
     except OSError as e:
-        if e.errno == errno.EEXIST:
-            return
+        if e.errno == errno.ENOENT:
+            raise SystemExit(
+                "%r not found.\nArguments: %r" % (command[0], command))
+        raise SystemExit(e)
+    except subprocess.CalledProcessError as e:
+        raise SystemExit(e)
+
+
+def pkg_config_version_check(pkg, version):
+    _run_pkg_config([
+        "--print-errors",
+        "--exists",
+        '%s >= %s' % (pkg, version),
+    ])
 
-        raise
 
+def pkg_config_parse(opt, pkg):
+    ret = _run_pkg_config([opt, pkg])
+    output = ret.decode()
+    opt = opt[-2:]
+    return [x.lstrip(opt) for x in output.split()]
 
-class Build(orig_build):
-    """Dummy version of distutils build which runs an Autotools build system
-    instead.
+
+du_sdist = get_command_class("sdist")
+
+
+class distcheck(du_sdist):
+    """Creates a tarball and does some additional sanity checks such as
+    checking if the tarballs includes all files and builds.
     """
+
+    def _check_manifest(self):
+        # make sure MANIFEST.in includes all tracked files
+        assert self.get_archive_files()
+
+        if subprocess.call(["git", "status"],
+                           stdout=subprocess.PIPE,
+                           stderr=subprocess.PIPE) != 0:
+            return
+
+        included_files = self.filelist.files
+        assert included_files
+
+        process = subprocess.Popen(
+            ["git", "ls-tree", "-r", "HEAD", "--name-only"],
+            stdout=subprocess.PIPE, universal_newlines=True)
+        out, err = process.communicate()
+        assert process.returncode == 0
+
+        tracked_files = out.splitlines()
+        for ignore in [".gitignore"]:
+            tracked_files.remove(ignore)
+
+        diff = set(tracked_files) - set(included_files)
+        assert not diff, (
+            "Not all tracked files included in tarball, check MANIFEST.in",
+            diff)
+
+    def _check_dist(self):
+        # make sure the tarball builds
+        assert self.get_archive_files()
+
+        distcheck_dir = os.path.join(self.dist_dir, "distcheck")
+        if os.path.exists(distcheck_dir):
+            dir_util.remove_tree(distcheck_dir)
+        self.mkpath(distcheck_dir)
+
+        archive = self.get_archive_files()[0]
+        tfile = tarfile.open(archive, "r:gz")
+        tfile.extractall(distcheck_dir)
+        tfile.close()
+
+        name = self.distribution.get_fullname()
+        extract_dir = os.path.join(distcheck_dir, name)
+
+        old_pwd = os.getcwd()
+        os.chdir(extract_dir)
+        try:
+            self.spawn([sys.executable, "setup.py", "build"])
+            self.spawn([sys.executable, "setup.py", "install",
+                        "--root", "../prefix", "--record", "../log.txt"])
+        finally:
+            os.chdir(old_pwd)
+
     def run(self):
-        srcdir = os.getcwd()
-        builddir = os.path.join(srcdir, self.build_temp)
-        makedirs(builddir)
-        configure = os.path.join(srcdir, 'configure')
-
-        if not os.path.exists(configure):
-            configure = os.path.join(srcdir, 'autogen.sh')
-
-        subprocess.check_call([
-                configure,
-                'PYTHON=%s' % sys.executable,
-                # Put the documentation, etc. out of the way: we only want
-                # the Python code and extensions
-                '--prefix=' + os.path.join(builddir, 'prefix'),
-            ],
-            cwd=builddir)
-        make_args = [
-            'pythondir=%s' % os.path.join(srcdir, self.build_lib),
-            'pyexecdir=%s' % os.path.join(srcdir, self.build_lib),
-        ]
-        subprocess.check_call(['make', '-C', builddir] + make_args)
-        subprocess.check_call(['make', '-C', builddir, 'install'] + make_args)
-
-
-class BuildExt(orig_build_ext):
-    def run(self):
-        pass
+        du_sdist.run(self)
+        self._check_manifest()
+        self._check_dist()
+
+
+du_build_ext = get_command_class("build_ext")
+
+
+class build_ext(du_build_ext):
+
+    def initialize_options(self):
+        du_build_ext.initialize_options(self)
+        self.compiler_type = None
+
+    def finalize_options(self):
+        du_build_ext.finalize_options(self)
+        self.compiler_type = new_compiler(compiler=self.compiler).compiler_type
+
+    def _write_config_h(self):
+        script_dir = os.path.dirname(os.path.realpath(__file__))
+        target = os.path.join(script_dir, "config.h")
+        versions = parse_versions(script_dir)
+        with io.open(target, 'w', encoding="utf-8") as h:
+            h.write("""
+/* Configuration header created by setup.py - do not edit */
+#ifndef _CONFIG_H
+#define _CONFIG_H 1
+
+#define PYGOBJECT_MAJOR_VERSION %(PYGOBJECT_MAJOR_VERSION)s
+#define PYGOBJECT_MINOR_VERSION %(PYGOBJECT_MINOR_VERSION)s
+#define PYGOBJECT_MICRO_VERSION %(PYGOBJECT_MICRO_VERSION)s
+#define VERSION "%(VERSION)s"
+
+#endif /* _CONFIG_H */
+""" % versions)
 
+    def _setup_extensions(self):
+        ext = {e.name: e for e in self.extensions}
+        script_dir = os.path.dirname(os.path.realpath(__file__))
+
+        msvc_libraries = {
+            "glib-2.0": ["glib-2.0"],
+            "gio-2.0": ["gio-2.0", "gobject-2.0", "glib-2.0"],
+            "gobject-introspection-1.0":
+                ["girepository-1.0", "gobject-2.0", "glib-2.0"],
+            get_pycairo_pkg_config_name(): ["cairo"],
+            "cairo": ["cairo"],
+            "cairo-gobject":
+                ["cairo-gobject", "cairo", "gobject-2.0", "glib-2.0"],
+            "libffi": ["ffi"],
+        }
+
+        def add_dependency(ext, name):
+            fallback_libs = msvc_libraries[name]
+
+            if self.compiler_type == "msvc":
+                # assume that INCLUDE and LIB contains the right paths
+                ext.libraries += fallback_libs
+
+                # The PyCairo header is installed in a subdir of the
+                # Python installation that we are building for, so
+                # deduce that include path here, and use it
+                ext.include_dirs += [
+                    os.path.join(sys.prefix, "include", "pycairo")]
+            else:
+                min_version = get_version_requirement(script_dir, name)
+                pkg_config_version_check(name, min_version)
+                ext.include_dirs += pkg_config_parse("--cflags-only-I", name)
+                ext.library_dirs += pkg_config_parse("--libs-only-L", name)
+                ext.libraries += pkg_config_parse("--libs-only-l", name)
+
+        gi_ext = ext["gi._gi"]
+        add_dependency(gi_ext, "glib-2.0")
+        add_dependency(gi_ext, "gio-2.0")
+        add_dependency(gi_ext, "gobject-introspection-1.0")
+        add_dependency(gi_ext, "libffi")
+
+        gi_cairo_ext = ext["gi._gi_cairo"]
+        add_dependency(gi_cairo_ext, "glib-2.0")
+        add_dependency(gi_cairo_ext, "gio-2.0")
+        add_dependency(gi_cairo_ext, "gobject-introspection-1.0")
+        add_dependency(gi_cairo_ext, "libffi")
+        add_dependency(gi_cairo_ext, "cairo")
+        add_dependency(gi_cairo_ext, "cairo-gobject")
+        add_dependency(gi_cairo_ext, get_pycairo_pkg_config_name())
 
-class BuildPy(orig_build_py):
     def run(self):
-        pass
-
-
-setup(
-    name='pygobject',
-    version=version,
-    description='Python bindings for GObject Introspection',
-    maintainer='The pygobject maintainers',
-    maintainer_email='http://mail.gnome.org/mailman/listinfo/python-hackers-list',
-    download_url='http://download.gnome.org/sources/pygobject/',
-    url='https://wiki.gnome.org/Projects/PyGObject',
-    packages=['gi', 'pygtkcompat'],
-    ext_modules=[
-        Extension(
-            '_gi', sources=['gi/gimodule.c'])
+        self._write_config_h()
+        self._setup_extensions()
+        du_build_ext.run(self)
+
+
+def main():
+    script_dir = os.path.dirname(os.path.realpath(__file__))
+    pkginfo = parse_pkg_info(script_dir)
+    gi_dir = os.path.join(script_dir, "gi")
+
+    sources = [
+        os.path.join("gi", n) for n in os.listdir(gi_dir)
+        if os.path.splitext(n)[-1] == ".c"
+    ]
+    cairo_sources = [os.path.join("gi", "pygi-foreign-cairo.c")]
+    for s in cairo_sources:
+        sources.remove(s)
+
+    gi_ext = Extension(
+        name='gi._gi',
+        sources=sources,
+        include_dirs=[script_dir, gi_dir],
+        define_macros=[("HAVE_CONFIG_H", None)],
+    )
+
+    gi_cairo_ext = Extension(
+        name='gi._gi_cairo',
+        sources=cairo_sources,
+        include_dirs=[script_dir, gi_dir],
+        define_macros=[("HAVE_CONFIG_H", None)],
+    )
+
+    setup(
+        name=pkginfo["Name"],
+        version=pkginfo["Version"],
+        description=pkginfo["Summary"],
+        url=pkginfo["Home-page"],
+        author=pkginfo["Author"],
+        author_email=pkginfo["Author-email"],
+        maintainer=pkginfo["Maintainer"],
+        maintainer_email=pkginfo["Maintainer-email"],
+        license=pkginfo["License"],
+        download_url=pkginfo["Download-url"],
+        long_description=pkginfo["Description"],
+        platforms=pkginfo.get_all("Platform"),
+        classifiers=pkginfo.get_all("Classifier"),
+        packages=find_packages(script_dir),
+        ext_modules=[
+            gi_ext,
+            gi_cairo_ext,
         ],
-    license='LGPL',
-    classifiers=[
-        'Development Status :: 5 - Production/Stable',
-        'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
-        'Programming Language :: C',
-        'Programming Language :: Python :: 2',
-        'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: Implementation :: CPython',
-    ],
-    cmdclass={
-        'build': Build,
-        'build_py': BuildPy,
-        'build_ext': BuildExt,
-    },
-)
+        cmdclass={
+            "build_ext": build_ext,
+            "distcheck": distcheck,
+        },
+        install_requires=[
+            "pycairo>=%s" % get_version_requirement(
+                script_dir, get_pycairo_pkg_config_name()),
+        ],
+    )
+
+
+if __name__ == "__main__":
+    main()
index ff10433c6eda1185cb4d092f38825993bfdb75ab..aef0528a0075edbe21ce3bfe8954a47442fcf875 100644 (file)
@@ -144,6 +144,7 @@ EXTRA_DIST = \
        test_docstring.py \
        test_repository.py \
        test_resulttuple.py \
+       test_ossig.py \
        compat_test_pygtk.py \
        gi/__init__.py \
        gi/overrides/__init__.py \
index f070538c5cf615aabcc11b57babd9b24c176ab04..d7e307baf80d380fae700cb26360d2ba44df3264 100644 (file)
@@ -493,6 +493,7 @@ EXTRA_DIST = \
        test_docstring.py \
        test_repository.py \
        test_resulttuple.py \
+       test_ossig.py \
        compat_test_pygtk.py \
        gi/__init__.py \
        gi/overrides/__init__.py \
index 03787816c8b9f9a7f1977223ee0f98cf1938f4d1..ac97e057b57321a71a528e4cb08749cac641836f 100644 (file)
@@ -7,6 +7,7 @@ import traceback
 import ctypes
 import warnings
 import sys
+import os
 
 from gi.repository import Regress as Everything
 from gi.repository import GObject
@@ -19,6 +20,7 @@ try:
 except:
     Gtk = None
 
+from compathelper import PY3
 from helper import capture_exceptions
 
 
@@ -246,7 +248,11 @@ class TestEverything(unittest.TestCase):
         self.assertEqual(Everything.test_utf8_inout(const_str), noconst_str)
 
     def test_filename_return(self):
-        self.assertEqual(Everything.test_filename_return(), ['åäö', '/etc/fstab'])
+        if PY3 and os.name != "nt":
+            result = [os.fsdecode(b'\xc3\xa5\xc3\xa4\xc3\xb6'), '/etc/fstab']
+        else:
+            result = ['åäö', '/etc/fstab']
+        self.assertEqual(Everything.test_filename_return(), result)
 
     def test_int_out_utf8(self):
         # returns g_utf8_strlen() in out argument
index d1b0cfd819e95dcfe28b4ff60de17dcdbdecb948..39aaf0c66f444bf3a17fb80e0f21aa471404cd49 100644 (file)
@@ -695,6 +695,13 @@ class TestFilename(unittest.TestCase):
     @unittest.skipIf(os.name == "nt", "fixme")
     def test_filename_in(self):
         fname = os.path.join(self.workdir, u'testäø.txt')
+
+        try:
+            os.path.exists(fname)
+        except ValueError:
+            # non-unicode fs encoding
+            return
+
         self.assertRaises(GLib.GError, GLib.file_get_contents, fname)
 
         with open(fname.encode('UTF-8'), 'wb') as f:
@@ -711,8 +718,15 @@ class TestFilename(unittest.TestCase):
     @unittest.skipIf(os.name == "nt", "fixme")
     def test_filename_out(self):
         self.assertRaises(GLib.GError, GLib.Dir.make_tmp, 'test')
+        name = 'testäø.XXXXXX'
+
+        try:
+            os.path.exists(name)
+        except ValueError:
+            # non-unicode fs encoding
+            return
 
-        dirname = GLib.Dir.make_tmp('testäø.XXXXXX')
+        dirname = GLib.Dir.make_tmp(name)
         self.assertTrue(os.path.sep + 'testäø.' in dirname, dirname)
         self.assertTrue(os.path.isdir(dirname))
         os.rmdir(dirname)
@@ -868,7 +882,16 @@ class TestFilename(unittest.TestCase):
         if os.name != "nt":
             paths.append((wdb, b"\xff\xfe-5"))
 
+        def valid_path(p):
+            try:
+                os.path.exists(p)
+            except ValueError:
+                return False
+            return True
+
         for (d, path) in paths:
+            if not valid_path(path):
+                continue
             path = os.path.join(d, path)
             with open(path, "wb"):
                 self.assertTrue(GIMarshallingTests.filename_exists(path))
diff --git a/tests/test_ossig.py b/tests/test_ossig.py
new file mode 100644 (file)
index 0000000..bf218b8
--- /dev/null
@@ -0,0 +1,173 @@
+# -*- coding: utf-8 -*-
+# Copyright 2017 Christoph Reiter
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+import os
+import signal
+import unittest
+import threading
+from contextlib import contextmanager
+
+from gi.repository import Gtk, Gio, GLib
+from gi._ossighelper import wakeup_on_signal, register_sigint_fallback
+
+
+class TestOverridesWakeupOnAlarm(unittest.TestCase):
+
+    @contextmanager
+    def _run_with_timeout(self, timeout, abort_func):
+        failed = []
+
+        def fail():
+            abort_func()
+            failed.append(1)
+            return True
+
+        fail_id = GLib.timeout_add(timeout, fail)
+        try:
+            yield
+        finally:
+            GLib.source_remove(fail_id)
+        self.assertFalse(failed)
+
+    def test_basic(self):
+        self.assertEqual(signal.set_wakeup_fd(-1), -1)
+        with wakeup_on_signal():
+            pass
+        self.assertEqual(signal.set_wakeup_fd(-1), -1)
+
+    def test_in_thread(self):
+        failed = []
+
+        def target():
+            try:
+                with wakeup_on_signal():
+                    pass
+            except:
+                failed.append(1)
+
+        t = threading.Thread(target=target)
+        t.start()
+        t.join(5)
+        self.assertFalse(failed)
+
+    @unittest.skipIf(os.name == "nt", "not on Windows")
+    def test_glib_mainloop(self):
+        loop = GLib.MainLoop()
+        signal.signal(signal.SIGALRM, lambda *args: loop.quit())
+        GLib.idle_add(signal.setitimer, signal.ITIMER_REAL, 0.001)
+
+        with self._run_with_timeout(2000, loop.quit):
+            loop.run()
+
+    @unittest.skipIf(os.name == "nt", "not on Windows")
+    def test_gio_application(self):
+        app = Gio.Application()
+        signal.signal(signal.SIGALRM, lambda *args: app.quit())
+        GLib.idle_add(signal.setitimer, signal.ITIMER_REAL, 0.001)
+
+        with self._run_with_timeout(2000, app.quit):
+            app.hold()
+            app.connect("activate", lambda *args: None)
+            app.run()
+
+    @unittest.skipIf(os.name == "nt", "not on Windows")
+    def test_gtk_main(self):
+        signal.signal(signal.SIGALRM, lambda *args: Gtk.main_quit())
+        GLib.idle_add(signal.setitimer, signal.ITIMER_REAL, 0.001)
+
+        with self._run_with_timeout(2000, Gtk.main_quit):
+            Gtk.main()
+
+    @unittest.skipIf(os.name == "nt", "not on Windows")
+    def test_gtk_dialog_run(self):
+        w = Gtk.Window()
+        d = Gtk.Dialog(transient_for=w)
+        signal.signal(signal.SIGALRM, lambda *args: d.destroy())
+        GLib.idle_add(signal.setitimer, signal.ITIMER_REAL, 0.001)
+
+        with self._run_with_timeout(2000, d.destroy):
+            d.run()
+
+
+class TestSigintFallback(unittest.TestCase):
+
+    def setUp(self):
+        self.assertEqual(
+            signal.getsignal(signal.SIGINT), signal.default_int_handler)
+
+    def tearDown(self):
+        self.assertEqual(
+            signal.getsignal(signal.SIGINT), signal.default_int_handler)
+
+    def test_replace_handler_and_restore_nested(self):
+        with register_sigint_fallback(lambda: None):
+            new_handler = signal.getsignal(signal.SIGINT)
+            self.assertNotEqual(new_handler, signal.default_int_handler)
+            with register_sigint_fallback(lambda: None):
+                self.assertTrue(signal.getsignal(signal.SIGINT) is new_handler)
+        self.assertEqual(
+            signal.getsignal(signal.SIGINT), signal.default_int_handler)
+
+    def test_no_replace_if_not_default(self):
+        new_handler = lambda *args: None
+        signal.signal(signal.SIGINT, new_handler)
+        try:
+            with register_sigint_fallback(lambda: None):
+                self.assertTrue(signal.getsignal(signal.SIGINT) is new_handler)
+                with register_sigint_fallback(lambda: None):
+                    self.assertTrue(
+                        signal.getsignal(signal.SIGINT) is new_handler)
+            self.assertTrue(signal.getsignal(signal.SIGINT) is new_handler)
+        finally:
+            signal.signal(signal.SIGINT, signal.default_int_handler)
+
+    def test_noop_in_threads(self):
+        failed = []
+
+        def target():
+            try:
+                with register_sigint_fallback(lambda: None):
+                    with register_sigint_fallback(lambda: None):
+                        self.assertTrue(
+                            signal.getsignal(signal.SIGINT) is
+                            signal.default_int_handler)
+            except:
+                failed.append(1)
+
+        t = threading.Thread(target=target)
+        t.start()
+        t.join(5)
+        self.assertFalse(failed)
+
+    @unittest.skipIf(os.name == "nt", "not on Windows")
+    def test_no_replace_if_set_by_glib(self):
+        id_ = GLib.unix_signal_add(
+            GLib.PRIORITY_DEFAULT, signal.SIGINT, lambda *args: None)
+        try:
+            # signal.getsignal() doesn't pick up that unix_signal_add()
+            # has changed the handler, but we should anyway.
+            self.assertEqual(
+                signal.getsignal(signal.SIGINT), signal.default_int_handler)
+            with register_sigint_fallback(lambda: None):
+                self.assertEqual(
+                    signal.getsignal(signal.SIGINT),
+                    signal.default_int_handler)
+            self.assertEqual(
+                signal.getsignal(signal.SIGINT), signal.default_int_handler)
+        finally:
+            GLib.source_remove(id_)
+            signal.signal(signal.SIGINT, signal.SIG_DFL)
+            signal.signal(signal.SIGINT, signal.default_int_handler)
index 61b7dc0ac53ec1c3d808f8a9e026afd06765973b..769e439c4970bcb97b1d19bb0c17f3db23ed374d 100644 (file)
@@ -1080,6 +1080,12 @@ class TestTreeModel(unittest.TestCase):
         tree_store.insert(None, 1)
         self.assertEqual(signals, ['row-inserted'])
 
+        # One set one signal
+        signals.pop()
+        tree_iter = tree_store.append(None, (10, False))
+        tree_store.set(tree_iter, (0, 1), (20, True))
+        self.assertEqual(signals, ['row-inserted', 'row-changed'])
+
     def test_list_store(self):
         class TestPyObject(object):
             pass
@@ -1337,6 +1343,12 @@ class TestTreeModel(unittest.TestCase):
         list_store.insert(1)
         self.assertEqual(signals, ['row-inserted'])
 
+        # One set one signal
+        signals.pop()
+        tree_iter = list_store.append((10, False))
+        list_store.set(tree_iter, (0, 1), (20, True))
+        self.assertEqual(signals, ['row-inserted', 'row-changed'])
+
     def test_tree_path(self):
         p1 = Gtk.TreePath()
         p2 = Gtk.TreePath.new_first()
diff --git a/tools/pygi-convert.sh b/tools/pygi-convert.sh
new file mode 100755 (executable)
index 0000000..43af662
--- /dev/null
@@ -0,0 +1,375 @@
+#!/bin/sh
+
+if [ -n "$1" ]; then
+    FILES_TO_CONVERT="$@"
+else
+    FILES_TO_CONVERT="$(find . -name '*.py')"
+fi
+
+for f in $FILES_TO_CONVERT; do
+    perl -i -0 \
+    -pe "s/import gconf\n/from gi.repository import GConf\n/g;" \
+    -pe "s/gconf\./GConf\./g;" \
+    -pe "s/GConf\.client_get_default/GConf.Client.get_default/g;" \
+    -pe "s/GConf\.CLIENT_/GConf.ClientPreloadType./g;" \
+    -pe "s/GConf\.VALUE_/GConf.ValueType./g;" \
+    -pe "s/gconf_client.notify_add\('\/desktop\/sugar\/collaboration\/publish_gadget',/return;gconf_client.notify_add\('\/desktop\/sugar\/collaboration\/publish_gadget',/g;" \
+\
+    -pe "s/import pygtk/import gi/g;" \
+    -pe "s/pygtk.require\('2.0'\)/gi.require_version\('Gtk', '3.0'\)/g;" \
+    -pe "s/pygtk.require\(\"2.0\"\)/gi.require_version\(\"Gtk\", \"3.0\"\)/g;" \
+    -pe "s/import gtk\n/from gi.repository import Gtk\n/g;" \
+    -pe "s/(?<!\.)gtk\./Gtk\./g;" \
+    -pe "s/Gtk.ACCEL_/Gtk.AccelFlags./g;" \
+    -pe "s/Gtk.ARROW_/Gtk.ArrowType./g;" \
+    -pe "s/Gtk.ASSISTANT_PAGE_/Gtk.AssistantPageType./g;" \
+    -pe "s/Gtk.BUTTONBOX_/Gtk.ButtonBoxStyle./g;" \
+    -pe "s/Gtk.BUTTONS_/Gtk.ButtonsType./g;" \
+    -pe "s/Gtk.CELL_RENDERER_MODE_/Gtk.CellRendererMode./g;" \
+    -pe "s/Gtk.CELL_RENDERER_FOCUSED/Gtk.CellRendererState.FOCUSED/g;" \
+    -pe "s/Gtk.CELL_RENDERER_INSENSITIVE/Gtk.CellRendererState.INSENSITIVE/g;" \
+    -pe "s/Gtk.CELL_RENDERER_PRELIT/Gtk.CellRendererState.PRELIT/g;" \
+    -pe "s/Gtk.CELL_RENDERER_SORTED/Gtk.CellRendererState.SORTED/g;" \
+    -pe "s/Gtk.CELL_RENDERER_SELECTED/Gtk.CellRendererState.SELECTED/g;" \
+    -pe "s/Gtk.CORNER_/Gtk.CornerType./g;" \
+    -pe "s/Gtk.DIALOG_/Gtk.DialogFlags./g;" \
+    -pe "s/Gtk.ENTRY_ICON_/Gtk.EntryIconPosition./g;" \
+    -pe "s/Gtk.EXPAND/Gtk.AttachOptions.EXPAND/g;" \
+    -pe "s/Gtk.FALSE/False/g;" \
+    -pe "s/Gtk.FILE_CHOOSER_ACTION_/Gtk.FileChooserAction./g;" \
+    -pe "s/Gtk.FILL/Gtk.AttachOptions.FILL/g;" \
+    -pe "s/Gtk.ICON_LOOKUP_/Gtk.IconLookupFlags./g;" \
+    -pe "s/Gtk.ICON_SIZE_/Gtk.IconSize./g;" \
+    -pe "s/Gtk.IMAGE_/Gtk.ImageType./g;" \
+    -pe "s/Gtk.JUSTIFY_/Gtk.Justification./g;" \
+    -pe "s/Gtk.MESSAGE_/Gtk.MessageType./g;" \
+    -pe "s/Gtk.MOVEMENT_/Gtk.MovementStep./g;" \
+    -pe "s/Gtk.ORIENTATION_/Gtk.Orientation./g;" \
+    -pe "s/Gtk.POLICY_/Gtk.PolicyType./g;" \
+    -pe "s/Gtk.POS_/Gtk.PositionType./g;" \
+    -pe "s/Gtk.RECENT_FILTER_/Gtk.RecentFilterFlags./g;" \
+    -pe "s/Gtk.RECENT_SORT_/Gtk.RecentSortType./g;" \
+    -pe "s/Gtk.RELIEF_/Gtk.ReliefStyle./g;" \
+    -pe "s/Gtk.RESPONSE_/Gtk.ResponseType./g;" \
+    -pe "s/Gtk.SELECTION_/Gtk.SelectionMode./g;" \
+    -pe "s/Gtk.SHADOW_/Gtk.ShadowType./g;" \
+    -pe "s/Gtk.SHADOW_NONE/Gtk.ShadowType.NONE/g;" \
+    -pe "s/Gtk.SHRINK/Gtk.AttachOptions.SHRINK/g;" \
+    -pe "s/Gtk.SIZE_GROUP_/Gtk.SizeGroupMode./g;" \
+    -pe "s/Gtk.SORT_/Gtk.SortType./g;" \
+    -pe "s/Gtk.STATE_/Gtk.StateType./g;" \
+    -pe "s/Gtk.TARGET_/Gtk.TargetFlags./g;" \
+    -pe "s/Gtk.TEXT_DIR_/Gtk.TextDirection./g;" \
+    -pe "s/Gtk.TEXT_SEARCH_/Gtk.TextSearchFlags./g;" \
+    -pe "s/Gtk.TEXT_WINDOW_/Gtk.TextWindowType./g;" \
+    -pe "s/Gtk.TOOLBAR_/Gtk.ToolbarStyle./g;" \
+    -pe "s/Gtk.TREE_MODEL_/Gtk.TreeModelFlags./g;" \
+    -pe "s/Gtk.TREE_VIEW_COLUMN_/Gtk.TreeViewColumnSizing./g;" \
+    -pe "s/Gtk.TREE_VIEW_DROP_/Gtk.TreeViewDropPosition./g;" \
+    -pe "s/Gtk.TRUE/True/g;" \
+    -pe "s/Gtk.WINDOW_/Gtk.WindowType./g;" \
+    -pe "s/Gtk.DEST_DEFAULT_/Gtk.DestDefaults./g;" \
+    -pe "s/Gtk.WIN_POS_/Gtk.WindowPosition./g;" \
+    -pe "s/Gtk.WRAP_/Gtk.WrapMode./g;" \
+    -pe "s/Gtk.UI_MANAGER_/Gtk.UIManagerItemType./g;" \
+    -pe "s/Gtk.accel_map_/Gtk.AccelMap./g;" \
+    -pe "s/Gtk.settings_get_/Gtk.Settings.get_/g;" \
+    -pe "s/Gtk.icon_theme_get_default/Gtk.IconTheme.get_default/g;" \
+    -pe "s/Gtk.recent_manager_get_default/Gtk.RecentManager.get_default/g;" \
+    -pe "s/Gtk.image_new_from_stock/Gtk.Image.new_from_stock/g;" \
+    -pe "s/Gtk.image_new_from_icon_name/Gtk.Image.new_from_icon_name/g;" \
+    -pe "s/Gtk.window_set_default_icon_name/Gtk.Window.set_default_icon_name/g; " \
+    -pe "s/Gtk.combo_box_new_text/Gtk.ComboBoxText/g;" \
+    -pe "s/Gtk.keysyms\./Gdk.KEY_/g;" \
+    -pe "s/set_flags\(Gtk.CAN_DEFAULT\)/set_can_default\(True\)/g;" \
+    -pe "s/.flags\(\) & Gtk.MAPPED/.get_mapped\(\)/g;" \
+    -pe "s/.flags\(\) & Gtk.REALIZED/.get_realized\(\)/g;" \
+    -pe "s/\.window\.set_type_hint/.set_type_hint/g;" \
+    -pe "s/\.window\.set_skip_taskbar_hint/.set_skip_taskbar_hint/g;" \
+    -pe "s/\.window\.set_transient_for/.set_transient_for/g;" \
+    -pe "s/Gtk.Alignment\(/Gtk.Alignment.new\(/g;" \
+    -pe "#s/Gtk.Window.__init__\(self\)/Gtk.Window.__init__\(Gtk.WindowType.TOPLEVEL\)/g;" \
+    -pe "s/\.child([^_A-Za-z])/.get_child\(\)\1/g;" \
+\
+    -pe "s/column.pack_start\(([^,\)]*)\)/column.pack_start\(\1, True\)/g;" \
+    -pe "s/pack_start\(([^,\)]*)\)/pack_start\(\1, True, True, 0\)/g;" \
+    -pe "s/pack_start\(([^,]*), fill=([^,\)]*)\)/pack_start\(\1, True, \2, 0\)/g;" \
+    -pe "s/pack_start\(([^,]*), expand=([^,\)]*)\)/pack_start\(\1, \2, True, 0\)/g;" \
+    -pe "s/pack_start\(([^,]*),(\s*)padding=([A-Za-z0-9._]*)\)/pack_start\(\1,\2True, True,\2\3\)/g;" \
+    -pe "s/column.pack_end\(([^,\)]*)\)/column.pack_end\(\1, True\)/g;" \
+    -pe "s/pack_end\(([^,\)]*)\)/pack_end\(\1, True, True, 0\)/g;" \
+    -pe "s/pack_end\(([^,]*), fill=([^,\)]*)\)/pack_end\(\1, True, \2, 0\)/g;" \
+    -pe "s/pack_end\(([^,]*), expand=([^,\)]*)\)/pack_end\(\1, \2, True, 0\)/g;" \
+    -pe "s/pack_end\(([^,]*),(\s*)padding=([A-Za-z0-9._]*)\)/pack_end\(\1,\2True, True,\2\3\)/g;" \
+    -pe "#s/Gtk.HBox\(\)/Gtk.HBox\(False, 0\)/g;" \
+    -pe "#s/Gtk.VBox\(\)/Gtk.VBox\(False, 0\)/g;" \
+    -pe "s/Gtk.Label\s*\(([^,\)]+)\)/Gtk.Label\(label=\1\)/g;" \
+    -pe "s/Gtk.AccelLabel\s*\(([^,\)]+)\)/Gtk.AccelLabel\(label=\1\)/g;" \
+    -pe "s/Gtk.((?:Accel)?Label)\(label=label=/Gtk.\1\(label=/g;" \
+    -pe "s/len\(self._content.get_children\(\)\) > 0/self._content.get_children\(\)/g;" \
+    -pe "s/len\(self.menu.get_children\(\)\) > 0/self.menu.get_children\(\)/g;" \
+    -pe "s/import gobject\n/from gi.repository import GObject\n/g;" \
+    -pe "s/Gtk\..*\.__init__/gobject.GObject.__init__/g;" \
+\
+    -pe "s/rsvg.Handle\s*\(data=([^,\)]+)\)/Rsvg.Handle.new_from_data(\1)/g;" \
+\
+    -pe "s/from gtk import gdk\n/from gi.repository import Gdk\n/g;" \
+    -pe "s/import gtk.gdk as gdk\n/from gi.repository import Gdk\n/g;" \
+    -pe "s/Gtk.gdk.x11_/GdkX11.x11_/g;" \
+    -pe "s/Gtk.gdk\./Gdk\./g;" \
+    -pe "s/(?<!\.)gdk\./Gdk\./g;" \
+    -pe "s/Gdk.screen_width/Gdk.Screen.width/g;" \
+    -pe "s/Gdk.screen_height/Gdk.Screen.height/g;" \
+    -pe "s/Gdk.screen_get_default/Gdk.Screen.get_default/g;" \
+    -pe "s/Gdk.display_get_default/Gdk.Display.get_default/g;" \
+    -pe "s/screen_, x_, y_, modmask = display.get_pointer\(\)/x_, y_, modmask = display.get_pointer\(None\)/g;" \
+    -pe "s/Gdk.WINDOW_TYPE_HINT_/Gdk.WindowTypeHint./g;" \
+    -pe "s/Gdk.SHIFT_MASK/Gdk.ModifierType.SHIFT_MASK/g;" \
+    -pe "s/Gdk.LOCK_MASK/Gdk.ModifierType.LOCK_MASK/g;" \
+    -pe "s/Gdk.CONTROL_MASK/Gdk.ModifierType.CONTROL_MASK/g;" \
+    -pe "s/Gdk.MOD1_MASK/Gdk.ModifierType.MOD1_MASK/g;" \
+    -pe "s/Gdk.MOD2_MASK/Gdk.ModifierType.MOD2_MASK/g;" \
+    -pe "s/Gdk.MOD3_MASK/Gdk.ModifierType.MOD3_MASK/g;" \
+    -pe "s/Gdk.MOD4_MASK/Gdk.ModifierType.MOD4_MASK/g;" \
+    -pe "s/Gdk.MOD5_MASK/Gdk.ModifierType.MOD5_MASK/g;" \
+    -pe "s/Gdk.BUTTON1_MASK/Gdk.ModifierType.BUTTON1_MASK/g;" \
+    -pe "s/Gdk.BUTTON2_MASK/Gdk.ModifierType.BUTTON2_MASK/g;" \
+    -pe "s/Gdk.BUTTON3_MASK/Gdk.ModifierType.BUTTON3_MASK/g;" \
+    -pe "s/Gdk.BUTTON4_MASK/Gdk.ModifierType.BUTTON4_MASK/g;" \
+    -pe "s/Gdk.BUTTON5_MASK/Gdk.ModifierType.BUTTON5_MASK/g;" \
+    -pe "s/Gdk.RELEASE_MASK/Gdk.ModifierType.RELEASE_MASK/g;" \
+    -pe "s/Gdk.MODIFIER_MASK/Gdk.ModifierType.MODIFIER_MASK/g;" \
+    -pe "s/Gdk.([A-Z_0-9]*)_MASK/Gdk.EventMask.\1_MASK/g;" \
+    -pe "s/Gdk.VISIBILITY_FULLY_OBSCURED/Gdk.VisibilityState.FULLY_OBSCURED/g;" \
+    -pe "s/Gdk.NOTIFY_ANCESTOR/Gdk.NotifyType.ANCESTOR/g;" \
+    -pe "s/Gdk.NOTIFY_INFERIOR/Gdk.NotifyType.INFERIOR/g;" \
+    -pe "s/Gdk.NOTIFY_NONLINEAR_VIRTUAL/Gdk.NotifyType.NONLINEAR_VIRTUAL/g;" \
+    -pe "s/Gdk.NOTIFY_NONLINEAR/Gdk.NotifyType.NONLINEAR/g;" \
+    -pe "s/Gdk.NOTIFY_UNKNOWN/Gdk.NotifyType.UNKNOWN/g;" \
+    -pe "s/Gdk.NOTIFY_VIRTUAL/Gdk.NotifyType.VIRTUAL/g;" \
+    -pe "s/Gdk.PROP_MODE_APPEND/Gdk.PropMode.APPEND/g;" \
+    -pe "s/Gdk.PROP_MODE_PREPEND/Gdk.PropMode.PREPEND/g;" \
+    -pe "s/Gdk.PROP_MODE_REPLACE/Gdk.PropMode.REPLACE/g;" \
+    -pe "s/Gdk.BUTTON_PRESS/Gdk.EventType.BUTTON_PRESS/g;" \
+    -pe "s/Gdk.ACTION_/Gdk.DragAction./g;" \
+    -pe "s/Gdk.GRAB_/Gdk.GrabStatus./g;" \
+    -pe "s/Gdk.SCROLL_(DOWN|LEFT|RIGHT|UP)/Gdk.ScrollDirection.\1/g;" \
+    -pe "s/Gdk.([A-Z]+_(PTR|CURSOR))/Gdk.CursorType.\1/g;" \
+    -pe "s/Gdk.(CROSSHAIR)/Gdk.CursorType.\1/g;" \
+    -pe "s/Gdk.(WATCH)/Gdk.CursorType.\1/g;" \
+    -pe "s/Gdk.(ARROW)/Gdk.CursorType.\1/g;" \
+    -pe "s/Gdk.(CLOCK)/Gdk.CursorType.\1/g;" \
+    -pe "s/Gdk.WINDOW_STATE_(ABOVE|BELOW|FOCUSED|FULLSCREEN|ICONIFIED|MAXIMIZED|STICKY|WITHDRAWN)/Gdk.WindowState.\1/g;" \
+    -pe "s/Gdk.Cursor\s*\(/Gdk.Cursor.new\(/g;" \
+    -pe "s/#Gdk.Rectangle\(([^,\)]*), ([^,\)]*), ([^,\)]*), ([^,\)]*)\)/\1, \2, \3, \4/g;" \
+    -pe "s/Gdk.Rectangle//g;" \
+    -pe "s/intersection = child_rect.intersect/intersects_, intersection = child_rect.intersect/g;" \
+    -pe "s/event.state/event.get_state\(\)/g;" \
+\
+    -pe "s/Gdk.pixbuf_/GdkPixbuf.Pixbuf./g;" \
+    -pe "s/Gdk.Pixbuf/GdkPixbuf.Pixbuf/g;" \
+    -pe "s/Gdk.INTERP_/GdkPixbuf.InterpType./g;" \
+    -pe "s/Gdk.COLORSPACE_RGB/GdkPixbuf.Colorspace.RGB/g;" \
+\
+    -pe "s/import pango\n/from gi.repository import Pango\n/g;" \
+    -pe "s/pango\./Pango\./g;" \
+    -pe "s/Pango.ALIGN_/Pango.Alignment./g;" \
+    -pe "s/Pango.ELLIPSIZE_/Pango.EllipsizeMode./g;" \
+    -pe "s/Pango.STYLE_/Pango.Style./g;" \
+    -pe "s/Pango.UNDERLINE_/Pango.Underline./g;" \
+    -pe "s/Pango.WEIGHT_/Pango.Weight./g;" \
+    -pe "s/Pango.WRAP_/Pango.WrapMode./g;" \
+    -pe "s/Pango.TAB_/Pango.TabAlign./g;" \
+\
+    -pe "s/import atk\n/from gi.repository import Atk\n/g;" \
+    -pe "s/atk\./Atk\./g;" \
+    -pe "s/Atk.HYPERLINK_/Atk.HyperlinkStateFlags./g;" \
+    -pe "s/Atk.KEY_EVENT_/Atk.KeyEventType./g;" \
+    -pe "s/Atk.LAYER_/Atk.Layer./g;" \
+    -pe "s/Atk.RELATION_/Atk.RelationType./g;" \
+    -pe "s/Atk.ROLE_/Atk.Role./g;" \
+    -pe "s/Atk.STATE_/Atk.StateType./g;" \
+    -pe "s/Atk.TEXT_ATTR_/Atk.TextAttribute./g;" \
+    -pe "s/Atk.TEXT_BOUNDARY_/Atk.TextBoundary./g;" \
+    -pe "s/Atk.TEXT_CLIP_/Atk.TextClipType./g;" \
+\
+    -pe "s/import gio\n/from gi.repository import Gio\n/g;" \
+    -pe "s/gio\./Gio\./g;" \
+    -pe "s/Gio\.File\(uri=/Gio\.File\.new_for_uri\(/g;" \
+    -pe "s/Gio\.File\(path=/Gio\.File\.new_for_path\(/g;" \
+    -pe "s/Gio.FILE_COPY_/Gio.FileCopyFlags./g;" \
+    -pe "s/Gio.FILE_CREATE_/Gio.FileCreateFlags./g;" \
+    -pe "s/Gio.FILE_MONITOR_EVENT_/Gio.FileMonitorEvent./g;" \
+    -pe "s/Gio.FILE_MONITOR_/Gio.FileMonitorFlags./g;" \
+    -pe "s/Gio.FILE_TYPE_/Gio.FileType./g;" \
+    -pe "s/Gio.FILE_QUERY_INFO_/Gio.FileQueryInfoFlags./g;" \
+    -pe "s/Gio.MOUNT_MOUNT_/Gio.MountMountFlags./g;" \
+    -pe "s/Gio.MOUNT_OPERATION_/Gio.MountOperationResult./g;" \
+    -pe "s/Gio.MOUNT_UNMOUNT_/Gio.MountUnmountFlags./g;" \
+    -pe "s/Gio.OUTPUT_STREAM_SPLICE_/Gio.OutputStreamSpliceFlags./g;" \
+    -pe "s/Gio.vfs_/Gio.Vfs./g;" \
+\
+    -pe "#s/import glib\n/from gi.repository import GLib\n/g;" \
+    -pe "#s/(?<!\.)glib\./GLib\./g;" \
+    -pe "#s/GLib.IO_(ERR|HUP|IN|NVAL|OUT|PRI)/GLib.IOCondition./g;" \
+    -pe "#s/GLib.IO_FLAG_/GLib.IOFlags./g;" \
+    -pe "#s/GLib.IO_STATUS_/GLib.IOStatus./g;" \
+    -pe "#s/GLib.OPTION_ERROR_/GLib.OptionError./g;" \
+    -pe "#s/GLib.OPTION_FLAG_/GLib.OptionFlags./g;" \
+    -pe "#s/GLib.SPAWN_/GLib.SpawnFlags./g;" \
+    -pe "#s/GLib.USER_DIRECTORY_/GLib.UserDirectory.DIRECTORY_/g;" \
+\
+    -pe "s/(?<!\.)gobject\./GObject\./g;" \
+    -pe "s/GObject.SIGNAL_/GObject.SignalFlags./g;" \
+    -pe "s/GObject.TYPE_NONE/None/g;" \
+\
+    -pe "s/import hippo\n/from gi.repository import Hippo\n/g;" \
+    -pe "s/hippo\./Hippo\./g;" \
+    -pe "s/Hippo\..*\.__init__/gobject.GObject.__init__/g;" \
+    -pe "s/Hippo.PACK_/Hippo.PackFlags./g;" \
+    -pe "s/Hippo.ORIENTATION_/Hippo.Orientation./g;" \
+    -pe "#s/insert_sorted\(([^,\)]*), ([^,\)]*), ([^,\)]*)\)/insert_sorted\(\1, \2, \3, None\)/g;" \
+    -pe "s/self\._box\.insert_sorted/#self\._box\.insert_sorted/g;" \
+    -pe "s/self._box.append\(([^,\)]*)\)/self._box.append\(\1, 0\)/g;" \
+    -pe "s/self.append\(self._buddy_icon\)/self.append\(self._buddy_icon, 0\)/g;" \
+    -pe "s/self._box.sort\(([^,\)]*)\)/self._box.sort\(\1, None\)/g;" \
+\
+    -pe "s/import wnck\n/from gi.repository import Wnck\n/g;" \
+    -pe "s/wnck\./Wnck\./g;" \
+    -pe "s/Wnck.screen_get_default/Wnck.Screen.get_default/g;" \
+    -pe "s/Wnck.WINDOW_/Wnck.WindowType./g;" \
+\
+    -pe "s/from sugar import _sugarext\n/from gi.repository import SugarExt\n/g;" \
+    -pe "s/_sugarext\.ICON_ENTRY_/SugarExt.SexyIconEntryPosition./g;" \
+    -pe "s/_sugarext\.IconEntry/SugarExt.SexyIconEntry/g;" \
+    -pe "s/_sugarext\.SMClientXSMP/SugarExt.GsmClientXSMP/g;" \
+    -pe "s/_sugarext\.VolumeAlsa/SugarExt.AcmeVolumeAlsa/g;" \
+    -pe "s/_sugarext\./SugarExt\./g;" \
+\
+    -pe "s/import gtksourceview2\n/from gi.repository import GtkSource\n/g;" \
+    -pe "s/import gtksourceview2 as gsv\n/from gi.repository import GtkSource\n/g;" \
+    -pe "s/gtksourceview2\./GtkSource\./g;" \
+    -pe "s/gsv\./GtkSource\./g;" \
+    -pe "s/GtkSource.DRAW_SPACES_/GtkSource.DrawSpacesFlags./g;" \
+    -pe "s/GtkSource.SMART_HOME_END_/GtkSource.SmartHomeEndType./g;" \
+    -pe "s/GtkSource.style_scheme_manager_get_default/GtkSource.StyleSchemeManager.get_default/g;" \
+    -pe "s/GtkSource.language_manager_get_default/GtkSource.LanguageManager.get_default/g;" \
+\
+    -pe "#s/import cairo\n/from gi.repository import cairo\n/g;" \
+\
+    -pe "s/SugarExt.xsmp_init\(\)/'mec'/g;" \
+    -pe "s/SugarExt.xsmp_run\(\)/#SugarExt.xsmp_run\(\)/g;" \
+    -pe "s/SugarExt.session_create_global\(\)/None #SugarExt.session_create_global\(\)/g;" \
+    -pe "s/self.session.start\(\)/return #self.session.start\(\)/g;" \
+\
+    -pe "s/self._box.sort\(self._layout.compare_activities, None\)/pass #self._box.sort(self._layout.compare_activities, None)/g;" \
+    -pe "s/attach_points = info.get_attach_points/has_attach_points_, attach_points = info.get_attach_points/g;" \
+    -pe "s/attach_points\[0\]\[0\]/attach_points\[0\].x/g;" \
+    -pe "s/attach_points\[0\]\[1\]/attach_points\[0\].y/g;" \
+    -pe "s/has_attach_points_/return 0,0;has_attach_points_/g;" \
+    -pe "s/gobject.GObject.__init__\(self, self._model_filter\)/gobject.GObject.__init__\(self, model=self._model_filter\)/g;" \
+    -pe "s/self._model_filter.set_visible_func/return;self._model_filter.set_visible_func/g;" \
+    -pe "s/buddies_column.set_cell_data_func/return;buddies_column.set_cell_data_func/g;" \
+    -pe "s/Hippo\.cairo_surface_from_gdk_pixbuf/SugarExt\.cairo_surface_from_pixbuf/g;" \
+\
+    -pe "s/import pynotify\n/from gi.repository import Notify\n/g;" \
+    -pe "s/pynotify\./Notify\./g;" \
+\
+    -pe "s/import webkit\n/from gi.repository import WebKit\n/g;" \
+    -pe "s/import clutter\n/from gi.repository import Clutter\n/g;" \
+    -pe "s/from clutter import cogl\n/from gi.repository import Cogl\n/g;" \
+    -pe "s/(?<!\.)clutter\./Clutter\./g;" \
+    -pe "s/(?<!\.)cogl\./Cogl\./g;" \
+    -pe "s/Clutter.ACTOR_/Clutter.ActorFlags./g;" \
+    -pe "s/Clutter.ALLOCATION_/Clutter.AllocationFlags./g;" \
+    -pe "s/Clutter.BIND_/Clutter.BindCoordinate./g;" \
+    -pe "s/Clutter.BIN_ALIGNMENT_/Clutter.BinAlignment./g;" \
+    -pe "s/Clutter.BOX_ALIGNMENT_/Clutter.BoxAlignment./g;" \
+    -pe "s/Clutter.DRAG_/Clutter.DragAxis./g;" \
+    -pe "s/Clutter.EASE_/Clutter.AnimationMode./g;" \
+    -pe "s/Clutter.FEATURE_/Clutter.FeatureFlags./g;" \
+    -pe "s/Clutter.FLOW_/Clutter.FLOW_ORIENTATION./g;" \
+    -pe "s/Clutter.FONT_/Clutter.FontFlags./g;" \
+    -pe "s/Clutter.GRAVITY_/Clutter.Gravity./g;" \
+    -pe "s/Clutter.INTERPOLATION/Clutter.Interpolation./g;" \
+    -pe "s/Clutter.LINEAR/Clutter.AnimationMode.LINEAR/g;" \
+    -pe "s/Clutter.PATH_/Clutter.PathNodeType./g;" \
+    -pe "s/Clutter.PICK_/Clutter.PickMode./g;" \
+    -pe "s/Clutter.REQUEST_/Clutter.RequestMode./g;" \
+    -pe "s/Clutter.ROTATE_/Clutter.RotateDirection./g;" \
+    -pe "s/Clutter.SCRIPT_/Clutter.ScriptError./g;" \
+    -pe "s/Clutter.STAGE_STATE_/Clutter.StageState./g;" \
+    -pe "s/Clutter.TABLE_ALIGNMENT_/Clutter.TableAlignment./g;" \
+    -pe "s/Clutter.TEXTURE_ERROR_/Clutter.TextureError./g;" \
+    -pe "s/Clutter.TEXTURE_/Clutter.TextureFlags./g;" \
+    -pe "s/Clutter.TEXT_/Clutter.TextDirection./g;" \
+    -pe "s/Clutter.TIMELINE_/Clutter.TimelineDirection./g;" \
+    -pe "s/Clutter.UNIT_/Clutter.UnitType./g;" \
+    -pe "s/Clutter.X_AXIS/Clutter.RotateAxis.X_AXIS/g;" \
+    -pe "s/Clutter.Y_AXIS/Clutter.RotateAxis.Y_AXIS/g;" \
+    -pe "s/Clutter.Z_AXIS/Clutter.RotateAxis.Z_AXIS/g;" \
+    -pe "s/Clutter.ENTER/Clutter.EventType.ENTER/g;" \
+    -pe "s/Clutter.LEAVE/Clutter.EventType.LEAVE/g;" \
+    -pe "s/Clutter.BUTTON_PRESS/Clutter.EventType.BUTTON_PRESS/g;" \
+    -pe "s/Clutter.BUTTON_RELEASE/Clutter.EventType.BUTTON_RELEASE/g;" \
+    -pe "s/Clutter.KEY_PRESS/Clutter.EventType.KEY_PRESS/g;" \
+    -pe "s/Clutter.KEY_RELEASE/Clutter.EventType.KEY_RELEASE/g;" \
+    -pe "s/Clutter.SCROLL/Clutter.EventType.SCROLL/g;" \
+    -pe "s/Clutter.DELETE/Clutter.EventType.DELETE/g;" \
+    -pe "s/Clutter.CLIENT_MESSAGE/Clutter.EventType.CLIENT_MESSAGE/g;" \
+    -pe "s/Clutter.DESTROY_NOTIFY/Clutter.EventType.DESTROY_NOTIFY/g;" \
+    -pe "s/Clutter.STAGE_STATE/Clutter.EventType.STAGE_STATE/g;" \
+    -pe "s/Clutter.MOTION/Clutter.EventType.MOTION/g;" \
+    -pe "s/Clutter.BUTTON1_MASK/Clutter.ModifierType.BUTTON1_MASK/g;" \
+    -pe "s/Clutter.BUTTON2_MASK/Clutter.ModifierType.BUTTON2_MASK/g;" \
+    -pe "s/Clutter.BUTTON3_MASK/Clutter.ModifierType.BUTTON3_MASK/g;" \
+    -pe "s/Clutter.BUTTON4_MASK/Clutter.ModifierType.BUTTON4_MASK/g;" \
+    -pe "s/Clutter.BUTTON5_MASK/Clutter.ModifierType.BUTTON5_MASK/g;" \
+    -pe "s/Clutter.CONTROL_MASK/Clutter.ModifierType.CONTROL_MASK/g;" \
+    -pe "s/Clutter.HYPER_MASK/Clutter.ModifierType.HYPER_MASK/g;" \
+    -pe "s/Clutter.LOCK_MASK/Clutter.ModifierType.LOCK_MASK/g;" \
+    -pe "s/Clutter.META_MASK/Clutter.ModifierType.META_MASK/g;" \
+    -pe "s/Clutter.MOD1_MASK/Clutter.ModifierType.MOD1_MASK/g;" \
+    -pe "s/Clutter.MOD2_MASK/Clutter.ModifierType.MOD2_MASK/g;" \
+    -pe "s/Clutter.MOD3_MASK/Clutter.ModifierType.MOD3_MASK/g;" \
+    -pe "s/Clutter.MOD4_MASK/Clutter.ModifierType.MOD4_MASK/g;" \
+    -pe "s/Clutter.MOD5_MASK/Clutter.ModifierType.MOD5_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_MASK/Clutter.ModifierType.MODIFIER_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_13_MASK/Clutter.ModifierType.MODIFIER_RESERVED_13_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_14_MASK/Clutter.ModifierType.MODIFIER_RESERVED_14_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_15_MASK/Clutter.ModifierType.MODIFIER_RESERVED_15_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_16_MASK/Clutter.ModifierType.MODIFIER_RESERVED_16_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_17_MASK/Clutter.ModifierType.MODIFIER_RESERVED_17_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_18_MASK/Clutter.ModifierType.MODIFIER_RESERVED_18_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_19_MASK/Clutter.ModifierType.MODIFIER_RESERVED_19_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_20_MASK/Clutter.ModifierType.MODIFIER_RESERVED_20_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_21_MASK/Clutter.ModifierType.MODIFIER_RESERVED_21_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_22_MASK/Clutter.ModifierType.MODIFIER_RESERVED_22_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_23_MASK/Clutter.ModifierType.MODIFIER_RESERVED_23_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_24_MASK/Clutter.ModifierType.MODIFIER_RESERVED_24_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_25_MASK/Clutter.ModifierType.MODIFIER_RESERVED_25_MASK/g;" \
+    -pe "s/Clutter.MODIFIER_RESERVED_29_MASK/Clutter.ModifierType.MODIFIER_RESERVED_29_MASK/g;" \
+    -pe "s/Clutter.RELEASE_MASK/Clutter.ModifierType.RELEASE_MASK/g;" \
+    -pe "s/Clutter.SHIFT_MASK/Clutter.ModifierType.SHIFT_MASK/g;" \
+    -pe "s/Clutter.SUPER_MASK/Clutter.ModifierType.SUPER_MASK/g;" \
+\
+    -pe "s/import gst\n/from gi.repository import Gst\n/g;" \
+    -pe "s/(?<!\.)gst\./Gst\./g;" \
+    -pe "s/Gst.element_factory_find/Gst.ElementFactory.find/g;" \
+    -pe "s/Gst.element_factory_make/Gst.ElementFactory.make/g;" \
+    -pe "s/Gst.caps_from_string/Gst.Caps.from_string/g;" \
+    -pe "s/Gst.STATE_CHANGE_/Gst.StateChangeReturn./g;" \
+    -pe "s/Gst.STATE_/Gst.State./g;" \
+    -pe "s/Gst.MESSAGE_/Gst.MessageType./g;" \
+    -pe "s/Gst.FORMAT_/Gst.Format./g;" \
+    -pe "s/Gst.SEEK_FLAG_/Gst.SeekFlags./g;" \
+    -pe "s/Gst.SEEK_TYPE_/Gst.SeekType./g;" \
+    -pe "s/Gst.LEVEL_/Gst.DebugLevel./g;" \
+    -pe "s/Gst.URI_/Gst.URIType./g;" \
+    -pe "s/Gst.element_make_from_uri/Gst.Element.make_from_uri/g;" \
+    -pe "s/Gst.event_new_seek/Gst.Event.new_seek/g;" \
+    -pe "s/Gst.GhostPad\(/Gst.GhostPad.new\(/g;" \
+    $f
+done
+
+