2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2010, Radegast Development Team
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
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.
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.
32 using System.Collections;
33 using System.Collections.Generic;
34 using System.ComponentModel;
36 using System.Windows.Forms;
37 using System.Threading;
39 using OpenMetaverse.StructuredData;
44 public partial class InventoryConsole : UserControl
46 RadegastInstance instance;
47 GridClient client { get { return instance.Client; } }
48 Dictionary<UUID, TreeNode> FolderNodes = new Dictionary<UUID, TreeNode>();
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;
69 #region Construction and disposal
70 public InventoryConsole(RadegastInstance instance)
72 InitializeComponent();
73 Disposed += new EventHandler(InventoryConsole_Disposed);
75 TreeUpdateTimer = new System.Timers.Timer()
77 Interval = updateInterval,
79 SynchronizingObject = invTree
81 TreeUpdateTimer.Elapsed += TreeUpdateTimerTick;
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);
93 sorter = new InvNodeSorter();
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);
100 sorter.ByDate = instance.GlobalSettings["inv_sort_bydate"].AsBoolean();
101 sorter.SystemFoldersFirst = instance.GlobalSettings["inv_sort_sysfirst"].AsBoolean();
103 tbtnSortByDate.Checked = sorter.ByDate;
104 tbtbSortByName.Checked = !sorter.ByDate;
105 tbtnSystemFoldersFirst.Checked = sorter.SystemFoldersFirst;
107 invTree.TreeViewNodeSorter = sorter;
109 if (instance.MonoRuntime)
111 invTree.BackColor = Color.FromKnownColor(KnownColor.Window);
112 invTree.ForeColor = invTree.LineColor = Color.FromKnownColor(KnownColor.WindowText);
113 InventoryFolder f = new InventoryFolder(UUID.Random());
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;
121 dirNode.ImageIndex = GetDirImageIndex(f.PreferredType.ToString().ToLower());
122 dirNode.SelectedImageIndex = dirNode.ImageIndex;
123 invTree.Nodes.Add(dirNode);
127 saveAllTToolStripMenuItem.Enabled = false;
128 InventoryUpdate = new Thread(new ThreadStart(StartTraverseNodes));
129 InventoryUpdate.Name = "InventoryUpdate";
130 InventoryUpdate.IsBackground = true;
131 InventoryUpdate.Start();
133 invRootNode.Expand();
135 invTree.AfterExpand += new TreeViewEventHandler(TreeView_AfterExpand);
136 invTree.NodeMouseClick += new TreeNodeMouseClickEventHandler(invTree_MouseClick);
137 invTree.NodeMouseDoubleClick += new TreeNodeMouseClickEventHandler(invTree_NodeMouseDoubleClick);
139 _EditTimer = new System.Threading.Timer(OnLabelEditTimer, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
142 Inventory.InventoryObjectAdded += new EventHandler<InventoryObjectAddedEventArgs>(Inventory_InventoryObjectAdded);
143 Inventory.InventoryObjectUpdated += new EventHandler<InventoryObjectUpdatedEventArgs>(Inventory_InventoryObjectUpdated);
144 Inventory.InventoryObjectRemoved += new EventHandler<InventoryObjectRemovedEventArgs>(Inventory_InventoryObjectRemoved);
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);
151 void InventoryConsole_Disposed(object sender, EventArgs e)
153 if (TreeUpdateTimer != null)
155 TreeUpdateTimer.Stop();
156 TreeUpdateTimer.Dispose();
157 TreeUpdateTimer = null;
159 if (InventoryUpdate != null)
161 if (InventoryUpdate.IsAlive)
162 InventoryUpdate.Abort();
163 InventoryUpdate = null;
166 Inventory.InventoryObjectAdded -= new EventHandler<InventoryObjectAddedEventArgs>(Inventory_InventoryObjectAdded);
167 Inventory.InventoryObjectUpdated -= new EventHandler<InventoryObjectUpdatedEventArgs>(Inventory_InventoryObjectUpdated);
168 Inventory.InventoryObjectRemoved -= new EventHandler<InventoryObjectRemovedEventArgs>(Inventory_InventoryObjectRemoved);
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);
176 #region Network callbacks
177 void Appearance_AppearanceSet(object sender, AppearanceSetEventArgs e)
180 if (appearnceWasBusy)
182 appearnceWasBusy = false;
183 client.Appearance.RequestSetAppearance(true);
187 void Objects_KillObject(object sender, KillObjectEventArgs e)
189 AttachmentInfo attachment = null;
192 foreach (AttachmentInfo att in attachments.Values)
194 if (att.Prim != null && att.Prim.LocalID == e.ObjectLocalID)
201 if (attachment != null)
203 attachments.Remove(attachment.InventoryID);
204 Inventory_InventoryObjectUpdated(this, new InventoryObjectUpdatedEventArgs(attachment.Item, attachment.Item));
209 void Objects_AttachmentUpdate(object sender, PrimEventArgs e)
211 Primitive prim = e.Prim;
213 if (client.Self.LocalID == 0 ||
214 prim.ParentID != client.Self.LocalID ||
215 prim.NameValues == null) return;
217 for (int i = 0; i < prim.NameValues.Length; i++)
219 if (prim.NameValues[i].Name == "AttachItemID")
221 AttachmentInfo attachment = new AttachmentInfo();
222 attachment.Prim = prim;
223 attachment.InventoryID = new UUID(prim.NameValues[i].Value.ToString());
224 attachment.PrimID = prim.ID;
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)
233 if (att.Value.Point == prim.PrimData.AttachmentPoint)
235 oldAttachment = att.Value;
236 oldAttachmentUUID = att.Key;
241 if (oldAttachment != null && oldAttachment.InventoryID != attachment.InventoryID)
243 attachments.Remove(oldAttachmentUUID);
244 if (oldAttachment.Item != null)
246 attachment.MarkedAttached = false;
247 Inventory_InventoryObjectUpdated(this, new InventoryObjectUpdatedEventArgs(oldAttachment.Item, oldAttachment.Item));
251 // Add new attachment info
252 if (!attachments.ContainsKey(attachment.InventoryID))
254 attachments.Add(attachment.InventoryID, attachment);
259 attachment = attachments[attachment.InventoryID];
260 if (attachment.Prim == null)
262 attachment.Prim = prim;
266 // Don't update the tree yet if we're still updating invetory tree from server
267 if (!TreeUpdateInProgress)
269 if (Inventory.Contains(attachment.InventoryID))
271 if (attachment.Item == null)
273 InventoryItem item = (InventoryItem)Inventory[attachment.InventoryID];
274 attachment.Item = item;
276 if (!attachment.MarkedAttached)
278 attachment.MarkedAttached = true;
279 Inventory_InventoryObjectUpdated(this, new InventoryObjectUpdatedEventArgs(attachments[attachment.InventoryID].Item, attachments[attachment.InventoryID].Item));
284 client.Inventory.RequestFetchInventory(attachment.InventoryID, client.Self.AgentID);
293 void Inventory_InventoryObjectAdded(object sender, InventoryObjectAddedEventArgs e)
295 if (TreeUpdateInProgress)
299 ItemsToAdd.Enqueue(e.Obj);
304 Exec_OnInventoryObjectAdded(e.Obj);
308 void Exec_OnInventoryObjectAdded(InventoryBase obj)
312 Invoke(new MethodInvoker(delegate()
314 Exec_OnInventoryObjectAdded(obj);
322 if (attachments.ContainsKey(obj.UUID))
324 attachments[obj.UUID].Item = (InventoryItem)obj;
328 TreeNode parent = findNodeForItem(obj.ParentUUID);
332 TreeNode newNode = AddBase(parent, obj);
333 if (obj.Name == newItemName)
335 if (newNode.Parent.IsExpanded)
341 newNode.Parent.Expand();
345 newItemName = string.Empty;
348 void Inventory_InventoryObjectRemoved(object sender, InventoryObjectRemovedEventArgs e)
352 BeginInvoke(new MethodInvoker(() => Inventory_InventoryObjectRemoved(sender, e)));
358 if (attachments.ContainsKey(e.Obj.UUID))
360 attachments.Remove(e.Obj.UUID);
364 TreeNode currentNode = findNodeForItem(e.Obj.UUID);
365 if (currentNode != null)
367 removeNode(currentNode);
371 void Inventory_InventoryObjectUpdated(object sender, InventoryObjectUpdatedEventArgs e)
373 if (TreeUpdateInProgress)
377 if (e.NewObject is InventoryFolder)
379 TreeNode currentNode = findNodeForItem(e.NewObject.UUID);
380 if (currentNode != null && currentNode.Text == e.NewObject.Name) return;
383 if (!ItemsToUpdate.Contains(e.NewObject))
385 ItemsToUpdate.Enqueue(e.NewObject);
391 Exec_OnInventoryObjectUpdated(e.OldObject, e.NewObject);
395 void Exec_OnInventoryObjectUpdated(InventoryBase oldObject, InventoryBase newObject)
397 if (newObject == null) return;
401 BeginInvoke(new MethodInvoker(() => Exec_OnInventoryObjectUpdated(oldObject, newObject)));
405 if (attachments.ContainsKey(newObject.UUID))
407 attachments[newObject.UUID].Item = (InventoryItem)newObject;
410 // Find our current node in the tree
411 TreeNode currentNode = findNodeForItem(newObject.UUID);
413 // Find which node should be our parrent
414 TreeNode parent = findNodeForItem(newObject.ParentUUID);
416 if (parent == null) return;
418 if (currentNode != null)
420 // Did we move to a different folder
421 if (currentNode.Parent != parent)
423 TreeNode movedNode = (TreeNode)currentNode.Clone();
424 movedNode.Tag = newObject;
425 parent.Nodes.Add(movedNode);
426 removeNode(currentNode);
427 cacheNode(movedNode);
431 currentNode.Tag = newObject;
432 currentNode.Text = ItemLabel(newObject, false);
433 currentNode.Name = newObject.Name;
436 else // We are not in the tree already, add
438 AddBase(parent, newObject);
443 #region Node manipulation
444 public static int GetDirImageIndex(string t)
446 int res = frmMain.ImageNames.IndexOf("inv_folder_" + t);
452 return frmMain.ImageNames.IndexOf("inv_folder_trash");
454 case "lostandfoundfolder":
455 return frmMain.ImageNames.IndexOf("inv_folder_lostandfound");
458 return frmMain.ImageNames.IndexOf("inv_folder_script");
460 return frmMain.ImageNames.IndexOf("inv_folder_plain_closed");
465 public static int GetItemImageIndex(string t)
467 int res = frmMain.ImageNames.IndexOf("inv_item_" + t);
472 return frmMain.ImageNames.IndexOf("inv_item_script");
474 else if (t == "callingcard")
476 return frmMain.ImageNames.IndexOf("inv_item_callingcard_offline");
482 TreeNode AddBase(TreeNode parent, InventoryBase obj)
484 if (obj is InventoryItem)
486 return AddItem(parent, (InventoryItem)obj);
490 return AddDir(parent, (InventoryFolder)obj);
494 TreeNode AddDir(TreeNode parentNode, InventoryFolder f)
496 TreeNode dirNode = new TreeNode();
497 dirNode.Name = f.UUID.ToString();
498 dirNode.Text = f.Name;
500 dirNode.ImageIndex = GetDirImageIndex(f.PreferredType.ToString().ToLower());
501 dirNode.SelectedImageIndex = dirNode.ImageIndex;
502 if (parentNode == null)
504 invTree.Nodes.Add(dirNode);
508 parentNode.Nodes.Add(dirNode);
510 lock (UUID2NodeCache)
512 UUID2NodeCache[f.UUID] = dirNode;
518 TreeNode AddItem(TreeNode parent, InventoryItem item)
520 TreeNode itemNode = new TreeNode();
521 itemNode.Name = item.UUID.ToString();
522 itemNode.Text = ItemLabel(item, false);
525 InventoryItem linkedItem = null;
527 if (item.IsLink() && Inventory.Contains(item.AssetUUID) && Inventory[item.AssetUUID] is InventoryItem)
529 linkedItem = (InventoryItem)Inventory[item.AssetUUID];
536 if (linkedItem is InventoryWearable)
538 InventoryWearable w = linkedItem as InventoryWearable;
539 img = GetItemImageIndex(w.WearableType.ToString().ToLower());
543 img = GetItemImageIndex(linkedItem.AssetType.ToString().ToLower());
546 itemNode.ImageIndex = img;
547 itemNode.SelectedImageIndex = img;
548 parent.Nodes.Add(itemNode);
549 lock (UUID2NodeCache)
551 UUID2NodeCache[item.UUID] = itemNode;
556 TreeNode findNodeForItem(UUID itemID)
558 lock (UUID2NodeCache)
560 if (UUID2NodeCache.ContainsKey(itemID))
562 return UUID2NodeCache[itemID];
568 void cacheNode(TreeNode node)
570 InventoryBase item = (InventoryBase)node.Tag;
573 foreach (TreeNode child in node.Nodes)
577 lock (UUID2NodeCache)
579 UUID2NodeCache[item.UUID] = node;
584 void removeNode(TreeNode node)
586 InventoryBase item = (InventoryBase)node.Tag;
589 foreach (TreeNode child in node.Nodes)
594 lock (UUID2NodeCache)
596 UUID2NodeCache.Remove(item.UUID);
604 #region Private methods
605 private void UpdateStatus(string text)
609 Invoke(new MethodInvoker(delegate() { UpdateStatus(text); }));
615 saveAllTToolStripMenuItem.Enabled = true;
618 tlabelStatus.Text = text;
621 private void AddFolderFromStore(TreeNode parent, InventoryFolder f)
623 List<InventoryBase> contents = Inventory.GetContents(f);
624 foreach (InventoryBase item in contents)
626 TreeNode node = AddBase(parent, item);
627 if (item is InventoryFolder)
629 AddFolderFromStore(node, (InventoryFolder)item);
634 private void TraverseNodes(InventoryNode start)
636 bool has_items = false;
638 foreach (InventoryNode node in start.Nodes.Values)
640 if (node.Data is InventoryItem)
647 if (!has_items || start.NeedsUpdate)
649 InventoryFolder f = (InventoryFolder)start.Data;
650 AutoResetEvent gotFolderEvent = new AutoResetEvent(false);
651 bool success = false;
653 EventHandler<FolderUpdatedEventArgs> callback = delegate(object sender, FolderUpdatedEventArgs ea)
655 if (f.UUID == ea.FolderID)
657 if (((InventoryFolder)Inventory.Items[ea.FolderID].Data).DescendentCount <= Inventory.Items[ea.FolderID].Nodes.Count)
660 gotFolderEvent.Set();
665 client.Inventory.FolderUpdated += callback;
666 fetchFolder(f.UUID, f.OwnerID, true);
667 gotFolderEvent.WaitOne(30 * 1000, false);
668 client.Inventory.FolderUpdated -= callback;
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);
676 foreach (InventoryBase item in Inventory.GetContents((InventoryFolder)start.Data))
678 if (item is InventoryFolder)
680 TraverseNodes(Inventory.GetNodeFor(item.UUID));
685 private void StartTraverseNodes()
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;
695 instance.TabConsole.DisplayNotificationInChat("Inventory update completed.");
697 // Updated labels on clothes that we are wearing
700 // Update attachments now that we are done
703 foreach (AttachmentInfo a in attachments.Values)
707 if (Inventory.Contains(a.InventoryID))
709 a.MarkedAttached = true;
710 a.Item = (InventoryItem)Inventory[a.InventoryID];
711 Inventory_InventoryObjectUpdated(this, new InventoryObjectUpdatedEventArgs(a.Item, a.Item));
715 client.Inventory.RequestFetchInventory(a.InventoryID, client.Self.AgentID);
722 Logger.Log("Finished updating invenory folders, saving cache...", Helpers.LogLevel.Debug, client);
723 ThreadPool.QueueUserWorkItem((object state) => Inventory.SaveToDisk(instance.InventoryCacheFileName));
725 if (!instance.MonoRuntime || IsHandleCreated)
726 Invoke(new MethodInvoker(() =>
735 public void ReloadInventory()
737 if (TreeUpdateInProgress)
739 TreeUpdateTimer.Stop();
740 InventoryUpdate.Abort();
741 InventoryUpdate = null;
744 saveAllTToolStripMenuItem.Enabled = false;
746 Inventory.Items = new Dictionary<UUID, InventoryNode>();
747 Inventory.RootFolder = Inventory.RootFolder;
749 invTree.Nodes.Clear();
750 UUID2NodeCache.Clear();
751 invRootNode = AddDir(null, Inventory.RootFolder);
753 InventoryUpdate = new Thread(new ThreadStart(StartTraverseNodes));
754 InventoryUpdate.Name = "InventoryUpdate";
755 InventoryUpdate.IsBackground = true;
756 InventoryUpdate.Start();
757 invRootNode.Expand();
760 private void reloadInventoryToolStripMenuItem_Click(object sender, EventArgs e)
765 private void TreeUpdateTimerTick(Object sender, EventArgs e)
769 if (ItemsToAdd.Count > 0)
771 invTree.BeginUpdate();
772 while (ItemsToAdd.Count > 0)
774 InventoryBase item = ItemsToAdd.Dequeue();
775 TreeNode node = findNodeForItem(item.ParentUUID);
787 if (ItemsToUpdate.Count > 0)
789 invTree.BeginUpdate();
790 while (ItemsToUpdate.Count > 0)
792 InventoryBase item = ItemsToUpdate.Dequeue();
793 Exec_OnInventoryObjectUpdated(item, item);
799 UpdateStatus("Loading... " + UUID2NodeCache.Count.ToString() + " items");
804 private void btnProfile_Click(object sender, EventArgs e)
806 instance.MainForm.ShowAgentProfile(txtCreator.Text, txtCreator.AgentID);
809 void UpdateItemInfo(InventoryItem item)
811 foreach (Control c in pnlDetail.Controls)
815 pnlDetail.Controls.Clear();
819 pnlItemProperties.Visible = false;
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();
830 if (item.AssetUUID != UUID.Zero)
832 txtAssetID.Text = item.AssetUUID.ToString();
836 txtAssetID.Text = String.Empty;
839 switch (item.AssetType)
841 case AssetType.Texture:
842 SLImageHandler image = new SLImageHandler(instance, item.AssetUUID, item.Name);
843 image.Dock = DockStyle.Fill;
844 pnlDetail.Controls.Add(image);
847 case AssetType.Notecard:
848 Notecard note = new Notecard(instance, (InventoryNotecard)item);
849 note.Dock = DockStyle.Fill;
852 pnlDetail.Controls.Add(note);
853 note.rtbContent.Focus();
856 case AssetType.Landmark:
857 Landmark landmark = new Landmark(instance, (InventoryLandmark)item);
858 landmark.Dock = DockStyle.Fill;
859 pnlDetail.Controls.Add(landmark);
862 case AssetType.LSLText:
863 ScriptEditor script = new ScriptEditor(instance, (InventoryLSL)item);
864 script.Dock = DockStyle.Fill;
866 script.TabStop = true;
867 pnlDetail.Controls.Add(script);
870 case AssetType.Gesture:
871 Guesture gesture = new Guesture(instance, (InventoryGesture)item);
872 gesture.Dock = DockStyle.Fill;
873 pnlDetail.Controls.Add(gesture);
878 tabsInventory.SelectedTab = tabDetail;
881 void invTree_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
883 if (invTree.SelectedNode.Tag is InventoryItem)
885 InventoryItem item = invTree.SelectedNode.Tag as InventoryItem;
886 switch (item.AssetType)
889 case AssetType.Landmark:
890 instance.TabConsole.DisplayNotificationInChat("Teleporting to " + item.Name);
891 client.Self.RequestTeleport(item.AssetUUID);
894 case AssetType.Gesture:
895 client.Self.PlayGesture(item.AssetUUID);
898 case AssetType.Notecard:
899 Notecard note = new Notecard(instance, (InventoryNotecard)item);
900 note.Dock = DockStyle.Fill;
904 case AssetType.LSLText:
905 ScriptEditor script = new ScriptEditor(instance, (InventoryLSL)item);
906 script.Dock = DockStyle.Fill;
907 script.ShowDetached();
913 private void fetchFolder(UUID folderID, UUID ownerID, bool force)
915 if (force || !fetchedFolders.Contains(folderID))
917 if (!fetchedFolders.Contains(folderID))
919 fetchedFolders.Add(folderID);
921 client.Inventory.RequestFolderContents(folderID, ownerID, true, true, InventorySortOrder.ByDate);
925 public bool IsWorn(InventoryItem item)
927 bool worn = client.Appearance.IsItemWorn(item) != WearableType.Invalid;
931 if (worn && !WornItems.Contains(item.UUID))
932 WornItems.Add(item.UUID);
937 public AttachmentPoint AttachedTo(InventoryItem item)
941 if (attachments.ContainsKey(item.UUID))
943 return attachments[item.UUID].Point;
947 return AttachmentPoint.Default;
950 public bool IsAttached(InventoryItem item)
954 return attachments.ContainsKey(item.UUID);
958 public InventoryItem AttachmentAt(AttachmentPoint point)
962 foreach (KeyValuePair<UUID, AttachmentInfo> att in attachments)
964 if (att.Value.Point == point)
966 return att.Value.Item;
974 /// Returns text of the label
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)
981 if (returnRaw || (invBase is InventoryFolder))
984 InventoryItem item = (InventoryItem)invBase;
986 string raw = item.Name;
991 if (Inventory.Contains(item.AssetUUID) && Inventory[item.AssetUUID] is InventoryItem)
993 item = (InventoryItem)Inventory[item.AssetUUID];
997 if ((item.Permissions.OwnerMask & PermissionMask.Modify) == 0)
998 raw += " (no modify)";
1000 if ((item.Permissions.OwnerMask & PermissionMask.Copy) == 0)
1001 raw += " (no copy)";
1003 if ((item.Permissions.OwnerMask & PermissionMask.Transfer) == 0)
1004 raw += " (no transfer)";
1009 if (IsAttached(item))
1011 raw += " (worn on " + AttachedTo(item).ToString() + ")";
1017 void invTree_MouseClick(object sender, TreeNodeMouseClickEventArgs e)
1019 TreeNode node = e.Node;
1021 if (e.Button == MouseButtons.Left)
1023 invTree.SelectedNode = node;
1024 if (node.Tag is InventoryItem)
1026 UpdateItemInfo(node.Tag as InventoryItem);
1030 UpdateItemInfo(null);
1033 else if (e.Button == MouseButtons.Right)
1035 invTree.SelectedNode = node;
1036 ctxInv.Show(invTree, e.X, e.Y);
1040 private void ctxInv_Opening(object sender, CancelEventArgs e)
1042 TreeNode node = invTree.SelectedNode;
1049 if (node.Tag is InventoryFolder)
1051 InventoryFolder folder = (InventoryFolder)node.Tag;
1052 ctxInv.Items.Clear();
1054 ToolStripMenuItem ctxItem;
1056 if ((int)folder.PreferredType >= (int)AssetType.EnsembleStart &&
1057 (int)folder.PreferredType <= (int)AssetType.EnsembleEnd)
1059 ctxItem = new ToolStripMenuItem("Fix type", null, OnInvContextClick);
1060 ctxItem.Name = "fix_type";
1061 ctxInv.Items.Add(ctxItem);
1062 ctxInv.Items.Add(new ToolStripSeparator());
1065 ctxItem = new ToolStripMenuItem("New Folder", null, OnInvContextClick);
1066 ctxItem.Name = "new_folder";
1067 ctxInv.Items.Add(ctxItem);
1069 ctxItem = new ToolStripMenuItem("New Note", null, OnInvContextClick);
1070 ctxItem.Name = "new_notecard";
1071 ctxInv.Items.Add(ctxItem);
1073 ctxItem = new ToolStripMenuItem("New Script", null, OnInvContextClick);
1074 ctxItem.Name = "new_script";
1075 ctxInv.Items.Add(ctxItem);
1077 ctxItem = new ToolStripMenuItem("Refresh", null, OnInvContextClick);
1078 ctxItem.Name = "refresh";
1079 ctxInv.Items.Add(ctxItem);
1081 ctxInv.Items.Add(new ToolStripSeparator());
1083 ctxItem = new ToolStripMenuItem("Expand", null, OnInvContextClick);
1084 ctxItem.Name = "expand";
1085 ctxInv.Items.Add(ctxItem);
1087 ctxItem = new ToolStripMenuItem("Expand All", null, OnInvContextClick);
1088 ctxItem.Name = "expand_all";
1089 ctxInv.Items.Add(ctxItem);
1091 ctxItem = new ToolStripMenuItem("Collapse", null, OnInvContextClick);
1092 ctxItem.Name = "collapse";
1093 ctxInv.Items.Add(ctxItem);
1095 if (folder.PreferredType == AssetType.TrashFolder)
1097 ctxItem = new ToolStripMenuItem("Empty Trash", null, OnInvContextClick);
1098 ctxItem.Name = "empty_trash";
1099 ctxInv.Items.Add(ctxItem);
1102 if (folder.PreferredType == AssetType.LostAndFoundFolder)
1104 ctxItem = new ToolStripMenuItem("Empty Lost and Found", null, OnInvContextClick);
1105 ctxItem.Name = "empty_lost_found";
1106 ctxInv.Items.Add(ctxItem);
1109 if (folder.PreferredType == AssetType.Unknown)
1111 ctxItem = new ToolStripMenuItem("Rename", null, OnInvContextClick);
1112 ctxItem.Name = "rename_folder";
1113 ctxInv.Items.Add(ctxItem);
1115 ctxInv.Items.Add(new ToolStripSeparator());
1117 ctxItem = new ToolStripMenuItem("Cut", null, OnInvContextClick);
1118 ctxItem.Name = "cut_folder";
1119 ctxInv.Items.Add(ctxItem);
1121 ctxItem = new ToolStripMenuItem("Copy", null, OnInvContextClick);
1122 ctxItem.Name = "copy_folder";
1123 ctxInv.Items.Add(ctxItem);
1126 if (instance.InventoryClipboard != null)
1128 ctxItem = new ToolStripMenuItem("Paste", null, OnInvContextClick);
1129 ctxItem.Name = "paste_folder";
1130 ctxInv.Items.Add(ctxItem);
1132 if (instance.InventoryClipboard.Item is InventoryItem)
1134 ctxItem = new ToolStripMenuItem("Paste as Link", null, OnInvContextClick);
1135 ctxItem.Name = "paste_folder_link";
1136 ctxInv.Items.Add(ctxItem);
1140 if (folder.PreferredType == AssetType.Unknown)
1142 ctxItem = new ToolStripMenuItem("Delete", null, OnInvContextClick);
1143 ctxItem.Name = "delete_folder";
1144 ctxInv.Items.Add(ctxItem);
1146 ctxInv.Items.Add(new ToolStripSeparator());
1148 ctxItem = new ToolStripMenuItem("Take off Items", null, OnInvContextClick);
1149 ctxItem.Name = "outfit_take_off";
1150 ctxInv.Items.Add(ctxItem);
1152 ctxItem = new ToolStripMenuItem("Add to Outfit", null, OnInvContextClick);
1153 ctxItem.Name = "outfit_add";
1154 ctxInv.Items.Add(ctxItem);
1156 ctxItem = new ToolStripMenuItem("Replace Outfit", null, OnInvContextClick);
1157 ctxItem.Name = "outfit_replace";
1158 ctxInv.Items.Add(ctxItem);
1161 instance.ContextActionManager.AddContributions(ctxInv, folder);
1163 else if (node.Tag is InventoryItem)
1165 InventoryItem item = (InventoryItem)node.Tag;
1166 ctxInv.Items.Clear();
1168 ToolStripMenuItem ctxItem;
1170 if (item.InventoryType == InventoryType.LSL)
1172 ctxItem = new ToolStripMenuItem("Edit script", null, OnInvContextClick);
1173 ctxItem.Name = "edit_script";
1174 ctxInv.Items.Add(ctxItem);
1177 if (item.AssetType == AssetType.Texture)
1179 ctxItem = new ToolStripMenuItem("View", null, OnInvContextClick);
1180 ctxItem.Name = "view_image";
1181 ctxInv.Items.Add(ctxItem);
1184 if (item.InventoryType == InventoryType.Landmark)
1186 ctxItem = new ToolStripMenuItem("Teleport", null, OnInvContextClick);
1187 ctxItem.Name = "lm_teleport";
1188 ctxInv.Items.Add(ctxItem);
1190 ctxItem = new ToolStripMenuItem("Info", null, OnInvContextClick);
1191 ctxItem.Name = "lm_info";
1192 ctxInv.Items.Add(ctxItem);
1195 if (item.InventoryType == InventoryType.Notecard)
1197 ctxItem = new ToolStripMenuItem("Open", null, OnInvContextClick);
1198 ctxItem.Name = "notecard_open";
1199 ctxInv.Items.Add(ctxItem);
1202 if (item.InventoryType == InventoryType.Gesture)
1204 ctxItem = new ToolStripMenuItem("Play", null, OnInvContextClick);
1205 ctxItem.Name = "gesture_play";
1206 ctxInv.Items.Add(ctxItem);
1208 ctxItem = new ToolStripMenuItem("Info", null, OnInvContextClick);
1209 ctxItem.Name = "gesture_info";
1210 ctxInv.Items.Add(ctxItem);
1213 if (item.InventoryType == InventoryType.Animation)
1215 if (!client.Self.SignaledAnimations.ContainsKey(item.AssetUUID))
1217 ctxItem = new ToolStripMenuItem("Play", null, OnInvContextClick);
1218 ctxItem.Name = "animation_play";
1219 ctxInv.Items.Add(ctxItem);
1223 ctxItem = new ToolStripMenuItem("Stop", null, OnInvContextClick);
1224 ctxItem.Name = "animation_stop";
1225 ctxInv.Items.Add(ctxItem);
1229 if (item.InventoryType == InventoryType.Object)
1231 ctxItem = new ToolStripMenuItem("Rez inworld", null, OnInvContextClick);
1232 ctxItem.Name = "rez_inworld";
1233 ctxInv.Items.Add(ctxItem);
1236 ctxItem = new ToolStripMenuItem("Rename", null, OnInvContextClick);
1237 ctxItem.Name = "rename_item";
1238 ctxInv.Items.Add(ctxItem);
1240 ctxInv.Items.Add(new ToolStripSeparator());
1242 ctxItem = new ToolStripMenuItem("Cut", null, OnInvContextClick);
1243 ctxItem.Name = "cut_item";
1244 ctxInv.Items.Add(ctxItem);
1246 ctxItem = new ToolStripMenuItem("Copy", null, OnInvContextClick);
1247 ctxItem.Name = "copy_item";
1248 ctxInv.Items.Add(ctxItem);
1250 if (instance.InventoryClipboard != null)
1252 ctxItem = new ToolStripMenuItem("Paste", null, OnInvContextClick);
1253 ctxItem.Name = "paste_item";
1254 ctxInv.Items.Add(ctxItem);
1256 if (instance.InventoryClipboard.Item is InventoryItem)
1258 ctxItem = new ToolStripMenuItem("Paste as Link", null, OnInvContextClick);
1259 ctxItem.Name = "paste_item_link";
1260 ctxInv.Items.Add(ctxItem);
1264 ctxItem = new ToolStripMenuItem("Delete", null, OnInvContextClick);
1265 ctxItem.Name = "delete_item";
1267 if (IsAttached(item) || IsWorn(item))
1269 ctxItem.Enabled = false;
1271 ctxInv.Items.Add(ctxItem);
1273 if (IsAttached(item) && instance.RLV.AllowDetach(attachments[item.UUID]))
1275 ctxItem = new ToolStripMenuItem("Detach from yourself", null, OnInvContextClick);
1276 ctxItem.Name = "detach";
1277 ctxInv.Items.Add(ctxItem);
1280 if (!IsAttached(item) && (item.InventoryType == InventoryType.Object || item.InventoryType == InventoryType.Attachment))
1282 ToolStripMenuItem ctxItemAttach = new ToolStripMenuItem("Attach to");
1283 ctxInv.Items.Add(ctxItemAttach);
1285 ToolStripMenuItem ctxItemAttachHUD = new ToolStripMenuItem("Attach to HUD");
1286 ctxInv.Items.Add(ctxItemAttachHUD);
1288 foreach (AttachmentPoint pt in Enum.GetValues(typeof(AttachmentPoint)))
1290 if (!pt.ToString().StartsWith("HUD"))
1292 string name = Utils.EnumToText(pt);
1294 InventoryItem alreadyAttached = null;
1295 if ((alreadyAttached = AttachmentAt(pt)) != null)
1297 name += " (" + alreadyAttached.Name + ")";
1300 ToolStripMenuItem ptItem = new ToolStripMenuItem(name, null, OnInvContextClick);
1301 ptItem.Name = pt.ToString();
1303 ptItem.Name = "attach_to";
1304 ctxItemAttach.DropDownItems.Add(ptItem);
1308 string name = Utils.EnumToText(pt).Substring(3);
1310 InventoryItem alreadyAttached = null;
1311 if ((alreadyAttached = AttachmentAt(pt)) != null)
1313 name += " (" + alreadyAttached.Name + ")";
1316 ToolStripMenuItem ptItem = new ToolStripMenuItem(name, null, OnInvContextClick);
1317 ptItem.Name = pt.ToString();
1319 ptItem.Name = "attach_to";
1320 ctxItemAttachHUD.DropDownItems.Add(ptItem);
1324 ctxItem = new ToolStripMenuItem("Wear", null, OnInvContextClick);
1325 ctxItem.Name = "wear_attachment";
1326 ctxInv.Items.Add(ctxItem);
1329 if (item is InventoryWearable)
1331 ctxInv.Items.Add(new ToolStripSeparator());
1335 ctxItem = new ToolStripMenuItem("Take off", null, OnInvContextClick);
1336 ctxItem.Name = "item_take_off";
1337 ctxInv.Items.Add(ctxItem);
1341 ctxItem = new ToolStripMenuItem("Wear", null, OnInvContextClick);
1342 ctxItem.Name = "item_wear";
1343 ctxInv.Items.Add(ctxItem);
1347 instance.ContextActionManager.AddContributions(ctxInv, item);
1353 #region Context menu folder
1354 private void OnInvContextClick(object sender, EventArgs e)
1356 if (invTree.SelectedNode == null || !(invTree.SelectedNode.Tag is InventoryBase))
1361 string cmd = ((ToolStripMenuItem)sender).Name;
1363 if (invTree.SelectedNode.Tag is InventoryFolder)
1365 #region Folder actions
1366 InventoryFolder f = (InventoryFolder)invTree.SelectedNode.Tag;
1371 foreach (TreeNode old in invTree.SelectedNode.Nodes)
1373 if (old.Tag is InventoryFolder)
1378 fetchFolder(f.UUID, f.OwnerID, true);
1382 invTree.SelectedNode.Expand();
1386 invTree.SelectedNode.ExpandAll();
1390 invTree.SelectedNode.Collapse();
1394 newItemName = "New folder";
1395 client.Inventory.CreateFolder(f.UUID, "New folder");
1399 client.Inventory.UpdateFolderProperties(f.UUID, f.ParentUUID, f.Name, AssetType.Unknown);
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);
1409 client.Inventory.RequestCreateItem(f.UUID, "New script", "Radegast script: " + DateTime.Now.ToString(),
1410 AssetType.LSLText, UUID.Zero, InventoryType.LSL, PermissionMask.All, ScriptCreated);
1414 instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Cut, f);
1418 instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Copy, f);
1421 case "paste_folder":
1422 PerformClipboardOperation(invTree.SelectedNode.Tag as InventoryFolder);
1425 case "paste_folder_link":
1426 PerformLinkOperation(invTree.SelectedNode.Tag as InventoryFolder);
1430 case "delete_folder":
1431 client.Inventory.MoveFolder(f.UUID, client.Inventory.FindFolderForType(AssetType.TrashFolder), f.Name);
1436 DialogResult res = MessageBox.Show("Are you sure you want to empty your trash?", "Confirmation", MessageBoxButtons.OKCancel);
1437 if (res == DialogResult.OK)
1439 client.Inventory.EmptyTrash();
1444 case "empty_lost_found":
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)
1449 client.Inventory.EmptyLostAndFound();
1454 case "rename_folder":
1455 invTree.SelectedNode.BeginEdit();
1458 case "outfit_replace":
1459 List<InventoryItem> newOutfit = new List<InventoryItem>();
1460 foreach (InventoryBase item in Inventory.GetContents(f))
1462 if (item is InventoryItem)
1463 newOutfit.Add((InventoryItem)item);
1465 appearnceWasBusy = client.Appearance.ManagerBusy;
1466 client.Appearance.ReplaceOutfit(newOutfit);
1467 client.Appearance.RequestSetAppearance(true);
1472 List<InventoryItem> addToOutfit = new List<InventoryItem>();
1473 foreach (InventoryBase item in Inventory.GetContents(f))
1475 if (item is InventoryItem)
1476 addToOutfit.Add((InventoryItem)item);
1478 appearnceWasBusy = client.Appearance.ManagerBusy;
1479 client.Appearance.AddToOutfit(addToOutfit);
1483 case "outfit_take_off":
1484 List<InventoryItem> removeFromOutfit = new List<InventoryItem>();
1485 foreach (InventoryBase item in Inventory.GetContents(f))
1487 if (item is InventoryItem)
1488 removeFromOutfit.Add((InventoryItem)item);
1490 appearnceWasBusy = client.Appearance.ManagerBusy;
1491 client.Appearance.RemoveFromOutfit(removeFromOutfit);
1497 else if (invTree.SelectedNode.Tag is InventoryItem)
1499 #region Item actions
1500 InventoryItem item = (InventoryItem)invTree.SelectedNode.Tag;
1505 instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Copy, item);
1509 instance.InventoryClipboard = new InventoryClipboard(ClipboardOperation.Cut, item);
1513 PerformClipboardOperation(invTree.SelectedNode.Parent.Tag as InventoryFolder);
1516 case "paste_item_link":
1517 PerformLinkOperation(invTree.SelectedNode.Parent.Tag as InventoryFolder);
1521 client.Inventory.MoveItem(item.UUID, client.Inventory.FindFolderForType(AssetType.TrashFolder), item.Name);
1525 invTree.SelectedNode.BeginEdit();
1529 client.Appearance.Detach(item.UUID);
1530 attachments.Remove(item.UUID);
1531 invTree.SelectedNode.Text = ItemLabel(item, false);
1534 case "wear_attachment":
1535 client.Appearance.Attach(item, AttachmentPoint.Default);
1539 AttachmentPoint pt = (AttachmentPoint)((ToolStripMenuItem)sender).Tag;
1540 client.Appearance.Attach(item, pt);
1544 ScriptEditor se = new ScriptEditor(instance, (InventoryLSL)item);
1549 UpdateItemInfo(item);
1552 case "item_take_off":
1553 appearnceWasBusy = client.Appearance.ManagerBusy;
1554 client.Appearance.RemoveFromOutfit(item);
1555 invTree.SelectedNode.Text = ItemLabel(item, false);
1558 if (WornItems.Contains(item.UUID))
1560 WornItems.Remove(item.UUID);
1566 appearnceWasBusy = client.Appearance.ManagerBusy;
1567 client.Appearance.AddToOutfit(item);
1568 invTree.SelectedNode.Text = ItemLabel(item, false);
1572 instance.TabConsole.DisplayNotificationInChat("Teleporting to " + item.Name);
1573 client.Self.RequestTeleport(item.AssetUUID);
1577 UpdateItemInfo(item);
1580 case "notecard_open":
1581 UpdateItemInfo(item);
1584 case "gesture_info":
1585 UpdateItemInfo(item);
1588 case "gesture_play":
1589 client.Self.PlayGesture(item.AssetUUID);
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);
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);
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);
1614 void NotecardCreated(bool success, InventoryItem item)
1618 BeginInvoke(new MethodInvoker(() => NotecardCreated(success, item)));
1624 instance.TabConsole.DisplayNotificationInChat("Creation of notecard failed");
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);
1634 invTree.SelectedNode = node;
1639 void ScriptCreated(bool success, InventoryItem item)
1643 BeginInvoke(new MethodInvoker(() => ScriptCreated(success, item)));
1649 instance.TabConsole.DisplayNotificationInChat("Creation of script failed");
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);
1659 invTree.SelectedNode = node;
1664 void PerformClipboardOperation(InventoryFolder dest)
1666 if (instance.InventoryClipboard == null) return;
1668 if (dest == null) return;
1670 if (instance.InventoryClipboard.Operation == ClipboardOperation.Cut)
1672 if (instance.InventoryClipboard.Item is InventoryItem)
1674 client.Inventory.MoveItem(instance.InventoryClipboard.Item.UUID, dest.UUID, instance.InventoryClipboard.Item.Name);
1676 else if (instance.InventoryClipboard.Item is InventoryFolder)
1678 if (instance.InventoryClipboard.Item.UUID != dest.UUID)
1680 client.Inventory.MoveFolder(instance.InventoryClipboard.Item.UUID, dest.UUID, instance.InventoryClipboard.Item.Name);
1684 instance.InventoryClipboard = null;
1686 else if (instance.InventoryClipboard.Operation == ClipboardOperation.Copy)
1688 if (instance.InventoryClipboard.Item is InventoryItem)
1690 client.Inventory.RequestCopyItem(instance.InventoryClipboard.Item.UUID, dest.UUID, instance.InventoryClipboard.Item.Name, instance.InventoryClipboard.Item.OwnerID, (InventoryBase target) =>
1695 else if (instance.InventoryClipboard.Item is InventoryFolder)
1697 ThreadPool.QueueUserWorkItem((object state) =>
1699 UUID newFolderID = client.Inventory.CreateFolder(dest.UUID, instance.InventoryClipboard.Item.Name, AssetType.Unknown);
1702 // FIXME: for some reason copying a bunch of items in one operation does not work
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;
1709 foreach (InventoryBase oldItem in Inventory.GetContents((InventoryFolder)instance.InventoryClipboard.Item))
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) => { });
1718 //if (folders.Count > 0)
1720 // client.Inventory.RequestCopyItems(items, folders, names, oldOwner, (InventoryBase target) => { });
1728 void PerformLinkOperation(InventoryFolder dest)
1730 if (instance.InventoryClipboard == null) return;
1732 if (dest == null) return;
1734 client.Inventory.CreateLink(dest.UUID, instance.InventoryClipboard.Item, (bool success, InventoryItem item) => { });
1739 private void UpdateWornLabels()
1743 BeginInvoke(new MethodInvoker(UpdateWornLabels));
1747 invTree.BeginUpdate();
1748 foreach (UUID itemID in WornItems)
1750 TreeNode node = findNodeForItem(itemID);
1753 node.Text = ItemLabel((InventoryBase)node.Tag, false);
1757 foreach (AppearanceManager.WearableData wearable in client.Appearance.GetWearables().Values)
1759 TreeNode node = findNodeForItem(wearable.ItemID);
1762 node.Text = ItemLabel((InventoryBase)node.Tag, false);
1765 invTree.EndUpdate();
1768 void TreeView_AfterExpand(object sender, TreeViewEventArgs e)
1770 // Check if we need to go into edit mode for new items
1771 if (newItemName != string.Empty)
1773 foreach (TreeNode n in e.Node.Nodes)
1775 if (n.Name == newItemName)
1781 newItemName = string.Empty;
1785 private bool _EditingNode = false;
1787 private void OnLabelEditTimer(object sender)
1789 if (_EditNode == null || !(_EditNode.Tag is InventoryBase))
1794 BeginInvoke(new MethodInvoker(delegate()
1796 OnLabelEditTimer(sender);
1802 _EditingNode = true;
1803 _EditNode.Text = ItemLabel((InventoryBase)_EditNode.Tag, true);
1804 _EditNode.BeginEdit();
1807 private void invTree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
1809 if (e.Node == null ||
1810 !(e.Node.Tag is InventoryBase) ||
1811 (e.Node.Tag is InventoryFolder && ((InventoryFolder)e.Node.Tag).PreferredType != AssetType.Unknown)
1814 e.CancelEdit = true;
1820 _EditingNode = false;
1824 e.CancelEdit = true;
1826 _EditTimer.Change(20, System.Threading.Timeout.Infinite);
1830 private void invTree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
1832 if (string.IsNullOrEmpty(e.Label))
1834 if (e.Node.Tag is InventoryBase)
1836 e.Node.Text = ItemLabel((InventoryBase)e.Node.Tag, false);
1838 e.CancelEdit = true;
1842 if (e.Node.Tag is InventoryFolder)
1844 InventoryFolder f = (InventoryFolder)e.Node.Tag;
1846 client.Inventory.MoveFolder(f.UUID, f.ParentUUID, f.Name);
1848 else if (e.Node.Tag is InventoryItem)
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);
1859 private void invTree_KeyUp(object sender, KeyEventArgs e)
1861 if (e.KeyCode == Keys.F2 && invTree.SelectedNode != null)
1863 invTree.SelectedNode.BeginEdit();
1865 else if (e.KeyCode == Keys.F5 && invTree.SelectedNode != null)
1867 if (invTree.SelectedNode.Tag is InventoryFolder)
1869 InventoryFolder f = (InventoryFolder)invTree.SelectedNode.Tag;
1870 fetchFolder(f.UUID, f.OwnerID, true);
1873 else if (e.KeyCode == Keys.Delete && invTree.SelectedNode != null)
1875 if (invTree.SelectedNode.Tag is InventoryItem)
1877 InventoryItem item = invTree.SelectedNode.Tag as InventoryItem;
1878 client.Inventory.MoveItem(item.UUID, client.Inventory.FindFolderForType(AssetType.TrashFolder), item.Name);
1880 else if (invTree.SelectedNode.Tag is InventoryFolder)
1882 InventoryFolder f = invTree.SelectedNode.Tag as InventoryFolder;
1883 client.Inventory.MoveFolder(f.UUID, client.Inventory.FindFolderForType(AssetType.TrashFolder), f.Name);
1886 else if (e.KeyCode == Keys.Apps && invTree.SelectedNode != null)
1892 #region Drag and Drop
1893 private void invTree_ItemDrag(object sender, ItemDragEventArgs e)
1895 invTree.SelectedNode = e.Item as TreeNode;
1896 if (invTree.SelectedNode.Tag is InventoryFolder && ((InventoryFolder)invTree.SelectedNode.Tag).PreferredType != AssetType.Unknown)
1900 invTree.DoDragDrop(e.Item, DragDropEffects.Move);
1903 private void invTree_DragDrop(object sender, DragEventArgs e)
1905 if (highlightedNode != null)
1907 highlightedNode.BackColor = invTree.BackColor;
1908 highlightedNode = null;
1911 TreeNode sourceNode = e.Data.GetData(typeof(TreeNode)) as TreeNode;
1912 if (sourceNode == null) return;
1914 Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
1915 TreeNode destinationNode = ((TreeView)sender).GetNodeAt(pt);
1917 if (destinationNode == null) return;
1919 if (sourceNode == destinationNode) return;
1921 // If droping to item within folder drop to its folder
1922 if (destinationNode.Tag is InventoryItem)
1924 destinationNode = destinationNode.Parent;
1927 InventoryFolder dest = destinationNode.Tag as InventoryFolder;
1929 if (dest == null) return;
1931 if (sourceNode.Tag is InventoryItem)
1933 InventoryItem item = (InventoryItem)sourceNode.Tag;
1934 client.Inventory.MoveItem(item.UUID, dest.UUID, item.Name);
1936 else if (sourceNode.Tag is InventoryFolder)
1938 InventoryFolder f = (InventoryFolder)sourceNode.Tag;
1939 client.Inventory.MoveFolder(f.UUID, dest.UUID, f.Name);
1943 private void invTree_DragEnter(object sender, DragEventArgs e)
1945 TreeNode node = e.Data.GetData(typeof(TreeNode)) as TreeNode;
1948 e.Effect = DragDropEffects.None;
1952 e.Effect = DragDropEffects.Move;
1956 TreeNode highlightedNode = null;
1958 private void invTree_DragOver(object sender, DragEventArgs e)
1960 TreeNode node = e.Data.GetData(typeof(TreeNode)) as TreeNode;
1963 e.Effect = DragDropEffects.None;
1966 Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
1967 TreeNode destinationNode = ((TreeView)sender).GetNodeAt(pt);
1969 if (highlightedNode != destinationNode)
1971 if (highlightedNode != null)
1973 highlightedNode.BackColor = invTree.BackColor;
1974 highlightedNode = null;
1977 if (destinationNode != null)
1979 highlightedNode = destinationNode;
1980 highlightedNode.BackColor = Color.LightSlateGray;
1984 if (destinationNode == null)
1986 e.Effect = DragDropEffects.None;
1990 e.Effect = DragDropEffects.Move;
1995 private void saveAllTToolStripMenuItem_Click(object sender, EventArgs e)
1997 (new InventoryBackup(instance)).Show();
2000 private void tbtnSystemFoldersFirst_Click(object sender, EventArgs e)
2002 sorter.SystemFoldersFirst = tbtnSystemFoldersFirst.Checked = !sorter.SystemFoldersFirst;
2003 instance.GlobalSettings["inv_sort_sysfirst"] = OSD.FromBoolean(sorter.SystemFoldersFirst);
2007 private void tbtbSortByName_Click(object sender, EventArgs e)
2009 if (tbtbSortByName.Checked) return;
2011 tbtbSortByName.Checked = true;
2012 tbtnSortByDate.Checked = sorter.ByDate = false;
2013 instance.GlobalSettings["inv_sort_bydate"] = OSD.FromBoolean(sorter.ByDate);
2018 private void tbtnSortByDate_Click(object sender, EventArgs e)
2020 if (tbtnSortByDate.Checked) return;
2022 tbtbSortByName.Checked = false;
2023 tbtnSortByDate.Checked = sorter.ByDate = true;
2024 instance.GlobalSettings["inv_sort_bydate"] = OSD.FromBoolean(sorter.ByDate);
2031 public class SearchResult
2033 public InventoryBase Inv;
2036 public SearchResult(InventoryBase inv, int level)
2043 List<SearchResult> searchRes;
2044 string searchString;
2045 Dictionary<int, ListViewItem> searchItemCache = new Dictionary<int, ListViewItem>();
2046 ListViewItem emptyItem = null;
2049 void PerformRecursiveSearch(int level, UUID folderID)
2051 var me = Inventory.Items[folderID].Data;
2052 searchRes.Add(new SearchResult(me, level));
2053 var sorted = Inventory.GetContents(folderID);
2055 sorted.Sort((InventoryBase b1, InventoryBase b2) =>
2057 if (b1 is InventoryFolder && !(b2 is InventoryFolder))
2061 else if (!(b1 is InventoryFolder) && b2 is InventoryFolder)
2067 return string.Compare(b1.Name, b2.Name);
2071 foreach (var item in sorted)
2073 if (item is InventoryFolder)
2075 PerformRecursiveSearch(level + 1, item.UUID);
2079 var it = item as InventoryItem;
2082 if (cbSrchName.Checked && it.Name.ToLower().Contains(searchString))
2086 else if (cbSrchDesc.Checked && it.Description.ToLower().Contains(searchString))
2091 if (cbSrchWorn.Checked && add &&
2093 (it.InventoryType == InventoryType.Wearable && IsWorn(it)) ||
2094 ((it.InventoryType == InventoryType.Attachment || it.InventoryType == InventoryType.Object) && IsAttached(it))
2101 if (cbSrchRecent.Checked && add && it.CreationDate < instance.StartupTimeUTC)
2109 searchRes.Add(new SearchResult(it, level + 1));
2114 if (searchRes[searchRes.Count - 1].Inv == me)
2116 searchRes.RemoveAt(searchRes.Count - 1);
2120 public void UpdateSearch()
2124 if (instance.MonoRuntime)
2126 lstInventorySearch.VirtualMode = false;
2127 lstInventorySearch.Items.Clear();
2128 lstInventorySearch.VirtualMode = true;
2131 lstInventorySearch.VirtualListSize = 0;
2132 searchString = txtSearch.Text.Trim().ToLower();
2134 //if (searchString == string.Empty && rbSrchAll.Checked)
2136 // lblSearchStatus.Text = "0 results";
2140 if (emptyItem == null)
2142 emptyItem = new ListViewItem(string.Empty);
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);
2152 private void lstInventorySearch_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
2154 if (searchItemCache.ContainsKey(e.ItemIndex))
2156 e.Item = searchItemCache[e.ItemIndex];
2158 else if (e.ItemIndex < searchRes.Count)
2160 InventoryBase inv = searchRes[e.ItemIndex].Inv;
2161 string desc = inv.Name;
2162 if (inv is InventoryItem)
2164 desc += string.Format(" - {0}", ((InventoryItem)inv).Description);
2166 ListViewItem item = new ListViewItem(desc);
2167 item.Tag = searchRes[e.ItemIndex];
2169 searchItemCache[e.ItemIndex] = item;
2177 private void btnInvSearch_Click(object sender, EventArgs e)
2182 private void cbSrchName_CheckedChanged(object sender, EventArgs e)
2184 if (!cbSrchName.Checked && !cbSrchDesc.Checked && !cbSrchCreator.Checked)
2186 cbSrchName.Checked = true;
2191 private void cbSrchWorn_CheckedChanged(object sender, EventArgs e)
2196 private void txtSearch_KeyDown(object sender, KeyEventArgs e)
2198 if (e.KeyCode == Keys.Enter)
2200 e.Handled = e.SuppressKeyPress = true;
2201 if (txtSearch.Text.Trim().Length > 0)
2208 private void lstInventorySearch_DrawItem(object sender, DrawListViewItemEventArgs e)
2210 Graphics g = e.Graphics;
2213 if (!(e.Item.Tag is SearchResult))
2216 if (e.Item.Selected)
2218 g.FillRectangle(SystemBrushes.Highlight, e.Bounds);
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);
2228 if (res.Inv is InventoryFolder)
2230 iconIx = GetDirImageIndex(((InventoryFolder)res.Inv).PreferredType.ToString().ToLower());
2232 else if (res.Inv is InventoryWearable)
2234 iconIx = GetItemImageIndex(((InventoryWearable)res.Inv).WearableType.ToString().ToLower());
2236 else if (res.Inv is InventoryItem)
2238 iconIx = GetItemImageIndex(((InventoryItem)res.Inv).AssetType.ToString().ToLower());
2248 icon = frmMain.ResourceImages.Images[iconIx];
2249 g.DrawImageUnscaled(icon, e.Bounds.X + offset - 18, e.Bounds.Y);
2253 using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap | StringFormatFlags.LineLimit))
2255 string label = ItemLabel(res.Inv, false);
2256 SizeF len = e.Graphics.MeasureString(label, lstInventorySearch.Font, rec.Width, sf);
2258 e.Graphics.DrawString(ItemLabel(res.Inv, false), lstInventorySearch.Font, SystemBrushes.WindowText, rec, sf);
2260 if (res.Inv is InventoryItem)
2262 string desc = ((InventoryItem)res.Inv).Description.Trim();
2263 if (desc != string.Empty)
2265 using (Font descFont = new Font(lstInventorySearch.Font, FontStyle.Italic))
2267 e.Graphics.DrawString(desc, descFont, SystemBrushes.GrayText, rec.X + len.Width + 5, rec.Y, sf);
2275 private void lstInventorySearch_SizeChanged(object sender, EventArgs e)
2277 chResItemName.Width = lstInventorySearch.Width - 30;
2280 private void txtSearch_TextChanged(object sender, EventArgs e)
2285 private void rbSrchAll_CheckedChanged(object sender, EventArgs e)
2290 private void lstInventorySearch_MouseClick(object sender, MouseEventArgs e)
2292 if (lstInventorySearch.SelectedIndices.Count != 1)
2297 SearchResult res = searchRes[lstInventorySearch.SelectedIndices[0]];
2298 TreeNode node = findNodeForItem(res.Inv.UUID);
2301 invTree.SelectedNode = node;
2302 if (e.Button == MouseButtons.Right)
2304 ctxInv.Show(lstInventorySearch, e.X, e.Y);
2310 private void lstInventorySearch_MouseDoubleClick(object sender, MouseEventArgs e)
2312 if (lstInventorySearch.SelectedIndices.Count != 1)
2317 SearchResult res = searchRes[lstInventorySearch.SelectedIndices[0]];
2318 TreeNode node = findNodeForItem(res.Inv.UUID);
2321 invTree.SelectedNode = node;
2322 invTree_NodeMouseDoubleClick(null, null);
2330 #region Sorter class
2331 // Create a node sorter that implements the IComparer interface.
2332 public class InvNodeSorter : System.Collections.IComparer
2334 bool _sysfirst = true;
2335 bool _bydate = true;
2337 int CompareFolders(InventoryFolder x, InventoryFolder y)
2341 if (x.PreferredType != AssetType.Unknown && y.PreferredType == AssetType.Unknown)
2345 else if (x.PreferredType == AssetType.Unknown && y.PreferredType != AssetType.Unknown)
2350 return String.Compare(x.Name, y.Name);
2353 public bool SystemFoldersFirst { set { _sysfirst = value; } get { return _sysfirst; } }
2354 public bool ByDate { set { _bydate = value; } get { return _bydate; } }
2356 public int Compare(object x, object y)
2358 TreeNode tx = x as TreeNode;
2359 TreeNode ty = y as TreeNode;
2361 if (tx.Tag is InventoryFolder && ty.Tag is InventoryFolder)
2363 return CompareFolders(tx.Tag as InventoryFolder, ty.Tag as InventoryFolder);
2365 else if (tx.Tag is InventoryFolder && ty.Tag is InventoryItem)
2369 else if (tx.Tag is InventoryItem && ty.Tag is InventoryFolder)
2375 if (!(tx.Tag is InventoryItem) || !(ty.Tag is InventoryItem))
2380 InventoryItem item1 = (InventoryItem)tx.Tag;
2381 InventoryItem item2 = (InventoryItem)ty.Tag;
2385 if (item1.CreationDate < item2.CreationDate)
2389 else if (item1.CreationDate > item2.CreationDate)
2394 return string.Compare(item1.Name, item2.Name);
2399 public class AttachmentInfo
2401 public Primitive Prim;
2402 public InventoryItem Item;
2403 public UUID InventoryID;
2405 public bool MarkedAttached = false;
2407 public AttachmentPoint Point
2413 return Prim.PrimData.AttachmentPoint;
2417 return AttachmentPoint.Default;