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