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'&\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'\1\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'&\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'\1\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