bc4af153af
Remove osVer function from ostools Remove isOSXLeopard only use chum.handle Fix bad except error Comment out console related function fix fix* except order fallback if i is not defined for log fix console Explicitly define nick() to appease pylint Comment out connect_cb, it's unused for pchum rn e is unsubscribable Explicitly define 'simple' irc commands fix exceptions part 2 fix send Explicitly define lastmsg as None on init iterate through copy or urls Comment out console for not as it's unmaintained
1168 lines
44 KiB
Python
1168 lines
44 KiB
Python
import sys
|
|
import logging
|
|
from string import Template
|
|
from time import strftime
|
|
from datetime import datetime, timedelta
|
|
|
|
try:
|
|
from PyQt6 import QtCore, QtGui, QtWidgets
|
|
from PyQt6.QtGui import QShortcut, QAction
|
|
except ImportError:
|
|
print("PyQt5 fallback (convo.py)")
|
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
from PyQt5.QtWidgets import QAction, QShortcut
|
|
|
|
import ostools
|
|
from dataobjs import PesterHistory
|
|
from parsetools import convertTags, lexMessage, mecmd, colorBegin, colorEnd, smiledict
|
|
import parsetools
|
|
from pnc.dep.attrdict import AttrDict
|
|
|
|
PchumLog = logging.getLogger("pchumLogger")
|
|
|
|
|
|
class PesterTabWindow(QtWidgets.QFrame):
|
|
def __init__(self, mainwindow, parent=None, convo="convo"):
|
|
super(PesterTabWindow, self).__init__(parent)
|
|
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_QuitOnClose, False)
|
|
self.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)
|
|
self.mainwindow = mainwindow
|
|
|
|
self.tabs = QtWidgets.QTabBar(self)
|
|
self.tabs.setMovable(True)
|
|
self.tabs.setTabsClosable(True)
|
|
self.tabs.currentChanged[int].connect(self.changeTab)
|
|
self.tabs.tabCloseRequested[int].connect(self.tabClose)
|
|
self.tabs.tabMoved[int, int].connect(self.tabMoved)
|
|
|
|
self.shortcuts = AttrDict()
|
|
self.shortcuts.tabNext = QShortcut(
|
|
QtGui.QKeySequence("Ctrl+j"),
|
|
self,
|
|
context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut,
|
|
)
|
|
self.shortcuts.tabLast = QShortcut(
|
|
QtGui.QKeySequence("Ctrl+k"),
|
|
self,
|
|
context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut,
|
|
)
|
|
# Note that we use reversed keys here.
|
|
self.shortcuts.tabUp = QShortcut(
|
|
QtGui.QKeySequence("Ctrl+PgDown"),
|
|
self,
|
|
context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut,
|
|
)
|
|
self.shortcuts.tabDn = QShortcut(
|
|
QtGui.QKeySequence("Ctrl+PgUp"),
|
|
self,
|
|
context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut,
|
|
)
|
|
|
|
self.shortcuts.tabNext.activated.connect(self.nudgeTabNext)
|
|
self.shortcuts.tabUp.activated.connect(self.nudgeTabNext)
|
|
self.shortcuts.tabLast.activated.connect(self.nudgeTabLast)
|
|
self.shortcuts.tabDn.activated.connect(self.nudgeTabLast)
|
|
|
|
self.initTheme(self.mainwindow.theme)
|
|
self.layout = QtWidgets.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)
|
|
|
|
"""
|
|
There are two instances of "convoHasFocus" for some reason?
|
|
This one seems to just get redefined.
|
|
|
|
def convoHasFocus(self, convo):
|
|
if ((self.hasFocus() or self.tabs.hasFocus()) and
|
|
self.tabs.tabText(self.tabs.currentIndex()) == convo.title()):
|
|
return True
|
|
"""
|
|
|
|
def isBot(self, *args, **kwargs):
|
|
return self.mainwindow.isBot(*args, **kwargs)
|
|
|
|
def keyPressEvent(self, event):
|
|
# TODO: Clean this up. Our text areas now call this.
|
|
keypress = event.key()
|
|
mods = event.modifiers()
|
|
if (
|
|
mods & QtCore.Qt.KeyboardModifier.ControlModifier
|
|
) and keypress == QtCore.Qt.Key.Key_Tab:
|
|
handles = list(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)
|
|
|
|
@QtCore.pyqtSlot()
|
|
def nudgeTabNext(self):
|
|
return self.nudgeTabIndex(+1)
|
|
|
|
@QtCore.pyqtSlot()
|
|
def nudgeTabLast(self):
|
|
return self.nudgeTabIndex(-1)
|
|
|
|
def nudgeTabIndex(self, direction):
|
|
# Inverted controls. Might add an option for this if people want
|
|
# it.
|
|
# ~if keypress == QtCore.Qt.Key.Key_PageDown:
|
|
# ~ direction = 1
|
|
# ~elif keypress == QtCore.Qt.Key.Key_PageUp:
|
|
# ~ direction = -1
|
|
# ...Processing...
|
|
tabs = self.tabs
|
|
# Pick our new index by sliding up or down the tab range.
|
|
# NOTE: This feels like it could error. In fact, it /will/ if
|
|
# there are no tabs, but...that shouldn't happen, should it?
|
|
# There are probably other scenarios, too, so we'll have to
|
|
# check on this later.
|
|
#
|
|
# Calculate the new index.
|
|
ct = tabs.count()
|
|
cind = tabs.currentIndex()
|
|
nind = cind + direction
|
|
if nind > (ct - 1):
|
|
# The new index would be higher than the maximum; loop.
|
|
nind = nind % ct
|
|
# Otherwise, negative syntax should get it for us.
|
|
nind = list(range(ct))[nind]
|
|
# Change to the selected tab.
|
|
# Note that this will send out the usual callbacks that handle
|
|
# focusing and such.
|
|
tabs.setCurrentIndex(nind)
|
|
|
|
def contextMenuEvent(self, event):
|
|
# ~if event.reason() == QtGui.QContextMenuEvent.Reason.Mouse:
|
|
tabi = self.tabs.tabAt(event.pos())
|
|
if tabi < 0:
|
|
tabi = self.tabs.currentIndex()
|
|
for h, i in list(self.tabIndices.items()):
|
|
if i == tabi:
|
|
# Our index matches, grab the object using our handle.
|
|
convo = self.convos[h]
|
|
break
|
|
else:
|
|
# No matches
|
|
return
|
|
# Pop up the options menu of the relevant tab.
|
|
convo.contextMenuEvent(event)
|
|
|
|
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 = str(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
|
|
# TODO: Let us disable this.
|
|
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(QtWidgets.QTabBar.Shape(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 list(self.convos.values()):
|
|
tabi = self.tabIndices[c.title()]
|
|
self.tabs.setTabIcon(tabi, c.icon())
|
|
currenttabi = self.tabs.currentIndex()
|
|
if currenttabi >= 0:
|
|
currentHandle = str(self.tabs.tabText(self.tabs.currentIndex()))
|
|
self.setWindowIcon(self.convos[currentHandle].icon())
|
|
self.defaultTabTextColor = self.getTabTextColor()
|
|
|
|
@QtCore.pyqtSlot(int)
|
|
def tabClose(self, i):
|
|
handle = str(self.tabs.tabText(i))
|
|
self.mainwindow.waitingMessages.messageAnswered(handle)
|
|
# print(self.convos.keys())
|
|
# I, legit don' t know why this is an issue, but, uh, yeah-
|
|
try:
|
|
convo = self.convos[handle]
|
|
except:
|
|
# handle = handle.replace("&","")
|
|
handle = "".join(handle.split("&", 1))
|
|
convo = self.convos[handle]
|
|
del self.convos[handle]
|
|
del self.tabIndices[handle]
|
|
self.tabs.removeTab(i)
|
|
for (h, j) in self.tabIndices.items():
|
|
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 = str(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 = str(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):
|
|
super(PesterMovie, self).__init__(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 = str(text.toHtml())
|
|
if html.find(url) != -1:
|
|
try:
|
|
# PyQt6
|
|
resource_type = QtGui.QTextDocument.ResourceType.ImageResource.value
|
|
except AttributeError:
|
|
# PyQt5
|
|
resource_type = QtGui.QTextDocument.ResourceType.ImageResource
|
|
if text.hasTabs:
|
|
i = text.tabobject.tabIndices[text.parent().title()]
|
|
if text.tabobject.tabs.currentIndex() == i:
|
|
text.document().addResource(
|
|
resource_type,
|
|
text.urls[movie],
|
|
movie.currentPixmap(),
|
|
)
|
|
text.setLineWrapColumnOrWidth(text.lineWrapColumnOrWidth())
|
|
else:
|
|
text.document().addResource(
|
|
resource_type,
|
|
text.urls[movie],
|
|
movie.currentPixmap(),
|
|
)
|
|
text.setLineWrapColumnOrWidth(text.lineWrapColumnOrWidth())
|
|
|
|
|
|
class PesterText(QtWidgets.QTextEdit):
|
|
def __init__(self, theme, parent=None):
|
|
super(PesterText, self).__init__(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.copyAvailable[bool].connect(self.textReady)
|
|
self.urls = {}
|
|
self.lastmsg = None
|
|
for k in smiledict:
|
|
self.addAnimation(
|
|
QtCore.QUrl("smilies/%s" % (smiledict[k])),
|
|
"smilies/%s" % (smiledict[k]),
|
|
)
|
|
# self.mainwindow.animationSetting[bool].connect(self.animateChanged)
|
|
|
|
def addAnimation(self, url, fileName):
|
|
# We don't need to treat images formats like .png as animation,
|
|
# this always opens a file handler otherwise, "movie.frameCount() > 1" isn't sufficient.
|
|
# Might be useful to use QImageReader's supportsAnimation function for this?
|
|
# As long as we're only using gifs there's no advantage to that though.
|
|
if not fileName.endswith(".gif"):
|
|
return
|
|
|
|
movie = PesterMovie(self)
|
|
movie.setFileName(fileName)
|
|
self.urls[movie] = url
|
|
movie.frameChanged[int].connect(movie.animate)
|
|
|
|
"""
|
|
@QtCore.pyqtSlot(bool)
|
|
def animateChanged(self, animate):
|
|
PchumLog.warning("aaa")
|
|
|
|
if animate:
|
|
for m in self.urls:
|
|
html = str(self.toHtml())
|
|
if html.find(self.urls[m].toString()) != -1:
|
|
if m.frameCount() > 1:
|
|
m.start()
|
|
else:
|
|
for m in self.urls:
|
|
html = str(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 "convo/scrollbar" in theme:
|
|
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.MovieState.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("<c=%s>" % (color), color), "%s: " % (initials)]
|
|
lexmsg.append(colorEnd("</c>"))
|
|
self.append(
|
|
'<span style="color:#000000">' + time + convertTags(lexmsg) + "</span>"
|
|
)
|
|
# self.append('<img src="/Users/lexi/pesterchum-lex/smilies/tab.gif" />'
|
|
# + '<img src="/Users/lexi/pesterchum/smilies/tab.gif" />'
|
|
# + '<img src="/Applications/Pesterchum.app/Contents/Resources/smilies/tab.gif" />'
|
|
# + '<img src="smilies/tab.gif" />');
|
|
if chum is me:
|
|
window.chatlog.log(parent.chum.handle, lexmsg)
|
|
else:
|
|
if (
|
|
(window.idler.auto or window.idler.manual)
|
|
and parent.chumopen
|
|
and not parent.isBot(chum.handle)
|
|
):
|
|
idlethreshhold = 60
|
|
do_idle_send = False
|
|
if self.lastmsg is None:
|
|
do_idle_send = True
|
|
else:
|
|
if datetime.now() - self.lastmsg > timedelta(0, idlethreshhold):
|
|
do_idle_send = True
|
|
if do_idle_send:
|
|
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()
|
|
QtWidgets.QTextEdit.focusInEvent(self, event)
|
|
|
|
def isBot(self, *args, **kwargs):
|
|
return self.parent().isBot(*args, **kwargs)
|
|
|
|
def keyPressEvent(self, event):
|
|
# First parent is the PesterConvo containing this.
|
|
# Second parent is the PesterTabWindow containing *it*.
|
|
pass_to_super = (
|
|
QtCore.Qt.Key.Key_PageUp,
|
|
QtCore.Qt.Key.Key_PageDown,
|
|
QtCore.Qt.Key.Key_Up,
|
|
QtCore.Qt.Key.Key_Down,
|
|
)
|
|
parent = self.parent()
|
|
key = event.key()
|
|
# keymods = event.modifiers()
|
|
if hasattr(parent, "textInput") and key not in pass_to_super:
|
|
# TODO: Shift focus here on bare (no modifiers) alphanumerics.
|
|
parent.textInput.keyPressEvent(event)
|
|
|
|
# Pass to the normal handler.
|
|
super(PesterText, self).keyPressEvent(event)
|
|
|
|
def mousePressEvent(self, event):
|
|
if event.button() == QtCore.Qt.MouseButton.LeftButton:
|
|
try:
|
|
# PyQt6
|
|
url = self.anchorAt(event.position().toPoint())
|
|
except AttributeError:
|
|
# PyQt5
|
|
url = self.anchorAt(event.pos())
|
|
if url != "":
|
|
if url[0] == "#" and url != "#pesterchum":
|
|
self.parent().mainwindow.showMemos(url[1:])
|
|
elif url[0] == "@":
|
|
handle = str(url[1:])
|
|
self.parent().mainwindow.newConversation(handle)
|
|
else:
|
|
if event.modifiers() == QtCore.Qt.KeyboardModifier.ControlModifier:
|
|
QtWidgets.QApplication.clipboard().setText(url)
|
|
else:
|
|
QtGui.QDesktopServices.openUrl(
|
|
QtCore.QUrl(url, QtCore.QUrl.ParsingMode.TolerantMode)
|
|
)
|
|
QtWidgets.QTextEdit.mousePressEvent(self, event)
|
|
|
|
def mouseMoveEvent(self, event):
|
|
QtWidgets.QTextEdit.mouseMoveEvent(self, event)
|
|
try:
|
|
# PyQt6
|
|
pos = event.position().toPoint()
|
|
except AttributeError:
|
|
# PyQt5
|
|
pos = event.pos()
|
|
if self.anchorAt(pos):
|
|
if (
|
|
self.viewport().cursor().shape
|
|
!= QtCore.Qt.CursorShape.PointingHandCursor
|
|
):
|
|
self.viewport().setCursor(
|
|
QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)
|
|
)
|
|
else:
|
|
self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.IBeamCursor))
|
|
|
|
def contextMenuEvent(self, event):
|
|
textMenu = self.createStandardContextMenu()
|
|
textMenu.exec(event.globalPos())
|
|
|
|
|
|
class PesterInput(QtWidgets.QLineEdit):
|
|
stylesheet_path = "convo/input/style"
|
|
|
|
def __init__(self, theme, parent=None):
|
|
super(PesterInput, self).__init__(parent)
|
|
self.changeTheme(theme)
|
|
|
|
def changeTheme(self, theme):
|
|
# Explicitly set color if not already set.
|
|
# (Some platforms seem to default to white instead of black.)
|
|
|
|
StyleSheet = theme[self.stylesheet_path]
|
|
if "color:" not in theme[self.stylesheet_path].replace(" ", ""):
|
|
StyleSheet = "color: black; " + StyleSheet
|
|
self.setStyleSheet(StyleSheet)
|
|
|
|
def focusInEvent(self, event):
|
|
self.parent().clearNewMessage()
|
|
self.parent().textArea.textCursor().clearSelection()
|
|
super(PesterInput, self).focusInEvent(event)
|
|
|
|
def keyPressEvent(self, event):
|
|
if event.key() == QtCore.Qt.Key.Key_Up:
|
|
text = str(self.text())
|
|
next = self.parent().history.next(text)
|
|
if next is not None:
|
|
self.setText(next)
|
|
elif event.key() == QtCore.Qt.Key.Key_Down:
|
|
prev = self.parent().history.prev()
|
|
if prev is not None:
|
|
self.setText(prev)
|
|
elif event.key() in [QtCore.Qt.Key.Key_PageUp, QtCore.Qt.Key.Key_PageDown]:
|
|
self.parent().textArea.keyPressEvent(event)
|
|
self.parent().mainwindow.idler.time = 0
|
|
super(PesterInput, self).keyPressEvent(event)
|
|
|
|
|
|
class PesterConvo(QtWidgets.QFrame):
|
|
def __init__(self, chum, initiated, mainwindow, parent=None):
|
|
super(PesterConvo, self).__init__(parent)
|
|
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_QuitOnClose, False)
|
|
self.setObjectName(chum.handle)
|
|
self.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)
|
|
self.chum = chum
|
|
self.mainwindow = mainwindow
|
|
theme = self.mainwindow.theme
|
|
self.resize(*theme["convo/size"])
|
|
self.setStyleSheet(
|
|
"QtWidgets.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 = QtWidgets.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(
|
|
QtWidgets.QSizePolicy(
|
|
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
|
|
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
|
|
)
|
|
)
|
|
self.textArea = PesterText(self.mainwindow.theme, self)
|
|
self.textInput = PesterInput(self.mainwindow.theme, self)
|
|
self.textInput.setFocus()
|
|
|
|
self.textInput.returnPressed.connect(self.sentMessage)
|
|
|
|
self.layout = QtWidgets.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 = QtWidgets.QMenu(self)
|
|
self.optionsMenu.setStyleSheet(
|
|
self.mainwindow.theme["main/defaultwindow/style"]
|
|
)
|
|
self.addChumAction = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self
|
|
)
|
|
self.addChumAction.triggered.connect(self.addThisChum)
|
|
self.blockAction = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/blockchum"], self
|
|
)
|
|
self.blockAction.triggered.connect(self.blockThisChum)
|
|
self.quirksOff = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self
|
|
)
|
|
self.quirksOff.setCheckable(True)
|
|
self.quirksOff.toggled[bool].connect(self.toggleQuirks)
|
|
self.oocToggle = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self
|
|
)
|
|
self.oocToggle.setCheckable(True)
|
|
self.oocToggle.toggled[bool].connect(self.toggleOOC)
|
|
self.unblockchum = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"], self
|
|
)
|
|
self.unblockchum.triggered.connect(self.unblockChumSlot)
|
|
self.reportchum = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/report"], self
|
|
)
|
|
self.reportchum.triggered.connect(self.reportThisChum)
|
|
self.logchum = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self
|
|
)
|
|
self.logchum.triggered.connect(self.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.
|
|
|
|
# Theme support :3c
|
|
# if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"):
|
|
try:
|
|
self._beepToggle = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"], self
|
|
)
|
|
except:
|
|
self._beepToggle = QAction("BEEP ON MESSAGE", self)
|
|
self._beepToggle.setCheckable(True)
|
|
self._beepToggle.toggled[bool].connect(self.toggleBeep)
|
|
|
|
# if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"):
|
|
try:
|
|
self._flashToggle = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"], self
|
|
)
|
|
except:
|
|
self._flashToggle = QAction("FLASH ON MESSAGE", self)
|
|
self._flashToggle.setCheckable(True)
|
|
self._flashToggle.toggled[bool].connect(self.toggleFlash)
|
|
|
|
# if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"):
|
|
try:
|
|
self._muteToggle = QAction(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"],
|
|
self,
|
|
)
|
|
except:
|
|
self._muteToggle = QAction("MUTE NOTIFICATIONS", self)
|
|
self._muteToggle.setCheckable(True)
|
|
self._muteToggle.toggled[bool].connect(self.toggleMute)
|
|
|
|
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 isBot(self, *args, **kwargs):
|
|
return self.parent().isBot(*args, **kwargs)
|
|
|
|
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):
|
|
PchumLog.debug("convo updateColor: " + str(color))
|
|
self.chum.color = color
|
|
|
|
def addMessage(self, msg, me=True):
|
|
if type(msg) in [str, str]:
|
|
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):
|
|
# Our imports have to be here to prevent circular import issues.
|
|
from memos import PesterMemo, MemoTabWindow
|
|
|
|
# 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
|
|
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:
|
|
# Just let the icon highlight normally.
|
|
# This function *also* highlights the tab, mind.
|
|
parent.notifyNewMessage(title)
|
|
if not mutednots:
|
|
# Remember that these two are descended from one another.
|
|
# TODO: Make these obey subclassing rules...ugh.
|
|
# They should really just use the class's function and do
|
|
# the checks there.
|
|
# PesterTabWindow -> MemoTabWindow
|
|
if isinstance(parent, MemoTabWindow):
|
|
if self.always_flash or memoblink:
|
|
self.mainwindow.gainAttention.emit(parent)
|
|
elif isinstance(parent, PesterTabWindow):
|
|
if self.always_flash or pesterblink:
|
|
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 mutednots:
|
|
# Once again, PesterMemo inherits from PesterConvo.
|
|
if isinstance(self, PesterMemo):
|
|
if self.always_flash or memoblink:
|
|
self.mainwindow.gainAttention.emit(self)
|
|
elif isinstance(self, PesterConvo):
|
|
if self.always_flash or pesterblink:
|
|
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.Reason.Mouse:
|
|
self.optionsMenu.popup(event.globalPos())
|
|
|
|
def closeEvent(self, event):
|
|
self.mainwindow.waitingMessages.messageAnswered(self.title())
|
|
for movie in self.textArea.urls.copy():
|
|
movie.setFileName("") # Required, sometimes, for some reason. . .
|
|
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(
|
|
"QtWidgets.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(
|
|
QtWidgets.QSizePolicy(
|
|
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
|
|
QtWidgets.QSizePolicy.Policy.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"])
|
|
|
|
# if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"):
|
|
try:
|
|
self._beepToggle.setText(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"]
|
|
)
|
|
except:
|
|
self._beepToggle.setText("BEEP ON MESSAGE")
|
|
|
|
# if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"):
|
|
try:
|
|
self._flashToggle.setText(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"]
|
|
)
|
|
except:
|
|
self._flashToggle.setText("FLASH ON MESSAGE", self)
|
|
|
|
# if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"):
|
|
try:
|
|
self._muteToggle.setText(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"]
|
|
)
|
|
except:
|
|
self._muteToggle.setText("MUTE NOTIFICATIONS")
|
|
|
|
# if self.mainwindow.theme.has_key("main/menus/rclickchumlist/report"):
|
|
try:
|
|
self.reportchum.setText(
|
|
self.mainwindow.theme["main/menus/rclickchumlist/report"]
|
|
)
|
|
except:
|
|
pass
|
|
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 = str(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.mainwindow.chumList.pesterlogviewer.rejected.connect(
|
|
self.mainwindow.chumList.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("QString", "QString")
|
|
windowClosed = QtCore.pyqtSignal("QString")
|
|
|
|
aligndict = {
|
|
"h": {
|
|
"center": QtCore.Qt.AlignmentFlag.AlignHCenter,
|
|
"left": QtCore.Qt.AlignmentFlag.AlignLeft,
|
|
"right": QtCore.Qt.AlignmentFlag.AlignRight,
|
|
},
|
|
"v": {
|
|
"center": QtCore.Qt.AlignmentFlag.AlignVCenter,
|
|
"top": QtCore.Qt.AlignmentFlag.AlignTop,
|
|
"bottom": QtCore.Qt.AlignmentFlag.AlignBottom,
|
|
},
|
|
}
|
|
|
|
|
|
# the import is way down here to avoid recursive imports
|
|
from logviewer import PesterLogViewer
|