changeset 18713:8000beb73585

propagate from branch 'im.pidgin.pidgin' (head c2cc31f23a71f23062555721f8101fc9c997bae2) to branch 'libgnt.pygnt' (head e69d5919b7bf3b9ace0cb482c147654e60e135a8)
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sun, 29 Jul 2007 07:59:10 +0000
parents c65c4caa817a (current diff) 254823d66aa5 (diff)
children 121d1560346c
files
diffstat 11 files changed, 565 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/finch/libgnt/gntbindable.c	Sun Jul 29 07:01:13 2007 +0000
+++ b/finch/libgnt/gntbindable.c	Sun Jul 29 07:59:10 2007 +0000
@@ -184,7 +184,7 @@
 
 	action = g_hash_table_lookup(klass->actions, name);
 	if (!action) {
-		g_printerr("GntWidget: Invalid action name %s for %s\n",
+		g_printerr("GntBindable: Invalid action name %s for %s\n",
 				name, g_type_name(G_OBJECT_CLASS_TYPE(klass)));
 		if (list)
 			g_list_free(list);
--- a/finch/libgnt/gnttextview.c	Sun Jul 29 07:01:13 2007 +0000
+++ b/finch/libgnt/gnttextview.c	Sun Jul 29 07:59:10 2007 +0000
@@ -398,7 +398,7 @@
 static void
 gnt_text_view_size_changed(GntWidget *widget, int w, int h)
 {
-	if (w != widget->priv.width) {
+	if (w != widget->priv.width && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED)) {
 		gnt_text_view_reflow(GNT_TEXT_VIEW(widget));
 	}
 }
@@ -422,11 +422,16 @@
 gnt_text_view_init(GTypeInstance *instance, gpointer class)
 {
 	GntWidget *widget = GNT_WIDGET(instance);
-	
-	GNT_WIDGET_SET_FLAGS(GNT_WIDGET(instance), GNT_WIDGET_GROW_Y | GNT_WIDGET_GROW_X);
+	GntTextView *view = GNT_TEXT_VIEW(widget);
+	GntTextLine *line = g_new0(GntTextLine, 1);
 
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW | 
+            GNT_WIDGET_GROW_Y | GNT_WIDGET_GROW_X);
 	widget->priv.minw = 5;
 	widget->priv.minh = 2;
+	view->string = g_string_new(NULL);
+	view->list = g_list_append(view->list, line);
+
 	GNTDEBUG;
 }
 
@@ -464,13 +469,6 @@
 GntWidget *gnt_text_view_new()
 {
 	GntWidget *widget = g_object_new(GNT_TYPE_TEXT_VIEW, NULL);
-	GntTextView *view = GNT_TEXT_VIEW(widget);
-	GntTextLine *line = g_new0(GntTextLine, 1);
-
-	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW);
-
-	view->string = g_string_new(NULL);
-	view->list = g_list_append(view->list, line);
 
 	return widget;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/libgnt/pygnt/Makefile.am	Sun Jul 29 07:59:10 2007 +0000
@@ -0,0 +1,38 @@
+EXTRA_DIST = gendef.sh
+
+pg_LTLIBRARIES = gnt.la
+
+pgdir = $(libdir)
+
+sources = \
+	gnt.def \
+	gnt.override \
+	gntfilesel.override \
+	gnttree.override
+
+gnt_la_SOURCES = gnt.c common.c common.h gntmodule.c
+
+gnt_la_LDFLAGS = -module -avoid-version \
+	`pkg-config --libs pygobject-2.0`
+
+gnt_la_LIBADD = \
+	$(GLIB_LIBS) \
+	../libgnt.la
+
+AM_CPPFLAGS = \
+	-I../ \
+	$(GLIB_CFLAGS) \
+	$(GNT_CFLAGS)  \
+	-I/usr/include/python2.4 \
+	`pkg-config --cflags pygobject-2.0`
+
+CLEANFILES = gnt.def gnt.c gnt.defe
+
+gnt.def: $(srcdir)/../*.h
+	$(srcdir)/gendef.sh
+
+gnt.c: $(sources)
+	pygtk-codegen-2.0 --prefix gnt \
+	--override gnt.override \
+	gnt.def > $@
+
--- a/finch/libgnt/pygnt/Makefile.make	Sun Jul 29 07:01:13 2007 +0000
+++ b/finch/libgnt/pygnt/Makefile.make	Sun Jul 29 07:59:10 2007 +0000
@@ -10,5 +10,7 @@
 	--override gnt.override \
 	gnt.def > $@
 
+#python codegen/codegen.py --prefix gnt \
+
 clean:
 	@rm *.so *.o gnt.c
--- a/finch/libgnt/pygnt/dbus-gnt	Sun Jul 29 07:01:13 2007 +0000
+++ b/finch/libgnt/pygnt/dbus-gnt	Sun Jul 29 07:59:10 2007 +0000
@@ -24,6 +24,7 @@
     key = get_dict_key(conv)
     stuff = convwins[key]
     stuff[0].destroy()
+	# if a conv window is closed, then reopened, this thing crashes
     convwins[key] = None
 
 def wrote_msg(account, who, msg, conv, flags):
@@ -34,7 +35,8 @@
     tv.append_text_with_flags(who + ": ", 1)
     tv.append_text_with_flags(msg, 0)
     tv.scroll(0)
-    stuff[0].set_urgent()
+    if flags & 3:
+        stuff[0].set_urgent()
 
 bus = dbus.SessionBus()
 obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
@@ -91,6 +93,7 @@
     tv.clear()
     win.show()
     convwins[key] = [win, tv, entry]
+	# XXX: listen to "destroy" for the window, and reset the key
     return convwins[key]
 
 def show_buddylist():
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/libgnt/pygnt/example/rss/gntrss-ui.py	Sun Jul 29 07:59:10 2007 +0000
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+
+# A very simple and stupid RSS reader
+#
+# Uses the Universal Feed Parser
+#
+
+import gntrss
+import gnt
+import gobject
+import sys
+
+__version__ = "0.0.1alpha"
+__author__ = "Sadrul Habib Chowdhury (sadrul@pidgin.im)"
+__copyright__ = "Copyright 2007, Sadrul Habib Chowdhury"
+__license__ = "GPL" # see full license statement below
+
+gnt.gnt_init()
+
+class RssTree(gnt.Tree):
+    __gsignals__ = {
+        'active_changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)),
+        'key_pressed' : 'override'
+    }
+
+    def __init__(self):
+        self.active = None
+        gnt.Tree.__init__(self)
+
+    def set_active(self, active):
+        if self.active == active:
+            return
+        if self.active:
+            self.set_row_flags(self.active, gnt.TEXT_FLAG_NORMAL)
+        old = self.active
+        self.active = active
+        self.set_row_flags(self.active, gnt.TEXT_FLAG_UNDERLINE)
+        self.emit('active_changed', old)
+
+    def do_key_pressed(self, text):
+        if text == '\r':
+            now = self.get_selection_data()
+            self.set_active(now)
+            return True
+        return False
+
+gobject.type_register(RssTree)
+
+win = gnt.Box(homo = False, vert = True)
+win.set_toplevel(True)
+win.set_title("GntRss")
+win.set_pad(0)
+
+#
+# [[[ Generic feed/item callbacks
+#
+def feed_item_added(feed, item):
+    add_feed_item(item)
+
+def add_feed(feed):
+    if not feed.get_data('gntrss-connected'):
+        feed.connect('added', feed_item_added)
+        feed.connect('notify', update_feed_title)
+        feed.set_data('gntrss-connected', True)
+    feeds.add_row_after(feed, [feed.title, str(feed.unread)], None, None)
+
+def remove_item(item, feed):
+    items.remove(item)
+
+def update_feed_item(item, property):
+    if property.name == 'unread':
+        if feeds.active != item.parent:
+            return
+        if item.unread:
+            item.parent.unread = item.parent.unread + 1
+            items.set_row_flags(item, gnt.TEXT_FLAG_BOLD)
+        else:
+            item.parent.unread = item.parent.unread - 1
+            items.set_row_flags(item, gnt.TEXT_FLAG_NORMAL)
+        item.parent.notify('unread')
+
+def add_feed_item(item):
+    currentfeed = feeds.active
+    if item.parent != currentfeed:
+        return
+    items.add_row_after(item, [str(item.title)], None, None)
+    if item.unread:
+        items.set_row_flags(item, gnt.TEXT_FLAG_BOLD)
+    if not item.get_data('gntrss-connected'):
+        item.set_data('gntrss-connected', True)
+        item.connect('notify', update_feed_item)
+        item.connect('delete', remove_item)
+
+#
+# ]]] Generic feed/item callbacks
+#
+
+
+####
+# [[[ The list of feeds
+###
+
+#
+# The active row in the feed-list has changed. Update the feed-item table.
+def feed_active_changed(tree, old):
+    items.remove_all()
+    if not tree.active:
+        return
+    for item in tree.active.items:
+        add_feed_item(item)
+
+#
+# Check for the action keys and decide how to deal with them.
+def feed_key_pressed(tree, text):
+    if text == 'r':
+        feed = tree.get_selection_data()
+        tree.perform_action_key('j')
+        #tree.perform_action('move-down')
+        feed.refresh()
+    elif text == 'R':
+        feeds = tree.get_rows()
+        for feed in feeds:
+            feed.refresh()
+    else:
+        return False
+    return True
+
+feeds = RssTree()
+feeds.set_property('columns', 2)
+feeds.set_col_width(0, 20)
+feeds.set_col_width(1, 4)
+feeds.set_column_resizable(0, False)
+feeds.set_column_resizable(1, False)
+feeds.set_column_is_right_aligned(1, True)
+feeds.set_show_separator(False)
+
+feeds.connect('active_changed', feed_active_changed)
+feeds.connect('key_pressed', feed_key_pressed)
+
+####
+# ]]] The list of feeds
+###
+
+####
+# [[[ The list of items in the feed
+####
+
+#
+# The active item in the feed-item list has changed. Update the
+# summary content.
+def item_active_changed(tree, old):
+    details.clear()
+    if not tree.active:
+        return
+    item = tree.active
+    details.append_text_with_flags(str(item.title) + "\n", gnt.TEXT_FLAG_BOLD)
+    details.append_text_with_flags(str(item.summary), gnt.TEXT_FLAG_NORMAL)
+    details.scroll(0)
+    if item.unread:
+        item.set_property('unread', False)
+
+#
+# Look for action keys in the feed-item list.
+def item_key_pressed(tree, text):
+    current = tree.get_selection_data()
+    if text == 'M':     # Mark all of the items 'read'
+        all = tree.get_rows()
+        for item in all:
+            item.unread = False
+    elif text == 'm':     # Mark the current item 'read'
+        if current.unread:
+            current.set_property('unread', False)
+    elif text == 'U':     # Mark the current item 'unread'
+        if not current.unread:
+            current.set_property('unread', True)
+    else:
+        return False
+    return True
+
+items = RssTree()
+items.set_property('columns', 1)
+items.set_col_width(0, 40)
+items.connect('key_pressed', item_key_pressed)
+items.connect('active_changed', item_active_changed)
+
+####
+# ]]] The list of items in the feed
+####
+
+# The container on the top
+box = gnt.Box(homo = False, vert = False)
+box.set_pad(0)
+box.add_widget(feeds)
+box.add_widget(items)
+
+win.add_widget(box)
+
+line = gnt.Line(vertical = False)
+win.add_widget(line)
+
+# The textview to show the details of a feed
+details = gnt.TextView()
+
+win.add_widget(details)
+
+browser = gnt.Button("Open in Browser")
+win.add_widget(browser)
+details.attach_scroll_widget(browser)
+
+win.show()
+
+def update_feed_title(feed, property):
+    if property.name == 'title':
+        feeds.change_text(feed, 0, feed.title)
+    elif property.name == 'unread':
+        feeds.change_text(feed, 1, str(feed.unread))
+
+# populate everything
+for feed in gntrss.feeds:
+    feed.refresh()
+    add_feed(feed)
+
+gnt.gnt_main()
+
+gnt.gnt_quit()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/libgnt/pygnt/example/rss/gntrss.py	Sun Jul 29 07:59:10 2007 +0000
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+
+# A very simple and stupid RSS reader
+#
+# Uses the Universal Feed Parser
+#
+
+import threading
+import feedparser
+import gobject
+import sys
+import time
+
+##
+# The FeedItem class. It will update emit 'delete' signal when it's
+# destroyed.
+##
+class FeedItem(gobject.GObject):
+    __gproperties__ = {
+        'unread' : (gobject.TYPE_BOOLEAN, 'read',
+            'The unread state of the item.',
+            False, gobject.PARAM_READWRITE)
+    }
+    __gsignals__ = {
+        'delete' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))
+    }
+    def __init__(self, item, parent):
+        self.__gobject_init__()
+        self.date = item['date']
+        self.date_parsed = item['date_parsed']
+        self.title = item['title']
+        self.summary = item['summary']
+        self.link = item['link']
+        self.parent = parent
+        self.unread = True
+
+    def remove(self):
+        self.emit('delete', self.parent)
+
+    def do_set_property(self, property, value):
+        if property.name == 'unread':
+            self.unread = value
+
+gobject.type_register(FeedItem)
+
+def item_hash(item):
+    return str(item['date'] + item['title'])
+
+##
+# The Feed class. It will update the 'link', 'title', 'desc' and 'items'
+# attributes if/when they are updated (triggering 'notify::<attr>' signal)
+##
+class Feed(gobject.GObject):
+    __gproperties__ = {
+        'link' : (gobject.TYPE_STRING, 'link',
+            'The web page this feed is associated with.',
+            '...', gobject.PARAM_READWRITE),
+        'title' : (gobject.TYPE_STRING, 'title',
+            'The title of the feed.',
+            '...', gobject.PARAM_READWRITE),
+        'desc' : (gobject.TYPE_STRING, 'description',
+            'The description for the feed.',
+            '...', gobject.PARAM_READWRITE),
+        'items' : (gobject.TYPE_POINTER, 'items',
+            'The items in the feed.', gobject.PARAM_READWRITE),
+        'unread' : (gobject.TYPE_INT, 'unread',
+            'Number of unread items in the feed.', 0, 10000, 0, gobject.PARAM_READWRITE)
+    }
+    __gsignals__ = {
+        'added' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))
+    }
+
+    def __init__(self, url):
+        self.__gobject_init__()
+        self.url = url           # The url of the feed itself
+        self.link = url          # The web page associated with the feed
+        self.desc = url
+        self.title = url
+        self.unread = 0
+        self.timer = 0
+        self.items = []
+        self.hash = {}
+
+    def do_set_property(self, property, value):
+        if property.name == 'link':
+            self.link = value
+        elif property.name == 'desc':
+            self.desc = value
+        elif property.name == 'title':
+            self.title = value
+        elif property.name == 'unread':
+            self.unread = value
+        pass
+
+    def check_thread_for_death(self):
+        #if self.thread.running:
+        #    sys.stderr.write(time.ctime() + "continue")
+        #    return True
+        # The thread has ended!!
+        #result = self.thread.result
+        #self.thread = None
+        result = feedparser.parse(self.url)
+        channel = result['channel']
+        self.set_property('link', channel['link'])
+        self.set_property('desc', channel['description'])
+        self.set_property('title', channel['title'])
+        self.set_property('unread', len(result['items']))
+        self.timer = 0
+        items = result['items']
+        tmp = {}
+        for item in self.items:
+            tmp[hash(item)] = item
+
+        for item in items:
+            try:
+                exist = self.hash[item_hash(item)]
+                del tmp[hash(exist)]
+            except:
+                itm = FeedItem(item, self)
+                self.items.append(itm)
+                self.emit('added', itm)
+                self.hash[item_hash(item)] = itm
+        for hv in tmp:
+            tmp[hv].remove()
+
+        return False
+
+    def refresh(self):
+        #if self.thread == 0:
+        #   self.thread = FeedReader(self)
+        #   self.thread.start()
+        if self.timer == 0:
+            self.timer = gobject.timeout_add(1000, self.check_thread_for_death)
+
+gobject.type_register(Feed)
+
+##
+# A FeedReader class, which is threaded to make sure it doesn't freeze the ui
+# (this thing doesn't quite work ... yet)
+##
+class FeedReader(threading.Thread):
+    def __init__(self, feed):
+        self.feed = feed
+        self.running = True
+        threading.Thread.__init__(self)
+
+    def run(self):
+        sys.stderr.write(str(time.ctime()) + " STARTED!!!\n\n\n")
+        self.running = True
+        self.result = feedparser.parse(self.feed.url)
+        self.running = False
+        sys.stderr.write(str(time.ctime()) + " DONE!!!\n\n\n")
+
+feeds = []
+urls = ("http://rss.slashdot.org/Slashdot/slashdot",
+        "http://www.python.org/channews.rdf",
+        "http://pidgin.im/rss.php"
+        )
+
+for url in urls:
+    feed = Feed(url)
+    feeds.append(feed)
+
--- a/finch/libgnt/pygnt/gendef.sh	Sun Jul 29 07:01:13 2007 +0000
+++ b/finch/libgnt/pygnt/gendef.sh	Sun Jul 29 07:59:10 2007 +0000
@@ -28,7 +28,7 @@
 	gnt.h"
 
 # Generate the def file
-rm gnt.def
+rm -f gnt.def
 for file in $FILES
 do
 	python /usr/share/pygtk/2.0/codegen/h2def.py ../$file >> gnt.def
--- a/finch/libgnt/pygnt/gntmodule.c	Sun Jul 29 07:01:13 2007 +0000
+++ b/finch/libgnt/pygnt/gntmodule.c	Sun Jul 29 07:59:10 2007 +0000
@@ -9,11 +9,12 @@
     PyObject *m, *d;
  
     init_pygobject ();
- 
+
     m = Py_InitModule ("gnt", gnt_functions);
     d = PyModule_GetDict (m);
  
     gnt_register_classes (d);
+    gnt_add_constants(m, "GNT_");
  
     if (PyErr_Occurred ()) {
         Py_FatalError ("can't initialise module sad");
--- a/finch/libgnt/pygnt/gnttree.override	Sun Jul 29 07:01:13 2007 +0000
+++ b/finch/libgnt/pygnt/gnttree.override	Sun Jul 29 07:59:10 2007 +0000
@@ -20,7 +20,7 @@
  * USA
  */
 %%
-headrs
+headers
 #include "common.h"
 %%
 ignore 
@@ -49,7 +49,7 @@
 		return NULL;
 	}
 	while (list) {
-		PyObject *obj = pyg_pointer_new(G_TYPE_POINTER, list->data);
+		PyObject *obj = list->data;
 		PyList_Append(py_list, obj);
 		Py_DECREF(obj);
 		list = list->next;
@@ -100,4 +100,65 @@
 	Py_INCREF(Py_None);
 	return Py_None;
 }
