OSDN Git Service

8a0f27d13531036c18d1040aeee424ad5b08837b
[radegast/radegast.git] / Radegast / GUI / Consoles / Inventory / InventoryConsole.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2014, 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;
33 using System.Collections.Generic;
34 using System.ComponentModel;
35 using System.Drawing;
36 using System.Linq;
37 using System.Windows.Forms;
38 #if (COGBOT_LIBOMV || USE_STHREADS)
39 using ThreadPoolUtil;
40 using Thread = ThreadPoolUtil.Thread;
41 using ThreadPool = ThreadPoolUtil.ThreadPool;
42 using Monitor = ThreadPoolUtil.Monitor;
43 #endif
44 using System.Threading;
45
46 using OpenMetaverse;
47 using OpenMetaverse.StructuredData;
48
49 namespace Radegast
50 {
51
52     public partial class InventoryConsole : UserControl
53     {
54         RadegastInstance instance;
55         GridClient client { get { return instance.Client; } }
56         Dictionary<UUID, TreeNode> FolderNodes = new Dictionary<UUID, TreeNode>();
57
58         private InventoryManager Manager;
59         private OpenMetaverse.Inventory Inventory;
60         private TreeNode invRootNode;
61         private string newItemName = string.Empty;
62         private List<UUID> fetchedFolders = new List<UUID>();
63         private System.Threading.Timer _EditTimer;
64         private TreeNode _EditNode;
65         private Dictionary<UUID, AttachmentInfo> attachments = new Dictionary<UUID, AttachmentInfo>();
66         private System.Timers.Timer TreeUpdateTimer;
67         private Queue<InventoryBase> ItemsToAdd = new Queue<InventoryBase>();
68         private Queue<InventoryBase> ItemsToUpdate = new Queue<InventoryBase>();
69         private bool TreeUpdateInProgress = false;
70         private Dictionary<UUID, TreeNode> UUID2NodeCache = new Dictionary<UUID, TreeNode>();
71         private int updateInterval = 1000;
72         private Thread InventoryUpdate;
73         private List<UUID> WornItems = new List<UUID>();
74         private bool appearnceWasBusy;
75         private InvNodeSorter sorter;
76         private List<UUID> QueuedFolders = new List<UUID>();
77         private Dictionary<UUID, int> FolderFetchRetries = new Dictionary<UUID, int>();
78         AutoResetEvent trashCreated = new AutoResetEvent(false);
79
80         #region Construction and disposal
81         public InventoryConsole(RadegastInstance instance)
82         {
83             InitializeComponent();
84             Disposed += new EventHandler(InventoryConsole_Disposed);
85
86             TreeUpdateTimer = new System.Timers.Timer()
87             {
88                 Interval = updateInterval,
89                 Enabled = false,
90                 SynchronizingObject = invTree
91             };
92             TreeUpdateTimer.Elapsed += TreeUpdateTimerTick;
93
94             this.instance = instance;
95             Manager = client.Inventory;
96             Inventory = Manager.Store;
97             Inventory.RootFolder.OwnerID = client.Self.AgentID;
98             invTree.ImageList = frmMain.ResourceImages;
99             invRootNode = AddDir(null, Inventory.RootFolder);
100             UpdateStatus("Reading cache");
101             Init1();
102         }
103
104         public void Init1()
105         {
106             WorkPool.QueueUserWorkItem(sync =>
107             {
108                 Logger.Log("Reading inventory cache from " + instance.InventoryCacheFileName, Helpers.LogLevel.Debug, client);
109                 Inventory.RestoreFromDisk(instance.InventoryCacheFileName);
110                 Init2();
111             });
112         }
113
114         public void Init2()
115         {
116             if (instance.MainForm.InvokeRequired)
117             {
118                 instance.MainForm.BeginInvoke(new MethodInvoker(() => Init2()));
119                 return;
120             }
121
122             AddFolderFromStore(invRootNode, Inventory.RootFolder);
123
124             sorter = new InvNodeSorter();
125
126             if (!instance.GlobalSettings.ContainsKey("inv_sort_bydate"))
127                 instance.GlobalSettings["inv_sort_bydate"] = OSD.FromBoolean(true);
128             if (!instance.GlobalSettings.ContainsKey("inv_sort_sysfirst"))
129                 instance.GlobalSettings["inv_sort_sysfirst"] = OSD.FromBoolean(true);
130
131             sorter.ByDate = instance.GlobalSettings["inv_sort_bydate"].AsBoolean();
132             sorter.SystemFoldersFirst = instance.GlobalSettings["inv_sort_sysfirst"].AsBoolean();
133
134             tbtnSortByDate.Checked = sorter.ByDate;
135             tbtbSortByName.Checked = !sorter.ByDate;
136             tbtnSystemFoldersFirst.Checked = sorter.SystemFoldersFirst;
137
138             invTree.TreeViewNodeSorter = sorter;
139
140             if (instance.MonoRuntime)
141             {
142                 invTree.BackColor = Color.FromKnownColor(KnownColor.Window);
143                 invTree.ForeColor = invTree.LineColor = Color.FromKnownColor(KnownColor.WindowText);
144                 InventoryFolder f = new InventoryFolder(UUID.Random());
145                 f.Name = "";
146                 f.ParentUUID = UUID.Zero;
147                 f.PreferredType = AssetType.Unknown;
148                 TreeNode dirNode = new TreeNode();
149                 dirNode.Name = f.UUID.ToString();
150                 dirNode.Text = f.Name;
151                 dirNode.Tag = f;
152                 dirNode.ImageIndex = GetDirImageIndex(f.PreferredType.ToString().ToLower());
153                 dirNode.SelectedImageIndex = dirNode.ImageIndex;
154                 invTree.Nodes.Add(dirNode);
155                 invTree.Sort();
156             }
157
158             saveAllTToolStripMenuItem.Enabled = false;
159             InventoryUpdate = new Thread(new ThreadStart(StartTraverseNodes));
160             InventoryUpdate.Name = "InventoryUpdate";
161             InventoryUpdate.IsBackground = true;
162             InventoryUpdate.Start();
163
164             invRootNode.Expand();
165
166             invTree.AfterExpand += new TreeViewEventHandler(TreeView_AfterExpand);
167             invTree.NodeMouseClick += new TreeNodeMouseClickEventHandler(invTree_MouseClick);
168             invTree.NodeMouseDoubleClick += new TreeNodeMouseClickEventHandler(invTree_NodeMouseDoubleClick);
169
170             _EditTimer = new System.Threading.Timer(OnLabelEditTimer, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
171
172             // Callbacks
173             Inventory.InventoryObjectAdded += new EventHandler<InventoryObjectAddedEventArgs>(Inventory_InventoryObjectAdded);
174             Inventory.InventoryObjectUpdated += new EventHandler<InventoryObjectUpdatedEventArgs>(Inventory_InventoryObjectUpdated);
175             Inventory.InventoryObjectRemoved += new EventHandler<InventoryObjectRemovedEventArgs>(Inventory_InventoryObjectRemoved);
176
177             client.Objects.ObjectUpdate += new EventHandler<PrimEventArgs>(Objects_AttachmentUpdate);
178             client.Objects.KillObject += new EventHandler<KillObjectEventArgs>(Objects_KillObject);
179             client.Appearance.AppearanceSet += new EventHandler<AppearanceSetEventArgs>(Appearance_AppearanceSet);
180         }
181
182         void InventoryConsole_Disposed(object sender, EventArgs e)
183         {
184             if (TreeUpdateTimer != null)
185             {
186                 TreeUpdateTimer.Stop();
187                 TreeUpdateTimer.Dispose();
188                 TreeUpdateTimer = null;
189             }
190             if (InventoryUpdate != null)
191             {
192                 if (InventoryUpdate.IsAlive)
193                     InventoryUpdate.Abort();
194                 InventoryUpdate = null;
195             }
196
197             Inventory.InventoryObjectAdded -= new EventHandler<InventoryObjectAddedEventArgs>(Inventory_InventoryObjectAdded);
198             Inventory.InventoryObjectUpdated -= new EventHandler<InventoryObjectUpdatedEventArgs>(Inventory_InventoryObjectUpdated);
199             Inventory.InventoryObjectRemoved -= new EventHandler<InventoryObjectRemovedEventArgs>(Inventory_InventoryObjectRemoved);
200
201             client.Objects.ObjectUpdate -= new EventHandler<PrimEventArgs>(Objects_AttachmentUpdate);
202             client.Objects.KillObject -= new EventHandler<KillObjectEventArgs>(Objects_KillObject);
203             client.Appearance.AppearanceSet -= new EventHandler<AppearanceSetEventArgs>(Appearance_AppearanceSet);
204         }
205         #endregion
206
207         #region Network callbacks
208         void Appearance_AppearanceSet(object sender, AppearanceSetEventArgs e)
209         {
210             UpdateWornLabels();
211             if (appearnceWasBusy)
212             {
213                 appearnceWasBusy = false;
214                 client.Appearance.RequestSetAppearance(true);
215             }
216         }
217
218         void Objects_KillObject(object sender, KillObjectEventArgs e)
219         {
220             AttachmentInfo attachment = null;
221             lock (attachments)
222             {
223                 foreach (AttachmentInfo att in attachments.Values)
224                 {
225                     if (att.Prim != null && att.Prim.LocalID == e.ObjectLocalID)
226                     {
227                         attachment = att;
228                         break;
229                     }
230                 }
231
232                 if (attachment != null)
233                 {
234                     attachments.Remove(attachment.InventoryID);
235                     UpdateNodeLabel(attachment.InventoryID);
236                 }
237             }
238         }
239
240         void Objects_AttachmentUpdate(object sender, PrimEventArgs e)
241         {
242             Primitive prim = e.Prim;
243
244             if (client.Self.LocalID == 0 ||
245                 prim.ParentID != client.Self.LocalID ||
246                 prim.NameValues == null) return;
247
248             for (int i = 0; i < prim.NameValues.Length; i++)
249             {
250                 if (prim.NameValues[i].Name == "AttachItemID")
251                 {
252                     AttachmentInfo attachment = new AttachmentInfo();
253                     attachment.Prim = prim;
254                     attachment.InventoryID = new UUID(prim.NameValues[i].Value.ToString());
255                     attachment.PrimID = prim.ID;
256
257                     lock (attachments)
258                     {
259                         // Add new attachment info
260                         if (!attachments.ContainsKey(attachment.InventoryID))
261                         {
262                             attachments.Add(attachment.InventoryID, attachment);
263
264                         }
265                         else
266                         {
267                             attachment = attachments[attachment.InventoryID];
268                             if (attachment.Prim == null)
269                             {
270                                 attachment.Prim = prim;
271                             }
272                         }
273
274                         // Don't update the tree yet if we're still updating invetory tree from server
275                         if (!TreeUpdateInProgress)
276                         {
277                             if (Inventory.Contains(attachment.InventoryID))
278                             {
279                                 if (attachment.Item == null)
280                                 {
281                                     InventoryItem item = (InventoryItem)Inventory[attachment.InventoryID];
282                                     attachment.Item = item;
283                                 }
284                                 if (!attachment.MarkedAttached)
285                                 {
286                                     attachment.MarkedAttached = true;
287                                     UpdateNodeLabel(attachment.InventoryID);
288                                 }
289                             }
290                             else
291                             {
292                                 client.Inventory.RequestFetchInventory(attachment.InventoryID, client.Self.AgentID);
293                             }
294                         }
295                     }
296                     break;
297                 }
298             }
299         }
300
301         void Inventory_InventoryObjectAdded(object sender, InventoryObjectAddedEventArgs e)
302         {
303             if (e.Obj is InventoryFolder && ((InventoryFolder)e.Obj).PreferredType == AssetType.TrashFolder)
304             {
305                 trashCreated.Set();
306             }
307
308             if (TreeUpdateInProgress)
309             {
310                 lock (ItemsToAdd)
311                 {
312                     ItemsToAdd.Enqueue(e.Obj);
313                 }
314             }
315             else
316             {
317                 Exec_OnInventoryObjectAdded(e.Obj);
318             }
319         }
320
321         void Exec_OnInventoryObjectAdded(InventoryBase obj)
322         {
323             if (InvokeRequired)
324             {
325                 Invoke(new MethodInvoker(delegate()
326                     {
327                         Exec_OnInventoryObjectAdded(obj);
328                     }
329                 ));
330                 return;
331             }
332
333             lock (attachments)
334             {
335                 if (attachments.ContainsKey(obj.UUID))
336                 {
337                     attachments[obj.UUID].Item = (InventoryItem)obj;
338                 }
339             }
340
341             TreeNode parent = findNodeForItem(obj.ParentUUID);
342
343             if (parent != null)
344             {
345                 TreeNode newNode = AddBase(parent, obj);
346                 if (obj.Name == newItemName)
347                 {
348                     if (newNode.Parent.IsExpanded)
349                     {
350                         newNode.BeginEdit();
351                     }
352                     else
353                     {
354                         newNode.Parent.Expand();
355                     }
356                 }
357             }
358             newItemName = string.Empty;
359         }
360
361         void Inventory_InventoryObjectRemoved(object sender, InventoryObjectRemovedEventArgs e)
362         {
363             if (InvokeRequired)
364             {
365                 BeginInvoke(new MethodInvoker(() => Inventory_InventoryObjectRemoved(sender, e)));
366                 return;
367             }
368
369             lock (attachments)
370             {
371                 if (attachments.ContainsKey(e.Obj.UUID))
372                 {
373                     attachments.Remove(e.Obj.UUID);
374                 }
375             }
376
377             TreeNode currentNode = findNodeForItem(e.Obj.UUID);
378             if (currentNode != null)
379             {
380                 removeNode(currentNode);
381             }
382         }
383
384         void Inventory_InventoryObjectUpdated(object sender, InventoryObjectUpdatedEventArgs e)
385         {
386             if (TreeUpdateInProgress)
387             {
388                 lock (ItemsToUpdate)
389                 {
390                     if (e.NewObject is InventoryFolder)
391                     {
392                         TreeNode currentNode = findNodeForItem(e.NewObject.UUID);
393                         if (currentNode != null && currentNode.Text == e.NewObject.Name) return;
394                     }
395
396                     if (!ItemsToUpdate.Contains(e.NewObject))
397                     {
398                         ItemsToUpdate.Enqueue(e.NewObject);
399                     }
400                 }
401             }
402             else
403             {
404                 Exec_OnInventoryObjectUpdated(e.OldObject, e.NewObject);
405             }
406         }
407
408         void Exec_OnInventoryObjectUpdated(InventoryBase oldObject, InventoryBase newObject)
409         {
410             if (newObject == null) return;
411
412             if (InvokeRequired)
413             {
414                 BeginInvoke(new MethodInvoker(() => Exec_OnInventoryObjectUpdated(oldObject, newObject)));
415                 return;
416             }
417
418             lock (attachments)
419             {
420                 if (attachments.ContainsKey(newObject.UUID))
421                 {
422                     attachments[newObject.UUID].Item = (InventoryItem)newObject;
423                 }
424             }
425
426             // Find our current node in the tree
427             TreeNode currentNode = findNodeForItem(newObject.UUID);
428
429             // Find which node should be our parrent
430             TreeNode parent = findNodeForItem(newObject.ParentUUID);
431
432             if (parent == null) return;
433
434             if (currentNode != null)
435             {
436                 // Did we move to a different folder
437                 if (currentNode.Parent != parent)
438                 {
439                     TreeNode movedNode = (TreeNode)currentNode.Clone();
440                     movedNode.Tag = newObject;
441                     parent.Nodes.Add(movedNode);
442                     removeNode(currentNode);
443                     cacheNode(movedNode);
444                 }
445                 else // Update
446                 {
447                     currentNode.Tag = newObject;
448                     currentNode.Text = ItemLabel(newObject, false);
449                     currentNode.Name = newObject.Name;
450                 }
451             }
452             else // We are not in the tree already, add
453             {
454                 AddBase(parent, newObject);
455             }
456         }
457         #endregion
458
459         #region Node manipulation
460         public static int GetDirImageIndex(string t)
461         {
462             t = System.Text.RegularExpressions.Regex.Replace(t, @"folder$", "");
463             int res = frmMain.ImageNames.IndexOf("inv_folder_" + t);
464             if (res == -1)
465             {
466                 switch (t)
467                 {
468                     case "currentoutfit":
469                     case "myoutfits":
470                         return frmMain.ImageNames.IndexOf("inv_folder_outfit");
471                     case "lsltext":
472                         return frmMain.ImageNames.IndexOf("inv_folder_script");
473                 }
474                 return frmMain.ImageNames.IndexOf("inv_folder_plain_closed");
475             }
476             return res;
477         }
478
479         public static int GetItemImageIndex(string t)
480         {
481             int res = frmMain.ImageNames.IndexOf("inv_item_" + t);
482             if (res == -1)
483             {
484                 if (t == "lsltext")
485                 {
486                     return frmMain.ImageNames.IndexOf("inv_item_script");
487                 }
488                 else if (t == "callingcard")
489                 {
490                     return frmMain.ImageNames.IndexOf("inv_item_callingcard_offline");
491                 }
492             }
493             return res;
494         }
495
496         TreeNode AddBase(TreeNode parent, InventoryBase obj)
497         {
498             if (obj is InventoryItem)
499             {
500                 return AddItem(parent, (InventoryItem)obj);
501             }
502             else
503             {
504                 return AddDir(parent, (InventoryFolder)obj);
505             }
506         }
507
508         TreeNode AddDir(TreeNode parentNode, InventoryFolder f)
509         {
510             TreeNode dirNode = new TreeNode();
511             dirNode.Name = f.UUID.ToString();
512             dirNode.Text = f.Name;
513             dirNode.Tag = f;
514             dirNode.ImageIndex = GetDirImageIndex(f.PreferredType.ToString().ToLower());
515             dirNode.SelectedImageIndex = dirNode.ImageIndex;
516             if (parentNode == null)
517             {
518                 invTree.Nodes.Add(dirNode);
519             }
520             else
521             {
522                 parentNode.Nodes.Add(dirNode);
523             }
524             lock (UUID2NodeCache)
525             {
526                 UUID2NodeCache[f.UUID] = dirNode;
527             }
528             return dirNode;
529         }
530
531
532         TreeNode AddItem(TreeNode parent, InventoryItem item)
533         {
534             TreeNode itemNode = new TreeNode();
535             itemNode.Name = item.UUID.ToString();
536             itemNode.Text = ItemLabel(item, false);
537             itemNode.Tag = item;
538             int img = -1;
539             InventoryItem linkedItem = null;
540
541             if (item.IsLink() && Inventory.Contains(item.AssetUUID) && Inventory[item.AssetUUID] is InventoryItem)
542             {
543                 linkedItem = (InventoryItem)Inventory[item.AssetUUID];
544             }
545             else
546             {
547                 linkedItem = item;
548             }
549
550             if (linkedItem is InventoryWearable)
551             {
552                 InventoryWearable w = linkedItem as InventoryWearable;
553                 img = GetItemImageIndex(w.WearableType.ToString().ToLower());
554             }
555             else
556             {
557                 img = GetItemImageIndex(linkedItem.AssetType.ToString().ToLower());
558             }
559
560             itemNode.ImageIndex = img;
561             itemNode.SelectedImageIndex = img;
562             parent.Nodes.Add(itemNode);
563             lock (UUID2NodeCache)
564             {
565                 UUID2NodeCache[item.UUID] = itemNode;
566             }
567             return itemNode;
568         }
569
570         TreeNode findNodeForItem(UUID itemID)
571         {
572             lock (UUID2NodeCache)
573             {
574                 if (UUID2NodeCache.ContainsKey(itemID))
575                 {
576                     return UUID2NodeCache[itemID];
577                 }
578             }
579             return null;
580         }
581
582         void cacheNode(TreeNode node)
583         {
584             InventoryBase item = (InventoryBase)node.Tag;
585             if (item != null)
586             {
587                 foreach (TreeNode child in node.Nodes)
588                 {
589                     cacheNode(child);
590                 }
591                 lock (UUID2NodeCache)
592                 {
593                     UUID2NodeCache[item.UUID] = node;
594                 }
595             }
596         }
597
598         void removeNode(TreeNode node)
599         {
600             InventoryBase item = (InventoryBase)node.Tag;
601             if (item != null)
602             {
603                 foreach (TreeNode child in node.Nodes)
604                 {
605                     removeNode(child);
606                 }
607
608                 lock (UUID2NodeCache)
609                 {
610                     UUID2NodeCache.Remove(item.UUID);
611                 }
612             }
613             node.Remove();
614         }
615
616         #endregion
617
618         #region Private methods
619         private void UpdateStatus(string text)
620         {
621             if (InvokeRequired)
622             {
623                 Invoke(new MethodInvoker(delegate() { UpdateStatus(text); }));
624                 return;
625             }
626
627             if (text == "OK")
628             {
629                 saveAllTToolStripMenuItem.Enabled = true;
630             }
631
632             tlabelStatus.Text = text;
633         }
634
635         private void UpdateNodeLabel(UUID itemID)
636         {
637             if (instance.MainForm.InvokeRequired)
638             {
639                 instance.MainForm.BeginInvoke(new MethodInvoker(() => UpdateNodeLabel(itemID)));
640                 return;
641             }
642
643             TreeNode node = findNodeForItem(itemID);
644             if (node != null)
645             {
646                 node.Text = ItemLabel((InventoryBase)node.Tag, false);
647             }
648         }
649
650         private void AddFolderFromStore(TreeNode parent, InventoryFolder f)
651         {
652             List<InventoryBase> contents = Inventory.GetContents(f);
653             foreach (InventoryBase item in contents)
654             {
655                 TreeNode node = AddBase(parent, item);
656                 if (item is InventoryFolder)
657                 {
658                     AddFolderFromStore(node, (InventoryFolder)item);
659                 }
660             }
661         }
662
663         private void TraverseAndQueueNodes(InventoryNode start)
664         {
665             bool has_items = false;
666
667             foreach (InventoryNode node in start.Nodes.Values)
668             {
669                 if (node.Data is InventoryItem)
670                 {
671                     has_items = true;
672                     break;
673                 }
674             }
675
676             if (!has_items || start.NeedsUpdate)
677             {
678                 lock (QueuedFolders)
679                 {
680                     lock (FolderFetchRetries)
681                     {
682                         int retries = 0;
683                         FolderFetchRetries.TryGetValue(start.Data.UUID, out retries);
684                         if (retries < 3)
685                         {
686                             if (!QueuedFolders.Contains(start.Data.UUID))
687                             {
688                                 QueuedFolders.Add(start.Data.UUID);
689                             }
690                         }
691                         FolderFetchRetries[start.Data.UUID] = retries + 1;
692                     }
693                 }
694             }
695
696             foreach (InventoryBase item in Inventory.GetContents((InventoryFolder)start.Data))
697             {
698                 if (item is InventoryFolder)
699                 {
700                     TraverseAndQueueNodes(Inventory.GetNodeFor(item.UUID));
701                 }
702             }
703         }
704
705         private void TraverseNodes(InventoryNode start)
706         {
707             bool has_items = false;
708
709             foreach (InventoryNode node in start.Nodes.Values)
710             {
711                 if (node.Data is InventoryItem)
712                 {
713                     has_items = true;
714                     break;
715                 }
716             }
717
718             if (!has_items || start.NeedsUpdate)
719             {
720                 InventoryFolder f = (InventoryFolder)start.Data;
721                 AutoResetEvent gotFolderEvent = new AutoResetEvent(false);
722                 bool success = false;
723
724                 EventHandler<FolderUpdatedEventArgs> callback = delegate(object sender, FolderUpdatedEventArgs ea)
725                 {
726                     if (f.UUID == ea.FolderID)
727                     {
728                         if (((InventoryFolder)Inventory.Items[ea.FolderID].Data).DescendentCount <= Inventory.Items[ea.FolderID].Nodes.Count)
729                         {
730                             success = true;
731                             gotFolderEvent.Set();
732                         }
733                     }
734                 };
735
736                 client.Inventory.FolderUpdated += callback;
737                 fetchFolder(f.UUID, f.OwnerID, true);
738                 gotFolderEvent.WaitOne(30 * 1000, false);
739                 client.Inventory.FolderUpdated -= callback;
740
741                 if (!success)
742                 {
743                     Logger.Log(string.Format("Failed fetching folder {0}, got {1} items out of {2}", f.Name, Inventory.Items[f.UUID].Nodes.Count, ((InventoryFolder)Inventory.Items[f.UUID].Data).DescendentCount), Helpers.LogLevel.Error, client);
744                 }
745             }
746
747             foreach (InventoryBase item in Inventory.GetContents((InventoryFolder)start.Data))
748             {
749                 if (item is InventoryFolder)
750                 {
751                     TraverseNodes(Inventory.GetNodeFor(item.UUID));
752                 }
753             }
754         }
755
756         private void StartTraverseNodes()
757         {
758             if (!client.Network.CurrentSim.Caps.IsEventQueueRunning)
759             {
760                 AutoResetEvent EQRunning = new AutoResetEvent(false);
761                 EventHandler<EventQueueRunningEventArgs> handler = (sender, e) =>
762                     {
763                         EQRunning.Set();
764                     };
765                 client.Network.EventQueueRunning += handler;
766                 EQRunning.WaitOne(10 * 1000, false);
767                 client.Network.EventQueueRunning -= handler;
768             }
769
770             if (!client.Network.CurrentSim.Caps.IsEventQueueRunning)
771             {
772                 return;
773             }
774
775             UpdateStatus("Loading...");
776             TreeUpdateInProgress = true;
777             TreeUpdateTimer.Start();
778
779             lock (FolderFetchRetries)
780             {
781                 FolderFetchRetries.Clear();
782             }
783
784             do
785             {
786                 lock (QueuedFolders)
787                 {
788                     QueuedFolders.Clear();
789                 }
790                 TraverseAndQueueNodes(Inventory.RootNode);
791                 if (QueuedFolders.Count == 0) break;
792                 Logger.DebugLog(string.Format("Queued {0} folders for update", QueuedFolders.Count));
793
794                 Parallel.ForEach<UUID>(Math.Min(QueuedFolders.Count, 6), QueuedFolders, folderID =>
795                 {
796                     bool success = false;
797
798                     AutoResetEvent gotFolder = new AutoResetEvent(false);
799                     EventHandler<FolderUpdatedEventArgs> handler = (sender, ev) =>
800                         {
801                             if (ev.FolderID == folderID)
802                             {
803                                 success = ev.Success;
804                                 gotFolder.Set();
805                             }
806                         };
807
808                     client.Inventory.FolderUpdated += handler;
809                     client.Inventory.RequestFolderContents(folderID, client.Self.AgentID, true, true, InventorySortOrder.ByDate);
810                     if (!gotFolder.WaitOne(15 * 1000, false))
811                     {
812                         success = false;
813                     }
814                     client.Inventory.FolderUpdated -= handler;
815                 });
816             }
817             while (QueuedFolders.Count > 0);
818
819             TreeUpdateTimer.Stop();
820             if (IsHandleCreated)
821             {
822                 Invoke(new MethodInvoker(() => TreeUpdateTimerTick(null, null)));
823             }
824             TreeUpdateInProgress = false;
825             UpdateStatus("OK");
826             instance.TabConsole.DisplayNotificationInChat("Inventory update completed.");
827
828             // Updated labels on clothes that we are wearing
829             UpdateWornLabels();
830
831             // Update attachments now that we are done
832             lock (attachments)
833             {
834                 foreach (AttachmentInfo a in attachments.Values)
835                 {
836                     if (a.Item == null)
837                     {
838                         if (Inventory.Contains(a.InventoryID))
839                         {
840                             a.MarkedAttached = true;
841                             a.Item = (InventoryItem)Inventory[a.InventoryID];
842                             UpdateNodeLabel(a.InventoryID);
843                         }
844                         else
845                         {
846                             client.Inventory.RequestFetchInventory(a.InventoryID, client.Self.AgentID);
847                             return;
848                         }
849                     }
850                 }
851             }
852
853             Logger.Log("Finished updating invenory folders, saving cache...", Helpers.LogLevel.Debug, client);
854             WorkPool.QueueUserWorkItem((object state) => Inventory.SaveToDisk(instance.InventoryCacheFileName));
855
856             if (!instance.MonoRuntime || IsHandleCreated)
857                 Invoke(new MethodInvoker(() =>
858                     {
859                         invTree.Sort();
860                     }
861             ));
862
863
864         }
865
866         public void ReloadInventory()
867         {
868             if (TreeUpdateInProgress)
869             {
870                 TreeUpdateTimer.Stop();
871                 InventoryUpdate.Abort();
872                 InventoryUpdate = null;
873             }
874
875             saveAllTToolStripMenuItem.Enabled = false;
876
877             Inventory.Items = new Dictionary<UUID, InventoryNode>();
878             Inventory.RootFolder = Inventory.RootFolder;
879
880             invTree.Nodes.Clear();
881             UUID2NodeCache.Clear();
882             invRootNode = AddDir(null, Inventory.RootFolder);
883             Inventory.RootNode.NeedsUpdate = true;
884
885             InventoryUpdate = new Thread(new ThreadStart(StartTraverseNodes));
886             InventoryUpdate.Name = "InventoryUpdate";
887             InventoryUpdate.IsBackground = true;
888             InventoryUpdate.Start();
889             invRootNode.Expand();
890         }
891
892         private void reloadInventoryToolStripMenuItem_Click(object sender, EventArgs e)
893         {
894             ReloadInventory();
895         }
896
897         private void TreeUpdateTimerTick(Object sender, EventArgs e)
898         {
899             lock (ItemsToAdd)
900             {
901                 if (ItemsToAdd.Count > 0)
902                 {
903                     invTree.BeginUpdate();
904                     while (ItemsToAdd.Count > 0)
905                     {
906                         InventoryBase item = ItemsToAdd.Dequeue();
907                         TreeNode node = findNodeForItem(item.ParentUUID);
908                         if (node != null)
909                         {
910                             AddBase(node, item);
911                         }
912                     }
913                     invTree.EndUpdate();
914                 }
915             }
916
917             lock (ItemsToUpdate)
918             {
919                 if (ItemsToUpdate.Count > 0)
920                 {
921                     invTree.BeginUpdate();
922                     while (ItemsToUpdate.Count > 0)
923                     {
924                         InventoryBase item = ItemsToUpdate.Dequeue();
925                         Exec_OnInventoryObjectUpdated(item, item);
926                     }
927                     invTree.EndUpdate();
928                 }
929             }
930
931             UpdateStatus("Loading... " + UUID2NodeCache.Count.ToString() + " items");
932         }
933
934         #endregion
935
936         private void btnProfile_Click(object sender, EventArgs e)
937         {
938             instance.MainForm.ShowAgentProfile(txtCreator.Text, txtCreator.AgentID);
939         }
940
941         void UpdateItemInfo(InventoryItem item)
942         {
943             foreach (Control c in pnlDetail.Controls)
944             {
945                 c.Dispose();
946             }
947             pnlDetail.Controls.Clear();
948             pnlItemProperties.Tag = item;
949
950             if (item == null)
951             {
952                 pnlItemProperties.Visible = false;
953                 return;
954             }
955
956             pnlItemProperties.Visible = true;
957             btnProfile.Enabled = true;
958             txtItemName.Text = item.Name;
959             txtItemDescription.Text = item.Description;
960             txtCreator.AgentID = item.CreatorID;
961             txtCreator.Tag = item.CreatorID;
962             txtCreated.Text = item.CreationDate.ToString();
963
964             if (item.AssetUUID != UUID.Zero)
965             {
966                 txtAssetID.Text = item.AssetUUID.ToString();
967             }
968             else
969             {
970                 txtAssetID.Text = String.Empty;
971             }
972
973             txtInvID.Text = item.UUID.ToString();
974
975             Permissions p = item.Permissions;
976             cbOwnerModify.Checked = (p.OwnerMask & PermissionMask.Modify) != 0;
977             cbOwnerCopy.Checked = (p.OwnerMask & PermissionMask.Copy) != 0;
978             cbOwnerTransfer.Checked = (p.OwnerMask & PermissionMask.Transfer) != 0;
979
980             cbNextOwnModify.CheckedChanged -= cbNextOwnerUpdate_CheckedChanged;
981             cbNextOwnCopy.CheckedChanged -= cbNextOwnerUpdate_CheckedChanged;
982             cbNextOwnTransfer.CheckedChanged -= cbNextOwnerUpdate_CheckedChanged;
983
984             cbNextOwnModify.Checked = (p.NextOwnerMask & PermissionMask.Modify) != 0;
985             cbNextOwnCopy.Checked = (p.NextOwnerMask & PermissionMask.Copy) != 0;
986             cbNextOwnTransfer.Checked = (p.NextOwnerMask & PermissionMask.Transfer) != 0;
987
988             cbNextOwnModify.CheckedChanged += cbNextOwnerUpdate_CheckedChanged;
989             cbNextOwnCopy.CheckedChanged += cbNextOwnerUpdate_CheckedChanged;
990             cbNextOwnTransfer.CheckedChanged += cbNextOwnerUpdate_CheckedChanged;
991
992
993             switch (item.AssetType)
994             {
995                 case AssetType.Texture:
996                     SLImageHandler image = new SLImageHandler(instance, item.AssetUUID, item.Name, IsFullPerm(item));
997                     image.Dock = DockStyle.Fill;
998                     pnlDetail.Controls.Add(image);
999                     break;
1000
1001                 case AssetType.Notecard:
1002                     Notecard note = new Notecard(instance, (InventoryNotecard)item);
1003                     note.Dock = DockStyle.Fill;
1004                     note.TabIndex = 3;
1005                     note.TabStop = true;
1006                     pnlDetail.Controls.Add(note);
1007                     note.rtbContent.Focus();
1008                     break;
1009
1010                 case AssetType.Landmark:
1011                     Landmark landmark = new Landmark(instance, (InventoryLandmark)item);
1012                     landmark.Dock = DockStyle.Fill;
1013                     pnlDetail.Controls.Add(landmark);
1014                     break;
1015
1016                 case AssetType.LSLText:
1017                     ScriptEditor script = new ScriptEditor(instance, (InventoryLSL)item);
1018                     script.Dock = DockStyle.Fill;
1019                     script.TabIndex = 3;
1020                     script.TabStop = true;
1021                     pnlDetail.Controls.Add(script);
1022                     break;
1023
1024                 case AssetType.Gesture:
1025                     Guesture gesture = new Guesture(instance, (InventoryGesture)item);
1026                     gesture.Dock = DockStyle.Fill;
1027                     pnlDetail.Controls.Add(gesture);
1028                     break;
1029
1030             }
1031
1032             tabsInventory.SelectedTab = tabDetail;
1033         }
1034
1035         void cbNextOwnerUpdate_CheckedChanged(object sender, EventArgs e)
1036         {
1037             InventoryItem item = null;
1038             if (pnlItemProperties.Tag != null && pnlItemProperties.Tag is InventoryItem)
1039             {
1040                 item = (InventoryItem)pnlItemProperties.Tag;
1041             }
1042             if (item == null) return;
1043
1044             PermissionMask pm = PermissionMask.Move;
1045             if (cbNextOwnCopy.Checked) pm |= PermissionMask.Copy;
1046             if (cbNextOwnModify.Checked) pm |= PermissionMask.Modify;
1047             if (cbNextOwnTransfer.Checked) pm |= PermissionMask.Transfer;
1048             item.Permissions.NextOwnerMask = pm;
1049
1050             client.Inventory.RequestUpdateItem(item);
1051             client.Inventory.RequestFetchInventory(item.UUID, item.OwnerID);
1052         }
1053
1054         private void txtItemName_Leave(object sender, EventArgs e)
1055         {
1056             InventoryItem item = null;
1057             if (pnlItemProperties.Tag != null && pnlItemProperties.Tag is InventoryItem)
1058             {
1059                 item = (InventoryItem)pnlItemProperties.Tag;
1060             }
1061             if (item == null) return;
1062
1063             item.Name = txtItemName.Text;
1064
1065             client.Inventory.RequestUpdateItem(item);
1066             client.Inventory.RequestFetchInventory(item.UUID, item.OwnerID);
1067         }
1068
1069         private void txtItemDescription_Leave(object sender, EventArgs e)
1070         {
1071             InventoryItem item = null;
1072             if (pnlItemProperties.Tag != null && pnlItemProperties.Tag is InventoryItem)
1073             {
1074                 item = (InventoryItem)pnlItemProperties.Tag;
1075             }
1076             if (item == null) return;
1077
1078             item.Description = txtItemDescription.Text;
1079
1080             client.Inventory.RequestUpdateItem(item);
1081             client.Inventory.RequestFetchInventory(item.UUID, item.OwnerID);
1082         }
1083
1084         void invTree_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
1085         {
1086             if (invTree.SelectedNode.Tag is InventoryItem)
1087             {
1088                 InventoryItem item = invTree.SelectedNode.Tag as InventoryItem;
1089                 switch (item.AssetType)
1090                 {
1091
1092                     case AssetType.Landmark:
1093                         instance.TabConsole.DisplayNotificationInChat("Teleporting to " + item.Name);
1094                         client.Self.RequestTeleport(item.AssetUUID);
1095                         break;
1096
1097                     case AssetType.Gesture:
1098                         client.Self.PlayGesture(item.AssetUUID);
1099                         break;
1100
1101                     case AssetType.Notecard:
1102                         Notecard note = new Notecard(instance, (InventoryNotecard)item);
1103                         note.Dock = DockStyle.Fill;
1104                         note.ShowDetached();
1105                         break;
1106
1107                     case AssetType.LSLText:
1108                         ScriptEditor script = new ScriptEditor(instance, (InventoryLSL)item);
1109                         script.Dock = DockStyle.Fill;
1110                         script.ShowDetached();
1111                         break;
1112                 }
1113             }
1114         }
1115
1116         private void fetchFolder(UUID folderID, UUID ownerID, bool force)
1117         {
1118             if (force || !fetchedFolders.Contains(folderID))
1119             {
1120                 if (!fetchedFolders.Contains(folderID))
1121                 {
1122                     fetchedFolders.Add(folderID);
1123                 }
1124
1125                 client.Inventory.RequestFolderContents(folderID, ownerID, true, true, InventorySortOrder.ByDate);
1126             }
1127         }
1128
1129         public bool IsWorn(InventoryItem item)
1130         {
1131             bool worn = client.Appearance.IsItemWorn(item) != WearableType.Invalid;
1132
1133             lock (WornItems)
1134             {
1135                 if (worn && !WornItems.Contains(item.UUID))
1136                     WornItems.Add(item.UUID);
1137             }
1138             return worn;
1139         }
1140
1141         public AttachmentPoint AttachedTo(InventoryItem item)
1142         {
1143             lock (attachments)
1144             {
1145                 if (attachments.ContainsKey(item.UUID))
1146                 {
1147                     return attachments[item.UUID].Point;
1148                 }
1149             }
1150
1151             return AttachmentPoint.Default;
1152         }
1153
1154         public bool IsAttached(InventoryItem item)
1155         {
1156             List<Primitive> myAtt = client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive p) => p.ParentID == client.Self.LocalID);
1157             foreach (Primitive prim in myAtt)
1158             {
1159                 if (prim.NameValues == null) continue;
1160                 UUID invID = UUID.Zero;
1161                 for (int i = 0; i < prim.NameValues.Length; i++)
1162                 {
1163                     if (prim.NameValues[i].Name == "AttachItemID")
1164                     {
1165                         invID = (UUID)prim.NameValues[i].Value.ToString();
1166                         break;
1167                     }
1168                 }
1169                 if (invID == item.UUID)
1170                 {
1171                     lock (attachments)
1172                     {
1173                         AttachmentInfo inf = new AttachmentInfo();
1174                         inf.InventoryID = item.UUID;
1175                         inf.Item = item;
1176                         inf.MarkedAttached = true;
1177                         inf.Prim = prim;
1178                         inf.PrimID = prim.ID;
1179                         attachments[invID] = inf;
1180                     }
1181                     return true;
1182                 }
1183             }
1184
1185             return false;
1186         }
1187
1188         public InventoryItem AttachmentAt(AttachmentPoint point)
1189         {
1190             lock (attachments)
1191             {
1192                 foreach (KeyValuePair<UUID, AttachmentInfo> att in attachments)
1193                 {
1194                     if (att.Value.Point == point)
1195                     {
1196                         return att.Value.Item;
1197                     }
1198                 }
1199             }
1200             return null;
1201         }
1202
1203         /// <summary>
1204         /// Returns text of the label
1205         /// </summary>
1206         /// <param name="invBase">Inventory item</param>
1207         /// <param name="returnRaw">Should we return raw text, or if false decorated text with (worn) info, and (no copy) etc. permission info</param>
1208         /// <returns></returns>
1209         public string ItemLabel(InventoryBase invBase, bool returnRaw)
1210         {
1211             if (returnRaw || (invBase is InventoryFolder))
1212                 return invBase.Name;
1213
1214             InventoryItem item = (InventoryItem)invBase;
1215
1216             string raw = item.Name;
1217
1218             if (item.IsLink())
1219             {
1220                 raw += " (link)";
1221                 item = instance.COF.RealInventoryItem(item);
1222                 if (Inventory.Contains(item.AssetUUID) && Inventory[item.AssetUUID] is InventoryItem)
1223                 {
1224                     item = (InventoryItem)Inventory[item.AssetUUID];
1225                 }
1226             }
1227
1228             if ((item.Permissions.OwnerMask & PermissionMask.Modify) == 0)
1229                 raw += " (no modify)";
1230
1231             if ((item.Permissions.OwnerMask & PermissionMask.Copy) == 0)
1232                 raw += " (no copy)";
1233
1234             if ((item.Permissions.OwnerMask & PermissionMask.Transfer) == 0)
1235                 raw += " (no transfer)";
1236
1237             if (IsWorn(item))
1238                 raw += " (worn)";
1239
1240             if (IsAttached(item))
1241             {
1242                 raw += " (worn on " + AttachedTo(item).ToString() + ")";
1243             }
1244
1245             return raw;
1246         }
1247
1248         public static bool IsFullPerm(InventoryItem item)
1249         {
1250             if (
1251                 ((item.Permissions.OwnerMask & PermissionMask.Modify) != 0) &&
1252                 ((item.Permissions.OwnerMask & PermissionMask.Copy) != 0) &&
1253                 ((item.Permissions.OwnerMask & PermissionMask.Transfer) != 0)
1254                 )
1255             {
1256                 return true;
1257             }
1258             else
1259             {
1260                 return false;
1261             }
1262         }
1263
1264         void invTree_MouseClick(object sender, TreeNodeMouseClickEventArgs e)
1265         {
1266             TreeNode node = e.Node;
1267
1268             if (e.Button == MouseButtons.Left)
1269             {
1270                 invTree.SelectedNode = node;
1271                 if (node.Tag is InventoryItem)
1272                 {
1273                     UpdateItemInfo(instance.COF.RealInventoryItem(node.Tag as InventoryItem));
1274                 }
1275                 else
1276                 {
1277                     UpdateItemInfo(null);
1278                 }
1279             }
1280             else if (e.Button == MouseButtons.Right)
1281             {
1282                 invTree.SelectedNode = node;
1283                 ctxInv.Show(invTree, e.X, e.Y);
1284             }
1285         }
1286
1287         private void ctxInv_Opening(object sender, CancelEventArgs e)
1288         {
1289             e.Cancel = false;
1290             TreeNode node = invTree.SelectedNode;
1291             if (node == null)
1292             {
1293                 e.Cancel = true;
1294             }
1295             else
1296             {
1297                 #region Folder context menu
1298                 if (node.Tag is InventoryFolder)
1299                 {
1300                     InventoryFolder folder = (InventoryFolder)node.Tag;
1301                     ctxInv.Items.Clear();
1302
1303                     ToolStripMenuItem ctxItem;
1304
1305                     if ((int)folder.PreferredType >= (int)AssetType.EnsembleStart &&
1306                         (int)folder.PreferredType <= (int)AssetType.EnsembleEnd)
1307                     {
1308                         ctxItem = new ToolStripMenuItem("Fix type", null, OnInvContextClick);
1309                         ctxItem.Name = "fix_type";
1310                         ctxInv.Items.Add(ctxItem);
1311                         ctxInv.Items.Add(new ToolStripSeparator());
1312                     }
1313
1314                     ctxItem = new ToolStripMenuItem("New Folder", null, OnInvContextClick);
1315                     ctxItem.Name = "new_folder";
1316                     ctxInv.Items.Add(ctxItem);
1317
1318                     ctxItem = new ToolStripMenuItem("New Note", null, OnInvContextClick);
1319                     ctxItem.Name = "new_notecard";
1320                     ctxInv.Items.Add(ctxItem);
1321
1322                     ctxItem = new ToolStripMenuItem("New Script", null, OnInvContextClick);
1323                     ctxItem.Name = "new_script";
1324                     ctxInv.Items.Add(ctxItem);
1325
1326                     ctxItem = new ToolStripMenuItem("Refresh", null, OnInvContextClick);
1327                     ctxItem.Name = "refresh";
1328                     ctxInv.Items.Add(ctxItem);
1329
1330                     ctxItem = new ToolStripMenuItem("Backup...", null, OnInvContextClick);
1331                     ctxItem.Name = "backup";
1332                     ctxInv.Items.Add(ctxItem);
1333
1334                     ctxInv.Items.Add(new ToolStripSeparator());
1335
1336                     ctxItem = new ToolStripMenuItem("Expand", null, OnInvContextClick);
1337                     ctxItem.Name = "expand";
1338                     ctxInv.Items.Add(ctxItem);
1339
1340                     ctxItem = new ToolStripMenuItem("Expand All", null, OnInvContextClick);
1341                     ctxItem.Name = "expand_all";
1342                     ctxInv.Items.Add(ctxItem);
1343
1344                     ctxItem = new ToolStripMenuItem("Collapse", null, OnInvContextClick);
1345                     ctxItem.Name = "collapse";
1346                     ctxInv.Items.Add(ctxItem);
1347
1348                     if (folder.PreferredType == AssetType.TrashFolder)
1349                     {
1350                         ctxItem = new ToolStripMenuItem("Empty Trash", null, OnInvContextClick);
1351                         ctxItem.Name = "empty_trash";
1352                         ctxInv.Items.Add(ctxItem);
1353                     }
1354
1355                     if (folder.PreferredType == AssetType.LostAndFoundFolder)
1356                     {
1357                         ctxItem = new ToolStripMenuItem("Empty Lost and Found", null, OnInvContextClick);
1358                         ctxItem.Name = "empty_lost_found";
1359                         ctxInv.Items.Add(ctxItem);
1360                     }
1361
1362                     if (folder.PreferredType == AssetType.Unknown ||
1363                         folder.PreferredType == AssetType.OutfitFolder)
1364                     {
1365                         ctxItem = new ToolStripMenuItem("Rename", null, OnInvContextClick);
1366                         ctxItem.Name = "rename_folder";
1367                         ctxInv.Items.Add(ctxItem);
1368
1369                         ctxInv.Items.Add(new ToolStripSeparator());
1370
1371                         ctxItem = new ToolStripMenuItem("Cut", null, OnInvContextClick);
1372                         ctxItem.Name = "cut_folder";
1373                         ctxInv.Items.Add(ctxItem);
1374
1375                         ctxItem = new ToolStripMenuItem("Copy", null, OnInvContextClick);
1376                         ctxItem.Name = "copy_folder";
1377                         ctxInv.Items.Add(ctxItem);
1378                     }
1379
1380                     if (instance.InventoryClipboard != null)
1381                     {
1382                         ctxItem = new ToolStripMenuItem("Paste", null, OnInvContextClick);
1383                         ctxItem.Name = "paste_folder";
1384                         ctxInv.Items.Add(ctxItem);
1385
1386                         if (instance.InventoryClipboard.Item is InventoryItem)
1387                         {
1388                             ctxItem = new ToolStripMenuItem("Paste as Link", null, OnInvContextClick);
1389                             ctxItem.Name = "paste_folder_link";
1390                             ctxInv.Items.Add(ctxItem);
1391                         }
1392                     }
1393
1394                     if (folder.PreferredType == AssetType.Unknown ||
1395                         folder.PreferredType == AssetType.OutfitFolder)
1396                     {
1397                         ctxItem = new ToolStripMenuItem("Delete", null, OnInvContextClick);
1398                         ctxItem.Name = "delete_folder";
1399                         ctxInv.Items.Add(ctxItem);
1400
1401                         ctxInv.Items.Add(new ToolStripSeparator());
1402                     }
1403
1404                     if (folder.PreferredType == AssetType.Unknown || folder.PreferredType == AssetType.OutfitFolder)
1405                     {
1406                         ctxItem = new ToolStripMenuItem("Take off Items", null, OnInvContextClick);
1407                         ctxItem.Name = "outfit_take_off";
1408                         ctxInv.Items.Add(ctxItem);
1409
1410                         ctxItem = new ToolStripMenuItem("Add to Outfit", null, OnInvContextClick);
1411                         ctxItem.Name = "outfit_add";
1412                         ctxInv.Items.Add(ctxItem);
1413
1414                         ctxItem = new ToolStripMenuItem("Replace Outfit", null, OnInvContextClick);
1415                         ctxItem.Name = "outfit_replace";
1416                         ctxInv.Items.Add(ctxItem);
1417                     }
1418
1419                     instance.ContextActionManager.AddContributions(ctxInv, folder);
1420                 #endregion Folder context menu
1421                 }
1422                 else if (node.Tag is InventoryItem)
1423                 {
1424                     #region Item context menu
1425                     InventoryItem item = instance.COF.RealInventoryItem((InventoryItem)node.Tag);
1426                     ctxInv.Items.Clear();
1427
1428                     ToolStripMenuItem ctxItem;
1429
1430                     if (item.InventoryType == InventoryType.LSL)
1431                     {
1432                         ctxItem = new ToolStripMenuItem("Edit script", null, OnInvContextClick);
1433                         ctxItem.Name = "edit_script";
1434                         ctxInv.Items.Add(ctxItem);
1435                     }
1436
1437                     if (item.AssetType == AssetType.Texture)
1438                     {
1439                         ctxItem = new ToolStripMenuItem("View", null, OnInvContextClick);
1440                         ctxItem.Name = "view_image";
1441                         ctxInv.Items.Add(ctxItem);
1442                     }
1443
1444                     if (item.InventoryType == InventoryType.Landmark)
1445                     {
1446                         ctxItem = new ToolStripMenuItem("Teleport", null, OnInvContextClick);
1447                         ctxItem.Name = "lm_teleport";
1448                         ctxInv.Items.Add(ctxItem);
1449
1450                         ctxItem = new ToolStripMenuItem("Info", null, OnInvContextClick);
1451                         ctxItem.Name = "lm_info";
1452                         ctxInv.Items.Add(ctxItem);
1453                     }
1454
1455                     if (item.InventoryType == InventoryType.Notecard)
1456                     {
1457                         ctxItem = new ToolStripMenuItem("Open", null, OnInvContextClick);
1458                         ctxItem.Name = "notecard_open";
1459                         ctxInv.Items.Add(ctxItem);
1460                     }
1461
1462                     if (item.InventoryType == InventoryType.Gesture)
1463                     {
1464                         ctxItem = new ToolStripMenuItem("Play", null, OnInvContextClick);
1465                         ctxItem.Name = "gesture_play";
1466                         ctxInv.Items.Add(ctxItem);
1467
1468                         ctxItem = new ToolStripMenuItem("Info", null, OnInvContextClick);
1469                         ctxItem.Name = "gesture_info";
1470                         ctxInv.Items.Add(ctxItem);
1471                     }
1472
1473                     if (item.InventoryType == InventoryType.Animation)
1474                     {
1475                         if (!client.Self.SignaledAnimations.ContainsKey(item.AssetUUID))
1476                         {
1477                             ctxItem = new ToolStripMenuItem("Play", null, OnInvContextClick);
1478                             ctxItem.Name = "animation_play";
1479                             ctxInv.Items.Add(ctxItem);
1480                         }
1481                         else
1482                         {
1483                             ctxItem = new ToolStripMenuItem("Stop", null, OnInvContextClick);
1484                             ctxItem.Name = "animation_stop";
1485                             ctxInv.Items.Add(ctxItem);
1486                         }
1487                     }
1488
1489                     if (item.InventoryType == InventoryType.Object)
1490                     {
1491                         ctxItem = new ToolStripMenuItem("Rez inworld", null, OnInvContextClick);
1492                         ctxItem.Name = "rez_inworld";
1493                         ctxInv.Items.Add(ctxItem);
1494                     }
1495
1496                     ctxItem = new ToolStripMenuItem("Rename", null, OnInvContextClick);
1497                     ctxItem.Name = "rename_item";
1498                     ctxInv.Items.Add(ctxItem);
1499
1500                     ctxInv.Items.Add(new ToolStripSeparator());
1501
1502                     ctxItem = new ToolStripMenuItem("Cut", null, OnInvContextClick);
1503                     ctxItem.Name = "cut_item";
1504                     ctxInv.Items.Add(ctxItem);
1505
1506                     ctxItem = new ToolStripMenuItem("Copy", null, OnInvContextClick);
1507                     ctxItem.Name = "copy_item";
1508                     ctxInv.Items.Add(ctxItem);
1509
1510                     if (instance.InventoryClipboard != null)
1511                     {
1512                         ctxItem = new ToolStripMenuItem("Paste", null, OnInvContextClick);
1513                         ctxItem.Name = "paste_item";
1514                         ctxInv.Items.Add(ctxItem);
1515
1516                         if (instance.InventoryClipboard.Item is InventoryItem)
1517                         {
1518                             ctxItem = new ToolStripMenuItem("Paste as Link", null, OnInvContextClick);
1519                             ctxItem.Name = "paste_item_link";
1520                             ctxInv.Items.Add(ctxItem);
1521                         }
1522                     }
1523
1524                     ctxItem = new ToolStripMenuItem("Delete", null, OnInvContextClick);
1525                     ctxItem.Name = "delete_item";
1526
1527                     if (IsAttached(item) || IsWorn(item))
1528                     {
1529                         ctxItem.Enabled = false;
1530                     }
1531                     ctxInv.Items.Add(ctxItem);
1532
1533                     if (IsAttached(item) && instance.RLV.AllowDetach(attachments[item.UUID]))
1534                     {
1535                         ctxItem = new ToolStripMenuItem("Detach from yourself", null, OnInvContextClick);
1536                         ctxItem.Name = "detach";
1537                         ctxInv.Items.Add(ctxItem);
1538                     }
1539
1540                     if (!IsAttached(item) && (item.InventoryType == InventoryType.Object || item.InventoryType == InventoryType.Attachment))
1541                     {
1542                         ToolStripMenuItem ctxItemAttach = new ToolStripMenuItem("Attach to");
1543                         ctxInv.Items.Add(ctxItemAttach);
1544
1545                         ToolStripMenuItem ctxItemAttachHUD = new ToolStripMenuItem("Attach to HUD");
1546                         ctxInv.Items.Add(ctxItemAttachHUD);
1547
1548                         foreach (AttachmentPoint pt in Enum.GetValues(typeof(AttachmentPoint)))
1549                         {
1550                             if (!pt.ToString().StartsWith("HUD"))
1551                             {
1552                                 string name = Utils.EnumToText(pt);
1553
1554                                 InventoryItem alreadyAttached = null;
1555                                 if ((alreadyAttached = AttachmentAt(pt)) != null)
1556                                 {
1557                                     name += " (" + alreadyAttached.Name + ")";
1558                                 }
1559
1560                                 ToolStripMenuItem ptItem = new ToolStripMenuItem(name, null, OnInvContextClick);
1561                                 ptItem.Name = pt.ToString();
1562                                 ptItem.Tag = pt;
1563                                 ptItem.Name = "attach_to";
1564                                 ctxItemAttach.DropDownItems.Add(ptItem);
1565                             }
1566                             else
1567                             {
1568                                 string name = Utils.EnumToText(pt).Substring(3);
1569
1570                                 InventoryItem alreadyAttached = null;
1571                                 if ((alreadyAttached = AttachmentAt(pt)) != null)
1572                                 {
1573                                     name += " (" + alreadyAttached.Name + ")";
1574                                 }
1575
1576                                 ToolStripMenuItem ptItem = new ToolStripMenuItem(name, null, OnInvContextClick);
1577                                 ptItem.Name = pt.ToString();
1578                                 ptItem.Tag = pt;
1579                                 ptItem.Name = "attach_to";
1580                                 ctxItemAttachHUD.DropDownItems.Add(ptItem);
1581                             }
1582                         }
1583
1584                         ctxItem = new ToolStripMenuItem("Add to Worn", null, OnInvContextClick);
1585                         ctxItem.Name = "wear_attachment_add";
1586                         ctxInv.Items.Add(ctxItem);
1587
1588                         ctxItem = new ToolStripMenuItem("Wear", null, OnInvContextClick);
1589                         ctxItem.Name = "wear_attachment";
1590                         ctxInv.Items.Add(ctxItem);
1591                     }
1592
1593                     if (item is InventoryWearable)
1594                     {
1595                         ctxInv.Items.Add(new ToolStripSeparator());
1596
1597                         if (IsWorn(item))
1598                         {
1599                             ctxItem = new ToolStripMenuItem("Take off", null, OnInvContextClick);
1600                             ctxItem.Name = "item_take_off";
1601                             ctxInv.Items.Add(ctxItem);
1602                         }
1603                         else
1604                         {
1605                             ctxItem = new ToolStripMenuItem("Wear", null, OnInvContextClick);
1606                             ctxItem.Name = "item_wear";
1607                             ctxInv.Items.Add(ctxItem);
1608                         }
1609                     }
1610
1611                     instance.ContextActionManager.AddContributions(ctxInv, item);
1612                     #endregion Item context menu
1613                 }
1614             }
1615         }
1616
1617         #region Context menu folder
1618         private void OnInvContextClick(object sender, EventArgs e)
1619         {
1620             if (invTree.SelectedNode == null || !(invTree.SelectedNode.Tag is InventoryBase))
1621             {
1622                 return;
1623             }
1624
1625             string cmd = ((ToolStripMenuItem)sender).Name;
1626
1627             if (invTree.SelectedNode.Tag is InventoryFolder)
1628             {
1629                 #region Folder actions
1630                 InventoryFolder f = (InventoryFolder)invTree.SelectedNode.Tag;
1631
1632                 switch (cmd)
1633                 {
1634                     case "refresh":
1635                         foreach (TreeNode old in invTree.SelectedNode.Nodes)
1636                         {
1637                             if (!(old.Tag is InventoryFolder))
1638                             {
1639                                 removeNode(old);
1640                             }
1641                         }
1642                         fetchFolder(f.UUID, f.OwnerID, true);
1643                         break;
1644
1645                     case "backup":
1646                         (new InventoryBackup(instance, f.UUID)).Show();
1647                         break;
1648
1649                     case "expand":
1650                         invTree.SelectedNode.Expand();
1651                         break;
1652
1653                     case "expand_all":
1654                         invTree.SelectedNode.ExpandAll();
1655                         break;
1656
1657                     case "collapse":
1658                         invTree.SelectedNode.Collapse();
1659                         break;
1660
1661                     case "new_folder":
1662                         newItemName = "New folder";
1663                         client.Inventory.CreateFolder(f.UUID, "New folder");
1664                         break;
1665
1666                     case "fix_type":
1667                         client.Inventory.UpdateFolderProperties(f.UUID, f.ParentUUID, f.Name, AssetType.Unknown);
1668                         invTree.Sort();
1669                         break;
1670
1671                     case "new_notecard":
1672                         client.Inventory.RequestCreateItem(f.UUID, "New Note", "Radegast note: " + DateTime.Now.ToString(),
1673                             AssetType.Notecard, UUID.Zero, InventoryType.Notecard, PermissionMask.All, NotecardCreated);
1674                         break;
1675
1676                     case "new_script":
1677                         client.Inventory.RequestCreateItem(f.UUID, "New script", "Radegast script: " + DateTime.Now.ToString(),
1678                             AssetType.LSLText, UUID.Zero, InventoryType.LSL, PermissionMask.All, ScriptCreated);
1679                         break;
1680
1681                     case "cut_folder":
1682                         instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Cut, f);
1683                         break;
1684
1685                     case "copy_folder":
1686                         instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Copy, f);
1687                         break;
1688
1689                     case "paste_folder":
1690                         PerformClipboardOperation(invTree.SelectedNode.Tag as InventoryFolder);
1691                         break;
1692
1693                     case "paste_folder_link":
1694                         PerformLinkOperation(invTree.SelectedNode.Tag as InventoryFolder);
1695                         break;
1696
1697
1698                     case "delete_folder":
1699                         var trash = client.Inventory.FindFolderForType(AssetType.TrashFolder);
1700                         if (trash == Inventory.RootFolder.UUID)
1701                         {
1702                             WorkPool.QueueUserWorkItem(sync =>
1703                             {
1704                                 trashCreated.Reset();
1705                                 trash = client.Inventory.CreateFolder(Inventory.RootFolder.UUID, "Trash", AssetType.TrashFolder);
1706                                 trashCreated.WaitOne(20 * 1000, false);
1707                                 Thread.Sleep(200);
1708                                 client.Inventory.MoveFolder(f.UUID, trash, f.Name);
1709                                 return;
1710                             });
1711                         }
1712
1713                         client.Inventory.MoveFolder(f.UUID, trash, f.Name);
1714                         break;
1715
1716                     case "empty_trash":
1717                         {
1718                             DialogResult res = MessageBox.Show("Are you sure you want to empty your trash?", "Confirmation", MessageBoxButtons.OKCancel);
1719                             if (res == DialogResult.OK)
1720                             {
1721                                 client.Inventory.EmptyTrash();
1722                             }
1723                         }
1724                         break;
1725
1726                     case "empty_lost_found":
1727                         {
1728                             DialogResult res = MessageBox.Show("Are you sure you want to empty your lost and found folder?", "Confirmation", MessageBoxButtons.OKCancel);
1729                             if (res == DialogResult.OK)
1730                             {
1731                                 client.Inventory.EmptyLostAndFound();
1732                             }
1733                         }
1734                         break;
1735
1736                     case "rename_folder":
1737                         invTree.SelectedNode.BeginEdit();
1738                         break;
1739
1740                     case "outfit_replace":
1741                         List<InventoryItem> newOutfit = new List<InventoryItem>();
1742                         foreach (InventoryBase item in Inventory.GetContents(f))
1743                         {
1744                             if (item is InventoryItem)
1745                                 newOutfit.Add((InventoryItem)item);
1746                         }
1747                         appearnceWasBusy = client.Appearance.ManagerBusy;
1748                         instance.COF.ReplaceOutfit(newOutfit);
1749                         UpdateWornLabels();
1750                         break;
1751
1752                     case "outfit_add":
1753                         List<InventoryItem> addToOutfit = new List<InventoryItem>();
1754                         foreach (InventoryBase item in Inventory.GetContents(f))
1755                         {
1756                             if (item is InventoryItem)
1757                                 addToOutfit.Add((InventoryItem)item);
1758                         }
1759                         appearnceWasBusy = client.Appearance.ManagerBusy;
1760                         instance.COF.AddToOutfit(addToOutfit, true);
1761                         UpdateWornLabels();
1762                         break;
1763
1764                     case "outfit_take_off":
1765                         List<InventoryItem> removeFromOutfit = new List<InventoryItem>();
1766                         foreach (InventoryBase item in Inventory.GetContents(f))
1767                         {
1768                             if (item is InventoryItem)
1769                                 removeFromOutfit.Add((InventoryItem)item);
1770                         }
1771                         appearnceWasBusy = client.Appearance.ManagerBusy;
1772                         instance.COF.RemoveFromOutfit(removeFromOutfit);
1773                         UpdateWornLabels();
1774                         break;
1775                 }
1776                 #endregion
1777             }
1778             else if (invTree.SelectedNode.Tag is InventoryItem)
1779             {
1780                 #region Item actions
1781                 InventoryItem item = (InventoryItem)invTree.SelectedNode.Tag;
1782
1783                 // Copy, cut, and delete works on links directly
1784                 // The rest operate on the item that is pointed by the link
1785                 if (cmd != "copy_item" && cmd != "cut_item" && cmd != "delete_item")
1786                 {
1787                     item = instance.COF.RealInventoryItem(item);
1788                 }
1789
1790                 switch (cmd)
1791                 {
1792                     case "copy_item":
1793                         instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Copy, item);
1794                         break;
1795
1796                     case "cut_item":
1797                         instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Cut, item);
1798                         break;
1799
1800                     case "paste_item":
1801                         PerformClipboardOperation(invTree.SelectedNode.Parent.Tag as InventoryFolder);
1802                         break;
1803
1804                     case "paste_item_link":
1805                         PerformLinkOperation(invTree.SelectedNode.Parent.Tag as InventoryFolder);
1806                         break;
1807
1808                     case "delete_item":
1809                         var trash = client.Inventory.FindFolderForType(AssetType.TrashFolder);
1810                         if (trash == Inventory.RootFolder.UUID)
1811                         {
1812                             WorkPool.QueueUserWorkItem(sync =>
1813                             {
1814                                 trashCreated.Reset();
1815                                 trash = client.Inventory.CreateFolder(Inventory.RootFolder.UUID, "Trash", AssetType.TrashFolder);
1816                                 trashCreated.WaitOne(20 * 1000, false);
1817                                 Thread.Sleep(200);
1818                                 client.Inventory.MoveItem(item.UUID, trash, item.Name);
1819                                 return;
1820                             });
1821                         }
1822
1823                         client.Inventory.MoveItem(item.UUID, client.Inventory.FindFolderForType(AssetType.TrashFolder), item.Name);
1824                         break;
1825
1826                     case "rename_item":
1827                         invTree.SelectedNode.BeginEdit();
1828                         break;
1829
1830                     case "detach":
1831                         instance.COF.Detach(item);
1832                         lock (attachments) attachments.Remove(item.UUID);
1833                         invTree.SelectedNode.Text = ItemLabel(item, false);
1834                         break;
1835
1836                     case "wear_attachment":
1837                         instance.COF.Attach(item, AttachmentPoint.Default, true);
1838                         break;
1839
1840                     case "wear_attachment_add":
1841                         instance.COF.Attach(item, AttachmentPoint.Default, false);
1842                         break;
1843
1844                     case "attach_to":
1845                         AttachmentPoint pt = (AttachmentPoint)((ToolStripMenuItem)sender).Tag;
1846                         instance.COF.Attach(item, pt, true);
1847                         break;
1848
1849                     case "edit_script":
1850                         ScriptEditor se = new ScriptEditor(instance, (InventoryLSL)item);
1851                         se.Detached = true;
1852                         return;
1853
1854                     case "view_image":
1855                         UpdateItemInfo(item);
1856                         break;
1857
1858                     case "item_take_off":
1859                         appearnceWasBusy = client.Appearance.ManagerBusy;
1860                         instance.COF.RemoveFromOutfit(item);
1861                         invTree.SelectedNode.Text = ItemLabel(item, false);
1862                         lock (WornItems)
1863                         {
1864                             if (WornItems.Contains(item.UUID))
1865                             {
1866                                 WornItems.Remove(item.UUID);
1867                             }
1868                         }
1869                         break;
1870
1871                     case "item_wear":
1872                         appearnceWasBusy = client.Appearance.ManagerBusy;
1873                         instance.COF.AddToOutfit(item, true);
1874                         invTree.SelectedNode.Text = ItemLabel(item, false);
1875                         break;
1876
1877                     case "lm_teleport":
1878                         instance.TabConsole.DisplayNotificationInChat("Teleporting to " + item.Name);
1879                         client.Self.RequestTeleport(item.AssetUUID);
1880                         break;
1881
1882                     case "lm_info":
1883                         UpdateItemInfo(item);
1884                         break;
1885
1886                     case "notecard_open":
1887                         UpdateItemInfo(item);
1888                         break;
1889
1890                     case "gesture_info":
1891                         UpdateItemInfo(item);
1892                         break;
1893
1894                     case "gesture_play":
1895                         client.Self.PlayGesture(item.AssetUUID);
1896                         break;
1897
1898                     case "animation_play":
1899                         Dictionary<UUID, bool> anim = new Dictionary<UUID, bool>();
1900                         anim.Add(item.AssetUUID, true);
1901                         client.Self.Animate(anim, true);
1902                         break;
1903
1904                     case "animation_stop":
1905                         Dictionary<UUID, bool> animStop = new Dictionary<UUID, bool>();
1906                         animStop.Add(item.AssetUUID, false);
1907                         client.Self.Animate(animStop, true);
1908                         break;
1909
1910                     case "rez_inworld":
1911                         instance.MediaManager.PlayUISound(UISounds.ObjectRez);
1912                         Vector3 rezpos = new Vector3(2, 0, 0);
1913                         rezpos = client.Self.SimPosition + rezpos * client.Self.Movement.BodyRotation;
1914                         client.Inventory.RequestRezFromInventory(client.Network.CurrentSim, Quaternion.Identity, rezpos, item);
1915                         break;
1916                 }
1917                 #endregion
1918             }
1919         }
1920
1921         void NotecardCreated(bool success, InventoryItem item)
1922         {
1923             if (InvokeRequired)
1924             {
1925                 BeginInvoke(new MethodInvoker(() => NotecardCreated(success, item)));
1926                 return;
1927             }
1928
1929             if (!success)
1930             {
1931                 instance.TabConsole.DisplayNotificationInChat("Creation of notecard failed");
1932                 return;
1933             }
1934
1935             instance.TabConsole.DisplayNotificationInChat("New notecard created, enter notecard name and press enter", ChatBufferTextStyle.Invisible);
1936             var node = findNodeForItem(item.ParentUUID);
1937             if (node != null) node.Expand();
1938             node = findNodeForItem(item.UUID);
1939             if (node != null)
1940             {
1941                 invTree.SelectedNode = node;
1942                 node.BeginEdit();
1943             }
1944         }
1945
1946         void ScriptCreated(bool success, InventoryItem item)
1947         {
1948             if (InvokeRequired)
1949             {
1950                 BeginInvoke(new MethodInvoker(() => ScriptCreated(success, item)));
1951                 return;
1952             }
1953
1954             if (!success)
1955             {
1956                 instance.TabConsole.DisplayNotificationInChat("Creation of script failed");
1957                 return;
1958             }
1959
1960             instance.TabConsole.DisplayNotificationInChat("New script created, enter script name and press enter", ChatBufferTextStyle.Invisible);
1961             var node = findNodeForItem(item.ParentUUID);
1962             if (node != null) node.Expand();
1963             node = findNodeForItem(item.UUID);
1964             if (node != null)
1965             {
1966                 invTree.SelectedNode = node;
1967                 node.BeginEdit();
1968             }
1969         }
1970
1971         void PerformClipboardOperation(InventoryFolder dest)
1972         {
1973             if (instance.InventoryClipboard == null) return;
1974
1975             if (dest == null) return;
1976
1977             if (instance.InventoryClipboard.Operation == ClipboardOperation.Cut)
1978             {
1979                 if (instance.InventoryClipboard.Item is InventoryItem)
1980                 {
1981                     client.Inventory.MoveItem(instance.InventoryClipboard.Item.UUID, dest.UUID, instance.InventoryClipboard.Item.Name);
1982                 }
1983                 else if (instance.InventoryClipboard.Item is InventoryFolder)
1984                 {
1985                     if (instance.InventoryClipboard.Item.UUID != dest.UUID)
1986                     {
1987                         client.Inventory.MoveFolder(instance.InventoryClipboard.Item.UUID, dest.UUID, instance.InventoryClipboard.Item.Name);
1988                     }
1989                 }
1990
1991                 instance.InventoryClipboard = null;
1992             }
1993             else if (instance.InventoryClipboard.Operation == ClipboardOperation.Copy)
1994             {
1995                 if (instance.InventoryClipboard.Item is InventoryItem)
1996                 {
1997                     client.Inventory.RequestCopyItem(instance.InventoryClipboard.Item.UUID, dest.UUID, instance.InventoryClipboard.Item.Name, instance.InventoryClipboard.Item.OwnerID, (InventoryBase target) =>
1998                     {
1999                     }
2000                     );
2001                 }
2002                 else if (instance.InventoryClipboard.Item is InventoryFolder)
2003                 {
2004                     WorkPool.QueueUserWorkItem((object state) =>
2005                         {
2006                             UUID newFolderID = client.Inventory.CreateFolder(dest.UUID, instance.InventoryClipboard.Item.Name, AssetType.Unknown);
2007                             Thread.Sleep(500);
2008
2009                             // FIXME: for some reason copying a bunch of items in one operation does not work
2010
2011                             //List<UUID> items = new List<UUID>();
2012                             //List<UUID> folders = new List<UUID>();
2013                             //List<string> names = new List<string>();
2014                             //UUID oldOwner = UUID.Zero;
2015
2016                             foreach (InventoryBase oldItem in Inventory.GetContents((InventoryFolder)instance.InventoryClipboard.Item))
2017                             {
2018                                 //folders.Add(newFolderID);
2019                                 //names.Add(oldItem.Name);
2020                                 //items.Add(oldItem.UUID);
2021                                 //oldOwner = oldItem.OwnerID;
2022                                 client.Inventory.RequestCopyItem(oldItem.UUID, newFolderID, oldItem.Name, oldItem.OwnerID, (InventoryBase target) => { });
2023                             }
2024
2025                             //if (folders.Count > 0)
2026                             //{
2027                             //    client.Inventory.RequestCopyItems(items, folders, names, oldOwner, (InventoryBase target) => { });
2028                             //}
2029                         }
2030                     );
2031                 }
2032             }
2033         }
2034
2035         void PerformLinkOperation(InventoryFolder dest)
2036         {
2037             if (instance.InventoryClipboard == null) return;
2038
2039             if (dest == null) return;
2040
2041             client.Inventory.CreateLink(dest.UUID, instance.InventoryClipboard.Item, (bool success, InventoryItem item) => { });
2042         }
2043
2044         #endregion
2045
2046         private void UpdateWornLabels()
2047         {
2048             if (InvokeRequired)
2049             {
2050                 BeginInvoke(new MethodInvoker(UpdateWornLabels));
2051                 return;
2052             }
2053
2054             invTree.BeginUpdate();
2055             foreach (UUID itemID in WornItems)
2056             {
2057                 TreeNode node = findNodeForItem(itemID);
2058                 if (node != null)
2059                 {
2060                     node.Text = ItemLabel((InventoryBase)node.Tag, false);
2061                 }
2062             }
2063             WornItems.Clear();
2064             foreach (AppearanceManager.WearableData wearable in client.Appearance.GetWearables().Values)
2065             {
2066                 TreeNode node = findNodeForItem(wearable.ItemID);
2067                 if (node != null)
2068                 {
2069                     node.Text = ItemLabel((InventoryBase)node.Tag, false);
2070                 }
2071             }
2072             invTree.EndUpdate();
2073         }
2074
2075         void TreeView_AfterExpand(object sender, TreeViewEventArgs e)
2076         {
2077             // Check if we need to go into edit mode for new items
2078             if (newItemName != string.Empty)
2079             {
2080                 foreach (TreeNode n in e.Node.Nodes)
2081                 {
2082                     if (n.Name == newItemName)
2083                     {
2084                         n.BeginEdit();
2085                         break;
2086                     }
2087                 }
2088                 newItemName = string.Empty;
2089             }
2090         }
2091
2092         private bool _EditingNode = false;
2093
2094         private void OnLabelEditTimer(object sender)
2095         {
2096             if (_EditNode == null || !(_EditNode.Tag is InventoryBase))
2097                 return;
2098
2099             if (InvokeRequired)
2100             {
2101                 BeginInvoke(new MethodInvoker(delegate()
2102                     {
2103                         OnLabelEditTimer(sender);
2104                     }
2105                 ));
2106                 return;
2107             }
2108
2109             _EditingNode = true;
2110             _EditNode.Text = ItemLabel((InventoryBase)_EditNode.Tag, true);
2111             _EditNode.BeginEdit();
2112         }
2113
2114         private void invTree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
2115         {
2116             if (e.Node == null ||
2117                 !(e.Node.Tag is InventoryBase) ||
2118                 (e.Node.Tag is InventoryFolder &&
2119                 ((InventoryFolder)e.Node.Tag).PreferredType != AssetType.Unknown &&
2120                 ((InventoryFolder)e.Node.Tag).PreferredType != AssetType.OutfitFolder)
2121                 )
2122             {
2123                 e.CancelEdit = true;
2124                 return;
2125             }
2126
2127             if (_EditingNode)
2128             {
2129                 _EditingNode = false;
2130             }
2131             else
2132             {
2133                 e.CancelEdit = true;
2134                 _EditNode = e.Node;
2135                 _EditTimer.Change(20, System.Threading.Timeout.Infinite);
2136             }
2137         }
2138
2139         private void invTree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
2140         {
2141             if (string.IsNullOrEmpty(e.Label))
2142             {
2143                 if (e.Node.Tag is InventoryBase)
2144                 {
2145                     e.Node.Text = ItemLabel((InventoryBase)e.Node.Tag, false);
2146                 }
2147                 e.CancelEdit = true;
2148                 return;
2149             }
2150
2151             if (e.Node.Tag is InventoryFolder)
2152             {
2153                 InventoryFolder f = (InventoryFolder)e.Node.Tag;
2154                 f.Name = e.Label;
2155                 client.Inventory.MoveFolder(f.UUID, f.ParentUUID, f.Name);
2156             }
2157             else if (e.Node.Tag is InventoryItem)
2158             {
2159                 InventoryItem item = (InventoryItem)e.Node.Tag;
2160                 item.Name = e.Label;
2161                 e.Node.Text = ItemLabel((InventoryBase)item, false);
2162                 client.Inventory.MoveItem(item.UUID, item.ParentUUID, item.Name);
2163                 UpdateItemInfo(item);
2164             }
2165
2166         }
2167
2168         private void invTree_KeyUp(object sender, KeyEventArgs e)
2169         {
2170             if (e.KeyCode == Keys.F2 && invTree.SelectedNode != null)
2171             {
2172                 invTree.SelectedNode.BeginEdit();
2173             }
2174             else if (e.KeyCode == Keys.F5 && invTree.SelectedNode != null)
2175             {
2176                 if (invTree.SelectedNode.Tag is InventoryFolder)
2177                 {
2178                     InventoryFolder f = (InventoryFolder)invTree.SelectedNode.Tag;
2179                     fetchFolder(f.UUID, f.OwnerID, true);
2180                 }
2181             }
2182             else if (e.KeyCode == Keys.Delete && invTree.SelectedNode != null)
2183             {
2184                 if (invTree.SelectedNode.Tag is InventoryItem)
2185                 {
2186                     InventoryItem item = invTree.SelectedNode.Tag as InventoryItem;
2187                     client.Inventory.MoveItem(item.UUID, client.Inventory.FindFolderForType(AssetType.TrashFolder), item.Name);
2188                 }
2189                 else if (invTree.SelectedNode.Tag is InventoryFolder)
2190                 {
2191                     InventoryFolder f = invTree.SelectedNode.Tag as InventoryFolder;
2192                     client.Inventory.MoveFolder(f.UUID, client.Inventory.FindFolderForType(AssetType.TrashFolder), f.Name);
2193                 }
2194             }
2195             else if (e.KeyCode == Keys.Apps && invTree.SelectedNode != null)
2196             {
2197                 ctxInv.Show();
2198             }
2199         }
2200
2201         #region Drag and Drop
2202         private void invTree_ItemDrag(object sender, ItemDragEventArgs e)
2203         {
2204             invTree.SelectedNode = e.Item as TreeNode;
2205             if (invTree.SelectedNode.Tag is InventoryFolder && ((InventoryFolder)invTree.SelectedNode.Tag).PreferredType != AssetType.Unknown)
2206             {
2207                 return;
2208             }
2209             invTree.DoDragDrop(e.Item, DragDropEffects.Move);
2210         }
2211
2212         private void invTree_DragDrop(object sender, DragEventArgs e)
2213         {
2214             if (highlightedNode != null)
2215             {
2216                 highlightedNode.BackColor = invTree.BackColor;
2217                 highlightedNode = null;
2218             }
2219
2220             TreeNode sourceNode = e.Data.GetData(typeof(TreeNode)) as TreeNode;
2221             if (sourceNode == null) return;
2222
2223             Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
2224             TreeNode destinationNode = ((TreeView)sender).GetNodeAt(pt);
2225
2226             if (destinationNode == null) return;
2227
2228             if (sourceNode == destinationNode) return;
2229
2230             // If droping to item within folder drop to its folder
2231             if (destinationNode.Tag is InventoryItem)
2232             {
2233                 destinationNode = destinationNode.Parent;
2234             }
2235
2236             InventoryFolder dest = destinationNode.Tag as InventoryFolder;
2237
2238             if (dest == null) return;
2239
2240             if (sourceNode.Tag is InventoryItem)
2241             {
2242                 InventoryItem item = (InventoryItem)sourceNode.Tag;
2243                 client.Inventory.MoveItem(item.UUID, dest.UUID, item.Name);
2244             }
2245             else if (sourceNode.Tag is InventoryFolder)
2246             {
2247                 InventoryFolder f = (InventoryFolder)sourceNode.Tag;
2248                 client.Inventory.MoveFolder(f.UUID, dest.UUID, f.Name);
2249             }
2250         }
2251
2252         private void invTree_DragEnter(object sender, DragEventArgs e)
2253         {
2254             TreeNode node = e.Data.GetData(typeof(TreeNode)) as TreeNode;
2255             if (node == null)
2256             {
2257                 e.Effect = DragDropEffects.None;
2258             }
2259             else
2260             {
2261                 e.Effect = DragDropEffects.Move;
2262             }
2263         }
2264
2265         TreeNode highlightedNode = null;
2266
2267         private void invTree_DragOver(object sender, DragEventArgs e)
2268         {
2269             TreeNode node = e.Data.GetData(typeof(TreeNode)) as TreeNode;
2270             if (node == null)
2271             {
2272                 e.Effect = DragDropEffects.None;
2273             }
2274
2275             Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
2276             TreeNode destinationNode = ((TreeView)sender).GetNodeAt(pt);
2277
2278             if (highlightedNode != destinationNode)
2279             {
2280                 if (highlightedNode != null)
2281                 {
2282                     highlightedNode.BackColor = invTree.BackColor;
2283                     highlightedNode = null;
2284                 }
2285
2286                 if (destinationNode != null)
2287                 {
2288                     highlightedNode = destinationNode;
2289                     highlightedNode.BackColor = Color.LightSlateGray;
2290                 }
2291             }
2292
2293             if (destinationNode == null)
2294             {
2295                 e.Effect = DragDropEffects.None;
2296                 return;
2297             }
2298
2299             e.Effect = DragDropEffects.Move;
2300
2301         }
2302         #endregion
2303
2304         private void saveAllTToolStripMenuItem_Click(object sender, EventArgs e)
2305         {
2306             (new InventoryBackup(instance, Inventory.RootFolder.UUID)).Show();
2307         }
2308
2309         private void tbtnSystemFoldersFirst_Click(object sender, EventArgs e)
2310         {
2311             sorter.SystemFoldersFirst = tbtnSystemFoldersFirst.Checked = !sorter.SystemFoldersFirst;
2312             instance.GlobalSettings["inv_sort_sysfirst"] = OSD.FromBoolean(sorter.SystemFoldersFirst);
2313             invTree.Sort();
2314         }
2315
2316         private void tbtbSortByName_Click(object sender, EventArgs e)
2317         {
2318             if (tbtbSortByName.Checked) return;
2319
2320             tbtbSortByName.Checked = true;
2321             tbtnSortByDate.Checked = sorter.ByDate = false;
2322             instance.GlobalSettings["inv_sort_bydate"] = OSD.FromBoolean(sorter.ByDate);
2323
2324             invTree.Sort();
2325         }
2326
2327         private void tbtnSortByDate_Click(object sender, EventArgs e)
2328         {
2329             if (tbtnSortByDate.Checked) return;
2330
2331             tbtbSortByName.Checked = false;
2332             tbtnSortByDate.Checked = sorter.ByDate = true;
2333             instance.GlobalSettings["inv_sort_bydate"] = OSD.FromBoolean(sorter.ByDate);
2334
2335             invTree.Sort();
2336         }
2337
2338         #region Search
2339
2340         public class SearchResult
2341         {
2342             public InventoryBase Inv;
2343             public int Level;
2344
2345             public SearchResult(InventoryBase inv, int level)
2346             {
2347                 this.Inv = inv;
2348                 this.Level = level;
2349             }
2350         }
2351
2352         List<SearchResult> searchRes;
2353         string searchString;
2354         Dictionary<int, ListViewItem> searchItemCache = new Dictionary<int, ListViewItem>();
2355         ListViewItem emptyItem = null;
2356         int found;
2357
2358         void PerformRecursiveSearch(int level, UUID folderID)
2359         {
2360             var me = Inventory.Items[folderID].Data;
2361             searchRes.Add(new SearchResult(me, level));
2362             var sorted = Inventory.GetContents(folderID);
2363
2364             sorted.Sort((InventoryBase b1, InventoryBase b2) =>
2365             {
2366                 if (b1 is InventoryFolder && !(b2 is InventoryFolder))
2367                 {
2368                     return -1;
2369                 }
2370                 else if (!(b1 is InventoryFolder) && b2 is InventoryFolder)
2371                 {
2372                     return 1;
2373                 }
2374                 else
2375                 {
2376                     return string.Compare(b1.Name, b2.Name);
2377                 }
2378             });
2379
2380             foreach (var item in sorted)
2381             {
2382                 if (item is InventoryFolder)
2383                 {
2384                     PerformRecursiveSearch(level + 1, item.UUID);
2385                 }
2386                 else
2387                 {
2388                     var it = item as InventoryItem;
2389                     bool add = false;
2390
2391                     if (cbSrchName.Checked && it.Name.ToLower().Contains(searchString))
2392                     {
2393                         add = true;
2394                     }
2395                     else if (cbSrchDesc.Checked && it.Description.ToLower().Contains(searchString))
2396                     {
2397                         add = true;
2398                     }
2399
2400                     if (cbSrchWorn.Checked && add &&
2401                             !(
2402                                 (it.InventoryType == InventoryType.Wearable && IsWorn(it)) ||
2403                                 ((it.InventoryType == InventoryType.Attachment || it.InventoryType == InventoryType.Object) && IsAttached(it))
2404                             )
2405                     )
2406                     {
2407                         add = false;
2408                     }
2409
2410                     if (cbSrchRecent.Checked && add && it.CreationDate < instance.StartupTimeUTC)
2411                     {
2412                         add = false;
2413                     }
2414
2415                     if (add)
2416                     {
2417                         found++;
2418                         searchRes.Add(new SearchResult(it, level + 1));
2419                     }
2420                 }
2421             }
2422
2423             if (searchRes[searchRes.Count - 1].Inv == me)
2424             {
2425                 searchRes.RemoveAt(searchRes.Count - 1);
2426             }
2427         }
2428
2429         public void UpdateSearch()
2430         {
2431             found = 0;
2432
2433             if (instance.MonoRuntime)
2434             {
2435                 lstInventorySearch.VirtualMode = false;
2436                 lstInventorySearch.Items.Clear();
2437                 lstInventorySearch.VirtualMode = true;
2438             }
2439
2440             lstInventorySearch.VirtualListSize = 0;
2441             searchString = txtSearch.Text.Trim().ToLower();
2442
2443             //if (searchString == string.Empty && rbSrchAll.Checked)
2444             //{
2445             //    lblSearchStatus.Text = "0 results";
2446             //    return;
2447             //}
2448
2449             if (emptyItem == null)
2450             {
2451                 emptyItem = new ListViewItem(string.Empty);
2452             }
2453
2454             searchRes = new List<SearchResult>(Inventory.Items.Count);
2455             searchItemCache.Clear();
2456             PerformRecursiveSearch(0, Inventory.RootFolder.UUID);
2457             lstInventorySearch.VirtualListSize = searchRes.Count;
2458             lblSearchStatus.Text = string.Format("{0} results", found);
2459         }
2460
2461         private void lstInventorySearch_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
2462         {
2463             if (searchItemCache.ContainsKey(e.ItemIndex))
2464             {
2465                 e.Item = searchItemCache[e.ItemIndex];
2466             }
2467             else if (e.ItemIndex < searchRes.Count)
2468             {
2469                 InventoryBase inv = searchRes[e.ItemIndex].Inv;
2470                 string desc = inv.Name;
2471                 if (inv is InventoryItem)
2472                 {
2473                     desc += string.Format(" - {0}", ((InventoryItem)inv).Description);
2474                 }
2475                 ListViewItem item = new ListViewItem(desc);
2476                 item.Tag = searchRes[e.ItemIndex];
2477                 e.Item = item;
2478                 searchItemCache[e.ItemIndex] = item;
2479             }
2480             else
2481             {
2482                 e.Item = emptyItem;
2483             }
2484         }
2485
2486         private void btnInvSearch_Click(object sender, EventArgs e)
2487         {
2488             UpdateSearch();
2489         }
2490
2491         private void cbSrchName_CheckedChanged(object sender, EventArgs e)
2492         {
2493             if (!cbSrchName.Checked && !cbSrchDesc.Checked && !cbSrchCreator.Checked)
2494             {
2495                 cbSrchName.Checked = true;
2496             }
2497             UpdateSearch();
2498         }
2499
2500         private void cbSrchWorn_CheckedChanged(object sender, EventArgs e)
2501         {
2502             UpdateSearch();
2503         }
2504
2505         private void txtSearch_KeyDown(object sender, KeyEventArgs e)
2506         {
2507             if (e.KeyCode == Keys.Enter)
2508             {
2509                 e.Handled = e.SuppressKeyPress = true;
2510                 if (txtSearch.Text.Trim().Length > 0)
2511                 {
2512                     UpdateSearch();
2513                 }
2514             }
2515         }
2516
2517         private void lstInventorySearch_DrawItem(object sender, DrawListViewItemEventArgs e)
2518         {
2519             Graphics g = e.Graphics;
2520             e.DrawBackground();
2521
2522             if (!(e.Item.Tag is SearchResult))
2523                 return;
2524
2525             if (e.Item.Selected)
2526             {
2527                 g.FillRectangle(SystemBrushes.Highlight, e.Bounds);
2528             }
2529
2530             SearchResult res = e.Item.Tag as SearchResult;
2531             int offset = 20 * (res.Level + 1);
2532             Rectangle rec = new Rectangle(e.Bounds.X + offset, e.Bounds.Y, e.Bounds.Width - offset, e.Bounds.Height);
2533
2534             Image icon = null;
2535             int iconIx = 0;
2536
2537             if (res.Inv is InventoryFolder)
2538             {
2539                 iconIx = GetDirImageIndex(((InventoryFolder)res.Inv).PreferredType.ToString().ToLower());
2540             }
2541             else if (res.Inv is InventoryWearable)
2542             {
2543                 iconIx = GetItemImageIndex(((InventoryWearable)res.Inv).WearableType.ToString().ToLower());
2544             }
2545             else if (res.Inv is InventoryItem)
2546             {
2547                 iconIx = GetItemImageIndex(((InventoryItem)res.Inv).AssetType.ToString().ToLower());
2548             }
2549
2550             if (iconIx < 0)
2551             {
2552                 iconIx = 0;
2553             }
2554
2555             try
2556             {
2557                 icon = frmMain.ResourceImages.Images[iconIx];
2558                 g.DrawImageUnscaled(icon, e.Bounds.X + offset - 18, e.Bounds.Y);
2559             }
2560             catch { }
2561
2562             using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap | StringFormatFlags.LineLimit))
2563             {
2564                 string label = ItemLabel(res.Inv, false);
2565                 SizeF len = e.Graphics.MeasureString(label, lstInventorySearch.Font, rec.Width, sf);
2566
2567                 e.Graphics.DrawString(
2568                     ItemLabel(res.Inv, false),
2569                     lstInventorySearch.Font,
2570                     e.Item.Selected ? SystemBrushes.HighlightText : SystemBrushes.WindowText,
2571                     rec,
2572                     sf);
2573
2574                 if (res.Inv is InventoryItem)
2575                 {
2576                     string desc = ((InventoryItem)res.Inv).Description.Trim();
2577                     if (desc != string.Empty)
2578                     {
2579                         using (Font descFont = new Font(lstInventorySearch.Font, FontStyle.Italic))
2580                         {
2581                             e.Graphics.DrawString(desc,
2582                                 descFont,
2583                                 e.Item.Selected ? SystemBrushes.HighlightText : SystemBrushes.GrayText,
2584                                 rec.X + len.Width + 5,
2585                                 rec.Y,
2586                                 sf);
2587                         }
2588                     }
2589                 }
2590             }
2591
2592         }
2593
2594         private void lstInventorySearch_SizeChanged(object sender, EventArgs e)
2595         {
2596             chResItemName.Width = lstInventorySearch.Width - 30;
2597         }
2598
2599         private void txtSearch_TextChanged(object sender, EventArgs e)
2600         {
2601             UpdateSearch();
2602         }
2603
2604         private void rbSrchAll_CheckedChanged(object sender, EventArgs e)
2605         {
2606             UpdateSearch();
2607         }
2608
2609         private void lstInventorySearch_KeyDown(object sender, KeyEventArgs e)
2610         {
2611             if ((e.KeyCode == Keys.Apps) || (e.Control && e.KeyCode == RadegastContextMenuStrip.ContexMenuKeyCode))
2612             {
2613                 lstInventorySearch_MouseClick(sender, new MouseEventArgs(MouseButtons.Right, 1, 50, 150, 0));
2614             }
2615         }
2616
2617         /// <summary>
2618         /// Finds and higlights inventory node
2619         /// </summary>
2620         /// <param name="itemID">Inventory of ID of the item to select</param>
2621         public void SelectInventoryNode(UUID itemID)
2622         {
2623             TreeNode node = findNodeForItem(itemID);
2624             if (node == null)
2625                 return;
2626             invTree.SelectedNode = node;
2627             if (node.Tag is InventoryItem)
2628             {
2629                 UpdateItemInfo(node.Tag as InventoryItem);
2630             }
2631         }
2632
2633         private void lstInventorySearch_MouseClick(object sender, MouseEventArgs e)
2634         {
2635             if (lstInventorySearch.SelectedIndices.Count != 1)
2636                 return;
2637
2638             try
2639             {
2640                 SearchResult res = searchRes[lstInventorySearch.SelectedIndices[0]];
2641                 TreeNode node = findNodeForItem(res.Inv.UUID);
2642                 if (node == null)
2643                     return;
2644                 invTree.SelectedNode = node;
2645                 if (e.Button == MouseButtons.Right)
2646                 {
2647                     ctxInv.Show(lstInventorySearch, e.X, e.Y);
2648                 }
2649             }
2650             catch { }
2651         }
2652
2653         private void lstInventorySearch_MouseDoubleClick(object sender, MouseEventArgs e)
2654         {
2655             if (lstInventorySearch.SelectedIndices.Count != 1)
2656                 return;
2657
2658             try
2659             {
2660                 SearchResult res = searchRes[lstInventorySearch.SelectedIndices[0]];
2661                 TreeNode node = findNodeForItem(res.Inv.UUID);
2662                 if (node == null)
2663                     return;
2664                 invTree.SelectedNode = node;
2665                 invTree_NodeMouseDoubleClick(null, null);
2666             }
2667             catch { }
2668
2669         }
2670         #endregion Search
2671
2672         private void txtAssetID_Enter(object sender, EventArgs e)
2673         {
2674             txtAssetID.SelectAll();
2675         }
2676
2677         private void txtInvID_Enter(object sender, EventArgs e)
2678         {
2679             txtInvID.SelectAll();
2680         }
2681
2682         private void copyInitialOutfitsToolStripMenuItem_Click(object sender, EventArgs e)
2683         {
2684             var c = new FolderCopy(instance);
2685             c.GetFolders("Initial Outfits");
2686         }
2687
2688
2689     }
2690
2691     #region Sorter class
2692     // Create a node sorter that implements the IComparer interface.
2693     public class InvNodeSorter : System.Collections.IComparer
2694     {
2695         bool _sysfirst = true;
2696         bool _bydate = true;
2697
2698         int CompareFolders(InventoryFolder x, InventoryFolder y)
2699         {
2700             if (_sysfirst)
2701             {
2702                 if (x.PreferredType != AssetType.Unknown && y.PreferredType == AssetType.Unknown)
2703                 {
2704                     return -1;
2705                 }
2706                 else if (x.PreferredType == AssetType.Unknown && y.PreferredType != AssetType.Unknown)
2707                 {
2708                     return 1;
2709                 }
2710             }
2711             return String.Compare(x.Name, y.Name);
2712         }
2713
2714         public bool SystemFoldersFirst { set { _sysfirst = value; } get { return _sysfirst; } }
2715         public bool ByDate { set { _bydate = value; } get { return _bydate; } }
2716
2717         public int Compare(object x, object y)
2718         {
2719             TreeNode tx = x as TreeNode;
2720             TreeNode ty = y as TreeNode;
2721
2722             if (tx.Tag is InventoryFolder && ty.Tag is InventoryFolder)
2723             {
2724                 return CompareFolders(tx.Tag as InventoryFolder, ty.Tag as InventoryFolder);
2725             }
2726             else if (tx.Tag is InventoryFolder && ty.Tag is InventoryItem)
2727             {
2728                 return -1;
2729             }
2730             else if (tx.Tag is InventoryItem && ty.Tag is InventoryFolder)
2731             {
2732                 return 1;
2733             }
2734
2735             // Two items
2736             if (!(tx.Tag is InventoryItem) || !(ty.Tag is InventoryItem))
2737             {
2738                 return 0;
2739             }
2740
2741             InventoryItem item1 = (InventoryItem)tx.Tag;
2742             InventoryItem item2 = (InventoryItem)ty.Tag;
2743
2744             if (_bydate)
2745             {
2746                 if (item1.CreationDate < item2.CreationDate)
2747                 {
2748                     return 1;
2749                 }
2750                 else if (item1.CreationDate > item2.CreationDate)
2751                 {
2752                     return -1;
2753                 }
2754             }
2755             return string.Compare(item1.Name, item2.Name);
2756         }
2757     }
2758     #endregion
2759
2760     public class AttachmentInfo
2761     {
2762         public Primitive Prim;
2763         public InventoryItem Item;
2764         public UUID InventoryID;
2765         public UUID PrimID;
2766         public bool MarkedAttached = false;
2767
2768         public AttachmentPoint Point
2769         {
2770             get
2771             {
2772                 if (Prim != null)
2773                 {
2774                     return Prim.PrimData.AttachmentPoint;
2775                 }
2776                 else
2777                 {
2778                     return AttachmentPoint.Default;
2779                 }
2780             }
2781         }
2782     }
2783
2784
2785 }