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 IMTabWindow)
205 IMTabWindow tab = (IMTabWindow)sTabControl;
206 SelectConversation(tab.TargetName);
208 else if (sTabControl is ObjectsConsole)
210 SelectConversation(surroundings);
216 /// Create conversations as tabs are created.
218 /// <param name="sender"></param>
219 /// <param name="e"></param>
220 void TabConsole_OnTabAdded(object sender, TabEventArgs e)
222 System.Windows.Forms.Control sTabControl = e.Tab.Control;
226 // Create a conversation on first appearance of its tab.
227 if (sTabControl is InventoryConsole)
228 newConv = inventory = new Closet(control);
229 else if (sTabControl is ChatConsole)
231 if (chat != null) return;
232 newConv = chat = new Chat(control);
234 else if (sTabControl is FriendsConsole)
235 newConv = friends = new Friends(control);
236 else if (sTabControl is VoiceConsole)
237 newConv = voice = new Voice(control);
238 else if (sTabControl is GroupIMTabWindow)
240 GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;
241 AddConversation(new GroupIMSession(control, tab.SessionId));
244 else if (sTabControl is IMTabWindow)
246 IMTabWindow tab = (IMTabWindow)sTabControl;
247 AddConversation(new SingleIMSession(control, tab.TargetName, tab.TargetId, tab.SessionId));
250 else if (sTabControl is ObjectsConsole)
252 surroundings = new Surroundings( control );
253 AddConversation( surroundings );
256 // If a conversation was created, switch to it.
259 AddConversation(newConv);
260 // Select CHAT as soon as it is created.
261 if (sTabControl is ChatConsole)
262 SelectConversation(newConv);
267 /// Quietly close conversations.
269 /// <param name="sender"></param>
270 /// <param name="e"></param>
271 void TabConsole_OnTabRemoved(object sender, TabEventArgs e)
273 System.Windows.Forms.Control sTabControl = e.Tab.Control;
274 if (sTabControl is InventoryConsole)
275 RemoveConversation( inventory.Title );
276 else if (sTabControl is ChatConsole)
277 RemoveConversation(chat.Title);
278 else if (sTabControl is FriendsConsole)
279 RemoveConversation(friends.Title);
280 else if (sTabControl is VoiceConsole)
281 RemoveConversation(voice.Title);
282 else if (sTabControl is GroupIMTabWindow ||
283 sTabControl is IMTabWindow)
284 RemoveConversation(sTabControl.Name); // TODO wrong name
288 internal override void Shutdown()
290 // Automatically handle notifications (blue dialogs)
291 Notification.OnNotificationDisplayed -=
292 new Notification.NotificationCallback(OnNotificationDisplayed);
294 // Announce connect and disconnect.
295 control.instance.Netcom.ClientConnected -=
296 new EventHandler<EventArgs>(Network_ClientConnected);
297 control.instance.Netcom.ClientDisconnected -=
298 new EventHandler<DisconnectedEventArgs>(Network_Disconnected);
300 control.instance.Netcom.ClientLoginStatus -=
301 new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
303 // Notice arrival in a new sim
304 control.instance.Client.Network.SimChanged -=
305 new EventHandler<SimChangedEventArgs>(Network_SimChanged);
307 control.instance.Netcom.ClientLoggingIn -=
308 new EventHandler<Radegast.Netcom.OverrideEventArgs>(Netcom_ClientLoggingIn);
309 // Watch the coming and going of main window tabs.
310 control.instance.TabConsole.OnTabAdded -=
311 new TabsConsole.TabCallback(TabConsole_OnTabAdded);
312 control.instance.TabConsole.OnTabRemoved -=
313 new TabsConsole.TabCallback(TabConsole_OnTabRemoved);
315 // Notice when the active tab changes on the graphics user interface.
316 control.instance.TabConsole.OnTabSelected -=
317 new TabsConsole.TabCallback(OnTabChange);
319 // Handle Instant Messages too
320 control.instance.Client.Self.IM +=
321 new EventHandler<InstantMessageEventArgs>(OnInstantMessage);
323 // System notifications in chat
324 control.instance.TabConsole.OnChatNotification -= new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);
326 control.listener.DeactivateGrammar(CONVGRAMMAR);
328 foreach (Mode m in conversations.Values)
332 foreach (Mode m in interruptions)
336 conversations.Clear();
337 interruptions.Clear();
344 internal bool amCurrent(Mode m)
346 return (currentMode == m);
350 /// Start an interrupting conversation.
352 void StartInterruption()
354 // Remember what we were talking about.
355 if (interrupted == null)
356 interrupted = currentMode;
358 // Visually they stack up, so we take the last one first.
359 currentMode = interruptions.Last();
364 /// Finish an interruption and resume normal conversation
366 internal void FinishInterruption( Mode m )
370 // Remove the terminating interruption from the list.
371 interruptions.Remove(m);
373 // Let it remove any event hooks, etc
376 // If there are any other interruptions pending, start one.
377 // Otherwise resume the interrupted conversation.
378 if (interruptions.Count > 0)
382 currentMode = interrupted;
389 private void Network_ClientConnected(object sender, EventArgs e)
391 Talker.Say("You are connected.", Talk.BeepType.Good);
394 chat = new Chat(control);
396 AddConversation(chat);
397 SelectConversation(chat);
401 void Network_SimChanged(object sender, SimChangedEventArgs e)
403 Talker.Say("You are now in " +
404 control.instance.Client.Network.CurrentSim.Name,
410 /// Announce reason for disconnect.
412 /// <param name="reason"></param>
413 /// <param name="message"></param>
414 private void Network_Disconnected(object sender, DisconnectedEventArgs e)
418 case NetworkManager.DisconnectType.ClientInitiated:
419 Talker.Say("You are disconnected.");
421 case NetworkManager.DisconnectType.SimShutdown:
422 Talker.Say("The region you were in has been shut down. You are disconnected.",
425 case NetworkManager.DisconnectType.NetworkTimeout:
426 Talker.Say("You have been disconnected by a network timeout.",
429 case NetworkManager.DisconnectType.ServerInitiated:
430 Talker.Say("The server has disconnected you. " + e.Message,
435 private void ListFriends()
437 List<FriendInfo> onlineFriends =
438 control.instance.Client.Friends.FriendList.FindAll(delegate(FriendInfo f) { return f.IsOnline; });
440 foreach (FriendInfo f in onlineFriends)
442 list += f.Name + ", ";
444 list += "are online.";
450 /// Check for general commands
452 /// <param name="message"></param>
453 /// <returns>true if command recognized</returns>
454 private bool Command(string message)
456 switch (message.ToLower())
459 AddInterruption(new Max(control));
461 case "who is online":
464 case "open the closet":
465 control.instance.TabConsole.SelectTab("inventory");
466 SelectConversation(inventory);
469 control.instance.TabConsole.SelectTab("friends");
470 SelectConversation(friends);
474 Talker.SayMore("Flushed.");
477 control.instance.TabConsole.SelectTab("chat");
478 SelectConversation(chat);
481 control.instance.TabConsole.SelectTab("voice");
482 SelectConversation(voice);
491 /// Dispatch recognized text to appropriate conversation.
493 /// <param name="message"></param>
494 internal void Hear(string message)
497 if (Command(message)) return;
499 // Let the current conversation handle it.
500 if (currentMode != null)
501 currentMode.Hear(message);
504 internal void SelectConversation(Mode c)
508 Talker.Say("Trying to start non-existant conversation", Talk.BeepType.Bad );
511 // Avoid multiple starts.
512 if (currentMode == c) return;
514 // Let the old conversation deactivate any event hooks, grammars, etc.
515 if (currentMode != null)
522 internal void SelectConversation(string name)
524 if (conversations.ContainsKey(name))
526 SelectConversation(conversations[name]);
530 Talker.Say("Can not find conversation " + name, Talk.BeepType.Bad);
535 /// Find an existing conversation by name.
537 /// <param name="title"></param>
538 /// <returns></returns>
539 /// <remarks>Used for IM sessions.</remarks>
540 internal Mode GetConversation(string title)
542 if (conversations.ContainsKey(title))
543 return conversations[title];
549 /// Add a conversation context to those we are tracking.
551 /// <param name="m"></param>
552 internal void AddConversation(Mode m)
554 if (!conversations.ContainsKey(m.Title))
555 conversations[m.Title] = m;
559 /// Remove the context for a conversation that is no longer visible.
561 /// <param name="name"></param>
562 internal void RemoveConversation(string name)
568 if (conversations.ContainsKey(name))
570 Mode doomed = conversations[name];
571 if (currentMode == doomed)
576 if (interrupted == doomed)
579 conversations.Remove(name);
581 SelectConversation(currentMode);
587 /// Take note of a new interruption.
589 /// <param name="m"></param>
590 internal void AddInterruption(Mode m)
594 // Add to the end of the list.
595 interruptions.AddLast(m);
597 // If the list WAS empty, start this.
598 if (interruptions.Count == 1)
603 internal void ChangeFocus(Mode toThis)
605 currentMode = toThis;
606 if (currentMode != null)
612 /// Event handler for new blue dialog boxes.
614 /// <param name="sender"></param>
615 /// <param name="e"></param>
616 void OnNotificationDisplayed(object sender, NotificationEventArgs e)
618 AddInterruption(new Conversation.BlueMenu(control,e));
622 /// Handle Instant Messages
624 /// <param name="im"></param>
625 /// <param name="simulator"></param>
626 void OnInstantMessage(object sender, InstantMessageEventArgs e)
628 Conversation.IMSession sess;
631 // All sorts of things come in as a instant messages. For actual messages
632 // we need to match them up with an existing Conversation. IM Conversations
633 // are keyed by the name of the group or individual involved.
636 case InstantMessageDialog.MessageFromAgent:
637 if (control.instance.Groups.ContainsKey(e.IM.IMSessionID))
639 // Message from a group member
640 groupName = control.instance.Groups[e.IM.IMSessionID].Name;
641 sess = (IMSession)control.converse.GetConversation(groupName);
643 sess.OnMessage( e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message );
645 else if (e.IM.BinaryBucket.Length >= 2)
647 // Ad-hoc friend conference
648 // TODO this is probably the wrong name
649 sess = (IMSession)control.converse.GetConversation(e.IM.FromAgentName);
651 sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
653 else if (e.IM.FromAgentName == "Second Life")
655 Talker.Say("Second Life says "+ e.IM.Message);
659 // Message from an individual
660 sess = (IMSession)control.converse.GetConversation(e.IM.FromAgentName);
662 sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
666 case InstantMessageDialog.SessionSend:
667 // Message from a group member
668 groupName = control.instance.Groups[e.IM.IMSessionID].Name;
669 sess = (IMSession)control.converse.GetConversation(groupName);
671 sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
674 case InstantMessageDialog.FriendshipOffered:
675 Talker.Say(e.IM.FromAgentName + " is offering friendship.");