OSDN Git Service

Set eol style property
[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 IMTabWindow)
204             {
205                 IMTabWindow tab = (IMTabWindow)sTabControl;
206                 SelectConversation(tab.TargetName);
207             }
208             else if (sTabControl is ObjectsConsole)
209             {
210                 SelectConversation(surroundings);
211             }
212
213         }
214
215         /// <summary>
216         /// Create conversations as tabs are created.
217         /// </summary>
218         /// <param name="sender"></param>
219         /// <param name="e"></param>
220         void TabConsole_OnTabAdded(object sender, TabEventArgs e)
221         {
222             System.Windows.Forms.Control sTabControl = e.Tab.Control;
223
224             Mode newConv = null;
225
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)
230             {
231                 if (chat != null) return;
232                 newConv = chat = new Chat(control);
233             }
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)
239             {
240                 GroupIMTabWindow tab = (GroupIMTabWindow)sTabControl;
241                 AddConversation(new GroupIMSession(control, tab.SessionId));
242                 return;
243             }
244             else if (sTabControl is IMTabWindow)
245             {
246                 IMTabWindow tab = (IMTabWindow)sTabControl;
247                 AddConversation(new SingleIMSession(control, tab.TargetName, tab.TargetId, tab.SessionId));
248                 return;
249             }
250             else if (sTabControl is ObjectsConsole)
251             {
252                 surroundings = new Surroundings( control );
253                 AddConversation( surroundings );
254             }
255
256             // If a conversation was created, switch to it.
257             if (newConv != null)
258             {
259                 AddConversation(newConv);
260                 // Select CHAT as soon as it is created.
261                 if (sTabControl is ChatConsole)
262                     SelectConversation(newConv);
263             }
264         }
265
266         /// <summary>
267         /// Quietly close conversations.
268         /// </summary>
269         /// <param name="sender"></param>
270         /// <param name="e"></param>
271         void TabConsole_OnTabRemoved(object sender, TabEventArgs e)
272         {
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
285         }
286
287
288         internal override void Shutdown()
289         {
290             // Automatically handle notifications (blue dialogs)
291             Notification.OnNotificationDisplayed -=
292                 new Notification.NotificationCallback(OnNotificationDisplayed);
293
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);
299
300             control.instance.Netcom.ClientLoginStatus -=
301                 new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
302
303             // Notice arrival in a new sim
304             control.instance.Client.Network.SimChanged -=
305                 new EventHandler<SimChangedEventArgs>(Network_SimChanged);
306
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);
314
315             // Notice when the active tab changes on the graphics user interface.
316             control.instance.TabConsole.OnTabSelected -=
317                 new TabsConsole.TabCallback(OnTabChange);
318
319             // Handle Instant Messages too
320             control.instance.Client.Self.IM +=
321                 new EventHandler<InstantMessageEventArgs>(OnInstantMessage);
322
323             // System notifications in chat
324             control.instance.TabConsole.OnChatNotification -= new TabsConsole.ChatNotificationCallback(TabConsole_OnChatNotification);
325
326             control.listener.DeactivateGrammar(CONVGRAMMAR);
327
328             foreach (Mode m in conversations.Values)
329             {
330                 m.Stop();
331             }
332             foreach (Mode m in interruptions)
333             {
334                 m.Stop();
335             }
336             conversations.Clear();
337             interruptions.Clear();
338         }
339
340         void WatchKeys()
341         {
342         }
343
344         internal bool amCurrent(Mode m)
345         {
346             return (currentMode == m);
347         }
348
349         /// <summary>
350         /// Start an interrupting conversation.
351         /// </summary>
352         void StartInterruption()
353         {
354             // Remember what we were talking about.
355             if (interrupted == null)
356                 interrupted = currentMode;
357
358             // Visually they stack up, so we take the last one first.
359             currentMode = interruptions.Last();
360             currentMode.Start();
361         }
362
363         /// <summary>
364         /// Finish an interruption and resume normal conversation
365         /// </summary>
366         internal void FinishInterruption( Mode m )
367         {
368             lock (interruptions)
369             {
370                 // Remove the terminating interruption from the list.
371                 interruptions.Remove(m);
372
373                 // Let it remove any event hooks, etc
374                 m.Stop();
375
376                 // If there are any other interruptions pending, start one.
377                 // Otherwise resume the interrupted conversation.
378                 if (interruptions.Count > 0)
379                     StartInterruption();
380                 else
381                 {
382                     currentMode = interrupted;
383                     interrupted = null;
384                     currentMode.Start();
385                 }
386             }
387         }
388  
389         private void Network_ClientConnected(object sender, EventArgs e)
390         {
391             Talker.Say("You are connected.", Talk.BeepType.Good);
392             if (chat == null)
393             {
394                 chat = new Chat(control);
395
396                 AddConversation(chat);
397                 SelectConversation(chat);
398             }
399         }
400
401         void Network_SimChanged(object sender, SimChangedEventArgs e)
402         {
403             Talker.Say("You are now in " +
404                 control.instance.Client.Network.CurrentSim.Name,
405                 Talk.BeepType.Good);
406         }
407
408
409         /// <summary>
410         /// Announce reason for disconnect.
411         /// </summary>
412         /// <param name="reason"></param>
413         /// <param name="message"></param>
414         private void Network_Disconnected(object sender, DisconnectedEventArgs e)
415         {
416             switch (e.Reason)
417             {
418                 case NetworkManager.DisconnectType.ClientInitiated:
419                     Talker.Say("You are disconnected.");
420                     break;
421                 case NetworkManager.DisconnectType.SimShutdown:
422                     Talker.Say("The region you were in has been shut down.  You are disconnected.",
423                         Talk.BeepType.Bad);
424                     break;
425                 case NetworkManager.DisconnectType.NetworkTimeout:
426                     Talker.Say("You have been disconnected by a network timeout.",
427                         Talk.BeepType.Bad);
428                     break;
429                 case NetworkManager.DisconnectType.ServerInitiated:
430                     Talker.Say("The server has disconnected you.  " + e.Message,
431                         Talk.BeepType.Bad);
432                     break;
433             }
434         }
435         private void ListFriends()
436         {
437             List<FriendInfo> onlineFriends =
438                 control.instance.Client.Friends.FriendList.FindAll(delegate(FriendInfo f) { return f.IsOnline; });
439             string list = "";
440             foreach (FriendInfo f in onlineFriends)
441             {
442                 list += f.Name + ", ";
443             }
444             list += "are online.";
445             Talker.Say(list);
446         }
447
448
449         /// <summary>
450         /// Check for general commands
451         /// </summary>
452         /// <param name="message"></param>
453         /// <returns>true if command recognized</returns>
454         private bool Command(string message)
455         {
456             switch (message.ToLower())
457             {
458                 case "talk to max":
459                     AddInterruption(new Max(control));
460                     break;
461                 case "who is online":
462                     ListFriends();
463                     break;
464                 case "open the closet":
465                     control.instance.TabConsole.SelectTab("inventory");
466                     SelectConversation(inventory);
467                     break;
468                 case "friends":
469                     control.instance.TabConsole.SelectTab("friends");
470                     SelectConversation(friends);
471                     break;
472                 case "skip":
473                     Talker.Flush();
474                     Talker.SayMore("Flushed.");
475                     break;
476                 case "talk":
477                     control.instance.TabConsole.SelectTab("chat");
478                     SelectConversation(chat);
479                     break;
480                 case "voice":
481                     control.instance.TabConsole.SelectTab("voice");
482                     SelectConversation(voice);
483                     break;
484                 default:
485                     return false;
486             }
487             return true;
488         }
489         
490         /// <summary>
491         /// Dispatch recognized text to appropriate conversation.
492         /// </summary>
493         /// <param name="message"></param>
494         internal void Hear(string message)
495         {
496             // General commands.
497             if (Command(message)) return;
498
499             // Let the current conversation handle it.
500             if (currentMode != null)
501                 currentMode.Hear(message);
502         }
503
504         internal void SelectConversation(Mode c)
505         {
506             if (c == null)
507             {
508                 Talker.Say("Trying to start non-existant conversation", Talk.BeepType.Bad );
509                 return;
510             }
511             // Avoid multiple starts.
512             if (currentMode == c) return;
513
514             // Let the old conversation deactivate any event hooks, grammars, etc.
515             if (currentMode != null)
516                 currentMode.Stop();
517
518             currentMode = c;
519             currentMode.Start();
520         }
521
522         internal void SelectConversation(string name)
523         {
524             if (conversations.ContainsKey(name))
525             {
526                 SelectConversation(conversations[name]);
527             }
528             else
529             {
530                 Talker.Say("Can not find conversation " + name, Talk.BeepType.Bad);
531             }
532         }
533
534         /// <summary>
535         /// Find an existing conversation by name.
536         /// </summary>
537         /// <param name="title"></param>
538         /// <returns></returns>
539         /// <remarks>Used for IM sessions.</remarks>
540         internal Mode GetConversation(string title)
541         {
542             if (conversations.ContainsKey(title))
543                 return conversations[title];
544
545             return null;
546         }
547
548         /// <summary>
549         /// Add a conversation context to those we are tracking.
550         /// </summary>
551         /// <param name="m"></param>
552         internal void AddConversation(Mode m)
553         {
554             if (!conversations.ContainsKey(m.Title))
555                 conversations[m.Title] = m;
556         }
557
558         /// <summary>
559         /// Remove the context for a conversation that is no longer visible.
560         /// </summary>
561         /// <param name="name"></param>
562         internal void RemoveConversation(string name)
563         {
564             bool change = false;
565
566             lock (conversations)
567             {
568                 if (conversations.ContainsKey(name))
569                 {
570                     Mode doomed = conversations[name];
571                     if (currentMode == doomed)
572                     {
573                         change = true;
574                         currentMode = chat;
575                     }
576                     if (interrupted == doomed)
577                         interrupted = chat;
578
579                     conversations.Remove(name);
580                     if (change)
581                         SelectConversation(currentMode);
582                 }
583             }
584         }
585
586         /// <summary>
587         /// Take note of a new interruption.
588         /// </summary>
589         /// <param name="m"></param>
590         internal void AddInterruption(Mode m)
591         {
592             lock (interruptions)
593             {
594                 // Add to the end of the list.
595                 interruptions.AddLast(m);
596
597                 // If the list WAS empty, start this.
598                 if (interruptions.Count == 1)
599                     StartInterruption();
600             }
601         }
602
603         internal void ChangeFocus(Mode toThis)
604         {
605             currentMode = toThis;
606              if (currentMode != null)
607                 currentMode.Start();
608         }
609
610  
611         /// <summary>
612         /// Event handler for new blue dialog boxes.
613         /// </summary>
614         /// <param name="sender"></param>
615         /// <param name="e"></param>
616         void OnNotificationDisplayed(object sender, NotificationEventArgs e)
617         {
618             AddInterruption(new Conversation.BlueMenu(control,e));
619         }
620
621         /// <summary>
622         /// Handle Instant Messages
623         /// </summary>
624         /// <param name="im"></param>
625         /// <param name="simulator"></param>
626         void OnInstantMessage(object sender, InstantMessageEventArgs e)
627         {
628             Conversation.IMSession sess;
629             string groupName;
630
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.
634             switch (e.IM.Dialog)
635             {
636                 case InstantMessageDialog.MessageFromAgent:
637                     if (control.instance.Groups.ContainsKey(e.IM.IMSessionID))
638                     {
639                         // Message from a group member
640                         groupName = control.instance.Groups[e.IM.IMSessionID].Name;
641                         sess = (IMSession)control.converse.GetConversation(groupName);
642                         if (sess!=null)
643                             sess.OnMessage( e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message );
644                     }
645                     else if (e.IM.BinaryBucket.Length >= 2)
646                     {
647                         // Ad-hoc friend conference
648                         // TODO this is probably the wrong name
649                         sess = (IMSession)control.converse.GetConversation(e.IM.FromAgentName);
650                         if (sess != null)
651                             sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
652                     }
653                     else if (e.IM.FromAgentName == "Second Life")
654                     {
655                         Talker.Say("Second Life says "+ e.IM.Message);
656                     }
657                     else
658                     {
659                         // Message from an individual
660                         sess = (IMSession)control.converse.GetConversation(e.IM.FromAgentName); 
661                         if (sess != null)
662                             sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
663                     }
664                     break;
665
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);
670                     if (sess != null)
671                         sess.OnMessage(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
672                     break;
673
674                 case InstantMessageDialog.FriendshipOffered:
675                     Talker.Say(e.IM.FromAgentName + " is offering friendship.");
676                     break;
677
678                 default:
679                     break;
680             }
681
682         }
683     }
684 }
685