diff --git a/TODO.mkdn b/TODO.mkdn index b0a7c58..2c74cd5 100644 --- a/TODO.mkdn +++ b/TODO.mkdn @@ -59,8 +59,6 @@ * Save commonly-used times on a per-handle basis! * Make the memo list highlight/recolor the names of channels you're in (including secret ones) -* Make right-clicking on a tab open up the right-click menu one would get on - right-clicking the title (frame??) * Add an option to Cycle (for log separation) * Add a separate 'Tweaks' section in Options @@ -80,15 +78,10 @@ * Enable/Disable toggle (Firefox style option sheet-esque? Seems okay.) * Ctrl+W closes tab * Ctrl+Shift+PGUP/PGDN moves tab - * Make Ctrl+J/K usable for tab changing * Option to disable Ctrl+Tab's jump to newest * Ctrl+Shift+V "Mass Paste" option (parse lines in sequence)? * Make system messages use timestamps like everything else * Offer option for timestamps in memos -* Make certain dialogues start on the safer of the two options - * Make the Reconnect dialog start on reconnect - * Make the Rejoin dialog start on rejoin - * Make the Invite dialog start on decline * Make a status window/popup to contain logs of information like invites ### "Security" @@ -98,7 +91,7 @@ design.** If you want Pesterchum to be more secure, either get ghostDunk to make changes to the server and its administration policies, or get everyone to switch to this -version of the client. There aren't really any other choices. +version of the client. There aren't really any other options. * Flood protection (don't send because of the same target too many times in a row) @@ -144,13 +137,23 @@ version of the client. There aren't really any other choices. ## Todo/Done **Everything in this section has already been completed.** +### GUI +* Toggle individual tab flash / alert sounds (from the same right-click memo + that lets us toggle OOC) +* Make CTRL+PGUP/PGDN switch memo/pester tabs +* Make Ctrl+J/K usable for tab changing +* Make right-clicking on a tab open up the right-click menu one would get on + right-clicking the title (frame??) +* Right-click in userlist offers option to Pester +* Make certain dialogues start on the safer of the two options + * Make the Reconnect dialog start on Reconnect + * Make the Rejoin dialog start on Rejoin + * Make the Invite dialog start on Decline + ### Usability * Fix parser text-loss bug that plagues everyone (especially Chumdroid users) * Make /me messages that cut continue into more /me messages * Make sound work on Windows through QSound (disables volume control) -* Toggle individual tab flash / alert sounds (from the same right-click memo - that lets us toggle OOC) -* Make CTRL+PGUP/PGDN switch memo/pester tabs * Color tags are now posted as their shorter hexadecimal forms, if applicable (255,255,255 -> #FFFFFF, for example) * Separate auto-idle and checkbox idle so they don't share a state (and make diff --git a/console.py b/console.py index 7594cc0..77b21f2 100644 --- a/console.py +++ b/console.py @@ -9,6 +9,7 @@ import dataobjs, generic, memos, parsetools, ostools from version import _pcVersion from pnc.dep.attrdict import AttrDict +#~from styling import styler _datadir = ostools.getDataDir() @@ -19,6 +20,7 @@ logging.basicConfig(level=logging.WARNING) class ConsoleWindow(QtGui.QDialog): +#~class ConsoleWindow(styler.PesterBaseWindow): # A simple console class, cobbled together from the corpse of another. # This is a holder for our text inputs. @@ -110,6 +112,10 @@ class ConsoleWindow(QtGui.QDialog): parent.console.is_open = False parent.console.window = None + def hideEvent(self, event): + parent = self.parent() + parent.console.is_open = False + # Actual console stuff. def execInConsole(self, scriptstr, env=None): # Since that's what imports *us*, this should be okay diff --git a/convo.py b/convo.py index b9baede..350efaa 100644 --- a/convo.py +++ b/convo.py @@ -15,10 +15,11 @@ from parsetools import convertTags, lexMessage, splitMessage, mecmd, colorBegin, import parsetools import pnc.lexercon as lexercon +from pnc.dep.attrdict import AttrDict class PesterTabWindow(QtGui.QFrame): def __init__(self, mainwindow, parent=None, convo="convo"): - QtGui.QFrame.__init__(self, parent) + super(PesterTabWindow, self).__init__(parent) self.setAttribute(QtCore.Qt.WA_QuitOnClose, False) self.setFocusPolicy(QtCore.Qt.ClickFocus) self.mainwindow = mainwindow @@ -33,6 +34,30 @@ class PesterTabWindow(QtGui.QFrame): self.connect(self.tabs, QtCore.SIGNAL('tabMoved(int, int)'), self, QtCore.SLOT('tabMoved(int, int)')) + self.shortcuts = AttrDict() + self.shortcuts.tabNext = QtGui.QShortcut( + QtGui.QKeySequence('Ctrl+j'), self, + context=QtCore.Qt.WidgetWithChildrenShortcut) + self.shortcuts.tabLast = QtGui.QShortcut( + QtGui.QKeySequence('Ctrl+k'), self, + context=QtCore.Qt.WidgetWithChildrenShortcut) + # Note that we use reversed keys here. + self.shortcuts.tabUp = QtGui.QShortcut( + QtGui.QKeySequence('Ctrl+PgDown'), self, + context=QtCore.Qt.WidgetWithChildrenShortcut) + self.shortcuts.tabDn = QtGui.QShortcut( + QtGui.QKeySequence('Ctrl+PgUp'), self, + context=QtCore.Qt.WidgetWithChildrenShortcut) + + self.connect(self.shortcuts.tabNext, QtCore.SIGNAL('activated()'), + self, QtCore.SLOT('nudgeTabNext()')) + self.connect(self.shortcuts.tabUp, QtCore.SIGNAL('activated()'), + self, QtCore.SLOT('nudgeTabNext()')) + self.connect(self.shortcuts.tabLast, QtCore.SIGNAL('activated()'), + self, QtCore.SLOT('nudgeTabLast()')) + self.connect(self.shortcuts.tabDn, QtCore.SIGNAL('activated()'), + self, QtCore.SLOT('nudgeTabLast()')) + self.initTheme(self.mainwindow.theme) self.layout = QtGui.QVBoxLayout() self.layout.setContentsMargins(0,0,0,0) @@ -96,38 +121,39 @@ class PesterTabWindow(QtGui.QFrame): nexti = (self.tabIndices[self.currentConvo.title()] + 1) % self.tabs.count() self.tabs.setCurrentIndex(nexti) - elif (mods == QtCore.Qt.ControlModifier and - keypress in (QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown)): - # Inverted controls. Might add an option for this if people want - # it. - if keypress == QtCore.Qt.Key_PageDown: - direction = 1 - elif keypress == QtCore.Qt.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 = 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) - # Ensure this doesn't fall through normally. - # (Not an issue here, but this used to be on a TextArea.) - return + @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_PageDown: + #~ direction = 1 + #~elif keypress == QtCore.Qt.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 = 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.Mouse: @@ -450,8 +476,7 @@ class PesterText(QtGui.QTextEdit): def keyPressEvent(self, event): # First parent is the PesterConvo containing this. # Second parent is the PesterTabWindow containing *it*. - pass_to_super = (QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown, - QtCore.Qt.Key_Up, QtCore.Qt.Key_Down) + pass_to_super = (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down) parent = self.parent() key = event.key() keymods = event.modifiers() @@ -554,8 +579,6 @@ class PesterInput(QtGui.QLineEdit): prev = self.parent().history.prev() if prev is not None: self.setText(prev) - elif event.key() in [QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown]: - self.parent().textArea.keyPressEvent(event) self.parent().mainwindow.idler.time = 0 super(PesterInput, self).keyPressEvent(event) diff --git a/memos.py b/memos.py index a1230f2..ac20036 100644 --- a/memos.py +++ b/memos.py @@ -375,6 +375,9 @@ class PesterMemo(PesterConvo): self.userlist = RightClickList(self) self.userlist.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding)) self.userlist.optionsMenu = QtGui.QMenu(self) + self.pesterChumAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/pester"], self) + self.connect(self.pesterChumAction, QtCore.SIGNAL('triggered()'), + self, QtCore.SLOT('newPesterSlot()')) self.addchumAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self) self.connect(self.addchumAction, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('addChumSlot()')) @@ -390,6 +393,7 @@ class PesterMemo(PesterConvo): self.quirkDisableAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirkkill"], self) self.connect(self.quirkDisableAction, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('killQuirkUser()')) + self.userlist.optionsMenu.addAction(self.pesterChumAction) self.userlist.optionsMenu.addAction(self.addchumAction) # ban & op list added if we are op @@ -1024,6 +1028,12 @@ class PesterMemo(PesterConvo): msgbox.setText(self.mainwindow.theme["convo/text/kickedmemo"]) msgbox.setInformativeText("press 0k to rec0nnect or cancel to absc0nd") msgbox.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) + # Find the OK button and make it default + for b in msgbox.buttons(): + if msgbox.buttonRole(b) == QtGui.QMessageBox.AcceptRole: + # We found the 'OK' button, set it as the default + b.setAutoDefault(True) + break ret = msgbox.exec_() if ret == QtGui.QMessageBox.Ok: self.userlist.clear() @@ -1160,6 +1170,15 @@ class PesterMemo(PesterConvo): elif c.lower() == self.channel.lower() and h == "" and update[0] in ["+","-"]: self.updateChanModes(update, op) + @QtCore.pyqtSlot() + def newPesterSlot(self): + # We're opening a pester with someone in our user list. + user = self.userlist.currentItem() + if not user: + return + user = unicode(user.text()) + self.mainwindow.newConversation(user) + @QtCore.pyqtSlot() def addChumSlot(self): if not self.userlist.currentItem(): diff --git a/menus.py b/menus.py index e10534b..72da1e5 100644 --- a/menus.py +++ b/menus.py @@ -1730,6 +1730,9 @@ class LoadingScreen(QtGui.QDialog): self.loadinglabel = QtGui.QLabel("CONN3CT1NG", self) self.cancel = QtGui.QPushButton("QU1T >:?", self) self.ok = QtGui.QPushButton("R3CONN3CT >:]", self) + # Help reduce the number of accidental Pesterchum closures... :| + self.cancel.setAutoDefault(False) + self.ok.setAutoDefault(True) self.connect(self.cancel, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT('reject()')) self.connect(self.ok, QtCore.SIGNAL('clicked()'), diff --git a/pesterchum.py b/pesterchum.py index f55d91b..5fbbee6 100644 --- a/pesterchum.py +++ b/pesterchum.py @@ -1085,6 +1085,7 @@ class PesterWindow(MovingWindow): self.memos = CaseInsensitiveDict() self.tabconvo = None self.tabmemo = None + self.shortcuts = AttrDict() if "advanced" in options: self.advanced = options["advanced"] else: self.advanced = False @@ -1179,6 +1180,16 @@ class PesterWindow(MovingWindow): self.console.action = QtGui.QAction("Console", self) self.connect(self.console.action, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('showConsole()')) + self.console.shortcut = QtGui.QShortcut( + QtGui.QKeySequence("Ctrl+`"), self) + # Make sure the shortcut works anywhere. + # karxi: There's something wrong with the inheritance scheme here. + self.console.shortcut.setContext(QtCore.Qt.ApplicationShortcut) + self.connect(self.console.shortcut, QtCore.SIGNAL('activated()'), + self, QtCore.SLOT('showConsole()')) + #~# Use new-style connections + #~self.console.shortcut.activated.connect(self.showConsole) + # Apparently those can crash sometimes...c'est la vie. Can't use 'em. self.console.is_open = False filemenu = self.menu.addMenu(self.theme["main/menus/client/_name"]) @@ -2002,6 +2013,12 @@ class PesterWindow(MovingWindow): msgbox.setText("You're invited!") msgbox.setInformativeText("%s has invited you to the memo: %s\nWould you like to join them?" % (handle, channel)) msgbox.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) + # Find the Cancel button and make it default + for b in msgbox.buttons(): + if msgbox.buttonRole(b) == QtGui.QMessageBox.RejectRole: + # We found the 'Cancel' button, set it as the default + b.setAutoDefault(True) + break ret = msgbox.exec_() if ret == QtGui.QMessageBox.Ok: self.newMemo(unicode(channel), "+0:00") @@ -2299,6 +2316,11 @@ class PesterWindow(MovingWindow): secret = self.memochooser.secretChannel.isChecked() invite = self.memochooser.inviteChannel.isChecked() + # Join the ones on the list first + for SelectedMemo in self.memochooser.SelectedMemos(): + channel = "#"+unicode(SelectedMemo.target) + self.newMemo(channel, time) + if self.memochooser.newmemoname(): newmemo = self.memochooser.newmemoname() channel = unicode(newmemo).replace(" ", "_") @@ -2313,10 +2335,6 @@ class PesterWindow(MovingWindow): # the server has confirmed that we've joined.... self.newMemo(c, time, secret=secret, invite=invite) - for SelectedMemo in self.memochooser.SelectedMemos(): - channel = "#"+unicode(SelectedMemo.target) - self.newMemo(channel, time) - self.memochooser = None @QtCore.pyqtSlot() def memoChooserClose(self):