OSDN Git Service

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