about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/command/command.c85
-rw-r--r--src/command/commands.c109
-rw-r--r--src/config/preferences.c266
-rw-r--r--src/config/preferences.h19
-rw-r--r--src/ui/chatwin.c22
-rw-r--r--src/ui/console.c28
-rw-r--r--src/ui/mucwin.c47
-rw-r--r--src/ui/notifier.c40
-rw-r--r--src/ui/privwin.c22
-rw-r--r--src/ui/ui.h7
-rw-r--r--src/ui/win_types.h3
-rw-r--r--src/ui/window.c23
-rw-r--r--src/window_list.c20
-rw-r--r--src/window_list.h1
-rw-r--r--tests/unittests/ui/stub_ui.c6
15 files changed, 606 insertions, 92 deletions
diff --git a/src/command/command.c b/src/command/command.c
index 3e9adc9c..b52952b7 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -1054,7 +1054,7 @@ static struct cmd_t command_defs[] =
     },
 
     { "/notify",
-        cmd_notify, parse_args, 2, 3, &cons_notify_setting,
+        cmd_notify, parse_args_with_freetext, 2, 4, &cons_notify_setting,
         CMD_TAGS(
             CMD_TAG_UI,
             CMD_TAG_CHAT,
@@ -1063,9 +1063,18 @@ static struct cmd_t command_defs[] =
             "/notify message on|off",
             "/notify message current on|off",
             "/notify message text on|off",
-            "/notify room on|off|mention",
+            "/notify message trigger add <text>",
+            "/notify message trigger remove <text>",
+            "/notify message trigger list",
+            "/notify message trigger on|off",
+            "/notify room on|off",
+            "/notify room mention on|off",
             "/notify room current on|off",
             "/notify room text on|off",
+            "/notify room trigger add <text>",
+            "/notify room trigger remove <text>",
+            "/notify room trigger list",
+            "/notify room trigger on|off",
             "/notify remind <seconds>",
             "/notify typing on|off",
             "/notify typing current on|off",
@@ -1074,21 +1083,30 @@ static struct cmd_t command_defs[] =
         CMD_DESC(
             "Settings for various kinds of desktop notifications.")
         CMD_ARGS(
-            { "message on|off", "Notifications for regular chat messages." },
-            { "message current on|off", "Whether messages in the current window trigger notifications." },
-            { "message text on|off", "Show message text in regular message notifications." },
-            { "room on|off|mention", "Notifications for chat room messages, mention triggers notifications only when your nick is mentioned." },
-            { "room current on|off", "Whether chat room messages in the current window trigger notifications." },
-            { "room text on|off", "Show message text in chat room message notifications." },
-            { "remind <seconds>", "Notification reminder period for unread messages, use 0 to disable." },
-            { "typing on|off", "Notifications when contacts are typing." },
-            { "typing current on|off", "Whether typing notifications are triggered for the current window." },
-            { "invite on|off", "Notifications for chat room invites." },
-            { "sub on|off", "Notifications for subscription requests." })
+            { "message on|off",                 "Notifications for regular chat messages." },
+            { "message current on|off",         "Whether messages in the current window trigger notifications." },
+            { "message text on|off",            "Show message text in regular message notifications." },
+            { "message trigger add <text>",     "Notify when specified text included in regular chat message." },
+            { "message trigger remove <text>",  "Remove regular chat notification for specified text." },
+            { "message trigger list",           "List all regular chat custom text notifications." },
+            { "message trigger on|off",         "Enable or disable all regular chat custom text notifications." },
+            { "room on|off",                    "Notifications for chat room messages, mention triggers notifications only when your nick is mentioned." },
+            { "room mention on|off",            "Notifications for chat room messages when your nick is mentioned." },
+            { "room current on|off",            "Whether chat room messages in the current window trigger notifications." },
+            { "room text on|off",               "Show message text in chat room message notifications." },
+            { "room trigger add <text>",        "Notify when specified text included in regular chat message." },
+            { "room trigger remove <text>",     "Remove regular chat notification for specified text." },
+            { "room trigger list",              "List all regular chat custom text notifications." },
+            { "room trigger on|off",            "Enable or disable all regular chat custom text notifications." },
+            { "remind <seconds>",               "Notification reminder period for unread messages, use 0 to disable." },
+            { "typing on|off",                  "Notifications when contacts are typing." },
+            { "typing current on|off",          "Whether typing notifications are triggered for the current window." },
+            { "invite on|off",                  "Notifications for chat room invites." },
+            { "sub on|off",                     "Notifications for subscription requests." })
         CMD_EXAMPLES(
             "/notify message on",
             "/notify message text on",
-            "/notify room mention",
+            "/notify room mention on",
             "/notify room current off",
             "/notify room text off",
             "/notify remind 10",
@@ -1791,6 +1809,7 @@ static Autocomplete notify_ac;
 static Autocomplete notify_room_ac;
 static Autocomplete notify_message_ac;
 static Autocomplete notify_typing_ac;
+static Autocomplete notify_trigger_ac;
 static Autocomplete prefs_ac;
 static Autocomplete sub_ac;
 static Autocomplete log_ac;
@@ -1929,6 +1948,7 @@ cmd_init(void)
     autocomplete_add(notify_message_ac, "off");
     autocomplete_add(notify_message_ac, "current");
     autocomplete_add(notify_message_ac, "text");
+    autocomplete_add(notify_message_ac, "trigger");
 
     notify_room_ac = autocomplete_new();
     autocomplete_add(notify_room_ac, "on");
@@ -1936,12 +1956,20 @@ cmd_init(void)
     autocomplete_add(notify_room_ac, "mention");
     autocomplete_add(notify_room_ac, "current");
     autocomplete_add(notify_room_ac, "text");
+    autocomplete_add(notify_room_ac, "trigger");
 
     notify_typing_ac = autocomplete_new();
     autocomplete_add(notify_typing_ac, "on");
     autocomplete_add(notify_typing_ac, "off");
     autocomplete_add(notify_typing_ac, "current");
 
+    notify_trigger_ac = autocomplete_new();
+    autocomplete_add(notify_trigger_ac, "add");
+    autocomplete_add(notify_trigger_ac, "remove");
+    autocomplete_add(notify_trigger_ac, "list");
+    autocomplete_add(notify_trigger_ac, "on");
+    autocomplete_add(notify_trigger_ac, "off");
+
     sub_ac = autocomplete_new();
     autocomplete_add(sub_ac, "request");
     autocomplete_add(sub_ac, "allow");
@@ -2338,6 +2366,7 @@ cmd_uninit(void)
     autocomplete_free(notify_message_ac);
     autocomplete_free(notify_room_ac);
     autocomplete_free(notify_typing_ac);
+    autocomplete_free(notify_trigger_ac);
     autocomplete_free(sub_ac);
     autocomplete_free(titlebar_ac);
     autocomplete_free(log_ac);
@@ -2524,6 +2553,7 @@ cmd_reset_autocomplete(ProfWin *window)
     autocomplete_reset(notify_message_ac);
     autocomplete_reset(notify_room_ac);
     autocomplete_reset(notify_typing_ac);
+    autocomplete_reset(notify_trigger_ac);
     autocomplete_reset(sub_ac);
 
     autocomplete_reset(who_room_ac);
@@ -2622,6 +2652,8 @@ cmd_reset_autocomplete(ProfWin *window)
     }
 
     bookmark_autocomplete_reset();
+    prefs_reset_message_trigger_ac();
+    prefs_reset_room_trigger_ac();
 }
 
 gboolean
