From b548c7ed40886c62d6af5a2a1ec78c02d1983a8d Mon Sep 17 00:00:00 2001
From: Stephen Dranger <dranger@gmail.com>
Date: Tue, 15 Feb 2011 11:10:57 -0600
Subject: [PATCH] oyoyo

---
 TODO                                       |   1 -
 irc.py                                     | 299 ++++++++---------
 irc.pyc                                    | Bin 14194 -> 14119 bytes
 menus.py                                   |   2 +-
 menus.pyc                                  | Bin 25570 -> 25571 bytes
 oyoyo/client.py                            |  19 +-
 oyoyo/client.pyc                           | Bin 9938 -> 10126 bytes
 pesterchum.js                              |   2 +-
 pesterchum.py                              |  29 +-
 qt4reactor/.gitignore                      |   9 -
 qt4reactor/LICENSE                         |  57 ----
 qt4reactor/README                          |  55 ----
 qt4reactor/__init__.py                     |   0
 qt4reactor/bin/gtrial                      |  24 --
 qt4reactor/ghtTests/buttonStress.py        |  57 ----
 qt4reactor/ghtTests/fakeAppButtonStress.py |  51 ---
 qt4reactor/ghtTests/ircClient.py           | 106 ------
 qt4reactor/ghtTests/testIterate.py         |  37 ---
 qt4reactor/ghtTests/trivialscript.py       |  25 --
 qt4reactor/gtrial.py                       |  41 ---
 qt4reactor/qt4reactor.py                   | 359 ---------------------
 qt4reactor/twisted/plugins/qt4.py          |   9 -
 22 files changed, 194 insertions(+), 988 deletions(-)
 delete mode 100644 qt4reactor/.gitignore
 delete mode 100644 qt4reactor/LICENSE
 delete mode 100644 qt4reactor/README
 delete mode 100644 qt4reactor/__init__.py
 delete mode 100755 qt4reactor/bin/gtrial
 delete mode 100755 qt4reactor/ghtTests/buttonStress.py
 delete mode 100755 qt4reactor/ghtTests/fakeAppButtonStress.py
 delete mode 100644 qt4reactor/ghtTests/ircClient.py
 delete mode 100755 qt4reactor/ghtTests/testIterate.py
 delete mode 100755 qt4reactor/ghtTests/trivialscript.py
 delete mode 100755 qt4reactor/gtrial.py
 delete mode 100644 qt4reactor/qt4reactor.py
 delete mode 100644 qt4reactor/twisted/plugins/qt4.py

diff --git a/TODO b/TODO
index 38c46ad..be2733d 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,4 @@
 Bugs:
-* memos need to test for max time
 * color swatch text doesnt disappear
 * X and _ buttons move around all crazy like
 
diff --git a/irc.py b/irc.py
index 1a87048..dc6ae08 100644
--- a/irc.py
+++ b/irc.py
@@ -1,9 +1,10 @@
 from PyQt4 import QtGui, QtCore
-from twisted.internet.protocol import ClientFactory
-from twisted.words.protocols.irc import IRCClient
-from twisted.internet import reactor
+from oyoyo.client import IRCClient
+from oyoyo.cmdhandler import DefaultCommandHandler
+from oyoyo import helpers
 import logging
 import random
+import socket
 
 from dataobjs import Mood, PesterProfile
 from generic import PesterList
@@ -17,32 +18,31 @@ class PesterIRC(QtCore.QObject):
         self.config = config
     def IRCConnect(self):
         server = self.config.server()
-        port = int(self.config.port())
-        nick = self.mainwindow.profile()
-        self.cli = PesterIRCFactory(nick, self)
-        logging.info("---> Logging on...")
-        reactor.connectTCP(server, port, self.cli)
-        reactor.run()
+        port = self.config.port()
+        self.cli = IRCClient(PesterHandler, host=server, port=int(port), nick=self.mainwindow.profile().handle, real_name='pcc30', blocking=True)
+        self.cli.command_handler.parent = self
+        self.cli.command_handler.mainwindow = self.mainwindow
+        self.conn = self.cli.connect()
+        self.brokenConnection = False
     def closeConnection(self):
-        #logging.info("---> Logging on...")
-        # self.cli.close()
-        pass
+        self.cli.close()
+    def setConnectionBroken(self, broken=True):
+        self.brokenConnection = True
     @QtCore.pyqtSlot(PesterProfile)
     def getMood(self, *chums):
-        self.cli.getMood(*chums)
+        self.cli.command_handler.getMood(*chums)
     @QtCore.pyqtSlot(PesterList)
     def getMoods(self, chums):
-        self.cli.getMood(*chums)
+        self.cli.command_handler.getMood(*chums)        
     @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
     def sendMessage(self, text, handle):
         h = unicode(handle)
         textl = [unicode(text)]
-        CMD_LENGTH = 450
         def splittext(l):
-            if len(l[0]) > CMD_LENGTH:
-                space = l[0].rfind(" ", 0,CMD_LENGTH)
+            if len(l[0]) > 400:
+                space = l[0].rfind(" ", 0,400)
                 if space == -1:
-                    space = CMD_LENGTH
+                    space = 400
                 a = l[0][0:space]
                 b = l[0][space:]
                 if len(b) > 0:
@@ -52,72 +52,117 @@ class PesterIRC(QtCore.QObject):
             else:
                 return l
         textl = splittext(textl)
-        for t in textl:
-            self.cli.msg(h, t)
+        try:
+            for t in textl:
+                helpers.msg(self.cli, h, t)
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString, bool)
     def startConvo(self, handle, initiated):
         h = unicode(handle)
-        if initiated:
-            self.cli.msg(h, "PESTERCHUM:BEGIN")
-        self.cli.msg(h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
+        try:
+            if initiated:
+                helpers.msg(self.cli, h, "PESTERCHUM:BEGIN")
+            helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString)
     def endConvo(self, handle):
         h = unicode(handle)
-        self.cli.msg(h, "PESTERCHUM:CEASE")
+        try:
+            helpers.msg(self.cli, h, "PESTERCHUM:CEASE")
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot()
     def updateProfile(self):
         me = self.mainwindow.profile()
         handle = me.handle
-        self.cli.setNick(handle)
+        try:
+            helpers.nick(self.cli, handle)
+        except socket.error:
+            self.setConnectionBroken()
         self.updateMood()
     @QtCore.pyqtSlot()
     def updateMood(self):
         me = self.mainwindow.profile()
-        self.cli.msg("#pesterchum", "MOOD >%d" % (me.mood.value()))
+        try:
+            helpers.msg(self.cli, "#pesterchum", "MOOD >%d" % (me.mood.value()))
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot()
     def updateColor(self):
         me = self.mainwindow.profile()
         for h in self.mainwindow.convos.keys():
-            self.cli.msg(h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
+            try:
+                helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
+            except socket.error:
+                self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString)
     def blockedChum(self, handle):
         h = unicode(handle)
-        self.cli.msg(h, "PESTERCHUM:BLOCK")
+        try:
+            helpers.msg(self.cli, h, "PESTERCHUM:BLOCK")
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString)
     def unblockedChum(self, handle):
         h = unicode(handle)
-        self.cli.msg(h, "PESTERCHUM:UNBLOCK")
+        try:
+            helpers.msg(self.cli, h, "PESTERCHUM:UNBLOCK")
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString)
     def requestNames(self, channel):
         c = unicode(channel)
-        self.cli.sendMessage("NAMES", c)
+        try:
+            helpers.names(self.cli, c)
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot()
     def requestChannelList(self):
-        self.cli.sendMessage("LIST")
+        try:
+            helpers.channel_list(self.cli)
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString)
     def joinChannel(self, channel):
         c = unicode(channel)
-        self.cli.join(c)
+        try:
+            helpers.join(self.cli, c)
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString)
     def leftChannel(self, channel):
         c = unicode(channel)
-        self.cli.leave(c)
+        try:
+            helpers.part(self.cli, c)
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
     def kickUser(self, handle, channel):
         c = unicode(channel)
         h = unicode(handle)
-        self.cli.kick(h, c)
+        try:
+            helpers.kick(self.cli, h, c)
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
     def setChannelMode(self, channel, mode, command):
         c = unicode(channel)
-        m = unicode(mode).replace("+", "")
+        m = unicode(mode)
         cmd = unicode(command)
         if cmd == "":
             cmd = None
-        self.cli.mode(c, True, m, cmd)
+        try:
+            helpers.mode(self.cli, c, m, cmd)
+        except socket.error:
+            self.setConnectionBroken()
     @QtCore.pyqtSlot()
     def reconnectIRC(self):
-        pass
+        self.setConnectionBroken()
+
+    def updateIRC(self):
+        self.conn.next()
 
     moodUpdated = QtCore.pyqtSignal(QtCore.QString, Mood)
     colorUpdated = QtCore.pyqtSignal(QtCore.QString, QtGui.QColor)
@@ -129,48 +174,10 @@ class PesterIRC(QtCore.QObject):
     nickCollision = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
     connected = QtCore.pyqtSignal()
     userPresentUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString,
