[smuxi/experiments/configurable_message_patterns: 7/7] Engine: implemented user configurable message patterns



commit 85ca287e6be41ef754a0653bc83fa7eb24df69ea
Author: Mirco Bauer <meebey meebey net>
Date:   Sat Feb 15 14:32:01 2014 +0100

    Engine: implemented user configurable message patterns

 src/Engine/Config/Config.cs                 |   16 +++
 src/Engine/Config/MessageBuilderSettings.cs |  109 +++++++++---------
 src/Engine/Config/MessagePatternModel.cs    |  134 ++++++++++++++++++++++
 src/Engine/Config/MessagePatternSettings.cs |  160 +++++++++++++++++++++++++++
 src/Engine/Engine.csproj                    |    2 +
 src/Engine/Makefile.am                      |    2 +
 src/Engine/Messages/MessageBuilder.cs       |    6 +-
 src/Engine/Session.cs                       |   56 +++++++---
 8 files changed, 410 insertions(+), 75 deletions(-)
---
diff --git a/src/Engine/Config/Config.cs b/src/Engine/Config/Config.cs
index 4ab2a86..051c806 100644
--- a/src/Engine/Config/Config.cs
+++ b/src/Engine/Config/Config.cs
@@ -618,6 +618,22 @@ namespace Smuxi.Engine
                     LoadUserEntry(user, cprefix + "MessageType", null);
                     LoadUserEntry(user, cprefix + "MessagePattern", null);
                 }
+
+                string lprefix = "MessagePatterns/";
+                var linkKeys = GetList(prefix + user + "/" + lprefix + "MessagePatterns");
+                if (linkKeys == null) {
+                    linkKeys = new string[] {};
+                    m_Preferences[prefix + user + "/" + lprefix + "MessagePatterns"] = new string[] {};
+                } else {
+                    m_Preferences[prefix + user + "/" + lprefix + "MessagePatterns"] = linkKeys;
+                }
+                foreach (var linkKey in linkKeys) {
+                    lprefix = "MessagePatterns/" + linkKey + "/";
+                    LoadUserEntry(user, lprefix + "MessagePartPattern", String.Empty);
+                    LoadUserEntry(user, lprefix + "MessagePartType", String.Empty);
+                    LoadUserEntry(user, lprefix + "LinkFormat", String.Empty);
+                    LoadUserEntry(user, lprefix + "TextFormat", String.Empty);
+                }
             }
         }
 
diff --git a/src/Engine/Config/MessageBuilderSettings.cs b/src/Engine/Config/MessageBuilderSettings.cs
index cf1da9c..0a41d13 100644
--- a/src/Engine/Config/MessageBuilderSettings.cs
+++ b/src/Engine/Config/MessageBuilderSettings.cs
@@ -20,46 +20,39 @@
 using System;
 using System.Text.RegularExpressions;
 using System.Collections.Generic;
+using Smuxi.Common;
 
 namespace Smuxi.Engine
 {
     public class MessageBuilderSettings
     {
-        public class SmartLink
-        {
-            public Regex MessagePartPattern { get; set; }
-            public Type MessagePartType { get; set; }
-            // what is linked to
-            public string LinkFormat { get; set; }
-            // what is displayed
-            public string TextFormat { get; set; }
-
-            public SmartLink(Regex pattern)
-            {
-                if (pattern == null) {
-                    throw new ArgumentNullException("pattern");
-                }
-                MessagePartPattern = pattern;
-                MessagePartType = typeof(UrlMessagePartModel);
-            }
-        }
-
-        static List<SmartLink> BuiltinSmartLinks { get; set; }
-        public List<SmartLink> SmartLinks { get; private set; }
+        static List<MessagePatternModel> BuiltinPatterns { get; set; }
+        static List<MessagePatternModel> GlobalPatterns { get; set; }
+        public List<MessagePatternModel> Patterns { get; private set; }
 
         static MessageBuilderSettings()
         {
-            BuiltinSmartLinks = new List<SmartLink>();
+            BuiltinPatterns = new List<MessagePatternModel>();
             InitBuiltinSmartLinks();
+            GlobalPatterns = new List<MessagePatternModel>();
         }
 
         public MessageBuilderSettings()
         {
-            // No need to lock BuiltinSmartLinks as List<T> is thread-safe for
+            var builtinPatterns = BuiltinPatterns;
+            var globalPatterns = GlobalPatterns;
+
+            // No need to lock BuiltinPatterns as List<T> is thread-safe for
             // multiple readers as long as there is no writer at the same time.
             // BuiltinSmartLinks is only written once before the first instance
             // of MessageBuilderSettings is created via the static initializer.
-            SmartLinks = new List<SmartLink>(BuiltinSmartLinks);
+            Patterns = new List<MessagePatternModel>(builtinPatterns.Count +
+                                                     globalPatterns.Count);
+            Patterns.AddRange(builtinPatterns);
+
+            // GlobalPatterns is only set atomically in ApplyStaticConfig()
+            // and the collection is never modified after that
+            Patterns.AddRange(globalPatterns);
         }
 
         static void InitBuiltinSmartLinks()
@@ -86,7 +79,7 @@ namespace Smuxi.Engine
                 @"(<[1-9][0-9]* attachments?>) 
(http://www\.facebook\.com/messages/\?action=read&tid=[0-9a-f]+)",
                 RegexOptions.Compiled
             );
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "{2}",
                 TextFormat = "{1}",
             });
