diff --git a/TODO.mkdn b/TODO.mkdn index 687d5b8..a863fd5 100644 --- a/TODO.mkdn +++ b/TODO.mkdn @@ -87,6 +87,9 @@ Bugs * Log folder/file names are not case-sensitive, so they break on non-Windows systems * Capitalized /me's don't render (should forcibly lowercase them) +* Volume control doesn't work without pygame +* Sound on Linux doesn't work without pygame + Windows Bugs ------------ * XP SP2: sometimes mouse clicks dont register? must be some kinda crash diff --git a/convo.py b/convo.py index ace98f7..f5d690b 100644 --- a/convo.py +++ b/convo.py @@ -133,6 +133,7 @@ class PesterTabWindow(QtGui.QFrame): i = self.tabIndices[handle] self.tabs.setTabTextColor(i, QtGui.QColor(self.mainwindow.theme["%s/tabs/newmsgcolor" % (self.type)])) convo = self.convos[handle] + # Create a function for the icon to use def func(): convo.showChat() self.mainwindow.waitingMessages.addMessage(handle, func) @@ -557,8 +558,34 @@ class PesterConvo(QtGui.QFrame): self.connect(self.logchum, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('openChumLogs()')) + # For this, we'll want to use setChecked to toggle these so they match + # the user's setting. Alternately (better), use a tristate checkbox, so + # that they start semi-checked? + # Easiest solution: Implement a 'Mute' option that overrides all + # notifications for that window, save for mentions. + # TODO: Look into setting up theme support here. + self._beepToggle = QtGui.QAction("Beep on Message", self) + self._beepToggle.setCheckable(True) + self.connect(self._beepToggle, QtCore.SIGNAL('toggled(bool)'), + self, QtCore.SLOT('toggleBeep(bool)')) + + self._flashToggle = QtGui.QAction("Flash on Message", self) + self._flashToggle.setCheckable(True) + self.connect(self._flashToggle, QtCore.SIGNAL('toggled(bool)'), + self, QtCore.SLOT('toggleFlash(bool)')) + + self._muteToggle = QtGui.QAction("Mute Notifications", self) + self._muteToggle.setCheckable(True) + self.connect(self._muteToggle, QtCore.SIGNAL('toggled(bool)'), + self, QtCore.SLOT('toggleMute(bool)')) + self.optionsMenu.addAction(self.quirksOff) self.optionsMenu.addAction(self.oocToggle) + + self.optionsMenu.addAction(self._beepToggle) + self.optionsMenu.addAction(self._flashToggle) + self.optionsMenu.addAction(self._muteToggle) + self.optionsMenu.addAction(self.logchum) self.optionsMenu.addAction(self.addChumAction) self.optionsMenu.addAction(self.blockAction) @@ -568,6 +595,10 @@ class PesterConvo(QtGui.QFrame): 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: @@ -637,31 +668,53 @@ class PesterConvo(QtGui.QFrame): def notifyNewMessage(self): # first see if this conversation HASS the focus + title = self.title() + parent = self.parent() + memoblink = pesterblink = self.mainwindow.config.blink() + memoblink &= self.mainwindow.config.MBLINK + pesterblink &= self.mainwindow.config.PBLINK + print "{!s}.notifications_muted: {!s}".format(self, + self.notifications_muted) + mutednots = self.notifications_muted + mtsrc = self + if parent: + try: + mutednots = parent.notifications_muted + mtsrc = parent + except: + pass if not (self.hasFocus() or self.textArea.hasFocus() or self.textInput.hasFocus() or - (self.parent() and self.parent().convoHasFocus(self.title()))): + (parent and parent.convoHasFocus(title))): # ok if it has a tabconvo parent, send that the notify. - if self.parent(): - self.parent().notifyNewMessage(self.title()) - if type(self.parent()).__name__ == "PesterTabWindow": - if self.mainwindow.config.blink() & self.mainwindow.config.PBLINK: - self.mainwindow.gainAttention.emit(self.parent()) - elif type(self.parent()).__name__ == "MemoTabWindow": - if self.mainwindow.config.blink() & self.mainwindow.config.MBLINK: - self.mainwindow.gainAttention.emit(self.parent()) + if parent: + print "{!s}.notifications_muted: {!s}".format(mtsrc, + mutednots) + if not mutednots: + # Stop the icon from highlighting + parent.notifyNewMessage(title) + if type(parent).__name__ == "PesterTabWindow": + if self.always_flash or pesterblink: + self.mainwindow.gainAttention.emit(parent) + elif type(parent).__name__ == "MemoTabWindow": + if self.always_flash or memoblink: + self.mainwindow.gainAttention.emit(parent) # if not change the window title and update system tray else: self.newmessage = True - self.setWindowTitle(self.title()+"*") + 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(self.title(), func) - if type(self).__name__ == "PesterConvo": - if self.mainwindow.config.blink() & self.mainwindow.config.PBLINK: - self.mainwindow.gainAttention.emit(self) - elif type(self).__name__ == "PesterMemo": - if self.mainwindow.config.blink() & self.mainwindow.config.MBLINK: - self.mainwindow.gainAttention.emit(self) + self.mainwindow.waitingMessages.addMessage(title, func) + if not self.notifications_muted: + if type(self).__name__ == "PesterConvo": + if self.always_flash or pesterblink: + self.mainwindow.gainAttention.emit(self) + elif type(self).__name__ == "PesterMemo": + if self.always_flash or memoblink: + self.mainwindow.gainAttention.emit(self) def clearNewMessage(self): if self.parent(): @@ -759,6 +812,18 @@ class PesterConvo(QtGui.QFrame): self.mainwindow.chumList.pesterlogviewer.raise_() self.mainwindow.chumList.pesterlogviewer.activateWindow() + @QtCore.pyqtSlot(bool) + def toggleBeep(self, toggled): + self.always_beep = toggled + + @QtCore.pyqtSlot(bool) + def toggleFlash(self, toggled): + self.always_flash = toggled + + @QtCore.pyqtSlot(bool) + def toggleMute(self, toggled): + self.notifications_muted = toggled + messageSent = QtCore.pyqtSignal(QtCore.QString, QtCore.QString) windowClosed = QtCore.pyqtSignal(QtCore.QString) diff --git a/memos.py b/memos.py index 523edf6..de50e39 100644 --- a/memos.py +++ b/memos.py @@ -407,8 +407,29 @@ class PesterMemo(PesterConvo): self.invitechum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/invitechum"], self) self.connect(self.invitechum, QtCore.SIGNAL('triggered()'), self, QtCore.SLOT('inviteChums()')) + + self._beepToggle = QtGui.QAction("Beep on Message", self) + self._beepToggle.setCheckable(True) + self.connect(self._beepToggle, QtCore.SIGNAL('toggled(bool)'), + self, QtCore.SLOT('toggleBeep(bool)')) + + self._flashToggle = QtGui.QAction("Flash on Message", self) + self._flashToggle.setCheckable(True) + self.connect(self._flashToggle, QtCore.SIGNAL('toggled(bool)'), + self, QtCore.SLOT('toggleFlash(bool)')) + + self._muteToggle = QtGui.QAction("Mute Notifications", self) + self._muteToggle.setCheckable(True) + self.connect(self._muteToggle, QtCore.SIGNAL('toggled(bool)'), + self, QtCore.SLOT('toggleMute(bool)')) + self.optionsMenu.addAction(self.quirksOff) self.optionsMenu.addAction(self.oocToggle) + + self.optionsMenu.addAction(self._beepToggle) + self.optionsMenu.addAction(self._flashToggle) + self.optionsMenu.addAction(self._muteToggle) + self.optionsMenu.addAction(self.logchum) self.optionsMenu.addAction(self.invitechum) @@ -508,6 +529,10 @@ class PesterMemo(PesterConvo): self.applyquirks = True self.ooc = False + self.always_beep = False + self.always_flash = False + self.notifications_muted = False + @QtCore.pyqtSlot() def toggleUserlist(self): if self.userlist.isHidden(): diff --git a/parsetools.py b/parsetools.py index 6e925e5..c14cc38 100644 --- a/parsetools.py +++ b/parsetools.py @@ -719,6 +719,7 @@ def kxhandleInput(ctx, text=None, flavor=None): print repr(msg) except Exception as err: print "(Couldn't print processed message: {!s})".format(err) + # karxi: We have a list...but I'm not sure if we ever get anything else, so # best to play it safe. I may remove this during later refactoring. if isinstance(msg, list): @@ -734,6 +735,12 @@ def kxhandleInput(ctx, text=None, flavor=None): # Quirks have been applied. Lex the messages (finally). msg = kxlexMsg(msg) + # Debug output. + try: + print repr(msg) + except Exception as err: + print "(Couldn't print lexed message: {!s})".format(err) + # Remove coloring if this is a /me! if is_action: # Filter out formatting specifiers (just ctags, at the moment). diff --git a/pesterchum.py b/pesterchum.py index 0b5e28d..423d577 100644 --- a/pesterchum.py +++ b/pesterchum.py @@ -1350,6 +1350,7 @@ class PesterWindow(MovingWindow): def newMemoMsg(self, chan, handle, msg): if not self.memos.has_key(chan): # silently ignore in case we forgot to /part + # TODO: This is really bad practice. Fix it later. return memo = self.memos[chan] msg = unicode(msg) @@ -1358,7 +1359,7 @@ class PesterWindow(MovingWindow): newtime = timedelta(0) time = TimeTracker(newtime) memo.times[handle] = time - if msg[0:3] != "/me" and msg[0:13] != "PESTERCHUM:ME": + if not (msg.startswith("/me") or msg.startswith("PESTERCHUM:ME")): msg = addTimeInitial(msg, memo.times[handle].getGrammar()) if handle == "ChanServ": systemColor = QtGui.QColor(self.theme["memos/systemMsgColor"]) @@ -1383,10 +1384,12 @@ class PesterWindow(MovingWindow): if mentioned: self.namesound.play() return - if self.honk and re.search(r"\bhonk\b", convertTags(msg, "text"), re.I): - self.honksound.play() - elif self.config.memoPing(): - self.memosound.play() + if not memo.notifications_muted: + if self.honk and re.search(r"\bhonk\b", convertTags(msg, "text"), re.I): + # TODO: I've got my eye on you, Gamzee. + self.honksound.play() + elif self.config.memoPing() or memo.always_beep: + self.memosound.play() def changeColor(self, handle, color): # pesterconvo and chumlist