OSDN Git Service

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