OSDN Git Service

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