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()