@@ -96,14 +89,14 @@ namespace Smuxi.Engine
                 protocol_user_domain_port_path,
                 RegexOptions.IgnoreCase | RegexOptions.Compiled
             );
-            BuiltinSmartLinks.Add(new SmartLink(regex));
+            BuiltinPatterns.Add(new MessagePatternModel(regex));
 
             // email
             regex = new Regex(
                 @"(?:mailto:)?(" + user_domain + ")",
                 RegexOptions.IgnoreCase | RegexOptions.Compiled
             );
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "mailto:{1}";
             });
 
@@ -113,31 +106,31 @@ namespace Smuxi.Engine
             string heuristic_domain = @"(?:(?:" + subdomain + ")+(?:" + common_tld + ")|localhost)";
             string heuristic_address = heuristic_domain + "(?:" + path + ")?";
             regex = new Regex(heuristic_address, RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://{0}";
             });
 
             // Smuxi bugtracker
             regex = new Regex(@"smuxi#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://www.smuxi.org/issues/show/{1}";
             });
 
             // RFCs
             regex = new Regex(@"RFC[ -]?([0-9]+) (?:s\.|ss\.|sec\.|sect\.|section) ?([1-9][0-9.]*)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://tools.ietf.org/html/rfc{1}#section-{2}";
             });
             regex = new Regex(@"RFC[ -]?([0-9]+) (?:p\.|pp\.|page) ?(" + short_number + ")",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://tools.ietf.org/html/rfc{1}#page-{2}";
             });
             regex = new Regex(@"RFC[ -]?([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://www.ietf.org/rfc/rfc{1}.txt";
             });
 
