diff --git a/TODO b/TODO
index c68d534..9ea530e 100644
--- a/TODO
+++ b/TODO
@@ -1,8 +1,4 @@
 Features:
-* More complex quirks: spelling, by-sound
-* IRC message limit!!!
-* on ban, redirect to memo chooser
-* ? time option???
 * help menu -- about and forum
 * Tray doesn't disappear on windows after close
 -- release alpha
@@ -12,7 +8,9 @@ Features:
 * spell check?
 * help button on quirks menu?
 -- release beta
-* flashing??
+* hide offline chums
+* chum list groups
+* More complex quirks: by-sound
 * change profile only once we have confirmation from server
 * log viewer
 * pick your own icon
@@ -22,5 +20,3 @@ Features:
 * Spy mode
 * Animated
 * put code into separate files
-* hide offline chums
-* chum list groups
\ No newline at end of file
diff --git a/convo.py b/convo.py
index 7a7431f..9924f47 100644
--- a/convo.py
+++ b/convo.py
@@ -117,13 +117,16 @@ class PesterTabWindow(QtGui.QFrame):
             return True
         else:
             return False
-    
+    def activateChat(self):
+        if platform.system() == "Windows":
+            self.activateWindow()
     def notifyNewMessage(self, handle):
         i = self.tabIndices[handle]
         self.tabs.setTabTextColor(i, QtGui.QColor(self.mainwindow.theme["%s/tabs/newmsgcolor" % (self.type)]))
         convo = self.convos[handle]
         def func():
             convo.showChat()
+        self.activateChat()
         self.mainwindow.waitingMessages.addMessage(handle, func)
         # set system tray
     def clearNewMessage(self, handle):
@@ -480,6 +483,11 @@ class PesterConvo(QtGui.QFrame):
         if self.parent():
             self.parent().showChat(self.title())
         self.raiseChat()
+    def activateChat(self):
+        if self.parent():
+            self.parent().activateChat()
+        if platform.system() == "Windows":
+            self.activateWindow()
     def contextMenuEvent(self, event):
         if event.reason() == QtGui.QContextMenuEvent.Mouse:
             self.optionsMenu.popup(event.globalPos())
diff --git a/convo.pyc b/convo.pyc
index 9651083..1578fea 100644
Binary files a/convo.pyc and b/convo.pyc differ
diff --git a/dataobjs.py b/dataobjs.py
index e0da239..fb0b62f 100644
--- a/dataobjs.py
+++ b/dataobjs.py
@@ -5,6 +5,9 @@ import random
 
 from generic import PesterIcon
 from parsetools import timeDifference, convertTags
