OSDN Git Service

Implement avatar name caching
[radegast/radegast.git] / Radegast / GUI / Dialogs / MainForm.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2012, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id$
30 //
31 using System;
32 using System.Collections.Generic;
33 using System.Drawing;
34 using System.Text;
35 using System.Text.RegularExpressions;
36 using System.Timers;
37 using System.Threading;
38 using System.Windows.Forms;
39 using System.Resources;
40 using System.IO;
41 using System.Web;
42 using Radegast.Netcom;
43 using OpenMetaverse;
44 using OpenMetaverse.StructuredData;
45 using OpenMetaverse.Assets;
46
47 namespace Radegast
48 {
49     public partial class frmMain : RadegastForm
50     {
51         #region Public members
52         public static ImageList ResourceImages = new ImageList();
53         public static List<string> ImageNames = new List<string>();
54         public bool PreventParcelUpdate = false;
55         public delegate void ProfileHandlerDelegate(string agentName, UUID agentID);
56         public ProfileHandlerDelegate ShowAgentProfile;
57
58         public TabsConsole TabConsole
59         {
60             get { return tabsConsole; }
61         }
62
63         public MapConsole WorldMap
64         {
65             get
66             {
67                 if (MapTab != null)
68                 {
69                     return (MapConsole)MapTab.Control;
70                 }
71                 return null;
72             }
73         }
74
75         public RadegastTab MapTab
76         {
77             get
78             {
79                 if (tabsConsole.TabExists("map"))
80                 {
81                     return tabsConsole.Tabs["map"];
82                 }
83                 else
84                 {
85                     return null;
86                 }
87             }
88         }
89
90         public MediaConsole MediaConsole { get { return mediaConsole; } }
91
92         /// <summary>
93         /// Drop down that contains the tools menu
94         /// </summary>
95         public ToolStripDropDownButton ToolsMenu
96         {
97             get { return tbnTools; }
98         }
99
100         /// <summary>
101         /// Dropdown that contains the heelp menu
102         /// </summary>
103         public ToolStripDropDownButton HelpMenu
104         {
105             get { return tbtnHelp; }
106         }
107
108         /// <summary>
109         /// Drop down that contants the plugins menu. Make sure to set it Visible if
110         /// you add items to this menu, it's hidden by default
111         /// </summary>
112         public ToolStripDropDownButton PluginsMenu
113         {
114             get { return tbnPlugins; }
115         }
116
117         #endregion
118
119         #region Private members
120         private RadegastInstance instance;
121         private GridClient client { get { return instance.Client; } }
122         private RadegastNetcom netcom { get { return instance.Netcom; } }
123         private TabsConsole tabsConsole;
124         private System.Timers.Timer statusTimer;
125         private AutoPilot ap;
126         private bool AutoPilotActive = false;
127         private TransparentButton btnDialogNextControl;
128         private MediaConsole mediaConsole;
129         #endregion
130
131         #region Constructor and disposal
132         public frmMain(RadegastInstance instance)
133             : base(instance)
134         {
135             InitializeComponent();
136             Disposed += new EventHandler(frmMain_Disposed);
137
138             this.instance = instance;
139             this.instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
140             netcom.NetcomSync = this;
141             ShowAgentProfile = ShowAgentProfileInternal;
142
143             pnlDialog.Visible = false;
144             btnDialogNextControl = new TransparentButton();
145             pnlDialog.Controls.Add(btnDialogNextControl);
146             pnlDialog.Top = 0;
147
148             btnDialogNextControl.Size = new Size(35, 20);
149             btnDialogNextControl.BackColor = Color.Transparent;
150             btnDialogNextControl.ForeColor = Color.Gold;
151             btnDialogNextControl.FlatAppearance.BorderSize = 0;
152             btnDialogNextControl.FlatStyle = FlatStyle.Flat;
153             btnDialogNextControl.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
154             btnDialogNextControl.Text = ">>";
155             btnDialogNextControl.Font = new Font(btnDialogNextControl.Font, FontStyle.Bold);
156             btnDialogNextControl.Margin = new Padding(0);
157             btnDialogNextControl.Padding = new Padding(0);
158             btnDialogNextControl.UseVisualStyleBackColor = false;
159             btnDialogNextControl.Top = btnDialogNextControl.Parent.ClientSize.Height - btnDialogNextControl.Size.Height;
160             btnDialogNextControl.Left = btnDialogNextControl.Parent.ClientSize.Width - btnDialogNextControl.Size.Width;
161             btnDialogNextControl.Click += new EventHandler(btnDialogNextControl_Click);
162
163             if (instance.MonoRuntime)
164             {
165                 statusStrip1.LayoutStyle = ToolStripLayoutStyle.Table;
166             }
167
168             // Callbacks
169             netcom.ClientLoginStatus += new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
170             netcom.ClientLoggedOut += new EventHandler(netcom_ClientLoggedOut);
171             netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
172             instance.Names.NameUpdated += new EventHandler<UUIDNameReplyEventArgs>(Names_NameUpdated);
173             RegisterClientEvents(client);
174
175             InitializeStatusTimer();
176             RefreshWindowTitle();
177         }
178
179         private void RegisterClientEvents(GridClient client)
180         {
181             client.Parcels.ParcelProperties += new EventHandler<ParcelPropertiesEventArgs>(Parcels_ParcelProperties);
182             client.Self.MoneyBalanceReply += new EventHandler<MoneyBalanceReplyEventArgs>(Self_MoneyBalanceReply);
183             client.Self.MoneyBalance += new EventHandler<BalanceEventArgs>(Self_MoneyBalance);
184         }
185
186         private void UnregisterClientEvents(GridClient client)
187         {
188             client.Parcels.ParcelProperties -= new EventHandler<ParcelPropertiesEventArgs>(Parcels_ParcelProperties);
189             client.Self.MoneyBalanceReply -= new EventHandler<MoneyBalanceReplyEventArgs>(Self_MoneyBalanceReply);
190             client.Self.MoneyBalance -= new EventHandler<BalanceEventArgs>(Self_MoneyBalance);
191         }
192
193         void instance_ClientChanged(object sender, ClientChangedEventArgs e)
194         {
195             UnregisterClientEvents(e.OldClient);
196             RegisterClientEvents(client);
197         }
198
199         void frmMain_Disposed(object sender, EventArgs e)
200         {
201             if (netcom != null)
202             {
203                 netcom.NetcomSync = null;
204                 netcom.ClientLoginStatus -= new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
205                 netcom.ClientLoggedOut -= new EventHandler(netcom_ClientLoggedOut);
206                 netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
207             }
208
209             if (client != null)
210             {
211                 UnregisterClientEvents(client);
212             }
213
214             if (instance != null && instance.Names != null)
215             {
216                 instance.Names.NameUpdated -= new EventHandler<UUIDNameReplyEventArgs>(Names_NameUpdated);
217             }
218
219             this.instance.CleanUp();
220         }
221         #endregion
222
223         #region Event handlers
224         bool firstMoneyNotification = true;
225         void Self_MoneyBalance(object sender, BalanceEventArgs e)
226         {
227             int oldBalance = 0;
228             int.TryParse(tlblMoneyBalance.Text, out oldBalance);
229             int delta = Math.Abs(oldBalance - e.Balance);
230
231             if (firstMoneyNotification)
232             {
233                 firstMoneyNotification = false;
234             }
235             else
236             {
237                 if (delta > 50)
238                 {
239                     if (oldBalance > e.Balance)
240                     {
241                         instance.MediaManager.PlayUISound(UISounds.MoneyIn);
242                     }
243                     else
244                     {
245                         instance.MediaManager.PlayUISound(UISounds.MoneyOut);
246                     }
247                 }
248             }
249         }
250
251         void Names_NameUpdated(object sender, UUIDNameReplyEventArgs e)
252         {
253             if (!e.Names.ContainsKey(client.Self.AgentID)) return;
254
255             if (InvokeRequired)
256             {
257                 if (IsHandleCreated || !instance.MonoRuntime)
258                 {
259                     BeginInvoke(new MethodInvoker(() => Names_NameUpdated(sender, e)));
260                 }
261                 return;
262             }
263
264             RefreshWindowTitle();
265             RefreshStatusBar();
266         }
267
268         void Self_MoneyBalanceReply(object sender, MoneyBalanceReplyEventArgs e)
269         {
270             if (!String.IsNullOrEmpty(e.Description))
271             {
272                 if (instance.GlobalSettings["transaction_notification_dialog"].AsBoolean())
273                     AddNotification(new ntfGeneric(instance, e.Description));
274                 if (instance.GlobalSettings["transaction_notification_chat"].AsBoolean())
275                     TabConsole.DisplayNotificationInChat(e.Description);
276             }
277         }
278
279         public void InitializeControls()
280         {
281             InitializeTabsConsole();
282
283             if (instance.MediaManager.SoundSystemAvailable)
284             {
285                 mediaConsole = new MediaConsole(instance);
286                 tbtnMedia.Visible = true;
287             }
288         }
289
290         public bool InAutoReconnect { get; set; }
291
292         private void DisplayAutoReconnectForm()
293         {
294             if (IsDisposed) return;
295
296             if (InvokeRequired)
297             {
298                 BeginInvoke(new MethodInvoker(DisplayAutoReconnectForm));
299                 return;
300             }
301
302             InAutoReconnect = true;
303             frmReconnect dialog = new frmReconnect(instance, instance.GlobalSettings["reconnect_time"]);
304             dialog.ShowDialog(this);
305             dialog.Dispose();
306             dialog = null;
307         }
308
309         public void BeginAutoReconnect()
310         {
311             // Sleep for 3 seconds on a separate thread while things unwind on
312             // disconnect, since ShowDialog() blocks GUI thread
313             (new Thread(new ThreadStart(() =>
314                 {
315                     System.Threading.Thread.Sleep(3000);
316                     DisplayAutoReconnectForm();
317                 }
318                 ))
319                 {
320                     Name = "Reconnect Delay Thread",
321                     IsBackground = true
322                 }
323             ).Start();
324         }
325
326         private void netcom_ClientLoginStatus(object sender, LoginProgressEventArgs e)
327         {
328             if (e.Status == LoginStatus.Failed)
329             {
330                 if (InAutoReconnect)
331                 {
332                     if (instance.GlobalSettings["auto_reconnect"].AsBoolean())
333                         BeginAutoReconnect();
334                     else
335                         InAutoReconnect = false;
336                 }
337             }
338             else if (e.Status == LoginStatus.Success)
339             {
340                 InAutoReconnect = false;
341                 reconnectToolStripMenuItem.Enabled = false;
342                 loginToolStripMenuItem.Enabled = false;
343                 tsb3D.Enabled = tbtnVoice.Enabled = disconnectToolStripMenuItem.Enabled =
344                 tbtnGroups.Enabled = tbnObjects.Enabled = tbtnWorld.Enabled = tbnTools.Enabled = tmnuImport.Enabled =
345                     tbtnFriends.Enabled = tbtnInventory.Enabled = tbtnSearch.Enabled = tbtnMap.Enabled = true;
346
347                 statusTimer.Start();
348                 RefreshWindowTitle();
349             }
350         }
351
352         private void netcom_ClientLoggedOut(object sender, EventArgs e)
353         {
354             tsb3D.Enabled = tbtnVoice.Enabled = disconnectToolStripMenuItem.Enabled =
355             tbtnGroups.Enabled = tbnObjects.Enabled = tbtnWorld.Enabled = tbnTools.Enabled = tmnuImport.Enabled =
356                 tbtnFriends.Enabled = tbtnInventory.Enabled = tbtnSearch.Enabled = tbtnMap.Enabled = false;
357
358             reconnectToolStripMenuItem.Enabled = true;
359             loginToolStripMenuItem.Enabled = true;
360             InAutoReconnect = false;
361
362             if (statusTimer != null)
363                 statusTimer.Stop();
364
365             RefreshStatusBar();
366             RefreshWindowTitle();
367         }
368
369         private void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
370         {
371             firstMoneyNotification = true;
372
373             if (e.Reason == NetworkManager.DisconnectType.ClientInitiated) return;
374             netcom_ClientLoggedOut(sender, EventArgs.Empty);
375
376             if (instance.GlobalSettings["auto_reconnect"].AsBoolean())
377             {
378                 BeginAutoReconnect();
379             }
380         }
381
382         private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
383         {
384             if (statusTimer != null)
385             {
386                 statusTimer.Stop();
387                 statusTimer.Dispose();
388                 statusTimer = null;
389             }
390
391             if (mediaConsole != null)
392             {
393                 if (tabsConsole.TabExists("media"))
394                 {
395                     tabsConsole.Tabs["media"].AllowClose = true;
396                     tabsConsole.Tabs["media"].Close();
397                 }
398                 else
399                 {
400                     mediaConsole.Dispose();
401                 }
402                 mediaConsole = null;
403             }
404
405             if (netcom.IsLoggedIn)
406             {
407                 Thread saveInvToDisk = new Thread(new ThreadStart(
408                     delegate()
409                     {
410                         client.Inventory.Store.SaveToDisk(instance.InventoryCacheFileName);
411                     }));
412                 saveInvToDisk.Name = "Save inventory to disk";
413                 saveInvToDisk.Start();
414
415                 netcom.Logout();
416             }
417         }
418         #endregion
419
420         # region Update status
421
422         void Parcels_ParcelProperties(object sender, ParcelPropertiesEventArgs e)
423         {
424             if (PreventParcelUpdate || e.Result != ParcelResult.Single) return;
425             if (InvokeRequired)
426             {
427                 BeginInvoke(new MethodInvoker(() => Parcels_ParcelProperties(sender, e)));
428                 return;
429             }
430
431             Parcel parcel = instance.State.Parcel = e.Parcel;
432
433             tlblParcel.Text = parcel.Name;
434             tlblParcel.ToolTipText = parcel.Desc;
435
436             if ((parcel.Flags & ParcelFlags.AllowFly) != ParcelFlags.AllowFly)
437                 icoNoFly.Visible = true;
438             else
439                 icoNoFly.Visible = false;
440
441             if ((parcel.Flags & ParcelFlags.CreateObjects) != ParcelFlags.CreateObjects)
442                 icoNoBuild.Visible = true;
443             else
444                 icoNoBuild.Visible = false;
445
446             if ((parcel.Flags & ParcelFlags.AllowOtherScripts) != ParcelFlags.AllowOtherScripts)
447                 icoNoScript.Visible = true;
448             else
449                 icoNoScript.Visible = false;
450
451             if ((parcel.Flags & ParcelFlags.RestrictPushObject) == ParcelFlags.RestrictPushObject)
452                 icoNoPush.Visible = true;
453             else
454                 icoNoPush.Visible = false;
455
456             if ((parcel.Flags & ParcelFlags.AllowDamage) == ParcelFlags.AllowDamage)
457                 icoHealth.Visible = true;
458             else
459                 icoHealth.Visible = false;
460
461             if ((parcel.Flags & ParcelFlags.AllowVoiceChat) != ParcelFlags.AllowVoiceChat)
462                 icoNoVoice.Visible = true;
463             else
464                 icoNoVoice.Visible = false;
465         }
466
467         private void RefreshStatusBar()
468         {
469             if (netcom.IsLoggedIn)
470             {
471                 tlblLoginName.Text = instance.Names.Get(client.Self.AgentID, client.Self.Name);
472                 tlblMoneyBalance.Text = client.Self.Balance.ToString();
473                 icoHealth.Text = client.Self.Health.ToString() + "%";
474
475                 var cs = client.Network.CurrentSim;
476                 tlblRegionInfo.Text =
477                     (cs == null ? "No region" : cs.Name) +
478                     " (" + Math.Floor(client.Self.SimPosition.X).ToString() + ", " +
479                     Math.Floor(client.Self.SimPosition.Y).ToString() + ", " +
480                     Math.Floor(client.Self.SimPosition.Z).ToString() + ")";
481             }
482             else
483             {
484                 tlblLoginName.Text = "Offline";
485                 tlblMoneyBalance.Text = "0";
486                 icoHealth.Text = "0%";
487                 tlblRegionInfo.Text = "No Region";
488                 tlblParcel.Text = "No Parcel";
489
490                 icoHealth.Visible = false;
491                 icoNoBuild.Visible = false;
492                 icoNoFly.Visible = false;
493                 icoNoPush.Visible = false;
494                 icoNoScript.Visible = false;
495                 icoNoVoice.Visible = false;
496             }
497         }
498
499         private void RefreshWindowTitle()
500         {
501             string name = instance.Names.Get(client.Self.AgentID, client.Self.Name);
502             StringBuilder sb = new StringBuilder();
503             sb.Append("Radegast - ");
504
505             if (netcom.IsLoggedIn)
506             {
507                 sb.Append("[" + name + "]");
508
509                 if (instance.State.IsAway)
510                 {
511                     sb.Append(" - Away");
512                     if (instance.State.IsBusy) sb.Append(", Busy");
513                 }
514                 else if (instance.State.IsBusy)
515                 {
516                     sb.Append(" - Busy");
517                 }
518
519                 if (instance.State.IsFollowing)
520                 {
521                     sb.Append(" - Following ");
522                     sb.Append(instance.State.FollowName);
523                 }
524             }
525             else
526             {
527                 sb.Append("Logged Out");
528             }
529
530             this.Text = sb.ToString();
531
532             // When minimized to tray, update tray tool tip also
533             if (WindowState == FormWindowState.Minimized && instance.GlobalSettings["minimize_to_tray"])
534             {
535                 trayIcon.Text = sb.ToString();
536                 ctxTrayMenuLabel.Text = sb.ToString();
537             }
538
539             sb = null;
540         }
541
542         private void InitializeStatusTimer()
543         {
544             statusTimer = new System.Timers.Timer(250);
545             statusTimer.SynchronizingObject = this;
546             statusTimer.Elapsed += new ElapsedEventHandler(statusTimer_Elapsed);
547         }
548
549         private void statusTimer_Elapsed(object sender, ElapsedEventArgs e)
550         {
551             // Mono sometimes fires timer after is's disposed
552             try
553             {
554                 RefreshWindowTitle();
555                 RefreshStatusBar();
556             }
557             catch { }
558         }
559         #endregion
560
561         #region Initialization, configuration, and key shortcuts
562         private void InitializeTabsConsole()
563         {
564             tabsConsole = new TabsConsole(instance);
565             tabsConsole.Dock = DockStyle.Fill;
566             toolStripContainer1.ContentPanel.Controls.Add(tabsConsole);
567         }
568
569         private void frmMain_KeyDown(object sender, KeyEventArgs e)
570         {
571             // Ctrl-Alt-Shift-H Say "Hippos!" in chat
572             if (e.Modifiers == (Keys.Control | Keys.Shift | Keys.Alt) && e.KeyCode == Keys.H)
573             {
574                 e.Handled = e.SuppressKeyPress = true;
575                 netcom.ChatOut("Hippos!", ChatType.Normal, 0);
576                 return;
577             }
578
579             // Ctrl-Shift-1 (sim/parcel info)
580             if (e.Modifiers == (Keys.Control | Keys.Shift) && e.KeyCode == Keys.D1)
581             {
582                 e.Handled = e.SuppressKeyPress = true;
583                 DisplayRegionParcelConsole();
584                 return;
585             }
586
587             // Ctrl-W: Close tab
588             if (e.Modifiers == Keys.Control && e.KeyCode == Keys.W)
589             {
590                 e.Handled = e.SuppressKeyPress = true;
591                 RadegastTab tab = tabsConsole.SelectedTab;
592
593                 if (tab.AllowClose)
594                 {
595                     tab.Close();
596                 }
597                 else if (tab.AllowHide)
598                 {
599                     tab.Hide();
600                 }
601
602                 return;
603             }
604
605             // Ctl-Shift-H: Teleport Home
606             if (e.Modifiers == (Keys.Control | Keys.Shift) && e.KeyCode == Keys.H)
607             {
608                 e.Handled = e.SuppressKeyPress = true;
609                 tmnuTeleportHome.PerformClick();
610                 return;
611             }
612
613             // Alt-Ctrl-D Open debug console
614             if (e.Modifiers == (Keys.Control | Keys.Alt) && e.KeyCode == Keys.D)
615             {
616                 e.Handled = e.SuppressKeyPress = true;
617                 debugConsoleToolStripMenuItem.PerformClick();
618                 return;
619             }
620
621             // Alt 1-8: Toggle various tabs
622             if (e.Modifiers == Keys.Alt)
623             {
624                 switch (e.KeyCode)
625                 {
626                     case Keys.D1:
627                         e.Handled = e.SuppressKeyPress = true;
628                         tabsConsole.Tabs["chat"].Select();
629                         return;
630
631                     case Keys.D2:
632                         e.Handled = e.SuppressKeyPress = true;
633                         tbtnFriends.PerformClick();
634                         return;
635
636                     case Keys.D3:
637                         e.Handled = e.SuppressKeyPress = true;
638                         tbtnGroups.PerformClick();
639                         return;
640
641                     case Keys.D4:
642                         e.Handled = e.SuppressKeyPress = true;
643                         tbtnInventory.PerformClick();
644                         return;
645
646                     case Keys.D5:
647                         e.Handled = e.SuppressKeyPress = true;
648                         tbtnSearch.PerformClick();
649                         return;
650
651                     case Keys.D6:
652                         e.Handled = e.SuppressKeyPress = true;
653                         tbtnMap.PerformClick();
654                         return;
655
656                     case Keys.D7:
657                         e.Handled = e.SuppressKeyPress = true;
658                         tbnObjects.PerformClick();
659                         return;
660
661                     case Keys.D8:
662                         e.Handled = e.SuppressKeyPress = true;
663                         tbtnMedia.PerformClick();
664                         return;
665
666                     case Keys.D9:
667                         e.Handled = e.SuppressKeyPress = true;
668                         tbtnVoice.PerformClick();
669                         return;
670                 }
671             }
672
673             // ctrl-g, goto slurl
674             if (e.Control && e.KeyCode == Keys.G)
675             {
676                 if (!ProcessLink(Clipboard.GetText(), true))
677                     MapToCurrentLocation();
678
679                 e.Handled = e.SuppressKeyPress = true;
680                 return;
681             }
682
683             // ctrl-(shift)-tab for next/previous tab
684             if (e.Control && e.KeyCode == Keys.Tab)
685             {
686                 if (e.Shift)
687                 {
688                     TabConsole.SelectPreviousTab();
689                 }
690                 else
691                 {
692                     TabConsole.SelectNextTab();
693                 }
694                 e.Handled = e.SuppressKeyPress = true;
695                 return;
696             }
697         }
698
699         bool firstLoad = true;
700
701         private void frmMain_Load(object sender, EventArgs e)
702         {
703             if (firstLoad)
704             {
705                 firstLoad = false;
706                 tabsConsole.SelectTab("login");
707                 ResourceManager rm = Properties.Resources.ResourceManager;
708                 ResourceSet set = rm.GetResourceSet(System.Globalization.CultureInfo.CurrentCulture, true, true);
709                 System.Collections.IDictionaryEnumerator de = set.GetEnumerator();
710                 while (de.MoveNext() == true)
711                 {
712                     if (de.Entry.Value is Image)
713                     {
714                         Bitmap bitMap = de.Entry.Value as Bitmap;
715                         ResourceImages.Images.Add(bitMap);
716                         ImageNames.Add(de.Entry.Key.ToString());
717                     }
718                 }
719                 StartUpdateCheck(false);
720
721                 if (instance.PlainColors)
722                 {
723                     pnlDialog.BackColor = System.Drawing.Color.FromArgb(120, 220, 255);
724                 }
725
726             }
727         }
728         #endregion
729
730         #region Public methods
731
732         private Dictionary<UUID, frmProfile> shownProfiles = new Dictionary<UUID, frmProfile>();
733
734         void ShowAgentProfileInternal(string name, UUID agentID)
735         {
736             lock (shownProfiles)
737             {
738                 frmProfile profile = null;
739                 if (shownProfiles.TryGetValue(agentID, out profile))
740                 {
741                     profile.WindowState = FormWindowState.Normal;
742                     profile.Focus();
743                 }
744                 else
745                 {
746                     profile = new frmProfile(instance, name, agentID);
747
748                     profile.Disposed += (object sender, EventArgs e) =>
749                         {
750                             lock (shownProfiles)
751                             {
752                                 frmProfile agentProfile = (frmProfile)sender;
753                                 if (shownProfiles.ContainsKey(agentProfile.AgentID))
754                                     shownProfiles.Remove(agentProfile.AgentID);
755                             }
756                         };
757
758                     profile.Show();
759                     profile.Focus();
760                     shownProfiles.Add(agentID, profile);
761                 }
762             }
763         }
764
765         private Dictionary<UUID, frmGroupInfo> shownGroupProfiles = new Dictionary<UUID, frmGroupInfo>();
766
767         public void ShowGroupProfile(UUID id)
768         {
769             ShowGroupProfile(new OpenMetaverse.Group()
770             {
771                 ID = id,
772             });
773         }
774
775         public void ShowGroupProfile(AvatarGroup group)
776         {
777             ShowGroupProfile(new OpenMetaverse.Group()
778             {
779                 ID = group.GroupID,
780                 InsigniaID = group.GroupInsigniaID,
781                 Name = group.GroupName
782             }
783             );
784         }
785
786         public void ShowGroupProfile(OpenMetaverse.Group group)
787         {
788             if (InvokeRequired)
789             {
790                 BeginInvoke(new MethodInvoker(() => ShowGroupProfile(group)));
791                 return;
792             }
793
794             lock (shownGroupProfiles)
795             {
796                 frmGroupInfo profile = null;
797                 if (shownGroupProfiles.TryGetValue(group.ID, out profile))
798                 {
799                     profile.WindowState = FormWindowState.Normal;
800                     profile.Focus();
801                 }
802                 else
803                 {
804                     profile = new frmGroupInfo(instance, group);
805
806                     profile.Disposed += (object sender, EventArgs e) =>
807                         {
808                             lock (shownGroupProfiles)
809                             {
810                                 frmGroupInfo groupProfile = (frmGroupInfo)sender;
811                                 if (shownGroupProfiles.ContainsKey(groupProfile.Group.ID))
812                                     shownGroupProfiles.Remove(groupProfile.Group.ID);
813                             }
814                         };
815
816                     profile.Show();
817                     profile.Focus();
818                     shownGroupProfiles.Add(group.ID, profile);
819                 }
820             }
821         }
822
823         public bool ProcessSecondlifeURI(string link)
824         {
825             // First try if we have a region name, assume it's a teleport link if we have
826             Regex r = new Regex(@"^(secondlife://)(?<region>[^/$]+)(/(?<x>\d+))?((/?<y>\d+))?(/(?<z>\d+))?",
827                 RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase);
828             Match m = r.Match(link);
829
830             if (m.Success)
831             {
832                 string region = HttpUtility.UrlDecode(m.Groups["region"].Value);
833                 int x = string.IsNullOrEmpty(m.Groups["x"].Value) ? 128 : int.Parse(m.Groups["x"].Value);
834                 int y = string.IsNullOrEmpty(m.Groups["y"].Value) ? 128 : int.Parse(m.Groups["y"].Value);
835                 int z = string.IsNullOrEmpty(m.Groups["z"].Value) ? 0 : int.Parse(m.Groups["z"].Value);
836                 MapTab.Select();
837                 WorldMap.DisplayLocation(region, x, y, z);
838                 return true;
839             }
840
841             // Is it group profile link
842             r = new Regex(@"^secondlife:///app/group/(?<id>[^/]+)/about",
843                 RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase);
844             m = r.Match(link);
845
846             if (m.Success)
847             {
848                 UUID id;
849                 if (UUID.TryParse(m.Groups["id"].Value, out id))
850                 {
851                     ShowGroupProfile(id);
852                     return true;
853                 }
854                 return false;
855             }
856
857             // Is it user profile link
858             r = new Regex(@"^secondlife:///app/agent/(?<id>[^/]+)/about",
859                 RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase);
860             m = r.Match(link);
861
862             if (m.Success)
863             {
864                 UUID id;
865                 if (UUID.TryParse(m.Groups["id"].Value, out id))
866                 {
867                     ShowAgentProfile(instance.Names.Get(id), id);
868                     return true;
869                 }
870                 return false;
871             }
872
873             return false;
874         }
875
876         public void ProcessLink(string link)
877         {
878             ProcessLink(link, false);
879         }
880
881         public bool ProcessLink(string link, bool onlyMap)
882         {
883             var pos = link.IndexOf(RRichTextBox.LinkSeparator);
884             if (pos > 0)
885             {
886                 link = link.Substring(pos + 1);
887             }
888
889             if (link.StartsWith("secondlife://"))
890             {
891                 return ProcessSecondlifeURI(link);
892             }
893
894             if (!link.Contains("://"))
895             {
896                 link = "http://" + link;
897             }
898
899             Regex r = new Regex(@"^(http://(slurl\.com|maps\.secondlife\.com)/secondlife/|secondlife://)(?<region>[^/]+)/(?<x>\d+)/(?<y>\d+)(/(?<z>\d+))?",
900                 RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase
901                 );
902             Match m = r.Match(link);
903
904             if (m.Success)
905             {
906                 string region = HttpUtility.UrlDecode(m.Groups["region"].Value);
907                 int x = int.Parse(m.Groups["x"].Value);
908                 int y = int.Parse(m.Groups["y"].Value);
909                 int z = 0;
910
911                 if (!string.IsNullOrEmpty(m.Groups["z"].Value))
912                 {
913                     z = int.Parse(m.Groups["z"].Value);
914                 }
915
916                 MapTab.Select();
917                 WorldMap.DisplayLocation(region, x, y, z);
918                 return true;
919             }
920             else if (!onlyMap)
921             {
922                 System.Diagnostics.Process.Start(link);
923             }
924             return false;
925         }
926         #endregion
927
928         #region Notifications
929         CircularList<Control> notifications = new CircularList<Control>();
930
931         public Color NotificationBackground
932         {
933             get { return pnlDialog.BackColor; }
934         }
935
936         void ResizeNotificationByControl(Control active)
937         {
938             int Width = active.Size.Width + 6;
939             int Height = notifications.Count > 1 ? active.Size.Height + 3 + btnDialogNextControl.Size.Height : active.Size.Height + 3;
940             pnlDialog.Size = new Size(Width, Height);
941             pnlDialog.Top = 0;
942             pnlDialog.Left = pnlDialog.Parent.ClientSize.Width - Width;
943
944             btnDialogNextControl.Top = btnDialogNextControl.Parent.ClientSize.Height - btnDialogNextControl.Size.Height;
945             btnDialogNextControl.Left = btnDialogNextControl.Parent.ClientSize.Width - btnDialogNextControl.Size.Width;
946
947             btnDialogNextControl.BringToFront();
948         }
949
950         public void AddNotification(Control control)
951         {
952             if (InvokeRequired)
953             {
954                 BeginInvoke(new MethodInvoker(delegate()
955                 {
956                     AddNotification(control);
957                 }
958                 ));
959                 return;
960             }
961
962             Control active = TabsConsole.FindFocusedControl(this);
963
964             FormFlash.StartFlash(this);
965             pnlDialog.Visible = true;
966             pnlDialog.BringToFront();
967
968             foreach (Control existing in notifications)
969             {
970                 existing.Visible = false;
971             }
972
973             notifications.Add(control);
974             control.Visible = true;
975             control.Anchor = AnchorStyles.Top | AnchorStyles.Left;
976             control.Top = 3;
977             control.Left = 3;
978             pnlDialog.Controls.Add(control);
979             ResizeNotificationByControl(control);
980
981             btnDialogNextControl.Visible = notifications.Count > 1;
982
983             if (active != null)
984             {
985                 active.Focus();
986             }
987         }
988
989         public void RemoveNotification(Control control)
990         {
991             pnlDialog.Controls.Remove(control);
992             notifications.Remove(control);
993             control.Dispose();
994
995             if (notifications.HasNext)
996             {
997                 pnlDialog.Visible = true;
998                 Control active = notifications.Next;
999                 active.Visible = true;
1000                 ResizeNotificationByControl(active);
1001             }
1002             else
1003             {
1004                 pnlDialog.Visible = false;
1005             }
1006
1007             btnDialogNextControl.Visible = notifications.Count > 1;
1008         }
1009
1010         private void btnDialogNextControl_Click(object sender, EventArgs e)
1011         {
1012             foreach (Control existing in notifications)
1013             {
1014                 existing.Visible = false;
1015             }
1016
1017             if (notifications.HasNext)
1018             {
1019                 pnlDialog.Visible = true;
1020                 Control active = notifications.Next;
1021                 active.Visible = true;
1022                 ResizeNotificationByControl(active);
1023             }
1024             else
1025             {
1026                 pnlDialog.Visible = false;
1027             }
1028
1029         }
1030         #endregion Notifications
1031
1032         #region Menu click handlers
1033
1034         private void tmnuStatusAway_Click(object sender, EventArgs e)
1035         {
1036             instance.State.SetAway(tmnuStatusAway.Checked);
1037         }
1038
1039         private void tmnuHelpReadme_Click(object sender, EventArgs e)
1040         {
1041             System.Diagnostics.Process.Start(Application.StartupPath + @"\Readme.txt");
1042         }
1043
1044         private void tmnuStatusBusy_Click(object sender, EventArgs e)
1045         {
1046             instance.State.SetBusy(tmnuStatusBusy.Checked);
1047         }
1048
1049         private void tmnuControlFly_Click(object sender, EventArgs e)
1050         {
1051             instance.State.SetFlying(tmnuControlFly.Checked);
1052         }
1053
1054         private void tmnuControlAlwaysRun_Click(object sender, EventArgs e)
1055         {
1056             instance.State.SetAlwaysRun(tmnuControlAlwaysRun.Checked);
1057         }
1058
1059         private void tmnuPrefs_Click(object sender, EventArgs e)
1060         {
1061             (new frmSettings(instance)).ShowDialog();
1062         }
1063
1064         private void tbtnAppearance_Click(object sender, EventArgs e)
1065         {
1066             client.Appearance.RequestSetAppearance(false);
1067         }
1068
1069         private void importObjectToolStripMenuItem_Click(object sender, EventArgs e)
1070         {
1071             PrimDeserializer.ImportFromFile(client);
1072         }
1073
1074         private void autopilotToolStripMenuItem_Click(object sender, EventArgs e)
1075         {
1076             if (ap == null)
1077             {
1078                 ap = new AutoPilot(client);
1079                 /*
1080                 ap.InsertWaypoint(new Vector3(66, 163, 21));
1081                 ap.InsertWaypoint(new Vector3(66, 98, 21));
1082
1083                 ap.InsertWaypoint(new Vector3(101, 98, 21));
1084                 ap.InsertWaypoint(new Vector3(101, 45, 21));
1085                 ap.InsertWaypoint(new Vector3(93, 27, 21));
1086                 ap.InsertWaypoint(new Vector3(106, 12, 21));
1087                 ap.InsertWaypoint(new Vector3(123, 24, 21));
1088                 ap.InsertWaypoint(new Vector3(114, 45, 21));
1089                 ap.InsertWaypoint(new Vector3(114, 98, 21));
1090
1091                 ap.InsertWaypoint(new Vector3(130, 98, 21));
1092                 ap.InsertWaypoint(new Vector3(130, 163, 21));
1093                  **/
1094                 ap.InsertWaypoint(new Vector3(64, 68, 21));
1095                 ap.InsertWaypoint(new Vector3(65, 20, 21));
1096                 ap.InsertWaypoint(new Vector3(33, 23, 21));
1097                 ap.InsertWaypoint(new Vector3(17, 39, 21));
1098                 ap.InsertWaypoint(new Vector3(17, 62, 21));
1099
1100
1101             }
1102             if (AutoPilotActive)
1103             {
1104                 AutoPilotActive = false;
1105                 ap.Stop();
1106             }
1107             else
1108             {
1109                 AutoPilotActive = true;
1110                 ap.Start();
1111             }
1112
1113         }
1114
1115         private void cleanCacheToolStripMenuItem_Click(object sender, EventArgs e)
1116         {
1117             ThreadPool.QueueUserWorkItem(sync => client.Assets.Cache.Clear());
1118             instance.Names.CleanCache();
1119         }
1120
1121         private void rebakeTexturesToolStripMenuItem_Click(object sender, EventArgs e)
1122         {
1123             client.Appearance.RequestSetAppearance(true);
1124         }
1125
1126         public void MapToCurrentLocation()
1127         {
1128             if (MapTab != null && client.Network.Connected)
1129             {
1130                 MapTab.Select();
1131                 WorldMap.DisplayLocation(client.Network.CurrentSim.Name,
1132                     (int)client.Self.SimPosition.X,
1133                     (int)client.Self.SimPosition.Y,
1134                     (int)client.Self.SimPosition.Z);
1135             }
1136         }
1137
1138         private void standToolStripMenuItem_Click(object sender, EventArgs e)
1139         {
1140             instance.State.SetSitting(false, UUID.Zero);
1141         }
1142
1143         private void groundSitToolStripMenuItem_Click(object sender, EventArgs e)
1144         {
1145             client.Self.SitOnGround();
1146         }
1147
1148         private void newWindowToolStripMenuItem_Click(object sender, EventArgs e)
1149         {
1150             try { System.Diagnostics.Process.Start(Application.ExecutablePath); }
1151             catch (Exception) { }
1152         }
1153
1154         private void tmnuExit_Click(object sender, EventArgs e)
1155         {
1156             Close();
1157         }
1158
1159         private void tlblRegionInfo_Click(object sender, EventArgs e)
1160         {
1161             if (WorldMap != null && client.Network.Connected)
1162             {
1163                 MapTab.Select();
1164             }
1165         }
1166
1167         private void scriptEditorToolStripMenuItem_Click(object sender, EventArgs e)
1168         {
1169             ScriptEditor se = new ScriptEditor(instance);
1170             se.Dock = DockStyle.Fill;
1171             se.ShowDetached();
1172         }
1173
1174         private void tmnuSetHome_Click(object sender, EventArgs e)
1175         {
1176             client.Self.SetHome();
1177         }
1178
1179         private void tmnuCreateLandmark_Click(object sender, EventArgs e)
1180         {
1181             string location = string.Format(", {0} ({1}, {2}, {3})",
1182                 client.Network.CurrentSim.Name,
1183                 (int)client.Self.SimPosition.X,
1184                 (int)client.Self.SimPosition.Y,
1185                 (int)client.Self.SimPosition.Z
1186                 );
1187
1188             string name = tlblParcel.Text;
1189             int maxLen = 63 - location.Length;
1190
1191             if (name.Length > maxLen)
1192                 name = name.Substring(0, maxLen);
1193
1194             name += location;
1195
1196             client.Inventory.RequestCreateItem(
1197                 client.Inventory.FindFolderForType(AssetType.Landmark),
1198                 name,
1199                 tlblParcel.ToolTipText,
1200                 AssetType.Landmark,
1201                 UUID.Random(),
1202                 InventoryType.Landmark,
1203                 PermissionMask.All,
1204                 (bool success, InventoryItem item) =>
1205                 {
1206                     if (success)
1207                     {
1208                         BeginInvoke(new MethodInvoker(() =>
1209                             {
1210                                 Landmark ln = new Landmark(instance, (InventoryLandmark)item);
1211                                 ln.Dock = DockStyle.Fill;
1212                                 ln.Detached = true;
1213                             }));
1214                     }
1215                 }
1216             );
1217         }
1218
1219
1220         private void timerWorldClock_Tick(object sender, EventArgs e)
1221         {
1222             lblTime.Text = instance.GetWorldTime().ToString("h:mm tt", System.Globalization.CultureInfo.InvariantCulture);
1223         }
1224
1225         private void reportBugsToolStripMenuItem_Click(object sender, EventArgs e)
1226         {
1227             ProcessLink("http://jira.openmetaverse.org/browse/RAD");
1228         }
1229
1230         private void aboutRadegastToolStripMenuItem_Click(object sender, EventArgs e)
1231         {
1232             (new frmAbout(instance)).ShowDialog();
1233         }
1234
1235         #region Update Checking
1236         private UpdateChecker updateChecker = null;
1237         private bool ManualUpdateCheck = false;
1238
1239         public void StartUpdateCheck(bool userInitiated)
1240         {
1241             ManualUpdateCheck = userInitiated;
1242
1243             if (updateChecker != null)
1244             {
1245                 if (ManualUpdateCheck)
1246                     tabsConsole.DisplayNotificationInChat("Update check already in progress.");
1247                 return;
1248             }
1249
1250             if (ManualUpdateCheck)
1251                 tabsConsole.DisplayNotificationInChat("Checking for updates...", ChatBufferTextStyle.StatusBlue);
1252             updateChecker = new UpdateChecker();
1253             updateChecker.OnUpdateInfoReceived += new UpdateChecker.UpdateInfoCallback(OnUpdateInfoReceived);
1254             updateChecker.StartCheck();
1255         }
1256
1257         private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
1258         {
1259             tabsConsole.SelectTab("chat");
1260             StartUpdateCheck(true);
1261         }
1262
1263         void OnUpdateInfoReceived(object sender, UpdateCheckerArgs e)
1264         {
1265             if (InvokeRequired)
1266             {
1267                 BeginInvoke(new MethodInvoker(() => OnUpdateInfoReceived(sender, e)));
1268                 return;
1269             }
1270
1271             if (!e.Success)
1272             {
1273                 if (ManualUpdateCheck)
1274                     tabsConsole.DisplayNotificationInChat("Error: Failed connecting to the update site.", ChatBufferTextStyle.StatusBlue);
1275             }
1276             else
1277             {
1278                 if (!ManualUpdateCheck && e.Info.DisplayMOTD)
1279                 {
1280                     tabsConsole.DisplayNotificationInChat(e.Info.MOTD, ChatBufferTextStyle.StatusBlue);
1281                 }
1282
1283                 if (e.Info.UpdateAvailable)
1284                 {
1285                     tabsConsole.DisplayNotificationInChat("New version available at " + e.Info.DownloadSite, ChatBufferTextStyle.Alert);
1286                 }
1287                 else
1288                 {
1289                     if (ManualUpdateCheck)
1290                         tabsConsole.DisplayNotificationInChat("Your version is up to date.", ChatBufferTextStyle.StatusBlue);
1291                 }
1292             }
1293
1294             updateChecker.Dispose();
1295             updateChecker = null;
1296         }
1297         #endregion
1298
1299         private void ToggleHidden(string tabName)
1300         {
1301             if (!tabsConsole.TabExists(tabName)) return;
1302
1303             RadegastTab tab = tabsConsole.Tabs[tabName];
1304
1305             if (tab.Hidden)
1306             {
1307                 tab.Show();
1308             }
1309             else
1310             {
1311                 if (!tab.Selected)
1312                 {
1313                     tab.Select();
1314                 }
1315                 else
1316                 {
1317                     tab.Hide();
1318                 }
1319             }
1320         }
1321
1322         private void tbtnFriends_Click(object sender, EventArgs e)
1323         {
1324             ToggleHidden("friends");
1325         }
1326
1327         private void tbtnInventory_Click(object sender, EventArgs e)
1328         {
1329             ToggleHidden("inventory");
1330         }
1331
1332         private void tbtnSearch_Click(object sender, EventArgs e)
1333         {
1334             ToggleHidden("search");
1335         }
1336
1337         private void tbtnGroups_Click(object sender, EventArgs e)
1338         {
1339             ToggleHidden("groups");
1340         }
1341
1342         private void tbtnVoice_Click(object sender, EventArgs e)
1343         {
1344             ToggleHidden("voice");
1345         }
1346
1347         private void tbtnMedia_Click(object sender, EventArgs e)
1348         {
1349             if (tabsConsole.TabExists("media"))
1350             {
1351                 ToggleHidden("media");
1352             }
1353             else
1354             {
1355                 RadegastTab tab = tabsConsole.AddTab("media", "Media", mediaConsole);
1356                 tab.AllowClose = false;
1357                 tab.AllowHide = true;
1358                 tab.Select();
1359             }
1360         }
1361
1362         private void debugConsoleToolStripMenuItem_Click(object sender, EventArgs e)
1363         {
1364             if (tabsConsole.TabExists("debug"))
1365             {
1366                 ToggleHidden("debug");
1367             }
1368             else
1369             {
1370                 RadegastTab tab = tabsConsole.AddTab("debug", "Debug", new DebugConsole(instance));
1371                 tab.AllowClose = false;
1372                 tab.AllowHide = true;
1373                 tab.Select();
1374             }
1375         }
1376
1377         private void tbnObjects_Click(object sender, EventArgs e)
1378         {
1379             if (tabsConsole.TabExists("objects"))
1380             {
1381                 RadegastTab tab = tabsConsole.Tabs["objects"];
1382                 if (!tab.Selected)
1383                 {
1384                     tab.Select();
1385                     ((ObjectsConsole)tab.Control).RefreshObjectList();
1386                 }
1387                 else
1388                 {
1389                     tab.Close();
1390                 }
1391             }
1392             else
1393             {
1394                 RadegastTab tab = tabsConsole.AddTab("objects", "Objects", new ObjectsConsole(instance));
1395                 tab.AllowClose = true;
1396                 tab.AllowDetach = true;
1397                 tab.Visible = true;
1398                 tab.AllowHide = false;
1399                 tab.Select();
1400                 ((ObjectsConsole)tab.Control).RefreshObjectList();
1401             }
1402         }
1403
1404         private void tbtnMap_Click(object sender, EventArgs e)
1405         {
1406             if (MapTab == null) return; // too soon!
1407
1408             ToggleHidden("map");
1409             if (!MapTab.Hidden)
1410                 MapToCurrentLocation();
1411         }
1412
1413         private void disconnectToolStripMenuItem_Click(object sender, EventArgs e)
1414         {
1415             if (client.Network.Connected)
1416                 client.Network.RequestLogout();
1417         }
1418
1419         private void reconnectToolStripMenuItem_Click(object sender, EventArgs e)
1420         {
1421             if (!client.Network.Connected)
1422             {
1423                 instance.Reconnect();
1424             }
1425         }
1426
1427         private frmKeyboardShortcuts keyboardShortcutsForm = null;
1428         private void keyboardShortcutsToolStripMenuItem_Click(object sender, EventArgs e)
1429         {
1430             if (keyboardShortcutsForm != null)
1431             {
1432                 keyboardShortcutsForm.Focus();
1433             }
1434             else
1435             {
1436                 keyboardShortcutsForm = new frmKeyboardShortcuts(instance);
1437
1438                 keyboardShortcutsForm.Disposed += (object senderx, EventArgs ex) =>
1439                     {
1440                         if (components != null)
1441                         {
1442                             components.Remove(keyboardShortcutsForm);
1443                         }
1444                         keyboardShortcutsForm = null;
1445                     };
1446
1447                 keyboardShortcutsForm.Show(this);
1448                 keyboardShortcutsForm.Top = Top + 100;
1449                 keyboardShortcutsForm.Left = Left + 100;
1450
1451                 if (components != null)
1452                 {
1453                     components.Add(keyboardShortcutsForm);
1454                 }
1455             }
1456         }
1457
1458         // Menu item for testing out stuff
1459         private void testToolStripMenuItem_Click(object sender, EventArgs e)
1460         {
1461         }
1462
1463         private void reloadInventoryToolStripMenuItem_Click(object sender, EventArgs e)
1464         {
1465             if (tabsConsole.TabExists("inventory"))
1466             {
1467                 ((InventoryConsole)tabsConsole.Tabs["inventory"].Control).ReloadInventory();
1468                 tabsConsole.Tabs["inventory"].Select();
1469             }
1470         }
1471
1472         private void btnLoadScript_Click(object sender, EventArgs e)
1473         {
1474             if (!TabConsole.TabExists("plugin_manager"))
1475             {
1476                 TabConsole.AddTab("plugin_manager", "Plugins", new PluginsTab(instance));
1477             }
1478             TabConsole.Tabs["plugin_manager"].Select();
1479         }
1480
1481         private void frmMain_Resize(object sender, EventArgs e)
1482         {
1483             if (WindowState == FormWindowState.Minimized && instance.GlobalSettings["minimize_to_tray"].AsBoolean())
1484             {
1485                 if (TabConsole.TabExists("scene_window") && !TabConsole.Tabs["scene_window"].Detached)
1486                 {
1487                     TabConsole.Tabs["scene_window"].Close();
1488                 }
1489                 ShowInTaskbar = false;
1490                 trayIcon.Visible = true;
1491                 FormBorderStyle = FormBorderStyle.SizableToolWindow;
1492             }
1493         }
1494
1495         private void treyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
1496         {
1497             WindowState = FormWindowState.Normal;
1498             ShowInTaskbar = true;
1499             trayIcon.Visible = false;
1500             FormBorderStyle = FormBorderStyle.Sizable;
1501         }
1502
1503         private void ctxTreyRestore_Click(object sender, EventArgs e)
1504         {
1505             treyIcon_MouseDoubleClick(this, null);
1506         }
1507
1508         private void ctxTreyExit_Click(object sender, EventArgs e)
1509         {
1510             tmnuExit_Click(this, EventArgs.Empty);
1511         }
1512
1513         private void tmnuTeleportHome_Click(object sender, EventArgs e)
1514         {
1515             TabConsole.DisplayNotificationInChat("Teleporting home...");
1516             client.Self.RequestTeleport(UUID.Zero);
1517         }
1518
1519         private void stopAllAnimationsToolStripMenuItem_Click(object sender, EventArgs e)
1520         {
1521             instance.State.StopAllAnimations();
1522         }
1523
1524         public void DisplayRegionParcelConsole()
1525         {
1526             if (tabsConsole.TabExists("current region info"))
1527             {
1528                 tabsConsole.Tabs["current region info"].Select();
1529                 (tabsConsole.Tabs["current region info"].Control as RegionInfo).UpdateDisplay();
1530             }
1531             else
1532             {
1533                 tabsConsole.AddTab("current region info", "Region info", new RegionInfo(instance));
1534                 tabsConsole.Tabs["current region info"].Select();
1535             }
1536         }
1537
1538         private void regionParcelToolStripMenuItem_Click(object sender, EventArgs e)
1539         {
1540             DisplayRegionParcelConsole();
1541         }
1542
1543         private void tlblParcel_Click(object sender, EventArgs e)
1544         {
1545             DisplayRegionParcelConsole();
1546         }
1547
1548         private void changeMyDisplayNameToolStripMenuItem_Click(object sender, EventArgs e)
1549         {
1550             if (!client.Avatars.DisplayNamesAvailable())
1551             {
1552                 tabsConsole.DisplayNotificationInChat("This grid does not support display names.", ChatBufferTextStyle.Error);
1553                 return;
1554             }
1555
1556             var dlg = new DisplayNameChange(instance);
1557             dlg.ShowDialog();
1558         }
1559
1560         private void muteListToolStripMenuItem_Click(object sender, EventArgs e)
1561         {
1562             if (!tabsConsole.TabExists("mute list console"))
1563             {
1564                 tabsConsole.AddTab("mute list console", "Mute list", new MuteList(instance));
1565             }
1566             tabsConsole.Tabs["mute list console"].Select();
1567         }
1568
1569         private void uploadImageToolStripMenuItem_Click(object sender, EventArgs e)
1570         {
1571             if (!tabsConsole.TabExists("image upload console"))
1572             {
1573                 tabsConsole.AddTab("image upload console", "Upload image", new ImageUploadConsole(instance));
1574             }
1575             tabsConsole.Tabs["image upload console"].Select();
1576         }
1577         #endregion
1578
1579         private void myAttachmentsToolStripMenuItem_Click(object sender, EventArgs e)
1580         {
1581             Avatar av = client.Network.CurrentSim.ObjectsAvatars.Find((Avatar a) => { return a.ID == client.Self.AgentID; });
1582
1583             if (av == null)
1584             {
1585                 tabsConsole.DisplayNotificationInChat("Unable to find my avatar!", ChatBufferTextStyle.Error);
1586                 return;
1587             }
1588
1589             if (!instance.TabConsole.TabExists("AT: " + av.ID.ToString()))
1590             {
1591                 instance.TabConsole.AddTab("AT: " + av.ID.ToString(), "My Attachments", new AttachmentTab(instance, av));
1592             }
1593             instance.TabConsole.SelectTab("AT: " + av.ID.ToString());
1594
1595         }
1596
1597         private void tsb3D_Click(object sender, EventArgs e)
1598         {
1599             if (instance.TabConsole.TabExists("scene_window"))
1600             {
1601                 instance.TabConsole.Tabs["scene_window"].Select();
1602             }
1603             else
1604             {
1605                 var control = new Rendering.SceneWindow(instance);
1606                 control.Dock = DockStyle.Fill;
1607                 instance.TabConsole.AddTab("scene_window", "Scene Viewer", control);
1608                 instance.TabConsole.Tabs["scene_window"].Floater = false;
1609                 instance.TabConsole.Tabs["scene_window"].CloseOnDetachedClose = true;
1610                 control.RegisterTabEvents();
1611
1612                 if (instance.GlobalSettings["scene_window_docked"])
1613                 {
1614                     instance.TabConsole.Tabs["scene_window"].Select();
1615                 }
1616                 else
1617                 {
1618                     instance.TabConsole.Tabs["scene_window"].Detach(instance);
1619                 }
1620             }
1621         }
1622
1623         private void loginToolStripMenuItem_Click(object sender, EventArgs e)
1624         {
1625             TabConsole.InitializeMainTab();
1626             TabConsole.Tabs["login"].Select();
1627         }
1628     }
1629 }