OSDN Git Service

Added 'group_invite' LSL automation command
[radegast/radegast.git] / Radegast / GUI / Consoles / TabsConsole.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2013, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id$
30 //
31 using System;
32 using System.Collections.Generic;
33 using System.ComponentModel;
34 using System.Drawing;
35 using System.Windows.Forms;
36 using Radegast.Netcom;
37 using OpenMetaverse;
38
39 namespace Radegast
40 {
41     public partial class TabsConsole : UserControl
42     {
43
44         /// <summary>
45         /// List of nearby avatars (radar data)
46         /// </summary>
47         public List<NearbyAvatar> NearbyAvatars
48         {
49             get
50             {
51                 List<NearbyAvatar> res = new List<NearbyAvatar>();
52
53                 if (TabExists("chat"))
54                 {
55                     ChatConsole c = (ChatConsole)Tabs["chat"].Control;
56                     lock (c.agentSimHandle)
57                     {
58                         foreach (ListViewItem item in c.lvwObjects.Items)
59                         {
60                             if (item.Name != client.Self.AgentID.ToString())
61                             {
62                                 res.Add(new NearbyAvatar() { ID = new UUID(item.Name), Name = item.Text });
63                             }
64                         }
65                     }
66                 }
67
68                 return res;
69             }
70         }
71
72         /// <summary>
73         /// Delegate invoked on tab operations
74         /// </summary>
75         /// <param name="sender">Event sender</param>
76         /// <param name="e">Event arguments</param>
77         public delegate void TabCallback(object sender, TabEventArgs e);
78
79         /// <summary>
80         /// Fired when a tab is selected
81         /// </summary>
82         public event TabCallback OnTabSelected;
83
84
85         /// <summary>
86         /// Delegate invoked when chat notification is printed
87         /// </summary>
88         /// <param name="sender">Event sender</param>
89         /// <param name="e">Event arguments</param>
90         public delegate void ChatNotificationCallback(object sender, ChatNotificationEventArgs e);
91
92         /// <summary>
93         /// Fired when a tab is selected
94         /// </summary>
95         public event ChatNotificationCallback OnChatNotification;
96
97         /// <summary>
98         /// Fired when a new tab is added
99         /// </summary>
100         public event TabCallback OnTabAdded;
101
102         /// <summary>
103         /// Fired when a tab is removed
104         /// </summary>
105         public event TabCallback OnTabRemoved;
106
107         private RadegastInstance instance;
108         private GridClient client { get { return instance.Client; } }
109         private RadegastNetcom netcom { get { return instance.Netcom; } }
110         private ChatTextManager mainChatManger;
111         public ChatTextManager MainChatManger { get { return mainChatManger; } }
112
113         private Dictionary<string, RadegastTab> tabs = new Dictionary<string, RadegastTab>();
114         public Dictionary<string, RadegastTab> Tabs { get { return tabs; } }
115
116         private ChatConsole chatConsole;
117
118         private RadegastTab selectedTab;
119
120         /// <summary>
121         /// Currently selected tab
122         /// </summary>
123         public RadegastTab SelectedTab
124         {
125             get
126             {
127                 return selectedTab;
128             }
129         }
130
131         private Form owner;
132
133         public TabsConsole(RadegastInstance instance)
134         {
135             InitializeComponent();
136             Disposed += new EventHandler(TabsConsole_Disposed);
137
138             this.instance = instance;
139             this.instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
140
141             AddNetcomEvents();
142
143             InitializeMainTab();
144             InitializeChatTab();
145
146             // Callbacks
147             RegisterClientEvents(client);
148         }
149
150         private void RegisterClientEvents(GridClient client)
151         {
152             client.Self.ScriptQuestion += new EventHandler<ScriptQuestionEventArgs>(Self_ScriptQuestion);
153             client.Self.ScriptDialog += new EventHandler<ScriptDialogEventArgs>(Self_ScriptDialog);
154             client.Self.LoadURL += new EventHandler<LoadUrlEventArgs>(Self_LoadURL);
155             client.Self.SetDisplayNameReply += new EventHandler<SetDisplayNameReplyEventArgs>(Self_SetDisplayNameReply);
156             client.Avatars.DisplayNameUpdate += new EventHandler<DisplayNameUpdateEventArgs>(Avatars_DisplayNameUpdate);
157             client.Network.EventQueueRunning += new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
158             client.Network.RegisterCallback(OpenMetaverse.Packets.PacketType.ScriptTeleportRequest, ScriptTeleportRequestHandler);
159         }
160
161         private void UnregisterClientEvents(GridClient client)
162         {
163             client.Self.ScriptQuestion -= new EventHandler<ScriptQuestionEventArgs>(Self_ScriptQuestion);
164             client.Self.ScriptDialog -= new EventHandler<ScriptDialogEventArgs>(Self_ScriptDialog);
165             client.Self.LoadURL -= new EventHandler<LoadUrlEventArgs>(Self_LoadURL);
166             client.Self.SetDisplayNameReply -= new EventHandler<SetDisplayNameReplyEventArgs>(Self_SetDisplayNameReply);
167             client.Avatars.DisplayNameUpdate -= new EventHandler<DisplayNameUpdateEventArgs>(Avatars_DisplayNameUpdate);
168             client.Network.EventQueueRunning -= new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
169             client.Network.UnregisterCallback(OpenMetaverse.Packets.PacketType.ScriptTeleportRequest, ScriptTeleportRequestHandler);
170         }
171
172         void instance_ClientChanged(object sender, ClientChangedEventArgs e)
173         {
174             UnregisterClientEvents(e.OldClient);
175             RegisterClientEvents(client);
176         }
177
178         void TabsConsole_Disposed(object sender, EventArgs e)
179         {
180             RemoveNetcomEvents();
181             UnregisterClientEvents(client);
182         }
183
184         private void AddNetcomEvents()
185         {
186             netcom.ClientLoginStatus += new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
187             netcom.ClientLoggedOut += new EventHandler(netcom_ClientLoggedOut);
188             netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
189             netcom.ChatSent += new EventHandler<ChatSentEventArgs>(netcom_ChatSent);
190             netcom.AlertMessageReceived += new EventHandler<AlertMessageEventArgs>(netcom_AlertMessageReceived);
191             netcom.InstantMessageReceived += new EventHandler<InstantMessageEventArgs>(netcom_InstantMessageReceived);
192         }
193
194         private void RemoveNetcomEvents()
195         {
196             netcom.ClientLoginStatus -= new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
197             netcom.ClientLoggedOut -= new EventHandler(netcom_ClientLoggedOut);
198             netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
199             netcom.ChatSent -= new EventHandler<ChatSentEventArgs>(netcom_ChatSent);
200             netcom.AlertMessageReceived -= new EventHandler<AlertMessageEventArgs>(netcom_AlertMessageReceived);
201             netcom.InstantMessageReceived -= new EventHandler<InstantMessageEventArgs>(netcom_InstantMessageReceived);
202         }
203
204         void ScriptTeleportRequestHandler(object sender, PacketReceivedEventArgs e)
205         {
206             if (InvokeRequired)
207             {
208                 if (IsHandleCreated || !instance.MonoRuntime)
209                     BeginInvoke(new MethodInvoker(() => ScriptTeleportRequestHandler(sender, e)));
210                 return;
211             }
212
213             var msg = (OpenMetaverse.Packets.ScriptTeleportRequestPacket)e.Packet;
214
215             if (TabExists("map"))
216             {
217                 Tabs["map"].Select();
218                 ((MapConsole)Tabs["map"].Control).CenterOnGlobalPos(
219                     (float)(client.Self.GlobalPosition.X - client.Self.SimPosition.X) + msg.Data.SimPosition.X,
220                     (float)(client.Self.GlobalPosition.Y - client.Self.SimPosition.Y) + msg.Data.SimPosition.Y,
221                     msg.Data.SimPosition.Z);
222             }
223         }
224
225         void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e)
226         {
227             if (InvokeRequired)
228             {
229                 BeginInvoke(new MethodInvoker(() => Network_EventQueueRunning(sender, e)));
230                 return;
231             }
232
233             if (TabExists("friends")) return;
234             if (e.Simulator == client.Network.CurrentSim)
235             {
236                 client.Self.UpdateAgentLanguage("en", true);
237                 InitializeOnlineTabs();
238             }
239         }
240
241         void Self_ScriptDialog(object sender, ScriptDialogEventArgs e)
242         {
243             if (instance.MainForm.InvokeRequired)
244             {
245                 instance.MainForm.BeginInvoke(new MethodInvoker(() => Self_ScriptDialog(sender, e)));
246                 return;
247             }
248
249             // Is this object muted
250             if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.ObjectID) // muted object by id
251                 || (m.Type == MuteType.ByName && m.Name == e.ObjectName) // object muted by name
252                 )) return;
253
254             instance.MainForm.AddNotification(new ntfScriptDialog(instance, e.Message, e.ObjectName, e.ImageID, e.ObjectID, e.FirstName, e.LastName, e.Channel, e.ButtonLabels));
255         }
256
257         void Self_ScriptQuestion(object sender, ScriptQuestionEventArgs e)
258         {
259             // Is this object muted
260             if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.TaskID) // muted object by id
261                 || (m.Type == MuteType.ByName && m.Name == e.ObjectName) // object muted by name
262                 )) return;
263
264             instance.MainForm.AddNotification(new ntfPermissions(instance, e.Simulator, e.TaskID, e.ItemID, e.ObjectName, e.ObjectOwnerName, e.Questions));
265         }
266
267         private void netcom_ClientLoginStatus(object sender, LoginProgressEventArgs e)
268         {
269             if (e.Status == LoginStatus.Failed)
270             {
271                 DisplayNotificationInChat("Login error: " + e.Message, ChatBufferTextStyle.Error);
272             }
273             else if (e.Status == LoginStatus.Success)
274             {
275                 DisplayNotificationInChat("Logged in as " + netcom.LoginOptions.FullName + ".", ChatBufferTextStyle.StatusDarkBlue);
276                 DisplayNotificationInChat("Login reply: " + e.Message, ChatBufferTextStyle.StatusDarkBlue);
277
278                 if (tabs.ContainsKey("login"))
279                 {
280                     if (selectedTab.Name == "login")
281                         SelectDefaultTab();
282                     ForceCloseTab("login");
283                 }
284
285                 client.Self.RetrieveInstantMessages();
286             }
287         }
288
289         private void netcom_ClientLoggedOut(object sender, EventArgs e)
290         {
291             DisposeOnlineTabs();
292
293             SelectDefaultTab();
294             DisplayNotificationInChat("Logged out.");
295
296         }
297
298         private void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
299         {
300             if (e.Reason == NetworkManager.DisconnectType.ClientInitiated) return;
301
302             DisposeOnlineTabs();
303             SelectDefaultTab();
304             DisplayNotificationInChat("Disconnected: " + e.Message, ChatBufferTextStyle.Error);
305         }
306
307         void Avatars_DisplayNameUpdate(object sender, DisplayNameUpdateEventArgs e)
308         {
309             DisplayNotificationInChat(string.Format("({0}) is now known as {1}", e.DisplayName.UserName, e.DisplayName.DisplayName));
310         }
311
312         void Self_SetDisplayNameReply(object sender, SetDisplayNameReplyEventArgs e)
313         {
314             if (e.Status == 200)
315             {
316                 DisplayNotificationInChat("You are now knows as " + e.DisplayName.DisplayName);
317             }
318             else
319             {
320                 DisplayNotificationInChat("Failed to set a new display name: " + e.Reason, ChatBufferTextStyle.Error);
321             }
322         }
323
324         private void netcom_AlertMessageReceived(object sender, AlertMessageEventArgs e)
325         {
326             tabs["chat"].Highlight();
327         }
328
329         private void netcom_ChatSent(object sender, ChatSentEventArgs e)
330         {
331             tabs["chat"].Highlight();
332         }
333
334         void Self_LoadURL(object sender, LoadUrlEventArgs e)
335         {
336             // Is the object or the owner muted?
337             if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.ObjectID) // muted object by id 
338                 || (m.Type == MuteType.ByName && m.Name == e.ObjectName) // object muted by name
339                 || (m.Type == MuteType.Resident && m.ID == e.OwnerID) // object's owner muted
340                 )) return;
341
342             instance.MainForm.AddNotification(new ntfLoadURL(instance, e));
343         }
344
345         private void netcom_InstantMessageReceived(object sender, InstantMessageEventArgs e)
346         {
347             // Messaage from someone we muted?
348             if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Resident && me.ID == e.IM.FromAgentID)) return;
349
350             if (instance.State.LSLHelper.ProcessIM(e))
351             {
352                 return;
353             }
354
355             switch (e.IM.Dialog)
356             {
357                 case InstantMessageDialog.SessionSend:
358                     if (instance.Groups.ContainsKey(e.IM.IMSessionID))
359                     {
360                         HandleGroupIM(e);
361                     }
362                     else
363                     {
364                         HandleConferenceIM(e);
365                     }
366                     break;
367
368                 case InstantMessageDialog.MessageFromAgent:
369                     if (e.IM.FromAgentName == "Second Life")
370                     {
371                         HandleIMFromObject(e);
372                     }
373                     else if (e.IM.FromAgentID == UUID.Zero)
374                     {
375                         instance.MainForm.AddNotification(new ntfGeneric(instance, e.IM.Message));
376                     }
377                     else if (e.IM.GroupIM || instance.Groups.ContainsKey(e.IM.IMSessionID))
378                     {
379                         HandleGroupIM(e);
380                     }
381                     else if (e.IM.BinaryBucket.Length > 1)
382                     { // conference
383                         HandleConferenceIM(e);
384                     }
385                     else if (e.IM.IMSessionID == UUID.Zero)
386                     {
387                         String msg = string.Format("Region message from {0}: {1}", instance.Names.Get(e.IM.FromAgentID, e.IM.FromAgentName), e.IM.Message);
388                         instance.MainForm.AddNotification(new ntfGeneric(instance, msg));
389                         DisplayNotificationInChat(msg);
390                     }
391                     else
392                     {
393                         HandleIM(e);
394                     }
395                     break;
396
397                 case InstantMessageDialog.MessageFromObject:
398                     HandleIMFromObject(e);
399                     break;
400
401                 case InstantMessageDialog.StartTyping:
402                     if (TabExists(e.IM.FromAgentName))
403                     {
404                         RadegastTab tab = tabs[e.IM.FromAgentName.ToLower()];
405                         if (!tab.Highlighted) tab.PartialHighlight();
406                     }
407
408                     break;
409
410                 case InstantMessageDialog.StopTyping:
411                     if (TabExists(e.IM.FromAgentName))
412                     {
413                         RadegastTab tab = tabs[e.IM.FromAgentName.ToLower()];
414                         if (!tab.Highlighted) tab.Unhighlight();
415                     }
416
417                     break;
418
419                 case InstantMessageDialog.MessageBox:
420                     instance.MainForm.AddNotification(new ntfGeneric(instance, e.IM.Message));
421                     break;
422
423                 case InstantMessageDialog.RequestTeleport:
424                     if (instance.RLV.AutoAcceptTP(e.IM.FromAgentID))
425                     {
426                         DisplayNotificationInChat("Auto accepting teleprot from " + e.IM.FromAgentName);
427                         instance.Client.Self.TeleportLureRespond(e.IM.FromAgentID, e.IM.IMSessionID, true);
428                     }
429                     else
430                     {
431                         instance.MainForm.AddNotification(new ntfTeleport(instance, e.IM));
432                     }
433                     break;
434
435                 case InstantMessageDialog.RequestLure:
436                     instance.MainForm.AddNotification(new ntfRequestLure(instance, e.IM));
437                     break;
438
439                 case InstantMessageDialog.GroupInvitation:
440                     instance.MainForm.AddNotification(new ntfGroupInvitation(instance, e.IM));
441                     break;
442
443                 case InstantMessageDialog.FriendshipOffered:
444                     if (e.IM.FromAgentName == "Second Life")
445                     {
446                         HandleIMFromObject(e);
447                     }
448                     else
449                     {
450                         instance.MainForm.AddNotification(new ntfFriendshipOffer(instance, e.IM));
451                     }
452                     break;
453
454                 case InstantMessageDialog.InventoryAccepted:
455                     DisplayNotificationInChat(e.IM.FromAgentName + " accepted your inventory offer.");
456                     break;
457
458                 case InstantMessageDialog.InventoryDeclined:
459                     DisplayNotificationInChat(e.IM.FromAgentName + " declined your inventory offer.");
460                     break;
461
462                 case InstantMessageDialog.GroupNotice:
463                     // Is this group muted?
464                     if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Group && me.ID == e.IM.FromAgentID)) break;
465
466                     instance.MainForm.AddNotification(new ntfGroupNotice(instance, e.IM));
467                     break;
468
469                 case InstantMessageDialog.InventoryOffered:
470                     var ion = new ntfInventoryOffer(instance, e.IM);
471                     instance.MainForm.AddNotification(ion);
472                     if (instance.GlobalSettings["inv_auto_accept_mode"].AsInteger() == 1)
473                     {
474                         ion.btnAccept.PerformClick();
475                     }
476                     else if (instance.GlobalSettings["inv_auto_accept_mode"].AsInteger() == 2)
477                     {
478                         ion.btnDiscard.PerformClick();
479                     }
480                     break;
481
482                 case InstantMessageDialog.TaskInventoryOffered:
483                     // Is the object muted by name?
484                     if (null != client.Self.MuteList.Find(me => me.Type == MuteType.ByName && me.Name == e.IM.FromAgentName)) break;
485
486                     var iont = new ntfInventoryOffer(instance, e.IM);
487                     instance.MainForm.AddNotification(iont);
488                     if (instance.GlobalSettings["inv_auto_accept_mode"].AsInteger() == 1)
489                     {
490                         iont.btnAccept.PerformClick();
491                     }
492                     else if (instance.GlobalSettings["inv_auto_accept_mode"].AsInteger() == 2)
493                     {
494                         iont.btnDiscard.PerformClick();
495                     }
496                     break;
497             }
498         }
499
500         /// <summary>
501         /// Make default tab (chat) active
502         /// </summary>
503         public void SelectDefaultTab()
504         {
505             if (IsHandleCreated && TabExists("chat"))
506                 tabs["chat"].Select();
507         }
508
509         /// <summary>
510         /// Displays notification in the main chat tab
511         /// </summary>
512         /// <param name="msg">Message to be printed in the chat tab</param>
513         public void DisplayNotificationInChat(string msg)
514         {
515             DisplayNotificationInChat(msg, ChatBufferTextStyle.ObjectChat);
516         }
517
518         /// <summary>
519         /// Displays notification in the main chat tab
520         /// </summary>
521         /// <param name="msg">Message to be printed in the chat tab</param>
522         /// <param name="style">Style of the message to be printed, normal, object, etc.</param>
523         public void DisplayNotificationInChat(string msg, ChatBufferTextStyle style)
524         {
525             DisplayNotificationInChat(msg, style, true);
526         }
527
528         /// <summary>
529         /// Displays notification in the main chat tab
530         /// </summary>
531         /// <param name="msg">Message to be printed in the chat tab</param>
532         /// <param name="style">Style of the message to be printed, normal, object, etc.</param>
533         /// <param name="highlightChatTab">Highligt (and flash in taskbar) chat tab if not selected</param>
534         public void DisplayNotificationInChat(string msg, ChatBufferTextStyle style, bool highlightChatTab)
535         {
536             if (InvokeRequired)
537             {
538                 if (!instance.MonoRuntime || IsHandleCreated)
539                     BeginInvoke(new MethodInvoker(() => DisplayNotificationInChat(msg, style, highlightChatTab)));
540                 return;
541             }
542
543             if (style != ChatBufferTextStyle.Invisible)
544             {
545                 ChatBufferItem line = new ChatBufferItem(
546                     DateTime.Now,
547                     string.Empty,
548                     UUID.Zero,
549                     msg,
550                     style
551                 );
552
553                 try
554                 {
555                     mainChatManger.ProcessBufferItem(line, true);
556                     if (highlightChatTab)
557                     {
558                         tabs["chat"].Highlight();
559                     }
560                 }
561                 catch (Exception) { }
562             }
563
564             if (OnChatNotification != null)
565             {
566                 try { OnChatNotification(this, new ChatNotificationEventArgs(msg, style)); }
567                 catch { }
568             }
569         }
570
571         private void HandleIMFromObject(InstantMessageEventArgs e)
572         {
573             // Is the object or the owner muted?
574             if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.IM.IMSessionID) // muted object by id 
575                 || (m.Type == MuteType.ByName && m.Name == e.IM.FromAgentName) // object muted by name
576                 || (m.Type == MuteType.Resident && m.ID == e.IM.FromAgentID) // object's owner muted
577                 )) return;
578
579             DisplayNotificationInChat(e.IM.FromAgentName + ": " + e.IM.Message);
580         }
581
582         public static Control FindFocusedControl(Control control)
583         {
584             var container = control as ContainerControl;
585             while (container != null)
586             {
587                 control = container.ActiveControl;
588                 container = control as ContainerControl;
589             }
590             return control;
591         }
592
593         /// <summary>
594         /// Creates new IM tab if needed
595         /// </summary>
596         /// <param name="agentID">IM session with agentID</param>
597         /// <param name="label">Tab label</param>
598         /// <param name="makeActive">Should tab be selected and focused</param>
599         /// <returns>True if there was an existing IM tab, false if it was created</returns>
600         public bool ShowIMTab(UUID agentID, string label, bool makeActive)
601         {
602             if (instance.TabConsole.TabExists((client.Self.AgentID ^ agentID).ToString()))
603             {
604                 if (makeActive)
605                 {
606                     instance.TabConsole.SelectTab((client.Self.AgentID ^ agentID).ToString());
607                 }
608                 return false;
609             }
610
611             if (makeActive)
612             {
613                 instance.MediaManager.PlayUISound(UISounds.IMWindow);
614             }
615             else
616             {
617                 instance.MediaManager.PlayUISound(UISounds.IM);
618             }
619
620             Control active = FindFocusedControl(instance.MainForm);
621
622             instance.TabConsole.AddIMTab(agentID, client.Self.AgentID ^ agentID, label);
623
624             if (makeActive)
625             {
626                 instance.TabConsole.SelectTab((client.Self.AgentID ^ agentID).ToString());
627             }
628             else if (active != null)
629             {
630                 active.Focus();
631             }
632
633             return true;
634         }
635
636         private void HandleIM(InstantMessageEventArgs e)
637         {
638             bool isNew = ShowIMTab(e.IM.FromAgentID, e.IM.FromAgentName, false);
639             if (!TabExists(e.IM.IMSessionID.ToString())) return; // this should now exist. sanity check anyway
640             RadegastTab tab = tabs[e.IM.IMSessionID.ToString()];
641             tab.Highlight();
642
643             if (isNew)
644             {
645                 ((IMTabWindow)tab.Control).TextManager.ProcessIM(e);
646             }
647         }
648
649         private void HandleConferenceIM(InstantMessageEventArgs e)
650         {
651             if (TabExists(e.IM.IMSessionID.ToString()))
652             {
653                 RadegastTab tab = tabs[e.IM.IMSessionID.ToString()];
654                 tab.Highlight();
655                 return;
656             }
657
658             instance.MediaManager.PlayUISound(UISounds.IM);
659
660             Control active = FindFocusedControl(instance.MainForm);
661
662             ConferenceIMTabWindow imTab = AddConferenceIMTab(e.IM.IMSessionID, Utils.BytesToString(e.IM.BinaryBucket));
663             tabs[e.IM.IMSessionID.ToString()].Highlight();
664             imTab.TextManager.ProcessIM(e);
665
666             if (active != null)
667             {
668                 active.Focus();
669             }
670         }
671
672         private void HandleGroupIM(InstantMessageEventArgs e)
673         {
674             // Ignore group IM from a muted group
675             if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Group && (me.ID == e.IM.IMSessionID || me.ID == e.IM.FromAgentID))) return;
676
677             if (TabExists(e.IM.IMSessionID.ToString()))
678             {
679                 RadegastTab tab = tabs[e.IM.IMSessionID.ToString()];
680                 tab.Highlight();
681                 return;
682             }
683
684             instance.MediaManager.PlayUISound(UISounds.IM);
685
686             Control active = FindFocusedControl(instance.MainForm);
687
688             GroupIMTabWindow imTab = AddGroupIMTab(e.IM.IMSessionID, Utils.BytesToString(e.IM.BinaryBucket));
689             imTab.TextManager.ProcessIM(e);
690             tabs[e.IM.IMSessionID.ToString()].Highlight();
691
692             if (active != null)
693             {
694                 active.Focus();
695             }
696         }
697
698         public void InitializeMainTab()
699         {
700             if (TabExists("login"))
701             {
702                 ForceCloseTab("login");
703             }
704
705             LoginConsole loginConsole = new LoginConsole(instance);
706
707             RadegastTab tab = AddTab("login", "Login", loginConsole);
708             tab.AllowClose = false;
709             tab.AllowDetach = false;
710             tab.AllowMerge = false;
711             tab.AllowHide = false;
712
713             loginConsole.RegisterTab(tab);
714         }
715
716         private void InitializeChatTab()
717         {
718             chatConsole = new ChatConsole(instance);
719             mainChatManger = chatConsole.ChatManager;
720
721             RadegastTab tab = AddTab("chat", "Chat", chatConsole);
722             tab.AllowClose = false;
723             tab.AllowDetach = false;
724             tab.AllowHide = false;
725         }
726
727
728         /// <summary>
729         /// Create Tabs that only make sense when connected
730         /// </summary>
731         private void InitializeOnlineTabs()
732         {
733             RadegastTab tab = AddTab("friends", "Friends", new FriendsConsole(instance));
734             tab.AllowClose = false;
735             tab.AllowDetach = true;
736             tab.Visible = false;
737
738             tab = AddTab("groups", "Groups", new GroupsConsole(instance));
739             tab.AllowClose = false;
740             tab.AllowDetach = true;
741             tab.Visible = false;
742
743             // Ugly workaround for a mono bug
744             InventoryConsole inv = new InventoryConsole(instance);
745             if (instance.MonoRuntime) inv.invTree.Scrollable = false;
746             tab = AddTab("inventory", "Inventory", inv);
747             if (instance.MonoRuntime) inv.invTree.Scrollable = true;
748             tab.AllowClose = false;
749             tab.AllowDetach = true;
750             tab.Visible = false;
751
752             tab = AddTab("search", "Search", new SearchConsole(instance));
753             tab.AllowClose = false;
754             tab.AllowDetach = true;
755             tab.Visible = false;
756
757             tab = AddTab("map", "Map", new MapConsole(instance));
758             tab.AllowClose = false;
759             tab.AllowDetach = true;
760             tab.Visible = false;
761
762             tab = AddTab("voice", "Voice", new VoiceConsole(instance));
763             tab.AllowClose = false;
764             tab.AllowDetach = true;
765             tab.Visible = false;
766         }
767
768         /// <summary>
769         /// Close tabs that make no sense when not connected
770         /// </summary>
771         private void DisposeOnlineTabs()
772         {
773             lock (tabs)
774             {
775                 ForceCloseTab("voice");
776                 ForceCloseTab("map");
777                 ForceCloseTab("search");
778                 ForceCloseTab("inventory");
779                 ForceCloseTab("groups");
780                 ForceCloseTab("friends");
781             }
782         }
783
784         private void ForceCloseTab(string name)
785         {
786             if (!TabExists(name)) return;
787
788             RadegastTab tab = tabs[name];
789             if (tab.Merged) SplitTab(tab);
790
791             tab.AllowClose = true;
792             tab.Close();
793             tab = null;
794         }
795
796
797         public void RegisterContextAction(Type libomvType, String label, EventHandler handler)
798         {
799             instance.ContextActionManager.RegisterContextAction(libomvType, label, handler);
800         }
801
802         public void RegisterContextAction(ContextAction handler)
803         {
804             instance.ContextActionManager.RegisterContextAction(handler);
805         }
806
807         public void AddTab(RadegastTab tab)
808         {
809             ToolStripButton button = (ToolStripButton)tstTabs.Items.Add(tab.Label);
810             button.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
811             button.Image = null;
812             button.AutoToolTip = false;
813             button.Tag = tab.Name;
814             button.Click += new EventHandler(TabButtonClick);
815             tab.Button = button;
816             tabs.Add(tab.Name, tab);
817
818             if (OnTabAdded != null)
819             {
820                 try { OnTabAdded(this, new TabEventArgs(tab)); }
821                 catch (Exception) { }
822             }
823         }
824
825         public RadegastTab AddTab(string name, string label, Control control)
826         {
827             // WORKAROUND: one should never add tab that alrady exists
828             // but under some weird conditions disconnect/connect
829             // fire in the wrong order
830             if (TabExists(name))
831             {
832                 Logger.Log("Force closing tab '" + name + "'", Helpers.LogLevel.Warning, client);
833                 ForceCloseTab(name);
834             }
835
836             control.Visible = false;
837             control.Dock = DockStyle.Fill;
838             toolStripContainer1.ContentPanel.Controls.Add(control);
839
840             ToolStripButton button = (ToolStripButton)tstTabs.Items.Add(label);
841             button.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
842             button.Image = null;
843             button.AutoToolTip = false;
844             button.Tag = name.ToLower();
845             button.AllowDrop = true;
846             button.Click += new EventHandler(TabButtonClick);
847
848             RadegastTab tab = new RadegastTab(instance, button, control, name.ToLower(), label);
849             if (control is RadegastTabControl)
850                 ((RadegastTabControl)control).RadegastTab = tab;
851             tab.TabAttached += new EventHandler(tab_TabAttached);
852             tab.TabDetached += new EventHandler(tab_TabDetached);
853             tab.TabSelected += new EventHandler(tab_TabSelected);
854             tab.TabClosed += new EventHandler(tab_TabClosed);
855             tab.TabHidden += new EventHandler(tab_TabHidden);
856             tabs.Add(name.ToLower(), tab);
857
858             if (OnTabAdded != null)
859             {
860                 try { OnTabAdded(this, new TabEventArgs(tab)); }
861                 catch (Exception) { }
862             }
863
864             button.MouseDown += (msender, margs) =>
865             {
866                 if (margs.Button == MouseButtons.Middle)
867                 {
868                     if (tab.AllowClose)
869                     {
870                         tab.Close();
871                     }
872                     else if (tab.AllowHide)
873                     {
874                         tab.Hide();
875                     }
876                 }
877             };
878
879             return tab;
880         }
881
882         private void tab_TabAttached(object sender, EventArgs e)
883         {
884             RadegastTab tab = (RadegastTab)sender;
885             tab.Select();
886         }
887
888         private void tab_TabDetached(object sender, EventArgs e)
889         {
890             RadegastTab tab = (RadegastTab)sender;
891             frmDetachedTab form = (frmDetachedTab)tab.Owner;
892
893             form.ReattachStrip = tstTabs;
894             form.ReattachContainer = toolStripContainer1.ContentPanel;
895         }
896
897         private void tab_TabSelected(object sender, EventArgs e)
898         {
899             RadegastTab tab = (RadegastTab)sender;
900
901             if (selectedTab != null &&
902                 selectedTab != tab)
903             { selectedTab.Deselect(); }
904
905             selectedTab = tab;
906
907             tbtnCloseTab.Enabled = !tab.Merged && (tab.AllowClose || tab.AllowHide);
908
909             if (owner != null)
910             {
911                 owner.AcceptButton = tab.DefaultControlButton;
912             }
913
914             if (OnTabSelected != null)
915             {
916                 try { OnTabSelected(this, new TabEventArgs(selectedTab)); }
917                 catch (Exception) { }
918             }
919         }
920
921         void tab_TabHidden(object sender, EventArgs e)
922         {
923             RadegastTab tab = (RadegastTab)sender;
924
925             if (selectedTab != null && selectedTab == tab)
926             {
927                 tab.Deselect();
928                 SelectDefaultTab();
929             }
930         }
931
932         private void tab_TabClosed(object sender, EventArgs e)
933         {
934             RadegastTab tab = (RadegastTab)sender;
935
936             if (selectedTab != null && selectedTab == tab && tab.Name != "chat")
937             {
938                 tab.Deselect();
939                 SelectDefaultTab();
940             }
941
942             tabs.Remove(tab.Name);
943
944             if (OnTabRemoved != null)
945             {
946                 try { OnTabRemoved(this, new TabEventArgs(tab)); }
947                 catch (Exception) { }
948             }
949
950             tab = null;
951         }
952
953         private void TabButtonClick(object sender, EventArgs e)
954         {
955             ToolStripButton button = (ToolStripButton)sender;
956
957             RadegastTab tab = tabs[button.Tag.ToString()];
958             tab.Select();
959         }
960
961         public void RemoveTabEntry(RadegastTab tab)
962         {
963             if (tstTabs.Items.Contains(tab.Button))
964             {
965                 tstTabs.Items.Remove(tab.Button);
966             }
967
968             tab.Button.Dispose();
969             tabs.Remove(tab.Name);
970         }
971
972         public void RemoveTab(string name)
973         {
974             if (tabs.ContainsKey(name))
975             {
976                 tabs.Remove(name);
977             }
978         }
979
980         //Used for outside classes that have a reference to TabsConsole
981         public void SelectTab(string name)
982         {
983             if (TabExists(name.ToLower()))
984                 tabs[name.ToLower()].Select();
985         }
986
987         public bool TabExists(string name)
988         {
989             return tabs.ContainsKey(name.ToLower());
990         }
991
992         public RadegastTab GetTab(string name)
993         {
994             if (TabExists(name.ToLower()))
995                 return tabs[name.ToLower()];
996             else
997                 return null;
998         }
999
1000         public List<RadegastTab> GetOtherTabs()
1001         {
1002             List<RadegastTab> otherTabs = new List<RadegastTab>();
1003
1004             foreach (ToolStripItem item in tstTabs.Items)
1005             {
1006                 if (item.Tag == null) continue;
1007                 if ((ToolStripItem)item == selectedTab.Button) continue;
1008
1009                 RadegastTab tab = tabs[item.Tag.ToString()];
1010                 if (!tab.AllowMerge) continue;
1011                 if (tab.Merged) continue;
1012
1013                 otherTabs.Add(tab);
1014             }
1015
1016             return otherTabs;
1017         }
1018
1019         /// <summary>
1020         /// Activates the next tab
1021         /// </summary>
1022         public void SelectNextTab()
1023         {
1024             List<ToolStripItem> buttons = new List<ToolStripItem>();
1025
1026             foreach (ToolStripItem item in tstTabs.Items)
1027             {
1028                 if (item.Tag == null || !item.Visible) continue;
1029
1030                 buttons.Add(item);
1031             }
1032
1033             int current = 0;
1034
1035             for (int i = 0; i < buttons.Count; i++)
1036             {
1037                 if (buttons[i] == selectedTab.Button)
1038                 {
1039                     current = i;
1040                     break;
1041                 }
1042             }
1043
1044             current++;
1045
1046             if (current == buttons.Count)
1047                 current = 0;
1048
1049             SelectTab(tabs[buttons[current].Tag.ToString()].Name);
1050         }
1051
1052         /// <summary>
1053         /// Activates the previous tab
1054         /// </summary>
1055         public void SelectPreviousTab()
1056         {
1057             List<ToolStripItem> buttons = new List<ToolStripItem>();
1058
1059             foreach (ToolStripItem item in tstTabs.Items)
1060             {
1061                 if (item.Tag == null || !item.Visible) continue;
1062
1063                 buttons.Add(item);
1064             }
1065
1066             int current = 0;
1067
1068             for (int i = 0; i < buttons.Count; i++)
1069             {
1070                 if (buttons[i] == selectedTab.Button)
1071                 {
1072                     current = i;
1073                     break;
1074                 }
1075             }
1076
1077             current--;
1078
1079             if (current == -1)
1080                 current = buttons.Count - 1;
1081
1082             SelectTab(tabs[buttons[current].Tag.ToString()].Name);
1083         }
1084
1085
1086         public IMTabWindow AddIMTab(UUID target, UUID session, string targetName)
1087         {
1088             IMTabWindow imTab = new IMTabWindow(instance, target, session, targetName);
1089
1090             RadegastTab tab = AddTab(session.ToString(), "IM: " + targetName, imTab);
1091             imTab.SelectIMInput();
1092
1093             return imTab;
1094         }
1095
1096         public ConferenceIMTabWindow AddConferenceIMTab(UUID session, string name)
1097         {
1098             ConferenceIMTabWindow imTab = new ConferenceIMTabWindow(instance, session, name);
1099
1100             RadegastTab tab = AddTab(session.ToString(), name, imTab);
1101             imTab.SelectIMInput();
1102
1103             return imTab;
1104         }
1105
1106         public GroupIMTabWindow AddGroupIMTab(UUID session, string name)
1107         {
1108             GroupIMTabWindow imTab = new GroupIMTabWindow(instance, session, name);
1109
1110             RadegastTab tab = AddTab(session.ToString(), name, imTab);
1111             imTab.SelectIMInput();
1112
1113             return imTab;
1114         }
1115
1116         public OutfitTextures AddOTTab(Avatar avatar)
1117         {
1118             OutfitTextures otTab = new OutfitTextures(instance, avatar);
1119
1120             RadegastTab tab = AddTab("OT: " + avatar.Name, "OT: " + avatar.Name, otTab);
1121             otTab.GetTextures();
1122             return otTab;
1123         }
1124
1125         public MasterTab AddMSTab(Avatar avatar)
1126         {
1127             MasterTab msTab = new MasterTab(instance, avatar);
1128
1129             RadegastTab tab = AddTab("MS: " + avatar.Name, "MS: " + avatar.Name, msTab);
1130             return msTab;
1131         }
1132
1133         public AnimTab AddAnimTab(Avatar avatar)
1134         {
1135             AnimTab animTab = new AnimTab(instance, avatar);
1136
1137             RadegastTab tab = AddTab("Anim: " + avatar.Name, "Anim: " + avatar.Name, animTab);
1138             return animTab;
1139         }
1140
1141         private void tbtnTabOptions_Click(object sender, EventArgs e)
1142         {
1143             tmnuMergeWith.Enabled = selectedTab.AllowMerge;
1144             tmnuDetachTab.Enabled = selectedTab.AllowDetach;
1145
1146             tmnuMergeWith.DropDown.Items.Clear();
1147
1148             if (!selectedTab.AllowMerge) return;
1149             if (!selectedTab.Merged)
1150             {
1151                 tmnuMergeWith.Text = "Merge With";
1152
1153                 List<RadegastTab> otherTabs = GetOtherTabs();
1154
1155                 tmnuMergeWith.Enabled = (otherTabs.Count > 0);
1156                 if (!tmnuMergeWith.Enabled) return;
1157
1158                 foreach (RadegastTab tab in otherTabs)
1159                 {
1160                     ToolStripItem item = tmnuMergeWith.DropDown.Items.Add(tab.Label);
1161                     item.Tag = tab.Name;
1162                     item.Click += new EventHandler(MergeItemClick);
1163                 }
1164             }
1165             else
1166             {
1167                 tmnuMergeWith.Text = "Split";
1168                 tmnuMergeWith.Click += new EventHandler(SplitClick);
1169             }
1170         }
1171
1172         private void MergeItemClick(object sender, EventArgs e)
1173         {
1174             ToolStripItem item = (ToolStripItem)sender;
1175             RadegastTab tab = tabs[item.Tag.ToString()];
1176
1177             selectedTab.MergeWith(tab);
1178
1179             SplitContainer container = (SplitContainer)selectedTab.Control;
1180             toolStripContainer1.ContentPanel.Controls.Add(container);
1181
1182             selectedTab.Select();
1183             RemoveTabEntry(tab);
1184
1185             tabs.Add(tab.Name, selectedTab);
1186         }
1187
1188         private void SplitClick(object sender, EventArgs e)
1189         {
1190             SplitTab(selectedTab);
1191             selectedTab.Select();
1192         }
1193
1194         public void SplitTab(RadegastTab tab)
1195         {
1196             RadegastTab otherTab = tab.Split();
1197             if (otherTab == null) return;
1198
1199             toolStripContainer1.ContentPanel.Controls.Add(tab.Control);
1200             toolStripContainer1.ContentPanel.Controls.Add(otherTab.Control);
1201
1202             tabs.Remove(otherTab.Name);
1203             AddTab(otherTab);
1204         }
1205
1206         private void tmnuDetachTab_Click(object sender, EventArgs e)
1207         {
1208             if (!selectedTab.AllowDetach) return;
1209             RadegastTab tab = selectedTab;
1210             SelectDefaultTab();
1211             tab.Detach(instance);
1212         }
1213
1214         private void tbtnCloseTab_Click(object sender, EventArgs e)
1215         {
1216             RadegastTab tab = selectedTab;
1217             if (tab.Merged)
1218                 return;
1219             else if (tab.AllowClose)
1220                 tab.Close();
1221             else if (tab.AllowHide)
1222                 tab.Hide();
1223         }
1224
1225         private void TabsConsole_Load(object sender, EventArgs e)
1226         {
1227             owner = this.FindForm();
1228         }
1229
1230         private void ctxTabs_Opening(object sender, CancelEventArgs e)
1231         {
1232             e.Cancel = false;
1233
1234             Point pt = this.PointToClient(Cursor.Position);
1235             ToolStripItem stripItem = tstTabs.GetItemAt(pt);
1236
1237             if (stripItem == null)
1238             {
1239                 e.Cancel = true;
1240             }
1241             else
1242             {
1243                 tabs[stripItem.Tag.ToString()].Select();
1244
1245                 ctxBtnClose.Enabled = !selectedTab.Merged && (selectedTab.AllowClose || selectedTab.AllowHide);
1246                 ctxBtnDetach.Enabled = selectedTab.AllowDetach;
1247                 ctxBtnMerge.Enabled = selectedTab.AllowMerge;
1248                 ctxBtnMerge.DropDown.Items.Clear();
1249
1250                 if (!ctxBtnClose.Enabled && !ctxBtnDetach.Enabled && !ctxBtnMerge.Enabled)
1251                 {
1252                     e.Cancel = true;
1253                     return;
1254                 }
1255
1256                 if (!selectedTab.AllowMerge) return;
1257                 if (!selectedTab.Merged)
1258                 {
1259                     ctxBtnMerge.Text = "Merge With";
1260
1261                     List<RadegastTab> otherTabs = GetOtherTabs();
1262
1263                     ctxBtnMerge.Enabled = (otherTabs.Count > 0);
1264                     if (!ctxBtnMerge.Enabled) return;
1265
1266                     foreach (RadegastTab tab in otherTabs)
1267                     {
1268                         ToolStripItem item = ctxBtnMerge.DropDown.Items.Add(tab.Label);
1269                         item.Tag = tab.Name;
1270                         item.Click += new EventHandler(MergeItemClick);
1271                     }
1272                 }
1273                 else
1274                 {
1275                     ctxBtnMerge.Text = "Split";
1276                     ctxBtnMerge.Click += new EventHandler(SplitClick);
1277                 }
1278
1279             }
1280         }
1281
1282     }
1283
1284     /// <summary>
1285     /// Arguments for tab events
1286     /// </summary>
1287     public class TabEventArgs : EventArgs
1288     {
1289         /// <summary>
1290         /// Tab that was manipulated in the event
1291         /// </summary>
1292         public RadegastTab Tab;
1293
1294         public TabEventArgs()
1295             : base()
1296         {
1297         }
1298
1299         public TabEventArgs(RadegastTab tab)
1300             : base()
1301         {
1302             Tab = tab;
1303         }
1304     }
1305
1306     /// <summary>
1307     /// Argument for chat notification events
1308     /// </summary>
1309     public class ChatNotificationEventArgs : EventArgs
1310     {
1311         public string Message;
1312         public ChatBufferTextStyle Style;
1313
1314         public ChatNotificationEventArgs(string message, ChatBufferTextStyle style)
1315         {
1316             Message = message;
1317             Style = style;
1318         }
1319     }
1320
1321     /// <summary>
1322     /// Element of list of nearby avatars
1323     /// </summary>
1324     public class NearbyAvatar
1325     {
1326         public UUID ID { get; set; }
1327         public string Name { get; set; }
1328     }
1329
1330 }