OSDN Git Service

Added initial region info console
[radegast/radegast.git] / Radegast / GUI / Dialogs / MainForm.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2010, 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
55         public TabsConsole TabConsole
56         {
57             get { return tabsConsole; }
58         }
59
60         public MapConsole WorldMap
61         {
62             get
63             {
64                 if (MapTab != null)
65                 {
66                     return (MapConsole)MapTab.Control;
67                 }
68                 return null;
69             }
70         }
71
72         public RadegastTab MapTab
73         {
74             get
75             {
76                 if (tabsConsole.TabExists("map"))
77                 {
78                     return tabsConsole.Tabs["map"];
79                 }
80                 else
81                 {
82                     return null;
83                 }
84             }
85         }
86
87         public MediaConsole MediaConsole { get { return mediaConsole; } }
88
89         /// <summary>
90         /// Drop down that contains the tools menu
91         /// </summary>
92         public ToolStripDropDownButton ToolsMenu
93         {
94             get { return tbnTools; }
95         }
96
97         /// <summary>
98         /// Dropdown that contains the heelp menu
99         /// </summary>
100         public ToolStripDropDownButton HelpMenu
101         {
102             get { return tbtnHelp; }
103         }
104
105         /// <summary>
106         /// Drop down that contants the plugins menu. Make sure to set it Visible if
107         /// you add items to this menu, it's hidden by default
108         /// </summary>
109         public ToolStripDropDownButton PluginsMenu
110         {
111             get { return tbnPlugins; }
112         }
113
114         #endregion
115
116         #region Private members
117         private RadegastInstance instance;
118         private GridClient client { get { return instance.Client; } }
119         private RadegastNetcom netcom { get { return instance.Netcom; } }
120         private TabsConsole tabsConsole;
121         private System.Timers.Timer statusTimer;
122         private AutoPilot ap;
123         private bool AutoPilotActive = false;
124         private TransparentButton btnDialogNextControl;
125         private MediaConsole mediaConsole;
126         #endregion
127
128         #region Constructor and disposal
129         public frmMain(RadegastInstance instance)
130             : base(instance)
131         {
132             InitializeComponent();
133             Disposed += new EventHandler(frmMain_Disposed);
134
135             this.instance = instance;
136             this.instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
137             netcom.NetcomSync = this;
138
139             pnlDialog.Visible = false;
140             btnDialogNextControl = new TransparentButton();
141             pnlDialog.Controls.Add(btnDialogNextControl);
142             pnlDialog.Top = 0;
143
144             btnDialogNextControl.Size = new Size(35, 20);
145             btnDialogNextControl.BackColor = Color.Transparent;
146             btnDialogNextControl.ForeColor = Color.Gold;
147             btnDialogNextControl.FlatAppearance.BorderSize = 0;
148             btnDialogNextControl.FlatStyle = FlatStyle.Flat;
149             btnDialogNextControl.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
150             btnDialogNextControl.Text = ">>";
151             btnDialogNextControl.Font = new Font(btnDialogNextControl.Font, FontStyle.Bold);
152             btnDialogNextControl.Margin = new Padding(0);
153             btnDialogNextControl.Padding = new Padding(0);
154             btnDialogNextControl.UseVisualStyleBackColor = false;
155             btnDialogNextControl.Top = btnDialogNextControl.Parent.ClientSize.Height - btnDialogNextControl.Size.Height;
156             btnDialogNextControl.Left = btnDialogNextControl.Parent.ClientSize.Width - btnDialogNextControl.Size.Width;
157             btnDialogNextControl.Click += new EventHandler(btnDialogNextControl_Click);
158
159             if (instance.MonoRuntime)
160             {
161                 statusStrip1.LayoutStyle = ToolStripLayoutStyle.Table;
162             }
163
164             // Config options
165             if (instance.GlobalSettings["transaction_notification_chat"].Type == OSDType.Unknown)
166                 instance.GlobalSettings["transaction_notification_chat"] = OSD.FromBoolean(true);
167
168             if (instance.GlobalSettings["transaction_notification_dialog"].Type == OSDType.Unknown)
169                 instance.GlobalSettings["transaction_notification_dialog"] = OSD.FromBoolean(true);
170
171             if (!instance.GlobalSettings.ContainsKey("minimize_to_tray"))
172                 instance.GlobalSettings["minimize_to_tray"] = OSD.FromBoolean(false);
173
174             // Callbacks
175             netcom.ClientLoginStatus += new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
176             netcom.ClientLoggedOut += new EventHandler(netcom_ClientLoggedOut);
177             netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
178             RegisterClientEvents(client);
179
180             InitializeStatusTimer();
181             RefreshWindowTitle();
182         }
183
184         private void RegisterClientEvents(GridClient client)
185         {
186             client.Parcels.ParcelProperties += new EventHandler<ParcelPropertiesEventArgs>(Parcels_ParcelProperties);
187             client.Self.MoneyBalanceReply += new EventHandler<MoneyBalanceReplyEventArgs>(Self_MoneyBalanceReply);
188         }
189
190         private void UnregisterClientEvents(GridClient client)
191         {
192             client.Parcels.ParcelProperties -= new EventHandler<ParcelPropertiesEventArgs>(Parcels_ParcelProperties);
193             client.Self.MoneyBalanceReply -= new EventHandler<MoneyBalanceReplyEventArgs>(Self_MoneyBalanceReply);
194         }
195
196         void instance_ClientChanged(object sender, ClientChangedEventArgs e)
197         {
198             UnregisterClientEvents(e.OldClient);
199             RegisterClientEvents(client);
200         }
201
202         void frmMain_Disposed(object sender, EventArgs e)
203         {
204             if (netcom != null)
205             {
206                 netcom.NetcomSync = null;
207                 netcom.ClientLoginStatus -= new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
208                 netcom.ClientLoggedOut -= new EventHandler(netcom_ClientLoggedOut);
209                 netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
210             }
211             if (client != null)
212             {
213                 UnregisterClientEvents(client);
214             }
215             this.instance.CleanUp();
216         }
217         #endregion
218
219         #region Event handlers
220         void Self_MoneyBalanceReply(object sender, MoneyBalanceReplyEventArgs e)
221         {
222             if (!String.IsNullOrEmpty(e.Description))
223             {
224                 if (instance.GlobalSettings["transaction_notification_dialog"].AsBoolean())
225                     AddNotification(new ntfGeneric(instance, e.Description));
226                 if (instance.GlobalSettings["transaction_notification_chat"].AsBoolean())
227                     TabConsole.DisplayNotificationInChat(e.Description);
228             }
229         }
230
231         public void InitializeControls()
232         {
233             InitializeTabsConsole();
234
235             if (instance.MediaManager.SoundSystemAvailable)
236             {
237                 mediaConsole = new MediaConsole(instance);
238                 tbtnMedia.Visible = true;
239             }
240         }
241
242         public bool InAutoReconnect { get; set; }
243
244         private void DisplayAutoReconnectForm()
245         {
246             if (IsDisposed) return;
247
248             if (InvokeRequired)
249             {
250                 BeginInvoke(new MethodInvoker(DisplayAutoReconnectForm));
251                 return;
252             }
253
254             InAutoReconnect = true;
255             frmReconnect dialog = new frmReconnect(instance, 120);
256             dialog.ShowDialog(this);
257             dialog.Dispose();
258             dialog = null;
259         }
260
261         public void BeginAutoReconnect()
262         {
263             // Sleep for 3 seconds on a separate thread while things unwind on
264             // disconnect, since ShowDialog() blocks GUI thread
265             (new Thread(new ThreadStart(() =>
266                 {
267                     System.Threading.Thread.Sleep(3000);
268                     DisplayAutoReconnectForm();
269                 }
270                 ))
271                 {
272                     Name = "Reconnect Delay Thread",
273                     IsBackground = true
274                 }
275             ).Start();
276         }
277
278         private void netcom_ClientLoginStatus(object sender, LoginProgressEventArgs e)
279         {
280             if (e.Status == LoginStatus.Failed)
281             {
282                 if (InAutoReconnect)
283                 {
284                     if (instance.GlobalSettings["auto_reconnect"].AsBoolean())
285                         BeginAutoReconnect();
286                     else
287                         InAutoReconnect = false;
288                 }
289             }
290             else if (e.Status == LoginStatus.Success)
291             {
292                 InAutoReconnect = false;
293                 tbtnVoice.Enabled = disconnectToolStripMenuItem.Enabled =
294                 tbtnGroups.Enabled = tbnObjects.Enabled = tbtnWorld.Enabled = tbnTools.Enabled = tmnuImport.Enabled =
295                     tbtnFriends.Enabled = tbtnInventory.Enabled = tbtnSearch.Enabled = tbtnMap.Enabled = true;
296
297                 statusTimer.Start();
298                 RefreshWindowTitle();
299             }
300         }
301
302         private void netcom_ClientLoggedOut(object sender, EventArgs e)
303         {
304             tbtnVoice.Enabled = disconnectToolStripMenuItem.Enabled =
305             tbtnGroups.Enabled = tbnObjects.Enabled = tbtnWorld.Enabled = tbnTools.Enabled = tmnuImport.Enabled =
306                 tbtnFriends.Enabled = tbtnInventory.Enabled = tbtnSearch.Enabled = tbtnMap.Enabled = false;
307
308             reconnectToolStripMenuItem.Enabled = true;
309             InAutoReconnect = false;
310
311             if (statusTimer != null)
312                 statusTimer.Stop();
313
314             RefreshStatusBar();
315             RefreshWindowTitle();
316         }
317
318         private void netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
319         {
320             if (e.Reason == NetworkManager.DisconnectType.ClientInitiated) return;
321             netcom_ClientLoggedOut(sender, EventArgs.Empty);
322
323             if (instance.GlobalSettings["auto_reconnect"].AsBoolean())
324             {
325                 BeginAutoReconnect();
326             }
327         }
328
329         private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
330         {
331             if (statusTimer != null)
332             {
333                 statusTimer.Stop();
334                 statusTimer.Dispose();
335                 statusTimer = null;
336             }
337
338             if (mediaConsole != null)
339             {
340                 if (tabsConsole.TabExists("media"))
341                 {
342                     tabsConsole.Tabs["media"].AllowClose = true;
343                     tabsConsole.Tabs["media"].Close();
344                 }
345                 else
346                 {
347                     mediaConsole.Dispose();
348                 }
349                 mediaConsole = null;
350             }
351
352             if (netcom.IsLoggedIn)
353             {
354                 Thread saveInvToDisk = new Thread(new ThreadStart(
355                     delegate()
356                     {
357                         client.Inventory.Store.SaveToDisk(instance.InventoryCacheFileName);
358                     }));
359                 saveInvToDisk.Name = "Save inventory to disk";
360                 saveInvToDisk.Start();
361
362                 netcom.Logout();
363             }
364         }
365         #endregion
366
367         # region Update status
368
369         void Parcels_ParcelProperties(object sender, ParcelPropertiesEventArgs e)
370         {
371             if (e.Result != ParcelResult.Single) return;
372             if (InvokeRequired)
373             {
374                 BeginInvoke(new MethodInvoker(() => Parcels_ParcelProperties(sender, e)));
375                 return;
376             }
377
378             Parcel parcel = instance.State.Parcel = e.Parcel;
379
380             tlblParcel.Text = parcel.Name;
381             tlblParcel.ToolTipText = parcel.Desc;
382
383             if ((parcel.Flags & ParcelFlags.AllowFly) != ParcelFlags.AllowFly)
384                 icoNoFly.Visible = true;
385             else
386                 icoNoFly.Visible = false;
387
388             if ((parcel.Flags & ParcelFlags.CreateObjects) != ParcelFlags.CreateObjects)
389                 icoNoBuild.Visible = true;
390             else
391                 icoNoBuild.Visible = false;
392
393             if ((parcel.Flags & ParcelFlags.AllowOtherScripts) != ParcelFlags.AllowOtherScripts)
394                 icoNoScript.Visible = true;
395             else
396                 icoNoScript.Visible = false;
397
398             if ((parcel.Flags & ParcelFlags.RestrictPushObject) == ParcelFlags.RestrictPushObject)
399                 icoNoPush.Visible = true;
400             else
401                 icoNoPush.Visible = false;
402
403             if ((parcel.Flags & ParcelFlags.AllowDamage) == ParcelFlags.AllowDamage)
404                 icoHealth.Visible = true;
405             else
406                 icoHealth.Visible = false;
407
408             if ((parcel.Flags & ParcelFlags.AllowVoiceChat) != ParcelFlags.AllowVoiceChat)
409                 icoNoVoice.Visible = true;
410             else
411                 icoNoVoice.Visible = false;
412         }
413
414         private void RefreshStatusBar()
415         {
416             if (netcom.IsLoggedIn)
417             {
418                 tlblLoginName.Text = netcom.LoginOptions.FullName;
419                 tlblMoneyBalance.Text = client.Self.Balance.ToString();
420                 icoHealth.Text = client.Self.Health.ToString() + "%";
421
422                 tlblRegionInfo.Text =
423                     client.Network.CurrentSim.Name +
424                     " (" + Math.Floor(client.Self.SimPosition.X).ToString() + ", " +
425                     Math.Floor(client.Self.SimPosition.Y).ToString() + ", " +
426                     Math.Floor(client.Self.SimPosition.Z).ToString() + ")";
427             }
428             else
429             {
430                 tlblLoginName.Text = "Offline";
431                 tlblMoneyBalance.Text = "0";
432                 icoHealth.Text = "0%";
433                 tlblRegionInfo.Text = "No Region";
434                 tlblParcel.Text = "No Parcel";
435
436                 icoHealth.Visible = false;
437                 icoNoBuild.Visible = false;
438                 icoNoFly.Visible = false;
439                 icoNoPush.Visible = false;
440                 icoNoScript.Visible = false;
441                 icoNoVoice.Visible = false;
442             }
443         }
444
445         private void RefreshWindowTitle()
446         {
447             StringBuilder sb = new StringBuilder();
448             sb.Append("Radegast - ");
449
450             if (netcom.IsLoggedIn)
451             {
452                 sb.Append("[" + netcom.LoginOptions.FullName + "]");
453
454                 if (instance.State.IsAway)
455                 {
456                     sb.Append(" - Away");
457                     if (instance.State.IsBusy) sb.Append(", Busy");
458                 }
459                 else if (instance.State.IsBusy)
460                 {
461                     sb.Append(" - Busy");
462                 }
463
464                 if (instance.State.IsFollowing)
465                 {
466                     sb.Append(" - Following ");
467                     sb.Append(instance.State.FollowName);
468                 }
469             }
470             else
471             {
472                 sb.Append("Logged Out");
473             }
474
475             this.Text = sb.ToString();
476             sb = null;
477         }
478
479         private void InitializeStatusTimer()
480         {
481             statusTimer = new System.Timers.Timer(250);
482             statusTimer.SynchronizingObject = this;
483             statusTimer.Elapsed += new ElapsedEventHandler(statusTimer_Elapsed);
484         }
485
486         private void statusTimer_Elapsed(object sender, ElapsedEventArgs e)
487         {
488             // Mono sometimes fires timer after is's disposed
489             try
490             {
491                 RefreshWindowTitle();
492                 RefreshStatusBar();
493             }
494             catch { }
495         }
496         #endregion
497
498         #region Initialization, configuration, and key shortcuts
499         private void InitializeTabsConsole()
500         {
501             tabsConsole = new TabsConsole(instance);
502             tabsConsole.Dock = DockStyle.Fill;
503             toolStripContainer1.ContentPanel.Controls.Add(tabsConsole);
504         }
505
506         private void frmMain_KeyDown(object sender, KeyEventArgs e)
507         {
508             // Ctrl-W: Close tab
509             if (e.Modifiers == Keys.Control && e.KeyCode == Keys.W)
510             {
511                 e.Handled = e.SuppressKeyPress = true;
512                 RadegastTab tab = tabsConsole.SelectedTab;
513
514                 if (tab.AllowClose)
515                 {
516                     tab.Close();
517                 }
518                 else if (tab.AllowHide)
519                 {
520                     tab.Hide();
521                 }
522
523                 return;
524             }
525
526             // Ctl-Shift-H: Teleport Home
527             if (e.Modifiers == (Keys.Control | Keys.Shift) && e.KeyCode == Keys.H)
528             {
529                 e.Handled = e.SuppressKeyPress = true;
530                 tmnuTeleportHome.PerformClick();
531                 return;
532             }
533
534             // Alt-Ctrl-D Open debug console
535             if (e.Modifiers == (Keys.Control | Keys.Alt) && e.KeyCode == Keys.D)
536             {
537                 e.Handled = e.SuppressKeyPress = true;
538                 debugConsoleToolStripMenuItem.PerformClick();
539                 return;
540             }
541
542             // Alt 1-8: Toggle various tabs
543             if (e.Modifiers == Keys.Alt)
544             {
545                 switch (e.KeyCode)
546                 {
547                     case Keys.D1:
548                         e.Handled = e.SuppressKeyPress = true;
549                         tabsConsole.Tabs["chat"].Select();
550                         return;
551
552                     case Keys.D2:
553                         e.Handled = e.SuppressKeyPress = true;
554                         tbtnFriends.PerformClick();
555                         return;
556
557                     case Keys.D3:
558                         e.Handled = e.SuppressKeyPress = true;
559                         tbtnGroups.PerformClick();
560                         return;
561
562                     case Keys.D4:
563                         e.Handled = e.SuppressKeyPress = true;
564                         tbtnInventory.PerformClick();
565                         return;
566
567                     case Keys.D5:
568                         e.Handled = e.SuppressKeyPress = true;
569                         tbtnSearch.PerformClick();
570                         return;
571
572                     case Keys.D6:
573                         e.Handled = e.SuppressKeyPress = true;
574                         tbtnMap.PerformClick();
575                         return;
576
577                     case Keys.D7:
578                         e.Handled = e.SuppressKeyPress = true;
579                         tbnObjects.PerformClick();
580                         return;
581
582                     case Keys.D8:
583                         e.Handled = e.SuppressKeyPress = true;
584                         tbtnMedia.PerformClick();
585                         return;
586
587                     case Keys.D9:
588                         e.Handled = e.SuppressKeyPress = true;
589                         tbtnVoice.PerformClick();
590                         return;
591                 }
592             }
593
594             // ctrl-g, goto slurl
595             if (e.Control && e.KeyCode == Keys.G)
596             {
597                 if (!ProcessLink(Clipboard.GetText(), true))
598                     MapToCurrentLocation();
599
600                 e.Handled = e.SuppressKeyPress = true;
601                 return;
602             }
603
604             // ctrl-(shift)-tab for next/previous tab
605             if (e.Control && e.KeyCode == Keys.Tab)
606             {
607                 if (e.Shift)
608                 {
609                     TabConsole.SelectPreviousTab();
610                 }
611                 else
612                 {
613                     TabConsole.SelectNextTab();
614                 }
615                 e.Handled = e.SuppressKeyPress = true;
616                 return;
617             }
618         }
619
620         bool firstLoad = true;
621
622         private void frmMain_Load(object sender, EventArgs e)
623         {
624             if (firstLoad)
625             {
626                 firstLoad = false;
627                 tabsConsole.SelectTab("login");
628                 ResourceManager rm = Properties.Resources.ResourceManager;
629                 ResourceSet set = rm.GetResourceSet(System.Globalization.CultureInfo.CurrentCulture, true, true);
630                 System.Collections.IDictionaryEnumerator de = set.GetEnumerator();
631                 while (de.MoveNext() == true)
632                 {
633                     if (de.Entry.Value is Image)
634                     {
635                         Bitmap bitMap = de.Entry.Value as Bitmap;
636                         ResourceImages.Images.Add(bitMap);
637                         ImageNames.Add(de.Entry.Key.ToString());
638                     }
639                 }
640                 StartUpdateCheck(false);
641             }
642         }
643         #endregion
644
645         #region Public methods
646
647         private Dictionary<UUID, frmProfile> shownProfiles = new Dictionary<UUID, frmProfile>();
648
649         public void ShowAgentProfile(string name, UUID agentID)
650         {
651             lock (shownProfiles)
652             {
653                 frmProfile profile = null;
654                 if (shownProfiles.TryGetValue(agentID, out profile))
655                 {
656                     profile.WindowState = FormWindowState.Normal;
657                     profile.Focus();
658                 }
659                 else
660                 {
661                     profile = new frmProfile(instance, name, agentID);
662
663                     profile.Disposed += (object sender, EventArgs e) =>
664                         {
665                             lock (shownProfiles)
666                             {
667                                 frmProfile agentProfile = (frmProfile)sender;
668                                 if (shownProfiles.ContainsKey(agentProfile.AgentID))
669                                     shownProfiles.Remove(agentProfile.AgentID);
670                             }
671                         };
672
673                     profile.Show();
674                     profile.Focus();
675                     shownProfiles.Add(agentID, profile);
676                 }
677             }
678         }
679
680         private Dictionary<UUID, frmGroupInfo> shownGroupProfiles = new Dictionary<UUID, frmGroupInfo>();
681
682         public void ShowGroupProfile(AvatarGroup group)
683         {
684             ShowGroupProfile(new OpenMetaverse.Group()
685             {
686                 ID = group.GroupID,
687                 InsigniaID = group.GroupInsigniaID,
688                 Name = group.GroupName
689             }
690             );
691         }
692
693         public void ShowGroupProfile(OpenMetaverse.Group group)
694         {
695             lock (shownGroupProfiles)
696             {
697                 frmGroupInfo profile = null;
698                 if (shownGroupProfiles.TryGetValue(group.ID, out profile))
699                 {
700                     profile.WindowState = FormWindowState.Normal;
701                     profile.Focus();
702                 }
703                 else
704                 {
705                     profile = new frmGroupInfo(instance, group);
706
707                     profile.Disposed += (object sender, EventArgs e) =>
708                         {
709                             lock (shownGroupProfiles)
710                             {
711                                 frmGroupInfo groupProfile = (frmGroupInfo)sender;
712                                 if (shownGroupProfiles.ContainsKey(groupProfile.Group.ID))
713                                     shownGroupProfiles.Remove(groupProfile.Group.ID);
714                             }
715                         };
716
717                     profile.Show();
718                     profile.Focus();
719                     shownGroupProfiles.Add(group.ID, profile);
720                 }
721             }
722         }
723
724         public void ProcessLink(string link)
725         {
726             ProcessLink(link, false);
727         }
728
729         public bool ProcessLink(string link, bool onlyMap)
730         {
731             if (!link.Contains("://"))
732             {
733                 link = "http://" + link;
734             }
735
736             Regex r = new Regex(@"^(http://(slurl\.com|maps\.secondlife\.com)/secondlife/|secondlife://)(?<region>[^/]+)/(?<x>\d+)/(?<y>\d+)(/(?<z>\d+))?",
737                 RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase
738                 );
739             Match m = r.Match(link);
740
741             if (m.Success)
742             {
743                 string region = HttpUtility.UrlDecode(m.Groups["region"].Value);
744                 int x = int.Parse(m.Groups["x"].Value);
745                 int y = int.Parse(m.Groups["y"].Value);
746                 int z = 0;
747
748                 if (!string.IsNullOrEmpty(m.Groups["z"].Value))
749                 {
750                     z = int.Parse(m.Groups["z"].Value);
751                 }
752
753                 MapTab.Select();
754                 WorldMap.DisplayLocation(region, x, y, z);
755                 return true;
756             }
757             else if (!onlyMap)
758             {
759                 System.Diagnostics.Process.Start(link);
760             }
761             return false;
762         }
763         #endregion
764
765         #region Notifications
766         CircularList<Control> notifications = new CircularList<Control>();
767
768         public Color NotificationBackground
769         {
770             get { return pnlDialog.BackColor; }
771         }
772
773         void ResizeNotificationByControl(Control active)
774         {
775             int Width = active.Size.Width + 6;
776             int Height = notifications.Count > 1 ? active.Size.Height + 3 + btnDialogNextControl.Size.Height : active.Size.Height + 3;
777             pnlDialog.Size = new Size(Width, Height);
778             pnlDialog.Top = 0;
779             pnlDialog.Left = pnlDialog.Parent.ClientSize.Width - Width;
780
781             btnDialogNextControl.Top = btnDialogNextControl.Parent.ClientSize.Height - btnDialogNextControl.Size.Height;
782             btnDialogNextControl.Left = btnDialogNextControl.Parent.ClientSize.Width - btnDialogNextControl.Size.Width;
783
784             btnDialogNextControl.BringToFront();
785         }
786
787         public void AddNotification(Control control)
788         {
789             if (InvokeRequired)
790             {
791                 BeginInvoke(new MethodInvoker(delegate()
792                 {
793                     AddNotification(control);
794                 }
795                 ));
796                 return;
797             }
798
799             FormFlash.StartFlash(this);
800             pnlDialog.Visible = true;
801             pnlDialog.BringToFront();
802
803             foreach (Control existing in notifications)
804             {
805                 existing.Visible = false;
806             }
807
808             notifications.Add(control);
809             control.Visible = true;
810             control.Anchor = AnchorStyles.Top | AnchorStyles.Left;
811             control.Top = 3;
812             control.Left = 3;
813             pnlDialog.Controls.Add(control);
814             ResizeNotificationByControl(control);
815
816             btnDialogNextControl.Visible = notifications.Count > 1;
817         }
818
819         public void RemoveNotification(Control control)
820         {
821             pnlDialog.Controls.Remove(control);
822             notifications.Remove(control);
823             control.Dispose();
824
825             if (notifications.HasNext)
826             {
827                 pnlDialog.Visible = true;
828                 Control active = notifications.Next;
829                 active.Visible = true;
830                 ResizeNotificationByControl(active);
831             }
832             else
833             {
834                 pnlDialog.Visible = false;
835             }
836
837             btnDialogNextControl.Visible = notifications.Count > 1;
838         }
839
840         private void btnDialogNextControl_Click(object sender, EventArgs e)
841         {
842             foreach (Control existing in notifications)
843             {
844                 existing.Visible = false;
845             }
846
847             if (notifications.HasNext)
848             {
849                 pnlDialog.Visible = true;
850                 Control active = notifications.Next;
851                 active.Visible = true;
852                 ResizeNotificationByControl(active);
853             }
854             else
855             {
856                 pnlDialog.Visible = false;
857             }
858
859         }
860         #endregion Notifications
861
862         #region Menu click handlers
863
864         private void tmnuStatusAway_Click(object sender, EventArgs e)
865         {
866             instance.State.SetAway(tmnuStatusAway.Checked);
867         }
868
869         private void tmnuHelpReadme_Click(object sender, EventArgs e)
870         {
871             System.Diagnostics.Process.Start(Application.StartupPath + @"\Readme.txt");
872         }
873
874         private void tmnuStatusBusy_Click(object sender, EventArgs e)
875         {
876             instance.State.SetBusy(tmnuStatusBusy.Checked);
877         }
878
879         private void tmnuControlFly_Click(object sender, EventArgs e)
880         {
881             instance.State.SetFlying(tmnuControlFly.Checked);
882         }
883
884         private void tmnuControlAlwaysRun_Click(object sender, EventArgs e)
885         {
886             instance.State.SetAlwaysRun(tmnuControlAlwaysRun.Checked);
887         }
888
889         private void tmnuPrefs_Click(object sender, EventArgs e)
890         {
891             (new frmSettings(instance)).ShowDialog();
892         }
893
894         private void tbtnAppearance_Click(object sender, EventArgs e)
895         {
896             client.Appearance.RequestSetAppearance(false);
897         }
898
899         private void importObjectToolStripMenuItem_Click(object sender, EventArgs e)
900         {
901             PrimDeserializer.ImportFromFile(client);
902         }
903
904         private void autopilotToolStripMenuItem_Click(object sender, EventArgs e)
905         {
906             if (ap == null)
907             {
908                 ap = new AutoPilot(client);
909                 /*
910                 ap.InsertWaypoint(new Vector3(66, 163, 21));
911                 ap.InsertWaypoint(new Vector3(66, 98, 21));
912
913                 ap.InsertWaypoint(new Vector3(101, 98, 21));
914                 ap.InsertWaypoint(new Vector3(101, 45, 21));
915                 ap.InsertWaypoint(new Vector3(93, 27, 21));
916                 ap.InsertWaypoint(new Vector3(106, 12, 21));
917                 ap.InsertWaypoint(new Vector3(123, 24, 21));
918                 ap.InsertWaypoint(new Vector3(114, 45, 21));
919                 ap.InsertWaypoint(new Vector3(114, 98, 21));
920
921                 ap.InsertWaypoint(new Vector3(130, 98, 21));
922                 ap.InsertWaypoint(new Vector3(130, 163, 21));
923                  **/
924                 ap.InsertWaypoint(new Vector3(64, 68, 21));
925                 ap.InsertWaypoint(new Vector3(65, 20, 21));
926                 ap.InsertWaypoint(new Vector3(33, 23, 21));
927                 ap.InsertWaypoint(new Vector3(17, 39, 21));
928                 ap.InsertWaypoint(new Vector3(17, 62, 21));
929
930
931             }
932             if (AutoPilotActive)
933             {
934                 AutoPilotActive = false;
935                 ap.Stop();
936             }
937             else
938             {
939                 AutoPilotActive = true;
940                 ap.Start();
941             }
942
943         }
944
945         private void cleanCacheToolStripMenuItem_Click(object sender, EventArgs e)
946         {
947             client.Assets.Cache.Clear();
948         }
949
950         private void rebakeTexturesToolStripMenuItem_Click(object sender, EventArgs e)
951         {
952             client.Appearance.RequestSetAppearance(true);
953         }
954
955         public void MapToCurrentLocation()
956         {
957             if (MapTab != null && client.Network.Connected)
958             {
959                 MapTab.Select();
960                 WorldMap.DisplayLocation(client.Network.CurrentSim.Name,
961                     (int)client.Self.SimPosition.X,
962                     (int)client.Self.SimPosition.Y,
963                     (int)client.Self.SimPosition.Z);
964             }
965         }
966
967         private void standToolStripMenuItem_Click(object sender, EventArgs e)
968         {
969             instance.State.SetSitting(false, UUID.Zero);
970         }
971
972         private void groundSitToolStripMenuItem_Click(object sender, EventArgs e)
973         {
974             client.Self.SitOnGround();
975         }
976
977         private void newWindowToolStripMenuItem_Click(object sender, EventArgs e)
978         {
979             try { System.Diagnostics.Process.Start(Application.ExecutablePath); }
980             catch (Exception) { }
981         }
982
983         private void tmnuExit_Click(object sender, EventArgs e)
984         {
985             Close();
986         }
987
988         private void tlblRegionInfo_Click(object sender, EventArgs e)
989         {
990             if (WorldMap != null && client.Network.Connected)
991             {
992                 MapTab.Select();
993             }
994         }
995
996         private void scriptEditorToolStripMenuItem_Click(object sender, EventArgs e)
997         {
998             ScriptEditor se = new ScriptEditor(instance);
999             se.Dock = DockStyle.Fill;
1000             se.ShowDetached();
1001         }
1002
1003         private void tmnuSetHome_Click(object sender, EventArgs e)
1004         {
1005             client.Self.SetHome();
1006         }
1007
1008         private void tmnuCreateLandmark_Click(object sender, EventArgs e)
1009         {
1010             string location = string.Format(", {0} ({1}, {2}, {3})",
1011                 client.Network.CurrentSim.Name,
1012                 (int)client.Self.SimPosition.X,
1013                 (int)client.Self.SimPosition.Y,
1014                 (int)client.Self.SimPosition.Z
1015                 );
1016
1017             string name = tlblParcel.Text;
1018             int maxLen = 63 - location.Length;
1019
1020             if (name.Length > maxLen)
1021                 name = name.Substring(0, maxLen);
1022
1023             name += location;
1024
1025             client.Inventory.RequestCreateItem(
1026                 client.Inventory.FindFolderForType(AssetType.Landmark),
1027                 name,
1028                 tlblParcel.ToolTipText,
1029                 AssetType.Landmark,
1030                 UUID.Random(),
1031                 InventoryType.Landmark,
1032                 PermissionMask.All,
1033                 (bool success, InventoryItem item) =>
1034                 {
1035                     if (success)
1036                     {
1037                         BeginInvoke(new MethodInvoker(() =>
1038                             {
1039                                 Landmark ln = new Landmark(instance, (InventoryLandmark)item);
1040                                 ln.Dock = DockStyle.Fill;
1041                                 ln.Detached = true;
1042                             }));
1043                     }
1044                 }
1045             );
1046         }
1047
1048
1049         private void timerWorldClock_Tick(object sender, EventArgs e)
1050         {
1051             lblTime.Text = instance.GetWorldTime().ToString("h:mm tt", System.Globalization.CultureInfo.InvariantCulture);
1052         }
1053
1054         private void reportBugsToolStripMenuItem_Click(object sender, EventArgs e)
1055         {
1056             ProcessLink("http://jira.openmetaverse.org/browse/RAD");
1057         }
1058
1059         private void aboutRadegastToolStripMenuItem_Click(object sender, EventArgs e)
1060         {
1061             (new frmAbout(instance)).ShowDialog();
1062         }
1063
1064         #region Update Checking
1065         private UpdateChecker updateChecker = null;
1066         private bool ManualUpdateCheck = false;
1067
1068         public void StartUpdateCheck(bool userInitiated)
1069         {
1070             ManualUpdateCheck = userInitiated;
1071
1072             if (updateChecker != null)
1073             {
1074                 if (ManualUpdateCheck)
1075                     tabsConsole.DisplayNotificationInChat("Update check already in progress.");
1076                 return;
1077             }
1078
1079             if (ManualUpdateCheck)
1080                 tabsConsole.DisplayNotificationInChat("Checking for updates...", ChatBufferTextStyle.StatusBlue);
1081             updateChecker = new UpdateChecker();
1082             updateChecker.OnUpdateInfoReceived += new UpdateChecker.UpdateInfoCallback(OnUpdateInfoReceived);
1083             updateChecker.StartCheck();
1084         }
1085
1086         private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
1087         {
1088             tabsConsole.SelectTab("chat");
1089             StartUpdateCheck(true);
1090         }
1091
1092         void OnUpdateInfoReceived(object sender, UpdateCheckerArgs e)
1093         {
1094             if (InvokeRequired)
1095             {
1096                 BeginInvoke(new MethodInvoker(() => OnUpdateInfoReceived(sender, e)));
1097                 return;
1098             }
1099
1100             if (!e.Success)
1101             {
1102                 if (ManualUpdateCheck)
1103                     tabsConsole.DisplayNotificationInChat("Error: Failed connecting to the update site.", ChatBufferTextStyle.StatusBlue);
1104             }
1105             else
1106             {
1107                 if (!ManualUpdateCheck && e.Info.DisplayMOTD)
1108                 {
1109                     tabsConsole.DisplayNotificationInChat(e.Info.MOTD, ChatBufferTextStyle.StatusBlue);
1110                 }
1111
1112                 if (e.Info.UpdateAvailable)
1113                 {
1114                     tabsConsole.DisplayNotificationInChat("New version available at " + e.Info.DownloadSite, ChatBufferTextStyle.Alert);
1115                 }
1116                 else
1117                 {
1118                     if (ManualUpdateCheck)
1119                         tabsConsole.DisplayNotificationInChat("Your version is up to date.", ChatBufferTextStyle.StatusBlue);
1120                 }
1121             }
1122
1123             updateChecker.Dispose();
1124             updateChecker = null;
1125         }
1126         #endregion
1127
1128         private void ToggleHidden(string tabName)
1129         {
1130             if (!tabsConsole.TabExists(tabName)) return;
1131
1132             RadegastTab tab = tabsConsole.Tabs[tabName];
1133
1134             if (tab.Hidden)
1135             {
1136                 tab.Show();
1137             }
1138             else
1139             {
1140                 if (!tab.Selected)
1141                 {
1142                     tab.Select();
1143                 }
1144                 else
1145                 {
1146                     tab.Hide();
1147                 }
1148             }
1149         }
1150
1151         private void tbtnFriends_Click(object sender, EventArgs e)
1152         {
1153             ToggleHidden("friends");
1154         }
1155
1156         private void tbtnInventory_Click(object sender, EventArgs e)
1157         {
1158             ToggleHidden("inventory");
1159         }
1160
1161         private void tbtnSearch_Click(object sender, EventArgs e)
1162         {
1163             ToggleHidden("search");
1164         }
1165
1166         private void tbtnGroups_Click(object sender, EventArgs e)
1167         {
1168             ToggleHidden("groups");
1169         }
1170
1171         private void tbtnVoice_Click(object sender, EventArgs e)
1172         {
1173             ToggleHidden("voice");
1174         }
1175
1176         private void tbtnMedia_Click(object sender, EventArgs e)
1177         {
1178             if (tabsConsole.TabExists("media"))
1179             {
1180                 ToggleHidden("media");
1181             }
1182             else
1183             {
1184                 RadegastTab tab = tabsConsole.AddTab("media", "Media", mediaConsole);
1185                 tab.AllowClose = false;
1186                 tab.AllowHide = true;
1187                 tab.Select();
1188             }
1189         }
1190
1191         private void debugConsoleToolStripMenuItem_Click(object sender, EventArgs e)
1192         {
1193             if (tabsConsole.TabExists("debug"))
1194             {
1195                 ToggleHidden("debug");
1196             }
1197             else
1198             {
1199                 RadegastTab tab = tabsConsole.AddTab("debug", "Debug", new DebugConsole(instance));
1200                 tab.AllowClose = false;
1201                 tab.AllowHide = true;
1202                 tab.Select();
1203             }
1204         }
1205
1206         private void tbnObjects_Click(object sender, EventArgs e)
1207         {
1208             if (tabsConsole.TabExists("objects"))
1209             {
1210                 RadegastTab tab = tabsConsole.Tabs["objects"];
1211                 if (!tab.Selected)
1212                 {
1213                     tab.Select();
1214                     ((ObjectsConsole)tab.Control).RefreshObjectList();
1215                 }
1216                 else
1217                 {
1218                     tab.Close();
1219                 }
1220             }
1221             else
1222             {
1223                 RadegastTab tab = tabsConsole.AddTab("objects", "Objects", new ObjectsConsole(instance));
1224                 tab.AllowClose = true;
1225                 tab.AllowDetach = true;
1226                 tab.Visible = true;
1227                 tab.AllowHide = false;
1228                 tab.Select();
1229                 ((ObjectsConsole)tab.Control).RefreshObjectList();
1230             }
1231         }
1232
1233         private void tbtnMap_Click(object sender, EventArgs e)
1234         {
1235             ToggleHidden("map");
1236             if (!MapTab.Hidden)
1237                 MapToCurrentLocation();
1238         }
1239
1240         private void disconnectToolStripMenuItem_Click(object sender, EventArgs e)
1241         {
1242             if (client.Network.Connected)
1243                 client.Network.RequestLogout();
1244         }
1245
1246         private void reconnectToolStripMenuItem_Click(object sender, EventArgs e)
1247         {
1248             instance.Reconnect();
1249         }
1250
1251         private frmKeyboardShortcuts keyboardShortcutsForm = null;
1252         private void keyboardShortcutsToolStripMenuItem_Click(object sender, EventArgs e)
1253         {
1254             if (keyboardShortcutsForm != null)
1255             {
1256                 keyboardShortcutsForm.Focus();
1257             }
1258             else
1259             {
1260                 keyboardShortcutsForm = new frmKeyboardShortcuts(instance);
1261
1262                 keyboardShortcutsForm.Disposed += (object senderx, EventArgs ex) =>
1263                     {
1264                         if (components != null)
1265                         {
1266                             components.Remove(keyboardShortcutsForm);
1267                         }
1268                         keyboardShortcutsForm = null;
1269                     };
1270
1271                 keyboardShortcutsForm.Show(this);
1272                 keyboardShortcutsForm.Top = Top + 100;
1273                 keyboardShortcutsForm.Left = Left + 100;
1274
1275                 if (components != null)
1276                 {
1277                     components.Add(keyboardShortcutsForm);
1278                 }
1279             }
1280         }
1281
1282         int nr_reg = 0;
1283         int nr_agent = 0;
1284
1285         // Menu item for testing out stuff
1286         private void testToolStripMenuItem_Click(object sender, EventArgs e)
1287         {
1288             if (nr_reg > 0)
1289             {
1290                 Logger.Log("Number of regions: " + nr_reg.ToString() + " agents: " + nr_agent.ToString(), Helpers.LogLevel.Info);
1291                 nr_reg = 0;
1292                 nr_agent = 0;
1293                 client.Grid.GridRegion -= new EventHandler<GridRegionEventArgs>(Grid_GridRegion);
1294                 return;
1295             }
1296
1297             client.Grid.GridRegion += new EventHandler<GridRegionEventArgs>(Grid_GridRegion);
1298             client.Grid.RequestMainlandSims(GridLayerType.Objects);
1299         }
1300
1301         void Grid_GridRegion(object sender, GridRegionEventArgs e)
1302         {
1303             nr_reg++;
1304             if ((nr_reg % 100) == 0)
1305             {
1306                 nr_agent += e.Region.Agents;
1307                 Logger.Log("Number of regions: " + nr_reg.ToString() + " agents: " + nr_agent.ToString(), Helpers.LogLevel.Info);
1308             }
1309         }
1310
1311         private void reloadInventoryToolStripMenuItem_Click(object sender, EventArgs e)
1312         {
1313             if (tabsConsole.TabExists("inventory"))
1314             {
1315                 ((InventoryConsole)tabsConsole.Tabs["inventory"].Control).ReloadInventory();
1316                 tabsConsole.Tabs["inventory"].Select();
1317             }
1318         }
1319
1320         private void btnLoadScript_Click(object sender, EventArgs e)
1321         {
1322             if (!TabConsole.TabExists("plugin_manager"))
1323             {
1324                 TabConsole.AddTab("plugin_manager", "Plugins", new PluginsTab(instance));
1325             }
1326             TabConsole.Tabs["plugin_manager"].Select();
1327         }
1328
1329         private void frmMain_Resize(object sender, EventArgs e)
1330         {
1331             if (WindowState == FormWindowState.Minimized && instance.GlobalSettings["minimize_to_tray"].AsBoolean())
1332             {
1333                 ShowInTaskbar = false;
1334                 trayIcon.Visible = true;
1335             }
1336         }
1337
1338         private void treyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
1339         {
1340             WindowState = FormWindowState.Normal;
1341             ShowInTaskbar = true;
1342             trayIcon.Visible = false;
1343         }
1344
1345         private void ctxTreyRestore_Click(object sender, EventArgs e)
1346         {
1347             treyIcon_MouseDoubleClick(this, null);
1348         }
1349
1350         private void ctxTreyExit_Click(object sender, EventArgs e)
1351         {
1352             tmnuExit_Click(this, EventArgs.Empty);
1353         }
1354
1355         private void tmnuTeleportHome_Click(object sender, EventArgs e)
1356         {
1357             TabConsole.DisplayNotificationInChat("Teleporting home...");
1358             client.Self.RequestTeleport(UUID.Zero);
1359         }
1360  
1361         private void stopAllAnimationsToolStripMenuItem_Click(object sender, EventArgs e)
1362         {
1363             instance.State.StopAllAnimations();
1364         }
1365
1366         private void restartRegionToolStripMenuItem_Click(object sender, EventArgs e)
1367         {
1368             if (MessageBox.Show(new WindowWrapper(Handle),
1369                 "Do you want to restart region " + client.Network.CurrentSim.Name + "?",
1370                 "Confirm restart", MessageBoxButtons.YesNo) == DialogResult.Yes)
1371             {
1372                 client.Estate.RestartRegion();
1373             }
1374         }
1375
1376         private void regionInfoToolStripMenuItem_Click(object sender, EventArgs e)
1377         {
1378             if (tabsConsole.TabExists("current region info"))
1379             {
1380                 tabsConsole.Tabs["current region info"].Select();
1381             }
1382             else
1383             {
1384                 tabsConsole.AddTab("current region info", "Region info", new RegionInfo(instance));
1385                 tabsConsole.Tabs["current region info"].Select();
1386             }
1387         }
1388         #endregion
1389     }
1390 }