OSDN Git Service

fa2f1c86c78a912da5b70248c69e0b94d75e4687
[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             if ((client.Network.LoginResponseData.FirstLogin) && !string.IsNullOrEmpty(client.Network.LoginResponseData.InitialOutfit))
829             {
830                 client.Self.SetAgentAccess("A");
831                 var initOufit = new InitialOutfit(instance);
832                 initOufit.SetInitialOutfit(client.Network.LoginResponseData.InitialOutfit);
833             }
834
835             // Updated labels on clothes that we are wearing
836             UpdateWornLabels();
837
838             // Update attachments now that we are done
839             lock (attachments)
840             {
841                 foreach (AttachmentInfo a in attachments.Values)
842                 {
843                     if (a.Item == null)
844                     {
845                         if (Inventory.Contains(a.InventoryID))
846                         {
847                             a.MarkedAttached = true;
848                             a.Item = (InventoryItem)Inventory[a.InventoryID];
849                             UpdateNodeLabel(a.InventoryID);
850                         }
851                         else
852                         {
853                             client.Inventory.RequestFetchInventory(a.InventoryID, client.Self.AgentID);
854                             return;
855                         }
856                     }
857                 }
858             }
859
860             Logger.Log("Finished updating invenory folders, saving cache...", Helpers.LogLevel.Debug, client);
861             WorkPool.QueueUserWorkItem((object state) => Inventory.SaveToDisk(instance.InventoryCacheFileName));
862
863             if (!instance.MonoRuntime || IsHandleCreated)
864                 Invoke(new MethodInvoker(() =>
865                     {
866                         invTree.Sort();
867                     }
868             ));
869
870
871         }
872
873         public void ReloadInventory()
874         {
875             if (TreeUpdateInProgress)
876             {
877                 TreeUpdateTimer.Stop();
878                 InventoryUpdate.Abort();
879                 InventoryUpdate = null;
880             }
881
882             saveAllTToolStripMenuItem.Enabled = false;
883
884             Inventory.Items = new Dictionary<UUID, InventoryNode>();
885             Inventory.RootFolder = Inventory.RootFolder;
886
887             invTree.Nodes.Clear();
888             UUID2NodeCache.Clear();
889             invRootNode = AddDir(null, Inventory.RootFolder);
890             Inventory.RootNode.NeedsUpdate = true;
891
892             InventoryUpdate = new Thread(new ThreadStart(StartTraverseNodes));
893             InventoryUpdate.Name = "InventoryUpdate";
894             InventoryUpdate.IsBackground = true;
895             InventoryUpdate.Start();
896             invRootNode.Expand();
897         }
898
899         private void reloadInventoryToolStripMenuItem_Click(object sender, EventArgs e)
900         {
901             ReloadInventory();
902         }
903
904         private void TreeUpdateTimerTick(Object sender, EventArgs e)
905         {
906             lock (ItemsToAdd)
907             {
908                 if (ItemsToAdd.Count > 0)
909                 {
910                     invTree.BeginUpdate();
911                     while (ItemsToAdd.Count > 0)
912                     {
913                         InventoryBase item = ItemsToAdd.Dequeue();
914                         TreeNode node = findNodeForItem(item.ParentUUID);
915                         if (node != null)
916                         {
917                             AddBase(node, item);
918                         }
919                     }
920                     invTree.EndUpdate();
921                 }
922             }
923
924             lock (ItemsToUpdate)
925             {
926                 if (ItemsToUpdate.Count > 0)
927                 {
928                     invTree.BeginUpdate();
929                     while (ItemsToUpdate.Count > 0)
930                     {
931                         InventoryBase item = ItemsToUpdate.Dequeue();
932                         Exec_OnInventoryObjectUpdated(item, item);
933                     }
934                     invTree.EndUpdate();
935                 }
936             }
937
938             UpdateStatus("Loading... " + UUID2NodeCache.Count.ToString() + " items");
939         }
940
941         #endregion
942
943         private void btnProfile_Click(object sender, EventArgs e)
944         {
945             instance.MainForm.ShowAgentProfile(txtCreator.Text, txtCreator.AgentID);
946         }
947
948         void UpdateItemInfo(InventoryItem item)
949         {
950             foreach (Control c in pnlDetail.Controls)
951             {
952                 c.Dispose();
953             }
954             pnlDetail.Controls.Clear();
955             pnlItemProperties.Tag = item;
956
957             if (item == null)
958             {
959                 pnlItemProperties.Visible = false;
960                 return;
961             }
962
963             pnlItemProperties.Visible = true;
964             btnProfile.Enabled = true;
965             txtItemName.Text = item.Name;
966             txtItemDescription.Text = item.Description;
967             txtCreator.AgentID = item.CreatorID;
968             txtCreator.Tag = item.CreatorID;
969             txtCreated.Text = item.CreationDate.ToString();
970
971             if (item.AssetUUID != UUID.Zero)
972             {
973                 txtAssetID.Text = item.AssetUUID.ToString();
974             }
975             else
976             {
977                 txtAssetID.Text = String.Empty;
978             }
979
980             txtInvID.Text = item.UUID.ToString();
981
982             Permissions p = item.Permissions;
983             cbOwnerModify.Checked = (p.OwnerMask & PermissionMask.Modify) != 0;
984             cbOwnerCopy.Checked = (p.OwnerMask & PermissionMask.Copy) != 0;
985             cbOwnerTransfer.Checked = (p.OwnerMask & PermissionMask.Transfer) != 0;
986
987             cbNextOwnModify.CheckedChanged -= cbNextOwnerUpdate_CheckedChanged;
988             cbNextOwnCopy.CheckedChanged -= cbNextOwnerUpdate_CheckedChanged;
989             cbNextOwnTransfer.CheckedChanged -= cbNextOwnerUpdate_CheckedChanged;
990
991             cbNextOwnModify.Checked = (p.NextOwnerMask & PermissionMask.Modify) != 0;
992             cbNextOwnCopy.Checked = (p.NextOwnerMask & PermissionMask.Copy) != 0;
993             cbNextOwnTransfer.Checked = (p.NextOwnerMask & PermissionMask.Transfer) != 0;
994
995             cbNextOwnModify.CheckedChanged += cbNextOwnerUpdate_CheckedChanged;
996             cbNextOwnCopy.CheckedChanged += cbNextOwnerUpdate_CheckedChanged;
997             cbNextOwnTransfer.CheckedChanged += cbNextOwnerUpdate_CheckedChanged;
998
999
1000             switch (item.AssetType)
1001             {
1002                 case AssetType.Texture:
1003                     SLImageHandler image = new SLImageHandler(instance, item.AssetUUID, item.Name, IsFullPerm(item));
1004                     image.Dock = DockStyle.Fill;
1005                     pnlDetail.Controls.Add(image);
1006                     break;
1007
1008                 case AssetType.Notecard:
1009                     Notecard note = new Notecard(instance, (InventoryNotecard)item);
1010                     note.Dock = DockStyle.Fill;
1011                     note.TabIndex = 3;
1012                     note.TabStop = true;
1013                     pnlDetail.Controls.Add(note);
1014                     note.rtbContent.Focus();
1015                     break;
1016
1017                 case AssetType.Landmark:
1018                     Landmark landmark = new Landmark(instance, (InventoryLandmark)item);
1019                     landmark.Dock = DockStyle.Fill;
1020                     pnlDetail.Controls.Add(landmark);
1021                     break;
1022
1023                 case AssetType.LSLText:
1024                     ScriptEditor script = new ScriptEditor(instance, (InventoryLSL)item);
1025                     script.Dock = DockStyle.Fill;
1026                     script.TabIndex = 3;
1027                     script.TabStop = true;
1028                     pnlDetail.Controls.Add(script);
1029                     break;
1030
1031                 case AssetType.Gesture:
1032                     Guesture gesture = new Guesture(instance, (InventoryGesture)item);
1033                     gesture.Dock = DockStyle.Fill;
1034                     pnlDetail.Controls.Add(gesture);
1035                     break;
1036
1037             }
1038
1039             tabsInventory.SelectedTab = tabDetail;
1040         }
1041
1042         void cbNextOwnerUpdate_CheckedChanged(object sender, EventArgs e)
1043         {
1044             InventoryItem item = null;
1045             if (pnlItemProperties.Tag != null && pnlItemProperties.Tag is InventoryItem)
1046             {
1047                 item = (InventoryItem)pnlItemProperties.Tag;
1048             }
1049             if (item == null) return;
1050
1051             PermissionMask pm = PermissionMask.Move;
1052             if (cbNextOwnCopy.Checked) pm |= PermissionMask.Copy;
1053             if (cbNextOwnModify.Checked) pm |= PermissionMask.Modify;
1054             if (cbNextOwnTransfer.Checked) pm |= PermissionMask.Transfer;
1055             item.Permissions.NextOwnerMask = pm;
1056
1057             client.Inventory.RequestUpdateItem(item);
1058             client.Inventory.RequestFetchInventory(item.UUID, item.OwnerID);
1059         }
1060
1061         private void txtItemName_Leave(object sender, EventArgs e)
1062         {
1063             InventoryItem item = null;
1064             if (pnlItemProperties.Tag != null && pnlItemProperties.Tag is InventoryItem)
1065             {
1066                 item = (InventoryItem)pnlItemProperties.Tag;
1067             }
1068             if (item == null) return;
1069
1070             item.Name = txtItemName.Text;
1071
1072             client.Inventory.RequestUpdateItem(item);
1073             client.Inventory.RequestFetchInventory(item.UUID, item.OwnerID);
1074         }
1075
1076         private void txtItemDescription_Leave(object sender, EventArgs e)
1077         {
1078             InventoryItem item = null;
1079             if (pnlItemProperties.Tag != null && pnlItemProperties.Tag is InventoryItem)
1080             {
1081                 item = (InventoryItem)pnlItemProperties.Tag;
1082             }
1083             if (item == null) return;
1084
1085             item.Description = txtItemDescription.Text;
1086
1087             client.Inventory.RequestUpdateItem(item);
1088             client.Inventory.RequestFetchInventory(item.UUID, item.OwnerID);
1089         }
1090
1091         void invTree_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
1092         {
1093             if (invTree.SelectedNode.Tag is InventoryItem)
1094             {
1095                 InventoryItem item = invTree.SelectedNode.Tag as InventoryItem;
1096                 item = instance.COF.RealInventoryItem(item);
1097
1098                 switch (item.AssetType)
1099                 {
1100
1101                     case AssetType.Landmark:
1102                         instance.TabConsole.DisplayNotificationInChat("Teleporting to " + item.Name);
1103                         client.Self.RequestTeleport(item.AssetUUID);
1104                         break;
1105
1106                     case AssetType.Gesture:
1107                         client.Self.PlayGesture(item.AssetUUID);
1108                         break;
1109
1110                     case AssetType.Notecard:
1111                         Notecard note = new Notecard(instance, (InventoryNotecard)item);
1112                         note.Dock = DockStyle.Fill;
1113                         note.ShowDetached();
1114                         break;
1115
1116                     case AssetType.LSLText:
1117                         ScriptEditor script = new ScriptEditor(instance, (InventoryLSL)item);
1118                         script.Dock = DockStyle.Fill;
1119                         script.ShowDetached();
1120                         break;
1121
1122                     case AssetType.Object:
1123                         if (IsAttached(item))
1124                         {
1125                             instance.COF.Detach(item);
1126                         }
1127                         else
1128                         {
1129                             instance.COF.Attach(item, AttachmentPoint.Default, true);
1130                         }
1131                         break;
1132
1133                     case AssetType.Bodypart:
1134                     case AssetType.Clothing:
1135                         if (IsWorn(item))
1136                         {
1137                             if (item.AssetType == AssetType.Clothing)
1138                             {
1139                                 instance.COF.RemoveFromOutfit(item);
1140                             }
1141                         }
1142                         else
1143                         {
1144                             instance.COF.AddToOutfit(item, true);
1145                         }
1146                         break;
1147                 }
1148             }
1149         }
1150
1151         private void fetchFolder(UUID folderID, UUID ownerID, bool force)
1152         {
1153             if (force || !fetchedFolders.Contains(folderID))
1154             {
1155                 if (!fetchedFolders.Contains(folderID))
1156                 {
1157                     fetchedFolders.Add(folderID);
1158                 }
1159
1160                 client.Inventory.RequestFolderContents(folderID, ownerID, true, true, InventorySortOrder.ByDate);
1161             }
1162         }
1163
1164         public bool IsWorn(InventoryItem item)
1165         {
1166             bool worn = client.Appearance.IsItemWorn(item) != WearableType.Invalid;
1167
1168             lock (WornItems)
1169             {
1170                 if (worn && !WornItems.Contains(item.UUID))
1171                     WornItems.Add(item.UUID);
1172             }
1173             return worn;
1174         }
1175
1176         public AttachmentPoint AttachedTo(InventoryItem item)
1177         {
1178             lock (attachments)
1179             {
1180                 if (attachments.ContainsKey(item.UUID))
1181                 {
1182                     return attachments[item.UUID].Point;
1183                 }
1184             }
1185
1186             return AttachmentPoint.Default;
1187         }
1188
1189         public bool IsAttached(InventoryItem item)
1190         {
1191             List<Primitive> myAtt = client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive p) => p.ParentID == client.Self.LocalID);
1192             foreach (Primitive prim in myAtt)
1193             {
1194                 if (prim.NameValues == null) continue;
1195                 UUID invID = UUID.Zero;
1196                 for (int i = 0; i < prim.NameValues.Length; i++)
1197                 {
1198                     if (prim.NameValues[i].Name == "AttachItemID")
1199                     {
1200                         invID = (UUID)prim.NameValues[i].Value.ToString();
1201                         break;
1202                     }
1203                 }
1204                 if (invID == item.UUID)
1205                 {
1206                     lock (attachments)
1207                     {
1208                         AttachmentInfo inf = new AttachmentInfo();
1209                         inf.InventoryID = item.UUID;
1210                         inf.Item = item;
1211                         inf.MarkedAttached = true;
1212                         inf.Prim = prim;
1213                         inf.PrimID = prim.ID;
1214                         attachments[invID] = inf;
1215                     }
1216                     return true;
1217                 }
1218             }
1219
1220             return false;
1221         }
1222
1223         public InventoryItem AttachmentAt(AttachmentPoint point)
1224         {
1225             lock (attachments)
1226             {
1227                 foreach (KeyValuePair<UUID, AttachmentInfo> att in attachments)
1228                 {
1229                     if (att.Value.Point == point)
1230                     {
1231                         return att.Value.Item;
1232                     }
1233                 }
1234             }
1235             return null;
1236         }
1237
1238         /// <summary>
1239         /// Returns text of the label
1240         /// </summary>
1241         /// <param name="invBase">Inventory item</param>
1242         /// <param name="returnRaw">Should we return raw text, or if false decorated text with (worn) info, and (no copy) etc. permission info</param>
1243         /// <returns></returns>
1244         public string ItemLabel(InventoryBase invBase, bool returnRaw)
1245         {
1246             if (returnRaw || (invBase is InventoryFolder))
1247                 return invBase.Name;
1248
1249             InventoryItem item = (InventoryItem)invBase;
1250
1251             string raw = item.Name;
1252
1253             if (item.IsLink())
1254             {
1255                 raw += " (link)";
1256                 item = instance.COF.RealInventoryItem(item);
1257                 if (Inventory.Contains(item.AssetUUID) && Inventory[item.AssetUUID] is InventoryItem)
1258                 {
1259                     item = (InventoryItem)Inventory[item.AssetUUID];
1260                 }
1261             }
1262
1263             if ((item.Permissions.OwnerMask & PermissionMask.Modify) == 0)
1264                 raw += " (no modify)";
1265
1266             if ((item.Permissions.OwnerMask & PermissionMask.Copy) == 0)
1267                 raw += " (no copy)";
1268
1269             if ((item.Permissions.OwnerMask & PermissionMask.Transfer) == 0)
1270                 raw += " (no transfer)";
1271
1272             if (IsWorn(item))
1273                 raw += " (worn)";
1274
1275             if (IsAttached(item))
1276             {
1277                 raw += " (worn on " + AttachedTo(item).ToString() + ")";
1278             }
1279
1280             return raw;
1281         }
1282
1283         public static bool IsFullPerm(InventoryItem item)
1284         {
1285             if (
1286                 ((item.Permissions.OwnerMask & PermissionMask.Modify) != 0) &&
1287                 ((item.Permissions.OwnerMask & PermissionMask.Copy) != 0) &&
1288                 ((item.Permissions.OwnerMask & PermissionMask.Transfer) != 0)
1289                 )
1290             {
1291                 return true;
1292             }
1293             else
1294             {
1295                 return false;
1296             }
1297         }
1298
1299         void invTree_MouseClick(object sender, TreeNodeMouseClickEventArgs e)
1300         {
1301             TreeNode node = e.Node;
1302
1303             if (e.Button == MouseButtons.Left)
1304             {
1305                 invTree.SelectedNode = node;
1306                 if (node.Tag is InventoryItem)
1307                 {
1308                     UpdateItemInfo(instance.COF.RealInventoryItem(node.Tag as InventoryItem));
1309                 }
1310                 else
1311                 {
1312                     UpdateItemInfo(null);
1313                 }
1314             }
1315             else if (e.Button == MouseButtons.Right)
1316             {
1317                 invTree.SelectedNode = node;
1318                 ctxInv.Show(invTree, e.X, e.Y);
1319             }
1320         }
1321
1322         private void ctxInv_Opening(object sender, CancelEventArgs e)
1323         {
1324             e.Cancel = false;
1325             TreeNode node = invTree.SelectedNode;
1326             if (node == null)
1327             {
1328                 e.Cancel = true;
1329             }
1330             else
1331             {
1332                 #region Folder context menu
1333                 if (node.Tag is InventoryFolder)
1334                 {
1335                     InventoryFolder folder = (InventoryFolder)node.Tag;
1336                     ctxInv.Items.Clear();
1337
1338                     ToolStripMenuItem ctxItem;
1339
1340                     if ((int)folder.PreferredType >= (int)AssetType.EnsembleStart &&
1341                         (int)folder.PreferredType <= (int)AssetType.EnsembleEnd)
1342                     {
1343                         ctxItem = new ToolStripMenuItem("Fix type", null, OnInvContextClick);
1344                         ctxItem.Name = "fix_type";
1345                         ctxInv.Items.Add(ctxItem);
1346                         ctxInv.Items.Add(new ToolStripSeparator());
1347                     }
1348
1349                     ctxItem = new ToolStripMenuItem("New Folder", null, OnInvContextClick);
1350                     ctxItem.Name = "new_folder";
1351                     ctxInv.Items.Add(ctxItem);
1352
1353                     ctxItem = new ToolStripMenuItem("New Note", null, OnInvContextClick);
1354                     ctxItem.Name = "new_notecard";
1355                     ctxInv.Items.Add(ctxItem);
1356
1357                     ctxItem = new ToolStripMenuItem("New Script", null, OnInvContextClick);
1358                     ctxItem.Name = "new_script";
1359                     ctxInv.Items.Add(ctxItem);
1360
1361                     ctxItem = new ToolStripMenuItem("Refresh", null, OnInvContextClick);
1362                     ctxItem.Name = "refresh";
1363                     ctxInv.Items.Add(ctxItem);
1364
1365                     ctxItem = new ToolStripMenuItem("Backup...", null, OnInvContextClick);
1366                     ctxItem.Name = "backup";
1367                     ctxInv.Items.Add(ctxItem);
1368
1369                     ctxInv.Items.Add(new ToolStripSeparator());
1370
1371                     ctxItem = new ToolStripMenuItem("Expand", null, OnInvContextClick);
1372                     ctxItem.Name = "expand";
1373                     ctxInv.Items.Add(ctxItem);
1374
1375                     ctxItem = new ToolStripMenuItem("Expand All", null, OnInvContextClick);
1376                     ctxItem.Name = "expand_all";
1377                     ctxInv.Items.Add(ctxItem);
1378
1379                     ctxItem = new ToolStripMenuItem("Collapse", null, OnInvContextClick);
1380                     ctxItem.Name = "collapse";
1381                     ctxInv.Items.Add(ctxItem);
1382
1383                     if (folder.PreferredType == AssetType.TrashFolder)
1384                     {
1385                         ctxItem = new ToolStripMenuItem("Empty Trash", null, OnInvContextClick);
1386                         ctxItem.Name = "empty_trash";
1387                         ctxInv.Items.Add(ctxItem);
1388                     }
1389
1390                     if (folder.PreferredType == AssetType.LostAndFoundFolder)
1391                     {
1392                         ctxItem = new ToolStripMenuItem("Empty Lost and Found", null, OnInvContextClick);
1393                         ctxItem.Name = "empty_lost_found";
1394                         ctxInv.Items.Add(ctxItem);
1395                     }
1396
1397                     if (folder.PreferredType == AssetType.Unknown ||
1398                         folder.PreferredType == AssetType.OutfitFolder)
1399                     {
1400                         ctxItem = new ToolStripMenuItem("Rename", null, OnInvContextClick);
1401                         ctxItem.Name = "rename_folder";
1402                         ctxInv.Items.Add(ctxItem);
1403
1404                         ctxInv.Items.Add(new ToolStripSeparator());
1405
1406                         ctxItem = new ToolStripMenuItem("Cut", null, OnInvContextClick);
1407                         ctxItem.Name = "cut_folder";
1408                         ctxInv.Items.Add(ctxItem);
1409
1410                         ctxItem = new ToolStripMenuItem("Copy", null, OnInvContextClick);
1411                         ctxItem.Name = "copy_folder";
1412                         ctxInv.Items.Add(ctxItem);
1413                     }
1414
1415                     if (instance.InventoryClipboard != null)
1416                     {
1417                         ctxItem = new ToolStripMenuItem("Paste", null, OnInvContextClick);
1418                         ctxItem.Name = "paste_folder";
1419                         ctxInv.Items.Add(ctxItem);
1420
1421                         if (instance.InventoryClipboard.Item is InventoryItem)
1422                         {
1423                             ctxItem = new ToolStripMenuItem("Paste as Link", null, OnInvContextClick);
1424                             ctxItem.Name = "paste_folder_link";
1425                             ctxInv.Items.Add(ctxItem);
1426                         }
1427                     }
1428
1429                     if (folder.PreferredType == AssetType.Unknown ||
1430                         folder.PreferredType == AssetType.OutfitFolder)
1431                     {
1432                         ctxItem = new ToolStripMenuItem("Delete", null, OnInvContextClick);
1433                         ctxItem.Name = "delete_folder";
1434                         ctxInv.Items.Add(ctxItem);
1435
1436                         ctxInv.Items.Add(new ToolStripSeparator());
1437                     }
1438
1439                     if (folder.PreferredType == AssetType.Unknown || folder.PreferredType == AssetType.OutfitFolder)
1440                     {
1441                         ctxItem = new ToolStripMenuItem("Take off Items", null, OnInvContextClick);
1442                         ctxItem.Name = "outfit_take_off";
1443                         ctxInv.Items.Add(ctxItem);
1444
1445                         ctxItem = new ToolStripMenuItem("Add to Outfit", null, OnInvContextClick);
1446                         ctxItem.Name = "outfit_add";
1447                         ctxInv.Items.Add(ctxItem);
1448
1449                         ctxItem = new ToolStripMenuItem("Replace Outfit", null, OnInvContextClick);
1450                         ctxItem.Name = "outfit_replace";
1451                         ctxInv.Items.Add(ctxItem);
1452                     }
1453
1454                     instance.ContextActionManager.AddContributions(ctxInv, folder);
1455                 #endregion Folder context menu
1456                 }
1457                 else if (node.Tag is InventoryItem)
1458                 {
1459                     #region Item context menu
1460                     InventoryItem item = instance.COF.RealInventoryItem((InventoryItem)node.Tag);
1461                     ctxInv.Items.Clear();
1462
1463                     ToolStripMenuItem ctxItem;
1464
1465                     if (item.InventoryType == InventoryType.LSL)
1466                     {
1467                         ctxItem = new ToolStripMenuItem("Edit script", null, OnInvContextClick);
1468                         ctxItem.Name = "edit_script";
1469                         ctxInv.Items.Add(ctxItem);
1470                     }
1471
1472                     if (item.AssetType == AssetType.Texture)
1473                     {
1474                         ctxItem = new ToolStripMenuItem("View", null, OnInvContextClick);
1475                         ctxItem.Name = "view_image";
1476                         ctxInv.Items.Add(ctxItem);
1477                     }
1478
1479                     if (item.InventoryType == InventoryType.Landmark)
1480                     {
1481                         ctxItem = new ToolStripMenuItem("Teleport", null, OnInvContextClick);
1482                         ctxItem.Name = "lm_teleport";
1483                         ctxInv.Items.Add(ctxItem);
1484
1485                         ctxItem = new ToolStripMenuItem("Info", null, OnInvContextClick);
1486                         ctxItem.Name = "lm_info";
1487                         ctxInv.Items.Add(ctxItem);
1488                     }
1489
1490                     if (item.InventoryType == InventoryType.Notecard)
1491                     {
1492                         ctxItem = new ToolStripMenuItem("Open", null, OnInvContextClick);
1493                         ctxItem.Name = "notecard_open";
1494                         ctxInv.Items.Add(ctxItem);
1495                     }
1496
1497                     if (item.InventoryType == InventoryType.Gesture)
1498                     {
1499                         ctxItem = new ToolStripMenuItem("Play", null, OnInvContextClick);
1500                         ctxItem.Name = "gesture_play";
1501                         ctxInv.Items.Add(ctxItem);
1502
1503                         ctxItem = new ToolStripMenuItem("Info", null, OnInvContextClick);
1504                         ctxItem.Name = "gesture_info";
1505                         ctxInv.Items.Add(ctxItem);
1506                     }
1507
1508                     if (item.InventoryType == InventoryType.Animation)
1509                     {
1510                         if (!client.Self.SignaledAnimations.ContainsKey(item.AssetUUID))
1511                         {
1512                             ctxItem = new ToolStripMenuItem("Play", null, OnInvContextClick);
1513                             ctxItem.Name = "animation_play";
1514                             ctxInv.Items.Add(ctxItem);
1515                         }
1516                         else
1517                         {
1518                             ctxItem = new ToolStripMenuItem("Stop", null, OnInvContextClick);
1519                             ctxItem.Name = "animation_stop";
1520                             ctxInv.Items.Add(ctxItem);
1521                         }
1522                     }
1523
1524                     if (item.InventoryType == InventoryType.Object)
1525                     {
1526                         ctxItem = new ToolStripMenuItem("Rez inworld", null, OnInvContextClick);
1527                         ctxItem.Name = "rez_inworld";
1528                         ctxInv.Items.Add(ctxItem);
1529                     }
1530
1531                     ctxItem = new ToolStripMenuItem("Rename", null, OnInvContextClick);
1532                     ctxItem.Name = "rename_item";
1533                     ctxInv.Items.Add(ctxItem);
1534
1535                     ctxInv.Items.Add(new ToolStripSeparator());
1536
1537                     ctxItem = new ToolStripMenuItem("Cut", null, OnInvContextClick);
1538                     ctxItem.Name = "cut_item";
1539                     ctxInv.Items.Add(ctxItem);
1540
1541                     ctxItem = new ToolStripMenuItem("Copy", null, OnInvContextClick);
1542                     ctxItem.Name = "copy_item";
1543                     ctxInv.Items.Add(ctxItem);
1544
1545                     if (instance.InventoryClipboard != null)
1546                     {
1547                         ctxItem = new ToolStripMenuItem("Paste", null, OnInvContextClick);
1548                         ctxItem.Name = "paste_item";
1549                         ctxInv.Items.Add(ctxItem);
1550
1551                         if (instance.InventoryClipboard.Item is InventoryItem)
1552                         {
1553                             ctxItem = new ToolStripMenuItem("Paste as Link", null, OnInvContextClick);
1554                             ctxItem.Name = "paste_item_link";
1555                             ctxInv.Items.Add(ctxItem);
1556                         }
1557                     }
1558
1559                     ctxItem = new ToolStripMenuItem("Delete", null, OnInvContextClick);
1560                     ctxItem.Name = "delete_item";
1561
1562                     if (IsAttached(item) || IsWorn(item))
1563                     {
1564                         ctxItem.Enabled = false;
1565                     }
1566                     ctxInv.Items.Add(ctxItem);
1567
1568                     if (IsAttached(item) && instance.RLV.AllowDetach(attachments[item.UUID]))
1569                     {
1570                         ctxItem = new ToolStripMenuItem("Detach from yourself", null, OnInvContextClick);
1571                         ctxItem.Name = "detach";
1572                         ctxInv.Items.Add(ctxItem);
1573                     }
1574
1575                     if (!IsAttached(item) && (item.InventoryType == InventoryType.Object || item.InventoryType == InventoryType.Attachment))
1576                     {
1577                         ToolStripMenuItem ctxItemAttach = new ToolStripMenuItem("Attach to");
1578                         ctxInv.Items.Add(ctxItemAttach);
1579
1580                         ToolStripMenuItem ctxItemAttachHUD = new ToolStripMenuItem("Attach to HUD");
1581                         ctxInv.Items.Add(ctxItemAttachHUD);
1582
1583                         foreach (AttachmentPoint pt in Enum.GetValues(typeof(AttachmentPoint)))
1584                         {
1585                             if (!pt.ToString().StartsWith("HUD"))
1586                             {
1587                                 string name = Utils.EnumToText(pt);
1588
1589                                 InventoryItem alreadyAttached = null;
1590                                 if ((alreadyAttached = AttachmentAt(pt)) != null)
1591                                 {
1592                                     name += " (" + alreadyAttached.Name + ")";
1593                                 }
1594
1595                                 ToolStripMenuItem ptItem = new ToolStripMenuItem(name, null, OnInvContextClick);
1596                                 ptItem.Name = pt.ToString();
1597                                 ptItem.Tag = pt;
1598                                 ptItem.Name = "attach_to";
1599                                 ctxItemAttach.DropDownItems.Add(ptItem);
1600                             }
1601                             else
1602                             {
1603                                 string name = Utils.EnumToText(pt).Substring(3);
1604
1605                                 InventoryItem alreadyAttached = null;
1606                                 if ((alreadyAttached = AttachmentAt(pt)) != null)
1607                                 {
1608                                     name += " (" + alreadyAttached.Name + ")";
1609                                 }
1610
1611                                 ToolStripMenuItem ptItem = new ToolStripMenuItem(name, null, OnInvContextClick);
1612                                 ptItem.Name = pt.ToString();
1613                                 ptItem.Tag = pt;
1614                                 ptItem.Name = "attach_to";
1615                                 ctxItemAttachHUD.DropDownItems.Add(ptItem);
1616                             }
1617                         }
1618
1619                         ctxItem = new ToolStripMenuItem("Add to Worn", null, OnInvContextClick);
1620                         ctxItem.Name = "wear_attachment_add";
1621                         ctxInv.Items.Add(ctxItem);
1622
1623                         ctxItem = new ToolStripMenuItem("Wear", null, OnInvContextClick);
1624                         ctxItem.Name = "wear_attachment";
1625                         ctxInv.Items.Add(ctxItem);
1626                     }
1627
1628                     if (item is InventoryWearable)
1629                     {
1630                         ctxInv.Items.Add(new ToolStripSeparator());
1631
1632                         if (IsWorn(item))
1633                         {
1634                             ctxItem = new ToolStripMenuItem("Take off", null, OnInvContextClick);
1635                             ctxItem.Name = "item_take_off";
1636                             ctxInv.Items.Add(ctxItem);
1637                         }
1638                         else
1639                         {
1640                             ctxItem = new ToolStripMenuItem("Wear", null, OnInvContextClick);
1641                             ctxItem.Name = "item_wear";
1642                             ctxInv.Items.Add(ctxItem);
1643                         }
1644                     }
1645
1646                     instance.ContextActionManager.AddContributions(ctxInv, item);
1647                     #endregion Item context menu
1648                 }
1649             }
1650         }
1651
1652         #region Context menu folder
1653         private void OnInvContextClick(object sender, EventArgs e)
1654         {
1655             if (invTree.SelectedNode == null || !(invTree.SelectedNode.Tag is InventoryBase))
1656             {
1657                 return;
1658             }
1659
1660             string cmd = ((ToolStripMenuItem)sender).Name;
1661
1662             if (invTree.SelectedNode.Tag is InventoryFolder)
1663             {
1664                 #region Folder actions
1665                 InventoryFolder f = (InventoryFolder)invTree.SelectedNode.Tag;
1666
1667                 switch (cmd)
1668                 {
1669                     case "refresh":
1670                         foreach (TreeNode old in invTree.SelectedNode.Nodes)
1671                         {
1672                             if (!(old.Tag is InventoryFolder))
1673                             {
1674                                 removeNode(old);
1675                             }
1676                         }
1677                         fetchFolder(f.UUID, f.OwnerID, true);
1678                         break;
1679
1680                     case "backup":
1681                         (new InventoryBackup(instance, f.UUID)).Show();
1682                         break;
1683
1684                     case "expand":
1685                         invTree.SelectedNode.Expand();
1686                         break;
1687
1688                     case "expand_all":
1689                         invTree.SelectedNode.ExpandAll();
1690                         break;
1691
1692                     case "collapse":
1693                         invTree.SelectedNode.Collapse();
1694                         break;
1695
1696                     case "new_folder":
1697                         newItemName = "New folder";
1698                         client.Inventory.CreateFolder(f.UUID, "New folder");
1699                         break;
1700
1701                     case "fix_type":
1702                         client.Inventory.UpdateFolderProperties(f.UUID, f.ParentUUID, f.Name, AssetType.Unknown);
1703                         invTree.Sort();
1704                         break;
1705
1706                     case "new_notecard":
1707                         client.Inventory.RequestCreateItem(f.UUID, "New Note", "Radegast note: " + DateTime.Now.ToString(),
1708                             AssetType.Notecard, UUID.Zero, InventoryType.Notecard, PermissionMask.All, NotecardCreated);
1709                         break;
1710
1711                     case "new_script":
1712                         client.Inventory.RequestCreateItem(f.UUID, "New script", "Radegast script: " + DateTime.Now.ToString(),
1713                             AssetType.LSLText, UUID.Zero, InventoryType.LSL, PermissionMask.All, ScriptCreated);
1714                         break;
1715
1716                     case "cut_folder":
1717                         instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Cut, f);
1718                         break;
1719
1720                     case "copy_folder":
1721                         instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Copy, f);
1722                         break;
1723
1724                     case "paste_folder":
1725                         PerformClipboardOperation(invTree.SelectedNode.Tag as InventoryFolder);
1726                         break;
1727
1728                     case "paste_folder_link":
1729                         PerformLinkOperation(invTree.SelectedNode.Tag as InventoryFolder);
1730                         break;
1731
1732
1733                     case "delete_folder":
1734                         var trash = client.Inventory.FindFolderForType(AssetType.TrashFolder);
1735                         if (trash == Inventory.RootFolder.UUID)
1736                         {
1737                             WorkPool.QueueUserWorkItem(sync =>
1738                             {
1739                                 trashCreated.Reset();
1740                                 trash = client.Inventory.CreateFolder(Inventory.RootFolder.UUID, "Trash", AssetType.TrashFolder);
1741                                 trashCreated.WaitOne(20 * 1000, false);
1742                                 Thread.Sleep(200);
1743                                 client.Inventory.MoveFolder(f.UUID, trash, f.Name);
1744                             });
1745                             return;
1746                         }
1747
1748                         client.Inventory.MoveFolder(f.UUID, trash, f.Name);
1749                         break;
1750
1751                     case "empty_trash":
1752                         {
1753                             DialogResult res = MessageBox.Show("Are you sure you want to empty your trash?", "Confirmation", MessageBoxButtons.OKCancel);
1754                             if (res == DialogResult.OK)
1755                             {
1756                                 client.Inventory.EmptyTrash();
1757                             }
1758                         }
1759                         break;
1760
1761                     case "empty_lost_found":
1762                         {
1763                             DialogResult res = MessageBox.Show("Are you sure you want to empty your lost and found folder?", "Confirmation", MessageBoxButtons.OKCancel);
1764                             if (res == DialogResult.OK)
1765                             {
1766                                 client.Inventory.EmptyLostAndFound();
1767                             }
1768                         }
1769                         break;
1770
1771                     case "rename_folder":
1772                         invTree.SelectedNode.BeginEdit();
1773                         break;
1774
1775                     case "outfit_replace":
1776                         List<InventoryItem> newOutfit = new List<InventoryItem>();
1777                         foreach (InventoryBase item in Inventory.GetContents(f))
1778                         {
1779                             if (item is InventoryItem)
1780                                 newOutfit.Add((InventoryItem)item);
1781                         }
1782                         appearnceWasBusy = client.Appearance.ManagerBusy;
1783                         instance.COF.ReplaceOutfit(newOutfit);
1784                         UpdateWornLabels();
1785                         break;
1786
1787                     case "outfit_add":
1788                         List<InventoryItem> addToOutfit = new List<InventoryItem>();
1789                         foreach (InventoryBase item in Inventory.GetContents(f))
1790                         {
1791                             if (item is InventoryItem)
1792                                 addToOutfit.Add((InventoryItem)item);
1793                         }
1794                         appearnceWasBusy = client.Appearance.ManagerBusy;
1795                         instance.COF.AddToOutfit(addToOutfit, true);
1796                         UpdateWornLabels();
1797                         break;
1798
1799                     case "outfit_take_off":
1800                         List<InventoryItem> removeFromOutfit = new List<InventoryItem>();
1801                         foreach (InventoryBase item in Inventory.GetContents(f))
1802                         {
1803                             if (item is InventoryItem)
1804                                 removeFromOutfit.Add((InventoryItem)item);
1805                         }
1806                         appearnceWasBusy = client.Appearance.ManagerBusy;
1807                         instance.COF.RemoveFromOutfit(removeFromOutfit);
1808                         UpdateWornLabels();
1809                         break;
1810                 }
1811                 #endregion
1812             }
1813             else if (invTree.SelectedNode.Tag is InventoryItem)
1814             {
1815                 #region Item actions
1816                 InventoryItem item = (InventoryItem)invTree.SelectedNode.Tag;
1817
1818                 // Copy, cut, and delete works on links directly
1819                 // The rest operate on the item that is pointed by the link
1820                 if (cmd != "copy_item" && cmd != "cut_item" && cmd != "delete_item")
1821                 {
1822                     item = instance.COF.RealInventoryItem(item);
1823                 }
1824
1825                 switch (cmd)
1826                 {
1827                     case "copy_item":
1828                         instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Copy, item);
1829                         break;
1830
1831                     case "cut_item":
1832                         instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Cut, item);
1833                         break;
1834
1835                     case "paste_item":
1836                         PerformClipboardOperation(invTree.SelectedNode.Parent.Tag as InventoryFolder);
1837                         break;
1838
1839                     case "paste_item_link":
1840                         PerformLinkOperation(invTree.SelectedNode.Parent.Tag as InventoryFolder);
1841                         break;
1842
1843                     case "delete_item":
1844                         var trash = client.Inventory.FindFolderForType(AssetType.TrashFolder);
1845                         if (trash == Inventory.RootFolder.UUID)
1846                         {
1847                             WorkPool.QueueUserWorkItem(sync =>
1848                             {
1849                                 trashCreated.Reset();
1850                                 trash = client.Inventory.CreateFolder(Inventory.RootFolder.UUID, "Trash", AssetType.TrashFolder);
1851                                 trashCreated.WaitOne(20 * 1000, false);
1852                                 Thread.Sleep(200);
1853                                 client.Inventory.MoveItem(item.UUID, trash, item.Name);
1854                             });
1855                             return;
1856                         }
1857
1858                         client.Inventory.MoveItem(item.UUID, client.Inventory.FindFolderForType(AssetType.TrashFolder), item.Name);
1859                         break;
1860
1861                     case "rename_item":
1862                         invTree.SelectedNode.BeginEdit();
1863                         break;
1864
1865                     case "detach":
1866                         instance.COF.Detach(item);
1867                         lock (attachments) attachments.Remove(item.UUID);
1868                         invTree.SelectedNode.Text = ItemLabel(item, false);
1869                         break;
1870
1871                     case "wear_attachment":
1872                         instance.COF.Attach(item, AttachmentPoint.Default, true);
1873                         break;
1874
1875                     case "wear_attachment_add":
1876                         instance.COF.Attach(item, AttachmentPoint.Default, false);
1877                         break;
1878
1879                     case "attach_to":
1880                         AttachmentPoint pt = (AttachmentPoint)((ToolStripMenuItem)sender).Tag;
1881                         instance.COF.Attach(item, pt, true);
1882                         break;
1883
1884                     case "edit_script":
1885                         ScriptEditor se = new ScriptEditor(instance, (InventoryLSL)item);
1886                         se.Detached = true;
1887                         return;
1888
1889                     case "view_image":
1890                         UpdateItemInfo(item);
1891                         break;
1892
1893                     case "item_take_off":
1894                         appearnceWasBusy = client.Appearance.ManagerBusy;
1895                         instance.COF.RemoveFromOutfit(item);
1896                         invTree.SelectedNode.Text = ItemLabel(item, false);
1897                         lock (WornItems)
1898                         {
1899                             if (WornItems.Contains(item.UUID))
1900                             {
1901                                 WornItems.Remove(item.UUID);
1902                             }
1903                         }
1904                         break;
1905
1906                     case "item_wear":
1907                         appearnceWasBusy = client.Appearance.ManagerBusy;
1908                         instance.COF.AddToOutfit(item, true);
1909                         invTree.SelectedNode.Text = ItemLabel(item, false);
1910                         break;
1911
1912                     case "lm_teleport":
1913                         instance.TabConsole.DisplayNotificationInChat("Teleporting to " + item.Name);
1914                         client.Self.RequestTeleport(item.AssetUUID);
1915                         break;
1916
1917                     case "lm_info":
1918                         UpdateItemInfo(item);
1919                         break;
1920
1921                     case "notecard_open":
1922                         UpdateItemInfo(item);
1923                         break;
1924
1925                     case "gesture_info":
1926                         UpdateItemInfo(item);
1927                         break;
1928
1929                     case "gesture_play":
1930                         client.Self.PlayGesture(item.AssetUUID);
1931                         break;
1932
1933                     case "animation_play":
1934                         Dictionary<UUID, bool> anim = new Dictionary<UUID, bool>();
1935                         anim.Add(item.AssetUUID, true);
1936                         client.Self.Animate(anim, true);
1937                         break;
1938
1939                     case "animation_stop":
1940                         Dictionary<UUID, bool> animStop = new Dictionary<UUID, bool>();
1941                         animStop.Add(item.AssetUUID, false);
1942                         client.Self.Animate(animStop, true);
1943                         break;
1944
1945                     case "rez_inworld":
1946                         instance.MediaManager.PlayUISound(UISounds.ObjectRez);
1947                         Vector3 rezpos = new Vector3(2, 0, 0);
1948                         rezpos = client.Self.SimPosition + rezpos * client.Self.Movement.BodyRotation;
1949                         client.Inventory.RequestRezFromInventory(client.Network.CurrentSim, Quaternion.Identity, rezpos, item);
1950                         break;
1951                 }
1952                 #endregion
1953             }
1954         }
1955
1956         void NotecardCreated(bool success, InventoryItem item)
1957         {
1958             if (InvokeRequired)
1959             {
1960                 BeginInvoke(new MethodInvoker(() => NotecardCreated(success, item)));
1961                 return;
1962             }
1963
1964             if (!success)
1965             {
1966                 instance.TabConsole.DisplayNotificationInChat("Creation of notecard failed");
1967                 return;
1968             }
1969
1970             instance.TabConsole.DisplayNotificationInChat("New notecard created, enter notecard name and press enter", ChatBufferTextStyle.Invisible);
1971             var node = findNodeForItem(item.ParentUUID);
1972             if (node != null) node.Expand();
1973             node = findNodeForItem(item.UUID);
1974             if (node != null)
1975             {
1976                 invTree.SelectedNode = node;
1977                 node.BeginEdit();
1978             }
1979         }
1980
1981         void ScriptCreated(bool success, InventoryItem item)
1982         {
1983             if (InvokeRequired)
1984             {
1985                 BeginInvoke(new MethodInvoker(() => ScriptCreated(success, item)));
1986                 return;
1987             }
1988
1989             if (!success)
1990             {
1991                 instance.TabConsole.DisplayNotificationInChat("Creation of script failed");
1992                 return;
1993             }
1994
1995             instance.TabConsole.DisplayNotificationInChat("New script created, enter script name and press enter", ChatBufferTextStyle.Invisible);
1996             var node = findNodeForItem(item.ParentUUID);
1997             if (node != null) node.Expand();
1998             node = findNodeForItem(item.UUID);
1999             if (node != null)
2000             {
2001                 invTree.SelectedNode = node;
2002                 node.BeginEdit();
2003             }
2004         }
2005
2006         void PerformClipboardOperation(InventoryFolder dest)
2007         {
2008             if (instance.InventoryClipboard == null) return;
2009
2010             if (dest == null) return;
2011
2012             if (instance.InventoryClipboard.Operation == ClipboardOperation.Cut)
2013             {
2014                 if (instance.InventoryClipboard.Item is InventoryItem)
2015                 {
2016                     client.Inventory.MoveItem(instance.InventoryClipboard.Item.UUID, dest.UUID, instance.InventoryClipboard.Item.Name);
2017                 }
2018                 else if (instance.InventoryClipboard.Item is InventoryFolder)
2019                 {
2020                     if (instance.InventoryClipboard.Item.UUID != dest.UUID)
2021                     {
2022                         client.Inventory.MoveFolder(instance.InventoryClipboard.Item.UUID, dest.UUID, instance.InventoryClipboard.Item.Name);
2023                     }
2024                 }
2025
2026                 instance.InventoryClipboard = null;
2027             }
2028             else if (instance.InventoryClipboard.Operation == ClipboardOperation.Copy)
2029             {
2030                 if (instance.InventoryClipboard.Item is InventoryItem)
2031                 {
2032                     client.Inventory.RequestCopyItem(instance.InventoryClipboard.Item.UUID, dest.UUID, instance.InventoryClipboard.Item.Name, instance.InventoryClipboard.Item.OwnerID, (InventoryBase target) =>
2033                     {
2034                     }
2035                     );
2036                 }
2037                 else if (instance.InventoryClipboard.Item is InventoryFolder)
2038                 {
2039                     WorkPool.QueueUserWorkItem((object state) =>
2040                         {
2041                             UUID newFolderID = client.Inventory.CreateFolder(dest.UUID, instance.InventoryClipboard.Item.Name, AssetType.Unknown);
2042                             Thread.Sleep(500);
2043
2044                             // FIXME: for some reason copying a bunch of items in one operation does not work
2045
2046                             //List<UUID> items = new List<UUID>();
2047                             //List<UUID> folders = new List<UUID>();
2048                             //List<string> names = new List<string>();
2049                             //UUID oldOwner = UUID.Zero;
2050
2051                             foreach (InventoryBase oldItem in Inventory.GetContents((InventoryFolder)instance.InventoryClipboard.Item))
2052                             {
2053                                 //folders.Add(newFolderID);
2054                                 //names.Add(oldItem.Name);
2055                                 //items.Add(oldItem.UUID);
2056                                 //oldOwner = oldItem.OwnerID;
2057                                 client.Inventory.RequestCopyItem(oldItem.UUID, newFolderID, oldItem.Name, oldItem.OwnerID, (InventoryBase target) => { });
2058                             }
2059
2060                             //if (folders.Count > 0)
2061                             //{
2062                             //    client.Inventory.RequestCopyItems(items, folders, names, oldOwner, (InventoryBase target) => { });
2063                             //}
2064                         }
2065                     );
2066                 }
2067             }
2068         }
2069
2070         void PerformLinkOperation(InventoryFolder dest)
2071         {
2072             if (instance.InventoryClipboard == null) return;
2073
2074             if (dest == null) return;
2075
2076             client.Inventory.CreateLink(dest.UUID, instance.InventoryClipboard.Item, (bool success, InventoryItem item) => { });
2077         }
2078
2079         #endregion
2080
2081         private void UpdateWornLabels()
2082         {
2083             if (InvokeRequired)
2084             {
2085                 BeginInvoke(new MethodInvoker(UpdateWornLabels));
2086                 return;
2087             }
2088
2089             invTree.BeginUpdate();
2090             foreach (UUID itemID in WornItems)
2091             {
2092                 TreeNode node = findNodeForItem(itemID);
2093                 if (node != null)
2094                 {
2095                     node.Text = ItemLabel((InventoryBase)node.Tag, false);
2096                 }
2097             }
2098             WornItems.Clear();
2099             foreach (AppearanceManager.WearableData wearable in client.Appearance.GetWearables().Values)
2100             {
2101                 TreeNode node = findNodeForItem(wearable.ItemID);
2102                 if (node != null)
2103                 {
2104                     node.Text = ItemLabel((InventoryBase)node.Tag, false);
2105                 }
2106             }
2107             invTree.EndUpdate();
2108         }
2109
2110         void TreeView_AfterExpand(object sender, TreeViewEventArgs e)
2111         {
2112             // Check if we need to go into edit mode for new items
2113             if (newItemName != string.Empty)
2114             {
2115                 foreach (TreeNode n in e.Node.Nodes)
2116                 {
2117                     if (n.Name == newItemName)
2118                     {
2119                         n.BeginEdit();
2120                         break;
2121                     }
2122                 }
2123                 newItemName = string.Empty;
2124             }
2125         }
2126
2127         private bool _EditingNode = false;
2128
2129         private void OnLabelEditTimer(object sender)
2130         {
2131             if (_EditNode == null || !(_EditNode.Tag is InventoryBase))
2132                 return;
2133
2134             if (InvokeRequired)
2135             {
2136                 BeginInvoke(new MethodInvoker(delegate()
2137                     {
2138                         OnLabelEditTimer(sender);
2139                     }
2140                 ));
2141                 return;
2142             }
2143
2144             _EditingNode = true;
2145             _EditNode.Text = ItemLabel((InventoryBase)_EditNode.Tag, true);
2146             _EditNode.BeginEdit();
2147         }
2148
2149         private void invTree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
2150         {
2151             if (e.Node == null ||
2152                 !(e.Node.Tag is InventoryBase) ||
2153                 (e.Node.Tag is InventoryFolder &&
2154                 ((InventoryFolder)e.Node.Tag).PreferredType != AssetType.Unknown &&
2155                 ((InventoryFolder)e.Node.Tag).PreferredType != AssetType.OutfitFolder)
2156                 )
2157             {
2158                 e.CancelEdit = true;
2159                 return;
2160             }
2161
2162             if (_EditingNode)
2163             {
2164                 _EditingNode = false;
2165             }
2166             else
2167             {
2168                 e.CancelEdit = true;
2169                 _EditNode = e.Node;
2170                 _EditTimer.Change(20, System.Threading.Timeout.Infinite);
2171             }
2172         }
2173
2174         private void invTree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
2175         {
2176             if (string.IsNullOrEmpty(e.Label))
2177             {
2178                 if (e.Node.Tag is InventoryBase)
2179                 {
2180                     e.Node.Text = ItemLabel((InventoryBase)e.Node.Tag, false);
2181                 }
2182                 e.CancelEdit = true;
2183                 return;
2184             }
2185
2186             if (e.Node.Tag is InventoryFolder)
2187             {
2188                 InventoryFolder f = (InventoryFolder)e.Node.Tag;
2189                 f.Name = e.Label;
2190                 client.Inventory.MoveFolder(f.UUID, f.ParentUUID, f.Name);
2191             }
2192             else if (e.Node.Tag is InventoryItem)
2193             {
2194                 InventoryItem item = (InventoryItem)e.Node.Tag;
2195                 item.Name = e.Label;
2196                 e.Node.Text = ItemLabel((InventoryBase)item, false);
2197                 client.Inventory.MoveItem(item.UUID, item.ParentUUID, item.Name);
2198                 UpdateItemInfo(item);
2199             }
2200
2201         }
2202
2203         private void invTree_KeyUp(object sender, KeyEventArgs e)
2204         {
2205             if (e.KeyCode == Keys.F2 && invTree.SelectedNode != null)
2206             {
2207                 invTree.SelectedNode.BeginEdit();
2208             }
2209             else if (e.KeyCode == Keys.F5 && invTree.SelectedNode != null)
2210             {
2211                 if (invTree.SelectedNode.Tag is InventoryFolder)
2212                 {
2213                     InventoryFolder f = (InventoryFolder)invTree.SelectedNode.Tag;
2214                     fetchFolder(f.UUID, f.OwnerID, true);
2215                 }
2216             }
2217             else if (e.KeyCode == Keys.Delete && invTree.SelectedNode != null)
2218             {
2219                 var trash = client.Inventory.FindFolderForType(AssetType.TrashFolder);
2220                 if (trash == Inventory.RootFolder.UUID)
2221                 {
2222                     trash = client.Inventory.CreateFolder(Inventory.RootFolder.UUID, "Trash", AssetType.TrashFolder);
2223                     Thread.Sleep(2000);
2224                 }
2225
2226                 if (invTree.SelectedNode.Tag is InventoryItem)
2227                 {
2228                     InventoryItem item = invTree.SelectedNode.Tag as InventoryItem;
2229                     client.Inventory.MoveItem(item.UUID, trash, item.Name);
2230                 }
2231                 else if (invTree.SelectedNode.Tag is InventoryFolder)
2232                 {
2233                     InventoryFolder f = invTree.SelectedNode.Tag as InventoryFolder;
2234                     client.Inventory.MoveFolder(f.UUID, trash, f.Name);
2235                 }
2236             }
2237             else if (e.KeyCode == Keys.Apps && invTree.SelectedNode != null)
2238             {
2239                 ctxInv.Show();
2240             }
2241         }
2242
2243         #region Drag and Drop
2244         private void invTree_ItemDrag(object sender, ItemDragEventArgs e)
2245         {
2246             invTree.SelectedNode = e.Item as TreeNode;
2247             if (invTree.SelectedNode.Tag is InventoryFolder && ((InventoryFolder)invTree.SelectedNode.Tag).PreferredType != AssetType.Unknown)
2248             {
2249                 return;
2250             }
2251             invTree.DoDragDrop(e.Item, DragDropEffects.Move);
2252         }
2253
2254         private void invTree_DragDrop(object sender, DragEventArgs e)
2255         {
2256             if (highlightedNode != null)
2257             {
2258                 highlightedNode.BackColor = invTree.BackColor;
2259                 highlightedNode = null;
2260             }
2261
2262             TreeNode sourceNode = e.Data.GetData(typeof(TreeNode)) as TreeNode;
2263             if (sourceNode == null) return;
2264
2265             Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
2266             TreeNode destinationNode = ((TreeView)sender).GetNodeAt(pt);
2267
2268             if (destinationNode == null) return;
2269
2270             if (sourceNode == destinationNode) return;
2271
2272             // If droping to item within folder drop to its folder
2273             if (destinationNode.Tag is InventoryItem)
2274             {
2275                 destinationNode = destinationNode.Parent;
2276             }
2277
2278             InventoryFolder dest = destinationNode.Tag as InventoryFolder;
2279
2280             if (dest == null) return;
2281
2282             if (sourceNode.Tag is InventoryItem)
2283             {
2284                 InventoryItem item = (InventoryItem)sourceNode.Tag;
2285                 client.Inventory.MoveItem(item.UUID, dest.UUID, item.Name);
2286             }
2287             else if (sourceNode.Tag is InventoryFolder)
2288             {
2289                 InventoryFolder f = (InventoryFolder)sourceNode.Tag;
2290                 client.Inventory.MoveFolder(f.UUID, dest.UUID, f.Name);
2291             }
2292         }
2293
2294         private void invTree_DragEnter(object sender, DragEventArgs e)
2295         {
2296             TreeNode node = e.Data.GetData(typeof(TreeNode)) as TreeNode;
2297             if (node == null)
2298             {
2299                 e.Effect = DragDropEffects.None;
2300             }
2301             else
2302             {
2303                 e.Effect = DragDropEffects.Move;
2304             }
2305         }
2306
2307         TreeNode highlightedNode = null;
2308
2309         private void invTree_DragOver(object sender, DragEventArgs e)
2310         {
2311             TreeNode node = e.Data.GetData(typeof(TreeNode)) as TreeNode;
2312             if (node == null)
2313             {
2314                 e.Effect = DragDropEffects.None;
2315             }
2316
2317             Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
2318             TreeNode destinationNode = ((TreeView)sender).GetNodeAt(pt);
2319
2320             if (highlightedNode != destinationNode)
2321             {
2322                 if (highlightedNode != null)
2323                 {
2324                     highlightedNode.BackColor = invTree.BackColor;
2325                     highlightedNode = null;
2326                 }
2327
2328                 if (destinationNode != null)
2329                 {
2330                     highlightedNode = destinationNode;
2331                     highlightedNode.BackColor = Color.LightSlateGray;
2332                 }
2333             }
2334
2335             if (destinationNode == null)
2336             {
2337                 e.Effect = DragDropEffects.None;
2338                 return;
2339             }
2340
2341             e.Effect = DragDropEffects.Move;
2342
2343         }
2344         #endregion
2345
2346         private void saveAllTToolStripMenuItem_Click(object sender, EventArgs e)
2347         {
2348             (new InventoryBackup(instance, Inventory.RootFolder.UUID)).Show();
2349         }
2350
2351         private void tbtnSystemFoldersFirst_Click(object sender, EventArgs e)
2352         {
2353             sorter.SystemFoldersFirst = tbtnSystemFoldersFirst.Checked = !sorter.SystemFoldersFirst;
2354             instance.GlobalSettings["inv_sort_sysfirst"] = OSD.FromBoolean(sorter.SystemFoldersFirst);
2355             invTree.Sort();
2356         }
2357
2358         private void tbtbSortByName_Click(object sender, EventArgs e)
2359         {
2360             if (tbtbSortByName.Checked) return;
2361
2362             tbtbSortByName.Checked = true;
2363             tbtnSortByDate.Checked = sorter.ByDate = false;
2364             instance.GlobalSettings["inv_sort_bydate"] = OSD.FromBoolean(sorter.ByDate);
2365
2366             invTree.Sort();
2367         }
2368
2369         private void tbtnSortByDate_Click(object sender, EventArgs e)
2370         {
2371             if (tbtnSortByDate.Checked) return;
2372
2373             tbtbSortByName.Checked = false;
2374             tbtnSortByDate.Checked = sorter.ByDate = true;
2375             instance.GlobalSettings["inv_sort_bydate"] = OSD.FromBoolean(sorter.ByDate);
2376
2377             invTree.Sort();
2378         }
2379
2380         #region Search
2381
2382         public class SearchResult
2383         {
2384             public InventoryBase Inv;
2385             public int Level;
2386
2387             public SearchResult(InventoryBase inv, int level)
2388             {
2389                 this.Inv = inv;
2390                 this.Level = level;
2391             }
2392         }
2393
2394         List<SearchResult> searchRes;
2395         string searchString;
2396         Dictionary<int, ListViewItem> searchItemCache = new Dictionary<int, ListViewItem>();
2397         ListViewItem emptyItem = null;
2398         int found;
2399
2400         void PerformRecursiveSearch(int level, UUID folderID)
2401         {
2402             var me = Inventory.Items[folderID].Data;
2403             searchRes.Add(new SearchResult(me, level));
2404             var sorted = Inventory.GetContents(folderID);
2405
2406             sorted.Sort((InventoryBase b1, InventoryBase b2) =>
2407             {
2408                 if (b1 is InventoryFolder && !(b2 is InventoryFolder))
2409                 {
2410                     return -1;
2411                 }
2412                 else if (!(b1 is InventoryFolder) && b2 is InventoryFolder)
2413                 {
2414                     return 1;
2415                 }
2416                 else
2417                 {
2418                     return string.Compare(b1.Name, b2.Name);
2419                 }
2420             });
2421
2422             foreach (var item in sorted)
2423             {
2424                 if (item is InventoryFolder)
2425                 {
2426                     PerformRecursiveSearch(level + 1, item.UUID);
2427                 }
2428                 else
2429                 {
2430                     var it = item as InventoryItem;
2431                     bool add = false;
2432
2433                     if (cbSrchName.Checked && it.Name.ToLower().Contains(searchString))
2434                     {
2435                         add = true;
2436                     }
2437                     else if (cbSrchDesc.Checked && it.Description.ToLower().Contains(searchString))
2438                     {
2439                         add = true;
2440                     }
2441
2442                     if (cbSrchWorn.Checked && add &&
2443                             !(
2444                                 (it.InventoryType == InventoryType.Wearable && IsWorn(it)) ||
2445                                 ((it.InventoryType == InventoryType.Attachment || it.InventoryType == InventoryType.Object) && IsAttached(it))
2446                             )
2447                     )
2448                     {
2449                         add = false;
2450                     }
2451
2452                     if (cbSrchRecent.Checked && add && it.CreationDate < instance.StartupTimeUTC)
2453                     {
2454                         add = false;
2455                     }
2456
2457                     if (add)
2458                     {
2459                         found++;
2460                         searchRes.Add(new SearchResult(it, level + 1));
2461                     }
2462                 }
2463             }
2464
2465             if (searchRes[searchRes.Count - 1].Inv == me)
2466             {
2467                 searchRes.RemoveAt(searchRes.Count - 1);
2468             }
2469         }
2470
2471         public void UpdateSearch()
2472         {
2473             found = 0;
2474
2475             if (instance.MonoRuntime)
2476             {
2477                 lstInventorySearch.VirtualMode = false;
2478                 lstInventorySearch.Items.Clear();
2479                 lstInventorySearch.VirtualMode = true;
2480             }
2481
2482             lstInventorySearch.VirtualListSize = 0;
2483             searchString = txtSearch.Text.Trim().ToLower();
2484
2485             //if (searchString == string.Empty && rbSrchAll.Checked)
2486             //{
2487             //    lblSearchStatus.Text = "0 results";
2488             //    return;
2489             //}
2490
2491             if (emptyItem == null)
2492             {
2493                 emptyItem = new ListViewItem(string.Empty);
2494             }
2495
2496             searchRes = new List<SearchResult>(Inventory.Items.Count);
2497             searchItemCache.Clear();
2498             PerformRecursiveSearch(0, Inventory.RootFolder.UUID);
2499             lstInventorySearch.VirtualListSize = searchRes.Count;
2500             lblSearchStatus.Text = string.Format("{0} results", found);
2501         }
2502
2503         private void lstInventorySearch_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
2504         {
2505             if (searchItemCache.ContainsKey(e.ItemIndex))
2506             {
2507                 e.Item = searchItemCache[e.ItemIndex];
2508             }
2509             else if (e.ItemIndex < searchRes.Count)
2510             {
2511                 InventoryBase inv = searchRes[e.ItemIndex].Inv;
2512                 string desc = inv.Name;
2513                 if (inv is InventoryItem)
2514                 {
2515                     desc += string.Format(" - {0}", ((InventoryItem)inv).Description);
2516                 }
2517                 ListViewItem item = new ListViewItem(desc);
2518                 item.Tag = searchRes[e.ItemIndex];
2519                 e.Item = item;
2520                 searchItemCache[e.ItemIndex] = item;
2521             }
2522             else
2523             {
2524                 e.Item = emptyItem;
2525             }
2526         }
2527
2528         private void btnInvSearch_Click(object sender, EventArgs e)
2529         {
2530             UpdateSearch();
2531         }
2532
2533         private void cbSrchName_CheckedChanged(object sender, EventArgs e)
2534         {
2535             if (!cbSrchName.Checked && !cbSrchDesc.Checked && !cbSrchCreator.Checked)
2536             {
2537                 cbSrchName.Checked = true;
2538             }
2539             UpdateSearch();
2540         }
2541
2542         private void cbSrchWorn_CheckedChanged(object sender, EventArgs e)
2543         {
2544             UpdateSearch();
2545         }
2546
2547         private void txtSearch_KeyDown(object sender, KeyEventArgs e)
2548         {
2549             if (e.KeyCode == Keys.Enter)
2550             {
2551                 e.Handled = e.SuppressKeyPress = true;
2552                 if (txtSearch.Text.Trim().Length > 0)
2553                 {
2554                     UpdateSearch();
2555                 }
2556             }
2557         }
2558
2559         private void lstInventorySearch_DrawItem(object sender, DrawListViewItemEventArgs e)
2560         {
2561             Graphics g = e.Graphics;
2562             e.DrawBackground();
2563
2564             if (!(e.Item.Tag is SearchResult))
2565                 return;
2566
2567             if (e.Item.Selected)
2568             {
2569                 g.FillRectangle(SystemBrushes.Highlight, e.Bounds);
2570             }
2571
2572             SearchResult res = e.Item.Tag as SearchResult;
2573             int offset = 20 * (res.Level + 1);
2574             Rectangle rec = new Rectangle(e.Bounds.X + offset, e.Bounds.Y, e.Bounds.Width - offset, e.Bounds.Height);
2575
2576             Image icon = null;
2577             int iconIx = 0;
2578
2579             if (res.Inv is InventoryFolder)
2580             {
2581                 iconIx = GetDirImageIndex(((InventoryFolder)res.Inv).PreferredType.ToString().ToLower());
2582             }
2583             else if (res.Inv is InventoryWearable)
2584             {
2585                 iconIx = GetItemImageIndex(((InventoryWearable)res.Inv).WearableType.ToString().ToLower());
2586             }
2587             else if (res.Inv is InventoryItem)
2588             {
2589                 iconIx = GetItemImageIndex(((InventoryItem)res.Inv).AssetType.ToString().ToLower());
2590             }
2591
2592             if (iconIx < 0)
2593             {
2594                 iconIx = 0;
2595             }
2596
2597             try
2598             {
2599                 icon = frmMain.ResourceImages.Images[iconIx];
2600                 g.DrawImageUnscaled(icon, e.Bounds.X + offset - 18, e.Bounds.Y);
2601             }
2602             catch { }
2603
2604             using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap | StringFormatFlags.LineLimit))
2605             {
2606                 string label = ItemLabel(res.Inv, false);
2607                 SizeF len = e.Graphics.MeasureString(label, lstInventorySearch.Font, rec.Width, sf);
2608
2609                 e.Graphics.DrawString(
2610                     ItemLabel(res.Inv, false),
2611                     lstInventorySearch.Font,
2612                     e.Item.Selected ? SystemBrushes.HighlightText : SystemBrushes.WindowText,
2613                     rec,
2614                     sf);
2615
2616                 if (res.Inv is InventoryItem)
2617                 {
2618                     string desc = ((InventoryItem)res.Inv).Description.Trim();
2619                     if (desc != string.Empty)
2620                     {
2621                         using (Font descFont = new Font(lstInventorySearch.Font, FontStyle.Italic))
2622                         {
2623                             e.Graphics.DrawString(desc,
2624                                 descFont,
2625                                 e.Item.Selected ? SystemBrushes.HighlightText : SystemBrushes.GrayText,
2626                                 rec.X + len.Width + 5,
2627                                 rec.Y,
2628                                 sf);
2629                         }
2630                     }
2631                 }
2632             }
2633
2634         }
2635
2636         private void lstInventorySearch_SizeChanged(object sender, EventArgs e)
2637         {
2638             chResItemName.Width = lstInventorySearch.Width - 30;
2639         }
2640
2641         private void txtSearch_TextChanged(object sender, EventArgs e)
2642         {
2643             UpdateSearch();
2644         }
2645
2646         private void rbSrchAll_CheckedChanged(object sender, EventArgs e)
2647         {
2648             UpdateSearch();
2649         }
2650
2651         private void lstInventorySearch_KeyDown(object sender, KeyEventArgs e)
2652         {
2653             if ((e.KeyCode == Keys.Apps) || (e.Control && e.KeyCode == RadegastContextMenuStrip.ContexMenuKeyCode))
2654             {
2655                 lstInventorySearch_MouseClick(sender, new MouseEventArgs(MouseButtons.Right, 1, 50, 150, 0));
2656             }
2657         }
2658
2659         /// <summary>
2660         /// Finds and higlights inventory node
2661         /// </summary>
2662         /// <param name="itemID">Inventory of ID of the item to select</param>
2663         public void SelectInventoryNode(UUID itemID)
2664         {
2665             TreeNode node = findNodeForItem(itemID);
2666             if (node == null)
2667                 return;
2668             invTree.SelectedNode = node;
2669             if (node.Tag is InventoryItem)
2670             {
2671                 UpdateItemInfo(node.Tag as InventoryItem);
2672             }
2673         }
2674
2675         private void lstInventorySearch_MouseClick(object sender, MouseEventArgs e)
2676         {
2677             if (lstInventorySearch.SelectedIndices.Count != 1)
2678                 return;
2679
2680             try
2681             {
2682                 SearchResult res = searchRes[lstInventorySearch.SelectedIndices[0]];
2683                 TreeNode node = findNodeForItem(res.Inv.UUID);
2684                 if (node == null)
2685                     return;
2686                 invTree.SelectedNode = node;
2687                 if (e.Button == MouseButtons.Right)
2688                 {
2689                     ctxInv.Show(lstInventorySearch, e.X, e.Y);
2690                 }
2691             }
2692             catch { }
2693         }
2694
2695         private void lstInventorySearch_MouseDoubleClick(object sender, MouseEventArgs e)
2696         {
2697             if (lstInventorySearch.SelectedIndices.Count != 1)
2698                 return;
2699
2700             try
2701             {
2702                 SearchResult res = searchRes[lstInventorySearch.SelectedIndices[0]];
2703                 TreeNode node = findNodeForItem(res.Inv.UUID);
2704                 if (node == null)
2705                     return;
2706                 invTree.SelectedNode = node;
2707                 invTree_NodeMouseDoubleClick(null, null);
2708             }
2709             catch { }
2710
2711         }
2712         #endregion Search
2713
2714         private void txtAssetID_Enter(object sender, EventArgs e)
2715         {
2716             txtAssetID.SelectAll();
2717         }
2718
2719         private void txtInvID_Enter(object sender, EventArgs e)
2720         {
2721             txtInvID.SelectAll();
2722         }
2723
2724         private void copyInitialOutfitsToolStripMenuItem_Click(object sender, EventArgs e)
2725         {
2726             var c = new FolderCopy(instance);
2727             c.GetFolders("Initial Outfits");
2728         }
2729
2730
2731     }
2732
2733     #region Sorter class
2734     // Create a node sorter that implements the IComparer interface.
2735     public class InvNodeSorter : System.Collections.IComparer
2736     {
2737         bool _sysfirst = true;
2738         bool _bydate = true;
2739
2740         int CompareFolders(InventoryFolder x, InventoryFolder y)
2741         {
2742             if (_sysfirst)
2743             {
2744                 if (x.PreferredType != AssetType.Unknown && y.PreferredType == AssetType.Unknown)
2745                 {
2746                     return -1;
2747                 }
2748                 else if (x.PreferredType == AssetType.Unknown && y.PreferredType != AssetType.Unknown)
2749                 {
2750                     return 1;
2751                 }
2752             }
2753             return String.Compare(x.Name, y.Name);
2754         }
2755
2756         public bool SystemFoldersFirst { set { _sysfirst = value; } get { return _sysfirst; } }
2757         public bool ByDate { set { _bydate = value; } get { return _bydate; } }
2758
2759         public int Compare(object x, object y)
2760         {
2761             TreeNode tx = x as TreeNode;
2762             TreeNode ty = y as TreeNode;
2763
2764             if (tx.Tag is InventoryFolder && ty.Tag is InventoryFolder)
2765             {
2766                 return CompareFolders(tx.Tag as InventoryFolder, ty.Tag as InventoryFolder);
2767             }
2768             else if (tx.Tag is InventoryFolder && ty.Tag is InventoryItem)
2769             {
2770                 return -1;
2771             }
2772             else if (tx.Tag is InventoryItem && ty.Tag is InventoryFolder)
2773             {
2774                 return 1;
2775             }
2776
2777             // Two items
2778             if (!(tx.Tag is InventoryItem) || !(ty.Tag is InventoryItem))
2779             {
2780                 return 0;
2781             }
2782
2783             InventoryItem item1 = (InventoryItem)tx.Tag;
2784             InventoryItem item2 = (InventoryItem)ty.Tag;
2785
2786             if (_bydate)
2787             {
2788                 if (item1.CreationDate < item2.CreationDate)
2789                 {
2790                     return 1;
2791                 }
2792                 else if (item1.CreationDate > item2.CreationDate)
2793                 {
2794                     return -1;
2795                 }
2796             }
2797             return string.Compare(item1.Name, item2.Name);
2798         }
2799     }
2800     #endregion
2801
2802     public class AttachmentInfo
2803     {
2804         public Primitive Prim;
2805         public InventoryItem Item;
2806         public UUID InventoryID;
2807         public UUID PrimID;
2808         public bool MarkedAttached = false;
2809
2810         public AttachmentPoint Point
2811         {
2812             get
2813             {
2814                 if (Prim != null)
2815                 {
2816                     return Prim.PrimData.AttachmentPoint;
2817                 }
2818                 else
2819                 {
2820                     return AttachmentPoint.Default;
2821                 }
2822             }
2823         }
2824     }
2825
2826
2827 }