diff --git a/TODO b/TODO
index eaf639e..2fbeac4 100644
--- a/TODO
+++ b/TODO
@@ -6,6 +6,7 @@ Features:
* help menu -- about and forum
* profile switch should say current profile
* Transparent background?
+* X closes to tray
-- release alpha
* User commands/stop user from sending commands accidentally
* shared buddy lists - changes to the buddy list should refresh it?
diff --git a/convo.pyc b/convo.pyc
index 17043a1..afd1769 100644
Binary files a/convo.pyc and b/convo.pyc differ
diff --git a/dataobjs.py b/dataobjs.py
index 4d19dbb..e12fbd2 100644
--- a/dataobjs.py
+++ b/dataobjs.py
@@ -100,12 +100,21 @@ class PesterProfile(object):
color = QtGui.QColor("black")
self.color = color
self.mood = mood
- def initials(self):
+ def initials(self, time=None):
handle = self.handle
caps = [l for l in handle if l.isupper()]
if not caps:
caps = [""]
- return (handle[0]+caps[0]).upper()
+ initials = (handle[0]+caps[0]).upper()
+ if hasattr(self, 'time') and time:
+ if self.time > time:
+ return "F"+initials
+ elif self.time < time:
+ return "P"+initials
+ else:
+ return "C"+initials
+ else:
+ return (handle[0]+caps[0]).upper()
def colorhtml(self):
return self.color.name()
def colorcmd(self):
@@ -118,11 +127,37 @@ class PesterProfile(object):
def blocked(self, config):
return self.handle in config.getBlocklist()
- def memsg(self, syscolor, suffix, msg):
+ def memsg(self, syscolor, suffix, msg, time=None):
uppersuffix = suffix.upper()
- return "-- %s%s [%s%s] %s --" % (syscolor.name(), self.handle, suffix, self.colorhtml(), self.initials(), uppersuffix, msg)
+ if time is not None:
+ handle = "%s %s" % (time.temporal, self.handle)
+ initials = time.pcf+self.initials()+uppersuffix
+ else:
+ handle = self.handle
+ initials = self.initials()+uppersuffix
+ return "-- %s%s [%s] %s --" % (syscolor.name(), handle, suffix, self.colorhtml(), initials, msg)
def pestermsg(self, otherchum, syscolor, verb):
return "-- %s [%s] %s %s [%s] at %s --" % (syscolor.name(), self.handle, self.colorhtml(), self.initials(), verb, otherchum.handle, otherchum.colorhtml(), otherchum.initials(), datetime.now().strftime("%H:%M"))
+ def memoclosemsg(self, syscolor, timeGrammar, verb):
+ return "%s%s%s %s." % (syscolor.name(), self.colorhtml(), timeGrammar.pcf, self.initials(), timeGrammar.number, verb)
+ def memojoinmsg(self, syscolor, td, timeGrammar, verb):
+ (temporal, pcf, when) = (timeGrammar.temporal, timeGrammar.pcf, timeGrammar.when)
+ atd = abs(td)
+ minutes = (atd.days*86400 + atd.seconds) // 60
+ hours = minutes // 60
+ leftoverminutes = minutes % 60
+ if atd == timedelta(0):
+ timetext = when
+ elif atd < timedelta(0,3600):
+ timetext = "%d MINUTES %s" % (minutes, when)
+ elif atd < timedelta(0,3600*100):
+ timetext = "%d:%02d HOURS %s" % (hours, leftoverminutes, when)
+ else:
+ timetext = "%d HOURS %s" % (hours, when)
+ initials = pcf+self.initials()+timeGrammar.number
+ return "%s %s [%s] %s %s." % \
+ (syscolor.name(), self.colorhtml(), temporal, self.handle,
+ initials, timetext, verb)
@staticmethod
def checkLength(handle):
diff --git a/dataobjs.pyc b/dataobjs.pyc
index b6d0ace..513d6e0 100644
Binary files a/dataobjs.pyc and b/dataobjs.pyc differ
diff --git a/generic.py b/generic.py
index 173285e..ad9a8a8 100644
--- a/generic.py
+++ b/generic.py
@@ -1,5 +1,9 @@
from PyQt4 import QtGui, QtCore
+class PesterList(list):
+ def __init__(self, l):
+ self.extend(l)
+
class PesterIcon(QtGui.QIcon):
def __init__(self, *x, **y):
QtGui.QIcon.__init__(self, *x, **y)
diff --git a/generic.pyc b/generic.pyc
index 6e857d8..69c7bc6 100644
Binary files a/generic.pyc and b/generic.pyc differ
diff --git a/irc.py b/irc.py
new file mode 100644
index 0000000..168c06c
--- /dev/null
+++ b/irc.py
@@ -0,0 +1,211 @@
+from PyQt4 import QtGui, QtCore
+from oyoyo.client import IRCClient
+from oyoyo.cmdhandler import DefaultCommandHandler
+from oyoyo import helpers
+import logging
+
+from dataobjs import Mood, PesterProfile
+from generic import PesterList
+
+logging.basicConfig(level=logging.INFO)
+
+class PesterIRC(QtCore.QObject):
+ def __init__(self, window):
+ QtCore.QObject.__init__(self)
+ self.mainwindow = window
+ def IRCConnect(self):
+ self.cli = IRCClient(PesterHandler, host="irc.tymoon.eu", port=6667, nick=self.mainwindow.profile().handle, blocking=True)
+ self.cli.command_handler.parent = self
+ self.cli.command_handler.mainwindow = self.mainwindow
+ self.conn = self.cli.connect()
+
+ @QtCore.pyqtSlot(PesterProfile)
+ def getMood(self, *chums):
+ self.cli.command_handler.getMood(*chums)
+ @QtCore.pyqtSlot(PesterList)
+ def getMoods(self, chums):
+ self.cli.command_handler.getMood(*chums)
+
+ @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
+ def sendMessage(self, text, handle):
+ h = unicode(handle)
+ helpers.msg(self.cli, h, text)
+
+ @QtCore.pyqtSlot(QtCore.QString, bool)
+ def startConvo(self, handle, initiated):
+ h = unicode(handle)
+ if initiated:
+ helpers.msg(self.cli, h, "PESTERCHUM:BEGIN")
+ helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
+ @QtCore.pyqtSlot(QtCore.QString)
+ def endConvo(self, handle):
+ h = unicode(handle)
+ helpers.msg(self.cli, h, "PESTERCHUM:CEASE")
+ @QtCore.pyqtSlot()
+ def updateProfile(self):
+ me = self.mainwindow.profile()
+ handle = me.handle
+ helpers.nick(self.cli, handle)
+ self.updateMood()
+ @QtCore.pyqtSlot()
+ def updateMood(self):
+ me = self.mainwindow.profile()
+ helpers.msg(self.cli, "#pesterchum", "MOOD >%d" % (me.mood.value()))
+ @QtCore.pyqtSlot()
+ def updateColor(self):
+ me = self.mainwindow.profile()
+ for h in self.mainwindow.convos.keys():
+ helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
+ @QtCore.pyqtSlot(QtCore.QString)
+ def blockedChum(self, handle):
+ h = unicode(handle)
+ helpers.msg(self.cli, h, "PESTERCHUM:BLOCK")
+ @QtCore.pyqtSlot(QtCore.QString)
+ def unblockedChum(self, handle):
+ h = unicode(handle)
+ helpers.msg(self.cli, h, "PESTERCHUM:UNBLOCK")
+ @QtCore.pyqtSlot(QtCore.QString)
+ def requestNames(self, channel):
+ c = unicode(channel)
+ helpers.names(self.cli, c)
+ @QtCore.pyqtSlot()
+ def requestChannelList(self):
+ helpers.channel_list(self.cli)
+ @QtCore.pyqtSlot(QtCore.QString)
+ def joinChannel(self, channel):
+ c = unicode(channel)
+ helpers.join(self.cli, c)
+ @QtCore.pyqtSlot(QtCore.QString)
+ def leftChannel(self, channel):
+ c = unicode(channel)
+ helpers.part(self.cli, c)
+ def updateIRC(self):
+ self.conn.next()
+
+ moodUpdated = QtCore.pyqtSignal(QtCore.QString, Mood)
+ colorUpdated = QtCore.pyqtSignal(QtCore.QString, QtGui.QColor)
+ messageReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
+ memoReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString)
+ timeCommand = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString)
+ namesReceived = QtCore.pyqtSignal(QtCore.QString, PesterList)
+ channelListReceived = QtCore.pyqtSignal(PesterList)
+ nickCollision = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
+ connected = QtCore.pyqtSignal()
+ userPresentUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString,
+ QtCore.QString)
+
+class PesterHandler(DefaultCommandHandler):
+ def privmsg(self, nick, chan, msg):
+ # display msg, do other stuff
+ # silently ignore CTCP
+ if msg[0] == '\x01':
+ return
+ handle = nick[0:nick.find("!")]
+ logging.info("---> recv \"PRIVMSG %s :%s\"" % (handle, msg))
+ if chan == "#pesterchum":
+ # follow instructions
+ if msg[0:6] == "MOOD >":
+ try:
+ mood = Mood(int(msg[6:]))
+ except ValueError:
+ mood = Mood(0)
+ self.parent.moodUpdated.emit(handle, mood)
+ elif msg[0:7] == "GETMOOD":
+ mychumhandle = self.mainwindow.profile().handle
+ mymood = self.mainwindow.profile().mood.value()
+ if msg.find(mychumhandle, 8) != -1:
+ 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:])
+ else:
+ self.parent.memoReceived.emit(chan, handle, msg)
+ else:
+ # private message
+ # silently ignore messages to yourself.
+ if handle == self.mainwindow.profile().handle:
+ return
+ if msg[0:7] == "COLOR >":
+ colors = msg[7:].split(",")
+ try:
+ colors = [int(d) for d in colors]
+ except ValueError:
+ colors = [0,0,0]
+ color = QtGui.QColor(*colors)
+ self.parent.colorUpdated.emit(handle, color)
+ else:
+ self.parent.messageReceived.emit(handle, msg)
+
+
+ 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))
+ helpers.nick(self.client, newnick)
+ self.parent.nickCollision.emit(nick, newnick)
+ def quit(self, nick, reason):
+ handle = nick[0:nick.find("!")]
+ self.parent.userPresentUpdate.emit(handle, "", "quit")
+ self.parent.moodUpdated.emit(handle, Mood("offline"))
+ 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 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 nick(self, oldnick, newnick):
+ oldhandle = oldnick[0:oldnick.find("!")]
+ newchum = PesterProfile(newnick, chumdb=self.mainwindow.chumdb)
+ self.parent.moodUpdated.emit(oldhandle, Mood("offline"))
+ if newnick in self.mainwindow.chumList.chums:
+ self.getMood(newchum)
+ 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 = []
+
+ def getMood(self, *chums):
+ chumglub = "GETMOOD "
+ for c in chums:
+ chandle = c.handle
+ if len(chumglub+chandle) >= 350:
+ helpers.msg(self.client, "#pesterchum", chumglub)
+ chumglub = "GETMOOD "
+ chumglub += chandle
+ if chumglub != "GETMOOD ":
+ helpers.msg(self.client, "#pesterchum", chumglub)
diff --git a/irc.pyc b/irc.pyc
new file mode 100644
index 0000000..2c6f50f
Binary files /dev/null and b/irc.pyc differ
diff --git a/logs/chums.js b/logs/chums.js
index 7c544c4..af455a4 100644
--- a/logs/chums.js
+++ b/logs/chums.js
@@ -1 +1 @@
-{"macruralAlchemist": {"color": "#700000", "handle": "macruralAlchemist", "mood": "offline"}, "agogPorphyry": {"color": "#522d80", "handle": "agogPorphyry", "mood": "offline"}, "fireSwallow": {"color": "#80bb9a", "handle": "fireSwallow", "mood": "offline"}, "aquaMarinist": {"color": "#00caca", "handle": "aquaMarinist", "mood": "offline"}, "nitroZealist": {"color": "#ff3737", "handle": "nitroZealist", "mood": "offline"}, "superGhost": {"color": "#800564", "handle": "superGhost", "mood": "offline"}, "tentacleTherapist": {"color": "#cc66ff", "handle": "tentacleTherapist", "mood": "offline"}, "gardenGnostic": {"color": "#00ff00", "handle": "gardenGnostic", "mood": "offline"}, "aquaticMarinist": {"color": "#00caca", "handle": "aquaticMarinist", "mood": "offline"}, "captainCaveman": {"color": "#7c414e", "handle": "captainCaveman", "mood": "offline"}, "greenZephyr": {"color": "#00ca40", "handle": "greenZephyr", "mood": "offline"}, "pesterClient394": {"color": "#ff3737", "handle": "pesterClient394", "mood": "offline"}, "gamblingGenocider": {"color": "#00ff00", "handle": "gamblingGenocider", "mood": "offline"}, "mechanicalSpectacle": {"color": "#0000ff", "handle": "mechanicalSpectacle", "mood": "offline"}, "elegantDiversion": {"color": "#12b40d", "handle": "elegantDiversion", "mood": "offline"}, "absoluteTranquility": {"color": "#000033", "handle": "absoluteTranquility", "mood": "offline"}, "centaursTesticle": {"color": "#000056", "handle": "centaursTesticle", "mood": "offline"}, "schlagzeugGator": {"color": "#61821f", "handle": "schlagzeugGator", "mood": "offline"}, "unknownTraveler": {"color": "#006666", "handle": "unknownTraveler", "mood": "offline"}, "remoteBloodbath": {"color": "#c70000", "handle": "remoteBloodbath", "mood": "offline"}, "marineAquist": {"color": "#00caca", "handle": "marineAquist", "mood": "offline"}}
\ No newline at end of file
+{"macruralAlchemist": {"color": "#700000", "handle": "macruralAlchemist", "mood": "offline"}, "fireSwallow": {"color": "#80bb9a", "handle": "fireSwallow", "mood": "offline"}, "aquaMarinist": {"color": "#00caca", "handle": "aquaMarinist", "mood": "offline"}, "nitroZealist": {"color": "#ff3737", "handle": "nitroZealist", "mood": "offline"}, "masterG": {"color": "#77003c", "handle": "masterG", "mood": "offline"}, "aquaticMarinist": {"color": "#00caca", "handle": "aquaticMarinist", "mood": "offline"}, "iw": {"color": "#ff0000", "handle": "iw", "mood": "offline"}, "pesterClient394": {"color": "#ff3737", "handle": "pesterClient394", "mood": "offline"}, "absoluteTranquility": {"color": "#000033", "handle": "absoluteTranquility", "mood": "offline"}, "centaursTesticle": {"color": "#000056", "handle": "centaursTesticle", "mood": "offline"}, "agogPorphyry": {"color": "#522d80", "handle": "agogPorphyry", "mood": "offline"}, "illuminatedWax": {"color": "#ffff00", "handle": "illuminatedWax", "mood": "offline"}, "gamblingGenocider": {"color": "#00ff00", "handle": "gamblingGenocider", "mood": "offline"}, "elegantDiversion": {"color": "#12b40d", "handle": "elegantDiversion", "mood": "offline"}, "testOut": {"color": "#c760cc", "handle": "testOut", "mood": "offline"}, "superGhost": {"color": "#800564", "handle": "superGhost", "mood": "offline"}, "tentacleTherapist": {"color": "#cc66ff", "handle": "tentacleTherapist", "mood": "offline"}, "captainCaveman": {"color": "#7c414e", "handle": "captainCaveman", "mood": "offline"}, "cuttlefishCuller": {"color": "#77003c", "handle": "cuttlefishCuller", "mood": "offline"}, "mechanicalSpectacle": {"color": "#0000ff", "handle": "mechanicalSpectacle", "mood": "offline"}, "remoteBloodbath": {"color": "#c70000", "handle": "remoteBloodbath", "mood": "offline"}, "rageInducer": {"color": "#00ffff", "handle": "rageInducer", "mood": "offline"}, "gallowsCalibrator": {"color": "#008282", "handle": "gallowsCalibrator", "mood": "offline"}, "greenZephyr": {"color": "#00ca40", "handle": "greenZephyr", "mood": "offline"}, "schlagzeugGator": {"color": "#61821f", "handle": "schlagzeugGator", "mood": "offline"}, "gardenGnostic": {"color": "#00ff00", "handle": "gardenGnostic", "mood": "offline"}, "unknownTraveler": {"color": "#006666", "handle": "unknownTraveler", "mood": "offline"}, "marineAquist": {"color": "#00caca", "handle": "marineAquist", "mood": "offline"}}
\ No newline at end of file
diff --git a/memos.py b/memos.py
index cbb4984..2fd3aaa 100644
--- a/memos.py
+++ b/memos.py
@@ -1,10 +1,170 @@
from string import Template
import re
from PyQt4 import QtGui, QtCore
+from datetime import time, timedelta, datetime
from dataobjs import PesterProfile, Mood
from generic import PesterIcon
from convo import PesterConvo, PesterInput, PesterText, PesterTabWindow
+from parsetools import convertTags, escapeBrackets, addTimeInitial, timeProtocol
+
+def delta2txt(d, format="pc"):
+ if format == "pc":
+ sign = "+" if d >= timedelta(0) else "-"
+ else:
+ if d == timedelta(0):
+ return ""
+ sign = "F" if d >= timedelta(0) else "P"
+ d = abs(d)
+ totalminutes = (d.days*86400 + d.seconds) // 60
+ hours = totalminutes // 60
+ leftovermins = totalminutes % 60
+ if hours < 100:
+ if format == "pc":
+ return "%s%d:%02d" % (sign, hours, leftovermins)
+ else:
+ return "%s%02d:%02d" % (sign, hours, leftovermins)
+ else:
+ if format == "pc":
+ return "%s%d" % (sign, hours)
+ else:
+ return "%s%02d:%02d" % (sign, hours, leftovermins)
+
+def txt2delta(txt):
+ sign = 1
+ if txt[0] == '+':
+ txt = txt[1:]
+ elif txt[0] == '-':
+ sign = -1
+ txt = txt[1:]
+ l = txt.split(":")
+ try:
+ h = int(l[0])
+ m = 0
+ if len(l) > 1:
+ m = int(l[1])
+ timed = timedelta(0, h*3600+m*60)
+ except ValueError:
+ timed = timedelta(0)
+ return sign*timed
+
+def pcfGrammar(td):
+ if td > timedelta(0):
+ when = "FROM NOW"
+ temporal = "FUTURE"
+ pcf = "F"
+ elif td < timedelta(0):
+ when = "AGO"
+ temporal = "PAST"
+ pcf = "P"
+ else:
+ when = "RIGHT NOW"
+ temporal = "CURRENT"
+ pcf = "C"
+ return (temporal, pcf, when)
+
+class TimeGrammar(object):
+ def __init__(self, temporal, pcf, when, number="0"):
+ self.temporal = temporal
+ self.pcf = pcf
+ self.when = when
+ if number == "0" or number == 0:
+ self.number = ""
+ else:
+ self.number = str(number)
+
+class TimeTracker(list):
+ def __init__(self, time=None):
+ self.timerecord = {"P": [], "F": []}
+ if time is not None:
+ self.append(time)
+ self.current=0
+ self.addRecord(time)
+ else:
+ self.current=-1
+ def addTime(self, timed):
+ try:
+ i = self.index(timed)
+ self.current = i
+ except ValueError:
+ self.current = len(self)
+ self.append(timed)
+ self.addRecord(timed)
+ def setCurrent(self, timed):
+ self.current = self.index(timed)
+ def addRecord(self, timed):
+ (temporal, pcf, when) = pcfGrammar(timed - timedelta(0))
+ if pcf == "C":
+ 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":
+ return 0
+ if len(self.timerecord[pcf]) > 1:
+ return self.timerecord[pcf].index(timed)+1
+ else:
+ return 0
+ def removeTime(self, timed):
+ try:
+ self.pop(self.index(timed))
+ self.current = len(self)-1
+ except ValueError:
+ pass
+ def getTime(self):
+ if self.current >= 0:
+ return self[self.current]
+ else:
+ return None
+ def getGrammar(self):
+ timed = self.getTime()
+ mytime = timedelta(0)
+ (temporal, pcf, when) = pcfGrammar(timed - mytime)
+ if timed == mytime:
+ return TimeGrammar(temporal, pcf, when, 0)
+ return TimeGrammar(temporal, pcf, when, self.getRecord(timed))
+
+class TimeInput(QtGui.QLineEdit):
+ def __init__(self, timeslider, parent):
+ QtGui.QLineEdit.__init__(self, parent)
+ self.timeslider = timeslider
+ self.setText("+0:00")
+ self.connect(self.timeslider, QtCore.SIGNAL('valueChanged(int)'),
+ self, QtCore.SLOT('setTime(int)'))
+ self.connect(self, QtCore.SIGNAL('editingFinished()'),
+ self, QtCore.SLOT('setSlider()'))
+ @QtCore.pyqtSlot(int)
+ def setTime(self, sliderval):
+ self.setText(self.timeslider.getTime())
+ @QtCore.pyqtSlot()
+ def setSlider(self):
+ value = unicode(self.text())
+ timed = txt2delta(value)
+ sign = 1 if timed >= timedelta(0) else -1
+ abstimed = abs(txt2delta(value))
+ index = 50
+ for i, td in enumerate(timedlist):
+ if abstimed < td:
+ index = i-1
+ break
+ self.timeslider.setValue(sign*index)
+ text = delta2txt(timed)
+ self.setText(text)
+
+class TimeSlider(QtGui.QSlider):
+ def __init__(self, orientation, parent):
+ QtGui.QSlider.__init__(self, orientation, parent)
+ self.setTracking(True)
+ self.setMinimum(-50)
+ self.setMaximum(50)
+ self.setValue(0)
+ self.setPageStep(1)
+ def getTime(self):
+ time = timelist[abs(self.value())]
+ sign = "+" if self.value() >= 0 else "-"
+ return sign+time
class MemoTabWindow(PesterTabWindow):
def __init__(self, mainwindow, parent=None):
@@ -30,10 +190,57 @@ class MemoText(PesterText):
self.setReadOnly(True)
self.setMouseTracking(True)
def addMessage(self, text, chum):
- # get chum color from c tag
- mobj = _ctag_begin.match(text)
- # tinychum sends straight /me with no color. go to chumdb!
- systemColor = QtGui.QColor(self.parent().mainwindow.theme["memo/systemMsgColor"])
+ parent = self.parent()
+ window = parent.mainwindow
+ me = window.profile()
+ msg = unicode(text)
+ chumdb = window.chumdb
+ if chum is not me: # SO MUCH WH1T3SP4C3 >:]
+ mobj = _ctag_begin.match(text) # get color from tag
+ if mobj:
+ try:
+ color = QtGui.QColor(*[int(c) for c in mobj.group(1).split(",")])
+ except ValueError:
+ color = QtGui.QColor("black")
+ else:
+ chumdb.setColor(chum.handle, color)
+ parent.updateColor(chum.handle, color)
+ else:
+ color = chumdb.getColor(chum.handle)
+ else:
+ color = me.color
+
+ chum.color = color
+ systemColor = QtGui.QColor(window.theme["memos/systemMsgColor"])
+ if chum is not me:
+ if parent.times.has_key(chum.handle):
+ time = parent.times[chum.handle]
+ else:
+ # new chum! time current
+ newtime = timedelta(0)
+ time = TimeTracker(newtime)
+ parent.times[chum.handle] = time
+ timeGrammar = time.getGrammar()
+ self.append(convertTags(chum.memojoinmsg(systemColor, time.getTime(), timeGrammar, window.theme["convo/text/joinmemo"])))
+ else:
+ time = parent.time
+
+ if msg[0:3] == "/me" or msg[0:13] == "PESTERCHUM:ME":
+ if msg[0:3] == "/me":
+ start = 3
+ else:
+ start = 13
+ space = msg.find(" ")
+ msg = chum.memsg(systemColor, msg[start:space], msg[space:], timegrammar=time.getGrammar())
+ window.chatlog.log(parent.channel, convertTags(msg, "bbcode"))
+ self.append(convertTags(msg))
+ else:
+ if chum is not me:
+ msg = addTimeInitial(msg, parent.times[chum.handle].getGrammar())
+ msg = escapeBrackets(msg)
+ self.append(convertTags(msg))
+ window.chatlog.log(parent.channel, convertTags(msg, "bbcode"))
+
def changeTheme(self):
pass
@@ -46,10 +253,11 @@ class MemoInput(PesterInput):
self.setStyleSheet(theme["memos/input/style"])
class PesterMemo(PesterConvo):
- def __init__(self, channel, mainwindow, parent=None):
+ def __init__(self, channel, timestr, mainwindow, parent=None):
QtGui.QFrame.__init__(self, parent)
self.channel = channel
self.mainwindow = mainwindow
+ self.time = TimeTracker(txt2delta(timestr))
self.setWindowTitle(channel)
self.channelLabel = QtGui.QLabel(self)
self.channelLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding))
@@ -61,8 +269,13 @@ class PesterMemo(PesterConvo):
self.userlist = QtGui.QListWidget(self)
self.userlist.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding))
- self.timeslider = QtGui.QSlider(QtCore.Qt.Vertical, self)
- self.timeinput = QtGui.QLineEdit(self)
+ self.timeslider = TimeSlider(QtCore.Qt.Horizontal, self)
+ self.timeinput = TimeInput(self.timeslider, self)
+ self.timeinput.setText(timestr)
+ self.timeinput.setSlider()
+ self.timetravel = QtGui.QPushButton("GO", self)
+
+ self.times = {}
self.initTheme(self.mainwindow.theme)
@@ -71,27 +284,36 @@ class PesterMemo(PesterConvo):
self, QtCore.SLOT('sentMessage()'))
layout_0 = QtGui.QVBoxLayout()
- layout_0.addWidget(self.channelLabel)
layout_0.addWidget(self.textArea)
layout_0.addWidget(self.textInput)
- layout_1 = QtGui.QGridLayout()
- layout_1.addWidget(self.timeslider, 0, 1, QtCore.Qt.AlignHCenter)
- layout_1.addWidget(self.timeinput, 1, 0, 1, 3)
+ layout_1 = QtGui.QHBoxLayout()
+ layout_1.addLayout(layout_0)
+ layout_1.addWidget(self.userlist)
- self.layout = QtGui.QHBoxLayout()
- self.setLayout(self.layout)
+# layout_1 = QtGui.QGridLayout()
+# layout_1.addWidget(self.timeslider, 0, 1, QtCore.Qt.AlignHCenter)
+# layout_1.addWidget(self.timeinput, 1, 0, 1, 3)
+ layout_2 = QtGui.QHBoxLayout()
+ layout_2.addWidget(self.timeslider)
+ layout_2.addWidget(self.timeinput)
+ layout_2.addWidget(self.timetravel)
- self.layout.addLayout(layout_0)
- self.layout.addWidget(self.userlist)
+ self.layout = QtGui.QVBoxLayout()
+
+ self.layout.addWidget(self.channelLabel)
self.layout.addLayout(layout_1)
+ self.layout.addLayout(layout_2)
self.layout.setSpacing(0)
margins = self.mainwindow.theme["memos/margins"]
self.layout.setContentsMargins(margins["left"], margins["top"],
margins["right"], margins["bottom"])
+ self.setLayout(self.layout)
+
if parent:
parent.addChat(self)
+
self.newmessage = False
def title(self):
@@ -99,17 +321,28 @@ class PesterMemo(PesterConvo):
def icon(self):
return PesterIcon(self.mainwindow.theme["memos/memoicon"])
+ def sendTimeInfo(self, newChum=False):
+ if newChum:
+ self.messageSent.emit("PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server")+"i"),
+ self.title())
+ else:
+ self.messageSent.emit("PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server")),
+ self.title())
+
def updateMood(self):
pass
def updateBlocked(self):
pass
- def updateColor(self):
- pass
+ def updateColor(self, handle, color):
+ chums = self.userlist.findItems(handle, QtCore.Qt.MatchFlags(0))
+ for c in chums:
+ c.setTextColor(color)
def addMessage(self, text, handle):
if type(handle) is bool:
chum = self.mainwindow.profile()
else:
chum = PesterProfile(handle)
+ self.notifyNewMessage()
self.textArea.addMessage(text, chum)
def initTheme(self, theme):
@@ -125,7 +358,7 @@ class PesterMemo(PesterConvo):
self.channelLabel.setMaximumHeight(theme["memos/label/maxheight"])
self.channelLabel.setMinimumHeight(theme["memos/label/minheight"])
- self.userlist.setStyleSheet(theme["main/chums/style"])
+ self.userlist.setStyleSheet(theme["memos/userlist/style"])
self.userlist.setFixedWidth(theme["memos/userlist/width"])
self.timeinput.setFixedWidth(theme["memos/time/text/width"])
@@ -137,23 +370,127 @@ class PesterMemo(PesterConvo):
self.initTheme(theme)
self.textArea.changeTheme(theme)
self.textInput.changeTheme(theme)
+ margins = theme["memos/margins"]
+ self.layout.setContentsMargins(margins["left"], margins["top"],
+ margins["right"], margins["bottom"])
+
+ def addUser(self, handle):
+ chumdb = self.mainwindow.chumdb
+ defaultcolor = QtGui.QColor("black")
+ op = False
+ if handle[0] == '@':
+ op = True
+ handle = handle[1:]
+ item = QtGui.QListWidgetItem(handle)
+ if handle == self.mainwindow.profile().handle:
+ color = self.mainwindow.profile().color
+ else:
+ color = chumdb.getColor(handle, defaultcolor)
+ item.setTextColor(color)
+ self.userlist.addItem(item)
+
+ def timeUpdate(self, handle, cmd):
+ window = self.mainwindow
+ chum = PesterProfile(handle)
+ systemColor = QtGui.QColor(window.theme["memos/systemMsgColor"])
+ close = None
+ # old TC command?
+ try:
+ secs = int(cmd)
+ time = datetime.fromtimestamp(secs)
+ timed = time - datetime.now()
+ s = (timed // 60)*60
+ timed = timedelta(timed.days, s)
+ except ValueError:
+ if cmd == "i":
+ timed = timedelta(0)
+ else:
+ if cmd[len(cmd)-1] == 'c':
+ close = timeProtocol(cmd)
+ timed = None
+ else:
+ timed = timeProtocol(cmd)
+
+ if self.times.has_key(handle):
+ if close is not None:
+ if close in self.times[handle]:
+ self.times[handle].setCurrent(close)
+ grammar = self.times[handle].getGrammar()
+ self.times[handle].removeTime(close)
+ self.textArea.append(convertTags(chum.memoclosemsg(systemColor, grammar, window.theme["convo/text/closememo"])))
+ elif timed not in self.times[handle]:
+ self.times[handle].addTime(timed)
+ grammar = self.times[handle].getGrammar()
+ self.textArea.append(convertTags(chum.memojoinmsg(systemColor, timed, grammar, window.theme["convo/text/joinmemo"])))
+ else:
+ self.times[handle].setCurrent(timed)
+ else:
+ if timed is not None:
+ ttracker = TimeTracker(timed)
+ grammar = ttracker.getGrammar()
+ self.textArea.append(convertTags(chum.memojoinmsg(systemColor, timed, grammar, window.theme["convo/text/joinmemo"])))
+ self.times[handle] = ttracker
@QtCore.pyqtSlot()
def sentMessage(self):
text = self.textInput.text()
if text == "":
return
- text = "%s" % (self.mainwindow.profile().colorcmd(), text)
- self.textInput.setText(text)
- PesterConvo.sentMessage(self)
+ grammar = self.time.getGrammar()
+ # deal with quirks here
+ qtext = self.mainwindow.userprofile.quirks.apply(unicode(text))
+ if qtext[0:3] != "/me":
+ initials = self.mainwindow.profile().initials()
+ colorcmd = self.mainwindow.profile().colorcmd()
+ clientText = "%s%s%s: %s" % (colorcmd, grammar.pcf, initials, grammar.number, qtext)
+ # account for TC's parsing error
+ serverText = "%s: %s " % (colorcmd, initials, qtext)
+ else:
+ clientText = qtext
+ serverText = clientText
+ self.textInput.setText("")
+ self.addMessage(clientText, True)
+ # convert color tags
+ text = convertTags(unicode(serverText), "ctag")
+ self.messageSent.emit(serverText, self.title())
+
+ @QtCore.pyqtSlot()
+ def namesUpdated(self):
+ # get namesdb
+ namesdb = self.mainwindow.namesdb
+ # reload names
+ self.userlist.clear()
+ for n in self.mainwindow.namesdb[self.channel]:
+ self.addUser(n)
+
+ @QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
+ def userPresentChange(self, handle, channel, update):
+ if channel != self.channel:
+ return
+ chums = self.userlist.findItems(handle, QtCore.Qt.MatchFlags(0))
+ h = unicode(handle)
+ c = unicode(channel)
+ systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
+ # print exit
+ if update == "quit" or update == "left":
+ for c in chums:
+ chum = PesterProfile(h)
+ self.userlist.takeItem(self.userlist.row(c))
+ while self.times[h].getTime() is not None:
+ t = self.times[h]
+ grammar = t.getGrammar()
+ self.textArea.append(convertTags(chum.memoclosemsg(systemColor, grammar, self.mainwindow.theme["convo/text/closememo"])))
+ self.times[h].removeTime(t.getTime())
+ elif update == "join":
+ self.addUser(h)
def closeEvent(self, event):
self.mainwindow.waitingMessages.messageAnswered(self.channel)
-# self.windowClosed.emit(self.chum.handle)
+ self.windowClosed.emit(self.title())
+
+ windowClosed = QtCore.pyqtSignal(QtCore.QString)
-# messageSent - signal -> sendMessage -> sendMessage(Memo)
-# windowClosed - signal -> closeMemo
+timelist = ["0:00", "0:01", "0:02", "0:04", "0:06", "0:10", "0:14", "0:22", "0:30", "0:41", "1:00", "1:34", "2:16", "3:14", "4:13", "4:20", "5:25", "6:12", "7:30", "8:44", "10:25", "11:34", "14:13", "16:12", "17:44", "22:22", "25:10", "33:33", "42:00", "43:14", "50:00", "62:12", "75:00", "88:44", "100", "133", "143", "188", "200", "222", "250", "314", "333", "413", "420", "500", "600", "612", "888", "1000", "1025"]
-# self.textInput
-# self.textArea
+timedlist = [timedelta(0), timedelta(0, 60), timedelta(0, 120), timedelta(0, 240), timedelta(0, 360), timedelta(0, 600), timedelta(0, 840), timedelta(0, 1320), timedelta(0, 1800), timedelta(0, 2460), timedelta(0, 3600), timedelta(0, 5640), timedelta(0, 8160), timedelta(0, 11640), timedelta(0, 15180), timedelta(0, 15600), timedelta(0, 19500), timedelta(0, 22320), timedelta(0, 27000), timedelta(0, 31440), timedelta(0, 37500), timedelta(0, 41640), timedelta(0, 51180), timedelta(0, 58320), timedelta(0, 63840), timedelta(0, 80520), timedelta(1, 4200), timedelta(1, 34380), timedelta(1, 64800), timedelta(1, 69240), timedelta(2, 7200), timedelta(2, 51120), timedelta(3, 10800), timedelta(3, 60240), timedelta(4, 14400), timedelta(5, 46800), timedelta(5, 82800), timedelta(7, 72000), timedelta(8, 28800), timedelta(9, 21600), timedelta(10, 36000), timedelta(13, 7200), timedelta(13, 75600), timedelta(17, 18000), timedelta(17, 43200), timedelta(20, 72000), timedelta(25), timedelta(25, 43200), timedelta(37), timedelta(41, 57600), timedelta(42, 61200)]
diff --git a/memos.pyc b/memos.pyc
index a2a0430..1f17bed 100644
Binary files a/memos.pyc and b/memos.pyc differ
diff --git a/menus.py b/menus.py
index e187afd..eb3611c 100644
--- a/menus.py
+++ b/menus.py
@@ -3,6 +3,7 @@ import re
from generic import RightClickList, MultiTextDialog
from dataobjs import pesterQuirk, PesterProfile
+from memos import TimeSlider, TimeInput
class PesterQuirkItem(QtGui.QListWidgetItem):
def __init__(self, quirk, parent):
@@ -407,13 +408,17 @@ class PesterMemoList(QtGui.QDialog):
self.orjoinlabel = QtGui.QLabel("OR MAKE A NEW MEMO:")
self.newmemo = QtGui.QLineEdit(self)
+ self.timelabel = QtGui.QLabel("TIMEFRAME:")
+ self.timeslider = TimeSlider(QtCore.Qt.Horizontal, self)
+ self.timeinput = TimeInput(self.timeslider, self)
+
self.cancel = QtGui.QPushButton("CANCEL", self)
self.connect(self.cancel, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('reject()'))
self.join = QtGui.QPushButton("JOIN", self)
self.join.setDefault(True)
self.connect(self.join, QtCore.SIGNAL('clicked()'),
- self, QtCore.SLOT('accept()'))
+ self, QtCore.SLOT('checkEmpty()'))
layout_ok = QtGui.QHBoxLayout()
layout_ok.addWidget(self.cancel)
layout_ok.addWidget(self.join)
@@ -423,6 +428,9 @@ class PesterMemoList(QtGui.QDialog):
layout_0.addWidget(self.channelarea)
layout_0.addWidget(self.orjoinlabel)
layout_0.addWidget(self.newmemo)
+ layout_0.addWidget(self.timelabel)
+ layout_0.addWidget(self.timeslider)
+ layout_0.addWidget(self.timeinput)
layout_0.addLayout(layout_ok)
self.setLayout(layout_0)
@@ -446,6 +454,12 @@ class PesterMemoList(QtGui.QDialog):
item.setTextColor(QtGui.QColor(theme["main/chums/userlistcolor"]))
item.setIcon(QtGui.QIcon(theme["memos/memoicon"]))
+ @QtCore.pyqtSlot()
+ def checkEmpty(self):
+ newmemo = self.newmemoname()
+ selectedmemo = self.selectedmemo()
+ if newmemo or selectedmemo:
+ self.accept()
@QtCore.pyqtSlot(QtGui.QListWidgetItem)
def joinActivatedMemo(self, item):
self.channelarea.setCurrentItem(item)
@@ -460,16 +474,11 @@ class LoadingScreen(QtGui.QDialog):
self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"])
self.loadinglabel = QtGui.QLabel("LO4D1NG")
+ self.cancel = QtGui.QPushButton("QU1T >:?")
+ self.connect(self.cancel, QtCore.SIGNAL('clicked()'),
+ self, QtCore.SLOT('reject()'))
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.loadinglabel)
+ self.layout.addWidget(self.cancel)
self.setLayout(self.layout)
- QtCore.QTimer.singleShot(25000, self, QtCore.SLOT('connectTimeout()'))
- @QtCore.pyqtSlot()
- def connectTimeout(self):
- if hasattr(self, 'failed'):
- self.accept()
- else:
- self.failed = True
- self.loadinglabel.setText("F41L3D")
- QtCore.QTimer.singleShot(1000, self, QtCore.SLOT('connectTimeout()'))
diff --git a/menus.pyc b/menus.pyc
index 7229cd5..e7171d2 100644
Binary files a/menus.pyc and b/menus.pyc differ
diff --git a/oyoyo/__init__.pyc b/oyoyo/__init__.pyc
index be26c04..28e3e80 100644
Binary files a/oyoyo/__init__.pyc and b/oyoyo/__init__.pyc differ
diff --git a/oyoyo/client.pyc b/oyoyo/client.pyc
index 5ba5325..9115adc 100644
Binary files a/oyoyo/client.pyc and b/oyoyo/client.pyc differ
diff --git a/oyoyo/cmdhandler.pyc b/oyoyo/cmdhandler.pyc
index e9def19..d97b3d1 100644
Binary files a/oyoyo/cmdhandler.pyc and b/oyoyo/cmdhandler.pyc differ
diff --git a/oyoyo/helpers.pyc b/oyoyo/helpers.pyc
index c05a1cd..16a01a9 100644
Binary files a/oyoyo/helpers.pyc and b/oyoyo/helpers.pyc differ
diff --git a/oyoyo/ircevents.pyc b/oyoyo/ircevents.pyc
index 1f1ccd6..c83999f 100644
Binary files a/oyoyo/ircevents.pyc and b/oyoyo/ircevents.pyc differ
diff --git a/oyoyo/parse.pyc b/oyoyo/parse.pyc
index e67f2ea..9095b24 100644
Binary files a/oyoyo/parse.pyc and b/oyoyo/parse.pyc differ
diff --git a/parsetools.py b/parsetools.py
index 5e9236b..fe3aca5 100644
--- a/parsetools.py
+++ b/parsetools.py
@@ -1,4 +1,5 @@
import re
+from datetime import timedelta
from PyQt4 import QtGui
_ctag_begin = re.compile(r'')
@@ -87,3 +88,23 @@ def escapeBrackets(string):
for i in range(0, btlen-etlen):
retval += ""
return retval
+
+def addTimeInitial(string, grammar):
+ endofi = string.find(":")
+ endoftag = string.find(">")
+ if endoftag < 0 or endoftag > 16 or endofi > 17:
+ return string
+ return string[0:endoftag+1]+grammar.pcf+string[endoftag+1:endofi]+grammar.number+string[endofi:]
+
+def timeProtocol(cmd):
+ dir = cmd[0]
+ cmd = cmd[1:]
+ cmd = re.sub("[^0-9:]", "", cmd)
+ try:
+ l = [int(x) for x in cmd.split(":")]
+ except ValueError:
+ l = [0,0]
+ timed = timedelta(0, l[0]*3600+l[1]*60)
+ if dir == "P":
+ timed = timed*-1
+ return timed
diff --git a/parsetools.pyc b/parsetools.pyc
index 82e6ea5..64d926f 100644
Binary files a/parsetools.pyc and b/parsetools.pyc differ
diff --git a/pesterchum.js b/pesterchum.js
index 129b3f8..9fb7056 100644
--- a/pesterchum.js
+++ b/pesterchum.js
@@ -1 +1 @@
-{"tabs": false, "chums": ["aquaMarinist", "marineAquist", "unknownTraveler", "tentacleTherapist", "macruralAlchemist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "fireSwallow", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr", "arsenicCatnip", "adiosToreador"], "defaultprofile": "testProfile", "block": []}
\ No newline at end of file
+{"tabs": true, "chums": ["aquaMarinist", "marineAquist", "unknownTraveler", "tentacleTherapist", "macruralAlchemist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "fireSwallow", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr", "arsenicCatnip", "adiosToreador", "cuttlefishCuller", "rageInducer", "gallowsCalibrator", "caligulasAquarium"], "defaultprofile": "testProfile", "block": []}
\ No newline at end of file
diff --git a/pesterchum.py b/pesterchum.py
index ba2b613..35b392b 100644
--- a/pesterchum.py
+++ b/pesterchum.py
@@ -1,7 +1,4 @@
# pesterchum
-from oyoyo.client import IRCClient
-from oyoyo.cmdhandler import DefaultCommandHandler
-from oyoyo import helpers
import logging
import os, sys
import os.path
@@ -17,12 +14,11 @@ from menus import PesterChooseQuirks, PesterChooseTheme, \
PesterChooseProfile, PesterOptions, PesterUserlist, PesterMemoList, \
LoadingScreen
from dataobjs import PesterProfile, Mood, pesterQuirk, pesterQuirks
-from generic import PesterIcon, RightClickList, MultiTextDialog
+from generic import PesterIcon, RightClickList, MultiTextDialog, PesterList
from convo import PesterTabWindow, PesterText, PesterInput, PesterConvo
from parsetools import convertTags
from memos import PesterMemo, MemoTabWindow
-
-logging.basicConfig(level=logging.INFO)
+from irc import PesterIRC
class waitingMessageHolder(object):
def __init__(self, mainwindow, **msgfuncs):
@@ -110,10 +106,6 @@ class PesterProfileDB(dict):
dict.__setitem__(self, key, val)
self.save()
-class PesterList(list):
- def __init__(self, l):
- self.extend(l)
-
class pesterTheme(dict):
def __init__(self, name):
self.path = "themes/%s" % (name)
@@ -708,6 +700,8 @@ class PesterWindow(MovingWindow):
if not self.config.defaultprofile():
self.changeProfile()
self.loadingscreen = LoadingScreen(self)
+ self.connect(self.loadingscreen, QtCore.SIGNAL('rejected()'),
+ self, QtCore.SLOT('close()'))
def profile(self):
return self.userprofile.chat
@@ -756,6 +750,7 @@ class PesterWindow(MovingWindow):
return
memo = self.memos[chan]
memo.addMessage(msg, handle)
+ self.alarm.play()
def changeColor(self, handle, color):
# pesterconvo and chumlist
@@ -798,28 +793,34 @@ class PesterWindow(MovingWindow):
self.connect(self.tabmemo, QtCore.SIGNAL('windowClosed()'),
self, QtCore.SLOT('memoTabsClosed()'))
- def newMemo(self, channel):
+ def newMemo(self, channel, timestr):
if channel == "#pesterchum":
return
if self.memos.has_key(channel):
- # load memo
+ self.memos[channel].showChat()
return
# do slider dialog then set
if self.config.tabs():
if not self.tabmemo:
self.createMemoTabWindow()
- memoWindow = PesterMemo(channel, self, self.tabmemo)
+ memoWindow = PesterMemo(channel, timestr, self, self.tabmemo)
self.tabmemo.show()
else:
- memoWindow = PesterMemo(channel, self, None)
+ memoWindow = PesterMemo(channel, timestr, self, None)
# connect signals
self.connect(memoWindow, QtCore.SIGNAL('messageSent(QString, QString)'),
self, QtCore.SIGNAL('sendMessage(QString, QString)'))
-# self.connect(memoWindow, QtCore.SIGNAL('windowClosed(QString)'),
-# self, QtCore.SLOT('closeConvo(QString)'))
+ self.connect(memoWindow, QtCore.SIGNAL('windowClosed(QString)'),
+ self, QtCore.SLOT('closeMemo(QString)'))
+ self.connect(self, QtCore.SIGNAL('namesUpdated()'),
+ memoWindow, QtCore.SLOT('namesUpdated()'))
+ self.connect(self,
+ QtCore.SIGNAL('userPresentSignal(QString, QString, QString)'),
+ memoWindow, QtCore.SLOT('userPresentChange(QString, QString, QString)'))
# chat client send memo open
self.memos[channel] = memoWindow
- self.joinChannel.emit(channel)
+ self.joinChannel.emit(channel) # race condition?
+ memoWindow.sendTimeInfo()
memoWindow.show()
def addChum(self, chum):
@@ -859,6 +860,7 @@ class PesterWindow(MovingWindow):
self.opts.setText(theme["main/menus/client/options"])
self.exitaction.setText(theme["main/menus/client/exit"])
self.userlistaction.setText(theme["main/menus/client/userlist"])
+ self.memoaction.setText(theme["main/menus/client/memos"])
self.filemenu.setTitle(theme["main/menus/client/_name"])
self.changetheme.setText(theme["main/menus/profile/theme"])
self.changequirks.setText(theme["main/menus/profile/quirks"])
@@ -949,7 +951,7 @@ class PesterWindow(MovingWindow):
@QtCore.pyqtSlot()
def connected(self):
if self.loadingscreen:
- self.loadingscreen.close()
+ self.loadingscreen.accept()
self.loadingscreen = None
@QtCore.pyqtSlot()
def blockSelectedChum(self):
@@ -977,9 +979,15 @@ class PesterWindow(MovingWindow):
chumopen = self.convos[h].chumopen
if chumopen:
self.chatlog.log(chum.handle, convertTags(self.profile().pestermsg(chum, QtGui.QColor(self.theme["convo/systemMsgColor"]), self.theme["convo/text/ceasepester"]), "bbcode"))
- self.chatlog.finish(h)
self.convoClosed.emit(handle)
+ self.chatlog.finish(h)
del self.convos[h]
+ @QtCore.pyqtSlot(QtCore.QString)
+ def closeMemo(self, channel):
+ c = unicode(channel)
+ self.chatlog.finish(c)
+ self.leftChannel.emit(channel)
+ del self.memos[c]
@QtCore.pyqtSlot()
def tabsClosed(self):
del self.tabconvo
@@ -1008,6 +1016,11 @@ class PesterWindow(MovingWindow):
def deliverMemo(self, chan, handle, msg):
(c, h, m) = (unicode(chan), unicode(handle), unicode(msg))
self.newMemoMsg(c,h,m)
+ @QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
+ def timeCommand(self, chan, handle, command):
+ (c, h, cmd) = (unicode(chan), unicode(handle), unicode(command))
+ if self.memos[c]:
+ self.memos[c].timeUpdate(h, cmd)
@QtCore.pyqtSlot(QtCore.QString, PesterList)
def updateNames(self, channel, names):
@@ -1121,12 +1134,13 @@ class PesterWindow(MovingWindow):
def joinSelectedMemo(self):
newmemo = self.memochooser.newmemoname()
selectedmemo = self.memochooser.selectedmemo()
+ time = unicode(self.memochooser.timeinput.text())
if newmemo:
channel = "#"+unicode(newmemo)
- self.newMemo(channel)
+ self.newMemo(channel, time)
elif selectedmemo:
channel = "#"+unicode(selectedmemo.text())
- self.newMemo(channel)
+ self.newMemo(channel, time)
self.memochooser = None
@QtCore.pyqtSlot()
def memoChooserClose(self):
@@ -1208,13 +1222,20 @@ class PesterWindow(MovingWindow):
tabsetting = self.optionmenu.tabcheck.isChecked()
if curtab and not tabsetting:
# split tabs into windows
+ windows = []
if self.tabconvo:
windows = list(self.tabconvo.convos.values())
- for w in windows:
- w.setParent(None)
- w.show()
- w.raiseChat()
+ if self.tabmemo:
+ windows += list(self.tabmemo.convos.values())
+
+ for w in windows:
+ w.setParent(None)
+ w.show()
+ w.raiseChat()
+ if self.tabconvo:
self.tabconvo.closeSoft()
+ if self.tabmemo:
+ self.tabmemo.closeSoft()
# save options
self.config.set("tabs", tabsetting)
elif tabsetting and not curtab:
@@ -1227,6 +1248,14 @@ class PesterWindow(MovingWindow):
self.tabconvo.show()
newconvos[h] = c
self.convos = newconvos
+ newmemos = {}
+ self.createMemoTabWindow()
+ for (h,m) in self.memos.iteritems():
+ m.setParent(self.tabmemo)
+ self.tabmemo.addChat(m)
+ self.tabmemo.show()
+ newmemos[h] = m
+ self.memos = newmemos
# save options
self.config.set("tabs", tabsetting)
self.optionmenu = None
@@ -1366,203 +1395,7 @@ class PesterWindow(MovingWindow):
blockedChum = QtCore.pyqtSignal(QtCore.QString)
unblockedChum = QtCore.pyqtSignal(QtCore.QString)
joinChannel = QtCore.pyqtSignal(QtCore.QString)
-
-class PesterIRC(QtCore.QObject):
- def __init__(self, window):
- QtCore.QObject.__init__(self)
- self.mainwindow = window
- def IRCConnect(self):
- self.cli = IRCClient(PesterHandler, host="irc.tymoon.eu", port=6667, nick=self.mainwindow.profile().handle, blocking=True)
- self.cli.command_handler.parent = self
- self.cli.command_handler.mainwindow = self.mainwindow
- self.conn = self.cli.connect()
-
- @QtCore.pyqtSlot(PesterProfile)
- def getMood(self, *chums):
- self.cli.command_handler.getMood(*chums)
- @QtCore.pyqtSlot(PesterList)
- def getMoods(self, chums):
- self.cli.command_handler.getMood(*chums)
-
- @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
- def sendMessage(self, text, handle):
- h = unicode(handle)
- helpers.msg(self.cli, h, text)
-
- @QtCore.pyqtSlot(QtCore.QString, bool)
- def startConvo(self, handle, initiated):
- h = unicode(handle)
- if initiated:
- helpers.msg(self.cli, h, "PESTERCHUM:BEGIN")
- helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
- @QtCore.pyqtSlot(QtCore.QString)
- def endConvo(self, handle):
- h = unicode(handle)
- helpers.msg(self.cli, h, "PESTERCHUM:CEASE")
- @QtCore.pyqtSlot()
- def updateProfile(self):
- me = self.mainwindow.profile()
- handle = me.handle
- helpers.nick(self.cli, handle)
- self.updateMood()
- @QtCore.pyqtSlot()
- def updateMood(self):
- me = self.mainwindow.profile()
- helpers.msg(self.cli, "#pesterchum", "MOOD >%d" % (me.mood.value()))
- @QtCore.pyqtSlot()
- def updateColor(self):
- me = self.mainwindow.profile()
- for h in self.mainwindow.convos.keys():
- helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()))
- @QtCore.pyqtSlot(QtCore.QString)
- def blockedChum(self, handle):
- h = unicode(handle)
- helpers.msg(self.cli, h, "PESTERCHUM:BLOCK")
- @QtCore.pyqtSlot(QtCore.QString)
- def unblockedChum(self, handle):
- h = unicode(handle)
- helpers.msg(self.cli, h, "PESTERCHUM:UNBLOCK")
- @QtCore.pyqtSlot(QtCore.QString)
- def requestNames(self, channel):
- c = unicode(channel)
- helpers.names(self.cli, c)
- @QtCore.pyqtSlot()
- def requestChannelList(self):
- helpers.channel_list(self.cli)
- @QtCore.pyqtSlot(QtCore.QString)
- def joinChannel(self, channel):
- c = unicode(channel)
- helpers.join(self.cli, c)
- def updateIRC(self):
- self.conn.next()
-
- moodUpdated = QtCore.pyqtSignal(QtCore.QString, Mood)
- colorUpdated = QtCore.pyqtSignal(QtCore.QString, QtGui.QColor)
- messageReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
- memoReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString)
- namesReceived = QtCore.pyqtSignal(QtCore.QString, PesterList)
- channelListReceived = QtCore.pyqtSignal(PesterList)
- nickCollision = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
- connected = QtCore.pyqtSignal()
- userPresentUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString,
- QtCore.QString)
-
-class PesterHandler(DefaultCommandHandler):
- def privmsg(self, nick, chan, msg):
- # display msg, do other stuff
- # silently ignore CTCP
- if msg[0] == '\x01':
- return
- handle = nick[0:nick.find("!")]
- logging.info("---> recv \"PRIVMSG %s :%s\"" % (handle, msg))
- if chan == "#pesterchum":
- # follow instructions
- if msg[0:6] == "MOOD >":
- try:
- mood = Mood(int(msg[6:]))
- except ValueError:
- mood = Mood(0)
- self.parent.moodUpdated.emit(handle, mood)
- elif msg[0:7] == "GETMOOD":
- mychumhandle = self.mainwindow.profile().handle
- mymood = self.mainwindow.profile().mood.value()
- if msg.find(mychumhandle, 8) != -1:
- helpers.msg(self.client, "#pesterchum",
- "MOOD >%d" % (mymood))
- elif chan[0] == '#':
- if msg[0:16] == "PESTERCHUM:TIME>":
- # send time msg
- pass
- else:
- self.parent.memoReceived.emit(chan, handle, msg)
- else:
- # private message
- # silently ignore messages to yourself.
- if handle == self.mainwindow.profile().handle:
- return
- if msg[0:7] == "COLOR >":
- colors = msg[7:].split(",")
- try:
- colors = [int(d) for d in colors]
- except ValueError:
- colors = [0,0,0]
- color = QtGui.QColor(*colors)
- self.parent.colorUpdated.emit(handle, color)
- else:
- self.parent.messageReceived.emit(handle, msg)
-
-
- 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))
- helpers.nick(self.client, newnick)
- self.parent.nickCollision.emit(nick, newnick)
- def quit(self, nick, reason):
- handle = nick[0:nick.find("!")]
- self.parent.userPresentUpdate.emit(handle, "", "quit")
- self.parent.moodUpdated.emit(handle, Mood("offline"))
- 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 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 nick(self, oldnick, newnick):
- oldhandle = oldnick[0:oldnick.find("!")]
- newchum = PesterProfile(newnick, chumdb=self.mainwindow.chumdb)
- self.parent.moodUpdated.emit(oldhandle, Mood("offline"))
- if newnick in self.mainwindow.chumList.chums:
- self.getMood(newchum)
- 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 = []
-
- def getMood(self, *chums):
- chumglub = "GETMOOD "
- for c in chums:
- chandle = c.handle
- if len(chumglub+chandle) >= 350:
- helpers.msg(self.client, "#pesterchum", chumglub)
- chumglub = "GETMOOD "
- chumglub += chandle
- if chumglub != "GETMOOD ":
- helpers.msg(self.client, "#pesterchum", chumglub)
+ leftChannel = QtCore.pyqtSignal(QtCore.QString)
class IRCThread(QtCore.QThread):
def __init__(self, ircobj):
@@ -1662,6 +1495,11 @@ def main():
QtCore.SIGNAL('joinChannel(QString)'),
irc,
QtCore.SLOT('joinChannel(QString)'))
+ irc.connect(widget,
+ QtCore.SIGNAL('leftChannel(QString)'),
+ irc,
+ QtCore.SLOT('leftChannel(QString)'))
+
# IRC --> Main window
irc.connect(irc, QtCore.SIGNAL('connected()'),
@@ -1698,10 +1536,16 @@ def main():
QtCore.SIGNAL('channelListReceived(PyQt_PyObject)'),
widget,
QtCore.SLOT('updateChannelList(PyQt_PyObject)'))
+ irc.connect(irc,
+ QtCore.SIGNAL('timeCommand(QString, QString, QString)'),
+ widget,
+ QtCore.SLOT('timeCommand(QString, QString, QString)'))
ircapp = IRCThread(irc)
ircapp.start()
- widget.loadingscreen.exec_()
+ status = widget.loadingscreen.exec_()
+ if status == QtGui.QDialog.Rejected:
+ sys.exit(0)
sys.exit(app.exec_())
main()
diff --git a/themes/pesterchum/style.js b/themes/pesterchum/style.js
index 466a065..1522af3 100644
--- a/themes/pesterchum/style.js
+++ b/themes/pesterchum/style.js
@@ -208,7 +208,10 @@
"beganpester": "began pestering",
"ceasepester": "ceased pestering",
"blocked": "blocked",
- "unblocked": "unblocked"
+ "unblocked": "unblocked",
+ "openmemo": "opened memo on board",
+ "joinmemo": "responded to memo",
+ "closememo": "ceased responding to memo"
},
"systemMsgColor": "#646464"
},
@@ -231,11 +234,14 @@
"input": { "style": "background: white; border:2px solid #c48a00;margin-top:5px;" },
"textarea": { "style": "background: white; font:bold; border:2px solid #c48a00;text-align:center;" },
"margins": {"top": 0, "bottom": 0, "left": 0, "right": 0 },
- "userlist": { "width": 150 },
+ "userlist": { "width": 150,
+ "style": "border:2px solid yellow; background: white;font: bold;font-family: 'Courier';selection-background-color:#646464; "
+ },
"time": { "text": { "width": 75, "style": "" },
"slider": { "style": "",
"groove": "",
- "handle": "" }
+ "handle": ""
+ }
},
"systemMsgColor": "#646464"
}
diff --git a/themes/trollian/style.js b/themes/trollian/style.js
index bf0d2d8..5eab3b7 100644
--- a/themes/trollian/style.js
+++ b/themes/trollian/style.js
@@ -256,13 +256,45 @@
"beganpester": "began trolling",
"ceasepester": "gave up trolling",
"blocked": "blocked",
- "unblocked": "mercifully forgave"
+ "unblocked": "mercifully forgave",
+ "openmemo": "opened memo on board",
+ "joinmemo": "responded to memo",
+ "closememo": "ceased responding to memo"
},
"systemMsgColor": "#646464"
},
"memos":
- {"memoicon": "$path/memo.png"
-
- }
-
+ {"memoicon": "$path/memo.png",
+ "style": "background: rgb(190, 19, 4); font-family: 'Arial';",
+ "size": [600,300],
+ "tabs": {
+ "style": "",
+ "selectedstyle": "",
+ "newmsgcolor": "red",
+ "tabstyle": 0
+ },
+ "label": { "text": "$channel",
+ "style": "background: rgb(255, 38, 18); color: white; padding: 2px; border:1px solid #c2c2c2;",
+ "align": { "h": "center", "v": "center" },
+ "minheight": 30,
+ "maxheight": 50
+ },
+ "textarea": {
+ "style": "background: white; border:2px solid #c2c2c2; font-size: 12px; margin-top: 4px;"
+ },
+ "input": {
+ "style": "background: white;margin-top:5px; border:1px solid #c2c2c2; margin-right: 54px; font-size: 12px;"
+ },
+ "margins": {"top": 0, "bottom": 0, "left": 0, "right": 0 },
+ "userlist": { "width": 150,
+ "style": "font-size: 12px; background: white; border:2px solid #c2c2c2; padding: 5px; font-family: 'Arial';selection-background-color:rgb(200,200,200);"
+ },
+ "time": { "text": { "width": 75, "style": "" },
+ "slider": { "style": "",
+ "groove": "",
+ "handle": ""
+ }
+ },
+ "systemMsgColor": "#646464"
+ }
}
\ No newline at end of file