OSDN Git Service

Updated license year
[radegast/radegast.git] / Radegast / GUI / Consoles / Inventory / CurrentOutfitFolder.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2012, 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.Linq;
35 using System.Threading;
36 using OpenMetaverse;
37 using OpenMetaverse.StructuredData;
38
39 namespace Radegast
40 {
41     public class CurrentOutfitFolder : IDisposable
42     {
43         #region Fields
44         GridClient Client;
45         RadegastInstance Instance;
46         bool InitiCOF = false;
47         bool InvCAP = false;
48         bool AppearanceSent = false;
49         bool COFReady = false;
50         bool InitialUpdateDone = false;
51         public List<InventoryItem> ContentLinks = new List<InventoryItem>();
52         public Dictionary<UUID, InventoryItem> Content = new Dictionary<UUID, InventoryItem>();
53         public InventoryFolder COF;
54         #endregion Fields
55
56         #region Construction and disposal
57         public CurrentOutfitFolder(RadegastInstance instance)
58         {
59             this.Instance = instance;
60             this.Client = instance.Client;
61             Instance.ClientChanged += new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
62             RegisterClientEvents(Client);
63         }
64
65         public void Dispose()
66         {
67             UnregisterClientEvents(Client);
68             Instance.ClientChanged -= new EventHandler<ClientChangedEventArgs>(instance_ClientChanged);
69         }
70         #endregion Construction and disposal
71
72         #region Event handling
73         void instance_ClientChanged(object sender, ClientChangedEventArgs e)
74         {
75             UnregisterClientEvents(Client);
76             Client = e.Client;
77             RegisterClientEvents(Client);
78         }
79
80         void RegisterClientEvents(GridClient client)
81         {
82             client.Network.EventQueueRunning += new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
83             client.Inventory.FolderUpdated += new EventHandler<FolderUpdatedEventArgs>(Inventory_FolderUpdated);
84             client.Inventory.ItemReceived += new EventHandler<ItemReceivedEventArgs>(Inventory_ItemReceived);
85             client.Appearance.AppearanceSet += new EventHandler<AppearanceSetEventArgs>(Appearance_AppearanceSet);
86             client.Objects.KillObject += new EventHandler<KillObjectEventArgs>(Objects_KillObject);
87         }
88
89         void UnregisterClientEvents(GridClient client)
90         {
91             client.Network.EventQueueRunning -= new EventHandler<EventQueueRunningEventArgs>(Network_EventQueueRunning);
92             client.Inventory.FolderUpdated -= new EventHandler<FolderUpdatedEventArgs>(Inventory_FolderUpdated);
93             client.Inventory.ItemReceived -= new EventHandler<ItemReceivedEventArgs>(Inventory_ItemReceived);
94             client.Appearance.AppearanceSet -= new EventHandler<AppearanceSetEventArgs>(Appearance_AppearanceSet);
95             client.Objects.KillObject -= new EventHandler<KillObjectEventArgs>(Objects_KillObject);
96             lock (Content) Content.Clear();
97             lock (ContentLinks) ContentLinks.Clear();
98             InitiCOF = false;
99             InvCAP = false;
100             AppearanceSent = false;
101             COFReady = false;
102             InitialUpdateDone = false;
103         }
104
105         void Appearance_AppearanceSet(object sender, AppearanceSetEventArgs e)
106         {
107             AppearanceSent = true;
108             if (COFReady)
109             {
110                 InitialUpdate();
111             }
112         }
113
114         void Inventory_ItemReceived(object sender, ItemReceivedEventArgs e)
115         {
116             lock (ContentLinks)
117             {
118                 bool partOfCOF = false;
119                 foreach (var cofItem in ContentLinks)
120                 {
121                     if (cofItem.AssetUUID == e.Item.UUID)
122                     {
123                         partOfCOF = true;
124                         break;
125                     }
126                 }
127
128                 if (partOfCOF)
129                 {
130                     lock (Content)
131                     {
132                         Content[e.Item.UUID] = e.Item;
133                     }
134                 }
135             }
136
137             if (Content.Count == ContentLinks.Count)
138             {
139                 COFReady = true;
140                 if (AppearanceSent)
141                 {
142                     InitialUpdate();
143                 }
144             }
145         }
146
147         object FolderSync = new object();
148
149         void Inventory_FolderUpdated(object sender, FolderUpdatedEventArgs e)
150         {
151             if (COF == null) return;
152
153             if (e.FolderID == COF.UUID && e.Success)
154             {
155                 lock (FolderSync)
156                 {
157                     lock (Content) Content.Clear();
158                     lock (ContentLinks) ContentLinks.Clear();
159
160                     List<InventoryBase> content = Client.Inventory.Store.GetContents(COF);
161                     foreach (var baseItem in content)
162                     {
163                         if (baseItem is InventoryItem)
164                         {
165                             InventoryItem item = (InventoryItem)baseItem;
166                             if (item.AssetType == AssetType.Link)
167                             {
168                                 ContentLinks.Add(item);
169                             }
170                         }
171                     }
172
173                     List<UUID> items = new List<UUID>();
174                     List<UUID> owners = new List<UUID>();
175
176                     lock (ContentLinks)
177                     {
178                         foreach (var link in ContentLinks)
179                         {
180                             items.Add(link.AssetUUID);
181                             owners.Add(Client.Self.AgentID);
182                         }
183                     }
184
185                     if (items.Count > 0)
186                     {
187                         Client.Inventory.RequestFetchInventory(items, owners);
188                     }
189                 }
190             }
191         }
192
193         void Objects_KillObject(object sender, KillObjectEventArgs e)
194         {
195             if (Client.Network.CurrentSim != e.Simulator) return;
196
197             Primitive prim = null;
198             if (Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(e.ObjectLocalID, out prim))
199             {
200                 UUID invItem = GetAttachmentItem(prim);
201                 if (invItem != UUID.Zero)
202                 {
203                     RemoveLink(invItem);
204                 }
205             }
206         }
207
208         void Network_EventQueueRunning(object sender, EventQueueRunningEventArgs e)
209         {
210             if (e.Simulator == Client.Network.CurrentSim && !InitiCOF)
211             {
212                 InitiCOF = true;
213                 InitCOF();
214             }
215         }
216         #endregion Event handling
217
218         #region Private methods
219         void RequestDescendants(UUID folderID)
220         {
221             if (InvCAP)
222             {
223                 Client.Inventory.RequestFolderContentsCap(folderID, Client.Self.AgentID, true, true, InventorySortOrder.ByDate);
224             }
225             else
226             {
227                 Client.Inventory.RequestFolderContents(folderID, Client.Self.AgentID, true, true, InventorySortOrder.ByDate);
228             }
229         }
230
231         void InitCOF()
232         {
233             Uri url = null;
234
235             if (Client.Network.CurrentSim.Caps == null ||
236                 null == (url = Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventoryDescendents2")))
237             {
238                 InvCAP = false;
239             }
240             else
241             {
242                 InvCAP = true;
243             }
244
245             List<InventoryBase> rootContent = Client.Inventory.Store.GetContents(Client.Inventory.Store.RootFolder.UUID);
246             foreach (InventoryBase baseItem in rootContent)
247             {
248                 if (baseItem is InventoryFolder && ((InventoryFolder)baseItem).PreferredType == AssetType.CurrentOutfitFolder)
249                 {
250                     COF = (InventoryFolder)baseItem;
251                     break;
252                 }
253             }
254
255             if (COF == null)
256             {
257                 CreateCOF();
258             }
259             else
260             {
261                 RequestDescendants(COF.UUID);
262             }
263         }
264
265         void CreateCOF()
266         {
267             UUID cofID = Client.Inventory.CreateFolder(Client.Inventory.Store.RootFolder.UUID, "Current Look", AssetType.CurrentOutfitFolder);
268             if (Client.Inventory.Store.Items.ContainsKey(cofID) && Client.Inventory.Store.Items[cofID].Data is InventoryFolder)
269             {
270                 COF = (InventoryFolder)Client.Inventory.Store.Items[cofID].Data;
271                 COFReady = true;
272                 if (AppearanceSent)
273                 {
274                     InitialUpdate();
275                 }
276             }
277         }
278
279         void InitialUpdate()
280         {
281             if (InitialUpdateDone) return;
282             InitialUpdateDone = true;
283             lock (Content)
284             {
285                 List<Primitive> myAtt = Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive p) => p.ParentID == Client.Self.LocalID);
286
287                 foreach (InventoryItem item in Content.Values)
288                 {
289                     if (item is InventoryObject || item is InventoryAttachment)
290                     {
291                         if (!IsAttached(myAtt, item))
292                         {
293                             Client.Appearance.Attach(item, AttachmentPoint.Default, false);
294                         }
295                     }
296                 }
297             }
298         }
299         #endregion Private methods
300
301         #region Public methods
302         /// <summary>
303         /// Get inventory ID of a prim
304         /// </summary>
305         /// <param name="prim">Prim to check</param>
306         /// <returns>Inventory ID of the object. UUID.Zero if not found</returns>
307         public static UUID GetAttachmentItem(Primitive prim)
308         {
309             if (prim.NameValues == null) return UUID.Zero;
310
311             for (int i = 0; i < prim.NameValues.Length; i++)
312             {
313                 if (prim.NameValues[i].Name == "AttachItemID")
314                 {
315                     return (UUID)prim.NameValues[i].Value.ToString();
316                 }
317             }
318             return UUID.Zero;
319         }
320
321         /// <summary>
322         /// Is an inventory item currently attached
323         /// </summary>
324         /// <param name="attachments">List of root prims that are attached to our avatar</param>
325         /// <param name="item">Inventory item to check</param>
326         /// <returns>True if the inventory item is attached to avatar</returns>
327         public static bool IsAttached(List<Primitive> attachments, InventoryItem item)
328         {
329             foreach (Primitive prim in attachments)
330             {
331                 if (GetAttachmentItem(prim) == item.UUID)
332                 {
333                     return true;
334                 }
335             }
336
337             return false;
338         }
339
340         /// <summary>
341         /// Attach an inventory item
342         /// </summary>
343         /// <param name="item">Item to be attached</param>
344         /// <param name="point">Attachment point</param>
345         /// <param name="replace">Replace existing attachment at that point first?</param>
346         public void Attach(InventoryItem item, AttachmentPoint point, bool replace)
347         {
348             Client.Appearance.Attach(item, point, replace);
349             if (COF == null) return;
350
351             bool linkExists = false;
352             lock (ContentLinks)
353             {
354                 linkExists = null != ContentLinks.Find(itemLink => itemLink.AssetUUID == item.UUID);
355             }
356             if (!linkExists)
357             {
358                 Client.Inventory.CreateLink(COF.UUID, item, (success, newItem) =>
359                 {
360                     if (success)
361                     {
362                         lock (ContentLinks)
363                         {
364                             ContentLinks.Add(newItem);
365                         }
366                     }
367                 });
368             }
369         }
370
371         /// <summary>
372         /// Remove a link to specified inventory item
373         /// </summary>
374         /// <param name="itemID">ID of the target inventory item for which we want link to be removed</param>
375         public void RemoveLink(UUID itemID)
376         {
377             if (COF == null) return;
378
379             lock (ContentLinks)
380             {
381                 InventoryItem attachment = ContentLinks.Find(itemLink => itemLink.AssetUUID == itemID);
382                 if (attachment != null)
383                 {
384                     Client.Inventory.RemoveItem(attachment.UUID);
385                     ContentLinks.Remove(attachment);
386                 }
387             }
388         }
389
390         /// <summary>
391         /// Remove attachment
392         /// </summary>
393         /// <param name="item">>Inventory item to be detached</param>
394         public void Detach(InventoryItem item)
395         {
396             Client.Appearance.Detach(item);
397             RemoveLink(item.UUID);
398         }
399         #endregion Public methods
400     }
401 }