diff --git a/TODO b/TODO index e07b9ba..eaf639e 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,7 @@ Features: * Chat rooms/browser +* PESTERLOG: in convo window +* help button on quirks menu? * tab recombining gives wrong window icon * help menu -- about and forum * profile switch should say current profile @@ -13,6 +15,7 @@ Features: * tab right-click menu on tabbed convos * comment history (up button) * page up/down scrolling +* get rid of border on chat window? * ctrl-tab should prefer new convos * More complex quirks: random, spelling, by-sound * Implement TC options @@ -28,7 +31,7 @@ Features: * Theme checking * don't clear new message when clicking away from tab? * Spy mode - +* Animated * put code into separate files * hide offline chums * chum list groups diff --git a/convo.py b/convo.py new file mode 100644 index 0000000..8e8b503 --- /dev/null +++ b/convo.py @@ -0,0 +1,438 @@ +from string import Template +import re +from PyQt4 import QtGui, QtCore + +from dataobjs import PesterProfile, Mood +from generic import PesterIcon +from parsetools import escapeBrackets, convertTags + +class PesterTabWindow(QtGui.QFrame): + def __init__(self, mainwindow, parent=None): + QtGui.QFrame.__init__(self, parent) + self.setFocusPolicy(QtCore.Qt.ClickFocus) + self.mainwindow = mainwindow + self.resize(*self.mainwindow.theme["convo/size"]) + self.setStyleSheet(self.mainwindow.theme["convo/style"]) + + self.tabs = QtGui.QTabBar(self) + self.tabs.setTabsClosable(True) + self.connect(self.tabs, QtCore.SIGNAL('currentChanged(int)'), + self, QtCore.SLOT('changeTab(int)')) + self.connect(self.tabs, QtCore.SIGNAL('tabCloseRequested(int)'), + self, QtCore.SLOT('tabClose(int)')) + self.tabs.setShape(self.mainwindow.theme["convo/tabs/tabstyle"]) + self.tabs.setStyleSheet("QTabBar::tab{ %s } QTabBar::tab:selected { %s }" % (self.mainwindow.theme["convo/tabs/style"], self.mainwindow.theme["convo/tabs/selectedstyle"])) + + self.layout = QtGui.QVBoxLayout() + self.layout.setContentsMargins(0,0,0,0) + self.layout.addWidget(self.tabs) + self.setLayout(self.layout) + self.convos = {} + self.tabIndices = {} + self.currentConvo = None + self.changedTab = False + self.softclose = False + + # get default tab color i guess + self.defaultTabTextColor = self.getTabTextColor() + def getTabTextColor(self): + # ugly, ugly hack + self.changedTab = True + i = self.tabs.addTab(".") + c = self.tabs.tabTextColor(i) + self.tabs.removeTab(i) + self.changedTab = False + return c + def addChat(self, convo): + self.convos[convo.chum.handle] = convo + # either addTab or setCurrentIndex will trigger changed() + newindex = self.tabs.addTab(convo.chum.handle) + self.tabIndices[convo.chum.handle] = newindex + self.tabs.setCurrentIndex(newindex) + self.tabs.setTabIcon(newindex, convo.chum.mood.icon(self.mainwindow.theme)) + def showChat(self, handle): + tabi = self.tabIndices[handle] + if self.tabs.currentIndex() == tabi: + self.activateWindow() + self.raise_() + self.convos[handle].raiseChat() + else: + self.tabs.setCurrentIndex(tabi) + + def convoHasFocus(self, convo): + if ((self.hasFocus() or self.tabs.hasFocus()) and + self.tabs.tabText(self.tabs.currentIndex()) == convo.chum.handle): + return True + + def keyPressEvent(self, event): + keypress = event.key() + mods = event.modifiers() + if ((mods & QtCore.Qt.ControlModifier) and + keypress == QtCore.Qt.Key_Tab): + nexti = (self.tabIndices[self.currentConvo.chum.handle] + 1) % self.tabs.count() + self.tabs.setCurrentIndex(nexti) + + def closeSoft(self): + self.softclose = True + self.close() + def updateBlocked(self, handle): + i = self.tabIndices[handle] + icon = QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]) + self.tabs.setTabIcon(i, icon) + if self.tabs.currentIndex() == i: + self.setWindowIcon(icon) + def updateMood(self, handle, mood, unblocked=False): + i = self.tabIndices[handle] + if handle in self.mainwindow.config.getBlocklist() and not unblocked: + icon = QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]) + else: + icon = mood.icon(self.mainwindow.theme) + self.tabs.setTabIcon(i, icon) + if self.tabs.currentIndex() == i: + self.setWindowIcon(icon) + def closeEvent(self, event): + if not self.softclose: + while self.tabs.count() > 0: + self.tabClose(0) + self.windowClosed.emit() + def focusInEvent(self, event): + # make sure we're not switching tabs! + i = self.tabs.tabAt(self.mapFromGlobal(QtGui.QCursor.pos())) + if i == -1: + i = self.tabs.currentIndex() + handle = unicode(self.tabs.tabText(i)) + self.clearNewMessage(handle) + def convoHasFocus(self, handle): + i = self.tabIndices[handle] + if (self.tabs.currentIndex() == i and + (self.hasFocus() or self.tabs.hasFocus())): + return True + else: + return False + + def notifyNewMessage(self, handle): + i = self.tabIndices[handle] + self.tabs.setTabTextColor(i, QtGui.QColor(self.mainwindow.theme["convo/tabs/newmsgcolor"])) + convo = self.convos[handle] + def func(): + convo.showChat() + self.mainwindow.waitingMessages.addMessage(handle, func) + # set system tray + def clearNewMessage(self, handle): + try: + i = self.tabIndices[handle] + self.tabs.setTabTextColor(i, self.defaultTabTextColor) + except KeyError: + pass + self.mainwindow.waitingMessages.messageAnswered(handle) + def changeTheme(self, theme): + self.resize(*theme["convo/size"]) + self.setStyleSheet(theme["convo/style"]) + self.tabs.setShape(theme["convo/tabs/tabstyle"]) + self.tabs.setStyleSheet("QTabBar::tabs{ %s }" % (theme["convo/tabs/style"])) + for c in self.convos.values(): + tabi = self.tabIndices[c.chum.handle] + self.tabs.setTabIcon(tabi, c.chum.mood.icon(theme)) + currenttabi = self.tabs.currentIndex() + if currenttabi >= 0: + currentHandle = unicode(self.tabs.tabText(self.tabs.currentIndex())) + self.setWindowIcon(self.convos[currentHandle].chum.mood.icon(theme)) + self.defaultTabTextColor = self.getTabTextColor() + + @QtCore.pyqtSlot(int) + def tabClose(self, i): + handle = unicode(self.tabs.tabText(i)) + self.mainwindow.waitingMessages.messageAnswered(handle) + convo = self.convos[handle] + del self.convos[handle] + del self.tabIndices[handle] + self.tabs.removeTab(i) + for (h, j) in self.tabIndices.iteritems(): + if j > i: + self.tabIndices[h] = j-1 + self.layout.removeWidget(convo) + convo.close() + if self.tabs.count() == 0: + self.close() + return + if self.currentConvo == convo: + currenti = self.tabs.currentIndex() + currenth = unicode(self.tabs.tabText(currenti)) + self.currentConvo = self.convos[currenth] + self.currentConvo.raiseChat() + + @QtCore.pyqtSlot(int) + def changeTab(self, i): + if i < 0: + return + if self.changedTab: + self.changedTab = False + return + handle = unicode(self.tabs.tabText(i)) + convo = self.convos[handle] + if self.currentConvo: + self.layout.removeWidget(self.currentConvo) + self.currentConvo = convo + self.layout.addWidget(convo) + self.setWindowIcon(convo.chum.mood.icon(self.mainwindow.theme)) + self.setWindowTitle(convo.chum.handle) + self.activateWindow() + self.raise_() + convo.raiseChat() + + windowClosed = QtCore.pyqtSignal() + +class PesterText(QtGui.QTextEdit): + def __init__(self, theme, parent=None): + QtGui.QTextEdit.__init__(self, parent) + self.setStyleSheet(theme["convo/textarea/style"]) + self.setReadOnly(True) + self.setMouseTracking(True) + def addMessage(self, text, chum): + color = chum.colorhtml() + systemColor = QtGui.QColor(self.parent().mainwindow.theme["convo/systemMsgColor"]) + initials = chum.initials() + msg = unicode(text) + parent = self.parent() + window = parent.mainwindow + me = window.profile() + if msg == "PESTERCHUM:BEGIN": + parent.setChumOpen(True) + msg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"]) + window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) + self.append(convertTags(msg)) + elif msg == "PESTERCHUM:CEASE": + parent.setChumOpen(False) + msg = chum.pestermsg(me, systemColor, window.theme["convo/text/ceasepester"]) + window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) + self.append(convertTags(msg)) + elif msg == "PESTERCHUM:BLOCK": + msg = chum.pestermsg(me, systemColor, window.theme['convo/text/blocked']) + window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) + self.append(convertTags(msg)) + elif msg == "PESTERCHUM:UNBLOCK": + msg = chum.pestermsg(me, systemColor, window.theme['convo/text/unblocked']) + window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) + self.append(convertTags(msg)) + elif 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:]) + if chum is me: + window.chatlog.log(parent.chum.handle, convertTags(msg, "bbcode")) + else: + window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) + self.append(convertTags(msg)) + else: + if not parent.chumopen and chum is not me: + beginmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"]) + parent.setChumOpen(True) + window.chatlog.log(chum.handle, convertTags(beginmsg, "bbcode")) + self.append(convertTags(beginmsg)) + + msg = "%s: %s" % (color, initials, msg) + msg = escapeBrackets(msg) + self.append(convertTags(msg)) + if chum is me: + window.chatlog.log(parent.chum.handle, convertTags(msg, "bbcode")) + else: + window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) + def changeTheme(self, theme): + self.setStyleSheet(theme["convo/textarea/style"]) + sb = self.verticalScrollBar() + sb.setMaximum(sb.maximum()+1000) # ugly hack but whatcha gonna do + sb.setValue(sb.maximum()) + def focusInEvent(self, event): + self.parent().clearNewMessage() + QtGui.QTextEdit.focusInEvent(self, event) + + def mousePressEvent(self, event): + url = self.anchorAt(event.pos()) + if url != "": + QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.TolerantMode)) + QtGui.QTextEdit.mousePressEvent(self, event) + def mouseMoveEvent(self, event): + QtGui.QTextEdit.mouseMoveEvent(self, event) + if self.anchorAt(event.pos()): + if self.viewport().cursor().shape != QtCore.Qt.PointingHandCursor: + self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) + else: + self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor)) + +class PesterInput(QtGui.QLineEdit): + def __init__(self, theme, parent=None): + QtGui.QLineEdit.__init__(self, parent) + self.setStyleSheet(theme["convo/input/style"]) + def changeTheme(self, theme): + self.setStyleSheet(theme["convo/input/style"]) + def focusInEvent(self, event): + self.parent().clearNewMessage() + QtGui.QLineEdit.focusInEvent(self, event) + +class PesterConvo(QtGui.QFrame): + def __init__(self, chum, initiated, mainwindow, parent=None): + QtGui.QFrame.__init__(self, parent) + self.setFocusPolicy(QtCore.Qt.ClickFocus) + self.chum = chum + self.mainwindow = mainwindow + convo = self.mainwindow.theme["convo"] + self.resize(*convo["size"]) + self.setStyleSheet(convo["style"]) + self.setWindowIcon(chum.mood.icon(self.mainwindow.theme)) + self.setWindowTitle(chum.handle) + + t = Template(self.mainwindow.theme["convo/chumlabel/text"]) + + self.chumLabel = QtGui.QLabel(t.safe_substitute(handle=chum.handle), self) + self.chumLabel.setStyleSheet(self.mainwindow.theme["convo/chumlabel/style"]) + self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]]) + self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"]) + self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"]) + self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding)) + self.textArea = PesterText(self.mainwindow.theme, self) + self.textInput = PesterInput(self.mainwindow.theme, self) + self.textInput.setFocus() + + self.connect(self.textInput, QtCore.SIGNAL('returnPressed()'), + self, QtCore.SLOT('sentMessage()')) + + self.layout = QtGui.QVBoxLayout() + self.layout.addWidget(self.chumLabel) + self.layout.addWidget(self.textArea) + self.layout.addWidget(self.textInput) + self.layout.setSpacing(0) + margins = self.mainwindow.theme["convo/margins"] + self.layout.setContentsMargins(margins["left"], margins["top"], + margins["right"], margins["bottom"]) + + self.setLayout(self.layout) + + self.chumopen = False + + if parent: + parent.addChat(self) + if initiated: + msg = self.mainwindow.profile().pestermsg(self.chum, QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), self.mainwindow.theme["convo/text/beganpester"]) + self.setChumOpen(True) + self.textArea.append(convertTags(msg)) + self.mainwindow.chatlog.log(self.chum.handle, convertTags(msg, "bbcode")) + self.newmessage = False + + def updateMood(self, mood, unblocked=False): + if mood.name() == "offline" and self.chumopen == True and not unblocked: + msg = self.chum.pestermsg(self.mainwindow.profile(), QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), self.mainwindow.theme["convo/text/ceasepester"]) + self.textArea.append(convertTags(msg)) + self.mainwindow.chatlog.log(self.chum.handle, convertTags(msg, "bbcode")) + self.chumopen = False + if self.parent(): + self.parent().updateMood(self.chum.handle, mood, unblocked) + else: + if self.chum.blocked(self.mainwindow.config) and not unblocked: + self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])) + else: + self.setWindowIcon(mood.icon(self.mainwindow.theme)) + # print mood update? + def updateBlocked(self): + if self.parent(): + self.parent().updateBlocked(self.chum.handle) + else: + self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])) + def updateColor(self, color): + self.chum.color = color + def addMessage(self, text, me=True): + if me: + chum = self.mainwindow.profile() + else: + chum = self.chum + self.notifyNewMessage() + self.textArea.addMessage(text, chum) + + def notifyNewMessage(self): + # first see if this conversation HASS the focus + if not (self.hasFocus() or self.textArea.hasFocus() or + self.textInput.hasFocus() or + (self.parent() and self.parent().convoHasFocus(self.chum.handle))): + # ok if it has a tabconvo parent, send that the notify. + if self.parent(): + self.parent().notifyNewMessage(self.chum.handle) + # if not change the window title and update system tray + else: + self.newmessage = True + self.setWindowTitle(self.chum.handle+"*") + def func(): + self.showChat() + self.mainwindow.waitingMessages.addMessage(self.chum.handle, func) + + def clearNewMessage(self): + if self.parent(): + self.parent().clearNewMessage(self.chum.handle) + elif self.newmessage: + self.newmessage = False + self.setWindowTitle(self.chum.handle) + self.mainwindow.waitingMessages.messageAnswered(self.chum.handle) + # reset system tray + def focusInEvent(self, event): + self.clearNewMessage() + self.textInput.setFocus() + def raiseChat(self): + self.activateWindow() + self.raise_() + self.textInput.setFocus() + + def showChat(self): + if self.parent(): + self.parent().showChat(self.chum.handle) + self.raiseChat() + + def closeEvent(self, event): + self.mainwindow.waitingMessages.messageAnswered(self.chum.handle) + self.windowClosed.emit(self.chum.handle) + def setChumOpen(self, o): + self.chumopen = o + def changeTheme(self, theme): + self.resize(*theme["convo/size"]) + self.setStyleSheet(theme["convo/style"]) + margins = theme["convo/margins"] + self.layout.setContentsMargins(margins["left"], margins["top"], + margins["right"], margins["bottom"]) + + self.setWindowIcon(self.chum.mood.icon(theme)) + t = Template(self.mainwindow.theme["convo/chumlabel/text"]) + self.chumLabel.setText(t.safe_substitute(handle=self.chum.handle)) + self.chumLabel.setStyleSheet(theme["convo/chumlabel/style"]) + self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]]) + self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"]) + self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"]) + self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding)) + self.textArea.changeTheme(theme) + self.textInput.changeTheme(theme) + + @QtCore.pyqtSlot() + def sentMessage(self): + text = self.textInput.text() + if text == "": + return + # deal with quirks here + qtext = self.mainwindow.userprofile.quirks.apply(unicode(text)) + text = QtCore.QString(qtext) + self.textInput.setText("") + self.addMessage(text, True) + # if ceased, rebegin + if not self.chumopen: + self.mainwindow.newConvoStarted.emit(QtCore.QString(self.chum.handle), True) + # convert color tags + text = convertTags(unicode(text), "ctag") + self.messageSent.emit(text, self.chum) + + messageSent = QtCore.pyqtSignal(QtCore.QString, PesterProfile) + windowClosed = QtCore.pyqtSignal(QtCore.QString) + + aligndict = {"h": {"center": QtCore.Qt.AlignHCenter, + "left": QtCore.Qt.AlignLeft, + "right": QtCore.Qt.AlignRight }, + "v": {"center": QtCore.Qt.AlignVCenter, + "top": QtCore.Qt.AlignTop, + "bottom": QtCore.Qt.AlignBottom } } diff --git a/convo.pyc b/convo.pyc new file mode 100644 index 0000000..6ea4496 Binary files /dev/null and b/convo.pyc differ diff --git a/pesterdata.py b/dataobjs.py similarity index 89% rename from pesterdata.py rename to dataobjs.py index 34b5197..4d19dbb 100644 --- a/pesterdata.py +++ b/dataobjs.py @@ -6,9 +6,10 @@ from generic import PesterIcon class Mood(object): moods = ["chummy", "rancorous", "offline", "pleasant", "distraught", - "unruly", "smooth", "ecstatic", "relaxed", "discontent", + "pranky", "smooth", "ecstatic", "relaxed", "discontent", "devious", "sleek", "detestful", "mirthful", "manipulative", - "vigorous", "perky", "acceptant", "protective"] + "vigorous", "perky", "acceptant", "protective", "mystified", + "amazed", "insolent", "bemused" ] def __init__(self, mood): if type(mood) is int: self.mood = mood @@ -66,14 +67,23 @@ class pesterQuirks(object): def plainList(self): return [q.quirk for q in self.quirklist] def apply(self, string): + # don't quirk /me commands + if string[0:3] == "/me": + space = string.find(" ") + cmd = string[0:space] + string = string[space:] + else: + cmd = "" presuffix = [q for q in self.quirklist if q.type=='prefix' or q.type=='suffix'] replace = [q for q in self.quirklist if q.type=='replace' or q.type=='regexp'] for r in replace: string = r.apply(string) - for ps in presuffix: - string = ps.apply(string) + if not cmd: + for ps in presuffix: + string = ps.apply(string) + string = cmd+string return string def __iter__(self): diff --git a/dataobjs.pyc b/dataobjs.pyc new file mode 100644 index 0000000..38eda05 Binary files /dev/null and b/dataobjs.pyc differ diff --git a/logs/chums.js b/logs/chums.js index a91102f..7c544c4 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"}, "aquaticMarinist": {"color": "#00caca", "handle": "aquaticMarinist", "mood": "offline"}, "marineAquist": {"color": "#00caca", "handle": "marineAquist", "mood": "offline"}, "captainCaveman": {"color": "#7c414e", "handle": "captainCaveman", "mood": "offline"}, "gamblingGenocider": {"color": "#00ff00", "handle": "gamblingGenocider", "mood": "offline"}, "mechanicalSpectacle": {"color": "#0000ff", "handle": "mechanicalSpectacle", "mood": "offline"}, "absoluteTranquility": {"color": "#000033", "handle": "absoluteTranquility", "mood": "offline"}, "centaursTesticle": {"color": "#000056", "handle": "centaursTesticle", "mood": "offline"}, "gardenGnostic": {"color": "#00ff00", "handle": "gardenGnostic", "mood": "offline"}, "unknownTraveler": {"color": "#006666", "handle": "unknownTraveler", "mood": "offline"}, "schlagzeugGator": {"color": "#61821f", "handle": "schlagzeugGator", "mood": "offline"}} \ No newline at end of file +{"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 diff --git a/memos.py b/memos.py new file mode 100644 index 0000000..0aa0a4a --- /dev/null +++ b/memos.py @@ -0,0 +1,125 @@ +from string import Template +import re +from PyQt4 import QtGui, QtCore + +from dataobjs import PesterProfile, Mood +from generic import PesterIcon +from convo import PesterConvo, PesterInput, PesterText + +class MemoText(PesterText): + def __init__(self, theme, parent=None): + QtGui.QTextEdit.__init__(self, parent) + self.setStyleSheet(theme["memos/textarea/style"]) + self.setReadOnly(True) + self.setMouseTracking(True) + def addMessage(self, text, chum): + pass + def changeTheme(self): + pass + +class MemoInput(PesterInput): + def __init__(self, theme, parent=None): + QtGui.QLineEdit.__init__(self, parent) + self.setStyleSheet(theme["memos/input/style"]) + def changeTheme(self, theme): + self.setStyleSheet(theme["memos/input/style"]) + +class PesterMemo(PesterConvo): + def __init__(self, channel, mainwindow, parent=None): + QtGui.QFrame.__init__(self, parent) + self.channel = channel + self.mainwindow = mainwindow + self.setWindowTitle(channel) + self.channelLabel = QtGui.QLabel(self) + self.channelLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding)) + + self.textArea = MemoText(self.mainwindow.theme, self) + self.textInput = MemoInput(self.mainwindow.theme, self) + self.textInput.setFocus() + + 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.initTheme(self.mainwindow.theme) + + # connect + 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) + + self.layout = QtGui.QHBoxLayout() + self.setLayout(self.layout) + + self.layout.addLayout(layout_0) + self.layout.addWidget(self.userlist) + self.layout.addLayout(layout_1) + self.layout.setSpacing(0) + margins = self.mainwindow.theme["memos/margins"] + self.layout.setContentsMargins(margins["left"], margins["top"], + margins["right"], margins["bottom"]) + + #if parent: + # parent.addChat(self) + self.newmessage = False + + def updateMood(self): + pass + def updateBlocked(self): + pass + def updateColor(self): + pass + def addMessage(self): + pass + def notifyNewMessage(self): + pass + def clearNewMessage(self): + pass + + def initTheme(self, theme): + memo = theme["memos"] + self.resize(*memo["size"]) + self.setStyleSheet(memo["style"]) + self.setWindowIcon(PesterIcon(theme["memos/memoicon"])) + + t = Template(theme["memos/label/text"]) + self.channelLabel.setText(t.safe_substitute(channel=self.channel)) + self.channelLabel.setStyleSheet(theme["memos/label/style"]) + self.channelLabel.setAlignment(self.aligndict["h"][theme["memos/label/align/h"]] | self.aligndict["v"][theme["memos/label/align/v"]]) + self.channelLabel.setMaximumHeight(theme["memos/label/maxheight"]) + self.channelLabel.setMinimumHeight(theme["memos/label/minheight"]) + + self.userlist.setStyleSheet(theme["main/chums/style"]) + self.userlist.setFixedWidth(theme["memos/userlist/width"]) + + self.timeinput.setFixedWidth(theme["memos/time/text/width"]) + self.timeinput.setStyleSheet(theme["memos/time/text/style"]) + slidercss = "QSlider { %s } QSlider::groove { %s } QSlider::handle { %s }" % (theme["memos/time/slider/style"], theme["memos/time/slider/groove"], theme["memos/time/slider/handle"]) + self.timeslider.setStyleSheet(slidercss) + + + def changeTheme(self, theme): + self.initTheme(theme) + self.textArea.changeTheme(theme) + self.textInput.changeTheme(theme) + + def sentMessage(self): + pass + + def closeEvent(self, event): + self.mainwindow.waitingMessages.messageAnswered(self.channel) +# self.windowClosed.emit(self.chum.handle) + + +# messageSent - signal -> sendMessage -> sendMessage(Memo) +# windowClosed - signal -> closeMemo + +# self.textInput +# self.textArea diff --git a/memos.pyc b/memos.pyc new file mode 100644 index 0000000..4107ccc Binary files /dev/null and b/memos.pyc differ diff --git a/pestermenus.py b/menus.py similarity index 86% rename from pestermenus.py rename to menus.py index 557d577..c93c286 100644 --- a/pestermenus.py +++ b/menus.py @@ -376,7 +376,6 @@ class PesterUserlist(QtGui.QDialog): self.userarea.setStyleSheet(theme["main/chums/style"]) self.addChumAction.setText(theme["main/menus/rclickchumlist/addchum"]) for item in [self.userarea.item(i) for i in range(0, self.userarea.count())]: - print item.text() item.setTextColor(QtGui.QColor(theme["main/chums/userlistcolor"])) @QtCore.pyqtSlot() @@ -388,3 +387,66 @@ class PesterUserlist(QtGui.QDialog): addChum = QtCore.pyqtSignal(QtCore.QString) +class PesterMemoList(QtGui.QDialog): + def __init__(self, parent): + QtGui.QDialog.__init__(self, parent) + self.setModal(False) + self.theme = parent.theme + self.mainwindow = parent + self.setStyleSheet(self.theme["main/defaultwindow/style"]) + self.resize(200, 300) + + self.label = QtGui.QLabel("MEMOS") + self.channelarea = RightClickList(self) + self.channelarea.setStyleSheet(self.theme["main/chums/style"]) + self.channelarea.optionsMenu = QtGui.QMenu(self) + self.connect(self.channelarea, + QtCore.SIGNAL('itemActivated(QListWidgetItem *)'), + self, QtCore.SLOT('joinActivatedMemo(QListWidgetItem *)')) + + self.orjoinlabel = QtGui.QLabel("OR MAKE A NEW MEMO:") + self.newmemo = QtGui.QLineEdit(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()')) + layout_ok = QtGui.QHBoxLayout() + layout_ok.addWidget(self.cancel) + layout_ok.addWidget(self.join) + + layout_0 = QtGui.QVBoxLayout() + layout_0.addWidget(self.label) + layout_0.addWidget(self.channelarea) + layout_0.addWidget(self.orjoinlabel) + layout_0.addWidget(self.newmemo) + layout_0.addLayout(layout_ok) + + self.setLayout(layout_0) + + def newmemoname(self): + return self.newmemo.text() + def selectedmemo(self): + return self.channelarea.currentItem() + + def updateChannels(self, channels): + for c in channels: + item = QtGui.QListWidgetItem(c[1:]) + item.setTextColor(QtGui.QColor(self.theme["main/chums/userlistcolor"])) + item.setIcon(QtGui.QIcon(self.theme["memos/memoicon"])) + self.channelarea.addItem(item) + + def updateTheme(self, theme): + self.theme = theme + self.setStyleSheet(theme["main/defaultwindow/style"]) + for item in [self.userarea.item(i) for i in range(0, self.channelarea.count())]: + item.setTextColor(QtGui.QColor(theme["main/chums/userlistcolor"])) + item.setIcon(QtGui.QIcon(theme["memos/memoicon"])) + + @QtCore.pyqtSlot(QtGui.QListWidgetItem) + def joinActivatedMemo(self, item): + self.channelarea.setCurrentItem(item) + self.accept() diff --git a/menus.pyc b/menus.pyc new file mode 100644 index 0000000..9196adf Binary files /dev/null and b/menus.pyc differ diff --git a/oyoyo/helpers.py b/oyoyo/helpers.py index 48c15db..081e94e 100644 --- a/oyoyo/helpers.py +++ b/oyoyo/helpers.py @@ -35,6 +35,9 @@ def names(cli, *channels): if len(msglist) > 0: cli.send("NAMES %s" % (",".join(msglist))) +def channel_list(cli): + cli.send("LIST") + def msgrandom(cli, choices, dest, user=None): o = "%s: " % user if user else "" o += random.choice(choices) diff --git a/oyoyo/helpers.pyc b/oyoyo/helpers.pyc index b75c0b0..16a01a9 100644 Binary files a/oyoyo/helpers.pyc and b/oyoyo/helpers.pyc differ diff --git a/parsetools.py b/parsetools.py new file mode 100644 index 0000000..bceaba2 --- /dev/null +++ b/parsetools.py @@ -0,0 +1,89 @@ +import re +from PyQt4 import QtGui + +_ctag_begin = re.compile(r'') +_ctag_rgb = re.compile(r'\d+,\d+,\d+') +_urlre = re.compile(r"(?i)(http://[^\s<]+)") + +def convertTags(string, format="html"): + if format not in ["html", "bbcode", "ctag"]: + raise ValueError("Color format not recognized") + def colorrepfunc(matchobj): + color = matchobj.group(1) + if _ctag_rgb.match(color) is not None: + if format=='ctag': + return "" + try: + qc = QtGui.QColor(*[int(c) for c in color.split(",")]) + except ValueError: + qc = QtGui.QColor("black") + else: + qc = QtGui.QColor(color) + if not qc.isValid(): + qc = QtGui.QColor("black") + if format == "html": + return '' % (qc.name()) + elif format == "bbcode": + return '[color=%s]' % (qc.name()) + elif format == "ctag": + (r,g,b,a) = qc.getRgb() + return '' % (r,g,b) + string = _ctag_begin.sub(colorrepfunc, string) + endtag = {"html": "", "bbcode": "[/color]", "ctag": ""} + string = string.replace("", endtag[format]) + def urlrep(matchobj): + if format=="html": + return "%s" % (matchobj.group(1).replace("&", "&"), matchobj.group(1)) + elif format=="bbcode": + return "[url]%s[/url]" % (matchobj.group(1).replace("&", "&")) + elif format=="ctag": + return matchobj.group(1) + string = _urlre.sub(urlrep, string) + return string + +def escapeBrackets(string): + class beginTag(object): + def __init__(self, tag): + self.tag = tag + class endTag(object): + pass + newlist = [] + begintagpos = [(m.start(), m.end()) for m in _ctag_begin.finditer(string)] + lasti = 0 + for (s, e) in begintagpos: + newlist.append(string[lasti:s]) + newlist.append(beginTag(string[s:e])) + lasti = e + if lasti < len(string): + newlist.append(string[lasti:]) + tmp = [] + for o in newlist: + if type(o) is not beginTag: + l = o.split("") + tmp.append(l[0]) + l = l[1:] + for item in l: + tmp.append(endTag()) + tmp.append(item) + else: + tmp.append(o) + btlen = 0 + etlen = 0 + retval = "" + newlist = tmp + for o in newlist: + if type(o) is beginTag: + retval += o.tag.replace("&", "&") + btlen +=1 + elif type(o) is endTag: + if etlen >= btlen: + continue + else: + retval += "" + etlen += 1 + else: + retval += o.replace("&", "&").replace("<", "<").replace(">", ">") + if btlen > etlen: + for i in range(0, btlen-etlen): + retval += "" + return retval diff --git a/parsetools.pyc b/parsetools.pyc new file mode 100644 index 0000000..11c33a8 Binary files /dev/null and b/parsetools.pyc differ diff --git a/pesterchum.js b/pesterchum.js index 0f7a8d6..4114dfc 100644 --- a/pesterchum.js +++ b/pesterchum.js @@ -1 +1 @@ -{"tabs": true, "chums": ["aquaMarinist", "marineAquist", "unknownTraveler", "tentacleTherapist", "macruralAlchemist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "fireSwallow", "grimAuxiliatrix"], "defaultprofile": "ghostDunk", "block": []} \ No newline at end of file +{"tabs": false, "chums": ["aquaMarinist", "marineAquist", "unknownTraveler", "tentacleTherapist", "macruralAlchemist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "fireSwallow", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr"], "defaultprofile": "testProfile", "block": []} \ No newline at end of file diff --git a/pesterchum.py b/pesterchum.py index dfb868c..f0ab366 100644 --- a/pesterchum.py +++ b/pesterchum.py @@ -13,95 +13,16 @@ import re from PyQt4 import QtGui, QtCore import pygame -from pestermenus import PesterChooseQuirks, PesterChooseTheme, \ - PesterChooseProfile, PesterOptions, PesterUserlist -from pesterdata import PesterProfile, Mood, pesterQuirk, pesterQuirks +from menus import PesterChooseQuirks, PesterChooseTheme, \ + PesterChooseProfile, PesterOptions, PesterUserlist, PesterMemoList +from dataobjs import PesterProfile, Mood, pesterQuirk, pesterQuirks from generic import PesterIcon, RightClickList, MultiTextDialog +from convo import PesterTabWindow, PesterText, PesterInput, PesterConvo +from parsetools import convertTags +from memos import PesterMemo logging.basicConfig(level=logging.INFO) -_ctag_begin = re.compile(r'') -_ctag_rgb = re.compile(r'\d+,\d+,\d+') -_urlre = re.compile(r"(?i)(http://[^\s<]+)") - -def convertTags(string, format="html"): - if format not in ["html", "bbcode", "ctag"]: - raise ValueError("Color format not recognized") - def colorrepfunc(matchobj): - color = matchobj.group(1) - if _ctag_rgb.match(color) is not None: - if format=='ctag': - return "" - try: - qc = QtGui.QColor(*[int(c) for c in color.split(",")]) - except ValueError: - qc = QtGui.QColor("black") - else: - qc = QtGui.QColor(color) - if not qc.isValid(): - qc = QtGui.QColor("black") - if format == "html": - return '' % (qc.name()) - elif format == "bbcode": - return '[color=%s]' % (qc.name()) - elif format == "ctag": - (r,g,b,a) = qc.getRgb() - return '' % (r,g,b) - string = _ctag_begin.sub(colorrepfunc, string) - endtag = {"html": "", "bbcode": "[/color]", "ctag": ""} - string = string.replace("", endtag[format]) - urlrep = {"html": r"\1", - "bbcode": r"[url]\1[/url]", - "ctag": r"\1" } - string = _urlre.sub(urlrep[format], string) - return string - -def escapeBrackets(string): - class beginTag(object): - def __init__(self, tag): - self.tag = tag - class endTag(object): - pass - newlist = [] - begintagpos = [(m.start(), m.end()) for m in _ctag_begin.finditer(string)] - lasti = 0 - for (s, e) in begintagpos: - newlist.append(string[lasti:s]) - newlist.append(beginTag(string[s:e])) - lasti = e - if lasti < len(string): - newlist.append(string[lasti:]) - tmp = [] - for o in newlist: - if type(o) is not beginTag: - l = o.split("") - tmp.append(l[0]) - l = l[1:] - for item in l: - tmp.append(endTag()) - tmp.append(item) - else: - tmp.append(o) - btlen = 0 - etlen = 0 - retval = "" - newlist = tmp - for o in newlist: - if type(o) is beginTag: - retval += o.tag.replace("&", "&") - btlen +=1 - elif type(o) is endTag: - if etlen >= btlen: - continue - else: - retval += "" - etlen += 1 - else: - retval += o.replace("&", "&").replace("<", "<").replace(">", ">") - if btlen > etlen: - for i in range(0, btlen-etlen): - retval += "" - return retval class waitingMessageHolder(object): def __init__(self, mainwindow, **msgfuncs): @@ -140,7 +61,7 @@ class PesterLog(object): self.convos = {} def log(self, handle, msg): if not self.convos.has_key(handle): - time = datetime.now().strftime("%Y-%m-%d.%H:%M") + time = datetime.now().strftime("%Y-%m-%d.%H.%M.txt") if not os.path.exists("logs/%s/%s" % (self.handle, handle)): os.mkdir("logs/%s/%s" % (self.handle, handle)) fp = open("logs/%s/%s/%s.%s" % (self.handle, handle, handle, time), 'a') @@ -652,422 +573,6 @@ class MovingWindow(QtGui.QFrame): self.update() self.moving = None -class PesterTabWindow(QtGui.QFrame): - def __init__(self, mainwindow, parent=None): - QtGui.QFrame.__init__(self, parent) - self.setFocusPolicy(QtCore.Qt.ClickFocus) - self.mainwindow = mainwindow - self.resize(*self.mainwindow.theme["convo/size"]) - self.setStyleSheet(self.mainwindow.theme["convo/style"]) - - self.tabs = QtGui.QTabBar(self) - self.tabs.setTabsClosable(True) - self.connect(self.tabs, QtCore.SIGNAL('currentChanged(int)'), - self, QtCore.SLOT('changeTab(int)')) - self.connect(self.tabs, QtCore.SIGNAL('tabCloseRequested(int)'), - self, QtCore.SLOT('tabClose(int)')) - self.tabs.setShape(self.mainwindow.theme["convo/tabs/tabstyle"]) - self.tabs.setStyleSheet("QTabBar::tab{ %s } QTabBar::tab:selected { %s }" % (self.mainwindow.theme["convo/tabs/style"], self.mainwindow.theme["convo/tabs/selectedstyle"])) - - self.layout = QtGui.QVBoxLayout() - self.layout.setContentsMargins(0,0,0,0) - self.layout.addWidget(self.tabs) - self.setLayout(self.layout) - self.convos = {} - self.tabIndices = {} - self.currentConvo = None - self.changedTab = False - self.softclose = False - - # get default tab color i guess - self.defaultTabTextColor = self.getTabTextColor() - def getTabTextColor(self): - # ugly, ugly hack - self.changedTab = True - i = self.tabs.addTab(".") - c = self.tabs.tabTextColor(i) - self.tabs.removeTab(i) - self.changedTab = False - return c - def addChat(self, convo): - self.convos[convo.chum.handle] = convo - # either addTab or setCurrentIndex will trigger changed() - newindex = self.tabs.addTab(convo.chum.handle) - self.tabIndices[convo.chum.handle] = newindex - self.tabs.setCurrentIndex(newindex) - self.tabs.setTabIcon(newindex, convo.chum.mood.icon(self.mainwindow.theme)) - def showChat(self, handle): - tabi = self.tabIndices[handle] - if self.tabs.currentIndex() == tabi: - self.activateWindow() - self.raise_() - self.convos[handle].raiseChat() - else: - self.tabs.setCurrentIndex(tabi) - - def convoHasFocus(self, convo): - if ((self.hasFocus() or self.tabs.hasFocus()) and - self.tabs.tabText(self.tabs.currentIndex()) == convo.chum.handle): - return True - - def keyPressEvent(self, event): - keypress = event.key() - mods = event.modifiers() - if ((mods & QtCore.Qt.ControlModifier) and - keypress == QtCore.Qt.Key_Tab): - nexti = (self.tabIndices[self.currentConvo.chum.handle] + 1) % self.tabs.count() - self.tabs.setCurrentIndex(nexti) - - def closeSoft(self): - self.softclose = True - self.close() - def updateBlocked(self, handle): - i = self.tabIndices[handle] - icon = QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]) - self.tabs.setTabIcon(i, icon) - if self.tabs.currentIndex() == i: - self.setWindowIcon(icon) - def updateMood(self, handle, mood, unblocked=False): - i = self.tabIndices[handle] - if handle in self.mainwindow.config.getBlocklist() and not unblocked: - icon = QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]) - else: - icon = mood.icon(self.mainwindow.theme) - self.tabs.setTabIcon(i, icon) - if self.tabs.currentIndex() == i: - self.setWindowIcon(icon) - def closeEvent(self, event): - if not self.softclose: - while self.tabs.count() > 0: - self.tabClose(0) - self.windowClosed.emit() - def focusInEvent(self, event): - # make sure we're not switching tabs! - i = self.tabs.tabAt(self.mapFromGlobal(QtGui.QCursor.pos())) - if i == -1: - i = self.tabs.currentIndex() - handle = unicode(self.tabs.tabText(i)) - self.clearNewMessage(handle) - def convoHasFocus(self, handle): - i = self.tabIndices[handle] - if (self.tabs.currentIndex() == i and - (self.hasFocus() or self.tabs.hasFocus())): - return True - else: - return False - - def notifyNewMessage(self, handle): - i = self.tabIndices[handle] - self.tabs.setTabTextColor(i, QtGui.QColor(self.mainwindow.theme["convo/tabs/newmsgcolor"])) - convo = self.convos[handle] - def func(): - convo.showChat() - self.mainwindow.waitingMessages.addMessage(handle, func) - # set system tray - def clearNewMessage(self, handle): - try: - i = self.tabIndices[handle] - except KeyError: - pass - self.tabs.setTabTextColor(i, self.defaultTabTextColor) - self.mainwindow.waitingMessages.messageAnswered(handle) - def changeTheme(self, theme): - self.resize(*theme["convo/size"]) - self.setStyleSheet(theme["convo/style"]) - self.tabs.setShape(theme["convo/tabs/tabstyle"]) - self.tabs.setStyleSheet("QTabBar::tabs{ %s }" % (theme["convo/tabs/style"])) - for c in self.convos.values(): - tabi = self.tabIndices[c.chum.handle] - self.tabs.setTabIcon(tabi, c.chum.mood.icon(theme)) - currenttabi = self.tabs.currentIndex() - if currenttabi >= 0: - currentHandle = unicode(self.tabs.tabText(self.tabs.currentIndex())) - self.setWindowIcon(self.convos[currentHandle].chum.mood.icon(theme)) - self.defaultTabTextColor = self.getTabTextColor() - - @QtCore.pyqtSlot(int) - def tabClose(self, i): - handle = unicode(self.tabs.tabText(i)) - self.mainwindow.waitingMessages.messageAnswered(handle) - convo = self.convos[handle] - del self.convos[handle] - del self.tabIndices[handle] - self.tabs.removeTab(i) - for (h, j) in self.tabIndices.iteritems(): - if j > i: - self.tabIndices[h] = j-1 - self.layout.removeWidget(convo) - convo.close() - if self.tabs.count() == 0: - self.close() - return - if self.currentConvo == convo: - currenti = self.tabs.currentIndex() - currenth = unicode(self.tabs.tabText(currenti)) - self.currentConvo = self.convos[currenth] - self.currentConvo.raiseChat() - - @QtCore.pyqtSlot(int) - def changeTab(self, i): - if i < 0: - return - if self.changedTab: - self.changedTab = False - return - handle = unicode(self.tabs.tabText(i)) - convo = self.convos[handle] - if self.currentConvo: - self.layout.removeWidget(self.currentConvo) - self.currentConvo = convo - self.layout.addWidget(convo) - self.setWindowIcon(convo.chum.mood.icon(self.mainwindow.theme)) - self.setWindowTitle(convo.chum.handle) - self.activateWindow() - self.raise_() - convo.raiseChat() - - windowClosed = QtCore.pyqtSignal() - -class PesterText(QtGui.QTextEdit): - def __init__(self, theme, parent=None): - QtGui.QTextEdit.__init__(self, parent) - self.setStyleSheet(theme["convo/textarea/style"]) - self.setReadOnly(True) - def addMessage(self, text, chum): - color = chum.colorhtml() - systemColor = QtGui.QColor(self.parent().mainwindow.theme["convo/systemMsgColor"]) - initials = chum.initials() - msg = unicode(text) - parent = self.parent() - window = parent.mainwindow - me = window.profile() - if msg == "PESTERCHUM:BEGIN": - parent.setChumOpen(True) - msg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"]) - window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) - self.append(convertTags(msg)) - elif msg == "PESTERCHUM:CEASE": - parent.setChumOpen(False) - msg = chum.pestermsg(me, systemColor, window.theme["convo/text/ceasepester"]) - window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) - self.append(convertTags(msg)) - elif msg == "PESTERCHUM:BLOCK": - msg = chum.pestermsg(me, systemColor, window.theme['convo/text/blocked']) - window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) - self.append(convertTags(msg)) - elif msg == "PESTERCHUM:UNBLOCK": - msg = chum.pestermsg(me, systemColor, window.theme['convo/text/unblocked']) - window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) - self.append(convertTags(msg)) - elif 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:]) - if chum is me: - window.chatlog.log(parent.chum.handle, convertTags(msg, "bbcode")) - else: - window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) - self.append(convertTags(msg)) - else: - if not parent.chumopen and chum is not me: - beginmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"]) - parent.setChumOpen(True) - window.chatlog.log(chum.handle, convertTags(beginmsg, "bbcode")) - self.append(convertTags(beginmsg)) - - msg = "%s: %s" % (color, initials, msg) - msg = escapeBrackets(msg) - self.append(convertTags(msg)) - if chum is me: - window.chatlog.log(parent.chum.handle, convertTags(msg, "bbcode")) - else: - window.chatlog.log(chum.handle, convertTags(msg, "bbcode")) - def changeTheme(self, theme): - self.setStyleSheet(theme["convo/textarea/style"]) - sb = self.verticalScrollBar() - sb.setMaximum(sb.maximum()+1000) # ugly hack but whatcha gonna do - sb.setValue(sb.maximum()) - def focusInEvent(self, event): - self.parent().clearNewMessage() - QtGui.QTextEdit.focusInEvent(self, event) - - def mousePressEvent(self, event): - url = self.anchorAt(event.pos()) - if url != "": - QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.TolerantMode)) - QtGui.QTextEdit.mousePressEvent(self, event) - -class PesterInput(QtGui.QLineEdit): - def __init__(self, theme, parent=None): - QtGui.QLineEdit.__init__(self, parent) - self.setStyleSheet(theme["convo/input/style"]) - def changeTheme(self, theme): - self.setStyleSheet(theme["convo/input/style"]) - def focusInEvent(self, event): - self.parent().clearNewMessage() - QtGui.QLineEdit.focusInEvent(self, event) - -class PesterConvo(QtGui.QFrame): - def __init__(self, chum, initiated, mainwindow, parent=None): - QtGui.QFrame.__init__(self, parent) - self.setFocusPolicy(QtCore.Qt.ClickFocus) - self.chum = chum - self.mainwindow = mainwindow - convo = self.mainwindow.theme["convo"] - self.resize(*convo["size"]) - self.setStyleSheet(convo["style"]) - self.setWindowIcon(chum.mood.icon(self.mainwindow.theme)) - self.setWindowTitle(chum.handle) - - t = Template(self.mainwindow.theme["convo/chumlabel/text"]) - - self.chumLabel = QtGui.QLabel(t.safe_substitute(handle=chum.handle), self) - self.chumLabel.setStyleSheet(self.mainwindow.theme["convo/chumlabel/style"]) - self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]]) - self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"]) - self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"]) - self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding)) - self.textArea = PesterText(self.mainwindow.theme, self) - self.textInput = PesterInput(self.mainwindow.theme, self) - self.textInput.setFocus() - - self.connect(self.textInput, QtCore.SIGNAL('returnPressed()'), - self, QtCore.SLOT('sentMessage()')) - - self.layout = QtGui.QVBoxLayout() - self.layout.addWidget(self.chumLabel) - self.layout.addWidget(self.textArea) - self.layout.addWidget(self.textInput) - self.layout.setSpacing(0) - self.layout.setMargin(0) - - self.setLayout(self.layout) - - self.chumopen = False - - if parent: - parent.addChat(self) - if initiated: - msg = self.mainwindow.profile().pestermsg(self.chum, QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), self.mainwindow.theme["convo/text/beganpester"]) - self.setChumOpen(True) - self.textArea.append(convertTags(msg)) - self.mainwindow.chatlog.log(self.chum.handle, convertTags(msg, "bbcode")) - self.newmessage = False - - def updateMood(self, mood, unblocked=False): - if mood.name() == "offline" and self.chumopen == True and not unblocked: - msg = self.chum.pestermsg(self.mainwindow.profile(), QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), self.mainwindow.theme["convo/text/ceasepester"]) - self.textArea.append(convertTags(msg)) - self.mainwindow.chatlog.log(self.chum.handle, convertTags(msg, "bbcode")) - self.chumopen = False - if self.parent(): - self.parent().updateMood(self.chum.handle, mood, unblocked) - else: - if self.chum.blocked(self.mainwindow.config) and not unblocked: - self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])) - else: - self.setWindowIcon(mood.icon(self.mainwindow.theme)) - # print mood update? - def updateBlocked(self): - if self.parent(): - self.parent().updateBlocked(self.chum.handle) - else: - self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])) - def updateColor(self, color): - self.chum.color = color - def addMessage(self, text, me=True): - if me: - chum = self.mainwindow.profile() - else: - chum = self.chum - self.notifyNewMessage() - self.textArea.addMessage(text, chum) - - def notifyNewMessage(self): - # first see if this conversation HASS the focus - if not (self.hasFocus() or self.textArea.hasFocus() or - self.textInput.hasFocus() or - (self.parent() and self.parent().convoHasFocus(self.chum.handle))): - # ok if it has a tabconvo parent, send that the notify. - if self.parent(): - self.parent().notifyNewMessage(self.chum.handle) - # if not change the window title and update system tray - else: - self.newmessage = True - self.setWindowTitle(self.chum.handle+"*") - def func(): - self.showChat() - self.mainwindow.waitingMessages.addMessage(self.chum.handle, func) - - def clearNewMessage(self): - if self.parent(): - self.parent().clearNewMessage(self.chum.handle) - elif self.newmessage: - self.newmessage = False - self.setWindowTitle(self.chum.handle) - self.mainwindow.waitingMessages.messageAnswered(self.chum.handle) - # reset system tray - def focusInEvent(self, event): - self.clearNewMessage() - self.textInput.setFocus() - def raiseChat(self): - self.activateWindow() - self.raise_() - self.textInput.setFocus() - - def showChat(self): - if self.parent(): - self.parent().showChat(self.chum.handle) - self.raiseChat() - - def closeEvent(self, event): - self.mainwindow.waitingMessages.messageAnswered(self.chum.handle) - self.windowClosed.emit(self.chum.handle) - def setChumOpen(self, o): - self.chumopen = o - def changeTheme(self, theme): - self.resize(*theme["convo/size"]) - self.setStyleSheet(theme["convo/style"]) - self.setWindowIcon(self.chum.mood.icon(theme)) - t = Template(self.mainwindow.theme["convo/chumlabel/text"]) - self.chumLabel.setText(t.safe_substitute(handle=self.chum.handle)) - self.chumLabel.setStyleSheet(theme["convo/chumlabel/style"]) - self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]]) - self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"]) - self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"]) - self.chumLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding)) - self.textArea.changeTheme(theme) - self.textInput.changeTheme(theme) - - @QtCore.pyqtSlot() - def sentMessage(self): - text = self.textInput.text() - if text == "": - return - # deal with quirks here - qtext = self.mainwindow.userprofile.quirks.apply(unicode(text)) - text = QtCore.QString(qtext) - self.textInput.setText("") - self.addMessage(text, True) - # if ceased, rebegin - if not self.chumopen: - self.mainwindow.newConvoStarted.emit(QtCore.QString(self.chum.handle), True) - # convert color tags - text = convertTags(unicode(text), "ctag") - self.messageSent.emit(text, self.chum) - - messageSent = QtCore.pyqtSignal(QtCore.QString, PesterProfile) - windowClosed = QtCore.pyqtSignal(QtCore.QString) - - aligndict = {"h": {"center": QtCore.Qt.AlignHCenter, - "left": QtCore.Qt.AlignLeft, - "right": QtCore.Qt.AlignRight }, - "v": {"center": QtCore.Qt.AlignVCenter, - "top": QtCore.Qt.AlignTop, - "bottom": QtCore.Qt.AlignBottom } } class PesterWindow(MovingWindow): def __init__(self, parent=None): @@ -1076,6 +581,7 @@ class PesterWindow(MovingWindow): QtCore.Qt.FramelessWindowHint)) self.convos = {} + self.memos = {} self.tabconvo = None self.setObjectName("main") @@ -1103,11 +609,16 @@ class PesterWindow(MovingWindow): self.userlistaction = userlistaction self.connect(userlistaction, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('showAllUsers()')) + memoaction = QtGui.QAction(self.theme["main/menus/client/memos"], self) + self.memoaction = memoaction + self.connect(memoaction, QtCore.SIGNAL('triggered()'), + self, QtCore.SLOT('showMemos()')) self.menu = QtGui.QMenuBar(self) filemenu = self.menu.addMenu(self.theme["main/menus/client/_name"]) self.filemenu = filemenu filemenu.addAction(opts) + filemenu.addAction(memoaction) filemenu.addAction(userlistaction) filemenu.addAction(exitaction) @@ -1268,6 +779,27 @@ class PesterWindow(MovingWindow): self.connect(self.tabconvo, QtCore.SIGNAL('windowClosed()'), self, QtCore.SLOT('tabsClosed()')) + + def newMemo(self, channel): + if self.memos.has_key(channel): + # load memo + return + else: + # do slider dialog then set + if self.config.tabs(): + # create new tabbed memo window + pass + else: + self.memoWindow = PesterMemo(channel, self, None) + # connect signals + # chat client send memo open + self.memoWindow.show() + + def addChum(self, chum): + self.chumList.addChum(chum) + self.config.addChum(chum) + self.moodRequest.emit(chum) + def changeProfile(self, collision=None): if not hasattr(self, 'chooseprofile'): self.chooseprofile = None @@ -1348,13 +880,7 @@ class PesterWindow(MovingWindow): if not pygame.mixer: self.alarm = NoneSound() else: - self.alarm = pygame.mixer.Sound(theme["main/sounds/alertsound"] -) - - def addChum(self, chum): - self.chumList.addChum(chum) - self.config.addChum(chum) - self.moodRequest.emit(chum) + self.alarm = pygame.mixer.Sound(theme["main/sounds/alertsound"]) def changeTheme(self, theme): self.theme = theme @@ -1393,6 +919,7 @@ class PesterWindow(MovingWindow): else: self.waitingMessages.answerMessage() + @QtCore.pyqtSlot() def blockSelectedChum(self): curChumListing = self.chumList.currentItem() @@ -1539,6 +1066,39 @@ class PesterWindow(MovingWindow): self.unblockedChum.emit(handle) @QtCore.pyqtSlot() + def showMemos(self): + if not hasattr(self, 'memochooser'): + self.memochooser = None + if self.memochooser: + return + self.memochooser = PesterMemoList(self) + self.connect(self.memochooser, QtCore.SIGNAL('accepted()'), + self, QtCore.SLOT('joinSelectedMemo()')) + self.connect(self.memochooser, QtCore.SIGNAL('rejected()'), + self, QtCore.SLOT('memoChooserClose()')) + self.requestChannelList.emit() + self.memochooser.show() + @QtCore.pyqtSlot() + def joinSelectedMemo(self): + newmemo = self.memochooser.newmemoname() + selectedmemo = self.memochooser.selectedmemo() + if newmemo: + self.newMemo('#'+newmemo) + else: + self.newMemo('#'+selectedmemo.text()) + self.memochooser = None + @QtCore.pyqtSlot() + def memoChooserClose(self): + self.memochooser = None + @QtCore.pyqtSlot() + def memoChooserClose(self): + self.memochooser = None + + @QtCore.pyqtSlot(PesterList) + def updateChannelList(self, channels): + if hasattr(self, 'memochooser') and self.memochooser: + self.memochooser.updateChannels(channels) + @QtCore.pyqtSlot() def showAllUsers(self): if not hasattr(self, 'allusers'): self.allusers = None @@ -1561,6 +1121,7 @@ class PesterWindow(MovingWindow): @QtCore.pyqtSlot() def userListClose(self): self.allusers = None + @QtCore.pyqtSlot() def openQuirks(self): if not hasattr(self, 'quirkmenu'): @@ -1705,6 +1266,8 @@ class PesterWindow(MovingWindow): return self.colorDialog = QtGui.QColorDialog(self) color = self.colorDialog.getColor(initial=self.profile().color) + if not color.isValid(): + color = self.profile().color self.mychumcolor.setStyleSheet("background: %s" % color.name()) self.userprofile.setColor(color) self.mycolorUpdated.emit() @@ -1753,6 +1316,7 @@ class PesterWindow(MovingWindow): moodRequest = QtCore.pyqtSignal(PesterProfile) moodsRequest = QtCore.pyqtSignal(PesterList) moodUpdated = QtCore.pyqtSignal() + requestChannelList = QtCore.pyqtSignal() requestNames = QtCore.pyqtSignal(QtCore.QString) namesUpdated = QtCore.pyqtSignal() userPresentSignal = QtCore.pyqtSignal(QtCore.QString,QtCore.QString,QtCore.QString) @@ -1820,6 +1384,9 @@ class PesterIRC(QtCore.QObject): def requestNames(self, channel): c = unicode(channel) helpers.names(self.cli, c) + @QtCore.pyqtSlot() + def requestChannelList(self): + helpers.channel_list(self.cli) def updateIRC(self): self.conn.next() @@ -1828,6 +1395,7 @@ class PesterIRC(QtCore.QObject): colorUpdated = QtCore.pyqtSignal(QtCore.QString, QtGui.QColor) messageReceived = QtCore.pyqtSignal(QtCore.QString, QtCore.QString) namesReceived = QtCore.pyqtSignal(QtCore.QString, PesterList) + channelListReceived = QtCore.pyqtSignal(PesterList) nickCollision = QtCore.pyqtSignal(QtCore.QString, QtCore.QString) userPresentUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString) @@ -1918,7 +1486,20 @@ class PesterHandler(DefaultCommandHandler): 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: @@ -2020,6 +1601,10 @@ def main(): QtCore.SIGNAL('requestNames(QString)'), irc, QtCore.SLOT('requestNames(QString)')) + irc.connect(widget, + QtCore.SIGNAL('requestChannelList()'), + irc, + QtCore.SLOT('requestChannelList()')) # IRC --> Main window irc.connect(irc, @@ -2046,6 +1631,10 @@ def main(): QtCore.SIGNAL('userPresentUpdate(QString, QString, QString)'), widget, QtCore.SLOT('userPresentUpdate(QString, QString, QString)')) + irc.connect(irc, + QtCore.SIGNAL('channelListReceived(PyQt_PyObject)'), + widget, + QtCore.SLOT('updateChannelList(PyQt_PyObject)')) ircapp = IRCThread(irc) ircapp.start() diff --git a/pesterdata.pyc b/pesterdata.pyc deleted file mode 100644 index 7d21240..0000000 Binary files a/pesterdata.pyc and /dev/null differ diff --git a/pestermenus.pyc b/pestermenus.pyc deleted file mode 100644 index fcaec88..0000000 Binary files a/pestermenus.pyc and /dev/null differ diff --git a/profiles/testProfile.js b/profiles/testProfile.js index 223100d..e63a331 100644 --- a/profiles/testProfile.js +++ b/profiles/testProfile.js @@ -1 +1 @@ -{"color": "#aa00ff", "theme": "trollian", "quirks": [], "handle": "testProfile"} \ No newline at end of file +{"color": "#aa00ff", "theme": "pesterchum", "quirks": [], "handle": "testProfile"} \ No newline at end of file diff --git a/setup-py2exe.py b/setup-py2exe.py new file mode 100644 index 0000000..2415bf1 --- /dev/null +++ b/setup-py2exe.py @@ -0,0 +1,174 @@ +# ======================================================# +# File automagically generated by GUI2Exe version 0.3 +# Andrea Gavana, 01 April 2007 +# ======================================================# + +# Let's start with some default (for me) imports... + +from distutils.core import setup +import py2exe +import glob +import os +import zlib +import shutil + +# Remove the build folder +shutil.rmtree("build", ignore_errors=True) + +# do the same for dist folder +shutil.rmtree("dist", ignore_errors=True) + +MANIFEST_TEMPLATE = """ + + + + %(prog)s + + + + + + + + + + + + + + + + + + + + +""" + +class Target(object): + """ A simple class that holds information on our executable file. """ + def __init__(self, **kw): + """ Default class constructor. Update as you need. """ + self.__dict__.update(kw) + + +# Ok, let's explain why I am doing that. +# Often, data_files, excludes and dll_excludes (but also resources) +# can be very long list of things, and this will clutter too much +# the setup call at the end of this file. So, I put all the big lists +# here and I wrap them using the textwrap module. + +data_files = [] + +includes = [] +excludes = ['_gtkagg', '_tkagg', 'bsddb', 'curses', 'email', 'pywin.debugger', + 'pywin.debugger.dbgcon', 'pywin.dialogs', 'tcl', + 'Tkconstants', 'Tkinter'] +packages = [] +dll_excludes = ['libgdk-win32-2.0-0.dll', 'libgobject-2.0-0.dll', 'tcl84.dll', + 'tk84.dll', + 'MSVCP90.dll', 'mswsock.dll', 'powrprof.dll'] +icon_resources = [] +bitmap_resources = [] +other_resources = [] +other_resources = [(24, 1, MANIFEST_TEMPLATE % dict(prog="MyAppName"))] + + +# This is a place where the user custom code may go. You can do almost +# whatever you want, even modify the data_files, includes and friends +# here as long as they have the same variable name that the setup call +# below is expecting. + + +# +# The following will copy the MSVC run time dll's +# (msvcm90.dll, msvcp90.dll and msvcr90.dll) and +# the Microsoft.VC90.CRT.manifest which I keep in the +# "Py26MSdlls" folder to the dist folder +# +# depending on wx widgets you use, you might need to add +# gdiplus.dll to the above collection + +py26MSdll = glob.glob(r"c:\Dev\Py26MSdlls-9.0.21022.8\msvc\*.*") + +# install the MSVC 9 runtime dll's into the application folder +data_files += [("", py26MSdll),] + +# I found on some systems one has to put them into sub-folders. +##data_files += [("Microsoft.VC90.CRT", py26MSdll), +## ("lib\Microsoft.VC90.CRT", py26MSdll)] + + + +# Ok, now we are going to build our target class. +# I chose this building strategy as it works perfectly for me :-D + + +GUI2Exe_Target_1 = Target( + # what to build + script = "test.py", + icon_resources = icon_resources, + bitmap_resources = bitmap_resources, + other_resources = other_resources, + dest_base = "test", + version = "0.1", + company_name = "No Company", + copyright = "No Copyrights", + name = "Py2Exe Sample File" + ) + + + +# That's serious now: we have all (or almost all) the options py2exe +# supports. I put them all even if some of them are usually defaulted +# and not used. Some of them I didn't even know about. + +setup( + + data_files = data_files, + + options = {"py2exe": {"compressed": 2, + "optimize": 2, + "includes": includes, + "excludes": excludes, + "packages": packages, + "dll_excludes": dll_excludes, + "bundle_files": 2, + "dist_dir": "dist", + "xref": False, + "skip_archive": False, + "ascii": False, + "custom_boot_script": '', + } + }, + + zipfile = "lib\library.zip", + console = [], + windows = [GUI2Exe_Target_1] + ) + +# This is a place where any post-compile code may go. +# You can add as much code as you want, which can be used, for example, +# to clean up your folders or to do some particular post-compilation +# actions. + +# And we are done. That's a setup script :-D diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..3454796 --- /dev/null +++ b/setup.py @@ -0,0 +1,32 @@ +from cx_Freeze import setup, Executable +import sys +import os +import shutil + +if sys.platform == "win32": + base = "Win32GUI" +else: + base = "Console" + +setup( + name = "P3ST3RCHUM", + version = "3.14", + description = "P3ST3RCHUM", + executables = [Executable("pesterchum.py", + base=base, + icon="pesterchum.ico", + compress=True, + )]) +if sys.platform == "win32": + os.rename("build/exe.win32-2.6", "build/pesterchum") + +shutil.copytree("themes", "build/pesterchum/themes") +shutil.copytree("imageformats", "build/pesterchum/imageformats") +shutil.copy("pesterchum.js", "build/pesterchum/") +shutil.copy("C:/Dev/Py26MSdlls-9.0.21022.8/msvc/msvcm90.dll", "build/pesterchum") +shutil.copy("C:/Dev/Py26MSdlls-9.0.21022.8/msvc/msvcp90.dll", "build/pesterchum") +shutil.copy("C:/Dev/Py26MSdlls-9.0.21022.8/msvc/msvcr90.dll", "build/pesterchum") +shutil.copy("C:/Dev/Py26MSdlls-9.0.21022.8/msvc/x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375.manifest", + "build/pesterchum") +os.mkdir("build/pesterchum/profiles") +os.mkdir("build/pesterchum/logs") diff --git a/themes/pesterchum/memo.png b/themes/pesterchum/memo.png new file mode 100644 index 0000000..f4eaa2e Binary files /dev/null and b/themes/pesterchum/memo.png differ diff --git a/themes/pesterchum/style.js b/themes/pesterchum/style.js index 8406246..0d3abb4 100644 --- a/themes/pesterchum/style.js +++ b/themes/pesterchum/style.js @@ -17,6 +17,7 @@ "sounds": { "alertsound": "$path/alarm.wav" }, "menus": {"client": {"_name": "CLIENT", "options": "OPTIONS", + "memos": "MEMOS", "userlist": "USERLIST", "exit": "EXIT"}, "profile": {"_name": "PROFILE", @@ -183,6 +184,7 @@ }, "convo": {"style": "background: #fdb302; border:2px solid yellow; font-family: 'Courier'", + "margins": {"top": 0, "bottom": 0, "left": 0, "right": 0 }, "size": [295, 191], "chumlabel": { "style": "background: rgb(196, 138, 0); color: white; border:0px;", "align": { "h": "center", "v": "center" }, @@ -209,6 +211,25 @@ "unblocked": "unblocked" }, "systemMsgColor": "#646464" + }, + "memos": + {"memoicon": "$path/memo.png", + "style": "background: #fdb302; font-family:'Courier';font:bold;selection-background-color:#919191; ", + "size": [600,300], + "label": { "text": "$channel", + "style": "background: rgb(196, 138, 0); color: white; border:0px;", + "align": { "h": "center", "v": "center" }, + "minheight": 30, + "maxheight": 50 + }, + "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 }, + "time": { "text": { "width": 75, "style": "" }, + "slider": { "style": "", + "groove": "", + "handle": "" } + } } - } \ No newline at end of file diff --git a/themes/trollian/memo.png b/themes/trollian/memo.png new file mode 100644 index 0000000..c5edf5f Binary files /dev/null and b/themes/trollian/memo.png differ diff --git a/themes/trollian/style.js b/themes/trollian/style.js index 300a32e..bf0d2d8 100644 --- a/themes/trollian/style.js +++ b/themes/trollian/style.js @@ -10,13 +10,14 @@ "loc": [621, 8]}, "menubar": { "style": "font-family: 'Arial'; font-size: 11px; color: rgba(0,0,0,0);" }, "menu" : { "style": "font-family: 'Arial'; font-size: 11px; background-color: #c2c2c2; border:1px solid #545454;", - "selected": "background-color: #545454", - "menuitem": "margin-right:14px;", - "loc": [14,90] + "selected": "background-color: #545454", + "menuitem": "margin-right:14px;", + "loc": [14,90] }, "sounds": { "alertsound": "$path/alarm.wav" }, "menus": {"client": {"_name": "Trollian", "options": "Options", + "memos": "Memos", "userlist": "Fresh Targets", "exit": "Abscond"}, "profile": {"_name": "View", @@ -31,58 +32,58 @@ "addchum": "Add Chump", "unblockchum": "Mercy"} }, - "chums": { "style": "border: 0px; background-color: white; padding: 5px; font-family: 'Arial';selection-background-color:rgb(200,200,200); ", - "loc": [476, 90], + "chums": { "style": "font-size: 12px; background: white; border:2px solid #c2c2c2; padding: 5px; font-family: 'Arial';selection-background-color:rgb(200,200,200); ", + "loc": [475, 89], "size": [175, 361], "userlistcolor": "black", - "moods": { - -"chummy": { "icon": "$path/chummy.png", "color": "#63ea00" }, - -"rancorous": { "icon": "$path/rancorous.png", "color": "#7f7f7f" }, - -"offline": { "icon": "$path/offline.png", "color": "black"}, - - -"pleasant": { "icon": "$path/pleasant.png", "color": "#d69df8" }, - -"distraught": { "icon": "$path/distraught.png", "color": "#706eba" }, - -"unruly": { "icon": "$path/unruly.png", "color": "blue" }, - - -"smooth": { "icon": "$path/smooth.png", "color": "red" }, - - -"ecstatic": { "icon": "$path/ecstatic.png", "color": "#99004d" }, - -"relaxed": { "icon": "$path/relaxed.png", "color": "#078446" }, - -"discontent": { "icon": "$path/discontent.png", "color": "#a75403" }, - -"devious": { "icon": "$path/devious.png", "color": "#008282" }, - -"sleek": { "icon": "$path/sleek.png", "color": "#a1a100" }, - -"detestful": { "icon": "$path/detestful.png", "color": "#6a006a" }, - -"mirthful": { "icon": "$path/mirthful.png", "color": "#450077" }, - -"manipulative": { "icon": "$path/manipulative.png", "color": "#004182" }, - -"vigorous": { "icon": "$path/vigorous.png", "color": "#0021cb" }, - -"perky": { "icon": "$path/perky.png", "color": "#406600" }, - -"acceptant": { "icon": "$path/acceptant.png", "color": "#a10000" }, - -"protective": { "icon": "$path/protective.png", "color": "white" }, - -"blocked": { "icon": "$path/blocked.png", "color": "black" } - - } + "moods": { + + "chummy": { "icon": "$path/chummy.png", "color": "#63ea00" }, + + "rancorous": { "icon": "$path/rancorous.png", "color": "#7f7f7f" }, + + "offline": { "icon": "$path/offline.png", "color": "black"}, + + + "pleasant": { "icon": "$path/pleasant.png", "color": "#d69df8" }, + + "distraught": { "icon": "$path/distraught.png", "color": "#706eba" }, + + "unruly": { "icon": "$path/unruly.png", "color": "blue" }, + + + "smooth": { "icon": "$path/smooth.png", "color": "red" }, + + + "ecstatic": { "icon": "$path/ecstatic.png", "color": "#99004d" }, + + "relaxed": { "icon": "$path/relaxed.png", "color": "#078446" }, + + "discontent": { "icon": "$path/discontent.png", "color": "#a75403" }, + + "devious": { "icon": "$path/devious.png", "color": "#008282" }, + + "sleek": { "icon": "$path/sleek.png", "color": "#a1a100" }, + + "detestful": { "icon": "$path/detestful.png", "color": "#6a006a" }, + + "mirthful": { "icon": "$path/mirthful.png", "color": "#450077" }, + + "manipulative": { "icon": "$path/manipulative.png", "color": "#004182" }, + + "vigorous": { "icon": "$path/vigorous.png", "color": "#0021cb" }, + + "perky": { "icon": "$path/perky.png", "color": "#406600" }, + + "acceptant": { "icon": "$path/acceptant.png", "color": "#a10000" }, + + "protective": { "icon": "$path/protective.png", "color": "white" }, + + "blocked": { "icon": "$path/blocked.png", "color": "black" } + + } }, - "trollslum": { + "trollslum": { "style": "background: rgb(190, 19, 4); font-family: 'Arial'", "size": [175, 461], "label": { "text": "Chumpdump", @@ -99,7 +100,7 @@ "size": [0,0], "text": "" } }, - "defaultwindow": { "style": "background: #c2c2c2; font-family:'Arial';font:bold;selection-background-color:#545454; " + "defaultwindow": { "style": "background: #c2c2c2; font-family:'Arial';font:bold;selection-background-color:#545454; " }, "addchum": { "style": "background: rgba(0,0,0,0); border:0px; color: rgba(0,0,0,0);", "loc": [475, 67], @@ -107,142 +108,143 @@ "text": "" }, "pester": { "style": "background: rgba(0,0,0,0); border:0px; color: rgba(0,0,0,0);", - "loc": [0,0], - "size": [0, 0], - "text": "" - }, + "loc": [0,0], + "size": [0, 0], + "text": "" + }, "block": { "style": "background: rgba(0,0,0,0); border:0px; color: rgba(0,0,0,0);", - "loc": [1500,202], - "size": [71, 22], - "text": "" - }, + "loc": [1500,202], + "size": [71, 22], + "text": "" + }, "defaultmood": 7, "moodlabel": { "style": "", - "loc": [0, 0], - "text": "" - }, + "loc": [0, 0], + "text": "" + }, "moods": [ - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck1.png); border:0px;", - "loc": [25, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 17 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck2.png); border:0px;", - "loc": [60, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 9 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck3.png); border:0px;", - "loc": [95, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 11 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck4.png); border:0px;", - "loc": [130, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 1 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck5.png); border:0px;", - "loc": [165, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 16 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck6.png); border:0px;", - "loc": [200, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 8 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck7.png); border:0px;", - "loc": [235, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 10 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck8.png); border:0px;", - "loc": [270, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 14 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck9.png); border:0px;", - "loc": [305, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 15 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck10.png); border:0px;", - "loc": [340, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 13 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck11.png); border:0px;", - "loc": [375, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 12 - }, - { "style": "border:0px;", - "selected": "background-image:url($path/moodcheck12.png); border:0px;", - "loc": [410, 141], - "size": [20, 270], - "text": "", - "icon": "", - "mood": 7 - }, - - { "style": "border:0px;color: rgba(0, 0, 0, 0%);", - "selected": "border:0px; color: rgba(0, 0, 0, 0%);", - "loc": [12, 117], - "size": [435, 18], - "text": "", - "icon": "", - "mood": 2 - } + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck1.png); border:0px;", + "loc": [25, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 17 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck2.png); border:0px;", + "loc": [60, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 9 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck3.png); border:0px;", + "loc": [95, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 11 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck4.png); border:0px;", + "loc": [130, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 1 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck5.png); border:0px;", + "loc": [165, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 16 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck6.png); border:0px;", + "loc": [200, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 8 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck7.png); border:0px;", + "loc": [235, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 10 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck8.png); border:0px;", + "loc": [270, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 14 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck9.png); border:0px;", + "loc": [305, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 15 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck10.png); border:0px;", + "loc": [340, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 13 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck11.png); border:0px;", + "loc": [375, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 12 + }, + { "style": "border:0px;", + "selected": "background-image:url($path/moodcheck12.png); border:0px;", + "loc": [410, 141], + "size": [20, 270], + "text": "", + "icon": "", + "mood": 7 + }, + + { "style": "border:0px;color: rgba(0, 0, 0, 0%);", + "selected": "border:0px; color: rgba(0, 0, 0, 0%);", + "loc": [12, 117], + "size": [435, 18], + "text": "", + "icon": "", + "mood": 2 + } ] }, "convo": {"style": "background: rgb(190, 19, 4); font-family: 'Arial';", - "size": [308, 194], - "chumlabel": { "style": "background: rgb(255, 38, 18); color: white;", - "align": { "h": "center", "v": "center" }, - "minheight": 30, - "maxheight": 50, - "text" : "trolling: $handle" + "margins": {"top": 22, "bottom": 9, "left": 10, "right": 4 }, + "size": [400, 250], + "chumlabel": { "style": "background: rgb(255, 38, 18); color: white; padding: 2px; border:1px solid #c2c2c2;", + "align": { "h": "left", "v": "center" }, + "minheight": 18, + "maxheight": 18, + "text" : "trolling: $handle" }, "textarea": { - "style": "background: white; border:0px;" + "style": "background: white; border:2px solid #c2c2c2; font-size: 12px; margin-top: 4px;" }, "input": { - "style": "background: white; border:0px solid #c48a00;margin-top:5px;" + "style": "background: white;margin-top:5px; border:1px solid #c2c2c2; margin-right: 54px; font-size: 12px;" }, "tabs": { "style": "", @@ -257,6 +259,10 @@ "unblocked": "mercifully forgave" }, "systemMsgColor": "#646464" - } - + }, + "memos": + {"memoicon": "$path/memo.png" + + } + } \ No newline at end of file diff --git a/tmp.py b/tmp.py new file mode 100644 index 0000000..937377c --- /dev/null +++ b/tmp.py @@ -0,0 +1,18 @@ +class PesterMemo(PesterConvo): + def __init__() + def updateMood: pass + def updateBlocked + def updateColor: pass + def addMessage + def notifyNewMessage + def clearNewMessage + def changeTheme + + slot + def sentMessage + + messageSent - signal -> sendMessage -> sendMessage(Memo) + windowClosed - signal -> closeMemo + + self.textInput + self.textArea