# HG changeset patch # User Sadrul Habib Chowdhury # Date 1185695950 0 # Node ID 8000beb73585cb9e320907cdba58ec0d3c52a6b3 # Parent c65c4caa817abfa7b488921e4b10dc9926695df7# Parent 254823d66aa545dafe55f6723a01eec1d2e3b518 propagate from branch 'im.pidgin.pidgin' (head c2cc31f23a71f23062555721f8101fc9c997bae2) to branch 'libgnt.pygnt' (head e69d5919b7bf3b9ace0cb482c147654e60e135a8) diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/gntbindable.c --- 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); diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/gnttextview.c --- 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; } diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/pygnt/Makefile.am --- /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 > $@ + diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/pygnt/Makefile.make --- 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 diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/pygnt/dbus-gnt --- 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(): diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/pygnt/example/rss/gntrss-ui.py --- /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() + diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/pygnt/example/rss/gntrss.py --- /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::' 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) + diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/pygnt/gendef.sh --- 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 diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/pygnt/gntmodule.c --- 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"); diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/pygnt/gnttree.override --- 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; +} diff -r c65c4caa817a -r 8000beb73585 finch/libgnt/pygnt/test.py --- 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()