OSDN Git Service

Added option to disable speech for objects, inventory and friends
[radegast/radegast.git] / plugins / Radegast.Plugin.Speech / RadSpeech / Conversation / Control.cs
index 914d541..ad58a8c 100644 (file)
-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)
-        {
-            System.Windows.Forms.Control sTabControl = e.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)
-        {
-            System.Windows.Forms.Control sTabControl = e.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;
-                    }
-                }
-            );
-
-        }
-    }
-}
+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
+#if (COGBOT_LIBOMV || USE_STHREADS)\r
+using ThreadPoolUtil;\r
+using Thread = ThreadPoolUtil.Thread;\r
+using ThreadPool = ThreadPoolUtil.ThreadPool;\r
+using Monitor = ThreadPoolUtil.Monitor;\r
+#endif\r
+using System.Threading;\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
+            // Outgoing IMs\r
+            control.instance.Netcom.InstantMessageSent += new EventHandler<Radegast.Netcom.InstantMessageSentEventArgs>(Netcom_InstantMessageSent);\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
+            ActivateConversationFromTab(e.Tab);\r
+        }\r
+\r
+        public void ActivateConversationFromTab(RadegastTab Tab)\r
+        {\r
+            System.Windows.Forms.Control sTabControl = Tab.Control;\r
+\r
+            if (sTabControl is InventoryConsole && control.config["enabled_for_inventory"])\r
+            {\r
+                SelectConversation(inventory);\r
+            }\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 && control.config["enabled_for_friends"])\r
+            {\r
+                SelectConversation(friends);\r
+            }\r
+            else if (sTabControl is VoiceConsole)\r
+            {\r
+                SelectConversation(voice);\r
+            }\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 ConferenceIMTabWindow)\r
+            {\r
+                ConferenceIMTabWindow tab = (ConferenceIMTabWindow)sTabControl;\r
+                SelectConversation(tab.SessionName);\r
+            }\r
+            else if (sTabControl is IMTabWindow)\r
+            {\r
+                IMTabWindow tab = (IMTabWindow)sTabControl;\r
+                SelectConversation(tab.TargetName);\r
+            }\r
+            else if (sTabControl is ObjectsConsole && control.config["enabled_for_objects"])\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
+            CreateConversationFromTab(e.Tab, true);\r
+        }\r
+\r
+        public void CreateConversationFromTab(RadegastTab Tab, bool selectConversation)\r
+        {\r
+            System.Windows.Forms.Control sTabControl = Tab.Control;\r
+\r
+            Mode newConv = null;\r
+\r
+            // Create a conversation on first appearance of its tab.\r
+            if (sTabControl is InventoryConsole && control.config["enabled_for_inventory"])\r
+            {\r
+                newConv = inventory = new Closet(control);\r
+            }\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 && control.config["enabled_for_friends"])\r
+            {\r
+                newConv = friends = new Friends(control);\r
+            }\r
+            else if (sTabControl is VoiceConsole)\r
+            {\r
+                newConv = voice = new Voice(control);\r
+            }\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 ConferenceIMTabWindow)\r
+            {\r
+                ConferenceIMTabWindow tab = (ConferenceIMTabWindow)sTabControl;\r
+                AddConversation(new ConferenceIMSession(control, tab.SessionId, tab.SessionName));\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 && control.config["enabled_for_objects"])\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 (selectConversation && 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 ConferenceIMTabWindow)\r
+                RemoveConversation(((ConferenceIMTabWindow)e.Tab.Control).SessionName);\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
+            if (chat != null)\r
+            {\r
+                chat.Dispose();\r
+                chat = null;\r
+            }\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
+            // Outgoing IMs\r
+            control.instance.Netcom.InstantMessageSent -= new EventHandler<Radegast.Netcom.InstantMessageSentEventArgs>(Netcom_InstantMessageSent);\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
+                Logger.Log("Trying to start non-existant conversation", Helpers.LogLevel.Warning);\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
+        /// Event handler for outgoing IMs\r
+        /// </summary>\r
+        /// <param name="sender"></param>\r
+        /// <param name="e"></param>\r
+        void Netcom_InstantMessageSent(object sender, Radegast.Netcom.InstantMessageSentEventArgs e)\r
+        {\r
+            // Message to an individual\r
+            Conversation.IMSession sess = (IMSession)control.converse.GetConversation(control.instance.Names.Get(e.TargetID, true));\r
+            if (sess != null)\r
+                sess.OnMessage(Client.Self.AgentID, Client.Self.Name, e.Message);\r
+        }\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
+            WorkPool.QueueUserWorkItem(sync =>\r
+                {\r
+                    Thread.Sleep(100); // Give tab a chance to show up\r
+                    Conversation.IMSession sess = null;\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
+                                else\r
+                                    Talker.Say(e.IM.FromAgentName + ", " + e.IM.Message);\r
+                            }\r
+                            else if (e.IM.BinaryBucket.Length >= 2)\r
+                            {\r
+                                // Ad-hoc friend conference\r
+                                sess = (IMSession)control.converse.GetConversation(Utils.BytesToString(e.IM.BinaryBucket));\r
+                                if (sess != null)\r
+                                    sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);\r
+                                else\r
+                                    Talker.Say(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
+                                else\r
+                                    Talker.Say(e.IM.FromAgentName + ", " + e.IM.Message);\r
+                            }\r
+                            break;\r
+\r
+                        case InstantMessageDialog.SessionSend:\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
+                            }\r
+                            else if (e.IM.BinaryBucket.Length >= 2) // ad hoc friends conference\r
+                            {\r
+                                sess = (IMSession)control.converse.GetConversation(Utils.BytesToString(e.IM.BinaryBucket));\r
+                            }\r
+\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
+}\r