diff --git a/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy b/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy index 68dfe04a..a8c0b396 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy @@ -250,6 +250,10 @@ class OptionsController { model.maxChatLines = maxChatLines uiSettings.maxChatLines = maxChatLines + boolean chatNotifyMentions = view.chatNotifyMentionsCheckbox.model.isSelected() + model.chatNotifyMentions = chatNotifyMentions + uiSettings.chatNotifyMentions = chatNotifyMentions + boolean joinDefaultChatRoom = view.joinDefaultChatRoomCheckbox.model.isSelected() model.joinDefaultChatRoom = joinDefaultChatRoom settings.joinDefaultChatRoom = joinDefaultChatRoom diff --git a/gui/griffon-app/i18n/messages.properties b/gui/griffon-app/i18n/messages.properties index 12da9f5c..2af37955 100644 --- a/gui/griffon-app/i18n/messages.properties +++ b/gui/griffon-app/i18n/messages.properties @@ -5,6 +5,7 @@ EXIT=Exit OPEN_MUWIRE=Open MuWire NEW_MESSAGE=New MuWire Message NEW_MESSAGE_FROM=New message from {0} +NEW_CHAT_MENTION=Mentioned in chat # Startup general MUWIRE_WILL_EXIT=MuWire will now exit. @@ -399,6 +400,7 @@ OPTIONS_START_CHAT_SERVER_STARTUP=Start chat server on startup OPTIONS_MAX_CHAT_CONNECTIONS=Maximum chat connections (-1 means unlimited) OPTIONS_ADVERTISE_CHAT=Advertise chat ability in search results OPTIONS_MAX_CHAT_SCROLLBACK=Maximum lines of scrollback (-1 means unlimited) +OPTIONS_CHAT_NOTIFY_MENTIONS=Notify when mentioned OPTIONS_CHAT_WELCOME_FILE=Welcome message file OPTIONS_CHAT_JOIN_DEFAULT_ROOM=Automatically join default room on servers OPTIONS_CHAT_DEFAULT_ROOM=Default chat room (empty for none) diff --git a/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy b/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy index 5bf68730..031c9372 100644 --- a/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy @@ -91,6 +91,7 @@ class OptionsModel { @Observable int maxChatConnections @Observable boolean advertiseChat @Observable int maxChatLines + @Observable boolean chatNotifyMentions @Observable String chatWelcomeFile @Observable String defaultChatRoom @Observable boolean joinDefaultChatRoom @@ -186,6 +187,7 @@ class OptionsModel { maxChatConnections = settings.maxChatConnections advertiseChat = settings.advertiseChat maxChatLines = uiSettings.maxChatLines + chatNotifyMentions = uiSettings.chatNotifyMentions chatWelcomeFile = settings.chatWelcomeFile?.getAbsolutePath() defaultChatRoom = settings.defaultChatRoom joinDefaultChatRoom = settings.joinDefaultChatRoom diff --git a/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy b/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy index 4febbf3c..d9a11492 100644 --- a/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy @@ -246,6 +246,12 @@ class ChatRoomView { } void appendSay(String text, PersonaOrProfile sender, long timestamp) { + + if (settings.chatNotifyMentions && + sender.getPersona() != model.core.me && + text.contains("@${model.core.me.getHumanReadableName()}")) + chatNotificator.notifyMention() + StyledDocument doc = roomTextArea.getStyledDocument() def label = new DateLabel(timestamp) def style = doc.addStyle("newStyle", null) diff --git a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy index 6f0f4909..9fe60824 100644 --- a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy @@ -30,6 +30,7 @@ import javax.swing.tree.DefaultMutableTreeNode import java.awt.GridBagConstraints import java.awt.Image import java.awt.KeyboardFocusManager +import java.awt.TrayIcon import java.awt.Window import java.awt.event.ActionEvent import java.awt.event.KeyEvent @@ -797,6 +798,7 @@ class MainFrameView { } chatNotificator = new ChatNotificator(application.getMvcGroupManager(), + (TrayIcon)application.context.get("tray-icon"), (Window)application.getWindowManager().findWindow("main-frame"), (Image) builder.imageIcon("/comment.png").image) chatFavorites = new ChatFavorites(application) diff --git a/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy b/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy index 445376c0..a2b85a1c 100644 --- a/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy @@ -114,6 +114,7 @@ class OptionsView { def startChatServerCheckbox def maxChatConnectionsField + def chatNotifyMentionsCheckbox def advertiseChatCheckbox def maxChatLinesField def joinDefaultChatRoomCheckbox @@ -547,25 +548,36 @@ class OptionsView { panel (border : titledBorder(title : trans("OPTIONS_CHAT_SETTINGS"), border : etchedBorder(), titlePosition : TitledBorder.TOP), constraints : gbc(gridx : 0, gridy : 0, fill : GridBagConstraints.HORIZONTAL, weightx: 100)) { gridBagLayout() - label(text : trans("OPTIONS_START_CHAT_SERVER_STARTUP"), constraints : gbc(gridx: 0, gridy: 0, anchor: GridBagConstraints.LINE_START, weightx: 100)) - startChatServerCheckbox = checkBox(selected : bind{model.startChatServer}, constraints : gbc(gridx:2, gridy:0, anchor:GridBagConstraints.LINE_END)) - label(text : trans("OPTIONS_MAX_CHAT_CONNECTIONS"), constraints : gbc(gridx: 0, gridy:1, anchor:GridBagConstraints.LINE_START, weightx:100)) - maxChatConnectionsField = textField(text : bind {model.maxChatConnections}, constraints : gbc(gridx: 2, gridy : 1, anchor:GridBagConstraints.LINE_END), + int j = 0 + label(text : trans("OPTIONS_START_CHAT_SERVER_STARTUP"), constraints : gbc(gridx: 0, gridy: j, anchor: GridBagConstraints.LINE_START, weightx: 100)) + startChatServerCheckbox = checkBox(selected : bind{model.startChatServer}, constraints : gbc(gridx:2, gridy:j, anchor:GridBagConstraints.LINE_END)) + j++ + label(text : trans("OPTIONS_MAX_CHAT_CONNECTIONS"), constraints : gbc(gridx: 0, gridy:j, anchor:GridBagConstraints.LINE_START, weightx:100)) + maxChatConnectionsField = textField(text : bind {model.maxChatConnections}, constraints : gbc(gridx: 2, gridy : j, anchor:GridBagConstraints.LINE_END), columns: COLUMNS, horizontalAlignment: JTextField.RIGHT) - label(text : trans("OPTIONS_ADVERTISE_CHAT"), constraints : gbc(gridx: 0, gridy:2, anchor:GridBagConstraints.LINE_START, weightx:100)) - advertiseChatCheckbox = checkBox(selected : bind{model.advertiseChat}, constraints : gbc(gridx:2, gridy:2, anchor:GridBagConstraints.LINE_END)) - label(text : trans("OPTIONS_MAX_CHAT_SCROLLBACK"), constraints : gbc(gridx:0, gridy:3, anchor : GridBagConstraints.LINE_START, weightx: 100)) - maxChatLinesField = textField(text : bind{model.maxChatLines}, constraints : gbc(gridx:2, gridy: 3, anchor: GridBagConstraints.LINE_END), + j++ + label(text : trans("OPTIONS_ADVERTISE_CHAT"), constraints : gbc(gridx: 0, gridy:j, anchor:GridBagConstraints.LINE_START, weightx:100)) + advertiseChatCheckbox = checkBox(selected : bind{model.advertiseChat}, constraints : gbc(gridx:2, gridy:j, anchor:GridBagConstraints.LINE_END)) + j++ + label(text : trans("OPTIONS_MAX_CHAT_SCROLLBACK"), constraints : gbc(gridx:0, gridy:j, anchor : GridBagConstraints.LINE_START, weightx: 100)) + maxChatLinesField = textField(text : bind{model.maxChatLines}, constraints : gbc(gridx:2, gridy: j, anchor: GridBagConstraints.LINE_END), columns: COLUMNS, horizontalAlignment: JTextField.RIGHT) - label(text: trans("OPTIONS_CHAT_JOIN_DEFAULT_ROOM"), constraints: gbc(gridx: 0, gridy: 4, anchor: GridBagConstraints.LINE_START, weightx: 100)) - joinDefaultChatRoomCheckbox = checkBox(selected: bind{model.joinDefaultChatRoom}, constraints: gbc(gridx: 2, gridy: 4, anchor: GridBagConstraints.LINE_END)) - label(text: trans("OPTIONS_CHAT_DEFAULT_ROOM"), constraints: gbc(gridx: 0, gridy: 5, anchor: GridBagConstraints.LINE_START, weightx: 100)) - defaultChatRoomField = textField(text : bind { model.defaultChatRoom}, constraints: gbc(gridx: 2, gridy: 5, anchor: GridBagConstraints.LINE_END), + j++ + label(text : trans("OPTIONS_CHAT_NOTIFY_MENTIONS"), constraints: gbc(gridx: 0, gridy: j, anchor: GridBagConstraints.LINE_START, weightx: 100)) + chatNotifyMentionsCheckbox = checkBox(selected: bind {model.chatNotifyMentions}, constraints: gbc(gridx:2, gridy:j, anchor: GridBagConstraints.LINE_END)) + j++ + label(text: trans("OPTIONS_CHAT_JOIN_DEFAULT_ROOM"), constraints: gbc(gridx: 0, gridy: j, anchor: GridBagConstraints.LINE_START, weightx: 100)) + joinDefaultChatRoomCheckbox = checkBox(selected: bind{model.joinDefaultChatRoom}, constraints: gbc(gridx: 2, gridy: j, anchor: GridBagConstraints.LINE_END)) + j++ + label(text: trans("OPTIONS_CHAT_DEFAULT_ROOM"), constraints: gbc(gridx: 0, gridy: j, anchor: GridBagConstraints.LINE_START, weightx: 100)) + defaultChatRoomField = textField(text : bind { model.defaultChatRoom}, constraints: gbc(gridx: 2, gridy: j, anchor: GridBagConstraints.LINE_END), columns: COLUMNS * 2, horizontalAlignment: JTextField.RIGHT) + j++ if (!isAqua()) { - label(text : trans("OPTIONS_CHAT_WELCOME_FILE"), constraints : gbc(gridx : 0, gridy : 6, anchor : GridBagConstraints.LINE_START, weightx: 100)) - label(text : bind {model.chatWelcomeFile}, constraints : gbc(gridx : 1, gridy : 6)) - button(text : trans("CHOOSE"), constraints : gbc(gridx : 2, gridy : 6, anchor : GridBagConstraints.LINE_END), chooseChatFileAction) + label(text : trans("OPTIONS_CHAT_WELCOME_FILE"), constraints : gbc(gridx : 0, gridy : j, anchor : GridBagConstraints.LINE_START, weightx: 100)) + label(text : bind {model.chatWelcomeFile}, constraints : gbc(gridx : 1, gridy : j)) + button(text : trans("CHOOSE"), constraints : gbc(gridx : 2, gridy : j, anchor : GridBagConstraints.LINE_END), chooseChatFileAction) + j++ } } panel (border : titledBorder(title : trans("OPTIONS_MESSAGING_SETTINGS"), border : etchedBorder(), titlePosition : TitledBorder.TOP), diff --git a/gui/src/main/groovy/com/muwire/gui/ChatNotificator.groovy b/gui/src/main/groovy/com/muwire/gui/ChatNotificator.groovy index 0057207c..5b9a2a8b 100644 --- a/gui/src/main/groovy/com/muwire/gui/ChatNotificator.groovy +++ b/gui/src/main/groovy/com/muwire/gui/ChatNotificator.groovy @@ -9,6 +9,9 @@ import javax.swing.JTabbedPane import griffon.core.mvc.MVCGroupManager +import static com.muwire.gui.Translator.trans + +import java.awt.TrayIcon import java.awt.Window class ChatNotificator { @@ -20,6 +23,7 @@ class ChatNotificator { private final MVCGroupManager groupManager private final Window window private final Image image + private final TrayIcon trayIcon private boolean chatInFocus private String currentServerTab @@ -29,10 +33,17 @@ class ChatNotificator { private Listener listener - ChatNotificator(MVCGroupManager groupManager, Window window, Image image) { + ChatNotificator(MVCGroupManager groupManager, TrayIcon trayIcon, Window window, Image image) { this.groupManager = groupManager this.window = window this.image = image + this.trayIcon = trayIcon + } + + void notifyMention() { + if (trayIcon == null) + return + trayIcon.displayMessage(trans("NEW_CHAT_MENTION"), trans("NEW_CHAT_MENTION"), TrayIcon.MessageType.INFO) } void serverTabChanged(JTabbedPane source) { diff --git a/gui/src/main/groovy/com/muwire/gui/UISettings.groovy b/gui/src/main/groovy/com/muwire/gui/UISettings.groovy index 4bd6d402..bbc0fa88 100644 --- a/gui/src/main/groovy/com/muwire/gui/UISettings.groovy +++ b/gui/src/main/groovy/com/muwire/gui/UISettings.groovy @@ -29,6 +29,7 @@ class UISettings { boolean storeSearchHistory boolean groupByFile int maxChatLines + boolean chatNotifyMentions Set searchHistory Set openTabs boolean messageNotifications @@ -57,6 +58,7 @@ class UISettings { storeSearchHistory = Boolean.parseBoolean(props.getProperty("storeSearchHistory","true")) groupByFile = Boolean.parseBoolean(props.getProperty("groupByFile","false")) maxChatLines = Integer.parseInt(props.getProperty("maxChatLines","-1")) + chatNotifyMentions = Boolean.parseBoolean(props.getProperty("chatNotifyMentions", "true")) mainFrameX = Integer.parseInt(props.getProperty("mainFrameX","-1")) mainFrameY = Integer.parseInt(props.getProperty("mainFrameY","-1")) @@ -91,6 +93,8 @@ class UISettings { props.setProperty("storeSearchHistory", String.valueOf(storeSearchHistory)) props.setProperty("groupByFile", String.valueOf(groupByFile)) props.setProperty("maxChatLines", String.valueOf(maxChatLines)) + props.setProperty("chatNotifyMentions", String.valueOf(chatNotifyMentions)) + props.setProperty("fontStyle", String.valueOf(fontStyle)) if (font != null) props.setProperty("font", font)