OSDN Git Service

bda23bc2e9e518557b5548b55266da018e1af3ff
[radegast/radegast.git] / plugins / Radegast.Plugin.SimpleBuilder / SimpleBuilder.cs
1 #region Copyright
2 // 
3 // Radegast SimpleBuilder plugin extension
4 //
5 // Copyright (c) 2014, Ano Nymous <anonymously@hotmail.de> | SecondLife-IM: anno1986 Resident
6 // All rights reserved.
7 // 
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are met:
10 // 
11 //     * Redistributions of source code must retain the above copyright notice,
12 //       this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above copyright
14 //       notice, this list of conditions and the following disclaimer in the
15 //       documentation and/or other materials provided with the distribution.
16 //     * Neither the name of the application "Radegast", nor the names of its
17 //       contributors may be used to endorse or promote products derived from
18 //       this software without specific prior written permission.
19 // 
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 //
31 // $Id$
32 //
33 #endregion
34
35 #region Usings
36 using System;
37 using System.Collections.Generic;
38 using System.ComponentModel;
39 using System.Drawing;
40 using System.Data;
41 using System.Linq;
42 using System.Text;
43 using System.Windows.Forms;
44 using Radegast;
45 using OpenMetaverse;
46 #endregion
47
48 namespace SimpleBuilderNamespace
49 {
50     /// <summary>
51     /// Example implementation of a control that can be used
52     /// as Radegast tab and loeaded as a plugin
53     /// </summary>
54     [Radegast.Plugin(Name = "SimpleBuilder Plugin", Description = "Allows you to build some basic prims, like boxes, cylinder, tubes, ... (requires permission!)", Version = "1.0")]
55     public partial class SimpleBuilder : RadegastTabControl, IRadegastPlugin
56     {
57         private string pluginName = "SimpleBuilder";
58         // Methods needed for proper registration of a GUI tab
59         #region Template for GUI radegast tab
60         /// <summary>String for internal identification of the tab (change this!)</summary>
61         static string tabID = "simplebuilder_tab";
62         /// <summary>Text displayed in the plugins menu and the tab label (change this!)</summary>
63         static string tabLabel = "Build Prims";
64
65         /// <summary>Menu item that gets added to the Plugins menu</summary>
66         ToolStripMenuItem ActivateTabButton;
67
68         public List<Primitive> Prims = new List<Primitive>();
69         PropertiesQueue propRequester;
70
71         private Primitive m_selectedPrim;
72         public Primitive selectedPrim {
73             get
74             {
75                 return m_selectedPrim;
76             }
77             set
78             {
79                 if(value == null)
80                     btnSave.Enabled = false;
81                 else
82                     btnSave.Enabled = true;
83
84                 m_selectedPrim = value;
85             }
86         }
87
88         /// <summary>Default constructor. Never used. Needed for VS designer</summary>
89         public SimpleBuilder()
90         {
91         }
92
93         /// <summary>
94         /// Main constructor used when actually creating the tab control for display
95         /// Register client and instance events
96         /// </summary>
97         /// <param name="instance">RadegastInstance</param>
98         /// <param name="unused">This param is not used, but needs to be there to keep the constructor signature</param>
99         public SimpleBuilder(RadegastInstance instance, bool unused)
100             : base(instance)
101         {
102             InitializeComponent();
103             Disposed += new EventHandler(DemoTab_Disposed);
104             instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
105             RegisterClientEvents(client);
106
107             propRequester = new PropertiesQueue(instance);
108             propRequester.OnTick += new PropertiesQueue.TickCallback(propRequester_OnTick);
109
110             selectedPrim = null;
111         }
112
113         /// <summary>
114         /// Cleanup after the tab is closed
115         /// Unregister event handler hooks we have installed
116         /// </summary>
117         /// <param name="sender"></param>
118         /// <param name="e"></param>
119         void DemoTab_Disposed(object sender, EventArgs e)
120         {
121             UnregisterClientEvents(client);
122             instance.ClientChanged -= new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
123         }
124
125         /// <summary>
126         /// Plugin loader calls this at the time plugin gets created
127         /// We add a button to the Plugins menu on the main window
128         /// for this tab
129         /// </summary>
130         /// <param name="inst">Main RadegastInstance</param>
131         public void StartPlugin(RadegastInstance inst)
132         {
133             this.instance = inst;
134
135             ActivateTabButton = new ToolStripMenuItem(tabLabel, null, MenuButtonClicked);
136             instance.MainForm.PluginsMenu.DropDownItems.Add(ActivateTabButton);
137
138         }
139
140         /// <summary>
141         /// Called when the plugin manager unloads our plugin. 
142         /// Close the tab if it's active and remove the menu button
143         /// </summary>
144         /// <param name="inst"></param>
145         public void StopPlugin(RadegastInstance inst)
146         {
147             ActivateTabButton.Dispose();
148             if (instance.TabConsole.Tabs.ContainsKey(tabID))
149             {
150                 instance.TabConsole.Tabs[tabID].Close();
151             }
152
153             propRequester.OnTick -= propRequester_OnTick;
154         }
155
156         void propRequester_OnTick(int remaining)
157         {
158             if (InvokeRequired)
159             {
160                 BeginInvoke(new MethodInvoker(delegate()
161                 {
162                     propRequester_OnTick(remaining);
163                 }
164                 ));
165                 return;
166             }
167
168             StringBuilder sb = new StringBuilder();
169             sb.AppendFormat("Tracking {0} objects", Prims.Count);
170
171             if (remaining > 10)
172             {
173                 sb.AppendFormat(", fetching {0} object names.", remaining);
174             }
175             else
176             {
177                 sb.Append(".");
178             }
179
180             lock (Prims)
181             {
182                 if(lstPrims != null)
183                     lstPrims.VirtualListSize = Prims.Count;
184             }
185             if(lstPrims != null)
186                 lstPrims.Invalidate();
187         }
188
189         private bool IncludePrim(Primitive prim)
190         {
191             if (prim.ParentID == 0 && (prim.Flags & PrimFlags.ObjectYouOwner) == PrimFlags.ObjectYouOwner)
192             {
193                 return true;
194             }
195             else return false;
196         }
197
198         private string GetObjectName(Primitive prim, int distance)
199         {
200             string name = "Loading...";
201             string ownerName = "Loading...";
202
203             if (prim.Properties != null)
204             {
205                 name = prim.Properties.Name;
206                 // prim.Properties.GroupID is the actual group when group owned, not prim.GroupID
207                 if (UUID.Zero == prim.Properties.OwnerID &&
208                     PrimFlags.ObjectGroupOwned == (prim.Flags & PrimFlags.ObjectGroupOwned) &&
209                     UUID.Zero != prim.Properties.GroupID)
210                 {
211                     System.Threading.AutoResetEvent nameReceivedSignal = new System.Threading.AutoResetEvent(false);
212                     EventHandler<GroupNamesEventArgs> cbGroupName = new EventHandler<GroupNamesEventArgs>(
213                         delegate(object sender, GroupNamesEventArgs e)
214                         {
215                             if (e.GroupNames.ContainsKey(prim.Properties.GroupID))
216                             {
217                                 e.GroupNames.TryGetValue(prim.Properties.GroupID, out ownerName);
218                                 if (string.IsNullOrEmpty(ownerName))
219                                     ownerName = "Loading...";
220                                 if (null != nameReceivedSignal)
221                                     nameReceivedSignal.Set();
222                             }
223                         });
224                     client.Groups.GroupNamesReply += cbGroupName;
225                     client.Groups.RequestGroupName(prim.Properties.GroupID);
226                     nameReceivedSignal.WaitOne(5000, false);
227                     nameReceivedSignal.Close();
228                     client.Groups.GroupNamesReply -= cbGroupName;
229                 }
230                 else
231                     ownerName = instance.Names.Get(prim.Properties.OwnerID);
232             }
233
234             if (prim.ParentID == client.Self.LocalID)
235             {
236                 return string.Format("{0} attached to {1}", name, prim.PrimData.AttachmentPoint.ToString());
237             }
238             else if (ownerName != "Loading...")
239             {
240                 return String.Format("{0} ({1}m) owned by {2}", name, distance, ownerName);
241             }
242             else
243             {
244                 return String.Format("{0} ({1}m)", name, distance);
245             }
246
247         }
248
249         private string GetObjectName(Primitive prim)
250         {
251             int distance = (int)Vector3.Distance(client.Self.SimPosition, prim.Position);
252             if (prim.ParentID == client.Self.LocalID) distance = 0;
253             return GetObjectName(prim, distance);
254         }
255
256         private void lstPrims_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
257         {
258             Primitive prim = null;
259             try
260             {
261                 lock (Prims)
262                 {
263                     prim = Prims[e.ItemIndex];
264                 }
265             }
266             catch
267             {
268                 e.Item = new ListViewItem();
269                 return;
270             }
271
272             string name = GetObjectName(prim);
273             var item = new ListViewItem(name);
274             item.Tag = prim;
275             item.Name = prim.ID.ToString();
276             e.Item = item;
277         }
278
279         /// <summary>
280         /// Hadle case when GridClient is changed (relog haa occured without
281         /// quiting Radegast). We need to unregister events from the old client
282         /// and re-register them with the new
283         /// </summary>
284         /// <param name="sender"></param>
285         /// <param name="e"></param>
286         void instance_ClientChanged(object sender, ClientChangedEventArgs e)
287         {
288             UnregisterClientEvents(e.OldClient);
289             RegisterClientEvents(e.Client);
290         }
291
292         /// <summary>
293         /// Registration of all GridClient (libomv) events go here
294         /// </summary>
295         /// <param name="client"></param>
296         void RegisterClientEvents(GridClient client)
297         {
298             client.Self.ChatFromSimulator += new EventHandler<ChatEventArgs>(Self_ChatFromSimulator);
299         }
300
301         /// <summary>
302         /// Unregistration of GridClient (libomv) events.
303         /// Important that this be symetric to RegisterClientEvents() calls
304         /// </summary>
305         /// <param name="client"></param>
306         void UnregisterClientEvents(GridClient client)
307         {
308             if (client == null) return;
309             client.Self.ChatFromSimulator -= new EventHandler<ChatEventArgs>(Self_ChatFromSimulator);
310         }
311
312         /// <summary>
313         /// Handling the click on Plugins -> Demo Tab button
314         /// Check if we already have a tab. If we do make it active tab.
315         /// If not, create a new tab and make it active.
316         /// </summary>
317         /// <param name="sender"></param>
318         /// <param name="e"></param>
319         void MenuButtonClicked(object sender, EventArgs e)
320         {
321             if (instance.TabConsole.TabExists(tabID))
322             {
323                 instance.TabConsole.Tabs[tabID].Select();
324             }
325             else
326             {
327                 instance.TabConsole.AddTab(tabID, tabLabel, new SimpleBuilder(instance, true));
328                 instance.TabConsole.Tabs[tabID].Select();
329             }
330         }
331         #endregion Template for GUI radegast tab
332
333         #region Implementation of the custom tab functionality
334         void Self_ChatFromSimulator(object sender, ChatEventArgs e)
335         {
336             // Boilerplate, make sure to be on the GUI thread
337             if (InvokeRequired)
338             {
339                 BeginInvoke(new MethodInvoker(() => Self_ChatFromSimulator(sender, e)));
340                 return;
341             }
342
343             //txtChat.Text = e.Message;
344         }
345
346         private void btnBuild_Click(object sender, EventArgs e)
347         {
348             Button btn = sender as Button;
349
350             if (btn == null) return;
351
352             PrimType primType = (PrimType)Enum.Parse(typeof(PrimType), btn.Text);
353
354             this.BuildAndRez(primType);
355         }
356
357         private void BuildAndRez(PrimType primType)
358         {
359             float size, distance;
360             if (!float.TryParse(tbox_Size.Text,  System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out size))
361             {
362                 instance.MainForm.TabConsole.DisplayNotificationInChat(pluginName + ": Invalid size", ChatBufferTextStyle.Error);
363                 return;
364             }
365
366             if (!float.TryParse(tbox_Distance.Text, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out distance))
367             {
368                 instance.MainForm.TabConsole.DisplayNotificationInChat(pluginName + ": Invalid distance", ChatBufferTextStyle.Error);
369                 return;
370             }
371
372             Primitive.ConstructionData primData = ObjectManager.BuildBasicShape(primType);
373
374             Vector3 rezpos = new Vector3(distance, 0, 0);
375             rezpos = client.Self.SimPosition + rezpos * client.Self.Movement.BodyRotation;
376
377             client.Objects.AddPrim(client.Network.CurrentSim, primData, UUID.Zero, rezpos, new Vector3(size), Quaternion.Identity);
378
379             instance.MainForm.TabConsole.DisplayNotificationInChat(pluginName + ": Object built and rezzed", ChatBufferTextStyle.Normal);
380         }
381
382         #endregion Implementation of the custom tab functionality
383
384         private void lstPrims_SelectedIndexChanged(object sender, EventArgs e)
385         {
386             if (lstPrims.SelectedIndices.Count > 0)
387             {
388                 selectedPrim = Prims[lstPrims.SelectedIndices[0]];
389                 getScaleFromSelection();
390                 getRotFromSelection();
391             }
392             else
393             {
394                 selectedPrim = null;
395             }
396         }
397
398         private void getScaleFromSelection(){
399             if (selectedPrim == null) return;
400             if (selectedPrim.Scale == null) return;
401
402             scaleX.Value = (Decimal)selectedPrim.Scale.X;
403             scaleY.Value = (Decimal)selectedPrim.Scale.Y;
404             scaleZ.Value = (Decimal)selectedPrim.Scale.Z;
405         }
406
407         private void getRotFromSelection()
408         {
409             if (selectedPrim == null) return;
410             if (selectedPrim.Rotation == null) return;
411
412             rotX.Value = (Decimal)selectedPrim.Rotation.X;
413             rotY.Value = (Decimal)selectedPrim.Rotation.Y;
414             rotZ.Value = (Decimal)selectedPrim.Rotation.Z;
415         }
416
417         private void setRotToSelection()
418         {
419             if (selectedPrim != null && selectedPrim.Rotation != null)
420             {
421                 selectedPrim.Rotation.X = (float)rotX.Value;
422                 selectedPrim.Rotation.Y = (float)rotY.Value;
423                 selectedPrim.Rotation.Z = (float)rotZ.Value;
424             }
425         }
426
427         private void setScaleToSelection()
428         {
429             if (selectedPrim != null && selectedPrim.Scale != null)
430             {
431                 selectedPrim.Scale.X = (float)scaleX.Value;
432                 selectedPrim.Scale.Y = (float)scaleY.Value;
433                 selectedPrim.Scale.Z = (float)scaleZ.Value;
434             }
435         }
436
437         private void button7_Click(object sender, EventArgs e)
438         {
439             Prims.Clear();
440             Vector3 location = client.Self.SimPosition;
441
442             lock (Prims)
443             {
444                 client.Network.CurrentSim.ObjectsPrimitives.ForEach(prim =>
445                 {
446                     int distance = (int)Vector3.Distance(prim.Position, location);
447                     if (prim.ParentID == client.Self.LocalID)
448                     {
449                         distance = 0;
450                     }
451                     if (IncludePrim(prim) && (prim.Position != Vector3.Zero) && (distance < (int)this.numRadius.Value))
452                     {
453                         Prims.Add(prim);
454                         if (prim.Properties == null)
455                         {
456                             propRequester.RequestProps(prim);
457                         }
458                     }
459                 });
460             }
461             lstPrims.VirtualListSize = Prims.Count;
462             lstPrims.Invalidate();
463         }
464
465         private void btn_Save_Click(object sender, EventArgs e)
466         {
467             setScaleToSelection();
468             setRotToSelection();
469         }
470     }
471 }