-                                          QtCore.QString)
-
-
-class PesterIRCClient(IRCClient):
-    realname = "pcc30"
-    username = "pcc30"
-
-    def __init__(self, nick, qobj):
-        self.nickname = nick
-        self.parent = qobj
-        self.mainwindow = qobj.mainwindow
-        qobj.irc = self
-
-    def msg(self, user, message):
-        logging.info("---> send PRIVMSG %s %s" % (user, message))
-        IRCClient.msg(self, user, message)
-
-    def signedOn(self):
-        logging.info("---> recv WELCOME")
-        self.parent.connected.emit()
-        self.join("#pesterchum")
-        mychumhandle = self.mainwindow.profile().handle
-        mymood = self.mainwindow.profile().mood.value()
-        self.msg("#pesterchum", "MOOD >%d" % (mymood))
-        
-        chums = self.mainwindow.chumList.chums
-        self.getMood(*chums)
-
-    def getMood(self, *chums):
-        chumglub = "GETMOOD "
-        for c in chums:
-            chandle = c.handle
-            if len(chumglub+chandle) >= 350:
-                self.msg("#pesterchum", chumglub)
-                chumglub = "GETMOOD "
-            chumglub += chandle
-        if chumglub != "GETMOOD ":
-            self.msg("#pesterchum", chumglub)
+                                   QtCore.QString)
 
+class PesterHandler(DefaultCommandHandler):
     def privmsg(self, nick, chan, msg):
-        # do we still need this?
-        #msg = msg.decode("utf-8")
         # display msg, do other stuff
         if len(msg) == 0:
             return
@@ -178,7 +185,7 @@ class PesterIRCClient(IRCClient):
         if msg[0] == '\x01':
             return
         handle = nick[0:nick.find("!")]
-        logging.info("---> recv PRIVMSG %s :%s" % (handle, msg))
+        logging.info("---> recv \"PRIVMSG %s :%s\"" % (handle, msg))
         if chan == "#pesterchum":
             # follow instructions
             if msg[0:6] == "MOOD >":
@@ -191,7 +198,8 @@ class PesterIRCClient(IRCClient):
                 mychumhandle = self.mainwindow.profile().handle
                 mymood = self.mainwindow.profile().mood.value()
                 if msg.find(mychumhandle, 8) != -1:
-                    self.msg("#pesterchum", "MOOD >%d" % (mymood))
+                    helpers.msg(self.client, "#pesterchum", 
+                                "MOOD >%d" % (mymood))
         elif chan[0] == '#':
             if msg[0:16] == "PESTERCHUM:TIME>":
                 self.parent.timeCommand.emit(chan, handle, msg[16:])
@@ -212,88 +220,89 @@ class PesterIRCClient(IRCClient):
                 self.parent.colorUpdated.emit(handle, color)
             else:
                 self.parent.messageReceived.emit(handle, msg)
-    
-    def irc_ERR_NICKNAMEINUSE(self, prefix, params):
-        logging.info("---> recv NICKINUSE %s %s" % (prefix, params))
+
+
+    def welcome(self, server, nick, msg):
+        self.parent.connected.emit()
+        helpers.join(self.client, "#pesterchum")
+        mychumhandle = self.mainwindow.profile().handle
+        mymood = self.mainwindow.profile().mood.value()
+        helpers.msg(self.client, "#pesterchum", "MOOD >%d" % (mymood))
+
+        chums = self.mainwindow.chumList.chums
+        self.getMood(*chums)
+
+    def nicknameinuse(self, server, cmd, nick, msg):
         newnick = "pesterClient%d" % (random.randint(100,999))
-        self.setNick(newnick)
+        helpers.nick(self.client, newnick)
         self.parent.nickCollision.emit(nick, newnick)
-    def userQuit(self, nick, reason):
-        logging.info("---> recv QUIT %s %s" % (nick, reason))
+    def quit(self, nick, reason):
         handle = nick[0:nick.find("!")]
         self.parent.userPresentUpdate.emit(handle, "", "quit")
         self.parent.moodUpdated.emit(handle, Mood("offline"))
-    def userKicked(self, kickee, channel, kicker, msg):
-        logging.info("---> recv KICK %s %s %s %s" % (kickee, channel, kicker, msg))
-        self.parent.userPresentUpdate.emit(kickee, channel, "kick:%s" % (op))
+    def kick(self, opnick, channel, handle, op):
+        self.parent.userPresentUpdate.emit(handle, channel, "kick:%s" % (op))
         # ok i shouldnt be overloading that but am lazy
-    def userLeft(self, nick, channel, reason="nanchos"):
-        logging.info("---> recv LEFT %s %s" % (nick, channel))
+    def part(self, nick, channel, reason="nanchos"):
         handle = nick[0:nick.find("!")]
         self.parent.userPresentUpdate.emit(handle, channel, "left")
         if channel == "#pesterchum":
             self.parent.moodUpdated.emit(handle, Mood("offline"))
-    def userJoined(self, nick, channel):
-        logging.info("---> recv JOIN %s %s" % (nick, channel))
+    def join(self, nick, channel):
         handle = nick[0:nick.find("!")]
         self.parent.userPresentUpdate.emit(handle, channel, "join")
         if channel == "#pesterchum":
             self.parent.moodUpdated.emit(handle, Mood("chummy"))
-    def modeChannel(self, op, channel, set, modes, args):
-        logging.info("---> recv MODE %s %s %s %s %s" % (op, channel, set, modes, args))
-        if set:
-            modes += "+"
-        else:
-            modes += "-"
-        handle = ""
-        print args
+    def mode(self, op, channel, mode, handle=""):
         self.parent.userPresentUpdate.emit(handle, channel, mode)
-    def userRenamed(self, oldnick, newnick):
-        logging.info("---> recv RENAME %s %s" % (oldnick, newnick))
+    def nick(self, oldnick, newnick):
+        oldhandle = oldnick[0:oldnick.find("!")]
         newchum = PesterProfile(newnick, chumdb=self.mainwindow.chumdb)
-        self.parent.moodUpdated.emit(oldnick, Mood("offline"))
-        self.parent.userPresentUpdate.emit("%s:%s" % (oldnick, newnick), "", "nick")
+        self.parent.moodUpdated.emit(oldhandle, Mood("offline"))
+        self.parent.userPresentUpdate.emit("%s:%s" % (oldhandle, newnick), "", "nick")
         if newnick in self.mainwindow.chumList.chums:
             self.getMood(newchum)
-    def irc_RPL_NAMREPLY(self, prefix, params):
-        logging.info("---> recv NAMREPLY %s %s" % (prefix, params))
-        # namelist = names.split(" ")
-        # logging.info("---> recv \"NAMES %s: %d names\"" % (channel, len(namelist)))
-        # if not hasattr(self, 'channelnames'):
-        #     self.channelnames = {}
-        # if not self.channelnames.has_key(channel):
-        #     self.channelnames[channel] = []
-        # self.channelnames[channel].extend(namelist)
-    def irc_RPL_ENDOFNAMES(self, prefix, params):
-        logging.info("---> recv ENDOFNAMES %s %s" % (prefix, params))
-        # namelist = self.channelnames[channel]
-        # pl = PesterList(namelist)
-        # del self.channelnames[channel]
-        # self.parent.namesReceived.emit(channel, pl)
-    def irc_RPL_LISTSTART(self, prefix, params):
-        logging.info("---> recv LISTSTART %s %s" % (prefix, params))
-        # self.channel_list = []
-        # info = list(info)
-        # self.channel_field = info.index("Channel") # dunno if this is protocol
-    def irc_RPL_LIST(self, prefix, params):
-        logging.info("---> recv LIST %s %s" % (prefix, params))
-        # channel = info[self.channel_field]
-        # if channel not in self.channel_list and channel != "#pesterchum":
-        #     self.channel_list.append(channel)
-    def irc_RPL_LISTEND(self, prefix, params):
-        logging.info("---> recv LISTEND %s %s" % (prefix, params))
-        # pl = PesterList(self.channel_list)
-        # self.parent.channelListReceived.emit(pl)
-        # self.channel_list = []
+    def namreply(self, server, nick, op, channel, names):
+        namelist = names.split(" ")
+        logging.info("---> recv \"NAMES %s: %d names\"" % (channel, len(namelist)))
+        if not hasattr(self, 'channelnames'):
+            self.channelnames = {}
+        if not self.channelnames.has_key(channel):
+            self.channelnames[channel] = []
+        self.channelnames[channel].extend(namelist)
+    def endofnames(self, server, nick, channel, msg):
+        namelist = self.channelnames[channel]
+        pl = PesterList(namelist)
+        del self.channelnames[channel]
+        self.parent.namesReceived.emit(channel, pl)
+
+    def liststart(self, server, handle, *info):
+        self.channel_list = []
+        info = list(info)
+        self.channel_field = info.index("Channel") # dunno if this is protocol
+    def list(self, server, handle, *info):
+        channel = info[self.channel_field]
+        if channel not in self.channel_list and channel != "#pesterchum":
+            self.channel_list.append(channel)
+    def listend(self, server, handle, msg):
+        pl = PesterList(self.channel_list)
+        self.parent.channelListReceived.emit(pl)
+        self.channel_list = []
     
-class PesterIRCFactory(ClientFactory):
-    protocol = PesterIRCClient
-    
-    def __init__(self, nick, qobj):
-        self.irc = self.protocol(nick, qobj)
-    def buildProtocol(self, addr=None):
-        return self.irc
-    def clientConnectionLost(self, conn, reason):
-        conn.connect()
-    def clientConnectionFailed(self, conn, reason):
-        conn.connect()
+    def getMood(self, *chums):
+        chumglub = "GETMOOD "
+        for c in chums:
+            chandle = c.handle
+            if len(chumglub+chandle) >= 350:
+                try:
+                    helpers.msg(self.client, "#pesterchum", chumglub)
+                except socket.error:
+                    self.parent.setConnectionBroken()
+                chumglub = "GETMOOD "
+            chumglub += chandle
+        if chumglub != "GETMOOD ":
+            try:
+                helpers.msg(self.client, "#pesterchum", chumglub)
+            except socket.error:
+                self.parent.setConnectionBroken()
+            
diff --git a/irc.pyc b/irc.pyc
index a7a711649de4e26496e6d430e1fa25e01f134bb4..301298b02dd19931612ca0af69486cfd39e4774f 100644
GIT binary patch
literal 14119
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
ZXqFLu*F1bF|M0$YG=rziOlI+K{}-rugI@pu