@@ -147,147 +140,147 @@ namespace Smuxi.Engine
             // boost bugtracker
             regex = new Regex(@"boost#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "https://svn.boost.org/trac/boost/ticket/{1}";
             });
 
             // Claws bugtracker
             regex = new Regex(@"claws#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://www.thewildbeast.co.uk/claws-mail/bugzilla/show_bug.cgi?id={1}";
             });
 
             // CVE list
             regex = new Regex(@"CVE-[0-9]{4}-[0-9]{4,}",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://cve.mitre.org/cgi-bin/cvename.cgi?name={0}";
             });
 
             // CPAN bugtracker
             regex = new Regex(@"RT#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://rt.cpan.org/Public/Bug/Display.html?id={1}";
             });
 
             // Debian bugtracker
             regex = new Regex(@"deb#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugs.debian.org/{1}";
             });
 
             // Debian Security Advisories (DSA)
             regex = new Regex(@"DSA-([0-9]{4})(-[0-9]{1,2})?",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://www.debian.org/security/dsa-{1}";
             });
 
             // openSUSE feature tracker
             regex = new Regex(@"fate#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://features.opensuse.org/{1}";
             });
 
             // freedesktop bugtracker
             regex = new Regex(@"fdo#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugs.freedesktop.org/{1}";
             });
 
             // GNU bugtracker
             regex = new Regex(@"gnu#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://debbugs.gnu.org/{1}";
             });
 
             // GCC bugtracker
             regex = new Regex(@"gcc#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://gcc.gnu.org/bugzilla/show_bug.cgi?id={1}";
             });
 
             // GNOME bugtracker
             regex = new Regex(@"bgo#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugzilla.gnome.org/{1}";
             });
 
             // KDE bugtracker
             regex = new Regex(@"kde#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugs.kde.org/{1}";
             });
 
             // kernel bugtracker
             regex = new Regex(@"bko#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugzilla.kernel.org/show_bug.cgi?id={1}";
             });
 
             // launchpad bugtracker
             regex = new Regex(@"LP#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://launchpad.net/bugs/{1}";
             });
 
             // Mozilla bugtracker
             regex = new Regex(@"bmo#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugzilla.mozilla.org/{1}";
             });
 
             // Novell bugtracker
             regex = new Regex(@"bnc#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugzilla.novell.com/{1}";
             });
 
             // Redhat bugtracker
             regex = new Regex(@"rh#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugzilla.redhat.com/{1}";
             });
 
             // Samba bugtracker
             regex = new Regex(@"bso#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugzilla.samba.org/show_bug.cgi?id={1}";
             });
 
             // sourceforge bugtracker
             regex = new Regex(@"sf#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://sf.net/support/tracker.php?aid={1}";
             });
 
             // Xfce bugtracker
             regex = new Regex(@"bxo#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugzilla.xfce.org/show_bug.cgi?id={1}";
             });
 
             // Xamarin bugtracker
             regex = new Regex(@"bxc#([0-9]+)",
                               RegexOptions.IgnoreCase | RegexOptions.Compiled);
-            BuiltinSmartLinks.Add(new SmartLink(regex) {
+            BuiltinPatterns.Add(new MessagePatternModel(regex) {
                 LinkFormat = "http://bugzilla.xamarin.com/show_bug.cgi?id={1}";
             });
 
@@ -304,8 +297,16 @@ namespace Smuxi.Engine
             // TODO: JID
         }
 
-        public void ApplyConfig(UserConfig userConfig)
+        public static void ApplyStaticConfig(UserConfig userConfig)
         {
+            Trace.Call(userConfig);
+
+            if (userConfig == null) {
+                throw new ArgumentNullException("userConfig");
+            }
+
+            var settings = new MessagePatternSettings(userConfig);
+            GlobalPatterns = settings.GetList();
         }
     }
 }
