OSDN Git Service

-Add RLV @FindFolder command support
[radegast/radegast.git] / Radegast / Core / ChatTextManager.cs
index 3898505..0053d1f 100644 (file)
-// \r
-// Radegast Metaverse Client\r
-// Copyright (c) 2009, Radegast Development Team\r
-// All rights reserved.\r
-// \r
-// Redistribution and use in source and binary forms, with or without\r
-// modification, are permitted provided that the following conditions are met:\r
-// \r
-//     * Redistributions of source code must retain the above copyright notice,\r
-//       this list of conditions and the following disclaimer.\r
-//     * Redistributions in binary form must reproduce the above copyright\r
-//       notice, this list of conditions and the following disclaimer in the\r
-//       documentation and/or other materials provided with the distribution.\r
-//     * Neither the name of the application "Radegast", nor the names of its\r
-//       contributors may be used to endorse or promote products derived from\r
-//       this software without specific prior written permission.\r
-// \r
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\r
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-//\r
-// $Id$\r
-//\r
-using System;\r
-using System.Collections.Generic;\r
-using System.Drawing;\r
-using System.Text;\r
-using Radegast.Netcom;\r
-using OpenMetaverse;\r
-\r
-namespace Radegast\r
-{\r
-    public class ChatTextManager\r
-    {\r
-        private RadegastInstance instance;\r
-        private RadegastNetcom netcom;\r
-        private GridClient client;\r
-        private ITextPrinter textPrinter;\r
-\r
-        private List<ChatBufferItem> textBuffer;\r
-\r
-        private bool showTimestamps;\r
-\r
-        public ChatTextManager(RadegastInstance instance, ITextPrinter textPrinter)\r
-        {\r
-            this.textPrinter = textPrinter;\r
-            this.textBuffer = new List<ChatBufferItem>();\r
-\r
-            this.instance = instance;\r
-            netcom = this.instance.Netcom;\r
-            client = this.instance.Client;\r
-            AddNetcomEvents();\r
-\r
-            showTimestamps = this.instance.Config.CurrentConfig.ChatTimestamps;\r
-            this.instance.Config.ConfigApplied += new EventHandler<ConfigAppliedEventArgs>(Config_ConfigApplied);\r
-        }\r
-\r
-        private void Config_ConfigApplied(object sender, ConfigAppliedEventArgs e)\r
-        {\r
-            showTimestamps = e.AppliedConfig.ChatTimestamps;\r
-            ReprintAllText();\r
-        }\r
-\r
-        private void AddNetcomEvents()\r
-        {\r
-            netcom.ClientLoginStatus += new EventHandler<ClientLoginEventArgs>(netcom_ClientLoginStatus);\r
-            netcom.ClientLoggedOut += new EventHandler(netcom_ClientLoggedOut);\r
-            netcom.ClientDisconnected += new EventHandler<ClientDisconnectEventArgs>(netcom_ClientDisconnected);\r
-            netcom.ChatReceived += new EventHandler<ChatEventArgs>(netcom_ChatReceived);\r
-            netcom.ChatSent += new EventHandler<ChatSentEventArgs>(netcom_ChatSent);\r
-            netcom.AlertMessageReceived += new EventHandler<AlertMessageEventArgs>(netcom_AlertMessageReceived);\r
-        }\r
-\r
-        private void netcom_ChatSent(object sender, ChatSentEventArgs e)\r
-        {\r
-            if (e.Channel == 0) return;\r
-\r
-            ProcessOutgoingChat(e);\r
-        }\r
-\r
-        private void netcom_ClientLoginStatus(object sender, ClientLoginEventArgs e)\r
-        {\r
-            if (e.Status == LoginStatus.Success)\r
-            {\r
-                ChatBufferItem loggedIn = new ChatBufferItem(\r
-                    DateTime.Now,\r
-                    "Logged into Second Life as " + netcom.LoginOptions.FullName + ".",\r
-                    ChatBufferTextStyle.StatusBlue);\r
-\r
-                ChatBufferItem loginReply = new ChatBufferItem(\r
-                    DateTime.Now, "Login reply: " + e.Message, ChatBufferTextStyle.StatusDarkBlue);\r
-\r
-                ProcessBufferItem(loggedIn, true);\r
-                ProcessBufferItem(loginReply, true);\r
-            }\r
-            else if (e.Status == LoginStatus.Failed)\r
-            {\r
-                ChatBufferItem loginError = new ChatBufferItem(\r
-                    DateTime.Now, "Login error: " + e.Message, ChatBufferTextStyle.Error);\r
-\r
-                ProcessBufferItem(loginError, true);\r
-            }\r
-        }\r
-\r
-        private void netcom_ClientLoggedOut(object sender, EventArgs e)\r
-        {\r
-            ChatBufferItem item = new ChatBufferItem(\r
-                DateTime.Now, "Logged out of Second Life.\n", ChatBufferTextStyle.StatusBlue);\r
-\r
-            ProcessBufferItem(item, true);\r
-        }\r
-\r
-        private void netcom_AlertMessageReceived(object sender, AlertMessageEventArgs e)\r
-        {\r
-            if (e.Message.ToLower().Contains("autopilot canceled")) return; //workaround the stupid autopilot alerts\r
-\r
-            ChatBufferItem item = new ChatBufferItem(\r
-                DateTime.Now, "Alert message: " + e.Message, ChatBufferTextStyle.Alert);\r
-\r
-            ProcessBufferItem(item, true);\r
-        }\r
-\r
-        private void netcom_ClientDisconnected(object sender, ClientDisconnectEventArgs e)\r
-        {\r
-            if (e.Type == NetworkManager.DisconnectType.ClientInitiated) return;\r
-\r
-            ChatBufferItem item = new ChatBufferItem(\r
-                DateTime.Now, "Client disconnected. Message: " + e.Message, ChatBufferTextStyle.Error);\r
-\r
-            ProcessBufferItem(item, true);\r
-        }\r
-\r
-        private void netcom_ChatReceived(object sender, ChatEventArgs e)\r
-        {\r
-            ProcessIncomingChat(e);\r
-        }\r
-\r
-        public void PrintStartupMessage()\r
-        {\r
-            ChatBufferItem title = new ChatBufferItem(\r
-                DateTime.Now, Properties.Resources.RadegastTitle, ChatBufferTextStyle.StartupTitle);\r
-\r
-            ChatBufferItem ready = new ChatBufferItem(\r
-                DateTime.Now, "Ready.\n", ChatBufferTextStyle.StatusBlue);\r
-\r
-            ProcessBufferItem(title, true);\r
-            ProcessBufferItem(ready, true);\r
-        }\r
-\r
-        public void ProcessBufferItem(ChatBufferItem item, bool addToBuffer)\r
-        {\r
-            instance.LogClientMessage("chat.txt", item.Text);\r
-            if (addToBuffer) textBuffer.Add(item);\r
-\r
-            if (showTimestamps)\r
-            {\r
-                textPrinter.ForeColor = Color.Gray;\r
-                textPrinter.PrintText(item.Timestamp.ToString("[HH:mm] "));\r
-            }\r
-\r
-            switch (item.Style)\r
-            {\r
-                case ChatBufferTextStyle.Normal:\r
-                    textPrinter.ForeColor = Color.Black;\r
-                    break;\r
-\r
-                case ChatBufferTextStyle.StatusBlue:\r
-                    textPrinter.ForeColor = Color.Blue;\r
-                    break;\r
-\r
-                case ChatBufferTextStyle.StatusDarkBlue:\r
-                    textPrinter.ForeColor = Color.DarkBlue;\r
-                    break;\r
-\r
-                case ChatBufferTextStyle.LindenChat:\r
-                    textPrinter.ForeColor = Color.DarkGreen;\r
-                    break;\r
-\r
-                case ChatBufferTextStyle.ObjectChat:\r
-                    textPrinter.ForeColor = Color.DarkCyan;\r
-                    break;\r
-\r
-                case ChatBufferTextStyle.StartupTitle:\r
-                    textPrinter.ForeColor = Color.Black;\r
-                    textPrinter.Font = new Font(textPrinter.Font, FontStyle.Bold);\r
-                    break;\r
-\r
-                case ChatBufferTextStyle.Alert:\r
-                    textPrinter.ForeColor = Color.DarkRed;\r
-                    break;\r
-\r
-                case ChatBufferTextStyle.Error:\r
-                    textPrinter.ForeColor = Color.Red;\r
-                    break;\r
-            }\r
-\r
-            textPrinter.PrintTextLine(item.Text);\r
-        }\r
-\r
-        //Used only for non-public chat\r
-        private void ProcessOutgoingChat(ChatSentEventArgs e)\r
-        {\r
-            StringBuilder sb = new StringBuilder();\r
-\r
-            sb.AppendFormat("(channel {0}) {1}", e.Channel, client.Self.Name);\r
-\r
-            switch (e.Type)\r
-            {\r
-                case ChatType.Normal:\r
-                    sb.Append(": ");\r
-                    break;\r
-\r
-                case ChatType.Whisper:\r
-                    sb.Append(" whisper: ");\r
-                    break;\r
-\r
-                case ChatType.Shout:\r
-                    sb.Append(" shout: ");\r
-                    break;\r
-            }\r
-\r
-            sb.Append(e.Message);\r
-\r
-            ChatBufferItem item = new ChatBufferItem(\r
-                DateTime.Now, sb.ToString(), ChatBufferTextStyle.StatusDarkBlue);\r
-\r
-            ProcessBufferItem(item, true);\r
-\r
-            sb = null;\r
-        }\r
-\r
-        private void ProcessIncomingChat(ChatEventArgs e)\r
-        {\r
-            if (string.IsNullOrEmpty(e.Message)) return;\r
-\r
-            StringBuilder sb = new StringBuilder();\r
-            // if (e.SourceType == ChatSourceType.Object) {\r
-            //    sb.Append(e.Position + " ");\r
-            // }\r
-            if (e.Message.StartsWith("/me "))\r
-            {\r
-                sb.Append(e.FromName);\r
-                sb.Append(e.Message.Substring(3));\r
-            }\r
-            else if (e.FromName == netcom.LoginOptions.FullName && e.SourceType == ChatSourceType.Agent)\r
-            {\r
-                sb.Append(client.Self.Name);\r
-\r
-                switch (e.Type)\r
-                {\r
-                    case ChatType.Normal:\r
-                        sb.Append(": ");\r
-                        break;\r
-\r
-                    case ChatType.Whisper:\r
-                        sb.Append(" whispers: ");\r
-                        break;\r
-\r
-                    case ChatType.Shout:\r
-                        sb.Append(" shouts: ");\r
-                        break;\r
-                }\r
-\r
-                sb.Append(e.Message);\r
-            }\r
-            else\r
-            {\r
-                sb.Append(e.FromName);\r
-\r
-                switch (e.Type)\r
-                {\r
-                    case ChatType.Normal:\r
-                        sb.Append(": ");\r
-                        break;\r
-\r
-                    case ChatType.Whisper:\r
-                        sb.Append(" whispers: ");\r
-                        break;\r
-\r
-                    case ChatType.Shout:\r
-                        sb.Append(" shouts: ");\r
-                        break;\r
-                }\r
-\r
-                sb.Append(e.Message);\r
-            }\r
-\r
-            ChatBufferItem item = new ChatBufferItem();\r
-            item.Timestamp = DateTime.Now;\r
-            item.Text = sb.ToString();\r
-\r
-            switch (e.SourceType)\r
-            {\r
-                case ChatSourceType.Agent:\r
-                    item.Style =\r
-                        (e.FromName.EndsWith("Linden") ?\r
-                        ChatBufferTextStyle.LindenChat : ChatBufferTextStyle.Normal);\r
-                    break;\r
-\r
-                case ChatSourceType.Object:\r
-                    item.Style = ChatBufferTextStyle.ObjectChat;\r
-                    break;\r
-            }\r
-\r
-            ProcessBufferItem(item, true);\r
-            sb = null;\r
-        }\r
-\r
-        public void ReprintAllText()\r
-        {\r
-            textPrinter.ClearText();\r
-\r
-            foreach (ChatBufferItem item in textBuffer)\r
-            {\r
-                ProcessBufferItem(item, false);\r
-            }\r
-        }\r
-\r
-        public void ClearInternalBuffer()\r
-        {\r
-            textBuffer.Clear();\r
-        }\r
-\r
-        public ITextPrinter TextPrinter\r
-        {\r
-            get { return textPrinter; }\r
-            set { textPrinter = value; }\r
-        }\r
-    }\r
-}\r
+// 
+// Radegast Metaverse Client
+// Copyright (c) 2009-2014, Radegast Development Team
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//     * Neither the name of the application "Radegast", nor the names of its
+//       contributors may be used to endorse or promote products derived from
+//       this software without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// $Id$
+//
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Text;
+using Radegast.Netcom;
+using OpenMetaverse;
+using OpenMetaverse.StructuredData;
+
+namespace Radegast
+{
+    public class ChatTextManager : IDisposable
+    {
+        public event EventHandler<ChatLineAddedArgs> ChatLineAdded;
+
+        private RadegastInstance instance;
+        private RadegastNetcom netcom { get { return instance.Netcom; } }
+        private GridClient client { get { return instance.Client; } }
+        private ITextPrinter textPrinter;
+
+        private List<ChatBufferItem> textBuffer;
+
+        private bool showTimestamps;
+
+        public ChatTextManager(RadegastInstance instance, ITextPrinter textPrinter)
+        {
+            this.textPrinter = textPrinter;
+            this.textBuffer = new List<ChatBufferItem>();
+
+            this.instance = instance;
+            InitializeConfig();
+
+            // Callbacks
+            netcom.ChatReceived += new EventHandler<ChatEventArgs>(netcom_ChatReceived);
+            netcom.ChatSent += new EventHandler<ChatSentEventArgs>(netcom_ChatSent);
+            netcom.AlertMessageReceived += new EventHandler<AlertMessageEventArgs>(netcom_AlertMessageReceived);
+        }
+
+        public void Dispose()
+        {
+            netcom.ChatReceived -= new EventHandler<ChatEventArgs>(netcom_ChatReceived);
+            netcom.ChatSent -= new EventHandler<ChatSentEventArgs>(netcom_ChatSent);
+            netcom.AlertMessageReceived -= new EventHandler<AlertMessageEventArgs>(netcom_AlertMessageReceived);
+        }
+
+        private void InitializeConfig()
+        {
+            Settings s = instance.GlobalSettings;
+
+            if (s["chat_timestamps"].Type == OSDType.Unknown)
+                s["chat_timestamps"] = OSD.FromBoolean(true);
+
+            showTimestamps = s["chat_timestamps"].AsBoolean();
+
+            s.OnSettingChanged += new Settings.SettingChangedCallback(s_OnSettingChanged);
+        }
+
+        void s_OnSettingChanged(object sender, SettingsEventArgs e)
+        {
+            if (e.Key == "chat_timestamps" && e.Value != null)
+            {
+                showTimestamps = e.Value.AsBoolean();
+                ReprintAllText();
+            }
+        }
+
+        private void netcom_ChatSent(object sender, ChatSentEventArgs e)
+        {
+            if (e.Channel == 0) return;
+
+            ProcessOutgoingChat(e);
+        }
+
+        private void netcom_AlertMessageReceived(object sender, AlertMessageEventArgs e)
+        {
+            if (e.Message.ToLower().Contains("autopilot canceled")) return; //workaround the stupid autopilot alerts
+
+            ChatBufferItem item = new ChatBufferItem(
+                DateTime.Now, "Alert message", UUID.Zero, ": " + e.Message, ChatBufferTextStyle.Alert);
+
+            ProcessBufferItem(item, true);
+        }
+
+        private void netcom_ChatReceived(object sender, ChatEventArgs e)
+        {
+            ProcessIncomingChat(e);
+        }
+
+        public void PrintStartupMessage()
+        {
+            ChatBufferItem title = new ChatBufferItem(
+                DateTime.Now, "", UUID.Zero, Properties.Resources.RadegastTitle + "." + RadegastBuild.CurrentRev, ChatBufferTextStyle.StartupTitle);
+
+            ChatBufferItem ready = new ChatBufferItem(
+                DateTime.Now, "", UUID.Zero, "Ready.", ChatBufferTextStyle.StatusBlue);
+
+            ProcessBufferItem(title, true);
+            ProcessBufferItem(ready, true);
+        }
+
+        private Object SyncChat = new Object();
+
+        public void ProcessBufferItem(ChatBufferItem item, bool addToBuffer)
+        {
+            if (ChatLineAdded != null)
+            {
+                ChatLineAdded(this, new ChatLineAddedArgs(item));
+            }
+
+            lock (SyncChat)
+            {
+                instance.LogClientMessage("chat.txt", item.From + item.Text);
+                if (addToBuffer) textBuffer.Add(item);
+
+                if (showTimestamps)
+                {
+                    textPrinter.ForeColor = SystemColors.GrayText;
+                    textPrinter.PrintText(item.Timestamp.ToString("[HH:mm] "));
+                }
+
+                switch (item.Style)
+                {
+                    case ChatBufferTextStyle.Normal:
+                        textPrinter.ForeColor = SystemColors.WindowText;
+                        break;
+
+                    case ChatBufferTextStyle.StatusBlue:
+                        textPrinter.ForeColor = Color.Blue;
+                        break;
+
+                    case ChatBufferTextStyle.StatusDarkBlue:
+                        textPrinter.ForeColor = Color.DarkBlue;
+                        break;
+
+                    case ChatBufferTextStyle.LindenChat:
+                        textPrinter.ForeColor = Color.DarkGreen;
+                        break;
+
+                    case ChatBufferTextStyle.ObjectChat:
+                        textPrinter.ForeColor = Color.DarkCyan;
+                        break;
+
+                    case ChatBufferTextStyle.StartupTitle:
+                        textPrinter.ForeColor = SystemColors.WindowText;
+                        textPrinter.Font = new Font(textPrinter.Font, FontStyle.Bold);
+                        break;
+
+                    case ChatBufferTextStyle.Alert:
+                        textPrinter.ForeColor = Color.DarkRed;
+                        break;
+
+                    case ChatBufferTextStyle.Error:
+                        textPrinter.ForeColor = Color.Red;
+                        break;
+
+                    case ChatBufferTextStyle.OwnerSay:
+                        textPrinter.ForeColor = Color.FromArgb(255, 180, 150, 0);
+                        break;
+                }
+
+                if (item.Style == ChatBufferTextStyle.Normal && item.ID != UUID.Zero && instance.GlobalSettings["av_name_link"])
+                {
+                    textPrinter.InsertLink(item.From, string.Format("secondlife:///app/agent/{0}/about", item.ID));
+                    textPrinter.PrintTextLine(item.Text);
+                }
+                else
+                {
+                    textPrinter.PrintTextLine(item.From + item.Text);
+                }
+            }
+        }
+
+        //Used only for non-public chat
+        private void ProcessOutgoingChat(ChatSentEventArgs e)
+        {
+            StringBuilder sb = new StringBuilder();
+
+            switch (e.Type)
+            {
+                case ChatType.Normal:
+                    sb.Append(": ");
+                    break;
+
+                case ChatType.Whisper:
+                    sb.Append(" whisper: ");
+                    break;
+
+                case ChatType.Shout:
+                    sb.Append(" shout: ");
+                    break;
+            }
+
+            sb.Append(e.Message);
+
+            ChatBufferItem item = new ChatBufferItem(
+                DateTime.Now, string.Format("(channel {0}) {1}", e.Channel, client.Self.Name), client.Self.AgentID, sb.ToString(), ChatBufferTextStyle.StatusDarkBlue);
+
+            ProcessBufferItem(item, true);
+
+            sb = null;
+        }
+
+        private void ProcessIncomingChat(ChatEventArgs e)
+        {
+            if (string.IsNullOrEmpty(e.Message)) return;
+
+            // Check if the sender agent is muted
+            if (e.SourceType == ChatSourceType.Agent &&
+                null != client.Self.MuteList.Find(me => me.Type == MuteType.Resident && me.ID == e.SourceID)
+                ) return;
+
+            // Check if it's script debug
+            if (e.Type == ChatType.Debug && !instance.GlobalSettings["show_script_errors"])
+            {
+                return;
+            }
+
+            // Check if sender object is muted
+            if (e.SourceType == ChatSourceType.Object &&
+                null != client.Self.MuteList.Find(me =>
+                    (me.Type == MuteType.Resident && me.ID == e.OwnerID) // Owner muted
+                    || (me.Type == MuteType.Object && me.ID == e.SourceID) // Object muted by ID
+                    || (me.Type == MuteType.ByName && me.Name == e.FromName) // Object muted by name
+                )) return;
+
+            if (instance.RLV.Enabled && e.Message.StartsWith("@"))
+            {
+                instance.RLV.TryProcessCMD(e);
+#if !DEBUG
+                if (!instance.RLV.EnabledDebugCommands) {
+                    return;
+                }
+#endif
+            }
+
+            ChatBufferItem item = new ChatBufferItem();
+            item.ID = e.SourceID;
+            item.RawMessage = e;
+            StringBuilder sb = new StringBuilder();
+
+            if (e.SourceType == ChatSourceType.Agent)
+            {
+                item.From = instance.Names.Get(e.SourceID, e.FromName);
+            }
+            else
+            {
+                item.From = e.FromName;
+            }
+
+            bool isEmote = e.Message.ToLower().StartsWith("/me ");
+
+            if (!isEmote)
+            {
+                switch (e.Type)
+                {
+
+                    case ChatType.Whisper:
+                        sb.Append(" whispers");
+                        break;
+
+                    case ChatType.Shout:
+                        sb.Append(" shouts");
+                        break;
+                }
+            }
+
+            if (isEmote)
+            {
+                if (e.SourceType == ChatSourceType.Agent && instance.RLV.RestictionActive("recvemote", e.SourceID.ToString()))
+                    sb.Append(" ...");
+                else
+                    sb.Append(e.Message.Substring(3));
+            }
+            else
+            {
+                sb.Append(": ");
+                if (e.SourceType == ChatSourceType.Agent && !e.Message.StartsWith("/") && instance.RLV.RestictionActive("recvchat", e.SourceID.ToString()))
+                    sb.Append("...");
+                else
+                    sb.Append(e.Message);
+            }
+
+            item.Timestamp = DateTime.Now;
+            item.Text = sb.ToString();
+
+            switch (e.SourceType)
+            {
+                case ChatSourceType.Agent:
+                    item.Style =
+                        (e.FromName.EndsWith("Linden") ?
+                        ChatBufferTextStyle.LindenChat : ChatBufferTextStyle.Normal);
+                    break;
+
+                case ChatSourceType.Object:
+                    if (e.Type == ChatType.OwnerSay)
+                    {
+                        item.Style = ChatBufferTextStyle.OwnerSay;
+                    }
+                    else if (e.Type == ChatType.Debug)
+                    {
+                        item.Style = ChatBufferTextStyle.Error;
+                    }
+                    else
+                    {
+                        item.Style = ChatBufferTextStyle.ObjectChat;
+                    }
+                    break;
+            }
+
+            ProcessBufferItem(item, true);
+            instance.TabConsole.Tabs["chat"].Highlight();
+
+            sb = null;
+        }
+
+        public void ReprintAllText()
+        {
+            textPrinter.ClearText();
+
+            foreach (ChatBufferItem item in textBuffer)
+            {
+                ProcessBufferItem(item, false);
+            }
+        }
+
+        public void ClearInternalBuffer()
+        {
+            textBuffer.Clear();
+        }
+
+        public ITextPrinter TextPrinter
+        {
+            get { return textPrinter; }
+            set { textPrinter = value; }
+        }
+    }
+
+    public class ChatLineAddedArgs : EventArgs
+    {
+        ChatBufferItem mItem;
+        public ChatBufferItem Item { get { return mItem; } }
+
+        public ChatLineAddedArgs(ChatBufferItem item)
+        {
+            mItem = item;
+        }
+    }
+}