diff --git a/mytwitter.py b/mytwitter.py
index fb9fbb6..3d0f1e4 100755
--- a/mytwitter.py
+++ b/mytwitter.py
@@ -2,11 +2,11 @@
 #
 # Custom twitter client... mostly for learning Python
 
-import sys, ConfigParser, os, re, subprocess
-import datetime, dateutil.tz
+import sys, ConfigParser, os, re
 import twitter
 import gtk, gtk.glade, gobject
 from urllib2 import HTTPError
+from twitterwidgets import TweetPane
 
 
 class MyTwitter():
@@ -227,431 +227,6 @@ class MyTwitter():
 ### end class MyTwitter
 
 
-class TweetPane(gtk.ScrolledWindow):
-    '''
-    Box that holds all the TweetBoxes for a given feed
-
-    This box will not update itself, the parent should do that.
-    
-    It will connect num_entries listeners to its parent's on_reply() and on_retweet()
-
-    It also gets some data from its parent, including num_entries
-    '''
-
-    def __init__(self, list_name, num_entries=20, single_tweet=None):
-        gtk.ScrolledWindow.__init__(self)
-
-        self.updated_once = False
-
-        self.single_tweet = single_tweet
-
-        self.list_name = list_name
-
-        self.tab_label = CloseTabLabel(self.list_name)
-
-        # These handle determining which tweets are unread
-        self.last_tweet_read = None
-        self.latest_tweet = None
-        self.num_new_tweets = 0
-
-        self.tweets = []
-
-        self.num_entries = num_entries
-        if self.single_tweet is not None:
-            self.num_entries = 1
-
-        self.init_widgets()
-
-
-    def init_widgets(self):
-        self.tab_label.connect('label_clicked', self.set_tweets_read_callback)
-
-        tweet_box = gtk.VBox()
-        viewport = gtk.Viewport()
-
-        # Build us some labels...
-        for i in range(0, self.num_entries):
-            self.tweets.append(TweetBox())
-            tweet_box.pack_start(self.tweets[i], expand=False)
-            self.tweets[i].connect('reply', self.on_tweet_reply)
-            self.tweets[i].connect('retweet', self.on_retweet)
-            self.tweets[i].connect('in-reply-to', self.on_tweet_reply_to)
-
-        viewport.add(tweet_box)
-
-        # Several different actions should mark the tweets as 'read'
-        self.connect('focus', self.set_tweets_read_callback)
-        viewport.connect('button_press_event', self.set_tweets_read_callback)
-        self.connect('scroll-event', self.set_tweets_read_callback)
-        self.connect('scroll-child', self.set_tweets_read_callback)
-
-        self.add(viewport)
-        self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
-        self.show_all()
-
-
-    def update_window(self, raw_statuses, using_results=False):
-        if using_results:
-            statuses = self.statuses_from_results(raw_statuses)
-        else:
-            statuses = raw_statuses
-
-        if self.updated_once is False:
-            self.updated_once = True
-
-        # If this is our first load of this list, don't treat anything as new!
-        if self.last_tweet_read is None:
-            self.last_tweet_read = statuses[0].id
-
-        # Keep count of the new tweets for posting a status message
-        self.num_new_tweets = 0
-
-        for i in range(0, self.num_entries):
-            read = True
-            if i < len(statuses):
-                if statuses[i].id > self.last_tweet_read:
-                    self.num_new_tweets += 1
-                    read = False
-                self.tweets[i].set_status(statuses[i], read)
-            else:
-                self.tweets[i].clear_status()
-
-        self.latest_tweet = statuses[0].id
-
-        self.update_tab_label()
-
-
-    # Update the label with the number of unread tweets
-    def update_tab_label(self):
-        pane_text = self.list_name
-        if self.num_new_tweets > 0:
-            pane_text += ' (' + str(self.num_new_tweets) + ')'
-        self.tab_label.set_label_text(pane_text)
-
-
-    def get_list_name(self):
-        return self.list_name
-
-
-    def set_tweets_read(self):
-        self.last_tweet_read = self.latest_tweet
-        self.num_new_tweets = 0
-        self.update_tab_label()
-
-
-    def set_tweets_read_callback(self, event, arg1=None, arg2=None):
-        self.set_tweets_read()
-
-    
-    def get_tab_label(self):
-        return self.tab_label
-
-
-    def get_single_tweet(self):
-        return self.single_tweet
-
-
-    def updated_once(self):
-        return self.updated_once
-
-
-    def on_tweet_reply(self, widget):
-        self.emit('tweet-reply', {'screen_name': widget.screen_name, 'id': widget.id})
-
-
-    def on_retweet(self, widget):
-        self.emit('tweet-retweet', {'id': widget.id})
-
-
-    def on_tweet_reply_to(self, widget, data):
-        self.emit('tweet-in-reply-to', data)
-
-
-    # To keep things simple elsewhere and improve code reuse
-    # we'll build a list of home-cooked Status objects out of results.
-    # Why is this even necessary?
-    # Why can't we have more consistency out of the Twitter API?
-    def statuses_from_results(self, results):
-        statuses = []
-        for result in results.results:
-            status = Status()
-            status.id = result.id
-            status.user = User()
-            status.user.screen_name = result.from_user
-            status.user.name = ""
-            status.in_reply_to_screen_name = result.to_user
-            # The Twitter Search API has different timestamps than the
-            # REST API... balls
-            # fixme:
-            # Gotta be a cleaner way to do this, but I can't think of it
-            # right now
-            created_at = re.sub(',', '', result.created_at)
-            created_split = re.split(' ', created_at)
-            status.created_at = created_split[0] + ' ' + created_split[2] + ' ' + created_split[1] + ' ' + created_split[4] + ' ' + created_split[5] + ' ' + created_split[3]
-            status.text = result.text
-            statuses.append(status)
-        return statuses
-
-### end class TweetPane
-
-# signals for TweetPane
-
-gobject.signal_new("tweet-reply", TweetPane,
-                   gobject.SIGNAL_RUN_LAST,
-                   gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
-gobject.signal_new("tweet-retweet", TweetPane,
-                   gobject.SIGNAL_RUN_LAST,
-                   gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
-gobject.signal_new("tweet-in-reply-to", TweetPane,
-                   gobject.SIGNAL_RUN_LAST,
-                   gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
-
-
-
-class TweetBox(gtk.VBox):
-
-    '''
-    GUI for displaying one tweet and associated buttons
-    
-    Also stores the data necessary for replying or retweeting (id, screen name)
-    '''
-
-    def __init__(self):
-        gtk.VBox.__init__(self)
-
-        self.screen_name = None
-        self.id = None
-        self.in_reply_to_id = None
-        self.in_reply_to_screen_name = None
-
-        self.init_widgets()
-
-
-    def init_widgets(self):
-        ## Build the header
-        self.header = gtk.Label()
-        label_eb = gtk.EventBox()
-        label_eb.add(self.header)
-        self.pack_start(label_eb)
-
-        # Set the header's properties
-        label_eb.modify_text(gtk.STATE_NORMAL,gtk.gdk.color_parse("#ffffff"))
-        label_eb.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse("#8888ff"))
-        self.header.set_alignment(0.0, 0.0)
-        self.header.set_selectable(True)
-        self.header.set_line_wrap(True)
-
-        ## Build the text
-        self.text = gtk.Label()
-        text_align = gtk.Alignment()
-        text_align.add(self.text)
-        self.text_eb = gtk.EventBox()
-        self.text_eb.add(text_align)
-        self.pack_start(self.text_eb)
-
-        # Set the text's properties
-        text_align.set_padding(2, 5, 10, 5)
-        self.text.set_alignment(0.0, 0.0)
-        self.text.set_selectable(True)
-        self.text.set_line_wrap(True)
-        if gtk.gtk_version[0] > 2 or (gtk.gtk_version[0] == 2 and gtk.gtk_version[1] >= 18):
-            self.text.connect('activate-link', self.on_url_clicked)
-            self.text.connect('button-press-event', self.on_mouse_clicked)
-
-        # Build the buttons
-        button_box_align = gtk.Alignment()
-        button_box_align.set_padding(0, 15, 0, 0)
-        button_box = gtk.HBox()
-        self.pack_start(button_box)
-
-        self.reply_to_button = gtk.Button("")
-        self.reply_to_button.set_relief(gtk.RELIEF_NONE)
-        button_box.pack_start(self.reply_to_button, expand=False)
-        self.reply_to_button.connect("clicked", self.on_in_reply_to_clicked)
-        
-        reply_button = gtk.Button("Reply")
-        reply_button.set_relief(gtk.RELIEF_HALF)
-        button_box.pack_end(reply_button, expand=False)
-        reply_button.connect("clicked", self.on_reply_clicked)
-
-        retweet_button = gtk.Button("Retweet")
-        retweet_button.set_relief(gtk.RELIEF_HALF)
-        button_box.pack_end(retweet_button, expand=False)
-        retweet_button.connect("clicked", self.on_retweet_clicked)
-
-
-    def set_status(self, status, read=True):
-        self.set_read(read)
-
-        timezone = dateutil.tz.gettz()
-        time_format = "%Y.%m.%d %H:%M:%S %Z"
-
-        # Get the user object
-        user = status.user
-
-        # Get user's data for retweeting / replying
-        self.screen_name = user.screen_name
-        self.id = status.id
-        self.in_reply_to_id = status.in_reply_to_status_id
-        self.in_reply_to_screen_name = status.in_reply_to_screen_name
-
-        # ... and a formatted timestamp
-        timestamp = datetime.datetime.strptime(status.created_at, "%a %b %d %H:%M:%S +0000 %Y")
-        timestamp = timestamp.replace(tzinfo=dateutil.tz.gettz('UTC'))
-        timestring = timestamp.astimezone(timezone).strftime(time_format)
-
-        # Set the header
-        self.header.set_markup(user.name + " (" + user.screen_name + ") " + timestring)
-
-        # and the text
-        new_text = status.text
-        new_text = re.sub(r'&([^;]*?)( |$)', r'&amp;\1\2', new_text)
-        if gtk.gtk_version[0] > 2 or (gtk.gtk_version[0] == 2 and gtk.gtk_version[1] >= 18):
-            new_text = re.sub(r"(http://.*?)( |$)", r'<a href="\1">\1</a>\2', new_text)
-        self.text.set_markup(new_text)
-
-        # If this is in reply to something, set appropriate label
-        if self.in_reply_to_screen_name:
-            self.reply_to_button.set_label('in reply to ' + self.in_reply_to_screen_name)
-        
-
-
-    def clear_status(self):
-        self.header.set_markup('')
-        self.text.set_markup('')
-        self.screen_name = None
-        self.id = None
-        self.set_read(True)
-
-
-    def set_read(self, read=True):
-        if read:
-            self.text_eb.modify_bg(gtk.STATE_NORMAL,
-                                   gtk.gdk.color_parse("#f2f1f0"))
-        else:
-            self.text_eb.modify_bg(gtk.STATE_NORMAL,
-                                   gtk.gdk.color_parse("#dbffdb"))
-
-
-    def on_reply_clicked(self, widget):
-        self.emit('reply')
-
-
-    def on_retweet_clicked(self, widget):
-        self.emit('retweet')
-
-
-    def on_in_reply_to_clicked(self, widget):
-        self.emit('in-reply-to', {'id': self.in_reply_to_id, 'name': self.in_reply_to_screen_name})
-
-
-    def on_mouse_clicked(self, widget, event):
-        if event.button == 1:
-            self.set_read(True)
-        # fixme: call on_url_clicked if there is an active uri
-        #  Apparently, this must wait until pygtk 2.18
-
-    
-    def on_url_clicked(self, widget):
-        # fixme: we're catching this signal just to debug why it doesn't get emitted
-        #   seems to be related to EventBox?
-        print 'debug: on_url_clicked()'
-        return True
-
-
-# end class TweetBox
-
-# signals for TweetBox
-gobject.signal_new("reply", TweetBox,
-                   gobject.SIGNAL_RUN_LAST,
-                   gobject.TYPE_NONE, ())
-gobject.signal_new("retweet", TweetBox,
-                   gobject.SIGNAL_RUN_LAST,
-                   gobject.TYPE_NONE, ())
-gobject.signal_new("in-reply-to", TweetBox,
-                   gobject.SIGNAL_RUN_LAST,
-                   gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
-
-
-
-class CloseTabLabel(gtk.EventBox):
-    '''
-    This class holds a label and a button with an 'I' in it.  This button causes the CloseTabLabel
-    to emit a clicked signal
-    '''
-
-    def __init__(self, name=None):
-        gtk.EventBox.__init__(self)
-        self.init_widgets(name)
-
-
-    # This code is still heinous, but at least it is
-    # isolated to its own class
-    def init_widgets(self, name):
-        #create a custom tab for notebook containing a 
-        #label and a button with STOCK_ICON
-        tabBox = gtk.HBox(False, 2)
-        tabButton=gtk.Button()
-        tabButton.connect('clicked', self.on_clicked)
-
-        self.label = gtk.Label(name)
-
-        #Add a picture on a button
-        iconBox = gtk.HBox(False, 0)
-        image = gtk.Image()
-        image.set_from_stock(gtk.STOCK_CLOSE,gtk.ICON_SIZE_MENU)
-        gtk.Button.set_relief(tabButton,gtk.RELIEF_NONE)
-        settings = gtk.Widget.get_settings(tabButton)
-        (w,h) = gtk.icon_size_lookup_for_settings(settings,gtk.ICON_SIZE_MENU)
-        gtk.Widget.set_size_request(tabButton, w + 4, h + 4);
-        iconBox.pack_start(image, True, False, 0)
-        tabButton.add(iconBox)
-
-        tabBox.pack_start(self.label, False)
-        tabBox.pack_start(tabButton, False)
-
-        self.connect('button-press-event', self.on_button_press)
-
-        # needed, otherwise even calling show_all on the notebook won't
-        # make the hbox contents appear.
-        tabBox.show_all()
-        self.add(tabBox)
-
-
-    def set_label_text(self, new_text):
-        self.label.set_text(new_text)
-
-
-    def on_clicked(self, event):
-        self.emit('close-clicked')
-
-
-    def on_button_press(self, event, direction):
-        self.emit('label-clicked')
-
-
-### end class CloseTabLabel
-
-# signals for CloseTabLabel
-gobject.signal_new("close-clicked", CloseTabLabel,
-                   gobject.SIGNAL_RUN_LAST,
-                   gobject.TYPE_NONE, ())
-gobject.signal_new("label-clicked", CloseTabLabel,
-                   gobject.SIGNAL_RUN_LAST,
-                   gobject.TYPE_NONE, ())
-
-
-
-class Status():
-    def __init__(self):
-        self.user = User()
-        self.id = None
-        self.created_at = None
-
-class User():
-    def __init__(self):
-        self.screen_name = None
-        self.name = None
 
 
 # main
diff --git a/twitterwidgets.py b/twitterwidgets.py
new file mode 100644
index 0000000..d307d2c
--- /dev/null
+++ b/twitterwidgets.py
@@ -0,0 +1,429 @@
+import re
+import datetime, dateutil.tz
+import gtk, gobject
+
+class TweetPane(gtk.ScrolledWindow):
+    '''
+    Box that holds all the TweetBoxes for a given feed
+
+    This box will not update itself, the parent should do that.
+    
+    It will connect num_entries listeners to its parent's on_reply() and on_retweet()
+
+    It also gets some data from its parent, including num_entries
+    '''
+
+    def __init__(self, list_name, num_entries=20, single_tweet=None):
+        gtk.ScrolledWindow.__init__(self)
+
+        self.updated_once = False
+
+        self.single_tweet = single_tweet
+
+        self.list_name = list_name
+
+        self.tab_label = CloseTabLabel(self.list_name)
+
+        # These handle determining which tweets are unread
+        self.last_tweet_read = None
+        self.latest_tweet = None
+        self.num_new_tweets = 0
+
+        self.tweets = []
+
+        self.num_entries = num_entries
+        if self.single_tweet is not None:
+            self.num_entries = 1
+
+        self.init_widgets()
+
+
+    def init_widgets(self):
+        self.tab_label.connect('label_clicked', self.set_tweets_read_callback)
+
+        tweet_box = gtk.VBox()
+        viewport = gtk.Viewport()
+
+        # Build us some labels...
+        for i in range(0, self.num_entries):
+            self.tweets.append(TweetBox())
+            tweet_box.pack_start(self.tweets[i], expand=False)
+            self.tweets[i].connect('reply', self.on_tweet_reply)
+            self.tweets[i].connect('retweet', self.on_retweet)
+            self.tweets[i].connect('in-reply-to', self.on_tweet_reply_to)
+
+        viewport.add(tweet_box)
+
+        # Several different actions should mark the tweets as 'read'
+        self.connect('focus', self.set_tweets_read_callback)
+        viewport.connect('button_press_event', self.set_tweets_read_callback)
+        self.connect('scroll-event', self.set_tweets_read_callback)
+        self.connect('scroll-child', self.set_tweets_read_callback)
+
+        self.add(viewport)
+        self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
+        self.show_all()
+
+
+    def update_window(self, raw_statuses, using_results=False):
+        if using_results:
+            statuses = self.statuses_from_results(raw_statuses)
+        else:
+            statuses = raw_statuses
+
+        if self.updated_once is False:
+            self.updated_once = True
+
+        # If this is our first load of this list, don't treat anything as new!
+        if self.last_tweet_read is None:
+            self.last_tweet_read = statuses[0].id
+
+        # Keep count of the new tweets for posting a status message
+        self.num_new_tweets = 0
+
+        for i in range(0, self.num_entries):
+            read = True
+            if i < len(statuses):
+                if statuses[i].id > self.last_tweet_read:
+                    self.num_new_tweets += 1
+                    read = False
+                self.tweets[i].set_status(statuses[i], read)
+            else:
+                self.tweets[i].clear_status()
+
+        self.latest_tweet = statuses[0].id
+
+        self.update_tab_label()
+
+
+    # Update the label with the number of unread tweets
+    def update_tab_label(self):
+        pane_text = self.list_name
+        if self.num_new_tweets > 0:
+            pane_text += ' (' + str(self.num_new_tweets) + ')'
+        self.tab_label.set_label_text(pane_text)
+
+
+    def get_list_name(self):
+        return self.list_name
+
+
+    def set_tweets_read(self):
+        self.last_tweet_read = self.latest_tweet
+        self.num_new_tweets = 0
+        self.update_tab_label()
+
+
+    def set_tweets_read_callback(self, event, arg1=None, arg2=None):
+        self.set_tweets_read()
+
+    
+    def get_tab_label(self):
+        return self.tab_label
+
+
+    def get_single_tweet(self):
+        return self.single_tweet
+
+
+    def updated_once(self):
+        return self.updated_once
+
+
+    def on_tweet_reply(self, widget):
+        self.emit('tweet-reply', {'screen_name': widget.screen_name, 'id': widget.id})
+
+
+    def on_retweet(self, widget):
+        self.emit('tweet-retweet', {'id': widget.id})
+
+
+    def on_tweet_reply_to(self, widget, data):
+        self.emit('tweet-in-reply-to', data)
+
+
+    # To keep things simple elsewhere and improve code reuse
+    # we'll build a list of home-cooked Status objects out of results.
+    # Why is this even necessary?
+    # Why can't we have more consistency out of the Twitter API?
+    def statuses_from_results(self, results):
+        statuses = []
+        for result in results.results:
+            status = Status()
+            status.id = result.id
+            status.user = User()
+            status.user.screen_name = result.from_user
+            status.user.name = ""
+            status.in_reply_to_screen_name = result.to_user
+            # The Twitter Search API has different timestamps than the
+            # REST API... balls
+            # fixme:
+            # Gotta be a cleaner way to do this, but I can't think of it
+            # right now
+            created_at = re.sub(',', '', result.created_at)
+            created_split = re.split(' ', created_at)
+            status.created_at = created_split[0] + ' ' + created_split[2] + ' ' + created_split[1] + ' ' + created_split[4] + ' ' + created_split[5] + ' ' + created_split[3]
+            status.text = result.text
+            statuses.append(status)
+        return statuses
+
+### end class TweetPane
+
+# signals for TweetPane
+
+gobject.signal_new("tweet-reply", TweetPane,
+                   gobject.SIGNAL_RUN_LAST,
+                   gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
+gobject.signal_new("tweet-retweet", TweetPane,
+                   gobject.SIGNAL_RUN_LAST,
+                   gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
+gobject.signal_new("tweet-in-reply-to", TweetPane,
+                   gobject.SIGNAL_RUN_LAST,
+                   gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
+
+
+
+class TweetBox(gtk.VBox):
+
+    '''
+    GUI for displaying one tweet and associated buttons
+    
+    Also stores the data necessary for replying or retweeting (id, screen name)
+    '''
+
+    def __init__(self):
+        gtk.VBox.__init__(self)
+
+        self.screen_name = None
+        self.id = None
+        self.in_reply_to_id = None
+        self.in_reply_to_screen_name = None
+
+        self.init_widgets()
+
+
+    def init_widgets(self):
+        ## Build the header
+        self.header = gtk.Label()
+        label_eb = gtk.EventBox()
+        label_eb.add(self.header)
+        self.pack_start(label_eb)
+
+        # Set the header's properties
+        label_eb.modify_text(gtk.STATE_NORMAL,gtk.gdk.color_parse("#ffffff"))
+        label_eb.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse("#8888ff"))
+        self.header.set_alignment(0.0, 0.0)
+        self.header.set_selectable(True)
+        self.header.set_line_wrap(True)
+
+        ## Build the text
+        self.text = gtk.Label()
+        text_align = gtk.Alignment()
+        text_align.add(self.text)
+        self.text_eb = gtk.EventBox()
+        self.text_eb.add(text_align)
+        self.pack_start(self.text_eb)
+
+        # Set the text's properties
+        text_align.set_padding(2, 5, 10, 5)
+        self.text.set_alignment(0.0, 0.0)
+        self.text.set_selectable(True)
+        self.text.set_line_wrap(True)
+        if gtk.gtk_version[0] > 2 or (gtk.gtk_version[0] == 2 and gtk.gtk_version[1] >= 18):
+            self.text.connect('activate-link', self.on_url_clicked)
+            self.text.connect('button-press-event', self.on_mouse_clicked)
+
+        # Build the buttons
+        button_box_align = gtk.Alignment()
+        button_box_align.set_padding(0, 15, 0, 0)
+        button_box = gtk.HBox()
+        self.pack_start(button_box)
+
+        self.reply_to_button = gtk.Button("")
+        self.reply_to_button.set_relief(gtk.RELIEF_NONE)
+        button_box.pack_start(self.reply_to_button, expand=False)
+        self.reply_to_button.connect("clicked", self.on_in_reply_to_clicked)
+        
+        reply_button = gtk.Button("Reply")
+        reply_button.set_relief(gtk.RELIEF_HALF)
+        button_box.pack_end(reply_button, expand=False)
+        reply_button.connect("clicked", self.on_reply_clicked)
+
+        retweet_button = gtk.Button("Retweet")
+        retweet_button.set_relief(gtk.RELIEF_HALF)
+        button_box.pack_end(retweet_button, expand=False)
+        retweet_button.connect("clicked", self.on_retweet_clicked)
+
+
+    def set_status(self, status, read=True):
+        self.set_read(read)
+
+        timezone = dateutil.tz.gettz()
+        time_format = "%Y.%m.%d %H:%M:%S %Z"
+
+        # Get the user object
+        user = status.user
+
+        # Get user's data for retweeting / replying
+        self.screen_name = user.screen_name
+        self.id = status.id
+        self.in_reply_to_id = status.in_reply_to_status_id
+        self.in_reply_to_screen_name = status.in_reply_to_screen_name
+
+        # ... and a formatted timestamp
+        timestamp = datetime.datetime.strptime(status.created_at, "%a %b %d %H:%M:%S +0000 %Y")
+        timestamp = timestamp.replace(tzinfo=dateutil.tz.gettz('UTC'))
+        timestring = timestamp.astimezone(timezone).strftime(time_format)
+
+        # Set the header
+        self.header.set_markup(user.name + " (" + user.screen_name + ") " + timestring)
+
+        # and the text
+        new_text = status.text
+        new_text = re.sub(r'&([^;]*?)( |$)', r'&amp;\1\2', new_text)
+        if gtk.gtk_version[0] > 2 or (gtk.gtk_version[0] == 2 and gtk.gtk_version[1] >= 18):
+            new_text = re.sub(r"(http://.*?)( |$)", r'<a href="\1">\1</a>\2', new_text)
+        self.text.set_markup(new_text)
+
+        # If this is in reply to something, set appropriate label
+        if self.in_reply_to_screen_name:
+            self.reply_to_button.set_label('in reply to ' + self.in_reply_to_screen_name)
+        
+
+
+    def clear_status(self):
+        self.header.set_markup('')
+        self.text.set_markup('')
+        self.screen_name = None
+        self.id = None
+        self.set_read(True)
+
+
+    def set_read(self, read=True):
+        if read:
+            self.text_eb.modify_bg(gtk.STATE_NORMAL,
+                                   gtk.gdk.color_parse("#f2f1f0"))
+        else:
+            self.text_eb.modify_bg(gtk.STATE_NORMAL,
+                                   gtk.gdk.color_parse("#dbffdb"))
+
+
+    def on_reply_clicked(self, widget):
+        self.emit('reply')
+
+
+    def on_retweet_clicked(self, widget):
+        self.emit('retweet')
+
+
+    def on_in_reply_to_clicked(self, widget):
+        self.emit('in-reply-to', {'id': self.in_reply_to_id, 'name': self.in_reply_to_screen_name})
+
+
+    def on_mouse_clicked(self, widget, event):
+        if event.button == 1:
+            self.set_read(True)
+        # fixme: call on_url_clicked if there is an active uri
+        #  Apparently, this must wait until pygtk 2.18
+
+    
+    def on_url_clicked(self, widget):
+        # fixme: we're catching this signal just to debug why it doesn't get emitted
+        #   seems to be related to EventBox?
+        print 'debug: on_url_clicked()'
+        return True
+
+
+# end class TweetBox
+
+# signals for TweetBox
+gobject.signal_new("reply", TweetBox,
+                   gobject.SIGNAL_RUN_LAST,
+                   gobject.TYPE_NONE, ())
+gobject.signal_new("retweet", TweetBox,
+                   gobject.SIGNAL_RUN_LAST,
+                   gobject.TYPE_NONE, ())
+gobject.signal_new("in-reply-to", TweetBox,
+                   gobject.SIGNAL_RUN_LAST,
+                   gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
+
+
+
+class CloseTabLabel(gtk.EventBox):
+    '''
+    This class holds a label and a button with an 'I' in it.  This button causes the CloseTabLabel
+    to emit a clicked signal
+    '''
+
+    def __init__(self, name=None):
+        gtk.EventBox.__init__(self)
+        self.init_widgets(name)
+
+
+    # This code is still heinous, but at least it is
+    # isolated to its own class
+    def init_widgets(self, name):
+        #create a custom tab for notebook containing a 
+        #label and a button with STOCK_ICON
+        tabBox = gtk.HBox(False, 2)
+        tabButton=gtk.Button()
+        tabButton.connect('clicked', self.on_clicked)
+
+        self.label = gtk.Label(name)
+
+        #Add a picture on a button
+        iconBox = gtk.HBox(False, 0)
+        image = gtk.Image()
+        image.set_from_stock(gtk.STOCK_CLOSE,gtk.ICON_SIZE_MENU)
+        gtk.Button.set_relief(tabButton,gtk.RELIEF_NONE)
+        settings = gtk.Widget.get_settings(tabButton)
+        (w,h) = gtk.icon_size_lookup_for_settings(settings,gtk.ICON_SIZE_MENU)
+        gtk.Widget.set_size_request(tabButton, w + 4, h + 4);
+        iconBox.pack_start(image, True, False, 0)
+        tabButton.add(iconBox)
+
+        tabBox.pack_start(self.label, False)
+        tabBox.pack_start(tabButton, False)
+
+        self.connect('button-press-event', self.on_button_press)
+
+        # needed, otherwise even calling show_all on the notebook won't
+        # make the hbox contents appear.
+        tabBox.show_all()
+        self.add(tabBox)
+
+
+    def set_label_text(self, new_text):
+        self.label.set_text(new_text)
+
+
+    def on_clicked(self, event):
+        self.emit('close-clicked')
+
+
+    def on_button_press(self, event, direction):
+        self.emit('label-clicked')
+
+
+### end class CloseTabLabel
+
+# signals for CloseTabLabel
+gobject.signal_new("close-clicked", CloseTabLabel,
+                   gobject.SIGNAL_RUN_LAST,
+                   gobject.TYPE_NONE, ())
+gobject.signal_new("label-clicked", CloseTabLabel,
+                   gobject.SIGNAL_RUN_LAST,
+                   gobject.TYPE_NONE, ())
+
+
+
+class Status():
+    def __init__(self):
+        self.user = User()
+        self.id = None
+        self.created_at = None
+
+class User():
+    def __init__(self):
+        self.screen_name = None
+        self.name = None