2 using System.Collections.Generic;
7 using System.Text.RegularExpressions;
9 using System.Windows.Forms;
11 namespace RadegastSpeech.Conversation
14 /// Manages all conversations
16 internal class Control : AreaControl
19 /// Conversations correspond to tabbed panels on the main window.
21 private Dictionary<string, Mode> conversations;
23 /// Interruptions are short-lived conversations about dialog boxes, etc
25 private LinkedList<Mode> interruptions;
27 // The permanent conversations.
28 private Conversation.Chat chat;
29 private Conversation.Closet inventory;
30 private Conversation.Friends friends;
31 private Conversation.Voice voice;
32 private Conversation.Surroundings surroundings;
33 private Mode currentMode;
34 private Mode interrupted;
35 internal string LoginName;
36 private bool firstTime = true;
37 private const string CONVGRAMMAR = "conv";
39 internal Control(PluginControl pc)
42 // Initialize the index to conversations and the list of pending interruptions.
43 interruptions = new LinkedList<Mode>();
44 conversations = new Dictionary<string,Mode>();
47 internal override void Start()
53 control.listener.CreateGrammar(
55 new string[] { "talk to Max",
63 // Automatically handle notifications (blue dialogs)
64 Notification.OnNotificationDisplayed +=
65 new Notification.NotificationCallback(OnNotificationDisplayed);
66 // Notification.OnNotificationClosed +=
67 // new Notification.NotificationCallback(OnNotificationClosed);
69 // Announce connect and disconnect.
70 control.instance.Netcom.ClientConnected +=
71 new EventHandler<EventArgs>(Network_ClientConnected);
72 control.instance.Netcom.ClientDisconnected +=
73 new EventHandler<DisconnectedEventArgs>(Network_Disconnected);
75 control.instance.Netcom.ClientLoginStatus +=
76 new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
78 // Notice arrival in a new sim
79 control.instance.Client.Network.SimChanged +=
80 new EventHandler<SimChangedEventArgs>(Network_SimChanged);
82 control.instance.Netcom.ClientLoggingIn +=
83 new EventHandler<Radegast.Netcom.OverrideEventArgs>(Netcom_ClientLoggingIn);
84 // Watch the coming and going of main window tabs.
85 control.instance.TabConsole.OnTabAdded +=
86 new TabsConsole.TabCallback(TabConsole_OnTabAdded);
87 control.instance.TabConsole.OnTabRemoved +=
88 new TabsConsole.TabCallback(TabConsole_OnTabRemoved);
90 // Notice when the active tab changes on the graphics user interface.
91 control.instance.TabConsole.OnTabSelected +=
92 new TabsConsole.TabCallback(OnTabChange);
94 // Handle Instant Messages too
95 control.instance.Client.Self.IM +=
96 new EventHandler<InstantMessageEventArgs>(OnInstantMessage);
98 // Watch for global keys
99 control.instance.MainForm.KeyUp += new KeyEventHandler(MainForm_KeyUp);
101 // System messages in chat window
102 control.instance.TabConsole.OnChatNotification += new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);
104 control.listener.ActivateGrammar(CONVGRAMMAR);
109 /// Say various notifications that come in the chat
111 /// <param name="sender">Message sender</param>
112 /// <param name="e">Event args</param>
113 void TabConsole_OnChatNotification(object sender, ChatNotificationEventArgs e)
115 Talker.Say(e.Message);
119 /// Watch for global function keys.
121 /// <param name="sender"></param>
122 /// <param name="e"></param>
123 void MainForm_KeyUp(object sender, KeyEventArgs e)
125 // Escape clears the speak-ahead queue.
126 if (e.KeyCode == Keys.Escape)
129 Talker.SayMore("Flushed.");
135 void Netcom_ClientLoggingIn(object sender, Radegast.Netcom.OverrideEventArgs e)
137 Talker.SayMore("Logging in. Please wait.");
140 private void netcom_ClientLoginStatus(
142 LoginProgressEventArgs e)
146 case LoginStatus.ConnectingToLogin:
147 // Never seems to happen. See Netcom_ClientLoggingIn
148 Talker.SayMore("Connecting to login server");
151 case LoginStatus.ConnectingToSim:
152 Talker.SayMore("Connecting to region");
155 case LoginStatus.Success:
156 LoginName = control.instance.Netcom.LoginOptions.FullName;
157 //Talker.SayMore("Logged in as " + LoginName);
158 //if (friends != null)
159 // friends.Announce = true;
162 case LoginStatus.Failed:
163 Talker.Say(e.Message +
164 ". Press Enter twice to retry", Talk.BeepType.Bad);
173 /// Switch active conversation as tab focus moves.
175 /// <param name="sender"></param>
176 /// <param name="e"></param>
177 void OnTabChange(object sender, TabEventArgs e)
179 System.Windows.Forms.Control sTabControl = e.Tab.Control;
181 if (sTabControl is InventoryConsole)
182 SelectConversation(inventory);
183 else if (sTabControl is ChatConsole)
187 chat = new Chat(control);
188 chat.Console = sTabControl;
189 AddConversation(chat);
191 SelectConversation(chat);
193 else if (sTabControl is FriendsConsole)
194 SelectConversation(friends);
195 else if (sTabControl is VoiceConsole)
196 SelectConversation(voice);
197 else if (sTabControl is GroupIMTabWindow)
199 GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;
201 control.instance.Groups[tab.SessionId].Name);
203 else if (sTabControl is ConferenceIMTabWindow)
205 ConferenceIMTabWindow tab = (ConferenceIMTabWindow)sTabControl;
206 SelectConversation(tab.SessionName);
208 else if (sTabControl is IMTabWindow)
210 IMTabWindow tab = (IMTabWindow)sTabControl;
211 SelectConversation(tab.TargetName);
213 else if (sTabControl is ObjectsConsole)
215 SelectConversation(surroundings);
221 /// Create conversations as tabs are created.
223 /// <param name="sender"></param>
224 /// <param name="e"></param>
225 void TabConsole_OnTabAdded(object sender, TabEventArgs e)
227 System.Windows.Forms.Control sTabControl = e.Tab.Control;
231 // Create a conversation on first appearance of its tab.
232 if (sTabControl is InventoryConsole)
233 newConv = inventory = new Closet(control);
234 else if (sTabControl is ChatConsole)
236 if (chat != null) return;
237 newConv = chat = new Chat(control);
239 else if (sTabControl is FriendsConsole)
240 newConv = friends = new Friends(control);
241 else if (sTabControl is VoiceConsole)
242 newConv = voice = new Voice(control);
243 else if (sTabControl is GroupIMTabWindow)
245 GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;
246 AddConversation(new GroupIMSession(control, tab.SessionId));
249 else if (sTabControl is ConferenceIMTabWindow)
251 ConferenceIMTabWindow tab = (ConferenceIMTabWindow)sTabControl;
252 AddConversation(new ConferenceIMSession(control, tab.SessionId, tab.SessionName));
255 else if (sTabControl is IMTabWindow)
257 IMTabWindow tab = (IMTabWindow)sTabControl;
258 AddConversation(new SingleIMSession(control, tab.TargetName, tab.TargetId, tab.SessionId));
261 else if (sTabControl is ObjectsConsole)
263 surroundings = new Surroundings(control);
264 AddConversation(surroundings);
267 // If a conversation was created, switch to it.
270 AddConversation(newConv);
271 // Select CHAT as soon as it is created.
272 if (sTabControl is ChatConsole)
273 SelectConversation(newConv);
278 /// Quietly close conversations.
280 /// <param name="sender"></param>
281 /// <param name="e"></param>
282 void TabConsole_OnTabRemoved(object sender, TabEventArgs e)
284 System.Windows.Forms.Control sTabControl = e.Tab.Control;
285 if (sTabControl is InventoryConsole)
286 RemoveConversation( inventory.Title );
287 else if (sTabControl is ChatConsole)
288 RemoveConversation(chat.Title);
289 else if (sTabControl is FriendsConsole)
290 RemoveConversation(friends.Title);
291 else if (sTabControl is VoiceConsole)
292 RemoveConversation(voice.Title);
293 else if (sTabControl is GroupIMTabWindow ||
294 sTabControl is IMTabWindow)
295 RemoveConversation(sTabControl.Name); // TODO wrong name
299 internal override void Shutdown()
301 // Automatically handle notifications (blue dialogs)
302 Notification.OnNotificationDisplayed -=
303 new Notification.NotificationCallback(OnNotificationDisplayed);
305 // Announce connect and disconnect.
306 control.instance.Netcom.ClientConnected -=
307 new EventHandler<EventArgs>(Network_ClientConnected);
308 control.instance.Netcom.ClientDisconnected -=
309 new EventHandler<DisconnectedEventArgs>(Network_Disconnected);
311 control.instance.Netcom.ClientLoginStatus -=
312 new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
314 // Notice arrival in a new sim
315 control.instance.Client.Network.SimChanged -=
316 new EventHandler<SimChangedEventArgs>(Network_SimChanged);
318 control.instance.Netcom.ClientLoggingIn -=
319 new EventHandler<Radegast.Netcom.OverrideEventArgs>(Netcom_ClientLoggingIn);
320 // Watch the coming and going of main window tabs.
321 control.instance.TabConsole.OnTabAdded -=
322 new TabsConsole.TabCallback(TabConsole_OnTabAdded);
323 control.instance.TabConsole.OnTabRemoved -=
324 new TabsConsole.TabCallback(TabConsole_OnTabRemoved);
326 // Notice when the active tab changes on the graphics user interface.
327 control.instance.TabConsole.OnTabSelected -=
328 new TabsConsole.TabCallback(OnTabChange);
330 // Handle Instant Messages too
331 control.instance.Client.Self.IM +=
332 new EventHandler<InstantMessageEventArgs>(OnInstantMessage);
334 // System notifications in chat
335 control.instance.TabConsole.OnChatNotification -= new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);
337 control.listener.DeactivateGrammar(CONVGRAMMAR);
339 foreach (Mode m in conversations.Values)
343 foreach (Mode m in interruptions)
347 conversations.Clear();
348 interruptions.Clear();
355 internal bool amCurrent(Mode m)
357 return (currentMode == m);
361 /// Start an interrupting conversation.
363 void StartInterruption()
365 // Remember what we were talking about.
366 if (interrupted == null)
367 interrupted = currentMode;
369 // Visually they stack up, so we take the last one first.
370 currentMode = interruptions.Last();
375 /// Finish an interruption and resume normal conversation
377 internal void FinishInterruption( Mode m )
381 // Remove the terminating interruption from the list.
382 interruptions.Remove(m);
384 // Let it remove any event hooks, etc
387 // If there are any other interruptions pending, start one.
388 // Otherwise resume the interrupted conversation.
389 if (interruptions.Count > 0)
393 currentMode = interrupted;
400 private void Network_ClientConnected(object sender, EventArgs e)
402 Talker.Say("You are connected.", Talk.BeepType.Good);
405 chat = new Chat(control);
407 AddConversation(chat);
408 SelectConversation(chat);
412 void Network_SimChanged(object sender, SimChangedEventArgs e)
414 Talker.Say("You are now in " +
415 control.instance.Client.Network.CurrentSim.Name,
421 /// Announce reason for disconnect.
423 /// <param name="reason"></param>
424 /// <param name="message"></param>
425 private void Network_Disconnected(object sender, DisconnectedEventArgs e)
429 case NetworkManager.DisconnectType.ClientInitiated:
430 Talker.Say("You are disconnected.");
432 case NetworkManager.DisconnectType.SimShutdown:
433 Talker.Say("The region you were in has been shut down. You are disconnected.",
436 case NetworkManager.DisconnectType.NetworkTimeout:
437 Talker.Say("You have been disconnected by a network timeout.",
440 case NetworkManager.DisconnectType.ServerInitiated:
441 Talker.Say("The server has disconnected you. " + e.Message,
446 private void ListFriends()
448 List<FriendInfo> onlineFriends =
449 control.instance.Client.Friends.FriendList.FindAll(delegate(FriendInfo f) { return f.IsOnline; });
451 foreach (FriendInfo f in onlineFriends)
453 list += f.Name + ", ";
455 list += "are online.";
461 /// Check for general commands
463 /// <param name="message"></param>
464 /// <returns>true if command recognized</returns>
465 private bool Command(string message)
467 switch (message.ToLower())
470 AddInterruption(new Max(control));
472 case "who is online":
475 case "open the closet":
476 control.instance.TabConsole.SelectTab("inventory");
477 SelectConversation(inventory);
480 control.instance.TabConsole.SelectTab("friends");
481 SelectConversation(friends);
485 Talker.SayMore("Flushed.");
488 control.instance.TabConsole.SelectTab("chat");
489 SelectConversation(chat);
492 control.instance.TabConsole.SelectTab("voice");
493 SelectConversation(voice);
502 /// Dispatch recognized text to appropriate conversation.
504 /// <param name="message"></param>
505 internal void Hear(string message)
508 if (Command(message)) return;
510 // Let the current conversation handle it.
511 if (currentMode != null)
512 currentMode.Hear(message);
515 internal void SelectConversation(Mode c)
519 Talker.Say("Trying to start non-existant conversation", Talk.BeepType.Bad );
522 // Avoid multiple starts.
523 if (currentMode == c) return;
525 // Let the old conversation deactivate any event hooks, grammars, etc.
526 if (currentMode != null)
533 internal void SelectConversation(string name)
535 if (conversations.ContainsKey(name))
537 SelectConversation(conversations[name]);
541 Talker.Say("Can not find conversation " + name, Talk.BeepType.Bad);
546 /// Find an existing conversation by name.
548 /// <param name="title"></param>
549 /// <returns></returns>
550 /// <remarks>Used for IM sessions.</remarks>
551 internal Mode GetConversation(string title)
553 if (conversations.ContainsKey(title))
554 return conversations[title];
560 /// Add a conversation context to those we are tracking.
562 /// <param name="m"></param>
563 internal void AddConversation(Mode m)
565 if (!conversations.ContainsKey(m.Title))
566 conversations[m.Title] = m;
570 /// Remove the context for a conversation that is no longer visible.
572 /// <param name="name"></param>
573 internal void RemoveConversation(string name)
579 if (conversations.ContainsKey(name))
581 Mode doomed = conversations[name];
582 if (currentMode == doomed)
587 if (interrupted == doomed)
590 conversations.Remove(name);
592 SelectConversation(currentMode);
598 /// Take note of a new interruption.
600 /// <param name="m"></param>
601 internal void AddInterruption(Mode m)
605 // Add to the end of the list.
606 interruptions.AddLast(m);
608 // If the list WAS empty, start this.
609 if (interruptions.Count == 1)
614 internal void ChangeFocus(Mode toThis)
616 currentMode = toThis;
617 if (currentMode != null)
623 /// Event handler for new blue dialog boxes.
625 /// <param name="sender"></param>
626 /// <param name="e"></param>
627 void OnNotificationDisplayed(object sender, NotificationEventArgs e)
629 AddInterruption(new Conversation.BlueMenu(control,e));
633 /// Handle Instant Messages
635 /// <param name="im"></param>
636 /// <param name="simulator"></param>
637 void OnInstantMessage(object sender, InstantMessageEventArgs e)
639 Conversation.IMSession sess = null;
642 // All sorts of things come in as a instant messages. For actual messages
643 // we need to match them up with an existing Conversation. IM Conversations
644 // are keyed by the name of the group or individual involved.
647 case InstantMessageDialog.MessageFromAgent:
648 if (control.instance.Groups.ContainsKey(e.IM.IMSessionID))
650 // Message from a group member
651 groupName = control.instance.Groups[e.IM.IMSessionID].Name;
652 sess = (IMSession)control.converse.GetConversation(groupName);
654 sess.OnMessage( e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message );
656 else if (e.IM.BinaryBucket.Length >= 2)
658 // Ad-hoc friend conference
659 sess = (IMSession)control.converse.GetConversation(Utils.BytesToString(e.IM.BinaryBucket));
661 sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
663 else if (e.IM.FromAgentName == "Second Life")
665 Talker.Say("Second Life says "+ e.IM.Message);
669 // Message from an individual
670 sess = (IMSession)control.converse.GetConversation(e.IM.FromAgentName);
672 sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
676 case InstantMessageDialog.SessionSend:
677 if (control.instance.Groups.ContainsKey(e.IM.IMSessionID))
679 // Message from a group member
680 groupName = control.instance.Groups[e.IM.IMSessionID].Name;
681 sess = (IMSession)control.converse.GetConversation(groupName);
683 else if (e.IM.BinaryBucket.Length >= 2) // ad hoc friends conference
685 sess = (IMSession)control.converse.GetConversation(Utils.BytesToString(e.IM.BinaryBucket));
689 sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
692 case InstantMessageDialog.FriendshipOffered:
693 Talker.Say(e.IM.FromAgentName + " is offering friendship.");