OSDN Git Service

RAD-124: Added Ability to read Friends Conference with speech plugin
[radegast/radegast.git] / plugins / Radegast.Plugin.Speech / RadSpeech / Conversation / Control.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using OpenMetaverse;
6 using Radegast;
7 using System.Text.RegularExpressions;
8 using System.Net;
9 using System.Windows.Forms;
10
11 namespace RadegastSpeech.Conversation
12 {
13     /// <summary>
14     /// Manages all conversations
15     /// </summary>
16     internal class Control : AreaControl
17     {
18         /// <summary>
19         /// Conversations correspond to tabbed panels on the main window.
20         /// </summary>
21         private Dictionary<string, Mode> conversations;
22         /// <summary>
23         /// Interruptions are short-lived conversations about dialog boxes, etc
24         /// </summary>
25         private LinkedList<Mode> interruptions;
26
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";
38
39         internal Control(PluginControl pc)
40             : base(pc)
41         {
42             // Initialize the index to conversations and the list of pending interruptions.
43             interruptions = new LinkedList<Mode>();
44             conversations = new Dictionary<string,Mode>();
45         }
46
47         internal override void Start()
48         {
49             if (firstTime)
50             {
51                 firstTime = false;
52
53                 control.listener.CreateGrammar(
54                     CONVGRAMMAR,
55                     new string[] { "talk to Max",
56                     "skip",
57                     "who is online",
58                     "open the closet",
59                     "friends",
60                     "talk" });
61             }
62
63             // Automatically handle notifications (blue dialogs)
64             Notification.OnNotificationDisplayed +=
65                 new Notification.NotificationCallback(OnNotificationDisplayed);
66 //            Notification.OnNotificationClosed +=
67 //                new Notification.NotificationCallback(OnNotificationClosed);
68
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);
74
75             control.instance.Netcom.ClientLoginStatus +=
76                 new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
77
78             // Notice arrival in a new sim
79             control.instance.Client.Network.SimChanged +=
80                 new EventHandler<SimChangedEventArgs>(Network_SimChanged);
81
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);
89
90             // Notice when the active tab changes on the graphics user interface.
91             control.instance.TabConsole.OnTabSelected +=
92                 new TabsConsole.TabCallback(OnTabChange);
93
94             // Handle Instant Messages too
95             control.instance.Client.Self.IM +=
96                 new EventHandler<InstantMessageEventArgs>(OnInstantMessage);
97
98             // Watch for global keys
99             control.instance.MainForm.KeyUp += new KeyEventHandler(MainForm_KeyUp);
100
101             // System messages in chat window
102             control.instance.TabConsole.OnChatNotification += new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);
103
104             control.listener.ActivateGrammar(CONVGRAMMAR);
105
106         }
107
108         /// <summary>
109         /// Say various notifications that come in the chat
110         /// </summary>
111         /// <param name="sender">Message sender</param>
112         /// <param name="e">Event args</param>
113         void TabConsole_OnChatNotification(object sender, ChatNotificationEventArgs e)
114         {
115             Talker.Say(e.Message);
116         }
117
118         /// <summary>
119         /// Watch for global function keys.
120         /// </summary>
121         /// <param name="sender"></param>
122         /// <param name="e"></param>
123         void MainForm_KeyUp(object sender, KeyEventArgs e)
124         {
125             // Escape clears the speak-ahead queue.
126             if (e.KeyCode == Keys.Escape)
127             {
128                 Talker.Flush();
129                 Talker.SayMore("Flushed.");
130                 e.Handled = true;
131             }
132         }
133
134
135         void Netcom_ClientLoggingIn(object sender, Radegast.Netcom.OverrideEventArgs e)
136         {
137             Talker.SayMore("Logging in.  Please wait.");
138         }
139
140         private void netcom_ClientLoginStatus(
141             object sender,
142             LoginProgressEventArgs e)
143         {
144             switch (e.Status)
145             {
146                 case LoginStatus.ConnectingToLogin:
147                     // Never seems to happen.  See Netcom_ClientLoggingIn
148                     Talker.SayMore("Connecting to login server");
149                     return;
150
151                 case LoginStatus.ConnectingToSim:
152                     Talker.SayMore("Connecting to region");
153                     return;
154
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;
160                     return;
161
162                 case LoginStatus.Failed:
163                     Talker.Say(e.Message +
164                         ". Press Enter twice to retry", Talk.BeepType.Bad);
165                     return;
166
167                 default:
168                     return;
169             }
170         }
171
172         /// <summary>
173         /// Switch active conversation as tab focus moves.
174         /// </summary>
175         /// <param name="sender"></param>
176         /// <param name="e"></param>
177         void OnTabChange(object sender, TabEventArgs e)
178         {
179             System.Windows.Forms.Control sTabControl = e.Tab.Control;
180
181             if (sTabControl is InventoryConsole)
182                 SelectConversation(inventory);
183             else if (sTabControl is ChatConsole)
184             {
185                 if (chat == null)
186                 {
187                     chat = new Chat(control);
188                     chat.Console = sTabControl;
189                     AddConversation(chat);
190                 }
191                 SelectConversation(chat);
192             }
193             else if (sTabControl is FriendsConsole)
194                 SelectConversation(friends);
195             else if (sTabControl is VoiceConsole)
196                 SelectConversation(voice);
197             else if (sTabControl is GroupIMTabWindow)
198             {
199                 GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;
200                 SelectConversation(
201                     control.instance.Groups[tab.SessionId].Name);
202             }
203             else if (sTabControl is ConferenceIMTabWindow)
204             {
205                 ConferenceIMTabWindow tab = (ConferenceIMTabWindow)sTabControl;
206                 SelectConversation(tab.SessionName);
207             }
208             else if (sTabControl is IMTabWindow)
209             {
210                 IMTabWindow tab = (IMTabWindow)sTabControl;
211                 SelectConversation(tab.TargetName);
212             }
213             else if (sTabControl is ObjectsConsole)
214             {
215                 SelectConversation(surroundings);
216             }
217
218         }
219
220         /// <summary>
221         /// Create conversations as tabs are created.
222         /// </summary>
223         /// <param name="sender"></param>
224         /// <param name="e"></param>
225         void TabConsole_OnTabAdded(object sender, TabEventArgs e)
226         {
227             System.Windows.Forms.Control sTabControl = e.Tab.Control;
228
229             Mode newConv = null;
230
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)
235             {
236                 if (chat != null) return;
237                 newConv = chat = new Chat(control);
238             }
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)
244             {
245                 GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;
246                 AddConversation(new GroupIMSession(control, tab.SessionId));
247                 return;
248             }
249             else if (sTabControl is ConferenceIMTabWindow)
250             {
251                 ConferenceIMTabWindow tab = (ConferenceIMTabWindow)sTabControl;
252                 AddConversation(new ConferenceIMSession(control, tab.SessionId, tab.SessionName));
253                 return;
254             }
255             else if (sTabControl is IMTabWindow)
256             {
257                 IMTabWindow tab = (IMTabWindow)sTabControl;
258                 AddConversation(new SingleIMSession(control, tab.TargetName, tab.TargetId, tab.SessionId));
259                 return;
260             }
261             else if (sTabControl is ObjectsConsole)
262             {
263                 surroundings = new Surroundings(control);
264                 AddConversation(surroundings);
265             }
266
267             // If a conversation was created, switch to it.
268             if (newConv != null)
269             {
270                 AddConversation(newConv);
271                 // Select CHAT as soon as it is created.
272                 if (sTabControl is ChatConsole)
273                     SelectConversation(newConv);
274             }
275         }
276
277         /// <summary>
278         /// Quietly close conversations.
279         /// </summary>
280         /// <param name="sender"></param>
281         /// <param name="e"></param>
282         void TabConsole_OnTabRemoved(object sender, TabEventArgs e)
283         {
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
296         }
297
298
299         internal override void Shutdown()
300         {
301             // Automatically handle notifications (blue dialogs)
302             Notification.OnNotificationDisplayed -=
303                 new Notification.NotificationCallback(OnNotificationDisplayed);
304
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);
310
311             control.instance.Netcom.ClientLoginStatus -=
312                 new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
313
314             // Notice arrival in a new sim
315             control.instance.Client.Network.SimChanged -=
316                 new EventHandler<SimChangedEventArgs>(Network_SimChanged);
317
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);
325
326             // Notice when the active tab changes on the graphics user interface.
327             control.instance.TabConsole.OnTabSelected -=
328                 new TabsConsole.TabCallback(OnTabChange);
329
330             // Handle Instant Messages too
331             control.instance.Client.Self.IM +=
332                 new EventHandler<InstantMessageEventArgs>(OnInstantMessage);
333
334             // System notifications in chat
335             control.instance.TabConsole.OnChatNotification -= new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);
336
337             control.listener.DeactivateGrammar(CONVGRAMMAR);
338
339             foreach (Mode m in conversations.Values)
340             {
341                 m.Stop();
342             }
343             foreach (Mode m in interruptions)
344             {
345                 m.Stop();
346             }
347             conversations.Clear();
348             interruptions.Clear();
349         }
350
351         void WatchKeys()
352         {
353         }
354
355         internal bool amCurrent(Mode m)
356         {
357             return (currentMode == m);
358         }
359
360         /// <summary>
361         /// Start an interrupting conversation.
362         /// </summary>
363         void StartInterruption()
364         {
365             // Remember what we were talking about.
366             if (interrupted == null)
367                 interrupted = currentMode;
368
369             // Visually they stack up, so we take the last one first.
370             currentMode = interruptions.Last();
371             currentMode.Start();
372         }
373
374         /// <summary>
375         /// Finish an interruption and resume normal conversation
376         /// </summary>
377         internal void FinishInterruption( Mode m )
378         {
379             lock (interruptions)
380             {
381                 // Remove the terminating interruption from the list.
382                 interruptions.Remove(m);
383
384                 // Let it remove any event hooks, etc
385                 m.Stop();
386
387                 // If there are any other interruptions pending, start one.
388                 // Otherwise resume the interrupted conversation.
389                 if (interruptions.Count > 0)
390                     StartInterruption();
391                 else
392                 {
393                     currentMode = interrupted;
394                     interrupted = null;
395                     currentMode.Start();
396                 }
397             }
398         }
399  
400         private void Network_ClientConnected(object sender, EventArgs e)
401         {
402             Talker.Say("You are connected.", Talk.BeepType.Good);
403             if (chat == null)
404             {
405                 chat = new Chat(control);
406
407                 AddConversation(chat);
408                 SelectConversation(chat);
409             }
410         }
411
412         void Network_SimChanged(object sender, SimChangedEventArgs e)
413         {
414             Talker.Say("You are now in " +
415                 control.instance.Client.Network.CurrentSim.Name,
416                 Talk.BeepType.Good);
417         }
418
419
420         /// <summary>
421         /// Announce reason for disconnect.
422         /// </summary>
423         /// <param name="reason"></param>
424         /// <param name="message"></param>
425         private void Network_Disconnected(object sender, DisconnectedEventArgs e)
426         {
427             switch (e.Reason)
428             {
429                 case NetworkManager.DisconnectType.ClientInitiated:
430                     Talker.Say("You are disconnected.");
431                     break;
432                 case NetworkManager.DisconnectType.SimShutdown:
433                     Talker.Say("The region you were in has been shut down.  You are disconnected.",
434                         Talk.BeepType.Bad);
435                     break;
436                 case NetworkManager.DisconnectType.NetworkTimeout:
437                     Talker.Say("You have been disconnected by a network timeout.",
438                         Talk.BeepType.Bad);
439                     break;
440                 case NetworkManager.DisconnectType.ServerInitiated:
441                     Talker.Say("The server has disconnected you.  " + e.Message,
442                         Talk.BeepType.Bad);
443                     break;
444             }
445         }
446         private void ListFriends()
447         {
448             List<FriendInfo> onlineFriends =
449                 control.instance.Client.Friends.FriendList.FindAll(delegate(FriendInfo f) { return f.IsOnline; });
450             string list = "";
451             foreach (FriendInfo f in onlineFriends)
452             {
453                 list += f.Name + ", ";
454             }
455             list += "are online.";
456             Talker.Say(list);
457         }
458
459
460         /// <summary>
461         /// Check for general commands
462         /// </summary>
463         /// <param name="message"></param>
464         /// <returns>true if command recognized</returns>
465         private bool Command(string message)
466         {
467             switch (message.ToLower())
468             {
469                 case "talk to max":
470                     AddInterruption(new Max(control));
471                     break;
472                 case "who is online":
473                     ListFriends();
474                     break;
475                 case "open the closet":
476                     control.instance.TabConsole.SelectTab("inventory");
477                     SelectConversation(inventory);
478                     break;
479                 case "friends":
480                     control.instance.TabConsole.SelectTab("friends");
481                     SelectConversation(friends);
482                     break;
483                 case "skip":
484                     Talker.Flush();
485                     Talker.SayMore("Flushed.");
486                     break;
487                 case "talk":
488                     control.instance.TabConsole.SelectTab("chat");
489                     SelectConversation(chat);
490                     break;
491                 case "voice":
492                     control.instance.TabConsole.SelectTab("voice");
493                     SelectConversation(voice);
494                     break;
495                 default:
496                     return false;
497             }
498             return true;
499         }
500         
501         /// <summary>
502         /// Dispatch recognized text to appropriate conversation.
503         /// </summary>
504         /// <param name="message"></param>
505         internal void Hear(string message)
506         {
507             // General commands.
508             if (Command(message)) return;
509
510             // Let the current conversation handle it.
511             if (currentMode != null)
512                 currentMode.Hear(message);
513         }
514
515         internal void SelectConversation(Mode c)
516         {
517             if (c == null)
518             {
519                 Talker.Say("Trying to start non-existant conversation", Talk.BeepType.Bad );
520                 return;
521             }
522             // Avoid multiple starts.
523             if (currentMode == c) return;
524
525             // Let the old conversation deactivate any event hooks, grammars, etc.
526             if (currentMode != null)
527                 currentMode.Stop();
528
529             currentMode = c;
530             currentMode.Start();
531         }
532
533         internal void SelectConversation(string name)
534         {
535             if (conversations.ContainsKey(name))
536             {
537                 SelectConversation(conversations[name]);
538             }
539             else
540             {
541                 Talker.Say("Can not find conversation " + name, Talk.BeepType.Bad);
542             }
543         }
544
545         /// <summary>
546         /// Find an existing conversation by name.
547         /// </summary>
548         /// <param name="title"></param>
549         /// <returns></returns>
550         /// <remarks>Used for IM sessions.</remarks>
551         internal Mode GetConversation(string title)
552         {
553             if (conversations.ContainsKey(title))
554                 return conversations[title];
555
556             return null;
557         }
558
559         /// <summary>
560         /// Add a conversation context to those we are tracking.
561         /// </summary>
562         /// <param name="m"></param>
563         internal void AddConversation(Mode m)
564         {
565             if (!conversations.ContainsKey(m.Title))
566                 conversations[m.Title] = m;
567         }
568
569         /// <summary>
570         /// Remove the context for a conversation that is no longer visible.
571         /// </summary>
572         /// <param name="name"></param>
573         internal void RemoveConversation(string name)
574         {
575             bool change = false;
576
577             lock (conversations)
578             {
579                 if (conversations.ContainsKey(name))
580                 {
581                     Mode doomed = conversations[name];
582                     if (currentMode == doomed)
583                     {
584                         change = true;
585                         currentMode = chat;
586                     }
587                     if (interrupted == doomed)
588                         interrupted = chat;
589
590                     conversations.Remove(name);
591                     if (change)
592                         SelectConversation(currentMode);
593                 }
594             }
595         }
596
597         /// <summary>
598         /// Take note of a new interruption.
599         /// </summary>
600         /// <param name="m"></param>
601         internal void AddInterruption(Mode m)
602         {
603             lock (interruptions)
604             {
605                 // Add to the end of the list.
606                 interruptions.AddLast(m);
607
608                 // If the list WAS empty, start this.
609                 if (interruptions.Count == 1)
610                     StartInterruption();
611             }
612         }
613
614         internal void ChangeFocus(Mode toThis)
615         {
616             currentMode = toThis;
617              if (currentMode != null)
618                 currentMode.Start();
619         }
620
621  
622         /// <summary>
623         /// Event handler for new blue dialog boxes.
624         /// </summary>
625         /// <param name="sender"></param>
626         /// <param name="e"></param>
627         void OnNotificationDisplayed(object sender, NotificationEventArgs e)
628         {
629             AddInterruption(new Conversation.BlueMenu(control,e));
630         }
631
632         /// <summary>
633         /// Handle Instant Messages
634         /// </summary>
635         /// <param name="im"></param>
636         /// <param name="simulator"></param>
637         void OnInstantMessage(object sender, InstantMessageEventArgs e)
638         {
639             Conversation.IMSession sess = null;
640             string groupName;
641
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.
645             switch (e.IM.Dialog)
646             {
647                 case InstantMessageDialog.MessageFromAgent:
648                     if (control.instance.Groups.ContainsKey(e.IM.IMSessionID))
649                     {
650                         // Message from a group member
651                         groupName = control.instance.Groups[e.IM.IMSessionID].Name;
652                         sess = (IMSession)control.converse.GetConversation(groupName);
653                         if (sess!=null)
654                             sess.OnMessage( e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message );
655                     }
656                     else if (e.IM.BinaryBucket.Length >= 2)
657                     {
658                         // Ad-hoc friend conference
659                         sess = (IMSession)control.converse.GetConversation(Utils.BytesToString(e.IM.BinaryBucket));
660                         if (sess != null)
661                             sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
662                     }
663                     else if (e.IM.FromAgentName == "Second Life")
664                     {
665                         Talker.Say("Second Life says "+ e.IM.Message);
666                     }
667                     else
668                     {
669                         // Message from an individual
670                         sess = (IMSession)control.converse.GetConversation(e.IM.FromAgentName); 
671                         if (sess != null)
672                             sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
673                     }
674                     break;
675
676                 case InstantMessageDialog.SessionSend:
677                     if (control.instance.Groups.ContainsKey(e.IM.IMSessionID))
678                     {
679                         // Message from a group member
680                         groupName = control.instance.Groups[e.IM.IMSessionID].Name;
681                         sess = (IMSession)control.converse.GetConversation(groupName);
682                     }
683                     else if (e.IM.BinaryBucket.Length >= 2) // ad hoc friends conference
684                     {
685                         sess = (IMSession)control.converse.GetConversation(Utils.BytesToString(e.IM.BinaryBucket));
686                     }
687
688                     if (sess != null)
689                         sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
690                     break;
691
692                 case InstantMessageDialog.FriendshipOffered:
693                     Talker.Say(e.IM.FromAgentName + " is offering friendship.");
694                     break;
695
696                 default:
697                     break;
698             }
699
700         }
701     }
702 }
703