OSDN Git Service

Implemented group muting.
[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                     // Is this group muted?
399                     if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Group && me.ID == e.IM.FromAgentID)) break;
400
401                     instance.MainForm.AddNotification(new ntfGroupNotice(instance, e.IM));
402                     break;
403
404                 case InstantMessageDialog.InventoryOffered:
405                     instance.MainForm.AddNotification(new ntfInventoryOffer(instance, e.IM));
406                     break;
407
408                 case InstantMessageDialog.TaskInventoryOffered:
409                     // Is the object muted by name?
410                     if (null != client.Self.MuteList.Find(me => me.Type == MuteType.ByName && me.Name == e.IM.FromAgentName)) break;
411
412                     instance.MainForm.AddNotification(new ntfInventoryOffer(instance, e.IM));
413                     break;
414             }
415         }
416
417         /// <summary>
418         /// Make default tab (chat) active
419         /// </summary>
420         public void SelectDefaultTab()
421         {
422             if (TabExists("chat"))
423                 tabs["chat"].Select();
424         }
425
426         /// <summary>
427         /// Displays notification in the main chat tab
428         /// </summary>
429         /// <param name="msg">Message to be printed in the chat tab</param>
430         public void DisplayNotificationInChat(string msg)
431         {
432             DisplayNotificationInChat(msg, ChatBufferTextStyle.ObjectChat);
433         }
434
435         /// <summary>
436         /// Displays notification in the main chat tab
437         /// </summary>
438         /// <param name="msg">Message to be printed in the chat tab</param>
439         /// <param name="style">Style of the message to be printed, normal, object, etc.</param>
440         public void DisplayNotificationInChat(string msg, ChatBufferTextStyle style)
441         {
442             DisplayNotificationInChat(msg, style, true);
443         }
444
445         /// <summary>
446         /// Displays notification in the main chat tab
447         /// </summary>
448         /// <param name="msg">Message to be printed in the chat tab</param>
449         /// <param name="style">Style of the message to be printed, normal, object, etc.</param>
450         /// <param name="highlightChatTab">Highligt (and flash in taskbar) chat tab if not selected</param>
451         public void DisplayNotificationInChat(string msg, ChatBufferTextStyle style, bool highlightChatTab)
452         {
453             if (InvokeRequired)
454             {
455                 if (!instance.MonoRuntime || IsHandleCreated)
456                     BeginInvoke(new MethodInvoker(() => DisplayNotificationInChat(msg, style, highlightChatTab)));
457                 return;
458             }
459
460             if (style != ChatBufferTextStyle.Invisible)
461             {
462                 ChatBufferItem line = new ChatBufferItem(
463                     DateTime.Now,
464                     msg,
465                     style
466                 );
467
468                 try
469                 {
470                     mainChatManger.ProcessBufferItem(line, true);
471                     if (highlightChatTab)
472                     {
473                         tabs["chat"].Highlight();
474                     }
475                 }
476                 catch (Exception) { }
477             }
478
479             if (OnChatNotification != null)
480             {
481                 try { OnChatNotification(this, new ChatNotificationEventArgs(msg, style)); }
482                 catch { }
483             }
484         }
485
486         private void HandleIMFromObject(InstantMessageEventArgs e)
487         {
488             // Is the object or the owner muted?
489             if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.IM.IMSessionID) // muted object by id 
490                 || (m.Type == MuteType.ByName && m.Name == e.IM.FromAgentName) // object muted by name
491                 || (m.Type == MuteType.Resident && m.ID == e.IM.FromAgentID) // object's owner muted
492                 )) return;
493
494             DisplayNotificationInChat(e.IM.FromAgentName + ": " + e.IM.Message);
495         }
496
497         private void HandleIM(InstantMessageEventArgs e)
498         {
499             if (TabExists(e.IM.IMSessionID.ToString()))
500             {
501                 RadegastTab tab = tabs[e.IM.IMSessionID.ToString()];
502                 if (!tab.Selected) tab.Highlight();
503                 return;
504             }
505             
506             instance.MediaManager.PlayUISound(UISounds.IM);
507
508             IMTabWindow imTab = AddIMTab(e);
509             tabs[e.IM.IMSessionID.ToString()].Highlight();
510         }
511
512         private void HandleConferenceIM(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             ConferenceIMTabWindow imTab = AddConferenceIMTab(e);
524             tabs[e.IM.IMSessionID.ToString()].Highlight();
525         }
526
527         private void HandleGroupIM(InstantMessageEventArgs e)
528         {
529             // Ignore group IM from a muted group
530             if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Group && (me.ID == e.IM.IMSessionID || me.ID == e.IM.FromAgentID))) return;
531
532             if (TabExists(e.IM.IMSessionID.ToString()))
533             {
534                 RadegastTab tab = tabs[e.IM.IMSessionID.ToString()];
535                 if (!tab.Selected) tab.Highlight();
536                 return;
537             }
538
539             instance.MediaManager.PlayUISound(UISounds.IM);
540
541             GroupIMTabWindow imTab = AddGroupIMTab(e);
542             tabs[e.IM.IMSessionID.ToString()].Highlight();
543         }
544
545         private void InitializeMainTab()
546         {
547             LoginConsole loginConsole = new LoginConsole(instance);
548
549             RadegastTab tab = AddTab("login", "Login", loginConsole);
550             tab.AllowClose = false;
551             tab.AllowDetach = false;
552             tab.AllowMerge = false;
553             tab.AllowHide = false;
554
555             loginConsole.RegisterTab(tab);
556         }
557
558         private void InitializeChatTab()
559         {
560             chatConsole = new ChatConsole(instance);
561             mainChatManger = chatConsole.ChatManager;
562
563             RadegastTab tab = AddTab("chat", "Chat", chatConsole);
564             tab.AllowClose = false;
565             tab.AllowDetach = false;
566             tab.AllowHide = false;
567         }
568
569
570         /// <summary>
571         /// Create Tabs that only make sense when connected
572         /// </summary>
573         private void InitializeOnlineTabs()
574         {
575             RadegastTab tab = AddTab("friends", "Friends", new FriendsConsole(instance));
576             tab.AllowClose = false;
577             tab.AllowDetach = true;
578             tab.Visible = false;
579
580             tab = AddTab("groups", "Groups", new GroupsConsole(instance));
581             tab.AllowClose = false;
582             tab.AllowDetach = true;
583             tab.Visible = false;
584
585             // Ugly workaround for a mono bug
586             InventoryConsole inv = new InventoryConsole(instance);
587             if (instance.MonoRuntime) inv.invTree.Scrollable = false;
588             tab = AddTab("inventory", "Inventory", inv);
589             if (instance.MonoRuntime) inv.invTree.Scrollable = true;
590             tab.AllowClose = false;
591             tab.AllowDetach = true;
592             tab.Visible = false;
593
594             tab = AddTab("search", "Search", new SearchConsole(instance));
595             tab.AllowClose = false;
596             tab.AllowDetach = true;
597             tab.Visible = false;
598
599             tab = AddTab("map", "Map", new MapConsole(instance));
600             tab.AllowClose = false;
601             tab.AllowDetach = true;
602             tab.Visible = false;
603
604             tab = AddTab("voice", "Voice", new VoiceConsole(instance));
605             tab.AllowClose = false;
606             tab.AllowDetach = true;
607             tab.Visible = false;
608         }
609
610         /// <summary>
611         /// Close tabs that make no sense when not connected
612         /// </summary>
613         private void DisposeOnlineTabs()
614         {
615             lock (tabs)
616             {
617                 ForceCloseTab("voice");
618                 ForceCloseTab("map");
619                 ForceCloseTab("search");
620                 ForceCloseTab("inventory");
621                 ForceCloseTab("groups");
622                 ForceCloseTab("friends");
623             }
624         }
625
626         private void ForceCloseTab(string name)
627         {
628             if (!TabExists(name)) return;
629
630             RadegastTab tab = tabs[name];
631             if (tab.Merged) SplitTab(tab);
632
633             tab.AllowClose = true;
634             tab.Close();
635             tab = null;
636         }
637
638
639         public void RegisterContextAction(Type libomvType, String label, EventHandler handler)
640         {
641             instance.ContextActionManager.RegisterContextAction(libomvType, label, handler);
642         }
643
644         public void RegisterContextAction(ContextAction handler)
645         {
646             instance.ContextActionManager.RegisterContextAction(handler);
647         }
648
649         public void AddTab(RadegastTab tab)
650         {
651             ToolStripButton button = (ToolStripButton)tstTabs.Items.Add(tab.Label);
652             button.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
653             button.Image = null;
654             button.AutoToolTip = false;
655             button.Tag = tab.Name;
656             button.Click += new EventHandler(TabButtonClick);
657             tab.Button = button;
658             tabs.Add(tab.Name, tab);
659
660             if (OnTabAdded != null)
661             {
662                 try { OnTabAdded(this, new TabEventArgs(tab)); }
663                 catch (Exception) { }
664             }
665         }
666
667         public RadegastTab AddTab(string name, string label, Control control)
668         {
669             // WORKAROUND: one should never add tab that alrady exists
670             // but under some weird conditions disconnect/connect
671             // fire in the wrong order
672             if (TabExists(name))
673             {
674                 Logger.Log("Force closing tab '" + name + "'", Helpers.LogLevel.Warning, client);
675                 ForceCloseTab(name);
676             }
677
678             control.Visible = false;
679             control.Dock = DockStyle.Fill;
680             toolStripContainer1.ContentPanel.Controls.Add(control);
681
682             ToolStripButton button = (ToolStripButton)tstTabs.Items.Add(label);
683             button.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
684             button.Image = null;
685             button.AutoToolTip = false;
686             button.Tag = name.ToLower();
687             button.Click += new EventHandler(TabButtonClick);
688
689             RadegastTab tab = new RadegastTab(instance, button, control, name.ToLower(), label);
690             if (control is RadegastTabControl)
691                 ((RadegastTabControl)control).RadegastTab = tab;
692             tab.TabAttached += new EventHandler(tab_TabAttached);
693             tab.TabDetached += new EventHandler(tab_TabDetached);
694             tab.TabSelected += new EventHandler(tab_TabSelected);
695             tab.TabClosed += new EventHandler(tab_TabClosed);
696             tab.TabHidden += new EventHandler(tab_TabHidden);
697             tabs.Add(name.ToLower(), tab);
698
699             if (OnTabAdded != null)
700             {
701                 try { OnTabAdded(this, new TabEventArgs(tab)); }
702                 catch (Exception) { }
703             }
704
705             return tab;
706         }
707
708         private void tab_TabAttached(object sender, EventArgs e)
709         {
710             RadegastTab tab = (RadegastTab)sender;
711             tab.Select();
712         }
713
714         private void tab_TabDetached(object sender, EventArgs e)
715         {
716             RadegastTab tab = (RadegastTab)sender;
717             frmDetachedTab form = (frmDetachedTab)tab.Owner;
718
719             form.ReattachStrip = tstTabs;
720             form.ReattachContainer = toolStripContainer1.ContentPanel;
721         }
722
723         private void tab_TabSelected(object sender, EventArgs e)
724         {
725             RadegastTab tab = (RadegastTab)sender;
726
727             if (selectedTab != null &&
728                 selectedTab != tab)
729             { selectedTab.Deselect(); }
730
731             selectedTab = tab;
732
733             tbtnCloseTab.Enabled = tab.AllowClose || tab.AllowHide;
734             
735             if (owner != null)
736             {
737                 owner.AcceptButton = tab.DefaultControlButton;
738             }
739
740             if (OnTabSelected != null)
741             {
742                 try { OnTabSelected(this, new TabEventArgs(selectedTab)); }
743                 catch (Exception) { }
744             }
745         }
746
747         void tab_TabHidden(object sender, EventArgs e)
748         {
749             RadegastTab tab = (RadegastTab)sender;
750
751             if (selectedTab != null && selectedTab == tab)
752             {
753                 tab.Deselect();
754                 SelectDefaultTab();
755             }
756         }
757
758         private void tab_TabClosed(object sender, EventArgs e)
759         {
760             RadegastTab tab = (RadegastTab)sender;
761
762             if (selectedTab != null && selectedTab == tab && tab.Name != "chat")
763             {
764                 tab.Deselect();
765                 SelectDefaultTab();
766             }
767
768             tabs.Remove(tab.Name);
769
770             if (OnTabRemoved != null)
771             {
772                 try { OnTabRemoved(this, new TabEventArgs(tab)); }
773                 catch (Exception) { }
774             }
775
776             tab = null;
777         }
778
779         private void TabButtonClick(object sender, EventArgs e)
780         {
781             ToolStripButton button = (ToolStripButton)sender;
782
783             RadegastTab tab = tabs[button.Tag.ToString()];
784             tab.Select();
785         }
786
787         public void RemoveTabEntry(RadegastTab tab)
788         {
789             if (tstTabs.Items.Contains(tab.Button))
790             {
791                 tstTabs.Items.Remove(tab.Button);
792             }
793
794             tab.Button.Dispose();
795             tabs.Remove(tab.Name);
796         }
797
798         public void RemoveTab(string name)
799         {
800             if (tabs.ContainsKey(name))
801             {
802                 tabs.Remove(name);
803             }
804         }
805
806         //Used for outside classes that have a reference to TabsConsole
807         public void SelectTab(string name)
808         {
809             if (TabExists(name.ToLower()))
810                 tabs[name.ToLower()].Select();
811         }
812
813         public bool TabExists(string name)
814         {
815             return tabs.ContainsKey(name.ToLower());
816         }
817
818         public RadegastTab GetTab(string name)
819         {
820             if (TabExists(name.ToLower()))
821                 return tabs[name.ToLower()];
822             else
823                 return null;
824         }
825
826         public List<RadegastTab> GetOtherTabs()
827         {
828             List<RadegastTab> otherTabs = new List<RadegastTab>();
829
830             foreach (ToolStripItem item in tstTabs.Items)
831             {
832                 if (item.Tag == null) continue;
833                 if ((ToolStripItem)item == selectedTab.Button) continue;
834
835                 RadegastTab tab = tabs[item.Tag.ToString()];
836                 if (!tab.AllowMerge) continue;
837                 if (tab.Merged) continue;
838
839                 otherTabs.Add(tab);
840             }
841
842             return otherTabs;
843         }
844
845         /// <summary>
846         /// Activates the next tab
847         /// </summary>
848         public void SelectNextTab()
849         {
850             List<ToolStripItem> buttons = new List<ToolStripItem>();
851
852             foreach (ToolStripItem item in tstTabs.Items)
853             {
854                 if (item.Tag == null || !item.Visible) continue;
855
856                 buttons.Add(item);
857             }
858
859             int current = 0;
860
861             for (int i = 0; i < buttons.Count; i++)
862             {
863                 if (buttons[i] == selectedTab.Button)
864                 {
865                     current = i;
866                     break;
867                 }
868             }
869
870             current++;
871
872             if (current == buttons.Count)
873                 current = 0;
874
875             SelectTab(tabs[buttons[current].Tag.ToString()].Name);
876         }
877
878         /// <summary>
879         /// Activates the previous tab
880         /// </summary>
881         public void SelectPreviousTab()
882         {
883             List<ToolStripItem> buttons = new List<ToolStripItem>();
884
885             foreach (ToolStripItem item in tstTabs.Items)
886             {
887                 if (item.Tag == null || !item.Visible) continue;
888
889                 buttons.Add(item);
890             }
891
892             int current = 0;
893
894             for (int i = 0; i < buttons.Count; i++)
895             {
896                 if (buttons[i] == selectedTab.Button)
897                 {
898                     current = i;
899                     break;
900                 }
901             }
902
903             current--;
904
905             if (current == -1)
906                 current = buttons.Count - 1;
907
908             SelectTab(tabs[buttons[current].Tag.ToString()].Name);
909         }
910
911
912         public IMTabWindow AddIMTab(UUID target, UUID session, string targetName)
913         {
914             IMTabWindow imTab = new IMTabWindow(instance, target, session, targetName);
915
916             RadegastTab tab = AddTab(session.ToString(), "IM: " + targetName, imTab);
917             imTab.SelectIMInput();
918             tab.Highlight();
919
920             return imTab;
921         }
922
923         public ConferenceIMTabWindow AddConferenceIMTab(UUID session, string name)
924         {
925             ConferenceIMTabWindow imTab = new ConferenceIMTabWindow(instance, session, name);
926
927             RadegastTab tab = AddTab(session.ToString(), name, imTab);
928             imTab.SelectIMInput();
929
930             return imTab;
931         }
932
933
934         public ConferenceIMTabWindow AddConferenceIMTab(InstantMessageEventArgs e)
935         {
936             ConferenceIMTabWindow imTab = AddConferenceIMTab(e.IM.IMSessionID, Utils.BytesToString(e.IM.BinaryBucket));
937             imTab.TextManager.ProcessIM(e);
938             return imTab;
939         }
940
941         public GroupIMTabWindow AddGroupIMTab(UUID session, string name)
942         {
943             GroupIMTabWindow imTab = new GroupIMTabWindow(instance, session, name);
944
945             RadegastTab tab = AddTab(session.ToString(), name, imTab);
946             imTab.SelectIMInput();
947
948             return imTab;
949         }
950
951         public GroupIMTabWindow AddGroupIMTab(InstantMessageEventArgs e)
952         {
953             GroupIMTabWindow imTab = AddGroupIMTab(e.IM.IMSessionID, Utils.BytesToString(e.IM.BinaryBucket));
954             imTab.TextManager.ProcessIM(e);
955             return imTab;
956         }
957
958         public IMTabWindow AddIMTab(InstantMessageEventArgs e)
959         {
960             IMTabWindow imTab = AddIMTab(e.IM.FromAgentID, e.IM.IMSessionID, e.IM.FromAgentName);
961             imTab.TextManager.ProcessIM(e);
962             return imTab;
963         }
964
965         public OutfitTextures AddOTTab(Avatar avatar)
966         {
967             OutfitTextures otTab = new OutfitTextures(instance, avatar);
968
969             RadegastTab tab = AddTab("OT: " + avatar.Name, "OT: " + avatar.Name, otTab);
970             otTab.GetTextures();
971             return otTab;
972         }
973
974         public MasterTab AddMSTab(Avatar avatar)
975         {
976             MasterTab msTab = new MasterTab(instance, avatar);
977
978             RadegastTab tab = AddTab("MS: " + avatar.Name, "MS: " + avatar.Name, msTab);
979             return msTab;
980         }
981
982         public AttachmentTab AddATTab(Avatar avatar)
983         {
984             AttachmentTab atTab = new AttachmentTab(instance, avatar);
985
986             RadegastTab tab = AddTab("AT: " + avatar.Name, "AT: " + avatar.Name, atTab);
987             return atTab;
988         }
989
990         public AnimTab AddAnimTab(Avatar avatar)
991         {
992             AnimTab animTab = new AnimTab(instance, avatar);
993
994             RadegastTab tab = AddTab("Anim: " + avatar.Name, "Anim: " + avatar.Name, animTab);
995             return animTab;
996         }
997
998         private void tbtnTabOptions_Click(object sender, EventArgs e)
999         {
1000             tmnuMergeWith.Enabled = selectedTab.AllowMerge;
1001             tmnuDetachTab.Enabled = selectedTab.AllowDetach;
1002
1003             tmnuMergeWith.DropDown.Items.Clear();
1004
1005             if (!selectedTab.AllowMerge) return;
1006             if (!selectedTab.Merged)
1007             {
1008                 tmnuMergeWith.Text = "Merge With";
1009
1010                 List<RadegastTab> otherTabs = GetOtherTabs();
1011
1012                 tmnuMergeWith.Enabled = (otherTabs.Count > 0);
1013                 if (!tmnuMergeWith.Enabled) return;
1014
1015                 foreach (RadegastTab tab in otherTabs)
1016                 {
1017                     ToolStripItem item = tmnuMergeWith.DropDown.Items.Add(tab.Label);
1018                     item.Tag = tab.Name;
1019                     item.Click += new EventHandler(MergeItemClick);
1020                 }
1021             }
1022             else
1023             {
1024                 tmnuMergeWith.Text = "Split";
1025                 tmnuMergeWith.Click += new EventHandler(SplitClick);
1026             }
1027         }
1028
1029         private void MergeItemClick(object sender, EventArgs e)
1030         {
1031             ToolStripItem item = (ToolStripItem)sender;
1032             RadegastTab tab = tabs[item.Tag.ToString()];
1033
1034             selectedTab.MergeWith(tab);
1035
1036             SplitContainer container = (SplitContainer)selectedTab.Control;
1037             toolStripContainer1.ContentPanel.Controls.Add(container);
1038
1039             selectedTab.Select();
1040             RemoveTabEntry(tab);
1041
1042             tabs.Add(tab.Name, selectedTab);
1043         }
1044
1045         private void SplitClick(object sender, EventArgs e)
1046         {
1047             SplitTab(selectedTab);
1048             selectedTab.Select();
1049         }
1050
1051         public void SplitTab(RadegastTab tab)
1052         {
1053             RadegastTab otherTab = tab.Split();
1054             if (otherTab == null) return;
1055
1056             toolStripContainer1.ContentPanel.Controls.Add(tab.Control);
1057             toolStripContainer1.ContentPanel.Controls.Add(otherTab.Control);
1058
1059             tabs.Remove(otherTab.Name);
1060             AddTab(otherTab);
1061         }
1062
1063         private void tmnuDetachTab_Click(object sender, EventArgs e)
1064         {
1065             if (!selectedTab.AllowDetach) return;
1066             RadegastTab tab = selectedTab;
1067             SelectDefaultTab();
1068             tab.Detach(instance);
1069         }
1070
1071         private void tbtnCloseTab_Click(object sender, EventArgs e)
1072         {
1073             RadegastTab tab = selectedTab;
1074             if (tab.AllowClose)
1075                 tab.Close();
1076             else if (tab.AllowHide)
1077                 tab.Hide();
1078         }
1079
1080         private void TabsConsole_Load(object sender, EventArgs e)
1081         {
1082             owner = this.FindForm();
1083         }
1084
1085         private void ctxTabs_Opening(object sender, CancelEventArgs e)
1086         {
1087             Point pt = this.PointToClient(Cursor.Position);
1088             ToolStripItem stripItem = tstTabs.GetItemAt(pt);
1089
1090             if (stripItem == null)
1091             {
1092                 e.Cancel = true;
1093             }
1094             else
1095             {
1096                 tabs[stripItem.Tag.ToString()].Select();
1097
1098                 ctxBtnClose.Enabled = selectedTab.AllowClose || selectedTab.AllowHide;
1099                 ctxBtnDetach.Enabled = selectedTab.AllowDetach;
1100                 ctxBtnMerge.Enabled = selectedTab.AllowMerge;
1101                 ctxBtnMerge.DropDown.Items.Clear();
1102
1103                 if (!ctxBtnClose.Enabled && !ctxBtnDetach.Enabled && !ctxBtnMerge.Enabled)
1104                 {
1105                     e.Cancel = true;
1106                     return;
1107                 }
1108
1109                 if (!selectedTab.AllowMerge) return;
1110                 if (!selectedTab.Merged)
1111                 {
1112                     ctxBtnMerge.Text = "Merge With";
1113
1114                     List<RadegastTab> otherTabs = GetOtherTabs();
1115
1116                     ctxBtnMerge.Enabled = (otherTabs.Count > 0);
1117                     if (!ctxBtnMerge.Enabled) return;
1118
1119                     foreach (RadegastTab tab in otherTabs)
1120                     {
1121                         ToolStripItem item = ctxBtnMerge.DropDown.Items.Add(tab.Label);
1122                         item.Tag = tab.Name;
1123                         item.Click += new EventHandler(MergeItemClick);
1124                     }
1125                 }
1126                 else
1127                 {
1128                     ctxBtnMerge.Text = "Split";
1129                     ctxBtnMerge.Click += new EventHandler(SplitClick);
1130                 }
1131
1132             }
1133         }
1134
1135     }
1136
1137     /// <summary>
1138     /// Arguments for tab events
1139     /// </summary>
1140     public class TabEventArgs : EventArgs
1141     {
1142         /// <summary>
1143         /// Tab that was manipulated in the event
1144         /// </summary>
1145         public RadegastTab Tab;
1146
1147         public TabEventArgs()
1148             : base()
1149         {
1150         }
1151
1152         public TabEventArgs(RadegastTab tab)
1153             : base()
1154         {
1155             Tab = tab;
1156         }
1157     }
1158
1159     /// <summary>
1160     /// Argument for chat notification events
1161     /// </summary>
1162     public class ChatNotificationEventArgs : EventArgs
1163     {
1164         public string Message;
1165         public ChatBufferTextStyle Style;
1166
1167         public ChatNotificationEventArgs(string message, ChatBufferTextStyle style)
1168         {
1169             Message = message;
1170             Style = style;
1171         }
1172     }
1173
1174     /// <summary>
1175     /// Element of list of nearby avatars
1176     /// </summary>
1177     public class NearbyAvatar
1178     {
1179         public UUID ID { get; set; }
1180         public string Name { get; set; }
1181     }
1182
1183 }