corrade-vassal – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | /* |
2 | * Copyright (c) 2006-2014, openmetaverse.org |
||
3 | * All rights reserved. |
||
4 | * |
||
5 | * - Redistribution and use in source and binary forms, with or without |
||
6 | * modification, are permitted provided that the following conditions are met: |
||
7 | * |
||
8 | * - Redistributions of source code must retain the above copyright notice, this |
||
9 | * list of conditions and the following disclaimer. |
||
10 | * - Neither the name of the openmetaverse.org nor the names |
||
11 | * of its contributors may be used to endorse or promote products derived from |
||
12 | * this software without specific prior written permission. |
||
13 | * |
||
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||
15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||
18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||
19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||
20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||
23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||
24 | * POSSIBILITY OF SUCH DAMAGE. |
||
25 | */ |
||
26 | |||
27 | using OpenMetaverse; |
||
28 | using System; |
||
29 | using System.Collections; |
||
30 | using System.Collections.Generic; |
||
31 | using System.Drawing; |
||
32 | using System.Windows.Forms; |
||
33 | |||
34 | namespace OpenMetaverse.GUI |
||
35 | { |
||
36 | |||
37 | /// <summary> |
||
38 | /// ListView GUI component for viewing a client's nearby avatars list |
||
39 | /// </summary> |
||
40 | public class AvatarList : ListView |
||
41 | { |
||
42 | private GridClient _Client; |
||
43 | private ListColumnSorter _ColumnSorter = new ListColumnSorter(); |
||
44 | private TrackedAvatar _SelectedAvatar; |
||
45 | |||
46 | private DoubleDictionary<uint, UUID, TrackedAvatar> _TrackedAvatars = new DoubleDictionary<uint, UUID, TrackedAvatar>(); |
||
47 | private Dictionary<UUID, TrackedAvatar> _UntrackedAvatars = new Dictionary<UUID, TrackedAvatar>(); |
||
48 | |||
49 | public delegate void AvatarCallback(TrackedAvatar trackedAvatar); |
||
50 | |||
51 | /// <summary> |
||
52 | /// Triggered when the user double clicks on an avatar in the list |
||
53 | /// </summary> |
||
54 | public event AvatarCallback OnAvatarDoubleClick; |
||
55 | |||
56 | /// <summary> |
||
57 | /// Triggered when a new avatar is added to the list |
||
58 | /// </summary> |
||
59 | public event AvatarCallback OnAvatarAdded; |
||
60 | |||
61 | /// <summary> |
||
62 | /// Triggered when an avatar is removed from the list |
||
63 | /// </summary> |
||
64 | public event AvatarCallback OnAvatarRemoved; |
||
65 | |||
66 | /// <summary> |
||
67 | /// Gets or sets the GridClient associated with this control |
||
68 | /// </summary> |
||
69 | public GridClient Client |
||
70 | { |
||
71 | get { return _Client; } |
||
72 | set { if (value != null) InitializeClient(value); } |
||
73 | } |
||
74 | |||
75 | /// <summary> |
||
76 | /// Returns the current selected avatar in the tracked avatars list |
||
77 | /// </summary> |
||
78 | public TrackedAvatar SelectedAvatar |
||
79 | { |
||
80 | get { return _SelectedAvatar; } |
||
81 | } |
||
82 | |||
83 | /// <summary> |
||
84 | /// TreeView control for an unspecified client's nearby avatar list |
||
85 | /// </summary> |
||
86 | public AvatarList() |
||
87 | { |
||
88 | ColumnHeader header1 = this.Columns.Add("Name"); |
||
89 | header1.Width = this.Width - 20; |
||
90 | |||
91 | ColumnHeader header2 = this.Columns.Add(" "); |
||
92 | header2.Width = 40; |
||
93 | |||
94 | this.MultiSelect = false; |
||
95 | this.SelectedIndexChanged += new EventHandler(AvatarList_SelectedIndexChanged); |
||
96 | |||
97 | _ColumnSorter.SortColumn = 1; |
||
98 | this.Sorting = SortOrder.Ascending; |
||
99 | this.ListViewItemSorter = _ColumnSorter; |
||
100 | |||
101 | EventHandler clickHandler = new EventHandler(defaultMenuItem_Click); |
||
102 | this.ContextMenu = new ContextMenu(); |
||
103 | this.ContextMenu.MenuItems.Add("Offer Teleport", clickHandler); |
||
104 | this.ContextMenu.MenuItems.Add("Teleport To", clickHandler); |
||
105 | this.ContextMenu.MenuItems.Add("Walk To", clickHandler); |
||
106 | |||
107 | this.DoubleBuffered = true; |
||
108 | this.ListViewItemSorter = _ColumnSorter; |
||
109 | this.View = View.Details; |
||
110 | this.ColumnClick += new ColumnClickEventHandler(AvatarList_ColumnClick); |
||
111 | this.DoubleClick += new EventHandler(AvatarList_DoubleClick); |
||
112 | } |
||
113 | |||
114 | void AvatarList_SelectedIndexChanged(object sender, EventArgs e) |
||
115 | { |
||
116 | lock (_TrackedAvatars) |
||
117 | { |
||
118 | lock (_UntrackedAvatars) |
||
119 | { |
||
120 | if (this.SelectedItems.Count > 0) |
||
121 | { |
||
122 | UUID selectedID = new UUID(this.SelectedItems[0].Name); |
||
123 | TrackedAvatar selectedAV; |
||
124 | if (!_TrackedAvatars.TryGetValue(selectedID, out selectedAV) && !_UntrackedAvatars.TryGetValue(selectedID, out selectedAV)) |
||
125 | selectedAV = null; |
||
126 | |||
127 | _SelectedAvatar = selectedAV; |
||
128 | } |
||
129 | } |
||
130 | } |
||
131 | } |
||
132 | |||
133 | /// <summary> |
||
134 | /// Thread-safe method for clearing the TreeView control |
||
135 | /// </summary> |
||
136 | public void ClearItems() |
||
137 | { |
||
138 | if (this.InvokeRequired) this.BeginInvoke((MethodInvoker)delegate { ClearItems(); }); |
||
139 | else |
||
140 | { |
||
141 | if (this.Handle != IntPtr.Zero) |
||
142 | this.Items.Clear(); |
||
143 | } |
||
144 | } |
||
145 | |||
146 | public TrackedAvatar GetAvatar(UUID avatarID) |
||
147 | { |
||
148 | TrackedAvatar av; |
||
149 | _TrackedAvatars.TryGetValue(avatarID, out av); |
||
150 | return av; |
||
151 | } |
||
152 | |||
153 | private void InitializeClient(GridClient client) |
||
154 | { |
||
155 | _Client = client; |
||
156 | _Client.Avatars.AvatarAppearance += Avatars_OnAvatarAppearance; |
||
157 | _Client.Avatars.UUIDNameReply += new EventHandler<UUIDNameReplyEventArgs>(Avatars_UUIDNameReply); |
||
158 | _Client.Grid.CoarseLocationUpdate += Grid_CoarseLocationUpdate; |
||
159 | _Client.Network.SimChanged += Network_OnCurrentSimChanged; |
||
160 | _Client.Objects.AvatarUpdate += Objects_OnNewAvatar; |
||
161 | _Client.Objects.TerseObjectUpdate += Objects_OnObjectUpdated; |
||
162 | |||
163 | } |
||
164 | |||
165 | void Avatars_UUIDNameReply(object sender, UUIDNameReplyEventArgs e) |
||
166 | { |
||
167 | lock (_UntrackedAvatars) |
||
168 | { |
||
169 | foreach (KeyValuePair<UUID, string> name in e.Names) |
||
170 | { |
||
171 | TrackedAvatar trackedAvatar; |
||
172 | if (_UntrackedAvatars.TryGetValue(name.Key, out trackedAvatar)) |
||
173 | { |
||
174 | trackedAvatar.Name = name.Value; |
||
175 | |||
176 | if (OnAvatarAdded != null && trackedAvatar.ListViewItem.Text == "(Loading...)") |
||
177 | { |
||
178 | try { OnAvatarAdded(trackedAvatar); } |
||
179 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
180 | } |
||
181 | |||
182 | this.BeginInvoke((MethodInvoker)delegate |
||
183 | { |
||
184 | trackedAvatar.ListViewItem.Text = name.Value; |
||
185 | }); |
||
186 | } |
||
187 | } |
||
188 | } |
||
189 | } |
||
190 | |||
191 | void Grid_CoarseLocationUpdate(object sender, CoarseLocationUpdateEventArgs e) |
||
192 | { |
||
193 | UpdateCoarseInfo(e.Simulator, e.NewEntries, e.RemovedEntries); |
||
194 | } |
||
195 | |||
196 | private void AddAvatar(UUID avatarID, Avatar avatar, Vector3 coarsePosition) |
||
197 | { |
||
198 | if (!this.IsHandleCreated) return; |
||
199 | |||
200 | if (this.InvokeRequired) this.BeginInvoke((MethodInvoker)delegate { AddAvatar(avatar.ID, avatar, coarsePosition); }); |
||
201 | else |
||
202 | { |
||
203 | TrackedAvatar trackedAvatar = new TrackedAvatar(); |
||
204 | trackedAvatar.CoarseLocation = coarsePosition; |
||
205 | trackedAvatar.ID = avatarID; |
||
206 | trackedAvatar.ListViewItem = this.Items.Add(avatarID.ToString(), trackedAvatar.Name, null); |
||
207 | trackedAvatar.ListViewItem.Name = avatarID.ToString(); |
||
208 | |||
209 | string strDist = avatarID == _Client.Self.AgentID ? "--" : (int)Vector3.Distance(_Client.Self.SimPosition, coarsePosition) + "m"; |
||
210 | trackedAvatar.ListViewItem.SubItems.Add(strDist); |
||
211 | |||
212 | if (avatar != null) |
||
213 | { |
||
214 | trackedAvatar.Name = avatar.Name; |
||
215 | trackedAvatar.ListViewItem.Text = avatar.Name; |
||
216 | |||
217 | lock (_TrackedAvatars) |
||
218 | { |
||
219 | if (_TrackedAvatars.ContainsKey(avatarID)) |
||
220 | _TrackedAvatars.Remove(avatarID); |
||
221 | |||
222 | _TrackedAvatars.Add(avatar.LocalID, avatarID, trackedAvatar); |
||
223 | } |
||
224 | |||
225 | if (OnAvatarAdded != null) |
||
226 | { |
||
227 | try { OnAvatarAdded(trackedAvatar); } |
||
228 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
229 | } |
||
230 | } |
||
231 | else |
||
232 | { |
||
233 | lock (_UntrackedAvatars) |
||
234 | { |
||
235 | _UntrackedAvatars.Add(avatarID, trackedAvatar); |
||
236 | |||
237 | trackedAvatar.ListViewItem.ForeColor = Color.FromKnownColor(KnownColor.GrayText); |
||
238 | |||
239 | if (avatarID == _Client.Self.AgentID) |
||
240 | { |
||
241 | trackedAvatar.Name = _Client.Self.Name; |
||
242 | trackedAvatar.ListViewItem.Text = _Client.Self.Name; |
||
243 | } |
||
244 | |||
245 | else if (_Client.Network.Connected) |
||
246 | Client.Avatars.RequestAvatarName(avatarID); |
||
247 | } |
||
248 | } |
||
249 | |||
250 | } |
||
251 | } |
||
252 | |||
253 | private void RemoveAvatar(UUID id) |
||
254 | { |
||
255 | if (!this.IsHandleCreated) return; |
||
256 | |||
257 | if (this.InvokeRequired) this.BeginInvoke((MethodInvoker)delegate { RemoveAvatar(id); }); |
||
258 | else |
||
259 | { |
||
260 | TrackedAvatar trackedAvatar; |
||
261 | |||
262 | lock (_TrackedAvatars) |
||
263 | { |
||
264 | if (_TrackedAvatars.TryGetValue(id, out trackedAvatar)) |
||
265 | { |
||
266 | this.Items.Remove(trackedAvatar.ListViewItem); |
||
267 | _TrackedAvatars.Remove(id); |
||
268 | } |
||
269 | } |
||
270 | |||
271 | lock (_UntrackedAvatars) |
||
272 | { |
||
273 | if (_UntrackedAvatars.TryGetValue(id, out trackedAvatar)) |
||
274 | { |
||
275 | this.Items.Remove(trackedAvatar.ListViewItem); |
||
276 | _UntrackedAvatars.Remove(trackedAvatar.ID); |
||
277 | } |
||
278 | } |
||
279 | |||
280 | if (OnAvatarRemoved != null) |
||
281 | { |
||
282 | try { OnAvatarRemoved(trackedAvatar); } |
||
283 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
284 | } |
||
285 | } |
||
286 | } |
||
287 | |||
288 | private void UpdateAvatar(Avatar avatar) |
||
289 | { |
||
290 | if (!this.IsHandleCreated) return; |
||
291 | |||
292 | if (this.InvokeRequired) this.BeginInvoke((MethodInvoker)delegate { UpdateAvatar(avatar); }); |
||
293 | else |
||
294 | { |
||
295 | TrackedAvatar trackedAvatar; |
||
296 | bool found; |
||
297 | |||
298 | lock (_UntrackedAvatars) |
||
299 | found = _UntrackedAvatars.TryGetValue(avatar.ID, out trackedAvatar); |
||
300 | |||
301 | if (found) |
||
302 | { |
||
303 | trackedAvatar.Name = avatar.Name; |
||
304 | trackedAvatar.ListViewItem.Text = avatar.Name; |
||
305 | trackedAvatar.ListViewItem.ForeColor = Color.FromKnownColor(KnownColor.ControlText); |
||
306 | |||
307 | lock (_TrackedAvatars) _TrackedAvatars.Add(avatar.LocalID, avatar.ID, trackedAvatar); |
||
308 | _UntrackedAvatars.Remove(avatar.ID); |
||
309 | } |
||
310 | |||
311 | lock (_TrackedAvatars) |
||
312 | found = _TrackedAvatars.TryGetValue(avatar.ID, out trackedAvatar); |
||
313 | |||
314 | if (found) |
||
315 | { |
||
316 | trackedAvatar.Avatar = avatar; |
||
317 | trackedAvatar.Name = avatar.Name; |
||
318 | trackedAvatar.ID = avatar.ID; |
||
319 | |||
320 | string strDist = avatar.ID == _Client.Self.AgentID ? "--" : (int)Vector3.Distance(_Client.Self.SimPosition, avatar.Position) + "m"; |
||
321 | trackedAvatar.ListViewItem.SubItems[1].Text = strDist; |
||
322 | } |
||
323 | else |
||
324 | { |
||
325 | AddAvatar(avatar.ID, avatar, Vector3.Zero); |
||
326 | } |
||
327 | |||
328 | this.Sort(); |
||
329 | } |
||
330 | } |
||
331 | |||
332 | private void UpdateCoarseInfo(Simulator sim, List<UUID> newEntries, List<UUID> removedEntries) |
||
333 | { |
||
334 | if (this.InvokeRequired) this.BeginInvoke((MethodInvoker)delegate { UpdateCoarseInfo(sim, newEntries, removedEntries); }); |
||
335 | else |
||
336 | { |
||
337 | if (sim == null) return; |
||
338 | |||
339 | if (removedEntries != null) |
||
340 | { |
||
341 | for (int i = 0; i < removedEntries.Count; i++) |
||
342 | RemoveAvatar(removedEntries[i]); |
||
343 | } |
||
344 | |||
345 | if (newEntries != null) |
||
346 | { |
||
347 | for (int i = 0; i < newEntries.Count; i++) |
||
348 | { |
||
349 | int index = this.Items.IndexOfKey(newEntries[i].ToString()); |
||
350 | if (index == -1) |
||
351 | { |
||
352 | Vector3 coarsePos; |
||
353 | if (!sim.AvatarPositions.TryGetValue(newEntries[i], out coarsePos)) |
||
354 | continue; |
||
355 | |||
356 | AddAvatar(newEntries[i], null, coarsePos); |
||
357 | } |
||
358 | } |
||
359 | } |
||
360 | } |
||
361 | } |
||
362 | |||
363 | private void defaultMenuItem_Click(object sender, EventArgs e) |
||
364 | { |
||
365 | MenuItem menuItem = (MenuItem)sender; |
||
366 | |||
367 | switch (menuItem.Text) |
||
368 | { |
||
369 | case "Offer Teleport": |
||
370 | { |
||
371 | Client.Self.SendTeleportLure(_SelectedAvatar.ID); |
||
372 | break; |
||
373 | } |
||
374 | case "Teleport To": |
||
375 | { |
||
376 | Vector3 pos; |
||
377 | if (Client.Network.CurrentSim.AvatarPositions.TryGetValue(_SelectedAvatar.ID, out pos)) |
||
378 | Client.Self.Teleport(Client.Network.CurrentSim.Name, pos); |
||
379 | |||
380 | break; |
||
381 | } |
||
382 | case "Walk To": |
||
383 | { |
||
384 | Vector3 pos; |
||
385 | if (Client.Network.CurrentSim.AvatarPositions.TryGetValue(_SelectedAvatar.ID, out pos)) |
||
386 | Client.Self.AutoPilotLocal((int)pos.X, (int)pos.Y, pos.Z); |
||
387 | |||
388 | break; |
||
389 | } |
||
390 | } |
||
391 | } |
||
392 | |||
393 | void AvatarList_ColumnClick(object sender, ColumnClickEventArgs e) |
||
394 | { |
||
395 | _ColumnSorter.SortColumn = e.Column; |
||
396 | if ((_ColumnSorter.Ascending = (this.Sorting == SortOrder.Ascending))) this.Sorting = SortOrder.Descending; |
||
397 | else this.Sorting = SortOrder.Ascending; |
||
398 | this.ListViewItemSorter = _ColumnSorter; |
||
399 | } |
||
400 | |||
401 | void AvatarList_DoubleClick(object sender, EventArgs e) |
||
402 | { |
||
403 | if (OnAvatarDoubleClick != null) |
||
404 | { |
||
405 | ListView list = (ListView)sender; |
||
406 | if (list.SelectedItems.Count > 0) |
||
407 | { |
||
408 | TrackedAvatar trackedAvatar; |
||
409 | if (!_TrackedAvatars.TryGetValue(new UUID(list.SelectedItems[0].Name), out trackedAvatar) |
||
410 | && !_UntrackedAvatars.TryGetValue(new UUID(list.SelectedItems[0].Name), out trackedAvatar)) |
||
411 | return; |
||
412 | |||
413 | try { OnAvatarDoubleClick(trackedAvatar); } |
||
414 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
415 | } |
||
416 | } |
||
417 | } |
||
418 | |||
419 | void Avatars_OnAvatarAppearance(object sender, AvatarAppearanceEventArgs e) |
||
420 | { |
||
421 | if (e.VisualParams.Count > 31) |
||
422 | { |
||
423 | TrackedAvatar trackedAvatar; |
||
424 | bool foundAvatar; |
||
425 | |||
426 | lock (_TrackedAvatars) |
||
427 | foundAvatar = _TrackedAvatars.TryGetValue(e.AvatarID, out trackedAvatar); |
||
428 | |||
429 | if (foundAvatar) |
||
430 | { |
||
431 | this.BeginInvoke((MethodInvoker)delegate |
||
432 | { |
||
433 | byte param = e.VisualParams[31]; |
||
434 | if (param > 0) |
||
435 | trackedAvatar.ListViewItem.ForeColor = Color.Blue; |
||
436 | else |
||
437 | trackedAvatar.ListViewItem.ForeColor = Color.Magenta; |
||
438 | }); |
||
439 | } |
||
440 | } |
||
441 | } |
||
442 | |||
443 | |||
444 | |||
445 | void Network_OnCurrentSimChanged(object sender, SimChangedEventArgs e) |
||
446 | { |
||
447 | lock (_TrackedAvatars) |
||
448 | _TrackedAvatars.Clear(); |
||
449 | |||
450 | lock (_UntrackedAvatars) |
||
451 | _UntrackedAvatars.Clear(); |
||
452 | |||
453 | ClearItems(); |
||
454 | } |
||
455 | |||
456 | void Objects_OnNewAvatar(object sender, AvatarUpdateEventArgs e) |
||
457 | { |
||
458 | UpdateAvatar(e.Avatar); |
||
459 | } |
||
460 | |||
461 | void Objects_OnObjectUpdated(object sender, TerseObjectUpdateEventArgs e) |
||
462 | { |
||
463 | bool found; |
||
464 | lock (_TrackedAvatars) |
||
465 | found = _TrackedAvatars.ContainsKey(e.Update.LocalID); |
||
466 | |||
467 | if (found) |
||
468 | { |
||
469 | Avatar av; |
||
470 | if (e.Simulator.ObjectsAvatars.TryGetValue(e.Update.LocalID, out av)) |
||
471 | UpdateAvatar(av); |
||
472 | } |
||
473 | } |
||
474 | |||
475 | } |
||
476 | |||
477 | /// <summary> |
||
478 | /// Contains any available information for an avatar in the simulator. |
||
479 | /// A null value for .Avatar indicates coarse data for an avatar outside of visible range. |
||
480 | /// </summary> |
||
481 | public class TrackedAvatar |
||
482 | { |
||
483 | /// <summary>Assigned if the avatar is within visible range</summary> |
||
484 | public Avatar Avatar = null; |
||
485 | |||
486 | /// <summary>Last known coarse location of avatar</summary> |
||
487 | public Vector3 CoarseLocation; |
||
488 | |||
489 | /// <summary>Avatar ID</summary> |
||
490 | public UUID ID; |
||
491 | |||
492 | /// <summary>ListViewItem associated with this avatar</summary> |
||
493 | public ListViewItem ListViewItem; |
||
494 | |||
495 | /// <summary>Populated by RequestAvatarName if avatar is not visible</summary> |
||
496 | public string Name = "(Loading...)"; |
||
497 | } |
||
498 | |||
499 | } |