OSDN Git Service

RAD-141: Allow speech plugin to be activated after login.
[radegast/radegast.git] / plugins / Radegast.Plugin.Speech / RadSpeech / Conversation / Control.cs
index 62ebe73..225db84 100644 (file)
-using System;\r
-using System.Collections.Generic;\r
-using System.Linq;\r
-using System.Text;\r
-using OpenMetaverse;\r
-using Radegast;\r
-using System.Text.RegularExpressions;\r
-using System.Net;\r
-using System.Windows.Forms;\r
-\r
-namespace RadegastSpeech.Conversation\r
-{\r
-    /// <summary>\r
-    /// Manages all conversations\r
-    /// </summary>\r
-    internal class Control : AreaControl\r
-    {\r
-        /// <summary>\r
-        /// Conversations correspond to tabbed panels on the main window.\r
-        /// </summary>\r
-        private Dictionary<string, Mode> conversations;\r
-        /// <summary>\r
-        /// Interruptions are short-lived conversations about dialog boxes, etc\r
-        /// </summary>\r
-        private LinkedList<Mode> interruptions;\r
-\r
-        // The permanent conversations.\r
-        private Conversation.Chat chat;\r
-        private Conversation.Closet inventory;\r
-        private Conversation.Friends friends;\r
-        private Conversation.Voice voice;\r
-        private Conversation.Surroundings surroundings;\r
-        private Mode currentMode;\r
-        private Mode interrupted;\r
-        internal string LoginName;\r
-        private bool firstTime = true;\r
-        private const string CONVGRAMMAR = "conv";\r
-\r
-        internal Control(PluginControl pc)\r
-            : base(pc)\r
-        {\r
-            // Initialize the index to conversations and the list of pending interruptions.\r
-            interruptions = new LinkedList<Mode>();\r
-            conversations = new Dictionary<string,Mode>();\r
-        }\r
-\r
-        internal override void Start()\r
-        {\r
-            if (firstTime)\r
-            {\r
-                firstTime = false;\r
-\r
-                control.listener.CreateGrammar(\r
-                    CONVGRAMMAR,\r
-                    new string[] { "talk to Max",\r
-                    "skip",\r
-                    "who is online",\r
-                    "open the closet",\r
-                    "friends",\r
-                    "talk" });\r
-            }\r
-\r
-            // Automatically handle notifications (blue dialogs)\r
-            Notification.OnNotificationDisplayed +=\r
-                new Notification.NotificationCallback(OnNotificationDisplayed);\r
-//            Notification.OnNotificationClosed +=\r
-//                new Notification.NotificationCallback(OnNotificationClosed);\r
-\r
-            // Announce connect and disconnect.\r
-            control.instance.Netcom.ClientConnected +=\r
-                new EventHandler<EventArgs>(Network_ClientConnected);\r
-            control.instance.Netcom.ClientDisconnected +=\r
-                new EventHandler<DisconnectedEventArgs>(Network_Disconnected);\r
-\r
-            control.instance.Netcom.ClientLoginStatus +=\r
-                new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);\r
-\r
-            // Notice arrival in a new sim\r
-            control.instance.Client.Network.SimChanged +=\r
-                new EventHandler<SimChangedEventArgs>(Network_SimChanged);\r
-\r
-            control.instance.Netcom.ClientLoggingIn +=\r
-                new EventHandler<Radegast.Netcom.OverrideEventArgs>(Netcom_ClientLoggingIn);\r
-            // Watch the coming and going of main window tabs.\r
-            control.instance.TabConsole.OnTabAdded +=\r
-                new TabsConsole.TabCallback(TabConsole_OnTabAdded);\r
-            control.instance.TabConsole.OnTabRemoved +=\r
-                new TabsConsole.TabCallback(TabConsole_OnTabRemoved);\r
-\r
-            // Notice when the active tab changes on the graphics user interface.\r
-            control.instance.TabConsole.OnTabSelected +=\r
-                new TabsConsole.TabCallback(OnTabChange);\r
-\r
-            // Handle Instant Messages too\r
-            control.instance.Client.Self.IM +=\r
-                new EventHandler<InstantMessageEventArgs>(OnInstantMessage);\r
-\r
-            // Watch for global keys\r
-            control.instance.MainForm.KeyUp += new KeyEventHandler(MainForm_KeyUp);\r
-\r
-            // System messages in chat window\r
-            control.instance.TabConsole.OnChatNotification += new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);\r
-\r
-            control.listener.ActivateGrammar(CONVGRAMMAR);\r
-\r
-        }\r
-\r
-        /// <summary>\r
-        /// Say various notifications that come in the chat\r
-        /// </summary>\r
-        /// <param name="sender">Message sender</param>\r
-        /// <param name="e">Event args</param>\r
-        void TabConsole_OnChatNotification(object sender, ChatNotificationEventArgs e)\r
-        {\r
-            Talker.Say(e.Message);\r
-        }\r
-\r
-        /// <summary>\r
-        /// Watch for global function keys.\r
-        /// </summary>\r
-        /// <param name="sender"></param>\r
-        /// <param name="e"></param>\r
-        void MainForm_KeyUp(object sender, KeyEventArgs e)\r
-        {\r
-            // Escape clears the speak-ahead queue.\r
-            if (e.KeyCode == Keys.Escape)\r
-            {\r
-                Talker.Flush();\r
-                Talker.SayMore("Flushed.");\r
-                e.Handled = true;\r
-            }\r
-        }\r
-\r
-\r
-        void Netcom_ClientLoggingIn(object sender, Radegast.Netcom.OverrideEventArgs e)\r
-        {\r
-            Talker.SayMore("Logging in.  Please wait.");\r
-        }\r
-\r
-        private void netcom_ClientLoginStatus(\r
-            object sender,\r
-            LoginProgressEventArgs e)\r
-        {\r
-            switch (e.Status)\r
-            {\r
-                case LoginStatus.ConnectingToLogin:\r
-                    // Never seems to happen.  See Netcom_ClientLoggingIn\r
-                    Talker.SayMore("Connecting to login server");\r
-                    return;\r
-\r
-                case LoginStatus.ConnectingToSim:\r
-                    Talker.SayMore("Connecting to region");\r
-                    return;\r
-\r
-               case LoginStatus.Success:\r
-                    LoginName = control.instance.Netcom.LoginOptions.FullName;\r
-                    //Talker.SayMore("Logged in as " + LoginName);\r
-                    //if (friends != null)\r
-                    //    friends.Announce = true;\r
-                    return;\r
-\r
-                case LoginStatus.Failed:\r
-                    Talker.Say(e.Message +\r
-                        ". Press Enter twice to retry", Talk.BeepType.Bad);\r
-                    return;\r
-\r
-                default:\r
-                    return;\r
-            }\r
-        }\r
-\r
-        /// <summary>\r
-        /// Switch active conversation as tab focus moves.\r
-        /// </summary>\r
-        /// <param name="sender"></param>\r
-        /// <param name="e"></param>\r
-        void OnTabChange(object sender, TabEventArgs e)\r
-        {\r
-            System.Windows.Forms.Control sTabControl = e.Tab.Control;\r
-\r
-            if (sTabControl is InventoryConsole)\r
-                SelectConversation(inventory);\r
-            else if (sTabControl is ChatConsole)\r
-            {\r
-                if (chat == null)\r
-                {\r
-                    chat = new Chat(control);\r
-                    chat.Console = sTabControl;\r
-                    AddConversation(chat);\r
-                }\r
-                SelectConversation(chat);\r
-            }\r
-            else if (sTabControl is FriendsConsole)\r
-                SelectConversation(friends);\r
-            else if (sTabControl is VoiceConsole)\r
-                SelectConversation(voice);\r
-            else if (sTabControl is GroupIMTabWindow)\r
-            {\r
-                GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;\r
-                SelectConversation(\r
-                    control.instance.Groups[tab.SessionId].Name);\r
-            }\r
-            else if (sTabControl is IMTabWindow)\r
-            {\r
-                IMTabWindow tab = (IMTabWindow)sTabControl;\r
-                SelectConversation(tab.TargetName);\r
-            }\r
-            else if (sTabControl is ObjectsConsole)\r
-            {\r
-                SelectConversation(surroundings);\r
-            }\r
-\r
-        }\r
-\r
-        /// <summary>\r
-        /// Create conversations as tabs are created.\r
-        /// </summary>\r
-        /// <param name="sender"></param>\r
-        /// <param name="e"></param>\r
-        void TabConsole_OnTabAdded(object sender, TabEventArgs e)\r
-        {\r
-            System.Windows.Forms.Control sTabControl = e.Tab.Control;\r
-\r
-            Mode newConv = null;\r
-\r
-            // Create a conversation on first appearance of its tab.\r
-            if (sTabControl is InventoryConsole)\r
-                newConv = inventory = new Closet(control);\r
-            else if (sTabControl is ChatConsole)\r
-            {\r
-                if (chat != null) return;\r
-                newConv = chat = new Chat(control);\r
-            }\r
-            else if (sTabControl is FriendsConsole)\r
-                newConv = friends = new Friends(control);\r
-            else if (sTabControl is VoiceConsole)\r
-                newConv = voice = new Voice(control);\r
-            else if (sTabControl is GroupIMTabWindow)\r
-            {\r
-                GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;\r
-                AddConversation(new GroupIMSession(control, tab.SessionId));\r
-                return;\r
-            }\r
-            else if (sTabControl is IMTabWindow)\r
-            {\r
-                IMTabWindow tab = (IMTabWindow)sTabControl;\r
-                AddConversation(new SingleIMSession(control, tab.TargetName, tab.TargetId, tab.SessionId));\r
-                return;\r
-            }\r
-            else if (sTabControl is ObjectsConsole)\r
-            {\r
-                surroundings = new Surroundings( control );\r
-                AddConversation( surroundings );\r
-            }\r
-\r
-            // If a conversation was created, switch to it.\r
-            if (newConv != null)\r
-            {\r
-                AddConversation(newConv);\r
-                // Select CHAT as soon as it is created.\r
-                if (sTabControl is ChatConsole)\r
-                    SelectConversation(newConv);\r
-            }\r
-        }\r
-\r
-        /// <summary>\r
-        /// Quietly close conversations.\r
-        /// </summary>\r
-        /// <param name="sender"></param>\r
-        /// <param name="e"></param>\r
-        void TabConsole_OnTabRemoved(object sender, TabEventArgs e)\r
-        {\r
-            System.Windows.Forms.Control sTabControl = e.Tab.Control;\r
-            if (sTabControl is InventoryConsole)\r
-                RemoveConversation( inventory.Title );\r
-            else if (sTabControl is ChatConsole)\r
-                RemoveConversation(chat.Title);\r
-            else if (sTabControl is FriendsConsole)\r
-                RemoveConversation(friends.Title);\r
-            else if (sTabControl is VoiceConsole)\r
-                RemoveConversation(voice.Title);\r
-            else if (sTabControl is GroupIMTabWindow ||\r
-                     sTabControl is IMTabWindow)\r
-                RemoveConversation(sTabControl.Name);  // TODO wrong name\r
-        }\r
-\r
-\r
-        internal override void Shutdown()\r
-        {\r
-            // Automatically handle notifications (blue dialogs)\r
-            Notification.OnNotificationDisplayed -=\r
-                new Notification.NotificationCallback(OnNotificationDisplayed);\r
-\r
-            // Announce connect and disconnect.\r
-            control.instance.Netcom.ClientConnected -=\r
-                new EventHandler<EventArgs>(Network_ClientConnected);\r
-            control.instance.Netcom.ClientDisconnected -=\r
-                new EventHandler<DisconnectedEventArgs>(Network_Disconnected);\r
-\r
-            control.instance.Netcom.ClientLoginStatus -=\r
-                new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);\r
-\r
-            // Notice arrival in a new sim\r
-            control.instance.Client.Network.SimChanged -=\r
-                new EventHandler<SimChangedEventArgs>(Network_SimChanged);\r
-\r
-            control.instance.Netcom.ClientLoggingIn -=\r
-                new EventHandler<Radegast.Netcom.OverrideEventArgs>(Netcom_ClientLoggingIn);\r
-            // Watch the coming and going of main window tabs.\r
-            control.instance.TabConsole.OnTabAdded -=\r
-                new TabsConsole.TabCallback(TabConsole_OnTabAdded);\r
-            control.instance.TabConsole.OnTabRemoved -=\r
-                new TabsConsole.TabCallback(TabConsole_OnTabRemoved);\r
-\r
-            // Notice when the active tab changes on the graphics user interface.\r
-            control.instance.TabConsole.OnTabSelected -=\r
-                new TabsConsole.TabCallback(OnTabChange);\r
-\r
-            // Handle Instant Messages too\r
-            control.instance.Client.Self.IM +=\r
-                new EventHandler<InstantMessageEventArgs>(OnInstantMessage);\r
-\r
-            // System notifications in chat\r
-            control.instance.TabConsole.OnChatNotification -= new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);\r
-\r
-            control.listener.DeactivateGrammar(CONVGRAMMAR);\r
-\r
-            foreach (Mode m in conversations.Values)\r
-            {\r
-                m.Stop();\r
-            }\r
-            foreach (Mode m in interruptions)\r
-            {\r
-                m.Stop();\r
-            }\r
-            conversations.Clear();\r
-            interruptions.Clear();\r
-        }\r
-\r
-        void WatchKeys()\r
-        {\r
-        }\r
-\r
-        internal bool amCurrent(Mode m)\r
-        {\r
-            return (currentMode == m);\r
-        }\r
-\r
-        /// <summary>\r
-        /// Start an interrupting conversation.\r
-        /// </summary>\r
-        void StartInterruption()\r
-        {\r
-            // Remember what we were talking about.\r
-            if (interrupted == null)\r
-                interrupted = currentMode;\r
-\r
-            // Visually they stack up, so we take the last one first.\r
-            currentMode = interruptions.Last();\r
-            currentMode.Start();\r
-        }\r
-\r
-        /// <summary>\r
-        /// Finish an interruption and resume normal conversation\r
-        /// </summary>\r
-        internal void FinishInterruption( Mode m )\r
-        {\r
-            lock (interruptions)\r
-            {\r
-                // Remove the terminating interruption from the list.\r
-                interruptions.Remove(m);\r
-\r
-                // Let it remove any event hooks, etc\r
-                m.Stop();\r
-\r
-                // If there are any other interruptions pending, start one.\r
-                // Otherwise resume the interrupted conversation.\r
-                if (interruptions.Count > 0)\r
-                    StartInterruption();\r
-                else\r
-                {\r
-                    currentMode = interrupted;\r
-                    interrupted = null;\r
-                    currentMode.Start();\r
-                }\r
-            }\r
-        }\r
\r
-        private void Network_ClientConnected(object sender, EventArgs e)\r
-        {\r
-            Talker.Say("You are connected.", Talk.BeepType.Good);\r
-            if (chat == null)\r
-            {\r
-                chat = new Chat(control);\r
-\r
-                AddConversation(chat);\r
-                SelectConversation(chat);\r
-            }\r
-        }\r
-\r
-        void Network_SimChanged(object sender, SimChangedEventArgs e)\r
-        {\r
-            Talker.Say("You are now in " +\r
-                control.instance.Client.Network.CurrentSim.Name,\r
-                Talk.BeepType.Good);\r
-        }\r
-\r
-\r
-        /// <summary>\r
-        /// Announce reason for disconnect.\r
-        /// </summary>\r
-        /// <param name="reason"></param>\r
-        /// <param name="message"></param>\r
-        private void Network_Disconnected(object sender, DisconnectedEventArgs e)\r
-        {\r
-            switch (e.Reason)\r
-            {\r
-                case NetworkManager.DisconnectType.ClientInitiated:\r
-                    Talker.Say("You are disconnected.");\r
-                    break;\r
-                case NetworkManager.DisconnectType.SimShutdown:\r
-                    Talker.Say("The region you were in has been shut down.  You are disconnected.",\r
-                        Talk.BeepType.Bad);\r
-                    break;\r
-                case NetworkManager.DisconnectType.NetworkTimeout:\r
-                    Talker.Say("You have been disconnected by a network timeout.",\r
-                        Talk.BeepType.Bad);\r
-                    break;\r
-                case NetworkManager.DisconnectType.ServerInitiated:\r
-                    Talker.Say("The server has disconnected you.  " + e.Message,\r
-                        Talk.BeepType.Bad);\r
-                    break;\r
-            }\r
-        }\r
-        private void ListFriends()\r
-        {\r
-            List<FriendInfo> onlineFriends =\r
-                control.instance.Client.Friends.FriendList.FindAll(delegate(FriendInfo f) { return f.IsOnline; });\r
-            string list = "";\r
-            foreach (FriendInfo f in onlineFriends)\r
-            {\r
-                list += f.Name + ", ";\r
-            }\r
-            list += "are online.";\r
-            Talker.Say(list);\r
-        }\r
-\r
-\r
-        /// <summary>\r
-        /// Check for general commands\r
-        /// </summary>\r
-        /// <param name="message"></param>\r
-        /// <returns>true if command recognized</returns>\r
-        private bool Command(string message)\r
-        {\r
-            switch (message.ToLower())\r
-            {\r
-                case "talk to max":\r
-                    AddInterruption(new Max(control));\r
-                    break;\r
-                case "who is online":\r
-                    ListFriends();\r
-                    break;\r
-                case "open the closet":\r
-                    control.instance.TabConsole.SelectTab("inventory");\r
-                    SelectConversation(inventory);\r
-                    break;\r
-                case "friends":\r
-                    control.instance.TabConsole.SelectTab("friends");\r
-                    SelectConversation(friends);\r
-                    break;\r
-                case "skip":\r
-                    Talker.Flush();\r
-                    Talker.SayMore("Flushed.");\r
-                    break;\r
-                case "talk":\r
-                    control.instance.TabConsole.SelectTab("chat");\r
-                    SelectConversation(chat);\r
-                    break;\r
-                case "voice":\r
-                    control.instance.TabConsole.SelectTab("voice");\r
-                    SelectConversation(voice);\r
-                    break;\r
-                default:\r
-                    return false;\r
-            }\r
-            return true;\r
-        }\r
-        \r
-        /// <summary>\r
-        /// Dispatch recognized text to appropriate conversation.\r
-        /// </summary>\r
-        /// <param name="message"></param>\r
-        internal void Hear(string message)\r
-        {\r
-            // General commands.\r
-            if (Command(message)) return;\r
-\r
-            // Let the current conversation handle it.\r
-            if (currentMode != null)\r
-                currentMode.Hear(message);\r
-        }\r
-\r
-        internal void SelectConversation(Mode c)\r
-        {\r
-            if (c == null)\r
-            {\r
-                Talker.Say("Trying to start non-existant conversation", Talk.BeepType.Bad );\r
-                return;\r
-            }\r
-            // Avoid multiple starts.\r
-            if (currentMode == c) return;\r
-\r
-            // Let the old conversation deactivate any event hooks, grammars, etc.\r
-            if (currentMode != null)\r
-                currentMode.Stop();\r
-\r
-            currentMode = c;\r
-            currentMode.Start();\r
-        }\r
-\r
-        internal void SelectConversation(string name)\r
-        {\r
-            if (conversations.ContainsKey(name))\r
-            {\r
-                SelectConversation(conversations[name]);\r
-            }\r
-            else\r
-            {\r
-                Talker.Say("Can not find conversation " + name, Talk.BeepType.Bad);\r
-            }\r
-        }\r
-\r
-        /// <summary>\r
-        /// Find an existing conversation by name.\r
-        /// </summary>\r
-        /// <param name="title"></param>\r
-        /// <returns></returns>\r
-        /// <remarks>Used for IM sessions.</remarks>\r
-        internal Mode GetConversation(string title)\r
-        {\r
-            if (conversations.ContainsKey(title))\r
-                return conversations[title];\r
-\r
-            return null;\r
-        }\r
-\r
-        /// <summary>\r
-        /// Add a conversation context to those we are tracking.\r
-        /// </summary>\r
-        /// <param name="m"></param>\r
-        internal void AddConversation(Mode m)\r
-        {\r
-            if (!conversations.ContainsKey(m.Title))\r
-                conversations[m.Title] = m;\r
-        }\r
-\r
-        /// <summary>\r
-        /// Remove the context for a conversation that is no longer visible.\r
-        /// </summary>\r
-        /// <param name="name"></param>\r
-        internal void RemoveConversation(string name)\r
-        {\r
-            bool change = false;\r
-\r
-            lock (conversations)\r
-            {\r
-                if (conversations.ContainsKey(name))\r
-                {\r
-                    Mode doomed = conversations[name];\r
-                    if (currentMode == doomed)\r
-                    {\r
-                        change = true;\r
-                        currentMode = chat;\r
-                    }\r
-                    if (interrupted == doomed)\r
-                        interrupted = chat;\r
-\r
-                    conversations.Remove(name);\r
-                    if (change)\r
-                        SelectConversation(currentMode);\r
-                }\r
-            }\r
-        }\r
-\r
-        /// <summary>\r
-        /// Take note of a new interruption.\r
-        /// </summary>\r
-        /// <param name="m"></param>\r
-        internal void AddInterruption(Mode m)\r
-        {\r
-            lock (interruptions)\r
-            {\r
-                // Add to the end of the list.\r
-                interruptions.AddLast(m);\r
-\r
-                // If the list WAS empty, start this.\r
-                if (interruptions.Count == 1)\r
-                    StartInterruption();\r
-            }\r
-        }\r
-\r
-        internal void ChangeFocus(Mode toThis)\r
-        {\r
-            currentMode = toThis;\r
-             if (currentMode != null)\r
-                currentMode.Start();\r
-        }\r
-\r
\r
-        /// <summary>\r
-        /// Event handler for new blue dialog boxes.\r
-        /// </summary>\r
-        /// <param name="sender"></param>\r
-        /// <param name="e"></param>\r
-        void OnNotificationDisplayed(object sender, NotificationEventArgs e)\r
-        {\r
-            AddInterruption(new Conversation.BlueMenu(control,e));\r
-        }\r
-\r
-        /// <summary>\r
-        /// Handle Instant Messages\r
-        /// </summary>\r
-        /// <param name="im"></param>\r
-        /// <param name="simulator"></param>\r
-        void OnInstantMessage(object sender, InstantMessageEventArgs e)\r
-        {\r
-            Conversation.IMSession sess;\r
-            string groupName;\r
-\r
-            // All sorts of things come in as a instant messages. For actual messages\r
-            // we need to match them up with an existing Conversation.  IM Conversations\r
-            // are keyed by the name of the group or individual involved.\r
-            switch (e.IM.Dialog)\r
-            {\r
-                case InstantMessageDialog.MessageFromAgent:\r
-                    if (control.instance.Groups.ContainsKey(e.IM.IMSessionID))\r
-                    {\r
-                        // Message from a group member\r
-                        groupName = control.instance.Groups[e.IM.IMSessionID].Name;\r
-                        sess = (IMSession)control.converse.GetConversation(groupName);\r
-                        if (sess!=null)\r
-                            sess.OnMessage( e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message );\r
-                    }\r
-                    else if (e.IM.BinaryBucket.Length >= 2)\r
-                    {\r
-                        // Ad-hoc friend conference\r
-                        // TODO this is probably the wrong name\r
-                        sess = (IMSession)control.converse.GetConversation(e.IM.FromAgentName);\r
-                        if (sess != null)\r
-                            sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);\r
-                    }\r
-                    else if (e.IM.FromAgentName == "Second Life")\r
-                    {\r
-                        Talker.Say("Second Life says "+ e.IM.Message);\r
-                    }\r
-                    else\r
-                    {\r
-                        // Message from an individual\r
-                        sess = (IMSession)control.converse.GetConversation(e.IM.FromAgentName); \r
-                        if (sess != null)\r
-                            sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);\r
-                    }\r
-                    break;\r
-\r
-                case InstantMessageDialog.SessionSend:\r
-                    // Message from a group member\r
-                    groupName = control.instance.Groups[e.IM.IMSessionID].Name;\r
-                    sess = (IMSession)control.converse.GetConversation(groupName);\r
-                    if (sess != null)\r
-                        sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);\r
-                    break;\r
-\r
-                case InstantMessageDialog.FriendshipOffered:\r
-                    Talker.Say(e.IM.FromAgentName + " is offering friendship.");\r
-                    break;\r
-\r
-                default:\r
-                    break;\r
-            }\r
-\r
-        }\r
-    }\r
-}\r
-\r
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OpenMetaverse;
+using Radegast;
+using System.Text.RegularExpressions;
+using System.Net;
+using System.Windows.Forms;
+using System.Threading;
+
+namespace RadegastSpeech.Conversation
+{
+    /// <summary>
+    /// Manages all conversations
+    /// </summary>
+    internal class Control : AreaControl
+    {
+        /// <summary>
+        /// Conversations correspond to tabbed panels on the main window.
+        /// </summary>
+        private Dictionary<string, Mode> conversations;
+        /// <summary>
+        /// Interruptions are short-lived conversations about dialog boxes, etc
+        /// </summary>
+        private LinkedList<Mode> interruptions;
+
+        // The permanent conversations.
+        private Conversation.Chat chat;
+        private Conversation.Closet inventory;
+        private Conversation.Friends friends;
+        private Conversation.Voice voice;
+        private Conversation.Surroundings surroundings;
+        private Mode currentMode;
+        private Mode interrupted;
+        internal string LoginName;
+        private bool firstTime = true;
+        private const string CONVGRAMMAR = "conv";
+
+        internal Control(PluginControl pc)
+            : base(pc)
+        {
+            // Initialize the index to conversations and the list of pending interruptions.
+            interruptions = new LinkedList<Mode>();
+            conversations = new Dictionary<string,Mode>();
+        }
+
+        internal override void Start()
+        {
+            if (firstTime)
+            {
+                firstTime = false;
+
+                control.listener.CreateGrammar(
+                    CONVGRAMMAR,
+                    new string[] { "talk to Max",
+                    "skip",
+                    "who is online",
+                    "open the closet",
+                    "friends",
+                    "talk" });
+            }
+
+            // Automatically handle notifications (blue dialogs)
+            Notification.OnNotificationDisplayed +=
+                new Notification.NotificationCallback(OnNotificationDisplayed);
+//            Notification.OnNotificationClosed +=
+//                new Notification.NotificationCallback(OnNotificationClosed);
+
+            // Announce connect and disconnect.
+            control.instance.Netcom.ClientConnected +=
+                new EventHandler<EventArgs>(Network_ClientConnected);
+            control.instance.Netcom.ClientDisconnected +=
+                new EventHandler<DisconnectedEventArgs>(Network_Disconnected);
+
+            control.instance.Netcom.ClientLoginStatus +=
+                new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
+
+            // Notice arrival in a new sim
+            control.instance.Client.Network.SimChanged +=
+                new EventHandler<SimChangedEventArgs>(Network_SimChanged);
+
+            control.instance.Netcom.ClientLoggingIn +=
+                new EventHandler<Radegast.Netcom.OverrideEventArgs>(Netcom_ClientLoggingIn);
+            // Watch the coming and going of main window tabs.
+            control.instance.TabConsole.OnTabAdded +=
+                new TabsConsole.TabCallback(TabConsole_OnTabAdded);
+            control.instance.TabConsole.OnTabRemoved +=
+                new TabsConsole.TabCallback(TabConsole_OnTabRemoved);
+
+            // Notice when the active tab changes on the graphics user interface.
+            control.instance.TabConsole.OnTabSelected +=
+                new TabsConsole.TabCallback(OnTabChange);
+
+            // Handle Instant Messages too
+            control.instance.Client.Self.IM +=
+                new EventHandler<InstantMessageEventArgs>(OnInstantMessage);
+
+            // Watch for global keys
+            control.instance.MainForm.KeyUp += new KeyEventHandler(MainForm_KeyUp);
+
+            // System messages in chat window
+            control.instance.TabConsole.OnChatNotification += new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);
+
+            control.listener.ActivateGrammar(CONVGRAMMAR);
+
+        }
+
+        /// <summary>
+        /// Say various notifications that come in the chat
+        /// </summary>
+        /// <param name="sender">Message sender</param>
+        /// <param name="e">Event args</param>
+        void TabConsole_OnChatNotification(object sender, ChatNotificationEventArgs e)
+        {
+            Talker.Say(e.Message);
+        }
+
+        /// <summary>
+        /// Watch for global function keys.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        void MainForm_KeyUp(object sender, KeyEventArgs e)
+        {
+            // Escape clears the speak-ahead queue.
+            if (e.KeyCode == Keys.Escape)
+            {
+                Talker.Flush();
+                Talker.SayMore("Flushed.");
+                e.Handled = true;
+            }
+        }
+
+
+        void Netcom_ClientLoggingIn(object sender, Radegast.Netcom.OverrideEventArgs e)
+        {
+            Talker.SayMore("Logging in.  Please wait.");
+        }
+
+        private void netcom_ClientLoginStatus(
+            object sender,
+            LoginProgressEventArgs e)
+        {
+            switch (e.Status)
+            {
+                case LoginStatus.ConnectingToLogin:
+                    // Never seems to happen.  See Netcom_ClientLoggingIn
+                    Talker.SayMore("Connecting to login server");
+                    return;
+
+                case LoginStatus.ConnectingToSim:
+                    Talker.SayMore("Connecting to region");
+                    return;
+
+               case LoginStatus.Success:
+                    LoginName = control.instance.Netcom.LoginOptions.FullName;
+                    //Talker.SayMore("Logged in as " + LoginName);
+                    //if (friends != null)
+                    //    friends.Announce = true;
+                    return;
+
+                case LoginStatus.Failed:
+                    Talker.Say(e.Message +
+                        ". Press Enter twice to retry", Talk.BeepType.Bad);
+                    return;
+
+                default:
+                    return;
+            }
+        }
+
+        /// <summary>
+        /// Switch active conversation as tab focus moves.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        void OnTabChange(object sender, TabEventArgs e)
+        {
+            ActivateConversationFromTab(e.Tab);
+        }
+
+        public void ActivateConversationFromTab(RadegastTab Tab)
+        {
+            System.Windows.Forms.Control sTabControl = Tab.Control;
+
+            if (sTabControl is InventoryConsole)
+                SelectConversation(inventory);
+            else if (sTabControl is ChatConsole)
+            {
+                if (chat == null)
+                {
+                    chat = new Chat(control);
+                    chat.Console = sTabControl;
+                    AddConversation(chat);
+                }
+                SelectConversation(chat);
+            }
+            else if (sTabControl is FriendsConsole)
+                SelectConversation(friends);
+            else if (sTabControl is VoiceConsole)
+                SelectConversation(voice);
+            else if (sTabControl is GroupIMTabWindow)
+            {
+                GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;
+                SelectConversation(
+                    control.instance.Groups[tab.SessionId].Name);
+            }
+            else if (sTabControl is ConferenceIMTabWindow)
+            {
+                ConferenceIMTabWindow tab = (ConferenceIMTabWindow)sTabControl;
+                SelectConversation(tab.SessionName);
+            }
+            else if (sTabControl is IMTabWindow)
+            {
+                IMTabWindow tab = (IMTabWindow)sTabControl;
+                SelectConversation(tab.TargetName);
+            }
+            else if (sTabControl is ObjectsConsole)
+            {
+                SelectConversation(surroundings);
+            }
+
+        }
+
+        /// <summary>
+        /// Create conversations as tabs are created.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        void TabConsole_OnTabAdded(object sender, TabEventArgs e)
+        {
+            CreateConversationFromTab(e.Tab, true);
+        }
+
+        public void CreateConversationFromTab(RadegastTab Tab, bool selectConversation)
+        {
+            System.Windows.Forms.Control sTabControl = Tab.Control;
+
+            Mode newConv = null;
+
+            // Create a conversation on first appearance of its tab.
+            if (sTabControl is InventoryConsole)
+                newConv = inventory = new Closet(control);
+            else if (sTabControl is ChatConsole)
+            {
+                if (chat != null) return;
+                newConv = chat = new Chat(control);
+            }
+            else if (sTabControl is FriendsConsole)
+                newConv = friends = new Friends(control);
+            else if (sTabControl is VoiceConsole)
+                newConv = voice = new Voice(control);
+            else if (sTabControl is GroupIMTabWindow)
+            {
+                GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;
+                AddConversation(new GroupIMSession(control, tab.SessionId));
+                return;
+            }
+            else if (sTabControl is ConferenceIMTabWindow)
+            {
+                ConferenceIMTabWindow tab = (ConferenceIMTabWindow)sTabControl;
+                AddConversation(new ConferenceIMSession(control, tab.SessionId, tab.SessionName));
+                return;
+            }
+            else if (sTabControl is IMTabWindow)
+            {
+                IMTabWindow tab = (IMTabWindow)sTabControl;
+                AddConversation(new SingleIMSession(control, tab.TargetName, tab.TargetId, tab.SessionId));
+                return;
+            }
+            else if (sTabControl is ObjectsConsole)
+            {
+                surroundings = new Surroundings(control);
+                AddConversation(surroundings);
+            }
+
+            // If a conversation was created, switch to it.
+            if (newConv != null)
+            {
+                AddConversation(newConv);
+                // Select CHAT as soon as it is created.
+                if (sTabControl is ChatConsole)
+                    SelectConversation(newConv);
+            }
+        }
+
+        /// <summary>
+        /// Quietly close conversations.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        void TabConsole_OnTabRemoved(object sender, TabEventArgs e)
+        {
+            System.Windows.Forms.Control sTabControl = e.Tab.Control;
+            if (sTabControl is InventoryConsole)
+                RemoveConversation(inventory.Title);
+            else if (sTabControl is ChatConsole)
+                RemoveConversation(chat.Title);
+            else if (sTabControl is FriendsConsole)
+                RemoveConversation(friends.Title);
+            else if (sTabControl is VoiceConsole)
+                RemoveConversation(voice.Title);
+            else if (sTabControl is ConferenceIMTabWindow)
+                RemoveConversation(((ConferenceIMTabWindow)e.Tab.Control).SessionName);
+            else if (sTabControl is GroupIMTabWindow ||
+                     sTabControl is IMTabWindow)
+                RemoveConversation(sTabControl.Name);  // TODO wrong name
+        }
+
+
+        internal override void Shutdown()
+        {
+            // Automatically handle notifications (blue dialogs)
+            Notification.OnNotificationDisplayed -=
+                new Notification.NotificationCallback(OnNotificationDisplayed);
+
+            // Announce connect and disconnect.
+            control.instance.Netcom.ClientConnected -=
+                new EventHandler<EventArgs>(Network_ClientConnected);
+            control.instance.Netcom.ClientDisconnected -=
+                new EventHandler<DisconnectedEventArgs>(Network_Disconnected);
+
+            control.instance.Netcom.ClientLoginStatus -=
+                new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
+
+            // Notice arrival in a new sim
+            control.instance.Client.Network.SimChanged -=
+                new EventHandler<SimChangedEventArgs>(Network_SimChanged);
+
+            control.instance.Netcom.ClientLoggingIn -=
+                new EventHandler<Radegast.Netcom.OverrideEventArgs>(Netcom_ClientLoggingIn);
+            // Watch the coming and going of main window tabs.
+            control.instance.TabConsole.OnTabAdded -=
+                new TabsConsole.TabCallback(TabConsole_OnTabAdded);
+            control.instance.TabConsole.OnTabRemoved -=
+                new TabsConsole.TabCallback(TabConsole_OnTabRemoved);
+
+            // Notice when the active tab changes on the graphics user interface.
+            control.instance.TabConsole.OnTabSelected -=
+                new TabsConsole.TabCallback(OnTabChange);
+
+            // Handle Instant Messages too
+            control.instance.Client.Self.IM +=
+                new EventHandler<InstantMessageEventArgs>(OnInstantMessage);
+
+            // System notifications in chat
+            control.instance.TabConsole.OnChatNotification -= new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);
+
+            control.listener.DeactivateGrammar(CONVGRAMMAR);
+
+            foreach (Mode m in conversations.Values)
+            {
+                m.Stop();
+            }
+            foreach (Mode m in interruptions)
+            {
+                m.Stop();
+            }
+            conversations.Clear();
+            interruptions.Clear();
+        }
+
+        void WatchKeys()
+        {
+        }
+
+        internal bool amCurrent(Mode m)
+        {
+            return (currentMode == m);
+        }
+
+        /// <summary>
+        /// Start an interrupting conversation.
+        /// </summary>
+        void StartInterruption()
+        {
+            // Remember what we were talking about.
+            if (interrupted == null)
+                interrupted = currentMode;
+
+            // Visually they stack up, so we take the last one first.
+            currentMode = interruptions.Last();
+            currentMode.Start();
+        }
+
+        /// <summary>
+        /// Finish an interruption and resume normal conversation
+        /// </summary>
+        internal void FinishInterruption( Mode m )
+        {
+            lock (interruptions)
+            {
+                // Remove the terminating interruption from the list.
+                interruptions.Remove(m);
+
+                // Let it remove any event hooks, etc
+                m.Stop();
+
+                // If there are any other interruptions pending, start one.
+                // Otherwise resume the interrupted conversation.
+                if (interruptions.Count > 0)
+                    StartInterruption();
+                else
+                {
+                    currentMode = interrupted;
+                    interrupted = null;
+                    currentMode.Start();
+                }
+            }
+        }
+        private void Network_ClientConnected(object sender, EventArgs e)
+        {
+            Talker.Say("You are connected.", Talk.BeepType.Good);
+            if (chat == null)
+            {
+                chat = new Chat(control);
+
+                AddConversation(chat);
+                SelectConversation(chat);
+            }
+        }
+
+        void Network_SimChanged(object sender, SimChangedEventArgs e)
+        {
+            Talker.Say("You are now in " +
+                control.instance.Client.Network.CurrentSim.Name,
+                Talk.BeepType.Good);
+        }
+
+
+        /// <summary>
+        /// Announce reason for disconnect.
+        /// </summary>
+        /// <param name="reason"></param>
+        /// <param name="message"></param>
+        private void Network_Disconnected(object sender, DisconnectedEventArgs e)
+        {
+            switch (e.Reason)
+            {
+                case NetworkManager.DisconnectType.ClientInitiated:
+                    Talker.Say("You are disconnected.");
+                    break;
+                case NetworkManager.DisconnectType.SimShutdown:
+                    Talker.Say("The region you were in has been shut down.  You are disconnected.",
+                        Talk.BeepType.Bad);
+                    break;
+                case NetworkManager.DisconnectType.NetworkTimeout:
+                    Talker.Say("You have been disconnected by a network timeout.",
+                        Talk.BeepType.Bad);
+                    break;
+                case NetworkManager.DisconnectType.ServerInitiated:
+                    Talker.Say("The server has disconnected you.  " + e.Message,
+                        Talk.BeepType.Bad);
+                    break;
+            }
+        }
+        private void ListFriends()
+        {
+            List<FriendInfo> onlineFriends =
+                control.instance.Client.Friends.FriendList.FindAll(delegate(FriendInfo f) { return f.IsOnline; });
+            string list = "";
+            foreach (FriendInfo f in onlineFriends)
+            {
+                list += f.Name + ", ";
+            }
+            list += "are online.";
+            Talker.Say(list);
+        }
+
+
+        /// <summary>
+        /// Check for general commands
+        /// </summary>
+        /// <param name="message"></param>
+        /// <returns>true if command recognized</returns>
+        private bool Command(string message)
+        {
+            switch (message.ToLower())
+            {
+                case "talk to max":
+                    AddInterruption(new Max(control));
+                    break;
+                case "who is online":
+                    ListFriends();
+                    break;
+                case "open the closet":
+                    control.instance.TabConsole.SelectTab("inventory");
+                    SelectConversation(inventory);
+                    break;
+                case "friends":
+                    control.instance.TabConsole.SelectTab("friends");
+                    SelectConversation(friends);
+                    break;
+                case "skip":
+                    Talker.Flush();
+                    Talker.SayMore("Flushed.");
+                    break;
+                case "talk":
+                    control.instance.TabConsole.SelectTab("chat");
+                    SelectConversation(chat);
+                    break;
+                case "voice":
+                    control.instance.TabConsole.SelectTab("voice");
+                    SelectConversation(voice);
+                    break;
+                default:
+                    return false;
+            }
+            return true;
+        }
+        
+        /// <summary>
+        /// Dispatch recognized text to appropriate conversation.
+        /// </summary>
+        /// <param name="message"></param>
+        internal void Hear(string message)
+        {
+            // General commands.
+            if (Command(message)) return;
+
+            // Let the current conversation handle it.
+            if (currentMode != null)
+                currentMode.Hear(message);
+        }
+
+        internal void SelectConversation(Mode c)
+        {
+            if (c == null)
+            {
+                Talker.Say("Trying to start non-existant conversation", Talk.BeepType.Bad );
+                return;
+            }
+            // Avoid multiple starts.
+            if (currentMode == c) return;
+
+            // Let the old conversation deactivate any event hooks, grammars, etc.
+            if (currentMode != null)
+                currentMode.Stop();
+
+            currentMode = c;
+            currentMode.Start();
+        }
+
+        internal void SelectConversation(string name)
+        {
+            if (conversations.ContainsKey(name))
+            {
+                SelectConversation(conversations[name]);
+            }
+            else
+            {
+                Talker.Say("Can not find conversation " + name, Talk.BeepType.Bad);
+            }
+        }
+
+        /// <summary>
+        /// Find an existing conversation by name.
+        /// </summary>
+        /// <param name="title"></param>
+        /// <returns></returns>
+        /// <remarks>Used for IM sessions.</remarks>
+        internal Mode GetConversation(string title)
+        {
+            if (conversations.ContainsKey(title))
+                return conversations[title];
+
+            return null;
+        }
+
+        /// <summary>
+        /// Add a conversation context to those we are tracking.
+        /// </summary>
+        /// <param name="m"></param>
+        internal void AddConversation(Mode m)
+        {
+            if (!conversations.ContainsKey(m.Title))
+                conversations[m.Title] = m;
+        }
+
+        /// <summary>
+        /// Remove the context for a conversation that is no longer visible.
+        /// </summary>
+        /// <param name="name"></param>
+        internal void RemoveConversation(string name)
+        {
+            bool change = false;
+
+            lock (conversations)
+            {
+                if (conversations.ContainsKey(name))
+                {
+                    Mode doomed = conversations[name];
+                    if (currentMode == doomed)
+                    {
+                        change = true;
+                        currentMode = chat;
+                    }
+                    if (interrupted == doomed)
+                        interrupted = chat;
+
+                    conversations.Remove(name);
+                    if (change)
+                        SelectConversation(currentMode);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Take note of a new interruption.
+        /// </summary>
+        /// <param name="m"></param>
+        internal void AddInterruption(Mode m)
+        {
+            lock (interruptions)
+            {
+                // Add to the end of the list.
+                interruptions.AddLast(m);
+
+                // If the list WAS empty, start this.
+                if (interruptions.Count == 1)
+                    StartInterruption();
+            }
+        }
+
+        internal void ChangeFocus(Mode toThis)
+        {
+            currentMode = toThis;
+             if (currentMode != null)
+                currentMode.Start();
+        }
+
+        /// <summary>
+        /// Event handler for new blue dialog boxes.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        void OnNotificationDisplayed(object sender, NotificationEventArgs e)
+        {
+            AddInterruption(new Conversation.BlueMenu(control,e));
+        }
+
+        /// <summary>
+        /// Handle Instant Messages
+        /// </summary>
+        /// <param name="im"></param>
+        /// <param name="simulator"></param>
+        void OnInstantMessage(object sender, InstantMessageEventArgs e)
+        {
+            ThreadPool.QueueUserWorkItem(sync =>
+                {
+                    Thread.Sleep(100); // Give tab a chance to show up
+                    Conversation.IMSession sess = null;
+                    string groupName;
+
+                    // All sorts of things come in as a instant messages. For actual messages
+                    // we need to match them up with an existing Conversation.  IM Conversations
+                    // are keyed by the name of the group or individual involved.
+                    switch (e.IM.Dialog)
+                    {
+                        case InstantMessageDialog.MessageFromAgent:
+                            if (control.instance.Groups.ContainsKey(e.IM.IMSessionID))
+                            {
+                                // Message from a group member
+                                groupName = control.instance.Groups[e.IM.IMSessionID].Name;
+                                sess = (IMSession)control.converse.GetConversation(groupName);
+                                if (sess != null)
+                                    sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
+                                else
+                                    Talker.Say(e.IM.FromAgentName + ", " + e.IM.Message);
+                            }
+                            else if (e.IM.BinaryBucket.Length >= 2)
+                            {
+                                // Ad-hoc friend conference
+                                sess = (IMSession)control.converse.GetConversation(Utils.BytesToString(e.IM.BinaryBucket));
+                                if (sess != null)
+                                    sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
+                                else
+                                    Talker.Say(e.IM.FromAgentName + ", " + e.IM.Message);
+                            }
+                            else if (e.IM.FromAgentName == "Second Life")
+                            {
+                                Talker.Say("Second Life says " + e.IM.Message);
+                            }
+                            else
+                            {
+                                // Message from an individual
+                                sess = (IMSession)control.converse.GetConversation(e.IM.FromAgentName);
+                                if (sess != null)
+                                    sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
+                                else
+                                    Talker.Say(e.IM.FromAgentName + ", " + e.IM.Message);
+                            }
+                            break;
+
+                        case InstantMessageDialog.SessionSend:
+                            if (control.instance.Groups.ContainsKey(e.IM.IMSessionID))
+                            {
+                                // Message from a group member
+                                groupName = control.instance.Groups[e.IM.IMSessionID].Name;
+                                sess = (IMSession)control.converse.GetConversation(groupName);
+                            }
+                            else if (e.IM.BinaryBucket.Length >= 2) // ad hoc friends conference
+                            {
+                                sess = (IMSession)control.converse.GetConversation(Utils.BytesToString(e.IM.BinaryBucket));
+                            }
+
+                            if (sess != null)
+                                sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
+                            break;
+
+                        case InstantMessageDialog.FriendshipOffered:
+                            Talker.Say(e.IM.FromAgentName + " is offering friendship.");
+                            break;
+
+                        default:
+                            break;
+                    }
+                }
+            );
+
+        }
+    }
+}