import re import logging from os import remove try: from PyQt6 import QtCore, QtGui, QtWidgets, QtMultimedia from PyQt6.QtGui import QAction except ImportError: print("PyQt5 fallback (menus.py)") from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia from PyQt5.QtWidgets import QAction import ostools import parsetools from generic import RightClickList, RightClickTree, MultiTextDialog from dataobjs import pesterQuirk, PesterProfile, PesterHistory from memos import TimeSlider, TimeInput from version import _pcVersion from convo import PesterInput, PesterText from parsetools import lexMessage _datadir = ostools.getDataDir() # Logger PchumLog = logging.getLogger("pchumLogger") class PesterQuirkItem(QtWidgets.QTreeWidgetItem): def __init__(self, quirk): parent = None QtWidgets.QTreeWidgetItem.__init__(self, parent) self.quirk = quirk self.setText(0, str(quirk)) # Typecast required. def update(self, quirk): self.quirk = quirk self.setText(0, str(quirk)) def __lt__(self, quirkitem): """Sets the order of quirks if auto-sorted by Qt. Obsolete now.""" if self.quirk.type == "prefix": return True elif self.quirk.type in ("replace", "regexp") and quirkitem.type == "suffix": return True else: return False class PesterQuirkList(QtWidgets.QTreeWidget): def __init__(self, mainwindow, parent): QtWidgets.QTreeWidget.__init__(self, parent) self.resize(400, 200) # make sure we have access to mainwindow info like profiles self.mainwindow = mainwindow self.setStyleSheet("background:black; color:white;") self.itemChanged[QtWidgets.QTreeWidgetItem, int].connect(self.changeCheckState) for q in mainwindow.userprofile.quirks: item = PesterQuirkItem(q) self.addItem(item, False) self.changeCheckState() # self.setDragEnabled(True) # self.setDragDropMode(QtGui.QAbstractItemView.DragDropMode.InternalMove) self.setDropIndicatorShown(True) self.setSortingEnabled(False) self.setIndentation(15) self.header().hide() def addItem(self, item, new=True): item.setFlags( QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsDragEnabled | QtCore.Qt.ItemFlag.ItemIsUserCheckable | QtCore.Qt.ItemFlag.ItemIsEnabled ) if item.quirk.on: item.setCheckState(0, QtCore.Qt.CheckState.Checked) else: item.setCheckState(0, QtCore.Qt.CheckState.Unchecked) if new: curgroup = self.currentItem() if curgroup: if curgroup.parent(): curgroup = curgroup.parent() item.quirk.quirk["group"] = item.quirk.group = curgroup.text(0) found = self.findItems(item.quirk.group, QtCore.Qt.MatchFlag.MatchExactly) if len(found) > 0: found[0].addChild(item) else: child_1 = QtWidgets.QTreeWidgetItem([item.quirk.group]) self.addTopLevelItem(child_1) child_1.setFlags( child_1.flags() | QtCore.Qt.ItemFlag.ItemIsUserCheckable | QtCore.Qt.ItemFlag.ItemIsEnabled ) child_1.setChildIndicatorPolicy( QtWidgets.QTreeWidgetItem.ChildIndicatorPolicy.DontShowIndicatorWhenChildless ) child_1.setCheckState(0, QtCore.Qt.CheckState.Unchecked) child_1.setExpanded(True) child_1.addChild(item) self.changeCheckState() def currentQuirk(self): if isinstance(self.currentItem(), PesterQuirkItem): return self.currentItem() else: return None @QtCore.pyqtSlot() def upShiftQuirk(self): found = self.findItems( self.currentItem().text(0), QtCore.Qt.MatchFlag.MatchExactly ) if len(found): # group i = self.indexOfTopLevelItem(found[0]) if i > 0: expand = found[0].isExpanded() shifted_item = self.takeTopLevelItem(i) self.insertTopLevelItem(i - 1, shifted_item) shifted_item.setExpanded(expand) self.setCurrentItem(shifted_item) else: # quirk found = self.findItems( self.currentItem().text(0), QtCore.Qt.MatchFlag.MatchExactly | QtCore.Qt.MatchFlag.MatchRecursive, ) for f in found: if not f.isSelected(): continue if not f.parent(): continue i = f.parent().indexOfChild(f) if i > 0: # keep in same group p = f.parent() shifted_item = f.parent().takeChild(i) p.insertChild(i - 1, shifted_item) self.setCurrentItem(shifted_item) else: # move to another group j = self.indexOfTopLevelItem(f.parent()) if j <= 0: continue shifted_item = f.parent().takeChild(i) self.topLevelItem(j - 1).addChild(shifted_item) self.setCurrentItem(shifted_item) self.changeCheckState() @QtCore.pyqtSlot() def downShiftQuirk(self): found = self.findItems( self.currentItem().text(0), QtCore.Qt.MatchFlag.MatchExactly ) if len(found): # group i = self.indexOfTopLevelItem(found[0]) if i < self.topLevelItemCount() - 1 and i >= 0: expand = found[0].isExpanded() shifted_item = self.takeTopLevelItem(i) self.insertTopLevelItem(i + 1, shifted_item) shifted_item.setExpanded(expand) self.setCurrentItem(shifted_item) else: # quirk found = self.findItems( self.currentItem().text(0), QtCore.Qt.MatchFlag.MatchExactly | QtCore.Qt.MatchFlag.MatchRecursive, ) for f in found: if not f.isSelected(): continue if not f.parent(): continue i = f.parent().indexOfChild(f) if i < f.parent().childCount() - 1 and i >= 0: p = f.parent() shifted_item = f.parent().takeChild(i) p.insertChild(i + 1, shifted_item) self.setCurrentItem(shifted_item) else: j = self.indexOfTopLevelItem(f.parent()) if j >= self.topLevelItemCount() - 1 or j < 0: continue shifted_item = f.parent().takeChild(i) self.topLevelItem(j + 1).insertChild(0, shifted_item) self.setCurrentItem(shifted_item) self.changeCheckState() @QtCore.pyqtSlot() def removeCurrent(self): i = self.currentItem() found = self.findItems( i.text(0), QtCore.Qt.MatchFlag.MatchExactly | QtCore.Qt.MatchFlag.MatchRecursive, ) for f in found: if not f.isSelected(): continue if not f.parent(): # group msgbox = QtWidgets.QMessageBox() msgbox.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) msgbox.setObjectName("delquirkwarning") msgbox.setWindowTitle("WARNING!") msgbox.setInformativeText( "Are you sure you want to delete the quirk group: %s" % (f.text(0)) ) msgbox.setStandardButtons( QtWidgets.QMessageBox.StandardButton.Ok | QtWidgets.QMessageBox.StandardButton.Cancel ) # Find the Cancel button and make it default for b in msgbox.buttons(): if ( msgbox.buttonRole(b) == QtWidgets.QMessageBox.ButtonRole.RejectRole ): # We found the 'OK' button, set it as the default b.setDefault(True) b.setAutoDefault(True) # Actually set it as the selected option, since we're # already stealing focus b.setFocus() break ret = msgbox.exec() if ret == QtWidgets.QMessageBox.StandardButton.Ok: self.takeTopLevelItem(self.indexOfTopLevelItem(f)) else: f.parent().takeChild(f.parent().indexOfChild(f)) self.changeCheckState() @QtCore.pyqtSlot() def addQuirkGroup(self): if not hasattr(self, "addgroupdialog"): self.addgroupdialog = None if not self.addgroupdialog: (gname, ok) = QtWidgets.QInputDialog.getText( self, "Add Group", "Enter a name for the new quirk group:" ) if ok: if re.search(r"[^A-Za-z0-9_\s]", gname) is not None: msgbox = QtWidgets.QMessageBox() msgbox.setInformativeText("THIS IS NOT A VALID GROUP NAME") msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) msgbox.exec() self.addgroupdialog = None return found = self.findItems(gname, QtCore.Qt.MatchFlag.MatchExactly) if found: msgbox = QtWidgets.QMessageBox() msgbox.setInformativeText("THIS QUIRK GROUP ALREADY EXISTS") msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) msgbox.exec() return child_1 = QtWidgets.QTreeWidgetItem([gname]) self.addTopLevelItem(child_1) child_1.setFlags( child_1.flags() | QtCore.Qt.ItemFlag.ItemIsUserCheckable | QtCore.Qt.ItemFlag.ItemIsEnabled ) child_1.setChildIndicatorPolicy( QtWidgets.QTreeWidgetItem.ChildIndicatorPolicy.DontShowIndicatorWhenChildless ) child_1.setCheckState(0, QtCore.Qt.CheckState.Unchecked) child_1.setExpanded(True) self.addgroupdialog = None @QtCore.pyqtSlot() def changeCheckState(self): index = self.indexOfTopLevelItem(self.currentItem()) if index == -1: for i in range(self.topLevelItemCount()): allChecked = True noneChecked = True for j in range(self.topLevelItem(i).childCount()): if self.topLevelItem(i).child(j).checkState(0): noneChecked = False else: allChecked = False if allChecked: self.topLevelItem(i).setCheckState(0, QtCore.Qt.CheckState.Checked) elif noneChecked: self.topLevelItem(i).setCheckState( 0, QtCore.Qt.CheckState.PartiallyChecked ) else: self.topLevelItem(i).setCheckState(0, QtCore.Qt.CheckState.Checked) else: state = self.topLevelItem(index).checkState(0) for j in range(self.topLevelItem(index).childCount()): self.topLevelItem(index).child(j).setCheckState(0, state) class QuirkTesterWindow(QtWidgets.QDialog): def __init__(self, parent): QtWidgets.QDialog.__init__(self, parent) self.prnt = parent self.mainwindow = parent.mainwindow self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) self.setWindowTitle("Quirk Tester") self.resize(350, 300) self.textArea = PesterText(self.mainwindow.theme, self) self.textInput = PesterInput(self.mainwindow.theme, self) self.textInput.setFocus() self.textInput.returnPressed.connect(self.sentMessage) self.chumopen = True self.chum = self.mainwindow.profile() self.history = PesterHistory() layout_0 = QtWidgets.QVBoxLayout() layout_0.addWidget(self.textArea) layout_0.addWidget(self.textInput) self.setLayout(layout_0) def parent(self): return self.prnt def clearNewMessage(self): pass @QtCore.pyqtSlot() def sentMessage(self): text = self.textInput.text() return parsetools.kxhandleInput( self, text, "menus", irc_compatible=self.mainwindow.config.irc_compatibility_mode(), ) def addMessage(self, msg, me=True): if isinstance(msg, str): lexmsg = lexMessage(msg) else: lexmsg = msg if me: chum = self.mainwindow.profile() else: chum = self.chum self.textArea.addMessage(lexmsg, chum) def closeEvent(self, event): self.parent().quirktester = None class PesterQuirkTypes(QtWidgets.QDialog): def __init__(self, parent, quirk=None): QtWidgets.QDialog.__init__(self, parent) self.mainwindow = parent.mainwindow self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) self.setWindowTitle("Quirk Wizard") self.resize(500, 310) self.quirk = quirk self.pages = QtWidgets.QStackedWidget(self) self.next = QtWidgets.QPushButton("Next", self) self.next.setDefault(True) self.next.clicked.connect(self.nextPage) self.back = QtWidgets.QPushButton("Back", self) self.back.setEnabled(False) self.back.clicked.connect(self.backPage) self.cancel = QtWidgets.QPushButton("Cancel", self) self.cancel.clicked.connect(self.reject) layout_2 = QtWidgets.QHBoxLayout() layout_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight) layout_2.addWidget(self.back) layout_2.addWidget(self.next) layout_2.addSpacing(5) layout_2.addWidget(self.cancel) vr = QtWidgets.QFrame() vr.setFrameShape(QtWidgets.QFrame.Shape.VLine) vr.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) vr2 = QtWidgets.QFrame() vr2.setFrameShape(QtWidgets.QFrame.Shape.VLine) vr2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.funclist = QtWidgets.QListWidget(self) self.funclist.setStyleSheet("color: #000000; background-color: #FFFFFF;") self.funclist2 = QtWidgets.QListWidget(self) self.funclist2.setStyleSheet("color: #000000; background-color: #FFFFFF;") from parsetools import quirkloader funcs = [q + "()" for q in list(quirkloader.quirks.keys())] funcs.sort() self.funclist.addItems(funcs) self.funclist2.addItems(funcs) self.reloadQuirkFuncButton = QtWidgets.QPushButton("RELOAD FUNCTIONS", self) self.reloadQuirkFuncButton.clicked.connect(self.reloadQuirkFuncSlot) self.reloadQuirkFuncButton2 = QtWidgets.QPushButton("RELOAD FUNCTIONS", self) self.reloadQuirkFuncButton2.clicked.connect(self.reloadQuirkFuncSlot) self.funclist.setMaximumWidth(160) self.funclist.resize(160, 50) self.funclist2.setMaximumWidth(160) self.funclist2.resize(160, 50) layout_f = QtWidgets.QVBoxLayout() layout_f.addWidget(QtWidgets.QLabel("Available Regexp\nFunctions")) layout_f.addWidget(self.funclist) layout_f.addWidget(self.reloadQuirkFuncButton) layout_g = QtWidgets.QVBoxLayout() layout_g.addWidget(QtWidgets.QLabel("Available Regexp\nFunctions")) layout_g.addWidget(self.funclist2) layout_g.addWidget(self.reloadQuirkFuncButton2) # Pages # Type select widget = QtWidgets.QWidget() self.pages.addWidget(widget) layout_select = QtWidgets.QVBoxLayout(widget) layout_select.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) self.radios = [] self.radios.append(QtWidgets.QRadioButton("Prefix", self)) self.radios.append(QtWidgets.QRadioButton("Suffix", self)) self.radios.append(QtWidgets.QRadioButton("Simple Replace", self)) self.radios.append(QtWidgets.QRadioButton("Regexp Replace", self)) self.radios.append(QtWidgets.QRadioButton("Random Replace", self)) self.radios.append(QtWidgets.QRadioButton("Mispeller", self)) layout_select.addWidget(QtWidgets.QLabel("Select Quirk Type:")) for r in self.radios: layout_select.addWidget(r) # Prefix widget = QtWidgets.QWidget() self.pages.addWidget(widget) layout_prefix = QtWidgets.QVBoxLayout(widget) layout_prefix.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_prefix.addWidget(QtWidgets.QLabel("Prefix")) layout_3 = QtWidgets.QHBoxLayout() layout_3.addWidget(QtWidgets.QLabel("Value:")) layout_3.addWidget(QtWidgets.QLineEdit()) layout_prefix.addLayout(layout_3) # Suffix widget = QtWidgets.QWidget() self.pages.addWidget(widget) layout_suffix = QtWidgets.QVBoxLayout(widget) layout_suffix.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_suffix.addWidget(QtWidgets.QLabel("Suffix")) layout_3 = QtWidgets.QHBoxLayout() layout_3.addWidget(QtWidgets.QLabel("Value:")) layout_3.addWidget(QtWidgets.QLineEdit()) layout_suffix.addLayout(layout_3) # Simple Replace widget = QtWidgets.QWidget() self.pages.addWidget(widget) layout_replace = QtWidgets.QVBoxLayout(widget) layout_replace.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_replace.addWidget(QtWidgets.QLabel("Simple Replace")) layout_3 = QtWidgets.QHBoxLayout() layout_3.addWidget(QtWidgets.QLabel("Replace:")) layout_3.addWidget(QtWidgets.QLineEdit()) layout_replace.addLayout(layout_3) layout_3 = QtWidgets.QHBoxLayout() layout_3.addWidget(QtWidgets.QLabel("With:")) layout_3.addWidget(QtWidgets.QLineEdit()) layout_replace.addLayout(layout_3) layout_3 = QtWidgets.QHBoxLayout() excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies") excludeCheckbox.setToolTip( "Splits input to exclude smilies, weblinks, @handles, and #memos." + "\nThe replace is applied on every substring individually." ) layout_3.addWidget(excludeCheckbox) layout_replace.addLayout(layout_3) # Regexp Replace widget = QtWidgets.QWidget() self.pages.addWidget(widget) layout_all = QtWidgets.QHBoxLayout(widget) layout_regexp = QtWidgets.QVBoxLayout() layout_regexp.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_regexp.addWidget(QtWidgets.QLabel("Regexp Replace")) layout_3 = QtWidgets.QHBoxLayout() layout_3.addWidget(QtWidgets.QLabel("Regexp:")) layout_3.addWidget(QtWidgets.QLineEdit()) layout_regexp.addLayout(layout_3) layout_3 = QtWidgets.QHBoxLayout() layout_3.addWidget(QtWidgets.QLabel("Replace With:")) layout_3.addWidget(QtWidgets.QLineEdit()) layout_regexp.addLayout(layout_3) layout_3 = QtWidgets.QHBoxLayout() excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies") excludeCheckbox.setToolTip( "Splits input to exclude smilies, weblinks, @handles, and #memos." + "\nSince the replace is applied on every substring individually," + "\ncertain patterns or functions like gradients may not work correctly." ) layout_3.addWidget(excludeCheckbox) layout_regexp.addLayout(layout_3) layout_all.addLayout(layout_f) layout_all.addWidget(vr) layout_all.addLayout(layout_regexp) # Random Replace widget = QtWidgets.QWidget() self.pages.addWidget(widget) layout_all = QtWidgets.QHBoxLayout(widget) layout_random = QtWidgets.QVBoxLayout() layout_random.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_random.addWidget(QtWidgets.QLabel("Random Replace")) layout_5 = QtWidgets.QHBoxLayout() regexpl = QtWidgets.QLabel("Regexp:", self) self.regexp = QtWidgets.QLineEdit("", self) layout_5.addWidget(regexpl) layout_5.addWidget(self.regexp) replacewithl = QtWidgets.QLabel("Replace With:", self) layout_all.addLayout(layout_g) layout_all.addWidget(vr2) layout_all.addLayout(layout_random) layout_6 = QtWidgets.QVBoxLayout() layout_7 = QtWidgets.QHBoxLayout() self.replacelist = QtWidgets.QListWidget(self) self.replaceinput = QtWidgets.QLineEdit(self) addbutton = QtWidgets.QPushButton("ADD", self) addbutton.clicked.connect(self.addRandomString) removebutton = QtWidgets.QPushButton("REMOVE", self) removebutton.clicked.connect(self.removeRandomString) layout_7.addWidget(addbutton) layout_7.addWidget(removebutton) layout_6.addLayout(layout_5) layout_6.addWidget(replacewithl) layout_6.addWidget(self.replacelist) layout_6.addWidget(self.replaceinput) layout_6.addLayout(layout_7) layout_random.addLayout(layout_6) layout_9 = QtWidgets.QHBoxLayout() excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies") excludeCheckbox.setToolTip( "Splits input to exclude smilies, weblinks, @handles, and #memos." + "\nSince the replace is applied on every substring individually," + "\ncertain patterns or functions like gradients may not work correctly." ) layout_9.addWidget(excludeCheckbox) layout_random.addLayout(layout_9) # Misspeller widget = QtWidgets.QWidget() self.pages.addWidget(widget) layout_mispeller = QtWidgets.QVBoxLayout(widget) layout_mispeller.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_mispeller.addWidget(QtWidgets.QLabel("Mispeller")) layout_1 = QtWidgets.QHBoxLayout() zero = QtWidgets.QLabel("1%", self) hund = QtWidgets.QLabel("100%", self) self.current = QtWidgets.QLabel("50%", self) self.current.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter) self.slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal, self) self.slider.setMinimum(1) self.slider.setMaximum(100) self.slider.setValue(50) self.slider.valueChanged[int].connect(self.printValue) layout_1.addWidget(zero) layout_1.addWidget(self.slider) layout_1.addWidget(hund) layout_mispeller.addLayout(layout_1) layout_mispeller.addWidget(self.current) layout_0 = QtWidgets.QVBoxLayout() layout_0.addWidget(self.pages) layout_0.addLayout(layout_2) layout_3 = QtWidgets.QHBoxLayout() excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies") excludeCheckbox.setToolTip( "Splits input to exclude smilies, weblinks, @handles, and #memos." + "\nThe replace is applied on every substring individually." ) layout_3.addWidget(excludeCheckbox) layout_mispeller.addLayout(layout_3) if quirk: types = ["prefix", "suffix", "replace", "regexp", "random", "spelling"] for i, r in enumerate(self.radios): if i == types.index(quirk.quirk.type): r.setChecked(True) self.changePage(types.index(quirk.quirk.type) + 1) page = self.pages.currentWidget().layout() q = quirk.quirk.quirk if q["type"] in ("prefix", "suffix"): page.itemAt(1).layout().itemAt(1).widget().setText(q["value"]) elif q["type"] == "replace": page.itemAt(1).layout().itemAt(1).widget().setText(q["from"]) page.itemAt(2).layout().itemAt(1).widget().setText(q["to"]) try: page.itemAt(3).layout().itemAt(0).widget().setCheckState( QtCore.Qt.CheckState(int(q["checkstate"])) ) except (KeyError, ValueError): PchumLog.exception("Exception setting replace quirk.") elif q["type"] == "regexp": page.itemAt(2).layout().itemAt(1).layout().itemAt(1).widget().setText( q["from"] ) page.itemAt(2).layout().itemAt(2).layout().itemAt(1).widget().setText( q["to"] ) try: page.itemAt(2).layout().itemAt(3).layout().itemAt( 0 ).widget().setCheckState(QtCore.Qt.CheckState(int(q["checkstate"]))) except (KeyError, ValueError): PchumLog.exception("Exception setting regexp quirk.") elif q["type"] == "random": self.regexp.setText(q["from"]) for v in q["randomlist"]: item = QtWidgets.QListWidgetItem(v, self.replacelist) try: page.itemAt(2).layout().itemAt(2).layout().itemAt( 0 ).widget().setCheckState(QtCore.Qt.CheckState(int(q["checkstate"]))) except (KeyError, ValueError): PchumLog.exception("Exception setting random quirk.") elif q["type"] == "spelling": self.slider.setValue(q["percentage"]) try: page.itemAt(3).layout().itemAt(0).widget().setCheckState( QtCore.Qt.CheckState(int(q["checkstate"])) ) except (KeyError, ValueError) as e: PchumLog.exception("Exception setting spelling quirk.") self.setLayout(layout_0) def closeEvent(self, event): self.parent().quirkadd = None def changePage(self, page): c = self.pages.count() if page >= c or page < 0: return self.back.setEnabled(page > 0) if page >= 1 and page <= 6: self.next.setText("Finish") else: self.next.setText("Next") self.pages.setCurrentIndex(page) @QtCore.pyqtSlot() def nextPage(self): if self.next.text() == "Finish": self.accept() return cur = self.pages.currentIndex() if cur == 0: for i, r in enumerate(self.radios): if r.isChecked(): self.changePage(i + 1) else: self.changePage(cur + 1) @QtCore.pyqtSlot() def backPage(self): cur = self.pages.currentIndex() if cur >= 1 and cur <= 6: self.changePage(0) @QtCore.pyqtSlot(int) def printValue(self, value): self.current.setText(f"{value}%") @QtCore.pyqtSlot() def addRandomString(self): text = self.replaceinput.text() item = QtWidgets.QListWidgetItem(text, self.replacelist) self.replaceinput.setText("") self.replaceinput.setFocus() @QtCore.pyqtSlot() def removeRandomString(self): if not self.replacelist.currentItem(): return else: self.replacelist.takeItem(self.replacelist.currentRow()) self.replaceinput.setFocus() @QtCore.pyqtSlot() def reloadQuirkFuncSlot(self): from parsetools import reloadQuirkFunctions, quirkloader reloadQuirkFunctions() funcs = [q + "()" for q in list(quirkloader.quirks.keys())] funcs.sort() self.funclist.clear() self.funclist.addItems(funcs) self.funclist2.clear() self.funclist2.addItems(funcs) class PesterChooseQuirks(QtWidgets.QDialog): def __init__(self, config, theme, parent): QtWidgets.QDialog.__init__(self, parent) self.setModal(False) self.config = config self.theme = theme self.mainwindow = parent self.setStyleSheet(self.theme["main/defaultwindow/style"]) self.setWindowTitle("Set Quirks") self.quirkList = PesterQuirkList(self.mainwindow, self) self.addQuirkButton = QtWidgets.QPushButton("ADD QUIRK", self) self.addQuirkButton.clicked.connect(self.addQuirkDialog) self.upShiftButton = QtWidgets.QPushButton("^", self) self.downShiftButton = QtWidgets.QPushButton("v", self) self.upShiftButton.setToolTip("Move quirk up one") self.downShiftButton.setToolTip("Move quirk down one") self.upShiftButton.clicked.connect(self.quirkList.upShiftQuirk) self.downShiftButton.clicked.connect(self.quirkList.downShiftQuirk) self.newGroupButton = QtWidgets.QPushButton("*", self) self.newGroupButton.setToolTip("New Quirk Group") self.newGroupButton.clicked.connect(self.quirkList.addQuirkGroup) layout_quirklist = QtWidgets.QHBoxLayout() # the nude layout quirklist layout_shiftbuttons = QtWidgets.QVBoxLayout() # the shift button layout layout_shiftbuttons.addWidget(self.upShiftButton) layout_shiftbuttons.addWidget(self.newGroupButton) layout_shiftbuttons.addWidget(self.downShiftButton) layout_quirklist.addWidget(self.quirkList) layout_quirklist.addLayout(layout_shiftbuttons) layout_1 = QtWidgets.QHBoxLayout() layout_1.addWidget(self.addQuirkButton) self.editSelectedButton = QtWidgets.QPushButton("EDIT", self) self.editSelectedButton.clicked.connect(self.editSelected) self.removeSelectedButton = QtWidgets.QPushButton("REMOVE", self) self.removeSelectedButton.clicked.connect(self.quirkList.removeCurrent) layout_3 = QtWidgets.QHBoxLayout() layout_3.addWidget(self.editSelectedButton) layout_3.addWidget(self.removeSelectedButton) self.ok = QtWidgets.QPushButton("OK", self) self.ok.setDefault(True) self.ok.clicked.connect(self.accept) self.test = QtWidgets.QPushButton("TEST QUIRKS", self) self.test.clicked.connect(self.testQuirks) self.cancel = QtWidgets.QPushButton("CANCEL", self) self.cancel.clicked.connect(self.reject) layout_ok = QtWidgets.QHBoxLayout() layout_ok.addWidget(self.cancel) layout_ok.addWidget(self.test) layout_ok.addWidget(self.ok) layout_0 = QtWidgets.QVBoxLayout() layout_0.addLayout(layout_quirklist) layout_0.addLayout(layout_1) # layout_0.addLayout(layout_2) layout_0.addLayout(layout_3) layout_0.addLayout(layout_ok) self.setLayout(layout_0) def quirks(self): u = [] for i in range(self.quirkList.topLevelItemCount()): for j in range(self.quirkList.topLevelItem(i).childCount()): u.append(self.quirkList.topLevelItem(i).child(j).quirk) return u # return [self.quirkList.item(i).quirk for i in range(self.quirkList.count())] def testquirks(self): u = [] for i in range(self.quirkList.topLevelItemCount()): for j in range(self.quirkList.topLevelItem(i).childCount()): item = self.quirkList.topLevelItem(i).child(j) if item.checkState(0) == QtCore.Qt.CheckState.Checked: u.append(item.quirk) return u @QtCore.pyqtSlot() def testQuirks(self): if not hasattr(self, "quirktester"): self.quirktester = None if self.quirktester: return self.quirktester = QuirkTesterWindow(self) self.quirktester.show() @QtCore.pyqtSlot() def editSelected(self): q = self.quirkList.currentQuirk() if not q: return # quirk = q.quirk self.addQuirkDialog(q) @QtCore.pyqtSlot() def addQuirkDialog(self, quirk=None): if not hasattr(self, "quirkadd"): self.quirkadd = None if self.quirkadd: return self.quirkadd = PesterQuirkTypes(self, quirk) self.quirkadd.accepted.connect(self.addQuirk) self.quirkadd.rejected.connect(self.closeQuirk) self.quirkadd.show() @QtCore.pyqtSlot() def addQuirk(self): types = ["prefix", "suffix", "replace", "regexp", "random", "spelling"] vdict = {} vdict["type"] = types[self.quirkadd.pages.currentIndex() - 1] page = self.quirkadd.pages.currentWidget().layout() if vdict["type"] in ("prefix", "suffix"): vdict["value"] = page.itemAt(1).layout().itemAt(1).widget().text() elif vdict["type"] == "replace": vdict["from"] = page.itemAt(1).layout().itemAt(1).widget().text() vdict["to"] = page.itemAt(2).layout().itemAt(1).widget().text() try: # PyQt6 vdict["checkstate"] = str( page.itemAt(3).layout().itemAt(0).widget().checkState().value ) except AttributeError: # PyQt5 vdict["checkstate"] = str( page.itemAt(3).layout().itemAt(0).widget().checkState() ) elif vdict["type"] == "regexp": vdict["from"] = ( page.itemAt(2).layout().itemAt(1).layout().itemAt(1).widget().text() ) vdict["to"] = ( page.itemAt(2).layout().itemAt(2).layout().itemAt(1).widget().text() ) try: # PyQt6 vdict["checkstate"] = str( page.itemAt(2) .layout() .itemAt(3) .layout() .itemAt(0) .widget() .checkState() .value ) except AttributeError: # PyQt5 vdict["checkstate"] = str( page.itemAt(2) .layout() .itemAt(3) .layout() .itemAt(0) .widget() .checkState() ) elif vdict["type"] == "random": vdict["from"] = str(self.quirkadd.regexp.text()) try: # PyQt6 vdict["checkstate"] = str( page.itemAt(2) .layout() .itemAt(2) .layout() .itemAt(0) .widget() .checkState() .value ) except AttributeError: # PyQt5 vdict["checkstate"] = str( page.itemAt(2) .layout() .itemAt(2) .layout() .itemAt(0) .widget() .checkState() ) randomlist = [ str(self.quirkadd.replacelist.item(i).text()) for i in range(0, self.quirkadd.replacelist.count()) ] vdict["randomlist"] = randomlist elif vdict["type"] == "spelling": vdict["percentage"] = self.quirkadd.slider.value() try: # PyQt6 vdict["checkstate"] = str( page.itemAt(3).layout().itemAt(0).widget().checkState().value ) except AttributeError: # PyQt5 vdict["checkstate"] = str( page.itemAt(3).layout().itemAt(0).widget().checkState() ) if vdict["type"] in ("regexp", "random"): try: re.compile(vdict["from"]) except re.error as e: quirkWarning = QtWidgets.QMessageBox(self) quirkWarning.setText("Not a valid regular expression!") quirkWarning.setInformativeText("H3R3S WHY DUMP4SS: %s" % (e)) quirkWarning.exec() self.quirkadd = None return quirk = pesterQuirk(vdict) if self.quirkadd.quirk is None: item = PesterQuirkItem(quirk) self.quirkList.addItem(item) else: self.quirkadd.quirk.update(quirk) self.quirkadd = None @QtCore.pyqtSlot() def closeQuirk(self): self.quirkadd = None class PesterChooseTheme(QtWidgets.QDialog): def __init__(self, config, theme, parent): QtWidgets.QDialog.__init__(self, parent) self.config = config self.theme = theme self.parent = parent self.setStyleSheet(self.theme["main/defaultwindow/style"]) self.setWindowTitle("Pick a theme") instructions = QtWidgets.QLabel("Pick a theme:") avail_themes = config.availableThemes() self.themeBox = QtWidgets.QComboBox(self) for i, t in enumerate(avail_themes): self.themeBox.addItem(t) if t == theme.name: self.themeBox.setCurrentIndex(i) self.ok = QtWidgets.QPushButton("OK", self) self.ok.setDefault(True) self.ok.clicked.connect(self.accept) self.cancel = QtWidgets.QPushButton("CANCEL", self) self.cancel.clicked.connect(self.reject) layout_ok = QtWidgets.QHBoxLayout() layout_ok.addWidget(self.cancel) layout_ok.addWidget(self.ok) layout_0 = QtWidgets.QVBoxLayout() layout_0.addWidget(instructions) layout_0.addWidget(self.themeBox) layout_0.addLayout(layout_ok) self.setLayout(layout_0) self.accepted.connect(parent.themeSelected) self.rejected.connect(parent.closeTheme) class PesterChooseProfile(QtWidgets.QDialog): def __init__( self, userprofile, config, theme, parent, collision=None, svsnick=None ): QtWidgets.QDialog.__init__(self, parent) self.userprofile = userprofile self.theme = theme self.config = config self.parent = parent self.setStyleSheet(self.theme["main/defaultwindow/style"]) self.currentHandle = QtWidgets.QLabel( "CHANGING FROM %s" % userprofile.chat.handle ) self.chumHandle = QtWidgets.QLineEdit(self) self.chumHandle.setMinimumWidth(200) self.chumHandle.setObjectName("setprofilehandle") self.chumHandleLabel = QtWidgets.QLabel( self.theme["main/mychumhandle/label/text"], self ) self.chumColorButton = QtWidgets.QPushButton(self) self.chumColorButton.setObjectName("setprofilecolor") self.chumColorButton.resize(50, 20) self.chumColorButton.setStyleSheet( "background: %s" % (userprofile.chat.colorhtml()) ) self.chumcolor = userprofile.chat.color self.chumColorButton.clicked.connect(self.openColorDialog) layout_1 = QtWidgets.QHBoxLayout() layout_1.addWidget(self.chumHandleLabel) layout_1.addWidget(self.chumHandle) layout_1.addWidget(self.chumColorButton) # available profiles? avail_profiles = self.config.availableProfiles() if avail_profiles: self.profileBox = QtWidgets.QComboBox(self) self.profileBox.addItem("Choose a profile...") for p in avail_profiles: # PchumLog.debug("Adding profile: %s" % p.chat.handle) self.profileBox.addItem(p.chat.handle) else: self.profileBox = None self.defaultcheck = QtWidgets.QCheckBox(self) self.defaultlabel = QtWidgets.QLabel("Set This Profile As Default", self) layout_2 = QtWidgets.QHBoxLayout() layout_2.addWidget(self.defaultlabel) layout_2.addWidget(self.defaultcheck) self.ok = QtWidgets.QPushButton("OK", self) self.ok.setDefault(True) self.ok.clicked.connect(self.validateProfile) self.cancel = QtWidgets.QPushButton("CANCEL", self) self.cancel.clicked.connect(self.reject) if not collision and avail_profiles: self.delete = QtWidgets.QPushButton("DELETE", self) self.delete.clicked.connect(self.deleteProfile) layout_ok = QtWidgets.QHBoxLayout() layout_ok.addWidget(self.cancel) layout_ok.addWidget(self.ok) layout_0 = QtWidgets.QVBoxLayout() if collision: collision_warning = QtWidgets.QLabel( "%s is taken already! Pick a new profile." % (collision) ) layout_0.addWidget(collision_warning) elif svsnick is not None: svsnick_warning = QtWidgets.QLabel( "Your handle got changed from %s to %s! Pick a new profile." % svsnick ) layout_0.addWidget(svsnick_warning) else: layout_0.addWidget( self.currentHandle, alignment=QtCore.Qt.AlignmentFlag.AlignHCenter ) layout_0.addLayout(layout_1) if avail_profiles: profileLabel = QtWidgets.QLabel("Or choose an existing profile:", self) layout_0.addWidget(profileLabel) layout_0.addWidget(self.profileBox) layout_0.addLayout(layout_ok) if not collision and avail_profiles: layout_0.addWidget(self.delete) layout_0.addLayout(layout_2) self.errorMsg = QtWidgets.QLabel(self) self.errorMsg.setObjectName("errormsg") self.errorMsg.setStyleSheet("color:red;") layout_0.addWidget(self.errorMsg) self.setLayout(layout_0) self.accepted.connect(parent.profileSelected) self.rejected.connect(parent.closeProfile) @QtCore.pyqtSlot() def openColorDialog(self): self.colorDialog = QtWidgets.QColorDialog(self) color = self.colorDialog.getColor(initial=self.userprofile.chat.color) self.chumColorButton.setStyleSheet("background: %s" % color.name()) self.chumcolor = color self.colorDialog = None @QtCore.pyqtSlot() def validateProfile(self): if not self.profileBox or self.profileBox.currentIndex() == 0: handle = self.chumHandle.text() if not PesterProfile.checkLength(handle): self.errorMsg.setText("PROFILE HANDLE IS TOO LONG") return if not PesterProfile.checkValid(handle)[0]: self.errorMsg.setText( "NOT A VALID CHUMTAG. REASON:\n%s" % (PesterProfile.checkValid(handle)[1]) ) return self.accept() @QtCore.pyqtSlot() def deleteProfile(self): if self.profileBox and self.profileBox.currentIndex() > 0: handle = self.profileBox.currentText() if handle == self.parent.profile().handle: problem = QtWidgets.QMessageBox() # karxi Will probably change this to its own name later. problem.setObjectName("errmsg") problem.setStyleSheet(self.theme["main/defaultwindow/style"]) problem.setWindowTitle("Problem!") problem.setInformativeText( "You can't delete the profile you're currently using!" ) problem.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) problem.exec() return # TODO: Make this select 'no' as the default, as usual. msgbox = QtWidgets.QMessageBox() msgbox.setStyleSheet(self.theme["main/defaultwindow/style"]) msgbox.setWindowTitle("WARNING!") msgbox.setInformativeText( "Are you sure you want to delete the profile: %s" % (handle) ) msgbox.setStandardButtons( QtWidgets.QMessageBox.StandardButton.Ok | QtWidgets.QMessageBox.StandardButton.Cancel ) ret = msgbox.exec() if ret == QtWidgets.QMessageBox.StandardButton.Ok: try: remove(_datadir + "profiles/%s.js" % (handle)) except OSError: problem = QtWidgets.QMessageBox() problem.setObjectName("errmsg") problem.setStyleSheet(self.theme["main/defaultwindow/style"]) problem.setWindowTitle("Problem!") problem.setInformativeText( "There was a problem deleting the profile: %s" % (handle) ) problem.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) problem.exec() class PesterMentions(QtWidgets.QDialog): def __init__(self, window, theme, parent): QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle("Mentions") self.setModal(True) self.mainwindow = window self.theme = theme self.setStyleSheet(self.theme["main/defaultwindow/style"]) self.mentionlist = QtWidgets.QListWidget(self) self.mentionlist.addItems(self.mainwindow.userprofile.getMentions()) self.addBtn = QtWidgets.QPushButton("ADD MENTION", self) self.addBtn.clicked.connect(self.addMention) self.editBtn = QtWidgets.QPushButton("EDIT", self) self.editBtn.clicked.connect(self.editSelected) self.rmBtn = QtWidgets.QPushButton("REMOVE", self) self.rmBtn.clicked.connect(self.removeCurrent) layout_1 = QtWidgets.QHBoxLayout() layout_1.addWidget(self.editBtn) layout_1.addWidget(self.rmBtn) self.ok = QtWidgets.QPushButton("OK", self) self.ok.setDefault(True) self.ok.clicked.connect(self.accept) self.cancel = QtWidgets.QPushButton("CANCEL", self) self.cancel.clicked.connect(self.reject) layout_2 = QtWidgets.QHBoxLayout() layout_2.addWidget(self.cancel) layout_2.addWidget(self.ok) layout_0 = QtWidgets.QVBoxLayout() layout_0.addWidget(self.mentionlist) layout_0.addWidget(self.addBtn) layout_0.addLayout(layout_1) layout_0.addLayout(layout_2) self.setLayout(layout_0) @QtCore.pyqtSlot() def editSelected(self): m = self.mentionlist.currentItem() if not m: return self.addMention(m) @QtCore.pyqtSlot() def addMention(self, mitem=None): d = {"label": "Mention:", "inputname": "value"} if mitem is not None: d["value"] = mitem.text() pdict = MultiTextDialog("ENTER MENTION", self, d).getText() if pdict is None: return try: re.compile(pdict["value"]) except re.error as e: quirkWarning = QtWidgets.QMessageBox(self) quirkWarning.setText("Not a valid regular expression!") quirkWarning.setInformativeText("H3R3S WHY DUMP4SS: %s" % (e)) quirkWarning.exec() else: if mitem is None: self.mentionlist.addItem(pdict["value"]) else: mitem.setText(pdict["value"]) @QtCore.pyqtSlot() def removeCurrent(self): i = self.mentionlist.currentRow() if i >= 0: self.mentionlist.takeItem(i) class PesterOptions(QtWidgets.QDialog): def __init__(self, config, theme, parent): QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle("Options") self.setModal(False) self.config = config self.theme = theme self.setStyleSheet(self.theme["main/defaultwindow/style"]) layout_4 = QtWidgets.QVBoxLayout() hr = QtWidgets.QFrame() hr.setFrameShape(QtWidgets.QFrame.Shape.HLine) hr.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) vr = QtWidgets.QFrame() vr.setFrameShape(QtWidgets.QFrame.Shape.VLine) vr.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.tabs = QtWidgets.QButtonGroup(self) self.tabs.buttonClicked.connect(self.changePage) # Verify working self.tabNames = [ "Chum List", "Conversations", "Interface", "Sound", "Notifications", "Logging", "Idle/Updates", "Theme", "IRC", ] if parent.advanced: self.tabNames.append("Advanced") for t in self.tabNames: button = QtWidgets.QPushButton(t) self.tabs.addButton(button) layout_4.addWidget(button) button.setCheckable(True) self.tabs.button(-2).setChecked(True) self.pages = QtWidgets.QStackedWidget(self) self.irc_mode_check = QtWidgets.QCheckBox("IRC compatibility mode", self) if self.config.irc_compatibility_mode(): self.irc_mode_check.setChecked(True) bandwidthLabel = QtWidgets.QLabel( "Enable this if you're planning on using Pesterchum on a server with normal IRC clients." "\nStops the client from sending or requesting:" "\n - Non-metadata moods (MOOD >0, GETMOOD, etc.)" "\n - Non-metadata dm colors (COLOR >0,0,0)" "\n - Memo message initials and color (EB: )" "\n - Memo timelines" "\n - Misc. PESTERCHUM:X commands (BEGIN, CEASE, BLOCK, IDLE, etc.)" ) font = bandwidthLabel.font() font.setPointSize(8) bandwidthLabel.setFont(font) self.force_prefix_check = QtWidgets.QCheckBox( "Force all memo messages to have valid initials.", self ) if self.config.force_prefix(): self.force_prefix_check.setChecked(True) initials_label = QtWidgets.QLabel( "Disable to allow users to send messages without initials, like Doc Scratch." ) font = initials_label.font() font.setPointSize(8) initials_label.setFont(font) self.autonickserv = QtWidgets.QCheckBox("Auto-Identify with NickServ", self) self.autonickserv.setChecked(parent.userprofile.getAutoIdentify()) self.autonickserv.stateChanged[int].connect(self.autoNickServChange) self.nickservpass = QtWidgets.QLineEdit(self) self.nickservpass.setPlaceholderText("NickServ Password") self.nickservpass.setEchoMode(QtWidgets.QLineEdit.EchoMode.PasswordEchoOnEdit) self.nickservpass.setText(parent.userprofile.getNickServPass()) self.autojoinlist = QtWidgets.QListWidget(self) self.autojoinlist.addItems(parent.userprofile.getAutoJoins()) self.addAutoJoinBtn = QtWidgets.QPushButton("Add", self) self.addAutoJoinBtn.clicked.connect(self.addAutoJoin) self.delAutoJoinBtn = QtWidgets.QPushButton("Remove", self) self.delAutoJoinBtn.clicked.connect(self.delAutoJoin) self.tabcheck = QtWidgets.QCheckBox("Tabbed Conversations", self) if self.config.tabs(): self.tabcheck.setChecked(True) self.tabmemocheck = QtWidgets.QCheckBox("Tabbed Memos", self) if self.config.tabMemos(): self.tabmemocheck.setChecked(True) self.hideOffline = QtWidgets.QCheckBox("Hide Offline Chums", self) if self.config.hideOfflineChums(): self.hideOffline.setChecked(True) self.soundcheck = QtWidgets.QCheckBox("Sounds On", self) self.soundcheck.stateChanged[int].connect(self.soundChange) self.chatsoundcheck = QtWidgets.QCheckBox("Pester Sounds", self) self.chatsoundcheck.setChecked(self.config.chatSound()) self.memosoundcheck = QtWidgets.QCheckBox("Memo Sounds", self) self.memosoundcheck.setChecked(self.config.memoSound()) self.memosoundcheck.stateChanged[int].connect(self.memoSoundChange) self.memopingcheck = QtWidgets.QCheckBox("Memo Ping", self) self.memopingcheck.setChecked(self.config.memoPing()) self.namesoundcheck = QtWidgets.QCheckBox("Memo Mention (initials)", self) self.namesoundcheck.setChecked(self.config.nameSound()) if self.config.soundOn(): self.soundcheck.setChecked(True) if not self.memosoundcheck.isChecked(): self.memoSoundChange(0) else: self.chatsoundcheck.setEnabled(False) self.memosoundcheck.setEnabled(False) self.memoSoundChange(0) self.editMentions = QtWidgets.QPushButton("Edit Mentions", self) self.editMentions.clicked.connect(self.openMentions) self.editMentions2 = QtWidgets.QPushButton("Edit Mentions", self) self.editMentions2.clicked.connect(self.openMentions) self.volume = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal, self) self.volume.setMinimum(0) self.volume.setMaximum(100) self.volume.setValue(self.config.volume()) self.volume.valueChanged[int].connect(self.printValue) # Disable the volume slider if we can't actually use it. if parent.canSetVolume(): self.currentVol = QtWidgets.QLabel(f"{self.config.volume()!s}%", self) # We don't need to explicitly set this, but it helps drive the # point home self.volume.setEnabled(True) else: # We can't set the volume.... self.currentVol = QtWidgets.QLabel("(Disabled: Sound Mixer Error)", self) self.volume.setEnabled(False) self.currentVol.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter) self.timestampcheck = QtWidgets.QCheckBox("Time Stamps", self) if self.config.showTimeStamps(): self.timestampcheck.setChecked(True) self.timestampBox = QtWidgets.QComboBox(self) self.timestampBox.addItem("12 hour") self.timestampBox.addItem("24 hour") if self.config.time12Format(): self.timestampBox.setCurrentIndex(0) else: self.timestampBox.setCurrentIndex(1) self.secondscheck = QtWidgets.QCheckBox("Show Seconds", self) if self.config.showSeconds(): self.secondscheck.setChecked(True) self.memomessagecheck = QtWidgets.QCheckBox( "Show OP and Voice Messages in Memos", self ) if self.config.opvoiceMessages(): self.memomessagecheck.setChecked(True) if not ostools.isOSXBundle(): self.animationscheck = QtWidgets.QCheckBox("Use animated smilies", self) if self.config.animations(): self.animationscheck.setChecked(True) animateLabel = QtWidgets.QLabel( "(Disable if you leave chats open for LOOOONG periods of time)" ) font = animateLabel.font() font.setPointSize(8) animateLabel.setFont(font) self.userlinkscheck = QtWidgets.QCheckBox("Disable #Memo and @User Links", self) self.userlinkscheck.setChecked(self.config.disableUserLinks()) self.userlinkscheck.setVisible(False) # Will add ability to turn off groups later # self.groupscheck = QtGui.QCheckBox("Use Groups", self) # self.groupscheck.setChecked(self.config.useGroups()) self.showemptycheck = QtWidgets.QCheckBox("Show Empty Groups", self) self.showemptycheck.setChecked(self.config.showEmptyGroups()) self.showonlinenumbers = QtWidgets.QCheckBox( "Show Number of Online Chums", self ) self.showonlinenumbers.setChecked(self.config.showOnlineNumbers()) sortLabel = QtWidgets.QLabel("Sort Chums") self.sortBox = QtWidgets.QComboBox(self) self.sortBox.addItem("Alphabetically") self.sortBox.addItem("By Mood") self.sortBox.addItem("Manually") method = self.config.sortMethod() if method >= 0 and method < self.sortBox.count(): self.sortBox.setCurrentIndex(method) layout_3 = QtWidgets.QHBoxLayout() layout_3.addWidget(sortLabel) layout_3.addWidget(self.sortBox, 10) self.logpesterscheck = QtWidgets.QCheckBox("Log all Pesters", self) if self.config.logPesters() & self.config.LOG: self.logpesterscheck.setChecked(True) self.logmemoscheck = QtWidgets.QCheckBox("Log all Memos", self) if self.config.logMemos() & self.config.LOG: self.logmemoscheck.setChecked(True) self.stamppestercheck = QtWidgets.QCheckBox("Log Time Stamps for Pesters", self) if self.config.logPesters() & self.config.STAMP: self.stamppestercheck.setChecked(True) self.stampmemocheck = QtWidgets.QCheckBox("Log Time Stamps for Memos", self) if self.config.logMemos() & self.config.STAMP: self.stampmemocheck.setChecked(True) self.idleBox = QtWidgets.QSpinBox(self) self.idleBox.setStyleSheet("background:#FFFFFF") self.idleBox.setRange(1, 1440) self.idleBox.setValue(self.config.idleTime()) layout_5 = QtWidgets.QHBoxLayout() layout_5.addWidget(QtWidgets.QLabel("Minutes before Idle:")) layout_5.addWidget(self.idleBox) # self.updateBox = QtWidgets.QComboBox(self) # self.updateBox.addItem("Once a Day") # self.updateBox.addItem("Once a Week") # self.updateBox.addItem("Only on Start") # self.updateBox.addItem("Never") # check = self.config.checkForUpdates() # if check >= 0 and check < self.updateBox.count(): # self.updateBox.setCurrentIndex(check) layout_6 = QtWidgets.QHBoxLayout() # layout_6.addWidget(QtWidgets.QLabel("Check for\nPesterchum Updates:")) # layout_6.addWidget(self.updateBox) # if not ostools.isOSXLeopard(): # self.mspaCheck = QtWidgets.QCheckBox("Check for MSPA Updates", self) # self.mspaCheck.setChecked(self.config.checkMSPA()) self.randomscheck = QtWidgets.QCheckBox("Receive Random Encounters") self.randomscheck.setChecked(parent.userprofile.randoms) if not parent.randhandler.running: self.randomscheck.setEnabled(False) avail_themes = self.config.availableThemes() self.themeBox = QtWidgets.QComboBox(self) notheme = theme.name not in avail_themes for i, t in enumerate(avail_themes): self.themeBox.addItem(t) if (not notheme and t == theme.name) or (notheme and t == "pesterchum"): self.themeBox.setCurrentIndex(i) self.refreshtheme = QtWidgets.QPushButton("Refresh current theme", self) self.refreshtheme.clicked.connect(parent.themeSelectOverride) self.ghostchum = QtWidgets.QCheckBox("Pesterdunk Ghostchum!!", self) self.ghostchum.setChecked(self.config.ghostchum()) self.buttonOptions = ["Minimize to Taskbar", "Minimize to Tray", "Quit"] self.miniBox = QtWidgets.QComboBox(self) self.miniBox.addItems(self.buttonOptions) self.miniBox.setCurrentIndex(self.config.minimizeAction()) self.closeBox = QtWidgets.QComboBox(self) self.closeBox.addItems(self.buttonOptions) self.closeBox.setCurrentIndex(self.config.closeAction()) layout_mini = QtWidgets.QHBoxLayout() layout_mini.addWidget(QtWidgets.QLabel("Minimize")) layout_mini.addWidget(self.miniBox) layout_close = QtWidgets.QHBoxLayout() layout_close.addWidget(QtWidgets.QLabel("Close")) layout_close.addWidget(self.closeBox) self.pesterBlink = QtWidgets.QCheckBox("Blink Taskbar on Pesters", self) if self.config.blink() & self.config.PBLINK: self.pesterBlink.setChecked(True) self.memoBlink = QtWidgets.QCheckBox("Blink Taskbar on Memos", self) if self.config.blink() & self.config.MBLINK: self.memoBlink.setChecked(True) self.notifycheck = QtWidgets.QCheckBox("Toast Notifications", self) if self.config.notify(): self.notifycheck.setChecked(True) self.notifycheck.stateChanged[int].connect(self.notifyChange) self.notifyOptions = QtWidgets.QComboBox(self) types = self.parent().tm.availableTypes() cur = self.parent().tm.currentType() self.notifyOptions.addItems(types) for i, t in enumerate(types): if t == cur: self.notifyOptions.setCurrentIndex(i) break self.notifyTypeLabel = QtWidgets.QLabel("Type", self) layout_type = QtWidgets.QHBoxLayout() layout_type.addWidget(self.notifyTypeLabel) layout_type.addWidget(self.notifyOptions) self.notifySigninCheck = QtWidgets.QCheckBox("Chum signs in", self) if self.config.notifyOptions() & self.config.SIGNIN: self.notifySigninCheck.setChecked(True) self.notifySignoutCheck = QtWidgets.QCheckBox("Chum signs out", self) if self.config.notifyOptions() & self.config.SIGNOUT: self.notifySignoutCheck.setChecked(True) self.notifyNewMsgCheck = QtWidgets.QCheckBox("New messages", self) if self.config.notifyOptions() & self.config.NEWMSG: self.notifyNewMsgCheck.setChecked(True) self.notifyNewConvoCheck = QtWidgets.QCheckBox("Only new conversations", self) if self.config.notifyOptions() & self.config.NEWCONVO: self.notifyNewConvoCheck.setChecked(True) self.notifyMentionsCheck = QtWidgets.QCheckBox("Memo Mentions (initials)", self) if self.config.notifyOptions() & self.config.INITIALS: self.notifyMentionsCheck.setChecked(True) self.notifyChange(self.notifycheck.checkState()) if parent.advanced: # NOTE: This doesn't do anything right now - so change it! self.modechange = QtWidgets.QLineEdit(self) layout_change = QtWidgets.QHBoxLayout() layout_change.addWidget(QtWidgets.QLabel("Change:")) layout_change.addWidget(self.modechange) self.ok = QtWidgets.QPushButton("OK", self) self.ok.setDefault(True) self.ok.clicked.connect(self.accept) self.cancel = QtWidgets.QPushButton("CANCEL", self) self.cancel.clicked.connect(self.reject) layout_2 = QtWidgets.QHBoxLayout() layout_2.addWidget(self.cancel) layout_2.addWidget(self.ok) # Tab layouts # Chum List widget = QtWidgets.QWidget() layout_chumlist = QtWidgets.QVBoxLayout(widget) layout_chumlist.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_chumlist.addWidget(self.hideOffline) # layout_chumlist.addWidget(self.groupscheck) layout_chumlist.addWidget(self.showemptycheck) layout_chumlist.addWidget(self.showonlinenumbers) layout_chumlist.addLayout(layout_3) self.pages.addWidget(widget) # Conversations widget = QtWidgets.QWidget() layout_chat = QtWidgets.QVBoxLayout(widget) layout_chat.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_chat.addWidget(self.timestampcheck) layout_chat.addWidget(self.timestampBox) layout_chat.addWidget(self.secondscheck) layout_chat.addWidget(self.memomessagecheck) if not ostools.isOSXBundle(): layout_chat.addWidget(self.animationscheck) layout_chat.addWidget(animateLabel) layout_chat.addWidget(self.randomscheck) # Re-enable these when it's possible to disable User and Memo links # layout_chat.addWidget(hr) # layout_chat.addWidget(QtGui.QLabel("User and Memo Links")) # layout_chat.addWidget(self.userlinkscheck) self.pages.addWidget(widget) # Interface widget = QtWidgets.QWidget() layout_interface = QtWidgets.QVBoxLayout(widget) layout_interface.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_interface.addWidget(self.tabcheck) layout_interface.addWidget(self.tabmemocheck) layout_interface.addLayout(layout_mini) layout_interface.addLayout(layout_close) layout_interface.addWidget(self.pesterBlink) layout_interface.addWidget(self.memoBlink) self.pages.addWidget(widget) # Sound widget = QtWidgets.QWidget() # Choose audio device audioDeviceLabel = QtWidgets.QLabel("Audio output device:") self.audioDeviceBox = QtWidgets.QComboBox(self) current_audio_device = self.config.audioDevice() active_index = None try: for i, output in enumerate(QtMultimedia.QMediaDevices.audioOutputs()): self.audioDeviceBox.addItem(f"{output.description()}", output.id()) if output.id() == current_audio_device: active_index = i if active_index is not None: self.audioDeviceBox.setCurrentIndex(active_index) except AttributeError: PchumLog.warning("Can't get audio devices, not using PyQt6 QtMultimedia?") layout_sound = QtWidgets.QVBoxLayout(widget) layout_sound.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_sound.addWidget(self.soundcheck) layout_indent = QtWidgets.QVBoxLayout() layout_indent.addWidget(self.chatsoundcheck) layout_indent.addWidget(self.memosoundcheck) layout_doubleindent = QtWidgets.QVBoxLayout() layout_doubleindent.addWidget(self.memopingcheck) layout_doubleindent.addWidget(self.namesoundcheck) layout_doubleindent.addWidget(self.editMentions) layout_doubleindent.setContentsMargins(22, 0, 0, 0) layout_indent.addLayout(layout_doubleindent) layout_indent.setContentsMargins(22, 0, 0, 0) layout_sound.addLayout(layout_indent) layout_sound.addSpacing(15) layout_audioDevice = QtWidgets.QHBoxLayout() layout_audioDevice.addWidget(audioDeviceLabel) layout_audioDevice.addWidget(self.audioDeviceBox) layout_sound.addLayout(layout_audioDevice) mvol = QtWidgets.QLabel("Master Volume:", self) # If we can't set the volume, grey this out as well # ~mvol.setEnabled(parent.canSetVolume()) # Normally we'd grey this out, but that presently makes things # rather unreadable # Later we can look into changing the color to a theme[] entry layout_sound.addWidget(mvol) layout_sound.addWidget(self.volume) layout_sound.addWidget(self.currentVol) self.pages.addWidget(widget) # Notifications widget = QtWidgets.QWidget() layout_notify = QtWidgets.QVBoxLayout(widget) layout_notify.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_notify.addWidget(self.notifycheck) layout_indent = QtWidgets.QVBoxLayout() layout_indent.addLayout(layout_type) layout_indent.setContentsMargins(22, 0, 0, 0) layout_indent.addWidget(self.notifySigninCheck) layout_indent.addWidget(self.notifySignoutCheck) layout_indent.addWidget(self.notifyNewMsgCheck) layout_doubleindent = QtWidgets.QVBoxLayout() layout_doubleindent.addWidget(self.notifyNewConvoCheck) layout_doubleindent.setContentsMargins(22, 0, 0, 0) layout_indent.addLayout(layout_doubleindent) layout_indent.addWidget(self.notifyMentionsCheck) layout_indent.addWidget(self.editMentions2) layout_notify.addLayout(layout_indent) self.pages.addWidget(widget) # Logging widget = QtWidgets.QWidget() layout_logs = QtWidgets.QVBoxLayout(widget) layout_logs.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_logs.addWidget(self.logpesterscheck) layout_logs.addWidget(self.logmemoscheck) layout_logs.addWidget(self.stamppestercheck) layout_logs.addWidget(self.stampmemocheck) self.pages.addWidget(widget) # Idle/Updates widget = QtWidgets.QWidget() layout_idle = QtWidgets.QVBoxLayout(widget) layout_idle.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_idle.addLayout(layout_5) layout_idle.addLayout(layout_6) # if not ostools.isOSXLeopard(): # layout_idle.addWidget(self.mspaCheck) self.pages.addWidget(widget) # Theme widget = QtWidgets.QWidget() layout_theme = QtWidgets.QVBoxLayout(widget) layout_theme.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_theme.addWidget(QtWidgets.QLabel("Pick a Theme:")) layout_theme.addWidget(self.themeBox) layout_theme.addWidget(self.refreshtheme) layout_theme.addWidget(self.ghostchum) self.pages.addWidget(widget) # Connection widget = QtWidgets.QWidget() layout_connect = QtWidgets.QVBoxLayout(widget) layout_connect.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_connect.addWidget(self.irc_mode_check) layout_connect.addWidget(bandwidthLabel) layout_connect.addWidget(self.force_prefix_check) layout_connect.addWidget(initials_label) layout_connect.addWidget(self.autonickserv) layout_indent = QtWidgets.QVBoxLayout() layout_indent.addWidget(self.nickservpass) layout_indent.setContentsMargins(22, 0, 0, 0) layout_connect.addLayout(layout_indent) layout_connect.addWidget(QtWidgets.QLabel("Auto-Join Memos:")) layout_connect.addWidget(self.autojoinlist) layout_8 = QtWidgets.QHBoxLayout() layout_8.addWidget(self.addAutoJoinBtn) layout_8.addWidget(self.delAutoJoinBtn) layout_connect.addLayout(layout_8) self.pages.addWidget(widget) # Advanced if parent.advanced: widget = QtWidgets.QWidget() layout_advanced = QtWidgets.QVBoxLayout(widget) layout_advanced.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_advanced.addWidget( QtWidgets.QLabel("Current User Mode: %s" % parent.modes) ) layout_advanced.addLayout(layout_change) self.pages.addWidget(widget) layout_0 = QtWidgets.QVBoxLayout() layout_1 = QtWidgets.QHBoxLayout() layout_1.addLayout(layout_4) layout_1.addWidget(vr) layout_1.addWidget(self.pages) layout_0.addLayout(layout_1) layout_0.addSpacing(30) layout_0.addLayout(layout_2) self.setLayout(layout_0) @QtCore.pyqtSlot(QtWidgets.QAbstractButton) def changePage(self, button): self.pages.setCurrentIndex(self.tabNames.index(button.text())) @QtCore.pyqtSlot(int) def notifyChange(self, state): if state == 0: self.notifyTypeLabel.setEnabled(False) self.notifyOptions.setEnabled(False) self.notifySigninCheck.setEnabled(False) self.notifySignoutCheck.setEnabled(False) self.notifyNewMsgCheck.setEnabled(False) self.notifyNewConvoCheck.setEnabled(False) self.notifyMentionsCheck.setEnabled(False) else: self.notifyTypeLabel.setEnabled(True) self.notifyOptions.setEnabled(True) self.notifySigninCheck.setEnabled(True) self.notifySignoutCheck.setEnabled(True) self.notifyNewMsgCheck.setEnabled(True) self.notifyNewConvoCheck.setEnabled(True) self.notifyMentionsCheck.setEnabled(True) @QtCore.pyqtSlot(int) def autoNickServChange(self, state): self.nickservpass.setEnabled(state != 0) @QtCore.pyqtSlot() def addAutoJoin(self, mitem=None): d = {"label": "Memo:", "inputname": "value"} if mitem is not None: d["value"] = mitem.text() pdict = MultiTextDialog("ENTER MEMO", self, d).getText() if pdict is None: return pdict["value"] = "#" + pdict["value"] if mitem is None: items = self.autojoinlist.findItems( pdict["value"], QtCore.Qt.MatchFlag.MatchFixedString ) if len(items) == 0: self.autojoinlist.addItem(pdict["value"]) else: mitem.setText(pdict["value"]) @QtCore.pyqtSlot() def delAutoJoin(self): i = self.autojoinlist.currentRow() if i >= 0: self.autojoinlist.takeItem(i) @QtCore.pyqtSlot(int) def soundChange(self, state): if state == 0: self.chatsoundcheck.setEnabled(False) self.memosoundcheck.setEnabled(False) self.memoSoundChange(0) else: self.chatsoundcheck.setEnabled(True) self.memosoundcheck.setEnabled(True) if self.memosoundcheck.isChecked(): self.memoSoundChange(1) @QtCore.pyqtSlot(int) def memoSoundChange(self, state): if state == 0: self.memopingcheck.setEnabled(False) self.namesoundcheck.setEnabled(False) else: self.memopingcheck.setEnabled(True) self.namesoundcheck.setEnabled(True) @QtCore.pyqtSlot(int) def printValue(self, v): self.currentVol.setText(f"{v}%") @QtCore.pyqtSlot() def openMentions(self): if not hasattr(self, "mentionmenu"): self.mentionmenu = None if not self.mentionmenu: self.mentionmenu = PesterMentions(self.parent(), self.theme, self) self.mentionmenu.accepted.connect(self.updateMentions) self.mentionmenu.rejected.connect(self.closeMentions) self.mentionmenu.show() self.mentionmenu.raise_() self.mentionmenu.activateWindow() @QtCore.pyqtSlot() def closeMentions(self): self.mentionmenu.close() self.mentionmenu = None @QtCore.pyqtSlot() def updateMentions(self): m = [] for i in range(self.mentionmenu.mentionlist.count()): m.append((self.mentionmenu.mentionlist.item(i).text())) self.parent().userprofile.setMentions(m) self.mentionmenu = None class PesterUserlist(QtWidgets.QDialog): def __init__(self, config, theme, parent): QtWidgets.QDialog.__init__(self, parent) self.setModal(False) self.config = config self.theme = theme self.mainwindow = parent self.setStyleSheet(self.theme["main/defaultwindow/style"]) self.resize(200, 600) self.searchbox = QtWidgets.QLineEdit(self) # self.searchbox.setStyleSheet(theme["convo/input/style"]) # which style is better? self.searchbox.setPlaceholderText("Search") self.searchbox.textChanged[str].connect(self.updateUsers) self.label = QtWidgets.QLabel("USERLIST") self.userarea = RightClickList(self) self.userarea.setStyleSheet(self.theme["main/chums/style"]) self.userarea.optionsMenu = QtWidgets.QMenu(self) self.addChumAction = QAction( self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self ) self.addChumAction.triggered.connect(self.addChumSlot) self.pesterChumAction = QAction( self.mainwindow.theme["main/menus/rclickchumlist/pester"], self ) self.pesterChumAction.triggered.connect(self.pesterChumSlot) self.userarea.optionsMenu.addAction(self.addChumAction) self.userarea.optionsMenu.addAction(self.pesterChumAction) self.ok = QtWidgets.QPushButton("OK", self) self.ok.setDefault(True) self.ok.clicked.connect(self.accept) layout_0 = QtWidgets.QVBoxLayout() layout_0.addWidget(self.label) layout_0.addWidget(self.userarea) layout_0.addWidget(self.searchbox) layout_0.addWidget(self.ok) self.setLayout(layout_0) self.mainwindow.namesUpdated.connect(self.updateUsers) self.mainwindow.userPresentSignal[str, str, str].connect(self.updateUserPresent) self.updateUsers() self.searchbox.setFocus() @QtCore.pyqtSlot() def updateUsers(self): try: names = self.mainwindow.namesdb["#pesterchum"] except KeyError: # Not in #pesterchum? return self.userarea.clear() for n in names: if (self.searchbox.text()) == "" or n.lower().find( self.searchbox.text().lower() ) != -1: # Strip channel membership prefixes n = n.strip("~").strip("@").strip("+").strip("&").strip("%") item = QtWidgets.QListWidgetItem(n) item.setForeground( QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])) ) self.userarea.addItem(item) self.userarea.sortItems() @QtCore.pyqtSlot(str, str, str) def updateUserPresent(self, handle, channel, update): if update == "quit": self.delUser(handle) elif update == "left" and channel == "#pesterchum": self.delUser(handle) elif update == "join" and channel == "#pesterchum": if ( self.searchbox.text() == "" or handle.lower().find(self.searchbox.text().lower()) != -1 ): self.addUser(handle) def addUser(self, name): item = QtWidgets.QListWidgetItem(name) item.setForeground( QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])) ) self.userarea.addItem(item) self.userarea.sortItems() def delUser(self, name): matches = self.userarea.findItems(name, QtCore.Qt.MatchFlag.MatchExactly) for m in matches: self.userarea.takeItem(self.userarea.row(m)) def changeTheme(self, theme): self.theme = theme self.setStyleSheet(theme["main/defaultwindow/style"]) 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())]: item.setForeground( 0, QtGui.QBrush(QtGui.QColor(theme["main/chums/userlistcolor"])) ) @QtCore.pyqtSlot() def addChumSlot(self): cur = self.userarea.currentItem() if not cur: return self.addChum.emit(cur.text()) @QtCore.pyqtSlot() def pesterChumSlot(self): cur = self.userarea.currentItem() if not cur: return self.pesterChum.emit(cur.text()) addChum = QtCore.pyqtSignal(str) pesterChum = QtCore.pyqtSignal(str) class MemoListItem(QtWidgets.QTreeWidgetItem): def __init__(self, channel, usercount): QtWidgets.QTreeWidgetItem.__init__(self, [channel, str(usercount)]) self.target = channel def __lt__(self, other): column = self.treeWidget().sortColumn() if (self.text(column)).isdigit() and (other.text(column)).isdigit(): return int(self.text(column)) < int(other.text(column)) return self.text(column) < other.text(column) class PesterMemoList(QtWidgets.QDialog): def __init__(self, parent, channel=""): QtWidgets.QDialog.__init__(self, parent) self.setModal(False) self.theme = parent.theme self.mainwindow = parent self.setStyleSheet(self.theme["main/defaultwindow/style"]) self.resize(460, 300) self.label = QtWidgets.QLabel("MEMOS") self.channelarea = RightClickTree(self) self.channelarea.setSelectionMode( QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection ) self.channelarea.setStyleSheet(self.theme["main/chums/style"]) self.channelarea.optionsMenu = QtWidgets.QMenu(self) self.channelarea.setColumnCount(2) self.channelarea.setHeaderLabels(["Memo", "Users"]) self.channelarea.setIndentation(0) self.channelarea.setColumnWidth(0, 200) self.channelarea.setColumnWidth(1, 10) self.channelarea.setSortingEnabled(True) self.channelarea.sortByColumn(0, QtCore.Qt.SortOrder.AscendingOrder) self.channelarea.itemDoubleClicked[QtWidgets.QTreeWidgetItem, int].connect( self.AcceptSelection ) self.orjoinlabel = QtWidgets.QLabel("OR MAKE A NEW MEMO:") self.newmemo = QtWidgets.QLineEdit(channel, self) self.secretChannel = QtWidgets.QCheckBox("HIDDEN CHANNEL?", self) self.inviteChannel = QtWidgets.QCheckBox("INVITATION ONLY?", self) self.timelabel = QtWidgets.QLabel("TIMEFRAME:") self.timeslider = TimeSlider(QtCore.Qt.Orientation.Horizontal, self) self.timeinput = TimeInput(self.timeslider, self) self.cancel = QtWidgets.QPushButton("CANCEL", self) self.cancel.clicked.connect(self.reject) self.join = QtWidgets.QPushButton("JOIN", self) self.join.setDefault(True) self.join.clicked.connect(self.AcceptIfSelectionMade) layout_ok = QtWidgets.QHBoxLayout() layout_ok.addWidget(self.cancel) layout_ok.addWidget(self.join) layout_left = QtWidgets.QVBoxLayout() layout_right = QtWidgets.QVBoxLayout() layout_right.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_0 = QtWidgets.QVBoxLayout() layout_1 = QtWidgets.QHBoxLayout() layout_left.addWidget(self.label) layout_left.addWidget(self.channelarea) layout_right.addWidget(self.orjoinlabel) layout_right.addWidget(self.newmemo) layout_right.addWidget(self.secretChannel) layout_right.addWidget(self.inviteChannel) layout_right.addWidget(self.timelabel) layout_right.addWidget(self.timeslider) layout_right.addWidget(self.timeinput) layout_1.addLayout(layout_left) layout_1.addLayout(layout_right) layout_0.addLayout(layout_1) layout_0.addLayout(layout_ok) self.setLayout(layout_0) def newmemoname(self): return self.newmemo.text() def SelectedMemos(self): return self.channelarea.selectedItems() def HasSelection(self): return len(self.SelectedMemos()) > 0 or self.newmemoname() def updateChannels(self, channels): for c in channels: item = MemoListItem(c[0][1:], c[1]) item.setForeground( 0, QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])) ) item.setForeground( 1, QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])) ) item.setIcon(0, QtGui.QIcon(self.theme["memos/memoicon"])) self.channelarea.addTopLevelItem(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.setForeground( 0, QtGui.QBrush(QtGui.QColor(theme["main/chums/userlistcolor"])) ) item.setIcon(QtGui.QIcon(theme["memos/memoicon"])) @QtCore.pyqtSlot() def AcceptIfSelectionMade(self): if self.HasSelection(): self.AcceptSelection() @QtCore.pyqtSlot() def AcceptSelection(self): self.accept() class LoadingScreen(QtWidgets.QDialog): tryAgain = QtCore.pyqtSignal() def __init__(self, parent=None): QtWidgets.QDialog.__init__( self, parent, QtCore.Qt.WindowType.CustomizeWindowHint | QtCore.Qt.WindowType.FramelessWindowHint, ) self.mainwindow = parent self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) # self.setWindowModality(QtCore.Qt.WindowModality.NonModal) # useless # self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose) # useless self.loadinglabel = QtWidgets.QLabel("CONN3CT1NG", self) # self.loadinglabel.setTextFormat(QtCore.Qt.TextFormat.RichText) # Clickable html links # self.loadinglabel.setWordWrap(True) # Unusable because of QT clipping bug (QTBUG-92381) self.cancel = QtWidgets.QPushButton("QU1T >:?", self) self.ok = QtWidgets.QPushButton("R3CONN3CT >:]", self) # Help reduce the number of accidental Pesterchum closures... :| self.cancel.setAutoDefault(False) self.ok.setAutoDefault(True) self.cancel.clicked.connect(self.reject) self.ok.clicked.connect(self.tryAgain) # self.finished.connect(self.finishedEvent) self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.loadinglabel) layout_1 = QtWidgets.QHBoxLayout() layout_1.addWidget(self.cancel) layout_1.addWidget(self.ok) self.layout.addLayout(layout_1) self.setLayout(self.layout) # Help reduce the number of accidental Pesterchum closures... :| self.cancel.setDefault(False) self.ok.setDefault(True) self.ok.setFocus() self.timer = None # def finishedEvent(self, result): # self.close() def hideReconnect(self, safe=True): self.ok.hide() if safe: # Set a timer so that we don't immediately disconnect anyway. self.cancel.setEnabled(False) # A few seconds should be enough. self.timer = QtCore.QTimer.singleShot(2000, self.enableQuit) def showReconnect(self): self.ok.show() # Again...stop accidental closes. self.ok.setFocus() @QtCore.pyqtSlot() def enableQuit(self): self.cancel.setEnabled(True) class AboutPesterchum(QtWidgets.QDialog): def __init__(self, parent=None): QtWidgets.QDialog.__init__(self, parent) self.mainwindow = parent self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) self.title = QtWidgets.QLabel("P3ST3RCHUM %s" % (_pcVersion)) self.credits = QtWidgets.QLabel( "Programming by:" "\n illuminatedwax (ghostDunk)" "\n Kiooeht (evacipatedBox)" "\n Lexi (lexicalNuance)" "\n oakwhiz" "\n alGore" "\n Cerxi (binaryCabalist)" "\n Arcane (arcaneAgilmente)" "\n karxi (Midna)" "\n Shou/Dpeta 🐱" "\n" "\nArt by:" "\n Grimlive (aquaMarinist)" "\n Cerxi (binaryCabalist)" "\n cubicSimulation" "\n" "\nSpecial Thanks:" "\n ABT" "\n gamblingGenocider" "\n Eco-Mono" ) self.ok = QtWidgets.QPushButton("OK", self) self.ok.clicked.connect(self.reject) layout_0 = QtWidgets.QVBoxLayout() layout_0.addWidget(self.title) layout_0.addWidget(self.credits) layout_0.addWidget(self.ok) self.setLayout(layout_0) class UpdatePesterchum(QtWidgets.QDialog): def __init__(self, ver, url, parent=None): QtWidgets.QDialog.__init__(self, parent) self.url = url self.mainwindow = parent self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) self.setWindowTitle("Pesterchum v%s Update" % (ver)) self.setModal(False) self.title = QtWidgets.QLabel("An update to Pesterchum is available!") layout_0 = QtWidgets.QVBoxLayout() layout_0.addWidget(self.title) self.ok = QtWidgets.QPushButton("D0WNL04D 4ND 1NST4LL N0W", self) self.ok.setDefault(True) self.ok.clicked.connect(self.accept) self.cancel = QtWidgets.QPushButton("CANCEL", self) self.cancel.clicked.connect(self.reject) layout_2 = QtWidgets.QHBoxLayout() layout_2.addWidget(self.cancel) layout_2.addWidget(self.ok) layout_0.addLayout(layout_2) self.setLayout(layout_0) class AddChumDialog(QtWidgets.QDialog): def __init__(self, avail_groups, parent=None): QtWidgets.QDialog.__init__(self, parent) self.mainwindow = parent self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) self.setWindowTitle("Enter Chum Handle") self.setModal(True) self.title = QtWidgets.QLabel("Enter Chum Handle") self.chumBox = QtWidgets.QLineEdit(self) self.groupBox = QtWidgets.QComboBox(self) avail_groups.sort() avail_groups.pop(avail_groups.index("Chums")) avail_groups.insert(0, "Chums") for g in avail_groups: self.groupBox.addItem(g) self.newgrouplabel = QtWidgets.QLabel("Or make a new group:") self.newgroup = QtWidgets.QLineEdit(self) layout_0 = QtWidgets.QVBoxLayout() layout_0.addWidget(self.title) layout_0.addWidget(self.chumBox) layout_0.addWidget(self.groupBox) layout_0.addWidget(self.newgrouplabel) layout_0.addWidget(self.newgroup) self.ok = QtWidgets.QPushButton("OK", self) self.ok.setDefault(True) self.ok.clicked.connect(self.accept) self.cancel = QtWidgets.QPushButton("CANCEL", self) self.cancel.clicked.connect(self.reject) layout_2 = QtWidgets.QHBoxLayout() layout_2.addWidget(self.cancel) layout_2.addWidget(self.ok) layout_0.addLayout(layout_2) self.setLayout(layout_0)