diff --git a/src/Engine/Config/MessagePatternModel.cs b/src/Engine/Config/MessagePatternModel.cs
new file mode 100644
index 0000000..a19cebb
--- /dev/null
+++ b/src/Engine/Config/MessagePatternModel.cs
@@ -0,0 +1,134 @@
+// Smuxi - Smart MUltipleXed Irc
+//
+// Copyright (c) 2014 Mirco Bauer <meebey meebey net>
+//
+// Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+using System;
+using System.Text.RegularExpressions;
+
+namespace Smuxi.Engine
+{
+    public class MessagePatternModel
+    {
+        public int? ID { get; set; }
+        public Regex MessagePartPattern { get; set; }
+        public Type MessagePartType { get; set; }
+        // what is linked to
+        public string LinkFormat { get; set; }
+        // what is displayed
+        public string TextFormat { get; set; }
+
+        protected string ConfigKeyPrefix {
+            get {
+                if (ID == null) {
+                    throw new ArgumentNullException("ID");
+                }
+                return "MessagePatterns/" + ID + "/";
+            }
+        }
+
+        public MessagePatternModel(Regex pattern)
+        {
+            if (pattern == null) {
+                throw new ArgumentNullException("pattern");
+            }
+            MessagePartPattern = pattern;
+            MessagePartType = typeof(UrlMessagePartModel);
+        }
+
+        public MessagePatternModel(int id)
+        {
+            ID = id;
+        }
+
+        public void Load(UserConfig config)
+        {
+            if (ID == null) {
+                throw new InvalidOperationException("ID must not be null.");
+            }
+
+            Load(config, ID.Value);
+        }
+
+        public virtual void Load(UserConfig config, int id)
+        {
+            if (config == null) {
+                throw new ArgumentNullException("config");
+            }
+
+            // don't use ConfigKeyPrefix, so exception guarantees can be kept
+            string prefix = "MessagePatterns/" + id + "/";
+            if (config[prefix + "MessagePartPattern"] == null) {
+                // SmartLink does not exist
+                throw new ArgumentException("MessagePattern ID not found in config", "id");
+            }
+
+            ID = id;
+            // now we have a valid ID, ConfigKeyPrefix works
+            var messagePartPattern = (string) config[ConfigKeyPrefix + "MessagePartPattern"];
+            if (messagePartPattern.StartsWith("/") && messagePartPattern.EndsWith("/i")) {
+                var regexPattern = messagePartPattern.Substring(1, messagePartPattern.Length - 3);
+                MessagePartPattern = new Regex(regexPattern, RegexOptions.IgnoreCase);
+            } else {
+                MessagePartPattern = new Regex(messagePartPattern);
+            }
+            var messagePartType = (string) config[ConfigKeyPrefix + "MessagePartType"];
+            switch (messagePartType.ToLower()) {
+                case "url":
+                    MessagePartType = typeof(UrlMessagePartModel);
+                    break;
+                case "image":
+                    MessagePartType = typeof(ImageMessagePartModel);
+                    break;
+            }
+            LinkFormat = (string) config[ConfigKeyPrefix + "LinkFormat"];
+            TextFormat = (string) config[ConfigKeyPrefix + "TextFormat"];
+        }
+
+        public virtual void Save(UserConfig config)
+        {
+            if (config == null) {
+                throw new ArgumentNullException("config");
+            }
+
+            if (MessagePartPattern == null) {
+                config[ConfigKeyPrefix + "MessagePartPattern"] = String.Empty;
+            } else {
+                config[ConfigKeyPrefix + "MessagePartPattern"] = MessagePartPattern.ToString();
+            }
+            if (MessagePartType == typeof(ImageMessagePartModel)) {
+                config[ConfigKeyPrefix + "MessagePartType"] = "Image";
+            } else if (MessagePartType == typeof(UrlMessagePartModel)) {
+                config[ConfigKeyPrefix + "MessagePartType"] = "Url";
+            } else {
+                config[ConfigKeyPrefix + "MessagePartType"] = String.Empty;
+            }
+            config[ConfigKeyPrefix + "LinkFormat"] = LinkFormat ?? String.Empty;
+            config[ConfigKeyPrefix + "TextFormat"] = TextFormat ?? String.Empty;
+        }
+
+        public override string ToString()
+        {
+            return String.Format("<{0}>", ToTraceString());
+        }
+
+        public string ToTraceString()
+        {
+            return String.Format("{0}", ID);
+        }
+    }
+}
diff --git a/src/Engine/Config/MessagePatternSettings.cs b/src/Engine/Config/MessagePatternSettings.cs
new file mode 100644
index 0000000..fc55e03
--- /dev/null
+++ b/src/Engine/Config/MessagePatternSettings.cs
@@ -0,0 +1,160 @@
+/*
+ * Smuxi - Smart MUltipleXed Irc
+ *
+ * Copyright (c) 2014 Mirco Bauer <meebey meebey net>
+ *
+ * Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+using System;
+using System.Collections.Generic;
+using Smuxi.Common;
+using Smuxi.Engine;
+
+namespace Smuxi.Engine
+{
+    public class MessagePatternSettings
+    {
+        UserConfig UserConfig { get; set; }
+
+        protected string[] PatternIDs {
+            get {
+                return (string[]) UserConfig["MessagePatterns/MessagePatterns"];
+            }
+            set {
+                UserConfig["MessagePatterns/MessagePatterns"] = value;
+            }
+        }
+
+        public MessagePatternSettings(UserConfig userConfig)
+        {
+            if (userConfig == null) {
+                throw new ArgumentNullException("userConfig");
+            }
+
+            UserConfig = userConfig;
+        }
+
+        public List<MessagePatternModel> GetList()
+        {
+            var keys = PatternIDs;
+            var list = new List<MessagePatternModel>(keys.Length);
+            if (keys == null) {
+                return list;
+            }
+            foreach (var key in keys) {
+                int parsedKey = Int32.Parse(key);
+                var link = Get(parsedKey);
+                if (link == null) {
+                    continue;
+                }
+                list.Add(link);
+            }
+            return list;
+        }
+
+        public MessagePatternModel Get(int id)
+        {
+            Trace.Call(id);
+
+            string prefix = "MessagePatterns/" + id + "/";
+            if (UserConfig[prefix + "MessagePartPattern"] == null) {
+                // link does not exist
+                return null;
+            }
+            var link = new MessagePatternModel(id);
+            link.Load(UserConfig);
+            return link;
+        }
+
+        public int Add(MessagePatternModel link)
+        {
+            return Add(link, -1);
+        }
+
+        public int Add(MessagePatternModel link, int id)
+        {
+            Trace.Call(link, id);
+
+            if (link == null) {
+                throw new ArgumentNullException("link");
+            }
+
+            string[] keys = PatternIDs;
+            if (keys == null) {
+                keys = new string[] {};
+            }
+            int highestKey = 0;
+            int newKey = id;
+            if (id == -1) {
+                foreach (string key in keys) {
+                    int parsedKey = Int32.Parse(key);
+                    if (parsedKey > highestKey) {
+                        highestKey = parsedKey;
+                    }
+                }
+                newKey = ++highestKey;
+            }
+
+            link.ID = newKey;
+            link.Save(UserConfig);
+
+            var keyList = new List<string>(keys);
+            keyList.Add(link.ID.ToString());
+            PatternIDs = keyList.ToArray();
+            return newKey;
+        }
+
+        public void Set(MessagePatternModel link)
+        {
+            Trace.Call(link);
+
+            if (link == null) {
+                throw new ArgumentNullException("link");
+            }
+
+            link.Save(UserConfig);
+        }
+
+        public void Remove(int key)
+        {
+            Trace.Call(key);
+
+            string section = "MessagePatterns/" + key + "/";
+            string[] keys = PatternIDs;
+            if (keys == null) {
+                keys = new string[] {};
+            }
+            var keyList = new List<string>(keys);
+            int idx = keyList.IndexOf(key.ToString());
+            if (idx == -1) {
+                // key not found
+                return;
+            }
+            keyList.RemoveAt(idx);
+            UserConfig.Remove(section);
+            PatternIDs = keyList.ToArray();
+        }
+
+        public void Save()
+        {
+            Trace.Call();
+
+            UserConfig.Save();
+        }
+    }
+}
diff --git a/src/Engine/Engine.csproj b/src/Engine/Engine.csproj
index 8bd8d98..37a6091 100644
--- a/src/Engine/Engine.csproj
+++ b/src/Engine/Engine.csproj
@@ -107,6 +107,8 @@
     <Compile Include="Hooks\Environments\CommandHookEnvironment.cs" />
     <Compile Include="Config\MessageBuilderSettings.cs" />
     <Compile Include="Hooks\Environments\PersonHookEnvironment.cs" />
+    <Compile Include="Config\MessagePatternModel.cs" />
+    <Compile Include="Config\MessagePatternSettings.cs" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="Protocols\" />
diff --git a/src/Engine/Makefile.am b/src/Engine/Makefile.am
index 1403715..99b0152 100644
--- a/src/Engine/Makefile.am
+++ b/src/Engine/Makefile.am
@@ -94,6 +94,8 @@ FILES = \
        Config/ProxyType.cs \
        Config/EntrySettings.cs \
        Config/MessageBuilderSettings.cs \
+       Config/MessagePatternModel.cs \
+       Config/MessagePatternSettings.cs \
        Protocols/ProtocolManagerBase.cs \
        Protocols/ProtocolManagerFactory.cs \
        Protocols/ProtocolManagerInfoModel.cs \
diff --git a/src/Engine/Messages/MessageBuilder.cs b/src/Engine/Messages/MessageBuilder.cs
index 49bce15..3334b1d 100644
--- a/src/Engine/Messages/MessageBuilder.cs
+++ b/src/Engine/Messages/MessageBuilder.cs
@@ -857,7 +857,7 @@ namespace Smuxi.Engine
         }
 
         public static IList<MessagePartModel> ParseSmartLinks(TextMessagePartModel textPart,
-                                                              List<MessageBuilderSettings.SmartLink> links)
+                                                              List<MessagePatternModel> links)
         {
             var msgParts = new List<MessagePartModel>();
             if (links.Count == 0) {
@@ -865,7 +865,7 @@ namespace Smuxi.Engine
                 msgParts.Add(textPart);
                 return msgParts;
             }
-            var subLinks = new List<MessageBuilderSettings.SmartLink>(links);
+            var subLinks = new List<MessagePatternModel>(links);
             var link = subLinks.First();
             subLinks.Remove(link);
             
@@ -941,7 +941,7 @@ namespace Smuxi.Engine
         
         public IEnumerable<MessagePartModel> ParseSmartLinks(TextMessagePartModel part)
         {
-            return ParseSmartLinks(part, Settings.SmartLinks);
+            return ParseSmartLinks(part, Settings.Patterns);
         }
     }
 }
diff --git a/src/Engine/Session.cs b/src/Engine/Session.cs
index 7bebecd..80bcf13 100644
--- a/src/Engine/Session.cs
+++ b/src/Engine/Session.cs
@@ -154,6 +154,7 @@ namespace Smuxi.Engine
             _UserConfig.Changed += OnUserConfigChanged;
             _FilterListController = new FilterListController(_UserConfig);
             _Filters = _FilterListController.GetFilterList().Values;
+            MessageBuilderSettings.ApplyStaticConfig(_UserConfig);
             _Chats = new List<ChatModel>();
 
             InitSessionChat();
@@ -730,31 +731,50 @@ namespace Smuxi.Engine
                         return;
                     }
                     string setKey = setParam.Split('=')[0];
-                    string setValue = setParam.Split('=')[1];
+                    string setValue = String.Join(
+                        "=", setParam.Split('=').Skip(1).ToArray()
+                    );
                     object oldValue = _UserConfig[setKey];
+                    if (oldValue == null && setKey.StartsWith("MessagePatterns/")) {
+                        var id = setKey.Split('/')[1];
+                        var parsedId = Int32.Parse(id);
+                        var msgPatternSettings = new MessagePatternSettings(_UserConfig);
+                        var pattern = msgPatternSettings.Get(parsedId);
+                        if (pattern == null) {
+                            // pattern does not exist, create it with default values
+                            pattern = new MessagePatternModel(parsedId);
+                            msgPatternSettings.Add(pattern, parsedId);
+                            oldValue = _UserConfig[setKey];
+                        }
+                    }
                     if (oldValue == null) {
                         builder.AppendErrorText(
                             _("Invalid config key: '{0}'"),
                             setKey
                         );
-                    } else {
-                        try {
-                            object newValue = Convert.ChangeType(setValue, oldValue.GetType());
-                            _UserConfig[setKey] = newValue;
-                            builder.AppendText("{0} = {1}", setKey, newValue.ToString());
-                        } catch (InvalidCastException) {
-                            builder.AppendErrorText(
-                                _("Could not convert config value: '{0}' to type: {1}"),
-                                setValue,
-                                oldValue.GetType().Name
-                            );
-                        } catch (FormatException) {
-                            builder.AppendErrorText(
-                                _("Could not convert config value: '{0}' to type: {1}"),
-                                setValue,
-                                oldValue.GetType().Name
-                            );
+                        AddMessageToFrontend(cd, builder.ToMessage());
+                        return;
+                    }
+
+                    try {
+                        object newValue = Convert.ChangeType(setValue, oldValue.GetType());
+                        _UserConfig[setKey] = newValue;
+                        builder.AppendText("{0} = {1}", setKey, newValue.ToString());
+                        if (setKey.StartsWith("MessagePatterns/")) {
+                            MessageBuilderSettings.ApplyStaticConfig(UserConfig);
                         }
+                    } catch (InvalidCastException) {
+                        builder.AppendErrorText(
+                            _("Could not convert config value: '{0}' to type: {1}"),
+                            setValue,
+                            oldValue.GetType().Name
+                        );
+                    } catch (FormatException) {
+                        builder.AppendErrorText(
+                            _("Could not convert config value: '{0}' to type: {1}"),
+                            setValue,
+                            oldValue.GetType().Name
+                        );
                     }
                     break;
                 default:


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]