from twisted.internet import gtk2reactor
gtk2reactor.install()
from twisted.internet import reactor
import gtk, gobject, gtk.glade, pango
import time, sys, sqlite3, xdg.BaseDirectory, os, logging
APP_NAME = "twitterapp" # FIXME think of something good
class MainApp:
def __init__(self):
self.widgets = gtk.glade.XML("twitterapp.glade")
self.window = self.widgets.get_widget("window_main")
self.window.connect("destroy", self.die)
self.window.show_all()
self.showing_services = False
self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str,
gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, float)
self.treeview = self.widgets.get_widget("list_tweet")
self.treeview.set_model(self.liststore)
# avatar
cell=gtk.CellRendererPixbuf()
col=gtk.TreeViewColumn('')
col.pack_start(cell)
self.treeview.append_column(col)
col.add_attribute(cell, "text", 0)
# tweet text
cell=gtk.CellRendererText()
cell.set_property("wrap-width", 300)
cell.set_property("wrap-mode", pango.WRAP_WORD)
col=gtk.TreeViewColumn('')
col.pack_start(cell)
self.treeview.append_column(col)
col.add_attribute(cell, "markup", 1)
# system
cell=gtk.CellRendererPixbuf()
col=gtk.TreeViewColumn('')
col.pack_start(cell)
self.treeview.append_column(col)
col.add_attribute(cell, "text", 2)
# time - note, no add_attribute because we don't render this
cell=gtk.CellRendererText()
col=gtk.TreeViewColumn('')
col.pack_start(cell)
self.treeview.append_column(col)
# hook up clickable stuff
self.widgets.get_widget("eventbox_arrow").connect("button_press_event", self.toggle_arrow)
self.widgets.get_widget("tweet").connect("key_press_event", self.typing)
# load config data
self.db = None
config_folder = xdg.BaseDirectory.load_first_config(APP_NAME)
if config_folder:
config_file = os.path.join(config_folder, "Settings")
if os.path.exists(config_file):
try:
self.db = sqlite3.Connection(config_file)
except:
pass
self.sql = self.db.cursor()
if not self.db:
config_folder = xdg.BaseDirectory.save_config_path(APP_NAME)
config_file = os.path.join(config_folder, "Settings")
try:
self.db = sqlite3.Connection(config_file)
except:
raise "Couldn't save configuration data in %s" % config_file
self.sql = self.db.cursor()
self.sql.execute("""create table login
(system varchar, username varchar, password varchar,
enabled varchar)""")
self.db.commit()
# load subsidiary modules
self.places = []
if DUMMY_MODE:
import ublogs.dummy
self.add_ublog(ublogs.dummy, "Dummy")
return
import ublogs.twitter
self.add_ublog(ublogs.twitter, "Twitter")
import ublogs.identica
self.add_ublog(ublogs.identica, "Identi.ca")
def printdata(self, data):
# data is (data to display, number)
# we must return (us_as_function, number)
tweets, number = data
tweets.reverse() # we get them in desc order but need to insert in asc
for tweet in tweets:
tweet["nicetime"] = tweet["time"].strftime("%H.%M")
text = u"""%(name)s (%(username)s)
%(tweet)s
%(nicetime)s \u21BB""" % tweet
epochsecs = time.mktime(tweet["time"].timetuple())
# insert this tweet at appropriate place in the list
last_item_time = sys.maxint
inserted = False
row = [None, text, None, None, epochsecs]
counter = 0
for i in self.liststore:
next_item_time = i[4]
if epochsecs < last_item_time and epochsecs > next_item_time:
iter = self.liststore.get_iter(counter)
self.liststore.insert_before(iter, row)
inserted = True
break
counter += 1
if not inserted:
self.liststore.append(row)
return (self.printdata, number)
def toggle_arrow(self, widget, event):
arrow = self.widgets.get_widget("arrow_services_revealer")
if self.showing_services:
arrow.set(gtk.ARROW_RIGHT, gtk.SHADOW_NONE)
self.widgets.get_widget("vbox_services").hide()
self.showing_services = False
else:
arrow.set(gtk.ARROW_DOWN, gtk.SHADOW_NONE)
self.widgets.get_widget("vbox_services").show()
self.showing_services = True
def typing(self, widget, event):
label = self.widgets.get_widget("label_chars_left")
label.set_text(str(140 - widget.get_buffer().get_char_count()))
def die(self, widget):
reactor.stop()
def login_details(self, widget, ublogapi, name, checkbox):
dialog = gtk.Dialog(title="Login details for %s" % name,
parent=self.window, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, buttons=None)
table = gtk.Table(rows=2, columns=2)
l_username = gtk.Label("Username:")
table.attach(l_username,0,1,0,1)
l_password = gtk.Label("Password:")
table.attach(l_password,0,1,1,2)
e_username = gtk.Entry()
table.attach(e_username,1,2,0,1)
e_password = gtk.Entry()
e_password.set_visibility(False)
table.attach(e_password,1,2,1,2)
table.show_all()
dialog.vbox.add(table)
self.sql.execute("select username, password from login where system=?",
(name,))
row = self.sql.fetchone()
if row:
e_username.set_text(row[0])
e_password.set_text(row[1])
dialog.run()
username = e_username.get_text()
password = e_password.get_text()
dialog.destroy()
self.sql.execute("delete from login where system=?", (name,))
self.db.commit()
if username:
# save data
self.sql.execute("insert into login (system,username," +
"password,enabled) values (?,?,?,'Y')",
(name, username, password))
self.db.commit()
ublogapi.set_login_details(username, password)
checkbox.set_sensitive(True)
checkbox.set_active(True)
# and poll now
ublogapi.poll(self.printdata,one_time_only=True)
else:
checkbox.set_sensitive(False)
def toggle_ublog(self, widget, name):
if widget.get_active():
enabled = "Y"
else:
enabled = "N"
self.sql.execute("select username, password from login where system=?",
(name,))
row = self.sql.fetchone()
if row:
username = row[0]
password = row[1]
else:
username = ""
password = ""
self.sql.execute("delete from login where system=?", (name,))
self.db.commit()
self.sql.execute("insert into login (system,username," +
"password,enabled) values (?,?,?,?)",
(name, username, password,enabled))
self.db.commit()
def add_ublog(self, ublogapi, name):
vbox_services = self.widgets.get_widget("vbox_services")
hbox = gtk.HBox()
checkbox = gtk.CheckButton(label=name)
checkbox.connect("toggled", self.toggle_ublog, name)
password_button = gtk.Button()
img = gtk.Image()
img.set_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_SMALL_TOOLBAR)
password_button.set_image(img)
password_button.connect("clicked", self.login_details, ublogapi, name, checkbox)
hbox.pack_start(checkbox, expand=True, fill=True)
hbox.pack_start(password_button, expand=False, fill=False)
vbox_services.pack_start(hbox)
vbox_services.show_all()
self.places.append([ublogapi, name, checkbox])
# check config file for data
self.sql.execute("select username, password, enabled from login where system=?",
(name,))
row = self.sql.fetchone()
if row:
# they have configured at least one ublog username/password
# so this is not first run: hide the list, and tell the API
ublogapi.set_login_details(row[0],row[1])
if row[2] == "Y":
checkbox.set_active(True)
ublogapi.poll(self.printdata) # we poll once, it does rescheduling
else:
checkbox.set_active(False)
self.widgets.get_widget("vbox_services").hide()
else:
checkbox.set_sensitive(False)
if __name__== "__main__":
DUMMY_MODE = False
for arg in sys.argv:
if arg == "--debug":
logging.basicConfig(level=logging.DEBUG)
elif arg == "--dummy":
DUMMY_MODE = True
MainApp()
reactor.run()