OSDN Git Service

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