about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command/command.c64
-rw-r--r--src/command/commands.c12
-rw-r--r--src/common.c67
-rw-r--r--src/common.h3
-rw-r--r--src/config/preferences.c12
-rw-r--r--src/config/preferences.h4
-rw-r--r--src/event/server_events.c16
-rw-r--r--src/ui/console.c10
-rw-r--r--src/ui/mucwin.c49
-rw-r--r--src/ui/ui.h2
10 files changed, 129 insertions, 110 deletions
diff --git a/src/command/command.c b/src/command/command.c
index 5aa86a90..bf323a02 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -1179,6 +1179,8 @@ static struct cmd_t command_defs[] =
             "/notify chat text on|off",
             "/notify room on|off",
             "/notify room mention on|off",
+            "/notify room mention case_sensitive|case_insensitive",
+            "/notify room mention word_whole|word_part",
             "/notify room current on|off",
             "/notify room text on|off",
             "/notify room trigger add <text>",
@@ -1197,26 +1199,30 @@ static struct cmd_t command_defs[] =
         CMD_DESC(
             "Settings for various kinds of desktop notifications.")
         CMD_ARGS(
-            { "chat on|off",                "Notifications for regular chat messages." },
-            { "chat current on|off",        "Whether to show regular chat message notifications when the window is focussed." },
-            { "chat text on|off",           "Show message text in regular message notifications." },
-            { "room on|off",                "Notifications for all chat room messages, 'mention' only notifies when your nick is mentioned." },
-            { "room mention on|off",        "Notifications for all chat room messages when your nick is mentioned." },
-            { "room current on|off",        "Whether to show all chat room messages notifications when the window is focussed." },
-            { "room text on|off",           "Show message text in chat room message notifications." },
-            { "room trigger add <text>",    "Notify when specified text included in all chat room messages." },
-            { "room trigger remove <text>", "Remove chat room notification trigger." },
-            { "room trigger list",          "List all chat room triggers." },
-            { "room trigger on|off",        "Enable or disable all chat room notification triggers." },
-            { "on|off",                     "Override the global message setting for the current chat room." },
-            { "mention on|off",             "Override the global 'mention' setting for the current chat room." },
-            { "trigger on|off",             "Override the global 'trigger' setting for the current chat room." },
-            { "reset",                      "Reset to global notification settings for the current chat room." },
-            { "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." })
+            { "chat on|off",                    "Notifications for regular chat messages." },
+            { "chat current on|off",            "Whether to show regular chat message notifications when the window is focussed." },
+            { "chat text on|off",               "Show message text in regular message notifications." },
+            { "room on|off",                    "Notifications for all chat room messages, 'mention' only notifies when your nick is mentioned." },
+            { "room mention on|off",            "Notifications for all chat room messages when your nick is mentioned." },
+            { "room mention case_sensitive",    "Set room mention notifications as case sensitive." },
+            { "room mention case_insensitive",  "Set room mention notifications as case insensitive." },
+            { "room mention word_whole",        "Set room mention notifications only on whole word match, i.e. when nickname is not part of a larger word." },
+            { "room mention word_part",         "Set room mention notifications on partial word match, i.e. nickname may be part of a larger word." },
+            { "room current on|off",            "Whether to show all chat room messages notifications when the window is focussed." },
+            { "room text on|off",               "Show message text in chat room message notifications." },
+            { "room trigger add <text>",        "Notify when specified text included in all chat room messages." },
+            { "room trigger remove <text>",     "Remove chat room notification trigger." },
+            { "room trigger list",              "List all chat room triggers." },
+            { "room trigger on|off",            "Enable or disable all chat room notification triggers." },
+            { "on|off",                         "Override the global message setting for the current chat room." },
+            { "mention on|off",                 "Override the global 'mention' setting for the current chat room." },
+            { "trigger on|off",                 "Override the global 'trigger' setting for the current chat room." },
+            { "reset",                          "Reset to global notification settings for the current chat room." },
+            { "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 chat on",
             "/notify chat text on",
@@ -1943,6 +1949,7 @@ static Autocomplete notify_ac;
 static Autocomplete notify_chat_ac;
 static Autocomplete notify_room_ac;
 static Autocomplete notify_typing_ac;
+static Autocomplete notify_mention_ac;
 static Autocomplete notify_trigger_ac;
 static Autocomplete prefs_ac;
 static Autocomplete sub_ac;
@@ -2111,6 +2118,14 @@ cmd_init(void)
     autocomplete_add(notify_typing_ac, "off");
     autocomplete_add(notify_typing_ac, "current");
 
+    notify_mention_ac = autocomplete_new();
+    autocomplete_add(notify_mention_ac, "on");
+    autocomplete_add(notify_mention_ac, "off");
+    autocomplete_add(notify_mention_ac, "case_sensitive");
+    autocomplete_add(notify_mention_ac, "case_insensitive");
+    autocomplete_add(notify_mention_ac, "word_whole");
+    autocomplete_add(notify_mention_ac, "word_part");
+
     notify_trigger_ac = autocomplete_new();
     autocomplete_add(notify_trigger_ac, "add");
     autocomplete_add(notify_trigger_ac, "remove");
@@ -2571,6 +2586,7 @@ cmd_uninit(void)
     autocomplete_free(notify_chat_ac);
     autocomplete_free(notify_room_ac);
     autocomplete_free(notify_typing_ac);
+    autocomplete_free(notify_mention_ac);
     autocomplete_free(notify_trigger_ac);
     autocomplete_free(sub_ac);
     autocomplete_free(titlebar_ac);
@@ -2788,6 +2804,7 @@ cmd_reset_autocomplete(ProfWin *window)
     autocomplete_reset(notify_chat_ac);
     autocomplete_reset(notify_room_ac);
     autocomplete_reset(notify_typing_ac);
+    autocomplete_reset(notify_mention_ac);
     autocomplete_reset(notify_trigger_ac);
     autocomplete_reset(sub_ac);
 
@@ -3491,7 +3508,7 @@ _notify_autocomplete(ProfWin *window, const char *const input)
     }
 
     gchar *boolean_choices1[] = { "/notify room current", "/notify chat current", "/notify typing current",
-        "/notify room text", "/notify room mention", "/notify chat text" };
+        "/notify room text", "/notify chat text" };
     for (i = 0; i < ARRAY_SIZE(boolean_choices1); i++) {
         result = autocomplete_param_with_func(input, boolean_choices1[i], prefs_autocomplete_boolean_choice);
         if (result) {
@@ -3499,6 +3516,11 @@ _notify_autocomplete(ProfWin *window, const char *const input)
         }
     }
 