@@ -3136,16 +3168,36 @@ _notify_autocomplete(ProfWin *window, const char *const input)
     int i = 0;
     char *result = NULL;
 
+    result = autocomplete_param_with_func(input, "/notify message trigger remove", prefs_autocomplete_message_trigger);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_func(input, "/notify room trigger remove", prefs_autocomplete_room_trigger);
+    if (result) {
+        return result;
+    }
+
     result = autocomplete_param_with_func(input, "/notify room current", prefs_autocomplete_boolean_choice);
     if (result) {
         return result;
     }
 
+    result = autocomplete_param_with_ac(input, "/notify room trigger", notify_trigger_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
     result = autocomplete_param_with_func(input, "/notify message current", prefs_autocomplete_boolean_choice);
     if (result) {
         return result;
     }
 
+    result = autocomplete_param_with_ac(input, "/notify message trigger", notify_trigger_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
     result = autocomplete_param_with_func(input, "/notify typing current", prefs_autocomplete_boolean_choice);
     if (result) {
         return result;
@@ -3156,6 +3208,11 @@ _notify_autocomplete(ProfWin *window, const char *const input)
         return result;
     }
 
+    result = autocomplete_param_with_func(input, "/notify room mention", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+
     result = autocomplete_param_with_func(input, "/notify message text", prefs_autocomplete_boolean_choice);
     if (result) {
         return result;
diff --git a/src/command/commands.c b/src/command/commands.c
index 5781ae5c..b87cf1f5 100644
--- a/src/command/commands.c
+++ b/src/command/commands.c
@@ -4302,6 +4302,51 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
             } else {
                 cons_show("Usage: /notify message text on|off");
             }
+        } else if (g_strcmp0(args[1], "trigger") == 0) {
+            if (g_strcmp0(args[2], "add") == 0) {
+                if (!args[3]) {
+                    cons_bad_cmd_usage(command);
+                } else {
+                    gboolean res = prefs_add_msg_notify_trigger(args[3]);
+                    if (res) {
+                        cons_show("Adding message notification trigger: %s", args[3]);
+                    } else {
+                        cons_show("Message notification trigger already exists: %s", args[3]);
+                    }
+                }
+            } else if (g_strcmp0(args[2], "remove") == 0) {
+                if (!args[3]) {
+                    cons_bad_cmd_usage(command);
+                } else {
+                    gboolean res = prefs_remove_msg_notify_trigger(args[3]);
+                    if (res) {
+                        cons_show("Removing message notification trigger: %s", args[3]);
+                    } else {
+                        cons_show("Message notification trigger does not exist: %s", args[3]);
+                    }
+                }
+            } else if (g_strcmp0(args[2], "list") == 0) {
+                GList *triggers = prefs_get_msg_notify_triggers();
+                GList *curr = triggers;
+                if (curr) {
+                    cons_show("Message notification triggers:");
+                } else {
+                    cons_show("No message notification triggers");
+                }
+                while (curr) {
+                    cons_show("  %s", curr->data);
+                    curr = g_list_next(curr);
+                }
+                g_list_free_full(triggers, free);
+            } else if (g_strcmp0(args[2], "on") == 0) {
+                cons_show("Enabling message notification triggers");
+                prefs_set_boolean(PREF_NOTIFY_MESSAGE_TRIGGER, TRUE);
+            } else if (g_strcmp0(args[2], "off") == 0) {
+                cons_show("Disabling message notification triggers");
+                prefs_set_boolean(PREF_NOTIFY_MESSAGE_TRIGGER, FALSE);
+            } else {
+                cons_bad_cmd_usage(command);
+            }
         } else {
             cons_show("Usage: /notify message on|off");
         }
@@ -4309,14 +4354,21 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
     // set room setting
     } else if (strcmp(kind, "room") == 0) {
         if (strcmp(args[1], "on") == 0) {
-            cons_show("Chat room notifications enabled.");
-            prefs_set_string(PREF_NOTIFY_ROOM, "on");
+            cons_show("Room notifications enabled.");
+            prefs_set_boolean(PREF_NOTIFY_ROOM, TRUE);
         } else if (strcmp(args[1], "off") == 0) {
-            cons_show("Chat room notifications disabled.");
-            prefs_set_string(PREF_NOTIFY_ROOM, "off");
+            cons_show("Room notifications disabled.");
+            prefs_set_boolean(PREF_NOTIFY_ROOM, FALSE);
         } else if (strcmp(args[1], "mention") == 0) {
-            cons_show("Chat room notifications enabled on mention.");
-            prefs_set_string(PREF_NOTIFY_ROOM, "mention");
+            if (strcmp(args[2], "on") == 0) {
+                cons_show("Room notifications with mention enabled.");
+                prefs_set_boolean(PREF_NOTIFY_ROOM_MENTION, TRUE);
+            } else if (strcmp(args[2], "off") == 0) {
+                cons_show("Room notifications with mention disabled.");
+                prefs_set_boolean(PREF_NOTIFY_ROOM_MENTION, FALSE);
+            } else {
+                cons_show("Usage: /notify room mention on|off");
+            }
         } else if (strcmp(args[1], "current") == 0) {
             if (g_strcmp0(args[2], "on") == 0) {
                 cons_show("Current window chat room message notifications enabled.");
@@ -4337,6 +4389,51 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
             } else {
                 cons_show("Usage: /notify room text on|off");
             }
+        } else if (g_strcmp0(args[1], "trigger") == 0) {
+            if (g_strcmp0(args[2], "add") == 0) {
+                if (!args[3]) {
+                    cons_bad_cmd_usage(command);
+                } else {
+                    gboolean res = prefs_add_room_notify_trigger(args[3]);
+                    if (res) {
+                        cons_show("Adding room notification trigger: %s", args[3]);
+                    } else {
+                        cons_show("Room notification trigger already exists: %s", args[3]);
+                    }
+                }
+            } else if (g_strcmp0(args[2], "remove") == 0) {
+                if (!args[3]) {
+                    cons_bad_cmd_usage(command);
+                } else {
+                    gboolean res = prefs_remove_room_notify_trigger(args[3]);
+                    if (res) {
+                        cons_show("Removing room notification trigger: %s", args[3]);
+                    } else {
+                        cons_show("Room notification trigger does not exist: %s", args[3]);
+                    }
+                }
+            } else if (g_strcmp0(args[2], "list") == 0) {
+                GList *triggers = prefs_get_room_notify_triggers();
+                GList *curr = triggers;
+                if (curr) {
+                    cons_show("Room notification triggers:");
+                } else {
+                    cons_show("No room notification triggers");
+                }
+                while (curr) {
+                    cons_show("  %s", curr->data);
+                    curr = g_list_next(curr);
+                }
+                g_list_free_full(triggers, free);
+            } else if (g_strcmp0(args[2], "on") == 0) {
+                cons_show("Enabling room notification triggers");
+                prefs_set_boolean(PREF_NOTIFY_ROOM_TRIGGER, TRUE);
+            } else if (g_strcmp0(args[2], "off") == 0) {
+                cons_show("Disabling room notification triggers");
+                prefs_set_boolean(PREF_NOTIFY_ROOM_TRIGGER, FALSE);
+            } else {
+                cons_bad_cmd_usage(command);
+            }
         } else {
             cons_show("Usage: /notify room on|off|mention");
         }
diff --git a/src/config/preferences.c b/src/config/preferences.c
index 07985efc..fa371cf0 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -45,6 +45,7 @@
 #include "log.h"
 #include "preferences.h"
 #include "tools/autocomplete.h"
+#include "config/conflists.h"
 
 // preference groups refer to the sections in .profrc, for example [ui]
 #define PREF_GROUP_LOGGING "logging"
@@ -64,6 +65,8 @@ static GKeyFile *prefs;
 gint log_maxsize = 0;
 
 static Autocomplete boolean_choice_ac;
+static Autocomplete message_trigger_ac;
+static Autocomplete room_trigger_ac;
 
 static void _save_prefs(void);
 static gchar* _get_preferences_file(void);
@@ -127,17 +130,51 @@ prefs_load(void)
         prefs_free_string(time);
     }
 
+    // move pre 0.4.8 notify settings
+    if (g_key_file_has_key(prefs, PREF_GROUP_NOTIFICATIONS, "room", NULL)) {
+        char *value = g_key_file_get_string(prefs, PREF_GROUP_NOTIFICATIONS, "room", NULL);
+        if (g_strcmp0(value, "on") == 0) {
+            g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room", TRUE);
+        } else if (g_strcmp0(value, "off") == 0) {
+            g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room", FALSE);
+        } else if (g_strcmp0(value, "mention") == 0) {
+            g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room", FALSE);
+            g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room.mention", TRUE);
+        }
+    }
+
     _save_prefs();
 
     boolean_choice_ac = autocomplete_new();
     autocomplete_add(boolean_choice_ac, "on");
     autocomplete_add(boolean_choice_ac, "off");
+
+    message_trigger_ac = autocomplete_new();
+    gsize len = 0;
+    gchar **triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "message.trigger.list", &len, NULL);
+
+    int i;
+    for (i = 0; i < len; i++) {
+        autocomplete_add(message_trigger_ac, triggers[i]);
+    }
+    g_strfreev(triggers);
+
+    room_trigger_ac = autocomplete_new();
+    len = 0;
+    triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
+
+    for (i = 0; i < len; i++) {
+        autocomplete_add(room_trigger_ac, triggers[i]);
+    }
+    g_strfreev(triggers);
 }
 
 void
 prefs_close(void)
 {
     autocomplete_free(boolean_choice_ac);
+    autocomplete_free(message_trigger_ac);
+    autocomplete_free(room_trigger_ac);
     g_key_file_free(prefs);
     prefs = NULL;
 }
