OSDN Git Service

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