OSDN Git Service

RAD-7 RAD-172: Complete inventory search, recent and worn filters
[radegast/radegast.git] / Radegast / Core / RadegastInstance.cs
index db173ad..70cbd78 100644 (file)
-// \r
-// Radegast Metaverse Client\r
-// Copyright (c) 2009, Radegast Development Team\r
-// All rights reserved.\r
-// \r
-// Redistribution and use in source and binary forms, with or without\r
-// modification, are permitted provided that the following conditions are met:\r
-// \r
-//     * Redistributions of source code must retain the above copyright notice,\r
-//       this list of conditions and the following disclaimer.\r
-//     * Redistributions in binary form must reproduce the above copyright\r
-//       notice, this list of conditions and the following disclaimer in the\r
-//       documentation and/or other materials provided with the distribution.\r
-//     * Neither the name of the application "Radegast", nor the names of its\r
-//       contributors may be used to endorse or promote products derived from\r
-//       this software without specific prior written permission.\r
-// \r
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\r
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-//\r
-// $Id$\r
-//\r
-using System;\r
-using System.Collections.Generic;\r
-using System.IO;\r
-using System.Reflection;\r
-using System.Threading;\r
-using System.Windows.Forms;\r
-using Radegast.Commands;\r
-using Radegast.Netcom;\r
-using Radegast.Media;\r
-using OpenMetaverse;\r
-\r
-namespace Radegast\r
-{\r
-    public class RadegastInstance\r
-    {\r
-        #region OnRadegastFormCreated\r
-        public event Action<RadegastForm> RadegastFormCreated;\r
-        /// <summary>\r
-        /// Triggers the RadegastFormCreated event.\r
-        /// </summary>\r
-        public virtual void OnRadegastFormCreated(RadegastForm radForm)\r
-        {\r
-            if (RadegastFormCreated != null) RadegastFormCreated(radForm);\r
-        }\r
-        #endregion        \r
-        private GridClient client;\r
-        private RadegastNetcom netcom;\r
-\r
-        private StateManager state;\r
-\r
-        private frmMain mainForm;\r
-\r
-        // Singleton, there can be only one instance\r
-        private static RadegastInstance globalInstance = null;\r
-        public static RadegastInstance GlobalInstance\r
-        {\r
-            get\r
-            {\r
-                if (globalInstance == null)\r
-                {\r
-                    globalInstance = new RadegastInstance(new GridClient());\r
-                }\r
-                return globalInstance;\r
-            }\r
-        }\r
-\r
-        private string userDir;\r
-        /// <summary>\r
-        /// System (not grid!) user's dir\r
-        /// </summary>\r
-        public string UserDir { get { return userDir; } }\r
-\r
-        /// <summary>\r
-        /// Grid client's user dir for settings and logs\r
-        /// </summary>\r
-        public string ClientDir\r
-        {\r
-            get\r
-            {\r
-                if (client != null && client.Self != null && !string.IsNullOrEmpty(client.Self.Name))\r
-                {\r
-                    return Path.Combine(userDir, client.Self.Name);\r
-                }\r
-                else\r
-                {\r
-                    return Environment.CurrentDirectory;\r
-                }\r
-            }\r
-        }\r
-\r
-        public string InventoryCacheFileName { get { return Path.Combine(ClientDir, "inventory.cache"); } }\r
-\r
-        private string globalLogFile;\r
-        public string GlobalLogFile { get { return globalLogFile; } }\r
-\r
-        private bool monoRuntime;\r
-        public bool MonoRuntime { get { return monoRuntime; } }\r
-\r
-        private Dictionary<UUID, Group> groups = new Dictionary<UUID, Group>();\r
-        public Dictionary<UUID, Group> Groups { get { return groups; } }\r
-\r
-        private Settings globalSettings;\r
-        /// <summary>\r
-        /// Global settings for the entire application\r
-        /// </summary>\r
-        public Settings GlobalSettings { get { return globalSettings; } }\r
-\r
-        private Settings clientSettings;\r
-        /// <summary>\r
-        /// Per client settings\r
-        /// </summary>\r
-        public Settings ClientSettings { get { return clientSettings; } }\r
-\r
-        public Dictionary<UUID, string> nameCache = new Dictionary<UUID, string>();\r
-\r
-        public const string INCOMPLETE_NAME = "Loading...";\r
-\r
-        public readonly bool advancedDebugging = false;\r
-\r
-        public readonly List<IRadegastPlugin> PluginsLoaded = new List<IRadegastPlugin>();\r
-\r
-        private MediaManager mediaManager;\r
-        /// <summary>\r
-        /// Radegast media manager for playing streams and in world sounds\r
-        /// </summary>\r
-        public MediaManager MediaManager { get { return mediaManager; } }\r
-\r
-\r
-        private CommandsManager commandsManager;\r
-        /// <summary>\r
-        /// Radegast command manager for executing textual console commands\r
-        /// </summary>\r
-        public CommandsManager CommandsManager { get { return commandsManager; } }\r
-\r
-        /// <summary>\r
-        /// Radegast ContextAction manager for context sensitive actions\r
-        /// </summary>\r
-        public ContextActionsManager ContextActionManager { get; private set; }\r
-\r
-        private RadegastMovement movement;\r
-        /// <summary>\r
-        /// Allows key emulation for moving avatar around\r
-        /// </summary>\r
-        public RadegastMovement Movement { get { return movement; } }\r
-\r
-        private InventoryClipboard inventoryClipboard;\r
-        /// <summary>\r
-        /// The last item that was cut or copied in the inventory, used for pasting\r
-        /// in a different place on the inventory, or other places like profile\r
-        /// that allow sending copied inventory items\r
-        /// </summary>\r
-        public InventoryClipboard InventoryClipboard\r
-        {\r
-            get { return inventoryClipboard; }\r
-            set\r
-            {\r
-                inventoryClipboard = value;\r
-                OnInventoryClipboardUpdated(EventArgs.Empty);\r
-            }\r
-        }\r
-\r
-        #region Events\r
-\r
-        #region ClientChanged event\r
-        /// <summary>The event subscribers, null of no subscribers</summary>\r
-        private EventHandler<ClientChangedEventArgs> m_ClientChanged;\r
-\r
-        ///<summary>Raises the ClientChanged Event</summary>\r
-        /// <param name="e">A ClientChangedEventArgs object containing\r
-        /// the old and the new client</param>\r
-        protected virtual void OnClientChanged(ClientChangedEventArgs e)\r
-        {\r
-            EventHandler<ClientChangedEventArgs> handler = m_ClientChanged;\r
-            if (handler != null)\r
-                handler(this, e);\r
-        }\r
-\r
-        /// <summary>Thread sync lock object</summary>\r
-        private readonly object m_ClientChangedLock = new object();\r
-\r
-        /// <summary>Raised when the GridClient object in the main Radegast instance is changed</summary>\r
-        public event EventHandler<ClientChangedEventArgs> ClientChanged\r
-        {\r
-            add { lock (m_ClientChangedLock) { m_ClientChanged += value; } }\r
-            remove { lock (m_ClientChangedLock) { m_ClientChanged -= value; } }\r
-        }\r
-        #endregion ClientChanged event\r
-\r
-        #region InventoryClipboardUpdated event\r
-        /// <summary>The event subscribers, null of no subscribers</summary>\r
-        private EventHandler<EventArgs> m_InventoryClipboardUpdated;\r
-\r
-        ///<summary>Raises the InventoryClipboardUpdated Event</summary>\r
-        /// <param name="e">A EventArgs object containing\r
-        /// the old and the new client</param>\r
-        protected virtual void OnInventoryClipboardUpdated(EventArgs e)\r
-        {\r
-            EventHandler<EventArgs> handler = m_InventoryClipboardUpdated;\r
-            if (handler != null)\r
-                handler(this, e);\r
-        }\r
-\r
-        /// <summary>Thread sync lock object</summary>\r
-        private readonly object m_InventoryClipboardUpdatedLock = new object();\r
-\r
-        /// <summary>Raised when the GridClient object in the main Radegast instance is changed</summary>\r
-        public event EventHandler<EventArgs> InventoryClipboardUpdated\r
-        {\r
-            add { lock (m_InventoryClipboardUpdatedLock) { m_InventoryClipboardUpdated += value; } }\r
-            remove { lock (m_InventoryClipboardUpdatedLock) { m_InventoryClipboardUpdated -= value; } }\r
-        }\r
-        #endregion InventoryClipboardUpdated event\r
-\r
-\r
-        #endregion Events\r
-\r
-        public RadegastInstance(GridClient client0)\r
-        {\r
-            // incase something else calls GlobalInstance while we are loading\r
-            globalInstance = this;\r
-\r
-#if !DEBUG\r
-            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);\r
-            Application.ThreadException += HandleThreadException;\r
-#endif\r
-\r
-            client = client0;\r
-\r
-            // Are we running mono?\r
-            monoRuntime = Type.GetType("Mono.Runtime") != null;\r
-\r
-            netcom = new RadegastNetcom(this);\r
-            state = new StateManager(this);\r
-            mediaManager = new MediaManager(this);\r
-            commandsManager = new CommandsManager(this);\r
-            ContextActionManager = new ContextActionsManager(this);\r
-            movement = new RadegastMovement(this);\r
-\r
-            InitializeLoggingAndConfig();\r
-            InitializeClient(client);\r
-\r
-            mainForm = new frmMain(this);\r
-            mainForm.InitializeControls();\r
-\r
-            mainForm.Load += new EventHandler(mainForm_Load);\r
-            ScanAndLoadPlugins();\r
-        }\r
-\r
-        private void InitializeClient(GridClient client)\r
-        {\r
-            client.Settings.MULTIPLE_SIMS = true;\r
-\r
-            client.Settings.USE_INTERPOLATION_TIMER = false;\r
-            client.Settings.ALWAYS_REQUEST_OBJECTS = true;\r
-            client.Settings.ALWAYS_DECODE_OBJECTS = true;\r
-            client.Settings.OBJECT_TRACKING = true;\r
-            client.Settings.ENABLE_SIMSTATS = true;\r
-            client.Settings.FETCH_MISSING_INVENTORY = true;\r
-            client.Settings.SEND_AGENT_THROTTLE = true;\r
-            client.Settings.SEND_AGENT_UPDATES = true;\r
-\r
-            client.Settings.USE_ASSET_CACHE = true;\r
-            client.Settings.ASSET_CACHE_DIR = Path.Combine(userDir, "cache");\r
-            client.Assets.Cache.AutoPruneEnabled = false;\r
-\r
-            client.Throttle.Total = 5000000f;\r
-            client.Settings.THROTTLE_OUTGOING_PACKETS = true;\r
-            client.Settings.LOGIN_TIMEOUT = 120 * 1000;\r
-            client.Settings.SIMULATOR_TIMEOUT = 120 * 1000;\r
-            client.Settings.MAX_CONCURRENT_TEXTURE_DOWNLOADS = 20;\r
-\r
-            RegisterClientEvents(client);\r
-        }\r
-\r
-        private void RegisterClientEvents(GridClient client)\r
-        {\r
-            client.Groups.CurrentGroups += new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);\r
-            client.Groups.GroupLeaveReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);\r
-            client.Groups.GroupDropped += new EventHandler<GroupDroppedEventArgs>(Groups_GroupsChanged);\r
-            client.Groups.GroupJoinedReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);\r
-            client.Avatars.UUIDNameReply += new EventHandler<UUIDNameReplyEventArgs>(Avatars_UUIDNameReply);\r
-            netcom.ClientConnected += new EventHandler<EventArgs>(netcom_ClientConnected);\r
-       }\r
-\r
-        private void UnregisterClientEvents(GridClient client)\r
-        {\r
-            client.Groups.CurrentGroups -= new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);\r
-            client.Groups.GroupLeaveReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);\r
-            client.Groups.GroupDropped -= new EventHandler<GroupDroppedEventArgs>(Groups_GroupsChanged);\r
-            client.Groups.GroupJoinedReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);\r
-            client.Avatars.UUIDNameReply -= new EventHandler<UUIDNameReplyEventArgs>(Avatars_UUIDNameReply);\r
-            netcom.ClientConnected -= new EventHandler<EventArgs>(netcom_ClientConnected);\r
-        }\r
-\r
-        public void Reconnect()\r
-        {\r
-            TabConsole.DisplayNotificationInChat("Attempting to reconnect...", ChatBufferTextStyle.StatusDarkBlue);\r
-            Logger.Log("Attemting to reconnect", Helpers.LogLevel.Info, client);\r
-            GridClient oldClient = client;\r
-            client = new GridClient();\r
-            UnregisterClientEvents(oldClient);\r
-            InitializeClient(client);\r
-            OnClientChanged(new ClientChangedEventArgs(oldClient, client));\r
-            netcom.Login();\r
-        }\r
-\r
-        public void CleanUp()\r
-        {\r
-            if (client != null)\r
-            {\r
-                UnregisterClientEvents(client);\r
-            }\r
-\r
-            lock (PluginsLoaded)\r
-            {\r
-                List<IRadegastPlugin> unload = new List<IRadegastPlugin>(PluginsLoaded);\r
-                unload.ForEach(plug =>\r
-               {\r
-                   PluginsLoaded.Remove(plug);\r
-                   try\r
-                   {\r
-                       plug.StopPlugin(this);\r
-                   }\r
-                   catch (Exception ex)\r
-                   {\r
-                       Logger.Log("ERROR in Shutdown Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug, ex);\r
-                   }\r
-               });\r
-            }\r
-\r
-            if (movement != null)\r
-            {\r
-                movement.Dispose();\r
-                movement = null;\r
-            }\r
-            if (commandsManager != null)\r
-            {\r
-                commandsManager.Dispose();\r
-                commandsManager = null;\r
-            }\r
-            if (ContextActionManager != null)\r
-            {\r
-                ContextActionManager.Dispose();\r
-                ContextActionManager = null;\r
-            }\r
-            if (mediaManager != null)\r
-            {\r
-                mediaManager.Dispose();\r
-                mediaManager = null;\r
-            }\r
-            if (state != null)\r
-            {\r
-                state.Dispose();\r
-                state = null;\r
-            }\r
-            if (netcom != null)\r
-            {\r
-                netcom.Dispose();\r
-                netcom = null;\r
-            }\r
-            if (mainForm != null)\r
-            {\r
-                mainForm.Load -= new EventHandler(mainForm_Load);\r
-            }\r
-            Logger.Log("RadegastInstance finished cleaning up.", Helpers.LogLevel.Debug);\r
-        }\r
-\r
-        void mainForm_Load(object sender, EventArgs e)\r
-        {\r
-            StartPlugins();\r
-        }\r
-\r
-        private void StartPlugins()\r
-        {\r
-            lock (PluginsLoaded)\r
-            {\r
-                foreach (IRadegastPlugin plug in PluginsLoaded)\r
-                {\r
-                    try\r
-                    {\r
-                        plug.StartPlugin(this);\r
-                    }\r
-                    catch (Exception ex)\r
-                    {\r
-                        Logger.Log("ERROR in Starting Radegast Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        private void ScanAndLoadPlugins()\r
-        {\r
-            string dirName = Application.StartupPath;\r
-\r
-            if (!Directory.Exists(dirName)) return;\r
-\r
-            foreach (string loadfilename in Directory.GetFiles(dirName))\r
-            {\r
-                if (loadfilename.ToLower().EndsWith(".dll") || loadfilename.ToLower().EndsWith(".exe"))\r
-                {\r
-                    try\r
-                    {\r
-                        Assembly assembly = Assembly.LoadFile(loadfilename);\r
-                        LoadAssembly(loadfilename, assembly);\r
-                    }\r
-                    catch (BadImageFormatException)\r
-                    {\r
-                        // non .NET .dlls\r
-                    }\r
-                    catch (ReflectionTypeLoadException)\r
-                    {\r
-                        // Out of date or dlls missing sub dependencies\r
-                    }\r
-                    catch (TypeLoadException)\r
-                    {\r
-                        // Another version of: Out of date or dlls missing sub dependencies\r
-                    }\r
-                    catch (Exception ex)\r
-                    {\r
-                        Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " because " + ex, Helpers.LogLevel.Debug);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        public void LoadAssembly(string loadfilename, Assembly assembly)\r
-        {\r
-            foreach (Type type in assembly.GetTypes())\r
-            {\r
-                if (typeof(IRadegastPlugin).IsAssignableFrom(type))\r
-                {\r
-                    if  (type.IsInterface) continue;\r
-                    try\r
-                    {\r
-                        IRadegastPlugin plug;\r
-                        ConstructorInfo constructorInfo = type.GetConstructor(new Type[] {typeof (RadegastInstance)});\r
-                        if (constructorInfo != null)\r
-                            plug = (IRadegastPlugin) constructorInfo.Invoke(new[] {this});\r
-                        else\r
-                        {\r
-                            constructorInfo = type.GetConstructor(new Type[] {});\r
-                            if (constructorInfo != null)\r
-                                plug = (IRadegastPlugin) constructorInfo.Invoke(new object[0]);\r
-                            else\r
-                            {\r
-                                Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because "+type+ " has no usable constructor.",Helpers.LogLevel.Debug);\r
-                                continue;\r
-                            }\r
-                        }\r
-                        lock (PluginsLoaded) PluginsLoaded.Add(plug);\r
-                    }\r
-                    catch (Exception ex)\r
-                    {\r
-                        Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + ex,\r
-                                   Helpers.LogLevel.Debug);\r
-                    }\r
-                }\r
-                else\r
-                {\r
-                    try\r
-                    {\r
-                        commandsManager.LoadType(type);\r
-                    }\r
-                    catch (Exception ex)\r
-                    {\r
-                        Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " Command: " + type +\r
-                                   " because " + ex.Message + " " + ex.StackTrace, Helpers.LogLevel.Debug);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        void netcom_ClientConnected(object sender, EventArgs e)\r
-        {\r
-            try\r
-            {\r
-                if (!Directory.Exists(ClientDir))\r
-                    Directory.CreateDirectory(ClientDir);\r
-            }\r
-            catch (Exception ex)\r
-            {\r
-                Logger.Log("Failed to create client directory", Helpers.LogLevel.Warning, ex);\r
-            }\r
-\r
-            clientSettings = new Settings(Path.Combine(ClientDir, "client_settings.xml"));\r
-        }\r
-\r
-\r
-        void Avatars_UUIDNameReply(object sender, UUIDNameReplyEventArgs e)\r
-        {\r
-            lock (nameCache)\r
-            {\r
-                foreach (KeyValuePair<UUID, string> av in e.Names)\r
-                {\r
-                    if (!nameCache.ContainsKey(av.Key))\r
-                    {\r
-                        nameCache.Add(av.Key, av.Value);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        public string getAvatarName(UUID key)\r
-        {\r
-            lock (nameCache)\r
-            {\r
-                if (key == UUID.Zero)\r
-                {\r
-                    return "(???) (???)";\r
-                }\r
-                if (nameCache.ContainsKey(key))\r
-                {\r
-                    return nameCache[key];\r
-                }\r
-                else\r
-                {\r
-                    client.Avatars.RequestAvatarName(key);\r
-                    return INCOMPLETE_NAME;\r
-                }\r
-            }\r
-        }\r
-\r
-        public void getAvatarNames(List<UUID> keys)\r
-        {\r
-            lock (nameCache)\r
-            {\r
-                List<UUID> newNames = new List<UUID>();\r
-                foreach (UUID key in keys)\r
-                {\r
-                    if (!nameCache.ContainsKey(key))\r
-                    {\r
-                        newNames.Add(key);\r
-                    }\r
-                }\r
-                if (newNames.Count > 0)\r
-                {\r
-                    client.Avatars.RequestAvatarNames(newNames);\r
-                }\r
-            }\r
-        }\r
-\r
-        public bool haveAvatarName(UUID key)\r
-        {\r
-            lock (nameCache)\r
-            {\r
-                if (nameCache.ContainsKey(key))\r
-                    return true;\r
-                else\r
-                    return false;\r
-            }\r
-        }\r
-\r
-        void Groups_GroupsChanged(object sender, EventArgs e)\r
-        {\r
-            client.Groups.RequestCurrentGroups();\r
-        }\r
-\r
-        public static string SafeFileName(string fileName)\r
-        {\r
-            foreach (char lDisallowed in Path.GetInvalidFileNameChars())\r
-            {\r
-                fileName = fileName.Replace(lDisallowed.ToString(), "_");\r
-            }\r
-\r
-            return fileName;\r
-        }\r
-\r
-        public void LogClientMessage(string fileName, string message)\r
-        {\r
-            lock (this)\r
-            {\r
-                try\r
-                {\r
-                    foreach (char lDisallowed in System.IO.Path.GetInvalidFileNameChars())\r
-                    {\r
-                        fileName = fileName.Replace(lDisallowed.ToString(), "_");\r
-                    }\r
-\r
-                    File.AppendAllText(Path.Combine(ClientDir, fileName),\r
-                        DateTime.Now.ToString("yyyy-MM-dd [HH:mm:ss] ") + message + Environment.NewLine);\r
-                }\r
-                catch (Exception) { }\r
-            }\r
-        }\r
-\r
-        void Groups_CurrentGroups(object sender, CurrentGroupsEventArgs e)\r
-        {\r
-            this.groups = e.Groups;\r
-        }\r
-\r
-        private void InitializeLoggingAndConfig()\r
-        {\r
-            try\r
-            {\r
-                userDir = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), Properties.Resources.ProgramName);\r
-                if (!Directory.Exists(userDir))\r
-                {\r
-                    Directory.CreateDirectory(userDir);\r
-                }\r
-            }\r
-            catch (Exception)\r
-            {\r
-                userDir = System.Environment.CurrentDirectory;\r
-            };\r
-\r
-            globalLogFile = Path.Combine(userDir, Properties.Resources.ProgramName + ".log");\r
-            globalSettings = new Settings(Path.Combine(userDir, "settings.xml"));\r
-        }\r
-\r
-        public GridClient Client\r
-        {\r
-            get { return client; }\r
-        }\r
-\r
-        public RadegastNetcom Netcom\r
-        {\r
-            get { return netcom; }\r
-        }\r
-\r
-        public StateManager State\r
-        {\r
-            get { return state; }\r
-        }\r
-\r
-        public frmMain MainForm\r
-        {\r
-            get { return mainForm; }\r
-        }\r
-\r
-        public TabsConsole TabConsole\r
-        {\r
-            get { return mainForm.TabConsole; }\r
-        }\r
-\r
-        public void HandleThreadException(object sender, ThreadExceptionEventArgs e)\r
-        {\r
-            Logger.Log("Unhandled Thread Exception: " \r
-                + e.Exception.Message + Environment.NewLine\r
-                + e.Exception.StackTrace + Environment.NewLine,\r
-                Helpers.LogLevel.Error,\r
-                client);\r
-#if !DEBUG\r
-            Application.Exit();\r
-#endif\r
-        }\r
-    }\r
-\r
-    #region Event classes\r
-    public class ClientChangedEventArgs : EventArgs\r
-    {\r
-        private GridClient m_OldClient;\r
-        private GridClient m_Client;\r
-\r
-        public GridClient OldClient { get { return m_OldClient; } }\r
-        public GridClient Client { get { return m_Client; } }\r
-\r
-        public ClientChangedEventArgs(GridClient OldClient, GridClient Client)\r
-        {\r
-            m_OldClient = OldClient;\r
-            m_Client = Client;\r
-        }\r
-    }\r
-    #endregion Event classes\r
-}\r
+// 
+// Radegast Metaverse Client
+// Copyright (c) 2009-2010, 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.IO;
+using System.Reflection;
+using System.Threading;
+using System.Windows.Forms;
+using Radegast.Commands;
+using Radegast.Netcom;
+using Radegast.Media;
+using OpenMetaverse;
+
+namespace Radegast
+{
+    public class RadegastInstance
+    {
+        #region OnRadegastFormCreated
+        public event Action<RadegastForm> RadegastFormCreated;
+        /// <summary>
+        /// Triggers the RadegastFormCreated event.
+        /// </summary>
+        public virtual void OnRadegastFormCreated(RadegastForm radForm)
+        {
+            if (RadegastFormCreated != null) RadegastFormCreated(radForm);
+        }
+        #endregion
+
+        private GridClient client;
+        private RadegastNetcom netcom;
+
+        private StateManager state;
+
+        private frmMain mainForm;
+
+        // Singleton, there can be only one instance
+        private static RadegastInstance globalInstance = null;
+        public static RadegastInstance GlobalInstance
+        {
+            get
+            {
+                if (globalInstance == null)
+                {
+                    globalInstance = new RadegastInstance(new GridClient());
+                }
+                return globalInstance;
+            }
+        }
+
+        /// <summary>
+        /// When was Radegast started (UTC)
+        /// </summary>
+        public readonly DateTime StartupTimeUTC;
+
+        /// <summary>
+        /// Time zone of the current world (currently hard coded to US Pacific time)
+        /// </summary>
+        public TimeZoneInfo WordTimeZone;
+
+        private string userDir;
+        /// <summary>
+        /// System (not grid!) user's dir
+        /// </summary>
+        public string UserDir { get { return userDir; } }
+
+        /// <summary>
+        /// Grid client's user dir for settings and logs
+        /// </summary>
+        public string ClientDir
+        {
+            get
+            {
+                if (client != null && client.Self != null && !string.IsNullOrEmpty(client.Self.Name))
+                {
+                    return Path.Combine(userDir, client.Self.Name);
+                }
+                else
+                {
+                    return Environment.CurrentDirectory;
+                }
+            }
+        }
+
+        public string InventoryCacheFileName { get { return Path.Combine(ClientDir, "inventory.cache"); } }
+
+        private string globalLogFile;
+        public string GlobalLogFile { get { return globalLogFile; } }
+
+        private bool monoRuntime;
+        public bool MonoRuntime { get { return monoRuntime; } }
+
+        private Dictionary<UUID, Group> groups = new Dictionary<UUID, Group>();
+        public Dictionary<UUID, Group> Groups { get { return groups; } }
+
+        private Settings globalSettings;
+        /// <summary>
+        /// Global settings for the entire application
+        /// </summary>
+        public Settings GlobalSettings { get { return globalSettings; } }
+
+        private Settings clientSettings;
+        /// <summary>
+        /// Per client settings
+        /// </summary>
+        public Settings ClientSettings { get { return clientSettings; } }
+
+        public Dictionary<UUID, string> nameCache = new Dictionary<UUID, string>();
+
+        public const string INCOMPLETE_NAME = "Loading...";
+
+        public readonly bool advancedDebugging = false;
+
+        private PluginManager pluginManager;
+        /// <summary> Handles loading plugins and scripts</summary>
+        public PluginManager PluginManager { get { return pluginManager; } }
+
+        private MediaManager mediaManager;
+        /// <summary>
+        /// Radegast media manager for playing streams and in world sounds
+        /// </summary>
+        public MediaManager MediaManager { get { return mediaManager; } }
+
+
+        private CommandsManager commandsManager;
+        /// <summary>
+        /// Radegast command manager for executing textual console commands
+        /// </summary>
+        public CommandsManager CommandsManager { get { return commandsManager; } }
+
+        /// <summary>
+        /// Radegast ContextAction manager for context sensitive actions
+        /// </summary>
+        public ContextActionsManager ContextActionManager { get; private set; }
+
+        private RadegastMovement movement;
+        /// <summary>
+        /// Allows key emulation for moving avatar around
+        /// </summary>
+        public RadegastMovement Movement { get { return movement; } }
+
+        private InventoryClipboard inventoryClipboard;
+        /// <summary>
+        /// The last item that was cut or copied in the inventory, used for pasting
+        /// in a different place on the inventory, or other places like profile
+        /// that allow sending copied inventory items
+        /// </summary>
+        public InventoryClipboard InventoryClipboard
+        {
+            get { return inventoryClipboard; }
+            set
+            {
+                inventoryClipboard = value;
+                OnInventoryClipboardUpdated(EventArgs.Empty);
+            }
+        }
+
+        private RLVManager rlv;
+
+        /// <summary>
+        /// Manager for RLV functionality
+        /// </summary>
+        public RLVManager RLV { get { return rlv; } }
+
+        private GridManager gridManager;
+        /// <summary>Manages default params for different grids</summary>
+        public GridManager GridManger { get { return gridManager; } }
+
+        #region Events
+
+        #region ClientChanged event
+        /// <summary>The event subscribers, null of no subscribers</summary>
+        private EventHandler<ClientChangedEventArgs> m_ClientChanged;
+
+        ///<summary>Raises the ClientChanged Event</summary>
+        /// <param name="e">A ClientChangedEventArgs object containing
+        /// the old and the new client</param>
+        protected virtual void OnClientChanged(ClientChangedEventArgs e)
+        {
+            EventHandler<ClientChangedEventArgs> handler = m_ClientChanged;
+            if (handler != null)
+                handler(this, e);
+        }
+
+        /// <summary>Thread sync lock object</summary>
+        private readonly object m_ClientChangedLock = new object();
+
+        /// <summary>Raised when the GridClient object in the main Radegast instance is changed</summary>
+        public event EventHandler<ClientChangedEventArgs> ClientChanged
+        {
+            add { lock (m_ClientChangedLock) { m_ClientChanged += value; } }
+            remove { lock (m_ClientChangedLock) { m_ClientChanged -= value; } }
+        }
+        #endregion ClientChanged event
+
+        #region InventoryClipboardUpdated event
+        /// <summary>The event subscribers, null of no subscribers</summary>
+        private EventHandler<EventArgs> m_InventoryClipboardUpdated;
+
+        ///<summary>Raises the InventoryClipboardUpdated Event</summary>
+        /// <param name="e">A EventArgs object containing
+        /// the old and the new client</param>
+        protected virtual void OnInventoryClipboardUpdated(EventArgs e)
+        {
+            EventHandler<EventArgs> handler = m_InventoryClipboardUpdated;
+            if (handler != null)
+                handler(this, e);
+        }
+
+        /// <summary>Thread sync lock object</summary>
+        private readonly object m_InventoryClipboardUpdatedLock = new object();
+
+        /// <summary>Raised when the GridClient object in the main Radegast instance is changed</summary>
+        public event EventHandler<EventArgs> InventoryClipboardUpdated
+        {
+            add { lock (m_InventoryClipboardUpdatedLock) { m_InventoryClipboardUpdated += value; } }
+            remove { lock (m_InventoryClipboardUpdatedLock) { m_InventoryClipboardUpdated -= value; } }
+        }
+        #endregion InventoryClipboardUpdated event
+
+
+        #endregion Events
+
+        public RadegastInstance(GridClient client0)
+        {
+            // incase something else calls GlobalInstance while we are loading
+            globalInstance = this;
+
+#if !DEBUG
+            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
+            Application.ThreadException += HandleThreadException;
+#endif
+
+            client = client0;
+
+            // Initialize current time zone, and mark when we started
+            GetWorldTimeZone();
+            StartupTimeUTC = DateTime.UtcNow;
+
+            // Are we running mono?
+            monoRuntime = Type.GetType("Mono.Runtime") != null;
+
+            netcom = new RadegastNetcom(this);
+            state = new StateManager(this);
+            mediaManager = new MediaManager(this);
+            commandsManager = new CommandsManager(this);
+            ContextActionManager = new ContextActionsManager(this);
+            movement = new RadegastMovement(this);
+
+            InitializeLoggingAndConfig();
+            InitializeClient(client);
+
+            rlv = new RLVManager(this);
+            gridManager = new GridManager(this);
+            gridManager.LoadGrids();
+
+            mainForm = new frmMain(this);
+            mainForm.InitializeControls();
+
+            mainForm.Load += new EventHandler(mainForm_Load);
+            pluginManager = new PluginManager(this);
+            pluginManager.ScanAndLoadPlugins();
+        }
+
+        private void InitializeClient(GridClient client)
+        {
+            client.Settings.MULTIPLE_SIMS = true;
+
+            client.Settings.USE_INTERPOLATION_TIMER = false;
+            client.Settings.ALWAYS_REQUEST_OBJECTS = true;
+            client.Settings.ALWAYS_DECODE_OBJECTS = true;
+            client.Settings.OBJECT_TRACKING = true;
+            client.Settings.ENABLE_SIMSTATS = true;
+            client.Settings.FETCH_MISSING_INVENTORY = true;
+            client.Settings.SEND_AGENT_THROTTLE = true;
+            client.Settings.SEND_AGENT_UPDATES = true;
+
+            client.Settings.USE_ASSET_CACHE = true;
+            client.Settings.ASSET_CACHE_DIR = Path.Combine(userDir, "cache");
+            client.Assets.Cache.AutoPruneEnabled = false;
+
+            client.Throttle.Total = 5000000f;
+            client.Settings.THROTTLE_OUTGOING_PACKETS = false;
+            client.Settings.LOGIN_TIMEOUT = 120 * 1000;
+            client.Settings.SIMULATOR_TIMEOUT = 120 * 1000;
+            client.Settings.MAX_CONCURRENT_TEXTURE_DOWNLOADS = 20;
+
+            RegisterClientEvents(client);
+        }
+
+        private void RegisterClientEvents(GridClient client)
+        {
+            client.Groups.CurrentGroups += new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);
+            client.Groups.GroupLeaveReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);
+            client.Groups.GroupDropped += new EventHandler<GroupDroppedEventArgs>(Groups_GroupsChanged);
+            client.Groups.GroupJoinedReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);
+            client.Avatars.UUIDNameReply += new EventHandler<UUIDNameReplyEventArgs>(Avatars_UUIDNameReply);
+            if (netcom != null)
+                netcom.ClientConnected += new EventHandler<EventArgs>(netcom_ClientConnected);
+        }
+
+        private void UnregisterClientEvents(GridClient client)
+        {
+            client.Groups.CurrentGroups -= new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);
+            client.Groups.GroupLeaveReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);
+            client.Groups.GroupDropped -= new EventHandler<GroupDroppedEventArgs>(Groups_GroupsChanged);
+            client.Groups.GroupJoinedReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);
+            client.Avatars.UUIDNameReply -= new EventHandler<UUIDNameReplyEventArgs>(Avatars_UUIDNameReply);
+            if (netcom != null)
+                netcom.ClientConnected -= new EventHandler<EventArgs>(netcom_ClientConnected);
+        }
+
+        private void GetWorldTimeZone()
+        {
+            try
+            {
+                foreach (TimeZoneInfo tz in TimeZoneInfo.GetSystemTimeZones())
+                {
+                    if (tz.Id == "Pacific Standard Time" || tz.Id == "America/Los_Angeles")
+                    {
+                        WordTimeZone = tz;
+                        break;
+                    }
+                }
+            }
+            catch (Exception) { }
+        }
+
+        public DateTime GetWorldTime()
+        {
+            DateTime now;
+            
+            try
+            {
+                if (WordTimeZone != null)
+                    now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, WordTimeZone);
+                else
+                    now = DateTime.UtcNow.AddHours(-7);
+            }
+            catch (Exception)
+            {
+                now = DateTime.UtcNow.AddHours(-7);
+            }
+
+            return now;
+        }
+
+
+        public void Reconnect()
+        {
+            TabConsole.DisplayNotificationInChat("Attempting to reconnect...", ChatBufferTextStyle.StatusDarkBlue);
+            Logger.Log("Attemting to reconnect", Helpers.LogLevel.Info, client);
+            GridClient oldClient = client;
+            client = new GridClient();
+            UnregisterClientEvents(oldClient);
+            InitializeClient(client);
+            OnClientChanged(new ClientChangedEventArgs(oldClient, client));
+            netcom.Login();
+        }
+
+        public void CleanUp()
+        {
+            if (gridManager != null)
+            {
+                gridManager.Dispose();
+                gridManager = null;
+            }
+
+            if (rlv != null)
+            {
+                rlv.Dispose();
+                rlv = null;
+            }
+
+            if (client != null)
+            {
+                UnregisterClientEvents(client);
+            }
+
+            if (pluginManager != null)
+            {
+                pluginManager.Dispose();
+                pluginManager = null;
+            }
+
+            if (movement != null)
+            {
+                movement.Dispose();
+                movement = null;
+            }
+            if (commandsManager != null)
+            {
+                commandsManager.Dispose();
+                commandsManager = null;
+            }
+            if (ContextActionManager != null)
+            {
+                ContextActionManager.Dispose();
+                ContextActionManager = null;
+            }
+            if (mediaManager != null)
+            {
+                mediaManager.Dispose();
+                mediaManager = null;
+            }
+            if (state != null)
+            {
+                state.Dispose();
+                state = null;
+            }
+            if (netcom != null)
+            {
+                netcom.Dispose();
+                netcom = null;
+            }
+            if (mainForm != null)
+            {
+                mainForm.Load -= new EventHandler(mainForm_Load);
+            }
+            Logger.Log("RadegastInstance finished cleaning up.", Helpers.LogLevel.Debug);
+        }
+
+        void mainForm_Load(object sender, EventArgs e)
+        {
+            pluginManager.StartPlugins();
+        }
+
+        void netcom_ClientConnected(object sender, EventArgs e)
+        {
+            try
+            {
+                if (!Directory.Exists(ClientDir))
+                    Directory.CreateDirectory(ClientDir);
+            }
+            catch (Exception ex)
+            {
+                Logger.Log("Failed to create client directory", Helpers.LogLevel.Warning, ex);
+            }
+
+            clientSettings = new Settings(Path.Combine(ClientDir, "client_settings.xml"));
+        }
+
+
+        void Avatars_UUIDNameReply(object sender, UUIDNameReplyEventArgs e)
+        {
+            lock (nameCache)
+            {
+                foreach (KeyValuePair<UUID, string> av in e.Names)
+                {
+                    if (!nameCache.ContainsKey(av.Key))
+                    {
+                        nameCache.Add(av.Key, av.Value);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Fetches avatar name
+        /// </summary>
+        /// <param name="key">Avatar UUID</param>
+        /// <param name="blocking">Should we wait until the name is retrieved</param>
+        /// <returns>Avatar name</returns>
+        public string getAvatarName(UUID key, bool blocking)
+        {
+            if (!blocking)
+                return getAvatarName(key);
+
+            string name = null;
+
+            using (ManualResetEvent gotName = new ManualResetEvent(false))
+            {
+
+                EventHandler<UUIDNameReplyEventArgs> handler = (object sender, UUIDNameReplyEventArgs e) =>
+                    {
+                        if (e.Names.ContainsKey(key))
+                        {
+                            name = e.Names[key];
+                            gotName.Set();
+                        }
+                    };
+
+                client.Avatars.UUIDNameReply += handler;
+                name = getAvatarName(key);
+
+                if (name == INCOMPLETE_NAME)
+                {
+                    gotName.WaitOne(10 * 1000, false);
+                }
+
+                client.Avatars.UUIDNameReply -= handler;
+            }
+            return name;
+
+        }
+
+        /// <summary>
+        /// Fetches avatar name from cache, if not in cache will requst name from the server
+        /// </summary>
+        /// <param name="key">Avatar UUID</param>
+        /// <returns>Avatar name</returns>
+        public string getAvatarName(UUID key)
+        {
+            lock (nameCache)
+            {
+                if (key == UUID.Zero)
+                {
+                    return "(???) (???)";
+                }
+                if (nameCache.ContainsKey(key))
+                {
+                    return nameCache[key];
+                }
+                else
+                {
+                    client.Avatars.RequestAvatarName(key);
+                    return INCOMPLETE_NAME;
+                }
+            }
+        }
+
+        public void getAvatarNames(List<UUID> keys)
+        {
+            lock (nameCache)
+            {
+                List<UUID> newNames = new List<UUID>();
+                foreach (UUID key in keys)
+                {
+                    if (!nameCache.ContainsKey(key))
+                    {
+                        newNames.Add(key);
+                    }
+                }
+                if (newNames.Count > 0)
+                {
+                    client.Avatars.RequestAvatarNames(newNames);
+                }
+            }
+        }
+
+        public bool haveAvatarName(UUID key)
+        {
+            lock (nameCache)
+            {
+                if (nameCache.ContainsKey(key))
+                    return true;
+                else
+                    return false;
+            }
+        }
+
+        void Groups_GroupsChanged(object sender, EventArgs e)
+        {
+            client.Groups.RequestCurrentGroups();
+        }
+
+        public static string SafeFileName(string fileName)
+        {
+            foreach (char lDisallowed in Path.GetInvalidFileNameChars())
+            {
+                fileName = fileName.Replace(lDisallowed.ToString(), "_");
+            }
+
+            return fileName;
+        }
+
+        public void LogClientMessage(string fileName, string message)
+        {
+            lock (this)
+            {
+                try
+                {
+                    foreach (char lDisallowed in System.IO.Path.GetInvalidFileNameChars())
+                    {
+                        fileName = fileName.Replace(lDisallowed.ToString(), "_");
+                    }
+
+                    File.AppendAllText(Path.Combine(ClientDir, fileName),
+                        DateTime.Now.ToString("yyyy-MM-dd [HH:mm:ss] ") + message + Environment.NewLine);
+                }
+                catch (Exception) { }
+            }
+        }
+
+        void Groups_CurrentGroups(object sender, CurrentGroupsEventArgs e)
+        {
+            this.groups = e.Groups;
+        }
+
+        private void InitializeLoggingAndConfig()
+        {
+            try
+            {
+                userDir = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), Properties.Resources.ProgramName);
+                if (!Directory.Exists(userDir))
+                {
+                    Directory.CreateDirectory(userDir);
+                }
+            }
+            catch (Exception)
+            {
+                userDir = System.Environment.CurrentDirectory;
+            };
+
+            globalLogFile = Path.Combine(userDir, Properties.Resources.ProgramName + ".log");
+            globalSettings = new Settings(Path.Combine(userDir, "settings.xml"));
+        }
+
+        public GridClient Client
+        {
+            get { return client; }
+        }
+
+        public RadegastNetcom Netcom
+        {
+            get { return netcom; }
+        }
+
+        public StateManager State
+        {
+            get { return state; }
+        }
+
+        public frmMain MainForm
+        {
+            get { return mainForm; }
+        }
+
+        public TabsConsole TabConsole
+        {
+            get { return mainForm.TabConsole; }
+        }
+
+        public void HandleThreadException(object sender, ThreadExceptionEventArgs e)
+        {
+            Logger.Log("Unhandled Thread Exception: "
+                + e.Exception.Message + Environment.NewLine
+                + e.Exception.StackTrace + Environment.NewLine,
+                Helpers.LogLevel.Error,
+                client);
+#if DEBUG
+            Application.Exit();
+#endif
+        }
+    }
+
+    #region Event classes
+    public class ClientChangedEventArgs : EventArgs
+    {
+        private GridClient m_OldClient;
+        private GridClient m_Client;
+
+        public GridClient OldClient { get { return m_OldClient; } }
+        public GridClient Client { get { return m_Client; } }
+
+        public ClientChangedEventArgs(GridClient OldClient, GridClient Client)
+        {
+            m_OldClient = OldClient;
+            m_Client = Client;
+        }
+    }
+    #endregion Event classes
+}