This commit is contained in:
Stephen Dranger 2011-02-04 15:17:27 -06:00
parent 4b965814b9
commit 79e4de5710
26 changed files with 1269 additions and 699 deletions

5
TODO
View file

@ -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

438
convo.py Normal file
View file

@ -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 = "<c=%s>%s: %s</c>" % (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 } }

BIN
convo.pyc Normal file

Binary file not shown.

View file

@ -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):

BIN
dataobjs.pyc Normal file

Binary file not shown.

View file

@ -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"}}
{"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"}}

125
memos.py Normal file
View file

@ -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

BIN
memos.pyc Normal file

Binary file not shown.

View file

@ -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()

BIN
menus.pyc Normal file

Binary file not shown.

View file

@ -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)

Binary file not shown.

89
parsetools.py Normal file
View file

@ -0,0 +1,89 @@
import re
from PyQt4 import QtGui
_ctag_begin = re.compile(r'<c=(.*?)>')
_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 "<c=%s,%s,%s>"
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 '<span style="color:%s">' % (qc.name())
elif format == "bbcode":
return '[color=%s]' % (qc.name())
elif format == "ctag":
(r,g,b,a) = qc.getRgb()
return '<c=%s,%s,%s>' % (r,g,b)
string = _ctag_begin.sub(colorrepfunc, string)
endtag = {"html": "</span>", "bbcode": "[/color]", "ctag": "</c>"}
string = string.replace("</c>", endtag[format])
def urlrep(matchobj):
if format=="html":
return "<a href='%s'>%s</a>" % (matchobj.group(1).replace("&amp;", "&"), matchobj.group(1))
elif format=="bbcode":
return "[url]%s[/url]" % (matchobj.group(1).replace("&amp;", "&"))
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("</c>")
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("&", "&amp;")
btlen +=1
elif type(o) is endTag:
if etlen >= btlen:
continue
else:
retval += "</c>"
etlen += 1
else:
retval += o.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
if btlen > etlen:
for i in range(0, btlen-etlen):
retval += "</c>"
return retval

BIN
parsetools.pyc Normal file

Binary file not shown.

View file

@ -1 +1 @@
{"tabs": true, "chums": ["aquaMarinist", "marineAquist", "unknownTraveler", "tentacleTherapist", "macruralAlchemist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "fireSwallow", "grimAuxiliatrix"], "defaultprofile": "ghostDunk", "block": []}
{"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": []}

View file

@ -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'<c=(.*?)>')
_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 "<c=%s,%s,%s>"
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 '<span style="color:%s">' % (qc.name())
elif format == "bbcode":
return '[color=%s]' % (qc.name())
elif format == "ctag":
(r,g,b,a) = qc.getRgb()
return '<c=%s,%s,%s>' % (r,g,b)
string = _ctag_begin.sub(colorrepfunc, string)
endtag = {"html": "</span>", "bbcode": "[/color]", "ctag": "</c>"}
string = string.replace("</c>", endtag[format])
urlrep = {"html": r"<a href='\1'>\1</a>",
"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("</c>")
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("&", "&amp;")
btlen +=1
elif type(o) is endTag:
if etlen >= btlen:
continue
else:
retval += "</c>"
etlen += 1
else:
retval += o.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
if btlen > etlen:
for i in range(0, btlen-etlen):
retval += "</c>"
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 = "<c=%s>%s: %s</c>" % (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()

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
{"color": "#aa00ff", "theme": "trollian", "quirks": [], "handle": "testProfile"}
{"color": "#aa00ff", "theme": "pesterchum", "quirks": [], "handle": "testProfile"}

174
setup-py2exe.py Normal file
View file

@ -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 = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="5.0.0.0"
processorArchitecture="x86"
name="%(prog)s"
type="win32"
/>
<description>%(prog)s</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false">
</requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.VC90.CRT"
version="9.0.21022.8"
processorArchitecture="x86"
publicKeyToken="1fc8b3b9a1e18e3b">
</assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
"""
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

32
setup.py Normal file
View file

@ -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")

BIN
themes/pesterchum/memo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

View file

@ -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": "" }
}
}
}

BIN
themes/trollian/memo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

View file

@ -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"
}
}

18
tmp.py Normal file
View file

@ -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