from string import Template import re import platform import httplib, urllib from time import strftime from copy import copy from datetime import datetime, timedelta from PyQt4 import QtGui, QtCore from mood import Mood from dataobjs import PesterProfile, PesterHistory from generic import PesterIcon from parsetools import convertTags, lexMessage, splitMessage, mecmd, colorBegin, colorEnd, \ img2smiley, smiledict import parsetools import pnc.lexercon as lexercon class PesterTabWindow(QtGui.QFrame): def __init__(self, mainwindow, parent=None, convo="convo"): QtGui.QFrame.__init__(self, parent) self.setAttribute(QtCore.Qt.WA_QuitOnClose, False) self.setFocusPolicy(QtCore.Qt.ClickFocus) self.mainwindow = mainwindow self.tabs = QtGui.QTabBar(self) self.tabs.setMovable(True) 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.connect(self.tabs, QtCore.SIGNAL('tabMoved(int, int)'), self, QtCore.SLOT('tabMoved(int, int)')) self.initTheme(self.mainwindow.theme) 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 self.type = convo # 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.title()] = convo # either addTab or setCurrentIndex will trigger changed() newindex = self.tabs.addTab(convo.title()) self.tabIndices[convo.title()] = newindex self.tabs.setCurrentIndex(newindex) self.tabs.setTabIcon(newindex, convo.icon()) 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.title()): return True def keyPressEvent(self, event): keypress = event.key() mods = event.modifiers() if ((mods & QtCore.Qt.ControlModifier) and keypress == QtCore.Qt.Key_Tab): handles = self.convos.keys() waiting = self.mainwindow.waitingMessages.waitingHandles() waitinghandles = list(set(handles) & set(waiting)) if len(waitinghandles) > 0: nexti = self.tabIndices[waitinghandles[0]] else: nexti = (self.tabIndices[self.currentConvo.title()] + 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["%s/tabs/newmsgcolor" % (self.type)])) convo = self.convos[handle] # Create a function for the icon to use 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 initTheme(self, theme): self.resize(*theme["convo/size"]) self.setStyleSheet(theme["convo/tabwindow/style"]) self.tabs.setShape(theme["convo/tabs/tabstyle"]) self.tabs.setStyleSheet("QTabBar::tab{ %s } QTabBar::tab:selected { %s }" % (theme["convo/tabs/style"], theme["convo/tabs/selectedstyle"])) def changeTheme(self, theme): self.initTheme(theme) for c in self.convos.values(): tabi = self.tabIndices[c.title()] self.tabs.setTabIcon(tabi, c.icon()) currenttabi = self.tabs.currentIndex() if currenttabi >= 0: currentHandle = unicode(self.tabs.tabText(self.tabs.currentIndex())) self.setWindowIcon(self.convos[currentHandle].icon()) 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.icon()) self.setWindowTitle(convo.title()) self.activateWindow() self.raise_() convo.raiseChat() @QtCore.pyqtSlot(int, int) def tabMoved(self, to, fr): l = self.tabIndices for i in l: if l[i] == fr: oldpos = i if l[i] == to: newpos = i l[oldpos] = to l[newpos] = fr windowClosed = QtCore.pyqtSignal() class PesterMovie(QtGui.QMovie): def __init__(self, parent): QtGui.QMovie.__init__(self, parent) self.textwindow = parent @QtCore.pyqtSlot(int) def animate(self, frame): text = self.textwindow if text.mainwindow.config.animations(): movie = self url = text.urls[movie].toString() html = unicode(text.toHtml()) if html.find(url) != -1: if text.hasTabs: i = text.tabobject.tabIndices[text.parent().title()] if text.tabobject.tabs.currentIndex() == i: text.document().addResource(QtGui.QTextDocument.ImageResource, text.urls[movie], movie.currentPixmap()) text.setLineWrapColumnOrWidth(text.lineWrapColumnOrWidth()) else: text.document().addResource(QtGui.QTextDocument.ImageResource, text.urls[movie], movie.currentPixmap()) text.setLineWrapColumnOrWidth(text.lineWrapColumnOrWidth()) class PesterText(QtGui.QTextEdit): def __init__(self, theme, parent=None): QtGui.QTextEdit.__init__(self, parent) if hasattr(self.parent(), 'mainwindow'): self.mainwindow = self.parent().mainwindow else: self.mainwindow = self.parent() if type(parent.parent()) is PesterTabWindow: self.tabobject = parent.parent() self.hasTabs = True else: self.hasTabs = False self.initTheme(theme) self.setReadOnly(True) self.setMouseTracking(True) self.textSelected = False self.connect(self, QtCore.SIGNAL('copyAvailable(bool)'), self, QtCore.SLOT('textReady(bool)')) self.urls = {} for k in smiledict: self.addAnimation(QtCore.QUrl("smilies/%s" % (smiledict[k])), "smilies/%s" % (smiledict[k])) self.connect(self.mainwindow, QtCore.SIGNAL('animationSetting(bool)'), self, QtCore.SLOT('animateChanged(bool)')) def addAnimation(self, url, fileName): movie = PesterMovie(self) movie.setFileName(fileName) movie.setCacheMode(QtGui.QMovie.CacheAll) if movie.frameCount() > 1: self.urls[movie] = url movie.connect(movie, QtCore.SIGNAL('frameChanged(int)'), movie, QtCore.SLOT('animate(int)')) #movie.start() @QtCore.pyqtSlot(bool) def animateChanged(self, animate): if animate: for m in self.urls: html = unicode(self.toHtml()) if html.find(self.urls[m].toString()) != -1: if m.frameCount() > 1: m.start() else: for m in self.urls: html = unicode(self.toHtml()) if html.find(self.urls[m].toString()) != -1: if m.frameCount() > 1: m.stop() @QtCore.pyqtSlot(bool) def textReady(self, ready): self.textSelected = ready def initTheme(self, theme): if theme.has_key("convo/scrollbar"): self.setStyleSheet("QTextEdit { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["convo/textarea/style"], theme["convo/scrollbar/style"], theme["convo/scrollbar/handle"], theme["convo/scrollbar/downarrow"], theme["convo/scrollbar/uparrow"], theme["convo/scrollbar/uarrowstyle"], theme["convo/scrollbar/darrowstyle"] )) else: self.setStyleSheet("QTextEdit { %s }" % (theme["convo/textarea/style"])) def addMessage(self, lexmsg, chum): if len(lexmsg) == 0: return color = chum.colorcmd() systemColor = QtGui.QColor(self.parent().mainwindow.theme["convo/systemMsgColor"]) initials = chum.initials() parent = self.parent() window = parent.mainwindow me = window.profile() if self.mainwindow.config.animations(): for m in self.urls: if convertTags(lexmsg).find(self.urls[m].toString()) != -1: if m.state() == QtGui.QMovie.NotRunning: m.start() if self.parent().mainwindow.config.showTimeStamps(): if self.parent().mainwindow.config.time12Format(): time = strftime("[%I:%M") else: time = strftime("[%H:%M") if self.parent().mainwindow.config.showSeconds(): time += strftime(":%S] ") else: time += "] " else: time = "" if lexmsg[0] == "PESTERCHUM:BEGIN": parent.setChumOpen(True) pmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"]) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:CEASE": parent.setChumOpen(False) pmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/ceasepester"]) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:BLOCK": pmsg = chum.pestermsg(me, systemColor, window.theme['convo/text/blocked']) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:UNBLOCK": pmsg = chum.pestermsg(me, systemColor, window.theme['convo/text/unblocked']) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:BLOCKED": pmsg = chum.pestermsg(me, systemColor, window.theme['convo/text/blockedmsg']) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:IDLE": imsg = chum.idlemsg(systemColor, window.theme['convo/text/idle']) window.chatlog.log(chum.handle, imsg) self.append(convertTags(imsg)) elif type(lexmsg[0]) is mecmd: memsg = chum.memsg(systemColor, lexmsg) if chum is me: window.chatlog.log(parent.chum.handle, memsg) else: window.chatlog.log(chum.handle, memsg) self.append(time + convertTags(memsg)) 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, beginmsg) self.append(convertTags(beginmsg)) lexmsg[0:0] = [colorBegin("" % (color), color), "%s: " % (initials)] lexmsg.append(colorEnd("")) self.append("" + time + convertTags(lexmsg) + "") #self.append('' # + '' # + '' # + ''); if chum is me: window.chatlog.log(parent.chum.handle, lexmsg) else: if window.idleaction.isChecked() and parent.chumopen: idlethreshhold = 60 if (not hasattr(self, 'lastmsg')) or \ datetime.now() - self.lastmsg > timedelta(0,idlethreshhold): verb = window.theme["convo/text/idle"] idlemsg = me.idlemsg(systemColor, verb) parent.textArea.append(convertTags(idlemsg)) window.chatlog.log(chum.handle, idlemsg) parent.messageSent.emit("PESTERCHUM:IDLE", parent.title()) self.lastmsg = datetime.now() window.chatlog.log(chum.handle, lexmsg) def changeTheme(self, theme): self.initTheme(theme) sb = self.verticalScrollBar() sb.setValue(sb.maximum()) def focusInEvent(self, event): self.parent().clearNewMessage() QtGui.QTextEdit.focusInEvent(self, event) def keyPressEvent(self, event): if hasattr(self.parent(), 'textInput'): if event.key() not in [QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown, \ QtCore.Qt.Key_Up, QtCore.Qt.Key_Down]: self.parent().textInput.keyPressEvent(event) QtGui.QTextEdit.keyPressEvent(self, event) def mousePressEvent(self, event): if event.button() == QtCore.Qt.LeftButton: url = self.anchorAt(event.pos()) if url != "": if url[0] == "#" and url != "#pesterchum": self.parent().mainwindow.showMemos(url[1:]) elif url[0] == "@": handle = unicode(url[1:]) self.parent().mainwindow.newConversation(handle) else: if event.modifiers() == QtCore.Qt.ControlModifier: QtGui.QApplication.clipboard().setText(url) else: 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)) def contextMenuEvent(self, event): textMenu = self.createStandardContextMenu() if self.textSelected: self.submitLogAction = QtGui.QAction("Submit to Pesterchum QDB", self) self.connect(self.submitLogAction, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('submitLog()')) textMenu.addAction(self.submitLogAction) textMenu.exec_(event.globalPos()) def submitLogTitle(self): return "[%s -> %s]" % (self.parent().mainwindow.profile().handle, self.parent().chum.handle) @QtCore.pyqtSlot() def submitLog(self): mimedata = self.createMimeDataFromSelection() htmldata = img2smiley(mimedata.data("text/html")) textdoc = QtGui.QTextDocument() textdoc.setHtml(htmldata) logdata = "%s\n%s" % (self.submitLogTitle(), textdoc.toPlainText()) self.sending = QtGui.QDialog(self) layout = QtGui.QVBoxLayout() self.sending.sendinglabel = QtGui.QLabel("S3ND1NG...", self.sending) cancelbutton = QtGui.QPushButton("OK", self.sending) self.sending.connect(cancelbutton, QtCore.SIGNAL('clicked()'), self.sending, QtCore.SLOT('close()')) layout.addWidget(self.sending.sendinglabel) layout.addWidget(cancelbutton) self.sending.setLayout(layout) self.sending.show() params = urllib.urlencode({'quote': logdata, 'do': "add"}) headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} try: pass hconn = httplib.HTTPConnection('qdb.pesterchum.net', 80, timeout=15) hconn.request("POST", "/index.php", params, headers) response = hconn.getresponse() if response.status == 200: self.sending.sendinglabel.setText("SUCC3SS!") else: self.sending.sendinglabel.setText("F41L3D: %s %s" % (response.status, response.reason)) hconn.close() except Exception as e: self.sending.sendinglabel.setText("F41L3D: %s" % (e)) del self.sending 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() self.parent().textArea.textCursor().clearSelection() QtGui.QLineEdit.focusInEvent(self, event) def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Up: text = unicode(self.text()) next = self.parent().history.next(text) if next is not None: self.setText(next) elif event.key() == QtCore.Qt.Key_Down: prev = self.parent().history.prev() if prev is not None: self.setText(prev) elif event.key() in [QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown]: self.parent().textArea.keyPressEvent(event) self.parent().mainwindow.idletime = 0 QtGui.QLineEdit.keyPressEvent(self, event) class PesterConvo(QtGui.QFrame): def __init__(self, chum, initiated, mainwindow, parent=None): QtGui.QFrame.__init__(self, parent) self.setAttribute(QtCore.Qt.WA_QuitOnClose, False) self.setObjectName(chum.handle) self.setFocusPolicy(QtCore.Qt.ClickFocus) self.chum = chum self.mainwindow = mainwindow theme = self.mainwindow.theme self.resize(*theme["convo/size"]) self.setStyleSheet("QFrame#%s { %s }" % (chum.handle, theme["convo/style"])) self.setWindowIcon(self.icon()) self.setWindowTitle(self.title()) 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.MinimumExpanding)) 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.optionsMenu = QtGui.QMenu(self) self.optionsMenu.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) self.addChumAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self) self.connect(self.addChumAction, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('addThisChum()')) self.blockAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/blockchum"], self) self.connect(self.blockAction, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('blockThisChum()')) self.quirksOff = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self) self.quirksOff.setCheckable(True) self.connect(self.quirksOff, QtCore.SIGNAL('toggled(bool)'), self, QtCore.SLOT('toggleQuirks(bool)')) self.oocToggle = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self) self.oocToggle.setCheckable(True) self.connect(self.oocToggle, QtCore.SIGNAL('toggled(bool)'), self, QtCore.SLOT('toggleOOC(bool)')) self.unblockchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"], self) self.connect(self.unblockchum, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('unblockChumSlot()')) self.reportchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/report"], self) self.connect(self.reportchum, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('reportThisChum()')) self.logchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self) self.connect(self.logchum, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('openChumLogs()')) # For this, we'll want to use setChecked to toggle these so they match # the user's setting. Alternately (better), use a tristate checkbox, so # that they start semi-checked? # Easiest solution: Implement a 'Mute' option that overrides all # notifications for that window, save for mentions. # TODO: Look into setting up theme support here. self._beepToggle = QtGui.QAction("Beep on Message", self) self._beepToggle.setCheckable(True) self.connect(self._beepToggle, QtCore.SIGNAL('toggled(bool)'), self, QtCore.SLOT('toggleBeep(bool)')) self._flashToggle = QtGui.QAction("Flash on Message", self) self._flashToggle.setCheckable(True) self.connect(self._flashToggle, QtCore.SIGNAL('toggled(bool)'), self, QtCore.SLOT('toggleFlash(bool)')) self._muteToggle = QtGui.QAction("Mute Notifications", self) self._muteToggle.setCheckable(True) self.connect(self._muteToggle, QtCore.SIGNAL('toggled(bool)'), self, QtCore.SLOT('toggleMute(bool)')) self.optionsMenu.addAction(self.quirksOff) self.optionsMenu.addAction(self.oocToggle) self.optionsMenu.addAction(self._beepToggle) self.optionsMenu.addAction(self._flashToggle) self.optionsMenu.addAction(self._muteToggle) self.optionsMenu.addAction(self.logchum) self.optionsMenu.addAction(self.addChumAction) self.optionsMenu.addAction(self.blockAction) self.optionsMenu.addAction(self.reportchum) self.chumopen = False self.applyquirks = True self.ooc = False self.always_beep = False self.always_flash = False self.notifications_muted = 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.title(), msg) self.newmessage = False self.history = PesterHistory() def title(self): return self.chum.handle def icon(self): return self.chum.mood.icon(self.mainwindow.theme) def myUpdateMood(self, mood): chum = self.mainwindow.profile() syscolor = QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]) msg = chum.moodmsg(mood, syscolor, self.mainwindow.theme) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.title(), msg) def updateMood(self, mood, unblocked=False, old=None): syscolor = QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]) #~ if mood.name() == "offline" and self.chumopen == True and not unblocked: #~ self.mainwindow.ceasesound.play() #~ msg = self.chum.pestermsg(self.mainwindow.profile(), syscolor, self.mainwindow.theme["convo/text/ceasepester"]) #~ self.textArea.append(convertTags(msg)) #~ self.mainwindow.chatlog.log(self.title(), msg) #~ self.chumopen = False if old and old.name() != mood.name(): msg = self.chum.moodmsg(mood, syscolor, self.mainwindow.theme) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.title(), msg) if self.parent(): self.parent().updateMood(self.title(), 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"])) self.optionsMenu.addAction(self.unblockchum) self.optionsMenu.removeAction(self.blockAction) else: self.setWindowIcon(mood.icon(self.mainwindow.theme)) self.optionsMenu.removeAction(self.unblockchum) self.optionsMenu.addAction(self.blockAction) # print mood update? def updateBlocked(self): if self.parent(): self.parent().updateBlocked(self.title()) else: self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])) self.optionsMenu.addAction(self.unblockchum) self.optionsMenu.removeAction(self.blockAction) def updateColor(self, color): self.chum.color = color def addMessage(self, msg, me=True): if type(msg) in [str, unicode]: lexmsg = lexMessage(msg) else: lexmsg = msg if me: chum = self.mainwindow.profile() else: chum = self.chum self.notifyNewMessage() self.textArea.addMessage(lexmsg, chum) def notifyNewMessage(self): # first see if this conversation HASS the focus title = self.title() parent = self.parent() memoblink = pesterblink = self.mainwindow.config.blink() memoblink &= self.mainwindow.config.MBLINK pesterblink &= self.mainwindow.config.PBLINK print "{!s}.notifications_muted: {!s}".format(self, self.notifications_muted) mutednots = self.notifications_muted mtsrc = self if parent: try: mutednots = parent.notifications_muted mtsrc = parent except: pass if not (self.hasFocus() or self.textArea.hasFocus() or self.textInput.hasFocus() or (parent and parent.convoHasFocus(title))): # ok if it has a tabconvo parent, send that the notify. if parent: print "{!s}.notifications_muted: {!s}".format(mtsrc, mutednots) if not mutednots: # Stop the icon from highlighting parent.notifyNewMessage(title) if type(parent).__name__ == "PesterTabWindow": if self.always_flash or pesterblink: self.mainwindow.gainAttention.emit(parent) elif type(parent).__name__ == "MemoTabWindow": if self.always_flash or memoblink: self.mainwindow.gainAttention.emit(parent) # if not change the window title and update system tray else: self.newmessage = True self.setWindowTitle(title + "*") # karxi: The order of execution here is a bit unclear...I'm not # entirely sure how much of this directly affects what we see. def func(): self.showChat() self.mainwindow.waitingMessages.addMessage(title, func) if not self.notifications_muted: if type(self).__name__ == "PesterConvo": if self.always_flash or pesterblink: self.mainwindow.gainAttention.emit(self) elif type(self).__name__ == "PesterMemo": if self.always_flash or memoblink: self.mainwindow.gainAttention.emit(self) def clearNewMessage(self): if self.parent(): self.parent().clearNewMessage(self.title()) elif self.newmessage: self.newmessage = False self.setWindowTitle(self.title()) self.mainwindow.waitingMessages.messageAnswered(self.title()) # 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.title()) self.raiseChat() def contextMenuEvent(self, event): if event.reason() == QtGui.QContextMenuEvent.Mouse: self.optionsMenu.popup(event.globalPos()) def closeEvent(self, event): self.mainwindow.waitingMessages.messageAnswered(self.title()) for movie in self.textArea.urls: movie.stop() del movie self.windowClosed.emit(self.title()) def setChumOpen(self, o): self.chumopen = o def changeTheme(self, theme): self.resize(*theme["convo/size"]) self.setStyleSheet("QFrame#%s { %s }" % (self.chum.handle, theme["convo/style"])) margins = theme["convo/margins"] self.layout.setContentsMargins(margins["left"], margins["top"], margins["right"], margins["bottom"]) self.setWindowIcon(self.icon()) t = Template(self.mainwindow.theme["convo/chumlabel/text"]) self.chumLabel.setText(t.safe_substitute(handle=self.title())) 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.quirksOff.setText(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"]) self.addChumAction.setText(self.mainwindow.theme["main/menus/rclickchumlist/addchum"]) self.blockAction.setText(self.mainwindow.theme["main/menus/rclickchumlist/blockchum"]) self.unblockchum.setText(self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"]) self.logchum.setText(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"]) self.textArea.changeTheme(theme) self.textInput.changeTheme(theme) @QtCore.pyqtSlot() def sentMessage(self): # Offloaded to another function, like its sisters. # Fetch the raw text from the input box. text = self.textInput.text() text = unicode(self.textInput.text()) return parsetools.kxhandleInput(self, text, flavor="convo") @QtCore.pyqtSlot() def addThisChum(self): self.mainwindow.addChum(self.chum) @QtCore.pyqtSlot() def blockThisChum(self): self.mainwindow.blockChum(self.chum.handle) @QtCore.pyqtSlot() def reportThisChum(self): self.mainwindow.reportChum(self.chum.handle) @QtCore.pyqtSlot() def unblockChumSlot(self): self.mainwindow.unblockChum(self.chum.handle) @QtCore.pyqtSlot(bool) def toggleQuirks(self, toggled): self.applyquirks = not toggled @QtCore.pyqtSlot(bool) def toggleOOC(self, toggled): self.ooc = toggled @QtCore.pyqtSlot() def openChumLogs(self): currentChum = self.chum.handle self.mainwindow.chumList.pesterlogviewer = PesterLogViewer(currentChum, self.mainwindow.config, self.mainwindow.theme, self.mainwindow) self.connect(self.mainwindow.chumList.pesterlogviewer, QtCore.SIGNAL('rejected()'), self.mainwindow.chumList, QtCore.SLOT('closeActiveLog()')) self.mainwindow.chumList.pesterlogviewer.show() self.mainwindow.chumList.pesterlogviewer.raise_() self.mainwindow.chumList.pesterlogviewer.activateWindow() @QtCore.pyqtSlot(bool) def toggleBeep(self, toggled): self.always_beep = toggled @QtCore.pyqtSlot(bool) def toggleFlash(self, toggled): self.always_flash = toggled @QtCore.pyqtSlot(bool) def toggleMute(self, toggled): self.notifications_muted = toggled messageSent = QtCore.pyqtSignal(QtCore.QString, QtCore.QString) 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 } } # the import is way down here to avoid recursive imports from logviewer import PesterLogViewer