+from mispeller import mispeller
+
+_upperre = re.compile(r"upper\(([\w\\]+)\)")
 
 class Mood(object):
     moods = ["chummy", "rancorous", "offline", "pleasant", "distraught", 
@@ -32,6 +35,7 @@ class Mood(object):
             return PesterIcon(theme["main/chums/moods/chummy/icon"])
         return PesterIcon(f)
 
+
 class pesterQuirk(object):
     def __init__(self, quirk):
         if type(quirk) != dict:
@@ -51,7 +55,13 @@ class pesterQuirk(object):
                 return string
             if not last and len(fr) > 0 and fr[len(fr)-1] == "$":
                 return string
-            return re.sub(fr, self.quirk["to"], string)
+            def regexprep(mo):
+                to = self.quirk["to"]
+                def upperrep(m):
+                    return mo.expand(m.group(1)).upper()
+                to = _upperre.sub(upperrep, to)
+                return mo.expand(to)
+            return re.sub(fr, regexprep, string)
         elif self.type == "random":
             fr = self.quirk["from"]
             if not first and len(fr) > 0 and fr[0] == "^":
@@ -62,6 +72,17 @@ class pesterQuirk(object):
                 choice = random.choice(self.quirk["randomlist"])
                 return mo.expand(choice)
             return re.sub(self.quirk["from"], randomrep, string)
+        elif self.type == "spelling":
+            percentage = self.quirk["percentage"]/100.0
+            words = string.split(" ")
+            newl = []
+            for w in words:
+                p = random.random()
+                if p < percentage:
+                    newl.append(mispeller(w))
+                else:
+                    newl.append(w)
+            return " ".join(newl)
 
     def __str__(self):
         if self.type == "prefix":
@@ -74,6 +95,8 @@ class pesterQuirk(object):
             return "REGEXP: %s REPLACED WITH %s" % (self.quirk["from"], self.quirk["to"])
         elif self.type == "random":
             return "REGEXP: %s RANDOMLY REPLACED WITH %s" % (self.quirk["from"], [str(r) for r in self.quirk["randomlist"]])
+        elif self.type == "spelling":
+            return "MISPELLER: %d%%" % (self.quirk["percentage"])
 
 class pesterQuirks(object):
     def __init__(self, quirklist):
@@ -91,6 +114,7 @@ class pesterQuirks(object):
         replace = [q for q in self.quirklist if
                    q.type=='replace' or q.type=='regexp']
         random = [q for q in self.quirklist if q.type=='random']
+        spelling = [q for q in self.quirklist if q.type=='spelling']
         
         newlist = []
         for (i, o) in enumerate(lexed):
@@ -104,6 +128,8 @@ class pesterQuirks(object):
                 continue
             lastStr = (i == len(lexed)-1)
             string = o
+            for s in spelling:
+                string = s.apply(string)
             for r in random:
                 string = r.apply(string, first=(i==0), last=lastStr)
             for r in replace:
diff --git a/dataobjs.pyc b/dataobjs.pyc
index 670a7cc..c548bdb 100644
Binary files a/dataobjs.pyc and b/dataobjs.pyc differ
diff --git a/generic.py b/generic.py
index ad9a8a8..fd77ad6 100644
--- a/generic.py
+++ b/generic.py
@@ -1,4 +1,13 @@
 from PyQt4 import QtGui, QtCore
+from datetime import timedelta
+
+class mysteryTime(timedelta):
+    def __sub__(self, other):
+        return self
+    def __eq__(self, other):
+        return (type(other) is mysteryTime)
+    def __neq__(self, other):
+        return (type(other) is not mysteryTime)
 
 class PesterList(list):
     def __init__(self, l):
diff --git a/generic.pyc b/generic.pyc
index 3b026d6..2fe98c5 100644
Binary files a/generic.pyc and b/generic.pyc differ
diff --git a/irc.py b/irc.py
index 93f7a49..7c49931 100644
--- a/irc.py
+++ b/irc.py
@@ -32,8 +32,24 @@ class PesterIRC(QtCore.QObject):
     @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
     def sendMessage(self, text, handle):
         h = unicode(handle)
+        textl = [unicode(text)]
+        def splittext(l):
+            if len(l[0]) > 400:
+                space = l[0].rfind(" ", 0,400)
+                if space == -1:
+                    space = 400
+                a = l[0][0:space]
+                b = l[0][space:]
+                if len(b) > 0:
+                    return [a] + splittext([b])
+                else:
+                    return [a]
+            else:
+                return l
+        textl = splittext(textl)
         try:
-            helpers.msg(self.cli, h, text)
+            for t in textl:
+                helpers.msg(self.cli, h, t)
         except socket.error:
             self.setConnectionBroken()
     @QtCore.pyqtSlot(QtCore.QString, bool)
diff --git a/irc.pyc b/irc.pyc
index e4a65a5..4fd91eb 100644
Binary files a/irc.pyc and b/irc.pyc differ
diff --git a/logs/chums.js b/logs/chums.js
index e55192a..74ba284 100644
--- a/logs/chums.js
+++ b/logs/chums.js
@@ -1 +1 @@
-{"macruralAlchemist": {"color": "#700000", "handle": "macruralAlchemist", "mood": "offline"}, "lyricalKeraunoscopic": {"color": "#00c000", "handle": "lyricalKeraunoscopic", "mood": "offline"}, "fireSwallow": {"color": "#80bb9a", "handle": "fireSwallow", "mood": "offline"}, "aquaMarinist": {"color": "#00caca", "handle": "aquaMarinist", "mood": "offline"}, "centaursTesticle": {"color": "#000056", "handle": "centaursTesticle", "mood": "offline"}, "mechanicalSpectacle": {"color": "#0000ff", "handle": "mechanicalSpectacle", "mood": "offline"}, "carcinoGeneticist": {"color": "#999999", "handle": "carcinoGeneticist", "mood": "offline"}, "greyscalePacifist": {"color": "#7f7f7f", "handle": "greyscalePacifist", "mood": "offline"}, "aquaticMarinist": {"color": "#00caca", "handle": "aquaticMarinist", "mood": "offline"}, "iw": {"color": "#ff0000", "handle": "iw", "mood": "offline"}, "insipidTranscient": {"color": "#145064", "handle": "insipidTranscient", "mood": "offline"}, "caffeinatedAnalyst": {"color": "#aa0000", "handle": "caffeinatedAnalyst", "mood": "offline"}, "pesterClient394": {"color": "#ff3737", "handle": "pesterClient394", "mood": "offline"}, "absoluteTranquility": {"color": "#000033", "handle": "absoluteTranquility", "mood": "offline"}, "nitroZealist": {"color": "#ff3737", "handle": "nitroZealist", "mood": "offline"}, "quintessentialArbalest": {"color": "#999999", "handle": "quintessentialArbalest", "mood": "offline"}, "noSense": {"color": "#1d0059", "handle": "noSense", "mood": "offline"}, "agogPorphyry": {"color": "#522d80", "handle": "agogPorphyry", "mood": "offline"}, "grimAuxiliatrix": {"color": "#008141", "handle": "grimAuxiliatrix", "mood": "offline"}, "terminallyCapricious": {"color": "#2b0057", "handle": "terminallyCapricious", "mood": "offline"}, "androidTechnician": {"color": "#0000ff", "handle": "androidTechnician", "mood": "offline"}, "ardentAbettor": {"color": "#c8b670", "handle": "ardentAbettor", "mood": "offline"}, "aceIsm": {"color": "#ffcc66", "handle": "aceIsm", "mood": "offline"}, "apocalypseArisen": {"color": "#a10000", "handle": "apocalypseArisen", "mood": "offline"}, "radicalApologist": {"color": "#ffaa00", "handle": "radicalApologist", "mood": "offline"}, "microMachines": {"color": "#aa00ff", "handle": "microMachines", "mood": "offline"}, "uroborosUnbound": {"color": "#00416a", "handle": "uroborosUnbound", "mood": "offline"}, "arachnidsGrip": {"color": "#005682", "handle": "arachnidsGrip", "mood": "offline"}, "caligulasAquarium": {"color": "#6a006a", "handle": "caligulasAquarium", "mood": "offline"}, "percipientPedestrian": {"color": "#00ffff", "handle": "percipientPedestrian", "mood": "offline"}, "tentacleTherapist": {"color": "#cc66ff", "handle": "tentacleTherapist", "mood": "offline"}, "sucroseSaboteur": {"color": "#00e700", "handle": "sucroseSaboteur", "mood": "offline"}, "illuminatedWax": {"color": "#000000", "handle": "illuminatedWax", "mood": "offline"}, "moirailBunp": {"color": "#6a3d0f", "handle": "moirailBunp", "mood": "offline"}, "madLurker": {"color": "#000000", "handle": "madLurker", "mood": "offline"}, "testOut": {"color": "#c760cc", "handle": "testOut", "mood": "offline"}, "pesterClient3022": {"color": "#336600", "handle": "pesterClient3022", "mood": "offline"}, "hellerificJefferson": {"color": "#3300ff", "handle": "hellerificJefferson", "mood": "offline"}, "DocScratch": {"color": "#ffffff", "handle": "DocScratch", "mood": "offline"}, "recalcitrantDisaster": {"color": "#8b0068", "handle": "recalcitrantDisaster", "mood": "offline"}, "superGhost": {"color": "#800564", "handle": "superGhost", "mood": "offline"}, "arsenicCatnip": {"color": "#006400", "handle": "arsenicCatnip", "mood": "offline"}, "kaleidoscopicMind": {"color": "#ff6666", "handle": "kaleidoscopicMind", "mood": "offline"}, "zealousScarecrow": {"color": "#00c882", "handle": "zealousScarecrow", "mood": "offline"}, "captainCaveman": {"color": "#7c414e", "handle": "captainCaveman", "mood": "offline"}, "gamblingGenocider": {"color": "#00ff00", "handle": "gamblingGenocider", "mood": "offline"}, "cuttlefishCuller": {"color": "#77003c", "handle": "cuttlefishCuller", "mood": "offline"}, "wovenWay": {"color": "#000000", "handle": "wovenWay", "mood": "offline"}, "masterG": {"color": "#77003c", "handle": "masterG", "mood": "offline"}, "elegantDiversion": {"color": "#14b40a", "handle": "elegantDiversion", "mood": "offline"}, "plasmaModerator": {"color": "#5685cc", "handle": "plasmaModerator", "mood": "offline"}, "carcinoGenetecist": {"color": "#7f7f7f", "handle": "carcinoGenetecist", "mood": "offline"}, "marineAquist": {"color": "#00caca", "handle": "marineAquist", "mood": "offline"}, "remoteBloodbath": {"color": "#c70000", "handle": "remoteBloodbath", "mood": "offline"}, "oilslickOrchid": {"color": "#743f7e", "handle": "oilslickOrchid", "mood": "offline"}, "acapellaWaterfall": {"color": "#000099", "handle": "acapellaWaterfall", "mood": "offline"}, "gallowsCalibrator": {"color": "#008282", "handle": "gallowsCalibrator", "mood": "offline"}, "rageInducer": {"color": "#00ffff", "handle": "rageInducer", "mood": "offline"}, "anguillaNuntia": {"color": "#ff007f", "handle": "anguillaNuntia", "mood": "offline"}, "greenZephyr": {"color": "#00ca40", "handle": "greenZephyr", "mood": "offline"}, "lawdEngrish": {"color": "#00ff00", "handle": "lawdEngrish", "mood": "offline"}, "pretentiousFantasia": {"color": "#ff66cf", "handle": "pretentiousFantasia", "mood": "offline"}, "counterRealist": {"color": "#10d985", "handle": "counterRealist", "mood": "offline"}, "maxiumumFatness": {"color": "#3366ff", "handle": "maxiumumFatness", "mood": "offline"}, "schlagzeugGator": {"color": "#61821f", "handle": "schlagzeugGator", "mood": "offline"}, "metaliAggressive": {"color": "#9289d5", "handle": "metaliAggressive", "mood": "offline"}, "midnightSparrow": {"color": "#ff55ff", "handle": "midnightSparrow", "mood": "offline"}, "magmaExploiter": {"color": "#d90000", "handle": "magmaExploiter", "mood": "offline"}, "gardenGnostic": {"color": "#00ff00", "handle": "gardenGnostic", "mood": "offline"}, "unknownTraveler": {"color": "#006666", "handle": "unknownTraveler", "mood": "offline"}, "utilitarianTurnabout": {"color": "#dd0000", "handle": "utilitarianTurnabout", "mood": "offline"}, "adiosToreador": {"color": "#aa5500", "handle": "adiosToreador", "mood": "offline"}}
\ No newline at end of file
+{"macruralAlchemist": {"color": "#700000", "handle": "macruralAlchemist", "mood": "offline"}, "lyricalKeraunoscopic": {"color": "#00aa00", "handle": "lyricalKeraunoscopic", "mood": "offline"}, "fireSwallow": {"color": "#80bb9a", "handle": "fireSwallow", "mood": "offline"}, "aquaMarinist": {"color": "#00caca", "handle": "aquaMarinist", "mood": "offline"}, "centaursTesticle": {"color": "#000056", "handle": "centaursTesticle", "mood": "offline"}, "mechanicalSpectacle": {"color": "#0000ff", "handle": "mechanicalSpectacle", "mood": "offline"}, "depthPerception": {"color": "#6f3886", "handle": "depthPerception", "mood": "offline"}, "carcinoGeneticist": {"color": "#999999", "handle": "carcinoGeneticist", "mood": "offline"}, "greyscalePacifist": {"color": "#7f7f7f", "handle": "greyscalePacifist", "mood": "offline"}, "aquaticMarinist": {"color": "#00caca", "handle": "aquaticMarinist", "mood": "offline"}, "iw": {"color": "#000000", "handle": "iw", "mood": "offline"}, "insipidTranscient": {"color": "#145064", "handle": "insipidTranscient", "mood": "offline"}, "caffeinatedAnalyst": {"color": "#aa0000", "handle": "caffeinatedAnalyst", "mood": "offline"}, "midnightSparrow": {"color": "#ff55ff", "handle": "midnightSparrow", "mood": "offline"}, "absoluteTranquility": {"color": "#000033", "handle": "absoluteTranquility", "mood": "offline"}, "nitroZealist": {"color": "#ff3737", "handle": "nitroZealist", "mood": "offline"}, "quintessentialArbalest": {"color": "#999999", "handle": "quintessentialArbalest", "mood": "offline"}, "noSense": {"color": "#1d0059", "handle": "noSense", "mood": "offline"}, "agogPorphyry": {"color": "#522d80", "handle": "agogPorphyry", "mood": "offline"}, "grimAuxiliatrix": {"color": "#008141", "handle": "grimAuxiliatrix", "mood": "offline"}, "terminallyCapricious": {"color": "#2b0057", "handle": "terminallyCapricious", "mood": "offline"}, "androidTechnician": {"color": "#0000ff", "handle": "androidTechnician", "mood": "offline"}, "ardentAbettor": {"color": "#c8b670", "handle": "ardentAbettor", "mood": "offline"}, "aceIsm": {"color": "#ffcc66", "handle": "aceIsm", "mood": "offline"}, "apocalypseArisen": {"color": "#a10000", "handle": "apocalypseArisen", "mood": "offline"}, "radicalApologist": {"color": "#ffaa00", "handle": "radicalApologist", "mood": "offline"}, "microMachines": {"color": "#aa00ff", "handle": "microMachines", "mood": "offline"}, "uroborosUnbound": {"color": "#00416a", "handle": "uroborosUnbound", "mood": "offline"}, "arachnidsGrip": {"color": "#005682", "handle": "arachnidsGrip", "mood": "offline"}, "caligulasAquarium": {"color": "#6a006a", "handle": "caligulasAquarium", "mood": "offline"}, "percipientPedestrian": {"color": "#00ffff", "handle": "percipientPedestrian", "mood": "offline"}, "tentacleTherapist": {"color": "#cc66ff", "handle": "tentacleTherapist", "mood": "offline"}, "sucroseSaboteur": {"color": "#00e700", "handle": "sucroseSaboteur", "mood": "offline"}, "illuminatedWax": {"color": "#000000", "handle": "illuminatedWax", "mood": "offline"}, "moirailBunp": {"color": "#6a3d0f", "handle": "moirailBunp", "mood": "offline"}, "madLurker": {"color": "#000000", "handle": "madLurker", "mood": "offline"}, "testOut": {"color": "#c760cc", "handle": "testOut", "mood": "offline"}, "pesterClient3022": {"color": "#336600", "handle": "pesterClient3022", "mood": "offline"}, "hellerificJefferson": {"color": "#3300ff", "handle": "hellerificJefferson", "mood": "offline"}, "DocScratch": {"color": "#ffffff", "handle": "DocScratch", "mood": "offline"}, "recalcitrantDisaster": {"color": "#8b0068", "handle": "recalcitrantDisaster", "mood": "offline"}, "superGhost": {"color": "#800564", "handle": "superGhost", "mood": "offline"}, "arsenicCatnip": {"color": "#006400", "handle": "arsenicCatnip", "mood": "offline"}, "kaleidoscopicMind": {"color": "#ff6666", "handle": "kaleidoscopicMind", "mood": "offline"}, "zealousScarecrow": {"color": "#00c882", "handle": "zealousScarecrow", "mood": "offline"}, "captainCaveman": {"color": "#7c414e", "handle": "captainCaveman", "mood": "offline"}, "gamblingGenocider": {"color": "#00ff00", "handle": "gamblingGenocider", "mood": "offline"}, "cuttlefishCuller": {"color": "#77003c", "handle": "cuttlefishCuller", "mood": "offline"}, "wovenWay": {"color": "#000000", "handle": "wovenWay", "mood": "offline"}, "masterG": {"color": "#77003c", "handle": "masterG", "mood": "offline"}, "elegantDiversion": {"color": "#14b40a", "handle": "elegantDiversion", "mood": "offline"}, "plasmaModerator": {"color": "#5685cc", "handle": "plasmaModerator", "mood": "offline"}, "carcinoGenetecist": {"color": "#7f7f7f", "handle": "carcinoGenetecist", "mood": "offline"}, "marineAquist": {"color": "#00caca", "handle": "marineAquist", "mood": "offline"}, "remoteBloodbath": {"color": "#c70000", "handle": "remoteBloodbath", "mood": "offline"}, "oilslickOrchid": {"color": "#743f7e", "handle": "oilslickOrchid", "mood": "offline"}, "acapellaWaterfall": {"color": "#000099", "handle": "acapellaWaterfall", "mood": "offline"}, "gallowsCalibrator": {"color": "#008282", "handle": "gallowsCalibrator", "mood": "offline"}, "rageInducer": {"color": "#00ffff", "handle": "rageInducer", "mood": "offline"}, "anguillaNuntia": {"color": "#ff007f", "handle": "anguillaNuntia", "mood": "offline"}, "doctectiveMiracles": {"color": "#999900", "handle": "doctectiveMiracles", "mood": "offline"}, "obliviousCrafter": {"color": "#fd36b1", "handle": "obliviousCrafter", "mood": "offline"}, "greenZephyr": {"color": "#00ca40", "handle": "greenZephyr", "mood": "offline"}, "lawdEngrish": {"color": "#00ff00", "handle": "lawdEngrish", "mood": "offline"}, "pretentiousFantasia": {"color": "#ff66cf", "handle": "pretentiousFantasia", "mood": "offline"}, "counterRealist": {"color": "#10d985", "handle": "counterRealist", "mood": "offline"}, "maxiumumFatness": {"color": "#3366ff", "handle": "maxiumumFatness", "mood": "offline"}, "schlagzeugGator": {"color": "#61821f", "handle": "schlagzeugGator", "mood": "offline"}, "metaliAggressive": {"color": "#9289d5", "handle": "metaliAggressive", "mood": "offline"}, "pesterClient394": {"color": "#ff3737", "handle": "pesterClient394", "mood": "offline"}, "magmaExploiter": {"color": "#d90000", "handle": "magmaExploiter", "mood": "offline"}, "gardenGnostic": {"color": "#00ff00", "handle": "gardenGnostic", "mood": "offline"}, "unknownTraveler": {"color": "#006666", "handle": "unknownTraveler", "mood": "offline"}, "utilitarianTurnabout": {"color": "#dd0000", "handle": "utilitarianTurnabout", "mood": "offline"}, "adiosToreador": {"color": "#aa5500", "handle": "adiosToreador", "mood": "offline"}}
\ No newline at end of file
diff --git a/memos.py b/memos.py
index ec8bb6b..6562550 100644
--- a/memos.py
+++ b/memos.py
@@ -5,12 +5,15 @@ from PyQt4 import QtGui, QtCore
 from datetime import time, timedelta, datetime
 
 from dataobjs import PesterProfile, Mood, PesterHistory
-from generic import PesterIcon, RightClickList
+from generic import PesterIcon, RightClickList, mysteryTime
 from convo import PesterConvo, PesterInput, PesterText, PesterTabWindow
 from parsetools import convertTags, addTimeInitial, timeProtocol, \
     lexMessage, colorBegin, colorEnd, mecmd
 
+
 def delta2txt(d, format="pc"):
+    if type(d) is mysteryTime:
+        return "?"
     if format == "pc":
         sign = "+" if d >= timedelta(0) else "-"
     else:
@@ -34,6 +37,8 @@ def delta2txt(d, format="pc"):
 
 def txt2delta(txt):
     sign = 1
+    if txt[0] == '?':
+        return mysteryTime()
     if txt[0] == '+':
         txt = txt[1:]
     elif txt[0] == '-':
@@ -51,7 +56,11 @@ def txt2delta(txt):
     return sign*timed
 
 def pcfGrammar(td):
-    if td > timedelta(0):
+    if type(td) is mysteryTime:
+        when = "???"
+        temporal = "???"
+        pcf = "?"
+    elif td > timedelta(0):
         when = "FROM NOW"
         temporal = "FUTURE"
         pcf = "F"
@@ -109,14 +118,14 @@ class TimeTracker(list):
         self.current = self.index(timed)
     def addRecord(self, timed):
         (temporal, pcf, when) = pcfGrammar(timed - timedelta(0))
-        if pcf == "C":
+        if pcf == "C" or pcf == "?":
             return
         if timed in self.timerecord[pcf]:
             return
         self.timerecord[pcf].append(timed)
     def getRecord(self, timed):
         (temporal, pcf, when) = pcfGrammar(timed - timedelta(0))
-        if pcf == "C":
+        if pcf == "C" or pcf == "?":
             return 0
         if len(self.timerecord[pcf]) > 1:
             return self.timerecord[pcf].index(timed)+1
@@ -170,6 +179,10 @@ class TimeInput(QtGui.QLineEdit):
     def setSlider(self):
         value = unicode(self.text())
         timed = txt2delta(value)
+        if type(timed) is mysteryTime:
+            self.timeslider.setValue(0)
+            self.setText("?")
+            return
         sign = 1 if timed >= timedelta(0) else -1
         abstimed = abs(txt2delta(value))
         index = 50
@@ -254,7 +267,7 @@ class MemoText(PesterText):
         if chum is not me:
             if parent.times.has_key(chum.handle):
                 time = parent.times[chum.handle]
-                if not time.getTime():
+                if time.getTime() is None:
                     # MY WAY OR THE HIGHWAY
                     time.addTime(timedelta(0))
             else:
diff --git a/memos.pyc b/memos.pyc
index 3061b67..e3f1eb4 100644
Binary files a/memos.pyc and b/memos.pyc differ
diff --git a/menus.py b/menus.py
index f7ff4e6..7e221a8 100644
--- a/menus.py
+++ b/menus.py
@@ -38,6 +38,45 @@ class PesterQuirkList(QtGui.QListWidget):
         if i >= 0:
             self.takeItem(i)
 
+class MispellQuirkDialog(QtGui.QDialog):
+    def __init__(self, parent):
+        QtGui.QDialog.__init__(self, parent)
+        self.setWindowTitle("MISPELLER")
+        layout_1 = QtGui.QHBoxLayout()
+        zero = QtGui.QLabel("1%", self)
+        hund = QtGui.QLabel("100%", self)
+        self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
+        self.slider.setMinimum(1)
+        self.slider.setMaximum(100)
+        self.slider.setValue(50)
+        layout_1.addWidget(zero)
+        layout_1.addWidget(self.slider)
+        layout_1.addWidget(hund)
+
+        self.ok = QtGui.QPushButton("OK", self)
+        self.ok.setDefault(True)
+        self.connect(self.ok, QtCore.SIGNAL('clicked()'),
+                     self, QtCore.SLOT('accept()'))
+        self.cancel = QtGui.QPushButton("CANCEL", self)
+        self.connect(self.cancel, QtCore.SIGNAL('clicked()'),
+                     self, QtCore.SLOT('reject()'))
+        layout_ok = QtGui.QHBoxLayout()
+        layout_ok.addWidget(self.cancel)
+        layout_ok.addWidget(self.ok)
+
+        layout_0 = QtGui.QVBoxLayout()
+        layout_0.addLayout(layout_1)
+        layout_0.addLayout(layout_ok)
+
+        self.setLayout(layout_0)
+    def getPercentage(self):
+        r = self.exec_()
+        if r == QtGui.QDialog.Accepted:
+            retval = {"percentage": self.slider.value()}
+            return retval
+        else:
+            return None
+
 class RandomQuirkDialog(MultiTextDialog):
     def __init__(self, parent):
         QtGui.QDialog.__init__(self, parent)
@@ -138,6 +177,10 @@ class PesterChooseQuirks(QtGui.QDialog):
         self.connect(self.addRandomReplaceButton, QtCore.SIGNAL('clicked()'),
                      self, QtCore.SLOT('addRandomDialog()'))
 
+        self.addMispellingButton = QtGui.QPushButton("MISPELLER", self)
+        self.connect(self.addMispellingButton, QtCore.SIGNAL('clicked()'),
+                     self, QtCore.SLOT('addSpellDialog()'))
+
         layout_1 = QtGui.QHBoxLayout()
         layout_1.addWidget(self.addPrefixButton)
         layout_1.addWidget(self.addSuffixButton)
@@ -145,6 +188,7 @@ class PesterChooseQuirks(QtGui.QDialog):
         layout_2 = QtGui.QHBoxLayout()
         layout_2.addWidget(self.addRegexpReplaceButton)
         layout_2.addWidget(self.addRandomReplaceButton)
+        layout_2.addWidget(self.addMispellingButton)
 
         self.removeSelectedButton = QtGui.QPushButton("REMOVE", self)
         self.connect(self.removeSelectedButton, QtCore.SIGNAL('clicked()'),
@@ -240,6 +284,16 @@ class PesterChooseQuirks(QtGui.QDialog):
         item = PesterQuirkItem(quirk, self.quirkList)
         self.quirkList.addItem(item)
         #self.quirkList.sortItems()
+    @QtCore.pyqtSlot()
+    def addSpellDialog(self):
+        vdict = MispellQuirkDialog(self).getPercentage()
+        if vdict is None:
+            return
+        vdict["type"] = "spelling"
+        quirk = pesterQuirk(vdict)
+        item = PesterQuirkItem(quirk, self.quirkList)
+        self.quirkList.addItem(item)
+        #self.quirkList.sortItems()
 
 class PesterChooseTheme(QtGui.QDialog):
     def __init__(self, config, theme, parent):
diff --git a/menus.pyc b/menus.pyc
index 271db3a..78b4664 100644
Binary files a/menus.pyc and b/menus.pyc differ
diff --git a/mispeller.py b/mispeller.py
new file mode 100644
index 0000000..b8240e3
--- /dev/null
+++ b/mispeller.py
@@ -0,0 +1,60 @@
+import random
+
+kbloc = [[x for x in "1234567890-="],
+         [x for x in "qwertyuiop[]"],
+         [x for x in "asdfghjkl:;'"],
+         [x for x in "zxcvbnm,.>/?"]]
+kbdict = {}
+for (i, l) in enumerate(kbloc):
+    for (j, k) in enumerate(l):
+        kbdict[k] = (i, j)
+
+sounddict = {"a": "e", "b": "d", "c": "k", "d": "g", "e": "eh",
+             "f": "ph", "g": "j", "h": "h", "i": "ai", "j": "ge",
+             "k": "c", "l": "ll", "m": "n", "n": "m", "o": "oa",
+             "p": "b", "q": "kw", "r": "ar", "s": "ss", "t": "d",
+             "u": "you", "v": "w", "w": "wn", "x": "cks", "y": "uy", "z": "s"}
+             
+
+def mispeller(word):
+    if len(word) <= 6:
+        num = 1
+    else:
+        num = random.choice([1,2])
+    wordseq = range(0, len(word))
+    random.shuffle(wordseq)
+    letters = wordseq[0:num]
+    def mistype(string, i):
+        l = string[i]
+        if not kbdict.has_key(l):
+            return string
+        lpos = kbdict[l]
+        newpos = lpos
+        while newpos == lpos:
+            newpos = ((lpos[0] + random.choice([-1, 0, 1])) % len(kbloc),
+                      (lpos[1] + random.choice([-1,0,1])) % len(kbloc[0]))
+        string = string[0:i]+kbloc[newpos[0]][newpos[1]]+string[i+1:]
+        return string
+    def transpose(string, i):
+        j = (i + random.choice([-1,1])) % len(string)
+        l = [c for c in string]
+        l[i], l[j] = l[j], l[i]
+        return "".join(l)
+    def randomletter(string, i):
+        string = string[0:i+1]+random.choice("abcdefghijklmnopqrstuvwxyz")+string[i+1:]
+        return string
+    def randomreplace(string, i):
+        string = string[0:i]+random.choice("abcdefghijklmnopqrstuvwxyz")+string[i+1:]
+        return string
+    def soundalike(string, i):
+        try:
+            c = sounddict[string[i]]
+        except:
+            return string
+        string = string[0:i]+c+string[i+1:]
+        return string
+    func = random.choice([mistype, transpose, randomletter, randomreplace,
+                          soundalike])
+    for i in letters:
+        word = func(word, i)
+    return word
diff --git a/mispeller.pyc b/mispeller.pyc
new file mode 100644
index 0000000..c5eb90b
Binary files /dev/null and b/mispeller.pyc differ
diff --git a/parsetools.py b/parsetools.py
index cc9f8b9..474034a 100644
--- a/parsetools.py
+++ b/parsetools.py
@@ -3,6 +3,8 @@ from copy import copy
 from datetime import timedelta
 from PyQt4 import QtGui
 
+from generic import mysteryTime
+
 _ctag_begin = re.compile(r'(?i)<c=(.*?)>')
 _gtag_begin = re.compile(r'(?i)<g[a-f]>')
 _ctag_end = re.compile(r'(?i)</c>')
@@ -183,6 +185,8 @@ def addTimeInitial(string, grammar):
 
 def timeProtocol(cmd):
     dir = cmd[0]
+    if dir == "?":
+        return mysteryTime(0)
     cmd = cmd[1:]
     cmd = re.sub("[^0-9:]", "", cmd)
     try:
@@ -195,6 +199,8 @@ def timeProtocol(cmd):
     return timed
 
 def timeDifference(td):
+    if type(td) is mysteryTime:
+        return "??:?? FROM ????"
     if td < timedelta(0):
         when = "AGO"
     else:
diff --git a/parsetools.pyc b/parsetools.pyc
index 35887e8..b589134 100644
Binary files a/parsetools.pyc and b/parsetools.pyc differ
diff --git a/pesterchum.js b/pesterchum.js
index ff954a3..e8e728a 100644
--- a/pesterchum.js
+++ b/pesterchum.js
@@ -1 +1 @@
-{"tabs": true, "soundon": true, "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"], "defaultprofile": "ghostDunk", "block": []}
\ No newline at end of file
+{"tabs": true, "soundon": true, "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"], "defaultprofile": "ghostDunk", "block": []}
\ No newline at end of file
diff --git a/pesterchum.py b/pesterchum.py
index b5edad3..91fa78e 100644
--- a/pesterchum.py
+++ b/pesterchum.py
@@ -825,6 +825,7 @@ class PesterWindow(MovingWindow):
         self.closeConversations()
         if hasattr(self, 'trollslum') and self.trollslum:
             self.trollslum.close()