literal 14194
zcmcIr%WoV<das@rIeb%mNR(`gv@Dyp_1ML3;*F6gYmG)SEpkW|DQkBEEi;^MQZ4p$
zlhaK~Or&7364o}>39vvCAP*qBNq`{P1jyfzb1pdq$R$9MLvqP!uQ_afzpwf+97+Z?
zT8XSubyaoM_x`^6s`)QJA0GI@$MvPAivJDb|8Jv&|AETJucfw?@+?)il$TTWoU+)P
zQ`>pv6;!=op7UzEsJxP@m&|iPZTBg!U)B4~b5U)VRlO`LqCKGM1JW+3?Lp-Ysrr!e
zhE;vo%<fa$Bgz|9^-*OxW#x`3yI-*}uAV5%vdhZJDffihw3I!do+|XJ@75=jJ*b?L
z^bV!Hlgb`ePC<G{(%va$7uD+gD3El2fq(Nkw4x%aThWa@H!4Z1611IpizCCRDxT~3
z(e*|%3fc#zyNvDyT`?o3s}EgmM<&(Gss;2c1%WM#tT<uhv{%}}rt8IP4`AGMFS=nQ
zKzPo+<3(KE3}BCxLtO4EO8C!!*->N+I07PoH{cc<c`}HNf^v$=Eh(o@J;AiR;16Dc
znx?-j{e`4|AnC^_Xesp*EL%(_3>}^@tT<XqMn?{fa>u@8Xfz%Ir@%|VhSB<1M`t<d
z9Pj8f%*D(TM^+w8=1&|MGR((YlShVzleeZib}?ds`O`<{k0$d^9vK=-hE5$C%7`|e
zOqhAbgm@Y_bt0K|I-W)nO(dOXr1LeY!I9TV@cJ8vJI^Y2R{G}-_b2SDzxj;*6oC4<
zuN-~r@F)ZZ0Gv-cMSkjUCy$54rv6Uc4~**b&zO_J@UGIU^OKP3W*!v_6|#a7zKtq^
znp(=usYfC>)+1@H=hY*NUF!v^z^W)<ZJt`JMQf>xx0W~VJIyHSM|*wU_1$QFJ)+9B
z8m|Aq_3hw+XnQm8H{GpyZXFd+g^stWsrE8zCeGtgNfo|@*5!La%eic~8~&ElzWg9)
zZ(rUK^={tVYh8BR%}YB6ng(?QC8SAF)|gecngot}un3O(XaEma(0roQV@pMXB75>Q
zZ%~)Vrl+9XB6}X^3ECsj0HcN;eF|v0u(B5CTR;<9OcI$lVFdD~cZdtnkgtk~s)Z*2
zz{QIfugoq6TU)NbH5>SsE?t@@{uvQMr@imAqdYoYKN2eK1no!<p(7%#cZ^w!#?Vd;
zS8OolwWjCFM9=IZOSt}Kph*eg3fSO-U2ByUd7-`M&vQ(ZgqrJWf+{ciZgaZ_j>tAN
z+GZy%7jkuFjapMEC#+#>+VF^cVyU2n1Vbpso|@U1O#!VJIOu$uUJyDlByQlJMpsD5
zS9uW<Hd$_<gjA1INWj%E6BZhPI8RTZ5(;lQ5q*xZS0EI>Lct7sfK|Vab>BcSz~!0=
zVEkXGb6ntLr3@;~MSB29)H%j6oa+FL!bwe|5nwyqLIv{y6`%@1XV4b)*uo?1hNZr+
z)MrY4W~oj1vYh&%QeP<bIi?llm4bRW$M&_mGwN|sIABZ>G!Kf1fN1x+x+9Xo4QuW{
zfgi#>N(|wcKnZ_}ihDzdEHwjIKDX5Uf(m}1thf&r4%=fCBT!V>000V~eLtsy0cD{V
zT|V?Wo{yQBFlWpH7sb=r7(>_$ylZxt!W%gSbcs`*4UC;!jXPKUqO7<o7!c$dj1FL>
zRLyn-M?}s;07Q)1n=qWBm@>_i&TyyEbmX}q^@a(5%9tPSK=C8zVI;~|S-Q5qSgqYy
z`&c-Cd|UbsmU@@VQQDMs(wc#;4p`&Xh&5qN<py)ceD`5+57r^D9itEgy%lcFQ!tD&
z5Kn=JUH2Nk?HS1L2__uz1c%7Trsx)T*L*%Uz!8mq==k=M6NZf~=Pf?ch^gEVEKSKe
zF+61TsgfmLk1(-RP{K7-si=af;9zi47!^B0L=Ipiq%W3;tlJp-ymBGGu+-u#ra9B5
zy(cYd0>nynb*-u^AKza3@M`tOLM<exD$9$@diKis&=8B%78>gJ*rKJ^6lc-DX5a<w
zX3GeIvCtPd#&zgu-A3ft`XZhT714MB+R;acQKKEfJnjd-#;1O+0Mhk=^<sjE3`+$i
zJclYHnW2KwRMD!G0%Yb93RJ2etyYCN`Y_I>PuvfY>lq!w4oMJXeiLo+Cqza}1(8T?
z>967;HS|v;8h~me`iUa|VXZM}v;~I4hPzbSnqghaD=l!Ro*{Ii8a$_H;@*x89*LtM
z1V%RqN6V47df^*m2D5H_%Bi3uX5A>FpD}|mcS)uF8+ho(7>MDJCt?n>NCG60Pq#2r
z%g$wym+ZB)ynGE}Y<JUwWDr@F5TeCn84cQg!`pK@+|mc=>j8O9)-9mK+#Suh%Uy&L
zpQ6!)>%(s+J%TY<e(mmsgxi8OxJ`2pn&20DkGjNKNU{57?0<}8Q~feErZMc$TiUxO
z6)F@0Y&!>Ghkkz@qa6ZID)cPy<cg7l3hl%a9#bippv@z+`M6X#dJa|33Vd~OxpMPW
zDlqZc@Pg*HV^<JHX+lf^79D;`To{CQnB&{ESB{Nnso#MQ#j+QCD3*w=689n^C@NL^
zXsK%4O@~GO8g|J9Od{1L+^>&p2TfPA2c3&(=-STi9*$nMM#~B7hcMx?ECfuXY!Z!B
zk?2DaQHf_Xpp&JG3#)6!7>KzLpt*+F$6i1re`63U23@9lzK7n<fSSsXf;o$a=P5%9
zGJ&df3BBm`{lN7zI_r-xPhUmV3-A<tt`|ezz~fOt`6a+j3+XwHedm`6xaVv}G2qT2
zz)5WG$p1wyE~7z$A2}%Clt6hgp#<89trKZ~F8#N8Hs$hJSd=lLXOr5G1-bHVoW$V>
z;+z6v2t*N-TiSYVD)>OAI|QhZ;RZ|~_VB4d0|y%7EXRe%$R?E7A0Ifd0J@U!(gYn?
z21g+L!uy!iZyZsI`o52~AY&3{zsv0jP}|w@crYM<wZM-<az3>XpiB2!8_ri8mBd*h
ztt-j<&7jpnfG*-IyR=Ly0dXg$7>Pc9M9vKm0mUM*1PFZg(1y^Ep8_d)q>!MD++`7R
z5@1jyNiyL{NxrFPn_$cqd@=JK$VcpB#n>9mGuVzZRz6HxoUvM82gUY0JW5tcpTR8s
z8Y@C6^LGw*qg5}6wD5qZb}e*0%L#1VT8-Ms?`YaIq5Vb>c$)E-{wAw$v0@aXe}mN(
zRt#4)iJ+^jZm?Qlb(7U?R`ebcQ3?R=mf!F+B~ea5jIwS^q-oP@nvB{li3-Gc%Q+&R
z5HTwPBhKTJcAAd6@7OY_<+OsdgC;3*TgaS<Y)EFEgbLFcJft);Q=9hF?=f+N-90x%
z%0*5qF&D7aIL7y&UMp<}2e-&Tl?j@(jxBAPp%`}zNym~^E|mMrg;V)c`MIe#N@ogF
zrK!SH@l1g~Q+d{N#drGA2RS@ty@4Mlq$raoEFnyqR5LdYO{^;aXnkqk!+%8OAlnN4
zMvb5mwS>@5(LlDtTniMLR$vd*U2^~RzBtE^n*o!~=Ut5??|D%p3H$n>!o>^kTaYOq
z;#~_@7*@8V+v_8|XJOB%vL&I8?7l7O^ZK~5C0ULvzAZ^{Ue!q28yR}!edRz&Y1wHu
zzwx!B8Q}LZg!msucwOu>8xnPxgz&l?^<cM=54JN6E#NKD39_5QA&K0bMjLE3Da$-&
zAWPffZkT@)t=J7V?q^&-KEo-PKSD8)E>1gT+GcLVLXavPy@U$3(pX}mmSgg1ntmO5
zUmSXXqUlB=s3STFPvS+%(=w|tyP_99U0S^{dp^Xk$&*fFg61sE4dtYh;U}n3y2TX4
zTGk7iv`;@qF>wG-<Fr6TFGcZlJftWRF`#4)bS5tmpIqsN;=z(~k)g({e#w2Ik@n!~
zZ-7Jb0T_}9(Uzo$9Dukq<_;-$m?=I!j?k%nnPf-_RV}1$3-Q`cbAR?ub+NL%R1L|p
z=bj#EjdVuSZ9Z<I5=uHPH`3SHMO!X;5p&jNvO<5zDmCQHyfWsKgr`Y@5-9N=#=aeJ
zN}P(K^==)|ZoX84G=w2`>}CItu$;*|!MTm0oPbf8%*CfuY90&idiZ@*hz>yLhngaI
z?d~T+Qn-JRAMRg-73t8EU>jqFq6$tY%rz{L=t!23*a6yEP#<8y-C=bHf{xYi=T$Iy
z^d(NWslbL^{e-EyCMS9Kdl0a0RvE#*fN6S;6^Y#ubTPGT@>C$~mbbTILI`?6dIqHY
zdJ%p4)SsZlImnXa@5)etF@y~xply@TQy?f<qU}#%o}mgR$|Cy6<ss=xR4sI(XNHvQ
z?3EA=<fny@;B1bxH~A2Rpb~{97K1b`%Ip6Lnwo@3NzK80bIDJYb#R*U%D@eSC3O~@
z!!JU=JN{W!fLXjhpn?fptXmf50UR^xBg*{_b*YakiEAZp8)v+~pG$V?$4+J0Y29#C
zE}f{xLku7Je%doky`&oV5ftFYcoT{Z1O`PYPMIa(Lg1o?)T6#Q!BMrWU2N%p%VO4A
z2sSKx>J;M<z%eR)b7m1hJtk0#021{Ppbm4&m14BU4^7~p|F3u(cy@w4B+DM=7<}R!
zyc#L|rGcSeoy8{!aB!qvzdCqdZNVHq2HE<dMHA}rfO=FGE&yXoo!I?_^?PN`yyL%_
za$#Dy00hnN7OnyMl`tZjQ0^qxf>C>nTRp><se~_sawzfR6ftn0PDUU;$#I3pP?Oyc
zX|F8_r{}^`XlHh@!>N3DKJ3;x*jdOvE0bY^a6q_O#i|NX0ciZD;!qw=wmJun;B8hN
z*^ae^rRtTCOT<|#nR{VgyciW*j4f{*IxH~_mZm|MsPj{%@2c%~(AHw2?{aj9)mK_(
zy}>CBuEO=?mO>&_&7-+Sw4wBGqnejh<ZO~g2s9yLaXL(Xn=gc@B-D_w)}-0if9vaa
z1uK(nl#w2ckI;Fi?e60;<7e0=WXz`u6IL-dZWVEBdD0rrO<H;DG%V3L>PfNmGuAAg
zCat;L>xCK2o5XEN-g+N%CJ|nq&PjYitHmM~YYmOmc^{5XV5wp|1dYNC1HzWr<%&^)
zWXGP}cyAbBs9q|8By%mCL7fR)ZJ}~=p>}(<YT_8Vj5XO@6Xl+_U6@Mc{!bp9U__mX
zfzTQE?{?4<+OUbMY5gmlC6eH<C6YkSWp~LY7;HP6?nBdTv>UBZ7V(`23>kaj57R}6
zDA%i6uM;iizy!5_!$2Gpo&=xZC4h|3n-me6Z=&%Wnu7x%JKX_{U+fM{m?NihTmaC#
z!F0nq!-ml|U;q)33;-!ueK02F0(Gq>4lLust=kK0h5>N&pxFafOxlX}?c$p#Ax^Nl
z>AAi$Pc6^*pnsQ-LhvoNgy5otL@%Sd7WJX^z$a2+;P-sV!0j2x-U3|zj&=uaXRIbS
zW#L9bTOPl<XbXzL&Tke>)J>9!cSUe&<ehR}#9$JClV7!v@Mko;2^Jfg#iC2XGC>2w
zX|f<)p<_xMsE|^@P8O|*5b{;Wkwj7U+%|Cy=D%pJV*sy^NH@8x{a<*B<Mr1qBj^pk
zc`pbhU`a?O<Rb68$dv{g&k#l9E>ls&Bg7P@fue|(iA`p^<Prf%r(CA7-?L<Mv3mW8
zY>HSiyVvcZ^p8lX7m`mRZxVInuSwLw&8n9c&qcuZ-&iOlD|lf&Emzh@mH!1Penk0?
zLveijaBPZyvb<0`VhyenlR;D+$0d<do?Kc7FGh3vnr@n3KwmFrHxcwtkfnC)|0S4R
z1g9mSWU+{dQxm&H+*kxexSP~_`Y{|+M)05HO+G*X?hl%$JkB%dEJ%P`R5w08WIhn|
zitR{E2gR!v^5Bq}j-};mvA;_Vh+ZU249qTy;r(h0qE|4nBWd~vcr=!SmI^6O32B&O
zH6ci&y%qK>&ty86s+f%0&$$D`A%)1I&l<@M;9G`b=F`$R;!iN8i^_>1fd!<{qBh=&
zAKO@QB!SNe<>(JfjQGKLJUTB<#Tc>y5ah&Ab3q;#;NpzIi5HYp$NiED#?6a3PKfK2
z4<-;$tP1gJAx(6~7hR>#HatmX+ujhP5|WYULl}4Ak2o{)iCkvpDQG6>{ByR%C1%m6
z7#fP6-etuTYIiPx5*m0mBM|*L+Qw2l52$0k=muFwC!ihX!0i9SV>pD0*65V=nstsJ
zyHR9VQpPd0{2ay!>FB;{T?YpcPFGhJziWtR?$1)fWJxIfL%gd$L)8m9V|b4dmR?z0
zPd3a!XH4e2XG{+J^=j?f@^#9=>MO;C$FT&LY|Fuy$L4T+&D+e?wU2b{LSGSMcos{L
zNp~E7d0Zy3Zl<1G`IYj7s?mwY^vh$wbL?RZ!0cCw1hbZ%NMOG+86NXIrRY1XzJp3q
zWC+JS$&vS?hvtG0=9cL_PJ}HmmP$Wi>kC%D&+5ml{*culv-&euf5GY}tbWSsFIoK+
ztG`B-DZR9gpIX0%muWTRgYXg@PAOL|;YMW;KNrhI{1k>t=qZ=`P)h946rLuE{G-jf
ziW2g4=-{Tp;-8W5dc^$sgm`r1U7q58L+(MJVh$WlUa`^7n+n?R)L7DBR=6J3jZP=c
z*^5)d6$KVf?hCoOP#WJzM*$K5+Cxu=Rg}oH#Xb~M9iKKd@^57vZgMvkbQuuoRSbv;
zKpZkKaH7dW7tm_hcDn}<<O*(M&-HA4^(6?t-2o3t#gf9q;DP5^e7cU_4mxp$xV-pE
zE~AhH{Or|VVoon0iZsX{@l?#ME3W?*@gWrNal8G;@gUWIM^K2F31xrAR<GdESK<SC
zTu)`-U2ovegzWb+0Ba@#l4`G}TIj!FMIq43tp1kO-?RD$R2iwHb$qEJz2>+zgK5#X
z+)%EZn<t<WU9KG5ioR(QW~5g{MTj2Ymcy|xAq@j7;zXBHffp5!z#^W4znHMYbUeHS
zlO?xS^7jMX>u3t6$v8aaHvOpZ@nQoPb)j4}%ayl?&X(ij`-rC8e9Hy&Mk92aAS(XA
zL!@Y-c70hBDY>9EBqXxEQc8gNb#pm?g_Mw>avE0x{K4lLIka;VxtZUY{?q>lz(wLx

diff --git a/menus.py b/menus.py
index c6be3a1..e68aea9 100644
--- a/menus.py
+++ b/menus.py
@@ -649,6 +649,6 @@ class LoadingScreen(QtGui.QDialog):
 class AboutPesterchum(QtGui.QMessageBox):
     def __init__(self, parent=None):
         QtGui.QMessageBox.__init__(self, parent)
-        self.setText("P3ST3RCHUM V. 3.14 alpha 4")
+        self.setText("P3ST3RCHUM V. 3.1.4 alpha 4")
         self.setInformativeText("Programming by illuminatedwax (ghostDunk), art by Grimlive (aquaMarinist)")
         self.mainwindow = parent
diff --git a/menus.pyc b/menus.pyc
index 8a86c4c53026c2a0a4186fabc5c49b127742422b..23b319bb18a484dbbd7b1f0568555f1ce386319d 100644
GIT binary patch
delta 46
zcmaEKobmB-M)r%Jc)7}bqc*bVCyGfkFfard2ZtC3IeUcqDun4N80#78ZSGDCX956#
C4i07j

delta 45
zcmaESobl0dM)r%Jc)4DGjNHhcpC~HDz`zh-92{aC<m?gZs}QEAV611jxhFB42>_P(
B4^998

diff --git a/oyoyo/client.py b/oyoyo/client.py
index fa60fa1..da5a716 100644
--- a/oyoyo/client.py
+++ b/oyoyo/client.py
@@ -29,8 +29,6 @@ from oyoyo.parse import *
 from oyoyo import helpers
 from oyoyo.cmdhandler import CommandError
 
-from datetime import *
-
 # Python < 3 compatibility
 if sys.version_info < (3,):
     class bytes(object):
@@ -127,7 +125,18 @@ class IRCClient:
 
         msg = bytes(" ", "ascii").join(bargs)
         logging.info('---> send "%s"' % msg)
-        self.socket.send(msg + bytes("\r\n", "ascii"))
+        try:
+            self.socket.send(msg + bytes("\r\n", "ascii"))
+        except socket.error, se:
+            try:  # a little dance of compatibility to get the errno
+                errno = e.errno
+            except AttributeError:
+                errno = e[0]                    
+            if not self.blocking and errno == 11:
+                print "O WELLS"
+                pass
+            else:
+                raise e
 
     def connect(self):
         """ initiates the connection to the server set in self.host:self.port 
@@ -157,16 +166,14 @@ class IRCClient:
             buffer = bytes()
             while not self._end:
                 try:
-                    #logfile.write("recv at %s\n" % datetime.now().strftime("%Y-%m-%d.%H.%M %S"))
                     buffer += self.socket.recv(1024)
-                    #logfile.write("recvd %s at %s\n" % (buffer, datetime.now().strftime("%Y-%m-%d.%H.%M %S")))
-                    #logfile.flush()
                 except socket.error, e:
                     try:  # a little dance of compatibility to get the errno
                         errno = e.errno
                     except AttributeError:
                         errno = e[0]                        
                     if not self.blocking and errno == 11:
+                        print "O WELLS"
                         pass
                     else:
                         raise e
diff --git a/oyoyo/client.pyc b/oyoyo/client.pyc
index f289bd05b4d34627cbf00bf16839959826db0dbe..9cc24225e5ffbe94dc4b2b808f89bbe09431f791 100644
GIT binary patch
delta 1153
zcmY*YU1$_n6h3EmXE(F|KkjCC!^YigHd%?97(p7)HU?7@o9xyJaidj{W=5D0cTILs
zQerK(FFq*hLC}Iqi%)_DnU_8ld?=)-1@)=4P#=8oML{1#eX-|EB-nxT&AHz>_s;#k
zd*{*Q`C7-rSK+{=+JVVg`0zyeJ<QR9iyc=_n+jMFFd{Gq(98iEJ}{$j6v6Pr4*=f-
z>^~5L26Ckh%(z?8)+z}w0$@bJ2(D2{FhXF&zzDBV>%c&ms>J!D)_Z=c739<waCTr4
zu|#{76WB#7%4O_z4trMGaDaM4rAUBVBx)S3pPiXd96d`#AS;|dU2n{T*A9afg}DGU
z4*}w-Ho&DY*fFr;a7u%EBhM`fw;ty@B9{yevRrE@fHTXyqa=iyX~1Pow9F~c)L3f}
zc8@Jwcl*!aYA?&0s)S`Dw-kVab2(h_WGx<p6%^LbP%G3&Cnlz<m38!M>`cs7nKKs`
zn~SbklkTPRem%=%B|zKbpK3O*1T9nFLNBw!fz3SlT8Kfq8!vA3V3E!7U>GA9#SE(G
zRc6IBjU&P#N7{PJ%r}3~yZG%uJBpqN91qc*L_Q++c)^OT+C%>&whxL<4;bp2PM_%f
z4RKik(YY+1pm@T@@(nQBXf!!B)I_hHahLJCE-o|?XBg^s7WU!79l&~>ZI!Qkq1g#&
zbH-OFKo64{Rb$_R&Wq&d82rd1+L}5a)%#h3zC~b=fu5y~`t<>qqkZW<-0J!=#Kbv{
z))?JScg8UHe_QBHdKyRQLiZkZgu8yHYwZI*nWK-;x5{X{Sk))f<tF(&K1CbU`m|4+
z{)|t-KWSUeQ1@`<0o}+w!N<;b9X_RFf{ALy%cyS@*d!ob`Xv37&*B7c>@#|okKkX9
z)_D`LO24c>iBFxL!jiISr!>l))idrC23rozvdu+~V=es);S&Nwbib>B{q(%6$g4(6
z8*<Y^R|R?*+&k2oH_hYvm!y}vQhS9IDYNsI*{}_>HLDNM*-|go#VvK1OV8<b=_=M-
zPmb`oo&N1!-#y0Js)(Xw?%s6CW(0->j)-IGIHxN#U+%*cT`5;^n(mgfc#!@s@1Sx|
jG4_?T%CDe{xB56w+C@M1B(OlYdvcnGk4Q~>lh^(O5H953

delta 1040
zcmX|AOKcNo5T4mxJNCx!7~8Q)aAM;)3Xm$IO<VDZ+LAy+99Y~q2|@_@-%vXtPU2Oo
zRB4f#9%-d^S|RQg2c#URFXcjoKs*E&5GSNchyynyipqrx%xqA8X+DqV`OS>>m&r#H
znTM}}CcK=PsKWaf;olLKrul2;(s>1-G?))T+18*I1{H!jKqC#PykJLQ4neiSPXOxx
zGqX`xME3Z=j=3KHmZt-h35xUEw@h(R0Z<W8!7bA^PzWcAQEp#*%dgbx$1cR3G^Txn
zdAhFsf&Hzre%FUX)D_5ud>rU!nH0W*tx_P0xi!uWLM;SqZQvNdWSHr?2FZ1y=I8Qe
z>vgbS*Gt7P4M%^}iavS~JwX4Y_nUjT4o?itZu%kCld2-?Qa{8Z3u^w)mhW!S{n*Bb
zF_carSZYIDO~V?3{5RYs8NkhkzelJno&|$h)FMzfp)pMx9s6QYz%?C|2kII$48Y}U
zzr~Y=mlsKy_=%}H8cxa$Zv*7FTt}Q{s5qT4h09j~D;1t<vs<GZ(BZy(2ER0QNpZil
z90cVW(;O=^--?eSB@#cIj)y0)S5_J;bT8UPe<kL8jtpvBpU{rv01i@EANgo6TllQd
z9A$uj&UeV7iE+IHJ2exdnjdvAQP<LJ(J_c23}cFJCciR9IDy_~#yp~2r>|2Vc%_;(
zLfOorr0jAxOVecV>R!!1<j?v)ZM{i(HM~QZQJy30xlQ%VQ+&`G-|p2i<7_Au4MuC1
zfaF^eWgVr3?oJ$M$_K-aoa&5HpD)zxMdwlLard8y6SSKB7XNMimtEC%AC#1mK*~*K
zaFc3|y~1*?X?-d7n804Tn9JhF^mnd@>p#i$r^{lV5a?rY*VtUJ?ImlHy7Il-j)_r*
zs=ED^-K<)lQ90j-Q?gOd0d_s78~ICyFmM-inw1D$*|{S>#@gmYJ)*hvj*C_ikW}lm
ztj}|p)f+Ti7{CNo3q>r`g+eEuq|1f<0r?!%8OL^N=WKdb=#9)uUQj@`ZXM<dUr;#U
Yp;LW9?4|j>bh~Ei5lnj$M#t~(0Dj8M1poj5

diff --git a/pesterchum.js b/pesterchum.js
index 185587d..aaa058c 100644
--- a/pesterchum.js
+++ b/pesterchum.js
@@ -1 +1 @@
-{"tabs": true, "soundon": true, "server": "irc.tymoon.eu", "chums": ["unknownTraveler", "tentacleTherapist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr", "arsenicCatnip", "adiosToreador", "cuttlefishCuller", "rageInducer", "gallowsCalibrator", "caligulasAquarium", "terminallyCapricious", "illuminatedWax", "aquaMarinist", "elegantDiversion", "moirailBunp", "uroborosUnbound", "androidTechnician", "midnightSparrow", "apocalypseArisen", "anguillaNuntia", "oilslickOrchid", "confusedTransient", "pretentiousFantasia", "aquaticMarinist", "lyricalKeraunoscopic", "counterRealist", "ectoBiologist", "percipientPedestrian", "asceticClinician", "doctectiveMiracles", "noSense", "obliviousCrafter", "ircMonster"], "defaultprofile": "testProfile", "block": []}
\ No newline at end of file
+{"tabs": true, "soundon": true, "server": "irc.tymoon.eu", "chums": ["unknownTraveler", "tentacleTherapist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr", "arsenicCatnip", "adiosToreador", "cuttlefishCuller", "rageInducer", "gallowsCalibrator", "caligulasAquarium", "terminallyCapricious", "illuminatedWax", "aquaMarinist", "elegantDiversion", "moirailBunp", "uroborosUnbound", "androidTechnician", "midnightSparrow", "apocalypseArisen", "anguillaNuntia", "oilslickOrchid", "confusedTransient", "pretentiousFantasia", "aquaticMarinist", "lyricalKeraunoscopic", "counterRealist", "ectoBiologist", "percipientPedestrian", "asceticClinician", "doctectiveMiracles", "noSense", "obliviousCrafter", "ircMonster"], "defaultprofile": "ghostDunk", "block": []}
\ No newline at end of file
diff --git a/pesterchum.py b/pesterchum.py
index c887918..f645fd5 100644
--- a/pesterchum.py
+++ b/pesterchum.py
@@ -21,6 +21,7 @@ from generic import PesterIcon, RightClickList, MultiTextDialog, PesterList
 from convo import PesterTabWindow, PesterText, PesterInput, PesterConvo
 from parsetools import convertTags, addTimeInitial
 from memos import PesterMemo, MemoTabWindow, TimeTracker
+from irc import PesterIRC
 
 class waitingMessageHolder(object):
     def __init__(self, mainwindow, **msgfuncs):
@@ -1656,6 +1657,26 @@ class PesterWindow(MovingWindow):
     closeSignal = QtCore.pyqtSignal()
     reconnectIRC = QtCore.pyqtSignal()
 
+class IRCThread(QtCore.QThread):
+    def __init__(self, ircobj):
+        QtCore.QThread.__init__(self)
+        self.irc = ircobj
+    def run(self):
+        irc = self.irc
+        irc.IRCConnect()
+        while 1:
+            if irc.brokenConnection:
+                irc.brokenConnection = False
+                self.restartIRC.emit()
+                irc.closeConnection()
+                irc.IRCConnect()
+            try:
+                irc.updateIRC()
+            except socket.error:
+                irc.setConnectionBroken()
+
+    restartIRC = QtCore.pyqtSignal()
+
 class PesterTray(QtGui.QSystemTrayIcon):
     def __init__(self, icon, mainwindow, parent):
         QtGui.QSystemTrayIcon.__init__(self, icon, parent)
@@ -1675,9 +1696,6 @@ class MainProgram(QtCore.QObject):
     def __init__(self):
         QtCore.QObject.__init__(self)
         self.app = QtGui.QApplication(sys.argv)
-        from qt4reactor import qt4reactor
-        qt4reactor.install()
-        from irc import PesterIRC
         if pygame.mixer:
             # we could set the frequency higher but i love how cheesy it sounds
             try:
@@ -1726,7 +1744,9 @@ class MainProgram(QtCore.QObject):
 
         self.irc = PesterIRC(self.widget.config, self.widget)
         self.connectWidgets(self.irc, self.widget)
-        self.irc.IRCConnect()
+        self.ircapp = IRCThread(self.irc)
+        self.connect(self.ircapp, QtCore.SIGNAL('restartIRC()'),
+                     self, QtCore.SLOT('restartIRC()'))
 
     def connectWidgets(self, irc, widget):
         irc.connect(widget, QtCore.SIGNAL('sendMessage(QString, QString)'),
@@ -1846,6 +1866,7 @@ class MainProgram(QtCore.QObject):
             sys.exit(0)
 
     def run(self):
+        self.ircapp.start()
         self.widget.loadingscreen = LoadingScreen(self.widget)
         self.connect(self.widget.loadingscreen, QtCore.SIGNAL('rejected()'),
                      self.widget, QtCore.SLOT('close()'))
diff --git a/qt4reactor/.gitignore b/qt4reactor/.gitignore
deleted file mode 100644
index e1fb410..0000000
--- a/qt4reactor/.gitignore
+++ /dev/null
@@ -1,9 +0,0 @@
-*.[oa]
-*~
-*.pyc
-*.pyo
-*.class
-.project
-.pydevproject
-*.cache
-_trial_temp
diff --git a/qt4reactor/LICENSE b/qt4reactor/LICENSE
deleted file mode 100644
index 5fc7ac7..0000000
--- a/qt4reactor/LICENSE
+++ /dev/null
@@ -1,57 +0,0 @@
-Copyright (c) 2001-2010
-Allen Short
-Andy Gayton
-Andrew Bennetts
-Antoine Pitrou
-Apple Computer, Inc.
-Benjamin Bruheim
-Bob Ippolito
-Canonical Limited
-Christopher Armstrong
-David Reid
-Donovan Preston
-Eric Mangold
-Eyal Lotem
-Itamar Shtull-Trauring
-James Knight
-Jason A. Mobarak
-Jean-Paul Calderone
-Jessica McKellar
-Jonathan Jacobs
-Jonathan Lange
-Jonathan D. Simms
-Jürgen Hermann
-Kevin Horn
-Kevin Turner
-Mary Gardiner
-Matthew Lefkowitz
-Massachusetts Institute of Technology
-Moshe Zadka
-Paul Swartz
-Pavel Pergamenshchik
-Ralph Meijer
-Sean Riley
-Software Freedom Conservancy
-Travis B. Hartwell
-Thijs Triemstra
-Thomas Herve
-Timothy Allen
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/qt4reactor/README b/qt4reactor/README
deleted file mode 100644
index e6b5fec..0000000
--- a/qt4reactor/README
+++ /dev/null
@@ -1,55 +0,0 @@
-Unpack this directory into your PYTHONPATH.
-
-test with:
-
-trial --reactor=qt4 twisted (or twisted.test or twisted.test.test_internet)
-
-= Contributors = 
-
-Many thanks to Darren Dale who provided the patch to fix the reactor for Qt4.4
-
-= Using the Qt4Reactor =
-
-In your own code, BEFORE you import the reactor...
-
-app = QApplication(sys.argv)
-import qt4reactor
-qt4reactor.install()
-
-= Gui =
-
-There is a way to run trial using a gui... in bin, there is a routine
-gtrial.  Put that in the same directory as trial and it pops up a
-trivial gui... hit the buton and it all runs the same... don't use the
---reactor option when calling gtrial... but all the other options
-appear to work.  This was just to make sure there wasn't anything
-strange with guis which there doesn't appear to be
-
-If you're writing a conventional Qt application and just want twisted
-as an addon, you can get that by calling reactor.runReturn() instead
-of run().  This call needs to occur after your installation of of the
-reactor and after QApplication.exec_() (or QCoreApplication.exec_()
-whichever you are using.
-
-reactor.run() will also work as expected in a typical twisted application
-
-more docs in qt4reactor.py
-
-Note that if a QApplication or QCoreApplication instance isn't
-constructed prior to calling reactor run, an internally owned
-QCoreApplication is created and destroyed. This won't work if you call
-runReturn instead of run unless you take responsibility for wacking
-QCoreApplication yourself...
-
-However, most users want this reactor to do gui stuff so this
-shouldn't be an issue.
-
-Performance impact of Qt has been reduced by minimizing use of
-signaling which is expensive.  186s for qt4reactor vs 180s for select
-for entire twisted trial suite.
-
--glenn
-
---
-Glenn H. Tarbox, PhD
-glenn@tarbox.org
diff --git a/qt4reactor/__init__.py b/qt4reactor/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/qt4reactor/bin/gtrial b/qt4reactor/bin/gtrial
deleted file mode 100755
index e0d899e..0000000
--- a/qt4reactor/bin/gtrial
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/python
-
-# Twisted, the Framework of Your Internet
-# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-
-
-### Twisted Preamble
-# This makes sure that users don't have to set up their environment
-# specially in order to run these programs from bin/.
-import sys, os, string
-if string.find(os.path.abspath(sys.argv[0]), os.sep+'Twisted') != -1:
-    sys.path.insert(0, os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), os.pardir, os.pardir)))
-if hasattr(os, "getuid") and os.getuid() != 0:
-    sys.path.insert(0, os.curdir)
-### end of preamble
-
-# begin chdir armor
-sys.path[:] = map(os.path.abspath, sys.path)
-# end chdir armor
-
-from gtrial import run
-run()
diff --git a/qt4reactor/ghtTests/buttonStress.py b/qt4reactor/ghtTests/buttonStress.py
deleted file mode 100755
index 7b4ec54..0000000
--- a/qt4reactor/ghtTests/buttonStress.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import sys
-from PySide import QtGui, QtScript
-from PySide.QtCore import QTimer, SIGNAL, QObject
-import qt4reactor
-
-app = QtGui.QApplication(sys.argv)
-qt4reactor.install()
-
-from twisted.internet import reactor, task
-from twisted.python import log
-log.startLogging(sys.stdout)
-
-class doNothing(QObject):
-    def __init__(self):
-        self.count = 0
-        self.looping=False
-        task.LoopingCall(self.printStat).start(1.0)
-        QObject.__init__(self)
-        
-    def doSomething(self):
-        if not self.looping: return
-        self.count += 1
-        reactor.callLater(0.003,self.doSomething)
-        
-    def buttonClick(self):
-        if self.looping: 
-            self.looping=False
-            log.msg('looping stopped....')
-        else: 
-            self.looping=True
-            self.doSomething()
-            log.msg('looping started....')
-        
-    def printStat(self):
-        log.msg(' c: ' + str(self.count) + 
-                ' st: ' + str(reactor._doSomethingCount))
-
-
-t=doNothing()
-
-engine = QtScript.QScriptEngine()
-
-button = QtGui.QPushButton()
-scriptButton = engine.newQObject(button)
-engine.globalObject().setProperty("button", scriptButton)
-
-app.connect(button, SIGNAL("clicked()"), t.buttonClick)
-
-engine.evaluate("button.text = 'Hello World!'")
-engine.evaluate("button.styleSheet = 'font-style: italic'")
-engine.evaluate("button.show()")
-
-reactor.runReturn()
-app.exec_()
-log.msg('fell off the bottom?...')
-
-
diff --git a/qt4reactor/ghtTests/fakeAppButtonStress.py b/qt4reactor/ghtTests/fakeAppButtonStress.py
deleted file mode 100755
index d5a2977..0000000
--- a/qt4reactor/ghtTests/fakeAppButtonStress.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import sys
-from PySide import QtGui, QtScript
-from PySide.QtCore import QTimer, SIGNAL, QEventLoop
-import qt4reactor
-        
-app = QtGui.QApplication(sys.argv)
-
-qt4reactor.install()
-
-from twisted.internet import reactor, task
-
-class doNothing(object):
-    def __init__(self):
-        self.count = 0
-        self.running=False
-        task.LoopingCall(self.printStat).start(1.0)
-
-        
-    def buttonClick(self):
-        if self.running:
-            self.running=False
-            print 'CLICK: calling reactor stop...'
-            reactor.stop()
-            print 'reactor stop called....'
-        else:
-            self.running=True
-            print 'CLICK: entering run'
-            reactor.run()
-            print 'reactor run returned...'
-        
-    def printStat(self):
-        print 'tick...'
-
-t=doNothing()
-
-engine = QtScript.QScriptEngine()
-
-button = QtGui.QPushButton()
-scriptButton = engine.newQObject(button)
-engine.globalObject().setProperty("button", scriptButton)
-
-app.connect(button, SIGNAL("clicked()"), t.buttonClick)
-
-engine.evaluate("button.text = 'Hello World!'")
-engine.evaluate("button.styleSheet = 'font-style: italic'")
-engine.evaluate("button.show()")
-
-app.exec_()
-print 'fell off the bottom?...'
-
-
diff --git a/qt4reactor/ghtTests/ircClient.py b/qt4reactor/ghtTests/ircClient.py
deleted file mode 100644
index 9e6fdfb..0000000
--- a/qt4reactor/ghtTests/ircClient.py
+++ /dev/null
@@ -1,106 +0,0 @@
-from PySide.QtCore import *
-from PySide.QtGui import *
-import sys, qt4reactor
-
-app = QApplication(sys.argv)
-qt4reactor.install()
-
-from twisted.words.protocols import irc
-from twisted.internet import reactor, protocol
-from twisted.python import log
-
-import time, sys
-
-class IRCCore(irc.IRCClient):
-    nickname = 'dosdsdssd'
-    def connectionMade(self):
-        self.nickname = self.factory.window.nickName.text().encode('ascii')
-        self.factory.window.protocol = self
-        irc.IRCClient.connectionMade(self)
-        self.log('connected!!')
-    def connectionLost(self, reason):
-        self.log('disconnected... :( %s'%reason)
-    def signedOn(self):
-        chanName = self.factory.window.channelName.text().encode('ascii')
-        self.join(chanName)
-    def joined(self, channel):
-        self.log('joined %s'%channel)
-    def privmsg(self, user, channel, msg):
-        self.log('%s %s %s'%(user, channel, msg))
-    def action(self, user, channel, msg):
-        self.log('action: %s %s %s'%(user, channel, msg))
-    def log(self, str):
-        self.factory.window.view.addItem(str)
-
-class IRCCoreFactory(protocol.ClientFactory):
-    protocol = IRCCore
-    def __init__(self, window):
-        self.window = window
-    def clientConnectionLost(self, connector, reason):
-        # reconnect to server if lose connection
-        connector.connect()
-    def clientConnectionFailed(self, connector, reason):
-        print('connection failed! :(', reason)
-        reactor.stop()
-
-class MainWindow(QMainWindow):
-    def __init__(self):
-        super(MainWindow, self).__init__()
-        connectLayout = QHBoxLayout()
-        connectLayout.addWidget(QLabel('Server:'))
-        self.serverName = QLineEdit('irc.freenode.org')
-        connectLayout.addWidget(self.serverName)
-        connectLayout.addWidget(QLabel('Channel:'))
-        self.channelName = QLineEdit('#pangaea')
-        connectLayout.addWidget(self.channelName)
-        connectLayout.addWidget(QLabel('Nick:'))
-        self.nickName = QLineEdit('ceruleanwave9832')
-        connectLayout.addWidget(self.nickName)
-        self.connectButton = QPushButton('Connect!')
-        connectLayout.addWidget(self.connectButton)
-        self.connectButton.clicked.connect(self.connectIRC)
-
-        self.view = QListWidget()
-        self.entry = QLineEdit()
-        self.entry.returnPressed.connect(self.sendMessage)
-        irc = QWidget(self)
-        vbox = QVBoxLayout()
-        vbox.addLayout(connectLayout)
-        vbox.addWidget(self.view)
-        vbox.addWidget(self.entry)
-        irc.setLayout(vbox)
-        self.setCentralWidget(irc)
-        self.setWindowTitle('IRC')
-        self.setUnifiedTitleAndToolBarOnMac(True)
-        self.showMaximized()
-
-        self.protocol = None
-    def connectIRC(self):
-        self.connectButton.setDisabled(True)
-        self.channelName.setDisabled(True)
-        self.nickName.setDisabled(True)
-        self.serverName.setDisabled(True)
-        ircCoreFactory = IRCCoreFactory(self)
-        serverName = self.serverName.text().encode('ascii')
-        reactor.connectTCP(serverName, 6667, ircCoreFactory)
-        #reactor.runReturn()
-        #app.exit()
-        #app.exit()
-        reactor.run()
-    def sendMessage(self):
-        if self.protocol:
-            chanName = self.channelName.text().encode('ascii')
-            message = self.entry.text().encode('ascii')
-            self.protocol.msg(chanName, message)
-            self.view.addItem('%s <%s> %s'%(chanName, self.protocol.nickname, message))
-        else:
-            self.view.addItem('Not connected.')
-        self.entry.setText('')
-    def closeEvent(self, event):
-        print('Attempting to close the main window!')
-        reactor.stop()
-        event.accept()
-
-if __name__ == '__main__':
-    mainWin = MainWindow()
-    sys.exit(app.exec_())
diff --git a/qt4reactor/ghtTests/testIterate.py b/qt4reactor/ghtTests/testIterate.py
deleted file mode 100755
index 261155b..0000000
--- a/qt4reactor/ghtTests/testIterate.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import sys
-from PySide import QtGui, QtScript
-from PySide.QtCore import QTimer, SIGNAL
-import qt4reactor
-
-app = QtGui.QApplication(sys.argv)
-qt4reactor.install()
-
-from twisted.internet import reactor, task
-from twisted.python import log
-log.startLogging(sys.stdout)
-
-def testReactor():
-    print 'tick...'
-
-def buttonClick():
-    print 'click...'
-    reactor.iterate(5.0)
-    print 'click return'
-    
-engine = QtScript.QScriptEngine()
-
-button = QtGui.QPushButton()
-scriptButton = engine.newQObject(button)
-engine.globalObject().setProperty("button", scriptButton)
-
-app.connect(button, SIGNAL("clicked()"), buttonClick)
-
-engine.evaluate("button.text = 'Hello World!'")
-engine.evaluate("button.styleSheet = 'font-style: italic'")
-engine.evaluate("button.show()")
-
-task.LoopingCall(testReactor).start(1.0)
-reactor.run()
-log.msg('fell off the bottom?...')
-
-
diff --git a/qt4reactor/ghtTests/trivialscript.py b/qt4reactor/ghtTests/trivialscript.py
deleted file mode 100755
index 2c015cc..0000000
--- a/qt4reactor/ghtTests/trivialscript.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import sys
-
-from twisted.application import reactors
-import qt4reactor
-qt4reactor.install()
-#reactors.installReactor('qt4')
-
-from twisted.internet import reactor, task
-from twisted.python import log
-log.startLogging(sys.stdout)
- 
-def testReactor():
-    print 'tick...'
-
-def doit():
-    task.LoopingCall(testReactor).start(1.0)
-    reactor.callLater(15.0,reactor.stop)
-    
-reactor.callWhenRunning(doit)
-log.msg('calling reactor.run()')
-reactor.run()
-log.msg('fell off the bottom?...')
-
-#sys.exit(app.exec_())
-
diff --git a/qt4reactor/gtrial.py b/qt4reactor/gtrial.py
deleted file mode 100755
index 704a2f4..0000000
--- a/qt4reactor/gtrial.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import sys
-from PyQt4 import QtGui, QtScript
-from PyQt4.QtCore import QTimer, SIGNAL, QEventLoop
-import qt4reactor
-        
-app = QtGui.QApplication(sys.argv)
-
-qt4reactor.install()
-
-from twisted.internet import reactor, task
-
-class doNothing(object):
-    def __init__(self):
-        self.count = 0
-        self.running=False
-        
-    def buttonClick(self):
-        if not self.running:
-            from twisted.scripts import trial
-            trial.run()
-            
-def run():
-
-    t=doNothing()
-    
-    engine = QtScript.QScriptEngine()
-    
-    button = QtGui.QPushButton()
-    scriptButton = engine.newQObject(button)
-    engine.globalObject().setProperty("button", scriptButton)
-    
-    app.connect(button, SIGNAL("clicked()"), t.buttonClick)
-    
-    engine.evaluate("button.text = 'Do Twisted Gui Trial'")
-    engine.evaluate("button.styleSheet = 'font-style: italic'")
-    engine.evaluate("button.show()")
-    
-    app.exec_()
-    print 'fell off the bottom?...'
-
-
diff --git a/qt4reactor/qt4reactor.py b/qt4reactor/qt4reactor.py
deleted file mode 100644
index eff7e2e..0000000
--- a/qt4reactor/qt4reactor.py
+++ /dev/null
@@ -1,359 +0,0 @@
-# Copyright (c) 2001-2011 Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-
-"""
-This module provides support for Twisted to be driven by the Qt mainloop.
-
-In order to use this support, simply do the following::
-    |  app = QApplication(sys.argv) # your code to init Qt
-    |  import qt4reactor
-    |  qt4reactor.install()
-    
-alternatively:
-
-    |  from twisted.application import reactors
-    |  reactors.installReactor('qt4')
-
-Then use twisted.internet APIs as usual.  The other methods here are not
-intended to be called directly.
-
-If you don't instantiate a QApplication or QCoreApplication prior to
-installing the reactor, a QCoreApplication will be constructed
-by the reactor.  QCoreApplication does not require a GUI so trial testing
-can occur normally.
-
-Twisted can be initialized after QApplication.exec_() with a call to
-reactor.runReturn().  calling reactor.stop() will unhook twisted but
-leave your Qt application running
-
-API Stability: stable
-
-Maintainer: U{Glenn H Tarbox, PhD<mailto:glenn@tarbox.org>}
-
-Previous maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
-Original port to QT4: U{Gabe Rudy<mailto:rudy@goldenhelix.com>}
-Subsequent port by therve
-"""
-
-import sys
-import time
-from zope.interface import implements
-from twisted.internet.interfaces import IReactorFDSet
-from twisted.python import log, runtime
-from twisted.internet import posixbase
-from twisted.python.runtime import platformType, platform
-
-try:
-    from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
-    from PyQt4.QtCore import QEventLoop
-except ImportError:
-    from PySide.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
-    from PySide.QtCore import QEventLoop
-
-
-class TwistedSocketNotifier(QObject):
-    """
-    Connection between an fd event and reader/writer callbacks.
-    """
-
-    def __init__(self, parent, reactor, watcher, socketType):
-        QObject.__init__(self, parent)
-        self.reactor = reactor
-        self.watcher = watcher
-        fd = watcher.fileno()
-        self.notifier = QSocketNotifier(fd, socketType, parent)
-        self.notifier.setEnabled(True)
-        if socketType == QSocketNotifier.Read:
-            self.fn = self.read
-        else:
-            self.fn = self.write
-        QObject.connect(self.notifier, SIGNAL("activated(int)"), self.fn)
-
-
-    def shutdown(self):
-        self.notifier.setEnabled(False)
-        self.disconnect(self.notifier, SIGNAL("activated(int)"), self.fn)
-        self.fn = self.watcher = None
-        self.notifier.deleteLater()
-        self.deleteLater()
-
-
-    def read(self, fd):
-        if not self.watcher:
-            return
-        w = self.watcher
-        # doRead can cause self.shutdown to be called so keep a reference to self.watcher
-        def _read():
-            #Don't call me again, until the data has been read
-            self.notifier.setEnabled(False)
-            why = None
-            try:
-                why = w.doRead()
-                inRead = True
-            except:
-                inRead = False
-                log.err()
-                why = sys.exc_info()[1]
-            if why:
-                self.reactor._disconnectSelectable(w, why, inRead)
-            elif self.watcher:
-                self.notifier.setEnabled(True) # Re enable notification following sucessfull read
-            self.reactor._iterate(fromqt=True)
-        log.callWithLogger(w, _read)
-
-    def write(self, sock):
-        if not self.watcher:
-            return
-        w = self.watcher
-        def _write():
-            why = None
-            self.notifier.setEnabled(False)
-            
-            try:
-                why = w.doWrite()
-            except:
-                log.err()
-                why = sys.exc_info()[1]
-            if why:
-                self.reactor._disconnectSelectable(w, why, False)
-            elif self.watcher:
-                self.notifier.setEnabled(True)
-            self.reactor._iterate(fromqt=True)
-        log.callWithLogger(w, _write)
-
-
-
-class QtReactor(posixbase.PosixReactorBase):
-    implements(IReactorFDSet)
-
-    def __init__(self):
-        self._reads = {}
-        self._writes = {}
-        self._notifiers = {}
-        self._timer = QTimer()
-        self._timer.setSingleShot(True)
-        QObject.connect(self._timer, SIGNAL("timeout()"), self.iterate)
-
-        if QCoreApplication.startingUp():
-            # Application Object has not been started yet
-            self.qApp=QCoreApplication([])
-            self._ownApp=True
-        else:
-            self.qApp = QCoreApplication.instance()
-            self._ownApp=False
-        self._blockApp = None
-        posixbase.PosixReactorBase.__init__(self)
-
-
-    def _add(self, xer, primary, type):
-        """
-        Private method for adding a descriptor from the event loop.
-
-        It takes care of adding it if  new or modifying it if already added
-        for another state (read -> read/write for example).
-        """
-        if xer not in primary:
-            primary[xer] = TwistedSocketNotifier(None, self, xer, type)
-
-
-    def addReader(self, reader):
-        """
-        Add a FileDescriptor for notification of data available to read.
-        """
-        self._add(reader, self._reads, QSocketNotifier.Read)
-
-
-    def addWriter(self, writer):
-        """
-        Add a FileDescriptor for notification of data available to write.
-        """
-        self._add(writer, self._writes, QSocketNotifier.Write)
-
-
-    def _remove(self, xer, primary):
-        """
-        Private method for removing a descriptor from the event loop.
-
-        It does the inverse job of _add, and also add a check in case of the fd
-        has gone away.
-        """
-        if xer in primary:
-            notifier = primary.pop(xer)
-            notifier.shutdown()
-
-        
-    def removeReader(self, reader):
-        """
-        Remove a Selectable for notification of data available to read.
-        """
-        self._remove(reader, self._reads)
-
-
-    def removeWriter(self, writer):
-        """
-        Remove a Selectable for notification of data available to write.
-        """
-        self._remove(writer, self._writes)
-
-
-    def removeAll(self):
-        """
-        Remove all selectables, and return a list of them.
-        """
-        rv = self._removeAll(self._reads, self._writes)
-        return rv
-
-
-    def getReaders(self):
-        return self._reads.keys()
-
-
-    def getWriters(self):
-        return self._writes.keys()
-
-
-    def callLater(self,howlong, *args, **kargs):
-        rval = super(QtReactor,self).callLater(howlong, *args, **kargs)
-        self.reactorInvocation()
-        return rval
-
-
-    def reactorInvocation(self):
-        self._timer.stop()
-        self._timer.setInterval(0)
-        self._timer.start()
-        
-
-    def _iterate(self, delay=None, fromqt=False):
-        """See twisted.internet.interfaces.IReactorCore.iterate.
-        """
-        self.runUntilCurrent()
-        self.doIteration(delay, fromqt)
-
-    iterate = _iterate
-
-    def doIteration(self, delay=None, fromqt=False):
-        'This method is called by a Qt timer or by network activity on a file descriptor'
-        
-        if not self.running and self._blockApp:
-            self._blockApp.quit()
-        self._timer.stop()
-        delay = max(delay, 1)
-        if not fromqt:
-            self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000)
-        if self.timeout() is None:
-            timeout = 0.1
-        elif self.timeout() == 0:
-            timeout = 0
-        else:
-            timeout = self.timeout()
-        self._timer.setInterval(timeout * 1000)
-        self._timer.start()
-
-
-    def runReturn(self, installSignalHandlers=True):
-        self.startRunning(installSignalHandlers=installSignalHandlers)
-        self.reactorInvocation()
-
-
-    def run(self, installSignalHandlers=True):
-        if self._ownApp:
-            self._blockApp = self.qApp
-        else:
-            self._blockApp = QEventLoop()
-        self.runReturn()
-        self._blockApp.exec_()
-
-
-class QtEventReactor(QtReactor):
-    def __init__(self, *args, **kwargs):
-        self._events = {}
-        super(QtEventReactor, self).__init__()
-
-        
-    def addEvent(self, event, fd, action):
-        """
-        Add a new win32 event to the event loop.
-        """
-        self._events[event] = (fd, action)
-
-
-    def removeEvent(self, event):
-        """
-        Remove an event.
-        """
-        if event in self._events:
-            del self._events[event]
-
-
-    def doEvents(self):
-        handles = self._events.keys()
-        if len(handles) > 0:
-            val = None
-            while val != WAIT_TIMEOUT:
-                val = MsgWaitForMultipleObjects(handles, 0, 0, QS_ALLINPUT | QS_ALLEVENTS)
-                if val >= WAIT_OBJECT_0 and val < WAIT_OBJECT_0 + len(handles):
-                    event_id = handles[val - WAIT_OBJECT_0]
-                    if event_id in self._events:
-                        fd, action = self._events[event_id]
-                        log.callWithLogger(fd, self._runAction, action, fd)
-                elif val == WAIT_TIMEOUT:
-                    pass
-                else:
-                    #print 'Got an unexpected return of %r' % val
-                    return
-
-
-    def _runAction(self, action, fd):
-        try:
-            closed = getattr(fd, action)()
-        except:
-            closed = sys.exc_info()[1]
-            log.deferr()
-
-        if closed:
-            self._disconnectSelectable(fd, closed, action == 'doRead')
-
-            
-    def timeout(self):
-        t = super(QtEventReactor, self).timeout()
-        return min(t, 0.01)
-
-
-    def iterate(self, delay=None):
-        """See twisted.internet.interfaces.IReactorCore.iterate.
-        """
-        self.runUntilCurrent()
-        self.doEvents()
-        self.doIteration(delay)
-
-
-def posixinstall():
-    """
-    Install the Qt reactor.
-    """
-    p = QtReactor()
-    from twisted.internet.main import installReactor
-    installReactor(p)
-
-
-def win32install():
-    """
-    Install the Qt reactor.
-    """
-    p = QtEventReactor()
-    from twisted.internet.main import installReactor
-    installReactor(p)
-
-
-if runtime.platform.getType() == 'win32':
-    from win32event import CreateEvent, MsgWaitForMultipleObjects
-    from win32event import WAIT_OBJECT_0, WAIT_TIMEOUT, QS_ALLINPUT, QS_ALLEVENTS
-    install = win32install
-else:
-    install = posixinstall
-
-
-__all__ = ["install"]
-
diff --git a/qt4reactor/twisted/plugins/qt4.py b/qt4reactor/twisted/plugins/qt4.py
deleted file mode 100644
index 0f380b5..0000000
--- a/qt4reactor/twisted/plugins/qt4.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
-# see LICENSE for details
-
-
-from twisted.application.reactors import Reactor
-
-qt4 = Reactor('qt4', 'qt4reactor', 'Qt4 integration reactor')
-qt4bad = Reactor('qt4bad', 'qt4reactor_bad', 'Qt4 broken reactor')
-