// // Radegast Metaverse Client // Copyright (c) 2009-2014, Radegast Development Team // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the application "Radegast", nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // $Id$ // using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using Radegast.Netcom; using OpenMetaverse; namespace Radegast { public partial class TabsConsole : UserControl { /// /// List of nearby avatars (radar data) /// public List NearbyAvatars { get { List res = new List(); if (TabExists("chat")) { ChatConsole c = (ChatConsole)Tabs["chat"].Control; lock (c.agentSimHandle) { foreach (ListViewItem item in c.lvwObjects.Items) { if (item.Name != client.Self.AgentID.ToString()) { res.Add(new NearbyAvatar() { ID = new UUID(item.Name), Name = item.Text }); } } } } return res; } } /// /// Delegate invoked on tab operations /// /// Event sender /// Event arguments public delegate void TabCallback(object sender, TabEventArgs e); /// /// Fired when a tab is selected /// public event TabCallback OnTabSelected; /// /// Delegate invoked when chat notification is printed /// /// Event sender /// Event arguments public delegate void ChatNotificationCallback(object sender, ChatNotificationEventArgs e); /// /// Fired when a tab is selected /// public event ChatNotificationCallback OnChatNotification; /// /// Fired when a new tab is added /// public event TabCallback OnTabAdded; /// /// Fired when a tab is removed /// public event TabCallback OnTabRemoved; private RadegastInstance instance; private GridClient client { get { return instance.Client; } } private RadegastNetcom netcom { get { return instance.Netcom; } } private ChatTextManager mainChatManger; public ChatTextManager MainChatManger { get { return mainChatManger; } } private Dictionary tabs = new Dictionary(); public Dictionary Tabs { get { return tabs; } } private ChatConsole chatConsole; private RadegastTab selectedTab; /// /// Currently selected tab /// public RadegastTab SelectedTab { get { return selectedTab; } } private Form owner; public TabsConsole(RadegastInstance instance) { InitializeComponent(); Disposed += new EventHandler(TabsConsole_Disposed); this.instance = instance; this.instance.ClientChanged += new EventHandler(instance_ClientChanged); AddNetcomEvents(); InitializeMainTab(); InitializeChatTab(); // Callbacks RegisterClientEvents(client); } private void RegisterClientEvents(GridClient client) { client.Self.ScriptQuestion += new EventHandler(Self_ScriptQuestion); client.Self.ScriptDialog += new EventHandler(Self_ScriptDialog); client.Self.LoadURL += new EventHandler(Self_LoadURL); client.Self.SetDisplayNameReply += new EventHandler(Self_SetDisplayNameReply); client.Avatars.DisplayNameUpdate += new EventHandler(Avatars_DisplayNameUpdate); client.Network.EventQueueRunning += new EventHandler(Network_EventQueueRunning); client.Network.RegisterCallback(OpenMetaverse.Packets.PacketType.ScriptTeleportRequest, ScriptTeleportRequestHandler); } private void UnregisterClientEvents(GridClient client) { client.Self.ScriptQuestion -= new EventHandler(Self_ScriptQuestion); client.Self.ScriptDialog -= new EventHandler(Self_ScriptDialog); client.Self.LoadURL -= new EventHandler(Self_LoadURL); client.Self.SetDisplayNameReply -= new EventHandler(Self_SetDisplayNameReply); client.Avatars.DisplayNameUpdate -= new EventHandler(Avatars_DisplayNameUpdate); client.Network.EventQueueRunning -= new EventHandler(Network_EventQueueRunning); client.Network.UnregisterCallback(OpenMetaverse.Packets.PacketType.ScriptTeleportRequest, ScriptTeleportRequestHandler); } void instance_ClientChanged(object sender, ClientChangedEventArgs e) { UnregisterClientEvents(e.OldClient); RegisterClientEvents(client); } void TabsConsole_Disposed(object sender, EventArgs e) { RemoveNetcomEvents(); UnregisterClientEvents(client); } private void AddNetcomEvents() { netcom.ClientLoginStatus += new EventHandler(netcom_ClientLoginStatus); netcom.ClientLoggedOut += new EventHandler(netcom_ClientLoggedOut); netcom.ClientDisconnected += new EventHandler(netcom_ClientDisconnected); netcom.ChatSent += new EventHandler(netcom_ChatSent); netcom.AlertMessageReceived += new EventHandler(netcom_AlertMessageReceived); netcom.InstantMessageReceived += new EventHandler(netcom_InstantMessageReceived); } private void RemoveNetcomEvents() { netcom.ClientLoginStatus -= new EventHandler(netcom_ClientLoginStatus); netcom.ClientLoggedOut -= new EventHandler(netcom_ClientLoggedOut); netcom.ClientDisconnected -= new EventHandler(netcom_ClientDisconnected); netcom.ChatSent -= new EventHandler(netcom_ChatSent); netcom.AlertMessageReceived -= new EventHandler(netcom_AlertMessageReceived); netcom.InstantMessageReceived -= new EventHandler(netcom_InstantMessageReceived); } void ScriptTeleportRequestHandler(object sender, PacketReceivedEventArgs e) { if (InvokeRequired) { if (IsHandleCreated || !instance.MonoRuntime) BeginInvoke(new MethodInvoker(() => ScriptTeleportRequestHandler(sender, e))); return; } var msg = (OpenMetaverse.Packets.ScriptTeleportRequestPacket)e.Packet; if (TabExists("map")) { Tabs["map"].Select(); ((MapConsole)Tabs["map"].Control).CenterOnGlobalPos( (float)(client.Self.GlobalPosition.X - client.Self.SimPosition.X) + msg.Data.SimPosition.X, (float)(client.Self.GlobalPosition.Y - client.Self.SimPosition.Y) + msg.Data.SimPosition.Y, msg.Data.SimPosition.Z); } } void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e) { if (InvokeRequired) { BeginInvoke(new MethodInvoker(() => Network_EventQueueRunning(sender, e))); return; } if (TabExists("friends")) return; if (e.Simulator == client.Network.CurrentSim) { client.Self.UpdateAgentLanguage("en", true); InitializeOnlineTabs(); } } void Self_ScriptDialog(object sender, ScriptDialogEventArgs e) { if (instance.MainForm.InvokeRequired) { instance.MainForm.BeginInvoke(new MethodInvoker(() => Self_ScriptDialog(sender, e))); return; } // Is this object muted if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.ObjectID) // muted object by id || (m.Type == MuteType.ByName && m.Name == e.ObjectName) // object muted by name )) return; instance.MainForm.AddNotification(new ntfScriptDialog(instance, e.Message, e.ObjectName, e.ImageID, e.ObjectID, e.FirstName, e.LastName, e.Channel, e.ButtonLabels)); } void Self_ScriptQuestion(object sender, ScriptQuestionEventArgs e) { // Is this object muted if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.TaskID) // muted object by id || (m.Type == MuteType.ByName && m.Name == e.ObjectName) // object muted by name )) return; if (instance.GlobalSettings["on_script_question"] == "Auto Decline" || instance.RLV.RestictionActive("denypermission")) { instance.Client.Self.ScriptQuestionReply(e.Simulator, e.ItemID, e.TaskID, 0); } else if (instance.GlobalSettings["on_script_question"] == "Auto Accept" || instance.RLV.RestictionActive("acceptpermission")) { instance.Client.Self.ScriptQuestionReply(e.Simulator, e.ItemID, e.TaskID, e.Questions); } else { instance.MainForm.AddNotification(new ntfPermissions(instance, e.Simulator, e.TaskID, e.ItemID, e.ObjectName, e.ObjectOwnerName, e.Questions)); } } private void netcom_ClientLoginStatus(object sender, LoginProgressEventArgs e) { if (e.Status == LoginStatus.Failed) { DisplayNotificationInChat("Login error: " + e.Message, ChatBufferTextStyle.Error); } else if (e.Status == LoginStatus.Success) { DisplayNotificationInChat("Logged in as " + netcom.LoginOptions.FullName + ".", ChatBufferTextStyle.StatusDarkBlue); DisplayNotificationInChat("Login reply: " + e.Message, ChatBufferTextStyle.StatusDarkBlue); if (tabs.ContainsKey("login")) { if (selectedTab.Name == "login") SelectDefaultTab(); ForceCloseTab("login"); } client.Self.RetrieveInstantMessages(); } } private void netcom_ClientLoggedOut(object sender, EventArgs e) { DisposeOnlineTabs(); SelectDefaultTab(); DisplayNotificationInChat("Logged out."); } private void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e) { if (e.Reason == NetworkManager.DisconnectType.ClientInitiated) return; DisposeOnlineTabs(); SelectDefaultTab(); DisplayNotificationInChat("Disconnected: " + e.Message, ChatBufferTextStyle.Error); } void Avatars_DisplayNameUpdate(object sender, DisplayNameUpdateEventArgs e) { DisplayNotificationInChat(string.Format("({0}) is now known as {1}", e.DisplayName.UserName, e.DisplayName.DisplayName)); } void Self_SetDisplayNameReply(object sender, SetDisplayNameReplyEventArgs e) { if (e.Status == 200) { DisplayNotificationInChat("You are now knows as " + e.DisplayName.DisplayName); } else { DisplayNotificationInChat("Failed to set a new display name: " + e.Reason, ChatBufferTextStyle.Error); } } private void netcom_AlertMessageReceived(object sender, AlertMessageEventArgs e) { tabs["chat"].Highlight(); } private void netcom_ChatSent(object sender, ChatSentEventArgs e) { tabs["chat"].Highlight(); } void Self_LoadURL(object sender, LoadUrlEventArgs e) { // Is the object or the owner muted? if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.ObjectID) // muted object by id || (m.Type == MuteType.ByName && m.Name == e.ObjectName) // object muted by name || (m.Type == MuteType.Resident && m.ID == e.OwnerID) // object's owner muted )) return; instance.MainForm.AddNotification(new ntfLoadURL(instance, e)); } private void netcom_InstantMessageReceived(object sender, InstantMessageEventArgs e) { // Messaage from someone we muted? if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Resident && me.ID == e.IM.FromAgentID)) return; try { if (instance.State.LSLHelper.ProcessIM(e)) { return; } } catch (Exception ex) { Logger.Log("Failed executing automation action: " + ex.ToString(), Helpers.LogLevel.Warning); } switch (e.IM.Dialog) { case InstantMessageDialog.SessionSend: if (instance.Groups.ContainsKey(e.IM.IMSessionID)) { HandleGroupIM(e); } else { HandleConferenceIM(e); } break; case InstantMessageDialog.MessageFromAgent: if (e.IM.FromAgentName == "Second Life") { HandleIMFromObject(e); } else if (e.IM.FromAgentID == UUID.Zero) { instance.MainForm.AddNotification(new ntfGeneric(instance, e.IM.Message)); } else if (e.IM.GroupIM || instance.Groups.ContainsKey(e.IM.IMSessionID)) { HandleGroupIM(e); } else if (e.IM.BinaryBucket.Length > 1) { // conference HandleConferenceIM(e); } else if (e.IM.IMSessionID == UUID.Zero) { String msg = string.Format("Message from {0}: {1}", instance.Names.Get(e.IM.FromAgentID, e.IM.FromAgentName), e.IM.Message); instance.MainForm.AddNotification(new ntfGeneric(instance, msg)); DisplayNotificationInChat(msg); } else { HandleIM(e); } break; case InstantMessageDialog.MessageFromObject: HandleIMFromObject(e); break; case InstantMessageDialog.StartTyping: if (TabExists(e.IM.FromAgentName)) { RadegastTab tab = tabs[e.IM.FromAgentName.ToLower()]; if (!tab.Highlighted) tab.PartialHighlight(); } break; case InstantMessageDialog.StopTyping: if (TabExists(e.IM.FromAgentName)) { RadegastTab tab = tabs[e.IM.FromAgentName.ToLower()]; if (!tab.Highlighted) tab.Unhighlight(); } break; case InstantMessageDialog.MessageBox: instance.MainForm.AddNotification(new ntfGeneric(instance, e.IM.Message)); break; case InstantMessageDialog.RequestTeleport: if (instance.RLV.AutoAcceptTP(e.IM.FromAgentID)) { DisplayNotificationInChat("Auto accepting teleprot from " + e.IM.FromAgentName); instance.Client.Self.TeleportLureRespond(e.IM.FromAgentID, e.IM.IMSessionID, true); } else { instance.MainForm.AddNotification(new ntfTeleport(instance, e.IM)); } break; case InstantMessageDialog.RequestLure: instance.MainForm.AddNotification(new ntfRequestLure(instance, e.IM)); break; case InstantMessageDialog.GroupInvitation: instance.MainForm.AddNotification(new ntfGroupInvitation(instance, e.IM)); break; case InstantMessageDialog.FriendshipOffered: if (e.IM.FromAgentName == "Second Life") { HandleIMFromObject(e); } else { instance.MainForm.AddNotification(new ntfFriendshipOffer(instance, e.IM)); } break; case InstantMessageDialog.InventoryAccepted: DisplayNotificationInChat(e.IM.FromAgentName + " accepted your inventory offer."); break; case InstantMessageDialog.InventoryDeclined: DisplayNotificationInChat(e.IM.FromAgentName + " declined your inventory offer."); break; case InstantMessageDialog.GroupNotice: // Is this group muted? if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Group && me.ID == e.IM.FromAgentID)) break; instance.MainForm.AddNotification(new ntfGroupNotice(instance, e.IM)); break; case InstantMessageDialog.InventoryOffered: var ion = new ntfInventoryOffer(instance, e.IM); instance.MainForm.AddNotification(ion); if (instance.GlobalSettings["inv_auto_accept_mode"].AsInteger() == 1) { ion.btnAccept.PerformClick(); } else if (instance.GlobalSettings["inv_auto_accept_mode"].AsInteger() == 2) { ion.btnDiscard.PerformClick(); } break; case InstantMessageDialog.TaskInventoryOffered: // Is the object muted by name? if (null != client.Self.MuteList.Find(me => me.Type == MuteType.ByName && me.Name == e.IM.FromAgentName)) break; var iont = new ntfInventoryOffer(instance, e.IM); instance.MainForm.AddNotification(iont); if (instance.GlobalSettings["inv_auto_accept_mode"].AsInteger() == 1) { iont.btnAccept.PerformClick(); } else if (instance.GlobalSettings["inv_auto_accept_mode"].AsInteger() == 2) { iont.btnDiscard.PerformClick(); } break; } } /// /// Make default tab (chat) active /// public void SelectDefaultTab() { if (IsHandleCreated && TabExists("chat")) tabs["chat"].Select(); } /// /// Displays notification in the main chat tab /// /// Message to be printed in the chat tab public void DisplayNotificationInChat(string msg) { DisplayNotificationInChat(msg, ChatBufferTextStyle.ObjectChat); } /// /// Displays notification in the main chat tab /// /// Message to be printed in the chat tab /// Style of the message to be printed, normal, object, etc. public void DisplayNotificationInChat(string msg, ChatBufferTextStyle style) { DisplayNotificationInChat(msg, style, true); } /// /// Displays notification in the main chat tab /// /// Message to be printed in the chat tab /// Style of the message to be printed, normal, object, etc. /// Highligt (and flash in taskbar) chat tab if not selected public void DisplayNotificationInChat(string msg, ChatBufferTextStyle style, bool highlightChatTab) { if (InvokeRequired) { if (!instance.MonoRuntime || IsHandleCreated) BeginInvoke(new MethodInvoker(() => DisplayNotificationInChat(msg, style, highlightChatTab))); return; } if (style != ChatBufferTextStyle.Invisible) { ChatBufferItem line = new ChatBufferItem( DateTime.Now, string.Empty, UUID.Zero, msg, style ); try { mainChatManger.ProcessBufferItem(line, true); if (highlightChatTab) { tabs["chat"].Highlight(); } } catch (Exception) { } } if (OnChatNotification != null) { try { OnChatNotification(this, new ChatNotificationEventArgs(msg, style)); } catch { } } } private void HandleIMFromObject(InstantMessageEventArgs e) { // Is the object or the owner muted? if (null != client.Self.MuteList.Find(m => (m.Type == MuteType.Object && m.ID == e.IM.IMSessionID) // muted object by id || (m.Type == MuteType.ByName && m.Name == e.IM.FromAgentName) // object muted by name || (m.Type == MuteType.Resident && m.ID == e.IM.FromAgentID) // object's owner muted )) return; DisplayNotificationInChat(e.IM.FromAgentName + ": " + e.IM.Message); } public static Control FindFocusedControl(Control control) { var container = control as ContainerControl; while (container != null) { control = container.ActiveControl; container = control as ContainerControl; } return control; } /// /// Creates new IM tab if needed /// /// IM session with agentID /// Tab label /// Should tab be selected and focused /// True if there was an existing IM tab, false if it was created public bool ShowIMTab(UUID agentID, string label, bool makeActive) { if (instance.TabConsole.TabExists((client.Self.AgentID ^ agentID).ToString())) { if (makeActive) { instance.TabConsole.SelectTab((client.Self.AgentID ^ agentID).ToString()); } return false; } if (makeActive) { instance.MediaManager.PlayUISound(UISounds.IMWindow); } else { instance.MediaManager.PlayUISound(UISounds.IM); } Control active = FindFocusedControl(instance.MainForm); instance.TabConsole.AddIMTab(agentID, client.Self.AgentID ^ agentID, label); if (makeActive) { instance.TabConsole.SelectTab((client.Self.AgentID ^ agentID).ToString()); } else if (active != null) { active.Focus(); } return true; } private void HandleIM(InstantMessageEventArgs e) { bool isNew = ShowIMTab(e.IM.FromAgentID, e.IM.FromAgentName, false); if (!TabExists(e.IM.IMSessionID.ToString())) return; // this should now exist. sanity check anyway RadegastTab tab = tabs[e.IM.IMSessionID.ToString()]; tab.Highlight(); if (isNew) { ((IMTabWindow)tab.Control).TextManager.ProcessIM(e); } } private void HandleConferenceIM(InstantMessageEventArgs e) { if (TabExists(e.IM.IMSessionID.ToString())) { RadegastTab tab = tabs[e.IM.IMSessionID.ToString()]; tab.Highlight(); return; } instance.MediaManager.PlayUISound(UISounds.IM); Control active = FindFocusedControl(instance.MainForm); ConferenceIMTabWindow imTab = AddConferenceIMTab(e.IM.IMSessionID, Utils.BytesToString(e.IM.BinaryBucket)); tabs[e.IM.IMSessionID.ToString()].Highlight(); imTab.TextManager.ProcessIM(e); if (active != null) { active.Focus(); } } private void HandleGroupIM(InstantMessageEventArgs e) { // Ignore group IM from a muted group if (null != client.Self.MuteList.Find(me => me.Type == MuteType.Group && (me.ID == e.IM.IMSessionID || me.ID == e.IM.FromAgentID))) return; if (TabExists(e.IM.IMSessionID.ToString())) { RadegastTab tab = tabs[e.IM.IMSessionID.ToString()]; tab.Highlight(); return; } instance.MediaManager.PlayUISound(UISounds.IM); Control active = FindFocusedControl(instance.MainForm); GroupIMTabWindow imTab = AddGroupIMTab(e.IM.IMSessionID, Utils.BytesToString(e.IM.BinaryBucket)); imTab.TextManager.ProcessIM(e); tabs[e.IM.IMSessionID.ToString()].Highlight(); if (active != null) { active.Focus(); } } public void InitializeMainTab() { if (TabExists("login")) { ForceCloseTab("login"); } LoginConsole loginConsole = new LoginConsole(instance); RadegastTab tab = AddTab("login", "Login", loginConsole); tab.AllowClose = false; tab.AllowDetach = false; tab.AllowMerge = false; tab.AllowHide = false; loginConsole.RegisterTab(tab); } private void InitializeChatTab() { chatConsole = new ChatConsole(instance); mainChatManger = chatConsole.ChatManager; RadegastTab tab = AddTab("chat", "Chat", chatConsole); tab.AllowClose = false; tab.AllowDetach = false; tab.AllowHide = false; } /// /// Create Tabs that only make sense when connected /// private void InitializeOnlineTabs() { RadegastTab tab = AddTab("friends", "Friends", new FriendsConsole(instance)); tab.AllowClose = false; tab.AllowDetach = true; tab.Visible = false; tab = AddTab("groups", "Groups", new GroupsConsole(instance)); tab.AllowClose = false; tab.AllowDetach = true; tab.Visible = false; // Ugly workaround for a mono bug InventoryConsole inv = new InventoryConsole(instance); if (instance.MonoRuntime) inv.invTree.Scrollable = false; tab = AddTab("inventory", "Inventory", inv); if (instance.MonoRuntime) inv.invTree.Scrollable = true; tab.AllowClose = false; tab.AllowDetach = true; tab.Visible = false; tab = AddTab("search", "Search", new SearchConsole(instance)); tab.AllowClose = false; tab.AllowDetach = true; tab.Visible = false; tab = AddTab("map", "Map", new MapConsole(instance)); tab.AllowClose = false; tab.AllowDetach = true; tab.Visible = false; tab = AddTab("voice", "Voice", new VoiceConsole(instance)); tab.AllowClose = false; tab.AllowDetach = true; tab.Visible = false; } /// /// Close tabs that make no sense when not connected /// private void DisposeOnlineTabs() { lock (tabs) { ForceCloseTab("voice"); ForceCloseTab("map"); ForceCloseTab("search"); ForceCloseTab("inventory"); ForceCloseTab("groups"); ForceCloseTab("friends"); } } private void ForceCloseTab(string name) { if (!TabExists(name)) return; RadegastTab tab = tabs[name]; if (tab.Merged) SplitTab(tab); tab.AllowClose = true; tab.Close(); tab = null; } public void RegisterContextAction(Type libomvType, String label, EventHandler handler) { instance.ContextActionManager.RegisterContextAction(libomvType, label, handler); } public void RegisterContextAction(ContextAction handler) { instance.ContextActionManager.RegisterContextAction(handler); } public void AddTab(RadegastTab tab) { ToolStripButton button = (ToolStripButton)tstTabs.Items.Add(tab.Label); button.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText; button.Image = null; button.AutoToolTip = false; button.Tag = tab.Name; button.Click += new EventHandler(TabButtonClick); tab.Button = button; tabs.Add(tab.Name, tab); if (OnTabAdded != null) { try { OnTabAdded(this, new TabEventArgs(tab)); } catch (Exception) { } } } public RadegastTab AddTab(string name, string label, Control control) { // WORKAROUND: one should never add tab that alrady exists // but under some weird conditions disconnect/connect // fire in the wrong order if (TabExists(name)) { Logger.Log("Force closing tab '" + name + "'", Helpers.LogLevel.Warning, client); ForceCloseTab(name); } control.Visible = false; control.Dock = DockStyle.Fill; toolStripContainer1.ContentPanel.Controls.Add(control); ToolStripButton button = (ToolStripButton)tstTabs.Items.Add(label); button.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText; button.Image = null; button.AutoToolTip = false; button.Tag = name.ToLower(); button.AllowDrop = true; button.Click += new EventHandler(TabButtonClick); RadegastTab tab = new RadegastTab(instance, button, control, name.ToLower(), label); if (control is RadegastTabControl) ((RadegastTabControl)control).RadegastTab = tab; tab.TabAttached += new EventHandler(tab_TabAttached); tab.TabDetached += new EventHandler(tab_TabDetached); tab.TabSelected += new EventHandler(tab_TabSelected); tab.TabClosed += new EventHandler(tab_TabClosed); tab.TabHidden += new EventHandler(tab_TabHidden); tabs.Add(name.ToLower(), tab); if (OnTabAdded != null) { try { OnTabAdded(this, new TabEventArgs(tab)); } catch (Exception) { } } button.MouseDown += (msender, margs) => { if (margs.Button == MouseButtons.Middle) { if (tab.AllowClose) { tab.Close(); } else if (tab.AllowHide) { tab.Hide(); } } }; return tab; } private void tab_TabAttached(object sender, EventArgs e) { RadegastTab tab = (RadegastTab)sender; tab.Select(); } private void tab_TabDetached(object sender, EventArgs e) { RadegastTab tab = (RadegastTab)sender; frmDetachedTab form = (frmDetachedTab)tab.Owner; form.ReattachStrip = tstTabs; form.ReattachContainer = toolStripContainer1.ContentPanel; } private void tab_TabSelected(object sender, EventArgs e) { RadegastTab tab = (RadegastTab)sender; if (selectedTab != null && selectedTab != tab) { selectedTab.Deselect(); } selectedTab = tab; tbtnCloseTab.Enabled = !tab.Merged && (tab.AllowClose || tab.AllowHide); if (owner != null) { owner.AcceptButton = tab.DefaultControlButton; } if (OnTabSelected != null) { try { OnTabSelected(this, new TabEventArgs(selectedTab)); } catch (Exception) { } } } void tab_TabHidden(object sender, EventArgs e) { RadegastTab tab = (RadegastTab)sender; if (selectedTab != null && selectedTab == tab) { tab.Deselect(); SelectDefaultTab(); } } private void tab_TabClosed(object sender, EventArgs e) { RadegastTab tab = (RadegastTab)sender; if (selectedTab != null && selectedTab == tab && tab.Name != "chat") { tab.Deselect(); SelectDefaultTab(); } tabs.Remove(tab.Name); if (OnTabRemoved != null) { try { OnTabRemoved(this, new TabEventArgs(tab)); } catch (Exception) { } } tab = null; } private void TabButtonClick(object sender, EventArgs e) { ToolStripButton button = (ToolStripButton)sender; RadegastTab tab = tabs[button.Tag.ToString()]; tab.Select(); } public void RemoveTabEntry(RadegastTab tab) { if (tstTabs.Items.Contains(tab.Button)) { tstTabs.Items.Remove(tab.Button); } tab.Button.Dispose(); tabs.Remove(tab.Name); } public void RemoveTab(string name) { if (tabs.ContainsKey(name)) { tabs.Remove(name); } } //Used for outside classes that have a reference to TabsConsole public void SelectTab(string name) { if (TabExists(name.ToLower())) tabs[name.ToLower()].Select(); } public bool TabExists(string name) { return tabs.ContainsKey(name.ToLower()); } public RadegastTab GetTab(string name) { if (TabExists(name.ToLower())) return tabs[name.ToLower()]; else return null; } public List GetOtherTabs() { List otherTabs = new List(); foreach (ToolStripItem item in tstTabs.Items) { if (item.Tag == null) continue; if ((ToolStripItem)item == selectedTab.Button) continue; RadegastTab tab = tabs[item.Tag.ToString()]; if (!tab.AllowMerge) continue; if (tab.Merged) continue; otherTabs.Add(tab); } return otherTabs; } /// /// Activates the next tab /// public void SelectNextTab() { List buttons = new List(); foreach (ToolStripItem item in tstTabs.Items) { if (item.Tag == null || !item.Visible) continue; buttons.Add(item); } int current = 0; for (int i = 0; i < buttons.Count; i++) { if (buttons[i] == selectedTab.Button) { current = i; break; } } current++; if (current == buttons.Count) current = 0; SelectTab(tabs[buttons[current].Tag.ToString()].Name); } /// /// Activates the previous tab /// public void SelectPreviousTab() { List buttons = new List(); foreach (ToolStripItem item in tstTabs.Items) { if (item.Tag == null || !item.Visible) continue; buttons.Add(item); } int current = 0; for (int i = 0; i < buttons.Count; i++) { if (buttons[i] == selectedTab.Button) { current = i; break; } } current--; if (current == -1) current = buttons.Count - 1; SelectTab(tabs[buttons[current].Tag.ToString()].Name); } public IMTabWindow AddIMTab(UUID target, UUID session, string targetName) { IMTabWindow imTab = new IMTabWindow(instance, target, session, targetName); RadegastTab tab = AddTab(session.ToString(), "IM: " + targetName, imTab); imTab.SelectIMInput(); return imTab; } public ConferenceIMTabWindow AddConferenceIMTab(UUID session, string name) { ConferenceIMTabWindow imTab = new ConferenceIMTabWindow(instance, session, name); RadegastTab tab = AddTab(session.ToString(), name, imTab); imTab.SelectIMInput(); return imTab; } public GroupIMTabWindow AddGroupIMTab(UUID session, string name) { GroupIMTabWindow imTab = new GroupIMTabWindow(instance, session, name); RadegastTab tab = AddTab(session.ToString(), name, imTab); imTab.SelectIMInput(); return imTab; } public OutfitTextures AddOTTab(Avatar avatar) { OutfitTextures otTab = new OutfitTextures(instance, avatar); RadegastTab tab = AddTab("OT: " + avatar.Name, "OT: " + avatar.Name, otTab); otTab.GetTextures(); return otTab; } public MasterTab AddMSTab(Avatar avatar) { MasterTab msTab = new MasterTab(instance, avatar); RadegastTab tab = AddTab("MS: " + avatar.Name, "MS: " + avatar.Name, msTab); return msTab; } public AnimTab AddAnimTab(Avatar avatar) { AnimTab animTab = new AnimTab(instance, avatar); RadegastTab tab = AddTab("Anim: " + avatar.Name, "Anim: " + avatar.Name, animTab); return animTab; } private void tbtnTabOptions_Click(object sender, EventArgs e) { tmnuMergeWith.Enabled = selectedTab.AllowMerge; tmnuDetachTab.Enabled = selectedTab.AllowDetach; tmnuMergeWith.DropDown.Items.Clear(); if (!selectedTab.AllowMerge) return; if (!selectedTab.Merged) { tmnuMergeWith.Text = "Merge With"; List otherTabs = GetOtherTabs(); tmnuMergeWith.Enabled = (otherTabs.Count > 0); if (!tmnuMergeWith.Enabled) return; foreach (RadegastTab tab in otherTabs) { ToolStripItem item = tmnuMergeWith.DropDown.Items.Add(tab.Label); item.Tag = tab.Name; item.Click += new EventHandler(MergeItemClick); } } else { tmnuMergeWith.Text = "Split"; tmnuMergeWith.Click += new EventHandler(SplitClick); } } private void MergeItemClick(object sender, EventArgs e) { ToolStripItem item = (ToolStripItem)sender; RadegastTab tab = tabs[item.Tag.ToString()]; selectedTab.MergeWith(tab); SplitContainer container = (SplitContainer)selectedTab.Control; toolStripContainer1.ContentPanel.Controls.Add(container); selectedTab.Select(); RemoveTabEntry(tab); tabs.Add(tab.Name, selectedTab); } private void SplitClick(object sender, EventArgs e) { SplitTab(selectedTab); selectedTab.Select(); } public void SplitTab(RadegastTab tab) { RadegastTab otherTab = tab.Split(); if (otherTab == null) return; toolStripContainer1.ContentPanel.Controls.Add(tab.Control); toolStripContainer1.ContentPanel.Controls.Add(otherTab.Control); tabs.Remove(otherTab.Name); AddTab(otherTab); } private void tmnuDetachTab_Click(object sender, EventArgs e) { if (!selectedTab.AllowDetach) return; RadegastTab tab = selectedTab; SelectDefaultTab(); tab.Detach(instance); } private void tbtnCloseTab_Click(object sender, EventArgs e) { RadegastTab tab = selectedTab; if (tab.Merged) return; else if (tab.AllowClose) tab.Close(); else if (tab.AllowHide) tab.Hide(); } private void TabsConsole_Load(object sender, EventArgs e) { owner = this.FindForm(); } private void ctxTabs_Opening(object sender, CancelEventArgs e) { e.Cancel = false; Point pt = this.PointToClient(Cursor.Position); ToolStripItem stripItem = tstTabs.GetItemAt(pt); if (stripItem == null) { e.Cancel = true; } else { tabs[stripItem.Tag.ToString()].Select(); ctxBtnClose.Enabled = !selectedTab.Merged && (selectedTab.AllowClose || selectedTab.AllowHide); ctxBtnDetach.Enabled = selectedTab.AllowDetach; ctxBtnMerge.Enabled = selectedTab.AllowMerge; ctxBtnMerge.DropDown.Items.Clear(); if (!ctxBtnClose.Enabled && !ctxBtnDetach.Enabled && !ctxBtnMerge.Enabled) { e.Cancel = true; return; } if (!selectedTab.AllowMerge) return; if (!selectedTab.Merged) { ctxBtnMerge.Text = "Merge With"; List otherTabs = GetOtherTabs(); ctxBtnMerge.Enabled = (otherTabs.Count > 0); if (!ctxBtnMerge.Enabled) return; foreach (RadegastTab tab in otherTabs) { ToolStripItem item = ctxBtnMerge.DropDown.Items.Add(tab.Label); item.Tag = tab.Name; item.Click += new EventHandler(MergeItemClick); } } else { ctxBtnMerge.Text = "Split"; ctxBtnMerge.Click += new EventHandler(SplitClick); } } } } /// /// Arguments for tab events /// public class TabEventArgs : EventArgs { /// /// Tab that was manipulated in the event /// public RadegastTab Tab; public TabEventArgs() : base() { } public TabEventArgs(RadegastTab tab) : base() { Tab = tab; } } /// /// Argument for chat notification events /// public class ChatNotificationEventArgs : EventArgs { public string Message; public ChatBufferTextStyle Style; public ChatNotificationEventArgs(string message, ChatBufferTextStyle style) { Message = message; Style = style; } } /// /// Element of list of nearby avatars /// public class NearbyAvatar { public UUID ID { get; set; } public string Name { get; set; } } }