OSDN Git Service

RAD-261: Add Tools -> My attachments menu for easily seeing own attachments
[radegast/radegast.git] / Radegast / GUI / Consoles / TabsConsole.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2011, 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
112         private Dictionary<string, RadegastTab> tabs = new Dictionary<string, RadegastTab>();
113         public Dictionary<string, RadegastTab> Tabs { get { return tabs; } }
114
115         private ChatConsole chatConsole;
116
117         private RadegastTab selectedTab;
118
119         /// <summary>
120         /// Currently selected tab
121         /// </summary>
122         public RadegastTab SelectedTab
123         {
124             get
125             {
126                 return selectedTab;
127             }
128         }
129
130         private Form owner;
131
132         public TabsConsole(RadegastInstance instance)
133         {
134             InitializeComponent();
135             Disposed += new EventHandler(TabsConsole_Disposed);
136
137             this.instance = instance;
138             this.instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
139
140             AddNetcomEvents();
141
142             InitializeMainTab();
143             InitializeChatTab();
144
145             // Callbacks
146             RegisterClientEvents(client);
147         }
148
149         private void RegisterClientEvents(GridClient client)
150         {
151             client.Self.ScriptQuestion += new EventHandler<ScriptQuestionEventArgs>(Self_ScriptQuestion);
152             client.Self.ScriptDialog += new EventHandler<ScriptDialogEventArgs>(Self_ScriptDialog);
153             client.Self.LoadURL += new EventHandler<LoadUrlEventArgs>(Self_LoadURL);
154             client.Self.SetDisplayNameReply += new EventHandler<SetDisplayNameReplyEventArgs>(Self_SetDisplayNameReply);
155             client.Avatars.DisplayNameUpdate += new EventHandler<DisplayNameUpdateEventArgs>(Avatars_DisplayNameUpdate);
156             client.Network.EventQueueRunning += new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
157         }
158
159         private void UnregisterClientEvents(GridClient client)
160         {
161             client.Self.ScriptQuestion -= new EventHandler<ScriptQuestionEventArgs>(Self_ScriptQuestion);
162             client.Self.ScriptDialog -= new EventHandler<ScriptDialogEventArgs>(Self_ScriptDialog);
163             client.Self.LoadURL -= new EventHandler<LoadUrlEventArgs>(Self_LoadURL);
164             client.Self.SetDisplayNameReply -= new EventHandler<SetDisplayNameReplyEventArgs>(Self_SetDisplayNameReply);
165             client.Avatars.DisplayNameUpdate -= new EventHandler<DisplayNameUpdateEventArgs>(Avatars_DisplayNameUpdate);
166             client.Network.EventQueueRunning -= new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
167         }
168
169         void instance_ClientChanged(object sender, ClientChangedEventArgs e)
170         {
171             UnregisterClientEvents(e.OldClient);
172             RegisterClientEvents(client);
173         }
174
175         void TabsConsole_Disposed(object sender, EventArgs e)
176         {
177             RemoveNetcomEvents();
178             UnregisterClientEvents(client);
179         }
180
181         private void AddNetcomEvents()
182         {
183             netcom.ClientLoginStatus += new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
184             netcom.ClientLoggedOut += new EventHandler(netcom_ClientLoggedOut);
185             netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
186             netcom.ChatSent += new EventHandler<ChatSentEventArgs>(netcom_ChatSent);
187             netcom.AlertMessageReceived += new EventHandler<AlertMessageEventArgs>(netcom_AlertMessageReceived);
188             netcom.InstantMessageReceived += new EventHandler<InstantMessageEventArgs>(netcom_InstantMessageReceived);
189         }
190
191         private void RemoveNetcomEvents()
192         {
193             netcom.ClientLoginStatus -= new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
194             netcom.ClientLoggedOut -= new EventHandler(netcom_ClientLoggedOut);
195             netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
196             netcom.ChatSent -= new EventHandler<ChatSentEventArgs>(netcom_ChatSent);
197             netcom.AlertMessageReceived -= new EventHandler<AlertMessageEventArgs>(netcom_AlertMessageReceived);
198             netcom.InstantMessageReceived -= new EventHandler<InstantMessageEventArgs>(netcom_InstantMessageReceived);
199         }
200
201         void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e)
202         {
203             if (InvokeRequired)
204             {
205                 BeginInvoke(new MethodInvoker(() => Network_EventQueueRunning(sender, e)));
206                 return;
207             }
208
209             if (TabExists("friends")) return;
210             if (e.Simulator == client.Network.CurrentSim)
211             {
212                 InitializeOnlineTabs();
213             }
214         }
215
216         void Self_ScriptDialog(object sender, ScriptDialogEventArgs e)
217         {
218             // Is this object muted
219             if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.ObjectID) // muted object by id
220                 || (m.Type == MuteType.ByName && m.Name == e.ObjectName) // object muted by name
221                 )) return;
222             
223             instance.MainForm.AddNotification(new ntfScriptDialog(instance, e.Message, e.ObjectName, e.ImageID, e.ObjectID, e.FirstName, e.LastName, e.Channel, e.ButtonLabels));
224         }
225
226         void Self_ScriptQuestion(object sender, ScriptQuestionEventArgs e)
227         {
228             // Is this object muted
229             if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.TaskID) // muted object by id
230                 || (m.Type == MuteType.ByName && m.Name == e.ObjectName) // object muted by name
231                 )) return;
232
233             instance.MainForm.AddNotification(new ntfPermissions(instance, e.Simulator, e.TaskID, e.ItemID, e.ObjectName, e.ObjectOwnerName, e.Questions));
234         }
235
236         private void netcom_ClientLoginStatus(object sender, LoginProgressEventArgs e)
237         {
238             if (e.Status == LoginStatus.Failed)
239             {
240                 DisplayNotificationInChat("Login error: " + e.Message, ChatBufferTextStyle.Error);
241             }
242             else if (e.Status == LoginStatus.Success)
243             {
244                 DisplayNotificationInChat("Logged in as " + netcom.LoginOptions.FullName + ".", ChatBufferTextStyle.StatusDarkBlue);
245                 DisplayNotificationInChat("Login reply: " + e.Message, ChatBufferTextStyle.StatusDarkBlue);
246
247                 if (tabs.ContainsKey("login"))
248                 {
249                     if (selectedTab.Name == "login")
250                         SelectDefaultTab();
251                     ForceCloseTab("login");
252                 }
253
254                 client.Self.RetrieveInstantMessages();
255             }
256         }
257
258         private void netcom_ClientLoggedOut(object sender, EventArgs e)
259         {
260             DisposeOnlineTabs();
261
262             SelectDefaultTab();
263             DisplayNotificationInChat("Logged out.");
264
265         }
266
267         private void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
268         {
269             if (e.Reason == NetworkManager.DisconnectType.ClientInitiated) return;
270
271             DisposeOnlineTabs();
272             SelectDefaultTab();
273             DisplayNotificationInChat("Disconnected: " + e.Message, ChatBufferTextStyle.Error);
274         }
275
276         void Avatars_DisplayNameUpdate(object sender, DisplayNameUpdateEventArgs e)
277         {
278             DisplayNotificationInChat(string.Format("({0}) is now known as {1}", e.DisplayName.UserName, e.DisplayName.DisplayName));
279         }
280
281         void Self_SetDisplayNameReply(object sender, SetDisplayNameReplyEventArgs e)
282         {
283             if (e.Status == 200)
284             {
285                 DisplayNotificationInChat("You are now knows as " + e.DisplayName.DisplayName);
286             }
287             else
288             {
289                 DisplayNotificationInChat("Failed to set a new display name: " + e.Reason, ChatBufferTextStyle.Error);
290             }
291         }
292
293         private void netcom_AlertMessageReceived(object sender, AlertMessageEventArgs e)
294         {
295             tabs["chat"].Highlight();
296         }
297
298         private void netcom_ChatSent(object sender, ChatSentEventArgs e)
299         {
300             tabs["chat"].Highlight();
301         }
302
303         void Self_LoadURL(object sender, LoadUrlEventArgs e)
304         {
305             // Is the object or the owner muted?
306             if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.ObjectID) // muted object by id 
307                 || (m.Type == MuteType.ByName && m.Name == e.ObjectName) // object muted by name
308                 || (m.Type == MuteType.Resident && m.ID == e.OwnerID) // object's owner muted
309                 )) return;
310
311             instance.MainForm.AddNotification(new ntfLoadURL(instance, e));
312         }
313
314         private void netcom_InstantMessageReceived(object sender, InstantMessageEventArgs e)
315         {
316             // Messaage from someone we muted?
317             if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Resident && me.ID == e.IM.FromAgentID)) return;
318
319             switch (e.IM.Dialog)
320             {
321                 case InstantMessageDialog.SessionSend:
322                     if (instance.Groups.ContainsKey(e.IM.IMSessionID))
323                     {
324                         HandleGroupIM(e);
325                     }
326                     else
327                     {
328                         HandleConferenceIM(e);
329                     }
330                     break;
331
332                 case InstantMessageDialog.MessageFromAgent:
333                     if (e.IM.FromAgentName == "Second Life")
334                     {
335                         HandleIMFromObject(e);
336                     }
337                     else if (e.IM.GroupIM || instance.Groups.ContainsKey(e.IM.IMSessionID))
338                     {
339                         HandleGroupIM(e);
340                     }
341                     else if (e.IM.BinaryBucket.Length > 1)
342                     { // conference
343                         HandleConferenceIM(e);
344                     }
345                     else
346                     {
347                         HandleIM(e);
348                     }
349                     break;
350
351                 case InstantMessageDialog.MessageFromObject:
352                     HandleIMFromObject(e);
353                     break;
354
355                 case InstantMessageDialog.StartTyping:
356                     if (TabExists(e.IM.FromAgentName))
357                     {
358                         RadegastTab tab = tabs[e.IM.FromAgentName.ToLower()];
359                         if (!tab.Highlighted) tab.PartialHighlight();
360                     }
361
362                     break;
363
364                 case InstantMessageDialog.StopTyping:
365                     if (TabExists(e.IM.FromAgentName))
366                     {
367                         RadegastTab tab = tabs[e.IM.FromAgentName.ToLower()];
368                         if (!tab.Highlighted) tab.Unhighlight();
369                     }
370
371                     break;
372
373                 case InstantMessageDialog.MessageBox:
374                     instance.MainForm.AddNotification(new ntfGeneric(instance, e.IM.Message));
375                     break;
376
377                 case InstantMessageDialog.RequestTeleport:
378                     if (instance.RLV.AutoAcceptTP(e.IM.FromAgentID))
379                     {
380                         DisplayNotificationInChat("Auto accepting teleprot from " + e.IM.FromAgentName);
381                         instance.Client.Self.TeleportLureRespond(e.IM.FromAgentID, e.IM.IMSessionID, true);
382                     }
383                     else
384                     {
385                         instance.MainForm.AddNotification(new ntfTeleport(instance, e.IM));
386                     }
387                     break;
388
389                 case InstantMessageDialog.GroupInvitation:
390                     instance.MainForm.AddNotification(new ntfGroupInvitation(instance, e.IM));
391                     break;
392
393                 case InstantMessageDialog.FriendshipOffered:
394                     if (e.IM.FromAgentName == "Second Life")
395                     {
396                         HandleIMFromObject(e);
397                     }
398                     else
399                     {
400                         instance.MainForm.AddNotification(new ntfFriendshipOffer(instance, e.IM));
401                     }
402                     break;
403
404                 case InstantMessageDialog.InventoryAccepted:
405                     DisplayNotificationInChat(e.IM.FromAgentName + " accepted your inventory offer.");
406                     break;
407
408                 case InstantMessageDialog.InventoryDeclined:
409                     DisplayNotificationInChat(e.IM.FromAgentName + " declined your inventory offer.");
410                     break;
411
412                 case InstantMessageDialog.GroupNotice:
413                     // Is this group muted?
414                     if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Group && me.ID == e.IM.FromAgentID)) break;
415
416                     instance.MainForm.AddNotification(new ntfGroupNotice(instance, e.IM));
417                     break;
418
419                 case InstantMessageDialog.InventoryOffered:
420                     instance.MainForm.AddNotification(new ntfInventoryOffer(instance, e.IM));
421                     break;
422
423                 case InstantMessageDialog.TaskInventoryOffered:
424                     // Is the object muted by name?
425                     if (null != client.Self.MuteList.Find(me => me.Type == MuteType.ByName && me.Name == e.IM.FromAgentName)) break;
426
427                     instance.MainForm.AddNotification(new ntfInventoryOffer(instance, e.IM));
428                     break;
429             }
430         }
431
432         /// <summary>
433         /// Make default tab (chat) active
434         /// </summary>
435         public void SelectDefaultTab()
436         {
437             if (TabExists("chat"))
438                 tabs["chat"].Select();
439         }
440
441         /// <summary>
442         /// Displays notification in the main chat tab
443         /// </summary>
444         /// <param name="msg">Message to be printed in the chat tab</param>
445         public void DisplayNotificationInChat(string msg)
446         {
447             DisplayNotificationInChat(msg, ChatBufferTextStyle.ObjectChat);
448         }
449
450         /// <summary>
451         /// Displays notification in the main chat tab
452         /// </summary>
453         /// <param name="msg">Message to be printed in the chat tab</param>
454         /// <param name="style">Style of the message to be printed, normal, object, etc.</param>
455         public void DisplayNotificationInChat(string msg, ChatBufferTextStyle style)
456         {
457             DisplayNotificationInChat(msg, style, true);
458         }
459
460         /// <summary>
461         /// Displays notification in the main chat tab
462         /// </summary>
463         /// <param name="msg">Message to be printed in the chat tab</param>
464         /// <param name="style">Style of the message to be printed, normal, object, etc.</param>
465         /// <param name="highlightChatTab">Highligt (and flash in taskbar) chat tab if not selected</param>
466         public void DisplayNotificationInChat(string msg, ChatBufferTextStyle style, bool highlightChatTab)
467         {
468             if (InvokeRequired)
469             {
470                 if (!instance.MonoRuntime || IsHandleCreated)
471                     BeginInvoke(new MethodInvoker(() => DisplayNotificationInChat(msg, style, highlightChatTab)));
472                 return;
473             }
474
475             if (style != ChatBufferTextStyle.Invisible)
476             {
477                 ChatBufferItem line = new ChatBufferItem(
478                     DateTime.Now,
479                     msg,
480                     style
481                 );
482
483                 try
484                 {
485                     mainChatManger.ProcessBufferItem(line, true);
486                     if (highlightChatTab)
487                     {
488                         tabs["chat"].Highlight();
489                     }
490                 }
491                 catch (Exception) { }
492             }
493
494             if (OnChatNotification != null)
495             {
496                 try { OnChatNotification(this, new ChatNotificationEventArgs(msg, style)); }
497                 catch { }
498             }
499         }
500
501         private void HandleIMFromObject(InstantMessageEventArgs e)
502         {
503             // Is the object or the owner muted?
504             if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.IM.IMSessionID) // muted object by id 
505                 || (m.Type == MuteType.ByName && m.Name == e.IM.FromAgentName) // object muted by name
506                 || (m.Type == MuteType.Resident && m.ID == e.IM.FromAgentID) // object's owner muted
507                 )) return;
508
509             DisplayNotificationInChat(e.IM.FromAgentName + ": " + e.IM.Message);
510         }
511
512         private void HandleIM(InstantMessageEventArgs e)
513         {
514             if (TabExists(e.IM.IMSessionID.ToString()))
515             {
516                 RadegastTab tab = tabs[e.IM.IMSessionID.ToString()];
517                 if (!tab.Selected) tab.Highlight();
518                 return;
519             }
520             
521             instance.MediaManager.PlayUISound(UISounds.IM);
522
523             IMTabWindow imTab = AddIMTab(e);
524             tabs[e.IM.IMSessionID.ToString()].Highlight();
525         }
526
527         private void HandleConferenceIM(InstantMessageEventArgs e)
528         {
529             if (TabExists(e.IM.IMSessionID.ToString()))
530             {
531                 RadegastTab tab = tabs[e.IM.IMSessionID.ToString()];
532                 if (!tab.Selected) tab.Highlight();
533                 return;
534             }
535
536             instance.MediaManager.PlayUISound(UISounds.IM);
537
538             ConferenceIMTabWindow imTab = AddConferenceIMTab(e);
539             tabs[e.IM.IMSessionID.ToString()].Highlight();
540         }
541
542         private void HandleGroupIM(InstantMessageEventArgs e)
543         {
544             // Ignore group IM from a muted group
545             if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Group && (me.ID == e.IM.IMSessionID || me.ID == e.IM.FromAgentID))) return;
546
547             if (TabExists(e.IM.IMSessionID.ToString()))
548             {
549                 RadegastTab tab = tabs[e.IM.IMSessionID.ToString()];
550                 if (!tab.Selected) tab.Highlight();
551                 return;
552             }
553
554             instance.MediaManager.PlayUISound(UISounds.IM);
555
556             GroupIMTabWindow imTab = AddGroupIMTab(e);
557             tabs[e.IM.IMSessionID.ToString()].Highlight();
558         }
559
560         private void InitializeMainTab()
561         {
562             LoginConsole loginConsole = new LoginConsole(instance);
563
564             RadegastTab tab = AddTab("login", "Login", loginConsole);
565             tab.AllowClose = false;
566             tab.AllowDetach = false;
567             tab.AllowMerge = false;
568             tab.AllowHide = false;
569
570             loginConsole.RegisterTab(tab);
571         }
572
573         private void InitializeChatTab()
574         {
575             chatConsole = new ChatConsole(instance);
576             mainChatManger = chatConsole.ChatManager;
577
578             RadegastTab tab = AddTab("chat", "Chat", chatConsole);
579             tab.AllowClose = false;
580             tab.AllowDetach = false;
581             tab.AllowHide = false;
582         }
583
584
585         /// <summary>
586         /// Create Tabs that only make sense when connected
587         /// </summary>
588         private void InitializeOnlineTabs()
589         {
590             RadegastTab tab = AddTab("friends", "Friends", new FriendsConsole(instance));
591             tab.AllowClose = false;
592             tab.AllowDetach = true;
593             tab.Visible = false;
594
595             tab = AddTab("groups", "Groups", new GroupsConsole(instance));
596             tab.AllowClose = false;
597             tab.AllowDetach = true;
598             tab.Visible = false;
599
600             // Ugly workaround for a mono bug
601             InventoryConsole inv = new InventoryConsole(instance);
602             if (instance.MonoRuntime) inv.invTree.Scrollable = false;
603             tab = AddTab("inventory", "Inventory", inv);
604             if (instance.MonoRuntime) inv.invTree.Scrollable = true;
605             tab.AllowClose = false;
606             tab.AllowDetach = true;
607             tab.Visible = false;
608
609             tab = AddTab("search", "Search", new SearchConsole(instance));
610             tab.AllowClose = false;
611             tab.AllowDetach = true;
612             tab.Visible = false;
613
614             tab = AddTab("map", "Map", new MapConsole(instance));
615             tab.AllowClose = false;
616             tab.AllowDetach = true;
617             tab.Visible = false;
618
619             tab = AddTab("voice", "Voice", new VoiceConsole(instance));
620             tab.AllowClose = false;
621             tab.AllowDetach = true;
622             tab.Visible = false;
623         }
624
625         /// <summary>
626         /// Close tabs that make no sense when not connected
627         /// </summary>
628         private void DisposeOnlineTabs()
629         {
630             lock (tabs)
631             {
632                 ForceCloseTab("voice");
633                 ForceCloseTab("map");
634                 ForceCloseTab("search");
635                 ForceCloseTab("inventory");
636                 ForceCloseTab("groups");
637                 ForceCloseTab("friends");
638             }
639         }
640
641         private void ForceCloseTab(string name)
642         {
643             if (!TabExists(name)) return;
644
645             RadegastTab tab = tabs[name];
646             if (tab.Merged) SplitTab(tab);
647
648             tab.AllowClose = true;
649             tab.Close();
650             tab = null;
651         }
652
653
654         public void RegisterContextAction(Type libomvType, String label, EventHandler handler)
655         {
656             instance.ContextActionManager.RegisterContextAction(libomvType, label, handler);
657         }
658
659         public void RegisterContextAction(ContextAction handler)
660         {
661             instance.ContextActionManager.RegisterContextAction(handler);
662         }
663
664         public void AddTab(RadegastTab tab)
665         {
666             ToolStripButton button = (ToolStripButton)tstTabs.Items.Add(tab.Label);
667             button.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
668             button.Image = null;
669             button.AutoToolTip = false;
670             button.Tag = tab.Name;
671             button.Click += new EventHandler(TabButtonClick);
672             tab.Button = button;
673             tabs.Add(tab.Name, tab);
674
675             if (OnTabAdded != null)
676             {
677                 try { OnTabAdded(this, new TabEventArgs(tab)); }
678                 catch (Exception) { }
679             }
680         }
681
682         public RadegastTab AddTab(string name, string label, Control control)
683         {
684             // WORKAROUND: one should never add tab that alrady exists
685             // but under some weird conditions disconnect/connect
686             // fire in the wrong order
687             if (TabExists(name))
688             {
689                 Logger.Log("Force closing tab '" + name + "'", Helpers.LogLevel.Warning, client);
690                 ForceCloseTab(name);
691             }
692
693             control.Visible = false;
694             control.Dock = DockStyle.Fill;
695             toolStripContainer1.ContentPanel.Controls.Add(control);
696
697             ToolStripButton button = (ToolStripButton)tstTabs.Items.Add(label);
698             button.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
699             button.Image = null;
700             button.AutoToolTip = false;
701             button.Tag = name.ToLower();
702             button.Click += new EventHandler(TabButtonClick);
703
704             RadegastTab tab = new RadegastTab(instance, button, control, name.ToLower(), label);
705             if (control is RadegastTabControl)
706                 ((RadegastTabControl)control).RadegastTab = tab;
707             tab.TabAttached += new EventHandler(tab_TabAttached);
708             tab.TabDetached += new EventHandler(tab_TabDetached);
709             tab.TabSelected += new EventHandler(tab_TabSelected);
710             tab.TabClosed += new EventHandler(tab_TabClosed);
711             tab.TabHidden += new EventHandler(tab_TabHidden);
712             tabs.Add(name.ToLower(), tab);
713
714             if (OnTabAdded != null)
715             {
716                 try { OnTabAdded(this, new TabEventArgs(tab)); }
717                 catch (Exception) { }
718             }
719
720             return tab;
721         }
722
723         private void tab_TabAttached(object sender, EventArgs e)
724         {
725             RadegastTab tab = (RadegastTab)sender;
726             tab.Select();
727         }
728
729         private void tab_TabDetached(object sender, EventArgs e)
730         {
731             RadegastTab tab = (RadegastTab)sender;
732             frmDetachedTab form = (frmDetachedTab)tab.Owner;
733
734             form.ReattachStrip = tstTabs;
735             form.ReattachContainer = toolStripContainer1.ContentPanel;
736         }
737
738         private void tab_TabSelected(object sender, EventArgs e)
739         {
740             RadegastTab tab = (RadegastTab)sender;
741
742             if (selectedTab != null &&
743                 selectedTab != tab)
744             { selectedTab.Deselect(); }
745
746             selectedTab = tab;
747
748             tbtnCloseTab.Enabled = tab.AllowClose || tab.AllowHide;
749             
750             if (owner != null)
751             {
752                 owner.AcceptButton = tab.DefaultControlButton;
753             }
754
755             if (OnTabSelected != null)
756             {
757                 try { OnTabSelected(this, new TabEventArgs(selectedTab)); }
758                 catch (Exception) { }
759             }
760         }
761
762         void tab_TabHidden(object sender, EventArgs e)
763         {
764             RadegastTab tab = (RadegastTab)sender;
765
766             if (selectedTab != null && selectedTab == tab)
767             {
768                 tab.Deselect();
769                 SelectDefaultTab();
770             }
771         }
772
773         private void tab_TabClosed(object sender, EventArgs e)
774         {
775             RadegastTab tab = (RadegastTab)sender;
776
777             if (selectedTab != null && selectedTab == tab && tab.Name != "chat")
778             {
779                 tab.Deselect();
780                 SelectDefaultTab();
781             }
782
783             tabs.Remove(tab.Name);
784
785             if (OnTabRemoved != null)
786             {
787                 try { OnTabRemoved(this, new TabEventArgs(tab)); }
788                 catch (Exception) { }
789             }
790
791             tab = null;
792         }
793
794         private void TabButtonClick(object sender, EventArgs e)
795         {
796             ToolStripButton button = (ToolStripButton)sender;
797
798             RadegastTab tab = tabs[button.Tag.ToString()];
799             tab.Select();
800         }
801
802         public void RemoveTabEntry(RadegastTab tab)
803         {
804             if (tstTabs.Items.Contains(tab.Button))
805             {
806                 tstTabs.Items.Remove(tab.Button);
807             }
808
809             tab.Button.Dispose();
810             tabs.Remove(tab.Name);
811         }
812
813         public void RemoveTab(string name)
814         {
815             if (tabs.ContainsKey(name))
816             {
817                 tabs.Remove(name);
818             }
819         }
820
821         //Used for outside classes that have a reference to TabsConsole
822         public void SelectTab(string name)
823         {
824             if (TabExists(name.ToLower()))
825                 tabs[name.ToLower()].Select();
826         }
827
828         public bool TabExists(string name)
829         {
830             return tabs.ContainsKey(name.ToLower());
831         }
832
833         public RadegastTab GetTab(string name)
834         {
835             if (TabExists(name.ToLower()))
836                 return tabs[name.ToLower()];
837             else
838                 return null;
839         }
840
841         public List<RadegastTab> GetOtherTabs()
842         {
843             List<RadegastTab> otherTabs = new List<RadegastTab>();
844
845             foreach (ToolStripItem item in tstTabs.Items)
846             {
847                 if (item.Tag == null) continue;
848                 if ((ToolStripItem)item == selectedTab.Button) continue;
849
850                 RadegastTab tab = tabs[item.Tag.ToString()];
851                 if (!tab.AllowMerge) continue;
852                 if (tab.Merged) continue;
853
854                 otherTabs.Add(tab);
855             }
856
857             return otherTabs;
858         }
859
860         /// <summary>
861         /// Activates the next tab
862         /// </summary>
863         public void SelectNextTab()
864         {
865             List<ToolStripItem> buttons = new List<ToolStripItem>();
866
867             foreach (ToolStripItem item in tstTabs.Items)
868             {
869                 if (item.Tag == null || !item.Visible) continue;
870
871                 buttons.Add(item);
872             }
873
874             int current = 0;
875
876             for (int i = 0; i < buttons.Count; i++)
877             {
878                 if (buttons[i] == selectedTab.Button)
879                 {
880                     current = i;
881                     break;
882                 }
883             }
884
885             current++;
886
887             if (current == buttons.Count)
888                 current = 0;
889
890             SelectTab(tabs[buttons[current].Tag.ToString()].Name);
891         }
892
893         /// <summary>
894         /// Activates the previous tab
895         /// </summary>
896         public void SelectPreviousTab()
897         {
898             List<ToolStripItem> buttons = new List<ToolStripItem>();
899
900             foreach (ToolStripItem item in tstTabs.Items)
901             {
902                 if (item.Tag == null || !item.Visible) continue;
903
904                 buttons.Add(item);
905             }
906
907             int current = 0;
908
909             for (int i = 0; i < buttons.Count; i++)
910             {
911                 if (buttons[i] == selectedTab.Button)
912                 {
913                     current = i;
914                     break;
915                 }
916             }
917
918             current--;
919
920             if (current == -1)
921                 current = buttons.Count - 1;
922
923             SelectTab(tabs[buttons[current].Tag.ToString()].Name);
924         }
925
926
927         public IMTabWindow AddIMTab(UUID target, UUID session, string targetName)
928         {
929             IMTabWindow imTab = new IMTabWindow(instance, target, session, targetName);
930
931             RadegastTab tab = AddTab(session.ToString(), "IM: " + targetName, imTab);
932             imTab.SelectIMInput();
933             tab.Highlight();
934
935             return imTab;
936         }
937
938         public ConferenceIMTabWindow AddConferenceIMTab(UUID session, string name)
939         {
940             ConferenceIMTabWindow imTab = new ConferenceIMTabWindow(instance, session, name);
941
942             RadegastTab tab = AddTab(session.ToString(), name, imTab);
943             imTab.SelectIMInput();
944
945             return imTab;
946         }
947
948
949         public ConferenceIMTabWindow AddConferenceIMTab(InstantMessageEventArgs e)
950         {
951             ConferenceIMTabWindow imTab = AddConferenceIMTab(e.IM.IMSessionID, Utils.BytesToString(e.IM.BinaryBucket));
952             imTab.TextManager.ProcessIM(e);
953             return imTab;
954         }
955
956         public GroupIMTabWindow AddGroupIMTab(UUID session, string name)
957         {
958             GroupIMTabWindow imTab = new GroupIMTabWindow(instance, session, name);
959
960             RadegastTab tab = AddTab(session.ToString(), name, imTab);
961             imTab.SelectIMInput();
962
963             return imTab;
964         }
965
966         public GroupIMTabWindow AddGroupIMTab(InstantMessageEventArgs e)
967         {
968             GroupIMTabWindow imTab = AddGroupIMTab(e.IM.IMSessionID, Utils.BytesToString(e.IM.BinaryBucket));
969             imTab.TextManager.ProcessIM(e);
970             return imTab;
971         }
972
973         public IMTabWindow AddIMTab(InstantMessageEventArgs e)
974         {
975             IMTabWindow imTab = AddIMTab(e.IM.FromAgentID, e.IM.IMSessionID, e.IM.FromAgentName);
976             imTab.TextManager.ProcessIM(e);
977             return imTab;
978         }
979
980         public OutfitTextures AddOTTab(Avatar avatar)
981         {
982             OutfitTextures otTab = new OutfitTextures(instance, avatar);
983
984             RadegastTab tab = AddTab("OT: " + avatar.Name, "OT: " + avatar.Name, otTab);
985             otTab.GetTextures();
986             return otTab;
987         }
988
989         public MasterTab AddMSTab(Avatar avatar)
990         {
991             MasterTab msTab = new MasterTab(instance, avatar);
992
993             RadegastTab tab = AddTab("MS: " + avatar.Name, "MS: " + avatar.Name, msTab);
994             return msTab;
995         }
996
997         public AnimTab AddAnimTab(Avatar avatar)
998         {
999             AnimTab animTab = new AnimTab(instance, avatar);
1000
1001             RadegastTab tab = AddTab("Anim: " + avatar.Name, "Anim: " + avatar.Name, animTab);
1002             return animTab;
1003         }
1004
1005         private void tbtnTabOptions_Click(object sender, EventArgs e)
1006         {
1007             tmnuMergeWith.Enabled = selectedTab.AllowMerge;
1008             tmnuDetachTab.Enabled = selectedTab.AllowDetach;
1009
1010             tmnuMergeWith.DropDown.Items.Clear();
1011
1012             if (!selectedTab.AllowMerge) return;
1013             if (!selectedTab.Merged)
1014             {
1015                 tmnuMergeWith.Text = "Merge With";
1016
1017                 List<RadegastTab> otherTabs = GetOtherTabs();
1018
1019                 tmnuMergeWith.Enabled = (otherTabs.Count > 0);
1020                 if (!tmnuMergeWith.Enabled) return;
1021
1022                 foreach (RadegastTab tab in otherTabs)
1023                 {
1024                     ToolStripItem item = tmnuMergeWith.DropDown.Items.Add(tab.Label);
1025                     item.Tag = tab.Name;
1026                     item.Click += new EventHandler(MergeItemClick);
1027                 }
1028             }
1029             else
1030             {
1031                 tmnuMergeWith.Text = "Split";
1032                 tmnuMergeWith.Click += new EventHandler(SplitClick);
1033             }
1034         }
1035
1036         private void MergeItemClick(object sender, EventArgs e)
1037         {
1038             ToolStripItem item = (ToolStripItem)sender;
1039             RadegastTab tab = tabs[item.Tag.ToString()];
1040
1041             selectedTab.MergeWith(tab);
1042
1043             SplitContainer container = (SplitContainer)selectedTab.Control;
1044             toolStripContainer1.ContentPanel.Controls.Add(container);
1045
1046             selectedTab.Select();
1047             RemoveTabEntry(tab);
1048
1049             tabs.Add(tab.Name, selectedTab);
1050         }
1051
1052         private void SplitClick(object sender, EventArgs e)
1053         {
1054             SplitTab(selectedTab);
1055             selectedTab.Select();
1056         }
1057
1058         public void SplitTab(RadegastTab tab)
1059         {
1060             RadegastTab otherTab = tab.Split();
1061             if (otherTab == null) return;
1062
1063             toolStripContainer1.ContentPanel.Controls.Add(tab.Control);
1064             toolStripContainer1.ContentPanel.Controls.Add(otherTab.Control);
1065
1066             tabs.Remove(otherTab.Name);
1067             AddTab(otherTab);
1068         }
1069
1070         private void tmnuDetachTab_Click(object sender, EventArgs e)
1071         {
1072             if (!selectedTab.AllowDetach) return;
1073             RadegastTab tab = selectedTab;
1074             SelectDefaultTab();
1075             tab.Detach(instance);
1076         }
1077
1078         private void tbtnCloseTab_Click(object sender, EventArgs e)
1079         {
1080             RadegastTab tab = selectedTab;
1081             if (tab.AllowClose)
1082                 tab.Close();
1083             else if (tab.AllowHide)
1084                 tab.Hide();
1085         }
1086
1087         private void TabsConsole_Load(object sender, EventArgs e)
1088         {
1089             owner = this.FindForm();
1090         }
1091
1092         private void ctxTabs_Opening(object sender, CancelEventArgs e)
1093         {
1094             Point pt = this.PointToClient(Cursor.Position);
1095             ToolStripItem stripItem = tstTabs.GetItemAt(pt);
1096
1097             if (stripItem == null)
1098             {
1099                 e.Cancel = true;
1100             }
1101             else
1102             {
1103                 tabs[stripItem.Tag.ToString()].Select();
1104
1105                 ctxBtnClose.Enabled = selectedTab.AllowClose || selectedTab.AllowHide;
1106                 ctxBtnDetach.Enabled = selectedTab.AllowDetach;
1107                 ctxBtnMerge.Enabled = selectedTab.AllowMerge;
1108                 ctxBtnMerge.DropDown.Items.Clear();
1109
1110                 if (!ctxBtnClose.Enabled && !ctxBtnDetach.Enabled && !ctxBtnMerge.Enabled)
1111                 {
1112                     e.Cancel = true;
1113                     return;
1114                 }
1115
1116                 if (!selectedTab.AllowMerge) return;
1117                 if (!selectedTab.Merged)
1118                 {
1119                     ctxBtnMerge.Text = "Merge With";
1120
1121                     List<RadegastTab> otherTabs = GetOtherTabs();
1122
1123                     ctxBtnMerge.Enabled = (otherTabs.Count > 0);
1124                     if (!ctxBtnMerge.Enabled) return;
1125
1126                     foreach (RadegastTab tab in otherTabs)
1127                     {
1128                         ToolStripItem item = ctxBtnMerge.DropDown.Items.Add(tab.Label);
1129                         item.Tag = tab.Name;
1130                         item.Click += new EventHandler(MergeItemClick);
1131                     }
1132                 }
1133                 else
1134                 {
1135                     ctxBtnMerge.Text = "Split";
1136                     ctxBtnMerge.Click += new EventHandler(SplitClick);
1137                 }
1138
1139             }
1140         }
1141
1142     }
1143
1144     /// <summary>
1145     /// Arguments for tab events
1146     /// </summary>
1147     public class TabEventArgs : EventArgs
1148     {
1149         /// <summary>
1150         /// Tab that was manipulated in the event
1151         /// </summary>
1152         public RadegastTab Tab;
1153
1154         public TabEventArgs()
1155             : base()
1156         {
1157         }
1158
1159         public TabEventArgs(RadegastTab tab)
1160             : base()
1161         {
1162             Tab = tab;
1163         }
1164     }
1165
1166     /// <summary>
1167     /// Argument for chat notification events
1168     /// </summary>
1169     public class ChatNotificationEventArgs : EventArgs
1170     {
1171         public string Message;
1172         public ChatBufferTextStyle Style;
1173
1174         public ChatNotificationEventArgs(string message, ChatBufferTextStyle style)
1175         {
1176             Message = message;
1177             Style = style;
1178         }
1179     }
1180
1181     /// <summary>
1182     /// Element of list of nearby avatars
1183     /// </summary>
1184     public class NearbyAvatar
1185     {
1186         public UUID ID { get; set; }
1187         public string Name { get; set; }
1188     }
1189
1190 }