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