+    result = autocomplete_param_with_ac(input, "/notify room mention", notify_mention_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
     result = autocomplete_param_with_ac(input, "/notify room trigger", notify_trigger_ac, TRUE);
     if (result) {
         return result;
diff --git a/src/command/commands.c b/src/command/commands.c
index da6fdec8..538a62ae 100644
--- a/src/command/commands.c
+++ b/src/command/commands.c
@@ -4855,6 +4855,18 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
             } else if (g_strcmp0(args[2], "off") == 0) {
                 cons_show("Room notifications with mention disabled.");
                 prefs_set_boolean(PREF_NOTIFY_ROOM_MENTION, FALSE);
+            } else if (g_strcmp0(args[2], "case_sensitive") == 0) {
+                cons_show("Room mention matching set to case sensitive.");
+                prefs_set_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE, TRUE);
+            } else if (g_strcmp0(args[2], "case_insensitive") == 0) {
+                cons_show("Room mention matching set to case insensitive.");
+                prefs_set_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE, FALSE);
+            } else if (g_strcmp0(args[2], "word_whole") == 0) {
+                cons_show("Room mention matching set to whole word.");
+                prefs_set_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD, TRUE);
+            } else if (g_strcmp0(args[2], "word_part") == 0) {
+                cons_show("Room mention matching set to partial word.");
+                prefs_set_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD, FALSE);
             } else {
                 cons_show("Usage: /notify room mention on|off");
             }
diff --git a/src/common.c b/src/common.c
index c6ead0ba..a8282fe7 100644
--- a/src/common.c
+++ b/src/common.c
@@ -658,62 +658,31 @@ is_notify_enabled(void)
     return notify_enabled;
 }
 
