OSDN Git Service

RAD-84 progress, added IM receiving and sending restrictions
[radegast/radegast.git] / Radegast / Core / RadegastInstance.cs
1 // \r
2 // Radegast Metaverse Client\r
3 // Copyright (c) 2009, Radegast Development Team\r
4 // All rights reserved.\r
5 // \r
6 // Redistribution and use in source and binary forms, with or without\r
7 // modification, are permitted provided that the following conditions are met:\r
8 // \r
9 //     * Redistributions of source code must retain the above copyright notice,\r
10 //       this list of conditions and the following disclaimer.\r
11 //     * Redistributions in binary form must reproduce the above copyright\r
12 //       notice, this list of conditions and the following disclaimer in the\r
13 //       documentation and/or other materials provided with the distribution.\r
14 //     * Neither the name of the application "Radegast", nor the names of its\r
15 //       contributors may be used to endorse or promote products derived from\r
16 //       this software without specific prior written permission.\r
17 // \r
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\r
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
28 //\r
29 // $Id$\r
30 //\r
31 using System;\r
32 using System.Collections.Generic;\r
33 using System.IO;\r
34 using System.Reflection;\r
35 using System.Threading;\r
36 using System.Windows.Forms;\r
37 using Radegast.Commands;\r
38 using Radegast.Netcom;\r
39 using Radegast.Media;\r
40 using OpenMetaverse;\r
41 \r
42 namespace Radegast\r
43 {\r
44     public class RadegastInstance\r
45     {\r
46         #region OnRadegastFormCreated\r
47         public event Action<RadegastForm> RadegastFormCreated;\r
48         /// <summary>\r
49         /// Triggers the RadegastFormCreated event.\r
50         /// </summary>\r
51         public virtual void OnRadegastFormCreated(RadegastForm radForm)\r
52         {\r
53             if (RadegastFormCreated != null) RadegastFormCreated(radForm);\r
54         }\r
55         #endregion        \r
56         private GridClient client;\r
57         private RadegastNetcom netcom;\r
58 \r
59         private StateManager state;\r
60 \r
61         private frmMain mainForm;\r
62 \r
63         // Singleton, there can be only one instance\r
64         private static RadegastInstance globalInstance = null;\r
65         public static RadegastInstance GlobalInstance\r
66         {\r
67             get\r
68             {\r
69                 if (globalInstance == null)\r
70                 {\r
71                     globalInstance = new RadegastInstance(new GridClient());\r
72                 }\r
73                 return globalInstance;\r
74             }\r
75         }\r
76 \r
77         private string userDir;\r
78         /// <summary>\r
79         /// System (not grid!) user's dir\r
80         /// </summary>\r
81         public string UserDir { get { return userDir; } }\r
82 \r
83         /// <summary>\r
84         /// Grid client's user dir for settings and logs\r
85         /// </summary>\r
86         public string ClientDir\r
87         {\r
88             get\r
89             {\r
90                 if (client != null && client.Self != null && !string.IsNullOrEmpty(client.Self.Name))\r
91                 {\r
92                     return Path.Combine(userDir, client.Self.Name);\r
93                 }\r
94                 else\r
95                 {\r
96                     return Environment.CurrentDirectory;\r
97                 }\r
98             }\r
99         }\r
100 \r
101         public string InventoryCacheFileName { get { return Path.Combine(ClientDir, "inventory.cache"); } }\r
102 \r
103         private string globalLogFile;\r
104         public string GlobalLogFile { get { return globalLogFile; } }\r
105 \r
106         private bool monoRuntime;\r
107         public bool MonoRuntime { get { return monoRuntime; } }\r
108 \r
109         private Dictionary<UUID, Group> groups = new Dictionary<UUID, Group>();\r
110         public Dictionary<UUID, Group> Groups { get { return groups; } }\r
111 \r
112         private Settings globalSettings;\r
113         /// <summary>\r
114         /// Global settings for the entire application\r
115         /// </summary>\r
116         public Settings GlobalSettings { get { return globalSettings; } }\r
117 \r
118         private Settings clientSettings;\r
119         /// <summary>\r
120         /// Per client settings\r
121         /// </summary>\r
122         public Settings ClientSettings { get { return clientSettings; } }\r
123 \r
124         public Dictionary<UUID, string> nameCache = new Dictionary<UUID, string>();\r
125 \r
126         public const string INCOMPLETE_NAME = "Loading...";\r
127 \r
128         public readonly bool advancedDebugging = false;\r
129 \r
130         public readonly List<IRadegastPlugin> PluginsLoaded = new List<IRadegastPlugin>();\r
131 \r
132         private MediaManager mediaManager;\r
133         /// <summary>\r
134         /// Radegast media manager for playing streams and in world sounds\r
135         /// </summary>\r
136         public MediaManager MediaManager { get { return mediaManager; } }\r
137 \r
138 \r
139         private CommandsManager commandsManager;\r
140         /// <summary>\r
141         /// Radegast command manager for executing textual console commands\r
142         /// </summary>\r
143         public CommandsManager CommandsManager { get { return commandsManager; } }\r
144 \r
145         /// <summary>\r
146         /// Radegast ContextAction manager for context sensitive actions\r
147         /// </summary>\r
148         public ContextActionsManager ContextActionManager { get; private set; }\r
149 \r
150         private RadegastMovement movement;\r
151         /// <summary>\r
152         /// Allows key emulation for moving avatar around\r
153         /// </summary>\r
154         public RadegastMovement Movement { get { return movement; } }\r
155 \r
156         private InventoryClipboard inventoryClipboard;\r
157         /// <summary>\r
158         /// The last item that was cut or copied in the inventory, used for pasting\r
159         /// in a different place on the inventory, or other places like profile\r
160         /// that allow sending copied inventory items\r
161         /// </summary>\r
162         public InventoryClipboard InventoryClipboard\r
163         {\r
164             get { return inventoryClipboard; }\r
165             set\r
166             {\r
167                 inventoryClipboard = value;\r
168                 OnInventoryClipboardUpdated(EventArgs.Empty);\r
169             }\r
170         }\r
171 \r
172         private RLVManager rlv;\r
173 \r
174         /// <summary>\r
175         /// Manager for RLV functionality\r
176         /// </summary>\r
177         public RLVManager RLV { get { return rlv; } }\r
178 \r
179         #region Events\r
180 \r
181         #region ClientChanged event\r
182         /// <summary>The event subscribers, null of no subscribers</summary>\r
183         private EventHandler<ClientChangedEventArgs> m_ClientChanged;\r
184 \r
185         ///<summary>Raises the ClientChanged Event</summary>\r
186         /// <param name="e">A ClientChangedEventArgs object containing\r
187         /// the old and the new client</param>\r
188         protected virtual void OnClientChanged(ClientChangedEventArgs e)\r
189         {\r
190             EventHandler<ClientChangedEventArgs> handler = m_ClientChanged;\r
191             if (handler != null)\r
192                 handler(this, e);\r
193         }\r
194 \r
195         /// <summary>Thread sync lock object</summary>\r
196         private readonly object m_ClientChangedLock = new object();\r
197 \r
198         /// <summary>Raised when the GridClient object in the main Radegast instance is changed</summary>\r
199         public event EventHandler<ClientChangedEventArgs> ClientChanged\r
200         {\r
201             add { lock (m_ClientChangedLock) { m_ClientChanged += value; } }\r
202             remove { lock (m_ClientChangedLock) { m_ClientChanged -= value; } }\r
203         }\r
204         #endregion ClientChanged event\r
205 \r
206         #region InventoryClipboardUpdated event\r
207         /// <summary>The event subscribers, null of no subscribers</summary>\r
208         private EventHandler<EventArgs> m_InventoryClipboardUpdated;\r
209 \r
210         ///<summary>Raises the InventoryClipboardUpdated Event</summary>\r
211         /// <param name="e">A EventArgs object containing\r
212         /// the old and the new client</param>\r
213         protected virtual void OnInventoryClipboardUpdated(EventArgs e)\r
214         {\r
215             EventHandler<EventArgs> handler = m_InventoryClipboardUpdated;\r
216             if (handler != null)\r
217                 handler(this, e);\r
218         }\r
219 \r
220         /// <summary>Thread sync lock object</summary>\r
221         private readonly object m_InventoryClipboardUpdatedLock = new object();\r
222 \r
223         /// <summary>Raised when the GridClient object in the main Radegast instance is changed</summary>\r
224         public event EventHandler<EventArgs> InventoryClipboardUpdated\r
225         {\r
226             add { lock (m_InventoryClipboardUpdatedLock) { m_InventoryClipboardUpdated += value; } }\r
227             remove { lock (m_InventoryClipboardUpdatedLock) { m_InventoryClipboardUpdated -= value; } }\r
228         }\r
229         #endregion InventoryClipboardUpdated event\r
230 \r
231 \r
232         #endregion Events\r
233 \r
234         public RadegastInstance(GridClient client0)\r
235         {\r
236             // incase something else calls GlobalInstance while we are loading\r
237             globalInstance = this;\r
238 \r
239 #if !DEBUG\r
240             Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);\r
241             Application.ThreadException += HandleThreadException;\r
242 #endif\r
243 \r
244             client = client0;\r
245 \r
246             // Are we running mono?\r
247             monoRuntime = Type.GetType("Mono.Runtime") != null;\r
248 \r
249             netcom = new RadegastNetcom(this);\r
250             state = new StateManager(this);\r
251             mediaManager = new MediaManager(this);\r
252             commandsManager = new CommandsManager(this);\r
253             ContextActionManager = new ContextActionsManager(this);\r
254             movement = new RadegastMovement(this);\r
255 \r
256             InitializeLoggingAndConfig();\r
257             InitializeClient(client);\r
258 \r
259             rlv = new RLVManager(this);\r
260 \r
261             mainForm = new frmMain(this);\r
262             mainForm.InitializeControls();\r
263 \r
264             mainForm.Load += new EventHandler(mainForm_Load);\r
265             ScanAndLoadPlugins();\r
266         }\r
267 \r
268         private void InitializeClient(GridClient client)\r
269         {\r
270             client.Settings.MULTIPLE_SIMS = true;\r
271 \r
272             client.Settings.USE_INTERPOLATION_TIMER = false;\r
273             client.Settings.ALWAYS_REQUEST_OBJECTS = true;\r
274             client.Settings.ALWAYS_DECODE_OBJECTS = true;\r
275             client.Settings.OBJECT_TRACKING = true;\r
276             client.Settings.ENABLE_SIMSTATS = true;\r
277             client.Settings.FETCH_MISSING_INVENTORY = true;\r
278             client.Settings.SEND_AGENT_THROTTLE = true;\r
279             client.Settings.SEND_AGENT_UPDATES = true;\r
280 \r
281             client.Settings.USE_ASSET_CACHE = true;\r
282             client.Settings.ASSET_CACHE_DIR = Path.Combine(userDir, "cache");\r
283             client.Assets.Cache.AutoPruneEnabled = false;\r
284 \r
285             client.Throttle.Total = 5000000f;\r
286             client.Settings.THROTTLE_OUTGOING_PACKETS = true;\r
287             client.Settings.LOGIN_TIMEOUT = 120 * 1000;\r
288             client.Settings.SIMULATOR_TIMEOUT = 120 * 1000;\r
289             client.Settings.MAX_CONCURRENT_TEXTURE_DOWNLOADS = 20;\r
290 \r
291             RegisterClientEvents(client);\r
292         }\r
293 \r
294         private void RegisterClientEvents(GridClient client)\r
295         {\r
296             client.Groups.CurrentGroups += new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);\r
297             client.Groups.GroupLeaveReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);\r
298             client.Groups.GroupDropped += new EventHandler<GroupDroppedEventArgs>(Groups_GroupsChanged);\r
299             client.Groups.GroupJoinedReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);\r
300             client.Avatars.UUIDNameReply += new EventHandler<UUIDNameReplyEventArgs>(Avatars_UUIDNameReply);\r
301             netcom.ClientConnected += new EventHandler<EventArgs>(netcom_ClientConnected);\r
302        }\r
303 \r
304         private void UnregisterClientEvents(GridClient client)\r
305         {\r
306             client.Groups.CurrentGroups -= new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);\r
307             client.Groups.GroupLeaveReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);\r
308             client.Groups.GroupDropped -= new EventHandler<GroupDroppedEventArgs>(Groups_GroupsChanged);\r
309             client.Groups.GroupJoinedReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupsChanged);\r
310             client.Avatars.UUIDNameReply -= new EventHandler<UUIDNameReplyEventArgs>(Avatars_UUIDNameReply);\r
311             netcom.ClientConnected -= new EventHandler<EventArgs>(netcom_ClientConnected);\r
312         }\r
313 \r
314         public void Reconnect()\r
315         {\r
316             TabConsole.DisplayNotificationInChat("Attempting to reconnect...", ChatBufferTextStyle.StatusDarkBlue);\r
317             Logger.Log("Attemting to reconnect", Helpers.LogLevel.Info, client);\r
318             GridClient oldClient = client;\r
319             client = new GridClient();\r
320             UnregisterClientEvents(oldClient);\r
321             InitializeClient(client);\r
322             OnClientChanged(new ClientChangedEventArgs(oldClient, client));\r
323             netcom.Login();\r
324         }\r
325 \r
326         public void CleanUp()\r
327         {\r
328             if (rlv != null)\r
329             {\r
330                 rlv.Dispose();\r
331                 rlv = null;\r
332             }\r
333 \r
334             if (client != null)\r
335             {\r
336                 UnregisterClientEvents(client);\r
337             }\r
338 \r
339             lock (PluginsLoaded)\r
340             {\r
341                 List<IRadegastPlugin> unload = new List<IRadegastPlugin>(PluginsLoaded);\r
342                 unload.ForEach(plug =>\r
343                {\r
344                    PluginsLoaded.Remove(plug);\r
345                    try\r
346                    {\r
347                        plug.StopPlugin(this);\r
348                    }\r
349                    catch (Exception ex)\r
350                    {\r
351                        Logger.Log("ERROR in Shutdown Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug, ex);\r
352                    }\r
353                });\r
354             }\r
355 \r
356             if (movement != null)\r
357             {\r
358                 movement.Dispose();\r
359                 movement = null;\r
360             }\r
361             if (commandsManager != null)\r
362             {\r
363                 commandsManager.Dispose();\r
364                 commandsManager = null;\r
365             }\r
366             if (ContextActionManager != null)\r
367             {\r
368                 ContextActionManager.Dispose();\r
369                 ContextActionManager = null;\r
370             }\r
371             if (mediaManager != null)\r
372             {\r
373                 mediaManager.Dispose();\r
374                 mediaManager = null;\r
375             }\r
376             if (state != null)\r
377             {\r
378                 state.Dispose();\r
379                 state = null;\r
380             }\r
381             if (netcom != null)\r
382             {\r
383                 netcom.Dispose();\r
384                 netcom = null;\r
385             }\r
386             if (mainForm != null)\r
387             {\r
388                 mainForm.Load -= new EventHandler(mainForm_Load);\r
389             }\r
390             Logger.Log("RadegastInstance finished cleaning up.", Helpers.LogLevel.Debug);\r
391         }\r
392 \r
393         void mainForm_Load(object sender, EventArgs e)\r
394         {\r
395             StartPlugins();\r
396         }\r
397 \r
398         private void StartPlugins()\r
399         {\r
400             lock (PluginsLoaded)\r
401             {\r
402                 foreach (IRadegastPlugin plug in PluginsLoaded)\r
403                 {\r
404                     try\r
405                     {\r
406                         plug.StartPlugin(this);\r
407                     }\r
408                     catch (Exception ex)\r
409                     {\r
410                         Logger.Log("ERROR in Starting Radegast Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug);\r
411                     }\r
412                 }\r
413             }\r
414         }\r
415 \r
416         private void ScanAndLoadPlugins()\r
417         {\r
418             string dirName = Application.StartupPath;\r
419 \r
420             if (!Directory.Exists(dirName)) return;\r
421 \r
422             foreach (string loadfilename in Directory.GetFiles(dirName))\r
423             {\r
424                 if (loadfilename.ToLower().EndsWith(".dll") || loadfilename.ToLower().EndsWith(".exe"))\r
425                 {\r
426                     try\r
427                     {\r
428                         Assembly assembly = Assembly.LoadFile(loadfilename);\r
429                         LoadAssembly(loadfilename, assembly);\r
430                     }\r
431                     catch (BadImageFormatException)\r
432                     {\r
433                         // non .NET .dlls\r
434                     }\r
435                     catch (ReflectionTypeLoadException)\r
436                     {\r
437                         // Out of date or dlls missing sub dependencies\r
438                     }\r
439                     catch (TypeLoadException)\r
440                     {\r
441                         // Another version of: Out of date or dlls missing sub dependencies\r
442                     }\r
443                     catch (Exception ex)\r
444                     {\r
445                         Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " because " + ex, Helpers.LogLevel.Debug);\r
446                     }\r
447                 }\r
448             }\r
449         }\r
450 \r
451         public void LoadAssembly(string loadfilename, Assembly assembly)\r
452         {\r
453             foreach (Type type in assembly.GetTypes())\r
454             {\r
455                 if (typeof(IRadegastPlugin).IsAssignableFrom(type))\r
456                 {\r
457                     if  (type.IsInterface) continue;\r
458                     try\r
459                     {\r
460                         IRadegastPlugin plug;\r
461                         ConstructorInfo constructorInfo = type.GetConstructor(new Type[] {typeof (RadegastInstance)});\r
462                         if (constructorInfo != null)\r
463                             plug = (IRadegastPlugin) constructorInfo.Invoke(new[] {this});\r
464                         else\r
465                         {\r
466                             constructorInfo = type.GetConstructor(new Type[] {});\r
467                             if (constructorInfo != null)\r
468                                 plug = (IRadegastPlugin) constructorInfo.Invoke(new object[0]);\r
469                             else\r
470                             {\r
471                                 Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because "+type+ " has no usable constructor.",Helpers.LogLevel.Debug);\r
472                                 continue;\r
473                             }\r
474                         }\r
475                         lock (PluginsLoaded) PluginsLoaded.Add(plug);\r
476                     }\r
477                     catch (Exception ex)\r
478                     {\r
479                         Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + ex,\r
480                                    Helpers.LogLevel.Debug);\r
481                     }\r
482                 }\r
483                 else\r
484                 {\r
485                     try\r
486                     {\r
487                         commandsManager.LoadType(type);\r
488                     }\r
489                     catch (Exception ex)\r
490                     {\r
491                         Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " Command: " + type +\r
492                                    " because " + ex.Message + " " + ex.StackTrace, Helpers.LogLevel.Debug);\r
493                     }\r
494                 }\r
495             }\r
496         }\r
497 \r
498         void netcom_ClientConnected(object sender, EventArgs e)\r
499         {\r
500             try\r
501             {\r
502                 if (!Directory.Exists(ClientDir))\r
503                     Directory.CreateDirectory(ClientDir);\r
504             }\r
505             catch (Exception ex)\r
506             {\r
507                 Logger.Log("Failed to create client directory", Helpers.LogLevel.Warning, ex);\r
508             }\r
509 \r
510             clientSettings = new Settings(Path.Combine(ClientDir, "client_settings.xml"));\r
511         }\r
512 \r
513 \r
514         void Avatars_UUIDNameReply(object sender, UUIDNameReplyEventArgs e)\r
515         {\r
516             lock (nameCache)\r
517             {\r
518                 foreach (KeyValuePair<UUID, string> av in e.Names)\r
519                 {\r
520                     if (!nameCache.ContainsKey(av.Key))\r
521                     {\r
522                         nameCache.Add(av.Key, av.Value);\r
523                     }\r
524                 }\r
525             }\r
526         }\r
527 \r
528         public string getAvatarName(UUID key)\r
529         {\r
530             lock (nameCache)\r
531             {\r
532                 if (key == UUID.Zero)\r
533                 {\r
534                     return "(???) (???)";\r
535                 }\r
536                 if (nameCache.ContainsKey(key))\r
537                 {\r
538                     return nameCache[key];\r
539                 }\r
540                 else\r
541                 {\r
542                     client.Avatars.RequestAvatarName(key);\r
543                     return INCOMPLETE_NAME;\r
544                 }\r
545             }\r
546         }\r
547 \r
548         public void getAvatarNames(List<UUID> keys)\r
549         {\r
550             lock (nameCache)\r
551             {\r
552                 List<UUID> newNames = new List<UUID>();\r
553                 foreach (UUID key in keys)\r
554                 {\r
555                     if (!nameCache.ContainsKey(key))\r
556                     {\r
557                         newNames.Add(key);\r
558                     }\r
559                 }\r
560                 if (newNames.Count > 0)\r
561                 {\r
562                     client.Avatars.RequestAvatarNames(newNames);\r
563                 }\r
564             }\r
565         }\r
566 \r
567         public bool haveAvatarName(UUID key)\r
568         {\r
569             lock (nameCache)\r
570             {\r
571                 if (nameCache.ContainsKey(key))\r
572                     return true;\r
573                 else\r
574                     return false;\r
575             }\r
576         }\r
577 \r
578         void Groups_GroupsChanged(object sender, EventArgs e)\r
579         {\r
580             client.Groups.RequestCurrentGroups();\r
581         }\r
582 \r
583         public static string SafeFileName(string fileName)\r
584         {\r
585             foreach (char lDisallowed in Path.GetInvalidFileNameChars())\r
586             {\r
587                 fileName = fileName.Replace(lDisallowed.ToString(), "_");\r
588             }\r
589 \r
590             return fileName;\r
591         }\r
592 \r
593         public void LogClientMessage(string fileName, string message)\r
594         {\r
595             lock (this)\r
596             {\r
597                 try\r
598                 {\r
599                     foreach (char lDisallowed in System.IO.Path.GetInvalidFileNameChars())\r
600                     {\r
601                         fileName = fileName.Replace(lDisallowed.ToString(), "_");\r
602                     }\r
603 \r
604                     File.AppendAllText(Path.Combine(ClientDir, fileName),\r
605                         DateTime.Now.ToString("yyyy-MM-dd [HH:mm:ss] ") + message + Environment.NewLine);\r
606                 }\r
607                 catch (Exception) { }\r
608             }\r
609         }\r
610 \r
611         void Groups_CurrentGroups(object sender, CurrentGroupsEventArgs e)\r
612         {\r
613             this.groups = e.Groups;\r
614         }\r
615 \r
616         private void InitializeLoggingAndConfig()\r
617         {\r
618             try\r
619             {\r
620                 userDir = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), Properties.Resources.ProgramName);\r
621                 if (!Directory.Exists(userDir))\r
622                 {\r
623                     Directory.CreateDirectory(userDir);\r
624                 }\r
625             }\r
626             catch (Exception)\r
627             {\r
628                 userDir = System.Environment.CurrentDirectory;\r
629             };\r
630 \r
631             globalLogFile = Path.Combine(userDir, Properties.Resources.ProgramName + ".log");\r
632             globalSettings = new Settings(Path.Combine(userDir, "settings.xml"));\r
633         }\r
634 \r
635         public GridClient Client\r
636         {\r
637             get { return client; }\r
638         }\r
639 \r
640         public RadegastNetcom Netcom\r
641         {\r
642             get { return netcom; }\r
643         }\r
644 \r
645         public StateManager State\r
646         {\r
647             get { return state; }\r
648         }\r
649 \r
650         public frmMain MainForm\r
651         {\r
652             get { return mainForm; }\r
653         }\r
654 \r
655         public TabsConsole TabConsole\r
656         {\r
657             get { return mainForm.TabConsole; }\r
658         }\r
659 \r
660         public void HandleThreadException(object sender, ThreadExceptionEventArgs e)\r
661         {\r
662             Logger.Log("Unhandled Thread Exception: " \r
663                 + e.Exception.Message + Environment.NewLine\r
664                 + e.Exception.StackTrace + Environment.NewLine,\r
665                 Helpers.LogLevel.Error,\r
666                 client);\r
667 #if DEBUG\r
668             Application.Exit();\r
669 #endif\r
670         }\r
671     }\r
672 \r
673     #region Event classes\r
674     public class ClientChangedEventArgs : EventArgs\r
675     {\r
676         private GridClient m_OldClient;\r
677         private GridClient m_Client;\r
678 \r
679         public GridClient OldClient { get { return m_OldClient; } }\r
680         public GridClient Client { get { return m_Client; } }\r
681 \r
682         public ClientChangedEventArgs(GridClient OldClient, GridClient Client)\r
683         {\r
684             m_OldClient = OldClient;\r
685             m_Client = Client;\r
686         }\r
687     }\r
688     #endregion Event classes\r
689 }\r