+        self.closeSignal.emit()
         event.accept()
     def newMessage(self, handle, msg):
         if handle in self.config.getBlocklist():
@@ -1625,6 +1626,7 @@ class PesterWindow(MovingWindow):
     joinChannel = QtCore.pyqtSignal(QtCore.QString)
     leftChannel = QtCore.pyqtSignal(QtCore.QString)
     setChannelMode = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString)
+    closeSignal = QtCore.pyqtSignal()
 
 class IRCThread(QtCore.QThread):
     def __init__(self, ircobj):
@@ -1652,6 +1654,9 @@ class PesterTray(QtGui.QSystemTrayIcon):
             self.setIcon(PesterIcon(self.mainwindow.theme["main/icon"]))
         else:
             self.setIcon(PesterIcon(self.mainwindow.theme["main/newmsgicon"]))
+    @QtCore.pyqtSlot()
+    def mainWindowClosed(self):
+        self.hide()
 
 class MainProgram(QtCore.QObject):
     def __init__(self):
@@ -1698,6 +1703,10 @@ class MainProgram(QtCore.QObject):
                               QtCore.SIGNAL('closeToTraySignal()'),
                               self.trayicon,
                               QtCore.SLOT('show()'))
+        self.trayicon.connect(self.widget,
+                              QtCore.SIGNAL('closeSignal()'),
+                              self.trayicon,
+                              QtCore.SLOT('mainWindowClosed()'))
 
         self.irc = PesterIRC(self.widget)
         self.connectWidgets(self.irc, self.widget)
@@ -1829,7 +1838,7 @@ class MainProgram(QtCore.QObject):
         status = self.widget.loadingscreen.exec_()
         if status == QtGui.QDialog.Rejected:
             sys.exit(0)
-        os._exit(self.app.exec_())
+        sys.exit(self.app.exec_())
 
 pesterchum = MainProgram()
 pesterchum.run()