-gboolean
-prof_strstr(const char *const needle, const char *const haystack, gboolean case_sensitive, gboolean whole_word)
+GSList*
+prof_occurrences(const char *const needle, const char *const haystack, int offset, gboolean whole_word, GSList **result)
 {
     if (needle == NULL || haystack == NULL) {
-        return FALSE;
+        return *result;
     }
 
-    char *needle_str = case_sensitive ? strdup(needle) : g_utf8_strdown(needle, -1);
-    char *haystack_str = case_sensitive ? strdup(haystack) : g_utf8_strdown(haystack, -1);
-
-    if (whole_word) {
-        if (g_strcmp0(needle_str, haystack_str) == 0) {
-            free(needle_str);
-            free(haystack_str);
-            return TRUE;
-        }
-
-        char *pos = g_strrstr(haystack_str, needle_str);
-        if (pos == NULL) {
-            free(needle_str);
-            free(haystack_str);
-            return FALSE;
-        }
-
-        gboolean at_start = g_str_has_prefix(haystack_str, needle_str);
-        gboolean at_end = g_str_has_suffix(haystack_str, needle_str);
-
-        if (at_start) {
-            char *next = g_utf8_next_char(pos + strlen(needle_str) - 1);
-            gunichar nextu = g_utf8_get_char(next);
-            gboolean result = g_unichar_isalnum(nextu) ? FALSE : TRUE;
-            free(needle_str);
-            free(haystack_str);
-            return result;
-        } else if (at_end) {
-            char *prev = g_utf8_prev_char(pos);
-            gunichar prevu = g_utf8_get_char(prev);
-            gboolean result = g_unichar_isalnum(prevu) ? FALSE : TRUE;
-            free(needle_str);
-            free(haystack_str);
-            return result;
-        } else {
-            char *prev = g_utf8_prev_char(pos);
-            char *next = g_utf8_next_char(pos + strlen(needle_str) - 1);
+    if (g_str_has_prefix(&haystack[offset], needle)) {
+        if (whole_word) {
+            char *prev = g_utf8_prev_char(&haystack[offset]);
+            char *next = g_utf8_next_char(&haystack[offset] + strlen(needle) - 1);
             gunichar prevu = g_utf8_get_char(prev);
             gunichar nextu = g_utf8_get_char(next);
-            gboolean result = g_unichar_isalnum(prevu) || g_unichar_isalnum(nextu) ? FALSE : TRUE;
-            free(needle_str);
-            free(haystack_str);
-            return result;
+            if (!g_unichar_isalnum(prevu) && !g_unichar_isalnum(nextu)) {
+                *result = g_slist_append(*result, GINT_TO_POINTER(offset));
+            }
+        } else {
+            *result = g_slist_append(*result, GINT_TO_POINTER(offset));
         }
-    } else {
-        gboolean result = g_strrstr(haystack_str, needle_str) != NULL ? TRUE : FALSE;
-        free(needle_str);
-        free(haystack_str);
-        return result;
     }
+
+    if (haystack[offset+1] != '\0') {
+        *result = prof_occurrences(needle, haystack, offset+1, whole_word, result);
+    }
+
+    return *result;
 }
 
diff --git a/src/common.h b/src/common.h
index 284a096e..c67b1460 100644
--- a/src/common.h
+++ b/src/common.h
@@ -128,6 +128,7 @@ char* get_file_or_linked(char *loc, char *basedir);
 char* strip_arg_quotes(const char *const input);
 gboolean is_notify_enabled(void);
 
-gboolean prof_strstr(const char *const needle, const char *const haystack, gboolean case_sensitive, gboolean whole_word);
+GSList* prof_occurrences(const char *const needle, const char *const haystack, int offset, gboolean whole_word,
+    GSList **result);
 
 #endif
diff --git a/src/config/preferences.c b/src/config/preferences.c
index ce40a321..4957a600 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -1230,8 +1230,8 @@ _get_group(preference_t pref)
         case PREF_NOTIFY_ROOM_TEXT:
         case PREF_NOTIFY_INVITE:
         case PREF_NOTIFY_SUB:
-        case PREF_MENTION_CASE_SENSITIVE:
-        case PREF_MENTION_WHOLE_WORD:
+        case PREF_NOTIFY_MENTION_CASE_SENSITIVE:
+        case PREF_NOTIFY_MENTION_WHOLE_WORD:
             return PREF_GROUP_NOTIFICATIONS;
         case PREF_CHLOG:
         case PREF_GRLOG:
@@ -1334,9 +1334,9 @@ _get_key(preference_t pref)
             return "invite";
         case PREF_NOTIFY_SUB:
             return "sub";
-        case PREF_MENTION_CASE_SENSITIVE:
+        case PREF_NOTIFY_MENTION_CASE_SENSITIVE:
             return "room.mention.casesensitive";
-        case PREF_MENTION_WHOLE_WORD:
+        case PREF_NOTIFY_MENTION_WHOLE_WORD:
             return "room.mention.wholeword";
         case PREF_CHLOG:
             return "chlog";
@@ -1494,8 +1494,8 @@ _get_default_boolean(preference_t pref)
         case PREF_ROSTER_ROOMS:
         case PREF_TLS_SHOW:
         case PREF_LASTACTIVITY:
-        case PREF_MENTION_CASE_SENSITIVE:
-        case PREF_MENTION_WHOLE_WORD:
+        case PREF_NOTIFY_MENTION_CASE_SENSITIVE:
+        case PREF_NOTIFY_MENTION_WHOLE_WORD:
             return TRUE;
         default:
             return FALSE;
diff --git a/src/config/preferences.h b/src/config/preferences.h
index 7165079b..8f18149c 100644
--- a/src/config/preferences.h
+++ b/src/config/preferences.h
@@ -113,8 +113,8 @@ typedef enum {
     PREF_NOTIFY_ROOM_TEXT,
     PREF_NOTIFY_INVITE,
     PREF_NOTIFY_SUB,
-    PREF_MENTION_CASE_SENSITIVE,
-    PREF_MENTION_WHOLE_WORD,
+    PREF_NOTIFY_MENTION_CASE_SENSITIVE,
+    PREF_NOTIFY_MENTION_WHOLE_WORD,
     PREF_CHLOG,
     PREF_GRLOG,
     PREF_AUTOAWAY_CHECK,
diff --git a/src/event/server_events.c b/src/event/server_events.c
index 11252dab..76c649cd 100644
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -251,12 +251,20 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha
     char *new_message = plugins_pre_room_message_display(room_jid, nick, message);
     char *mynick = muc_nick(mucwin->roomjid);
 
-    gboolean case_sensitive = prefs_get_boolean(PREF_MENTION_CASE_SENSITIVE);
-    gboolean whole_word = prefs_get_boolean(PREF_MENTION_WHOLE_WORD);
-    gboolean mention = prof_strstr(mynick, new_message, case_sensitive, whole_word);
+    gboolean whole_word = prefs_get_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD);
+    gboolean case_sensitive = prefs_get_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE);
+    char *message_search = case_sensitive ? strdup(new_message) : g_utf8_strdown(new_message, -1);
+    char *mynick_search = case_sensitive ? strdup(mynick) : g_utf8_strdown(mynick, -1);
+
+    GSList *mentions = NULL;
+    mentions = prof_occurrences(mynick_search, message_search, 0, whole_word, &mentions);
+    gboolean mention = g_slist_length(mentions) > 0;
+    g_free(message_search);
+    g_free(mynick_search);
+
     GList *triggers = prefs_message_get_triggers(new_message);
 
-    mucwin_message(mucwin, nick, new_message, mention, triggers);
+    mucwin_message(mucwin, nick, new_message, mentions, triggers);
 
     ProfWin *window = (ProfWin*)mucwin;
     int num = wins_get_num(window);
diff --git a/src/ui/console.c b/src/ui/console.c
index 69984333..c104e21b 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -1550,6 +1550,16 @@ cons_notify_setting(void)
     else
         cons_show("Room mention (/notify room)         : OFF");
 
+    if (prefs_get_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE))
+        cons_show("Room mention case (/notify room)    : Case sensitive");
+    else
+        cons_show("Room mention case (/notify room)    : Case insensitive");
+
+    if (prefs_get_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD))
+        cons_show("Room mention word (/notify room)    : Whole word only");
+    else
+        cons_show("Room mention word (/notify room)    : Part of word");
+
     if (prefs_get_boolean(PREF_NOTIFY_ROOM_TRIGGER))
         cons_show("Room trigger (/notify room)         : ON");
     else
diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c
index 4d323ef1..0b3c65f7 100644
--- a/src/ui/mucwin.c
+++ b/src/ui/mucwin.c
@@ -359,33 +359,30 @@ mucwin_history(ProfMucWin *mucwin, const char *const nick, GDateTime *timestamp,
 }
 
 static void
-_mucwin_print_mention(ProfWin *window, const char *const message, const char *const my_nick)
+_mucwin_print_mention(ProfWin *window, const char *const message, const char *const nick, GSList *mentions)
 {
-    char *mynick_lower = g_utf8_strdown(my_nick, -1);
-    char *message_lower = g_utf8_strdown(message, -1);
-    char message_section[strlen(message) + 1];
+    int last_pos = 0;
+    int pos = 0;
+    GSList *curr = mentions;
+    while (curr) {
+        pos = GPOINTER_TO_INT(curr->data);
 
-    int i = 0;
-    while(!g_str_has_prefix(&message_lower[i], mynick_lower) && i < strlen(message)) {
-        message_section[i] = message[i];
-        i++;
-    }
-    message_section[i] = '\0';
+        char *before_str = g_strndup(message + last_pos, pos - last_pos);
+        win_print(window, '-', 0, NULL, NO_DATE | NO_ME | NO_EOL, THEME_ROOMMENTION, "", before_str);
+        g_free(before_str);
+        char *nick_str = g_strndup(message + pos, strlen(nick));
+        win_print(window, '-', 0, NULL, NO_DATE | NO_ME | NO_EOL, THEME_ROOMMENTION_TERM, "", nick_str);
+        g_free(nick_str);
 
-    char *mention_section = strndup(&message[i], strlen(my_nick));
-    int used = strlen(message_section) + strlen(mention_section);
+        last_pos = pos + strlen(nick);
 
-    win_print(window, '-', 0, NULL, NO_DATE | NO_ME | NO_EOL, THEME_ROOMMENTION, "", message_section);
-    if (strlen(message) > used) {
-        win_print(window, '-', 0, NULL, NO_DATE | NO_ME | NO_EOL, THEME_ROOMMENTION_TERM, "", mention_section);
-        _mucwin_print_mention(window, &message[used], my_nick);
+        curr = g_slist_next(curr);
+    }
+    if (last_pos < strlen(message)) {
+        win_print(window, '-', 0, NULL, NO_DATE | NO_ME, THEME_ROOMMENTION, "", &message[last_pos]);
     } else {
-        win_print(window, '-', 0, NULL, NO_DATE | NO_ME, THEME_ROOMMENTION_TERM, "", mention_section);
+        win_print(window, '-', 0, NULL, NO_DATE | NO_ME, THEME_ROOMMENTION, "", "");
     }
-
-    free(mention_section);
-    g_free(mynick_lower);
-    g_free(message_lower);
 }
 
 gint
@@ -472,17 +469,17 @@ _mucwin_print_triggers(ProfWin *window, const char *const message, GList *trigge
 }
 
 void
-mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, gboolean mention, GList *triggers)
+mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers)
 {
     assert(mucwin != NULL);
 
     ProfWin *window = (ProfWin*)mucwin;
-    char *my_nick = muc_nick(mucwin->roomjid);
+    char *mynick = muc_nick(mucwin->roomjid);
 
-    if (g_strcmp0(nick, my_nick) != 0) {
-        if (mention) {
+    if (g_strcmp0(nick, mynick) != 0) {
+        if (g_slist_length(mentions) > 0) {
             win_print(window, '-', 0, NULL, NO_ME | NO_EOL, THEME_ROOMMENTION, nick, "");
-            _mucwin_print_mention(window, message, my_nick);
+            _mucwin_print_mention(window, message, mynick, mentions);
         } else if (triggers) {
             win_print(window, '-', 0, NULL, NO_ME | NO_EOL, THEME_ROOMTRIGGER, nick, "");
             _mucwin_print_triggers(window, message, triggers);
diff --git a/src/ui/ui.h b/src/ui/ui.h
index ab42f4d1..924ba33c 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -157,7 +157,7 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char
     const char *const role, const char *const affiliation, const char *const actor, const char *const reason);
 void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char *const presence);
 void mucwin_history(ProfMucWin *mucwin, const char *const nick, GDateTime *timestamp, const char *const message);
-void mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, gboolean mention, GList *triggers);
+void mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers);
 void mucwin_subject(ProfMucWin *mucwin, const char *const nick, const char *const subject);
 void mucwin_requires_config(ProfMucWin *mucwin);
 void mucwin_info(ProfMucWin *mucwin);