+%%
+override gnt_tree_get_selection_data noargs
+static PyObject *
+_wrap_gnt_tree_get_selection_data(PyGObject *self)
+{
+	PyObject *ret = gnt_tree_get_selection_data(GNT_TREE(self->obj));
+	if (!ret)
+		ret = Py_None;
+	Py_INCREF(ret);
+	return ret;
+}
+%%
+override gnt_tree_change_text
+static PyObject *
+_wrap_gnt_tree_change_text(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+	static char *kwlist[] = { "key", "colno", "text", NULL };
+	char *text;
+	int colno;
+	gpointer key;
 
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Ois:GntTree.change_text", kwlist, &key, &colno, &text))
+		return NULL;
+
+	gnt_tree_change_text(GNT_TREE(self->obj), key, colno, text);
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+%%
+override gnt_tree_set_row_flags
+static PyObject *
+_wrap_gnt_tree_set_row_flags(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+	static char *kwlist[] = { "key", "flag", NULL };
+	int flag;
+	gpointer key;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Oi:GntTree.set_row_flags", kwlist, &key, &flag))
+		return NULL;
+
+	gnt_tree_set_row_flags(GNT_TREE(self->obj), key, flag);
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+%%
+override gnt_tree_remove
+static PyObject *
+_wrap_gnt_tree_remove(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+	static char *kwlist[] = { "key", NULL };
+	gpointer key;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:GntTree.remove", kwlist, &key))
+		return NULL;
+
+	gnt_tree_remove(GNT_TREE(self->obj), key);
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
--- a/finch/libgnt/pygnt/test.py	Sun Jul 29 07:01:13 2007 +0000
+++ b/finch/libgnt/pygnt/test.py	Sun Jul 29 07:59:10 2007 +0000
@@ -1,18 +1,59 @@
 #!/usr/bin/python
+import gobject
 import gnt
 
+class MyObject(gobject.GObject):
+    __gproperties__ = {
+        'mytype': (gobject.TYPE_INT, 'mytype', 'the type of the object',
+                0, 10000, 0, gobject.PARAM_READWRITE),
+        'string': (gobject.TYPE_STRING, 'string property', 'the string',
+                None, gobject.PARAM_READWRITE),
+        'gobject': (gobject.TYPE_OBJECT, 'object property', 'the object',
+                gobject.PARAM_READWRITE),
+    }
+
+    def __init__(self, type = 'string', value = None):
+        self.__gobject_init__()
+        self.set_property(type, value)
+
+    def do_set_property(self, pspec, value):
+        if pspec.name == 'string':
+            self.string = value
+            self.type = gobject.TYPE_STRING
+        elif pspec.name == 'gobject':
+            self.gobject = value
+            self.type = gobject.TYPE_OBJECT
+        else:
+            raise AttributeError, 'unknown property %s' % pspec.name
+    def do_get_property(self, pspec):
+        if pspec.name == 'string':
+            return self.string
+        elif pspec.name == 'gobject':
+            return self.gobject
+        elif pspec.name == 'mytype':
+            return self.type
+        else:
+            raise AttributeError, 'unknown property %s' % pspec.name
+gobject.type_register(MyObject)
+
 def button_activate(button, tree):
-	list = tree.get_selection_text_list()
-	str = ""
-	for i in list:
-		str = str + i
-	entry.set_text("clicked!!!" + str)
+    list = tree.get_selection_text_list()
+    ent = tree.get_selection_data()
+    if ent.type == gobject.TYPE_STRING:
+        str = ""
+        for i in list:
+            str = str + i
+        entry.set_text("clicked!!!" + str)
+    elif ent.type == gobject.TYPE_OBJECT:
+        ent.gobject.set_text("mwhahaha!!!")
 
 gnt.gnt_init()
 
 win = gnt.Window()
 
 entry = gnt.Entry("")
+obj = MyObject()
+obj.set_property('gobject', entry)
 
 win.add_widget(entry)
 win.set_title("Entry")
@@ -27,12 +68,20 @@
 # so random non-string values can be used as the key for a row in a GntTree!
 last = None
 for i in range(1, 100):
-	tree.add_row_after(i, [str(i), ""], None, i-1)
-tree.add_row_after(entry, ["asd"], None, None)
-tree.add_row_after("b", ["123", ""], entry, None)
+    key = MyObject('string', str(i))
+    tree.add_row_after(key, [str(i)], None, last)
+    last = key
+
+tree.add_row_after(MyObject('gobject', entry), ["asd"], None, None)
+tree.add_row_after(MyObject('string', "b"), ["123"], MyObject('gobject', entry), None)
 
 button.connect("activate", button_activate, tree)
 
+tv = gnt.TextView()
+
+win.add_widget(tv)
+tv.append_text_with_flags("What up!!", gnt.TEXT_FLAG_BOLD)
+
 win.show()
 
 gnt.gnt_main()