summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorErik Kundiman <erik@megapahit.org>2023-10-04 06:08:09 +0800
committerErik Kundiman <erik@megapahit.org>2023-10-04 06:08:09 +0800
commita332b8faefef8a044a2409957b415b62d6a85181 (patch)
treee76c2d8c9500c700d2d45ea1b4708747854f2f7d /indra/newview
parentbdfd8198eb8fe7128230d562fe37eafac5c52bec (diff)
parentf352fd1090ce4d50db349cdadfa61d66783a20e8 (diff)
Merge tag '6.6.15-release'
source for viewer 6.6.15.581961
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt22
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/settings.xml68
-rw-r--r--indra/newview/llagent.cpp98
-rw-r--r--indra/newview/llagent.h15
-rw-r--r--indra/newview/llagentwearables.cpp13
-rw-r--r--indra/newview/llagentwearables.h2
-rw-r--r--indra/newview/llaisapi.cpp880
-rw-r--r--indra/newview/llaisapi.h54
-rw-r--r--indra/newview/llappearancemgr.cpp348
-rw-r--r--indra/newview/llappearancemgr.h2
-rw-r--r--indra/newview/llappviewer.cpp4
-rw-r--r--indra/newview/llattachmentsmgr.cpp7
-rw-r--r--indra/newview/llavataractions.cpp181
-rw-r--r--indra/newview/llavataractions.h1
-rw-r--r--indra/newview/llconversationmodel.h3
-rw-r--r--indra/newview/llfloaterchangeitemthumbnail.cpp956
-rw-r--r--indra/newview/llfloaterchangeitemthumbnail.h139
-rw-r--r--indra/newview/llfloatereditenvironmentbase.cpp2
-rw-r--r--indra/newview/llfloaterforgetuser.cpp14
-rw-r--r--indra/newview/llfloatergesture.cpp2
-rw-r--r--indra/newview/llfloaterinventorysettings.cpp44
-rw-r--r--indra/newview/llfloaterinventorysettings.h45
-rw-r--r--indra/newview/llfloaterlinkreplace.cpp4
-rw-r--r--indra/newview/llfloatermarketplacelistings.cpp144
-rw-r--r--indra/newview/llfloatermarketplacelistings.h8
-rw-r--r--indra/newview/llfloaternewfeaturenotification.cpp76
-rw-r--r--indra/newview/llfloaternewfeaturenotification.h49
-rw-r--r--indra/newview/llfloateropenobject.cpp45
-rw-r--r--indra/newview/llfloateroutfitphotopreview.cpp288
-rw-r--r--indra/newview/llfloateroutfitphotopreview.h77
-rw-r--r--indra/newview/llfloaterproperties.cpp891
-rw-r--r--indra/newview/llfloaterproperties.h98
-rw-r--r--indra/newview/llfloatersidepanelcontainer.cpp23
-rw-r--r--indra/newview/llfloatersidepanelcontainer.h2
-rw-r--r--indra/newview/llfloatersimpleoutfitsnapshot.cpp333
-rw-r--r--indra/newview/llfloatersimplesnapshot.cpp499
-rw-r--r--indra/newview/llfloatersimplesnapshot.h (renamed from indra/newview/llfloatersimpleoutfitsnapshot.h)61
-rw-r--r--indra/newview/llfolderviewmodelinventory.h2
-rw-r--r--indra/newview/llfriendcard.cpp41
-rw-r--r--indra/newview/llinspectobject.cpp4
-rw-r--r--indra/newview/llinspecttexture.cpp249
-rw-r--r--indra/newview/llinspecttexture.h49
-rw-r--r--indra/newview/llinventorybridge.cpp832
-rw-r--r--indra/newview/llinventorybridge.h37
-rw-r--r--indra/newview/llinventoryfilter.cpp89
-rw-r--r--indra/newview/llinventoryfilter.h24
-rw-r--r--indra/newview/llinventoryfunctions.cpp865
-rw-r--r--indra/newview/llinventoryfunctions.h85
-rw-r--r--indra/newview/llinventorygallery.cpp3824
-rw-r--r--indra/newview/llinventorygallery.h419
-rw-r--r--indra/newview/llinventorygallerymenu.cpp714
-rw-r--r--indra/newview/llinventorygallerymenu.h62
-rw-r--r--indra/newview/llinventorymodel.cpp738
-rw-r--r--indra/newview/llinventorymodel.h38
-rw-r--r--indra/newview/llinventorymodelbackgroundfetch.cpp789
-rw-r--r--indra/newview/llinventorymodelbackgroundfetch.h75
-rw-r--r--indra/newview/llinventoryobserver.cpp164
-rw-r--r--indra/newview/llinventoryobserver.h10
-rw-r--r--indra/newview/llinventorypanel.cpp628
-rw-r--r--indra/newview/llinventorypanel.h88
-rw-r--r--indra/newview/lllogininstance.cpp28
-rw-r--r--indra/newview/lllogininstance.h3
-rw-r--r--indra/newview/llmarketplacefunctions.cpp10
-rw-r--r--indra/newview/lloutfitgallery.cpp880
-rw-r--r--indra/newview/lloutfitgallery.h68
-rw-r--r--indra/newview/lloutfitslist.cpp37
-rw-r--r--indra/newview/lloutfitslist.h5
-rw-r--r--indra/newview/llpanellandmarkinfo.cpp4
-rw-r--r--indra/newview/llpanellandmarkinfo.h2
-rw-r--r--indra/newview/llpanellogin.cpp5
-rw-r--r--indra/newview/llpanelmaininventory.cpp1266
-rw-r--r--indra/newview/llpanelmaininventory.h83
-rw-r--r--indra/newview/llpanelmarketplaceinbox.cpp3
-rw-r--r--indra/newview/llpanelmarketplaceinboxinventory.cpp34
-rw-r--r--indra/newview/llpanelmarketplaceinboxinventory.h11
-rw-r--r--indra/newview/llpanelobjectinventory.cpp17
-rw-r--r--indra/newview/llpaneloutfitedit.cpp16
-rw-r--r--indra/newview/llpaneloutfitsinventory.cpp8
-rw-r--r--indra/newview/llpanelprofile.cpp59
-rw-r--r--indra/newview/llpanelprofile.h5
-rw-r--r--indra/newview/llpanelwearing.cpp13
-rw-r--r--indra/newview/llpanelwearing.h1
-rw-r--r--indra/newview/llselectmgr.cpp3
-rw-r--r--indra/newview/llsettingsvo.cpp14
-rw-r--r--indra/newview/llsettingsvo.h1
-rw-r--r--indra/newview/llsidepanelinventory.cpp302
-rw-r--r--indra/newview/llsidepanelinventory.h24
-rw-r--r--indra/newview/llsidepaneliteminfo.cpp357
-rw-r--r--indra/newview/llsidepaneliteminfo.h37
-rw-r--r--indra/newview/llsidepaneltaskinfo.cpp171
-rw-r--r--indra/newview/llsidepaneltaskinfo.h34
-rw-r--r--indra/newview/llsnapshotlivepreview.cpp6
-rw-r--r--indra/newview/llsnapshotlivepreview.h1
-rw-r--r--indra/newview/llstartup.cpp79
-rw-r--r--indra/newview/llstartup.h1
-rw-r--r--indra/newview/lltexturectrl.cpp254
-rw-r--r--indra/newview/lltexturectrl.h28
-rw-r--r--indra/newview/llthumbnailctrl.cpp239
-rw-r--r--indra/newview/llthumbnailctrl.h88
-rw-r--r--indra/newview/lltoastalertpanel.cpp8
-rw-r--r--indra/newview/lltoolbarview.cpp2
-rw-r--r--indra/newview/lltooldraganddrop.cpp10
-rw-r--r--indra/newview/llviewerfloaterreg.cpp17
-rw-r--r--indra/newview/llviewerinventory.cpp464
-rw-r--r--indra/newview/llviewerinventory.h42
-rw-r--r--indra/newview/llviewermenu.cpp8
-rw-r--r--indra/newview/llviewermenufile.cpp9
-rw-r--r--indra/newview/llviewermessage.cpp102
-rw-r--r--indra/newview/llviewerobject.cpp6
-rwxr-xr-xindra/newview/llviewerregion.cpp1
-rw-r--r--indra/newview/llviewertexteditor.cpp16
-rw-r--r--indra/newview/llviewertexturelist.cpp69
-rw-r--r--indra/newview/llviewertexturelist.h12
-rw-r--r--indra/newview/llvoavatar.cpp9
-rw-r--r--indra/newview/llvovolume.cpp8
-rw-r--r--indra/newview/skins/default/colors.xml2
-rw-r--r--indra/newview/skins/default/textures/icons/copy_clipboard.pngbin0 -> 3443 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/delete_icon.pngbin0 -> 3570 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/file_upload.pngbin0 -> 3191 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/multi_folder_mode.pngbin0 -> 2786 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/paste_clipboard.pngbin0 -> 3412 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/single_folder_back.pngbin0 -> 3276 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/single_folder_forward.pngbin0 -> 3260 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/single_folder_mode.pngbin0 -> 2780 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/single_folder_up.pngbin0 -> 3118 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/snapshot_icon.pngbin0 -> 3341 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/texture_icon.pngbin0 -> 2812 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.pngbin0 -> 6162 bytes
-rw-r--r--indra/newview/skins/default/textures/textures.xml19
-rw-r--r--indra/newview/skins/default/xui/da/floater_inventory_item_properties.xml67
-rw-r--r--indra/newview/skins/default/xui/de/floater_inventory_item_properties.xml36
-rw-r--r--indra/newview/skins/default/xui/de/panel_main_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/en/floater_change_item_thumbnail.xml164
-rw-r--r--indra/newview/skins/default/xui/en/floater_inventory_settings.xml179
-rw-r--r--indra/newview/skins/default/xui/en/floater_item_properties.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_my_inventory.xml6
-rw-r--r--indra/newview/skins/default/xui/en/floater_new_feature_notification.xml60
-rw-r--r--indra/newview/skins/default/xui/en/floater_outfit_photo_preview.xml65
-rw-r--r--indra/newview/skins/default/xui/en/floater_simple_snapshot.xml (renamed from indra/newview/skins/default/xui/en/floater_simple_outfit_snapshot.xml)10
-rw-r--r--indra/newview/skins/default/xui/en/floater_snapshot.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_task_properties.xml24
-rw-r--r--indra/newview/skins/default/xui/en/floater_texture_ctrl.xml16
-rw-r--r--indra/newview/skins/default/xui/en/menu_gallery_inventory.xml515
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml32
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory.xml311
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory_add.xml11
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml104
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory_search_visibility.xml11
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory_view_default.xml114
-rw-r--r--indra/newview/skins/default/xui/en/menu_outfit_gear.xml27
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml118
-rw-r--r--indra/newview/skins/default/xui/en/panel_inventory_gallery.xml33
-rw-r--r--indra/newview/skins/default/xui/en/panel_inventory_gallery_item.xml75
-rw-r--r--indra/newview/skins/default/xui/en/panel_main_inventory.xml509
-rw-r--r--indra/newview/skins/default/xui/en/panel_outfit_gallery.xml42
-rw-r--r--indra/newview/skins/default/xui/en/panel_outfits_wearing.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_profile_firstlife.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_profile_secondlife.xml2
-rw-r--r--indra/newview/skins/default/xui/en/sidepanel_inventory.xml194
-rw-r--r--indra/newview/skins/default/xui/en/sidepanel_item_info.xml749
-rw-r--r--indra/newview/skins/default/xui/en/sidepanel_task_info.xml18
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml11
-rw-r--r--indra/newview/skins/default/xui/es/floater_inventory_item_properties.xml36
-rw-r--r--indra/newview/skins/default/xui/es/panel_main_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/fr/floater_inventory_item_properties.xml36
-rw-r--r--indra/newview/skins/default/xui/fr/panel_main_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/it/floater_inventory_item_properties.xml36
-rw-r--r--indra/newview/skins/default/xui/it/panel_main_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml68
-rw-r--r--indra/newview/skins/default/xui/ja/panel_main_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/pl/floater_inventory_item_properties.xml59
-rw-r--r--indra/newview/skins/default/xui/pl/panel_main_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/pt/floater_inventory_item_properties.xml36
-rw-r--r--indra/newview/skins/default/xui/pt/panel_main_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/ru/floater_inventory_item_properties.xml36
-rw-r--r--indra/newview/skins/default/xui/ru/panel_main_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/tr/floater_inventory_item_properties.xml36
-rw-r--r--indra/newview/skins/default/xui/tr/panel_main_inventory.xml1
-rw-r--r--indra/newview/skins/default/xui/zh/floater_inventory_item_properties.xml36
-rw-r--r--indra/newview/skins/default/xui/zh/panel_main_inventory.xml1
181 files changed, 17791 insertions, 6436 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 1e2fcaa3ad..40a858cfeb 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -207,6 +207,7 @@ set(viewer_SOURCE_FILES
llfloaterbuyland.cpp
llfloatercamera.cpp
llfloatercamerapresets.cpp
+ llfloaterchangeitemthumbnail.cpp
llfloaterchatvoicevolume.cpp
llfloaterclassified.cpp
llfloatercolorpicker.cpp
@@ -243,6 +244,7 @@ set(viewer_SOURCE_FILES
llfloaterimsession.cpp
llfloaterimcontainer.cpp
llfloaterinspect.cpp
+ llfloaterinventorysettings.cpp
llfloaterjoystick.cpp
llfloaterlagmeter.cpp
llfloaterland.cpp
@@ -258,12 +260,12 @@ set(viewer_SOURCE_FILES
llfloatermyscripts.cpp
llfloatermyenvironment.cpp
llfloaternamedesc.cpp
+ llfloaternewfeaturenotification.cpp
llfloaternotificationsconsole.cpp
llfloaternotificationstabbed.cpp
- llfloateroutfitphotopreview.cpp
llfloaterobjectweights.cpp
llfloateropenobject.cpp
- llfloatersimpleoutfitsnapshot.cpp
+ llfloatersimplesnapshot.cpp
llfloaterpathfindingcharacters.cpp
llfloaterpathfindingconsole.cpp
llfloaterpathfindinglinksets.cpp
@@ -278,7 +280,6 @@ set(viewer_SOURCE_FILES
llfloaterpreferenceviewadvanced.cpp
llfloaterpreviewtrash.cpp
llfloaterprofiletexture.cpp
- llfloaterproperties.cpp
llfloaterregiondebugconsole.cpp
llfloaterregioninfo.cpp
llfloaterreporter.cpp
@@ -350,10 +351,13 @@ set(viewer_SOURCE_FILES
llinspectgroup.cpp
llinspectobject.cpp
llinspectremoteobject.cpp
+ llinspecttexture.cpp
llinspecttoast.cpp
llinventorybridge.cpp
llinventoryfilter.cpp
llinventoryfunctions.cpp
+ llinventorygallery.cpp
+ llinventorygallerymenu.cpp
llinventoryicon.cpp
llinventoryitemslist.cpp
llinventorylistitem.cpp
@@ -579,6 +583,7 @@ set(viewer_SOURCE_FILES
lltextureinfodetails.cpp
lltexturestats.cpp
lltextureview.cpp
+ llthumbnailctrl.cpp
lltoast.cpp
lltoastalertpanel.cpp
lltoastgroupnotifypanel.cpp
@@ -847,6 +852,7 @@ set(viewer_HEADER_FILES
llfloaterbuycurrencyhtml.h
llfloaterbuyland.h
llfloatercamerapresets.h
+ llfloaterchangeitemthumbnail.h
llfloatercamera.h
llfloaterchatvoicevolume.h
llfloaterclassified.h
@@ -887,6 +893,7 @@ set(viewer_HEADER_FILES
llfloaterimsession.h
llfloaterimcontainer.h
llfloaterinspect.h
+ llfloaterinventorysettings.h
llfloaterjoystick.h
llfloaterlagmeter.h
llfloaterland.h
@@ -902,12 +909,12 @@ set(viewer_HEADER_FILES
llfloatermyscripts.h
llfloatermyenvironment.h
llfloaternamedesc.h
+ llfloaternewfeaturenotification.h
llfloaternotificationsconsole.h
llfloaternotificationstabbed.h
- llfloateroutfitphotopreview.h
llfloaterobjectweights.h
llfloateropenobject.h
- llfloatersimpleoutfitsnapshot.h
+ llfloatersimplesnapshot.h
llfloaterpathfindingcharacters.h
llfloaterpathfindingconsole.h
llfloaterpathfindinglinksets.h
@@ -922,7 +929,6 @@ set(viewer_HEADER_FILES
llfloaterpreferenceviewadvanced.h
llfloaterpreviewtrash.h
llfloaterprofiletexture.h
- llfloaterproperties.h
llfloaterregiondebugconsole.h
llfloaterregioninfo.h
llfloaterreporter.h
@@ -992,10 +998,13 @@ set(viewer_HEADER_FILES
llinspectgroup.h
llinspectobject.h
llinspectremoteobject.h
+ llinspecttexture.h
llinspecttoast.h
llinventorybridge.h
llinventoryfilter.h
llinventoryfunctions.h
+ llinventorygallery.h
+ llinventorygallerymenu.h
llinventoryicon.h
llinventoryitemslist.h
llinventorylistitem.h
@@ -1212,6 +1221,7 @@ set(viewer_HEADER_FILES
lltextureinfodetails.h
lltexturestats.h
lltextureview.h
+ llthumbnailctrl.h
lltoast.h
lltoastalertpanel.h
lltoastgroupnotifypanel.h
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index e9d5f8c17f..b69c12176f 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-6.6.14
+6.6.15
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 816b7b7c92..28d84aba21 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5002,17 +5002,6 @@
<key>Value</key>
<integer>0</integer>
</map>
- <key>InventoryInboxToggleState</key>
- <map>
- <key>Comment</key>
- <string>Stores the open/closed state of inventory Received items panel</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>Boolean</string>
- <key>Value</key>
- <integer>0</integer>
- </map>
<key>InventoryLinking</key>
<map>
<key>Comment</key>
@@ -5508,6 +5497,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>LastUIFeatureVersion</key>
+ <map>
+ <key>Comment</key>
+ <string>UI Feature Version number for tracking feature notification between viewer builds</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>LLSD</string>
+ <key>Value</key>
+ <string></string>
+ </map>
<key>LastFindPanel</key>
<map>
<key>Comment</key>
@@ -15371,6 +15371,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>BatchSizeAIS3</key>
+ <map>
+ <key>Comment</key>
+ <string>Amount of folder ais packs into category subset request</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>20</integer>
+ </map>
<key>PoolSizeAIS</key>
<map>
<key>Comment</key>
@@ -15378,7 +15389,7 @@
<key>Type</key>
<string>U32</string>
<key>Value</key>
- <integer>1</integer>
+ <integer>20</integer>
</map>
<key>PoolSizeUpload</key>
<map>
@@ -17107,6 +17118,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>FindOriginalOpenWindow</key>
+ <map>
+ <key>Comment</key>
+ <string>Sets the action for 'Find original' and 'Show in Inventory' (0 - shows item in main Inventory, 1 - opens a new single-folder window)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>StatsReportMaxDuration</key>
<map>
<key>Comment</key>
@@ -17140,5 +17162,27 @@
<key>Value</key>
<integer>0</integer>
</map>
+<key>MultiModeDoubleClickFolder</key>
+<map>
+ <key>Comment</key>
+ <string>Sets the action for Double-click on folder in multi-folder view (0 - expands and collapses folder, 1 - opens a new window, 2 – stays in current floater but switches to SFV)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>0</integer>
+</map>
+<key>SingleModeDoubleClickOpenWindow</key>
+<map>
+ <key>Comment</key>
+ <string>Sets the action for Double-click on folder in single-folder view (0 - stays in current window, 1 - opens a new window)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+</map>
</map>
</llsd>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index f87fa5b281..77fe601c1e 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -120,6 +120,11 @@ const F64 CHAT_AGE_FAST_RATE = 3.0;
const F32 MIN_FIDGET_TIME = 8.f; // seconds
const F32 MAX_FIDGET_TIME = 20.f; // seconds
+const S32 UI_FEATURE_VERSION = 1;
+// For version 1: 1 - inventory, 2 - gltf
+// Will need to change to 3 once either inventory or gltf releases and cause a conflict
+const S32 UI_FEATURE_FLAGS = 1;
+
// The agent instance.
LLAgent gAgent;
@@ -372,7 +377,7 @@ LLAgent::LLAgent() :
mHideGroupTitle(FALSE),
mGroupID(),
- mInitialized(FALSE),
+ mInitialized(false),
mListener(),
mDoubleTapRunTimer(),
@@ -448,7 +453,7 @@ LLAgent::LLAgent() :
mNextFidgetTime(0.f),
mCurrentFidget(0),
- mFirstLogin(FALSE),
+ mFirstLogin(false),
mOutfitChosen(FALSE),
mVoiceConnected(false),
@@ -505,7 +510,7 @@ void LLAgent::init()
mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_AGENT);
- mInitialized = TRUE;
+ mInitialized = true;
}
//-----------------------------------------------------------------------------
@@ -560,6 +565,93 @@ void LLAgent::onAppFocusGained()
}
}
+void LLAgent::setFirstLogin(bool b)
+{
+ mFirstLogin = b;
+
+ if (mFirstLogin)
+ {
+ // Don't notify new users about new features
+ if (getFeatureVersion() <= UI_FEATURE_VERSION)
+ {
+ setFeatureVersion(UI_FEATURE_VERSION, UI_FEATURE_FLAGS);
+ }
+ }
+}
+
+void LLAgent::setFeatureVersion(S32 version, S32 flags)
+{
+ LLSD updated_version;
+ updated_version["version"] = version;
+ updated_version["flags"] = flags;
+ gSavedSettings.setLLSD("LastUIFeatureVersion", updated_version);
+}
+
+S32 LLAgent::getFeatureVersion()
+{
+ S32 version;
+ S32 flags;
+ getFeatureVersionAndFlags(version, flags);
+ return version;
+}
+
+void LLAgent::getFeatureVersionAndFlags(S32& version, S32& flags)
+{
+ version = 0;
+ flags = 0;
+ LLSD feature_version = gSavedSettings.getLLSD("LastUIFeatureVersion");
+ if (feature_version.isInteger())
+ {
+ version = feature_version.asInteger();
+ flags = 1; // inventory flag
+ }
+ else if (feature_version.isMap())
+ {
+ version = feature_version["version"];
+ flags = feature_version["flags"];
+ }
+ else if (!feature_version.isString() && !feature_version.isUndefined())
+ {
+ // is something newer inside?
+ version = UI_FEATURE_VERSION;
+ flags = UI_FEATURE_FLAGS;
+ }
+}
+
+void LLAgent::showLatestFeatureNotification(const std::string key)
+{
+ S32 version;
+ S32 flags; // a single release can have multiple new features
+ getFeatureVersionAndFlags(version, flags);
+ if (version <= UI_FEATURE_VERSION && (flags & UI_FEATURE_FLAGS) != UI_FEATURE_FLAGS)
+ {
+ S32 flag = 0;
+
+ if (key == "inventory")
+ {
+ // Notify user about new thumbnail support
+ flag = 1;
+ }
+
+ if (key == "gltf")
+ {
+ flag = 2;
+ }
+
+ if ((flags & flag) == 0)
+ {
+ // Need to open on top even if called from onOpen,
+ // do on idle to make sure it's on top
+ LLSD floater_key(key);
+ doOnIdleOneTime([floater_key]()
+ {
+ LLFloaterReg::showInstance("new_feature_notification", floater_key);
+ });
+
+ setFeatureVersion(UI_FEATURE_VERSION, flags | flag);
+ }
+ }
+}
void LLAgent::ageChat()
{
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index ea91d2b720..fd3a9b1d7b 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -117,15 +117,20 @@ private:
//--------------------------------------------------------------------
public:
void onAppFocusGained();
- void setFirstLogin(BOOL b) { mFirstLogin = b; }
+ void setFirstLogin(bool b);
// Return TRUE if the database reported this login as the first for this particular user.
- BOOL isFirstLogin() const { return mFirstLogin; }
- BOOL isInitialized() const { return mInitialized; }
+ bool isFirstLogin() const { return mFirstLogin; }
+ bool isInitialized() const { return mInitialized; }
+
+ void setFeatureVersion(S32 version, S32 flags);
+ S32 getFeatureVersion();
+ void getFeatureVersionAndFlags(S32 &version, S32 &flags);
+ void showLatestFeatureNotification(const std::string key);
public:
std::string mMOTD; // Message of the day
private:
- BOOL mInitialized;
- BOOL mFirstLogin;
+ bool mInitialized;
+ bool mFirstLogin;
boost::shared_ptr<LLAgentListener> mListener;
//--------------------------------------------------------------------
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 53397978e0..db99f20775 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -1306,8 +1306,9 @@ void LLAgentWearables::findAttachmentsAddRemoveInfo(LLInventoryModel::item_array
}
// Build up list of objects to be removed and items currently attached.
- for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();
- iter != gAgentAvatarp->mAttachmentPoints.end();)
+ LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();
+ LLVOAvatar::attachment_map_t::iterator end = gAgentAvatarp->mAttachmentPoints.end();
+ while (iter != end)
{
LLVOAvatar::attachment_map_t::iterator curiter = iter++;
LLViewerJointAttachment* attachment = curiter->second;
@@ -1526,7 +1527,7 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos
}
// static
-void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id)
+void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id, std::function<void(const LLUUID&)> created_cb)
{
if (type == LLWearableType::WT_INVALID || type == LLWearableType::WT_NONE) return;
@@ -1538,7 +1539,7 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con
LLViewerWearable* wearable = LLWearableList::instance().createNewWearable(type, gAgentAvatarp);
LLAssetType::EType asset_type = wearable->getAssetType();
- LLPointer<LLInventoryCallback> cb;
+ LLPointer<LLBoostFuncInventoryCallback> cb;
if(wear)
{
cb = new LLBoostFuncInventoryCallback(wear_and_edit_cb);
@@ -1547,6 +1548,10 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con
{
cb = new LLBoostFuncInventoryCallback(wear_cb);
}
+ if (created_cb != NULL)
+ {
+ cb->addOnFireFunc(created_cb);
+ }
LLUUID folder_id;
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index 2710262910..e20f5df7fa 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -129,7 +129,7 @@ protected:
//--------------------------------------------------------------------
public:
- static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null);
+ static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null, std::function<void(const LLUUID&)> created_cb = NULL);
static void editWearable(const LLUUID& item_id);
bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);
diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp
index 005259bcb8..087cfb8d48 100644
--- a/indra/newview/llaisapi.cpp
+++ b/indra/newview/llaisapi.cpp
@@ -29,11 +29,15 @@
#include "llaisapi.h"
#include "llagent.h"
+#include "llappviewer.h"
#include "llcallbacklist.h"
#include "llinventorymodel.h"
+#include "llinventoryobserver.h"
+#include "llnotificationsutil.h"
#include "llsdutil.h"
#include "llviewerregion.h"
-#include "llinventoryobserver.h"
+#include "llvoavatar.h"
+#include "llvoavatarself.h"
#include "llviewercontrol.h"
///----------------------------------------------------------------------------
@@ -43,11 +47,16 @@
//=========================================================================
const std::string AISAPI::INVENTORY_CAP_NAME("InventoryAPIv3");
const std::string AISAPI::LIBRARY_CAP_NAME("LibraryAPIv3");
+const S32 AISAPI::HTTP_TIMEOUT = 180;
std::list<AISAPI::ais_query_item_t> AISAPI::sPostponedQuery;
const S32 MAX_SIMULTANEOUS_COROUTINES = 2048;
+// AIS3 allows '*' requests, but in reality those will be cut at some point
+// Specify own depth to be able to anticipate it and mark folders as incomplete
+const S32 MAX_FOLDER_DEPTH_REQUEST = 50;
+
//-------------------------------------------------------------------------
/*static*/
bool AISAPI::isAvailable()
@@ -93,6 +102,10 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c
if (cap.empty())
{
LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
@@ -100,7 +113,7 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c
tid.generate();
std::string url = cap + std::string("/category/") + parentId.asString() + "?tid=" + tid.asString();
- LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
+ LL_DEBUGS("Inventory") << "url: " << url << " parentID " << parentId << " newInventory " << newInventory << LL_ENDL;
// I may be suffering from golden hammer here, but the first part of this bind
// is actually a static cast for &HttpCoroutineAdapter::postAndSuspend so that
@@ -124,7 +137,7 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c
(&LLCoreHttpUtil::HttpCoroutineAdapter::postAndSuspend), _1, _2, _3, _4, _5, _6);
LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro,
- _1, postFn, url, parentId, newInventory, callback, COPYINVENTORY));
+ _1, postFn, url, parentId, newInventory, callback, CREATEINVENTORY));
EnqueueAISCommand("CreateInventory", proc);
}
@@ -135,6 +148,10 @@ void AISAPI::SlamFolder(const LLUUID& folderId, const LLSD& newInventory, comple
if (cap.empty())
{
LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
@@ -170,6 +187,10 @@ void AISAPI::RemoveCategory(const LLUUID &categoryId, completion_t callback)
if (cap.empty())
{
LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
@@ -203,6 +224,10 @@ void AISAPI::RemoveItem(const LLUUID &itemId, completion_t callback)
if (cap.empty())
{
LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
@@ -235,6 +260,10 @@ void AISAPI::CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, b
if (cap.empty())
{
LL_WARNS("Inventory") << "Library cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
@@ -279,6 +308,10 @@ void AISAPI::PurgeDescendents(const LLUUID &categoryId, completion_t callback)
if (cap.empty())
{
LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
@@ -313,6 +346,10 @@ void AISAPI::UpdateCategory(const LLUUID &categoryId, const LLSD &updates, compl
if (cap.empty())
{
LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
std::string url = cap + std::string("/category/") + categoryId.asString();
@@ -345,6 +382,10 @@ void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t
if (cap.empty())
{
LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
std::string url = cap + std::string("/item/") + itemId.asString();
@@ -368,6 +409,380 @@ void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t
}
/*static*/
+void AISAPI::FetchItem(const LLUUID &itemId, ITEM_TYPE type, completion_t callback)
+{
+ std::string cap;
+
+ cap = (type == INVENTORY) ? getInvCap() : getLibCap();
+ if (cap.empty())
+ {
+ LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+ std::string url = cap + std::string("/item/") + itemId.asString();
+
+ invokationFn_t getFn = boost::bind(
+ // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
+ static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>
+ //----
+ // _1 -> httpAdapter
+ // _2 -> httpRequest
+ // _3 -> url
+ // _4 -> body
+ // _5 -> httpOptions
+ // _6 -> httpHeaders
+ (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6);
+
+ LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro,
+ _1, getFn, url, itemId, LLSD(), callback, FETCHITEM));
+
+ EnqueueAISCommand("FetchItem", proc);
+}
+
+/*static*/
+void AISAPI::FetchCategoryChildren(const LLUUID &catId, ITEM_TYPE type, bool recursive, completion_t callback, S32 depth)
+{
+ std::string cap;
+
+ cap = (type == INVENTORY) ? getInvCap() : getLibCap();
+ if (cap.empty())
+ {
+ LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+ std::string url = cap + std::string("/category/") + catId.asString() + "/children";
+
+ if (recursive)
+ {
+ // can specify depth=*, but server side is going to cap requests
+ // and reject everything 'over the top',.
+ depth = MAX_FOLDER_DEPTH_REQUEST;
+ }
+ else
+ {
+ depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST);
+ }
+
+ url += "?depth=" + std::to_string(depth);
+
+ invokationFn_t getFn = boost::bind(
+ // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
+ static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>
+ //----
+ // _1 -> httpAdapter
+ // _2 -> httpRequest
+ // _3 -> url
+ // _4 -> body
+ // _5 -> httpOptions
+ // _6 -> httpHeaders
+ (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6);
+
+ // get doesn't use body, can pass additional data
+ LLSD body;
+ body["depth"] = depth;
+ LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro,
+ _1, getFn, url, catId, body, callback, FETCHCATEGORYCHILDREN));
+
+ EnqueueAISCommand("FetchCategoryChildren", proc);
+}
+
+// some folders can be requested by name, like
+// animatn | bodypart | clothing | current | favorite | gesture | inbox | landmark | lsltext
+// lstndfnd | my_otfts | notecard | object | outbox | root | snapshot | sound | texture | trash
+void AISAPI::FetchCategoryChildren(const std::string &identifier, bool recursive, completion_t callback, S32 depth)
+{
+ std::string cap;
+
+ cap = getInvCap();
+ if (cap.empty())
+ {
+ LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+ std::string url = cap + std::string("/category/") + identifier + "/children";
+
+ if (recursive)
+ {
+ // can specify depth=*, but server side is going to cap requests
+ // and reject everything 'over the top',.
+ depth = MAX_FOLDER_DEPTH_REQUEST;
+ }
+ else
+ {
+ depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST);
+ }
+
+ url += "?depth=" + std::to_string(depth);
+
+ invokationFn_t getFn = boost::bind(
+ // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
+ static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>
+ //----
+ // _1 -> httpAdapter
+ // _2 -> httpRequest
+ // _3 -> url
+ // _4 -> body
+ // _5 -> httpOptions
+ // _6 -> httpHeaders
+ (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6);
+
+ // get doesn't use body, can pass additional data
+ LLSD body;
+ body["depth"] = depth;
+ LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro,
+ _1, getFn, url, LLUUID::null, body, callback, FETCHCATEGORYCHILDREN));
+
+ EnqueueAISCommand("FetchCategoryChildren", proc);
+}
+
+/*static*/
+void AISAPI::FetchCategoryCategories(const LLUUID &catId, ITEM_TYPE type, bool recursive, completion_t callback, S32 depth)
+{
+ std::string cap;
+
+ cap = (type == INVENTORY) ? getInvCap() : getLibCap();
+ if (cap.empty())
+ {
+ LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+ std::string url = cap + std::string("/category/") + catId.asString() + "/categories";
+
+ if (recursive)
+ {
+ // can specify depth=*, but server side is going to cap requests
+ // and reject everything 'over the top',.
+ depth = MAX_FOLDER_DEPTH_REQUEST;
+ }
+ else
+ {
+ depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST);
+ }
+
+ url += "?depth=" + std::to_string(depth);
+
+ invokationFn_t getFn = boost::bind(
+ // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
+ static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>
+ //----
+ // _1 -> httpAdapter
+ // _2 -> httpRequest
+ // _3 -> url
+ // _4 -> body
+ // _5 -> httpOptions
+ // _6 -> httpHeaders
+ (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6);
+
+ // get doesn't use body, can pass additional data
+ LLSD body;
+ body["depth"] = depth;
+ LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro,
+ _1, getFn, url, catId, body, callback, FETCHCATEGORYCATEGORIES));
+
+ EnqueueAISCommand("FetchCategoryCategories", proc);
+}
+
+void AISAPI::FetchCategorySubset(const LLUUID& catId,
+ const uuid_vec_t specificChildren,
+ ITEM_TYPE type,
+ bool recursive,
+ completion_t callback,
+ S32 depth)
+{
+ std::string cap = (type == INVENTORY) ? getInvCap() : getLibCap();
+ if (cap.empty())
+ {
+ LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+ if (specificChildren.empty())
+ {
+ LL_WARNS("Inventory") << "Empty request!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+ // category/any_folder_id/children?depth=*&children=child_id1,child_id2,child_id3
+ std::string url = cap + std::string("/category/") + catId.asString() + "/children";
+
+ if (recursive)
+ {
+ depth = MAX_FOLDER_DEPTH_REQUEST;
+ }
+ else
+ {
+ depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST);
+ }
+
+ uuid_vec_t::const_iterator iter = specificChildren.begin();
+ uuid_vec_t::const_iterator end = specificChildren.end();
+
+ url += "?depth=" + std::to_string(depth) + "&children=" + iter->asString();
+ iter++;
+
+ while (iter != end)
+ {
+ url += "," + iter->asString();
+ iter++;
+ }
+
+ const S32 MAX_URL_LENGH = 2000; // RFC documentation specifies a maximum length of 2048
+ if (url.length() > MAX_URL_LENGH)
+ {
+ LL_WARNS("Inventory") << "Request url is too long, url: " << url << LL_ENDL;
+ }
+
+ invokationFn_t getFn = boost::bind(
+ // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
+ static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>
+ //----
+ // _1 -> httpAdapter
+ // _2 -> httpRequest
+ // _3 -> url
+ // _4 -> body
+ // _5 -> httpOptions
+ // _6 -> httpHeaders
+ (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6);
+
+ // get doesn't use body, can pass additional data
+ LLSD body;
+ body["depth"] = depth;
+ LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro,
+ _1, getFn, url, catId, body, callback, FETCHCATEGORYSUBSET));
+
+ EnqueueAISCommand("FetchCategorySubset", proc);
+}
+
+/*static*/
+// Will get COF folder, links in it and items those links point to
+void AISAPI::FetchCOF(completion_t callback)
+{
+ std::string cap = getInvCap();
+ if (cap.empty())
+ {
+ LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+ std::string url = cap + std::string("/category/current/links");
+
+ invokationFn_t getFn = boost::bind(
+ // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
+ static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>
+ //----
+ // _1 -> httpAdapter
+ // _2 -> httpRequest
+ // _3 -> url
+ // _4 -> body
+ // _5 -> httpOptions
+ // _6 -> httpHeaders
+ (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6);
+
+ LLSD body;
+ // Only cof folder will be full, but cof can contain an outfit
+ // link with embedded outfit folder for request to parse
+ body["depth"] = 0;
+ LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro,
+ _1, getFn, url, LLUUID::null, body, callback, FETCHCOF));
+
+ EnqueueAISCommand("FetchCOF", proc);
+}
+
+void AISAPI::FetchCategoryLinks(const LLUUID &catId, completion_t callback)
+{
+ std::string cap = getInvCap();
+ if (cap.empty())
+ {
+ LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+ std::string url = cap + std::string("/category/") + catId.asString() + "/links";
+
+ invokationFn_t getFn = boost::bind(
+ // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
+ static_cast<LLSD (LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &,
+ LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>
+ //----
+ // _1 -> httpAdapter
+ // _2 -> httpRequest
+ // _3 -> url
+ // _4 -> body
+ // _5 -> httpOptions
+ // _6 -> httpHeaders
+ (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
+ _1, _2, _3, _5, _6);
+
+ LLSD body;
+ body["depth"] = 0;
+ LLCoprocedureManager::CoProcedure_t proc(
+ boost::bind(&AISAPI::InvokeAISCommandCoro, _1, getFn, url, LLUUID::null, body, callback, FETCHCATEGORYLINKS));
+
+ EnqueueAISCommand("FetchCategoryLinks", proc);
+}
+
+/*static*/
+void AISAPI::FetchOrphans(completion_t callback)
+{
+ std::string cap = getInvCap();
+ if (cap.empty())
+ {
+ LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+ std::string url = cap + std::string("/orphans");
+
+ invokationFn_t getFn = boost::bind(
+ // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
+ static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t , const std::string& , LLCore::HttpOptions::ptr_t , LLCore::HttpHeaders::ptr_t)>
+ //----
+ // _1 -> httpAdapter
+ // _2 -> httpRequest
+ // _3 -> url
+ // _4 -> body
+ // _5 -> httpOptions
+ // _6 -> httpHeaders
+ (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend) , _1 , _2 , _3 , _5 , _6);
+
+ LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro ,
+ _1 , getFn , url , LLUUID::null , LLSD() , callback , FETCHORPHANS));
+
+ EnqueueAISCommand("FetchOrphans" , proc);
+}
+
+/*static*/
void AISAPI::EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc)
{
LLCoprocedureManager &inst = LLCoprocedureManager::instance();
@@ -418,21 +833,66 @@ void AISAPI::onIdle(void *userdata)
}
/*static*/
+void AISAPI::onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD& request_body)
+{
+ LLTimer timer;
+ if ( (type == UPDATECATEGORY || type == UPDATEITEM)
+ && gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
+ {
+ dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update);
+ }
+
+ AISUpdate ais_update(update, type, request_body);
+ ais_update.doUpdate(); // execute the updates in the appropriate order.
+ LL_DEBUGS("Inventory", "AIS3") << "Elapsed processing: " << timer.getElapsedTimeF32() << LL_ENDL;
+}
+
+/*static*/
void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter,
invokationFn_t invoke, std::string url,
LLUUID targetId, LLSD body, completion_t callback, COMMAND_TYPE type)
{
+ if (gDisconnected)
+ {
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
+ }
+
LLCore::HttpOptions::ptr_t httpOptions(new LLCore::HttpOptions);
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest());
LLCore::HttpHeaders::ptr_t httpHeaders;
- httpOptions->setTimeout(LLCoreHttpUtil::HTTP_REQUEST_EXPIRY_SECS);
+ httpOptions->setTimeout(HTTP_TIMEOUT);
- LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
+ LL_DEBUGS("Inventory") << "Request url: " << url << LL_ENDL;
- LLSD result = invoke(httpAdapter, httpRequest, url, body, httpOptions, httpHeaders);
- LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+ LLSD result;
+ LLSD httpResults;
+ LLCore::HttpStatus status;
+
+ if (debugLoggingEnabled("Inventory"))
+ {
+ LLTimer ais_timer;
+ ais_timer.start();
+ result = invoke(httpAdapter , httpRequest , url , body , httpOptions , httpHeaders);
+ httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+ F32MillisecondsImplicit elapsed_time = ais_timer.getElapsedTimeF32();
+
+ LL_DEBUGS("Inventory") << "Request type: " << (S32)type
+ << " \nRequest target: " << targetId
+ << " \nElapsed time since request: " << elapsed_time
+ << " \nstatus: " << status.toULong() << LL_ENDL;
+ }
+ else
+ {
+ result = invoke(httpAdapter , httpRequest , url , body , httpOptions , httpHeaders);
+ httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+ }
if (!status || !result.isMap())
{
@@ -474,29 +934,127 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht
}
}
}
+ else if (status == LLCore::HttpStatus(HTTP_FORBIDDEN) /*403*/)
+ {
+ if (type == FETCHCATEGORYCHILDREN)
+ {
+ if (body.has("depth") && body["depth"].asInteger() == 0)
+ {
+ // Can't fetch a single folder with depth 0, folder is too big.
+ static bool first_call = true;
+ if (first_call)
+ {
+ first_call = false;
+ LLNotificationsUtil::add("InventoryLimitReachedAISAlert");
+ }
+ else
+ {
+ LLNotificationsUtil::add("InventoryLimitReachedAIS");
+ }
+ LL_WARNS("Inventory") << "Fetch failed, content is over limit, url: " << url << LL_ENDL;
+ }
+ else
+ {
+ // Result was too big, but situation is recoverable by requesting with lower depth
+ LL_DEBUGS("Inventory") << "Fetch failed, content is over limit, url: " << url << LL_ENDL;
+ }
+ }
+ }
LL_WARNS("Inventory") << "Inventory error: " << status.toString() << LL_ENDL;
LL_WARNS("Inventory") << ll_pretty_print_sd(result) << LL_ENDL;
}
- gInventory.onAISUpdateReceived("AISCommand", result);
+ LL_DEBUGS("Inventory", "AIS3") << "Result: " << result << LL_ENDL;
+ onUpdateReceived(result, type, body);
if (callback && !callback.empty())
- {
+ {
+ bool needs_callback = true;
LLUUID id(LLUUID::null);
- if (result.has("category_id") && (type == COPYLIBRARYCATEGORY))
- {
- id = result["category_id"];
- }
+ switch (type)
+ {
+ case COPYLIBRARYCATEGORY:
+ case FETCHCATEGORYCATEGORIES:
+ case FETCHCATEGORYCHILDREN:
+ case FETCHCATEGORYSUBSET:
+ case FETCHCATEGORYLINKS:
+ case FETCHCOF:
+ if (result.has("category_id"))
+ {
+ id = result["category_id"];
+ }
+ break;
+ case FETCHITEM:
+ if (result.has("item_id"))
+ {
+ // Error message might contain an item_id!!!
+ id = result["item_id"];
+ }
+ if (result.has("linked_id"))
+ {
+ id = result["linked_id"];
+ }
+ break;
+ case CREATEINVENTORY:
+ // CREATEINVENTORY can have multiple callbacks
+ if (result.has("_created_categories"))
+ {
+ LLSD& cats = result["_created_categories"];
+ LLSD::array_const_iterator cat_iter;
+ for (cat_iter = cats.beginArray(); cat_iter != cats.endArray(); ++cat_iter)
+ {
+ LLUUID cat_id = *cat_iter;
+ callback(cat_id);
+ needs_callback = false;
+ }
+ }
+ if (result.has("_created_items"))
+ {
+ LLSD& items = result["_created_items"];
+ LLSD::array_const_iterator item_iter;
+ for (item_iter = items.beginArray(); item_iter != items.endArray(); ++item_iter)
+ {
+ LLUUID item_id = *item_iter;
+ callback(item_id);
+ needs_callback = false;
+ }
+ }
+ break;
+ default:
+ break;
+ }
- callback(id);
+ if (needs_callback)
+ {
+ // Call callback at least once regardless of failure.
+ // UPDATEITEM doesn't expect an id
+ callback(id);
+ }
}
}
//-------------------------------------------------------------------------
-AISUpdate::AISUpdate(const LLSD& update)
+AISUpdate::AISUpdate(const LLSD& update, AISAPI::COMMAND_TYPE type, const LLSD& request_body)
+: mType(type)
{
+ mFetch = (type == AISAPI::FETCHITEM)
+ || (type == AISAPI::FETCHCATEGORYCHILDREN)
+ || (type == AISAPI::FETCHCATEGORYCATEGORIES)
+ || (type == AISAPI::FETCHCATEGORYSUBSET)
+ || (type == AISAPI::FETCHCOF)
+ || (type == AISAPI::FETCHCATEGORYLINKS)
+ || (type == AISAPI::FETCHORPHANS);
+ // parse update llsd into stuff to do or parse received items.
+ mFetchDepth = MAX_FOLDER_DEPTH_REQUEST;
+ if (mFetch && request_body.has("depth"))
+ {
+ mFetchDepth = request_body["depth"].asInteger();
+ }
+
+ mTimer.setTimerExpirySec(debugLoggingEnabled("Inventory") ? EXPIRY_SECONDS_DEBUG : EXPIRY_SECONDS_LIVE);
+ mTimer.start();
parseUpdate(update);
}
@@ -506,6 +1064,7 @@ void AISUpdate::clearParseResults()
mCatDescendentsKnown.clear();
mCatVersionsUpdated.clear();
mItemsCreated.clear();
+ mItemsLost.clear();
mItemsUpdated.clear();
mCategoriesCreated.clear();
mCategoriesUpdated.clear();
@@ -514,6 +1073,16 @@ void AISUpdate::clearParseResults()
mCategoryIds.clear();
}
+void AISUpdate::checkTimeout()
+{
+ if (mTimer.hasExpired())
+ {
+ llcoro::suspend();
+ LLCoros::checkStop();
+ mTimer.setTimerExpirySec(debugLoggingEnabled("Inventory") ? EXPIRY_SECONDS_DEBUG : EXPIRY_SECONDS_LIVE);
+ }
+}
+
void AISUpdate::parseUpdate(const LLSD& update)
{
clearParseResults();
@@ -601,24 +1170,37 @@ void AISUpdate::parseMeta(const LLSD& update)
void AISUpdate::parseContent(const LLSD& update)
{
- if (update.has("linked_id"))
+ // Errors from a fetch request might contain id without
+ // full item or folder.
+ // Todo: Depending on error we might want to do something,
+ // like removing a 404 item or refetching parent folder
+ if (update.has("linked_id") && update.has("parent_id"))
{
- parseLink(update);
+ parseLink(update, mFetchDepth);
}
- else if (update.has("item_id"))
+ else if (update.has("item_id") && update.has("parent_id"))
{
parseItem(update);
}
- if (update.has("category_id"))
- {
- parseCategory(update);
- }
+ if (mType == AISAPI::FETCHCATEGORYSUBSET)
+ {
+ // initial category is incomplete, don't process it,
+ // go for content instead
+ if (update.has("_embedded"))
+ {
+ parseEmbedded(update["_embedded"], mFetchDepth - 1);
+ }
+ }
+ else if (update.has("category_id") && update.has("parent_id"))
+ {
+ parseCategory(update, mFetchDepth);
+ }
else
{
if (update.has("_embedded"))
{
- parseEmbedded(update["_embedded"]);
+ parseEmbedded(update["_embedded"], mFetchDepth);
}
}
}
@@ -636,7 +1218,17 @@ void AISUpdate::parseItem(const LLSD& item_map)
BOOL rv = new_item->unpackMessage(item_map);
if (rv)
{
- if (curr_item)
+ if (mFetch)
+ {
+ mItemsCreated[item_id] = new_item;
+ new_item->setComplete(true);
+
+ if (new_item->getParentUUID().isNull())
+ {
+ mItemsLost[item_id] = new_item;
+ }
+ }
+ else if (curr_item)
{
mItemsUpdated[item_id] = new_item;
// This statement is here to cause a new entry with 0
@@ -648,6 +1240,7 @@ void AISUpdate::parseItem(const LLSD& item_map)
{
mItemsCreated[item_id] = new_item;
mCatDescendentDeltas[new_item->getParentUUID()]++;
+ new_item->setComplete(true);
}
}
else
@@ -657,7 +1250,7 @@ void AISUpdate::parseItem(const LLSD& item_map)
}
}
-void AISUpdate::parseLink(const LLSD& link_map)
+void AISUpdate::parseLink(const LLSD& link_map, S32 depth)
{
LLUUID item_id = link_map["item_id"].asUUID();
LLPointer<LLViewerInventoryItem> new_link(new LLViewerInventoryItem);
@@ -671,7 +1264,24 @@ void AISUpdate::parseLink(const LLSD& link_map)
if (rv)
{
const LLUUID& parent_id = new_link->getParentUUID();
- if (curr_link)
+ if (mFetch)
+ {
+ LLPermissions default_perms;
+ default_perms.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
+ default_perms.initMasks(PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE);
+ new_link->setPermissions(default_perms);
+ LLSaleInfo default_sale_info;
+ new_link->setSaleInfo(default_sale_info);
+ //LL_DEBUGS("Inventory") << "creating link from llsd: " << ll_pretty_print_sd(link_map) << LL_ENDL;
+ mItemsCreated[item_id] = new_link;
+ new_link->setComplete(true);
+
+ if (new_link->getParentUUID().isNull())
+ {
+ mItemsLost[item_id] = new_link;
+ }
+ }
+ else if (curr_link)
{
mItemsUpdated[item_id] = new_link;
// This statement is here to cause a new entry with 0
@@ -690,7 +1300,13 @@ void AISUpdate::parseLink(const LLSD& link_map)
//LL_DEBUGS("Inventory") << "creating link from llsd: " << ll_pretty_print_sd(link_map) << LL_ENDL;
mItemsCreated[item_id] = new_link;
mCatDescendentDeltas[parent_id]++;
+ new_link->setComplete(true);
}
+
+ if (link_map.has("_embedded"))
+ {
+ parseEmbedded(link_map["_embedded"], depth);
+ }
}
else
{
@@ -700,19 +1316,30 @@ void AISUpdate::parseLink(const LLSD& link_map)
}
-void AISUpdate::parseCategory(const LLSD& category_map)
+void AISUpdate::parseCategory(const LLSD& category_map, S32 depth)
{
- LLUUID category_id = category_map["category_id"].asUUID();
+ LLUUID category_id = category_map["category_id"].asUUID();
+ S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN;
- // Check descendent count first, as it may be needed
- // to populate newly created categories
- if (category_map.has("_embedded"))
- {
- parseDescendentCount(category_id, category_map["_embedded"]);
- }
+ if (category_map.has("version"))
+ {
+ version = category_map["version"].asInteger();
+ }
+
+ LLViewerInventoryCategory *curr_cat = gInventory.getCategory(category_id);
+
+ if (curr_cat
+ && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN
+ && curr_cat->getDescendentCount() != LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN
+ && version > LLViewerInventoryCategory::VERSION_UNKNOWN
+ && version < curr_cat->getVersion())
+ {
+ LL_WARNS() << "Got stale folder, known: " << curr_cat->getVersion()
+ << ", received: " << version << LL_ENDL;
+ return;
+ }
LLPointer<LLViewerInventoryCategory> new_cat;
- LLViewerInventoryCategory *curr_cat = gInventory.getCategory(category_id);
if (curr_cat)
{
// Default to current values where not provided.
@@ -732,13 +1359,58 @@ void AISUpdate::parseCategory(const LLSD& category_map)
}
BOOL rv = new_cat->unpackMessage(category_map);
// *NOTE: unpackMessage does not unpack version or descendent count.
- //if (category_map.has("version"))
- //{
- // mCatVersionsUpdated[category_id] = category_map["version"].asInteger();
- //}
if (rv)
{
- if (curr_cat)
+ // Check descendent count first, as it may be needed
+ // to populate newly created categories
+ if (category_map.has("_embedded"))
+ {
+ parseDescendentCount(category_id, new_cat->getPreferredType(), category_map["_embedded"]);
+ }
+
+ if (mFetch)
+ {
+ uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id);
+ if (mCatDescendentsKnown.end() != lookup_it)
+ {
+ S32 descendent_count = lookup_it->second;
+ LL_DEBUGS("Inventory") << "Setting descendents count to " << descendent_count
+ << " for category " << category_id << LL_ENDL;
+ new_cat->setDescendentCount(descendent_count);
+
+ // set version only if we are sure this update has full data and embeded items
+ // since viewer uses version to decide if folder and content still need fetching
+ if (version > LLViewerInventoryCategory::VERSION_UNKNOWN
+ && depth >= 0)
+ {
+ if (curr_cat && curr_cat->getVersion() > version)
+ {
+ LL_WARNS("Inventory") << "Version was " << curr_cat->getVersion()
+ << ", but fetch returned version " << version
+ << " for category " << category_id << LL_ENDL;
+ }
+ else
+ {
+ LL_DEBUGS("Inventory") << "Setting version to " << version
+ << " for category " << category_id << LL_ENDL;
+ }
+
+ new_cat->setVersion(version);
+ }
+ }
+ else if (curr_cat
+ && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN
+ && version > curr_cat->getVersion())
+ {
+ // Potentially should new_cat->setVersion(unknown) here,
+ // but might be waiting for a callback that would increment
+ LL_DEBUGS("Inventory") << "Category " << category_id
+ << " is stale. Known version: " << curr_cat->getVersion()
+ << " server version: " << version << LL_ENDL;
+ }
+ mCategoriesCreated[category_id] = new_cat;
+ }
+ else if (curr_cat)
{
mCategoriesUpdated[category_id] = new_cat;
// This statement is here to cause a new entry with 0
@@ -751,20 +1423,22 @@ void AISUpdate::parseCategory(const LLSD& category_map)
else
{
// Set version/descendents for newly created categories.
- if (category_map.has("version"))
- {
- S32 version = category_map["version"].asInteger();
- LL_DEBUGS("Inventory") << "Setting version to " << version
- << " for new category " << category_id << LL_ENDL;
- new_cat->setVersion(version);
- }
- uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id);
- if (mCatDescendentsKnown.end() != lookup_it)
- {
- S32 descendent_count = lookup_it->second;
- LL_DEBUGS("Inventory") << "Setting descendents count to " << descendent_count
- << " for new category " << category_id << LL_ENDL;
- new_cat->setDescendentCount(descendent_count);
+ uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id);
+ if (mCatDescendentsKnown.end() != lookup_it)
+ {
+ S32 descendent_count = lookup_it->second;
+ LL_DEBUGS("Inventory") << "Setting descendents count to " << descendent_count
+ << " for new category " << category_id << LL_ENDL;
+ new_cat->setDescendentCount(descendent_count);
+
+ // Don't set version unles correct children count is present
+ if (category_map.has("version"))
+ {
+ S32 version = category_map["version"].asInteger();
+ LL_DEBUGS("Inventory") << "Setting version to " << version
+ << " for new category " << category_id << LL_ENDL;
+ new_cat->setVersion(version);
+ }
}
mCategoriesCreated[category_id] = new_cat;
mCatDescendentDeltas[new_cat->getParentUUID()]++;
@@ -779,28 +1453,35 @@ void AISUpdate::parseCategory(const LLSD& category_map)
// Check for more embedded content.
if (category_map.has("_embedded"))
{
- parseEmbedded(category_map["_embedded"]);
+ parseEmbedded(category_map["_embedded"], depth - 1);
}
}
-void AISUpdate::parseDescendentCount(const LLUUID& category_id, const LLSD& embedded)
+void AISUpdate::parseDescendentCount(const LLUUID& category_id, LLFolderType::EType type, const LLSD& embedded)
{
- // We can only determine true descendent count if this contains all descendent types.
- if (embedded.has("categories") &&
- embedded.has("links") &&
- embedded.has("items"))
- {
- mCatDescendentsKnown[category_id] = embedded["categories"].size();
- mCatDescendentsKnown[category_id] += embedded["links"].size();
- mCatDescendentsKnown[category_id] += embedded["items"].size();
- }
+ // We can only determine true descendent count if this contains all descendent types.
+ if (embedded.has("categories") &&
+ embedded.has("links") &&
+ embedded.has("items"))
+ {
+ mCatDescendentsKnown[category_id] = embedded["categories"].size();
+ mCatDescendentsKnown[category_id] += embedded["links"].size();
+ mCatDescendentsKnown[category_id] += embedded["items"].size();
+ }
+ else if (mFetch && embedded.has("links") && (type == LLFolderType::FT_CURRENT_OUTFIT || type == LLFolderType::FT_OUTFIT))
+ {
+ // COF and outfits contain links only
+ mCatDescendentsKnown[category_id] = embedded["links"].size();
+ }
}
-void AISUpdate::parseEmbedded(const LLSD& embedded)
+void AISUpdate::parseEmbedded(const LLSD& embedded, S32 depth)
{
+ checkTimeout();
+
if (embedded.has("links")) // _embedded in a category
{
- parseEmbeddedLinks(embedded["links"]);
+ parseEmbeddedLinks(embedded["links"], depth);
}
if (embedded.has("items")) // _embedded in a category
{
@@ -812,11 +1493,11 @@ void AISUpdate::parseEmbedded(const LLSD& embedded)
}
if (embedded.has("categories")) // _embedded in a category
{
- parseEmbeddedCategories(embedded["categories"]);
+ parseEmbeddedCategories(embedded["categories"], depth);
}
if (embedded.has("category")) // _embedded in a link
{
- parseEmbeddedCategory(embedded["category"]);
+ parseEmbeddedCategory(embedded["category"], depth);
}
}
@@ -833,7 +1514,7 @@ void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uui
}
}
-void AISUpdate::parseEmbeddedLinks(const LLSD& links)
+void AISUpdate::parseEmbeddedLinks(const LLSD& links, S32 depth)
{
for(LLSD::map_const_iterator linkit = links.beginMap(),
linkend = links.endMap();
@@ -841,13 +1522,13 @@ void AISUpdate::parseEmbeddedLinks(const LLSD& links)
{
const LLUUID link_id((*linkit).first);
const LLSD& link_map = (*linkit).second;
- if (mItemIds.end() == mItemIds.find(link_id))
+ if (!mFetch && mItemIds.end() == mItemIds.find(link_id))
{
LL_DEBUGS("Inventory") << "Ignoring link not in items list " << link_id << LL_ENDL;
}
else
{
- parseLink(link_map);
+ parseLink(link_map, depth);
}
}
}
@@ -857,7 +1538,7 @@ void AISUpdate::parseEmbeddedItem(const LLSD& item)
// a single item (_embedded in a link)
if (item.has("item_id"))
{
- if (mItemIds.end() != mItemIds.find(item["item_id"].asUUID()))
+ if (mFetch || mItemIds.end() != mItemIds.find(item["item_id"].asUUID()))
{
parseItem(item);
}
@@ -873,7 +1554,7 @@ void AISUpdate::parseEmbeddedItems(const LLSD& items)
{
const LLUUID item_id((*itemit).first);
const LLSD& item_map = (*itemit).second;
- if (mItemIds.end() == mItemIds.find(item_id))
+ if (!mFetch && mItemIds.end() == mItemIds.find(item_id))
{
LL_DEBUGS("Inventory") << "Ignoring item not in items list " << item_id << LL_ENDL;
}
@@ -884,19 +1565,19 @@ void AISUpdate::parseEmbeddedItems(const LLSD& items)
}
}
-void AISUpdate::parseEmbeddedCategory(const LLSD& category)
+void AISUpdate::parseEmbeddedCategory(const LLSD& category, S32 depth)
{
// a single category (_embedded in a link)
if (category.has("category_id"))
{
- if (mCategoryIds.end() != mCategoryIds.find(category["category_id"].asUUID()))
+ if (mFetch || mCategoryIds.end() != mCategoryIds.find(category["category_id"].asUUID()))
{
- parseCategory(category);
+ parseCategory(category, depth);
}
}
}
-void AISUpdate::parseEmbeddedCategories(const LLSD& categories)
+void AISUpdate::parseEmbeddedCategories(const LLSD& categories, S32 depth)
{
// a map of categories (_embedded in a category)
for(LLSD::map_const_iterator categoryit = categories.beginMap(),
@@ -905,19 +1586,21 @@ void AISUpdate::parseEmbeddedCategories(const LLSD& categories)
{
const LLUUID category_id((*categoryit).first);
const LLSD& category_map = (*categoryit).second;
- if (mCategoryIds.end() == mCategoryIds.find(category_id))
+ if (!mFetch && mCategoryIds.end() == mCategoryIds.find(category_id))
{
LL_DEBUGS("Inventory") << "Ignoring category not in categories list " << category_id << LL_ENDL;
}
else
{
- parseCategory(category_map);
+ parseCategory(category_map, depth);
}
}
}
void AISUpdate::doUpdate()
{
+ checkTimeout();
+
// Do version/descendant accounting.
for (std::map<LLUUID,S32>::const_iterator catit = mCatDescendentDeltas.begin();
catit != mCatDescendentDeltas.end(); ++catit)
@@ -959,6 +1642,7 @@ void AISUpdate::doUpdate()
}
// CREATE CATEGORIES
+ const S32 MAX_UPDATE_BACKLOG = 50; // stall prevention
for (deferred_category_map_t::const_iterator create_it = mCategoriesCreated.begin();
create_it != mCategoriesCreated.end(); ++create_it)
{
@@ -967,6 +1651,13 @@ void AISUpdate::doUpdate()
gInventory.updateCategory(new_category, LLInventoryObserver::CREATE);
LL_DEBUGS("Inventory") << "created category " << category_id << LL_ENDL;
+
+ // fetching can receive massive amount of items and folders
+ if (gInventory.getChangedIDs().size() > MAX_UPDATE_BACKLOG)
+ {
+ gInventory.notifyObservers();
+ checkTimeout();
+ }
}
// UPDATE CATEGORIES
@@ -991,6 +1682,24 @@ void AISUpdate::doUpdate()
}
}
+ // LOST ITEMS
+ if (!mItemsLost.empty())
+ {
+ LL_INFOS("Inventory") << "Received " << (S32)mItemsLost.size() << " items without a parent" << LL_ENDL;
+ const LLUUID lost_uuid(gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
+ if (lost_uuid.notNull())
+ {
+ for (deferred_item_map_t::const_iterator lost_it = mItemsLost.begin();
+ lost_it != mItemsLost.end(); ++lost_it)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = lost_it->second;
+
+ new_item->setParent(lost_uuid);
+ new_item->updateParentOnServer(FALSE);
+ }
+ }
+ }
+
// CREATE ITEMS
for (deferred_item_map_t::const_iterator create_it = mItemsCreated.begin();
create_it != mItemsCreated.end(); ++create_it)
@@ -1003,6 +1712,13 @@ void AISUpdate::doUpdate()
// case this is create.
LL_DEBUGS("Inventory") << "created item " << item_id << LL_ENDL;
gInventory.updateItem(new_item, LLInventoryObserver::CREATE);
+
+ // fetching can receive massive amount of items and folders
+ if (gInventory.getChangedIDs().size() > MAX_UPDATE_BACKLOG)
+ {
+ gInventory.notifyObservers();
+ checkTimeout();
+ }
}
// UPDATE ITEMS
@@ -1063,6 +1779,8 @@ void AISUpdate::doUpdate()
}
}
+ checkTimeout();
+
gInventory.notifyObservers();
}
diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h
index 856f3fc180..0fdf4a0b74 100644
--- a/indra/newview/llaisapi.h
+++ b/indra/newview/llaisapi.h
@@ -38,6 +38,12 @@
class AISAPI
{
public:
+ static const S32 HTTP_TIMEOUT;
+ typedef enum {
+ INVENTORY,
+ LIBRARY
+ } ITEM_TYPE;
+
typedef boost::function<void(const LLUUID &invItem)> completion_t;
static bool isAvailable();
@@ -50,9 +56,16 @@ public:
static void PurgeDescendents(const LLUUID &categoryId, completion_t callback = completion_t());
static void UpdateCategory(const LLUUID &categoryId, const LLSD &updates, completion_t callback = completion_t());
static void UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t callback = completion_t());
+ static void FetchItem(const LLUUID &itemId, ITEM_TYPE type, completion_t callback = completion_t());
+ static void FetchCategoryChildren(const LLUUID &catId, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0);
+ static void FetchCategoryChildren(const std::string &identifier, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0);
+ static void FetchCategoryCategories(const LLUUID &catId, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0);
+ static void FetchCategorySubset(const LLUUID& catId, const uuid_vec_t specificChildren, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0);
+ static void FetchCOF(completion_t callback = completion_t());
+ static void FetchCategoryLinks(const LLUUID &catId, completion_t callback = completion_t());
+ static void FetchOrphans(completion_t callback = completion_t() );
static void CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, bool copySubfolders, completion_t callback = completion_t());
-private:
typedef enum {
COPYINVENTORY,
SLAMFOLDER,
@@ -61,9 +74,18 @@ private:
PURGEDESCENDENTS,
UPDATECATEGORY,
UPDATEITEM,
- COPYLIBRARYCATEGORY
+ COPYLIBRARYCATEGORY,
+ CREATEINVENTORY,
+ FETCHITEM,
+ FETCHCATEGORYCHILDREN,
+ FETCHCATEGORYCATEGORIES,
+ FETCHCATEGORYSUBSET,
+ FETCHCOF,
+ FETCHORPHANS,
+ FETCHCATEGORYLINKS
} COMMAND_TYPE;
+private:
static const std::string INVENTORY_CAP_NAME;
static const std::string LIBRARY_CAP_NAME;
@@ -72,6 +94,7 @@ private:
static void EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc);
static void onIdle(void *userdata); // launches postponed AIS commands
+ static void onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD& request_body);
static std::string getInvCap();
static std::string getLibCap();
@@ -87,24 +110,30 @@ private:
class AISUpdate
{
public:
- AISUpdate(const LLSD& update);
+ AISUpdate(const LLSD& update, AISAPI::COMMAND_TYPE type, const LLSD& request_body);
void parseUpdate(const LLSD& update);
void parseMeta(const LLSD& update);
void parseContent(const LLSD& update);
void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids);
- void parseLink(const LLSD& link_map);
+ void parseLink(const LLSD& link_map, S32 depth);
void parseItem(const LLSD& link_map);
- void parseCategory(const LLSD& link_map);
- void parseDescendentCount(const LLUUID& category_id, const LLSD& embedded);
- void parseEmbedded(const LLSD& embedded);
- void parseEmbeddedLinks(const LLSD& links);
+ void parseCategory(const LLSD& link_map, S32 depth);
+ void parseDescendentCount(const LLUUID& category_id, LLFolderType::EType type, const LLSD& embedded);
+ void parseEmbedded(const LLSD& embedded, S32 depth);
+ void parseEmbeddedLinks(const LLSD& links, S32 depth);
void parseEmbeddedItems(const LLSD& items);
- void parseEmbeddedCategories(const LLSD& categories);
+ void parseEmbeddedCategories(const LLSD& categories, S32 depth);
void parseEmbeddedItem(const LLSD& item);
- void parseEmbeddedCategory(const LLSD& category);
+ void parseEmbeddedCategory(const LLSD& category, S32 depth);
void doUpdate();
private:
void clearParseResults();
+ void checkTimeout();
+
+ // Debug is very log-heavy, give it more time or it will take forever to process
+ // Todo: find a way to make throttle static isntead of per-request
+ const F32 EXPIRY_SECONDS_DEBUG = 1.f;
+ const F32 EXPIRY_SECONDS_LIVE = 0.008f;
typedef std::map<LLUUID,S32> uuid_int_map_t;
uuid_int_map_t mCatDescendentDeltas;
@@ -113,6 +142,7 @@ private:
typedef std::map<LLUUID,LLPointer<LLViewerInventoryItem> > deferred_item_map_t;
deferred_item_map_t mItemsCreated;
+ deferred_item_map_t mItemsLost;
deferred_item_map_t mItemsUpdated;
typedef std::map<LLUUID,LLPointer<LLViewerInventoryCategory> > deferred_category_map_t;
deferred_category_map_t mCategoriesCreated;
@@ -123,6 +153,10 @@ private:
uuid_list_t mObjectsDeletedIds;
uuid_list_t mItemIds;
uuid_list_t mCategoryIds;
+ bool mFetch;
+ S32 mFetchDepth;
+ LLTimer mTimer;
+ AISAPI::COMMAND_TYPE mType;
};
#endif
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index d0fad07f1c..8010b84c20 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -40,6 +40,7 @@
#include "llgesturemgr.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
+#include "llinventorymodelbackgroundfetch.h"
#include "llinventoryobserver.h"
#include "llmd5.h"
#include "llnotificationsutil.h"
@@ -590,6 +591,71 @@ LLUpdateAppearanceAndEditWearableOnDestroy::~LLUpdateAppearanceAndEditWearableOn
}
}
+class LLBrokenLinkObserver : public LLInventoryObserver
+{
+public:
+ LLUUID mUUID;
+ bool mEnforceItemRestrictions;
+ bool mEnforceOrdering;
+ nullary_func_t mPostUpdateFunc;
+
+ LLBrokenLinkObserver(const LLUUID& uuid,
+ bool enforce_item_restrictions ,
+ bool enforce_ordering ,
+ nullary_func_t post_update_func) :
+ mUUID(uuid),
+ mEnforceItemRestrictions(enforce_item_restrictions),
+ mEnforceOrdering(enforce_ordering),
+ mPostUpdateFunc(post_update_func)
+ {
+ }
+ /* virtual */ void changed(U32 mask);
+ void postProcess();
+};
+
+void LLBrokenLinkObserver::changed(U32 mask)
+{
+ if (mask & LLInventoryObserver::REBUILD)
+ {
+ // This observer should be executed after LLInventoryPanel::itemChanged(),
+ // but if it isn't, consider calling updateAppearanceFromCOF with a delay
+ const uuid_set_t& changed_item_ids = gInventory.getChangedIDs();
+ for (uuid_set_t::const_iterator it = changed_item_ids.begin(); it != changed_item_ids.end(); ++it)
+ {
+ const LLUUID& id = *it;
+ if (id == mUUID)
+ {
+ // Might not be processed yet and it is not a
+ // good idea to update appearane here, postpone.
+ doOnIdleOneTime([this]()
+ {
+ postProcess();
+ });
+
+ gInventory.removeObserver(this);
+ return;
+ }
+ }
+ }
+}
+
+void LLBrokenLinkObserver::postProcess()
+{
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ llassert(item && !item->getIsBrokenLink()); // the whole point was to get a correct link
+ if (item && item->getIsBrokenLink())
+ {
+ LL_INFOS_ONCE("Avatar") << "Outfit link broken despite being regenerated" << LL_ENDL;
+ LL_DEBUGS("Avatar", "Inventory") << "Outfit link " << mUUID << " \"" << item->getName() << "\" is broken despite being regenerated" << LL_ENDL;
+ }
+
+ LLAppearanceMgr::instance().updateAppearanceFromCOF(
+ mEnforceItemRestrictions ,
+ mEnforceOrdering ,
+ mPostUpdateFunc);
+ delete this;
+}
+
struct LLFoundData
{
@@ -1706,12 +1772,18 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds
{
parent_id = gInventory.getRootFolderID();
}
- LLUUID subfolder_id = gInventory.createNewCategory( parent_id,
- LLFolderType::FT_NONE,
- src_cat->getName());
- shallowCopyCategoryContents(src_id, subfolder_id, cb);
+ gInventory.createNewCategory(
+ parent_id,
+ LLFolderType::FT_NONE,
+ src_cat->getName(),
+ [src_id, cb](const LLUUID &new_id)
+ {
+ LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_id, new_id, cb);
- gInventory.notifyObservers();
+ gInventory.notifyObservers();
+ },
+ src_cat->getThumbnailUUID()
+ );
}
void LLAppearanceMgr::slamCategoryLinks(const LLUUID& src_id, const LLUUID& dst_id,
@@ -2414,6 +2486,39 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions,
LL_DEBUGS("Avatar") << self_av_string() << "starting" << LL_ENDL;
+ if (gInventory.hasPosiblyBrockenLinks())
+ {
+ // Inventory has either broken links or links that
+ // haven't loaded yet.
+ // Check if LLAppearanceMgr needs to wait.
+ LLUUID current_outfit_id = getCOF();
+ LLInventoryModel::item_array_t cof_items;
+ LLInventoryModel::cat_array_t cof_cats;
+ LLFindBrokenLinks is_brocken_link;
+ gInventory.collectDescendentsIf(current_outfit_id,
+ cof_cats,
+ cof_items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_brocken_link);
+
+ if (cof_items.size() > 0)
+ {
+ // Some links haven't loaded yet, but fetch isn't complete so
+ // links are likely fine and we will have to wait for them to
+ // load
+ if (LLInventoryModelBackgroundFetch::getInstance()->folderFetchActive())
+ {
+
+ LLBrokenLinkObserver* observer = new LLBrokenLinkObserver(cof_items.front()->getUUID(),
+ enforce_item_restrictions,
+ enforce_ordering,
+ post_update_func);
+ gInventory.addObserver(observer);
+ return;
+ }
+ }
+ }
+
if (enforce_item_restrictions)
{
// The point here is just to call
@@ -2730,22 +2835,29 @@ void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool ap
{
pid = gInventory.getRootFolderID();
}
-
- LLUUID new_cat_id = gInventory.createNewCategory(
+
+ gInventory.createNewCategory(
pid,
LLFolderType::FT_NONE,
- name);
-
- // Create a CopyMgr that will copy items, manage its own destruction
- new LLCallAfterInventoryCopyMgr(
- *items, new_cat_id, std::string("wear_inventory_category_callback"),
- boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar,
- LLAppearanceMgr::getInstance(),
- gInventory.getCategory(new_cat_id),
- append));
-
- // BAP fixes a lag in display of created dir.
- gInventory.notifyObservers();
+ name,
+ [cat_id, append](const LLUUID& new_cat_id)
+ {
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(cat_id, cats, items);
+ // Create a CopyMgr that will copy items, manage its own destruction
+ new LLCallAfterInventoryCopyMgr(
+ *items, new_cat_id, std::string("wear_inventory_category_callback"),
+ boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar,
+ LLAppearanceMgr::getInstance(),
+ gInventory.getCategory(new_cat_id),
+ append));
+
+ // BAP fixes a lag in display of created dir.
+ gInventory.notifyObservers();
+ },
+ cat->getThumbnailUUID()
+ );
}
else
{
@@ -3203,7 +3315,7 @@ void LLAppearanceMgr::copyLibraryGestures()
// Copy gestures
LLUUID lib_gesture_cat_id =
- gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE,false);
+ gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE);
if (lib_gesture_cat_id.isNull())
{
LL_WARNS() << "Unable to copy gestures, source category not found" << LL_ENDL;
@@ -3714,7 +3826,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd
if (cofVersion == LLViewerInventoryCategory::VERSION_UNKNOWN)
{
- LL_WARNS("AVatar") << "COF version is unknown... not requesting until COF version is known." << LL_ENDL;
+ LL_INFOS("AVatar") << "COF version is unknown... not requesting until COF version is known." << LL_ENDL;
return;
}
else
@@ -3986,26 +4098,15 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo
// First, make a folder in the My Outfits directory.
const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
- if (AISAPI::isAvailable())
- {
- // cap-based category creation was buggy until recently. use
- // existence of AIS as an indicator the fix is present. Does
- // not actually use AIS to create the category.
- inventory_func_type func = boost::bind(&LLAppearanceMgr::onOutfitFolderCreated,this,_1,show_panel);
- gInventory.createNewCategory(
- parent_id,
- LLFolderType::FT_OUTFIT,
- new_folder_name,
- func);
- }
- else
- {
- LLUUID folder_id = gInventory.createNewCategory(
- parent_id,
- LLFolderType::FT_OUTFIT,
- new_folder_name);
- onOutfitFolderCreated(folder_id, show_panel);
- }
+
+ gInventory.createNewCategory(
+ parent_id,
+ LLFolderType::FT_OUTFIT,
+ new_folder_name,
+ [show_panel](const LLUUID &new_cat_id)
+ {
+ LLAppearanceMgr::getInstance()->onOutfitFolderCreated(new_cat_id, show_panel);
+ });
}
void LLAppearanceMgr::wearBaseOutfit()
@@ -4338,6 +4439,73 @@ public:
~CallAfterCategoryFetchStage1()
{
}
+ /*virtual*/ void startFetch()
+ {
+ bool ais3 = AISAPI::isAvailable();
+ for (uuid_vec_t::const_iterator it = mIDs.begin(); it != mIDs.end(); ++it)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
+ if (!cat) continue;
+ if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ // CHECK IT: isCategoryComplete() checks both version and descendant count but
+ // fetch() only works for Unknown version and doesn't care about descentants,
+ // as result fetch won't start and folder will potentially get stuck as
+ // incomplete in observer.
+ // Likely either both should use only version or both should check descendants.
+ cat->fetch(); //blindly fetch it without seeing if anything else is fetching it.
+ mIncomplete.push_back(*it); //Add to list of things being downloaded for this observer.
+ }
+ else if (!isCategoryComplete(cat))
+ {
+ LL_DEBUGS("Inventory") << "Categoty " << *it << " incomplete despite having version" << LL_ENDL;
+ LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true);
+ mIncomplete.push_back(*it);
+ }
+ else if (ais3)
+ {
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items);
+
+ if (items)
+ {
+ S32 complete_count = 0;
+ S32 incomplete_count = 0;
+ for (LLInventoryModel::item_array_t::const_iterator it = items->begin(); it < items->end(); ++it)
+ {
+ if (!(*it)->isFinished())
+ {
+ incomplete_count++;
+ }
+ else
+ {
+ complete_count++;
+ }
+ }
+ // AIS can fetch couple items, but if there
+ // is more than a dozen it will be very slow
+ // it's faster to get whole folder in such case
+ if (incomplete_count > LLInventoryFetchItemsObserver::MAX_INDIVIDUAL_ITEM_REQUESTS
+ || (incomplete_count > 1 && complete_count == 0))
+ {
+ LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true);
+ mIncomplete.push_back(*it);
+ }
+ else
+ {
+ // let stage2 handle incomplete ones
+ mComplete.push_back(*it);
+ }
+ }
+ // else should have been handled by isCategoryComplete
+ }
+ else
+ {
+ mComplete.push_back(*it);
+ }
+ }
+ }
virtual void done()
{
if (mComplete.size() <= 0)
@@ -4354,13 +4522,11 @@ public:
// What we do here is get the complete information on the
// items in the requested category, and set up an observer
// that will wait for that to happen.
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- gInventory.collectDescendents(mComplete.front(),
- cat_array,
- item_array,
- LLInventoryModel::EXCLUDE_TRASH);
- S32 count = item_array.size();
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(mComplete.front(), cats, items);
+
+ S32 count = items->size();
if(!count)
{
LL_WARNS() << "Nothing fetched in category " << mComplete.front()
@@ -4372,11 +4538,13 @@ public:
return;
}
- LL_INFOS() << "stage1 got " << item_array.size() << " items, passing to stage2 " << LL_ENDL;
+ LLViewerInventoryCategory* cat = gInventory.getCategory(mComplete.front());
+ S32 version = cat ? cat->getVersion() : -2;
+ LL_INFOS() << "stage1, category " << mComplete.front() << " got " << count << " items, version " << version << " passing to stage2 " << LL_ENDL;
uuid_vec_t ids;
for(S32 i = 0; i < count; ++i)
{
- ids.push_back(item_array.at(i)->getUUID());
+ ids.push_back(items->at(i)->getUUID());
}
gInventory.removeObserver(this);
@@ -4401,18 +4569,78 @@ protected:
nullary_func_t mCallable;
};
+void callAfterCOFFetch(nullary_func_t cb)
+{
+ LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
+ LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
+
+ if (AISAPI::isAvailable())
+ {
+ // Mark cof (update timer) so that background fetch won't request it
+ cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
+ // For reliability assume that we have no relevant cache, so
+ // fetch cof along with items cof's links point to.
+ AISAPI::FetchCOF([cb](const LLUUID& id)
+ {
+ cb();
+ LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
+ LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
+ if (cat)
+ {
+ cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
+ }
+ });
+ }
+ else
+ {
+ LL_INFOS() << "AIS API v3 not available, using callAfterCategoryFetch" << LL_ENDL;
+ // startup should have marked folder as fetching, remove that
+ cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
+ callAfterCategoryFetch(cat_id, cb);
+ }
+}
+
void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb)
{
- CallAfterCategoryFetchStage1 *stage1 = new CallAfterCategoryFetchStage1(cat_id, cb);
- stage1->startFetch();
- if (stage1->isFinished())
- {
- stage1->done();
- }
- else
- {
- gInventory.addObserver(stage1);
- }
+ CallAfterCategoryFetchStage1* stage1 = new CallAfterCategoryFetchStage1(cat_id, cb);
+ stage1->startFetch();
+ if (stage1->isFinished())
+ {
+ stage1->done();
+ }
+ else
+ {
+ gInventory.addObserver(stage1);
+ }
+}
+
+void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb)
+{
+ LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
+ if (AISAPI::isAvailable())
+ {
+ // Mark folder (update timer) so that background fetch won't request it
+ cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
+ // Assume that we have no relevant cache. Fetch folder, and items folder's links point to.
+ AISAPI::FetchCategoryLinks(cat_id,
+ [cb, cat_id](const LLUUID &id)
+ {
+ cb();
+ LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
+ if (cat)
+ {
+ cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
+ }
+ });
+ }
+ else
+ {
+ LL_WARNS() << "AIS API v3 not available, can't use AISAPI::FetchCOF" << LL_ENDL;
+ // startup should have marked folder as fetching, remove that
+ cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
+ callAfterCategoryFetch(cat_id, cb);
+ }
+
}
void add_wearable_type_counts(const uuid_vec_t& ids,
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index cf953d21ac..43839e47a6 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -338,7 +338,9 @@ public:
LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name);
// Invoke a given callable after category contents are fully fetched.
+void callAfterCOFFetch(nullary_func_t cb);
void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb);
+void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb);
// Wear all items in a uuid vector.
void wear_multiple(const uuid_vec_t& ids, bool replace);
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 1de0ed3f93..3991b069e6 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -216,7 +216,7 @@
#include "llcommandlineparser.h"
#include "llfloatermemleak.h"
#include "llfloaterreg.h"
-#include "llfloatersimpleoutfitsnapshot.h"
+#include "llfloatersimplesnapshot.h"
#include "llfloatersnapshot.h"
#include "llsidepanelinventory.h"
#include "llatmosphere.h"
@@ -1537,7 +1537,7 @@ bool LLAppViewer::doFrame()
LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df Snapshot" )
pingMainloopTimeout("Main:Snapshot");
LLFloaterSnapshot::update(); // take snapshots
- LLFloaterSimpleOutfitSnapshot::update();
+ LLFloaterSimpleSnapshot::update();
gGLActive = FALSE;
}
diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp
index 1feefc3ef8..d3fce306bc 100644
--- a/indra/newview/llattachmentsmgr.cpp
+++ b/indra/newview/llattachmentsmgr.cpp
@@ -240,6 +240,13 @@ void LLAttachmentsMgr::linkRecentlyArrivedAttachments()
return;
}
+ if (LLAppearanceMgr::instance().getCOFVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ // Wait for cof to load
+ LL_DEBUGS_ONCE("Avatar") << "Received atachments, but cof isn't loaded yet, postponing processing" << LL_ENDL;
+ return;
+ }
+
LL_DEBUGS("Avatar") << "ATT checking COF linkability for " << mRecentlyArrivedAttachments.size()
<< " recently arrived items" << LL_ENDL;
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index 3e450e6dec..313339f131 100644
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -730,39 +730,55 @@ namespace action_give_inventory
/**
* Checks My Inventory visibility.
*/
+ static bool is_give_inventory_acceptable_ids(const std::set<LLUUID> inventory_selected_uuids)
+ {
+ if (inventory_selected_uuids.empty()) return false; // nothing selected
+
+ bool acceptable = false;
+ std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin();
+ const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end();
+ for (; it != it_end; ++it)
+ {
+ LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it);
+ // any category can be offered.
+ if (inv_cat)
+ {
+ acceptable = true;
+ continue;
+ }
+
+ LLViewerInventoryItem* inv_item = gInventory.getItem(*it);
+ // check if inventory item can be given
+ if (LLGiveInventory::isInventoryGiveAcceptable(inv_item))
+ {
+ acceptable = true;
+ continue;
+ }
+
+ // there are neither item nor category in inventory
+ acceptable = false;
+ break;
+ }
+ return acceptable;
+ }
static bool is_give_inventory_acceptable(LLInventoryPanel* panel = NULL)
{
// check selection in the panel
- const std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);
- if (inventory_selected_uuids.empty()) return false; // nothing selected
-
- bool acceptable = false;
- std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin();
- const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end();
- for (; it != it_end; ++it)
- {
- LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it);
- // any category can be offered.
- if (inv_cat)
- {
- acceptable = true;
- continue;
- }
-
- LLViewerInventoryItem* inv_item = gInventory.getItem(*it);
- // check if inventory item can be given
- if (LLGiveInventory::isInventoryGiveAcceptable(inv_item))
- {
- acceptable = true;
- continue;
- }
+ std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);
+ if (inventory_selected_uuids.empty())
+ {
+ if(panel && panel->getRootFolder() && panel->getRootFolder()->isSingleFolderMode())
+ {
+ inventory_selected_uuids.insert(panel->getRootFolderID());
+ }
+ else
+ {
+ return false; // nothing selected
+ }
+ }
- // there are neither item nor category in inventory
- acceptable = false;
- break;
- }
- return acceptable;
+ return is_give_inventory_acceptable_ids(inventory_selected_uuids);
}
static void build_items_string(const std::set<LLUUID>& inventory_selected_uuids , std::string& items_string)
@@ -890,46 +906,65 @@ namespace action_give_inventory
* @param avatar_names - avatar names request to be sent.
* @param avatar_uuids - avatar names request to be sent.
*/
- static void give_inventory(const uuid_vec_t& avatar_uuids, const std::vector<LLAvatarName> avatar_names, LLInventoryPanel* panel = NULL)
- {
- llassert(avatar_names.size() == avatar_uuids.size());
- const std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);
- if (inventory_selected_uuids.empty())
- {
- return;
- }
+ static void give_inventory_ids(const uuid_vec_t& avatar_uuids, const std::vector<LLAvatarName> avatar_names, const uuid_set_t inventory_selected_uuids)
+ {
+ llassert(avatar_names.size() == avatar_uuids.size());
- std::string residents;
- LLAvatarActions::buildResidentsString(avatar_names, residents, true);
+ if (inventory_selected_uuids.empty())
+ {
+ return;
+ }
- std::string items;
- build_items_string(inventory_selected_uuids, items);
+ std::string residents;
+ LLAvatarActions::buildResidentsString(avatar_names, residents, true);
- int folders_count = 0;
- std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin();
+ std::string items;
+ build_items_string(inventory_selected_uuids, items);
- //traverse through selected inventory items and count folders among them
- for ( ; it != inventory_selected_uuids.end() && folders_count <=1 ; ++it)
- {
- LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it);
- if (NULL != inv_cat)
- {
- folders_count++;
- }
- }
+ int folders_count = 0;
+ std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin();
- // EXP-1599
- // In case of sharing multiple folders, make the confirmation
- // dialog contain a warning that only one folder can be shared at a time.
- std::string notification = (folders_count > 1) ? "ShareFolderConfirmation" : "ShareItemsConfirmation";
- LLSD substitutions;
- substitutions["RESIDENTS"] = residents;
- substitutions["ITEMS"] = items;
- LLShareInfo::instance().mAvatarNames = avatar_names;
- LLShareInfo::instance().mAvatarUuids = avatar_uuids;
- LLNotificationsUtil::add(notification, substitutions, LLSD(), boost::bind(&give_inventory_cb, _1, _2, inventory_selected_uuids));
- }
+ //traverse through selected inventory items and count folders among them
+ for ( ; it != inventory_selected_uuids.end() && folders_count <=1 ; ++it)
+ {
+ LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it);
+ if (NULL != inv_cat)
+ {
+ folders_count++;
+ }
+ }
+
+ // EXP-1599
+ // In case of sharing multiple folders, make the confirmation
+ // dialog contain a warning that only one folder can be shared at a time.
+ std::string notification = (folders_count > 1) ? "ShareFolderConfirmation" : "ShareItemsConfirmation";
+ LLSD substitutions;
+ substitutions["RESIDENTS"] = residents;
+ substitutions["ITEMS"] = items;
+ LLShareInfo::instance().mAvatarNames = avatar_names;
+ LLShareInfo::instance().mAvatarUuids = avatar_uuids;
+ LLNotificationsUtil::add(notification, substitutions, LLSD(), boost::bind(&give_inventory_cb, _1, _2, inventory_selected_uuids));
+ }
+
+ static void give_inventory(const uuid_vec_t& avatar_uuids, const std::vector<LLAvatarName> avatar_names, LLInventoryPanel* panel = NULL)
+ {
+ llassert(avatar_names.size() == avatar_uuids.size());
+ std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);;
+
+ if (inventory_selected_uuids.empty())
+ {
+ if(panel && panel->getRootFolder() && panel->getRootFolder()->isSingleFolderMode())
+ {
+ inventory_selected_uuids.insert(panel->getRootFolderID());
+ }
+ else
+ {
+ return;
+ }
+ }
+ give_inventory_ids(avatar_uuids, avatar_names, inventory_selected_uuids);
+ }
}
// static
@@ -1037,6 +1072,28 @@ void LLAvatarActions::shareWithAvatars(LLView * panel)
LLNotificationsUtil::add("ShareNotification");
}
+//static
+void LLAvatarActions::shareWithAvatars(const uuid_set_t inventory_selected_uuids, LLFloater* root_floater)
+{
+ using namespace action_give_inventory;
+
+ LLFloaterAvatarPicker* picker =
+ LLFloaterAvatarPicker::show(boost::bind(give_inventory_ids, _1, _2, inventory_selected_uuids), TRUE, FALSE, FALSE, root_floater->getName());
+ if (!picker)
+ {
+ return;
+ }
+
+ picker->setOkBtnEnableCb(boost::bind(is_give_inventory_acceptable_ids, inventory_selected_uuids));
+ picker->openFriendsTab();
+
+ if (root_floater)
+ {
+ root_floater->addDependentFloater(picker);
+ }
+ LLNotificationsUtil::add("ShareNotification");
+}
+
// static
bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NULL*/)
{
diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h
index 86183cc119..8a0f40dd52 100644
--- a/indra/newview/llavataractions.h
+++ b/indra/newview/llavataractions.h
@@ -133,6 +133,7 @@ public:
* Share items with the picked avatars.
*/
static void shareWithAvatars(LLView * panel);
+ static void shareWithAvatars(const uuid_set_t inventory_selected_uuids, LLFloater* root_floater);
/**
* Block/unblock the avatar by id.
diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h
index 7c6980a7e6..3f607d434e 100644
--- a/indra/newview/llconversationmodel.h
+++ b/indra/newview/llconversationmodel.h
@@ -111,6 +111,7 @@ public:
virtual void previewItem( void );
virtual void selectItem(void) { }
virtual void showProperties(void);
+ virtual void navigateToFolder(bool new_window = false, bool change_mode = false) {}
// Methods used in sorting (see LLConversationSort::operator())
EConversationType const getType() const { return mConvType; }
@@ -249,7 +250,7 @@ public:
bool check(const LLFolderViewModelItem* item) { return true; }
bool checkFolder(const LLFolderViewModelItem* folder) const { return true; }
void setEmptyLookupMessage(const std::string& message) { }
- std::string getEmptyLookupMessage() const { return mEmpty; }
+ std::string getEmptyLookupMessage(bool is_empty_folder = false) const { return mEmpty; }
bool showAllResults() const { return true; }
std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const { return std::string::npos; }
std::string::size_type getFilterStringSize() const { return 0; }
diff --git a/indra/newview/llfloaterchangeitemthumbnail.cpp b/indra/newview/llfloaterchangeitemthumbnail.cpp
new file mode 100644
index 0000000000..780130039b
--- /dev/null
+++ b/indra/newview/llfloaterchangeitemthumbnail.cpp
@@ -0,0 +1,956 @@
+/**
+ * @file llfloaterchangeitemthumbnail.cpp
+ * @brief LLFloaterChangeItemThumbnail class implementation
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterchangeitemthumbnail.h"
+
+#include "llbutton.h"
+#include "llclipboard.h"
+#include "lliconctrl.h"
+#include "llinventoryfunctions.h"
+#include "llinventoryicon.h"
+#include "llinventorymodel.h"
+#include "llinventoryobserver.h"
+#include "llfloaterreg.h"
+#include "llfloatersimplesnapshot.h"
+#include "lllineeditor.h"
+#include "llnotificationsutil.h"
+#include "lltextbox.h"
+#include "lltexturectrl.h"
+#include "llthumbnailctrl.h"
+#include "llviewerfoldertype.h"
+#include "llviewermenufile.h"
+#include "llviewerobjectlist.h"
+#include "llviewertexturelist.h"
+#include "llwindow.h"
+
+
+class LLThumbnailImagePicker : public LLFilePickerThread
+{
+public:
+ LLThumbnailImagePicker(const LLUUID &item_id);
+ LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id);
+ ~LLThumbnailImagePicker();
+ void notify(const std::vector<std::string>& filenames) override;
+
+private:
+ LLUUID mInventoryId;
+ LLUUID mTaskId;
+};
+
+LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id)
+ : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE)
+ , mInventoryId(item_id)
+{
+}
+
+LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id)
+ : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE)
+ , mInventoryId(item_id)
+ , mTaskId(task_id)
+{
+}
+
+LLThumbnailImagePicker::~LLThumbnailImagePicker()
+{
+}
+
+void LLThumbnailImagePicker::notify(const std::vector<std::string>& filenames)
+{
+ if (filenames.empty())
+ {
+ return;
+ }
+ std::string file_path = filenames[0];
+ if (file_path.empty())
+ {
+ return;
+ }
+
+ LLFloaterSimpleSnapshot::uploadThumbnail(file_path, mInventoryId, mTaskId);
+}
+
+LLFloaterChangeItemThumbnail::LLFloaterChangeItemThumbnail(const LLSD& key)
+ : LLFloater(key)
+ , mObserverInitialized(false)
+ , mTooltipState(TOOLTIP_NONE)
+{
+}
+
+LLFloaterChangeItemThumbnail::~LLFloaterChangeItemThumbnail()
+{
+ gInventory.removeObserver(this);
+ removeVOInventoryListener();
+}
+
+BOOL LLFloaterChangeItemThumbnail::postBuild()
+{
+ mItemNameText = getChild<LLUICtrl>("item_name");
+ mItemTypeIcon = getChild<LLIconCtrl>("item_type_icon");
+ mThumbnailCtrl = getChild<LLThumbnailCtrl>("item_thumbnail");
+ mToolTipTextBox = getChild<LLTextBox>("tooltip_text");
+
+ LLSD tooltip_text;
+ mToolTipTextBox->setValue(tooltip_text);
+
+ LLButton *upload_local = getChild<LLButton>("upload_local");
+ upload_local->setClickedCallback(onUploadLocal, (void*)this);
+ upload_local->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_LOCAL));
+ upload_local->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_LOCAL));
+
+ LLButton *upload_snapshot = getChild<LLButton>("upload_snapshot");
+ upload_snapshot->setClickedCallback(onUploadSnapshot, (void*)this);
+ upload_snapshot->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT));
+ upload_snapshot->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT));
+
+ LLButton *use_texture = getChild<LLButton>("use_texture");
+ use_texture->setClickedCallback(onUseTexture, (void*)this);
+ use_texture->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_USE_TEXTURE));
+ use_texture->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_USE_TEXTURE));
+
+ mCopyToClipboardBtn = getChild<LLButton>("copy_to_clipboard");
+ mCopyToClipboardBtn->setClickedCallback(onCopyToClipboard, (void*)this);
+ mCopyToClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD));
+ mCopyToClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD));
+
+ mPasteFromClipboardBtn = getChild<LLButton>("paste_from_clipboard");
+ mPasteFromClipboardBtn->setClickedCallback(onPasteFromClipboard, (void*)this);
+ mPasteFromClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD));
+ mPasteFromClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD));
+
+ mRemoveImageBtn = getChild<LLButton>("remove_image");
+ mRemoveImageBtn->setClickedCallback(onRemove, (void*)this);
+ mRemoveImageBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_REMOVE));
+ mRemoveImageBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_REMOVE));
+
+ return LLFloater::postBuild();
+}
+
+void LLFloaterChangeItemThumbnail::onOpen(const LLSD& key)
+{
+ if (!key.has("item_id") && !key.isUUID())
+ {
+ closeFloater();
+ }
+
+ if (key.isUUID())
+ {
+ mItemId = key.asUUID();
+ }
+ else
+ {
+ mItemId = key["item_id"].asUUID();
+ mTaskId = key["task_id"].asUUID();
+ }
+
+ refreshFromInventory();
+}
+
+void LLFloaterChangeItemThumbnail::onFocusReceived()
+{
+ mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents());
+}
+
+void LLFloaterChangeItemThumbnail::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents());
+}
+
+BOOL LLFloaterChangeItemThumbnail::handleDragAndDrop(
+ S32 x,
+ S32 y,
+ MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void *cargo_data,
+ EAcceptance *accept,
+ std::string& tooltip_msg)
+{
+ if (cargo_type == DAD_TEXTURE)
+ {
+ LLInventoryItem *item = (LLInventoryItem *)cargo_data;
+ if (item->getAssetUUID().notNull())
+ {
+ if (drop)
+ {
+ assignAndValidateAsset(item->getAssetUUID());
+ }
+
+ *accept = ACCEPT_YES_SINGLE;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFloaterChangeItemThumbnail " << getKey() << LL_ENDL;
+
+ return TRUE;
+}
+
+void LLFloaterChangeItemThumbnail::changed(U32 mask)
+{
+ //LLInventoryObserver
+
+ if (mTaskId.notNull() || mItemId.isNull())
+ {
+ // Task inventory or not set up yet
+ return;
+ }
+
+ const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs();
+ std::set<LLUUID>::const_iterator it;
+
+ for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++)
+ {
+ // set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288)
+ if (*it == mItemId)
+ {
+ // if there's a change we're interested in.
+ if ((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
+ {
+ refreshFromInventory();
+ }
+ }
+ }
+}
+
+void LLFloaterChangeItemThumbnail::inventoryChanged(LLViewerObject* object,
+ LLInventoryObject::object_list_t* inventory,
+ S32 serial_num,
+ void* user_data)
+{
+ //LLVOInventoryListener
+ refreshFromInventory();
+}
+
+LLInventoryObject* LLFloaterChangeItemThumbnail::getInventoryObject()
+{
+ LLInventoryObject* obj = NULL;
+ if (mTaskId.isNull())
+ {
+ // it is in agent inventory
+ if (!mObserverInitialized)
+ {
+ gInventory.addObserver(this);
+ mObserverInitialized = true;
+ }
+
+ obj = gInventory.getObject(mItemId);
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(mTaskId);
+ if (object)
+ {
+ if (!mObserverInitialized)
+ {
+ registerVOInventoryListener(object, NULL);
+ mObserverInitialized = false;
+ }
+
+ obj = object->getInventoryObject(mItemId);
+ }
+ }
+ return obj;
+}
+
+void LLFloaterChangeItemThumbnail::refreshFromInventory()
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if (!obj)
+ {
+ closeFloater();
+ }
+
+ if (obj)
+ {
+ const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ bool in_trash = gInventory.isObjectDescendentOf(obj->getUUID(), trash_id);
+ if (in_trash && obj->getUUID() != trash_id)
+ {
+ // Close properties when moving to trash
+ // Aren't supposed to view properties from trash
+ closeFloater();
+ }
+ else
+ {
+ refreshFromObject(obj);
+ }
+ }
+ else
+ {
+ closeFloater();
+ }
+}
+
+class LLIsOutfitTextureType : public LLInventoryCollectFunctor
+{
+public:
+ LLIsOutfitTextureType() {}
+ virtual ~LLIsOutfitTextureType() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+};
+
+bool LLIsOutfitTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ return item && (item->getType() == LLAssetType::AT_TEXTURE);
+}
+
+void LLFloaterChangeItemThumbnail::refreshFromObject(LLInventoryObject* obj)
+{
+ LLUIImagePtr icon_img;
+ LLUUID thumbnail_id = obj->getThumbnailUUID();
+
+ LLViewerInventoryItem* item = dynamic_cast<LLViewerInventoryItem*>(obj);
+ if (item)
+ {
+ setTitle(getString("title_item_thumbnail"));
+
+ icon_img = LLInventoryIcon::getIcon(item->getType(), item->getInventoryType(), item->getFlags(), FALSE);
+ mRemoveImageBtn->setEnabled(thumbnail_id.notNull() && ((item->getActualType() != LLAssetType::AT_TEXTURE) || (item->getAssetUUID() != thumbnail_id)));
+ }
+ else
+ {
+ LLViewerInventoryCategory* cat = dynamic_cast<LLViewerInventoryCategory*>(obj);
+
+ if (cat)
+ {
+ setTitle(getString("title_folder_thumbnail"));
+ icon_img = LLUI::getUIImage(LLViewerFolderType::lookupIconName(cat->getPreferredType(), true));
+
+ if (thumbnail_id.isNull() && (cat->getPreferredType() == LLFolderType::FT_OUTFIT))
+ {
+ // Legacy support, check if there is an image inside
+
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ // Not LLIsOfAssetType, because we allow links
+ LLIsOutfitTextureType f;
+ gInventory.getDirectDescendentsOf(mItemId, cats, items, f);
+
+ if (1 == items.size())
+ {
+ LLViewerInventoryItem* item = items.front();
+ if (item && item->getIsLinkType())
+ {
+ item = item->getLinkedItem();
+ }
+ if (item)
+ {
+ thumbnail_id = item->getAssetUUID();
+ if (thumbnail_id.notNull())
+ {
+ // per SL-19188, set this image as a thumbnail
+ LL_INFOS() << "Setting image " << thumbnail_id
+ << " from outfit as a thumbnail for inventory object " << obj->getUUID()
+ << LL_ENDL;
+ assignAndValidateAsset(thumbnail_id, true);
+ }
+ }
+ }
+ }
+
+ mRemoveImageBtn->setEnabled(thumbnail_id.notNull());
+ }
+ }
+ mItemTypeIcon->setImage(icon_img);
+ mItemNameText->setValue(obj->getName());
+
+ mThumbnailCtrl->setValue(thumbnail_id);
+
+ mCopyToClipboardBtn->setEnabled(thumbnail_id.notNull());
+ mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents());
+
+ // todo: some elements might not support setting thumbnails
+ // since they already have them
+ // It is unclear how system folders should function
+}
+
+void LLFloaterChangeItemThumbnail::onUploadLocal(void *userdata)
+{
+ LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
+
+ (new LLThumbnailImagePicker(self->mItemId, self->mTaskId))->getFile();
+
+ LLFloater* floaterp = self->mPickerHandle.get();
+ if (floaterp)
+ {
+ floaterp->closeFloater();
+ }
+ floaterp = self->mSnapshotHandle.get();
+ if (floaterp)
+ {
+ floaterp->closeFloater();
+ }
+}
+
+void LLFloaterChangeItemThumbnail::onUploadSnapshot(void *userdata)
+{
+ LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
+
+ LLFloater* floaterp = self->mSnapshotHandle.get();
+ // Show the dialog
+ if (floaterp)
+ {
+ floaterp->openFloater();
+ }
+ else
+ {
+ LLSD key;
+ key["item_id"] = self->mItemId;
+ key["task_id"] = self->mTaskId;
+ LLFloaterSimpleSnapshot* snapshot_floater = (LLFloaterSimpleSnapshot*)LLFloaterReg::showInstance("simple_snapshot", key, true);
+ if (snapshot_floater)
+ {
+ self->addDependentFloater(snapshot_floater);
+ self->mSnapshotHandle = snapshot_floater->getHandle();
+ snapshot_floater->setOwner(self);
+ }
+ }
+
+ floaterp = self->mPickerHandle.get();
+ if (floaterp)
+ {
+ floaterp->closeFloater();
+ }
+}
+
+void LLFloaterChangeItemThumbnail::onUseTexture(void *userdata)
+{
+ LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
+ LLInventoryObject* obj = self->getInventoryObject();
+ if (obj)
+ {
+ self->showTexturePicker(obj->getThumbnailUUID());
+ }
+
+ LLFloater* floaterp = self->mSnapshotHandle.get();
+ if (floaterp)
+ {
+ floaterp->closeFloater();
+ }
+}
+
+void LLFloaterChangeItemThumbnail::onCopyToClipboard(void *userdata)
+{
+ LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
+ LLInventoryObject* obj = self->getInventoryObject();
+ if (obj)
+ {
+ LLClipboard::instance().reset();
+ LLClipboard::instance().addToClipboard(obj->getThumbnailUUID(), LLAssetType::AT_NONE);
+ self->mPasteFromClipboardBtn->setEnabled(true);
+ }
+}
+
+void LLFloaterChangeItemThumbnail::onPasteFromClipboard(void *userdata)
+{
+ LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
+ std::vector<LLUUID> objects;
+ LLClipboard::instance().pasteFromClipboard(objects);
+ if (objects.size() > 0)
+ {
+ LLUUID potential_uuid = objects[0];
+ LLUUID asset_id;
+
+ if (potential_uuid.notNull())
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(potential_uuid);
+ if (item)
+ {
+ // no point checking snapshot?
+ if (item->getType() == LLAssetType::AT_TEXTURE)
+ {
+ bool copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID());
+
+ if (copy && xfer)
+ {
+ asset_id = item->getAssetUUID();
+ }
+ else
+ {
+ LLNotificationsUtil::add("ThumbnailInsufficientPermissions");
+ return;
+ }
+ }
+ }
+ else
+ {
+ // assume that this is a texture
+ asset_id = potential_uuid;
+ }
+ }
+
+ LLInventoryObject* obj = self->getInventoryObject();
+ if (obj && obj->getThumbnailUUID() == asset_id)
+ {
+ // nothing to do
+ return;
+ }
+ if (asset_id.notNull())
+ {
+ self->assignAndValidateAsset(asset_id);
+ }
+ // else show 'buffer has no texture' warning?
+ }
+}
+
+void LLFloaterChangeItemThumbnail::onRemove(void *userdata)
+{
+ LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
+
+ LLSD payload;
+ payload["item_id"] = self->mItemId;
+ payload["object_id"] = self->mTaskId;
+ LLNotificationsUtil::add("DeleteThumbnail", LLSD(), payload, boost::bind(&LLFloaterChangeItemThumbnail::onRemovalConfirmation, _1, _2, self->getHandle()));
+}
+
+// static
+void LLFloaterChangeItemThumbnail::onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle<LLFloater> handle)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0 && !handle.isDead() && !handle.get()->isDead())
+ {
+ LLFloaterChangeItemThumbnail* self = (LLFloaterChangeItemThumbnail*)handle.get();
+ self->setThumbnailId(LLUUID::null);
+ }
+}
+
+struct ImageLoadedData
+{
+ LLUUID mThumbnailId;
+ LLUUID mObjectId;
+ LLHandle<LLFloater> mFloaterHandle;
+ bool mSilent;
+ // Keep image reference to prevent deletion on timeout
+ LLPointer<LLViewerFetchedTexture> mTexturep;
+};
+
+void LLFloaterChangeItemThumbnail::assignAndValidateAsset(const LLUUID &asset_id, bool silent)
+{
+ LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::getFetchedTexture(asset_id);
+ if (texturep->isMissingAsset())
+ {
+ LL_WARNS() << "Attempted to assign missing asset " << asset_id << LL_ENDL;
+ if (!silent)
+ {
+ LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+ }
+ }
+ else if (texturep->getFullWidth() == 0)
+ {
+ if (silent)
+ {
+ mExpectingAssetId = LLUUID::null;
+ }
+ else
+ {
+ // don't warn user multiple times if some textures took their time
+ mExpectingAssetId = asset_id;
+ }
+ ImageLoadedData *data = new ImageLoadedData();
+ data->mObjectId = mItemId;
+ data->mThumbnailId = asset_id;
+ data->mFloaterHandle = getHandle();
+ data->mSilent = silent;
+ data->mTexturep = texturep;
+
+ texturep->setLoadedCallback(onImageDataLoaded,
+ MAX_DISCARD_LEVEL, // Don't need full image, just size data
+ FALSE,
+ FALSE,
+ (void*)data,
+ NULL,
+ FALSE);
+ }
+ else
+ {
+ if (validateAsset(asset_id))
+ {
+ setThumbnailId(asset_id);
+ }
+ else if (!silent)
+ {
+ LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+ }
+ }
+}
+bool LLFloaterChangeItemThumbnail::validateAsset(const LLUUID &asset_id)
+{
+ if (asset_id.isNull())
+ {
+ return false;
+ }
+
+ LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD);
+
+ if (!texturep)
+ {
+ return false;
+ }
+
+ if (texturep->isMissingAsset())
+ {
+ return false;
+ }
+
+ if (texturep->getFullWidth() != texturep->getFullHeight())
+ {
+ return false;
+ }
+
+ if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX
+ || texturep->getFullHeight() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX)
+ {
+ return false;
+ }
+
+ if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN
+ || texturep->getFullHeight() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN)
+ {
+ return false;
+ }
+ return true;
+}
+
+//static
+void LLFloaterChangeItemThumbnail::onImageDataLoaded(
+ BOOL success,
+ LLViewerFetchedTexture *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata)
+{
+ if (!userdata) return;
+
+ if (!final && success) return; //not done yet
+
+ ImageLoadedData* data = (ImageLoadedData*)userdata;
+
+ if (success)
+ {
+ // Update the item, set it even if floater is dead
+ if (validateAsset(data->mThumbnailId))
+ {
+ setThumbnailId(data->mThumbnailId, data->mObjectId);
+ }
+ else if (!data->mSilent)
+ {
+ // Should this only appear if floater is alive?
+ LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+ }
+ }
+
+ // Update floater
+ if (!data->mSilent && !data->mFloaterHandle.isDead())
+ {
+ LLFloaterChangeItemThumbnail* self = static_cast<LLFloaterChangeItemThumbnail*>(data->mFloaterHandle.get());
+ if (self && self->mExpectingAssetId == data->mThumbnailId)
+ {
+ self->mExpectingAssetId = LLUUID::null;
+ }
+ }
+
+ delete data;
+}
+
+//static
+void LLFloaterChangeItemThumbnail::onFullImageLoaded(
+ BOOL success,
+ LLViewerFetchedTexture* src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata)
+{
+ if (!userdata) return;
+
+ if (!final && success) return; //not done yet
+
+ ImageLoadedData* data = (ImageLoadedData*)userdata;
+
+ if (success)
+ {
+ if (src_vi->getFullWidth() != src_vi->getFullHeight()
+ || src_vi->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN)
+ {
+ if (!data->mSilent)
+ {
+ LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+ }
+ }
+ else if (src_vi->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX)
+ {
+ LLFloaterSimpleSnapshot::uploadThumbnail(src, data->mObjectId, LLUUID::null);
+ }
+ else
+ {
+ setThumbnailId(data->mThumbnailId, data->mObjectId);
+ }
+ }
+
+ delete data;
+}
+
+void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id)
+{
+ // show hourglass cursor when loading inventory window
+ getWindow()->setCursor(UI_CURSOR_WAIT);
+
+ LLFloater* floaterp = mPickerHandle.get();
+ // Show the dialog
+ if (floaterp)
+ {
+ floaterp->openFloater();
+ }
+ else
+ {
+ floaterp = new LLFloaterTexturePicker(
+ this,
+ thumbnail_id,
+ thumbnail_id,
+ thumbnail_id,
+ FALSE,
+ TRUE,
+ "SELECT PHOTO",
+ PERM_NONE,
+ PERM_NONE,
+ PERM_NONE,
+ FALSE,
+ NULL);
+
+ mPickerHandle = floaterp->getHandle();
+
+ LLFloaterTexturePicker* texture_floaterp = dynamic_cast<LLFloaterTexturePicker*>(floaterp);
+ if (texture_floaterp)
+ {
+ //texture_floaterp->setTextureSelectedCallback();
+ //texture_floaterp->setOnUpdateImageStatsCallback();
+ texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource, const LLUUID&, const LLUUID&)
+ {
+ if (op == LLTextureCtrl::TEXTURE_SELECT)
+ {
+ onTexturePickerCommit();
+ }
+ }
+ );
+
+ texture_floaterp->setLocalTextureEnabled(FALSE);
+ texture_floaterp->setBakeTextureEnabled(FALSE);
+ texture_floaterp->setCanApplyImmediately(false);
+ texture_floaterp->setCanApply(false, true, false /*Hide 'preview disabled'*/);
+ texture_floaterp->setMinDimentionsLimits(LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN);
+
+ addDependentFloater(texture_floaterp);
+ }
+
+ floaterp->openFloater();
+ }
+ floaterp->setFocus(TRUE);
+}
+
+void LLFloaterChangeItemThumbnail::onTexturePickerCommit()
+{
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mPickerHandle.get();
+
+ if (floaterp)
+ {
+ LLUUID asset_id = floaterp->getAssetID();
+
+ if (asset_id.isNull())
+ {
+ setThumbnailId(asset_id);
+ return;
+ }
+
+ LLInventoryObject* obj = getInventoryObject();
+ if (obj && obj->getThumbnailUUID() == asset_id)
+ {
+ // nothing to do
+ return;
+ }
+
+ LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD);
+ if (!texturep)
+ {
+ LL_WARNS() << "Image " << asset_id << " doesn't exist" << LL_ENDL;
+ return;
+ }
+
+ if (texturep->isMissingAsset())
+ {
+ LL_WARNS() << "Image " << asset_id << " is missing" << LL_ENDL;
+ return;
+ }
+
+ if (texturep->getFullWidth() != texturep->getFullHeight())
+ {
+ LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+ return;
+ }
+
+ if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN
+ && texturep->getFullWidth() > 0)
+ {
+ LLNotificationsUtil::add("ThumbnailDimentionsLimit");
+ return;
+ }
+
+ if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX
+ || texturep->getFullWidth() == 0)
+ {
+ if (texturep->isFullyLoaded()
+ && (texturep->getCachedRawImageLevel() == 0 || texturep->getRawImageLevel() == 0)
+ && (texturep->isCachedRawImageReady() || texturep->isRawImageValid()))
+ {
+ if (texturep->isRawImageValid())
+ {
+ LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getRawImage(), mItemId, mTaskId);
+ }
+ else
+ {
+ LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getCachedRawImage(), mItemId, mTaskId);
+ }
+ }
+ else
+ {
+ ImageLoadedData* data = new ImageLoadedData();
+ data->mObjectId = mItemId;
+ data->mThumbnailId = asset_id;
+ data->mFloaterHandle = getHandle();
+ data->mSilent = false;
+ data->mTexturep = texturep;
+
+ texturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
+ texturep->setMinDiscardLevel(0);
+ texturep->setLoadedCallback(onFullImageLoaded,
+ 0, // Need best quality
+ TRUE,
+ FALSE,
+ (void*)data,
+ NULL,
+ FALSE);
+ texturep->forceToSaveRawImage(0);
+ }
+ return;
+ }
+
+ setThumbnailId(asset_id);
+ }
+}
+
+
+void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID &new_thumbnail_id)
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if (!obj)
+ {
+ return;
+ }
+
+ if (mTaskId.notNull())
+ {
+ LL_ERRS() << "Not implemented yet" << LL_ENDL;
+ return;
+ }
+
+ setThumbnailId(new_thumbnail_id, mItemId, obj);
+}
+
+void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id)
+{
+ LLInventoryObject* obj = gInventory.getObject(object_id);
+ if (!obj)
+ {
+ return;
+ }
+
+ setThumbnailId(new_thumbnail_id, object_id, obj);
+}
+void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id, LLInventoryObject* obj)
+{
+ if (obj->getThumbnailUUID() != new_thumbnail_id)
+ {
+ LLSD updates;
+ if (new_thumbnail_id.notNull())
+ {
+ // At the moment server expects id as a string
+ updates["thumbnail"] = LLSD().with("asset_id", new_thumbnail_id.asString());
+ }
+ else
+ {
+ // No thumbnail isntead of 'null id thumbnail'
+ updates["thumbnail"] = LLSD();
+ }
+ LLViewerInventoryCategory* view_folder = dynamic_cast<LLViewerInventoryCategory*>(obj);
+ if (view_folder)
+ {
+ update_inventory_category(object_id, updates, NULL);
+ }
+ LLViewerInventoryItem* view_item = dynamic_cast<LLViewerInventoryItem*>(obj);
+ if (view_item)
+ {
+ update_inventory_item(object_id, updates, NULL);
+ }
+ }
+}
+
+void LLFloaterChangeItemThumbnail::onButtonMouseEnter(LLUICtrl* button, const LLSD& param, EToolTipState state)
+{
+ mTooltipState = state;
+
+ std::string tooltip_text;
+ std::string tooltip_name = "tooltip_" + button->getName();
+ if (hasString(tooltip_name))
+ {
+ tooltip_text = getString(tooltip_name);
+ }
+
+ mToolTipTextBox->setValue(tooltip_text);
+}
+
+void LLFloaterChangeItemThumbnail::onButtonMouseLeave(LLUICtrl* button, const LLSD& param, EToolTipState state)
+{
+ if (mTooltipState == state)
+ {
+ mTooltipState = TOOLTIP_NONE;
+ LLSD tooltip_text;
+ mToolTipTextBox->setValue(tooltip_text);
+ }
+}
+
diff --git a/indra/newview/llfloaterchangeitemthumbnail.h b/indra/newview/llfloaterchangeitemthumbnail.h
new file mode 100644
index 0000000000..a91e9b8ee9
--- /dev/null
+++ b/indra/newview/llfloaterchangeitemthumbnail.h
@@ -0,0 +1,139 @@
+/**
+ * @file llfloaterchangeitemthumbnail.h
+ * @brief LLFloaterChangeItemThumbnail class definition
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERCHANGEITEMTHUMBNAIL_H
+#define LL_LLFLOATERCHANGEITEMTHUMBNAIL_H
+
+#include "llfloater.h"
+#include "llinventoryobserver.h"
+#include "llvoinventorylistener.h"
+
+class LLButton;
+class LLIconCtrl;
+class LLTextBox;
+class LLThumbnailCtrl;
+class LLUICtrl;
+class LLViewerInventoryItem;
+class LLViewerFetchedTexture;
+
+class LLFloaterChangeItemThumbnail : public LLFloater, public LLInventoryObserver, public LLVOInventoryListener
+{
+public:
+ LLFloaterChangeItemThumbnail(const LLSD& key);
+ ~LLFloaterChangeItemThumbnail();
+
+ BOOL postBuild() override;
+ void onOpen(const LLSD& key) override;
+ void onFocusReceived() override;
+ void onMouseEnter(S32 x, S32 y, MASK mask) override;
+
+ BOOL handleDragAndDrop(
+ S32 x,
+ S32 y,
+ MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void *cargo_data,
+ EAcceptance *accept,
+ std::string& tooltip_msg) override;
+
+ void changed(U32 mask) override;
+ void inventoryChanged(LLViewerObject* object,
+ LLInventoryObject::object_list_t* inventory,
+ S32 serial_num,
+ void* user_data) override;
+
+ static bool validateAsset(const LLUUID &asset_id);
+
+private:
+
+ LLInventoryObject* getInventoryObject();
+ void refreshFromInventory();
+ void refreshFromObject(LLInventoryObject* obj);
+
+ static void onUploadLocal(void*);
+ static void onUploadSnapshot(void*);
+ static void onUseTexture(void*);
+ static void onCopyToClipboard(void*);
+ static void onPasteFromClipboard(void*);
+ static void onRemove(void*);
+ static void onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle<LLFloater> handle);
+
+ void assignAndValidateAsset(const LLUUID &asset_id, bool silent = false);
+ static void onImageDataLoaded(BOOL success,
+ LLViewerFetchedTexture *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata);
+ static void onFullImageLoaded(BOOL success,
+ LLViewerFetchedTexture* src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata);
+
+ void showTexturePicker(const LLUUID &thumbnail_id);
+ void onTexturePickerCommit();
+
+ void setThumbnailId(const LLUUID &new_thumbnail_id);
+ static void setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id);
+ static void setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id, LLInventoryObject* obj);
+
+ enum EToolTipState
+ {
+ TOOLTIP_NONE,
+ TOOLTIP_UPLOAD_LOCAL,
+ TOOLTIP_UPLOAD_SNAPSHOT,
+ TOOLTIP_USE_TEXTURE,
+ TOOLTIP_COPY_TO_CLIPBOARD,
+ TOOLTIP_COPY_FROM_CLIPBOARD,
+ TOOLTIP_REMOVE,
+ };
+
+ void onButtonMouseEnter(LLUICtrl* button, const LLSD& param, EToolTipState state);
+ void onButtonMouseLeave(LLUICtrl* button, const LLSD& param, EToolTipState state);
+
+ bool mObserverInitialized;
+ EToolTipState mTooltipState;
+ LLUUID mItemId;
+ LLUUID mTaskId;
+ LLUUID mExpectingAssetId;
+
+ LLIconCtrl *mItemTypeIcon;
+ LLUICtrl *mItemNameText;
+ LLThumbnailCtrl *mThumbnailCtrl;
+ LLTextBox *mToolTipTextBox;
+ LLButton *mCopyToClipboardBtn;
+ LLButton *mPasteFromClipboardBtn;
+ LLButton *mRemoveImageBtn;
+
+ LLHandle<LLFloater> mPickerHandle;
+ LLHandle<LLFloater> mSnapshotHandle;
+};
+#endif // LL_LLFLOATERCHANGEITEMTHUMBNAIL_H
diff --git a/indra/newview/llfloatereditenvironmentbase.cpp b/indra/newview/llfloatereditenvironmentbase.cpp
index 2850951668..cd24d79b7f 100644
--- a/indra/newview/llfloatereditenvironmentbase.cpp
+++ b/indra/newview/llfloatereditenvironmentbase.cpp
@@ -260,7 +260,7 @@ void LLFloaterEditEnvironmentBase::onSaveAsCommit(const LLSD& notification, cons
}
else if (mInventoryItem)
{
- const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
LLUUID parent_id = mInventoryItem->getParentUUID();
if (marketplacelistings_id == parent_id || gInventory.isObjectDescendentOf(mInventoryItem->getUUID(), gInventory.getLibraryRootFolderID()))
{
diff --git a/indra/newview/llfloaterforgetuser.cpp b/indra/newview/llfloaterforgetuser.cpp
index 97b022699f..f576ce7a76 100644
--- a/indra/newview/llfloaterforgetuser.cpp
+++ b/indra/newview/llfloaterforgetuser.cpp
@@ -164,6 +164,12 @@ bool LLFloaterForgetUser::onConfirmLogout(const LLSD& notification, const LLSD&
if (option == 0)
{
// Remove creds
+ std::string grid_id = LLGridManager::getInstance()->getGridId(grid);
+ if (grid_id.empty())
+ {
+ grid_id = grid;
+ }
+ gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid_id, LLStartUp::getUserId()); // doesn't write
gSecAPIHandler->removeFromCredentialMap("login_list", grid, LLStartUp::getUserId());
LLPointer<LLCredential> cred = gSecAPIHandler->loadCredential(grid);
@@ -228,7 +234,13 @@ void LLFloaterForgetUser::processForgetUser()
void LLFloaterForgetUser::forgetUser(const std::string &userid, const std::string &fav_id, const std::string &grid, bool delete_data)
{
// Remove creds
- gSecAPIHandler->removeFromCredentialMap("login_list", grid, userid);
+ std::string grid_id = LLGridManager::getInstance()->getGridId(grid);
+ if (grid_id.empty())
+ {
+ grid_id = grid;
+ }
+ gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid_id, userid); // doesn't write
+ gSecAPIHandler->removeFromCredentialMap("login_list", grid, userid); // write operation
LLPointer<LLCredential> cred = gSecAPIHandler->loadCredential(grid);
if (cred.notNull() && cred->userID() == userid)
diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp
index d17889bed1..f29046c513 100644
--- a/indra/newview/llfloatergesture.cpp
+++ b/indra/newview/llfloatergesture.cpp
@@ -211,7 +211,7 @@ BOOL LLFloaterGesture::postBuild()
getChildView("play_btn")->setVisible( true);
getChildView("stop_btn")->setVisible( false);
setDefaultBtn("play_btn");
- mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE, false);
+ mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE);
uuid_vec_t folders;
folders.push_back(mGestureFolderID);
diff --git a/indra/newview/llfloaterinventorysettings.cpp b/indra/newview/llfloaterinventorysettings.cpp
new file mode 100644
index 0000000000..29d6e90a33
--- /dev/null
+++ b/indra/newview/llfloaterinventorysettings.cpp
@@ -0,0 +1,44 @@
+/**
+ * @file llfloaterinventorysettings.cpp
+ * @brief LLFloaterInventorySettings class implementation
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterinventorysettings.h"
+
+LLFloaterInventorySettings::LLFloaterInventorySettings(const LLSD& key)
+ : LLFloater(key)
+{
+}
+
+LLFloaterInventorySettings::~LLFloaterInventorySettings()
+{}
+
+BOOL LLFloaterInventorySettings::postBuild()
+{
+ getChild<LLButton>("ok_btn")->setCommitCallback(boost::bind(&LLFloater::closeFloater, this, false));
+ return TRUE;
+}
+
diff --git a/indra/newview/llfloaterinventorysettings.h b/indra/newview/llfloaterinventorysettings.h
new file mode 100644
index 0000000000..50304276c7
--- /dev/null
+++ b/indra/newview/llfloaterinventorysettings.h
@@ -0,0 +1,45 @@
+/**
+ * @file llfloaterinventorysettings.h
+ * @brief LLFloaterInventorySettings class definition
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERINVENTORYSETTINGS_H
+#define LL_LLFLOATERINVENTORYSETTINGS_H
+
+#include "llfloater.h"
+
+class LLFloaterInventorySettings
+ : public LLFloater
+{
+ friend class LLFloaterReg;
+
+public:
+ virtual BOOL postBuild();
+
+private:
+ LLFloaterInventorySettings(const LLSD& key);
+ ~LLFloaterInventorySettings();
+};
+
+#endif
diff --git a/indra/newview/llfloaterlinkreplace.cpp b/indra/newview/llfloaterlinkreplace.cpp
index 8ee7a72055..b42c49c607 100644
--- a/indra/newview/llfloaterlinkreplace.cpp
+++ b/indra/newview/llfloaterlinkreplace.cpp
@@ -335,8 +335,8 @@ BOOL LLFloaterLinkReplace::tick()
void LLFloaterLinkReplace::processBatch(LLInventoryModel::item_array_t items)
{
const LLViewerInventoryItem* target_item = gInventory.getItem(mTargetUUID);
- const LLUUID cof_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
- const LLUUID outfit_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
+ const LLUUID cof_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ const LLUUID outfit_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it)
{
diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp
index e755e9924c..71b3b16809 100644
--- a/indra/newview/llfloatermarketplacelistings.cpp
+++ b/indra/newview/llfloatermarketplacelistings.cpp
@@ -41,8 +41,11 @@
#include "llnotificationmanager.h"
#include "llnotificationsutil.h"
#include "llsidepaneliteminfo.h"
+#include "llsidepaneltaskinfo.h"
+#include "lltabcontainer.h"
#include "lltextbox.h"
#include "lltrans.h"
+#include "llviewerwindow.h"
///----------------------------------------------------------------------------
/// LLPanelMarketplaceListings
@@ -227,18 +230,31 @@ void LLPanelMarketplaceListings::onTabChange()
void LLPanelMarketplaceListings::onAddButtonClicked()
{
- // Find active panel
- LLInventoryPanel* panel = (LLInventoryPanel*)getChild<LLTabContainer>("marketplace_filter_tabs")->getCurrentPanel();
- if (panel)
- {
- LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
- llassert(marketplacelistings_id.notNull());
- LLFolderType::EType preferred_type = LLFolderType::lookup("category");
- LLUUID category = gInventory.createNewCategory(marketplacelistings_id, preferred_type, LLStringUtil::null);
- gInventory.notifyObservers();
- panel->setSelectionByID(category, TRUE);
- panel->getRootFolder()->setNeedsAutoRename(TRUE);
+ LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ llassert(marketplacelistings_id.notNull());
+ LLFolderType::EType preferred_type = LLFolderType::lookup("category");
+ LLHandle<LLPanel> handle = getHandle();
+ gInventory.createNewCategory(
+ marketplacelistings_id,
+ preferred_type,
+ LLStringUtil::null,
+ [handle](const LLUUID &new_cat_id)
+ {
+ // Find active panel
+ LLPanel *marketplace_panel = handle.get();
+ if (!marketplace_panel)
+ {
+ return;
+ }
+ LLInventoryPanel* panel = (LLInventoryPanel*)marketplace_panel->getChild<LLTabContainer>("marketplace_filter_tabs")->getCurrentPanel();
+ if (panel)
+ {
+ gInventory.notifyObservers();
+ panel->setSelectionByID(new_cat_id, TRUE);
+ panel->getRootFolder()->setNeedsAutoRename(TRUE);
+ }
}
+ );
}
void LLPanelMarketplaceListings::onAuditButtonClicked()
@@ -359,6 +375,7 @@ LLFloaterMarketplaceListings::LLFloaterMarketplaceListings(const LLSD& key)
, mInventoryTitle(NULL)
, mPanelListings(NULL)
, mPanelListingsSet(false)
+, mRootFolderCreating(false)
{
}
@@ -431,7 +448,7 @@ void LLFloaterMarketplaceListings::fetchContents()
{
LLMarketplaceData::instance().setDataFetchedSignal(boost::bind(&LLFloaterMarketplaceListings::updateView, this));
LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_LOADING);
- LLInventoryModelBackgroundFetch::instance().start(mRootFolderId);
+ LLInventoryModelBackgroundFetch::instance().start(mRootFolderId, true);
LLMarketplaceData::instance().getSLMListings();
}
}
@@ -444,15 +461,50 @@ void LLFloaterMarketplaceListings::setRootFolder()
// If we are *not* a merchant or we have no market place connection established yet, do nothing
return;
}
+ if (!gInventory.isInventoryUsable())
+ {
+ return;
+ }
+ LLFolderType::EType preferred_type = LLFolderType::FT_MARKETPLACE_LISTINGS;
// We are a merchant. Get the Marketplace listings folder, create it if needs be.
- LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true);
- if (marketplacelistings_id.isNull())
- {
- // We should never get there unless the inventory fails badly
- LL_ERRS("SLM") << "Inventory problem: failure to create the marketplace listings folder for a merchant!" << LL_ENDL;
- return;
- }
+ LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(preferred_type);
+
+ if (marketplacelistings_id.isNull())
+ {
+ if (!mRootFolderCreating)
+ {
+ mRootFolderCreating = true;
+ gInventory.createNewCategory(
+ gInventory.getRootFolderID(),
+ preferred_type,
+ LLStringUtil::null,
+ [](const LLUUID &new_cat_id)
+ {
+ LLFloaterMarketplaceListings* marketplace = LLFloaterReg::findTypedInstance<LLFloaterMarketplaceListings>("marketplace_listings");
+ if (marketplace)
+ {
+ if (new_cat_id.notNull())
+ {
+ // will call setRootFolder again
+ marketplace->updateView();
+ }
+ // don't update in case of failure, createNewCategory can return
+ // immediately if cap is missing and will cause a loop
+ else
+ {
+ // unblock
+ marketplace->mRootFolderCreating = false;
+ LL_WARNS("SLM") << "Inventory warning: Failed to create marketplace listings folder for a merchant" << LL_ENDL;
+ }
+ }
+ }
+ );
+ }
+ return;
+ }
+
+ mRootFolderCreating = false;
// No longer need to observe new category creation
if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver))
@@ -540,6 +592,11 @@ void LLFloaterMarketplaceListings::updateView()
{
setRootFolder();
}
+ if (mRootFolderCreating)
+ {
+ // waiting for callback
+ return;
+ }
// Update the bottom initializing status and progress dial if we are initializing or if we're a merchant and still loading
if ((mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) || (is_merchant && (data_fetched <= MarketplaceFetchCodes::MARKET_FETCH_LOADING)) )
@@ -843,14 +900,17 @@ void LLFloaterMarketplaceValidation::onOpen(const LLSD& key)
LLUUID cat_id(key.asUUID());
if (cat_id.isNull())
{
- cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
}
// Validates the folder
if (cat_id.notNull())
{
- LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
- validate_marketplacelistings(cat, boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3), false);
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
+ cat_id,
+ NULL,
+ boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3),
+ false);
}
// Handle the listing folder being processed
@@ -954,18 +1014,44 @@ LLFloaterItemProperties::~LLFloaterItemProperties()
BOOL LLFloaterItemProperties::postBuild()
{
- // On the standalone properties floater, we have no need for a back button...
- LLSidepanelItemInfo* panel = getChild<LLSidepanelItemInfo>("item_panel");
- LLButton* back_btn = panel->getChild<LLButton>("back_btn");
- back_btn->setVisible(FALSE);
-
return LLFloater::postBuild();
}
void LLFloaterItemProperties::onOpen(const LLSD& key)
{
// Tell the panel which item it needs to visualize
- LLSidepanelItemInfo* panel = getChild<LLSidepanelItemInfo>("item_panel");
- panel->setItemID(key["id"].asUUID());
+ LLPanel* panel = findChild<LLPanel>("sidepanel");
+
+ LLSidepanelItemInfo* item_panel = dynamic_cast<LLSidepanelItemInfo*>(panel);
+ if (item_panel)
+ {
+ item_panel->setItemID(key["id"].asUUID());
+ if (key.has("object"))
+ {
+ item_panel->setObjectID(key["object"].asUUID());
+ }
+ item_panel->setParentFloater(this);
+ }
+
+ LLSidepanelTaskInfo* task_panel = dynamic_cast<LLSidepanelTaskInfo*>(panel);
+ if (task_panel)
+ {
+ task_panel->setObjectSelection(LLSelectMgr::getInstance()->getSelection());
+ }
}
+LLMultiItemProperties::LLMultiItemProperties(const LLSD& key)
+ : LLMultiFloater(LLSD())
+{
+ // start with a small rect in the top-left corner ; will get resized
+ LLRect rect;
+ rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 350, 350);
+ setRect(rect);
+ LLFloater* last_floater = LLFloaterReg::getLastFloaterInGroup(key.asString());
+ if (last_floater)
+ {
+ stackWith(*last_floater);
+ }
+ setTitle(LLTrans::getString("MultiPropertiesTitle"));
+ buildTabContainer();
+}
diff --git a/indra/newview/llfloatermarketplacelistings.h b/indra/newview/llfloatermarketplacelistings.h
index ffc098e28a..085e517a9d 100644
--- a/indra/newview/llfloatermarketplacelistings.h
+++ b/indra/newview/llfloatermarketplacelistings.h
@@ -33,6 +33,7 @@
#include "llinventorypanel.h"
#include "llnotificationptr.h"
#include "llmodaldialog.h"
+#include "llmultifloater.h"
#include "lltexteditor.h"
class LLInventoryCategoriesObserver;
@@ -139,6 +140,7 @@ private:
LLTextBox * mInventoryTitle;
LLUUID mRootFolderId;
+ bool mRootFolderCreating;
LLPanelMarketplaceListings * mPanelListings;
bool mPanelListingsSet;
};
@@ -223,4 +225,10 @@ public:
private:
};
+class LLMultiItemProperties : public LLMultiFloater
+{
+public:
+ LLMultiItemProperties(const LLSD& key);
+};
+
#endif // LL_LLFLOATERMARKETPLACELISTINGS_H
diff --git a/indra/newview/llfloaternewfeaturenotification.cpp b/indra/newview/llfloaternewfeaturenotification.cpp
new file mode 100644
index 0000000000..3a2035b9b9
--- /dev/null
+++ b/indra/newview/llfloaternewfeaturenotification.cpp
@@ -0,0 +1,76 @@
+/**
+ * @file llfloaternewfeaturenotification.cpp
+ * @brief LLFloaterNewFeatureNotification class implementation
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaternewfeaturenotification.h"
+
+
+LLFloaterNewFeatureNotification::LLFloaterNewFeatureNotification(const LLSD& key)
+ : LLFloater(key)
+{
+}
+
+LLFloaterNewFeatureNotification::~LLFloaterNewFeatureNotification()
+{
+}
+
+BOOL LLFloaterNewFeatureNotification::postBuild()
+{
+ setCanDrag(FALSE);
+ getChild<LLButton>("close_btn")->setCommitCallback(boost::bind(&LLFloaterNewFeatureNotification::onCloseBtn, this));
+
+ const std::string title_txt = "title_txt";
+ const std::string dsc_txt = "description_txt";
+ std::string feature = "_" + getKey().asString();
+
+ getChild<LLUICtrl>(title_txt)->setValue(getString(title_txt + feature));
+ getChild<LLUICtrl>(dsc_txt)->setValue(getString(dsc_txt + feature));
+
+ return TRUE;
+}
+
+void LLFloaterNewFeatureNotification::onOpen(const LLSD& key)
+{
+ centerOnScreen();
+}
+
+void LLFloaterNewFeatureNotification::onCloseBtn()
+{
+ closeFloater();
+}
+
+void LLFloaterNewFeatureNotification::centerOnScreen()
+{
+ LLVector2 window_size = LLUI::getInstance()->getWindowSize();
+ centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY])));
+ LLFloaterView* parent = dynamic_cast<LLFloaterView*>(getParent());
+ if (parent)
+ {
+ parent->bringToFront(this);
+ }
+}
+
diff --git a/indra/newview/llfloaternewfeaturenotification.h b/indra/newview/llfloaternewfeaturenotification.h
new file mode 100644
index 0000000000..95501451dc
--- /dev/null
+++ b/indra/newview/llfloaternewfeaturenotification.h
@@ -0,0 +1,49 @@
+/**
+ * @file llfloaternewfeaturenotification.h
+ * @brief LLFloaterNewFeatureNotification class definition
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_FLOATER_NEW_FEATURE_NOTOFICATION_H
+#define LL_FLOATER_NEW_FEATURE_NOTOFICATION_H
+
+#include "llfloater.h"
+
+class LLFloaterNewFeatureNotification:
+ public LLFloater
+{
+ friend class LLFloaterReg;
+public:
+ BOOL postBuild() override;
+ void onOpen(const LLSD& key) override;
+
+private:
+ LLFloaterNewFeatureNotification(const LLSD& key);
+ /*virtual*/ ~LLFloaterNewFeatureNotification();
+
+ void centerOnScreen();
+
+ void onCloseBtn();
+};
+
+#endif
diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp
index a682064dad..d3ab22f792 100644
--- a/indra/newview/llfloateropenobject.cpp
+++ b/indra/newview/llfloateropenobject.cpp
@@ -164,34 +164,12 @@ void LLFloaterOpenObject::moveToInventory(bool wear, bool replace)
}
inventory_func_type func = boost::bind(LLFloaterOpenObject::callbackCreateInventoryCategory,_1,object_id,wear,replace);
- LLUUID category_id = gInventory.createNewCategory(parent_category_id,
- LLFolderType::FT_NONE,
- name,
- func);
-
- //If we get a null category ID, we are using a capability in createNewCategory and we will
- //handle the following in the callbackCreateInventoryCategory routine.
- if ( category_id.notNull() )
- {
- LLCatAndWear* data = new LLCatAndWear;
- data->mCatID = category_id;
- data->mWear = wear;
- data->mFolderResponded = false;
- data->mReplace = replace;
-
- // Copy and/or move the items into the newly created folder.
- // Ignore any "you're going to break this item" messages.
- BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE,
- callbackMoveInventory,
- (void*)data);
- if (!success)
- {
- delete data;
- data = NULL;
-
- LLNotificationsUtil::add("OpenObjectCannotCopy");
- }
- }
+ // D567 copy thumbnail info
+ gInventory.createNewCategory(
+ parent_category_id,
+ LLFolderType::FT_NONE,
+ name,
+ func);
}
// static
@@ -206,9 +184,14 @@ void LLFloaterOpenObject::callbackCreateInventoryCategory(const LLUUID& category
// Copy and/or move the items into the newly created folder.
// Ignore any "you're going to break this item" messages.
- BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE,
- callbackMoveInventory,
- (void*)wear_data);
+ BOOL success = move_inv_category_world_to_agent(object_id,
+ category_id,
+ TRUE,
+ [](S32 result, void* data, const LLMoveInv*)
+ {
+ callbackMoveInventory(result, data);
+ },
+ (void*)wear_data);
if (!success)
{
delete wear_data;
diff --git a/indra/newview/llfloateroutfitphotopreview.cpp b/indra/newview/llfloateroutfitphotopreview.cpp
deleted file mode 100644
index ade258aef7..0000000000
--- a/indra/newview/llfloateroutfitphotopreview.cpp
+++ /dev/null
@@ -1,288 +0,0 @@
-/**
- * @file llfloateroutfitphotopreview.cpp
- * @brief LLFloaterOutfitPhotoPreview class implementation
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llwindow.h"
-
-#include "llfloateroutfitphotopreview.h"
-
-#include "llagent.h"
-#include "llappearancemgr.h"
-#include "llbutton.h"
-#include "llcombobox.h"
-#include "llfilepicker.h"
-#include "llfloaterreg.h"
-#include "llimagetga.h"
-#include "llimagepng.h"
-#include "llinventory.h"
-#include "llinventorymodel.h"
-#include "llnotificationsutil.h"
-#include "llresmgr.h"
-#include "lltrans.h"
-#include "lltextbox.h"
-#include "lltextureview.h"
-#include "llui.h"
-#include "llviewerinventory.h"
-#include "llviewertexture.h"
-#include "llviewertexturelist.h"
-#include "lluictrlfactory.h"
-#include "llviewerwindow.h"
-#include "lllineeditor.h"
-
-const S32 MAX_OUTFIT_PHOTO_WIDTH = 256;
-const S32 MAX_OUTFIT_PHOTO_HEIGHT = 256;
-
-const S32 CLIENT_RECT_VPAD = 4;
-
-LLFloaterOutfitPhotoPreview::LLFloaterOutfitPhotoPreview(const LLSD& key)
- : LLPreview(key),
- mUpdateDimensions(TRUE),
- mImage(NULL),
- mOutfitID(LLUUID()),
- mImageOldBoostLevel(LLGLTexture::BOOST_NONE),
- mExceedLimits(FALSE)
-{
- updateImageID();
-}
-
-LLFloaterOutfitPhotoPreview::~LLFloaterOutfitPhotoPreview()
-{
- LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ;
-
- if (mImage.notNull())
- {
- mImage->setBoostLevel(mImageOldBoostLevel);
- mImage = NULL;
- }
-}
-
-// virtual
-BOOL LLFloaterOutfitPhotoPreview::postBuild()
-{
- getChild<LLButton>("ok_btn")->setClickedCallback(boost::bind(&LLFloaterOutfitPhotoPreview::onOkBtn, this));
- getChild<LLButton>("cancel_btn")->setClickedCallback(boost::bind(&LLFloaterOutfitPhotoPreview::onCancelBtn, this));
-
- return LLPreview::postBuild();
-}
-
-void LLFloaterOutfitPhotoPreview::draw()
-{
- updateDimensions();
-
- LLPreview::draw();
-
- if (!isMinimized())
- {
- LLGLSUIDefault gls_ui;
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- const LLRect& border = mClientRect;
- LLRect interior = mClientRect;
- interior.stretch( -PREVIEW_BORDER_WIDTH );
-
- // ...border
- gl_rect_2d( border, LLColor4(0.f, 0.f, 0.f, 1.f));
- gl_rect_2d_checkerboard( interior );
-
- if ( mImage.notNull() )
- {
- // Draw the texture
- gGL.diffuseColor3f( 1.f, 1.f, 1.f );
- gl_draw_scaled_image(interior.mLeft,
- interior.mBottom,
- interior.getWidth(),
- interior.getHeight(),
- mImage);
-
- // Pump the texture priority
- F32 pixel_area = (F32)(interior.getWidth() * interior.getHeight() );
- mImage->addTextureStats( pixel_area );
-
- S32 int_width = interior.getWidth();
- S32 int_height = interior.getHeight();
- mImage->setKnownDrawSize(int_width, int_height);
- }
- }
-
-}
-
-// virtual
-void LLFloaterOutfitPhotoPreview::reshape(S32 width, S32 height, BOOL called_from_parent)
-{
- LLPreview::reshape(width, height, called_from_parent);
-
- LLRect dim_rect(getChildView("dimensions")->getRect());
-
- S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) + PREVIEW_RESIZE_HANDLE_SIZE;
-
- S32 info_height = dim_rect.mTop + CLIENT_RECT_VPAD;
-
- LLRect client_rect(horiz_pad, getRect().getHeight(), getRect().getWidth() - horiz_pad, 0);
- client_rect.mTop -= (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD);
- client_rect.mBottom += PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height ;
-
- S32 client_width = client_rect.getWidth();
- S32 client_height = client_width;
-
- if(client_height > client_rect.getHeight())
- {
- client_height = client_rect.getHeight();
- client_width = client_height;
- }
- mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() + (client_height / 2), client_width, client_height);
-
-}
-
-
-void LLFloaterOutfitPhotoPreview::updateDimensions()
-{
- if (!mImage)
- {
- return;
- }
- if ((mImage->getFullWidth() * mImage->getFullHeight()) == 0)
- {
- return;
- }
-
- if (mAssetStatus != PREVIEW_ASSET_LOADED)
- {
- mAssetStatus = PREVIEW_ASSET_LOADED;
- mUpdateDimensions = TRUE;
- }
-
- getChild<LLUICtrl>("dimensions")->setTextArg("[WIDTH]", llformat("%d", mImage->getFullWidth()));
- getChild<LLUICtrl>("dimensions")->setTextArg("[HEIGHT]", llformat("%d", mImage->getFullHeight()));
-
- if ((mImage->getFullWidth() <= MAX_OUTFIT_PHOTO_WIDTH) && (mImage->getFullHeight() <= MAX_OUTFIT_PHOTO_HEIGHT))
- {
- getChild<LLButton>("ok_btn")->setEnabled(TRUE);
- mExceedLimits = FALSE;
- }
- else
- {
- mExceedLimits = TRUE;
- LLStringUtil::format_map_t args;
- args["MAX_WIDTH"] = llformat("%d", MAX_OUTFIT_PHOTO_WIDTH);
- args["MAX_HEIGHT"] = llformat("%d", MAX_OUTFIT_PHOTO_HEIGHT);
- std::string label = getString("exceed_limits", args);
- getChild<LLUICtrl>("notification")->setValue(label);
- getChild<LLUICtrl>("notification")->setColor(LLColor4::yellow);
- getChild<LLButton>("ok_btn")->setEnabled(FALSE);
- }
-
- if (mUpdateDimensions)
- {
- mUpdateDimensions = FALSE;
-
- reshape(getRect().getWidth(), getRect().getHeight());
- gFloaterView->adjustToFitScreen(this, FALSE);
- }
-}
-
-void LLFloaterOutfitPhotoPreview::loadAsset()
-{
- if (mImage.notNull())
- {
- mImage->setBoostLevel(mImageOldBoostLevel);
- }
- mImage = LLViewerTextureManager::getFetchedTexture(mImageID, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- mImageOldBoostLevel = mImage->getBoostLevel();
- mImage->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
- mImage->forceToSaveRawImage(0) ;
- mAssetStatus = PREVIEW_ASSET_LOADING;
- mUpdateDimensions = TRUE;
- updateDimensions();
-}
-
-LLPreview::EAssetStatus LLFloaterOutfitPhotoPreview::getAssetStatus()
-{
- if (mImage.notNull() && (mImage->getFullWidth() * mImage->getFullHeight() > 0))
- {
- mAssetStatus = PREVIEW_ASSET_LOADED;
- }
- return mAssetStatus;
-}
-
-void LLFloaterOutfitPhotoPreview::updateImageID()
-{
- const LLViewerInventoryItem *item = static_cast<const LLViewerInventoryItem*>(getItem());
- if(item)
- {
- mImageID = item->getAssetUUID();
- }
- else
- {
- mImageID = mItemUUID;
- }
-
-}
-
-/* virtual */
-void LLFloaterOutfitPhotoPreview::setObjectID(const LLUUID& object_id)
-{
- mObjectUUID = object_id;
-
- const LLUUID old_image_id = mImageID;
-
- updateImageID();
- if (mImageID != old_image_id)
- {
- mAssetStatus = PREVIEW_ASSET_UNLOADED;
- loadAsset();
- }
- refreshFromItem();
-}
-
-void LLFloaterOutfitPhotoPreview::setOutfitID(const LLUUID& outfit_id)
-{
- mOutfitID = outfit_id;
- LLViewerInventoryCategory* outfit_folder = gInventory.getCategory(mOutfitID);
- if(outfit_folder && !mExceedLimits)
- {
- getChild<LLUICtrl>("notification")->setValue( getString("photo_confirmation"));
- getChild<LLUICtrl>("notification")->setTextArg("[OUTFIT]", outfit_folder->getName());
- getChild<LLUICtrl>("notification")->setColor(LLColor4::white);
- }
-
-}
-
-void LLFloaterOutfitPhotoPreview::onOkBtn()
-{
- if(mOutfitID.notNull() && getItem())
- {
- LLAppearanceMgr::instance().removeOutfitPhoto(mOutfitID);
- LLPointer<LLInventoryCallback> cb = NULL;
- link_inventory_object(mOutfitID, LLConstPointer<LLInventoryObject>(getItem()), cb);
- }
- closeFloater();
-}
-
-void LLFloaterOutfitPhotoPreview::onCancelBtn()
-{
- closeFloater();
-}
diff --git a/indra/newview/llfloateroutfitphotopreview.h b/indra/newview/llfloateroutfitphotopreview.h
deleted file mode 100644
index a1e7b58abe..0000000000
--- a/indra/newview/llfloateroutfitphotopreview.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * @file llfloateroutfitphotopreview.h
- * @brief LLFloaterOutfitPhotoPreview class definition
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLFLOATEROUTFITPHOTOPREVIEW_H
-#define LL_LLFLOATEROUTFITPHOTOPREVIEW_H
-
-#include "llpreview.h"
-#include "llbutton.h"
-#include "llframetimer.h"
-#include "llviewertexture.h"
-
-class LLComboBox;
-class LLImageRaw;
-
-class LLFloaterOutfitPhotoPreview : public LLPreview
-{
-public:
- LLFloaterOutfitPhotoPreview(const LLSD& key);
- ~LLFloaterOutfitPhotoPreview();
-
- virtual void draw();
-
- virtual void loadAsset();
- virtual EAssetStatus getAssetStatus();
-
- virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
-
- /*virtual*/ void setObjectID(const LLUUID& object_id);
-
- void setOutfitID(const LLUUID& outfit_id);
- void onOkBtn();
- void onCancelBtn();
-
-protected:
- void init();
- /* virtual */ BOOL postBuild();
-
-private:
- void updateImageID(); // set what image is being uploaded.
- void updateDimensions();
- LLUUID mImageID;
- LLUUID mOutfitID;
- LLPointer<LLViewerFetchedTexture> mImage;
- S32 mImageOldBoostLevel;
-
- // This is stored off in a member variable, because the save-as
- // button and drag and drop functionality need to know.
- BOOL mUpdateDimensions;
-
- BOOL mExceedLimits;
-
- LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ;
-};
-#endif // LL_LLFLOATEROUTFITPHOTOPREVIEW_H
diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp
deleted file mode 100644
index 64ad40f419..0000000000
--- a/indra/newview/llfloaterproperties.cpp
+++ /dev/null
@@ -1,891 +0,0 @@
-/**
- * @file llfloaterproperties.cpp
- * @brief A floater which shows an inventory item's properties.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-#include "llfloaterproperties.h"
-
-#include <algorithm>
-#include <functional>
-#include "llcachename.h"
-#include "llavatarnamecache.h"
-#include "lldbstrings.h"
-#include "llfloaterreg.h"
-
-#include "llagent.h"
-#include "llbutton.h"
-#include "llcheckboxctrl.h"
-#include "llcombobox.h"
-#include "llavataractions.h"
-#include "llinventorydefines.h"
-#include "llinventoryobserver.h"
-#include "llinventorymodel.h"
-#include "lllineeditor.h"
-//#include "llspinctrl.h"
-#include "llradiogroup.h"
-#include "llresmgr.h"
-#include "roles_constants.h"
-#include "llselectmgr.h"
-#include "lltextbox.h"
-#include "lltrans.h"
-#include "lluiconstants.h"
-#include "llviewerinventory.h"
-#include "llviewerobjectlist.h"
-#include "llviewerregion.h"
-#include "llviewercontrol.h"
-#include "llviewerwindow.h"
-#include "llgroupactions.h"
-
-#include "lluictrlfactory.h"
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLPropertiesObserver
-//
-// helper class to watch the inventory.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-// Ugh. This can't be a singleton because it needs to remove itself
-// from the inventory observer list when destroyed, which could
-// happen after gInventory has already been destroyed if a singleton.
-// Instead, do our own ref counting and create / destroy it as needed
-class LLPropertiesObserver : public LLInventoryObserver
-{
-public:
- LLPropertiesObserver(LLFloaterProperties* floater)
- : mFloater(floater)
- {
- gInventory.addObserver(this);
- }
- virtual ~LLPropertiesObserver()
- {
- gInventory.removeObserver(this);
- }
- virtual void changed(U32 mask);
-private:
- LLFloaterProperties* mFloater; // Not a handle because LLFloaterProperties is managing LLPropertiesObserver
-};
-
-void LLPropertiesObserver::changed(U32 mask)
-{
- // if there's a change we're interested in.
- if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
- {
- mFloater->dirty();
- }
-}
-
-
-
-///----------------------------------------------------------------------------
-/// Class LLFloaterProperties
-///----------------------------------------------------------------------------
-
-// Default constructor
-LLFloaterProperties::LLFloaterProperties(const LLUUID& item_id)
- : LLFloater(mItemID),
- mItemID(item_id),
- mDirty(TRUE)
-{
- mPropertiesObserver = new LLPropertiesObserver(this);
-}
-
-// Destroys the object
-LLFloaterProperties::~LLFloaterProperties()
-{
- delete mPropertiesObserver;
- mPropertiesObserver = NULL;
-}
-
-// virtual
-BOOL LLFloaterProperties::postBuild()
-{
- // build the UI
- // item name & description
- getChild<LLLineEditor>("LabelItemName")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
- getChild<LLUICtrl>("LabelItemName")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitName,this));
- getChild<LLLineEditor>("LabelItemDesc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
- getChild<LLUICtrl>("LabelItemDesc")->setCommitCallback(boost::bind(&LLFloaterProperties:: onCommitDescription, this));
- // Creator information
- getChild<LLUICtrl>("BtnCreator")->setCommitCallback(boost::bind(&LLFloaterProperties::onClickCreator,this));
- // owner information
- getChild<LLUICtrl>("BtnOwner")->setCommitCallback(boost::bind(&LLFloaterProperties::onClickOwner,this));
- // acquired date
- // owner permissions
- // Permissions debug text
- // group permissions
- getChild<LLUICtrl>("CheckShareWithGroup")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
- // everyone permissions
- getChild<LLUICtrl>("CheckEveryoneCopy")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
- // next owner permissions
- getChild<LLUICtrl>("CheckNextOwnerModify")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
- getChild<LLUICtrl>("CheckNextOwnerCopy")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
- getChild<LLUICtrl>("CheckNextOwnerTransfer")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
- // Mark for sale or not, and sale info
- getChild<LLUICtrl>("CheckPurchase")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this));
- getChild<LLUICtrl>("ComboBoxSaleType")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleType, this));
- // "Price" label for edit
- getChild<LLUICtrl>("Edit Cost")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this));
- // The UI has been built, now fill in all the values
- refresh();
-
- return TRUE;
-}
-
-// virtual
-void LLFloaterProperties::onOpen(const LLSD& key)
-{
- refresh();
-}
-
-void LLFloaterProperties::refresh()
-{
- LLInventoryItem* item = findItem();
- if(item)
- {
- refreshFromItem(item);
- }
- else
- {
- //RN: it is possible that the container object is in the middle of an inventory refresh
- // causing findItem() to fail, so just temporarily disable everything
-
- mDirty = TRUE;
-
- const char* enableNames[]={
- "LabelItemName",
- "LabelItemDesc",
- "LabelCreatorName",
- "BtnCreator",
- "LabelOwnerName",
- "BtnOwner",
- "CheckOwnerModify",
- "CheckOwnerCopy",
- "CheckOwnerTransfer",
- "CheckShareWithGroup",
- "CheckEveryoneCopy",
- "CheckNextOwnerModify",
- "CheckNextOwnerCopy",
- "CheckNextOwnerTransfer",
- "CheckPurchase",
- "ComboBoxSaleType",
- "Edit Cost"
- };
- for(size_t t=0; t<LL_ARRAY_SIZE(enableNames); ++t)
- {
- getChildView(enableNames[t])->setEnabled(false);
- }
- const char* hideNames[]={
- "BaseMaskDebug",
- "OwnerMaskDebug",
- "GroupMaskDebug",
- "EveryoneMaskDebug",
- "NextMaskDebug"
- };
- for(size_t t=0; t<LL_ARRAY_SIZE(hideNames); ++t)
- {
- getChildView(hideNames[t])->setVisible(false);
- }
- }
-}
-
-void LLFloaterProperties::draw()
-{
- if (mDirty)
- {
- // RN: clear dirty first because refresh can set dirty to TRUE
- mDirty = FALSE;
- refresh();
- }
-
- LLFloater::draw();
-}
-
-void LLFloaterProperties::refreshFromItem(LLInventoryItem* item)
-{
- ////////////////////////
- // PERMISSIONS LOOKUP //
- ////////////////////////
-
- // do not enable the UI for incomplete items.
- LLViewerInventoryItem* i = (LLViewerInventoryItem*)item;
- BOOL is_complete = i->isFinished();
- const BOOL cannot_restrict_permissions = LLInventoryType::cannotRestrictPermissions(i->getInventoryType());
- const BOOL is_calling_card = (i->getInventoryType() == LLInventoryType::IT_CALLINGCARD);
- const LLPermissions& perm = item->getPermissions();
- const BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm,
- GP_OBJECT_MANIPULATE);
- const BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm,
- GP_OBJECT_SET_SALE) &&
- !cannot_restrict_permissions;
- const BOOL is_link = i->getIsLinkType();
-
- // You need permission to modify the object to modify an inventory
- // item in it.
- LLViewerObject* object = NULL;
- if(!mObjectID.isNull()) object = gObjectList.findObject(mObjectID);
- BOOL is_obj_modify = TRUE;
- if(object)
- {
- is_obj_modify = object->permOwnerModify();
- }
-
- //////////////////////
- // ITEM NAME & DESC //
- //////////////////////
- BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, perm,
- GP_OBJECT_MANIPULATE)
- && is_obj_modify && is_complete;
-
- getChildView("LabelItemNameTitle")->setEnabled(TRUE);
- getChildView("LabelItemName")->setEnabled(is_modifiable && !is_calling_card); // for now, don't allow rename of calling cards
- getChild<LLUICtrl>("LabelItemName")->setValue(item->getName());
- getChildView("LabelItemDescTitle")->setEnabled(TRUE);
- getChildView("LabelItemDesc")->setEnabled(is_modifiable);
- getChildView("IconLocked")->setVisible(!is_modifiable);
- getChild<LLUICtrl>("LabelItemDesc")->setValue(item->getDescription());
-
- //////////////////
- // CREATOR NAME //
- //////////////////
- if(!gCacheName) return;
- if(!gAgent.getRegion()) return;
-
- if (item->getCreatorUUID().notNull())
- {
- LLAvatarName av_name;
- LLAvatarNameCache::get(item->getCreatorUUID(), &av_name);
- getChildView("BtnCreator")->setEnabled(TRUE);
- getChildView("LabelCreatorTitle")->setEnabled(TRUE);
- getChildView("LabelCreatorName")->setEnabled(TRUE);
- getChild<LLUICtrl>("LabelCreatorName")->setValue(av_name.getUserName());
- }
- else
- {
- getChildView("BtnCreator")->setEnabled(FALSE);
- getChildView("LabelCreatorTitle")->setEnabled(FALSE);
- getChildView("LabelCreatorName")->setEnabled(FALSE);
- getChild<LLUICtrl>("LabelCreatorName")->setValue(getString("unknown"));
- }
-
- ////////////////
- // OWNER NAME //
- ////////////////
- if(perm.isOwned())
- {
- std::string name;
- if (perm.isGroupOwned())
- {
- gCacheName->getGroupName(perm.getGroup(), name);
- }
- else
- {
- LLAvatarName av_name;
- LLAvatarNameCache::get(perm.getOwner(), &av_name);
- name = av_name.getUserName();
- }
- getChildView("BtnOwner")->setEnabled(TRUE);
- getChildView("LabelOwnerTitle")->setEnabled(TRUE);
- getChildView("LabelOwnerName")->setEnabled(TRUE);
- getChild<LLUICtrl>("LabelOwnerName")->setValue(name);
- }
- else
- {
- getChildView("BtnOwner")->setEnabled(FALSE);
- getChildView("LabelOwnerTitle")->setEnabled(FALSE);
- getChildView("LabelOwnerName")->setEnabled(FALSE);
- getChild<LLUICtrl>("LabelOwnerName")->setValue(getString("public"));
- }
-
- //////////////////
- // ACQUIRE DATE //
- //////////////////
-
- time_t time_utc = item->getCreationDate();
- if (0 == time_utc)
- {
- getChild<LLUICtrl>("LabelAcquiredDate")->setValue(getString("unknown"));
- }
- else
- {
- std::string timeStr = getString("acquiredDate");
- LLSD substitution;
- substitution["datetime"] = (S32) time_utc;
- LLStringUtil::format (timeStr, substitution);
- getChild<LLUICtrl>("LabelAcquiredDate")->setValue(timeStr);
- }
-
- ///////////////////////
- // OWNER PERMISSIONS //
- ///////////////////////
- if(can_agent_manipulate)
- {
- getChild<LLUICtrl>("OwnerLabel")->setValue(getString("you_can"));
- }
- else
- {
- getChild<LLUICtrl>("OwnerLabel")->setValue(getString("owner_can"));
- }
-
- U32 base_mask = perm.getMaskBase();
- U32 owner_mask = perm.getMaskOwner();
- U32 group_mask = perm.getMaskGroup();
- U32 everyone_mask = perm.getMaskEveryone();
- U32 next_owner_mask = perm.getMaskNextOwner();
-
- getChildView("OwnerLabel")->setEnabled(TRUE);
- getChildView("CheckOwnerModify")->setEnabled(FALSE);
- getChild<LLUICtrl>("CheckOwnerModify")->setValue(LLSD((BOOL)(owner_mask & PERM_MODIFY)));
- getChildView("CheckOwnerCopy")->setEnabled(FALSE);
- getChild<LLUICtrl>("CheckOwnerCopy")->setValue(LLSD((BOOL)(owner_mask & PERM_COPY)));
- getChildView("CheckOwnerTransfer")->setEnabled(FALSE);
- getChild<LLUICtrl>("CheckOwnerTransfer")->setValue(LLSD((BOOL)(owner_mask & PERM_TRANSFER)));
-
- ///////////////////////
- // DEBUG PERMISSIONS //
- ///////////////////////
-
- if( gSavedSettings.getBOOL("DebugPermissions") )
- {
- BOOL slam_perm = FALSE;
- BOOL overwrite_group = FALSE;
- BOOL overwrite_everyone = FALSE;
-
- if (item->getType() == LLAssetType::AT_OBJECT)
- {
- U32 flags = item->getFlags();
- slam_perm = flags & LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_PERM;
- overwrite_everyone = flags & LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
- overwrite_group = flags & LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
- }
-
- std::string perm_string;
-
- perm_string = "B: ";
- perm_string += mask_to_string(base_mask);
- getChild<LLUICtrl>("BaseMaskDebug")->setValue(perm_string);
- getChildView("BaseMaskDebug")->setVisible(TRUE);
-
- perm_string = "O: ";
- perm_string += mask_to_string(owner_mask);
- getChild<LLUICtrl>("OwnerMaskDebug")->setValue(perm_string);
- getChildView("OwnerMaskDebug")->setVisible(TRUE);
-
- perm_string = "G";
- perm_string += overwrite_group ? "*: " : ": ";
- perm_string += mask_to_string(group_mask);
- getChild<LLUICtrl>("GroupMaskDebug")->setValue(perm_string);
- getChildView("GroupMaskDebug")->setVisible(TRUE);
-
- perm_string = "E";
- perm_string += overwrite_everyone ? "*: " : ": ";
- perm_string += mask_to_string(everyone_mask);
- getChild<LLUICtrl>("EveryoneMaskDebug")->setValue(perm_string);
- getChildView("EveryoneMaskDebug")->setVisible(TRUE);
-
- perm_string = "N";
- perm_string += slam_perm ? "*: " : ": ";
- perm_string += mask_to_string(next_owner_mask);
- getChild<LLUICtrl>("NextMaskDebug")->setValue(perm_string);
- getChildView("NextMaskDebug")->setVisible(TRUE);
- }
- else
- {
- getChildView("BaseMaskDebug")->setVisible(FALSE);
- getChildView("OwnerMaskDebug")->setVisible(FALSE);
- getChildView("GroupMaskDebug")->setVisible(FALSE);
- getChildView("EveryoneMaskDebug")->setVisible(FALSE);
- getChildView("NextMaskDebug")->setVisible(FALSE);
- }
-
- /////////////
- // SHARING //
- /////////////
-
- // Check for ability to change values.
- if (is_link || cannot_restrict_permissions)
- {
- getChildView("CheckShareWithGroup")->setEnabled(FALSE);
- getChildView("CheckEveryoneCopy")->setEnabled(FALSE);
- }
- else if (is_obj_modify && can_agent_manipulate)
- {
- getChildView("CheckShareWithGroup")->setEnabled(TRUE);
- getChildView("CheckEveryoneCopy")->setEnabled((owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER));
- }
- else
- {
- getChildView("CheckShareWithGroup")->setEnabled(FALSE);
- getChildView("CheckEveryoneCopy")->setEnabled(FALSE);
- }
-
- // Set values.
- BOOL is_group_copy = (group_mask & PERM_COPY) ? TRUE : FALSE;
- BOOL is_group_modify = (group_mask & PERM_MODIFY) ? TRUE : FALSE;
- BOOL is_group_move = (group_mask & PERM_MOVE) ? TRUE : FALSE;
-
- if (is_group_copy && is_group_modify && is_group_move)
- {
- getChild<LLUICtrl>("CheckShareWithGroup")->setValue(LLSD((BOOL)TRUE));
-
- LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
- if(ctl)
- {
- ctl->setTentative(FALSE);
- }
- }
- else if (!is_group_copy && !is_group_modify && !is_group_move)
- {
- getChild<LLUICtrl>("CheckShareWithGroup")->setValue(LLSD((BOOL)FALSE));
- LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
- if(ctl)
- {
- ctl->setTentative(FALSE);
- }
- }
- else
- {
- LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
- if(ctl)
- {
- ctl->setTentative(TRUE);
- ctl->set(TRUE);
- }
- }
-
- getChild<LLUICtrl>("CheckEveryoneCopy")->setValue(LLSD((BOOL)(everyone_mask & PERM_COPY)));
-
- ///////////////
- // SALE INFO //
- ///////////////
-
- const LLSaleInfo& sale_info = item->getSaleInfo();
- BOOL is_for_sale = sale_info.isForSale();
- LLComboBox* combo_sale_type = getChild<LLComboBox>("ComboBoxSaleType");
- LLUICtrl* edit_cost = getChild<LLUICtrl>("Edit Cost");
-
- // Check for ability to change values.
- if (is_obj_modify && can_agent_sell
- && gAgent.allowOperation(PERM_TRANSFER, perm, GP_OBJECT_MANIPULATE))
- {
- getChildView("CheckPurchase")->setEnabled(is_complete);
-
- getChildView("NextOwnerLabel")->setEnabled(TRUE);
- getChildView("CheckNextOwnerModify")->setEnabled((base_mask & PERM_MODIFY) && !cannot_restrict_permissions);
- getChildView("CheckNextOwnerCopy")->setEnabled((base_mask & PERM_COPY) && !cannot_restrict_permissions);
- getChildView("CheckNextOwnerTransfer")->setEnabled((next_owner_mask & PERM_COPY) && !cannot_restrict_permissions);
-
- combo_sale_type->setEnabled(is_complete && is_for_sale);
- edit_cost->setEnabled(is_complete && is_for_sale);
- }
- else
- {
- getChildView("CheckPurchase")->setEnabled(FALSE);
-
- getChildView("NextOwnerLabel")->setEnabled(FALSE);
- getChildView("CheckNextOwnerModify")->setEnabled(FALSE);
- getChildView("CheckNextOwnerCopy")->setEnabled(FALSE);
- getChildView("CheckNextOwnerTransfer")->setEnabled(FALSE);
-
- combo_sale_type->setEnabled(FALSE);
- edit_cost->setEnabled(FALSE);
- }
-
- // Set values.
- getChild<LLUICtrl>("CheckPurchase")->setValue(is_for_sale);
- getChild<LLUICtrl>("CheckNextOwnerModify")->setValue(LLSD(BOOL(next_owner_mask & PERM_MODIFY)));
- getChild<LLUICtrl>("CheckNextOwnerCopy")->setValue(LLSD(BOOL(next_owner_mask & PERM_COPY)));
- getChild<LLUICtrl>("CheckNextOwnerTransfer")->setValue(LLSD(BOOL(next_owner_mask & PERM_TRANSFER)));
-
- if (is_for_sale)
- {
- S32 numerical_price;
- numerical_price = sale_info.getSalePrice();
- edit_cost->setValue(llformat("%d",numerical_price));
- combo_sale_type->setValue(sale_info.getSaleType());
- }
- else
- {
- edit_cost->setValue(llformat("%d",0));
- combo_sale_type->setValue(LLSaleInfo::FS_COPY);
- }
-}
-
-void LLFloaterProperties::onClickCreator()
-{
- LLInventoryItem* item = findItem();
- if(!item) return;
- if(!item->getCreatorUUID().isNull())
- {
- LLAvatarActions::showProfile(item->getCreatorUUID());
- }
-}
-
-// static
-void LLFloaterProperties::onClickOwner()
-{
- LLInventoryItem* item = findItem();
- if(!item) return;
- if(item->getPermissions().isGroupOwned())
- {
- LLGroupActions::show(item->getPermissions().getGroup());
- }
- else
- {
- LLAvatarActions::showProfile(item->getPermissions().getOwner());
- }
-}
-
-// static
-void LLFloaterProperties::onCommitName()
-{
- //LL_INFOS() << "LLFloaterProperties::onCommitName()" << LL_ENDL;
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
- if(!item)
- {
- return;
- }
- LLLineEditor* labelItemName = getChild<LLLineEditor>("LabelItemName");
-
- if(labelItemName&&
- (item->getName() != labelItemName->getText()) &&
- (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) )
- {
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
- new_item->rename(labelItemName->getText());
- if(mObjectID.isNull())
- {
- new_item->updateServer(FALSE);
- gInventory.updateItem(new_item);
- gInventory.notifyObservers();
- }
- else
- {
- LLViewerObject* object = gObjectList.findObject(mObjectID);
- if(object)
- {
- object->updateInventory(
- new_item,
- TASK_INVENTORY_ITEM_KEY,
- false);
- }
- }
- }
-}
-
-void LLFloaterProperties::onCommitDescription()
-{
- //LL_INFOS() << "LLFloaterProperties::onCommitDescription()" << LL_ENDL;
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
- if(!item) return;
-
- LLLineEditor* labelItemDesc = getChild<LLLineEditor>("LabelItemDesc");
- if(!labelItemDesc)
- {
- return;
- }
- if((item->getDescription() != labelItemDesc->getText()) &&
- (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)))
- {
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
-
- new_item->setDescription(labelItemDesc->getText());
- if(mObjectID.isNull())
- {
- new_item->updateServer(FALSE);
- gInventory.updateItem(new_item);
- gInventory.notifyObservers();
- }
- else
- {
- LLViewerObject* object = gObjectList.findObject(mObjectID);
- if(object)
- {
- object->updateInventory(
- new_item,
- TASK_INVENTORY_ITEM_KEY,
- false);
- }
- }
- }
-}
-
-// static
-void LLFloaterProperties::onCommitPermissions()
-{
- //LL_INFOS() << "LLFloaterProperties::onCommitPermissions()" << LL_ENDL;
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
- if(!item) return;
- LLPermissions perm(item->getPermissions());
-
-
- LLCheckBoxCtrl* CheckShareWithGroup = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
-
- if(CheckShareWithGroup)
- {
- perm.setGroupBits(gAgent.getID(), gAgent.getGroupID(),
- CheckShareWithGroup->get(),
- PERM_MODIFY | PERM_MOVE | PERM_COPY);
- }
- LLCheckBoxCtrl* CheckEveryoneCopy = getChild<LLCheckBoxCtrl>("CheckEveryoneCopy");
- if(CheckEveryoneCopy)
- {
- perm.setEveryoneBits(gAgent.getID(), gAgent.getGroupID(),
- CheckEveryoneCopy->get(), PERM_COPY);
- }
-
- LLCheckBoxCtrl* CheckNextOwnerModify = getChild<LLCheckBoxCtrl>("CheckNextOwnerModify");
- if(CheckNextOwnerModify)
- {
- perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
- CheckNextOwnerModify->get(), PERM_MODIFY);
- }
- LLCheckBoxCtrl* CheckNextOwnerCopy = getChild<LLCheckBoxCtrl>("CheckNextOwnerCopy");
- if(CheckNextOwnerCopy)
- {
- perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
- CheckNextOwnerCopy->get(), PERM_COPY);
- }
- LLCheckBoxCtrl* CheckNextOwnerTransfer = getChild<LLCheckBoxCtrl>("CheckNextOwnerTransfer");
- if(CheckNextOwnerTransfer)
- {
- perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
- CheckNextOwnerTransfer->get(), PERM_TRANSFER);
- }
- if(perm != item->getPermissions()
- && item->isFinished())
- {
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
- new_item->setPermissions(perm);
- U32 flags = new_item->getFlags();
- // If next owner permissions have changed (and this is an object)
- // then set the slam permissions flag so that they are applied on rez.
- if((perm.getMaskNextOwner()!=item->getPermissions().getMaskNextOwner())
- && (item->getType() == LLAssetType::AT_OBJECT))
- {
- flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_PERM;
- }
- // If everyone permissions have changed (and this is an object)
- // then set the overwrite everyone permissions flag so they
- // are applied on rez.
- if ((perm.getMaskEveryone()!=item->getPermissions().getMaskEveryone())
- && (item->getType() == LLAssetType::AT_OBJECT))
- {
- flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
- }
- // If group permissions have changed (and this is an object)
- // then set the overwrite group permissions flag so they
- // are applied on rez.
- if ((perm.getMaskGroup()!=item->getPermissions().getMaskGroup())
- && (item->getType() == LLAssetType::AT_OBJECT))
- {
- flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
- }
- new_item->setFlags(flags);
- if(mObjectID.isNull())
- {
- new_item->updateServer(FALSE);
- gInventory.updateItem(new_item);
- gInventory.notifyObservers();
- }
- else
- {
- LLViewerObject* object = gObjectList.findObject(mObjectID);
- if(object)
- {
- object->updateInventory(
- new_item,
- TASK_INVENTORY_ITEM_KEY,
- false);
- }
- }
- }
- else
- {
- // need to make sure we don't just follow the click
- refresh();
- }
-}
-
-// static
-void LLFloaterProperties::onCommitSaleInfo()
-{
- //LL_INFOS() << "LLFloaterProperties::onCommitSaleInfo()" << LL_ENDL;
- updateSaleInfo();
-}
-
-// static
-void LLFloaterProperties::onCommitSaleType()
-{
- //LL_INFOS() << "LLFloaterProperties::onCommitSaleType()" << LL_ENDL;
- updateSaleInfo();
-}
-
-void LLFloaterProperties::updateSaleInfo()
-{
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
- if(!item) return;
- LLSaleInfo sale_info(item->getSaleInfo());
- if(!gAgent.allowOperation(PERM_TRANSFER, item->getPermissions(), GP_OBJECT_SET_SALE))
- {
- getChild<LLUICtrl>("CheckPurchase")->setValue(LLSD((BOOL)FALSE));
- }
-
- if((BOOL)getChild<LLUICtrl>("CheckPurchase")->getValue())
- {
- // turn on sale info
- LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_COPY;
-
- LLComboBox* combo_sale_type = getChild<LLComboBox>("ComboBoxSaleType");
- if (combo_sale_type)
- {
- sale_type = static_cast<LLSaleInfo::EForSale>(combo_sale_type->getValue().asInteger());
- }
-
- if (sale_type == LLSaleInfo::FS_COPY
- && !gAgent.allowOperation(PERM_COPY, item->getPermissions(),
- GP_OBJECT_SET_SALE))
- {
- sale_type = LLSaleInfo::FS_ORIGINAL;
- }
-
-
-
- S32 price = -1;
- price = getChild<LLUICtrl>("Edit Cost")->getValue().asInteger();;
-
- // Invalid data - turn off the sale
- if (price < 0)
- {
- sale_type = LLSaleInfo::FS_NOT;
- price = 0;
- }
-
- sale_info.setSaleType(sale_type);
- sale_info.setSalePrice(price);
- }
- else
- {
- sale_info.setSaleType(LLSaleInfo::FS_NOT);
- }
- if(sale_info != item->getSaleInfo()
- && item->isFinished())
- {
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
-
- // Force an update on the sale price at rez
- if (item->getType() == LLAssetType::AT_OBJECT)
- {
- U32 flags = new_item->getFlags();
- flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_SALE;
- new_item->setFlags(flags);
- }
-
- new_item->setSaleInfo(sale_info);
- if(mObjectID.isNull())
- {
- // This is in the agent's inventory.
- new_item->updateServer(FALSE);
- gInventory.updateItem(new_item);
- gInventory.notifyObservers();
- }
- else
- {
- // This is in an object's contents.
- LLViewerObject* object = gObjectList.findObject(mObjectID);
- if(object)
- {
- object->updateInventory(
- new_item,
- TASK_INVENTORY_ITEM_KEY,
- false);
- }
- }
- }
- else
- {
- // need to make sure we don't just follow the click
- refresh();
- }
-}
-
-LLInventoryItem* LLFloaterProperties::findItem() const
-{
- LLInventoryItem* item = NULL;
- if(mObjectID.isNull())
- {
- // it is in agent inventory
- item = gInventory.getItem(mItemID);
- }
- else
- {
- LLViewerObject* object = gObjectList.findObject(mObjectID);
- if(object)
- {
- item = (LLInventoryItem*)object->getInventoryObject(mItemID);
- }
- }
- return item;
-}
-
-//static
-void LLFloaterProperties::dirtyAll()
-{
- LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("properties");
- for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
- iter != inst_list.end(); ++iter)
- {
- LLFloaterProperties* floater = dynamic_cast<LLFloaterProperties*>(*iter);
- llassert(floater); // else cast failed - wrong type D:
- if (floater)
- {
- floater->dirty();
- }
- }
-}
-
-///----------------------------------------------------------------------------
-/// LLMultiProperties
-///----------------------------------------------------------------------------
-
-LLMultiProperties::LLMultiProperties()
- : LLMultiFloater(LLSD())
-{
- // start with a small rect in the top-left corner ; will get resized
- LLRect rect;
- rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 20, 20);
- setRect(rect);
- LLFloater* last_floater = LLFloaterReg::getLastFloaterInGroup("properties");
- if (last_floater)
- {
- stackWith(*last_floater);
- }
- setTitle(LLTrans::getString("MultiPropertiesTitle"));
- buildTabContainer();
-}
-
-///----------------------------------------------------------------------------
-/// Local function definitions
-///----------------------------------------------------------------------------
diff --git a/indra/newview/llfloaterproperties.h b/indra/newview/llfloaterproperties.h
deleted file mode 100644
index aa3fcec337..0000000000
--- a/indra/newview/llfloaterproperties.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * @file llfloaterproperties.h
- * @brief A floater which shows an inventory item's properties.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLFLOATERPROPERTIES_H
-#define LL_LLFLOATERPROPERTIES_H
-
-#include <map>
-#include "llmultifloater.h"
-#include "lliconctrl.h"
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLFloaterProperties
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLButton;
-class LLCheckBoxCtrl;
-class LLInventoryItem;
-class LLLineEditor;
-class LLRadioGroup;
-class LLTextBox;
-
-class LLPropertiesObserver;
-
-class LLFloaterProperties : public LLFloater
-{
-public:
- LLFloaterProperties(const LLUUID& item_id);
- /*virtual*/ ~LLFloaterProperties();
-
- /*virtual*/ BOOL postBuild();
- /*virtual*/ void onOpen(const LLSD& key);
- void setObjectID(const LLUUID& object_id) { mObjectID = object_id; }
-
- void dirty() { mDirty = TRUE; }
- void refresh();
-
- static void dirtyAll();
-
-protected:
- // ui callbacks
- void onClickCreator();
- void onClickOwner();
- void onCommitName();
- void onCommitDescription();
- void onCommitPermissions();
- void onCommitSaleInfo();
- void onCommitSaleType();
- void updateSaleInfo();
-
- LLInventoryItem* findItem() const;
-
- void refreshFromItem(LLInventoryItem* item);
- virtual void draw();
-
-protected:
- // The item id of the inventory item in question.
- LLUUID mItemID;
-
- // mObjectID will have a value if it is associated with a task in
- // the world, and will be == LLUUID::null if it's in the agent
- // inventory.
- LLUUID mObjectID;
-
- BOOL mDirty;
-
- LLPropertiesObserver* mPropertiesObserver;
-};
-
-class LLMultiProperties : public LLMultiFloater
-{
-public:
- LLMultiProperties();
-};
-
-#endif // LL_LLFLOATERPROPERTIES_H
diff --git a/indra/newview/llfloatersidepanelcontainer.cpp b/indra/newview/llfloatersidepanelcontainer.cpp
index dd7ce40e97..a875d33679 100644
--- a/indra/newview/llfloatersidepanelcontainer.cpp
+++ b/indra/newview/llfloatersidepanelcontainer.cpp
@@ -90,6 +90,29 @@ void LLFloaterSidePanelContainer::closeFloater(bool app_quitting)
}
}
+LLFloater* LLFloaterSidePanelContainer::getTopmostInventoryFloater()
+{
+ LLFloater* topmost_floater = NULL;
+ S32 z_min = S32_MAX;
+
+ LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory");
+ for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
+ {
+ LLFloater* inventory_floater = (*iter);
+
+ if (inventory_floater && inventory_floater->getVisible())
+ {
+ S32 z_order = gFloaterView->getZOrder(inventory_floater);
+ if (z_order < z_min)
+ {
+ z_min = z_order;
+ topmost_floater = inventory_floater;
+ }
+ }
+ }
+ return topmost_floater;
+}
+
LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_name, const LLSD& params)
{
LLView* view = findChildView(panel_name, true);
diff --git a/indra/newview/llfloatersidepanelcontainer.h b/indra/newview/llfloatersidepanelcontainer.h
index 20baf28184..5e7e755d1f 100644
--- a/indra/newview/llfloatersidepanelcontainer.h
+++ b/indra/newview/llfloatersidepanelcontainer.h
@@ -57,6 +57,8 @@ public:
LLPanel* openChildPanel(const std::string& panel_name, const LLSD& params);
+ static LLFloater* getTopmostInventoryFloater();
+
static void showPanel(const std::string& floater_name, const LLSD& key);
static void showPanel(const std::string& floater_name, const std::string& panel_name, const LLSD& key);
diff --git a/indra/newview/llfloatersimpleoutfitsnapshot.cpp b/indra/newview/llfloatersimpleoutfitsnapshot.cpp
deleted file mode 100644
index bab2efbbd5..0000000000
--- a/indra/newview/llfloatersimpleoutfitsnapshot.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/**
-* @file llfloatersimpleoutfitsnapshot.cpp
-* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery
-*
-* $LicenseInfo:firstyear=2022&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2022, Linden Research, Inc.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation;
-* version 2.1 of the License only.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*
-* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-* $/LicenseInfo$
-*/
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llfloatersimpleoutfitsnapshot.h"
-
-#include "llfloaterreg.h"
-#include "llimagefiltersmanager.h"
-#include "llstatusbar.h" // can_afford_transaction()
-#include "llnotificationsutil.h"
-#include "llagentbenefits.h"
-#include "llviewercontrol.h"
-
-LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView = NULL;
-
-const S32 OUTFIT_SNAPSHOT_WIDTH = 256;
-const S32 OUTFIT_SNAPSHOT_HEIGHT = 256;
-
-static LLDefaultChildRegistry::Register<LLSimpleOutfitSnapshotFloaterView> r("simple_snapshot_outfit_floater_view");
-
-///----------------------------------------------------------------------------
-/// Class LLFloaterSimpleOutfitSnapshot::Impl
-///----------------------------------------------------------------------------
-
-LLSnapshotModel::ESnapshotFormat LLFloaterSimpleOutfitSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater)
-{
- return LLSnapshotModel::SNAPSHOT_FORMAT_PNG;
-}
-
-LLSnapshotModel::ESnapshotLayerType LLFloaterSimpleOutfitSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater)
-{
- return LLSnapshotModel::SNAPSHOT_TYPE_COLOR;
-}
-
-void LLFloaterSimpleOutfitSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater)
-{
- LLSnapshotLivePreview* previewp = getPreviewView();
- updateResolution(floater);
- if (previewp)
- {
- previewp->setSnapshotType(LLSnapshotModel::ESnapshotType::SNAPSHOT_TEXTURE);
- previewp->setSnapshotFormat(LLSnapshotModel::ESnapshotFormat::SNAPSHOT_FORMAT_PNG);
- previewp->setSnapshotBufferType(LLSnapshotModel::ESnapshotLayerType::SNAPSHOT_TYPE_COLOR);
- }
-}
-
-std::string LLFloaterSimpleOutfitSnapshot::Impl::getSnapshotPanelPrefix()
-{
- return "panel_outfit_snapshot_";
-}
-
-void LLFloaterSimpleOutfitSnapshot::Impl::updateResolution(void* data)
-{
- LLFloaterSimpleOutfitSnapshot *view = (LLFloaterSimpleOutfitSnapshot *)data;
-
- if (!view)
- {
- llassert(view);
- return;
- }
-
- S32 width = OUTFIT_SNAPSHOT_WIDTH;
- S32 height = OUTFIT_SNAPSHOT_HEIGHT;
-
- LLSnapshotLivePreview* previewp = getPreviewView();
- if (previewp)
- {
- S32 original_width = 0, original_height = 0;
- previewp->getSize(original_width, original_height);
-
- if (gSavedSettings.getBOOL("RenderHUDInSnapshot"))
- { //clamp snapshot resolution to window size when showing UI HUD in snapshot
- width = llmin(width, gViewerWindow->getWindowWidthRaw());
- height = llmin(height, gViewerWindow->getWindowHeightRaw());
- }
-
- llassert(width > 0 && height > 0);
-
- previewp->setSize(width, height);
-
- if (original_width != width || original_height != height)
- {
- // hide old preview as the aspect ratio could be wrong
- checkAutoSnapshot(previewp, FALSE);
- previewp->updateSnapshot(TRUE);
- }
- }
-}
-
-void LLFloaterSimpleOutfitSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg)
-{
- switch (status)
- {
- case STATUS_READY:
- mFloater->setCtrlsEnabled(true);
- break;
- case STATUS_WORKING:
- mFloater->setCtrlsEnabled(false);
- break;
- case STATUS_FINISHED:
- mFloater->setCtrlsEnabled(true);
- break;
- }
-
- mStatus = status;
-}
-
-///----------------------------------------------------------------re------------
-/// Class LLFloaterSimpleOutfitSnapshot
-///----------------------------------------------------------------------------
-
-LLFloaterSimpleOutfitSnapshot::LLFloaterSimpleOutfitSnapshot(const LLSD& key)
- : LLFloaterSnapshotBase(key),
- mOutfitGallery(NULL)
-{
- impl = new Impl(this);
-}
-
-LLFloaterSimpleOutfitSnapshot::~LLFloaterSimpleOutfitSnapshot()
-{
-}
-
-BOOL LLFloaterSimpleOutfitSnapshot::postBuild()
-{
- getChild<LLUICtrl>("save_btn")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost()));
-
- childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this);
- childSetAction("save_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onSend, this));
- childSetAction("cancel_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onCancel, this));
-
- mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
-
- // create preview window
- LLRect full_screen_rect = getRootView()->getRect();
- LLSnapshotLivePreview::Params p;
- p.rect(full_screen_rect);
- LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
- LLView* parent_view = gSnapshotFloaterView->getParent();
-
- parent_view->removeChild(gSnapshotFloaterView);
- // make sure preview is below snapshot floater
- parent_view->addChild(previewp);
- parent_view->addChild(gSnapshotFloaterView);
-
- //move snapshot floater to special purpose snapshotfloaterview
- gFloaterView->removeChild(this);
- gSnapshotFloaterView->addChild(this);
-
- impl->mPreviewHandle = previewp->getHandle();
- previewp->setContainer(this);
- impl->updateControls(this);
- impl->setAdvanced(true);
- impl->setSkipReshaping(true);
-
- previewp->mKeepAspectRatio = FALSE;
- previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect());
- previewp->setAllowRenderUI(false);
-
- return TRUE;
-}
-const S32 PREVIEW_OFFSET_X = 12;
-const S32 PREVIEW_OFFSET_Y = 70;
-
-void LLFloaterSimpleOutfitSnapshot::draw()
-{
- LLSnapshotLivePreview* previewp = getPreviewView();
-
- if (previewp && (previewp->isSnapshotActive() || previewp->getThumbnailLock()))
- {
- // don't render snapshot window in snapshot, even if "show ui" is turned on
- return;
- }
-
- LLFloater::draw();
-
- if (previewp && !isMinimized() && mThumbnailPlaceholder->getVisible())
- {
- if(previewp->getThumbnailImage())
- {
- bool working = impl->getStatus() == ImplBase::STATUS_WORKING;
- const LLRect& thumbnail_rect = getThumbnailPlaceholderRect();
- const S32 thumbnail_w = previewp->getThumbnailWidth();
- const S32 thumbnail_h = previewp->getThumbnailHeight();
-
- S32 offset_x = PREVIEW_OFFSET_X;
- S32 offset_y = PREVIEW_OFFSET_Y;
-
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- // Apply floater transparency to the texture unless the floater is focused.
- F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
- LLColor4 color = working ? LLColor4::grey4 : LLColor4::white;
- gl_draw_scaled_image(offset_x, offset_y,
- thumbnail_w, thumbnail_h,
- previewp->getThumbnailImage(), color % alpha);
-#if LL_DARWIN
- std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "OutfitSnapshotMacMask" : "OutfitSnapshotMacMask2";
-#else
- std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "FloaterFocusBackgroundColor" : "DkGray";
-#endif
-
- previewp->drawPreviewRect(offset_x, offset_y, LLUIColorTable::instance().getColor(alpha_color));
-
- gGL.pushUIMatrix();
- LLUI::translate((F32) thumbnail_rect.mLeft, (F32) thumbnail_rect.mBottom);
- mThumbnailPlaceholder->draw();
- gGL.popUIMatrix();
- }
- }
- impl->updateLayout(this);
-}
-
-void LLFloaterSimpleOutfitSnapshot::onOpen(const LLSD& key)
-{
- LLSnapshotLivePreview* preview = getPreviewView();
- if (preview)
- {
- preview->updateSnapshot(TRUE);
- }
- focusFirstItem(FALSE);
- gSnapshotFloaterView->setEnabled(TRUE);
- gSnapshotFloaterView->setVisible(TRUE);
- gSnapshotFloaterView->adjustToFitScreen(this, FALSE);
-
- impl->updateControls(this);
- impl->setStatus(ImplBase::STATUS_READY);
-}
-
-void LLFloaterSimpleOutfitSnapshot::onCancel()
-{
- closeFloater();
-}
-
-void LLFloaterSimpleOutfitSnapshot::onSend()
-{
- S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost();
- if (can_afford_transaction(expected_upload_cost))
- {
- saveTexture();
- postSave();
- }
- else
- {
- LLSD args;
- args["COST"] = llformat("%d", expected_upload_cost);
- LLNotificationsUtil::add("ErrorPhotoCannotAfford", args);
- inventorySaveFailed();
- }
-}
-
-void LLFloaterSimpleOutfitSnapshot::postSave()
-{
- impl->setStatus(ImplBase::STATUS_WORKING);
-}
-
-// static
-void LLFloaterSimpleOutfitSnapshot::update()
-{
- LLFloaterSimpleOutfitSnapshot* inst = findInstance();
- if (inst != NULL)
- {
- inst->impl->updateLivePreview();
- }
-}
-
-
-// static
-LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::findInstance()
-{
- return LLFloaterReg::findTypedInstance<LLFloaterSimpleOutfitSnapshot>("simple_outfit_snapshot");
-}
-
-// static
-LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::getInstance()
-{
- return LLFloaterReg::getTypedInstance<LLFloaterSimpleOutfitSnapshot>("simple_outfit_snapshot");
-}
-
-void LLFloaterSimpleOutfitSnapshot::saveTexture()
-{
- LLSnapshotLivePreview* previewp = getPreviewView();
- if (!previewp)
- {
- llassert(previewp != NULL);
- return;
- }
-
- if (mOutfitGallery)
- {
- mOutfitGallery->onBeforeOutfitSnapshotSave();
- }
- previewp->saveTexture(TRUE, getOutfitID().asString());
- if (mOutfitGallery)
- {
- mOutfitGallery->onAfterOutfitSnapshotSave();
- }
- closeFloater();
-}
-
-///----------------------------------------------------------------------------
-/// Class LLSimpleOutfitSnapshotFloaterView
-///----------------------------------------------------------------------------
-
-LLSimpleOutfitSnapshotFloaterView::LLSimpleOutfitSnapshotFloaterView(const Params& p) : LLFloaterView(p)
-{
-}
-
-LLSimpleOutfitSnapshotFloaterView::~LLSimpleOutfitSnapshotFloaterView()
-{
-}
diff --git a/indra/newview/llfloatersimplesnapshot.cpp b/indra/newview/llfloatersimplesnapshot.cpp
new file mode 100644
index 0000000000..58604c5628
--- /dev/null
+++ b/indra/newview/llfloatersimplesnapshot.cpp
@@ -0,0 +1,499 @@
+/**
+* @file llfloatersimplesnapshot.cpp
+* @brief Snapshot preview window for saving as a thumbnail
+*
+* $LicenseInfo:firstyear=2022&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2022, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatersimplesnapshot.h"
+
+#include "llfloaterreg.h"
+#include "llimagefiltersmanager.h"
+#include "llinventorymodel.h"
+#include "llinventoryobserver.h"
+#include "llstatusbar.h" // can_afford_transaction()
+#include "llnotificationsutil.h"
+#include "llagent.h"
+#include "llagentbenefits.h"
+#include "llviewercontrol.h"
+#include "llviewertexturelist.h"
+
+
+
+LLSimpleSnapshotFloaterView* gSimpleSnapshotFloaterView = NULL;
+
+const S32 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX = 256;
+const S32 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN = 64;
+
+// Thumbnail posting coro
+
+static const std::string THUMBNAIL_UPLOAD_CAP = "InventoryThumbnailUpload";
+
+void post_thumbnail_image_coro(std::string cap_url, std::string path_to_image, LLSD first_data)
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("post_profile_image_coro", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpHeaders::ptr_t httpHeaders;
+
+ LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+ httpOpts->setFollowRedirects(true);
+
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (!status)
+ {
+ // todo: notification?
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL;
+ return;
+ }
+ if (!result.has("uploader"))
+ {
+ // todo: notification?
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL;
+ return;
+ }
+ std::string uploader_cap = result["uploader"].asString();
+ if (uploader_cap.empty())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL;
+ return;
+ }
+
+ // Upload the image
+
+ LLCore::HttpRequest::ptr_t uploaderhttpRequest(new LLCore::HttpRequest);
+ LLCore::HttpHeaders::ptr_t uploaderhttpHeaders(new LLCore::HttpHeaders);
+ LLCore::HttpOptions::ptr_t uploaderhttpOpts(new LLCore::HttpOptions);
+ S64 length;
+
+ {
+ llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate);
+ if (!instream.is_open())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL;
+ return;
+ }
+ length = instream.tellg();
+ }
+
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2"); // optional
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", length)); // required!
+ uploaderhttpOpts->setFollowRedirects(true);
+
+ result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders);
+
+ httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ LL_DEBUGS("Thumbnail") << result << LL_ENDL;
+
+ if (!status)
+ {
+ LL_WARNS("Thumbnail") << "Failed to upload image " << status.toString() << LL_ENDL;
+ return;
+ }
+
+ if (result["state"].asString() != "complete")
+ {
+ if (result.has("message"))
+ {
+ LL_WARNS("Thumbnail") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("Thumbnail") << "Failed to upload image " << result << LL_ENDL;
+ }
+ return;
+ }
+
+ if (first_data.has("category_id"))
+ {
+ LLUUID cat_id = first_data["category_id"].asUUID();
+ LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
+ if (cat)
+ {
+ cat->setThumbnailUUID(result["new_asset"].asUUID());
+ }
+ gInventory.addChangedMask(LLInventoryObserver::INTERNAL, cat_id);
+ }
+ if (first_data.has("item_id"))
+ {
+ LLUUID item_id = first_data["item_id"].asUUID();
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if (item)
+ {
+ item->setThumbnailUUID(result["new_asset"].asUUID());
+ }
+ // Are we supposed to get BulkUpdateInventory?
+ gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id);
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterSimpleSnapshot::Impl
+///----------------------------------------------------------------------------
+
+LLSnapshotModel::ESnapshotFormat LLFloaterSimpleSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater)
+{
+ return LLSnapshotModel::SNAPSHOT_FORMAT_PNG;
+}
+
+LLSnapshotModel::ESnapshotLayerType LLFloaterSimpleSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater)
+{
+ return LLSnapshotModel::SNAPSHOT_TYPE_COLOR;
+}
+
+void LLFloaterSimpleSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater)
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ updateResolution(floater);
+ if (previewp)
+ {
+ previewp->setSnapshotType(LLSnapshotModel::ESnapshotType::SNAPSHOT_TEXTURE);
+ previewp->setSnapshotFormat(LLSnapshotModel::ESnapshotFormat::SNAPSHOT_FORMAT_PNG);
+ previewp->setSnapshotBufferType(LLSnapshotModel::ESnapshotLayerType::SNAPSHOT_TYPE_COLOR);
+ }
+}
+
+std::string LLFloaterSimpleSnapshot::Impl::getSnapshotPanelPrefix()
+{
+ return "panel_outfit_snapshot_";
+}
+
+void LLFloaterSimpleSnapshot::Impl::updateResolution(void* data)
+{
+ LLFloaterSimpleSnapshot *view = (LLFloaterSimpleSnapshot *)data;
+
+ if (!view)
+ {
+ llassert(view);
+ return;
+ }
+
+ S32 width = THUMBNAIL_SNAPSHOT_DIM_MAX;
+ S32 height = THUMBNAIL_SNAPSHOT_DIM_MAX;
+
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ if (previewp)
+ {
+ S32 original_width = 0, original_height = 0;
+ previewp->getSize(original_width, original_height);
+
+ if (gSavedSettings.getBOOL("RenderHUDInSnapshot"))
+ { //clamp snapshot resolution to window size when showing UI HUD in snapshot
+ width = llmin(width, gViewerWindow->getWindowWidthRaw());
+ height = llmin(height, gViewerWindow->getWindowHeightRaw());
+ }
+
+ llassert(width > 0 && height > 0);
+
+ previewp->setSize(width, height);
+
+ if (original_width != width || original_height != height)
+ {
+ // hide old preview as the aspect ratio could be wrong
+ checkAutoSnapshot(previewp, FALSE);
+ previewp->updateSnapshot(TRUE);
+ }
+ }
+}
+
+void LLFloaterSimpleSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg)
+{
+ switch (status)
+ {
+ case STATUS_READY:
+ mFloater->setCtrlsEnabled(true);
+ break;
+ case STATUS_WORKING:
+ mFloater->setCtrlsEnabled(false);
+ break;
+ case STATUS_FINISHED:
+ mFloater->setCtrlsEnabled(true);
+ break;
+ }
+
+ mStatus = status;
+}
+
+///----------------------------------------------------------------re------------
+/// Class LLFloaterSimpleSnapshot
+///----------------------------------------------------------------------------
+
+LLFloaterSimpleSnapshot::LLFloaterSimpleSnapshot(const LLSD& key)
+ : LLFloaterSnapshotBase(key)
+ , mOwner(NULL)
+ , mContextConeOpacity(0.f)
+{
+ impl = new Impl(this);
+}
+
+LLFloaterSimpleSnapshot::~LLFloaterSimpleSnapshot()
+{
+}
+
+BOOL LLFloaterSimpleSnapshot::postBuild()
+{
+ childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this);
+ childSetAction("save_btn", boost::bind(&LLFloaterSimpleSnapshot::onSend, this));
+ childSetAction("cancel_btn", boost::bind(&LLFloaterSimpleSnapshot::onCancel, this));
+
+ mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
+
+ // create preview window
+ LLRect full_screen_rect = getRootView()->getRect();
+ LLSnapshotLivePreview::Params p;
+ p.rect(full_screen_rect);
+ LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
+
+ // Do not move LLFloaterSimpleSnapshot floater into gSnapshotFloaterView
+ // since it can be a dependednt floater and does not draw UI
+
+ impl->mPreviewHandle = previewp->getHandle();
+ previewp->setContainer(this);
+ impl->updateControls(this);
+ impl->setAdvanced(true);
+ impl->setSkipReshaping(true);
+
+ previewp->mKeepAspectRatio = FALSE;
+ previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect());
+ previewp->setAllowRenderUI(false);
+ previewp->setThumbnailSubsampled(TRUE);
+
+ return TRUE;
+}
+
+const S32 PREVIEW_OFFSET_Y = 70;
+
+void LLFloaterSimpleSnapshot::draw()
+{
+ if (mOwner)
+ {
+ static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);
+ drawConeToOwner(mContextConeOpacity, max_opacity, mOwner);
+ }
+
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ if (previewp && (previewp->isSnapshotActive() || previewp->getThumbnailLock()))
+ {
+ // don't render snapshot window in snapshot, even if "show ui" is turned on
+ return;
+ }
+
+ LLFloater::draw();
+
+ if (previewp && !isMinimized() && mThumbnailPlaceholder->getVisible())
+ {
+ if(previewp->getThumbnailImage())
+ {
+ bool working = impl->getStatus() == ImplBase::STATUS_WORKING;
+ const S32 thumbnail_w = previewp->getThumbnailWidth();
+ const S32 thumbnail_h = previewp->getThumbnailHeight();
+
+ LLRect local_rect = getLocalRect();
+ S32 offset_x = (local_rect.getWidth() - thumbnail_w) / 2;
+ S32 offset_y = PREVIEW_OFFSET_Y;
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ // Apply floater transparency to the texture unless the floater is focused.
+ F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
+ LLColor4 color = working ? LLColor4::grey4 : LLColor4::white;
+ gl_draw_scaled_image(offset_x, offset_y,
+ thumbnail_w, thumbnail_h,
+ previewp->getThumbnailImage(), color % alpha);
+ }
+ }
+ impl->updateLayout(this);
+}
+
+void LLFloaterSimpleSnapshot::onOpen(const LLSD& key)
+{
+ LLSnapshotLivePreview* preview = getPreviewView();
+ if (preview)
+ {
+ preview->updateSnapshot(TRUE);
+ }
+ focusFirstItem(FALSE);
+ gSnapshotFloaterView->setEnabled(TRUE);
+ gSnapshotFloaterView->setVisible(TRUE);
+ gSnapshotFloaterView->adjustToFitScreen(this, FALSE);
+
+ impl->updateControls(this);
+ impl->setStatus(ImplBase::STATUS_READY);
+
+ mInventoryId = key["item_id"].asUUID();
+ mTaskId = key["task_id"].asUUID();
+}
+
+void LLFloaterSimpleSnapshot::onCancel()
+{
+ closeFloater();
+}
+
+void LLFloaterSimpleSnapshot::onSend()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ std::string temp_file = gDirUtilp->getTempFilename();
+ if (previewp->createUploadFile(temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN))
+ {
+ uploadImageUploadFile(temp_file, mInventoryId, mTaskId);
+ closeFloater();
+ }
+ else
+ {
+ LLSD notif_args;
+ notif_args["REASON"] = LLImage::getLastError().c_str();
+ LLNotificationsUtil::add("CannotUploadTexture", notif_args);
+ }
+}
+
+void LLFloaterSimpleSnapshot::postSave()
+{
+ impl->setStatus(ImplBase::STATUS_WORKING);
+}
+
+// static
+void LLFloaterSimpleSnapshot::uploadThumbnail(const std::string &file_path, const LLUUID &inventory_id, const LLUUID &task_id)
+{
+ // generate a temp texture file for coroutine
+ std::string temp_file = gDirUtilp->getTempFilename();
+ U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(file_path));
+ if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN, true))
+ {
+ LLSD notif_args;
+ notif_args["REASON"] = LLImage::getLastError().c_str();
+ LLNotificationsUtil::add("CannotUploadTexture", notif_args);
+ LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << inventory_id << " " << task_id << ", reason: " << notif_args["REASON"].asString() << LL_ENDL;
+ return;
+ }
+ uploadImageUploadFile(temp_file, inventory_id, task_id);
+}
+
+// static
+void LLFloaterSimpleSnapshot::uploadThumbnail(LLPointer<LLImageRaw> raw_image, const LLUUID& inventory_id, const LLUUID& task_id)
+{
+ std::string temp_file = gDirUtilp->getTempFilename();
+ if (!LLViewerTextureList::createUploadFile(raw_image, temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN))
+ {
+ LLSD notif_args;
+ notif_args["REASON"] = LLImage::getLastError().c_str();
+ LLNotificationsUtil::add("CannotUploadTexture", notif_args);
+ LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << inventory_id << " " << task_id << ", reason: " << notif_args["REASON"].asString() << LL_ENDL;
+ return;
+ }
+ uploadImageUploadFile(temp_file, inventory_id, task_id);
+}
+
+// static
+void LLFloaterSimpleSnapshot::uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id)
+{
+ LLSD data;
+
+ if (task_id.notNull())
+ {
+ data["item_id"] = inventory_id;
+ data["task_id"] = task_id;
+ }
+ else if (gInventory.getCategory(inventory_id))
+ {
+ data["category_id"] = inventory_id;
+ }
+ else
+ {
+ data["item_id"] = inventory_id;
+ }
+
+ std::string cap_url = gAgent.getRegionCapability(THUMBNAIL_UPLOAD_CAP);
+ if (cap_url.empty())
+ {
+ LLSD args;
+ args["CAPABILITY"] = THUMBNAIL_UPLOAD_CAP;
+ LLNotificationsUtil::add("RegionCapabilityRequestError", args);
+ LL_WARNS("Thumbnail") << "Failed to upload profile image for item " << inventory_id << " " << task_id << ", no cap found" << LL_ENDL;
+ return;
+ }
+
+ LLCoros::instance().launch("postAgentUserImageCoro",
+ boost::bind(post_thumbnail_image_coro, cap_url, temp_file, data));
+}
+
+void LLFloaterSimpleSnapshot::update()
+{
+ // initializes snapshots when needed
+ LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("simple_snapshot");
+ for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
+ iter != inst_list.end(); ++iter)
+ {
+ LLFloaterSimpleSnapshot* floater = dynamic_cast<LLFloaterSimpleSnapshot*>(*iter);
+ if (floater)
+ {
+ floater->impl->updateLivePreview();
+ }
+ }
+}
+
+
+// static
+LLFloaterSimpleSnapshot* LLFloaterSimpleSnapshot::findInstance(const LLSD &key)
+{
+ return LLFloaterReg::findTypedInstance<LLFloaterSimpleSnapshot>("simple_snapshot", key);
+}
+
+// static
+LLFloaterSimpleSnapshot* LLFloaterSimpleSnapshot::getInstance(const LLSD &key)
+{
+ return LLFloaterReg::getTypedInstance<LLFloaterSimpleSnapshot>("simple_snapshot", key);
+}
+
+void LLFloaterSimpleSnapshot::saveTexture()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ if (!previewp)
+ {
+ llassert(previewp != NULL);
+ return;
+ }
+
+ previewp->saveTexture(TRUE, getInventoryId().asString());
+ closeFloater();
+}
+
+///----------------------------------------------------------------------------
+/// Class LLSimpleOutfitSnapshotFloaterView
+///----------------------------------------------------------------------------
+
+LLSimpleSnapshotFloaterView::LLSimpleSnapshotFloaterView(const Params& p) : LLFloaterView(p)
+{
+}
+
+LLSimpleSnapshotFloaterView::~LLSimpleSnapshotFloaterView()
+{
+}
diff --git a/indra/newview/llfloatersimpleoutfitsnapshot.h b/indra/newview/llfloatersimplesnapshot.h
index cc9a6c5d1e..91a81ee5c3 100644
--- a/indra/newview/llfloatersimpleoutfitsnapshot.h
+++ b/indra/newview/llfloatersimplesnapshot.h
@@ -1,6 +1,6 @@
/**
-* @file llfloatersimpleoutfitsnapshot.h
-* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery
+* @file llfloatersimplesnapshot.h
+* @brief Snapshot preview window for saving as a thumbnail
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -24,26 +24,25 @@
* $/LicenseInfo$
*/
-#ifndef LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H
-#define LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H
+#ifndef LL_LLFLOATERSIMPLESNAPSHOT_H
+#define LL_LLFLOATERSIMPLESNAPSHOT_H
#include "llfloater.h"
#include "llfloatersnapshot.h"
-#include "lloutfitgallery.h"
#include "llsnapshotlivepreview.h"
///----------------------------------------------------------------------------
-/// Class LLFloaterSimpleOutfitSnapshot
+/// Class LLFloaterSimpleSnapshot
///----------------------------------------------------------------------------
-class LLFloaterSimpleOutfitSnapshot : public LLFloaterSnapshotBase
+class LLFloaterSimpleSnapshot : public LLFloaterSnapshotBase
{
- LOG_CLASS(LLFloaterSimpleOutfitSnapshot);
+ LOG_CLASS(LLFloaterSimpleSnapshot);
public:
- LLFloaterSimpleOutfitSnapshot(const LLSD& key);
- ~LLFloaterSimpleOutfitSnapshot();
+ LLFloaterSimpleSnapshot(const LLSD& key);
+ ~LLFloaterSimpleSnapshot();
BOOL postBuild();
void onOpen(const LLSD& key);
@@ -51,36 +50,48 @@ public:
static void update();
- static LLFloaterSimpleOutfitSnapshot* getInstance();
- static LLFloaterSimpleOutfitSnapshot* findInstance();
+ static LLFloaterSimpleSnapshot* getInstance(const LLSD &key);
+ static LLFloaterSimpleSnapshot* findInstance(const LLSD &key);
void saveTexture();
const LLRect& getThumbnailPlaceholderRect() { return mThumbnailPlaceholder->getRect(); }
- void setOutfitID(LLUUID id) { mOutfitID = id; }
- LLUUID getOutfitID() { return mOutfitID; }
- void setGallery(LLOutfitGallery* gallery) { mOutfitGallery = gallery; }
+ void setInventoryId(const LLUUID &inventory_id) { mInventoryId = inventory_id; }
+ LLUUID getInventoryId() { return mInventoryId; }
+ void setTaskId(const LLUUID &task_id) { mTaskId = task_id; }
+ void setOwner(LLView *owner_view) { mOwner = owner_view; }
void postSave();
+ static void uploadThumbnail(const std::string &file_path, const LLUUID &inventory_id, const LLUUID &task_id);
+ static void uploadThumbnail(LLPointer<LLImageRaw> raw_image, const LLUUID& inventory_id, const LLUUID& task_id);
class Impl;
friend class Impl;
+ static const S32 THUMBNAIL_SNAPSHOT_DIM_MAX;
+ static const S32 THUMBNAIL_SNAPSHOT_DIM_MIN;
+
private:
void onSend();
void onCancel();
- LLUUID mOutfitID;
- LLOutfitGallery* mOutfitGallery;
+ // uploads upload-ready file
+ static void uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id);
+
+ LLUUID mInventoryId;
+ LLUUID mTaskId;
+
+ LLView* mOwner;
+ F32 mContextConeOpacity;
};
///----------------------------------------------------------------------------
-/// Class LLFloaterSimpleOutfitSnapshot::Impl
+/// Class LLFloaterSimpleSnapshot::Impl
///----------------------------------------------------------------------------
-class LLFloaterSimpleOutfitSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase
+class LLFloaterSimpleSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase
{
- LOG_CLASS(LLFloaterSimpleOutfitSnapshot::Impl);
+ LOG_CLASS(LLFloaterSimpleSnapshot::Impl);
public:
Impl(LLFloaterSnapshotBase* floater)
: LLFloaterSnapshotBase::ImplBase(floater)
@@ -108,7 +119,7 @@ private:
/// Class LLSimpleOutfitSnapshotFloaterView
///----------------------------------------------------------------------------
-class LLSimpleOutfitSnapshotFloaterView : public LLFloaterView
+class LLSimpleSnapshotFloaterView : public LLFloaterView
{
public:
struct Params
@@ -117,13 +128,13 @@ public:
};
protected:
- LLSimpleOutfitSnapshotFloaterView(const Params& p);
+ LLSimpleSnapshotFloaterView(const Params& p);
friend class LLUICtrlFactory;
public:
- virtual ~LLSimpleOutfitSnapshotFloaterView();
+ virtual ~LLSimpleSnapshotFloaterView();
};
-extern LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView;
+extern LLSimpleSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView;
-#endif // LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H
+#endif // LL_LLFLOATERSIMPLESNAPSHOT_H
diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h
index de28091c32..1649b2eed7 100644
--- a/indra/newview/llfolderviewmodelinventory.h
+++ b/indra/newview/llfolderviewmodelinventory.h
@@ -39,12 +39,14 @@ class LLFolderViewModelItemInventory
public:
LLFolderViewModelItemInventory(class LLFolderViewModelInventory& root_view_model);
virtual const LLUUID& getUUID() const = 0;
+ virtual const LLUUID& getThumbnailUUID() const = 0;
virtual time_t getCreationDate() const = 0; // UTC seconds
virtual void setCreationDate(time_t creation_date_utc) = 0;
virtual PermissionMask getPermissionMask() const = 0;
virtual LLFolderType::EType getPreferredType() const = 0;
virtual void showProperties(void) = 0;
virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual.
+ virtual bool isItemInOutfits() const { return false; }
virtual BOOL isAgentInventory() const { return FALSE; }
virtual BOOL isUpToDate() const = 0;
virtual void addChild(LLFolderViewModelItem* child);
diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp
index e395da7f1e..ce28915d93 100644
--- a/indra/newview/llfriendcard.cpp
+++ b/indra/newview/llfriendcard.cpp
@@ -478,14 +478,24 @@ void LLFriendCardsManager::ensureFriendsFolderExists()
LL_WARNS() << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << LL_ENDL;
}
- friends_folder_ID = gInventory.createNewCategory(calling_cards_folder_ID,
- LLFolderType::FT_CALLINGCARD, get_friend_folder_name());
-
- gInventory.createNewCategory(friends_folder_ID,
- LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name());
-
- // Now when we have all needed folders we can sync their contents with buddies list.
- syncFriendsFolder();
+ gInventory.createNewCategory(
+ calling_cards_folder_ID,
+ LLFolderType::FT_CALLINGCARD,
+ get_friend_folder_name(),
+ [](const LLUUID &new_category_id)
+ {
+ gInventory.createNewCategory(
+ new_category_id,
+ LLFolderType::FT_CALLINGCARD,
+ get_friend_all_subfolder_name(),
+ [](const LLUUID &new_category_id)
+ {
+ // Now when we have all needed folders we can sync their contents with buddies list.
+ LLFriendCardsManager::getInstance()->syncFriendsFolder();
+ }
+ );
+ }
+ );
}
}
@@ -510,11 +520,16 @@ void LLFriendCardsManager::ensureFriendsAllFolderExists()
LL_WARNS() << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << LL_ENDL;
}
- friends_all_folder_ID = gInventory.createNewCategory(friends_folder_ID,
- LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name());
-
- // Now when we have all needed folders we can sync their contents with buddies list.
- syncFriendsFolder();
+ gInventory.createNewCategory(
+ friends_folder_ID,
+ LLFolderType::FT_CALLINGCARD,
+ get_friend_all_subfolder_name(),
+ [](const LLUUID &new_cat_id)
+ {
+ // Now when we have all needed folders we can sync their contents with buddies list.
+ LLFriendCardsManager::getInstance()->syncFriendsFolder();
+ }
+ );
}
}
diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp
index 5329f10612..c52d0be213 100644
--- a/indra/newview/llinspectobject.cpp
+++ b/indra/newview/llinspectobject.cpp
@@ -662,9 +662,7 @@ void LLInspectObject::onClickOpen()
void LLInspectObject::onClickMoreInfo()
{
- LLSD key;
- key["task"] = "task";
- LLFloaterSidePanelContainer::showPanel("inventory", key);
+ LLFloaterReg::showInstance("task_properties");
closeFloater();
}
diff --git a/indra/newview/llinspecttexture.cpp b/indra/newview/llinspecttexture.cpp
new file mode 100644
index 0000000000..da4e3c0949
--- /dev/null
+++ b/indra/newview/llinspecttexture.cpp
@@ -0,0 +1,249 @@
+/**
+ * @file llinspecttexture.cpp
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llinspect.h"
+#include "llinspecttexture.h"
+#include "llinventoryfunctions.h"
+#include "llinventorymodel.h"
+#include "lltexturectrl.h"
+#include "lltrans.h"
+#include "llviewertexturelist.h"
+
+
+// ============================================================================
+// Helper functions
+//
+
+LLToolTip* LLInspectTextureUtil::createInventoryToolTip(LLToolTip::Params p)
+{
+ const LLSD& sdTooltip = p.create_params;
+
+ if (sdTooltip.has("thumbnail_id") && sdTooltip["thumbnail_id"].asUUID().notNull())
+ {
+ // go straight for thumbnail regardless of type
+ // TODO: make a tooltip factory?
+ return LLUICtrlFactory::create<LLTextureToolTip>(p);
+ }
+
+ LLInventoryType::EType eInvType = (sdTooltip.has("inv_type")) ? (LLInventoryType::EType)sdTooltip["inv_type"].asInteger() : LLInventoryType::IT_NONE;
+ switch (eInvType)
+ {
+ case LLInventoryType::IT_CATEGORY:
+ {
+ if (sdTooltip.has("item_id"))
+ {
+ const LLUUID idCategory = sdTooltip["item_id"].asUUID();
+ LLViewerInventoryCategory* cat = gInventory.getCategory(idCategory);
+ if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT)
+ {
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ // Not LLIsOfAssetType, because we allow links
+ LLIsTextureType f;
+ gInventory.getDirectDescendentsOf(idCategory, cats, items, f);
+
+ // Exactly one texture found => show the texture tooltip
+ if (1 == items.size())
+ {
+ LLViewerInventoryItem* item = items.front();
+ if (item && item->getIsLinkType())
+ {
+ item = item->getLinkedItem();
+ }
+ if (item)
+ {
+ // Note: LLFloaterChangeItemThumbnail will attempt to write this
+ // into folder's thumbnail id when opened
+ p.create_params.getValue()["thumbnail_id"] = item->getAssetUUID();
+ return LLUICtrlFactory::create<LLTextureToolTip>(p);
+ }
+ }
+ }
+ }
+
+ // No or more than one texture found => show default tooltip
+ return LLUICtrlFactory::create<LLToolTip>(p);
+ }
+ default:
+ return LLUICtrlFactory::create<LLToolTip>(p);
+ }
+}
+
+// ============================================================================
+// LLTexturePreviewView helper class
+//
+
+class LLTexturePreviewView : public LLView
+{
+public:
+ LLTexturePreviewView(const LLView::Params& p);
+ ~LLTexturePreviewView();
+
+public:
+ void draw() override;
+
+public:
+ void setImageFromAssetId(const LLUUID& idAsset);
+ void setImageFromItemId(const LLUUID& idItem);
+
+protected:
+ LLPointer<LLViewerFetchedTexture> m_Image;
+ S32 mImageBoostLevel = LLGLTexture::BOOST_NONE;
+ std::string mLoadingText;
+};
+
+
+LLTexturePreviewView::LLTexturePreviewView(const LLView::Params& p)
+ : LLView(p)
+{
+ mLoadingText = LLTrans::getString("texture_loading");
+}
+
+LLTexturePreviewView::~LLTexturePreviewView()
+{
+ if (m_Image)
+ {
+ m_Image->setBoostLevel(mImageBoostLevel);
+ m_Image = nullptr;
+ }
+}
+
+void LLTexturePreviewView::draw()
+{
+ LLView::draw();
+
+ if (m_Image)
+ {
+ LLRect rctClient = getLocalRect();
+
+ if (4 == m_Image->getComponents())
+ {
+ const LLColor4 color(.098f, .098f, .098f);
+ gl_rect_2d(rctClient, color, TRUE);
+ }
+ gl_draw_scaled_image(rctClient.mLeft, rctClient.mBottom, rctClient.getWidth(), rctClient.getHeight(), m_Image);
+
+ bool isLoading = (!m_Image->isFullyLoaded()) && (m_Image->getDiscardLevel() > 0);
+ if (isLoading)
+ LLFontGL::getFontSansSerif()->renderUTF8(mLoadingText, 0, llfloor(rctClient.mLeft + 3), llfloor(rctClient.mTop - 25), LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW);
+ m_Image->addTextureStats((isLoading) ? MAX_IMAGE_AREA : (F32)(rctClient.getWidth() * rctClient.getHeight()));
+ }
+}
+
+void LLTexturePreviewView::setImageFromAssetId(const LLUUID& idAsset)
+{
+ m_Image = LLViewerTextureManager::getFetchedTexture(idAsset, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ if (m_Image)
+ {
+ mImageBoostLevel = m_Image->getBoostLevel();
+ m_Image->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
+ m_Image->forceToSaveRawImage(0);
+ if ( (!m_Image->isFullyLoaded()) && (!m_Image->hasFetcher()) )
+ {
+ if (m_Image->isInFastCacheList())
+ {
+ m_Image->loadFromFastCache();
+ }
+ gTextureList.forceImmediateUpdate(m_Image);
+ }
+ }
+}
+
+void LLTexturePreviewView::setImageFromItemId(const LLUUID& idItem)
+{
+ const LLViewerInventoryItem* pItem = gInventory.getItem(idItem);
+ setImageFromAssetId( (pItem) ? pItem->getAssetUUID() : LLUUID::null );
+}
+
+// ============================================================================
+// LLTextureToolTip class
+//
+
+LLTextureToolTip::LLTextureToolTip(const LLToolTip::Params& p)
+ : LLToolTip(p)
+ , mPreviewView(nullptr)
+ , mPreviewSize(256)
+{
+ mMaxWidth = llmax(mMaxWidth, mPreviewSize);
+
+ // Currently has to share params with LLToolTip, override values
+ setBackgroundColor(LLColor4::black);
+ setTransparentColor(LLColor4::black);
+ setBorderVisible(true);
+}
+
+LLTextureToolTip::~LLTextureToolTip()
+{
+}
+
+void LLTextureToolTip::initFromParams(const LLToolTip::Params& p)
+{
+ LLToolTip::initFromParams(p);
+
+ // Create and add the preview control
+ LLView::Params p_preview;
+ p_preview.name = "texture_preview";
+ LLRect rctPreview;
+ rctPreview.setOriginAndSize(mPadding, mTextBox->getRect().mTop, mPreviewSize, mPreviewSize);
+ p_preview.rect = rctPreview;
+ mPreviewView = LLUICtrlFactory::create<LLTexturePreviewView>(p_preview);
+ addChild(mPreviewView);
+
+ // Parse the control params
+ const LLSD& sdTextureParams = p.create_params;
+ if (sdTextureParams.has("thumbnail_id"))
+ {
+ mPreviewView->setImageFromAssetId(sdTextureParams["thumbnail_id"].asUUID());
+ }
+ else if (sdTextureParams.has("item_id"))
+ {
+ mPreviewView->setImageFromItemId(sdTextureParams["item_id"].asUUID());
+ }
+
+ // Currently has to share params with LLToolTip, override values manually
+ // Todo: provide from own params instead, may be like object inspector does it
+ LLViewBorder::Params border_params;
+ border_params.border_thickness(LLPANEL_BORDER_WIDTH);
+ border_params.highlight_light_color(LLColor4::white);
+ border_params.highlight_dark_color(LLColor4::white);
+ border_params.shadow_light_color(LLColor4::white);
+ border_params.shadow_dark_color(LLColor4::white);
+ addBorder(border_params);
+ setBorderVisible(true);
+
+ setBackgroundColor(LLColor4::black);
+ setBackgroundVisible(true);
+ setBackgroundOpaque(true);
+ setBackgroundImage(nullptr);
+ setTransparentImage(nullptr);
+
+ mTextBox->setColor(LLColor4::white);
+
+ snapToChildren();
+}
+
+// ============================================================================
diff --git a/indra/newview/llinspecttexture.h b/indra/newview/llinspecttexture.h
new file mode 100644
index 0000000000..ff0d80b825
--- /dev/null
+++ b/indra/newview/llinspecttexture.h
@@ -0,0 +1,49 @@
+/**
+ * @file llinspecttexture.h
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#pragma once
+
+#include "lltooltip.h"
+
+class LLTexturePreviewView;
+
+namespace LLInspectTextureUtil
+{
+ LLToolTip* createInventoryToolTip(LLToolTip::Params p);
+}
+
+class LLTextureToolTip : public LLToolTip
+{
+public:
+ LLTextureToolTip(const LLToolTip::Params& p);
+ ~LLTextureToolTip();
+
+public:
+ void initFromParams(const LLToolTip::Params& p) override;
+
+protected:
+ LLTexturePreviewView* mPreviewView;
+ S32 mPreviewSize;
+};
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index db347f7096..73005d6903 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -41,7 +41,6 @@
#include "llfloateropenobject.h"
#include "llfloaterreg.h"
#include "llfloatermarketplacelistings.h"
-#include "llfloateroutfitphotopreview.h"
#include "llfloatersidepanelcontainer.h"
#include "llsidepanelinventory.h"
#include "llfloaterworldmap.h"
@@ -90,29 +89,13 @@
void copy_slurl_to_clipboard_callback_inv(const std::string& slurl);
-typedef std::pair<LLUUID, LLUUID> two_uuids_t;
-typedef std::list<two_uuids_t> two_uuids_list_t;
-
const F32 SOUND_GAIN = 1.0f;
-struct LLMoveInv
-{
- LLUUID mObjectID;
- LLUUID mCategoryID;
- two_uuids_list_t mMoveList;
- void (*mCallback)(S32, void*);
- void* mUserData;
-};
-
using namespace LLOldEvents;
// Function declarations
-bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, boost::shared_ptr<LLMoveInv>);
bool confirm_attachment_rez(const LLSD& notification, const LLSD& response);
void teleport_via_landmark(const LLUUID& asset_id);
-static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit);
-static bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit);
-static BOOL can_move_to_landmarks(LLInventoryItem* inv_item);
static bool check_category(LLInventoryModel* model,
const LLUUID& cat_id,
LLInventoryPanel* active_panel,
@@ -138,6 +121,12 @@ bool isMarketplaceSendAction(const std::string& action)
return ("send_to_marketplace" == action);
}
+bool isPanelActive(const std::string& panel_name)
+{
+ LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE);
+ return (active_panel && (active_panel->getName() == panel_name));
+}
+
// Used by LLFolderBridge as callback for directory fetching recursion
class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
{
@@ -173,6 +162,65 @@ public:
}
};
+class LLPasteIntoFolderCallback: public LLInventoryCallback
+{
+public:
+ LLPasteIntoFolderCallback(LLHandle<LLInventoryPanel>& handle)
+ : mInventoryPanel(handle)
+ {
+ }
+ ~LLPasteIntoFolderCallback()
+ {
+ processItems();
+ }
+
+ void fire(const LLUUID& inv_item)
+ {
+ mChangedIds.push_back(inv_item);
+ }
+
+ void processItems()
+ {
+ LLInventoryPanel* panel = mInventoryPanel.get();
+ bool has_elements = false;
+ for (LLUUID& inv_item : mChangedIds)
+ {
+ LLInventoryItem* item = gInventory.getItem(inv_item);
+ if (item && panel)
+ {
+ LLUUID root_id = panel->getRootFolderID();
+
+ if (inv_item == root_id)
+ {
+ return;
+ }
+
+ LLFolderViewItem* item = panel->getItemByID(inv_item);
+ if (item)
+ {
+ if (!has_elements)
+ {
+ panel->clearSelection();
+ panel->getRootFolder()->clearSelection();
+ panel->getRootFolder()->requestArrange();
+ panel->getRootFolder()->update();
+ has_elements = true;
+ }
+ panel->getRootFolder()->changeSelection(item, TRUE);
+ }
+ }
+ }
+
+ if (has_elements)
+ {
+ panel->getRootFolder()->scrollToShowSelection();
+ }
+ }
+private:
+ LLHandle<LLInventoryPanel> mInventoryPanel;
+ std::vector<LLUUID> mChangedIds;
+};
+
// +=================================================+
// | LLInvFVBridge |
// +=================================================+
@@ -212,54 +260,17 @@ const std::string& LLInvFVBridge::getDisplayName() const
std::string LLInvFVBridge::getSearchableDescription() const
{
- const LLInventoryModel* model = getInventoryModel();
- if (model)
- {
- const LLInventoryItem *item = model->getItem(mUUID);
- if(item)
- {
- std::string desc = item->getDescription();
- LLStringUtil::toUpper(desc);
- return desc;
- }
- }
- return LLStringUtil::null;
+ return get_searchable_description(getInventoryModel(), mUUID);
}
std::string LLInvFVBridge::getSearchableCreatorName() const
{
- const LLInventoryModel* model = getInventoryModel();
- if (model)
- {
- const LLInventoryItem *item = model->getItem(mUUID);
- if(item)
- {
- LLAvatarName av_name;
- if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name))
- {
- std::string username = av_name.getUserName();
- LLStringUtil::toUpper(username);
- return username;
- }
- }
- }
- return LLStringUtil::null;
+ return get_searchable_creator_name(getInventoryModel(), mUUID);
}
std::string LLInvFVBridge::getSearchableUUIDString() const
{
- const LLInventoryModel* model = getInventoryModel();
- if (model)
- {
- const LLViewerInventoryItem *item = model->getItem(mUUID);
- if(item && (item->getIsFullPerm() || gAgent.isGodlikeWithoutAdminMenuFakery()))
- {
- std::string uuid = item->getAssetUUID().asString();
- LLStringUtil::toUpper(uuid);
- return uuid;
- }
- }
- return LLStringUtil::null;
+ return get_searchable_UUID(getInventoryModel(), mUUID);
}
// Folders have full perms
@@ -327,7 +338,7 @@ BOOL LLInvFVBridge::cutToClipboard()
const LLInventoryObject* obj = gInventory.getObject(mUUID);
if (obj && isItemMovable() && isItemRemovable())
{
- const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
const BOOL cut_from_marketplacelistings = gInventory.isObjectDescendentOf(mUUID, marketplacelistings_id);
if (cut_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(mUUID) ||
@@ -407,6 +418,32 @@ void LLInvFVBridge::showProperties()
}
}
+void LLInvFVBridge::navigateToFolder(bool new_window, bool change_mode)
+{
+ if(new_window)
+ {
+ mInventoryPanel.get()->openSingleViewInventory(mUUID);
+ }
+ else
+ {
+ if(change_mode)
+ {
+ LLInventoryPanel::setSFViewAndOpenFolder(mInventoryPanel.get(), mUUID);
+ }
+ else
+ {
+ LLInventorySingleFolderPanel* panel = dynamic_cast<LLInventorySingleFolderPanel*>(mInventoryPanel.get());
+ if (!panel || !getInventoryModel() || mUUID.isNull())
+ {
+ return;
+ }
+
+ panel->changeFolderRoot(mUUID);
+ }
+
+ }
+}
+
void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch)
{
// Deactivate gestures when moving them into Trash
@@ -800,6 +837,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
menuentry_vec_t &disabled_items, U32 flags)
{
const LLInventoryObject *obj = getInventoryObject();
+ bool single_folder_root = (mRoot == NULL);
if (obj)
{
@@ -811,7 +849,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
disabled_items.push_back(std::string("Copy"));
}
- if (isAgentInventory())
+ if (isAgentInventory() && !single_folder_root)
{
items.push_back(std::string("New folder from selected"));
items.push_back(std::string("Subfolder Separator"));
@@ -845,7 +883,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
items.push_back(std::string("Find Links"));
}
- if (!isInboxFolder())
+ if (!isInboxFolder() && !single_folder_root)
{
items.push_back(std::string("Rename"));
if (!isItemRenameable() || ((flags & FIRST_SELECTED_ITEM) == 0))
@@ -853,14 +891,20 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
disabled_items.push_back(std::string("Rename"));
}
}
-
+
+ items.push_back(std::string("thumbnail"));
+ if (isLibraryItem())
+ {
+ disabled_items.push_back(std::string("thumbnail"));
+ }
+
+ LLViewerInventoryItem *inv_item = gInventory.getItem(mUUID);
if (show_asset_id)
{
items.push_back(std::string("Copy Asset UUID"));
bool is_asset_knowable = false;
- LLViewerInventoryItem* inv_item = gInventory.getItem(mUUID);
if (inv_item)
{
is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(inv_item->getType());
@@ -873,6 +917,8 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
}
}
+ if(!single_folder_root)
+ {
items.push_back(std::string("Cut"));
if (!isItemMovable() || !isItemRemovable())
{
@@ -894,6 +940,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
}
}
}
+ }
}
}
@@ -919,16 +966,12 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
items.push_back(std::string("Paste Separator"));
- addDeleteContextMenuOptions(items, disabled_items);
-
- // If multiple items are selected, disable properties (if it exists).
- if ((flags & FIRST_SELECTED_ITEM) == 0)
- {
- disabled_items.push_back(std::string("Properties"));
- }
+ if(!single_folder_root)
+ {
+ addDeleteContextMenuOptions(items, disabled_items);
+ }
- LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE);
- if (active_panel && (active_panel->getName() != "All Items"))
+ if (!isPanelActive("All Items") && !isPanelActive("comb_single_folder_inv"))
{
items.push_back(std::string("Show in Main Panel"));
}
@@ -1019,7 +1062,7 @@ void LLInvFVBridge::addDeleteContextMenuOptions(menuentry_vec_t &items,
items.push_back(std::string("Delete"));
- if (!isItemRemovable())
+ if (!isItemRemovable() || isPanelActive("Favorite Items"))
{
disabled_items.push_back(std::string("Delete"));
}
@@ -1250,6 +1293,16 @@ BOOL LLInvFVBridge::isLinkedObjectInTrash() const
return FALSE;
}
+bool LLInvFVBridge::isItemInOutfits() const
+{
+ const LLInventoryModel* model = getInventoryModel();
+ if(!model) return false;
+
+ const LLUUID my_outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+
+ return isCOFFolder() || (my_outfits_cat == mUUID) || model->isObjectDescendentOf(mUUID, my_outfits_cat);
+}
+
BOOL LLInvFVBridge::isLinkedObjectMissing() const
{
const LLInventoryObject *obj = getInventoryObject();
@@ -1280,7 +1333,7 @@ BOOL LLInvFVBridge::isCOFFolder() const
// *TODO : Suppress isInboxFolder() once Merchant Outbox is fully deprecated
BOOL LLInvFVBridge::isInboxFolder() const
{
- const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false);
+ const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX);
if (inbox_id.isNull())
{
@@ -1292,7 +1345,7 @@ BOOL LLInvFVBridge::isInboxFolder() const
BOOL LLInvFVBridge::isMarketplaceListingsFolder() const
{
- const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
if (folder_id.isNull())
{
@@ -1592,7 +1645,7 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const
{
std::string error_msg;
LLInventoryModel* model = getInventoryModel();
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
if (marketplacelistings_id.notNull())
{
LLViewerInventoryCategory * master_folder = model->getCategory(marketplacelistings_id);
@@ -1691,6 +1744,12 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action)
restoreItem();
return;
}
+ else if ("thumbnail" == action)
+ {
+ LLSD data(mUUID);
+ LLFloaterReg::showInstance("change_item_thumbnail", data);
+ return;
+ }
else if ("copy_uuid" == action)
{
// Single item only
@@ -1745,7 +1804,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action)
{
LLInventoryItem* itemp = model->getItem(mUUID);
if (!itemp) return;
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
// Note: For a single item, if it's not a copy, then it's a move
move_item_to_marketplacelistings(itemp, marketplacelistings_id, ("copy_to_marketplace_listings" == action));
}
@@ -1959,9 +2018,9 @@ std::string LLItemBridge::getLabelSuffix() const
{
// String table is loaded before login screen and inventory items are
// loaded after login, so LLTrans should be ready.
- static std::string NO_COPY = LLTrans::getString("no_copy");
- static std::string NO_MOD = LLTrans::getString("no_modify");
- static std::string NO_XFER = LLTrans::getString("no_transfer");
+ static std::string NO_COPY = LLTrans::getString("no_copy_lbl");
+ static std::string NO_MOD = LLTrans::getString("no_modify_lbl");
+ static std::string NO_XFER = LLTrans::getString("no_transfer_lbl");
static std::string LINK = LLTrans::getString("link");
static std::string BROKEN_LINK = LLTrans::getString("broken_link");
std::string suffix;
@@ -1982,17 +2041,20 @@ std::string LLItemBridge::getLabelSuffix() const
BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
if (!copy)
{
+ suffix += " ";
suffix += NO_COPY;
}
BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
if (!mod)
{
- suffix += NO_MOD;
+ suffix += suffix.empty() ? " " : ",";
+ suffix += NO_MOD;
}
BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
gAgent.getID());
if (!xfer)
{
+ suffix += suffix.empty() ? " " : ",";
suffix += NO_XFER;
}
}
@@ -2161,6 +2223,21 @@ LLViewerInventoryItem* LLItemBridge::getItem() const
return item;
}
+const LLUUID& LLItemBridge::getThumbnailUUID() const
+{
+ LLViewerInventoryItem* item = NULL;
+ LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ item = (LLViewerInventoryItem*)model->getItem(mUUID);
+ }
+ if (item)
+ {
+ return item->getThumbnailUUID();
+ }
+ return LLUUID::null;
+}
+
BOOL LLItemBridge::isItemPermissive() const
{
LLViewerInventoryItem* item = getItem();
@@ -2244,13 +2321,32 @@ void LLFolderBridge::buildDisplayName() const
std::string LLFolderBridge::getLabelSuffix() const
{
static LLCachedControl<F32> folder_loading_message_delay(gSavedSettings, "FolderLoadingMessageWaitTime", 0.5f);
+ static LLCachedControl<bool> xui_debug(gSavedSettings, "DebugShowXUINames", 0);
if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= folder_loading_message_delay())
{
return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str());
}
std::string suffix = "";
- if(mShowDescendantsCount)
+ if (xui_debug)
+ {
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(getUUID(), cats, items);
+
+ LLViewerInventoryCategory* cat = gInventory.getCategory(getUUID());
+ if (cat)
+ {
+ LLStringUtil::format_map_t args;
+ args["[FOLDER_COUNT]"] = llformat("%d", cats->size());
+ args["[ITEMS_COUNT]"] = llformat("%d", items->size());
+ args["[VERSION]"] = llformat("%d", cat->getVersion());
+ args["[VIEWER_DESCENDANT_COUNT]"] = llformat("%d", cats->size() + items->size());
+ args["[SERVER_DESCENDANT_COUNT]"] = llformat("%d", cat->getDescendentCount());
+ suffix = " " + LLTrans::getString("InventoryFolderDebug", args);
+ }
+ }
+ else if(mShowDescendantsCount)
{
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
@@ -2274,6 +2370,16 @@ LLFontGL::StyleFlags LLFolderBridge::getLabelStyle() const
return LLFontGL::NORMAL;
}
+const LLUUID& LLFolderBridge::getThumbnailUUID() const
+{
+ LLViewerInventoryCategory* cat = getCategory();
+ if (cat)
+ {
+ return cat->getThumbnailUUID();
+ }
+ return LLUUID::null;
+}
+
void LLFolderBridge::update()
{
// we know we have children but haven't fetched them (doesn't obey filter)
@@ -2484,7 +2590,8 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
BOOL drop,
std::string& tooltip_msg,
BOOL is_link,
- BOOL user_confirm)
+ BOOL user_confirm,
+ LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel* model = getInventoryModel();
@@ -2501,8 +2608,8 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
if (!filter) return false;
const LLUUID &cat_id = inv_cat->getUUID();
- const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
const LLUUID from_folder_uuid = inv_cat->getParentUUID();
const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
@@ -2520,10 +2627,10 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
if (is_agent_inventory)
{
- const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false);
- const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false);
- const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
- const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND, false);
+ const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
+ const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+ const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id);
@@ -2789,7 +2896,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
{
// Category can contains objects,
// create a new folder and populate it with links to original objects
- dropToMyOutfits(inv_cat);
+ dropToMyOutfits(inv_cat, cb);
}
// if target is current outfit folder we use link
else if (move_is_into_current_outfit &&
@@ -2799,14 +2906,16 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
// traverse category and add all contents to currently worn.
BOOL append = true;
LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append);
+ if (cb) cb->fire(inv_cat->getUUID());
}
else if (move_is_into_marketplacelistings)
{
move_folder_to_marketplacelistings(inv_cat, mUUID);
+ if (cb) cb->fire(inv_cat->getUUID());
}
else
{
- if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false)))
+ if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX)))
{
set_dad_inbox_object(cat_id);
}
@@ -2818,6 +2927,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
(LLViewerInventoryCategory*)inv_cat,
mUUID,
move_is_into_trash);
+ if (cb) cb->fire(inv_cat->getUUID());
}
if (move_is_from_marketplacelistings)
{
@@ -2836,14 +2946,20 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid);
if (version_folder_id.notNull())
{
- LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id);
- if (!validate_marketplacelistings(cat,NULL))
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
+ version_folder_id,
+ [version_folder_id](bool result)
{
- LLMarketplaceData::instance().activateListing(version_folder_id,false);
+ if (!result)
+ {
+ LLMarketplaceData::instance().activateListing(version_folder_id, false);
+ }
}
+ );
}
// In all cases, update the listing we moved from so suffix are updated
update_marketplace_category(from_folder_uuid);
+ if (cb) cb->fire(inv_cat->getUUID());
}
}
}
@@ -2857,7 +2973,22 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
}
else
{
- accept = move_inv_category_world_to_agent(cat_id, mUUID, drop, NULL, NULL, filter);
+ // Todo: fix me. moving from task inventory doesn't have a completion callback,
+ // yet making a copy creates new item id so this doesn't work right
+ std::function<void(S32, void*, const LLMoveInv*)> callback = [cb](S32, void*, const LLMoveInv* move_inv) mutable
+ {
+ two_uuids_list_t::const_iterator move_it;
+ for (move_it = move_inv->mMoveList.begin();
+ move_it != move_inv->mMoveList.end();
+ ++move_it)
+ {
+ if (cb)
+ {
+ cb->fire(move_it->second);
+ }
+ }
+ };
+ accept = move_inv_category_world_to_agent(cat_id, mUUID, drop, callback, NULL, filter);
}
}
else if (LLToolDragAndDrop::SOURCE_LIBRARY == source)
@@ -2928,7 +3059,7 @@ void warn_move_inventory(LLViewerObject* object, boost::shared_ptr<LLMoveInv> mo
BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
const LLUUID& category_id,
BOOL drop,
- void (*callback)(S32, void*),
+ std::function<void(S32, void*, const LLMoveInv*)> callback,
void* user_data,
LLInventoryFilter* filter)
{
@@ -3231,6 +3362,12 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
return;
}
+ else if ("thumbnail" == action)
+ {
+ LLSD data(mUUID);
+ LLFloaterReg::showInstance("change_item_thumbnail", data);
+ return;
+ }
else if ("paste" == action)
{
pasteFromClipboard();
@@ -3300,18 +3437,26 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
if (depth_nesting_in_marketplace(mUUID) == 1)
{
LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(mUUID);
- LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id);
mMessage = "";
- if (!validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3)))
- {
- LLSD subs;
- subs["[ERROR_CODE]"] = mMessage;
- LLNotificationsUtil::add("MerchantListingFailed", subs);
- }
- else
+
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
+ version_folder_id,
+ [this](bool result)
{
- LLMarketplaceData::instance().activateListing(mUUID,true);
- }
+ // todo: might need to ensure bridge/mUUID exists or this will cause crashes
+ if (!result)
+ {
+ LLSD subs;
+ subs["[ERROR_CODE]"] = mMessage;
+ LLNotificationsUtil::add("MerchantListingFailed", subs);
+ }
+ else
+ {
+ LLMarketplaceData::instance().activateListing(mUUID, true);
+ }
+ },
+ boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3)
+ );
}
return;
}
@@ -3319,18 +3464,27 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
{
if (depth_nesting_in_marketplace(mUUID) == 2)
{
- LLInventoryCategory* category = gInventory.getCategory(mUUID);
mMessage = "";
- if (!validate_marketplacelistings(category,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),false,2))
- {
- LLSD subs;
- subs["[ERROR_CODE]"] = mMessage;
- LLNotificationsUtil::add("MerchantFolderActivationFailed", subs);
- }
- else
+
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
+ mUUID,
+ [this](bool result)
{
- LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID);
- }
+ if (!result)
+ {
+ LLSD subs;
+ subs["[ERROR_CODE]"] = mMessage;
+ LLNotificationsUtil::add("MerchantFolderActivationFailed", subs);
+ }
+ else
+ {
+ LLInventoryCategory* category = gInventory.getCategory(mUUID);
+ LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID);
+ }
+ },
+ boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),
+ false,
+ 2);
}
return;
}
@@ -3353,29 +3507,44 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
}
else if ("marketplace_create_listing" == action)
{
- LLViewerInventoryCategory* cat = gInventory.getCategory(mUUID);
mMessage = "";
- bool validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),false);
- if (!validates)
+
+ // first run vithout fix_hierarchy, second run with fix_hierarchy
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
+ mUUID,
+ [this](bool result)
{
- mMessage = "";
- validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),true);
- if (validates)
+ if (!result)
+ {
+ mMessage = "";
+
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
+ mUUID,
+ [this](bool result)
+ {
+ if (result)
+ {
+ LLNotificationsUtil::add("MerchantForceValidateListing");
+ LLMarketplaceData::instance().createListing(mUUID);
+ }
+ else
+ {
+ LLSD subs;
+ subs["[ERROR_CODE]"] = mMessage;
+ LLNotificationsUtil::add("MerchantListingFailed", subs);
+ }
+ },
+ boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),
+ true);
+ }
+ else
{
- LLNotificationsUtil::add("MerchantForceValidateListing");
+ LLMarketplaceData::instance().createListing(mUUID);
}
- }
+ },
+ boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),
+ false);
- if (!validates)
- {
- LLSD subs;
- subs["[ERROR_CODE]"] = mMessage;
- LLNotificationsUtil::add("MerchantListingFailed", subs);
- }
- else
- {
- LLMarketplaceData::instance().createListing(mUUID);
- }
return;
}
else if ("marketplace_disassociate_listing" == action)
@@ -3419,7 +3588,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
{
LLInventoryCategory * cat = gInventory.getCategory(mUUID);
if (!cat) return;
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
move_folder_to_marketplacelistings(cat, marketplacelistings_id, ("move_to_marketplace_listings" != action), (("copy_or_move_to_marketplace_listings" == action)));
}
}
@@ -3669,7 +3838,7 @@ void LLFolderBridge::pasteFromClipboard()
LLInventoryModel* model = getInventoryModel();
if (model && isClipboardPasteable())
{
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
const BOOL paste_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
BOOL cut_from_marketplacelistings = FALSE;
@@ -3730,11 +3899,11 @@ void LLFolderBridge::perform_pasteFromClipboard()
LLInventoryModel* model = getInventoryModel();
if (model && isClipboardPasteable())
{
- const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
- const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false);
- const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
- const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND, false);
+ const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
+ const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+ const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id);
@@ -3745,6 +3914,13 @@ void LLFolderBridge::perform_pasteFromClipboard()
std::vector<LLUUID> objects;
LLClipboard::instance().pasteFromClipboard(objects);
+
+ LLPointer<LLInventoryCallback> cb = NULL;
+ LLInventoryPanel* panel = mInventoryPanel.get();
+ if (panel->getRootFolder()->isSingleFolderMode() && panel->getRootFolderID() == mUUID)
+ {
+ cb = new LLPasteIntoFolderCallback(mInventoryPanel);
+ }
LLViewerInventoryCategory * dest_folder = getCategory();
if (move_is_into_marketplacelistings)
@@ -3820,7 +3996,7 @@ void LLFolderBridge::perform_pasteFromClipboard()
{
if (!move_is_into_my_outfits && item && can_move_to_outfit(item, move_is_into_current_outfit))
{
- dropToOutfit(item, move_is_into_current_outfit);
+ dropToOutfit(item, move_is_into_current_outfit, cb);
}
else if (move_is_into_my_outfits && LLAssetType::AT_CATEGORY == obj->getType())
{
@@ -3828,7 +4004,7 @@ void LLFolderBridge::perform_pasteFromClipboard()
U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit");
if (cat && can_move_to_my_outfits(model, cat, max_items_to_wear))
{
- dropToMyOutfits(cat);
+ dropToMyOutfits(cat, cb);
}
else
{
@@ -3844,7 +4020,7 @@ void LLFolderBridge::perform_pasteFromClipboard()
{
if (item && can_move_to_outfit(item, move_is_into_current_outfit))
{
- dropToOutfit(item, move_is_into_current_outfit);
+ dropToOutfit(item, move_is_into_current_outfit, cb);
}
else
{
@@ -3863,11 +4039,12 @@ void LLFolderBridge::perform_pasteFromClipboard()
{
//changeItemParent() implicity calls dirtyFilter
changeItemParent(model, viitem, parent_id, FALSE);
+ if (cb) cb->fire(item_id);
}
}
else
{
- dropToFavorites(item);
+ dropToFavorites(item, cb);
}
}
}
@@ -3895,6 +4072,7 @@ void LLFolderBridge::perform_pasteFromClipboard()
//changeCategoryParent() implicity calls dirtyFilter
changeCategoryParent(model, vicat, parent_id, FALSE);
}
+ if (cb) cb->fire(item_id);
}
}
else
@@ -3916,6 +4094,7 @@ void LLFolderBridge::perform_pasteFromClipboard()
//changeItemParent() implicity calls dirtyFilter
changeItemParent(model, viitem, parent_id, FALSE);
}
+ if (cb) cb->fire(item_id);
}
}
}
@@ -3936,6 +4115,7 @@ void LLFolderBridge::perform_pasteFromClipboard()
{
copy_inventory_category(model, vicat, parent_id);
}
+ if (cb) cb->fire(item_id);
}
}
else
@@ -3951,11 +4131,13 @@ void LLFolderBridge::perform_pasteFromClipboard()
// Stop pasting into the marketplace as soon as we get an error
break;
}
+ if (cb) cb->fire(item_id);
}
else if (item->getIsLinkType())
{
- link_inventory_object(parent_id, item_id,
- LLPointer<LLInventoryCallback>(NULL));
+ link_inventory_object(parent_id,
+ item_id,
+ cb);
}
else
{
@@ -3965,7 +4147,7 @@ void LLFolderBridge::perform_pasteFromClipboard()
item->getUUID(),
parent_id,
std::string(),
- LLPointer<LLInventoryCallback>(NULL));
+ cb);
}
}
}
@@ -3982,9 +4164,9 @@ void LLFolderBridge::pasteLinkFromClipboard()
LLInventoryModel* model = getInventoryModel();
if(model)
{
- const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
- const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
+ const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id);
@@ -4001,6 +4183,14 @@ void LLFolderBridge::pasteLinkFromClipboard()
std::vector<LLUUID> objects;
LLClipboard::instance().pasteFromClipboard(objects);
+
+ LLPointer<LLInventoryCallback> cb = NULL;
+ LLInventoryPanel* panel = mInventoryPanel.get();
+ if (panel->getRootFolder()->isSingleFolderMode())
+ {
+ cb = new LLPasteIntoFolderCallback(mInventoryPanel);
+ }
+
for (std::vector<LLUUID>::const_iterator iter = objects.begin();
iter != objects.end();
++iter)
@@ -4011,12 +4201,12 @@ void LLFolderBridge::pasteLinkFromClipboard()
LLInventoryItem *item = model->getItem(object_id);
if (item && can_move_to_outfit(item, move_is_into_current_outfit))
{
- dropToOutfit(item, move_is_into_current_outfit);
+ dropToOutfit(item, move_is_into_current_outfit, cb);
}
}
else if (LLConstPointer<LLInventoryObject> obj = model->getObject(object_id))
{
- link_inventory_object(parent_id, obj, LLPointer<LLInventoryCallback>(NULL));
+ link_inventory_object(parent_id, obj, cb);
}
}
// Change mode to paste for next paste
@@ -4054,8 +4244,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
const LLUUID &favorites = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
- const LLUUID &marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
- const LLUUID &outfits_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
+ const LLUUID &marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ const LLUUID &outfits_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
if (outfits_id == mUUID)
{
@@ -4077,12 +4267,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
}
disabled_items.push_back(std::string("New Folder"));
- disabled_items.push_back(std::string("New Script"));
- disabled_items.push_back(std::string("New Note"));
- disabled_items.push_back(std::string("New Settings"));
- disabled_items.push_back(std::string("New Gesture"));
- disabled_items.push_back(std::string("New Clothes"));
- disabled_items.push_back(std::string("New Body Parts"));
disabled_items.push_back(std::string("upload_def"));
}
if (favorites == mUUID)
@@ -4105,11 +4289,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
{
disabled_items.push_back(std::string("New Folder"));
- disabled_items.push_back(std::string("New Script"));
- disabled_items.push_back(std::string("New Note"));
- disabled_items.push_back(std::string("New Gesture"));
- disabled_items.push_back(std::string("New Clothes"));
- disabled_items.push_back(std::string("New Body Parts"));
disabled_items.push_back(std::string("upload_def"));
}
if (marketplace_listings_id == mUUID)
@@ -4119,14 +4298,14 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
disabled_items.push_back(std::string("Cut"));
disabled_items.push_back(std::string("Delete"));
}
+
+ if (isPanelActive("Favorite Items"))
+ {
+ disabled_items.push_back(std::string("Delete"));
+ }
if(trash_id == mUUID)
{
- bool is_recent_panel = false;
- LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE);
- if (active_panel && (active_panel->getName() == "Recent Items"))
- {
- is_recent_panel = true;
- }
+ bool is_recent_panel = isPanelActive("Recent Items");
// This is the trash.
items.push_back(std::string("Empty Trash"));
@@ -4145,6 +4324,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
{
disabled_items.push_back(std::string("Empty Trash"));
}
+
+ items.push_back(std::string("thumbnail"));
}
else if(isItemInTrash())
{
@@ -4169,19 +4350,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
}
if (!isMarketplaceListingsFolder())
{
- items.push_back(std::string("New Script"));
- items.push_back(std::string("New Note"));
- items.push_back(std::string("New Gesture"));
- items.push_back(std::string("New Clothes"));
- items.push_back(std::string("New Body Parts"));
- items.push_back(std::string("New Settings"));
items.push_back(std::string("upload_def"));
-
- if (!LLEnvironment::instance().isInventoryEnabled())
- {
- disabled_items.push_back("New Settings");
- }
-
}
}
getClipboardEntries(false, items, disabled_items, flags);
@@ -4192,6 +4361,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT))
{
items.push_back(std::string("Rename"));
+ items.push_back(std::string("thumbnail"));
addDeleteContextMenuOptions(items, disabled_items);
// EXT-4030: disallow deletion of currently worn outfit
@@ -4206,6 +4376,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
if (model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) == mUUID)
{
items.push_back(std::string("Copy outfit list to clipboard"));
+ addOpenFolderMenuOptions(flags, items);
}
//Added by aura to force inventory pull on right-click to display folder options correctly. 07-17-06
@@ -4312,9 +4483,12 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t&
if(!category) return;
const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
- if (trash_id == mUUID) return;
- if (isItemInTrash()) return;
-
+ if ((trash_id == mUUID) || isItemInTrash())
+ {
+ addOpenFolderMenuOptions(flags, items);
+ return;
+ }
+
if (!isItemRemovable())
{
disabled_items.push_back(std::string("Delete"));
@@ -4327,7 +4501,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t&
const bool is_agent_inventory = isAgentInventory();
// Only enable calling-card related options for non-system folders.
- if (!is_system_folder && is_agent_inventory)
+ if (!is_system_folder && is_agent_inventory && (mRoot != NULL))
{
LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard))
@@ -4347,6 +4521,14 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t&
disabled_items.push_back(std::string("New folder from selected"));
}
+ //skip the rest options in single-folder mode
+ if (mRoot == NULL)
+ {
+ return;
+ }
+
+ addOpenFolderMenuOptions(flags, items);
+
#ifndef LL_RELEASE_FOR_DOWNLOAD
if (LLFolderType::lookupIsProtectedType(type) && is_agent_inventory)
{
@@ -4372,26 +4554,29 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t&
if (type != LLFolderType::FT_OUTFIT)
{
items.push_back(std::string("Add To Outfit"));
+ if (!LLAppearanceMgr::instance().getCanAddToCOF(mUUID))
+ {
+ disabled_items.push_back(std::string("Add To Outfit"));
+ }
}
items.push_back(std::string("Replace Outfit"));
+ if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID))
+ {
+ disabled_items.push_back(std::string("Replace Outfit"));
+ }
}
if (is_agent_inventory)
{
items.push_back(std::string("Folder Wearables Separator"));
+ // Note: If user tries to unwear "My Inventory", it's going to deactivate everything including gestures
+ // Might be safer to disable this for "My Inventory"
items.push_back(std::string("Remove From Outfit"));
- if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID))
- {
- disabled_items.push_back(std::string("Remove From Outfit"));
- }
- }
- if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID))
- {
- disabled_items.push_back(std::string("Replace Outfit"));
- }
- if (!LLAppearanceMgr::instance().getCanAddToCOF(mUUID))
- {
- disabled_items.push_back(std::string("Add To Outfit"));
+ if (type != LLFolderType::FT_ROOT_INVENTORY // Unless COF is empty, whih shouldn't be, warrantied to have worn items
+ && !LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) // expensive from root!
+ {
+ disabled_items.push_back(std::string("Remove From Outfit"));
+ }
}
items.push_back(std::string("Outfit Separator"));
@@ -4424,6 +4609,20 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
menu.arrangeAndClear();
}
+void LLFolderBridge::addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items)
+{
+ if ((flags & ITEM_IN_MULTI_SELECTION) == 0)
+ {
+ items.push_back(std::string("open_in_new_window"));
+ items.push_back(std::string("Open Folder Separator"));
+ items.push_back(std::string("Copy Separator"));
+ if(isPanelActive("comb_single_folder_inv"))
+ {
+ items.push_back(std::string("open_in_current_window"));
+ }
+ }
+}
+
bool LLFolderBridge::hasChildren() const
{
LLInventoryModel* model = getInventoryModel();
@@ -4440,6 +4639,18 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
{
LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
+ static LLPointer<LLInventoryCallback> drop_cb = NULL;
+ LLInventoryPanel* panel = mInventoryPanel.get();
+ LLToolDragAndDrop* drop_tool = LLToolDragAndDrop::getInstance();
+ if (drop
+ && panel->getRootFolder()->isSingleFolderMode()
+ && panel->getRootFolderID() == mUUID
+ && drop_tool->getCargoIndex() == 0)
+ {
+ drop_cb = new LLPasteIntoFolderCallback(mInventoryPanel);
+ }
+
+
//LL_INFOS() << "LLFolderBridge::dragOrDrop()" << LL_ENDL;
BOOL accept = FALSE;
switch(cargo_type)
@@ -4457,7 +4668,7 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
case DAD_GESTURE:
case DAD_MESH:
case DAD_SETTINGS:
- accept = dragItemIntoFolder(inv_item, drop, tooltip_msg);
+ accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, TRUE, drop_cb);
break;
case DAD_LINK:
// DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER.
@@ -4468,12 +4679,12 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID());
if (linked_category)
{
- accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE);
+ accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE, TRUE, drop_cb);
}
}
else
{
- accept = dragItemIntoFolder(inv_item, drop, tooltip_msg);
+ accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, TRUE, drop_cb);
}
break;
case DAD_CATEGORY:
@@ -4483,7 +4694,7 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
}
else
{
- accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop, tooltip_msg);
+ accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop, tooltip_msg, FALSE, TRUE, drop_cb);
}
break;
case DAD_ROOT_CATEGORY:
@@ -4493,6 +4704,11 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
LL_WARNS() << "Unhandled cargo type for drag&drop " << cargo_type << LL_ENDL;
break;
}
+
+ if (!drop || drop_tool->getCargoIndex() + 1 == drop_tool->getCargoCount())
+ {
+ drop_cb = NULL;
+ }
return accept;
}
@@ -4793,138 +5009,56 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response
if (move_inv->mCallback)
{
- move_inv->mCallback(option, move_inv->mUserData);
+ move_inv->mCallback(option, move_inv->mUserData, move_inv.get());
}
move_inv.reset(); //since notification will persist
return false;
}
-// Returns true if the item can be moved to Current Outfit or any outfit folder.
-static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit)
-{
- LLInventoryType::EType inv_type = inv_item->getInventoryType();
- if ((inv_type != LLInventoryType::IT_WEARABLE) &&
- (inv_type != LLInventoryType::IT_GESTURE) &&
- (inv_type != LLInventoryType::IT_ATTACHMENT) &&
- (inv_type != LLInventoryType::IT_OBJECT) &&
- (inv_type != LLInventoryType::IT_SNAPSHOT) &&
- (inv_type != LLInventoryType::IT_TEXTURE))
- {
- return FALSE;
- }
-
- U32 flags = inv_item->getFlags();
- if(flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS)
- {
- return FALSE;
- }
-
- if((inv_type == LLInventoryType::IT_TEXTURE) || (inv_type == LLInventoryType::IT_SNAPSHOT))
- {
- return !move_is_into_current_outfit;
- }
-
- if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID()))
- {
- return FALSE;
- }
-
- return TRUE;
-}
-
-// Returns true if folder's content can be moved to Current Outfit or any outfit folder.
-static bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit)
-{
- LLInventoryModel::cat_array_t *cats;
- LLInventoryModel::item_array_t *items;
- model->getDirectDescendentsOf(inv_cat->getUUID(), cats, items);
-
- if (items->size() > wear_limit)
- {
- return false;
- }
-
- if (items->size() == 0)
- {
- // Nothing to move(create)
- return false;
- }
-
- if (cats->size() > 0)
- {
- // We do not allow subfolders in outfits of "My Outfits" yet
- return false;
- }
-
- LLInventoryModel::item_array_t::iterator iter = items->begin();
- LLInventoryModel::item_array_t::iterator end = items->end();
-
- while (iter != end)
- {
- LLViewerInventoryItem *item = *iter;
- if (!can_move_to_outfit(item, false))
- {
- return false;
- }
- iter++;
- }
-
- return true;
-}
-
-// Returns TRUE if item is a landmark or a link to a landmark
-// and can be moved to Favorites or Landmarks folder.
-static BOOL can_move_to_landmarks(LLInventoryItem* inv_item)
+void drop_to_favorites_cb(const LLUUID& id, LLPointer<LLInventoryCallback> cb1, LLPointer<LLInventoryCallback> cb2)
{
- // Need to get the linked item to know its type because LLInventoryItem::getType()
- // returns actual type AT_LINK for links, not the asset type of a linked item.
- if (LLAssetType::AT_LINK == inv_item->getType())
- {
- LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID());
- if (linked_item)
- {
- return LLAssetType::AT_LANDMARK == linked_item->getType();
- }
- }
-
- return LLAssetType::AT_LANDMARK == inv_item->getType();
+ cb1->fire(id);
+ cb2->fire(id);
}
-void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item)
+void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item, LLPointer<LLInventoryCallback> cb)
{
// use callback to rearrange favorite landmarks after adding
// to have new one placed before target (on which it was dropped). See EXT-4312.
- LLPointer<AddFavoriteLandmarkCallback> cb = new AddFavoriteLandmarkCallback();
+ LLPointer<AddFavoriteLandmarkCallback> cb_fav = new AddFavoriteLandmarkCallback();
LLInventoryPanel* panel = mInventoryPanel.get();
LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL;
LLFolderViewModelItemInventory* view_model = drag_over_item ? static_cast<LLFolderViewModelItemInventory*>(drag_over_item->getViewModelItem()) : NULL;
if (view_model)
{
- cb.get()->setTargetLandmarkId(view_model->getUUID());
+ cb_fav.get()->setTargetLandmarkId(view_model->getUUID());
}
+ LLPointer <LLInventoryCallback> callback = cb_fav;
+ if (cb)
+ {
+ callback = new LLBoostFuncInventoryCallback(boost::bind(drop_to_favorites_cb, _1, cb, cb_fav));
+ }
+
copy_inventory_item(
gAgent.getID(),
inv_item->getPermissions().getOwner(),
inv_item->getUUID(),
mUUID,
std::string(),
- cb);
+ callback);
}
-void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit)
+void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit, LLPointer<LLInventoryCallback> cb)
{
if((inv_item->getInventoryType() == LLInventoryType::IT_TEXTURE) || (inv_item->getInventoryType() == LLInventoryType::IT_SNAPSHOT))
{
- const LLUUID &my_outifts_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
+ const LLUUID &my_outifts_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
if(mUUID != my_outifts_id)
{
- LLFloaterOutfitPhotoPreview* photo_preview = LLFloaterReg::showTypedInstance<LLFloaterOutfitPhotoPreview>("outfit_photo_preview", inv_item->getUUID());
- if(photo_preview)
- {
- photo_preview->setOutfitID(mUUID);
- }
+ // Legacy: prior to thumbnails images in outfits were used for outfit gallery.
+ LLNotificationsUtil::add("ThumbnailOutfitPhoto");
}
return;
}
@@ -4941,21 +5075,22 @@ void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_c
}
}
-void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat)
+void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLInventoryCallback> cb)
{
// make a folder in the My Outfits directory.
const LLUUID dest_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
// Note: creation will take time, so passing folder id to callback is slightly unreliable,
// but so is collecting and passing descendants' ids
- inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1);
+ inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1, cb);
gInventory.createNewCategory(dest_id,
LLFolderType::FT_OUTFIT,
inv_cat->getName(),
- func);
+ func,
+ inv_cat->getThumbnailUUID());
}
-void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id)
+void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel::cat_array_t* categories;
LLInventoryModel::item_array_t* items;
@@ -4986,7 +5121,6 @@ void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID ca
if (!link_array.empty())
{
- LLPointer<LLInventoryCallback> cb = NULL;
link_inventory_array(cat_dest_id, link_array, cb);
}
}
@@ -5019,7 +5153,8 @@ void LLFolderBridge::callback_dropCategoryIntoFolder(const LLSD& notification, c
BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
BOOL drop,
std::string& tooltip_msg,
- BOOL user_confirm)
+ BOOL user_confirm,
+ LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel* model = getInventoryModel();
@@ -5033,11 +5168,11 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
LLInventoryFilter* filter = getInventoryFilter();
if (!filter) return false;
- const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
- const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false);
- const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false);
- const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
- const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
+ const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
+ const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
const LLUUID from_folder_uuid = inv_item->getParentUUID();
const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
@@ -5057,7 +5192,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
LLViewerObject* object = NULL;
if(LLToolDragAndDrop::SOURCE_AGENT == source)
{
- const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false);
+ const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
const BOOL move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID());
@@ -5199,26 +5334,27 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
// (copy the item)
else if (move_is_into_favorites)
{
- dropToFavorites(inv_item);
+ dropToFavorites(inv_item, cb);
}
// CURRENT OUTFIT or OUTFIT folder
// (link the item)
else if (move_is_into_current_outfit || move_is_into_outfit)
{
- dropToOutfit(inv_item, move_is_into_current_outfit);
+ dropToOutfit(inv_item, move_is_into_current_outfit, cb);
}
// MARKETPLACE LISTINGS folder
// Move the item
else if (move_is_into_marketplacelistings)
{
move_item_to_marketplacelistings(inv_item, mUUID);
+ if (cb) cb->fire(inv_item->getUUID());
}
// NORMAL or TRASH folder
// (move the item, restamp if into trash)
else
{
// set up observer to select item once drag and drop from inbox is complete
- if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false)))
+ if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX)))
{
set_dad_inbox_object(inv_item->getUUID());
}
@@ -5228,6 +5364,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
(LLViewerInventoryItem*)inv_item,
mUUID,
move_is_into_trash);
+ if (cb) cb->fire(inv_item->getUUID());
}
if (move_is_from_marketplacelistings)
@@ -5236,11 +5373,15 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid);
if (version_folder_id.notNull())
{
- LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id);
- if (!validate_marketplacelistings(cat,NULL))
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
+ version_folder_id,
+ [version_folder_id](bool result)
{
- LLMarketplaceData::instance().activateListing(version_folder_id,false);
- }
+ if (!result)
+ {
+ LLMarketplaceData::instance().activateListing(version_folder_id, false);
+ }
+ });
}
}
@@ -5308,11 +5449,16 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
if (accept && drop)
{
+ LLUUID item_id = inv_item->getUUID();
boost::shared_ptr<LLMoveInv> move_inv (new LLMoveInv());
move_inv->mObjectID = inv_item->getParentUUID();
- two_uuids_t item_pair(mUUID, inv_item->getUUID());
+ two_uuids_t item_pair(mUUID, item_id);
move_inv->mMoveList.push_back(item_pair);
- move_inv->mCallback = NULL;
+ if (cb)
+ {
+ move_inv->mCallback = [item_id, cb](S32, void*, const LLMoveInv* move_inv) mutable
+ { cb->fire(item_id); };
+ }
move_inv->mUserData = NULL;
if(is_move)
{
@@ -5404,13 +5550,13 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
// (copy the item)
if (move_is_into_favorites)
{
- dropToFavorites(inv_item);
+ dropToFavorites(inv_item, cb);
}
// CURRENT OUTFIT or OUTFIT folder
// (link the item)
else if (move_is_into_current_outfit || move_is_into_outfit)
{
- dropToOutfit(inv_item, move_is_into_current_outfit);
+ dropToOutfit(inv_item, move_is_into_current_outfit, cb);
}
else
{
@@ -5420,7 +5566,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
inv_item->getUUID(),
mUUID,
std::string(),
- LLPointer<LLInventoryCallback>(NULL));
+ cb);
}
}
}
@@ -5975,7 +6121,7 @@ std::string LLCallingCardBridge::getLabelSuffix() const
LLViewerInventoryItem* item = getItem();
if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) )
{
- return LLItemBridge::getLabelSuffix() + " (online)";
+ return LLItemBridge::getLabelSuffix() + " online";
}
else
{
@@ -7558,16 +7704,26 @@ class LLObjectBridgeAction: public LLInvFVBridgeAction
public:
virtual void doIt()
{
- /*
- LLFloaterReg::showInstance("properties", mUUID);
- */
- LLInvFVBridgeAction::doIt();
+ attachOrDetach();
}
virtual ~LLObjectBridgeAction(){}
protected:
LLObjectBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {}
+ void attachOrDetach();
};
+void LLObjectBridgeAction::attachOrDetach()
+{
+ if (get_is_item_worn(mUUID))
+ {
+ LLAppearanceMgr::instance().removeItemFromAvatar(mUUID);
+ }
+ else
+ {
+ LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding.
+ }
+}
+
class LLLSLTextBridgeAction: public LLInvFVBridgeAction
{
friend class LLInvFVBridgeAction;
@@ -7626,7 +7782,17 @@ void LLWearableBridgeAction::wearOnAvatar()
LLViewerInventoryItem* item = getItem();
if(item)
{
- LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true);
+ if (get_is_item_worn(mUUID))
+ {
+ if(item->getType() != LLAssetType::AT_BODYPART)
+ {
+ LLAppearanceMgr::instance().removeItemFromAvatar(item->getUUID());
+ }
+ }
+ else
+ {
+ LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true);
+ }
}
}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index bdffecf1c6..decaee7db3 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -28,7 +28,6 @@
#define LL_LLINVENTORYBRIDGE_H
#include "llcallingcard.h"
-#include "llfloaterproperties.h"
#include "llfolderviewmodel.h"
#include "llinventorymodel.h"
#include "llinventoryobserver.h"
@@ -47,9 +46,11 @@ class LLMenuGL;
class LLCallingCardObserver;
class LLViewerJointAttachment;
class LLFolderView;
+struct LLMoveInv;
typedef std::vector<std::string> menuentry_vec_t;
-
+typedef std::pair<LLUUID, LLUUID> two_uuids_t;
+typedef std::list<two_uuids_t> two_uuids_list_t;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLInvFVBridge
//
@@ -84,6 +85,7 @@ public:
// LLInvFVBridge functionality
//--------------------------------------------------------------------
virtual const LLUUID& getUUID() const { return mUUID; }
+ virtual const LLUUID& getThumbnailUUID() const { return LLUUID::null; }
virtual void clearDisplayName() { mDisplayName.clear(); }
virtual void restoreItem() {}
virtual void restoreToWorld() {}
@@ -107,6 +109,7 @@ public:
virtual std::string getLabelSuffix() const { return LLStringUtil::null; }
virtual void openItem() {}
virtual void closeItem() {}
+ virtual void navigateToFolder(bool new_window = false, bool change_mode = false);
virtual void showProperties();
virtual BOOL isItemRenameable() const { return TRUE; }
virtual BOOL isMultiPreviewAllowed() { return TRUE; }
@@ -114,6 +117,7 @@ public:
virtual BOOL isItemRemovable() const;
virtual BOOL isItemMovable() const;
virtual BOOL isItemInTrash() const;
+ virtual bool isItemInOutfits() const;
virtual BOOL isLink() const;
virtual BOOL isLibraryItem() const;
//virtual BOOL removeItem() = 0;
@@ -251,6 +255,7 @@ public:
virtual LLUIImagePtr getIconOverlay() const;
LLViewerInventoryItem* getItem() const;
+ virtual const LLUUID& getThumbnailUUID() const;
protected:
BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response);
@@ -275,8 +280,8 @@ public:
mShowDescendantsCount(false)
{}
- BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE);
- BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL is_link = FALSE, BOOL user_confirm = TRUE);
+ BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE, LLPointer<LLInventoryCallback> cb = NULL);
+ BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL is_link = FALSE, BOOL user_confirm = TRUE, LLPointer<LLInventoryCallback> cb = NULL);
void callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item);
void callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_category);
@@ -296,6 +301,7 @@ public:
static LLUIImagePtr getIcon(LLFolderType::EType preferred_type);
virtual std::string getLabelSuffix() const;
virtual LLFontGL::StyleFlags getLabelStyle() const;
+ virtual const LLUUID& getThumbnailUUID() const;
void setShowDescendantsCount(bool show_count) {mShowDescendantsCount = show_count;}
@@ -335,6 +341,7 @@ public:
protected:
void buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items);
void buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items);
+ void addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items);
//--------------------------------------------------------------------
// Menu callbacks
@@ -360,9 +367,9 @@ protected:
void copyOutfitToClipboard();
void determineFolderType();
- void dropToFavorites(LLInventoryItem* inv_item);
- void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit);
- void dropToMyOutfits(LLInventoryCategory* inv_cat);
+ void dropToFavorites(LLInventoryItem* inv_item, LLPointer<LLInventoryCallback> cb = NULL);
+ void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit, LLPointer<LLInventoryCallback> cb = NULL);
+ void dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLInventoryCallback> cb = NULL);
//--------------------------------------------------------------------
// Messy hacks for handling folder options
@@ -372,7 +379,7 @@ public:
static void staticFolderOptionsMenu();
protected:
- void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id);
+ void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb);
void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response);
void perform_pasteFromClipboard();
void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level);
@@ -741,7 +748,7 @@ void rez_attachment(LLViewerInventoryItem* item,
BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
const LLUUID& category_id,
BOOL drop,
- void (*callback)(S32, void*) = NULL,
+ std::function<void(S32, void*, const LLMoveInv *)> callback = NULL,
void* user_data = NULL,
LLInventoryFilter* filter = NULL);
@@ -767,4 +774,16 @@ public:
bool canWearSelected(const uuid_vec_t& item_ids) const;
};
+struct LLMoveInv
+{
+ LLUUID mObjectID;
+ LLUUID mCategoryID;
+ two_uuids_list_t mMoveList;
+ std::function<void(S32, void*, const LLMoveInv*)> mCallback;
+ void* mUserData;
+};
+
+void warn_move_inventory(LLViewerObject* object, boost::shared_ptr<LLMoveInv> move_inv);
+bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, boost::shared_ptr<LLMoveInv>);
+
#endif // LL_LLINVENTORYBRIDGE_H
diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp
index e3a6b2dc85..5cf6c3fb7d 100644
--- a/indra/newview/llinventoryfilter.cpp
+++ b/indra/newview/llinventoryfilter.cpp
@@ -63,6 +63,7 @@ LLInventoryFilter::FilterOps::FilterOps(const Params& p)
mFilterTypes(p.types),
mFilterUUID(p.uuid),
mFilterLinks(p.links),
+ mFilterThumbnails(p.thumbnails),
mSearchVisibility(p.search_visibility)
{
}
@@ -81,7 +82,8 @@ LLInventoryFilter::LLInventoryFilter(const Params& p)
mCurrentGeneration(0),
mFirstRequiredGeneration(0),
mFirstSuccessGeneration(0),
- mSearchType(SEARCHTYPE_NAME)
+ mSearchType(SEARCHTYPE_NAME),
+ mSingleFolderMode(false)
{
// copy mFilterOps into mDefaultFilterOps
markDefault();
@@ -157,6 +159,8 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item)
passed = passed && checkAgainstCreator(listener);
passed = passed && checkAgainstSearchVisibility(listener);
+ passed = passed && checkAgainstFilterThumbnails(listener->getUUID());
+
return passed;
}
@@ -194,17 +198,23 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const
// when applying a filter, matching folders get their contents downloaded first
// but make sure we are not interfering with pre-download
if (isNotDefault()
- && LLStartUp::getStartupState() > STATE_WEARABLES_WAIT)
+ && LLStartUp::getStartupState() > STATE_WEARABLES_WAIT
+ && !LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress())
{
LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id);
- if (!cat || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
+ if ((!cat && folder_id.notNull()) || (cat && cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
{
// At the moment background fetch only cares about VERSION_UNKNOWN,
// so do not check isCategoryComplete that compares descendant count
- LLInventoryModelBackgroundFetch::instance().start(folder_id);
+ LLInventoryModelBackgroundFetch::instance().start(folder_id, false);
}
}
+ if (!checkAgainstFilterThumbnails(folder_id))
+ {
+ return false;
+ }
+
// Marketplace folder filtering
const U32 filterTypes = mFilterOps.mFilterTypes;
const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE |
@@ -565,6 +575,19 @@ bool LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewModelItemInven
return TRUE;
}
+bool LLInventoryFilter::checkAgainstFilterThumbnails(const LLUUID& object_id) const
+{
+ const LLInventoryObject *object = gInventory.getObject(object_id);
+ if (!object) return true;
+
+ const bool is_thumbnail = object->getThumbnailUUID().notNull();
+ if (is_thumbnail && (mFilterOps.mFilterThumbnails == FILTER_EXCLUDE_THUMBNAILS))
+ return false;
+ if (!is_thumbnail && (mFilterOps.mFilterThumbnails == FILTER_ONLY_THUMBNAILS))
+ return false;
+ return true;
+}
+
bool LLInventoryFilter::checkAgainstCreator(const LLFolderViewModelItemInventory* listener) const
{
if (!listener) return TRUE;
@@ -595,6 +618,9 @@ bool LLInventoryFilter::checkAgainstSearchVisibility(const LLFolderViewModelItem
if (is_link && ((mFilterOps.mSearchVisibility & VISIBILITY_LINKS) == 0))
return FALSE;
+ if (listener->isItemInOutfits() && ((mFilterOps.mSearchVisibility & VISIBILITY_OUTFITS) == 0))
+ return FALSE;
+
if (listener->isItemInTrash() && ((mFilterOps.mSearchVisibility & VISIBILITY_TRASH) == 0))
return FALSE;
@@ -733,6 +759,32 @@ void LLInventoryFilter::setFilterSettingsTypes(U64 types)
mFilterOps.mFilterTypes |= FILTERTYPE_SETTINGS;
}
+void LLInventoryFilter::setFilterThumbnails(U64 filter_thumbnails)
+{
+ if (mFilterOps.mFilterThumbnails != filter_thumbnails)
+ {
+ if (mFilterOps.mFilterThumbnails == FILTER_EXCLUDE_THUMBNAILS
+ && filter_thumbnails == FILTER_ONLY_THUMBNAILS)
+ {
+ setModified(FILTER_RESTART);
+ }
+ else if (mFilterOps.mFilterThumbnails == FILTER_ONLY_THUMBNAILS
+ && filter_thumbnails == FILTER_EXCLUDE_THUMBNAILS)
+ {
+ setModified(FILTER_RESTART);
+ }
+ else if (mFilterOps.mFilterThumbnails == FILTER_INCLUDE_THUMBNAILS)
+ {
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ }
+ mFilterOps.mFilterThumbnails = filter_thumbnails;
+}
+
void LLInventoryFilter::setFilterEmptySystemFolders()
{
mFilterOps.mFilterTypes |= FILTERTYPE_EMPTYFOLDERS;
@@ -791,6 +843,24 @@ void LLInventoryFilter::toggleSearchVisibilityLinks()
}
}
+void LLInventoryFilter::toggleSearchVisibilityOutfits()
+{
+ bool hide_outfits = mFilterOps.mSearchVisibility & VISIBILITY_OUTFITS;
+ if (hide_outfits)
+ {
+ mFilterOps.mSearchVisibility &= ~VISIBILITY_OUTFITS;
+ }
+ else
+ {
+ mFilterOps.mSearchVisibility |= VISIBILITY_OUTFITS;
+ }
+
+ if (hasFilterString())
+ {
+ setModified(hide_outfits ? FILTER_MORE_RESTRICTIVE : FILTER_LESS_RESTRICTIVE);
+ }
+}
+
void LLInventoryFilter::toggleSearchVisibilityTrash()
{
bool hide_trash = mFilterOps.mSearchVisibility & VISIBILITY_TRASH;
@@ -1503,6 +1573,11 @@ U64 LLInventoryFilter::getSearchVisibilityTypes() const
return mFilterOps.mSearchVisibility;
}
+U64 LLInventoryFilter::getFilterThumbnails() const
+{
+ return mFilterOps.mFilterThumbnails;
+}
+
bool LLInventoryFilter::hasFilterString() const
{
return mFilterSubString.size() > 0;
@@ -1580,9 +1655,9 @@ void LLInventoryFilter::setDefaultEmptyLookupMessage(const std::string& message)
mDefaultEmptyLookupMessage = message;
}
-std::string LLInventoryFilter::getEmptyLookupMessage() const
+std::string LLInventoryFilter::getEmptyLookupMessage(bool is_empty_folder) const
{
- if (isDefault() && !mDefaultEmptyLookupMessage.empty())
+ if ((isDefault() || is_empty_folder) && !mDefaultEmptyLookupMessage.empty())
{
return LLTrans::getString(mDefaultEmptyLookupMessage);
}
@@ -1605,7 +1680,7 @@ bool LLInventoryFilter::areDateLimitsSet()
bool LLInventoryFilter::showAllResults() const
{
- return hasFilterString();
+ return hasFilterString() && !mSingleFolderMode;
}
diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h
index 384de3e889..ada1d0f4b4 100644
--- a/indra/newview/llinventoryfilter.h
+++ b/indra/newview/llinventoryfilter.h
@@ -75,6 +75,13 @@ public:
FILTERLINK_ONLY_LINKS // only show links
};
+ enum EFilterThumbnail
+ {
+ FILTER_INCLUDE_THUMBNAILS,
+ FILTER_EXCLUDE_THUMBNAILS,
+ FILTER_ONLY_THUMBNAILS
+ };
+
enum ESortOrderType
{
SO_NAME = 0, // Sort inventory by name
@@ -104,7 +111,8 @@ public:
VISIBILITY_NONE = 0,
VISIBILITY_TRASH = 0x1 << 0,
VISIBILITY_LIBRARY = 0x1 << 1,
- VISIBILITY_LINKS = 0x1 << 2
+ VISIBILITY_LINKS = 0x1 << 2,
+ VISIBILITY_OUTFITS = 0x1 << 3
};
struct FilterOps
@@ -139,12 +147,14 @@ public:
Optional<EFolderShow> show_folder_state;
Optional<PermissionMask> permissions;
Optional<EFilterCreatorType> creator_type;
+ Optional<EFilterThumbnail> thumbnails;
Params()
: types("filter_types", FILTERTYPE_OBJECT),
object_types("object_types", 0xffffFFFFffffFFFFULL),
wearable_types("wearable_types", 0xffffFFFFffffFFFFULL),
settings_types("settings_types", 0xffffFFFFffffFFFFULL),
+ thumbnails("thumbnails", FILTER_INCLUDE_THUMBNAILS),
category_types("category_types", 0xffffFFFFffffFFFFULL),
links("links", FILTERLINK_INCLUDE_LINKS),
search_visibility("search_visibility", 0xFFFFFFFF),
@@ -165,6 +175,7 @@ public:
U64 mFilterObjectTypes, // For _OBJECT
mFilterWearableTypes,
mFilterSettingsTypes, // for _SETTINGS
+ mFilterThumbnails,
mFilterLinks,
mFilterCategoryTypes; // For _CATEGORY
LLUUID mFilterUUID; // for UUID
@@ -207,6 +218,7 @@ public:
U64 getFilterWearableTypes() const;
U64 getFilterSettingsTypes() const;
U64 getSearchVisibilityTypes() const;
+ U64 getFilterThumbnails() const;
bool isFilterObjectTypesWith(LLInventoryType::EType t) const;
void setFilterObjectTypes(U64 types);
@@ -221,6 +233,7 @@ public:
void setFilterMarketplaceUnassociatedFolders();
void setFilterMarketplaceListingFolders(bool select_only_listing_folders);
void setFilterNoMarketplaceFolder();
+ void setFilterThumbnails(U64 filter_thumbnails);
void updateFilterTypes(U64 types, U64& current_types);
void setSearchType(ESearchType type);
ESearchType getSearchType() { return mSearchType; }
@@ -228,6 +241,7 @@ public:
void toggleSearchVisibilityLinks();
void toggleSearchVisibilityTrash();
+ void toggleSearchVisibilityOutfits();
void toggleSearchVisibilityLibrary();
void setSearchVisibilityTypes(U32 types);
void setSearchVisibilityTypes(const Params& params);
@@ -237,6 +251,8 @@ public:
const std::string& getFilterSubStringOrig() const { return mFilterSubStringOrig; }
bool hasFilterString() const;
+ void setSingleFolderMode(bool is_single_folder) { mSingleFolderMode = is_single_folder; }
+
void setFilterPermissions(PermissionMask perms);
PermissionMask getFilterPermissions() const;
@@ -277,7 +293,7 @@ public:
void setEmptyLookupMessage(const std::string& message);
void setDefaultEmptyLookupMessage(const std::string& message);
- std::string getEmptyLookupMessage() const;
+ std::string getEmptyLookupMessage(bool is_empty_folder = false) const;
// +-------------------------------------------------------------------+
// + Status
@@ -321,6 +337,8 @@ public:
LLInventoryFilter& operator =(const LLInventoryFilter& other);
+ bool checkAgainstFilterThumbnails(const LLUUID& object_id) const;
+
private:
bool areDateLimitsSet();
bool checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const;
@@ -359,6 +377,8 @@ private:
std::vector<std::string> mFilterTokens;
std::string mExactToken;
+
+ bool mSingleFolderMode;
};
#endif
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 145814ab41..4aeacae6ed 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -46,13 +46,16 @@
#include "llappearancemgr.h"
#include "llappviewer.h"
#include "llavataractions.h"
+#include "llavatarnamecache.h"
#include "llclipboard.h"
#include "lldirpicker.h"
#include "lldonotdisturbnotificationstorage.h"
+#include "llfloatermarketplacelistings.h"
#include "llfloatersidepanelcontainer.h"
#include "llfocusmgr.h"
#include "llfolderview.h"
#include "llgesturemgr.h"
+#include "llgiveinventory.h"
#include "lliconctrl.h"
#include "llimview.h"
#include "llinventorybridge.h"
@@ -93,6 +96,7 @@
BOOL LLInventoryState::sWearNewClothing = FALSE;
LLUUID LLInventoryState::sWearNewClothingTransactionID;
std::list<LLUUID> LLInventoryAction::sMarketplaceFolders;
+bool LLInventoryAction::sDeleteConfirmationDisplayed = false;
// Helper function : callback to update a folder after inventory action happened in the background
void update_folder_cb(const LLUUID& dest_folder)
@@ -400,7 +404,7 @@ void update_all_marketplace_count(const LLUUID& cat_id)
void update_all_marketplace_count()
{
// Get the marketplace root and launch the recursive exploration
- const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
if (!marketplace_listings_uuid.isNull())
{
update_all_marketplace_count(marketplace_listings_uuid);
@@ -426,14 +430,36 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s
}
void copy_inventory_category(LLInventoryModel* model,
- LLViewerInventoryCategory* cat,
- const LLUUID& parent_id,
- const LLUUID& root_copy_id,
- bool move_no_copy_items )
+ LLViewerInventoryCategory* cat,
+ const LLUUID& parent_id,
+ const LLUUID& root_copy_id,
+ bool move_no_copy_items)
+{
+ // Create the initial folder
+ inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items](const LLUUID& new_id)
+ {
+ copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items);
+ };
+ gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID());
+}
+
+void copy_inventory_category(LLInventoryModel* model,
+ LLViewerInventoryCategory* cat,
+ const LLUUID& parent_id,
+ const LLUUID& root_copy_id,
+ bool move_no_copy_items,
+ inventory_func_type callback)
{
// Create the initial folder
- inventory_func_type func = boost::bind(&copy_inventory_category_content, _1, model, cat, root_copy_id, move_no_copy_items);
- gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func);
+ inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items, callback](const LLUUID &new_id)
+ {
+ copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items);
+ if (callback)
+ {
+ callback(new_id);
+ }
+ };
+ gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID());
}
void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items)
@@ -558,11 +584,12 @@ BOOL get_is_item_worn(const LLUUID& id)
const LLViewerInventoryItem* item = gInventory.getItem(id);
if (!item)
return FALSE;
-
+
if (item->getIsLinkType() && !gInventory.getItem(item->getLinkedUUID()))
{
return FALSE;
}
+
// Consider the item as worn if it has links in COF.
if (LLAppearanceMgr::instance().isLinkedInCOF(id))
{
@@ -797,18 +824,37 @@ BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id)
void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id)
{
- LLFloaterSidePanelContainer::showPanel("inventory", LLSD().with("id", item_uuid).with("object", object_id));
+ LLSD params;
+ params["id"] = item_uuid;
+ params["object"] = object_id;
+
+ LLFloaterReg::showInstance("item_properties", params);
}
void show_item_profile(const LLUUID& item_uuid)
{
LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid);
- LLFloaterSidePanelContainer::showPanel("inventory", LLSD().with("id", linked_uuid));
+ LLFloaterReg::showInstance("item_properties", LLSD().with("id", linked_uuid));
}
void show_item_original(const LLUUID& item_uuid)
{
- LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory");
+ static LLUICachedControl<bool> find_original_new_floater("FindOriginalOpenWindow", false);
+
+ //show in a new single-folder window
+ if(find_original_new_floater)
+ {
+ const LLUUID& linked_item_uuid = gInventory.getLinkedItemID(item_uuid);
+ const LLInventoryObject *obj = gInventory.getObject(linked_item_uuid);
+ if (obj && obj->getParentUUID().notNull())
+ {
+ LLPanelMainInventory::newFolderWindow(obj->getParentUUID(), linked_item_uuid);
+ }
+ }
+ //show in main Inventory
+ else
+ {
+ LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory");
if (!floater_inventory)
{
LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL;
@@ -820,6 +866,10 @@ void show_item_original(const LLUUID& item_uuid)
LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();
if (main_inventory)
{
+ if(main_inventory->isSingleFolderMode())
+ {
+ main_inventory->toggleViewMode();
+ }
main_inventory->resetAllItemsFilters();
}
reset_inventory_filter();
@@ -828,7 +878,6 @@ void show_item_original(const LLUUID& item_uuid)
{
LLFloaterReg::toggleInstanceOrBringToFront("inventory");
}
- sidepanel_inventory->showInventoryPanel();
const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX);
if (gInventory.isObjectDescendentOf(gInventory.getLinkedItemID(item_uuid), inbox_id))
@@ -848,6 +897,7 @@ void show_item_original(const LLUUID& item_uuid)
}
}
}
+ }
}
@@ -869,22 +919,6 @@ void open_marketplace_listings()
LLFloaterReg::showInstance("marketplace_listings");
}
-// Create a new folder in destFolderId with the same name as the item name and return the uuid of the new folder
-// Note: this is used locally in various situation where we need to wrap an item into a special folder
-LLUUID create_folder_for_item(LLInventoryItem* item, const LLUUID& destFolderId)
-{
- llassert(item);
- llassert(destFolderId.notNull());
-
- LLUUID created_folder_id = gInventory.createNewCategory(destFolderId, LLFolderType::FT_NONE, item->getName());
- gInventory.notifyObservers();
-
- // *TODO : Create different notifications for the various cases
- LLNotificationsUtil::add("OutboxFolderCreated");
-
- return created_folder_id;
-}
-
///----------------------------------------------------------------------------
// Marketplace functions
//
@@ -899,7 +933,7 @@ S32 depth_nesting_in_marketplace(LLUUID cur_uuid)
// Todo: findCategoryUUIDForType is somewhat expensive with large
// flat root folders yet we use depth_nesting_in_marketplace at
// every turn, find a way to correctly cache this id.
- const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
if (marketplace_listings_uuid.isNull())
{
return -1;
@@ -1371,6 +1405,7 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn
return accept;
}
+// Can happen asynhroneously!!!
bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy)
{
// Get the marketplace listings depth of the destination folder, exit with error if not under marketplace
@@ -1410,55 +1445,119 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol
if (can_move_to_marketplace(inv_item, error_msg, true))
{
// When moving an isolated item, we might need to create the folder structure to support it
+
+ LLUUID item_id = inv_item->getUUID();
+ std::function<void(const LLUUID&)> callback_create_stock = [copy, item_id](const LLUUID& new_cat_id)
+ {
+ if (new_cat_id.isNull())
+ {
+ LL_WARNS() << "Failed to create category" << LL_ENDL;
+ LLSD subs;
+ subs["[ERROR_CODE]"] =
+ LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted");
+ LLNotificationsUtil::add("MerchantPasteFailed", subs);
+ return;
+ }
+
+ // Verify we can have this item in that destination category
+ LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id);
+ LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id);
+ if (!dest_cat || !viewer_inv_item)
+ {
+ LL_WARNS() << "Move to marketplace: item or folder do not exist" << LL_ENDL;
+
+ LLSD subs;
+ subs["[ERROR_CODE]"] =
+ LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted");
+ LLNotificationsUtil::add("MerchantPasteFailed", subs);
+ return;
+ }
+ if (!dest_cat->acceptItem(viewer_inv_item))
+ {
+ LLSD subs;
+ subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted");
+ LLNotificationsUtil::add("MerchantPasteFailed", subs);
+ }
+
+ if (copy)
+ {
+ // Copy the item
+ LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_id));
+ copy_inventory_item(
+ gAgent.getID(),
+ viewer_inv_item->getPermissions().getOwner(),
+ viewer_inv_item->getUUID(),
+ new_cat_id,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ // Reparent the item
+ gInventory.changeItemParent(viewer_inv_item, new_cat_id, true);
+ }
+ };
+
+ std::function<void(const LLUUID&)> callback_dest_create = [item_id, callback_create_stock](const LLUUID& new_cat_id)
+ {
+ if (new_cat_id.isNull())
+ {
+ LL_WARNS() << "Failed to create category" << LL_ENDL;
+ LLSD subs;
+ subs["[ERROR_CODE]"] =
+ LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted");
+ LLNotificationsUtil::add("MerchantPasteFailed", subs);
+ return;
+ }
+
+ LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id);
+ LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id);
+ if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) &&
+ (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK))
+ {
+ // We need to create a stock folder to move a no copy item
+ gInventory.createNewCategory(new_cat_id, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName(), callback_create_stock);
+ }
+ else
+ {
+ callback_create_stock(new_cat_id);
+ }
+ };
+
if (depth == 0)
{
// We need a listing folder
- dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName());
- depth++;
+ gInventory.createNewCategory(dest_folder,
+ LLFolderType::FT_NONE,
+ viewer_inv_item->getName(),
+ [callback_dest_create](const LLUUID &new_cat_id)
+ {
+ if (new_cat_id.isNull())
+ {
+ LL_WARNS() << "Failed to create listing folder for marketpace" << LL_ENDL;
+ return;
+ }
+ LLViewerInventoryCategory *dest_cat = gInventory.getCategory(new_cat_id);
+ if (!dest_cat)
+ {
+ LL_WARNS() << "Failed to find freshly created listing folder" << LL_ENDL;
+ return;
+ }
+ // version folder
+ gInventory.createNewCategory(new_cat_id,
+ LLFolderType::FT_NONE,
+ dest_cat->getName(),
+ callback_dest_create);
+ });
}
- if (depth == 1)
+ else if (depth == 1)
{
// We need a version folder
- dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName());
- depth++;
- }
- LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder);
- if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) &&
- (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK))
- {
- // We need to create a stock folder to move a no copy item
- dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName());
- dest_cat = gInventory.getCategory(dest_folder);
- depth++;
- }
-
- // Verify we can have this item in that destination category
- if (!dest_cat->acceptItem(viewer_inv_item))
- {
- LLSD subs;
- subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted");
- LLNotificationsUtil::add("MerchantPasteFailed", subs);
- return false;
- }
-
- if (copy)
- {
- // Copy the item
- LL_INFOS("SLM") << "Copy item '" << make_info(viewer_inv_item) << "' to '" << make_inventory_path(dest_folder) << "'" << LL_ENDL;
- LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder));
- copy_inventory_item(
- gAgent.getID(),
- viewer_inv_item->getPermissions().getOwner(),
- viewer_inv_item->getUUID(),
- dest_folder,
- std::string(),
- cb);
+ gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName(), callback_dest_create);
}
else
{
- LL_INFOS("SLM") << "Move item '" << make_info(viewer_inv_item) << "' to '" << make_inventory_path(dest_folder) << "'" << LL_ENDL;
- // Reparent the item
- gInventory.changeItemParent(viewer_inv_item, dest_folder, true);
+ callback_dest_create(dest_folder);
}
}
else
@@ -1507,7 +1606,7 @@ bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUU
// Reparent the folder
gInventory.changeCategoryParent(viewer_inv_cat, dest_folder, false);
// Check the destination folder recursively for no copy items and promote the including folders if any
- validate_marketplacelistings(dest_cat);
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(dest_folder);
}
// Update the modified folders
@@ -1532,32 +1631,23 @@ bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCa
return cat1->getName().compare(cat2->getName()) < 0;
}
-void dump_trace(std::string& message, S32 depth, LLError::ELevel log_level)
-{
- LL_INFOS() << "validate_marketplacelistings : error = "<< log_level << ", depth = " << depth << ", message = " << message << LL_ENDL;
-}
-
// Make all relevant business logic checks on the marketplace listings starting with the folder as argument.
// This function does no deletion of listings but a mere audit and raises issues to the user (through the
-// optional callback cb). It also returns a boolean, true if things validate, false if issues are raised.
+// optional callback cb).
// The only inventory changes that are done is to move and sort folders containing no-copy items to stock folders.
-bool validate_marketplacelistings(
+// @pending_callbacks - how many callbacks we are waiting for, must be inited before use
+// @result - true if things validate, false if issues are raised, must be inited before use
+typedef boost::function<void(S32 pending_callbacks, bool result)> validation_result_callback_t;
+void validate_marketplacelistings(
LLInventoryCategory* cat,
- validation_callback_t cb,
+ validation_result_callback_t cb_result,
+ LLMarketplaceValidator::validation_msg_callback_t cb_msg,
bool fix_hierarchy,
S32 depth,
- bool notify_observers)
+ bool notify_observers,
+ S32 &pending_callbacks,
+ bool &result)
{
-#if 0
- // Used only for debug
- if (!cb)
- {
- cb = boost::bind(&dump_trace, _1, _2, _3);
- }
-#endif
- // Folder is valid unless issue is raised
- bool result = true;
-
// Get the type and the depth of the folder
LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (cat);
const LLFolderType::EType folder_type = cat->getPreferredType();
@@ -1589,10 +1679,10 @@ bool validate_marketplacelistings(
if (!can_move_folder_to_marketplace(cat, cat, cat, message, 0, fix_hierarchy))
{
result = false;
- if (cb)
+ if (cb_msg)
{
message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + message;
- cb(message,depth,LLError::LEVEL_ERROR);
+ cb_msg(message,depth,LLError::LEVEL_ERROR);
}
}
}
@@ -1602,26 +1692,46 @@ bool validate_marketplacelistings(
{
if (fix_hierarchy)
{
- if (cb)
+ if (cb_msg)
{
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning") + " " + LLTrans::getString("Marketplace Validation Warning Stock");
- cb(message,depth,LLError::LEVEL_WARN);
+ cb_msg(message,depth,LLError::LEVEL_WARN);
}
+
// Nest the stock folder one level deeper in a normal folder and restart from there
+ pending_callbacks++;
LLUUID parent_uuid = cat->getParentUUID();
- LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, LLFolderType::FT_NONE, cat->getName());
- LLInventoryCategory* new_cat = gInventory.getCategory(folder_uuid);
- gInventory.changeCategoryParent(viewer_cat, folder_uuid, false);
- result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy, depth + 1, notify_observers);
- return result;
+ LLUUID cat_uuid = cat->getUUID();
+ gInventory.createNewCategory(parent_uuid,
+ LLFolderType::FT_NONE,
+ cat->getName(),
+ [cat_uuid, cb_result, cb_msg, fix_hierarchy, depth](const LLUUID &new_cat_id)
+ {
+ if (new_cat_id.isNull())
+ {
+ cb_result(0, false);
+ return;
+ }
+ LLInventoryCategory * move_cat = gInventory.getCategory(cat_uuid);
+ LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *)(move_cat);
+ LLInventoryCategory * new_cat = gInventory.getCategory(new_cat_id);
+ gInventory.changeCategoryParent(viewer_cat, new_cat_id, false);
+ S32 pending = 0;
+ bool result = true;
+ validate_marketplacelistings(new_cat, cb_result, cb_msg, fix_hierarchy, depth + 1, true, pending, result);
+ cb_result(pending, result);
+ }
+ );
+ result = false;
+ return;
}
else
{
result = false;
- if (cb)
+ if (cb_msg)
{
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + LLTrans::getString("Marketplace Validation Warning Stock");
- cb(message,depth,LLError::LEVEL_ERROR);
+ cb_msg(message,depth,LLError::LEVEL_ERROR);
}
}
}
@@ -1652,10 +1762,10 @@ bool validate_marketplacelistings(
if (!can_move_to_marketplace(item, error_msg, false))
{
has_bad_items = true;
- if (cb && fix_hierarchy)
+ if (cb_msg && fix_hierarchy)
{
std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg;
- cb(message,depth,LLError::LEVEL_ERROR);
+ cb_msg(message,depth,LLError::LEVEL_ERROR);
}
continue;
}
@@ -1686,35 +1796,35 @@ bool validate_marketplacelistings(
if (depth == 2)
{
// If this is an empty version folder, warn only (listing won't be delivered by AIS, but only AIS should unlist)
- if (cb)
+ if (cb_msg)
{
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Version");
- cb(message,depth,LLError::LEVEL_WARN);
+ cb_msg(message,depth,LLError::LEVEL_WARN);
}
}
else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2))
{
// If this is a legit but empty stock folder, warn only (listing must stay searchable when out of stock)
- if (cb)
+ if (cb_msg)
{
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Stock");
- cb(message,depth,LLError::LEVEL_WARN);
+ cb_msg(message,depth,LLError::LEVEL_WARN);
}
}
- else if (cb)
+ else if (cb_msg)
{
// We warn if there's nothing in a regular folder (may be it's an under construction listing)
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Empty");
- cb(message,depth,LLError::LEVEL_WARN);
+ cb_msg(message,depth,LLError::LEVEL_WARN);
}
}
else
{
// Done with that folder : Print out the folder name unless we already found an error here
- if (cb && result && (depth >= 1))
+ if (cb_msg && result && (depth >= 1))
{
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log");
- cb(message,depth,LLError::LEVEL_INFO);
+ cb_msg(message,depth,LLError::LEVEL_INFO);
}
}
}
@@ -1722,10 +1832,10 @@ bool validate_marketplacelistings(
else if ((count == 1) && !has_bad_items && (((unique_key == default_key) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2) && (cat_array->size() == 0))))
{
// Done with that folder : Print out the folder name unless we already found an error here
- if (cb && result && (depth >= 1))
+ if (cb_msg && result && (depth >= 1))
{
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log");
- cb(message,depth,LLError::LEVEL_INFO);
+ cb_msg(message,depth,LLError::LEVEL_INFO);
}
}
else
@@ -1747,11 +1857,12 @@ bool validate_marketplacelistings(
while (items_vector_it != items_vector.end())
{
// Create a new folder
- LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID());
+ const LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID());
+ const LLUUID origin_uuid = viewer_cat->getUUID();
LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back());
std::string folder_name = (depth >= 1 ? viewer_cat->getName() : viewer_inv_item->getName());
LLFolderType::EType new_folder_type = (items_vector_it->first == default_key ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK);
- if (cb)
+ if (cb_msg)
{
std::string message = "";
if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK)
@@ -1762,30 +1873,71 @@ bool validate_marketplacelistings(
{
message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Version");
}
- cb(message,depth,LLError::LEVEL_WARN);
+ cb_msg(message,depth,LLError::LEVEL_WARN);
}
- LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, new_folder_type, folder_name);
-
- // Move each item to the new folder
- while (!items_vector_it->second.empty())
+
+ pending_callbacks++;
+ std::vector<LLUUID> uuid_vector = items_vector_it->second; // needs to be a copy for lambda
+ gInventory.createNewCategory(
+ parent_uuid,
+ new_folder_type,
+ folder_name,
+ [uuid_vector, cb_result, cb_msg, depth, parent_uuid, origin_uuid, notify_observers](const LLUUID &new_category_id)
{
- LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back());
- if (cb)
+ // Move each item to the new folder
+ std::vector<LLUUID>::const_reverse_iterator iter = uuid_vector.rbegin();
+ while (iter != uuid_vector.rend())
{
- std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move");
- cb(message,depth,LLError::LEVEL_WARN);
+ LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(*iter);
+ if (cb_msg)
+ {
+ std::string indent;
+ for (int i = 1; i < depth; i++)
+ {
+ indent += " ";
+ }
+ std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move");
+ cb_msg(message, depth, LLError::LEVEL_WARN);
+ }
+ gInventory.changeItemParent(viewer_inv_item, new_category_id, true);
+ iter++;
}
- gInventory.changeItemParent(viewer_inv_item, folder_uuid, true);
- items_vector_it->second.pop_back();
- }
-
- // Next type
- update_marketplace_category(parent_uuid);
- update_marketplace_category(folder_uuid);
- if (notify_observers)
- {
- gInventory.notifyObservers();
+
+ if (origin_uuid != parent_uuid)
+ {
+ // We might have moved last item from a folder, check if it needs to be removed
+ LLViewerInventoryCategory* cat = gInventory.getCategory(origin_uuid);
+ if (cat->getDescendentCount() == 0)
+ {
+ // Remove previous folder if it ends up empty
+ if (cb_msg)
+ {
+ std::string indent;
+ for (int i = 1; i < depth; i++)
+ {
+ indent += " ";
+ }
+ std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete");
+ cb_msg(message, depth, LLError::LEVEL_WARN);
+ }
+ gInventory.removeCategory(cat->getUUID());
+ if (notify_observers)
+ {
+ gInventory.notifyObservers();
+ }
+ }
+ }
+
+ // Next type
+ update_marketplace_category(parent_uuid);
+ update_marketplace_category(new_category_id);
+ if (notify_observers)
+ {
+ gInventory.notifyObservers();
+ }
+ cb_result(0, true);
}
+ );
items_vector_it++;
}
}
@@ -1799,11 +1951,11 @@ bool validate_marketplacelistings(
{
LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (*iter);
gInventory.changeCategoryParent(viewer_cat, parent_uuid, false);
- result &= validate_marketplacelistings(viewer_cat, cb, fix_hierarchy, depth, false);
+ validate_marketplacelistings(viewer_cat, cb_result, cb_msg, fix_hierarchy, depth, false, pending_callbacks, result);
}
}
}
- else if (cb)
+ else if (cb_msg)
{
// We are not fixing the hierarchy but reporting problems, report everything we can find
// Print the folder name
@@ -1814,20 +1966,20 @@ bool validate_marketplacelistings(
// Report if a stock folder contains a mix of items
result = false;
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Mixed Stock");
- cb(message,depth,LLError::LEVEL_ERROR);
+ cb_msg(message,depth,LLError::LEVEL_ERROR);
}
else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (cat_array->size() != 0))
{
// Report if a stock folder contains subfolders
result = false;
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Subfolder In Stock");
- cb(message,depth,LLError::LEVEL_ERROR);
+ cb_msg(message,depth,LLError::LEVEL_ERROR);
}
else
{
// Simply print the folder name
std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log");
- cb(message,depth,LLError::LEVEL_INFO);
+ cb_msg(message,depth,LLError::LEVEL_INFO);
}
}
// Scan each item and report if there's a problem
@@ -1842,21 +1994,21 @@ bool validate_marketplacelistings(
// Report items that shouldn't be there to start with
result = false;
std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg;
- cb(message,depth,LLError::LEVEL_ERROR);
+ cb_msg(message,depth,LLError::LEVEL_ERROR);
}
else if ((!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) && (folder_type != LLFolderType::FT_MARKETPLACE_STOCK))
{
// Report stock items that are misplaced
result = false;
std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error Stock Item");
- cb(message,depth,LLError::LEVEL_ERROR);
+ cb_msg(message,depth,LLError::LEVEL_ERROR);
}
else if (depth == 1)
{
// Report items not wrapped in version folder
result = false;
std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Unwrapped Item");
- cb(message,depth,LLError::LEVEL_ERROR);
+ cb_msg(message,depth,LLError::LEVEL_ERROR);
}
}
}
@@ -1865,17 +2017,18 @@ bool validate_marketplacelistings(
if (viewer_cat->getDescendentCount() == 0)
{
// Remove the current folder if it ends up empty
- if (cb)
+ if (cb_msg)
{
std::string message = indent + viewer_cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete");
- cb(message,depth,LLError::LEVEL_WARN);
+ cb_msg(message,depth,LLError::LEVEL_WARN);
}
gInventory.removeCategory(cat->getUUID());
if (notify_observers)
{
gInventory.notifyObservers();
}
- return result && !has_bad_items;
+ result &=!has_bad_items;
+ return;
}
}
@@ -1888,15 +2041,15 @@ bool validate_marketplacelistings(
for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
{
LLInventoryCategory* category = *iter;
- result &= validate_marketplacelistings(category, cb, fix_hierarchy, depth + 1, false);
+ validate_marketplacelistings(category, cb_result, cb_msg, fix_hierarchy, depth + 1, false, pending_callbacks, result);
}
-
+
update_marketplace_category(cat->getUUID(), true, true);
if (notify_observers)
{
gInventory.notifyObservers();
}
- return result && !has_bad_items;
+ result &= !has_bad_items;
}
void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id)
@@ -1996,9 +2149,346 @@ void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::st
inventory_func_type func = boost::bind(&move_items_to_folder, _1, selected_uuids);
gInventory.createNewCategory(first_item->getParentUUID(), LLFolderType::FT_NONE, folder_name, func);
+}
+
+// Returns true if the item can be moved to Current Outfit or any outfit folder.
+bool can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit)
+{
+ LLInventoryType::EType inv_type = inv_item->getInventoryType();
+ if ((inv_type != LLInventoryType::IT_WEARABLE) &&
+ (inv_type != LLInventoryType::IT_GESTURE) &&
+ (inv_type != LLInventoryType::IT_ATTACHMENT) &&
+ (inv_type != LLInventoryType::IT_OBJECT) &&
+ (inv_type != LLInventoryType::IT_SNAPSHOT) &&
+ (inv_type != LLInventoryType::IT_TEXTURE))
+ {
+ return false;
+ }
+
+ U32 flags = inv_item->getFlags();
+ if(flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS)
+ {
+ return false;
+ }
+
+ if((inv_type == LLInventoryType::IT_TEXTURE) || (inv_type == LLInventoryType::IT_SNAPSHOT))
+ {
+ return !move_is_into_current_outfit;
+ }
+
+ if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID()))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Returns TRUE if item is a landmark or a link to a landmark
+// and can be moved to Favorites or Landmarks folder.
+bool can_move_to_landmarks(LLInventoryItem* inv_item)
+{
+ // Need to get the linked item to know its type because LLInventoryItem::getType()
+ // returns actual type AT_LINK for links, not the asset type of a linked item.
+ if (LLAssetType::AT_LINK == inv_item->getType())
+ {
+ LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID());
+ if (linked_item)
+ {
+ return LLAssetType::AT_LANDMARK == linked_item->getType();
+ }
+ }
+
+ return LLAssetType::AT_LANDMARK == inv_item->getType();
+}
+
+// Returns true if folder's content can be moved to Current Outfit or any outfit folder.
+bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit)
+{
+ LLInventoryModel::cat_array_t *cats;
+ LLInventoryModel::item_array_t *items;
+ model->getDirectDescendentsOf(inv_cat->getUUID(), cats, items);
+
+ if (items->size() > wear_limit)
+ {
+ return false;
+ }
+
+ if (items->size() == 0)
+ {
+ // Nothing to move(create)
+ return false;
+ }
+
+ if (cats->size() > 0)
+ {
+ // We do not allow subfolders in outfits of "My Outfits" yet
+ return false;
+ }
+
+ LLInventoryModel::item_array_t::iterator iter = items->begin();
+ LLInventoryModel::item_array_t::iterator end = items->end();
+
+ while (iter != end)
+ {
+ LLViewerInventoryItem *item = *iter;
+ if (!can_move_to_outfit(item, false))
+ {
+ return false;
+ }
+ iter++;
+ }
+
+ return true;
+}
+
+std::string get_localized_folder_name(LLUUID cat_uuid)
+{
+ std::string localized_root_name;
+ const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid);
+ if (cat)
+ {
+ LLFolderType::EType preferred_type = cat->getPreferredType();
+
+ // Translation of Accessories folder in Library inventory folder
+ bool accessories = false;
+ if(cat->getName() == "Accessories")
+ {
+ const LLUUID& parent_folder_id = cat->getParentUUID();
+ accessories = (parent_folder_id == gInventory.getLibraryRootFolderID());
+ }
+
+ //"Accessories" inventory category has folder type FT_NONE. So, this folder
+ //can not be detected as protected with LLFolderType::lookupIsProtectedType
+ localized_root_name.assign(cat->getName());
+ if (accessories || LLFolderType::lookupIsProtectedType(preferred_type))
+ {
+ LLTrans::findString(localized_root_name, std::string("InvFolder ") + cat->getName(), LLSD());
+ }
+ }
+
+ return localized_root_name;
+}
+
+void new_folder_window(const LLUUID& folder_id)
+{
+ LLPanelMainInventory::newFolderWindow(folder_id);
+}
+
+void ungroup_folder_items(const LLUUID& folder_id)
+{
+ LLInventoryCategory* inv_cat = gInventory.getCategory(folder_id);
+ if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType()))
+ {
+ return;
+ }
+ const LLUUID &new_cat_uuid = inv_cat->getParentUUID();
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array);
+ LLInventoryModel::cat_array_t cats = *cat_array;
+ LLInventoryModel::item_array_t items = *item_array;
+
+ for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter)
+ {
+ LLViewerInventoryCategory* cat = *cat_iter;
+ if (cat)
+ {
+ gInventory.changeCategoryParent(cat, new_cat_uuid, false);
+ }
+ }
+ for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter)
+ {
+ LLViewerInventoryItem* item = *item_iter;
+ if(item)
+ {
+ gInventory.changeItemParent(item, new_cat_uuid, false);
+ }
+ }
+ gInventory.removeCategory(inv_cat->getUUID());
+ gInventory.notifyObservers();
+}
+
+std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id)
+{
+ if (model)
+ {
+ const LLInventoryItem *item = model->getItem(item_id);
+ if(item)
+ {
+ std::string desc = item->getDescription();
+ LLStringUtil::toUpper(desc);
+ return desc;
+ }
+ }
+ return LLStringUtil::null;
+}
+
+std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id)
+{
+ if (model)
+ {
+ const LLInventoryItem *item = model->getItem(item_id);
+ if(item)
+ {
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name))
+ {
+ std::string username = av_name.getUserName();
+ LLStringUtil::toUpper(username);
+ return username;
+ }
+ }
+ }
+ return LLStringUtil::null;
+}
+
+std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id)
+{
+ if (model)
+ {
+ const LLViewerInventoryItem *item = model->getItem(item_id);
+ if(item && (item->getIsFullPerm() || gAgent.isGodlikeWithoutAdminMenuFakery()))
+ {
+ std::string uuid = item->getAssetUUID().asString();
+ LLStringUtil::toUpper(uuid);
+ return uuid;
+ }
+ }
+ return LLStringUtil::null;
+}
+
+bool can_share_item(const LLUUID& item_id)
+{
+ bool can_share = false;
+
+ if (gInventory.isObjectDescendentOf(item_id, gInventory.getRootFolderID()))
+ {
+ const LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ if (item)
+ {
+ if (LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item))
+ {
+ can_share = LLGiveInventory::isInventoryGiveAcceptable(item);
+ }
+ }
+ else
+ {
+ can_share = (gInventory.getCategory(item_id) != NULL);
+ }
+
+ const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ if ((item_id == trash_id) || gInventory.isObjectDescendentOf(item_id, trash_id))
+ {
+ can_share = false;
+ }
+ }
+
+ return can_share;
+}
+///----------------------------------------------------------------------------
+/// LLMarketplaceValidator implementations
+///----------------------------------------------------------------------------
+
+LLMarketplaceValidator::LLMarketplaceValidator()
+ : mPendingCallbacks(0)
+ , mValidationInProgress(false)
+{
}
+LLMarketplaceValidator::~LLMarketplaceValidator()
+{
+}
+
+void LLMarketplaceValidator::validateMarketplaceListings(
+ const LLUUID &category_id,
+ LLMarketplaceValidator::validation_done_callback_t cb_done,
+ LLMarketplaceValidator::validation_msg_callback_t cb_msg,
+ bool fix_hierarchy,
+ S32 depth)
+{
+
+ mValidationQueue.emplace(category_id, cb_done, cb_msg, fix_hierarchy, depth);
+ if (!mValidationInProgress)
+ {
+ start();
+ }
+}
+
+void LLMarketplaceValidator::start()
+{
+ if (mValidationQueue.empty())
+ {
+ mValidationInProgress = false;
+ return;
+ }
+ mValidationInProgress = true;
+
+ const ValidationRequest &first = mValidationQueue.front();
+ LLViewerInventoryCategory* cat = gInventory.getCategory(first.mCategoryId);
+ if (!cat)
+ {
+ LL_WARNS() << "Tried to validate a folder that doesn't exist" << LL_ENDL;
+ if (first.mCbDone)
+ {
+ first.mCbDone(false);
+ }
+ mValidationQueue.pop();
+ start();
+ return;
+ }
+
+ validation_result_callback_t result_callback = [](S32 pending, bool result)
+ {
+ LLMarketplaceValidator* validator = LLMarketplaceValidator::getInstance();
+ validator->mPendingCallbacks--; // we just got a callback
+ validator->mPendingCallbacks += pending;
+ validator->mPendingResult &= result;
+ if (validator->mPendingCallbacks <= 0)
+ {
+ llassert(validator->mPendingCallbacks == 0); // shouldn't be below 0
+ const ValidationRequest &first = validator->mValidationQueue.front();
+ if (first.mCbDone)
+ {
+ first.mCbDone(validator->mPendingResult);
+ }
+ validator->mValidationQueue.pop(); // done;
+ validator->start();
+ }
+ };
+
+ mPendingResult = true;
+ mPendingCallbacks = 1; // do '1' in case something decides to callback immediately
+
+ S32 pending_calbacks = 0;
+ bool result = true;
+ validate_marketplacelistings(
+ cat,
+ result_callback,
+ first.mCbMsg,
+ first.mFixHierarchy,
+ first.mDepth,
+ true,
+ pending_calbacks,
+ result);
+
+ result_callback(pending_calbacks, result);
+}
+
+LLMarketplaceValidator::ValidationRequest::ValidationRequest(
+ LLUUID category_id,
+ validation_done_callback_t cb_done,
+ validation_msg_callback_t cb_msg,
+ bool fix_hierarchy,
+ S32 depth)
+: mCategoryId(category_id)
+, mCbDone(cb_done)
+, mCbMsg(cb_msg)
+, mFixHierarchy(fix_hierarchy)
+, mDepth(depth)
+{}
+
///----------------------------------------------------------------------------
/// LLInventoryCollectFunctor implementations
///----------------------------------------------------------------------------
@@ -2182,6 +2672,19 @@ bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat,
}
}
+bool LLFindBrokenLinks::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ // only for broken links getType will be a link
+ // otherwise it's supposed to have the type of an item
+ // it is linked too
+ if (item && LLAssetType::lookupIsLinkType(item->getType()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
bool LLFindWearables::operator()(LLInventoryCategory* cat,
LLInventoryItem* item)
{
@@ -2247,6 +2750,11 @@ void LLFindWearablesOfType::setType(LLWearableType::EType type)
mWearableType = type;
}
+bool LLIsTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ return item && (item->getType() == LLAssetType::AT_TEXTURE);
+}
+
bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
{
if (item)
@@ -2514,8 +3022,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
if ("delete" == action)
{
- static bool sDisplayedAtSession = false;
- const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
bool marketplacelistings_item = false;
LLAllDescendentsPassedFilter f;
for (std::set<LLFolderViewItem*>::iterator it = selected_items.begin(); (it != selected_items.end()) && (f.allDescendentsPassedFilter()); ++it)
@@ -2538,10 +3045,10 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
}
else
{
- if (!sDisplayedAtSession) // ask for the confirmation at least once per session
+ if (!sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session
{
LLNotifications::instance().setIgnored("DeleteItems", false);
- sDisplayedAtSession = true;
+ sDeleteConfirmationDisplayed = true;
}
LLSD args;
@@ -2593,7 +3100,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
LLMultiPreview* multi_previewp = NULL;
- LLMultiProperties* multi_propertiesp = NULL;
+ LLMultiItemProperties* multi_itempropertiesp = nullptr;
if (("task_open" == action || "open" == action) && selected_items.size() > 1)
{
@@ -2627,10 +3134,9 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
}
else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1)
{
- multi_propertiesp = new LLMultiProperties();
- gFloaterView->addChild(multi_propertiesp);
-
- LLFloater::setFloaterHost(multi_propertiesp);
+ multi_itempropertiesp = new LLMultiItemProperties("item_properties");
+ gFloaterView->addChild(multi_itempropertiesp);
+ LLFloater::setFloaterHost(multi_itempropertiesp);
}
std::set<LLUUID> selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs();
@@ -2640,7 +3146,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
if (action == "wear" || action == "wear_add")
{
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
- const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
std::copy_if(selected_uuid_set.begin(),
selected_uuid_set.end(),
std::back_inserter(ids),
@@ -2755,36 +3261,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
{
if (ids.size() == 1)
{
- LLInventoryCategory* inv_cat = gInventory.getCategory(*ids.begin());
- if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType()))
- {
- return;
- }
- const LLUUID &new_cat_uuid = inv_cat->getParentUUID();
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array);
- LLInventoryModel::cat_array_t cats = *cat_array;
- LLInventoryModel::item_array_t items = *item_array;
-
- for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter)
- {
- LLViewerInventoryCategory* cat = *cat_iter;
- if (cat)
- {
- gInventory.changeCategoryParent(cat, new_cat_uuid, false);
- }
- }
- for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter)
- {
- LLViewerInventoryItem* item = *item_iter;
- if(item)
- {
- gInventory.changeItemParent(item, new_cat_uuid, false);
- }
- }
- gInventory.removeCategory(inv_cat->getUUID());
- gInventory.notifyObservers();
+ ungroup_folder_items(*ids.begin());
}
}
else
@@ -2798,6 +3275,14 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
if(!bridge) continue;
bridge->performAction(model, action);
}
+ if(root->isSingleFolderMode() && selected_items.empty())
+ {
+ LLInvFVBridge* bridge = (LLInvFVBridge*)root->getViewModelItem();
+ if(bridge)
+ {
+ bridge->performAction(model, action);
+ }
+ }
}
// Update the marketplace listings that have been affected by the operation
@@ -2808,9 +3293,9 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
{
multi_previewp->openFloater(LLSD());
}
- else if (multi_propertiesp)
+ else if (multi_itempropertiesp)
{
- multi_propertiesp->openFloater(LLSD());
+ multi_itempropertiesp->openFloater(LLSD());
}
}
@@ -2898,7 +3383,7 @@ void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root)
// target listing *and* the original listing. So we need to keep track of both.
// Note: do not however put the marketplace listings root itself in this list or the whole marketplace data will be rebuilt.
sMarketplaceFolders.clear();
- const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
if (marketplacelistings_id.isNull())
{
return;
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index 8c8bd789c2..925217dda3 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -75,6 +75,7 @@ void update_all_marketplace_count();
void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name);
void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null, bool move_no_copy_items = false);
+void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id, bool move_no_copy_items, inventory_func_type callback);
void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items);
@@ -91,13 +92,11 @@ std::string make_info(const LLInventoryObject* object);
// Generates a string containing the path name and id of the object specified by id.
std::string make_inventory_info(const LLUUID& id);
-typedef boost::function<void(std::string& validation_message, S32 depth, LLError::ELevel log_level)> validation_callback_t;
-
bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size = 1, bool from_paste = false);
bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size = 1, bool check_items = true, bool from_paste = false);
bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy = false);
bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false, bool move_no_copy_items = false);
-bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true, S32 depth = -1, bool notify_observers = true);
+
S32 depth_nesting_in_marketplace(LLUUID cur_uuid);
LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth);
S32 compute_stock_count(LLUUID cat_uuid, bool force_count = false);
@@ -108,10 +107,65 @@ void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected
bool is_only_cats_selected(const uuid_vec_t& selected_uuids);
bool is_only_items_selected(const uuid_vec_t& selected_uuids);
+bool can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit);
+bool can_move_to_landmarks(LLInventoryItem* inv_item);
+bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit);
+std::string get_localized_folder_name(LLUUID cat_uuid);
+void new_folder_window(const LLUUID& folder_id);
+void ungroup_folder_items(const LLUUID& folder_id);
+std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id);
+std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id);
+std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id);
+bool can_share_item(const LLUUID& item_id);
+
/** Miscellaneous global functions
** **
*******************************************************************************/
+class LLMarketplaceValidator: public LLSingleton<LLMarketplaceValidator>
+{
+ LLSINGLETON(LLMarketplaceValidator);
+ ~LLMarketplaceValidator();
+ LOG_CLASS(LLMarketplaceValidator);
+public:
+
+ typedef boost::function<void(std::string& validation_message, S32 depth, LLError::ELevel log_level)> validation_msg_callback_t;
+ typedef boost::function<void(bool result)> validation_done_callback_t;
+
+ void validateMarketplaceListings(
+ const LLUUID &category_id,
+ validation_done_callback_t cb_done = NULL,
+ validation_msg_callback_t cb_msg = NULL,
+ bool fix_hierarchy = true,
+ S32 depth = -1);
+
+private:
+ void start();
+
+ class ValidationRequest
+ {
+ public:
+ ValidationRequest(
+ LLUUID category_id,
+ validation_done_callback_t cb_done,
+ validation_msg_callback_t cb_msg,
+ bool fix_hierarchy,
+ S32 depth);
+ LLUUID mCategoryId;
+ validation_done_callback_t mCbDone;
+ validation_msg_callback_t mCbMsg;
+ bool mFixHierarchy;
+ S32 mDepth;
+ };
+
+ bool mValidationInProgress;
+ S32 mPendingCallbacks;
+ bool mPendingResult;
+ // todo: might be a good idea to memorize requests by id and
+ // filter out ones that got multiple validation requests
+ std::queue<ValidationRequest> mValidationQueue;
+};
+
/********************************************************************************
** **
** INVENTORY COLLECTOR FUNCTIONS
@@ -328,6 +382,20 @@ public:
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFindBrokenLinks
+//
+// Collects broken links
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLFindBrokenLinks : public LLInventoryCollectFunctor
+{
+public:
+ LLFindBrokenLinks() {}
+ virtual ~LLFindBrokenLinks() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLFindByMask
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class LLFindByMask : public LLInventoryCollectFunctor
@@ -425,6 +493,15 @@ private:
LLWearableType::EType mWearableType;
};
+class LLIsTextureType : public LLInventoryCollectFunctor
+{
+public:
+ LLIsTextureType() {}
+ virtual ~LLIsTextureType() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+};
+
/** Filter out wearables-links */
class LLFindActualWearablesOfType : public LLFindWearablesOfType
{
@@ -484,7 +561,7 @@ struct LLInventoryAction
static void saveMultipleTextures(const std::vector<std::string>& filenames, std::set<LLFolderViewItem*> selected_items, LLInventoryModel* model);
- static const int sConfirmOnDeleteItemsNumber;
+ static bool sDeleteConfirmationDisplayed;
private:
static void buildMarketplaceFolders(LLFolderView* root);
diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp
new file mode 100644
index 0000000000..4838ba7a47
--- /dev/null
+++ b/indra/newview/llinventorygallery.cpp
@@ -0,0 +1,3824 @@
+/**
+ * @file llinventorygallery.cpp
+ * @brief LLInventoryGallery class implementation
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llinventorygallery.h"
+#include "llinventorygallerymenu.h"
+
+#include "llclipboard.h"
+#include "llcommonutils.h"
+#include "lliconctrl.h"
+#include "llinventorybridge.h"
+#include "llinventoryfunctions.h"
+#include "llinventoryicon.h"
+#include "llinventorymodel.h"
+#include "llinventorymodelbackgroundfetch.h"
+#include "llthumbnailctrl.h"
+#include "lltextbox.h"
+#include "llviewerfoldertype.h"
+
+#include "llagent.h"
+#include "llappearancemgr.h"
+#include "llenvironment.h"
+#include "llfriendcard.h"
+#include "llgesturemgr.h"
+#include "llmarketplacefunctions.h"
+#include "llnotificationsutil.h"
+#include "lloutfitobserver.h"
+#include "lltrans.h"
+#include "llviewerassettype.h"
+#include "llviewermessage.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatarself.h"
+
+static LLPanelInjector<LLInventoryGallery> t_inventory_gallery("inventory_gallery");
+
+const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;
+
+// Helper dnd functions
+BOOL dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, BOOL drop, std::string& tooltip_msg, BOOL is_link);
+BOOL dragItemIntoFolder(LLUUID folder_id, LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm);
+void dropToMyOutfits(LLInventoryCategory* inv_cat);
+
+class LLGalleryPanel: public LLPanel
+{
+public:
+
+ BOOL canFocusChildren() const override
+ {
+ // Tell Tab to not focus children
+ return FALSE;
+ }
+
+protected:
+
+ LLGalleryPanel(const LLPanel::Params& params): LLPanel(params)
+ {
+ };
+
+ friend class LLUICtrlFactory;
+};
+
+//-----------------------------
+// LLInventoryGallery
+//-----------------------------
+
+LLInventoryGallery::LLInventoryGallery(const LLInventoryGallery::Params& p)
+ : LLPanel(),
+ mScrollPanel(NULL),
+ mGalleryPanel(NULL),
+ mLastRowPanel(NULL),
+ mGalleryCreated(false),
+ mRowCount(0),
+ mItemsAddedCount(0),
+ mRowPanelHeight(p.row_panel_height),
+ mVerticalGap(p.vertical_gap),
+ mHorizontalGap(p.horizontal_gap),
+ mItemWidth(p.item_width),
+ mItemHeight(p.item_height),
+ mItemHorizontalGap(p.item_horizontal_gap),
+ mItemsInRow(p.items_in_row),
+ mRowPanWidthFactor(p.row_panel_width_factor),
+ mGalleryWidthFactor(p.gallery_width_factor),
+ mIsInitialized(false),
+ mRootDirty(false),
+ mNeedsArrange(false),
+ mSearchType(LLInventoryFilter::SEARCHTYPE_NAME),
+ mSortOrder(LLInventoryFilter::SO_DATE)
+{
+ updateGalleryWidth();
+ mFilter = new LLInventoryFilter();
+ mCategoriesObserver = new LLInventoryCategoriesObserver();
+ mThumbnailsObserver = new LLThumbnailsObserver();
+ gInventory.addObserver(mThumbnailsObserver);
+
+ mGestureObserver = new LLGalleryGestureObserver(this);
+ LLGestureMgr::instance().addObserver(mGestureObserver);
+
+ mUsername = gAgentUsername;
+ LLStringUtil::toUpper(mUsername);
+}
+
+LLInventoryGallery::Params::Params()
+ : row_panel_height("row_panel_height", 180),
+ vertical_gap("vertical_gap", 10),
+ horizontal_gap("horizontal_gap", 10),
+ item_width("item_width", 150),
+ item_height("item_height", 175),
+ item_horizontal_gap("item_horizontal_gap", 16),
+ items_in_row("items_in_row", GALLERY_ITEMS_PER_ROW_MIN),
+ row_panel_width_factor("row_panel_width_factor", 166),
+ gallery_width_factor("gallery_width_factor", 163)
+{
+ addSynonym(row_panel_height, "row_height");
+}
+
+const LLInventoryGallery::Params& LLInventoryGallery::getDefaultParams()
+{
+ return LLUICtrlFactory::getDefaultParams<LLInventoryGallery>();
+}
+
+BOOL LLInventoryGallery::postBuild()
+{
+ mScrollPanel = getChild<LLScrollContainer>("gallery_scroll_panel");
+ LLPanel::Params params = LLPanel::getDefaultParams();
+ mGalleryPanel = LLUICtrlFactory::create<LLPanel>(params);
+ mMessageTextBox = getChild<LLTextBox>("empty_txt");
+ mInventoryGalleryMenu = new LLInventoryGalleryContextMenu(this);
+ mRootGalleryMenu = new LLInventoryGalleryContextMenu(this);
+ mRootGalleryMenu->setRootFolder(true);
+ return TRUE;
+}
+
+LLInventoryGallery::~LLInventoryGallery()
+{
+ if (gEditMenuHandler == this)
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ delete mInventoryGalleryMenu;
+ delete mRootGalleryMenu;
+ delete mFilter;
+
+ gIdleCallbacks.deleteFunction(onIdle, (void*)this);
+
+ while (!mUnusedRowPanels.empty())
+ {
+ LLPanel* panelp = mUnusedRowPanels.back();
+ mUnusedRowPanels.pop_back();
+ panelp->die();
+ }
+ while (!mUnusedItemPanels.empty())
+ {
+ LLPanel* panelp = mUnusedItemPanels.back();
+ mUnusedItemPanels.pop_back();
+ panelp->die();
+ }
+ while (!mHiddenItems.empty())
+ {
+ LLPanel* panelp = mHiddenItems.back();
+ mHiddenItems.pop_back();
+ panelp->die();
+ }
+
+
+ if (gInventory.containsObserver(mCategoriesObserver))
+ {
+ gInventory.removeObserver(mCategoriesObserver);
+ }
+ delete mCategoriesObserver;
+
+ if (gInventory.containsObserver(mThumbnailsObserver))
+ {
+ gInventory.removeObserver(mThumbnailsObserver);
+ }
+ delete mThumbnailsObserver;
+
+ LLGestureMgr::instance().removeObserver(mGestureObserver);
+ delete mGestureObserver;
+}
+
+void LLInventoryGallery::setRootFolder(const LLUUID cat_id)
+{
+ LLViewerInventoryCategory* category = gInventory.getCategory(cat_id);
+ if(!category || (mFolderID == cat_id))
+ {
+ return;
+ }
+ if(mFolderID.notNull())
+ {
+ mBackwardFolders.push_back(mFolderID);
+ }
+
+ gIdleCallbacks.deleteFunction(onIdle, (void*)this);
+
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ if (mItemMap[id])
+ {
+ mItemMap[id]->setSelected(FALSE);
+ }
+ }
+
+ mFolderID = cat_id;
+ mItemsToSelect.clear();
+ mSelectedItemIDs.clear();
+ mItemBuildQuery.clear();
+ mNeedsArrange = false;
+ dirtyRootFolder();
+}
+
+void LLInventoryGallery::dirtyRootFolder()
+{
+ if (getVisible())
+ {
+ updateRootFolder();
+ }
+ else
+ {
+ mRootDirty = true;
+ }
+}
+
+void LLInventoryGallery::updateRootFolder()
+{
+ llassert(mFolderID.notNull());
+ if (mIsInitialized && mFolderID.notNull())
+ {
+ S32 count = mItemsAddedCount;
+ for (S32 i = count - 1; i >= 0; i--)
+ {
+ updateRemovedItem(mItems[i]->getUUID());
+ }
+ S32 hidden_count = mHiddenItems.size();
+ for (S32 i = hidden_count - 1; i >= 0; i--)
+ {
+ updateRemovedItem(mHiddenItems[i]->getUUID());
+ }
+ mItemBuildQuery.clear();
+
+ if (gInventory.containsObserver(mCategoriesObserver))
+ {
+ gInventory.removeObserver(mCategoriesObserver);
+ }
+ delete mCategoriesObserver;
+
+ mCategoriesObserver = new LLInventoryCategoriesObserver();
+
+ if (gInventory.containsObserver(mThumbnailsObserver))
+ {
+ gInventory.removeObserver(mThumbnailsObserver);
+ }
+ delete mThumbnailsObserver;
+ mThumbnailsObserver = new LLThumbnailsObserver();
+ gInventory.addObserver(mThumbnailsObserver);
+ }
+ {
+ mRootChangedSignal();
+
+ gInventory.addObserver(mCategoriesObserver);
+
+ // Start observing changes in selected category.
+ mCategoriesObserver->addCategory(mFolderID,
+ boost::bind(&LLInventoryGallery::refreshList, this, mFolderID));
+
+ LLViewerInventoryCategory* category = gInventory.getCategory(mFolderID);
+ //If not all items are fetched now
+ // the observer will refresh the list as soon as the new items
+ // arrive.
+ category->fetch();
+
+ //refreshList(cat_id);
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+
+ gInventory.getDirectDescendentsOf(mFolderID, cat_array, item_array);
+
+ // Creating a vector of newly collected sub-categories UUIDs.
+ for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array->begin();
+ iter != cat_array->end();
+ iter++)
+ {
+ mItemBuildQuery.insert((*iter)->getUUID());
+ }
+
+ for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin();
+ iter != item_array->end();
+ iter++)
+ {
+ mItemBuildQuery.insert((*iter)->getUUID());
+ }
+ mIsInitialized = true;
+ mRootDirty = false;
+
+ if (mScrollPanel)
+ {
+ mScrollPanel->goToTop();
+ }
+ }
+
+ LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLInventoryGallery::onCOFChanged, this));
+
+ if (!mGalleryCreated)
+ {
+ initGallery();
+ }
+
+ if (!mItemBuildQuery.empty())
+ {
+ gIdleCallbacks.addFunction(onIdle, (void*)this);
+ }
+}
+
+void LLInventoryGallery::initGallery()
+{
+ if (!mGalleryCreated)
+ {
+ uuid_vec_t cats;
+ getCurrentCategories(cats);
+ int n = cats.size();
+ buildGalleryPanel(n);
+ mScrollPanel->addChild(mGalleryPanel);
+ for (int i = 0; i < n; i++)
+ {
+ addToGallery(mItemMap[cats[i]]);
+ }
+ reArrangeRows();
+ mGalleryCreated = true;
+ }
+}
+
+void LLInventoryGallery::draw()
+{
+ LLPanel::draw();
+ if (mGalleryCreated)
+ {
+ if(!updateRowsIfNeeded())
+ {
+ handleModifiedFilter();
+ }
+ }
+}
+
+void LLInventoryGallery::onVisibilityChange(BOOL new_visibility)
+{
+ if (new_visibility)
+ {
+ if (mRootDirty)
+ {
+ updateRootFolder();
+ }
+ else if (mNeedsArrange)
+ {
+ gIdleCallbacks.addFunction(onIdle, (void*)this);
+ }
+ }
+ LLPanel::onVisibilityChange(new_visibility);
+}
+
+bool LLInventoryGallery::updateRowsIfNeeded()
+{
+ S32 scroll_content_width = mScrollPanel ? mScrollPanel->getVisibleContentRect().getWidth() : getRect().getWidth();
+ if(((scroll_content_width - mRowPanelWidth) > mItemWidth)
+ && mRowCount > 1)
+ {
+ reArrangeRows(1);
+ return true;
+ }
+ else if((mRowPanelWidth > (scroll_content_width + mItemHorizontalGap))
+ && mItemsInRow > GALLERY_ITEMS_PER_ROW_MIN)
+ {
+ reArrangeRows(-1);
+ return true;
+ }
+ return false;
+}
+
+bool compareGalleryItem(LLInventoryGalleryItem* item1, LLInventoryGalleryItem* item2, bool sort_by_date, bool sort_folders_by_name)
+{
+ if (item1->getSortGroup() != item2->getSortGroup())
+ {
+ return (item1->getSortGroup() < item2->getSortGroup());
+ }
+
+ if(sort_folders_by_name && (item1->getSortGroup() != LLInventoryGalleryItem::SG_ITEM))
+ {
+ std::string name1 = item1->getItemName();
+ std::string name2 = item2->getItemName();
+
+ return (LLStringUtil::compareDict(name1, name2) < 0);
+ }
+
+ if(((item1->isDefaultImage() && item2->isDefaultImage()) || (!item1->isDefaultImage() && !item2->isDefaultImage())))
+ {
+ if(sort_by_date)
+ {
+ return item1->getCreationDate() > item2->getCreationDate();
+ }
+ else
+ {
+ std::string name1 = item1->getItemName();
+ std::string name2 = item2->getItemName();
+
+ return (LLStringUtil::compareDict(name1, name2) < 0);
+ }
+ }
+ else
+ {
+ return item2->isDefaultImage();
+ }
+}
+
+void LLInventoryGallery::reArrangeRows(S32 row_diff)
+{
+ std::vector<LLInventoryGalleryItem*> buf_items = mItems;
+ for (std::vector<LLInventoryGalleryItem*>::const_reverse_iterator it = buf_items.rbegin(); it != buf_items.rend(); ++it)
+ {
+ removeFromGalleryLast(*it, false);
+ }
+ for (std::vector<LLInventoryGalleryItem*>::const_reverse_iterator it = mHiddenItems.rbegin(); it != mHiddenItems.rend(); ++it)
+ {
+ buf_items.push_back(*it);
+ }
+ mHiddenItems.clear();
+
+ mItemsInRow+= row_diff;
+ updateGalleryWidth();
+
+ bool sort_by_date = (mSortOrder & LLInventoryFilter::SO_DATE);
+ bool sort_folders_by_name = (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME);
+ std::sort(buf_items.begin(), buf_items.end(), [sort_by_date, sort_folders_by_name](LLInventoryGalleryItem* item1, LLInventoryGalleryItem* item2)
+ {
+ return compareGalleryItem(item1, item2, sort_by_date, sort_folders_by_name);
+ });
+
+ for (std::vector<LLInventoryGalleryItem*>::const_iterator it = buf_items.begin(); it != buf_items.end(); ++it)
+ {
+ (*it)->setHidden(false);
+ applyFilter(*it, mFilterSubString);
+ addToGallery(*it);
+ }
+ mFilter->clearModified();
+ updateMessageVisibility();
+}
+
+void LLInventoryGallery::updateGalleryWidth()
+{
+ mRowPanelWidth = mRowPanWidthFactor * mItemsInRow - mItemHorizontalGap;
+ mGalleryWidth = mGalleryWidthFactor * mItemsInRow - mItemHorizontalGap;
+}
+
+LLPanel* LLInventoryGallery::addLastRow()
+{
+ mRowCount++;
+ int row = 0;
+ int vgap = mVerticalGap * row;
+ LLPanel* result = buildRowPanel(0, row * mRowPanelHeight + vgap);
+ mGalleryPanel->addChild(result);
+ return result;
+}
+
+void LLInventoryGallery::moveRowUp(int row)
+{
+ moveRow(row, mRowCount - 1 - row + 1);
+}
+
+void LLInventoryGallery::moveRowDown(int row)
+{
+ moveRow(row, mRowCount - 1 - row - 1);
+}
+
+void LLInventoryGallery::moveRow(int row, int pos)
+{
+ int vgap = mVerticalGap * pos;
+ moveRowPanel(mRowPanels[row], 0, pos * mRowPanelHeight + vgap);
+}
+
+void LLInventoryGallery::removeLastRow()
+{
+ mRowCount--;
+ mGalleryPanel->removeChild(mLastRowPanel);
+ mUnusedRowPanels.push_back(mLastRowPanel);
+ mRowPanels.pop_back();
+ if (mRowPanels.size() > 0)
+ {
+ // Just removed last row
+ mLastRowPanel = mRowPanels.back();
+ }
+ else
+ {
+ mLastRowPanel = NULL;
+ }
+}
+
+LLPanel* LLInventoryGallery::addToRow(LLPanel* row_stack, LLInventoryGalleryItem* item, int pos, int hgap)
+{
+ LLPanel* lpanel = buildItemPanel(pos * mItemWidth + hgap);
+ lpanel->addChild(item);
+ row_stack->addChild(lpanel);
+ mItemPanels.push_back(lpanel);
+ return lpanel;
+}
+
+void LLInventoryGallery::addToGallery(LLInventoryGalleryItem* item)
+{
+ if(item->isHidden())
+ {
+ mHiddenItems.push_back(item);
+ return;
+ }
+ mItemIndexMap[item] = mItemsAddedCount;
+ mIndexToItemMap[mItemsAddedCount] = item;
+ mItemsAddedCount++;
+ int n = mItemsAddedCount;
+ int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1;
+ int n_prev = n - 1;
+ int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1;
+
+ bool add_row = row_count != row_count_prev;
+ int pos = 0;
+ if (add_row)
+ {
+ for (int i = 0; i < row_count_prev; i++)
+ {
+ moveRowUp(i);
+ }
+ mLastRowPanel = addLastRow();
+ mRowPanels.push_back(mLastRowPanel);
+ }
+ pos = (n - 1) % mItemsInRow;
+ mItems.push_back(item);
+ addToRow(mLastRowPanel, item, pos, mHorizontalGap * pos);
+ reshapeGalleryPanel(row_count);
+}
+
+
+void LLInventoryGallery::removeFromGalleryLast(LLInventoryGalleryItem* item, bool needs_reshape)
+{
+ if(item->isHidden())
+ {
+ mHiddenItems.pop_back();
+ // Note: item still exists!!!
+ return;
+ }
+ int n_prev = mItemsAddedCount;
+ int n = mItemsAddedCount - 1;
+ int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1;
+ int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1;
+ mItemsAddedCount--;
+ mIndexToItemMap.erase(mItemsAddedCount);
+
+ bool remove_row = row_count != row_count_prev;
+ removeFromLastRow(mItems[mItemsAddedCount]);
+ mItems.pop_back();
+ if (remove_row)
+ {
+ for (int i = 0; i < row_count_prev - 1; i++)
+ {
+ moveRowDown(i);
+ }
+ removeLastRow();
+ }
+ if (needs_reshape)
+ {
+ reshapeGalleryPanel(row_count);
+ }
+}
+
+
+void LLInventoryGallery::removeFromGalleryMiddle(LLInventoryGalleryItem* item)
+{
+ if(item->isHidden())
+ {
+ mHiddenItems.erase(std::remove(mHiddenItems.begin(), mHiddenItems.end(), item), mHiddenItems.end());
+ // item still exists and needs to be deleted or used!!!
+ return;
+ }
+ int n = mItemIndexMap[item];
+ mItemIndexMap.erase(item);
+ mIndexToItemMap.erase(n);
+ std::vector<LLInventoryGalleryItem*> saved;
+ for (int i = mItemsAddedCount - 1; i > n; i--)
+ {
+ saved.push_back(mItems[i]);
+ removeFromGalleryLast(mItems[i]);
+ }
+ removeFromGalleryLast(mItems[n]);
+ int saved_count = saved.size();
+ for (int i = 0; i < saved_count; i++)
+ {
+ addToGallery(saved.back());
+ saved.pop_back();
+ }
+}
+
+void LLInventoryGallery::removeFromLastRow(LLInventoryGalleryItem* item)
+{
+ mItemPanels.back()->removeChild(item);
+ mLastRowPanel->removeChild(mItemPanels.back());
+ mUnusedItemPanels.push_back(mItemPanels.back());
+ mItemPanels.pop_back();
+}
+
+LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn)
+{
+ LLInventoryGalleryItem::Params giparams;
+ giparams.visible = true;
+ giparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP);
+ giparams.rect(LLRect(0,mItemHeight, mItemWidth, 0));
+ LLInventoryGalleryItem* gitem = LLUICtrlFactory::create<LLInventoryGalleryItem>(giparams);
+ gitem->setItemName(name);
+ gitem->setUUID(item_id);
+ gitem->setGallery(this);
+ gitem->setType(type, inventory_type, flags, is_link);
+ gitem->setThumbnail(thumbnail_id);
+ gitem->setWorn(is_worn);
+ gitem->setCreatorName(get_searchable_creator_name(&gInventory, item_id));
+ gitem->setDescription(get_searchable_description(&gInventory, item_id));
+ gitem->setAssetIDStr(get_searchable_UUID(&gInventory, item_id));
+ gitem->setCreationDate(creation_date);
+ return gitem;
+}
+
+void LLInventoryGallery::buildGalleryPanel(int row_count)
+{
+ LLPanel::Params params;
+ params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP);
+ params.visible = true;
+ params.use_bounding_rect = false;
+ mGalleryPanel = LLUICtrlFactory::create<LLGalleryPanel>(params);
+ reshapeGalleryPanel(row_count);
+}
+
+void LLInventoryGallery::reshapeGalleryPanel(int row_count)
+{
+ int bottom = 0;
+ int left = 0;
+ int height = row_count * (mRowPanelHeight + mVerticalGap);
+ LLRect rect = LLRect(left, bottom + height, left + mGalleryWidth, bottom);
+ mGalleryPanel->setRect(rect);
+ mGalleryPanel->reshape(mGalleryWidth, height);
+}
+
+LLPanel* LLInventoryGallery::buildItemPanel(int left)
+{
+ int top = 0;
+ LLPanel* lpanel = NULL;
+ if(mUnusedItemPanels.empty())
+ {
+ LLPanel::Params lpparams;
+ lpparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP);
+ lpparams.visible = true;
+ lpparams.rect(LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top));
+ lpparams.use_bounding_rect = false;
+ lpparams.focus_root = false;
+ //lpparams.tab_stop = false;
+ lpanel = LLUICtrlFactory::create<LLPanel>(lpparams);
+ }
+ else
+ {
+ lpanel = mUnusedItemPanels.back();
+ mUnusedItemPanels.pop_back();
+
+ LLRect rect = LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top);
+ lpanel->setShape(rect, false);
+ }
+ return lpanel;
+}
+
+LLPanel* LLInventoryGallery::buildRowPanel(int left, int bottom)
+{
+ LLPanel* stack = NULL;
+ if(mUnusedRowPanels.empty())
+ {
+ LLPanel::Params sparams;
+ sparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP);
+ sparams.use_bounding_rect = false;
+ sparams.visible = true;
+ sparams.focus_root = false;
+ //sparams.tab_stop = false;
+ stack = LLUICtrlFactory::create<LLPanel>(sparams);
+ }
+ else
+ {
+ stack = mUnusedRowPanels.back();
+ mUnusedRowPanels.pop_back();
+ }
+ moveRowPanel(stack, left, bottom);
+ return stack;
+}
+
+void LLInventoryGallery::moveRowPanel(LLPanel* stack, int left, int bottom)
+{
+ LLRect rect = LLRect(left, bottom + mRowPanelHeight, left + mRowPanelWidth, bottom);
+ stack->setRect(rect);
+ stack->reshape(mRowPanelWidth, mRowPanelHeight);
+}
+
+void LLInventoryGallery::setFilterSubString(const std::string& string)
+{
+ mFilterSubString = string;
+ mFilter->setFilterSubString(string);
+
+ //reArrangeRows();
+}
+
+bool LLInventoryGallery::applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring)
+{
+ if(item)
+ {
+ bool visible = checkAgainstFilters(item, filter_substring);
+ item->setHidden(!visible);
+ return visible;
+ }
+ return false;
+}
+
+bool LLInventoryGallery::checkAgainstFilters(LLInventoryGalleryItem* item, const std::string& filter_substring)
+{
+ if (!item) return false;
+
+ if (item->isFolder() && (mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS))
+ {
+ return true;
+ }
+
+ if(item->isLink() && ((mFilter->getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LINKS) == 0) && !filter_substring.empty())
+ {
+ return false;
+ }
+
+ bool hidden = false;
+
+ if(mFilter->getFilterCreatorType() == LLInventoryFilter::FILTERCREATOR_SELF)
+ {
+ hidden = (item->getCreatorName() == mUsername) || item->isFolder();
+ }
+ else if(mFilter->getFilterCreatorType() == LLInventoryFilter::FILTERCREATOR_OTHERS)
+ {
+ hidden = (item->getCreatorName() != mUsername) || item->isFolder();
+ }
+ if(hidden)
+ {
+ return false;
+ }
+
+ if(!mFilter->checkAgainstFilterThumbnails(item->getUUID()))
+ {
+ return false;
+ }
+
+ if(!checkAgainstFilterType(item->getUUID()))
+ {
+ return false;
+ }
+
+ std::string desc;
+ switch(mSearchType)
+ {
+ case LLInventoryFilter::SEARCHTYPE_CREATOR:
+ desc = item->getCreatorName();
+ break;
+ case LLInventoryFilter::SEARCHTYPE_DESCRIPTION:
+ desc = item->getDescription();
+ break;
+ case LLInventoryFilter::SEARCHTYPE_UUID:
+ desc = item->getAssetIDStr();
+ break;
+ case LLInventoryFilter::SEARCHTYPE_NAME:
+ default:
+ desc = item->getItemName() + item->getItemNameSuffix();
+ break;
+ }
+
+ LLStringUtil::toUpper(desc);
+
+ std::string cur_filter = filter_substring;
+ LLStringUtil::toUpper(cur_filter);
+
+ hidden = (std::string::npos == desc.find(cur_filter));
+ return !hidden;
+}
+
+void LLInventoryGallery::onIdle(void* userdata)
+{
+ LLInventoryGallery* self = (LLInventoryGallery*)userdata;
+
+ if (!self->mIsInitialized || !self->mGalleryCreated)
+ {
+ self->mNeedsArrange = false;
+ return;
+ }
+
+ bool visible = self->getVisible(); // In visible chain?
+ const F64 MAX_TIME_VISIBLE = 0.020f;
+ const F64 MAX_TIME_HIDDEN = 0.001f; // take it slow
+ const F64 max_time = visible ? MAX_TIME_VISIBLE : MAX_TIME_HIDDEN;
+ F64 curent_time = LLTimer::getTotalSeconds();
+ const F64 end_time = curent_time + max_time;
+
+ while (!self->mItemBuildQuery.empty() && end_time > curent_time)
+ {
+ uuid_set_t::iterator iter = self->mItemBuildQuery.begin();
+ LLUUID item_id = *iter;
+ self->mNeedsArrange |= self->updateAddedItem(item_id);
+ self->mItemBuildQuery.erase(iter);
+ curent_time = LLTimer::getTotalSeconds();
+ }
+
+ if (self->mNeedsArrange && visible)
+ {
+ self->mNeedsArrange = false;
+ self->reArrangeRows();
+ self->updateMessageVisibility();
+ }
+
+ if (!self->mItemsToSelect.empty() && !self->mNeedsArrange)
+ {
+ selection_deque selection_list(self->mItemsToSelect);
+ self->mItemsToSelect.clear();
+ for (LLUUID & item_to_select : selection_list)
+ {
+ self->addItemSelection(item_to_select, true);
+ }
+ }
+
+ if (self->mItemsToSelect.empty() && self->mItemBuildQuery.empty())
+ {
+ gIdleCallbacks.deleteFunction(onIdle, (void*)self);
+ }
+}
+
+void LLInventoryGallery::setSearchType(LLInventoryFilter::ESearchType type)
+{
+ if(mSearchType != type)
+ {
+ mSearchType = type;
+ if(!mFilterSubString.empty())
+ {
+ reArrangeRows();
+ }
+ }
+}
+
+void LLInventoryGallery::getCurrentCategories(uuid_vec_t& vcur)
+{
+ for (gallery_item_map_t::const_iterator iter = mItemMap.begin();
+ iter != mItemMap.end();
+ iter++)
+ {
+ if ((*iter).second != NULL)
+ {
+ vcur.push_back((*iter).first);
+ }
+ }
+}
+
+bool LLInventoryGallery::updateAddedItem(LLUUID item_id)
+{
+ LLInventoryObject* obj = gInventory.getObject(item_id);
+ if (!obj)
+ {
+ LL_WARNS("InventoryGallery") << "Failed to find item: " << item_id << LL_ENDL;
+ return false;
+ }
+
+ std::string name = obj->getName();
+ LLUUID thumbnail_id = obj->getThumbnailUUID();;
+ LLInventoryType::EType inventory_type(LLInventoryType::IT_CATEGORY);
+ U32 misc_flags = 0;
+ bool is_worn = false;
+ LLInventoryItem* inv_item = gInventory.getItem(item_id);
+ if (inv_item)
+ {
+ inventory_type = inv_item->getInventoryType();
+ misc_flags = inv_item->getFlags();
+ if (LLAssetType::AT_GESTURE == obj->getType())
+ {
+ is_worn = LLGestureMgr::instance().isGestureActive(item_id);
+ }
+ else
+ {
+ is_worn = LLAppearanceMgr::instance().isLinkedInCOF(item_id);
+ }
+ }
+ else if (LLAssetType::AT_CATEGORY == obj->getType())
+ {
+ name = get_localized_folder_name(item_id);
+ if(thumbnail_id.isNull())
+ {
+ thumbnail_id = getOutfitImageID(item_id);
+ }
+ }
+
+ bool res = false;
+
+ LLInventoryGalleryItem* item = buildGalleryItem(name, item_id, obj->getType(), thumbnail_id, inventory_type, misc_flags, obj->getCreationDate(), obj->getIsLinkType(), is_worn);
+ mItemMap.insert(LLInventoryGallery::gallery_item_map_t::value_type(item_id, item));
+ if (mGalleryCreated)
+ {
+ res = applyFilter(item, mFilterSubString);
+ addToGallery(item);
+ }
+
+ mThumbnailsObserver->addItem(item_id,
+ boost::bind(&LLInventoryGallery::updateItemThumbnail, this, item_id));
+ return res;
+}
+
+void LLInventoryGallery::updateRemovedItem(LLUUID item_id)
+{
+ gallery_item_map_t::iterator item_iter = mItemMap.find(item_id);
+ if (item_iter != mItemMap.end())
+ {
+ mThumbnailsObserver->removeItem(item_id);
+
+ LLInventoryGalleryItem* item = item_iter->second;
+
+ deselectItem(item_id);
+ mItemMap.erase(item_iter);
+ removeFromGalleryMiddle(item);
+
+ // kill removed item
+ if (item != NULL)
+ {
+ // Todo: instead of deleting, store somewhere to reuse later
+ item->die();
+ }
+ }
+
+ mItemBuildQuery.erase(item_id);
+}
+
+void LLInventoryGallery::updateChangedItemName(LLUUID item_id, std::string name)
+{
+ gallery_item_map_t::iterator iter = mItemMap.find(item_id);
+ if (iter != mItemMap.end())
+ {
+ LLInventoryGalleryItem* item = iter->second;
+ if (item)
+ {
+ item->setItemName(name);
+ }
+ }
+}
+
+void LLInventoryGallery::updateWornItem(LLUUID item_id, bool is_worn)
+{
+ gallery_item_map_t::iterator iter = mItemMap.find(item_id);
+ if (iter != mItemMap.end())
+ {
+ LLInventoryGalleryItem* item = iter->second;
+ if (item)
+ {
+ item->setWorn(is_worn);
+ }
+ }
+}
+
+void LLInventoryGallery::updateItemThumbnail(LLUUID item_id)
+{
+ LLInventoryObject* obj = gInventory.getObject(item_id);
+ if (!obj)
+ {
+ return;
+ }
+ LLUUID thumbnail_id = obj->getThumbnailUUID();
+
+ if ((LLAssetType::AT_CATEGORY == obj->getType()) && thumbnail_id.isNull())
+ {
+ thumbnail_id = getOutfitImageID(item_id);
+ }
+
+ if (mItemMap[item_id])
+ {
+ mItemMap[item_id]->setThumbnail(thumbnail_id);
+
+ bool passes_filter = checkAgainstFilters(mItemMap[item_id], mFilterSubString);
+ if((mItemMap[item_id]->isHidden() && passes_filter)
+ || (!mItemMap[item_id]->isHidden() && !passes_filter))
+ {
+ reArrangeRows();
+ }
+ }
+}
+
+BOOL LLInventoryGallery::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mSelectedItemIDs.size() > 0)
+ {
+ setFocus(true);
+ }
+ mLastInteractedUUID = LLUUID::null;
+
+ // Scroll is going to always return true
+ BOOL res = LLPanel::handleRightMouseDown(x, y, mask);
+
+ if (mLastInteractedUUID.isNull()) // no child were hit
+ {
+ clearSelection();
+ if (mInventoryGalleryMenu && mFolderID.notNull())
+ {
+ uuid_vec_t selected_uuids;
+ selected_uuids.push_back(mFolderID);
+ mRootGalleryMenu->show(this, selected_uuids, x, y);
+ return TRUE;
+ }
+ }
+ return res;
+}
+
+
+BOOL LLInventoryGallery::handleKeyHere(KEY key, MASK mask)
+{
+ BOOL handled = FALSE;
+ switch (key)
+ {
+ case KEY_RETURN:
+ // Open selected items if enter key hit on the inventory panel
+ if (mask == MASK_NONE && mInventoryGalleryMenu && mSelectedItemIDs.size() == 1)
+ {
+ selection_deque::iterator iter = mSelectedItemIDs.begin();
+ LLViewerInventoryCategory* category = gInventory.getCategory(*iter);
+ if (category)
+ {
+ setRootFolder(*iter);
+ handled = TRUE;
+ }
+ else
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*iter);
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(), *iter, &gInventory);
+ }
+ }
+ }
+ handled = TRUE;
+ break;
+ case KEY_DELETE:
+#if LL_DARWIN
+ case KEY_BACKSPACE:
+#endif
+ // Delete selected items if delete or backspace key hit on the inventory panel
+ // Note: on Mac laptop keyboards, backspace and delete are one and the same
+ if (canDeleteSelection())
+ {
+ deleteSelection();
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_F2:
+ mFilterSubString.clear();
+ if (mInventoryGalleryMenu && mSelectedItemIDs.size() == 1)
+ {
+ mInventoryGalleryMenu->rename(mSelectedItemIDs.front());
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_PAGE_UP:
+ mFilterSubString.clear();
+ if (mScrollPanel)
+ {
+ mScrollPanel->pageUp(30);
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_PAGE_DOWN:
+ mFilterSubString.clear();
+ if (mScrollPanel)
+ {
+ mScrollPanel->pageDown(30);
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_HOME:
+ mFilterSubString.clear();
+ if (mScrollPanel)
+ {
+ mScrollPanel->goToTop();
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_END:
+ mFilterSubString.clear();
+ if (mScrollPanel)
+ {
+ mScrollPanel->goToBottom();
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_LEFT:
+ moveLeft(mask);
+ handled = TRUE;
+ break;
+
+ case KEY_RIGHT:
+ moveRight(mask);
+ handled = TRUE;
+ break;
+
+ case KEY_UP:
+ moveUp(mask);
+ handled = TRUE;
+ break;
+
+ case KEY_DOWN:
+ moveDown(mask);
+ handled = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ if (handled)
+ {
+ mInventoryGalleryMenu->hide();
+ }
+
+ return handled;
+}
+
+void LLInventoryGallery::moveUp(MASK mask)
+{
+ mFilterSubString.clear();
+
+ if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1)
+ {
+ LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID];
+ if (item)
+ {
+ if (mask == MASK_NONE || mask == MASK_CONTROL)
+ {
+ S32 n = mItemIndexMap[item];
+ n -= mItemsInRow;
+ if (n >= 0)
+ {
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ if (mask == MASK_CONTROL)
+ {
+ addItemSelection(item_id, true);
+ }
+ else
+ {
+ changeItemSelection(item_id, true);
+ }
+ item->setFocus(TRUE);
+ claimEditHandler();
+ }
+ }
+ else if (mask == MASK_SHIFT)
+ {
+ S32 n = mItemIndexMap[item];
+ S32 target = llmax(0, n - mItemsInRow);
+ if (target != n)
+ {
+ item = mIndexToItemMap[target];
+ toggleSelectionRangeFromLast(item->getUUID());
+ item->setFocus(TRUE);
+ claimEditHandler();
+ }
+ }
+ }
+ }
+}
+
+void LLInventoryGallery::moveDown(MASK mask)
+{
+ mFilterSubString.clear();
+
+ if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1)
+ {
+ LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID];
+ if (item)
+ {
+ if (mask == MASK_NONE || mask == MASK_CONTROL)
+ {
+ S32 n = mItemIndexMap[item];
+ n += mItemsInRow;
+ if (n < mItemsAddedCount)
+ {
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ if (mask == MASK_CONTROL)
+ {
+ addItemSelection(item_id, true);
+ }
+ else
+ {
+ changeItemSelection(item_id, true);
+ }
+ item->setFocus(TRUE);
+ claimEditHandler();
+ }
+ }
+ else if (mask == MASK_SHIFT)
+ {
+ S32 n = mItemIndexMap[item];
+ S32 target = llmin(mItemsAddedCount - 1, n + mItemsInRow);
+ if (target != n)
+ {
+ item = mIndexToItemMap[target];
+ toggleSelectionRangeFromLast(item->getUUID());
+ item->setFocus(TRUE);
+ claimEditHandler();
+ }
+ }
+ }
+ }
+}
+
+void LLInventoryGallery::moveLeft(MASK mask)
+{
+ mFilterSubString.clear();
+
+ if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1)
+ {
+ LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID];
+ if (mask == MASK_SHIFT)
+ {
+ item = mItemMap[mLastInteractedUUID];
+ }
+ if (item)
+ {
+ // Might be better to get item from panel
+ S32 n = mItemIndexMap[item];
+ n--;
+ if (n < 0)
+ {
+ n = mItemsAddedCount - 1;
+ }
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ if (mask == MASK_CONTROL)
+ {
+ addItemSelection(item_id, true);
+ }
+ else if (mask == MASK_SHIFT)
+ {
+ if (item->isSelected())
+ {
+ toggleItemSelection(mLastInteractedUUID, true);
+ }
+ else
+ {
+ toggleItemSelection(item_id, true);
+ }
+ mLastInteractedUUID = item_id;
+ }
+ else
+ {
+ changeItemSelection(item_id, true);
+ }
+ item->setFocus(TRUE);
+ claimEditHandler();
+ }
+ }
+}
+
+void LLInventoryGallery::moveRight(MASK mask)
+{
+ mFilterSubString.clear();
+
+ if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1)
+ {
+ LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID];
+ if (item)
+ {
+ S32 n = mItemIndexMap[item];
+ n++;
+ if (n == mItemsAddedCount)
+ {
+ n = 0;
+ }
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ if (mask == MASK_CONTROL)
+ {
+ addItemSelection(item_id, true);
+ }
+ else if (mask == MASK_SHIFT)
+ {
+ if (item->isSelected())
+ {
+ toggleItemSelection(mLastInteractedUUID, true);
+ }
+ else
+ {
+ toggleItemSelection(item_id, true);
+ }
+ mLastInteractedUUID = item_id;
+ }
+ else
+ {
+ changeItemSelection(item_id, true);
+ }
+ item->setFocus(TRUE);
+ claimEditHandler();
+ }
+ }
+}
+
+void LLInventoryGallery::toggleSelectionRange(S32 start_idx, S32 end_idx)
+{
+ LLInventoryGalleryItem* item = NULL;
+ if (end_idx > start_idx)
+ {
+ for (S32 i = start_idx; i <= end_idx; i++)
+ {
+ item = mIndexToItemMap[i];
+ LLUUID item_id = item->getUUID();
+ toggleItemSelection(item_id, true);
+ }
+ }
+ else
+ {
+ for (S32 i = start_idx; i >= end_idx; i--)
+ {
+ item = mIndexToItemMap[i];
+ LLUUID item_id = item->getUUID();
+ toggleItemSelection(item_id, true);
+ }
+ }
+}
+
+void LLInventoryGallery::toggleSelectionRangeFromLast(const LLUUID target)
+{
+ if (mLastInteractedUUID == target)
+ {
+ return;
+ }
+ LLInventoryGalleryItem* last_item = mItemMap[mLastInteractedUUID];
+ LLInventoryGalleryItem* next_item = mItemMap[target];
+ if (last_item && next_item)
+ {
+ S32 last_idx = mItemIndexMap[last_item];
+ S32 next_idx = mItemIndexMap[next_item];
+ if (next_item->isSelected())
+ {
+ if (last_idx < next_idx)
+ {
+ toggleSelectionRange(last_idx, next_idx - 1);
+ }
+ else
+ {
+ toggleSelectionRange(last_idx, next_idx + 1);
+ }
+ }
+ else
+ {
+ if (last_idx < next_idx)
+ {
+ toggleSelectionRange(last_idx + 1, next_idx);
+ }
+ else
+ {
+ toggleSelectionRange(last_idx - 1, next_idx);
+ }
+ }
+ }
+ mLastInteractedUUID = next_item->getUUID();
+}
+
+void LLInventoryGallery::onFocusLost()
+{
+ // inventory no longer handles cut/copy/paste/delete
+ if (gEditMenuHandler == this)
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ LLPanel::onFocusLost();
+
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ if (mItemMap[id])
+ {
+ mItemMap[id]->setSelected(false);
+ }
+ }
+}
+
+void LLInventoryGallery::onFocusReceived()
+{
+ // inventory now handles cut/copy/paste/delete
+ gEditMenuHandler = this;
+
+ // Tab support, when tabbing into this view, select first item
+ if (mSelectedItemIDs.size() > 0)
+ {
+ LLInventoryGalleryItem* focus_item = NULL;
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ if (mItemMap[id])
+ {
+ focus_item = mItemMap[id];
+ focus_item->setSelected(true);
+ }
+ }
+ if (focus_item)
+ {
+ focus_item->setFocus(TRUE);
+ }
+ }
+ else if (mIndexToItemMap.size() > 0 && mItemsToSelect.empty())
+ {
+ // choose any items from visible rect
+ S32 vert_offset = mScrollPanel->getDocPosVertical();
+ S32 panel_size = mVerticalGap + mRowPanelHeight;
+ S32 n = llclamp((S32)(vert_offset / panel_size) * mItemsInRow, 0, (S32)(mIndexToItemMap.size() - 1) );
+
+ LLInventoryGalleryItem* focus_item = mIndexToItemMap[n];
+ changeItemSelection(focus_item->getUUID(), true);
+ focus_item->setFocus(TRUE);
+ }
+
+ LLPanel::onFocusReceived();
+}
+
+void LLInventoryGallery::showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id)
+{
+ if (mInventoryGalleryMenu && item_id.notNull())
+ {
+ if (std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) == mSelectedItemIDs.end())
+ {
+ changeItemSelection(item_id, false);
+ }
+ uuid_vec_t selected_uuids(mSelectedItemIDs.begin(), mSelectedItemIDs.end());
+ mInventoryGalleryMenu->show(ctrl, selected_uuids, x, y);
+ }
+}
+
+void LLInventoryGallery::changeItemSelection(const LLUUID& item_id, bool scroll_to_selection)
+{
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ if (mItemMap[id])
+ {
+ mItemMap[id]->setSelected(FALSE);
+ }
+ }
+ mSelectedItemIDs.clear();
+ mItemsToSelect.clear();
+
+ if ((mItemMap.count(item_id) == 0) || mNeedsArrange)
+ {
+ mItemsToSelect.push_back(item_id);
+ return;
+ }
+ if (mSelectedItemIDs.size() == 1
+ && std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) != mSelectedItemIDs.end())
+ {
+ // Already selected
+ mLastInteractedUUID = item_id;
+ return;
+ }
+
+ if (mItemMap[item_id])
+ {
+ mItemMap[item_id]->setSelected(TRUE);
+ }
+ mSelectedItemIDs.push_back(item_id);
+ signalSelectionItemID(item_id);
+ mLastInteractedUUID = item_id;
+
+ if (scroll_to_selection)
+ {
+ scrollToShowItem(item_id);
+ }
+}
+
+void LLInventoryGallery::addItemSelection(const LLUUID& item_id, bool scroll_to_selection)
+{
+ if ((mItemMap.count(item_id) == 0) || mNeedsArrange)
+ {
+ mItemsToSelect.push_back(item_id);
+ return;
+ }
+ if (std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) != mSelectedItemIDs.end())
+ {
+ // Already selected
+ mLastInteractedUUID = item_id;
+ return;
+ }
+
+ if (mItemMap[item_id])
+ {
+ mItemMap[item_id]->setSelected(TRUE);
+ }
+ mSelectedItemIDs.push_back(item_id);
+ signalSelectionItemID(item_id);
+ mLastInteractedUUID = item_id;
+
+ if (scroll_to_selection)
+ {
+ scrollToShowItem(item_id);
+ }
+}
+
+bool LLInventoryGallery::toggleItemSelection(const LLUUID& item_id, bool scroll_to_selection)
+{
+ bool result = false;
+ if ((mItemMap.count(item_id) == 0) || mNeedsArrange)
+ {
+ mItemsToSelect.push_back(item_id);
+ return result;
+ }
+ selection_deque::iterator found = std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id);
+ if (found != mSelectedItemIDs.end())
+ {
+ if (mItemMap[item_id])
+ {
+ mItemMap[item_id]->setSelected(FALSE);
+ }
+ mSelectedItemIDs.erase(found);
+ result = false;
+ }
+ else
+ {
+ if (mItemMap[item_id])
+ {
+ mItemMap[item_id]->setSelected(TRUE);
+ }
+ mSelectedItemIDs.push_back(item_id);
+ signalSelectionItemID(item_id);
+ result = true;
+ }
+ mLastInteractedUUID = item_id;
+
+ if (scroll_to_selection)
+ {
+ scrollToShowItem(item_id);
+ }
+ return result;
+}
+
+void LLInventoryGallery::scrollToShowItem(const LLUUID& item_id)
+{
+ LLInventoryGalleryItem* item = mItemMap[item_id];
+ if(item)
+ {
+ const LLRect visible_content_rect = mScrollPanel->getVisibleContentRect();
+
+ LLRect item_rect;
+ item->localRectToOtherView(item->getLocalRect(), &item_rect, mScrollPanel);
+ LLRect overlap_rect(item_rect);
+ overlap_rect.intersectWith(visible_content_rect);
+
+ //Scroll when the selected item is outside the visible area
+ if (overlap_rect.getHeight() + 5 < item->getRect().getHeight())
+ {
+ LLRect content_rect = mScrollPanel->getContentWindowRect();
+ LLRect constraint_rect;
+ constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight());
+
+ LLRect item_doc_rect;
+ item->localRectToOtherView(item->getLocalRect(), &item_doc_rect, mGalleryPanel);
+
+ mScrollPanel->scrollToShowRect( item_doc_rect, constraint_rect );
+ }
+ }
+}
+
+LLInventoryGalleryItem* LLInventoryGallery::getFirstSelectedItem()
+{
+ if (mSelectedItemIDs.size() > 0)
+ {
+ selection_deque::iterator iter = mSelectedItemIDs.begin();
+ return mItemMap[*iter];
+ }
+ return NULL;
+}
+
+void LLInventoryGallery::copy()
+{
+ if (!getVisible() || !getEnabled())
+ {
+ return;
+ }
+
+ LLClipboard::instance().reset();
+
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ LLClipboard::instance().addToClipboard(id);
+ }
+ mFilterSubString.clear();
+}
+
+BOOL LLInventoryGallery::canCopy() const
+{
+ if (!getVisible() || !getEnabled() || mSelectedItemIDs.empty())
+ {
+ return FALSE;
+ }
+
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ if (!isItemCopyable(id))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void LLInventoryGallery::cut()
+{
+ if (!getVisible() || !getEnabled())
+ {
+ return;
+ }
+
+ // clear the inventory clipboard
+ LLClipboard::instance().reset();
+ LLClipboard::instance().setCutMode(true);
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ // todo: fade out selected item
+ LLClipboard::instance().addToClipboard(id);
+ }
+
+ mFilterSubString.clear();
+}
+
+BOOL LLInventoryGallery::canCut() const
+{
+ if (!getVisible() || !getEnabled() || mSelectedItemIDs.empty())
+ {
+ return FALSE;
+ }
+
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(id);
+ if (cat)
+ {
+ if (!get_is_category_removable(&gInventory, id))
+ {
+ return FALSE;
+ }
+ }
+ else if (!get_is_item_removable(&gInventory, id))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void LLInventoryGallery::paste()
+{
+ if (!LLClipboard::instance().hasContents())
+ {
+ return;
+ }
+
+ const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ if (mSelectedItemIDs.size() == 1 && gInventory.isObjectDescendentOf(*mSelectedItemIDs.begin(), marketplacelistings_id))
+ {
+ return;
+ }
+
+ bool is_cut_mode = LLClipboard::instance().isCutMode();
+ std::vector<LLUUID> objects;
+ LLClipboard::instance().pasteFromClipboard(objects);
+
+ bool paste_into_root = mSelectedItemIDs.empty();
+ for (LLUUID& dest : mSelectedItemIDs)
+ {
+ LLInventoryObject* obj = gInventory.getObject(dest);
+ if (!obj || (obj->getType() != LLAssetType::AT_CATEGORY))
+ {
+ paste_into_root = true;
+ continue;
+ }
+
+ paste(dest, objects, is_cut_mode, marketplacelistings_id);
+ is_cut_mode = false;
+ }
+
+ if (paste_into_root)
+ {
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ if (mItemMap[id])
+ {
+ mItemMap[id]->setSelected(FALSE);
+ }
+ }
+ mSelectedItemIDs.clear();
+
+ paste(mFolderID, objects, is_cut_mode, marketplacelistings_id);
+ }
+
+ LLClipboard::instance().setCutMode(false);
+}
+
+void LLInventoryGallery::paste(const LLUUID& dest,
+ std::vector<LLUUID>& objects,
+ bool is_cut_mode,
+ const LLUUID& marketplacelistings_id)
+{
+ LLHandle<LLPanel> handle = getHandle();
+ std::function <void(const LLUUID)> on_copy_callback = NULL;
+ LLPointer<LLInventoryCallback> cb = NULL;
+ if (dest == mFolderID)
+ {
+ on_copy_callback = [handle](const LLUUID& inv_item)
+ {
+ LLInventoryGallery* panel = (LLInventoryGallery*)handle.get();
+ if (panel)
+ {
+ // Scroll to pasted item and highlight it
+ // Should it only highlight the last one?
+ panel->addItemSelection(inv_item, true);
+ }
+ };
+ cb = new LLBoostFuncInventoryCallback(on_copy_callback);
+ }
+
+ for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter)
+ {
+ const LLUUID& item_id = (*iter);
+ if (gInventory.isObjectDescendentOf(item_id, marketplacelistings_id) && (LLMarketplaceData::instance().isInActiveFolder(item_id) ||
+ LLMarketplaceData::instance().isListedAndActive(item_id)))
+ {
+ return;
+ }
+ LLViewerInventoryCategory* cat = gInventory.getCategory(item_id);
+ if (cat)
+ {
+ if (is_cut_mode)
+ {
+ gInventory.changeCategoryParent(cat, dest, false);
+ if (dest == mFolderID)
+ {
+ // Don't select immediately, wait for item to arrive
+ mItemsToSelect.push_back(item_id);
+ }
+ }
+ else
+ {
+ copy_inventory_category(&gInventory, cat, dest, LLUUID::null, false, on_copy_callback);
+ }
+ }
+ else
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if (item)
+ {
+ if (is_cut_mode)
+ {
+ gInventory.changeItemParent(item, dest, false);
+ if (dest == mFolderID)
+ {
+ // Don't select immediately, wait for item to arrive
+ mItemsToSelect.push_back(item_id);
+ }
+ }
+ else
+ {
+ if (item->getIsLinkType())
+ {
+ link_inventory_object(dest, item_id, cb);
+ }
+ else
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ dest,
+ std::string(),
+ cb);
+ }
+ }
+ }
+ }
+ }
+
+ LLClipboard::instance().setCutMode(false);
+}
+
+BOOL LLInventoryGallery::canPaste() const
+{
+ // Return FALSE on degenerated cases: empty clipboard, no inventory, no agent
+ if (!LLClipboard::instance().hasContents())
+ {
+ return FALSE;
+ }
+
+ // In cut mode, whatever is on the clipboard is always pastable
+ if (LLClipboard::instance().isCutMode())
+ {
+ return TRUE;
+ }
+
+ // In normal mode, we need to check each element of the clipboard to know if we can paste or not
+ std::vector<LLUUID> objects;
+ LLClipboard::instance().pasteFromClipboard(objects);
+ S32 count = objects.size();
+ for (S32 i = 0; i < count; i++)
+ {
+ const LLUUID& item_id = objects.at(i);
+
+ // Each item must be copyable to be pastable
+ if (!isItemCopyable(item_id))
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void LLInventoryGallery::onDelete(const LLSD& notification, const LLSD& response, const selection_deque selected_ids)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0)
+ {
+ for (const LLUUID& id : selected_ids)
+ {
+ LLInventoryObject* obj = gInventory.getObject(id);
+ if (!obj)
+ {
+ return;
+ }
+ if (obj->getType() == LLAssetType::AT_CATEGORY)
+ {
+ if (get_is_category_removable(&gInventory, id))
+ {
+ gInventory.removeCategory(id);
+ }
+ }
+ else
+ {
+ if (get_is_item_removable(&gInventory, id))
+ {
+ gInventory.removeItem(id);
+ }
+ }
+ }
+ }
+}
+
+void LLInventoryGallery::deleteSelection()
+{
+ if (!LLInventoryAction::sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session
+ {
+ LLNotifications::instance().setIgnored("DeleteItems", false);
+ LLInventoryAction::sDeleteConfirmationDisplayed = true;
+ }
+
+ LLSD args;
+ args["QUESTION"] = LLTrans::getString("DeleteItem");
+ LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryGallery::onDelete, _1, _2, mSelectedItemIDs));
+}
+
+bool LLInventoryGallery::canDeleteSelection()
+{
+ if (mSelectedItemIDs.empty())
+ {
+ return false;
+ }
+
+ const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ if (mFolderID == trash_id || gInventory.isObjectDescendentOf(mFolderID, trash_id))
+ {
+ return false;
+ }
+
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(id);
+ if (cat)
+ {
+ if (!get_is_category_removable(&gInventory, id))
+ {
+ return false;
+ }
+ }
+ else if (!get_is_item_removable(&gInventory, id))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void LLInventoryGallery::pasteAsLink()
+{
+ if (!LLClipboard::instance().hasContents())
+ {
+ return;
+ }
+
+ const LLUUID& current_outfit_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ const LLUUID& my_outifts_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+
+ std::vector<LLUUID> objects;
+ LLClipboard::instance().pasteFromClipboard(objects);
+
+ bool paste_into_root = mSelectedItemIDs.empty();
+ for (LLUUID& dest : mSelectedItemIDs)
+ {
+ LLInventoryObject* obj = gInventory.getObject(dest);
+ if (!obj || obj->getType() != LLAssetType::AT_CATEGORY)
+ {
+ paste_into_root = true;
+ continue;
+ }
+
+ pasteAsLink(dest, objects, current_outfit_id, marketplacelistings_id, my_outifts_id);
+ }
+
+ if (paste_into_root)
+ {
+ for (const LLUUID& id : mSelectedItemIDs)
+ {
+ if (mItemMap[id])
+ {
+ mItemMap[id]->setSelected(FALSE);
+ }
+ }
+ mSelectedItemIDs.clear();
+
+ pasteAsLink(mFolderID, objects, current_outfit_id, marketplacelistings_id, my_outifts_id);
+ }
+
+ LLClipboard::instance().setCutMode(false);
+}
+
+void LLInventoryGallery::pasteAsLink(const LLUUID& dest,
+ std::vector<LLUUID>& objects,
+ const LLUUID& current_outfit_id,
+ const LLUUID& marketplacelistings_id,
+ const LLUUID& my_outifts_id)
+{
+ const BOOL move_is_into_current_outfit = (dest == current_outfit_id);
+ const BOOL move_is_into_my_outfits = (dest == my_outifts_id) || gInventory.isObjectDescendentOf(dest, my_outifts_id);
+ const BOOL move_is_into_marketplacelistings = gInventory.isObjectDescendentOf(dest, marketplacelistings_id);
+
+ if (move_is_into_marketplacelistings || move_is_into_current_outfit || move_is_into_my_outfits)
+ {
+ return;
+ }
+
+ LLPointer<LLInventoryCallback> cb = NULL;
+ if (dest == mFolderID)
+ {
+ LLHandle<LLPanel> handle = getHandle();
+ std::function <void(const LLUUID)> on_link_callback = [handle](const LLUUID& inv_item)
+ {
+ LLInventoryGallery* panel = (LLInventoryGallery*)handle.get();
+ if (panel)
+ {
+ // Scroll to pasted item and highlight it
+ // Should it only highlight the last one?
+ panel->addItemSelection(inv_item, true);
+ }
+ };
+ cb = new LLBoostFuncInventoryCallback(on_link_callback);
+ }
+
+ for (std::vector<LLUUID>::const_iterator iter = objects.begin();
+ iter != objects.end();
+ ++iter)
+ {
+ const LLUUID& object_id = (*iter);
+ if (LLConstPointer<LLInventoryObject> link_obj = gInventory.getObject(object_id))
+ {
+ link_inventory_object(dest, link_obj, cb);
+ }
+ }
+}
+
+void LLInventoryGallery::claimEditHandler()
+{
+ gEditMenuHandler = this;
+}
+
+void LLInventoryGallery::resetEditHandler()
+{
+ if (gEditMenuHandler == this)
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+bool LLInventoryGallery::isItemCopyable(const LLUUID & item_id)
+{
+ const LLInventoryCategory* cat = gInventory.getCategory(item_id);
+ if (cat)
+ {
+ // Folders are copyable if items in them are, recursively, copyable.
+ // Get the content of the folder
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ gInventory.getDirectDescendentsOf(item_id, cat_array, item_array);
+
+ // Check the items
+ LLInventoryModel::item_array_t item_array_copy = *item_array;
+ for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++)
+ {
+ LLInventoryItem* item = *iter;
+ if (!isItemCopyable(item->getUUID()))
+ {
+ return false;
+ }
+ }
+
+ // Check the folders
+ LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
+ for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
+ {
+ LLViewerInventoryCategory* category = *iter;
+ if (!isItemCopyable(category->getUUID()))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if (item)
+ {
+ // Can't copy worn objects.
+ // Worn objects are tied to their inworld conterparts
+ // Copy of modified worn object will return object with obsolete asset and inventory
+ if (get_is_item_worn(item_id))
+ {
+ return false;
+ }
+
+ static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true);
+ return (item->getIsLinkType() && inventory_linking)
+ || item->getPermissions().allowCopyBy(gAgent.getID());
+ }
+
+ return false;
+}
+
+void LLInventoryGallery::updateMessageVisibility()
+{
+
+ mMessageTextBox->setVisible(mItems.empty());
+ if(mItems.empty())
+ {
+ mMessageTextBox->setText(hasDescendents(mFolderID) ? LLTrans::getString("InventorySingleFolderEmpty") : LLTrans::getString("InventorySingleFolderNoMatches"));
+ }
+
+ mScrollPanel->setVisible(!mItems.empty());
+}
+
+void LLInventoryGallery::refreshList(const LLUUID& category_id)
+{
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+
+ gInventory.getDirectDescendentsOf(category_id, cat_array, item_array);
+ uuid_vec_t vadded;
+ uuid_vec_t vremoved;
+
+ // Create added and removed items vectors.
+ computeDifference(*cat_array, *item_array, vadded, vremoved);
+
+ // Handle added tabs.
+ for (uuid_vec_t::const_iterator iter = vadded.begin();
+ iter != vadded.end();
+ ++iter)
+ {
+ const LLUUID cat_id = (*iter);
+ updateAddedItem(cat_id);
+ mNeedsArrange = true;
+ }
+
+ // Handle removed tabs.
+ for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter)
+ {
+ const LLUUID cat_id = (*iter);
+ updateRemovedItem(cat_id);
+ }
+
+ const LLInventoryModel::changed_items_t& changed_items = gInventory.getChangedIDs();
+ for (LLInventoryModel::changed_items_t::const_iterator items_iter = changed_items.begin();
+ items_iter != changed_items.end();
+ ++items_iter)
+ {
+ LLInventoryObject* obj = gInventory.getObject(*items_iter);
+ if(!obj)
+ {
+ return;
+ }
+
+ updateChangedItemName(*items_iter, obj->getName());
+ mNeedsArrange = true;
+ }
+
+ if(mNeedsArrange || !mItemsToSelect.empty())
+ {
+ // Don't scroll to target/arrange immediately
+ // since more updates might be pending
+ gIdleCallbacks.addFunction(onIdle, (void*)this);
+ }
+ updateMessageVisibility();
+}
+
+void LLInventoryGallery::computeDifference(
+ const LLInventoryModel::cat_array_t vcats,
+ const LLInventoryModel::item_array_t vitems,
+ uuid_vec_t& vadded,
+ uuid_vec_t& vremoved)
+{
+ uuid_vec_t vnew;
+ // Creating a vector of newly collected UUIDs.
+ for (LLInventoryModel::cat_array_t::const_iterator iter = vcats.begin();
+ iter != vcats.end();
+ iter++)
+ {
+ vnew.push_back((*iter)->getUUID());
+ }
+ for (LLInventoryModel::item_array_t::const_iterator iter = vitems.begin();
+ iter != vitems.end();
+ iter++)
+ {
+ vnew.push_back((*iter)->getUUID());
+ }
+
+ uuid_vec_t vcur;
+ getCurrentCategories(vcur);
+ std::copy(mItemBuildQuery.begin(), mItemBuildQuery.end(), std::back_inserter(vcur));
+
+ LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved);
+}
+
+void LLInventoryGallery::onCOFChanged()
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+
+ gInventory.collectDescendents(
+ LLAppearanceMgr::instance().getCOF(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+
+ uuid_vec_t vnew;
+ uuid_vec_t vadded;
+ uuid_vec_t vremoved;
+
+ for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
+ iter != item_array.end();
+ ++iter)
+ {
+ vnew.push_back((*iter)->getLinkedUUID());
+ }
+
+ // We need to update only items that were added or removed from COF.
+ LLCommonUtils::computeDifference(vnew, mCOFLinkedItems, vadded, vremoved);
+
+ mCOFLinkedItems = vnew;
+
+ for (uuid_vec_t::const_iterator iter = vadded.begin();
+ iter != vadded.end();
+ ++iter)
+ {
+ updateWornItem(*iter, true);
+ }
+
+ for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter)
+ {
+ updateWornItem(*iter, false);
+ }
+}
+
+void LLInventoryGallery::onGesturesChanged()
+{
+ uuid_vec_t vnew;
+ uuid_vec_t vadded;
+ uuid_vec_t vremoved;
+
+ const LLGestureMgr::item_map_t& active_gestures = LLGestureMgr::instance().getActiveGestures();
+ for (LLGestureMgr::item_map_t::const_iterator iter = active_gestures.begin();
+ iter != active_gestures.end();
+ ++iter)
+ {
+ vnew.push_back(iter->first);
+ }
+
+ LLCommonUtils::computeDifference(vnew, mActiveGestures, vadded, vremoved);
+
+ mActiveGestures = vnew;
+
+ for (uuid_vec_t::const_iterator iter = vadded.begin();
+ iter != vadded.end();
+ ++iter)
+ {
+ updateWornItem(*iter, true);
+ }
+
+ for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter)
+ {
+ updateWornItem(*iter, false);
+ }
+}
+
+void LLInventoryGallery::deselectItem(const LLUUID& category_id)
+{
+ // Reset selection if the item is selected.
+ LLInventoryGalleryItem* item = mItemMap[category_id];
+ if (item && item->isSelected())
+ {
+ mItemMap[category_id]->setSelected(FALSE);
+ setFocus(true);
+ // Todo: support multiselect
+ // signalSelectionItemID(LLUUID::null);
+ }
+
+ selection_deque::iterator found = std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), category_id);
+ if (found != mSelectedItemIDs.end())
+ {
+ mSelectedItemIDs.erase(found);
+ }
+}
+
+void LLInventoryGallery::clearSelection()
+{
+ for (const LLUUID& id: mSelectedItemIDs)
+ {
+ if (mItemMap[id])
+ {
+ mItemMap[id]->setSelected(FALSE);
+ }
+ }
+ if (!mSelectedItemIDs.empty())
+ {
+ mSelectedItemIDs.clear();
+ // BUG: wrong, item can be null
+ signalSelectionItemID(LLUUID::null);
+ }
+}
+
+void LLInventoryGallery::signalSelectionItemID(const LLUUID& category_id)
+{
+ mSelectionChangeSignal(category_id);
+}
+
+boost::signals2::connection LLInventoryGallery::setSelectionChangeCallback(selection_change_callback_t cb)
+{
+ return mSelectionChangeSignal.connect(cb);
+}
+
+LLUUID LLInventoryGallery::getFirstSelectedItemID()
+{
+ if (mSelectedItemIDs.size() > 0)
+ {
+ return *mSelectedItemIDs.begin();
+ }
+ return LLUUID::null;
+}
+
+LLUUID LLInventoryGallery::getOutfitImageID(LLUUID outfit_id)
+{
+ LLUUID thumbnail_id;
+ LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id);
+ if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT)
+ {
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ // Not LLIsOfAssetType, because we allow links
+ LLIsTextureType f;
+ gInventory.getDirectDescendentsOf(outfit_id, cats, items, f);
+
+ // Exactly one texture found => show the texture as thumbnail
+ if (1 == items.size())
+ {
+ LLViewerInventoryItem* item = items.front();
+ if (item && item->getIsLinkType())
+ {
+ item = item->getLinkedItem();
+ }
+ if (item)
+ {
+ thumbnail_id = item->getAssetUUID();
+ }
+ }
+ }
+ return thumbnail_id;
+}
+
+boost::signals2::connection LLInventoryGallery::setRootChangedCallback(callback_t cb)
+{
+ return mRootChangedSignal.connect(cb);
+}
+
+void LLInventoryGallery::onForwardFolder()
+{
+ if(isForwardAvailable())
+ {
+ mBackwardFolders.push_back(mFolderID);
+ mFolderID = mForwardFolders.back();
+ mForwardFolders.pop_back();
+ dirtyRootFolder();
+ }
+}
+
+void LLInventoryGallery::onBackwardFolder()
+{
+ if(isBackwardAvailable())
+ {
+ mForwardFolders.push_back(mFolderID);
+ mFolderID = mBackwardFolders.back();
+ mBackwardFolders.pop_back();
+ dirtyRootFolder();
+ }
+}
+
+void LLInventoryGallery::clearNavigationHistory()
+{
+ mForwardFolders.clear();
+ mBackwardFolders.clear();
+}
+
+bool LLInventoryGallery::isBackwardAvailable()
+{
+ return (!mBackwardFolders.empty() && (mFolderID != mBackwardFolders.back()));
+}
+
+bool LLInventoryGallery::isForwardAvailable()
+{
+ return (!mForwardFolders.empty() && (mFolderID != mForwardFolders.back()));
+}
+
+BOOL LLInventoryGallery::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type, void* cargo_data,
+ EAcceptance* accept, std::string& tooltip_msg)
+{
+ // have children handle it first
+ BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data,
+ accept, tooltip_msg);
+
+ // when drop is not handled by child, it should be handled by the root folder .
+ if (!handled || (*accept == ACCEPT_NO))
+ {
+ handled = baseHandleDragAndDrop(mFolderID, drop, cargo_type, cargo_data, accept, tooltip_msg);
+ }
+
+ return handled;
+}
+
+void LLInventoryGallery::startDrag()
+{
+ std::vector<EDragAndDropType> types;
+ uuid_vec_t ids;
+ LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_AGENT;
+ for (LLUUID& selected_id : mSelectedItemIDs)
+ {
+ const LLInventoryItem* item = gInventory.getItem(selected_id);
+ if (item)
+ {
+ if (item->getPermissions().getOwner() == ALEXANDRIA_LINDEN_ID)
+ {
+ src = LLToolDragAndDrop::SOURCE_LIBRARY;
+ }
+
+ EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(item->getType());
+ types.push_back(type);
+ ids.push_back(selected_id);
+ }
+
+ const LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id);
+ if (cat && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID())
+ && !LLFolderType::lookupIsProtectedType((cat)->getPreferredType()))
+ {
+ if (cat->getOwnerID() == ALEXANDRIA_LINDEN_ID)
+ {
+ src = LLToolDragAndDrop::SOURCE_LIBRARY;
+ }
+
+ EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(cat->getType());
+ types.push_back(type);
+ ids.push_back(selected_id);
+ }
+ }
+ LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, LLToolDragAndDrop::SOURCE_AGENT);
+}
+
+bool LLInventoryGallery::areViewsInitialized()
+{
+ return mGalleryCreated && mItemBuildQuery.empty();
+}
+
+bool LLInventoryGallery::hasDescendents(const LLUUID& cat_id)
+{
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(cat_id, cats, items);
+
+ return (cats->empty() && items->empty());
+}
+
+bool LLInventoryGallery::checkAgainstFilterType(const LLUUID& object_id)
+{
+ const LLInventoryObject *object = gInventory.getObject(object_id);
+ if(!object) return false;
+
+ LLInventoryType::EType object_type = LLInventoryType::IT_CATEGORY;
+ LLInventoryItem* inv_item = gInventory.getItem(object_id);
+ if (inv_item)
+ {
+ object_type = inv_item->getInventoryType();
+ }
+ const U32 filterTypes = mFilter->getFilterTypes();
+
+ if ((filterTypes & LLInventoryFilter::FILTERTYPE_OBJECT) && inv_item)
+ {
+ switch (object_type)
+ {
+ case LLInventoryType::IT_NONE:
+ // If it has no type, pass it, unless it's a link.
+ if (object && object->getIsLinkType())
+ {
+ return false;
+ }
+ break;
+ case LLInventoryType::IT_UNKNOWN:
+ {
+ // Unknows are only shown when we show every type.
+ // Unknows are 255 and won't fit in 64 bits.
+ if (mFilter->getFilterObjectTypes() != 0xffffffffffffffffULL)
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ if ((1LL << object_type & mFilter->getFilterObjectTypes()) == U64(0))
+ {
+ return false;
+ }
+ break;
+ }
+ }
+
+ if (filterTypes & LLInventoryFilter::FILTERTYPE_DATE)
+ {
+ const U16 HOURS_TO_SECONDS = 3600;
+ time_t earliest = time_corrected() - mFilter->getHoursAgo() * HOURS_TO_SECONDS;
+
+ if (mFilter->getMinDate() > time_min() && mFilter->getMinDate() < earliest)
+ {
+ earliest = mFilter->getMinDate();
+ }
+ else if (!mFilter->getHoursAgo())
+ {
+ earliest = 0;
+ }
+
+ if (LLInventoryFilter::FILTERDATEDIRECTION_NEWER == mFilter->getDateSearchDirection() || mFilter->isSinceLogoff())
+ {
+ if (object->getCreationDate() < earliest ||
+ object->getCreationDate() > mFilter->getMaxDate())
+ return false;
+ }
+ else
+ {
+ if (object->getCreationDate() > earliest ||
+ object->getCreationDate() > mFilter->getMaxDate())
+ return false;
+ }
+ }
+ return true;
+}
+
+bool LLInventoryGallery::hasVisibleItems()
+{
+ return mItemsAddedCount > 0;
+}
+
+void LLInventoryGallery::handleModifiedFilter()
+{
+ if(mFilter->isModified())
+ {
+ reArrangeRows();
+ }
+}
+
+void LLInventoryGallery::setSortOrder(U32 order, bool update)
+{
+ bool dirty = (mSortOrder != order);
+
+ mSortOrder = order;
+ if(update && dirty)
+ {
+ mNeedsArrange = true;
+ gIdleCallbacks.addFunction(onIdle, (void*)this);
+ }
+}
+//-----------------------------
+// LLInventoryGalleryItem
+//-----------------------------
+
+static LLDefaultChildRegistry::Register<LLInventoryGalleryItem> r("inventory_gallery_item");
+
+LLInventoryGalleryItem::LLInventoryGalleryItem(const Params& p)
+ : LLPanel(p),
+ mSelected(false),
+ mDefaultImage(true),
+ mItemName(""),
+ mWornSuffix(""),
+ mPermSuffix(""),
+ mUUID(LLUUID()),
+ mIsFolder(true),
+ mIsLink(false),
+ mHidden(false),
+ mGallery(NULL),
+ mType(LLAssetType::AT_NONE),
+ mSortGroup(SG_ITEM),
+ mCutGeneration(0),
+ mSelectedForCut(false)
+{
+ buildFromFile("panel_inventory_gallery_item.xml");
+}
+
+LLInventoryGalleryItem::~LLInventoryGalleryItem()
+{
+}
+
+BOOL LLInventoryGalleryItem::postBuild()
+{
+ mNameText = getChild<LLTextBox>("item_name");
+ mTextBgPanel = getChild<LLPanel>("text_bg_panel");
+
+ return TRUE;
+}
+
+void LLInventoryGalleryItem::setType(LLAssetType::EType type, LLInventoryType::EType inventory_type, U32 flags, bool is_link)
+{
+ mType = type;
+ mIsFolder = (mType == LLAssetType::AT_CATEGORY);
+ mIsLink = is_link;
+
+ std::string icon_name = LLInventoryIcon::getIconName(mType, inventory_type, flags);
+ if(mIsFolder)
+ {
+ mSortGroup = SG_NORMAL_FOLDER;
+ LLUUID folder_id = mUUID;
+ if(mIsLink)
+ {
+ LLInventoryObject* obj = gInventory.getObject(mUUID);
+ if (obj)
+ {
+ folder_id = obj->getLinkedUUID();
+ }
+ }
+ LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id);
+ if (cat)
+ {
+ LLFolderType::EType preferred_type = cat->getPreferredType();
+ icon_name = LLViewerFolderType::lookupIconName(preferred_type);
+
+ if (preferred_type == LLFolderType::FT_TRASH)
+ {
+ mSortGroup = SG_TRASH_FOLDER;
+ }
+ else if(LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
+ {
+ mSortGroup = SG_SYSTEM_FOLDER;
+ }
+ }
+ }
+ else
+ {
+ const LLInventoryItem *item = gInventory.getItem(mUUID);
+ if(item && (LLAssetType::AT_CALLINGCARD != item->getType()) && !mIsLink)
+ {
+ std::string delim(" --");
+ bool copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ if (!copy)
+ {
+ mPermSuffix += delim;
+ mPermSuffix += LLTrans::getString("no_copy_lbl");
+ }
+ bool mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ if (!mod)
+ {
+ mPermSuffix += mPermSuffix.empty() ? delim : ",";
+ mPermSuffix += LLTrans::getString("no_modify_lbl");
+ }
+ bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID());
+ if (!xfer)
+ {
+ mPermSuffix += mPermSuffix.empty() ? delim : ",";
+ mPermSuffix += LLTrans::getString("no_transfer_lbl");
+ }
+ }
+ }
+
+ getChild<LLIconCtrl>("item_type")->setValue(icon_name);
+ getChild<LLIconCtrl>("link_overlay")->setVisible(is_link);
+}
+
+void LLInventoryGalleryItem::setThumbnail(LLUUID id)
+{
+ mDefaultImage = id.isNull();
+ if(mDefaultImage)
+ {
+ getChild<LLThumbnailCtrl>("preview_thumbnail")->clearTexture();
+ }
+ else
+ {
+ getChild<LLThumbnailCtrl>("preview_thumbnail")->setValue(id);
+ }
+}
+
+void LLInventoryGalleryItem::draw()
+{
+ if (isFadeItem())
+ {
+ // Fade out to indicate it's being cut
+ LLViewDrawContext context(0.5f);
+ LLPanel::draw();
+ }
+ else
+ {
+ LLPanel::draw();
+
+ // Draw border
+ LLUIColor border_color = LLUIColorTable::instance().getColor(mSelected ? "MenuItemHighlightBgColor" : "TextFgTentativeColor", LLColor4::white);
+ LLRect border = getChildView("preview_thumbnail")->getRect();
+ border.mRight = border.mRight + 1;
+ border.mTop = border.mTop + 1;
+ gl_rect_2d(border, border_color.get(), FALSE);
+ }
+}
+
+void LLInventoryGalleryItem::setItemName(std::string name)
+{
+ mItemName = name;
+ updateNameText();
+}
+
+void LLInventoryGalleryItem::setSelected(bool value)
+{
+ mSelected = value;
+ mTextBgPanel->setBackgroundVisible(value);
+
+ if(mSelected)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ if(item && !item->isFinished())
+ {
+ LLInventoryModelBackgroundFetch::instance().start(mUUID, false);
+ }
+ }
+}
+
+BOOL LLInventoryGalleryItem::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // call changeItemSelection directly, before setFocus
+ // to avoid autoscroll from LLInventoryGallery::onFocusReceived()
+ if (mask == MASK_CONTROL)
+ {
+ mGallery->addItemSelection(mUUID, false);
+ }
+ else if (mask == MASK_SHIFT)
+ {
+ mGallery->toggleSelectionRangeFromLast(mUUID);
+ }
+ else
+ {
+ mGallery->changeItemSelection(mUUID, false);
+ }
+ setFocus(TRUE);
+ mGallery->claimEditHandler();
+
+ gFocusMgr.setMouseCapture(this);
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ LLToolDragAndDrop::getInstance()->setDragStart(screen_x, screen_y);
+ return TRUE;
+}
+
+BOOL LLInventoryGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (!isSelected())
+ {
+ mGallery->changeItemSelection(mUUID, false);
+ }
+ else
+ {
+ // refresh last interacted
+ mGallery->addItemSelection(mUUID, false);
+ }
+ setFocus(TRUE);
+ mGallery->claimEditHandler();
+ mGallery->showContextMenu(this, x, y, mUUID);
+
+ LLUICtrl::handleRightMouseDown(x, y, mask);
+ return TRUE;
+}
+
+BOOL LLInventoryGalleryItem::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if(hasMouseCapture())
+ {
+ gFocusMgr.setMouseCapture(NULL);
+ return TRUE;
+ }
+ return LLPanel::handleMouseUp(x, y, mask);
+}
+
+BOOL LLInventoryGalleryItem::handleHover(S32 x, S32 y, MASK mask)
+{
+ if(hasMouseCapture())
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+
+ if(LLToolDragAndDrop::getInstance()->isOverThreshold(screen_x, screen_y) && mGallery)
+ {
+ mGallery->startDrag();
+ return LLToolDragAndDrop::getInstance()->handleHover(x, y, mask);
+ }
+ }
+ return LLUICtrl::handleHover(x,y,mask);
+}
+
+BOOL LLInventoryGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (mIsFolder && mGallery)
+ {
+ // setRootFolder can destroy this item.
+ // Delay it until handleDoubleClick processing is complete
+ // or make gallery handle doubleclicks.
+ LLHandle<LLPanel> handle = mGallery->getHandle();
+ LLUUID navigate_to = mUUID;
+ doOnIdleOneTime([handle, navigate_to]()
+ {
+ LLInventoryGallery* gallery = (LLInventoryGallery*)handle.get();
+ if (gallery)
+ {
+ gallery->setRootFolder(navigate_to);
+ }
+ });
+ }
+ else
+ {
+ LLInvFVBridgeAction::doAction(mUUID, &gInventory);
+ }
+
+ return TRUE;
+}
+
+BOOL LLInventoryGalleryItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ if (!mIsFolder)
+ {
+ return FALSE;
+ }
+ return mGallery->baseHandleDragAndDrop(mUUID, drop, cargo_type, cargo_data, accept, tooltip_msg);
+}
+
+BOOL LLInventoryGalleryItem::handleKeyHere(KEY key, MASK mask)
+{
+ if (!mGallery)
+ {
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+ switch (key)
+ {
+ case KEY_LEFT:
+ mGallery->moveLeft(mask);
+ handled = true;
+ break;
+
+ case KEY_RIGHT:
+ mGallery->moveRight(mask);
+ handled = true;
+ break;
+
+ case KEY_UP:
+ mGallery->moveUp(mask);
+ handled = true;
+ break;
+
+ case KEY_DOWN:
+ mGallery->moveDown(mask);
+ handled = true;
+ break;
+
+ default:
+ break;
+ }
+ return handled;
+}
+
+void LLInventoryGalleryItem::onFocusLost()
+{
+ // inventory no longer handles cut/copy/paste/delete
+ mGallery->resetEditHandler();
+
+ LLPanel::onFocusLost();
+}
+
+void LLInventoryGalleryItem::onFocusReceived()
+{
+ // inventory now handles cut/copy/paste/delete
+ mGallery->claimEditHandler();
+
+ LLPanel::onFocusReceived();
+}
+
+void LLInventoryGalleryItem::setWorn(bool value)
+{
+ mWorn = value;
+
+ if(mWorn)
+ {
+ mWornSuffix = (mType == LLAssetType::AT_GESTURE) ? LLTrans::getString("active") : LLTrans::getString("worn");
+ }
+ else
+ {
+ mWornSuffix = "";
+ }
+
+ updateNameText();
+}
+
+LLFontGL* LLInventoryGalleryItem::getTextFont()
+{
+ if(mWorn)
+ {
+ return LLFontGL::getFontSansSerifSmallBold();
+ }
+ return mIsLink ? LLFontGL::getFontSansSerifSmallItalic() : LLFontGL::getFontSansSerifSmall();
+}
+
+void LLInventoryGalleryItem::updateNameText()
+{
+ mNameText->setFont(getTextFont());
+ mNameText->setText(mItemName + mPermSuffix + mWornSuffix);
+ mNameText->setToolTip(mItemName + mPermSuffix + mWornSuffix);
+ getChild<LLThumbnailCtrl>("preview_thumbnail")->setToolTip(mItemName + mPermSuffix + mWornSuffix);
+}
+
+bool LLInventoryGalleryItem::isFadeItem()
+{
+ LLClipboard& clipboard = LLClipboard::instance();
+ if (mCutGeneration == clipboard.getGeneration())
+ {
+ return mSelectedForCut;
+ }
+
+ mCutGeneration = clipboard.getGeneration();
+ mSelectedForCut = clipboard.isCutMode() && clipboard.isOnClipboard(mUUID);
+ return mSelectedForCut;
+}
+
+//-----------------------------
+// LLThumbnailsObserver
+//-----------------------------
+
+void LLThumbnailsObserver::changed(U32 mask)
+{
+ std::vector<LLUUID> deleted_ids;
+ for (item_map_t::iterator iter = mItemMap.begin();
+ iter != mItemMap.end();
+ ++iter)
+ {
+ const LLUUID& obj_id = (*iter).first;
+ LLItemData& data = (*iter).second;
+
+ LLInventoryObject* obj = gInventory.getObject(obj_id);
+ if (!obj)
+ {
+ deleted_ids.push_back(obj_id);
+ continue;
+ }
+
+ const LLUUID thumbnail_id = obj->getThumbnailUUID();
+ if (data.mThumbnailID != thumbnail_id)
+ {
+ data.mThumbnailID = thumbnail_id;
+ data.mCallback();
+ }
+ }
+
+ // Remove deleted items from the list
+ for (std::vector<LLUUID>::iterator deleted_id = deleted_ids.begin(); deleted_id != deleted_ids.end(); ++deleted_id)
+ {
+ removeItem(*deleted_id);
+ }
+}
+
+bool LLThumbnailsObserver::addItem(const LLUUID& obj_id, callback_t cb)
+{
+ LLInventoryObject* obj = gInventory.getObject(obj_id);
+ if (obj)
+ {
+ mItemMap.insert(item_map_value_t(obj_id, LLItemData(obj_id, obj->getThumbnailUUID(), cb)));
+ return true;
+ }
+ return false;
+}
+
+void LLThumbnailsObserver::removeItem(const LLUUID& obj_id)
+{
+ mItemMap.erase(obj_id);
+}
+
+//-----------------------------
+// Helper drag&drop functions
+//-----------------------------
+
+BOOL LLInventoryGallery::baseHandleDragAndDrop(LLUUID dest_id, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
+
+ if (drop && LLToolDragAndDrop::getInstance()->getCargoIndex() == 0)
+ {
+ clearSelection();
+ }
+
+ BOOL accepted = FALSE;
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_CALLINGCARD:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_CLOTHING:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ case DAD_MESH:
+ case DAD_SETTINGS:
+ accepted = dragItemIntoFolder(dest_id, inv_item, drop, tooltip_msg, true);
+ if (accepted && drop)
+ {
+ // Don't select immediately, wait for item to arrive
+ mItemsToSelect.push_back(inv_item->getUUID());
+ }
+ break;
+ case DAD_LINK:
+ // DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER.
+ // If we have an item of AT_LINK_FOLDER type we should process the linked
+ // category being dragged or dropped into folder.
+ if (inv_item && LLAssetType::AT_LINK_FOLDER == inv_item->getActualType())
+ {
+ LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID());
+ if (linked_category)
+ {
+ accepted = dragCategoryIntoFolder(dest_id, (LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE);
+ }
+ }
+ else
+ {
+ accepted = dragItemIntoFolder(dest_id, inv_item, drop, tooltip_msg, TRUE);
+ }
+ if (accepted && drop && inv_item)
+ {
+ mItemsToSelect.push_back(inv_item->getUUID());
+ }
+ break;
+ case DAD_CATEGORY:
+ if (LLFriendCardsManager::instance().isAnyFriendCategory(dest_id))
+ {
+ accepted = FALSE;
+ }
+ else
+ {
+ LLInventoryCategory* cat_ptr = (LLInventoryCategory*)cargo_data;
+ accepted = dragCategoryIntoFolder(dest_id, cat_ptr, drop, tooltip_msg, FALSE);
+ if (accepted && drop)
+ {
+ mItemsToSelect.push_back(cat_ptr->getUUID());
+ }
+ }
+ break;
+ case DAD_ROOT_CATEGORY:
+ case DAD_NONE:
+ break;
+ default:
+ LL_WARNS() << "Unhandled cargo type for drag&drop " << cargo_type << LL_ENDL;
+ break;
+ }
+ if (accepted)
+ {
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ return accepted;
+}
+
+// copy of LLFolderBridge::dragItemIntoFolder
+BOOL dragItemIntoFolder(LLUUID folder_id, LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm)
+{
+ LLViewerInventoryCategory * cat = gInventory.getCategory(folder_id);
+ if (!cat)
+ {
+ return FALSE;
+ }
+ LLInventoryModel* model = &gInventory;
+
+ if (!model || !inv_item) return FALSE;
+
+ // cannot drag into library
+ if((gInventory.getRootFolderID() != folder_id) && !model->isObjectDescendentOf(folder_id, gInventory.getRootFolderID()))
+ {
+ return FALSE;
+ }
+ if (!isAgentAvatarValid()) return FALSE;
+
+ const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
+ const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+
+ const BOOL move_is_into_current_outfit = (folder_id == current_outfit_id);
+ const BOOL move_is_into_favorites = (folder_id == favorites_id);
+ const BOOL move_is_into_my_outfits = (folder_id == my_outifts_id) || model->isObjectDescendentOf(folder_id, my_outifts_id);
+ const BOOL move_is_into_outfit = move_is_into_my_outfits || (cat && cat->getPreferredType()==LLFolderType::FT_OUTFIT);
+ const BOOL move_is_into_landmarks = (folder_id == landmarks_id) || model->isObjectDescendentOf(folder_id, landmarks_id);
+ const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(folder_id, marketplacelistings_id);
+ const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id);
+
+ LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
+ BOOL accept = FALSE;
+ LLViewerObject* object = NULL;
+ if(LLToolDragAndDrop::SOURCE_AGENT == source)
+ {
+ const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+
+ const BOOL move_is_into_trash = (folder_id == trash_id) || model->isObjectDescendentOf(folder_id, trash_id);
+ const BOOL move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID());
+
+ //--------------------------------------------------------------------------------
+ // Determine if item can be moved.
+ //
+
+ BOOL is_movable = TRUE;
+
+ switch (inv_item->getActualType())
+ {
+ case LLAssetType::AT_CATEGORY:
+ is_movable = !LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)inv_item)->getPreferredType());
+ break;
+ default:
+ break;
+ }
+ // Can't explicitly drag things out of the COF.
+ if (move_is_outof_current_outfit)
+ {
+ is_movable = FALSE;
+ }
+ if (move_is_into_trash)
+ {
+ is_movable &= inv_item->getIsLinkType() || !get_is_item_worn(inv_item->getUUID());
+ }
+ if (is_movable)
+ {
+ // Don't allow creating duplicates in the Calling Card/Friends
+ // subfolders, see bug EXT-1599. Check is item direct descendent
+ // of target folder and forbid item's movement if it so.
+ // Note: isItemDirectDescendentOfCategory checks if
+ // passed category is in the Calling Card/Friends folder
+ is_movable &= !LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(inv_item, cat);
+ }
+
+ //
+ //--------------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------------
+ // Determine if item can be moved & dropped
+ // Note: if user_confirm is false, we already went through those accept logic test and can skip them
+
+ accept = TRUE;
+
+ if (user_confirm && !is_movable)
+ {
+ accept = FALSE;
+ }
+ else if (user_confirm && (folder_id == inv_item->getParentUUID()) && !move_is_into_favorites)
+ {
+ accept = FALSE;
+ }
+ else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit))
+ {
+ accept = can_move_to_outfit(inv_item, move_is_into_current_outfit);
+ }
+ else if (user_confirm && (move_is_into_favorites || move_is_into_landmarks))
+ {
+ accept = can_move_to_landmarks(inv_item);
+ }
+ else if (user_confirm && move_is_into_marketplacelistings)
+ {
+ //disable dropping in or out of marketplace for now
+ return FALSE;
+
+ /*const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, folder_id);
+ LLViewerInventoryCategory * dest_folder = cat;
+ accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, LLToolDragAndDrop::instance().getCargoCount() - LLToolDragAndDrop::instance().getCargoIndex());*/
+ }
+
+ // Check that the folder can accept this item based on folder/item type compatibility (e.g. stock folder compatibility)
+ if (user_confirm && accept)
+ {
+ LLViewerInventoryCategory * dest_folder = cat;
+ accept = dest_folder->acceptItem(inv_item);
+ }
+
+ LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE);
+
+ if (accept && drop)
+ {
+ if (inv_item->getType() == LLAssetType::AT_GESTURE
+ && LLGestureMgr::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash)
+ {
+ LLGestureMgr::instance().deactivateGesture(inv_item->getUUID());
+ }
+ // If an item is being dragged between windows, unselect everything in the active window
+ // so that we don't follow the selection to its new location (which is very annoying).
+ // RN: a better solution would be to deselect automatically when an item is moved
+ // and then select any item that is dropped only in the panel that it is dropped in
+ if (active_panel)
+ {
+ active_panel->unSelectAll();
+ }
+ // Dropping in or out of marketplace needs (sometimes) confirmation
+ if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings))
+ {
+ //disable dropping in or out of marketplace for now
+ return FALSE;
+ }
+
+ //--------------------------------------------------------------------------------
+ // Destination folder logic
+ //
+
+ // FAVORITES folder
+ // (copy the item)
+ else if (move_is_into_favorites)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ inv_item->getPermissions().getOwner(),
+ inv_item->getUUID(),
+ folder_id,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ // CURRENT OUTFIT or OUTFIT folder
+ // (link the item)
+ else if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+ if (move_is_into_current_outfit)
+ {
+ LLAppearanceMgr::instance().wearItemOnAvatar(inv_item->getUUID(), true, true);
+ }
+ else
+ {
+ LLPointer<LLInventoryCallback> cb = NULL;
+ link_inventory_object(folder_id, LLConstPointer<LLInventoryObject>(inv_item), cb);
+ }
+ }
+ // MARKETPLACE LISTINGS folder
+ // Move the item
+ else if (move_is_into_marketplacelistings)
+ {
+ //move_item_to_marketplacelistings(inv_item, mUUID);
+ return FALSE;
+ }
+ // NORMAL or TRASH folder
+ // (move the item, restamp if into trash)
+ else
+ {
+ // set up observer to select item once drag and drop from inbox is complete
+ if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX)))
+ {
+ set_dad_inbox_object(inv_item->getUUID());
+ }
+
+ gInventory.changeItemParent((LLViewerInventoryItem*)inv_item, folder_id, move_is_into_trash);
+ }
+
+ if (move_is_from_marketplacelistings)
+ {
+ // If we move from an active (listed) listing, checks that it's still valid, if not, unlist
+ /*LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid);
+ if (version_folder_id.notNull())
+ {
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
+ version_folder_id,
+ [version_folder_id](bool result)
+ {
+ if (!result)
+ {
+ LLMarketplaceData::instance().activateListing(version_folder_id, false);
+ }
+ });
+ }*/
+ return FALSE;
+ }
+
+ //
+ //--------------------------------------------------------------------------------
+ }
+ }
+ else if (LLToolDragAndDrop::SOURCE_WORLD == source)
+ {
+ // Make sure the object exists. If we allowed dragging from
+ // anonymous objects, it would be possible to bypass
+ // permissions.
+ object = gObjectList.findObject(inv_item->getParentUUID());
+ if (!object)
+ {
+ LL_INFOS() << "Object not found for drop." << LL_ENDL;
+ return FALSE;
+ }
+
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ LLPermissions perm(inv_item->getPermissions());
+ bool is_move = false;
+ if ((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
+ && perm.allowTransferTo(gAgent.getID())))
+ // || gAgent.isGodlike())
+ {
+ accept = TRUE;
+ }
+ else if(object->permYouOwner())
+ {
+ // If the object cannot be copied, but the object the
+ // inventory is owned by the agent, then the item can be
+ // moved from the task to agent inventory.
+ is_move = true;
+ accept = TRUE;
+ }
+
+ // Don't allow placing an original item into Current Outfit or an outfit folder
+ // because they must contain only links to wearable items.
+ if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+ accept = FALSE;
+ }
+ // Don't allow to move a single item to Favorites or Landmarks
+ // if it is not a landmark or a link to a landmark.
+ else if ((move_is_into_favorites || move_is_into_landmarks)
+ && !can_move_to_landmarks(inv_item))
+ {
+ accept = FALSE;
+ }
+ else if (move_is_into_marketplacelistings)
+ {
+ tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
+ accept = FALSE;
+ }
+
+ if (accept && drop)
+ {
+ boost::shared_ptr<LLMoveInv> move_inv (new LLMoveInv());
+ move_inv->mObjectID = inv_item->getParentUUID();
+ std::pair<LLUUID, LLUUID> item_pair(folder_id, inv_item->getUUID());
+ move_inv->mMoveList.push_back(item_pair);
+ move_inv->mCallback = NULL;
+ move_inv->mUserData = NULL;
+ if(is_move)
+ {
+ warn_move_inventory(object, move_inv);
+ }
+ else
+ {
+ // store dad inventory item to select added one later. See EXT-4347
+ set_dad_inventory_item(inv_item, folder_id);
+
+ LLNotification::Params params("MoveInventoryFromObject");
+ params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv));
+ LLNotifications::instance().forceResponse(params, 0);
+ }
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_NOTECARD == source)
+ {
+ if (move_is_into_marketplacelistings)
+ {
+ tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
+ accept = FALSE;
+ }
+ else if ((inv_item->getActualType() == LLAssetType::AT_SETTINGS) && !LLEnvironment::instance().isInventoryEnabled())
+ {
+ tooltip_msg = LLTrans::getString("NoEnvironmentSettings");
+ accept = FALSE;
+ }
+ else
+ {
+ // Don't allow placing an original item from a notecard to Current Outfit or an outfit folder
+ // because they must contain only links to wearable items.
+ accept = !(move_is_into_current_outfit || move_is_into_outfit);
+ }
+
+ if (accept && drop)
+ {
+ copy_inventory_from_notecard(folder_id, // Drop to the chosen destination folder
+ LLToolDragAndDrop::getInstance()->getObjectID(),
+ LLToolDragAndDrop::getInstance()->getSourceID(),
+ inv_item);
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_LIBRARY == source)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item;
+ if(item && item->isFinished())
+ {
+ accept = TRUE;
+
+ if (move_is_into_marketplacelistings)
+ {
+ tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
+ accept = FALSE;
+ }
+ else if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+ accept = can_move_to_outfit(inv_item, move_is_into_current_outfit);
+ }
+ // Don't allow to move a single item to Favorites or Landmarks
+ // if it is not a landmark or a link to a landmark.
+ else if (move_is_into_favorites || move_is_into_landmarks)
+ {
+ accept = can_move_to_landmarks(inv_item);
+ }
+
+ if (accept && drop)
+ {
+ // FAVORITES folder
+ // (copy the item)
+ if (move_is_into_favorites)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ inv_item->getPermissions().getOwner(),
+ inv_item->getUUID(),
+ folder_id,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ // CURRENT OUTFIT or OUTFIT folder
+ // (link the item)
+ else if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+ if (move_is_into_current_outfit)
+ {
+ LLAppearanceMgr::instance().wearItemOnAvatar(inv_item->getUUID(), true, true);
+ }
+ else
+ {
+ LLPointer<LLInventoryCallback> cb = NULL;
+ link_inventory_object(folder_id, LLConstPointer<LLInventoryObject>(inv_item), cb);
+ }
+ }
+ else
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ inv_item->getPermissions().getOwner(),
+ inv_item->getUUID(),
+ folder_id,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS() << "unhandled drag source" << LL_ENDL;
+ }
+ return accept;
+}
+
+// copy of LLFolderBridge::dragCategoryIntoFolder
+BOOL dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat,
+ BOOL drop, std::string& tooltip_msg, BOOL is_link)
+{
+ BOOL user_confirm = TRUE;
+ LLInventoryModel* model = &gInventory;
+ LLViewerInventoryCategory * dest_cat = gInventory.getCategory(dest_id);
+ if (!dest_cat)
+ {
+ return FALSE;
+ }
+
+ if (!inv_cat) return FALSE; // shouldn't happen, but in case item is incorrectly parented in which case inv_cat will be NULL
+
+ if (!isAgentAvatarValid()) return FALSE;
+ // cannot drag into library
+ if((gInventory.getRootFolderID() != dest_id) && !model->isObjectDescendentOf(dest_id, gInventory.getRootFolderID()))
+ {
+ return FALSE;
+ }
+
+ const LLUUID &cat_id = inv_cat->getUUID();
+ const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ //const LLUUID from_folder_uuid = inv_cat->getParentUUID();
+
+ const BOOL move_is_into_current_outfit = (dest_id == current_outfit_id);
+ const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(dest_id, marketplacelistings_id);
+ const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(cat_id, marketplacelistings_id);
+
+ // check to make sure source is agent inventory, and is represented there.
+ LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
+ const BOOL is_agent_inventory = (model->getCategory(cat_id) != NULL)
+ && (LLToolDragAndDrop::SOURCE_AGENT == source);
+
+ BOOL accept = FALSE;
+
+ if (is_agent_inventory)
+ {
+ const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
+ const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+ const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
+
+ const BOOL move_is_into_trash = (dest_id == trash_id) || model->isObjectDescendentOf(dest_id, trash_id);
+ const BOOL move_is_into_my_outfits = (dest_id == my_outifts_id) || model->isObjectDescendentOf(dest_id, my_outifts_id);
+ const BOOL move_is_into_outfit = move_is_into_my_outfits || (dest_cat && dest_cat->getPreferredType()==LLFolderType::FT_OUTFIT);
+ const BOOL move_is_into_current_outfit = (dest_cat && dest_cat->getPreferredType()==LLFolderType::FT_CURRENT_OUTFIT);
+ const BOOL move_is_into_landmarks = (dest_id == landmarks_id) || model->isObjectDescendentOf(dest_id, landmarks_id);
+ const BOOL move_is_into_lost_and_found = model->isObjectDescendentOf(dest_id, lost_and_found_id);
+
+ //--------------------------------------------------------------------------------
+ // Determine if folder can be moved.
+ //
+
+ BOOL is_movable = TRUE;
+
+ if (is_movable && (marketplacelistings_id == cat_id))
+ {
+ is_movable = FALSE;
+ tooltip_msg = LLTrans::getString("TooltipOutboxCannotMoveRoot");
+ }
+ if (is_movable && move_is_from_marketplacelistings)
+ //&& LLMarketplaceData::instance().getActivationState(cat_id))
+ {
+ // If the incoming folder is listed and active (and is therefore either the listing or the version folder),
+ // then moving is *not* allowed
+ is_movable = FALSE;
+ tooltip_msg = LLTrans::getString("TooltipOutboxDragActive");
+ }
+ if (is_movable && (dest_id == cat_id))
+ {
+ is_movable = FALSE;
+ tooltip_msg = LLTrans::getString("TooltipDragOntoSelf");
+ }
+ if (is_movable && (model->isObjectDescendentOf(dest_id, cat_id)))
+ {
+ is_movable = FALSE;
+ tooltip_msg = LLTrans::getString("TooltipDragOntoOwnChild");
+ }
+ if (is_movable && LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType()))
+ {
+ is_movable = FALSE;
+ // tooltip?
+ }
+
+ U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit");
+ if (is_movable && move_is_into_outfit)
+ {
+ if (dest_id == my_outifts_id)
+ {
+ if (source != LLToolDragAndDrop::SOURCE_AGENT || move_is_from_marketplacelistings)
+ {
+ tooltip_msg = LLTrans::getString("TooltipOutfitNotInInventory");
+ is_movable = false;
+ }
+ else if (can_move_to_my_outfits(model, inv_cat, max_items_to_wear))
+ {
+ is_movable = true;
+ }
+ else
+ {
+ tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit");
+ is_movable = false;
+ }
+ }
+ else if(dest_cat && dest_cat->getPreferredType() == LLFolderType::FT_NONE)
+ {
+ is_movable = ((inv_cat->getPreferredType() == LLFolderType::FT_NONE) || (inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT));
+ }
+ else
+ {
+ is_movable = false;
+ }
+ }
+ if(is_movable && move_is_into_current_outfit && is_link)
+ {
+ is_movable = FALSE;
+ }
+ if (is_movable && move_is_into_lost_and_found)
+ {
+ is_movable = FALSE;
+ }
+ if (is_movable && (dest_id == model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE)))
+ {
+ is_movable = FALSE;
+ // tooltip?
+ }
+ if (is_movable && (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK))
+ {
+ // One cannot move a folder into a stock folder
+ is_movable = FALSE;
+ // tooltip?
+ }
+
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ if (is_movable)
+ {
+ model->collectDescendents(cat_id, descendent_categories, descendent_items, FALSE);
+ for (S32 i=0; i < descendent_categories.size(); ++i)
+ {
+ LLInventoryCategory* category = descendent_categories[i];
+ if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
+ {
+ // Can't move "special folders" (e.g. Textures Folder).
+ is_movable = FALSE;
+ break;
+ }
+ }
+ }
+ if (is_movable
+ && move_is_into_current_outfit
+ && descendent_items.size() > max_items_to_wear)
+ {
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false);
+ gInventory.collectDescendentsIf(cat_id,
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ not_worn);
+
+ if (items.size() > max_items_to_wear)
+ {
+ // Can't move 'large' folders into current outfit: MAINT-4086
+ is_movable = FALSE;
+ LLStringUtil::format_map_t args;
+ args["AMOUNT"] = llformat("%d", max_items_to_wear);
+ tooltip_msg = LLTrans::getString("TooltipTooManyWearables",args);
+ }
+ }
+ if (is_movable && move_is_into_trash)
+ {
+ for (S32 i=0; i < descendent_items.size(); ++i)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (get_is_item_worn(item->getUUID()))
+ {
+ is_movable = FALSE;
+ break; // It's generally movable, but not into the trash.
+ }
+ }
+ }
+ if (is_movable && move_is_into_landmarks)
+ {
+ for (S32 i=0; i < descendent_items.size(); ++i)
+ {
+ LLViewerInventoryItem* item = descendent_items[i];
+
+ // Don't move anything except landmarks and categories into Landmarks folder.
+ // We use getType() instead of getActua;Type() to allow links to landmarks and folders.
+ if (LLAssetType::AT_LANDMARK != item->getType() && LLAssetType::AT_CATEGORY != item->getType())
+ {
+ is_movable = FALSE;
+ break; // It's generally movable, but not into Landmarks.
+ }
+ }
+ }
+
+ if (is_movable && move_is_into_marketplacelistings)
+ {
+ const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, dest_id);
+ LLViewerInventoryCategory * dest_folder = dest_cat;
+ S32 bundle_size = (drop ? 1 : LLToolDragAndDrop::instance().getCargoCount());
+ is_movable = can_move_folder_to_marketplace(master_folder, dest_folder, inv_cat, tooltip_msg, bundle_size);
+ }
+
+ //
+ //--------------------------------------------------------------------------------
+
+ accept = is_movable;
+
+ if (accept && drop)
+ {
+ // Dropping in or out of marketplace needs (sometimes) confirmation
+ if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings))
+ {
+ //disable dropping in or out of marketplace for now
+ return FALSE;
+ }
+ // Look for any gestures and deactivate them
+ if (move_is_into_trash)
+ {
+ for (S32 i=0; i < descendent_items.size(); i++)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && LLGestureMgr::instance().isGestureActive(item->getUUID()))
+ {
+ LLGestureMgr::instance().deactivateGesture(item->getUUID());
+ }
+ }
+ }
+
+ if (dest_id == my_outifts_id)
+ {
+ // Category can contains objects,
+ // create a new folder and populate it with links to original objects
+ dropToMyOutfits(inv_cat);
+ }
+ // if target is current outfit folder we use link
+ else if (move_is_into_current_outfit &&
+ (inv_cat->getPreferredType() == LLFolderType::FT_NONE ||
+ inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT))
+ {
+ // traverse category and add all contents to currently worn.
+ BOOL append = true;
+ LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append);
+ }
+ else if (move_is_into_marketplacelistings)
+ {
+ //move_folder_to_marketplacelistings(inv_cat, dest_id);
+ }
+ else
+ {
+ if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX)))
+ {
+ set_dad_inbox_object(cat_id);
+ }
+
+ // Reparent the folder and restamp children if it's moving
+ // into trash.
+ gInventory.changeCategoryParent(
+ (LLViewerInventoryCategory*)inv_cat,
+ dest_id,
+ move_is_into_trash);
+ }
+ if (move_is_from_marketplacelistings)
+ {
+ //disable dropping in or out of marketplace for now
+ return FALSE;
+
+ // If we are moving a folder at the listing folder level (i.e. its parent is the marketplace listings folder)
+ /*if (from_folder_uuid == marketplacelistings_id)
+ {
+ // Clear the folder from the marketplace in case it is a listing folder
+ if (LLMarketplaceData::instance().isListed(cat_id))
+ {
+ LLMarketplaceData::instance().clearListing(cat_id);
+ }
+ }
+ else
+ {
+ // If we move from within an active (listed) listing, checks that it's still valid, if not, unlist
+ LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid);
+ if (version_folder_id.notNull())
+ {
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
+ version_folder_id,
+ [version_folder_id](bool result)
+ {
+ if (!result)
+ {
+ LLMarketplaceData::instance().activateListing(version_folder_id, false);
+ }
+ }
+ );
+ }
+ // In all cases, update the listing we moved from so suffix are updated
+ update_marketplace_category(from_folder_uuid);
+ }*/
+ }
+ }
+ }
+ else if (LLToolDragAndDrop::SOURCE_WORLD == source)
+ {
+ if (move_is_into_marketplacelistings)
+ {
+ tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
+ accept = FALSE;
+ }
+ else
+ {
+ accept = move_inv_category_world_to_agent(cat_id, dest_id, drop);
+ }
+ }
+ else if (LLToolDragAndDrop::SOURCE_LIBRARY == source)
+ {
+ if (move_is_into_marketplacelistings)
+ {
+ tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
+ accept = FALSE;
+ }
+ else
+ {
+ // Accept folders that contain complete outfits.
+ accept = move_is_into_current_outfit && LLAppearanceMgr::instance().getCanMakeFolderIntoOutfit(cat_id);
+ }
+
+ if (accept && drop)
+ {
+ LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, true, false);
+ }
+ }
+
+ return accept;
+}
+
+void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id)
+{
+ LLInventoryModel::cat_array_t* categories;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(cat_source_id, categories, items);
+
+ LLInventoryObject::const_object_list_t link_array;
+
+
+ LLInventoryModel::item_array_t::iterator iter = items->begin();
+ LLInventoryModel::item_array_t::iterator end = items->end();
+ while (iter!=end)
+ {
+ const LLViewerInventoryItem* item = (*iter);
+ // By this point everything is supposed to be filtered,
+ // but there was a delay to create folder so something could have changed
+ LLInventoryType::EType inv_type = item->getInventoryType();
+ if ((inv_type == LLInventoryType::IT_WEARABLE) ||
+ (inv_type == LLInventoryType::IT_GESTURE) ||
+ (inv_type == LLInventoryType::IT_ATTACHMENT) ||
+ (inv_type == LLInventoryType::IT_OBJECT) ||
+ (inv_type == LLInventoryType::IT_SNAPSHOT) ||
+ (inv_type == LLInventoryType::IT_TEXTURE))
+ {
+ link_array.push_back(LLConstPointer<LLInventoryObject>(item));
+ }
+ iter++;
+ }
+
+ if (!link_array.empty())
+ {
+ LLPointer<LLInventoryCallback> cb = NULL;
+ link_inventory_array(cat_dest_id, link_array, cb);
+ }
+}
+
+void dropToMyOutfits(LLInventoryCategory* inv_cat)
+{
+ // make a folder in the My Outfits directory.
+ const LLUUID dest_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+
+ // Note: creation will take time, so passing folder id to callback is slightly unreliable,
+ // but so is collecting and passing descendants' ids
+ inventory_func_type func = boost::bind(&outfitFolderCreatedCallback, inv_cat->getUUID(), _1);
+ gInventory.createNewCategory(dest_id, LLFolderType::FT_OUTFIT, inv_cat->getName(), func, inv_cat->getThumbnailUUID());
+}
+
diff --git a/indra/newview/llinventorygallery.h b/indra/newview/llinventorygallery.h
new file mode 100644
index 0000000000..9b3f12701f
--- /dev/null
+++ b/indra/newview/llinventorygallery.h
@@ -0,0 +1,419 @@
+/**
+ * @file llinventorygallery.h
+ * @brief LLInventoryGallery class definition
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLINVENTORYGALLERY_H
+#define LL_LLINVENTORYGALLERY_H
+
+#include "llgesturemgr.h"
+#include "lllistcontextmenu.h"
+#include "llpanel.h"
+#include "llinventoryfilter.h"
+#include "llinventoryobserver.h"
+#include "llinventorymodel.h"
+
+class LLInventoryCategoriesObserver;
+class LLInventoryGalleryItem;
+class LLScrollContainer;
+class LLTextBox;
+class LLThumbnailsObserver;
+class LLGalleryGestureObserver;
+
+class LLInventoryGalleryContextMenu;
+
+typedef boost::function<void()> callback_t;
+
+class LLInventoryGallery : public LLPanel, public LLEditMenuHandler
+{
+public:
+
+ typedef boost::signals2::signal<void(const LLUUID&)> selection_change_signal_t;
+ typedef boost::function<void(const LLUUID&)> selection_change_callback_t;
+ typedef std::deque<LLUUID> selection_deque;
+
+ struct Params
+ : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ Optional<S32> row_panel_height;
+ Optional<S32> row_panel_width_factor;
+ Optional<S32> gallery_width_factor;
+ Optional<S32> vertical_gap;
+ Optional<S32> horizontal_gap;
+ Optional<S32> item_width;
+ Optional<S32> item_height;
+ Optional<S32> item_horizontal_gap;
+ Optional<S32> items_in_row;
+
+ Params();
+ };
+
+ static const LLInventoryGallery::Params& getDefaultParams();
+
+ LLInventoryGallery(const LLInventoryGallery::Params& params = getDefaultParams());
+ ~LLInventoryGallery();
+
+ BOOL postBuild() override;
+ void initGallery();
+ void draw() override;
+ void onVisibilityChange(BOOL new_visibility) override;
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type,
+ void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) override;
+ void startDrag();
+ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override;
+ BOOL handleKeyHere(KEY key, MASK mask) override;
+ void moveUp(MASK mask);
+ void moveDown(MASK mask);
+ void moveLeft(MASK mask);
+ void moveRight(MASK mask);
+ void toggleSelectionRange(S32 start_idx, S32 end_idx);
+ void toggleSelectionRangeFromLast(const LLUUID target);
+
+ void onFocusLost() override;
+ void onFocusReceived() override;
+
+ void setFilterSubString(const std::string& string);
+ std::string getFilterSubString() { return mFilterSubString; }
+ LLInventoryFilter& getFilter() const { return *mFilter; }
+ bool checkAgainstFilterType(const LLUUID& object_id);
+
+ void getCurrentCategories(uuid_vec_t& vcur);
+ bool updateAddedItem(LLUUID item_id); // returns true if added item is visible
+ void updateRemovedItem(LLUUID item_id);
+ void updateChangedItemName(LLUUID item_id, std::string name);
+ void updateItemThumbnail(LLUUID item_id);
+ void updateWornItem(LLUUID item_id, bool is_worn);
+
+ void updateMessageVisibility();
+
+ void setRootFolder(const LLUUID cat_id);
+ void updateRootFolder();
+ LLUUID getRootFolder() { return mFolderID; }
+ bool isRootDirty() { return mRootDirty; }
+ boost::signals2::connection setRootChangedCallback(callback_t cb);
+ void onForwardFolder();
+ void onBackwardFolder();
+ void clearNavigationHistory();
+ bool isBackwardAvailable();
+ bool isForwardAvailable();
+
+ void setNavBackwardList(std::list<LLUUID> backward_list) { mBackwardFolders = backward_list; }
+ void setNavForwardList(std::list<LLUUID> forward_list) { mForwardFolders = forward_list; }
+ std::list<LLUUID> getNavBackwardList() { return mBackwardFolders; }
+ std::list<LLUUID> getNavForwardList() { return mForwardFolders; }
+
+ LLUUID getOutfitImageID(LLUUID outfit_id);
+
+ void refreshList(const LLUUID& category_id);
+ void onCOFChanged();
+ void onGesturesChanged();
+ void computeDifference(const LLInventoryModel::cat_array_t vcats, const LLInventoryModel::item_array_t vitems, uuid_vec_t& vadded, uuid_vec_t& vremoved);
+
+ void deselectItem(const LLUUID& category_id);
+ void clearSelection();
+ void changeItemSelection(const LLUUID& item_id, bool scroll_to_selection = false);
+ void addItemSelection(const LLUUID& item_id, bool scroll_to_selection = false);
+ bool toggleItemSelection(const LLUUID& item_id, bool scroll_to_selection = false);
+ void scrollToShowItem(const LLUUID& item_id);
+ void signalSelectionItemID(const LLUUID& category_id);
+ boost::signals2::connection setSelectionChangeCallback(selection_change_callback_t cb);
+ LLUUID getFirstSelectedItemID();
+
+ void setSearchType(LLInventoryFilter::ESearchType type);
+ LLInventoryFilter::ESearchType getSearchType() { return mSearchType; }
+
+ bool areViewsInitialized();
+ bool hasDescendents(const LLUUID& cat_id);
+ bool hasVisibleItems();
+ void handleModifiedFilter();
+ LLScrollContainer* getScrollableContainer() { return mScrollPanel; }
+ LLInventoryGalleryItem* getFirstSelectedItem();
+
+ // Copy & paste (LLEditMenuHandler)
+ void copy() override;
+ BOOL canCopy() const override;
+
+ void cut() override;
+ BOOL canCut() const override;
+
+ void paste() override;
+ BOOL canPaste() const override;
+
+ // Copy & paste & delete
+ static void onDelete(const LLSD& notification, const LLSD& response, const selection_deque selected_ids);
+ void deleteSelection();
+ bool canDeleteSelection();
+ void pasteAsLink();
+
+ void setSortOrder(U32 order, bool update = false);
+ U32 getSortOrder() { return mSortOrder; };
+
+ void claimEditHandler();
+ void resetEditHandler();
+ static bool isItemCopyable(const LLUUID & item_id);
+
+ BOOL baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, EDragAndDropType cargo_type,
+ void* cargo_data, EAcceptance* accept, std::string& tooltip_msg);
+
+ void showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id);
+
+protected:
+ void paste(const LLUUID& dest,
+ std::vector<LLUUID>& objects,
+ bool is_cut_mode,
+ const LLUUID& marketplacelistings_id);
+ void pasteAsLink(const LLUUID& dest,
+ std::vector<LLUUID>& objects,
+ const LLUUID& current_outfit_id,
+ const LLUUID& marketplacelistings_id,
+ const LLUUID& my_outifts_id);
+
+ bool applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring);
+ bool checkAgainstFilters(LLInventoryGalleryItem* item, const std::string& filter_substring);
+ static void onIdle(void* userdata);
+ void dirtyRootFolder();
+
+ LLInventoryCategoriesObserver* mCategoriesObserver;
+ LLThumbnailsObserver* mThumbnailsObserver;
+ LLGalleryGestureObserver* mGestureObserver;
+ LLInventoryObserver* mInventoryObserver;
+ selection_deque mSelectedItemIDs;
+ selection_deque mItemsToSelect;
+ LLUUID mLastInteractedUUID;
+ bool mIsInitialized;
+ bool mRootDirty;
+
+ selection_change_signal_t mSelectionChangeSignal;
+ boost::signals2::signal<void()> mRootChangedSignal;
+ LLUUID mFolderID;
+ std::list<LLUUID> mBackwardFolders;
+ std::list<LLUUID> mForwardFolders;
+
+private:
+ void addToGallery(LLInventoryGalleryItem* item);
+ void removeFromGalleryLast(LLInventoryGalleryItem* item, bool needs_reshape = true);
+ void removeFromGalleryMiddle(LLInventoryGalleryItem* item);
+ LLPanel* addLastRow();
+ void removeLastRow();
+ void moveRowUp(int row);
+ void moveRowDown(int row);
+ void moveRow(int row, int pos);
+ LLPanel* addToRow(LLPanel* row_stack, LLInventoryGalleryItem* item, int pos, int hgap);
+ void removeFromLastRow(LLInventoryGalleryItem* item);
+ void reArrangeRows(S32 row_diff = 0);
+ bool updateRowsIfNeeded();
+ void updateGalleryWidth();
+
+ LLInventoryGalleryItem* buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn);
+
+ void buildGalleryPanel(int row_count);
+ void reshapeGalleryPanel(int row_count);
+ LLPanel* buildItemPanel(int left);
+ LLPanel* buildRowPanel(int left, int bottom);
+ void moveRowPanel(LLPanel* stack, int left, int bottom);
+
+ std::vector<LLPanel*> mRowPanels;
+ std::vector<LLPanel*> mItemPanels;
+ std::vector<LLPanel*> mUnusedRowPanels;
+ std::vector<LLPanel*> mUnusedItemPanels;
+ std::vector<LLInventoryGalleryItem*> mItems;
+ std::vector<LLInventoryGalleryItem*> mHiddenItems;
+ LLScrollContainer* mScrollPanel;
+ LLPanel* mGalleryPanel;
+ LLPanel* mLastRowPanel;
+ LLTextBox* mMessageTextBox;
+ int mRowCount;
+ int mItemsAddedCount;
+ bool mGalleryCreated;
+ bool mNeedsArrange;
+
+ /* Params */
+ int mRowPanelHeight;
+ int mVerticalGap;
+ int mHorizontalGap;
+ int mItemWidth;
+ int mItemHeight;
+ int mItemHorizontalGap;
+ int mItemsInRow;
+ int mRowPanelWidth;
+ int mGalleryWidth;
+ int mRowPanWidthFactor;
+ int mGalleryWidthFactor;
+
+ LLInventoryGalleryContextMenu* mInventoryGalleryMenu;
+ LLInventoryGalleryContextMenu* mRootGalleryMenu;
+ std::string mFilterSubString;
+ LLInventoryFilter* mFilter;
+ U32 mSortOrder;
+
+ typedef std::map<LLUUID, LLInventoryGalleryItem*> gallery_item_map_t;
+ gallery_item_map_t mItemMap;
+ uuid_vec_t mCOFLinkedItems;
+ uuid_vec_t mActiveGestures;
+ uuid_set_t mItemBuildQuery;
+ std::map<LLInventoryGalleryItem*, S32> mItemIndexMap;
+ std::map<S32, LLInventoryGalleryItem*> mIndexToItemMap;
+
+ LLInventoryFilter::ESearchType mSearchType;
+ std::string mUsername;
+};
+
+class LLInventoryGalleryItem : public LLPanel
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {};
+
+ enum EInventorySortGroup
+ {
+ SG_SYSTEM_FOLDER,
+ SG_TRASH_FOLDER,
+ SG_NORMAL_FOLDER,
+ SG_ITEM
+ };
+
+ LLInventoryGalleryItem(const Params& p);
+ virtual ~LLInventoryGalleryItem();
+
+ BOOL postBuild();
+ void draw();
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ BOOL handleHover(S32 x, S32 y, MASK mask);
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+ BOOL handleKeyHere(KEY key, MASK mask);
+
+ void onFocusLost();
+ void onFocusReceived();
+
+ LLFontGL* getTextFont();
+
+ void setItemName(std::string name);
+ bool isSelected() { return mSelected; }
+ void setSelected(bool value);
+ void setWorn(bool value);
+ void setUUID(LLUUID id) {mUUID = id;}
+ LLUUID getUUID() { return mUUID;}
+
+ void setAssetIDStr(std::string asset_id) {mAssetIDStr = asset_id;}
+ std::string getAssetIDStr() { return mAssetIDStr;}
+ void setDescription(std::string desc) {mDesc = desc;}
+ std::string getDescription() { return mDesc;}
+ void setCreatorName(std::string name) {mCreatorName = name;}
+ std::string getCreatorName() { return mCreatorName;}
+ void setCreationDate(time_t date) {mCreationDate = date;}
+ time_t getCreationDate() { return mCreationDate;}
+
+ std::string getItemName() {return mItemName;}
+ std::string getItemNameSuffix() {return mPermSuffix + mWornSuffix;}
+ bool isDefaultImage() {return mDefaultImage;}
+
+ bool isHidden() {return mHidden;}
+ void setHidden(bool hidden) {mHidden = hidden;}
+
+ void setType(LLAssetType::EType type, LLInventoryType::EType inventory_type, U32 flags, bool is_link);
+ LLAssetType::EType getAssetType() { return mType; }
+ void setThumbnail(LLUUID id);
+ void setGallery(LLInventoryGallery* gallery) { mGallery = gallery; }
+ bool isFolder() { return mIsFolder; }
+ bool isLink() { return mIsLink; }
+ EInventorySortGroup getSortGroup() { return mSortGroup; }
+
+ void updateNameText();
+
+private:
+ bool isFadeItem();
+
+ LLUUID mUUID;
+ LLTextBox* mNameText;
+ LLPanel* mTextBgPanel;
+ bool mSelected;
+ bool mWorn;
+ bool mDefaultImage;
+ bool mHidden;
+ bool mIsFolder;
+ bool mIsLink;
+ S32 mCutGeneration;
+ bool mSelectedForCut;
+
+ std::string mAssetIDStr;
+ std::string mDesc;
+ std::string mCreatorName;
+ time_t mCreationDate;
+
+ EInventorySortGroup mSortGroup;
+ LLAssetType::EType mType;
+ std::string mItemName;
+ std::string mWornSuffix;
+ std::string mPermSuffix;
+ LLInventoryGallery* mGallery;
+};
+
+class LLThumbnailsObserver : public LLInventoryObserver
+{
+public:
+ LLThumbnailsObserver(){};
+
+ virtual void changed(U32 mask);
+ bool addItem(const LLUUID& obj_id, callback_t cb);
+ void removeItem(const LLUUID& obj_id);
+
+protected:
+
+ struct LLItemData
+ {
+ LLItemData(const LLUUID& obj_id, const LLUUID& thumbnail_id, callback_t cb)
+ : mItemID(obj_id)
+ , mCallback(cb)
+ , mThumbnailID(thumbnail_id)
+ {}
+
+ callback_t mCallback;
+ LLUUID mItemID;
+ LLUUID mThumbnailID;
+ };
+
+ typedef std::map<LLUUID, LLItemData> item_map_t;
+ typedef item_map_t::value_type item_map_value_t;
+ item_map_t mItemMap;
+};
+
+class LLGalleryGestureObserver : public LLGestureManagerObserver
+{
+public:
+ LLGalleryGestureObserver(LLInventoryGallery* gallery) : mGallery(gallery) {}
+ virtual ~LLGalleryGestureObserver() {}
+ virtual void changed() { mGallery->onGesturesChanged(); }
+
+private:
+ LLInventoryGallery* mGallery;
+};
+
+#endif
diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp
new file mode 100644
index 0000000000..5f4b816b99
--- /dev/null
+++ b/indra/newview/llinventorygallerymenu.cpp
@@ -0,0 +1,714 @@
+/**
+ * @file llinventorygallerymenu.cpp
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llinventorygallery.h"
+#include "llinventorygallerymenu.h"
+
+#include "llagent.h"
+#include "llappearancemgr.h"
+#include "llavataractions.h"
+#include "llclipboard.h"
+#include "llfloaterreg.h"
+#include "llfloatersidepanelcontainer.h"
+#include "llfloaterworldmap.h"
+#include "llinventorybridge.h"
+#include "llinventoryfunctions.h"
+#include "llinventorymodel.h"
+#include "lllandmarkactions.h"
+#include "llmarketplacefunctions.h"
+#include "llmenugl.h"
+#include "llnotificationsutil.h"
+#include "llpreviewtexture.h"
+#include "lltrans.h"
+#include "llviewerfoldertype.h"
+#include "llviewerwindow.h"
+#include "llvoavatarself.h"
+
+LLContextMenu* LLInventoryGalleryContextMenu::createMenu()
+{
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+
+ registrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryGalleryContextMenu::doToSelected, this, _2));
+ registrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::fileUploadLocation, this, _2));
+ registrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH));
+ registrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND));
+
+ std::set<LLUUID> uuids(mUUIDs.begin(), mUUIDs.end());
+ registrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, uuids, gFloaterView->getParentFloater(mGallery)));
+
+ enable_registrar.add("Inventory.CanSetUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::canSetUploadLocation, this, _2));
+
+ LLContextMenu* menu = createFromFile("menu_gallery_inventory.xml");
+
+ updateMenuItemsVisibility(menu);
+
+ return menu;
+}
+
+void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata)
+{
+ std::string action = userdata.asString();
+ LLInventoryObject* obj = gInventory.getObject(mUUIDs.front());
+ if(!obj) return;
+
+ if ("open_selected_folder" == action)
+ {
+ mGallery->setRootFolder(mUUIDs.front());
+ }
+ else if ("open_in_new_window" == action)
+ {
+ new_folder_window(mUUIDs.front());
+ }
+ else if ("properties" == action)
+ {
+ show_item_profile(mUUIDs.front());
+ }
+ else if ("restore" == action)
+ {
+ for (LLUUID& selected_id : mUUIDs)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id);
+ if (cat)
+ {
+ const LLUUID new_parent = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType()));
+ // do not restamp children on restore
+ gInventory.changeCategoryParent(cat, new_parent, false);
+ }
+ else
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(selected_id);
+ if (item)
+ {
+ bool is_snapshot = (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT);
+
+ const LLUUID new_parent = gInventory.findCategoryUUIDForType(is_snapshot ? LLFolderType::FT_SNAPSHOT_CATEGORY : LLFolderType::assetTypeToFolderType(item->getType()));
+ // do not restamp children on restore
+ gInventory.changeItemParent(item, new_parent, false);
+ }
+ }
+ }
+ }
+ else if ("copy_uuid" == action)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front());
+ if(item)
+ {
+ LLUUID asset_id = item->getProtectedAssetUUID();
+ std::string buffer;
+ asset_id.toString(buffer);
+
+ gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer));
+ }
+ }
+ else if ("purge" == action)
+ {
+ for (LLUUID& selected_id : mUUIDs)
+ {
+ remove_inventory_object(selected_id, NULL);
+ }
+ }
+ else if ("goto" == action)
+ {
+ show_item_original(mUUIDs.front());
+ }
+ else if ("thumbnail" == action)
+ {
+ LLSD data(mUUIDs.front());
+ LLFloaterReg::showInstance("change_item_thumbnail", data);
+ }
+ else if ("cut" == action)
+ {
+ if (mGallery->canCut())
+ {
+ mGallery->cut();
+ }
+ }
+ else if ("paste" == action)
+ {
+ if (mGallery->canPaste())
+ {
+ mGallery->paste();
+ }
+ }
+ else if ("delete" == action)
+ {
+ mGallery->deleteSelection();
+ }
+ else if ("copy" == action)
+ {
+ if (mGallery->canCopy())
+ {
+ mGallery->copy();
+ }
+ }
+ else if ("paste_link" == action)
+ {
+ mGallery->pasteAsLink();
+ }
+ else if ("rename" == action)
+ {
+ rename(mUUIDs.front());
+ }
+ else if ("open" == action || "open_original" == action)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front());
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(), mUUIDs.front(), &gInventory);
+ }
+ }
+ else if ("ungroup_folder_items" == action)
+ {
+ ungroup_folder_items(mUUIDs.front());
+ }
+ else if ("take_off" == action || "detach" == action)
+ {
+ for (LLUUID& selected_id : mUUIDs)
+ {
+ LLAppearanceMgr::instance().removeItemFromAvatar(selected_id);
+ }
+ }
+ else if ("wear_add" == action)
+ {
+ for (LLUUID& selected_id : mUUIDs)
+ {
+ LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, false); // Don't replace if adding.
+ }
+ }
+ else if ("wear" == action)
+ {
+ for (LLUUID& selected_id : mUUIDs)
+ {
+ LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, true);
+ }
+ }
+ else if ("activate" == action)
+ {
+ for (LLUUID& selected_id : mUUIDs)
+ {
+ LLGestureMgr::instance().activateGesture(selected_id);
+
+ LLViewerInventoryItem* item = gInventory.getItem(selected_id);
+ if (!item) return;
+
+ gInventory.updateItem(item);
+ }
+ gInventory.notifyObservers();
+ }
+ else if ("deactivate" == action)
+ {
+ for (LLUUID& selected_id : mUUIDs)
+ {
+ LLGestureMgr::instance().deactivateGesture(selected_id);
+
+ LLViewerInventoryItem* item = gInventory.getItem(selected_id);
+ if (!item) return;
+
+ gInventory.updateItem(item);
+ }
+ gInventory.notifyObservers();
+ }
+ else if ("replace_links" == action)
+ {
+ LLFloaterReg::showInstance("linkreplace", LLSD(mUUIDs.front()));
+ }
+ else if ("copy_slurl" == action)
+ {
+ boost::function<void(LLLandmark*)> copy_slurl_cb = [](LLLandmark* landmark)
+ {
+ LLVector3d global_pos;
+ landmark->getGlobalPos(global_pos);
+ boost::function<void(std::string& slurl)> copy_slurl_to_clipboard_cb = [](const std::string& slurl)
+ {
+ gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
+ LLSD args;
+ args["SLURL"] = slurl;
+ LLNotificationsUtil::add("CopySLURL", args);
+ };
+ LLLandmarkActions::getSLURLfromPosGlobal(global_pos, copy_slurl_to_clipboard_cb, true);
+ };
+ LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), copy_slurl_cb);
+ if (landmark)
+ {
+ copy_slurl_cb(landmark);
+ }
+ }
+ else if ("about" == action)
+ {
+ LLSD key;
+ key["type"] = "landmark";
+ key["id"] = mUUIDs.front();
+ LLFloaterSidePanelContainer::showPanel("places", key);
+ }
+ else if ("show_on_map" == action)
+ {
+ boost::function<void(LLLandmark*)> show_on_map_cb = [](LLLandmark* landmark)
+ {
+ LLVector3d landmark_global_pos;
+ if (landmark->getGlobalPos(landmark_global_pos))
+ {
+ LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
+ if (!landmark_global_pos.isExactlyZero() && worldmap_instance)
+ {
+ worldmap_instance->trackLocation(landmark_global_pos);
+ LLFloaterReg::showInstance("world_map", "center");
+ }
+ }
+ };
+ LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), show_on_map_cb);
+ if(landmark)
+ {
+ show_on_map_cb(landmark);
+ }
+ }
+ else if ("save_as" == action)
+ {
+ LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance<LLPreviewTexture>("preview_texture", mUUIDs.front());
+ if (preview_texture)
+ {
+ preview_texture->openToSave();
+ preview_texture->saveAs();
+ }
+ }
+}
+
+void LLInventoryGalleryContextMenu::rename(const LLUUID& item_id)
+{
+ LLInventoryObject* obj = gInventory.getObject(item_id);
+ if (!obj) return;
+
+ LLSD args;
+ args["NAME"] = obj->getName();
+
+ LLSD payload;
+ payload["id"] = mUUIDs.front();
+
+ LLNotificationsUtil::add("RenameItem", args, payload, boost::bind(onRename, _1, _2));
+}
+
+void LLInventoryGalleryContextMenu::onRename(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option != 0) return; // canceled
+
+ std::string new_name = response["new_name"].asString();
+ LLStringUtil::trim(new_name);
+ if (!new_name.empty())
+ {
+ LLUUID id = notification["payload"]["id"].asUUID();
+
+ LLViewerInventoryCategory* cat = gInventory.getCategory(id);
+ if(cat && (cat->getName() != new_name))
+ {
+ LLSD updates;
+ updates["name"] = new_name;
+ update_inventory_category(cat->getUUID(),updates, NULL);
+ return;
+ }
+
+ LLViewerInventoryItem* item = gInventory.getItem(id);
+ if(item && (item->getName() != new_name))
+ {
+ LLSD updates;
+ updates["name"] = new_name;
+ update_inventory_item(item->getUUID(),updates, NULL);
+ }
+ }
+}
+
+void LLInventoryGalleryContextMenu::fileUploadLocation(const LLSD& userdata)
+{
+ const std::string param = userdata.asString();
+ if (param == "model")
+ {
+ gSavedPerAccountSettings.setString("ModelUploadFolder", mUUIDs.front().asString());
+ }
+ else if (param == "texture")
+ {
+ gSavedPerAccountSettings.setString("TextureUploadFolder", mUUIDs.front().asString());
+ }
+ else if (param == "sound")
+ {
+ gSavedPerAccountSettings.setString("SoundUploadFolder", mUUIDs.front().asString());
+ }
+ else if (param == "animation")
+ {
+ gSavedPerAccountSettings.setString("AnimationUploadFolder", mUUIDs.front().asString());
+ }
+}
+
+bool LLInventoryGalleryContextMenu::canSetUploadLocation(const LLSD& userdata)
+{
+ if (mUUIDs.size() != 1)
+ {
+ return false;
+ }
+ LLInventoryCategory* cat = gInventory.getCategory(mUUIDs.front());
+ if (!cat)
+ {
+ return false;
+ }
+ return true;
+}
+
+bool is_inbox_folder(LLUUID item_id)
+{
+ const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX);
+
+ if (inbox_id.isNull())
+ {
+ return false;
+ }
+
+ return gInventory.isObjectDescendentOf(item_id, inbox_id);
+}
+
+void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* menu)
+{
+ LLUUID selected_id = mUUIDs.front();
+ LLInventoryObject* obj = gInventory.getObject(selected_id);
+ if (!obj)
+ {
+ return;
+ }
+
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ bool is_agent_inventory = gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID());
+ bool is_link = obj->getIsLinkType();
+ bool is_folder = (obj->getType() == LLAssetType::AT_CATEGORY);
+ bool is_cof = LLAppearanceMgr::instance().getIsInCOF(selected_id);
+ bool is_inbox = is_inbox_folder(selected_id);
+ bool is_trash = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH));
+ bool is_in_trash = gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH));
+ bool is_lost_and_found = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
+ bool is_outfits= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));
+ //bool is_favorites= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE));
+
+ bool is_system_folder = false;
+ LLFolderType::EType folder_type(LLFolderType::FT_NONE);
+ bool has_children = false;
+ bool is_full_perm_item = false;
+ bool is_copyable = false;
+ LLViewerInventoryItem* selected_item = gInventory.getItem(selected_id);
+
+ if(is_folder)
+ {
+ LLInventoryCategory* category = gInventory.getCategory(selected_id);
+ if (category)
+ {
+ folder_type = category->getPreferredType();
+ is_system_folder = LLFolderType::lookupIsProtectedType(folder_type);
+ has_children = (gInventory.categoryHasChildren(selected_id) != LLInventoryModel::CHILDREN_NO);
+ }
+ }
+ else
+ {
+ if (selected_item)
+ {
+ is_full_perm_item = selected_item->getIsFullPerm();
+ is_copyable = selected_item->getPermissions().allowCopyBy(gAgent.getID());
+ }
+ }
+
+ if(!is_link)
+ {
+ items.push_back(std::string("thumbnail"));
+ if (!is_agent_inventory || (is_in_trash && !is_trash))
+ {
+ disabled_items.push_back(std::string("thumbnail"));
+ }
+ }
+
+ if (is_folder)
+ {
+ if(!isRootFolder())
+ {
+ items.push_back(std::string("Copy Separator"));
+
+ items.push_back(std::string("open_in_current_window"));
+ items.push_back(std::string("open_in_new_window"));
+ items.push_back(std::string("Open Folder Separator"));
+ }
+ }
+ else
+ {
+ if (is_agent_inventory && (obj->getType() != LLAssetType::AT_LINK_FOLDER))
+ {
+ items.push_back(std::string("Replace Links"));
+ }
+ if (obj->getType() == LLAssetType::AT_LANDMARK)
+ {
+ items.push_back(std::string("Landmark Separator"));
+ items.push_back(std::string("url_copy"));
+ items.push_back(std::string("About Landmark"));
+ items.push_back(std::string("show_on_map"));
+ }
+ }
+
+ if(is_trash)
+ {
+ items.push_back(std::string("Empty Trash"));
+
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ gInventory.getDirectDescendentsOf(selected_id, cat_array, item_array);
+ if (0 == cat_array->size() && 0 == item_array->size())
+ {
+ disabled_items.push_back(std::string("Empty Trash"));
+ }
+ }
+ else if(is_in_trash)
+ {
+ if (is_link)
+ {
+ items.push_back(std::string("Find Original"));
+ if (LLAssetType::lookupIsLinkType(obj->getType()))
+ {
+ disabled_items.push_back(std::string("Find Original"));
+ }
+ }
+ items.push_back(std::string("Purge Item"));
+ if (is_folder && !get_is_category_removable(&gInventory, selected_id))
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ if(can_share_item(selected_id))
+ {
+ items.push_back(std::string("Share"));
+ }
+ if (LLClipboard::instance().hasContents() && is_agent_inventory && !is_cof && !is_inbox_folder(selected_id))
+ {
+ items.push_back(std::string("Paste"));
+
+ static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true);
+ if (inventory_linking)
+ {
+ items.push_back(std::string("Paste As Link"));
+ }
+ }
+ if (is_folder && is_agent_inventory)
+ {
+ if (!is_cof && (folder_type != LLFolderType::FT_OUTFIT) && !is_outfits && !is_inbox_folder(selected_id))
+ {
+ if (!gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD)) && !isRootFolder())
+ {
+ items.push_back(std::string("New Folder"));
+ }
+ items.push_back(std::string("upload_def"));
+ }
+
+ if(is_outfits && !isRootFolder())
+ {
+ items.push_back(std::string("New Outfit"));
+ }
+
+ items.push_back(std::string("Subfolder Separator"));
+ if (!is_system_folder && !isRootFolder())
+ {
+ if(has_children && (folder_type != LLFolderType::FT_OUTFIT))
+ {
+ items.push_back(std::string("Ungroup folder items"));
+ }
+ items.push_back(std::string("Cut"));
+ items.push_back(std::string("Delete"));
+ if(!get_is_category_removable(&gInventory, selected_id))
+ {
+ disabled_items.push_back(std::string("Delete"));
+ disabled_items.push_back(std::string("Cut"));
+ }
+
+ if(!is_inbox)
+ {
+ items.push_back(std::string("Rename"));
+ }
+ }
+ if(!is_system_folder)
+ {
+ items.push_back(std::string("Copy"));
+ }
+ }
+ else if(!is_folder)
+ {
+ items.push_back(std::string("Properties"));
+ items.push_back(std::string("Copy Asset UUID"));
+ items.push_back(std::string("Copy Separator"));
+
+ bool is_asset_knowable = is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(obj->getType());
+ if ( !is_asset_knowable // disable menu item for Inventory items with unknown asset. EXT-5308
+ || (! ( is_full_perm_item || gAgent.isGodlike())))
+ {
+ disabled_items.push_back(std::string("Copy Asset UUID"));
+ }
+ if(is_agent_inventory)
+ {
+ items.push_back(std::string("Cut"));
+ if (!is_link || !is_cof || !get_is_item_worn(selected_id))
+ {
+ items.push_back(std::string("Delete"));
+ }
+ if(!get_is_item_removable(&gInventory, selected_id))
+ {
+ disabled_items.push_back(std::string("Delete"));
+ disabled_items.push_back(std::string("Cut"));
+ }
+
+ if (selected_item && (selected_item->getInventoryType() != LLInventoryType::IT_CALLINGCARD) && !is_inbox && selected_item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID()))
+ {
+ items.push_back(std::string("Rename"));
+ }
+ }
+ items.push_back(std::string("Copy"));
+ if (!is_copyable)
+ {
+ disabled_items.push_back(std::string("Copy"));
+ }
+ }
+ if((obj->getType() == LLAssetType::AT_SETTINGS)
+ || ((obj->getType() <= LLAssetType::AT_GESTURE)
+ && obj->getType() != LLAssetType::AT_OBJECT
+ && obj->getType() != LLAssetType::AT_CLOTHING
+ && obj->getType() != LLAssetType::AT_CATEGORY
+ && obj->getType() != LLAssetType::AT_LANDMARK
+ && obj->getType() != LLAssetType::AT_BODYPART))
+ {
+ bool can_open = !LLAssetType::lookupIsLinkType(obj->getType());
+
+ if (can_open)
+ {
+ if (is_link)
+ items.push_back(std::string("Open Original"));
+ else
+ items.push_back(std::string("Open"));
+ }
+ else
+ {
+ disabled_items.push_back(std::string("Open"));
+ disabled_items.push_back(std::string("Open Original"));
+ }
+
+ if(LLAssetType::AT_GESTURE == obj->getType())
+ {
+ items.push_back(std::string("Gesture Separator"));
+ if(!LLGestureMgr::instance().isGestureActive(selected_id))
+ {
+ items.push_back(std::string("Activate"));
+ }
+ else
+ {
+ items.push_back(std::string("Deactivate"));
+ }
+ }
+ }
+ else if(LLAssetType::AT_LANDMARK == obj->getType())
+ {
+ items.push_back(std::string("Landmark Open"));
+ }
+ else if (obj->getType() == LLAssetType::AT_OBJECT || obj->getType() == LLAssetType::AT_CLOTHING || obj->getType() == LLAssetType::AT_BODYPART)
+ {
+ items.push_back(std::string("Wearable And Object Separator"));
+ if(obj->getType() == LLAssetType::AT_CLOTHING)
+ {
+ items.push_back(std::string("Take Off"));
+ }
+ if(get_is_item_worn(selected_id))
+ {
+ if(obj->getType() == LLAssetType::AT_OBJECT)
+ {
+ items.push_back(std::string("Detach From Yourself"));
+ }
+ disabled_items.push_back(std::string("Wearable And Object Wear"));
+ disabled_items.push_back(std::string("Wearable Add"));
+ }
+ else
+ {
+ if(obj->getType() == LLAssetType::AT_OBJECT)
+ {
+ items.push_back(std::string("Wearable Add"));
+ }
+ items.push_back(std::string("Wearable And Object Wear"));
+ disabled_items.push_back(std::string("Take Off"));
+ }
+
+ if (!gAgentAvatarp->canAttachMoreObjects() && (obj->getType() == LLAssetType::AT_OBJECT))
+ {
+ disabled_items.push_back(std::string("Wearable And Object Wear"));
+ disabled_items.push_back(std::string("Wearable Add"));
+ }
+ if (selected_item && (obj->getType() != LLAssetType::AT_OBJECT) && LLWearableType::getInstance()->getAllowMultiwear(selected_item->getWearableType()))
+ {
+ items.push_back(std::string("Wearable Add"));
+ if (!gAgentWearables.canAddWearable(selected_item->getWearableType()))
+ {
+ disabled_items.push_back(std::string("Wearable Add"));
+ }
+ }
+ }
+ if(obj->getType() == LLAssetType::AT_TEXTURE)
+ {
+ items.push_back(std::string("Save As"));
+ bool can_copy = selected_item && selected_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED);
+ if (!can_copy)
+ {
+ disabled_items.push_back(std::string("Save As"));
+ }
+ }
+ if (is_link)
+ {
+ items.push_back(std::string("Find Original"));
+ if (LLAssetType::lookupIsLinkType(obj->getType()))
+ {
+ disabled_items.push_back(std::string("Find Original"));
+ }
+ }
+ if (is_lost_and_found)
+ {
+ items.push_back(std::string("Empty Lost And Found"));
+
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ gInventory.getDirectDescendentsOf(selected_id, cat_array, item_array);
+ // Enable Empty menu item only when there is something to act upon.
+ if (0 == cat_array->size() && 0 == item_array->size())
+ {
+ disabled_items.push_back(std::string("Empty Lost And Found"));
+ }
+
+ disabled_items.push_back(std::string("New Folder"));
+ disabled_items.push_back(std::string("upload_def"));
+ }
+ }
+
+ hide_context_entries(*menu, items, disabled_items);
+}
+
diff --git a/indra/newview/llinventorygallerymenu.h b/indra/newview/llinventorygallerymenu.h
new file mode 100644
index 0000000000..7c3545432b
--- /dev/null
+++ b/indra/newview/llinventorygallerymenu.h
@@ -0,0 +1,62 @@
+/**
+ * @file llinventorygallerymenu.h
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLINVENTORYGALLERYMENU_H
+#define LL_LLINVENTORYGALLERYMENU_H
+
+#include "lllistcontextmenu.h"
+
+class LLInventoryGalleryContextMenu : public LLListContextMenu
+{
+public:
+ LLInventoryGalleryContextMenu(LLInventoryGallery* gallery)
+ : LLListContextMenu(),
+ mGallery(gallery),
+ mRootFolder(false){}
+ /*virtual*/ LLContextMenu* createMenu();
+
+ bool isRootFolder() { return mRootFolder; }
+ void setRootFolder(bool is_root) { mRootFolder = is_root; }
+ void doToSelected(const LLSD& userdata);
+ void rename(const LLUUID& item_id);
+
+protected:
+ //virtual void buildContextMenu(class LLMenuGL& menu, U32 flags);
+ void updateMenuItemsVisibility(LLContextMenu* menu);
+
+ void fileUploadLocation(const LLSD& userdata);
+ bool canSetUploadLocation(const LLSD& userdata);
+
+ static void onRename(const LLSD& notification, const LLSD& response);
+
+private:
+ bool enableContextMenuItem(const LLSD& userdata);
+ bool checkContextMenuItem(const LLSD& userdata);
+
+ LLInventoryGallery* mGallery;
+ bool mRootFolder;
+};
+
+#endif
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 9c4e122481..ea771661ec 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -37,9 +37,11 @@
#include "llappearancemgr.h"
#include "llavatarnamecache.h"
#include "llclipboard.h"
+#include "lldispatcher.h"
#include "llinventorypanel.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
+#include "llinventorymodelbackgroundfetch.h"
#include "llinventoryobserver.h"
#include "llinventorypanel.h"
#include "llfloaterpreviewtrash.h"
@@ -49,6 +51,7 @@
#include "llviewercontrol.h"
#include "llviewernetwork.h"
#include "llpreview.h"
+#include "llviewergenericmessage.h"
#include "llviewermessage.h"
#include "llviewerfoldertype.h"
#include "llviewerwindow.h"
@@ -74,9 +77,11 @@
// Increment this if the inventory contents change in a non-backwards-compatible way.
// For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect.
-const S32 LLInventoryModel::sCurrentInvCacheVersion = 2;
+const S32 LLInventoryModel::sCurrentInvCacheVersion = 3;
BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE;
+S32 LLInventoryModel::sPendingSystemFolders = 0;
+
///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------
@@ -133,6 +138,222 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
return rv;
}
+struct InventoryCallbackInfo
+{
+ InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) :
+ mCallback(callback), mInvID(inv_id) {}
+ U32 mCallback;
+ LLUUID mInvID;
+};
+
+///----------------------------------------------------------------------------
+/// Class LLDispatchClassifiedClickThrough
+///----------------------------------------------------------------------------
+
+class LLDispatchBulkUpdateInventory : public LLDispatchHandler
+{
+public:
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+ {
+ LLSD message;
+
+ // Expect single string parameter in the form of a notation serialized LLSD.
+ sparam_t::const_iterator it = strings.begin();
+ if (it != strings.end()) {
+ const std::string& llsdRaw = *it++;
+ std::istringstream llsdData(llsdRaw);
+ if (!LLSDSerialize::deserialize(message, llsdData, llsdRaw.length()))
+ {
+ LL_WARNS() << "LLDispatchBulkUpdateInventory: Attempted to read parameter data into LLSD but failed:" << llsdRaw << LL_ENDL;
+ }
+ }
+
+ LLInventoryModel::update_map_t update;
+ LLInventoryModel::cat_array_t folders;
+ LLInventoryModel::item_array_t items;
+ std::list<InventoryCallbackInfo> cblist;
+ uuid_vec_t wearable_ids;
+
+ LLSD item_data = message["item_data"];
+ if (item_data.isArray())
+ {
+ for (LLSD::array_iterator itd = item_data.beginArray(); itd != item_data.endArray(); ++itd)
+ {
+ const LLSD &item(*itd);
+
+ // Agent id probably should be in the root of the message
+ LLUUID agent_id = item["agent_id"].asUUID();
+ if (agent_id != gAgent.getID())
+ {
+ LL_WARNS() << "Got a BulkUpdateInventory for the wrong agent." << LL_ENDL;
+ return false;
+ }
+
+ LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
+ titem->unpackMessage(item);
+ LL_DEBUGS("Inventory") << "unpacked item '" << titem->getName() << "' in "
+ << titem->getParentUUID() << LL_ENDL;
+ // callback id might be no longer supported
+ U32 callback_id = item["callback_id"].asInteger();
+
+ if (titem->getUUID().notNull())
+ {
+ items.push_back(titem);
+ cblist.push_back(InventoryCallbackInfo(callback_id, titem->getUUID()));
+ if (titem->getInventoryType() == LLInventoryType::IT_WEARABLE)
+ {
+ wearable_ids.push_back(titem->getUUID());
+ }
+
+ // examine update for changes.
+ LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
+ if (itemp)
+ {
+ if (titem->getParentUUID() == itemp->getParentUUID())
+ {
+ update[titem->getParentUUID()];
+ }
+ else
+ {
+ ++update[titem->getParentUUID()];
+ --update[itemp->getParentUUID()];
+ }
+ }
+ else
+ {
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID());
+ if (folderp)
+ {
+ ++update[titem->getParentUUID()];
+ }
+ }
+ }
+ else
+ {
+ cblist.push_back(InventoryCallbackInfo(callback_id, LLUUID::null));
+ }
+ }
+ }
+
+ LLSD folder_data = message["folder_data"];
+ if (folder_data.isArray())
+ {
+ for (LLSD::array_iterator itd = folder_data.beginArray(); itd != folder_data.endArray(); ++itd)
+ {
+ const LLSD &folder(*itd);
+
+ LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
+ tfolder->unpackMessage(folder);
+
+ LL_DEBUGS("Inventory") << "unpacked folder '" << tfolder->getName() << "' ("
+ << tfolder->getUUID() << ") in " << tfolder->getParentUUID()
+ << LL_ENDL;
+
+ // If the folder is a listing or a version folder, all we need to do is update the SLM data
+ int depth_folder = depth_nesting_in_marketplace(tfolder->getUUID());
+ if ((depth_folder == 1) || (depth_folder == 2))
+ {
+ // Trigger an SLM listing update
+ LLUUID listing_uuid = (depth_folder == 1 ? tfolder->getUUID() : tfolder->getParentUUID());
+ S32 listing_id = LLMarketplaceData::instance().getListingID(listing_uuid);
+ LLMarketplaceData::instance().getListing(listing_id);
+ // In that case, there is no item to update so no callback -> we skip the rest of the update
+ }
+ else if (tfolder->getUUID().notNull())
+ {
+ folders.push_back(tfolder);
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
+ if (folderp)
+ {
+ if (tfolder->getParentUUID() == folderp->getParentUUID())
+ {
+ update[tfolder->getParentUUID()];
+ }
+ else
+ {
+ ++update[tfolder->getParentUUID()];
+ --update[folderp->getParentUUID()];
+ }
+ }
+ else
+ {
+ // we could not find the folder, so it is probably
+ // new. However, we only want to attempt accounting
+ // for the parent if we can find the parent.
+ folderp = gInventory.getCategory(tfolder->getParentUUID());
+ if (folderp)
+ {
+ ++update[tfolder->getParentUUID()];
+ }
+ }
+ }
+ }
+ }
+
+ gInventory.accountForUpdate(update);
+
+ for (LLInventoryModel::cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit)
+ {
+ gInventory.updateCategory(*cit);
+ }
+ for (LLInventoryModel::item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit)
+ {
+ gInventory.updateItem(*iit);
+ }
+ gInventory.notifyObservers();
+
+ /*
+ Transaction id not included?
+
+ // The incoming inventory could span more than one BulkInventoryUpdate packet,
+ // so record the transaction ID for this purchase, then wear all clothing
+ // that comes in as part of that transaction ID. JC
+ if (LLInventoryState::sWearNewClothing)
+ {
+ LLInventoryState::sWearNewClothingTransactionID = tid;
+ LLInventoryState::sWearNewClothing = FALSE;
+ }
+
+ if (tid.notNull() && tid == LLInventoryState::sWearNewClothingTransactionID)
+ {
+ count = wearable_ids.size();
+ for (i = 0; i < count; ++i)
+ {
+ LLViewerInventoryItem* wearable_item;
+ wearable_item = gInventory.getItem(wearable_ids[i]);
+ LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true);
+ }
+ }
+ */
+
+ if (LLInventoryState::sWearNewClothing && wearable_ids.size() > 0)
+ {
+ LLInventoryState::sWearNewClothing = FALSE;
+
+ size_t count = wearable_ids.size();
+ for (S32 i = 0; i < count; ++i)
+ {
+ LLViewerInventoryItem* wearable_item;
+ wearable_item = gInventory.getItem(wearable_ids[i]);
+ LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true);
+ }
+ }
+
+ std::list<InventoryCallbackInfo>::iterator inv_it;
+ for (inv_it = cblist.begin(); inv_it != cblist.end(); ++inv_it)
+ {
+ InventoryCallbackInfo cbinfo = (*inv_it);
+ gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID);
+ }
+ return true;
+ }
+};
+static LLDispatchBulkUpdateInventory sBulkUpdateInventory;
+
///----------------------------------------------------------------------------
/// Class LLInventoryValidationInfo
///----------------------------------------------------------------------------
@@ -222,6 +443,7 @@ LLInventoryModel::LLInventoryModel()
mIsNotifyObservers(FALSE),
mModifyMask(LLInventoryObserver::ALL),
mChangedItemIDs(),
+ mBulkFecthCallbackSlot(),
mObservers(),
mHttpRequestFG(NULL),
mHttpRequestBG(NULL),
@@ -253,6 +475,11 @@ void LLInventoryModel::cleanupInventory()
mObservers.erase(iter);
delete observer;
}
+
+ if (mBulkFecthCallbackSlot.connected())
+ {
+ mBulkFecthCallbackSlot.disconnect();
+ }
mObservers.clear();
// Run down HTTP transport
@@ -452,6 +679,31 @@ void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
items = get_ptr_in_map(mParentChildItemTree, cat_id);
}
+void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const
+{
+ if (cat_array_t* categoriesp = get_ptr_in_map(mParentChildCategoryTree, cat_id))
+ {
+ for (LLViewerInventoryCategory* pFolder : *categoriesp)
+ {
+ if (f(pFolder, nullptr))
+ {
+ categories.push_back(pFolder);
+ }
+ }
+ }
+
+ if (item_array_t* itemsp = get_ptr_in_map(mParentChildItemTree, cat_id))
+ {
+ for (LLViewerInventoryItem* pItem : *itemsp)
+ {
+ if (f(nullptr, pItem))
+ {
+ items.push_back(pItem);
+ }
+ }
+ }
+}
+
LLInventoryModel::digest_t LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const
{
LLInventoryModel::cat_array_t* cat_array;
@@ -561,10 +813,77 @@ void LLInventoryModel::consolidateForType(const LLUUID& main_id, LLFolderType::E
}
}
+void LLInventoryModel::ensureCategoryForTypeExists(LLFolderType::EType preferred_type)
+{
+ LLUUID rv = LLUUID::null;
+ LLUUID root_id = gInventory.getRootFolderID();
+ if (LLFolderType::FT_ROOT_INVENTORY == preferred_type)
+ {
+ rv = root_id;
+ }
+ else if (root_id.notNull())
+ {
+ cat_array_t* cats = NULL;
+ cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
+ if (cats)
+ {
+ S32 count = cats->size();
+ for (S32 i = 0; i < count; ++i)
+ {
+ LLViewerInventoryCategory* p_cat = cats->at(i);
+ if (p_cat && p_cat->getPreferredType() == preferred_type)
+ {
+ const LLUUID& folder_id = cats->at(i)->getUUID();
+ if (rv.isNull() || folder_id < rv)
+ {
+ rv = folder_id;
+ }
+ }
+ }
+ }
+ }
+
+ if (rv.isNull() && root_id.notNull())
+ {
+
+ if (isInventoryUsable())
+ {
+ createNewCategory(
+ root_id,
+ preferred_type,
+ LLStringUtil::null,
+ [preferred_type](const LLUUID &new_cat_id)
+ {
+ if (new_cat_id.isNull())
+ {
+ LL_WARNS("Inventory")
+ << "Failed to create folder of type " << preferred_type
+ << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("Inventory") << "Created category: " << new_cat_id
+ << " for type: " << preferred_type << LL_ENDL;
+ sPendingSystemFolders--;
+ }
+ }
+ );
+ }
+ else
+ {
+ LL_WARNS("Inventory") << "Can't create requested folder, type " << preferred_type
+ << " because inventory is not usable" << LL_ENDL;
+ }
+ }
+ else
+ {
+ sPendingSystemFolders--;
+ }
+}
+
const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot(
LLFolderType::EType preferred_type,
- bool create_folder,
- const LLUUID& root_id)
+ const LLUUID& root_id) const
{
LLUUID rv = LLUUID::null;
if(LLFolderType::FT_ROOT_INVENTORY == preferred_type)
@@ -595,20 +914,15 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot(
if(rv.isNull()
&& root_id.notNull()
- && create_folder
&& preferred_type != LLFolderType::FT_MARKETPLACE_LISTINGS
&& preferred_type != LLFolderType::FT_OUTBOX)
{
-
- if (isInventoryUsable())
- {
- return createNewCategory(root_id, preferred_type, LLStringUtil::null);
- }
- else
- {
- LL_WARNS("Inventory") << "Can't create requested folder, type " << preferred_type
- << " because inventory is not usable" << LL_ENDL;
- }
+ // if it does not exists, it should either be added
+ // to createCommonSystemCategories or server should
+ // have set it
+ llassert(!isInventoryUsable());
+ LL_WARNS("Inventory") << "Tried to find folder, type " << preferred_type
+ << " but category does not exist" << LL_ENDL;
}
return rv;
}
@@ -617,12 +931,12 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot(
// specifies 'type' as what it defaults to containing. The category is
// not necessarily only for that type. *NOTE: This will create a new
// inventory category on the fly if one does not exist.
-const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder)
+const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type) const
{
- return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getRootFolderID());
+ return findCategoryUUIDForTypeInRoot(preferred_type, gInventory.getRootFolderID());
}
-const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type)
+const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) const
{
LLUUID cat_id;
switch (preferred_type)
@@ -653,40 +967,46 @@ const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType::
if (cat_id.isNull() || !getCategory(cat_id))
{
- cat_id = findCategoryUUIDForTypeInRoot(preferred_type, true, getRootFolderID());
+ cat_id = findCategoryUUIDForTypeInRoot(preferred_type, getRootFolderID());
}
return cat_id;
}
-const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder)
+const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type) const
{
- return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getLibraryRootFolderID());
+ return findCategoryUUIDForTypeInRoot(preferred_type, gInventory.getLibraryRootFolderID());
}
// Convenience function to create a new category. You could call
// updateCategory() with a newly generated UUID category, but this
// version will take care of details like what the name should be
-// based on preferred type. Returns the UUID of the new category.
-LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
+// based on preferred type.
+void LLInventoryModel::createNewCategory(const LLUUID& parent_id,
LLFolderType::EType preferred_type,
const std::string& pname,
- inventory_func_type callback)
+ inventory_func_type callback,
+ const LLUUID& thumbnail_id)
{
LL_DEBUGS(LOG_INV) << "Create '" << pname << "' in '" << make_inventory_path(parent_id) << "'" << LL_ENDL;
- LLUUID id;
if (!isInventoryUsable())
{
LL_WARNS(LOG_INV) << "Inventory is not usable; can't create requested category of type "
<< preferred_type << LL_ENDL;
- // FIXME failing but still returning an id?
- return id;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
}
if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup())
{
LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL;
- // FIXME failing but still returning an id?
- return id;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
+ return;
}
if (preferred_type != LLFolderType::FT_NONE)
@@ -697,26 +1017,72 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
LL_WARNS(LOG_INV) << "Creating new system folder, type " << preferred_type << LL_ENDL;
}
- id.generate();
std::string name = pname;
- if(!pname.empty())
+ if (pname.empty())
{
- name.assign(pname);
+ name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type));
}
- else
+
+ if (AISAPI::isAvailable())
{
- name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type));
+ LLSD new_inventory = LLSD::emptyMap();
+ new_inventory["categories"] = LLSD::emptyArray();
+ LLViewerInventoryCategory cat(LLUUID::null, parent_id, preferred_type, name, gAgent.getID());
+ cat.setThumbnailUUID(thumbnail_id);
+ LLSD cat_sd = cat.asAISCreateCatLLSD();
+ new_inventory["categories"].append(cat_sd);
+ AISAPI::CreateInventory(
+ parent_id,
+ new_inventory,
+ [this, callback, parent_id, preferred_type, name] (const LLUUID& new_category)
+ {
+ if (new_category.isNull())
+ {
+ if (callback && !callback.empty())
+ {
+ callback(new_category);
+ }
+ return;
+ }
+
+ // todo: not needed since AIS does the accounting?
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(new_category);
+ if (!folderp)
+ {
+ // Add the category to the internal representation
+ LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(
+ new_category,
+ parent_id,
+ preferred_type,
+ name,
+ gAgent.getID());
+
+ LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1);
+ accountForUpdate(update);
+
+ cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1
+ cat->setDescendentCount(0);
+ updateCategory(cat);
+ }
+
+ if (callback && !callback.empty())
+ {
+ callback(new_category);
+ }
+ });
+ return;
}
-
+
LLViewerRegion* viewer_region = gAgent.getRegion();
std::string url;
if ( viewer_region )
url = viewer_region->getCapability("CreateInventoryCategory");
- if (!url.empty() && callback)
+ if (!url.empty())
{
//Let's use the new capability.
-
+ LLUUID id;
+ id.generate();
LLSD request, body;
body["folder_id"] = id;
body["parent_id"] = parent_id;
@@ -729,44 +1095,13 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
LL_DEBUGS(LOG_INV) << "Creating category via request: " << ll_pretty_print_sd(request) << LL_ENDL;
LLCoros::instance().launch("LLInventoryModel::createNewCategoryCoro",
boost::bind(&LLInventoryModel::createNewCategoryCoro, this, url, body, callback));
-
- return LLUUID::null;
- }
-
- if (!gMessageSystem)
- {
- return LLUUID::null;
+ return;
}
- // FIXME this UDP code path needs to be removed. Requires
- // reworking many of the callers to use callbacks rather than
- // assuming instant success.
-
- // Add the category to the internal representation
- LLPointer<LLViewerInventoryCategory> cat =
- new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID());
- cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1
- cat->setDescendentCount(0);
- LLCategoryUpdate update(cat->getParentUUID(), 1);
- accountForUpdate(update);
- updateCategory(cat);
-
- LL_DEBUGS(LOG_INV) << "Creating category via UDP message CreateInventoryFolder, type " << preferred_type << LL_ENDL;
-
- // Create the category on the server. We do this to prevent people
- // from munging their protected folders.
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessage("CreateInventoryFolder");
- msg->nextBlock("AgentData");
- msg->addUUID("AgentID", gAgent.getID());
- msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlock("FolderData");
- cat->packMessage(msg);
- gAgent.sendReliableMessage();
-
- LL_INFOS(LOG_INV) << "Created new category '" << make_inventory_path(id) << "'" << LL_ENDL;
- // return the folder id of the newly created folder
- return id;
+ if (callback)
+ {
+ callback(LLUUID::null); // Notify about failure
+ }
}
void LLInventoryModel::createNewCategoryCoro(std::string url, LLSD postData, inventory_func_type callback)
@@ -790,12 +1125,20 @@ void LLInventoryModel::createNewCategoryCoro(std::string url, LLSD postData, inv
if (!status)
{
LL_WARNS() << "HTTP failure attempting to create category." << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
if (!result.has("folder_id"))
{
LL_WARNS() << "Malformed response contents" << ll_pretty_print_sd(result) << LL_ENDL;
+ if (callback)
+ {
+ callback(LLUUID::null);
+ }
return;
}
@@ -1300,7 +1643,7 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32
mask |= LLInventoryObserver::LABEL;
}
// Under marketplace, category labels are quite complex and need extra upate
- const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
if (marketplace_id.notNull() && isObjectDescendentOf(cat->getUUID(), marketplace_id))
{
mask |= LLInventoryObserver::LABEL;
@@ -1442,17 +1785,25 @@ void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat,
notifyObservers();
}
-void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLSD& update)
+void LLInventoryModel::rebuildBrockenLinks()
{
- LLTimer timer;
- if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
- {
- dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update);
- }
+ // make sure we aren't adding expensive Rebuild to anything else.
+ notifyObservers();
- AISUpdate ais_update(update); // parse update llsd into stuff to do.
- ais_update.doUpdate(); // execute the updates in the appropriate order.
- LL_INFOS(LOG_INV) << "elapsed: " << timer.getElapsedTimeF32() << LL_ENDL;
+ for (const broken_links_t::value_type &link_list : mPossiblyBrockenLinks)
+ {
+ for (const LLUUID& link_id : link_list.second)
+ {
+ addChangedMask(LLInventoryObserver::REBUILD , link_id);
+ }
+ }
+ for (const LLUUID& link_id : mLinksRebuildList)
+ {
+ addChangedMask(LLInventoryObserver::REBUILD , link_id);
+ }
+ mPossiblyBrockenLinks.clear();
+ mLinksRebuildList.clear();
+ notifyObservers();
}
// Does not appear to be used currently.
@@ -1758,6 +2109,20 @@ void LLInventoryModel::idleNotifyObservers()
{
// *FIX: Think I want this conditional or moved elsewhere...
handleResponses(true);
+
+ if (mLinksRebuildList.size() > 0)
+ {
+ if (mModifyMask != LLInventoryObserver::NONE || (mChangedItemIDs.size() != 0))
+ {
+ notifyObservers();
+ }
+ for (const LLUUID& link_id : mLinksRebuildList)
+ {
+ addChangedMask(LLInventoryObserver::REBUILD , link_id);
+ }
+ mLinksRebuildList.clear();
+ notifyObservers();
+ }
if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0))
{
@@ -2077,10 +2442,52 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item)
// The item will show up as a broken link.
if (item->getIsBrokenLink())
{
- LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName()
- << " itemID: " << item->getUUID()
- << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL;
+ if (item->getAssetUUID().notNull()
+ && LLInventoryModelBackgroundFetch::getInstance()->folderFetchActive())
+ {
+ // Schedule this link for a recheck as inventory gets loaded
+ // Todo: expand to cover not just an initial fetch
+ mPossiblyBrockenLinks[item->getAssetUUID()].insert(item->getUUID());
+
+ // Do a blank rebuild of links once fetch is done
+ if (!mBulkFecthCallbackSlot.connected())
+ {
+ // Links might take a while to update this way, and there
+ // might be a lot of them. A better option might be to check
+ // links periodically with final check on fetch completion.
+ mBulkFecthCallbackSlot =
+ LLInventoryModelBackgroundFetch::getInstance()->setFetchCompletionCallback(
+ [this]()
+ {
+ // rebuild is just in case, primary purpose is to wipe
+ // the list since we won't be getting anything 'new'
+ // see mLinksRebuildList
+ rebuildBrockenLinks();
+ mBulkFecthCallbackSlot.disconnect();
+ });
+ }
+ LL_DEBUGS(LOG_INV) << "Scheduling a link to be rebuilt later [ name: " << item->getName()
+ << " itemID: " << item->getUUID()
+ << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL;
+
+ }
+ else
+ {
+ LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName()
+ << " itemID: " << item->getUUID()
+ << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL;
+ }
}
+ if (!mPossiblyBrockenLinks.empty())
+ {
+ // check if we are waiting for this item
+ broken_links_t::iterator iter = mPossiblyBrockenLinks.find(item->getUUID());
+ if (iter != mPossiblyBrockenLinks.end())
+ {
+ mLinksRebuildList.insert(iter->second.begin() , iter->second.end());
+ mPossiblyBrockenLinks.erase(iter);
+ }
+ }
if (item->getIsLinkType())
{
// Add back-link from linked-to UUID.
@@ -2348,6 +2755,10 @@ bool LLInventoryModel::loadSkeleton(
else
{
cached_ids.insert(tcat->getUUID());
+
+ // At the moment download does not provide a thumbnail
+ // uuid, use the one from cache
+ tcat->setThumbnailUUID(cat->getThumbnailUUID());
}
}
@@ -2635,7 +3046,7 @@ void LLInventoryModel::buildParentChildMap()
}
}
- const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, FALSE) != LLUUID::null);
+ const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) != LLUUID::null);
sFirstTimeInViewer2 = !COF_exists || gAgent.isFirstLogin();
@@ -2798,6 +3209,11 @@ void LLInventoryModel::initHttpRequest()
mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML);
mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_INVENTORY);
}
+
+ if (!gGenericDispatcher.isHandlerPresent("BulkUpdateInventory"))
+ {
+ gGenericDispatcher.addHandler("BulkUpdateInventory", &sBulkUpdateInventory);
+ }
}
void LLInventoryModel::handleResponses(bool foreground)
@@ -2850,14 +3266,17 @@ LLCore::HttpHandle LLInventoryModel::requestPost(bool foreground,
void LLInventoryModel::createCommonSystemCategories()
{
- gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH,true);
- gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE,true);
- gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD,true);
- gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS,true);
- gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, true);
- gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK, true); // folder should exist before user tries to 'landmark this'
- gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS, true);
- gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, true);
+ //amount of System Folder we should wait for
+ sPendingSystemFolders = 8;
+
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_TRASH);
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_FAVORITE);
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CALLINGCARD);
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_MY_OUTFITS);
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CURRENT_OUTFIT);
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_LANDMARK); // folder should exist before user tries to 'landmark this'
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_SETTINGS);
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_INBOX);
}
struct LLUUIDAndName
@@ -3082,9 +3501,6 @@ void LLInventoryModel::registerCallbacks(LLMessageSystem* msg)
msg->setHandlerFuncFast(_PREHASH_RemoveInventoryItem,
processRemoveInventoryItem,
NULL);
- msg->setHandlerFuncFast(_PREHASH_UpdateInventoryFolder,
- processUpdateInventoryFolder,
- NULL);
msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder,
processRemoveInventoryFolder,
NULL);
@@ -3113,6 +3529,10 @@ void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, vo
msg->getU32Fast(_PREHASH_InventoryData, _PREHASH_CallbackID, callback_id);
gInventoryCallbacks.fire(callback_id, item_id);
+
+ // todo: instead of unpacking message fully,
+ // grab only an item_id, then fetch
+ LLInventoryModelBackgroundFetch::instance().scheduleItemFetch(item_id, true);
}
}
@@ -3228,66 +3648,6 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)
}
// static
-void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg,
- void**)
-{
- LL_DEBUGS(LOG_INV) << "LLInventoryModel::processUpdateInventoryFolder()" << LL_ENDL;
- LLUUID agent_id, folder_id, parent_id;
- //char name[DB_INV_ITEM_NAME_BUF_SIZE];
- msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
- if(agent_id != gAgent.getID())
- {
- LL_WARNS(LOG_INV) << "Got an UpdateInventoryFolder for the wrong agent."
- << LL_ENDL;
- return;
- }
- LLPointer<LLViewerInventoryCategory> lastfolder; // hack
- cat_array_t folders;
- update_map_t update;
- S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
- for(S32 i = 0; i < count; ++i)
- {
- LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
- lastfolder = tfolder;
- tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
- // make sure it's not a protected folder
- tfolder->setPreferredType(LLFolderType::FT_NONE);
- folders.push_back(tfolder);
- // examine update for changes.
- LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
- if(folderp)
- {
- if(tfolder->getParentUUID() == folderp->getParentUUID())
- {
- update[tfolder->getParentUUID()];
- }
- else
- {
- ++update[tfolder->getParentUUID()];
- --update[folderp->getParentUUID()];
- }
- }
- else
- {
- ++update[tfolder->getParentUUID()];
- }
- }
- gInventory.accountForUpdate(update);
- for (cat_array_t::iterator it = folders.begin(); it != folders.end(); ++it)
- {
- gInventory.updateCategory(*it);
- }
- gInventory.notifyObservers();
-
- // *HACK: Do the 'show' logic for a new item in the inventory.
- LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel();
- if (active_panel)
- {
- active_panel->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO);
- }
-}
-
-// static
void LLInventoryModel::removeInventoryFolder(LLUUID agent_id,
LLMessageSystem* msg)
{
@@ -3389,14 +3749,6 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg,
}
}
-struct InventoryCallbackInfo
-{
- InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) :
- mCallback(callback), mInvID(inv_id) {}
- U32 mCallback;
- LLUUID mInvID;
-};
-
// static
void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
{
@@ -3442,15 +3794,22 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
if(folderp)
{
- if(tfolder->getParentUUID() == folderp->getParentUUID())
- {
- update[tfolder->getParentUUID()];
- }
- else
- {
- ++update[tfolder->getParentUUID()];
- --update[folderp->getParentUUID()];
- }
+ if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ if (tfolder->getParentUUID() == folderp->getParentUUID())
+ {
+ update[tfolder->getParentUUID()];
+ }
+ else
+ {
+ ++update[tfolder->getParentUUID()];
+ --update[folderp->getParentUUID()];
+ }
+ }
+ else
+ {
+ folderp->fetch();
+ }
}
else
{
@@ -3460,7 +3819,14 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
folderp = gInventory.getCategory(tfolder->getParentUUID());
if(folderp)
{
- ++update[tfolder->getParentUUID()];
+ if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ ++update[tfolder->getParentUUID()];
+ }
+ else
+ {
+ folderp->fetch();
+ }
}
}
}
@@ -3506,7 +3872,14 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID());
if(folderp)
{
- ++update[titem->getParentUUID()];
+ if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ ++update[titem->getParentUUID()];
+ }
+ else
+ {
+ folderp->fetch();
+ }
}
}
}
@@ -3520,10 +3893,20 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
for (cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit)
{
gInventory.updateCategory(*cit);
+
+ // Temporary workaround: just fetch the item using AIS to get missing fields.
+ // If this works fine we might want to extract ids only from the message
+ // then use AIS as a primary fetcher
+ LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch((*cit)->getUUID(), true /*force, since it has changes*/);
}
for (item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit)
{
gInventory.updateItem(*iit);
+
+ // Temporary workaround: just fetch the item using AIS to get missing fields.
+ // If this works fine we might want to extract ids only from the message
+ // then use AIS as a primary fetcher
+ LLInventoryModelBackgroundFetch::instance().scheduleItemFetch((*iit)->getUUID(), true);
}
gInventory.notifyObservers();
@@ -4348,7 +4731,6 @@ LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const
}
else if (count_under_root > 1)
{
- LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL;
validation_info->mDuplicateRequiredSystemFolders.insert(folder_type);
if (!is_automatic && folder_type != LLFolderType::FT_SETTINGS)
{
@@ -4356,6 +4738,7 @@ LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const
// outfits, trash and other non-automatic folders.
validation_info->mFatalSystemDuplicate++;
fatal_errs++;
+ LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL;
}
else
{
@@ -4364,6 +4747,7 @@ LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const
// Exception: FT_SETTINGS is not automatic, but only deserves a warning.
validation_info->mWarnings["non_fatal_system_duplicate_under_root"]++;
warning_count++;
+ LL_WARNS("Inventory") << "System folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL;
}
}
if (count_elsewhere > 0)
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index 6c5706f871..4309c03f8e 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -226,10 +226,14 @@ private:
//--------------------------------------------------------------------
public:
static BOOL getIsFirstTimeInViewer2();
+ static bool isSysFoldersReady() { return (sPendingSystemFolders == 0); }
+
private:
static BOOL sFirstTimeInViewer2;
const static S32 sCurrentInvCacheVersion; // expected inventory cache version
+ static S32 sPendingSystemFolders;
+
/** Initialization/Setup
** **
*******************************************************************************/
@@ -255,6 +259,7 @@ public:
void getDirectDescendentsOf(const LLUUID& cat_id,
cat_array_t*& categories,
item_array_t*& items) const;
+ void getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const;
typedef LLUUID digest_t; // To clarify the actual usage of this "UUID"
// Compute a hash of direct descendant names (for detecting child name changes)
@@ -302,24 +307,25 @@ public:
// Find
//--------------------------------------------------------------------
public:
+
+ // Checks if category exists ("My Inventory" only), if it does not, creates it
+ void ensureCategoryForTypeExists(LLFolderType::EType preferred_type);
+
const LLUUID findCategoryUUIDForTypeInRoot(
LLFolderType::EType preferred_type,
- bool create_folder,
- const LLUUID& root_id);
+ const LLUUID& root_id) const;
// Returns the uuid of the category that specifies 'type' as what it
// defaults to containing. The category is not necessarily only for that type.
// NOTE: If create_folder is true, this will create a new inventory category
// on the fly if one does not exist. *NOTE: if find_in_library is true it
// will search in the user's library folder instead of "My Inventory"
- const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type,
- bool create_folder = true);
+ const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type) const;
// will search in the user's library folder instead of "My Inventory"
- const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type,
- bool create_folder = true);
+ const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type) const;
// Returns user specified category for uploads, returns default id if there are no
// user specified one or it does not exist, creates default category if it is missing.
- const LLUUID findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type);
+ const LLUUID findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) const;
// Get whatever special folder this object is a child of, if any.
const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const;
@@ -405,13 +411,15 @@ public:
const LLUUID& new_parent_id,
BOOL restamp);
+ // Marks links from a "possibly" broken list for a rebuild
+ // clears the list
+ void rebuildBrockenLinks();
+ bool hasPosiblyBrockenLinks() const { return mPossiblyBrockenLinks.size() > 0; }
+
//--------------------------------------------------------------------
// Delete
//--------------------------------------------------------------------
public:
-
- // Update model after an AISv3 update received for any operation.
- void onAISUpdateReceived(const std::string& context, const LLSD& update);
// Update model after an item is confirmed as removed from
// server. Works for categories or items.
@@ -475,10 +483,11 @@ public:
public:
// Returns the UUID of the new category. If you want to use the default
// name based on type, pass in a NULL to the 'name' parameter.
- LLUUID createNewCategory(const LLUUID& parent_id,
+ void createNewCategory(const LLUUID& parent_id,
LLFolderType::EType preferred_type,
const std::string& name,
- inventory_func_type callback = NULL);
+ inventory_func_type callback = NULL,
+ const LLUUID& thumbnail_id = LLUUID::null);
protected:
// Internal methods that add inventory and make sure that all of
// the internal data structures are consistent. These methods
@@ -575,6 +584,10 @@ private:
U32 mModifyMaskBacklog;
changed_items_t mChangedItemIDsBacklog;
changed_items_t mAddedItemIDsBacklog;
+ typedef std::map<LLUUID , changed_items_t> broken_links_t;
+ broken_links_t mPossiblyBrockenLinks; // there can be multiple links per item
+ changed_items_t mLinksRebuildList;
+ boost::signals2::connection mBulkFecthCallbackSlot;
//--------------------------------------------------------------------
@@ -663,7 +676,6 @@ public:
static void processUpdateCreateInventoryItem(LLMessageSystem* msg, void**);
static void removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg, const char* msg_label);
static void processRemoveInventoryItem(LLMessageSystem* msg, void**);
- static void processUpdateInventoryFolder(LLMessageSystem* msg, void**);
static void removeInventoryFolder(LLUUID agent_id, LLMessageSystem* msg);
static void processRemoveInventoryFolder(LLMessageSystem* msg, void**);
static void processRemoveInventoryObjects(LLMessageSystem* msg, void**);
diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp
index f544b318d6..91adef8047 100644
--- a/indra/newview/llinventorymodelbackgroundfetch.cpp
+++ b/indra/newview/llinventorymodelbackgroundfetch.cpp
@@ -27,6 +27,7 @@
#include "llviewerprecompiledheaders.h"
#include "llinventorymodelbackgroundfetch.h"
+#include "llaisapi.h"
#include "llagent.h"
#include "llappviewer.h"
#include "llcallbacklist.h"
@@ -186,12 +187,14 @@ const char * const LOG_INV("Inventory");
///----------------------------------------------------------------------------
LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch():
- mBackgroundFetchActive(FALSE),
+ mBackgroundFetchActive(false),
mFolderFetchActive(false),
mFetchCount(0),
- mAllFoldersFetched(FALSE),
- mRecursiveInventoryFetchStarted(FALSE),
- mRecursiveLibraryFetchStarted(FALSE),
+ mLastFetchCount(0),
+ mFetchFolderCount(0),
+ mAllRecursiveFoldersFetched(false),
+ mRecursiveInventoryFetchStarted(false),
+ mRecursiveLibraryFetchStarted(false),
mMinTimeBetweenFetches(0.3f)
{}
@@ -200,7 +203,12 @@ LLInventoryModelBackgroundFetch::~LLInventoryModelBackgroundFetch()
bool LLInventoryModelBackgroundFetch::isBulkFetchProcessingComplete() const
{
- return mFetchQueue.empty() && mFetchCount <= 0;
+ return mFetchFolderQueue.empty() && mFetchItemQueue.empty() && mFetchCount <= 0;
+}
+
+bool LLInventoryModelBackgroundFetch::isFolderFetchProcessingComplete() const
+{
+ return mFetchFolderQueue.empty() && mFetchFolderCount <= 0;
}
bool LLInventoryModelBackgroundFetch::libraryFetchStarted() const
@@ -235,7 +243,7 @@ bool LLInventoryModelBackgroundFetch::inventoryFetchInProgress() const
bool LLInventoryModelBackgroundFetch::isEverythingFetched() const
{
- return mAllFoldersFetched;
+ return mAllRecursiveFoldersFetched;
}
BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const
@@ -243,17 +251,33 @@ BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const
return mFolderFetchActive;
}
-void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category)
+void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, bool recursive, bool is_category)
{
- mFetchQueue.push_front(FetchQueueInfo(id, recursive, is_category));
+ EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT;
+ if (is_category)
+ {
+ mFetchFolderQueue.push_front(FetchQueueInfo(id, recursion_type, is_category));
+ }
+ else
+ {
+ mFetchItemQueue.push_front(FetchQueueInfo(id, recursion_type, is_category));
+ }
}
-void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category)
+void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, bool recursive, bool is_category)
{
- mFetchQueue.push_back(FetchQueueInfo(id, recursive, is_category));
+ EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT;
+ if (is_category)
+ {
+ mFetchFolderQueue.push_back(FetchQueueInfo(id, recursion_type, is_category));
+ }
+ else
+ {
+ mFetchItemQueue.push_back(FetchQueueInfo(id, recursion_type, is_category));
+ }
}
-void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive)
+void LLInventoryModelBackgroundFetch::start(const LLUUID& id, bool recursive)
{
LLViewerInventoryCategory * cat(gInventory.getCategory(id));
@@ -262,31 +286,53 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive)
// it's a folder, do a bulk fetch
LL_DEBUGS(LOG_INV) << "Start fetching category: " << id << ", recursive: " << recursive << LL_ENDL;
- mBackgroundFetchActive = TRUE;
+ mBackgroundFetchActive = true;
mFolderFetchActive = true;
+ EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT;
if (id.isNull())
{
if (! mRecursiveInventoryFetchStarted)
{
mRecursiveInventoryFetchStarted |= recursive;
- mFetchQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursive));
+ if (recursive && AISAPI::isAvailable())
+ {
+ // Not only root folder can be massive, but
+ // most system folders will be requested independently
+ // so request root folder and content separately
+ mFetchFolderQueue.push_front(FetchQueueInfo(gInventory.getRootFolderID(), FT_FOLDER_AND_CONTENT));
+ }
+ else
+ {
+ mFetchFolderQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursion_type));
+ }
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
if (! mRecursiveLibraryFetchStarted)
{
mRecursiveLibraryFetchStarted |= recursive;
- mFetchQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursive));
+ mFetchFolderQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursion_type));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
else
{
- // Specific folder requests go to front of queue.
- if (mFetchQueue.empty() || mFetchQueue.front().mUUID != id)
- {
- mFetchQueue.push_front(FetchQueueInfo(id, recursive));
- gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
- }
+ if (AISAPI::isAvailable())
+ {
+ if (mFetchFolderQueue.empty() || mFetchFolderQueue.back().mUUID != id)
+ {
+ // On AIS make sure root goes to the top and follow up recursive
+ // fetches, not individual requests
+ mFetchFolderQueue.push_back(FetchQueueInfo(id, recursion_type));
+ gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
+ }
+ }
+ else if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != id)
+ {
+ // Specific folder requests go to front of queue.
+ mFetchFolderQueue.push_front(FetchQueueInfo(id, recursion_type));
+ gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
+ }
+
if (id == gInventory.getLibraryRootFolderID())
{
mRecursiveLibraryFetchStarted |= recursive;
@@ -299,21 +345,41 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive)
}
else if (LLViewerInventoryItem * itemp = gInventory.getItem(id))
{
- if (! itemp->mIsComplete && (mFetchQueue.empty() || mFetchQueue.front().mUUID != id))
+ if (! itemp->mIsComplete)
{
- mBackgroundFetchActive = TRUE;
-
- mFetchQueue.push_front(FetchQueueInfo(id, false, false));
- gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
+ scheduleItemFetch(id);
}
}
}
+void LLInventoryModelBackgroundFetch::scheduleFolderFetch(const LLUUID& cat_id, bool forced)
+{
+ if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != cat_id)
+ {
+ mBackgroundFetchActive = true;
+
+ // Specific folder requests go to front of queue.
+ mFetchFolderQueue.push_front(FetchQueueInfo(cat_id, forced ? FT_FORCED : FT_DEFAULT));
+ gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
+ }
+}
+
+void LLInventoryModelBackgroundFetch::scheduleItemFetch(const LLUUID& item_id, bool forced)
+{
+ if (mFetchItemQueue.empty() || mFetchItemQueue.front().mUUID != item_id)
+ {
+ mBackgroundFetchActive = true;
+
+ mFetchItemQueue.push_front(FetchQueueInfo(item_id, forced ? FT_FORCED : FT_DEFAULT, false));
+ gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
+ }
+}
+
void LLInventoryModelBackgroundFetch::findLostItems()
{
- mBackgroundFetchActive = TRUE;
- mFolderFetchActive = true;
- mFetchQueue.push_back(FetchQueueInfo(LLUUID::null, TRUE));
+ mBackgroundFetchActive = true;
+ mFolderFetchActive = true;
+ mFetchFolderQueue.push_back(FetchQueueInfo(LLUUID::null, FT_RECURSIVE));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
@@ -322,15 +388,28 @@ void LLInventoryModelBackgroundFetch::setAllFoldersFetched()
if (mRecursiveInventoryFetchStarted &&
mRecursiveLibraryFetchStarted)
{
- mAllFoldersFetched = TRUE;
+ mAllRecursiveFoldersFetched = true;
//LL_INFOS(LOG_INV) << "All folders fetched, validating" << LL_ENDL;
//gInventory.validate();
}
+
mFolderFetchActive = false;
- mBackgroundFetchActive = false;
+ if (isBulkFetchProcessingComplete())
+ {
+ mBackgroundFetchActive = false;
+ }
+
+ // For now only informs about initial fetch being done
+ mFoldersFetchedSignal();
+
LL_INFOS(LOG_INV) << "Inventory background fetch completed" << LL_ENDL;
}
+boost::signals2::connection LLInventoryModelBackgroundFetch::setFetchCompletionCallback(folders_fetched_callback_t cb)
+{
+ return mFoldersFetchedSignal.connect(cb);
+}
+
void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *)
{
LLInventoryModelBackgroundFetch::instance().backgroundFetch();
@@ -338,10 +417,17 @@ void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *)
void LLInventoryModelBackgroundFetch::backgroundFetch()
{
- if (mBackgroundFetchActive && gAgent.getRegion() && gAgent.getRegion()->capabilitiesReceived())
+ if (mBackgroundFetchActive)
{
- // If we'll be using the capability, we'll be sending batches and the background thing isn't as important.
- bulkFetch();
+ if (AISAPI::isAvailable())
+ {
+ bulkFetchViaAis();
+ }
+ else if (gAgent.getRegion() && gAgent.getRegion()->capabilitiesReceived())
+ {
+ // If we'll be using the capability, we'll be sending batches and the background thing isn't as important.
+ bulkFetch();
+ }
}
}
@@ -354,9 +440,476 @@ void LLInventoryModelBackgroundFetch::incrFetchCount(S32 fetching)
mFetchCount = 0;
}
}
+void LLInventoryModelBackgroundFetch::incrFetchFolderCount(S32 fetching)
+{
+ incrFetchCount(fetching);
+ mFetchFolderCount += fetching;
+ if (mFetchCount < 0)
+ {
+ LL_WARNS_ONCE(LOG_INV) << "Inventory fetch count fell below zero (0)." << LL_ENDL;
+ mFetchFolderCount = 0;
+ }
+}
+
+void ais_simple_item_callback(const LLUUID& inv_id)
+{
+ LL_DEBUGS(LOG_INV , "AIS3") << "Response for " << inv_id << LL_ENDL;
+ LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1);
+}
+
+void LLInventoryModelBackgroundFetch::onAISContentCalback(
+ const LLUUID& request_id,
+ const uuid_vec_t& content_ids,
+ const LLUUID& response_id,
+ EFetchType fetch_type)
+{
+ // Don't push_front on failure - there is a chance it was fired from inside bulkFetchViaAis
+ incrFetchFolderCount(-1);
+
+ uuid_vec_t::const_iterator folder_iter = content_ids.begin();
+ uuid_vec_t::const_iterator folder_end = content_ids.end();
+ while (folder_iter != folder_end)
+ {
+ std::list<LLUUID>::const_iterator found = std::find(mExpectedFolderIds.begin(), mExpectedFolderIds.end(), *folder_iter);
+ if (found != mExpectedFolderIds.end())
+ {
+ mExpectedFolderIds.erase(found);
+ }
+
+ LLViewerInventoryCategory* cat(gInventory.getCategory(*folder_iter));
+ if (cat)
+ {
+ cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
+ }
+ if (response_id.isNull())
+ {
+ // Failed to fetch, get it individually
+ mFetchFolderQueue.push_back(FetchQueueInfo(*folder_iter, FT_RECURSIVE));
+ }
+ else
+ {
+ // push descendant back to verify they are fetched fully (ex: didn't encounter depth limit)
+ LLInventoryModel::cat_array_t* categories(NULL);
+ LLInventoryModel::item_array_t* items(NULL);
+ gInventory.getDirectDescendentsOf(*folder_iter, categories, items);
+ if (categories)
+ {
+ for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
+ it != categories->end();
+ ++it)
+ {
+ mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE));
+ }
+ }
+ }
+
+ folder_iter++;
+ }
+
+ if (!mFetchFolderQueue.empty())
+ {
+ mBackgroundFetchActive = true;
+ mFolderFetchActive = true;
+ gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
+ }
+}
+void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType fetch_type)
+{
+ // Don't push_front on failure - there is a chance it was fired from inside bulkFetchViaAis
+ incrFetchFolderCount(-1);
+ std::list<LLUUID>::const_iterator found = std::find(mExpectedFolderIds.begin(), mExpectedFolderIds.end(), request_id);
+ if (found != mExpectedFolderIds.end())
+ {
+ mExpectedFolderIds.erase(found);
+ }
+ else
+ {
+ // ais shouldn't respond twice
+ llassert(false);
+ LL_WARNS() << "Unexpected folder response for " << request_id << LL_ENDL;
+ }
+
+ if (request_id.isNull())
+ {
+ // orhans, no other actions needed
+ return;
+ }
+
+ bool request_descendants = false;
+ if (response_id.isNull()) // Failure
+ {
+ LL_DEBUGS(LOG_INV , "AIS3") << "Failure response for folder " << request_id << LL_ENDL;
+ if (fetch_type == FT_RECURSIVE)
+ {
+ // A full recursive request failed.
+ // Try requesting folder and nested content separately
+ mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_FOLDER_AND_CONTENT));
+ }
+ else if (fetch_type == FT_FOLDER_AND_CONTENT)
+ {
+ LL_WARNS() << "Failed to download folder: " << request_id << " Requesting known content separately" << LL_ENDL;
+ mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE));
+
+ // set folder's version to prevent viewer from trying to request folder indefinetely
+ LLViewerInventoryCategory* cat(gInventory.getCategory(request_id));
+ if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ cat->setVersion(0);
+ }
+ }
+ }
+ else
+ {
+ if (fetch_type == FT_RECURSIVE)
+ {
+ // Got the folder and content, now verify content
+ // Request content even for FT_RECURSIVE in case of changes, failures
+ // or if depth limit gets imlemented.
+ // This shouldn't redownload folders if they already have version
+ request_descendants = true;
+ LL_DEBUGS(LOG_INV, "AIS3") << "Got folder " << request_id << ". Requesting content" << LL_ENDL;
+ }
+ else if (fetch_type == FT_FOLDER_AND_CONTENT)
+ {
+ // readd folder for content request
+ mFetchFolderQueue.push_front(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE));
+ }
+ else
+ {
+ LL_DEBUGS(LOG_INV, "AIS3") << "Got folder " << request_id << "." << LL_ENDL;
+ }
+
+ }
+
+ if (request_descendants)
+ {
+ LLInventoryModel::cat_array_t* categories(NULL);
+ LLInventoryModel::item_array_t* items(NULL);
+ gInventory.getDirectDescendentsOf(request_id, categories, items);
+ if (categories)
+ {
+ for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
+ it != categories->end();
+ ++it)
+ {
+ mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE));
+ }
+ }
+ }
+
+ if (!mFetchFolderQueue.empty())
+ {
+ mBackgroundFetchActive = true;
+ mFolderFetchActive = true;
+ gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
+ }
+
+ // done
+ LLViewerInventoryCategory * cat(gInventory.getCategory(request_id));
+ if (cat)
+ {
+ cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
+ }
+}
static LLTrace::BlockTimerStatHandle FTM_BULK_FETCH("Bulk Fetch");
+void LLInventoryModelBackgroundFetch::bulkFetchViaAis()
+{
+ LL_RECORD_BLOCK_TIME(FTM_BULK_FETCH);
+ //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped.
+ if (gDisconnected)
+ {
+ return;
+ }
+
+ static LLCachedControl<U32> ais_pool(gSavedSettings, "PoolSizeAIS", 20);
+ // Don't have too many requests at once, AIS throttles
+ // Reserve one request for actions outside of fetch (like renames)
+ const U32 max_concurrent_fetches = llclamp(ais_pool - 1, 1, 50);
+
+ if (mFetchCount >= max_concurrent_fetches)
+ {
+ return;
+ }
+
+ // Don't loop for too long (in case of large, fully loaded inventory)
+ F64 curent_time = LLTimer::getTotalSeconds();
+ const F64 max_time = LLStartUp::getStartupState() > STATE_WEARABLES_WAIT
+ ? 0.006f // 6 ms
+ : 1.f;
+ const F64 end_time = curent_time + max_time;
+ S32 last_fetch_count = mFetchCount;
+
+ while (!mFetchFolderQueue.empty() && mFetchCount < max_concurrent_fetches && curent_time < end_time)
+ {
+ const FetchQueueInfo & fetch_info(mFetchFolderQueue.front());
+ bulkFetchViaAis(fetch_info);
+ mFetchFolderQueue.pop_front();
+ curent_time = LLTimer::getTotalSeconds();
+ }
+
+ // Ideally we shouldn't fetch items if recursive fetch isn't done,
+ // but there is a chance some request will start timeouting and recursive
+ // fetch will get stuck on a signle folder, don't block item fetch in such case
+ while (!mFetchItemQueue.empty() && mFetchCount < max_concurrent_fetches && curent_time < end_time)
+ {
+ const FetchQueueInfo& fetch_info(mFetchItemQueue.front());
+ bulkFetchViaAis(fetch_info);
+ mFetchItemQueue.pop_front();
+ curent_time = LLTimer::getTotalSeconds();
+ }
+
+ if (last_fetch_count != mFetchCount // if anything was added
+ || mLastFetchCount != mFetchCount) // if anything was substracted
+ {
+ LL_DEBUGS(LOG_INV , "AIS3") << "Total active fetches: " << mLastFetchCount << "->" << last_fetch_count << "->" << mFetchCount
+ << ", scheduled folder fetches: " << (S32)mFetchFolderQueue.size()
+ << ", scheduled item fetches: " << (S32)mFetchItemQueue.size()
+ << LL_ENDL;
+ mLastFetchCount = mFetchCount;
+
+ if (!mExpectedFolderIds.empty())
+ {
+ // A folder seem to be stack fetching on QA account, print oldest folder out
+ LL_DEBUGS(LOG_INV , "AIS3") << "Oldest expected folder: ";
+ std::list<LLUUID>::const_iterator iter = mExpectedFolderIds.begin();
+ LL_CONT << *iter;
+ if ((*iter).notNull())
+ {
+ LLViewerInventoryCategory* cat(gInventory.getCategory(*iter));
+ if (cat)
+ {
+ LL_CONT << " Folder name: " << cat->getName() << " Parent: " << cat->getParentUUID();
+ }
+ else
+ {
+ LL_CONT << " This folder doesn't exist";
+ }
+ }
+ else
+ {
+ LL_CONT << " Orphans request";
+ }
+ LL_CONT << LL_ENDL;
+ }
+ }
+
+ if (isFolderFetchProcessingComplete() && mFolderFetchActive)
+ {
+ setAllFoldersFetched();
+ }
+
+ if (isBulkFetchProcessingComplete())
+ {
+ mBackgroundFetchActive = false;
+ }
+}
+
+void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetch_info)
+{
+ if (fetch_info.mIsCategory)
+ {
+ const LLUUID & cat_id(fetch_info.mUUID);
+ if (cat_id.isNull())
+ {
+ incrFetchFolderCount(1);
+ mExpectedFolderIds.push_back(cat_id);
+ // Lost and found
+ // Should it actually be recursive?
+ AISAPI::FetchOrphans([](const LLUUID& response_id)
+ {
+ LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(LLUUID::null,
+ response_id,
+ FT_DEFAULT);
+ });
+ }
+ else
+ {
+ LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id));
+ if (cat)
+ {
+ if (fetch_info.mFetchType == FT_CONTENT_RECURSIVE)
+ {
+ // fetch content only, ignore cat itself
+ uuid_vec_t children;
+ LLInventoryModel::cat_array_t* categories(NULL);
+ LLInventoryModel::item_array_t* items(NULL);
+ gInventory.getDirectDescendentsOf(cat_id, categories, items);
+
+ LLViewerInventoryCategory::EFetchType target_state = LLViewerInventoryCategory::FETCH_RECURSIVE;
+ bool content_done = true;
+
+ // Top limit is 'as many as you can put into url'
+ static LLCachedControl<S32> ais_batch(gSavedSettings, "BatchSizeAIS3", 20);
+ S32 batch_limit = llclamp(ais_batch(), 1, 40);
+
+ for (LLInventoryModel::cat_array_t::iterator it = categories->begin();
+ it != categories->end();
+ ++it)
+ {
+ LLViewerInventoryCategory* child_cat = (*it);
+ if (LLViewerInventoryCategory::VERSION_UNKNOWN != child_cat->getVersion()
+ || child_cat->getFetching() >= target_state)
+ {
+ continue;
+ }
+
+ if (child_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_LISTINGS)
+ {
+ // special case
+ content_done = false;
+ if (children.empty())
+ {
+ // fetch marketplace alone
+ // Should it actually be fetched as FT_FOLDER_AND_CONTENT?
+ children.push_back(child_cat->getUUID());
+ mExpectedFolderIds.push_back(child_cat->getUUID());
+ child_cat->setFetching(target_state);
+ break;
+ }
+ else
+ {
+ // fetch marketplace alone next run
+ continue;
+ }
+ }
+
+ children.push_back(child_cat->getUUID());
+ mExpectedFolderIds.push_back(child_cat->getUUID());
+ child_cat->setFetching(target_state);
+
+ if (children.size() >= batch_limit)
+ {
+ content_done = false;
+ break;
+ }
+ }
+
+ if (!children.empty())
+ {
+ // increment before call in case of immediate callback
+ incrFetchFolderCount(1);
+
+ EFetchType type = fetch_info.mFetchType;
+ LLUUID cat_id = cat->getUUID(); // need a copy for lambda
+ AISAPI::completion_t cb = [cat_id, children, type](const LLUUID& response_id)
+ {
+ LLInventoryModelBackgroundFetch::instance().onAISContentCalback(cat_id, children, response_id, type);
+ };
+
+ AISAPI::ITEM_TYPE item_type = AISAPI::INVENTORY;
+ if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
+ {
+ item_type = AISAPI::LIBRARY;
+ }
+
+ AISAPI::FetchCategorySubset(cat_id, children, item_type, true, cb, 0);
+ }
+
+ if (content_done)
+ {
+ // This will have a bit of overlap with onAISContentCalback,
+ // but something else might have dowloaded folders, so verify
+ // every child that is complete has it's children done as well
+ for (LLInventoryModel::cat_array_t::iterator it = categories->begin();
+ it != categories->end();
+ ++it)
+ {
+ LLViewerInventoryCategory* child_cat = (*it);
+ if (LLViewerInventoryCategory::VERSION_UNKNOWN != child_cat->getVersion())
+ {
+ mFetchFolderQueue.push_back(FetchQueueInfo(child_cat->getUUID(), FT_RECURSIVE));
+ }
+ }
+ }
+ else
+ {
+ // send it back to get the rest
+ mFetchFolderQueue.push_back(FetchQueueInfo(cat_id, FT_CONTENT_RECURSIVE));
+ }
+ }
+ else if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()
+ || fetch_info.mFetchType == FT_FORCED)
+ {
+ LLViewerInventoryCategory::EFetchType target_state =
+ fetch_info.mFetchType > FT_CONTENT_RECURSIVE
+ ? LLViewerInventoryCategory::FETCH_RECURSIVE
+ : LLViewerInventoryCategory::FETCH_NORMAL;
+ // start again if we did a non-recursive fetch before
+ // to get all children in a single request
+ if (cat->getFetching() < target_state)
+ {
+ // increment before call in case of immediate callback
+ incrFetchFolderCount(1);
+ cat->setFetching(target_state);
+ mExpectedFolderIds.push_back(cat_id);
+
+ EFetchType type = fetch_info.mFetchType;
+ LLUUID cat_id = cat->getUUID();
+ AISAPI::completion_t cb = [cat_id , type](const LLUUID& response_id)
+ {
+ LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(cat_id , response_id , type);
+ };
+
+ AISAPI::ITEM_TYPE item_type = AISAPI::INVENTORY;
+ if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
+ {
+ item_type = AISAPI::LIBRARY;
+ }
+
+ AISAPI::FetchCategoryChildren(cat_id , item_type , type == FT_RECURSIVE , cb, 0);
+ }
+ }
+ else
+ {
+ // Already fetched, check if anything inside needs fetching
+ if (fetch_info.mFetchType == FT_RECURSIVE
+ || fetch_info.mFetchType == FT_FOLDER_AND_CONTENT)
+ {
+ LLInventoryModel::cat_array_t * categories(NULL);
+ LLInventoryModel::item_array_t * items(NULL);
+ gInventory.getDirectDescendentsOf(cat_id, categories, items);
+ for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
+ it != categories->end();
+ ++it)
+ {
+ // not push_front to not cause an infinite loop
+ mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE));
+ }
+ }
+ }
+ } // else try to fetch folder either way?
+ }
+ }
+ else
+ {
+ LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID));
+
+ if (itemp)
+ {
+ if (!itemp->isFinished() || fetch_info.mFetchType == FT_FORCED)
+ {
+ mFetchCount++;
+ if (itemp->getPermissions().getOwner() == gAgent.getID())
+ {
+ AISAPI::FetchItem(fetch_info.mUUID, AISAPI::INVENTORY, ais_simple_item_callback);
+ }
+ else
+ {
+ AISAPI::FetchItem(fetch_info.mUUID, AISAPI::LIBRARY, ais_simple_item_callback);
+ }
+ }
+ }
+ else // We don't know it, assume incomplete
+ {
+ // Assume agent's inventory, library wouldn't have gotten here
+ mFetchCount++;
+ AISAPI::FetchItem(fetch_info.mUUID, AISAPI::INVENTORY, ais_simple_item_callback);
+ }
+ }
+}
+
// Bundle up a bunch of requests to send all at once.
void LLInventoryModelBackgroundFetch::bulkFetch()
{
@@ -376,13 +929,6 @@ void LLInventoryModelBackgroundFetch::bulkFetch()
// inventory more quickly.
static const U32 max_batch_size(10);
static const S32 max_concurrent_fetches(12); // Outstanding requests, not connections
- static const F32 new_min_time(0.05f); // *HACK: Clean this up when old code goes away entirely.
-
- mMinTimeBetweenFetches = new_min_time;
- if (mMinTimeBetweenFetches < new_min_time)
- {
- mMinTimeBetweenFetches = new_min_time; // *HACK: See above.
- }
if (mFetchCount)
{
@@ -396,8 +942,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch()
gInventory.notifyObservers();
}
- if ((mFetchCount > max_concurrent_fetches) ||
- (mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches))
+ if (mFetchCount > max_concurrent_fetches)
{
return;
}
@@ -417,95 +962,105 @@ void LLInventoryModelBackgroundFetch::bulkFetch()
LLSD item_request_body;
LLSD item_request_body_lib;
- while (! mFetchQueue.empty()
+ while (! mFetchFolderQueue.empty()
&& (item_count + folder_count) < max_batch_size)
{
- const FetchQueueInfo & fetch_info(mFetchQueue.front());
+ const FetchQueueInfo & fetch_info(mFetchFolderQueue.front());
if (fetch_info.mIsCategory)
{
const LLUUID & cat_id(fetch_info.mUUID);
- if (cat_id.isNull()) //DEV-17797
+ if (cat_id.isNull()) //DEV-17797 Lost and found
{
LLSD folder_sd;
folder_sd["folder_id"] = LLUUID::null.asString();
folder_sd["owner_id"] = gAgent.getID();
folder_sd["sort_order"] = LLSD::Integer(sort_order);
- folder_sd["fetch_folders"] = LLSD::Boolean(FALSE);
- folder_sd["fetch_items"] = LLSD::Boolean(TRUE);
+ folder_sd["fetch_folders"] = LLSD::Boolean(false);
+ folder_sd["fetch_items"] = LLSD::Boolean(true);
folder_request_body["folders"].append(folder_sd);
folder_count++;
}
else
{
- const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id));
-
- if (cat)
- {
- if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()
- && std::find(all_cats.begin(), all_cats.end(), cat_id) == all_cats.end())
- {
- LLSD folder_sd;
- folder_sd["folder_id"] = cat->getUUID();
- folder_sd["owner_id"] = cat->getOwnerID();
- folder_sd["sort_order"] = LLSD::Integer(sort_order);
- folder_sd["fetch_folders"] = LLSD::Boolean(TRUE); //(LLSD::Boolean)sFullFetchStarted;
- folder_sd["fetch_items"] = LLSD::Boolean(TRUE);
-
- if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
- {
- folder_request_body_lib["folders"].append(folder_sd);
- }
- else
- {
- folder_request_body["folders"].append(folder_sd);
- }
- folder_count++;
- }
-
- // May already have this folder, but append child folders to list.
- if (fetch_info.mRecursive)
- {
- LLInventoryModel::cat_array_t * categories(NULL);
- LLInventoryModel::item_array_t * items(NULL);
- gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items);
- for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
- it != categories->end();
- ++it)
- {
- mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mRecursive));
- }
- }
- }
+ const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id));
+ if (cat)
+ {
+ if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
+ {
+ if (std::find(all_cats.begin(), all_cats.end(), cat_id) == all_cats.end())
+ {
+ LLSD folder_sd;
+ folder_sd["folder_id"] = cat->getUUID();
+ folder_sd["owner_id"] = cat->getOwnerID();
+ folder_sd["sort_order"] = LLSD::Integer(sort_order);
+ folder_sd["fetch_folders"] = LLSD::Boolean(TRUE); //(LLSD::Boolean)sFullFetchStarted;
+ folder_sd["fetch_items"] = LLSD::Boolean(TRUE);
+
+ if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
+ {
+ folder_request_body_lib["folders"].append(folder_sd);
+ }
+ else
+ {
+ folder_request_body["folders"].append(folder_sd);
+ }
+ folder_count++;
+ }
+ }
+ else
+ {
+ // May already have this folder, but append child folders to list.
+ if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE)
+ {
+ LLInventoryModel::cat_array_t * categories(NULL);
+ LLInventoryModel::item_array_t * items(NULL);
+ gInventory.getDirectDescendentsOf(cat_id, categories, items);
+ for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
+ it != categories->end();
+ ++it)
+ {
+ mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mFetchType));
+ }
+ }
+ }
+ }
}
- if (fetch_info.mRecursive)
+ if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE)
{
recursive_cats.push_back(cat_id);
}
all_cats.push_back(cat_id);
}
- else
- {
- LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID));
- if (itemp)
- {
- LLSD item_sd;
- item_sd["owner_id"] = itemp->getPermissions().getOwner();
- item_sd["item_id"] = itemp->getUUID();
- if (itemp->getPermissions().getOwner() == gAgent.getID())
- {
- item_request_body.append(item_sd);
- }
- else
- {
- item_request_body_lib.append(item_sd);
- }
- //itemp->fetchFromServer();
- item_count++;
- }
- }
+ mFetchFolderQueue.pop_front();
+ }
+
+
+ while (!mFetchItemQueue.empty()
+ && (item_count + folder_count) < max_batch_size)
+ {
+ const FetchQueueInfo & fetch_info(mFetchItemQueue.front());
+
+ LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID));
+
+ if (itemp)
+ {
+ LLSD item_sd;
+ item_sd["owner_id"] = itemp->getPermissions().getOwner();
+ item_sd["item_id"] = itemp->getUUID();
+ if (itemp->getPermissions().getOwner() == gAgent.getID())
+ {
+ item_request_body.append(item_sd);
+ }
+ else
+ {
+ item_request_body_lib.append(item_sd);
+ }
+ //itemp->fetchFromServer();
+ item_count++;
+ }
- mFetchQueue.pop_front();
+ mFetchItemQueue.pop_front();
}
// Issue HTTP POST requests to fetch folders and items
@@ -576,14 +1131,22 @@ void LLInventoryModelBackgroundFetch::bulkFetch()
bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LLUUID & cat_id) const
{
- for (fetch_queue_t::const_iterator it = mFetchQueue.begin();
- it != mFetchQueue.end();
+ for (fetch_queue_t::const_iterator it = mFetchFolderQueue.begin();
+ it != mFetchFolderQueue.end();
++it)
{
const LLUUID & fetch_id = (*it).mUUID;
if (gInventory.isObjectDescendentOf(fetch_id, cat_id))
return false;
}
+ for (fetch_queue_t::const_iterator it = mFetchItemQueue.begin();
+ it != mFetchItemQueue.end();
+ ++it)
+ {
+ const LLUUID & fetch_id = (*it).mUUID;
+ if (gInventory.isObjectDescendentOf(fetch_id, cat_id))
+ return false;
+ }
return true;
}
@@ -829,10 +1392,10 @@ void BGFolderHttpHandler::processFailure(LLCore::HttpStatus status, LLCore::Http
while (iter != end)
{
folders.append(*iter);
- LLUUID fodler_id = iter->get("folder_id").asUUID();
- if (std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(), fodler_id) != mRecursiveCatUUIDs.end())
+ LLUUID folder_id = iter->get("folder_id").asUUID();
+ if (std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(), folder_id) != mRecursiveCatUUIDs.end())
{
- recursive_cats.push_back(fodler_id);
+ recursive_cats.push_back(folder_id);
}
if (folders.size() == (S32)(size / 2))
{
diff --git a/indra/newview/llinventorymodelbackgroundfetch.h b/indra/newview/llinventorymodelbackgroundfetch.h
index 00d2908c1b..e7be265a3d 100644
--- a/indra/newview/llinventorymodelbackgroundfetch.h
+++ b/indra/newview/llinventorymodelbackgroundfetch.h
@@ -47,9 +47,11 @@ class LLInventoryModelBackgroundFetch : public LLSingleton<LLInventoryModelBackg
~LLInventoryModelBackgroundFetch();
public:
- // Start and stop background breadth-first fetching of inventory contents.
+ // Start background breadth-first fetching of inventory contents.
// This gets triggered when performing a filter-search.
- void start(const LLUUID& cat_id = LLUUID::null, BOOL recursive = TRUE);
+ void start(const LLUUID& cat_id = LLUUID::null, bool recursive = true);
+ void scheduleFolderFetch(const LLUUID& cat_id, bool forced = false);
+ void scheduleItemFetch(const LLUUID& item_id, bool forced = false);
BOOL folderFetchActive() const;
bool isEverythingFetched() const; // completing the fetch once per session should be sufficient
@@ -62,16 +64,47 @@ public:
bool inventoryFetchCompleted() const;
bool inventoryFetchInProgress() const;
- void findLostItems();
- void incrFetchCount(S32 fetching);
+ void findLostItems();
+ void incrFetchCount(S32 fetching);
+ void incrFetchFolderCount(S32 fetching);
bool isBulkFetchProcessingComplete() const;
+ bool isFolderFetchProcessingComplete() const;
void setAllFoldersFetched();
- void addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category);
- void addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category);
+ typedef boost::function<void()> folders_fetched_callback_t;
+ boost::signals2::connection setFetchCompletionCallback(folders_fetched_callback_t cb);
+
+ void addRequestAtFront(const LLUUID & id, bool recursive, bool is_category);
+ void addRequestAtBack(const LLUUID & id, bool recursive, bool is_category);
protected:
+
+ typedef enum {
+ FT_DEFAULT = 0,
+ FT_FORCED, // request non-recursively even if already loaded
+ FT_CONTENT_RECURSIVE, // request content recursively
+ FT_FOLDER_AND_CONTENT, // request folder, then content recursively
+ FT_RECURSIVE, // request everything recursively
+ } EFetchType;
+ struct FetchQueueInfo
+ {
+ FetchQueueInfo(const LLUUID& id, EFetchType recursive, bool is_category = true)
+ : mUUID(id),
+ mIsCategory(is_category),
+ mFetchType(recursive)
+ {}
+
+ LLUUID mUUID;
+ bool mIsCategory;
+ EFetchType mFetchType;
+ };
+ typedef std::deque<FetchQueueInfo> fetch_queue_t;
+
+ void onAISContentCalback(const LLUUID& request_id, const uuid_vec_t &content_ids, const LLUUID& response_id, EFetchType fetch_type);
+ void onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType fetch_type);
+ void bulkFetchViaAis();
+ void bulkFetchViaAis(const FetchQueueInfo& fetch_info);
void bulkFetch();
void backgroundFetch();
@@ -80,31 +113,23 @@ protected:
bool fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const;
private:
- BOOL mRecursiveInventoryFetchStarted;
- BOOL mRecursiveLibraryFetchStarted;
- BOOL mAllFoldersFetched;
+ bool mRecursiveInventoryFetchStarted;
+ bool mRecursiveLibraryFetchStarted;
+ bool mAllRecursiveFoldersFetched;
+ typedef boost::signals2::signal<void()> folders_fetched_signal_t;
+ folders_fetched_signal_t mFoldersFetchedSignal;
- BOOL mBackgroundFetchActive;
+ bool mBackgroundFetchActive;
bool mFolderFetchActive;
S32 mFetchCount;
+ S32 mLastFetchCount; // for debug
+ S32 mFetchFolderCount;
LLFrameTimer mFetchTimer;
F32 mMinTimeBetweenFetches;
-
- struct FetchQueueInfo
- {
- FetchQueueInfo(const LLUUID& id, BOOL recursive, bool is_category = true)
- : mUUID(id),
- mIsCategory(is_category),
- mRecursive(recursive)
- {}
-
- LLUUID mUUID;
- bool mIsCategory;
- BOOL mRecursive;
- };
- typedef std::deque<FetchQueueInfo> fetch_queue_t;
- fetch_queue_t mFetchQueue;
+ fetch_queue_t mFetchFolderQueue;
+ fetch_queue_t mFetchItemQueue;
+ std::list<LLUUID> mExpectedFolderIds; // for debug, should this track time?
};
#endif // LL_LLINVENTORYMODELBACKGROUNDFETCH_H
diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp
index db0751cb89..281a8bc789 100644
--- a/indra/newview/llinventoryobserver.cpp
+++ b/indra/newview/llinventoryobserver.cpp
@@ -37,8 +37,10 @@
#include "llagent.h"
#include "llagentwearables.h"
+#include "llaisapi.h"
#include "llfloater.h"
#include "llfocusmgr.h"
+#include "llinventorymodelbackgroundfetch.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
@@ -56,6 +58,7 @@
#include "llsdutil.h"
#include <deque>
+const S32 LLInventoryFetchItemsObserver::MAX_INDIVIDUAL_ITEM_REQUESTS = 7;
const F32 LLInventoryFetchItemsObserver::FETCH_TIMER_EXPIRY = 60.0f;
@@ -149,7 +152,7 @@ LLInventoryFetchItemsObserver::LLInventoryFetchItemsObserver(const uuid_vec_t& i
void LLInventoryFetchItemsObserver::changed(U32 mask)
{
- LL_DEBUGS() << this << " remaining incomplete " << mIncomplete.size()
+ LL_DEBUGS("InventoryFetch") << this << " remaining incomplete " << mIncomplete.size()
<< " complete " << mComplete.size()
<< " wait period " << mFetchingPeriod.getRemainingTimeF32()
<< LL_ENDL;
@@ -158,6 +161,15 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)
// appropriate.
if (!mIncomplete.empty())
{
+ if (!LLInventoryModelBackgroundFetch::getInstance()->isEverythingFetched())
+ {
+ // Folders have a priority over items and they download items as well
+ // Wait untill initial folder fetch is done
+ LL_DEBUGS("InventoryFetch") << "Folder fetch in progress, resetting fetch timer" << LL_ENDL;
+
+ mFetchingPeriod.reset();
+ mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY);
+ }
// Have we exceeded max wait time?
bool timeout_expired = mFetchingPeriod.hasExpired();
@@ -176,7 +188,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)
if (timeout_expired)
{
// Just concede that this item hasn't arrived in reasonable time and continue on.
- LL_WARNS() << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL;
+ LL_WARNS("InventoryFetch") << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL;
it = mIncomplete.erase(it);
}
else
@@ -191,7 +203,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)
if (mIncomplete.empty())
{
- LL_DEBUGS() << this << " done at remaining incomplete "
+ LL_DEBUGS("InventoryFetch") << this << " done at remaining incomplete "
<< mIncomplete.size() << " complete " << mComplete.size() << LL_ENDL;
done();
}
@@ -251,29 +263,21 @@ void fetch_items_from_llsd(const LLSD& items_llsd)
void LLInventoryFetchItemsObserver::startFetch()
{
- LLUUID owner_id;
+ bool aisv3 = AISAPI::isAvailable();
+
LLSD items_llsd;
+
+ typedef std::map<LLUUID, uuid_vec_t> requests_by_folders_t;
+ requests_by_folders_t requests;
for (uuid_vec_t::const_iterator it = mIDs.begin(); it < mIDs.end(); ++it)
{
- LLViewerInventoryItem* item = gInventory.getItem(*it);
- if (item)
- {
- if (item->isFinished())
- {
- // It's complete, so put it on the complete container.
- mComplete.push_back(*it);
- continue;
- }
- else
- {
- owner_id = item->getPermissions().getOwner();
- }
- }
- else
- {
- // assume it's agent inventory.
- owner_id = gAgent.getID();
- }
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if (item && item->isFinished())
+ {
+ // It's complete, so put it on the complete container.
+ mComplete.push_back(*it);
+ continue;
+ }
// Ignore categories since they're not items. We
// could also just add this to mComplete but not sure what the
@@ -294,17 +298,98 @@ void LLInventoryFetchItemsObserver::startFetch()
// pack this on the message.
mIncomplete.push_back(*it);
- // Prepare the data to fetch
- LLSD item_entry;
- item_entry["owner_id"] = owner_id;
- item_entry["item_id"] = (*it);
- items_llsd.append(item_entry);
+ if (aisv3)
+ {
+ if (item)
+ {
+ LLUUID parent_id = item->getParentUUID();
+ requests[parent_id].push_back(*it);
+ }
+ else
+ {
+ // Can happen for gestures and calling cards if server notified us before they fetched
+ // Request by id without checking for an item.
+ LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(*it);
+ }
+ }
+ else
+ {
+ // Prepare the data to fetch
+ LLSD item_entry;
+ if (item)
+ {
+ item_entry["owner_id"] = item->getPermissions().getOwner();
+ }
+ else
+ {
+ // assume it's agent inventory.
+ item_entry["owner_id"] = gAgent.getID();
+ }
+ item_entry["item_id"] = (*it);
+ items_llsd.append(item_entry);
+ }
}
mFetchingPeriod.reset();
mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY);
- fetch_items_from_llsd(items_llsd);
+ if (aisv3)
+ {
+ for (requests_by_folders_t::value_type &folder : requests)
+ {
+ if (folder.second.size() > MAX_INDIVIDUAL_ITEM_REQUESTS)
+ {
+ // requesting one by one will take a while
+ // do whole folder
+ LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true);
+ }
+ else
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(folder.first);
+ if (cat)
+ {
+ if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ // start fetching whole folder since it's not ready either way
+ cat->fetch();
+ }
+ else if (cat->getViewerDescendentCount() <= folder.second.size()
+ || cat->getDescendentCount() <= folder.second.size())
+ {
+ // Start fetching whole folder since we need all items
+ LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true);
+
+ }
+ else
+ {
+ // get items one by one
+ for (LLUUID &item_id : folder.second)
+ {
+ LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(item_id);
+ }
+ }
+ }
+ else
+ {
+ // Isn't supposed to happen? We should have all folders
+ // and if item exists, folder is supposed to exist as well.
+ llassert(false);
+ LL_WARNS("Inventory") << "Missing folder: " << folder.first << " fetching items individually" << LL_ENDL;
+
+ // get items one by one
+ for (LLUUID &item_id : folder.second)
+ {
+ LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(item_id);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ fetch_items_from_llsd(items_llsd);
+ }
+
}
LLInventoryFetchDescendentsObserver::LLInventoryFetchDescendentsObserver(const LLUUID& cat_id) :
@@ -649,6 +734,13 @@ void LLInventoryCategoriesObserver::changed(U32 mask)
}
}
+ const LLUUID thumbnail_id = category->getThumbnailUUID();
+ if (cat_data.mThumbnailId != thumbnail_id)
+ {
+ cat_data.mThumbnailId = thumbnail_id;
+ cat_changed = true;
+ }
+
// If anything has changed above, fire the callback.
if (cat_changed)
cat_data.mCallback();
@@ -666,6 +758,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t
S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN;
S32 current_num_known_descendents = LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN;
bool can_be_added = true;
+ LLUUID thumbnail_id;
LLViewerInventoryCategory* category = gInventory.getCategory(cat_id);
// If category could not be retrieved it might mean that
@@ -677,6 +770,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t
// Inventory category version is used to find out if some changes
// to a category have been made.
version = category->getVersion();
+ thumbnail_id = category->getThumbnailUUID();
LLInventoryModel::cat_array_t* cats;
LLInventoryModel::item_array_t* items;
@@ -701,12 +795,12 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t
{
if(init_name_hash)
{
- digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id);
- mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents,item_name_hash)));
+ digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id);
+ mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents,item_name_hash)));
}
else
{
- mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents)));
+ mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents)));
}
}
@@ -719,23 +813,25 @@ void LLInventoryCategoriesObserver::removeCategory(const LLUUID& cat_id)
}
LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData(
- const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents)
+ const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents)
: mCatID(cat_id)
, mCallback(cb)
, mVersion(version)
, mDescendentsCount(num_descendents)
+ , mThumbnailId(thumbnail_id)
, mIsNameHashInitialized(false)
{
}
LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData(
- const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash)
+ const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash)
: mCatID(cat_id)
, mCallback(cb)
, mVersion(version)
, mDescendentsCount(num_descendents)
+ , mThumbnailId(thumbnail_id)
, mIsNameHashInitialized(true)
, mItemNameHash(name_hash)
{
diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h
index 4af8102055..bec08d2cdf 100644
--- a/indra/newview/llinventoryobserver.h
+++ b/indra/newview/llinventoryobserver.h
@@ -104,6 +104,9 @@ public:
/*virtual*/ void startFetch();
/*virtual*/ void changed(U32 mask);
+
+ // For attempts to group requests if too many items are requested
+ static const S32 MAX_INDIVIDUAL_ITEM_REQUESTS;
private:
LLTimer mFetchingPeriod;
@@ -125,7 +128,7 @@ public:
LLInventoryFetchDescendentsObserver(const LLUUID& cat_id = LLUUID::null);
LLInventoryFetchDescendentsObserver(const uuid_vec_t& cat_ids);
- /*virtual*/ void startFetch();
+ virtual void startFetch();
/*virtual*/ void changed(U32 mask);
protected:
BOOL isCategoryComplete(const LLViewerInventoryCategory* cat) const;
@@ -273,14 +276,15 @@ protected:
typedef LLUUID digest_t; // To clarify the actual usage of this "UUID"
struct LLCategoryData
{
- LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents);
- LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash);
+ LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents);
+ LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash);
callback_t mCallback;
S32 mVersion;
S32 mDescendentsCount;
digest_t mItemNameHash;
bool mIsNameHashInitialized;
LLUUID mCatID;
+ LLUUID mThumbnailId;
};
typedef std::map<LLUUID, LLCategoryData> category_map_t;
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index 2799cb4cdf..54f91451ac 100644
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -40,6 +40,7 @@
#include "llfolderviewitem.h"
#include "llfloaterimcontainer.h"
#include "llimview.h"
+#include "llinspecttexture.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorymodelbackgroundfetch.h"
@@ -160,13 +161,15 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :
mInvFVBridgeBuilder(NULL),
mInventoryViewModel(p.name),
mGroupedItemBridge(new LLFolderViewGroupedItemBridge),
- mFocusSelection(false)
+ mFocusSelection(false),
+ mBuildChildrenViews(true),
+ mRootInited(false)
{
mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER;
if (!sColorSetInitialized)
{
- sDefaultColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
+ sDefaultColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE);
sDefaultHighlightColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);
sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE);
sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE);
@@ -182,6 +185,7 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :
mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this));
mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this));
mCommitCallbackRegistrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryPanel::fileUploadLocation, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.OpenNewFolderWindow", boost::bind(&LLInventoryPanel::openSingleViewInventory, this, LLUUID()));
}
LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id )
@@ -248,52 +252,116 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
{
// save off copy of params
mParams = params;
- // Clear up the root view
- // Note: This needs to be done *before* we build the new folder view
- LLUUID root_id = getRootFolderID();
- if (mFolderRoot.get())
- {
- removeItemID(root_id);
- mFolderRoot.get()->destroyView();
- }
- mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves
- {
- // Determine the root folder in case specified, and
- // build the views starting with that folder.
+ initFolderRoot();
+
+ // Initialize base class params.
+ LLPanel::initFromParams(mParams);
+}
+
+LLInventoryPanel::~LLInventoryPanel()
+{
+ U32 sort_order = getFolderViewModel()->getSorter().getSortOrder();
+ if (mSortOrderSetting != INHERIT_SORT_ORDER)
+ {
+ gSavedSettings.setU32(mSortOrderSetting, sort_order);
+ }
+
+ clearFolderRoot();
+}
+
+void LLInventoryPanel::initFolderRoot()
+{
+ // Clear up the root view
+ // Note: This needs to be done *before* we build the new folder view
+ LLUUID root_id = getRootFolderID();
+ if (mFolderRoot.get())
+ {
+ removeItemID(root_id);
+ mFolderRoot.get()->destroyView();
+ }
+
+ mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves
+ {
+ // Determine the root folder in case specified, and
+ // build the views starting with that folder.
LLFolderView* folder_view = createFolderRoot(root_id);
- mFolderRoot = folder_view->getHandle();
-
- addItemID(root_id, mFolderRoot.get());
- }
- mCommitCallbackRegistrar.popScope();
- mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar);
- mFolderRoot.get()->setEnableRegistrar(&mEnableCallbackRegistrar);
-
- // Scroller
- LLRect scroller_view_rect = getRect();
- scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
- LLScrollContainer::Params scroller_params(mParams.scroll());
- scroller_params.rect(scroller_view_rect);
- mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
- addChild(mScroller);
- mScroller->addChild(mFolderRoot.get());
- mFolderRoot.get()->setScrollContainer(mScroller);
- mFolderRoot.get()->setFollowsAll();
- mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox);
-
- // Set up the callbacks from the inventory we're viewing, and then build everything.
- mInventoryObserver = new LLInventoryPanelObserver(this);
- mInventory->addObserver(mInventoryObserver);
-
- mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this));
- mInventory->addObserver(mCompletionObserver);
-
- if (mBuildViewsOnInit && mViewsInitialized == VIEWS_UNINITIALIZED)
+ mFolderRoot = folder_view->getHandle();
+ mRootInited = true;
+
+ addItemID(root_id, mFolderRoot.get());
+ }
+ mCommitCallbackRegistrar.popScope();
+ mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar);
+ mFolderRoot.get()->setEnableRegistrar(&mEnableCallbackRegistrar);
+
+ // Scroller
+ LLRect scroller_view_rect = getRect();
+ scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
+ LLScrollContainer::Params scroller_params(mParams.scroll());
+ scroller_params.rect(scroller_view_rect);
+ mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
+ addChild(mScroller);
+ mScroller->addChild(mFolderRoot.get());
+ mFolderRoot.get()->setScrollContainer(mScroller);
+ mFolderRoot.get()->setFollowsAll();
+ mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox);
+
+ if (mSelectionCallback)
{
+ mFolderRoot.get()->setSelectCallback(mSelectionCallback);
+ }
+
+ // Set up the callbacks from the inventory we're viewing, and then build everything.
+ mInventoryObserver = new LLInventoryPanelObserver(this);
+ mInventory->addObserver(mInventoryObserver);
+
+ mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this));
+ mInventory->addObserver(mCompletionObserver);
+
+ if (mBuildViewsOnInit)
+ {
+ initializeViewBuilding();
+ }
+
+ if (mSortOrderSetting != INHERIT_SORT_ORDER)
+ {
+ setSortOrder(gSavedSettings.getU32(mSortOrderSetting));
+ }
+ else
+ {
+ setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER));
+ }
+
+ // hide inbox
+ if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible"))
+ {
+ getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX));
+ }
+ // hide marketplace listing box, unless we are a marketplace panel
+ if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mParams.use_marketplace_folders)
+ {
+ getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS));
+ }
+
+ // set the filter for the empty folder if the debug setting is on
+ if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders"))
+ {
+ getFilter().setFilterEmptySystemFolders();
+ }
+
+ // keep track of the clipboard state so that we avoid filtering too much
+ mClipboardState = LLClipboard::instance().getGeneration();
+}
+
+void LLInventoryPanel::initializeViewBuilding()
+{
+ if (mViewsInitialized == VIEWS_UNINITIALIZED)
+ {
+ LL_DEBUGS("Inventory") << "Setting views for " << getName() << " to initialize" << LL_ENDL;
// Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle.
// Initializing views takes a while so always do it onIdle if viewer already loaded.
- if (mInventory->isInventoryUsable()
+ if (mInventory->isInventoryUsable()
&& LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT)
{
// Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect
@@ -306,49 +374,6 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
gIdleCallbacks.addFunction(onIdle, (void*)this);
}
}
-
- if (mSortOrderSetting != INHERIT_SORT_ORDER)
- {
- setSortOrder(gSavedSettings.getU32(mSortOrderSetting));
- }
- else
- {
- setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER));
- }
-
- // hide inbox
- if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible"))
- {
- getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX));
- }
- // hide marketplace listing box, unless we are a marketplace panel
- if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mParams.use_marketplace_folders)
- {
- getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS));
- }
-
- // set the filter for the empty folder if the debug setting is on
- if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders"))
- {
- getFilter().setFilterEmptySystemFolders();
- }
-
- // keep track of the clipboard state so that we avoid filtering too much
- mClipboardState = LLClipboard::instance().getGeneration();
-
- // Initialize base class params.
- LLPanel::initFromParams(mParams);
-}
-
-LLInventoryPanel::~LLInventoryPanel()
-{
- U32 sort_order = getFolderViewModel()->getSorter().getSortOrder();
- if (mSortOrderSetting != INHERIT_SORT_ORDER)
- {
- gSavedSettings.setU32(mSortOrderSetting, sort_order);
- }
-
- clearFolderRoot();
}
/*virtual*/
@@ -356,8 +381,11 @@ void LLInventoryPanel::onVisibilityChange(BOOL new_visibility)
{
if (new_visibility && mViewsInitialized == VIEWS_UNINITIALIZED)
{
- mViewsInitialized = VIEWS_INITIALIZING;
- gIdleCallbacks.addFunction(onIdle, (void*)this);
+ // first call can be from tab initialization
+ if (gFloaterView->getParentFloater(this) != NULL)
+ {
+ initializeViewBuilding();
+ }
}
LLPanel::onVisibilityChange(new_visibility);
}
@@ -743,7 +771,7 @@ LLUUID LLInventoryPanel::getRootFolderID()
LLStringExplicit label(mParams.start_folder.name());
setLabel(label);
- root_id = gInventory.findCategoryUUIDForType(preferred_type, false);
+ root_id = gInventory.findCategoryUUIDForType(preferred_type);
if (root_id.isNull())
{
LL_WARNS() << "Could not find folder of type " << preferred_type << LL_ENDL;
@@ -878,6 +906,7 @@ void LLInventoryPanel::idle(void* user_data)
void LLInventoryPanel::initializeViews(F64 max_time)
{
if (!gInventory.isInventoryUsable()) return;
+ if (!mRootInited) return;
mViewsInitialized = VIEWS_BUILDING;
@@ -905,7 +934,10 @@ void LLInventoryPanel::initializeViews(F64 max_time)
gIdleCallbacks.addFunction(idle, this);
- openStartFolderOrMyInventory();
+ if(mParams.open_first_folder)
+ {
+ openStartFolderOrMyInventory();
+ }
// Special case for new user login
if (gAgent.isFirstLogin())
@@ -937,8 +969,8 @@ LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * br
params.tool_tip = params.name;
params.allow_drop = allow_drop;
- params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor));
- params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor));
+ params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor);
+ params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);
return LLUICtrlFactory::create<LLFolderViewFolder>(params);
}
@@ -954,8 +986,8 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge
params.rect = LLRect (0, 0, 0, 0);
params.tool_tip = params.name;
- params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor));
- params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor));
+ params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor);
+ params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);
return LLUICtrlFactory::create<LLFolderViewItem>(params);
}
@@ -1011,8 +1043,11 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
LLInventoryObject const* objectp,
LLFolderViewItem *folder_view_item,
LLFolderViewFolder *parent_folder,
- const EBuildModes &mode)
+ const EBuildModes &mode,
+ S32 depth)
{
+ depth++;
+
// Force the creation of an extra root level folder item if required by the inventory panel (default is "false")
bool allow_drop = true;
bool create_root = false;
@@ -1042,7 +1077,7 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
if (objectp->getType() >= LLAssetType::AT_COUNT)
{
// Example: Happens when we add assets of new, not yet supported type to library
- LL_DEBUGS() << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : "
+ LL_DEBUGS("Inventory") << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : "
<< ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID()
<< LL_ENDL;
@@ -1112,7 +1147,8 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
}
}
- bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY;
+ bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY
+ && (mBuildChildrenViews || depth == 0);
if (create_children)
{
@@ -1132,12 +1168,15 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
{
create_children = false;
// run it again for the sake of creating children
- mBuildViewsQueue.push_back(id);
+ if (mBuildChildrenViews || depth == 0)
+ {
+ mBuildViewsQueue.push_back(id);
+ }
}
else
{
create_children = true;
- folder_view_item->setChildrenInited(true);
+ folder_view_item->setChildrenInited(mBuildChildrenViews);
}
break;
}
@@ -1145,7 +1184,10 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
{
create_children = false;
// run it to create children, current caller is only interested in current view
- mBuildViewsQueue.push_back(id);
+ if (mBuildChildrenViews || depth == 0)
+ {
+ mBuildViewsQueue.push_back(id);
+ }
break;
}
case BUILD_ONE_FOLDER:
@@ -1175,7 +1217,13 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
LLViewerInventoryItem::item_array_t* items;
mInventory->lockDirectDescendentArrays(id, categories, items);
+ // Make sure panel won't lock in a loop over existing items if
+ // folder is enormous and at least some work gets done
+ const S32 MIN_ITEMS_PER_CALL = 500;
+ const S32 starting_item_count = mItemMap.size();
+
LLFolderViewFolder *parentp = dynamic_cast<LLFolderViewFolder*>(folder_view_item);
+ bool done = true;
if(categories)
{
@@ -1193,11 +1241,28 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
// each time, especially since content is growing, we can just
// iter over copy of mItemMap in some way
LLFolderViewItem* view_itemp = getItemByID(cat->getUUID());
- buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode));
+ buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode), depth);
}
else
{
- buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode));
+ buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode), depth);
+ }
+ }
+
+ if (!mBuildChildrenViews
+ && mode == BUILD_TIMELIMIT
+ && MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size())
+ {
+ // Single folder view, check if we still have time
+ //
+ // Todo: make sure this causes no dupplciates, breaks nothing,
+ // especially filters and arrange
+ F64 curent_time = LLTimer::getTotalSeconds();
+ if (mBuildViewsEndTime < curent_time)
+ {
+ mBuildViewsQueue.push_back(id);
+ done = false;
+ break;
}
}
}
@@ -1217,10 +1282,33 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
// each time, especially since content is growing, we can just
// iter over copy of mItemMap in some way
LLFolderViewItem* view_itemp = getItemByID(item->getUUID());
- buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode);
+ buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode, depth);
+ }
+
+ if (!mBuildChildrenViews
+ && mode == BUILD_TIMELIMIT
+ && MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size())
+ {
+ // Single folder view, check if we still have time
+ //
+ // Todo: make sure this causes no dupplciates, breaks nothing,
+ // especially filters and arrange
+ F64 curent_time = LLTimer::getTotalSeconds();
+ if (mBuildViewsEndTime < curent_time)
+ {
+ mBuildViewsQueue.push_back(id);
+ done = false;
+ break;
+ }
}
}
}
+
+ if (!mBuildChildrenViews && done)
+ {
+ // flat list is done initializing folder
+ folder_view_item->setChildrenInited(true);
+ }
mInventory->unlockDirectDescendentArrays(id);
}
@@ -1285,6 +1373,37 @@ BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask)
return TRUE;
}
+BOOL LLInventoryPanel::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ if (const LLFolderViewItem* hover_item_p = (!mFolderRoot.isDead()) ? mFolderRoot.get()->getHoveredItem() : nullptr)
+ {
+ if (const LLFolderViewModelItemInventory* vm_item_p = static_cast<const LLFolderViewModelItemInventory*>(hover_item_p->getViewModelItem()))
+ {
+ LLSD params;
+ params["inv_type"] = vm_item_p->getInventoryType();
+ params["thumbnail_id"] = vm_item_p->getThumbnailUUID();
+ params["item_id"] = vm_item_p->getUUID();
+
+ // tooltip should only show over folder, but screen
+ // rect includes items under folder as well
+ LLRect actionable_rect = hover_item_p->calcScreenRect();
+ if (hover_item_p->isOpen() && hover_item_p->hasVisibleChildren())
+ {
+ actionable_rect.mBottom = actionable_rect.mTop - hover_item_p->getItemHeight();
+ }
+
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(hover_item_p->getToolTip())
+ .sticky_rect(actionable_rect)
+ .delay_time(LLView::getTooltipTimeout())
+ .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1))
+ .create_params(params));
+ return TRUE;
+ }
+ }
+ return LLPanel::handleToolTip(x, y, mask);
+}
+
BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
@@ -1330,6 +1449,45 @@ void LLInventoryPanel::onFocusReceived()
// inventory now handles cut/copy/paste/delete
LLEditMenuHandler::gEditMenuHandler = mFolderRoot.get();
+ // Tab support, when tabbing into this view, select first item
+ // (ideally needs to account for scroll)
+ bool select_first = mSelectThisID.isNull() && mFolderRoot.get() && mFolderRoot.get()->getSelectedCount() == 0;
+
+ if (select_first)
+ {
+ LLFolderViewFolder::folders_t::const_iterator folders_it = mFolderRoot.get()->getFoldersBegin();
+ LLFolderViewFolder::folders_t::const_iterator folders_end = mFolderRoot.get()->getFoldersEnd();
+
+ for (; folders_it != folders_end; ++folders_it)
+ {
+ const LLFolderViewFolder* folder_view = *folders_it;
+ if (folder_view->getVisible())
+ {
+ const LLFolderViewModelItemInventory* modelp = static_cast<const LLFolderViewModelItemInventory*>(folder_view->getViewModelItem());
+ setSelectionByID(modelp->getUUID(), TRUE);
+ select_first = false;
+ break;
+ }
+ }
+ }
+
+ if (select_first)
+ {
+ LLFolderViewFolder::items_t::const_iterator items_it = mFolderRoot.get()->getItemsBegin();
+ LLFolderViewFolder::items_t::const_iterator items_end = mFolderRoot.get()->getItemsEnd();
+
+ for (; items_it != items_end; ++items_it)
+ {
+ const LLFolderViewItem* item_view = *items_it;
+ if (item_view->getVisible())
+ {
+ const LLFolderViewModelItemInventory* modelp = static_cast<const LLFolderViewModelItemInventory*>(item_view->getViewModelItem());
+ setSelectionByID(modelp->getUUID(), TRUE);
+ break;
+ }
+ }
+ }
+
LLPanel::onFocusReceived();
}
@@ -1380,6 +1538,7 @@ void LLInventoryPanel::setSelectCallback(const boost::function<void (const std::
{
mFolderRoot.get()->setSelectCallback(cb);
}
+ mSelectionCallback = cb;
}
void LLInventoryPanel::clearSelection()
@@ -1426,6 +1585,10 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it
{
fv->startRenamingSelectedItem();
}
+ else
+ {
+ LL_DEBUGS("Inventory") << "Failed to start renemr, no items selected" << LL_ENDL;
+ }
}
std::set<LLFolderViewItem*> selected_items = mFolderRoot.get()->getSelectionList();
@@ -1621,6 +1784,11 @@ void LLInventoryPanel::fileUploadLocation(const LLSD& userdata)
}
}
+void LLInventoryPanel::openSingleViewInventory(LLUUID folder_id)
+{
+ LLPanelMainInventory::newFolderWindow(folder_id.isNull() ? LLFolderBridge::sSelf.get()->getUUID() : folder_id);
+}
+
void LLInventoryPanel::purgeSelectedItems()
{
if (!mFolderRoot.get()) return;
@@ -1754,22 +1922,49 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open)
}
//static
-void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id, BOOL main_panel, BOOL take_keyboard_focus, BOOL reset_filter)
+void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id, BOOL use_main_panel, BOOL take_keyboard_focus, BOOL reset_filter)
{
LLSidepanelInventory* sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory");
sidepanel_inventory->showInventoryPanel();
bool in_inbox = (gInventory.isObjectDescendentOf(obj_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX)));
- if (!in_inbox && (main_panel || !sidepanel_inventory->getMainInventoryPanel()->isRecentItemsPanelSelected()))
+ if (!in_inbox && (use_main_panel || !sidepanel_inventory->getMainInventoryPanel()->isRecentItemsPanelSelected()))
{
sidepanel_inventory->selectAllItemsPanel();
}
+
+ LLFloater* inventory_floater = LLFloaterSidePanelContainer::getTopmostInventoryFloater();
+ if(!auto_open && inventory_floater && inventory_floater->getVisible())
+ {
+ LLSidepanelInventory *inventory_panel = inventory_floater->findChild<LLSidepanelInventory>("main_panel");
+ LLPanelMainInventory* main_panel = inventory_panel->getMainInventoryPanel();
+ if(main_panel->isSingleFolderMode() && main_panel->isGalleryViewMode())
+ {
+ LL_DEBUGS("Inventory") << "Opening gallery panel for item" << obj_id << LL_ENDL;
+ main_panel->setGallerySelection(obj_id);
+ return;
+ }
+ }
+
+ LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();
+ if (main_inventory && main_inventory->isSingleFolderMode()
+ && use_main_panel)
+ {
+ const LLInventoryObject *obj = gInventory.getObject(obj_id);
+ if (obj)
+ {
+ LL_DEBUGS("Inventory") << "Opening main inventory panel for item" << obj_id << LL_ENDL;
+ main_inventory->setSingleFolderViewRoot(obj->getParentUUID(), false);
+ main_inventory->setGallerySelection(obj_id);
+ return;
+ }
+ }
LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open);
if (active_panel)
{
- LL_DEBUGS("Messaging") << "Highlighting" << obj_id << LL_ENDL;
+ LL_DEBUGS("Messaging", "Inventory") << "Highlighting" << obj_id << LL_ENDL;
if (reset_filter)
{
@@ -1788,7 +1983,7 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L
inventory_panel->setSelection(obj_id, take_keyboard_focus);
}
}
- else
+ else if (auto_open)
{
LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory");
if (floater_inventory)
@@ -1797,9 +1992,33 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L
}
active_panel->setSelection(obj_id, take_keyboard_focus);
}
+ else
+ {
+ // Created items are going to receive proper focus from callbacks
+ active_panel->setSelection(obj_id, take_keyboard_focus);
+ }
}
}
+void LLInventoryPanel::setSFViewAndOpenFolder(const LLInventoryPanel* panel, const LLUUID& folder_id)
+{
+
+ LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory");
+ for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
+ {
+ LLFloaterSidePanelContainer* inventory_floater = dynamic_cast<LLFloaterSidePanelContainer*>(*iter);
+ LLSidepanelInventory* sidepanel_inventory = inventory_floater->findChild<LLSidepanelInventory>("main_panel");
+
+ LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();
+ if (main_inventory && panel->hasAncestor(main_inventory) && !main_inventory->isSingleFolderMode())
+ {
+ main_inventory->initSingleFolderRoot(folder_id);
+ main_inventory->toggleViewMode();
+ main_inventory->setSingleFolderViewRoot(folder_id, false);
+ }
+ }
+}
+
void LLInventoryPanel::addHideFolderType(LLFolderType::EType folder_type)
{
getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << folder_type));
@@ -2008,10 +2227,205 @@ LLInventoryRecentItemsPanel::LLInventoryRecentItemsPanel( const Params& params)
mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER;
}
+static LLDefaultChildRegistry::Register<LLInventorySingleFolderPanel> t_single_folder_inventory_panel("single_folder_inventory_panel");
+
+LLInventorySingleFolderPanel::LLInventorySingleFolderPanel(const Params& params)
+ : LLInventoryPanel(params)
+{
+ mBuildChildrenViews = false;
+ getFilter().setSingleFolderMode(true);
+ getFilter().setEmptyLookupMessage("InventorySingleFolderNoMatches");
+ getFilter().setDefaultEmptyLookupMessage("InventorySingleFolderEmpty");
+
+ mCommitCallbackRegistrar.replace("Inventory.DoToSelected", boost::bind(&LLInventorySingleFolderPanel::doToSelected, this, _2));
+ mCommitCallbackRegistrar.replace("Inventory.DoCreate", boost::bind(&LLInventorySingleFolderPanel::doCreate, this, _2));
+ mCommitCallbackRegistrar.replace("Inventory.Share", boost::bind(&LLInventorySingleFolderPanel::doShare, this));
+}
+
+LLInventorySingleFolderPanel::~LLInventorySingleFolderPanel()
+{
+}
+
+void LLInventorySingleFolderPanel::initFromParams(const Params& p)
+{
+ mFolderID = gInventory.getRootFolderID();
+
+ mParams = p;
+ LLPanel::initFromParams(mParams);
+}
+
+void LLInventorySingleFolderPanel::initFolderRoot(const LLUUID& start_folder_id)
+{
+ if(mRootInited) return;
+
+ mRootInited = true;
+ if(start_folder_id.notNull())
+ {
+ mFolderID = start_folder_id;
+ }
+
+ mParams.open_first_folder = false;
+ mParams.start_folder.id = mFolderID;
+
+ LLInventoryPanel::initFolderRoot();
+ mFolderRoot.get()->setSingleFolderMode(true);
+}
+
+void LLInventorySingleFolderPanel::changeFolderRoot(const LLUUID& new_id)
+{
+ if (mFolderID != new_id)
+ {
+ if(mFolderID.notNull())
+ {
+ mBackwardFolders.push_back(mFolderID);
+ }
+ mFolderID = new_id;
+ updateSingleFolderRoot();
+ }
+}
+
+void LLInventorySingleFolderPanel::onForwardFolder()
+{
+ if(isForwardAvailable())
+ {
+ mBackwardFolders.push_back(mFolderID);
+ mFolderID = mForwardFolders.back();
+ mForwardFolders.pop_back();
+ updateSingleFolderRoot();
+ }
+}
+
+void LLInventorySingleFolderPanel::onBackwardFolder()
+{
+ if(isBackwardAvailable())
+ {
+ mForwardFolders.push_back(mFolderID);
+ mFolderID = mBackwardFolders.back();
+ mBackwardFolders.pop_back();
+ updateSingleFolderRoot();
+ }
+}
+
+void LLInventorySingleFolderPanel::clearNavigationHistory()
+{
+ mForwardFolders.clear();
+ mBackwardFolders.clear();
+}
+
+bool LLInventorySingleFolderPanel::isBackwardAvailable()
+{
+ return (!mBackwardFolders.empty() && (mFolderID != mBackwardFolders.back()));
+}
+
+bool LLInventorySingleFolderPanel::isForwardAvailable()
+{
+ return (!mForwardFolders.empty() && (mFolderID != mForwardFolders.back()));
+}
+
+boost::signals2::connection LLInventorySingleFolderPanel::setRootChangedCallback(root_changed_callback_t cb)
+{
+ return mRootChangedSignal.connect(cb);
+}
+
+void LLInventorySingleFolderPanel::updateSingleFolderRoot()
+{
+ if (mFolderID != getRootFolderID())
+ {
+ mRootChangedSignal();
+
+ LLUUID root_id = mFolderID;
+ if (mFolderRoot.get())
+ {
+ mItemMap.clear();
+ mFolderRoot.get()->destroyRoot();
+ }
+
+ mCommitCallbackRegistrar.pushScope();
+ {
+ LLFolderView* folder_view = createFolderRoot(root_id);
+ folder_view->setChildrenInited(false);
+ mFolderRoot = folder_view->getHandle();
+ mFolderRoot.get()->setSingleFolderMode(true);
+ addItemID(root_id, mFolderRoot.get());
+
+ LLRect scroller_view_rect = getRect();
+ scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
+ LLScrollContainer::Params scroller_params(mParams.scroll());
+ scroller_params.rect(scroller_view_rect);
+
+ if (mScroller)
+ {
+ removeChild(mScroller);
+ delete mScroller;
+ mScroller = NULL;
+ }
+ mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
+ addChild(mScroller);
+ mScroller->addChild(mFolderRoot.get());
+ mFolderRoot.get()->setScrollContainer(mScroller);
+ mFolderRoot.get()->setFollowsAll();
+ mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox);
+
+ if (!mSelectionCallback.empty())
+ {
+ mFolderRoot.get()->setSelectCallback(mSelectionCallback);
+ }
+ }
+ mCommitCallbackRegistrar.popScope();
+ mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar);
+
+ buildNewViews(mFolderID);
+
+ LLFloater* root_floater = gFloaterView->getParentFloater(this);
+ if(root_floater)
+ {
+ root_floater->setFocus(true);
+ }
+ }
+}
+
+bool LLInventorySingleFolderPanel::hasVisibleItems()
+{
+ return mFolderRoot.get()->hasVisibleChildren();
+}
+
+void LLInventorySingleFolderPanel::doCreate(const LLSD& userdata)
+{
+ std::string type_name = userdata.asString();
+ LLUUID dest_id = LLFolderBridge::sSelf.get()->getUUID();
+ if (("category" == type_name) || ("outfit" == type_name))
+ {
+ changeFolderRoot(dest_id);
+ }
+ reset_inventory_filter();
+ menu_create_inventory_item(this, dest_id, userdata);
+}
+
+void LLInventorySingleFolderPanel::doToSelected(const LLSD& userdata)
+{
+ if (("open_in_current_window" == userdata.asString()))
+ {
+ changeFolderRoot(LLFolderBridge::sSelf.get()->getUUID());
+ return;
+ }
+ LLInventoryPanel::doToSelected(userdata);
+}
+
+void LLInventorySingleFolderPanel::doShare()
+{
+ LLAvatarActions::shareWithAvatars(this);
+}
/************************************************************************/
/* Asset Pre-Filtered Inventory Panel related class */
/************************************************************************/
+LLAssetFilteredInventoryPanel::LLAssetFilteredInventoryPanel(const Params& p)
+ : LLInventoryPanel(p)
+ , mAssetType(LLAssetType::AT_NONE)
+{
+}
+
+
void LLAssetFilteredInventoryPanel::initFromParams(const Params& p)
{
mAssetType = LLAssetType::lookup(p.filter_asset_type.getValue());
diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h
index 2c782a5ea7..341be0cf86 100644
--- a/indra/newview/llinventorypanel.h
+++ b/indra/newview/llinventorypanel.h
@@ -107,6 +107,7 @@ public:
Optional<LLFolderView::Params> folder_view;
Optional<LLFolderViewFolder::Params> folder;
Optional<LLFolderViewItem::Params> item;
+ Optional<bool> open_first_folder;
// All item and folder views will be initialized on init if true (default)
// Will initialize on visibility change otherwise.
@@ -126,6 +127,7 @@ public:
show_root_folder("show_root_folder", false),
allow_drop_on_root("allow_drop_on_root", true),
use_marketplace_folders("use_marketplace_folders", false),
+ open_first_folder("open_first_folder", true),
scroll("scroll"),
accepts_drag_and_drop("accepts_drag_and_drop"),
folder_view("folder_view"),
@@ -159,22 +161,23 @@ public:
LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; }
// LLView methods
- /*virtual*/ void onVisibilityChange(BOOL new_visibility);
- void draw();
- /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask );
- BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ void onVisibilityChange(BOOL new_visibility) override;
+ void draw() override;
+ /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ) override;
+ BOOL handleHover(S32 x, S32 y, MASK mask) override;
/*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
EAcceptance* accept,
- std::string& tooltip_msg);
+ std::string& tooltip_msg) override;
+ BOOL handleToolTip(S32 x, S32 y, MASK mask) override;
// LLUICtrl methods
- /*virtual*/ void onFocusLost();
- /*virtual*/ void onFocusReceived();
+ /*virtual*/ void onFocusLost() override;
+ /*virtual*/ void onFocusReceived() override;
void onFolderOpening(const LLUUID &id);
// LLBadgeHolder methods
- bool addBadge(LLBadge * badge);
+ bool addBadge(LLBadge * badge) override;
// Call this method to set the selection.
void openAllFolders();
@@ -211,6 +214,7 @@ public:
LLUUID getRootFolderID();
LLScrollContainer* getScrollableContainer() { return mScroller; }
bool getAllowDropOnRoot() { return mParams.allow_drop_on_root; }
+ bool areViewsInitialized() { return mViewsInitialized == VIEWS_INITIALIZED && mFolderRoot.get() && !mFolderRoot.get()->needsArrange(); }
void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action);
@@ -221,6 +225,7 @@ public:
void doCreate(const LLSD& userdata);
bool beginIMSession();
void fileUploadLocation(const LLSD& userdata);
+ void openSingleViewInventory(LLUUID folder_id = LLUUID());
void purgeSelectedItems();
bool attachObject(const LLSD& userdata);
static void idle(void* user_data);
@@ -241,10 +246,10 @@ public:
static void openInventoryPanelAndSetSelection(BOOL auto_open,
const LLUUID& obj_id,
- BOOL main_panel = FALSE,
+ BOOL use_main_panel = FALSE,
BOOL take_keyboard_focus = TAKE_FOCUS_YES,
BOOL reset_filter = FALSE);
-
+ static void setSFViewAndOpenFolder(const LLInventoryPanel* panel, const LLUUID& folder_id);
void addItemID(const LLUUID& id, LLFolderViewItem* itemp);
void removeItemID(const LLUUID& id);
LLFolderViewItem* getItemByID(const LLUUID& id);
@@ -262,6 +267,10 @@ public:
static void callbackPurgeSelectedItems(const LLSD& notification, const LLSD& response, const std::vector<LLUUID> inventory_selected);
+ void changeFolderRoot(const LLUUID& new_id) {};
+ void initFolderRoot();
+ void initializeViewBuilding();
+
protected:
void openStartFolderOrMyInventory(); // open the first level of inventory
void onItemsCompletion(); // called when selected items are complete
@@ -298,6 +307,9 @@ protected:
*/
const LLInventoryFolderViewModelBuilder* mInvFVBridgeBuilder;
+ bool mBuildChildrenViews; // build root and children
+ bool mRootInited;
+
//--------------------------------------------------------------------
// Sorting
@@ -357,6 +369,8 @@ protected:
virtual LLFolderView * createFolderRoot(LLUUID root_id );
virtual LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop);
virtual LLFolderViewItem* createFolderViewItem(LLInvFVBridge * bridge);
+
+ boost::function<void(const std::deque<LLFolderViewItem*>& items, BOOL user_action)> mSelectionCallback;
private:
// buildViewsTree does not include some checks and is meant
// for recursive use, use buildNewViews() for first call
@@ -365,7 +379,8 @@ private:
LLInventoryObject const* objectp,
LLFolderViewItem *target_view,
LLFolderViewFolder *parent_folder_view,
- const EBuildModes &mode);
+ const EBuildModes &mode,
+ S32 depth = -1);
typedef enum e_views_initialization_state
{
@@ -381,6 +396,55 @@ private:
std::deque<LLUUID> mBuildViewsQueue;
};
+
+class LLInventorySingleFolderPanel : public LLInventoryPanel
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLInventoryPanel::Params>
+ {};
+
+ void initFromParams(const Params& p);
+ bool isSelectionRemovable() { return false; }
+
+ void initFolderRoot(const LLUUID& start_folder_id = LLUUID::null);
+
+ void changeFolderRoot(const LLUUID& new_id);
+ void onForwardFolder();
+ void onBackwardFolder();
+ void clearNavigationHistory();
+ LLUUID getSingleFolderRoot() { return mFolderID; }
+
+ void doCreate(const LLSD& userdata);
+ void doToSelected(const LLSD& userdata);
+ void doShare();
+
+ bool isBackwardAvailable();
+ bool isForwardAvailable();
+
+ bool hasVisibleItems();
+
+ void setNavBackwardList(std::list<LLUUID> backward_list) { mBackwardFolders = backward_list; }
+ void setNavForwardList(std::list<LLUUID> forward_list) { mForwardFolders = forward_list; }
+ std::list<LLUUID> getNavBackwardList() { return mBackwardFolders; }
+ std::list<LLUUID> getNavForwardList() { return mForwardFolders; }
+
+ typedef boost::function<void()> root_changed_callback_t;
+ boost::signals2::connection setRootChangedCallback(root_changed_callback_t cb);
+
+protected:
+ LLInventorySingleFolderPanel(const Params& params);
+ ~LLInventorySingleFolderPanel();
+ void updateSingleFolderRoot();
+
+ friend class LLUICtrlFactory;
+
+ LLUUID mFolderID;
+ std::list<LLUUID> mBackwardFolders;
+ std::list<LLUUID> mForwardFolders;
+
+ boost::signals2::signal<void()> mRootChangedSignal;
+};
+
/************************************************************************/
/* Asset Pre-Filtered Inventory Panel related class */
/* Exchanges filter's flexibility for speed of generation and */
@@ -400,7 +464,7 @@ public:
void initFromParams(const Params& p);
protected:
- LLAssetFilteredInventoryPanel(const Params& p) : LLInventoryPanel(p) {}
+ LLAssetFilteredInventoryPanel(const Params& p);
friend class LLUICtrlFactory;
public:
~LLAssetFilteredInventoryPanel() {}
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index dd8c9b2dde..01496fa7ce 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -87,6 +87,7 @@ LLLoginInstance::LLLoginInstance() :
mLoginModule(new LLLogin()),
mNotifications(NULL),
mLoginState("offline"),
+ mSaveMFA(true),
mAttemptComplete(false),
mTransferRate(0.0f),
mDispatcher("LLLoginInstance", "change")
@@ -449,10 +450,7 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)
gViewerWindow->setShowProgress(FALSE);
}
- LLSD args(llsd::map( "MESSAGE", LLTrans::getString(response["message_id"]) ));
- LLSD payload;
- LLNotificationsUtil::add("PromptMFAToken", args, payload,
- boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2));
+ showMFAChallange(LLTrans::getString(response["message_id"]));
}
else if( reason_response == "key"
|| reason_response == "presence"
@@ -540,10 +538,7 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)
{
// SL-18511 this TOS failure happened while we are in the middle of an MFA challenge/response.
// the previously entered token is very likely expired, so prompt again
- LLSD args(llsd::map( "MESSAGE", LLTrans::getString("LoginFailedAuthenticationMFARequired") ));
- LLSD payload;
- LLNotificationsUtil::add("PromptMFAToken", args, payload,
- boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2));
+ showMFAChallange(LLTrans::getString("LoginFailedAuthenticationMFARequired"));
}
else
{
@@ -561,6 +556,22 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)
return true;
}
+void LLLoginInstance::showMFAChallange(const std::string& message)
+{
+ LLSD args(llsd::map("MESSAGE", message));
+ LLSD payload;
+ if (gSavedSettings.getBOOL("RememberUser"))
+ {
+ LLNotificationsUtil::add("PromptMFATokenWithSave", args, payload,
+ boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2));
+ }
+ else
+ {
+ LLNotificationsUtil::add("PromptMFAToken", args, payload,
+ boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2));
+ }
+}
+
bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & response)
{
bool continue_clicked = response["continue"].asBoolean();
@@ -576,6 +587,7 @@ bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & respon
// Set the request data to true and retry login.
mRequestData["params"]["token"] = token;
+ mSaveMFA = response.has("ignore") ? response["ignore"].asBoolean() : false;
reconnect();
} else {
LL_INFOS("LLLogin") << "PromptMFAToken: no token, attemptComplete" << LL_ENDL;
diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h
index ee3ef0e4b1..2e9aab7c00 100644
--- a/indra/newview/lllogininstance.h
+++ b/indra/newview/lllogininstance.h
@@ -56,6 +56,7 @@ public:
bool authSuccess() { return mAttemptComplete && mLoginState == "online"; }
const std::string& getLoginState() { return mLoginState; }
+ bool saveMFA() const { return mSaveMFA; }
LLSD getResponse(const std::string& key) { return getResponse()[key]; }
LLSD getResponse();
@@ -84,6 +85,7 @@ private:
void syncWithUpdater(ResponsePtr resp, const LLSD& notification, const LLSD& response);
bool handleTOSResponse(bool v, const std::string& key);
+ void showMFAChallange(const std::string& message);
bool handleMFAChallenge(LLSD const & notif, LLSD const & response);
void attemptComplete() { mAttemptComplete = true; } // In the future an event?
@@ -95,6 +97,7 @@ private:
LLSD mRequestData;
LLSD mResponseData;
bool mAttemptComplete;
+ bool mSaveMFA;
F64 mTransferRate;
std::string mSerialNumber;
int mLastExecEvent;
diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp
index 6f3d40bb3a..8784f403cb 100644
--- a/indra/newview/llmarketplacefunctions.cpp
+++ b/indra/newview/llmarketplacefunctions.cpp
@@ -700,10 +700,9 @@ void LLMarketplaceInventoryObserver::onIdleProcessQueue(void *userdata)
// If it's a folder known to the marketplace, let's check it's in proper shape
if (LLMarketplaceData::instance().isListed(*id_it) || LLMarketplaceData::instance().isVersionFolder(*id_it))
{
- LLInventoryCategory* cat = (LLInventoryCategory*)(obj);
// can trigger notifyObservers
// can cause more structural changes
- validate_marketplacelistings(cat);
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(obj->getUUID());
}
}
else
@@ -897,7 +896,7 @@ void LLMarketplaceData::setDataFetchedSignal(const status_updated_signal_t::slot
// Get/Post/Put requests to the SLM Server using the SLM API
void LLMarketplaceData::getSLMListings()
{
- const LLUUID marketplaceFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID marketplaceFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
setUpdating(marketplaceFolderId, true);
LLCoros::instance().launch("getSLMListings",
@@ -1804,7 +1803,7 @@ bool LLMarketplaceData::isUpdating(const LLUUID& folder_id, S32 depth)
}
else
{
- const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
std::set<LLUUID>::iterator it = mPendingUpdateSet.find(marketplace_listings_uuid);
if (it != mPendingUpdateSet.end())
{
@@ -1848,8 +1847,7 @@ void LLMarketplaceData::decrementValidationWaiting(const LLUUID& folder_id, S32
if (found->second <= 0)
{
mValidationWaitingList.erase(found);
- LLInventoryCategory *cat = gInventory.getCategory(folder_id);
- validate_marketplacelistings(cat);
+ LLMarketplaceValidator::getInstance()->validateMarketplaceListings(folder_id);
update_marketplace_category(folder_id);
gInventory.notifyObservers();
}
diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp
index 602b7412a4..de988555c5 100644
--- a/indra/newview/lloutfitgallery.cpp
+++ b/indra/newview/lloutfitgallery.cpp
@@ -36,12 +36,11 @@
#include "llaccordionctrltab.h"
#include "llappearancemgr.h"
-#include "llagentbenefits.h"
#include "llerror.h"
#include "llfilepicker.h"
#include "llfloaterperms.h"
#include "llfloaterreg.h"
-#include "llfloatersimpleoutfitsnapshot.h"
+#include "llfloatersimplesnapshot.h"
#include "llimagedimensionsinfo.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
@@ -65,7 +64,6 @@ const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;
LLOutfitGallery::LLOutfitGallery(const LLOutfitGallery::Params& p)
: LLOutfitListBase(),
- mTexturesObserver(NULL),
mOutfitsObserver(NULL),
mScrollPanel(NULL),
mGalleryPanel(NULL),
@@ -125,7 +123,6 @@ void LLOutfitGallery::onOpen(const LLSD& info)
LLOutfitListBase::onOpen(info);
if (!mGalleryCreated)
{
- loadPhotos();
uuid_vec_t cats;
getCurrentCategories(cats);
int n = cats.size();
@@ -149,6 +146,264 @@ void LLOutfitGallery::draw()
}
}
+BOOL LLOutfitGallery::handleKeyHere(KEY key, MASK mask)
+{
+ BOOL handled = FALSE;
+ switch (key)
+ {
+ case KEY_RETURN:
+ // Open selected items if enter key hit on the inventory panel
+ if (mask == MASK_NONE && mSelectedOutfitUUID.notNull())
+ {
+ // Or should it wearSelectedOutfit?
+ getSelectedItem()->openOutfitsContent();
+ }
+ handled = TRUE;
+ break;
+ case KEY_DELETE:
+#if LL_DARWIN
+ case KEY_BACKSPACE:
+#endif
+ // Delete selected items if delete or backspace key hit on the inventory panel
+ // Note: on Mac laptop keyboards, backspace and delete are one and the same
+ if (mSelectedOutfitUUID.notNull())
+ {
+ onRemoveOutfit(mSelectedOutfitUUID);
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_F2:
+ LLAppearanceMgr::instance().renameOutfit(mSelectedOutfitUUID);
+ handled = TRUE;
+ break;
+
+ case KEY_PAGE_UP:
+ if (mScrollPanel)
+ {
+ mScrollPanel->pageUp(30);
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_PAGE_DOWN:
+ if (mScrollPanel)
+ {
+ mScrollPanel->pageDown(30);
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_HOME:
+ if (mScrollPanel)
+ {
+ mScrollPanel->goToTop();
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_END:
+ if (mScrollPanel)
+ {
+ mScrollPanel->goToBottom();
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_LEFT:
+ moveLeft();
+ handled = TRUE;
+ break;
+
+ case KEY_RIGHT:
+ moveRight();
+ handled = TRUE;
+ break;
+
+ case KEY_UP:
+ moveUp();
+ handled = TRUE;
+ break;
+
+ case KEY_DOWN:
+ moveDown();
+ handled = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ if (handled)
+ {
+ mOutfitGalleryMenu->hide();
+ }
+
+ return handled;
+}
+
+void LLOutfitGallery::moveUp()
+{
+ if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ S32 n = mItemIndexMap[item];
+ n -= mItemsInRow;
+ if (n >= 0)
+ {
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ ChangeOutfitSelection(nullptr, item_id);
+ item->setFocus(TRUE);
+
+ scrollToShowItem(mSelectedOutfitUUID);
+ }
+ }
+ }
+}
+
+void LLOutfitGallery::moveDown()
+{
+ if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ S32 n = mItemIndexMap[item];
+ n += mItemsInRow;
+ if (n < mItemsAddedCount)
+ {
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ ChangeOutfitSelection(nullptr, item_id);
+ item->setFocus(TRUE);
+
+ scrollToShowItem(mSelectedOutfitUUID);
+ }
+ }
+ }
+}
+
+void LLOutfitGallery::moveLeft()
+{
+ if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ // Might be better to get item from panel
+ S32 n = mItemIndexMap[item];
+ n--;
+ if (n < 0)
+ {
+ n = mItemsAddedCount - 1;
+ }
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ ChangeOutfitSelection(nullptr, item_id);
+ item->setFocus(TRUE);
+
+ scrollToShowItem(mSelectedOutfitUUID);
+ }
+ }
+}
+
+void LLOutfitGallery::moveRight()
+{
+ if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1)
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ S32 n = mItemIndexMap[item];
+ n++;
+ if (n == mItemsAddedCount)
+ {
+ n = 0;
+ }
+ item = mIndexToItemMap[n];
+ LLUUID item_id = item->getUUID();
+ ChangeOutfitSelection(nullptr, item_id);
+ item->setFocus(TRUE);
+
+ scrollToShowItem(mSelectedOutfitUUID);
+ }
+ }
+}
+
+void LLOutfitGallery::onFocusLost()
+{
+ LLOutfitListBase::onFocusLost();
+
+ if (mSelectedOutfitUUID.notNull())
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ item->setSelected(false);
+ }
+ }
+}
+
+void LLOutfitGallery::onFocusReceived()
+{
+ LLOutfitListBase::onFocusReceived();
+
+ if (mSelectedOutfitUUID.notNull())
+ {
+ LLOutfitGalleryItem* item = getSelectedItem();
+ if (item)
+ {
+ item->setSelected(true);
+ }
+ }
+}
+
+void LLOutfitGallery::onRemoveOutfit(const LLUUID& outfit_cat_id)
+{
+ LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(onOutfitsRemovalConfirmation, _1, _2, outfit_cat_id));
+}
+
+void LLOutfitGallery::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option != 0) return; // canceled
+
+ if (outfit_cat_id.notNull())
+ {
+ gInventory.removeCategory(outfit_cat_id);
+ }
+}
+
+void LLOutfitGallery::scrollToShowItem(const LLUUID& item_id)
+{
+ LLOutfitGalleryItem* item = mOutfitMap[item_id];
+ if (item)
+ {
+ const LLRect visible_content_rect = mScrollPanel->getVisibleContentRect();
+
+ LLRect item_rect;
+ item->localRectToOtherView(item->getLocalRect(), &item_rect, mScrollPanel);
+ LLRect overlap_rect(item_rect);
+ overlap_rect.intersectWith(visible_content_rect);
+
+ //Scroll when the selected item is outside the visible area
+ if (overlap_rect.getHeight() + 5 < item->getRect().getHeight())
+ {
+ LLRect content_rect = mScrollPanel->getContentWindowRect();
+ LLRect constraint_rect;
+ constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight());
+
+ LLRect item_doc_rect;
+ item->localRectToOtherView(item->getLocalRect(), &item_doc_rect, mGalleryPanel);
+
+ mScrollPanel->scrollToShowRect(item_doc_rect, constraint_rect);
+ }
+ }
+}
+
void LLOutfitGallery::updateRowsIfNeeded()
{
if(((getRect().getWidth() - mRowPanelWidth) > mItemWidth) && mRowCount > 1)
@@ -269,8 +524,9 @@ void LLOutfitGallery::addToGallery(LLOutfitGalleryItem* item)
mHiddenItems.push_back(item);
return;
}
+ mItemIndexMap[item] = mItemsAddedCount;
+ mIndexToItemMap[mItemsAddedCount] = item;
mItemsAddedCount++;
- mItemIndexMap[item] = mItemsAddedCount - 1;
int n = mItemsAddedCount;
int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1;
int n_prev = n - 1;
@@ -306,6 +562,7 @@ void LLOutfitGallery::removeFromGalleryLast(LLOutfitGalleryItem* item)
int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1;
int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1;
mItemsAddedCount--;
+ mIndexToItemMap.erase(mItemsAddedCount);
bool remove_row = row_count != row_count_prev;
removeFromLastRow(mItems[mItemsAddedCount]);
@@ -331,6 +588,7 @@ void LLOutfitGallery::removeFromGalleryMiddle(LLOutfitGalleryItem* item)
}
int n = mItemIndexMap[item];
mItemIndexMap.erase(item);
+ mIndexToItemMap.erase(n);
std::vector<LLOutfitGalleryItem*> saved;
for (int i = mItemsAddedCount - 1; i > n; i--)
{
@@ -364,9 +622,15 @@ LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name, LLUUID
gitem->setFollowsTop();
gitem->setOutfitName(name);
gitem->setUUID(outfit_id);
+ gitem->setGallery(this);
return gitem;
}
+LLOutfitGalleryItem* LLOutfitGallery::getSelectedItem()
+{
+ return mOutfitMap[mSelectedOutfitUUID];
+}
+
void LLOutfitGallery::buildGalleryPanel(int row_count)
{
LLPanel::Params params;
@@ -440,12 +704,6 @@ void LLOutfitGallery::moveRowPanel(LLPanel* stack, int left, int bottom)
LLOutfitGallery::~LLOutfitGallery()
{
delete mOutfitGalleryMenu;
-
- if (gInventory.containsObserver(mTexturesObserver))
- {
- gInventory.removeObserver(mTexturesObserver);
- }
- delete mTexturesObserver;
if (gInventory.containsObserver(mOutfitsObserver))
{
@@ -617,6 +875,7 @@ void LLOutfitGallery::onChangeOutfitSelection(LLWearableItemsList* list, const L
{
mOutfitMap[category_id]->setSelected(TRUE);
}
+ // mSelectedOutfitUUID will be set in LLOutfitListBase::ChangeOutfitSelection
}
void LLOutfitGallery::wearSelectedOutfit()
@@ -668,7 +927,8 @@ static LLDefaultChildRegistry::Register<LLOutfitGalleryItem> r("outfit_gallery_i
LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p)
: LLPanel(p),
- mTexturep(NULL),
+ mGallery(nullptr),
+ mTexturep(nullptr),
mSelected(false),
mWorn(false),
mDefaultImage(true),
@@ -743,10 +1003,13 @@ void LLOutfitGalleryItem::setOutfitWorn(bool value)
mWorn = value;
LLStringUtil::format_map_t worn_string_args;
std::string worn_string = getString("worn_string", worn_string_args);
- LLUIColor text_color = LLUIColorTable::instance().getColor(mSelected ? "White" : (mWorn ? "OutfitGalleryItemWorn" : "White"), LLColor4::white);
+ LLUIColor text_color = LLUIColorTable::instance().getColor("White", LLColor4::white);
mOutfitWornText->setReadOnlyColor(text_color.get());
mOutfitNameText->setReadOnlyColor(text_color.get());
+ mOutfitWornText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall());
+ mOutfitNameText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall());
mOutfitWornText->setValue(value ? worn_string : "");
+ mOutfitNameText->setText(mOutfitName); // refresh LLTextViewModel to pick up font changes
}
void LLOutfitGalleryItem::setSelected(bool value)
@@ -770,8 +1033,63 @@ BOOL LLOutfitGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask)
BOOL LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask)
{
+ return openOutfitsContent() || LLPanel::handleDoubleClick(x, y, mask);
+}
+
+BOOL LLOutfitGalleryItem::handleKeyHere(KEY key, MASK mask)
+{
+ if (!mGallery)
+ {
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+ switch (key)
+ {
+ case KEY_LEFT:
+ mGallery->moveLeft();
+ handled = true;
+ break;
+
+ case KEY_RIGHT:
+ mGallery->moveRight();
+ handled = true;
+ break;
+
+ case KEY_UP:
+ mGallery->moveUp();
+ handled = true;
+ break;
+
+ case KEY_DOWN:
+ mGallery->moveDown();
+ handled = true;
+ break;
+
+ default:
+ break;
+ }
+ return handled;
+}
+
+void LLOutfitGalleryItem::onFocusLost()
+{
+ setSelected(false);
+
+ LLPanel::onFocusLost();
+}
+
+void LLOutfitGalleryItem::onFocusReceived()
+{
+ setSelected(true);
+
+ LLPanel::onFocusReceived();
+}
+
+bool LLOutfitGalleryItem::openOutfitsContent()
+{
LLTabContainer* appearence_tabs = LLPanelOutfitsInventory::findInstance()->getChild<LLTabContainer>("appearance_tabs");
- if (appearence_tabs && (mUUID != LLUUID()))
+ if (appearence_tabs && mUUID.notNull())
{
appearence_tabs->selectTabByName("outfitslist_tab");
LLPanel* panel = appearence_tabs->getCurrentPanel();
@@ -784,12 +1102,11 @@ BOOL LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask)
outfit_list->setSelectedOutfitByUUID(mUUID);
LLAccordionCtrlTab* tab = accordion->getSelectedTab();
tab->showAndFocusHeader();
- return TRUE;
+ return true;
}
}
}
-
- return LLPanel::handleDoubleClick(x, y, mask);
+ return false;
}
bool LLOutfitGalleryItem::setImageAssetId(LLUUID image_asset_id)
@@ -835,68 +1152,22 @@ LLContextMenu* LLOutfitGalleryContextMenu::createMenu()
boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id));
registrar.add("Outfit.Edit", boost::bind(editOutfit));
registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id));
- registrar.add("Outfit.Delete", boost::bind(&LLOutfitGalleryContextMenu::onRemoveOutfit, this, selected_id));
+ registrar.add("Outfit.Delete", boost::bind(LLOutfitGallery::onRemoveOutfit, selected_id));
registrar.add("Outfit.Create", boost::bind(&LLOutfitGalleryContextMenu::onCreate, this, _2));
- registrar.add("Outfit.UploadPhoto", boost::bind(&LLOutfitGalleryContextMenu::onUploadPhoto, this, selected_id));
- registrar.add("Outfit.SelectPhoto", boost::bind(&LLOutfitGalleryContextMenu::onSelectPhoto, this, selected_id));
- registrar.add("Outfit.TakeSnapshot", boost::bind(&LLOutfitGalleryContextMenu::onTakeSnapshot, this, selected_id));
- registrar.add("Outfit.RemovePhoto", boost::bind(&LLOutfitGalleryContextMenu::onRemovePhoto, this, selected_id));
+ registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitGalleryContextMenu::onThumbnail, this, selected_id));
enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitGalleryContextMenu::onEnable, this, _2));
enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitGalleryContextMenu::onVisible, this, _2));
return createFromFile("menu_gallery_outfit_tab.xml");
}
-void LLOutfitGalleryContextMenu::onUploadPhoto(const LLUUID& outfit_cat_id)
-{
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- if (gallery && outfit_cat_id.notNull())
- {
- gallery->uploadPhoto(outfit_cat_id);
- }
-}
-
-void LLOutfitGalleryContextMenu::onSelectPhoto(const LLUUID& outfit_cat_id)
-{
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- if (gallery && outfit_cat_id.notNull())
- {
- gallery->onSelectPhoto(outfit_cat_id);
- }
-}
-
-void LLOutfitGalleryContextMenu::onRemovePhoto(const LLUUID& outfit_cat_id)
-{
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- if (gallery && outfit_cat_id.notNull())
- {
- gallery->checkRemovePhoto(outfit_cat_id);
- gallery->refreshOutfit(outfit_cat_id);
- }
-}
-
-void LLOutfitGalleryContextMenu::onTakeSnapshot(const LLUUID& outfit_cat_id)
+void LLOutfitGalleryContextMenu::onThumbnail(const LLUUID& outfit_cat_id)
{
LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
if (gallery && outfit_cat_id.notNull())
{
- gallery->onTakeSnapshot(outfit_cat_id);
- }
-}
-
-void LLOutfitGalleryContextMenu::onRemoveOutfit(const LLUUID& outfit_cat_id)
-{
- LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitGalleryContextMenu::onOutfitsRemovalConfirmation, this, _1, _2, outfit_cat_id));
-}
-
-void LLOutfitGalleryContextMenu::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option != 0) return; // canceled
-
- if (outfit_cat_id.notNull())
- {
- gInventory.removeCategory(outfit_cat_id);
+ LLSD data(outfit_cat_id);
+ LLFloaterReg::showInstance("change_item_thumbnail", data);
}
}
@@ -919,16 +1190,6 @@ bool LLOutfitGalleryContextMenu::onEnable(LLSD::String param)
bool LLOutfitGalleryContextMenu::onVisible(LLSD::String param)
{
- mMenuHandle.get()->getChild<LLUICtrl>("upload_photo")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost()));
- if ("remove_photo" == param)
- {
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- LLUUID selected_id = mUUIDs.front();
- if (gallery && selected_id.notNull())
- {
- return !gallery->hasDefaultImage(selected_id);
- }
- }
return LLOutfitContextMenu::onVisible(param);
}
@@ -943,56 +1204,12 @@ void LLOutfitGalleryGearMenu::onUpdateItemsVisibility()
bool have_selection = getSelectedOutfitID().notNull();
mMenu->setItemVisible("expand", FALSE);
mMenu->setItemVisible("collapse", FALSE);
- mMenu->setItemVisible("upload_photo", have_selection);
- mMenu->setItemVisible("select_photo", have_selection);
- mMenu->setItemVisible("take_snapshot", have_selection);
- mMenu->setItemVisible("remove_photo", !hasDefaultImage());
+ mMenu->setItemVisible("thumbnail", have_selection);
mMenu->setItemVisible("sepatator3", TRUE);
mMenu->setItemVisible("sort_folders_by_name", TRUE);
LLOutfitListGearMenuBase::onUpdateItemsVisibility();
}
-void LLOutfitGalleryGearMenu::onUploadFoto()
-{
- LLUUID selected_outfit_id = getSelectedOutfitID();
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- if (gallery && selected_outfit_id.notNull())
- {
- gallery->uploadPhoto(selected_outfit_id);
- }
-}
-
-void LLOutfitGalleryGearMenu::onSelectPhoto()
-{
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- LLUUID selected_outfit_id = getSelectedOutfitID();
- if (gallery && !selected_outfit_id.isNull())
- {
- gallery->onSelectPhoto(selected_outfit_id);
- }
-}
-
-void LLOutfitGalleryGearMenu::onRemovePhoto()
-{
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- LLUUID selected_outfit_id = getSelectedOutfitID();
- if (gallery && !selected_outfit_id.isNull())
- {
- gallery->checkRemovePhoto(selected_outfit_id);
- gallery->refreshOutfit(selected_outfit_id);
- }
-}
-
-void LLOutfitGalleryGearMenu::onTakeSnapshot()
-{
- LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);
- LLUUID selected_outfit_id = getSelectedOutfitID();
- if (gallery && !selected_outfit_id.isNull())
- {
- gallery->onTakeSnapshot(selected_outfit_id);
- }
-}
-
void LLOutfitGalleryGearMenu::onChangeSortOrder()
{
bool sort_by_name = !gSavedSettings.getBOOL("OutfitGallerySortByName");
@@ -1019,240 +1236,89 @@ void LLOutfitGallery::onTextureSelectionChanged(LLInventoryItem* itemp)
{
}
-void LLOutfitGallery::loadPhotos()
-{
- //Iterate over inventory
- mSnapshotFolderID = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE);
- LLViewerInventoryCategory* textures_category = gInventory.getCategory(mSnapshotFolderID);
- if (!textures_category)
- return;
- if (mTexturesObserver == NULL)
- {
- mTexturesObserver = new LLInventoryCategoriesObserver();
- gInventory.addObserver(mTexturesObserver);
- }
-
- // Start observing changes in "Textures" category.
- mTexturesObserver->addCategory(mSnapshotFolderID,
- boost::bind(&LLOutfitGallery::refreshTextures, this, mSnapshotFolderID));
-
- textures_category->fetch();
-}
-
-void LLOutfitGallery::updateSnapshotFolderObserver()
-{
- if(mSnapshotFolderID != gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE))
- {
- if (gInventory.containsObserver(mTexturesObserver))
- {
- gInventory.removeObserver(mTexturesObserver);
- }
- delete mTexturesObserver;
- mTexturesObserver = NULL;
- loadPhotos();
- }
-}
-
void LLOutfitGallery::refreshOutfit(const LLUUID& category_id)
{
LLViewerInventoryCategory* category = gInventory.getCategory(category_id);
if (category)
{
bool photo_loaded = false;
- LLInventoryModel::cat_array_t sub_cat_array;
- LLInventoryModel::item_array_t outfit_item_array;
- // Collect all sub-categories of a given category.
- gInventory.collectDescendents(
- category->getUUID(),
- sub_cat_array,
- outfit_item_array,
- LLInventoryModel::EXCLUDE_TRASH);
- BOOST_FOREACH(LLViewerInventoryItem* outfit_item, outfit_item_array)
+ LLUUID asset_id = category->getThumbnailUUID();
+ if (asset_id.isNull())
{
- LLViewerInventoryItem* linked_item = outfit_item->getLinkedItem();
- LLUUID asset_id, inv_id;
- std::string item_name;
- if (linked_item != NULL)
+ LLInventoryModel::cat_array_t sub_cat_array;
+ LLInventoryModel::item_array_t outfit_item_array;
+ // Collect all sub-categories of a given category.
+ gInventory.collectDescendents(
+ category->getUUID(),
+ sub_cat_array,
+ outfit_item_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+ BOOST_FOREACH(LLViewerInventoryItem* outfit_item, outfit_item_array)
{
- if (linked_item->getActualType() == LLAssetType::AT_TEXTURE)
+ LLViewerInventoryItem* linked_item = outfit_item->getLinkedItem();
+ LLUUID asset_id, inv_id;
+ std::string item_name;
+ if (linked_item != NULL)
{
- asset_id = linked_item->getAssetUUID();
- inv_id = linked_item->getUUID();
- item_name = linked_item->getName();
+ if (linked_item->getActualType() == LLAssetType::AT_TEXTURE)
+ {
+ asset_id = linked_item->getAssetUUID();
+ inv_id = linked_item->getUUID();
+ item_name = linked_item->getName();
+ }
}
- }
- else if (outfit_item->getActualType() == LLAssetType::AT_TEXTURE)
- {
- asset_id = outfit_item->getAssetUUID();
- inv_id = outfit_item->getUUID();
- item_name = outfit_item->getName();
- }
- if (asset_id.notNull())
- {
- photo_loaded |= mOutfitMap[category_id]->setImageAssetId(asset_id);
- // Rename links
- if (!mOutfitRenamePending.isNull() && mOutfitRenamePending.asString() == item_name)
+ else if (outfit_item->getActualType() == LLAssetType::AT_TEXTURE)
+ {
+ asset_id = outfit_item->getAssetUUID();
+ inv_id = outfit_item->getUUID();
+ item_name = outfit_item->getName();
+ }
+ if (category->getThumbnailUUID().notNull())
+ {
+ asset_id = category->getThumbnailUUID();
+ }
+ if (asset_id.notNull())
{
- LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(mOutfitRenamePending);
- LLStringUtil::format_map_t photo_string_args;
- photo_string_args["OUTFIT_NAME"] = outfit_cat->getName();
- std::string new_name = getString("outfit_photo_string", photo_string_args);
- LLSD updates;
- updates["name"] = new_name;
- update_inventory_item(inv_id, updates, NULL);
- mOutfitRenamePending.setNull();
- LLFloater* appearance_floater = LLFloaterReg::getInstance("appearance");
- if (appearance_floater)
+ photo_loaded |= mOutfitMap[category_id]->setImageAssetId(asset_id);
+ // Rename links
+ if (!mOutfitRenamePending.isNull() && mOutfitRenamePending.asString() == item_name)
+ {
+ LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(mOutfitRenamePending);
+ LLStringUtil::format_map_t photo_string_args;
+ photo_string_args["OUTFIT_NAME"] = outfit_cat->getName();
+ std::string new_name = getString("outfit_photo_string", photo_string_args);
+ LLSD updates;
+ updates["name"] = new_name;
+ update_inventory_item(inv_id, updates, NULL);
+ mOutfitRenamePending.setNull();
+ LLFloater* appearance_floater = LLFloaterReg::getInstance("appearance");
+ if (appearance_floater)
+ {
+ appearance_floater->setFocus(TRUE);
+ }
+ }
+ if (item_name == LLAppearanceMgr::sExpectedTextureName)
{
- appearance_floater->setFocus(TRUE);
+ // Images with "appropriate" name take priority
+ break;
}
}
- if (item_name == LLAppearanceMgr::sExpectedTextureName)
+ if (!photo_loaded)
{
- // Images with "appropriate" name take priority
- break;
+ mOutfitMap[category_id]->setDefaultImage();
}
}
- if (!photo_loaded)
- {
- mOutfitMap[category_id]->setDefaultImage();
- }
- }
- }
-
- if (mGalleryCreated && !LLApp::isExiting())
- {
- reArrangeRows();
- }
-}
-
-// Refresh linked textures from "textures" uploads folder
-void LLOutfitGallery::refreshTextures(const LLUUID& category_id)
-{
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
-
- // Collect all sub-categories of a given category.
- LLIsType is_texture(LLAssetType::AT_TEXTURE);
- gInventory.collectDescendentsIf(
- category_id,
- cat_array,
- item_array,
- LLInventoryModel::EXCLUDE_TRASH,
- is_texture);
-
- //Find texture which contain pending outfit ID string in name
- LLViewerInventoryItem* photo_upload_item = NULL;
- BOOST_FOREACH(LLViewerInventoryItem* item, item_array)
- {
- std::string name = item->getName();
- if (!mOutfitLinkPending.isNull() && name == mOutfitLinkPending.asString())
- {
- photo_upload_item = item;
- break;
- }
- }
-
- if (photo_upload_item != NULL)
- {
- LLUUID photo_item_id = photo_upload_item->getUUID();
- LLInventoryObject* upload_object = gInventory.getObject(photo_item_id);
- if (!upload_object)
- {
- LL_WARNS() << "LLOutfitGallery::refreshTextures added_object is null!" << LL_ENDL;
}
else
{
- linkPhotoToOutfit(photo_item_id, mOutfitLinkPending);
- mOutfitRenamePending = mOutfitLinkPending;
- mOutfitLinkPending.setNull();
+ mOutfitMap[category_id]->setImageAssetId(asset_id);
}
}
-}
-
-void LLOutfitGallery::uploadPhoto(LLUUID outfit_id)
-{
- outfit_map_t::iterator outfit_it = mOutfitMap.find(outfit_id);
- if (outfit_it == mOutfitMap.end() || outfit_it->first.isNull())
- {
- return;
- }
- (new LLFilePickerReplyThread(boost::bind(&LLOutfitGallery::uploadOutfitImage, this, _1, outfit_id), LLFilePicker::FFLOAD_IMAGE, false))->getFile();
-}
-
-void LLOutfitGallery::uploadOutfitImage(const std::vector<std::string>& filenames, LLUUID outfit_id)
-{
- std::string filename = filenames[0];
- LLLocalBitmap* unit = new LLLocalBitmap(filename);
- if (unit->getValid())
+
+ if (mGalleryCreated && !LLApp::isExiting())
{
- std::string exten = gDirUtilp->getExtension(filename);
- U32 codec = LLImageBase::getCodecFromExtension(exten);
-
- LLImageDimensionsInfo image_info;
- std::string image_load_error;
- if (!image_info.load(filename, codec))
- {
- image_load_error = image_info.getLastError();
- }
-
- S32 max_width = MAX_OUTFIT_PHOTO_WIDTH;
- S32 max_height = MAX_OUTFIT_PHOTO_HEIGHT;
-
- if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
- {
- LLStringUtil::format_map_t args;
- args["WIDTH"] = llformat("%d", max_width);
- args["HEIGHT"] = llformat("%d", max_height);
-
- image_load_error = LLTrans::getString("outfit_photo_load_dimensions_error", args);
- }
-
- if (!image_load_error.empty())
- {
- LLSD subst;
- subst["REASON"] = image_load_error;
- LLNotificationsUtil::add("OutfitPhotoLoadError", subst);
- return;
- }
-
- S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost();
- void *nruserdata = NULL;
- nruserdata = (void *)&outfit_id;
-
- LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(outfit_id);
- if (!outfit_cat) return;
- updateSnapshotFolderObserver();
- checkRemovePhoto(outfit_id);
- std::string upload_pending_name = outfit_id.asString();
- std::string upload_pending_desc = "";
- upload_new_resource(filename, // file
- upload_pending_name,
- upload_pending_desc,
- 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
- LLFloaterPerms::getNextOwnerPerms("Uploads"),
- LLFloaterPerms::getGroupPerms("Uploads"),
- LLFloaterPerms::getEveryonePerms("Uploads"),
- upload_pending_name, LLAssetStorage::LLStoreAssetCallback(), expected_upload_cost, nruserdata, false);
- mOutfitLinkPending = outfit_id;
+ reArrangeRows();
}
- delete unit;
-}
-
-void LLOutfitGallery::linkPhotoToOutfit(LLUUID photo_id, LLUUID outfit_id)
-{
- LLPointer<LLInventoryCallback> cb = new LLUpdateGalleryOnPhotoLinked();
- link_inventory_object(outfit_id, photo_id, cb);
-}
-
-bool LLOutfitGallery::checkRemovePhoto(LLUUID outfit_id)
-{
- LLAppearanceMgr::instance().removeOutfitPhoto(outfit_id);
- return true;
-}
-
-void LLUpdateGalleryOnPhotoLinked::fire(const LLUUID& inv_item_id)
-{
}
LLUUID LLOutfitGallery::getPhotoAssetId(const LLUUID& outfit_id)
@@ -1270,151 +1336,3 @@ LLUUID LLOutfitGallery::getDefaultPhoto()
return LLUUID();
}
-void LLOutfitGallery::onTexturePickerCommit(LLTextureCtrl::ETexturePickOp op, LLUUID id)
-{
- LLUUID selected_outfit_id = getSelectedOutfitUUID();
-
- if (selected_outfit_id.isNull())
- {
- return;
- }
-
- LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get();
-
- if (floaterp && op == LLTextureCtrl::TEXTURE_SELECT)
- {
- LLUUID image_item_id;
- if (id.notNull())
- {
- image_item_id = id;
- }
- else
- {
- image_item_id = floaterp->findItemID(floaterp->getAssetID(), FALSE, TRUE);
- if (image_item_id.isNull())
- {
- LL_WARNS() << "id or image_item_id is NULL!" << LL_ENDL;
- return;
- }
- }
-
- std::string image_load_error;
- S32 max_width = MAX_OUTFIT_PHOTO_WIDTH;
- S32 max_height = MAX_OUTFIT_PHOTO_HEIGHT;
- if (mTextureSelected.isNull() ||
- mTextureSelected->getFullWidth() == 0 ||
- mTextureSelected->getFullHeight() == 0)
- {
- image_load_error = LLTrans::getString("outfit_photo_verify_dimensions_error");
- LL_WARNS() << "Cannot verify selected texture dimensions" << LL_ENDL;
- return;
- }
- S32 width = mTextureSelected->getFullWidth();
- S32 height = mTextureSelected->getFullHeight();
- if ((width > max_width) || (height > max_height))
- {
- LLStringUtil::format_map_t args;
- args["WIDTH"] = llformat("%d", max_width);
- args["HEIGHT"] = llformat("%d", max_height);
-
- image_load_error = LLTrans::getString("outfit_photo_select_dimensions_error", args);
- }
-
- if (!image_load_error.empty())
- {
- LLSD subst;
- subst["REASON"] = image_load_error;
- LLNotificationsUtil::add("OutfitPhotoLoadError", subst);
- return;
- }
-
- checkRemovePhoto(selected_outfit_id);
- linkPhotoToOutfit(image_item_id, selected_outfit_id);
- }
-}
-
-void LLOutfitGallery::onSelectPhoto(LLUUID selected_outfit_id)
-{
- if (selected_outfit_id.notNull())
- {
-
- // show hourglass cursor when loading inventory window
- // because inventory construction is slooow
- getWindow()->setCursor(UI_CURSOR_WAIT);
- LLFloater* floaterp = mFloaterHandle.get();
-
- // Show the dialog
- if (floaterp)
- {
- floaterp->openFloater();
- }
- else
- {
- floaterp = new LLFloaterTexturePicker(
- this,
- getPhotoAssetId(selected_outfit_id),
- getPhotoAssetId(selected_outfit_id),
- getPhotoAssetId(selected_outfit_id),
- FALSE,
- TRUE,
- "SELECT PHOTO",
- PERM_NONE,
- PERM_NONE,
- PERM_NONE,
- FALSE,
- NULL);
-
- mFloaterHandle = floaterp->getHandle();
- mTextureSelected = NULL;
-
- LLFloaterTexturePicker* texture_floaterp = dynamic_cast<LLFloaterTexturePicker*>(floaterp);
- if (texture_floaterp)
- {
- texture_floaterp->setTextureSelectedCallback(boost::bind(&LLOutfitGallery::onTextureSelectionChanged, this, _1));
- texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLOutfitGallery::onTexturePickerCommit, this, _1, _2));
- texture_floaterp->setOnUpdateImageStatsCallback(boost::bind(&LLOutfitGallery::onTexturePickerUpdateImageStats, this, _1));
- texture_floaterp->setLocalTextureEnabled(FALSE);
- texture_floaterp->setBakeTextureEnabled(FALSE);
- texture_floaterp->setCanApply(false, true);
- }
-
- floaterp->openFloater();
- }
- floaterp->setFocus(TRUE);
- }
-}
-
-void LLOutfitGallery::onTakeSnapshot(LLUUID selected_outfit_id)
-{
- LLFloaterReg::toggleInstanceOrBringToFront("simple_outfit_snapshot");
- LLFloaterSimpleOutfitSnapshot* snapshot_floater = LLFloaterSimpleOutfitSnapshot::getInstance();
- if (snapshot_floater)
- {
- snapshot_floater->setOutfitID(selected_outfit_id);
- snapshot_floater->getInstance()->setGallery(this);
- }
-}
-
-void LLOutfitGallery::onBeforeOutfitSnapshotSave()
-{
- LLUUID selected_outfit_id = getSelectedOutfitUUID();
- if (!selected_outfit_id.isNull())
- {
- checkRemovePhoto(selected_outfit_id);
- updateSnapshotFolderObserver();
- }
-}
-
-void LLOutfitGallery::onAfterOutfitSnapshotSave()
-{
- LLUUID selected_outfit_id = getSelectedOutfitUUID();
- if (!selected_outfit_id.isNull())
- {
- mOutfitLinkPending = selected_outfit_id;
- }
-}
-
-void LLOutfitGallery::onTexturePickerUpdateImageStats(LLPointer<LLViewerTexture> texture)
-{
- mTextureSelected = texture;
-}
diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h
index ce5c090134..9915752962 100644
--- a/indra/newview/lloutfitgallery.h
+++ b/indra/newview/lloutfitgallery.h
@@ -33,7 +33,6 @@
#include "lllayoutstack.h"
#include "lloutfitslist.h"
#include "llpanelappearancetab.h"
-#include "lltexturectrl.h"
#include "llviewertexture.h"
#include <vector>
@@ -44,15 +43,6 @@ class LLOutfitListGearMenuBase;
class LLOutfitGalleryGearMenu;
class LLOutfitGalleryContextMenu;
-class LLUpdateGalleryOnPhotoLinked : public LLInventoryCallback
-{
-public:
- LLUpdateGalleryOnPhotoLinked(){}
- virtual ~LLUpdateGalleryOnPhotoLinked(){}
- /* virtual */ void fire(const LLUUID& inv_item_id);
-private:
-};
-
class LLOutfitGallery : public LLOutfitListBase
{
public:
@@ -83,10 +73,19 @@ public:
/*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& info);
- /*virtual*/ void draw();
-
- void onSelectPhoto(LLUUID selected_outfit_id);
- void onTakeSnapshot(LLUUID selected_outfit_id);
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
+ void moveUp();
+ void moveDown();
+ void moveLeft();
+ void moveRight();
+
+ /*virtual*/ void onFocusLost();
+ /*virtual*/ void onFocusReceived();
+
+ static void onRemoveOutfit(const LLUUID& outfit_cat_id);
+ static void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id);
+ void scrollToShowItem(const LLUUID& item_id);
void wearSelectedOutfit();
@@ -106,14 +105,8 @@ public:
void updateMessageVisibility();
bool hasDefaultImage(const LLUUID& outfit_cat_id);
- void refreshTextures(const LLUUID& category_id);
void refreshOutfit(const LLUUID& category_id);
- void onTexturePickerCommit(LLTextureCtrl::ETexturePickOp op, LLUUID id);
- void onTexturePickerUpdateImageStats(LLPointer<LLViewerTexture> texture);
- void onBeforeOutfitSnapshotSave();
- void onAfterOutfitSnapshotSave();
-
protected:
/*virtual*/ void onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id);
/*virtual*/ void onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid);
@@ -127,14 +120,8 @@ protected:
void applyFilter(LLOutfitGalleryItem* item, const std::string& filter_substring);
private:
- void loadPhotos();
- void uploadPhoto(LLUUID outfit_id);
- void uploadOutfitImage(const std::vector<std::string>& filenames, LLUUID outfit_id);
- void updateSnapshotFolderObserver();
LLUUID getPhotoAssetId(const LLUUID& outfit_id);
LLUUID getDefaultPhoto();
- void linkPhotoToOutfit(LLUUID outfit_id, LLUUID photo_id);
- bool checkRemovePhoto(LLUUID outfit_id);
void addToGallery(LLOutfitGalleryItem* item);
void removeFromGalleryLast(LLOutfitGalleryItem* item);
void removeFromGalleryMiddle(LLOutfitGalleryItem* item);
@@ -150,6 +137,7 @@ private:
void updateGalleryWidth();
LLOutfitGalleryItem* buildGalleryItem(std::string name, LLUUID outfit_id);
+ LLOutfitGalleryItem* getSelectedItem();
void onTextureSelectionChanged(LLInventoryItem* itemp);
@@ -190,17 +178,15 @@ private:
LLListContextMenu* mOutfitGalleryMenu;
- LLHandle<LLFloater> mFloaterHandle;
-
typedef std::map<LLUUID, LLOutfitGalleryItem*> outfit_map_t;
typedef outfit_map_t::value_type outfit_map_value_t;
outfit_map_t mOutfitMap;
- typedef std::map<LLOutfitGalleryItem*, int> item_num_map_t;
+ typedef std::map<LLOutfitGalleryItem*, S32> item_num_map_t;
typedef item_num_map_t::value_type item_numb_map_value_t;
item_num_map_t mItemIndexMap;
+ std::map<S32, LLOutfitGalleryItem*> mIndexToItemMap;
- LLInventoryCategoriesObserver* mTexturesObserver;
LLInventoryCategoriesObserver* mOutfitsObserver;
};
class LLOutfitGalleryContextMenu : public LLOutfitContextMenu
@@ -211,17 +197,13 @@ public:
LLOutfitGalleryContextMenu(LLOutfitListBase* outfit_list)
: LLOutfitContextMenu(outfit_list),
mOutfitList(outfit_list){}
+
protected:
/* virtual */ LLContextMenu* createMenu();
bool onEnable(LLSD::String param);
bool onVisible(LLSD::String param);
- void onUploadPhoto(const LLUUID& outfit_cat_id);
- void onSelectPhoto(const LLUUID& outfit_cat_id);
- void onRemovePhoto(const LLUUID& outfit_cat_id);
- void onTakeSnapshot(const LLUUID& outfit_cat_id);
+ void onThumbnail(const LLUUID& outfit_cat_id);
void onCreate(const LLSD& data);
- void onRemoveOutfit(const LLUUID& outfit_cat_id);
- void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id);
private:
LLOutfitListBase* mOutfitList;
};
@@ -236,10 +218,6 @@ public:
protected:
/*virtual*/ void onUpdateItemsVisibility();
private:
- /*virtual*/ void onUploadFoto();
- /*virtual*/ void onSelectPhoto();
- /*virtual*/ void onTakeSnapshot();
- /*virtual*/ void onRemovePhoto();
/*virtual*/ void onChangeSortOrder();
bool hasDefaultImage();
@@ -259,14 +237,21 @@ public:
/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
+ /*virtual*/ void onFocusLost();
+ /*virtual*/ void onFocusReceived();
+
+ bool openOutfitsContent();
+ void setGallery(LLOutfitGallery* gallery) { mGallery = gallery; }
void setDefaultImage();
bool setImageAssetId(LLUUID asset_id);
LLUUID getImageAssetId();
void setOutfitName(std::string name);
void setOutfitWorn(bool value);
void setSelected(bool value);
- void setUUID(LLUUID outfit_id) {mUUID = outfit_id;}
+ void setUUID(const LLUUID &outfit_id) {mUUID = outfit_id;}
+ LLUUID getUUID() const { return mUUID; }
std::string getItemName() {return mOutfitName;}
bool isDefaultImage() {return mDefaultImage;}
@@ -275,6 +260,7 @@ public:
void setHidden(bool hidden) {mHidden = hidden;}
private:
+ LLOutfitGallery* mGallery;
LLPointer<LLViewerFetchedTexture> mTexturep;
LLUUID mUUID;
LLUUID mImageAssetId;
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 4171fd8822..5c7792b0df 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -35,7 +35,7 @@
#include "llaccordionctrltab.h"
#include "llagentwearables.h"
#include "llappearancemgr.h"
-#include "llagentbenefits.h"
+#include "llfloaterreg.h"
#include "llfloatersidepanelcontainer.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
@@ -122,9 +122,8 @@ void LLOutfitsList::onOpen(const LLSD& info)
{
if (!mIsInitialized)
{
- const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
// Start observing changes in Current Outfit category.
- mCategoriesObserver->addCategory(cof, boost::bind(&LLOutfitsList::onCOFChanged, this));
+ LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLOutfitsList::onCOFChanged, this));
}
LLOutfitListBase::onOpen(info);
@@ -1112,10 +1111,7 @@ LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist)
registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenuBase::onAdd, this));
- registrar.add("Gear.UploadPhoto", boost::bind(&LLOutfitListGearMenuBase::onUploadFoto, this));
- registrar.add("Gear.SelectPhoto", boost::bind(&LLOutfitListGearMenuBase::onSelectPhoto, this));
- registrar.add("Gear.TakeSnapshot", boost::bind(&LLOutfitListGearMenuBase::onTakeSnapshot, this));
- registrar.add("Gear.RemovePhoto", boost::bind(&LLOutfitListGearMenuBase::onRemovePhoto, this));
+ registrar.add("Gear.Thumbnail", boost::bind(&LLOutfitListGearMenuBase::onThumbnail, this));
registrar.add("Gear.SortByName", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this));
enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenuBase::onEnable, this, _2));
@@ -1232,7 +1228,6 @@ bool LLOutfitListGearMenuBase::onEnable(LLSD::String param)
bool LLOutfitListGearMenuBase::onVisible(LLSD::String param)
{
- getMenu()->getChild<LLUICtrl>("upload_photo")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost()));
const LLUUID& selected_outfit_id = getSelectedOutfitID();
if (selected_outfit_id.isNull()) // no selection or invalid outfit selected
{
@@ -1251,24 +1246,11 @@ bool LLOutfitListGearMenuBase::onVisible(LLSD::String param)
return true;
}
-void LLOutfitListGearMenuBase::onUploadFoto()
+void LLOutfitListGearMenuBase::onThumbnail()
{
-
-}
-
-void LLOutfitListGearMenuBase::onSelectPhoto()
-{
-
-}
-
-void LLOutfitListGearMenuBase::onTakeSnapshot()
-{
-
-}
-
-void LLOutfitListGearMenuBase::onRemovePhoto()
-{
-
+ const LLUUID& selected_outfit_id = getSelectedOutfitID();
+ LLSD data(selected_outfit_id);
+ LLFloaterReg::showInstance("change_item_thumbnail", data);
}
void LLOutfitListGearMenuBase::onChangeSortOrder()
@@ -1288,10 +1270,7 @@ void LLOutfitListGearMenu::onUpdateItemsVisibility()
if (!mMenu) return;
mMenu->setItemVisible("expand", TRUE);
mMenu->setItemVisible("collapse", TRUE);
- mMenu->setItemVisible("upload_photo", FALSE);
- mMenu->setItemVisible("select_photo", FALSE);
- mMenu->setItemVisible("take_snapshot", FALSE);
- mMenu->setItemVisible("remove_photo", FALSE);
+ mMenu->setItemVisible("thumbnail", FALSE); // Never visible?
mMenu->setItemVisible("sepatator3", FALSE);
mMenu->setItemVisible("sort_folders_by_name", FALSE);
LLOutfitListGearMenuBase::onUpdateItemsVisibility();
diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h
index 81be8de94f..66b3165169 100644
--- a/indra/newview/lloutfitslist.h
+++ b/indra/newview/lloutfitslist.h
@@ -163,10 +163,7 @@ public:
protected:
virtual void onUpdateItemsVisibility();
- virtual void onUploadFoto();
- virtual void onSelectPhoto();
- virtual void onTakeSnapshot();
- virtual void onRemovePhoto();
+ virtual void onThumbnail();
virtual void onChangeSortOrder();
const LLUUID& getSelectedOutfitID();
diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp
index 834e664723..cc3c51dd83 100644
--- a/indra/newview/llpanellandmarkinfo.cpp
+++ b/indra/newview/llpanellandmarkinfo.cpp
@@ -111,9 +111,9 @@ void LLPanelLandmarkInfo::setInfoType(EInfoType type)
}
// Sets CREATE_LANDMARK infotype and creates landmark at desired folder
-void LLPanelLandmarkInfo::setInfoAndCreateLandmark(const LLUUID& fodler_id)
+void LLPanelLandmarkInfo::setInfoAndCreateLandmark(const LLUUID& folder_id)
{
- setInfoType(CREATE_LANDMARK, fodler_id);
+ setInfoType(CREATE_LANDMARK, folder_id);
}
void LLPanelLandmarkInfo::setInfoType(EInfoType type, const LLUUID &folder_id)
diff --git a/indra/newview/llpanellandmarkinfo.h b/indra/newview/llpanellandmarkinfo.h
index 46e2a1935b..8802ce066e 100644
--- a/indra/newview/llpanellandmarkinfo.h
+++ b/indra/newview/llpanellandmarkinfo.h
@@ -48,7 +48,7 @@ public:
/*virtual*/ void setInfoType(EInfoType type);
// Sets CREATE_LANDMARK infotype and creates landmark at desired folder
- void setInfoAndCreateLandmark(const LLUUID& fodler_id);
+ void setInfoAndCreateLandmark(const LLUUID& folder_id);
/*virtual*/ void processParcelInfo(const LLParcelData& parcel_data);
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index 8f1e57e44c..49756a4e09 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -1110,11 +1110,6 @@ void LLPanelLogin::onRememberPasswordCheck(void*)
std::string grid(LLGridManager::getInstance()->getGridId());
std::string user_id(cred->userID());
- if (!remember_password)
- {
- gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id);
- gSecAPIHandler->syncProtectedMap();
- }
}
}
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index 744d49ff57..30f301027c 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -37,8 +37,8 @@
#include "llfilepicker.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
+#include "llinventorygallery.h"
#include "llinventorymodelbackgroundfetch.h"
-#include "llinventorypanel.h"
#include "llfiltereditor.h"
#include "llfloatersidepanelcontainer.h"
#include "llfloaterreg.h"
@@ -53,12 +53,14 @@
#include "llspinctrl.h"
#include "lltoggleablemenu.h"
#include "lltooldraganddrop.h"
+#include "lltrans.h"
#include "llviewermenu.h"
#include "llviewertexturelist.h"
#include "llsidepanelinventory.h"
#include "llfolderview.h"
#include "llradiogroup.h"
#include "llenvironment.h"
+#include "llweb.h"
const std::string FILTERS_FILENAME("filters.xml");
@@ -113,7 +115,13 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p)
mMenuGearDefault(NULL),
mMenuVisibility(NULL),
mMenuAddHandle(),
- mNeedUploadCost(true)
+ mNeedUploadCost(true),
+ mMenuViewDefault(NULL),
+ mSingleFolderMode(false),
+ mForceShowInvLayout(false),
+ mViewMode(MODE_COMBINATION),
+ mListViewRootUpdatedConnection(),
+ mGalleryRootUpdatedConnection()
{
// Menu Callbacks (non contex menus)
mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelMainInventory::doToSelected, this, _2));
@@ -124,7 +132,6 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p)
mCommitCallbackRegistrar.add("Inventory.ShowFilters", boost::bind(&LLPanelMainInventory::toggleFindOptions, this));
mCommitCallbackRegistrar.add("Inventory.ResetFilters", boost::bind(&LLPanelMainInventory::resetFilters, this));
mCommitCallbackRegistrar.add("Inventory.SetSortBy", boost::bind(&LLPanelMainInventory::setSortBy, this, _2));
- mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this));
mEnableCallbackRegistrar.add("Inventory.EnvironmentEnabled", [](LLUICtrl *, const LLSD &) { return LLPanelMainInventory::hasSettingsInventory(); });
@@ -192,7 +199,7 @@ BOOL LLPanelMainInventory::postBuild()
}
// Now load the stored settings from disk, if available.
std::string filterSaveName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, FILTERS_FILENAME));
- LL_INFOS() << "LLPanelMainInventory::init: reading from " << filterSaveName << LL_ENDL;
+ LL_INFOS("Inventory") << "LLPanelMainInventory::init: reading from " << filterSaveName << LL_ENDL;
llifstream file(filterSaveName.c_str());
LLSD savedFilterState;
if (file.is_open())
@@ -237,6 +244,34 @@ BOOL LLPanelMainInventory::postBuild()
mGearMenuButton = getChild<LLMenuButton>("options_gear_btn");
mVisibilityMenuButton = getChild<LLMenuButton>("options_visibility_btn");
+ mViewMenuButton = getChild<LLMenuButton>("view_btn");
+
+ mBackBtn = getChild<LLButton>("back_btn");
+ mForwardBtn = getChild<LLButton>("forward_btn");
+ mUpBtn = getChild<LLButton>("up_btn");
+ mViewModeBtn = getChild<LLButton>("view_mode_btn");
+ mNavigationBtnsPanel = getChild<LLLayoutPanel>("nav_buttons");
+
+ mDefaultViewPanel = getChild<LLPanel>("default_inventory_panel");
+ mCombinationViewPanel = getChild<LLPanel>("combination_view_inventory");
+ mCombinationGalleryLayoutPanel = getChild<LLLayoutPanel>("comb_gallery_layout");
+ mCombinationListLayoutPanel = getChild<LLLayoutPanel>("comb_inventory_layout");
+ mCombinationLayoutStack = getChild<LLLayoutStack>("combination_view_stack");
+
+ mCombinationInventoryPanel = getChild<LLInventorySingleFolderPanel>("comb_single_folder_inv");
+ LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter();
+ comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_EXCLUDE_THUMBNAILS);
+ comb_inv_filter.markDefault();
+ mCombinationInventoryPanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onCombinationInventorySelectionChanged, this, _1, _2));
+ mListViewRootUpdatedConnection = mCombinationInventoryPanel->setRootChangedCallback(boost::bind(&LLPanelMainInventory::onCombinationRootChanged, this, false));
+
+ mCombinationGalleryPanel = getChild<LLInventoryGallery>("comb_gallery_view_inv");
+ mCombinationGalleryPanel->setSortOrder(mCombinationInventoryPanel->getSortOrder());
+ LLInventoryFilter& comb_gallery_filter = mCombinationGalleryPanel->getFilter();
+ comb_gallery_filter.setFilterThumbnails(LLInventoryFilter::FILTER_ONLY_THUMBNAILS);
+ comb_gallery_filter.markDefault();
+ mGalleryRootUpdatedConnection = mCombinationGalleryPanel->setRootChangedCallback(boost::bind(&LLPanelMainInventory::onCombinationRootChanged, this, true));
+ mCombinationGalleryPanel->setSelectionChangeCallback(boost::bind(&LLPanelMainInventory::onCombinationGallerySelectionChanged, this, _1));
initListCommandsHandlers();
@@ -308,12 +343,21 @@ LLPanelMainInventory::~LLPanelMainInventory( void )
gInventory.removeObserver(this);
delete mSavedFolderState;
- auto menu = mMenuAddHandle.get();
- if(menu)
- {
- menu->die();
- mMenuAddHandle.markDead();
- }
+ auto menu = mMenuAddHandle.get();
+ if(menu)
+ {
+ menu->die();
+ mMenuAddHandle.markDead();
+ }
+
+ if (mListViewRootUpdatedConnection.connected())
+ {
+ mListViewRootUpdatedConnection.disconnect();
+ }
+ if (mGalleryRootUpdatedConnection.connected())
+ {
+ mGalleryRootUpdatedConnection.disconnect();
+ }
}
LLInventoryPanel* LLPanelMainInventory::getAllItemsPanel()
@@ -362,6 +406,10 @@ BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask)
{
startSearch();
}
+ if(mSingleFolderMode && key == KEY_LEFT)
+ {
+ onBackFolderClicked();
+ }
}
return LLPanel::handleKeyHere(key, mask);
@@ -381,27 +429,145 @@ void LLPanelMainInventory::closeAllFolders()
getPanel()->getRootFolder()->closeAllFolders();
}
-void LLPanelMainInventory::newWindow()
+S32 get_instance_num()
+{
+ static S32 instance_num = 0;
+ instance_num = (instance_num + 1) % S32_MAX;
+
+ return instance_num;
+}
+
+LLFloaterSidePanelContainer* LLPanelMainInventory::newWindow()
{
- static S32 instance_num = 0;
- instance_num = (instance_num + 1) % S32_MAX;
+ S32 instance_num = get_instance_num();
if (!gAgentCamera.cameraMouselook())
{
- LLFloaterReg::showTypedInstance<LLFloaterSidePanelContainer>("inventory", LLSD(instance_num));
+ LLFloaterSidePanelContainer* floater = LLFloaterReg::showTypedInstance<LLFloaterSidePanelContainer>("inventory", LLSD(instance_num));
+ LLSidepanelInventory* sidepanel_inventory = floater->findChild<LLSidepanelInventory>("main_panel");
+ sidepanel_inventory->initInventoryViews();
+ return floater;
}
+ return NULL;
+}
+
+//static
+void LLPanelMainInventory::newFolderWindow(LLUUID folder_id, LLUUID item_to_select)
+{
+ LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory");
+ for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end();)
+ {
+ LLFloaterSidePanelContainer* inventory_container = dynamic_cast<LLFloaterSidePanelContainer*>(*iter++);
+ if (inventory_container)
+ {
+ LLSidepanelInventory* sidepanel_inventory = dynamic_cast<LLSidepanelInventory*>(inventory_container->findChild<LLPanel>("main_panel", true));
+ if (sidepanel_inventory)
+ {
+ LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();
+ if (main_inventory && main_inventory->isSingleFolderMode()
+ && (main_inventory->getCurrentSFVRoot() == folder_id))
+ {
+ main_inventory->setFocus(true);
+ if(item_to_select.notNull())
+ {
+ main_inventory->setGallerySelection(item_to_select);
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ S32 instance_num = get_instance_num();
+
+ LLFloaterSidePanelContainer* inventory_container = LLFloaterReg::showTypedInstance<LLFloaterSidePanelContainer>("inventory", LLSD(instance_num));
+ if(inventory_container)
+ {
+ LLSidepanelInventory* sidepanel_inventory = dynamic_cast<LLSidepanelInventory*>(inventory_container->findChild<LLPanel>("main_panel", true));
+ if (sidepanel_inventory)
+ {
+ LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();
+ if (main_inventory)
+ {
+ main_inventory->initSingleFolderRoot(folder_id);
+ main_inventory->toggleViewMode();
+ if(folder_id.notNull())
+ {
+ if(item_to_select.notNull())
+ {
+ main_inventory->setGallerySelection(item_to_select, true);
+ }
+ }
+ }
+ }
+ }
}
void LLPanelMainInventory::doCreate(const LLSD& userdata)
{
reset_inventory_filter();
- menu_create_inventory_item(getPanel(), NULL, userdata);
+ if(mSingleFolderMode)
+ {
+ if(isListViewMode() || isCombinationViewMode())
+ {
+ LLFolderViewItem* current_folder = getActivePanel()->getRootFolder();
+ if (current_folder)
+ {
+ if(isCombinationViewMode())
+ {
+ mForceShowInvLayout = true;
+ }
+
+ LLHandle<LLPanel> handle = getHandle();
+ std::function<void(const LLUUID&)> callback_created = [handle](const LLUUID& new_id)
+ {
+ gInventory.notifyObservers(); // not really needed, should have been already done
+ LLPanelMainInventory* panel = (LLPanelMainInventory*)handle.get();
+ if (new_id.notNull() && panel)
+ {
+ // might need to refresh visibility, delay rename
+ panel->mCombInvUUIDNeedsRename = new_id;
+
+ if (panel->isCombinationViewMode())
+ {
+ panel->mForceShowInvLayout = true;
+ }
+
+ LL_DEBUGS("Inventory") << "Done creating inventory: " << new_id << LL_ENDL;
+ }
+ };
+ menu_create_inventory_item(NULL, getCurrentSFVRoot(), userdata, LLUUID::null, callback_created);
+ }
+ }
+ else
+ {
+ LLHandle<LLPanel> handle = getHandle();
+ std::function<void(const LLUUID&)> callback_created = [handle](const LLUUID &new_id)
+ {
+ gInventory.notifyObservers(); // not really needed, should have been already done
+ if (new_id.notNull())
+ {
+ LLPanelMainInventory* panel = (LLPanelMainInventory*)handle.get();
+ if (panel)
+ {
+ panel->setGallerySelection(new_id);
+ LL_DEBUGS("Inventory") << "Done creating inventory: " << new_id << LL_ENDL;
+ }
+ }
+ };
+ menu_create_inventory_item(NULL, getCurrentSFVRoot(), userdata, LLUUID::null, callback_created);
+ }
+ }
+ else
+ {
+ menu_create_inventory_item(getPanel(), NULL, userdata);
+ }
}
void LLPanelMainInventory::resetFilters()
{
LLFloaterInventoryFinder *finder = getFinder();
- getActivePanel()->getFilter().resetDefault();
+ getCurrentFilter().resetDefault();
if (finder)
{
finder->updateElementsFromFilter();
@@ -422,6 +588,17 @@ void LLPanelMainInventory::resetAllItemsFilters()
setFilterTextFromFilter();
}
+void LLPanelMainInventory::findLinks(const LLUUID& item_id, const std::string& item_name)
+{
+ mFilterSubString = item_name;
+
+ LLInventoryFilter &filter = mActivePanel->getFilter();
+ filter.setFindAllLinksMode(item_name, item_id);
+
+ mFilterEditor->setText(item_name);
+ mFilterEditor->setFocus(TRUE);
+}
+
void LLPanelMainInventory::setSortBy(const LLSD& userdata)
{
U32 sort_order_mask = getActivePanel()->getSortOrder();
@@ -456,6 +633,10 @@ void LLPanelMainInventory::setSortBy(const LLSD& userdata)
sort_order_mask |= LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
}
}
+ if(mSingleFolderMode && !isListViewMode())
+ {
+ mCombinationGalleryPanel->setSortOrder(sort_order_mask, true);
+ }
getActivePanel()->setSortOrder(sort_order_mask);
if (isRecentItemsPanelSelected())
@@ -473,25 +654,56 @@ void LLPanelMainInventory::onSelectSearchType()
std::string new_type = mSearchTypeCombo->getValue();
if (new_type == "search_by_name")
{
- getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_NAME);
+ setSearchType(LLInventoryFilter::SEARCHTYPE_NAME);
}
if (new_type == "search_by_creator")
{
- getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_CREATOR);
+ setSearchType(LLInventoryFilter::SEARCHTYPE_CREATOR);
}
if (new_type == "search_by_description")
{
- getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_DESCRIPTION);
+ setSearchType(LLInventoryFilter::SEARCHTYPE_DESCRIPTION);
}
if (new_type == "search_by_UUID")
{
- getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_UUID);
+ setSearchType(LLInventoryFilter::SEARCHTYPE_UUID);
}
}
+void LLPanelMainInventory::setSearchType(LLInventoryFilter::ESearchType type)
+{
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ mCombinationGalleryPanel->setSearchType(type);
+ }
+ if(mSingleFolderMode && isCombinationViewMode())
+ {
+ mCombinationInventoryPanel->setSearchType(type);
+ mCombinationGalleryPanel->setSearchType(type);
+ }
+ else
+ {
+ getActivePanel()->setSearchType(type);
+ }
+}
+
void LLPanelMainInventory::updateSearchTypeCombo()
{
- LLInventoryFilter::ESearchType search_type = getActivePanel()->getSearchType();
+ LLInventoryFilter::ESearchType search_type(LLInventoryFilter::SEARCHTYPE_NAME);
+
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ search_type = mCombinationGalleryPanel->getSearchType();
+ }
+ else if(mSingleFolderMode && isCombinationViewMode())
+ {
+ search_type = mCombinationGalleryPanel->getSearchType();
+ }
+ else
+ {
+ search_type = getActivePanel()->getSearchType();
+ }
+
switch(search_type)
{
case LLInventoryFilter::SEARCHTYPE_CREATOR:
@@ -537,7 +749,7 @@ void LLPanelMainInventory::onClearSearch()
}
// re-open folders that were initially open in case filter was active
- if (mActivePanel && (mFilterSubString.size() || initially_active))
+ if (mActivePanel && (mFilterSubString.size() || initially_active) && !mSingleFolderMode)
{
mSavedFolderState->setApply(TRUE);
mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
@@ -547,7 +759,7 @@ void LLPanelMainInventory::onClearSearch()
}
mFilterSubString = "";
- LLSidepanelInventory * sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory");
+ LLSidepanelInventory * sidepanel_inventory = getParentSidepanelInventory();
if (sidepanel_inventory)
{
LLPanelMarketplaceInbox* inbox_panel = sidepanel_inventory->getChild<LLPanelMarketplaceInbox>("marketplace_inbox");
@@ -560,16 +772,32 @@ void LLPanelMainInventory::onClearSearch()
void LLPanelMainInventory::onFilterEdit(const std::string& search_string )
{
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ mFilterSubString = search_string;
+ mCombinationGalleryPanel->setFilterSubString(mFilterSubString);
+ return;
+ }
+ if(mSingleFolderMode && isCombinationViewMode())
+ {
+ mCombinationGalleryPanel->setFilterSubString(search_string);
+ }
+
if (search_string == "")
{
onClearSearch();
}
+
if (!mActivePanel)
{
return;
}
- LLInventoryModelBackgroundFetch::instance().start();
+ if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
+ {
+ llassert(false); // this should have been done on startup
+ LLInventoryModelBackgroundFetch::instance().start();
+ }
mFilterSubString = search_string;
if (mActivePanel->getFilterSubString().empty() && mFilterSubString.empty())
@@ -588,7 +816,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string )
// set new filter string
setFilterSubString(mFilterSubString);
- LLSidepanelInventory * sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory");
+ LLSidepanelInventory * sidepanel_inventory = getParentSidepanelInventory();
if (sidepanel_inventory)
{
LLPanelMarketplaceInbox* inbox_panel = sidepanel_inventory->getChild<LLPanelMarketplaceInbox>("marketplace_inbox");
@@ -643,7 +871,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string )
void LLPanelMainInventory::onFilterSelected()
{
// Find my index
- mActivePanel = (LLInventoryPanel*)getChild<LLTabContainer>("inventory filter tabs")->getCurrentPanel();
+ setActivePanel();
if (!mActivePanel)
{
@@ -656,15 +884,19 @@ void LLPanelMainInventory::onFilterSelected()
}
updateSearchTypeCombo();
setFilterSubString(mFilterSubString);
- LLInventoryFilter& filter = mActivePanel->getFilter();
+ LLInventoryFilter& filter = getCurrentFilter();
LLFloaterInventoryFinder *finder = getFinder();
if (finder)
{
finder->changeFilter(&filter);
+ if (mSingleFolderMode)
+ {
+ finder->setTitle(getLocalizedRootName());
+ }
}
- if (filter.isActive())
+ if (filter.isActive() && !LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
{
- // If our filter is active we may be the first thing requiring a fetch so we better start it here.
+ llassert(false); // this should have been done on startup
LLInventoryModelBackgroundFetch::instance().start();
}
setFilterTextFromFilter();
@@ -736,6 +968,7 @@ void LLPanelMainInventory::draw()
}
LLPanel::draw();
updateItemcountText();
+ updateCombinationVisibility();
}
void LLPanelMainInventory::updateItemcountText()
@@ -775,6 +1008,21 @@ void LLPanelMainInventory::updateItemcountText()
{
text = getString("ItemcountUnknown", string_args);
}
+
+ if (mSingleFolderMode)
+ {
+ LLInventoryModel::cat_array_t *cats;
+ LLInventoryModel::item_array_t *items;
+
+ gInventory.getDirectDescendentsOf(getCurrentSFVRoot(), cats, items);
+
+ if (items && cats)
+ {
+ string_args["[ITEM_COUNT]"] = llformat("%d", items->size());
+ string_args["[CATEGORY_COUNT]"] = llformat("%d", cats->size());
+ text = getString("ItemcountCompleted", string_args);
+ }
+ }
mCounterCtrl->setValue(text);
mCounterCtrl->setToolTip(text);
@@ -794,7 +1042,7 @@ void LLPanelMainInventory::onFocusReceived()
void LLPanelMainInventory::setFilterTextFromFilter()
{
- mFilterText = mActivePanel->getFilter().getFilterText();
+ mFilterText = getCurrentFilter().getFilterText();
}
void LLPanelMainInventory::toggleFindOptions()
@@ -809,8 +1057,17 @@ void LLPanelMainInventory::toggleFindOptions()
LLFloater* parent_floater = gFloaterView->getParentFloater(this);
if (parent_floater)
parent_floater->addDependentFloater(mFinderHandle);
- // start background fetch of folders
- LLInventoryModelBackgroundFetch::instance().start();
+
+ if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
+ {
+ llassert(false); // this should have been done on startup
+ LLInventoryModelBackgroundFetch::instance().start();
+ }
+
+ if (mSingleFolderMode)
+ {
+ finder->setTitle(getLocalizedRootName());
+ }
}
else
{
@@ -1043,10 +1300,27 @@ void LLFloaterInventoryFinder::draw()
filter &= ~(0x1 << LLInventoryType::IT_CATEGORY);
}
- // update the panel, panel will update the filter
- mPanelMainInventory->getPanel()->setShowFolderState(getCheckShowEmpty() ?
- LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
- mPanelMainInventory->getPanel()->setFilterTypes(filter);
+
+ bool is_sf_mode = mPanelMainInventory->isSingleFolderMode();
+ if(is_sf_mode && mPanelMainInventory->isGalleryViewMode())
+ {
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setShowFolderState(getCheckShowEmpty() ?
+ LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setFilterObjectTypes(filter);
+ }
+ else
+ {
+ if(is_sf_mode && mPanelMainInventory->isCombinationViewMode())
+ {
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setShowFolderState(getCheckShowEmpty() ?
+ LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setFilterObjectTypes(filter);
+ }
+ // update the panel, panel will update the filter
+ mPanelMainInventory->getPanel()->setShowFolderState(getCheckShowEmpty() ?
+ LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ mPanelMainInventory->getPanel()->setFilterTypes(filter);
+ }
if (getCheckSinceLogoff())
{
@@ -1068,10 +1342,26 @@ void LLFloaterInventoryFinder::draw()
}
hours += days * 24;
- mPanelMainInventory->getPanel()->setHoursAgo(hours);
- mPanelMainInventory->getPanel()->setSinceLogoff(getCheckSinceLogoff());
+
mPanelMainInventory->setFilterTextFromFilter();
- mPanelMainInventory->getPanel()->setDateSearchDirection(getDateSearchDirection());
+ if(is_sf_mode && mPanelMainInventory->isGalleryViewMode())
+ {
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setHoursAgo(hours);
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateRangeLastLogoff(getCheckSinceLogoff());
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateSearchDirection(getDateSearchDirection());
+ }
+ else
+ {
+ if(is_sf_mode && mPanelMainInventory->isCombinationViewMode())
+ {
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setHoursAgo(hours);
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateRangeLastLogoff(getCheckSinceLogoff());
+ mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateSearchDirection(getDateSearchDirection());
+ }
+ mPanelMainInventory->getPanel()->setHoursAgo(hours);
+ mPanelMainInventory->getPanel()->setSinceLogoff(getCheckSinceLogoff());
+ mPanelMainInventory->getPanel()->setDateSearchDirection(getDateSearchDirection());
+ }
LLPanel::draw();
}
@@ -1083,15 +1373,15 @@ void LLFloaterInventoryFinder::onCreatorSelfFilterCommit()
if(show_creator_self && show_creator_other)
{
- mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL);
+ mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL);
}
else if(show_creator_self)
{
- mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF);
+ mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF);
}
else if(!show_creator_self || !show_creator_other)
{
- mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS);
+ mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS);
mCreatorOthers->set(TRUE);
}
}
@@ -1103,15 +1393,15 @@ void LLFloaterInventoryFinder::onCreatorOtherFilterCommit()
if(show_creator_self && show_creator_other)
{
- mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL);
+ mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL);
}
else if(show_creator_other)
{
- mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS);
+ mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS);
}
else if(!show_creator_other || !show_creator_self)
{
- mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF);
+ mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF);
mCreatorSelf->set(TRUE);
}
}
@@ -1182,26 +1472,25 @@ void LLFloaterInventoryFinder::selectNoTypes(void* user_data)
void LLPanelMainInventory::initListCommandsHandlers()
{
- childSetAction("trash_btn", boost::bind(&LLPanelMainInventory::onTrashButtonClick, this));
childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddButtonClick, this));
-
- mTrashButton = getChild<LLDragAndDropButton>("trash_btn");
- mTrashButton->setDragAndDropHandler(boost::bind(&LLPanelMainInventory::handleDragAndDropToTrash, this
- , _4 // BOOL drop
- , _5 // EDragAndDropType cargo_type
- , _7 // EAcceptance* accept
- ));
+ childSetAction("view_mode_btn", boost::bind(&LLPanelMainInventory::onViewModeClick, this));
+ childSetAction("up_btn", boost::bind(&LLPanelMainInventory::onUpFolderClicked, this));
+ childSetAction("back_btn", boost::bind(&LLPanelMainInventory::onBackFolderClicked, this));
+ childSetAction("forward_btn", boost::bind(&LLPanelMainInventory::onForwardFolderClicked, this));
mCommitCallbackRegistrar.add("Inventory.GearDefault.Custom.Action", boost::bind(&LLPanelMainInventory::onCustomAction, this, _2));
mEnableCallbackRegistrar.add("Inventory.GearDefault.Check", boost::bind(&LLPanelMainInventory::isActionChecked, this, _2));
mEnableCallbackRegistrar.add("Inventory.GearDefault.Enable", boost::bind(&LLPanelMainInventory::isActionEnabled, this, _2));
+ mEnableCallbackRegistrar.add("Inventory.GearDefault.Visible", boost::bind(&LLPanelMainInventory::isActionVisible, this, _2));
mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_inventory_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
- mGearMenuButton->setMenu(mMenuGearDefault, LLMenuButton::MP_TOP_LEFT, true);
+ mGearMenuButton->setMenu(mMenuGearDefault, LLMenuButton::MP_BOTTOM_LEFT, true);
+ mMenuViewDefault = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_inventory_view_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ mViewMenuButton->setMenu(mMenuViewDefault, LLMenuButton::MP_BOTTOM_LEFT, true);
LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory_add.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
mMenuAddHandle = menu->getHandle();
mMenuVisibility = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_inventory_search_visibility.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
- mVisibilityMenuButton->setMenu(mMenuVisibility, LLMenuButton::MP_BOTTOM_LEFT, true);
+ mVisibilityMenuButton->setMenu(mMenuVisibility, LLMenuButton::MP_BOTTOM_LEFT, true);
// Update the trash button when selected item(s) get worn or taken off.
LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLPanelMainInventory::updateListCommands, this));
@@ -1209,9 +1498,6 @@ void LLPanelMainInventory::initListCommandsHandlers()
void LLPanelMainInventory::updateListCommands()
{
- bool trash_enabled = isActionEnabled("delete");
-
- mTrashButton->setEnabled(trash_enabled);
}
void LLPanelMainInventory::onAddButtonClick()
@@ -1222,7 +1508,7 @@ void LLPanelMainInventory::onAddButtonClick()
LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get();
if (menu)
{
- menu->getChild<LLMenuItemGL>("New Folder")->setEnabled(!isRecentItemsPanelSelected());
+ disableAddIfNeeded();
setUploadCostIfNeeded();
@@ -1230,6 +1516,215 @@ void LLPanelMainInventory::onAddButtonClick()
}
}
+void LLPanelMainInventory::setActivePanel()
+{
+ // Todo: should cover gallery mode in some way
+ if(mSingleFolderMode && isListViewMode())
+ {
+ mActivePanel = getChild<LLInventoryPanel>("comb_single_folder_inv");
+ }
+ else if(mSingleFolderMode && isCombinationViewMode())
+ {
+ mActivePanel = getChild<LLInventoryPanel>("comb_single_folder_inv");
+ }
+ else
+ {
+ mActivePanel = (LLInventoryPanel*)getChild<LLTabContainer>("inventory filter tabs")->getCurrentPanel();
+ }
+ mViewModeBtn->setEnabled(mSingleFolderMode || (getAllItemsPanel() == getActivePanel()));
+}
+
+void LLPanelMainInventory::initSingleFolderRoot(const LLUUID& start_folder_id)
+{
+ mCombinationInventoryPanel->initFolderRoot(start_folder_id);
+}
+
+void LLPanelMainInventory::initInventoryViews()
+{
+ LLInventoryPanel* all_item = getChild<LLInventoryPanel>(ALL_ITEMS);
+ all_item->initializeViewBuilding();
+ LLInventoryPanel* recent_item = getChild<LLInventoryPanel>(RECENT_ITEMS);
+ recent_item->initializeViewBuilding();
+ LLInventoryPanel* worn_item = getChild<LLInventoryPanel>(WORN_ITEMS);
+ worn_item->initializeViewBuilding();
+}
+
+void LLPanelMainInventory::toggleViewMode()
+{
+ if(mSingleFolderMode && isCombinationViewMode())
+ {
+ mCombinationInventoryPanel->getRootFolder()->setForceArrange(false);
+ }
+
+ mSingleFolderMode = !mSingleFolderMode;
+ mReshapeInvLayout = true;
+
+ if (mCombinationGalleryPanel->getRootFolder().isNull())
+ {
+ mCombinationGalleryPanel->setRootFolder(mCombinationInventoryPanel->getSingleFolderRoot());
+ mCombinationGalleryPanel->updateRootFolder();
+ }
+
+ updatePanelVisibility();
+ setActivePanel();
+ updateTitle();
+ onFilterSelected();
+
+ LLSidepanelInventory* sidepanel_inventory = getParentSidepanelInventory();
+ if (sidepanel_inventory)
+ {
+ if(mSingleFolderMode)
+ {
+ sidepanel_inventory->hideInbox();
+ }
+ else
+ {
+ sidepanel_inventory->toggleInbox();
+ }
+ }
+}
+
+void LLPanelMainInventory::onViewModeClick()
+{
+ LLUUID selected_folder;
+ LLUUID new_root_folder;
+ if(mSingleFolderMode)
+ {
+ selected_folder = getCurrentSFVRoot();
+ }
+ else
+ {
+ LLFolderView* root = getActivePanel()->getRootFolder();
+ std::set<LLFolderViewItem*> selection_set = root->getSelectionList();
+ if (selection_set.size() == 1)
+ {
+ LLFolderViewItem* current_item = *selection_set.begin();
+ if (current_item)
+ {
+ const LLUUID& id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID();
+ if(gInventory.getCategory(id) != NULL)
+ {
+ new_root_folder = id;
+ }
+ else
+ {
+ const LLViewerInventoryItem* selected_item = gInventory.getItem(id);
+ if (selected_item && selected_item->getParentUUID().notNull())
+ {
+ new_root_folder = selected_item->getParentUUID();
+ selected_folder = id;
+ }
+ }
+ }
+ }
+ mCombinationInventoryPanel->initFolderRoot(new_root_folder);
+ }
+
+ toggleViewMode();
+
+ if (mSingleFolderMode && new_root_folder.notNull())
+ {
+ setSingleFolderViewRoot(new_root_folder, true);
+ if(selected_folder.notNull() && isListViewMode())
+ {
+ getActivePanel()->setSelection(selected_folder, TAKE_FOCUS_YES);
+ }
+ }
+ else
+ {
+ if(selected_folder.notNull())
+ {
+ selectAllItemsPanel();
+ getActivePanel()->setSelection(selected_folder, TAKE_FOCUS_YES);
+ }
+ }
+}
+
+void LLPanelMainInventory::onUpFolderClicked()
+{
+ const LLViewerInventoryCategory* cat = gInventory.getCategory(getCurrentSFVRoot());
+ if (cat)
+ {
+ if (cat->getParentUUID().notNull())
+ {
+ if(isListViewMode())
+ {
+ mCombinationInventoryPanel->changeFolderRoot(cat->getParentUUID());
+ }
+ if(isGalleryViewMode())
+ {
+ mCombinationGalleryPanel->setRootFolder(cat->getParentUUID());
+ }
+ if(isCombinationViewMode())
+ {
+ mCombinationInventoryPanel->changeFolderRoot(cat->getParentUUID());
+ }
+ }
+ }
+}
+
+void LLPanelMainInventory::onBackFolderClicked()
+{
+ if(isListViewMode())
+ {
+ mCombinationInventoryPanel->onBackwardFolder();
+ }
+ if(isGalleryViewMode())
+ {
+ mCombinationGalleryPanel->onBackwardFolder();
+ }
+ if(isCombinationViewMode())
+ {
+ mCombinationInventoryPanel->onBackwardFolder();
+ }
+}
+
+void LLPanelMainInventory::onForwardFolderClicked()
+{
+ if(isListViewMode())
+ {
+ mCombinationInventoryPanel->onForwardFolder();
+ }
+ if(isGalleryViewMode())
+ {
+ mCombinationGalleryPanel->onForwardFolder();
+ }
+ if(isCombinationViewMode())
+ {
+ mCombinationInventoryPanel->onForwardFolder();
+ }
+}
+
+void LLPanelMainInventory::setSingleFolderViewRoot(const LLUUID& folder_id, bool clear_nav_history)
+{
+ if(isListViewMode())
+ {
+ mCombinationInventoryPanel->changeFolderRoot(folder_id);
+ if(clear_nav_history)
+ {
+ mCombinationInventoryPanel->clearNavigationHistory();
+ }
+ }
+ else if(isGalleryViewMode())
+ {
+ mCombinationGalleryPanel->setRootFolder(folder_id);
+ if(clear_nav_history)
+ {
+ mCombinationGalleryPanel->clearNavigationHistory();
+ }
+ }
+ else if(isCombinationViewMode())
+ {
+ mCombinationInventoryPanel->changeFolderRoot(folder_id);
+ }
+ updateNavButtons();
+}
+
+LLUUID LLPanelMainInventory::getSingleFolderViewRoot()
+{
+ return mCombinationInventoryPanel->getSingleFolderRoot();
+}
+
void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_view_name)
{
if (menu)
@@ -1239,17 +1734,11 @@ void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_v
LLView* spawning_view = getChild<LLView> (spawning_view_name);
S32 menu_x, menu_y;
//show menu in co-ordinates of panel
- spawning_view->localPointToOtherView(0, spawning_view->getRect().getHeight(), &menu_x, &menu_y, this);
- menu_y += menu->getRect().getHeight();
+ spawning_view->localPointToOtherView(0, 0, &menu_x, &menu_y, this);
LLMenuGL::showPopup(this, menu, menu_x, menu_y);
}
}
-void LLPanelMainInventory::onTrashButtonClick()
-{
- onClipboardAction("delete");
-}
-
void LLPanelMainInventory::onClipboardAction(const LLSD& userdata)
{
std::string command_name = userdata.asString();
@@ -1258,13 +1747,22 @@ void LLPanelMainInventory::onClipboardAction(const LLSD& userdata)
void LLPanelMainInventory::saveTexture(const LLSD& userdata)
{
- LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
- if (!current_item)
- {
- return;
- }
-
- const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID();
+ LLUUID item_id;
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ item_id = mCombinationGalleryPanel->getFirstSelectedItemID();
+ if (item_id.isNull()) return;
+ }
+ else
+ {
+ LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (!current_item)
+ {
+ return;
+ }
+ item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID();
+ }
+
LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item_id), TAKE_FOCUS_YES);
if (preview_texture)
{
@@ -1278,6 +1776,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata)
return;
const std::string command_name = userdata.asString();
+
if (command_name == "new_window")
{
newWindow();
@@ -1347,35 +1846,69 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata)
}
if (command_name == "find_original")
{
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ LLInventoryObject *obj = gInventory.getObject(mCombinationGalleryPanel->getFirstSelectedItemID());
+ if (obj && obj->getIsLinkType())
+ {
+ show_item_original(obj->getUUID());
+ }
+ }
+ else
+ {
LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
if (!current_item)
{
return;
}
static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(getActivePanel()->getModel(), "goto");
+ }
}
if (command_name == "find_links")
{
- LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
- if (!current_item)
- {
- return;
- }
- const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID();
- const std::string &item_name = current_item->getViewModelItem()->getName();
- mFilterSubString = item_name;
-
- LLInventoryFilter &filter = mActivePanel->getFilter();
- filter.setFindAllLinksMode(item_name, item_id);
-
- mFilterEditor->setText(item_name);
- mFilterEditor->setFocus(TRUE);
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ LLFloaterSidePanelContainer* inventory_container = newWindow();
+ if (inventory_container)
+ {
+ LLSidepanelInventory* sidepanel_inventory = dynamic_cast<LLSidepanelInventory*>(inventory_container->findChild<LLPanel>("main_panel", true));
+ if (sidepanel_inventory)
+ {
+ LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();
+ if (main_inventory)
+ {
+ LLInventoryObject *obj = gInventory.getObject(mCombinationGalleryPanel->getFirstSelectedItemID());
+ if (obj)
+ {
+ main_inventory->findLinks(obj->getUUID(), obj->getName());
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (!current_item)
+ {
+ return;
+ }
+ const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID();
+ const std::string &item_name = current_item->getViewModelItem()->getName();
+ findLinks(item_id, item_name);
+ }
}
if (command_name == "replace_links")
{
- LLSD params;
+ LLSD params;
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ params = LLSD(mCombinationGalleryPanel->getFirstSelectedItemID());
+ }
+ else
+ {
LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
if (current_item)
{
@@ -1390,23 +1923,72 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata)
}
}
}
+ }
LLFloaterReg::showInstance("linkreplace", params);
}
+ if (command_name == "close_inv_windows")
+ {
+ LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory");
+ for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end();)
+ {
+ LLFloaterSidePanelContainer* iv = dynamic_cast<LLFloaterSidePanelContainer*>(*iter++);
+ if (iv)
+ {
+ iv->closeFloater();
+ }
+ }
+ LLFloaterReg::hideInstance("inventory_settings");
+ }
+
+ if (command_name == "toggle_search_outfits")
+ {
+ getCurrentFilter().toggleSearchVisibilityOutfits();
+ }
+
if (command_name == "toggle_search_trash")
{
- mActivePanel->getFilter().toggleSearchVisibilityTrash();
+ getCurrentFilter().toggleSearchVisibilityTrash();
}
if (command_name == "toggle_search_library")
{
- mActivePanel->getFilter().toggleSearchVisibilityLibrary();
+ getCurrentFilter().toggleSearchVisibilityLibrary();
}
if (command_name == "include_links")
{
- mActivePanel->getFilter().toggleSearchVisibilityLinks();
- }
+ getCurrentFilter().toggleSearchVisibilityLinks();
+ }
+
+ if (command_name == "share")
+ {
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ std::set<LLUUID> uuids{ mCombinationGalleryPanel->getFirstSelectedItemID()};
+ LLAvatarActions::shareWithAvatars(uuids, gFloaterView->getParentFloater(this));
+ }
+ else
+ {
+ LLAvatarActions::shareWithAvatars(this);
+ }
+ }
+ if (command_name == "shop")
+ {
+ LLWeb::loadURL(gSavedSettings.getString("MarketplaceURL"));
+ }
+ if (command_name == "list_view")
+ {
+ setViewMode(MODE_LIST);
+ }
+ if (command_name == "gallery_view")
+ {
+ setViewMode(MODE_GALLERY);
+ }
+ if (command_name == "combination_view")
+ {
+ setViewMode(MODE_COMBINATION);
+ }
}
void LLPanelMainInventory::onVisibilityChange( BOOL new_visibility )
@@ -1424,17 +2006,26 @@ void LLPanelMainInventory::onVisibilityChange( BOOL new_visibility )
bool LLPanelMainInventory::isSaveTextureEnabled(const LLSD& userdata)
{
- LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
- if (current_item)
- {
- LLViewerInventoryItem *inv_item = dynamic_cast<LLViewerInventoryItem*>(static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryObject());
+ LLViewerInventoryItem *inv_item = NULL;
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ inv_item = gInventory.getItem(mCombinationGalleryPanel->getFirstSelectedItemID());
+ }
+ else
+ {
+ LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (current_item)
+ {
+ inv_item = dynamic_cast<LLViewerInventoryItem*>(static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryObject());
+ }
+ }
if(inv_item)
{
bool can_save = inv_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED);
- LLInventoryType::EType curr_type = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryType();
+ LLInventoryType::EType curr_type = inv_item->getInventoryType();
return can_save && (curr_type == LLInventoryType::IT_TEXTURE || curr_type == LLInventoryType::IT_SNAPSHOT);
}
- }
+
return false;
}
@@ -1465,9 +2056,16 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)
}
if (command_name == "find_original")
{
+ LLUUID item_id;
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ item_id = mCombinationGalleryPanel->getFirstSelectedItemID();
+ }
+ else{
LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
if (!current_item) return FALSE;
- const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID();
+ item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID();
+ }
const LLViewerInventoryItem *item = gInventory.getItem(item_id);
if (item && item->getIsLinkType() && !item->getIsBrokenLink())
{
@@ -1478,12 +2076,19 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)
if (command_name == "find_links")
{
+ LLUUID item_id;
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ item_id = mCombinationGalleryPanel->getFirstSelectedItemID();
+ }
+ else{
LLFolderView* root = getActivePanel()->getRootFolder();
std::set<LLFolderViewItem*> selection_set = root->getSelectionList();
if (selection_set.size() != 1) return FALSE;
LLFolderViewItem* current_item = root->getCurSelectedItem();
if (!current_item) return FALSE;
- const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID();
+ item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID();
+ }
const LLInventoryObject *obj = gInventory.getObject(item_id);
if (obj && !obj->getIsLinkType() && LLAssetType::lookupCanLink(obj->getType()))
{
@@ -1507,10 +2112,16 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)
if (command_name == "share")
{
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ return can_share_item(mCombinationGalleryPanel->getFirstSelectedItemID());
+ }
+ else{
LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
if (!current_item) return FALSE;
LLSidepanelInventory* parent = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory");
return parent ? parent->canShare() : FALSE;
+ }
}
if (command_name == "empty_trash")
{
@@ -1528,9 +2139,24 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)
return TRUE;
}
+bool LLPanelMainInventory::isActionVisible(const LLSD& userdata)
+{
+ const std::string param_str = userdata.asString();
+ if (param_str == "single_folder_view")
+ {
+ return mSingleFolderMode;
+ }
+ if (param_str == "multi_folder_view")
+ {
+ return !mSingleFolderMode;
+ }
+
+ return true;
+}
+
BOOL LLPanelMainInventory::isActionChecked(const LLSD& userdata)
{
- U32 sort_order_mask = getActivePanel()->getSortOrder();
+ U32 sort_order_mask = (mSingleFolderMode && isGalleryViewMode()) ? mCombinationGalleryPanel->getSortOrder() : getActivePanel()->getSortOrder();
const std::string command_name = userdata.asString();
if (command_name == "sort_by_name")
{
@@ -1552,36 +2178,40 @@ BOOL LLPanelMainInventory::isActionChecked(const LLSD& userdata)
return sort_order_mask & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
}
+ if (command_name == "toggle_search_outfits")
+ {
+ return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_OUTFITS) != 0;
+ }
+
if (command_name == "toggle_search_trash")
{
- return (mActivePanel->getFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_TRASH) != 0;
+ return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_TRASH) != 0;
}
if (command_name == "toggle_search_library")
{
- return (mActivePanel->getFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LIBRARY) != 0;
+ return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LIBRARY) != 0;
}
if (command_name == "include_links")
{
- return (mActivePanel->getFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LINKS) != 0;
+ return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LINKS) != 0;
}
- return FALSE;
-}
-
-bool LLPanelMainInventory::handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept)
-{
- *accept = ACCEPT_NO;
-
- const bool is_enabled = isActionEnabled("delete");
- if (is_enabled) *accept = ACCEPT_YES_MULTI;
+ if (command_name == "list_view")
+ {
+ return isListViewMode();
+ }
+ if (command_name == "gallery_view")
+ {
+ return isGalleryViewMode();
+ }
+ if (command_name == "combination_view")
+ {
+ return isCombinationViewMode();
+ }
- if (is_enabled && drop)
- {
- onClipboardAction("delete");
- }
- return true;
+ return FALSE;
}
void LLPanelMainInventory::setUploadCostIfNeeded()
@@ -1599,10 +2229,410 @@ void LLPanelMainInventory::setUploadCostIfNeeded()
}
}
+bool is_add_allowed(LLUUID folder_id)
+{
+ if(!gInventory.isObjectDescendentOf(folder_id, gInventory.getRootFolderID()))
+ {
+ return false;
+ }
+
+ std::vector<LLFolderType::EType> not_allowed_types;
+ not_allowed_types.push_back(LLFolderType::FT_LOST_AND_FOUND);
+ not_allowed_types.push_back(LLFolderType::FT_FAVORITE);
+ not_allowed_types.push_back(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ not_allowed_types.push_back(LLFolderType::FT_TRASH);
+ not_allowed_types.push_back(LLFolderType::FT_CURRENT_OUTFIT);
+ not_allowed_types.push_back(LLFolderType::FT_INBOX);
+
+ for (std::vector<LLFolderType::EType>::const_iterator it = not_allowed_types.begin();
+ it != not_allowed_types.end(); ++it)
+ {
+ if(gInventory.isObjectDescendentOf(folder_id, gInventory.findCategoryUUIDForType(*it)))
+ {
+ return false;
+ }
+ }
+
+ LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id);
+ if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT))
+ {
+ return false;
+ }
+ return true;
+}
+
+void LLPanelMainInventory::disableAddIfNeeded()
+{
+ LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get();
+ if (menu)
+ {
+ bool enable = !mSingleFolderMode || is_add_allowed(getCurrentSFVRoot());
+
+ menu->getChild<LLMenuItemGL>("New Folder")->setEnabled(enable && !isRecentItemsPanelSelected());
+ menu->getChild<LLMenuItemGL>("New Script")->setEnabled(enable);
+ menu->getChild<LLMenuItemGL>("New Note")->setEnabled(enable);
+ menu->getChild<LLMenuItemGL>("New Gesture")->setEnabled(enable);
+ menu->setItemEnabled("New Clothes", enable);
+ menu->setItemEnabled("New Body Parts", enable);
+ menu->setItemEnabled("New Settings", enable);
+ }
+}
+
bool LLPanelMainInventory::hasSettingsInventory()
{
return LLEnvironment::instance().isInventoryEnabled();
}
+void LLPanelMainInventory::updateTitle()
+{
+ LLFloater* inventory_floater = gFloaterView->getParentFloater(this);
+ if(inventory_floater)
+ {
+ if(mSingleFolderMode)
+ {
+ inventory_floater->setTitle(getLocalizedRootName());
+ LLFloaterInventoryFinder *finder = getFinder();
+ if (finder)
+ {
+ finder->setTitle(getLocalizedRootName());
+ }
+ }
+ else
+ {
+ inventory_floater->setTitle(getString("inventory_title"));
+ }
+ }
+ updateNavButtons();
+}
+
+void LLPanelMainInventory::onCombinationRootChanged(bool gallery_clicked)
+{
+ if(gallery_clicked)
+ {
+ mCombinationInventoryPanel->changeFolderRoot(mCombinationGalleryPanel->getRootFolder());
+ }
+ else
+ {
+ mCombinationGalleryPanel->setRootFolder(mCombinationInventoryPanel->getSingleFolderRoot());
+ }
+ mForceShowInvLayout = false;
+ updateTitle();
+ mReshapeInvLayout = true;
+}
+
+void LLPanelMainInventory::onCombinationGallerySelectionChanged(const LLUUID& category_id)
+{
+}
+
+void LLPanelMainInventory::onCombinationInventorySelectionChanged(const std::deque<LLFolderViewItem*>& items, BOOL user_action)
+{
+ onSelectionChange(mCombinationInventoryPanel, items, user_action);
+}
+
+void LLPanelMainInventory::updatePanelVisibility()
+{
+ mDefaultViewPanel->setVisible(!mSingleFolderMode);
+ mCombinationViewPanel->setVisible(mSingleFolderMode);
+ mNavigationBtnsPanel->setVisible(mSingleFolderMode);
+ mViewModeBtn->setImageOverlay(mSingleFolderMode ? getString("default_mode_btn") : getString("single_folder_mode_btn"));
+ mViewModeBtn->setEnabled(mSingleFolderMode || (getAllItemsPanel() == getActivePanel()));
+ if (mSingleFolderMode)
+ {
+ if (isCombinationViewMode())
+ {
+ LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter();
+ comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_EXCLUDE_THUMBNAILS);
+ comb_inv_filter.markDefault();
+
+ LLInventoryFilter& comb_gallery_filter = mCombinationGalleryPanel->getFilter();
+ comb_gallery_filter.setFilterThumbnails(LLInventoryFilter::FILTER_ONLY_THUMBNAILS);
+ comb_gallery_filter.markDefault();
+
+ // visibility will be controled by updateCombinationVisibility()
+ mCombinationGalleryLayoutPanel->setVisible(true);
+ mCombinationGalleryPanel->setVisible(true);
+ mCombinationListLayoutPanel->setVisible(true);
+ }
+ else
+ {
+ LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter();
+ comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_INCLUDE_THUMBNAILS);
+ comb_inv_filter.markDefault();
+
+ LLInventoryFilter& comb_gallery_filter = mCombinationGalleryPanel->getFilter();
+ comb_gallery_filter.setFilterThumbnails(LLInventoryFilter::FILTER_INCLUDE_THUMBNAILS);
+ comb_gallery_filter.markDefault();
+
+ mCombinationLayoutStack->setPanelSpacing(0);
+ mCombinationGalleryLayoutPanel->setVisible(mSingleFolderMode && isGalleryViewMode());
+ mCombinationGalleryPanel->setVisible(mSingleFolderMode && isGalleryViewMode()); // to prevent or process updates
+ mCombinationListLayoutPanel->setVisible(mSingleFolderMode && isListViewMode());
+ }
+ }
+ else
+ {
+ mCombinationGalleryLayoutPanel->setVisible(false);
+ mCombinationGalleryPanel->setVisible(false); // to prevent updates
+ mCombinationListLayoutPanel->setVisible(false);
+ }
+}
+
+void LLPanelMainInventory::updateCombinationVisibility()
+{
+ if(mSingleFolderMode && isCombinationViewMode())
+ {
+ bool is_gallery_empty = !mCombinationGalleryPanel->hasVisibleItems();
+ bool show_inv_pane = mCombinationInventoryPanel->hasVisibleItems() || is_gallery_empty || mForceShowInvLayout;
+
+ const S32 DRAG_HANDLE_PADDING = 12; // for drag handle to not overlap gallery when both inventories are visible
+ mCombinationLayoutStack->setPanelSpacing(show_inv_pane ? DRAG_HANDLE_PADDING : 0);
+
+ mCombinationGalleryLayoutPanel->setVisible(!is_gallery_empty);
+ mCombinationListLayoutPanel->setVisible(show_inv_pane);
+ mCombinationInventoryPanel->getRootFolder()->setForceArrange(!show_inv_pane);
+ if(mCombinationInventoryPanel->hasVisibleItems())
+ {
+ mForceShowInvLayout = false;
+ }
+ if(is_gallery_empty)
+ {
+ mCombinationGalleryPanel->handleModifiedFilter();
+ }
+
+ getActivePanel()->getRootFolder();
+
+ if (mReshapeInvLayout
+ && show_inv_pane
+ && (mCombinationGalleryPanel->hasVisibleItems() || mCombinationGalleryPanel->areViewsInitialized())
+ && mCombinationInventoryPanel->areViewsInitialized())
+ {
+ mReshapeInvLayout = false;
+
+ // force drop previous shape (because panel doesn't decrease shape properly)
+ LLRect list_latout = mCombinationListLayoutPanel->getRect();
+ list_latout.mTop = list_latout.mBottom; // min height is at 100, so it should snap to be bigger
+ mCombinationListLayoutPanel->setShape(list_latout, false);
+
+ LLRect inv_inner_rect = mCombinationInventoryPanel->getScrollableContainer()->getScrolledViewRect();
+ S32 inv_height = inv_inner_rect.getHeight()
+ + (mCombinationInventoryPanel->getScrollableContainer()->getBorderWidth() * 2)
+ + mCombinationInventoryPanel->getScrollableContainer()->getSize();
+ LLRect inner_galery_rect = mCombinationGalleryPanel->getScrollableContainer()->getScrolledViewRect();
+ S32 gallery_height = inner_galery_rect.getHeight()
+ + (mCombinationGalleryPanel->getScrollableContainer()->getBorderWidth() * 2)
+ + mCombinationGalleryPanel->getScrollableContainer()->getSize();
+ LLRect layout_rect = mCombinationViewPanel->getRect();
+
+ // by default make it take 1/3 of the panel
+ S32 list_default_height = layout_rect.getHeight() / 3;
+ // Don't set height from gallery_default_height - needs to account for a resizer in such case
+ S32 gallery_default_height = layout_rect.getHeight() - list_default_height;
+
+ if (inv_height > list_default_height
+ && gallery_height < gallery_default_height)
+ {
+ LLRect gallery_latout = mCombinationGalleryLayoutPanel->getRect();
+ gallery_latout.mTop = gallery_latout.mBottom + gallery_height;
+ mCombinationGalleryLayoutPanel->setShape(gallery_latout, true /*tell stack to account for new shape*/);
+ }
+ else if (inv_height < list_default_height
+ && gallery_height > gallery_default_height)
+ {
+ LLRect list_latout = mCombinationListLayoutPanel->getRect();
+ list_latout.mTop = list_latout.mBottom + inv_height;
+ mCombinationListLayoutPanel->setShape(list_latout, true /*tell stack to account for new shape*/);
+ }
+ else
+ {
+ LLRect list_latout = mCombinationListLayoutPanel->getRect();
+ list_latout.mTop = list_latout.mBottom + list_default_height;
+ mCombinationListLayoutPanel->setShape(list_latout, true /*tell stack to account for new shape*/);
+ }
+ }
+ }
+
+ if (mSingleFolderMode
+ && !isGalleryViewMode()
+ && mCombInvUUIDNeedsRename.notNull()
+ && mCombinationInventoryPanel->areViewsInitialized())
+ {
+ mCombinationInventoryPanel->setSelectionByID(mCombInvUUIDNeedsRename, TRUE);
+ mCombinationInventoryPanel->getRootFolder()->scrollToShowSelection();
+ mCombinationInventoryPanel->getRootFolder()->setNeedsAutoRename(TRUE);
+ mCombInvUUIDNeedsRename.setNull();
+ }
+}
+
+void LLPanelMainInventory::updateNavButtons()
+{
+ if(isListViewMode())
+ {
+ mBackBtn->setEnabled(mCombinationInventoryPanel->isBackwardAvailable());
+ mForwardBtn->setEnabled(mCombinationInventoryPanel->isForwardAvailable());
+ }
+ if(isGalleryViewMode())
+ {
+ mBackBtn->setEnabled(mCombinationGalleryPanel->isBackwardAvailable());
+ mForwardBtn->setEnabled(mCombinationGalleryPanel->isForwardAvailable());
+ }
+ if(isCombinationViewMode())
+ {
+ mBackBtn->setEnabled(mCombinationInventoryPanel->isBackwardAvailable());
+ mForwardBtn->setEnabled(mCombinationInventoryPanel->isForwardAvailable());
+ }
+
+ const LLViewerInventoryCategory* cat = gInventory.getCategory(getCurrentSFVRoot());
+ bool up_enabled = (cat && cat->getParentUUID().notNull());
+ mUpBtn->setEnabled(up_enabled);
+}
+
+LLSidepanelInventory* LLPanelMainInventory::getParentSidepanelInventory()
+{
+ LLFloaterSidePanelContainer* inventory_container = dynamic_cast<LLFloaterSidePanelContainer*>(gFloaterView->getParentFloater(this));
+ if(inventory_container)
+ {
+ return dynamic_cast<LLSidepanelInventory*>(inventory_container->findChild<LLPanel>("main_panel", true));
+ }
+ return NULL;
+}
+
+void LLPanelMainInventory::setViewMode(EViewModeType mode)
+{
+ if(mode != mViewMode)
+ {
+ std::list<LLUUID> forward_history;
+ std::list<LLUUID> backward_history;
+ U32 sort_order = 0;
+ switch(mViewMode)
+ {
+ case MODE_LIST:
+ forward_history = mCombinationInventoryPanel->getNavForwardList();
+ backward_history = mCombinationInventoryPanel->getNavBackwardList();
+ sort_order = mCombinationInventoryPanel->getSortOrder();
+ break;
+ case MODE_GALLERY:
+ forward_history = mCombinationGalleryPanel->getNavForwardList();
+ backward_history = mCombinationGalleryPanel->getNavBackwardList();
+ sort_order = mCombinationGalleryPanel->getSortOrder();
+ break;
+ case MODE_COMBINATION:
+ forward_history = mCombinationInventoryPanel->getNavForwardList();
+ backward_history = mCombinationInventoryPanel->getNavBackwardList();
+ mCombinationInventoryPanel->getRootFolder()->setForceArrange(false);
+ sort_order = mCombinationInventoryPanel->getSortOrder();
+ break;
+ }
+
+ LLUUID cur_root = getCurrentSFVRoot();
+ mViewMode = mode;
+
+ updatePanelVisibility();
+
+ if(isListViewMode())
+ {
+ mCombinationInventoryPanel->changeFolderRoot(cur_root);
+ mCombinationInventoryPanel->setNavForwardList(forward_history);
+ mCombinationInventoryPanel->setNavBackwardList(backward_history);
+ mCombinationInventoryPanel->setSortOrder(sort_order);
+ }
+ if(isGalleryViewMode())
+ {
+ mCombinationGalleryPanel->setRootFolder(cur_root);
+ mCombinationGalleryPanel->setNavForwardList(forward_history);
+ mCombinationGalleryPanel->setNavBackwardList(backward_history);
+ mCombinationGalleryPanel->setSortOrder(sort_order, true);
+ }
+ if(isCombinationViewMode())
+ {
+ mCombinationInventoryPanel->changeFolderRoot(cur_root);
+ mCombinationGalleryPanel->setRootFolder(cur_root);
+ mCombinationInventoryPanel->setNavForwardList(forward_history);
+ mCombinationInventoryPanel->setNavBackwardList(backward_history);
+ mCombinationGalleryPanel->setNavForwardList(forward_history);
+ mCombinationGalleryPanel->setNavBackwardList(backward_history);
+ mCombinationInventoryPanel->setSortOrder(sort_order);
+ mCombinationGalleryPanel->setSortOrder(sort_order, true);
+ }
+
+ updateNavButtons();
+
+ onFilterSelected();
+ if((isListViewMode() && (mActivePanel->getFilterSubString() != mFilterSubString)) ||
+ (isGalleryViewMode() && (mCombinationGalleryPanel->getFilterSubString() != mFilterSubString)))
+ {
+ onFilterEdit(mFilterSubString);
+ }
+ }
+}
+
+std::string LLPanelMainInventory::getLocalizedRootName()
+{
+ return mSingleFolderMode ? get_localized_folder_name(getCurrentSFVRoot()) : "";
+}
+
+LLUUID LLPanelMainInventory::getCurrentSFVRoot()
+{
+ if(isListViewMode())
+ {
+ return mCombinationInventoryPanel->getSingleFolderRoot();
+ }
+ if(isGalleryViewMode())
+ {
+ return mCombinationGalleryPanel->getRootFolder();
+ }
+ if(isCombinationViewMode())
+ {
+ return mCombinationInventoryPanel->getSingleFolderRoot();
+ }
+ return LLUUID::null;
+}
+
+LLInventoryFilter& LLPanelMainInventory::getCurrentFilter()
+{
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ return mCombinationGalleryPanel->getFilter();
+ }
+ else
+ {
+ return mActivePanel->getFilter();
+ }
+}
+
+void LLPanelMainInventory::setGallerySelection(const LLUUID& item_id, bool new_window)
+{
+ if(mSingleFolderMode && isGalleryViewMode())
+ {
+ mCombinationGalleryPanel->changeItemSelection(item_id, true);
+ }
+ else if(mSingleFolderMode && isCombinationViewMode())
+ {
+ if(mCombinationGalleryPanel->getFilter().checkAgainstFilterThumbnails(item_id))
+ {
+ mCombinationGalleryPanel->changeItemSelection(item_id, false);
+ scrollToGallerySelection();
+ }
+ else
+ {
+ mCombinationInventoryPanel->setSelection(item_id, true);
+ scrollToInvPanelSelection();
+ }
+ }
+ else if (mSingleFolderMode && isListViewMode())
+ {
+ mCombinationInventoryPanel->setSelection(item_id, true);
+ }
+}
+
+void LLPanelMainInventory::scrollToGallerySelection()
+{
+ mCombinationGalleryPanel->scrollToShowItem(mCombinationGalleryPanel->getFirstSelectedItemID());
+}
+
+void LLPanelMainInventory::scrollToInvPanelSelection()
+{
+ mCombinationInventoryPanel->getRootFolder()->scrollToShowSelection();
+}
+
// List Commands //
////////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h
index 7aae5a0b3c..79501e63bc 100644
--- a/indra/newview/llpanelmaininventory.h
+++ b/indra/newview/llpanelmaininventory.h
@@ -30,6 +30,7 @@
#include "llpanel.h"
#include "llinventoryobserver.h"
+#include "llinventorypanel.h"
#include "lldndbutton.h"
#include "llfolderview.h"
@@ -37,14 +38,17 @@
class LLComboBox;
class LLFolderViewItem;
class LLInventoryPanel;
+class LLInventoryGallery;
class LLSaveFolderState;
class LLFilterEditor;
class LLTabContainer;
class LLFloaterInventoryFinder;
class LLMenuButton;
class LLMenuGL;
+class LLSidepanelInventory;
class LLToggleableMenu;
class LLFloater;
+class LLFloaterSidePanelContainer;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLPanelMainInventory
@@ -63,6 +67,13 @@ public:
BOOL postBuild();
+ enum EViewModeType
+ {
+ MODE_LIST,
+ MODE_GALLERY,
+ MODE_COMBINATION
+ };
+
virtual BOOL handleKeyHere(KEY key, MASK mask);
// Inherited functionality
@@ -80,6 +91,7 @@ public:
LLInventoryPanel* getAllItemsPanel();
void selectAllItemsPanel();
const LLInventoryPanel* getActivePanel() const { return mActivePanel; }
+ void setActivePanel();
bool isRecentItemsPanelSelected();
@@ -91,13 +103,40 @@ public:
void setFocusFilterEditor();
- static void newWindow();
+ static LLFloaterSidePanelContainer* newWindow();
+ static void newFolderWindow(LLUUID folder_id = LLUUID(), LLUUID item_to_select = LLUUID());
void toggleFindOptions();
void resetFilters();
void resetAllItemsFilters();
+ void findLinks(const LLUUID& item_id, const std::string& item_name);
+
+ void onViewModeClick();
+ void toggleViewMode();
+ void initSingleFolderRoot(const LLUUID& start_folder_id = LLUUID::null);
+ void initInventoryViews();
+ void onUpFolderClicked();
+ void onBackFolderClicked();
+ void onForwardFolderClicked();
+ void setSingleFolderViewRoot(const LLUUID& folder_id, bool clear_nav_history = true);
+ void setGallerySelection(const LLUUID& item_id, bool new_window = false);
+ LLUUID getSingleFolderViewRoot();
+ bool isSingleFolderMode() { return mSingleFolderMode; }
+
+ void scrollToGallerySelection();
+ void scrollToInvPanelSelection();
+
+ void setViewMode(EViewModeType mode);
+ bool isListViewMode() { return (mViewMode == MODE_LIST); }
+ bool isGalleryViewMode() { return (mViewMode == MODE_GALLERY); }
+ bool isCombinationViewMode() { return (mViewMode == MODE_COMBINATION); }
+ LLUUID getCurrentSFVRoot();
+ std::string getLocalizedRootName();
+
+ LLInventoryFilter& getCurrentFilter();
+
protected:
//
// Misc functions
@@ -127,9 +166,15 @@ protected:
bool isSaveTextureEnabled(const LLSD& userdata);
void updateItemcountText();
+ void updatePanelVisibility();
+ void updateCombinationVisibility();
+
void onFocusReceived();
void onSelectSearchType();
void updateSearchTypeCombo();
+ void setSearchType(LLInventoryFilter::ESearchType type);
+
+ LLSidepanelInventory* getParentSidepanelInventory();
private:
LLFloaterInventoryFinder* getFinder();
@@ -150,7 +195,26 @@ private:
std::string mCategoryCountString;
LLComboBox* mSearchTypeCombo;
+ LLButton* mBackBtn;
+ LLButton* mForwardBtn;
+ LLButton* mUpBtn;
+ LLButton* mViewModeBtn;
+ LLLayoutPanel* mNavigationBtnsPanel;
+
+ LLPanel* mDefaultViewPanel;
+ LLPanel* mCombinationViewPanel;
+ bool mSingleFolderMode;
+ EViewModeType mViewMode;
+
+ LLInventorySingleFolderPanel* mCombinationInventoryPanel;
+ LLInventoryGallery* mCombinationGalleryPanel;
+ LLPanel* mCombinationGalleryLayoutPanel;
+ LLLayoutPanel* mCombinationListLayoutPanel;
+ LLLayoutStack* mCombinationLayoutStack;
+
+ boost::signals2::connection mListViewRootUpdatedConnection;
+ boost::signals2::connection mGalleryRootUpdatedConnection;
//////////////////////////////////////////////////////////////////////////////////
// List Commands //
@@ -159,26 +223,37 @@ protected:
void updateListCommands();
void onAddButtonClick();
void showActionMenu(LLMenuGL* menu, std::string spawning_view_name);
- void onTrashButtonClick();
void onClipboardAction(const LLSD& userdata);
BOOL isActionEnabled(const LLSD& command_name);
BOOL isActionChecked(const LLSD& userdata);
void onCustomAction(const LLSD& command_name);
- bool handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept);
+ bool isActionVisible(const LLSD& userdata);
static bool hasSettingsInventory();
+ void updateTitle();
+ void updateNavButtons();
+
+ void onCombinationRootChanged(bool gallery_clicked);
+ void onCombinationGallerySelectionChanged(const LLUUID& category_id);
+ void onCombinationInventorySelectionChanged(const std::deque<LLFolderViewItem*>& items, BOOL user_action);
/**
* Set upload cost in "Upload" sub menu.
*/
void setUploadCostIfNeeded();
+ void disableAddIfNeeded();
private:
- LLDragAndDropButton* mTrashButton;
LLToggleableMenu* mMenuGearDefault;
+ LLToggleableMenu* mMenuViewDefault;
LLToggleableMenu* mMenuVisibility;
LLMenuButton* mGearMenuButton;
+ LLMenuButton* mViewMenuButton;
LLMenuButton* mVisibilityMenuButton;
LLHandle<LLView> mMenuAddHandle;
bool mNeedUploadCost;
+
+ bool mForceShowInvLayout;
+ bool mReshapeInvLayout;
+ LLUUID mCombInvUUIDNeedsRename;
// List Commands //
////////////////////////////////////////////////////////////////////////////////
};
diff --git a/indra/newview/llpanelmarketplaceinbox.cpp b/indra/newview/llpanelmarketplaceinbox.cpp
index 8a86f4f63d..3638ee14fc 100644
--- a/indra/newview/llpanelmarketplaceinbox.cpp
+++ b/indra/newview/llpanelmarketplaceinbox.cpp
@@ -75,9 +75,6 @@ BOOL LLPanelMarketplaceInbox::postBuild()
void LLPanelMarketplaceInbox::onSelectionChange()
{
- LLSidepanelInventory* sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory");
-
- sidepanel_inventory->updateVerbs();
}
diff --git a/indra/newview/llpanelmarketplaceinboxinventory.cpp b/indra/newview/llpanelmarketplaceinboxinventory.cpp
index 7a6631448b..e13bd0412d 100644
--- a/indra/newview/llpanelmarketplaceinboxinventory.cpp
+++ b/indra/newview/llpanelmarketplaceinboxinventory.cpp
@@ -62,10 +62,13 @@ LLInboxInventoryPanel::LLInboxInventoryPanel(const LLInboxInventoryPanel::Params
: LLInventoryPanel(p)
{
LLInboxNewItemsStorage::getInstance()->load();
+ LLInboxNewItemsStorage::getInstance()->addInboxPanel(this);
}
LLInboxInventoryPanel::~LLInboxInventoryPanel()
-{}
+{
+ LLInboxNewItemsStorage::getInstance()->removeInboxPanel(this);
+}
void LLInboxInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
{
@@ -108,6 +111,21 @@ LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * b
return LLUICtrlFactory::create<LLInboxFolderViewItem>(params);
}
+void LLInboxInventoryPanel::onRemoveItemFreshness(const LLUUID& item_id)
+{
+ LLInboxFolderViewFolder* inbox_folder_view = dynamic_cast<LLInboxFolderViewFolder*>(getFolderByID(item_id));
+ if(inbox_folder_view)
+ {
+ inbox_folder_view->setFresh(false);
+ }
+
+ LLInboxFolderViewItem* inbox_item_view = dynamic_cast<LLInboxFolderViewItem*>(getItemByID(item_id));
+ if(inbox_item_view)
+ {
+ inbox_item_view->setFresh(false);
+ }
+}
+
//
// LLInboxFolderViewFolder Implementation
//
@@ -340,4 +358,18 @@ void LLInboxNewItemsStorage::load()
}
}
}
+
+void LLInboxNewItemsStorage::removeItem(const LLUUID& id)
+{
+ mNewItemsIDs.erase(id);
+
+ //notify inbox panels
+ for (auto inbox : mInboxPanels)
+ {
+ if(inbox)
+ {
+ inbox->onRemoveItemFreshness(id);
+ }
+ }
+}
// eof
diff --git a/indra/newview/llpanelmarketplaceinboxinventory.h b/indra/newview/llpanelmarketplaceinboxinventory.h
index 3e508e801b..9eef5f209c 100644
--- a/indra/newview/llpanelmarketplaceinboxinventory.h
+++ b/indra/newview/llpanelmarketplaceinboxinventory.h
@@ -49,6 +49,8 @@ public:
void initFromParams(const LLInventoryPanel::Params&);
LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop);
LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge);
+
+ void onRemoveItemFreshness(const LLUUID& item_id);
};
@@ -77,6 +79,7 @@ public:
void deFreshify();
bool isFresh() const { return mFresh; }
+ void setFresh(bool is_fresh) { mFresh = is_fresh; }
protected:
bool mFresh;
@@ -108,6 +111,7 @@ public:
void deFreshify();
bool isFresh() const { return mFresh; }
+ void setFresh(bool is_fresh) { mFresh = is_fresh; }
protected:
bool mFresh;
@@ -125,11 +129,16 @@ public:
void load();
void addFreshItem(const LLUUID& id) { mNewItemsIDs.insert(id); }
- void removeItem(const LLUUID& id) { mNewItemsIDs.erase(id); }
+ void removeItem(const LLUUID& id);
bool isItemFresh(const LLUUID& id) { return (mNewItemsIDs.find(id) != mNewItemsIDs.end()); }
+ void addInboxPanel(LLInboxInventoryPanel* inbox) { mInboxPanels.insert(inbox); }
+ void removeInboxPanel(LLInboxInventoryPanel* inbox) { mInboxPanels.erase(inbox); }
+
private:
std::set<LLUUID> mNewItemsIDs;
+
+ std::set<LLInboxInventoryPanel*> mInboxPanels;
};
#endif //LL_INBOXINVENTORYPANEL_H
diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp
index 6a82a3b35d..a3bbd00601 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -116,6 +116,7 @@ public:
virtual PermissionMask getPermissionMask() const { return PERM_NONE; }
/*virtual*/ LLFolderType::EType getPreferredType() const { return LLFolderType::FT_NONE; }
virtual const LLUUID& getUUID() const { return mUUID; }
+ virtual const LLUUID& getThumbnailUUID() const { return LLUUID::null;}
virtual time_t getCreationDate() const;
virtual void setCreationDate(time_t creation_date_utc);
@@ -124,6 +125,7 @@ public:
virtual BOOL canOpenItem() const { return FALSE; }
virtual void closeItem() {}
virtual void selectItem() {}
+ virtual void navigateToFolder(bool new_window = false, bool change_mode = false) {}
virtual BOOL isItemRenameable() const;
virtual BOOL renameItem(const std::string& new_name);
virtual BOOL isItemMovable() const;
@@ -1398,21 +1400,6 @@ void LLPanelObjectInventory::inventoryChanged(LLViewerObject* object,
{
mInventoryNeedsUpdate = TRUE;
}
-
- // refresh any properties floaters that are hanging around.
- if(inventory)
- {
- for (LLInventoryObject::object_list_t::const_iterator iter = inventory->begin();
- iter != inventory->end(); )
- {
- LLInventoryObject* item = *iter++;
- LLFloaterProperties* floater = LLFloaterReg::findTypedInstance<LLFloaterProperties>("properties", item->getUUID());
- if(floater)
- {
- floater->refresh();
- }
- }
- }
}
void LLPanelObjectInventory::updateInventory()
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp
index 35582d2967..4a755a6e93 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -699,8 +699,12 @@ void LLPanelOutfitEdit::onFolderViewFilterCommitted(LLUICtrl* ctrl)
LLOpenFoldersWithSelection opener;
mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener);
mInventoryItemsPanel->getRootFolder()->scrollToShowSelection();
-
- LLInventoryModelBackgroundFetch::instance().start();
+
+ if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
+ {
+ llassert(false); // this should have been done on startup
+ LLInventoryModelBackgroundFetch::instance().start();
+ }
}
void LLPanelOutfitEdit::onListViewFilterCommitted(LLUICtrl* ctrl)
@@ -737,8 +741,12 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string)
mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener);
mInventoryItemsPanel->getRootFolder()->scrollToShowSelection();
}
-
- LLInventoryModelBackgroundFetch::instance().start();
+
+ if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
+ {
+ llassert(false); // this should have been done on startup
+ LLInventoryModelBackgroundFetch::instance().start();
+ }
if (mInventoryItemsPanel->getFilterSubString().empty() && mSearchString.empty())
{
diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp
index 531073526b..d8c34d5c40 100644
--- a/indra/newview/llpaneloutfitsinventory.cpp
+++ b/indra/newview/llpaneloutfitsinventory.cpp
@@ -87,7 +87,7 @@ BOOL LLPanelOutfitsInventory::postBuild()
// ( This is only necessary if we want to show a warning if a user deletes an item that has a
// a link in an outfit, see "ConfirmItemDeleteHasLinks". )
- const LLUUID &outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
+ const LLUUID &outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
if (outfits_cat.notNull())
{
LLInventoryModelBackgroundFetch::instance().start(outfits_cat);
@@ -166,7 +166,11 @@ void LLPanelOutfitsInventory::onSearchEdit(const std::string& string)
mActivePanel->setFilterSubString(LLStringUtil::null);
}
- LLInventoryModelBackgroundFetch::instance().start();
+ if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
+ {
+ llassert(false); // this should have been done on startup
+ LLInventoryModelBackgroundFetch::instance().start();
+ }
if (mActivePanel->getFilterSubString().empty() && string.empty())
{
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 7aabd5247a..3333c832d2 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -71,6 +71,7 @@
#include "llpanelblockedlist.h"
#include "llpanelprofileclassifieds.h"
#include "llpanelprofilepicks.h"
+#include "llthumbnailctrl.h"
#include "lltrans.h"
#include "llviewercontrol.h"
#include "llviewermenu.h" //is_agent_mappable
@@ -366,7 +367,7 @@ LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::stri
httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
- LL_WARNS("AvatarProperties") << result << LL_ENDL;
+ LL_DEBUGS("AvatarProperties") << result << LL_ENDL;
if (!status)
{
@@ -913,7 +914,7 @@ BOOL LLPanelProfileSecondLife::postBuild()
{
mGroupList = getChild<LLGroupList>("group_list");
mShowInSearchCombo = getChild<LLComboBox>("show_in_search");
- mSecondLifePic = getChild<LLIconCtrl>("2nd_life_pic");
+ mSecondLifePic = getChild<LLThumbnailCtrl>("2nd_life_pic");
mSecondLifePicLayout = getChild<LLPanel>("image_panel");
mDescriptionEdit = getChild<LLTextEditor>("sl_description_edit");
mAgentActionMenuButton = getChild<LLMenuButton>("agent_actions_menu");
@@ -1497,7 +1498,6 @@ void LLPanelProfileSecondLife::setLoaded()
}
-
class LLProfileImagePicker : public LLFilePickerThread
{
public:
@@ -1544,15 +1544,20 @@ void LLProfileImagePicker::notify(const std::vector<std::string>& filenames)
const S32 MAX_DIM = 256;
if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, MAX_DIM))
{
- //todo: image not supported notification
- LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", failed to open image" << LL_ENDL;
+ LLSD notif_args;
+ notif_args["REASON"] = LLImage::getLastError().c_str();
+ LLNotificationsUtil::add("CannotUploadTexture", notif_args);
+ LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", " << notif_args["REASON"].asString() << LL_ENDL;
return;
}
std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);
if (cap_url.empty())
{
- LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", no cap found" << LL_ENDL;
+ LLSD args;
+ args["CAPABILITY"] = PROFILE_IMAGE_UPLOAD_CAP;
+ LLNotificationsUtil::add("RegionCapabilityRequestError", args);
+ LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", no cap found" << LL_ENDL;
return;
}
@@ -1955,30 +1960,16 @@ void LLPanelProfileSecondLife::onShowTexturePicker()
mFloaterTexturePickerHandle = texture_floaterp->getHandle();
- texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLUUID id)
+ texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&)
{
if (op == LLTextureCtrl::TEXTURE_SELECT)
{
- LLUUID image_asset_id;
- LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterTexturePickerHandle.get();
- if (floaterp)
- {
- if (id.notNull())
- {
- image_asset_id = id;
- }
- else
- {
- image_asset_id = floaterp->getAssetID();
- }
- }
-
- onCommitProfileImage(image_asset_id);
+ onCommitProfileImage(asset_id);
}
});
texture_floaterp->setLocalTextureEnabled(FALSE);
texture_floaterp->setBakeTextureEnabled(FALSE);
- texture_floaterp->setCanApply(false, true);
+ texture_floaterp->setCanApply(false, true, false);
parent_floater->addDependentFloater(mFloaterTexturePickerHandle);
@@ -2193,7 +2184,7 @@ LLPanelProfileFirstLife::~LLPanelProfileFirstLife()
BOOL LLPanelProfileFirstLife::postBuild()
{
mDescriptionEdit = getChild<LLTextEditor>("fl_description_edit");
- mPicture = getChild<LLIconCtrl>("real_world_pic");
+ mPicture = getChild<LLThumbnailCtrl>("real_world_pic");
mUploadPhoto = getChild<LLButton>("fl_upload_image");
mChangePhoto = getChild<LLButton>("fl_change_image");
@@ -2296,29 +2287,15 @@ void LLPanelProfileFirstLife::onChangePhoto()
mFloaterTexturePickerHandle = texture_floaterp->getHandle();
- texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLUUID id)
+ texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&)
{
if (op == LLTextureCtrl::TEXTURE_SELECT)
{
- LLUUID image_asset_id;
- LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterTexturePickerHandle.get();
- if (floaterp)
- {
- if (id.notNull())
- {
- image_asset_id = id;
- }
- else
- {
- image_asset_id = floaterp->getAssetID();
- }
- }
-
- onCommitPhoto(image_asset_id);
+ onCommitPhoto(asset_id);
}
});
texture_floaterp->setLocalTextureEnabled(FALSE);
- texture_floaterp->setCanApply(false, true);
+ texture_floaterp->setCanApply(false, true, false);
parent_floater->addDependentFloater(mFloaterTexturePickerHandle);
diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h
index d32bb943bd..11632a10ae 100644
--- a/indra/newview/llpanelprofile.h
+++ b/indra/newview/llpanelprofile.h
@@ -58,6 +58,7 @@ class LLTextBase;
class LLMenuButton;
class LLLineEditor;
class LLTextEditor;
+class LLThumbnailCtrl;
class LLPanelProfileClassifieds;
class LLPanelProfilePicks;
class LLViewerFetchedTexture;
@@ -192,7 +193,7 @@ private:
LLGroupList* mGroupList;
LLComboBox* mShowInSearchCombo;
- LLIconCtrl* mSecondLifePic;
+ LLThumbnailCtrl* mSecondLifePic;
LLPanel* mSecondLifePicLayout;
LLTextEditor* mDescriptionEdit;
LLMenuButton* mAgentActionMenuButton;
@@ -301,7 +302,7 @@ protected:
void onDiscardDescriptionChanges();
LLTextEditor* mDescriptionEdit;
- LLIconCtrl* mPicture;
+ LLThumbnailCtrl* mPicture;
LLButton* mUploadPhoto;
LLButton* mChangePhoto;
LLButton* mRemovePhoto;
diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp
index bc9f0cef83..5242c4fef9 100644
--- a/indra/newview/llpanelwearing.cpp
+++ b/indra/newview/llpanelwearing.cpp
@@ -40,6 +40,7 @@
#include "llinventorymodel.h"
#include "llinventoryobserver.h"
#include "llmenubutton.h"
+#include "lloutfitobserver.h"
#include "llscrolllistctrl.h"
#include "llviewermenu.h"
#include "llviewerregion.h"
@@ -218,8 +219,6 @@ LLPanelWearing::LLPanelWearing()
, mIsInitialized(false)
, mAttachmentsChangedConnection()
{
- mCategoriesObserver = new LLInventoryCategoriesObserver();
-
mGearMenu = new LLWearingGearMenu(this);
mContextMenu = new LLWearingContextMenu();
mAttachmentsMenu = new LLTempAttachmentsContextMenu(this);
@@ -231,12 +230,6 @@ LLPanelWearing::~LLPanelWearing()
delete mContextMenu;
delete mAttachmentsMenu;
- if (gInventory.containsObserver(mCategoriesObserver))
- {
- gInventory.removeObserver(mCategoriesObserver);
- }
- delete mCategoriesObserver;
-
if (mAttachmentsChangedConnection.connected())
{
mAttachmentsChangedConnection.disconnect();
@@ -281,10 +274,8 @@ void LLPanelWearing::onOpen(const LLSD& /*info*/)
if (!category)
return;
- gInventory.addObserver(mCategoriesObserver);
-
// Start observing changes in Current Outfit category.
- mCategoriesObserver->addCategory(cof, boost::bind(&LLWearableItemsList::updateList, mCOFItemsList, cof));
+ LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLWearableItemsList::updateList, mCOFItemsList, cof));
// Fetch Current Outfit contents and refresh the list to display
// initially fetched items. If not all items are fetched now
diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h
index 715404a457..18e543eec6 100644
--- a/indra/newview/llpanelwearing.h
+++ b/indra/newview/llpanelwearing.h
@@ -90,7 +90,6 @@ private:
void getAttachmentLimitsCoro(std::string url);
- LLInventoryCategoriesObserver* mCategoriesObserver;
LLWearableItemsList* mCOFItemsList;
LLScrollListCtrl* mTempItemsList;
LLWearingGearMenu* mGearMenu;
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index cab0b74cd0..ba7af63f76 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -57,7 +57,6 @@
#include "llviewerwindow.h"
#include "lldrawable.h"
#include "llfloaterinspect.h"
-#include "llfloaterproperties.h"
#include "llfloaterreporter.h"
#include "llfloaterreg.h"
#include "llfloatertools.h"
@@ -6858,8 +6857,6 @@ void dialog_refresh_all()
gMenuAttachmentOther->arrange();
}
- LLFloaterProperties::dirtyAll();
-
LLFloaterInspect* inspect_instance = LLFloaterReg::getTypedInstance<LLFloaterInspect>("inspect");
if(inspect_instance)
{
diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp
index f5212a3026..eaa23d6a6c 100644
--- a/indra/newview/llsettingsvo.cpp
+++ b/indra/newview/llsettingsvo.cpp
@@ -95,6 +95,20 @@ namespace
//=========================================================================
+void LLSettingsVOBase::createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID& parent_id, std::function<void(const LLUUID&)> created_cb)
+{
+ inventory_result_fn cb = NULL;
+
+ if (created_cb != NULL)
+ {
+ cb = [created_cb](LLUUID asset_id, LLUUID inventory_id, LLUUID object_id, LLSD results)
+ {
+ created_cb(inventory_id);
+ };
+ }
+ createNewInventoryItem(stype, parent_id, cb);
+}
+
void LLSettingsVOBase::createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID &parent_id, inventory_result_fn callback)
{
LLTransactionID tid;
diff --git a/indra/newview/llsettingsvo.h b/indra/newview/llsettingsvo.h
index 05ec0e9275..4f410ab7d9 100644
--- a/indra/newview/llsettingsvo.h
+++ b/indra/newview/llsettingsvo.h
@@ -49,6 +49,7 @@ public:
typedef std::function<void(LLInventoryItem *inv_item, LLSettingsBase::ptr_t settings, S32 status, LLExtStat extstat)> inventory_download_fn;
typedef std::function<void(LLUUID asset_id, LLUUID inventory_id, LLUUID object_id, LLSD results)> inventory_result_fn;
+ static void createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID& parent_id, std::function<void(const LLUUID&)> created_cb);
static void createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID &parent_id, inventory_result_fn callback = inventory_result_fn());
static void createInventoryItem(const LLSettingsBase::ptr_t &settings, const LLUUID &parent_id, std::string settings_name, inventory_result_fn callback = inventory_result_fn());
static void createInventoryItem(const LLSettingsBase::ptr_t &settings, U32 next_owner_perm, const LLUUID &parent_id, std::string settings_name, inventory_result_fn callback = inventory_result_fn());
diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp
index a5dcdc41ed..e970f70e92 100644
--- a/indra/newview/llsidepanelinventory.cpp
+++ b/indra/newview/llsidepanelinventory.cpp
@@ -73,6 +73,8 @@ static const char * const INBOX_LAYOUT_PANEL_NAME = "inbox_layout_panel";
static const char * const INVENTORY_LAYOUT_STACK_NAME = "inventory_layout_stack";
static const char * const MARKETPLACE_INBOX_PANEL = "marketplace_inbox";
+static bool sLoginCompleted = false;
+
//
// Helpers
//
@@ -115,21 +117,19 @@ private:
LLSidepanelInventory::LLSidepanelInventory()
: LLPanel()
- , mItemPanel(NULL)
, mPanelMainInventory(NULL)
, mInboxEnabled(false)
, mCategoriesObserver(NULL)
, mInboxAddedObserver(NULL)
+ , mInboxLayoutPanel(NULL)
{
//buildFromFile( "panel_inventory.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()
}
LLSidepanelInventory::~LLSidepanelInventory()
{
- LLLayoutPanel* inbox_layout_panel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME);
-
// Save the InventoryMainPanelHeight in settings per account
- gSavedPerAccountSettings.setS32("InventoryInboxHeight", inbox_layout_panel->getTargetDim());
+ gSavedPerAccountSettings.setS32("InventoryInboxHeight", mInboxLayoutPanel->getTargetDim());
if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver))
{
@@ -158,29 +158,11 @@ BOOL LLSidepanelInventory::postBuild()
// UI elements from inventory panel
{
mInventoryPanel = getChild<LLPanel>("sidepanel_inventory_panel");
-
- mInfoBtn = mInventoryPanel->getChild<LLButton>("info_btn");
- mInfoBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onInfoButtonClicked, this));
-
- mShareBtn = mInventoryPanel->getChild<LLButton>("share_btn");
- mShareBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShareButtonClicked, this));
-
- mShopBtn = mInventoryPanel->getChild<LLButton>("shop_btn");
- mShopBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShopButtonClicked, this));
-
- mWearBtn = mInventoryPanel->getChild<LLButton>("wear_btn");
- mWearBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onWearButtonClicked, this));
-
- mPlayBtn = mInventoryPanel->getChild<LLButton>("play_btn");
- mPlayBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onPlayButtonClicked, this));
-
- mTeleportBtn = mInventoryPanel->getChild<LLButton>("teleport_btn");
- mTeleportBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onTeleportButtonClicked, this));
mPanelMainInventory = mInventoryPanel->getChild<LLPanelMainInventory>("panel_main_inventory");
mPanelMainInventory->setSelectCallback(boost::bind(&LLSidepanelInventory::onSelectionChange, this, _1, _2));
- LLTabContainer* tabs = mPanelMainInventory->getChild<LLTabContainer>("inventory filter tabs");
- tabs->setCommitCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this));
+ //LLTabContainer* tabs = mPanelMainInventory->getChild<LLTabContainer>("inventory filter tabs");
+ //tabs->setCommitCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this));
/*
EXT-4846 : "Can we suppress the "Landmarks" and "My Favorites" folder since they have their own Task Panel?"
@@ -190,25 +172,7 @@ BOOL LLSidepanelInventory::postBuild()
my_inventory_panel->addHideFolderType(LLFolderType::FT_FAVORITE);
*/
- LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this));
- }
-
- // UI elements from item panel
- {
- mItemPanel = getChild<LLSidepanelItemInfo>("sidepanel__item_panel");
-
- LLButton* back_btn = mItemPanel->getChild<LLButton>("back_btn");
- back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this));
- }
-
- // UI elements from task panel
- {
- mTaskPanel = findChild<LLSidepanelTaskInfo>("sidepanel__task_panel");
- if (mTaskPanel)
- {
- LLButton* back_btn = mTaskPanel->getChild<LLButton>("back_btn");
- back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this));
- }
+ //LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this));
}
// Received items inbox setup
@@ -220,38 +184,55 @@ BOOL LLSidepanelInventory::postBuild()
inbox_button->setCommitCallback(boost::bind(&LLSidepanelInventory::onToggleInboxBtn, this));
- // Get the previous inbox state from "InventoryInboxToggleState" setting.
- bool is_inbox_collapsed = !inbox_button->getToggleState();
+ // For main Inventory floater: Get the previous inbox state from "InventoryInboxToggleState" setting.
+ // For additional Inventory floaters: Collapsed state is default.
+ bool is_inbox_collapsed = !inbox_button->getToggleState() || sLoginCompleted;
// Restore the collapsed inbox panel state
- LLLayoutPanel* inbox_panel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME);
- inv_stack->collapsePanel(inbox_panel, is_inbox_collapsed);
- if (!is_inbox_collapsed)
- {
- inbox_panel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight"));
- }
-
- // Set the inbox visible based on debug settings (final setting comes from http request below)
- enableInbox(gSavedSettings.getBOOL("InventoryDisplayInbox"));
-
- // Trigger callback for after login so we can setup to track inbox changes after initial inventory load
- LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLSidepanelInventory::updateInbox, this));
+ mInboxLayoutPanel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME);
+ inv_stack->collapsePanel(mInboxLayoutPanel, is_inbox_collapsed);
+ if (!is_inbox_collapsed)
+ {
+ mInboxLayoutPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight"));
+ }
+
+ if (sLoginCompleted)
+ {
+ //save the state of Inbox panel only for main Inventory floater
+ inbox_button->removeControlVariable();
+ inbox_button->setToggleState(false);
+ updateInbox();
+ }
+ else
+ {
+ // Trigger callback for after login so we can setup to track inbox changes after initial inventory load
+ LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLSidepanelInventory::updateInbox, this));
+ }
}
gSavedSettings.getControl("InventoryDisplayInbox")->getCommitSignal()->connect(boost::bind(&handleInventoryDisplayInboxChanged));
- // Update the verbs buttons state.
- updateVerbs();
+ LLFloater *floater = dynamic_cast<LLFloater*>(getParent());
+ if (floater && floater->getKey().isUndefined() && !sLoginCompleted)
+ {
+ // Prefill inventory for primary inventory floater
+ // Other floaters should fill on visibility change
+ //
+ // see get_instance_num();
+ // Primary inventory floater will have undefined key
+ initInventoryViews();
+ }
return TRUE;
}
void LLSidepanelInventory::updateInbox()
{
+ sLoginCompleted = true;
//
// Track inbox folder changes
//
- const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, true);
+ const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX);
// Set up observer to listen for creation of inbox if it doesn't exist
if (inbox_id.isNull())
@@ -336,8 +317,20 @@ void LLSidepanelInventory::enableInbox(bool enabled)
{
mInboxEnabled = enabled;
- LLLayoutPanel * inbox_layout_panel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME);
- inbox_layout_panel->setVisible(enabled);
+ if(!enabled || !mPanelMainInventory->isSingleFolderMode())
+ {
+ toggleInbox();
+ }
+}
+
+void LLSidepanelInventory::hideInbox()
+{
+ mInboxLayoutPanel->setVisible(false);
+}
+
+void LLSidepanelInventory::toggleInbox()
+{
+ mInboxLayoutPanel->setVisible(mInboxEnabled);
}
void LLSidepanelInventory::openInbox()
@@ -367,25 +360,24 @@ void LLSidepanelInventory::onInboxChanged(const LLUUID& inbox_id)
void LLSidepanelInventory::onToggleInboxBtn()
{
LLButton* inboxButton = getChild<LLButton>(INBOX_BUTTON_NAME);
- LLLayoutPanel* inboxPanel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME);
LLLayoutStack* inv_stack = getChild<LLLayoutStack>(INVENTORY_LAYOUT_STACK_NAME);
const bool inbox_expanded = inboxButton->getToggleState();
// Expand/collapse the indicated panel
- inv_stack->collapsePanel(inboxPanel, !inbox_expanded);
+ inv_stack->collapsePanel(mInboxLayoutPanel, !inbox_expanded);
if (inbox_expanded)
{
- inboxPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight"));
- if (inboxPanel->isInVisibleChain())
+ mInboxLayoutPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight"));
+ if (mInboxLayoutPanel->isInVisibleChain())
{
gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected());
}
}
else
{
- gSavedPerAccountSettings.setS32("InventoryInboxHeight", inboxPanel->getTargetDim());
+ gSavedPerAccountSettings.setS32("InventoryInboxHeight", mInboxLayoutPanel->getTargetDim());
}
}
@@ -409,47 +401,7 @@ void LLSidepanelInventory::onOpen(const LLSD& key)
}
#endif
- if(key.size() == 0)
- return;
-
- mItemPanel->reset();
-
- if (key.has("id"))
- {
- mItemPanel->setItemID(key["id"].asUUID());
- if (key.has("object"))
- {
- mItemPanel->setObjectID(key["object"].asUUID());
- }
- showItemInfoPanel();
- }
- if (key.has("task"))
- {
- if (mTaskPanel)
- mTaskPanel->setObjectSelection(LLSelectMgr::getInstance()->getSelection());
- showTaskInfoPanel();
- }
-}
-
-void LLSidepanelInventory::onInfoButtonClicked()
-{
- LLInventoryItem *item = getSelectedItem();
- if (item)
- {
- mItemPanel->reset();
- mItemPanel->setItemID(item->getUUID());
- showItemInfoPanel();
- }
-}
-
-void LLSidepanelInventory::onShareButtonClicked()
-{
- LLAvatarActions::shareWithAvatars(this);
-}
-
-void LLSidepanelInventory::onShopButtonClicked()
-{
- LLWeb::loadURL(gSavedSettings.getString("MarketplaceURL"));
+ gAgent.showLatestFeatureNotification("inventory");
}
void LLSidepanelInventory::performActionOnSelection(const std::string &action)
@@ -471,47 +423,6 @@ void LLSidepanelInventory::performActionOnSelection(const std::string &action)
static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(mPanelMainInventory->getActivePanel()->getModel(), action);
}
-void LLSidepanelInventory::onWearButtonClicked()
-{
- // Get selected items set.
- const std::set<LLUUID> selected_uuids_set = LLAvatarActions::getInventorySelectedUUIDs();
- if (selected_uuids_set.empty()) return; // nothing selected
-
- // Convert the set to a vector.
- uuid_vec_t selected_uuids_vec;
- for (std::set<LLUUID>::const_iterator it = selected_uuids_set.begin(); it != selected_uuids_set.end(); ++it)
- {
- selected_uuids_vec.push_back(*it);
- }
-
- // Wear all selected items.
- wear_multiple(selected_uuids_vec, true);
-}
-
-void LLSidepanelInventory::onPlayButtonClicked()
-{
- const LLInventoryItem *item = getSelectedItem();
- if (!item)
- {
- return;
- }
-
- switch(item->getInventoryType())
- {
- case LLInventoryType::IT_GESTURE:
- performActionOnSelection("play");
- break;
- default:
- performActionOnSelection("open");
- break;
- }
-}
-
-void LLSidepanelInventory::onTeleportButtonClicked()
-{
- performActionOnSelection("teleport");
-}
-
void LLSidepanelInventory::onBackButtonClicked()
{
showInventoryPanel();
@@ -519,102 +430,17 @@ void LLSidepanelInventory::onBackButtonClicked()
void LLSidepanelInventory::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action)
{
- updateVerbs();
-}
-
-void LLSidepanelInventory::showItemInfoPanel()
-{
- mItemPanel->setVisible(TRUE);
- if (mTaskPanel)
- mTaskPanel->setVisible(FALSE);
- mInventoryPanel->setVisible(FALSE);
- mItemPanel->dirty();
- mItemPanel->setIsEditing(FALSE);
-}
-
-void LLSidepanelInventory::showTaskInfoPanel()
-{
- mItemPanel->setVisible(FALSE);
- mInventoryPanel->setVisible(FALSE);
-
- if (mTaskPanel)
- {
- mTaskPanel->setVisible(TRUE);
- mTaskPanel->dirty();
- mTaskPanel->setIsEditing(FALSE);
- }
}
void LLSidepanelInventory::showInventoryPanel()
{
- mItemPanel->setVisible(FALSE);
- if (mTaskPanel)
- mTaskPanel->setVisible(FALSE);
mInventoryPanel->setVisible(TRUE);
- updateVerbs();
}
-void LLSidepanelInventory::updateVerbs()
+void LLSidepanelInventory::initInventoryViews()
{
- mInfoBtn->setEnabled(FALSE);
- mShareBtn->setEnabled(FALSE);
-
- mWearBtn->setVisible(FALSE);
- mWearBtn->setEnabled(FALSE);
- mPlayBtn->setVisible(FALSE);
- mPlayBtn->setEnabled(FALSE);
- mPlayBtn->setToolTip(std::string(""));
- mTeleportBtn->setVisible(FALSE);
- mTeleportBtn->setEnabled(FALSE);
- mShopBtn->setVisible(TRUE);
-
- mShareBtn->setEnabled(canShare());
-
- const LLInventoryItem *item = getSelectedItem();
- if (!item)
- return;
-
- bool is_single_selection = getSelectedCount() == 1;
-
- mInfoBtn->setEnabled(is_single_selection);
-
- switch(item->getInventoryType())
- {
- case LLInventoryType::IT_WEARABLE:
- case LLInventoryType::IT_OBJECT:
- case LLInventoryType::IT_ATTACHMENT:
- mWearBtn->setVisible(TRUE);
- mWearBtn->setEnabled(canWearSelected());
- mShopBtn->setVisible(FALSE);
- break;
- case LLInventoryType::IT_SOUND:
- mPlayBtn->setVisible(TRUE);
- mPlayBtn->setEnabled(TRUE);
- mPlayBtn->setToolTip(LLTrans::getString("InventoryPlaySoundTooltip"));
- mShopBtn->setVisible(FALSE);
- break;
- case LLInventoryType::IT_GESTURE:
- mPlayBtn->setVisible(TRUE);
- mPlayBtn->setEnabled(TRUE);
- mPlayBtn->setToolTip(LLTrans::getString("InventoryPlayGestureTooltip"));
- mShopBtn->setVisible(FALSE);
- break;
- case LLInventoryType::IT_ANIMATION:
- mPlayBtn->setVisible(TRUE);
- mPlayBtn->setEnabled(TRUE);
- mPlayBtn->setEnabled(TRUE);
- mPlayBtn->setToolTip(LLTrans::getString("InventoryPlayAnimationTooltip"));
- mShopBtn->setVisible(FALSE);
- break;
- case LLInventoryType::IT_LANDMARK:
- mTeleportBtn->setVisible(TRUE);
- mTeleportBtn->setEnabled(TRUE);
- mShopBtn->setVisible(FALSE);
- break;
- default:
- break;
- }
+ mPanelMainInventory->initInventoryViews();
}
bool LLSidepanelInventory::canShare()
@@ -737,12 +563,10 @@ void LLSidepanelInventory::clearSelections(bool clearMain, bool clearInbox)
}
}
- if (clearInbox && mInboxEnabled && mInventoryPanelInbox.get())
+ if (clearInbox && mInboxEnabled && !mInventoryPanelInbox.isDead())
{
mInventoryPanelInbox.get()->getRootFolder()->clearSelection();
}
-
- updateVerbs();
}
std::set<LLFolderViewItem*> LLSidepanelInventory::getInboxSelectionList()
diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h
index a3cd20a2c6..08989bb6af 100644
--- a/indra/newview/llsidepanelinventory.h
+++ b/indra/newview/llsidepanelinventory.h
@@ -66,9 +66,8 @@ public:
void clearSelections(bool clearMain, bool clearInbox);
std::set<LLFolderViewItem*> getInboxSelectionList();
- void showItemInfoPanel();
- void showTaskInfoPanel();
void showInventoryPanel();
+ void initInventoryViews();
// checks can share selected item(s)
bool canShare();
@@ -76,13 +75,13 @@ public:
void onToggleInboxBtn();
void enableInbox(bool enabled);
+ void toggleInbox();
+ void hideInbox();
void openInbox();
bool isInboxEnabled() const { return mInboxEnabled; }
- void updateVerbs();
-
static void cleanup();
protected:
@@ -103,27 +102,14 @@ protected:
private:
LLPanel* mInventoryPanel; // Main inventory view
LLHandle<LLInventoryPanel> mInventoryPanelInbox;
- LLSidepanelItemInfo* mItemPanel; // Individual item view
- LLSidepanelTaskInfo* mTaskPanel; // Individual in-world object view
LLPanelMainInventory* mPanelMainInventory;
+ LLLayoutPanel* mInboxLayoutPanel;
+
protected:
- void onInfoButtonClicked();
- void onShareButtonClicked();
- void onShopButtonClicked();
- void onWearButtonClicked();
- void onPlayButtonClicked();
- void onTeleportButtonClicked();
void onBackButtonClicked();
private:
- LLButton* mInfoBtn;
- LLButton* mShareBtn;
- LLButton* mWearBtn;
- LLButton* mPlayBtn;
- LLButton* mTeleportBtn;
- LLButton* mShopBtn;
-
bool mInboxEnabled;
LLInventoryCategoriesObserver* mCategoriesObserver;
diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp
index b23e24a222..d6d5a4ef2d 100644
--- a/indra/newview/llsidepaneliteminfo.cpp
+++ b/indra/newview/llsidepaneliteminfo.cpp
@@ -31,16 +31,23 @@
#include "llagent.h"
#include "llavataractions.h"
+#include "llavatarnamecache.h"
#include "llbutton.h"
+#include "llcallbacklist.h"
#include "llcombobox.h"
+#include "llfloater.h"
#include "llfloaterreg.h"
#include "llgroupactions.h"
+#include "llgroupmgr.h"
+#include "lliconctrl.h"
#include "llinventorydefines.h"
+#include "llinventoryicon.h"
#include "llinventorymodel.h"
#include "llinventoryobserver.h"
#include "lllineeditor.h"
#include "llradiogroup.h"
#include "llslurl.h"
+#include "lltexteditor.h"
#include "llviewercontrol.h"
#include "llviewerinventory.h"
#include "llviewerobjectlist.h"
@@ -73,49 +80,6 @@ private:
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLItemPropertiesObserver
-//
-// Helper class to watch for changes to the item.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLItemPropertiesObserver : public LLInventoryObserver
-{
-public:
- LLItemPropertiesObserver(LLSidepanelItemInfo* floater)
- : mFloater(floater)
- {
- gInventory.addObserver(this);
- }
- virtual ~LLItemPropertiesObserver()
- {
- gInventory.removeObserver(this);
- }
- virtual void changed(U32 mask);
-private:
- LLSidepanelItemInfo* mFloater; // Not a handle because LLSidepanelItemInfo is managing LLItemPropertiesObserver
-};
-
-void LLItemPropertiesObserver::changed(U32 mask)
-{
- const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs();
- std::set<LLUUID>::const_iterator it;
-
- const LLUUID& item_id = mFloater->getItemID();
-
- for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++)
- {
- // set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288)
- if (*it == item_id)
- {
- // if there's a change we're interested in.
- if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
- {
- mFloater->dirty();
- }
- }
- }
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLObjectInventoryObserver
//
// Helper class to watch for changes in an object inventory.
@@ -158,36 +122,48 @@ static LLPanelInjector<LLSidepanelItemInfo> t_item_info("sidepanel_item_info");
// Default constructor
LLSidepanelItemInfo::LLSidepanelItemInfo(const LLPanel::Params& p)
- : LLSidepanelInventorySubpanel(p)
+ : LLPanel(p)
, mItemID(LLUUID::null)
, mObjectInventoryObserver(NULL)
, mUpdatePendingId(-1)
+ , mIsDirty(false) /*Not ready*/
+ , mParentFloater(NULL)
{
- mPropertiesObserver = new LLItemPropertiesObserver(this);
+ gInventory.addObserver(this);
+ gIdleCallbacks.addFunction(&LLSidepanelItemInfo::onIdle, (void*)this);
}
// Destroys the object
LLSidepanelItemInfo::~LLSidepanelItemInfo()
{
- delete mPropertiesObserver;
- mPropertiesObserver = NULL;
+ gInventory.removeObserver(this);
+ gIdleCallbacks.deleteFunction(&LLSidepanelItemInfo::onIdle, (void*)this);
stopObjectInventoryObserver();
+
+ if (mOwnerCacheConnection.connected())
+ {
+ mOwnerCacheConnection.disconnect();
+ }
+ if (mCreatorCacheConnection.connected())
+ {
+ mCreatorCacheConnection.disconnect();
+ }
}
// virtual
BOOL LLSidepanelItemInfo::postBuild()
{
- LLSidepanelInventorySubpanel::postBuild();
-
+ mChangeThumbnailBtn = getChild<LLUICtrl>("change_thumbnail_btn");
+ mItemTypeIcon = getChild<LLIconCtrl>("item_type_icon");
+ mLabelOwnerName = getChild<LLTextBox>("LabelOwnerName");
+ mLabelCreatorName = getChild<LLTextBox>("LabelCreatorName");
+
getChild<LLLineEditor>("LabelItemName")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
getChild<LLUICtrl>("LabelItemName")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onCommitName,this));
- getChild<LLLineEditor>("LabelItemDesc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
getChild<LLUICtrl>("LabelItemDesc")->setCommitCallback(boost::bind(&LLSidepanelItemInfo:: onCommitDescription, this));
- // Creator information
- getChild<LLUICtrl>("BtnCreator")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onClickCreator,this));
- // owner information
- getChild<LLUICtrl>("BtnOwner")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onClickOwner,this));
+ // Thumnail edition
+ mChangeThumbnailBtn->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onEditThumbnail, this));
// acquired date
// owner permissions
// Permissions debug text
@@ -226,6 +202,12 @@ void LLSidepanelItemInfo::setItemID(const LLUUID& item_id)
mItemID = item_id;
mUpdatePendingId = -1;
}
+ dirty();
+}
+
+void LLSidepanelItemInfo::setParentFloater(LLFloater* parent)
+{
+ mParentFloater = parent;
}
const LLUUID& LLSidepanelItemInfo::getObjectID() const
@@ -249,12 +231,11 @@ void LLSidepanelItemInfo::onUpdateCallback(const LLUUID& item_id, S32 received_u
void LLSidepanelItemInfo::reset()
{
- LLSidepanelInventorySubpanel::reset();
-
mObjectID = LLUUID::null;
mItemID = LLUUID::null;
stopObjectInventoryObserver();
+ dirty();
}
void LLSidepanelItemInfo::refresh()
@@ -262,60 +243,37 @@ void LLSidepanelItemInfo::refresh()
LLViewerInventoryItem* item = findItem();
if(item)
{
- refreshFromItem(item);
- updateVerbs();
+ const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ bool in_trash = (item->getUUID() == trash_id) || gInventory.isObjectDescendentOf(item->getUUID(), trash_id);
+ if (in_trash && mParentFloater)
+ {
+ // Close properties when moving to trash
+ // Aren't supposed to view properties from trash
+ mParentFloater->closeFloater();
+ }
+ else
+ {
+ refreshFromItem(item);
+ }
return;
}
- else
- {
- if (getIsEditing())
- {
- setIsEditing(FALSE);
- }
- }
-
- if (!getIsEditing())
- {
- const std::string no_item_names[]={
- "LabelItemName",
- "LabelItemDesc",
- "LabelCreatorName",
- "LabelOwnerName"
- };
-
- for(size_t t=0; t<LL_ARRAY_SIZE(no_item_names); ++t)
- {
- getChildView(no_item_names[t])->setEnabled(false);
- }
-
- setPropertiesFieldsEnabled(false);
-
- const std::string hide_names[]={
- "BaseMaskDebug",
- "OwnerMaskDebug",
- "GroupMaskDebug",
- "EveryoneMaskDebug",
- "NextMaskDebug"
- };
- for(size_t t=0; t<LL_ARRAY_SIZE(hide_names); ++t)
- {
- getChildView(hide_names[t])->setVisible(false);
- }
- }
- if (!item)
- {
- const std::string no_edit_mode_names[]={
- "BtnCreator",
- "BtnOwner",
- };
- for(size_t t=0; t<LL_ARRAY_SIZE(no_edit_mode_names); ++t)
- {
- getChildView(no_edit_mode_names[t])->setEnabled(false);
- }
- }
-
- updateVerbs();
+ if (mObjectID.notNull())
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if (object)
+ {
+ // Object exists, but object's content is not nessesary
+ // loaded, so assume item exists as well
+ return;
+ }
+ }
+
+ if (mParentFloater)
+ {
+ // if we failed to get item, it likely no longer exists
+ mParentFloater->closeFloater();
+ }
}
void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
@@ -333,7 +291,7 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
}
// do not enable the UI for incomplete items.
- BOOL is_complete = item->isFinished();
+ bool is_complete = item->isFinished();
const BOOL cannot_restrict_permissions = LLInventoryType::cannotRestrictPermissions(item->getInventoryType());
const BOOL is_calling_card = (item->getInventoryType() == LLInventoryType::IT_CALLINGCARD);
const BOOL is_settings = (item->getInventoryType() == LLInventoryType::IT_SETTINGS);
@@ -385,8 +343,22 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
getChild<LLUICtrl>("LabelItemName")->setValue(item->getName());
getChildView("LabelItemDescTitle")->setEnabled(TRUE);
getChildView("LabelItemDesc")->setEnabled(is_modifiable);
- getChildView("IconLocked")->setVisible(!is_modifiable);
getChild<LLUICtrl>("LabelItemDesc")->setValue(item->getDescription());
+ getChild<LLUICtrl>("item_thumbnail")->setValue(item->getThumbnailUUID());
+
+ LLUIImagePtr icon_img = LLInventoryIcon::getIcon(item->getType(), item->getInventoryType(), item->getFlags(), FALSE);
+ mItemTypeIcon->setImage(icon_img);
+
+ // Style for creator and owner links
+ LLStyle::Params style_params;
+ LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ style_params.color = link_color;
+ style_params.readonly_color = link_color;
+ style_params.is_link = true; // link will be added later
+ const LLFontGL* fontp = mLabelCreatorName->getFont();
+ style_params.font.name = LLFontGL::nameFromFont(fontp);
+ style_params.font.size = LLFontGL::sizeFromFont(fontp);
+ style_params.font.style = "UNDERLINE";
//////////////////
// CREATOR NAME //
@@ -397,19 +369,34 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
if (item->getCreatorUUID().notNull())
{
LLUUID creator_id = item->getCreatorUUID();
- std::string name =
- LLSLURL("agent", creator_id, "completename").getSLURLString();
- getChildView("BtnCreator")->setEnabled(TRUE);
+ std::string slurl =
+ LLSLURL("agent", creator_id, "inspect").getSLURLString();
+
+ style_params.link_href = slurl;
+
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(creator_id, &av_name))
+ {
+ updateCreatorName(creator_id, av_name, style_params);
+ }
+ else
+ {
+ if (mCreatorCacheConnection.connected())
+ {
+ mCreatorCacheConnection.disconnect();
+ }
+ mLabelCreatorName->setText(LLTrans::getString("None"));
+ mCreatorCacheConnection = LLAvatarNameCache::get(creator_id, boost::bind(&LLSidepanelItemInfo::updateCreatorName, this, _1, _2, style_params));
+ }
+
getChildView("LabelCreatorTitle")->setEnabled(TRUE);
- getChildView("LabelCreatorName")->setEnabled(FALSE);
- getChild<LLUICtrl>("LabelCreatorName")->setValue(name);
+ mLabelCreatorName->setEnabled(TRUE);
}
else
{
- getChildView("BtnCreator")->setEnabled(FALSE);
getChildView("LabelCreatorTitle")->setEnabled(FALSE);
- getChildView("LabelCreatorName")->setEnabled(FALSE);
- getChild<LLUICtrl>("LabelCreatorName")->setValue(getString("unknown_multiple"));
+ mLabelCreatorName->setEnabled(FALSE);
+ mLabelCreatorName->setValue(getString("unknown_multiple"));
}
////////////////
@@ -417,28 +404,60 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
////////////////
if(perm.isOwned())
{
- std::string name;
+ std::string slurl;
if (perm.isGroupOwned())
{
- gCacheName->getGroupName(perm.getGroup(), name);
+ LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(perm.getGroup());
+
+ slurl = LLSLURL("group", perm.getGroup(), "inspect").getSLURLString();
+ style_params.link_href = slurl;
+ if (group_data && group_data->isGroupPropertiesDataComplete())
+ {
+ mLabelOwnerName->setText(group_data->mName, style_params);
+ }
+ else
+ {
+ // Triggers refresh
+ LLGroupMgr::getInstance()->sendGroupPropertiesRequest(perm.getGroup());
+
+ std::string name;
+ gCacheName->getGroupName(perm.getGroup(), name);
+ mLabelOwnerName->setText(name, style_params);
+ }
}
else
{
LLUUID owner_id = perm.getOwner();
- name = LLSLURL("agent", owner_id, "completename").getSLURLString();
+ slurl = LLSLURL("agent", owner_id, "inspect").getSLURLString();
+
+ style_params.link_href = slurl;
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(owner_id, &av_name))
+ {
+ updateOwnerName(owner_id, av_name, style_params);
+ }
+ else
+ {
+ if (mOwnerCacheConnection.connected())
+ {
+ mOwnerCacheConnection.disconnect();
+ }
+ mLabelOwnerName->setText(LLTrans::getString("None"));
+ mOwnerCacheConnection = LLAvatarNameCache::get(owner_id, boost::bind(&LLSidepanelItemInfo::updateOwnerName, this, _1, _2, style_params));
+ }
}
- getChildView("BtnOwner")->setEnabled(TRUE);
getChildView("LabelOwnerTitle")->setEnabled(TRUE);
- getChildView("LabelOwnerName")->setEnabled(FALSE);
- getChild<LLUICtrl>("LabelOwnerName")->setValue(name);
+ mLabelOwnerName->setEnabled(TRUE);
}
else
{
- getChildView("BtnOwner")->setEnabled(FALSE);
getChildView("LabelOwnerTitle")->setEnabled(FALSE);
- getChildView("LabelOwnerName")->setEnabled(FALSE);
- getChild<LLUICtrl>("LabelOwnerName")->setValue(getString("public"));
+ mLabelOwnerName->setEnabled(FALSE);
+ mLabelOwnerName->setValue(getString("public"));
}
+
+ // Not yet supported for task inventories
+ mChangeThumbnailBtn->setEnabled(mObjectID.isNull() && ALEXANDRIA_LINDEN_ID != perm.getOwner());
////////////
// ORIGIN //
@@ -548,6 +567,8 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
if( gSavedSettings.getBOOL("DebugPermissions") )
{
+ childSetVisible("layout_debug_permissions", true);
+
BOOL slam_perm = FALSE;
BOOL overwrite_group = FALSE;
BOOL overwrite_everyone = FALSE;
@@ -565,38 +586,29 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
perm_string = "B: ";
perm_string += mask_to_string(base_mask);
getChild<LLUICtrl>("BaseMaskDebug")->setValue(perm_string);
- getChildView("BaseMaskDebug")->setVisible(TRUE);
perm_string = "O: ";
perm_string += mask_to_string(owner_mask);
getChild<LLUICtrl>("OwnerMaskDebug")->setValue(perm_string);
- getChildView("OwnerMaskDebug")->setVisible(TRUE);
perm_string = "G";
perm_string += overwrite_group ? "*: " : ": ";
perm_string += mask_to_string(group_mask);
getChild<LLUICtrl>("GroupMaskDebug")->setValue(perm_string);
- getChildView("GroupMaskDebug")->setVisible(TRUE);
perm_string = "E";
perm_string += overwrite_everyone ? "*: " : ": ";
perm_string += mask_to_string(everyone_mask);
getChild<LLUICtrl>("EveryoneMaskDebug")->setValue(perm_string);
- getChildView("EveryoneMaskDebug")->setVisible(TRUE);
perm_string = "N";
perm_string += slam_perm ? "*: " : ": ";
perm_string += mask_to_string(next_owner_mask);
getChild<LLUICtrl>("NextMaskDebug")->setValue(perm_string);
- getChildView("NextMaskDebug")->setVisible(TRUE);
}
else
{
- getChildView("BaseMaskDebug")->setVisible(FALSE);
- getChildView("OwnerMaskDebug")->setVisible(FALSE);
- getChildView("GroupMaskDebug")->setVisible(FALSE);
- getChildView("EveryoneMaskDebug")->setVisible(FALSE);
- getChildView("NextMaskDebug")->setVisible(FALSE);
+ childSetVisible("layout_debug_permissions", false);
}
/////////////
@@ -731,6 +743,68 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
}
}
+void LLSidepanelItemInfo::updateCreatorName(const LLUUID& creator_id, const LLAvatarName& creator_name, const LLStyle::Params& style_params)
+{
+ if (mCreatorCacheConnection.connected())
+ {
+ mCreatorCacheConnection.disconnect();
+ }
+ std::string name = creator_name.getCompleteName();
+ mLabelCreatorName->setText(name, style_params);
+}
+
+void LLSidepanelItemInfo::updateOwnerName(const LLUUID& owner_id, const LLAvatarName& owner_name, const LLStyle::Params& style_params)
+{
+ if (mOwnerCacheConnection.connected())
+ {
+ mOwnerCacheConnection.disconnect();
+ }
+ std::string name = owner_name.getCompleteName();
+ mLabelOwnerName->setText(name, style_params);
+}
+
+void LLSidepanelItemInfo::changed(U32 mask)
+{
+ const LLUUID& item_id = getItemID();
+ if (getObjectID().notNull() || item_id.isNull())
+ {
+ // Task inventory or not set up yet
+ return;
+ }
+
+ const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs();
+ std::set<LLUUID>::const_iterator it;
+
+ for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++)
+ {
+ // set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288)
+ if (*it == item_id)
+ {
+ // if there's a change we're interested in.
+ if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
+ {
+ dirty();
+ }
+ }
+ }
+}
+
+void LLSidepanelItemInfo::dirty()
+{
+ mIsDirty = true;
+}
+
+// static
+void LLSidepanelItemInfo::onIdle( void* user_data )
+{
+ LLSidepanelItemInfo* self = reinterpret_cast<LLSidepanelItemInfo*>(user_data);
+
+ if( self->mIsDirty )
+ {
+ self->refresh();
+ self->mIsDirty = false;
+ }
+}
void LLSidepanelItemInfo::setAssociatedExperience( LLHandle<LLSidepanelItemInfo> hInfo, const LLSD& experience )
{
@@ -853,7 +927,7 @@ void LLSidepanelItemInfo::onCommitDescription()
LLViewerInventoryItem* item = findItem();
if(!item) return;
- LLLineEditor* labelItemDesc = getChild<LLLineEditor>("LabelItemDesc");
+ LLTextEditor* labelItemDesc = getChild<LLTextEditor>("LabelItemDesc");
if(!labelItemDesc)
{
return;
@@ -966,7 +1040,14 @@ void LLSidepanelItemInfo::updatePermissions()
}
}
-// static
+void LLSidepanelItemInfo::onEditThumbnail()
+{
+ LLSD data;
+ data["task_id"] = mObjectID;
+ data["item_id"] = mItemID;
+ LLFloaterReg::showInstance("change_item_thumbnail", data);
+}
+
void LLSidepanelItemInfo::onCommitSaleInfo(LLUICtrl* ctrl)
{
if (ctrl)
diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h
index 5f29254182..b916f44520 100644
--- a/indra/newview/llsidepaneliteminfo.h
+++ b/indra/newview/llsidepaneliteminfo.h
@@ -27,42 +27,55 @@
#ifndef LL_LLSIDEPANELITEMINFO_H
#define LL_LLSIDEPANELITEMINFO_H
-#include "llsidepanelinventorysubpanel.h"
+#include "llinventoryobserver.h"
+#include "llpanel.h"
+#include "llstyle.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLSidepanelItemInfo
// Object properties for inventory side panel.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLAvatarName;
class LLButton;
+class LLFloater;
+class LLIconCtrl;
class LLViewerInventoryItem;
class LLItemPropertiesObserver;
class LLObjectInventoryObserver;
class LLViewerObject;
class LLPermissions;
+class LLTextBox;
-class LLSidepanelItemInfo : public LLSidepanelInventorySubpanel
+class LLSidepanelItemInfo : public LLPanel, public LLInventoryObserver
{
public:
LLSidepanelItemInfo(const LLPanel::Params& p = getDefaultParams());
virtual ~LLSidepanelItemInfo();
- /*virtual*/ BOOL postBuild();
+ /*virtual*/ BOOL postBuild() override;
/*virtual*/ void reset();
void setObjectID(const LLUUID& object_id);
void setItemID(const LLUUID& item_id);
- void setEditMode(BOOL edit);
+ void setParentFloater(LLFloater* parent); // For simplicity
const LLUUID& getObjectID() const;
const LLUUID& getItemID() const;
// if received update and item id (from callback) matches internal ones, update UI
void onUpdateCallback(const LLUUID& item_id, S32 received_update_id);
+
+ void changed(U32 mask) override;
+ void dirty();
+
+ static void onIdle( void* user_data );
+ void updateOwnerName(const LLUUID& owner_id, const LLAvatarName& owner_name, const LLStyle::Params& style_params);
+ void updateCreatorName(const LLUUID& creator_id, const LLAvatarName& creator_name, const LLStyle::Params& style_params);
protected:
- /*virtual*/ void refresh();
- /*virtual*/ void save();
+ void refresh() override;
+ void save();
LLViewerInventoryItem* findItem() const;
LLViewerObject* findObject() const;
@@ -75,14 +88,23 @@ private:
void startObjectInventoryObserver();
void stopObjectInventoryObserver();
void setPropertiesFieldsEnabled(bool enabled);
+
+ boost::signals2::connection mOwnerCacheConnection;
+ boost::signals2::connection mCreatorCacheConnection;
LLUUID mItemID; // inventory UUID for the inventory item.
LLUUID mObjectID; // in-world task UUID, or null if in agent inventory.
- LLItemPropertiesObserver* mPropertiesObserver; // for syncing changes to item
LLObjectInventoryObserver* mObjectInventoryObserver; // for syncing changes to items inside an object
// We can send multiple properties updates simultaneously, make sure only last response counts and there won't be a race condition.
S32 mUpdatePendingId;
+ bool mIsDirty; // item properties need to be updated
+ LLFloater* mParentFloater;
+
+ LLUICtrl* mChangeThumbnailBtn;
+ LLIconCtrl* mItemTypeIcon;
+ LLTextBox* mLabelOwnerName;
+ LLTextBox* mLabelCreatorName;
//
// UI Elements
@@ -94,6 +116,7 @@ protected:
void onCommitDescription();
void onCommitPermissions(LLUICtrl* ctrl);
void updatePermissions();
+ void onEditThumbnail();
void onCommitSaleInfo(LLUICtrl* ctrl);
void updateSaleInfo();
void onCommitChanges(LLPointer<LLViewerInventoryItem> item);
diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp
index 225751ab92..1d6b3cd80c 100644
--- a/indra/newview/llsidepaneltaskinfo.cpp
+++ b/indra/newview/llsidepaneltaskinfo.cpp
@@ -42,6 +42,7 @@
#include "llresmgr.h"
#include "lltextbox.h"
#include "llbutton.h"
+#include "llcallbacklist.h"
#include "llcheckboxctrl.h"
#include "llviewerobject.h"
#include "llselectmgr.h"
@@ -75,9 +76,11 @@ static LLPanelInjector<LLSidepanelTaskInfo> t_task_info("sidepanel_task_info");
// Default constructor
LLSidepanelTaskInfo::LLSidepanelTaskInfo()
+ : mVisibleDebugPermissions(true) // space was allocated by default
{
setMouseOpaque(FALSE);
LLSelectMgr::instance().mUpdateSignal.connect(boost::bind(&LLSidepanelTaskInfo::refreshAll, this));
+ gIdleCallbacks.addFunction(&LLSidepanelTaskInfo::onIdle, (void*)this);
}
@@ -85,13 +88,12 @@ LLSidepanelTaskInfo::~LLSidepanelTaskInfo()
{
if (sActivePanel == this)
sActivePanel = NULL;
+ gIdleCallbacks.deleteFunction(&LLSidepanelTaskInfo::onIdle, (void*)this);
}
// virtual
BOOL LLSidepanelTaskInfo::postBuild()
{
- LLSidepanelInventorySubpanel::postBuild();
-
mOpenBtn = getChild<LLButton>("open_btn");
mOpenBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onOpenButtonClicked, this));
mPayBtn = getChild<LLButton>("pay_btn");
@@ -146,12 +148,12 @@ BOOL LLSidepanelTaskInfo::postBuild()
mDALabelClickAction = getChildView("label click action");
mDAComboClickAction = getChild<LLComboBox>("clickaction");
mDAPathfindingAttributes = getChild<LLTextBase>("pathfinding_attributes_value");
- mDAB = getChildView("B:");
- mDAO = getChildView("O:");
- mDAG = getChildView("G:");
- mDAE = getChildView("E:");
- mDAN = getChildView("N:");
- mDAF = getChildView("F:");
+ mDAB = getChild<LLUICtrl>("B:");
+ mDAO = getChild<LLUICtrl>("O:");
+ mDAG = getChild<LLUICtrl>("G:");
+ mDAE = getChild<LLUICtrl>("E:");
+ mDAN = getChild<LLUICtrl>("N:");
+ mDAF = getChild<LLUICtrl>("F:");
return TRUE;
}
@@ -201,12 +203,22 @@ void LLSidepanelTaskInfo::disableAll()
disablePermissions();
- mDAB->setVisible(FALSE);
- mDAO->setVisible(FALSE);
- mDAG->setVisible(FALSE);
- mDAE->setVisible(FALSE);
- mDAN->setVisible(FALSE);
- mDAF->setVisible(FALSE);
+ if (mVisibleDebugPermissions)
+ {
+ mDAB->setVisible(FALSE);
+ mDAO->setVisible(FALSE);
+ mDAG->setVisible(FALSE);
+ mDAE->setVisible(FALSE);
+ mDAN->setVisible(FALSE);
+ mDAF->setVisible(FALSE);
+
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ LLRect parent_rect = parent_floater->getRect();
+ LLRect debug_rect = mDAB->getRect();
+ // use double the debug rect for padding (since it isn't trivial to extract top_pad)
+ parent_floater->reshape(parent_rect.getWidth(), parent_rect.getHeight() - (debug_rect.getHeight() * 2));
+ mVisibleDebugPermissions = false;
+ }
mOpenBtn->setEnabled(FALSE);
mPayBtn->setEnabled(FALSE);
@@ -253,6 +265,8 @@ void LLSidepanelTaskInfo::disablePermissions()
void LLSidepanelTaskInfo::refresh()
{
+ mIsDirty = false;
+
LLButton* btn_deed_to_group = mDeedBtn;
if (btn_deed_to_group)
{
@@ -606,23 +620,23 @@ void LLSidepanelTaskInfo::refresh()
if (gSavedSettings.getBOOL("DebugPermissions") )
{
- if (valid_base_perms)
- {
- getChild<LLUICtrl>("B:")->setValue("B: " + mask_to_string(base_mask_on));
- getChildView("B:")->setVisible( TRUE);
-
- getChild<LLUICtrl>("O:")->setValue("O: " + mask_to_string(owner_mask_on));
- getChildView("O:")->setVisible( TRUE);
-
- getChild<LLUICtrl>("G:")->setValue("G: " + mask_to_string(group_mask_on));
- getChildView("G:")->setVisible( TRUE);
-
- getChild<LLUICtrl>("E:")->setValue("E: " + mask_to_string(everyone_mask_on));
- getChildView("E:")->setVisible( TRUE);
-
- getChild<LLUICtrl>("N:")->setValue("N: " + mask_to_string(next_owner_mask_on));
- getChildView("N:")->setVisible( TRUE);
- }
+ if (valid_base_perms)
+ {
+ mDAB->setValue("B: " + mask_to_string(base_mask_on));
+ mDAB->setVisible( TRUE);
+
+ mDAO->setValue("O: " + mask_to_string(owner_mask_on));
+ mDAO->setVisible( TRUE);
+
+ mDAG->setValue("G: " + mask_to_string(group_mask_on));
+ mDAG->setVisible( TRUE);
+
+ mDAE->setValue("E: " + mask_to_string(everyone_mask_on));
+ mDAE->setVisible( TRUE);
+
+ mDAN->setValue("N: " + mask_to_string(next_owner_mask_on));
+ mDAN->setVisible( TRUE);
+ }
U32 flag_mask = 0x0;
if (objectp->permMove()) flag_mask |= PERM_MOVE;
@@ -630,18 +644,35 @@ void LLSidepanelTaskInfo::refresh()
if (objectp->permCopy()) flag_mask |= PERM_COPY;
if (objectp->permTransfer()) flag_mask |= PERM_TRANSFER;
- getChild<LLUICtrl>("F:")->setValue("F:" + mask_to_string(flag_mask));
- getChildView("F:")->setVisible( TRUE);
- }
- else
- {
- getChildView("B:")->setVisible( FALSE);
- getChildView("O:")->setVisible( FALSE);
- getChildView("G:")->setVisible( FALSE);
- getChildView("E:")->setVisible( FALSE);
- getChildView("N:")->setVisible( FALSE);
- getChildView("F:")->setVisible( FALSE);
- }
+ mDAF->setValue("F:" + mask_to_string(flag_mask));
+ mDAF->setVisible(TRUE);
+
+ if (!mVisibleDebugPermissions)
+ {
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ LLRect parent_rect = parent_floater->getRect();
+ LLRect debug_rect = mDAB->getRect();
+ // use double the debug rect for padding (since it isn't trivial to extract top_pad)
+ parent_floater->reshape(parent_rect.getWidth(), parent_rect.getHeight() + (debug_rect.getHeight() * 2));
+ mVisibleDebugPermissions = true;
+ }
+ }
+ else if (mVisibleDebugPermissions)
+ {
+ mDAB->setVisible(FALSE);
+ mDAO->setVisible(FALSE);
+ mDAG->setVisible(FALSE);
+ mDAE->setVisible(FALSE);
+ mDAN->setVisible(FALSE);
+ mDAF->setVisible(FALSE);
+
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ LLRect parent_rect = parent_floater->getRect();
+ LLRect debug_rect = mDAB->getRect();
+ // use double the debug rect for padding (since it isn't trivial to extract top_pad)
+ parent_floater->reshape(parent_rect.getWidth(), parent_rect.getHeight() - (debug_rect.getHeight() * 2));
+ mVisibleDebugPermissions = false;
+ }
BOOL has_change_perm_ability = FALSE;
BOOL has_change_sale_ability = FALSE;
@@ -864,33 +895,6 @@ void LLSidepanelTaskInfo::refresh()
getChildView("label click action")->setEnabled(is_perm_modify && is_nonpermanent_enforced && all_volume);
getChildView("clickaction")->setEnabled(is_perm_modify && is_nonpermanent_enforced && all_volume);
- if (!getIsEditing())
- {
- const std::string no_item_names[] =
- {
- "Object Name",
- "Object Description",
- "button set group",
- "checkbox share with group",
- "button deed",
- "checkbox allow everyone move",
- "checkbox allow everyone copy",
- "checkbox for sale",
- "sale type",
- "Edit Cost",
- "checkbox next owner can modify",
- "checkbox next owner can copy",
- "checkbox next owner can transfer",
- "clickaction",
- "search_check",
- "perm_modify",
- "Group Name",
- };
- for (size_t t=0; t<LL_ARRAY_SIZE(no_item_names); ++t)
- {
- getChildView(no_item_names[t])->setEnabled( FALSE);
- }
- }
updateVerbs();
}
@@ -1203,16 +1207,6 @@ void LLSidepanelTaskInfo::onCommitIncludeInSearch(LLUICtrl* ctrl, void* data)
// virtual
void LLSidepanelTaskInfo::updateVerbs()
{
- LLSidepanelInventorySubpanel::updateVerbs();
-
- /*
- mOpenBtn->setVisible(!getIsEditing());
- mPayBtn->setVisible(!getIsEditing());
- mBuyBtn->setVisible(!getIsEditing());
- //const LLViewerObject *obj = getFirstSelectedObject();
- //mEditBtn->setEnabled(obj && obj->permModify());
- */
-
LLSafeHandle<LLObjectSelection> object_selection = LLSelectMgr::getInstance()->getSelection();
const BOOL any_selected = (object_selection->getNumNodes() > 0);
@@ -1297,6 +1291,23 @@ LLSidepanelTaskInfo* LLSidepanelTaskInfo::getActivePanel()
return sActivePanel;
}
+void LLSidepanelTaskInfo::dirty()
+{
+ mIsDirty = true;
+}
+
+// static
+void LLSidepanelTaskInfo::onIdle( void* user_data )
+{
+ LLSidepanelTaskInfo* self = reinterpret_cast<LLSidepanelTaskInfo*>(user_data);
+
+ if( self->mIsDirty )
+ {
+ self->refresh();
+ self->mIsDirty = false;
+ }
+}
+
LLViewerObject* LLSidepanelTaskInfo::getObject()
{
if (!mObject->isDead())
diff --git a/indra/newview/llsidepaneltaskinfo.h b/indra/newview/llsidepaneltaskinfo.h
index dc259cb22d..852d36293b 100644
--- a/indra/newview/llsidepaneltaskinfo.h
+++ b/indra/newview/llsidepaneltaskinfo.h
@@ -27,8 +27,8 @@
#ifndef LL_LLSIDEPANELTASKINFO_H
#define LL_LLSIDEPANELTASKINFO_H
-#include "llsidepanelinventorysubpanel.h"
#include "lluuid.h"
+#include "llpanel.h"
#include "llselectmgr.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -43,14 +43,14 @@ class LLNameBox;
class LLViewerObject;
class LLTextBase;
-class LLSidepanelTaskInfo : public LLSidepanelInventorySubpanel
+class LLSidepanelTaskInfo : public LLPanel
{
public:
LLSidepanelTaskInfo();
virtual ~LLSidepanelTaskInfo();
- /*virtual*/ BOOL postBuild();
- /*virtual*/ void onVisibilityChange ( BOOL new_visibility );
+ BOOL postBuild() override;
+ void onVisibilityChange ( BOOL new_visibility ) override;
void setObjectSelection(LLObjectSelectionHandle selection);
@@ -58,10 +58,12 @@ public:
LLViewerObject* getFirstSelectedObject();
static LLSidepanelTaskInfo *getActivePanel();
+ void dirty();
+ static void onIdle( void* user_data );
protected:
- /*virtual*/ void refresh(); // refresh all labels as needed
- /*virtual*/ void save();
- /*virtual*/ void updateVerbs();
+ void refresh() override; // refresh all labels as needed
+ void save();
+ void updateVerbs();
void refreshAll(); // ignore current keyboard focus and update all fields
@@ -103,6 +105,8 @@ private:
LLUUID mCreatorID;
LLUUID mOwnerID;
LLUUID mLastOwnerID;
+
+ bool mIsDirty;
protected:
void onOpenButtonClicked();
@@ -121,6 +125,10 @@ protected:
private:
LLPointer<LLViewerObject> mObject;
LLObjectSelectionHandle mObjectSelection;
+
+ // mVisibleDebugPermissions doesn't nessesarily matche state
+ // of viewes and is primarily for floater resize
+ bool mVisibleDebugPermissions;
static LLSidepanelTaskInfo* sActivePanel;
private:
@@ -148,12 +156,12 @@ private:
LLView* mDALabelClickAction;
LLComboBox* mDAComboClickAction;
LLTextBase* mDAPathfindingAttributes;
- LLView* mDAB;
- LLView* mDAO;
- LLView* mDAG;
- LLView* mDAE;
- LLView* mDAN;
- LLView* mDAF;
+ LLUICtrl* mDAB;
+ LLUICtrl* mDAO;
+ LLUICtrl* mDAG;
+ LLUICtrl* mDAE;
+ LLUICtrl* mDAN;
+ LLUICtrl* mDAF;
};
diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
index ed7e18fadc..b7a1832b17 100644
--- a/indra/newview/llsnapshotlivepreview.cpp
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -51,6 +51,7 @@
#include "llviewercontrol.h"
#include "llviewermenufile.h" // upload_new_resource()
#include "llviewerstats.h"
+#include "llviewertexturelist.h"
#include "llwindow.h"
#include "llworld.h"
#include <boost/filesystem.hpp>
@@ -873,6 +874,11 @@ LLPointer<LLImageRaw> LLSnapshotLivePreview::getEncodedImage()
return mPreviewImageEncoded;
}
+bool LLSnapshotLivePreview::createUploadFile(const std::string &out_filename, const S32 max_image_dimentions, const S32 min_image_dimentions)
+{
+ return LLViewerTextureList::createUploadFile(mPreviewImage, out_filename, max_image_dimentions, min_image_dimentions);
+}
+
// We actually estimate the data size so that we do not require actual compression when showing the preview
// Note : whenever formatted image is computed, mDataSize will be updated to reflect the true size
void LLSnapshotLivePreview::estimateDataSize()
diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h
index 1f81307976..6e38a957b4 100644
--- a/indra/newview/llsnapshotlivepreview.h
+++ b/indra/newview/llsnapshotlivepreview.h
@@ -106,6 +106,7 @@ public:
LLPointer<LLImageFormatted> getFormattedImage();
LLPointer<LLImageRaw> getEncodedImage();
+ bool createUploadFile(const std::string &out_file, const S32 max_image_dimentions, const S32 min_image_dimentions);
/// Sets size of preview thumbnail image and the surrounding rect.
void setThumbnailPlaceholderRect(const LLRect& rect) {mThumbnailPlaceholderRect = rect; }
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 1b0a814c45..ad87fca25b 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -256,7 +256,7 @@ static bool mLoginStatePastUI = false;
static bool mBenefitsSuccessfullyInit = false;
const F32 STATE_AGENT_WAIT_TIMEOUT = 240; //seconds
-const S32 MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN = 3; // Give region 3 chances
+const S32 MAX_SEED_CAP_ATTEMPTS_BEFORE_ABORT = 4; // Give region 4 chances
std::unique_ptr<LLEventPump> LLStartUp::sStateWatcher(new LLEventStream("StartupState"));
std::unique_ptr<LLStartupListener> LLStartUp::sListener(new LLStartupListener());
@@ -1412,11 +1412,18 @@ bool idle_startup()
else
{
U32 num_retries = regionp->getNumSeedCapRetries();
- if (num_retries > MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN)
+ if (num_retries > MAX_SEED_CAP_ATTEMPTS_BEFORE_ABORT)
{
- // Region will keep trying to get capabilities,
- // but for now continue as if caps were granted
- LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED);
+ LL_WARNS("AppInit") << "Failed to get capabilities. Backing up to login screen!" << LL_ENDL;
+ if (gRememberPassword)
+ {
+ LLNotificationsUtil::add("LoginPacketNeverReceived", LLSD(), LLSD(), login_alert_status);
+ }
+ else
+ {
+ LLNotificationsUtil::add("LoginPacketNeverReceivedNoTP", LLSD(), LLSD(), login_alert_status);
+ }
+ reset_login();
}
else if (num_retries > 0)
{
@@ -1915,6 +1922,34 @@ bool idle_startup()
LLInventoryModelBackgroundFetch::instance().start();
gInventory.createCommonSystemCategories();
+ LLStartUp::setStartupState(STATE_INVENTORY_CALLBACKS );
+ display_startup();
+
+ return FALSE;
+ }
+
+ //---------------------------------------------------------------------
+ // STATE_INVENTORY_CALLBACKS
+ //---------------------------------------------------------------------
+ if (STATE_INVENTORY_CALLBACKS == LLStartUp::getStartupState())
+ {
+ if (!LLInventoryModel::isSysFoldersReady())
+ {
+ display_startup();
+ return FALSE;
+ }
+ LLInventoryModelBackgroundFetch::instance().start();
+ LLUUID cof_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ LLViewerInventoryCategory* cof = gInventory.getCategory(cof_id);
+ if (cof
+ && cof->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ // Special case, dupplicate request prevention.
+ // Cof folder will be requested via FetchCOF
+ // in appearance manager, prevent recursive fetch
+ cof->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
+ }
+
// It's debatable whether this flag is a good idea - sets all
// bits, and in general it isn't true that inventory
@@ -2176,7 +2211,7 @@ bool idle_startup()
gAgentWearables.notifyLoadingStarted();
gAgent.setOutfitChosen(TRUE);
gAgentWearables.sendDummyAgentWearablesUpdate();
- callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(), set_flags_and_update_appearance);
+ callAfterCOFFetch(set_flags_and_update_appearance);
}
display_startup();
@@ -2787,7 +2822,7 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name,
// Not going through the processAgentInitialWearables path, so need to set this here.
LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true);
// Initiate creation of COF, since we're also bypassing that.
- gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CURRENT_OUTFIT);
ESex gender;
if (gender_name == "male")
@@ -2840,8 +2875,15 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name,
bool do_append = false;
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
// Need to fetch cof contents before we can wear.
- callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(),
+ if (do_copy)
+ {
+ callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(),
boost::bind(&LLAppearanceMgr::wearInventoryCategory, LLAppearanceMgr::getInstance(), cat, do_copy, do_append));
+ }
+ else
+ {
+ callAfterCategoryLinksFetch(cat_id, boost::bind(&LLAppearanceMgr::wearInventoryCategory, LLAppearanceMgr::getInstance(), cat, do_copy, do_append));
+ }
LL_DEBUGS() << "initial outfit category id: " << cat_id << LL_ENDL;
}
@@ -2894,6 +2936,7 @@ std::string LLStartUp::startupStateToString(EStartupState state)
RTNENUM( STATE_AGENT_SEND );
RTNENUM( STATE_AGENT_WAIT );
RTNENUM( STATE_INVENTORY_SEND );
+ RTNENUM(STATE_INVENTORY_CALLBACKS );
RTNENUM( STATE_MISC );
RTNENUM( STATE_PRECACHE );
RTNENUM( STATE_WEARABLES_WAIT );
@@ -2948,6 +2991,11 @@ void reset_login()
LLFloaterReg::hideVisibleInstances();
LLStartUp::setStartupState( STATE_BROWSER_INIT );
+ if (LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->terminate();
+ }
+
// Clear any verified certs and verify them again on next login
// to ensure cert matches server instead of just getting reused
LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore("");
@@ -3621,7 +3669,7 @@ bool process_login_success_response()
std::string flag = login_flags["ever_logged_in"];
if(!flag.empty())
{
- gAgent.setFirstLogin((flag == "N") ? TRUE : FALSE);
+ gAgent.setFirstLogin(flag == "N");
}
/* Flag is currently ignored by the viewer.
@@ -3712,7 +3760,7 @@ bool process_login_success_response()
std::string fake_initial_outfit_name = gSavedSettings.getString("FakeInitialOutfitName");
if (!fake_initial_outfit_name.empty())
{
- gAgent.setFirstLogin(TRUE);
+ gAgent.setFirstLogin(true);
sInitialOutfit = fake_initial_outfit_name;
if (sInitialOutfitGender.empty())
{
@@ -3747,7 +3795,9 @@ bool process_login_success_response()
// Only save mfa_hash for future logins if the user wants their info remembered.
- if(response.has("mfa_hash") && gSavedSettings.getBOOL("RememberUser") && gSavedSettings.getBOOL("RememberPassword"))
+ if(response.has("mfa_hash")
+ && gSavedSettings.getBOOL("RememberUser")
+ && LLLoginInstance::getInstance()->saveMFA())
{
std::string grid(LLGridManager::getInstance()->getGridId());
std::string user_id(gUserCredential->userID());
@@ -3755,6 +3805,13 @@ bool process_login_success_response()
// TODO(brad) - related to SL-17223 consider building a better interface that sync's automatically
gSecAPIHandler->syncProtectedMap();
}
+ else if (!LLLoginInstance::getInstance()->saveMFA())
+ {
+ std::string grid(LLGridManager::getInstance()->getGridId());
+ std::string user_id(gUserCredential->userID());
+ gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id);
+ gSecAPIHandler->syncProtectedMap();
+ }
bool success = false;
// JC: gesture loading done below, when we have an asset system
diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h
index fe8e215f76..921f088423 100644
--- a/indra/newview/llstartup.h
+++ b/indra/newview/llstartup.h
@@ -71,6 +71,7 @@ typedef enum {
STATE_AGENT_SEND, // Connect to a region
STATE_AGENT_WAIT, // Wait for region
STATE_INVENTORY_SEND, // Do inventory transfer
+ STATE_INVENTORY_CALLBACKS, // Wait for missing system folders and register callbacks
STATE_MISC, // Do more things (set bandwidth, start audio, save location, etc)
STATE_PRECACHE, // Wait a bit for textures to download
STATE_WEARABLES_WAIT, // Wait for clothing to download
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 0ce82a1297..2e137a8e12 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -176,6 +176,9 @@ LLFloaterTexturePicker::LLFloaterTexturePicker(
mSelectedItemPinned( FALSE ),
mCanApply(true),
mCanPreview(true),
+ mLimitsSet(false),
+ mMaxDim(S32_MAX),
+ mMinDim(0),
mPreviewSettingChanged(false),
mOnFloaterCommitCallback(NULL),
mOnFloaterCloseCallback(NULL),
@@ -270,19 +273,37 @@ void LLFloaterTexturePicker::stopUsingPipette()
}
}
-void LLFloaterTexturePicker::updateImageStats()
+bool LLFloaterTexturePicker::updateImageStats()
{
+ bool result = true;
if (mTexturep.notNull())
{
//RN: have we received header data for this image?
- if (mTexturep->getFullWidth() > 0 && mTexturep->getFullHeight() > 0)
+ S32 width = mTexturep->getFullWidth();
+ S32 height = mTexturep->getFullHeight();
+ if (width > 0 && height > 0)
{
- std::string formatted_dims = llformat("%d x %d", mTexturep->getFullWidth(),mTexturep->getFullHeight());
- mResolutionLabel->setTextArg("[DIMENSIONS]", formatted_dims);
- if (mOnUpdateImageStatsCallback)
- {
- mOnUpdateImageStatsCallback(mTexturep);
- }
+ if ((mLimitsSet && (width != height))
+ || width < mMinDim
+ || width > mMaxDim
+ || height < mMinDim
+ || height > mMaxDim
+ )
+ {
+ std::string formatted_dims = llformat("%dx%d", width, height);
+ mResolutionWarning->setTextArg("[TEXDIM]", formatted_dims);
+ result = false;
+ }
+ else
+ {
+ std::string formatted_dims = llformat("%d x %d", width, height);
+ mResolutionLabel->setTextArg("[DIMENSIONS]", formatted_dims);
+ }
+
+ if (mOnUpdateImageStatsCallback)
+ {
+ mOnUpdateImageStatsCallback(mTexturep);
+ }
}
else
{
@@ -293,6 +314,21 @@ void LLFloaterTexturePicker::updateImageStats()
{
mResolutionLabel->setTextArg("[DIMENSIONS]", std::string(""));
}
+ mResolutionLabel->setVisible(result);
+ mResolutionWarning->setVisible(!result);
+
+ // Hide buttons and pipete to make space for mResolutionWarning
+ // Hiding buttons is suboptimal, but at the moment limited to inventory thumbnails
+ // may be this should be an info/warning icon with a tooltip?
+ S32 index = mModeSelector->getValue().asInteger();
+ if (index == 0)
+ {
+ mDefaultBtn->setVisible(result);
+ mNoneBtn->setVisible(result);
+ mBlankBtn->setVisible(result);
+ mPipetteBtn->setVisible(result);
+ }
+ return result;
}
// virtual
@@ -410,11 +446,22 @@ BOOL LLFloaterTexturePicker::postBuild()
mTentativeLabel = getChild<LLTextBox>("Multiple");
mResolutionLabel = getChild<LLTextBox>("size_lbl");
+ mResolutionWarning = getChild<LLTextBox>("over_limit_lbl");
- childSetAction("Default",LLFloaterTexturePicker::onBtnSetToDefault,this);
- childSetAction("None", LLFloaterTexturePicker::onBtnNone,this);
- childSetAction("Blank", LLFloaterTexturePicker::onBtnBlank,this);
+ mDefaultBtn = getChild<LLButton>("Default");
+ mNoneBtn = getChild<LLButton>("None");
+ mBlankBtn = getChild<LLButton>("Blank");
+ mPipetteBtn = getChild<LLButton>("Pipette");
+ mSelectBtn = getChild<LLButton>("Select");
+ mCancelBtn = getChild<LLButton>("Cancel");
+
+ mDefaultBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnSetToDefault,this));
+ mNoneBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnNone, this));
+ mBlankBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnBlank, this));
+ mPipetteBtn->setCommitCallback(boost::bind(&LLFloaterTexturePicker::onBtnPipette, this));
+ mSelectBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnSelect, this));
+ mCancelBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnCancel, this));
childSetCommitCallback("show_folders_check", onShowFolders, this);
@@ -481,10 +528,6 @@ BOOL LLFloaterTexturePicker::postBuild()
getChildView("show_folders_check")->setEnabled(FALSE);
}
- getChild<LLUICtrl>("Pipette")->setCommitCallback( boost::bind(&LLFloaterTexturePicker::onBtnPipette, this));
- childSetAction("Cancel", LLFloaterTexturePicker::onBtnCancel,this);
- childSetAction("Select", LLFloaterTexturePicker::onBtnSelect,this);
-
// update permission filter once UI is fully initialized
updateFilterPermMask();
mSavedFolderState.setApply(FALSE);
@@ -504,13 +547,13 @@ void LLFloaterTexturePicker::draw()
static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);
drawConeToOwner(mContextConeOpacity, max_opacity, mOwner);
- updateImageStats();
+ bool valid_dims = updateImageStats();
// if we're inactive, gray out "apply immediate" checkbox
getChildView("show_folders_check")->setEnabled(mActive && mCanApplyImmediately && !mNoCopyTextureSelected);
- getChildView("Select")->setEnabled(mActive && mCanApply);
- getChildView("Pipette")->setEnabled(mActive);
- getChild<LLUICtrl>("Pipette")->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance());
+ mSelectBtn->setEnabled(mActive && mCanApply && valid_dims);
+ mPipetteBtn->setEnabled(mActive);
+ mPipetteBtn->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance());
//BOOL allow_copy = FALSE;
if( mOwner )
@@ -544,9 +587,9 @@ void LLFloaterTexturePicker::draw()
mTentativeLabel->setVisible( FALSE );
}
- getChildView("Default")->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative);
- getChildView("Blank")->setEnabled(mImageAssetID != mBlankImageAssetID || mTentative);
- getChildView("None")->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative));
+ mDefaultBtn->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative);
+ mBlankBtn->setEnabled(mImageAssetID != mBlankImageAssetID || mTentative);
+ mNoneBtn->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative));
LLFloater::draw();
@@ -672,17 +715,76 @@ PermissionMask LLFloaterTexturePicker::getFilterPermMask()
void LLFloaterTexturePicker::commitIfImmediateSet()
{
- if (!mNoCopyTextureSelected && mOnFloaterCommitCallback && mCanApply)
+ if (!mNoCopyTextureSelected && mCanApply)
{
- mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, LLUUID::null);
+ commitCallback(LLTextureCtrl::TEXTURE_CHANGE);
}
}
+void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op)
+{
+ if (!mOnFloaterCommitCallback)
+ {
+ return;
+ }
+ LLUUID asset_id = mImageAssetID;
+ LLUUID inventory_id;
+ LLPickerSource mode = (LLPickerSource)mModeSelector->getValue().asInteger();
+
+ switch (mode)
+ {
+ case PICKER_INVENTORY:
+ {
+ LLFolderView* root_folder = mInventoryPanel->getRootFolder();
+ if (root_folder && root_folder->getCurSelectedItem())
+ {
+ LLFolderViewItem* last_selected = root_folder->getCurSelectedItem();
+ LLFolderViewModelItemInventory* inv_view = static_cast<LLFolderViewModelItemInventory*>(last_selected->getViewModelItem());
+
+ LLInventoryItem* itemp = gInventory.getItem(inv_view->getUUID());
+ if (itemp && itemp->getAssetUUID() == mImageAssetID)
+ {
+ inventory_id = inv_view->getUUID();
+ }
+ else
+ {
+ mode = PICKER_UNKNOWN; // source of id unknown
+ }
+ }
+ else
+ {
+ mode = PICKER_UNKNOWN; // source of id unknown
+ }
+ break;
+ }
+ case PICKER_LOCAL:
+ {
+ if (!mLocalScrollCtrl->getAllSelected().empty())
+ {
+ LLUUID temp_id = mLocalScrollCtrl->getFirstSelected()->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID();
+ asset_id = LLLocalBitmapMgr::getInstance()->getWorldID(temp_id);
+ }
+ else
+ {
+ asset_id = mImageAssetID;
+ mode = PICKER_UNKNOWN; // source of id unknown
+ }
+ break;
+ }
+ case PICKER_BAKE:
+ break;
+ default:
+ mode = PICKER_UNKNOWN; // source of id unknown
+ break;
+ }
+
+ mOnFloaterCommitCallback(op, mode, asset_id, inventory_id);
+}
void LLFloaterTexturePicker::commitCancel()
{
if (!mNoCopyTextureSelected && mOnFloaterCommitCallback && mCanApply)
{
- mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, LLUUID::null);
+ mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, mOriginalImageAssetID, LLUUID::null);
}
}
@@ -735,7 +837,7 @@ void LLFloaterTexturePicker::onBtnCancel(void* userdata)
self->setImageID( self->mOriginalImageAssetID );
if (self->mOnFloaterCommitCallback)
{
- self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, LLUUID::null);
+ self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, self->mOriginalImageAssetID, LLUUID::null);
}
self->mViewModel->resetDirty();
self->closeFloater();
@@ -745,20 +847,7 @@ void LLFloaterTexturePicker::onBtnCancel(void* userdata)
void LLFloaterTexturePicker::onBtnSelect(void* userdata)
{
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
- LLUUID local_id = LLUUID::null;
- if (self->mOwner)
- {
- if (self->mLocalScrollCtrl->getVisible() && !self->mLocalScrollCtrl->getAllSelected().empty())
- {
- LLUUID temp_id = self->mLocalScrollCtrl->getFirstSelected()->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID();
- local_id = LLLocalBitmapMgr::getInstance()->getWorldID(temp_id);
- }
- }
-
- if (self->mOnFloaterCommitCallback)
- {
- self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_SELECT, local_id);
- }
+ self->commitCallback(LLTextureCtrl::TEXTURE_SELECT);
self->closeFloater();
}
@@ -820,25 +909,25 @@ void LLFloaterTexturePicker::onModeSelect(LLUICtrl* ctrl, void *userdata)
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
int index = self->mModeSelector->getValue().asInteger();
- self->getChild<LLButton>("Default")->setVisible(index == 0 ? TRUE : FALSE);
- self->getChild<LLButton>("Blank")->setVisible(index == 0 ? TRUE : FALSE);
- self->getChild<LLButton>("None")->setVisible(index == 0 ? TRUE : FALSE);
- self->getChild<LLButton>("Pipette")->setVisible(index == 0 ? TRUE : FALSE);
- self->getChild<LLFilterEditor>("inventory search editor")->setVisible(index == 0 ? TRUE : FALSE);
- self->getChild<LLInventoryPanel>("inventory panel")->setVisible(index == 0 ? TRUE : FALSE);
+ self->mDefaultBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE);
+ self->mBlankBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE);
+ self->mNoneBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE);
+ self->mPipetteBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE);
+ self->getChild<LLFilterEditor>("inventory search editor")->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE);
+ self->getChild<LLInventoryPanel>("inventory panel")->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE);
/*self->getChild<LLCheckBox>("show_folders_check")->setVisible(mode);
no idea under which conditions the above is even shown, needs testing. */
- self->getChild<LLButton>("l_add_btn")->setVisible(index == 1 ? TRUE : FALSE);
- self->getChild<LLButton>("l_rem_btn")->setVisible(index == 1 ? TRUE : FALSE);
- self->getChild<LLButton>("l_upl_btn")->setVisible(index == 1 ? TRUE : FALSE);
- self->getChild<LLScrollListCtrl>("l_name_list")->setVisible(index == 1 ? TRUE : FALSE);
+ self->getChild<LLButton>("l_add_btn")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE);
+ self->getChild<LLButton>("l_rem_btn")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE);
+ self->getChild<LLButton>("l_upl_btn")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE);
+ self->getChild<LLScrollListCtrl>("l_name_list")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE);
- self->getChild<LLComboBox>("l_bake_use_texture_combo_box")->setVisible(index == 2 ? TRUE : FALSE);
+ self->getChild<LLComboBox>("l_bake_use_texture_combo_box")->setVisible(index == PICKER_BAKE ? TRUE : FALSE);
self->getChild<LLCheckBoxCtrl>("hide_base_mesh_region")->setVisible(FALSE);// index == 2 ? TRUE : FALSE);
- if (index == 2)
+ if (index == PICKER_BAKE)
{
self->stopUsingPipette();
@@ -979,7 +1068,7 @@ void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata)
{
if (self->mOnFloaterCommitCallback)
{
- self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, inworld_id);
+ self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, PICKER_LOCAL, inworld_id, LLUUID::null);
}
}
}
@@ -1098,10 +1187,10 @@ void LLFloaterTexturePicker::updateFilterPermMask()
//mInventoryPanel->setFilterPermMask( getFilterPermMask() ); Commented out due to no-copy texture loss.
}
-void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply)
+void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply, bool inworld_image)
{
- getChildRef<LLUICtrl>("Select").setEnabled(can_apply);
- getChildRef<LLUICtrl>("preview_disabled").setVisible(!can_preview);
+ mSelectBtn->setEnabled(can_apply);
+ getChildRef<LLUICtrl>("preview_disabled").setVisible(!can_preview && inworld_image);
getChildRef<LLUICtrl>("apply_immediate_check").setVisible(can_preview);
mCanApply = can_apply;
@@ -1109,6 +1198,15 @@ void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply)
mPreviewSettingChanged = true;
}
+void LLFloaterTexturePicker::setMinDimentionsLimits(S32 min_dim)
+{
+ mMinDim = min_dim;
+ mLimitsSet = true;
+
+ std::string formatted_dims = llformat("%dx%d", mMinDim, mMinDim);
+ mResolutionWarning->setTextArg("[MINTEXDIM]", formatted_dims);
+}
+
void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string )
{
std::string upper_case_search_string = search_string;
@@ -1405,7 +1503,7 @@ void LLTextureCtrl::showPicker(BOOL take_focus)
}
if (texture_floaterp)
{
- texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLTextureCtrl::onFloaterCommit, this, _1, _2));
+ texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLTextureCtrl::onFloaterCommit, this, _1, _2, _3, _4));
}
if (texture_floaterp)
{
@@ -1478,8 +1576,11 @@ BOOL LLTextureCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
showPicker(FALSE);
//grab textures first...
LLInventoryModelBackgroundFetch::instance().start(gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE));
- //...then start full inventory fetch.
- LLInventoryModelBackgroundFetch::instance().start();
+ //...then start full inventory fetch (should have been done on startup, but just in case.
+ if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
+ {
+ LLInventoryModelBackgroundFetch::instance().start();
+ }
handled = TRUE;
}
else
@@ -1519,7 +1620,7 @@ void LLTextureCtrl::onFloaterClose()
mFloaterHandle.markDead();
}
-void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id)
+void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inv_id)
{
LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get();
@@ -1533,22 +1634,29 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id)
else if (mCommitOnSelection || op == TEXTURE_SELECT)
mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here?
- if(floaterp->isDirty() || id.notNull()) // mModelView->setDirty does not work.
+ if(floaterp->isDirty() || asset_id.notNull()) // mModelView->setDirty does not work.
{
setTentative( FALSE );
- if (id.notNull())
- {
- mImageItemID = id;
- mImageAssetID = id;
- }
- else
- {
- mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
- LL_DEBUGS() << "mImageItemID: " << mImageItemID << LL_ENDL;
- mImageAssetID = floaterp->getAssetID();
- LL_DEBUGS() << "mImageAssetID: " << mImageAssetID << LL_ENDL;
- }
+ switch(source)
+ {
+ case PICKER_INVENTORY:
+ mImageItemID = inv_id;
+ mImageAssetID = asset_id;
+ break;
+ case PICKER_BAKE:
+ case PICKER_LOCAL:
+ mImageItemID = LLUUID::null;
+ mImageAssetID = asset_id;
+ break;
+ case PICKER_UNKNOWN:
+ default:
+ mImageItemID = floaterp->findItemID(asset_id, FALSE);
+ mImageAssetID = asset_id;
+ break;
+ }
+
+ LL_DEBUGS() << "mImageAssetID: " << mImageAssetID << ", mImageItemID: " << mImageItemID << LL_ENDL;
if (op == TEXTURE_SELECT && mOnSelectCallback)
{
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
index fbb38c4464..e2bfe286d3 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -63,6 +63,14 @@ bool get_is_predefined_texture(LLUUID asset_id);
LLUUID get_copy_free_item_by_asset_id(LLUUID image_id, bool no_trans_perm = false);
bool get_can_copy_texture(LLUUID image_id);
+enum LLPickerSource
+{
+ PICKER_INVENTORY,
+ PICKER_LOCAL,
+ PICKER_BAKE,
+ PICKER_UNKNOWN, // on cancel, default ids
+};
+
//////////////////////////////////////////////////////////////////////////////////////////
// LLTextureCtrl
@@ -188,7 +196,7 @@ public:
void closeDependentFloater();
void onFloaterClose();
- void onFloaterCommit(ETexturePickOp op, LLUUID id);
+ void onFloaterCommit(ETexturePickOp op, LLPickerSource source, const LLUUID& local_id, const LLUUID& inv_id);
// This call is returned when a drag is detected. Your callback
// should return TRUE if the drag is acceptable.
@@ -256,7 +264,7 @@ private:
//////////////////////////////////////////////////////////////////////////////////////////
// LLFloaterTexturePicker
-typedef boost::function<void(LLTextureCtrl::ETexturePickOp op, LLUUID id)> floater_commit_callback;
+typedef boost::function<void(LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inventory_id)> floater_commit_callback;
typedef boost::function<void()> floater_close_callback;
typedef boost::function<void(const LLUUID& asset_id)> set_image_asset_id_callback;
typedef boost::function<void(LLPointer<LLViewerTexture> texture)> set_on_update_image_stats_callback;
@@ -295,7 +303,7 @@ public:
// New functions
void setImageID(const LLUUID& image_asset_id, bool set_selection = true);
- void updateImageStats();
+ bool updateImageStats(); // true if within limits
const LLUUID& getAssetID() { return mImageAssetID; }
const LLUUID& findItemID(const LLUUID& asset_id, BOOL copyable_only, BOOL ignore_library = FALSE);
void setCanApplyImmediately(BOOL b);
@@ -309,11 +317,13 @@ public:
void updateFilterPermMask();
void commitIfImmediateSet();
+ void commitCallback(LLTextureCtrl::ETexturePickOp op);
void commitCancel();
void onFilterEdit(const std::string& search_string);
- void setCanApply(bool can_preview, bool can_apply);
+ void setCanApply(bool can_preview, bool can_apply, bool inworld_image = true);
+ void setMinDimentionsLimits(S32 min_dim);
void setTextureSelectedCallback(const texture_selected_callback& cb) { mTextureSelectedCallback = cb; }
void setOnFloaterCloseCallback(const floater_close_callback& cb) { mOnFloaterCloseCallback = cb; }
void setOnFloaterCommitCallback(const floater_commit_callback& cb) { mOnFloaterCommitCallback = cb; }
@@ -364,6 +374,7 @@ protected:
LLTextBox* mTentativeLabel;
LLTextBox* mResolutionLabel;
+ LLTextBox* mResolutionWarning;
std::string mPendingName;
BOOL mActive;
@@ -381,11 +392,20 @@ protected:
LLComboBox* mModeSelector;
LLScrollListCtrl* mLocalScrollCtrl;
+ LLButton* mDefaultBtn;
+ LLButton* mNoneBtn;
+ LLButton* mBlankBtn;
+ LLButton* mPipetteBtn;
+ LLButton* mSelectBtn;
+ LLButton* mCancelBtn;
private:
bool mCanApply;
bool mCanPreview;
bool mPreviewSettingChanged;
+ bool mLimitsSet;
+ S32 mMaxDim;
+ S32 mMinDim;
texture_selected_callback mTextureSelectedCallback;
diff --git a/indra/newview/llthumbnailctrl.cpp b/indra/newview/llthumbnailctrl.cpp
new file mode 100644
index 0000000000..04130fc724
--- /dev/null
+++ b/indra/newview/llthumbnailctrl.cpp
@@ -0,0 +1,239 @@
+/**
+ * @file llthumbnailctrl.cpp
+ * @brief LLThumbnailCtrl base class
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llthumbnailctrl.h"
+
+#include "linden_common.h"
+#include "llagent.h"
+#include "lluictrlfactory.h"
+#include "lluuid.h"
+#include "lltrans.h"
+#include "llviewborder.h"
+#include "llviewertexture.h"
+#include "llviewertexturelist.h"
+#include "llwindow.h"
+
+static LLDefaultChildRegistry::Register<LLThumbnailCtrl> r("thumbnail");
+
+LLThumbnailCtrl::Params::Params()
+: border("border")
+, border_color("border_color")
+, fallback_image("fallback_image")
+, image_name("image_name")
+, border_visible("show_visible", false)
+, interactable("interactable", false)
+, show_loading("show_loading", true)
+{}
+
+LLThumbnailCtrl::LLThumbnailCtrl(const LLThumbnailCtrl::Params& p)
+: LLUICtrl(p)
+, mBorderColor(p.border_color())
+, mBorderVisible(p.border_visible())
+, mFallbackImagep(p.fallback_image)
+, mInteractable(p.interactable())
+, mShowLoadingPlaceholder(p.show_loading())
+, mPriority(LLGLTexture::BOOST_PREVIEW)
+{
+ mLoadingPlaceholderString = LLTrans::getString("texture_loading");
+
+ LLRect border_rect = getLocalRect();
+ LLViewBorder::Params vbparams(p.border);
+ vbparams.name("border");
+ vbparams.rect(border_rect);
+ mBorder = LLUICtrlFactory::create<LLViewBorder> (vbparams);
+ addChild(mBorder);
+
+ if (p.image_name.isProvided())
+ {
+ setValue(p.image_name());
+ }
+}
+
+LLThumbnailCtrl::~LLThumbnailCtrl()
+{
+ mTexturep = nullptr;
+ mImagep = nullptr;
+ mFallbackImagep = nullptr;
+}
+
+
+void LLThumbnailCtrl::draw()
+{
+ LLRect draw_rect = getLocalRect();
+
+ if (mBorderVisible)
+ {
+ mBorder->setKeyboardFocusHighlight(hasFocus());
+
+ gl_rect_2d( draw_rect, mBorderColor.get(), FALSE );
+ draw_rect.stretch( -1 );
+ }
+
+ // If we're in a focused floater, don't apply the floater's alpha to the texture.
+ const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
+ if( mTexturep )
+ {
+ if( mTexturep->getComponents() == 4 )
+ {
+ const LLColor4 color(.098f, .098f, .098f);
+ gl_rect_2d( draw_rect, color, TRUE);
+ }
+
+ gl_draw_scaled_image( draw_rect.mLeft, draw_rect.mBottom, draw_rect.getWidth(), draw_rect.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha);
+
+ mTexturep->setKnownDrawSize(draw_rect.getWidth(), draw_rect.getHeight());
+ }
+ else if( mImagep.notNull() )
+ {
+ mImagep->draw(draw_rect, UI_VERTEX_COLOR % alpha );
+ }
+ else if (mFallbackImagep.notNull())
+ {
+ if (draw_rect.getWidth() > mFallbackImagep->getWidth()
+ && draw_rect.getHeight() > mFallbackImagep->getHeight())
+ {
+ S32 img_width = mFallbackImagep->getWidth();
+ S32 img_height = mFallbackImagep->getHeight();
+ S32 rect_width = draw_rect.getWidth();
+ S32 rect_height = draw_rect.getHeight();
+
+ LLRect fallback_rect;
+ fallback_rect.mLeft = draw_rect.mLeft + (rect_width - img_width) / 2;
+ fallback_rect.mRight = fallback_rect.mLeft + img_width;
+ fallback_rect.mBottom = draw_rect.mBottom + (rect_height - img_height) / 2;
+ fallback_rect.mTop = fallback_rect.mBottom + img_height;
+
+ mFallbackImagep->draw(fallback_rect, UI_VERTEX_COLOR % alpha);
+ }
+ else
+ {
+ mFallbackImagep->draw(draw_rect, UI_VERTEX_COLOR % alpha);
+ }
+ }
+ else
+ {
+ gl_rect_2d( draw_rect, LLColor4::grey % alpha, TRUE );
+
+ // Draw X
+ gl_draw_x( draw_rect, LLColor4::black );
+ }
+
+ // Show "Loading..." string on the top left corner while this texture is loading.
+ // Using the discard level, do not show the string if the texture is almost but not
+ // fully loaded.
+ if (mTexturep.notNull()
+ && mShowLoadingPlaceholder
+ && !mTexturep->isFullyLoaded())
+ {
+ U32 v_offset = 25;
+ LLFontGL* font = LLFontGL::getFontSansSerif();
+
+ // Don't show as loaded if the texture is almost fully loaded (i.e. discard1) unless god
+ if ((mTexturep->getDiscardLevel() > 1) || gAgent.isGodlike())
+ {
+ font->renderUTF8(
+ mLoadingPlaceholderString,
+ 0,
+ llfloor(draw_rect.mLeft+3),
+ llfloor(draw_rect.mTop-v_offset),
+ LLColor4::white,
+ LLFontGL::LEFT,
+ LLFontGL::BASELINE,
+ LLFontGL::DROP_SHADOW);
+ }
+ }
+
+ LLUICtrl::draw();
+}
+
+void LLThumbnailCtrl::clearTexture()
+{
+ mImageAssetID = LLUUID::null;
+ mTexturep = nullptr;
+ mImagep = nullptr;
+}
+
+// virtual
+// value might be a string or a UUID
+void LLThumbnailCtrl::setValue(const LLSD& value)
+{
+ LLSD tvalue(value);
+ if (value.isString() && LLUUID::validate(value.asString()))
+ {
+ //RN: support UUIDs masquerading as strings
+ tvalue = LLSD(LLUUID(value.asString()));
+ }
+
+ LLUICtrl::setValue(tvalue);
+
+ mImageAssetID = LLUUID::null;
+ mTexturep = nullptr;
+ mImagep = nullptr;
+
+ if (tvalue.isUUID())
+ {
+ mImageAssetID = tvalue.asUUID();
+ if (mImageAssetID.notNull())
+ {
+ // Should it support baked textures?
+ mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+
+ mTexturep->setBoostLevel(mPriority);
+ mTexturep->forceToSaveRawImage(0);
+
+ S32 desired_draw_width = mTexturep->getWidth();
+ S32 desired_draw_height = mTexturep->getHeight();
+
+ mTexturep->setKnownDrawSize(desired_draw_width, desired_draw_height);
+ }
+ }
+ else if (tvalue.isString())
+ {
+ mImagep = LLUI::getUIImage(tvalue.asString(), LLGLTexture::BOOST_UI);
+ if (mImagep)
+ {
+ LLViewerFetchedTexture* texture = dynamic_cast<LLViewerFetchedTexture*>(mImagep->getImage().get());
+ if(texture)
+ {
+ mImageAssetID = texture->getID();
+ }
+ }
+ }
+}
+
+BOOL LLThumbnailCtrl::handleHover(S32 x, S32 y, MASK mask)
+{
+ if (mInteractable && getEnabled())
+ {
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ return TRUE;
+ }
+ return LLUICtrl::handleHover(x, y, mask);
+}
+
+
diff --git a/indra/newview/llthumbnailctrl.h b/indra/newview/llthumbnailctrl.h
new file mode 100644
index 0000000000..686603b373
--- /dev/null
+++ b/indra/newview/llthumbnailctrl.h
@@ -0,0 +1,88 @@
+/**
+ * @file llthumbnailctrl.h
+ * @brief LLThumbnailCtrl base class
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023 Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTHUMBNAILCTRL_H
+#define LL_LLTHUMBNAILCTRL_H
+
+#include "llui.h"
+#include "lluictrl.h"
+#include "llviewborder.h" // for params
+
+class LLUICtrlFactory;
+class LLUUID;
+class LLViewerFetchedTexture;
+
+//
+// Classes
+//
+
+//
+class LLThumbnailCtrl
+: public LLUICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLViewBorder::Params> border;
+ Optional<LLUIColor> border_color;
+ Optional<std::string> image_name;
+ Optional<LLUIImage*> fallback_image;
+ Optional<bool> border_visible;
+ Optional<bool> interactable;
+ Optional<bool> show_loading;
+
+ Params();
+ };
+protected:
+ LLThumbnailCtrl(const Params&);
+ friend class LLUICtrlFactory;
+
+public:
+ virtual ~LLThumbnailCtrl();
+
+ virtual void draw() override;
+
+ virtual void setValue(const LLSD& value ) override;
+ void clearTexture();
+
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask) override;
+
+private:
+ S32 mPriority;
+ bool mBorderVisible;
+ bool mInteractable;
+ bool mShowLoadingPlaceholder;
+ std::string mLoadingPlaceholderString;
+ LLUUID mImageAssetID;
+ LLViewBorder* mBorder;
+ LLUIColor mBorderColor;
+
+ LLPointer<LLViewerFetchedTexture> mTexturep;
+ LLPointer<LLUIImage> mImagep;
+ LLPointer<LLUIImage> mFallbackImagep;
+};
+
+#endif
diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp
index 692e8d91a9..d35833fac9 100644
--- a/indra/newview/lltoastalertpanel.cpp
+++ b/indra/newview/lltoastalertpanel.cpp
@@ -279,6 +279,10 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal
if (!edit_text_name.empty())
{
S32 y = VPAD + BTN_HEIGHT + VPAD/2;
+ if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO)
+ {
+ y += EDITOR_HEIGHT;
+ }
mLineEditor = LLUICtrlFactory::getInstance()->createFromFile<LLLineEditor>("alert_line_editor.xml", this, LLPanel::child_registry_t::instance());
if (mLineEditor)
@@ -522,6 +526,10 @@ void LLToastAlertPanel::onButtonPressed( const LLSD& data, S32 button )
{
response[mLineEditor->getName()] = mLineEditor->getValue();
}
+ if (mNotification->getForm()->getIgnoreType() != LLNotificationForm::IGNORE_NO)
+ {
+ response["ignore"] = mNotification->isIgnored();
+ }
response[button_data->mButton->getName()] = true;
// If we declared a URL and chose the URL option, go to the url
diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp
index 4f47c465c4..f6628293ee 100644
--- a/indra/newview/lltoolbarview.cpp
+++ b/indra/newview/lltoolbarview.cpp
@@ -322,9 +322,9 @@ bool LLToolBarView::loadToolbars(bool force_default)
}
// SL-18581: Don't show the starter avatar toolbar button for NUX users
- LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));
if (gAgent.isFirstLogin())
{
+ LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));
LL_WARNS() << "First login: checking for NUX user." << LL_ENDL;
if (my_outfits_cat != NULL && my_outfits_cat->getDescendentCount() > 0)
{
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index b16b26b96e..78e068f808 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -764,7 +764,7 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop,
if (!handled)
{
// Disallow drag and drop to 3D from the marketplace
- const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
if (marketplacelistings_id.notNull())
{
for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++)
@@ -1732,8 +1732,8 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv(
return ACCEPT_NO;
}
- const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false);
- if(gInventory.isObjectDescendentOf(item->getUUID(), outbox_id))
+ const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX);
+ if(outbox_id.notNull() && gInventory.isObjectDescendentOf(item->getUUID(), outbox_id))
{
// Legacy
return ACCEPT_NO;
@@ -2159,8 +2159,8 @@ EAcceptance LLToolDragAndDrop::dad3dWearCategory(
return ACCEPT_NO;
}
- const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false);
- if(gInventory.isObjectDescendentOf(category->getUUID(), outbox_id))
+ const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX);
+ if(outbox_id.notNull() && gInventory.isObjectDescendentOf(category->getUUID(), outbox_id))
{
// Legacy
return ACCEPT_NO;
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 0f2fe1e1cd..15b95d70a9 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -56,6 +56,7 @@
#include "llfloaterbvhpreview.h"
#include "llfloatercamera.h"
#include "llfloatercamerapresets.h"
+#include "llfloaterchangeitemthumbnail.h"
#include "llfloaterchatvoicevolume.h"
#include "llfloaterclassified.h"
#include "llfloaterconversationlog.h"
@@ -84,6 +85,7 @@
#include "llfloaterimagepreview.h"
#include "llfloaterimsession.h"
#include "llfloaterinspect.h"
+#include "llfloaterinventorysettings.h"
#include "llfloaterjoystick.h"
#include "llfloaterlagmeter.h"
#include "llfloaterland.h"
@@ -98,12 +100,12 @@
#include "llfloatermyscripts.h"
#include "llfloatermyenvironment.h"
#include "llfloaternamedesc.h"
+#include "llfloaternewfeaturenotification.h"
#include "llfloaternotificationsconsole.h"
#include "llfloaternotificationstabbed.h"
#include "llfloaterobjectweights.h"
#include "llfloateropenobject.h"
-#include "llfloateroutfitphotopreview.h"
-#include "llfloatersimpleoutfitsnapshot.h"
+#include "llfloatersimplesnapshot.h"
#include "llfloaterpathfindingcharacters.h"
#include "llfloaterpathfindingconsole.h"
#include "llfloaterpathfindinglinksets.h"
@@ -116,7 +118,6 @@
#include "llfloaterpreferenceviewadvanced.h"
#include "llfloaterpreviewtrash.h"
#include "llfloaterprofile.h"
-#include "llfloaterproperties.h"
#include "llfloaterregiondebugconsole.h"
#include "llfloaterregioninfo.h"
#include "llfloaterregionrestarting.h"
@@ -229,6 +230,7 @@ public:
"avatar_picker",
"camera",
"camera_presets",
+ "change_item_thumbnail"
"classified",
"add_landmark",
"delete_pref_preset",
@@ -247,6 +249,7 @@ public:
"message_critical", // Modal!!! Login specific. If this is in use elsewhere, better to create a non modal variant
"message_tos", // Modal!!! Login specific.
"mute_object_by_name",
+ "new_feature_notification",
"publish_classified",
"save_pref_preset",
"save_camera_preset",
@@ -330,6 +333,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>);
LLFloaterReg::add("camera_presets", "floater_camera_presets.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCameraPresets>);
LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>);
+ LLFloaterReg::add("change_item_thumbnail", "floater_change_item_thumbnail.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChangeItemThumbnail>);
LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater);
LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterClassified>);
LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>);
@@ -373,6 +377,8 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
LLFloaterReg::add("inspect", "floater_inspect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInspect>);
LLFloaterReg::add("item_properties", "floater_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterItemProperties>);
+ LLFloaterReg::add("task_properties", "floater_task_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterItemProperties>);
+ LLFloaterReg::add("inventory_settings", "floater_inventory_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInventorySettings>);
LLInspectAvatarUtil::registerFloater();
LLInspectGroupUtil::registerFloater();
LLInspectObjectUtil::registerFloater();
@@ -400,6 +406,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("moveview", "floater_moveview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMove>);
LLFloaterReg::add("mute_object_by_name", "floater_mute_object.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGetBlockedObjectName>);
LLFloaterReg::add("mini_map", "floater_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMap>);
+ LLFloaterReg::add("new_feature_notification", "floater_new_feature_notification.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNewFeatureNotification>);
LLFloaterReg::add("notifications_console", "floater_notifications_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotificationConsole>);
@@ -408,7 +415,6 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("object_weights", "floater_object_weights.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterObjectWeights>);
LLFloaterReg::add("openobject", "floater_openobject.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterOpenObject>);
LLFloaterReg::add("outgoing_call", "floater_outgoing_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLOutgoingCallDialog>);
- LLFloaterReg::add("outfit_photo_preview", "floater_outfit_photo_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterOutfitPhotoPreview>);
LLFloaterPayUtil::registerFloater();
LLFloaterReg::add("pathfinding_characters", "floater_pathfinding_characters.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPathfindingCharacters>);
@@ -436,7 +442,6 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("preview_sound", "floater_preview_sound.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewSound>, "preview");
LLFloaterReg::add("preview_texture", "floater_preview_texture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewTexture>, "preview");
LLFloaterReg::add("preview_trash", "floater_preview_trash.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreviewTrash>);
- LLFloaterReg::add("properties", "floater_inventory_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterProperties>);
LLFloaterReg::add("publish_classified", "floater_publish_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPublishClassifiedFloater>);
LLFloaterReg::add("save_pref_preset", "floater_save_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSavePrefPreset>);
LLFloaterReg::add("save_camera_preset", "floater_save_camera_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSaveCameraPreset>);
@@ -470,7 +475,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("scene_load_stats", "floater_scene_load_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSceneLoadStats>);
LLFloaterReg::add("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotRunQueue>);
LLFloaterReg::add("snapshot", "floater_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSnapshot>);
- LLFloaterReg::add("simple_outfit_snapshot", "floater_simple_outfit_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSimpleOutfitSnapshot>);
+ LLFloaterReg::add("simple_snapshot", "floater_simple_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSimpleSnapshot>);
LLFloaterReg::add("search", "floater_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSearch>);
LLFloaterReg::add("profile", "floater_profile.xml",(LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterProfile>);
LLFloaterReg::add("guidebook", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHowTo>);
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 793eb56734..c0a74e828e 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -314,7 +314,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& uuid,
time_t creation_date_utc) :
LLInventoryItem(uuid, parent_uuid, perm, asset_uuid, type, inv_type,
name, desc, sale_info, flags, creation_date_utc),
- mIsComplete(TRUE)
+ mIsComplete(true)
{
}
@@ -323,7 +323,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& item_id,
const std::string& name,
LLInventoryType::EType inv_type) :
LLInventoryItem(),
- mIsComplete(FALSE)
+ mIsComplete(false)
{
mUUID = item_id;
mParentUUID = parent_id;
@@ -333,7 +333,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& item_id,
LLViewerInventoryItem::LLViewerInventoryItem() :
LLInventoryItem(),
- mIsComplete(FALSE)
+ mIsComplete(false)
{
}
@@ -350,7 +350,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLViewerInventoryItem* other)
LLViewerInventoryItem::LLViewerInventoryItem(const LLInventoryItem *other) :
LLInventoryItem(other),
- mIsComplete(TRUE)
+ mIsComplete(true)
{
}
@@ -432,48 +432,43 @@ void LLViewerInventoryItem::fetchFromServer(void) const
{
if(!mIsComplete)
{
- std::string url;
-
- LLViewerRegion* region = gAgent.getRegion();
- // we have to check region. It can be null after region was destroyed. See EXT-245
- if (region)
- {
- if (gAgent.getID() != mPermissions.getOwner())
- {
- url = region->getCapability("FetchLib2");
- }
- else
- {
- url = region->getCapability("FetchInventory2");
- }
- }
- else
- {
- LL_WARNS(LOG_INV) << "Agent Region is absent" << LL_ENDL;
- }
-
- if (!url.empty())
- {
- LLSD body;
- body["agent_id"] = gAgent.getID();
- body["items"][0]["owner_id"] = mPermissions.getOwner();
- body["items"][0]["item_id"] = mUUID;
-
- LLCore::HttpHandler::ptr_t handler(new LLInventoryModel::FetchItemHttpHandler(body));
- gInventory.requestPost(true, url, body, handler, "Inventory Item");
- }
- else
- {
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessage("FetchInventory");
- msg->nextBlock("AgentData");
- msg->addUUID("AgentID", gAgent.getID());
- msg->addUUID("SessionID", gAgent.getSessionID());
- msg->nextBlock("InventoryData");
- msg->addUUID("OwnerID", mPermissions.getOwner());
- msg->addUUID("ItemID", mUUID);
- gAgent.sendReliableMessage();
- }
+ if (AISAPI::isAvailable()) // AIS v 3
+ {
+ LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(mUUID);
+ }
+ else
+ {
+ std::string url;
+
+ LLViewerRegion* region = gAgent.getRegion();
+ // we have to check region. It can be null after region was destroyed. See EXT-245
+ if (region)
+ {
+ if (gAgent.getID() != mPermissions.getOwner())
+ {
+ url = region->getCapability("FetchLib2");
+ }
+ else
+ {
+ url = region->getCapability("FetchInventory2");
+ }
+ }
+ else
+ {
+ LL_WARNS(LOG_INV) << "Agent Region is absent" << LL_ENDL;
+ }
+
+ if (!url.empty())
+ {
+ LLSD body;
+ body["agent_id"] = gAgent.getID();
+ body["items"][0]["owner_id"] = mPermissions.getOwner();
+ body["items"][0]["item_id"] = mUUID;
+
+ LLCore::HttpHandler::ptr_t handler(new LLInventoryModel::FetchItemHttpHandler(body));
+ gInventory.requestPost(true, url, body, handler, "Inventory Item");
+ }
+ }
}
}
@@ -568,7 +563,8 @@ LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& uuid,
LLInventoryCategory(uuid, parent_uuid, pref, name),
mOwnerID(owner_id),
mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN),
- mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
+ mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN),
+ mFetching(FETCH_NONE)
{
mDescendentsRequested.reset();
}
@@ -576,7 +572,8 @@ LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& uuid,
LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& owner_id) :
mOwnerID(owner_id),
mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN),
- mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
+ mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN),
+ mFetching(FETCH_NONE)
{
mDescendentsRequested.reset();
}
@@ -584,6 +581,7 @@ LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& owner_id) :
LLViewerInventoryCategory::LLViewerInventoryCategory(const LLViewerInventoryCategory* other)
{
copyViewerCategory(other);
+ mFetching = FETCH_NONE;
}
LLViewerInventoryCategory::~LLViewerInventoryCategory()
@@ -659,7 +657,6 @@ bool LLViewerInventoryCategory::fetch()
mDescendentsRequested.reset();
mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY);
-
std::string url;
if (gAgent.getRegion())
{
@@ -669,15 +666,52 @@ bool LLViewerInventoryCategory::fetch()
{
LL_WARNS(LOG_INV) << "agent region is null" << LL_ENDL;
}
- if (!url.empty()) //Capability found. Build up LLSD and use it.
+ if (!url.empty() || AISAPI::isAvailable())
{
- LLInventoryModelBackgroundFetch::instance().start(mUUID, false);
+ LLInventoryModelBackgroundFetch::instance().start(mUUID, false);
}
return true;
}
return false;
}
+LLViewerInventoryCategory::EFetchType LLViewerInventoryCategory::getFetching()
+{
+ // if timer hasn't expired, request was scheduled, but not in progress
+ // if mFetching request was actually started
+ if (mDescendentsRequested.hasExpired())
+ {
+ mFetching = FETCH_NONE;
+ }
+ return mFetching;
+}
+
+void LLViewerInventoryCategory::setFetching(LLViewerInventoryCategory::EFetchType fetching)
+{
+ if (fetching > mFetching) // allow a switch from normal to recursive
+ {
+ if (mDescendentsRequested.hasExpired() || (mFetching == FETCH_NONE))
+ {
+ mDescendentsRequested.reset();
+ if (AISAPI::isAvailable())
+ {
+ mDescendentsRequested.setTimerExpirySec(AISAPI::HTTP_TIMEOUT);
+ }
+ else
+ {
+ const F32 FETCH_TIMER_EXPIRY = 30.0f;
+ mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY);
+ }
+ }
+ mFetching = fetching;
+ }
+ else if (fetching == FETCH_NONE)
+ {
+ mDescendentsRequested.stop();
+ mFetching = fetching;
+ }
+}
+
S32 LLViewerInventoryCategory::getViewerDescendentCount() const
{
LLInventoryModel::cat_array_t* cats;
@@ -1002,13 +1036,18 @@ void create_notecard_cb(const LLUUID& inv_item)
LLInventoryCallbackManager gInventoryCallbacks;
-void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id,
- const LLUUID& parent, const LLTransactionID& transaction_id,
- const std::string& name,
- const std::string& desc, LLAssetType::EType asset_type,
- LLInventoryType::EType inv_type, U8 subtype,
- U32 next_owner_perm,
- LLPointer<LLInventoryCallback> cb)
+void create_inventory_item(
+ const LLUUID& agent_id,
+ const LLUUID& session_id,
+ const LLUUID& parent_id,
+ const LLTransactionID& transaction_id,
+ const std::string& name,
+ const std::string& desc,
+ LLAssetType::EType asset_type,
+ LLInventoryType::EType inv_type,
+ U8 subtype,
+ U32 next_owner_perm,
+ LLPointer<LLInventoryCallback> cb)
{
//check if name is equal to one of special inventory items names
//EXT-5839
@@ -1029,6 +1068,54 @@ void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id,
}
}
+#ifdef USE_AIS_FOR_NC
+ // D567 18.03.2023 not yet implemented within AIS3
+ if (AISAPI::isAvailable())
+ {
+ LLSD new_inventory = LLSD::emptyMap();
+ new_inventory["items"] = LLSD::emptyArray();
+
+ LLPermissions perms;
+ perms.init(
+ gAgentID,
+ gAgentID,
+ LLUUID::null,
+ LLUUID::null);
+ perms.initMasks(
+ PERM_ALL,
+ PERM_ALL,
+ PERM_NONE,
+ PERM_NONE,
+ next_owner_perm);
+
+ LLUUID null_id;
+ LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem(
+ null_id, /*don't know yet*/
+ parent_id,
+ perms,
+ null_id, /*don't know yet*/
+ asset_type,
+ inv_type,
+ server_name,
+ desc,
+ LLSaleInfo(),
+ 0,
+ 0 /*don't know yet, whenever server creates it*/);
+ LLSD item_sd = item->asLLSD();
+ new_inventory["items"].append(item_sd);
+ AISAPI::completion_t cr = boost::bind(&doInventoryCb, cb, _1);
+ AISAPI::CreateInventory(
+ parent_id,
+ new_inventory,
+ cr);
+ return;
+ }
+ else
+ {
+ LL_WARNS() << "AIS v3 not available" << LL_ENDL;
+ }
+#endif
+
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_CreateInventoryItem);
msg->nextBlock(_PREHASH_AgentData);
@@ -1036,7 +1123,7 @@ void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id,
msg->addUUIDFast(_PREHASH_SessionID, session_id);
msg->nextBlock(_PREHASH_InventoryBlock);
msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb));
- msg->addUUIDFast(_PREHASH_FolderID, parent);
+ msg->addUUIDFast(_PREHASH_FolderID, parent_id);
msg->addUUIDFast(_PREHASH_TransactionID, transaction_id);
msg->addU32Fast(_PREHASH_NextOwnerMask, next_owner_perm);
msg->addS8Fast(_PREHASH_Type, (S8)asset_type);
@@ -1292,17 +1379,15 @@ void update_inventory_category(
LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL;
if(obj)
{
- if (LLFolderType::lookupIsProtectedType(obj->getPreferredType()))
+ if (LLFolderType::lookupIsProtectedType(obj->getPreferredType())
+ && (updates.size() != 1 || !updates.has("thumbnail")))
{
LLNotificationsUtil::add("CannotModifyProtectedCategories");
return;
}
- LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(obj);
- new_cat->fromLLSD(updates);
- LLSD new_llsd = new_cat->asLLSD();
AISAPI::completion_t cr = boost::bind(&doInventoryCb, cb, _1);
- AISAPI::UpdateCategory(cat_id, new_llsd, cr);
+ AISAPI::UpdateCategory(cat_id, updates, cr);
}
}
@@ -1354,25 +1439,10 @@ void remove_inventory_item(
gInventory.onObjectDeletedFromServer(item_id);
}
}
- else // no cap
- {
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_RemoveInventoryItem);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_InventoryData);
- msg->addUUIDFast(_PREHASH_ItemID, item_id);
- gAgent.sendReliableMessage();
-
- // Update inventory and call callback immediately since
- // message-based system has no callback mechanism (!)
- gInventory.onObjectDeletedFromServer(item_id);
- if (cb)
- {
- cb->fire(item_id);
- }
- }
+ else
+ {
+ LL_WARNS(LOG_INV) << "Tried to use inventory without AIS API" << LL_ENDL;
+ }
}
else
{
@@ -1501,28 +1571,10 @@ void purge_descendents_of(const LLUUID& id, LLPointer<LLInventoryCallback> cb)
AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t();
AISAPI::PurgeDescendents(id, cr);
}
- else // no cap
- {
- // Fast purge
- LL_DEBUGS(LOG_INV) << "purge_descendents_of fast case " << cat->getName() << LL_ENDL;
-
- // send it upstream
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessage("PurgeInventoryDescendents");
- msg->nextBlock("AgentData");
- msg->addUUID("AgentID", gAgent.getID());
- msg->addUUID("SessionID", gAgent.getSessionID());
- msg->nextBlock("InventoryData");
- msg->addUUID("FolderID", id);
- gAgent.sendReliableMessage();
-
- // Update model immediately because there is no callback mechanism.
- gInventory.onDescendentsPurgedFromServer(id);
- if (cb)
- {
- cb->fire(id);
- }
- }
+ else
+ {
+ LL_WARNS(LOG_INV) << "Tried to use inventory without AIS API" << LL_ENDL;
+ }
}
}
@@ -1594,13 +1646,14 @@ void create_new_item(const std::string& name,
const LLUUID& parent_id,
LLAssetType::EType asset_type,
LLInventoryType::EType inv_type,
- U32 next_owner_perm)
+ U32 next_owner_perm,
+ std::function<void(const LLUUID&)> created_cb = NULL)
{
std::string desc;
LLViewerAssetType::generateDescriptionFor(asset_type, desc);
next_owner_perm = (next_owner_perm) ? next_owner_perm : PERM_MOVE | PERM_TRANSFER;
- LLPointer<LLInventoryCallback> cb = NULL;
+ LLPointer<LLBoostFuncInventoryCallback> cb = NULL;
switch (inv_type)
{
@@ -1624,9 +1677,17 @@ void create_new_item(const std::string& name,
next_owner_perm = LLFloaterPerms::getNextOwnerPerms("Notecards");
break;
}
- default:
- break;
- }
+
+ default:
+ {
+ cb = new LLBoostFuncInventoryCallback();
+ break;
+ }
+ }
+ if (created_cb != NULL)
+ {
+ cb->addOnFireFunc(created_cb);
+ }
create_inventory_item(gAgent.getID(),
gAgent.getSessionID(),
@@ -1678,57 +1739,86 @@ const std::string NEW_GESTURE_NAME = "New Gesture"; // *TODO:Translate? (probabl
// ! REFACTOR ! Really need to refactor this so that it's not a bunch of if-then statements...
void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge, const LLSD& userdata, const LLUUID& default_parent_uuid)
{
- std::string type_name = userdata.asString();
-
- if (("inbox" == type_name) || ("category" == type_name) || ("current" == type_name) || ("outfit" == type_name) || ("my_otfts" == type_name))
- {
- LLFolderType::EType preferred_type = LLFolderType::lookup(type_name);
+ menu_create_inventory_item(panel, bridge ? bridge->getUUID() : LLUUID::null, userdata, default_parent_uuid);
+}
- LLUUID parent_id;
- if (bridge)
- {
- parent_id = bridge->getUUID();
- }
- else if (default_parent_uuid.notNull())
- {
- parent_id = default_parent_uuid;
- }
- else
- {
- parent_id = gInventory.getRootFolderID();
- }
+void menu_create_inventory_item(LLInventoryPanel* panel, LLUUID dest_id, const LLSD& userdata, const LLUUID& default_parent_uuid, std::function<void(const LLUUID&)> created_cb)
+{
+ std::string type_name = userdata.asString();
+
+ if (("inbox" == type_name) || ("category" == type_name) || ("current" == type_name) || ("outfit" == type_name) || ("my_otfts" == type_name))
+ {
+ LLFolderType::EType preferred_type = LLFolderType::lookup(type_name);
- LLUUID category = gInventory.createNewCategory(parent_id, preferred_type, LLStringUtil::null);
- gInventory.notifyObservers();
- panel->setSelectionByID(category, TRUE);
- }
- else if ("lsl" == type_name)
- {
- const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_LSL_TEXT);
- create_new_item(NEW_LSL_NAME,
- parent_id,
- LLAssetType::AT_LSL_TEXT,
- LLInventoryType::IT_LSL,
- PERM_MOVE | PERM_TRANSFER); // overridden in create_new_item
- }
- else if ("notecard" == type_name)
- {
- const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_NOTECARD);
- create_new_item(NEW_NOTECARD_NAME,
- parent_id,
- LLAssetType::AT_NOTECARD,
- LLInventoryType::IT_NOTECARD,
- PERM_ALL); // overridden in create_new_item
- }
- else if ("gesture" == type_name)
- {
- const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE);
- create_new_item(NEW_GESTURE_NAME,
- parent_id,
- LLAssetType::AT_GESTURE,
- LLInventoryType::IT_GESTURE,
- PERM_ALL); // overridden in create_new_item
- }
+ LLUUID parent_id;
+ if (dest_id.notNull())
+ {
+ parent_id = dest_id;
+ }
+ else if (default_parent_uuid.notNull())
+ {
+ parent_id = default_parent_uuid;
+ }
+ else
+ {
+ parent_id = gInventory.getRootFolderID();
+ }
+
+ std::function<void(const LLUUID&)> callback_cat_created = NULL;
+ if (panel)
+ {
+ LLHandle<LLPanel> handle = panel->getHandle();
+ callback_cat_created = [handle](const LLUUID& new_category_id)
+ {
+ gInventory.notifyObservers();
+ LLInventoryPanel* panel = static_cast<LLInventoryPanel*>(handle.get());
+ if (panel)
+ {
+ panel->setSelectionByID(new_category_id, TRUE);
+ }
+ LL_DEBUGS(LOG_INV) << "Done creating inventory: " << new_category_id << LL_ENDL;
+ };
+ }
+ else if (created_cb != NULL)
+ {
+ callback_cat_created = created_cb;
+ }
+ gInventory.createNewCategory(
+ parent_id,
+ preferred_type,
+ LLStringUtil::null,
+ callback_cat_created);
+ }
+ else if ("lsl" == type_name)
+ {
+ const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_LSL_TEXT);
+ create_new_item(NEW_LSL_NAME,
+ parent_id,
+ LLAssetType::AT_LSL_TEXT,
+ LLInventoryType::IT_LSL,
+ PERM_MOVE | PERM_TRANSFER,
+ created_cb); // overridden in create_new_item
+ }
+ else if ("notecard" == type_name)
+ {
+ const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_NOTECARD);
+ create_new_item(NEW_NOTECARD_NAME,
+ parent_id,
+ LLAssetType::AT_NOTECARD,
+ LLInventoryType::IT_NOTECARD,
+ PERM_ALL,
+ created_cb); // overridden in create_new_item
+ }
+ else if ("gesture" == type_name)
+ {
+ const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE);
+ create_new_item(NEW_GESTURE_NAME,
+ parent_id,
+ LLAssetType::AT_GESTURE,
+ LLInventoryType::IT_GESTURE,
+ PERM_ALL,
+ created_cb); // overridden in create_new_item
+ }
else if (("sky" == type_name) || ("water" == type_name) || ("daycycle" == type_name))
{
LLSettingsType::type_e stype(LLSettingsType::ST_NONE);
@@ -1751,25 +1841,28 @@ void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge,
return;
}
- LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS);
+ LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS);
- LLSettingsVOBase::createNewInventoryItem(stype, parent_id);
+ LLSettingsVOBase::createNewInventoryItem(stype, parent_id, created_cb);
+ }
+ else
+ {
+ // Use for all clothing and body parts. Adding new wearable types requires updating LLWearableDictionary.
+ LLWearableType::EType wearable_type = LLWearableType::getInstance()->typeNameToType(type_name);
+ if (wearable_type >= LLWearableType::WT_SHAPE && wearable_type < LLWearableType::WT_COUNT)
+ {
+ const LLUUID parent_id = dest_id;
+ LLAgentWearables::createWearable(wearable_type, false, parent_id, created_cb);
+ }
+ else
+ {
+ LL_WARNS(LOG_INV) << "Can't create unrecognized type " << type_name << LL_ENDL;
+ }
+ }
+ if(panel)
+ {
+ panel->getRootFolder()->setNeedsAutoRename(TRUE);
}
- else
- {
- // Use for all clothing and body parts. Adding new wearable types requires updating LLWearableDictionary.
- LLWearableType::EType wearable_type = LLWearableType::getInstance()->typeNameToType(type_name);
- if (wearable_type >= LLWearableType::WT_SHAPE && wearable_type < LLWearableType::WT_COUNT)
- {
- const LLUUID parent_id = bridge ? bridge->getUUID() : LLUUID::null;
- LLAgentWearables::createWearable(wearable_type, false, parent_id);
- }
- else
- {
- LL_WARNS(LOG_INV) << "Can't create unrecognized type " << type_name << LL_ENDL;
- }
- }
- panel->getRootFolder()->setNeedsAutoRename(TRUE);
}
LLAssetType::EType LLViewerInventoryItem::getType() const
@@ -1895,6 +1988,25 @@ const LLSaleInfo& LLViewerInventoryItem::getSaleInfo() const
return LLInventoryItem::getSaleInfo();
}
+const LLUUID& LLViewerInventoryItem::getThumbnailUUID() const
+{
+ if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_TEXTURE)
+ {
+ return mAssetUUID;
+ }
+ if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_LINK)
+ {
+ LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID);
+ return linked_item ? linked_item->getThumbnailUUID() : LLUUID::null;
+ }
+ if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_LINK_FOLDER)
+ {
+ LLViewerInventoryCategory *linked_cat = gInventory.getCategory(mAssetUUID);
+ return linked_cat ? linked_cat->getThumbnailUUID() : LLUUID::null;
+ }
+ return mThumbnailUUID;
+}
+
LLInventoryType::EType LLViewerInventoryItem::getInventoryType() const
{
if (const LLViewerInventoryItem *linked_item = getLinkedItem())
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
index 24b632632b..6d3676ba2e 100644
--- a/indra/newview/llviewerinventory.h
+++ b/indra/newview/llviewerinventory.h
@@ -72,6 +72,7 @@ public:
virtual const LLUUID& getCreatorUUID() const;
virtual const std::string& getDescription() const;
virtual const LLSaleInfo& getSaleInfo() const;
+ virtual const LLUUID& getThumbnailUUID() const;
virtual LLInventoryType::EType getInventoryType() const;
virtual bool isWearableType() const;
virtual LLWearableType::EType getWearableType() const;
@@ -134,8 +135,8 @@ public:
virtual BOOL importLegacyStream(std::istream& input_stream);
// new methods
- BOOL isFinished() const { return mIsComplete; }
- void setComplete(BOOL complete) { mIsComplete = complete; }
+ bool isFinished() const { return mIsComplete; }
+ void setComplete(bool complete) { mIsComplete = complete; }
//void updateAssetOnServer() const;
virtual void setTransactionID(const LLTransactionID& transaction_id);
@@ -163,7 +164,7 @@ public:
BOOL regenerateLink();
public:
- BOOL mIsComplete;
+ bool mIsComplete;
LLTransactionID mTransactionID;
};
@@ -208,9 +209,18 @@ public:
S32 getVersion() const;
void setVersion(S32 version);
- // Returns true if a fetch was issued.
+ // Returns true if a fetch was issued (not nessesary in progress).
bool fetch();
+ typedef enum {
+ FETCH_NONE = 0,
+ FETCH_NORMAL,
+ FETCH_RECURSIVE,
+ } EFetchType;
+ EFetchType getFetching();
+ // marks as fetch being in progress or as done
+ void setFetching(EFetchType);
+
// used to help make caching more robust - for example, if
// someone is getting 4 packets but logs out after 3. the viewer
// may never know the cache is wrong.
@@ -239,12 +249,14 @@ protected:
LLUUID mOwnerID;
S32 mVersion;
S32 mDescendentCount;
+ EFetchType mFetching;
LLFrameTimer mDescendentsRequested;
};
class LLInventoryCallback : public LLRefCount
{
public:
+ virtual ~LLInventoryCallback() {}
virtual void fire(const LLUUID& inv_item) = 0;
};
@@ -283,17 +295,29 @@ class LLBoostFuncInventoryCallback: public LLInventoryCallback
{
public:
- LLBoostFuncInventoryCallback(inventory_func_type fire_func = no_op_inventory_func,
+ LLBoostFuncInventoryCallback(inventory_func_type fire_func,
nullary_func_type destroy_func = no_op):
- mFireFunc(fire_func),
mDestroyFunc(destroy_func)
{
+ mFireFuncs.push_back(fire_func);
}
+ LLBoostFuncInventoryCallback()
+ {
+ }
+
+ void addOnFireFunc(inventory_func_type fire_func)
+ {
+ mFireFuncs.push_back(fire_func);
+ }
+
// virtual
void fire(const LLUUID& item_id)
{
- mFireFunc(item_id);
+ for (inventory_func_type &func: mFireFuncs)
+ {
+ func(item_id);
+ }
}
// virtual
@@ -304,7 +328,7 @@ public:
private:
- inventory_func_type mFireFunc;
+ std::list<inventory_func_type> mFireFuncs;
nullary_func_type mDestroyFunc;
};
@@ -445,6 +469,8 @@ void menu_create_inventory_item(LLInventoryPanel* root,
const LLSD& userdata,
const LLUUID& default_parent_uuid = LLUUID::null);
+void menu_create_inventory_item(LLInventoryPanel* panel, LLUUID dest_id, const LLSD& userdata, const LLUUID& default_parent_uuid = LLUUID::null, std::function<void(const LLUUID&)> folder_created_cb = NULL);
+
void slam_inventory_folder(const LLUUID& folder_id,
const LLSD& contents,
LLPointer<LLInventoryCallback> cb);
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index cc59e559e9..8686fad3e6 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -402,14 +402,14 @@ void set_merchant_SLM_menu()
LLCommand* command = LLCommandManager::instance().getCommand("marketplacelistings");
gToolBarView->enableCommand(command->id(), true);
- const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
if (marketplacelistings_id.isNull())
{
U32 mkt_status = LLMarketplaceData::instance().getSLMStatus();
bool is_merchant = (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) || (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT);
if (is_merchant)
{
- gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true);
+ gInventory.ensureCategoryForTypeExists(LLFolderType::FT_MARKETPLACE_LISTINGS);
LL_WARNS("SLM") << "Creating the marketplace listings folder for a merchant" << LL_ENDL;
}
}
@@ -3065,9 +3065,7 @@ void handle_object_inspect()
LLViewerObject* selected_objectp = selection->getFirstRootObject();
if (selected_objectp)
{
- LLSD key;
- key["task"] = "task";
- LLFloaterSidePanelContainer::showPanel("inventory", key);
+ LLFloaterReg::showInstance("task_properties");
}
/*
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 2634c8d908..17e89e1850 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -38,7 +38,7 @@
#include "llfloatermap.h"
#include "llfloatermodelpreview.h"
#include "llfloatersnapshot.h"
-#include "llfloatersimpleoutfitsnapshot.h"
+#include "llfloatersimplesnapshot.h"
#include "llimage.h"
#include "llimagebmp.h"
#include "llimagepng.h"
@@ -735,9 +735,7 @@ class LLFileEnableCloseAllWindows : public view_listener_t
bool handleEvent(const LLSD& userdata)
{
LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance();
- LLFloaterSimpleOutfitSnapshot* floater_outfit_snapshot = LLFloaterSimpleOutfitSnapshot::findInstance();
- bool is_floaters_snapshot_opened = (floater_snapshot && floater_snapshot->isInVisibleChain())
- || (floater_outfit_snapshot && floater_outfit_snapshot->isInVisibleChain());
+ bool is_floaters_snapshot_opened = (floater_snapshot && floater_snapshot->isInVisibleChain());
bool open_children = gFloaterView->allChildrenClosed() && !is_floaters_snapshot_opened;
return !open_children && !LLNotificationsUI::LLToast::isAlertToastShown();
}
@@ -752,9 +750,6 @@ class LLFileCloseAllWindows : public view_listener_t
LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance();
if (floater_snapshot)
floater_snapshot->closeFloater(app_quitting);
- LLFloaterSimpleOutfitSnapshot* floater_outfit_snapshot = LLFloaterSimpleOutfitSnapshot::findInstance();
- if (floater_outfit_snapshot)
- floater_outfit_snapshot->closeFloater(app_quitting);
if (gMenuHolder) gMenuHolder->hideMenus();
return true;
}
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 7421bba733..f3288a5300 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -76,6 +76,7 @@
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llpanelgrouplandmoney.h"
+#include "llpanelmaininventory.h"
#include "llrecentpeople.h"
#include "llscriptfloater.h"
#include "llscriptruntimeperms.h"
@@ -1537,11 +1538,35 @@ void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_nam
}
////////////////////////////////////////////////////////////////////////////////
+ static LLUICachedControl<bool> find_original_new_floater("FindOriginalOpenWindow", false);
+ //show in a new single-folder window
+ if(find_original_new_floater && !from_name.empty())
+ {
+ const LLInventoryObject *obj = gInventory.getObject(obj_id);
+ if (obj && obj->getParentUUID().notNull())
+ {
+ if (obj->getActualType() == LLAssetType::AT_CATEGORY)
+ {
+ LLPanelMainInventory::newFolderWindow(obj_id);
+ }
+ else
+ {
+ LLPanelMainInventory::newFolderWindow(obj->getParentUUID(), obj_id);
+ }
+ }
+ }
+ else
+ {
// Highlight item
const BOOL auto_open =
gSavedSettings.getBOOL("ShowInInventory") && // don't open if showininventory is false
!from_name.empty(); // don't open if it's not from anyone.
- LLInventoryPanel::openInventoryPanelAndSetSelection(auto_open, obj_id);
+ if(auto_open)
+ {
+ LLFloaterReg::showInstance("inventory");
+ }
+ LLInventoryPanel::openInventoryPanelAndSetSelection(auto_open, obj_id, true);
+ }
}
}
@@ -5918,42 +5943,47 @@ void container_inventory_arrived(LLViewerObject* object,
{
// create a new inventory category to put this in
LLUUID cat_id;
- cat_id = gInventory.createNewCategory(gInventory.getRootFolderID(),
- LLFolderType::FT_NONE,
- LLTrans::getString("AcquiredItems"));
+ gInventory.createNewCategory(
+ gInventory.getRootFolderID(),
+ LLFolderType::FT_NONE,
+ LLTrans::getString("AcquiredItems"),
+ [inventory](const LLUUID &new_cat_id)
+ {
+ LLInventoryObject::object_list_t::const_iterator it = inventory->begin();
+ LLInventoryObject::object_list_t::const_iterator end = inventory->end();
+ for (; it != end; ++it)
+ {
+ if ((*it)->getType() != LLAssetType::AT_CATEGORY)
+ {
+ LLInventoryObject* obj = (LLInventoryObject*)(*it);
+ LLInventoryItem* item = (LLInventoryItem*)(obj);
+ LLUUID item_id;
+ item_id.generate();
+ time_t creation_date_utc = time_corrected();
+ LLPointer<LLViewerInventoryItem> new_item
+ = new LLViewerInventoryItem(item_id,
+ new_cat_id,
+ item->getPermissions(),
+ item->getAssetUUID(),
+ item->getType(),
+ item->getInventoryType(),
+ item->getName(),
+ item->getDescription(),
+ LLSaleInfo::DEFAULT,
+ item->getFlags(),
+ creation_date_utc);
+ new_item->updateServer(TRUE);
+ gInventory.updateItem(new_item);
+ }
+ }
+ gInventory.notifyObservers();
- LLInventoryObject::object_list_t::const_iterator it = inventory->begin();
- LLInventoryObject::object_list_t::const_iterator end = inventory->end();
- for ( ; it != end; ++it)
- {
- if ((*it)->getType() != LLAssetType::AT_CATEGORY)
- {
- LLInventoryObject* obj = (LLInventoryObject*)(*it);
- LLInventoryItem* item = (LLInventoryItem*)(obj);
- LLUUID item_id;
- item_id.generate();
- time_t creation_date_utc = time_corrected();
- LLPointer<LLViewerInventoryItem> new_item
- = new LLViewerInventoryItem(item_id,
- cat_id,
- item->getPermissions(),
- item->getAssetUUID(),
- item->getType(),
- item->getInventoryType(),
- item->getName(),
- item->getDescription(),
- LLSaleInfo::DEFAULT,
- item->getFlags(),
- creation_date_utc);
- new_item->updateServer(TRUE);
- gInventory.updateItem(new_item);
- }
- }
- gInventory.notifyObservers();
- if(active_panel)
- {
- active_panel->setSelection(cat_id, TAKE_FOCUS_NO);
- }
+ LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel();
+ if (active_panel)
+ {
+ active_panel->setSelection(new_cat_id, TAKE_FOCUS_NO);
+ }
+ });
}
else if (inventory->size() == 2)
{
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index f47f0b4572..9275cfb86d 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -63,7 +63,6 @@
#include "llcontrolavatar.h"
#include "lldrawable.h"
#include "llface.h"
-#include "llfloaterproperties.h"
#include "llfloatertools.h"
#include "llfollowcam.h"
#include "llhudtext.h"
@@ -3474,9 +3473,6 @@ void LLViewerObject::doInventoryCallback()
void LLViewerObject::removeInventory(const LLUUID& item_id)
{
- // close any associated floater properties
- LLFloaterReg::hideInstance("properties", item_id);
-
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_RemoveTaskInventory);
msg->nextBlockFast(_PREHASH_AgentData);
@@ -6038,7 +6034,7 @@ LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 para
}
default:
{
- LL_INFOS() << "Unknown param type." << LL_ENDL;
+ LL_INFOS_ONCE() << "Unknown param type: " << param_type << LL_ENDL;
break;
}
};
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 4a1dd1b8d6..84956d3b3d 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -3027,6 +3027,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("InterestList");
+ capabilityNames.append("InventoryThumbnailUpload");
capabilityNames.append("GetDisplayNames");
capabilityNames.append("GetExperiences");
capabilityNames.append("AgentExperiences");
diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp
index d2701f0aff..c28fed9ca4 100644
--- a/indra/newview/llviewertexteditor.cpp
+++ b/indra/newview/llviewertexteditor.cpp
@@ -36,6 +36,7 @@
#include "llfloatersidepanelcontainer.h"
#include "llfloaterworldmap.h"
#include "llfocusmgr.h"
+#include "llinspecttexture.h"
#include "llinventorybridge.h"
#include "llinventorydefines.h"
#include "llinventorymodel.h"
@@ -245,6 +246,21 @@ public:
}
virtual BOOL handleToolTip(S32 x, S32 y, MASK mask )
{
+ if (mItem->getThumbnailUUID().notNull())
+ {
+ LLSD params;
+ params["inv_type"] = mItem->getInventoryType();
+ params["thumbnail_id"] = mItem->getThumbnailUUID();
+ params["asset_id"] = mItem->getAssetUUID();
+
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(mToolTip)
+ .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1))
+ .create_params(params));
+
+ return TRUE;
+ }
+
if (!mToolTip.empty())
{
LLToolTipMgr::instance().show(mToolTip);
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index b48290776a..ddb7014648 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -1295,11 +1295,52 @@ void LLViewerTextureList::decodeAllImages(F32 max_time)
<< LL_ENDL;
}
+bool LLViewerTextureList::createUploadFile(LLPointer<LLImageRaw> raw_image,
+ const std::string& out_filename,
+ const S32 max_image_dimentions,
+ const S32 min_image_dimentions)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+
+ // make a copy, since convertToUploadFile scales raw image
+ LLPointer<LLImageRaw> scale_image = new LLImageRaw(
+ raw_image->getData(),
+ raw_image->getWidth(),
+ raw_image->getHeight(),
+ raw_image->getComponents());
+
+ LLPointer<LLImageJ2C> compressedImage = LLViewerTextureList::convertToUploadFile(scale_image, max_image_dimentions);
+ if (compressedImage->getWidth() < min_image_dimentions || compressedImage->getHeight() < min_image_dimentions)
+ {
+ std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
+ min_image_dimentions,
+ min_image_dimentions,
+ compressedImage->getWidth(),
+ compressedImage->getHeight());
+ compressedImage->setLastError(reason);
+ return false;
+ }
+ if (compressedImage.isNull())
+ {
+ compressedImage->setLastError("Couldn't convert the image to jpeg2000.");
+ LL_INFOS() << "Couldn't convert to j2c, file : " << out_filename << LL_ENDL;
+ return false;
+ }
+ if (!compressedImage->save(out_filename))
+ {
+ compressedImage->setLastError("Couldn't create the jpeg2000 image for upload.");
+ LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
+ return false;
+ }
+ return true;
+}
BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
const std::string& out_filename,
const U8 codec,
- const S32 max_image_dimentions)
+ const S32 max_image_dimentions,
+ const S32 min_image_dimentions,
+ bool force_square)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
// Load the image
@@ -1327,8 +1368,18 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return FALSE;
}
+ if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions)
+ {
+ std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
+ min_image_dimentions,
+ min_image_dimentions,
+ image->getWidth(),
+ image->getHeight());
+ image->setLastError(reason);
+ return FALSE;
+ }
// Convert to j2c (JPEG2000) and save the file locally
- LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions);
+ LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);
if (compressedImage.isNull())
{
image->setLastError("Couldn't convert the image to jpeg2000.");
@@ -1353,10 +1404,20 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
}
// note: modifies the argument raw_image!!!!
-LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImageRaw> raw_image, const S32 max_image_dimentions)
+LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImageRaw> raw_image, const S32 max_image_dimentions, bool force_square)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- raw_image->biasedScaleToPowerOfTwo(max_image_dimentions);
+ if (force_square)
+ {
+ S32 biggest_side = llmax(raw_image->getWidth(), raw_image->getHeight());
+ S32 square_size = raw_image->biasedDimToPowerOfTwo(biggest_side, max_image_dimentions);
+
+ raw_image->scale(square_size, square_size);
+ }
+ else
+ {
+ raw_image->biasedScaleToPowerOfTwo(max_image_dimentions);
+ }
LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C();
if (gSavedSettings.getBOOL("LosslessJ2CUpload") &&
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index 0018e78d45..82dec6b329 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -92,11 +92,19 @@ class LLViewerTextureList
friend class LLLocalBitmap;
public:
+ static bool createUploadFile(LLPointer<LLImageRaw> raw_image,
+ const std::string& out_filename,
+ const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT,
+ const S32 min_image_dimentions = 0);
static BOOL createUploadFile(const std::string& filename,
const std::string& out_filename,
const U8 codec,
- const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
- static LLPointer<LLImageJ2C> convertToUploadFile(LLPointer<LLImageRaw> raw_image, const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
+ const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT,
+ const S32 min_image_dimentions = 0,
+ bool force_square = false);
+ static LLPointer<LLImageJ2C> convertToUploadFile(LLPointer<LLImageRaw> raw_image,
+ const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT,
+ bool force_square = false);
static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data );
public:
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 348442cf18..185e8612bc 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -9455,7 +9455,14 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
// RequestAgentUpdateAppearanceResponder::onRequestRequested()
// assumes that cof version is only updated with server-bake
// appearance messages.
- LL_INFOS("Avatar") << "Processing appearance message version " << thisAppearanceVersion << LL_ENDL;
+ if (isSelf())
+ {
+ LL_INFOS("Avatar") << "Processing appearance message version " << thisAppearanceVersion << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("Avatar") << "Processing appearance message for " << getID() << ", version " << thisAppearanceVersion << LL_ENDL;
+ }
// Note:
// locally the COF is maintained via LLInventoryModel::accountForUpdate
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 86c6567c83..c0d4dd0181 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -5114,8 +5114,6 @@ void LLControlAVBridge::updateSpatialExtents()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE
- LLControlAvatar* controlAvatar = getVObj()->getControlAvatar();
-
LLSpatialGroup* root = (LLSpatialGroup*)mOctree->getListener(0);
bool rootWasDirty = root->isDirty();
@@ -5126,7 +5124,11 @@ void LLControlAVBridge::updateSpatialExtents()
// disappear when root goes off-screen"
//
// Expand extents to include Control Avatar placed outside of the bounds
- if (controlAvatar && (rootWasDirty || controlAvatar->mPlaying))
+ LLControlAvatar* controlAvatar = getVObj() ? getVObj()->getControlAvatar() : NULL;
+ if (controlAvatar
+ && controlAvatar->mDrawable
+ && controlAvatar->mDrawable->getEntry()
+ && (rootWasDirty || controlAvatar->mPlaying))
{
root->expandExtents(controlAvatar->mDrawable->getSpatialExtents(), *mDrawable->getXform());
}
diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml
index a6820086fc..c977cd3bef 100644
--- a/indra/newview/skins/default/colors.xml
+++ b/indra/newview/skins/default/colors.xml
@@ -407,7 +407,7 @@
value="0.43 0.06 0.06 1" />
<color
name="HTMLLinkColor"
- reference="EmphasisColor" />
+ value="0.3 0.82 1 1" />
<color
name="HealthTextColor"
reference="White" />
diff --git a/indra/newview/skins/default/textures/icons/copy_clipboard.png b/indra/newview/skins/default/textures/icons/copy_clipboard.png
new file mode 100644
index 0000000000..bb1ceff1ce
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/copy_clipboard.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/delete_icon.png b/indra/newview/skins/default/textures/icons/delete_icon.png
new file mode 100644
index 0000000000..37ce374653
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/delete_icon.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/file_upload.png b/indra/newview/skins/default/textures/icons/file_upload.png
new file mode 100644
index 0000000000..58f2757136
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/file_upload.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/multi_folder_mode.png b/indra/newview/skins/default/textures/icons/multi_folder_mode.png
new file mode 100644
index 0000000000..8cda3efc36
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/multi_folder_mode.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/paste_clipboard.png b/indra/newview/skins/default/textures/icons/paste_clipboard.png
new file mode 100644
index 0000000000..e1589ab098
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/paste_clipboard.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/single_folder_back.png b/indra/newview/skins/default/textures/icons/single_folder_back.png
new file mode 100644
index 0000000000..b614e9ef9b
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/single_folder_back.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/single_folder_forward.png b/indra/newview/skins/default/textures/icons/single_folder_forward.png
new file mode 100644
index 0000000000..c7bee3522d
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/single_folder_forward.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/single_folder_mode.png b/indra/newview/skins/default/textures/icons/single_folder_mode.png
new file mode 100644
index 0000000000..f70b754123
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/single_folder_mode.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/single_folder_up.png b/indra/newview/skins/default/textures/icons/single_folder_up.png
new file mode 100644
index 0000000000..651b2b1af1
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/single_folder_up.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/snapshot_icon.png b/indra/newview/skins/default/textures/icons/snapshot_icon.png
new file mode 100644
index 0000000000..41d524678f
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/snapshot_icon.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/texture_icon.png b/indra/newview/skins/default/textures/icons/texture_icon.png
new file mode 100644
index 0000000000..278760a5b0
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/texture_icon.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.png b/indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.png
new file mode 100644
index 0000000000..8d5ca624af
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index d0fa19f1a1..656c9b146e 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -253,10 +253,16 @@ with the same filename but different name
<texture name="Icon_Close_Foreground" file_name="windows/Icon_Close_Foreground.png" preload="true" />
<texture name="Icon_Close_Press" file_name="windows/Icon_Close_Press.png" preload="true" />
<texture name="Icon_Close_Toast" file_name="windows/Icon_Close_Toast.png" preload="true" />
+
+ <texture name="Icon_Copy" file_name="icons/copy_clipboard.png" preload="true" />
+
+ <texture name="Icon_Delete" file_name="icons/delete_icon.png" preload="true" />
<texture name="Icon_Dock_Foreground" file_name="windows/Icon_Dock_Foreground.png" preload="true" />
<texture name="Icon_Dock_Press" file_name="windows/Icon_Dock_Press.png" preload="true" />
+ <texture name="Icon_File_Upload" file_name="icons/file_upload.png" preload="true" />
+
<texture name="Icon_For_Sale" file_name="icons/Icon_For_Sale.png" preload="false" />
<texture name="Icon_Gear_Background" file_name="windows/Icon_Gear_Background.png" preload="false" />
@@ -269,10 +275,16 @@ with the same filename but different name
<texture name="Icon_Minimize_Foreground" file_name="windows/Icon_Minimize_Foreground.png" preload="true" />
<texture name="Icon_Minimize_Press" file_name="windows/Icon_Minimize_Press.png" preload="true" />
+
+ <texture name="Icon_Paste" file_name="icons/paste_clipboard.png" preload="true" />
<texture name="Icon_Restore_Foreground" file_name="windows/Icon_Restore_Foreground.png" preload="false" />
<texture name="Icon_Restore_Press" file_name="windows/Icon_Restore_Press.png" preload="false" />
+ <texture name="Icon_Snapshot" file_name="icons/snapshot_icon.png" preload="true" />
+
+ <texture name="Icon_Use_Texture" file_name="icons/texture_icon.png" preload="true" />
+
<texture name="Info" file_name="icons/Info.png" preload="false" />
<texture name="Info_Small" file_name="icons/Info_Small.png" preload="false" />
<texture name="Info_Off" file_name="navbar/Info_Off.png" preload="false" />
@@ -685,6 +697,7 @@ with the same filename but different name
<texture name="TextField_Active" file_name="widgets/TextField_Active.png" preload="true" scale.left="9" scale.top="12" scale.right="248" scale.bottom="12" />
<texture name="TextField_Search_Highlight" file_name="widgets/TextField_Search_Highlight.png" preload="true" scale.left="9" scale.top="12" scale.right="248" scale.bottom="12" />
+ <texture name="Thumbnail_Fallback" file_name="icons/thumbnail_fallback_icon.png" preload="true" />
<texture name="Toast_CloseBtn" file_name="windows/Toast_CloseBtn.png" preload="true" />
<texture name="Toast_Background" file_name="windows/Toast_Background.png" preload="true"
@@ -881,4 +894,10 @@ with the same filename but different name
<texture name="System_Notification" file_name="icons/SL_Logo.png" preload="true"/>
<texture name="Icon_Attachment_Small" file_name="icons/Icon_Attachment_Small.png" preload="true"/>
<texture name="Icon_Attachment_Large" file_name="icons/Icon_Attachment_Large.png" preload="true"/>
+
+ <texture name="Single_Folder_Mode" file_name="icons/single_folder_mode.png" preload="true"/>
+ <texture name="Multi_Folder_Mode" file_name="icons/multi_folder_mode.png" preload="true"/>
+ <texture name="Single_Folder_Back" file_name="icons/single_folder_back.png" preload="true"/>
+ <texture name="Single_Folder_Forward" file_name="icons/single_folder_forward.png" preload="true"/>
+ <texture name="Single_Folder_Up" file_name="icons/single_folder_up.png" preload="true"/>
</textures>
diff --git a/indra/newview/skins/default/xui/da/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/da/floater_inventory_item_properties.xml
deleted file mode 100644
index 59dcc87140..0000000000
--- a/indra/newview/skins/default/xui/da/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="OPLYSNINGER OM BEHOLDNINGSGENSTAND">
- <floater.string name="unknown">
- (ukendt)
- </floater.string>
- <floater.string name="public">
- (offentlig)
- </floater.string>
- <floater.string name="you_can">
- Du kan:
- </floater.string>
- <floater.string name="owner_can">
- Ejer kan:
- </floater.string>
- <floater.string name="acquiredDate">
- [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
- </floater.string>
- <text name="LabelItemNameTitle">
- Navn:
- </text>
- <text name="LabelItemDescTitle">
- Beskrivelse:
- </text>
- <text name="LabelCreatorTitle">
- Skaber:
- </text>
- <button label="Profil..." label_selected="" name="BtnCreator"/>
- <text name="LabelOwnerTitle">
- Ejer:
- </text>
- <button label="Profil..." label_selected="" name="BtnOwner"/>
- <text name="LabelAcquiredTitle">
- Erhvervet:
- </text>
- <text name="LabelAcquiredDate">
- Wed May 24 12:50:46 2006
- </text>
- <text name="OwnerLabel">
- Dig:
- </text>
- <check_box label="Redigér" name="CheckOwnerModify"/>
- <check_box label="Kopiere" name="CheckOwnerCopy"/>
- <check_box label="Sælg" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel">
- Enhver:
- </text>
- <check_box label="Kopiér" name="CheckEveryoneCopy"/>
- <text name="GroupLabel">
- Gruppe:
- </text>
- <check_box label="Del" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel">
- Næste ejer:
- </text>
- <check_box label="Redigér" name="CheckNextOwnerModify"/>
- <check_box label="Kopiere" name="CheckNextOwnerCopy"/>
- <check_box label="Sælg" name="CheckNextOwnerTransfer"/>
- <check_box label="Til salg" name="CheckPurchase"/>
- <combo_box name="combobox sale copy">
- <combo_box.item label="Kopiér" name="Copy"/>
- <combo_box.item label="Original" name="Original"/>
- </combo_box>
- <spinner label="Pris:" name="Edit Cost"/>
- <text name="CurrencySymbol">
- L$
- </text>
-</floater>
diff --git a/indra/newview/skins/default/xui/de/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/de/floater_inventory_item_properties.xml
deleted file mode 100644
index 92c038057f..0000000000
--- a/indra/newview/skins/default/xui/de/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="EIGENSCHAFTEN: INVENTAROBJEKT">
- <floater.string name="unknown">(unbekannt)</floater.string>
- <floater.string name="public">(öffentlich)</floater.string>
- <floater.string name="you_can">Sie können:</floater.string>
- <floater.string name="owner_can">Eigentümer kann:</floater.string>
- <floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string>
- <text name="LabelItemNameTitle">Name:</text>
- <text name="LabelItemDescTitle">Beschreibung:</text>
- <text name="LabelCreatorTitle">Ersteller:</text>
- <button label="Profil..." label_selected="" name="BtnCreator"/>
- <text name="LabelOwnerTitle">Eigentümer:</text>
- <button label="Profil..." label_selected="" name="BtnOwner"/>
- <text name="LabelAcquiredTitle">Erworben:</text>
- <text name="LabelAcquiredDate">Mittwoch, 24. Mai 2006, 12:50:46</text>
- <text name="OwnerLabel">Sie:</text>
- <check_box label="Bearbeiten" name="CheckOwnerModify"/>
- <check_box label="Kopieren" left_delta="85" name="CheckOwnerCopy"/>
- <check_box label="Wiederverkaufen" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel">Jeder:</text>
- <check_box label="Kopieren" name="CheckEveryoneCopy"/>
- <text name="GroupLabel">Gruppe:</text>
- <check_box label="Teilen" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel" width="150">Nächster Eigentümer:</text>
- <check_box label="Bearbeiten" name="CheckNextOwnerModify"/>
- <check_box label="Kopieren" left_delta="55" name="CheckNextOwnerCopy"/>
- <check_box label="Wiederverkaufen" name="CheckNextOwnerTransfer"/>
- <check_box label="Zum Verkauf" name="CheckPurchase"/>
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="Kopie" name="Copy"/>
- <combo_box.item label="Inhalt" name="Contents"/>
- <combo_box.item label="Original" name="Original"/>
- </combo_box>
- <spinner label="Preis:" name="Edit Cost"/>
- <text name="CurrencySymbol">L$</text>
-</floater>
diff --git a/indra/newview/skins/default/xui/de/panel_main_inventory.xml b/indra/newview/skins/default/xui/de/panel_main_inventory.xml
index a3adea9fa2..175f6e1003 100644
--- a/indra/newview/skins/default/xui/de/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/de/panel_main_inventory.xml
@@ -9,6 +9,7 @@
<panel.string name="ItemcountUnknown">
Geholte [ITEM_COUNT] Bestellungen und [CATEGORY_COUNT] Ordner [FILTER]
</panel.string>
+ <panel.string name="inventory_title">INVENTAR</panel.string>
<text name="ItemcountText">
Objekte:
</text>
diff --git a/indra/newview/skins/default/xui/en/floater_change_item_thumbnail.xml b/indra/newview/skins/default/xui/en/floater_change_item_thumbnail.xml
new file mode 100644
index 0000000000..726cb38481
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_change_item_thumbnail.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ can_resize="false"
+ height="366"
+ layout="topleft"
+ name="change_item_thumbnail"
+ help_topic="change_item_thumbnail"
+ title="CHANGE ITEM IMAGE"
+ width="319">
+
+ <floater.string
+ name="title_item_thumbnail">
+ CHANGE ITEM IMAGE
+ </floater.string>
+ <floater.string
+ name="title_folder_thumbnail">
+ CHANGE FOLDER IMAGE
+ </floater.string>
+ <floater.string
+ name="tooltip_upload_local">
+ Upload from computer
+ </floater.string>
+ <floater.string
+ name="tooltip_upload_snapshot">
+ Use snapshot tool
+ </floater.string>
+ <floater.string
+ name="tooltip_use_texture">
+ Choose texture
+ </floater.string>
+ <floater.string
+ name="tooltip_copy_to_clipboard">
+ Copy to clipboard
+ </floater.string>
+ <floater.string
+ name="tooltip_paste_from_clipboard">
+ Paste from clipboard
+ </floater.string>
+ <floater.string
+ name="tooltip_remove_image">
+ Remove image
+ </floater.string>
+
+ <icon
+ follows="top|left"
+ height="16"
+ image_name="Inv_Object"
+ layout="topleft"
+ left="10"
+ mouse_opaque="true"
+ name="item_type_icon"
+ top="4"
+ width="16" />
+ <text
+ name="item_name"
+ font="SansSerif"
+ use_ellipses="true"
+ follows="left|top|right"
+ layout="topleft"
+ height="19"
+ top_delta="1"
+ left_pad="3"
+ width="286"/>
+
+ <thumbnail
+ name="item_thumbnail"
+ fallback_image="Thumbnail_Fallback"
+ follows="top|left"
+ layout="topleft"
+ left="32"
+ top_pad="9"
+ height="256"
+ width="256"
+ />
+
+ <button
+ follows="right|bottom"
+ layout="topleft"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Icon_File_Upload"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ name="upload_local"
+ left="38"
+ top_pad="9"
+ height="32"
+ width="32" />
+ <button
+ follows="right|bottom"
+ layout="topleft"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Icon_Snapshot"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ name="upload_snapshot"
+ left_pad="10"
+ top_delta="0"
+ height="32"
+ width="32" />
+ <button
+ follows="right|bottom"
+ layout="topleft"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Icon_Use_Texture"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ name="use_texture"
+ left_pad="10"
+ top_delta="0"
+ height="32"
+ width="32" />
+ <button
+ follows="right|bottom"
+ layout="topleft"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Icon_Copy"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ name="copy_to_clipboard"
+ left_pad="10"
+ top_delta="0"
+ height="32"
+ width="32" />
+ <button
+ follows="right|bottom"
+ layout="topleft"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Icon_Paste"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ name="paste_from_clipboard"
+ left_pad="10"
+ top_delta="0"
+ height="32"
+ width="32" />
+ <button
+ follows="right|bottom"
+ layout="topleft"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Icon_Delete"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ name="remove_image"
+ left_pad="10"
+ top_delta="0"
+ height="32"
+ width="32" />
+
+ <text
+ type="string"
+ halign="center"
+ length="1"
+ follows="left|top"
+ height="17"
+ layout="topleft"
+ left="5"
+ right="-5"
+ name="tooltip_text"
+ top_pad="12"
+ width="78">
+ tooltip
+ </text>
+
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_inventory_settings.xml b/indra/newview/skins/default/xui/en/floater_inventory_settings.xml
new file mode 100644
index 0000000000..2e619d91fb
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_inventory_settings.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ can_close="true"
+ can_minimize="true"
+ can_resize="false"
+ save_rect="true"
+ height="370"
+ width="370"
+ name="inventory_settings"
+ title="INVENTORY SETTINGS">
+ <icon
+ follows="top|left"
+ height="18"
+ image_name="Multi_Folder_Mode"
+ layout="topleft"
+ left="18"
+ mouse_opaque="true"
+ name="multi_folder_icon"
+ top="25"
+ width="18" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="13"
+ layout="topleft"
+ left_pad="12"
+ top_delta="2"
+ name="multi_folder_txt"
+ font="SansSerifMedium"
+ text_color="White"
+ width="300">
+ Double-click on folder in multi-folder view:
+ </text>
+ <radio_group
+ control_name="MultiModeDoubleClickFolder"
+ follows="left|top"
+ top_pad="8"
+ layout="topleft"
+ font="SansSerifMedium"
+ left="60"
+ width="300"
+ height="70"
+ name="multi_double_click_setting">
+ <radio_item
+ height="20"
+ label="Expands &amp; collapses folder"
+ label_text.text_color="White"
+ follows="left|top"
+ layout="topleft"
+ name="0"
+ width="200"/>
+ <radio_item
+ height="20"
+ follows="left|top"
+ label="Opens a new window"
+ label_text.text_color="White"
+ layout="topleft"
+ left_delta="0"
+ name="1"
+ top_pad ="5"
+ width="200" />
+ <radio_item
+ height="20"
+ follows="left|top"
+ label="Switches view"
+ label_text.text_color="White"
+ layout="topleft"
+ left_delta="0"
+ name="2"
+ top_pad ="5"
+ width="200" />
+ </radio_group>
+ <icon
+ follows="top|left"
+ height="18"
+ image_name="Single_Folder_Mode"
+ layout="topleft"
+ left="18"
+ mouse_opaque="true"
+ name="single_folder_icon"
+ top_pad="30"
+ width="18" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="13"
+ layout="topleft"
+ left_pad="12"
+ top_delta="2"
+ name="single_folder_txt"
+ font="SansSerifMedium"
+ text_color="White"
+ width="300">
+ Double-click on folder in single-folder view:
+ </text>
+ <radio_group
+ control_name="SingleModeDoubleClickOpenWindow"
+ follows="left|top"
+ top_pad="8"
+ layout="topleft"
+ font="SansSerifMedium"
+ left="60"
+ width="300"
+ height="45"
+ name="single_double_click_setting">
+ <radio_item
+ height="20"
+ label="Stays in current window"
+ label_text.text_color="White"
+ follows="left|top"
+ layout="topleft"
+ name="false"
+ width="200"/>
+ <radio_item
+ height="20"
+ follows="left|top"
+ label="Opens a new window"
+ label_text.text_color="White"
+ layout="topleft"
+ left_delta="0"
+ name="true"
+ top_pad ="5"
+ width="200" />
+ </radio_group>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="13"
+ layout="topleft"
+ left="48"
+ name="find_original_txt"
+ font="SansSerifMedium"
+ text_color="White"
+ top_pad="30"
+ width="300">
+ Clicking on "Show in inventory" or "Find original"
+ </text>
+ <radio_group
+ control_name="FindOriginalOpenWindow"
+ follows="left|top"
+ top_pad="8"
+ layout="topleft"
+ font="SansSerifMedium"
+ left="60"
+ width="300"
+ height="45"
+ name="find_original_settings">
+ <radio_item
+ height="20"
+ label="Shows item in main inventory window"
+ label_text.text_color="White"
+ follows="left|top"
+ layout="topleft"
+ name="false"
+ width="200"/>
+ <radio_item
+ height="20"
+ follows="left|top"
+ label="Opens a new single-folder window"
+ label_text.text_color="White"
+ layout="topleft"
+ left_delta="0"
+ name="true"
+ top_pad ="5"
+ width="200" />
+ </radio_group>
+ <button
+ height="20"
+ label="OK"
+ layout="topleft"
+ left="140"
+ bottom="-20"
+ name="ok_btn"
+ label_color="White"
+ width="90" />
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_item_properties.xml b/indra/newview/skins/default/xui/en/floater_item_properties.xml
index 0fc54a9c8b..336bb902ca 100644
--- a/indra/newview/skins/default/xui/en/floater_item_properties.xml
+++ b/indra/newview/skins/default/xui/en/floater_item_properties.xml
@@ -13,7 +13,7 @@
left="0"
class="sidepanel_item_info"
filename="sidepanel_item_info.xml"
- name="item_panel"
+ name="sidepanel"
top="20"
label=""
height="570"
diff --git a/indra/newview/skins/default/xui/en/floater_my_inventory.xml b/indra/newview/skins/default/xui/en/floater_my_inventory.xml
index f182d27da8..a9900f05b7 100644
--- a/indra/newview/skins/default/xui/en/floater_my_inventory.xml
+++ b/indra/newview/skins/default/xui/en/floater_my_inventory.xml
@@ -5,14 +5,14 @@
can_resize="true"
height="570"
help_topic="sidebar_inventory"
- min_width="333"
- min_height="590"
+ min_width="363"
+ min_height="270"
name="floater_my_inventory"
save_rect="true"
save_visibility="true"
reuse_instance="true"
title="INVENTORY"
- width="333" >
+ width="363" >
<panel
class="sidepanel_inventory"
name="main_panel"
diff --git a/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml
new file mode 100644
index 0000000000..5f0eeab71c
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ height="130"
+ width="300"
+ layout="topleft"
+ name="floater_new_feature_notification"
+ title="NEW FEATURE"
+ show_title="false"
+ header_height="0"
+ bg_opaque_image="Window_NoTitle_Foreground"
+ bg_alpha_image="Window_NoTitle_Background"
+ can_resize="false"
+ can_drag_on_left="false"
+ can_minimize="false"
+ can_close="false">
+ <floater.string name="title_txt_inventory">
+New inventory features
+ </floater.string>
+ <floater.string name="description_txt_inventory">
+You can now add preview images to inventory items and view a folder in its own window.
+Learn more in this [https://community.secondlife.com/blogs/entry/13637-new-features-inventory-item-preview-and-single-folder-view/ blogpost]
+ </floater.string>
+ <text
+ type="string"
+ length="1"
+ follows="top|left|right"
+ font="SansSerifLargeBold"
+ text_color="White"
+ layout="topleft"
+ left="10"
+ height="14"
+ top="10"
+ right="-10"
+ name="title_txt">
+New feature
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="top|left|right"
+ text_color="White"
+ layout="topleft"
+ left="10"
+ height="40"
+ top_pad="14"
+ right="-10"
+ word_wrap="true"
+ name="description_txt">
+Feature description
+ </text>
+ <button
+ follows="bottom|left|right"
+ layout="topleft"
+ height="24"
+ label="Got it!"
+ left="104"
+ bottom="-10"
+ name="close_btn"
+ width="90"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_outfit_photo_preview.xml b/indra/newview/skins/default/xui/en/floater_outfit_photo_preview.xml
deleted file mode 100644
index bfc1c39e9d..0000000000
--- a/indra/newview/skins/default/xui/en/floater_outfit_photo_preview.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<floater
- legacy_header_height="18"
- can_resize="false"
- height="325"
- layout="topleft"
- name="outfit_photo_preview"
- help_topic="preview_texture"
- width="410">
- <floater.string
- name="Title">
- Texture: [NAME]
- </floater.string>
- <floater.string
- name="exceed_limits">
- Max outfit photo size is [MAX_WIDTH]*[MAX_HEIGHT]. Please select another texture.
- </floater.string>
- <floater.string
- name="photo_confirmation">
- Set this as Outfit Photo for [OUTFIT]?
- </floater.string>
- <text
- type="string"
- halign="right"
- length="1"
- follows="right|bottom"
- height="16"
- layout="topleft"
- left="110"
- name="dimensions"
- top="255"
- width="200">
- [WIDTH]px x [HEIGHT]px
- </text>
- <text
- type="string"
- follows="left|top"
- height="16"
- layout="topleft"
- name="notification"
- left="25"
- halign="center"
- top_pad="5"
- width="360">
- </text>
- <button
- follows="right|bottom"
- height="22"
- label="OK"
- layout="topleft"
- name="ok_btn"
- top_pad="5"
- right="-115"
- top_delta="0"
- width="90" />
- <button
- follows="right|bottom"
- height="22"
- label="Cancel"
- layout="topleft"
- name="cancel_btn"
- right="-20"
- top_delta="0"
- width="90" />
-</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_simple_outfit_snapshot.xml b/indra/newview/skins/default/xui/en/floater_simple_snapshot.xml
index 5ece7b85d5..484ad159d1 100644
--- a/indra/newview/skins/default/xui/en/floater_simple_outfit_snapshot.xml
+++ b/indra/newview/skins/default/xui/en/floater_simple_snapshot.xml
@@ -2,17 +2,17 @@
<floater
positioning="cascading"
legacy_header_height="18"
- can_minimize="true"
+ can_minimize="false"
can_resize="false"
can_close="true"
height="305"
layout="topleft"
- name="simple_outfit_snapshot"
- single_instance="true"
+ name="simple_snapshot"
+ single_instance="false"
help_topic="snapshot"
save_rect="true"
save_visibility="false"
- title="OUTFIT SNAPSHOT"
+ title="ITEM SNAPSHOT"
width="351">
<ui_ctrl
layout="topleft"
@@ -36,7 +36,7 @@
height="22"
layout="topleft"
left_pad="10"
- label="Save (L$[UPLOAD_COST])"
+ label="Save"
name="save_btn"
width="90" />
<button
diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml
index f441e3cbd7..fcd24d83bb 100644
--- a/indra/newview/skins/default/xui/en/floater_snapshot.xml
+++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml
@@ -440,7 +440,7 @@
length="1"
halign="right"
name="360_label"
- text_color="0.3 0.82 1 1"
+ text_color="HTMLLinkColor"
top_delta="0"
type="string"
width="115">
diff --git a/indra/newview/skins/default/xui/en/floater_task_properties.xml b/indra/newview/skins/default/xui/en/floater_task_properties.xml
new file mode 100644
index 0000000000..56c236eab4
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_task_properties.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ height="590"
+ layout="topleft"
+ name="Task Properties"
+ help_topic="item+properties"
+ title="ITEM PROPERTIES"
+ single_instance="true"
+ width="330">
+ <panel
+ follows="all"
+ layout="topleft"
+ left="0"
+ class="sidepanel_task_info"
+ filename="sidepanel_task_info.xml"
+ name="sidepanel"
+ top="20"
+ label=""
+ height="570"
+ visible="true"
+ width="330">
+ </panel>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
index 3a66911389..8081af6673 100644
--- a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
+++ b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
@@ -83,6 +83,22 @@
top_pad="4">
[DIMENSIONS]
</text>
+ <text
+ type="string"
+ text_color="Yellow"
+ length="1"
+ word_wrap="true"
+ follows="left|top"
+ height="56"
+ width="164"
+ layout="topleft"
+ left="8"
+ name="over_limit_lbl"
+ visible="false"
+ top_delta="0">
+ Selected texture is [TEXDIM]. Inventory image must be square, no less than [MINTEXDIM].
+ </text>
+
<!-- middle: inventory mode -->
<button
diff --git a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
new file mode 100644
index 0000000000..d82c453e5f
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
@@ -0,0 +1,515 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<context_menu
+ layout="topleft"
+ name="Gallery">
+ <menu_item_call
+ label="Share"
+ layout="topleft"
+ name="Share">
+ <menu_item_call.on_click
+ function="Inventory.Share" />
+ </menu_item_call>
+ <menu_item_call
+ label="Empty Trash"
+ layout="topleft"
+ name="Empty Trash">
+ <menu_item_call.on_click
+ function="Inventory.EmptyTrash"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Empty Lost And Found"
+ layout="topleft"
+ name="Empty Lost And Found">
+ <menu_item_call.on_click
+ function="Inventory.EmptyLostAndFound"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Teleport"
+ layout="topleft"
+ name="Landmark Open">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="open" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Folder Wearables Separator" />
+ <menu_item_call
+ label="Replace Current Outfit"
+ layout="topleft"
+ name="Replace Outfit">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="replaceoutfit" />
+ </menu_item_call>
+ <menu_item_call
+ label="Add To Current Outfit"
+ layout="topleft"
+ name="Add To Outfit">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="addtooutfit" />
+ </menu_item_call>
+ <menu_item_call
+ label="Remove From Current Outfit"
+ layout="topleft"
+ name="Remove From Outfit">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="removefromoutfit" />
+ </menu_item_call>
+ <menu_item_call
+ label="Copy outfit list to clipboard"
+ layout="topleft"
+ name="Copy outfit list to clipboard">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="copyoutfittoclipboard" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Outfit Separator" />
+ <menu_item_call
+ label="Find Original"
+ layout="topleft"
+ name="Find Original">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="goto" />
+ </menu_item_call>
+ <menu_item_call
+ label="Purge Item"
+ layout="topleft"
+ name="Purge Item">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="purge"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Restore Item"
+ layout="topleft"
+ name="Restore Item">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="restore" />
+ </menu_item_call>
+ <menu_item_call
+ label="Open"
+ layout="topleft"
+ name="Open">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="open" />
+ </menu_item_call>
+ <menu_item_call
+ label="Open Original"
+ layout="topleft"
+ name="Open Original">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="open_original" />
+ </menu_item_call>
+ <menu_item_call
+ label="Properties"
+ layout="topleft"
+ name="Properties">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="properties" />
+ </menu_item_call>
+ <menu_item_call
+ label="Image..."
+ layout="topleft"
+ name="thumbnail">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="thumbnail" />
+ </menu_item_call>
+ <menu_item_call
+ label="Copy Asset UUID"
+ layout="topleft"
+ name="Copy Asset UUID">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="copy_uuid" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Copy Separator" />
+ <menu_item_call
+ label="Open"
+ layout="topleft"
+ name="open_in_current_window">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="open_selected_folder" />
+ </menu_item_call>
+ <menu_item_call
+ label="Open in new window"
+ layout="topleft"
+ name="open_in_new_window">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="open_in_new_window" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Open Folder Separator" />
+ <menu_item_call
+ label="Rename"
+ layout="topleft"
+ name="Rename">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="rename" />
+ </menu_item_call>
+ <menu_item_call
+ label="Cut"
+ layout="topleft"
+ name="Cut">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="cut" />
+ </menu_item_call>
+ <menu_item_call
+ label="Copy"
+ layout="topleft"
+ name="Copy">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="copy" />
+ </menu_item_call>
+ <menu_item_call
+ label="Paste"
+ layout="topleft"
+ name="Paste">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="paste" />
+ </menu_item_call>
+ <menu_item_call
+ label="Paste As Link"
+ layout="topleft"
+ name="Paste As Link">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="paste_link" />
+ </menu_item_call>
+ <menu_item_call
+ label="Replace Links"
+ layout="topleft"
+ name="Replace Links">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="replace_links" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Paste Separator" />
+ <menu_item_call
+ label="Delete"
+ layout="topleft"
+ name="Delete">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="delete" />
+ </menu_item_call>
+ <menu_item_call
+ label="Delete System Folder"
+ layout="topleft"
+ name="Delete System Folder">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="delete_system_folder" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ label="Play"
+ layout="topleft"
+ name="Sound Play">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="sound_play" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Landmark Separator" />
+ <menu_item_call
+ label="Copy SLurl"
+ layout="topleft"
+ name="url_copy">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="copy_slurl" />
+ </menu_item_call>
+ <menu_item_call
+ label="About Landmark"
+ layout="topleft"
+ name="About Landmark">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="about" />
+ </menu_item_call>
+ <menu_item_call
+ label="Show on Map"
+ layout="topleft"
+ name="show_on_map">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="show_on_map" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Animation Separator" />
+ <menu_item_call
+ label="Play Inworld"
+ layout="topleft"
+ name="Animation Play">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="playworld" />
+ </menu_item_call>
+ <menu_item_call
+ label="Play Locally"
+ layout="topleft"
+ name="Animation Audition">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="playlocal" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Send Instant Message Separator" />
+ <menu_item_call
+ label="Send Instant Message"
+ layout="topleft"
+ name="Send Instant Message">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="begin_im" />
+ </menu_item_call>
+ <menu_item_call
+ label="Offer Teleport..."
+ layout="topleft"
+ name="Offer Teleport...">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="lure" />
+ </menu_item_call>
+ <menu_item_call
+ label="Request Teleport..."
+ layout="topleft"
+ name="Request Teleport...">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="request_lure" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Gesture Separator" />
+ <menu_item_call
+ label="Activate"
+ layout="topleft"
+ name="Activate">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="activate" />
+ </menu_item_call>
+ <menu_item_call
+ label="Deactivate"
+ layout="topleft"
+ name="Deactivate">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="deactivate" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Texture Separator" />
+ <menu_item_call
+ label="Save As"
+ layout="topleft"
+ name="Save As">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="save_as" />
+ </menu_item_call>
+ <menu_item_call
+ label="Save Selected As"
+ layout="topleft"
+ name="Save Selected As">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="save_selected_as" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Wearable And Object Separator"/>
+ <menu_item_call
+ label="Wear"
+ layout="topleft"
+ name="Wearable And Object Wear">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="wear" />
+ </menu_item_call>
+ <menu
+ label="Attach To"
+ layout="topleft"
+ name="Attach To" />
+ <menu
+ label="Attach To HUD"
+ layout="topleft"
+ name="Attach To HUD" />
+ <menu_item_call
+ label="Touch"
+ layout="topleft"
+ name="Attachment Touch">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="touch" />
+ </menu_item_call>
+ <menu_item_call
+ label="Edit"
+ layout="topleft"
+ name="Wearable Edit">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="edit" />
+ </menu_item_call>
+ <menu_item_call
+ label="Add"
+ layout="topleft"
+ name="Wearable Add">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="wear_add" />
+ </menu_item_call>
+ <menu_item_call
+ label="Detach From Yourself"
+ layout="topleft"
+ name="Detach From Yourself">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="detach" />
+ </menu_item_call>
+ <menu_item_call
+ label="Take Off"
+ layout="topleft"
+ name="Take Off">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="take_off" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Settings Separator" />
+ <menu_item_call
+ name="Settings Apply Local"
+ layout="topleft"
+ label="Apply Only To Myself">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="apply_settings_local" />
+ </menu_item_call>
+ <menu_item_call
+ name="Settings Apply Parcel"
+ layout="topleft"
+ label="Apply To Parcel">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="apply_settings_parcel" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Subfolder Separator" />
+ <menu_item_call
+ label="Create folder from selected"
+ layout="topleft"
+ name="New folder from selected">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="new_folder_from_selected" />
+ </menu_item_call>
+ <menu_item_call
+ label="Ungroup folder items"
+ layout="topleft"
+ name="Ungroup folder items">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="ungroup_folder_items" />
+ </menu_item_call>
+ <menu
+ label="Use as default for"
+ layout="topleft"
+ name="upload_def">
+ <menu_item_call
+ label="Image uploads"
+ layout="topleft"
+ name="Image uploads">
+ <menu_item_call.on_click
+ function="Inventory.FileUploadLocation"
+ parameter="texture" />
+ <menu_item_call.on_visible
+ function="Inventory.CanSetUploadLocation" />
+ </menu_item_call>
+ <menu_item_call
+ label="Sound uploads"
+ layout="topleft"
+ name="Sound uploads">
+ <menu_item_call.on_click
+ function="Inventory.FileUploadLocation"
+ parameter="sound" />
+ <menu_item_call.on_visible
+ function="Inventory.CanSetUploadLocation" />
+ </menu_item_call>
+ <menu_item_call
+ label="Animation uploads"
+ layout="topleft"
+ name="Animation uploads">
+ <menu_item_call.on_click
+ function="Inventory.FileUploadLocation"
+ parameter="animation" />
+ <menu_item_call.on_visible
+ function="Inventory.CanSetUploadLocation" />
+ </menu_item_call>
+ <menu_item_call
+ label="Model uploads"
+ layout="topleft"
+ name="Model uploads">
+ <menu_item_call.on_click
+ function="Inventory.FileUploadLocation"
+ parameter="model" />
+ <menu_item_call.on_visible
+ function="Inventory.CanSetUploadLocation" />
+ </menu_item_call>
+ </menu>
+ <menu_item_separator
+ layout="topleft"
+ name="Marketplace Separator" />
+ <menu_item_call
+ label="Copy to Marketplace Listings"
+ layout="topleft"
+ name="Marketplace Copy">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="copy_to_marketplace_listings" />
+ </menu_item_call>
+ <menu_item_call
+ label="Move to Marketplace Listings"
+ layout="topleft"
+ name="Marketplace Move">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="move_to_marketplace_listings" />
+ </menu_item_call>
+ <menu_item_call
+ label="--no options--"
+ layout="topleft"
+ name="--no options--" />
+ <menu_item_separator
+ layout="topleft" />
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml b/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml
index 99ca910062..0ca505dd5d 100755
--- a/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml
+++ b/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml
@@ -42,35 +42,11 @@
parameter="take_off" />
</menu_item_call>
<menu_item_call
- label="Upload Photo (L$[UPLOAD_COST])"
- layout="topleft"
- name="upload_photo">
- <on_click
- function="Outfit.UploadPhoto" />
- </menu_item_call>
- <menu_item_call
- label="Select Photo"
- layout="topleft"
- name="select_photo">
+ label="Image..."
+ layout="topleft"
+ name="thumbnail">
<on_click
- function="Outfit.SelectPhoto" />
- </menu_item_call>
- <menu_item_call
- label="Take a Snapshot"
- layout="topleft"
- name="take_snapshot">
- <on_click
- function="Outfit.TakeSnapshot" />
- </menu_item_call>
- <menu_item_call
- label="Remove Photo"
- layout="topleft"
- name="remove_photo">
- <on_click
- function="Outfit.RemovePhoto" />
- <on_visible
- function="Outfit.OnVisible"
- parameter="remove_photo" />
+ function="Outfit.Thumbnail" />
</menu_item_call>
<menu_item_separator name="sepatator1" />
<menu
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index aa3d0ae071..e650c10603 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -159,248 +159,6 @@
function="Inventory.DoCreate"
parameter="outfit" />
</menu_item_call>
- <menu_item_call
- label="New Script"
- layout="topleft"
- name="New Script">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="lsl" />
- </menu_item_call>
- <menu_item_call
- label="New Notecard"
- layout="topleft"
- name="New Note">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="notecard" />
- </menu_item_call>
- <menu_item_call
- label="New Gesture"
- layout="topleft"
- name="New Gesture">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="gesture" />
- </menu_item_call>
- <menu
- label="New Clothes"
- layout="topleft"
- name="New Clothes">
- <menu_item_call
- label="New Shirt"
- layout="topleft"
- name="New Shirt">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="shirt" />
- </menu_item_call>
- <menu_item_call
- label="New Pants"
- layout="topleft"
- name="New Pants">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="pants" />
- </menu_item_call>
- <menu_item_call
- label="New Shoes"
- layout="topleft"
- name="New Shoes">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="shoes" />
- </menu_item_call>
- <menu_item_call
- label="New Socks"
- layout="topleft"
- name="New Socks">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="socks" />
- </menu_item_call>
- <menu_item_call
- label="New Jacket"
- layout="topleft"
- name="New Jacket">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="jacket" />
- </menu_item_call>
- <menu_item_call
- label="New Skirt"
- layout="topleft"
- name="New Skirt">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="skirt" />
- </menu_item_call>
- <menu_item_call
- label="New Gloves"
- layout="topleft"
- name="New Gloves">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="gloves" />
- </menu_item_call>
- <menu_item_call
- label="New Undershirt"
- layout="topleft"
- name="New Undershirt">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="undershirt" />
- </menu_item_call>
- <menu_item_call
- label="New Underpants"
- layout="topleft"
- name="New Underpants">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="underpants" />
- </menu_item_call>
- <menu_item_call
- label="New Alpha Mask"
- layout="topleft"
- name="New Alpha Mask">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="alpha" />
- </menu_item_call>
- <menu_item_call
- label="New Tattoo"
- layout="topleft"
- name="New Tattoo">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="tattoo" />
- </menu_item_call>
- <menu_item_call
- label="New Universal"
- layout="topleft"
- name="New Universal">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="universal" />
- </menu_item_call>
- <menu_item_call
- label="New Physics"
- layout="topleft"
- name="New Physics">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="physics" />
- </menu_item_call>
- </menu>
- <menu
- label="New Body Parts"
- layout="topleft"
- name="New Body Parts">
- <menu_item_call
- label="New Shape"
- layout="topleft"
- name="New Shape">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="shape" />
- </menu_item_call>
- <menu_item_call
- label="New Skin"
- layout="topleft"
- name="New Skin">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="skin" />
- </menu_item_call>
- <menu_item_call
- label="New Hair"
- layout="topleft"
- name="New Hair">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="hair" />
- </menu_item_call>
- <menu_item_call
- label="New Eyes"
- layout="topleft"
- name="New Eyes">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="eyes" />
- </menu_item_call>
- </menu>
- <menu
- label="New Settings"
- layout="topleft"
- name="New Settings">
- <menu_item_call
- label="New Sky"
- layout="topleft"
- name="New Sky">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="sky"/>
- <menu_item_call.on_enable
- function="Inventory.EnvironmentEnabled" />
- </menu_item_call>
- <menu_item_call
- label="New Water"
- layout="topleft"
- name="New Water">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="water"/>
- <menu_item_call.on_enable
- function="Inventory.EnvironmentEnabled" />
- </menu_item_call>
- <menu_item_call
- label="New Day Cycle"
- layout="topleft"
- name="New Day Cycle">
- <menu_item_call.on_click
- function="Inventory.DoCreate"
- parameter="daycycle"/>
- <menu_item_call.on_enable
- function="Inventory.EnvironmentEnabled" />
- </menu_item_call>
- </menu>
- <menu
- label="Use as default for"
- layout="topleft"
- name="upload_def">
- <menu_item_call
- label="Image uploads"
- layout="topleft"
- name="Image uploads">
- <menu_item_call.on_click
- function="Inventory.FileUploadLocation"
- parameter="texture" />
- </menu_item_call>
- <menu_item_call
- label="Sound uploads"
- layout="topleft"
- name="Sound uploads">
- <menu_item_call.on_click
- function="Inventory.FileUploadLocation"
- parameter="sound" />
- </menu_item_call>
- <menu_item_call
- label="Animation uploads"
- layout="topleft"
- name="Animation uploads">
- <menu_item_call.on_click
- function="Inventory.FileUploadLocation"
- parameter="animation" />
- </menu_item_call>
- <menu_item_call
- label="Model uploads"
- layout="topleft"
- name="Model uploads">
- <menu_item_call.on_click
- function="Inventory.FileUploadLocation"
- parameter="model" />
- </menu_item_call>
- </menu>
<menu
label="Change Type"
layout="topleft"
@@ -597,12 +355,12 @@
parameter="properties" />
</menu_item_call>
<menu_item_call
- label="Rename"
+ label="Image..."
layout="topleft"
- name="Rename">
+ name="thumbnail">
<menu_item_call.on_click
function="Inventory.DoToSelected"
- parameter="rename" />
+ parameter="thumbnail" />
</menu_item_call>
<menu_item_call
label="Copy Asset UUID"
@@ -624,6 +382,32 @@
layout="topleft"
name="Copy Separator" />
<menu_item_call
+ label="Open"
+ layout="topleft"
+ name="open_in_current_window">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="open_in_current_window" />
+ </menu_item_call>
+ <menu_item_call
+ label="Open in new window"
+ layout="topleft"
+ name="open_in_new_window">
+ <menu_item_call.on_click
+ function="Inventory.OpenNewFolderWindow"/>
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="Open Folder Separator" />
+ <menu_item_call
+ label="Rename"
+ layout="topleft"
+ name="Rename">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="rename" />
+ </menu_item_call>
+ <menu_item_call
label="Cut"
layout="topleft"
name="Cut">
@@ -931,6 +715,43 @@
function="Inventory.DoToSelected"
parameter="ungroup_folder_items" />
</menu_item_call>
+ <menu
+ label="Use as default for"
+ layout="topleft"
+ name="upload_def">
+ <menu_item_call
+ label="Image uploads"
+ layout="topleft"
+ name="Image uploads">
+ <menu_item_call.on_click
+ function="Inventory.FileUploadLocation"
+ parameter="texture" />
+ </menu_item_call>
+ <menu_item_call
+ label="Sound uploads"
+ layout="topleft"
+ name="Sound uploads">
+ <menu_item_call.on_click
+ function="Inventory.FileUploadLocation"
+ parameter="sound" />
+ </menu_item_call>
+ <menu_item_call
+ label="Animation uploads"
+ layout="topleft"
+ name="Animation uploads">
+ <menu_item_call.on_click
+ function="Inventory.FileUploadLocation"
+ parameter="animation" />
+ </menu_item_call>
+ <menu_item_call
+ label="Model uploads"
+ layout="topleft"
+ name="Model uploads">
+ <menu_item_call.on_click
+ function="Inventory.FileUploadLocation"
+ parameter="model" />
+ </menu_item_call>
+ </menu>
<menu_item_separator
layout="topleft"
name="Marketplace Separator" />
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_add.xml b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
index 3385a29a6c..12ba121d3c 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory_add.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
@@ -290,4 +290,13 @@
function="Inventory.EnvironmentEnabled" />
</menu_item_call>
</menu>
-</menu> \ No newline at end of file
+ <menu_item_separator/>
+ <menu_item_call
+ label="Shop..."
+ layout="topleft"
+ name="Shop">
+ <menu_item_call.on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="shop" />
+ </menu_item_call>
+</menu>
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml b/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml
index 3eacdbc781..2c630880c2 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml
@@ -15,52 +15,25 @@
function="Inventory.GearDefault.Custom.Action"
parameter="new_window" />
</menu_item_call>
- <menu_item_separator
- layout="topleft" />
- <menu_item_check
- label="Sort by Name"
- layout="topleft"
- name="sort_by_name">
- <on_click
- function="Inventory.GearDefault.Custom.Action"
- parameter="sort_by_name" />
- <on_check
- function="Inventory.GearDefault.Check"
- parameter="sort_by_name" />
- </menu_item_check>
- <menu_item_check
- label="Sort by Most Recent"
- layout="topleft"
- name="sort_by_recent">
- <on_click
- function="Inventory.GearDefault.Custom.Action"
- parameter="sort_by_recent" />
- <on_check
- function="Inventory.GearDefault.Check"
- parameter="sort_by_recent" />
- </menu_item_check>
- <menu_item_check
- label="Sort Folders Always by Name"
+ <menu_item_call
+ label="Collapse All Folders"
layout="topleft"
- name="sort_folders_by_name">
+ name="close_folders">
<on_click
function="Inventory.GearDefault.Custom.Action"
- parameter="sort_folders_by_name" />
- <on_check
- function="Inventory.GearDefault.Check"
- parameter="sort_folders_by_name" />
- </menu_item_check>
- <menu_item_check
- label="Sort System Folders to Top"
+ parameter="close_folders" />
+ <on_visible
+ function="Inventory.GearDefault.Visible"
+ parameter="multi_folder_view" />
+ </menu_item_call>
+ <menu_item_call
+ label="Close All Windows"
layout="topleft"
- name="sort_system_folders_to_top">
+ name="close_windows">
<on_click
function="Inventory.GearDefault.Custom.Action"
- parameter="sort_system_folders_to_top" />
- <on_check
- function="Inventory.GearDefault.Check"
- parameter="sort_system_folders_to_top" />
- </menu_item_check>
+ parameter="close_inv_windows" />
+ </menu_item_call>
<menu_item_separator
layout="topleft" />
<menu_item_call
@@ -79,29 +52,6 @@
function="Inventory.GearDefault.Custom.Action"
parameter="reset_filters" />
</menu_item_call>
- <menu_item_call
- label="Close All Folders"
- layout="topleft"
- name="close_folders">
- <on_click
- function="Inventory.GearDefault.Custom.Action"
- parameter="close_folders" />
- </menu_item_call>
- <menu_item_separator
- layout="topleft" />
- <menu_item_call
- label="Empty Lost and Found"
- layout="topleft"
- name="empty_lostnfound">
- <on_click
- function="Inventory.GearDefault.Custom.Action"
- parameter="empty_lostnfound" />
- <on_enable
- function="Inventory.GearDefault.Enable"
- parameter="empty_lostnfound" />
- </menu_item_call>
- <menu_item_separator
- layout="topleft" />
<menu_item_call
label="Save Texture As"
layout="topleft"
@@ -119,7 +69,8 @@
name="Share"
visible="true">
<on_click
- function="Inventory.Share" />
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="share" />
<on_enable
function="Inventory.GearDefault.Enable"
parameter="share" />
@@ -154,9 +105,25 @@
function="Inventory.GearDefault.Custom.Action"
parameter="replace_links" />
</menu_item_call>
- <menu_item_separator
- layout="topleft" />
-
+ <menu_item_separator>
+ <menu_item_separator.on_visible
+ function="Inventory.GearDefault.Visible"
+ parameter="multi_folder_view" />
+ </menu_item_separator>
+ <menu_item_call
+ label="Empty Lost and Found"
+ layout="topleft"
+ name="empty_lostnfound">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="empty_lostnfound" />
+ <on_enable
+ function="Inventory.GearDefault.Enable"
+ parameter="empty_lostnfound" />
+ <on_visible
+ function="Inventory.GearDefault.Visible"
+ parameter="multi_folder_view" />
+ </menu_item_call>
<menu_item_call
label="Empty Trash"
layout="topleft"
@@ -167,5 +134,8 @@
<on_enable
function="Inventory.GearDefault.Enable"
parameter="empty_trash" />
+ <on_visible
+ function="Inventory.GearDefault.Visible"
+ parameter="multi_folder_view" />
</menu_item_call>
</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_search_visibility.xml b/indra/newview/skins/default/xui/en/menu_inventory_search_visibility.xml
index 46193f4a7a..8e34f52f3a 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory_search_visibility.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory_search_visibility.xml
@@ -7,6 +7,17 @@
name="menu_search_visibility"
visible="false">
<menu_item_check
+ label="Search outfit folders"
+ layout="topleft"
+ name="search_outfits">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="toggle_search_outfits" />
+ <on_check
+ function="Inventory.GearDefault.Check"
+ parameter="toggle_search_outfits" />
+ </menu_item_check>
+ <menu_item_check
label="Search Trash"
layout="topleft"
name="search_trash">
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_view_default.xml b/indra/newview/skins/default/xui/en/menu_inventory_view_default.xml
new file mode 100644
index 0000000000..c7f9822e41
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_inventory_view_default.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ bottom="806"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="menu_view_default"
+ visible="false">
+ <menu_item_check
+ label="Sort by Name"
+ layout="topleft"
+ name="sort_by_name">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="sort_by_name" />
+ <on_check
+ function="Inventory.GearDefault.Check"
+ parameter="sort_by_name" />
+ </menu_item_check>
+ <menu_item_check
+ label="Sort by Most Recent"
+ layout="topleft"
+ name="sort_by_recent">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="sort_by_recent" />
+ <on_check
+ function="Inventory.GearDefault.Check"
+ parameter="sort_by_recent" />
+ </menu_item_check>
+ <menu_item_check
+ label="Sort Folders Always by Name"
+ layout="topleft"
+ name="sort_folders_by_name">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="sort_folders_by_name" />
+ <on_check
+ function="Inventory.GearDefault.Check"
+ parameter="sort_folders_by_name" />
+ </menu_item_check>
+ <menu_item_check
+ label="Sort System Folders to Top"
+ layout="topleft"
+ name="sort_system_folders_to_top">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="sort_system_folders_to_top" />
+ <on_check
+ function="Inventory.GearDefault.Check"
+ parameter="sort_system_folders_to_top" />
+ <on_visible
+ function="Inventory.GearDefault.Visible"
+ parameter="multi_folder_view" />
+ </menu_item_check>
+ <menu_item_separator>
+ <menu_item_separator.on_visible
+ function="Inventory.GearDefault.Visible"
+ parameter="single_folder_view" />
+ </menu_item_separator>
+ <menu_item_check
+ label="List view"
+ layout="topleft"
+ name="list_view">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="list_view" />
+ <on_check
+ function="Inventory.GearDefault.Check"
+ parameter="list_view" />
+ <on_visible
+ function="Inventory.GearDefault.Visible"
+ parameter="single_folder_view" />
+ </menu_item_check>
+ <menu_item_check
+ label="Gallery view"
+ layout="topleft"
+ name="gallery_view">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="gallery_view" />
+ <on_check
+ function="Inventory.GearDefault.Check"
+ parameter="gallery_view" />
+ <on_visible
+ function="Inventory.GearDefault.Visible"
+ parameter="single_folder_view" />
+ </menu_item_check>
+ <menu_item_check
+ label="Combination view"
+ layout="topleft"
+ name="combination_view">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="combination_view" />
+ <on_check
+ function="Inventory.GearDefault.Check"
+ parameter="combination_view" />
+ <on_visible
+ function="Inventory.GearDefault.Visible"
+ parameter="single_folder_view" />
+ </menu_item_check>
+ <menu_item_separator/>
+ <menu_item_check
+ label="Inventory settings..."
+ name="inv_settings">
+ <menu_item_check.on_check
+ function="Floater.Visible"
+ parameter="inventory_settings" />
+ <menu_item_check.on_click
+ function="Floater.Toggle"
+ parameter="inventory_settings" />
+ </menu_item_check>
+</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
index 32d9d28434..e216962d12 100644
--- a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
@@ -40,32 +40,11 @@
parameter="take_off" />
</menu_item_call>
<menu_item_call
- label="Upload Photo (L$[UPLOAD_COST])"
+ label="Image..."
layout="topleft"
- name="upload_photo">
+ name="thumbnail">
<on_click
- function="Gear.UploadPhoto" />
- </menu_item_call>
- <menu_item_call
- label="Select Photo"
- layout="topleft"
- name="select_photo">
- <on_click
- function="Gear.SelectPhoto" />
- </menu_item_call>
- <menu_item_call
- label="Take a Snapshot"
- layout="topleft"
- name="take_snapshot">
- <on_click
- function="Gear.TakeSnapshot" />
- </menu_item_call>
- <menu_item_call
- label="Remove Photo"
- layout="topleft"
- name="remove_photo">
- <on_click
- function="Gear.RemovePhoto" />
+ function="Gear.Thumbnail" />
</menu_item_call>
<menu_item_separator name="sepatator1" />
<!-- copied (with minor modifications) from menu_inventory_add.xml -->
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index a216ef7c0d..204fead7e0 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -3284,6 +3284,30 @@ See https://wiki.secondlife.com/wiki/Adding_Spelling_Dictionaries
<notification
icon="alertmodal.tga"
+ label="Rename Selected Item"
+ name="RenameItem"
+ type="alertmodal">
+ Choose a new name for:
+[NAME]
+ <tag>confirm</tag>
+ <form name="form">
+ <input name="new_name" type="text" width="300">
+ [NAME]
+ </input>
+ <button
+ default="true"
+ index="0"
+ name="OK"
+ text="OK"/>
+ <button
+ index="1"
+ name="Cancel"
+ text="Cancel"/>
+ </form>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
name="RemoveFromFriends"
type="alertmodal">
<tag>friendship</tag>
@@ -6129,7 +6153,54 @@ Are you sure you want to delete them?
notext="Cancel"
yestext="OK"/>
</notification>
-
+
+ <notification
+ icon="alertmodal.tga"
+ name="DeleteThumbnail"
+ type="alertmodal">
+ <unique/>
+ Delete the image for this item? There is no undo.
+ <tag>confirm</tag>
+ <usetemplate
+ ignoretext="Don't show me this again"
+ name="okcancelignore"
+ notext="Cancel"
+ yestext="Delete"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="ThumbnailDimentionsLimit"
+ type="alertmodal">
+ <unique/>
+ Only square images from 64 to 256 pixels per side are allowed.
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="ThumbnailInsufficientPermissions"
+ type="alertmodal">
+ <unique/>
+ Only copy and transfer free images can be assigned as thumbnails.
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="ThumbnailOutfitPhoto"
+ type="alertmodal">
+ <unique/>
+ To add an image to an outfit, use the Outfit Gallery window, or right-click on the outfit folder and select "Image..."
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
<notification
icon="alertmodal.tga"
name="ConfirmUnlink"
@@ -6379,6 +6450,22 @@ Your inventory is experiencing issues. Please, contact support.
<notification
icon="alertmodal.tga"
+ name="InventoryLimitReachedAISAlert"
+ type="alertmodal">
+Your inventory is experiencing issues. Please, contact support.
+ <tag>fail</tag>
+ </notification>
+
+ <notification
+ icon="notifytip.tga"
+ name="InventoryLimitReachedAIS"
+ type="notifytip">
+Your inventory is experiencing issues. Please, contact support.
+ <tag>fail</tag>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
name="ConfirmClearBrowserCache"
type="alertmodal">
Are you sure you want to delete your travel, web, and search history?
@@ -11901,16 +11988,41 @@ Packing: [PACK_TIME]s [PSIZE]KB
Unpacking: [UNPACK_TIME]s [USIZE]KB
<tag>fail</tag>
</notification>
-
+
+ <notification
+ icon="alertmodal.tga"
+ label="Prompt for MFA Token"
+ name="PromptMFAToken"
+ type="alertmodal">
+ [MESSAGE]
+ <tag>confirm</tag>
+ <form name="form">
+ <input name="token" type="text" width="400" />
+ <button
+ default="true"
+ index="0"
+ name="continue"
+ text="Continue"/>
+ <button
+ index="1"
+ name="cancel"
+ text="Cancel"/>
+ </form>
+ </notification>
+
<notification
icon="alertmodal.tga"
label="Prompt for MFA Token"
- name="PromptMFAToken"
+ name="PromptMFATokenWithSave"
type="alertmodal">
[MESSAGE]
<tag>confirm</tag>
<form name="form">
<input name="token" type="text" width="400" />
+ <ignore
+ name="ignore"
+ checkbox_only="true"
+ text="Remember this computer for 30 days."/>
<button
default="true"
index="0"
diff --git a/indra/newview/skins/default/xui/en/panel_inventory_gallery.xml b/indra/newview/skins/default/xui/en/panel_inventory_gallery.xml
new file mode 100644
index 0000000000..ed04e12193
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_inventory_gallery.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ background_visible="true"
+ bg_alpha_color="InventoryBackgroundColor"
+ border="false"
+ follows="all"
+ height="390"
+ name="Inventory Gallery"
+ layout="topleft">
+ <text
+ type="string"
+ clip_partial="false"
+ follows="all"
+ layout="topleft"
+ name="empty_txt"
+ height="390"
+ halign="center"
+ valign="center"
+ parse_urls="true"
+ wrap="true">
+ Folder is empty.
+ </text>
+ <scroll_container
+ follows="all"
+ height="390"
+ layout="topleft"
+ left="0"
+ top="0"
+ tab_stop="true"
+ name="gallery_scroll_panel"
+ opaque="false">
+ </scroll_container>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_inventory_gallery_item.xml b/indra/newview/skins/default/xui/en/panel_inventory_gallery_item.xml
new file mode 100644
index 0000000000..574872a870
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_inventory_gallery_item.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ background_visible="false"
+ background_opaque="false"
+ bg_alpha_color="FrogGreen"
+ bg_opaque_color="FrogGreen"
+ border="false"
+ bevel_style="none"
+ follows="left|top"
+ height="149"
+ width="130"
+ name="gallery_item_panel"
+ layout="topleft"
+ left="0"
+ top="0">
+ <thumbnail
+ name="preview_thumbnail"
+ fallback_image="Thumbnail_Fallback"
+ layout="topleft"
+ follows="left|top"
+ interactable="false"
+ height="128"
+ width="128"
+ top="0"
+ left="1"/>
+ <icon
+ left="5"
+ top_pad="-21"
+ layout="topleft"
+ name="item_type"
+ height="16"
+ width="16"
+ follows="left|top"
+ visible="true"
+ image_name="Inv_Eye"/>
+ <icon
+ left="5"
+ top_pad="-8"
+ layout="topleft"
+ name="link_overlay"
+ height="8"
+ width="6"
+ follows="left|top"
+ visible="false"
+ image_name="Inv_Link"/>
+ <panel
+ background_visible="false"
+ background_opaque="true"
+ bg_opaque_color="MenuItemHighlightBgColor"
+ border="false"
+ bevel_style="none"
+ follows="left|top"
+ left="0"
+ top="129"
+ height="25"
+ width="130"
+ name="text_bg_panel">
+ <text
+ read_only="true"
+ length="1"
+ follows="left|top"
+ left="1"
+ height="23"
+ layout="topleft"
+ name="item_name"
+ parse_urls="false"
+ text_readonly_color="White"
+ word_wrap="true"
+ top="2"
+ width="127"
+ use_ellipses="true">
+ Item name, folder name.
+ </text>
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_main_inventory.xml b/indra/newview/skins/default/xui/en/panel_main_inventory.xml
index 2ff58035ed..f7a9c552cc 100644
--- a/indra/newview/skins/default/xui/en/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/en/panel_main_inventory.xml
@@ -6,8 +6,7 @@
layout="topleft"
min_height="300"
min_width="240"
- name="main inventory panel"
- width="330">
+ name="main inventory panel">
<panel.string
name="Itemcount">
</panel.string>
@@ -23,211 +22,325 @@
name="ItemcountUnknown">
Fetched [ITEM_COUNT] Items and [CATEGORY_COUNT] Folders [FILTER]
</panel.string>
+ <panel.string name="inventory_title">INVENTORY</panel.string>
+ <panel.string name="default_mode_btn">Multi_Folder_Mode</panel.string>
+ <panel.string name="single_folder_mode_btn">Single_Folder_Mode</panel.string>
<text
- type="string"
- length="1"
- follows="left|top|right"
- height="13"
- layout="topleft"
- left="12"
- name="ItemcountText"
- font="SansSerifMedium"
- text_color="InventoryItemLinkColor"
- use_ellipses="true"
- top_pad="0"
- width="300">
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="13"
+ layout="topleft"
+ left="12"
+ right="-12"
+ name="ItemcountText"
+ font="SansSerifMedium"
+ text_color="InventoryItemLinkColor"
+ use_ellipses="true"
+ top_pad="0">
Items:
</text>
- <combo_box
- height="23"
- layout="topleft"
- left="10"
- top="18"
- name="search_type"
- follows="top|left"
- width="88">
- <item
- label="Name"
- name="Name"
- value="search_by_name"/>
- <item
- label="Creator"
- name="Creator"
- value="search_by_creator"/>
- <item
- label="Description"
- name="Description"
- value="search_by_description"/>
- <item
- label="UUID"
- name="UUID"
- value="search_by_UUID"/>
- </combo_box>
- <menu_button
- follows="top|left"
- tool_tip="Show search visibility options"
- height="23"
- image_overlay="Inv_Toolbar_SearchVisibility"
- layout="topleft"
- left_pad="3"
- name="options_visibility_btn"
- width="31" />
- <filter_editor
- text_pad_left="10"
+ <layout_stack
follows="left|top|right"
- height="23"
- label="Enter search text"
- layout="topleft"
- left_pad="3"
- max_length_chars="300"
- highlight_text_field="true"
- name="inventory search editor"
- width="177" />
- <tab_container
- follows="all"
- halign="center"
- height="339"
- layout="topleft"
- left="7"
- name="inventory filter tabs"
- tab_height="30"
- tab_position="top"
- tab_min_width="100"
- top_pad="10"
- width="312">
- <inventory_panel
- bg_opaque_color="DkGray2"
- bg_alpha_color="DkGray2"
- background_visible="true"
- border="false"
- bevel_style="none"
- follows="all"
- height="338"
- label="MY INVENTORY"
- help_topic="my_inventory_tab"
- layout="topleft"
- left="0"
- name="All Items"
- sort_order_setting="InventorySortOrder"
- show_item_link_overlays="true"
- top="16"
- width="288" />
- <recent_inventory_panel
- bg_opaque_color="DkGray2"
- bg_alpha_color="DkGray2"
- background_visible="true"
+ height="25"
+ animate="false"
+ top_pad="10"
+ left="2"
+ right="-4"
+ orientation="horizontal">
+ <layout_panel
border="false"
- bevel_style="none"
- follows="all"
- height="338"
- label="RECENT"
- help_topic="recent_inventory_tab"
- layout="topleft"
- left_delta="0"
- name="Recent Items"
- show_item_link_overlays="true"
- width="290" />
- <inventory_panel
- name="Worn Items"
- label="WORN"
- show_empty_message="false"
- follows="all"
+ bevel_style="in"
+ user_resize="false"
+ auto_resize="false"
+ height="25"
+ width="65"
+ name="nav_buttons"
+ visible="false">
+ <button
+ follows="top|left"
+ height="23"
+ image_selected="Single_Folder_Back"
+ image_pressed="Single_Folder_Back"
+ image_unselected="Single_Folder_Back"
+ scale_image="false"
+ layout="topleft"
+ left="3"
+ top="2"
+ name="back_btn"
+ tool_tip="Back"
+ width="20" />
+ <button
+ follows="top|left"
+ height="23"
+ image_selected="Single_Folder_Forward"
+ image_pressed="Single_Folder_Forward"
+ image_unselected="Single_Folder_Forward"
+ scale_image="false"
+ layout="topleft"
+ left_pad="1"
+ name="forward_btn"
+ tool_tip="Forward"
+ width="20" />
+ <button
+ follows="top|left"
+ height="23"
+ image_selected="Single_Folder_Up"
+ image_pressed="Single_Folder_Up"
+ image_unselected="Single_Folder_Up"
+ scale_image="false"
layout="topleft"
- width="290"
- bg_opaque_color="DkGray2"
- bg_alpha_color="DkGray2"
- background_visible="true"
+ left_pad="1"
+ name="up_btn"
+ tool_tip="Go up one level"
+ width="20" />
+ </layout_panel>
+ <layout_panel
border="false"
- bevel_style="none"
- scroll.reserve_scroll_corner="false">
- </inventory_panel>
- </tab_container>
- <layout_stack
- animate="false"
- border_size="0"
- follows="left|right|bottom"
- height="25"
- layout="topleft"
- orientation="horizontal"
- top_pad="0"
- left="10"
- name="bottom_panel"
- width="307">
- <layout_panel
- auto_resize="false"
- height="25"
+ bevel_style="in"
+ user_resize="false"
+ height="25"
+ width="381"
+ visible="true">
+ <combo_box
+ height="23"
+ layout="topleft"
+ left="2"
+ top="0"
+ name="search_type"
+ tool_tip="Search by"
+ follows="top|left"
+ width="67">
+ <item
+ label="Name"
+ name="Name"
+ value="search_by_name"/>
+ <item
+ label="Creator"
+ name="Creator"
+ value="search_by_creator"/>
+ <item
+ label="Description"
+ name="Description"
+ value="search_by_description"/>
+ <item
+ label="UUID"
+ name="UUID"
+ value="search_by_UUID"/>
+ </combo_box>
+ <menu_button
+ follows="top|left"
+ tool_tip="Search visibility options"
+ height="23"
+ image_overlay="Inv_Toolbar_SearchVisibility"
layout="topleft"
- name="options_gear_btn_panel"
- width="32">
- <menu_button
- follows="bottom|left"
- tool_tip="Show additional options"
- height="25"
- image_hover_unselected="Toolbar_Left_Over"
- image_overlay="OptionsMenu_Off"
- image_selected="Toolbar_Left_Selected"
- image_unselected="Toolbar_Left_Off"
- layout="topleft"
- left="0"
- name="options_gear_btn"
- top="0"
- width="31" />
- </layout_panel>
- <layout_panel
- auto_resize="false"
- height="25"
+ left_pad="1"
+ name="options_visibility_btn"
+ width="31" />
+ <filter_editor
+ text_pad_left="10"
+ follows="left|top|right"
+ height="23"
+ label="Enter search text"
layout="topleft"
- name="add_btn_panel"
- width="32">
- <button
- follows="bottom|left"
- height="25"
- image_hover_unselected="Toolbar_Middle_Over"
- image_overlay="AddItem_Off"
- image_selected="Toolbar_Middle_Selected"
- image_unselected="Toolbar_Middle_Off"
- layout="topleft"
- left="0"
- name="add_btn"
- tool_tip="Add new item"
- top="0"
- width="31" />
- </layout_panel>
- <layout_panel
- auto_resize="true"
- height="25"
+ left_pad="1"
+ max_length_chars="300"
+ highlight_text_field="true"
+ name="inventory search editor"
+ width="150" />
+ <menu_button
+ follows="top|right"
+ tool_tip="Actions"
+ height="23"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="OptionsMenu_Off"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
layout="topleft"
- name="dummy_panel"
- width="212">
- <icon
- follows="bottom|left|right"
- height="25"
- image_name="Toolbar_Middle_Off"
- layout="topleft"
- left="0"
- top="0"
- name="dummy_icon"
- width="211" />
- </layout_panel>
- <layout_panel
- auto_resize="false"
- height="25"
+ left_pad="1"
+ name="options_gear_btn"
+ width="31" />
+ <menu_button
+ follows="top|right"
+ tool_tip="View &amp; sort options"
+ height="23"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Conv_toolbar_sort"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ layout="topleft"
+ left_pad="1"
+ name="view_btn"
+ width="31" />
+ <button
+ follows="top|right"
+ height="23"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="AddItem_Off"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ layout="topleft"
+ left_pad="1"
+ name="add_btn"
+ tool_tip="Create new item"
+ width="31" />
+ <button
+ follows="top|right"
+ tool_tip="Switch between views"
+ height="23"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Single_Folder_Mode"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ layout="topleft"
+ left_pad="1"
+ name="view_mode_btn"
+ width="31" />
+ </layout_panel>
+ </layout_stack>
+ <panel
+ follows="all"
+ halign="center"
+ height="372"
+ layout="topleft"
+ left="3"
+ right="-3"
+ name="default_inventory_panel"
+ top_pad="5">
+ <tab_container
+ follows="all"
+ halign="center"
+ height="372"
layout="topleft"
- name="trash_btn_panel"
- width="31">
- <dnd_button
- follows="bottom|left"
- height="25"
- image_hover_unselected="Toolbar_Right_Over"
- image_overlay="TrashItem_Off"
- image_selected="Toolbar_Right_Selected"
- image_unselected="Toolbar_Right_Off"
- left="0"
- layout="topleft"
- name="trash_btn"
- tool_tip="Remove selected item"
- top="0"
- width="31"/>
- </layout_panel>
- </layout_stack>
+ left="0"
+ name="inventory filter tabs"
+ tab_height="30"
+ tab_position="top"
+ tab_min_width="100"
+ top="0">
+ <inventory_panel
+ bg_opaque_color="DkGray2"
+ bg_alpha_color="DkGray2"
+ background_visible="true"
+ border="false"
+ bevel_style="none"
+ follows="all"
+ label="MY INVENTORY"
+ help_topic="my_inventory_tab"
+ layout="topleft"
+ name="All Items"
+ sort_order_setting="InventorySortOrder"
+ show_item_link_overlays="true"
+ preinitialize_views="false"
+ scroll.reserve_scroll_corner="false">
+ <folder double_click_override="true"/>
+ </inventory_panel>
+ <recent_inventory_panel
+ bg_opaque_color="DkGray2"
+ bg_alpha_color="DkGray2"
+ background_visible="true"
+ border="false"
+ bevel_style="none"
+ follows="all"
+ label="RECENT"
+ help_topic="recent_inventory_tab"
+ layout="topleft"
+ name="Recent Items"
+ show_item_link_overlays="true"
+ preinitialize_views="false"
+ scroll.reserve_scroll_corner="false">
+ <folder double_click_override="true"/>
+ </recent_inventory_panel>
+ <inventory_panel
+ name="Worn Items"
+ label="WORN"
+ show_empty_message="false"
+ follows="all"
+ layout="topleft"
+ bg_opaque_color="DkGray2"
+ bg_alpha_color="DkGray2"
+ background_visible="true"
+ preinitialize_views="false"
+ border="false"
+ bevel_style="none"
+ scroll.reserve_scroll_corner="false">
+ <folder double_click_override="true"/>
+ </inventory_panel>
+ </tab_container>
+ </panel>
+ <panel
+ follows="all"
+ halign="center"
+ height="375"
+ layout="topleft"
+ left="7"
+ name="combination_view_inventory"
+ top_delta="0"
+ visible="false">
+ <layout_stack
+ follows="all"
+ layout="topleft"
+ height="375"
+ name="combination_view_stack"
+ animate="false"
+ drag_handle_thickness="6"
+ drag_handle_first_indent="18"
+ drag_handle_second_indent="18"
+ drag_handle_shift="5"
+ show_drag_handle="true"
+ top="0"
+ left="0"
+ orientation="vertical">
+ <layout_panel
+ border="false"
+ bevel_style="in"
+ user_resize="true"
+ auto_resize="true"
+ height="248"
+ min_width="150"
+ name="comb_gallery_layout">
+ <panel
+ class="inventory_gallery"
+ filename="panel_inventory_gallery.xml"
+ left="0"
+ top="1"
+ height="248"
+ name="comb_gallery_view_inv"
+ background_visible="true"
+ follows="all"
+ layout="topleft">
+ </panel>
+ </layout_panel>
+ <layout_panel
+ border="false"
+ bevel_style="in"
+ user_resize="true"
+ auto_resize="true"
+ height="127"
+ min_height="100"
+ name="comb_inventory_layout">
+ <single_folder_inventory_panel
+ name="comb_single_folder_inv"
+ follows="all"
+ left="0"
+ top="1"
+ height="127"
+ layout="topleft"
+ show_item_link_overlays="true"
+ bg_opaque_color="DkGray2"
+ bg_alpha_color="DkGray2"
+ background_visible="true"
+ border="false"
+ bevel_style="none"
+ scroll.reserve_scroll_corner="false">
+ <item
+ single_folder_mode="true"
+ folder_indentation="-8"/>
+ <folder
+ single_folder_mode="true"
+ folder_indentation="-8"/>
+ </single_folder_inventory_panel>
+ </layout_panel>
+ </layout_stack>
+ </panel>
</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml
index e3790ae09b..e951d25391 100644
--- a/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml
@@ -40,50 +40,10 @@
layout="topleft"
left="4"
top="0"
+ tab_stop="true"
name="gallery_scroll_panel"
opaque="false"
top_pad="0">
- <!--outfit_gallery_item
- layout="topleft"
- left="10"
- name="preview_outfit1"
- height="175"
- width="150"
- follows="left|top"/-->
- <!--layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="0" name="top_gallery_stack" orientation="horizontal">
- <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel">
- <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/>
- </layout_panel>
- <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel">
- <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/>
- </layout_panel>
- <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel">
- <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/>
- </layout_panel>
- </layout_stack>
- <layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="190" name="top_gallery_stack" orientation="horizontal">
- <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel">
- <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/>
- </layout_panel>
- <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel">
- <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/>
- </layout_panel>
- <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel">
- <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/>
- </layout_panel>
- </layout_stack>
- <layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="380" name="top_gallery_stack" orientation="horizontal">
- <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel">
- <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/>
- </layout_panel>
- <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel">
- <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/>
- </layout_panel>
- <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel">
- <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/>
- </layout_panel>
- </layout_stack-->
- <!--</panel>-->
</scroll_container>
<panel
background_visible="true"
diff --git a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml
index b2dc975c6e..ceaff0ea69 100644
--- a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml
@@ -18,7 +18,7 @@
follows="all"
height="400"
layout="topleft"
- left="0"
+ left="3"
single_expansion="true"
top="0"
name="wearables_accordion"
diff --git a/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml b/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml
index ca1e405a62..f899f83ad4 100644
--- a/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml
@@ -19,7 +19,7 @@
layout="topleft"
visible="false"
/>
- <icon
+ <thumbnail
name="real_world_pic"
image_name="Generic_Person_Large"
follows="top|left"
diff --git a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
index 07cdd6d71e..fea7d1bcb8 100644
--- a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
@@ -68,7 +68,7 @@ Account: [ACCTTYPE]
auto_resize="false"
user_resize="false">
- <icon
+ <thumbnail
name="2nd_life_pic"
image_name="Generic_Person_Large"
layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml
index 9995523e61..76d0ffcb8e 100644
--- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml
@@ -6,8 +6,7 @@
layout="topleft"
min_height="350"
min_width="240"
- name="objects panel"
- width="333">
+ name="objects panel">
<panel
follows="all"
layout="topleft"
@@ -17,25 +16,22 @@
label=""
height="570"
visible="true"
- default_tab_group="1"
- width="330">
+ default_tab_group="1">
<layout_stack
follows="left|right|top|bottom"
layout="topleft"
left="0"
top="0"
- tab_group="1"
+ tab_group="1"
orientation="vertical"
name="inventory_layout_stack"
- height="535"
- width="330">
+ height="560">
<layout_panel
name="main_inventory_layout_panel"
layout="topleft"
auto_resize="true"
user_resize="true"
min_dim="150"
- width="330"
follows="bottom|left|right"
height="300">
<panel
@@ -47,17 +43,15 @@
name="panel_main_inventory"
top="0"
label=""
- height="300"
- width="330" />
+ height="300" />
</layout_panel>
<layout_panel
- width="330"
layout="topleft"
auto_resize="false"
user_resize="true"
follows="left|right|top"
name="inbox_layout_panel"
- visible="false"
+ visible="true"
min_dim="35"
expanded_min_dim="90"
height="235">
@@ -69,17 +63,15 @@
class="panel_marketplace_inbox"
top="0"
label=""
- height="235"
- width="330">
+ height="235">
<string name="InboxLabelWithArg">Received items ([NUM])</string>
<string name="InboxLabelNoArg">Received items</string>
<button
control_name="InventoryInboxToggleState"
label="Received items"
- font="SansSerifMedium"
+ font="SansSerifMedium"
name="inbox_btn"
height="35"
- width="308"
image_unselected="MarketplaceBtn_Off"
image_selected="MarketplaceBtn_Selected"
halign="left"
@@ -89,7 +81,8 @@
tab_stop="false"
pad_left="35"
top="0"
- left="10" />
+ left="5"
+ right="-5" />
<text
type="string"
length="1"
@@ -101,174 +94,35 @@
name="inbox_fresh_new_count"
font="SansSerifMedium"
halign="right"
- top_pad="0"
- width="300">
+ top_pad="0">
[NUM] new
</text>
<panel
name="inbox_inventory_placeholder_panel"
follows="all"
- left="10"
- bottom="235"
- width="308"
+ left="5"
+ right="-5"
top="35"
+ height="200"
bg_opaque_color="InventoryBackgroundColor"
background_visible="true"
background_opaque="true"
tool_tip="Drag and drop items to your inventory to use them"
>
<text name="inbox_inventory_placeholder"
- type="string"
- follows="all"
- layout="topleft"
- top="0"
- left="0"
- width="308"
- height="200"
- wrap="true"
- halign="center">
-Purchases from the marketplace will be delivered here.
- </text>
+ type="string"
+ follows="all"
+ layout="topleft"
+ top="0"
+ height="200"
+ wrap="true"
+ halign="center"
+ valign="center">
+ Purchases from the marketplace will be delivered here.
+ </text>
</panel>
</panel>
</layout_panel>
</layout_stack>
- <panel follows="bottom|left|right"
- height="30"
- layout="topleft"
- name="button_panel"
- left="9"
- top_pad="7"
- width="308">
- <layout_stack follows="bottom|left|right"
- height="23"
- layout="topleft"
- mouse_opaque="false"
- name="button_panel_ls"
- left="0"
- orientation="horizontal"
- top="0"
- width="308">
- <layout_panel follows="bottom|left|right"
- height="23"
- layout="bottomleft"
- left="0"
- mouse_opaque="false"
- name="info_btn_lp"
- auto_resize="true"
- width="101">
- <button enabled="true"
- follows="bottom|left|right"
- height="23"
- label="Profile"
- layout="topleft"
- left="1"
- name="info_btn"
- tool_tip="Show object profile"
- top="0"
- width="100" />
- </layout_panel>
- <layout_panel
- follows="bottom|left|right"
- height="23"
- layout="bottomleft"
- left_pad="1"
- mouse_opaque="false"
- name="share_btn_lp"
- auto_resize="true"
- width="100">
- <button
- enabled="true"
- follows="bottom|left|right"
- height="23"
- label="Share"
- layout="topleft"
- left="1"
- name="share_btn"
- tool_tip="Share an inventory item"
- top="0"
- width="99" />
- </layout_panel>
- <layout_panel
- follows="bottom|left|right"
- height="23"
- layout="bottomleft"
- left_pad="1"
- mouse_opaque="false"
- name="shop_btn_lp"
- auto_resize="true"
- width="100">
- <button
- enabled="true"
- follows="bottom|left|right"
- height="23"
- label="Shop"
- layout="topleft"
- left="1"
- name="shop_btn"
- tool_tip="Open Marketplace webpage"
- top="0"
- width="99" />
- <button
- enabled="false"
- follows="bottom|left|right"
- height="23"
- label="Wear"
- layout="topleft"
- left="1"
- name="wear_btn"
- tool_tip="Wear seleceted outfit"
- top="0"
- width="99" />
- <button
- enabled="false"
- follows="bottom|left|right"
- height="23"
- label="Play"
- layout="topleft"
- name="play_btn"
- left="1"
- top="0"
- width="99" />
- <button
- enabled="false"
- follows="bottom|left|right"
- height="23"
- label="Teleport"
- layout="topleft"
- left="1"
- name="teleport_btn"
- tool_tip="Teleport to the selected area"
- top="0"
- width="99" />
- </layout_panel>
- </layout_stack>
- </panel>
- </panel>
- <panel
- follows="all"
- layout="topleft"
- left="0"
- class="sidepanel_item_info"
- filename="sidepanel_item_info.xml"
- name="sidepanel__item_panel"
- top="0"
- label=""
- height="570"
- visible="false"
- width="330">
- </panel>
- <panel
- follows="all"
- layout="topleft"
- left="0"
- class="sidepanel_task_info"
- filename="sidepanel_task_info.xml"
- name="sidepanel__task_panel"
- top="0"
- label=""
- height="570"
- visible="false"
- width="330">
</panel>
</panel>
diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
index 35d14251c7..ad521cb1af 100644
--- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
+++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
@@ -43,214 +43,126 @@
name="origin_inworld">
(Inworld)
</panel.string>
- <icon
- follows="top|right"
- height="18"
- image_name="Lock"
- layout="topleft"
- right="-15"
- mouse_opaque="true"
- name="IconLocked"
- top="8"
- width="18" />
- <button
- follows="top|left"
- height="24"
- image_hover_unselected="BackButton_Over"
- image_pressed="BackButton_Press"
- image_unselected="BackButton_Off"
- layout="topleft"
- left="12"
- name="back_btn"
- tab_stop="false"
- top="2"
- width="30"
- use_draw_context_alpha="false" />
- <text
- follows="top|left|right"
- font="SansSerifHugeBold"
- height="26"
- layout="topleft"
- left_pad="3"
- name="title"
- text_color="LtGray"
- top="2"
- use_ellipses="true"
- value="Item Profile"
- width="275" />
- <text
- follows="top|left|right"
- height="13"
- layout="topleft"
- left="45"
- name="origin"
- text_color="LtGray_50"
- use_ellipses="true"
- value="(Inventory)"
- width="275" />
- <scroll_container
- color="DkGray2"
- follows="all"
- layout="topleft"
- left="9"
- name="item_profile_scroll"
- opaque="true"
- height="493"
- width="313"
- top="45">
- <panel
- follows="left|top|right"
- height="390"
- help_topic=""
- label=""
+
+<layout_stack
+ animate="false"
+ name="main_stack"
+ layout="topleft"
+ follows="all"
+ orientation="vertical"
+ left="0"
+ top="0"
+ right="-1"
+ bottom="-1">
+ <layout_panel
+ auto_resize="false"
+ name="layout_item_name"
layout="topleft"
- left="0"
- name="item_profile"
- top="0"
- width="295">
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
+ follows="all"
+ height="25">
+ <icon
+ follows="top|left"
+ height="16"
+ image_name="Inv_Object"
layout="topleft"
left="5"
- name="LabelItemNameTitle"
- top="10"
- width="78">
- Name:
- </text>
+ mouse_opaque="true"
+ name="item_type_icon"
+ top="3"
+ width="16" />
<line_editor
border_style="line"
border_thickness="1"
follows="left|top|right"
- height="20"
layout="topleft"
- left_delta="78"
+ left_pad="5"
+ top="1"
+ right="-5"
+ height="20"
max_length_bytes="63"
name="LabelItemName"
- top_delta="0"
- width="210"
tool_tip="The name is limited to 63 characters. Longer prim names are cut short. Names can only consist of printable characters found in the ASCII-7 (non-extended) character set, with the exception of the vertical bar/pipe &apos;|&apos;." />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
+ </layout_panel>
+
+ <layout_panel
+ auto_resize="false"
+ name="layout_item_details"
+ layout="topleft"
+ follows="all"
+ height="133">
+
+ <thumbnail
+ name="item_thumbnail"
+ fallback_image="Thumbnail_Fallback"
+ follows="top|left"
layout="topleft"
left="5"
- name="LabelItemDescTitle"
- top_pad="10"
- width="78">
- Description:
- </text>
- <line_editor
- border_style="line"
- border_thickness="1"
- follows="left|top|right"
- height="23"
- layout="topleft"
- left_delta="78"
- max_length_bytes="127"
- name="LabelItemDesc"
- top_delta="-5"
- width="210"
- tool_tip="When people have &apos;Hover Tips on All Objects&apos; selected in the viewer's settings, they'll see the object description pop-up for any object under their mouse pointer. The prim description is limited to 127 bytes any string longer then that will be truncated." />
+ top="2"
+ height="128"
+ width="128"
+ />
+
<text
type="string"
length="1"
follows="left|top"
- height="23"
+ height="16"
layout="topleft"
- left="5"
- name="LabelCreatorTitle"
- top_pad="10"
+ left_pad="5"
+ name="LabelOwnerTitle"
+ top="0"
width="78">
- Creator:
+ Owner:
</text>
- <avatar_icon
- follows="top|left"
- height="20"
- default_icon_name="Generic_Person"
- layout="topleft"
- left_pad="0"
- top_delta="-6"
- mouse_opaque="true"
- width="20" />
<text
type="string"
- follows="left|right|top"
font="SansSerifSmall"
- height="15"
+ follows="left|right|top"
layout="topleft"
- left_pad="5"
- name="LabelCreatorName"
- top_delta="6"
+ height="15"
+ width="187"
+ left_delta="0"
+ top_pad="0"
+ name="LabelOwnerName"
use_ellipses="true"
- width="165">
+ translate="false">
+TestString PleaseIgnore
</text>
- <button
- follows="top|right"
- height="16"
- image_selected="Inspector_I"
- image_unselected="Inspector_I"
- layout="topleft"
- right="-5"
- name="BtnCreator"
- top_delta="-6"
- width="16" />
<text
type="string"
length="1"
follows="left|top"
- height="23"
+ height="16"
layout="topleft"
- left="5"
- name="LabelOwnerTitle"
- top_pad="10"
+ left_delta="0"
+ name="LabelCreatorTitle"
+ top_pad="7"
width="78">
- Owner:
+ Creator:
</text>
- <avatar_icon
- follows="top|left"
- height="20"
- default_icon_name="Generic_Person"
- layout="topleft"
- left_pad="0"
- top_delta="-6"
- mouse_opaque="true"
- width="20" />
<text
type="string"
- follows="left|right|top"
font="SansSerifSmall"
- height="15"
+ follows="left|right|top"
layout="topleft"
- left_pad="5"
- name="LabelOwnerName"
- top_delta="6"
+ left_delta="0"
+ top_pad="0"
+ width="187"
+ height="15"
+ name="LabelCreatorName"
use_ellipses="true"
- width="165">
+ translate="false">
+TestString PleaseIgnore
</text>
- <button
- follows="top|right"
- height="16"
- image_selected="Inspector_I"
- image_unselected="Inspector_I"
- layout="topleft"
- right="-5"
- name="BtnOwner"
- top_delta="-3"
- width="16" />
<text
type="string"
length="1"
follows="left|top"
- height="23"
+ height="16"
layout="topleft"
- left="5"
+ left_delta="0"
name="LabelAcquiredTitle"
- top_pad="10"
+ top_pad="7"
width="78">
Acquired:
</text>
@@ -258,171 +170,247 @@
type="string"
length="1"
follows="left|top|right"
- height="23"
+ height="18"
layout="topleft"
- left_delta="78"
+ left_delta="0"
name="LabelAcquiredDate"
- top_delta="0"
- width="210">
+ top_pad="0"
+ width="187">
+ 00/00/00
</text>
- <text
- type="string"
- length="1"
+ <button
follows="left|top"
- height="10"
+ height="21"
+ label="Image..."
layout="topleft"
- left="5"
- name="LabelItemExperienceTitle"
+ left_delta="0"
+ name="change_thumbnail_btn"
top_pad="0"
- width="78"
- visible="true">
- Experience:
- </text>
+ width="120" />
+ </layout_panel>
+
+ <layout_panel
+ auto_resize="false"
+ name="layout_item_description"
+ layout="topleft"
+ follows="all"
+ height="84">
<text
type="string"
length="1"
- follows="left|top|right"
+ follows="left|top"
height="10"
layout="topleft"
- left_delta="78"
- name="LabelItemExperience"
- top_delta="0"
- width="210"
- visible="true"
- />
- <panel
- border="false"
- follows="left|top|right"
+ left="5"
+ name="LabelItemDescTitle"
+ top="0"
+ width="78">
+ Description:
+ </text>
+ <text_editor
+ text_type="ascii_printable_no_pipe"
+ commit_on_focus_lost="true"
+ border_style="line"
+ border_thickness="1"
+ word_wrap="true"
+ use_ellipses="false"
+ follows="all"
layout="topleft"
- mouse_opaque="false"
- name="perms_inv"
- left="0"
- top_pad="25"
- height="155"
- width="313">
- <text
- type="string"
- length="1"
- left="10"
- top_pad="13"
- text_color="EmphasisColor"
- height="15"
- follows="left|top|right"
- layout="topleft"
- name="perm_modify"
- width="200">
- You can:
- </text>
- <check_box
- height="18"
- label="Modify"
- layout="topleft"
- left="20"
- name="CheckOwnerModify"
- top_pad="0"
- width="90" />
- <check_box
- height="18"
- label="Copy"
- layout="topleft"
- left_pad="0"
- name="CheckOwnerCopy"
- width="90" />
- <check_box
- height="18"
- label="Transfer"
- layout="topleft"
- left_pad="0"
- name="CheckOwnerTransfer"
- width="106" />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="16"
- layout="topleft"
- left="10"
- name="AnyoneLabel"
- top_pad="8"
- width="100">
- Anyone:
- </text>
- <check_box
- height="18"
- label="Copy"
- layout="topleft"
- left_pad="0"
- name="CheckEveryoneCopy"
- tool_tip="Anyone can take a copy of the object . Object and all of its contents must be copy and transfer permissive."
- top_delta="-2"
- width="150" />
+ left="5"
+ top_pad="5"
+ right="-5"
+ height="46"
+ max_length="127"
+ name="LabelItemDesc"
+ tool_tip="When people have &apos;Hover Tips on All Objects&apos; selected in the viewer's settings, they'll see the object description pop-up for any object under their mouse pointer. The prim description is limited to 127 bytes any string longer then that will be truncated." />
+
<text
type="string"
length="1"
follows="left|top"
- height="16"
+ height="10"
layout="topleft"
- left="10"
- name="GroupLabel"
- top_pad="8"
- width="100">
- Group:
+ left="5"
+ name="LabelItemExperienceTitle"
+ top_pad="7"
+ width="78"
+ visible="true">
+ Experience:
</text>
- <check_box
- height="18"
- label="Share"
- layout="topleft"
- left_pad="0"
- top_delta="-2"
- name="CheckShareWithGroup"
- tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions."
- width="150" />
<text
type="string"
length="1"
- follows="left|top"
- height="16"
- layout="topleft"
- left="10"
- name="NextOwnerLabel"
- top_pad="8"
- width="200"
- word_wrap="true">
- Next owner:
- </text>
- <check_box
- height="18"
- label="Modify"
- layout="topleft"
- left="20"
- top_pad="0"
- name="CheckNextOwnerModify"
- tool_tip="Next owner can edit properties like item name or scale of this object."
- width="90" />
- <check_box
- height="18"
- label="Copy"
- layout="topleft"
- left_pad="0"
- name="CheckNextOwnerCopy"
- tool_tip="Next owner can make unlimited copies of this object. Copies maintain creator information, and can never be more permissive than the item being copied."
- width="90" />
- <check_box
- height="18"
- label="Transfer"
+ follows="left|top|right"
+ height="10"
layout="topleft"
- left_pad="0"
- name="CheckNextOwnerTransfer"
- tool_tip="Next owner can give away or resell this object."
- width="106" />
- </panel>
+ left_delta="78"
+ name="LabelItemExperience"
+ top_delta="0"
+ width="210"
+ visible="true"
+ />
+ </layout_panel>
+
+ <layout_panel
+ auto_resize="false"
+ name="layout_item_permissions_sale"
+ layout="topleft"
+ follows="all"
+ height="235">
+
+ <view_border
+ bevel_style="none"
+ height="0"
+ layout="topleft"
+ left="5"
+ right="-5"
+ name="cost_text_border"
+ top="1"/>
+
+ <text
+ type="string"
+ length="1"
+ left="10"
+ top_pad="7"
+ height="15"
+ follows="left|top"
+ layout="topleft"
+ name="perm_modify"
+ width="200">
+ Permissions
+ </text>
+
+ <text
+ type="string"
+ length="1"
+ left="10"
+ top_pad="5"
+ height="15"
+ follows="left|top"
+ layout="topleft"
+ name="perm_modify"
+ width="200">
+ You can:
+ </text>
+ <check_box
+ height="18"
+ label="Modify"
+ layout="topleft"
+ left="20"
+ name="CheckOwnerModify"
+ top_pad="0"
+ width="90" />
+ <check_box
+ height="18"
+ label="Copy"
+ layout="topleft"
+ left_pad="0"
+ name="CheckOwnerCopy"
+ width="90" />
+ <check_box
+ height="18"
+ label="Transfer"
+ layout="topleft"
+ left_pad="0"
+ name="CheckOwnerTransfer"
+ width="106" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="AnyoneLabel"
+ top_pad="8"
+ width="100">
+ Anyone:
+ </text>
+ <check_box
+ height="18"
+ label="Copy"
+ layout="topleft"
+ left_pad="0"
+ name="CheckEveryoneCopy"
+ tool_tip="Anyone can take a copy of the object . Object and all of its contents must be copy and transfer permissive."
+ top_delta="-2"
+ width="150" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="GroupLabel"
+ top_pad="8"
+ width="100">
+ Group:
+ </text>
+ <check_box
+ height="18"
+ label="Share"
+ layout="topleft"
+ left_pad="0"
+ top_delta="-2"
+ name="CheckShareWithGroup"
+ tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions."
+ width="150" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="NextOwnerLabel"
+ top_pad="8"
+ width="200"
+ word_wrap="true">
+ Next owner:
+ </text>
+ <check_box
+ height="18"
+ label="Modify"
+ layout="topleft"
+ left="20"
+ top_pad="0"
+ name="CheckNextOwnerModify"
+ tool_tip="Next owner can edit properties like item name or scale of this object."
+ width="90" />
+ <check_box
+ height="18"
+ label="Copy"
+ layout="topleft"
+ left_pad="0"
+ name="CheckNextOwnerCopy"
+ tool_tip="Next owner can make unlimited copies of this object. Copies maintain creator information, and can never be more permissive than the item being copied."
+ width="90" />
+ <check_box
+ height="18"
+ label="Transfer"
+ layout="topleft"
+ left_pad="0"
+ name="CheckNextOwnerTransfer"
+ tool_tip="Next owner can give away or resell this object."
+ width="106" />
+
+ <view_border
+ bevel_style="none"
+ height="0"
+ layout="topleft"
+ left="5"
+ right="-5"
+ name="cost_text_border"
+ top_pad="9"/>
+
<check_box
height="18"
label="For Sale"
layout="topleft"
left="20"
name="CheckPurchase"
- top_pad="20"
+ top_pad="15"
width="100"
tool_tip="Lets people buy this object, its content or it copy inworld for specified price." />
<combo_box
@@ -450,6 +438,7 @@
follows="left|top"
decimal_digits="0"
increment="1"
+ control_name="Edit Cost"
name="Edit Cost"
label="Price: L$"
label_width="75"
@@ -460,88 +449,80 @@
max_val="999999999"
top_pad="10"
tool_tip="Object cost." />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left="10"
- name="BaseMaskDebug"
- text_color="White"
- top_pad="30"
- width="130">
- B:
- </text>
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left_delta="60"
- name="OwnerMaskDebug"
- text_color="White"
- top_delta="0"
- width="270">
- O:
- </text>
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left_delta="60"
- name="GroupMaskDebug"
- text_color="White"
- top_delta="0"
- width="210">
- G:
- </text>
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left_delta="60"
- name="EveryoneMaskDebug"
- text_color="White"
- top_delta="0"
- width="150">
- E:
- </text>
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left_delta="60"
- name="NextMaskDebug"
- text_color="White"
- top_delta="0"
- width="90">
- N:
- </text>
- </panel>
- </scroll_container>
- <panel
- height="30"
- layout="topleft"
- name="button_panel"
- left="5"
- top_pad="0"
- width="313"
- follows="top|right|left">
- <button
- follows="top|right"
- height="23"
- label="Cancel"
- layout="topleft"
- name="cancel_btn"
- right="-1"
- width="100" />
- </panel>
+
+ </layout_panel>
+
+ <layout_panel
+ auto_resize="false"
+ name="layout_debug_permissions"
+ layout="topleft"
+ follows="all"
+ height="30">
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="BaseMaskDebug"
+ text_color="White"
+ top="2"
+ width="130">
+ B:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="OwnerMaskDebug"
+ text_color="White"
+ top_delta="0"
+ width="270">
+ O:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="GroupMaskDebug"
+ text_color="White"
+ top_delta="0"
+ width="210">
+ G:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="EveryoneMaskDebug"
+ text_color="White"
+ top_delta="0"
+ width="150">
+ E:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="NextMaskDebug"
+ text_color="White"
+ top_delta="0"
+ width="90">
+ N:
+ </text>
+ </layout_panel>
+ </layout_stack>
</panel>
diff --git a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml
index 5f0241512a..faff6185ab 100644
--- a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml
+++ b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml
@@ -62,25 +62,12 @@
name="Sale Mixed">
Mixed Sale
</panel.string>
- <button
- follows="top|left"
- height="24"
- image_hover_unselected="BackButton_Over"
- image_pressed="BackButton_Press"
- image_unselected="BackButton_Off"
- layout="topleft"
- left="8"
- name="back_btn"
- tab_stop="false"
- top="0"
- width="30"
- use_draw_context_alpha="false" />
- <text
+ <text
follows="top|left|right"
font="SansSerifHuge"
height="26"
layout="topleft"
- left_pad="10"
+ left="48"
name="title"
text_color="LtGray"
top="0"
@@ -181,7 +168,6 @@
translate="false"
use_ellipses="true"
width="225">
- TestString PleaseIgnore
</text>
<text
type="string"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 09ccc7b8d2..d0e09492b3 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -2306,6 +2306,8 @@ For AI Character: Get the closest navigable point to the point provided.
<string name="FavoritesNoMatchingItems">To add a place to your favorites, click the star to the right of the location name, then save the landmark to "Favorites bar".</string>
<string name="MarketplaceNoListing">You have no listings yet.</string>
<string name="MarketplaceNoMatchingItems">No items found. Check the spelling of your search string and try again.</string>
+ <string name="InventorySingleFolderEmpty">Folder is empty.</string>
+ <string name="InventorySingleFolderNoMatches">No matches.</string>
<string name="InventoryNoTexture">You do not have a copy of this texture in your inventory</string>
<string name="InventoryInboxNoItems">Your Marketplace purchases will appear here. You may then drag them into your inventory to use them.</string>
<string name="MarketplaceURL">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/</string>
@@ -2353,6 +2355,7 @@ If you continue to receive this message, please contact Second Life support for
<string name="InventoryMarketplaceListingsNoItems">
Drag folders to this area to list them for sale on the [[MARKETPLACE_DASHBOARD_URL] Marketplace].
</string>
+ <string name="InventoryFolderDebug">( F:[FOLDER_COUNT] I:[ITEMS_COUNT] V:[VERSION] DC:[VIEWER_DESCENDANT_COUNT]/[SERVER_DESCENDANT_COUNT] )</string>
<string name="InventoryItemsCount">( [ITEMS_COUNT] Items )</string>
<string name="Marketplace Validation Log"></string>
<string name="Marketplace Validation Warning Stock">stock folder must be contained by a version folder</string>
@@ -2391,12 +2394,16 @@ If you continue to receive this message, please contact Second Life support for
<string name="Unconstrained">Unconstrained</string>
<!-- use value="" because they have preceding spaces -->
+ <string name="active" value=" (active)"/>
<string name="no_transfer" value=" (no transfer)" />
<string name="no_modify" value=" (no modify)" />
<string name="no_copy" value=" (no copy)" />
<string name="worn" value=" (worn)" />
- <string name="link" value=" (link)" />
- <string name="broken_link" value=" (broken_link)" />
+ <string name="link" value=" link" />
+ <string name="broken_link" value=" broken_link" />
+ <string name="no_transfer_lbl" value=" no transfer" />
+ <string name="no_modify_lbl" value=" no modify" />
+ <string name="no_copy_lbl" value=" no copy" />
<string name="LoadingContents">Loading contents...</string>
<string name="NoContents">No contents</string>
<string name="WornOnAttachmentPoint" value=" (worn on [ATTACHMENT_POINT])" />
diff --git a/indra/newview/skins/default/xui/es/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/es/floater_inventory_item_properties.xml
deleted file mode 100644
index a8a3ad08f8..0000000000
--- a/indra/newview/skins/default/xui/es/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="PROPIEDADES DEL ÍTEM DEL INVENTARIO">
- <floater.string name="unknown">(desconocido)</floater.string>
- <floater.string name="public">(público)</floater.string>
- <floater.string name="you_can">Usted puede:</floater.string>
- <floater.string name="owner_can">El propietario puede:</floater.string>
- <floater.string name="acquiredDate">[wkday,datetime,local][day,datetime,local] [mth,datetime,local] [year,datetime,local][hour,datetime,local]:[min,datetime,local]:[second,datetime,local]</floater.string>
- <text name="LabelItemNameTitle">Nombre:</text>
- <text name="LabelItemDescTitle">Descripción:</text>
- <text name="LabelCreatorTitle">Creador:</text>
- <button label="Perfil..." label_selected="" name="BtnCreator"/>
- <text name="LabelOwnerTitle">Propietario:</text>
- <button label="Perfil..." label_selected="" name="BtnOwner"/>
- <text name="LabelAcquiredTitle">Adquirido:</text>
- <text name="LabelAcquiredDate">May Mié 24 12:50:46 2006</text>
- <text name="OwnerLabel">Tú:</text>
- <check_box label="Editar" name="CheckOwnerModify"/>
- <check_box label="Copiarlo" left_delta="88" name="CheckOwnerCopy"/>
- <check_box label="Revender" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel">Cualquiera:</text>
- <check_box label="Copiar" name="CheckEveryoneCopy"/>
- <text name="GroupLabel">Grupo:</text>
- <check_box label="Compartir" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel" width="230">Próximo propietario:</text>
- <check_box label="Editar" name="CheckNextOwnerModify"/>
- <check_box label="Copiarlo" left_delta="88" name="CheckNextOwnerCopy"/>
- <check_box label="Revender" name="CheckNextOwnerTransfer"/>
- <check_box label="En venta" name="CheckPurchase"/>
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="Copiar" name="Copy"/>
- <combo_box.item label="Contenidos" name="Contents"/>
- <combo_box.item label="Original" name="Original"/>
- </combo_box>
- <spinner label="Precio:" name="Edit Cost"/>
- <text name="CurrencySymbol">L$</text>
-</floater>
diff --git a/indra/newview/skins/default/xui/es/panel_main_inventory.xml b/indra/newview/skins/default/xui/es/panel_main_inventory.xml
index 1252c7ce0d..bf1205046b 100644
--- a/indra/newview/skins/default/xui/es/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/es/panel_main_inventory.xml
@@ -9,6 +9,7 @@
<panel.string name="ItemcountUnknown">
[ITEM_COUNT] Objetos y [CATEGORY_COUNT] Carpetas Obtenidos [FILTER]
</panel.string>
+ <panel.string name="inventory_title">INVENTARIO</panel.string>
<text name="ItemcountText">
Ítems:
</text>
diff --git a/indra/newview/skins/default/xui/fr/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/fr/floater_inventory_item_properties.xml
deleted file mode 100644
index 1d4e7c818f..0000000000
--- a/indra/newview/skins/default/xui/fr/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="PROPRIÉTÉS DES ARTICLES DE L&apos;INVENTAIRE">
- <floater.string name="unknown">(inconnu)</floater.string>
- <floater.string name="public">(public)</floater.string>
- <floater.string name="you_can">Vous pouvez :</floater.string>
- <floater.string name="owner_can">Le propriétaire peut :</floater.string>
- <floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string>
- <text name="LabelItemNameTitle">Nom :</text>
- <text name="LabelItemDescTitle">Description :</text>
- <text name="LabelCreatorTitle">Créateur :</text>
- <button label="Profil..." label_selected="" name="BtnCreator"/>
- <text name="LabelOwnerTitle">Propriétaire :</text>
- <button label="Profil..." label_selected="" name="BtnOwner"/>
- <text name="LabelAcquiredTitle">Acquis :</text>
- <text name="LabelAcquiredDate">Wed May 24 12:50:46 2006</text>
- <text name="OwnerLabel">Vous :</text>
- <check_box label="Modifier" name="CheckOwnerModify"/>
- <check_box label="Copier" name="CheckOwnerCopy"/>
- <check_box label="Revendre" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel" width="80">N&apos;importe qui :</text>
- <check_box label="Copier" name="CheckEveryoneCopy"/>
- <text name="GroupLabel" width="80">Groupe :</text>
- <check_box label="Partager" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel" width="192">Le prochain propriétaire :</text>
- <check_box label="Modifier" name="CheckNextOwnerModify"/>
- <check_box label="Copier" name="CheckNextOwnerCopy"/>
- <check_box label="Revendre" name="CheckNextOwnerTransfer"/>
- <check_box label="À vendre" name="CheckPurchase"/>
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="Copier" name="Copy"/>
- <combo_box.item label="Contenu" name="Contents"/>
- <combo_box.item label="Original" name="Original"/>
- </combo_box>
- <spinner label="Prix :" name="Edit Cost"/>
- <text name="CurrencySymbol">L$</text>
-</floater>
diff --git a/indra/newview/skins/default/xui/fr/panel_main_inventory.xml b/indra/newview/skins/default/xui/fr/panel_main_inventory.xml
index 5bcee89752..5bf4d6c15d 100644
--- a/indra/newview/skins/default/xui/fr/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/fr/panel_main_inventory.xml
@@ -9,6 +9,7 @@
<panel.string name="ItemcountUnknown">
Recherche effectuée [ITEM_COUNT] d&apos;articles et [CATEGORY_COUNT] de dossiers [FILTER]
</panel.string>
+ <panel.string name="inventory_title">INVENTAIRE</panel.string>
<text name="ItemcountText">
Articles :
</text>
diff --git a/indra/newview/skins/default/xui/it/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/it/floater_inventory_item_properties.xml
deleted file mode 100644
index 8cf680b3f0..0000000000
--- a/indra/newview/skins/default/xui/it/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="CARATTERISTICHE DELL&apos;ARTICOLO IN INVENTARIO">
- <floater.string name="unknown">(sconosciuto)</floater.string>
- <floater.string name="public">(pubblico)</floater.string>
- <floater.string name="you_can">Tu puoi:</floater.string>
- <floater.string name="owner_can">Il proprietario può:</floater.string>
- <floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string>
- <text name="LabelItemNameTitle">Nome:</text>
- <text name="LabelItemDescTitle">Descrizione:</text>
- <text name="LabelCreatorTitle">Creatore:</text>
- <button label="Profilo..." label_selected="" name="BtnCreator"/>
- <text name="LabelOwnerTitle">proprietario:</text>
- <button label="Profilo..." label_selected="" name="BtnOwner"/>
- <text name="LabelAcquiredTitle">Acquisito:</text>
- <text name="LabelAcquiredDate">Wed May 24 12:50:46 2006</text>
- <text name="OwnerLabel">Tu:</text>
- <check_box label="Modifica" name="CheckOwnerModify"/>
- <check_box label="Copiare" left_delta="88" name="CheckOwnerCopy"/>
- <check_box label="Rivendi" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel">Chiunque:</text>
- <check_box label="Copia" name="CheckEveryoneCopy"/>
- <text name="GroupLabel">Gruppo:</text>
- <check_box label="Condividi" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel" width="230">Proprietario successivo:</text>
- <check_box label="Modifica" name="CheckNextOwnerModify"/>
- <check_box label="Copiare" left_delta="88" name="CheckNextOwnerCopy"/>
- <check_box label="Rivendi" name="CheckNextOwnerTransfer"/>
- <check_box label="In vendita" name="CheckPurchase"/>
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="Copia" name="Copy"/>
- <combo_box.item label="Contenuti" name="Contents"/>
- <combo_box.item label="Originale" name="Original"/>
- </combo_box>
- <spinner label="Prezzo:" name="Edit Cost"/>
- <text name="CurrencySymbol">L$</text>
-</floater>
diff --git a/indra/newview/skins/default/xui/it/panel_main_inventory.xml b/indra/newview/skins/default/xui/it/panel_main_inventory.xml
index 5d11967cee..d6890229e7 100644
--- a/indra/newview/skins/default/xui/it/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/it/panel_main_inventory.xml
@@ -9,6 +9,7 @@
<panel.string name="ItemcountUnknown">
Recuperati [ITEM_COUNT] oggetti e [CATEGORY_COUNT] cartelle [FILTER]
</panel.string>
+ <panel.string name="inventory_title">INVENTARIO</panel.string>
<text name="ItemcountText">
Oggetti:
</text>
diff --git a/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml
deleted file mode 100644
index 2a8c9f83b2..0000000000
--- a/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="インベントリアイテムのプロパティ">
- <floater.string name="unknown">
- (不明)
- </floater.string>
- <floater.string name="public">
- (公共)
- </floater.string>
- <floater.string name="you_can">
- できること:
- </floater.string>
- <floater.string name="owner_can">
- オーナーは次のことができます:
- </floater.string>
- <floater.string name="acquiredDate">
- [year,datetime,local]年[mth,datetime,local]月[day,datetime,local]日[wkday,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]
- </floater.string>
- <text name="LabelItemNameTitle">
- 名前:
- </text>
- <text name="LabelItemDescTitle">
- 説明:
- </text>
- <text name="LabelCreatorTitle">
- 制作者:
- </text>
- <button label="プロフィール…" label_selected="" name="BtnCreator"/>
- <text name="LabelOwnerTitle">
- 所有者:
- </text>
- <button label="プロフィール…" label_selected="" name="BtnOwner"/>
- <text name="LabelAcquiredTitle">
- 入手日時:
- </text>
- <text name="LabelAcquiredDate">
- 2006年5月24日水曜日 12:50:46
- </text>
- <text name="OwnerLabel">
- あなた:
- </text>
- <check_box label="編集" name="CheckOwnerModify"/>
- <check_box label="コピー" name="CheckOwnerCopy"/>
- <check_box label="再販・譲渡" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel">
- 全員:
- </text>
- <check_box label="コピー" name="CheckEveryoneCopy"/>
- <text name="GroupLabel">
- グループ:
- </text>
- <check_box label="共有" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel">
- 次の所有者:
- </text>
- <check_box label="編集" name="CheckNextOwnerModify"/>
- <check_box label="コピー" name="CheckNextOwnerCopy"/>
- <check_box label="再販・譲渡" name="CheckNextOwnerTransfer"/>
- <check_box label="売り出し中" name="CheckPurchase"/>
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="コピー" name="Copy"/>
- <combo_box.item label="コンテンツ" name="Contents"/>
- <combo_box.item label="オリジナル" name="Original"/>
- </combo_box>
- <spinner label="価格:" name="Edit Cost"/>
- <text name="CurrencySymbol">
- L$
- </text>
-</floater>
diff --git a/indra/newview/skins/default/xui/ja/panel_main_inventory.xml b/indra/newview/skins/default/xui/ja/panel_main_inventory.xml
index 9d78beb9da..a38492c6d1 100644
--- a/indra/newview/skins/default/xui/ja/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/ja/panel_main_inventory.xml
@@ -9,6 +9,7 @@
<panel.string name="ItemcountUnknown">
[ITEM_COUNT]個のアイテムと[CATEGORY_COUNT]個のフォルダーを取得しました。[FILTER]
</panel.string>
+ <panel.string name="inventory_title">インベントリ</panel.string>
<text name="ItemcountText">
アイテム:
</text>
diff --git a/indra/newview/skins/default/xui/pl/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/pl/floater_inventory_item_properties.xml
deleted file mode 100644
index d2844e117f..0000000000
--- a/indra/newview/skins/default/xui/pl/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<floater name="item properties" title="WŁAŚCIWOŚCI OBIEKTÓW W SZAFIE">
- <floater.string name="unknown">
- (nieznany)
- </floater.string>
- <floater.string name="public">
- (publiczny)
- </floater.string>
- <floater.string name="you_can">
- Ty możesz:
- </floater.string>
- <floater.string name="owner_can">
- Właściciel może:
- </floater.string>
- <text name="LabelItemNameTitle">
- Nazwa:
- </text>
- <text name="LabelItemDescTitle">
- Opis:
- </text>
- <text name="LabelCreatorTitle">
- Twórca:
- </text>
- <button label="Profil..." name="BtnCreator" />
- <text name="LabelOwnerTitle">
- Właściciel:
- </text>
- <button label="Profil..." name="BtnOwner" />
- <text name="LabelAcquiredTitle">
- Nabyte:
- </text>
- <text name="OwnerLabel">
- Ty:
- </text>
- <check_box label="Modyfikacja" name="CheckOwnerModify" />
- <check_box label="Kopiowanie" name="CheckOwnerCopy" />
- <check_box label="Transferowanie" name="CheckOwnerTransfer" />
- <text name="AnyoneLabel">
- Każdy:
- </text>
- <check_box label="Kopiowanie" name="CheckEveryoneCopy" />
- <text name="GroupLabel">
- Grupa:
- </text>
- <check_box label="Udostępnij" name="CheckShareWithGroup" />
- <text name="NextOwnerLabel">
- Nast. właściciel:
- </text>
- <check_box label="Modyfikacja" name="CheckNextOwnerModify" />
- <check_box label="Kopiowanie" name="CheckNextOwnerCopy" />
- <check_box label="Transferowanie" name="CheckNextOwnerTransfer" />
- <check_box label="Sprzedaż" name="CheckPurchase" />
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="Kopia" name="Copy" />
- <combo_box.item label="Zawartość" name="Contents" />
- <combo_box.item label="Oryginał" name="Original" />
- </combo_box>
- <spinner name="Edit Cost" label="Cena:" />
-</floater>
diff --git a/indra/newview/skins/default/xui/pl/panel_main_inventory.xml b/indra/newview/skins/default/xui/pl/panel_main_inventory.xml
index dc254e246f..1011c38378 100644
--- a/indra/newview/skins/default/xui/pl/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/pl/panel_main_inventory.xml
@@ -6,6 +6,7 @@
<panel.string name="ItemcountCompleted">
[ITEM_COUNT] obiekty [FILTER]
</panel.string>
+ <panel.string name="inventory_title">MOJA SZAFA</panel.string>
<text name="ItemcountText">
Obiekty:
</text>
diff --git a/indra/newview/skins/default/xui/pt/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/pt/floater_inventory_item_properties.xml
deleted file mode 100644
index 5f04c08531..0000000000
--- a/indra/newview/skins/default/xui/pt/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="PROPRIEDADES DE ITEM NO INVENTÁRIO">
- <floater.string name="unknown">(desconhecido)</floater.string>
- <floater.string name="public">(público)</floater.string>
- <floater.string name="you_can">Você pode:</floater.string>
- <floater.string name="owner_can">Proprietário pode :</floater.string>
- <floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string>
- <text name="LabelItemNameTitle">Nome:</text>
- <text name="LabelItemDescTitle">Descrição:</text>
- <text name="LabelCreatorTitle">Criador:</text>
- <button label="Perfil..." label_selected="" name="BtnCreator"/>
- <text name="LabelOwnerTitle">Dono:</text>
- <button label="Perfil..." label_selected="" name="BtnOwner"/>
- <text name="LabelAcquiredTitle">Adquirido:</text>
- <text name="LabelAcquiredDate">Qua Mai 24 12:50:46 2006</text>
- <text name="OwnerLabel">Você:</text>
- <check_box label="Editar" name="CheckOwnerModify"/>
- <check_box label="Copiar" name="CheckOwnerCopy"/>
- <check_box label="Revender" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel">Todos:</text>
- <check_box label="Cortar" name="CheckEveryoneCopy"/>
- <text name="GroupLabel">Grupo:</text>
- <check_box label="Compartilhar" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel" width="230">Próximo proprietário:</text>
- <check_box label="Editar" name="CheckNextOwnerModify"/>
- <check_box label="Copiar" name="CheckNextOwnerCopy"/>
- <check_box label="Revender" name="CheckNextOwnerTransfer"/>
- <check_box label="À venda" name="CheckPurchase"/>
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="Cortar" name="Copy"/>
- <combo_box.item label="Conteúdo" name="Contents"/>
- <combo_box.item label="Original" name="Original"/>
- </combo_box>
- <spinner label="Preço:" name="Edit Cost"/>
- <text name="CurrencySymbol">L$</text>
-</floater>
diff --git a/indra/newview/skins/default/xui/pt/panel_main_inventory.xml b/indra/newview/skins/default/xui/pt/panel_main_inventory.xml
index 009b5b3193..e0cf528468 100644
--- a/indra/newview/skins/default/xui/pt/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/pt/panel_main_inventory.xml
@@ -9,6 +9,7 @@
<panel.string name="ItemcountUnknown">
Itens [ITEM_COUNT] e Pastas [CATEGORY_COUNT] Reunidos [FILTER]
</panel.string>
+ <panel.string name="inventory_title">INVENTÁRIO</panel.string>
<text name="ItemcountText">
Itens:
</text>
diff --git a/indra/newview/skins/default/xui/ru/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/ru/floater_inventory_item_properties.xml
deleted file mode 100644
index c988825756..0000000000
--- a/indra/newview/skins/default/xui/ru/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="СВОЙСТВА ПРЕДМЕТА">
- <floater.string name="unknown">(неизвестно)</floater.string>
- <floater.string name="public">(публичное)</floater.string>
- <floater.string name="you_can">Вы можете:</floater.string>
- <floater.string name="owner_can">Владелец может:</floater.string>
- <floater.string name="acquiredDate">[wkday,datetime,local], [day,datetime,local] [mth,datetime,local] [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]</floater.string>
- <text name="LabelItemNameTitle">Название:</text>
- <text name="LabelItemDescTitle">Описание:</text>
- <text name="LabelCreatorTitle">Создатель:</text>
- <button label="Профиль…" name="BtnCreator"/>
- <text name="LabelOwnerTitle">Владелец:</text>
- <button label="Профиль…" name="BtnOwner"/>
- <text name="LabelAcquiredTitle">Приобретено:</text>
- <text name="LabelAcquiredDate">Ср 24 Май 12:50:46 2006</text>
- <text name="OwnerLabel">Вы:</text>
- <check_box label="Изменить" name="CheckOwnerModify"/>
- <check_box label="Копировать" name="CheckOwnerCopy"/>
- <check_box label="Перепродать" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel">Все:</text>
- <check_box label="Копировать" name="CheckEveryoneCopy"/>
- <text name="GroupLabel">Группа:</text>
- <check_box label="Поделиться" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel">Следующий владелец:</text>
- <check_box label="Изменить" name="CheckNextOwnerModify"/>
- <check_box label="Копировать" name="CheckNextOwnerCopy"/>
- <check_box label="Перепродать" name="CheckNextOwnerTransfer"/>
- <check_box label="Для продажи" name="CheckPurchase"/>
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="Копировать" name="Copy"/>
- <combo_box.item label="Содержимое" name="Contents"/>
- <combo_box.item label="Оригинал" name="Original"/>
- </combo_box>
- <spinner label="Цена:" name="Edit Cost"/>
- <text name="CurrencySymbol">L$</text>
-</floater>
diff --git a/indra/newview/skins/default/xui/ru/panel_main_inventory.xml b/indra/newview/skins/default/xui/ru/panel_main_inventory.xml
index f2502bf6d3..b473fb8f98 100644
--- a/indra/newview/skins/default/xui/ru/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/ru/panel_main_inventory.xml
@@ -9,6 +9,7 @@
<panel.string name="ItemcountUnknown">
Выборка [ITEM_COUNT] предметов и [CATEGORY_COUNT] папок [FILTER]
</panel.string>
+ <panel.string name="inventory_title">ИНВЕНТАРЬ</panel.string>
<text name="ItemcountText">
Вещи:
</text>
diff --git a/indra/newview/skins/default/xui/tr/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/tr/floater_inventory_item_properties.xml
deleted file mode 100644
index c6a5515c6e..0000000000
--- a/indra/newview/skins/default/xui/tr/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="ENVANTER ÖGESİ ÖZELLİKLERİ">
- <floater.string name="unknown">(bilinmiyor)</floater.string>
- <floater.string name="public">(kamuya açık)</floater.string>
- <floater.string name="you_can">Şunu yapabilirsiniz:</floater.string>
- <floater.string name="owner_can">Sahip şunu yapabilir:</floater.string>
- <floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string>
- <text name="LabelItemNameTitle">Ad:</text>
- <text name="LabelItemDescTitle">Açıklama:</text>
- <text name="LabelCreatorTitle">Oluşturan:</text>
- <button label="Profil..." name="BtnCreator"/>
- <text name="LabelOwnerTitle">Sahip:</text>
- <button label="Profil..." name="BtnOwner"/>
- <text name="LabelAcquiredTitle">Alınan:</text>
- <text name="LabelAcquiredDate">24 Mayıs Çarş 12:50:46 2006</text>
- <text name="OwnerLabel">Siz:</text>
- <check_box label="Düzenle" name="CheckOwnerModify"/>
- <check_box label="Kopyala" name="CheckOwnerCopy"/>
- <check_box label="Tekrar Sat" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel">Herkes:</text>
- <check_box label="Kopyala" name="CheckEveryoneCopy"/>
- <text name="GroupLabel">Grup:</text>
- <check_box label="Paylaş" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel">Sonraki sahip:</text>
- <check_box label="Düzenle" name="CheckNextOwnerModify"/>
- <check_box label="Kopyala" name="CheckNextOwnerCopy"/>
- <check_box label="Tekrar Sat" name="CheckNextOwnerTransfer"/>
- <check_box label="Satılık" name="CheckPurchase"/>
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="Kopyala" name="Copy"/>
- <combo_box.item label="İçerik" name="Contents"/>
- <combo_box.item label="Orijinal" name="Original"/>
- </combo_box>
- <spinner label="Fiyat:" name="Edit Cost"/>
- <text name="CurrencySymbol">L$</text>
-</floater>
diff --git a/indra/newview/skins/default/xui/tr/panel_main_inventory.xml b/indra/newview/skins/default/xui/tr/panel_main_inventory.xml
index a11fd98b9a..7e98078635 100644
--- a/indra/newview/skins/default/xui/tr/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/tr/panel_main_inventory.xml
@@ -9,6 +9,7 @@
<panel.string name="ItemcountUnknown">
[ITEM_COUNT] Öğe ve [CATEGORY_COUNT] Klasör Alındı [FILTER]
</panel.string>
+ <panel.string name="inventory_title">ENVANTER</panel.string>
<text name="ItemcountText">
Ögeler:
</text>
diff --git a/indra/newview/skins/default/xui/zh/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/zh/floater_inventory_item_properties.xml
deleted file mode 100644
index 4f17b96579..0000000000
--- a/indra/newview/skins/default/xui/zh/floater_inventory_item_properties.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="收納區物品屬性">
- <floater.string name="unknown">(未知)</floater.string>
- <floater.string name="public">(公開)</floater.string>
- <floater.string name="you_can">你可以:</floater.string>
- <floater.string name="owner_can">所有人可以:</floater.string>
- <floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string>
- <text name="LabelItemNameTitle">名稱:</text>
- <text name="LabelItemDescTitle">描述:</text>
- <text name="LabelCreatorTitle">創造者:</text>
- <button label="檔案..." name="BtnCreator"/>
- <text name="LabelOwnerTitle">所有人:</text>
- <button label="檔案..." name="BtnOwner"/>
- <text name="LabelAcquiredTitle">取得於:</text>
- <text name="LabelAcquiredDate">Wed May 24 12:50:46 2006</text>
- <text name="OwnerLabel">你:</text>
- <check_box label="編輯" name="CheckOwnerModify"/>
- <check_box label="恚庨" name="CheckOwnerCopy"/>
- <check_box label="轉售" name="CheckOwnerTransfer"/>
- <text name="AnyoneLabel">任何人:</text>
- <check_box label="恚庨" name="CheckEveryoneCopy"/>
- <text name="GroupLabel">群組:</text>
- <check_box label="分享" name="CheckShareWithGroup"/>
- <text name="NextOwnerLabel">下一個所有人:</text>
- <check_box label="編輯" name="CheckNextOwnerModify"/>
- <check_box label="恚庨" name="CheckNextOwnerCopy"/>
- <check_box label="轉售" name="CheckNextOwnerTransfer"/>
- <check_box label="出售" name="CheckPurchase"/>
- <combo_box name="ComboBoxSaleType">
- <combo_box.item label="複製" name="Copy"/>
- <combo_box.item label="內容" name="Contents"/>
- <combo_box.item label="原件" name="Original"/>
- </combo_box>
- <spinner label="價格:" name="Edit Cost"/>
- <text name="CurrencySymbol">L$</text>
-</floater>
diff --git a/indra/newview/skins/default/xui/zh/panel_main_inventory.xml b/indra/newview/skins/default/xui/zh/panel_main_inventory.xml
index 1a28f4c3b5..9ffa9323cc 100644
--- a/indra/newview/skins/default/xui/zh/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/zh/panel_main_inventory.xml
@@ -9,6 +9,7 @@
<panel.string name="ItemcountUnknown">
擷取了[ITEM_COUNT]個物項及[CATEGORY_COUNT]個資料夾[FILTER]
</panel.string>
+ <panel.string name="inventory_title">收納區</panel.string>
<text name="ItemcountText">
物品:
</text>