@@ -154,6 +191,137 @@ prefs_reset_boolean_choice(void)
     autocomplete_reset(boolean_choice_ac);
 }
 
+char*
+prefs_autocomplete_message_trigger(const char *const prefix)
+{
+    return autocomplete_complete(message_trigger_ac, prefix, TRUE);
+}
+
+void
+prefs_reset_message_trigger_ac(void)
+{
+    autocomplete_reset(message_trigger_ac);
+}
+
+char*
+prefs_autocomplete_room_trigger(const char *const prefix)
+{
+    return autocomplete_complete(room_trigger_ac, prefix, TRUE);
+}
+
+void
+prefs_reset_room_trigger_ac(void)
+{
+    autocomplete_reset(room_trigger_ac);
+}
+
+
+
+gboolean
+prefs_get_notify_chat(gboolean current_win, const char *const message)
+{
+    gboolean notify_current = prefs_get_boolean(PREF_NOTIFY_MESSAGE_CURRENT);
+    gboolean notify_window = FALSE;
+    if (!current_win || (current_win && notify_current) ) {
+        notify_window = TRUE;
+    }
+    if (!notify_window) {
+        return FALSE;
+    }
+
+    gboolean notify_message = prefs_get_boolean(PREF_NOTIFY_MESSAGE);
+    if (notify_message) {
+        return TRUE;
+    }
+
+    gboolean notify_trigger = prefs_get_boolean(PREF_NOTIFY_MESSAGE_TRIGGER);
+    if (!notify_trigger) {
+        return FALSE;
+    }
+
+    gboolean trigger_found = FALSE;
+    char *message_lower = g_utf8_strdown(message, -1);
+    gsize len = 0;
+    gchar **triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "message.trigger.list", &len, NULL);
+    int i;
+    for (i = 0; i < len; i++) {
+        char *trigger_lower = g_utf8_strdown(triggers[i], -1);
+        if (g_strrstr(message_lower, trigger_lower)) {
+            trigger_found = TRUE;
+            g_free(trigger_lower);
+            break;
+        }
+        g_free(trigger_lower);
+    }
+    g_strfreev(triggers);
+    g_free(message_lower);
+
+    if (trigger_found) {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+gboolean
+prefs_get_notify_room(gboolean current_win, const char *const nick, const char *const message)
+{
+    gboolean notify_current = prefs_get_boolean(PREF_NOTIFY_ROOM_CURRENT);
+    gboolean notify_window = FALSE;
+    if (!current_win || (current_win && notify_current) ) {
+        notify_window = TRUE;
+    }
+    if (!notify_window) {
+        return FALSE;
+    }
+
+    gboolean notify_room = prefs_get_boolean(PREF_NOTIFY_ROOM);
+    if (notify_room) {
+        return TRUE;
+    }
+
+    gboolean notify_mention = prefs_get_boolean(PREF_NOTIFY_ROOM_MENTION);
+    if (notify_mention) {
+        char *message_lower = g_utf8_strdown(message, -1);
+        char *nick_lower = g_utf8_strdown(nick, -1);
+        if (g_strrstr(message_lower, nick_lower)) {
+            g_free(message_lower);
+            g_free(nick_lower);
+            return TRUE;
+        }
+        g_free(message_lower);
+        g_free(nick_lower);
+    }
+
+    gboolean notify_trigger = prefs_get_boolean(PREF_NOTIFY_ROOM_TRIGGER);
+    if (!notify_trigger) {
+        return FALSE;
+    }
+
+    gboolean trigger_found = FALSE;
+    char *message_lower = g_utf8_strdown(message, -1);
+    gsize len = 0;
+    gchar **triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
+    int i;
+    for (i = 0; i < len; i++) {
+        char *trigger_lower = g_utf8_strdown(triggers[i], -1);
+        if (g_strrstr(message_lower, trigger_lower)) {
+            trigger_found = TRUE;
+            g_free(trigger_lower);
+            break;
+        }
+        g_free(trigger_lower);
+    }
+    g_strfreev(triggers);
+    g_free(message_lower);
+
+    if (trigger_found) {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
 gboolean
 prefs_get_boolean(preference_t pref)
 {
@@ -607,6 +775,92 @@ prefs_set_roster_presence_indent(gint value)
 }
 
 gboolean
+prefs_add_msg_notify_trigger(const char * const text)
+{
+    gboolean res = conf_string_list_add(prefs, PREF_GROUP_NOTIFICATIONS, "message.trigger.list", text);
+    _save_prefs();
+
+    if (res) {
+        autocomplete_add(message_trigger_ac, text);
+    }
+
+    return res;
+}
+
+gboolean
+prefs_remove_msg_notify_trigger(const char * const text)
+{
+    gboolean res = conf_string_list_remove(prefs, PREF_GROUP_NOTIFICATIONS, "message.trigger.list", text);
+    _save_prefs();
+
+    if (res) {
+        autocomplete_remove(message_trigger_ac, text);
+    }
+
+    return res;
+}
+
+GList*
+prefs_get_msg_notify_triggers(void)
+{
+    GList *result = NULL;
+    gsize len = 0;
+    gchar **triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "message.trigger.list", &len, NULL);
+
+    int i;
+    for (i = 0; i < len; i++) {
+        result = g_list_append(result, strdup(triggers[i]));
+    }
+
+    g_strfreev(triggers);
+
+    return result;
+}
+
+gboolean
+prefs_add_room_notify_trigger(const char * const text)
+{
+    gboolean res = conf_string_list_add(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", text);
+    _save_prefs();
+
+    if (res) {
+        autocomplete_add(room_trigger_ac, text);
+    }
+
+    return res;
+}
+
+gboolean
+prefs_remove_room_notify_trigger(const char * const text)
+{
+    gboolean res = conf_string_list_remove(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", text);
+    _save_prefs();
+
+    if (res) {
+        autocomplete_remove(room_trigger_ac, text);
+    }
+
+    return res;
+}
+
+GList*
+prefs_get_room_notify_triggers(void)
+{
+    GList *result = NULL;
+    gsize len = 0;
+    gchar **triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
+
+    int i;
+    for (i = 0; i < len; i++) {
+        result = g_list_append(result, strdup(triggers[i]));
+    }
+
+    g_strfreev(triggers);
+
+    return result;
+}
+
+gboolean
 prefs_add_alias(const char *const name, const char *const value)
 {
     if (g_key_file_has_key(prefs, PREF_GROUP_ALIAS, name, NULL)) {
@@ -780,9 +1034,12 @@ _get_group(preference_t pref)
         case PREF_NOTIFY_TYPING:
         case PREF_NOTIFY_TYPING_CURRENT:
         case PREF_NOTIFY_MESSAGE:
+        case PREF_NOTIFY_MESSAGE_TRIGGER:
         case PREF_NOTIFY_MESSAGE_CURRENT:
         case PREF_NOTIFY_MESSAGE_TEXT:
         case PREF_NOTIFY_ROOM:
+        case PREF_NOTIFY_ROOM_MENTION:
+        case PREF_NOTIFY_ROOM_TRIGGER:
         case PREF_NOTIFY_ROOM_CURRENT:
         case PREF_NOTIFY_ROOM_TEXT:
         case PREF_NOTIFY_INVITE:
@@ -871,12 +1128,18 @@ _get_key(preference_t pref)
             return "typing.current";
         case PREF_NOTIFY_MESSAGE:
             return "message";
+        case PREF_NOTIFY_MESSAGE_TRIGGER:
+            return "message.trigger";
         case PREF_NOTIFY_MESSAGE_CURRENT:
             return "message.current";
         case PREF_NOTIFY_MESSAGE_TEXT:
             return "message.text";
         case PREF_NOTIFY_ROOM:
             return "room";
+        case PREF_NOTIFY_ROOM_TRIGGER:
+            return "room.trigger";
+        case PREF_NOTIFY_ROOM_MENTION:
+            return "room.mention";
         case PREF_NOTIFY_ROOM_CURRENT:
             return "room.current";
         case PREF_NOTIFY_ROOM_TEXT:
@@ -989,6 +1252,7 @@ _get_default_boolean(preference_t pref)
         case PREF_LOG_SHARED:
         case PREF_NOTIFY_MESSAGE:
         case PREF_NOTIFY_MESSAGE_CURRENT:
+        case PREF_NOTIFY_ROOM:
         case PREF_NOTIFY_ROOM_CURRENT:
         case PREF_NOTIFY_TYPING:
         case PREF_NOTIFY_TYPING_CURRENT:
@@ -1026,8 +1290,6 @@ _get_default_string(preference_t pref)
     {
         case PREF_AUTOAWAY_MODE:
             return "off";
-        case PREF_NOTIFY_ROOM:
-            return "on";
         case PREF_OTR_LOG:
             return "redact";
         case PREF_OTR_POLICY:
diff --git a/src/config/preferences.h b/src/config/preferences.h
index 607a1abe..56c87d92 100644
--- a/src/config/preferences.h
+++ b/src/config/preferences.h
@@ -94,9 +94,12 @@ typedef enum {
     PREF_NOTIFY_TYPING,
     PREF_NOTIFY_TYPING_CURRENT,
     PREF_NOTIFY_MESSAGE,
+    PREF_NOTIFY_MESSAGE_TRIGGER,
     PREF_NOTIFY_MESSAGE_CURRENT,
     PREF_NOTIFY_MESSAGE_TEXT,
     PREF_NOTIFY_ROOM,
+    PREF_NOTIFY_ROOM_MENTION,
+    PREF_NOTIFY_ROOM_TRIGGER,
     PREF_NOTIFY_ROOM_CURRENT,
     PREF_NOTIFY_ROOM_TEXT,
     PREF_NOTIFY_INVITE,
@@ -133,9 +136,15 @@ void prefs_close(void);
 
 char* prefs_find_login(char *prefix);
 void prefs_reset_login_search(void);
+
 char* prefs_autocomplete_boolean_choice(const char *const prefix);
 void prefs_reset_boolean_choice(void);
 
+char* prefs_autocomplete_message_trigger(const char *const prefix);
+void prefs_reset_message_trigger_ac(void);
+char* prefs_autocomplete_room_trigger(const char *const prefix);
+void prefs_reset_room_trigger_ac(void);
+
 gint prefs_get_gone(void);
 void prefs_set_gone(gint value);
 
@@ -192,10 +201,20 @@ char* prefs_get_alias(const char *const name);
 GList* prefs_get_aliases(void);
 void prefs_free_aliases(GList *aliases);
 
+gboolean prefs_add_msg_notify_trigger(const char * const text);
+gboolean prefs_add_room_notify_trigger(const char * const text);
+gboolean prefs_remove_msg_notify_trigger(const char * const text);
+gboolean prefs_remove_room_notify_trigger(const char * const text);
+GList* prefs_get_msg_notify_triggers(void);
+GList* prefs_get_room_notify_triggers(void);
+
 gboolean prefs_get_boolean(preference_t pref);
 void prefs_set_boolean(preference_t pref, gboolean value);
 char* prefs_get_string(preference_t pref);
 void prefs_free_string(char *pref);
 void prefs_set_string(preference_t pref, char *value);
 
+gboolean prefs_get_notify_chat(gboolean current_win, const char *const message);
+gboolean prefs_get_notify_room(gboolean current_win, const char *const nick, const char *const message);
+
 #endif
diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c
index c34cc1fc..c8cee66b 100644
--- a/src/ui/chatwin.c
+++ b/src/ui/chatwin.c
@@ -239,6 +239,9 @@ chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const cha
 
     char *display_name = roster_get_msg_display_name(chatwin->barejid, resource);
 
+    gboolean is_current = wins_is_current(window);
+    gboolean notify = prefs_get_notify_chat(is_current, message);
+
     // currently viewing chat window with sender
     if (wins_is_current(window)) {
         win_print_incoming_message(window, timestamp, display_name, message, enc_mode);
@@ -255,6 +258,9 @@ chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const cha
         }
 
         chatwin->unread++;
+        if (notify) {
+            chatwin->notify = TRUE;
+        }
         if (prefs_get_boolean(PREF_CHLOG) && prefs_get_boolean(PREF_HISTORY)) {
             _chatwin_history(chatwin, chatwin->barejid);
         }
@@ -274,8 +280,20 @@ chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const cha
         beep();
     }
 
-    if (prefs_get_boolean(PREF_NOTIFY_MESSAGE)) {
-        notify_message(window, display_name, message);
+    if (!notify) {
+        free(display_name);
+        return;
+    }
+
+    int ui_index = num;
+    if (ui_index == 10) {
+        ui_index = 0;
+    }
+
+    if (prefs_get_boolean(PREF_NOTIFY_MESSAGE_TEXT)) {
+        notify_message(display_name, ui_index, message);
+    } else {
+        notify_message(display_name, ui_index, NULL);
     }
 
     free(display_name);
diff --git a/src/ui/console.c b/src/ui/console.c
index 6aa706c5..6081d56a 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -1329,6 +1329,11 @@ cons_notify_setting(void)
         else
             cons_show("Messages (/notify message)          : OFF");
 
+        if (prefs_get_boolean(PREF_NOTIFY_MESSAGE_TRIGGER))
+            cons_show("Messages trigger (/notify message)  : ON");
+        else
+            cons_show("Messages trigger (/notify message)  : OFF");
+
         if (prefs_get_boolean(PREF_NOTIFY_MESSAGE_CURRENT))
             cons_show("Messages current (/notify message)  : ON");
         else
@@ -1339,15 +1344,20 @@ cons_notify_setting(void)
         else
             cons_show("Messages text (/notify message)     : OFF");
 
-        char *room_setting = prefs_get_string(PREF_NOTIFY_ROOM);
-        if (g_strcmp0(room_setting, "on") == 0) {
-        cons_show    ("Room messages (/notify room)        : ON");
-        } else if (g_strcmp0(room_setting, "off") == 0) {
-        cons_show    ("Room messages (/notify room)        : OFF");
-        } else {
-        cons_show    ("Room messages (/notify room)        : %s", room_setting);
-        }
-        prefs_free_string(room_setting);
+        if (prefs_get_boolean(PREF_NOTIFY_ROOM))
+            cons_show("Room messages (/notify room)        : ON");
+        else
+            cons_show("Room messages (/notify room)        : OFF");
+
+        if (prefs_get_boolean(PREF_NOTIFY_ROOM_MENTION))
+            cons_show("Room mention (/notify room)         : ON");
+        else
+            cons_show("Room mention (/notify room)         : OFF");
+
+        if (prefs_get_boolean(PREF_NOTIFY_ROOM_TRIGGER))
+            cons_show("Room trigger (/notify room)         : ON");
+        else
+            cons_show("Room trigger (/notify room)         : OFF");
 
         if (prefs_get_boolean(PREF_NOTIFY_ROOM_CURRENT))
             cons_show("Room current (/notify room)         : ON");
diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c
index 58fef4d1..82b74e1b 100644
--- a/src/ui/mucwin.c
+++ b/src/ui/mucwin.c
@@ -374,6 +374,9 @@ mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const mes
         win_print(window, '-', 0, NULL, 0, THEME_TEXT_ME, nick, message);
     }
 
+    gboolean is_current = wins_is_current(window);
+    gboolean notify = prefs_get_notify_room(is_current, my_nick, message);
+
     // currently in groupchat window
     if (wins_is_current(window)) {
         status_bar_active(num);
@@ -388,11 +391,9 @@ mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const mes
         }
 
         mucwin->unread++;
-    }
-
-    int ui_index = num;
-    if (ui_index == 10) {
-        ui_index = 0;
+        if (notify) {
+            mucwin->notify = TRUE;
+        }
     }
 
     // don't notify self messages
@@ -404,34 +405,22 @@ mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const mes
         beep();
     }
 
-    gboolean notify = FALSE;
-    char *room_setting = prefs_get_string(PREF_NOTIFY_ROOM);
-    if (g_strcmp0(room_setting, "on") == 0) {
-        notify = TRUE;
+    if (!notify) {
+        return;
     }
-    if (g_strcmp0(room_setting, "mention") == 0) {
-        char *message_lower = g_utf8_strdown(message, -1);
-        char *nick_lower = g_utf8_strdown(nick, -1);
-        if (g_strrstr(message_lower, nick_lower)) {
-            notify = TRUE;
-        }
-        g_free(message_lower);
-        g_free(nick_lower);
+
+    Jid *jidp = jid_create(mucwin->roomjid);
+    int ui_index = num;
+    if (ui_index == 10) {
+        ui_index = 0;
     }
-    prefs_free_string(room_setting);
 
-    if (notify) {
-        gboolean is_current = wins_is_current(window);
-        if ( !is_current || (is_current && prefs_get_boolean(PREF_NOTIFY_ROOM_CURRENT)) ) {
-            Jid *jidp = jid_create(mucwin->roomjid);
-            if (prefs_get_boolean(PREF_NOTIFY_ROOM_TEXT)) {
-                notify_room_message(nick, jidp->localpart, ui_index, message);
-            } else {
-                notify_room_message(nick, jidp->localpart, ui_index, NULL);
-            }
-            jid_destroy(jidp);
-        }
+    if (prefs_get_boolean(PREF_NOTIFY_ROOM_TEXT)) {
+        notify_room_message(nick, jidp->localpart, ui_index, message);
+    } else {
+        notify_room_message(nick, jidp->localpart, ui_index, NULL);
     }
+    jid_destroy(jidp);
 }
 
 void
diff --git a/src/ui/notifier.c b/src/ui/notifier.c
index 9127b1e7..9a185e38 100644
--- a/src/ui/notifier.c
+++ b/src/ui/notifier.c
@@ -73,17 +73,16 @@ notifier_uninit(void)
 }
 
 void
-notify_typing(const char *const handle)
+notify_typing(const char *const name)
 {
-    char message[strlen(handle) + 1 + 11];
-    sprintf(message, "%s: typing...", handle);
+    char message[strlen(name) + 1 + 11];
+    sprintf(message, "%s: typing...", name);
 
     _notify(message, 10000, "Incoming message");
 }
 
 void
-notify_invite(const char *const from, const char *const room,
-    const char *const reason)
+notify_invite(const char *const from, const char *const room, const char *const reason)
 {
     GString *message = g_string_new("Room invite\nfrom: ");
     g_string_append(message, from);
@@ -99,32 +98,24 @@ notify_invite(const char *const from, const char *const room,
 }
 
 void
-notify_message(ProfWin *window, const char *const name, const char *const text)
+notify_message(const char *const name, int win, const char *const text)
 {
-    int num = wins_get_num(window);
-    if (num == 10) {
-        num = 0;
+    GString *message = g_string_new("");
+    g_string_append_printf(message, "%s (win %d)", name, win);
+    if (text) {
+        g_string_append_printf(message, "\n%s", text);
     }
 
-    gboolean is_current = wins_is_current(window);
-    if (!is_current || (is_current && prefs_get_boolean(PREF_NOTIFY_MESSAGE_CURRENT)) ) {
-        GString *message = g_string_new("");
-        g_string_append_printf(message, "%s (win %d)", name, num);
-
-        if (prefs_get_boolean(PREF_NOTIFY_MESSAGE_TEXT) && text) {
-            g_string_append_printf(message, "\n%s", text);
-        }
+    _notify(message->str, 10000, "incoming message");
 
-        _notify(message->str, 10000, "incoming message");
-        g_string_free(message, TRUE);
-    }
+    g_string_free(message, TRUE);
 }
 
 void
-notify_room_message(const char *const handle, const char *const room, int win, const char *const text)
+notify_room_message(const char *const nick, const char *const room, int win, const char *const text)
 {
     GString *message = g_string_new("");
-    g_string_append_printf(message, "%s in %s (win %d)", handle, room, win);
+    g_string_append_printf(message, "%s in %s (win %d)", nick, room, win);
     if (text) {
         g_string_append_printf(message, "\n%s", text);
     }
@@ -149,13 +140,14 @@ notify_remind(void)
     gdouble elapsed = g_timer_elapsed(remind_timer, NULL);
     gint remind_period = prefs_get_notify_remind();
     if (remind_period > 0 && elapsed >= remind_period) {
+        gboolean notify = wins_get_notify();
         gint unread = wins_get_total_unread();
         gint open = muc_invites_count();
         gint subs = presence_sub_request_count();
 
         GString *text = g_string_new("");
 
-        if (unread > 0) {
+        if (notify && unread > 0) {
             if (unread == 1) {
                 g_string_append(text, "1 unread message");
             } else {
@@ -184,7 +176,7 @@ notify_remind(void)
             }
         }
 
-        if ((unread > 0) || (open > 0) || (subs > 0)) {
+        if ((notify && unread > 0) || (open > 0) || (subs > 0)) {
             _notify(text->str, 5000, "Incoming message");
         }
 
diff --git a/src/ui/privwin.c b/src/ui/privwin.c
index 6031a2c0..67bad379 100644
--- a/src/ui/privwin.c
+++ b/src/ui/privwin.c
@@ -53,6 +53,9 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDat
 
     char *display_from = get_nick_from_full_jid(privatewin->fulljid);
 
+    gboolean is_current = wins_is_current(window);
+    gboolean notify = prefs_get_notify_chat(is_current, message);
+
     // currently viewing chat window with sender
     if (wins_is_current(window)) {
         win_print_incoming_message(window, timestamp, display_from, message, PROF_MSG_PLAIN);
@@ -62,6 +65,9 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDat
     // not currently viewing chat window with sender
     } else {
         privatewin->unread++;
+        if (notify) {
+            privatewin->notify = TRUE;
+        }
         status_bar_new(num);
         cons_show_incoming_message(display_from, num);
         win_print_incoming_message(window, timestamp, display_from, message, PROF_MSG_PLAIN);
@@ -75,8 +81,20 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDat
         beep();
     }
 
-    if (prefs_get_boolean(PREF_NOTIFY_MESSAGE)) {
-        notify_message(window, display_from, message);
+    if (!notify) {
+        free(display_from);
+        return;
+    }
+
+    int ui_index = num;
+    if (ui_index == 10) {
+        ui_index = 0;
+    }
+
+    if (prefs_get_boolean(PREF_NOTIFY_MESSAGE_TEXT)) {
+        notify_message(display_from, ui_index, message);
+    } else {
+        notify_message(display_from, ui_index, NULL);
     }
 
     free(display_from);
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 032a1161..458255f1 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -319,6 +319,7 @@ ProfWin* win_create_muc_config(const char *const title, DataForm *form);
 ProfWin* win_create_private(const char *const fulljid);
 void win_update_virtual(ProfWin *window);
 void win_free(ProfWin *window);
+gboolean win_notify(ProfWin *window);
 int win_unread(ProfWin *window);
 void win_resize(ProfWin *window);
 void win_hide_subwin(ProfWin *window);
@@ -339,9 +340,9 @@ void win_clear(ProfWin *window);
 // desktop notifications
 void notifier_initialise(void);
 void notifier_uninit(void);
-void notify_typing(const char *const handle);
-void notify_message(ProfWin *window, const char *const name, const char *const text);
-void notify_room_message(const char *const handle, const char *const room, int win, const char *const text);
+void notify_typing(const char *const name);
+void notify_message(const char *const name, int win, const char *const text);
+void notify_room_message(const char *const nick, const char *const room, int win, const char *const text);
 void notify_remind(void);
 void notify_invite(const char *const from, const char *const room, const char *const reason);
 void notify_subscription(const char *const from);
diff --git a/src/ui/win_types.h b/src/ui/win_types.h
index bace4537..94901957 100644
--- a/src/ui/win_types.h
+++ b/src/ui/win_types.h
@@ -102,6 +102,7 @@ typedef struct prof_chat_win_t {
     ProfWin window;
     char *barejid;
     int unread;
+    gboolean notify;
     ChatState *state;
     gboolean is_otr;
     gboolean otr_is_trusted;
@@ -116,6 +117,7 @@ typedef struct prof_muc_win_t {
     ProfWin window;
     char *roomjid;
     int unread;
+    gboolean notify;
     gboolean showjid;
     unsigned long memcheck;
 } ProfMucWin;
@@ -131,6 +133,7 @@ typedef struct prof_private_win_t {
     ProfWin window;
     char *fulljid;
     int unread;
+    gboolean notify;
     unsigned long memcheck;
 } ProfPrivateWin;
 
diff --git a/src/ui/window.c b/src/ui/window.c
index 9317f3b8..d6500a67 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -141,6 +141,7 @@ win_create_chat(const char *const barejid)
     new_win->pgp_send = FALSE;
     new_win->history_shown = FALSE;
     new_win->unread = 0;
+    new_win->notify = FALSE;
     new_win->state = chat_state_new();
 
     new_win->memcheck = PROFCHATWIN_MEMCHECK;
@@ -180,6 +181,7 @@ win_create_muc(const char *const roomjid)
 
     new_win->roomjid = strdup(roomjid);
     new_win->unread = 0;
+    new_win->notify = FALSE;
     if (prefs_get_boolean(PREF_OCCUPANTS_JID)) {
         new_win->showjid = TRUE;
     } else {
@@ -215,6 +217,7 @@ win_create_private(const char *const fulljid)
 
     new_win->fulljid = strdup(fulljid);
     new_win->unread = 0;
+    new_win->notify = FALSE;
 
     new_win->memcheck = PROFPRIVATEWIN_MEMCHECK;
 
@@ -1245,6 +1248,26 @@ win_has_active_subwin(ProfWin *window)
     }
 }
 
+gboolean
+win_notify(ProfWin *window)
+{
+    if (window->type == WIN_CHAT) {
+        ProfChatWin *chatwin = (ProfChatWin*) window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        return chatwin->notify;
+    } else if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*) window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+        return mucwin->notify;
+    } else if (window->type == WIN_PRIVATE) {
+        ProfPrivateWin *privatewin = (ProfPrivateWin*) window;
+        assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
+        return privatewin->notify;
+    } else {
+        return FALSE;
+    }
+}
+
 int
 win_unread(ProfWin *window)
 {
diff --git a/src/window_list.c b/src/window_list.c
index 46da0e75..14bf6ea5 100644
--- a/src/window_list.c
+++ b/src/window_list.c
@@ -189,13 +189,16 @@ wins_set_current_by_num(int i)
             ProfChatWin *chatwin = (ProfChatWin*) window;
             assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
             chatwin->unread = 0;
+            chatwin->notify = FALSE;
         } else if (window->type == WIN_MUC) {
             ProfMucWin *mucwin = (ProfMucWin*) window;
             assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
             mucwin->unread = 0;
+            mucwin->notify = FALSE;
         } else if (window->type == WIN_PRIVATE) {
             ProfPrivateWin *privatewin = (ProfPrivateWin*) window;
             privatewin->unread = 0;
+            privatewin->notify = FALSE;
         }
     }
 }
@@ -382,6 +385,23 @@ wins_new_private(const char *const fulljid)
     return newwin;
 }
 
+gboolean
+wins_get_notify(void)
+{
+    GList *values = g_hash_table_get_values(windows);
+    GList *curr = values;
+
+    while (curr) {
+        ProfWin *window = curr->data;
+        if (win_notify(window)) {
+            g_list_free(values);
+            return TRUE;
+        }
+        curr = g_list_next(curr);
+    }
+    return FALSE;
+}
+
 int
 wins_get_total_unread(void)
 {
diff --git a/src/window_list.h b/src/window_list.h
index 4b7dca8c..4cf4e5f8 100644
--- a/src/window_list.h
+++ b/src/window_list.h
@@ -67,6 +67,7 @@ int wins_get_current_num(void);
 void wins_close_current(void);
 void wins_close_by_num(int i);
 gboolean wins_is_current(ProfWin *window);
+gboolean wins_get_notify(void);
 int wins_get_total_unread(void);
 void wins_resize_all(void);
 GSList* wins_get_chat_recipients(void);
diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c
index 084b36be..37d49d4d 100644
--- a/tests/unittests/ui/stub_ui.c
+++ b/tests/unittests/ui/stub_ui.c
@@ -486,6 +486,10 @@ ProfWin* win_create_private(const char * const fulljid)
 
 void win_update_virtual(ProfWin *window) {}
 void win_free(ProfWin *window) {}
+gboolean win_notify(ProfWin *window)
+{
+    return TRUE;
+}
 int win_unread(ProfWin *window)
 {
     return 0;
@@ -514,7 +518,7 @@ void win_clear(ProfWin *window) {}
 void notifier_uninit(void) {}
 
 void notify_typing(const char * const handle) {}
-void notify_message(ProfWin *window, const char * const name, const char * const text) {}
+void notify_message(const char *const name, int win, const char *const text) {}
 void notify_room_message(const char * const handle, const char * const room,
     int win, const char * const text) {}
 void notify_remind(void) {}